Compare commits
276
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0fe62facef | ||
|
|
110bec1507 | ||
|
|
68635cc1a8 | ||
|
|
8bb929c2ff | ||
|
|
4161aac03e | ||
|
|
26613eec97 | ||
|
|
8a5dbbe3fb | ||
|
|
4202daa1fa | ||
|
|
1c58d6d3d1 | ||
|
|
b2acd235b2 | ||
|
|
b15d483611 | ||
|
|
ecfab8fe9f | ||
|
|
935be38e4f | ||
|
|
23a78751d5 | ||
|
|
21de36604a | ||
|
|
1420fc6b63 | ||
|
|
723a613236 | ||
|
|
60c8f8adbf | ||
|
|
fd4c6892a4 | ||
|
|
42a506cd53 | ||
|
|
d6205812cf | ||
|
|
85484ed1fc | ||
|
|
0857e8e22f | ||
|
|
79de70139c | ||
|
|
a662e29b50 | ||
|
|
787df32cb3 | ||
|
|
a3c51c8a70 | ||
|
|
05154bad34 | ||
|
|
8c8b52d572 | ||
|
|
718bece30c | ||
|
|
3ce24ea2ba | ||
|
|
62c0444cbf | ||
|
|
6594329638 | ||
|
|
8ab890b7d7 | ||
|
|
9166d8fdcb | ||
|
|
11aa88c5e0 | ||
|
|
ef7fd16dca | ||
|
|
722d64ef3b | ||
|
|
81af422efb | ||
|
|
de0f5a1da4 | ||
|
|
ed10bed8c8 | ||
|
|
c794dae53b | ||
|
|
164ac6fcff | ||
|
|
2284f6864b | ||
|
|
037201e9ad | ||
|
|
66f8e1b45c | ||
|
|
cadbb91bd1 | ||
|
|
78a69c9cc8 | ||
|
|
5ab7e7c638 | ||
|
|
1aa9d39127 | ||
|
|
9a70f50e22 | ||
|
|
c4957f1907 | ||
|
|
1ae3aad336 | ||
|
|
88bb203290 | ||
|
|
c7bce2f75b | ||
|
|
647d8970cc | ||
|
|
14fab74111 | ||
|
|
adf2951518 | ||
|
|
aa8e466e42 | ||
|
|
4cd1a923b9 | ||
|
|
6eb8c2d270 | ||
|
|
50b14ca232 | ||
|
|
01c10910bc | ||
|
|
683aa2df24 | ||
|
|
ce3bb062dd | ||
|
|
1faba2376a | ||
|
|
cfabc24ac9 | ||
|
|
d9937c5b82 | ||
|
|
a75beafe1d | ||
|
|
1589d13f94 | ||
|
|
163a08057c | ||
|
|
35cd559791 | ||
|
|
8519bdc6be | ||
|
|
328d5c088a | ||
|
|
fe3033ef0e | ||
|
|
9863bc5bd6 | ||
|
|
026c42fc3c | ||
|
|
2d83759e49 | ||
|
|
f4fdd37735 | ||
|
|
568b241762 | ||
|
|
16c3f055ff | ||
|
|
e428d22e39 | ||
|
|
5bba155477 | ||
|
|
823eef1d9a | ||
|
|
6118beb9e0 | ||
|
|
ffc2f31b6f | ||
|
|
c27bec417c | ||
|
|
45e13dec0f | ||
|
|
2ebe366a38 | ||
|
|
a7f7fc94ea | ||
|
|
46c73c8745 | ||
|
|
d000d212e5 | ||
|
|
742132da1b | ||
|
|
5cec99aaf6 | ||
|
|
7a28243aed | ||
|
|
1c6430c2f5 | ||
|
|
e5ff79d23b | ||
|
|
b4b6962b66 | ||
|
|
91cb6bee22 | ||
|
|
9fdc1462d5 | ||
|
|
b504bfa3ca | ||
|
|
3b8fe18288 | ||
|
|
8aa7b97764 | ||
|
|
d01640402c | ||
|
|
89fa720b26 | ||
|
|
b8376455d3 | ||
|
|
778f7a9d8d | ||
|
|
ea12a7e892 | ||
|
|
6885a99955 | ||
|
|
4a06040b70 | ||
|
|
80d78f5167 | ||
|
|
b9b7df82a1 | ||
|
|
336d51bb0a | ||
|
|
38618a58c9 | ||
|
|
cce556551b | ||
|
|
a71509b8ce | ||
|
|
0aee632a11 | ||
|
|
a49d3e5217 | ||
|
|
a8502b5b29 | ||
|
|
a6b8ca5654 | ||
|
|
771ae1fc3c | ||
|
|
8b17779a49 | ||
|
|
6c0852dda1 | ||
|
|
a363b97bcb | ||
|
|
e8b211bd5f | ||
|
|
389af71c69 | ||
|
|
f927c87804 | ||
|
|
49459d0fd5 | ||
|
|
81e5489f5e | ||
|
|
26841dd3e3 | ||
|
|
e1666702d4 | ||
|
|
6c3c25d439 | ||
|
|
13bd25ea34 | ||
|
|
e6f54f8b92 | ||
|
|
465e5e784e | ||
|
|
369e76bd18 | ||
|
|
d8cbe8df22 | ||
|
|
c069b097ac | ||
|
|
9a65629fe1 | ||
|
|
c43a0694ed | ||
|
|
87af9d2bf9 | ||
|
|
534eebd513 | ||
|
|
1b69afdc8b | ||
|
|
5f56e1cc83 | ||
|
|
b0df436103 | ||
|
|
252623ff45 | ||
|
|
a4b15cc776 | ||
|
|
751d3431d9 | ||
|
|
23329a8f71 | ||
|
|
7d42a94add | ||
|
|
991d893615 | ||
|
|
839f3190a0 | ||
|
|
444b8187f6 | ||
|
|
6842b88608 | ||
|
|
16b1a2a9dc | ||
|
|
6cff8dbcd9 | ||
|
|
c7c650ef9b | ||
|
|
9508bac66d | ||
|
|
e818c916f1 | ||
|
|
d3a1102f33 | ||
|
|
096c35163c | ||
|
|
06e2be7cd0 | ||
|
|
b13bc88382 | ||
|
|
56b3d254a0 | ||
|
|
3059cf06c7 | ||
|
|
61f6c8e115 | ||
|
|
91561aaef1 | ||
|
|
f3a6439117 | ||
|
|
fd01b3287b | ||
|
|
9159121ff7 | ||
|
|
80916f85a4 | ||
|
|
e37314ff51 | ||
|
|
4d47bb42f3 | ||
|
|
eba1824ebb | ||
|
|
9b4c527442 | ||
|
|
66258d17ba | ||
|
|
5359237ffd | ||
|
|
808a8b1a18 | ||
|
|
db6d9df56b | ||
|
|
55a6d1b25a | ||
|
|
e42c849e49 | ||
|
|
935f060063 | ||
|
|
add10e3fc4 | ||
|
|
56eedbaf06 | ||
|
|
a300efe718 | ||
|
|
6b3e60ebda | ||
|
|
1adead75ec | ||
|
|
449df5ceea | ||
|
|
73aa96adbf | ||
|
|
d193264b40 | ||
|
|
f5d0658223 | ||
|
|
c145a0c687 | ||
|
|
947cb04ea4 | ||
|
|
075c9caea4 | ||
|
|
77810e3741 | ||
|
|
15934bebb9 | ||
|
|
8ba39f2976 | ||
|
|
a2daad497d | ||
|
|
a357c4fe42 | ||
|
|
a8aca346a2 | ||
|
|
eec9f7dd86 | ||
|
|
b34d2c0d8d | ||
|
|
55dbbf5dad | ||
|
|
a068a86210 | ||
|
|
d3d0f92b83 | ||
|
|
9a2f460d17 | ||
|
|
00f7b1b9a6 | ||
|
|
0232930c8c | ||
|
|
a05009f387 | ||
|
|
1ac3d6e674 | ||
|
|
84bfa01f0f | ||
|
|
c679e7412f | ||
|
|
8b954e93ff | ||
|
|
11a6b81af2 | ||
|
|
35f1c1a4ed | ||
|
|
3e4b36d0e1 | ||
|
|
e9a11836bb | ||
|
|
c7100ca6e7 | ||
|
|
45a1b19426 | ||
|
|
e4c76ed57e | ||
|
|
b2d011bfb6 | ||
|
|
e1c3e701a8 | ||
|
|
a32f106d89 | ||
|
|
12645a3942 | ||
|
|
9a3300bc3f | ||
|
|
5dfc439f32 | ||
|
|
54f34f9ff1 | ||
|
|
0bc0814235 | ||
|
|
dccb44ad35 | ||
|
|
1726be86b9 | ||
|
|
a65ee60983 | ||
|
|
ce47a35740 | ||
|
|
e138f366fe | ||
|
|
9e0970016f | ||
|
|
c788c4787c | ||
|
|
1e642147ae | ||
|
|
ef6074c420 | ||
|
|
9f875b317e | ||
|
|
eb06c855b5 | ||
|
|
ec729707e9 | ||
|
|
31cbe9beed | ||
|
|
29ecee103b | ||
|
|
49ebade007 | ||
|
|
76650a0d1e | ||
|
|
bdf529b9fe | ||
|
|
a53acb7ce8 | ||
|
|
35bd5c84e9 | ||
|
|
ad21adbf57 | ||
|
|
ff5238ed5b | ||
|
|
5594a32678 | ||
|
|
6ae0a5bb89 | ||
|
|
8dedcb1ee3 | ||
|
|
417837e238 | ||
|
|
b7a0ae8f6a | ||
|
|
b9bf7a57c1 | ||
|
|
8d66b1732f | ||
|
|
bbd9db5e53 | ||
|
|
6d5a451f09 | ||
|
|
398c060d4f | ||
|
|
59543bd226 | ||
|
|
0d3d2954a0 | ||
|
|
3c7f68e231 | ||
|
|
3a1b7ebc51 | ||
|
|
8edc9eaab0 | ||
|
|
052b5e0cf2 | ||
|
|
6738a32c03 | ||
|
|
c2138c03e3 | ||
|
|
0b8b7d748c | ||
|
|
53bc105b9b | ||
|
|
c7f42bd83c | ||
|
|
e7fc38420d | ||
|
|
5295015a31 | ||
|
|
48750e304b | ||
|
|
d234f822b8 | ||
|
|
39570992ee | ||
|
|
c60d0934ca |
+2
-1
@@ -266,6 +266,7 @@ miniapps/navier/*_output
|
||||
miniapps/nurbs/nurbs_ex1
|
||||
miniapps/nurbs/nurbs_ex1p
|
||||
miniapps/nurbs/nurbs_ex11p
|
||||
miniapps/nurbs/nurbs_printfunc
|
||||
miniapps/nurbs/nurbs_patch_ex1
|
||||
miniapps/nurbs/nurbs_curveint
|
||||
miniapps/nurbs/refined.mesh
|
||||
@@ -299,7 +300,7 @@ miniapps/tools/convert-dc
|
||||
miniapps/tools/lor-transfer
|
||||
miniapps/tools/plor-transfer
|
||||
miniapps/tools/get-values
|
||||
miniapps/tools/check-tmop-metric
|
||||
miniapps/tools/tmop-check-metric
|
||||
miniapps/tools/tmop-metric-magnitude
|
||||
miniapps/tools/nodal-transfer
|
||||
miniapps/tools/ParaView
|
||||
|
||||
@@ -11,6 +11,15 @@
|
||||
Version 4.6.1 (development)
|
||||
===========================
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Introduced support for higher order non conformal Nedelec elements on
|
||||
simplices in ParMesh.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
|
||||
|
||||
|
||||
Version 4.6, released on September 27, 2023
|
||||
===========================================
|
||||
@@ -31,6 +40,7 @@ Meshing improvements
|
||||
* The edge to knot map for NURBS meshes can be determined automatically. It is
|
||||
no longer needed to specify this in the NURBS mesh.
|
||||
* Added curve interpolation method for NURBS.
|
||||
* Added new small miniapp for printing of shape functions of a KnotVector
|
||||
* See miniapps/nurbs for example meshes and miniapps.
|
||||
|
||||
Discretization improvements
|
||||
@@ -77,6 +87,8 @@ Linear and nonlinear solvers
|
||||
|
||||
- Added HIP support to the PETSc and SUNDIALS interfaces.
|
||||
|
||||
- Efficient GPU-accelerated LOR assembly now supports surface meshes.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Added a new H(div) solver miniapp demonstrating the use of a matrix-free
|
||||
|
||||
+20
-2
@@ -641,16 +641,34 @@ if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
|
||||
foreach(Header mfem.hpp mfem-performance.hpp)
|
||||
message(STATUS
|
||||
"Writing substitute header --> \"${Header}\"")
|
||||
file(WRITE "${PROJECT_BINARY_DIR}/${Header}"
|
||||
file(WRITE "${PROJECT_BINARY_DIR}/${Header}.tmp"
|
||||
"// Auto-generated file.
|
||||
#define MFEM_CONFIG_FILE \"${PROJECT_BINARY_DIR}/config/_config.hpp\"
|
||||
#include \"${PROJECT_SOURCE_DIR}/${Header}\"
|
||||
")
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${PROJECT_BINARY_DIR}/${Header}.tmp"
|
||||
"${PROJECT_BINARY_DIR}/${Header}"
|
||||
)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E remove
|
||||
"${PROJECT_BINARY_DIR}/${Header}.tmp"
|
||||
)
|
||||
|
||||
# This version will be installed in the top include directory:
|
||||
file(WRITE "${PROJECT_BINARY_DIR}/InstallHeaders/${Header}"
|
||||
file(WRITE "${PROJECT_BINARY_DIR}/InstallHeaders/${Header}.tmp"
|
||||
"// Auto-generated file.
|
||||
#include \"mfem/${Header}\"
|
||||
")
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}.tmp"
|
||||
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}"
|
||||
)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E remove
|
||||
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}.tmp"
|
||||
)
|
||||
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -659,8 +659,7 @@ The specific libraries and their options are:
|
||||
requires the PT-Scotch and Scalapack libraries as well as ParMETIS, which
|
||||
includes METIS 5 in its distribution. Starting with STRUMPACK v2.2.0, ParMETIS
|
||||
and PT-Scotch are optional dependencies.
|
||||
The support for STRUMPACK was added in MFEM v3.3.2 and it requires STRUMPACK
|
||||
2.0.0 or later.
|
||||
The support for STRUMPACK was added in MFEM v3.3.2.
|
||||
URL: http://portal.nersc.gov/project/sparse/strumpack
|
||||
Options: STRUMPACK_OPT, STRUMPACK_LIB.
|
||||
Versions: STRUMPACK >= 3.0.0.
|
||||
|
||||
@@ -157,7 +157,8 @@ set(STRUMPACK_DIR "${MFEM_DIR}/../STRUMPACK-build" CACHE PATH
|
||||
# STRUMPACK may also depend on "OpenMP", depending on how it was compiled.
|
||||
# Starting with v2.2.0 of STRUMPACK, ParMETIS and Scotch are optional.
|
||||
set(STRUMPACK_REQUIRED_PACKAGES "MPI" "MPI_Fortran" "ParMETIS" "METIS"
|
||||
"ScaLAPACK" "Scotch/ptscotch/ptscotcherr/scotch/scotcherr" CACHE STRING
|
||||
"Scotch/ptscotch/ptscotcherr/scotch/scotcherr"
|
||||
"ScaLAPACK" "LAPACK" "BLAS" CACHE STRING
|
||||
"Additional packages required by STRUMPACK.")
|
||||
# If the MPI package does not find all required Fortran libraries:
|
||||
# set(STRUMPACK_REQUIRED_LIBRARIES "gfortran" "mpi_mpifh" CACHE STRING
|
||||
|
||||
+20
-5
@@ -331,16 +331,30 @@ STRUMPACK_OPT = -I$(STRUMPACK_DIR)/include $(SCOTCH_OPT)
|
||||
STRUMPACK_LIB = -L$(STRUMPACK_DIR)/lib -lstrumpack $(MPI_FORTRAN_LIB)\
|
||||
$(SCOTCH_LIB) $(SCALAPACK_LIB)
|
||||
|
||||
# Ginkgo library configuration (currently not needed)
|
||||
# Ginkgo library configuration
|
||||
GINKGO_DIR = @MFEM_DIR@/../ginkgo/install
|
||||
GINKGO_SEARCH_DIR = $(subst @MFEM_DIR@,$(MFEM_DIR),$(GINKGO_DIR))
|
||||
GINKGO_BUILD_TYPE=Release
|
||||
ifeq ($(MFEM_USE_GINKGO),YES)
|
||||
BASE_FLAGS = -std=c++14
|
||||
endif
|
||||
GINKGO_OPT = -isystem $(GINKGO_DIR)/include
|
||||
GINKGO_LIB_DIR = $(sort $(dir $(wildcard $(GINKGO_DIR)/lib*/libginkgo*.a $(GINKGO_DIR)/lib*/libginkgo*.so $(GINKGO_DIR)/lib*/libginkgo*.dylib $(GINKGO_DIR)/lib*/libginkgo*.dll)))
|
||||
ALL_GINKGO_LIBS_DEBUG = $(notdir $(basename $(wildcard $(GINKGO_DIR)/lib*/libginkgo*d.a $(GINKGO_DIR)/lib*/libginkgo*d.so $(GINKGO_DIR)/lib*/libginkgo*d.dylib $(GINKGO_DIR)/lib*/libginkgo*d.dll)))
|
||||
ALL_GINKGO_LIBS = $(notdir $(basename $(wildcard $(GINKGO_DIR)/lib*/libginkgo*.a $(GINKGO_DIR)/lib*/libginkgo*.so $(GINKGO_DIR)/lib*/libginkgo*.dylib $(GINKGO_DIR)/lib*/libginkgo*.dll)))
|
||||
GINKGO_LIB_DIR = $(sort $(dir $(wildcard\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.a\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.so\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.dylib\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.dll)))
|
||||
GINKGO_LINK_LIB_DIR = $(GINKGO_DIR)$(subst $(GINKGO_SEARCH_DIR),,$(GINKGO_LIB_DIR))
|
||||
ALL_GINKGO_LIBS_DEBUG = $(notdir $(basename $(wildcard\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*d.a\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*d.so\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*d.dylib\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*d.dll)))
|
||||
ALL_GINKGO_LIBS = $(notdir $(basename $(wildcard\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.a\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.so\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.dylib\
|
||||
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.dll)))
|
||||
ALL_GINKGO_LIBS_RELEASE = $(filter-out $(ALL_GINKGO_LIBS_DEBUG),$(ALL_GINKGO_LIBS))
|
||||
GINKGO_LINK = $(subst libginkgo,-lginkgo,$(ALL_GINKGO_LIBS_RELEASE))
|
||||
ifeq ($(GINKGO_BUILD_TYPE),Debug)
|
||||
@@ -349,7 +363,8 @@ ifeq ($(GINKGO_BUILD_TYPE),Debug)
|
||||
endif
|
||||
else
|
||||
endif
|
||||
GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_LIB_DIR) -L$(GINKGO_LIB_DIR) $(GINKGO_LINK)
|
||||
GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_LINK_LIB_DIR) -L$(GINKGO_LINK_LIB_DIR)\
|
||||
$(GINKGO_LINK)
|
||||
|
||||
# AmgX library configuration
|
||||
AMGX_DIR = @MFEM_DIR@/../amgx
|
||||
|
||||
+3
-2
@@ -262,12 +262,13 @@ int main(int argc, char *argv[])
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
if (sp_solver)
|
||||
{
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(MPI_COMM_WORLD, argc, argv);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
strumpack->DisableMatching();
|
||||
strumpack->SetMatching(strumpack::MatchingJob::NONE);
|
||||
strumpack->SetCompression(strumpack::CompressionType::NONE);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
precond = strumpack;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Sample runs: mpirun -np 4 ex13p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex13p -m ../data/square-disc.mesh -o 2 -n 4
|
||||
// mpirun -np 4 ex13p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex13p -m ../data/beam-tet.mesh -nc -o 2 -rs 1
|
||||
// mpirun -np 4 ex13p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex13p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex13p -m ../data/fichera.mesh
|
||||
@@ -54,6 +55,7 @@ int main(int argc, char *argv[])
|
||||
int par_ref_levels = 1;
|
||||
int order = 1;
|
||||
int nev = 5;
|
||||
bool nc = false;
|
||||
bool visualization = 1;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
@@ -69,6 +71,9 @@ int main(int argc, char *argv[])
|
||||
" isoparametric space.");
|
||||
args.AddOption(&nev, "-n", "--num-eigs",
|
||||
"Number of desired eigenmodes.");
|
||||
args.AddOption(&nc, "-nc", "--non-conforming", "-c",
|
||||
"--conforming",
|
||||
"Mark the mesh as nonconforming before partitioning.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -98,6 +103,10 @@ int main(int argc, char *argv[])
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
if (nc)
|
||||
{
|
||||
mesh->EnsureNCMesh(true);
|
||||
}
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement (2 by default, or
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// mpirun -np 4 ex15p -m ../data/square-disc-nurbs.mesh
|
||||
// mpirun -np 4 ex15p -m ../data/disc-nurbs.mesh
|
||||
// mpirun -np 4 ex15p -m ../data/fichera.mesh -tf 0.5
|
||||
// mpirun -np 4 ex15p -m ../data/fichera-mixed.mesh -tf 0.5
|
||||
// mpirun -np 4 ex15p -m ../data/ball-nurbs.mesh -tf 0.5
|
||||
// mpirun -np 4 ex15p -m ../data/mobius-strip.mesh
|
||||
// mpirun -np 4 ex15p -m ../data/amr-quad.mesh
|
||||
|
||||
+29
-4
@@ -170,6 +170,7 @@ int main(int argc, char *argv[])
|
||||
bool herm_conv = true;
|
||||
bool slu_solver = false;
|
||||
bool mumps_solver = false;
|
||||
bool strumpack_solver = false;
|
||||
bool visualization = 1;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
@@ -200,6 +201,11 @@ int main(int argc, char *argv[])
|
||||
#ifdef MFEM_USE_MUMPS
|
||||
args.AddOption(&mumps_solver, "-mumps", "--mumps-solver", "-no-mumps",
|
||||
"--no-mumps-solver", "Use the MUMPS Solver.");
|
||||
#endif
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
args.AddOption(&strumpack_solver, "-strumpack", "--strumpack-solver",
|
||||
"-no-strumpack", "--no-strumpack-solver",
|
||||
"Use the STRUMPACK Solver.");
|
||||
#endif
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
@@ -209,13 +215,14 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.Parse();
|
||||
if (slu_solver && mumps_solver)
|
||||
if (slu_solver + mumps_solver + strumpack_solver > 1)
|
||||
{
|
||||
if (myid == 0)
|
||||
cout << "WARNING: Both SuperLU and MUMPS have been selected,"
|
||||
<< " please choose either one." << endl
|
||||
cout << "WARNING: More than one of SuperLU, MUMPS, and STRUMPACK have"
|
||||
<< " been selected, please choose only one." << endl
|
||||
<< " Defaulting to SuperLU." << endl;
|
||||
mumps_solver = false;
|
||||
strumpack_solver = false;
|
||||
}
|
||||
|
||||
if (iprob > 4) { iprob = 4; }
|
||||
@@ -474,6 +481,24 @@ int main(int argc, char *argv[])
|
||||
delete A;
|
||||
}
|
||||
#endif
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
if (!pa && strumpack_solver)
|
||||
{
|
||||
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
|
||||
STRUMPACKRowLocMatrix SA(*A);
|
||||
STRUMPACKSolver strumpack(MPI_COMM_WORLD, argc, argv);
|
||||
strumpack.SetPrintFactorStatistics(false);
|
||||
strumpack.SetPrintSolveStatistics(false);
|
||||
strumpack.SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack.SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
strumpack.SetMatching(strumpack::MatchingJob::NONE);
|
||||
strumpack.SetCompression(strumpack::CompressionType::NONE);
|
||||
strumpack.SetFromCommandLine();
|
||||
strumpack.SetOperator(SA);
|
||||
strumpack.Mult(B, X);
|
||||
delete A;
|
||||
}
|
||||
#endif
|
||||
#ifdef MFEM_USE_MUMPS
|
||||
if (!pa && mumps_solver)
|
||||
{
|
||||
@@ -493,7 +518,7 @@ int main(int argc, char *argv[])
|
||||
//
|
||||
// In PML: 1/mu (abs(1/det(J) J^T J) Curl E, Curl F)
|
||||
// + omega^2 * epsilon (abs(det(J) * (J^T J)^-1) * E, F)
|
||||
if (pa || (!slu_solver && !mumps_solver))
|
||||
if (pa || (!slu_solver && !mumps_solver && !strumpack_solver))
|
||||
{
|
||||
ConstantCoefficient absomeg(pow(omega, 2) * epsilon);
|
||||
RestrictedCoefficient restr_absomeg(absomeg,attr);
|
||||
|
||||
@@ -63,6 +63,7 @@ int main(int argc, char *argv[])
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
bool nc = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 1;
|
||||
|
||||
@@ -77,6 +78,9 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&nc, "-nc", "--non-conforming", "-c",
|
||||
"--conforming",
|
||||
"Mark the mesh as nonconforming before partitioning.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -102,6 +106,11 @@ int main(int argc, char *argv[])
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
if (nc)
|
||||
{
|
||||
// Can set to false to use conformal refinement for simplices.
|
||||
mesh->EnsureNCMesh(true);
|
||||
}
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh -nc -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// mpirun -np 4 ex3p -m ../data/escher.mesh
|
||||
@@ -70,6 +71,7 @@ int main(int argc, char *argv[])
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
bool nc = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
#ifdef MFEM_USE_AMGX
|
||||
@@ -87,6 +89,9 @@ int main(int argc, char *argv[])
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&nc, "-nc", "--non-conforming", "-c",
|
||||
"--conforming",
|
||||
"Mark the mesh as nonconforming before partitioning.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -124,6 +129,11 @@ int main(int argc, char *argv[])
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
if (nc)
|
||||
{
|
||||
// Can set to false to use conformal refinement for simplices.
|
||||
mesh->EnsureNCMesh(true);
|
||||
}
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
|
||||
+2
-1
@@ -450,7 +450,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Implementation of class FE_Evolution
|
||||
FE_Evolution::FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_)
|
||||
: TimeDependentOperator(M_.Height()), M(M_), K(K_), b(b_), z(M_.Height())
|
||||
: TimeDependentOperator(M_.FESpace()->GetTrueVSize()),
|
||||
M(M_), K(K_), b(b_), z(height)
|
||||
{
|
||||
Array<int> ess_tdof_list;
|
||||
if (M.GetAssemblyLevel() == AssemblyLevel::LEGACY)
|
||||
|
||||
+2
-2
@@ -659,9 +659,9 @@ int main(int argc, char *argv[])
|
||||
// Implementation of class FE_Evolution
|
||||
FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
|
||||
const Vector &b_, PrecType prec_type)
|
||||
: TimeDependentOperator(M_.Height()), b(b_),
|
||||
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize()), b(b_),
|
||||
M_solver(M_.ParFESpace()->GetComm()),
|
||||
z(M_.Height())
|
||||
z(height)
|
||||
{
|
||||
if (M_.GetAssemblyLevel()==AssemblyLevel::LEGACY)
|
||||
{
|
||||
|
||||
@@ -273,12 +273,13 @@ int main(int argc, char *argv[])
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
if (sp_solver)
|
||||
{
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
|
||||
STRUMPACKSolver * strumpack = new STRUMPACKSolver(MPI_COMM_WORLD, argc, argv);
|
||||
strumpack->SetPrintFactorStatistics(true);
|
||||
strumpack->SetPrintSolveStatistics(false);
|
||||
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
|
||||
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
|
||||
strumpack->DisableMatching();
|
||||
strumpack->SetMatching(strumpack::MatchingJob::NONE);
|
||||
strumpack->SetCompression(strumpack::CompressionType::NONE);
|
||||
strumpack->SetOperator(*Arow);
|
||||
strumpack->SetFromCommandLine();
|
||||
precond = strumpack;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// finite elements (velocity u) and piecewise discontinuous
|
||||
// polynomials (pressure p).
|
||||
//
|
||||
// The example demonstrates the use of the BlockMatrix class, as
|
||||
// The example demonstrates the use of the BlockOperator class, as
|
||||
// well as the collective saving of several grid functions in a
|
||||
// VisIt (visit.llnl.gov) visualization format.
|
||||
//
|
||||
|
||||
@@ -520,10 +520,10 @@ int main(int argc, char *argv[])
|
||||
// Implementation of class FE_Evolution
|
||||
FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
|
||||
const Vector &b_,bool M_in_lhs)
|
||||
: TimeDependentOperator(M_.Height(), 0.0,
|
||||
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize(), 0.0,
|
||||
M_in_lhs ? TimeDependentOperator::IMPLICIT
|
||||
: TimeDependentOperator::EXPLICIT),
|
||||
b(b_), comm(M_.ParFESpace()->GetComm()), M_solver(comm), z(M_.Height()),
|
||||
b(b_), comm(M_.ParFESpace()->GetComm()), M_solver(comm), z(height),
|
||||
iJacobian(NULL), rJacobian(NULL)
|
||||
{
|
||||
MAlev = M_.GetAssemblyLevel();
|
||||
|
||||
@@ -476,7 +476,8 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Implementation of class FE_Evolution
|
||||
FE_Evolution::FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_)
|
||||
: TimeDependentOperator(M_.Height()), M(M_), K(K_), b(b_), z(M_.Height())
|
||||
: TimeDependentOperator(M_.FESpace()->GetTrueVSize()),
|
||||
M(M_), K(K_), b(b_), z(height)
|
||||
{
|
||||
Array<int> ess_tdof_list;
|
||||
if (M.GetAssemblyLevel() == AssemblyLevel::LEGACY)
|
||||
|
||||
@@ -679,10 +679,10 @@ int main(int argc, char *argv[])
|
||||
// Implementation of class FE_Evolution
|
||||
FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
|
||||
const Vector &b_, PrecType prec_type)
|
||||
: TimeDependentOperator(M_.Height()),
|
||||
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize()),
|
||||
b(b_),
|
||||
M_solver(M_.ParFESpace()->GetComm()),
|
||||
z(M_.Height())
|
||||
z(height)
|
||||
{
|
||||
if (M_.GetAssemblyLevel()==AssemblyLevel::LEGACY)
|
||||
{
|
||||
|
||||
@@ -96,9 +96,6 @@ set(SRCS
|
||||
lor/lor_ads.cpp
|
||||
lor/lor_ams.cpp
|
||||
lor/lor_batched.cpp
|
||||
lor/lor_h1.cpp
|
||||
lor/lor_nd.cpp
|
||||
lor/lor_rt.cpp
|
||||
multigrid.cpp
|
||||
nonlinearform.cpp
|
||||
nonlinearform_ext.cpp
|
||||
|
||||
+85
-42
@@ -101,6 +101,7 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
|
||||
|
||||
// Copy the pointers to the integrators
|
||||
domain_integs = bf->domain_integs;
|
||||
domain_integs_marker = bf->domain_integs_marker;
|
||||
|
||||
boundary_integs = bf->boundary_integs;
|
||||
boundary_integs_marker = bf->boundary_integs_marker;
|
||||
@@ -433,7 +434,6 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
// Element-wise integration
|
||||
for (int i = 0; i < fes -> GetNE(); i++)
|
||||
{
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
if (element_matrices)
|
||||
{
|
||||
elmat_p = &(*element_matrices)(i);
|
||||
@@ -441,6 +441,9 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
else
|
||||
{
|
||||
const int elem_attr = fes->GetMesh()->GetAttribute(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
eltrans = fes->GetElementTransformation(i);
|
||||
|
||||
elmat.SetSize(0);
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
@@ -448,9 +451,8 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
|
||||
&& !domain_integs[k]->Patchwise())
|
||||
{
|
||||
const FiniteElement &fe = *fes->GetFE(i);
|
||||
eltrans = fes->GetElementTransformation(i);
|
||||
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
|
||||
domain_integs[k]->AssembleElementMatrix(*fes->GetFE(i),
|
||||
*eltrans, elemmat);
|
||||
if (elmat.Size() == 0)
|
||||
{
|
||||
elmat = elemmat;
|
||||
@@ -1222,11 +1224,14 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
|
||||
|
||||
// Copy the pointers to the integrators
|
||||
domain_integs = mbf->domain_integs;
|
||||
boundary_integs = mbf->boundary_integs;
|
||||
trace_face_integs = mbf->trace_face_integs;
|
||||
boundary_trace_face_integs = mbf->boundary_trace_face_integs;
|
||||
domain_integs_marker = mbf->domain_integs_marker;
|
||||
|
||||
boundary_integs = mbf->boundary_integs;
|
||||
boundary_integs_marker = mbf->boundary_integs_marker;
|
||||
|
||||
trace_face_integs = mbf->trace_face_integs;
|
||||
|
||||
boundary_trace_face_integs = mbf->boundary_trace_face_integs;
|
||||
boundary_trace_face_integs_marker = mbf->boundary_trace_face_integs_marker;
|
||||
|
||||
assembly = AssemblyLevel::LEGACY;
|
||||
@@ -1349,6 +1354,14 @@ void MixedBilinearForm::GetBlocks(Array2D<SparseMatrix *> &blocks) const
|
||||
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi)
|
||||
{
|
||||
domain_integs.Append (bfi);
|
||||
domain_integs_marker.Append(NULL); // NULL marker means apply everywhere
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi,
|
||||
Array<int> &elem_marker)
|
||||
{
|
||||
domain_integs.Append (bfi);
|
||||
domain_integs_marker.Append(&elem_marker);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
|
||||
@@ -1383,7 +1396,7 @@ void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi,
|
||||
boundary_trace_face_integs_marker.Append(&bdr_marker);
|
||||
}
|
||||
|
||||
void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
void MixedBilinearForm::Assemble(int skip_zeros)
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
@@ -1405,8 +1418,20 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
if (domain_integs_marker[k] != NULL)
|
||||
{
|
||||
MFEM_VERIFY(domain_integs_marker[k]->Size() ==
|
||||
(mesh->attributes.Size() ? mesh->attributes.Max() : 0),
|
||||
"invalid element marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < test_fes -> GetNE(); i++)
|
||||
{
|
||||
const int elem_attr = mesh->GetAttribute(i);
|
||||
dom_dof_trans = trial_fes -> GetElementVDofs (i, trial_vdofs);
|
||||
ran_dof_trans = test_fes -> GetElementVDofs (i, test_vdofs);
|
||||
eltrans = test_fes -> GetElementTransformation (i);
|
||||
@@ -1415,10 +1440,14 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
elmat = 0.0;
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
domain_integs[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
|
||||
*test_fes -> GetFE(i),
|
||||
*eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
if (domain_integs_marker[k] == NULL ||
|
||||
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
|
||||
{
|
||||
domain_integs[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
|
||||
*test_fes -> GetFE(i),
|
||||
*eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
@@ -1941,41 +1970,56 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
return;
|
||||
}
|
||||
|
||||
Array<int> dom_vdofs, ran_vdofs;
|
||||
ElementTransformation *T;
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation * dom_dof_trans;
|
||||
DofTransformation * ran_dof_trans;
|
||||
const FiniteElement *dom_fe, *ran_fe;
|
||||
DenseMatrix totelmat, elmat;
|
||||
DenseMatrix elmat;
|
||||
|
||||
Mesh *mesh = test_fes->GetMesh();
|
||||
|
||||
if (mat == NULL)
|
||||
{
|
||||
mat = new SparseMatrix(height, width);
|
||||
}
|
||||
|
||||
if (domain_integs.Size() > 0)
|
||||
if (domain_integs.Size())
|
||||
{
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
if (domain_integs_marker[k] != NULL)
|
||||
{
|
||||
MFEM_VERIFY(domain_integs_marker[k]->Size() ==
|
||||
(mesh->attributes.Size() ? mesh->attributes.Max() : 0),
|
||||
"invalid element marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < test_fes->GetNE(); i++)
|
||||
{
|
||||
dom_dof_trans = trial_fes->GetElementVDofs(i, dom_vdofs);
|
||||
ran_dof_trans = test_fes->GetElementVDofs(i, ran_vdofs);
|
||||
T = test_fes->GetElementTransformation(i);
|
||||
dom_fe = trial_fes->GetFE(i);
|
||||
ran_fe = test_fes->GetFE(i);
|
||||
const int elem_attr = mesh->GetAttribute(i);
|
||||
dom_dof_trans = trial_fes->GetElementVDofs(i, trial_vdofs);
|
||||
ran_dof_trans = test_fes->GetElementVDofs(i, test_vdofs);
|
||||
eltrans = test_fes->GetElementTransformation(i);
|
||||
|
||||
domain_integs[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
|
||||
totelmat);
|
||||
for (int j = 1; j < domain_integs.Size(); j++)
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
elmat = 0.0;
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
domain_integs[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
|
||||
elmat);
|
||||
totelmat += elmat;
|
||||
if (domain_integs_marker[k] == NULL ||
|
||||
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
|
||||
{
|
||||
domain_integs[k]->AssembleElementMatrix2(*trial_fes->GetFE(i),
|
||||
*test_fes->GetFE(i),
|
||||
*eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformPrimal(ran_dof_trans, dom_dof_trans, totelmat);
|
||||
TransformPrimal(ran_dof_trans, dom_dof_trans, elemmat);
|
||||
}
|
||||
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
|
||||
mat->SetSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1984,21 +2028,20 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
const int nfaces = test_fes->GetMesh()->GetNumFaces();
|
||||
for (int i = 0; i < nfaces; i++)
|
||||
{
|
||||
trial_fes->GetFaceVDofs(i, dom_vdofs);
|
||||
test_fes->GetFaceVDofs(i, ran_vdofs);
|
||||
T = test_fes->GetMesh()->GetFaceTransformation(i);
|
||||
dom_fe = trial_fes->GetFaceElement(i);
|
||||
ran_fe = test_fes->GetFaceElement(i);
|
||||
trial_fes->GetFaceVDofs(i, trial_vdofs);
|
||||
test_fes->GetFaceVDofs(i, test_vdofs);
|
||||
eltrans = test_fes->GetMesh()->GetFaceTransformation(i);
|
||||
|
||||
trace_face_integs[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
|
||||
totelmat);
|
||||
for (int j = 1; j < trace_face_integs.Size(); j++)
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
elmat = 0.0;
|
||||
for (int k = 0; k < trace_face_integs.Size(); k++)
|
||||
{
|
||||
trace_face_integs[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
|
||||
elmat);
|
||||
totelmat += elmat;
|
||||
trace_face_integs[k]->AssembleElementMatrix2(*trial_fes->GetFaceElement(i),
|
||||
*test_fes->GetFaceElement(i),
|
||||
*eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
|
||||
mat->SetSubMatrix(test_vdofs, trial_vdofs, elmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-7
@@ -100,7 +100,7 @@ protected:
|
||||
/// Includes all by default.
|
||||
/// 0 - ignore attribute
|
||||
/// 1 - include attribute
|
||||
Array<Array<int>*> domain_integs_marker;
|
||||
Array<Array<int>*> domain_integs_marker; ///< Entries are not owned.
|
||||
|
||||
/// Set of Boundary Integrators to be applied.
|
||||
Array<BilinearFormIntegrator*> boundary_integs;
|
||||
@@ -722,10 +722,13 @@ protected:
|
||||
|
||||
/// Domain integrators.
|
||||
Array<BilinearFormIntegrator*> domain_integs;
|
||||
/// Entries are not owned.
|
||||
Array<Array<int>*> domain_integs_marker;
|
||||
|
||||
/// Boundary integrators.
|
||||
Array<BilinearFormIntegrator*> boundary_integs;
|
||||
Array<Array<int>*> boundary_integs_marker; ///< Entries are not owned.
|
||||
/// Entries are not owned.
|
||||
Array<Array<int>*> boundary_integs_marker;
|
||||
|
||||
/// Trace face (skeleton) integrators.
|
||||
Array<BilinearFormIntegrator*> trace_face_integs;
|
||||
@@ -805,12 +808,16 @@ public:
|
||||
/// Adds a domain integrator. Assumes ownership of @a bfi.
|
||||
void AddDomainIntegrator(BilinearFormIntegrator *bfi);
|
||||
|
||||
/// Adds a domain integrator. Assumes ownership of @a bfi.
|
||||
void AddDomainIntegrator(BilinearFormIntegrator *bfi,
|
||||
Array<int> &elem_marker);
|
||||
|
||||
/// Adds a boundary integrator. Assumes ownership of @a bfi.
|
||||
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi);
|
||||
|
||||
/// Adds a boundary integrator. Assumes ownership of @a bfi.
|
||||
void AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
|
||||
Array<int> &bdr_marker);
|
||||
void AddBoundaryIntegrator(BilinearFormIntegrator * bfi,
|
||||
Array<int> &bdr_marker);
|
||||
|
||||
/** @brief Add a trace face integrator. Assumes ownership of @a bfi.
|
||||
|
||||
@@ -820,14 +827,18 @@ public:
|
||||
void AddTraceFaceIntegrator(BilinearFormIntegrator *bfi);
|
||||
|
||||
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
|
||||
void AddBdrTraceFaceIntegrator (BilinearFormIntegrator * bfi);
|
||||
void AddBdrTraceFaceIntegrator(BilinearFormIntegrator * bfi);
|
||||
|
||||
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
|
||||
void AddBdrTraceFaceIntegrator (BilinearFormIntegrator * bfi,
|
||||
Array<int> &bdr_marker);
|
||||
void AddBdrTraceFaceIntegrator(BilinearFormIntegrator * bfi,
|
||||
Array<int> &bdr_marker);
|
||||
|
||||
/// Access all integrators added with AddDomainIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetDBFI() { return &domain_integs; }
|
||||
/** @brief Access all domain markers added with AddDomainIntegrator().
|
||||
If no marker was specified when the integrator was added, the
|
||||
corresponding pointer (to Array<int>) will be NULL. */
|
||||
Array<Array<int>*> *GetDBFI_Marker() { return &domain_integs_marker; }
|
||||
|
||||
/// Access all integrators added with AddBoundaryIntegrator().
|
||||
Array<BilinearFormIntegrator*> *GetBBFI() { return &boundary_integs; }
|
||||
@@ -1065,6 +1076,9 @@ public:
|
||||
/// Adds a domain interpolator. Assumes ownership of @a di.
|
||||
void AddDomainInterpolator(DiscreteInterpolator *di)
|
||||
{ AddDomainIntegrator(di); }
|
||||
void AddDomainInterpolator(DiscreteInterpolator *di,
|
||||
Array<int> &elem_marker)
|
||||
{ AddDomainIntegrator(di, elem_marker); }
|
||||
|
||||
/// Adds a trace face interpolator. Assumes ownership of @a di.
|
||||
void AddTraceFaceInterpolator(DiscreteInterpolator *di)
|
||||
@@ -1072,6 +1086,7 @@ public:
|
||||
|
||||
/// Access all interpolators added with AddDomainInterpolator().
|
||||
Array<BilinearFormIntegrator*> *GetDI() { return &domain_integs; }
|
||||
Array<Array<int>*> *GetDI_Marker() { return &domain_integs_marker; }
|
||||
|
||||
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
|
||||
/** This method must be called before assembly. */
|
||||
|
||||
@@ -303,7 +303,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
|
||||
std::unordered_map<int,int> f_to_be;
|
||||
for (int i = 0; i < mesh.GetNBE(); ++i)
|
||||
{
|
||||
const int f = mesh.GetBdrElementEdgeIndex(i);
|
||||
const int f = mesh.GetBdrElementFaceIndex(i);
|
||||
f_to_be[f] = i;
|
||||
}
|
||||
const int nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
|
||||
|
||||
+10
-3
@@ -220,12 +220,12 @@ double TransformedCoefficient::Eval(ElementTransformation &T,
|
||||
{
|
||||
if (Q2)
|
||||
{
|
||||
return (*Transform2)(Q1->Eval(T, ip, GetTime()),
|
||||
Q2->Eval(T, ip, GetTime()));
|
||||
return Transform2(Q1->Eval(T, ip, GetTime()),
|
||||
Q2->Eval(T, ip, GetTime()));
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*Transform1)(Q1->Eval(T, ip, GetTime()));
|
||||
return Transform1(Q1->Eval(T, ip, GetTime()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1592,6 +1592,10 @@ void VectorQuadratureFunctionCoefficient::Eval(Vector &V,
|
||||
QuadF.HostRead();
|
||||
|
||||
const int el_idx = QuadF.GetSpace()->GetEntityIndex(T);
|
||||
// Handle the case of "interior boundary elements" and FaceQuadratureSpace
|
||||
// with FaceType::Boundary.
|
||||
if (el_idx < 0) { V = 0.0; return; }
|
||||
|
||||
const int ip_idx = QuadF.GetSpace()->GetPermutedIndex(el_idx, ip.index);
|
||||
|
||||
if (index == 0 && vdim == QuadF.GetVDim())
|
||||
@@ -1629,6 +1633,9 @@ double QuadratureFunctionCoefficient::Eval(ElementTransformation &T,
|
||||
QuadF.HostRead();
|
||||
Vector temp(1);
|
||||
const int el_idx = QuadF.GetSpace()->GetEntityIndex(T);
|
||||
// Handle the case of "interior boundary elements" and FaceQuadratureSpace
|
||||
// with FaceType::Boundary.
|
||||
if (el_idx < 0) { return 0.0; }
|
||||
const int ip_idx = QuadF.GetSpace()->GetPermutedIndex(el_idx, ip.index);
|
||||
QuadF.GetValues(el_idx, ip_idx, temp);
|
||||
return temp[0];
|
||||
|
||||
+6
-6
@@ -422,15 +422,15 @@ class TransformedCoefficient : public Coefficient
|
||||
private:
|
||||
Coefficient * Q1;
|
||||
Coefficient * Q2;
|
||||
double (*Transform1)(double);
|
||||
double (*Transform2)(double,double);
|
||||
std::function<double(double)> Transform1;
|
||||
std::function<double(double, double)> Transform2;
|
||||
|
||||
public:
|
||||
TransformedCoefficient (Coefficient * q,double (*F)(double))
|
||||
: Q1(q), Transform1(F) { Q2 = 0; Transform2 = 0; }
|
||||
TransformedCoefficient (Coefficient * q, std::function<double(double)> F)
|
||||
: Q1(q), Transform1(std::move(F)) { Q2 = 0; Transform2 = 0; }
|
||||
TransformedCoefficient (Coefficient * q1,Coefficient * q2,
|
||||
double (*F)(double,double))
|
||||
: Q1(q1), Q2(q2), Transform2(F) { Transform1 = 0; }
|
||||
std::function<double(double, double)> F)
|
||||
: Q1(q1), Q2(q2), Transform2(std::move(F)) { Transform1 = 0; }
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(double t);
|
||||
|
||||
+169
-172
@@ -14,175 +14,166 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
void DofTransformation::TransformPrimal(double *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
dof_trans_->TransformPrimal(Fo_, &v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
dof_trans_->TransformPrimal(Fo_, vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DofTransformation::InvTransformPrimal(double *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
int size = dof_trans_->Height();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
dof_trans_->InvTransformPrimal(Fo_, &v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
dof_trans_->InvTransformPrimal(Fo_, vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DofTransformation::TransformDual(double *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
dof_trans_->TransformDual(Fo_, &v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
dof_trans_->TransformDual(Fo_, vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DofTransformation::InvTransformDual(double *v) const
|
||||
{
|
||||
MFEM_ASSERT(dof_trans_,
|
||||
"DofTransformation has no local transformation, call "
|
||||
"SetDofTransformation first!");
|
||||
int size = dof_trans_->Size();
|
||||
|
||||
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
dof_trans_->InvTransformDual(Fo_, &v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
dof_trans_->InvTransformDual(Fo_, vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
if (ran_dof_trans && dom_dof_trans)
|
||||
{
|
||||
ran_dof_trans->TransformPrimalCols(elmat);
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
else if (ran_dof_trans)
|
||||
// No action if both transformations are NULL
|
||||
if (ran_dof_trans)
|
||||
{
|
||||
ran_dof_trans->TransformPrimalCols(elmat);
|
||||
}
|
||||
else if (dom_dof_trans)
|
||||
if (dom_dof_trans)
|
||||
{
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If both transformations are NULL this function should not be called
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
if (ran_dof_trans && dom_dof_trans)
|
||||
{
|
||||
ran_dof_trans->TransformDualCols(elmat);
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
else if (ran_dof_trans)
|
||||
// No action if both transformations are NULL
|
||||
if (ran_dof_trans)
|
||||
{
|
||||
ran_dof_trans->TransformDualCols(elmat);
|
||||
}
|
||||
else if (dom_dof_trans)
|
||||
if (dom_dof_trans)
|
||||
{
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If both transformations are NULL this function should not be called
|
||||
}
|
||||
}
|
||||
|
||||
void StatelessVDofTransformation::TransformPrimal(const Array<int> & face_ori,
|
||||
double *v) const
|
||||
{
|
||||
int size = sdoftrans_->Size();
|
||||
|
||||
if ((Ordering::Type)ordering_ == Ordering::byNODES || vdim_ == 1)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
sdoftrans_->TransformPrimal(face_ori, &v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
sdoftrans_->TransformPrimal(face_ori, vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatelessVDofTransformation::InvTransformPrimal(
|
||||
const Array<int> & face_ori,
|
||||
double *v) const
|
||||
{
|
||||
int size = sdoftrans_->Height();
|
||||
|
||||
if ((Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
sdoftrans_->InvTransformPrimal(face_ori, &v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
sdoftrans_->InvTransformPrimal(face_ori, vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatelessVDofTransformation::TransformDual(const Array<int> & face_ori,
|
||||
double *v) const
|
||||
{
|
||||
int size = sdoftrans_->Size();
|
||||
|
||||
if ((Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
sdoftrans_->TransformDual(face_ori, &v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
sdoftrans_->TransformDual(face_ori, vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatelessVDofTransformation::InvTransformDual(const Array<int> & face_ori,
|
||||
double *v) const
|
||||
{
|
||||
int size = sdoftrans_->Size();
|
||||
|
||||
if ((Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
sdoftrans_->InvTransformDual(face_ori, &v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
sdoftrans_->InvTransformDual(face_ori, vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ordering (i0j0, i1j0, i0j1, i1j1), each row is a column major matrix
|
||||
const double ND_StatelessDofTransformation::T_data[24] =
|
||||
const double ND_DofTransformation::T_data[24] =
|
||||
{
|
||||
1.0, 0.0, 0.0, 1.0,
|
||||
-1.0, -1.0, 0.0, 1.0,
|
||||
@@ -192,11 +183,11 @@ const double ND_StatelessDofTransformation::T_data[24] =
|
||||
0.0, 1.0, 1.0, 0.0
|
||||
};
|
||||
|
||||
const DenseTensor ND_StatelessDofTransformation
|
||||
::T(const_cast<double*>(ND_StatelessDofTransformation::T_data), 2, 2, 6);
|
||||
const DenseTensor ND_DofTransformation
|
||||
::T(const_cast<double *>(ND_DofTransformation::T_data), 2, 2, 6);
|
||||
|
||||
// ordering (i0j0, i1j0, i0j1, i1j1), each row is a column major matrix
|
||||
const double ND_StatelessDofTransformation::TInv_data[24] =
|
||||
const double ND_DofTransformation::TInv_data[24] =
|
||||
{
|
||||
1.0, 0.0, 0.0, 1.0,
|
||||
-1.0, -1.0, 0.0, 1.0,
|
||||
@@ -206,12 +197,11 @@ const double ND_StatelessDofTransformation::TInv_data[24] =
|
||||
0.0, 1.0, 1.0, 0.0
|
||||
};
|
||||
|
||||
const DenseTensor ND_StatelessDofTransformation
|
||||
::TInv(const_cast<double*>(TInv_data), 2, 2, 6);
|
||||
const DenseTensor ND_DofTransformation
|
||||
::TInv(const_cast<double *>(TInv_data), 2, 2, 6);
|
||||
|
||||
ND_StatelessDofTransformation::ND_StatelessDofTransformation(int size, int p,
|
||||
int num_edges,
|
||||
int num_tri_faces)
|
||||
ND_DofTransformation::ND_DofTransformation(int size, int p, int num_edges,
|
||||
int num_tri_faces)
|
||||
: StatelessDofTransformation(size)
|
||||
, order(p)
|
||||
, nedofs(p)
|
||||
@@ -221,18 +211,19 @@ ND_StatelessDofTransformation::ND_StatelessDofTransformation(int size, int p,
|
||||
{
|
||||
}
|
||||
|
||||
void ND_StatelessDofTransformation::TransformPrimal(const Array<int> & Fo,
|
||||
double *v) const
|
||||
void ND_DofTransformation::TransformPrimal(const Array<int> & Fo,
|
||||
double *v) const
|
||||
{
|
||||
// Return immediately when no face DoFs are present
|
||||
if (nfdofs < 2) { return; }
|
||||
if (IsIdentity()) { return; }
|
||||
|
||||
MFEM_VERIFY(Fo.Size() >= nfaces,
|
||||
"Face orientation array is shorter than the number of faces in "
|
||||
"ND_StatelessDofTransformation");
|
||||
"ND_DofTransformation");
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
DenseMatrix T2;
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<nfaces; f++)
|
||||
@@ -240,23 +231,25 @@ void ND_StatelessDofTransformation::TransformPrimal(const Array<int> & Fo,
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
|
||||
T(Fo[f]).Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
T2.UseExternalData(const_cast<double *>(T.GetData(Fo[f])), 2, 2);
|
||||
T2.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ND_StatelessDofTransformation::InvTransformPrimal(const Array<int> & Fo,
|
||||
double *v) const
|
||||
void ND_DofTransformation::InvTransformPrimal(const Array<int> & Fo,
|
||||
double *v) const
|
||||
{
|
||||
// Return immediately when no face DoFs are present
|
||||
if (nfdofs < 2) { return; }
|
||||
if (IsIdentity()) { return; }
|
||||
|
||||
MFEM_VERIFY(Fo.Size() >= nfaces,
|
||||
"Face orientation array is shorter than the number of faces in "
|
||||
"ND_StatelessDofTransformation");
|
||||
"ND_DofTransformation");
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
DenseMatrix T2Inv;
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<nfaces; f++)
|
||||
@@ -264,23 +257,24 @@ void ND_StatelessDofTransformation::InvTransformPrimal(const Array<int> & Fo,
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
|
||||
TInv(Fo[f]).Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
T2Inv.UseExternalData(const_cast<double *>(TInv.GetData(Fo[f])), 2, 2);
|
||||
T2Inv.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ND_StatelessDofTransformation::TransformDual(const Array<int> & Fo,
|
||||
double *v) const
|
||||
void ND_DofTransformation::TransformDual(const Array<int> & Fo, double *v) const
|
||||
{
|
||||
// Return immediately when no face DoFs are present
|
||||
if (nfdofs < 2) { return; }
|
||||
if (IsIdentity()) { return; }
|
||||
|
||||
MFEM_VERIFY(Fo.Size() >= nfaces,
|
||||
"Face orientation array is shorter than the number of faces in "
|
||||
"ND_StatelessDofTransformation");
|
||||
"ND_DofTransformation");
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
DenseMatrix T2Inv;
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<nfaces; f++)
|
||||
@@ -288,23 +282,25 @@ void ND_StatelessDofTransformation::TransformDual(const Array<int> & Fo,
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
|
||||
TInv(Fo[f]).MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
T2Inv.UseExternalData(const_cast<double *>(TInv.GetData(Fo[f])), 2, 2);
|
||||
T2Inv.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ND_StatelessDofTransformation::InvTransformDual(const Array<int> & Fo,
|
||||
double *v) const
|
||||
void ND_DofTransformation::InvTransformDual(const Array<int> & Fo,
|
||||
double *v) const
|
||||
{
|
||||
// Return immediately when no face DoFs are present
|
||||
if (nfdofs < 2) { return; }
|
||||
if (IsIdentity()) { return; }
|
||||
|
||||
MFEM_VERIFY(Fo.Size() >= nfaces,
|
||||
"Face orientation array is shorter than the number of faces in "
|
||||
"ND_StatelessDofTransformation");
|
||||
"ND_DofTransformation");
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
DenseMatrix T2;
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<nfaces; f++)
|
||||
@@ -312,7 +308,8 @@ void ND_StatelessDofTransformation::InvTransformDual(const Array<int> & Fo,
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
|
||||
T(Fo[f]).MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
T2.UseExternalData(const_cast<double *>(T.GetData(Fo[f])), 2, 2);
|
||||
T2.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+77
-251
@@ -80,6 +80,9 @@ public:
|
||||
inline int Width() const { return size_; }
|
||||
inline int NumCols() const { return size_; }
|
||||
|
||||
/// If the DofTransformation performs no transformation
|
||||
virtual bool IsIdentity() const = 0;
|
||||
|
||||
/** Transform local DoFs to align with the global DoFs. For example, this
|
||||
transformation can be used to map the local vector computed by
|
||||
FiniteElement::Project() to the transformed vector stored within a
|
||||
@@ -115,6 +118,8 @@ public:
|
||||
inline void InvTransformDual(const Array<int> & face_orientation,
|
||||
Vector &v) const
|
||||
{ InvTransformDual(face_orientation, v.GetData()); }
|
||||
|
||||
virtual ~StatelessDofTransformation() = default;
|
||||
};
|
||||
|
||||
/** The DofTransformation class is an extension of the
|
||||
@@ -133,35 +138,76 @@ public:
|
||||
transferring finite element degrees of freedom between different meshes.
|
||||
For examples of its use see the TransferMap used by the SubMesh class.
|
||||
*/
|
||||
class DofTransformation : virtual public StatelessDofTransformation
|
||||
class DofTransformation
|
||||
{
|
||||
protected:
|
||||
Array<int> Fo;
|
||||
|
||||
DofTransformation(int size)
|
||||
: StatelessDofTransformation(size) {}
|
||||
Array<int> Fo_;
|
||||
const StatelessDofTransformation * dof_trans_;
|
||||
int vdim_;
|
||||
int ordering_;
|
||||
|
||||
public:
|
||||
/** @brief Default constructor which requires that SetDofTransformation be
|
||||
called before use. */
|
||||
DofTransformation(int vdim = 1, int ordering = 0)
|
||||
: dof_trans_(NULL)
|
||||
, vdim_(vdim)
|
||||
, ordering_(ordering)
|
||||
{}
|
||||
|
||||
/// Constructor with a known StatelessDofTransformation
|
||||
DofTransformation(const StatelessDofTransformation & dof_trans,
|
||||
int vdim = 1, int ordering = 0)
|
||||
: dof_trans_(&dof_trans)
|
||||
, vdim_(vdim)
|
||||
, ordering_(ordering)
|
||||
{}
|
||||
|
||||
/** @brief Configure the transformation using face orientations for the
|
||||
current element. */
|
||||
/// The face_orientation array can be obtained from Mesh::GetElementFaces.
|
||||
inline void SetFaceOrientations(const Array<int> & face_orientation)
|
||||
{ Fo = face_orientation; }
|
||||
inline void SetFaceOrientations(const Array<int> & Fo)
|
||||
{ Fo_ = Fo; }
|
||||
|
||||
inline const Array<int> & GetFaceOrientations() const { return Fo; }
|
||||
/// Return the face orientations for the current element
|
||||
inline const Array<int> & GetFaceOrientations() const { return Fo_; }
|
||||
|
||||
using StatelessDofTransformation::TransformPrimal;
|
||||
using StatelessDofTransformation::InvTransformPrimal;
|
||||
using StatelessDofTransformation::TransformDual;
|
||||
using StatelessDofTransformation::InvTransformDual;
|
||||
/// Set or change the nested StatelessDofTransformation object
|
||||
inline void SetDofTransformation(const StatelessDofTransformation & dof_trans)
|
||||
{
|
||||
dof_trans_ = &dof_trans;
|
||||
}
|
||||
inline void SetDofTransformation(const StatelessDofTransformation * dof_trans)
|
||||
{
|
||||
dof_trans_ = dof_trans;
|
||||
}
|
||||
|
||||
/// Return the nested StatelessDofTransformation object
|
||||
inline const StatelessDofTransformation * GetDofTransformation() const
|
||||
{ return dof_trans_; }
|
||||
|
||||
/// Set or change the vdim and ordering parameter
|
||||
inline void SetVDim(int vdim = 1, int ordering = 0)
|
||||
{
|
||||
vdim_ = vdim;
|
||||
ordering_ = ordering;
|
||||
}
|
||||
|
||||
/// Return the current vdim value
|
||||
inline int GetVDim() const { return vdim_; }
|
||||
|
||||
inline int Size() const { return dof_trans_->Size(); }
|
||||
inline int Height() const { return dof_trans_->Height(); }
|
||||
inline int NumRows() const { return dof_trans_->NumRows(); }
|
||||
inline int Width() const { return dof_trans_->Width(); }
|
||||
inline int NumCols() const { return dof_trans_->NumCols(); }
|
||||
inline bool IsIdentity() const { return dof_trans_->IsIdentity(); }
|
||||
|
||||
/** Transform local DoFs to align with the global DoFs. For example, this
|
||||
transformation can be used to map the local vector computed by
|
||||
FiniteElement::Project() to the transformed vector stored within a
|
||||
GridFunction object. */
|
||||
inline void TransformPrimal(double *v) const
|
||||
{ TransformPrimal(Fo, v); }
|
||||
void TransformPrimal(double *v) const;
|
||||
inline void TransformPrimal(Vector &v) const
|
||||
{ TransformPrimal(v.GetData()); }
|
||||
|
||||
@@ -179,21 +225,18 @@ public:
|
||||
transform the vector obtained using GridFunction::GetSubVector before it
|
||||
can be used to compute a local interpolation.
|
||||
*/
|
||||
inline void InvTransformPrimal(double *v) const
|
||||
{ InvTransformPrimal(Fo, v); }
|
||||
void InvTransformPrimal(double *v) const;
|
||||
inline void InvTransformPrimal(Vector &v) const
|
||||
{ InvTransformPrimal(v.GetData()); }
|
||||
|
||||
/** Transform dual DoFs as computed by a LinearFormIntegrator before summing
|
||||
into a LinearForm object. */
|
||||
inline void TransformDual(double *v) const
|
||||
{ TransformDual(Fo, v); }
|
||||
void TransformDual(double *v) const;
|
||||
inline void TransformDual(Vector &v) const
|
||||
{ TransformDual(v.GetData()); }
|
||||
|
||||
/** Inverse Transform dual DoFs */
|
||||
inline void InvTransformDual(double *v) const
|
||||
{ InvTransformDual(Fo, v); }
|
||||
void InvTransformDual(double *v) const;
|
||||
inline void InvTransformDual(Vector &v) const
|
||||
{ InvTransformDual(v.GetData()); }
|
||||
|
||||
@@ -225,8 +268,6 @@ public:
|
||||
TransformDual(V.GetColumn(c));
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~DofTransformation() = default;
|
||||
};
|
||||
|
||||
/** Transform a matrix of DoFs entries from different finite element spaces as
|
||||
@@ -245,145 +286,6 @@ void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** The StatelessVDofTransformation class implements a nested transformation
|
||||
where an arbitrary StatelessDofTransformation is replicated with a
|
||||
vdim >= 1.
|
||||
*/
|
||||
class StatelessVDofTransformation : virtual public StatelessDofTransformation
|
||||
{
|
||||
protected:
|
||||
int vdim_;
|
||||
int ordering_;
|
||||
StatelessDofTransformation * sdoftrans_;
|
||||
|
||||
public:
|
||||
/** @brief Default constructor which requires that SetDofTransformation be
|
||||
called before use. */
|
||||
StatelessVDofTransformation(int vdim = 1, int ordering = 0)
|
||||
: StatelessDofTransformation(0)
|
||||
, vdim_(vdim)
|
||||
, ordering_(ordering)
|
||||
, sdoftrans_(NULL)
|
||||
{}
|
||||
|
||||
/// Constructor with a known StatelessDofTransformation
|
||||
StatelessVDofTransformation(StatelessDofTransformation & doftrans,
|
||||
int vdim = 1,
|
||||
int ordering = 0)
|
||||
: StatelessDofTransformation(vdim * doftrans.Size())
|
||||
, vdim_(vdim)
|
||||
, ordering_(ordering)
|
||||
, sdoftrans_(&doftrans)
|
||||
{}
|
||||
|
||||
/// Set or change the vdim parameter
|
||||
inline void SetVDim(int vdim)
|
||||
{
|
||||
vdim_ = vdim;
|
||||
if (sdoftrans_)
|
||||
{
|
||||
size_ = vdim_ * sdoftrans_->Size();
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the current vdim value
|
||||
inline int GetVDim() const { return vdim_; }
|
||||
|
||||
/// Set or change the nested StatelessDofTransformation object
|
||||
inline void SetDofTransformation(StatelessDofTransformation & doftrans)
|
||||
{
|
||||
size_ = vdim_ * doftrans.Size();
|
||||
sdoftrans_ = &doftrans;
|
||||
}
|
||||
|
||||
/// Return the nested StatelessDofTransformation object
|
||||
inline StatelessDofTransformation * GetDofTransformation() const
|
||||
{ return sdoftrans_; }
|
||||
|
||||
using StatelessDofTransformation::TransformPrimal;
|
||||
using StatelessDofTransformation::InvTransformPrimal;
|
||||
using StatelessDofTransformation::TransformDual;
|
||||
using StatelessDofTransformation::InvTransformDual;
|
||||
|
||||
/** Specializations of these base class methods which account for the vdim
|
||||
and ordering of the full set of DoFs.
|
||||
*/
|
||||
void TransformPrimal(const Array<int> & face_ori, double *v) const;
|
||||
void InvTransformPrimal(const Array<int> & face_ori, double *v) const;
|
||||
void TransformDual(const Array<int> & face_ori, double *v) const;
|
||||
void InvTransformDual(const Array<int> & face_ori, double *v) const;
|
||||
};
|
||||
|
||||
/** The VDofTransformation class implements a nested transformation where an
|
||||
arbitrary DofTransformation is replicated with a vdim >= 1.
|
||||
*/
|
||||
class VDofTransformation : public StatelessVDofTransformation,
|
||||
public DofTransformation
|
||||
{
|
||||
protected:
|
||||
DofTransformation * doftrans_;
|
||||
|
||||
public:
|
||||
/** @brief Default constructor which requires that SetDofTransformation be
|
||||
called before use. */
|
||||
VDofTransformation(int vdim = 1, int ordering = 0)
|
||||
: StatelessDofTransformation(0)
|
||||
, StatelessVDofTransformation(vdim, ordering)
|
||||
, DofTransformation(0)
|
||||
, doftrans_(NULL)
|
||||
{}
|
||||
|
||||
/// Constructor with a known DofTransformation
|
||||
/// @note The face orientations in @a doftrans will be copied into the
|
||||
/// new VDofTransformation object.
|
||||
VDofTransformation(DofTransformation & doftrans, int vdim = 1,
|
||||
int ordering = 0)
|
||||
: StatelessDofTransformation(vdim * doftrans.Size())
|
||||
, StatelessVDofTransformation(doftrans, vdim, ordering)
|
||||
, DofTransformation(vdim * doftrans.Size())
|
||||
, doftrans_(&doftrans)
|
||||
{
|
||||
DofTransformation::SetFaceOrientations(doftrans.GetFaceOrientations());
|
||||
}
|
||||
|
||||
using StatelessVDofTransformation::SetDofTransformation;
|
||||
|
||||
/// Set or change the nested DofTransformation object
|
||||
/// @note The face orientations in @a doftrans will be copied into the
|
||||
/// VDofTransformation object.
|
||||
void SetDofTransformation(DofTransformation & doftrans)
|
||||
{
|
||||
doftrans_ = &doftrans;
|
||||
StatelessVDofTransformation::SetDofTransformation(doftrans);
|
||||
DofTransformation::SetFaceOrientations(doftrans.GetFaceOrientations());
|
||||
}
|
||||
|
||||
/// Return the nested DofTransformation object
|
||||
inline DofTransformation * GetDofTransformation() const { return doftrans_; }
|
||||
|
||||
/// Set new face orientations in both the VDofTransformation and the
|
||||
/// DofTransformation contained within (if there is one).
|
||||
inline void SetFaceOrientations(const Array<int> & face_orientation)
|
||||
{
|
||||
DofTransformation::SetFaceOrientations(face_orientation);
|
||||
if (doftrans_) { doftrans_->SetFaceOrientations(face_orientation); }
|
||||
}
|
||||
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
using DofTransformation::InvTransformDual;
|
||||
|
||||
inline void TransformPrimal(double *v) const
|
||||
{ TransformPrimal(Fo, v); }
|
||||
inline void InvTransformPrimal(double *v) const
|
||||
{ InvTransformPrimal(Fo, v); }
|
||||
inline void TransformDual(double *v) const
|
||||
{ TransformDual(Fo, v); }
|
||||
inline void InvTransformDual(double *v) const
|
||||
{ InvTransformDual(Fo, v); }
|
||||
};
|
||||
|
||||
/** Abstract base class for high-order Nedelec spaces on elements with
|
||||
triangular faces.
|
||||
|
||||
@@ -396,7 +298,7 @@ public:
|
||||
be accessed as DenseMatrices using the GetFaceTransform() and
|
||||
GetFaceInverseTransform() methods.
|
||||
*/
|
||||
class ND_StatelessDofTransformation : virtual public StatelessDofTransformation
|
||||
class ND_DofTransformation : public StatelessDofTransformation
|
||||
{
|
||||
private:
|
||||
static const double T_data[24];
|
||||
@@ -410,8 +312,7 @@ protected:
|
||||
const int nedges; // number of edges per element
|
||||
const int nfaces; // number of triangular faces per element
|
||||
|
||||
ND_StatelessDofTransformation(int size, int order,
|
||||
int num_edges, int num_tri_faces);
|
||||
ND_DofTransformation(int size, int order, int num_edges, int num_tri_faces);
|
||||
|
||||
public:
|
||||
// Return the 2x2 transformation operator for the given face orientation
|
||||
@@ -421,116 +322,41 @@ public:
|
||||
static const DenseMatrix & GetFaceInverseTransform(int ori)
|
||||
{ return TInv(ori); }
|
||||
|
||||
void TransformPrimal(const Array<int> & face_orientation,
|
||||
double *v) const;
|
||||
bool IsIdentity() const override { return nfdofs < 2; }
|
||||
|
||||
void InvTransformPrimal(const Array<int> & face_orientation,
|
||||
double *v) const;
|
||||
|
||||
void TransformDual(const Array<int> & face_orientation,
|
||||
double *v) const;
|
||||
|
||||
void InvTransformDual(const Array<int> & face_orientation,
|
||||
double *v) const;
|
||||
void TransformPrimal(const Array<int> & Fo, double *v) const override;
|
||||
void InvTransformPrimal(const Array<int> & Fo, double *v) const override;
|
||||
void TransformDual(const Array<int> & Fo, double *v) const override;
|
||||
void InvTransformDual(const Array<int> & Fo, double *v) const override;
|
||||
};
|
||||
|
||||
/// Stateless DoF transformation implementation for the Nedelec basis on
|
||||
/// triangles
|
||||
class ND_TriStatelessDofTransformation : public ND_StatelessDofTransformation
|
||||
{
|
||||
public:
|
||||
ND_TriStatelessDofTransformation(int order)
|
||||
: StatelessDofTransformation(order*(order + 2))
|
||||
, ND_StatelessDofTransformation(order*(order + 2), order, 3, 1)
|
||||
{}
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on triangles
|
||||
class ND_TriDofTransformation : public DofTransformation,
|
||||
public ND_TriStatelessDofTransformation
|
||||
class ND_TriDofTransformation : public ND_DofTransformation
|
||||
{
|
||||
public:
|
||||
ND_TriDofTransformation(int order)
|
||||
: StatelessDofTransformation(order*(order + 2))
|
||||
, DofTransformation(order*(order + 2))
|
||||
, ND_TriStatelessDofTransformation(order)
|
||||
{}
|
||||
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
using DofTransformation::InvTransformDual;
|
||||
|
||||
using ND_TriStatelessDofTransformation::TransformPrimal;
|
||||
using ND_TriStatelessDofTransformation::InvTransformPrimal;
|
||||
using ND_TriStatelessDofTransformation::TransformDual;
|
||||
using ND_TriStatelessDofTransformation::InvTransformDual;
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on tetrahedra
|
||||
class ND_TetStatelessDofTransformation : public ND_StatelessDofTransformation
|
||||
{
|
||||
public:
|
||||
ND_TetStatelessDofTransformation(int order)
|
||||
: StatelessDofTransformation(order*(order + 2)*(order + 3)/2)
|
||||
, ND_StatelessDofTransformation(order*(order + 2)*(order + 3)/2, order,
|
||||
6, 4)
|
||||
: ND_DofTransformation(order*(order + 2), order, 3, 1)
|
||||
{}
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on tetrahedra
|
||||
class ND_TetDofTransformation : public DofTransformation,
|
||||
public ND_TetStatelessDofTransformation
|
||||
class ND_TetDofTransformation : public ND_DofTransformation
|
||||
{
|
||||
public:
|
||||
ND_TetDofTransformation(int order)
|
||||
: StatelessDofTransformation(order*(order + 2)*(order + 3)/2)
|
||||
, DofTransformation(order*(order + 2)*(order + 3)/2)
|
||||
, ND_TetStatelessDofTransformation(order)
|
||||
{}
|
||||
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
using DofTransformation::InvTransformDual;
|
||||
|
||||
using ND_TetStatelessDofTransformation::TransformPrimal;
|
||||
using ND_TetStatelessDofTransformation::InvTransformPrimal;
|
||||
using ND_TetStatelessDofTransformation::TransformDual;
|
||||
using ND_TetStatelessDofTransformation::InvTransformDual;
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on wedge elements
|
||||
class ND_WedgeStatelessDofTransformation : public ND_StatelessDofTransformation
|
||||
{
|
||||
public:
|
||||
ND_WedgeStatelessDofTransformation(int order)
|
||||
: StatelessDofTransformation(3 * order * ((order + 1) * (order + 2))/2)
|
||||
, ND_StatelessDofTransformation(3 * order * ((order + 1) * (order + 2))/2,
|
||||
order, 9, 2)
|
||||
: ND_DofTransformation(order*(order + 2)*(order + 3)/2, order, 6, 4)
|
||||
{}
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on wedge elements
|
||||
class ND_WedgeDofTransformation : public DofTransformation,
|
||||
public ND_WedgeStatelessDofTransformation
|
||||
class ND_WedgeDofTransformation : public ND_DofTransformation
|
||||
{
|
||||
public:
|
||||
ND_WedgeDofTransformation(int order)
|
||||
: StatelessDofTransformation(3 * order * ((order + 1) * (order + 2))/2)
|
||||
, DofTransformation(3 * order * ((order + 1) * (order + 2))/2)
|
||||
, ND_WedgeStatelessDofTransformation(order)
|
||||
: ND_DofTransformation(3 * order * ((order + 1) * (order + 2))/2,
|
||||
order, 9, 2)
|
||||
{}
|
||||
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
using DofTransformation::InvTransformDual;
|
||||
|
||||
using ND_WedgeStatelessDofTransformation::TransformPrimal;
|
||||
using ND_WedgeStatelessDofTransformation::InvTransformPrimal;
|
||||
using ND_WedgeStatelessDofTransformation::TransformDual;
|
||||
using ND_WedgeStatelessDofTransformation::InvTransformDual;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -492,6 +492,7 @@ int IsoparametricTransformation::OrderGrad(const FiniteElement *fe) const
|
||||
void IsoparametricTransformation::Transform (const IntegrationPoint &ip,
|
||||
Vector &trans)
|
||||
{
|
||||
MFEM_ASSERT(FElem != nullptr, "Must provide a valid FiniteElement object!");
|
||||
shape.SetSize(FElem->GetDof());
|
||||
trans.SetSize(PointMat.Height());
|
||||
|
||||
|
||||
+1
-1
@@ -596,7 +596,7 @@ public:
|
||||
/** @brief Return a DoF transformation object for this particular type of
|
||||
basis.
|
||||
*/
|
||||
virtual StatelessDofTransformation * GetDofTransformation() const
|
||||
virtual const StatelessDofTransformation *GetDofTransformation() const
|
||||
{ return NULL; }
|
||||
|
||||
/// Deconstruct the FiniteElement
|
||||
|
||||
@@ -6031,7 +6031,7 @@ void RT0PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
|
||||
shape(1,2) = z;
|
||||
|
||||
shape(2,0) = x * (2.0 - z) * ozi;
|
||||
shape(2,1) = - y * z * ozi;;
|
||||
shape(2,1) = - y * z * ozi;
|
||||
shape(2,2) = z;
|
||||
|
||||
shape(3,0) = - x * z * ozi;
|
||||
|
||||
+6
-6
@@ -179,7 +179,7 @@ class ND_TetrahedronElement : public VectorFiniteElement
|
||||
Array<int> dof2tk;
|
||||
DenseMatrixInverse Ti;
|
||||
|
||||
mutable ND_TetStatelessDofTransformation doftrans;
|
||||
ND_TetDofTransformation doftrans;
|
||||
|
||||
public:
|
||||
/// Construct the ND_TetrahedronElement of order @a p
|
||||
@@ -201,7 +201,7 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &I) const
|
||||
{ LocalInterpolation_ND(CheckVectorFE(fe), tk, dof2tk, Trans, I); }
|
||||
virtual StatelessDofTransformation * GetDofTransformation() const
|
||||
virtual const StatelessDofTransformation *GetDofTransformation() const
|
||||
{ return &doftrans; }
|
||||
using FiniteElement::Project;
|
||||
virtual void Project(VectorCoefficient &vc,
|
||||
@@ -242,7 +242,7 @@ class ND_TriangleElement : public VectorFiniteElement
|
||||
Array<int> dof2tk;
|
||||
DenseMatrixInverse Ti;
|
||||
|
||||
mutable ND_TriStatelessDofTransformation doftrans;
|
||||
ND_TriDofTransformation doftrans;
|
||||
|
||||
public:
|
||||
/// Construct the ND_TriangleElement of order @a p
|
||||
@@ -264,7 +264,7 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &I) const
|
||||
{ LocalInterpolation_ND(CheckVectorFE(fe), tk, dof2tk, Trans, I); }
|
||||
virtual StatelessDofTransformation * GetDofTransformation() const
|
||||
virtual const StatelessDofTransformation *GetDofTransformation() const
|
||||
{ return &doftrans; }
|
||||
using FiniteElement::Project;
|
||||
virtual void Project(VectorCoefficient &vc,
|
||||
@@ -346,7 +346,7 @@ private:
|
||||
#endif
|
||||
Array<int> dof2tk, t_dof, s_dof;
|
||||
|
||||
mutable ND_WedgeStatelessDofTransformation doftrans;
|
||||
ND_WedgeDofTransformation doftrans;
|
||||
|
||||
H1_TriangleElement H1TriangleFE;
|
||||
ND_TriangleElement NDTriangleFE;
|
||||
@@ -379,7 +379,7 @@ public:
|
||||
DenseMatrix &I) const
|
||||
{ LocalInterpolation_ND(CheckVectorFE(fe), tk, dof2tk, Trans, I); }
|
||||
|
||||
virtual StatelessDofTransformation * GetDofTransformation() const
|
||||
virtual const StatelessDofTransformation *GetDofTransformation() const
|
||||
{ return &doftrans; }
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
+1
-1
@@ -2896,7 +2896,7 @@ ND_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
}
|
||||
}
|
||||
|
||||
StatelessDofTransformation *
|
||||
const StatelessDofTransformation *
|
||||
ND_FECollection::DofTransformationForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if (!Geometry::IsTensorProduct(GeomType) && this->GetOrder() > 1)
|
||||
|
||||
+2
-2
@@ -63,7 +63,7 @@ public:
|
||||
/** @brief Returns a DoF transformation object compatible with this basis
|
||||
and geometry type.
|
||||
*/
|
||||
virtual StatelessDofTransformation *
|
||||
virtual const StatelessDofTransformation *
|
||||
DofTransformationForGeometry(Geometry::Type GeomType) const
|
||||
{ return NULL; }
|
||||
|
||||
@@ -483,7 +483,7 @@ public:
|
||||
int DofForGeometry(Geometry::Type GeomType) const override
|
||||
{ return ND_dof[GeomType]; }
|
||||
|
||||
StatelessDofTransformation *
|
||||
const StatelessDofTransformation *
|
||||
DofTransformationForGeometry(Geometry::Type GeomType) const override;
|
||||
|
||||
const int *DofOrderForOrientation(Geometry::Type GeomType,
|
||||
|
||||
+197
-198
@@ -63,7 +63,6 @@ FiniteElementSpace::FiniteElementSpace()
|
||||
elem_dof(NULL), elem_fos(NULL), bdr_elem_dof(NULL), bdr_elem_fos(NULL),
|
||||
face_dof(NULL),
|
||||
NURBSext(NULL), own_ext(false),
|
||||
DoFTrans(0), VDoFTrans(vdim, ordering),
|
||||
cP_is_set(false),
|
||||
Th(Operator::ANY_TYPE),
|
||||
sequence(0), mesh_sequence(0), orders_changed(false), relaxed_hp(false)
|
||||
@@ -72,7 +71,6 @@ FiniteElementSpace::FiniteElementSpace()
|
||||
FiniteElementSpace::FiniteElementSpace(const FiniteElementSpace &orig,
|
||||
Mesh *mesh_,
|
||||
const FiniteElementCollection *fec_)
|
||||
: VDoFTrans(orig.vdim, orig.ordering)
|
||||
{
|
||||
mesh_ = mesh_ ? mesh_ : orig.mesh;
|
||||
fec_ = fec_ ? fec_ : orig.fec;
|
||||
@@ -212,7 +210,7 @@ void FiniteElementSpace::GetVDofs(int vd, Array<int>& dofs, int ndofs_) const
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::DofsToVDofs (Array<int> &dofs, int ndofs_) const
|
||||
void FiniteElementSpace::DofsToVDofs(Array<int> &dofs, int ndofs_) const
|
||||
{
|
||||
if (vdim == 1) { return; }
|
||||
if (ndofs_ < 0) { ndofs_ = this->ndofs; }
|
||||
@@ -264,7 +262,7 @@ int FiniteElementSpace::DofToVDof(int dof, int vd, int ndofs_) const
|
||||
}
|
||||
|
||||
// static function
|
||||
void FiniteElementSpace::AdjustVDofs (Array<int> &vdofs)
|
||||
void FiniteElementSpace::AdjustVDofs(Array<int> &vdofs)
|
||||
{
|
||||
int n = vdofs.Size(), *vdof = vdofs;
|
||||
for (int i = 0; i < n; i++)
|
||||
@@ -277,36 +275,36 @@ void FiniteElementSpace::AdjustVDofs (Array<int> &vdofs)
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
GetElementDofs(i, vdofs, doftrans);
|
||||
DofsToVDofs(vdofs);
|
||||
doftrans.SetVDim(vdim, ordering);
|
||||
}
|
||||
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
DofTransformation * doftrans = GetElementDofs(i, vdofs);
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
GetBdrElementDofs(i, vdofs, doftrans);
|
||||
DofsToVDofs(vdofs);
|
||||
if (vdim == 1 || doftrans == NULL)
|
||||
{
|
||||
return doftrans;
|
||||
}
|
||||
else
|
||||
{
|
||||
VDoFTrans.SetDofTransformation(*doftrans);
|
||||
return &VDoFTrans;
|
||||
}
|
||||
doftrans.SetVDim(vdim, ordering);
|
||||
}
|
||||
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
DofTransformation * doftrans = GetBdrElementDofs(i, vdofs);
|
||||
DofsToVDofs(vdofs);
|
||||
if (vdim == 1 || doftrans == NULL)
|
||||
{
|
||||
return doftrans;
|
||||
}
|
||||
else
|
||||
{
|
||||
VDoFTrans.SetDofTransformation(*doftrans);
|
||||
return &VDoFTrans;
|
||||
}
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetBdrElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetPatchVDofs(int i, Array<int> &vdofs) const
|
||||
@@ -777,9 +775,9 @@ FiniteElementSpace::H2L_GlobalRestrictionMatrix (FiniteElementSpace *lfes)
|
||||
return R;
|
||||
}
|
||||
|
||||
void FiniteElementSpace
|
||||
::AddDependencies(SparseMatrix& deps, Array<int>& master_dofs,
|
||||
Array<int>& slave_dofs, DenseMatrix& I, int skipfirst)
|
||||
void FiniteElementSpace::AddDependencies(
|
||||
SparseMatrix& deps, Array<int>& master_dofs, Array<int>& slave_dofs,
|
||||
DenseMatrix& I, int skipfirst)
|
||||
{
|
||||
for (int i = skipfirst; i < slave_dofs.Size(); i++)
|
||||
{
|
||||
@@ -802,11 +800,9 @@ void FiniteElementSpace
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace
|
||||
::AddEdgeFaceDependencies(SparseMatrix &deps, Array<int> &master_dofs,
|
||||
const FiniteElement *master_fe,
|
||||
Array<int> &slave_dofs, int slave_face,
|
||||
const DenseMatrix *pm) const
|
||||
void FiniteElementSpace::AddEdgeFaceDependencies(
|
||||
SparseMatrix &deps, Array<int> &master_dofs, const FiniteElement *master_fe,
|
||||
Array<int> &slave_dofs, int slave_face, const DenseMatrix *pm) const
|
||||
{
|
||||
// In variable-order spaces in 3D, we need to only constrain interior face
|
||||
// DOFs (this is done one level up), since edge dependencies can be more
|
||||
@@ -1533,12 +1529,12 @@ SparseMatrix* FiniteElementSpace::RefinementMatrix(int old_ndofs,
|
||||
localP);
|
||||
}
|
||||
|
||||
FiniteElementSpace::RefinementOperator::RefinementOperator
|
||||
(const FiniteElementSpace* fespace, Table* old_elem_dof, Table* old_elem_fos,
|
||||
int old_ndofs)
|
||||
: fespace(fespace)
|
||||
, old_elem_dof(old_elem_dof)
|
||||
, old_elem_fos(old_elem_fos)
|
||||
FiniteElementSpace::RefinementOperator::RefinementOperator(
|
||||
const FiniteElementSpace* fespace, Table* old_elem_dof, Table* old_elem_fos,
|
||||
int old_ndofs)
|
||||
: fespace(fespace),
|
||||
old_elem_dof(old_elem_dof),
|
||||
old_elem_fos(old_elem_fos)
|
||||
{
|
||||
MFEM_VERIFY(fespace->GetNE() >= old_elem_dof->Size(),
|
||||
"Previous mesh is not coarser.");
|
||||
@@ -1553,7 +1549,7 @@ FiniteElementSpace::RefinementOperator::RefinementOperator
|
||||
fespace->GetLocalRefinementMatrices(elem_geoms[i], localP[elem_geoms[i]]);
|
||||
}
|
||||
|
||||
ConstructDoFTrans();
|
||||
ConstructDoFTransArray();
|
||||
}
|
||||
|
||||
FiniteElementSpace::RefinementOperator::RefinementOperator(
|
||||
@@ -1578,59 +1574,58 @@ FiniteElementSpace::RefinementOperator::RefinementOperator(
|
||||
old_elem_fos = new Table(*coarse_fes->GetElementToFaceOrientationTable());
|
||||
}
|
||||
|
||||
ConstructDoFTrans();
|
||||
ConstructDoFTransArray();
|
||||
}
|
||||
|
||||
FiniteElementSpace::RefinementOperator::~RefinementOperator()
|
||||
{
|
||||
delete old_elem_dof;
|
||||
delete old_elem_fos;
|
||||
for (int i=0; i<old_DoFTrans.Size(); i++)
|
||||
for (int i=0; i<old_DoFTransArray.Size(); i++)
|
||||
{
|
||||
delete old_DoFTrans[i];
|
||||
delete old_DoFTransArray[i];
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::RefinementOperator
|
||||
::ConstructDoFTrans()
|
||||
void FiniteElementSpace::RefinementOperator::ConstructDoFTransArray()
|
||||
{
|
||||
old_DoFTrans.SetSize(Geometry::NUM_GEOMETRIES);
|
||||
for (int i=0; i<old_DoFTrans.Size(); i++)
|
||||
old_DoFTransArray.SetSize(Geometry::NUM_GEOMETRIES);
|
||||
for (int i=0; i<old_DoFTransArray.Size(); i++)
|
||||
{
|
||||
old_DoFTrans[i] = NULL;
|
||||
old_DoFTransArray[i] = NULL;
|
||||
}
|
||||
|
||||
const FiniteElementCollection *fec_ref = fespace->FEColl();
|
||||
if (dynamic_cast<const ND_FECollection*>(fec_ref))
|
||||
{
|
||||
const FiniteElement * nd_tri =
|
||||
const FiniteElement *nd_tri =
|
||||
fec_ref->FiniteElementForGeometry(Geometry::TRIANGLE);
|
||||
if (nd_tri)
|
||||
{
|
||||
old_DoFTrans[Geometry::TRIANGLE] =
|
||||
old_DoFTransArray[Geometry::TRIANGLE] =
|
||||
new ND_TriDofTransformation(nd_tri->GetOrder());
|
||||
}
|
||||
|
||||
const FiniteElement * nd_tet =
|
||||
const FiniteElement *nd_tet =
|
||||
fec_ref->FiniteElementForGeometry(Geometry::TETRAHEDRON);
|
||||
if (nd_tet)
|
||||
{
|
||||
old_DoFTrans[Geometry::TETRAHEDRON] =
|
||||
old_DoFTransArray[Geometry::TETRAHEDRON] =
|
||||
new ND_TetDofTransformation(nd_tet->GetOrder());
|
||||
}
|
||||
|
||||
const FiniteElement * nd_pri =
|
||||
const FiniteElement *nd_pri =
|
||||
fec_ref->FiniteElementForGeometry(Geometry::PRISM);
|
||||
if (nd_pri)
|
||||
{
|
||||
old_DoFTrans[Geometry::PRISM] =
|
||||
old_DoFTransArray[Geometry::PRISM] =
|
||||
new ND_WedgeDofTransformation(nd_pri->GetOrder());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::RefinementOperator
|
||||
::Mult(const Vector &x, Vector &y) const
|
||||
void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
Mesh* mesh_ref = fespace->GetMesh();
|
||||
const CoarseFineTransformations &trans_ref =
|
||||
@@ -1662,6 +1657,7 @@ void FiniteElementSpace::RefinementOperator
|
||||
fespace->DofsToVDofs(vd, vdofs);
|
||||
old_dofs.Copy(old_vdofs);
|
||||
fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
|
||||
|
||||
x.GetSubVector(old_vdofs, subX);
|
||||
lP.Mult(subX, subY);
|
||||
y.SetSubVector(vdofs, subY);
|
||||
@@ -1670,40 +1666,30 @@ void FiniteElementSpace::RefinementOperator
|
||||
else
|
||||
{
|
||||
old_elem_fos->GetRow(emb.parent, old_Fo);
|
||||
old_DoFTrans[geom]->SetFaceOrientations(old_Fo);
|
||||
|
||||
DofTransformation *new_doftrans = NULL;
|
||||
VDofTransformation *vdoftrans =
|
||||
dynamic_cast<VDofTransformation*>(doftrans);
|
||||
if (vdoftrans)
|
||||
{
|
||||
new_doftrans = doftrans;
|
||||
doftrans = vdoftrans->GetDofTransformation();
|
||||
}
|
||||
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
|
||||
old_DoFTrans.SetFaceOrientations(old_Fo);
|
||||
|
||||
doftrans->SetVDim();
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
dofs.Copy(vdofs);
|
||||
fespace->DofsToVDofs(vd, vdofs);
|
||||
old_dofs.Copy(old_vdofs);
|
||||
fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
|
||||
|
||||
x.GetSubVector(old_vdofs, subX);
|
||||
old_DoFTrans[geom]->InvTransformPrimal(subX);
|
||||
old_DoFTrans.InvTransformPrimal(subX);
|
||||
lP.Mult(subX, subY);
|
||||
doftrans->TransformPrimal(subY);
|
||||
y.SetSubVector(vdofs, subY);
|
||||
}
|
||||
|
||||
if (vdoftrans)
|
||||
{
|
||||
doftrans = new_doftrans;
|
||||
}
|
||||
doftrans->SetVDim(rvdim, fespace->GetOrdering());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::RefinementOperator
|
||||
::MultTranspose(const Vector &x, Vector &y) const
|
||||
void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
y = 0.0;
|
||||
|
||||
@@ -1727,7 +1713,7 @@ void FiniteElementSpace::RefinementOperator
|
||||
const Geometry::Type geom = mesh_ref->GetElementBaseGeometry(k);
|
||||
const DenseMatrix &lP = localP[geom](emb.matrix);
|
||||
|
||||
DofTransformation * doftrans = fespace->GetElementDofs(k, f_dofs);
|
||||
DofTransformation *doftrans = fespace->GetElementDofs(k, f_dofs);
|
||||
old_elem_dof->GetRow(emb.parent, c_dofs);
|
||||
|
||||
if (!doftrans)
|
||||
@@ -1742,7 +1728,6 @@ void FiniteElementSpace::RefinementOperator
|
||||
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
|
||||
|
||||
x.GetSubVector(f_vdofs, subX);
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
{
|
||||
if (processed[DecodeDof(f_dofs[p])])
|
||||
@@ -1750,7 +1735,6 @@ void FiniteElementSpace::RefinementOperator
|
||||
subX[p] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
lP.MultTranspose(subX, subY);
|
||||
y.AddElementVector(c_vdofs, subY);
|
||||
}
|
||||
@@ -1760,17 +1744,10 @@ void FiniteElementSpace::RefinementOperator
|
||||
subYt.SetSize(lP.Width());
|
||||
|
||||
old_elem_fos->GetRow(emb.parent, old_Fo);
|
||||
old_DoFTrans[geom]->SetFaceOrientations(old_Fo);
|
||||
|
||||
DofTransformation *new_doftrans = NULL;
|
||||
VDofTransformation *vdoftrans =
|
||||
dynamic_cast<VDofTransformation*>(doftrans);
|
||||
if (vdoftrans)
|
||||
{
|
||||
new_doftrans = doftrans;
|
||||
doftrans = vdoftrans->GetDofTransformation();
|
||||
}
|
||||
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
|
||||
old_DoFTrans.SetFaceOrientations(old_Fo);
|
||||
|
||||
doftrans->SetVDim();
|
||||
for (int vd = 0; vd < rvdim; vd++)
|
||||
{
|
||||
f_dofs.Copy(f_vdofs);
|
||||
@@ -1787,16 +1764,11 @@ void FiniteElementSpace::RefinementOperator
|
||||
subX[p] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
lP.MultTranspose(subX, subYt);
|
||||
old_DoFTrans[geom]->TransformDual(subYt);
|
||||
old_DoFTrans.TransformDual(subYt);
|
||||
y.AddElementVector(c_vdofs, subYt);
|
||||
}
|
||||
|
||||
if (vdoftrans)
|
||||
{
|
||||
doftrans = new_doftrans;
|
||||
}
|
||||
doftrans->SetVDim(rvdim, fespace->GetOrdering());
|
||||
}
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
@@ -2024,8 +1996,8 @@ FiniteElementSpace::DerefinementOperator::~DerefinementOperator()
|
||||
delete coarse_elem_dof;
|
||||
}
|
||||
|
||||
void FiniteElementSpace::DerefinementOperator
|
||||
::Mult(const Vector &x, Vector &y) const
|
||||
void FiniteElementSpace::DerefinementOperator::Mult(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
Array<int> c_vdofs, f_vdofs;
|
||||
Vector loc_x, loc_y;
|
||||
@@ -2227,7 +2199,7 @@ void FiniteElementSpace::Constructor(Mesh *mesh_, NURBSExtension *NURBSext_,
|
||||
R_transpose.reset();
|
||||
cP_is_set = false;
|
||||
|
||||
ConstructDoFTrans();
|
||||
ConstructDoFTransArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2239,40 +2211,39 @@ void FiniteElementSpace::Constructor(Mesh *mesh_, NURBSExtension *NURBSext_,
|
||||
BuildElementToDofTable();
|
||||
}
|
||||
|
||||
void FiniteElementSpace::ConstructDoFTrans()
|
||||
void FiniteElementSpace::ConstructDoFTransArray()
|
||||
{
|
||||
DestroyDoFTrans();
|
||||
DestroyDoFTransArray();
|
||||
|
||||
VDoFTrans.SetVDim(vdim);
|
||||
DoFTrans.SetSize(Geometry::NUM_GEOMETRIES);
|
||||
for (int i=0; i<DoFTrans.Size(); i++)
|
||||
DoFTransArray.SetSize(Geometry::NUM_GEOMETRIES);
|
||||
for (int i=0; i<DoFTransArray.Size(); i++)
|
||||
{
|
||||
DoFTrans[i] = NULL;
|
||||
DoFTransArray[i] = NULL;
|
||||
}
|
||||
if (mesh->Dimension() < 3) { return; }
|
||||
if (dynamic_cast<const ND_FECollection*>(fec))
|
||||
{
|
||||
const FiniteElement * nd_tri =
|
||||
const FiniteElement *nd_tri =
|
||||
fec->FiniteElementForGeometry(Geometry::TRIANGLE);
|
||||
if (nd_tri)
|
||||
{
|
||||
DoFTrans[Geometry::TRIANGLE] =
|
||||
DoFTransArray[Geometry::TRIANGLE] =
|
||||
new ND_TriDofTransformation(nd_tri->GetOrder());
|
||||
}
|
||||
|
||||
const FiniteElement * nd_tet =
|
||||
const FiniteElement *nd_tet =
|
||||
fec->FiniteElementForGeometry(Geometry::TETRAHEDRON);
|
||||
if (nd_tet)
|
||||
{
|
||||
DoFTrans[Geometry::TETRAHEDRON] =
|
||||
DoFTransArray[Geometry::TETRAHEDRON] =
|
||||
new ND_TetDofTransformation(nd_tet->GetOrder());
|
||||
}
|
||||
|
||||
const FiniteElement * nd_pri =
|
||||
const FiniteElement *nd_pri =
|
||||
fec->FiniteElementForGeometry(Geometry::PRISM);
|
||||
if (nd_pri)
|
||||
{
|
||||
DoFTrans[Geometry::PRISM] =
|
||||
DoFTransArray[Geometry::PRISM] =
|
||||
new ND_WedgeDofTransformation(nd_pri->GetOrder());
|
||||
}
|
||||
}
|
||||
@@ -2324,7 +2295,7 @@ void FiniteElementSpace::BuildNURBSFaceToDofTable() const
|
||||
face_to_be = -1;
|
||||
for (int b = 0; b < GetNBE(); b++)
|
||||
{
|
||||
int f = mesh->GetBdrElementEdgeIndex(b);
|
||||
int f = mesh->GetBdrElementFaceIndex(b);
|
||||
face_to_be[f] = b;
|
||||
}
|
||||
|
||||
@@ -2428,6 +2399,7 @@ void FiniteElementSpace::Construct()
|
||||
{
|
||||
// the simple case: all edges are of the same order
|
||||
nedofs = mesh->GetNEdges() * fec->GetNumDof(Geometry::SEGMENT, order);
|
||||
var_edge_dofs.Clear(); // ensure any old var_edge_dof table is dumped.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2446,6 +2418,7 @@ void FiniteElementSpace::Construct()
|
||||
// the simple case: all faces are of the same geometry and order
|
||||
uni_fdof = fec->GetNumDof(mesh->GetFaceGeometry(0), order);
|
||||
nfdofs = mesh->GetNFaces() * uni_fdof;
|
||||
var_face_dofs.Clear(); // ensure any old var_face_dof table is dumped.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2474,7 +2447,7 @@ void FiniteElementSpace::Construct()
|
||||
|
||||
ndofs = nvdofs + nedofs + nfdofs + nbdofs;
|
||||
|
||||
ConstructDoFTrans();
|
||||
ConstructDoFTransArray();
|
||||
|
||||
// record the current mesh sequence number to detect refinement etc.
|
||||
mesh_sequence = mesh->GetSequence();
|
||||
@@ -2499,9 +2472,8 @@ int FiniteElementSpace::MinOrder(VarOrderBits bits)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FiniteElementSpace
|
||||
::CalcEdgeFaceVarOrders(Array<VarOrderBits> &edge_orders,
|
||||
Array<VarOrderBits> &face_orders) const
|
||||
void FiniteElementSpace::CalcEdgeFaceVarOrders(
|
||||
Array<VarOrderBits> &edge_orders, Array<VarOrderBits> &face_orders) const
|
||||
{
|
||||
MFEM_ASSERT(IsVariableOrder(), "");
|
||||
MFEM_ASSERT(Nonconforming(), "");
|
||||
@@ -2656,7 +2628,6 @@ int FiniteElementSpace::MakeDofTable(int ent_dim,
|
||||
int dofs = fec->GetNumDof(geom, order);
|
||||
list.Append(Connection(i, total_dofs));
|
||||
total_dofs += dofs;
|
||||
|
||||
if (var_ent_order) { var_ent_order->Append(order); }
|
||||
}
|
||||
}
|
||||
@@ -2667,7 +2638,6 @@ int FiniteElementSpace::MakeDofTable(int ent_dim,
|
||||
|
||||
// build the table
|
||||
entity_dofs.MakeFromList(num_ent+1, list);
|
||||
|
||||
return total_dofs;
|
||||
}
|
||||
|
||||
@@ -2727,8 +2697,8 @@ int FiniteElementSpace::GetNVariants(int entity, int index) const
|
||||
static const char* msg_orders_changed =
|
||||
"Element orders changed, you need to Update() the space first.";
|
||||
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
|
||||
void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
@@ -2736,13 +2706,16 @@ FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
|
||||
{
|
||||
elem_dof->GetRow(elem, dofs);
|
||||
|
||||
if (DoFTrans[mesh->GetElementBaseGeometry(elem)])
|
||||
if (DoFTransArray[mesh->GetElementBaseGeometry(elem)])
|
||||
{
|
||||
Array<int> Fo;
|
||||
elem_fos -> GetRow (elem, Fo);
|
||||
DoFTrans[mesh->GetElementBaseGeometry(elem)]->SetFaceOrientations(Fo);
|
||||
doftrans.SetDofTransformation(
|
||||
*DoFTransArray[mesh->GetElementBaseGeometry(elem)]);
|
||||
doftrans.SetFaceOrientations(Fo);
|
||||
doftrans.SetVDim();
|
||||
}
|
||||
return DoFTrans[mesh->GetElementBaseGeometry(elem)];
|
||||
return;
|
||||
}
|
||||
|
||||
Array<int> V, E, Eo, F, Fo; // TODO: LocalArray
|
||||
@@ -2766,10 +2739,12 @@ FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
|
||||
{
|
||||
nfd += fec->GetNumDof(mesh->GetFaceGeometry(F[i]), order);
|
||||
}
|
||||
if (DoFTrans[mesh->GetElementBaseGeometry(elem)])
|
||||
if (DoFTransArray[mesh->GetElementBaseGeometry(elem)])
|
||||
{
|
||||
DoFTrans[mesh->GetElementBaseGeometry(elem)]
|
||||
-> SetFaceOrientations(Fo);
|
||||
doftrans.SetDofTransformation(
|
||||
*DoFTransArray[mesh->GetElementBaseGeometry(elem)]);
|
||||
doftrans.SetFaceOrientations(Fo);
|
||||
doftrans.SetVDim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2828,54 +2803,18 @@ FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
|
||||
dofs.Append(bbase + j);
|
||||
}
|
||||
}
|
||||
return DoFTrans[mesh->GetElementBaseGeometry(elem)];
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetPatchDofs(int patch, Array<int> &dofs) const
|
||||
DofTransformation *FiniteElementSpace::GetElementDofs(int elem,
|
||||
Array<int> &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(NURBSext,
|
||||
"FiniteElementSpace::GetPatchDofs needs a NURBSExtension");
|
||||
NURBSext->GetPatchDofs(patch, dofs);
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetElementDofs(elem, dofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetFE(int i) const
|
||||
{
|
||||
if (i < 0 || i >= mesh->GetNE())
|
||||
{
|
||||
if (mesh->GetNE() == 0)
|
||||
{
|
||||
MFEM_ABORT("Empty MPI partitions are not permitted!");
|
||||
}
|
||||
MFEM_ABORT("Invalid element id:" << i << "; minimum allowed:" << 0 <<
|
||||
", maximum allowed:" << mesh->GetNE()-1);
|
||||
}
|
||||
|
||||
const FiniteElement *FE =
|
||||
fec->GetFE(mesh->GetElementGeometry(i), GetElementOrderImpl(i));
|
||||
|
||||
if (NURBSext)
|
||||
{
|
||||
NURBSext->LoadFE(i, FE);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_DEBUG
|
||||
// consistency check: fec->GetOrder() and FE->GetOrder() should return
|
||||
// the same value (for standard, constant-order spaces)
|
||||
if (!IsVariableOrder() && FE->GetDim() > 0)
|
||||
{
|
||||
MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
|
||||
"internal error: " <<
|
||||
FE->GetOrder() << " != " << fec->GetOrder());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return FE;
|
||||
}
|
||||
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
|
||||
void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
@@ -2883,17 +2822,19 @@ FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
|
||||
{
|
||||
bdr_elem_dof->GetRow(bel, dofs);
|
||||
|
||||
if (DoFTrans[mesh->GetBdrElementBaseGeometry(bel)])
|
||||
if (DoFTransArray[mesh->GetBdrElementBaseGeometry(bel)])
|
||||
{
|
||||
Array<int> Fo;
|
||||
bdr_elem_fos -> GetRow (bel, Fo);
|
||||
DoFTrans[mesh->GetBdrElementBaseGeometry(bel)]->
|
||||
SetFaceOrientations(Fo);
|
||||
doftrans.SetDofTransformation(
|
||||
*DoFTransArray[mesh->GetBdrElementBaseGeometry(bel)]);
|
||||
doftrans.SetFaceOrientations(Fo);
|
||||
doftrans.SetVDim();
|
||||
}
|
||||
return DoFTrans[mesh->GetBdrElementBaseGeometry(bel)];
|
||||
return;
|
||||
}
|
||||
|
||||
Array<int> V, E, Eo, Fo; // TODO: LocalArray
|
||||
Array<int> V, E, Eo; // TODO: LocalArray
|
||||
int F, oF;
|
||||
|
||||
int dim = mesh->Dimension();
|
||||
@@ -2917,11 +2858,14 @@ FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
|
||||
{
|
||||
mesh->GetBdrElementFace(bel, &F, &oF);
|
||||
|
||||
if (DoFTrans[mesh->GetBdrElementBaseGeometry(bel)])
|
||||
if (DoFTransArray[mesh->GetBdrElementBaseGeometry(bel)])
|
||||
{
|
||||
Fo.Append(oF);
|
||||
DoFTrans[mesh->GetBdrElementBaseGeometry(bel)]->
|
||||
SetFaceOrientations(Fo);
|
||||
mfem::Array<int> Fo(1);
|
||||
Fo[0] = oF;
|
||||
doftrans.SetDofTransformation(
|
||||
*DoFTransArray[mesh->GetBdrElementBaseGeometry(bel)]);
|
||||
doftrans.SetFaceOrientations(Fo);
|
||||
doftrans.SetVDim();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2963,8 +2907,14 @@ FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
|
||||
dofs.Append(EncodeDof(nvdofs + nedofs + fbase, ind[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return DoFTrans[mesh->GetBdrElementBaseGeometry(bel)];
|
||||
DofTransformation *FiniteElementSpace::GetBdrElementDofs(int bel,
|
||||
Array<int> &dofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetBdrElementDofs(bel, dofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
|
||||
int FiniteElementSpace::GetFaceDofs(int face, Array<int> &dofs,
|
||||
@@ -2996,7 +2946,14 @@ int FiniteElementSpace::GetFaceDofs(int face, Array<int> &dofs,
|
||||
|
||||
order = !IsVariableOrder() ? fec->GetOrder() :
|
||||
var_face_orders[var_face_dofs.GetI()[face] + variant];
|
||||
MFEM_ASSERT(fec->GetNumDof(fgeom, order) == nf, "");
|
||||
MFEM_ASSERT(fec->GetNumDof(fgeom, order) == nf, [&]()
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "fec->GetNumDof(" << (fgeom == Geometry::SQUARE ? "square" : "triangle")
|
||||
<< ", " << order << ") = " << fec->GetNumDof(fgeom, order) << " nf " << nf;
|
||||
msg << " face " << face << " variant " << variant << std::endl;
|
||||
return msg.str();
|
||||
}());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3127,18 +3084,6 @@ int FiniteElementSpace::GetNumElementInteriorDofs(int i) const
|
||||
GetElementOrderImpl(i));
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetEdgeInteriorDofs(int i, Array<int> &dofs) const
|
||||
{
|
||||
MFEM_VERIFY(!IsVariableOrder(), "not implemented");
|
||||
|
||||
int ne = fec->DofForGeometry(Geometry::SEGMENT);
|
||||
dofs.SetSize (ne);
|
||||
for (int j = 0, k = nvdofs+i*ne; j < ne; j++, k++)
|
||||
{
|
||||
dofs[j] = k;
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetFaceInteriorDofs(int i, Array<int> &dofs) const
|
||||
{
|
||||
MFEM_VERIFY(!IsVariableOrder(), "not implemented");
|
||||
@@ -3163,6 +3108,61 @@ void FiniteElementSpace::GetFaceInteriorDofs(int i, Array<int> &dofs) const
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetEdgeInteriorDofs(int i, Array<int> &dofs) const
|
||||
{
|
||||
MFEM_VERIFY(!IsVariableOrder(), "not implemented");
|
||||
|
||||
int ne = fec->DofForGeometry(Geometry::SEGMENT);
|
||||
dofs.SetSize (ne);
|
||||
for (int j = 0, k = nvdofs+i*ne; j < ne; j++, k++)
|
||||
{
|
||||
dofs[j] = k;
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetPatchDofs(int patch, Array<int> &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(NURBSext,
|
||||
"FiniteElementSpace::GetPatchDofs needs a NURBSExtension");
|
||||
NURBSext->GetPatchDofs(patch, dofs);
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetFE(int i) const
|
||||
{
|
||||
if (i < 0 || i >= mesh->GetNE())
|
||||
{
|
||||
if (mesh->GetNE() == 0)
|
||||
{
|
||||
MFEM_ABORT("Empty MPI partitions are not permitted!");
|
||||
}
|
||||
MFEM_ABORT("Invalid element id:" << i << "; minimum allowed:" << 0 <<
|
||||
", maximum allowed:" << mesh->GetNE()-1);
|
||||
}
|
||||
|
||||
const FiniteElement *FE =
|
||||
fec->GetFE(mesh->GetElementGeometry(i), GetElementOrderImpl(i));
|
||||
|
||||
if (NURBSext)
|
||||
{
|
||||
NURBSext->LoadFE(i, FE);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_DEBUG
|
||||
// consistency check: fec->GetOrder() and FE->GetOrder() should return
|
||||
// the same value (for standard, constant-order spaces)
|
||||
if (!IsVariableOrder() && FE->GetDim() > 0)
|
||||
{
|
||||
MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
|
||||
"internal error: " <<
|
||||
FE->GetOrder() << " != " << fec->GetOrder());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return FE;
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetBE(int i) const
|
||||
{
|
||||
int order = fec->GetOrder();
|
||||
@@ -3235,8 +3235,8 @@ const FiniteElement *FiniteElementSpace::GetEdgeElement(int i,
|
||||
return fec->GetFE(Geometry::SEGMENT, eo);
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace
|
||||
::GetTraceElement(int i, Geometry::Type geom_type) const
|
||||
const FiniteElement *FiniteElementSpace::GetTraceElement(
|
||||
int i, Geometry::Type geom_type) const
|
||||
{
|
||||
return fec->TraceFiniteElementForGeometry(geom_type);
|
||||
}
|
||||
@@ -3276,7 +3276,7 @@ void FiniteElementSpace::Destroy()
|
||||
}
|
||||
E2BFQ_array.SetSize(0);
|
||||
|
||||
DestroyDoFTrans();
|
||||
DestroyDoFTransArray();
|
||||
|
||||
dof_elem_array.DeleteAll();
|
||||
dof_ldof_array.DeleteAll();
|
||||
@@ -3294,19 +3294,18 @@ void FiniteElementSpace::Destroy()
|
||||
delete bdr_elem_dof;
|
||||
delete bdr_elem_fos;
|
||||
delete face_dof;
|
||||
|
||||
delete [] bdofs;
|
||||
}
|
||||
ceed::RemoveBasisAndRestriction(this);
|
||||
}
|
||||
|
||||
void FiniteElementSpace::DestroyDoFTrans()
|
||||
void FiniteElementSpace::DestroyDoFTransArray()
|
||||
{
|
||||
for (int i = 0; i < DoFTrans.Size(); i++)
|
||||
for (int i = 0; i < DoFTransArray.Size(); i++)
|
||||
{
|
||||
delete DoFTrans[i];
|
||||
delete DoFTransArray[i];
|
||||
}
|
||||
DoFTrans.SetSize(0);
|
||||
DoFTransArray.SetSize(0);
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetTransferOperator(
|
||||
|
||||
+56
-21
@@ -271,8 +271,8 @@ protected:
|
||||
int own_ext;
|
||||
mutable Array<int> face_to_be; // NURBS FE space only
|
||||
|
||||
Array<DofTransformation*> DoFTrans;
|
||||
mutable VDofTransformation VDoFTrans;
|
||||
Array<StatelessDofTransformation *> DoFTransArray;
|
||||
mutable DofTransformation DoFTrans;
|
||||
|
||||
/** Matrix representing the prolongation from the global conforming dofs to
|
||||
a set of intermediate partially conforming dofs, e.g. the dofs associated
|
||||
@@ -328,8 +328,8 @@ protected:
|
||||
void Construct();
|
||||
void Destroy();
|
||||
|
||||
void ConstructDoFTrans();
|
||||
void DestroyDoFTrans();
|
||||
void ConstructDoFTransArray();
|
||||
void DestroyDoFTransArray();
|
||||
|
||||
void BuildElementToDofTable() const;
|
||||
void BuildBdrElementToDofTable() const;
|
||||
@@ -416,10 +416,10 @@ protected:
|
||||
Table* old_elem_dof; // Owned.
|
||||
Table* old_elem_fos; // Owned.
|
||||
|
||||
Array<DofTransformation*> old_DoFTrans;
|
||||
mutable VDofTransformation old_VDoFTrans;
|
||||
Array<StatelessDofTransformation*> old_DoFTransArray;
|
||||
mutable DofTransformation old_DoFTrans;
|
||||
|
||||
void ConstructDoFTrans();
|
||||
void ConstructDoFTransArray();
|
||||
|
||||
public:
|
||||
/** Construct the operator based on the elem_dof table of the original
|
||||
@@ -803,7 +803,16 @@ public:
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
virtual DofTransformation *GetElementDofs(int elem, Array<int> &dofs) const;
|
||||
DofTransformation *GetElementDofs(int elem, Array<int> &dofs) const;
|
||||
|
||||
/// @brief The same as GetElementDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
virtual void GetElementDofs(int elem, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
/// @brief Returns indices of degrees of freedom for boundary element 'bel'.
|
||||
/// The returned indices are offsets into an @ref ldof vector. See also
|
||||
@@ -817,13 +826,16 @@ public:
|
||||
/// with triangular faces.
|
||||
///
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
virtual DofTransformation *GetBdrElementDofs(int bel,
|
||||
Array<int> &dofs) const;
|
||||
DofTransformation *GetBdrElementDofs(int bel, Array<int> &dofs) const;
|
||||
|
||||
/** @brief Returns indices of degrees of freedom for NURBS patch index
|
||||
@a patch. Cartesian ordering is used, for the tensor-product degrees of
|
||||
freedom. */
|
||||
void GetPatchDofs(int patch, Array<int> &dofs) const;
|
||||
/// @brief The same as GetBdrElementDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
virtual void GetBdrElementDofs(int bel, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
/// @brief Returns the indices of the degrees of freedom for the specified
|
||||
/// face, including the DOFs for the edges and the vertices of the face.
|
||||
@@ -870,6 +882,13 @@ public:
|
||||
/// GetElementInteriorVDofs().
|
||||
void GetElementInteriorDofs(int i, Array<int> &dofs) const;
|
||||
|
||||
/// @brief Returns the number of degrees of freedom associated with the
|
||||
/// interior of the specified element.
|
||||
///
|
||||
/// See GetElementInteriorDofs() for more information or to obtain the
|
||||
/// relevant indices.
|
||||
int GetNumElementInteriorDofs(int i) const;
|
||||
|
||||
/// @brief Returns the indices of the degrees of freedom for the interior
|
||||
/// of the specified face.
|
||||
///
|
||||
@@ -882,13 +901,6 @@ public:
|
||||
/// GetFaceInteriorVDofs().
|
||||
void GetFaceInteriorDofs(int i, Array<int> &dofs) const;
|
||||
|
||||
/// @brief Returns the number of degrees of freedom associated with the
|
||||
/// interior of the specified element.
|
||||
///
|
||||
/// See GetElementInteriorDofs() for more information or to obtain the
|
||||
/// relevant indices.
|
||||
int GetNumElementInteriorDofs(int i) const;
|
||||
|
||||
/// @brief Returns the indices of the degrees of freedom for the interior
|
||||
/// of the specified edge.
|
||||
///
|
||||
@@ -897,6 +909,11 @@ public:
|
||||
void GetEdgeInteriorDofs(int i, Array<int> &dofs) const;
|
||||
///@}
|
||||
|
||||
/** @brief Returns indices of degrees of freedom for NURBS patch index
|
||||
@a patch. Cartesian ordering is used, for the tensor-product degrees of
|
||||
freedom. */
|
||||
void GetPatchDofs(int patch, Array<int> &dofs) const;
|
||||
|
||||
/// @anchor dof2vdof @name DoF To VDoF Conversion methods
|
||||
/// These methods convert between local dof and local vector dof using the
|
||||
/// appropriate relationship based on the Ordering::Type defined in this
|
||||
@@ -1023,6 +1040,15 @@ public:
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
DofTransformation *GetElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// @brief The same as GetElementVDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
void GetElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
/// @brief Returns indices of degrees of freedom for @a i'th boundary
|
||||
/// element.
|
||||
/// The returned indices are offsets into an @ref ldof vector with @b vdim
|
||||
@@ -1038,6 +1064,15 @@ public:
|
||||
/// @note The returned object should NOT be deleted by the caller.
|
||||
DofTransformation *GetBdrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// @brief The same as GetBdrElementVDofs(), but with a user-allocated
|
||||
/// DofTransformation object. @a doftrans must be allocated in advance and
|
||||
/// will be owned by the caller. The user can use the
|
||||
/// DofTransformation::GetDofTransformation method on the returned
|
||||
/// @a doftrans object to detect if the DofTransformation should actually be
|
||||
/// used.
|
||||
void GetBdrElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const;
|
||||
|
||||
/// Returns indices of degrees of freedom in @a vdofs for NURBS patch @a i.
|
||||
void GetPatchVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
|
||||
+9
-9
@@ -31,13 +31,13 @@ FmsBasisTypeToMfemBasis(FmsBasisType b)
|
||||
switch (b)
|
||||
{
|
||||
case FMS_NODAL_GAUSS_OPEN:
|
||||
retval = mfem::BasisType::GaussLegendre;;
|
||||
retval = mfem::BasisType::GaussLegendre;
|
||||
break;
|
||||
case FMS_NODAL_GAUSS_CLOSED:
|
||||
retval = mfem::BasisType::GaussLobatto;;
|
||||
retval = mfem::BasisType::GaussLobatto;
|
||||
break;
|
||||
case FMS_POSITIVE:
|
||||
retval = mfem::BasisType::Positive;;
|
||||
retval = mfem::BasisType::Positive;
|
||||
break;
|
||||
case FMS_NODAL_UNIFORM_OPEN:
|
||||
retval = mfem::BasisType::OpenUniform;
|
||||
@@ -1812,22 +1812,22 @@ MeshToFmsMesh(const Mesh *mmesh, FmsMesh *fmesh, FmsComponent *volume)
|
||||
switch (betype)
|
||||
{
|
||||
case Element::POINT:
|
||||
bdr_eles[FMS_VERTEX].push_back(mmesh->GetBdrElementEdgeIndex(i));
|
||||
bdr_eles[FMS_VERTEX].push_back(mmesh->GetBdrElementFaceIndex(i));
|
||||
break;
|
||||
case Element::SEGMENT:
|
||||
bdr_eles[FMS_EDGE].push_back(mmesh->GetBdrElementEdgeIndex(i));
|
||||
bdr_eles[FMS_EDGE].push_back(mmesh->GetBdrElementFaceIndex(i));
|
||||
break;
|
||||
case Element::TRIANGLE:
|
||||
bdr_eles[FMS_TRIANGLE].push_back(mmesh->GetBdrElementEdgeIndex(i));
|
||||
bdr_eles[FMS_TRIANGLE].push_back(mmesh->GetBdrElementFaceIndex(i));
|
||||
break;
|
||||
case Element::QUADRILATERAL:
|
||||
bdr_eles[FMS_QUADRILATERAL].push_back(mmesh->GetBdrElementEdgeIndex(i));
|
||||
bdr_eles[FMS_QUADRILATERAL].push_back(mmesh->GetBdrElementFaceIndex(i));
|
||||
break;
|
||||
case Element::TETRAHEDRON:
|
||||
bdr_eles[FMS_TETRAHEDRON].push_back(mmesh->GetBdrElementEdgeIndex(i));
|
||||
bdr_eles[FMS_TETRAHEDRON].push_back(mmesh->GetBdrElementFaceIndex(i));
|
||||
break;
|
||||
case Element::HEXAHEDRON:
|
||||
bdr_eles[FMS_HEXAHEDRON].push_back(mmesh->GetBdrElementEdgeIndex(i));
|
||||
bdr_eles[FMS_HEXAHEDRON].push_back(mmesh->GetBdrElementFaceIndex(i));
|
||||
break;
|
||||
default:
|
||||
MFEM_WARNING("Unsupported boundary element " << betype << " at boundary index "
|
||||
|
||||
+44
-113
@@ -720,56 +720,6 @@ void GridFunction::GetVectorValues(int i, const IntegrationRule &ir,
|
||||
GetVectorValues(*Tr, ir, vals);
|
||||
}
|
||||
|
||||
void be_to_bfe(Geometry::Type geom, int o, const IntegrationPoint &ip,
|
||||
IntegrationPoint &fip)
|
||||
{
|
||||
if (geom == Geometry::TRIANGLE)
|
||||
{
|
||||
if (o == 2)
|
||||
{
|
||||
fip.x = 1.0 - ip.x - ip.y;
|
||||
fip.y = ip.x;
|
||||
}
|
||||
else if (o == 4)
|
||||
{
|
||||
fip.x = ip.y;
|
||||
fip.y = 1.0 - ip.x - ip.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
fip.x = ip.x;
|
||||
fip.y = ip.y;
|
||||
}
|
||||
fip.z = ip.z;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (o == 2)
|
||||
{
|
||||
fip.x = ip.y;
|
||||
fip.y = 1.0 - ip.x;
|
||||
}
|
||||
else if (o == 4)
|
||||
{
|
||||
fip.x = 1.0 - ip.x;
|
||||
fip.y = 1.0 - ip.y;
|
||||
}
|
||||
else if (o == 6)
|
||||
{
|
||||
fip.x = 1.0 - ip.y;
|
||||
fip.y = ip.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
fip.x = ip.x;
|
||||
fip.y = ip.y;
|
||||
}
|
||||
fip.z = ip.z;
|
||||
}
|
||||
fip.weight = ip.weight;
|
||||
fip.index = ip.index;
|
||||
}
|
||||
|
||||
double GridFunction::GetValue(ElementTransformation &T,
|
||||
const IntegrationPoint &ip,
|
||||
int comp, Vector *tr) const
|
||||
@@ -834,18 +784,15 @@ double GridFunction::GetValue(ElementTransformation &T,
|
||||
// boundary so we'll evaluate it in the neighboring element.
|
||||
FaceElementTransformations * FET =
|
||||
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
|
||||
MFEM_ASSERT(FET != nullptr,
|
||||
"FaceElementTransformation must be valid for a boundary element");
|
||||
|
||||
// Boundary elements and Boundary Faces may have different
|
||||
// Boundary elements and boundary faces may have different
|
||||
// orientations so adjust the integration point if necessary.
|
||||
int o = 0;
|
||||
if (fes->GetMesh()->Dimension() == 3)
|
||||
{
|
||||
int f;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
}
|
||||
|
||||
IntegrationPoint fip;
|
||||
be_to_bfe(FET->GetGeometryType(), o, ip, fip);
|
||||
int f, o;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
IntegrationPoint fip =
|
||||
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o, ip);
|
||||
|
||||
// Compute and set the point in element 1 from fip
|
||||
FET->SetAllIntPoints(&fip);
|
||||
@@ -973,18 +920,15 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
// the boundary so we'll evaluate it in the neighboring element.
|
||||
FaceElementTransformations * FET =
|
||||
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
|
||||
MFEM_ASSERT(FET != nullptr,
|
||||
"FaceElementTransformation must be valid for a boundary element");
|
||||
|
||||
// Boundary elements and Boundary Faces may have different
|
||||
// Boundary elements and boundary faces may have different
|
||||
// orientations so adjust the integration point if necessary.
|
||||
int o = 0;
|
||||
if (fes->GetMesh()->Dimension() == 3)
|
||||
{
|
||||
int f;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
}
|
||||
|
||||
IntegrationPoint fip;
|
||||
be_to_bfe(FET->GetGeometryType(), o, ip, fip);
|
||||
int f, o;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
IntegrationPoint fip =
|
||||
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o, ip);
|
||||
|
||||
// Compute and set the point in element 1 from fip
|
||||
FET->SetAllIntPoints(&fip);
|
||||
@@ -997,6 +941,8 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
{
|
||||
FaceElementTransformations * FET =
|
||||
dynamic_cast<FaceElementTransformations *>(&T);
|
||||
MFEM_ASSERT(FET != nullptr,
|
||||
"FaceElementTransformation must be valid for a boundary element");
|
||||
|
||||
// Evaluate in neighboring element for both continuous and
|
||||
// discontinuous fields (the integration point in T1 should have
|
||||
@@ -1115,11 +1061,10 @@ int GridFunction::GetFaceVectorValues(
|
||||
int i, int side, const IntegrationRule &ir,
|
||||
DenseMatrix &vals, DenseMatrix &tr) const
|
||||
{
|
||||
int n, di;
|
||||
int di;
|
||||
FaceElementTransformations *Transf;
|
||||
|
||||
n = ir.GetNPoints();
|
||||
IntegrationRule eir(n); // ---
|
||||
IntegrationRule eir(ir.GetNPoints()); // ---
|
||||
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 0);
|
||||
if (side == 2)
|
||||
{
|
||||
@@ -1141,12 +1086,14 @@ int GridFunction::GetFaceVectorValues(
|
||||
if (di == 0)
|
||||
{
|
||||
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 5);
|
||||
MFEM_ASSERT(Transf != nullptr, "FaceElementTransformation cannot be null!");
|
||||
Transf->Loc1.Transform(ir, eir);
|
||||
GetVectorValues(*Transf->Elem1, eir, vals, &tr);
|
||||
}
|
||||
else
|
||||
{
|
||||
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 10);
|
||||
MFEM_ASSERT(Transf != nullptr, "FaceElementTransformation cannot be null!");
|
||||
Transf->Loc2.Transform(ir, eir);
|
||||
GetVectorValues(*Transf->Elem2, eir, vals, &tr);
|
||||
}
|
||||
@@ -1504,17 +1451,13 @@ double GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
FaceElementTransformations * FET =
|
||||
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
|
||||
|
||||
// Boundary elements and Boundary Faces may have different
|
||||
// Boundary elements and boundary faces may have different
|
||||
// orientations so adjust the integration point if necessary.
|
||||
int o = 0;
|
||||
if (fes->GetMesh()->Dimension() == 3)
|
||||
{
|
||||
int f;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
}
|
||||
|
||||
IntegrationPoint fip;
|
||||
be_to_bfe(FET->GetGeometryType(), o, T.GetIntPoint(), fip);
|
||||
int f, o;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
IntegrationPoint fip =
|
||||
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o,
|
||||
T.GetIntPoint());
|
||||
|
||||
// Compute and set the point in element 1 from fip
|
||||
FET->SetAllIntPoints(&fip);
|
||||
@@ -1601,17 +1544,13 @@ void GridFunction::GetCurl(ElementTransformation &T, Vector &curl) const
|
||||
FaceElementTransformations * FET =
|
||||
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
|
||||
|
||||
// Boundary elements and Boundary Faces may have different
|
||||
// Boundary elements and boundary faces may have different
|
||||
// orientations so adjust the integration point if necessary.
|
||||
int o = 0;
|
||||
if (fes->GetMesh()->Dimension() == 3)
|
||||
{
|
||||
int f;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
}
|
||||
|
||||
IntegrationPoint fip;
|
||||
be_to_bfe(FET->GetGeometryType(), o, T.GetIntPoint(), fip);
|
||||
int f, o;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
IntegrationPoint fip =
|
||||
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o,
|
||||
T.GetIntPoint());
|
||||
|
||||
// Compute and set the point in element 1 from fip
|
||||
FET->SetAllIntPoints(&fip);
|
||||
@@ -1670,17 +1609,13 @@ void GridFunction::GetGradient(ElementTransformation &T, Vector &grad) const
|
||||
FaceElementTransformations * FET =
|
||||
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
|
||||
|
||||
// Boundary elements and Boundary Faces may have different
|
||||
// Boundary elements and boundary faces may have different
|
||||
// orientations so adjust the integration point if necessary.
|
||||
int o = 0;
|
||||
if (fes->GetMesh()->Dimension() == 3)
|
||||
{
|
||||
int f;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
}
|
||||
|
||||
IntegrationPoint fip;
|
||||
be_to_bfe(FET->GetGeometryType(), o, T.GetIntPoint(), fip);
|
||||
int f, o;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
IntegrationPoint fip =
|
||||
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o,
|
||||
T.GetIntPoint());
|
||||
|
||||
// Compute and set the point in element 1 from fip
|
||||
FET->SetAllIntPoints(&fip);
|
||||
@@ -1756,17 +1691,13 @@ void GridFunction::GetVectorGradient(
|
||||
FaceElementTransformations * FET =
|
||||
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
|
||||
|
||||
// Boundary elements and Boundary Faces may have different
|
||||
// Boundary elements and boundary faces may have different
|
||||
// orientations so adjust the integration point if necessary.
|
||||
int o = 0;
|
||||
if (fes->GetMesh()->Dimension() == 3)
|
||||
{
|
||||
int f;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
}
|
||||
|
||||
IntegrationPoint fip;
|
||||
be_to_bfe(FET->GetGeometryType(), o, T.GetIntPoint(), fip);
|
||||
int f, o;
|
||||
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
|
||||
IntegrationPoint fip =
|
||||
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o,
|
||||
T.GetIntPoint());
|
||||
|
||||
// Compute and set the point in element 1 from fip
|
||||
FET->SetAllIntPoints(&fip);
|
||||
|
||||
+35
-8
@@ -10,6 +10,7 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "gslib.hpp"
|
||||
#include "geom.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
@@ -238,7 +239,8 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
}
|
||||
|
||||
// Map element number for simplices, and ref_pos from [-1,1] to [0,1] for
|
||||
// both simplices and quads.
|
||||
// both simplices and quads. Also sets code to 1 for points found on element
|
||||
// faces/edges.
|
||||
MapRefPosAndElemIndices();
|
||||
}
|
||||
|
||||
@@ -681,6 +683,9 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
int nptorig = points_cnt,
|
||||
npt = points_cnt;
|
||||
|
||||
// tolerance for point to be marked as on element edge/face
|
||||
double btol = 1e-12;
|
||||
|
||||
GridFunction *gf_rst_map_temp = NULL;
|
||||
int nptsend = 0;
|
||||
|
||||
@@ -694,7 +699,7 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
|
||||
// Pack data to send via crystal router
|
||||
struct gslib::array *outpt = new gslib::array;
|
||||
struct out_pt { double r[3]; uint index, el, proc; };
|
||||
struct out_pt { double r[3]; uint index, el, proc, code; };
|
||||
struct out_pt *pt;
|
||||
array_init(struct out_pt, outpt, nptsend);
|
||||
outpt->n=nptsend;
|
||||
@@ -712,12 +717,12 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
pt->index = index;
|
||||
pt->proc = gsl_proc[index];
|
||||
pt->el = gsl_elem[index];
|
||||
pt->code = gsl_code[index];
|
||||
++pt;
|
||||
}
|
||||
|
||||
// Transfer data to target MPI ranks
|
||||
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
|
||||
|
||||
// Map received points
|
||||
npt = outpt->n;
|
||||
pt = (struct out_pt *)outpt->ptr;
|
||||
@@ -731,7 +736,13 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
const Geometry::Type gt = fe->GetGeomType();
|
||||
pt->el = mesh_elem;
|
||||
|
||||
if (gt == Geometry::SQUARE || gt == Geometry::CUBE) { ++pt; continue; }
|
||||
if (gt == Geometry::SQUARE || gt == Geometry::CUBE)
|
||||
{
|
||||
// check if it is on element boundary
|
||||
pt->code = Geometry::CheckPoint(gt, ip, -btol) ? 0 : 1;
|
||||
++pt;
|
||||
continue;
|
||||
}
|
||||
else if (gt == Geometry::TRIANGLE)
|
||||
{
|
||||
gf_rst_map_temp = gf_rst_map[0];
|
||||
@@ -758,6 +769,10 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
{
|
||||
pt->r[d] = mfem_ref(d);
|
||||
}
|
||||
|
||||
// check if point is on element boundary
|
||||
ip.Set3(&pt->r[0]);
|
||||
pt->code = Geometry::CheckPoint(gt, ip, -btol) ? 0 : 1;
|
||||
++pt;
|
||||
}
|
||||
|
||||
@@ -774,6 +789,7 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
{
|
||||
gsl_mfem_ref(d + pt->index*dim) = pt->r[d];
|
||||
}
|
||||
gsl_code[pt->index] = pt->code;
|
||||
++pt;
|
||||
}
|
||||
array_free(outpt);
|
||||
@@ -784,12 +800,22 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
{
|
||||
if (gsl_code[index] != 2 && gsl_proc[index] == gsl_comm->id)
|
||||
{
|
||||
|
||||
IntegrationPoint ip;
|
||||
Vector mfem_ref(gsl_mfem_ref.GetData()+index*dim, dim);
|
||||
ip.Set2(mfem_ref.GetData());
|
||||
if (dim == 3) { ip.z = mfem_ref(2); }
|
||||
|
||||
const int elem = gsl_elem[index];
|
||||
const int mesh_elem = split_element_map[elem];
|
||||
const FiniteElement *fe = mesh->GetNodalFESpace()->GetFE(mesh_elem);
|
||||
const Geometry::Type gt = fe->GetGeomType();
|
||||
gsl_mfem_elem[index] = mesh_elem;
|
||||
if (gt == Geometry::SQUARE || gt == Geometry::CUBE) { continue; }
|
||||
if (gt == Geometry::SQUARE || gt == Geometry::CUBE)
|
||||
{
|
||||
gsl_code[index] = Geometry::CheckPoint(gt, ip, -btol) ? 0 : 1;
|
||||
continue;
|
||||
}
|
||||
else if (gt == Geometry::TRIANGLE)
|
||||
{
|
||||
gf_rst_map_temp = gf_rst_map[0];
|
||||
@@ -808,11 +834,12 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
}
|
||||
|
||||
int local_elem = split_element_index[elem];
|
||||
IntegrationPoint ip;
|
||||
Vector mfem_ref(gsl_mfem_ref.GetData()+index*dim, dim);
|
||||
gf_rst_map_temp->GetVectorValue(local_elem, ip, mfem_ref);
|
||||
|
||||
// Check if the point is on element boundary
|
||||
ip.Set2(mfem_ref.GetData());
|
||||
if (dim == 3) { ip.z = mfem_ref(2); }
|
||||
gf_rst_map_temp->GetVectorValue(local_elem, ip, mfem_ref);
|
||||
gsl_code[index] = Geometry::CheckPoint(gt, ip, -btol) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ void MassIntegrator::AssemblePABoundary(const FiniteElementSpace &fes)
|
||||
|
||||
int map_type = el.GetMapType();
|
||||
dim = el.GetDim(); // Dimension of the boundary element, *not* the mesh
|
||||
ne = fes.GetMesh()->GetNBE();
|
||||
ne = fes.GetMesh()->GetNFbyType(FaceType::Boundary);
|
||||
nq = ir->GetNPoints();
|
||||
face_geom = mesh->GetFaceGeometricFactors(*ir, GeometricFactors::DETERMINANTS,
|
||||
FaceType::Boundary, mt);
|
||||
|
||||
+1
-1
@@ -137,7 +137,7 @@ bool LinearForm::SupportsDevice() const
|
||||
// Make sure every boundary element corresponds to a boundary face
|
||||
for (int be = 0; be < fes->GetNBE(); ++be)
|
||||
{
|
||||
const int f = mesh.GetBdrElementEdgeIndex(be);
|
||||
const int f = mesh.GetBdrElementFaceIndex(be);
|
||||
const auto face_info = mesh.GetFaceInformation(f);
|
||||
if (!face_info.IsBoundary())
|
||||
{
|
||||
|
||||
@@ -148,7 +148,7 @@ void LinearFormExtension::Update()
|
||||
std::unordered_map<int,int> f_to_be;
|
||||
for (int i = 0; i < mesh.GetNBE(); ++i)
|
||||
{
|
||||
const int f = mesh.GetBdrElementEdgeIndex(i);
|
||||
const int f = mesh.GetBdrElementFaceIndex(i);
|
||||
f_to_be[f] = i;
|
||||
}
|
||||
MFEM_VERIFY(size_t(nf_bdr) == f_to_be.size(), "Incompatible sizes");
|
||||
|
||||
+1
-1
@@ -395,8 +395,8 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
|
||||
Q.Eval(vec, Tr, ip);
|
||||
Tr.SetIntPoint (&ip);
|
||||
Q.Eval(vec, Tr, ip);
|
||||
vec *= Tr.Weight() * ip.weight;
|
||||
el.CalcShape(ip, shape);
|
||||
for (int k = 0; k < vdim; k++)
|
||||
|
||||
+8
-8
@@ -257,13 +257,13 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
// vertices of the LOR mesh. The vertex coordinates are already computed in
|
||||
// E-vector format and passed in in X_vert.
|
||||
//
|
||||
// In this function, we need to convert X_vert (which has the shape (dim,
|
||||
// In this function, we need to convert X_vert (which has the shape (sdim,
|
||||
// ndof_per_el, nel_ho)) to T-DOF format.
|
||||
//
|
||||
// We place the results in the vector xyz_tvec, which has shape (ntdofs, dim)
|
||||
// We place the results in the vector xyz_tvec, which has shape (ntdofs, sdim)
|
||||
// and then make the hypre vectors x, y, and z point to subvectors.
|
||||
//
|
||||
// In 2D, z is NULL.
|
||||
// When the space dimension is 2, z is NULL.
|
||||
|
||||
// Create the H1 vertex space and get the element restriction
|
||||
ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC;
|
||||
@@ -275,17 +275,17 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
const int nel_ho = vert_fes.GetNE();
|
||||
const int ndp1 = order + 1;
|
||||
const int ndof_per_el = static_cast<int>(pow(ndp1, dim));
|
||||
const int sdim = dim;
|
||||
const int sdim = vert_fes.GetMesh()->SpaceDimension();
|
||||
const int ntdofs = R->Height();
|
||||
|
||||
const MemoryClass mc = GetHypreMemoryClass();
|
||||
bool dev = (mc == MemoryClass::DEVICE);
|
||||
|
||||
xyz_tvec = new Vector(ntdofs*dim);
|
||||
xyz_tvec = new Vector(ntdofs*sdim);
|
||||
|
||||
auto xyz_tv = Reshape(HypreWrite(xyz_tvec->GetMemory()), ntdofs, dim);
|
||||
auto xyz_tv = Reshape(HypreWrite(xyz_tvec->GetMemory()), ntdofs, sdim);
|
||||
const auto xyz_e =
|
||||
Reshape(HypreRead(X_vert.GetMemory()), dim, ndof_per_el, nel_ho);
|
||||
Reshape(HypreRead(X_vert.GetMemory()), sdim, ndof_per_el, nel_ho);
|
||||
const auto d_offsets = HypreRead(el_restr->Offsets().GetMemory());
|
||||
const auto d_indices = HypreRead(el_restr->Indices().GetMemory());
|
||||
const auto ltdof_ldof = HypreRead(R->GetMemoryJ());
|
||||
@@ -309,7 +309,7 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
x = new HypreParVector(vert_fes.GetComm(), glob_size, d_x_ptr, cols, dev);
|
||||
double *d_y_ptr = xyz_tv + 1*ntdofs;
|
||||
y = new HypreParVector(vert_fes.GetComm(), glob_size, d_y_ptr, cols, dev);
|
||||
if (dim == 3)
|
||||
if (sdim == 3)
|
||||
{
|
||||
double *d_z_ptr = xyz_tv + 2*ntdofs;
|
||||
z = new HypreParVector(vert_fes.GetComm(), glob_size, d_z_ptr, cols, dev);
|
||||
|
||||
+37
-31
@@ -77,6 +77,7 @@ void BatchedLORAssembly::FormLORVertexCoordinates(FiniteElementSpace &fes_ho,
|
||||
|
||||
// Get nodal points at the LOR vertices
|
||||
const int dim = mesh_ho.Dimension();
|
||||
const int sdim = mesh_ho.SpaceDimension();
|
||||
const int nel_ho = mesh_ho.GetNE();
|
||||
const int order = fes_ho.GetMaxElementOrder();
|
||||
const int nd1d = order + 1;
|
||||
@@ -94,7 +95,7 @@ void BatchedLORAssembly::FormLORVertexCoordinates(FiniteElementSpace &fes_ho,
|
||||
IntegrationRule ir = GetCollocatedIntRule(fes_ho);
|
||||
|
||||
// Map from nodal E-vector to Q-vector at the LOR vertex points
|
||||
X_vert.SetSize(dim*ndof_per_el*nel_ho);
|
||||
X_vert.SetSize(sdim*ndof_per_el*nel_ho);
|
||||
const QuadratureInterpolator *quad_interp =
|
||||
nodal_fes->GetQuadratureInterpolator(ir);
|
||||
quad_interp->SetOutputLayout(QVectorLayout::byVDIM);
|
||||
@@ -380,44 +381,49 @@ void BatchedLORAssembly::SparseIJToCSR(OperatorHandle &A) const
|
||||
FillJAndData(*A_mat);
|
||||
}
|
||||
|
||||
template <int ORDER, int SDIM, typename LOR_KERNEL>
|
||||
static void Assemble_(LOR_KERNEL &kernel, int dim)
|
||||
{
|
||||
if (dim == 2) { kernel.template Assemble2D<ORDER,SDIM>(); }
|
||||
else if (dim == 3) { kernel.template Assemble3D<ORDER>(); }
|
||||
else { MFEM_ABORT("Unsupported dimension"); }
|
||||
}
|
||||
|
||||
template <int ORDER, typename LOR_KERNEL>
|
||||
static void Assemble_(LOR_KERNEL &kernel, int dim, int sdim)
|
||||
{
|
||||
if (sdim == 2) { Assemble_<ORDER,2>(kernel, dim); }
|
||||
else if (sdim == 3) { Assemble_<ORDER,3>(kernel, dim); }
|
||||
else { MFEM_ABORT("Unsupported space dimension."); }
|
||||
}
|
||||
|
||||
template <typename LOR_KERNEL>
|
||||
static void Assemble_(LOR_KERNEL &kernel, int dim, int sdim, int order)
|
||||
{
|
||||
switch (order)
|
||||
{
|
||||
case 1: Assemble_<1>(kernel, dim, sdim); break;
|
||||
case 2: Assemble_<2>(kernel, dim, sdim); break;
|
||||
case 3: Assemble_<3>(kernel, dim, sdim); break;
|
||||
case 4: Assemble_<4>(kernel, dim, sdim); break;
|
||||
case 5: Assemble_<5>(kernel, dim, sdim); break;
|
||||
case 6: Assemble_<6>(kernel, dim, sdim); break;
|
||||
case 7: Assemble_<7>(kernel, dim, sdim); break;
|
||||
case 8: Assemble_<8>(kernel, dim, sdim); break;
|
||||
default: MFEM_ABORT("No kernel order " << order << "!");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename LOR_KERNEL>
|
||||
void BatchedLORAssembly::AssemblyKernel(BilinearForm &a)
|
||||
{
|
||||
LOR_KERNEL kernel(a, fes_ho, X_vert, sparse_ij, sparse_mapping);
|
||||
|
||||
const int dim = fes_ho.GetMesh()->Dimension();
|
||||
const int sdim = fes_ho.GetMesh()->SpaceDimension();
|
||||
const int order = fes_ho.GetMaxElementOrder();
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
switch (order)
|
||||
{
|
||||
case 1: kernel.template Assemble2D<1>(); break;
|
||||
case 2: kernel.template Assemble2D<2>(); break;
|
||||
case 3: kernel.template Assemble2D<3>(); break;
|
||||
case 4: kernel.template Assemble2D<4>(); break;
|
||||
case 5: kernel.template Assemble2D<5>(); break;
|
||||
case 6: kernel.template Assemble2D<6>(); break;
|
||||
case 7: kernel.template Assemble2D<7>(); break;
|
||||
case 8: kernel.template Assemble2D<8>(); break;
|
||||
default: MFEM_ABORT("No kernel order " << order << "!");
|
||||
}
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
switch (order)
|
||||
{
|
||||
case 1: kernel.template Assemble3D<1>(); break;
|
||||
case 2: kernel.template Assemble3D<2>(); break;
|
||||
case 3: kernel.template Assemble3D<3>(); break;
|
||||
case 4: kernel.template Assemble3D<4>(); break;
|
||||
case 5: kernel.template Assemble3D<5>(); break;
|
||||
case 6: kernel.template Assemble3D<6>(); break;
|
||||
case 7: kernel.template Assemble3D<7>(); break;
|
||||
case 8: kernel.template Assemble3D<8>(); break;
|
||||
default: MFEM_ABORT("No kernel order " << order << "!");
|
||||
}
|
||||
}
|
||||
Assemble_(kernel, dim, sdim, order);
|
||||
}
|
||||
|
||||
void BatchedLORAssembly::AssembleWithoutBC(BilinearForm &a, OperatorHandle &A)
|
||||
|
||||
+9
-2
@@ -22,15 +22,22 @@ namespace mfem
|
||||
class BatchedLOR_H1 : BatchedLORKernel
|
||||
{
|
||||
public:
|
||||
template <int ORDER> void Assemble2D();
|
||||
template <int ORDER, int SDIM> void Assemble2D();
|
||||
template <int ORDER> void Assemble3D();
|
||||
BatchedLOR_H1(BilinearForm &a,
|
||||
FiniteElementSpace &fes_ho_,
|
||||
Vector &X_vert_,
|
||||
Vector &sparse_ij_,
|
||||
Array<int> &sparse_mapping_);
|
||||
Array<int> &sparse_mapping_)
|
||||
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
|
||||
{
|
||||
ProjectLORCoefficient<MassIntegrator>(a, c1);
|
||||
ProjectLORCoefficient<DiffusionIntegrator>(a, c2);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "lor_h1_impl.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "lor_h1.hpp"
|
||||
#include "lor_util.hpp"
|
||||
#include "../../linalg/dtensor.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
@@ -17,7 +16,7 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <int ORDER>
|
||||
template <int ORDER, int SDIM>
|
||||
void BatchedLOR_H1::Assemble2D()
|
||||
{
|
||||
const int nel_ho = fes_ho.GetNE();
|
||||
@@ -74,31 +73,8 @@ void BatchedLOR_H1::Assemble2D()
|
||||
|
||||
for (int i=0; i<sz_local_mat; ++i) { local_mat[i] = 0.0; }
|
||||
|
||||
double vx[4], vy[4];
|
||||
LORVertexCoordinates2D<ORDER>(X, iel_ho, kx, ky, vx, vy);
|
||||
SetupLORQuadData2D<ORDER,SDIM,false,false>(X, iel_ho, kx, ky, Q, false);
|
||||
|
||||
for (int iqy=0; iqy<2; ++iqy)
|
||||
{
|
||||
for (int iqx=0; iqx<2; ++iqx)
|
||||
{
|
||||
const double x = iqx;
|
||||
const double y = iqy;
|
||||
const double w = 1.0/4.0;
|
||||
|
||||
double J_[2*2];
|
||||
DeviceTensor<2> J(J_, 2, 2);
|
||||
|
||||
Jacobian2D(x, y, vx, vy, J);
|
||||
|
||||
const double detJ = Det2D(J);
|
||||
const double w_detJ = w/detJ;
|
||||
|
||||
Q(0,iqy,iqx) = w_detJ * (J(0,1)*J(0,1) + J(1,1)*J(1,1)); // 1,1
|
||||
Q(1,iqy,iqx) = -w_detJ * (J(0,1)*J(0,0) + J(1,1)*J(1,0)); // 1,2
|
||||
Q(2,iqy,iqx) = w_detJ * (J(0,0)*J(0,0) + J(1,0)*J(1,0)); // 2,2
|
||||
Q(3,iqy,iqx) = w*detJ;
|
||||
}
|
||||
}
|
||||
for (int iqx=0; iqx<2; ++iqx)
|
||||
{
|
||||
for (int iqy=0; iqy<2; ++iqy)
|
||||
@@ -519,34 +495,4 @@ void BatchedLOR_H1::Assemble3D()
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
template void BatchedLOR_H1::Assemble2D<1>();
|
||||
template void BatchedLOR_H1::Assemble2D<2>();
|
||||
template void BatchedLOR_H1::Assemble2D<3>();
|
||||
template void BatchedLOR_H1::Assemble2D<4>();
|
||||
template void BatchedLOR_H1::Assemble2D<5>();
|
||||
template void BatchedLOR_H1::Assemble2D<6>();
|
||||
template void BatchedLOR_H1::Assemble2D<7>();
|
||||
template void BatchedLOR_H1::Assemble2D<8>();
|
||||
|
||||
template void BatchedLOR_H1::Assemble3D<1>();
|
||||
template void BatchedLOR_H1::Assemble3D<2>();
|
||||
template void BatchedLOR_H1::Assemble3D<3>();
|
||||
template void BatchedLOR_H1::Assemble3D<4>();
|
||||
template void BatchedLOR_H1::Assemble3D<5>();
|
||||
template void BatchedLOR_H1::Assemble3D<6>();
|
||||
template void BatchedLOR_H1::Assemble3D<7>();
|
||||
template void BatchedLOR_H1::Assemble3D<8>();
|
||||
|
||||
BatchedLOR_H1::BatchedLOR_H1(BilinearForm &a,
|
||||
FiniteElementSpace &fes_ho_,
|
||||
Vector &X_vert_,
|
||||
Vector &sparse_ij_,
|
||||
Array<int> &sparse_mapping_)
|
||||
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
|
||||
{
|
||||
ProjectLORCoefficient<MassIntegrator>(a, c1);
|
||||
ProjectLORCoefficient<DiffusionIntegrator>(a, c2);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
+9
-2
@@ -22,15 +22,22 @@ namespace mfem
|
||||
class BatchedLOR_ND : BatchedLORKernel
|
||||
{
|
||||
public:
|
||||
template <int ORDER> void Assemble2D();
|
||||
template <int ORDER, int SDIM> void Assemble2D();
|
||||
template <int ORDER> void Assemble3D();
|
||||
BatchedLOR_ND(BilinearForm &a,
|
||||
FiniteElementSpace &fes_ho_,
|
||||
Vector &X_vert_,
|
||||
Vector &sparse_ij_,
|
||||
Array<int> &sparse_mapping_);
|
||||
Array<int> &sparse_mapping_)
|
||||
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
|
||||
{
|
||||
ProjectLORCoefficient<VectorFEMassIntegrator>(a, c1);
|
||||
ProjectLORCoefficient<CurlCurlIntegrator>(a, c2);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "lor_nd_impl.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "lor_nd.hpp"
|
||||
#include "lor_util.hpp"
|
||||
#include "../../linalg/dtensor.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
@@ -17,7 +16,7 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <int ORDER>
|
||||
template <int ORDER, int SDIM>
|
||||
void BatchedLOR_ND::Assemble2D()
|
||||
{
|
||||
const int nel_ho = fes_ho.GetNE();
|
||||
@@ -83,31 +82,8 @@ void BatchedLOR_ND::Assemble2D()
|
||||
// local_mat is the local (dense) stiffness matrix
|
||||
for (int i=0; i<sz_local_mat; ++i) { local_mat[i] = 0.0; }
|
||||
|
||||
double vx[4], vy[4];
|
||||
LORVertexCoordinates2D<ORDER>(X, iel_ho, kx, ky, vx, vy);
|
||||
SetupLORQuadData2D<ORDER,SDIM,false,true>(X, iel_ho, kx, ky, Q, true);
|
||||
|
||||
for (int iqx=0; iqx<2; ++iqx)
|
||||
{
|
||||
for (int iqy=0; iqy<2; ++iqy)
|
||||
{
|
||||
const double x = iqx;
|
||||
const double y = iqy;
|
||||
const double w = 1.0/4.0;
|
||||
|
||||
double J_[2*2];
|
||||
DeviceTensor<2> J(J_, 2, 2);
|
||||
|
||||
Jacobian2D(x, y, vx, vy, J);
|
||||
|
||||
const double detJ = Det2D(J);
|
||||
const double w_detJ = w/detJ;
|
||||
|
||||
Q(0,iqy,iqx) = w_detJ * (J(0,1)*J(0,1) + J(1,1)*J(1,1)); // 1,1
|
||||
Q(1,iqy,iqx) = -w_detJ * (J(0,1)*J(0,0) + J(1,1)*J(1,0)); // 1,2
|
||||
Q(2,iqy,iqx) = w_detJ * (J(0,0)*J(0,0) + J(1,0)*J(1,0)); // 2,2
|
||||
Q(3,iqy,iqx) = w_detJ;
|
||||
}
|
||||
}
|
||||
for (int iqx=0; iqx<2; ++iqx)
|
||||
{
|
||||
for (int iqy=0; iqy<2; ++iqy)
|
||||
@@ -563,34 +539,4 @@ void BatchedLOR_ND::Assemble3D()
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
template void BatchedLOR_ND::Assemble2D<1>();
|
||||
template void BatchedLOR_ND::Assemble2D<2>();
|
||||
template void BatchedLOR_ND::Assemble2D<3>();
|
||||
template void BatchedLOR_ND::Assemble2D<4>();
|
||||
template void BatchedLOR_ND::Assemble2D<5>();
|
||||
template void BatchedLOR_ND::Assemble2D<6>();
|
||||
template void BatchedLOR_ND::Assemble2D<7>();
|
||||
template void BatchedLOR_ND::Assemble2D<8>();
|
||||
|
||||
template void BatchedLOR_ND::Assemble3D<1>();
|
||||
template void BatchedLOR_ND::Assemble3D<2>();
|
||||
template void BatchedLOR_ND::Assemble3D<3>();
|
||||
template void BatchedLOR_ND::Assemble3D<4>();
|
||||
template void BatchedLOR_ND::Assemble3D<5>();
|
||||
template void BatchedLOR_ND::Assemble3D<6>();
|
||||
template void BatchedLOR_ND::Assemble3D<7>();
|
||||
template void BatchedLOR_ND::Assemble3D<8>();
|
||||
|
||||
BatchedLOR_ND::BatchedLOR_ND(BilinearForm &a,
|
||||
FiniteElementSpace &fes_ho_,
|
||||
Vector &X_vert_,
|
||||
Vector &sparse_ij_,
|
||||
Array<int> &sparse_mapping_)
|
||||
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
|
||||
{
|
||||
ProjectLORCoefficient<VectorFEMassIntegrator>(a, c1);
|
||||
ProjectLORCoefficient<CurlCurlIntegrator>(a, c2);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
+9
-2
@@ -22,15 +22,22 @@ namespace mfem
|
||||
class BatchedLOR_RT : BatchedLORKernel
|
||||
{
|
||||
public:
|
||||
template <int ORDER> void Assemble2D();
|
||||
template <int ORDER, int SDIM> void Assemble2D();
|
||||
template <int ORDER> void Assemble3D();
|
||||
BatchedLOR_RT(BilinearForm &a,
|
||||
FiniteElementSpace &fes_ho_,
|
||||
Vector &X_vert_,
|
||||
Vector &sparse_ij_,
|
||||
Array<int> &sparse_mapping_);
|
||||
Array<int> &sparse_mapping_)
|
||||
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
|
||||
{
|
||||
ProjectLORCoefficient<VectorFEMassIntegrator>(a, c1);
|
||||
ProjectLORCoefficient<DivDivIntegrator>(a, c2);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "lor_rt_impl.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "lor_rt.hpp"
|
||||
#include "lor_util.hpp"
|
||||
#include "../../linalg/dtensor.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
@@ -17,7 +16,7 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <int ORDER>
|
||||
template <int ORDER, int SDIM>
|
||||
void BatchedLOR_RT::Assemble2D()
|
||||
{
|
||||
const int nel_ho = fes_ho.GetNE();
|
||||
@@ -79,31 +78,8 @@ void BatchedLOR_RT::Assemble2D()
|
||||
// local_mat is the local (dense) stiffness matrix
|
||||
for (int i=0; i<sz_local_mat; ++i) { local_mat[i] = 0.0; }
|
||||
|
||||
double vx[4], vy[4];
|
||||
LORVertexCoordinates2D<ORDER>(X, iel_ho, kx, ky, vx, vy);
|
||||
SetupLORQuadData2D<ORDER,SDIM,true,false>(X, iel_ho, kx, ky, Q, true);
|
||||
|
||||
for (int iqx=0; iqx<2; ++iqx)
|
||||
{
|
||||
for (int iqy=0; iqy<2; ++iqy)
|
||||
{
|
||||
const double x = iqx;
|
||||
const double y = iqy;
|
||||
const double w = 1.0/4.0;
|
||||
|
||||
double J_[2*2];
|
||||
DeviceTensor<2> J(J_, 2, 2);
|
||||
|
||||
Jacobian2D(x, y, vx, vy, J);
|
||||
|
||||
const double detJ = Det2D(J);
|
||||
const double w_detJ = w/detJ;
|
||||
|
||||
Q(0,iqy,iqx) = w_detJ * (J(0,0)*J(0,0) + J(1,0)*J(1,0)); // 1,1
|
||||
Q(1,iqy,iqx) = w_detJ * (J(0,0)*J(0,1) + J(1,0)*J(1,1)); // 1,2
|
||||
Q(2,iqy,iqx) = w_detJ * (J(0,1)*J(0,1) + J(1,1)*J(1,1)); // 2,2
|
||||
Q(3,iqy,iqx) = w_detJ;
|
||||
}
|
||||
}
|
||||
for (int iqx=0; iqx<2; ++iqx)
|
||||
{
|
||||
for (int iqy=0; iqy<2; ++iqy)
|
||||
@@ -547,34 +523,4 @@ void BatchedLOR_RT::Assemble3D()
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit template instantiations
|
||||
template void BatchedLOR_RT::Assemble2D<1>();
|
||||
template void BatchedLOR_RT::Assemble2D<2>();
|
||||
template void BatchedLOR_RT::Assemble2D<3>();
|
||||
template void BatchedLOR_RT::Assemble2D<4>();
|
||||
template void BatchedLOR_RT::Assemble2D<5>();
|
||||
template void BatchedLOR_RT::Assemble2D<6>();
|
||||
template void BatchedLOR_RT::Assemble2D<7>();
|
||||
template void BatchedLOR_RT::Assemble2D<8>();
|
||||
|
||||
template void BatchedLOR_RT::Assemble3D<1>();
|
||||
template void BatchedLOR_RT::Assemble3D<2>();
|
||||
template void BatchedLOR_RT::Assemble3D<3>();
|
||||
template void BatchedLOR_RT::Assemble3D<4>();
|
||||
template void BatchedLOR_RT::Assemble3D<5>();
|
||||
template void BatchedLOR_RT::Assemble3D<6>();
|
||||
template void BatchedLOR_RT::Assemble3D<7>();
|
||||
template void BatchedLOR_RT::Assemble3D<8>();
|
||||
|
||||
BatchedLOR_RT::BatchedLOR_RT(BilinearForm &a,
|
||||
FiniteElementSpace &fes_ho_,
|
||||
Vector &X_vert_,
|
||||
Vector &sparse_ij_,
|
||||
Array<int> &sparse_mapping_)
|
||||
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
|
||||
{
|
||||
ProjectLORCoefficient<VectorFEMassIntegrator>(a, c1);
|
||||
ProjectLORCoefficient<DivDivIntegrator>(a, c2);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
+107
-35
@@ -20,11 +20,22 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template <int ORDER>
|
||||
MFEM_HOST_DEVICE inline void LORVertexCoordinates2D(
|
||||
const double *X, int iel_ho, int kx, int ky, double vx[4], double vy[4])
|
||||
MFEM_HOST_DEVICE inline double Det2D(DeviceMatrix &J)
|
||||
{
|
||||
return J(0,0)*J(1,1) - J(1,0)*J(0,1);
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline double Det3D(DeviceMatrix &J)
|
||||
{
|
||||
return J(0,0) * (J(1,1) * J(2,2) - J(2,1) * J(1,2)) -
|
||||
J(1,0) * (J(0,1) * J(2,2) - J(2,1) * J(0,2)) +
|
||||
J(2,0) * (J(0,1) * J(1,2) - J(1,1) * J(0,2));
|
||||
}
|
||||
|
||||
template <int ORDER, int SDIM=2>
|
||||
MFEM_HOST_DEVICE inline void LORVertexCoordinates2D(
|
||||
const double *X, int iel_ho, int kx, int ky, double **v)
|
||||
{
|
||||
const int dim = 2;
|
||||
const int nd1d = ORDER + 1;
|
||||
const int nvert_per_el = nd1d*nd1d;
|
||||
|
||||
@@ -33,23 +44,31 @@ MFEM_HOST_DEVICE inline void LORVertexCoordinates2D(
|
||||
const int v2 = kx + 1 + nd1d*(ky + 1);
|
||||
const int v3 = kx + nd1d*(ky + 1);
|
||||
|
||||
const int e0 = dim*(v0 + nvert_per_el*iel_ho);
|
||||
const int e1 = dim*(v1 + nvert_per_el*iel_ho);
|
||||
const int e2 = dim*(v2 + nvert_per_el*iel_ho);
|
||||
const int e3 = dim*(v3 + nvert_per_el*iel_ho);
|
||||
const int e0 = SDIM*(v0 + nvert_per_el*iel_ho);
|
||||
const int e1 = SDIM*(v1 + nvert_per_el*iel_ho);
|
||||
const int e2 = SDIM*(v2 + nvert_per_el*iel_ho);
|
||||
const int e3 = SDIM*(v3 + nvert_per_el*iel_ho);
|
||||
|
||||
// Vertex coordinates
|
||||
vx[0] = X[e0 + 0];
|
||||
vy[0] = X[e0 + 1];
|
||||
v[0][0] = X[e0 + 0];
|
||||
v[1][0] = X[e0 + 1];
|
||||
|
||||
vx[1] = X[e1 + 0];
|
||||
vy[1] = X[e1 + 1];
|
||||
v[0][1] = X[e1 + 0];
|
||||
v[1][1] = X[e1 + 1];
|
||||
|
||||
vx[2] = X[e2 + 0];
|
||||
vy[2] = X[e2 + 1];
|
||||
v[0][2] = X[e2 + 0];
|
||||
v[1][2] = X[e2 + 1];
|
||||
|
||||
vx[3] = X[e3 + 0];
|
||||
vy[3] = X[e3 + 1];
|
||||
v[0][3] = X[e3 + 0];
|
||||
v[1][3] = X[e3 + 1];
|
||||
|
||||
if (SDIM == 3)
|
||||
{
|
||||
v[2][0] = X[e0 + 2];
|
||||
v[2][1] = X[e1 + 2];
|
||||
v[2][2] = X[e2 + 2];
|
||||
v[2][3] = X[e3 + 2];
|
||||
}
|
||||
}
|
||||
|
||||
template <int ORDER>
|
||||
@@ -112,15 +131,80 @@ MFEM_HOST_DEVICE inline void LORVertexCoordinates3D(
|
||||
vz[7] = X[e7 + 2];
|
||||
}
|
||||
|
||||
template <int SDIM=2>
|
||||
MFEM_HOST_DEVICE inline void Jacobian2D(
|
||||
const double x, const double y, const double vx[4], const double vy[4],
|
||||
DeviceMatrix &J)
|
||||
{
|
||||
J(0,0) = -(1-y)*vx[0] + (1-y)*vx[1] + y*vx[2] - y*vx[3];
|
||||
J(0,1) = -(1-x)*vx[0] - x*vx[1] + x*vx[2] + (1-x)*vx[3];
|
||||
const double x, const double y, double **v, DeviceMatrix &J);
|
||||
|
||||
J(1,0) = -(1-y)*vy[0] + (1-y)*vy[1] + y*vy[2] - y*vy[3];
|
||||
J(1,1) = -(1-x)*vy[0] - x*vy[1] + x*vy[2] + (1-x)*vy[3];
|
||||
template <> MFEM_HOST_DEVICE inline void Jacobian2D<2>(
|
||||
const double x, const double y, double **v, DeviceMatrix &J)
|
||||
{
|
||||
J(0,0) = -(1-y)*v[0][0] + (1-y)*v[0][1] + y*v[0][2] - y*v[0][3];
|
||||
J(0,1) = -(1-x)*v[0][0] - x*v[0][1] + x*v[0][2] + (1-x)*v[0][3];
|
||||
|
||||
J(1,0) = -(1-y)*v[1][0] + (1-y)*v[1][1] + y*v[1][2] - y*v[1][3];
|
||||
J(1,1) = -(1-x)*v[1][0] - x*v[1][1] + x*v[1][2] + (1-x)*v[1][3];
|
||||
}
|
||||
|
||||
template <> MFEM_HOST_DEVICE inline void Jacobian2D<3>(
|
||||
const double x, const double y, double **v, DeviceMatrix &J)
|
||||
{
|
||||
J(0,0) = -(1-y)*v[0][0] + (1-y)*v[0][1] + y*v[0][2] - y*v[0][3];
|
||||
J(0,1) = -(1-x)*v[0][0] - x*v[0][1] + x*v[0][2] + (1-x)*v[0][3];
|
||||
|
||||
J(1,0) = -(1-y)*v[1][0] + (1-y)*v[1][1] + y*v[1][2] - y*v[1][3];
|
||||
J(1,1) = -(1-x)*v[1][0] - x*v[1][1] + x*v[1][2] + (1-x)*v[1][3];
|
||||
|
||||
J(2,0) = -(1-y)*v[2][0] + (1-y)*v[2][1] + y*v[2][2] - y*v[2][3];
|
||||
J(2,1) = -(1-x)*v[2][0] - x*v[2][1] + x*v[2][2] + (1-x)*v[2][3];
|
||||
}
|
||||
|
||||
template <int ORDER, int SDIM, bool RT, bool ND>
|
||||
MFEM_HOST_DEVICE inline void SetupLORQuadData2D(
|
||||
const double *X, int iel_ho, int kx, int ky, DeviceTensor<3> &Q, bool piola)
|
||||
{
|
||||
double vx[4], vy[4], vz[4];
|
||||
double *v[] = {vx, vy, vz};
|
||||
LORVertexCoordinates2D<ORDER,SDIM>(X, iel_ho, kx, ky, v);
|
||||
|
||||
for (int iqy=0; iqy<2; ++iqy)
|
||||
{
|
||||
for (int iqx=0; iqx<2; ++iqx)
|
||||
{
|
||||
const double x = iqx;
|
||||
const double y = iqy;
|
||||
const double w = 1.0/4.0;
|
||||
|
||||
double J_[SDIM*2];
|
||||
DeviceTensor<2> J(J_, SDIM, 2);
|
||||
|
||||
Jacobian2D<SDIM>(x, y, v, J);
|
||||
|
||||
if (SDIM == 2)
|
||||
{
|
||||
const double detJ = Det2D(J);
|
||||
const double w_detJ = w/detJ;
|
||||
const double E = J(0,0)*J(0,0) + J(1,0)*J(1,0);
|
||||
const double F = J(0,0)*J(0,1) + J(1,0)*J(1,1);
|
||||
const double G = J(0,1)*J(0,1) + J(1,1)*J(1,1);
|
||||
Q(0,iqy,iqx) = w_detJ * (RT ? E : G); // 1,1
|
||||
Q(1,iqy,iqx) = w_detJ * (RT ? F : -F); // 1,2
|
||||
Q(2,iqy,iqx) = w_detJ * (RT ? G : E); // 2,2
|
||||
Q(3,iqy,iqx) = (ND || RT) ? w_detJ : w*detJ;
|
||||
}
|
||||
else
|
||||
{
|
||||
const double E = J(0,0)*J(0,0) + J(1,0)*J(1,0) + J(2,0)*J(2,0);
|
||||
const double F = J(0,0)*J(0,1) + J(1,0)*J(1,1) + J(2,0)*J(2,1);
|
||||
const double G = J(0,1)*J(0,1) + J(1,1)*J(1,1) + J(2,1)*J(2,1);
|
||||
const double detJ = sqrt(E*G - F*F);
|
||||
const double w_detJ = w/detJ;
|
||||
Q(0,iqy,iqx) = w_detJ * (RT ? E : G); // 1,1
|
||||
Q(1,iqy,iqx) = w_detJ * (RT ? F : -F); // 1,2
|
||||
Q(2,iqy,iqx) = w_detJ * (RT ? G : E); // 2,2
|
||||
Q(3,iqy,iqx) = (ND || RT) ? w_detJ : w*detJ;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline void Jacobian3D(
|
||||
@@ -180,18 +264,6 @@ MFEM_HOST_DEVICE inline void Adjugate3D(const DeviceMatrix &J, DeviceMatrix &A)
|
||||
A(2,2) = (J(0,0) * J(1,1)) - (J(0,1) * J(1,0));
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline double Det2D(DeviceMatrix &J)
|
||||
{
|
||||
return J(0,0)*J(1,1) - J(1,0)*J(0,1);
|
||||
}
|
||||
|
||||
MFEM_HOST_DEVICE inline double Det3D(DeviceMatrix &J)
|
||||
{
|
||||
return J(0,0) * (J(1,1) * J(2,2) - J(2,1) * J(1,2)) -
|
||||
J(1,0) * (J(0,1) * J(2,2) - J(2,1) * J(0,2)) +
|
||||
J(2,0) * (J(0,1) * J(1,2) - J(1,1) * J(0,2));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+169
-11
@@ -97,12 +97,37 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
double energy = 0.0;
|
||||
|
||||
if (dnfi.Size())
|
||||
{
|
||||
// Which attributes need to be processed?
|
||||
Array<int> attr_marker(mesh->attributes.Size() ?
|
||||
mesh->attributes.Max() : 0);
|
||||
attr_marker = 0;
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] == NULL)
|
||||
{
|
||||
attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &marker = *dnfi_marker[k];
|
||||
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
|
||||
"invalid marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
for (int i = 0; i < attr_marker.Size(); i++)
|
||||
{
|
||||
attr_marker[i] |= marker[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetFE(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
T = fes->GetElementTransformation(i);
|
||||
@@ -110,6 +135,9 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
energy += dnfi[k]->GetElementEnergy(*fe, *T, el_x);
|
||||
}
|
||||
}
|
||||
@@ -175,8 +203,32 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
|
||||
if (dnfi.Size())
|
||||
{
|
||||
// Which attributes need to be processed?
|
||||
Array<int> attr_marker(mesh->attributes.Size() ?
|
||||
mesh->attributes.Max() : 0);
|
||||
attr_marker = 0;
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] == NULL)
|
||||
{
|
||||
attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &marker = *dnfi_marker[k];
|
||||
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
|
||||
"invalid marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
for (int i = 0; i < attr_marker.Size(); i++)
|
||||
{
|
||||
attr_marker[i] |= marker[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetFE(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
T = fes->GetElementTransformation(i);
|
||||
@@ -184,6 +236,9 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
dnfi[k]->AssembleElementVector(*fe, *T, el_x, el_y);
|
||||
if (doftrans) {doftrans->TransformDual(el_y); }
|
||||
py.AddElementVector(vdofs, el_y);
|
||||
@@ -322,8 +377,32 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
|
||||
if (dnfi.Size())
|
||||
{
|
||||
// Which attributes need to be processed?
|
||||
Array<int> attr_marker(mesh->attributes.Size() ?
|
||||
mesh->attributes.Max() : 0);
|
||||
attr_marker = 0;
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] == NULL)
|
||||
{
|
||||
attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &marker = *dnfi_marker[k];
|
||||
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
|
||||
"invalid marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
for (int i = 0; i < attr_marker.Size(); i++)
|
||||
{
|
||||
attr_marker[i] |= marker[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
fe = fes->GetFE(i);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
T = fes->GetElementTransformation(i);
|
||||
@@ -331,6 +410,9 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
|
||||
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
dnfi[k]->AssembleElementGrad(*fe, *T, el_x, elmat);
|
||||
if (doftrans) { doftrans->TransformDual(elmat); }
|
||||
Grad->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
|
||||
@@ -561,13 +643,6 @@ BlockNonlinearForm::BlockNonlinearForm(Array<FiniteElementSpace *> &f) :
|
||||
SetSpaces(f);
|
||||
}
|
||||
|
||||
void BlockNonlinearForm::AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nfi,
|
||||
Array<int> &bdr_attr_marker)
|
||||
{
|
||||
bfnfi.Append(nfi);
|
||||
bfnfi_marker.Append(&bdr_attr_marker);
|
||||
}
|
||||
|
||||
void BlockNonlinearForm::SetEssentialBC(
|
||||
const Array<Array<int> *> &bdr_attr_is_ess, Array<Vector *> &rhs)
|
||||
{
|
||||
@@ -592,6 +667,7 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
Array<const FiniteElement *> fe(fes.Size());
|
||||
ElementTransformation *T;
|
||||
DofTransformation *doftrans;
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
double energy = 0.0;
|
||||
|
||||
for (int i=0; i<fes.Size(); ++i)
|
||||
@@ -601,8 +677,33 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
}
|
||||
|
||||
if (dnfi.Size())
|
||||
{
|
||||
// Which attributes need to be processed?
|
||||
Array<int> attr_marker(mesh->attributes.Size() ?
|
||||
mesh->attributes.Max() : 0);
|
||||
attr_marker = 0;
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] == NULL)
|
||||
{
|
||||
attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &marker = *dnfi_marker[k];
|
||||
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
|
||||
"invalid marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
for (int i = 0; i < attr_marker.Size(); i++)
|
||||
{
|
||||
attr_marker[i] |= marker[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fes[0]->GetNE(); ++i)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
T = fes[0]->GetElementTransformation(i);
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
@@ -614,9 +715,13 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
energy += dnfi[k]->GetElementEnergy(fe, *T, el_x_const);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// free the allocated memory
|
||||
for (int i = 0; i < fes.Size(); ++i)
|
||||
@@ -656,6 +761,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
Array<const FiniteElement *> fe2(fes.Size());
|
||||
ElementTransformation *T;
|
||||
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
|
||||
by.UseDevice(true);
|
||||
by = 0.0;
|
||||
@@ -670,8 +776,32 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
|
||||
if (dnfi.Size())
|
||||
{
|
||||
// Which attributes need to be processed?
|
||||
Array<int> attr_marker(mesh->attributes.Size() ?
|
||||
mesh->attributes.Max() : 0);
|
||||
attr_marker = 0;
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] == NULL)
|
||||
{
|
||||
attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &marker = *dnfi_marker[k];
|
||||
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
|
||||
"invalid marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
for (int i = 0; i < attr_marker.Size(); i++)
|
||||
{
|
||||
attr_marker[i] |= marker[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fes[0]->GetNE(); ++i)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
T = fes[0]->GetElementTransformation(i);
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
@@ -683,6 +813,9 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
dnfi[k]->AssembleElementVector(fe, *T,
|
||||
el_x_const, el_y);
|
||||
|
||||
@@ -698,7 +831,6 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
|
||||
if (fnfi.Size())
|
||||
{
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
FaceElementTransformations *tr;
|
||||
|
||||
for (int i = 0; i < mesh->GetNumFaces(); ++i)
|
||||
@@ -736,8 +868,8 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
|
||||
|
||||
if (bfnfi.Size())
|
||||
{
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
FaceElementTransformations *tr;
|
||||
|
||||
// Which boundary attributes need to be processed?
|
||||
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
|
||||
mesh->bdr_attributes.Max() : 0);
|
||||
@@ -858,6 +990,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
Array<const FiniteElement *>fe2(fes.Size());
|
||||
ElementTransformation * T;
|
||||
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
|
||||
for (int i=0; i<fes.Size(); ++i)
|
||||
{
|
||||
@@ -888,8 +1021,32 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
|
||||
if (dnfi.Size())
|
||||
{
|
||||
// Which attributes need to be processed?
|
||||
Array<int> attr_marker(mesh->attributes.Size() ?
|
||||
mesh->attributes.Max() : 0);
|
||||
attr_marker = 0;
|
||||
for (int k = 0; k < dnfi.Size(); k++)
|
||||
{
|
||||
if (dnfi_marker[k] == NULL)
|
||||
{
|
||||
attr_marker = 1;
|
||||
break;
|
||||
}
|
||||
Array<int> &marker = *dnfi_marker[k];
|
||||
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
|
||||
"invalid marker for domain integrator #"
|
||||
<< k << ", counting from zero");
|
||||
for (int i = 0; i < attr_marker.Size(); i++)
|
||||
{
|
||||
attr_marker[i] |= marker[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < fes[0]->GetNE(); ++i)
|
||||
{
|
||||
const int attr = mesh->GetAttribute(i);
|
||||
if (attr_marker[attr-1] == 0) { continue; }
|
||||
|
||||
T = fes[0]->GetElementTransformation(i);
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
@@ -901,6 +1058,9 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
|
||||
for (int k = 0; k < dnfi.Size(); ++k)
|
||||
{
|
||||
if (dnfi_marker[k] &&
|
||||
(*dnfi_marker[k])[attr-1] == 0) { continue; }
|
||||
|
||||
dnfi[k]->AssembleElementGrad(fe, *T, el_x_const, elmats);
|
||||
|
||||
for (int j=0; j<fes.Size(); ++j)
|
||||
@@ -923,7 +1083,6 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
if (fnfi.Size())
|
||||
{
|
||||
FaceElementTransformations *tr;
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
|
||||
for (int i = 0; i < mesh->GetNumFaces(); ++i)
|
||||
{
|
||||
@@ -960,7 +1119,6 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
|
||||
if (bfnfi.Size())
|
||||
{
|
||||
FaceElementTransformations *tr;
|
||||
Mesh *mesh = fes[0]->GetMesh();
|
||||
|
||||
// Which boundary attributes need to be processed?
|
||||
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
|
||||
|
||||
+17
-4
@@ -37,6 +37,7 @@ protected:
|
||||
|
||||
/// Set of Domain Integrators to be assembled (added).
|
||||
Array<NonlinearFormIntegrator*> dnfi; // owned
|
||||
Array<Array<int>*> dnfi_marker; // not owned
|
||||
|
||||
/// Set of interior face Integrators to be assembled (added).
|
||||
Array<NonlinearFormIntegrator*> fnfi; // owned
|
||||
@@ -108,7 +109,12 @@ public:
|
||||
|
||||
/// Adds new Domain Integrator.
|
||||
void AddDomainIntegrator(NonlinearFormIntegrator *nlfi)
|
||||
{ dnfi.Append(nlfi); }
|
||||
{ dnfi.Append(nlfi); dnfi_marker.Append(NULL); }
|
||||
|
||||
/// Adds new Domain Integrator, restricted to specific attributes.
|
||||
void AddDomainIntegrator(NonlinearFormIntegrator *nlfi,
|
||||
Array<int> &elem_marker)
|
||||
{ dnfi.Append(nlfi); dnfi_marker.Append(&elem_marker); }
|
||||
|
||||
/// Access all integrators added with AddDomainIntegrator().
|
||||
Array<NonlinearFormIntegrator*> *GetDNFI() { return &dnfi; }
|
||||
@@ -227,13 +233,14 @@ protected:
|
||||
|
||||
/// Set of Domain Integrators to be assembled (added).
|
||||
Array<BlockNonlinearFormIntegrator*> dnfi;
|
||||
Array<Array<int>*> dnfi_marker;
|
||||
|
||||
/// Set of interior face Integrators to be assembled (added).
|
||||
Array<BlockNonlinearFormIntegrator*> fnfi;
|
||||
|
||||
/// Set of Boundary Face Integrators to be assembled (added).
|
||||
Array<BlockNonlinearFormIntegrator*> bfnfi;
|
||||
Array<Array<int>*> bfnfi_marker;
|
||||
Array<Array<int>*> bfnfi_marker;
|
||||
|
||||
/** Auxiliary block-vectors for wrapping input and output vectors or holding
|
||||
GridFunction-like block-vector data (e.g. in parallel). */
|
||||
@@ -298,7 +305,12 @@ public:
|
||||
|
||||
/// Adds new Domain Integrator.
|
||||
void AddDomainIntegrator(BlockNonlinearFormIntegrator *nlfi)
|
||||
{ dnfi.Append(nlfi); }
|
||||
{ dnfi.Append(nlfi); dnfi_marker.Append(NULL); }
|
||||
|
||||
/// Adds new Domain Integrator, restricted to specific attributes.
|
||||
void AddDomainIntegrator(BlockNonlinearFormIntegrator *nlfi,
|
||||
Array<int> &elem_marker)
|
||||
{ dnfi.Append(nlfi); dnfi_marker.Append(&elem_marker); }
|
||||
|
||||
/// Adds new Interior Face Integrator.
|
||||
void AddInteriorFaceIntegrator(BlockNonlinearFormIntegrator *nlfi)
|
||||
@@ -311,7 +323,8 @@ public:
|
||||
/** @brief Adds new Boundary Face Integrator, restricted to specific boundary
|
||||
attributes. */
|
||||
void AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nlfi,
|
||||
Array<int> &bdr_marker);
|
||||
Array<int> &bdr_marker)
|
||||
{ bfnfi.Append(nlfi); bfnfi_marker.Append(&bdr_marker); }
|
||||
|
||||
virtual void SetEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
|
||||
Array<Vector *> &rhs);
|
||||
|
||||
+109
-50
@@ -466,53 +466,54 @@ void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof) const
|
||||
ApplyLDofSigns(all_dofs);
|
||||
}
|
||||
|
||||
DofTransformation *
|
||||
ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs) const
|
||||
void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
if (elem_dof)
|
||||
{
|
||||
elem_dof->GetRow(i, dofs);
|
||||
|
||||
if (DoFTrans[mesh->GetElementBaseGeometry(i)])
|
||||
if (DoFTransArray[mesh->GetElementBaseGeometry(i)])
|
||||
{
|
||||
Array<int> Fo;
|
||||
elem_fos->GetRow(i, Fo);
|
||||
DoFTrans[mesh->GetElementBaseGeometry(i)]->SetFaceOrientations(Fo);
|
||||
return DoFTrans[mesh->GetElementBaseGeometry(i)];
|
||||
doftrans.SetDofTransformation(
|
||||
*DoFTransArray[mesh->GetElementBaseGeometry(i)]);
|
||||
doftrans.SetFaceOrientations(Fo);
|
||||
doftrans.SetVDim();
|
||||
}
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
DofTransformation * doftrans = FiniteElementSpace::GetElementDofs(i, dofs);
|
||||
FiniteElementSpace::GetElementDofs(i, dofs, doftrans);
|
||||
if (Conforming())
|
||||
{
|
||||
ApplyLDofSigns(dofs);
|
||||
}
|
||||
return doftrans;
|
||||
}
|
||||
|
||||
DofTransformation *
|
||||
ParFiniteElementSpace::GetBdrElementDofs(int i, Array<int> &dofs) const
|
||||
void ParFiniteElementSpace::GetBdrElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
if (bdr_elem_dof)
|
||||
{
|
||||
bdr_elem_dof->GetRow(i, dofs);
|
||||
|
||||
if (DoFTrans[mesh->GetBdrElementBaseGeometry(i)])
|
||||
if (DoFTransArray[mesh->GetBdrElementBaseGeometry(i)])
|
||||
{
|
||||
Array<int> Fo;
|
||||
bdr_elem_fos -> GetRow (i, Fo);
|
||||
DoFTrans[mesh->GetBdrElementBaseGeometry(i)]->SetFaceOrientations(Fo);
|
||||
return DoFTrans[mesh->GetBdrElementBaseGeometry(i)];
|
||||
bdr_elem_fos->GetRow(i, Fo);
|
||||
doftrans.SetDofTransformation(
|
||||
*DoFTransArray[mesh->GetBdrElementBaseGeometry(i)]);
|
||||
doftrans.SetFaceOrientations(Fo);
|
||||
doftrans.SetVDim();
|
||||
}
|
||||
return NULL;
|
||||
return;
|
||||
}
|
||||
DofTransformation * doftrans =
|
||||
FiniteElementSpace::GetBdrElementDofs(i, dofs);
|
||||
FiniteElementSpace::GetBdrElementDofs(i, dofs, doftrans);
|
||||
if (Conforming())
|
||||
{
|
||||
ApplyLDofSigns(dofs);
|
||||
}
|
||||
return doftrans;
|
||||
}
|
||||
|
||||
int ParFiniteElementSpace::GetFaceDofs(int i, Array<int> &dofs,
|
||||
@@ -939,8 +940,8 @@ void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const // matrix P
|
||||
}
|
||||
else if (i_offd[i+1] == i_offd[i] + 2)
|
||||
{
|
||||
const double * T = ND_StatelessDofTransformation
|
||||
::GetFaceTransform(ltori[i]).GetData();
|
||||
const double *T =
|
||||
ND_DofTransformation::GetFaceTransform(ltori[i]).GetData();
|
||||
j_offd[i_offd[i] + 1] = j_offd[i_offd[i]] + 1;
|
||||
d_offd[i_offd[i]] = T[0]; d_offd[i_offd[i] + 1] = T[2];
|
||||
i++;
|
||||
@@ -1454,31 +1455,30 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
delete [] requests;
|
||||
}
|
||||
|
||||
DofTransformation *ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
int i, Array<int> &vdofs) const
|
||||
void ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
int i, Array<int> &vdofs, DofTransformation &doftrans) const
|
||||
{
|
||||
face_nbr_element_dof.GetRow(i, vdofs);
|
||||
|
||||
DofTransformation *doftrans = NULL;
|
||||
Geometry::Type geom = GetFaceNbrFE(i)->GetGeomType();
|
||||
if (DoFTrans[geom])
|
||||
if (DoFTransArray[GetFaceNbrFE(i)->GetGeomType()])
|
||||
{
|
||||
Array<int> F, Fo;
|
||||
pmesh->GetFaceNbrElementFaces(pmesh->GetNE() + i, F, Fo);
|
||||
doftrans = DoFTrans[geom];
|
||||
doftrans->SetFaceOrientations(Fo);
|
||||
}
|
||||
if (vdim == 1 || doftrans == NULL)
|
||||
{
|
||||
return doftrans;
|
||||
}
|
||||
else
|
||||
{
|
||||
VDoFTrans.SetDofTransformation(*doftrans);
|
||||
return &VDoFTrans;
|
||||
doftrans.SetDofTransformation(
|
||||
*DoFTransArray[GetFaceNbrFE(i)->GetGeomType()]);
|
||||
doftrans.SetFaceOrientations(Fo);
|
||||
doftrans.SetVDim(vdim, ordering);
|
||||
}
|
||||
}
|
||||
|
||||
DofTransformation *ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
int i, Array<int> &vdofs) const
|
||||
{
|
||||
DoFTrans.SetDofTransformation(NULL);
|
||||
GetFaceNbrElementVDofs(i, vdofs, DoFTrans);
|
||||
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
// Works for NC mesh where 'i' is an index returned by
|
||||
@@ -1946,9 +1946,8 @@ struct PMatrixRow
|
||||
void AddRow(const PMatrixRow &other, double coef)
|
||||
{
|
||||
elems.reserve(elems.size() + other.elems.size());
|
||||
for (unsigned i = 0; i < other.elems.size(); i++)
|
||||
for (const PMatrixElement &oei : other.elems)
|
||||
{
|
||||
const PMatrixElement &oei = other.elems[i];
|
||||
elems.push_back(
|
||||
PMatrixElement(oei.column, oei.stride, coef * oei.value));
|
||||
}
|
||||
@@ -2058,7 +2057,7 @@ void NeighborRowMessage::Encode(int rank)
|
||||
for (unsigned i = 0; i < rows.size(); i++)
|
||||
{
|
||||
const RowInfo &ri = rows[i];
|
||||
const MeshId &id = pncmesh->GetNCList(ri.entity).LookUp(ri.index);
|
||||
const MeshId &id = *pncmesh->GetNCList(ri.entity).GetMeshIdAndType(ri.index).id;
|
||||
ent_ids[ri.entity].Append(id);
|
||||
row_idx[ri.entity].Append(i);
|
||||
group_ids[ri.entity].Append(ri.group);
|
||||
@@ -2132,25 +2131,33 @@ void NeighborRowMessage::Decode(int rank)
|
||||
rows.clear();
|
||||
rows.reserve(nrows);
|
||||
|
||||
// read rows
|
||||
// read rows ent = {0,1,2} means vertex, edge and face entity
|
||||
for (int ent = 0, gi = 0; ent < 3; ent++)
|
||||
{
|
||||
// extract the vertex list, edge list or face list.
|
||||
const Array<MeshId> &ids = ent_ids[ent];
|
||||
for (int i = 0; i < ids.Size(); i++)
|
||||
{
|
||||
const MeshId &id = ids[i];
|
||||
// read the particular element dof value off the stream.
|
||||
int edof = bin_io::read<int>(stream);
|
||||
|
||||
// handle orientation and sign change
|
||||
const int *ind = NULL;
|
||||
// Handle orientation and sign change. This flips the sign on dofs
|
||||
// where necessary, and for edges and faces also reorders if flipped,
|
||||
// i.e. an edge with 1 -> 2 -> 3 -> 4 might become -4 -> -3 -> -2 -> -1
|
||||
// This cannot treat all face dofs, as they can have rotations and
|
||||
// reflections.
|
||||
const int *ind = nullptr;
|
||||
Geometry::Type geom = Geometry::Type::INVALID;
|
||||
if (ent == 1)
|
||||
{
|
||||
// edge NC orientation is element defined.
|
||||
int eo = pncmesh->GetEdgeNCOrientation(id);
|
||||
ind = fec->DofOrderForOrientation(Geometry::SEGMENT, eo);
|
||||
}
|
||||
else if (ent == 2)
|
||||
{
|
||||
Geometry::Type geom = pncmesh->GetFaceGeometry(id.index);
|
||||
geom = pncmesh->GetFaceGeometry(id.index);
|
||||
int fo = pncmesh->GetFaceOrientation(id.index);
|
||||
ind = fec->DofOrderForOrientation(geom, fo);
|
||||
}
|
||||
@@ -2165,13 +2172,14 @@ void NeighborRowMessage::Decode(int rank)
|
||||
// If edof arrived with a negative index, flip it, and the scaling.
|
||||
double s = (edof < 0) ? -1.0 : 1.0;
|
||||
edof = (edof < 0) ? -1 - edof : edof;
|
||||
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = -1 - edof;
|
||||
s *= -1.0;
|
||||
}
|
||||
|
||||
// Create a row for this entity, recording the index of the mesh
|
||||
// element
|
||||
rows.push_back(RowInfo(ent, id.index, edof, group_ids[gi++]));
|
||||
rows.back().row.read(stream, s);
|
||||
|
||||
@@ -2181,6 +2189,63 @@ void NeighborRowMessage::Decode(int rank)
|
||||
<< rows.back().index << ", edof " << rows.back().edof
|
||||
<< std::endl;
|
||||
#endif
|
||||
|
||||
if (ent == 2 && fec->GetContType() == FiniteElementCollection::TANGENTIAL
|
||||
&& !Geometry::IsTensorProduct(geom))
|
||||
{
|
||||
// ND face dofs need to be processed together, as the transformation
|
||||
// is given by a 2x2 matrix, so we manually apply an extra increment
|
||||
// to the loop counter and add in a new row. Once these rows are
|
||||
// placed, they represent the Identity transformation. To map across
|
||||
// the processor boundary, we also need to apply a Primal
|
||||
// Transformation (see doftrans.hpp) to a notional "global dof"
|
||||
// orientation. For simplicity we perform the action of these 2x2
|
||||
// matrices manually using the AddRow capability, followed by a
|
||||
// Collapse.
|
||||
|
||||
// To perform the operations, we add and subtract initial versions
|
||||
// of the rows, that represent [1 0; 0 1] in row major notation. The
|
||||
// first row represents the 1 at (0,0) in [1 0; 0 1] The second row
|
||||
// represents the 1 at (1,1) in [1 0; 0 1]
|
||||
|
||||
// We can safely bind this reference as rows was reserved above so
|
||||
// there is no hidden copying that could result in a dangling
|
||||
// reference.
|
||||
auto &first_row = rows.back().row;
|
||||
// This is the first "fundamental unit" used in the transformation.
|
||||
const auto initial_first_row = first_row;
|
||||
// Extract the next dof too, and apply any dof order transformation
|
||||
// expected.
|
||||
const MeshId &next_id = ids[++i];
|
||||
const int fo = pncmesh->GetFaceOrientation(next_id.index);
|
||||
ind = fec->DofOrderForOrientation(geom, fo);
|
||||
edof = bin_io::read<int>(stream);
|
||||
|
||||
// If edof arrived with a negative index, flip it, and the scaling.
|
||||
s = (edof < 0) ? -1.0 : 1.0;
|
||||
edof = (edof < 0) ? -1 - edof : edof;
|
||||
if (ind && (edof = ind[edof]) < 0)
|
||||
{
|
||||
edof = -1 - edof;
|
||||
s *= -1.0;
|
||||
}
|
||||
rows.push_back(RowInfo(ent, next_id.index, edof, group_ids[gi++]));
|
||||
rows.back().row.read(stream, s);
|
||||
auto &second_row = rows.back().row;
|
||||
|
||||
// This is the second "fundamental unit" used in the transformation.
|
||||
const auto initial_second_row = second_row;
|
||||
const double *T =
|
||||
ND_DofTransformation::GetFaceTransform(fo).GetData();
|
||||
|
||||
first_row.AddRow(initial_first_row, T[0] - 1.0);
|
||||
first_row.AddRow(initial_second_row, T[2]);
|
||||
second_row.AddRow(initial_first_row, T[1]);
|
||||
second_row.AddRow(initial_second_row, T[3] - 1.0);
|
||||
|
||||
first_row.Collapse();
|
||||
second_row.Collapse();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2308,12 +2373,6 @@ int ParFiniteElementSpace
|
||||
Array<int> *dof_tdof,
|
||||
bool partial) const
|
||||
{
|
||||
// TODO: general face DOF transformations in NeighborRowMessage::Decode()
|
||||
MFEM_VERIFY(!(fec->GetOrder() >= 2
|
||||
&& pmesh->HasGeometry(Geometry::TETRAHEDRON)
|
||||
&& fec->GetContType() == FiniteElementCollection::TANGENTIAL),
|
||||
"Nedelec NC tets of order >= 2 are not supported yet.");
|
||||
|
||||
const bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
|
||||
|
||||
#ifdef MFEM_PMATRIX_STATS
|
||||
|
||||
+37
-29
@@ -190,15 +190,15 @@ private:
|
||||
/// Updates the internal mesh pointer. @warning @a new_mesh must be
|
||||
/// <b>topologically identical</b> to the existing mesh. Used if the address
|
||||
/// of the Mesh object has changed, e.g. in @a Mesh::Swap.
|
||||
virtual void UpdateMeshPointer(Mesh *new_mesh);
|
||||
void UpdateMeshPointer(Mesh *new_mesh) override;
|
||||
|
||||
/// Copies the prolongation and restriction matrices from @a fes.
|
||||
///
|
||||
/// Used for low order preconditioning on non-conforming meshes. If the DOFs
|
||||
/// require a permutation, it will be supplied by non-NULL @a perm. NULL @a
|
||||
/// perm indicates that no permutation is required.
|
||||
virtual void CopyProlongationAndRestriction(const FiniteElementSpace &fes,
|
||||
const Array<int> *perm);
|
||||
void CopyProlongationAndRestriction(const FiniteElementSpace &fes,
|
||||
const Array<int> *perm) override;
|
||||
|
||||
public:
|
||||
// Face-neighbor data
|
||||
@@ -282,32 +282,38 @@ public:
|
||||
{ return Dof_TrueDof_Matrix()->GetGlobalNumCols(); }
|
||||
|
||||
/// Return the number of local vector true dofs.
|
||||
virtual int GetTrueVSize() const { return ltdof_size; }
|
||||
int GetTrueVSize() const override { return ltdof_size; }
|
||||
|
||||
/// Returns indexes of degrees of freedom in array dofs for i'th element.
|
||||
virtual DofTransformation *GetElementDofs(int i, Array<int> &dofs) const;
|
||||
/// Returns indexes of degrees of freedom in array dofs for i'th element and
|
||||
/// returns the DofTransformation data in a user-provided object.
|
||||
using FiniteElementSpace::GetElementDofs;
|
||||
void GetElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const override;
|
||||
|
||||
/// Returns indexes of degrees of freedom for i'th boundary element.
|
||||
virtual DofTransformation *GetBdrElementDofs(int i, Array<int> &dofs) const;
|
||||
/// Returns indexes of degrees of freedom for i'th boundary element and
|
||||
/// returns the DofTransformation data in a user-provided object.
|
||||
using FiniteElementSpace::GetBdrElementDofs;
|
||||
void GetBdrElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const override;
|
||||
|
||||
/** Returns the indexes of the degrees of freedom for i'th face
|
||||
including the dofs for the edges and the vertices of the face. */
|
||||
virtual int GetFaceDofs(int i, Array<int> &dofs, int variant = 0) const;
|
||||
int GetFaceDofs(int i, Array<int> &dofs, int variant = 0) const override;
|
||||
|
||||
/** Returns pointer to the FiniteElement in the FiniteElementCollection
|
||||
associated with i'th element in the mesh object. If @a i is greater than
|
||||
or equal to the number of local mesh elements, @a i will be interpreted
|
||||
as a shifted index of a face neighbor element. */
|
||||
virtual const FiniteElement *GetFE(int i) const;
|
||||
const FiniteElement *GetFE(int i) const override;
|
||||
|
||||
/** Returns an Operator that converts L-vectors to E-vectors on each face.
|
||||
The parallel version is different from the serial one because of the
|
||||
presence of shared faces. Shared faces are treated as interior faces,
|
||||
the returned operator handles the communication needed to get the
|
||||
shared face values from other MPI ranks */
|
||||
virtual const FaceRestriction *GetFaceRestriction(
|
||||
const FaceRestriction *GetFaceRestriction(
|
||||
ElementDofOrdering f_ordering, FaceType type,
|
||||
L2FaceValues mul = L2FaceValues::DoubleValued) const;
|
||||
L2FaceValues mul = L2FaceValues::DoubleValued) const override;
|
||||
|
||||
void GetSharedEdgeDofs(int group, int ei, Array<int> &dofs) const;
|
||||
void GetSharedTriangleDofs(int group, int fi, Array<int> &dofs) const;
|
||||
@@ -347,15 +353,15 @@ public:
|
||||
void Synchronize(Array<int> &ldof_marker) const;
|
||||
|
||||
/// Determine the boundary degrees of freedom
|
||||
virtual void GetEssentialVDofs(const Array<int> &bdr_attr_is_ess,
|
||||
Array<int> &ess_dofs,
|
||||
int component = -1) const;
|
||||
void GetEssentialVDofs(const Array<int> &bdr_attr_is_ess,
|
||||
Array<int> &ess_dofs,
|
||||
int component = -1) const override;
|
||||
|
||||
/** Get a list of essential true dofs, ess_tdof_list, corresponding to the
|
||||
boundary attributes marked in the array bdr_attr_is_ess. */
|
||||
virtual void GetEssentialTrueDofs(const Array<int> &bdr_attr_is_ess,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component = -1);
|
||||
void GetEssentialTrueDofs(const Array<int> &bdr_attr_is_ess,
|
||||
Array<int> &ess_tdof_list,
|
||||
int component = -1) override;
|
||||
|
||||
/** If the given ldof is owned by the current processor, return its local
|
||||
tdof number, otherwise return -1 */
|
||||
@@ -370,18 +376,20 @@ public:
|
||||
HYPRE_BigInt GetMyDofOffset() const;
|
||||
HYPRE_BigInt GetMyTDofOffset() const;
|
||||
|
||||
virtual const Operator *GetProlongationMatrix() const;
|
||||
const Operator *GetProlongationMatrix() const override;
|
||||
/** Get an Operator that performs the action of GetRestrictionMatrix(),
|
||||
but potentially with a non-assembled optimized matrix-free
|
||||
implementation. */
|
||||
virtual const Operator *GetRestrictionOperator() const;
|
||||
const Operator *GetRestrictionOperator() const override;
|
||||
/// Get the R matrix which restricts a local dof vector to true dof vector.
|
||||
virtual const SparseMatrix *GetRestrictionMatrix() const
|
||||
const SparseMatrix *GetRestrictionMatrix() const override
|
||||
{ Dof_TrueDof_Matrix(); return R; }
|
||||
|
||||
// Face-neighbor functions
|
||||
void ExchangeFaceNbrData();
|
||||
int GetFaceNbrVSize() const { return num_face_nbr_dofs; }
|
||||
void GetFaceNbrElementVDofs(int i, Array<int> &vdofs,
|
||||
DofTransformation &doftrans) const;
|
||||
DofTransformation *GetFaceNbrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
void GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const;
|
||||
const FiniteElement *GetFaceNbrFE(int i) const;
|
||||
@@ -402,15 +410,15 @@ public:
|
||||
// Transfer parallel true-dof data from coarse_fes, defined on a coarse mesh,
|
||||
// to this FE space, defined on a refined mesh. See full documentation in the
|
||||
// base class, FiniteElementSpace::GetTrueTransferOperator.
|
||||
virtual void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes,
|
||||
OperatorHandle &T) const;
|
||||
void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes,
|
||||
OperatorHandle &T) const override;
|
||||
|
||||
/** Reflect changes in the mesh. Calculate one of the refinement/derefinement
|
||||
/rebalance matrices, unless want_transform is false. */
|
||||
virtual void Update(bool want_transform = true);
|
||||
void Update(bool want_transform = true) override;
|
||||
|
||||
/// Free ParGridFunction transformation matrix (if any), to save memory.
|
||||
virtual void UpdatesFinished()
|
||||
void UpdatesFinished() override
|
||||
{
|
||||
FiniteElementSpace::UpdatesFinished();
|
||||
old_dof_offsets.DeleteAll();
|
||||
@@ -442,9 +450,9 @@ public:
|
||||
|
||||
const GroupCommunicator &GetGroupCommunicator() const;
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
virtual void MultTranspose(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
};
|
||||
|
||||
/// Auxiliary device class used by ParFiniteElementSpace.
|
||||
@@ -493,9 +501,9 @@ public:
|
||||
|
||||
virtual ~DeviceConformingProlongationOperator();
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
virtual void MultTranspose(const Vector &x, Vector &y) const;
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+21
-6
@@ -469,17 +469,16 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
|
||||
}
|
||||
|
||||
Array<int> vdofs;
|
||||
DofTransformation * doftrans = pfes->GetFaceNbrElementVDofs(nbr_el_no,
|
||||
vdofs);
|
||||
const FiniteElement *fe = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
|
||||
int dof = fe->GetDof();
|
||||
DofTransformation * doftrans = pfes->GetFaceNbrElementVDofs(nbr_el_no, vdofs);
|
||||
Vector loc_data;
|
||||
face_nbr_data.GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
|
||||
const FiniteElement *fe = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
const int dof = fe->GetDof();
|
||||
if (fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
@@ -694,7 +693,23 @@ void ParGridFunction::ProjectBdrCoefficient(
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
Array<int> ess_vdofs_marker;
|
||||
pfes->GetEssentialVDofs(attr, ess_vdofs_marker);
|
||||
if (vcoeff) { pfes->GetEssentialVDofs(attr, ess_vdofs_marker); }
|
||||
else
|
||||
{
|
||||
ess_vdofs_marker.SetSize(Size());
|
||||
ess_vdofs_marker = 0;
|
||||
for (int i = 0; i < fes->GetVDim(); i++)
|
||||
{
|
||||
if (!coeff[i]) { continue; }
|
||||
Array<int> component_dof_marker;
|
||||
pfes->GetEssentialVDofs(attr, component_dof_marker,i);
|
||||
for (int j = 0; j<Size(); j++)
|
||||
{
|
||||
ess_vdofs_marker[j] = bool(ess_vdofs_marker[j]) ||
|
||||
bool(component_dof_marker[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < values_counter.Size(); i++)
|
||||
{
|
||||
MFEM_ASSERT(pfes->GetLocalTDofNumber(i) == -1 ||
|
||||
|
||||
+1
-1
@@ -212,7 +212,7 @@ public:
|
||||
virtual double GetValue(int i, const IntegrationPoint &ip,
|
||||
int vdim = 1) const;
|
||||
double GetValue(ElementTransformation &T)
|
||||
{ return GetValue(T.ElementNo, T.GetIntPoint()); }
|
||||
{ return GetValue(T, T.GetIntPoint()); }
|
||||
|
||||
// Redefine to handle the case when T describes a face-neighbor element
|
||||
virtual double GetValue(ElementTransformation &T, const IntegrationPoint &ip,
|
||||
|
||||
+9
-2
@@ -164,13 +164,20 @@ int FaceQuadratureSpace::GetPermutedIndex(int idx, int iq) const
|
||||
|
||||
int FaceQuadratureSpace::GetEntityIndex(const ElementTransformation &T) const
|
||||
{
|
||||
auto get_face_index = [this](const int idx)
|
||||
{
|
||||
const auto it = face_indices_inv.find(idx);
|
||||
if (it == face_indices_inv.end()) { return -1; }
|
||||
else { return it->second; }
|
||||
};
|
||||
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::FACE:
|
||||
return face_indices_inv.at(T.ElementNo);
|
||||
return get_face_index(T.ElementNo);
|
||||
case ElementTransformation::BDR_ELEMENT:
|
||||
case ElementTransformation::BDR_FACE:
|
||||
return face_indices_inv.at(mesh.GetBdrElementEdgeIndex(T.ElementNo));
|
||||
return get_face_index(mesh.GetBdrElementFaceIndex(T.ElementNo));
|
||||
default:
|
||||
MFEM_ABORT("Invalid element type.");
|
||||
return -1;
|
||||
|
||||
+2
-1
@@ -87,7 +87,8 @@ public:
|
||||
///
|
||||
/// For a QuadratureSpace defined on elements, this just returns the element
|
||||
/// index. For FaceQuadratureSpace, the returned index depends on the chosen
|
||||
/// FaceType.
|
||||
/// FaceType. If the entity is not found (for example, if @a T represents an
|
||||
/// interior face, and the space has FaceType::Boundary) then -1 is returned.
|
||||
virtual int GetEntityIndex(const ElementTransformation &T) const = 0;
|
||||
|
||||
/// Write the QuadratureSpace to the stream @a out.
|
||||
|
||||
+41
-10
@@ -462,21 +462,52 @@ void TMOP_Metric_009::AssembleH(const DenseMatrix &Jpt,
|
||||
ie.Assemble_ddI1b(weight, A.GetData());
|
||||
}
|
||||
|
||||
// mu_14 = |T-I|^2
|
||||
double TMOP_Metric_014::EvalWMatrixForm(const DenseMatrix &Jpt) const
|
||||
{
|
||||
// mu_14 = |J - I|^2.
|
||||
DenseMatrix Mat(Jpt);
|
||||
Mat(0,0) -= 1.0;
|
||||
Mat(1,1) -= 1.0;
|
||||
return Mat.FNorm2();
|
||||
}
|
||||
|
||||
double TMOP_Metric_014::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
MFEM_VERIFY(Jtr != NULL,
|
||||
"Requires a target Jacobian, use SetTargetJacobian().");
|
||||
// mu_14 = |J - I|^2 = I1[J-I].
|
||||
DenseMatrix Mat(Jpt);
|
||||
Mat(0,0) -= 1.0;
|
||||
Mat(1,1) -= 1.0;
|
||||
|
||||
DenseMatrix Id(2,2);
|
||||
ie.SetJacobian(Mat.GetData());
|
||||
return ie.Get_I1();
|
||||
}
|
||||
|
||||
Id(0,0) = 1; Id(0,1) = 0;
|
||||
Id(1,0) = 0; Id(1,1) = 1;
|
||||
void TMOP_Metric_014::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{
|
||||
// P = dI1[J-I] d/dJ[J-I] = dI1[J-I].
|
||||
DenseMatrix JptMinusId = Jpt;
|
||||
for (int i = 0; i < Jpt.Size(); i++)
|
||||
{
|
||||
JptMinusId(i, i) -= 1.0;
|
||||
}
|
||||
ie.SetJacobian(JptMinusId.GetData());
|
||||
P = ie.Get_dI1();
|
||||
}
|
||||
|
||||
DenseMatrix Mat(2,2);
|
||||
Mat = Jpt;
|
||||
Mat.Add(-1,Id);
|
||||
return Mat.FNorm2();
|
||||
void TMOP_Metric_014::AssembleH(const DenseMatrix &Jpt,
|
||||
const DenseMatrix &DS,
|
||||
const double weight,
|
||||
DenseMatrix &A) const
|
||||
{
|
||||
// dP = ddI1[J-I].
|
||||
DenseMatrix JptMinusId = Jpt;
|
||||
for (int i = 0; i < Jpt.Size(); i++)
|
||||
{
|
||||
JptMinusId(i, i) -= 1.0;
|
||||
}
|
||||
ie.SetJacobian(JptMinusId.GetData());
|
||||
ie.SetDerivativeMatrix(DS.Height(), DS.GetData());
|
||||
ie.Assemble_ddI1(weight, A.GetData());
|
||||
}
|
||||
|
||||
double TMOP_Metric_022::EvalW(const DenseMatrix &Jpt) const
|
||||
|
||||
+9
-5
@@ -373,16 +373,20 @@ public:
|
||||
/// 2D non-barrier Shape+Size+Orientation (VOS) metric (polyconvex).
|
||||
class TMOP_Metric_014 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator2D<double> ie;
|
||||
|
||||
public:
|
||||
// W = |T-I|^2.
|
||||
// W = |J - I|^2.
|
||||
virtual double EvalWMatrixForm(const DenseMatrix &Jpt) const;
|
||||
|
||||
// W = I1[J-I].
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// 2D Shifted barrier form of shape metric (mu_2).
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
#include "sort_pairs.hpp"
|
||||
#include "globals.hpp"
|
||||
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
#include <StrumpackConfig.hpp> // STRUMPACK_USE_PTSCOTCH, etc.
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
|
||||
@@ -34,6 +38,14 @@ using namespace std;
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
#if defined(MFEM_USE_STRUMPACK) && \
|
||||
(defined(STRUMPACK_USE_PTSCOTCH) || defined(STRUMPACK_USE_SLATE_SCALAPACK))
|
||||
int Mpi::default_thread_required = MPI_THREAD_MULTIPLE;
|
||||
#else
|
||||
int Mpi::default_thread_required = MPI_THREAD_SINGLE;
|
||||
#endif
|
||||
|
||||
|
||||
GroupTopology::GroupTopology(const GroupTopology >)
|
||||
: MyComm(gt.MyComm),
|
||||
group_lproc(gt.group_lproc)
|
||||
|
||||
+36
-14
@@ -22,7 +22,6 @@
|
||||
#include "globals.hpp"
|
||||
#include <mpi.h>
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -32,10 +31,34 @@ namespace mfem
|
||||
class Mpi
|
||||
{
|
||||
public:
|
||||
/// Singleton creation with Mpi::Init();
|
||||
static void Init() { Init_(NULL, NULL); }
|
||||
/// Singleton creation with Mpi::Init(argc,argv);
|
||||
static void Init(int &argc, char **&argv) { Init_(&argc, &argv); }
|
||||
/// Singleton creation with Mpi::Init(argc, argv).
|
||||
static void Init(int &argc, char **&argv,
|
||||
int required = default_thread_required,
|
||||
int *provided = nullptr)
|
||||
{ Init(&argc, &argv, required, provided); }
|
||||
/// Singleton creation with Mpi::Init().
|
||||
static void Init(int *argc = nullptr, char ***argv = nullptr,
|
||||
int required = default_thread_required,
|
||||
int *provided = nullptr)
|
||||
{
|
||||
MFEM_VERIFY(!IsInitialized(), "MPI already initialized!");
|
||||
if (required == MPI_THREAD_SINGLE)
|
||||
{
|
||||
int mpi_err = MPI_Init(argc, argv);
|
||||
MFEM_VERIFY(!mpi_err, "error in MPI_Init()!");
|
||||
if (provided) { *provided = MPI_THREAD_SINGLE; }
|
||||
}
|
||||
else
|
||||
{
|
||||
int mpi_provided;
|
||||
int mpi_err = MPI_Init_thread(argc, argv, required, &mpi_provided);
|
||||
MFEM_VERIFY(!mpi_err, "error in MPI_Init()!");
|
||||
if (provided) { *provided = mpi_provided; }
|
||||
}
|
||||
// The Mpi singleton object below needs to be created after MPI_Init() for
|
||||
// some MPI implementations.
|
||||
Singleton();
|
||||
}
|
||||
/// Finalize MPI (if it has been initialized and not yet already finalized).
|
||||
static void Finalize()
|
||||
{
|
||||
@@ -71,20 +94,19 @@ public:
|
||||
}
|
||||
/// Return true if the rank in MPI_COMM_WORLD is zero.
|
||||
static bool Root() { return WorldRank() == 0; }
|
||||
/// Default level of thread support for MPI_Init_thread.
|
||||
static MFEM_EXPORT int default_thread_required;
|
||||
private:
|
||||
/// Initialize MPI
|
||||
static void Init_(int *argc, char ***argv)
|
||||
/// Initialize the Mpi singleton.
|
||||
static Mpi &Singleton()
|
||||
{
|
||||
MFEM_VERIFY(!IsInitialized(), "MPI already initialized!")
|
||||
MPI_Init(argc, argv);
|
||||
// The "mpi" object below needs to be created after MPI_Init() for some
|
||||
// MPI implementations
|
||||
static Mpi mpi;
|
||||
return mpi;
|
||||
}
|
||||
/// Finalize MPI
|
||||
/// Finalize MPI.
|
||||
~Mpi() { Finalize(); }
|
||||
/// Prevent direct construction of objects of this class
|
||||
Mpi() { }
|
||||
/// Prevent direct construction of objects of this class.
|
||||
Mpi() {}
|
||||
};
|
||||
|
||||
/** @brief A simple convenience class based on the Mpi singleton class above.
|
||||
|
||||
+50
-31
@@ -16,13 +16,13 @@
|
||||
#include <cstdlib>
|
||||
#include <errno.h>
|
||||
#ifndef _WIN32
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <winsock.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#ifdef _MSC_VER
|
||||
typedef int ssize_t;
|
||||
// Link with ws2_32.lib
|
||||
@@ -51,47 +51,66 @@ int isockstream::establish()
|
||||
{
|
||||
// char myname[129];
|
||||
char myname[] = "localhost";
|
||||
int port;
|
||||
struct sockaddr_in sa;
|
||||
struct hostent *hp;
|
||||
int sfd;
|
||||
struct addrinfo hints, *res, *rp;
|
||||
|
||||
memset(&sa, 0, sizeof(struct sockaddr_in));
|
||||
// gethostname(myname, 128);
|
||||
hp= gethostbyname(myname);
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
if (hp == NULL)
|
||||
int s = getaddrinfo(myname, NULL, &hints, &res);
|
||||
if (s != 0)
|
||||
{
|
||||
mfem::err << "isockstream::establish(): gethostbyname() failed!\n"
|
||||
<< "isockstream::establish(): gethostname() returned: '"
|
||||
mfem::err << "isockstream::establish(): getaddrinfo() failed!\n"
|
||||
<< "isockstream::establish(): getaddrinfo() returned: '"
|
||||
<< myname << "'" << endl;
|
||||
error = 1;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
sa.sin_family= hp->h_addrtype;
|
||||
sa.sin_port= htons(portnum);
|
||||
|
||||
if ((port = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
// loop the list of address structures returned by getaddrinfo()
|
||||
for (rp = res; rp != NULL; rp = rp->ai_next)
|
||||
{
|
||||
mfem::err << "isockstream::establish(): socket() failed!" << endl;
|
||||
error = 2;
|
||||
if ((sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
|
||||
{
|
||||
mfem::err << "isockstream::establish(): socket() failed!" << endl;
|
||||
error = 2;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int on = 1;
|
||||
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
|
||||
{
|
||||
mfem::err << "isockstream::establish(): setsockopt() failed!" << endl;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#if defined(__APPLE__)
|
||||
if (bind(sfd, (const struct sockaddr *)rp->ai_addr, rp->ai_addrlen) < 0)
|
||||
#else
|
||||
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) < 0)
|
||||
#endif
|
||||
{
|
||||
mfem::err << "isockstream::establish(): bind() failed!" << endl;
|
||||
close(sfd);
|
||||
error = 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// No address succeeded
|
||||
if (rp == NULL)
|
||||
{
|
||||
mfem::err << "Could not bind\n";
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int on=1;
|
||||
setsockopt(port, SOL_SOCKET, SO_REUSEADDR, (char *)(&on), sizeof(on));
|
||||
|
||||
if (bind(port,(const sockaddr*)&sa,(socklen_t)sizeof(struct sockaddr_in)) < 0)
|
||||
{
|
||||
mfem::err << "isockstream::establish(): bind() failed!" << endl;
|
||||
close(port);
|
||||
error = 3;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
listen(port, 4);
|
||||
error = 0;
|
||||
return (port);
|
||||
freeaddrinfo(res);
|
||||
listen(sfd, 4);
|
||||
return (sfd);
|
||||
}
|
||||
|
||||
int isockstream::read_data(int s, char *buf, int n)
|
||||
|
||||
+35
-31
@@ -19,15 +19,15 @@
|
||||
#include <cstring> // memset, memcpy, strerror
|
||||
#include <cerrno> // errno
|
||||
#ifndef _WIN32
|
||||
#include <netdb.h> // gethostbyname
|
||||
#include <netdb.h> // getaddrinfo
|
||||
#include <arpa/inet.h> // htons
|
||||
#include <sys/types.h> // socket, setsockopt, connect, recv, send
|
||||
#include <sys/socket.h> // socket, setsockopt, connect, recv, send
|
||||
#include <unistd.h> // close
|
||||
#include <netinet/in.h> // sockaddr_in
|
||||
#define closesocket (::close)
|
||||
#else
|
||||
#include <winsock.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#ifdef _MSC_VER
|
||||
typedef int ssize_t;
|
||||
// Link with ws2_32.lib
|
||||
@@ -93,8 +93,7 @@ int socketbuf::attach(int sd)
|
||||
|
||||
int socketbuf::open(const char hostname[], int port)
|
||||
{
|
||||
struct sockaddr_in sa;
|
||||
struct hostent *hp;
|
||||
struct addrinfo hints, *res, *rp;
|
||||
|
||||
if (!wsInit_.Initialized())
|
||||
{
|
||||
@@ -105,42 +104,47 @@ int socketbuf::open(const char hostname[], int port)
|
||||
setg(NULL, NULL, NULL);
|
||||
setp(obuf, obuf + buflen);
|
||||
|
||||
hp = gethostbyname(hostname);
|
||||
if (hp == NULL)
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
int s = getaddrinfo(hostname, NULL, &hints, &res);
|
||||
if (s != 0)
|
||||
{
|
||||
socket_descriptor = -3;
|
||||
return -1;
|
||||
}
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
memcpy((char *)&sa.sin_addr, hp->h_addr, hp->h_length);
|
||||
sa.sin_family = hp->h_addrtype;
|
||||
sa.sin_port = htons(port);
|
||||
socket_descriptor = socket(hp->h_addrtype, SOCK_STREAM, 0);
|
||||
if (socket_descriptor < 0)
|
||||
|
||||
for (rp = res; rp != NULL; rp = rp->ai_next)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
socket_descriptor = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (socket_descriptor < 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if defined __APPLE__
|
||||
// OS X does not support the MSG_NOSIGNAL option of send().
|
||||
// Instead we can use the SO_NOSIGPIPE socket option.
|
||||
int on = 1;
|
||||
if (setsockopt(socket_descriptor, SOL_SOCKET, SO_NOSIGPIPE,
|
||||
(char *)(&on), sizeof(on)) < 0)
|
||||
{
|
||||
closesocket(socket_descriptor);
|
||||
socket_descriptor = -2;
|
||||
return -1;
|
||||
}
|
||||
// OS X does not support the MSG_NOSIGNAL option of send().
|
||||
// Instead we can use the SO_NOSIGPIPE socket option.
|
||||
int on = 1;
|
||||
if (setsockopt(socket_descriptor, SOL_SOCKET, SO_NOSIGPIPE,
|
||||
&on, sizeof(on)) < 0)
|
||||
{
|
||||
closesocket(socket_descriptor);
|
||||
socket_descriptor = -2;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (connect(socket_descriptor,
|
||||
(const struct sockaddr *)&sa, sizeof(sa)) < 0)
|
||||
{
|
||||
closesocket(socket_descriptor);
|
||||
socket_descriptor = -2;
|
||||
return -1;
|
||||
if (connect(socket_descriptor, rp->ai_addr, rp->ai_addrlen) < 0)
|
||||
{
|
||||
closesocket(socket_descriptor);
|
||||
socket_descriptor = -2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(res);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -218,7 +218,8 @@ void Table::SetIJ(int *newI, int *newJ, int newsize)
|
||||
|
||||
int Table::Push(int i, int j)
|
||||
{
|
||||
MFEM_ASSERT( i >=0 && i<size, "Index out of bounds. i = "<<i);
|
||||
MFEM_ASSERT(i >=0 &&
|
||||
i<size, "Index out of bounds. i = " << i << " size " << size);
|
||||
|
||||
for (int k = I[i], end = I[i+1]; k < end; k++)
|
||||
{
|
||||
|
||||
+252
-15
@@ -21,6 +21,7 @@
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -28,8 +29,14 @@ namespace mfem
|
||||
namespace Ginkgo
|
||||
{
|
||||
|
||||
// Create a GinkgoExecutor of type exec_type.
|
||||
GinkgoExecutor::GinkgoExecutor(ExecType exec_type)
|
||||
{
|
||||
#if defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP)
|
||||
gko::version_info gko_version = gko::version_info::get();
|
||||
bool gko_with_omp_support = (strcmp(gko_version.omp_version.tag,
|
||||
"not compiled") != 0);
|
||||
#endif
|
||||
switch (exec_type)
|
||||
{
|
||||
case GinkgoExecutor::REFERENCE:
|
||||
@@ -49,13 +56,23 @@ GinkgoExecutor::GinkgoExecutor(ExecType exec_type)
|
||||
#ifdef MFEM_USE_CUDA
|
||||
int current_device = 0;
|
||||
MFEM_GPU_CHECK(cudaGetDevice(¤t_device));
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
if (gko_with_omp_support)
|
||||
{
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::ReferenceExecutor::create());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
|
||||
"any valid devices.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GinkgoExecutor::HIP:
|
||||
@@ -65,24 +82,111 @@ GinkgoExecutor::GinkgoExecutor(ExecType exec_type)
|
||||
#ifdef MFEM_USE_HIP
|
||||
int current_device = 0;
|
||||
MFEM_GPU_CHECK(hipGetDevice(¤t_device));
|
||||
executor = gko::HipExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
if (gko_with_omp_support)
|
||||
{
|
||||
executor = gko::HipExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::HipExecutor::create(current_device,
|
||||
gko::ReferenceExecutor::create());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
mfem::err << "gko::HipExecutor::get_num_devices() did not report "
|
||||
<< "any valid devices" << std::endl;
|
||||
{
|
||||
MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
|
||||
"any valid devices.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
mfem::err << "Invalid ExecType specified" << std::endl;
|
||||
MFEM_ABORT("Invalid ExecType specified");
|
||||
}
|
||||
}
|
||||
|
||||
// Create a GinkgoExecutor of type exec_type, with host_exec_type for the
|
||||
// related CPU Executor (only applicable to GPU backends).
|
||||
GinkgoExecutor::GinkgoExecutor(ExecType exec_type, ExecType host_exec_type)
|
||||
{
|
||||
switch (exec_type)
|
||||
{
|
||||
case GinkgoExecutor::REFERENCE:
|
||||
{
|
||||
MFEM_WARNING("Parameter host_exec_type ignored for CPU GinkgoExecutor.");
|
||||
executor = gko::ReferenceExecutor::create();
|
||||
break;
|
||||
}
|
||||
case GinkgoExecutor::OMP:
|
||||
{
|
||||
MFEM_WARNING("Parameter host_exec_type ignored for CPU GinkgoExecutor.");
|
||||
executor = gko::OmpExecutor::create();
|
||||
break;
|
||||
}
|
||||
case GinkgoExecutor::CUDA:
|
||||
{
|
||||
if (gko::CudaExecutor::get_num_devices() > 0)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
int current_device = 0;
|
||||
MFEM_GPU_CHECK(cudaGetDevice(¤t_device));
|
||||
if (host_exec_type == GinkgoExecutor::OMP)
|
||||
{
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::ReferenceExecutor::create());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
|
||||
"any valid devices.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GinkgoExecutor::HIP:
|
||||
{
|
||||
if (gko::HipExecutor::get_num_devices() > 0)
|
||||
{
|
||||
#ifdef MFEM_USE_HIP
|
||||
int current_device = 0;
|
||||
MFEM_GPU_CHECK(hipGetDevice(¤t_device));
|
||||
if (host_exec_type == GinkgoExecutor::OMP)
|
||||
{
|
||||
executor = gko::HipExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::HipExecutor::create(current_device,
|
||||
gko::ReferenceExecutor::create());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
|
||||
"any valid devices.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MFEM_ABORT("Invalid ExecType specified");
|
||||
}
|
||||
}
|
||||
|
||||
// Create a GinkgoExecutor to match MFEM's device configuration.
|
||||
GinkgoExecutor::GinkgoExecutor(Device &mfem_device)
|
||||
{
|
||||
|
||||
// Pick "best match" Executor based on MFEM device configuration.
|
||||
gko::version_info gko_version = gko::version_info::get();
|
||||
bool gko_with_omp_support = (strcmp(gko_version.omp_version.tag,
|
||||
"not compiled") != 0);
|
||||
if (mfem_device.Allows(Backend::CUDA_MASK))
|
||||
{
|
||||
if (gko::CudaExecutor::get_num_devices() > 0)
|
||||
@@ -90,13 +194,23 @@ GinkgoExecutor::GinkgoExecutor(Device &mfem_device)
|
||||
#ifdef MFEM_USE_CUDA
|
||||
int current_device = 0;
|
||||
MFEM_GPU_CHECK(cudaGetDevice(¤t_device));
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
if (gko_with_omp_support)
|
||||
{
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::ReferenceExecutor::create());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
|
||||
"any valid devices.");
|
||||
}
|
||||
}
|
||||
else if (mfem_device.Allows(Backend::HIP_MASK))
|
||||
{
|
||||
@@ -105,16 +219,123 @@ GinkgoExecutor::GinkgoExecutor(Device &mfem_device)
|
||||
#ifdef MFEM_USE_HIP
|
||||
int current_device = 0;
|
||||
MFEM_GPU_CHECK(hipGetDevice(¤t_device));
|
||||
executor = gko::HipExecutor::create(current_device, gko::OmpExecutor::create());
|
||||
if (gko_with_omp_support)
|
||||
{
|
||||
executor = gko::HipExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::HipExecutor::create(current_device,
|
||||
gko::ReferenceExecutor::create());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
|
||||
"any valid devices.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::OmpExecutor::create();
|
||||
if (mfem_device.Allows(Backend::OMP_MASK))
|
||||
{
|
||||
// Also use OpenMP for Ginkgo, if Ginkgo supports it
|
||||
if (gko_with_omp_support)
|
||||
{
|
||||
executor = gko::OmpExecutor::create();
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::ReferenceExecutor::create();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::ReferenceExecutor::create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a GinkgoExecutor to match MFEM's device configuration, with
|
||||
// a specific host_exec_type for the associated CPU Executor (only
|
||||
// applicable to GPU backends).
|
||||
GinkgoExecutor::GinkgoExecutor(Device &mfem_device, ExecType host_exec_type)
|
||||
{
|
||||
|
||||
if (mfem_device.Allows(Backend::CUDA_MASK))
|
||||
{
|
||||
if (gko::CudaExecutor::get_num_devices() > 0)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
int current_device = 0;
|
||||
MFEM_GPU_CHECK(cudaGetDevice(¤t_device));
|
||||
if (host_exec_type == GinkgoExecutor::OMP)
|
||||
{
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::CudaExecutor::create(current_device,
|
||||
gko::ReferenceExecutor::create());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
|
||||
"any valid devices.");
|
||||
}
|
||||
}
|
||||
else if (mfem_device.Allows(Backend::HIP_MASK))
|
||||
{
|
||||
if (gko::HipExecutor::get_num_devices() > 0)
|
||||
{
|
||||
#ifdef MFEM_USE_HIP
|
||||
int current_device = 0;
|
||||
MFEM_GPU_CHECK(hipGetDevice(¤t_device));
|
||||
if (host_exec_type == GinkgoExecutor::OMP)
|
||||
{
|
||||
executor = gko::HipExecutor::create(current_device,
|
||||
gko::OmpExecutor::create());
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::HipExecutor::create(current_device,
|
||||
gko::ReferenceExecutor::create());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
|
||||
"any valid devices.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_WARNING("Parameter host_exec_type ignored for CPU GinkgoExecutor.");
|
||||
if (mfem_device.Allows(Backend::OMP_MASK))
|
||||
{
|
||||
// Also use OpenMP for Ginkgo, if Ginkgo supports it
|
||||
gko::version_info gko_version = gko::version_info::get();
|
||||
bool gko_with_omp_support = (strcmp(gko_version.omp_version.tag,
|
||||
"not compiled") != 0);
|
||||
if (gko_with_omp_support)
|
||||
{
|
||||
executor = gko::OmpExecutor::create();
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::ReferenceExecutor::create();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
executor = gko::ReferenceExecutor::create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +416,7 @@ const
|
||||
gko::log::Logger::criterion_check_completed_mask);
|
||||
#endif
|
||||
residual_logger = std::make_shared<ResidualLogger<>>(executor,
|
||||
gko::lend(system_oper),b);
|
||||
system_oper.get(),b);
|
||||
|
||||
}
|
||||
|
||||
@@ -334,7 +555,7 @@ GinkgoIterativeSolver::Mult(const Vector &x, Vector &y) const
|
||||
|
||||
// Create the logger object to log some data from the solvers to confirm
|
||||
// convergence.
|
||||
initialize_ginkgo_log(gko::lend(gko_x));
|
||||
initialize_ginkgo_log(gko_x.get());
|
||||
|
||||
MFEM_VERIFY(convergence_logger, "convergence logger not initialized" );
|
||||
if (print_level==1)
|
||||
@@ -350,7 +571,11 @@ GinkgoIterativeSolver::Mult(const Vector &x, Vector &y) const
|
||||
combined_factory->add_logger(convergence_logger);
|
||||
|
||||
// Finally, apply the solver to x and get the solution in y.
|
||||
#if MFEM_GINKGO_VERSION < 10600
|
||||
solver->apply(gko::lend(gko_x), gko::lend(gko_y));
|
||||
#else
|
||||
solver->apply(gko_x, gko_y);
|
||||
#endif
|
||||
|
||||
// Get the number of iterations taken to converge to the solution.
|
||||
final_iter = convergence_logger->get_num_iterations();
|
||||
@@ -467,6 +692,10 @@ void GinkgoIterativeSolver::SetOperator(const Operator &op)
|
||||
new OperatorWrapper(executor, op.Height(), &op));
|
||||
}
|
||||
|
||||
// Set MFEM Solver size values
|
||||
height = op.Height();
|
||||
width = op.Width();
|
||||
|
||||
// Generate the solver from the solver using the system matrix or operator.
|
||||
solver = solver_gen->generate(system_oper);
|
||||
}
|
||||
@@ -878,7 +1107,11 @@ GinkgoPreconditioner::Mult(const Vector &x, Vector &y) const
|
||||
gko_array<double>::view(executor,
|
||||
y.Size(),
|
||||
y.ReadWrite(on_device)), 1);
|
||||
#if MFEM_GINKGO_VERSION < 10600
|
||||
generated_precond.get()->apply(gko::lend(gko_x), gko::lend(gko_y));
|
||||
#else
|
||||
generated_precond.get()->apply(gko_x, gko_y);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GinkgoPreconditioner::SetOperator(const Operator &op)
|
||||
@@ -917,6 +1150,10 @@ void GinkgoPreconditioner::SetOperator(const Operator &op)
|
||||
|
||||
generated_precond = precond_gen->generate(gko::give(gko_matrix));
|
||||
has_generated_precond = true;
|
||||
|
||||
// Set MFEM Solver size values
|
||||
height = op.Height();
|
||||
width = op.Width();
|
||||
}
|
||||
|
||||
|
||||
|
||||
+143
-75
@@ -265,9 +265,13 @@ double compute_norm(const gko::matrix::Dense<ValueType> *b)
|
||||
// Initialize a result scalar containing the value 0.0.
|
||||
auto b_norm = gko::initialize<gko::matrix::Dense<ValueType>>({0.0}, exec);
|
||||
// Use the dense `compute_norm2` function to compute the norm.
|
||||
b->compute_norm2(lend(b_norm));
|
||||
#if MFEM_GINKGO_VERSION < 10600
|
||||
b->compute_norm2(gko::lend(b_norm));
|
||||
#else
|
||||
b->compute_norm2(b_norm);
|
||||
#endif
|
||||
// Use the other utility function to return the norm contained in `b_norm``
|
||||
return std::pow(get_norm(lend(b_norm)),2);
|
||||
return std::pow(get_norm(b_norm.get()),2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,84 +323,43 @@ struct ResidualLogger : gko::log::Logger
|
||||
|
||||
using gko_dense = gko::matrix::Dense<ValueType>;
|
||||
|
||||
// Customize the logging hook which is called every time an iteration is
|
||||
// completed
|
||||
void on_iteration_complete(const gko::LinOp *,
|
||||
const gko::size_type &iteration,
|
||||
const gko::LinOp *residual,
|
||||
const gko::LinOp *solution,
|
||||
const gko::LinOp *residual_norm,
|
||||
const gko::LinOp *implicit_sq_residual_norm) const override
|
||||
{
|
||||
// If the solver shares the current solution vector and we want to
|
||||
// compute the residual from that
|
||||
if (solution && compute_real_residual)
|
||||
{
|
||||
// Store the matrix's executor
|
||||
auto exec = matrix->get_executor();
|
||||
// Compute the real residual vector by calling apply on the system
|
||||
// First, compute res = A * x
|
||||
matrix->apply(gko::lend(solution), gko::lend(res));
|
||||
// Now do res = res - b, depending on which vector/oper type
|
||||
// Check if b is a Ginkgo vector or wrapped MFEM Vector
|
||||
if (dynamic_cast<const VectorWrapper*>(b))
|
||||
{
|
||||
const VectorWrapper *b_cast = gko::as<const VectorWrapper>(b);
|
||||
// Copy the MFEM Vector stored in b
|
||||
VectorWrapper *res_cast = gko::as<VectorWrapper>(res);
|
||||
res_cast->get_mfem_vec_ref() -= b_cast->get_mfem_vec_const_ref();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a scalar containing the value -1.0
|
||||
auto neg_one = gko::initialize<gko_dense>({-1.0}, exec);
|
||||
res->add_scaled(gko::lend(neg_one), gko::lend(b));
|
||||
}
|
||||
|
||||
// Compute the norm of the residual vector and add it to the
|
||||
// `residual_norms` vector
|
||||
residual_norms.push_back(compute_norm(gko::lend(res)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the solver shares an implicit or recurrent residual norm, log its value
|
||||
if (implicit_sq_residual_norm)
|
||||
{
|
||||
auto dense_norm = gko::as<gko_dense>(implicit_sq_residual_norm);
|
||||
// Add the norm to the `residual_norms` vector
|
||||
residual_norms.push_back(get_norm(dense_norm));
|
||||
// Otherwise, use the recurrent residual vector
|
||||
}
|
||||
else if (residual_norm)
|
||||
{
|
||||
auto dense_norm = gko::as<gko_dense>(residual_norm);
|
||||
// Add the norm to the `residual_norms` vector
|
||||
residual_norms.push_back(get_norm(dense_norm));
|
||||
// Otherwise, use the recurrent residual vector
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dense_residual = gko::as<gko_dense>(residual);
|
||||
// Compute the residual vector's norm
|
||||
auto norm = compute_norm(gko::lend(dense_residual));
|
||||
// Add the computed norm to the `residual_norms` vector
|
||||
residual_norms.push_back(norm);
|
||||
}
|
||||
}
|
||||
// Add the current iteration number to the `iterations` vector
|
||||
iterations.push_back(iteration);
|
||||
}
|
||||
|
||||
// Version for solver that doesn't log implicit res norm
|
||||
// Ginkgo 1.5 and older: version for solver that doesn't log implicit res norm
|
||||
void on_iteration_complete(const gko::LinOp *op,
|
||||
const gko::size_type &iteration,
|
||||
const gko::LinOp *residual,
|
||||
const gko::LinOp *solution,
|
||||
const gko::LinOp *residual_norm) const override
|
||||
{
|
||||
on_iteration_complete(op, iteration, residual, solution, residual_norm,
|
||||
nullptr);
|
||||
iteration_complete_core(iteration, residual, solution, residual_norm,
|
||||
nullptr);
|
||||
}
|
||||
// Ginkgo 1.5 and older: version with implicit residual norm
|
||||
void on_iteration_complete(const gko::LinOp *op,
|
||||
const gko::size_type &iteration,
|
||||
const gko::LinOp *residual,
|
||||
const gko::LinOp *solution,
|
||||
const gko::LinOp *residual_norm,
|
||||
const gko::LinOp *implicit_sq_residual_norm) const override
|
||||
{
|
||||
iteration_complete_core(iteration, residual, solution, residual_norm,
|
||||
implicit_sq_residual_norm);
|
||||
}
|
||||
#if MFEM_GINKGO_VERSION > 10500
|
||||
// Ginkgo 1.6 and newer
|
||||
void on_iteration_complete(const gko::LinOp *op,
|
||||
const gko::LinOp *rhs,
|
||||
const gko::LinOp *solution,
|
||||
const gko::size_type &iteration,
|
||||
const gko::LinOp *residual,
|
||||
const gko::LinOp *residual_norm,
|
||||
const gko::LinOp *implicit_sq_residual_norm,
|
||||
const gko::array<gko::stopping_status>* status,
|
||||
bool stopped) const override
|
||||
{
|
||||
iteration_complete_core(iteration, residual, solution, residual_norm,
|
||||
implicit_sq_residual_norm);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Construct the logger and store the system matrix and b vectors
|
||||
ResidualLogger(std::shared_ptr<const gko::Executor> exec,
|
||||
@@ -428,6 +391,89 @@ struct ResidualLogger : gko::log::Logger
|
||||
}
|
||||
|
||||
private:
|
||||
// Customize the logging hook which is called every time an iteration is
|
||||
// completed.
|
||||
void iteration_complete_core(const gko::size_type &iteration,
|
||||
const gko::LinOp *residual,
|
||||
const gko::LinOp *solution,
|
||||
const gko::LinOp *residual_norm,
|
||||
const gko::LinOp *implicit_sq_residual_norm) const
|
||||
{
|
||||
// If the solver shares the current solution vector and we want to
|
||||
// compute the residual from that
|
||||
if (solution && compute_real_residual)
|
||||
{
|
||||
// Store the matrix's executor
|
||||
auto exec = matrix->get_executor();
|
||||
// Compute the real residual vector by calling apply on the system
|
||||
// First, compute res = A * x
|
||||
#if MFEM_GINKGO_VERSION < 10600
|
||||
matrix->apply(gko::lend(solution), gko::lend(res));
|
||||
#else
|
||||
matrix->apply(solution, res);
|
||||
#endif
|
||||
// Now do res = res - b, depending on which vector/oper type
|
||||
// Check if b is a Ginkgo vector or wrapped MFEM Vector
|
||||
if (dynamic_cast<const VectorWrapper*>(b))
|
||||
{
|
||||
const VectorWrapper *b_cast = gko::as<const VectorWrapper>(b);
|
||||
// Copy the MFEM Vector stored in b
|
||||
VectorWrapper *res_cast = gko::as<VectorWrapper>(res);
|
||||
res_cast->get_mfem_vec_ref() -= b_cast->get_mfem_vec_const_ref();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a scalar containing the value -1.0
|
||||
auto neg_one = gko::initialize<gko_dense>({-1.0}, exec);
|
||||
#if MFEM_GINKGO_VERSION < 10600
|
||||
res->add_scaled(gko::lend(neg_one), gko::lend(b));
|
||||
#else
|
||||
res->add_scaled(neg_one, b);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Compute the norm of the residual vector and add it to the
|
||||
// `residual_norms` vector
|
||||
#if MFEM_GINKGO_VERSION < 10600
|
||||
residual_norms.push_back(compute_norm(gko::lend(res)));
|
||||
#else
|
||||
residual_norms.push_back(compute_norm(res));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the solver shares an implicit or recurrent residual norm, log its value
|
||||
if (implicit_sq_residual_norm)
|
||||
{
|
||||
auto dense_norm = gko::as<gko_dense>(implicit_sq_residual_norm);
|
||||
// Add the norm to the `residual_norms` vector
|
||||
residual_norms.push_back(get_norm(dense_norm));
|
||||
// Otherwise, use the recurrent residual vector
|
||||
}
|
||||
else if (residual_norm)
|
||||
{
|
||||
auto dense_norm = gko::as<gko_dense>(residual_norm);
|
||||
// Add the norm to the `residual_norms` vector
|
||||
residual_norms.push_back(get_norm(dense_norm));
|
||||
// Otherwise, use the recurrent residual vector
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dense_residual = gko::as<gko_dense>(residual);
|
||||
// Compute the residual vector's norm
|
||||
#if MFEM_GINKGO_VERSION < 10600
|
||||
auto norm = compute_norm(gko::lend(dense_residual));
|
||||
#else
|
||||
auto norm = compute_norm(dense_residual);
|
||||
#endif
|
||||
// Add the computed norm to the `residual_norms` vector
|
||||
residual_norms.push_back(norm);
|
||||
}
|
||||
}
|
||||
// Add the current iteration number to the `iterations` vector
|
||||
iterations.push_back(iteration);
|
||||
}
|
||||
|
||||
// Pointer to the system matrix
|
||||
const gko::LinOp *matrix;
|
||||
// Pointer to the right hand sides
|
||||
@@ -469,18 +515,40 @@ public:
|
||||
/**
|
||||
* Constructor.
|
||||
* Takes an @p GinkgoExecType argument and creates an Executor.
|
||||
* In Ginkgo, GPU Executors must have an associated host Executor.
|
||||
* This routine will select a CPU Executor based on the OpenMP support
|
||||
* for Ginkgo.
|
||||
*/
|
||||
GinkgoExecutor(ExecType exec_type);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Takes an @p GinkgoExecType argument and creates an Executor.
|
||||
* In Ginkgo, GPU Executors must have an associated host Executor.
|
||||
* This routine allows for explicite setting of the CPU Executor
|
||||
* for GPU backends.
|
||||
*/
|
||||
GinkgoExecutor(ExecType exec_type, ExecType host_exec_type);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Takes an MFEM @p Device object and creates an Executor
|
||||
* that "matches" (e.g., if MFEM is using the CPU, Ginkgo
|
||||
* will choose the OmpExecutor; if MFEM is using CUDA,
|
||||
* Ginkgo will choose the CudaExecutor).
|
||||
* will choose the Reference or OmpExecutor based on MFEM's
|
||||
* configuration and Ginkgo's capabilities; if MFEM is using
|
||||
* CUDA, Ginkgo will choose the CudaExecutor with a default
|
||||
* CPU Executor based on Ginkgo's OpenMP support).
|
||||
*/
|
||||
GinkgoExecutor(Device &mfem_device);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Takes an MFEM @p Device object and creates an Executor
|
||||
* that "matches", but allows the user to specify the host
|
||||
* Executor for GPU backends.
|
||||
*/
|
||||
GinkgoExecutor(Device &mfem_device, ExecType host_exec_type);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
|
||||
@@ -264,7 +264,6 @@ void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
|
||||
Vector * HypreParVector::GlobalVector() const
|
||||
{
|
||||
hypre_Vector *hv = hypre_ParVectorToVectorAll(*this);
|
||||
if (hv == nullptr) { return nullptr; }
|
||||
Vector *v = new Vector(hv->data, internal::to_int(hv->size));
|
||||
v->MakeDataOwner();
|
||||
hypre_SeqVectorSetDataOwner(hv,0);
|
||||
|
||||
+442
-209
@@ -16,238 +16,471 @@
|
||||
|
||||
#include "strumpack.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace strumpack;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
STRUMPACKRowLocMatrix::STRUMPACKRowLocMatrix(MPI_Comm comm,
|
||||
int num_loc_rows, int first_loc_row,
|
||||
int glob_nrows, int glob_ncols,
|
||||
int *I, int *J, double *data)
|
||||
: comm_(comm), A_(NULL)
|
||||
int num_loc_rows,
|
||||
HYPRE_BigInt first_loc_row,
|
||||
HYPRE_BigInt glob_nrows,
|
||||
HYPRE_BigInt glob_ncols,
|
||||
int *I, HYPRE_BigInt *J,
|
||||
double *data, bool sym_sparse)
|
||||
{
|
||||
// Set mfem::Operator member data
|
||||
height = num_loc_rows;
|
||||
width = num_loc_rows;
|
||||
|
||||
// Allocate STRUMPACK's CSRMatrixMPI
|
||||
int nprocs, rank;
|
||||
MPI_Comm_rank(comm_, &rank);
|
||||
MPI_Comm_size(comm_, &nprocs);
|
||||
int * dist = new int[nprocs + 1];
|
||||
dist[rank + 1] = first_loc_row + num_loc_rows;
|
||||
// Allocate STRUMPACK's CSRMatrixMPI (copies all inputs)
|
||||
int rank, nprocs;
|
||||
MPI_Comm_rank(comm, &rank);
|
||||
MPI_Comm_size(comm, &nprocs);
|
||||
Array<HYPRE_BigInt> dist(nprocs + 1);
|
||||
dist[0] = 0;
|
||||
MPI_Allgather(MPI_IN_PLACE, 0, MPI_INT, dist + 1, 1, MPI_INT, comm_);
|
||||
A_ = new CSRMatrixMPI<double,int>(num_loc_rows, I, J, data, dist, comm_, false);
|
||||
delete[] dist;
|
||||
}
|
||||
dist[rank + 1] = first_loc_row + (HYPRE_BigInt)num_loc_rows;
|
||||
MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL,
|
||||
dist.GetData() + 1, 1, HYPRE_MPI_BIG_INT, comm);
|
||||
|
||||
STRUMPACKRowLocMatrix::STRUMPACKRowLocMatrix(const HypreParMatrix & hypParMat)
|
||||
: comm_(hypParMat.GetComm()),
|
||||
A_(NULL)
|
||||
{
|
||||
// First cast the parameter to a hypre_ParCSRMatrix
|
||||
hypre_ParCSRMatrix * parcsr_op =
|
||||
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix&>(hypParMat);
|
||||
|
||||
MFEM_ASSERT(parcsr_op != NULL,"STRUMPACK: const_cast failed in SetOperator");
|
||||
|
||||
// Create the CSRMatrixMPI A_ by borrowing the internal data from a
|
||||
// hypre_CSRMatrix.
|
||||
hypParMat.HostRead();
|
||||
hypre_CSRMatrix * csr_op = hypre_MergeDiagAndOffd(parcsr_op);
|
||||
hypParMat.HypreRead();
|
||||
hypre_CSRMatrixSetDataOwner(csr_op,0);
|
||||
#if MFEM_HYPRE_VERSION >= 21600
|
||||
// For now, this method assumes that HYPRE_Int is int. Also, csr_op->num_cols
|
||||
// is of type HYPRE_Int, so if we want to check for big indices in
|
||||
// csr_op->big_j, we'll have to check all entries and that check will only be
|
||||
// necessary in HYPRE_MIXEDINT mode which is not supported at the moment.
|
||||
hypre_CSRMatrixBigJtoJ(csr_op);
|
||||
#endif
|
||||
|
||||
height = csr_op->num_rows;
|
||||
width = csr_op->num_rows;
|
||||
|
||||
int nprocs, rank;
|
||||
MPI_Comm_rank(comm_, &rank);
|
||||
MPI_Comm_size(comm_, &nprocs);
|
||||
int * dist = new int[nprocs + 1];
|
||||
dist[rank + 1] = parcsr_op->first_row_index + csr_op->num_rows;
|
||||
dist[0] = 0;
|
||||
MPI_Allgather(MPI_IN_PLACE, 0, MPI_INT, dist + 1, 1, MPI_INT, comm_);
|
||||
A_ = new CSRMatrixMPI<double,int>(csr_op->num_rows, csr_op->i, csr_op->j,
|
||||
csr_op->data, dist, comm_, false);
|
||||
delete[] dist;
|
||||
|
||||
// Everything has been copied or abducted so delete the structure
|
||||
hypre_CSRMatrixDestroy(csr_op);
|
||||
}
|
||||
|
||||
STRUMPACKRowLocMatrix::~STRUMPACKRowLocMatrix()
|
||||
{
|
||||
// Delete the struct
|
||||
if ( A_ != NULL ) { delete A_; }
|
||||
}
|
||||
|
||||
STRUMPACKSolver::STRUMPACKSolver( int argc, char* argv[], MPI_Comm comm )
|
||||
: comm_(comm),
|
||||
APtr_(NULL),
|
||||
solver_(NULL)
|
||||
{
|
||||
this->Init(argc, argv);
|
||||
}
|
||||
|
||||
STRUMPACKSolver::STRUMPACKSolver( STRUMPACKRowLocMatrix & A )
|
||||
: comm_(A.GetComm()),
|
||||
APtr_(&A),
|
||||
solver_(NULL)
|
||||
{
|
||||
height = A.Height();
|
||||
width = A.Width();
|
||||
|
||||
this->Init(0, NULL);
|
||||
}
|
||||
|
||||
STRUMPACKSolver::~STRUMPACKSolver()
|
||||
{
|
||||
if ( solver_ != NULL ) { delete solver_; }
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::Init( int argc, char* argv[] )
|
||||
{
|
||||
MPI_Comm_size(comm_, &numProcs_);
|
||||
MPI_Comm_rank(comm_, &myid_);
|
||||
|
||||
factor_verbose_ = false;
|
||||
solve_verbose_ = false;
|
||||
|
||||
solver_ = new StrumpackSparseSolverMPIDist<double,int>(comm_, argc, argv,
|
||||
false);
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::SetFromCommandLine( )
|
||||
{
|
||||
solver_->options().set_from_command_line( );
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::SetPrintFactorStatistics( bool print_stat )
|
||||
{
|
||||
factor_verbose_ = print_stat;
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::SetPrintSolveStatistics( bool print_stat )
|
||||
{
|
||||
solve_verbose_ = print_stat;
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::SetKrylovSolver( strumpack::KrylovSolver method )
|
||||
{
|
||||
solver_->options().set_Krylov_solver( method );
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::SetReorderingStrategy( strumpack::ReorderingStrategy
|
||||
method )
|
||||
{
|
||||
solver_->options().set_reordering_method( method );
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::DisableMatching( )
|
||||
{
|
||||
#if STRUMPACK_VERSION_MAJOR >= 3
|
||||
solver_->options().set_matching( strumpack::MatchingJob::NONE );
|
||||
#if !(defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT))
|
||||
A_ = new strumpack::CSRMatrixMPI<double, HYPRE_BigInt>(
|
||||
(HYPRE_BigInt)num_loc_rows, I, J, data, dist.GetData(),
|
||||
comm, sym_sparse);
|
||||
#else
|
||||
solver_->options().set_mc64job( strumpack::MC64Job::NONE );
|
||||
Array<HYPRE_BigInt> II(num_loc_rows+1);
|
||||
for (int i = 0; i <= num_loc_rows; i++) { II[i] = (HYPRE_BigInt)I[i]; }
|
||||
A_ = new strumpack::CSRMatrixMPI<double, HYPRE_BigInt>(
|
||||
(HYPRE_BigInt)num_loc_rows, II.GetData(), J, data, dist.GetData(),
|
||||
comm, sym_sparse);
|
||||
#endif
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::EnableMatching( )
|
||||
STRUMPACKRowLocMatrix::STRUMPACKRowLocMatrix(const Operator &op,
|
||||
bool sym_sparse)
|
||||
{
|
||||
#if STRUMPACK_VERSION_MAJOR >= 3
|
||||
solver_->options().set_matching
|
||||
( strumpack::MatchingJob::MAX_DIAGONAL_PRODUCT_SCALING );
|
||||
#else
|
||||
solver_->options().set_mc64job
|
||||
( strumpack::MC64Job::MAX_DIAGONAL_PRODUCT_SCALING );
|
||||
#endif
|
||||
}
|
||||
|
||||
#if STRUMPACK_VERSION_MAJOR >= 3
|
||||
void STRUMPACKSolver::EnableParallelMatching( )
|
||||
{
|
||||
solver_->options().set_matching
|
||||
( strumpack::MatchingJob::COMBBLAS );
|
||||
}
|
||||
#endif
|
||||
|
||||
void STRUMPACKSolver::SetRelTol( double rtol )
|
||||
{
|
||||
solver_->options().set_rel_tol( rtol );
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::SetAbsTol( double atol )
|
||||
{
|
||||
solver_->options().set_abs_tol( atol );
|
||||
}
|
||||
|
||||
|
||||
void STRUMPACKSolver::Mult( const Vector & x, Vector & y ) const
|
||||
{
|
||||
MFEM_ASSERT(APtr_ != NULL,
|
||||
"STRUMPACK Error: The operator must be set before"
|
||||
" the system can be solved.");
|
||||
MFEM_ASSERT(x.Size() == Width(), "invalid x.Size() = " << x.Size()
|
||||
<< ", expected size = " << Width());
|
||||
MFEM_ASSERT(y.Size() == Height(), "invalid y.Size() = " << y.Size()
|
||||
<< ", expected size = " << Height());
|
||||
|
||||
double* yPtr = y.HostWrite();
|
||||
const double* xPtr = x.HostRead();
|
||||
|
||||
solver_->options().set_verbose( factor_verbose_ );
|
||||
ReturnCode ret = solver_->factor();
|
||||
switch (ret)
|
||||
{
|
||||
case ReturnCode::SUCCESS: break;
|
||||
case ReturnCode::MATRIX_NOT_SET:
|
||||
{
|
||||
MFEM_ABORT("STRUMPACK: Matrix was not set!");
|
||||
}
|
||||
break;
|
||||
case ReturnCode::REORDERING_ERROR:
|
||||
{
|
||||
MFEM_ABORT("STRUMPACK: Matrix reordering failed!");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
MFEM_ABORT("STRUMPACK: 'factor()' error code = " << ret);
|
||||
}
|
||||
}
|
||||
solver_->options().set_verbose( solve_verbose_ );
|
||||
solver_->solve(xPtr, yPtr);
|
||||
|
||||
}
|
||||
|
||||
void STRUMPACKSolver::SetOperator( const Operator & op )
|
||||
{
|
||||
// Verify that we have a compatible operator
|
||||
APtr_ = dynamic_cast<const STRUMPACKRowLocMatrix*>(&op);
|
||||
if ( APtr_ == NULL )
|
||||
{
|
||||
mfem_error("STRUMPACKSolver::SetOperator : not STRUMPACKRowLocMatrix!");
|
||||
}
|
||||
|
||||
solver_->set_matrix( *(APtr_->getA()) );
|
||||
const HypreParMatrix *APtr = dynamic_cast<const HypreParMatrix *>(&op);
|
||||
MFEM_VERIFY(APtr, "Not a compatible matrix type");
|
||||
MPI_Comm comm = APtr->GetComm();
|
||||
|
||||
// Set mfem::Operator member data
|
||||
height = op.Height();
|
||||
width = op.Width();
|
||||
|
||||
// First cast the parameter to a hypre_ParCSRMatrix
|
||||
hypre_ParCSRMatrix *parcsr_op =
|
||||
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix &>(*APtr);
|
||||
|
||||
// Create the CSRMatrixMPI A by taking the internal data from a
|
||||
// hypre_CSRMatrix
|
||||
APtr->HostRead();
|
||||
hypre_CSRMatrix *csr_op = hypre_MergeDiagAndOffd(parcsr_op);
|
||||
APtr->HypreRead();
|
||||
HYPRE_Int *Iptr = csr_op->i;
|
||||
#if MFEM_HYPRE_VERSION >= 21600
|
||||
HYPRE_BigInt *Jptr = csr_op->big_j;
|
||||
#else
|
||||
HYPRE_Int *Jptr = csr_op->j;
|
||||
#endif
|
||||
double *data = csr_op->data;
|
||||
|
||||
HYPRE_BigInt fst_row = parcsr_op->first_row_index;
|
||||
HYPRE_Int m_loc = csr_op->num_rows;
|
||||
|
||||
// Allocate STRUMPACK's CSRMatrixMPI
|
||||
int rank, nprocs;
|
||||
MPI_Comm_rank(comm, &rank);
|
||||
MPI_Comm_size(comm, &nprocs);
|
||||
Array<HYPRE_BigInt> dist(nprocs + 1);
|
||||
dist[0] = 0;
|
||||
dist[rank + 1] = fst_row + (HYPRE_BigInt)m_loc;
|
||||
MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL,
|
||||
dist.GetData() + 1, 1, HYPRE_MPI_BIG_INT, comm);
|
||||
|
||||
#if !defined(HYPRE_MIXEDINT)
|
||||
A_ = new strumpack::CSRMatrixMPI<double, HYPRE_BigInt>(
|
||||
(HYPRE_BigInt)m_loc, Iptr, Jptr, data, dist.GetData(),
|
||||
comm, sym_sparse);
|
||||
#else
|
||||
Array<HYPRE_BigInt> II(m_loc+1);
|
||||
for (int i = 0; i <= m_loc; i++) { II[i] = (HYPRE_BigInt)Iptr[i]; }
|
||||
A_ = new strumpack::CSRMatrixMPI<double, HYPRE_BigInt>(
|
||||
(HYPRE_BigInt)m_loc, II.GetData(), Jptr, data, dist.GetData(),
|
||||
comm, sym_sparse);
|
||||
#endif
|
||||
|
||||
// Everything has been copied so delete the structure
|
||||
hypre_CSRMatrixDestroy(csr_op);
|
||||
}
|
||||
|
||||
STRUMPACKRowLocMatrix::~STRUMPACKRowLocMatrix()
|
||||
{
|
||||
delete A_;
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
STRUMPACKSolverBase(MPI_Comm comm, int argc, char *argv[])
|
||||
: APtr_(NULL),
|
||||
factor_verbose_(false),
|
||||
solve_verbose_(false),
|
||||
reorder_reuse_(false),
|
||||
nrhs_(-1)
|
||||
{
|
||||
solver_ = new STRUMPACKSolverType(comm, argc, argv, false);
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
STRUMPACKSolverBase(STRUMPACKRowLocMatrix &A, int argc, char *argv[])
|
||||
: APtr_(&A),
|
||||
factor_verbose_(false),
|
||||
solve_verbose_(false),
|
||||
reorder_reuse_(false),
|
||||
nrhs_(-1)
|
||||
{
|
||||
solver_ = new STRUMPACKSolverType(A.GetComm(), argc, argv, false);
|
||||
SetOperator(A);
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
~STRUMPACKSolverBase()
|
||||
{
|
||||
delete solver_;
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetFromCommandLine()
|
||||
{
|
||||
solver_->options().set_from_command_line();
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetPrintFactorStatistics(bool print_stat)
|
||||
{
|
||||
factor_verbose_ = print_stat;
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetPrintSolveStatistics(bool print_stat)
|
||||
{
|
||||
solve_verbose_ = print_stat;
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>
|
||||
::SetRelTol(double rtol)
|
||||
{
|
||||
solver_->options().set_rel_tol(rtol);
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>
|
||||
::SetAbsTol(double atol)
|
||||
{
|
||||
solver_->options().set_abs_tol(atol);
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>
|
||||
::SetMaxIter(int max_it)
|
||||
{
|
||||
solver_->options().set_maxit(max_it);
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>
|
||||
::SetReorderingReuse(bool reuse)
|
||||
{
|
||||
reorder_reuse_ = reuse;
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>
|
||||
::EnableGPU()
|
||||
{
|
||||
solver_->options().enable_gpu();
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>
|
||||
::DisableGPU()
|
||||
{
|
||||
solver_->options().disable_gpu();
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetKrylovSolver(strumpack::KrylovSolver method)
|
||||
{
|
||||
solver_->options().set_Krylov_solver(method);
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetReorderingStrategy(strumpack::ReorderingStrategy method)
|
||||
{
|
||||
solver_->options().set_reordering_method(method);
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetMatching(strumpack::MatchingJob job)
|
||||
{
|
||||
solver_->options().set_matching(job);
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetCompression(strumpack::CompressionType type)
|
||||
{
|
||||
#if STRUMPACK_VERSION_MAJOR >= 5
|
||||
solver_->options().set_compression(type);
|
||||
#else
|
||||
switch (type)
|
||||
{
|
||||
case strumpack::NONE:
|
||||
solver_->options().disable_BLR();
|
||||
solver_->options().disable_HSS();
|
||||
break;
|
||||
case strumpack::BLR:
|
||||
solver_->options().enable_BLR();
|
||||
break;
|
||||
case strumpack::HSS:
|
||||
solver_->options().enable_HSS();
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Invalid compression type for STRUMPACK version " <<
|
||||
STRUMPACK_VERSION_MAJOR << "!");
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetCompressionRelTol(double rtol)
|
||||
{
|
||||
#if STRUMPACK_VERSION_MAJOR >= 5
|
||||
solver_->options().set_compression_rel_tol(rtol);
|
||||
#else
|
||||
solver_->options().BLR_options().set_rel_tol(rtol);
|
||||
solver_->options().HSS_options().set_rel_tol(rtol);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetCompressionAbsTol(double atol)
|
||||
{
|
||||
#if STRUMPACK_VERSION_MAJOR >= 5
|
||||
solver_->options().set_compression_abs_tol(atol);
|
||||
#else
|
||||
solver_->options().BLR_options().set_abs_tol(atol);
|
||||
solver_->options().HSS_options().set_abs_tol(atol);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if STRUMPACK_VERSION_MAJOR >= 5
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetCompressionLossyPrecision(int precision)
|
||||
{
|
||||
solver_->options().set_lossy_precision(precision);
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetCompressionButterflyLevels(int levels)
|
||||
{
|
||||
solver_->options().HODLR_options().set_butterfly_levels(levels);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
SetOperator(const Operator &op)
|
||||
{
|
||||
// Verify that we have a compatible operator
|
||||
bool first_mat = !APtr_;
|
||||
APtr_ = dynamic_cast<const STRUMPACKRowLocMatrix *>(&op);
|
||||
MFEM_VERIFY(APtr_,
|
||||
"STRUMPACK: Operator is not a STRUMPACKRowLocMatrix!");
|
||||
|
||||
// Set mfem::Operator member data
|
||||
height = op.Height();
|
||||
width = op.Width();
|
||||
|
||||
if (first_mat || !reorder_reuse_)
|
||||
{
|
||||
solver_->set_matrix(*(APtr_->GetA()));
|
||||
}
|
||||
else
|
||||
{
|
||||
solver_->update_matrix_values(*(APtr_->GetA()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
FactorInternal() const
|
||||
{
|
||||
MFEM_ASSERT(APtr_,
|
||||
"STRUMPACK: Operator must be set before the system can be "
|
||||
"solved!");
|
||||
solver_->options().set_verbose(factor_verbose_);
|
||||
strumpack::ReturnCode ret = solver_->factor();
|
||||
if (ret != strumpack::ReturnCode::SUCCESS)
|
||||
{
|
||||
#if STRUMPACK_VERSION_MAJOR >= 7
|
||||
MFEM_ABORT("STRUMPACK: Factor failed with return code " << ret << "!");
|
||||
#else
|
||||
MFEM_ABORT("STRUMPACK: Factor failed!");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
MFEM_ASSERT(x.Size() == Width(),
|
||||
"STRUMPACK: Invalid x.Size() = " << x.Size() <<
|
||||
", expected size = " << Width() << "!");
|
||||
MFEM_ASSERT(y.Size() == Height(),
|
||||
"STRUMPACK: Invalid y.Size() = " << y.Size() <<
|
||||
", expected size = " << Height() << "!");
|
||||
|
||||
const double *xPtr = x.HostRead();
|
||||
double *yPtr = y.HostReadWrite();
|
||||
|
||||
FactorInternal();
|
||||
solver_->options().set_verbose(solve_verbose_);
|
||||
strumpack::ReturnCode ret = solver_->solve(xPtr, yPtr, false);
|
||||
if (ret != strumpack::ReturnCode::SUCCESS)
|
||||
{
|
||||
#if STRUMPACK_VERSION_MAJOR >= 7
|
||||
MFEM_ABORT("STRUMPACK: Solve failed with return code " << ret << "!");
|
||||
#else
|
||||
MFEM_ABORT("STRUMPACK: Solve failed!");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
template <typename STRUMPACKSolverType>
|
||||
void STRUMPACKSolverBase<STRUMPACKSolverType>::
|
||||
ArrayMult(const Array<const Vector *> &X, Array<Vector *> &Y) const
|
||||
{
|
||||
MFEM_ASSERT(X.Size() == Y.Size(),
|
||||
"Number of columns mismatch in STRUMPACK solve!");
|
||||
if (X.Size() == 1)
|
||||
{
|
||||
nrhs_ = 1;
|
||||
MFEM_ASSERT(X[0] && Y[0], "Missing Vector in STRUMPACK solve!");
|
||||
Mult(*X[0], *Y[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Multiple RHS case
|
||||
int ldx = Height();
|
||||
if (nrhs_ != X.Size())
|
||||
{
|
||||
rhs_.SetSize(X.Size() * ldx);
|
||||
sol_.SetSize(X.Size() * ldx);
|
||||
nrhs_ = X.Size();
|
||||
}
|
||||
for (int i = 0; i < nrhs_; i++)
|
||||
{
|
||||
MFEM_ASSERT(X[i] && X[i]->Size() == Width(),
|
||||
"STRUMPACK: Missing or invalid sized RHS Vector in solve!");
|
||||
Vector s(rhs_, i * ldx, ldx);
|
||||
s = *X[i];
|
||||
rhs_.SyncMemory(s); // Update flags for rhs_ if updated on device
|
||||
}
|
||||
const double *xPtr = rhs_.HostRead();
|
||||
double *yPtr = sol_.HostReadWrite();
|
||||
|
||||
FactorInternal();
|
||||
solver_->options().set_verbose(solve_verbose_);
|
||||
strumpack::ReturnCode ret = solver_->solve(nrhs_, xPtr, ldx, yPtr, ldx,
|
||||
false);
|
||||
if (ret != strumpack::ReturnCode::SUCCESS)
|
||||
{
|
||||
#if STRUMPACK_VERSION_MAJOR >= 7
|
||||
MFEM_ABORT("STRUMPACK: Solve failed with return code " << ret << "!");
|
||||
#else
|
||||
MFEM_ABORT("STRUMPACK: Solve failed!");
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int i = 0; i < nrhs_; i++)
|
||||
{
|
||||
MFEM_ASSERT(Y[i] && Y[i]->Size() == Width(),
|
||||
"STRUMPACK: Missing or invalid sized solution Vector in solve!");
|
||||
Vector s(sol_, i * ldx, ldx);
|
||||
*Y[i] = s;
|
||||
}
|
||||
}
|
||||
|
||||
STRUMPACKSolver::
|
||||
STRUMPACKSolver(MPI_Comm comm)
|
||||
: STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMPIDist<double, HYPRE_BigInt>>
|
||||
(comm, 0, NULL) {}
|
||||
|
||||
STRUMPACKSolver::
|
||||
STRUMPACKSolver(STRUMPACKRowLocMatrix &A)
|
||||
: STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMPIDist<double, HYPRE_BigInt>>
|
||||
(A, 0, NULL) {}
|
||||
|
||||
STRUMPACKSolver::
|
||||
STRUMPACKSolver(MPI_Comm comm, int argc, char *argv[])
|
||||
: STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMPIDist<double, HYPRE_BigInt>>
|
||||
(comm, argc, argv) {}
|
||||
|
||||
STRUMPACKSolver::
|
||||
STRUMPACKSolver(STRUMPACKRowLocMatrix &A, int argc, char *argv[])
|
||||
: STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMPIDist<double, HYPRE_BigInt>>
|
||||
(A, argc, argv) {}
|
||||
|
||||
#if STRUMPACK_VERSION_MAJOR >= 7
|
||||
STRUMPACKMixedPrecisionSolver::
|
||||
STRUMPACKMixedPrecisionSolver(MPI_Comm comm)
|
||||
: STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
|
||||
(comm, 0, NULL) {}
|
||||
|
||||
STRUMPACKMixedPrecisionSolver::
|
||||
STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A)
|
||||
: STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
|
||||
(A, 0, NULL) {}
|
||||
|
||||
STRUMPACKMixedPrecisionSolver::
|
||||
STRUMPACKMixedPrecisionSolver(MPI_Comm comm, int argc, char *argv[])
|
||||
: STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
|
||||
(comm, argc, argv) {}
|
||||
|
||||
STRUMPACKMixedPrecisionSolver::
|
||||
STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A, int argc, char *argv[])
|
||||
: STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
|
||||
(A, argc, argv) {}
|
||||
#endif
|
||||
|
||||
template class STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMPIDist<double, HYPRE_BigInt>>;
|
||||
#if STRUMPACK_VERSION_MAJOR >= 7
|
||||
template class STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>;
|
||||
#endif
|
||||
|
||||
} // mfem namespace
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
+169
-67
@@ -16,12 +16,14 @@
|
||||
|
||||
#ifdef MFEM_USE_STRUMPACK
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "operator.hpp"
|
||||
#include "hypre.hpp"
|
||||
|
||||
#include <mpi.h>
|
||||
|
||||
// STRUMPACK headers
|
||||
#include "StrumpackSparseSolverMPIDist.hpp"
|
||||
#include "StrumpackSparseSolverMixedPrecisionMPIDist.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -34,63 +36,80 @@ public:
|
||||
be of size (local) nrows by (global) glob_ncols. The new parallel matrix
|
||||
contains copies of all input arrays (so they can be deleted). */
|
||||
STRUMPACKRowLocMatrix(MPI_Comm comm,
|
||||
int num_loc_rows, int first_loc_row,
|
||||
int glob_nrows, int glob_ncols,
|
||||
int *I, int *J, double *data);
|
||||
int num_loc_rows, HYPRE_BigInt first_loc_row,
|
||||
HYPRE_BigInt glob_nrows, HYPRE_BigInt glob_ncols,
|
||||
int *I, HYPRE_BigInt *J, double *data,
|
||||
bool sym_sparse = false);
|
||||
|
||||
/** Creates a copy of the parallel matrix hypParMat in STRUMPACK's RowLoc
|
||||
format. All data is copied so the original matrix may be deleted. */
|
||||
STRUMPACKRowLocMatrix(const HypreParMatrix & hypParMat);
|
||||
STRUMPACKRowLocMatrix(const Operator &op, bool sym_sparse = false);
|
||||
|
||||
~STRUMPACKRowLocMatrix();
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
mfem_error("STRUMPACKRowLocMatrix::Mult(...)\n"
|
||||
" matrix vector products are not supported.");
|
||||
MFEM_ABORT("STRUMPACKRowLocMatrix::Mult: Matrix vector products are not "
|
||||
"supported!");
|
||||
}
|
||||
|
||||
MPI_Comm GetComm() const { return comm_; }
|
||||
MPI_Comm GetComm() const { return A_->comm(); }
|
||||
|
||||
strumpack::CSRMatrixMPI<double,int>* getA() const { return A_; }
|
||||
strumpack::CSRMatrixMPI<double, HYPRE_BigInt> *GetA() const { return A_; }
|
||||
|
||||
private:
|
||||
MPI_Comm comm_;
|
||||
strumpack::CSRMatrixMPI<double,int>* A_;
|
||||
|
||||
}; // mfem::STRUMPACKRowLocMatrix
|
||||
strumpack::CSRMatrixMPI<double, HYPRE_BigInt> *A_;
|
||||
};
|
||||
|
||||
/** The MFEM STRUMPACK Direct Solver class.
|
||||
|
||||
The mfem::STRUMPACKSolver class uses the STRUMPACK library to perform LU
|
||||
factorization of a parallel sparse matrix. The solver is capable of handling
|
||||
double precision types. See http://portal.nersc.gov/project/sparse/strumpack
|
||||
double precision types. See
|
||||
http://portal.nersc.gov/project/sparse/strumpack/.
|
||||
*/
|
||||
class STRUMPACKSolver : public mfem::Solver
|
||||
template <typename STRUMPACKSolverType>
|
||||
class STRUMPACKSolverBase : public Solver
|
||||
{
|
||||
protected:
|
||||
// Constructor with MPI_Comm parameter and command line arguments.
|
||||
STRUMPACKSolverBase(MPI_Comm comm, int argc, char *argv[]);
|
||||
|
||||
// Constructor with STRUMPACK matrix object and command line arguments.
|
||||
STRUMPACKSolverBase(STRUMPACKRowLocMatrix &A, int argc, char *argv[]);
|
||||
|
||||
public:
|
||||
// Constructor with MPI_Comm parameter.
|
||||
STRUMPACKSolver( int argc, char* argv[], MPI_Comm comm );
|
||||
|
||||
// Constructor with STRUMPACK Matrix Object.
|
||||
STRUMPACKSolver( STRUMPACKRowLocMatrix & A);
|
||||
|
||||
// Default destructor.
|
||||
~STRUMPACKSolver( void );
|
||||
virtual ~STRUMPACKSolverBase();
|
||||
|
||||
// Factor and solve the linear system y = Op^{-1} x.
|
||||
void Mult( const Vector & x, Vector & y ) const;
|
||||
void Mult(const Vector &x, Vector &y) const;
|
||||
void ArrayMult(const Array<const Vector *> &X, Array<Vector *> &Y) const;
|
||||
|
||||
// Set the operator.
|
||||
void SetOperator( const Operator & op );
|
||||
void SetOperator(const Operator &op);
|
||||
|
||||
// Set various solver options. Refer to STRUMPACK documentation for
|
||||
// details.
|
||||
void SetFromCommandLine( );
|
||||
void SetPrintFactorStatistics( bool print_stat );
|
||||
void SetPrintSolveStatistics( bool print_stat );
|
||||
void SetRelTol( double rtol );
|
||||
void SetAbsTol( double atol );
|
||||
void SetFromCommandLine();
|
||||
void SetPrintFactorStatistics(bool print_stat);
|
||||
void SetPrintSolveStatistics(bool print_stat);
|
||||
|
||||
// Set tolerances and iterations for iterative solvers. Compression
|
||||
// tolerance is handled below.
|
||||
void SetRelTol(double rtol);
|
||||
void SetAbsTol(double atol);
|
||||
void SetMaxIter(int max_it);
|
||||
|
||||
// Set the flag controlling reuse of the symbolic factorization for multiple
|
||||
// operators. This method has to be called before repeated calls to
|
||||
// SetOperator.
|
||||
void SetReorderingReuse(bool reuse);
|
||||
|
||||
// Enable or not GPU off-loading available if STRUMPACK was compiled with CUDA. Note
|
||||
// that input/output from MFEM to STRUMPACK is all still through host memory.
|
||||
void EnableGPU();
|
||||
void DisableGPU();
|
||||
|
||||
/**
|
||||
* STRUMPACK is an (approximate) direct solver. It can be used as a direct
|
||||
@@ -100,70 +119,153 @@ public:
|
||||
* used without preconditioner.
|
||||
*
|
||||
* Supported values are:
|
||||
* AUTO: Use iterative refinement if no HSS compression is used,
|
||||
* otherwise use GMRes.
|
||||
* DIRECT: No outer iterative solver, just a single application of
|
||||
* the multifrontal solver.
|
||||
* REFINE: Iterative refinement.
|
||||
* PREC_GMRES: Preconditioned GMRes.
|
||||
* The preconditioner is the (approx) multifrontal solver.
|
||||
* GMRES: UN-preconditioned GMRes. (for testing mainly)
|
||||
* PREC_BICGSTAB: Preconditioned BiCGStab.
|
||||
* The preconditioner is the (approx) multifrontal solver.
|
||||
* AUTO: Use iterative refinement if no HSS compression is
|
||||
* used, otherwise use GMRes
|
||||
* DIRECT: No outer iterative solver, just a single application
|
||||
* of the multifrontal solver
|
||||
* REFINE: Iterative refinement
|
||||
* PREC_GMRES: Preconditioned GMRes
|
||||
* The preconditioner is the (approx) multifrontal solver
|
||||
* GMRES: UN-preconditioned GMRes (for testing mainly)
|
||||
* PREC_BICGSTAB: Preconditioned BiCGStab
|
||||
* The preconditioner is the (approx) multifrontal solver
|
||||
* BICGSTAB: UN-preconditioned BiCGStab. (for testing mainly)
|
||||
*/
|
||||
void SetKrylovSolver( strumpack::KrylovSolver method );
|
||||
void SetKrylovSolver(strumpack::KrylovSolver method);
|
||||
|
||||
/**
|
||||
* Supported reorderings are:
|
||||
* METIS, PARMETIS, SCOTCH, PTSCOTCH, RCM
|
||||
* NATURAL: Do not reorder the system
|
||||
* METIS: Use Metis nested-dissection reordering (default)
|
||||
* PARMETIS: Use ParMetis nested-dissection reordering
|
||||
* SCOTCH: Use Scotch nested-dissection reordering
|
||||
* PTSCOTCH: Use PT-Scotch nested-dissection reordering
|
||||
* RCM: Use RCM reordering
|
||||
* GEOMETRIC: A simple geometric nested dissection code that
|
||||
* only works for regular meshes
|
||||
* AMD: Approximate minimum degree
|
||||
* MMD: Multiple minimum degree
|
||||
* AND: Nested dissection
|
||||
* MLF: Minimum local fill
|
||||
* SPECTRAL: Spectral nested dissection
|
||||
*/
|
||||
void SetReorderingStrategy( strumpack::ReorderingStrategy method );
|
||||
void SetReorderingStrategy(strumpack::ReorderingStrategy method);
|
||||
|
||||
/**
|
||||
* Disable static pivoting for stability. The static pivoting in strumpack
|
||||
* Configure static pivoting for stability. The static pivoting in STRUMPACK
|
||||
* permutes the sparse input matrix in order to get large (nonzero) elements
|
||||
* on the diagonal. If the input matrix is already diagonally dominant, this
|
||||
* reordering can be disabled.
|
||||
*
|
||||
* Supported matching algorithms are:
|
||||
* NONE: Don't do anything
|
||||
* MAX_CARDINALITY: Maximum cardinality
|
||||
* MAX_SMALLEST_DIAGONAL: Maximum smallest diagonal value
|
||||
* MAX_SMALLEST_DIAGONAL_2: Same as MAX_SMALLEST_DIAGONAL
|
||||
* but different algorithm
|
||||
* MAX_DIAGONAL_SUM: Maximum sum of diagonal values
|
||||
* MAX_DIAGONAL_PRODUCT_SCALING: Maximum product of diagonal values
|
||||
* and row and column scaling (default)
|
||||
* COMBBLAS: Use AWPM from CombBLAS (only with
|
||||
* version >= 3)
|
||||
*/
|
||||
void DisableMatching();
|
||||
void SetMatching(strumpack::MatchingJob job);
|
||||
|
||||
/**
|
||||
* Enable static pivoting for stability using the MC64 algorithm with
|
||||
* job=5. Using a matching algorithm, this will permute the sparse input
|
||||
* matrix in order to get nonzero elements (as large as possible) on the
|
||||
* diagonal. And will also scale the rows and columns of the matrix.
|
||||
* Enable support for rank-structured data formats, which can be used
|
||||
* for compression within the sparse solver.
|
||||
*
|
||||
* Supported compression types are:
|
||||
* NONE: No compression, purely direct solver (default)
|
||||
* HSS: HSS compression of frontal matrices
|
||||
* BLR: Block low-rank compression of fronts
|
||||
* HODLR: Hierarchically Off-diagonal Low-Rank
|
||||
* compression of frontal matrices
|
||||
* BLR_HODLR: Block low-rank compression of medium
|
||||
* fronts and Hierarchically Off-diagonal
|
||||
* Low-Rank compression of large fronts
|
||||
* ZFP_BLR_HODLR: ZFP compression for small fronts,
|
||||
* Block low-rank compression of medium
|
||||
* fronts and Hierarchically Off-diagonal
|
||||
* Low-Rank compression of large fronts
|
||||
* LOSSLESS: Lossless compression
|
||||
* LOSSY: Lossy compression
|
||||
*
|
||||
* For versions of STRUMPACK < 5, we support only NONE, HSS, and BLR.
|
||||
* BLR_HODLR and ZPR_BLR_HODLR are supported in STRUMPACK >= 6.
|
||||
*/
|
||||
void EnableMatching();
|
||||
|
||||
#if STRUMPACK_VERSION_MAJOR >= 3
|
||||
/**
|
||||
* Use the AWPM (approximate weight perfect matching) algorithm from the
|
||||
* Combinatorial BLAS library for static pivoting, i.e. getting large
|
||||
* nonzeros on the diagonal. This requires that strumpack was compiled with
|
||||
* support for Combinatorial BLAS.
|
||||
*/
|
||||
void EnableParallelMatching();
|
||||
void SetCompression(strumpack::CompressionType type);
|
||||
void SetCompressionRelTol(double rtol);
|
||||
void SetCompressionAbsTol(double atol);
|
||||
#if STRUMPACK_VERSION_MAJOR >= 5
|
||||
void SetCompressionLossyPrecision(int precision);
|
||||
void SetCompressionButterflyLevels(int levels);
|
||||
#endif
|
||||
|
||||
private:
|
||||
void Init( int argc, char* argv[] );
|
||||
// Helper method for calling the STRUMPACK factoriation routine.
|
||||
void FactorInternal() const;
|
||||
|
||||
protected:
|
||||
|
||||
MPI_Comm comm_;
|
||||
int numProcs_;
|
||||
int myid_;
|
||||
const STRUMPACKRowLocMatrix *APtr_;
|
||||
STRUMPACKSolverType *solver_;
|
||||
|
||||
bool factor_verbose_;
|
||||
bool solve_verbose_;
|
||||
bool reorder_reuse_;
|
||||
|
||||
const STRUMPACKRowLocMatrix * APtr_;
|
||||
strumpack::StrumpackSparseSolverMPIDist<double,int> * solver_;
|
||||
mutable Vector rhs_, sol_;
|
||||
mutable int nrhs_;
|
||||
};
|
||||
|
||||
}; // mfem::STRUMPACKSolver class
|
||||
class STRUMPACKSolver :
|
||||
public STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMPIDist<double, HYPRE_BigInt>>
|
||||
{
|
||||
public:
|
||||
// Constructor with MPI_Comm parameter.
|
||||
STRUMPACKSolver(MPI_Comm comm);
|
||||
|
||||
} // mfem namespace
|
||||
// Constructor with STRUMPACK matrix object.
|
||||
STRUMPACKSolver(STRUMPACKRowLocMatrix &A);
|
||||
|
||||
// Constructor with MPI_Comm parameter and command line arguments.
|
||||
STRUMPACKSolver(MPI_Comm comm, int argc, char *argv[]);
|
||||
MFEM_DEPRECATED STRUMPACKSolver(int argc, char *argv[], MPI_Comm comm)
|
||||
: STRUMPACKSolver(comm, argc, argv) {}
|
||||
|
||||
// Constructor with STRUMPACK matrix object and command line arguments.
|
||||
STRUMPACKSolver(STRUMPACKRowLocMatrix &A, int argc, char *argv[]);
|
||||
|
||||
// Destructor.
|
||||
~STRUMPACKSolver() {}
|
||||
};
|
||||
|
||||
#if STRUMPACK_VERSION_MAJOR >= 7
|
||||
class STRUMPACKMixedPrecisionSolver :
|
||||
public STRUMPACKSolverBase<strumpack::
|
||||
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
|
||||
{
|
||||
public:
|
||||
// Constructor with MPI_Comm parameter.
|
||||
STRUMPACKMixedPrecisionSolver(MPI_Comm comm);
|
||||
|
||||
// Constructor with STRUMPACK matrix object.
|
||||
STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A);
|
||||
|
||||
// Constructor with MPI_Comm parameter and command line arguments.
|
||||
STRUMPACKMixedPrecisionSolver(MPI_Comm comm, int argc, char *argv[]);
|
||||
|
||||
// Constructor with STRUMPACK matrix object and command line arguments.
|
||||
STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A,
|
||||
int argc, char *argv[]);
|
||||
|
||||
// Destructor.
|
||||
~STRUMPACKMixedPrecisionSolver() {}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
#endif // MFEM_USE_STRUMPACK
|
||||
|
||||
@@ -650,6 +650,7 @@ void SuperLUSolver::ArrayMult(const Array<const Vector *> &X,
|
||||
MFEM_ASSERT(X[i], "Missing Vector in SuperLUSolver::Mult!");
|
||||
Vector s(sol_, i * ldx, ldx);
|
||||
s = *X[i];
|
||||
sol_.SyncMemory(s); // Update flags for sol_ if updated on device
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
-5
@@ -35,10 +35,6 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** Count the number of entries in an array of doubles for which isfinite
|
||||
is false, i.e. the entry is a NaN or +/-Inf. */
|
||||
inline int CheckFinite(const double *v, const int n);
|
||||
|
||||
/// Define a shortcut for std::numeric_limits<double>::infinity()
|
||||
#ifndef __CYGWIN__
|
||||
inline double infinity()
|
||||
@@ -444,7 +440,7 @@ public:
|
||||
|
||||
/** @brief Count the number of entries in the Vector for which isfinite
|
||||
is false, i.e. the entry is a NaN or +/-Inf. */
|
||||
int CheckFinite() const { return mfem::CheckFinite(HostRead(), size); }
|
||||
int CheckFinite() const;
|
||||
|
||||
/// Destroys vector.
|
||||
virtual ~Vector();
|
||||
@@ -494,6 +490,8 @@ inline bool IsFinite(const double &val)
|
||||
#endif
|
||||
}
|
||||
|
||||
/** Count the number of entries in an array of doubles for which isfinite
|
||||
is false, i.e. the entry is a NaN or +/-Inf. */
|
||||
inline int CheckFinite(const double *v, const int n)
|
||||
{
|
||||
int bad = 0;
|
||||
@@ -504,6 +502,11 @@ inline int CheckFinite(const double *v, const int n)
|
||||
return bad;
|
||||
}
|
||||
|
||||
inline int Vector::CheckFinite() const
|
||||
{
|
||||
return mfem::CheckFinite(HostRead(), size);
|
||||
}
|
||||
|
||||
inline Vector::Vector(int s)
|
||||
{
|
||||
MFEM_ASSERT(s>=0,"Unexpected negative size.");
|
||||
|
||||
+196
-132
@@ -384,6 +384,12 @@ void Mesh::GetElementTransformation(int i, IsoparametricTransformation *ElTr)
|
||||
}
|
||||
}
|
||||
|
||||
ElementTransformation *Mesh::GetElementTransformation(int i)
|
||||
{
|
||||
GetElementTransformation(i, &Transformation);
|
||||
return &Transformation;
|
||||
}
|
||||
|
||||
void Mesh::GetElementTransformation(int i, const Vector &nodes,
|
||||
IsoparametricTransformation *ElTr)
|
||||
{
|
||||
@@ -428,19 +434,6 @@ void Mesh::GetElementTransformation(int i, const Vector &nodes,
|
||||
}
|
||||
}
|
||||
|
||||
ElementTransformation *Mesh::GetElementTransformation(int i)
|
||||
{
|
||||
GetElementTransformation(i, &Transformation);
|
||||
|
||||
return &Transformation;
|
||||
}
|
||||
|
||||
ElementTransformation *Mesh::GetBdrElementTransformation(int i)
|
||||
{
|
||||
GetBdrElementTransformation(i, &BdrTransformation);
|
||||
return &BdrTransformation;
|
||||
}
|
||||
|
||||
void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
|
||||
{
|
||||
ElTr->Attribute = GetBdrAttribute(i);
|
||||
@@ -501,6 +494,12 @@ void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
|
||||
}
|
||||
}
|
||||
|
||||
ElementTransformation *Mesh::GetBdrElementTransformation(int i)
|
||||
{
|
||||
GetBdrElementTransformation(i, &BdrTransformation);
|
||||
return &BdrTransformation;
|
||||
}
|
||||
|
||||
void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
|
||||
{
|
||||
FTr->Attribute = (Dim == 1) ? 1 : faces[FaceNo]->GetAttribute();
|
||||
@@ -1102,7 +1101,7 @@ void Mesh::ApplyLocalSlaveTransformation(FaceElementTransformations &FT,
|
||||
FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
|
||||
{
|
||||
FaceElementTransformations *tr;
|
||||
int fn = GetBdrFace(BdrElemNo);
|
||||
int fn = GetBdrElementFaceIndex(BdrElemNo);
|
||||
|
||||
// Check if the face is interior, shared, or nonconforming.
|
||||
if (FaceIsTrueInterior(fn) || faces_info[fn].NCFace >= 0)
|
||||
@@ -1117,24 +1116,6 @@ FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
|
||||
return tr;
|
||||
}
|
||||
|
||||
int Mesh::GetBdrFace(int BdrElemNo) const
|
||||
{
|
||||
int fn;
|
||||
if (Dim == 3)
|
||||
{
|
||||
fn = be_to_face[BdrElemNo];
|
||||
}
|
||||
else if (Dim == 2)
|
||||
{
|
||||
fn = be_to_edge[BdrElemNo];
|
||||
}
|
||||
else
|
||||
{
|
||||
fn = boundary[BdrElemNo]->GetVertices()[0];
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
|
||||
{
|
||||
FaceInformation face;
|
||||
@@ -1448,7 +1429,7 @@ Array<int> Mesh::GetFaceToBdrElMap() const
|
||||
face_to_be = -1;
|
||||
for (int i = 0; i < NumOfBdrElements; i++)
|
||||
{
|
||||
face_to_be[GetBdrElementEdgeIndex(i)] = i;
|
||||
face_to_be[GetBdrElementFaceIndex(i)] = i;
|
||||
}
|
||||
return face_to_be;
|
||||
}
|
||||
@@ -1539,7 +1520,6 @@ void Mesh::Destroy()
|
||||
faces.DeleteAll();
|
||||
faces_info.DeleteAll();
|
||||
nc_faces_info.DeleteAll();
|
||||
be_to_edge.DeleteAll();
|
||||
be_to_face.DeleteAll();
|
||||
|
||||
// TODO:
|
||||
@@ -1906,12 +1886,7 @@ int Mesh::AddBdrPoint(int v, int attr)
|
||||
|
||||
void Mesh::GenerateBoundaryElements()
|
||||
{
|
||||
int i, j;
|
||||
Array<int> &be2face = (Dim == 2) ? be_to_edge : be_to_face;
|
||||
|
||||
// GenerateFaces();
|
||||
|
||||
for (i = 0; i < boundary.Size(); i++)
|
||||
for (int i = 0; i < boundary.Size(); i++)
|
||||
{
|
||||
FreeElement(boundary[i]);
|
||||
}
|
||||
@@ -1924,22 +1899,24 @@ void Mesh::GenerateBoundaryElements()
|
||||
|
||||
// count the 'NumOfBdrElements'
|
||||
NumOfBdrElements = 0;
|
||||
for (i = 0; i < faces_info.Size(); i++)
|
||||
for (int i = 0; i < faces_info.Size(); i++)
|
||||
{
|
||||
if (faces_info[i].Elem2No < 0) { NumOfBdrElements++; }
|
||||
}
|
||||
|
||||
// Add the boundary elements
|
||||
boundary.SetSize(NumOfBdrElements);
|
||||
be2face.SetSize(NumOfBdrElements);
|
||||
for (j = i = 0; i < faces_info.Size(); i++)
|
||||
be_to_face.SetSize(NumOfBdrElements);
|
||||
for (int i = 0, j = 0; i < faces_info.Size(); i++)
|
||||
{
|
||||
if (faces_info[i].Elem2No < 0)
|
||||
{
|
||||
boundary[j] = faces[i]->Duplicate(this);
|
||||
be2face[j++] = i;
|
||||
be_to_face[j++] = i;
|
||||
}
|
||||
}
|
||||
// In 3D, 'bel_to_edge' is destroyed but it's not updated.
|
||||
|
||||
// Note: in 3D, 'bel_to_edge' is destroyed but it's not updated.
|
||||
}
|
||||
|
||||
void Mesh::FinalizeCheck()
|
||||
@@ -1969,7 +1946,7 @@ void Mesh::FinalizeTriMesh(int generate_edges, int refine, bool fix_orientation)
|
||||
if (generate_edges)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
GenerateFaces();
|
||||
CheckBdrElementOrientation();
|
||||
}
|
||||
@@ -1997,7 +1974,7 @@ void Mesh::FinalizeQuadMesh(int generate_edges, int refine,
|
||||
if (generate_edges)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
GenerateFaces();
|
||||
CheckBdrElementOrientation();
|
||||
}
|
||||
@@ -2299,8 +2276,7 @@ void Mesh::ReorderElements(const Array<int> &ordering, bool reorder_vertices)
|
||||
// - edge_vertex - no need to rebuild
|
||||
// - geom_factors - no need to rebuild
|
||||
|
||||
// - be_to_edge - 2D only
|
||||
// - be_to_face - 3D only
|
||||
// - be_to_face
|
||||
|
||||
// - Nodes
|
||||
|
||||
@@ -2386,9 +2362,9 @@ void Mesh::ReorderElements(const Array<int> &ordering, bool reorder_vertices)
|
||||
|
||||
if (Dim > 1)
|
||||
{
|
||||
// generate el_to_edge, be_to_edge (2D), bel_to_edge (3D)
|
||||
// generate el_to_edge, be_to_face (2D), bel_to_edge (3D)
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
if (Dim > 2)
|
||||
{
|
||||
@@ -2778,8 +2754,8 @@ void Mesh::DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
|
||||
}
|
||||
if (el_to_edge)
|
||||
{
|
||||
// update 'el_to_edge', 'be_to_edge' (2D), 'bel_to_edge' (3D)
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
// update 'el_to_edge', 'be_to_face' (2D), 'bel_to_edge' (3D)
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
if (Dim == 2)
|
||||
{
|
||||
// update 'faces' and 'faces_info'
|
||||
@@ -2856,7 +2832,7 @@ void Mesh::FinalizeTetMesh(int generate_edges, int refine, bool fix_orientation)
|
||||
if (generate_edges == 1)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2891,7 +2867,7 @@ void Mesh::FinalizeWedgeMesh(int generate_edges, int refine,
|
||||
if (generate_edges == 1)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2923,7 +2899,7 @@ void Mesh::FinalizeHexMesh(int generate_edges, int refine, bool fix_orientation)
|
||||
if (generate_edges)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3002,7 +2978,7 @@ void Mesh::FinalizeTopology(bool generate_bdr)
|
||||
{
|
||||
// el_to_edge may already be allocated (P2 VTK meshes)
|
||||
if (!el_to_edge) { el_to_edge = new Table; }
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
if (Dim == 2)
|
||||
{
|
||||
GenerateFaces(); // 'Faces' in 2D refers to the edges
|
||||
@@ -3022,8 +2998,17 @@ void Mesh::FinalizeTopology(bool generate_bdr)
|
||||
GenerateFaces();
|
||||
if (NumOfBdrElements == 0 && generate_bdr)
|
||||
{
|
||||
// be_to_face will be set inside GenerateBoundaryElements
|
||||
GenerateBoundaryElements();
|
||||
}
|
||||
else
|
||||
{
|
||||
be_to_face.SetSize(NumOfBdrElements);
|
||||
for (int i = 0; i < NumOfBdrElements; ++i)
|
||||
{
|
||||
be_to_face[i] = boundary[i]->GetVertices()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ncmesh)
|
||||
@@ -3555,7 +3540,7 @@ void Mesh::Make2D(int nx, int ny, Element::Type type,
|
||||
if (generate_edges == 1)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
GenerateFaces();
|
||||
CheckBdrElementOrientation();
|
||||
}
|
||||
@@ -3613,6 +3598,11 @@ void Mesh::Make1D(int n, double sx)
|
||||
SetMeshGen();
|
||||
GenerateFaces();
|
||||
|
||||
// Set be_to_face
|
||||
be_to_face.SetSize(2);
|
||||
be_to_face[0] = 0;
|
||||
be_to_face[1] = n;
|
||||
|
||||
attributes.Append(1);
|
||||
bdr_attributes.Append(1); bdr_attributes.Append(2);
|
||||
}
|
||||
@@ -3666,9 +3656,6 @@ Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
|
||||
// Copy the boundary-to-edge Table, bel_to_edge (3D)
|
||||
bel_to_edge = (mesh.bel_to_edge) ? new Table(*mesh.bel_to_edge) : NULL;
|
||||
|
||||
// Copy the boundary-to-edge Array, be_to_edge (2D)
|
||||
mesh.be_to_edge.Copy(be_to_edge);
|
||||
|
||||
// Duplicate the faces and faces_info.
|
||||
faces.SetSize(mesh.faces.Size());
|
||||
for (int i = 0; i < faces.Size(); i++)
|
||||
@@ -5269,7 +5256,7 @@ void Mesh::UpdateNURBS()
|
||||
|
||||
if (el_to_edge)
|
||||
{
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
|
||||
if (el_to_face)
|
||||
@@ -6169,15 +6156,15 @@ int Mesh::CheckBdrElementOrientation(bool fix_it)
|
||||
if (el_to_edge == NULL) // edges were not generated
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
GenerateFaces(); // 'Faces' in 2D refers to the edges
|
||||
}
|
||||
for (int i = 0; i < NumOfBdrElements; i++)
|
||||
{
|
||||
if (faces_info[be_to_edge[i]].Elem2No < 0) // boundary face
|
||||
if (faces_info[be_to_face[i]].Elem2No < 0) // boundary face
|
||||
{
|
||||
int *bv = boundary[i]->GetVertices();
|
||||
int *fv = faces[be_to_edge[i]]->GetVertices();
|
||||
int *fv = faces[be_to_face[i]]->GetVertices();
|
||||
if (bv[0] != fv[0])
|
||||
{
|
||||
if (fix_it)
|
||||
@@ -6271,6 +6258,111 @@ int Mesh::CheckBdrElementOrientation(bool fix_it)
|
||||
return wo;
|
||||
}
|
||||
|
||||
IntegrationPoint Mesh::TransformBdrElementToFace(Geometry::Type geom, int o,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
IntegrationPoint fip = ip;
|
||||
if (geom == Geometry::POINT)
|
||||
{
|
||||
return fip;
|
||||
}
|
||||
else if (geom == Geometry::SEGMENT)
|
||||
{
|
||||
MFEM_ASSERT(o >= 0 && o < 2, "Invalid orientation for Geometry::SEGMENT!");
|
||||
if (o == 0)
|
||||
{
|
||||
fip.x = ip.x;
|
||||
}
|
||||
else if (o == 1)
|
||||
{
|
||||
fip.x = 1.0 - ip.x;
|
||||
}
|
||||
}
|
||||
else if (geom == Geometry::TRIANGLE)
|
||||
{
|
||||
MFEM_ASSERT(o >= 0 && o < 6, "Invalid orientation for Geometry::TRIANGLE!");
|
||||
if (o == 0) // 0, 1, 2
|
||||
{
|
||||
fip.x = ip.x;
|
||||
fip.y = ip.y;
|
||||
}
|
||||
else if (o == 5) // 0, 2, 1
|
||||
{
|
||||
fip.x = ip.y;
|
||||
fip.y = ip.x;
|
||||
}
|
||||
else if (o == 2) // 1, 2, 0
|
||||
{
|
||||
fip.x = 1.0 - ip.x - ip.y;
|
||||
fip.y = ip.x;
|
||||
}
|
||||
else if (o == 1) // 1, 0, 2
|
||||
{
|
||||
fip.x = 1.0 - ip.x - ip.y;
|
||||
fip.y = ip.y;
|
||||
}
|
||||
else if (o == 4) // 2, 0, 1
|
||||
{
|
||||
fip.x = ip.y;
|
||||
fip.y = 1.0 - ip.x - ip.y;
|
||||
}
|
||||
else if (o == 3) // 2, 1, 0
|
||||
{
|
||||
fip.x = ip.x;
|
||||
fip.y = 1.0 - ip.x - ip.y;
|
||||
}
|
||||
}
|
||||
else if (geom == Geometry::SQUARE)
|
||||
{
|
||||
MFEM_ASSERT(o >= 0 && o < 8, "Invalid orientation for Geometry::SQUARE!");
|
||||
if (o == 0) // 0, 1, 2, 3
|
||||
{
|
||||
fip.x = ip.x;
|
||||
fip.y = ip.y;
|
||||
}
|
||||
else if (o == 1) // 0, 3, 2, 1
|
||||
{
|
||||
fip.x = ip.y;
|
||||
fip.y = ip.x;
|
||||
}
|
||||
else if (o == 2) // 1, 2, 3, 0
|
||||
{
|
||||
fip.x = ip.y;
|
||||
fip.y = 1.0 - ip.x;
|
||||
}
|
||||
else if (o == 3) // 1, 0, 3, 2
|
||||
{
|
||||
fip.x = 1.0 - ip.x;
|
||||
fip.y = ip.y;
|
||||
}
|
||||
else if (o == 4) // 2, 3, 0, 1
|
||||
{
|
||||
fip.x = 1.0 - ip.x;
|
||||
fip.y = 1.0 - ip.y;
|
||||
}
|
||||
else if (o == 5) // 2, 1, 0, 3
|
||||
{
|
||||
fip.x = 1.0 - ip.y;
|
||||
fip.y = 1.0 - ip.x;
|
||||
}
|
||||
else if (o == 6) // 3, 0, 1, 2
|
||||
{
|
||||
fip.x = 1.0 - ip.y;
|
||||
fip.y = ip.x;
|
||||
}
|
||||
else if (o == 7) // 3, 2, 1, 0
|
||||
{
|
||||
fip.x = ip.x;
|
||||
fip.y = 1.0 - ip.y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported face geometry for TransformBdrElementToFace!");
|
||||
}
|
||||
return fip;
|
||||
}
|
||||
|
||||
int Mesh::GetNumGeometries(int dim) const
|
||||
{
|
||||
MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
|
||||
@@ -6323,7 +6415,7 @@ void Mesh::GetBdrElementEdges(int i, Array<int> &edges, Array<int> &cor) const
|
||||
{
|
||||
edges.SetSize(1);
|
||||
cor.SetSize(1);
|
||||
edges[0] = be_to_edge[i];
|
||||
edges[0] = be_to_face[i];
|
||||
const int *v = boundary[i]->GetVertices();
|
||||
cor[0] = (v[0] < v[1]) ? (1) : (-1);
|
||||
}
|
||||
@@ -6564,42 +6656,26 @@ Array<int> Mesh::FindFaceNeighbors(const int elem) const
|
||||
|
||||
void Mesh::GetBdrElementFace(int i, int *f, int *o) const
|
||||
{
|
||||
const int *bv, *fv;
|
||||
*f = GetBdrElementFaceIndex(i);
|
||||
|
||||
*f = be_to_face[i];
|
||||
bv = boundary[i]->GetVertices();
|
||||
fv = faces[be_to_face[i]]->GetVertices();
|
||||
const int *fv = (Dim > 1) ? faces[*f]->GetVertices() : NULL;
|
||||
const int *bv = boundary[i]->GetVertices();
|
||||
|
||||
// find the orientation of the bdr. elem. w.r.t.
|
||||
// the corresponding face element (that's the base)
|
||||
switch (GetBdrElementType(i))
|
||||
switch (GetBdrElementGeometry(i))
|
||||
{
|
||||
case Element::TRIANGLE:
|
||||
*o = GetTriOrientation(fv, bv);
|
||||
break;
|
||||
case Element::QUADRILATERAL:
|
||||
*o = GetQuadOrientation(fv, bv);
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("invalid geometry");
|
||||
case Geometry::POINT: *o = 0; break;
|
||||
case Geometry::SEGMENT: *o = (fv[0] == bv[0]) ? 0 : 1; break;
|
||||
case Geometry::TRIANGLE: *o = GetTriOrientation(fv, bv); break;
|
||||
case Geometry::SQUARE: *o = GetQuadOrientation(fv, bv); break;
|
||||
default: MFEM_ABORT("invalid geometry");
|
||||
}
|
||||
}
|
||||
|
||||
int Mesh::GetBdrElementEdgeIndex(int i) const
|
||||
{
|
||||
switch (Dim)
|
||||
{
|
||||
case 1: return boundary[i]->GetVertices()[0];
|
||||
case 2: return be_to_edge[i];
|
||||
case 3: return be_to_face[i];
|
||||
default: MFEM_ABORT("invalid dimension!");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Mesh::GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
|
||||
{
|
||||
int fid = GetBdrElementEdgeIndex(bdr_el);
|
||||
int fid = GetBdrElementFaceIndex(bdr_el);
|
||||
|
||||
const FaceInfo &fi = faces_info[fid];
|
||||
MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
|
||||
@@ -6621,7 +6697,7 @@ void Mesh::GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
|
||||
|
||||
void Mesh::GetBdrElementAdjacentElement2(int bdr_el, int &el, int &info) const
|
||||
{
|
||||
int fid = GetBdrElementEdgeIndex(bdr_el);
|
||||
int fid = GetBdrElementFaceIndex(bdr_el);
|
||||
|
||||
const FaceInfo &fi = faces_info[fid];
|
||||
MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
|
||||
@@ -6747,7 +6823,7 @@ void Mesh::GetVertexToVertexTable(DSTable &v_to_v) const
|
||||
}
|
||||
}
|
||||
|
||||
int Mesh::GetElementToEdgeTable(Table & e_to_f, Array<int> &be_to_f)
|
||||
int Mesh::GetElementToEdgeTable(Table &e_to_f)
|
||||
{
|
||||
int i, NumberOfEdges;
|
||||
|
||||
@@ -6762,11 +6838,11 @@ int Mesh::GetElementToEdgeTable(Table & e_to_f, Array<int> &be_to_f)
|
||||
if (Dim == 2)
|
||||
{
|
||||
// Initialize the indices for the boundary elements.
|
||||
be_to_f.SetSize(NumOfBdrElements);
|
||||
be_to_face.SetSize(NumOfBdrElements);
|
||||
for (i = 0; i < NumOfBdrElements; i++)
|
||||
{
|
||||
const int *v = boundary[i]->GetVertices();
|
||||
be_to_f[i] = v_to_v(v[0], v[1]);
|
||||
be_to_face[i] = v_to_v(v[0], v[1]);
|
||||
}
|
||||
}
|
||||
else if (Dim == 3)
|
||||
@@ -7183,7 +7259,7 @@ STable3D *Mesh::GetFacesTable()
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MFEM_ABORT("Unexpected type of Element.");
|
||||
MFEM_ABORT("Unexpected type of Element: " << GetElementType(i));
|
||||
}
|
||||
}
|
||||
return faces_tbl;
|
||||
@@ -7369,7 +7445,7 @@ void Mesh::ReorientTetMesh()
|
||||
GenerateFaces();
|
||||
if (el_to_edge)
|
||||
{
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -8404,7 +8480,7 @@ void Mesh::UniformRefinement2D_base(bool update_nodes)
|
||||
if (el_to_edge == NULL)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
|
||||
int quad_counter = 0;
|
||||
@@ -8493,8 +8569,8 @@ void Mesh::UniformRefinement2D_base(bool update_nodes)
|
||||
const int attr = boundary[i]->GetAttribute();
|
||||
int *v = boundary[i]->GetVertices();
|
||||
|
||||
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);
|
||||
new_boundary[j++] = new Segment(v[0], oedge+be_to_face[i], attr);
|
||||
new_boundary[j++] = new Segment(oedge+be_to_face[i], v[1], attr);
|
||||
|
||||
FreeElement(boundary[i]);
|
||||
}
|
||||
@@ -8534,7 +8610,7 @@ void Mesh::UniformRefinement2D_base(bool update_nodes)
|
||||
NumOfBdrElements = 2 * NumOfBdrElements;
|
||||
NumOfFaces = 0;
|
||||
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
GenerateFaces();
|
||||
|
||||
last_operation = Mesh::REFINE;
|
||||
@@ -8564,7 +8640,7 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
if (el_to_edge == NULL)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
|
||||
if (el_to_face == NULL)
|
||||
@@ -9255,7 +9331,7 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
CheckBdrElementOrientation(false);
|
||||
#endif
|
||||
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
|
||||
last_operation = Mesh::REFINE;
|
||||
sequence++;
|
||||
@@ -9389,7 +9465,7 @@ void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
|
||||
|
||||
if (el_to_edge != NULL)
|
||||
{
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
GenerateFaces();
|
||||
}
|
||||
|
||||
@@ -9485,7 +9561,7 @@ void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
|
||||
// 5. Update element-to-edge and element-to-face relations.
|
||||
if (el_to_edge != NULL)
|
||||
{
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
if (el_to_face != NULL)
|
||||
{
|
||||
@@ -9676,7 +9752,7 @@ void Mesh::InitFromNCMesh(const NCMesh &ncmesh_)
|
||||
if (Dim > 1)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
if (Dim > 2)
|
||||
{
|
||||
@@ -9723,7 +9799,6 @@ void Mesh::Swap(Mesh& other, bool non_geometry)
|
||||
mfem::Swap(el_to_edge, other.el_to_edge);
|
||||
mfem::Swap(el_to_face, other.el_to_face);
|
||||
mfem::Swap(el_to_el, other.el_to_el);
|
||||
mfem::Swap(be_to_edge, other.be_to_edge);
|
||||
mfem::Swap(bel_to_edge, other.bel_to_edge);
|
||||
mfem::Swap(be_to_face, other.be_to_face);
|
||||
mfem::Swap(face_edge, other.face_edge);
|
||||
@@ -12178,9 +12253,9 @@ void Mesh::RemoveUnusedVertices()
|
||||
DeleteTables();
|
||||
if (Dim > 1)
|
||||
{
|
||||
// generate el_to_edge, be_to_edge (2D), bel_to_edge (3D)
|
||||
// generate el_to_edge, be_to_face (2D), bel_to_edge (3D)
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
if (Dim > 2)
|
||||
{
|
||||
@@ -12211,7 +12286,7 @@ void Mesh::RemoveInternalBoundaries()
|
||||
int new_bel_to_edge_nnz = 0;
|
||||
for (int i = 0; i < GetNBE(); i++)
|
||||
{
|
||||
if (FaceIsInterior(GetBdrElementEdgeIndex(i)))
|
||||
if (FaceIsInterior(GetBdrElementFaceIndex(i)))
|
||||
{
|
||||
FreeElement(boundary[i]);
|
||||
}
|
||||
@@ -12228,32 +12303,24 @@ void Mesh::RemoveInternalBoundaries()
|
||||
if (num_bdr_elem == GetNBE()) { return; }
|
||||
|
||||
Array<Element *> new_boundary(num_bdr_elem);
|
||||
Array<int> new_be_to_edge, new_be_to_face;
|
||||
Array<int> new_be_to_face;
|
||||
Table *new_bel_to_edge = NULL;
|
||||
new_boundary.SetSize(0);
|
||||
if (Dim == 2)
|
||||
new_be_to_face.Reserve(num_bdr_elem);
|
||||
if (Dim == 3)
|
||||
{
|
||||
new_be_to_edge.Reserve(num_bdr_elem);
|
||||
}
|
||||
else if (Dim == 3)
|
||||
{
|
||||
new_be_to_face.Reserve(num_bdr_elem);
|
||||
new_bel_to_edge = new Table;
|
||||
new_bel_to_edge->SetDims(num_bdr_elem, new_bel_to_edge_nnz);
|
||||
}
|
||||
for (int i = 0; i < GetNBE(); i++)
|
||||
{
|
||||
if (!FaceIsInterior(GetBdrElementEdgeIndex(i)))
|
||||
if (!FaceIsInterior(GetBdrElementFaceIndex(i)))
|
||||
{
|
||||
new_boundary.Append(boundary[i]);
|
||||
if (Dim == 2)
|
||||
int row = new_be_to_face.Size();
|
||||
new_be_to_face.Append(be_to_face[i]);
|
||||
if (Dim == 3)
|
||||
{
|
||||
new_be_to_edge.Append(be_to_edge[i]);
|
||||
}
|
||||
else if (Dim == 3)
|
||||
{
|
||||
int row = new_be_to_face.Size();
|
||||
new_be_to_face.Append(be_to_face[i]);
|
||||
int *e = bel_to_edge->GetRow(i);
|
||||
int ne = bel_to_edge->RowSize(i);
|
||||
int *new_e = new_bel_to_edge->GetRow(row);
|
||||
@@ -12269,13 +12336,10 @@ void Mesh::RemoveInternalBoundaries()
|
||||
NumOfBdrElements = new_boundary.Size();
|
||||
mfem::Swap(boundary, new_boundary);
|
||||
|
||||
if (Dim == 2)
|
||||
mfem::Swap(be_to_face, new_be_to_face);
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
mfem::Swap(be_to_edge, new_be_to_edge);
|
||||
}
|
||||
else if (Dim == 3)
|
||||
{
|
||||
mfem::Swap(be_to_face, new_be_to_face);
|
||||
delete bel_to_edge;
|
||||
bel_to_edge = new_bel_to_edge;
|
||||
}
|
||||
|
||||
+60
-12
@@ -220,9 +220,9 @@ protected:
|
||||
Table *el_to_edge;
|
||||
Table *el_to_face;
|
||||
Table *el_to_el;
|
||||
Array<int> be_to_edge; // for 2D
|
||||
Table *bel_to_edge; // for 3D
|
||||
Array<int> be_to_face;
|
||||
Array<int> be_to_face; // faces = vertices (1D), edges (2D), faces (3D)
|
||||
|
||||
Table *bel_to_edge; // for 3D only
|
||||
|
||||
// Note that the following tables are owned by this class and should not be
|
||||
// deleted by the caller. Of these three tables, only face_edge and
|
||||
@@ -322,9 +322,38 @@ protected:
|
||||
void ReadNURBSMesh(std::istream &input, int &curved, int &read_gf);
|
||||
void ReadInlineMesh(std::istream &input, bool generate_edges = false);
|
||||
void ReadGmshMesh(std::istream &input, int &curved, int &read_gf);
|
||||
|
||||
/* Note NetCDF (optional library) is used for reading cubit files */
|
||||
#ifdef MFEM_USE_NETCDF
|
||||
|
||||
/// @brief Load a mesh from a Genesis file.
|
||||
void ReadCubit(const char *filename, int &curved, int &read_gf);
|
||||
|
||||
/// @brief The final step in constructing the mesh from a Genesis file. This
|
||||
/// is only called if the mesh order == 2 (determined internally from the
|
||||
/// cubit element type).
|
||||
void FinalizeCubitSecondOrderMesh(const int cubit_element_type,
|
||||
const int num_element_blocks,
|
||||
const int num_nodes_per_element,
|
||||
const int *start_of_block,
|
||||
const double *coordx,
|
||||
const double *coordy,
|
||||
const double *coordz,
|
||||
const int **element_blocks);
|
||||
|
||||
/// @brief Returns a pointer to a new mfem::Element based on the provided
|
||||
/// cubit element type. This is used internally to create the mesh elements
|
||||
/// from a Genesis file.
|
||||
Element *CreateCubitElement(const int cubit_element_type,
|
||||
const int *vertex_ids,
|
||||
const int block_id);
|
||||
|
||||
/// @brief Returns a pointer to a new mfem::Element based on the provided
|
||||
/// cubit face type. This is used internally to create the boundary elements
|
||||
/// from a Genesis file.
|
||||
Element *CreateCubitBoundaryElement(const int cubit_face_type,
|
||||
const int *vertex_ids,
|
||||
const int sideset_id) const;
|
||||
#endif
|
||||
|
||||
/// Determine the mesh generator bitmask #meshgen, see MeshGenerator().
|
||||
@@ -504,7 +533,7 @@ protected:
|
||||
nodes in the elements. For example, if T is the element to edge table
|
||||
T(i, 0) gives the index of edge in element i that connects vertex 0
|
||||
to vertex 1, etc. Returns the number of the edges. */
|
||||
int GetElementToEdgeTable(Table &, Array<int> &);
|
||||
int GetElementToEdgeTable(Table &);
|
||||
|
||||
/// Used in GenerateFaces()
|
||||
void AddPointFaceElement(int lf, int gf, int el);
|
||||
@@ -1330,13 +1359,13 @@ public:
|
||||
element @a elem, including @a elem. */
|
||||
Array<int> FindFaceNeighbors(const int elem) const;
|
||||
|
||||
/// Return the index and the orientation of the face of bdr element i. (3D)
|
||||
void GetBdrElementFace(int i, int *f, int *o) const;
|
||||
/** Return the index and the orientation of the vertex of bdr element i. (1D)
|
||||
Return the index and the orientation of the edge of bdr element i. (2D)
|
||||
Return the index and the orientation of the face of bdr element i. (3D)
|
||||
|
||||
/** Return the vertex index of boundary element i. (1D)
|
||||
Return the edge index of boundary element i. (2D)
|
||||
Return the face index of boundary element i. (3D) */
|
||||
int GetBdrElementEdgeIndex(int i) const;
|
||||
In 2D, the returned edge orientation is 0 or 1, not +/-1 as returned by
|
||||
GetElementEdges/GetBdrElementEdges. */
|
||||
void GetBdrElementFace(int i, int *f, int *o) const;
|
||||
|
||||
/** @brief For the given boundary element, bdr_el, return its adjacent
|
||||
element and its info, i.e. 64*local_bdr_index+bdr_orientation.
|
||||
@@ -1358,8 +1387,19 @@ public:
|
||||
@sa GetBdrElementAdjacentElement() */
|
||||
void GetBdrElementAdjacentElement2(int bdr_el, int &el, int &info) const;
|
||||
|
||||
/// Return the local face index for the given boundary face.
|
||||
int GetBdrFace(int BdrElemNo) const;
|
||||
/// @brief Return the local face (codimension-1) index for the given boundary
|
||||
/// element index.
|
||||
int GetBdrElementFaceIndex(int be_idx) const { return be_to_face[be_idx]; }
|
||||
|
||||
/// Deprecated in favor of GetBdrElementFaceIndex().
|
||||
MFEM_DEPRECATED int GetBdrFace(int i) const { return GetBdrElementFaceIndex(i); }
|
||||
|
||||
/** Return the vertex index of boundary element i. (1D)
|
||||
Return the edge index of boundary element i. (2D)
|
||||
Return the face index of boundary element i. (3D)
|
||||
|
||||
Deprecated in favor of GetBdrElementFaceIndex(). */
|
||||
MFEM_DEPRECATED int GetBdrElementEdgeIndex(int i) const { return GetBdrElementFaceIndex(i); }
|
||||
|
||||
/// @}
|
||||
|
||||
@@ -1409,6 +1449,14 @@ public:
|
||||
/// should not be deleted by the caller.
|
||||
static FiniteElement *GetTransformationFEforElementType(Element::Type);
|
||||
|
||||
/** @brief For the vertex (1D), edge (2D), or face (3D) of a boundary element
|
||||
with the orientation @a o, return the transformation of the boundary
|
||||
element integration point @ ip to the face element. In 2D, the
|
||||
the orientation is 0 or 1 as returned by GetBdrElementFace, not +/-1.
|
||||
Supports both internal and external boundaries. */
|
||||
static IntegrationPoint TransformBdrElementToFace(Geometry::Type geom, int o,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
/// @anchor mfem_Mesh_elem_trans
|
||||
/// @name Access the coordinate transformation for individual elements
|
||||
///
|
||||
|
||||
+968
-584
File diff suppressed because it is too large
Load Diff
+80
-71
@@ -3021,13 +3021,13 @@ void NCMesh::TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
|
||||
if (nd.HasEdge())
|
||||
{
|
||||
// check if the edge is already a master in 'edge_list'
|
||||
int type;
|
||||
const MeshId &eid = edge_list.LookUp(nd.edge_index, &type);
|
||||
if (type == 1)
|
||||
const auto eid_and_type = edge_list.GetMeshIdAndType(nd.edge_index);
|
||||
if (eid_and_type.type == NCList::MeshIdType::MASTER
|
||||
|| eid_and_type.type == NCList::MeshIdType::CONFORMING)
|
||||
{
|
||||
// in this case we need to add an edge-face constraint, because the
|
||||
// master edge is really a (face-)slave itself
|
||||
|
||||
// non-slave edge is really a (face-)slave itself.
|
||||
const MeshId &eid = *eid_and_type.id;
|
||||
face_list.slaves.Append(
|
||||
Slave(-1 - eid.index, eid.element, eid.local, Geometry::TRIANGLE));
|
||||
|
||||
@@ -3048,9 +3048,10 @@ void NCMesh::TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
|
||||
TraverseTetEdge(mid, vn1, pmid, p1, matrix_map);
|
||||
}
|
||||
|
||||
bool NCMesh::TraverseTriFace(int vn0, int vn1, int vn2,
|
||||
const PointMatrix& pm, int level,
|
||||
MatrixMap &matrix_map)
|
||||
NCMesh::TriFaceTraverseResults NCMesh::TraverseTriFace(int vn0, int vn1,
|
||||
int vn2,
|
||||
const PointMatrix& pm, int level,
|
||||
MatrixMap &matrix_map)
|
||||
{
|
||||
if (level > 0)
|
||||
{
|
||||
@@ -3069,7 +3070,7 @@ bool NCMesh::TraverseTriFace(int vn0, int vn1, int vn2,
|
||||
sl.local = ReorderFacePointMat(vn0, vn1, vn2, -1, elem, pm, pm_r);
|
||||
sl.matrix = matrix_map.GetIndex(pm_r);
|
||||
|
||||
return true;
|
||||
return {true, elements[elem].rank != MyRank};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3077,7 +3078,7 @@ bool NCMesh::TraverseTriFace(int vn0, int vn1, int vn2,
|
||||
if (TriFaceSplit(vn0, vn1, vn2, mid))
|
||||
{
|
||||
Point pmid0(pm(0), pm(1)), pmid1(pm(1), pm(2)), pmid2(pm(2), pm(0));
|
||||
bool b[4];
|
||||
TriFaceTraverseResults b[4];
|
||||
|
||||
b[0] = TraverseTriFace(vn0, mid[0], mid[2],
|
||||
PointMatrix(pm(0), pmid0, pmid2),
|
||||
@@ -3095,16 +3096,21 @@ bool NCMesh::TraverseTriFace(int vn0, int vn1, int vn2,
|
||||
PointMatrix(pmid1, pmid2, pmid0),
|
||||
level+1, matrix_map);
|
||||
|
||||
// traverse possible tet edges constrained by the master face
|
||||
if (HaveTets() && !b[3])
|
||||
// Traverse possible tet edges constrained by the master face. This needs to occur if
|
||||
// none of these first NC level faces are split further, OR if they are on different
|
||||
// processors. The different processor constraint is needed in the case of local
|
||||
// elements constrained by this face via the edge alone. Cannot know this a priori, so
|
||||
// just constrain any edge attached to two neighbors.
|
||||
if (HaveTets() && (!b[3].unsplit || b[3].ghost_neighbor))
|
||||
{
|
||||
if (!b[1]) { TraverseTetEdge(mid[0],mid[1], pmid0,pmid1, matrix_map); }
|
||||
if (!b[2]) { TraverseTetEdge(mid[1],mid[2], pmid1,pmid2, matrix_map); }
|
||||
if (!b[0]) { TraverseTetEdge(mid[2],mid[0], pmid2,pmid0, matrix_map); }
|
||||
// If the faces have no further splits, so would not be captured by normal face
|
||||
// relations, add possible edge constraints.
|
||||
if (!b[1].unsplit || b[1].ghost_neighbor) { TraverseTetEdge(mid[0],mid[1], pmid0,pmid1, matrix_map); }
|
||||
if (!b[2].unsplit || b[2].ghost_neighbor) { TraverseTetEdge(mid[1],mid[2], pmid1,pmid2, matrix_map); }
|
||||
if (!b[0].unsplit || b[0].ghost_neighbor) { TraverseTetEdge(mid[2],mid[0], pmid2,pmid0, matrix_map); }
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return {false, false};
|
||||
}
|
||||
|
||||
void NCMesh::BuildFaceList()
|
||||
@@ -3402,77 +3408,80 @@ void NCMesh::NCList::Clear()
|
||||
point_matrices[i].DeleteAll();
|
||||
}
|
||||
|
||||
inv_index.DeleteAll();
|
||||
inv_index.clear();
|
||||
}
|
||||
|
||||
long NCMesh::NCList::TotalSize() const
|
||||
NCMesh::NCList::MeshIdAndType
|
||||
NCMesh::NCList::GetMeshIdAndType(int index) const
|
||||
{
|
||||
return conforming.Size() + masters.Size() + slaves.Size();
|
||||
}
|
||||
|
||||
const NCMesh::MeshId& NCMesh::NCList::LookUp(int index, int *type) const
|
||||
{
|
||||
if (!inv_index.Size())
|
||||
BuildIndex();
|
||||
const auto it = inv_index.find(index);
|
||||
auto ft = it != inv_index.end() ? it->second.first : MeshIdType::UNRECOGNIZED;
|
||||
switch (ft)
|
||||
{
|
||||
int max_index = -1;
|
||||
case MeshIdType::CONFORMING:
|
||||
return {&conforming[it->second.second], it->second.first};
|
||||
case MeshIdType::MASTER:
|
||||
return {&masters[it->second.second], it->second.first};
|
||||
case MeshIdType::SLAVE:
|
||||
return {&slaves[it->second.second], it->second.first};
|
||||
case MeshIdType::UNRECOGNIZED:
|
||||
default:
|
||||
return {nullptr, MeshIdType::UNRECOGNIZED};
|
||||
}
|
||||
}
|
||||
|
||||
NCMesh::NCList::MeshIdType
|
||||
NCMesh::NCList::GetMeshIdType(int index) const
|
||||
{
|
||||
BuildIndex();
|
||||
auto it = inv_index.find(index);
|
||||
return (it != inv_index.end()) ? it->second.first : MeshIdType::UNRECOGNIZED;
|
||||
}
|
||||
|
||||
bool
|
||||
NCMesh::NCList::CheckMeshIdType(int index, MeshIdType ft) const
|
||||
{
|
||||
return GetMeshIdType(index) == ft;
|
||||
}
|
||||
|
||||
void
|
||||
NCMesh::NCList::BuildIndex() const
|
||||
{
|
||||
if (inv_index.size() == 0)
|
||||
{
|
||||
auto index_compare = [](const MeshId &a, const MeshId &b) { return a.index < b.index; };
|
||||
auto max_conforming = std::max_element(conforming.begin(), conforming.end(),
|
||||
index_compare);
|
||||
auto max_master = std::max_element(masters.begin(), masters.end(),
|
||||
index_compare);
|
||||
auto max_slave = std::max_element(slaves.begin(), slaves.end(), index_compare);
|
||||
|
||||
int max_conforming_index = max_conforming != nullptr ? max_conforming->index :
|
||||
-1;
|
||||
int max_master_index = max_master != nullptr ? max_master->index : -1;
|
||||
int max_slave_index = max_slave != nullptr ? max_slave->index : -1;
|
||||
|
||||
inv_index.reserve(std::max({max_conforming_index, max_master_index, max_slave_index}));
|
||||
for (int i = 0; i < conforming.Size(); i++)
|
||||
{
|
||||
max_index = std::max(conforming[i].index, max_index);
|
||||
inv_index.emplace(conforming[i].index, std::make_pair(MeshIdType::CONFORMING,
|
||||
i));
|
||||
}
|
||||
for (int i = 0; i < masters.Size(); i++)
|
||||
{
|
||||
max_index = std::max(masters[i].index, max_index);
|
||||
inv_index.emplace(masters[i].index, std::make_pair(MeshIdType::MASTER, i));
|
||||
}
|
||||
for (int i = 0; i < slaves.Size(); i++)
|
||||
{
|
||||
if (slaves[i].index < 0) { continue; }
|
||||
max_index = std::max(slaves[i].index, max_index);
|
||||
}
|
||||
|
||||
inv_index.SetSize(max_index + 1);
|
||||
inv_index = -1;
|
||||
|
||||
for (int i = 0; i < conforming.Size(); i++)
|
||||
{
|
||||
inv_index[conforming[i].index] = (i << 2);
|
||||
}
|
||||
for (int i = 0; i < masters.Size(); i++)
|
||||
{
|
||||
inv_index[masters[i].index] = (i << 2) + 1;
|
||||
}
|
||||
for (int i = 0; i < slaves.Size(); i++)
|
||||
{
|
||||
if (slaves[i].index < 0) { continue; }
|
||||
inv_index[slaves[i].index] = (i << 2) + 2;
|
||||
inv_index.emplace(slaves[i].index, std::make_pair(MeshIdType::SLAVE, i));
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_ASSERT(index >= 0 && index < inv_index.Size(), "");
|
||||
int key = inv_index[index];
|
||||
|
||||
if (!type)
|
||||
{
|
||||
MFEM_VERIFY(key >= 0, "index " << index << " not found.");
|
||||
}
|
||||
else // return entity type if requested, don't abort when not found
|
||||
{
|
||||
*type = (key >= 0) ? (key & 0x3) : -1;
|
||||
|
||||
static MeshId invalid;
|
||||
if (*type < 0) { return invalid; } // not found
|
||||
}
|
||||
|
||||
// return found entity MeshId
|
||||
switch (key & 0x3)
|
||||
{
|
||||
case 0: return conforming[key >> 2];
|
||||
case 1: return masters[key >> 2];
|
||||
case 2: return slaves[key >> 2];
|
||||
default: MFEM_ABORT("internal error"); return conforming[0];
|
||||
}
|
||||
MFEM_ASSERT(inv_index.size() > 0,
|
||||
"Empty inverse index, member lists must be populated before BuildIndex is called!");
|
||||
}
|
||||
|
||||
|
||||
//// Neighbors /////////////////////////////////////////////////////////////////
|
||||
|
||||
void NCMesh::CollectEdgeVertices(int v0, int v1, Array<int> &indices)
|
||||
|
||||
+62
-13
@@ -24,6 +24,7 @@
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -223,30 +224,73 @@ public:
|
||||
, master(-1), matrix(0), edge_flags(0) {}
|
||||
};
|
||||
|
||||
|
||||
/// Lists all edges/faces in the nonconforming mesh.
|
||||
struct NCList
|
||||
{
|
||||
Array<MeshId> conforming;
|
||||
Array<Master> masters;
|
||||
Array<Slave> slaves;
|
||||
Array<MeshId> conforming; ///< All MeshIds corresponding to conformal faces
|
||||
Array<Master> masters; ///< All MeshIds corresponding to master faces
|
||||
Array<Slave> slaves; ///< All MeshIds corresponding to slave faces
|
||||
|
||||
/// List of unique point matrices for each slave geometry.
|
||||
Array<DenseMatrix*> point_matrices[Geometry::NumGeom];
|
||||
|
||||
/// Return the point matrix oriented according to the master and slave edges
|
||||
void OrientedPointMatrix(const Slave &slave,
|
||||
DenseMatrix &oriented_matrix) const;
|
||||
|
||||
/// Particular MeshId type, used for allowing static casting to the
|
||||
/// appropriate child type after searching the NCList. UNRECOGNIZED
|
||||
/// denotes that an instance is not known within the NCList, meaning that
|
||||
/// it does not play a part in NC mechanics. This can be because the index
|
||||
/// did not exist in the original Mesh, or because the entry is a boundary
|
||||
/// face, whose NC status is always conforming.
|
||||
enum class MeshIdType : char {CONFORMING, MASTER, SLAVE, UNRECOGNIZED};
|
||||
|
||||
/// Helper storing a reference to a MeshId type, and the face type it can
|
||||
/// be cast to
|
||||
struct MeshIdAndType
|
||||
{
|
||||
const MeshId * const id; ///< Pointer to a possible MeshId, nullptr if not found
|
||||
/// MeshIdType corresponding to the MeshId. UNRECOGNIZED if unfound.
|
||||
const MeshIdType type;
|
||||
};
|
||||
/// Return a mesh id and type for a given nc index.
|
||||
MeshIdAndType GetMeshIdAndType(int index) const;
|
||||
|
||||
/// Return a face type for a given nc index.
|
||||
MeshIdType GetMeshIdType(int index) const;
|
||||
|
||||
/// Given an index, check if this is a certain face type.
|
||||
bool CheckMeshIdType(int index, MeshIdType type) const;
|
||||
|
||||
/// Erase the contents of the conforming, master and slave arrays.
|
||||
void Clear();
|
||||
bool Empty() const { return !conforming.Size() && !masters.Size(); }
|
||||
long TotalSize() const;
|
||||
/// Whether the NCList is empty.
|
||||
bool Empty() const
|
||||
{
|
||||
return conforming.Size() == 0
|
||||
&& masters.Size() == 0
|
||||
&& slaves.Size() == 0;
|
||||
}
|
||||
/// The total size of the component arrays in the NCList.
|
||||
long TotalSize() const
|
||||
{
|
||||
return conforming.Size() + masters.Size() + slaves.Size();
|
||||
}
|
||||
/// The memory usage of the three public arrays. Does not account for the
|
||||
/// inverse index.
|
||||
long MemoryUsage() const;
|
||||
|
||||
const MeshId& LookUp(int index, int *type = NULL) const;
|
||||
|
||||
~NCList() { Clear(); }
|
||||
private:
|
||||
mutable Array<int> inv_index;
|
||||
// Check for existence or construct the inv_index list map if necessary.
|
||||
// const because only modifies the mutable member inv_index.
|
||||
void BuildIndex() const;
|
||||
|
||||
/// A lazily constructed map from index to MeshId. Built whenever
|
||||
/// GetMeshIdAndType, GetMeshIdType or CheckMeshIdType is called for the
|
||||
/// first time. The MeshIdType is stored with, to enable casting to Slave
|
||||
/// or Master elements appropriately.
|
||||
mutable std::unordered_map<int, std::pair<MeshIdType, int>> inv_index;
|
||||
};
|
||||
|
||||
/// Return the current list of conforming and nonconforming faces.
|
||||
@@ -727,9 +771,14 @@ protected: // implementation
|
||||
void TraverseQuadFace(int vn0, int vn1, int vn2, int vn3,
|
||||
const PointMatrix& pm, int level, Face* eface[4],
|
||||
MatrixMap &matrix_map);
|
||||
bool TraverseTriFace(int vn0, int vn1, int vn2,
|
||||
const PointMatrix& pm, int level,
|
||||
MatrixMap &matrix_map);
|
||||
struct TriFaceTraverseResults
|
||||
{
|
||||
bool unsplit; ///< Whether this face has no further splits.
|
||||
bool ghost_neighbor; ///< Whether the face neighbor is a ghost.
|
||||
};
|
||||
TriFaceTraverseResults TraverseTriFace(int vn0, int vn1, int vn2,
|
||||
const PointMatrix& pm, int level,
|
||||
MatrixMap &matrix_map);
|
||||
void TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
|
||||
MatrixMap &matrix_map);
|
||||
void TraverseEdge(int vn0, int vn1, double t0, double t1, int flags,
|
||||
|
||||
+15
-6
@@ -39,6 +39,7 @@ KnotVector::KnotVector(int Order_, int NCP)
|
||||
Order = Order_;
|
||||
NumOfControlPoints = NCP;
|
||||
knot.SetSize(NumOfControlPoints + Order + 1);
|
||||
NumOfElements = 0;
|
||||
|
||||
knot = -1.;
|
||||
}
|
||||
@@ -129,27 +130,35 @@ void KnotVector::Print(std::ostream &os) const
|
||||
knot.Print(os, knot.Size());
|
||||
}
|
||||
|
||||
|
||||
void KnotVector::PrintFunctions(std::ostream &os, int samples) const
|
||||
{
|
||||
MFEM_VERIFY(GetNE(), "Elements not counted. Use GetElements().");
|
||||
|
||||
Vector shape(Order+1);
|
||||
|
||||
double x, dx = 1.0/double (samples - 1);
|
||||
|
||||
for (int i = 0; i <GetNE() ; i++)
|
||||
/* @a cnt is a counter including elements between repeated knots if
|
||||
present. This is required for usage of CalcShape. */
|
||||
int cnt = 0;
|
||||
|
||||
for (int e = 0; e < GetNE(); e++, cnt++)
|
||||
{
|
||||
// Avoid printing shapes between repeated knots
|
||||
if (!isElement(cnt)) { e--; continue; }
|
||||
|
||||
for (int j = 0; j <samples; j++)
|
||||
{
|
||||
x =j*dx;
|
||||
os<< x + i;
|
||||
os<< x + e;
|
||||
|
||||
CalcShape ( shape, i, x);
|
||||
CalcShape ( shape, cnt, x);
|
||||
for (int d = 0; d < Order+1; d++) { os<<"\t"<<shape[d]; }
|
||||
|
||||
CalcDShape ( shape, i, x);
|
||||
CalcDShape ( shape, cnt, x);
|
||||
for (int d = 0; d < Order+1; d++) { os<<"\t"<<shape[d]; }
|
||||
|
||||
CalcD2Shape ( shape, i, x);
|
||||
CalcD2Shape ( shape, cnt, x);
|
||||
for (int d = 0; d < Order+1; d++) { os<<"\t"<<shape[d]; }
|
||||
os<<endl;
|
||||
}
|
||||
|
||||
@@ -88,6 +88,10 @@ public:
|
||||
|
||||
void Print(std::ostream &out) const;
|
||||
|
||||
/** Prints the non-zero shape functions and their first and second
|
||||
derivatives associated with the KnotVector per element. Use GetElements()
|
||||
to count the elements before using this function. @a samples is the
|
||||
number of samples of the shape functions per element.*/
|
||||
void PrintFunctions(std::ostream &out, int samples=11) const;
|
||||
|
||||
/// Destroys KnotVector
|
||||
|
||||
+137
-214
@@ -34,7 +34,6 @@ ParMesh::ParMesh(const ParMesh &pmesh, bool copy_nodes)
|
||||
group_sedge(pmesh.group_sedge),
|
||||
group_stria(pmesh.group_stria),
|
||||
group_squad(pmesh.group_squad),
|
||||
face_nbr_el_to_face(NULL),
|
||||
glob_elem_offset(-1),
|
||||
glob_offset_sequence(-1),
|
||||
gtopo(pmesh.gtopo)
|
||||
@@ -106,8 +105,7 @@ ParMesh& ParMesh::operator=(ParMesh &&mesh)
|
||||
|
||||
ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
int part_method)
|
||||
: face_nbr_el_to_face(NULL)
|
||||
, glob_elem_offset(-1)
|
||||
: glob_elem_offset(-1)
|
||||
, glob_offset_sequence(-1)
|
||||
, gtopo(comm)
|
||||
{
|
||||
@@ -188,7 +186,7 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
if (Dim > 1)
|
||||
{
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = Mesh::GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = Mesh::GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
|
||||
STable3D *faces_tbl = NULL;
|
||||
@@ -199,6 +197,19 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
|
||||
|
||||
GenerateFaces();
|
||||
|
||||
// Make sure the be_to_face array is initialized.
|
||||
// In 2D, it will be set in the above call to Mesh::GetElementToEdgeTable.
|
||||
// In 3D, it will be set in GetElementToFaceTable.
|
||||
// In 1D, we need to set it manually.
|
||||
if (Dim == 1)
|
||||
{
|
||||
be_to_face.SetSize(NumOfBdrElements);
|
||||
for (int i = 0; i < NumOfBdrElements; ++i)
|
||||
{
|
||||
be_to_face[i] = boundary[i]->GetVertices()[0];
|
||||
}
|
||||
}
|
||||
|
||||
ListOfIntegerSets groups;
|
||||
{
|
||||
// the first group is the local one
|
||||
@@ -437,7 +448,7 @@ int ParMesh::BuildLocalBoundary(const Mesh& mesh, const int* partitioning,
|
||||
|
||||
for (int i = 0; i < mesh.GetNBE(); i++)
|
||||
{
|
||||
int edge = mesh.GetBdrElementEdgeIndex(i);
|
||||
int edge = mesh.GetBdrElementFaceIndex(i);
|
||||
int el1 = edge_element->GetRow(edge)[0];
|
||||
if (partitioning[el1] == MyRank)
|
||||
{
|
||||
@@ -453,7 +464,7 @@ int ParMesh::BuildLocalBoundary(const Mesh& mesh, const int* partitioning,
|
||||
boundary.SetSize(nbdry);
|
||||
for (int i = 0; i < mesh.GetNBE(); i++)
|
||||
{
|
||||
int edge = mesh.GetBdrElementEdgeIndex(i);
|
||||
int edge = mesh.GetBdrElementFaceIndex(i);
|
||||
int el1 = edge_element->GetRow(edge)[0];
|
||||
if (partitioning[el1] == MyRank)
|
||||
{
|
||||
@@ -854,7 +865,6 @@ ParMesh::ParMesh(const ParNCMesh &pncmesh)
|
||||
: MyComm(pncmesh.MyComm)
|
||||
, NRanks(pncmesh.NRanks)
|
||||
, MyRank(pncmesh.MyRank)
|
||||
, face_nbr_el_to_face(NULL)
|
||||
, glob_elem_offset(-1)
|
||||
, glob_offset_sequence(-1)
|
||||
, gtopo(MyComm)
|
||||
@@ -906,7 +916,7 @@ void ParMesh::FinalizeParTopo()
|
||||
sface_lface.SetSize(nst + shared_quads.Size());
|
||||
if (sface_lface.Size())
|
||||
{
|
||||
STable3D *faces_tbl = GetFacesTable();
|
||||
auto faces_tbl = std::unique_ptr<STable3D>(GetFacesTable());
|
||||
for (int st = 0; st < nst; st++)
|
||||
{
|
||||
const int *v = shared_trias[st].v;
|
||||
@@ -917,13 +927,11 @@ void ParMesh::FinalizeParTopo()
|
||||
const int *v = shared_quads[sq].v;
|
||||
sface_lface[nst+sq] = (*faces_tbl)(v[0], v[1], v[2], v[3]);
|
||||
}
|
||||
delete faces_tbl;
|
||||
}
|
||||
}
|
||||
|
||||
ParMesh::ParMesh(MPI_Comm comm, istream &input, bool refine)
|
||||
: face_nbr_el_to_face(NULL)
|
||||
, glob_elem_offset(-1)
|
||||
: glob_elem_offset(-1)
|
||||
, glob_offset_sequence(-1)
|
||||
, gtopo(comm)
|
||||
{
|
||||
@@ -1134,7 +1142,7 @@ void ParMesh::MakeRefined_(ParMesh &orig_mesh, int ref_factor, int ref_type)
|
||||
MyComm = orig_mesh.GetComm();
|
||||
NRanks = orig_mesh.GetNRanks();
|
||||
MyRank = orig_mesh.GetMyRank();
|
||||
face_nbr_el_to_face = NULL;
|
||||
face_nbr_el_to_face = nullptr;
|
||||
glob_elem_offset = -1;
|
||||
glob_offset_sequence = -1;
|
||||
gtopo = orig_mesh.gtopo;
|
||||
@@ -2123,7 +2131,7 @@ void ParMesh::ExchangeFaceNbrData()
|
||||
|
||||
if (Nonconforming())
|
||||
{
|
||||
// with ParNCMesh we can set up face neighbors without communication
|
||||
// with ParNCMesh we can set up face neighbors mostly without communication
|
||||
pncmesh->GetFaceNeighbors(*this);
|
||||
have_face_nbr_data = true;
|
||||
|
||||
@@ -2192,7 +2200,7 @@ void ParMesh::ExchangeFaceNbrData()
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
GetFaceNbrElementToFaceTable();
|
||||
BuildFaceNbrElementToFaceTable();
|
||||
}
|
||||
|
||||
if (del_tables) { delete gr_sface; }
|
||||
@@ -2457,8 +2465,7 @@ void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
|
||||
|
||||
// convert the element data into face_nbr_elements
|
||||
face_nbr_elements.SetSize(face_nbr_elements_offset[num_face_nbrs]);
|
||||
face_nbr_el_ori.Clear();
|
||||
face_nbr_el_ori.SetSize(face_nbr_elements_offset[num_face_nbrs], 6);
|
||||
face_nbr_el_ori.reset(new Table(face_nbr_elements_offset[num_face_nbrs], 6));
|
||||
while (true)
|
||||
{
|
||||
int fn;
|
||||
@@ -2489,7 +2496,7 @@ void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
|
||||
if (Dim == 3)
|
||||
{
|
||||
int nf = el->GetNFaces();
|
||||
int * fn_ori = face_nbr_el_ori.GetRow(elem_off);
|
||||
int * fn_ori = face_nbr_el_ori->GetRow(elem_off);
|
||||
for (int j = 0; j < nf; j++)
|
||||
{
|
||||
fn_ori[j] = recv_elemdata[j];
|
||||
@@ -2499,7 +2506,7 @@ void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
|
||||
face_nbr_elements[elem_off++] = el;
|
||||
}
|
||||
}
|
||||
face_nbr_el_ori.Finalize();
|
||||
face_nbr_el_ori->Finalize();
|
||||
|
||||
MPI_Waitall(num_face_nbrs, send_requests, statuses);
|
||||
|
||||
@@ -2721,191 +2728,118 @@ STable3D *ParMesh::GetSharedFacesTable()
|
||||
return sfaces_tbl;
|
||||
}
|
||||
|
||||
STable3D *ParMesh::GetFaceNbrElementToFaceTable(int ret_ftbl)
|
||||
template <int N>
|
||||
void
|
||||
ParMesh::AddTriFaces(const Array<int> &elem_vertices,
|
||||
const std::unique_ptr<STable3D> &faces,
|
||||
const std::unique_ptr<STable3D> &shared_faces,
|
||||
int elem, int start, int end, const int fverts[][N])
|
||||
{
|
||||
int i, *v;
|
||||
STable3D * faces_tbl = GetFacesTable();
|
||||
STable3D * sfaces_tbl = GetSharedFacesTable();
|
||||
for (int i = start; i < end; ++i)
|
||||
{
|
||||
// Reference face vertices.
|
||||
const auto fv = fverts[i];
|
||||
// Element specific face vertices.
|
||||
const Vert3 elem_fv(elem_vertices[fv[0]], elem_vertices[fv[1]],
|
||||
elem_vertices[fv[2]]);
|
||||
|
||||
if (face_nbr_el_to_face != NULL)
|
||||
{
|
||||
delete face_nbr_el_to_face;
|
||||
// Check amongst the faces of elements local to this rank for this set of vertices
|
||||
const int lf = faces->Index(elem_fv.v[0], elem_fv.v[1], elem_fv.v[2]);
|
||||
|
||||
// If the face wasn't found amonst processor local elements, search the
|
||||
// ghosts for this set of vertices.
|
||||
const int sf = lf < 0 ? shared_faces->Index(elem_fv.v[0], elem_fv.v[1],
|
||||
elem_fv.v[2]) : -1;
|
||||
// If find local face -> use that
|
||||
// else if find shared face -> shift and use that
|
||||
// else no face found -> set to -1
|
||||
const int face_to_add = lf < 0 ? (sf >= 0 ? sf + NumOfFaces : -1) : lf;
|
||||
|
||||
MFEM_ASSERT(sf >= 0 ||
|
||||
lf >= 0, "Face must be from a local or a face neighbor element");
|
||||
|
||||
// Add this discovered face to the list of faces of this face neighbor element
|
||||
face_nbr_el_to_face->Push(elem, face_to_add);
|
||||
}
|
||||
face_nbr_el_to_face = new Table(face_nbr_elements.Size(), 6);
|
||||
for (i = 0; i < face_nbr_elements.Size(); i++)
|
||||
}
|
||||
|
||||
void ParMesh::BuildFaceNbrElementToFaceTable()
|
||||
{
|
||||
const auto faces = std::unique_ptr<STable3D>(GetFacesTable());
|
||||
const auto shared_faces = std::unique_ptr<STable3D>(GetSharedFacesTable());
|
||||
|
||||
face_nbr_el_to_face.reset(new Table(face_nbr_elements.Size(), 6));
|
||||
|
||||
Array<int> v;
|
||||
|
||||
// Helper for adding quadrilateral faces.
|
||||
auto add_quad_faces = [&faces, &shared_faces, &v, this]
|
||||
(int elem, int start, int end, const int fverts[][4])
|
||||
{
|
||||
v = face_nbr_elements[i]->GetVertices();
|
||||
for (int i = start; i < end; ++i)
|
||||
{
|
||||
const int * const fv = fverts[i];
|
||||
int k = 0;
|
||||
int max = v[fv[0]];
|
||||
|
||||
if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
|
||||
if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
|
||||
if (max < v[fv[3]]) { k = 3; }
|
||||
|
||||
int v0 = -1, v1 = -1, v2 = -1;
|
||||
switch (k)
|
||||
{
|
||||
case 0:
|
||||
v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 1:
|
||||
v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 2:
|
||||
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 3:
|
||||
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
|
||||
break;
|
||||
}
|
||||
int lf = faces->Index(v0, v1, v2);
|
||||
if (lf < 0)
|
||||
{
|
||||
lf = shared_faces->Index(v0, v1, v2);
|
||||
if (lf >= 0)
|
||||
{
|
||||
lf += NumOfFaces;
|
||||
}
|
||||
}
|
||||
face_nbr_el_to_face->Push(elem, lf);
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < face_nbr_elements.Size(); i++)
|
||||
{
|
||||
face_nbr_elements[i]->GetVertices(v);
|
||||
switch (face_nbr_elements[i]->GetType())
|
||||
{
|
||||
case Element::TETRAHEDRON:
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
const int *fv = tet_t::FaceVert[j];
|
||||
int lf = faces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
|
||||
if (lf < 0)
|
||||
{
|
||||
lf = sfaces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
|
||||
if (lf >= 0)
|
||||
{
|
||||
lf += NumOfFaces;
|
||||
}
|
||||
}
|
||||
face_nbr_el_to_face->Push(i, lf);
|
||||
}
|
||||
AddTriFaces(v, faces, shared_faces, i, 0, 4, tet_t::FaceVert);
|
||||
break;
|
||||
}
|
||||
case Element::WEDGE:
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
const int *fv = pri_t::FaceVert[j];
|
||||
int lf = faces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
|
||||
if (lf < 0)
|
||||
{
|
||||
lf = sfaces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
|
||||
if (lf >= 0)
|
||||
{
|
||||
lf += NumOfFaces;
|
||||
}
|
||||
}
|
||||
face_nbr_el_to_face->Push(i, lf);
|
||||
}
|
||||
for (int j = 2; j < 5; j++)
|
||||
{
|
||||
const int *fv = pri_t::FaceVert[j];
|
||||
int k = 0;
|
||||
int max = v[fv[0]];
|
||||
|
||||
if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
|
||||
if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
|
||||
if (max < v[fv[3]]) { k = 3; }
|
||||
|
||||
int v0 = -1, v1 = -1, v2 = -1;
|
||||
switch (k)
|
||||
{
|
||||
case 0:
|
||||
v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 1:
|
||||
v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 2:
|
||||
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 3:
|
||||
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
|
||||
break;
|
||||
}
|
||||
int lf = faces_tbl->Index(v0, v1, v2);
|
||||
if (lf < 0)
|
||||
{
|
||||
lf = sfaces_tbl->Index(v0, v1, v2);
|
||||
if (lf >= 0)
|
||||
{
|
||||
lf += NumOfFaces;
|
||||
}
|
||||
}
|
||||
face_nbr_el_to_face->Push(i, lf);
|
||||
}
|
||||
AddTriFaces(v, faces, shared_faces, i, 0, 2, pri_t::FaceVert);
|
||||
add_quad_faces(i, 2, 5, pri_t::FaceVert);
|
||||
break;
|
||||
}
|
||||
case Element::PYRAMID:
|
||||
{
|
||||
for (int j = 0; j < 1; j++)
|
||||
{
|
||||
const int *fv = pyr_t::FaceVert[j];
|
||||
int k = 0;
|
||||
int max = v[fv[0]];
|
||||
|
||||
if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
|
||||
if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
|
||||
if (max < v[fv[3]]) { k = 3; }
|
||||
|
||||
int v0 = -1, v1 = -1, v2 = -1;
|
||||
switch (k)
|
||||
{
|
||||
case 0:
|
||||
v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 1:
|
||||
v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 2:
|
||||
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 3:
|
||||
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
|
||||
break;
|
||||
}
|
||||
int lf = faces_tbl->Index(v0, v1, v2);
|
||||
if (lf < 0)
|
||||
{
|
||||
lf = sfaces_tbl->Index(v0, v1, v2);
|
||||
if (lf >= 0)
|
||||
{
|
||||
lf += NumOfFaces;
|
||||
}
|
||||
}
|
||||
face_nbr_el_to_face->Push(i, lf);
|
||||
}
|
||||
for (int j = 1; j < 5; j++)
|
||||
{
|
||||
const int *fv = pyr_t::FaceVert[j];
|
||||
int lf = faces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
|
||||
if (lf < 0)
|
||||
{
|
||||
lf = sfaces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
|
||||
if (lf >= 0)
|
||||
{
|
||||
lf += NumOfFaces;
|
||||
}
|
||||
}
|
||||
face_nbr_el_to_face->Push(i, lf);
|
||||
}
|
||||
add_quad_faces(i, 0, 1, pyr_t::FaceVert);
|
||||
AddTriFaces(v, faces, shared_faces, i, 1, 5, pyr_t::FaceVert);
|
||||
break;
|
||||
}
|
||||
case Element::HEXAHEDRON:
|
||||
{
|
||||
// find the face by the vertices with the smallest 3 numbers
|
||||
// z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
|
||||
for (int j = 0; j < 6; j++)
|
||||
{
|
||||
const int *fv = hex_t::FaceVert[j];
|
||||
int k = 0;
|
||||
int max = v[fv[0]];
|
||||
|
||||
if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
|
||||
if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
|
||||
if (max < v[fv[3]]) { k = 3; }
|
||||
|
||||
int v0 = -1, v1 = -1, v2 = -1;
|
||||
switch (k)
|
||||
{
|
||||
case 0:
|
||||
v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 1:
|
||||
v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 2:
|
||||
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
|
||||
break;
|
||||
case 3:
|
||||
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
|
||||
break;
|
||||
}
|
||||
int lf = faces_tbl->Index(v0, v1, v2);
|
||||
if (lf < 0)
|
||||
{
|
||||
lf = sfaces_tbl->Index(v0, v1, v2);
|
||||
if (lf >= 0)
|
||||
{
|
||||
lf += NumOfFaces;
|
||||
}
|
||||
}
|
||||
face_nbr_el_to_face->Push(i, lf);
|
||||
}
|
||||
add_quad_faces(i, 0, 6, hex_t::FaceVert);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -2913,14 +2847,6 @@ STable3D *ParMesh::GetFaceNbrElementToFaceTable(int ret_ftbl)
|
||||
}
|
||||
}
|
||||
face_nbr_el_to_face->Finalize();
|
||||
|
||||
delete sfaces_tbl;
|
||||
if (ret_ftbl)
|
||||
{
|
||||
return faces_tbl;
|
||||
}
|
||||
delete faces_tbl;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ParMesh::GetFaceNbrRank(int fn) const
|
||||
@@ -2941,33 +2867,28 @@ int ParMesh::GetFaceNbrRank(int fn) const
|
||||
}
|
||||
|
||||
void
|
||||
ParMesh::GetFaceNbrElementFaces(int i, Array<int> &fcs, Array<int> &cor) const
|
||||
ParMesh::GetFaceNbrElementFaces(int i, Array<int> &faces,
|
||||
Array<int> &orientations) const
|
||||
{
|
||||
int n, j;
|
||||
int el_nbr = i - GetNE();
|
||||
if (face_nbr_el_to_face)
|
||||
if (face_nbr_el_to_face != nullptr && el_nbr < face_nbr_el_to_face->Size())
|
||||
{
|
||||
face_nbr_el_to_face->GetRow(el_nbr, fcs);
|
||||
face_nbr_el_to_face->GetRow(el_nbr, faces);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
|
||||
"face_nbr_el_to_face not generated.");
|
||||
"face_nbr_el_to_face not generated correctly.");
|
||||
}
|
||||
if (el_nbr < face_nbr_el_ori.Size())
|
||||
|
||||
if (face_nbr_el_ori != nullptr && el_nbr < face_nbr_el_ori->Size())
|
||||
{
|
||||
const int * row = face_nbr_el_ori.GetRow(el_nbr);
|
||||
n = fcs.Size();
|
||||
cor.SetSize(n);
|
||||
for (j=0; j<n; j++)
|
||||
{
|
||||
cor[j] = row[j];
|
||||
}
|
||||
face_nbr_el_ori->GetRow(el_nbr, orientations);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
|
||||
"face_nbr_el_to_face not generated.");
|
||||
"face_nbr_el_ori not generated correctly.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3227,7 +3148,7 @@ int ParMesh::GetSharedFace(int sface) const
|
||||
{
|
||||
MFEM_ASSERT(Dim > 1, "");
|
||||
const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
|
||||
int csize = (int) shared.conforming.Size();
|
||||
int csize = shared.conforming.Size();
|
||||
return sface < csize
|
||||
? shared.conforming[sface].index
|
||||
: shared.slaves[sface - csize].index;
|
||||
@@ -3412,7 +3333,7 @@ void ParMesh::ReorientTetMesh()
|
||||
GenerateFaces();
|
||||
if (el_to_edge)
|
||||
{
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -3647,7 +3568,7 @@ void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
|
||||
// 6. Update element-to-edge relations.
|
||||
if (el_to_edge != NULL)
|
||||
{
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
} // 'if (Dim == 3)'
|
||||
|
||||
@@ -3885,7 +3806,7 @@ void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
|
||||
|
||||
if (el_to_edge != NULL)
|
||||
{
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
GenerateFaces();
|
||||
}
|
||||
} // 'if (Dim == 2)'
|
||||
@@ -3947,6 +3868,8 @@ void ParMesh::NonconformingRefinement(const Array<Refinement> &refinements,
|
||||
"serial Mesh)");
|
||||
}
|
||||
|
||||
ResetLazyData();
|
||||
|
||||
DeleteFaceNbrData();
|
||||
|
||||
// NOTE: no check of !refinements.Size(), in parallel we would have to reduce
|
||||
@@ -4579,7 +4502,7 @@ void ParMesh::UniformRefinement3D()
|
||||
|
||||
DSTable v_to_v(NumOfVertices);
|
||||
GetVertexToVertexTable(v_to_v);
|
||||
STable3D *faces_tbl = GetFacesTable();
|
||||
auto faces_tbl = std::unique_ptr<STable3D>(GetFacesTable());
|
||||
|
||||
// call Mesh::UniformRefinement3D_base so that it won't update the nodes
|
||||
Array<int> f2qf;
|
||||
@@ -4594,7 +4517,6 @@ void ParMesh::UniformRefinement3D()
|
||||
// update the groups
|
||||
UniformRefineGroups3D(old_nv, old_nedges, v_to_v, *faces_tbl,
|
||||
f2qf.Size() ? &f2qf : NULL);
|
||||
delete faces_tbl;
|
||||
|
||||
UpdateNodes();
|
||||
}
|
||||
@@ -5379,7 +5301,7 @@ Mesh ParMesh::GetSerialMesh(int save_rank) const
|
||||
for (int e = 0; e < NumOfElements; e++)
|
||||
{
|
||||
const int attr = elements[e]->GetAttribute();
|
||||
const int geom_type = elements[e]->GetGeometryType();;
|
||||
const int geom_type = elements[e]->GetGeometryType();
|
||||
ints.Append(attr);
|
||||
ints.Append(geom_type);
|
||||
pfespace_linear.GetElementDofs(e, dofs);
|
||||
@@ -6693,6 +6615,8 @@ void ParMesh::Swap(ParMesh &other)
|
||||
mfem::Swap(face_nbr_vertices, other.face_nbr_vertices);
|
||||
mfem::Swap(send_face_nbr_elements, other.send_face_nbr_elements);
|
||||
mfem::Swap(send_face_nbr_vertices, other.send_face_nbr_vertices);
|
||||
std::swap(face_nbr_el_ori, other.face_nbr_el_ori);
|
||||
std::swap(face_nbr_el_to_face, other.face_nbr_el_to_face);
|
||||
|
||||
// Nodes, NCMesh, and NURBSExtension are taken care of by Mesh::Swap
|
||||
mfem::Swap(pncmesh, other.pncmesh);
|
||||
@@ -6713,8 +6637,7 @@ void ParMesh::Destroy()
|
||||
}
|
||||
shared_edges.DeleteAll();
|
||||
|
||||
delete face_nbr_el_to_face;
|
||||
face_nbr_el_to_face = NULL;
|
||||
face_nbr_el_to_face = nullptr;
|
||||
}
|
||||
|
||||
ParMesh::~ParMesh()
|
||||
|
||||
+34
-5
@@ -77,8 +77,11 @@ protected:
|
||||
// sface ids: all triangles first, then all quads
|
||||
Array<int> sface_lface;
|
||||
|
||||
Table *face_nbr_el_to_face;
|
||||
Table face_nbr_el_ori; // orientations for each face (from nbr processor)
|
||||
/// Table that maps from face neighbor element number, to the face numbers of
|
||||
/// that element.
|
||||
std::unique_ptr<Table> face_nbr_el_to_face;
|
||||
/// orientations for each face (from nbr processor)
|
||||
std::unique_ptr<Table> face_nbr_el_ori;
|
||||
|
||||
IsoparametricTransformation FaceNbrTransformation;
|
||||
|
||||
@@ -113,7 +116,32 @@ protected:
|
||||
bool DecodeFaceSplittings(HashTable<Hashed2> &v_to_v, const int *v,
|
||||
const Array<unsigned> &codes, int &pos);
|
||||
|
||||
STable3D *GetFaceNbrElementToFaceTable(int ret_ftbl = 0);
|
||||
// Given a completed FacesTable and SharedFacesTable, construct a table that
|
||||
// maps from face neighbor element number, to the set of faces of that
|
||||
// element. Store the resulting data in the member variable
|
||||
// face_nbr_el_to_face. If the mesh is nonconforming, this also builds the
|
||||
// the face_nbr_el_ori variable from the faces_info.
|
||||
void BuildFaceNbrElementToFaceTable();
|
||||
|
||||
/**
|
||||
* @brief Helper function for adding triangle face neighbor element to face
|
||||
* table entries. Have to use a template here rather than lambda capture
|
||||
* because the FaceVert entries in Geometry have inner size of 3 for tets and
|
||||
* 4 for everything else.
|
||||
*
|
||||
* @tparam N Inner dimension on the fvert variable, 3 for tet, 4 otherwise
|
||||
* @param[in] v Set of vertices for this element
|
||||
* @param[in] faces Table of faces interior to this rank
|
||||
* @param[in] shared_faces Table of faces shared by this rank and another
|
||||
* @param[in] elem The face neighbor element
|
||||
* @param[in] start Starting index into fverts
|
||||
* @param[in] end End index into fverts
|
||||
* @param[in] fverts Array of face vertices for this particular geometry.
|
||||
*/
|
||||
template <int N>
|
||||
void AddTriFaces(const Array<int> &v, const std::unique_ptr<STable3D> &faces,
|
||||
const std::unique_ptr<STable3D> &shared_faces,
|
||||
int elem, int start, int end, const int fverts[][N]);
|
||||
|
||||
void GetFaceNbrElementTransformation(
|
||||
int i, IsoparametricTransformation *ElTr);
|
||||
@@ -287,7 +315,7 @@ protected:
|
||||
|
||||
public:
|
||||
/// Default constructor. Create an empty @a ParMesh.
|
||||
ParMesh() : MyComm(0), NRanks(0), MyRank(-1), face_nbr_el_to_face(NULL),
|
||||
ParMesh() : MyComm(0), NRanks(0), MyRank(-1),
|
||||
glob_elem_offset(-1), glob_offset_sequence(-1),
|
||||
have_face_nbr_data(false), pncmesh(NULL) { }
|
||||
|
||||
@@ -465,7 +493,8 @@ public:
|
||||
int GetFaceNbrRank(int fn) const;
|
||||
|
||||
/** Similar to Mesh::GetElementFaces */
|
||||
void GetFaceNbrElementFaces(int i, Array<int> &fcs, Array<int> &cor) const;
|
||||
void GetFaceNbrElementFaces(int i, Array<int> &faces,
|
||||
Array<int> &orientation) const;
|
||||
|
||||
/** Similar to Mesh::GetFaceToElementTable with added face-neighbor elements
|
||||
with indices offset by the local number of elements. */
|
||||
|
||||
+135
-1
@@ -16,10 +16,12 @@
|
||||
#include "mesh_headers.hpp"
|
||||
#include "pncmesh.hpp"
|
||||
#include "../general/binaryio.hpp"
|
||||
#include "../general/communication.hpp"
|
||||
|
||||
#include <numeric> // std::accumulate
|
||||
#include <map>
|
||||
#include <climits> // INT_MIN, INT_MAX
|
||||
#include <array>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -887,6 +889,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
|
||||
Array<Element*> fnbr;
|
||||
Array<Connection> send_elems;
|
||||
std::map<int, std::vector<int>> recv_elems;
|
||||
|
||||
// Counts the number of slave faces of a master. This may be larger than the
|
||||
// number of shared slaves if there exist degenerate slave-faces from face-edge constraints.
|
||||
@@ -901,6 +904,11 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
fnbr.Reserve(bound);
|
||||
send_elems.Reserve(bound);
|
||||
|
||||
// If there are face neighbor elements with triangular faces, the
|
||||
// `face_nbr_el_ori` structure will need to be built. This requires
|
||||
// communication so we attempt to avoid it by checking first.
|
||||
bool face_nbr_w_tri_faces = false;
|
||||
|
||||
// go over all shared faces and collect face neighbor elements
|
||||
for (int i = 0; i < shared.conforming.Size(); i++)
|
||||
{
|
||||
@@ -914,8 +922,12 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
if (e[0]->rank == MyRank) { std::swap(e[0], e[1]); }
|
||||
MFEM_ASSERT(e[0]->rank != MyRank && e[1]->rank == MyRank, "");
|
||||
|
||||
face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[0]->geom));
|
||||
face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[1]->geom));
|
||||
|
||||
fnbr.Append(e[0]);
|
||||
send_elems.Append(Connection(e[0]->rank, e[1]->index));
|
||||
recv_elems[e[0]->rank].push_back(e[0]->index);
|
||||
}
|
||||
|
||||
for (int i = 0; i < shared.masters.Size(); i++)
|
||||
@@ -924,7 +936,7 @@ 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.element < 0) { continue; }
|
||||
if (sf.element < 0 || sf.index < 0) { continue; }
|
||||
|
||||
MFEM_ASSERT(mf.element >= 0, "");
|
||||
Element* e[2] = { &elements[mf.element], &elements[sf.element] };
|
||||
@@ -938,8 +950,12 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
}
|
||||
if (loc0) { std::swap(e[0], e[1]); }
|
||||
|
||||
face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[0]->geom));
|
||||
face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[1]->geom));
|
||||
|
||||
fnbr.Append(e[0]);
|
||||
send_elems.Append(Connection(e[0]->rank, e[1]->index));
|
||||
recv_elems[e[0]->rank].push_back(e[0]->index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1022,6 +1038,13 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
send_elems.Sort();
|
||||
send_elems.Unique();
|
||||
|
||||
for (auto &kv : recv_elems)
|
||||
{
|
||||
std::sort(kv.second.begin(), kv.second.end());
|
||||
kv.second.erase(std::unique(kv.second.begin(), kv.second.end()),
|
||||
kv.second.end());
|
||||
}
|
||||
|
||||
for (int i = 0, last_rank = -1; i < send_elems.Size(); i++)
|
||||
{
|
||||
Connection &c = send_elems[i];
|
||||
@@ -1175,6 +1198,117 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// In 3D some extra orientation data structures can be needed.
|
||||
if (Dim == 3)
|
||||
{
|
||||
// Populates face_nbr_el_to_face, always needed.
|
||||
pmesh.BuildFaceNbrElementToFaceTable();
|
||||
|
||||
if (face_nbr_w_tri_faces)
|
||||
{
|
||||
// There are face neighbor elements with triangular faces, need to
|
||||
// perform communication to ensure the orientation is valid.
|
||||
using RankToOrientation = std::map<int, std::vector<std::array<int, 6>>>;
|
||||
constexpr std::array<int, 6> unset_ori{{-1,-1,-1,-1,-1,-1}};
|
||||
const int rank = pmesh.GetMyRank();
|
||||
|
||||
// Loop over send elems, compute the orientation and place in the
|
||||
// buffer to send to each processor. Note elements are
|
||||
// lexicographically sorted with rank and element number, and this
|
||||
// ordering holds across processors.
|
||||
RankToOrientation send_rank_to_face_neighbor_orientations;
|
||||
Array<int> orientations, faces;
|
||||
|
||||
// send_elems goes from rank of the receiving processor, to the index
|
||||
// of the face neighbor element on this processor.
|
||||
for (const auto &se : send_elems)
|
||||
{
|
||||
const auto &true_rank = pmesh.face_nbr_group[se.from];
|
||||
pmesh.GetElementFaces(se.to, faces, orientations);
|
||||
|
||||
// Place a new entry of unset orientations
|
||||
send_rank_to_face_neighbor_orientations[true_rank].emplace_back(unset_ori);
|
||||
|
||||
// Copy the entries, any unset faces will remain -1.
|
||||
std::copy(orientations.begin(), orientations.end(),
|
||||
send_rank_to_face_neighbor_orientations[true_rank].back().begin());
|
||||
}
|
||||
|
||||
// Initialize the receive buffers and resize to match the expected
|
||||
// number of elements coming in. The copy ensures the appropriate rank
|
||||
// pairings are in place, and for a purely conformal interface, the
|
||||
// resize is a no-op.
|
||||
auto recv_rank_to_face_neighbor_orientations =
|
||||
send_rank_to_face_neighbor_orientations;
|
||||
for (auto &kv : recv_rank_to_face_neighbor_orientations)
|
||||
{
|
||||
kv.second.resize(recv_elems[kv.first].size());
|
||||
}
|
||||
|
||||
// For asynchronous send/recv, will use arrays of requests to monitor the
|
||||
// status of the connections.
|
||||
std::vector<MPI_Request> send_requests, recv_requests;
|
||||
std::vector<MPI_Status> status(nranks);
|
||||
|
||||
// NOTE: This is CRITICAL, to ensure the addresses of these requests
|
||||
// do not change between the send/recv and the wait.
|
||||
send_requests.reserve(nranks);
|
||||
recv_requests.reserve(nranks);
|
||||
|
||||
// Shared face communication is bidirectional -> any rank to whom
|
||||
// orientations must be sent, will need to send orientations back. The
|
||||
// orientation data is contiguous because std::array<int,6> is an
|
||||
// aggregate. Loop over each communication pairing, and dispatch the
|
||||
// buffer loaded with all the orientation data.
|
||||
for (const auto &kv : send_rank_to_face_neighbor_orientations)
|
||||
{
|
||||
send_requests.emplace_back(); // instantiate a request for tracking.
|
||||
|
||||
// low rank sends on low, high rank sends on high.
|
||||
const int send_tag = (rank < kv.first)
|
||||
? std::min(rank, kv.first)
|
||||
: std::max(rank, kv.first);
|
||||
MPI_Isend(&kv.second[0][0], int(kv.second.size() * 6),
|
||||
MPI_INT, kv.first, send_tag, pmesh.MyComm, &send_requests.back());
|
||||
}
|
||||
|
||||
// Loop over the communication pairing again, and receive the
|
||||
// symmetric buffer from the other processor.
|
||||
for (auto &kv : recv_rank_to_face_neighbor_orientations)
|
||||
{
|
||||
recv_requests.emplace_back(); // instantiate a request for tracking
|
||||
|
||||
// low rank receives on high, high rank receives on low.
|
||||
const int recv_tag = (rank < kv.first)
|
||||
? std::max(rank, kv.first)
|
||||
: std::min(rank, kv.first);
|
||||
MPI_Irecv(&kv.second[0][0], int(kv.second.size() * 6),
|
||||
MPI_INT, kv.first, recv_tag, pmesh.MyComm, &recv_requests.back());
|
||||
}
|
||||
|
||||
// Wait until all receive buffers are full before beginning to process.
|
||||
MPI_Waitall(int(recv_requests.size()), recv_requests.data(), status.data());
|
||||
|
||||
pmesh.face_nbr_el_ori.reset(new Table(pmesh.face_nbr_elements.Size(), 6));
|
||||
int elem = 0;
|
||||
for (const auto &kv : recv_rank_to_face_neighbor_orientations)
|
||||
{
|
||||
// All elements associated to this face-neighbor rank
|
||||
for (const auto &eo : kv.second)
|
||||
{
|
||||
std::copy(eo.begin(), eo.end(), pmesh.face_nbr_el_ori->GetRow(elem));
|
||||
++elem;
|
||||
}
|
||||
}
|
||||
pmesh.face_nbr_el_ori->Finalize();
|
||||
|
||||
// Must wait for all send buffers to be released before the scope closes.
|
||||
MPI_Waitall(int(send_requests.size()), send_requests.data(), status.data());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE: this function skips ParMesh::send_face_nbr_vertices and
|
||||
// ParMesh::face_nbr_vertices_offset, these are not used outside of ParMesh
|
||||
}
|
||||
|
||||
+2
-4
@@ -108,9 +108,7 @@ public:
|
||||
passed. */
|
||||
void Rebalance(const Array<int> *custom_partition = NULL);
|
||||
|
||||
|
||||
// interface for ParFiniteElementSpace
|
||||
|
||||
int GetNElements() const { return NElements; }
|
||||
|
||||
int GetNGhostVertices() const { return NGhostVertices; }
|
||||
@@ -141,8 +139,8 @@ public:
|
||||
return (index < NFaces) ? face_orient[index] : 0;
|
||||
}
|
||||
|
||||
typedef short GroupId;
|
||||
typedef std::vector<int> CommGroup;
|
||||
using GroupId = short;
|
||||
using CommGroup = std::vector<int>;
|
||||
|
||||
/// Return vertex/edge/face ('entity' == 0/1/2, resp.) owner.
|
||||
GroupId GetEntityOwnerId(int entity, int index)
|
||||
|
||||
@@ -1028,9 +1028,6 @@ void ParPumiMesh::UpdateMesh(const ParMesh* AdaptedpMesh)
|
||||
bel_to_edge = (AdaptedpMesh->bel_to_edge) ?
|
||||
new Table(*(AdaptedpMesh->bel_to_edge)) : NULL;
|
||||
|
||||
// Copy the boudary-to-edge Array, be_to_edge (2D)
|
||||
AdaptedpMesh->be_to_edge.Copy(be_to_edge);
|
||||
|
||||
// Duplicate the faces and faces_info.
|
||||
faces.SetSize(AdaptedpMesh->faces.Size());
|
||||
for (int i = 0; i < faces.Size(); i++)
|
||||
|
||||
@@ -235,10 +235,13 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
|
||||
// Add boundaries
|
||||
{
|
||||
int num_of_faces_or_edges =
|
||||
(Dim == 3) ? NumOfFaces :
|
||||
((Dim == 2) ? NumOfEdges : NumOfVertices);
|
||||
Array<int> &be2face = (Dim == 2) ? be_to_edge : be_to_face;
|
||||
const int num_codim_1 = [this]()
|
||||
{
|
||||
if (Dim == 1) { return NumOfVertices; }
|
||||
else if (Dim == 2) { return NumOfEdges; }
|
||||
else if (Dim == 3) { return NumOfFaces; }
|
||||
else { MFEM_ABORT("Invalid dimension."); return -1; }
|
||||
}();
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
@@ -249,7 +252,7 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
}
|
||||
|
||||
NumOfBdrElements = 0;
|
||||
for (int i = 0; i < num_of_faces_or_edges; i++)
|
||||
for (int i = 0; i < num_codim_1; i++)
|
||||
{
|
||||
if (GetFaceInformation(i).IsBoundary())
|
||||
{
|
||||
@@ -258,14 +261,17 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
}
|
||||
|
||||
boundary.SetSize(NumOfBdrElements);
|
||||
be2face.SetSize(NumOfBdrElements);
|
||||
be_to_face.SetSize(NumOfBdrElements);
|
||||
Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
|
||||
int max_bdr_attr = parent.bdr_attributes.Max();
|
||||
for (int i = 0, j = 0; i < num_of_faces_or_edges; i++)
|
||||
|
||||
for (int i = 0, j = 0; i < num_codim_1; i++)
|
||||
{
|
||||
if (GetFaceInformation(i).IsBoundary())
|
||||
{
|
||||
boundary[j] = faces[i]->Duplicate(this);
|
||||
be_to_face[j] = i;
|
||||
|
||||
if (from == SubMesh::From::Domain && Dim >= 2)
|
||||
{
|
||||
int pbeid = Dim == 3 ? parent_face_to_be[parent_face_ids_[i]] :
|
||||
@@ -283,7 +289,7 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
{
|
||||
boundary[j]->SetAttribute(SubMesh::GENERATED_ATTRIBUTE);
|
||||
}
|
||||
be2face[j++] = i;
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,7 +329,7 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
if (Dim > 1)
|
||||
{
|
||||
if (!el_to_edge) { el_to_edge = new Table; }
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
|
||||
SetAttributes();
|
||||
|
||||
@@ -317,8 +317,7 @@ ParTransferMap::CorrectFaceOrientations(const ParFiniteElementSpace &fes,
|
||||
|
||||
if (parent_face_ori.Size() == 0) { return; }
|
||||
|
||||
VDofTransformation vdoftrans(fes.GetVDim(),
|
||||
fes.GetOrdering());
|
||||
DofTransformation doftrans(fes.GetVDim(), fes.GetOrdering());
|
||||
|
||||
int dim = mesh->Dimension();
|
||||
bool face = (dim == 3);
|
||||
@@ -332,17 +331,13 @@ ParTransferMap::CorrectFaceOrientations(const ParFiniteElementSpace &fes,
|
||||
if (parent_face_ori[i] == 0) { continue; }
|
||||
|
||||
Geometry::Type geom = face ? mesh->GetFaceGeometry(i) :
|
||||
mesh->GetElementGeometry(i);;
|
||||
mesh->GetElementGeometry(i);
|
||||
|
||||
StatelessDofTransformation * doftrans =
|
||||
fec->DofTransformationForGeometry(geom);
|
||||
|
||||
if (doftrans == NULL) { continue; }
|
||||
|
||||
vdoftrans.SetDofTransformation(*doftrans);
|
||||
if (!fec->DofTransformationForGeometry(geom)) { continue; }
|
||||
doftrans.SetDofTransformation(*fec->DofTransformationForGeometry(geom));
|
||||
|
||||
Fo[0] = parent_face_ori[i];
|
||||
vdoftrans.SetFaceOrientations(Fo);
|
||||
doftrans.SetFaceOrientations(Fo);
|
||||
|
||||
if (face)
|
||||
{
|
||||
@@ -356,12 +351,12 @@ ParTransferMap::CorrectFaceOrientations(const ParFiniteElementSpace &fes,
|
||||
if (sub_to_parent_map)
|
||||
{
|
||||
src.GetSubVector(vdofs, face_vector);
|
||||
vdoftrans.TransformPrimal(face_vector);
|
||||
doftrans.TransformPrimal(face_vector);
|
||||
}
|
||||
else
|
||||
{
|
||||
dst.GetSubVector(vdofs, face_vector);
|
||||
vdoftrans.InvTransformPrimal(face_vector);
|
||||
doftrans.InvTransformPrimal(face_vector);
|
||||
}
|
||||
|
||||
for (int j = 0; j < vdofs.Size(); j++)
|
||||
|
||||
@@ -65,7 +65,7 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
|
||||
for (int i = 0; i < NumOfBdrElements; i++)
|
||||
{
|
||||
int pbeid = parent_face_to_be[parent_face_ids_[GetBdrFace(i)]];
|
||||
int pbeid = parent_face_to_be[parent_face_ids_[GetBdrElementFaceIndex(i)]];
|
||||
if (pbeid != -1)
|
||||
{
|
||||
int attr = parent.GetBdrElement(pbeid)->GetAttribute();
|
||||
@@ -117,7 +117,7 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
|
||||
for (int i = 0; i < NumOfBdrElements; i++)
|
||||
{
|
||||
int pbeid = parent_face_to_be[parent_edge_ids_[GetBdrFace(i)]];
|
||||
int pbeid = parent_face_to_be[parent_edge_ids_[GetBdrElementFaceIndex(i)]];
|
||||
if (pbeid != -1)
|
||||
{
|
||||
int attr = parent.GetBdrElement(pbeid)->GetAttribute();
|
||||
|
||||
@@ -241,8 +241,7 @@ void TransferMap::CorrectFaceOrientations(const FiniteElementSpace &fes,
|
||||
|
||||
if (parent_face_ori.Size() == 0) { return; }
|
||||
|
||||
VDofTransformation vdoftrans(fes.GetVDim(),
|
||||
fes.GetOrdering());
|
||||
DofTransformation doftrans(fes.GetVDim(), fes.GetOrdering());
|
||||
|
||||
int dim = mesh->Dimension();
|
||||
bool face = (dim == 3);
|
||||
@@ -256,17 +255,13 @@ void TransferMap::CorrectFaceOrientations(const FiniteElementSpace &fes,
|
||||
if (parent_face_ori[i] == 0) { continue; }
|
||||
|
||||
Geometry::Type geom = face ? mesh->GetFaceGeometry(i) :
|
||||
mesh->GetElementGeometry(i);;
|
||||
mesh->GetElementGeometry(i);
|
||||
|
||||
StatelessDofTransformation * doftrans =
|
||||
fec->DofTransformationForGeometry(geom);
|
||||
|
||||
if (doftrans == NULL) { continue; }
|
||||
|
||||
vdoftrans.SetDofTransformation(*doftrans);
|
||||
if (!fec->DofTransformationForGeometry(geom)) { continue; }
|
||||
doftrans.SetDofTransformation(*fec->DofTransformationForGeometry(geom));
|
||||
|
||||
Fo[0] = parent_face_ori[i];
|
||||
vdoftrans.SetFaceOrientations(Fo);
|
||||
doftrans.SetFaceOrientations(Fo);
|
||||
|
||||
if (face)
|
||||
{
|
||||
@@ -280,12 +275,12 @@ void TransferMap::CorrectFaceOrientations(const FiniteElementSpace &fes,
|
||||
if (sub_to_parent_map)
|
||||
{
|
||||
src.GetSubVector(vdofs, face_vector);
|
||||
vdoftrans.TransformPrimal(face_vector);
|
||||
doftrans.TransformPrimal(face_vector);
|
||||
}
|
||||
else
|
||||
{
|
||||
dst.GetSubVector(vdofs, face_vector);
|
||||
vdoftrans.InvTransformPrimal(face_vector);
|
||||
doftrans.InvTransformPrimal(face_vector);
|
||||
}
|
||||
|
||||
for (int j = 0; j < vdofs.Size(); j++)
|
||||
|
||||
@@ -1403,7 +1403,7 @@ void maxwell_solution_curlcurl(const Vector & X,
|
||||
curlcurlE.resize(dim);
|
||||
for (int i = 0; i < dim; ++i)
|
||||
{
|
||||
curlcurlE[i] = 0.0;;
|
||||
curlcurlE[i] = 0.0;
|
||||
}
|
||||
switch (prob)
|
||||
{
|
||||
|
||||
@@ -347,8 +347,7 @@ int main (int argc, char *argv[])
|
||||
{
|
||||
if ((double) rand() / RAND_MAX < 0.5)
|
||||
{
|
||||
int element_order = sc_fes.GetElementOrder(e);
|
||||
sc_fes.SetElementOrder(e, element_order + 1);
|
||||
sc_fes.SetElementOrder(e, order + 1);
|
||||
}
|
||||
}
|
||||
sc_fes.Update(false);
|
||||
|
||||
@@ -278,4 +278,4 @@ void SolveCG(Operator &A, Solver &P, const Vector &B, Vector &X)
|
||||
cout << "Done.\nIterations: " << cg.GetNumIterations()
|
||||
<< "\nElapsed: " << tic_toc.RealTime() << endl;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
// Adapted analytic shape:
|
||||
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -bnd -qt 1 -qo 8
|
||||
// Adapted analytic size+orientation:
|
||||
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
|
||||
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 100 -bnd -qt 1 -qo 8
|
||||
// Adapted analytic shape+orientation:
|
||||
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
|
||||
//
|
||||
|
||||
@@ -1012,7 +1012,7 @@ struct QuarterPeach: public Surface
|
||||
for (int i = 0; i < GetNBE(); i++)
|
||||
{
|
||||
Element *el = GetBdrElement(i);
|
||||
const int fn = GetBdrElementEdgeIndex(i);
|
||||
const int fn = GetBdrElementFaceIndex(i);
|
||||
MFEM_VERIFY(!FaceIsTrueInterior(fn),"");
|
||||
Array<int> vertices;
|
||||
GetFaceVertices(fn, vertices);
|
||||
|
||||
@@ -359,7 +359,6 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
pmesh->ExchangeFaceNbrData();
|
||||
|
||||
|
||||
// Surface fitting.
|
||||
L2_FECollection mat_coll(0, dim);
|
||||
H1_FECollection surf_fit_fec(mesh_poly_deg, dim);
|
||||
@@ -432,7 +431,6 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
else { surf_fit_bg_gf0->ProjectCoefficient(*ls_coeff); }
|
||||
|
||||
|
||||
surf_fit_bg_grad_fes =
|
||||
new ParFiniteElementSpace(pmesh_surf_fit_bg, surf_fit_bg_fec, dim);
|
||||
surf_fit_bg_grad = new ParGridFunction(surf_fit_bg_grad_fes);
|
||||
@@ -630,6 +628,7 @@ int main (int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
}
|
||||
pmesh->SetAttributes();
|
||||
|
||||
// 13. Setup the final NonlinearForm (which defines the integral of interest,
|
||||
// its first and second derivatives). Here we can use a combination of
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
// Adapted analytic shape:
|
||||
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -bnd -qt 1 -qo 8
|
||||
// Adapted analytic size+orientation:
|
||||
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 200 -bnd -qt 1 -qo 8 -fd
|
||||
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 200 -bnd -qt 1 -qo 8
|
||||
// Adapted analytic shape+orientation:
|
||||
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
|
||||
//
|
||||
|
||||
@@ -1019,7 +1019,7 @@ struct QuarterPeach: public Surface
|
||||
for (int i = 0; i < GetNBE(); i++)
|
||||
{
|
||||
Element *el = GetBdrElement(i);
|
||||
const int fn = GetBdrElementEdgeIndex(i);
|
||||
const int fn = GetBdrElementFaceIndex(i);
|
||||
MFEM_VERIFY(!FaceIsTrueInterior(fn),"");
|
||||
Array<int> vertices;
|
||||
GetFaceVertices(fn, vertices);
|
||||
|
||||
@@ -322,17 +322,19 @@ int main(int argc, char *argv[])
|
||||
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream cyl_sol_sock(vishost, visport);
|
||||
socketstream cyl_sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
cyl_sol_sock.open(vishost, visport);
|
||||
cyl_sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
cyl_sol_sock.precision(8);
|
||||
cyl_sol_sock << "solution\n" << cylinder_submesh << temperature_cylinder_gf <<
|
||||
"pause\n" << std::flush;
|
||||
}
|
||||
socketstream block_sol_sock(vishost, visport);
|
||||
socketstream block_sol_sock;
|
||||
if (visualization)
|
||||
{
|
||||
block_sol_sock.open(vishost, visport);
|
||||
block_sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
block_sol_sock.precision(8);
|
||||
block_sol_sock << "solution\n" << block_submesh << temperature_block_gf <<
|
||||
|
||||
@@ -17,6 +17,10 @@ add_mfem_miniapp(nurbs_curveint
|
||||
MAIN nurbs_curveint.cpp
|
||||
LIBRARIES mfem)
|
||||
|
||||
add_mfem_miniapp(nurbs_printfunc
|
||||
MAIN nurbs_printfunc.cpp
|
||||
LIBRARIES mfem)
|
||||
|
||||
add_mfem_miniapp(nurbs_patch_ex1
|
||||
MAIN nurbs_patch_ex1.cpp
|
||||
LIBRARIES mfem)
|
||||
@@ -67,6 +71,9 @@ if (MFEM_ENABLE_TESTING)
|
||||
add_test(NAME nurbs_ex1_weak_patch_format_r1_ser
|
||||
COMMAND $<TARGET_FILE:nurbs_ex1> -no-vis
|
||||
-m ${PROJECT_SOURCE_DIR}/data/square-disc-nurbs-patch.mesh -o 2 --weak-bc -r 1)
|
||||
|
||||
add_test(NAME nurbs_printfunc
|
||||
COMMAND $<TARGET_FILE:nurbs_printfunc>)
|
||||
|
||||
if (MFEM_USE_LAPACK)
|
||||
add_test(NAME nurbs_patch_ex1_o4_r2_iro8_patcha_ser
|
||||
|
||||
@@ -21,7 +21,7 @@ CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_curveint
|
||||
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_curveint nurbs_printfunc
|
||||
PAR_MINIAPPS = nurbs_ex1p nurbs_ex11p
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
MINIAPPS = $(SEQ_MINIAPPS)
|
||||
@@ -109,6 +109,9 @@ nurbs_curveint-test-seq: nurbs_curveint
|
||||
@$(call mfem-test,$<,, NURBS miniapp,$(CI_ARGS_1))
|
||||
@$(call mfem-test,$<,, NURBS miniapp,$(CI_ARGS_2))
|
||||
|
||||
nurbs_printfunc-test-seq: nurbs_printfunc
|
||||
@$(call mfem-test,$<,, NURBS miniapp)
|
||||
|
||||
EX1P_ARGS_1 :=
|
||||
EX1P_ARGS_2 := -m ../../data/pipe-nurbs-2d.mesh -o 2 -no-ibp
|
||||
EX1P_ARGS_3 := -m ../../data/ball-nurbs.mesh -o 2 --weak-bc -r 0
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user