Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a8898a89e | ||
|
|
07c92a7e00 | ||
|
|
66e5d3782b | ||
|
|
4d3928d9b9 | ||
|
|
784148ca84 | ||
|
|
aed9fb51c2 | ||
|
|
1bb3b662c6 | ||
|
|
e92710ae06 | ||
|
|
c95bbd8c56 | ||
|
|
f9d9479d4d | ||
|
|
c6b5e80ebc | ||
|
|
8944ee325b | ||
|
|
f2a42123f7 | ||
|
|
85c95269ad | ||
|
|
dfd55716bd | ||
|
|
49a50027dd | ||
|
|
b5af0e9f81 | ||
|
|
33452bd3e8 | ||
|
|
27ed1f64f9 | ||
|
|
230dc805f3 | ||
|
|
f6bb6982e8 | ||
|
|
8df194e6b0 | ||
|
|
a0f2250a76 | ||
|
|
882dedf5f3 | ||
|
|
5f051efc49 | ||
|
|
20bf4d5a98 | ||
|
|
8921101d14 | ||
|
|
f74b394c8a | ||
|
|
e282d4518e | ||
|
|
5e43a958b7 | ||
|
|
14fc04e9ee | ||
|
|
8102cb8141 | ||
|
|
99c858c389 | ||
|
|
23986c5c64 | ||
|
|
af23695747 | ||
|
|
1f97289d2a | ||
|
|
19db07b30d | ||
|
|
419f890e5c | ||
|
|
3216d86e6c | ||
|
|
9c85a6365f | ||
|
|
2a10ab2f48 | ||
|
|
19e60a38cc | ||
|
|
e2a382dca4 | ||
|
|
ffaae2ea82 | ||
|
|
7f17f33763 | ||
|
|
d33fd770cd | ||
|
|
b2947bbaca | ||
|
|
6b7b6e8966 | ||
|
|
4a17243415 | ||
|
|
b1edfc3497 | ||
|
|
654fd1b611 | ||
|
|
5731d2f22b | ||
|
|
150cbcf8c5 | ||
|
|
1e35d9e49f | ||
|
|
ba2ebd318b | ||
|
|
a9fd723a4e | ||
|
|
0fa8a5c90d | ||
|
|
cdc6893087 | ||
|
|
059bbd7f2e | ||
|
|
53d7207786 | ||
|
|
6a99a7a63e | ||
|
|
1d7e1e9bb4 | ||
|
|
ea0bec7d13 | ||
|
|
ad6baa0cff | ||
|
|
a049988e16 | ||
|
|
db75e98609 | ||
|
|
88d2d8bdb1 | ||
|
|
902f2fdb08 | ||
|
|
46a22b93eb | ||
|
|
58c8905261 | ||
|
|
d191906f7a | ||
|
|
8e3a7b90fc | ||
|
|
25fc5c18dd | ||
|
|
dd3414a3a6 | ||
|
|
e6a990c2a5 | ||
|
|
935c5e4967 | ||
|
|
2a6f07a2fb | ||
|
|
bab0f55127 | ||
|
|
821b7b31cf | ||
|
|
8bdb7f9a0d | ||
|
|
7f314e25ce | ||
|
|
1b5f71f1fb | ||
|
|
65092ce621 | ||
|
|
2f38aa9f73 | ||
|
|
e30bf35f60 | ||
|
|
c2623f407b | ||
|
|
d26be8cbca | ||
|
|
e3f710518e | ||
|
|
31551a3b03 | ||
|
|
dee699eede | ||
|
|
dc74fa2aeb | ||
|
|
9f8b7ed137 | ||
|
|
6eb6a632c9 | ||
|
|
371e7389ec | ||
|
|
a49bbf288e | ||
|
|
f003563fcd | ||
|
|
b0eb063b35 | ||
|
|
51c140f4ff | ||
|
|
b0ed0c81e4 | ||
|
|
d7e7ac746f | ||
|
|
1535b5a932 | ||
|
|
5db0bffc2f | ||
|
|
3446841376 | ||
|
|
c0d5cd54c5 | ||
|
|
10b38b4d2b | ||
|
|
09acac006e | ||
|
|
eb606dd1b7 | ||
|
|
1282722ecf | ||
|
|
d1ca02310d | ||
|
|
4fa3f6a15d | ||
|
|
b6285fb7eb | ||
|
|
4ab7f66cc8 | ||
|
|
81272cb468 | ||
|
|
9ed202928e | ||
|
|
809eb996ca | ||
|
|
62b27d3989 | ||
|
|
b444c89ab0 | ||
|
|
eafb82943b | ||
|
|
a2243413e6 | ||
|
|
8452c8cc7f | ||
|
|
bd4df5fad0 | ||
|
|
3d25fe2e3d | ||
|
|
9093d35699 | ||
|
|
b718da4997 | ||
|
|
3c1f6d96f6 | ||
|
|
6e46adeae2 | ||
|
|
a25c71d57e | ||
|
|
de99fd0caa | ||
|
|
fe6e84553f | ||
|
|
411a35656e | ||
|
|
125e883264 | ||
|
|
cff888bbaa | ||
|
|
ca8aa8aef2 | ||
|
|
240dcdc693 | ||
|
|
b35a103a9d | ||
|
|
40edc5b23c | ||
|
|
30e470ec12 | ||
|
|
15060b8da4 | ||
|
|
62bbb917ca | ||
|
|
28d95ecf0c | ||
|
|
117fb91d34 | ||
|
|
6227904dbd | ||
|
|
1b07ccd1b8 | ||
|
|
d8576eeb3b | ||
|
|
7579eb32c9 | ||
|
|
edc818f036 | ||
|
|
3f4d577042 | ||
|
|
df23247bc8 | ||
|
|
c9246e28f4 | ||
|
|
4b2cc48687 | ||
|
|
3c910d2651 | ||
|
|
2f4f364a27 | ||
|
|
46714ce661 | ||
|
|
f768ba1c75 | ||
|
|
d5969dfb94 | ||
|
|
5bf0ff54d0 | ||
|
|
2def15c3f0 | ||
|
|
ac257b0ee9 | ||
|
|
3bdcae8928 | ||
|
|
c95ab91de1 | ||
|
|
b51338e308 | ||
|
|
29cfcec850 | ||
|
|
7c89e72c98 | ||
|
|
5c938034c8 | ||
|
|
a5f68ad7a6 | ||
|
|
b993e42c24 | ||
|
|
79d02d1f3b | ||
|
|
709fe826dd | ||
|
|
f3a53ab812 | ||
|
|
fc4f4fb5ed | ||
|
|
b74f76bdc4 | ||
|
|
0d7b7d0766 | ||
|
|
a5835794e5 | ||
|
|
db8c62eaf5 | ||
|
|
faaaa4b62a | ||
|
|
83232d67d1 | ||
|
|
5fd5b76e47 | ||
|
|
b57c18b664 | ||
|
|
888c2bc308 | ||
|
|
b2ab00eec7 | ||
|
|
5e6e2fdd83 | ||
|
|
1b10de0da7 | ||
|
|
61587437e5 | ||
|
|
bf21910fe4 | ||
|
|
99aa716c89 | ||
|
|
5484856fc0 | ||
|
|
9338a6ea61 | ||
|
|
65b2525ad7 | ||
|
|
13727dace4 | ||
|
|
b47c3f4b94 | ||
|
|
02dfb6319f | ||
|
|
9e28f2da81 | ||
|
|
749d2c0b66 | ||
|
|
67311104a5 | ||
|
|
bdd476b87e | ||
|
|
29bbdd4694 | ||
|
|
cd6bcb0890 | ||
|
|
4b6f25a743 | ||
|
|
26937bdd2b | ||
|
|
addb6db73e | ||
|
|
9c8d50656f | ||
|
|
d1333c2732 | ||
|
|
26b618aef7 | ||
|
|
05b60c9c7b | ||
|
|
d630bf5250 | ||
|
|
d3bcc68e2a | ||
|
|
694e78eb39 | ||
|
|
883acbc190 | ||
|
|
70b021c228 | ||
|
|
178acf2113 | ||
|
|
bcc475d13c | ||
|
|
c2e9390a36 | ||
|
|
c390f35b95 | ||
|
|
ccb013a4ab | ||
|
|
6f83f57737 | ||
|
|
c01fefe638 | ||
|
|
a96f2e5227 | ||
|
|
75c7e1e350 | ||
|
|
cfd21dd69e | ||
|
|
0d1dfae3d7 | ||
|
|
e7157ad835 | ||
|
|
8fd826dfbf | ||
|
|
080361c668 | ||
|
|
cc8ba26bbb | ||
|
|
70dcb6902d | ||
|
|
71050660b8 | ||
|
|
817e3b340d | ||
|
|
a5e64f7c80 | ||
|
|
ec69300bc1 | ||
|
|
946a396b33 | ||
|
|
095cc69a45 | ||
|
|
479c2f7cc0 | ||
|
|
4c5181bddb | ||
|
|
704c150d70 | ||
|
|
08d5a2a44f | ||
|
|
4f575e6708 | ||
|
|
1d7c4785fa | ||
|
|
771f947918 | ||
|
|
7023ecd83b | ||
|
|
54300f99ab | ||
|
|
efe2804020 | ||
|
|
7b2c295670 | ||
|
|
b872e07a4a | ||
|
|
8f152bd7ad | ||
|
|
1ce8a82200 | ||
|
|
05c5bae7bf | ||
|
|
5c0587765f | ||
|
|
9e140eecfa | ||
|
|
4a46614e33 | ||
|
|
2bef31bee0 | ||
|
|
7feb9341b3 | ||
|
|
33e88aa7ad | ||
|
|
47335190e0 | ||
|
|
b02c99b9ef | ||
|
|
96f6292915 | ||
|
|
59142c46bd | ||
|
|
d06558db12 | ||
|
|
1eb05fe75c | ||
|
|
07ed6bfb6e | ||
|
|
bb52bc02ac | ||
|
|
e4766b309a | ||
|
|
6a3cfb85c3 | ||
|
|
e5fbfcf0c8 | ||
|
|
5026449f49 | ||
|
|
daf5b446a0 | ||
|
|
70ce649d6c | ||
|
|
4f2da87ca7 | ||
|
|
67e13d9c40 | ||
|
|
0f85bd80c3 | ||
|
|
b9a60afd77 | ||
|
|
f9957edc77 | ||
|
|
c828c72ec6 | ||
|
|
4976d446a2 | ||
|
|
1594ac92c5 | ||
|
|
3787362e97 | ||
|
|
97a9330f71 | ||
|
|
a52d97b44d | ||
|
|
b6d7817bd6 | ||
|
|
06c2bcbac1 | ||
|
|
df786b2dfc | ||
|
|
5ba2a73a37 | ||
|
|
6118eb20cf | ||
|
|
2f0bb591ca | ||
|
|
a9a687a780 | ||
|
|
bf27c77c86 | ||
|
|
217f5f70e3 | ||
|
|
43025ad7cb | ||
|
|
16d03b0cdc | ||
|
|
6ba83329a3 | ||
|
|
247119adc3 | ||
|
|
e72bd42913 | ||
|
|
3e5c4abeef | ||
|
|
057732b2df | ||
|
|
69bf849af8 | ||
|
|
0515f4695a | ||
|
|
9463b1b68e | ||
|
|
50bb49fd0c | ||
|
|
d2cb937744 | ||
|
|
d212192b27 | ||
|
|
775a6e38de | ||
|
|
2e66d2bc6d | ||
|
|
8fb1804c8a | ||
|
|
47dde022a3 | ||
|
|
d77488e91d | ||
|
|
eee3fa1285 | ||
|
|
0ce54fea42 | ||
|
|
04bc1180af | ||
|
|
710e747f05 | ||
|
|
c72436d718 | ||
|
|
3ae5df47b8 | ||
|
|
9a80d45519 | ||
|
|
7764148c77 | ||
|
|
c5866395f4 | ||
|
|
b01f679eb2 | ||
|
|
9fb31e3339 | ||
|
|
335592d8b3 | ||
|
|
fd4e49dac7 | ||
|
|
a4376f597e | ||
|
|
45596d0efd | ||
|
|
54c4bfea26 | ||
|
|
4bf54a7633 | ||
|
|
f86e08b979 | ||
|
|
e3b3688c26 | ||
|
|
d109c7eb05 | ||
|
|
85d89a8dcd | ||
|
|
deabbdd18a | ||
|
|
477e642582 | ||
|
|
99e8701696 | ||
|
|
f2745a149c | ||
|
|
1c84b89c47 | ||
|
|
bea1969e5c |
@@ -313,6 +313,8 @@ miniapps/nurbs/nurbs_ex1
|
||||
miniapps/nurbs/nurbs_ex1p
|
||||
miniapps/nurbs/nurbs_ex3
|
||||
miniapps/nurbs/nurbs_ex5
|
||||
miniapps/nurbs/nurbs_ex10
|
||||
miniapps/nurbs/nurbs_ex10p
|
||||
miniapps/nurbs/nurbs_ex11p
|
||||
miniapps/nurbs/nurbs_ex24
|
||||
miniapps/nurbs/nurbs_solenoidal
|
||||
@@ -338,7 +340,14 @@ miniapps/nurbs/nurbs_naca_cmesh
|
||||
miniapps/nurbs/naca-cmesh.mesh
|
||||
miniapps/nurbs/glvis_naca-cmesh.mesh
|
||||
miniapps/nurbs/Naca_cmesh
|
||||
miniapps/nurbs/nurbs_mesh_info
|
||||
miniapps/nurbs/k*_*.dat
|
||||
miniapps/nurbs/*-Surface.mesh
|
||||
miniapps/nurbs/*.mesh
|
||||
miniapps/nurbs/*.sol
|
||||
miniapps/nurbs/deformed.*
|
||||
miniapps/nurbs/elastic_energy.*
|
||||
miniapps/nurbs/velocity.*
|
||||
|
||||
miniapps/performance/ex1
|
||||
miniapps/performance/ex1p
|
||||
|
||||
@@ -11,6 +11,17 @@
|
||||
Version 4.9.1 (development)
|
||||
===========================
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Improved the gridfunction projection routines. Projections work for Scalar,
|
||||
Vector and VectorFE, also NURBS versions. Optionally different types of
|
||||
projections can be selected, default behaviour has not changed.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- Improved support for 1D NURBS meshes with variable order, including using
|
||||
the patches construct for 1D NURBS meshes.
|
||||
|
||||
|
||||
Version 4.9, released on Dec 11, 2025
|
||||
=====================================
|
||||
|
||||
+11
-5
@@ -723,6 +723,7 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
# Declaring the library
|
||||
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
|
||||
target_compile_features(mfem PUBLIC cxx_std_${CMAKE_CXX_STANDARD})
|
||||
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
|
||||
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES} ${TPL_TARGETS})
|
||||
if (TPL_TARGETS)
|
||||
@@ -869,11 +870,12 @@ add_dependencies(exec
|
||||
# - https://cmake.org/Bug/view.php?id=8438
|
||||
|
||||
# Add a target to copy the mfem data directory to the build directory
|
||||
add_custom_command(OUTPUT data_is_copied
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/data data
|
||||
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
|
||||
COMMENT "Copying the data directory ...")
|
||||
add_custom_target(copy_data DEPENDS data_is_copied)
|
||||
# Implementable as a single copy_directory_if_different command w/ CMake >= 3.26
|
||||
file(GLOB DATA_FILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/data/*)
|
||||
add_custom_target(copy_data
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory data
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_FILES} data
|
||||
COMMENT "Syncing the data directory ...")
|
||||
# Add 'copy_data' as a prerequisite for all executables, if the source and the
|
||||
# build directories are not the same.
|
||||
if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
|
||||
@@ -1005,6 +1007,10 @@ install(FILES
|
||||
install(EXPORT ${PROJECT_NAME_UC}Targets
|
||||
DESTINATION ${INSTALL_CMAKE_DIR})
|
||||
|
||||
# Install the data directory if present, i.e. if the copy_data target is built
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
DESTINATION ${MFEM_INSTALL_DIR} OPTIONAL)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# Create 'config.mk' from 'config.mk.in' for the build and install locations and
|
||||
# define install rules for 'config.mk' and 'test.mk'
|
||||
|
||||
@@ -725,7 +725,9 @@ The specific libraries and their options are:
|
||||
URL: https://ginkgo-project.github.io
|
||||
Options: GINKGO_OPT, GINKGO_LIB, GINKGO_DIR, GINKGO_BUILD_TYPE (Release or
|
||||
Debug).
|
||||
Versions: Ginkgo >= 1.9.0.
|
||||
Versions: Ginkgo >= 1.9.0. When building Ginkgo with distributed support, a
|
||||
recent version of the "develop" branch is required (1.11 as defined
|
||||
in include/ginkgo/config.hpp).
|
||||
|
||||
- AmgX (optional), used when MFEM_USE_AMGX = YES.
|
||||
URL: https://github.com/NVIDIA/AMGX
|
||||
|
||||
+1
-1
@@ -407,7 +407,7 @@ AMGX_LIB = -L$(AMGX_DIR)/lib -lamgx -lcusparse -lcusolver -lcublas -lnvToolsExt
|
||||
# MAGMA library configuration
|
||||
MAGMA_DIR = @MFEM_DIR@/../magma
|
||||
MAGMA_OPT = -I$(MAGMA_DIR)/include
|
||||
MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a -lcublas -lcusparse $(LAPACK_LIB)
|
||||
MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a $(LAPACK_LIB)
|
||||
|
||||
# GnuTLS library configuration
|
||||
GNUTLS_OPT =
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
# Four segments with different NURBS orders, described via patches.
|
||||
elements
|
||||
4
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
4 1 6 7
|
||||
|
||||
boundary
|
||||
0
|
||||
|
||||
edges
|
||||
4
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
3 6 7
|
||||
|
||||
vertices
|
||||
8
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: linear (order 1, 3 spans)
|
||||
knotvectors
|
||||
1
|
||||
1 4 0 0 .4 .6 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 1.0
|
||||
0.6 0.4 1.0
|
||||
0.4 0.6 1.0
|
||||
1.0 1.0 1.0
|
||||
|
||||
# Patch 1: quadratic (order 2, 2 spans)
|
||||
knotvectors
|
||||
1
|
||||
2 4 0 0 0 .5 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 1.0
|
||||
1.9 0.0 1.21
|
||||
2.0 0.9 1.22
|
||||
2.0 1.0 1.0
|
||||
|
||||
# Patch 2: cubic (order 3, 3 spans)
|
||||
knotvectors
|
||||
1
|
||||
3 6 0 0 0 0 .33 .66 1 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 1.0
|
||||
2.1 0.2 1.31
|
||||
3.5 0.4 1.32
|
||||
2.5 0.6 1.33
|
||||
2.9 1.0 1.34
|
||||
3.0 1.0 1.0
|
||||
|
||||
# Patch 3: quartic (order 4, 1 span)
|
||||
knotvectors
|
||||
1
|
||||
4 5 0 0 0 0 0 1 1 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
3.0 0.0 1.0
|
||||
3.45 0.5 1.41
|
||||
3.50 1.0 1.42
|
||||
3.75 0.8 1.43
|
||||
4.0 0.0 1.0
|
||||
@@ -0,0 +1,79 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
# Three segments with different NURBS orders, described via patches.
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: linear (order 1, 2 control points)
|
||||
knotvectors
|
||||
1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 1.0
|
||||
1.0 1.0 1.0
|
||||
|
||||
# Patch 1: quadratic (order 2, 3 control points)
|
||||
knotvectors
|
||||
1
|
||||
2 3 0 0 0 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 1.0
|
||||
1.02 1.02 1.2
|
||||
2.0 1.0 1.0
|
||||
|
||||
# Patch 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 1.0
|
||||
2.03 0.83 1.31
|
||||
2.33 1.03 1.32
|
||||
3.0 1.0 1.0
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
# Edge 0: linear (order 1, 2 control points)
|
||||
# Edge 1: quadratic (order 2, 3 control points)
|
||||
# Edge 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
3
|
||||
1 2 0 0 1 1
|
||||
2 3 0 0 0 1 1 1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
# One weight per control point, in the same order as the control points; (2 + 3 + 4) = 9 weights total
|
||||
weights
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1.2
|
||||
1.31
|
||||
1.32
|
||||
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: NURBS
|
||||
VDim: 2
|
||||
Ordering: 1
|
||||
|
||||
0.0 0.0
|
||||
1.0 1.0
|
||||
1.0 0.0
|
||||
2.0 1.0
|
||||
2.0 0.0
|
||||
3.0 1.0
|
||||
1.02 1.02
|
||||
2.03 0.83
|
||||
2.33 1.03
|
||||
@@ -0,0 +1,79 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
# Three segments with different NURBS orders, described via patches.
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: linear (order 1, 2 control points)
|
||||
knotvectors
|
||||
1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 0.01 1.0
|
||||
1.0 1.0 1.01 1.0
|
||||
|
||||
# Patch 1: quadratic (order 2, 3 control points)
|
||||
knotvectors
|
||||
1
|
||||
2 3 0 0 0 1 1 1
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 0.02 1.0
|
||||
1.02 1.02 0.52 1.2
|
||||
2.0 1.0 1.02 1.0
|
||||
|
||||
# Patch 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 0.03 1.0
|
||||
2.03 0.83 0.33 1.31
|
||||
2.33 1.03 0.63 1.32
|
||||
3.0 1.0 1.03 1.0
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see fem/geom.hpp):
|
||||
#
|
||||
# SEGMENT = 1
|
||||
# SQUARE = 3
|
||||
# CUBE = 5
|
||||
#
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
elements
|
||||
3
|
||||
1 1 0 1
|
||||
2 1 2 3
|
||||
3 1 4 5
|
||||
|
||||
boundary
|
||||
6
|
||||
1 0 0
|
||||
1 0 1
|
||||
1 0 2
|
||||
1 0 3
|
||||
1 0 4
|
||||
1 0 5
|
||||
|
||||
edges
|
||||
3
|
||||
0 0 1
|
||||
1 2 3
|
||||
2 4 5
|
||||
|
||||
vertices
|
||||
6
|
||||
|
||||
# Edge 0: linear (order 1, 2 control points)
|
||||
# Edge 1: quadratic (order 2, 3 control points)
|
||||
# Edge 2: cubic (order 3, 4 control points)
|
||||
knotvectors
|
||||
3
|
||||
1 2 0 0 1 1
|
||||
2 3 0 0 0 1 1 1
|
||||
3 4 0 0 0 0 1 1 1 1
|
||||
|
||||
# One weight per control point, in the same order as the control points; (2 + 3 + 4) = 9 weights total
|
||||
weights
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1.2
|
||||
1.31
|
||||
1.32
|
||||
|
||||
FiniteElementSpace
|
||||
FiniteElementCollection: NURBS
|
||||
VDim: 3
|
||||
Ordering: 1
|
||||
|
||||
0.0 0.0 0.01
|
||||
1.0 1.0 1.01
|
||||
1.0 0.0 0.02
|
||||
2.0 1.0 1.02
|
||||
2.0 0.0 0.03
|
||||
3.0 1.0 1.03
|
||||
1.02 1.02 0.52
|
||||
2.03 0.83 0.33
|
||||
2.33 1.03 0.63
|
||||
@@ -117,6 +117,10 @@ namespace mfem {
|
||||
* - <a class="el" href="ex39p_8cpp_source.html">Example 39p</a>: parallel named mesh attributes
|
||||
* - <a class="el" href="ex40_8cpp_source.html">Example 40</a>: eikonal equation
|
||||
* - <a class="el" href="ex40p_8cpp_source.html">Example 40p</a>: parallel eikonal equation
|
||||
* - <a class="el" href="ex41_8cpp_source.html">Example 41</a>: DG/CG IMEX time-dependent advection-diffusion
|
||||
* - <a class="el" href="ex41p_8cpp_source.html">Example 41p</a>: parallel DG/CG IMEX time-dependent advection-diffusion
|
||||
* - <a class="el" href="ex43_8cpp_source.html">Example 43</a>: sliding boundary conditions in linear elasticity
|
||||
* - <a class="el" href="ex43p_8cpp_source.html">Example 43p</a>: parallel sliding boundary conditions in linear elasticity
|
||||
*
|
||||
* <H4>AmgX Examples</H4>
|
||||
* - Variants of Examples
|
||||
@@ -188,6 +192,8 @@ namespace mfem {
|
||||
* <a class="el" href="nurbs__ex1p_8cpp_source.html">1p</a>,
|
||||
* <a class="el" href="nurbs__ex3_8cpp_source.html">3</a>,
|
||||
* <a class="el" href="nurbs__ex5_8cpp_source.html">5</a>,
|
||||
* <a class="el" href="nurbs__ex10_8cpp_source.html">10</a>,
|
||||
* <a class="el" href="nurbs__ex10p_8cpp_source.html">10p</a>,
|
||||
* <a class="el" href="nurbs__ex11p_8cpp_source.html">11p</a>, and
|
||||
* <a class="el" href="nurbs__ex24_8cpp_source.html">24</a>,
|
||||
* demonstrating howto perform NURBS-based Isogeometric Analysis.
|
||||
@@ -196,6 +202,7 @@ namespace mfem {
|
||||
* - <a class="el" href="nurbs__curveint_8cpp_source.html">NURBS Interpolation</a>: NURBS interpolation of given geometry
|
||||
* - <a class="el" href="nurbs__naca__cmesh_8cpp_source.html">NURBS NACA Mesher</a>: generate NURBS based mesh around a NACA foil
|
||||
* - <a class="el" href="nurbs__printfunc_8cpp_source.html">NURBS Printer</a>: print the NURBS-basis
|
||||
* - <a class="el" href="nurbs__mesh_info_8cpp_source.html">NURBS Mesh info</a>: print the info of a NURBS mesh
|
||||
*
|
||||
* <H3>Miniapps</H3>
|
||||
* - <a class="el" href="volta_8cpp_source.html">Volta</a>: simple electrostatics simulation code
|
||||
@@ -234,7 +241,8 @@ namespace mfem {
|
||||
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Poisson problem
|
||||
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Poisson problem
|
||||
* - <a class="el" href="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
|
||||
* - <a class="el" href="contact-patch-test_8cpp_source.html">Contact</a>: mortar contact patch test for elasticity
|
||||
* - <a class="el" href="contact-patch-test_8cpp_source.html">Tribol</a>: mortar contact patch test for elasticity
|
||||
* - <a class="el" href="contact_8cpp_source.html">Contact</a>: Frictionless contact examples using <a class="el" href="classmfem_1_1IPSolver.html#details">IP optimization</a> and the <a class="el" href="classmfem_1_1AMGFSolver.html#details">AMGF solver</a>
|
||||
* - <a class="el" href="multidomain_8cpp_source.html">Multidomain miniapp</a>: Multidomain and Submesh demonstration miniapp
|
||||
* - <a class="el" href="pdiffusion_8cpp_source.html">DPG Diffusion example</a>: DPG formulation for the diffusion problem
|
||||
* - <a class="el" href="pmaxwell_8cpp_source.html">DPG Maxwell example</a>: DPG formulation for the indefinite Maxwell problem
|
||||
|
||||
@@ -47,6 +47,7 @@ list(APPEND ALL_EXE_SRCS
|
||||
ex39.cpp
|
||||
ex40.cpp
|
||||
ex41.cpp
|
||||
ex43.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
@@ -91,6 +92,7 @@ if (MFEM_USE_MPI)
|
||||
ex39p.cpp
|
||||
ex40p.cpp
|
||||
ex41p.cpp
|
||||
ex43p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
+29
-6
@@ -105,6 +105,7 @@ int main(int argc, char *argv[])
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
bool solve_implicit_state = false;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
@@ -126,6 +127,9 @@ int main(int argc, char *argv[])
|
||||
"Alpha coefficient.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"Kappa coefficient offset.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -179,6 +183,11 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 7. Initialize the conduction operator and the visualization.
|
||||
ConductionOperator oper(fespace, alpha, kappa, u);
|
||||
using ImplicitVariableType = ConductionOperator::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
oper.SetImplicitVariableType(imp_var);
|
||||
|
||||
u_gf.SetFromTrueDofs(u);
|
||||
{
|
||||
@@ -316,11 +325,14 @@ void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
}
|
||||
|
||||
void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
const Vector &u, Vector &du_dt)
|
||||
const Vector &u, Vector &k)
|
||||
{
|
||||
// Solve the equation:
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
// M*k = -K(u + dt*k) for k = du/dt, if solving for stage-slope
|
||||
// or
|
||||
// M*k = -dt*K(k) + M*u for k = u_s, if solving for stage-state
|
||||
// where K is linearized by using u from the previous timestep, and
|
||||
// the stage-state and slope relation: du/dt = (u_s - u)/dt.
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
@@ -328,9 +340,20 @@ void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
T_solver.SetOperator(*T);
|
||||
}
|
||||
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
T_solver.Mult(z, du_dt);
|
||||
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u_s
|
||||
Mmat.Mult(u, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
}
|
||||
T_solver.Mult(z, k);
|
||||
}
|
||||
|
||||
void ConductionOperator::SetParameters(const Vector &u)
|
||||
|
||||
+29
-6
@@ -115,6 +115,7 @@ int main(int argc, char *argv[])
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
bool adios2 = false;
|
||||
bool solve_implicit_state = false;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
@@ -138,6 +139,9 @@ int main(int argc, char *argv[])
|
||||
"Alpha coefficient.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"Kappa coefficient offset.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -212,6 +216,11 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 9. Initialize the conduction operator and the VisIt visualization.
|
||||
ConductionOperator oper(fespace, alpha, kappa, u);
|
||||
using ImplicitVariableType = ConductionOperator::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
oper.SetImplicitVariableType(imp_var);
|
||||
|
||||
u_gf.SetFromTrueDofs(u);
|
||||
{
|
||||
@@ -407,11 +416,14 @@ void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
}
|
||||
|
||||
void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
const Vector &u, Vector &du_dt)
|
||||
const Vector &u, Vector &k)
|
||||
{
|
||||
// Solve the equation:
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
// M*k = -K(u + dt*k) for k = du/dt, if solving for stage-slope
|
||||
// or
|
||||
// M*k = -dt*K(k) + M*u for k = u_s, if solving for stage-state
|
||||
// where K is linearized by using u from the previous timestep, and
|
||||
// the stage-state and slope relation: du/dt = (u_s - u)/dt.
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
@@ -419,9 +431,20 @@ void ConductionOperator::ImplicitSolve(const real_t dt,
|
||||
T_solver.SetOperator(*T);
|
||||
}
|
||||
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
T_solver.Mult(z, du_dt);
|
||||
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u
|
||||
Mmat.Mult(u, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg();
|
||||
}
|
||||
T_solver.Mult(z, k);
|
||||
}
|
||||
|
||||
void ConductionOperator::SetParameters(const Vector &u)
|
||||
|
||||
+1
-1
@@ -119,7 +119,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
LinearForm b(&fespace);
|
||||
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
|
||||
// 6. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the linear elasticity integrator with piece-wise
|
||||
|
||||
+1
-1
@@ -140,7 +140,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
ParLinearForm b(&fespace);
|
||||
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
|
||||
|
||||
// 6. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the linear elasticity integrator with piece-wise
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
// MFEM Example 43
|
||||
//
|
||||
// Compile with: make ex43
|
||||
//
|
||||
// Sample runs: ex43 -m ../data/ball-nurbs.mesh -r 2
|
||||
// ex43 -m ../data/ref-cube.mesh -r 2
|
||||
// ex43 -m ../data/fichera.mesh
|
||||
// ex43 -m ../data/star.mesh
|
||||
//
|
||||
// Description: This example code solves a linear elasticity problem using
|
||||
// Nitsche's method to enforce sliding boundary conditions. In
|
||||
// particular, we consider a linear elastic body that is displaced
|
||||
// in the normal direction on the entire boundary, but is free to
|
||||
// slide in the tangential direction. This is achieved by imposing
|
||||
// homogeneous Dirichlet boundary conditions on the normal
|
||||
// component of the displacement, while applying homogeneous
|
||||
// Neumann boundary conditions on the tangential components of the
|
||||
// displacement. By enforcing a uniform, constant normal
|
||||
// displacement on the boundary, we can simulate the effect of
|
||||
// compressing or expanding the elastic body uniformly. These
|
||||
// boundary conditions are applied weakly using Nitsche's method,
|
||||
// allowing for more flexibility in handling complex geometries in
|
||||
// either 2D or 3D.
|
||||
//
|
||||
// The strong form is given by:
|
||||
//
|
||||
// −Div(σ(u)) = 0 in Ω
|
||||
// u ⋅ n = g on Γ
|
||||
// σ(u) ⊥ n on Γ
|
||||
//
|
||||
// where σ(u) = λ tr(ε(u)) I + 2μ ε(u) is the stress tensor, ε(u)
|
||||
// is the strain tensor, λ and μ are the Lamé parameters, and g is
|
||||
// the prescribed displacement on the boundary. Here, n is the
|
||||
// outward normal on the boundary Γ = ∂Ω.
|
||||
//
|
||||
// The weak form using Nitsche's method is:
|
||||
//
|
||||
// Find u ∈ V such that a(u,v) = b(v) for all v ∈ V
|
||||
//
|
||||
// where
|
||||
//
|
||||
// a(u,v) := ∫_Ω σ(u) : ε(v) dx
|
||||
// - ∫_Γ (σ(u) n ⋅ n) (v ⋅ n) dS
|
||||
// - ∫_Γ (σ(v) n ⋅ n) (u ⋅ n) dS
|
||||
// + κ ∫_Γ h⁻¹ (λ + 2μ) (u ⋅ n) (v ⋅ n) dS,
|
||||
//
|
||||
// b(v) := - ∫_Γ σ(v) n ⋅ n g dS
|
||||
// + κ ∫_Γ h⁻¹ (λ + 2μ) (v ⋅ n) g dS,
|
||||
//
|
||||
// with κ > 0 being a penalty parameter. Here, h is a
|
||||
// characteristic element size on the boundary. The function
|
||||
// space V is a vector H1-conforming finite element space.
|
||||
//
|
||||
// This example can be viewed as an alternative to Example 28.
|
||||
// Whereas Example 28 imposes sliding boundary conditions using
|
||||
// the general-purpose constrained system solvers found in
|
||||
// mfem/linalg/constraints.hpp, this example employs Nitsche's
|
||||
// method to weakly enforce the same condition by modifying the
|
||||
// underlying variational formulation. Unlike Example 28, the
|
||||
// approach here is specialized to isotropic linear elasticity,
|
||||
// but it has the advantage of producing a well-conditioned SPD
|
||||
// stiffness matrix that can be readily preconditioned with
|
||||
// standard AMG. We recommend reviewing Example 2 before working
|
||||
// through this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
real_t displ_mag = 0.1;
|
||||
int order = 1;
|
||||
int ref_levels = 0;
|
||||
real_t lambda = 1.0;
|
||||
real_t mu = 1.0;
|
||||
real_t kappa = -1.0;
|
||||
bool static_cond = false;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&displ_mag, "-g", "--displ",
|
||||
"Magnitude of the normal displacement.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&ref_levels, "-r", "--ref_levels",
|
||||
"Number of uniform mesh refinements.");
|
||||
args.AddOption(&lambda, "-l", "--lambda", "First Lamé parameter.");
|
||||
args.AddOption(&mu, "-mu", "--mu", "Second Lamé parameter.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"The penalty parameter, should be positive."
|
||||
" Negative values are replaced with (order+1)^2.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
if (kappa < 0)
|
||||
{
|
||||
kappa = (order+1)*(order+1);
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral or hexahedral elements with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 3. Select the order of the finite element discretization space. For NURBS
|
||||
// meshes, we increase the order by degree elevation.
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
mesh->DegreeElevate(order, order);
|
||||
}
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement.
|
||||
for (int i = 0; i < ref_levels; i++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 5. Interpolate the geometry after refinement to control geometry error.
|
||||
int curvature_order = max(order, 2);
|
||||
mesh->SetCurvature(curvature_order);
|
||||
|
||||
// 6. Define a finite element space on the mesh. Here we use vector finite
|
||||
// elements, i.e. dim copies of a scalar finite element space. The vector
|
||||
// dimension is specified by the last argument of the FiniteElementSpace
|
||||
// constructor. For NURBS meshes, we use the (degree elevated) NURBS space
|
||||
// associated with the mesh nodes.
|
||||
FiniteElementCollection *fec;
|
||||
FiniteElementSpace *fespace;
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
fec = NULL;
|
||||
fespace = mesh->GetNodes()->FESpace();
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
fespace = new FiniteElementSpace(mesh, fec, dim);
|
||||
}
|
||||
cout << "Number of finite element unknowns: " << fespace->GetTrueVSize()
|
||||
<< endl << "Assembling: " << flush;
|
||||
|
||||
// 7. Mark the boundary attributes where the sliding (Nitsche) boundary
|
||||
// conditions are to be applied. These b.c. are imposed weakly, by adding
|
||||
// the appropriate boundary integrators over the marked 'ess_bdr' to the
|
||||
// bilinear and linear forms. Thus, no dofs are eliminated; there are no
|
||||
// essential boundary conditions.
|
||||
Array<int> ess_tdof_list, ess_bdr(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
|
||||
// 8. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
GridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 9. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the linear elasticity integrator with constant
|
||||
// coefficients lambda and mu.
|
||||
ConstantCoefficient lambda_c(lambda);
|
||||
ConstantCoefficient mu_c(mu);
|
||||
|
||||
BilinearForm *a = new BilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new ElasticityIntegrator(lambda_c,mu_c));
|
||||
a->AddBdrFaceIntegrator(
|
||||
new SlidingElasticityIntegrator(lambda_c, mu_c, kappa),
|
||||
ess_bdr);
|
||||
|
||||
// 10. Set up the linear form b(.) corresponding to the Nitsche method
|
||||
// to impose the Dirichlet boundary conditions. Here, we set the
|
||||
// prescribed displacement on the Dirichlet boundary to be a constant
|
||||
// normal displacement of magnitude 'displ_mag'.
|
||||
ConstantCoefficient g(displ_mag);
|
||||
|
||||
LinearForm *b = new LinearForm(fespace);
|
||||
b->AddBdrFaceIntegrator(
|
||||
new SlidingElasticityLFIntegrator(
|
||||
g, lambda_c, mu_c, kappa), ess_bdr);
|
||||
b->Assemble();
|
||||
|
||||
// 11. Assemble the bilinear form and the corresponding linear system,
|
||||
// applying any necessary transformations such as: eliminating boundary
|
||||
// conditions, applying conforming constraints for non-conforming AMR,
|
||||
// static condensation, etc.
|
||||
cout << "matrix ... " << flush;
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
SparseMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
cout << "done." << endl;
|
||||
|
||||
cout << "Size of linear system: " << A.Height() << endl;
|
||||
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// 12. Define a simple symmetric Gauss-Seidel preconditioner and use it to
|
||||
// solve the system Ax=b with PCG.
|
||||
GSSmoother M(A);
|
||||
PCG(A, M, B, X, 1, 500, 1e-12, 0.0);
|
||||
#else
|
||||
// 12. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(A);
|
||||
umf_solver.Mult(B, X);
|
||||
#endif
|
||||
|
||||
// 13. Recover the solution as a finite element grid function.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 14. For non-NURBS meshes, make the mesh curved based on the finite element
|
||||
// space. This means that we define the mesh elements through a fespace
|
||||
// based transformation of the reference element. This allows us to save
|
||||
// the displaced mesh as a curved mesh when using high-order finite
|
||||
// element displacement field. We assume that the initial mesh (read from
|
||||
// the file) is not higher order curved mesh compared to the chosen FE
|
||||
// space.
|
||||
if (!mesh->NURBSext)
|
||||
{
|
||||
mesh->SetNodalFESpace(fespace);
|
||||
}
|
||||
|
||||
// 15. Save the displaced mesh and the inverted solution (which gives the
|
||||
// backward displacements to the original grid). This output can be
|
||||
// viewed later using GLVis: "glvis -m displaced.mesh -g sol.gf".
|
||||
{
|
||||
GridFunction *nodes = mesh->GetNodes();
|
||||
*nodes += x;
|
||||
x *= -1;
|
||||
ofstream mesh_ofs("displaced.mesh");
|
||||
mesh_ofs.precision(8);
|
||||
mesh->Print(mesh_ofs);
|
||||
ofstream sol_ofs("sol.gf");
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the above data by socket to a GLVis server. Use the "n" and "b"
|
||||
// keys in GLVis to visualize the displacements.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *mesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
delete a;
|
||||
delete b;
|
||||
if (fec)
|
||||
{
|
||||
delete fespace;
|
||||
delete fec;
|
||||
}
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,332 @@
|
||||
// MFEM Example 43 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex43p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex43p -m ../data/ball-nurbs.mesh -r 2
|
||||
// mpirun -np 4 ex43p -m ../data/ref-cube.mesh -r 2
|
||||
// mpirun -np 4 ex43p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex43p -m ../data/star.mesh
|
||||
//
|
||||
// Description: This example code solves a linear elasticity problem using
|
||||
// Nitsche's method to enforce sliding boundary conditions. In
|
||||
// particular, we consider a linear elastic body that is displaced
|
||||
// in the normal direction on the entire boundary, but is free to
|
||||
// slide in the tangential direction. This is achieved by imposing
|
||||
// homogeneous Dirichlet boundary conditions on the normal
|
||||
// component of the displacement, while applying homogeneous
|
||||
// Neumann boundary conditions on the tangential components of the
|
||||
// displacement. By enforcing a uniform, constant normal
|
||||
// displacement on the boundary, we can simulate the effect of
|
||||
// compressing or expanding the elastic body uniformly. These
|
||||
// boundary conditions are applied weakly using Nitsche's method,
|
||||
// allowing for more flexibility in handling complex geometries in
|
||||
// either 2D or 3D.
|
||||
//
|
||||
// The strong form is given by:
|
||||
//
|
||||
// −Div(σ(u)) = 0 in Ω
|
||||
// u ⋅ n = g on Γ
|
||||
// σ(u) ⊥ n on Γ
|
||||
//
|
||||
// where σ(u) = λ tr(ε(u)) I + 2μ ε(u) is the stress tensor, ε(u)
|
||||
// is the strain tensor, λ and μ are the Lamé parameters, and g is
|
||||
// the prescribed displacement on the boundary. Here, n is the
|
||||
// outward normal on the boundary Γ = ∂Ω.
|
||||
//
|
||||
// The weak form using Nitsche's method is:
|
||||
//
|
||||
// Find u ∈ V such that a(u,v) = b(v) for all v ∈ V
|
||||
//
|
||||
// where
|
||||
//
|
||||
// a(u,v) := ∫_Ω σ(u) : ε(v) dx
|
||||
// - ∫_Γ (σ(u) n ⋅ n) (v ⋅ n) dS
|
||||
// - ∫_Γ (σ(v) n ⋅ n) (u ⋅ n) dS
|
||||
// + κ ∫_Γ h⁻¹ (λ + 2μ) (u ⋅ n) (v ⋅ n) dS,
|
||||
//
|
||||
// b(v) := - ∫_Γ σ(v) n ⋅ n g dS
|
||||
// + κ ∫_Γ h⁻¹ (λ + 2μ) (v ⋅ n) g dS,
|
||||
//
|
||||
// with κ > 0 being a penalty parameter. Here, h is a
|
||||
// characteristic element size on the boundary. The function
|
||||
// space V is a vector H1-conforming finite element space.
|
||||
//
|
||||
// This example can be viewed as an alternative to Example 28.
|
||||
// Whereas Example 28 imposes sliding boundary conditions using
|
||||
// the general-purpose constrained system solvers found in
|
||||
// mfem/linalg/constraints.hpp, this example employs Nitsche's
|
||||
// method to weakly enforce the same condition by modifying the
|
||||
// underlying variational formulation. Unlike Example 28, the
|
||||
// approach here is specialized to isotropic linear elasticity,
|
||||
// but it has the advantage of producing a well-conditioned SPD
|
||||
// stiffness matrix that can be readily preconditioned with
|
||||
// standard AMG. We recommend reviewing Example 2 before working
|
||||
// through this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
Mpi::Init(argc, argv);
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
real_t displ_mag = 0.1;
|
||||
int order = 1;
|
||||
int ref_levels = 0;
|
||||
real_t lambda = 1.0;
|
||||
real_t mu = 1.0;
|
||||
real_t kappa = -1.0;
|
||||
bool static_cond = false;
|
||||
bool reorder_space = false;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&displ_mag, "-g", "--displ",
|
||||
"Magnitude of the normal displacement.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&ref_levels, "-r", "--ref_levels",
|
||||
"Number of uniform mesh refinements.");
|
||||
args.AddOption(&lambda, "-l", "--lambda", "First Lamé parameter.");
|
||||
args.AddOption(&mu, "-mu", "--mu", "Second Lamé parameter.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"The penalty parameter, should be positive."
|
||||
" Negative values are replaced with (order+1)^2.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&reorder_space, "-nodes", "--by-nodes", "-vdim", "--by-vdim",
|
||||
"Use byNODES ordering of vector space instead of byVDIM");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (kappa < 0)
|
||||
{
|
||||
kappa = (order+1)*(order+1);
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Read the (serial) mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral or hexahedral elements with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Select the order of the finite element discretization space. For NURBS
|
||||
// meshes, we increase the order by degree elevation.
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
mesh->DegreeElevate(order, order);
|
||||
}
|
||||
|
||||
// 5. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement.
|
||||
for (int i = 0; i < ref_levels; i++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 6. Interpolate the geometry after refinement to control geometry error.
|
||||
int curvature_order = max(order, 2);
|
||||
mesh->SetCurvature(curvature_order);
|
||||
|
||||
// 7. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
|
||||
// 8. Define a finite element space on the mesh. Here we use vector finite
|
||||
// elements, i.e. dim copies of a scalar finite element space. The vector
|
||||
// dimension is specified by the last argument of the FiniteElementSpace
|
||||
// constructor. For NURBS meshes, we use the (degree elevated) NURBS space
|
||||
// associated with the mesh nodes.
|
||||
FiniteElementCollection *fec;
|
||||
ParFiniteElementSpace *fespace;
|
||||
const bool use_nodal_fespace = pmesh->NURBSext;
|
||||
if (use_nodal_fespace)
|
||||
{
|
||||
fec = NULL;
|
||||
fespace = (ParFiniteElementSpace *)pmesh->GetNodes()->FESpace();
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
if (reorder_space)
|
||||
{
|
||||
fespace = new ParFiniteElementSpace(pmesh, fec, dim, Ordering::byNODES);
|
||||
}
|
||||
else
|
||||
{
|
||||
fespace = new ParFiniteElementSpace(pmesh, fec, dim, Ordering::byVDIM);
|
||||
}
|
||||
}
|
||||
HYPRE_BigInt size = fespace->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl
|
||||
<< "Assembling: " << flush;
|
||||
}
|
||||
|
||||
// 9. Mark the boundary attributes where the sliding (Nitsche) boundary
|
||||
// conditions are to be applied. These b.c. are imposed weakly, by adding
|
||||
// the appropriate boundary integrators over the marked 'ess_bdr' to the
|
||||
// bilinear and linear forms. Thus, no dofs are eliminated; there are no
|
||||
// essential boundary conditions.
|
||||
Array<int> ess_tdof_list, ess_bdr;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
}
|
||||
|
||||
// 10. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
ParGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 11. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the linear elasticity integrator with constant
|
||||
// coefficients lambda and mu.
|
||||
ConstantCoefficient lambda_c(lambda);
|
||||
ConstantCoefficient mu_c(mu);
|
||||
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new ElasticityIntegrator(lambda_c,mu_c));
|
||||
a->AddBdrFaceIntegrator(
|
||||
new SlidingElasticityIntegrator(lambda_c, mu_c, kappa),
|
||||
ess_bdr);
|
||||
|
||||
// 12. Set up the linear form b(.) corresponding to the Nitsche method
|
||||
// to impose the Dirichlet boundary conditions. Here, we set the
|
||||
// prescribed displacement on the Dirichlet boundary to be a constant
|
||||
// normal displacement of magnitude 'displ_mag'.
|
||||
ConstantCoefficient g(displ_mag);
|
||||
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
b->AddBdrFaceIntegrator(
|
||||
new SlidingElasticityLFIntegrator(
|
||||
g, lambda_c, mu_c, kappa), ess_bdr);
|
||||
b->Assemble();
|
||||
|
||||
// 13. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (myid == 0) { cout << "matrix ... " << flush; }
|
||||
if (static_cond) { a->EnableStaticCondensation(); }
|
||||
a->Assemble();
|
||||
|
||||
HypreParMatrix A;
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "done." << endl;
|
||||
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
// 14. Define and apply a parallel PCG solver for A X = B with the BoomerAMG
|
||||
// preconditioner from hypre.
|
||||
HypreBoomerAMG *amg = new HypreBoomerAMG(A);
|
||||
if (!a->StaticCondensationIsEnabled())
|
||||
{
|
||||
amg->SetElasticityOptions(fespace);
|
||||
}
|
||||
else
|
||||
{
|
||||
amg->SetSystemsOptions(dim, reorder_space);
|
||||
}
|
||||
HyprePCG *pcg = new HyprePCG(A);
|
||||
pcg->SetTol(1e-8);
|
||||
pcg->SetMaxIter(500);
|
||||
pcg->SetPrintLevel(2);
|
||||
pcg->SetPreconditioner(*amg);
|
||||
pcg->Mult(B, X);
|
||||
|
||||
// 15. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a->RecoverFEMSolution(X, *b, x);
|
||||
|
||||
// 16. For non-NURBS meshes, make the mesh curved based on the finite element
|
||||
// space. This means that we define the mesh elements through a fespace
|
||||
// based transformation of the reference element. This allows us to save
|
||||
// the displaced mesh as a curved mesh when using high-order finite
|
||||
// element displacement field. We assume that the initial mesh (read from
|
||||
// the file) is not higher order curved mesh compared to the chosen FE
|
||||
// space.
|
||||
if (!use_nodal_fespace)
|
||||
{
|
||||
pmesh->SetNodalFESpace(fespace);
|
||||
}
|
||||
|
||||
// 17. Save in parallel the displaced mesh and the inverted solution (which
|
||||
// gives the backward displacements to the original grid). This output
|
||||
// can be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
GridFunction *nodes = pmesh->GetNodes();
|
||||
*nodes += x;
|
||||
x *= -1;
|
||||
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 18. Send the above data by socket to a GLVis server. Use the "n" and "b"
|
||||
// keys in GLVis to visualize the displacements.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 19. Free the used memory.
|
||||
delete pcg;
|
||||
delete amg;
|
||||
delete a;
|
||||
delete b;
|
||||
if (fec)
|
||||
{
|
||||
delete fespace;
|
||||
delete fec;
|
||||
}
|
||||
delete pmesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+20
-1
@@ -160,6 +160,7 @@ int main(int argc, char *argv[])
|
||||
bool paraview = false;
|
||||
bool binary = false;
|
||||
int vis_steps = 5;
|
||||
bool solve_implicit_state = false;
|
||||
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
@@ -187,6 +188,9 @@ int main(int argc, char *argv[])
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -366,6 +370,11 @@ int main(int argc, char *argv[])
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
FE_Evolution adv(m, k, b);
|
||||
using ImplicitVariableType = FE_Evolution::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
adv.SetImplicitVariableType(imp_var);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
@@ -459,7 +468,17 @@ void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
MFEM_VERIFY(dg_solver != NULL,
|
||||
"Implicit time integration is not supported with partial assembly");
|
||||
K.Mult(x, z);
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u
|
||||
M.Mult(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
K.Mult(x, z);
|
||||
}
|
||||
z += b;
|
||||
dg_solver->SetTimeStep(dt);
|
||||
dg_solver->Mult(z, k);
|
||||
|
||||
+20
-1
@@ -257,6 +257,7 @@ int main(int argc, char *argv[])
|
||||
bool adios2 = false;
|
||||
bool binary = false;
|
||||
int vis_steps = 5;
|
||||
bool solve_implicit_state = false;
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
PrecType prec_type = PrecType::AIR;
|
||||
#else
|
||||
@@ -290,6 +291,9 @@ int main(int argc, char *argv[])
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
|
||||
"-imp-slope", "--implicit-slope",
|
||||
"Implicitly solve for stage state or slope.");
|
||||
args.AddOption((int *)&prec_type, "-pt", "--prec-type", "Preconditioner for "
|
||||
"implicit solves. 0 for ILU, 1 for pAIR-AMG.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -536,6 +540,11 @@ int main(int argc, char *argv[])
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
FE_Evolution adv(*m, *k, *B, prec_type);
|
||||
using ImplicitVariableType = FE_Evolution::ImplicitVariableType;
|
||||
ImplicitVariableType imp_var = solve_implicit_state ?
|
||||
ImplicitVariableType::STATE
|
||||
: ImplicitVariableType::SLOPE;
|
||||
adv.SetImplicitVariableType(imp_var);
|
||||
|
||||
real_t t = 0.0;
|
||||
adv.SetTime(t);
|
||||
@@ -676,7 +685,17 @@ FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
|
||||
// (M - dt*K) d = K*u + b
|
||||
void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
|
||||
{
|
||||
K->Mult(x, z);
|
||||
// Construct current right-hand side for stage state vs. slope solve
|
||||
if (ImplicitVarTypeIsState())
|
||||
{
|
||||
// k, on return, is the stage value u
|
||||
M->Mult(x, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
// k, on return, is the stage slope du/dt
|
||||
K->Mult(x, z);
|
||||
}
|
||||
z += b;
|
||||
dg_solver->SetTimeStep(dt);
|
||||
dg_solver->Mult(z, k);
|
||||
|
||||
@@ -14,6 +14,12 @@ list(APPEND GINKGO_EXAMPLES_SRCS
|
||||
ex1.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI AND GINKGO_BUILD_MPI)
|
||||
list(APPEND GINKGO_EXAMPLES_SRCS
|
||||
ex1p.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
|
||||
include_directories(BEFORE ${PROJECT_BINARY_DIR})
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
|
||||
Ginkgo::IcPreconditioner ginkgo_precond(exec, "paric", 30);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, ginkgo_precond);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(1e-12);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
@@ -225,7 +225,7 @@ int main(int argc, char *argv[])
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(1e-12);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
@@ -283,7 +283,7 @@ int main(int argc, char *argv[])
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(1e-12);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
// MFEM Example 1 - Parallel Version
|
||||
// GINKGO Modification
|
||||
//
|
||||
// Compile with: make ex1p
|
||||
//
|
||||
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/escher.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-annulus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-torus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/star-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
|
||||
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed-p2.mesh -o 2
|
||||
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex1p -pa -d cuda
|
||||
// mpirun -np 4 ex1p -fa -d cuda
|
||||
// mpirun -np 4 ex1p -pa -d occa-cuda
|
||||
// mpirun -np 4 ex1p -pa -d raja-omp
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -o 4 -a
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/fichera-mixed.mesh
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-cuda
|
||||
// * mpirun -np 4 ex1p -pa -d ceed-hip
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/square-mixed.mesh
|
||||
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to define a
|
||||
// simple finite element discretization of the Laplace problem
|
||||
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
|
||||
// Specifically, we discretize using a FE space of the specified
|
||||
// order, or if order < 1 using an isoparametric/isogeometric
|
||||
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of mesh refinement, finite
|
||||
// element grid functions, as well as linear and bilinear forms
|
||||
// corresponding to the left-hand side and right-hand side of the
|
||||
// discrete linear system. We also cover the explicit elimination
|
||||
// of essential boundary conditions, static condensation, and the
|
||||
// optional connection to the GLVis tool for visualization.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#ifndef MFEM_USE_GINKGO
|
||||
#error This example requires that MFEM is built with MFEM_USE_GINKGO=YES
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
Mpi::Init();
|
||||
int num_procs = Mpi::WorldSize();
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
bool fa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
int solver_config = 0;
|
||||
int print_lvl = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
|
||||
"--no-full-assembly", "Enable Full Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&solver_config, "-s", "--solver-config",
|
||||
"Solver and preconditioner combination: \n\t"
|
||||
" 0 - Ginkgo solver and Ginkgo preconditioner, \n\t"
|
||||
" 1 - Ginkgo solver and MFEM preconditioner, \n\t"
|
||||
" 2 - MFEM solver and Ginkgo preconditioner, \n\t"
|
||||
" 3 - MFEM solver and MFEM preconditioner.");
|
||||
args.AddOption(&print_lvl, "-pl", "--print-level",
|
||||
"Print level for iterative solver (1 prints every iteration).");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
device.SetGPUAwareMPI(true);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement. We choose
|
||||
// 'ref_levels' to be the largest number that gives a final mesh with no
|
||||
// more than 10,000 elements.
|
||||
{
|
||||
int ref_levels =
|
||||
(int)floor(log(10000./mesh.GetNE())/log(2.)/dim);
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
mesh.Clear();
|
||||
{
|
||||
int par_ref_levels = 2;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh.UniformRefinement();
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
bool delete_fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
else if (pmesh.GetNodes())
|
||||
{
|
||||
fec = pmesh.GetNodes()->OwnFEC();
|
||||
delete_fec = false;
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Using isoparametric FEs: " << fec->Name() << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
delete_fec = true;
|
||||
}
|
||||
ParFiniteElementSpace fespace(&pmesh, fec);
|
||||
HYPRE_BigInt size = fespace.GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 8. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// by marking all the boundary attributes from the mesh as essential
|
||||
// (Dirichlet) and converting them to a list of true dofs.
|
||||
Array<int> ess_tdof_list;
|
||||
if (pmesh.bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
|
||||
// 9. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system, which in this case is
|
||||
// (1,phi_i) where phi_i are the basis functions in fespace.
|
||||
ParLinearForm b(&fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid
|
||||
// function corresponding to fespace. Initialize x with initial guess of
|
||||
// zero, which satisfies the boundary conditions.
|
||||
ParGridFunction x(&fespace);
|
||||
x = 0.0;
|
||||
|
||||
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the
|
||||
// Diffusion domain integrator.
|
||||
ParBilinearForm a(&fespace);
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
if (fa)
|
||||
{
|
||||
a.SetAssemblyLevel(AssemblyLevel::FULL);
|
||||
// Sort the matrix column indices when running on GPU or with OpenMP (i.e.
|
||||
// when Device::IsEnabled() returns true). This makes the results
|
||||
// bit-for-bit deterministic at the cost of somewhat longer run time.
|
||||
a.EnableSparseMatrixSorting(Device::IsEnabled());
|
||||
}
|
||||
a.AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
|
||||
// 12. Assemble the parallel bilinear form and the corresponding linear
|
||||
// system, applying any necessary transformations such as: parallel
|
||||
// assembly, eliminating boundary conditions, applying conforming
|
||||
// constraints for non-conforming AMR, static condensation, etc.
|
||||
if (static_cond) { a.EnableStaticCondensation(); }
|
||||
a.Assemble();
|
||||
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
|
||||
// 13. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
{
|
||||
switch (solver_config)
|
||||
{
|
||||
// Solve the linear system with CG + Schwarz (with IC) from Ginkgo
|
||||
case 0:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
Ginkgo::IcPreconditioner local_solver(exec, "exact");
|
||||
Ginkgo::SchwarzPreconditioner gko_M(exec, MPI_COMM_WORLD, local_solver);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
ginkgo_solver.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// Solve the linear system with CG from Ginkgo + MFEM preconditioner
|
||||
case 1:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + MFEM preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
//Create MFEM preconditioner and wrap it for Ginkgo's use.
|
||||
HypreBoomerAMG M((HypreParMatrix&)(*A));
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M, MPI_COMM_WORLD);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
ginkgo_solver.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// Ginkgo Schwarz preconditioner (local ParIC) + MFEM CG solver
|
||||
case 2:
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + Ginkgo preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
Ginkgo::IcPreconditioner local_M(exec, "exact");
|
||||
Ginkgo::SchwarzPreconditioner M(exec, MPI_COMM_WORLD, local_M);
|
||||
M.SetOperator(*(A.Ptr())); // Generate the preconditioner for the matrix A.
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// MFEM solver + MFEM preconditioner
|
||||
case 3:
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + MFEM preconditioner...\n"; }
|
||||
HypreBoomerAMG M((HypreParMatrix&)(*A));
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
} // End switch on solver_config
|
||||
}
|
||||
// Partial assembly mode. Cannot use Ginkgo preconditioners, but can use Ginkgo
|
||||
// solvers.
|
||||
else
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
// Use Jacobi preconditioning in partial assembly mode.
|
||||
OperatorJacobiSmoother M(a, ess_tdof_list);
|
||||
switch (solver_config)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
|
||||
MFEM_ABORT("Cannot use Ginkgo preconditioner in partial assembly mode.\n"
|
||||
" Try -s 1 to test Ginkgo solver with an MFEM preconditioner.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Use Ginkgo solver with MFEM preconditioner
|
||||
case 1:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + MFEM preconditioner...\n"; }
|
||||
Ginkgo::GinkgoExecutor exec(device);
|
||||
// Wrap MFEM preconditioner for Ginkgo's use.
|
||||
Ginkgo::MFEMPreconditioner gko_M(exec, M, MPI_COMM_WORLD);
|
||||
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
|
||||
ginkgo_solver.SetPrintLevel(print_lvl);
|
||||
ginkgo_solver.SetRelTol(sqrt(1e-12));
|
||||
ginkgo_solver.SetAbsTol(0.0);
|
||||
ginkgo_solver.SetMaxIter(400);
|
||||
ginkgo_solver.SetOperator(*(A.Ptr()));
|
||||
ginkgo_solver.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
|
||||
// No Ginkgo preconditioners work with matrix-free; error
|
||||
case 2:
|
||||
{
|
||||
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
|
||||
MFEM_ABORT("Cannot use Ginkgo preconditioner in partial assembly mode.\n"
|
||||
" Try -s 1 to test Ginkgo solver with an MFEM preconditioner.");
|
||||
break;
|
||||
}
|
||||
|
||||
// Use MFEM solver and preconditioner
|
||||
case 3:
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + MFEM preconditioner...\n"; }
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetPreconditioner(M);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
break;
|
||||
}
|
||||
} // End switch on solver_config
|
||||
}
|
||||
else // CG with no preconditioning
|
||||
{
|
||||
if (myid == 0) { cout << "Using MFEM solver + no preconditioner...\n"; }
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(sqrt(1e-12));
|
||||
cg.SetMaxIter(400);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
}
|
||||
}
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
// 15. Save the refined mesh and the solution in parallel. This output can
|
||||
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
|
||||
{
|
||||
ostringstream mesh_name, sol_name;
|
||||
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
|
||||
sol_name << "sol." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh.Print(mesh_ofs);
|
||||
|
||||
ofstream sol_ofs(sol_name.str().c_str());
|
||||
sol_ofs.precision(8);
|
||||
x.Save(sol_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock << "parallel " << num_procs << " " << myid << "\n";
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << pmesh << x << flush;
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
if (delete_fec)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -20,9 +20,8 @@ CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
# Currently there are only serial Ginkgo examples
|
||||
SEQ_EXAMPLES = ex1
|
||||
PAR_EXAMPLES =
|
||||
PAR_EXAMPLES = ex1p
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
|
||||
+2
-2
@@ -22,11 +22,11 @@ MFEM_LIB_FILE = mfem_is_not_built
|
||||
|
||||
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
|
||||
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30 \
|
||||
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40 ex41
|
||||
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40 ex41 ex43
|
||||
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
|
||||
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
|
||||
ex25p ex26p ex27p ex28p ex29p ex30p ex31p ex32p ex33p ex34p ex35p ex36p \
|
||||
ex37p ex39p ex40p ex41p
|
||||
ex37p ex39p ex40p ex41p ex43p
|
||||
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
|
||||
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p \
|
||||
ex22p ex24p ex25p ex26p ex34p ex35p
|
||||
|
||||
@@ -4213,6 +4213,181 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
|
||||
}
|
||||
}
|
||||
|
||||
void SlidingElasticityIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_ASSERT(Trans.Elem2No < 0,
|
||||
"support for interior faces is not implemented");
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
// For descriptions of these variables, see the class declaration.
|
||||
Vector shape1;
|
||||
DenseMatrix dshape1;
|
||||
DenseMatrix adjJ;
|
||||
DenseMatrix dshape1_ps;
|
||||
Vector nor;
|
||||
Vector nL1;
|
||||
Vector nM1;
|
||||
Vector nt1;
|
||||
Vector dshape1_dnM;
|
||||
Vector dshape1_dnt;
|
||||
DenseMatrix jmat;
|
||||
#endif
|
||||
|
||||
const int dim = el1.GetDim();
|
||||
const int ndofs1 = el1.GetDof();
|
||||
const int nvdofs = dim * ndofs1;
|
||||
|
||||
// Initially 'elmat' corresponds to the term:
|
||||
// < { sigma(u) n . ñ }, v . ñ > =
|
||||
// < { (lambda div(u) I + mu (grad(u) + grad(u)^T)) n . ñ }, v . ñ >
|
||||
// But eventually, it's going to be replaced by:
|
||||
// elmat := -elmat + alpha*elmat^T + jmat
|
||||
elmat.SetSize(nvdofs);
|
||||
elmat = 0.;
|
||||
|
||||
const bool kappa_is_nonzero = (kappa != 0.0);
|
||||
if (kappa_is_nonzero)
|
||||
{
|
||||
jmat.SetSize(nvdofs);
|
||||
jmat = 0.;
|
||||
}
|
||||
|
||||
adjJ.SetSize(dim);
|
||||
shape1.SetSize(ndofs1);
|
||||
dshape1.SetSize(ndofs1, dim);
|
||||
dshape1_ps.SetSize(ndofs1, dim);
|
||||
nor.SetSize(dim);
|
||||
nL1.SetSize(dim);
|
||||
nM1.SetSize(dim);
|
||||
nt1.SetSize(dim);
|
||||
dshape1_dnM.SetSize(ndofs1);
|
||||
dshape1_dnt.SetSize(ndofs1);
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
// a simple choice for the integration order; is this OK?
|
||||
const int order = 2 * el1.GetOrder();
|
||||
ir = &IntRules.Get(Trans.GetGeometryType(), order);
|
||||
}
|
||||
|
||||
for (int pind = 0; pind < ir->GetNPoints(); ++pind)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(pind);
|
||||
|
||||
// Set the integration point in the face and the neighboring elements
|
||||
Trans.SetAllIntPoints(&ip);
|
||||
|
||||
// Access the neighboring element's integration point
|
||||
const IntegrationPoint &eip1 = Trans.GetElement1IntPoint();
|
||||
|
||||
el1.CalcShape(eip1, shape1);
|
||||
el1.CalcDShape(eip1, dshape1);
|
||||
|
||||
CalcAdjugate(Trans.Elem1->Jacobian(), adjJ);
|
||||
Mult(dshape1, adjJ, dshape1_ps);
|
||||
|
||||
if (dim == 1)
|
||||
{
|
||||
nor(0) = 2*eip1.x - 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CalcOrtho(Trans.Jacobian(), nor);
|
||||
}
|
||||
|
||||
if (!nt)
|
||||
{
|
||||
// Set ñ to the unit normal vector if not provided
|
||||
nt1 = nor;
|
||||
nt1 /= nt1.Norml2();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Evaluate vector function ñ at integration point
|
||||
nt->Eval(nt1, *Trans.Elem1, eip1);
|
||||
}
|
||||
|
||||
const real_t W = ip.weight;
|
||||
const real_t W1 = W / Trans.Elem1->Weight();
|
||||
const real_t WL1 = W1 * lambda->Eval(*Trans.Elem1, eip1);
|
||||
const real_t WM1 = W1 * mu->Eval(*Trans.Elem1, eip1);
|
||||
nL1.Set(WL1, nor);
|
||||
nM1.Set(WM1, nor);
|
||||
const real_t WLM = WL1 + 2.0*WM1;
|
||||
dshape1_ps.Mult(nM1, dshape1_dnM);
|
||||
dshape1_ps.Mult(nt1, dshape1_dnt);
|
||||
|
||||
const real_t jmatcoef = kappa * (nor*nor) * WLM;
|
||||
|
||||
const real_t nL_dot_nt1 = nL1 * nt1;
|
||||
for (int jm = 0, j = 0; jm < dim; ++jm)
|
||||
{
|
||||
for (int jdof = 0; jdof < ndofs1; ++jdof, ++j)
|
||||
{
|
||||
const real_t t1 = dshape1_ps(jdof, jm) * nL_dot_nt1;
|
||||
const real_t t2 = dshape1_dnM(jdof) * nt1(jm);
|
||||
const real_t t3 = dshape1_dnt(jdof) * nM1(jm);
|
||||
const real_t tt = t1 + t2 + t3;
|
||||
for (int im = 0, i = 0; im < dim; ++im)
|
||||
{
|
||||
for (int idof = 0; idof < ndofs1; ++idof, ++i)
|
||||
{
|
||||
elmat(i, j) += tt * shape1(idof) * nt1(im);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (kappa_is_nonzero)
|
||||
{
|
||||
for (int jm = 0, j = 0; jm < dim; ++jm)
|
||||
{
|
||||
for (int jdof = 0; jdof < ndofs1; ++jdof, ++j)
|
||||
{
|
||||
const real_t sj = jmatcoef * shape1(jdof) * nt1(jm);
|
||||
for (int im = 0, i = 0; im < dim; ++im)
|
||||
{
|
||||
for (int idof = 0; idof < ndofs1; ++idof, ++i)
|
||||
{
|
||||
jmat(i, j) += shape1(idof) * sj * nt1(im);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// elmat := -elmat + alpha*elmat^t + jmat
|
||||
if (kappa_is_nonzero)
|
||||
{
|
||||
for (int i = 0; i < nvdofs; ++i)
|
||||
{
|
||||
for (int j = 0; j < i; ++j)
|
||||
{
|
||||
real_t aij = elmat(i,j), aji = elmat(j,i), mij = jmat(i,j);
|
||||
elmat(i,j) = alpha*aji - aij + mij;
|
||||
elmat(j,i) = alpha*aij - aji + mij;
|
||||
}
|
||||
elmat(i,i) = (alpha - 1.)*elmat(i,i) + jmat(i,i);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < nvdofs; ++i)
|
||||
{
|
||||
for (int j = 0; j < i; ++j)
|
||||
{
|
||||
real_t aij = elmat(i,j), aji = elmat(j,i);
|
||||
elmat(i,j) = alpha*aji - aij;
|
||||
elmat(j,i) = alpha*aij - aji;
|
||||
}
|
||||
elmat(i,i) *= (alpha - 1.);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TraceJumpIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &trial_face_fe, const FiniteElement &test_fe1,
|
||||
|
||||
@@ -3738,6 +3738,84 @@ protected:
|
||||
DenseMatrix &elmat, DenseMatrix &jmat);
|
||||
};
|
||||
|
||||
/** Integrator for the Nitsche elasticity form:
|
||||
$$
|
||||
\begin{split}
|
||||
a(u,v)
|
||||
&:= -\langle \sigma(u)\, \vec{n} \cdot \tilde{n},\ v \cdot \tilde{n}
|
||||
\rangle + \alpha \langle \sigma(v)\, \vec{n} \cdot \tilde{n},\ u \cdot
|
||||
\tilde{n} \rangle + \kappa \langle h^{-1} (\lambda + 2\mu)\, u \cdot
|
||||
\tilde{n},\ v \cdot \tilde{n} \rangle \\
|
||||
&= -\int_\Gamma (\sigma(u)\, n \cdot \tilde{n})(v \cdot \tilde{n})\, dS +
|
||||
\alpha \int_\Gamma (\sigma(v)\, n \cdot \tilde{n})(u \cdot \tilde{n})\,
|
||||
dS + \kappa \int_\Gamma h^{-1} (\lambda + 2\mu)(u \cdot \tilde{n})(v
|
||||
\cdot \tilde{n})\, dS.
|
||||
\end{split}
|
||||
$$
|
||||
|
||||
For isotropic media,
|
||||
$$
|
||||
\begin{split}
|
||||
\sigma(u) &= \lambda \nabla \cdot u I + 2 \mu \varepsilon(u) \\
|
||||
&= \lambda \nabla \cdot u I + 2 \mu \frac{1}{2} (\nabla u + \nabla
|
||||
u^{\mathrm{T}}) \\
|
||||
&= \lambda \nabla \cdot u I + \mu (\nabla u + \nabla u^{\mathrm{T}})
|
||||
\end{split}
|
||||
$$
|
||||
where $I$ is the identity matrix, $\lambda$ and $\mu$ are the Lamé
|
||||
coefficients (see ElasticityIntegrator), $\tilde{n}$ is a unit vector
|
||||
field, $\alpha = \pm 1$ and $\kappa > 0$ are the Nitsche parameters, and
|
||||
$u$, $v$ are the trial and test functions, respectively.
|
||||
|
||||
This is a '%Vector' integrator, i.e. defined for FE spaces using multiple
|
||||
copies of a scalar FE space.
|
||||
*/
|
||||
class SlidingElasticityIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
public:
|
||||
SlidingElasticityIntegrator(Coefficient &lambda_, Coefficient &mu_,
|
||||
real_t kappa_)
|
||||
: nt(NULL), lambda(&lambda_), mu(&mu_), alpha(-1.0), kappa(kappa_) { }
|
||||
|
||||
SlidingElasticityIntegrator(VectorCoefficient &nt_, Coefficient &lambda_,
|
||||
Coefficient &mu_, real_t alpha_, real_t kappa_)
|
||||
: nt(&nt_), lambda(&lambda_), mu(&mu_), alpha(alpha_), kappa(kappa_) { }
|
||||
|
||||
using BilinearFormIntegrator::AssembleFaceMatrix;
|
||||
void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat) override;
|
||||
|
||||
protected:
|
||||
VectorCoefficient *nt;
|
||||
Coefficient *lambda, *mu;
|
||||
real_t alpha, kappa;
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
// values of all scalar basis functions for one component of u (which is a
|
||||
// vector) at the integration point in the reference space
|
||||
Vector shape1;
|
||||
// values of derivatives of all scalar basis functions for one component
|
||||
// of u (which is a vector) at the integration point in the reference space
|
||||
DenseMatrix dshape1;
|
||||
// Adjugate of the Jacobian of the transformation: adjJ = det(J) J^{-1}
|
||||
DenseMatrix adjJ;
|
||||
// gradient of shape functions in the real (physical, not reference)
|
||||
// coordinates, scaled by det(J):
|
||||
// dshape_ps(jdof,jm) = sum_{t} adjJ(t,jm)*dshape(jdof,t)
|
||||
DenseMatrix dshape1_ps;
|
||||
Vector nor; // nor = |weight(J_face)| n
|
||||
Vector nL1; // nL1 = (lambda1 * ip.weight / detJ1) nor
|
||||
Vector nM1; // nM1 = (mu1 * ip.weight / detJ1) nor
|
||||
Vector nt1; // nt1 = vector function ñ evaluated at ip1
|
||||
Vector dshape1_dnM; // dshape1_dnM = dshape1_ps . nM1
|
||||
Vector dshape1_dnt; // dshape1_dnt = dshape1_ps . nt1
|
||||
// 'jmat' corresponds to the term: kappa <h⁻¹ u ⋅ ñ, v ⋅ ñ>
|
||||
DenseMatrix jmat;
|
||||
#endif
|
||||
};
|
||||
|
||||
/** Integrator for the DPG form:$ \langle v, [w] \rangle $ over all faces (the interface) where
|
||||
the trial variable $v$ is defined on the interface and the test variable $w$ is
|
||||
defined inside the elements, generally in a DG space. */
|
||||
|
||||
@@ -1302,6 +1302,73 @@ real_t TraceCoefficient::Eval(ElementTransformation &T,
|
||||
return ma.Trace();
|
||||
}
|
||||
|
||||
VectorComponentCoefficient::VectorComponentCoefficient(VectorCoefficient &A,
|
||||
int c)
|
||||
: a(&A), va(A.GetVDim())
|
||||
{
|
||||
SetComponent(c);
|
||||
}
|
||||
|
||||
void VectorComponentCoefficient::SetComponent(int c)
|
||||
{
|
||||
MFEM_ASSERT(c < a->GetVDim() && c >= 0,
|
||||
"VectorComponentCoefficient: "
|
||||
"Index not in range.");
|
||||
|
||||
component = c;
|
||||
}
|
||||
|
||||
void VectorComponentCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
real_t VectorComponentCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(va, T, ip);
|
||||
return va[component];
|
||||
}
|
||||
|
||||
MatrixComponentCoefficient::MatrixComponentCoefficient(MatrixCoefficient &A,
|
||||
int ri, int ci)
|
||||
: a(&A), ma(A.GetHeight(), A.GetWidth())
|
||||
{
|
||||
SetRowIndex(ri);
|
||||
SetColumnIndex(ci);
|
||||
}
|
||||
|
||||
void MatrixComponentCoefficient::SetRowIndex(int ri)
|
||||
{
|
||||
MFEM_ASSERT(ri < a->GetHeight() && ri >= 0,
|
||||
"MatrixComponentCoefficient: "
|
||||
"Row index not in range.");
|
||||
|
||||
row_idx = ri;
|
||||
}
|
||||
|
||||
void MatrixComponentCoefficient::SetColumnIndex(int ci)
|
||||
{
|
||||
MFEM_ASSERT(ci < a->GetWidth() && ci >= 0,
|
||||
"MatrixComponentCoefficient: "
|
||||
"Column index not in range.");
|
||||
col_idx = ci;
|
||||
}
|
||||
|
||||
void MatrixComponentCoefficient::SetTime(real_t t)
|
||||
{
|
||||
if (a) { a->SetTime(t); }
|
||||
this->Coefficient::SetTime(t);
|
||||
}
|
||||
|
||||
real_t MatrixComponentCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
a->Eval(ma, T, ip);
|
||||
return ma(row_idx,col_idx);
|
||||
}
|
||||
|
||||
VectorSumCoefficient::VectorSumCoefficient(int dim)
|
||||
: VectorCoefficient(dim),
|
||||
ACoef(NULL), BCoef(NULL),
|
||||
|
||||
+83
-5
@@ -114,11 +114,10 @@ public:
|
||||
/// Construct the constant coefficient using a vector of constants.
|
||||
/** @a c should be a vector defined by attributes, so for region with
|
||||
attribute @a i @a c[i-1] is the coefficient in that region */
|
||||
PWConstCoefficient(Vector &c)
|
||||
{ constants.SetSize(c.Size()); constants=c; }
|
||||
PWConstCoefficient(const Vector &c) { UpdateConstants(c); }
|
||||
|
||||
/// Update the constants with vector @a c.
|
||||
void UpdateConstants(Vector &c) { constants.SetSize(c.Size()); constants=c; }
|
||||
void UpdateConstants(const Vector &c) { constants = c; }
|
||||
|
||||
/// Return a reference to the i-th constant
|
||||
real_t &operator()(int i) { return constants(i-1); }
|
||||
@@ -1332,8 +1331,8 @@ public:
|
||||
/// Get the coefficient located at (i,j) in the matrix.
|
||||
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
|
||||
|
||||
/** @brief Set the coefficient located at (i,j) in the matrix. By default by
|
||||
default this will take ownership of the Coefficient passed in, but this
|
||||
/** @brief Set the coefficient located at (i,j) in the matrix. By default
|
||||
this will take ownership of the Coefficient passed in, but this
|
||||
can be overridden with the @a own parameter. */
|
||||
void Set(int i, int j, Coefficient * c, bool own=true);
|
||||
|
||||
@@ -1873,6 +1872,85 @@ public:
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// Scalar coefficient defined as component of a vector coefficient
|
||||
class VectorComponentCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
VectorCoefficient *a = nullptr;
|
||||
|
||||
mutable Vector va;
|
||||
int component;
|
||||
|
||||
public:
|
||||
/// Construct with a vector coefficient.
|
||||
VectorComponentCoefficient(VectorCoefficient &A)
|
||||
: a(&A), va(A.GetVDim()), component(0) {};
|
||||
|
||||
VectorComponentCoefficient(VectorCoefficient &A, int c);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// Reset the vector coefficient
|
||||
void SetACoef(VectorCoefficient &A) { a = &A; }
|
||||
|
||||
/// Return the vector coefficient
|
||||
VectorCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Set the component
|
||||
void SetComponent(int c);
|
||||
|
||||
/// Return the component
|
||||
int GetComponent() const { return component; }
|
||||
|
||||
/// Evaluate the trace coefficient at @a ip.
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// Scalar coefficient defined as component of a matrix coefficient
|
||||
class MatrixComponentCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
MatrixCoefficient *a = nullptr;
|
||||
|
||||
mutable DenseMatrix ma;
|
||||
int row_idx,col_idx;
|
||||
|
||||
public:
|
||||
MatrixComponentCoefficient(MatrixCoefficient &A)
|
||||
: a(&A), ma(A.GetHeight(), A.GetWidth()), row_idx(0), col_idx(0) {};
|
||||
|
||||
/// Construct with the matrix coefficient.
|
||||
MatrixComponentCoefficient(MatrixCoefficient &A, int ri, int ci);
|
||||
|
||||
/// Set the time for internally stored coefficients
|
||||
void SetTime(real_t t) override;
|
||||
|
||||
/// Reset the matrix coefficient
|
||||
void SetACoef(MatrixCoefficient &A) { a = &A; }
|
||||
|
||||
/// Return the matrix coefficient
|
||||
MatrixCoefficient * GetACoef() const { return a; }
|
||||
|
||||
/// Reset the index
|
||||
void SetRowIndex(int ri);
|
||||
|
||||
/// Return the index
|
||||
int GetRowIndex() const { return row_idx; }
|
||||
|
||||
/// Reset the index
|
||||
void SetColumnIndex(int ci);
|
||||
|
||||
/// Return the index
|
||||
int GetColumnIndex() const { return col_idx; }
|
||||
|
||||
|
||||
/// Evaluate the trace coefficient at @a ip.
|
||||
real_t Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip) override;
|
||||
};
|
||||
|
||||
/// Vector coefficient defined as the linear combination of two vectors
|
||||
class VectorSumCoefficient : public VectorCoefficient
|
||||
{
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
#pragma once
|
||||
|
||||
// This is serac's tuple implementation
|
||||
// This is smith's tuple implementation
|
||||
|
||||
#include <ostream>
|
||||
#include "../../config/config.hpp"
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
#include "dgmassinv.hpp"
|
||||
#include "bilinearform.hpp"
|
||||
#include "dgmassinv_kernels.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -119,151 +118,6 @@ void DGMassInverse::Update()
|
||||
|
||||
DGMassInverse::~DGMassInverse() = default;
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
|
||||
{
|
||||
using namespace internal; // host/device kernel functions
|
||||
|
||||
const int NE = fes.GetNE();
|
||||
const int d1d = m->dofs1D;
|
||||
const int q1d = m->quad1D;
|
||||
|
||||
const int ND = static_cast<int>(pow(d1d, DIM));
|
||||
|
||||
const auto B = m->maps->B.Read();
|
||||
const auto Bt = m->maps->Bt.Read();
|
||||
const auto pa_data = m->pa_data.Read();
|
||||
const auto dinv = diag_inv.Read();
|
||||
auto r = r_.Write();
|
||||
auto d = d_.Write();
|
||||
auto z = z_.Write();
|
||||
auto u = u_.ReadWrite();
|
||||
|
||||
const real_t RELTOL = rel_tol;
|
||||
const real_t ABSTOL = abs_tol;
|
||||
const int MAXIT = max_iter;
|
||||
const bool IT_MODE = iterative_mode;
|
||||
const bool CHANGE_BASIS = (d2q != nullptr);
|
||||
|
||||
// b is the right-hand side (if no change of basis, this just points to the
|
||||
// incoming RHS vector, if we have to change basis, this points to the
|
||||
// internal b2 vector where we put the transformed RHS)
|
||||
const real_t *b;
|
||||
// the following are non-null if we have to change basis
|
||||
real_t *b2 = nullptr; // non-const access to b2
|
||||
const real_t *b_orig = nullptr; // RHS vector in "original" basis
|
||||
const real_t *d2q_B = nullptr; // matrix to transform initial guess
|
||||
const real_t *q2d_B = nullptr; // matrix to transform solution
|
||||
const real_t *q2d_Bt = nullptr; // matrix to transform RHS
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
d2q_B = d2q->B.Read();
|
||||
q2d_B = B_.Read();
|
||||
q2d_Bt = Bt_.Read();
|
||||
|
||||
b2 = b2_.Write();
|
||||
b_orig = b_.Read();
|
||||
b = b2;
|
||||
}
|
||||
else
|
||||
{
|
||||
b = b_.Read();
|
||||
}
|
||||
|
||||
static constexpr int NB = Q1D ? Q1D : 1; // block size
|
||||
|
||||
mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
// Perform change of basis if needed
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
// Transform RHS
|
||||
DGMassBasis<DIM,D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
|
||||
if (IT_MODE)
|
||||
{
|
||||
// Transform initial guess
|
||||
DGMassBasis<DIM,D1D>(e, NE, d2q_B, u, u, d1d);
|
||||
}
|
||||
}
|
||||
|
||||
const int tid = MFEM_THREAD_ID(x) + NB*MFEM_THREAD_ID(y);
|
||||
|
||||
// Compute first residual
|
||||
if (IT_MODE)
|
||||
{
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, u, r, d1d, q1d);
|
||||
DGMassAxpy(e, NE, ND, 1.0, b, -1.0, r, r); // r = b - r
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not in iterative mode, use zero initial guess
|
||||
const int BX = MFEM_THREAD_SIZE(x);
|
||||
const int BY = MFEM_THREAD_SIZE(y);
|
||||
const int bxy = BX*BY;
|
||||
const auto B = ConstDeviceMatrix(b, ND, NE);
|
||||
auto U = DeviceMatrix(u, ND, NE);
|
||||
auto R = DeviceMatrix(r, ND, NE);
|
||||
for (int i = tid; i < ND; i += bxy)
|
||||
{
|
||||
U(i, e) = 0.0;
|
||||
R(i, e) = B(i, e);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
DGMassPreconditioner(e, NE, ND, dinv, r, z);
|
||||
DGMassAxpy(e, NE, ND, 1.0, z, 0.0, z, d); // d = z
|
||||
|
||||
real_t nom = DGMassDot<NB>(e, NE, ND, d, r);
|
||||
if (nom < 0.0) { return; /* Not positive definite */ }
|
||||
real_t r0 = fmax(nom*RELTOL*RELTOL, ABSTOL*ABSTOL);
|
||||
if (nom <= r0) { return; /* Converged */ }
|
||||
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d);
|
||||
real_t den = DGMassDot<NB>(e, NE, ND, z, d);
|
||||
if (den <= 0.0)
|
||||
{
|
||||
DGMassDot<NB>(e, NE, ND, d, d);
|
||||
// d2 > 0 => not positive definite
|
||||
if (den == 0.0) { return; }
|
||||
}
|
||||
|
||||
// start iteration
|
||||
int i = 1;
|
||||
while (true)
|
||||
{
|
||||
const real_t alpha = nom/den;
|
||||
DGMassAxpy(e, NE, ND, 1.0, u, alpha, d, u); // u = u + alpha*d
|
||||
DGMassAxpy(e, NE, ND, 1.0, r, -alpha, z, r); // r = r - alpha*A*d
|
||||
|
||||
DGMassPreconditioner(e, NE, ND, dinv, r, z);
|
||||
|
||||
real_t betanom = DGMassDot<NB>(e, NE, ND, r, z);
|
||||
if (betanom < 0.0) { return; /* Not positive definite */ }
|
||||
if (betanom <= r0) { break; /* Converged */ }
|
||||
|
||||
if (++i > MAXIT) { break; }
|
||||
|
||||
const real_t beta = betanom/nom;
|
||||
DGMassAxpy(e, NE, ND, 1.0, z, beta, d, d); // d = z + beta*d
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d); // z = A d
|
||||
den = DGMassDot<NB>(e, NE, ND, d, z);
|
||||
if (den <= 0.0)
|
||||
{
|
||||
DGMassDot<NB>(e, NE, ND, d, d);
|
||||
// d2 > 0 => not positive definite
|
||||
if (den == 0.0) { break; }
|
||||
}
|
||||
nom = betanom;
|
||||
}
|
||||
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
DGMassBasis<DIM,D1D>(e, NE, q2d_B, u, u, d1d);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void DGMassInverse::Mult(const Vector &Mu, Vector &u) const
|
||||
{
|
||||
// Dispatch to templated version based on dim, d1d, and q1d.
|
||||
@@ -306,23 +160,4 @@ DGMassInvKernels::DGMassInvKernels()
|
||||
k::Specialization<3,6,7>::Add();
|
||||
}
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
|
||||
{
|
||||
return &DGMassInverse::DGMassCGIteration<DIM,D1D,Q1D>;
|
||||
}
|
||||
|
||||
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
|
||||
int dim, int, int)
|
||||
{
|
||||
if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
|
||||
else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
|
||||
else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
|
||||
else { MFEM_ABORT("Unsupported dimension."); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "../linalg/kernels.hpp"
|
||||
#include "kernels.hpp"
|
||||
#include "integ/bilininteg_mass_kernels.hpp"
|
||||
#include "dgmassinv.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -333,6 +334,170 @@ void DGMassBasis(const int e,
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template<int DIM, int D1D, int Q1D>
|
||||
void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
|
||||
{
|
||||
using namespace internal; // host/device kernel functions
|
||||
|
||||
const int NE = fes.GetNE();
|
||||
const int d1d = m->dofs1D;
|
||||
const int q1d = m->quad1D;
|
||||
|
||||
const int ND = static_cast<int>(pow(d1d, DIM));
|
||||
|
||||
const auto B = m->maps->B.Read();
|
||||
const auto Bt = m->maps->Bt.Read();
|
||||
const auto pa_data = m->pa_data.Read();
|
||||
const auto dinv = diag_inv.Read();
|
||||
auto r = r_.Write();
|
||||
auto d = d_.Write();
|
||||
auto z = z_.Write();
|
||||
auto u = u_.ReadWrite();
|
||||
|
||||
const real_t RELTOL = rel_tol;
|
||||
const real_t ABSTOL = abs_tol;
|
||||
const int MAXIT = max_iter;
|
||||
const bool IT_MODE = iterative_mode;
|
||||
const bool CHANGE_BASIS = (d2q != nullptr);
|
||||
|
||||
// b is the right-hand side (if no change of basis, this just points to the
|
||||
// incoming RHS vector, if we have to change basis, this points to the
|
||||
// internal b2 vector where we put the transformed RHS)
|
||||
const real_t *b;
|
||||
// the following are non-null if we have to change basis
|
||||
real_t *b2 = nullptr; // non-const access to b2
|
||||
const real_t *b_orig = nullptr; // RHS vector in "original" basis
|
||||
const real_t *d2q_B = nullptr; // matrix to transform initial guess
|
||||
const real_t *q2d_B = nullptr; // matrix to transform solution
|
||||
const real_t *q2d_Bt = nullptr; // matrix to transform RHS
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
d2q_B = d2q->B.Read();
|
||||
q2d_B = B_.Read();
|
||||
q2d_Bt = Bt_.Read();
|
||||
|
||||
b2 = b2_.Write();
|
||||
b_orig = b_.Read();
|
||||
b = b2;
|
||||
}
|
||||
else
|
||||
{
|
||||
b = b_.Read();
|
||||
}
|
||||
|
||||
static constexpr int NB = Q1D ? Q1D : 1; // block size
|
||||
|
||||
mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
// Perform change of basis if needed
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
// Transform RHS
|
||||
DGMassBasis<DIM,D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
|
||||
if (IT_MODE)
|
||||
{
|
||||
// Transform initial guess
|
||||
DGMassBasis<DIM,D1D>(e, NE, d2q_B, u, u, d1d);
|
||||
}
|
||||
}
|
||||
|
||||
const int tid = MFEM_THREAD_ID(x) + NB*MFEM_THREAD_ID(y);
|
||||
|
||||
// Compute first residual
|
||||
if (IT_MODE)
|
||||
{
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, u, r, d1d, q1d);
|
||||
DGMassAxpy(e, NE, ND, 1.0, b, -1.0, r, r); // r = b - r
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not in iterative mode, use zero initial guess
|
||||
const int BX = MFEM_THREAD_SIZE(x);
|
||||
const int BY = MFEM_THREAD_SIZE(y);
|
||||
const int bxy = BX*BY;
|
||||
const auto B = ConstDeviceMatrix(b, ND, NE);
|
||||
auto U = DeviceMatrix(u, ND, NE);
|
||||
auto R = DeviceMatrix(r, ND, NE);
|
||||
for (int i = tid; i < ND; i += bxy)
|
||||
{
|
||||
U(i, e) = 0.0;
|
||||
R(i, e) = B(i, e);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
DGMassPreconditioner(e, NE, ND, dinv, r, z);
|
||||
DGMassAxpy(e, NE, ND, 1.0, z, 0.0, z, d); // d = z
|
||||
|
||||
real_t nom = DGMassDot<NB>(e, NE, ND, d, r);
|
||||
if (nom < 0.0) { return; /* Not positive definite */ }
|
||||
real_t r0 = fmax(nom*RELTOL*RELTOL, ABSTOL*ABSTOL);
|
||||
if (nom <= r0) { return; /* Converged */ }
|
||||
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d);
|
||||
real_t den = DGMassDot<NB>(e, NE, ND, z, d);
|
||||
if (den <= 0.0)
|
||||
{
|
||||
DGMassDot<NB>(e, NE, ND, d, d);
|
||||
// d2 > 0 => not positive definite
|
||||
if (den == 0.0) { return; }
|
||||
}
|
||||
|
||||
// start iteration
|
||||
int i = 1;
|
||||
while (true)
|
||||
{
|
||||
const real_t alpha = nom/den;
|
||||
DGMassAxpy(e, NE, ND, 1.0, u, alpha, d, u); // u = u + alpha*d
|
||||
DGMassAxpy(e, NE, ND, 1.0, r, -alpha, z, r); // r = r - alpha*A*d
|
||||
|
||||
DGMassPreconditioner(e, NE, ND, dinv, r, z);
|
||||
|
||||
real_t betanom = DGMassDot<NB>(e, NE, ND, r, z);
|
||||
if (betanom < 0.0) { return; /* Not positive definite */ }
|
||||
if (betanom <= r0) { break; /* Converged */ }
|
||||
|
||||
if (++i > MAXIT) { break; }
|
||||
|
||||
const real_t beta = betanom/nom;
|
||||
DGMassAxpy(e, NE, ND, 1.0, z, beta, d, d); // d = z + beta*d
|
||||
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d); // z = A d
|
||||
den = DGMassDot<NB>(e, NE, ND, d, z);
|
||||
if (den <= 0.0)
|
||||
{
|
||||
DGMassDot<NB>(e, NE, ND, d, d);
|
||||
// d2 > 0 => not positive definite
|
||||
if (den == 0.0) { break; }
|
||||
}
|
||||
nom = betanom;
|
||||
}
|
||||
|
||||
if (CHANGE_BASIS)
|
||||
{
|
||||
DGMassBasis<DIM,D1D>(e, NE, q2d_B, u, u, d1d);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
template <int DIM, int D1D, int Q1D>
|
||||
inline DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
|
||||
{
|
||||
return &DGMassInverse::DGMassCGIteration<DIM,D1D,Q1D>;
|
||||
}
|
||||
|
||||
inline DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
|
||||
int dim, int, int)
|
||||
{
|
||||
if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
|
||||
else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
|
||||
else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
|
||||
else { MFEM_ABORT("Unsupported dimension."); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -44,7 +44,7 @@ public:
|
||||
NumBasisTypes = 9 /**< Keep track of maximum types to prevent
|
||||
hard-coding */
|
||||
};
|
||||
/** @brief If the input does not represents a valid BasisType, abort with an
|
||||
/** @brief If the input does not represent a valid BasisType, abort with an
|
||||
error; otherwise return the input. */
|
||||
static int Check(int b_type)
|
||||
{
|
||||
@@ -52,7 +52,7 @@ public:
|
||||
"unknown BasisType: " << b_type);
|
||||
return b_type;
|
||||
}
|
||||
/** @brief If the input does not represents a valid nodal BasisType, abort
|
||||
/** @brief If the input does not represent a valid nodal BasisType, abort
|
||||
with an error; otherwise return the input. */
|
||||
static int CheckNodal(int b_type)
|
||||
{
|
||||
@@ -1120,7 +1120,7 @@ public:
|
||||
return GetPoints(p, btype, on_device);
|
||||
}
|
||||
|
||||
/// Get coordinates of a closed (GaussLegendre) set of points if degree @a p
|
||||
/// Get coordinates of a closed (GaussLobatto) set of points if degree @a p
|
||||
const real_t *ClosedPoints(const int p,
|
||||
const int btype = BasisType::GaussLobatto,
|
||||
bool on_device = false)
|
||||
|
||||
+516
-1
@@ -84,6 +84,46 @@ void NURBS1DFiniteElement::CalcHessian (const IntegrationPoint &ip,
|
||||
add(1.0, hess, (-d2sum + 2*dsum*dsum*sum)*sum*sum, shape_x, hess);
|
||||
}
|
||||
|
||||
void NURBS1DFiniteElement::Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
IntegrationPoint ip;
|
||||
|
||||
for (int i = 0; i <= order; i++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+order)) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+order);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
dofs(i) = coeff.Eval(Trans, ip);
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS1DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
|
||||
Vector x(vc.GetVDim());
|
||||
IntegrationPoint ip;
|
||||
|
||||
for (int i = 0; i <= order; i++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+order)) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+order);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
for (int j = 0; j < x.Size(); j++)
|
||||
{
|
||||
dofs(dof*j+i) = x(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NURBS2DFiniteElement::SetOrder() const
|
||||
{
|
||||
@@ -215,6 +255,63 @@ void NURBS2DFiniteElement::CalcHessian (const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS2DFiniteElement::Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
IntegrationPoint ip;
|
||||
for (int o = 0, j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
dofs(o) = coeff.Eval(Trans, ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS2DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
|
||||
Vector x(vc.GetVDim());
|
||||
IntegrationPoint ip;
|
||||
for (int o = 0, j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
for (int v = 0; v < x.Size(); v++)
|
||||
{
|
||||
dofs(dof*v+o) = x(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS3DFiniteElement::SetOrder() const
|
||||
{
|
||||
@@ -401,6 +498,85 @@ void NURBS3DFiniteElement::CalcHessian (const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS3DFiniteElement::Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
IntegrationPoint ip;
|
||||
|
||||
for (int o = 0, k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
dofs(o) = coeff.Eval(Trans, ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS3DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
|
||||
Vector x(vc.GetVDim());
|
||||
IntegrationPoint ip;
|
||||
|
||||
for (int o = 0, k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
for (int v = 0; v < x.Size(); v++)
|
||||
{
|
||||
dofs(dof*v+o) = x(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS_HDiv2DFiniteElement::SetOrder() const
|
||||
{
|
||||
@@ -517,6 +693,63 @@ void NURBS_HDiv2DFiniteElement::CalcDivShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS_HDiv2DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == dof, "");
|
||||
MFEM_ASSERT(vc.GetVDim() == 2, "");
|
||||
Vector x(2), mx(2);
|
||||
IntegrationPoint ip;
|
||||
int o = 0;
|
||||
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NURBS_HDiv2DFiniteElement::~NURBS_HDiv2DFiniteElement()
|
||||
{
|
||||
if (kv1[0]) { delete kv1[0]; }
|
||||
@@ -696,6 +929,120 @@ void NURBS_HDiv3DFiniteElement::CalcDivShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NURBS_HDiv3DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == dof, "");
|
||||
MFEM_ASSERT(vc.GetVDim() == 3, "");
|
||||
Vector x(2), mx(3);
|
||||
IntegrationPoint ip;
|
||||
|
||||
int o = 0;
|
||||
|
||||
for (int k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 2)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 2);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k <= orders[2]+1; k++)
|
||||
{
|
||||
real_t kz = kv1[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv1[2]->inSpan(kz, ijk[2]+orders[2]+1))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv1[2]->GetRefPoint(kz, ijk[2]+orders[2]+1);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.AdjugateJacobian().Mult(x,mx);
|
||||
dofs(o) = mx(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
NURBS_HDiv3DFiniteElement::~NURBS_HDiv3DFiniteElement()
|
||||
{
|
||||
if (kv1[0]) { delete kv1[0]; }
|
||||
@@ -817,13 +1164,68 @@ void NURBS_HCurl2DFiniteElement::CalcCurlShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS_HCurl2DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == dof, "");
|
||||
MFEM_ASSERT(vc.GetVDim() == 2, "");
|
||||
Vector x(2), xm(2);
|
||||
IntegrationPoint ip;
|
||||
int i, j, o;
|
||||
for (o = 0, j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NURBS_HCurl2DFiniteElement::~NURBS_HCurl2DFiniteElement()
|
||||
{
|
||||
if (kv1[0]) { delete kv1[0]; }
|
||||
if (kv1[1]) { delete kv1[1]; }
|
||||
}
|
||||
|
||||
|
||||
void NURBS_HCurl3DFiniteElement::SetOrder() const
|
||||
{
|
||||
orders[0] = kv[0]->GetOrder();
|
||||
@@ -1003,11 +1405,124 @@ void NURBS_HCurl3DFiniteElement::CalcCurlShape(const IntegrationPoint &ip,
|
||||
curl_shape(o,0) = shape1_x(i)*dsy1_sz;
|
||||
curl_shape(o,1) = -dshape1_x(i)*sy1_sz;
|
||||
curl_shape(o,2) = 0.0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NURBS_HCurl3DFiniteElement::Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans,
|
||||
Vector &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(dofs.Size() == dof, "");
|
||||
MFEM_ASSERT(vc.GetVDim() == 3, "");
|
||||
Vector x(3), xm(3);
|
||||
IntegrationPoint ip;
|
||||
|
||||
int o = 0;
|
||||
for (int k = 0; k <= orders[2]+1; k++)
|
||||
{
|
||||
real_t kz = kv1[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv1[2]->inSpan(kz, ijk[2]+orders[2]+1))
|
||||
{
|
||||
o += (orders[0] + 1)*(orders[1] + 2);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv1[2]->GetRefPoint(kz, ijk[2]+orders[2]+1);
|
||||
for (int j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 1;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (int i = 0; i <= orders[0]; i++, o++)
|
||||
{
|
||||
real_t kx = kv[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
|
||||
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k <= orders[2]+1; k++)
|
||||
{
|
||||
real_t kz = kv1[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv1[2]->inSpan(kz, ijk[2]+orders[2]+1))
|
||||
{
|
||||
o += (orders[0] + 2)*(orders[1] + 1);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv1[2]->GetRefPoint(kz, ijk[2]+orders[2]+1);
|
||||
for (int j = 0; j <= orders[1]; j++)
|
||||
{
|
||||
real_t ky = kv[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
|
||||
for (int i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k <= orders[2]; k++)
|
||||
{
|
||||
real_t kz = kv[2]->GetBotella(ijk[2] + k);
|
||||
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
|
||||
{
|
||||
o += (orders[0] + 2)*(orders[1] + 2);
|
||||
continue;
|
||||
}
|
||||
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
|
||||
for (int j = 0; j <= orders[1]+1; j++)
|
||||
{
|
||||
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
|
||||
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
|
||||
{
|
||||
o += orders[0] + 2;
|
||||
continue;
|
||||
}
|
||||
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
|
||||
for (int i = 0; i <= orders[0]+1; i++, o++)
|
||||
{
|
||||
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
|
||||
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
|
||||
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
vc.Eval(x, Trans, ip);
|
||||
|
||||
Trans.Jacobian().MultTranspose(x,xm);
|
||||
dofs(o) = xm(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
NURBS_HCurl3DFiniteElement::~NURBS_HCurl3DFiniteElement()
|
||||
{
|
||||
if (kv1[0]) { delete kv1[0]; }
|
||||
|
||||
@@ -86,6 +86,18 @@ public:
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian (const IntegrationPoint &ip,
|
||||
DenseMatrix &hessian) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
};
|
||||
|
||||
/// An arbitrary order 2D NURBS element on a square
|
||||
@@ -121,6 +133,18 @@ public:
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian (const IntegrationPoint &ip,
|
||||
DenseMatrix &hessian) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
};
|
||||
|
||||
/// An arbitrary order 3D NURBS element on a cube
|
||||
@@ -161,6 +185,18 @@ public:
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian (const IntegrationPoint &ip,
|
||||
DenseMatrix &hessian) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
};
|
||||
|
||||
|
||||
@@ -242,6 +278,13 @@ public:
|
||||
void CalcDivShape(const IntegrationPoint &ip,
|
||||
Vector &divshape) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
~NURBS_HDiv2DFiniteElement();
|
||||
};
|
||||
|
||||
@@ -336,6 +379,13 @@ public:
|
||||
void CalcDivShape(const IntegrationPoint &ip,
|
||||
Vector &divshape) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
~NURBS_HDiv3DFiniteElement();
|
||||
};
|
||||
|
||||
@@ -415,6 +465,13 @@ public:
|
||||
void CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
~NURBS_HCurl2DFiniteElement();
|
||||
};
|
||||
|
||||
@@ -506,6 +563,13 @@ public:
|
||||
void CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape) const override;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
/** Evaluate the dofs that are defined on this element.
|
||||
Dofs that can not be evaluated will remain unmodified. */
|
||||
void Project(VectorCoefficient &vcoeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const override;
|
||||
|
||||
~NURBS_HCurl3DFiniteElement();
|
||||
};
|
||||
|
||||
|
||||
+13
-13
@@ -111,36 +111,36 @@ public:
|
||||
| :------: | :---: | :---: | :-------: | :-----: | :---: |
|
||||
| H1_[DIM]_[ORDER] | H1 | * | 1 | VALUE | H1 nodal elements |
|
||||
| H1@[BTYPE]_[DIM]_[ORDER] | H1 | * | * | VALUE | H1 nodal elements |
|
||||
| H1Pos_[DIM]_[ORDER] | H1 | * | 1 | VALUE | H1 nodal elements |
|
||||
| H1Pos_[DIM]_[ORDER] | H1 | * | 2 | VALUE | H1 nodal elements |
|
||||
| H1Pos_Trace_[DIM]_[ORDER] | H^{1/2} | * | 2 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
| H1_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
| H1_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
| H1_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
|
||||
| ND_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | Nedelec vector elements |
|
||||
| ND@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | Nedelec vector elements |
|
||||
| ND_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
|
||||
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
|
||||
| ND_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces,edges) |
|
||||
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * / * | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces,edges) |
|
||||
| ND_R1D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
|
||||
| ND_R1D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
|
||||
| ND_R2D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
|
||||
| ND_R2D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
|
||||
| RT_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | Raviart-Thomas vector elements |
|
||||
| RT@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | Raviart-Thomas vector elements |
|
||||
| RT_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_Trace_[DIM]_[ORDER] | H^{1/2} | * | 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace_[DIM]_[ORDER] | H^{1/2} | * | 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
|
||||
| RT_R1D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
|
||||
| RT_R1D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
|
||||
| RT_R2D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
|
||||
| RT_R2D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
|
||||
| L2_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
|
||||
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
|
||||
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | * | VALUE | Discontinuous L2 elements |
|
||||
| L2Int_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
|
||||
| L2Int_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
|
||||
| L2Int_T[BTYPE]_[DIM]_[ORDER] | L2 | * | * | INTEGRAL | Discontinuous L2 elements |
|
||||
| DG_Iface_[DIM]_[ORDER] | - | * | 0 | VALUE | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_Iface@[BTYPE]_[DIM]_[ORDER] | - | * | 0 | VALUE | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_Iface@[BTYPE]_[DIM]_[ORDER] | - | * | * | VALUE | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_IntIface_[DIM]_[ORDER] | - | * | 0 | INTEGRAL | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_IntIface@[BTYPE]_[DIM]_[ORDER] | - | * | 0 | INTEGRAL | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| DG_IntIface@[BTYPE]_[DIM]_[ORDER] | - | * | * | INTEGRAL | Discontinuous elements on the interface between mesh elements (faces) |
|
||||
| NURBS[ORDER] | - | * | - | VALUE | Non-Uniform Rational B-Splines (NURBS) elements |
|
||||
| LinearNonConf3D | - | 1 | 1 | VALUE | Piecewise-linear nonconforming finite elements in 3D |
|
||||
| CrouzeixRaviart | - | - | - | - | Crouzeix-Raviart nonconforming elements in 2D |
|
||||
@@ -172,7 +172,7 @@ public:
|
||||
| :------: | :--------: |
|
||||
| [DIM] | Dimension of the elements (1D, 2D, 3D) |
|
||||
| [ORDER] | Approximation order of the elements (P0, P1, P2, ...) |
|
||||
| [BTYPE] | BasisType of the element (0-GaussLegendre, 1 - GaussLobatto, 2-Bernstein, 3-OpenUniform, 4-CloseUniform, 5-OpenHalfUniform) |
|
||||
| [BTYPE] | BasisType of the element (0-GaussLegendre, 1-GaussLobatto, 2-Bernstein, 3-OpenUniform, 4-CloseUniform, 5-OpenHalfUniform 6-Serendipity 7-ClosedGL 8-IntegratedGLL) |
|
||||
| [OBTYPE] | Open BasisType of the element for elements which have both types |
|
||||
| [CBTYPE] | Closed BasisType of the element for elements which have both types |
|
||||
|
||||
|
||||
+521
-60
@@ -2352,52 +2352,83 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
void GridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
|
||||
{
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
DofTransformation doftrans;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
if (delta_c == NULL)
|
||||
{
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
switch (type)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
case ProjectType::ELEMENT_L2:
|
||||
ProjectCoefficientElementL2(coeff);
|
||||
return;
|
||||
case ProjectType::GLOBAL_L2:
|
||||
ProjectCoefficientGlobalL2(coeff);
|
||||
return;
|
||||
default:
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Define and assemble linear form
|
||||
LinearForm b(fes);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
|
||||
b.Assemble();
|
||||
switch (type)
|
||||
{
|
||||
case ProjectType::DEFAULT:
|
||||
case ProjectType::ELEMENT_L2:
|
||||
ProjectCoefficientElementL2(coeff);
|
||||
return;
|
||||
case ProjectType::GLOBAL_L2:
|
||||
ProjectCoefficientGlobalL2(coeff);
|
||||
return;
|
||||
case ProjectType::ELEMENT:
|
||||
constexpr real_t signal = std::numeric_limits<real_t>::min();
|
||||
|
||||
// Define and assemble bilinear form
|
||||
BilinearForm a(fes);
|
||||
a.AddDomainIntegrator(new MassIntegrator());
|
||||
a.Assemble();
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
vals = signal;
|
||||
|
||||
// Set solver and preconditioner
|
||||
SparseMatrix A(a.SpMat());
|
||||
GSSmoother prec(A);
|
||||
CGSolver cg;
|
||||
cg.SetOperator(A);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(1000);
|
||||
cg.SetPrintLevel(0);
|
||||
fes->GetFE(i)->Project(coeff,
|
||||
*fes->GetElementTransformation(i),
|
||||
vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
|
||||
// Solve and get solution
|
||||
*this = 0.0;
|
||||
cg.Mult(b,*this);
|
||||
// Remove undefined dofs
|
||||
// The knot location (either Botella, Demko or Greville point)
|
||||
// where the NURBS dof are evaluated might fall outside of the
|
||||
// domain of the element. In that case the value is not set, and
|
||||
// the value remains the signal value.
|
||||
int s = 0;
|
||||
for (int ii = 0; ii < vals.Size(); ii++)
|
||||
{
|
||||
if (vals[ii] != signal)
|
||||
{
|
||||
vdofs[s] = vdofs[ii];
|
||||
vals(s) = vals(ii);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
vdofs.SetSize(s);
|
||||
vals.SetSize(s);
|
||||
|
||||
// Add reduced dofs to global vector
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2410,6 +2441,167 @@ void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientGlobalL2(Coefficient &coeff, real_t rtol,
|
||||
int iter)
|
||||
{
|
||||
// Define and assemble linear form
|
||||
LinearForm b(fes);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
|
||||
b.Assemble();
|
||||
|
||||
// Define and assemble bilinear form
|
||||
BilinearForm a(fes);
|
||||
a.AddDomainIntegrator(new MassIntegrator());
|
||||
a.Assemble();
|
||||
|
||||
// Set solver and preconditioner
|
||||
SparseMatrix A(a.SpMat());
|
||||
GSSmoother prec(A);
|
||||
CGSolver cg;
|
||||
cg.SetOperator(A);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetRelTol(rtol);
|
||||
cg.SetMaxIter(iter);
|
||||
cg.SetPrintLevel(0);
|
||||
|
||||
// Solve and get solution
|
||||
*this = 0.0;
|
||||
cg.Mult(b,*this);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientElementL2(Coefficient &coeff)
|
||||
{
|
||||
Vector Va;
|
||||
ProjectCoefficientElementL2_(coeff, *this, Va);
|
||||
(*this) /= Va;
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientElementL2_(Coefficient &coeff,
|
||||
Vector &x, Vector &Va)
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
Array<int> vdofs;
|
||||
Vector shape,shape2, elvect, elwght;
|
||||
DenseMatrix elmat;
|
||||
Va.SetSize(fes->GetNDofs() );
|
||||
x.SetSize(fes->GetNDofs() );
|
||||
Va = 0.0;
|
||||
x = 0.0;
|
||||
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
for (int e = 0; e < fes->GetNE(); e++)
|
||||
{
|
||||
fes->GetElementDofs (e, vdofs, doftrans);
|
||||
ElementTransformation &tr = *fes -> GetElementTransformation (e);
|
||||
const FiniteElement &el = *fes->GetFE(e);
|
||||
int dof = el.GetDof();
|
||||
shape.SetSize(dof);
|
||||
elvect.SetSize(dof);
|
||||
elwght.SetSize(dof);
|
||||
elmat.SetSize(dof,dof);
|
||||
elvect = 0.0;
|
||||
elwght = 0.0;
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
|
||||
2 * el.GetOrder() + 1);
|
||||
|
||||
// Element vector & weight
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
|
||||
tr.SetIntPoint (&ip);
|
||||
real_t wght = ip.weight*tr.Weight();
|
||||
real_t val = coeff.Eval(tr, ip);
|
||||
|
||||
el.CalcPhysShape(tr, shape);
|
||||
|
||||
elvect.Add(wght * val, shape);
|
||||
elwght.Add(wght, shape);
|
||||
AddMult_a_VVt(wght, shape, elmat);
|
||||
}
|
||||
|
||||
// Solve
|
||||
if (!LinearSolve(elmat, elvect.GetData(),1e-12))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix");
|
||||
}
|
||||
|
||||
// Scale
|
||||
elvect *= elwght;
|
||||
|
||||
// Add reduced dofs to global vector
|
||||
x.AddElementVector(vdofs, elvect);
|
||||
Va.AddElementVector(vdofs, elwght);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int e = 0; e < fes->GetNE(); e++)
|
||||
{
|
||||
fes->GetElementDofs (e, vdofs, doftrans);
|
||||
ElementTransformation &tr = *fes -> GetElementTransformation (e);
|
||||
const FiniteElement &el = *fes->GetFE(e);
|
||||
int dof = el.GetDof();
|
||||
int dim = el.GetDim();
|
||||
int p = el.GetOrder();
|
||||
L2_FECollection fe_coll(p, dim);
|
||||
//H1_FECollection fe_coll(p, dim, BasisType::Positive);
|
||||
const FiniteElement &el2 = *fe_coll.FiniteElementForGeometry(el.GetGeomType());
|
||||
MFEM_ASSERT(el2.GetDof() == dof, "Element dofs do not match.");
|
||||
|
||||
shape.SetSize(dof);
|
||||
shape2.SetSize(dof);
|
||||
elvect.SetSize(dof);
|
||||
elwght.SetSize(dof);
|
||||
elmat.SetSize(dof,dof);
|
||||
elvect = 0.0;
|
||||
elwght = 0.0;
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
|
||||
2 * el.GetOrder() + 1);
|
||||
|
||||
// Element vector & weight
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
|
||||
tr.SetIntPoint (&ip);
|
||||
real_t wght = ip.weight*tr.Weight();
|
||||
real_t val = coeff.Eval(tr, ip);
|
||||
el.CalcPhysShape(tr, shape);
|
||||
el2.CalcPhysShape(tr, shape2);
|
||||
|
||||
elvect.Add(wght * val, shape2);
|
||||
elwght.Add(wght, shape);
|
||||
AddMult_a_VVt(wght, shape2, elmat);
|
||||
}
|
||||
// Solve
|
||||
if (!LinearSolve(elmat, elvect.GetData(),1e-12))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix 2");
|
||||
}
|
||||
// Map to NURBS
|
||||
DenseMatrix I;
|
||||
el2.Project(el,tr,I);
|
||||
if (!LinearSolve(I, elvect.GetData(),1e-32))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix 3");
|
||||
}
|
||||
|
||||
// Scale
|
||||
elvect *= elwght;
|
||||
|
||||
// Add reduced dofs to global vector
|
||||
x.AddElementVector(vdofs, elvect);
|
||||
Va.AddElementVector(vdofs, elwght);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficient(
|
||||
Coefficient &coeff, Array<int> &dofs, int vd)
|
||||
{
|
||||
@@ -2434,49 +2626,318 @@ void GridFunction::ProjectCoefficient(
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff)
|
||||
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
ProjectType type)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
DofTransformation doftrans;
|
||||
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
int i;
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
switch (type)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
case ProjectType::ELEMENT_L2:
|
||||
ProjectCoefficientElementL2(vcoeff);
|
||||
return;
|
||||
case ProjectType::GLOBAL_L2:
|
||||
ProjectCoefficientGlobalL2(vcoeff);
|
||||
return;
|
||||
default:
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Define and assemble linear form
|
||||
LinearForm b(fes);
|
||||
switch (type)
|
||||
{
|
||||
case ProjectType::DEFAULT:
|
||||
case ProjectType::ELEMENT_L2:
|
||||
ProjectCoefficientElementL2(vcoeff);
|
||||
return;
|
||||
case ProjectType::GLOBAL_L2:
|
||||
ProjectCoefficientGlobalL2(vcoeff);
|
||||
return;
|
||||
case ProjectType::ELEMENT:
|
||||
constexpr real_t signal = std::numeric_limits<real_t>::min();
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs, doftrans);
|
||||
vals.SetSize(vdofs.Size());
|
||||
vals = signal;
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
doftrans.TransformPrimal(vals);
|
||||
// Remove undefined dofs
|
||||
// The knot location (either Botella, Demko or Greville point)
|
||||
// where the NURBS dof are evaluated might fall outside of the
|
||||
// domain of the element. In that case the value is not set, and
|
||||
// the value remains the signal value.
|
||||
int s = 0;
|
||||
for (int ii = 0; ii < vals.Size(); ii++)
|
||||
{
|
||||
if (vals[ii] != signal)
|
||||
{
|
||||
vdofs[s] = vdofs[ii];
|
||||
vals(s) = vals(ii);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
vdofs.SetSize(s);
|
||||
vals.SetSize(s);
|
||||
|
||||
// Add reduced dofs to global vector
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol, int iter)
|
||||
{
|
||||
// Define and assemble linear form
|
||||
LinearForm b(fes);
|
||||
BilinearForm a(fes);
|
||||
|
||||
if (fes->GetTypicalFE()->GetRangeType() == mfem::FiniteElement::VECTOR)
|
||||
{
|
||||
b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(vcoeff));
|
||||
b.Assemble();
|
||||
|
||||
// Define and assemble bilinear form
|
||||
BilinearForm a(fes);
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator());
|
||||
a.Assemble();
|
||||
}
|
||||
else
|
||||
{
|
||||
b.AddDomainIntegrator(new VectorDomainLFIntegrator(vcoeff));
|
||||
a.AddDomainIntegrator(new VectorMassIntegrator());
|
||||
}
|
||||
a.Assemble();
|
||||
b.Assemble();
|
||||
|
||||
// Set solver and preconditioner
|
||||
SparseMatrix A(a.SpMat());
|
||||
GSSmoother prec(A);
|
||||
CGSolver cg;
|
||||
cg.SetOperator(A);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(1000);
|
||||
cg.SetPrintLevel(0);
|
||||
// Set solver and preconditioner
|
||||
SparseMatrix A(a.SpMat());
|
||||
GSSmoother prec(A);
|
||||
CGSolver cg;
|
||||
cg.SetOperator(A);
|
||||
cg.SetPreconditioner(prec);
|
||||
cg.SetRelTol(rtol);
|
||||
cg.SetMaxIter(iter);
|
||||
cg.SetPrintLevel(0);
|
||||
|
||||
// Solve and get solution
|
||||
*this = 0.0;
|
||||
cg.Mult(b,*this);
|
||||
// Solve and get solution
|
||||
*this = 0.0;
|
||||
cg.Mult(b,*this);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientElementL2_(VectorCoefficient &vcoeff,
|
||||
Vector &x, Vector &Va)
|
||||
{
|
||||
DofTransformation doftrans;
|
||||
Array<int> vdofs;
|
||||
Vector shapel2, elvect, elwght, val;
|
||||
DenseMatrix shape, elmat;
|
||||
Va.SetSize(Size());
|
||||
x.SetSize(Size());
|
||||
Va = 0.0;
|
||||
x = 0.0;
|
||||
|
||||
if (fes->GetNURBSext() == NULL)
|
||||
{
|
||||
for (int e = 0; e < fes->GetNE(); e++)
|
||||
{
|
||||
fes->GetElementVDofs (e, vdofs, doftrans);
|
||||
ElementTransformation &tr = *fes -> GetElementTransformation (e);
|
||||
const FiniteElement &el = *fes->GetFE(e);
|
||||
int dof = el.GetDof();
|
||||
int dim = el.GetRangeDim();
|
||||
shape.SetSize(dof,dim);
|
||||
shapel2.SetSize(dof);
|
||||
elvect.SetSize(dof);
|
||||
elwght.SetSize(dof);
|
||||
elmat.SetSize(dof,dof);
|
||||
elvect = 0.0;
|
||||
elwght = 0.0;
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
|
||||
2 * el.GetOrder() + 1);
|
||||
|
||||
// Element vector & weight
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
|
||||
tr.SetIntPoint (&ip);
|
||||
real_t wght = ip.weight*tr.Weight();
|
||||
vcoeff.Eval(val, tr, ip);
|
||||
val *= wght;
|
||||
|
||||
el.CalcPhysVShape(tr, shape);
|
||||
|
||||
shape.AddMult (val, elvect);
|
||||
AddMult_a_AAt(wght, shape, elmat);
|
||||
|
||||
shape.GetRowl2(shapel2);
|
||||
elwght.Add(wght, shapel2);
|
||||
}
|
||||
|
||||
// Solve
|
||||
if (!LinearSolve(elmat, elvect.GetData(),1e-12))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix");
|
||||
}
|
||||
|
||||
// Scale
|
||||
elvect *= elwght;
|
||||
|
||||
// Add to global vector
|
||||
x.AddElementVector(vdofs, elvect);
|
||||
|
||||
// Add to weight vector -- no need for an orientation
|
||||
for (int i = 0; i < vdofs.Size(); i++)
|
||||
{
|
||||
vdofs[i] = FiniteElementSpace::DecodeDof(vdofs[i]);
|
||||
}
|
||||
Va.AddElementVector(vdofs, elwght);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DenseMatrix partelmat;
|
||||
Vector shape2;
|
||||
|
||||
if (fes->GetTypicalFE()->GetOrder() >= 6 )
|
||||
{
|
||||
MFEM_WARNING("This project is not stable for"
|
||||
"NURBS VectorFE with order >= 5");
|
||||
}
|
||||
for (int e = 0; e < fes->GetNE(); e++)
|
||||
{
|
||||
fes->GetElementVDofs (e, vdofs, doftrans);
|
||||
ElementTransformation &tr = *fes -> GetElementTransformation (e);
|
||||
const FiniteElement &el = *fes->GetFE(e);
|
||||
int dof = el.GetDof();
|
||||
int dim = el.GetRangeDim();
|
||||
int p = el.GetOrder();
|
||||
L2_FECollection fe_coll(p, dim);
|
||||
const FiniteElement &el2 = *fe_coll.FiniteElementForGeometry(el.GetGeomType());
|
||||
int dof2 = el2.GetDof();
|
||||
MFEM_ASSERT(dof2*dim >= dof, "Element dofs do not match.");
|
||||
shape2.SetSize(dof2);
|
||||
shape.SetSize(dof,dim);
|
||||
shapel2.SetSize(dof);
|
||||
elvect.SetSize(dof2*dim);
|
||||
elwght.SetSize(dof);
|
||||
elmat.SetSize(dof2*dim,dof2*dim);
|
||||
partelmat.SetSize(dof2,dof2);
|
||||
elvect = 0.0;
|
||||
elwght = 0.0;
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
|
||||
2 * el.GetOrder() + 1);
|
||||
|
||||
// Element vector & weight
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
|
||||
tr.SetIntPoint (&ip);
|
||||
real_t wght = ip.weight*tr.Weight();
|
||||
vcoeff.Eval(val, tr, ip);
|
||||
val *= wght;
|
||||
|
||||
el2.CalcPhysShape(tr, shape2);
|
||||
el.CalcPhysVShape(tr, shape);
|
||||
|
||||
for (int k = 0; k < dim; k++)
|
||||
{
|
||||
for (int s = 0; s < dof2; s++)
|
||||
{
|
||||
elvect(dof2*k+s) += val(k) * shape2(s);
|
||||
}
|
||||
}
|
||||
|
||||
MultVVt(shape2, partelmat);
|
||||
partelmat *= wght;
|
||||
for (int k = 0; k < dim; k++)
|
||||
{
|
||||
elmat.AddMatrix(partelmat, dof2*k, dof2*k);
|
||||
}
|
||||
|
||||
shape.GetRowl2(shapel2);
|
||||
elwght.Add(wght, shapel2);
|
||||
}
|
||||
|
||||
// Solve
|
||||
if (!LinearSolve(elmat, elvect.GetData()))
|
||||
{
|
||||
MFEM_WARNING("Error in inverting element local matrix");
|
||||
}
|
||||
|
||||
// Map to NURBS
|
||||
DenseMatrix I;
|
||||
el2.Project(el,tr,I);
|
||||
|
||||
// LSQ solve
|
||||
// For higher order NURBS solving this non-square matrix causes issues.
|
||||
// For Order <=4 the routine seems to work fine.
|
||||
Vector vec(dof);
|
||||
DenseMatrix mat(dof, dof);
|
||||
I.Transpose();
|
||||
I.Mult(elvect, vec);
|
||||
MultAAt(I, mat);
|
||||
if (!LinearSolve(mat, vec.GetData(), 1e-24))
|
||||
{
|
||||
mat.TestInversion();
|
||||
MFEM_WARNING("Error in inverting element local matrix");
|
||||
}
|
||||
elvect = vec;
|
||||
|
||||
// Scale
|
||||
elvect *= elwght;
|
||||
|
||||
// Add to global vector
|
||||
x.AddElementVector(vdofs, elvect);
|
||||
|
||||
// Add to weight vector -- no need for an orientation
|
||||
for (int i = 0; i < vdofs.Size(); i++)
|
||||
{
|
||||
vdofs[i] = FiniteElementSpace::DecodeDof(vdofs[i]);
|
||||
}
|
||||
Va.AddElementVector(vdofs, elwght);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
|
||||
{
|
||||
if (fes->GetTypicalFE()->GetRangeType() == mfem::FiniteElement::VECTOR)
|
||||
{
|
||||
Vector Va;
|
||||
ProjectCoefficientElementL2_(vcoeff, *this, Va);
|
||||
(*this) /= Va;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array<int> vdofs(fes->GetNDofs());
|
||||
Vector x, Va;
|
||||
VectorComponentCoefficient coeff(vcoeff,
|
||||
0); // 0 to ensure we have a valid object
|
||||
|
||||
for (int v = 0; v < VectorDim(); v++)
|
||||
{
|
||||
coeff.SetComponent(v);
|
||||
ProjectCoefficientElementL2_(coeff, x, Va);
|
||||
x /= Va;
|
||||
fes->GetVDofs(v, vdofs);
|
||||
SetSubVector(vdofs, x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+71
-7
@@ -27,6 +27,24 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** This enumerated type describes the three main projection types:
|
||||
- ELEMENT, assigns the degree of freedom per element, as specified in the
|
||||
specific element
|
||||
- GLOBAL_L2, solves a global L2 projection
|
||||
- ELEMENT_L2, solves a element level L2 projection. Inter element
|
||||
connectivity is dealt with similar as in:
|
||||
Bezier-Projection : A unified approach for local projection and
|
||||
quadrature-free refinement and coarsening of NURBS and T-splines with
|
||||
particular application to isogeometric design and analysis
|
||||
[CMAME (284) 2015 pg 55-105]
|
||||
- DEFAULT, for NURBS spaces this is ELEMENT_L2, while for all other spaces
|
||||
this ELEMENT.
|
||||
Note 1: ELEMENT_L2 also works for non NURBS elements
|
||||
Note 2: For NURBS elements the ELEMENT projection gives results without
|
||||
over and undershoots. However, the gradient near the boundary does not
|
||||
converge.*/
|
||||
enum class ProjectType { DEFAULT, ELEMENT, GLOBAL_L2, ELEMENT_L2 };
|
||||
|
||||
/// Class for grid function - Vector with associated FE space.
|
||||
class GridFunction : public Vector
|
||||
{
|
||||
@@ -66,13 +84,17 @@ protected:
|
||||
degree of freedom. */
|
||||
void ProjectDiscCoefficient(VectorCoefficient &coeff, Array<int> &dof_attr);
|
||||
|
||||
/** Helper function for ProjectCoefficientElementL2 */
|
||||
void ProjectCoefficientElementL2_(Coefficient &coeff, Vector &sol, Vector &Va);
|
||||
void ProjectCoefficientElementL2_(VectorCoefficient &vcoeff, Vector &sol,
|
||||
Vector &Va);
|
||||
|
||||
/// Loading helper.
|
||||
void LegacyNCReorder();
|
||||
|
||||
void Destroy();
|
||||
|
||||
public:
|
||||
|
||||
GridFunction() { fes = NULL; fec_owned = NULL; fes_sequence = 0; UseDevice(true); }
|
||||
|
||||
/// Copy constructor. The internal true-dof vector #t_vec is not copied.
|
||||
@@ -84,6 +106,10 @@ public:
|
||||
GridFunction(FiniteElementSpace *f) : Vector(f->GetVSize())
|
||||
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
|
||||
|
||||
/// Same as above but specify the memory type
|
||||
GridFunction(FiniteElementSpace *f, MemoryType mt) : Vector(f->GetVSize(), mt)
|
||||
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
|
||||
|
||||
/// Construct a GridFunction using previously allocated array @a data.
|
||||
/** The GridFunction does not assume ownership of @a data which is assumed to
|
||||
be of size at least `f->GetVSize()`. Similar to the Vector constructor
|
||||
@@ -420,9 +446,30 @@ public:
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection computation depends on the choice of the FiniteElementSpace
|
||||
#fes. Note that this is usually interpolation at the degrees of freedom
|
||||
in each element (not L2 projection). For NURBS spaces these degrees of
|
||||
freedom are not available and L2 projection is resorted to as fallback. */
|
||||
virtual void ProjectCoefficient(Coefficient &coeff);
|
||||
in each element (not L2 projection). For elements without a projection
|
||||
member function one could use ProjectCoefficientGlobalL2 instead.
|
||||
NOTE: For parallel simulations with NURBS elements some dofs might
|
||||
not be defined, if the evaluation point does not reside on this rank.
|
||||
If that is the case it is defined on another rank, and the issue is
|
||||
rectified with the appropriate communication, see in ParGridFunction.
|
||||
*/
|
||||
virtual void ProjectCoefficient(Coefficient &coeff,
|
||||
ProjectType type = ProjectType::DEFAULT);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection is a global L2 projection. This routine can be used a
|
||||
fallback for elements without a projection member function.*/
|
||||
virtual void ProjectCoefficientGlobalL2(Coefficient &coeff,
|
||||
real_t rtol = 1e-12,
|
||||
int iter = 1000);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection is an element local L2 projection, with an appropriate
|
||||
weighting for Dofs that are shared between elements. Inspired on
|
||||
Bezier-Projection [CMAME (284) 2015 pg 55-105]
|
||||
This routine can be used a fallback for elements without a projection
|
||||
member function.*/
|
||||
virtual void ProjectCoefficientElementL2(Coefficient &coeff);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction, using one
|
||||
element for each degree of freedom in @a dofs and nodal interpolation on
|
||||
@@ -432,9 +479,26 @@ public:
|
||||
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction. The
|
||||
projection computation depends on the choice of the FiniteElementSpace
|
||||
#fes. Note that this is usually interpolation at the degrees of freedom
|
||||
in each element (not L2 projection). For NURBS spaces these degrees of
|
||||
freedom are not available and L2 projection is resorted to as fallback. */
|
||||
void ProjectCoefficient(VectorCoefficient &vcoeff);
|
||||
in each element (not L2 projection). For elements without a projection
|
||||
member function one could use ProjectCoefficientGlobalL2 instead.
|
||||
NOTE: For parallel simulations with NURBS elements some dofs might
|
||||
not be defined, if the evaluation point does not reside on this rank.
|
||||
If that is the case it is defined on another rank, and the issue is
|
||||
rectified with the appropriate communication, see in ParGridFunction.*/
|
||||
virtual void ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
ProjectType type = ProjectType::DEFAULT);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection is a global L2 projection. This routine can be used a
|
||||
fallback for elements without a projection member function.*/
|
||||
virtual void ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol = 1e-12,
|
||||
int iter = 1000);
|
||||
|
||||
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
|
||||
projection is a global L2 projection. This routine can be used a
|
||||
fallback for elements without a projection member function.*/
|
||||
virtual void ProjectCoefficientElementL2(VectorCoefficient &vcoeff);
|
||||
|
||||
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction, using
|
||||
one element for each degree of freedom in @a dofs and nodal interpolation
|
||||
|
||||
@@ -1054,7 +1054,154 @@ void DGElasticityDirichletLFIntegrator::AssembleRHSElementVect(
|
||||
}
|
||||
}
|
||||
|
||||
void SlidingElasticityLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
|
||||
{
|
||||
mfem_error("SlidingElasticityLFIntegrator::AssembleRHSElementVect");
|
||||
}
|
||||
|
||||
void SlidingElasticityLFIntegrator::AssembleRHSElementVect(
|
||||
const FiniteElement &el, FaceElementTransformations &Tr, Vector &elvect)
|
||||
{
|
||||
MFEM_ASSERT(Tr.Elem2No < 0, "interior boundary is not supported");
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector shape;
|
||||
DenseMatrix dshape;
|
||||
DenseMatrix adjJ;
|
||||
DenseMatrix dshape_ps;
|
||||
Vector nor;
|
||||
Vector dshape_dn;
|
||||
Vector dshape_du;
|
||||
real_t g_val;
|
||||
Vector nt_val;
|
||||
#endif
|
||||
|
||||
const int dim = el.GetDim();
|
||||
const int ndofs = el.GetDof();
|
||||
const int nvdofs = dim*ndofs;
|
||||
|
||||
elvect.SetSize(nvdofs);
|
||||
elvect = 0.0;
|
||||
|
||||
adjJ.SetSize(dim);
|
||||
shape.SetSize(ndofs);
|
||||
dshape.SetSize(ndofs, dim);
|
||||
dshape_ps.SetSize(ndofs, dim);
|
||||
nor.SetSize(dim);
|
||||
dshape_dn.SetSize(ndofs);
|
||||
dshape_du.SetSize(ndofs);
|
||||
nt_val.SetSize(dim);
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
const int order = 2*el.GetOrder(); // <-----
|
||||
ir = &IntRules.Get(Tr.GetGeometryType(), order);
|
||||
}
|
||||
|
||||
for (int pi = 0; pi < ir->GetNPoints(); ++pi)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(pi);
|
||||
|
||||
// Set the integration point in the face and the neighboring element
|
||||
Tr.SetAllIntPoints(&ip);
|
||||
|
||||
// Access the neighboring element's integration point
|
||||
const IntegrationPoint &eip = Tr.GetElement1IntPoint();
|
||||
|
||||
el.CalcShape(eip, shape);
|
||||
el.CalcDShape(eip, dshape);
|
||||
|
||||
CalcAdjugate(Tr.Elem1->Jacobian(), adjJ);
|
||||
Mult(dshape, adjJ, dshape_ps);
|
||||
|
||||
if (dim == 1)
|
||||
{
|
||||
nor(0) = 2*eip.x - 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
CalcOrtho(Tr.Jacobian(), nor);
|
||||
}
|
||||
|
||||
if (!nt)
|
||||
{
|
||||
// Set nt to the unit normal vector if not provided
|
||||
nt_val = nor;
|
||||
nt_val /= nt_val.Norml2();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Evaluate the vector field using the face transformation.
|
||||
nt->Eval(nt_val, Tr, ip);
|
||||
}
|
||||
|
||||
// Evaluate the Dirichlet b.c. using the face transformation.
|
||||
g_val = g->Eval(Tr, ip);
|
||||
|
||||
real_t WL, WM, jcoef;
|
||||
{
|
||||
const real_t W = ip.weight / Tr.Elem1->Weight();
|
||||
WL = W * lambda->Eval(*Tr.Elem1, eip);
|
||||
WM = W * mu->Eval(*Tr.Elem1, eip);
|
||||
jcoef = kappa * (WL + 2.0*WM) * (nor*nor);
|
||||
dshape_ps.Mult(nor, dshape_dn);
|
||||
dshape_ps.Mult(nt_val, dshape_du);
|
||||
}
|
||||
|
||||
// alpha < g, (lambda div(v) I + mu (grad(v) + grad(v)^T)) n . ñ > +
|
||||
// + kappa < h^{-1} (lambda + 2 mu) g, v . ñ >
|
||||
|
||||
// i = idof + ndofs * im
|
||||
// v_phi(i,d) = delta(im,d) phi(idof)
|
||||
// div(v_phi(i)) = dphi(idof,im)
|
||||
// (grad(v_phi(i)))(k,l) = delta(im,k) dphi(idof,l)
|
||||
//
|
||||
// term 1:
|
||||
// alpha < g, lambda div(v_phi(i)) n . ñ > =
|
||||
// alpha lambda g div(v_phi(i)) (n.ñ) =
|
||||
// alpha lambda g dphi(idof,im) (n.ñ) --> quadrature -->
|
||||
// ip.weight/det(J1) alpha lambda g (nor.ñ) dshape_ps(idof,im) =
|
||||
// alpha * WL * g_val * (nor*nt_val) * dshape_ps(idof,im)
|
||||
// term 2:
|
||||
// alpha < g, mu grad(v_phi(i)) n . ñ > =
|
||||
// alpha mu g ñ^T grad(v_phi(i)) n =
|
||||
// alpha mu g ñ(k) delta(im,k) dphi(idof,l) n(l) =
|
||||
// alpha mu g ñ(im) dphi(idof,l) n(l) --> quadrature -->
|
||||
// ip.weight/det(J1) alpha mu ñ(im) g dshape_ps(idof,l) nor(l) =
|
||||
// alpha * WM * g_val * nt_val(im) * dshape_dn(idof)
|
||||
// term 3:
|
||||
// alpha < g, mu (grad(v_phi(i)))^T n . ñ > =
|
||||
// alpha mu g n^T grad(v_phi(i)) ñ =
|
||||
// alpha mu g n(k) delta(im,k) dphi(idof,l) ñ(l) =
|
||||
// alpha mu g n(im) dphi(idof,l) ñ(l) --> quadrature -->
|
||||
// ip.weight/det(J1) alpha mu g nor(im) dshape_ps(idof,l) ñ(l) =
|
||||
// alpha * WM * g_val * nor(im) * dshape_du(idof)
|
||||
// term j:
|
||||
// < kappa h^{-1} (lambda + 2 mu) g, ñ . v_phi(i) > =
|
||||
// kappa/h (lambda + 2 mu) g ñ(k) v_phi(i,k) =
|
||||
// kappa/h (lambda + 2 mu) g ñ(k) delta(im,k) phi(idof) =
|
||||
// kappa/h (lambda + 2 mu) g ñ(im) phi(idof) --> quadrature -->
|
||||
// [ 1/h = |nor|/det(J1) ]
|
||||
// ip.weight/det(J1) |nor|^2 (lambda + 2 mu) kappa g ñ(im) phi(idof) =
|
||||
// jcoef * g_val * nt_val(im) * shape(idof)
|
||||
|
||||
WM *= alpha;
|
||||
const real_t t1 = alpha * WL * g_val * (nor*nt_val);
|
||||
for (int im = 0, i = 0; im < dim; ++im)
|
||||
{
|
||||
const real_t t2 = WM * g_val * nt_val(im);
|
||||
const real_t t3 = WM * g_val * nor(im);
|
||||
const real_t tj = jcoef * g_val * nt_val(im);
|
||||
for (int idof = 0; idof < ndofs; ++idof, ++i)
|
||||
{
|
||||
elvect(i) += (t1*dshape_ps(idof,im) + t2*dshape_dn(idof) +
|
||||
t3*dshape_du(idof) + tj*shape(idof));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WhiteGaussianNoiseDomainLFIntegrator::AssembleRHSElementVect
|
||||
(const FiniteElement &el,
|
||||
|
||||
@@ -646,6 +646,62 @@ public:
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
|
||||
/** Boundary linear form integrator for imposing non-zero Dirichlet boundary
|
||||
conditions, in a Nitsche elasticity formulation. Specifically, the linear
|
||||
form is given by
|
||||
$$
|
||||
\begin{split}
|
||||
b(v) &:= \alpha \int_\Gamma (\lambda\, \mathrm{div}(v)\, I + \mu (\nabla v
|
||||
+ \nabla v^{\mathrm{T}}))\, n \cdot \tilde{n}\, g\, dS + \kappa \int_\Gamma
|
||||
h^{-1} (\lambda + 2\mu) (v \cdot \tilde{n})\, g\, dS
|
||||
\end{split}
|
||||
$$
|
||||
where $g$ is the given Dirichlet data, $n$ is the unit normal, $\tilde{n}$ is
|
||||
a unit vector field, and $\alpha = \pm 1$, $\kappa > 0$ are the Nitsche
|
||||
parameters. The parameters $\lambda$ and $\mu$ should match the parameters
|
||||
with the same names used in the bilinear form integrator,
|
||||
SlidingElasticityIntegrator.
|
||||
*/
|
||||
class SlidingElasticityLFIntegrator : public LinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
Coefficient *g;
|
||||
VectorCoefficient *nt;
|
||||
Coefficient *lambda, *mu;
|
||||
real_t alpha, kappa;
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector shape;
|
||||
DenseMatrix dshape;
|
||||
DenseMatrix adjJ;
|
||||
DenseMatrix dshape_ps;
|
||||
Vector nor;
|
||||
Vector dshape_dn;
|
||||
Vector dshape_du;
|
||||
real_t g_val;
|
||||
Vector nt_val;
|
||||
#endif
|
||||
|
||||
public:
|
||||
SlidingElasticityLFIntegrator(Coefficient &g_,
|
||||
Coefficient &lambda_, Coefficient &mu_,
|
||||
real_t kappa_)
|
||||
: g(&g_), nt(NULL), lambda(&lambda_), mu(&mu_), alpha(-1.0), kappa(kappa_) {}
|
||||
|
||||
SlidingElasticityLFIntegrator(Coefficient &g_, VectorCoefficient &nt_,
|
||||
Coefficient &lambda_, Coefficient &mu_,
|
||||
real_t alpha_, real_t kappa_)
|
||||
: g(&g_), nt(&nt_), lambda(&lambda_), mu(&mu_), alpha(alpha_), kappa(kappa_) {}
|
||||
|
||||
void AssembleRHSElementVect(const FiniteElement &el,
|
||||
ElementTransformation &Tr,
|
||||
Vector &elvect) override;
|
||||
void AssembleRHSElementVect(const FiniteElement &el,
|
||||
FaceElementTransformations &Tr,
|
||||
Vector &elvect) override;
|
||||
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
|
||||
/** Class for spatial white Gaussian noise integration.
|
||||
|
||||
|
||||
+16
-2
@@ -23,6 +23,8 @@ class BatchedLOR_DG : BatchedLORKernel
|
||||
{
|
||||
IntegrationRule ir_face; ///< Collocated Gauss-Lobatto face quadrature rule.
|
||||
real_t kappa; ///< DG penalty parameter.
|
||||
bool has_bdr_integ; ///< Is there a boundary integrator?
|
||||
const Array<int> *bdr_markers; ///< Boundary integrator markers.
|
||||
public:
|
||||
template <int ORDER, int SDIM> void Assemble2D();
|
||||
template <int ORDER> void Assemble3D();
|
||||
@@ -38,8 +40,7 @@ public:
|
||||
ProjectLORCoefficient<MassIntegrator>(a, c1);
|
||||
ProjectLORCoefficient<DiffusionIntegrator>(a, c2);
|
||||
|
||||
auto *integ = GetInteriorFaceIntegrator<DGDiffusionIntegrator>(a);
|
||||
if (integ)
|
||||
if (auto *integ = GetInteriorFaceIntegrator<DGDiffusionIntegrator>(a))
|
||||
{
|
||||
kappa = integ->GetPenaltyParameter();
|
||||
}
|
||||
@@ -47,6 +48,19 @@ public:
|
||||
{
|
||||
kappa = 0.0;
|
||||
}
|
||||
|
||||
has_bdr_integ = false;
|
||||
auto *bdr_face_integs = a.GetBFBFI();
|
||||
for (int i = 0; i < bdr_face_integs->Size(); ++i)
|
||||
{
|
||||
if (auto *integ = dynamic_cast<DGDiffusionIntegrator*>((*bdr_face_integs)[i]))
|
||||
{
|
||||
kappa = integ->GetPenaltyParameter();
|
||||
bdr_markers = (*a.GetBFBFI_Marker())[i];
|
||||
has_bdr_integ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Compute and return the face info array.
|
||||
|
||||
@@ -22,9 +22,13 @@ namespace mfem
|
||||
Array<int> BatchedLOR_DG::GetFaceInfo() const
|
||||
{
|
||||
Mesh &mesh = *fes_ho.GetMesh();
|
||||
const Array<int> &bdr_face_attrs = mesh.GetBdrFaceAttributes();
|
||||
const int nf = mesh.GetNumFaces();
|
||||
Array<int> face_info(nf * 6); // (e0, f0, o0, e1, f1, o1)
|
||||
auto h_face_info = Reshape(face_info.HostWrite(), 6, nf);
|
||||
|
||||
int bdr_face_counter = 0;
|
||||
|
||||
for (int f = 0; f < nf; ++f)
|
||||
{
|
||||
auto finfo = mesh.GetFaceInformation(f);
|
||||
@@ -43,6 +47,19 @@ Array<int> BatchedLOR_DG::GetFaceInfo() const
|
||||
h_face_info(4, f) = -1;
|
||||
h_face_info(5, f) = -1;
|
||||
}
|
||||
|
||||
if (finfo.IsBoundary())
|
||||
{
|
||||
// Check if Neumann boundary; skip these when adding boundary penalties
|
||||
const int bdr_attr = bdr_face_attrs[bdr_face_counter];
|
||||
if (!has_bdr_integ || (bdr_markers && !(*bdr_markers)[bdr_attr - 1]))
|
||||
{
|
||||
h_face_info(0, f) = -1;
|
||||
h_face_info(1, f) = -1;
|
||||
h_face_info(2, f) = -1;
|
||||
}
|
||||
bdr_face_counter += 1;
|
||||
}
|
||||
}
|
||||
return face_info;
|
||||
}
|
||||
@@ -144,6 +161,7 @@ void BatchedLOR_DG::AssembleFaceTerms()
|
||||
{
|
||||
const int f_0 = d_face_info(1, f);
|
||||
const int f_1 = d_face_info(4, f);
|
||||
if (f_0 < 0) { return; } // Skip Neumann boundary faces
|
||||
const int nsides = (f_1 >= 0) ? 2 : 1;
|
||||
for (int el_i = 0; el_i < nsides; ++el_i)
|
||||
{
|
||||
|
||||
@@ -78,10 +78,7 @@ template <int Dim>
|
||||
void BuildBoxes(const Mesh &mesh,
|
||||
std::vector<::moonolith::AABB<Dim, double>> &element_boxes)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
const int dim = mesh.Dimension();
|
||||
assert(dim == Dim);
|
||||
#endif
|
||||
MFEM_ASSERT(mesh.Dimension() == Dim, "Mesh and box dimensions mismatched");
|
||||
element_boxes.resize(mesh.GetNE());
|
||||
|
||||
DenseMatrix pts;
|
||||
|
||||
+152
-2
@@ -543,13 +543,22 @@ void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
void ParGridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
|
||||
{
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
|
||||
if (delta_c == NULL)
|
||||
{
|
||||
GridFunction::ProjectCoefficient(coeff);
|
||||
(*this) = std::numeric_limits<real_t>::min();
|
||||
GridFunction::ProjectCoefficient(coeff,type);
|
||||
|
||||
// Accumulate for all vdofs.
|
||||
if (pfes->GetNURBSext())
|
||||
{
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(data, GroupCommunicator::Max);
|
||||
gcomm.Bcast<real_t>(data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -565,6 +574,147 @@ void ParGridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
ProjectType type)
|
||||
{
|
||||
GridFunction::ProjectCoefficient(vcoeff, type);
|
||||
|
||||
// Accumulate for all vdofs.
|
||||
if (pfes->GetNURBSext())
|
||||
{
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(data, GroupCommunicator::Max);
|
||||
gcomm.Bcast<real_t>(data);
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficientGlobalL2(Coefficient &coeff,
|
||||
real_t rtol,
|
||||
int iter)
|
||||
{
|
||||
// Define and assemble linear form
|
||||
ParLinearForm b(pfes);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
|
||||
b.Assemble();
|
||||
|
||||
// Define and assemble bilinear form
|
||||
ParBilinearForm a(pfes);
|
||||
a.AddDomainIntegrator(new MassIntegrator());
|
||||
a.Assemble();
|
||||
|
||||
// Configure solver
|
||||
OperatorPtr A;
|
||||
Vector B, X, x(*this);
|
||||
Array<int> ess_tdof_list;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
Solver *prec = new HypreBoomerAMG;
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(rtol);
|
||||
cg.SetMaxIter(iter);
|
||||
cg.SetPrintLevel(0);
|
||||
cg.SetPreconditioner(*prec);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
delete prec;
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficientElementL2(Coefficient &coeff)
|
||||
{
|
||||
Vector Va;
|
||||
ProjectCoefficientElementL2_(coeff, *this, Va);
|
||||
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(GetData());
|
||||
|
||||
gcomm.Reduce<real_t>(Va.GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(Va.GetData());
|
||||
(*this)/=Va;
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol, int iter)
|
||||
{
|
||||
// Define and assemble linear form
|
||||
ParLinearForm b(pfes);
|
||||
ParBilinearForm a(pfes);
|
||||
|
||||
// Dimension argument to GetRangeType is arbitrary to be 3, could also be 2.
|
||||
if (fes->FEColl()->GetRangeType(3) == mfem::FiniteElement::VECTOR)
|
||||
{
|
||||
b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(vcoeff));
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator());
|
||||
}
|
||||
else
|
||||
{
|
||||
b.AddDomainIntegrator(new VectorDomainLFIntegrator(vcoeff));
|
||||
a.AddDomainIntegrator(new VectorMassIntegrator());
|
||||
}
|
||||
b.Assemble();
|
||||
a.Assemble();
|
||||
|
||||
// Configure solver
|
||||
OperatorPtr A;
|
||||
Vector B, X, x(*this);
|
||||
x = 0.0;
|
||||
Array<int> ess_tdof_list;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
Solver *prec = new HypreBoomerAMG;
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(rtol);
|
||||
cg.SetMaxIter(iter);
|
||||
cg.SetPrintLevel(0);
|
||||
cg.SetPreconditioner(*prec);
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
x.Print();
|
||||
delete prec;
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
|
||||
{
|
||||
if (fes->GetTypicalFE()->GetRangeType() == mfem::FiniteElement::VECTOR)
|
||||
{
|
||||
Vector Va;
|
||||
ProjectCoefficientElementL2_(vcoeff, *this, Va);
|
||||
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(GetData());
|
||||
|
||||
gcomm.Reduce<real_t>(Va.GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(Va.GetData());
|
||||
(*this)/=Va;
|
||||
}
|
||||
else
|
||||
{
|
||||
Array<int> vdofs(fes->GetNDofs());
|
||||
Vector x, Va, gVa(Size());
|
||||
VectorComponentCoefficient coeff(vcoeff,0);
|
||||
*this = 0.0;
|
||||
gVa = 0.0;
|
||||
for (int v = 0; v < VectorDim(); v++)
|
||||
{
|
||||
coeff.SetComponent(v);
|
||||
ProjectCoefficientElementL2_(coeff, x, Va);
|
||||
fes->GetVDofs(v, vdofs);
|
||||
SetSubVector(vdofs, x);
|
||||
gVa.SetSubVector(vdofs, Va);
|
||||
}
|
||||
|
||||
GroupCommunicator &gcomm = pfes->GroupComm();
|
||||
gcomm.Reduce<real_t>(GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(GetData());
|
||||
|
||||
gcomm.Reduce<real_t>(gVa.GetData(), GroupCommunicator::Sum);
|
||||
gcomm.Bcast<real_t>(gVa.GetData());
|
||||
*this /= gVa;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParGridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff)
|
||||
{
|
||||
// local maximal element attribute for each dof
|
||||
|
||||
+21
-1
@@ -72,6 +72,10 @@ public:
|
||||
|
||||
ParGridFunction(ParFiniteElementSpace *pf) : GridFunction(pf), pfes(pf) { }
|
||||
|
||||
/// Same as above but specify the device memory type
|
||||
ParGridFunction(ParFiniteElementSpace *pf, MemoryType mt) :
|
||||
GridFunction(pf, mt), pfes(pf) { }
|
||||
|
||||
/// Construct a ParGridFunction using previously allocated array @a data.
|
||||
/** The ParGridFunction does not assume ownership of @a data which is assumed
|
||||
to be of size at least `pf->GetVSize()`. Similar to the GridFunction and
|
||||
@@ -257,7 +261,11 @@ public:
|
||||
void GetElementDofValues(int el, Vector &dof_vals) const override;
|
||||
|
||||
using GridFunction::ProjectCoefficient;
|
||||
void ProjectCoefficient(Coefficient &coeff) override;
|
||||
void ProjectCoefficient(Coefficient &coeff,
|
||||
ProjectType type = ProjectType::DEFAULT) override;
|
||||
|
||||
void ProjectCoefficient(VectorCoefficient &vcoeff,
|
||||
ProjectType type = ProjectType::DEFAULT) override;
|
||||
|
||||
using GridFunction::ProjectDiscCoefficient;
|
||||
/** @brief Project a discontinuous vector coefficient as a grid function on
|
||||
@@ -282,6 +290,18 @@ public:
|
||||
void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
const Array<int> &bdr_attr) override;
|
||||
|
||||
void ProjectCoefficientGlobalL2(Coefficient &coeff,
|
||||
real_t rtol = 1e-12,
|
||||
int iter = 1000) override;
|
||||
|
||||
void ProjectCoefficientElementL2(Coefficient &coeff) override;
|
||||
|
||||
void ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
|
||||
real_t rtol = 1e-12,
|
||||
int iter = 1000) override;
|
||||
|
||||
void ProjectCoefficientElementL2(VectorCoefficient &vcoeff) override;
|
||||
|
||||
/// @brief Returns ||u_ex - u_h||_L1 in parallel for H1 or L2 elements
|
||||
///
|
||||
/// @see GridFunction::ComputeL1Error(Coefficient *exsol[],
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -716,6 +718,29 @@ void Device::DeviceMem(size_t *free, size_t *total)
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string Device::GetUUID(const int device_id)
|
||||
{
|
||||
std::stringstream res;
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
cudaDeviceProp prop;
|
||||
MFEM_GPU_CHECK(cudaGetDeviceProperties(&prop, device_id));
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
res << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< static_cast<unsigned>(prop.uuid.bytes[i]);
|
||||
}
|
||||
#elif defined(MFEM_USE_HIP)
|
||||
hipUUID uuid;
|
||||
MFEM_GPU_CHECK(hipDeviceGetUuid(&uuid, device_id));
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
res << std::setfill('0') << std::setw(2) << std::hex
|
||||
<< static_cast<unsigned>(uuid.bytes[i]);
|
||||
}
|
||||
#endif
|
||||
return res.str();
|
||||
}
|
||||
|
||||
int Device::NumMultiprocessors(int dev)
|
||||
{
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
|
||||
@@ -255,6 +255,10 @@ public:
|
||||
/// Get the number of available devices (may be called before configuration).
|
||||
static int GetDeviceCount();
|
||||
|
||||
/// Gets a string representation of the GPU UUID.
|
||||
/// 0 <= @a device_id < GetDeviceCount()
|
||||
static std::string GetUUID(const int device_id = 0);
|
||||
|
||||
/** @brief Return true if any of the backends in the backend mask, @a b_mask,
|
||||
are allowed. */
|
||||
/** This method can be used with any of the Backend::Id constants, the
|
||||
|
||||
+1
-1
@@ -146,7 +146,7 @@ public:
|
||||
int *ReadWriteJ(bool on_dev = true) { return J.ReadWrite(on_dev); }
|
||||
const int *HostReadJ() const { return J.HostRead(); }
|
||||
int *HostWriteJ() { return J.HostWrite(); }
|
||||
int *ReadWriteJ() { return J.HostReadWrite(); }
|
||||
int *HostReadWriteJ() { return J.HostReadWrite(); }
|
||||
|
||||
/// Sort the column (TYPE II) indices in each row.
|
||||
void SortRows();
|
||||
|
||||
@@ -1370,6 +1370,35 @@ void DenseMatrix::Getl1Diag(Vector &l) const
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::GetRowl1(Vector &l) const
|
||||
{
|
||||
l.SetSize(height);
|
||||
l = 0.0;
|
||||
|
||||
for (int j = 0; j < width; ++j)
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
l(i) += fabs((*this)(i,j));
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::GetRowl2(Vector &l) const
|
||||
{
|
||||
l.SetSize(height);
|
||||
l = 0.0;
|
||||
|
||||
for (int j = 0; j < width; ++j)
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
l[i] += operator()(i,j)*operator()(i,j);
|
||||
}
|
||||
|
||||
for (int i = 0; i < height; ++i)
|
||||
{
|
||||
l[i] = sqrt(l[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void DenseMatrix::GetRowSums(Vector &l) const
|
||||
{
|
||||
l.SetSize(height);
|
||||
|
||||
+6
-2
@@ -346,8 +346,12 @@ public:
|
||||
/// Returns the diagonal of the matrix
|
||||
void GetDiag(Vector &d) const;
|
||||
/// Returns the l1 norm of the rows of the matrix v_i = sum_j |a_ij|
|
||||
void Getl1Diag(Vector &l) const;
|
||||
/// Compute the row sums of the DenseMatrix
|
||||
MFEM_DEPRECATED void Getl1Diag(Vector &l) const;
|
||||
/// Returns the l1 norm of the rows of the matrix v_i = sum_j |a_ij|
|
||||
void GetRowl1(Vector &l) const;
|
||||
/// Returns the l2norm of the rows of the DenseMatrix
|
||||
void GetRowl2(Vector &l) const;
|
||||
/// Returns the row sums of the DenseMatrix
|
||||
void GetRowSums(Vector &l) const;
|
||||
|
||||
/// Creates n x n diagonal matrix with diagonal elements c
|
||||
|
||||
+823
-146
File diff suppressed because it is too large
Load Diff
+755
-80
File diff suppressed because it is too large
Load Diff
@@ -1681,6 +1681,13 @@ void HypreParMatrix::GetOffd(SparseMatrix &offd, HYPRE_BigInt* &cmap) const
|
||||
cmap = A->col_map_offd;
|
||||
}
|
||||
|
||||
void HypreParMatrix::GetOffdColMap(HYPRE_BigInt* &cmap,
|
||||
HYPRE_Int &num_cols) const
|
||||
{
|
||||
cmap = A->col_map_offd;
|
||||
num_cols = hypre_CSRMatrixNumCols(A->offd);
|
||||
}
|
||||
|
||||
void HypreParMatrix::MergeDiagAndOffd(SparseMatrix &merged)
|
||||
{
|
||||
HostRead();
|
||||
|
||||
@@ -665,6 +665,8 @@ public:
|
||||
void GetDiag(SparseMatrix &diag) const;
|
||||
/// Get the local off-diagonal block. NOTE: 'offd' will not own any data.
|
||||
void GetOffd(SparseMatrix &offd, HYPRE_BigInt* &cmap) const;
|
||||
/// Get the global column mapping for the local off-diagonal block.
|
||||
void GetOffdColMap(HYPRE_BigInt* &cmap, HYPRE_Int &num_cols) const;
|
||||
/** @brief Get a single SparseMatrix containing all rows from this processor,
|
||||
merged from the diagonal and off-diagonal blocks stored by the
|
||||
HypreParMatrix. */
|
||||
@@ -959,6 +961,14 @@ public:
|
||||
const Memory<HYPRE_Int> &GetDiagMemoryJ() const { return mem_diag.J; }
|
||||
const Memory<real_t> &GetDiagMemoryData() const { return mem_diag.data; }
|
||||
|
||||
Memory<HYPRE_Int> &GetOffdMemoryI() { return mem_offd.I; }
|
||||
Memory<HYPRE_Int> &GetOffdMemoryJ() { return mem_offd.J; }
|
||||
Memory<real_t> &GetOffdMemoryData() { return mem_offd.data; }
|
||||
|
||||
const Memory<HYPRE_Int> &GetOffdMemoryI() const { return mem_offd.I; }
|
||||
const Memory<HYPRE_Int> &GetOffdMemoryJ() const { return mem_offd.J; }
|
||||
const Memory<real_t> &GetOffdMemoryData() const { return mem_offd.data; }
|
||||
|
||||
/// @brief Prints the locally owned rows in parallel. The resulting files can
|
||||
/// be read with Read_IJMatrix().
|
||||
void Print(const std::string &fname, HYPRE_Int offi = 0,
|
||||
|
||||
+101
-6
@@ -10,6 +10,7 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../general/communication.hpp"
|
||||
#include "../general/forall.hpp"
|
||||
#include "operator.hpp"
|
||||
#include "ode.hpp"
|
||||
|
||||
@@ -184,6 +185,23 @@ void ODESolver::Init(TimeDependentOperator &f_)
|
||||
mem_type = GetMemoryType(f_.GetMemoryClass());
|
||||
}
|
||||
|
||||
void ODESolver::ComputeSlopeFromState(const real_t dt, const Vector &u,
|
||||
Vector &k)
|
||||
{
|
||||
// k currently holds state u(t+dt),
|
||||
// convert to slope k = du/dt ~= (u(t+dt)-u(t))/dt
|
||||
const int usz = u.Size();
|
||||
real_t fac = 1.0/dt;
|
||||
auto d_u = u.Read();
|
||||
auto d_k = k.ReadWrite();
|
||||
|
||||
mfem::forall(usz, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
d_k[i] -= d_u[i];
|
||||
d_k[i] *= fac;
|
||||
});
|
||||
}
|
||||
|
||||
void ForwardEulerSolver::Init(TimeDependentOperator &f_)
|
||||
{
|
||||
ODESolver::Init(f_);
|
||||
@@ -629,6 +647,10 @@ void AdamsMoultonSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
}
|
||||
state.ShiftStages();
|
||||
f->ImplicitSolve(a[0]*dt, x, state[0]);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a[0]*dt, x, state[0]);
|
||||
}
|
||||
x.Add(a[0]*dt, state[0]);
|
||||
t += dt;
|
||||
}
|
||||
@@ -661,7 +683,15 @@ void BackwardEulerSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
{
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(dt, x, k); // solve for k: k = f(x + dt*k, t + dt)
|
||||
x.Add(dt, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
x = k; // x = u_{i+1}
|
||||
}
|
||||
else
|
||||
{
|
||||
x.Add(dt, k);
|
||||
}
|
||||
|
||||
t += dt;
|
||||
}
|
||||
|
||||
@@ -676,7 +706,16 @@ void ImplicitMidpointSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
{
|
||||
f->SetTime(t + dt/2);
|
||||
f->ImplicitSolve(dt/2, x, k);
|
||||
x.Add(dt, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
x.Neg();
|
||||
x.Add(2.0, k);
|
||||
}
|
||||
else
|
||||
{
|
||||
x.Add(dt, k);
|
||||
}
|
||||
|
||||
t += dt;
|
||||
}
|
||||
|
||||
@@ -718,11 +757,19 @@ void SDIRK23Solver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
// note: with gamma_opt=3, both solve are outside [t,t+dt] since a>1
|
||||
f->SetTime(t + gamma*dt);
|
||||
f->ImplicitSolve(gamma*dt, x, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(gamma*dt, x, k);
|
||||
}
|
||||
add(x, (1.-2.*gamma)*dt, k, y); // y = x + (1-2*gamma)*dt*k
|
||||
x.Add(dt/2, k);
|
||||
|
||||
f->SetTime(t + (1.-gamma)*dt);
|
||||
f->ImplicitSolve(gamma*dt, y, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(gamma*dt, y, k);
|
||||
}
|
||||
x.Add(dt/2, k);
|
||||
t += dt;
|
||||
}
|
||||
@@ -749,17 +796,29 @@ void SDIRK34Solver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
|
||||
f->SetTime(t + a*dt);
|
||||
f->ImplicitSolve(a*dt, x, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, x, k);
|
||||
}
|
||||
add(x, (0.5-a)*dt, k, y);
|
||||
add(x, (2.*a)*dt, k, z);
|
||||
x.Add(b*dt, k);
|
||||
|
||||
f->SetTime(t + dt/2);
|
||||
f->ImplicitSolve(a*dt, y, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, y, k);
|
||||
}
|
||||
z.Add((1.-4.*a)*dt, k);
|
||||
x.Add((1.-2.*b)*dt, k);
|
||||
|
||||
f->SetTime(t + (1.-a)*dt);
|
||||
f->ImplicitSolve(a*dt, z, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, z, k);
|
||||
}
|
||||
x.Add(b*dt, k);
|
||||
t += dt;
|
||||
}
|
||||
@@ -785,15 +844,27 @@ void SDIRK33Solver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
|
||||
f->SetTime(t + a*dt);
|
||||
f->ImplicitSolve(a*dt, x, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, x, k);
|
||||
}
|
||||
add(x, (c-a)*dt, k, y);
|
||||
x.Add(b*dt, k);
|
||||
|
||||
f->SetTime(t + c*dt);
|
||||
f->ImplicitSolve(a*dt, y, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, y, k);
|
||||
}
|
||||
x.Add((1.0-a-b)*dt, k);
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(a*dt, x, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, x, k);
|
||||
}
|
||||
x.Add(a*dt, k);
|
||||
t += dt;
|
||||
}
|
||||
@@ -818,6 +889,10 @@ void TrapezoidalRuleSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(dt/2.0, y, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(0.5*dt, y, k);
|
||||
}
|
||||
x.Add(dt/2.0, k);
|
||||
t += dt;
|
||||
}
|
||||
@@ -848,11 +923,19 @@ void ESDIRK32Solver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
|
||||
f->SetTime(t + (2.0*a)*dt);
|
||||
f->ImplicitSolve(a*dt, y, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, y, k);
|
||||
}
|
||||
z.Add(b*dt, k);
|
||||
x.Add(b*dt, k);
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(a*dt, z, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, z, k);
|
||||
}
|
||||
x.Add(a*dt, k);
|
||||
t += dt;
|
||||
}
|
||||
@@ -885,11 +968,19 @@ void ESDIRK33Solver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
|
||||
f->SetTime(t + (2.0*a)*dt);
|
||||
f->ImplicitSolve(a*dt, y, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, y, k);
|
||||
}
|
||||
z.Add(b*dt, k);
|
||||
x.Add(b_2*dt, k);
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(a*dt, z, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(a*dt, z, k);
|
||||
}
|
||||
x.Add(b_3*dt, k);
|
||||
t += dt;
|
||||
}
|
||||
@@ -955,6 +1046,10 @@ void GeneralizedAlphaSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
real_t dt_eff = (gamma*alpha_f/alpha_m)*dt;
|
||||
f->SetTime(t + alpha_f*dt);
|
||||
f->ImplicitSolve(dt_eff, y, k);
|
||||
if (f->ImplicitVarTypeIsState())
|
||||
{
|
||||
ComputeSlopeFromState(dt_eff, y, k);
|
||||
}
|
||||
|
||||
// Update x and xdot
|
||||
x.Add((1.0 - (gamma/alpha_m))*dt, state[0]);
|
||||
@@ -1116,8 +1211,8 @@ void SecondOrderODESolver::EulerStep(Vector &x, Vector &dxdt, real_t &t,
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(0.5*dt*dt, dt, x, dxdt, state[0]);
|
||||
|
||||
x .Add(0.5*dt*dt, state[0]);
|
||||
dxdt.Add(dt, state[0]);
|
||||
x.Add(0.5*dt*dt, state[0]);
|
||||
dxdt.Add(dt, state[0]);
|
||||
t += dt;
|
||||
}
|
||||
|
||||
@@ -1203,8 +1298,8 @@ void NewmarkSolver::Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt)
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(fac3*dt*dt, fac4*dt, x, dxdt, state[0]);
|
||||
|
||||
x .Add(fac3*dt*dt, state[0]);
|
||||
dxdt.Add(fac4*dt, state[0]);
|
||||
x.Add(fac3*dt*dt, state[0]);
|
||||
dxdt.Add(fac4*dt, state[0]);
|
||||
t += dt;
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ public:
|
||||
class ODESolver
|
||||
{
|
||||
protected:
|
||||
using ImplicitVariableType = TimeDependentOperator::ImplicitVariableType;
|
||||
/// Pointer to the associated TimeDependentOperator.
|
||||
TimeDependentOperator *f; // f(.,t) : R^n --> R^n
|
||||
MemoryType mem_type;
|
||||
@@ -192,6 +193,22 @@ public:
|
||||
/// Returns how many State vectors the ODE requires
|
||||
virtual int GetStateSize() { return 0; };
|
||||
|
||||
///@brief Returns @a true if the ODESolver supports the given
|
||||
/// #ImplicitVariableType, @a var, and returns @a false otherwise.
|
||||
///@note Should be overriden in ODESolver that calls TimeDependentOperator::ImplicitSolve().
|
||||
virtual bool SupportsImplicitVariableType(ImplicitVariableType var) const
|
||||
{ return false; };
|
||||
|
||||
/** @brief Compute the finite-difference slope, @a $\frac{du}{dt} \approx \frac{u(t+dt)-u(t)}{dt}$,
|
||||
* and store it in @a k.
|
||||
* @param [in] dt Finite difference step size.
|
||||
* @param [in] u state vector, @a u(t).
|
||||
* @param [in,out] k On input, @a k contains the state vector, @a u( @a t+ @a dt).
|
||||
* On output, @a k contains the computed slope, @a du/dt.
|
||||
* */
|
||||
virtual void ComputeSlopeFromState(const real_t dt, const Vector &u,
|
||||
Vector &k);
|
||||
|
||||
// Help info for ODESolver options
|
||||
static MFEM_EXPORT std::string ExplicitTypes;
|
||||
static MFEM_EXPORT std::string ImplicitTypes;
|
||||
@@ -361,6 +378,12 @@ public:
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -374,6 +397,12 @@ public:
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -395,6 +424,12 @@ public:
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -409,6 +444,12 @@ public:
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -423,6 +464,12 @@ public:
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -437,6 +484,12 @@ public:
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -451,6 +504,12 @@ public:
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -465,6 +524,12 @@ public:
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -490,6 +555,12 @@ public:
|
||||
|
||||
ODEStateData& GetState() override { return state; }
|
||||
const ODEStateData& GetState() const override { return state; }
|
||||
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -606,6 +677,11 @@ public:
|
||||
|
||||
ODEStateData& GetState() override { return state; }
|
||||
const ODEStateData& GetState() const override { return state; }
|
||||
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
|
||||
{
|
||||
return (var == ImplicitVariableType::STATE ||
|
||||
var == ImplicitVariableType::SLOPE);
|
||||
}
|
||||
};
|
||||
|
||||
/** A 1-stage, 2nd order AM method. */
|
||||
|
||||
+37
-3
@@ -381,11 +381,24 @@ public:
|
||||
ADDITIVE_TERM_2
|
||||
};
|
||||
|
||||
/** Used to specify the variable being returned by ImplicitSolve(). This can
|
||||
* be queried by ODESolver to identify the variable being solved for.
|
||||
* @warning Not all ODESolver may support all options. See ODESolver::SupportsImplicitVariableType() */
|
||||
enum ImplicitVariableType
|
||||
{
|
||||
SLOPE, ///< stage slope, $k = \frac{du}{dt}$.
|
||||
STATE ///< stage state, $k = u$.
|
||||
};
|
||||
|
||||
protected:
|
||||
real_t t; ///< Current time.
|
||||
Type type; /**< @brief Describes the form of the TimeDependentOperator, see
|
||||
the documentation of #Type. */
|
||||
EvalMode eval_mode; ///< Current evaluation mode.
|
||||
ImplicitVariableType implicit_variable_type =
|
||||
ImplicitVariableType::SLOPE; /**< @brief
|
||||
Return variable for
|
||||
ImplicitSolve()*/
|
||||
|
||||
public:
|
||||
/** @brief Construct a "square" TimeDependentOperator (u,t) -> k(u,t), where
|
||||
@@ -429,6 +442,24 @@ public:
|
||||
virtual void SetEvalMode(const EvalMode new_eval_mode)
|
||||
{ eval_mode = new_eval_mode; }
|
||||
|
||||
/** @brief Sets the #ImplicitVariableType for ImplicitSolve()*/
|
||||
virtual void SetImplicitVariableType(const ImplicitVariableType variable_type)
|
||||
{ implicit_variable_type = variable_type; }
|
||||
|
||||
/** @brief Returns the #ImplicitVariableType for ImplicitSolve(). */
|
||||
virtual ImplicitVariableType GetImplicitVariableType() const
|
||||
{ return implicit_variable_type; }
|
||||
|
||||
/** @brief Returns @a true if implicit variable is #STATE and @a false otherwise.
|
||||
* Used by ODESolver to identify the stage variable returned by ImplicitSolve() */
|
||||
virtual bool ImplicitVarTypeIsState() const
|
||||
{ return (implicit_variable_type == ImplicitVariableType::STATE); }
|
||||
|
||||
/** @brief Returns @a true if implicit variable is #SLOPE and @a false otherwise.
|
||||
* Used by ODESolver to identify the stage variable returned by ImplicitSolve() */
|
||||
virtual bool ImplicitVarTypeIsSlope() const
|
||||
{ return (implicit_variable_type == ImplicitVariableType::SLOPE); }
|
||||
|
||||
/** @brief Perform the action of the explicit part of the operator, G:
|
||||
@a v = G(@a u, t) where t is the current time.
|
||||
|
||||
@@ -462,7 +493,8 @@ public:
|
||||
|
||||
/** @brief Solve for the unknown @a k, at the current time t, the following
|
||||
equation:
|
||||
F(@a u + @a gamma @a k, @a k, t) = G(@a u + @a gamma @a k, t).
|
||||
1. $F( u + \gamma k, k, t) = G( u + \gamma k, t)$, if solving for stage-slope (default)
|
||||
2. $F( u , \frac{k-u}{\gamma}, t) = G(k, t)$, if solving for stage-state
|
||||
|
||||
For solving an ordinary differential equation of the form
|
||||
$ M \frac{dy}{dt} = g(y,t) $, recall that F and G can be defined in
|
||||
@@ -472,8 +504,9 @@ public:
|
||||
2. F(u,k,t) = M k and G(u,t) = g(u,t)
|
||||
3. F(u,k,t) = M k - g(u,t) and G(u,t) = 0
|
||||
|
||||
Regardless of the choice of F and G, this function should solve for @a k
|
||||
in M @a k = g(@a u + @a gamma @a k, t).
|
||||
Regardless of the choice of F and G, this function should solve for @a k:
|
||||
- $~Mk = g( u + \gamma k, t)~$, if solving for stage-slope.
|
||||
- $~Mk = \gamma g(k, t) + Mu~$, if solving for stage-state
|
||||
|
||||
To see how @a k can be useful, consider the backward Euler method defined
|
||||
by $ y(t + \Delta t) = y(t) + \Delta t k_0 $ where
|
||||
@@ -491,6 +524,7 @@ public:
|
||||
$ y(t) + \Delta t \sum_{j=1}^{i-1} a_{ij} k_j $ and @a gamma set to
|
||||
$ a_{ii} \Delta t $, for $ k_i $. For example, see class SDIRK33Solver.
|
||||
|
||||
See SetImplicitVariableType() to switch between different variable modes.
|
||||
If not re-implemented, this method simply generates an error. */
|
||||
virtual void ImplicitSolve(const real_t gamma, const Vector &u, Vector &k);
|
||||
|
||||
|
||||
+65
-18
@@ -23,15 +23,31 @@ namespace mfem
|
||||
void SparseSmoother::SetOperator(const Operator &a)
|
||||
{
|
||||
oper = dynamic_cast<const SparseMatrix*>(&a);
|
||||
if (oper == NULL)
|
||||
{
|
||||
mfem_error("SparseSmoother::SetOperator : not a SparseMatrix!");
|
||||
}
|
||||
MFEM_VERIFY(oper != nullptr, "Operator must be a SparseMatrix");
|
||||
height = oper->Height();
|
||||
width = oper->Width();
|
||||
|
||||
At.reset();
|
||||
oper_T = nullptr;
|
||||
}
|
||||
|
||||
void SparseSmoother::EnsureTranspose() const
|
||||
{
|
||||
if (oper_T) { return; }
|
||||
|
||||
const real_t tol = 1e-14;
|
||||
if (oper->IsSymmetric() > tol * oper->MaxNorm())
|
||||
{
|
||||
At.reset(Transpose(*oper));
|
||||
oper_T = At.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
At.reset();
|
||||
oper_T = oper;
|
||||
}
|
||||
}
|
||||
|
||||
/// Matrix vector multiplication with GS Smoother.
|
||||
void GSSmoother::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (!iterative_mode)
|
||||
@@ -51,21 +67,33 @@ void GSSmoother::Mult(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
/// Create the Jacobi smoother.
|
||||
DSmoother::DSmoother(const SparseMatrix &a, int t, real_t s, int it)
|
||||
: SparseSmoother(a)
|
||||
void GSSmoother::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
type = t;
|
||||
scale = s;
|
||||
iterations = it;
|
||||
EnsureTranspose();
|
||||
|
||||
if (!iterative_mode)
|
||||
{
|
||||
y = 0.0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < iterations; i++)
|
||||
{
|
||||
if (type != 1)
|
||||
{
|
||||
oper_T->Gauss_Seidel_forw(x, y);
|
||||
}
|
||||
if (type != 2)
|
||||
{
|
||||
oper_T->Gauss_Seidel_back(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Matrix vector multiplication with Jacobi smoother.
|
||||
void DSmoother::Mult(const Vector &x, Vector &y) const
|
||||
void DSmoother::Mult_(const SparseMatrix &A, const Vector &x, Vector &y) const
|
||||
{
|
||||
if (!iterative_mode && type == 0 && iterations == 1)
|
||||
{
|
||||
oper->DiagScale(x, y, scale, use_abs_diag);
|
||||
A.DiagScale(x, y, scale, use_abs_diag);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -90,22 +118,41 @@ void DSmoother::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (type == 0)
|
||||
{
|
||||
oper->Jacobi(x, *p, *r, scale, use_abs_diag);
|
||||
A.Jacobi(x, *p, *r, scale, use_abs_diag);
|
||||
}
|
||||
else if (type == 1)
|
||||
{
|
||||
oper->Jacobi2(x, *p, *r, scale);
|
||||
A.Jacobi2(x, *p, *r, scale);
|
||||
}
|
||||
else if (type == 2)
|
||||
{
|
||||
oper->Jacobi3(x, *p, *r, scale);
|
||||
A.Jacobi3(x, *p, *r, scale);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("DSmoother::Mult wrong type");
|
||||
MFEM_ABORT("Invalid type.");
|
||||
}
|
||||
Swap<Vector*>(r, p);
|
||||
}
|
||||
}
|
||||
|
||||
void DSmoother::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
Mult_(*oper, x, y);
|
||||
}
|
||||
|
||||
void DSmoother::MultTranspose(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (iterations == 1 && !iterative_mode)
|
||||
{
|
||||
Mult_(*oper, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureTranspose();
|
||||
MFEM_VERIFY(type == 0 || !At, "l1 or lumped Jacobi transpose not implemented"
|
||||
" for non-symmetric matrices");
|
||||
Mult_(*oper_T, x, y);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+117
-25
@@ -15,67 +15,159 @@
|
||||
#include "../config/config.hpp"
|
||||
#include "sparsemat.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Abstract base class for smoothers created from a SparseMatrix.
|
||||
class SparseSmoother : public MatrixInverse
|
||||
{
|
||||
protected:
|
||||
const SparseMatrix *oper;
|
||||
const SparseMatrix *oper = nullptr; ///< The underlying matrix.
|
||||
|
||||
/// Pointer to the transpose of the underlying matrix. If the matrix is
|
||||
/// symmetric, this will be the same as @a oper. If the matrix is not
|
||||
/// symmetric, the transpose will be formed and stored in @a At. The
|
||||
/// transpose will only be formed if MultTranspose() is called.
|
||||
mutable const SparseMatrix *oper_T = nullptr;
|
||||
|
||||
mutable std::unique_ptr<SparseMatrix> At; ///< Transpose of A, if needed.
|
||||
|
||||
void EnsureTranspose() const; ///< Ensure that the transpose is set.
|
||||
|
||||
public:
|
||||
SparseSmoother() { oper = NULL; }
|
||||
SparseSmoother() = default;
|
||||
|
||||
SparseSmoother(const SparseMatrix &a)
|
||||
: MatrixInverse(a) { oper = &a; }
|
||||
SparseSmoother(const SparseMatrix &a) { SetOperator(a); }
|
||||
|
||||
/// Sets the underlying matrix. @a a must be a SparseMatrix.
|
||||
void SetOperator(const Operator &a) override;
|
||||
};
|
||||
|
||||
/// Data type for Gauss-Seidel smoother of sparse matrix
|
||||
/// Gauss-Seidel smoother of a sparse matrix.
|
||||
class GSSmoother : public SparseSmoother
|
||||
{
|
||||
public:
|
||||
enum GSType
|
||||
{
|
||||
SYMMETRIC, ///< Forward Gauss-Seidel, then backward.
|
||||
FORWARD, ///< Forward Gauss-Seidel ($L^{-1}$).
|
||||
BACKWARD ///< Backward Gauss-Seidel ($U^{-1}$).
|
||||
};
|
||||
protected:
|
||||
int type; // 0, 1, 2 - symmetric, forward, backward
|
||||
int iterations;
|
||||
GSType type; ///< Type of Gauss-Seidel, see GSSmoother::GSType.
|
||||
int iterations; ///< Number of stationary iterations.
|
||||
|
||||
public:
|
||||
/// Create GSSmoother.
|
||||
GSSmoother(int t = 0, int it = 1) { type = t; iterations = it; }
|
||||
/// @brief Create a Gauss-Seidel smoother. SetOperator() will need to be
|
||||
/// called with a SparseMatrix before first use.
|
||||
///
|
||||
/// @param[in] t Type of GS smoother (see GSSmoother::GSType)
|
||||
/// @param[in] it Number of stationary iterations to perform
|
||||
GSSmoother(GSType t = SYMMETRIC, int it = 1) { type = t; iterations = it; }
|
||||
|
||||
/// Create GSSmoother.
|
||||
GSSmoother(const SparseMatrix &a, int t = 0, int it = 1)
|
||||
: SparseSmoother(a) { type = t; iterations = it; }
|
||||
/// @brief Create a Jacobi smoother using the SparseMatrix @a a.
|
||||
///
|
||||
/// @param[in] a The underlying SparseMatrix
|
||||
/// @param[in] t Type of GS smoother (see GSSmoother::GSType)
|
||||
/// @param[in] it Number of stationary iterations to perform
|
||||
GSSmoother(const SparseMatrix &a, GSType t = SYMMETRIC, int it = 1)
|
||||
: GSSmoother(t, it) { SetOperator(a); }
|
||||
|
||||
/// Matrix vector multiplication with GS Smoother.
|
||||
/// Same as GSSmoother(GSType,int), for backwards compatibility.
|
||||
GSSmoother(int t, int it = 1) : GSSmoother(GSType(t), it) { }
|
||||
|
||||
/// @brief Same as GSSmoother(const SparseMatrix&,GSType,int), for
|
||||
/// backwards compatibility.
|
||||
GSSmoother(const SparseMatrix &a, int t, int it = 1)
|
||||
: GSSmoother(a, GSType(t), it) { }
|
||||
|
||||
/// @brief Application of the Gauss-Seidel smoother.
|
||||
///
|
||||
/// Applies a stationary Gauss-Seidel iteration. If Solver::iterative_mode is
|
||||
/// true, then @a y is used as the initial guess, and Gauss-Seidel is applied
|
||||
/// to the residual $x - Ay$.
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
/// Application of the transpose of the Gauss-Seidel smoother.
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
};
|
||||
|
||||
/// Data type for scaled Jacobi-type smoother of sparse matrix
|
||||
/// Jacobi-type diagonal smoother of a sparse matrix.
|
||||
class DSmoother : public SparseSmoother
|
||||
{
|
||||
public:
|
||||
enum JacobiType
|
||||
{
|
||||
JACOBI, ///< Scale by the diagonal of the matrix.
|
||||
L1_JACOBI, ///< Scale by the l1-norm of the rows.
|
||||
LUMPED_JACOBI ///< Scale by the sum of the rows.
|
||||
};
|
||||
protected:
|
||||
int type; // 0, 1, 2 - scaled Jacobi, scaled l1-Jacobi, scaled lumped-Jacobi
|
||||
real_t scale;
|
||||
int iterations;
|
||||
/// Uses abs values of the diagonal entries. Relevant only when type = 0.
|
||||
JacobiType type; ///< Type of diagonal scaling, see DSmoother::JacobiType.
|
||||
real_t scale; ///< Scaling (damping) factor.
|
||||
int iterations; ///< Number of stationary iterations to perform.
|
||||
|
||||
/// @brief Uses abs values of the diagonal entries. Relevant only with type
|
||||
/// JacobiType::JACOBI.
|
||||
bool use_abs_diag = false;
|
||||
|
||||
mutable Vector z;
|
||||
mutable Vector z; ///< Temporary work vector.
|
||||
|
||||
/// Apply the Jacobi smoother (used internally by Mult() and MultTranspose())
|
||||
void Mult_(const SparseMatrix &A, const Vector &x, Vector &y) const;
|
||||
|
||||
public:
|
||||
/// Create Jacobi smoother.
|
||||
DSmoother(int t = 0, real_t s = 1., int it = 1)
|
||||
/// @brief Create a Jacobi smoother. SetOperator() will need to be called
|
||||
/// with a SparseMatrix before first use.
|
||||
///
|
||||
/// @param[in] t Type of Jacobi smoother (see DSmoother::JacobiType)
|
||||
/// @param[in] s Scaling factor
|
||||
/// @param[in] it Number of stationary iterations to perform
|
||||
DSmoother(JacobiType t = JACOBI, real_t s = 1., int it = 1)
|
||||
{ type = t; scale = s; iterations = it; }
|
||||
|
||||
/// Create Jacobi smoother.
|
||||
DSmoother(const SparseMatrix &a, int t = 0, real_t s = 1., int it = 1);
|
||||
/// @brief Create a Jacobi smoother using the SparseMatrix @a a.
|
||||
///
|
||||
/// @param[in] a The underlying SparseMatrix
|
||||
/// @param[in] t Type of Jacobi smoother (see DSmoother::JacobiType)
|
||||
/// @param[in] s Scaling factor
|
||||
/// @param[in] it Number of stationary iterations to perform
|
||||
DSmoother(const SparseMatrix &a, JacobiType t = JACOBI, real_t s = 1.,
|
||||
int it = 1) : DSmoother(t, s, it) { SetOperator(a); }
|
||||
|
||||
/// Replace diag entries with their abs values. Relevant only when type = 0.
|
||||
/// @brief Same as DSmoother(JacobiType,real_t,int), for backwards compatbility.
|
||||
DSmoother(int t, real_t s = 1., int it = 1)
|
||||
: DSmoother(JacobiType(t), s, it) { }
|
||||
|
||||
/// @brief Same as DSmoother(const SparseMatrix&,JacobiType,real_t,int), for
|
||||
/// backwards compatbility.
|
||||
DSmoother(const SparseMatrix &a, int t, real_t s = 1., int it = 1)
|
||||
: DSmoother(a, JacobiType(t), s, it) { }
|
||||
|
||||
/// @brief Replace diagonal entries with their absolute values. Relevant only
|
||||
/// with JacobiType::JACOBI.
|
||||
void SetPositiveDiagonal(bool pos_diag = true) { use_abs_diag = pos_diag; }
|
||||
|
||||
/// Matrix vector multiplication with Jacobi smoother.
|
||||
/// @brief Apply the Jacobi smoother.
|
||||
///
|
||||
/// Applies a stationary iteration with diagonal scaling. If
|
||||
/// Solver::iterative_mode is true, then @a y is used as the initial guess
|
||||
/// (and the diagonal scaling is applied to the residual $x - Ay$, giving
|
||||
/// $D^{-1}(x - Ay)$).
|
||||
///
|
||||
/// By default, Solver::iterative_mode is false and only one iteration is
|
||||
/// performed, corresponding to $y = D^{-1}x$.
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
/// @brief Apply the transpose of the Jacobi smoother.
|
||||
///
|
||||
/// If the underlying matrix is symmetric, or if only one iteration is
|
||||
/// performed with zero initial guess (Solver::iterative_mode is false), then
|
||||
/// this is the same as Mult(). For non-symmetric matrices with iteration
|
||||
/// count greater than one, only JacobiType::JACOBI is supported.
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -113,13 +113,13 @@ AttributeSets::GetAttributeSetMarker(const std::string & set_name) const
|
||||
|
||||
Array<int> AttributeSets::AttrToMarker(int max_attr, const Array<int> &attrs)
|
||||
{
|
||||
MFEM_ASSERT(attrs.Max() <= max_attr, "Invalid attribute number present.");
|
||||
MFEM_VERIFY(attrs.Min() >= 1, "Found attribute less than one")
|
||||
MFEM_ASSERT(attrs.Max() <= max_attr, "Found attribute greater than max_attr")
|
||||
|
||||
Array<int> marker(max_attr);
|
||||
marker = 0;
|
||||
for (auto const &attr : attrs)
|
||||
{
|
||||
MFEM_VERIFY(attr > 0, "Attribute number less than one!");
|
||||
marker[attr-1] = 1;
|
||||
}
|
||||
return marker;
|
||||
|
||||
+430
-14
@@ -36,6 +36,7 @@
|
||||
#include <numeric>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <list>
|
||||
|
||||
// Include the METIS header, if using version 5. If using METIS 4, the needed
|
||||
// declarations are inlined below, i.e. no header is needed.
|
||||
@@ -4772,12 +4773,12 @@ Mesh::Mesh(real_t *vertices_, int num_vertices,
|
||||
FinalizeTopology();
|
||||
}
|
||||
|
||||
Mesh::Mesh( const NURBSExtension& ext )
|
||||
Mesh::Mesh(const NURBSExtension& ext)
|
||||
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
|
||||
{
|
||||
SetEmpty();
|
||||
/// make an internal copy of the NURBSExtension
|
||||
NURBSext = new NURBSExtension( ext );
|
||||
NURBSext = new NURBSExtension(ext);
|
||||
|
||||
Dim = NURBSext->Dimension();
|
||||
NumOfVertices = NURBSext->GetNV();
|
||||
@@ -4791,11 +4792,12 @@ Mesh::Mesh( const NURBSExtension& ext )
|
||||
if (NURBSext->HavePatches())
|
||||
{
|
||||
NURBSFECollection *fec = new NURBSFECollection(NURBSext->GetOrder());
|
||||
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
|
||||
const int vdim = NURBSext->GetPatchSpaceDimension();
|
||||
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
|
||||
Ordering::byVDIM);
|
||||
Nodes = new GridFunction(fes);
|
||||
Nodes->MakeOwner(fec);
|
||||
NURBSext->SetCoordsFromPatches(*Nodes);
|
||||
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
|
||||
own_nodes = 1;
|
||||
spaceDim = Nodes->VectorDim();
|
||||
for (int i = 0; i < spaceDim; i++)
|
||||
@@ -6409,7 +6411,7 @@ void Mesh::UpdateNURBS()
|
||||
NURBSext->SetKnotsFromPatches();
|
||||
|
||||
Dim = NURBSext->Dimension();
|
||||
spaceDim = Dim;
|
||||
spaceDim = Nodes->FESpace()->GetVDim();
|
||||
|
||||
if (NumOfElements != NURBSext->GetNE())
|
||||
{
|
||||
@@ -6434,7 +6436,8 @@ void Mesh::UpdateNURBS()
|
||||
Nodes->FESpace()->Update();
|
||||
Nodes->Update();
|
||||
NodesUpdated();
|
||||
NURBSext->SetCoordsFromPatches(*Nodes);
|
||||
const int vdim = Nodes->FESpace()->GetVDim();
|
||||
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
|
||||
|
||||
if (NumOfVertices != NURBSext->GetNV())
|
||||
{
|
||||
@@ -6537,6 +6540,8 @@ void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_ukv)
|
||||
Array<int> ukv_to_rpkv;
|
||||
GetEdgeToUniqueKnotvector(edge_to_ukv, ukv_to_rpkv);
|
||||
}
|
||||
|
||||
CorrectPatchTopoOrientations(edge_to_ukv);
|
||||
}
|
||||
|
||||
void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
@@ -6547,9 +6552,9 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
const int NPKV = NP * dim; // number of patch knotvectors
|
||||
constexpr int notset = -9999999;
|
||||
// Sign convention
|
||||
auto sign = [](int i) { return -1 - i; };
|
||||
auto unsign = [](int i) { return (i < 0) ? -1 - i : i; };
|
||||
// Edge index -> dimension convention
|
||||
auto flipSign = [](int i) { return -1 - i; };
|
||||
auto unSign = [](int i) { return (i < 0) ? -1 - i : i; };
|
||||
// Local edge index -> dimension convention
|
||||
auto edge_to_dim = [](int i) { return (i < 8) ? ((i & 1) ? 1 : 0) : 2; };
|
||||
|
||||
Array<int> v(2); // vertices of an edge
|
||||
@@ -6564,7 +6569,7 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
{
|
||||
GetElementVertices(i, v);
|
||||
// Sign is based on the edge's vertex indices
|
||||
edge_to_ukv[i] = (v[1] > v[0]) ? i : sign(i);
|
||||
edge_to_ukv[i] = (v[1] > v[0]) ? i : flipSign(i);
|
||||
ukv_to_rpkv[i] = i;
|
||||
}
|
||||
return;
|
||||
@@ -6614,14 +6619,14 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
// We've set this edge already - link this index to it
|
||||
if (edge_to_pkv[edge] != notset)
|
||||
{
|
||||
const int pkv_other = unsign(edge_to_pkv[edge]);
|
||||
const int pkv_other = unSign(edge_to_pkv[edge]);
|
||||
unite(pkv, pkv_other);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetEdgeVertices(edge, v);
|
||||
// Sign is based on the edge's vertex indices
|
||||
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : sign(pkv);
|
||||
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : flipSign(pkv);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6648,11 +6653,255 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
|
||||
edge_to_ukv.SetSize(NumOfEdges);
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
const int pkv = unsign(edge_to_pkv[i]);
|
||||
const int pkv = unSign(edge_to_pkv[i]);
|
||||
const int rpkv = pkv_to_rpkv[pkv];
|
||||
const int ukv = rpkv_to_ukv[rpkv];
|
||||
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? sign(ukv) : ukv;
|
||||
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? flipSign(ukv) : ukv;
|
||||
}
|
||||
|
||||
CorrectPatchTopoOrientations(edge_to_ukv);
|
||||
}
|
||||
|
||||
void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
|
||||
{
|
||||
const int dim = Dimension(); // Topological (not physical) dimension
|
||||
if (dim == 1) { return; }
|
||||
|
||||
// Sign convention
|
||||
auto flipSign = [](int i) { return -1 - i; };
|
||||
|
||||
const Table *face2elem = GetFaceToElementTable();
|
||||
Array<int> pfaces, orient;
|
||||
Array<int> fe, feo;
|
||||
|
||||
// Finds elements sharing a face containing knotvector kv.
|
||||
auto faceNeighbors = [&](int p, int kv, std::unordered_set<int> &nghb)
|
||||
{
|
||||
if (dim == 2) { GetElementEdges(p, pfaces, orient); }
|
||||
else { GetElementFaces(p, pfaces, orient); }
|
||||
|
||||
for (auto face : pfaces)
|
||||
{
|
||||
// Check whether this face contains kv.
|
||||
GetFaceEdges(face, fe, feo);
|
||||
bool hasKV = false;
|
||||
for (auto e : fe)
|
||||
{
|
||||
const int skv = edge_to_ukv[e];
|
||||
if (skv == kv || flipSign(skv) == kv) { hasKV = true; }
|
||||
}
|
||||
if (hasKV)
|
||||
{
|
||||
Array<int> row;
|
||||
face2elem->GetRow(face, row);
|
||||
for (auto elem : row) { nghb.insert(elem); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::vector<int>> dir_edges;
|
||||
if (dim == 2)
|
||||
{
|
||||
dir_edges =
|
||||
{
|
||||
{0,2},
|
||||
{1,3}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
dir_edges =
|
||||
{
|
||||
{0,2,4,6},
|
||||
{1,3,5,7},
|
||||
{8,9,10,11}
|
||||
};
|
||||
}
|
||||
|
||||
Array<int> ukvs((dim==2) ? 4 : 12);
|
||||
Array<int> pe, oe;
|
||||
bool initKV = false;
|
||||
|
||||
auto setPatchDirections = [&](int p, int kv, Array<bool> &edgeSet,
|
||||
std::unordered_set<int> &visited)
|
||||
{
|
||||
// Edges and orientations for this patch
|
||||
GetElementEdges(p, pe, oe);
|
||||
|
||||
// Get the signed unique knot vector indices
|
||||
for (int i = 0; i < pe.Size(); i++)
|
||||
{
|
||||
ukvs[i] = edge_to_ukv[pe[i]];
|
||||
ukvs[i] = (oe[i] < 0) ? flipSign(ukvs[i]) : ukvs[i];
|
||||
}
|
||||
|
||||
// Find the direction with this kv.
|
||||
int thisDir = -1;
|
||||
for (int d=0; d<dim; ++d) // Loop over directions.
|
||||
{
|
||||
const int skv = edge_to_ukv[pe[dir_edges[d][0]]];
|
||||
if (skv == kv || flipSign(skv) == kv)
|
||||
{
|
||||
thisDir = d;
|
||||
}
|
||||
}
|
||||
MFEM_VERIFY(thisDir >= 0, "");
|
||||
|
||||
// For this direction, find any edge already set. If no edge is set, we
|
||||
// arbitrarily take the first.
|
||||
int ref_edge0 = dir_edges[thisDir][0];
|
||||
for (auto ref_edge : dir_edges[thisDir])
|
||||
{
|
||||
const int edge = pe[ref_edge];
|
||||
if (edgeSet[edge])
|
||||
{
|
||||
ref_edge0 = ref_edge;
|
||||
}
|
||||
}
|
||||
|
||||
if (initKV && !edgeSet[pe[ref_edge0]])
|
||||
{
|
||||
visited.erase(p);
|
||||
return false; // There is no set edge in this direction on this patch.
|
||||
}
|
||||
|
||||
initKV = true;
|
||||
|
||||
// Use ref_edge0 to set other edges in this direction.
|
||||
edgeSet[pe[ref_edge0]] = true;
|
||||
for (auto i : dir_edges[thisDir])
|
||||
{
|
||||
if (i == ref_edge0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const int edge = pe[i];
|
||||
if ((dim == 2 && ukvs[i] != flipSign(ukvs[ref_edge0])) ||
|
||||
(dim == 3 && ukvs[i] == flipSign(ukvs[ref_edge0])))
|
||||
{
|
||||
// Flip the sign of this edge
|
||||
MFEM_VERIFY(!edgeSet[edge], "");
|
||||
edge_to_ukv[edge] = flipSign(edge_to_ukv[edge]);
|
||||
}
|
||||
|
||||
edgeSet[edge] = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Array<bool> edgeSet(NumOfEdges); // Whether edge has orientation set
|
||||
edgeSet = false;
|
||||
|
||||
std::unordered_set<int> unset; // Patches with an unset edge
|
||||
for (int i=0; i<NumOfElements; ++i) { unset.insert(i); }
|
||||
|
||||
const int max_iter = 3 * NumOfElements;
|
||||
for (int iter=0; iter<max_iter; ++iter)
|
||||
{
|
||||
// Iteratively choose an unset patch (meaning not all edges have
|
||||
// orientation set), choose a knotvector index for which the corresponding
|
||||
// edges on this patch are not set, and sweep over all patches containing
|
||||
// this knotvector. The patch sweep is ordered, by maintaining an ordered
|
||||
// list `nextPatches` set by finding face-neighbor patches of visited
|
||||
// patches, where the common face contains the knotvector. When each patch
|
||||
// is visited, the edge orientations are set consistently. This iteration
|
||||
// terminates when all edges have been set on all patches.
|
||||
|
||||
std::list<int> nextPatches; // Next patches to visit, ordered
|
||||
std::unordered_set<int> nextSet; // nextPatches as a set
|
||||
std::unordered_set<int> visited; // Visit each patch only once
|
||||
|
||||
if (unset.size() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
const int p0 = *unset.begin();
|
||||
nextPatches.push_back(p0); // Start from arbitrary unset patch
|
||||
nextSet.insert(p0);
|
||||
|
||||
// Choose an arbitrary unset direction for the first patch.
|
||||
GetElementEdges(p0, pe, oe);
|
||||
int unsetDim = -1;
|
||||
for (int d=0; d<dim; ++d) // Loop over dimensions.
|
||||
{
|
||||
if (!edgeSet[pe[dir_edges[d][0]]])
|
||||
{
|
||||
unsetDim = d;
|
||||
}
|
||||
}
|
||||
|
||||
if (unsetDim == -1)
|
||||
{
|
||||
unset.erase(p0);
|
||||
continue;
|
||||
}
|
||||
|
||||
const int kv_signed = edge_to_ukv[pe[dir_edges[unsetDim][0]]];
|
||||
const int kv = kv_signed < 0 ? flipSign(kv_signed) : kv_signed;
|
||||
MFEM_VERIFY(!edgeSet[pe[dir_edges[unsetDim][0]]], "");
|
||||
|
||||
initKV = false;
|
||||
|
||||
while (nextPatches.size() > 0)
|
||||
{
|
||||
const int p = nextPatches.front();
|
||||
nextPatches.pop_front();
|
||||
nextSet.erase(p);
|
||||
visited.insert(p);
|
||||
|
||||
const bool somethingSet = setPatchDirections(p, kv, edgeSet, visited);
|
||||
if (!somethingSet)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find neighbors of patch p sharing a conforming face, via face2elem.
|
||||
std::unordered_set<int> neighbors;
|
||||
faceNeighbors(p, kv, neighbors);
|
||||
|
||||
bool allSet = true;
|
||||
GetElementEdges(p, pe, oe);
|
||||
for (auto edge : pe)
|
||||
{
|
||||
if (!edgeSet[edge])
|
||||
{
|
||||
allSet = false;
|
||||
}
|
||||
}
|
||||
if (allSet)
|
||||
{
|
||||
unset.erase(p);
|
||||
}
|
||||
|
||||
// Add neighbors not done to nextPatches.
|
||||
for (auto n : neighbors)
|
||||
{
|
||||
if (n != p && visited.count(n) == 0 && unset.count(n) > 0)
|
||||
{
|
||||
if (nextSet.count(n) == 0)
|
||||
{
|
||||
nextPatches.push_back(n);
|
||||
nextSet.insert(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool allSet = true;
|
||||
for (auto eset : edgeSet)
|
||||
{
|
||||
if (!eset)
|
||||
{
|
||||
allSet = false;
|
||||
}
|
||||
}
|
||||
MFEM_VERIFY(allSet && unset.size() == 0, "Some edge is not set");
|
||||
|
||||
delete face2elem;
|
||||
}
|
||||
|
||||
void Mesh::LoadNonconformingPatchTopo(std::istream &input,
|
||||
@@ -7587,6 +7836,17 @@ bool Mesh::IsMixedMesh() const
|
||||
|
||||
void Mesh::GetElementEdges(int i, Array<int> &edges, Array<int> &cor) const
|
||||
{
|
||||
if (Dim == 1)
|
||||
{
|
||||
// In 1D, elements are segments and can be treated as edges.
|
||||
edges.SetSize(1);
|
||||
cor.SetSize(1);
|
||||
edges[0] = i;
|
||||
const int *v = elements[i]->GetVertices();
|
||||
cor[0] = (v[0] < v[1]) ? (1) : (-1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (el_to_edge)
|
||||
{
|
||||
el_to_edge->GetRow(i, edges);
|
||||
@@ -9563,6 +9823,8 @@ void Mesh::GetVertices(Vector &vert_coord) const
|
||||
|
||||
void Mesh::SetVertices(const Vector &vert_coord)
|
||||
{
|
||||
MFEM_VERIFY(vert_coord.Size() == spaceDim * NumOfVertices, "");
|
||||
vertices.SetSize(NumOfVertices);
|
||||
for (int i = 0, nv = vertices.Size(); i < nv; i++)
|
||||
for (int j = 0; j < spaceDim; j++)
|
||||
{
|
||||
@@ -12140,6 +12402,38 @@ void Mesh::PrintTopoEdges(std::ostream &os, const Array<int> &e_to_k,
|
||||
{
|
||||
Array<int> vert;
|
||||
|
||||
// In 1D patch-topology NURBS meshes, knotvector orientation is stored in the
|
||||
// file's `edges` section, but the topological 1D mesh has NumOfEdges == 0
|
||||
// (its "faces" are vertices). When a valid edge->knotvector map is provided,
|
||||
// print a pseudo-edge list derived from the 1D elements so external tools
|
||||
// (e.g. VisIt) can consume the mapping.
|
||||
if (Dim == 1 && NumOfEdges == 0 && e_to_k.Size() == NumOfElements)
|
||||
{
|
||||
const int ne = NumOfElements;
|
||||
os << "\nedges\n" << ne << '\n';
|
||||
for (int i = 0; i < ne; i++)
|
||||
{
|
||||
const int *v = elements[i]->GetVertices();
|
||||
int v0 = v[0], v1 = v[1];
|
||||
|
||||
int ki = e_to_k[i];
|
||||
const bool flip = (ki < 0); // desired output vertex order: descending
|
||||
if (flip) { ki = -1 - ki; } // print the unsigned knotvector index
|
||||
|
||||
// Encode the sign of e_to_k in the vertex ordering, consistent with
|
||||
// Mesh::LoadPatchTopo(): v0 > v1 => negative sign.
|
||||
if ((v0 > v1) != flip) { std::swap(v0, v1); }
|
||||
|
||||
os << ki << ' ' << v0 << ' ' << v1 << '\n';
|
||||
}
|
||||
|
||||
if (!vmap)
|
||||
{
|
||||
os << "\nvertices\n" << NumOfVertices << '\n';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
os << "\nedges\n" << NumOfEdges << '\n';
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
@@ -15452,6 +15746,128 @@ Mesh *Extrude2D(Mesh *mesh, const int nz, const real_t sz)
|
||||
return mesh3d;
|
||||
}
|
||||
|
||||
Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
|
||||
int &par_ref, Array<int> &partitioning)
|
||||
{
|
||||
MFEM_VERIFY(dim > 1, "Not implemented for 1D meshes.");
|
||||
|
||||
auto factor = [&](int N)
|
||||
{
|
||||
for (int i = static_cast<int>(sqrt(N)); i > 0; i--)
|
||||
{ if (N % i == 0) { return i; } }
|
||||
return 1;
|
||||
};
|
||||
|
||||
par_ref = 0;
|
||||
const int ref_factor = (dim == 2) ? 4 : 8;
|
||||
|
||||
// Elements per task before performing parallel refinements.
|
||||
// This will be used to form the serial mesh.
|
||||
int el0 = elem_per_mpi;
|
||||
while (el0 % ref_factor == 0)
|
||||
{
|
||||
el0 /= ref_factor;
|
||||
par_ref++;
|
||||
}
|
||||
|
||||
// In the serial mesh we have:
|
||||
// The number of MPI blocks is mpi_cnt = mp_x.mpy_y.mpy_z.
|
||||
// The size of each MPI block is el0 = el0_x.el0_y.el0_z.
|
||||
int mpi_x, mpi_y, mpi_z;
|
||||
int el0_x, el0_y, el0_z;
|
||||
if (dim == 2)
|
||||
{
|
||||
mpi_x = factor(mpi_cnt);
|
||||
mpi_y = mpi_cnt / mpi_x;
|
||||
|
||||
// Switch order for better balance.
|
||||
el0_y = factor(el0);
|
||||
el0_x = el0 / el0_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
mpi_x = factor(mpi_cnt);
|
||||
mpi_y = factor(mpi_cnt / mpi_x);
|
||||
mpi_z = mpi_cnt / mpi_x / mpi_y;
|
||||
|
||||
// Switch order for better balance.
|
||||
el0_z = factor(el0);
|
||||
el0_y = factor(el0 / el0_z);
|
||||
el0_x = el0 / el0_y / el0_z;
|
||||
}
|
||||
|
||||
if (print && dim == 2)
|
||||
{
|
||||
int elem_par_x = mpi_x * el0_x * pow(2, par_ref),
|
||||
elem_par_y = mpi_y * el0_y * pow(2, par_ref);
|
||||
|
||||
mfem::out << "--- Mesh generation: \n";
|
||||
mfem::out << "Par mesh: " << elem_par_x << " x " << elem_par_y
|
||||
<< " (" << elem_par_x * elem_par_y << " elements)\n"
|
||||
<< "Elem / task: "
|
||||
<< el0_x * pow(2, par_ref) << " x "
|
||||
<< el0_y * pow(2, par_ref)
|
||||
<< " (" << el0_x * pow(2, 2*par_ref) * el0_y << " elements)\n"
|
||||
<< "MPI blocks: " << mpi_x << " x " << mpi_y
|
||||
<< " (" << mpi_x * mpi_y << " mpi tasks)\n" << "-\n"
|
||||
<< "Serial mesh: "
|
||||
<< mpi_x * el0_x << " x " << mpi_y * el0_y
|
||||
<< " (" << mpi_x * el0_x * mpi_y * el0_y << " elements)\n"
|
||||
<< "Elem / task: " << el0_x << " x " << el0_y << std::endl
|
||||
<< "Par refine: " << par_ref << std::endl;
|
||||
mfem::out << "--- \n";
|
||||
}
|
||||
|
||||
if (print && dim == 3)
|
||||
{
|
||||
int elem_par_x = mpi_x * el0_x * pow(2, par_ref),
|
||||
elem_par_y = mpi_y * el0_y * pow(2, par_ref),
|
||||
elem_par_z = mpi_z * el0_z * pow(2, par_ref);
|
||||
|
||||
mfem::out << "--- Mesh generation: \n";
|
||||
mfem::out << "Par mesh: "
|
||||
<< elem_par_x << " x " << elem_par_y << " x " << elem_par_z
|
||||
<< " (" << elem_par_x*elem_par_y*elem_par_z << " elements)\n"
|
||||
<< "Elem / task: "
|
||||
<< el0_x * pow(2, par_ref) << " x "
|
||||
<< el0_y * pow(2, par_ref) << " x "
|
||||
<< el0_z * pow(2, par_ref)
|
||||
<< " (" << el0_x*pow(2, 3*par_ref)*el0_y*el0_z << " elements)\n"
|
||||
<< "MPI blocks: " << mpi_x << " x " << mpi_y << " x " << mpi_z
|
||||
<< " (" << mpi_x * mpi_y * mpi_z << " mpi tasks)\n" << "-\n"
|
||||
<< "Serial mesh: "
|
||||
<< mpi_x*el0_x << " x " << mpi_y*el0_y << " x " << mpi_z*el0_z
|
||||
<< " (" << mpi_x*el0_x*mpi_y*el0_y*mpi_z*el0_z << " elements)\n"
|
||||
<< "Elem / task: "
|
||||
<< el0_x << " x " << el0_y << " x " << el0_z << std::endl
|
||||
<< "Par refine: " << par_ref << std::endl;
|
||||
mfem::out << "--- \n";
|
||||
}
|
||||
|
||||
Mesh mesh;
|
||||
int nxyz[3];
|
||||
if (dim == 2)
|
||||
{
|
||||
mesh = Mesh::MakeCartesian2D(mpi_x * el0_x,
|
||||
mpi_y * el0_y, Element::QUADRILATERAL, true);
|
||||
nxyz[0] = mpi_x; nxyz[1] = mpi_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
mesh = Mesh::MakeCartesian3D(mpi_x * el0_x,
|
||||
mpi_y * el0_y,
|
||||
mpi_z * el0_z, Element::HEXAHEDRON, true);
|
||||
nxyz[0] = mpi_x; nxyz[1] = mpi_y; nxyz[2] = mpi_z;
|
||||
}
|
||||
|
||||
const int NE = mesh.GetNE();
|
||||
partitioning.SetSize(NE);
|
||||
std::unique_ptr<int[]> p_raw(mesh.CartesianPartitioning(nxyz));
|
||||
std::copy(p_raw.get(), p_raw.get() + NE, partitioning.GetData());
|
||||
|
||||
return mesh;
|
||||
}
|
||||
|
||||
bool Mesh::Conforming() const
|
||||
{
|
||||
if (NURBSext)
|
||||
|
||||
+33
-5
@@ -527,6 +527,9 @@ protected:
|
||||
void PrintTopoEdges(std::ostream &out, const Array<int> &e_to_k,
|
||||
bool vmap = false) const;
|
||||
|
||||
/// Set signs to ensure knotvectors are pointed in the same direction.
|
||||
void CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const;
|
||||
|
||||
/// Used in GetFaceElementTransformations (...)
|
||||
void GetLocalPtToSegTransformation(IsoparametricTransformation &,
|
||||
int i) const;
|
||||
@@ -984,8 +987,8 @@ public:
|
||||
|
||||
///@}
|
||||
|
||||
/// Construct a Mesh from a NURBSExtension
|
||||
explicit Mesh( const NURBSExtension& ext );
|
||||
/// Construct a Mesh from a NURBSExtension, which is deep-copied.
|
||||
explicit Mesh(const NURBSExtension& ext);
|
||||
|
||||
/** @anchor mfem_Mesh_construction
|
||||
@name Methods for piecewise Mesh construction.
|
||||
@@ -2538,13 +2541,16 @@ public:
|
||||
changing the mesh file itself. Examples in miniapps/nurbs/meshes. */
|
||||
void RefineNURBSFromFile(std::string ref_file);
|
||||
|
||||
/// For NURBS meshes, insert the new knots in @a kv, for each direction.
|
||||
/// For NURBS meshes, insert the new knots in @a kv, for each KnotVector.
|
||||
/// The size of @a kv should be the number of KnotVectors in NURBSExtension.
|
||||
void KnotInsert(Array<KnotVector*> &kv);
|
||||
|
||||
/// For NURBS meshes, insert the knots in @a kv, for each direction.
|
||||
/// For NURBS meshes, insert the knots in @a kv, for each KnotVector.
|
||||
/// The size of @a kv should be the number of KnotVectors in NURBSExtension.
|
||||
void KnotInsert(Array<Vector*> &kv);
|
||||
|
||||
/// For NURBS meshes, remove the knots in @a kv, for each direction.
|
||||
/// For NURBS meshes, remove the knots in @a kv, for each KnotVector.
|
||||
/// The size of @a kv should be the number of KnotVectors in NURBSExtension.
|
||||
void KnotRemove(Array<Vector*> &kv);
|
||||
|
||||
/* For each knot vector:
|
||||
@@ -3201,6 +3207,28 @@ Mesh *Extrude1D(Mesh *mesh, const int ny, const real_t sy,
|
||||
/// Extrude a 2D mesh
|
||||
Mesh *Extrude2D(Mesh *mesh, const int nz, const real_t sz);
|
||||
|
||||
/** @brief Constructs the smallest possible [0,1]^dim serial mesh that can be
|
||||
used later to obtain a ParMesh with @a elem_per_mpi elements, with the same
|
||||
topology, for each of the @a mpi_cnt MPI tasks. For quads and hexes.
|
||||
|
||||
The serial mesh has the smallest possible number of elements. The parallel
|
||||
mesh will be obtained by parallel refinements. Each MPI task will have
|
||||
elements with the same topology (same number, same connectivity).
|
||||
|
||||
@param[in] dim dimension (2 or 3).
|
||||
@param[in] mpi_cnt number of MPI tasks.
|
||||
@param[in] elem_per_mpi number of elements per MPI task.
|
||||
@param[in] print shows meshing info in the terminal.
|
||||
@param[out] par_ref number of parallel refinement needed afterwards.
|
||||
@param[out] partitioning partitioning to create the desired ParMesh.
|
||||
|
||||
Usual use case:
|
||||
Mesh mesh = PartitionMPI(dim, mpi_cnt, elem_per_mpi, print, par_ref, par);
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh, par.GetData());
|
||||
for (int lev = 0; lev < par_ref; lev++) { pmesh.UniformRefinement(); } */
|
||||
Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
|
||||
int &par_ref, Array<int> &partitioning);
|
||||
|
||||
// shift cyclically 3 integers left-to-right
|
||||
inline void ShiftRight(int &a, int &b, int &c)
|
||||
{
|
||||
|
||||
@@ -1328,11 +1328,12 @@ void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf,
|
||||
if (NURBSext->HavePatches())
|
||||
{
|
||||
NURBSFECollection *fec = new NURBSFECollection(NURBSext->GetOrder());
|
||||
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
|
||||
const int vdim = NURBSext->GetPatchSpaceDimension();
|
||||
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
|
||||
Ordering::byVDIM);
|
||||
Nodes = new GridFunction(fes);
|
||||
Nodes->MakeOwner(fec);
|
||||
NURBSext->SetCoordsFromPatches(*Nodes);
|
||||
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
|
||||
own_nodes = 1;
|
||||
read_gf = 0;
|
||||
spaceDim = Nodes->VectorDim();
|
||||
|
||||
@@ -2827,6 +2827,8 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
|
||||
}
|
||||
}
|
||||
|
||||
delete face2elem;
|
||||
|
||||
// For any unset entries of kvf, set to default refinement factor rf_default.
|
||||
for (size_t i=0; i<kvf.size(); ++i)
|
||||
{
|
||||
|
||||
+634
-241
File diff suppressed because it is too large
Load Diff
+139
-32
@@ -51,6 +51,21 @@ protected:
|
||||
/// Number of elements, defined by distinct knots.
|
||||
int NumOfElements;
|
||||
|
||||
// Stores the demko points
|
||||
mutable Vector demko;
|
||||
|
||||
/// Compute all the Demko points
|
||||
void ComputeDemko() const;
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
// Data for reusing banded matrix factorization in FindInterpolant().
|
||||
mutable DenseMatrix fact_AB; /// Banded matrix factorization
|
||||
mutable Array<int> fact_ipiv; /// Row pivot indices
|
||||
#else
|
||||
mutable DenseMatrix A_coll_inv; /// Collocation matrix inverse
|
||||
#endif
|
||||
|
||||
|
||||
public:
|
||||
/// Create an empty KnotVector.
|
||||
KnotVector() = default;
|
||||
@@ -63,6 +78,14 @@ public:
|
||||
order @a order and number of control points @a NCP. */
|
||||
KnotVector(int order, int NCP);
|
||||
|
||||
/** @brief Create a KnotVector with order @a order and knots @a knot.
|
||||
If @a k has the correct number of repeated knots at the begin and end,
|
||||
then this constructor will copy the knots as provided.
|
||||
Otherwise, the knot vector will be extended by repeating the end knots
|
||||
(order + 1) times. Internal knots will retain the multiplicity as given
|
||||
in the input. */
|
||||
KnotVector(int order, const Vector &k);
|
||||
|
||||
/** @brief Create a KnotVector by passing in a degree, a Vector of interval
|
||||
lengths of length n, and a list of continuity of length n + 1.
|
||||
|
||||
@@ -103,13 +126,69 @@ public:
|
||||
with @a isElement for non-empty knot spans (elements). */
|
||||
int GetNKS() const { return NumOfControlPoints - Order; }
|
||||
|
||||
/** @brief Return the parameter for element reference coordinate @a xi
|
||||
in [0,1], for the element beginning at knot @a ni. */
|
||||
real_t getKnotLocation(real_t xi, int ni) const
|
||||
{ return (xi*knot(ni+1) + (1. - xi)*knot(ni)); }
|
||||
/// Return whether knot location @a u is in a given span @a ni.
|
||||
bool inSpan(real_t u, int ni) const
|
||||
{
|
||||
if ((u < knot(ni)) || (u > knot(ni+1))) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Return the index of the knot span containing parameter @a u.
|
||||
int findKnotSpan(real_t u) const;
|
||||
int GetSpan(real_t u) const;
|
||||
|
||||
/** @brief Return the reference coordinate in [0,1] for parameter @a u
|
||||
in the element beginning at knot @a ni. */
|
||||
real_t GetRefPoint(real_t u, int ni) const
|
||||
{ return (u-knot(ni))/(knot(ni+1)-knot(ni)); };
|
||||
|
||||
/** @brief Return the knot location for element reference coordinate @a xi
|
||||
in [0,1], for the element beginning at knot @a ni. */
|
||||
real_t GetKnotLocation(real_t xi, int ni) const
|
||||
{ return (xi*knot(ni+1) + (1. - xi)*knot(ni)); }
|
||||
|
||||
/** @brief Return the parameter for element reference coordinate @a xi
|
||||
in [0,1], for the element beginning at knot @a ni. */
|
||||
MFEM_DEPRECATED real_t getKnotLocation(real_t xi, int ni) const
|
||||
{ return (xi*knot(ni+1) + (1. - xi)*knot(ni)); } // Use GetKnotLocation instead
|
||||
|
||||
/// Return the index of the knot span containing parameter @a u.
|
||||
MFEM_DEPRECATED int findKnotSpan(real_t u) const; // Use GetSpan instead
|
||||
|
||||
/** Gives the @a i average knot location. Average is taken over @a Order
|
||||
number of knots.*/
|
||||
real_t GetGreville(int i) const;
|
||||
|
||||
void GetGreville(Vector &xi) const;
|
||||
|
||||
/** Gives the knot location where the @a i shape function is maximum.
|
||||
Reverts to the Greville point if knot is repeated @a Order +1 times.
|
||||
For background see:
|
||||
|
||||
Olivier Botella and Karim Shariff.
|
||||
"B-spline methods in fluid dynamics."
|
||||
International Journal of Computational Fluid Dynamics 17.2 (2003): 133-149.
|
||||
|
||||
Points are found using Newton iteration, with the Greville point as the
|
||||
starting value. */
|
||||
real_t GetBotella(int i) const;
|
||||
|
||||
void GetBotella(Vector &xi) const;
|
||||
|
||||
/** Gives the knot location of the @a i extremum of the Chebyshev spline.
|
||||
For background see:
|
||||
|
||||
Stephen Demko
|
||||
"On the existence of interpolating projections onto spline spaces."
|
||||
Journal of approximation theory 43.2 (1985): 151-156.
|
||||
|
||||
Points are found using Remez iteration:
|
||||
- Find interpolant, given by a, through given points, given by Demko
|
||||
- Find extrema of this polynomial and update Demko points
|
||||
- Repeat until converged
|
||||
- Use the Greville point as starting point */
|
||||
real_t GetDemko(int i) const;
|
||||
|
||||
void GetDemko(Vector &xi) const;
|
||||
|
||||
// The following functions evaluate shape functions, which are B-spline basis
|
||||
// functions.
|
||||
@@ -136,19 +215,32 @@ public:
|
||||
/** @brief Gives the locations of the maxima of the KnotVector in reference
|
||||
space. The function gives the knot span @a ks, the coordinate in the
|
||||
knot span @a xi, and the coordinate of the maximum in parameter space
|
||||
@a u. */
|
||||
void FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const;
|
||||
@a u.
|
||||
The main purpose of this function is its use in FindInterpolant.
|
||||
Use GetBotella instead for each shape function separately, perhaps in
|
||||
conjuction with GetSpan and GetRefPoint.*/
|
||||
MFEM_DEPRECATED void FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const;
|
||||
|
||||
/** @brief Global curve interpolation through the points @a x (overwritten).
|
||||
@a x is an array with the length of the spatial dimension containing
|
||||
vectors with spatial coordinates. The control points of the interpolated
|
||||
curve are returned in @a x in the same form.
|
||||
Use GetInterpolant instead. For the knot location one can use either
|
||||
GetBotella, GetDemko or GetGreville. FindInterpolant uses the Botella
|
||||
points, however, the Demko points might be more appropriate. */
|
||||
MFEM_DEPRECATED void FindInterpolant(Array<Vector*> &x, bool reuse_inverse);
|
||||
|
||||
The inverse of the collocation matrix, used in the interpolation, is
|
||||
stored for repeated calls and used if @a reuse_inverse is true. Reuse is
|
||||
valid only if this KnotVector has not changed since the initial call with
|
||||
@a reuse_inverse false. */
|
||||
void FindInterpolant(Array<Vector*> &x, bool reuse_inverse = false);
|
||||
/** @brief Global curve interpolation through the points @a x (overwritten)
|
||||
at the knot location @a u. The control points of the
|
||||
interpolated curve are returned in @a x in the same form.
|
||||
For the knot location one can use for instance GetBotella, GetDemko or
|
||||
GetGreville. The Demko points might be most appropriate.*/
|
||||
void GetInterpolant(Array<Vector*> &x, const Vector &u,
|
||||
bool reuse_inverse = false) const;
|
||||
|
||||
/// Different interface to same routine
|
||||
void GetInterpolant(const Vector &x, const Vector &u,
|
||||
Vector &a, bool reuse_inverse = false) const;
|
||||
|
||||
/** Set @a diff, comprised of knots in @a kv not contained in this KnotVector.
|
||||
@a kv must be of the same order as this KnotVector. The current
|
||||
@@ -191,6 +283,18 @@ public:
|
||||
number of samples of the shape functions per element.*/
|
||||
void PrintFunctions(std::ostream &os, int samples=11) const;
|
||||
|
||||
/** Prints the function with basis function coefficient @a a, and its 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 PrintFunction(std::ostream &os, const Vector &a, int samples=11) const;
|
||||
|
||||
/** Prints the @a i-th function and its 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 PrintFunction(std::ostream &os, int i, int samples=11) const;
|
||||
|
||||
/// Destroys KnotVector
|
||||
~KnotVector() { }
|
||||
|
||||
@@ -209,14 +313,6 @@ public:
|
||||
/** @brief Flag to indicate whether the KnotVector has been coarsened, which
|
||||
means it is ready for non-nested refinement. */
|
||||
bool coarse;
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
// Data for reusing banded matrix factorization in FindInterpolant().
|
||||
DenseMatrix fact_AB; /// Banded matrix factorization
|
||||
Array<int> fact_ipiv; /// Row pivot indices
|
||||
#else
|
||||
DenseMatrix A_coll_inv; /// Collocation matrix inverse
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -596,28 +692,29 @@ protected:
|
||||
if the KnotVector index associated with edge @a edge is negative. */
|
||||
inline const KnotVector *KnotVec(int edge, int oedge, int *okv) const;
|
||||
|
||||
/// Throw an error if any patch has an inconsistent edge_to_ukv mapping.
|
||||
void CheckPatches();
|
||||
|
||||
/// Throw an error if any boundary patch has invalid KnotVector orientation.
|
||||
void CheckBdrPatches();
|
||||
MFEM_DEPRECATED void CheckBdrPatches();
|
||||
|
||||
/// Return the patch-topology edge indices that define the KnotVectors for
|
||||
/// patch @a p in each parametric direction.
|
||||
void GetPatchDirectionEdges(int p, Array<int> &edges);
|
||||
|
||||
/** @brief Return the directions in @a kvdir of the KnotVectors in patch @a p
|
||||
based on the patch edge orientations. Each entry of @a kvdir is -1 if the
|
||||
KnotVector direction is flipped, +1 otherwise. */
|
||||
void CheckKVDirection(int p, Array <int> &kvdir);
|
||||
|
||||
/** @brief Create the comprehensive set of KnotVectors. In 1D, this set is
|
||||
identical to the unique set of KnotVectors. */
|
||||
/** @brief Create the comprehensive set of KnotVectors, one per patch and
|
||||
parametric direction, accounting for the edge orientations. */
|
||||
void CreateComprehensiveKV();
|
||||
|
||||
/** Update the unique set of KnotVectors. In 1D, this set is identical to
|
||||
the comprehensive set of KnotVectors. */
|
||||
/** @brief Update the unique set of KnotVectors from the comprehensive set
|
||||
of KnotVectors. */
|
||||
void UpdateUniqueKV();
|
||||
|
||||
/** @brief Check if the comprehensive array of KnotVectors agrees with the
|
||||
unique set of KnotVectors, on each patch. Return false if there is a
|
||||
difference, true otherwise. This function throws an error in 1D. */
|
||||
difference, true otherwise. */
|
||||
bool ConsistentKVSets();
|
||||
|
||||
/// Return KnotVectors in @a kv in each dimension for patch @a p.
|
||||
@@ -794,6 +891,9 @@ public:
|
||||
void MergeGridFunctions(GridFunction *gf_array[], int num_pieces,
|
||||
GridFunction &merged);
|
||||
|
||||
/// Returns false if any patch has an inconsistent edge_to_ukv mapping.
|
||||
bool CheckPatches();
|
||||
|
||||
/// Destroy a NURBSExtension.
|
||||
virtual ~NURBSExtension();
|
||||
|
||||
@@ -820,6 +920,13 @@ public:
|
||||
/// Return the dimension of the reference space (not physical space).
|
||||
int Dimension() const { return patchTopo->Dimension(); }
|
||||
|
||||
/** @brief Return the physical dimension of the NURBS geometry
|
||||
|
||||
The physical dimension is inferred from the first patch,
|
||||
i.e. number of coordinates per control point minus one (for the weight).
|
||||
This method requires patch data to be present, i.e. HavePatches() == true */
|
||||
int GetPatchSpaceDimension() const;
|
||||
|
||||
/// Return the number of patches.
|
||||
int GetNP() const { return patchTopo->GetNE(); }
|
||||
|
||||
@@ -933,9 +1040,9 @@ public:
|
||||
void ConvertToPatches(const Vector &Nodes);
|
||||
/// Set KnotVectors from @a patches and construct mesh and space data.
|
||||
void SetKnotsFromPatches();
|
||||
/** @brief Set FE coordinates in @a Nodes, using data from @a patches, and
|
||||
erase @a patches. */
|
||||
void SetCoordsFromPatches(Vector &Nodes);
|
||||
/** @brief Set FE coordinates in @a Nodes, using data from @a patches,
|
||||
with physical vector dimension @a vdim, and erase @a patches. */
|
||||
void SetCoordsFromPatches(Vector &Nodes, int vdim);
|
||||
|
||||
/** @brief Read a GridFunction @a sol from stream @a input, written
|
||||
patch-by-patch, e.g. with PrintSolution(). */
|
||||
|
||||
+13
-3
@@ -3041,7 +3041,7 @@ void ParMesh::GetSharedFaceTransformationsByLocalIndex(
|
||||
// for ghost faces we need a special version of GetFaceTransformation
|
||||
if (is_ghost)
|
||||
{
|
||||
GetGhostFaceTransformation(FElTr, face_type, face_geom);
|
||||
GetGhostFaceTransformation(FaceNo, FElTr);
|
||||
mask |= FaceElementTransformations::HAVE_FACE;
|
||||
}
|
||||
|
||||
@@ -3064,19 +3064,29 @@ void ParMesh::GetSharedFaceTransformationsByLocalIndex(
|
||||
}
|
||||
|
||||
void ParMesh::GetGhostFaceTransformation(
|
||||
FaceElementTransformations &FElTr, Element::Type face_type,
|
||||
Geometry::Type face_geom) const
|
||||
int FaceNo, FaceElementTransformations &FElTr) const
|
||||
{
|
||||
MFEM_ASSERT(FaceNo >= GetNumFaces(), "Not a ghost face.");
|
||||
|
||||
// use the local face data
|
||||
const int LocFaceNo = nc_faces_info[faces_info[FaceNo].NCFace].MasterFace;
|
||||
FElTr.Attribute = (Dim == 1) ? 1 : faces[LocFaceNo]->GetAttribute();
|
||||
FElTr.ElementNo = FaceNo;
|
||||
FElTr.ElementType = ElementTransformation::FACE;
|
||||
FElTr.mesh = this;
|
||||
|
||||
// calculate composition of FElTr.Loc1 and FElTr.Elem1
|
||||
DenseMatrix &face_pm = FElTr.GetPointMat();
|
||||
FElTr.Reset();
|
||||
if (Nodes == NULL)
|
||||
{
|
||||
const Element::Type face_type = GetFaceElementType(LocFaceNo);
|
||||
FElTr.Elem1->Transform(FElTr.Loc1.Transf.GetPointMat(), face_pm);
|
||||
FElTr.SetFE(GetTransformationFEforElementType(face_type));
|
||||
}
|
||||
else
|
||||
{
|
||||
const Geometry::Type face_geom = GetFaceGeometry(LocFaceNo);
|
||||
const FiniteElement* face_el =
|
||||
Nodes->FESpace()->GetTraceElement(FElTr.Elem1No, face_geom);
|
||||
MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
|
||||
|
||||
+1
-9
@@ -150,15 +150,7 @@ protected:
|
||||
int elem, int start, int end, const int fverts[][N]);
|
||||
|
||||
void GetGhostFaceTransformation(
|
||||
FaceElementTransformations &FElTr, Element::Type face_type,
|
||||
Geometry::Type face_geom) const;
|
||||
void GetGhostFaceTransformation(
|
||||
FaceElementTransformations *FElTr, Element::Type face_type,
|
||||
Geometry::Type face_geom) const
|
||||
{
|
||||
MFEM_ASSERT(FElTr, "Missing FaceElementTransformations object!");
|
||||
GetGhostFaceTransformation(*FElTr, face_type, face_geom);
|
||||
}
|
||||
int FaceNo, FaceElementTransformations &FElTr) const;
|
||||
|
||||
/// Update the groups after triangle refinement
|
||||
void RefineGroups(const DSTable &v_to_v, int *middle);
|
||||
|
||||
+10
-2
@@ -1195,8 +1195,17 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
}
|
||||
}
|
||||
|
||||
// If there are shared slaves, they will also need to be updated.
|
||||
// If there are shared slaves, they will also need to be updated. First,
|
||||
// check whether the update has already been done.
|
||||
bool sharedUpdated = false;
|
||||
if (shared.slaves.Size())
|
||||
{
|
||||
int nfaces = NFaces, nghosts = NGhostFaces;
|
||||
if (Dim <= 2) { nfaces = NEdges, nghosts = NGhostEdges; }
|
||||
sharedUpdated = (pmesh.faces_info.Size() == nfaces + nghosts);
|
||||
}
|
||||
|
||||
if (shared.slaves.Size() && !sharedUpdated)
|
||||
{
|
||||
int nfaces = NFaces, nghosts = NGhostFaces;
|
||||
if (Dim <= 2) { nfaces = NEdges, nghosts = NGhostEdges; }
|
||||
@@ -1310,7 +1319,6 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// In 3D some extra orientation data structures can be needed.
|
||||
if (Dim == 3)
|
||||
{
|
||||
|
||||
@@ -34,14 +34,16 @@ class ParNCSubMesh;
|
||||
* subset of the parent Mesh and reuses the parallel distribution.
|
||||
*
|
||||
* The attributes are taken from the parent. That means if a volume is extracted
|
||||
* from a volume, it has the same domain attribute as the parent. Its boundary
|
||||
* attributes are generated (there will be one boundary attribute 1 for all of
|
||||
* the boundaries).
|
||||
* from a volume, it has the same domain attribute as the parent. Its new
|
||||
* boundary attributes are, for any boundary common to the parent and the new
|
||||
* submesh, the boundary attribute of the parent; and, for all new boundaries,
|
||||
* a single, generated, common attribute equal to one plus the largest boundary
|
||||
* attribute of the parent.
|
||||
*
|
||||
* If a surface is extracted from a volume, the boundary attribute from the
|
||||
* parent is assigned to be the new domain attribute. Its boundary attributes
|
||||
* are generated (there will be one boundary attribute 1 for all of the
|
||||
* boundaries).
|
||||
* parent is assigned to be the new domain attribute. Its new boundary attribute
|
||||
* is a single, generated, common attribute equal to one plus the largest
|
||||
* boundary attribute of the parent.
|
||||
*
|
||||
* For more customized boundary attributes, the resulting ParSubMesh has to be
|
||||
* postprocessed.
|
||||
|
||||
@@ -28,14 +28,16 @@ class NCSubMesh;
|
||||
* subset of the parents Mesh and reuses the parallel distribution.
|
||||
*
|
||||
* The attributes are taken from the parent. That means if a volume is extracted
|
||||
* from a volume, it has the same domain attribute as the parent. Its boundary
|
||||
* attributes are generated (there will be one boundary attribute 1 for all of
|
||||
* the boundaries).
|
||||
* from a volume, it has the same domain attribute as the parent. Its new
|
||||
* boundary attributes are, for any boundary common to the parent and the new
|
||||
* submesh, the boundary attribute of the parent; and, for all new boundaries,
|
||||
* a single, generated, common attribute equal to one plus the largest boundary
|
||||
* attribute of the parent.
|
||||
*
|
||||
* If a surface is extracted from a volume, the boundary attribute from the
|
||||
* parent is assigned to be the new domain attribute. Its boundary attributes
|
||||
* are generated (there will be one boundary attribute 1 for all of the
|
||||
* boundaries).
|
||||
* parent is assigned to be the new domain attribute. Its new boundary attribute
|
||||
* is a single, generated, common attribute equal to one plus the largest
|
||||
* boundary attribute of the parent.
|
||||
*
|
||||
* For more customized boundary attributes, the resulting SubMesh has to be
|
||||
* postprocessed.
|
||||
|
||||
@@ -232,27 +232,6 @@ MergeMeshNodes(Mesh * mesh, int logging)
|
||||
}
|
||||
}
|
||||
|
||||
void AttrToMarker(int max_attr, const Array<int> &attrs, Array<int> &marker)
|
||||
{
|
||||
MFEM_ASSERT(attrs.Max() <= max_attr, "Invalid attribute number present.");
|
||||
|
||||
marker.SetSize(max_attr);
|
||||
if (attrs.Size() == 1 && attrs[0] == -1)
|
||||
{
|
||||
marker = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
marker = 0;
|
||||
for (int j=0; j<attrs.Size(); j++)
|
||||
{
|
||||
int attr = attrs[j];
|
||||
MFEM_VERIFY(attr > 0, "Attribute number less than one!");
|
||||
marker[attr-1] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AffineTransformation::Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
|
||||
@@ -33,9 +33,14 @@ void MergeMeshNodes(Mesh * mesh, int logging);
|
||||
/// Convert a set of attribute numbers to a marker array
|
||||
/** The marker array will be of size max_attr and it will contain only zeroes
|
||||
and ones. Ones indicate which attribute numbers are present in the attrs
|
||||
array. In the special case when attrs has a single entry equal to -1 the
|
||||
marker array will contain all ones. */
|
||||
void AttrToMarker(int max_attr, const Array<int> &attrs, Array<int> &marker);
|
||||
array. In the special case when attrs has an entry equal to -1 the marker
|
||||
array will contain all ones. */
|
||||
inline
|
||||
void AttrToMarker(int max_attr, const Array<int> &attrs, Array<int> &marker)
|
||||
{
|
||||
if (attrs.Find(-1) != -1) { (marker = Array<int>(max_attr)) = 1; }
|
||||
else { marker = AttributeSets::AttrToMarker(max_attr, attrs); }
|
||||
}
|
||||
|
||||
/// Transform a mesh according to an arbitrary affine transformation
|
||||
/// y = A x + b
|
||||
|
||||
@@ -127,7 +127,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// Load mesh + complete any serial refinements
|
||||
Mesh mesh("../../data/channel-bifurcation-2d.mesh");
|
||||
Mesh mesh("../../../data/channel-bifurcation-2d.mesh");
|
||||
for (int lev = 0; lev < ctx.rs_levels; lev++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
|
||||
@@ -15,16 +15,14 @@ set(MESH_GF_FILES
|
||||
triple-pt-1.gf
|
||||
triple-pt-2.gf
|
||||
)
|
||||
|
||||
# add target which keeps required mesh files in sync
|
||||
set(SRC_MESH_GF_FILES)
|
||||
foreach(MESH_GF_FILE ${MESH_GF_FILES})
|
||||
list(APPEND SRC_MESH_GF_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${MESH_GF_FILE})
|
||||
endforeach()
|
||||
add_custom_command(OUTPUT data_is_copied
|
||||
add_custom_target(copy_miniapps_gslib_data
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_GF_FILES} .
|
||||
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
|
||||
COMMENT "Copying gslib miniapps data files ...")
|
||||
add_custom_target(copy_miniapps_gslib_data DEPENDS data_is_copied)
|
||||
COMMENT "Syncing gslib miniapps data files ...")
|
||||
|
||||
if (MFEM_USE_GSLIB)
|
||||
add_mfem_miniapp(schwarz_ex1
|
||||
|
||||
@@ -27,11 +27,9 @@ set(SRC_MESH_FILES)
|
||||
foreach(MESH_FILE ${MESH_FILES})
|
||||
list(APPEND SRC_MESH_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${MESH_FILE})
|
||||
endforeach()
|
||||
add_custom_command(OUTPUT data_is_copied
|
||||
add_custom_target(copy_miniapps_meshing_data
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_FILES} .
|
||||
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
|
||||
COMMENT "Copying meshing miniapps data files ...")
|
||||
add_custom_target(copy_miniapps_meshing_data DEPENDS data_is_copied)
|
||||
COMMENT "Syncing meshing miniapps data files ...")
|
||||
|
||||
add_mfem_miniapp(klein-bottle
|
||||
MAIN klein-bottle.cpp
|
||||
|
||||
+234
-12
@@ -33,8 +33,7 @@
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
|
||||
void ReflectPoint(Vector & p, Vector const& origin, Vector const& normal)
|
||||
void ReflectPoint(Vector &p, const Vector &origin, const Vector &normal)
|
||||
{
|
||||
Vector diff(3);
|
||||
Vector proj(3);
|
||||
@@ -57,12 +56,12 @@ private:
|
||||
// Map from reflected to original mesh elements
|
||||
std::vector<int> *r2o;
|
||||
|
||||
std::vector<std::vector<int>> *perm;
|
||||
std::vector<std::array<int, 8>> *perm;
|
||||
|
||||
public:
|
||||
ReflectedCoefficient(VectorCoefficient &A, Vector const& origin_,
|
||||
Vector const& normal_, std::vector<int> *r2o_,
|
||||
Mesh *mesh, std::vector<std::vector<int>> *refPerm) :
|
||||
Mesh *mesh, std::vector<std::array<int, 8>> *refPerm) :
|
||||
VectorCoefficient(3), a(&A), origin(origin_), normal(normal_),
|
||||
meshOrig(mesh), r2o(r2o_), perm(refPerm)
|
||||
{ }
|
||||
@@ -109,7 +108,7 @@ void ReflectedCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
// give the columns of A.
|
||||
|
||||
// Permutation p is such that hex_reflected[i] = hex_init[p[i]]
|
||||
const std::vector<int>& p = (*perm)[elem];
|
||||
const std::array<int, 8>& p = (*perm)[elem];
|
||||
|
||||
// ip is on reflected hex. We map from the reflected hex to the initial
|
||||
// hex, in reference space. Thus we use y = Ax + b, where x is in the
|
||||
@@ -164,7 +163,7 @@ void ReflectedCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
|
||||
// Find perm such that h1[i] = h2[perm[i]]
|
||||
void GetHexPermutation(Array<int> const& h1, Array<int> const& h2,
|
||||
std::vector<int> & perm)
|
||||
std::array<int, 8> &perm)
|
||||
{
|
||||
std::map<int, int> h2inv;
|
||||
const int n = perm.size();
|
||||
@@ -236,7 +235,7 @@ public:
|
||||
int AddElement(Array<int> const& vertices, const bool reorder);
|
||||
|
||||
Mesh *mesh;
|
||||
std::vector<std::vector<int>> refPerm;
|
||||
std::vector<std::array<int, 8>> refPerm;
|
||||
|
||||
private:
|
||||
std::vector<std::vector<int>> faces;
|
||||
@@ -279,13 +278,13 @@ int HexMeshBuilder::AddElement(Array<int> const& vertices, const bool reorder)
|
||||
}
|
||||
while (reordered);
|
||||
|
||||
std::vector<int> perm_e(8);
|
||||
std::array<int, 8> perm_e;
|
||||
GetHexPermutation(rvert, vertices, perm_e);
|
||||
refPerm.push_back(perm_e);
|
||||
}
|
||||
else
|
||||
{
|
||||
refPerm.push_back(std::vector<int> {0, 1, 2, 3, 4, 5, 6, 7});
|
||||
refPerm.push_back(std::array<int, 8> {0, 1, 2, 3, 4, 5, 6, 7});
|
||||
}
|
||||
|
||||
SaveHexFaces(mesh->GetNE(), rvert);
|
||||
@@ -762,7 +761,10 @@ bool GetMeshElementOrder(Mesh const& mesh, Vector const& origin,
|
||||
return true;
|
||||
}
|
||||
|
||||
Mesh* ReflectHighOrderMesh(Mesh & mesh, Vector origin, Vector normal)
|
||||
Mesh* ReflectHighOrderMesh(Mesh &mesh,
|
||||
const Vector &origin, const Vector &normal,
|
||||
std::vector<std::array<int, 8>> &hexPerm,
|
||||
std::vector<int> &elOrder)
|
||||
{
|
||||
MFEM_VERIFY(mesh.Dimension() == 3, "Only 3D meshes can be reflected");
|
||||
|
||||
@@ -837,7 +839,6 @@ Mesh* ReflectHighOrderMesh(Mesh & mesh, Vector origin, Vector normal)
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<int> elOrder;
|
||||
const bool onPlane = GetMeshElementOrder(mesh, origin, normal, elOrder);
|
||||
|
||||
for (int eidx=0; eidx<mesh.GetNE(); eidx++)
|
||||
@@ -1006,6 +1007,133 @@ Mesh* ReflectHighOrderMesh(Mesh & mesh, Vector origin, Vector normal)
|
||||
*reflected_nodes = newReflectedNodes;
|
||||
}
|
||||
|
||||
hexPerm = builder.refPerm;
|
||||
|
||||
return reflected;
|
||||
}
|
||||
|
||||
void ReorderHexArray(const std::array<int, 3> &dim,
|
||||
const array<int, 8> &hexperm,
|
||||
std::array<int, 3> &dir, std::array<int, 3> &dims,
|
||||
Array3D<int> &permArray);
|
||||
|
||||
NURBSPatch* ReflectPatch(NURBSPatch *patch, int nx, int ny, int nz,
|
||||
const Vector &origin, const Vector &normal,
|
||||
const std::array<int, 8> &hexPerm)
|
||||
{
|
||||
// The hexahedral element for this patch in the reflected patch topology mesh
|
||||
// is the reflection of an original patch topology mesh element, with
|
||||
// reference vertices permuted according to hexPerm. The original grid of
|
||||
// (nx + 1) x (ny + 1) x (nz + 1)
|
||||
// control points has a new size and ordering, depending on hexPerm. Now,
|
||||
// ReorderHexArray finds the new dimensions of this grid in `dims`, maps the
|
||||
// directions in `dir`, and sets the permutation of grid indices as triples
|
||||
// in `permArray`.
|
||||
std::array<int, 3> dims, dir;
|
||||
Array3D<int> permArray;
|
||||
ReorderHexArray({nx+1, ny+1, nz+1}, hexPerm, dir, dims, permArray);
|
||||
|
||||
const KnotVector *kv0 = patch->GetKV(dir[0]);
|
||||
const KnotVector *kv1 = patch->GetKV(dir[1]);
|
||||
const KnotVector *kv2 = patch->GetKV(dir[2]);
|
||||
|
||||
NURBSPatch *rpatch = new NURBSPatch(kv0, kv1, kv2, 4);
|
||||
|
||||
// Reflect the control points in this reflected patch `rpatch`.
|
||||
Vector vr(3);
|
||||
for (int i=0; i<dims[0]; ++i)
|
||||
{
|
||||
for (int j=0; j<dims[1]; ++j)
|
||||
{
|
||||
for (int k=0; k<dims[2]; ++k)
|
||||
{
|
||||
const int old = permArray(i,j,k);
|
||||
const int i0 = old / ((ny + 1) * (nz + 1));
|
||||
const int j0 = (old - (i0 * (ny + 1) * (nz + 1))) / (nz + 1);
|
||||
const int k0 = old - (i0 * (ny + 1) * (nz + 1)) - (j0 * (nz+1));
|
||||
|
||||
const real_t w = (*patch)(i0,j0,k0,3); // Weight
|
||||
for (int l=0; l<3; ++l) { vr[l] = (*patch)(i0,j0,k0,l) / w; }
|
||||
|
||||
ReflectPoint(vr, origin, normal);
|
||||
|
||||
for (int l=0; l<3; ++l) { (*rpatch)(i,j,k,l) = vr[l] * w; }
|
||||
(*rpatch)(i,j,k,3) = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rpatch;
|
||||
}
|
||||
|
||||
Mesh* ReflectNURBSMesh(Mesh &mesh, const Vector &origin, const Vector &normal)
|
||||
{
|
||||
MFEM_VERIFY(mesh.NURBSext && mesh.Dimension() == 3,
|
||||
"Only 3D NURBS meshes can be reflected");
|
||||
|
||||
Mesh patchTopo = mesh.NURBSext->GetPatchTopology(); // Deep copy
|
||||
|
||||
Array<NURBSPatch*> patchesOriginal, patches;
|
||||
mesh.GetNURBSPatches(patchesOriginal); // Deep copy
|
||||
|
||||
NURBSPatchMap p2g(mesh.NURBSext);
|
||||
const KnotVector *kv[3];
|
||||
|
||||
const int pnv = patchTopo.GetNV();
|
||||
Vector vert_coord(3 * patchTopo.GetNV());
|
||||
for (int p=0; p<patchesOriginal.Size(); ++p)
|
||||
{
|
||||
p2g.SetPatchDofMap(p, kv);
|
||||
const int nx = p2g.nx();
|
||||
const int ny = p2g.ny();
|
||||
const int nz = p2g.nz();
|
||||
|
||||
Array<int> vert;
|
||||
patchTopo.GetElementVertices(p, vert);
|
||||
|
||||
for (int l=0; l<3; ++l)
|
||||
{
|
||||
const int os = l * pnv;
|
||||
vert_coord[vert[0] + os] = (*patchesOriginal[p])(0,0,0,l);
|
||||
vert_coord[vert[1] + os] = (*patchesOriginal[p])(nx,0,0,l);
|
||||
vert_coord[vert[2] + os] = (*patchesOriginal[p])(nx,ny,0,l);
|
||||
vert_coord[vert[3] + os] = (*patchesOriginal[p])(0,ny,0,l);
|
||||
vert_coord[vert[4] + os] = (*patchesOriginal[p])(0,0,nz,l);
|
||||
vert_coord[vert[5] + os] = (*patchesOriginal[p])(nx,0,nz,l);
|
||||
vert_coord[vert[6] + os] = (*patchesOriginal[p])(nx,ny,nz,l);
|
||||
vert_coord[vert[7] + os] = (*patchesOriginal[p])(0,ny,nz,l);
|
||||
}
|
||||
}
|
||||
|
||||
patchTopo.SetVertices(vert_coord);
|
||||
|
||||
std::vector<std::array<int, 8>> hexPerm;
|
||||
std::vector<int> elOrder;
|
||||
Mesh *reflectedPatchTopo = ReflectHighOrderMesh(patchTopo, origin, normal,
|
||||
hexPerm, elOrder);
|
||||
|
||||
// Construct reflected patches. Note that reflectedPatchTopo has patch
|
||||
// ordering depending on patchTopo.
|
||||
for (int p=0; p<patchesOriginal.Size(); ++p)
|
||||
{
|
||||
const int p_orig = elOrder[p]; // TODO: use r2o instead?
|
||||
p2g.SetPatchDofMap(p_orig, kv);
|
||||
const int nx = p2g.nx();
|
||||
const int ny = p2g.ny();
|
||||
const int nz = p2g.nz();
|
||||
|
||||
patches.Append(patchesOriginal[p_orig]);
|
||||
patches.Append(ReflectPatch(patchesOriginal[p_orig], nx, ny, nz,
|
||||
origin, normal, hexPerm[(2 * p) + 1]));
|
||||
}
|
||||
|
||||
NURBSExtension *ne = new NURBSExtension(reflectedPatchTopo, patches);
|
||||
delete reflectedPatchTopo;
|
||||
|
||||
for (auto patch : patches) { delete patch; }
|
||||
|
||||
Mesh *reflected = new Mesh(*ne);
|
||||
delete ne;
|
||||
return reflected;
|
||||
}
|
||||
|
||||
@@ -1045,7 +1173,19 @@ int main(int argc, char *argv[])
|
||||
|
||||
Mesh mesh(mesh_file, 0, 0);
|
||||
|
||||
Mesh *reflected = ReflectHighOrderMesh(mesh, origin, normal);
|
||||
Mesh *reflected{nullptr};
|
||||
|
||||
//if (mesh.IsNURBS()) // TODO: available in PR 4936
|
||||
if (mesh.NURBSext)
|
||||
{
|
||||
reflected = ReflectNURBSMesh(mesh, origin, normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::array<int, 8>> hexPerm;
|
||||
std::vector<int> elOrder;
|
||||
reflected = ReflectHighOrderMesh(mesh, origin, normal, hexPerm, elOrder);
|
||||
}
|
||||
|
||||
// Save the final mesh
|
||||
ofstream mesh_ofs("reflected.mesh");
|
||||
@@ -1065,3 +1205,85 @@ int main(int argc, char *argv[])
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void HexVertexIJK(const int idx, std::array<int, 3>& ijk)
|
||||
{
|
||||
ijk[2] = idx / 4;
|
||||
const int id2d = idx - (4 * ijk[2]);
|
||||
ijk[1] = id2d / 2;
|
||||
ijk[0] = (ijk[1] == 0) ? id2d : 3 - id2d;
|
||||
}
|
||||
|
||||
void ReorderHexArray(const std::array<int, 3> &dim,
|
||||
const array<int, 8> &hexperm,
|
||||
std::array<int, 3> &dir, std::array<int, 3> &dims,
|
||||
Array3D<int> &permArray)
|
||||
{
|
||||
int prinV[4] = {0, 1, 3, 4}; // Vertices in principal directions (after 0)
|
||||
int newPrinV[4];
|
||||
|
||||
// newVertices[i] = oldVertices[hexperm[i]]
|
||||
// Hence newPrinV[0] = hexperm[0] is the index
|
||||
// of new vertex 0 in the old hex.
|
||||
|
||||
std::array<int, 3> newIJK[4];
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
newPrinV[i] = hexperm[prinV[i]];
|
||||
HexVertexIJK(newPrinV[i], newIJK[i]);
|
||||
}
|
||||
|
||||
// For direction i in the new hex, dir[i] is the direction in the old hex.
|
||||
Array<bool> rev(3);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
bool iset = false;
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
const int d = newIJK[i + 1][j] - newIJK[0][j];
|
||||
if (d != 0)
|
||||
{
|
||||
MFEM_VERIFY(!iset, "");
|
||||
MFEM_VERIFY(d == 1 || d == -1, "");
|
||||
dir[i] = j;
|
||||
rev[i] = (d == -1);
|
||||
iset = true;
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_VERIFY(iset, "");
|
||||
|
||||
dims[i] = dim[dir[i]];
|
||||
}
|
||||
|
||||
MFEM_VERIFY(dir[0] + dir[1] + dir[2] == 3, "");
|
||||
|
||||
permArray.SetSize(dims[0], dims[1], dims[2]);
|
||||
|
||||
Array<int> old_ijk(3);
|
||||
Array<int> new_ijk(3);
|
||||
for (int i = 0; i < dims[0]; ++i)
|
||||
for (int j = 0; j < dims[1]; ++j)
|
||||
for (int k = 0; k < dims[2]; ++k)
|
||||
{
|
||||
new_ijk[0] = i;
|
||||
new_ijk[1] = j;
|
||||
new_ijk[2] = k;
|
||||
|
||||
for (int m = 0; m < 3; ++m)
|
||||
{
|
||||
const int d = dir[m]; // Old hex direction
|
||||
if (rev[m])
|
||||
{
|
||||
old_ijk[d] = dim[d] - 1 - new_ijk[m];
|
||||
}
|
||||
else
|
||||
{
|
||||
old_ijk[d] = new_ijk[m];
|
||||
}
|
||||
}
|
||||
|
||||
permArray(i, j, k) =
|
||||
old_ijk[2] + (old_ijk[1] * dim[2]) + (old_ijk[0] * dim[1] * dim[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,9 @@ set(SRC_MESH_FILES)
|
||||
foreach(MESH_FILE ${MESH_FILES})
|
||||
list(APPEND SRC_MESH_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${MESH_FILE})
|
||||
endforeach()
|
||||
add_custom_command(OUTPUT data_is_copied
|
||||
add_custom_target(copy_miniapps_multidomain_data
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_FILES} .
|
||||
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
|
||||
COMMENT "Copying multidomain miniapps data files ...")
|
||||
add_custom_target(copy_miniapps_multidomain_data DEPENDS data_is_copied)
|
||||
COMMENT "Syncing multidomain miniapps data files ...")
|
||||
|
||||
# Parallel apps.
|
||||
if (MFEM_USE_MPI)
|
||||
|
||||
@@ -9,31 +9,12 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
set(MESH_FILES
|
||||
cube-nurbs.mesh
|
||||
ijk-hex-nurbs.mesh
|
||||
plus-nurbs-permuted.mesh
|
||||
plus-nurbs.mesh
|
||||
square-nurbs.mesh
|
||||
two-cubes-nurbs-autoedge.mesh
|
||||
two-cubes-nurbs-rot.mesh
|
||||
two-cubes-nurbs.mesh
|
||||
two-squares-nurbs-autoedge.mesh
|
||||
two-squares-nurbs-rot.mesh
|
||||
two-squares-nurbs.mesh
|
||||
)
|
||||
# Add a target to copy the mesh files from the source directory; used by sample
|
||||
# runs.
|
||||
set(SRC_MESH_FILES)
|
||||
foreach(MESH_FILE ${MESH_FILES})
|
||||
list(APPEND SRC_MESH_FILES ${CMAKE_CURRENT_SOURCE_DIR}/meshes/${MESH_FILE})
|
||||
endforeach()
|
||||
add_custom_command(OUTPUT data_is_copied
|
||||
# add target which keeps required mesh files in sync
|
||||
file(GLOB SRC_MESH_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/meshes/*)
|
||||
add_custom_target(copy_miniapps_nurbs_data
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory meshes
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_FILES} meshes/
|
||||
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
|
||||
COMMENT "Copying nurbs miniapps data files ...")
|
||||
add_custom_target(copy_miniapps_nurbs_data DEPENDS data_is_copied)
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_FILES} meshes
|
||||
COMMENT "Syncing nurbs miniapps data directory ...")
|
||||
|
||||
add_mfem_miniapp(nurbs_ex1
|
||||
MAIN nurbs_ex1.cpp
|
||||
@@ -50,6 +31,11 @@ add_mfem_miniapp(nurbs_ex5
|
||||
LIBRARIES mfem)
|
||||
add_dependencies(nurbs_ex5 copy_miniapps_nurbs_data)
|
||||
|
||||
add_mfem_miniapp(nurbs_ex10
|
||||
MAIN nurbs_ex10.cpp
|
||||
LIBRARIES mfem)
|
||||
add_dependencies(nurbs_ex10 copy_miniapps_nurbs_data)
|
||||
|
||||
add_mfem_miniapp(nurbs_ex24
|
||||
MAIN nurbs_ex24.cpp
|
||||
LIBRARIES mfem)
|
||||
@@ -70,6 +56,10 @@ add_mfem_miniapp(nurbs_printfunc
|
||||
LIBRARIES mfem)
|
||||
add_dependencies(nurbs_printfunc copy_miniapps_nurbs_data)
|
||||
|
||||
add_mfem_miniapp(nurbs_mesh_info
|
||||
MAIN nurbs_mesh_info.cpp
|
||||
LIBRARIES mfem)
|
||||
|
||||
add_mfem_miniapp(nurbs_patch_ex1
|
||||
MAIN nurbs_patch_ex1.cpp
|
||||
LIBRARIES mfem)
|
||||
@@ -267,6 +257,11 @@ if (MFEM_USE_MPI)
|
||||
LIBRARIES mfem)
|
||||
add_dependencies(nurbs_ex1p copy_miniapps_nurbs_data)
|
||||
|
||||
add_mfem_miniapp(nurbs_ex10p
|
||||
MAIN nurbs_ex10p.cpp
|
||||
LIBRARIES mfem)
|
||||
add_dependencies(nurbs_ex10p copy_miniapps_nurbs_data)
|
||||
|
||||
add_mfem_miniapp(nurbs_ex11p
|
||||
MAIN nurbs_ex11p.cpp
|
||||
LIBRARIES mfem)
|
||||
|
||||
@@ -20,9 +20,11 @@ CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_ex3 nurbs_ex5 nurbs_ex24 \
|
||||
nurbs_curveint nurbs_printfunc nurbs_solenoidal nurbs_naca_cmesh nurbs_surface
|
||||
PAR_MINIAPPS = nurbs_ex1p nurbs_ex11p
|
||||
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_ex3 nurbs_ex5 nurbs_ex10 \
|
||||
nurbs_ex24 nurbs_curveint nurbs_printfunc nurbs_solenoidal nurbs_naca_cmesh \
|
||||
nurbs_mesh_info
|
||||
PAR_MINIAPPS = nurbs_ex1p nurbs_ex10p nurbs_ex11p
|
||||
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
MINIAPPS = $(SEQ_MINIAPPS)
|
||||
else
|
||||
@@ -199,6 +201,7 @@ clean-build:
|
||||
|
||||
clean-exec:
|
||||
@rm -f refined.mesh sin-fit.mesh ex5.mesh exsol.mesh mesh.* sol.* mode_*
|
||||
@rm -f naca-cmesh.mesh sol_?.gf *-Surface.mesh
|
||||
@rm -f naca-cmesh.mesh sol_?.gf k?_*.dat *-Surface.mesh
|
||||
@rm -rf Example1* Example3* Example5* Solenoidal_* ParaView
|
||||
@rm -rf CurveInt Naca_cmesh glvis_naca-cmesh.mesh solution.dat
|
||||
@rm -rf velocity.* elastic_energy.* deformed.*
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
# Same as 3patch-nurbs.mesh but with some flipped edges
|
||||
# This will fail to load without CorrectPatchTopoOrientations
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
elements
|
||||
3
|
||||
1 3 0 1 4 3
|
||||
1 3 1 2 5 4
|
||||
1 3 5 6 3 4
|
||||
|
||||
boundary
|
||||
0
|
||||
|
||||
edges
|
||||
9
|
||||
2 1 0
|
||||
1 1 4
|
||||
2 4 3
|
||||
1 0 3
|
||||
0 1 2
|
||||
1 5 2
|
||||
0 5 4
|
||||
2 5 6
|
||||
0 3 6
|
||||
|
||||
|
||||
vertices
|
||||
7
|
||||
|
||||
patches
|
||||
|
||||
knotvectors
|
||||
2
|
||||
1 2 0 0 1 1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0 0 1
|
||||
1 0 1
|
||||
0 1 1
|
||||
1 1 1
|
||||
|
||||
knotvectors
|
||||
2
|
||||
1 2 0 0 1 1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
1 0 1
|
||||
2 0 1
|
||||
1 1 1
|
||||
2 2 1
|
||||
|
||||
knotvectors
|
||||
2
|
||||
1 2 0 0 1 1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
2 2 1
|
||||
1 2 1
|
||||
1 1 1
|
||||
0 1 1
|
||||
@@ -0,0 +1,72 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
elements
|
||||
3
|
||||
1 3 0 1 4 3
|
||||
1 3 1 2 5 4
|
||||
1 3 5 6 3 4
|
||||
|
||||
boundary
|
||||
0
|
||||
|
||||
edges
|
||||
9
|
||||
2 0 1
|
||||
1 1 4
|
||||
2 3 4
|
||||
1 0 3
|
||||
0 1 2
|
||||
1 2 5
|
||||
0 4 5
|
||||
2 6 5
|
||||
0 3 6
|
||||
|
||||
vertices
|
||||
7
|
||||
|
||||
patches
|
||||
|
||||
knotvectors
|
||||
2
|
||||
1 2 0 0 1 1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0 0 1
|
||||
1 0 1
|
||||
0 1 1
|
||||
1 1 1
|
||||
|
||||
knotvectors
|
||||
2
|
||||
1 2 0 0 1 1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
1 0 1
|
||||
2 0 1
|
||||
1 1 1
|
||||
2 2 1
|
||||
|
||||
knotvectors
|
||||
2
|
||||
1 2 0 0 1 1
|
||||
1 2 0 0 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
2 2 1
|
||||
1 2 1
|
||||
1 1 1
|
||||
0 1 1
|
||||
@@ -127,14 +127,6 @@ int main(int argc, char *argv[])
|
||||
patch(1,1,0) = 0.5*l;
|
||||
patch(1,1,1) = 0.5*l;
|
||||
|
||||
// 2. Interpolation process
|
||||
Array<Vector*> xy(2);
|
||||
xy[0] = new Vector();
|
||||
xy[1] = new Vector();
|
||||
Vector xi_args, u_args;
|
||||
Array<int> i_args;
|
||||
xy[0]->SetSize(ncp); xy[1]->SetSize(ncp);
|
||||
|
||||
// Refine direction which has fitting
|
||||
if (!ifbspline)
|
||||
{
|
||||
@@ -150,24 +142,28 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
patch.KnotInsert(0, *kv);
|
||||
|
||||
// We locate the control points at the location of the maxima of the
|
||||
// knot vectors. This works very well for patches with unit weights.
|
||||
kv->FindMaxima(i_args,xi_args, u_args);
|
||||
// We locate the control points at the demko points.
|
||||
Vector u(ncp),x(ncp),interp(ncp);
|
||||
kv->GetDemko(u);
|
||||
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
(*xy[0])[i] = u_args[i]*l;
|
||||
(*xy[1])[i] = a * sin((*xy[0])[i]/l*2*M_PI)-0.5*l;
|
||||
(*xy[0])[i] -= 0.5*l;
|
||||
x[i] = (u[i] - 0.5)*l;
|
||||
}
|
||||
kv->GetInterpolant(x,u,interp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch(i,0,0) = interp[i];
|
||||
}
|
||||
|
||||
kv->FindInterpolant(xy);
|
||||
|
||||
// Apply interpolation to patch
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch(i,0,0) = (*xy[0])[i];
|
||||
patch(i,0,1) = (*xy[1])[i];
|
||||
x[i] = a * sin(u[i]*2*M_PI)-0.5*l;
|
||||
}
|
||||
kv->GetInterpolant(x,u,interp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch(i,0,1) = interp[i];
|
||||
}
|
||||
|
||||
if (!ifbspline)
|
||||
@@ -243,8 +239,6 @@ int main(int argc, char *argv[])
|
||||
delete mesh;
|
||||
delete kv_o1;
|
||||
delete kv;
|
||||
delete xy[0];
|
||||
delete xy[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -139,10 +139,24 @@ public:
|
||||
|
||||
};
|
||||
|
||||
real_t sol(const Vector & x)
|
||||
{
|
||||
if (x.Size() >= 2)
|
||||
{
|
||||
if ((x[1] - x[0] - 0.5 < 0.0) &&
|
||||
(x[0] + x[1] -0.99 < 0.0))
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
const char *mesh_file = "../../data/square-nurbs.mesh";
|
||||
const char *per_file = "none";
|
||||
const char *ref_file = "";
|
||||
int ref_levels = -1;
|
||||
@@ -158,6 +172,7 @@ int main(int argc, char *argv[])
|
||||
Array<int> order(1);
|
||||
int visport = 19916;
|
||||
order[0] = 1;
|
||||
bool homogenousBC = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -174,11 +189,14 @@ int main(int argc, char *argv[])
|
||||
"Slave boundaries for periodic BCs");
|
||||
args.AddOption(&neu, "-n", "--neu",
|
||||
"Boundaries with Neumann BCs");
|
||||
args.AddOption(&homogenousBC, "-h", "--hom",
|
||||
"-nh", "--no-hom",
|
||||
"Selection for using homogenous Dirichelet boundary conditions.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&ibp, "-ibp", "--ibp", "-no-ibp",
|
||||
"--no-ibp",
|
||||
args.AddOption(&ibp, "-ibp", "--ibp",
|
||||
"-no-ibp", "--no-ibp",
|
||||
"Selects the standard weak form (IBP) or the nonstandard (NO-IBP).");
|
||||
args.AddOption(&strongBC, "-sbc", "--strong-bc", "-wbc",
|
||||
"--weak-bc",
|
||||
@@ -408,10 +426,20 @@ int main(int argc, char *argv[])
|
||||
b->Assemble();
|
||||
|
||||
// 7. Define the solution vector x as a finite element grid function
|
||||
// corresponding to fespace. Initialize x with initial guess of zero,
|
||||
// which satisfies the boundary conditions.
|
||||
// corresponding to fespace. Initialize x with initial guess that
|
||||
// satisfies the boundary conditions. Force the use of the ELEMENT
|
||||
// projection type, also in the case of a NURBS spaces. For a NURBS space
|
||||
// this will give a projection without any over and undershoots.
|
||||
GridFunction x(fespace);
|
||||
x = 0.0;
|
||||
if (homogenousBC)
|
||||
{
|
||||
x = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
FunctionCoefficient sol_cf(sol);
|
||||
x.ProjectCoefficient(sol_cf, ProjectType::ELEMENT);
|
||||
}
|
||||
|
||||
// 8. Set up the bilinear form a(.,.) on the finite element space
|
||||
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
|
||||
|
||||
@@ -0,0 +1,613 @@
|
||||
// MFEM Example 10 -- modified for NURBS FE
|
||||
//
|
||||
// Compile with: make nurbs_ex10
|
||||
//
|
||||
// Sample runs:
|
||||
// nurbs_ex10 -m ../../data/beam-quad-nurbs.mesh -s 23 -r 2 -o 2 -dt 0.1
|
||||
//
|
||||
// Description: This examples solves a time dependent nonlinear elasticity
|
||||
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
|
||||
// hyperelastic model and S is a viscosity operator of Laplacian
|
||||
// type. The geometry of the domain is assumed to be as follows:
|
||||
//
|
||||
// +---------------------+
|
||||
// boundary --->| |
|
||||
// attribute 1 | |
|
||||
// (fixed) +---------------------+
|
||||
//
|
||||
// The example demonstrates the use of nonlinear operators (the
|
||||
// class HyperelasticOperator defining H(x)), as well as their
|
||||
// implicit time integration using a Newton method for solving an
|
||||
// associated reduced backward-Euler type nonlinear equation
|
||||
// (class ReducedSystemOperator). Each Newton step requires the
|
||||
// inversion of a Jacobian matrix, which is done through a
|
||||
// (preconditioned) inner solver. Note that implementing the
|
||||
// method HyperelasticOperator::ImplicitSolve is the only
|
||||
// requirement for high-order implicit (SDIRK) time integration.
|
||||
//
|
||||
// We recommend viewing examples 2 and 9 before viewing this
|
||||
// example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class ReducedSystemOperator;
|
||||
|
||||
/** After spatial discretization, the hyperelastic model can be written as a
|
||||
* system of ODEs:
|
||||
* dv/dt = -M^{-1}*(H(x) + S*v)
|
||||
* dx/dt = v,
|
||||
* where x is the vector representing the deformation, v is the velocity field,
|
||||
* M is the mass matrix, S is the viscosity matrix, and H(x) is the nonlinear
|
||||
* hyperelastic operator.
|
||||
*
|
||||
* Class HyperelasticOperator represents the right-hand side of the above
|
||||
* system of ODEs. */
|
||||
class HyperelasticOperator : public TimeDependentOperator
|
||||
{
|
||||
protected:
|
||||
FiniteElementSpace &fespace;
|
||||
|
||||
BilinearForm M, S;
|
||||
NonlinearForm H;
|
||||
real_t viscosity;
|
||||
HyperelasticModel *model;
|
||||
|
||||
CGSolver M_solver; // Krylov solver for inverting the mass matrix M
|
||||
DSmoother M_prec; // Preconditioner for the mass matrix M
|
||||
|
||||
/** Nonlinear operator defining the reduced backward Euler equation for the
|
||||
velocity. Used in the implementation of method ImplicitSolve. */
|
||||
ReducedSystemOperator *reduced_oper;
|
||||
|
||||
/// Newton solver for the reduced backward Euler equation
|
||||
NewtonSolver newton_solver;
|
||||
|
||||
/// Solver for the Jacobian solve in the Newton method
|
||||
Solver *J_solver;
|
||||
/// Preconditioner for the Jacobian solve in the Newton method
|
||||
Solver *J_prec;
|
||||
|
||||
mutable Vector z; // auxiliary vector
|
||||
|
||||
public:
|
||||
HyperelasticOperator(FiniteElementSpace &f, Array<int> &ess_bdr,
|
||||
real_t visc, real_t mu, real_t K);
|
||||
|
||||
/// Compute the right-hand side of the ODE system.
|
||||
void Mult(const Vector &vx, Vector &dvx_dt) const override;
|
||||
/** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
|
||||
This is the only requirement for high-order SDIRK implicit integration.*/
|
||||
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
|
||||
|
||||
real_t ElasticEnergy(const Vector &x) const;
|
||||
real_t KineticEnergy(const Vector &v) const;
|
||||
void GetElasticEnergyDensity(const GridFunction &x,
|
||||
GridFunction &w,
|
||||
ProjectType proj_type) const;
|
||||
|
||||
~HyperelasticOperator() override;
|
||||
};
|
||||
|
||||
/** Nonlinear operator of the form:
|
||||
k --> (M + dt*S)*k + H(x + dt*v + dt^2*k) + S*v,
|
||||
where M and S are given BilinearForms, H is a given NonlinearForm, v and x
|
||||
are given vectors, and dt is a scalar. */
|
||||
class ReducedSystemOperator : public Operator
|
||||
{
|
||||
private:
|
||||
BilinearForm *M, *S;
|
||||
NonlinearForm *H;
|
||||
mutable SparseMatrix *Jacobian;
|
||||
real_t dt;
|
||||
const Vector *v, *x;
|
||||
mutable Vector w, z;
|
||||
|
||||
public:
|
||||
ReducedSystemOperator(BilinearForm *M_, BilinearForm *S_, NonlinearForm *H_);
|
||||
|
||||
/// Set current dt, v, x values - needed to compute action and Jacobian.
|
||||
void SetParameters(real_t dt_, const Vector *v_, const Vector *x_);
|
||||
|
||||
/// Compute y = H(x + dt (v + dt k)) + M k + S (v + dt k).
|
||||
void Mult(const Vector &k, Vector &y) const override;
|
||||
|
||||
/// Compute J = M + dt S + dt^2 grad_H(x + dt (v + dt k)).
|
||||
Operator &GetGradient(const Vector &k) const override;
|
||||
|
||||
~ReducedSystemOperator() override;
|
||||
};
|
||||
|
||||
|
||||
/** Function representing the elastic energy density for the given hyperelastic
|
||||
model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
|
||||
class ElasticEnergyCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
HyperelasticModel &model;
|
||||
const GridFunction &x;
|
||||
DenseMatrix J;
|
||||
|
||||
public:
|
||||
ElasticEnergyCoefficient(HyperelasticModel &m, const GridFunction &x_)
|
||||
: model(m), x(x_) { }
|
||||
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
|
||||
~ElasticEnergyCoefficient() override { }
|
||||
};
|
||||
|
||||
void InitialDeformation(const Vector &x, Vector &y);
|
||||
|
||||
void InitialVelocity(const Vector &x, Vector &v);
|
||||
|
||||
void visualize(ostream &os, Mesh *mesh, GridFunction *deformed_nodes,
|
||||
GridFunction *field, const char *field_name = NULL,
|
||||
bool init_vis = false);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../data/beam-quad-nurbs.mesh";
|
||||
int ref_levels = 1;
|
||||
int order = 2;
|
||||
int ode_solver_type = 23;
|
||||
real_t t_final = 0.5;
|
||||
real_t dt = 0.1;
|
||||
real_t visc = 1e-2;
|
||||
real_t mu = 0.25;
|
||||
real_t K = 5.0;
|
||||
int proj_type_int = 0;
|
||||
bool visualization = true;
|
||||
int vis_steps = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&ref_levels, "-r", "--refine",
|
||||
"Number of times to refine the mesh uniformly.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&visc, "-v", "--viscosity",
|
||||
"Viscosity coefficient.");
|
||||
args.AddOption(&mu, "-mu", "--shear-modulus",
|
||||
"Shear modulus in the Neo-Hookean hyperelastic model.");
|
||||
args.AddOption(&K, "-K", "--bulk-modulus",
|
||||
"Bulk modulus in the Neo-Hookean hyperelastic model.");
|
||||
args.AddOption(&proj_type_int, "-proj", "--projection",
|
||||
"Projection type:\n."
|
||||
" 0 = DEFAULT: ELEMENTL2 for NURBS elements, ELEMENT else.\n"
|
||||
" 1 = ELEMENT: As defined in the respective element.\n"
|
||||
" 2 = GLOBALL2: Global L2 projection.\n"
|
||||
" 3 = ELEMENTL2: Element L2 projection.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
ProjectType proj_type = static_cast<ProjectType>(proj_type_int);
|
||||
|
||||
// 2. Read the mesh from the given mesh file. We can handle triangular,
|
||||
// quadrilateral, tetrahedral and hexahedral meshes with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
// command-line parameter.
|
||||
for (int lev = 0; lev < ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 5. Define the vector finite element spaces representing the mesh
|
||||
// deformation x, the velocity v, and the initial configuration, x_ref.
|
||||
// Define also the elastic energy density, w, which is in a discontinuous
|
||||
// higher-order space. Since x and v are integrated in time as a system,
|
||||
// we group them together in block vector vx, with offsets given by the
|
||||
// fe_offset array.
|
||||
FiniteElementCollection *fec = nullptr;
|
||||
NURBSExtension *NURBSext = nullptr;
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
NURBSext = new NURBSExtension(mesh->NURBSext, order);
|
||||
fec = new NURBSFECollection(order);
|
||||
cout << "Using NURBS FEs: " << fec->Name() << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
cout << "Using H1 FEs: " << fec->Name() << endl;
|
||||
}
|
||||
|
||||
FiniteElementSpace fespace(mesh, NURBSext, fec, dim);
|
||||
|
||||
int fe_size = fespace.GetTrueVSize();
|
||||
cout << "Number of velocity/deformation unknowns: " << fe_size << endl;
|
||||
Array<int> fe_offset(3);
|
||||
fe_offset[0] = 0;
|
||||
fe_offset[1] = fe_size;
|
||||
fe_offset[2] = 2*fe_size;
|
||||
|
||||
BlockVector vx(fe_offset);
|
||||
GridFunction v, x;
|
||||
v.MakeTRef(&fespace, vx.GetBlock(0), 0);
|
||||
x.MakeTRef(&fespace, vx.GetBlock(1), 0);
|
||||
|
||||
GridFunction x_ref(&fespace);
|
||||
mesh->GetNodes(x_ref);
|
||||
|
||||
L2_FECollection w_fec(order + 1, dim);
|
||||
FiniteElementSpace w_fespace(mesh, &w_fec);
|
||||
GridFunction w(&w_fespace);
|
||||
|
||||
// 6. Set the initial conditions for v and x, and the boundary conditions on
|
||||
// a beam-like mesh (see description above).
|
||||
VectorFunctionCoefficient velo(dim, InitialVelocity);
|
||||
v.ProjectCoefficient(velo, proj_type);
|
||||
|
||||
v.SetTrueVector();
|
||||
VectorFunctionCoefficient deform(dim, InitialDeformation);
|
||||
x.ProjectCoefficient(deform, proj_type);
|
||||
|
||||
x.SetTrueVector();
|
||||
|
||||
Array<int> ess_bdr(fespace.GetMesh()->bdr_attributes.Max());
|
||||
ess_bdr = 0;
|
||||
ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed
|
||||
|
||||
// 7. Initialize the hyperelastic operator, the GLVis visualization and print
|
||||
// the initial energies.
|
||||
HyperelasticOperator oper(fespace, ess_bdr, visc, mu, K);
|
||||
|
||||
socketstream vis_v, vis_w;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
vis_v.open(vishost, visport);
|
||||
vis_v.precision(8);
|
||||
v.SetFromTrueVector(); x.SetFromTrueVector();
|
||||
visualize(vis_v, mesh, &x, &v, "Velocity", true);
|
||||
vis_w.open(vishost, visport);
|
||||
if (vis_w)
|
||||
{
|
||||
oper.GetElasticEnergyDensity(x, w, proj_type);
|
||||
vis_w.precision(8);
|
||||
visualize(vis_w, mesh, &x, &w, "Elastic energy density", true);
|
||||
}
|
||||
cout << "GLVis visualization paused."
|
||||
<< " Press space (in the GLVis window) to resume it.\n";
|
||||
}
|
||||
|
||||
real_t ee0 = oper.ElasticEnergy(x.GetTrueVector());
|
||||
real_t ke0 = oper.KineticEnergy(v.GetTrueVector());
|
||||
cout << "initial elastic energy (EE) = " << ee0 << endl;
|
||||
cout << "initial kinetic energy (KE) = " << ke0 << endl;
|
||||
cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
|
||||
|
||||
real_t t = 0.0;
|
||||
oper.SetTime(t);
|
||||
ode_solver->Init(oper);
|
||||
|
||||
// 8. Perform time-integration (looping over the time iterations, ti, with a
|
||||
// time-step dt).
|
||||
bool last_step = false;
|
||||
for (int ti = 1; !last_step; ti++)
|
||||
{
|
||||
real_t dt_real = min(dt, t_final - t);
|
||||
|
||||
ode_solver->Step(vx, t, dt_real);
|
||||
|
||||
last_step = (t >= t_final - 1e-8*dt);
|
||||
|
||||
if (last_step || (ti % vis_steps) == 0)
|
||||
{
|
||||
real_t ee = oper.ElasticEnergy(x.GetTrueVector());
|
||||
real_t ke = oper.KineticEnergy(v.GetTrueVector());
|
||||
|
||||
cout << "step " << ti << ", t = " << t << ", EE = " << ee << ", KE = "
|
||||
<< ke << ", ΔTE = " << (ee+ke)-(ee0+ke0) << endl;
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
v.SetFromTrueVector(); x.SetFromTrueVector();
|
||||
visualize(vis_v, mesh, &x, &v);
|
||||
if (vis_w)
|
||||
{
|
||||
oper.GetElasticEnergyDensity(x, w, proj_type);
|
||||
visualize(vis_w, mesh, &x, &w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 9. Save the displaced mesh, the velocity and elastic energy.
|
||||
{
|
||||
v.SetFromTrueVector(); x.SetFromTrueVector();
|
||||
GridFunction *nodes = &x;
|
||||
int owns_nodes = 0;
|
||||
mesh->SwapNodes(nodes, owns_nodes);
|
||||
ofstream mesh_ofs("deformed.mesh");
|
||||
mesh_ofs.precision(8);
|
||||
mesh->Print(mesh_ofs);
|
||||
mesh->SwapNodes(nodes, owns_nodes);
|
||||
ofstream velo_ofs("velocity.sol");
|
||||
velo_ofs.precision(8);
|
||||
v.Save(velo_ofs);
|
||||
ofstream ee_ofs("elastic_energy.sol");
|
||||
ee_ofs.precision(8);
|
||||
oper.GetElasticEnergyDensity(x, w, proj_type);
|
||||
w.Save(ee_ofs);
|
||||
}
|
||||
|
||||
// 10. Free the used memory.
|
||||
delete fec;
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void visualize(ostream &os, Mesh *mesh, GridFunction *deformed_nodes,
|
||||
GridFunction *field, const char *field_name, bool init_vis)
|
||||
{
|
||||
if (!os)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GridFunction *nodes = deformed_nodes;
|
||||
int owns_nodes = 0;
|
||||
|
||||
mesh->SwapNodes(nodes, owns_nodes);
|
||||
|
||||
os << "solution\n" << *mesh << *field;
|
||||
|
||||
mesh->SwapNodes(nodes, owns_nodes);
|
||||
|
||||
if (init_vis)
|
||||
{
|
||||
os << "window_size 800 800\n";
|
||||
os << "window_title '" << field_name << "'\n";
|
||||
if (mesh->SpaceDimension() == 2)
|
||||
{
|
||||
os << "view 0 0\n"; // view from top
|
||||
os << "keys jl\n"; // turn off perspective and light
|
||||
}
|
||||
os << "keys cm\n"; // show colorbar and mesh
|
||||
// update value-range; keep mesh-extents fixed
|
||||
os << "autoscale value\n";
|
||||
os << "pause\n";
|
||||
}
|
||||
os << flush;
|
||||
}
|
||||
|
||||
|
||||
ReducedSystemOperator::ReducedSystemOperator(
|
||||
BilinearForm *M_, BilinearForm *S_, NonlinearForm *H_)
|
||||
: Operator(M_->Height()), M(M_), S(S_), H(H_), Jacobian(NULL),
|
||||
dt(0.0), v(NULL), x(NULL), w(height), z(height)
|
||||
{ }
|
||||
|
||||
void ReducedSystemOperator::SetParameters(real_t dt_, const Vector *v_,
|
||||
const Vector *x_)
|
||||
{
|
||||
dt = dt_; v = v_; x = x_;
|
||||
}
|
||||
|
||||
void ReducedSystemOperator::Mult(const Vector &k, Vector &y) const
|
||||
{
|
||||
// compute: y = H(x + dt*(v + dt*k)) + M*k + S*(v + dt*k)
|
||||
add(*v, dt, k, w);
|
||||
add(*x, dt, w, z);
|
||||
H->Mult(z, y);
|
||||
M->AddMult(k, y);
|
||||
S->AddMult(w, y);
|
||||
}
|
||||
|
||||
Operator &ReducedSystemOperator::GetGradient(const Vector &k) const
|
||||
{
|
||||
delete Jacobian;
|
||||
Jacobian = Add(1.0, M->SpMat(), dt, S->SpMat());
|
||||
add(*v, dt, k, w);
|
||||
add(*x, dt, w, z);
|
||||
SparseMatrix *grad_H = dynamic_cast<SparseMatrix *>(&H->GetGradient(z));
|
||||
Jacobian->Add(dt*dt, *grad_H);
|
||||
return *Jacobian;
|
||||
}
|
||||
|
||||
ReducedSystemOperator::~ReducedSystemOperator()
|
||||
{
|
||||
delete Jacobian;
|
||||
}
|
||||
|
||||
|
||||
HyperelasticOperator::HyperelasticOperator(FiniteElementSpace &f,
|
||||
Array<int> &ess_bdr, real_t visc,
|
||||
real_t mu, real_t K)
|
||||
: TimeDependentOperator(2*f.GetTrueVSize(), (real_t) 0.0), fespace(f),
|
||||
M(&fespace), S(&fespace), H(&fespace),
|
||||
viscosity(visc), z(height/2)
|
||||
{
|
||||
#if defined(MFEM_USE_DOUBLE)
|
||||
const real_t rel_tol = 1e-8;
|
||||
const real_t newton_abs_tol = 0.0;
|
||||
#elif defined(MFEM_USE_SINGLE)
|
||||
const real_t rel_tol = 1e-3;
|
||||
const real_t newton_abs_tol = 1e-4;
|
||||
#else
|
||||
#error "Only single and double precision are supported!"
|
||||
const real_t rel_tol = real_t(1);
|
||||
const real_t newton_abs_tol = real_t(0);
|
||||
#endif
|
||||
const int skip_zero_entries = 0;
|
||||
|
||||
const real_t ref_density = 1.0; // density in the reference configuration
|
||||
ConstantCoefficient rho0(ref_density);
|
||||
M.AddDomainIntegrator(new VectorMassIntegrator(rho0));
|
||||
M.Assemble(skip_zero_entries);
|
||||
Array<int> ess_tdof_list;
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
SparseMatrix tmp;
|
||||
M.FormSystemMatrix(ess_tdof_list, tmp);
|
||||
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(rel_tol);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(30);
|
||||
M_solver.SetPrintLevel(0);
|
||||
M_solver.SetPreconditioner(M_prec);
|
||||
M_solver.SetOperator(M.SpMat());
|
||||
|
||||
model = new NeoHookeanModel(mu, K);
|
||||
H.AddDomainIntegrator(new HyperelasticNLFIntegrator(model));
|
||||
H.SetEssentialTrueDofs(ess_tdof_list);
|
||||
|
||||
ConstantCoefficient visc_coeff(viscosity);
|
||||
S.AddDomainIntegrator(new VectorDiffusionIntegrator(visc_coeff));
|
||||
S.Assemble(skip_zero_entries);
|
||||
S.FormSystemMatrix(ess_tdof_list, tmp);
|
||||
|
||||
reduced_oper = new ReducedSystemOperator(&M, &S, &H);
|
||||
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
J_prec = new DSmoother(1);
|
||||
MINRESSolver *J_minres = new MINRESSolver;
|
||||
J_minres->SetRelTol(rel_tol);
|
||||
J_minres->SetAbsTol(0.0);
|
||||
J_minres->SetMaxIter(300);
|
||||
J_minres->SetPrintLevel(-1);
|
||||
J_minres->SetPreconditioner(*J_prec);
|
||||
J_solver = J_minres;
|
||||
#else
|
||||
J_solver = new UMFPackSolver;
|
||||
J_prec = NULL;
|
||||
#endif
|
||||
|
||||
newton_solver.iterative_mode = false;
|
||||
newton_solver.SetSolver(*J_solver);
|
||||
newton_solver.SetOperator(*reduced_oper);
|
||||
newton_solver.SetPrintLevel(1); // print Newton iterations
|
||||
newton_solver.SetRelTol(rel_tol);
|
||||
newton_solver.SetAbsTol(newton_abs_tol);
|
||||
newton_solver.SetMaxIter(10);
|
||||
}
|
||||
|
||||
void HyperelasticOperator::Mult(const Vector &vx, Vector &dvx_dt) const
|
||||
{
|
||||
// Create views to the sub-vectors v, x of vx, and dv_dt, dx_dt of dvx_dt
|
||||
int sc = height/2;
|
||||
Vector v(vx.GetData() + 0, sc);
|
||||
Vector x(vx.GetData() + sc, sc);
|
||||
Vector dv_dt(dvx_dt.GetData() + 0, sc);
|
||||
Vector dx_dt(dvx_dt.GetData() + sc, sc);
|
||||
|
||||
H.Mult(x, z);
|
||||
if (viscosity != 0.0)
|
||||
{
|
||||
S.AddMult(v, z);
|
||||
}
|
||||
z.Neg(); // z = -z
|
||||
M_solver.Mult(z, dv_dt);
|
||||
|
||||
dx_dt = v;
|
||||
}
|
||||
|
||||
void HyperelasticOperator::ImplicitSolve(const real_t dt,
|
||||
const Vector &vx, Vector &dvx_dt)
|
||||
{
|
||||
int sc = height/2;
|
||||
Vector v(vx.GetData() + 0, sc);
|
||||
Vector x(vx.GetData() + sc, sc);
|
||||
Vector dv_dt(dvx_dt.GetData() + 0, sc);
|
||||
Vector dx_dt(dvx_dt.GetData() + sc, sc);
|
||||
|
||||
// By eliminating kx from the coupled system:
|
||||
// kv = -M^{-1}*[H(x + dt*kx) + S*(v + dt*kv)]
|
||||
// kx = v + dt*kv
|
||||
// we reduce it to a nonlinear equation for kv, represented by the
|
||||
// reduced_oper. This equation is solved with the newton_solver
|
||||
// object (using J_solver and J_prec internally).
|
||||
reduced_oper->SetParameters(dt, &v, &x);
|
||||
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
|
||||
newton_solver.Mult(zero, dv_dt);
|
||||
MFEM_VERIFY(newton_solver.GetConverged(), "Newton solver did not converge.");
|
||||
add(v, dt, dv_dt, dx_dt);
|
||||
}
|
||||
|
||||
real_t HyperelasticOperator::ElasticEnergy(const Vector &x) const
|
||||
{
|
||||
return H.GetEnergy(x);
|
||||
}
|
||||
|
||||
real_t HyperelasticOperator::KineticEnergy(const Vector &v) const
|
||||
{
|
||||
return 0.5*M.InnerProduct(v, v);
|
||||
}
|
||||
|
||||
void HyperelasticOperator::GetElasticEnergyDensity(
|
||||
const GridFunction &x, GridFunction &w, ProjectType proj_type) const
|
||||
{
|
||||
ElasticEnergyCoefficient w_coeff(*model, x);
|
||||
w.ProjectCoefficient(w_coeff, proj_type);
|
||||
}
|
||||
|
||||
HyperelasticOperator::~HyperelasticOperator()
|
||||
{
|
||||
delete J_solver;
|
||||
delete J_prec;
|
||||
delete reduced_oper;
|
||||
delete model;
|
||||
}
|
||||
|
||||
|
||||
real_t ElasticEnergyCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
model.SetTransformation(T);
|
||||
x.GetVectorGradient(T, J);
|
||||
// return model.EvalW(J); // in reference configuration
|
||||
return model.EvalW(J)/J.Det(); // in deformed configuration
|
||||
}
|
||||
|
||||
|
||||
void InitialDeformation(const Vector &x, Vector &y)
|
||||
{
|
||||
// set the initial configuration to be the same as the reference, stress
|
||||
// free, configuration
|
||||
y = x;
|
||||
}
|
||||
|
||||
void InitialVelocity(const Vector &x, Vector &v)
|
||||
{
|
||||
const int dim = x.Size();
|
||||
const real_t s = 0.1/64.;
|
||||
|
||||
v = 0.0;
|
||||
v(dim-1) = s*x(0)*x(0)*(8.0-x(0));
|
||||
v(0) = -s*x(0)*x(0);
|
||||
}
|
||||
@@ -0,0 +1,682 @@
|
||||
// MFEM Example 10 - Parallel NURBS Version
|
||||
//
|
||||
// Compile with: make nurbs_ex10p
|
||||
//
|
||||
// Sample runs:
|
||||
// mpirun -np 4 nurbs_ex10p -m ../../data/beam-quad-nurbs.mesh -s 23 -rs 2 -dt 3
|
||||
//
|
||||
// Description: This examples solves a time dependent nonlinear elasticity
|
||||
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
|
||||
// hyperelastic model and S is a viscosity operator of Laplacian
|
||||
// type. The geometry of the domain is assumed to be as follows:
|
||||
//
|
||||
// +---------------------+
|
||||
// boundary --->| |
|
||||
// attribute 1 | |
|
||||
// (fixed) +---------------------+
|
||||
//
|
||||
// The example demonstrates the use of nonlinear operators (the
|
||||
// class HyperelasticOperator defining H(x)), as well as their
|
||||
// implicit time integration using a Newton method for solving an
|
||||
// associated reduced backward-Euler type nonlinear equation
|
||||
// (class ReducedSystemOperator). Each Newton step requires the
|
||||
// inversion of a Jacobian matrix, which is done through a
|
||||
// (preconditioned) inner solver. Note that implementing the
|
||||
// method HyperelasticOperator::ImplicitSolve is the only
|
||||
// requirement for high-order implicit (SDIRK) time integration.
|
||||
//
|
||||
// We recommend viewing examples 2 and 9 before viewing this
|
||||
// example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
class ReducedSystemOperator;
|
||||
|
||||
/** After spatial discretization, the hyperelastic model can be written as a
|
||||
* system of ODEs:
|
||||
* dv/dt = -M^{-1}*(H(x) + S*v)
|
||||
* dx/dt = v,
|
||||
* where x is the vector representing the deformation, v is the velocity field,
|
||||
* M is the mass matrix, S is the viscosity matrix, and H(x) is the nonlinear
|
||||
* hyperelastic operator.
|
||||
*
|
||||
* Class HyperelasticOperator represents the right-hand side of the above
|
||||
* system of ODEs. */
|
||||
class HyperelasticOperator : public TimeDependentOperator
|
||||
{
|
||||
protected:
|
||||
ParFiniteElementSpace &fespace;
|
||||
Array<int> ess_tdof_list;
|
||||
|
||||
ParBilinearForm M, S;
|
||||
ParNonlinearForm H;
|
||||
real_t viscosity;
|
||||
HyperelasticModel *model;
|
||||
|
||||
HypreParMatrix *Mmat; // Mass matrix from ParallelAssemble()
|
||||
CGSolver M_solver; // Krylov solver for inverting the mass matrix M
|
||||
HypreSmoother M_prec; // Preconditioner for the mass matrix M
|
||||
|
||||
/** Nonlinear operator defining the reduced backward Euler equation for the
|
||||
velocity. Used in the implementation of method ImplicitSolve. */
|
||||
ReducedSystemOperator *reduced_oper;
|
||||
|
||||
/// Newton solver for the reduced backward Euler equation
|
||||
NewtonSolver newton_solver;
|
||||
|
||||
/// Solver for the Jacobian solve in the Newton method
|
||||
Solver *J_solver;
|
||||
/// Preconditioner for the Jacobian solve in the Newton method
|
||||
Solver *J_prec;
|
||||
|
||||
mutable Vector z; // auxiliary vector
|
||||
|
||||
public:
|
||||
HyperelasticOperator(ParFiniteElementSpace &f, Array<int> &ess_bdr,
|
||||
real_t visc, real_t mu, real_t K);
|
||||
|
||||
/// Compute the right-hand side of the ODE system.
|
||||
void Mult(const Vector &vx, Vector &dvx_dt) const override;
|
||||
/** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
|
||||
This is the only requirement for high-order SDIRK implicit integration.*/
|
||||
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
|
||||
|
||||
real_t ElasticEnergy(const ParGridFunction &x) const;
|
||||
real_t KineticEnergy(const ParGridFunction &v) const;
|
||||
void GetElasticEnergyDensity(const ParGridFunction &x,
|
||||
ParGridFunction &w,
|
||||
ProjectType proj_type) const;
|
||||
|
||||
~HyperelasticOperator() override;
|
||||
};
|
||||
|
||||
/** Nonlinear operator of the form:
|
||||
k --> (M + dt*S)*k + H(x + dt*v + dt^2*k) + S*v,
|
||||
where M and S are given BilinearForms, H is a given NonlinearForm, v and x
|
||||
are given vectors, and dt is a scalar. */
|
||||
class ReducedSystemOperator : public Operator
|
||||
{
|
||||
private:
|
||||
ParBilinearForm *M, *S;
|
||||
ParNonlinearForm *H;
|
||||
mutable HypreParMatrix *Jacobian;
|
||||
real_t dt;
|
||||
const Vector *v, *x;
|
||||
mutable Vector w, z;
|
||||
const Array<int> &ess_tdof_list;
|
||||
|
||||
public:
|
||||
ReducedSystemOperator(ParBilinearForm *M_, ParBilinearForm *S_,
|
||||
ParNonlinearForm *H_, const Array<int> &ess_tdof_list);
|
||||
|
||||
/// Set current dt, v, x values - needed to compute action and Jacobian.
|
||||
void SetParameters(real_t dt_, const Vector *v_, const Vector *x_);
|
||||
|
||||
/// Compute y = H(x + dt (v + dt k)) + M k + S (v + dt k).
|
||||
void Mult(const Vector &k, Vector &y) const override;
|
||||
|
||||
/// Compute J = M + dt S + dt^2 grad_H(x + dt (v + dt k)).
|
||||
Operator &GetGradient(const Vector &k) const override;
|
||||
|
||||
~ReducedSystemOperator() override;
|
||||
};
|
||||
|
||||
|
||||
/** Function representing the elastic energy density for the given hyperelastic
|
||||
model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
|
||||
class ElasticEnergyCoefficient : public Coefficient
|
||||
{
|
||||
private:
|
||||
HyperelasticModel &model;
|
||||
const ParGridFunction &x;
|
||||
DenseMatrix J;
|
||||
|
||||
public:
|
||||
ElasticEnergyCoefficient(HyperelasticModel &m, const ParGridFunction &x_)
|
||||
: model(m), x(x_) { }
|
||||
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
|
||||
~ElasticEnergyCoefficient() override { }
|
||||
};
|
||||
|
||||
void InitialDeformation(const Vector &x, Vector &y);
|
||||
|
||||
void InitialVelocity(const Vector &x, Vector &v);
|
||||
|
||||
void visualize(ostream &os, ParMesh *mesh,
|
||||
ParGridFunction *deformed_nodes,
|
||||
ParGridFunction *field, const char *field_name = NULL,
|
||||
bool init_vis = false);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI and HYPRE.
|
||||
Mpi::Init(argc, argv);
|
||||
int myid = Mpi::WorldRank();
|
||||
Hypre::Init();
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../../data/beam-quad-nurbs.mesh";
|
||||
int ser_ref_levels = 2;
|
||||
int par_ref_levels = 0;
|
||||
int order = 2;
|
||||
int ode_solver_type = 23;
|
||||
real_t t_final = 1.0;
|
||||
real_t dt = 0.1;
|
||||
real_t visc = 1e-2;
|
||||
real_t mu = 0.25;
|
||||
real_t K = 5.0;
|
||||
int proj_type_int = 0;
|
||||
bool adaptive_lin_rtol = true;
|
||||
bool visualization = true;
|
||||
int vis_steps = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
|
||||
"Number of times to refine the mesh uniformly in parallel.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
ODESolver::Types.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption(&visc, "-v", "--viscosity",
|
||||
"Viscosity coefficient.");
|
||||
args.AddOption(&mu, "-mu", "--shear-modulus",
|
||||
"Shear modulus in the Neo-Hookean hyperelastic model.");
|
||||
args.AddOption(&K, "-K", "--bulk-modulus",
|
||||
"Bulk modulus in the Neo-Hookean hyperelastic model.");
|
||||
args.AddOption(&proj_type_int, "-proj", "--projection",
|
||||
"Projection type:\n."
|
||||
" 0 = DEFAULT: ELEMENTL2 for NURBS elements, ELEMENT else.\n"
|
||||
" 1 = ELEMENT: As defined in the respective element.\n"
|
||||
" 2 = GLOBALL2: Global L2 projection.\n"
|
||||
" 3 = ELEMENTL2: Element L2 projection.");
|
||||
args.AddOption(&adaptive_lin_rtol, "-alrtol", "--adaptive-lin-rtol",
|
||||
"-no-alrtol", "--no-adaptive-lin-rtol",
|
||||
"Enable or disable adaptive linear solver rtol.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
|
||||
"Visualize every n-th timestep.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
ProjectType proj_type = static_cast<ProjectType>(proj_type_int);
|
||||
|
||||
// 3. Read the serial mesh from the given mesh file on all processors. We can
|
||||
// handle triangular, quadrilateral, tetrahedral and hexahedral meshes
|
||||
// with the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
// 5. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
// a command-line parameter.
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
for (int lev = 0; lev < par_ref_levels; lev++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 7. Define the parallel vector finite element spaces representing the mesh
|
||||
// deformation x_gf, the velocity v_gf, and the initial configuration,
|
||||
// x_ref. Define also the elastic energy density, w_gf, which is in a
|
||||
// discontinuous higher-order space. Since x and v are integrated in time
|
||||
// as a system, we group them together in block vector vx, on the unique
|
||||
// parallel degrees of freedom, with offsets given by array true_offset.
|
||||
FiniteElementCollection *fec = nullptr;
|
||||
NURBSExtension *NURBSext = nullptr;
|
||||
if (mesh->NURBSext)
|
||||
{
|
||||
NURBSext = new NURBSExtension(pmesh->NURBSext, order);
|
||||
fec = new NURBSFECollection(order);
|
||||
if (myid == 0) { cout << "Using NURBS FEs: " << fec->Name() << endl; }
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
if (myid == 0) { cout << "Using H1 FEs: " << fec->Name() << endl; }
|
||||
}
|
||||
ParFiniteElementSpace fespace(pmesh, NURBSext, fec, dim);
|
||||
|
||||
HYPRE_BigInt glob_size = fespace.GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Number of velocity/deformation unknowns: " << glob_size << endl;
|
||||
}
|
||||
int true_size = fespace.TrueVSize();
|
||||
Array<int> true_offset(3);
|
||||
true_offset[0] = 0;
|
||||
true_offset[1] = true_size;
|
||||
true_offset[2] = 2*true_size;
|
||||
|
||||
BlockVector vx(true_offset);
|
||||
ParGridFunction v_gf, x_gf;
|
||||
v_gf.MakeTRef(&fespace, vx, true_offset[0]);
|
||||
x_gf.MakeTRef(&fespace, vx, true_offset[1]);
|
||||
|
||||
ParGridFunction x_ref(&fespace);
|
||||
pmesh->GetNodes(x_ref);
|
||||
|
||||
L2_FECollection w_fec(order + 1, dim);
|
||||
ParFiniteElementSpace w_fespace(pmesh, &w_fec);
|
||||
ParGridFunction w_gf(&w_fespace);
|
||||
|
||||
// 8. Set the initial conditions for v_gf, x_gf and vx, and define the
|
||||
// boundary conditions on a beam-like mesh (see description above).
|
||||
VectorFunctionCoefficient velo(dim, InitialVelocity);
|
||||
v_gf.ProjectCoefficient(velo, proj_type);
|
||||
v_gf.SetTrueVector();
|
||||
VectorFunctionCoefficient deform(dim, InitialDeformation);
|
||||
x_gf.ProjectCoefficient(deform, proj_type);
|
||||
x_gf.SetTrueVector();
|
||||
|
||||
v_gf.SetFromTrueVector(); x_gf.SetFromTrueVector();
|
||||
|
||||
Array<int> ess_bdr(fespace.GetMesh()->bdr_attributes.Max());
|
||||
ess_bdr = 0;
|
||||
ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed
|
||||
|
||||
// 9. Initialize the hyperelastic operator, the GLVis visualization and print
|
||||
// the initial energies.
|
||||
HyperelasticOperator oper(fespace, ess_bdr, visc, mu, K);
|
||||
|
||||
socketstream vis_v, vis_w;
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
vis_v.open(vishost, visport);
|
||||
vis_v.precision(8);
|
||||
visualize(vis_v, pmesh, &x_gf, &v_gf, "Velocity", true);
|
||||
// Make sure all ranks have sent their 'v' solution before initiating
|
||||
// another set of GLVis connections (one from each rank):
|
||||
MPI_Barrier(pmesh->GetComm());
|
||||
vis_w.open(vishost, visport);
|
||||
if (vis_w)
|
||||
{
|
||||
oper.GetElasticEnergyDensity(x_gf, w_gf, proj_type);
|
||||
vis_w.precision(8);
|
||||
visualize(vis_w, pmesh, &x_gf, &w_gf, "Elastic energy density", true);
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "GLVis visualization paused."
|
||||
<< " Press space (in the GLVis window) to resume it.\n";
|
||||
}
|
||||
}
|
||||
|
||||
real_t ee0 = oper.ElasticEnergy(x_gf);
|
||||
real_t ke0 = oper.KineticEnergy(v_gf);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "initial elastic energy (EE) = " << ee0 << endl;
|
||||
cout << "initial kinetic energy (KE) = " << ke0 << endl;
|
||||
cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
|
||||
}
|
||||
|
||||
real_t t = 0.0;
|
||||
oper.SetTime(t);
|
||||
ode_solver->Init(oper);
|
||||
|
||||
// 10. Perform time-integration
|
||||
// (looping over the time iterations, ti, with a time-step dt).
|
||||
bool last_step = false;
|
||||
for (int ti = 1; !last_step; ti++)
|
||||
{
|
||||
real_t dt_real = min(dt, t_final - t);
|
||||
|
||||
ode_solver->Step(vx, t, dt_real);
|
||||
|
||||
last_step = (t >= t_final - 1e-8*dt);
|
||||
|
||||
if (last_step || (ti % vis_steps) == 0)
|
||||
{
|
||||
v_gf.SetFromTrueVector(); x_gf.SetFromTrueVector();
|
||||
|
||||
real_t ee = oper.ElasticEnergy(x_gf);
|
||||
real_t ke = oper.KineticEnergy(v_gf);
|
||||
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "step " << ti << ", t = " << t << ", EE = " << ee
|
||||
<< ", KE = " << ke << ", ΔTE = " << (ee+ke)-(ee0+ke0) << endl;
|
||||
}
|
||||
|
||||
if (visualization)
|
||||
{
|
||||
visualize(vis_v, pmesh, &x_gf, &v_gf);
|
||||
if (vis_w)
|
||||
{
|
||||
oper.GetElasticEnergyDensity(x_gf, w_gf, proj_type);
|
||||
visualize(vis_w, pmesh, &x_gf, &w_gf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Save the displaced mesh, the velocity and elastic energy.
|
||||
{
|
||||
v_gf.SetFromTrueVector(); x_gf.SetFromTrueVector();
|
||||
GridFunction *nodes = &x_gf;
|
||||
int owns_nodes = 0;
|
||||
pmesh->SwapNodes(nodes, owns_nodes);
|
||||
|
||||
ostringstream mesh_name, velo_name, ee_name;
|
||||
mesh_name << "deformed." << setfill('0') << setw(6) << myid;
|
||||
velo_name << "velocity." << setfill('0') << setw(6) << myid;
|
||||
ee_name << "elastic_energy." << setfill('0') << setw(6) << myid;
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
pmesh->Print(mesh_ofs);
|
||||
pmesh->SwapNodes(nodes, owns_nodes);
|
||||
ofstream velo_ofs(velo_name.str().c_str());
|
||||
velo_ofs.precision(8);
|
||||
v_gf.Save(velo_ofs);
|
||||
ofstream ee_ofs(ee_name.str().c_str());
|
||||
ee_ofs.precision(8);
|
||||
oper.GetElasticEnergyDensity(x_gf, w_gf, proj_type);
|
||||
w_gf.Save(ee_ofs);
|
||||
}
|
||||
|
||||
// 12. Free the used memory.
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void visualize(ostream &os, ParMesh *mesh,
|
||||
ParGridFunction *deformed_nodes,
|
||||
ParGridFunction *field, const char *field_name, bool init_vis)
|
||||
{
|
||||
if (!os)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GridFunction *nodes = deformed_nodes;
|
||||
int owns_nodes = 0;
|
||||
|
||||
mesh->SwapNodes(nodes, owns_nodes);
|
||||
|
||||
os << "parallel " << mesh->GetNRanks()
|
||||
<< " " << mesh->GetMyRank() << "\n";
|
||||
os << "solution\n" << *mesh << *field;
|
||||
|
||||
mesh->SwapNodes(nodes, owns_nodes);
|
||||
|
||||
if (init_vis)
|
||||
{
|
||||
os << "window_size 800 800\n";
|
||||
os << "window_title '" << field_name << "'\n";
|
||||
if (mesh->SpaceDimension() == 2)
|
||||
{
|
||||
os << "view 0 0\n"; // view from top
|
||||
os << "keys jl\n"; // turn off perspective and light
|
||||
}
|
||||
os << "keys cm\n"; // show colorbar and mesh
|
||||
// update value-range; keep mesh-extents fixed
|
||||
os << "autoscale value\n";
|
||||
os << "pause\n";
|
||||
}
|
||||
os << flush;
|
||||
}
|
||||
|
||||
|
||||
ReducedSystemOperator::ReducedSystemOperator(
|
||||
ParBilinearForm *M_, ParBilinearForm *S_, ParNonlinearForm *H_,
|
||||
const Array<int> &ess_tdof_list_)
|
||||
: Operator(M_->ParFESpace()->TrueVSize()), M(M_), S(S_), H(H_),
|
||||
Jacobian(NULL), dt(0.0), v(NULL), x(NULL), w(height), z(height),
|
||||
ess_tdof_list(ess_tdof_list_)
|
||||
{ }
|
||||
|
||||
void ReducedSystemOperator::SetParameters(real_t dt_, const Vector *v_,
|
||||
const Vector *x_)
|
||||
{
|
||||
dt = dt_; v = v_; x = x_;
|
||||
}
|
||||
|
||||
void ReducedSystemOperator::Mult(const Vector &k, Vector &y) const
|
||||
{
|
||||
// compute: y = H(x + dt*(v + dt*k)) + M*k + S*(v + dt*k)
|
||||
add(*v, dt, k, w);
|
||||
add(*x, dt, w, z);
|
||||
H->Mult(z, y);
|
||||
M->TrueAddMult(k, y);
|
||||
S->TrueAddMult(w, y);
|
||||
y.SetSubVector(ess_tdof_list, 0.0);
|
||||
}
|
||||
|
||||
Operator &ReducedSystemOperator::GetGradient(const Vector &k) const
|
||||
{
|
||||
delete Jacobian;
|
||||
SparseMatrix *localJ = Add(1.0, M->SpMat(), dt, S->SpMat());
|
||||
add(*v, dt, k, w);
|
||||
add(*x, dt, w, z);
|
||||
localJ->Add(dt*dt, H->GetLocalGradient(z));
|
||||
Jacobian = M->ParallelAssemble(localJ);
|
||||
delete localJ;
|
||||
HypreParMatrix *Je = Jacobian->EliminateRowsCols(ess_tdof_list);
|
||||
delete Je;
|
||||
return *Jacobian;
|
||||
}
|
||||
|
||||
ReducedSystemOperator::~ReducedSystemOperator()
|
||||
{
|
||||
delete Jacobian;
|
||||
}
|
||||
|
||||
|
||||
HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
|
||||
Array<int> &ess_bdr, real_t visc,
|
||||
real_t mu, real_t K)
|
||||
: TimeDependentOperator(2*f.TrueVSize(), (real_t) 0.0), fespace(f),
|
||||
M(&fespace), S(&fespace), H(&fespace),
|
||||
viscosity(visc), M_solver(f.GetComm()), newton_solver(f.GetComm()),
|
||||
z(height/2)
|
||||
{
|
||||
#if defined(MFEM_USE_DOUBLE)
|
||||
const real_t rel_tol = 1e-8;
|
||||
const real_t newton_abs_tol = 0.0;
|
||||
#elif defined(MFEM_USE_SINGLE)
|
||||
const real_t rel_tol = 1e-3;
|
||||
const real_t newton_abs_tol = 1e-4;
|
||||
#else
|
||||
#error "Only single and double precision are supported!"
|
||||
const real_t rel_tol = real_t(1);
|
||||
const real_t newton_abs_tol = real_t(0);
|
||||
#endif
|
||||
const int skip_zero_entries = 0;
|
||||
|
||||
const real_t ref_density = 1.0; // density in the reference configuration
|
||||
ConstantCoefficient rho0(ref_density);
|
||||
M.AddDomainIntegrator(new VectorMassIntegrator(rho0));
|
||||
M.Assemble(skip_zero_entries);
|
||||
M.Finalize(skip_zero_entries);
|
||||
Mmat = M.ParallelAssemble();
|
||||
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
HypreParMatrix *Me = Mmat->EliminateRowsCols(ess_tdof_list);
|
||||
delete Me;
|
||||
|
||||
M_solver.iterative_mode = false;
|
||||
M_solver.SetRelTol(rel_tol);
|
||||
M_solver.SetAbsTol(0.0);
|
||||
M_solver.SetMaxIter(30);
|
||||
M_solver.SetPrintLevel(0);
|
||||
M_prec.SetType(HypreSmoother::Jacobi);
|
||||
M_solver.SetPreconditioner(M_prec);
|
||||
M_solver.SetOperator(*Mmat);
|
||||
|
||||
model = new NeoHookeanModel(mu, K);
|
||||
H.AddDomainIntegrator(new HyperelasticNLFIntegrator(model));
|
||||
H.SetEssentialTrueDofs(ess_tdof_list);
|
||||
|
||||
ConstantCoefficient visc_coeff(viscosity);
|
||||
S.AddDomainIntegrator(new VectorDiffusionIntegrator(visc_coeff));
|
||||
S.Assemble(skip_zero_entries);
|
||||
S.Finalize(skip_zero_entries);
|
||||
|
||||
reduced_oper = new ReducedSystemOperator(&M, &S, &H, ess_tdof_list);
|
||||
|
||||
HypreSmoother *J_hypreSmoother = new HypreSmoother;
|
||||
J_hypreSmoother->SetType(HypreSmoother::l1Jacobi);
|
||||
J_hypreSmoother->SetPositiveDiagonal(true);
|
||||
J_prec = J_hypreSmoother;
|
||||
|
||||
MINRESSolver *J_minres = new MINRESSolver(f.GetComm());
|
||||
J_minres->SetRelTol(rel_tol);
|
||||
J_minres->SetAbsTol(0.0);
|
||||
J_minres->SetMaxIter(300);
|
||||
J_minres->SetPrintLevel(-1);
|
||||
J_minres->SetPreconditioner(*J_prec);
|
||||
J_solver = J_minres;
|
||||
|
||||
newton_solver.iterative_mode = false;
|
||||
newton_solver.SetSolver(*J_solver);
|
||||
newton_solver.SetOperator(*reduced_oper);
|
||||
newton_solver.SetPrintLevel(1); // print Newton iterations
|
||||
newton_solver.SetRelTol(rel_tol);
|
||||
newton_solver.SetAbsTol(newton_abs_tol);
|
||||
newton_solver.SetAdaptiveLinRtol(2, 0.5, 0.9);
|
||||
newton_solver.SetMaxIter(10);
|
||||
}
|
||||
|
||||
void HyperelasticOperator::Mult(const Vector &vx, Vector &dvx_dt) const
|
||||
{
|
||||
// Create views to the sub-vectors v, x of vx, and dv_dt, dx_dt of dvx_dt
|
||||
int sc = height/2;
|
||||
Vector v(vx.GetData() + 0, sc);
|
||||
Vector x(vx.GetData() + sc, sc);
|
||||
Vector dv_dt(dvx_dt.GetData() + 0, sc);
|
||||
Vector dx_dt(dvx_dt.GetData() + sc, sc);
|
||||
|
||||
H.Mult(x, z);
|
||||
if (viscosity != 0.0)
|
||||
{
|
||||
S.TrueAddMult(v, z);
|
||||
z.SetSubVector(ess_tdof_list, 0.0);
|
||||
}
|
||||
z.Neg(); // z = -z
|
||||
M_solver.Mult(z, dv_dt);
|
||||
|
||||
dx_dt = v;
|
||||
}
|
||||
|
||||
void HyperelasticOperator::ImplicitSolve(const real_t dt,
|
||||
const Vector &vx, Vector &dvx_dt)
|
||||
{
|
||||
int sc = height/2;
|
||||
Vector v(vx.GetData() + 0, sc);
|
||||
Vector x(vx.GetData() + sc, sc);
|
||||
Vector dv_dt(dvx_dt.GetData() + 0, sc);
|
||||
Vector dx_dt(dvx_dt.GetData() + sc, sc);
|
||||
|
||||
// By eliminating kx from the coupled system:
|
||||
// kv = -M^{-1}*[H(x + dt*kx) + S*(v + dt*kv)]
|
||||
// kx = v + dt*kv
|
||||
// we reduce it to a nonlinear equation for kv, represented by the
|
||||
// reduced_oper. This equation is solved with the newton_solver
|
||||
// object (using J_solver and J_prec internally).
|
||||
reduced_oper->SetParameters(dt, &v, &x);
|
||||
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
|
||||
newton_solver.Mult(zero, dv_dt);
|
||||
MFEM_VERIFY(newton_solver.GetConverged(), "Newton solver did not converge.");
|
||||
add(v, dt, dv_dt, dx_dt);
|
||||
}
|
||||
|
||||
real_t HyperelasticOperator::ElasticEnergy(const ParGridFunction &x) const
|
||||
{
|
||||
return H.GetEnergy(x);
|
||||
}
|
||||
|
||||
real_t HyperelasticOperator::KineticEnergy(const ParGridFunction &v) const
|
||||
{
|
||||
real_t energy = 0.5*M.ParInnerProduct(v, v);
|
||||
return energy;
|
||||
}
|
||||
|
||||
void HyperelasticOperator::GetElasticEnergyDensity(
|
||||
const ParGridFunction &x, ParGridFunction &w, ProjectType proj_type) const
|
||||
{
|
||||
ElasticEnergyCoefficient w_coeff(*model, x);
|
||||
w.ProjectCoefficient(w_coeff, proj_type);
|
||||
}
|
||||
|
||||
HyperelasticOperator::~HyperelasticOperator()
|
||||
{
|
||||
delete J_solver;
|
||||
delete J_prec;
|
||||
delete reduced_oper;
|
||||
delete model;
|
||||
delete Mmat;
|
||||
}
|
||||
|
||||
|
||||
real_t ElasticEnergyCoefficient::Eval(ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
model.SetTransformation(T);
|
||||
x.GetVectorGradient(T, J);
|
||||
// return model.EvalW(J); // in reference configuration
|
||||
return model.EvalW(J)/J.Det(); // in deformed configuration
|
||||
}
|
||||
|
||||
|
||||
void InitialDeformation(const Vector &x, Vector &y)
|
||||
{
|
||||
// set the initial configuration to be the same as the reference, stress
|
||||
// free, configuration
|
||||
y = x;
|
||||
}
|
||||
|
||||
void InitialVelocity(const Vector &x, Vector &v)
|
||||
{
|
||||
const int dim = x.Size();
|
||||
const real_t s = 0.1/64.;
|
||||
|
||||
v = 0.0;
|
||||
v(dim-1) = s*x(0)*x(0)*(8.0-x(0));
|
||||
v(0) = -s*x(0)*x(0);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// MFEM Example 24 -- modified for NURBS FE
|
||||
// MFEM Example 24 -- modified for NURBS FE
|
||||
//
|
||||
// Compile with: make nurbs_ex24
|
||||
//
|
||||
@@ -43,6 +43,8 @@ void gradp_exact(const Vector &, Vector &);
|
||||
real_t div_gradp_exact(const Vector &x);
|
||||
void v_exact(const Vector &x, Vector &v);
|
||||
void curlv_exact(const Vector &x, Vector &cv);
|
||||
template <typename CoefficientType>
|
||||
void Project(GridFunction &gf, CoefficientType &coef, int proj_type);
|
||||
|
||||
int dim;
|
||||
real_t freq = 1.0, kappa;
|
||||
@@ -57,6 +59,7 @@ int main(int argc, char *argv[])
|
||||
int prob = 0;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
int proj_type_int = 0;
|
||||
const char *device_config = "cpu";
|
||||
int visport = 19916;
|
||||
bool visualization = 1;
|
||||
@@ -68,14 +71,20 @@ int main(int argc, char *argv[])
|
||||
"Number of times to refine the mesh uniformly, -1 for auto.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree).");
|
||||
args.AddOption(&NURBS, "-n", "--nurbs", "-nn","--no-nurbs",
|
||||
"NURBS.");
|
||||
args.AddOption(&NURBS, "-n", "--nurbs", "-nn", "--no-nurbs",
|
||||
"Use NURBS spaces if the mesh is a NURBS mesh.");
|
||||
args.AddOption(&prob, "-p", "--problem-type",
|
||||
"Choose between 0: grad, 1: curl, 2: div");
|
||||
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
|
||||
"--no-static-condensation", "Enable static condensation.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&proj_type_int, "-proj", "--projection",
|
||||
"Projection type:\n."
|
||||
" 0 = DEFAULT: ELEMENTL2 for NURBS elements, ELEMENT else.\n"
|
||||
" 1 = ELEMENT: As defined in the respective element.\n"
|
||||
" 2 = GLOBALL2: Global L2 projection.\n"
|
||||
" 3 = ELEMENTL2: Element L2 projection.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -86,10 +95,11 @@ int main(int argc, char *argv[])
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
args.PrintUsage(mfem::out);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
args.PrintOptions(mfem::out);
|
||||
ProjectType proj_type = static_cast<ProjectType>(proj_type_int);
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 2. Enable hardware devices such as GPUs, and programming models such as
|
||||
@@ -102,7 +112,7 @@ int main(int argc, char *argv[])
|
||||
// the same code.
|
||||
Mesh *mesh = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
if ((prob == 1) &&(dim != 3))
|
||||
if ((prob == 1) && (dim != 3))
|
||||
{
|
||||
MFEM_ABORT("The curl problem is only defined in 3D.");
|
||||
}
|
||||
@@ -123,17 +133,17 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use Nedelec or
|
||||
// Raviart-Thomas finite elements of the specified order.
|
||||
// 5. Define a finite element space on the mesh. Here we use H^1, H(Div) or
|
||||
// H(Curl) finite elements of the specified order.
|
||||
FiniteElementCollection *trial_fec = nullptr;
|
||||
FiniteElementCollection *test_fec = nullptr;
|
||||
NURBSExtension *NURBSext = nullptr;
|
||||
if (mesh->NURBSext && NURBS)
|
||||
{
|
||||
NURBSext = new NURBSExtension(mesh->NURBSext, order);
|
||||
NURBSext = new NURBSExtension(mesh->NURBSext, order);
|
||||
if (prob == 0)
|
||||
{
|
||||
trial_fec = new NURBSFECollection(order);
|
||||
trial_fec = new NURBSFECollection(order);
|
||||
test_fec = new NURBS_HCurlFECollection(order, dim);
|
||||
}
|
||||
else if (prob == 1)
|
||||
@@ -146,7 +156,7 @@ int main(int argc, char *argv[])
|
||||
trial_fec = new NURBS_HDivFECollection(order, dim);
|
||||
test_fec = new NURBSFECollection(order);
|
||||
}
|
||||
mfem::out<<"Create NURBS fec and ext"<<std::endl;
|
||||
mfem::out << "Create NURBS finite element" << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -165,6 +175,7 @@ int main(int argc, char *argv[])
|
||||
trial_fec = new RT_FECollection(order-1, dim);
|
||||
test_fec = new L2_FECollection(order-1, dim);
|
||||
}
|
||||
mfem::out << "Create standard finite elements" << endl;
|
||||
}
|
||||
|
||||
FiniteElementSpace trial_fes(mesh, NURBSext, trial_fec);
|
||||
@@ -175,20 +186,20 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (prob == 0)
|
||||
{
|
||||
cout << "Number of Nedelec finite element unknowns: " << test_size << endl;
|
||||
cout << "Number of H1 finite element unknowns: " << trial_size << endl;
|
||||
mfem::out << "Number of HCurl finite element unknowns: " << test_size << endl;
|
||||
mfem::out << "Number of H1 finite element unknowns: " << trial_size << endl;
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
cout << "Number of Nedelec finite element unknowns: " << trial_size << endl;
|
||||
cout << "Number of Raviart-Thomas finite element unknowns: " << test_size <<
|
||||
endl;
|
||||
mfem::out << "Number of HCurl finite element unknowns: " << trial_size << endl;
|
||||
mfem::out << "Number of HDiv finite element unknowns: " << test_size
|
||||
<< endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "Number of Raviart-Thomas finite element unknowns: "
|
||||
<< trial_size << endl;
|
||||
cout << "Number of L2 finite element unknowns: " << test_size << endl;
|
||||
mfem::out << "Number of HDiv finite element unknowns: "
|
||||
<< trial_size << endl;
|
||||
mfem::out << "Number of L2 finite element unknowns: " << test_size << endl;
|
||||
}
|
||||
|
||||
// 6. Define the solution vector as a finite element grid function
|
||||
@@ -204,17 +215,16 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (prob == 0)
|
||||
{
|
||||
gftrial.ProjectCoefficient(p_coef);
|
||||
gftrial.ProjectCoefficient(p_coef, proj_type);
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
gftrial.ProjectCoefficient(v_coef);
|
||||
gftrial.ProjectCoefficient(v_coef, proj_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
gftrial.ProjectCoefficient(gradp_coef);
|
||||
gftrial.ProjectCoefficient(gradp_coef, proj_type);
|
||||
}
|
||||
|
||||
gftrial.SetTrueVector();
|
||||
gftrial.SetFromTrueVector();
|
||||
|
||||
@@ -293,6 +303,7 @@ int main(int argc, char *argv[])
|
||||
cg.SetOperator(Amat);
|
||||
cg.SetPreconditioner(Jacobi);
|
||||
cg.Mult(rhs, x);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,17 +311,16 @@ int main(int argc, char *argv[])
|
||||
GridFunction exact_proj(&test_fes);
|
||||
if (prob == 0)
|
||||
{
|
||||
exact_proj.ProjectCoefficient(gradp_coef);
|
||||
exact_proj.ProjectCoefficient(gradp_coef, proj_type);
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
exact_proj.ProjectCoefficient(curlv_coef);
|
||||
exact_proj.ProjectCoefficient(curlv_coef, proj_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
exact_proj.ProjectCoefficient(divgradp_coef);
|
||||
exact_proj.ProjectCoefficient(divgradp_coef, proj_type);
|
||||
}
|
||||
|
||||
exact_proj.SetTrueVector();
|
||||
exact_proj.SetFromTrueVector();
|
||||
|
||||
@@ -320,20 +330,20 @@ int main(int argc, char *argv[])
|
||||
real_t errSol = x.ComputeL2Error(gradp_coef);
|
||||
real_t errProj = exact_proj.ComputeL2Error(gradp_coef);
|
||||
|
||||
cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl): "
|
||||
"|| E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
|
||||
cout << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
mfem::out << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl): "
|
||||
"|| E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
|
||||
mfem::out << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
}
|
||||
else if (prob == 1)
|
||||
{
|
||||
real_t errSol = x.ComputeL2Error(curlv_coef);
|
||||
real_t errProj = exact_proj.ComputeL2Error(curlv_coef);
|
||||
|
||||
cout << "\n Solution of (E_h,w) = (curl v_h,w) for E_h and w in H(div): "
|
||||
"|| E_h - curl v ||_{L_2} = " << errSol << '\n' << endl;
|
||||
cout << " Projection E_h of exact curl v in H(div): || E_h - curl v "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
mfem::out << "\n Solution of (E_h,w) = (curl v_h,w) for E_h and w in H(div): "
|
||||
"|| E_h - curl v ||_{L_2} = " << errSol << '\n' << endl;
|
||||
mfem::out << " Projection E_h of exact curl v in H(div): || E_h - curl v "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -347,11 +357,11 @@ int main(int argc, char *argv[])
|
||||
real_t errSol = x.ComputeL2Error(divgradp_coef, irs);
|
||||
real_t errProj = exact_proj.ComputeL2Error(divgradp_coef, irs);
|
||||
|
||||
cout << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
|
||||
"|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
|
||||
mfem::out << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
|
||||
"|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
|
||||
|
||||
cout << " Projection f_h of exact div v in L_2: || f_h - div v "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
mfem::out << " Projection f_h of exact div v in L_2: || f_h - div v "
|
||||
"||_{L_2} = " << errProj << '\n' << endl;
|
||||
}
|
||||
|
||||
// 12. Save the refined mesh and the solution. This output can be viewed
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
// MFEM Print info of NURBS mesh
|
||||
//
|
||||
// Compile with: make nurbs_mesh_info
|
||||
//
|
||||
// Sample runs:
|
||||
// nurbs_mesh_info -m ../../data/cube-nurbs.mesh -o 0 -r 2
|
||||
//
|
||||
// Description: This code prints detailed mesh information such as:
|
||||
// - Print separate patch info
|
||||
// - 1D shape functions associated knot vectors
|
||||
// - Give Greville, Botella and Demko points of the knot vectors
|
||||
|
||||
#include <iostream>
|
||||
#include "mfem.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Read parameters from command line
|
||||
const char *mesh_file = "../../data/square-nurbs.mesh";
|
||||
const char *ref_file = "";
|
||||
int ref_levels = -1;
|
||||
int order = 1;
|
||||
bool visualization = true;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&ref_levels, "-r", "--refine",
|
||||
"Number of times to refine the mesh uniformly, -1 for auto.");
|
||||
args.AddOption(&ref_file, "-rf", "--ref-file",
|
||||
"File with refinement data");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"NURBS order (polynomial degree) or -1 for");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization."); // Dummy arg for `make test`
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read the mesh
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
NURBSExtension *ext = mesh.NURBSext;
|
||||
|
||||
if (!ext)
|
||||
{
|
||||
mfem_error("Mesh is not a NURBS mesh.");
|
||||
}
|
||||
|
||||
// Refine the mesh as specified
|
||||
mesh.DegreeElevate(16, order);
|
||||
|
||||
if (mesh.NURBSext && (strlen(ref_file) != 0))
|
||||
{
|
||||
mesh.RefineNURBSFromFile(ref_file);
|
||||
}
|
||||
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
// Print mesh info
|
||||
mesh.PrintInfo();
|
||||
|
||||
// Print patch info
|
||||
mfem::out<<"=======================================;"<<endl;
|
||||
mfem::out<<" Patch info"<<endl;
|
||||
mfem::out<<"=======================================;"<<endl;
|
||||
for (int p = 0; p < ext->GetNP(); p++)
|
||||
{
|
||||
Array<const KnotVector *> kv;
|
||||
ext->GetPatchKnotVectors(p, kv);
|
||||
|
||||
mfem::out<<p<<": Order = "<<kv[0]->GetOrder();
|
||||
for (int k = 1; k < kv.Size(); k++)
|
||||
{
|
||||
mfem::out<<"x"<<kv[k]->GetOrder();
|
||||
}
|
||||
mfem::out<<" : DOFs = "<<kv[0]->GetNCP();
|
||||
for (int k = 1; k < kv.Size(); k++)
|
||||
{
|
||||
mfem::out<<"x"<<kv[k]->GetNCP();
|
||||
}
|
||||
mfem::out<<endl;
|
||||
}
|
||||
|
||||
// Print knotvector info
|
||||
for (int k = 0; k < ext->GetNKV() ; k++)
|
||||
{
|
||||
mfem::out<<"=======================================;"<<endl;
|
||||
mfem::out<<" KnotVector "<<k<<endl;
|
||||
mfem::out<<"=======================================;"<<endl;
|
||||
const KnotVector &kv = *ext->GetKnotVector(k);
|
||||
mfem::out<<"Knotvector : "; kv.Print(mfem::out);
|
||||
|
||||
std::string gnuplot = "plot 0";
|
||||
Vector a(kv.GetNCP());
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
a = 0.0;
|
||||
a[i] = 1.0;
|
||||
std::string filename = "k" + std::to_string(k) +"_n" + std::to_string(
|
||||
i) + ".dat";
|
||||
mfem::out<<"Write shape function to: "<<filename<<"\n";
|
||||
std::ofstream ofs(filename);
|
||||
kv.PrintFunction(ofs, a, 201);
|
||||
ofs.close();
|
||||
gnuplot += ", '" + filename+"' u 1:2 w l";
|
||||
}
|
||||
mfem::out<<gnuplot<<endl;
|
||||
|
||||
// Greville
|
||||
Vector greville(kv.GetNCP());
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
greville[i] = kv.GetGreville(i);
|
||||
}
|
||||
mfem::out<<"Greville points : "; greville.Print(mfem::out, 32);
|
||||
|
||||
// Botella
|
||||
Vector botella(kv.GetNCP());
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
botella[i] = kv.GetBotella(i);
|
||||
}
|
||||
mfem::out<<"Botella points : "; botella.Print(mfem::out, 32);
|
||||
|
||||
// Demko
|
||||
Vector demko(kv.GetNCP());
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
demko[i] = kv.GetDemko(i);
|
||||
}
|
||||
mfem::out<<"Demko points : "; demko.Print(mfem::out, 32);
|
||||
|
||||
// Chebyshev spline
|
||||
Vector x(kv.GetNCP());
|
||||
for ( int i = 0; i <kv.GetNCP(); i++)
|
||||
{
|
||||
x[i] = std::pow(-1.0, i);
|
||||
}
|
||||
kv.GetInterpolant(x, demko, a);
|
||||
mfem::out<<"Chebyshev spline coeff : "; a.Print(mfem::out, 32);
|
||||
|
||||
std::string filename = "k" + std::to_string(k) +"_cheby.dat";
|
||||
mfem::out<<"Write Chebyshev spline to: "<<filename<<"\n";
|
||||
std::ofstream ofs(filename);
|
||||
kv.PrintFunction(ofs, a, 201);
|
||||
ofs.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -88,7 +88,7 @@ public:
|
||||
// with an even number of control points. These may be streamlined in the
|
||||
// future.
|
||||
void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
|
||||
Array<Vector*> &xy);
|
||||
Vector &x, Vector &y);
|
||||
|
||||
// Function that returns a uniform knot vector based on the @a order and the
|
||||
// number of control points @a ncp.
|
||||
@@ -221,12 +221,8 @@ int main(int argc, char *argv[])
|
||||
unique_ptr<KnotVector> kv_o1 = UniformKnotVector(1, 2);
|
||||
unique_ptr<KnotVector> kv_o2 = UniformKnotVector(2, 3);
|
||||
|
||||
|
||||
// Variables required for curve interpolation
|
||||
Vector xi_args, u_args;
|
||||
Array<int> i_args;
|
||||
Array<Vector*> xyf(2);
|
||||
xyf[0] = new Vector();
|
||||
xyf[1] = new Vector();
|
||||
|
||||
// 3. Create required (variables for) curves: foil_section and flair
|
||||
const NACA4 foil_section(foil_thickness, foil_length);
|
||||
@@ -301,21 +297,32 @@ int main(int argc, char *argv[])
|
||||
patch1.KnotInsert(0, *kv1);
|
||||
|
||||
int ncp = kv1->GetNCP();
|
||||
xyf[0]->SetSize(ncp); xyf[1]->SetSize(ncp);
|
||||
|
||||
// Project foil
|
||||
kv1->FindMaxima(i_args,xi_args, u_args);
|
||||
// We locate the control points at the location of the maxima of the
|
||||
// shapefunctions defined by the knot vectors -- the Botella points.
|
||||
Vector u(ncp),x(ncp),y(ncp),interp(ncp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
(*xyf[0])[i] = foil_length*(1.0 - tail_fraction*u_args[i]);
|
||||
(*xyf[1])[i] = -foil_section.y((*xyf[0])[i]);
|
||||
u[i] = kv1->GetBotella(i);
|
||||
}
|
||||
|
||||
kv1->FindInterpolant(xyf);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch1(i,0,0) = (*xyf[0])[i];
|
||||
patch1(i,0,1) = (*xyf[1])[i];
|
||||
x[i] = foil_length*(1.0 - tail_fraction*u[i]);
|
||||
}
|
||||
kv1->GetInterpolant(x,u,interp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch1(i,0,0) = interp[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
y[i] = -foil_section.y(x[i]);
|
||||
}
|
||||
kv1->GetInterpolant(y,u,interp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch1(i,0,1) = interp[i];
|
||||
}
|
||||
|
||||
patch1.DegreeElevate(1, order-1);
|
||||
@@ -368,17 +375,24 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Project foil
|
||||
int ncp = kv2->GetNCP();
|
||||
xyf[0]->SetSize(ncp); xyf[1]->SetSize(ncp);
|
||||
Vector x(ncp), y(ncp);
|
||||
|
||||
GetTipXY(foil_section, *kv2, tip_fraction, xyf);
|
||||
GetTipXY(foil_section, *kv2, tip_fraction,x,y);
|
||||
|
||||
kv2->FindInterpolant(xyf);
|
||||
Vector u(ncp),interp(ncp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
// Also deal with non-uniform weights here: convert to homogeneous
|
||||
// coordinates
|
||||
patch2(i,0,0) = (*xyf[0])[i]*patch2(i,0,2);
|
||||
patch2(i,0,1) = (*xyf[1])[i]*patch2(i,0,2);
|
||||
u[i] = kv2->GetBotella(i);
|
||||
}
|
||||
kv2->GetInterpolant(x,u,interp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch2(i,0,0) = interp[i]*patch2(i,0,2);
|
||||
}
|
||||
kv2->GetInterpolant(y,u,interp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch2(i,0,1) = interp[i]*patch2(i,0,2);
|
||||
}
|
||||
|
||||
// Project circle
|
||||
@@ -413,21 +427,31 @@ int main(int argc, char *argv[])
|
||||
patch3.KnotInsert(0, *kv3);
|
||||
|
||||
int ncp = kv3->GetNCP();
|
||||
xyf[0]->SetSize(ncp); xyf[1]->SetSize(ncp);
|
||||
|
||||
// Project foil
|
||||
kv3->FindMaxima(i_args,xi_args, u_args);
|
||||
Vector u(ncp),x(ncp),y(ncp),interp(ncp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
(*xyf[0])[i] = foil_length*(tip_fraction + tail_fraction*u_args[i]);
|
||||
(*xyf[1])[i] = foil_section.y((*xyf[0])[i]);
|
||||
u[i] = kv3->GetBotella(i);
|
||||
}
|
||||
|
||||
kv3->FindInterpolant(xyf);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch3(i,0,0) = (*xyf[0])[i];
|
||||
patch3(i,0,1) = (*xyf[1])[i];
|
||||
x[i] = foil_length*(tip_fraction + tail_fraction*u[i]);
|
||||
}
|
||||
kv3->GetInterpolant(x,u,interp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch3(i,0,0) = interp[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
y[i] = foil_section.y(x[i]);
|
||||
}
|
||||
kv3->GetInterpolant(y,u,interp);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
patch3(i,0,1) = interp[i];
|
||||
}
|
||||
|
||||
patch3.DegreeElevate(1, order-1);
|
||||
@@ -557,8 +581,6 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Close
|
||||
output.close();
|
||||
delete xyf[0];
|
||||
delete xyf[1];
|
||||
|
||||
cout << endl << "Boundary identifiers:" << endl;
|
||||
cout << " 1 Bottom" << endl;
|
||||
@@ -657,16 +679,22 @@ real_t NACA4::xl(real_t l) const
|
||||
}
|
||||
|
||||
void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
|
||||
Array<Vector*> &xy)
|
||||
Vector &x,Vector &y)
|
||||
{
|
||||
int ncp = kv.GetNCP();
|
||||
// Length of half the curve: the boundary covers both sides of the tip
|
||||
const real_t l = foil_section.len(tf * foil_section.GetChord());
|
||||
|
||||
// Find location of maxima of knot vector
|
||||
Array<int> i_args;
|
||||
Vector xi_args, u_args;
|
||||
kv.FindMaxima(i_args,xi_args, u_args);
|
||||
Array<int> i_args(ncp);
|
||||
Vector xi_args(ncp), u_args(ncp);
|
||||
// kv.FindMaxima(i_args,xi_args, u_args);
|
||||
for (int i = 0; i < ncp; i++)
|
||||
{
|
||||
u_args[i] = kv.GetBotella(i);
|
||||
i_args[i] = kv.GetSpan(u_args[i]) - kv.GetOrder();
|
||||
xi_args[i] = kv.GetRefPoint(u_args[i],i_args[i]+kv.GetOrder());
|
||||
}
|
||||
|
||||
// We have two cases: one with an odd number of control points and one
|
||||
// with an even number of control points.
|
||||
@@ -684,17 +712,16 @@ void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
|
||||
}
|
||||
|
||||
// Find corresponding xy vector
|
||||
xy[0]->SetSize(2*n+1); xy[1]->SetSize(2*n+1);
|
||||
xy[0]->Elem(n) = 0; xy[1]->Elem(n) = 0; // Foil section tip
|
||||
x[n] = 0; y[n] = 0; // Foil section tip
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
// Lower half
|
||||
xy[0]->Elem(i) = xcp[n-i];
|
||||
xy[1]->Elem(i) = -foil_section.y(xcp[n-i]);
|
||||
x[i] = xcp[n-i];
|
||||
y[i] = -foil_section.y(xcp[n-i]);
|
||||
|
||||
// Upper half
|
||||
xy[0]->Elem(n+1+i) = xcp[i+1];
|
||||
xy[1]->Elem(n+1+i) = foil_section.y(xcp[i+1]);
|
||||
x[n+1+i] = xcp[i+1];
|
||||
y[n+1+i] = foil_section.y(xcp[i+1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -708,18 +735,16 @@ void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
|
||||
real_t lcp = u * l;
|
||||
xcp[i] = foil_section.xl(lcp);
|
||||
}
|
||||
|
||||
// Find corresponding xy vector
|
||||
xy[0]->SetSize(2*n); xy[1]->SetSize(2*n);
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
// Lower half
|
||||
xy[0]->Elem(i) = xcp[n-1-i];
|
||||
xy[1]->Elem(i) = -foil_section.y(xcp[n-1-i]);
|
||||
x[i] = xcp[n-1-i];
|
||||
y[i] = -foil_section.y(xcp[n-1-i]);
|
||||
|
||||
// Upper half
|
||||
xy[0]->Elem(n+i) = xcp[i];
|
||||
xy[1]->Elem(n+i) = foil_section.y(xcp[i]);
|
||||
x[n+i] = xcp[i];
|
||||
y[n+i] = foil_section.y(xcp[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -458,11 +458,9 @@ SurfaceInterpolator::SurfaceInterpolator(int num_elem_x, int num_elem_y,
|
||||
hy = 1.0 / (real_t) (ncp[1] - 1);
|
||||
hz = 1.0 / (real_t) (ncp[2] - 1);
|
||||
|
||||
Vector xi_args;
|
||||
Array<int> i_args;
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
kv[i].FindMaxima(i_args, xi_args, ugrid[i]);
|
||||
kv[i].GetDemko(ugrid[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -533,7 +531,7 @@ void SurfaceInterpolator::ComputeNURBS(int coordinate,
|
||||
}
|
||||
|
||||
const bool reuse_factorization = j > 0;
|
||||
kv[0].FindInterpolant(x, reuse_factorization);
|
||||
kv[0].GetInterpolant(x,ugrid[0], reuse_factorization);
|
||||
|
||||
for (int i = 0; i < ncp[0]; i++)
|
||||
{
|
||||
@@ -558,7 +556,7 @@ void SurfaceInterpolator::ComputeNURBS(int coordinate,
|
||||
}
|
||||
|
||||
const bool reuse_factorization = i > 0;
|
||||
kv[1].FindInterpolant(x, reuse_factorization);
|
||||
kv[1].GetInterpolant(x, ugrid[1], reuse_factorization);
|
||||
|
||||
for (int j = 0; j < ncp[1]; ++j)
|
||||
{
|
||||
|
||||
@@ -20,11 +20,9 @@ set(SRC_MESH_GF_FILES)
|
||||
foreach(MESH_FILE ${MESH_GF_FILES})
|
||||
list(APPEND SRC_MESH_GF_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${MESH_FILE})
|
||||
endforeach()
|
||||
add_custom_command(OUTPUT data_is_copied
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_GF_FILES} ../gslib/.
|
||||
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
|
||||
COMMENT "Copying tools miniapps data files ...")
|
||||
add_custom_target(copy_miniapps_tools_data DEPENDS data_is_copied)
|
||||
add_custom_target(copy_miniapps_tools_data
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_GF_FILES} .
|
||||
COMMENT "Syncing tools miniapps data files ...")
|
||||
|
||||
add_mfem_miniapp(display-basis
|
||||
MAIN display-basis.cpp
|
||||
|
||||
@@ -27,8 +27,8 @@ accelerations, so the relationship between forces/contact pressures and
|
||||
deformations/contact gaps is linear and, therefore, the problem can be solved
|
||||
exactly with a single linear solve. The mortar implementation is based on [Puso
|
||||
and Laursen (2004)](https://doi.org/10.1016/j.cma.2003.10.010). A description of
|
||||
the Tribol implementation is available in [Serac
|
||||
documentation](https://serac.readthedocs.io/en/latest/sphinx/theory_reference/solid.html#contact-mechanics).
|
||||
the Tribol implementation is available in [smith
|
||||
documentation](https://llnlsmith.readthedocs.io/en/latest/sphinx/theory_reference/solid.html#contact-mechanics).
|
||||
Lagrange multipliers are used to solve for the pressure required to prevent
|
||||
violation of the contact constraints.
|
||||
|
||||
|
||||
+29
-15
@@ -11,6 +11,24 @@
|
||||
|
||||
project(mfem-unit-tests NONE)
|
||||
|
||||
# Define a target that all examples and miniapps will depend on.
|
||||
set(MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME test_exec_prerequisites)
|
||||
add_custom_target(${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
|
||||
|
||||
# Add a target to copy the mfem data directory to the build directory
|
||||
# Implementable as a single copy_directory_if_different command w/ CMake >= 3.26
|
||||
file(GLOB DATA_FILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/data/*)
|
||||
add_custom_target(copy_test_data
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory data
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_FILES} data
|
||||
COMMENT "Syncing the test data directory ...")
|
||||
# Add 'copy_test_data' as a prerequisite for test executables, if the source and the
|
||||
# build directories are not the same.
|
||||
if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
|
||||
add_dependencies(${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME} copy_test_data)
|
||||
add_dependencies(${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME} copy_data)
|
||||
endif()
|
||||
|
||||
# Include the source directory for the unit tests - catch.hpp is there.
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
@@ -53,6 +71,7 @@ set(UNIT_TESTS_SRCS
|
||||
linalg/test_ode2.cpp
|
||||
linalg/test_operator.cpp
|
||||
linalg/test_particlevector.cpp
|
||||
linalg/test_sparsesmoothers.cpp
|
||||
linalg/test_vector.cpp
|
||||
mesh/mesh_test_utils.cpp
|
||||
mesh/test_exodus_reader.cpp
|
||||
@@ -68,8 +87,6 @@ set(UNIT_TESTS_SRCS
|
||||
mesh/test_psubmesh.cpp
|
||||
mesh/test_submesh.cpp
|
||||
mesh/test_vtu.cpp
|
||||
mesh/test_nurbs.cpp
|
||||
mesh/test_exodus_writer.cpp
|
||||
fem/make_permuted_mesh.cpp
|
||||
fem/test_1d_bilininteg.cpp
|
||||
fem/test_2d_bilininteg.cpp
|
||||
@@ -168,25 +185,19 @@ endif()
|
||||
|
||||
add_library(unit_tests_srcs OBJECT ${UNIT_TESTS_SRCS})
|
||||
target_link_libraries(unit_tests_srcs PUBLIC mfem)
|
||||
add_dependencies(unit_tests_srcs
|
||||
${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
|
||||
|
||||
# All serial non-device unit tests are built into a single executable,
|
||||
# 'unit_tests'.
|
||||
mfem_add_executable(unit_tests unit_test_main.cpp)
|
||||
target_link_libraries(unit_tests unit_tests_srcs)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} unit_tests)
|
||||
# Unit tests need the ../../data directory.
|
||||
add_dependencies(unit_tests copy_data)
|
||||
# ParSubMesh tests need meshes in ../../miniapps/multidomain
|
||||
add_dependencies(unit_tests copy_miniapps_multidomain_data)
|
||||
# NURBS tests need meshes in ../../miniapps/nurbs
|
||||
add_dependencies(unit_tests copy_miniapps_nurbs_data)
|
||||
|
||||
# Copy data to the build directory.
|
||||
add_custom_command(TARGET unit_tests POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/data data
|
||||
COMMENT "Copying the unit tests data directory ...")
|
||||
|
||||
# Create a test called 'unit_tests' that runs the 'unit_tests' executable.
|
||||
# The unit tests can be built and run separately from the rest of the tests:
|
||||
# make unit_tests
|
||||
@@ -205,7 +216,6 @@ if (MFEM_USE_CUDA)
|
||||
set_property(SOURCE ${GPU_UNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
|
||||
mfem_add_executable(gpu_unit_tests ${GPU_UNIT_TESTS_SRCS})
|
||||
target_link_libraries(gpu_unit_tests unit_tests_srcs)
|
||||
add_dependencies(gpu_unit_tests copy_data)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} gpu_unit_tests)
|
||||
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
add_test(NAME gpu_unit_tests COMMAND gpu_unit_tests)
|
||||
@@ -221,7 +231,6 @@ if (MFEM_USE_HIP)
|
||||
set(GPU_UNIT_TESTS_SRCS gpu_unit_test_main.cpp)
|
||||
mfem_add_executable(gpu_unit_tests ${GPU_UNIT_TESTS_SRCS})
|
||||
target_link_libraries(gpu_unit_tests unit_tests_srcs)
|
||||
add_dependencies(gpu_unit_tests copy_data)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} gpu_unit_tests)
|
||||
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
|
||||
add_test(NAME gpu_unit_tests COMMAND gpu_unit_tests)
|
||||
@@ -248,6 +257,8 @@ function(add_serial_miniapp_test name test_uvm)
|
||||
endif(MFEM_USE_HIP)
|
||||
|
||||
mfem_add_executable(${name}_tests_cpu ${${NAME}_TESTS_SRCS})
|
||||
add_dependencies(${name}_tests_cpu copy_miniapps_meshing_data)
|
||||
add_dependencies(${name}_tests_cpu ${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
|
||||
target_compile_definitions(${name}_tests_cpu PUBLIC MFEM_${NAME}_DEVICE="cpu")
|
||||
target_link_libraries(${name}_tests_cpu mfem)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_cpu)
|
||||
@@ -256,6 +267,8 @@ function(add_serial_miniapp_test name test_uvm)
|
||||
endif()
|
||||
|
||||
mfem_add_executable(${name}_tests_debug ${${NAME}_TESTS_SRCS})
|
||||
add_dependencies(${name}_tests_debug copy_miniapps_meshing_data)
|
||||
add_dependencies(${name}_tests_debug ${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
|
||||
target_compile_definitions(${name}_tests_debug PUBLIC MFEM_${NAME}_DEVICE="debug")
|
||||
target_link_libraries(${name}_tests_debug mfem)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_debug)
|
||||
@@ -265,6 +278,8 @@ function(add_serial_miniapp_test name test_uvm)
|
||||
|
||||
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
|
||||
mfem_add_executable(${name}_tests_gpu ${${NAME}_TESTS_SRCS})
|
||||
add_dependencies(${name}_tests_gpu copy_miniapps_meshing_data)
|
||||
add_dependencies(${name}_tests_gpu ${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
|
||||
target_compile_definitions(${name}_tests_gpu PUBLIC MFEM_${NAME}_DEVICE="gpu")
|
||||
target_link_libraries(${name}_tests_gpu mfem)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_gpu)
|
||||
@@ -274,6 +289,8 @@ function(add_serial_miniapp_test name test_uvm)
|
||||
|
||||
if (test_uvm)
|
||||
mfem_add_executable(${name}_tests_gpu_uvm ${${NAME}_TESTS_SRCS})
|
||||
add_dependencies(${name}_tests_gpu_uvm copy_miniapps_meshing_data)
|
||||
add_dependencies(${name}_tests_gpu_uvm ${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
|
||||
target_compile_definitions(${name}_tests_gpu_uvm PUBLIC
|
||||
MFEM_${NAME}_DEVICE="gpu:uvm")
|
||||
target_link_libraries(${name}_tests_gpu_uvm mfem)
|
||||
@@ -289,7 +306,6 @@ endfunction(add_serial_miniapp_test)
|
||||
add_serial_miniapp_test(sedov ON) # UVM ON
|
||||
add_serial_miniapp_test(tmop_pa OFF) # UVM OFF
|
||||
# TMOP tests need meshes in ../../miniapps/meshing
|
||||
add_dependencies(tmop_pa_tests_cpu copy_miniapps_meshing_data)
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# SERIAL CEED TESTS:
|
||||
@@ -343,7 +359,6 @@ if (MFEM_USE_MPI)
|
||||
set(PGPU_UNIT_TESTS_SRCS pgpu_unit_test_main.cpp)
|
||||
set_property(SOURCE ${PGPU_UNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
|
||||
mfem_add_executable(pgpu_unit_tests ${PGPU_UNIT_TESTS_SRCS})
|
||||
add_dependencies(pgpu_unit_tests copy_data)
|
||||
target_link_libraries(pgpu_unit_tests unit_tests_srcs)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pgpu_unit_tests)
|
||||
foreach(np 1 ${MFEM_MPI_NP})
|
||||
@@ -359,7 +374,6 @@ if (MFEM_USE_MPI)
|
||||
# pgpu_unit_tests
|
||||
set(PGPU_UNIT_TESTS_SRCS pgpu_unit_test_main.cpp)
|
||||
mfem_add_executable(pgpu_unit_tests ${PGPU_UNIT_TESTS_SRCS})
|
||||
add_dependencies(pgpu_unit_tests copy_data)
|
||||
target_link_libraries(pgpu_unit_tests unit_tests_srcs)
|
||||
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pgpu_unit_tests)
|
||||
foreach(np 1 ${MFEM_MPI_NP})
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
elements
|
||||
2
|
||||
1 1 0 1
|
||||
1 1 2 1
|
||||
|
||||
boundary
|
||||
2
|
||||
1 0 0
|
||||
1 0 2
|
||||
|
||||
# Both edges map to the same unique KnotVector (index 0), but the second edge
|
||||
# has opposite orientation (v0 > v1), so its mapping is signed.
|
||||
edges
|
||||
2
|
||||
0 0 1
|
||||
0 2 1
|
||||
|
||||
vertices
|
||||
3
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: u increases from x=0 to x=1
|
||||
knotvectors
|
||||
1
|
||||
2 4 0 0 0 0.3 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 1.0
|
||||
0.3 0.0 1.0
|
||||
0.7 0.0 1.0
|
||||
1.0 0.0 1.0
|
||||
|
||||
# Patch 1: same KnotVector, but control points are reversed to match the
|
||||
# element/edge orientation.
|
||||
knotvectors
|
||||
1
|
||||
2 4 0 0 0 0.3 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
2.0 0.0 1.0
|
||||
1.7 0.0 1.0
|
||||
1.3 0.0 1.0
|
||||
1.0 0.0 1.0
|
||||
@@ -0,0 +1,53 @@
|
||||
MFEM NURBS mesh v1.0
|
||||
|
||||
dimension
|
||||
1
|
||||
|
||||
elements
|
||||
2
|
||||
1 1 0 1
|
||||
1 1 1 2
|
||||
|
||||
boundary
|
||||
2
|
||||
1 0 0
|
||||
1 0 2
|
||||
|
||||
# Both edges map to the same unique KnotVector (index 0).
|
||||
edges
|
||||
2
|
||||
0 0 1
|
||||
0 1 2
|
||||
|
||||
vertices
|
||||
3
|
||||
|
||||
patches
|
||||
|
||||
# Patch 0: quadratic, 4 control points, 1 interior knot at u=0.3
|
||||
knotvectors
|
||||
1
|
||||
2 4 0 0 0 0.3 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
0.0 0.0 1.0
|
||||
0.3 0.0 1.0
|
||||
0.7 0.0 1.0
|
||||
1.0 0.0 1.0
|
||||
|
||||
# Patch 1: same KnotVector, different control points (translated)
|
||||
knotvectors
|
||||
1
|
||||
2 4 0 0 0 0.3 1 1 1
|
||||
|
||||
dimension
|
||||
2
|
||||
|
||||
controlpoints
|
||||
1.0 0.0 1.0
|
||||
1.3 0.0 1.0
|
||||
1.7 0.0 1.0
|
||||
2.0 0.0 1.0
|
||||
@@ -362,3 +362,38 @@ TEST_CASE("H(div) Linear Form Extension", "[LinearFormExtension], [GPU]")
|
||||
d1 -= d2;
|
||||
REQUIRE(d1.Norml2() == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
TEST_CASE("Parallel Fast LinearForm Assembly",
|
||||
"[AssemblyLevel], [Parallel], [GPU]")
|
||||
{
|
||||
auto order = GENERATE(1, 2);
|
||||
auto mesh_fname = GENERATE(
|
||||
"../../data/amr-quad.mesh",
|
||||
"../../data/fichera-amr.mesh"
|
||||
);
|
||||
|
||||
Mesh serial_mesh(mesh_fname);
|
||||
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
|
||||
serial_mesh.Clear();
|
||||
|
||||
Array<int> ess_bdr(mesh.bdr_attributes.Max());
|
||||
ess_bdr = 0;
|
||||
mesh.MarkExternalBoundaries(ess_bdr);
|
||||
|
||||
H1_FECollection fec(order, mesh.Dimension());
|
||||
ParFiniteElementSpace fespace(&mesh, &fec);
|
||||
|
||||
Array<int> ess_tdof_list;
|
||||
fespace.GetBoundaryTrueDofs(ess_tdof_list);
|
||||
|
||||
ParLinearForm b(&fespace);
|
||||
ConstantCoefficient one(1.0);
|
||||
b.AddDomainIntegrator(new DomainLFIntegrator(one));
|
||||
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(one), ess_bdr);
|
||||
b.UseFastAssembly(true);
|
||||
b.Assemble();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "unit_tests.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
static void TestTranspose(const Operator &A)
|
||||
{
|
||||
DenseMatrix A_dense(A.Height(), A.Width());
|
||||
|
||||
Vector e(A.Width());
|
||||
e = 0.0;
|
||||
for (int i = 0; i < A.Width(); ++i)
|
||||
{
|
||||
e[i] = 1.0;
|
||||
|
||||
Vector Ae(A.Height());
|
||||
A.Mult(e, Ae);
|
||||
A_dense.SetCol(i, Ae);
|
||||
|
||||
e[i] = 0.0;
|
||||
}
|
||||
|
||||
Vector v(A.Height());
|
||||
v.Randomize();
|
||||
|
||||
Vector w1(A.Width()), w2(A.Width());
|
||||
|
||||
A.MultTranspose(v, w1);
|
||||
A_dense.MultTranspose(v, w2);
|
||||
|
||||
w1 -= w2;
|
||||
|
||||
REQUIRE(w1.Normlinf() == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
TEST_CASE("Sparse Smoothers Transposed", "[DSmoother][GSSmoother]")
|
||||
{
|
||||
const bool sym = GENERATE(true, false);
|
||||
|
||||
constexpr int n = 10;
|
||||
SparseMatrix A(n, n);
|
||||
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
for (int j = 0; j < n; ++j)
|
||||
{
|
||||
const real_t val = rand_real();
|
||||
A.Set(i, j, val);
|
||||
if (sym) { A.Set(j, i, val); }
|
||||
}
|
||||
A.Add(i, i, 10.0);
|
||||
}
|
||||
A.Finalize();
|
||||
|
||||
constexpr int nit = 2; // Number of smoother iterations
|
||||
TestTranspose(DSmoother(A, 0, 1.0, nit)); // scaled
|
||||
if (sym)
|
||||
{
|
||||
TestTranspose(DSmoother(A, 1, 1.0, nit)); // l1-Jacobi
|
||||
TestTranspose(DSmoother(A, 2, 1.0, nit)); // lumped Jacobi
|
||||
}
|
||||
TestTranspose(GSSmoother(A, 0, nit)); // symmetric
|
||||
TestTranspose(GSSmoother(A, 1, nit)); // forward
|
||||
TestTranspose(GSSmoother(A, 2, nit)); // backward
|
||||
}
|
||||
@@ -350,3 +350,131 @@ TEST_CASE("MakeNurbs", "[Mesh]")
|
||||
Mesh::MakeCartesian3D(1, 1, 1, Element::Type::HEXAHEDRON);
|
||||
test_nurbs_extension(patch_topology_3d);
|
||||
}
|
||||
|
||||
TEST_CASE("NURBS 1D curve in 2D from patches", "[Mesh]")
|
||||
{
|
||||
// Build a 1D patch topology embedded in 2D physical space with
|
||||
// three segments of varying orders (linear, quadratic and cubic)
|
||||
constexpr int dim = 1;
|
||||
constexpr int space_dim = 2;
|
||||
Mesh patch_topology(dim, 0, 0, 0, space_dim);
|
||||
|
||||
constexpr int nv_input = 6;
|
||||
for (int i = 0; i < nv_input; i++)
|
||||
{
|
||||
patch_topology.AddVertex((real_t)i, 0.0, 0.0);
|
||||
}
|
||||
|
||||
patch_topology.AddSegment(0, 1, 1);
|
||||
patch_topology.AddSegment(2, 3, 2);
|
||||
patch_topology.AddSegment(4, 5, 3);
|
||||
|
||||
// Three 1D NURBS patches with variable order, each with control points
|
||||
// given in (x, y, w) format so that the physical dimension is 2 while the
|
||||
// topological dimension is 1.
|
||||
|
||||
auto set_knots = [](KnotVector &kv, std::initializer_list<real_t> knots)
|
||||
{
|
||||
MFEM_VERIFY(kv.Size() == static_cast<int>(knots.size()),
|
||||
"KnotVector and knot list must have the same size.");
|
||||
int i = 0;
|
||||
for (const auto &k : knots)
|
||||
{
|
||||
kv[i++] = k;
|
||||
}
|
||||
};
|
||||
|
||||
auto set_cp = [](std::initializer_list<std::array<real_t, 3>> pts)
|
||||
{
|
||||
Array<real_t> cp(3 * static_cast<int>(pts.size()));
|
||||
int i = 0;
|
||||
for (const auto &[x, y, w] : pts)
|
||||
{
|
||||
cp[i++] = x;
|
||||
cp[i++] = y;
|
||||
cp[i++] = w;
|
||||
}
|
||||
return cp;
|
||||
};
|
||||
|
||||
// Patch 0: order 1, 4 control points
|
||||
KnotVector kv1(1, 4);
|
||||
set_knots(kv1, {0.0, 0.0, 0.4, 0.6, 1.0, 1.0});
|
||||
kv1.GetElements();
|
||||
Array<real_t> cp1 = set_cp(
|
||||
{
|
||||
{0.0, 0.0, 1.0},
|
||||
{0.4, 0.6, 1.0},
|
||||
{0.6, 0.4, 1.0},
|
||||
{1.0, 1.0, 1.0}});
|
||||
Array<const KnotVector *> kvs1({&kv1});
|
||||
|
||||
// Patch 1: order 2, 3 control points
|
||||
KnotVector kv2(2, 3);
|
||||
set_knots(kv2, {0.0, 0.0, 0.0, 1.0, 1.0, 1.0});
|
||||
kv2.GetElements();
|
||||
Array<real_t> cp2 = set_cp(
|
||||
{
|
||||
{1.0, 0.0, 1.0},
|
||||
{1.0, 1.0, 1.2},
|
||||
{2.0, 1.0, 1.0}});
|
||||
Array<const KnotVector *> kvs2({&kv2});
|
||||
|
||||
// Patch 2: order 3, 4 control points
|
||||
KnotVector kv3(3, 4);
|
||||
set_knots(kv3, {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0});
|
||||
kv3.GetElements();
|
||||
Array<real_t> cp3 = set_cp(
|
||||
{
|
||||
{2.0, 0.0, 1.0},
|
||||
{2.0, 0.9, 1.31},
|
||||
{2.1, 1.0, 1.32},
|
||||
{3.0, 1.0, 1.0}});
|
||||
Array<const KnotVector *> kvs3({&kv3});
|
||||
|
||||
auto p1 = std::make_unique<NURBSPatch>(kvs1, 3, cp1.GetData());
|
||||
auto p2 = std::make_unique<NURBSPatch>(kvs2, 3, cp2.GetData());
|
||||
auto p3 = std::make_unique<NURBSPatch>(kvs3, 3, cp3.GetData());
|
||||
|
||||
Array<const NURBSPatch *> patches(3);
|
||||
patches[0] = p1.get();
|
||||
patches[1] = p2.get();
|
||||
patches[2] = p3.get();
|
||||
|
||||
NURBSExtension ne(&patch_topology, patches);
|
||||
Mesh mesh(ne);
|
||||
|
||||
// Check that we created a 1D NURBS mesh embedded in 2D physical space and
|
||||
// that the associated finite element space uses the correct vector dimension.
|
||||
REQUIRE(mesh.Dimension() == dim);
|
||||
REQUIRE(mesh.SpaceDimension() == space_dim);
|
||||
REQUIRE(mesh.GetNE() == 5);
|
||||
REQUIRE(mesh.GetNV() == 8);
|
||||
|
||||
GridFunction *nodes = mesh.GetNodes();
|
||||
REQUIRE(nodes != NULL);
|
||||
REQUIRE(nodes->FESpace() != NULL);
|
||||
REQUIRE(nodes->FESpace()->GetVDim() == space_dim);
|
||||
|
||||
REQUIRE(mesh.NURBSext != NULL);
|
||||
REQUIRE(mesh.NURBSext->GetNP() == 3);
|
||||
|
||||
// Additionally, exercise degree elevation and ensure basic invariants hold
|
||||
{
|
||||
const Array<int> &orders = mesh.NURBSext->GetOrders();
|
||||
const int max_order = orders.Max();
|
||||
mesh.DegreeElevate(max_order, max_order);
|
||||
|
||||
REQUIRE(mesh.NURBSext != nullptr);
|
||||
REQUIRE(mesh.Dimension() == dim);
|
||||
REQUIRE(mesh.SpaceDimension() == space_dim);
|
||||
REQUIRE(mesh.NURBSext->Dimension() == dim);
|
||||
|
||||
const Array<int> &new_orders = mesh.NURBSext->GetOrders();
|
||||
REQUIRE(new_orders.Size() == orders.Size());
|
||||
for (int i = 0; i < new_orders.Size(); ++i)
|
||||
{
|
||||
REQUIRE(new_orders[i] == max_order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +132,188 @@ TEST_CASE("NURBS mesh reconstruction", "[NURBS]")
|
||||
for (auto *p : patches) { delete p; }
|
||||
}
|
||||
|
||||
TEST_CASE("Location conversion check", "[NURBS]")
|
||||
{
|
||||
|
||||
KnotVector kv(3, Vector({0.0,
|
||||
0.2,0.2,0.2,
|
||||
0.5,0.5,0.5,
|
||||
0.8,0.8,0.8,
|
||||
1.0}));
|
||||
|
||||
mfem::out<<"knotvector : ";
|
||||
kv.Print(mfem::out);
|
||||
|
||||
constexpr int samples = 31;
|
||||
for (int i = 0; i < samples; i++)
|
||||
{
|
||||
const real_t u = i/real_t(samples-1);
|
||||
const int ks = kv.GetSpan (u);
|
||||
REQUIRE( ((kv[ks] <= u) && (u <= kv[ks+1])) );
|
||||
const real_t xi = kv.GetRefPoint(u, ks);
|
||||
REQUIRE( ((0.0 <= xi) && (xi <= 1.0)) );
|
||||
const real_t un = kv.GetKnotLocation(xi,ks);
|
||||
REQUIRE((un - u) == MFEM_Approx(0.0));
|
||||
|
||||
mfem::out<<i<<" : "<<ks<<" ";
|
||||
mfem::out<<kv[ks] <<" "<<u<<" "<<kv[ks+1]<<" : ";
|
||||
mfem::out<<u<<" "<<un<<" = "<<un -u<<std::endl;
|
||||
}
|
||||
|
||||
for (int i = 0; i < kv.Size(); i++)
|
||||
{
|
||||
const real_t u = kv[i];
|
||||
const int ks = kv.GetSpan (u);
|
||||
REQUIRE( ((kv[ks] <= u) && (u <= kv[ks+1])) );
|
||||
const real_t xi = kv.GetRefPoint(u, ks);
|
||||
REQUIRE( ((0.0 <= xi) && (xi <= 1.0)) );
|
||||
const real_t un = kv.GetKnotLocation(xi,ks);
|
||||
REQUIRE((un - u) == MFEM_Approx(0.0));
|
||||
|
||||
mfem::out<<i<<" : "<<ks<<" ";
|
||||
mfem::out<<kv[ks] <<" "<<u<<" "<<kv[ks+1]<<" : ";
|
||||
mfem::out<<u<<" "<<un<<" = "<<un -u<<std::endl;
|
||||
}
|
||||
|
||||
KnotVector kv2(1, Vector({0.0, 1.0/3.0, 2.0/3.0, 1.0}));
|
||||
mfem::out<<"knotvector2 : ";
|
||||
kv2.Print(mfem::out);
|
||||
|
||||
for (int i = 0; i < samples; i++)
|
||||
{
|
||||
const real_t u = i/real_t(samples-1);
|
||||
const int ks = kv2.GetSpan (u);
|
||||
REQUIRE( ((kv2[ks] <= u) && (u <= kv2[ks+1])) );
|
||||
const real_t xi = kv2.GetRefPoint(u, ks);
|
||||
REQUIRE( ((0.0 <= xi) && (xi <= 1.0)) );
|
||||
const real_t un = kv2.GetKnotLocation(xi,ks);
|
||||
REQUIRE((un - u) == MFEM_Approx(0.0));
|
||||
|
||||
mfem::out<<i<<" : "<<ks<<" ";
|
||||
mfem::out<<kv2[ks] <<" "<<u<<" "<<kv2[ks+1]<<" : ";
|
||||
mfem::out<<u<<" "<<un<<" = "<<un -u<<std::endl;
|
||||
}
|
||||
|
||||
for (int i = 0; i < kv2.Size(); i++)
|
||||
{
|
||||
const real_t u = kv2[i];
|
||||
const int ks = kv2.GetSpan (u);
|
||||
REQUIRE( ((kv2[ks] <= u) && (u <= kv2[ks+1])) );
|
||||
const real_t xi = kv2.GetRefPoint(u, ks);
|
||||
REQUIRE( ((0.0 <= xi) && (xi <= 1.0)) );
|
||||
const real_t un = kv2.GetKnotLocation(xi,ks);
|
||||
REQUIRE((un - u) == MFEM_Approx(0.0));
|
||||
|
||||
mfem::out<<i<<" : "<<ks<<" ";
|
||||
mfem::out<<kv2[ks] <<" "<<u<<" "<<kv2[ks+1]<<" : ";
|
||||
mfem::out<<u<<" "<<un<<" = "<<un -u<<std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("Greville, Botella and Demko points", "[NURBS]")
|
||||
{
|
||||
|
||||
Vector xi;
|
||||
for ( int p = 1; p <= 9; p++)
|
||||
{
|
||||
mfem::out<<"Order : "<<p<<std::endl;
|
||||
KnotVector kvp(p, Vector({0., 1.}));
|
||||
mfem::out<<"Knotvector : "; kvp.Print(mfem::out);
|
||||
|
||||
kvp.GetGreville(xi);
|
||||
mfem::out<<"Greville points : "; xi.Print(std::cout,999);
|
||||
|
||||
kvp.GetBotella(xi);
|
||||
mfem::out<<"Botella points : "; xi.Print(std::cout,999);
|
||||
|
||||
kvp.GetDemko(xi);
|
||||
mfem::out<<"Demko points : "; xi.Print(std::cout,999);
|
||||
}
|
||||
|
||||
KnotVector kv(3, Vector({0.0, 0.3, 0.3, 0.3, 0.6, 1.0}));
|
||||
|
||||
mfem::out<<"Knotvector : "; kv.Print(mfem::out);
|
||||
|
||||
// Greville
|
||||
Vector greville(kv.GetNCP());
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
greville[i] = kv.GetGreville(i);
|
||||
}
|
||||
mfem::out<<"Greville points : "; greville.Print(mfem::out, 32);
|
||||
|
||||
Vector gref({0.0,0.1,0.2,0.3,0.4,19./30,26./30, 1.0});
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
REQUIRE((greville[i] - gref[i]) == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
// Botella
|
||||
Vector botella(kv.GetNCP());
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
botella[i] = kv.GetBotella(i);
|
||||
}
|
||||
mfem::out<<"Botella points : "; botella.Print(mfem::out, 32);
|
||||
|
||||
Vector bref({0.0,0.1,0.2,0.3,
|
||||
0.444007481526490333,
|
||||
0.626666666666666594,
|
||||
0.828131261741523739, 1.0});
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
REQUIRE((botella[i] - bref[i]) == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
// Demko
|
||||
Vector demko(kv.GetNCP());
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
demko[i] = kv.GetDemko(i);
|
||||
}
|
||||
mfem::out<<"Demko points : "; demko.Print(mfem::out, 32);
|
||||
|
||||
Vector dref({0.0,0.075,0.225,0.3,
|
||||
0.406122105546614987,
|
||||
0.621569465634039919,
|
||||
0.87385648854468001,1.0});
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
REQUIRE((demko[i] - dref[i]) == MFEM_Approx(0.0,1e-9,1e-9));
|
||||
}
|
||||
|
||||
// Chebyshev spline
|
||||
Vector a(kv.GetNCP());
|
||||
Vector x(kv.GetNCP());
|
||||
for ( int i = 0; i <x.Size(); i++)
|
||||
{
|
||||
x[i] = std::pow(-1.0, i);
|
||||
}
|
||||
kv.GetInterpolant(x, demko, a);
|
||||
mfem::out<<"Chebyshev spline coeff : "; a.Print(mfem::out, 32);
|
||||
|
||||
Vector aref({1.0, -5.0, 5.0, -1.0,
|
||||
3.24079982256718635,
|
||||
-5.51623733136825933,
|
||||
3.75648721902370486, -1.0});
|
||||
for (int i = 0; i < kv.GetNCP(); i++)
|
||||
{
|
||||
REQUIRE((a[i] - aref[i]) == MFEM_Approx(0.0));
|
||||
}
|
||||
|
||||
mfem::out<<"Chebyshev spline \n";
|
||||
kv.PrintFunction(mfem::out, a, 21);
|
||||
}
|
||||
|
||||
TEST_CASE("NURBS knotvector orientation", "[NURBS]")
|
||||
{
|
||||
// This will fail to load without CorrectPatchTopoOrientations
|
||||
auto mesh_fname = "../../miniapps/nurbs/meshes/3patch-nurbs-flipedge.mesh";
|
||||
Mesh mesh(mesh_fname, 1, 1);
|
||||
REQUIRE(mesh.NURBSext->CheckPatches());
|
||||
}
|
||||
|
||||
TEST_CASE("NURBS NC-patch mesh loading", "[NURBS]")
|
||||
{
|
||||
auto mesh_fname = GENERATE("../../data/nc3-nurbs.mesh",
|
||||
@@ -146,6 +328,177 @@ TEST_CASE("NURBS NC-patch mesh loading", "[NURBS]")
|
||||
REQUIRE(mesh.GetNE() == ne * std::pow(2, dim));
|
||||
}
|
||||
|
||||
TEST_CASE("NURBS 1D variable-order mesh load", "[NURBS]")
|
||||
{
|
||||
auto mesh_fname = GENERATE("../../data/nurbs-segments2d.mesh",
|
||||
"../../data/nurbs-segments3d.mesh",
|
||||
"../../data/nurbs-segments2d-patches.mesh",
|
||||
"../../data/nurbs-segments3d-patches.mesh",
|
||||
"../../data/nurbs-segments2d-patches-multispan.mesh");
|
||||
|
||||
// Set up hard-coded expected values based on the input meshes.
|
||||
// This should be easy to update as needed.
|
||||
struct ExpectedSizes
|
||||
{
|
||||
int phys_dim, ne, nv, nkv;
|
||||
Array<int> orders, ncp;
|
||||
};
|
||||
|
||||
const auto expected = [&]() -> ExpectedSizes
|
||||
{
|
||||
ExpectedSizes e;
|
||||
|
||||
const bool is_2d = (std::string(mesh_fname).find("2d") != std::string::npos);
|
||||
e.phys_dim = is_2d ? 2 : 3;
|
||||
|
||||
if (std::string(mesh_fname).find("multispan") != std::string::npos)
|
||||
{
|
||||
// multispan: 4 input segments w/ 9 elements, 13 vertices
|
||||
e.ne = 9;
|
||||
e.nv = 13;
|
||||
e.nkv = 4;
|
||||
e.orders = Array<int>({1, 2, 3, 4});
|
||||
e.ncp = Array<int>({4, 4, 6, 5});
|
||||
}
|
||||
else
|
||||
{
|
||||
// standard: 3 elements, 6 vertices
|
||||
e.ne = 3;
|
||||
e.nv = 6;
|
||||
e.nkv = 3;
|
||||
e.orders = Array<int>({1, 2, 3});
|
||||
e.ncp = Array<int>({2, 3, 4});
|
||||
}
|
||||
|
||||
return e;
|
||||
}();
|
||||
|
||||
Mesh mesh(mesh_fname, 1, 0);
|
||||
|
||||
// Basic mesh properties
|
||||
REQUIRE(mesh.Dimension() == 1);
|
||||
REQUIRE(mesh.SpaceDimension() == expected.phys_dim);
|
||||
|
||||
REQUIRE(mesh.GetNE() == expected.ne);
|
||||
REQUIRE(mesh.GetNV() == expected.nv);
|
||||
|
||||
// NURBS extension must be present and 1D
|
||||
REQUIRE(mesh.NURBSext != nullptr);
|
||||
REQUIRE(mesh.NURBSext->Dimension() == 1);
|
||||
|
||||
// Check that we have the expected number of knotvectors
|
||||
const int n_kv = mesh.NURBSext->GetNKV();
|
||||
REQUIRE(n_kv == expected.nkv);
|
||||
|
||||
const Array<int> &orders = mesh.NURBSext->GetOrders();
|
||||
REQUIRE(orders.Size() == n_kv);
|
||||
|
||||
// Validate each KnotVector's order and number of control points.
|
||||
for (int i = 0; i < n_kv; i++)
|
||||
{
|
||||
const KnotVector *kv = mesh.NURBSext->GetKnotVector(i);
|
||||
REQUIRE(kv != nullptr);
|
||||
|
||||
const int o = kv->GetOrder();
|
||||
const int ncp = kv->GetNCP();
|
||||
|
||||
bool matched = false;
|
||||
for (int j = 0; j < expected.orders.Size(); ++j)
|
||||
{
|
||||
if (o == expected.orders[j] && ncp == expected.ncp[j])
|
||||
{
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
REQUIRE(matched);
|
||||
}
|
||||
|
||||
// Additionally, exercise degree elevation and ensure basic invariants hold
|
||||
{
|
||||
const int max_order = orders.Max();
|
||||
mesh.DegreeElevate(max_order, max_order);
|
||||
|
||||
REQUIRE(mesh.NURBSext != nullptr);
|
||||
REQUIRE(mesh.Dimension() == 1);
|
||||
REQUIRE(mesh.SpaceDimension() == expected.phys_dim);
|
||||
REQUIRE(mesh.NURBSext->Dimension() == 1);
|
||||
|
||||
const Array<int> &new_orders = mesh.NURBSext->GetOrders();
|
||||
REQUIRE(new_orders.Size() == orders.Size());
|
||||
for (int i = 0; i < new_orders.Size(); ++i)
|
||||
{
|
||||
REQUIRE(new_orders[i] == max_order);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("NURBS 1D shared KnotVector in patches", "[NURBS]")
|
||||
{
|
||||
auto RequireSameKnotVector = [](const KnotVector &a, const KnotVector &b)
|
||||
{
|
||||
REQUIRE(a.GetOrder() == b.GetOrder());
|
||||
REQUIRE(a.GetNCP() == b.GetNCP());
|
||||
REQUIRE(a.Size() == b.Size());
|
||||
for (int i = 0; i < a.Size(); i++)
|
||||
{
|
||||
REQUIRE( (a[i]-b[i]) == MFEM_Approx(0.));
|
||||
}
|
||||
};
|
||||
|
||||
SECTION("Same orientation")
|
||||
{
|
||||
const auto mesh_fname = "./data/nurbs-segments-same-orientation.mesh";
|
||||
Mesh mesh(mesh_fname, 1, 0);
|
||||
|
||||
REQUIRE(mesh.NURBSext != nullptr);
|
||||
REQUIRE(mesh.Dimension() == 1);
|
||||
REQUIRE(mesh.SpaceDimension() == 2);
|
||||
REQUIRE(mesh.NURBSext->GetNP() == 2);
|
||||
REQUIRE(mesh.NURBSext->GetNKV() == 1);
|
||||
|
||||
const KnotVector *unique_kv = mesh.NURBSext->GetKnotVector(0);
|
||||
REQUIRE(unique_kv != nullptr);
|
||||
|
||||
Array<const KnotVector *> pkv0, pkv1;
|
||||
mesh.NURBSext->GetPatchKnotVectors(0, pkv0);
|
||||
mesh.NURBSext->GetPatchKnotVectors(1, pkv1);
|
||||
REQUIRE(pkv0.Size() == 1);
|
||||
REQUIRE(pkv1.Size() == 1);
|
||||
|
||||
RequireSameKnotVector(*unique_kv, *pkv0[0]);
|
||||
RequireSameKnotVector(*unique_kv, *pkv1[0]);
|
||||
RequireSameKnotVector(*pkv0[0], *pkv1[0]);
|
||||
}
|
||||
|
||||
SECTION("Opposite orientation")
|
||||
{
|
||||
const auto mesh_fname = "./data/nurbs-segments-opposite-orientation.mesh";
|
||||
Mesh mesh(mesh_fname, 1, 0);
|
||||
|
||||
REQUIRE(mesh.NURBSext != nullptr);
|
||||
REQUIRE(mesh.Dimension() == 1);
|
||||
REQUIRE(mesh.SpaceDimension() == 2);
|
||||
REQUIRE(mesh.NURBSext->GetNP() == 2);
|
||||
REQUIRE(mesh.NURBSext->GetNKV() == 1);
|
||||
|
||||
const KnotVector *unique_kv = mesh.NURBSext->GetKnotVector(0);
|
||||
REQUIRE(unique_kv != nullptr);
|
||||
|
||||
Array<const KnotVector *> pkv0, pkv1;
|
||||
mesh.NURBSext->GetPatchKnotVectors(0, pkv0);
|
||||
mesh.NURBSext->GetPatchKnotVectors(1, pkv1);
|
||||
REQUIRE(pkv0.Size() == 1);
|
||||
REQUIRE(pkv1.Size() == 1);
|
||||
|
||||
RequireSameKnotVector(*unique_kv, *pkv0[0]);
|
||||
|
||||
KnotVector flipped(*unique_kv);
|
||||
flipped.Flip();
|
||||
RequireSameKnotVector(flipped, *pkv1[0]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("NURBS NC-patch large meshes", "[MFEMData][NURBS]")
|
||||
{
|
||||
auto mesh_fname = GENERATE("bricks2D.mesh",
|
||||
|
||||
@@ -121,6 +121,31 @@ TEST_CASE("ParMeshGlobalIndices", "[Parallel], [ParMesh]")
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ParMeshSharedFaces", "[Parallel], [ParMesh]")
|
||||
{
|
||||
const char *mesh_file = "../../data/fichera-amr.mesh";
|
||||
|
||||
Mesh mesh(mesh_file);
|
||||
ParMesh pmesh(MPI_COMM_WORLD, mesh);
|
||||
pmesh.ExchangeFaceNbrData();
|
||||
|
||||
const int nshared = pmesh.GetNSharedFaces();
|
||||
int local_ghosts_nonmatching = 0;
|
||||
for (int sf = 0; sf < nshared; sf++)
|
||||
{
|
||||
const int f = pmesh.GetSharedFace(sf);
|
||||
FaceElementTransformations *ftr =
|
||||
pmesh.GetSharedFaceTransformationsByLocalIndex(f, false);
|
||||
if (f != ftr->ElementNo) { local_ghosts_nonmatching++; }
|
||||
}
|
||||
|
||||
int global_ghosts_nonmatching = 0;
|
||||
MPI_Allreduce(&local_ghosts_nonmatching, &global_ghosts_nonmatching, 1, MPI_INT,
|
||||
MPI_SUM, pmesh.GetComm());
|
||||
|
||||
REQUIRE(global_ghosts_nonmatching == 0);
|
||||
}
|
||||
|
||||
namespace simplicial
|
||||
{
|
||||
|
||||
|
||||
Reference in New Issue
Block a user