Compare commits
467
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f2fcf8b82 | ||
|
|
2369e8ebd0 | ||
|
|
8efb9483bc | ||
|
|
55bafba69a | ||
|
|
3a29fda4cd | ||
|
|
c53bce08f1 | ||
|
|
afadd435f5 | ||
|
|
36d7629b26 | ||
|
|
8f32e6620b | ||
|
|
766760659d | ||
|
|
9185762478 | ||
|
|
2c41e483fe | ||
|
|
78a4a77ed7 | ||
|
|
ea4aceeffc | ||
|
|
1f8af4538c | ||
|
|
2ce98bec12 | ||
|
|
3db24b1b40 | ||
|
|
0e61a94b5f | ||
|
|
194f2a56b7 | ||
|
|
efe05b9b1a | ||
|
|
91600c12eb | ||
|
|
35aef0486f | ||
|
|
df6d58da30 | ||
|
|
500e952d5c | ||
|
|
1c56fe47c4 | ||
|
|
bbd33cfcc1 | ||
|
|
5083a29ccd | ||
|
|
6e063a5d23 | ||
|
|
87b11c227b | ||
|
|
072147289b | ||
|
|
f2a42123f7 | ||
|
|
85c95269ad | ||
|
|
cde2b05366 | ||
|
|
e8d1fc9b60 | ||
|
|
60771f2f27 | ||
|
|
dbe2c6862c | ||
|
|
35442a2004 | ||
|
|
1a4c7eb027 | ||
|
|
60a9893d52 | ||
|
|
8c2ffb9d26 | ||
|
|
08f41f6450 | ||
|
|
d184921e09 | ||
|
|
57e26f75b0 | ||
|
|
2eaf46c80d | ||
|
|
48a2648ec5 | ||
|
|
bfffb837d3 | ||
|
|
988cc5b18d | ||
|
|
dfd55716bd | ||
|
|
49a50027dd | ||
|
|
ae49f4be68 | ||
|
|
c51d05f1e1 | ||
|
|
c5ef67adcf | ||
|
|
c5f78ea58a | ||
|
|
d066b11e18 | ||
|
|
cb85a7b804 | ||
|
|
4c6be0bc6f | ||
|
|
b5af0e9f81 | ||
|
|
33452bd3e8 | ||
|
|
27ed1f64f9 | ||
|
|
4d1cd791f3 | ||
|
|
230dc805f3 | ||
|
|
cb2d4bde47 | ||
|
|
f6bb6982e8 | ||
|
|
2352f7be6b | ||
|
|
8df194e6b0 | ||
|
|
a0f2250a76 | ||
|
|
244db1b571 | ||
|
|
75795809ef | ||
|
|
825a5a7f2e | ||
|
|
4ed8f16325 | ||
|
|
882dedf5f3 | ||
|
|
5f051efc49 | ||
|
|
ec896295d6 | ||
|
|
20bf4d5a98 | ||
|
|
8921101d14 | ||
|
|
f74b394c8a | ||
|
|
e282d4518e | ||
|
|
1e48c7e6d0 | ||
|
|
88456472dc | ||
|
|
7040fe65da | ||
|
|
5e43a958b7 | ||
|
|
14fc04e9ee | ||
|
|
847cf4a646 | ||
|
|
bdb3f39ffa | ||
|
|
803e2bd5c9 | ||
|
|
2f7ba402dd | ||
|
|
26374d9be8 | ||
|
|
7ae7690846 | ||
|
|
8102cb8141 | ||
|
|
bc0ec2e717 | ||
|
|
99c858c389 | ||
|
|
fcb78b81ec | ||
|
|
92ab53dec6 | ||
|
|
c2475e43fd | ||
|
|
232853214d | ||
|
|
23986c5c64 | ||
|
|
a024fb10bc | ||
|
|
531e8d7ad9 | ||
|
|
5293b9694d | ||
|
|
9008d5a050 | ||
|
|
c18f3ba6f9 | ||
|
|
af23695747 | ||
|
|
1f97289d2a | ||
|
|
19db07b30d | ||
|
|
419f890e5c | ||
|
|
3216d86e6c | ||
|
|
9c85a6365f | ||
|
|
2a10ab2f48 | ||
|
|
19e60a38cc | ||
|
|
e2a382dca4 | ||
|
|
ffaae2ea82 | ||
|
|
7f17f33763 | ||
|
|
d33fd770cd | ||
|
|
d48af86cdf | ||
|
|
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 | ||
|
|
dee699eede | ||
|
|
dc74fa2aeb | ||
|
|
9f8b7ed137 | ||
|
|
6eb6a632c9 | ||
|
|
371e7389ec | ||
|
|
a49bbf288e | ||
|
|
f003563fcd | ||
|
|
b0eb063b35 | ||
|
|
51c140f4ff | ||
|
|
b0ed0c81e4 | ||
|
|
d7e7ac746f | ||
|
|
1535b5a932 | ||
|
|
5db0bffc2f | ||
|
|
3446841376 | ||
|
|
c0d5cd54c5 | ||
|
|
10b38b4d2b | ||
|
|
09acac006e | ||
|
|
eb606dd1b7 | ||
|
|
1282722ecf | ||
|
|
d1ca02310d | ||
|
|
4fa3f6a15d | ||
|
|
b6285fb7eb | ||
|
|
4ab7f66cc8 | ||
|
|
bd4f07f6cb | ||
|
|
6f72e7f752 | ||
|
|
ddde1ff8d4 | ||
|
|
81272cb468 | ||
|
|
7c09989768 | ||
|
|
9ed202928e | ||
|
|
809eb996ca | ||
|
|
62b27d3989 | ||
|
|
7794557c18 | ||
|
|
b444c89ab0 | ||
|
|
eafb82943b | ||
|
|
a2243413e6 | ||
|
|
495cb138ee | ||
|
|
8452c8cc7f | ||
|
|
b177b2f0dc | ||
|
|
bd4df5fad0 | ||
|
|
b494d821b1 | ||
|
|
f63b033c72 | ||
|
|
4d782b8fad | ||
|
|
fc1bd60e49 | ||
|
|
3d25fe2e3d | ||
|
|
9093d35699 | ||
|
|
112a9871ee | ||
|
|
1c1ffa875e | ||
|
|
b718da4997 | ||
|
|
3f50a6f4ce | ||
|
|
1e61c5e366 | ||
|
|
3c1f6d96f6 | ||
|
|
6e46adeae2 | ||
|
|
a25c71d57e | ||
|
|
de99fd0caa | ||
|
|
ee0821d62f | ||
|
|
9cd037dfdd | ||
|
|
aaa828472f | ||
|
|
15443a32a1 | ||
|
|
9360abf011 | ||
|
|
7475a13e6a | ||
|
|
9d2df07b71 | ||
|
|
fe6e84553f | ||
|
|
7f80725ddc | ||
|
|
bc9ba8c8da | ||
|
|
7718b37ecf | ||
|
|
10efeb79d1 | ||
|
|
bdd9db4892 | ||
|
|
c0f61c5cb4 | ||
|
|
85a4d88e2a | ||
|
|
fd7efca993 | ||
|
|
742db7c701 | ||
|
|
9a47796ea3 | ||
|
|
30e470ec12 | ||
|
|
15060b8da4 | ||
|
|
62bbb917ca | ||
|
|
28d95ecf0c | ||
|
|
54cb56988b | ||
|
|
09dddd6f11 | ||
|
|
91590f39c3 | ||
|
|
161278cd30 | ||
|
|
117fb91d34 | ||
|
|
6227904dbd | ||
|
|
1b07ccd1b8 | ||
|
|
d8576eeb3b | ||
|
|
fe3251bf02 | ||
|
|
da4f94e9ef | ||
|
|
628818b2f1 | ||
|
|
c9cf8d080d | ||
|
|
7579eb32c9 | ||
|
|
edc818f036 | ||
|
|
3f4d577042 | ||
|
|
df23247bc8 | ||
|
|
c9246e28f4 | ||
|
|
4b2cc48687 | ||
|
|
3c910d2651 | ||
|
|
2f4f364a27 | ||
|
|
46714ce661 | ||
|
|
f768ba1c75 | ||
|
|
d5969dfb94 | ||
|
|
5bf0ff54d0 | ||
|
|
2def15c3f0 | ||
|
|
ac257b0ee9 | ||
|
|
3bdcae8928 | ||
|
|
c95ab91de1 | ||
|
|
b51338e308 | ||
|
|
29cfcec850 | ||
|
|
7c89e72c98 | ||
|
|
5c938034c8 | ||
|
|
a5f68ad7a6 | ||
|
|
1e7f897efb | ||
|
|
46de4f5911 | ||
|
|
b993e42c24 | ||
|
|
e51ea52ca4 | ||
|
|
79d02d1f3b | ||
|
|
709fe826dd | ||
|
|
f3a53ab812 | ||
|
|
003afb8a4c | ||
|
|
fc4f4fb5ed | ||
|
|
b74f76bdc4 | ||
|
|
0d7b7d0766 | ||
|
|
a5835794e5 | ||
|
|
db8c62eaf5 | ||
|
|
faaaa4b62a | ||
|
|
83232d67d1 | ||
|
|
5fd5b76e47 | ||
|
|
b57c18b664 | ||
|
|
888c2bc308 | ||
|
|
b2ab00eec7 | ||
|
|
5e6e2fdd83 | ||
|
|
1b10de0da7 | ||
|
|
61587437e5 | ||
|
|
bf21910fe4 | ||
|
|
99aa716c89 | ||
|
|
5484856fc0 | ||
|
|
2a013af660 | ||
|
|
2bf7cff7b4 | ||
|
|
fa41baa1c8 | ||
|
|
6558294943 | ||
|
|
85b8bfb57d | ||
|
|
9338a6ea61 | ||
|
|
65b2525ad7 | ||
|
|
13727dace4 | ||
|
|
b47c3f4b94 | ||
|
|
02dfb6319f | ||
|
|
9e28f2da81 | ||
|
|
749d2c0b66 | ||
|
|
67311104a5 | ||
|
|
bdd476b87e | ||
|
|
29bbdd4694 | ||
|
|
cd6bcb0890 | ||
|
|
4b6f25a743 | ||
|
|
26937bdd2b | ||
|
|
addb6db73e | ||
|
|
9c8d50656f | ||
|
|
d1333c2732 | ||
|
|
26b618aef7 | ||
|
|
05b60c9c7b | ||
|
|
d630bf5250 | ||
|
|
d3bcc68e2a | ||
|
|
7714f8f42c | ||
|
|
694e78eb39 | ||
|
|
883acbc190 | ||
|
|
70b021c228 | ||
|
|
1c1f622b7d | ||
|
|
16f1935531 | ||
|
|
a402b4e9b2 | ||
|
|
d269b25c17 | ||
|
|
7573b7c9fa | ||
|
|
56dfb0e67f | ||
|
|
3474158bf6 | ||
|
|
93a2f318e0 | ||
|
|
4a5ae97be5 | ||
|
|
186105c664 | ||
|
|
8df6973bc7 | ||
|
|
2cc76c588b | ||
|
|
8096c493e8 | ||
|
|
c0fe8c599b | ||
|
|
7cda1566e2 | ||
|
|
e83191f54e | ||
|
|
985dfe2749 | ||
|
|
e275737aa9 | ||
|
|
91b4825cca | ||
|
|
4c0a122240 | ||
|
|
db4060cf77 | ||
|
|
34addd59c5 | ||
|
|
8caddf1738 | ||
|
|
d9ca28ba43 | ||
|
|
a331a1951b | ||
|
|
336f1f8f35 | ||
|
|
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 |
+10
@@ -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
|
||||
@@ -360,6 +369,7 @@ miniapps/shifted/lsf_integral
|
||||
miniapps/tools/display-basis
|
||||
miniapps/tools/load-dc
|
||||
miniapps/tools/convert-dc
|
||||
miniapps/tools/compare-dc
|
||||
miniapps/tools/gridfunction-bounds
|
||||
miniapps/tools/lor-transfer
|
||||
miniapps/tools/plor-transfer
|
||||
|
||||
@@ -11,6 +11,22 @@
|
||||
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.
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
|
||||
capability.
|
||||
|
||||
|
||||
Version 4.9, released on Dec 11, 2025
|
||||
=====================================
|
||||
@@ -95,6 +111,23 @@ Linear and nonlinear solvers
|
||||
Filtering (AMGF), providing robust preconditioning for linear systems arising
|
||||
in constrained optimization problems such as frictionless contact.
|
||||
|
||||
Added 'GetResiduals' and 'GetFinalAbsResidualNorm' to 'HyprePCG',
|
||||
'HypreGMRES', and 'HypreFGMRES' to get 'r' and '|r|_p'. Note that the latter
|
||||
computes '|r|_p' from 'r' instead of returning a cached value like the
|
||||
relative 'GetFinalResidualNorm'. These require Hypre >= 2.15.0.
|
||||
|
||||
Changed the default solver parameters for 'HyprePCG' to 'tol=1e-6' and
|
||||
'max_iter=1000'. This matches the default parameters in Hypre 3.0.
|
||||
|
||||
Added various helper functions for querying/modifying Hypre solvers:
|
||||
'HypreSmoother::GetType', 'HypreSmoother::GetSOROptions',
|
||||
'HypreSmoother::GetPolyOptions', 'HypreSmoother::GetWindowParameters',
|
||||
'HypreSmoother::IsOperatorSymmetric', 'HyprePCG::GetTol',
|
||||
'HyprePCG::GetAbsTol', 'HyprePCG::GetMaxIter', 'HyprePCG::SetUseTwoNorm',
|
||||
'HypreGMRES::GetTol', 'HypreGMRES::GetAbsTol', 'HypreGMRES::GetMaxIter',
|
||||
'HypreGMRES::GetKDim', 'HypreFGMRES::GetTol', 'HypreFGMRES::GetMaxIter',
|
||||
'HypreFGMRES::GetKDim', and 'HypreBoomerAMG::GetMaxIter'.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Added the 'gpu', 'raja-gpu', and 'ceed-gpu' backend aliases/shortcuts which
|
||||
|
||||
+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
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@
|
||||
# Some choices below are based on the OS type:
|
||||
NOTMAC := $(subst Darwin,,$(shell uname -s))
|
||||
|
||||
ASTYLE_BIN = astyle
|
||||
ETAGS_BIN = $(shell command -v etags 2> /dev/null)
|
||||
EGREP_BIN = $(shell command -v egrep 2> /dev/null)
|
||||
|
||||
@@ -407,7 +408,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,8 @@ 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
|
||||
*
|
||||
* <H4>AmgX Examples</H4>
|
||||
* - Variants of Examples
|
||||
@@ -188,6 +190,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 +200,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 +239,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
|
||||
|
||||
+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
|
||||
|
||||
+5
-1
@@ -9,6 +9,7 @@
|
||||
// ex4 -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// ex4 -m ../data/escher.mesh
|
||||
// ex4 -m ../data/fichera.mesh -o 2 -hb
|
||||
// ex4 -m ../data/fichera.mesh -o 2 -hb -ea
|
||||
// ex4 -m ../data/fichera-q2.vtk
|
||||
// ex4 -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// ex4 -m ../data/square-disc-nurbs.mesh
|
||||
@@ -18,6 +19,7 @@
|
||||
// ex4 -m ../data/amr-quad.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb -ea
|
||||
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
|
||||
// ex4 -m ../data/ref-prism.mesh -o 1
|
||||
// ex4 -m ../data/octahedron.mesh -o 1
|
||||
@@ -25,6 +27,8 @@
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex4 -m ../data/star.mesh -pa -d cuda
|
||||
// ex4 -m ../data/star.mesh -hb -ea -d cuda
|
||||
// ex4 -m ../data/amr-quad.mesh -hb -ea -d cuda
|
||||
// ex4 -m ../data/star.mesh -pa -d raja-cuda
|
||||
// ex4 -m ../data/star.mesh -pa -d raja-omp
|
||||
// ex4 -m ../data/beam-hex.mesh -pa -d cuda
|
||||
@@ -193,7 +197,7 @@ int main(int argc, char *argv[])
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
if (!pa && (!ea || hybridization))
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
|
||||
+6
-1
@@ -9,6 +9,7 @@
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -o 2 -pa
|
||||
// mpirun -np 4 ex4p -m ../data/escher.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
|
||||
// mpirun -np 4 ex4p -m ../data/fichera-q3.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/square-disc-nurbs.mesh -o 3
|
||||
@@ -17,14 +18,18 @@
|
||||
// mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-quad.mesh
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb -ea
|
||||
// mpirun -np 4 ex4p -m ../data/ref-prism.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -ea -hb -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -ea -hb -d cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-cuda
|
||||
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-omp
|
||||
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -pa -d cuda
|
||||
@@ -230,7 +235,7 @@ int main(int argc, char *argv[])
|
||||
pcg->SetMaxIter(2000);
|
||||
pcg->SetPrintLevel(1);
|
||||
if (hybridization) { prec = new HypreBoomerAMG(*A.As<HypreParMatrix>()); }
|
||||
else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
|
||||
else if (pa || ea) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
|
||||
else
|
||||
{
|
||||
ParFiniteElementSpace *prec_fespace =
|
||||
|
||||
+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
|
||||
|
||||
+35
-6
@@ -825,14 +825,46 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
Vector &b, OperatorHandle &A, Vector &X,
|
||||
Vector &B, int copy_interior)
|
||||
{
|
||||
const SparseMatrix *P = fes->GetConformingProlongation();
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
if (ext)
|
||||
{
|
||||
if (hybridization)
|
||||
{
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
ConstrainedOperator A_constrained(this, ess_tdof_list);
|
||||
A_constrained.EliminateRHS(x, b);
|
||||
hybridization->ReduceRHS(b, B);
|
||||
|
||||
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
|
||||
{
|
||||
Operator *op;
|
||||
Operator::FormSystemOperator(ess_tdof_list, op);
|
||||
return dynamic_cast<ConstrainedOperator*>(op);
|
||||
}());
|
||||
MFEM_ASSERT(A_constrained != nullptr, "");
|
||||
|
||||
Vector conf_b, conf_x;
|
||||
if (P)
|
||||
{
|
||||
// Nonconforming
|
||||
conf_b.SetSize(P->Width());
|
||||
conf_x.SetSize(P->Width());
|
||||
P->MultTranspose(b, conf_b);
|
||||
R->Mult(x, conf_x);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Conforming
|
||||
conf_b.MakeRef(b, 0, b.Size());
|
||||
conf_x.MakeRef(x, 0, x.Size());
|
||||
}
|
||||
|
||||
A_constrained->EliminateRHS(conf_x, conf_b);
|
||||
|
||||
if (P)
|
||||
{
|
||||
R->MultTranspose(conf_b, b); // store eliminated rhs in b
|
||||
}
|
||||
|
||||
hybridization->ReduceRHS(conf_b, B);
|
||||
X.SetSize(B.Size());
|
||||
X = 0.0;
|
||||
}
|
||||
@@ -842,7 +874,6 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
}
|
||||
return;
|
||||
}
|
||||
const SparseMatrix *P = fes->GetConformingProlongation();
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
|
||||
// Transform the system and perform the elimination in B, based on the
|
||||
@@ -878,7 +909,6 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
if (hybridization)
|
||||
{
|
||||
// Reduction to the Lagrange multipliers system
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
Vector conf_b(P->Width()), conf_x(P->Width());
|
||||
P->MultTranspose(b, conf_b);
|
||||
R->Mult(x, conf_x);
|
||||
@@ -891,7 +921,6 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
|
||||
else
|
||||
{
|
||||
// Variational restriction with P
|
||||
const SparseMatrix *R = fes->GetConformingRestriction();
|
||||
B.SetSize(P->Width());
|
||||
P->MultTranspose(b, B);
|
||||
X.SetSize(R->Height());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -69,9 +69,9 @@ inline int ToLexOrdering2D(const int face_id, const int size1d, const int i)
|
||||
}
|
||||
|
||||
/// @brief Given a face DOF index on a shared face, ordered lexicographically
|
||||
/// relative to element the element (where the local face is face_id), and
|
||||
/// return the corresponding face DOF index ordered lexicographically relative
|
||||
/// to the face itself.
|
||||
/// relative to the element (where the local face is face_id), return the
|
||||
/// corresponding face DOF index ordered lexicographically relative to the face
|
||||
/// itself.
|
||||
MFEM_HOST_DEVICE
|
||||
inline int PermuteFace2D(const int face_id, const int orientation,
|
||||
const int size1d, const int index)
|
||||
|
||||
+22
-34
@@ -231,7 +231,7 @@ void FiniteElement::CalcPhysLaplacian(ElementTransformation &Trans,
|
||||
{
|
||||
for (int nd = 0; nd < dof; nd++)
|
||||
{
|
||||
Laplacian[nd] = hess(nd,0) + hess(nd,4) + hess(nd,5);
|
||||
Laplacian[nd] = hess(nd,0) + hess(nd,3) + hess(nd,5);
|
||||
}
|
||||
}
|
||||
else if (dim == 2)
|
||||
@@ -268,11 +268,9 @@ void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
|
||||
scale[0] = Gij(0,0);
|
||||
scale[1] = 2*Gij(0,1);
|
||||
scale[2] = 2*Gij(0,2);
|
||||
|
||||
scale[3] = 2*Gij(1,2);
|
||||
scale[4] = Gij(2,2);
|
||||
|
||||
scale[5] = Gij(1,1);
|
||||
scale[3] = Gij(1,1);
|
||||
scale[4] = 2*Gij(1,2);
|
||||
scale[5] = Gij(2,2);
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
@@ -309,12 +307,12 @@ void FiniteElement::CalcPhysHessian(ElementTransformation &Trans,
|
||||
map[2] = 2;
|
||||
|
||||
map[3] = 1;
|
||||
map[4] = 5;
|
||||
map[5] = 3;
|
||||
map[4] = 3;
|
||||
map[5] = 4;
|
||||
|
||||
map[6] = 2;
|
||||
map[7] = 3;
|
||||
map[8] = 4;
|
||||
map[7] = 4;
|
||||
map[8] = 5;
|
||||
}
|
||||
else if (dim == 2)
|
||||
{
|
||||
@@ -382,11 +380,7 @@ const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
|
||||
}
|
||||
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
|
||||
if (!d2q)
|
||||
{
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
@@ -661,14 +655,22 @@ void ScalarFiniteElement::ScalarLocalL2Restriction(
|
||||
void NodalFiniteElement::CreateLexicographicFullMap(const IntegrationRule &ir)
|
||||
const
|
||||
{
|
||||
// Get the FULL version of the map. This call contains omp critical region,
|
||||
// so it is done before the critical region below.
|
||||
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
|
||||
|
||||
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
// Get the FULL version of the map.
|
||||
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
|
||||
//Undo the native ordering which is what FiniteElement::GetDofToQuad returns.
|
||||
// If the new Dof2Quad is already present, e.g. added in a previous call
|
||||
// or added by another omp thread, return.
|
||||
if (DofToQuad::SearchArray(dof2quad_array, ir,
|
||||
DofToQuad::LEXICOGRAPHIC_FULL))
|
||||
{ return; }
|
||||
|
||||
// Undo the native ordering which is what FiniteElement::GetDofToQuad
|
||||
// returns.
|
||||
auto *d2q_new = new DofToQuad(d2q);
|
||||
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
|
||||
const int nqpt = ir.GetNPoints();
|
||||
@@ -724,13 +726,7 @@ const DofToQuad &NodalFiniteElement::GetDofToQuad(const IntegrationRule &ir,
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
//Should make this loop a function of FiniteElement
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule == &ir && d2q->mode == mode) { break; }
|
||||
d2q = nullptr;
|
||||
}
|
||||
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
|
||||
}
|
||||
if (d2q) { return *d2q; }
|
||||
if (mode != DofToQuad::LEXICOGRAPHIC_FULL)
|
||||
@@ -2631,15 +2627,7 @@ const DofToQuad &TensorBasisElement::GetTensorDofToQuad(
|
||||
#pragma omp critical (DofToQuad)
|
||||
#endif
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
auto* d2q_ = dof2quad_array[i];
|
||||
if (d2q_->IntRule == &ir && d2q_->mode == mode)
|
||||
{
|
||||
d2q = d2q_;
|
||||
break;
|
||||
}
|
||||
}
|
||||
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
|
||||
if (!d2q)
|
||||
{
|
||||
d2q = new DofToQuad;
|
||||
|
||||
+25
-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)
|
||||
{
|
||||
@@ -222,6 +222,12 @@ public:
|
||||
|
||||
/// Returns absolute value of the maps
|
||||
DofToQuad Abs() const;
|
||||
|
||||
/// Auxiliary function for searching DofToQuad arrays.
|
||||
static inline DofToQuad *SearchArray(
|
||||
const Array<DofToQuad*> &dof2quad_array,
|
||||
const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode);
|
||||
};
|
||||
|
||||
/// Describes the function space on each element
|
||||
@@ -407,6 +413,7 @@ public:
|
||||
/** Each row of the result DenseMatrix @a Hessian contains upper triangular
|
||||
part of the Hessian of one shape function.
|
||||
The order in 2D is {u_xx, u_xy, u_yy}.
|
||||
The order in 3D is {u_xx, u_xy, u_xz, u_yy, u_yz, u_zz}.
|
||||
The size (#dof x (#dim (#dim+1)/2) of @a Hessian must be set in advance.*/
|
||||
virtual void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &Hessian) const;
|
||||
@@ -1120,7 +1127,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)
|
||||
@@ -1376,6 +1383,21 @@ public:
|
||||
void InvertLinearTrans(ElementTransformation &trans,
|
||||
const IntegrationPoint &pt, Vector &x);
|
||||
|
||||
|
||||
// static inline method
|
||||
inline DofToQuad *DofToQuad::SearchArray(
|
||||
const Array<DofToQuad*> &dof2quad_array,
|
||||
const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode)
|
||||
{
|
||||
for (int i = 0; i < dof2quad_array.Size(); i++)
|
||||
{
|
||||
DofToQuad *d2q = dof2quad_array[i];
|
||||
if (d2q->IntRule == &ir && d2q->mode == mode) { return d2q; }
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
|
||||
@@ -60,6 +60,12 @@ void Linear1DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
dshape(1,0) = 1.;
|
||||
}
|
||||
|
||||
void Linear1DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
h = 0.0;
|
||||
}
|
||||
|
||||
Linear2DFiniteElement::Linear2DFiniteElement()
|
||||
: NodalFiniteElement(2, Geometry::TRIANGLE, 3, 1)
|
||||
{
|
||||
@@ -87,6 +93,11 @@ void Linear2DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
dshape(2,0) = 0.; dshape(2,1) = 1.;
|
||||
}
|
||||
|
||||
void Linear2DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
h = 0.0;
|
||||
}
|
||||
|
||||
BiLinear2DFiniteElement::BiLinear2DFiniteElement()
|
||||
: NodalFiniteElement(2, Geometry::SQUARE, 4, 1, FunctionSpace::Qk)
|
||||
@@ -1256,6 +1267,12 @@ void Linear3DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
}
|
||||
}
|
||||
|
||||
void Linear3DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
h = 0.0;
|
||||
}
|
||||
|
||||
void Linear3DFiniteElement::GetFaceDofs (int face, int **dofs, int *ndofs)
|
||||
const
|
||||
{
|
||||
@@ -1632,6 +1649,37 @@ void TriLinear3DFiniteElement::CalcDShape(const IntegrationPoint &ip,
|
||||
dshape(7,2) = ox * y;
|
||||
}
|
||||
|
||||
void TriLinear3DFiniteElement::CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const
|
||||
{
|
||||
real_t x = ip.x, y = ip.y, z = ip.z;
|
||||
real_t ox = 1.-x, oy = 1.-y, oz = 1.-z;
|
||||
|
||||
h(0,0) = 0.; h(0,1) = oz; h(0,2) = oy;
|
||||
h(0,3) = 0.; h(0,4) = ox; h(0,5) = 0.;
|
||||
|
||||
h(1,0) = 0.; h(1,1) = -oz; h(1,2) = -oy;
|
||||
h(1,3) = 0.; h(1,4) = x; h(1,5) = 0.;
|
||||
|
||||
h(2,0) = 0.; h(2,1) = oz; h(2,2) = -y;
|
||||
h(2,3) = 0.; h(2,4) = -x; h(2,5) = 0.;
|
||||
|
||||
h(3,0) = 0.; h(3,1) = -oz; h(3,2) = y;
|
||||
h(3,3) = 0.; h(3,4) = -ox; h(3,5) = 0.;
|
||||
|
||||
h(4,0) = 0.; h(4,1) = z; h(4,2) = -oy;
|
||||
h(4,3) = 0.; h(4,4) = -ox; h(4,5) = 0.;
|
||||
|
||||
h(5,0) = 0.; h(5,1) = -z; h(5,2) = oy;
|
||||
h(5,3) = 0.; h(5,4) = -x; h(5,5) = 0.;
|
||||
|
||||
h(6,0) = 0.; h(6,1) = z; h(6,2) = y;
|
||||
h(6,3) = 0.; h(6,4) = x; h(6,5) = 0.;
|
||||
|
||||
h(7,0) = 0.; h(7,1) = -z; h(7,2) = -y;
|
||||
h(7,3) = 0.; h(7,4) = ox; h(7,5) = 0.;
|
||||
}
|
||||
|
||||
|
||||
P0SegmentFiniteElement::P0SegmentFiniteElement(int Ord)
|
||||
: NodalFiniteElement(1, Geometry::SEGMENT, 1, Ord) // default Ord = 0
|
||||
|
||||
@@ -50,6 +50,8 @@ public:
|
||||
contains the derivative of one shape function */
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
};
|
||||
|
||||
/// A 2D linear element on triangle with nodes at the vertices of the triangle
|
||||
@@ -70,6 +72,8 @@ public:
|
||||
so that each row contains the derivatives of one shape function */
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
void ProjectDelta(int vertex, Vector &dofs) const override
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
};
|
||||
@@ -404,6 +408,9 @@ public:
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
|
||||
void ProjectDelta(int vertex, Vector &dofs) const override
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
|
||||
@@ -445,7 +452,8 @@ public:
|
||||
so that each row contains the derivatives of one shape function */
|
||||
void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const override;
|
||||
|
||||
void CalcHessian(const IntegrationPoint &ip,
|
||||
DenseMatrix &h) const override;
|
||||
void ProjectDelta(int vertex, Vector &dofs) const override
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
};
|
||||
|
||||
+519
-5
@@ -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
|
||||
{
|
||||
@@ -348,11 +445,10 @@ void NURBS3DFiniteElement::CalcHessian (const IntegrationPoint &ip,
|
||||
d2sum[0] += ( hessian(o,0) = d2sx*sy*sz*weights(o) );
|
||||
d2sum[1] += ( hessian(o,1) = dsx*dsy*sz*weights(o) );
|
||||
d2sum[2] += ( hessian(o,2) = dsx*sy*dsz*weights(o) );
|
||||
d2sum[3] += ( hessian(o,3) = sx*d2sy*sz*weights(o) );
|
||||
d2sum[4] += ( hessian(o,4) = sx*dsy*dsz*weights(o) );
|
||||
d2sum[5] += ( hessian(o,5) = sx*sy*d2sz*weights(o) );
|
||||
|
||||
d2sum[3] += ( hessian(o,3) = sx*dsy*dsz*weights(o) );
|
||||
|
||||
d2sum[4] += ( hessian(o,4) = sx*sy*d2sz*weights(o) );
|
||||
d2sum[5] += ( hessian(o,5) = sx*d2sy*sz*weights(o) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,6 +497,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 +692,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 +928,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 +1163,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 +1404,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 |
|
||||
|
||||
|
||||
+50
-13
@@ -1516,36 +1516,76 @@ const FaceRestriction *FiniteElementSpace::GetFaceRestriction(
|
||||
const bool is_dg_space = IsDGSpace();
|
||||
const L2FaceValues m = (is_dg_space && mul==L2FaceValues::DoubleValued) ?
|
||||
L2FaceValues::DoubleValued : L2FaceValues::SingleValued;
|
||||
key_face key = std::make_tuple(is_dg_space, f_ordering, type, m);
|
||||
auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
|
||||
auto itr = L2F.find(key);
|
||||
if (itr != L2F.end())
|
||||
{
|
||||
return itr->second;
|
||||
return itr->second.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
FaceRestriction *res;
|
||||
std::unique_ptr<FaceRestriction> res;
|
||||
if (is_dg_space)
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
res = new L2FaceRestriction(*this, f_ordering, type, m);
|
||||
res.reset(new L2FaceRestriction(*this, f_ordering, type, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new NCL2FaceRestriction(*this, f_ordering, type, m);
|
||||
res.reset(new NCL2FaceRestriction(*this, f_ordering, type, m));
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
|
||||
{
|
||||
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new ConformingFaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
L2F[key] = res;
|
||||
return res;
|
||||
return L2F.emplace(key, std::move(res)).first->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
const InterpolationManager &FiniteElementSpace::GetInterpolationManager(
|
||||
ElementDofOrdering f_ordering, FaceType type) const
|
||||
{
|
||||
const auto key = make_tuple(f_ordering, type);
|
||||
|
||||
auto it = interpolations.find(key);
|
||||
if (it != interpolations.end())
|
||||
{
|
||||
return *it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto interp = make_unique<InterpolationManager>(*this, f_ordering, type);
|
||||
|
||||
int face_idx = 0;
|
||||
for (int f = 0; f < mesh->GetNumFacesWithGhost(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation face = mesh->GetFaceInformation(f);
|
||||
if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (face.IsConforming() || face.IsBoundary())
|
||||
{
|
||||
interp->RegisterFaceConformingInterpolation(face, face_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
interp->RegisterFaceCoarseToFineInterpolation(face, face_idx);
|
||||
}
|
||||
++face_idx;
|
||||
}
|
||||
|
||||
// Transform the interpolation matrix map into contiguous memory.
|
||||
interp->LinearizeInterpolatorMapIntoVector();
|
||||
interp->InitializeNCInterpConfig();
|
||||
|
||||
return *interpolations.emplace(key, std::move(interp)).first->second;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3969,11 +4009,8 @@ void FiniteElementSpace::Destroy()
|
||||
delete E2Q_array[i];
|
||||
}
|
||||
E2Q_array.SetSize(0);
|
||||
for (auto &x : L2F)
|
||||
{
|
||||
delete x.second;
|
||||
}
|
||||
L2F.clear();
|
||||
interpolations.clear();
|
||||
for (int i = 0; i < E2IFQ_array.Size(); i++)
|
||||
{
|
||||
delete E2IFQ_array[i];
|
||||
|
||||
+9
-12
@@ -13,6 +13,7 @@
|
||||
#define MFEM_FESPACE
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/hash_util.hpp"
|
||||
#include "../linalg/ordering.hpp"
|
||||
#include "../linalg/sparsemat.hpp"
|
||||
#include "../mesh/mesh.hpp"
|
||||
@@ -320,18 +321,11 @@ protected:
|
||||
mutable OperatorHandle L2E_nat, L2E_lex;
|
||||
/// The face restriction operators, see GetFaceRestriction().
|
||||
using key_face = std::tuple<bool, ElementDofOrdering, FaceType, L2FaceValues>;
|
||||
struct key_hash
|
||||
{
|
||||
std::size_t operator()(const key_face& k) const
|
||||
{
|
||||
return std::get<0>(k)
|
||||
+ 2 * (int)std::get<1>(k)
|
||||
+ 4 * (int)std::get<2>(k)
|
||||
+ 8 * (int)std::get<3>(k);
|
||||
}
|
||||
};
|
||||
using map_L2F = std::unordered_map<const key_face,FaceRestriction*,key_hash>;
|
||||
mutable map_L2F L2F;
|
||||
mutable std::unordered_map<key_face,std::unique_ptr<FaceRestriction>,
|
||||
TupleHasher> L2F;
|
||||
|
||||
mutable std::unordered_map<std::tuple<ElementDofOrdering,FaceType>,
|
||||
std::unique_ptr<InterpolationManager>, TupleHasher> interpolations;
|
||||
|
||||
mutable Array<QuadratureInterpolator*> E2Q_array;
|
||||
mutable Array<FaceQuadratureInterpolator*> E2IFQ_array;
|
||||
@@ -751,6 +745,9 @@ public:
|
||||
ElementDofOrdering f_ordering, FaceType,
|
||||
L2FaceValues mul = L2FaceValues::DoubleValued) const;
|
||||
|
||||
const InterpolationManager &GetInterpolationManager(
|
||||
ElementDofOrdering f_ordering, FaceType type) const;
|
||||
|
||||
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
|
||||
quadrature point values and/or derivatives (Q-vectors). */
|
||||
/** An E-vector represents the element-wise discontinuous version of the FE
|
||||
|
||||
+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
|
||||
|
||||
+59
-32
@@ -234,7 +234,7 @@ void FindPointsGSLIB::Setup(Mesh &m, const double bb_t, const double newt_tol,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
int point_pos_ordering)
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Use FindPointsGSLIB::Setup before finding points.");
|
||||
bool dev_mode = (point_pos.UseDevice() && Device::IsEnabled());
|
||||
@@ -482,7 +482,7 @@ void FindPointsGSLIB::SetupDevice()
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
int point_pos_ordering)
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
if (!DEV.setup_device)
|
||||
{
|
||||
@@ -505,13 +505,13 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
|
||||
if (dim == 2)
|
||||
{
|
||||
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
|
||||
gsl_dist, points_cnt);
|
||||
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem,
|
||||
gsl_ref, gsl_dist, points_cnt);
|
||||
}
|
||||
else
|
||||
{
|
||||
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
|
||||
gsl_dist, points_cnt);
|
||||
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem,
|
||||
gsl_ref, gsl_dist, points_cnt);
|
||||
}
|
||||
|
||||
// Sync from device to host
|
||||
@@ -1085,7 +1085,7 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
|
||||
#else
|
||||
void FindPointsGSLIB::SetupDevice() {};
|
||||
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
|
||||
int point_pos_ordering) {};
|
||||
const int point_pos_ordering) {};
|
||||
void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
|
||||
Vector &field_out,
|
||||
const int nel, const int ncomp,
|
||||
@@ -1094,7 +1094,8 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
|
||||
#endif
|
||||
|
||||
void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
|
||||
int point_pos_ordering, const double bb_t,
|
||||
const int point_pos_ordering,
|
||||
const double bb_t,
|
||||
const double newt_tol, const int npt_max)
|
||||
{
|
||||
if (!setupflag || (mesh != &m) )
|
||||
@@ -1105,16 +1106,28 @@ void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering)
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
FindPoints(point_pos, point_pos_ordering);
|
||||
Interpolate(field_in, field_out);
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
FindPoints(point_pos, point_pos_ordering);
|
||||
Interpolate(field_in, field_out, field_out_ordering);
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(Mesh &m, const Vector &point_pos,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering)
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
FindPoints(m, point_pos, point_pos_ordering);
|
||||
Interpolate(field_in, field_out);
|
||||
@@ -1470,7 +1483,7 @@ void FindPointsGSLIB::SetupSplitMeshesAndIntegrationRules(const int order)
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
|
||||
Vector &node_vals)
|
||||
Vector &node_vals) const
|
||||
{
|
||||
const GridFunction *nodes = gf_in;
|
||||
const FiniteElementSpace *fes = nodes->FESpace();
|
||||
@@ -1758,6 +1771,13 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
Vector &field_out)
|
||||
{
|
||||
Interpolate(field_in, field_out, field_in.FESpace()->GetOrdering());
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
const int gf_order = field_in.FESpace()->GetMaxElementOrder(),
|
||||
mesh_order = mesh->GetNodalFESpace()->GetMaxElementOrder();
|
||||
@@ -1800,7 +1820,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
const int maxOrder = field_in.FESpace()->GetMaxElementOrder();
|
||||
|
||||
InterpolateOnDevice(node_vals, field_out, NE_split_total, ncomp,
|
||||
maxOrder+1, field_in.FESpace()->GetOrdering());
|
||||
maxOrder+1, field_out_ordering);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
@@ -1812,12 +1832,13 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
field_in.FESpace()->IsVariableOrder() ==
|
||||
mesh->GetNodalFESpace()->IsVariableOrder())
|
||||
{
|
||||
InterpolateH1(field_in, field_out);
|
||||
InterpolateH1(field_in, field_out, field_out_ordering);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
InterpolateGeneral(field_in, field_out);
|
||||
InterpolateGeneral(field_in, field_out,
|
||||
field_out_ordering);
|
||||
if (!fec_l2 || avgtype == AvgType::NONE) { return; }
|
||||
}
|
||||
|
||||
@@ -1861,11 +1882,11 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
|
||||
if (gf_order_h1 == mesh_order) // basis is GaussLobatto by default
|
||||
{
|
||||
InterpolateH1(field_in_h1, field_out_l2);
|
||||
InterpolateH1(field_in_h1, field_out_l2, field_out_ordering);
|
||||
}
|
||||
else
|
||||
{
|
||||
InterpolateGeneral(field_in_h1, field_out_l2);
|
||||
InterpolateGeneral(field_in_h1, field_out_l2, field_out_ordering);
|
||||
}
|
||||
|
||||
// Copy interpolated values for the points on element border
|
||||
@@ -1873,7 +1894,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
{
|
||||
for (int i = 0; i < indl2.Size(); i++)
|
||||
{
|
||||
int idx = field_in_h1.FESpace()->GetOrdering() == Ordering::byNODES?
|
||||
int idx = field_out_ordering == Ordering::byNODES?
|
||||
indl2[i] + j*points_cnt:
|
||||
indl2[i]*ncomp + j;
|
||||
field_out(idx) = field_out_l2(idx);
|
||||
@@ -1883,7 +1904,8 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
Vector &field_out)
|
||||
Vector &field_out,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
FiniteElementSpace ind_fes(mesh, field_in.FESpace()->FEColl());
|
||||
if (field_in.FESpace()->IsVariableOrder())
|
||||
@@ -1913,7 +1935,8 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
dataptrout = i*points_cnt;
|
||||
if (field_in.FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin, points_fld);
|
||||
field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin,
|
||||
points_fld);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1945,7 +1968,7 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
(gslib::findpts_data_3 *)this->fdataD);
|
||||
}
|
||||
}
|
||||
if (field_in.FESpace()->GetOrdering() == Ordering::byVDIM)
|
||||
if (field_out_ordering == Ordering::byVDIM)
|
||||
{
|
||||
Vector field_out_temp = field_out;
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
@@ -1959,7 +1982,8 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
Vector &field_out)
|
||||
Vector &field_out,
|
||||
const int field_out_ordering)
|
||||
{
|
||||
int ncomp = field_in.VectorDim(),
|
||||
nptorig = points_cnt,
|
||||
@@ -1979,7 +2003,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
if (dim == 3) { ip.z = gsl_mfem_ref(index*dim + 2); }
|
||||
Vector localval(ncomp);
|
||||
field_in.GetVectorValue(gsl_mfem_elem[index], ip, localval);
|
||||
if (field_in.FESpace()->GetOrdering() == Ordering::byNODES)
|
||||
if (field_out_ordering == Ordering::byNODES)
|
||||
{
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
{
|
||||
@@ -2014,7 +2038,10 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
for (int index = 0; index < npt; index++)
|
||||
{
|
||||
if (gsl_code[index] == 2) { continue; }
|
||||
for (int d = 0; d < dim; ++d) { pt->r[d]= gsl_mfem_ref(index*dim + d); }
|
||||
for (int d = 0; d < dim; ++d)
|
||||
{
|
||||
pt->r[d]= gsl_mfem_ref(index*dim + d);
|
||||
}
|
||||
pt->index = index;
|
||||
pt->proc = gsl_proc[index];
|
||||
pt->el = gsl_mfem_elem[index];
|
||||
@@ -2104,7 +2131,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
|
||||
sdpt = (struct send_pt *)sendpt->ptr;
|
||||
for (int index = 0; index < static_cast<int>(sendpt->n); index++)
|
||||
{
|
||||
int idx = field_in.FESpace()->GetOrdering() == Ordering::byNODES ?
|
||||
int idx = field_out_ordering == Ordering::byNODES ?
|
||||
sdpt->index + j*nptorig :
|
||||
sdpt->index*ncomp + j;
|
||||
field_out(idx) = sdpt->ival;
|
||||
@@ -2246,7 +2273,7 @@ void FindPointsGSLIB::DistributeInterpolatedValues(const Vector &int_vals,
|
||||
}
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb)
|
||||
void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb) const
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
|
||||
auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
|
||||
@@ -2317,7 +2344,7 @@ void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb)
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
|
||||
Vector &obbV)
|
||||
Vector &obbV) const
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
|
||||
auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
|
||||
@@ -2502,8 +2529,8 @@ void OversetFindPointsGSLIB::Setup(Mesh &m, const int meshid,
|
||||
}
|
||||
|
||||
void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
Array<unsigned int> &point_id,
|
||||
int point_pos_ordering)
|
||||
const Array<unsigned int> &point_id,
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
MFEM_VERIFY(setupflag, "Use OversetFindPointsGSLIB::Setup before "
|
||||
"finding points.");
|
||||
@@ -2582,10 +2609,10 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
|
||||
}
|
||||
|
||||
void OversetFindPointsGSLIB::Interpolate(const Vector &point_pos,
|
||||
Array<unsigned int> &point_id,
|
||||
const Array<unsigned int> &point_id,
|
||||
const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
int point_pos_ordering)
|
||||
const int point_pos_ordering)
|
||||
{
|
||||
FindPoints(point_pos, point_id, point_pos_ordering);
|
||||
Interpolate(field_in, field_out);
|
||||
|
||||
+32
-15
@@ -119,11 +119,13 @@ protected:
|
||||
} DEV;
|
||||
|
||||
/// Use GSLIB for communication and interpolation
|
||||
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out);
|
||||
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
/// Uses GSLIB Crystal Router for communication followed by MFEM's
|
||||
/// interpolation functions
|
||||
virtual void InterpolateGeneral(const GridFunction &field_in,
|
||||
Vector &field_out);
|
||||
Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
|
||||
/// Since GSLIB is designed to work with quads/hexes, we split every
|
||||
/// triangle/tet/prism/pyramid element into quads/hexes.
|
||||
@@ -140,7 +142,7 @@ protected:
|
||||
virtual void SetupSplitMeshesAndIntegrationRules(const int order);
|
||||
|
||||
/// Get GridFunction value at the points expected by GSLIB.
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals);
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals) const;
|
||||
|
||||
/// Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For simplices,
|
||||
/// find the original element number (that was split into micro quads/hexes)
|
||||
@@ -182,7 +184,7 @@ protected:
|
||||
These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) specified by @a point_pos_ordering. */
|
||||
void FindPointsOnDevice(const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** Interpolation of field values at prescribed reference space positions.
|
||||
@param[in] field_in_evec E-vector of grid function to be interpolated.
|
||||
@@ -253,10 +255,15 @@ public:
|
||||
#gsl_dist Distance between the sought and the found point
|
||||
in physical space. */
|
||||
void FindPoints(const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
/// Convenience function when point positions are in a ParticleVector
|
||||
void FindPoints(const ParticleVector &point_pos)
|
||||
{
|
||||
FindPoints(point_pos, point_pos.GetOrdering());
|
||||
}
|
||||
/// Setup FindPoints and search positions
|
||||
void FindPoints(Mesh &m, const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES,
|
||||
const int point_pos_ordering = Ordering::byNODES,
|
||||
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
@@ -266,20 +273,28 @@ public:
|
||||
\p field_in is in H1 and in the same space as the
|
||||
mesh that was given to Setup().
|
||||
@param[out] field_out Interpolated values. For points that are not found
|
||||
the value is set to #default_interp_value. */
|
||||
the value is set to #default_interp_value.
|
||||
The output ordering is determined from field_in.*/
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out);
|
||||
/// Interpolation of field values, with output ordering specification.
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
/** Search positions and interpolate. The ordering (byNODES or byVDIM) of
|
||||
the output values in \p field_out corresponds to the ordering used
|
||||
in the input GridFunction \p field_in. */
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
/// Search positions and interpolate with given point and output ordering.
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out, const int point_pos_ordering,
|
||||
const int field_out_ordering);
|
||||
/** Setup FindPoints, search positions and interpolate. The ordering (byNODES
|
||||
or byVDIM) of the output values in \p field_out corresponds to the
|
||||
ordering used in the input GridFunction \p field_in. */
|
||||
void Interpolate(Mesh &m, const Vector &point_pos,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/// Average type to be used for L2 functions in-case a point is located at
|
||||
/// an element boundary where the function might be multi-valued.
|
||||
@@ -376,7 +391,7 @@ public:
|
||||
/// The size of the returned vector is (nel x nverts x dim), where nel is the
|
||||
/// number of elements (after splitting for simplcies), nverts is number of
|
||||
/// vertices (4 in 2D, 8 in 3D), and dim is the spatial dimension.
|
||||
void GetAxisAlignedBoundingBoxes(Vector &aabb);
|
||||
void GetAxisAlignedBoundingBoxes(Vector &aabb) const;
|
||||
|
||||
/// Return the oriented bounding boxes (OBB) computed during \ref Setup.
|
||||
/// Each OBB is represented using the inverse transformation (A^{-1}) and
|
||||
@@ -386,7 +401,8 @@ public:
|
||||
/// size (dim x dim x nel), and the OBB centers are returned in \p obbC,
|
||||
/// a vector of size (nel x dim). The vertices of the OBBs are returned in
|
||||
/// \p obbV, a vector of size (nel x nverts x dim) .
|
||||
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC, Vector &obbV);
|
||||
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
|
||||
Vector &obbV) const;
|
||||
};
|
||||
|
||||
/** \brief OversetFindPointsGSLIB enables use of findpts for arbitrary number of
|
||||
@@ -446,13 +462,14 @@ public:
|
||||
byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) */
|
||||
void FindPoints(const Vector &point_pos,
|
||||
Array<unsigned int> &point_id,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const Array<unsigned int> &point_id,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** Search positions and interpolate */
|
||||
void Interpolate(const Vector &point_pos, Array<unsigned int> &point_id,
|
||||
void Interpolate(const Vector &point_pos,
|
||||
const Array<unsigned int> &point_id,
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
using FindPointsGSLIB::Interpolate;
|
||||
};
|
||||
|
||||
|
||||
@@ -789,7 +789,6 @@ void Hybridization::ComputeH()
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: add ones on the diagonal of zero rows
|
||||
V->Finalize();
|
||||
Array<HYPRE_BigInt> V_J(V->NumNonZeroElems());
|
||||
MFEM_ASSERT(c_pfes, "");
|
||||
@@ -823,6 +822,13 @@ void Hybridization::ComputeH()
|
||||
MFEM_VERIFY(pH.Type() != Operator::PETSC_MATIS, "To be implemented");
|
||||
pH.MakePtAP(plpH, pP);
|
||||
delete lpH;
|
||||
|
||||
HypreParMatrix *hH = pH.As<HypreParMatrix>();
|
||||
MFEM_ASSERT(hH, "");
|
||||
|
||||
SparseMatrix H_diag;
|
||||
hH->GetDiag(H_diag);
|
||||
H_diag.SetDiagIdentity();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
+455
-275
File diff suppressed because it is too large
Load Diff
@@ -14,8 +14,11 @@
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/array.hpp"
|
||||
#include "../linalg/operator.hpp"
|
||||
#include "../linalg/vector.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -45,15 +48,30 @@ protected:
|
||||
Array<int> hat_dof_gather_map;
|
||||
Array<DofType> hat_dof_marker;
|
||||
|
||||
Array<int> el_to_face;
|
||||
Array<int> face_to_el;
|
||||
Array<int> el_to_face; ///< Element to face connectivity.
|
||||
Array<int> el_face_offsets; ///< Per-element offsets into @a el_to_face.
|
||||
Array<int> face_to_el; ///< Face-to-element connectivity.
|
||||
Array<int> face_face_offsets; ///< Face-to-face offsets.
|
||||
|
||||
int n_el_face; ///< Total number of element-to-face connections.
|
||||
int n_face_face; ///< Total number of face-to-face connections.
|
||||
|
||||
Vector Ct_mat; ///< Constraint matrix (transposed) stored element-wise.
|
||||
|
||||
/// @name For parallel non-conforming meshes
|
||||
///@{
|
||||
std::unique_ptr<Operator> P_pc; ///< Partially conforming prolongation.
|
||||
std::unique_ptr<Operator> P_nbr; ///< Face-neighbor prolongation.
|
||||
///@}
|
||||
|
||||
Array<int> idofs, bdofs;
|
||||
|
||||
Vector Ahat, Ahat_ii, Ahat_ib, Ahat_bi, Ahat_bb;
|
||||
Array<int> Ahat_ii_piv, Ahat_bb_piv;
|
||||
|
||||
/// Return the (partially) conforming prolongation on the constraint space.
|
||||
const Operator &GetProlongation() const;
|
||||
|
||||
public:
|
||||
/// Construct the constraint matrix.
|
||||
void ConstructC();
|
||||
|
||||
@@ -28,7 +28,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
const FaceType ftype = FaceType::Interior;
|
||||
const int nf = mesh.GetNFbyType(ftype);
|
||||
|
||||
const Geometry::Type geom = mesh.GetFaceGeometry(0);
|
||||
const Geometry::Type geom = mesh.GetTypicalFaceGeometry();
|
||||
const int trial_order = trial_fes.GetMaxElementOrder();
|
||||
const int test_order = test_fes.GetMaxElementOrder();
|
||||
const int qorder = test_order + trial_order - 1;
|
||||
@@ -47,7 +47,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
});
|
||||
}
|
||||
|
||||
const FiniteElement &trial_face_el = *trial_fes.GetFaceElement(0);
|
||||
const FiniteElement &trial_face_el = *trial_fes.GetTypicalTraceElement();
|
||||
const auto maps = &trial_face_el.GetDofToQuad(ir, DofToQuad::TENSOR);
|
||||
const int ndof_face = trial_face_el.GetDof();
|
||||
|
||||
@@ -72,7 +72,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
MFEM_ABORT("Unknown kernel.");
|
||||
}
|
||||
|
||||
const FiniteElement &test_el = *test_fes.GetFE(0);
|
||||
const FiniteElement &test_el = *test_fes.GetTypicalFE();
|
||||
const int n_faces_per_el = 2*dim; // assuming tensor product
|
||||
// Get all the local face maps (mapping from lexicographic face index to
|
||||
// lexicographic volume index, depending on the local face index).
|
||||
@@ -90,10 +90,10 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
Array<int> face_info(nf * 4);
|
||||
{
|
||||
int fidx = 0;
|
||||
for (int f = 0; f < mesh.GetNumFaces(); ++f)
|
||||
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation finfo = mesh.GetFaceInformation(f);
|
||||
if (!finfo.IsInterior()) { continue; }
|
||||
if (!finfo.IsInterior() || finfo.IsNonconformingCoarse()) { continue; }
|
||||
face_info[0 + fidx*4] = finfo.element[0].local_face_id;
|
||||
face_info[1 + fidx*4] = finfo.element[0].orientation;
|
||||
face_info[2 + fidx*4] = finfo.element[1].local_face_id;
|
||||
@@ -114,7 +114,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
else
|
||||
{
|
||||
d_emat = emat.Write();
|
||||
mfem::forall(emat.Size(), [=] MFEM_HOST_DEVICE (int i) { d_emat[i] = 0.0; });
|
||||
emat = 0.0; // Will execute on device, since Write() sets the device flag
|
||||
}
|
||||
|
||||
const auto face_mats = Reshape(mass_emat.Read(), ndof_face, ndof_face, nf);
|
||||
@@ -133,26 +133,104 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
|
||||
}
|
||||
};
|
||||
|
||||
mfem::forall_3D(nf, ndof_face, ndof_face, 2, [=] MFEM_HOST_DEVICE (int f)
|
||||
auto permute_face_2 = [=] MFEM_HOST_DEVICE(int local_face_1, int local_face_2,
|
||||
int orient, int size1d, int index)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(el_i, z, 2)
|
||||
if (dim == 2)
|
||||
{
|
||||
const int lf_i = d_face_info(0, el_i, f);
|
||||
const int orient = d_face_info(1, el_i, f);
|
||||
// Loop over face indices in "native ordering"
|
||||
MFEM_FOREACH_THREAD(i_lex, x, ndof_face)
|
||||
return internal::PermuteFace2D(local_face_1, local_face_2, orient,
|
||||
size1d, index);
|
||||
}
|
||||
else // dim == 3
|
||||
{
|
||||
return internal::PermuteFace3D(local_face_1, local_face_2, orient,
|
||||
size1d, index);
|
||||
}
|
||||
};
|
||||
|
||||
if (mesh.Conforming())
|
||||
{
|
||||
mfem::forall_3D(nf, ndof_face, ndof_face, 2, [=] MFEM_HOST_DEVICE (int f)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(el_i, z, 2)
|
||||
{
|
||||
// Convert to lexicographic relative to the face itself
|
||||
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i = d_face_maps(i_lex, lf_i);
|
||||
MFEM_FOREACH_THREAD(j, y, ndof_face)
|
||||
const int lf_i = d_face_info(0, el_i, f);
|
||||
const int orient = d_face_info(1, el_i, f);
|
||||
// Loop over face indices in "native ordering"
|
||||
MFEM_FOREACH_THREAD(i_lex, x, ndof_face)
|
||||
{
|
||||
el_mats(i, j, el_i, f) += face_mats(i_face, j, f);
|
||||
// Convert to lexicographic relative to the face itself
|
||||
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i = d_face_maps(i_lex, lf_i);
|
||||
MFEM_FOREACH_THREAD(j, y, ndof_face)
|
||||
{
|
||||
el_mats(i, j, el_i, f) += face_mats(i_face, j, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
const InterpolationManager &interp =
|
||||
test_fes.GetInterpolationManager(ElementDofOrdering::LEXICOGRAPHIC, ftype);
|
||||
|
||||
auto interp_configs = interp.GetFaceInterpConfig().Read();
|
||||
const int nc_size = interp.GetNumInterpolators();
|
||||
auto d_interp = Reshape(interp.GetInterpolators().Read(),
|
||||
ndof_face, ndof_face, nc_size);
|
||||
|
||||
mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
|
||||
{
|
||||
const InterpConfig conf = interp_configs[f];
|
||||
const int master_side = conf.master_side;
|
||||
const int interp_index = conf.index;
|
||||
|
||||
const int lf_0 = d_face_info(0, 0, f);
|
||||
|
||||
for (int el_i = 0; el_i < 2; ++el_i)
|
||||
{
|
||||
const int lf_i = d_face_info(0, el_i, f);
|
||||
const int orient = d_face_info(1, el_i, f);
|
||||
|
||||
for (int j = 0; j < ndof_face; j++)
|
||||
{
|
||||
for (int i_lex = 0; i_lex < ndof_face; i_lex++)
|
||||
{
|
||||
real_t val = 0.0;
|
||||
if (conf.is_non_conforming && el_i == master_side)
|
||||
{
|
||||
// Interpolate from el_i (coarse element) to the fine face.
|
||||
// The mapping is given by d_interp, which uses indices
|
||||
// relative to element 0.
|
||||
|
||||
// i0 is lexicographic relative to element 0
|
||||
const int i0 = permute_face_2(lf_i, lf_0, orient, d1d, i_lex);
|
||||
|
||||
// k0 is lexicographic relative to element 0
|
||||
for (int k0 = 0; k0 < ndof_face; k0++)
|
||||
{
|
||||
// k is relative to the face itself
|
||||
const int k = permute_face(lf_0, orient, d1d, k0);
|
||||
val += d_interp(k0, i0, interp_index)
|
||||
* face_mats(k, j, f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert to lexicographic relative to the face itself
|
||||
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
|
||||
val = face_mats(i_face, j, f);
|
||||
}
|
||||
// Convert from lexicographic face DOF to volume DOF
|
||||
const int i = d_face_maps(i_lex, lf_i);
|
||||
el_mats(i, j, el_i, f) += val;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
-31
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "kernel_reporter.hpp"
|
||||
#include "../general/hash_util.hpp"
|
||||
#include <unordered_map>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -86,35 +87,6 @@ namespace mfem
|
||||
} \
|
||||
}
|
||||
|
||||
/// @brief Hashes variadic packs for which each type contained in the variadic
|
||||
/// pack has a specialization of `std::hash` available.
|
||||
///
|
||||
/// For example, packs containing int, bool, enum values, etc.
|
||||
template<typename ...KernelParameters>
|
||||
struct KernelDispatchKeyHash
|
||||
{
|
||||
private:
|
||||
template<int N>
|
||||
size_t operator()(std::tuple<KernelParameters...> value) const { return 0; }
|
||||
|
||||
// The hashing formula here is taken directly from the Boost library, with
|
||||
// the magic number 0x9e3779b9 chosen to minimize hashing collisions.
|
||||
template<std::size_t N, typename THead, typename... TTail>
|
||||
size_t operator()(std::tuple<KernelParameters...> value) const
|
||||
{
|
||||
constexpr int Index = N - sizeof...(TTail) - 1;
|
||||
auto lhs_hash = std::hash<THead>()(std::get<Index>(value));
|
||||
auto rhs_hash = operator()<N, TTail...>(value);
|
||||
return lhs_hash^(rhs_hash + 0x9e3779b9 + (lhs_hash<<6) + (lhs_hash>>2));
|
||||
}
|
||||
public:
|
||||
/// Returns the hash of the given @a value.
|
||||
size_t operator()(std::tuple<KernelParameters...> value) const
|
||||
{
|
||||
return operator()<sizeof...(KernelParameters),KernelParameters...>(value);
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal { template<typename... Types> struct KernelTypeList { }; }
|
||||
|
||||
template<typename... T> class KernelDispatchTable { };
|
||||
@@ -128,8 +100,8 @@ class KernelDispatchTable<Kernels,
|
||||
internal::KernelTypeList<Params...>,
|
||||
internal::KernelTypeList<OptParams...>>
|
||||
{
|
||||
using TableType = std::unordered_map<std::tuple<Params...>,
|
||||
Signature, KernelDispatchKeyHash<Params...>>;
|
||||
using TableType =
|
||||
std::unordered_map<std::tuple<Params...>, Signature, TupleHasher>;
|
||||
TableType table;
|
||||
|
||||
/// @brief Call function @a f with arguments @a args (perfect forwaring).
|
||||
|
||||
+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;
|
||||
|
||||
@@ -488,10 +488,16 @@ void ParBilinearForm::FormLinearSystem(
|
||||
R.Mult(x, true_X);
|
||||
|
||||
FormSystemMatrix(ess_tdof_list, A);
|
||||
ConstrainedOperator *A_constrained;
|
||||
Operator::FormConstrainedSystemOperator(ess_tdof_list, A_constrained);
|
||||
|
||||
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
|
||||
{
|
||||
Operator *op;
|
||||
Operator::FormSystemOperator(ess_tdof_list, op);
|
||||
return dynamic_cast<ConstrainedOperator*>(op);
|
||||
}());
|
||||
MFEM_ASSERT(A_constrained != nullptr, "");
|
||||
|
||||
A_constrained->EliminateRHS(true_X, true_B);
|
||||
delete A_constrained;
|
||||
R.MultTranspose(true_B, b);
|
||||
hybridization->ReduceRHS(true_B, B);
|
||||
X.SetSize(B.Size());
|
||||
|
||||
+8
-9
@@ -646,39 +646,38 @@ const FaceRestriction *ParFiniteElementSpace::GetFaceRestriction(
|
||||
auto itr = L2F.find(key);
|
||||
if (itr != L2F.end())
|
||||
{
|
||||
return itr->second;
|
||||
return itr->second.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
FaceRestriction *res;
|
||||
std::unique_ptr<FaceRestriction> res;
|
||||
if (is_dg_space)
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
res = new ParL2FaceRestriction(*this, f_ordering, type, m);
|
||||
res.reset(new ParL2FaceRestriction(*this, f_ordering, type, m));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new ParNCL2FaceRestriction(*this, f_ordering, type, m);
|
||||
res.reset(new ParNCL2FaceRestriction(*this, f_ordering, type, m));
|
||||
}
|
||||
}
|
||||
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
|
||||
{
|
||||
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
res = new ConformingFaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
else
|
||||
{
|
||||
res = new ParNCH1FaceRestriction(*this, f_ordering, type);
|
||||
res.reset(new ParNCH1FaceRestriction(*this, f_ordering, type));
|
||||
}
|
||||
}
|
||||
L2F[key] = res;
|
||||
return res;
|
||||
return L2F.emplace(key, std::move(res)).first->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -483,6 +483,8 @@ public:
|
||||
const FiniteElement *GetFaceNbrFaceFE(int i) const;
|
||||
const Array<HYPRE_BigInt> &GetFaceNbrGlobalDofMapArray() { return face_nbr_glob_dof_map; }
|
||||
const HYPRE_BigInt *GetFaceNbrGlobalDofMap() { return face_nbr_glob_dof_map; }
|
||||
const Array<HYPRE_BigInt> &GetFaceNbrGlobalDofMapArray() const
|
||||
{ return face_nbr_glob_dof_map; }
|
||||
ElementTransformation *GetFaceNbrElementTransformation(int i) const
|
||||
{ return pmesh->GetFaceNbrElementTransformation(i); }
|
||||
|
||||
|
||||
+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[],
|
||||
|
||||
@@ -994,7 +994,6 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
if ( face.IsConforming() )
|
||||
{
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
@@ -1010,7 +1009,6 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
}
|
||||
else // Non-conforming face
|
||||
{
|
||||
interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
@@ -1028,7 +1026,6 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
}
|
||||
else if (type==FaceType::Boundary && face.IsBoundary())
|
||||
{
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
SetFaceDofsScatterIndices1(face,f_ind);
|
||||
if ( m==L2FaceValues::DoubleValued )
|
||||
{
|
||||
@@ -1046,10 +1043,6 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
gather_offsets[i] += gather_offsets[i - 1];
|
||||
}
|
||||
|
||||
// Transform the interpolation matrix map into a contiguous memory structure.
|
||||
interpolations.LinearizeInterpolatorMapIntoVector();
|
||||
interpolations.InitializeNCInterpConfig();
|
||||
}
|
||||
|
||||
void ParNCL2FaceRestriction::ComputeGatherIndices()
|
||||
|
||||
@@ -326,9 +326,7 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
default behavior is to disregard those rows. */
|
||||
void FillI(SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
|
||||
@@ -364,9 +362,7 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
default behavior is to disregard those rows. */
|
||||
void FillJAndData(const Vector &fea_data,
|
||||
SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
|
||||
+7
-1
@@ -50,7 +50,13 @@ QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Fallback(
|
||||
int DIM, int SDIM, int D1D, int Q1D)
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
if (DIM == 1)
|
||||
{
|
||||
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<0,0,2>; }
|
||||
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<0,0,3>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface; }
|
||||
else if (DIM == 3)
|
||||
|
||||
+51
-1
@@ -56,6 +56,50 @@ inline void Det1D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, int T_SDIM = 3>
|
||||
inline void Det1DSurface(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *x,
|
||||
real_t *y,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0,
|
||||
Vector *d_buff = nullptr)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(b);
|
||||
MFEM_CONTRACT_VAR(d_buff);
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto G = Reshape(g, Q1D, D1D);
|
||||
const auto X = Reshape(x, D1D, T_SDIM, NE);
|
||||
auto Y = Reshape(y, Q1D, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
for (int q = 0; q < Q1D; q++)
|
||||
{
|
||||
real_t grad[T_SDIM];
|
||||
for (int s = 0; s < T_SDIM; s++) { grad[s] = 0.0; }
|
||||
for (int d = 0; d < D1D; d++)
|
||||
{
|
||||
const real_t gval = G(q, d);
|
||||
for (int s = 0; s < T_SDIM; s++)
|
||||
{
|
||||
grad[s] += gval * X(d, s, e);
|
||||
}
|
||||
}
|
||||
real_t norm2 = 0.0;
|
||||
for (int s = 0; s < T_SDIM; s++)
|
||||
{
|
||||
norm2 += grad[s] * grad[s];
|
||||
}
|
||||
Y(q, e) = std::sqrt(norm2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
inline void Det2D(const int NE,
|
||||
const real_t *b,
|
||||
@@ -290,7 +334,13 @@ template<int DIM, int SDIM, int D1D, int Q1D>
|
||||
QuadratureInterpolator::DetKernelType
|
||||
QuadratureInterpolator::DetKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
if (DIM == 1)
|
||||
{
|
||||
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
|
||||
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
|
||||
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
|
||||
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
|
||||
|
||||
@@ -542,7 +542,8 @@ void QuadratureInterpolator::Mult(const Vector &e_vec,
|
||||
}
|
||||
|
||||
MFEM_ASSERT(!(eval_flags & DETERMINANTS) || dim == vdim ||
|
||||
(dim == 2 && vdim == 3), "Invalid dimensions for determinants.");
|
||||
(dim == 2 && vdim == 3) || (dim == 1 && vdim == 2) ||
|
||||
(dim == 1 && vdim == 3), "Invalid dimensions for determinants.");
|
||||
MFEM_ASSERT(fespace->GetMesh()->GetNumGeometries(
|
||||
fespace->GetMesh()->Dimension()) == 1,
|
||||
"mixed meshes are not supported");
|
||||
|
||||
+118
-42
@@ -1506,12 +1506,12 @@ void L2FaceRestriction::EnsureNormalDerivativeRestriction() const
|
||||
}
|
||||
}
|
||||
|
||||
InterpolationManager::InterpolationManager(const FiniteElementSpace &fes,
|
||||
ElementDofOrdering ordering,
|
||||
InterpolationManager::InterpolationManager(const FiniteElementSpace &fes_,
|
||||
ElementDofOrdering ordering_,
|
||||
FaceType type)
|
||||
: fes(fes),
|
||||
ordering(ordering),
|
||||
interp_config( fes.GetNFbyType(type) ),
|
||||
: fes(fes_),
|
||||
ordering(ordering_),
|
||||
interp_config(fes.GetNFbyType(type)),
|
||||
nc_cpt(0)
|
||||
{ }
|
||||
|
||||
@@ -1536,7 +1536,8 @@ void InterpolationManager::RegisterFaceCoarseToFineInterpolation(
|
||||
face.element[0].local_face_id +
|
||||
6*face.element[1].local_face_id +
|
||||
36*face.element[1].orientation ;
|
||||
// Unfortunately we can't trust unicity of the ptMat to identify the transformation.
|
||||
// Unfortunately we can't trust uniqueness of the ptMat to identify the
|
||||
// transformation.
|
||||
Key key(ptMat, face_key);
|
||||
auto itr = interp_map.find(key);
|
||||
if ( itr == interp_map.end() )
|
||||
@@ -1583,17 +1584,27 @@ const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
|
||||
IsoparametricTransformation isotr;
|
||||
isotr.SetIdentityTransformation(trace_fe->GetGeomType());
|
||||
isotr.SetPointMat(*ptMat);
|
||||
DenseMatrix& trans_pt_mat = isotr.GetPointMat();
|
||||
// PointMatrix needs to be flipped in 2D
|
||||
if ( trace_fe->GetGeomType()==Geometry::SEGMENT && !is_ghost_slave )
|
||||
{
|
||||
std::swap(trans_pt_mat(0,0),trans_pt_mat(0,1));
|
||||
}
|
||||
DenseMatrix native_interpolator(face_dofs,face_dofs);
|
||||
trace_fe->GetLocalInterpolation(isotr, native_interpolator);
|
||||
|
||||
if (trace_fe->GetMapType() == FiniteElement::INTEGRAL)
|
||||
{
|
||||
// Handle potentially inverted Jacobian matrix
|
||||
isotr.SetIntPoint(&Geometries.GetCenter(trace_fe->GetGeomType()));
|
||||
native_interpolator *= (isotr.Weight() >= 0) ? 1.0 : -1.0;
|
||||
}
|
||||
|
||||
const int dim = trace_fe->GetDim()+1;
|
||||
const int dof1d = trace_fe->GetOrder()+1;
|
||||
const int orientation = face.element[1].orientation;
|
||||
int orientation_i = face.element[1].orientation;
|
||||
const int orientation_j = face.element[1].orientation;
|
||||
|
||||
// In 2D, need to flip orientation of the segments`
|
||||
if (trace_fe->GetGeomType() == Geometry::SEGMENT && !is_ghost_slave)
|
||||
{
|
||||
orientation_i = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < face_dofs; i++)
|
||||
{
|
||||
const int ni = (dof_map.Size()==0) ? i : dof_map[i];
|
||||
@@ -1602,7 +1613,7 @@ const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
|
||||
{
|
||||
// master side is elem 2, so we permute to order dofs as elem 1.
|
||||
li = PermuteFaceL2(dim, face_id2, face_id1,
|
||||
orientation, dof1d, li);
|
||||
orientation_i, dof1d, li);
|
||||
}
|
||||
for (int j = 0; j < face_dofs; j++)
|
||||
{
|
||||
@@ -1611,7 +1622,7 @@ const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
|
||||
{
|
||||
// master side is elem 2, so we permute to order dofs as elem 1.
|
||||
lj = PermuteFaceL2(dim, face_id2, face_id1,
|
||||
orientation, dof1d, lj);
|
||||
orientation_j, dof1d, lj);
|
||||
}
|
||||
const int nj = (dof_map.Size()==0) ? j : dof_map[j];
|
||||
(*interpolator)(li,lj) = native_interpolator(ni,nj);
|
||||
@@ -1676,7 +1687,7 @@ NCL2FaceRestriction::NCL2FaceRestriction(const FiniteElementSpace &fes,
|
||||
const L2FaceValues m,
|
||||
bool build)
|
||||
: L2FaceRestriction(fes, f_ordering, type, m, false),
|
||||
interpolations(fes, f_ordering, type)
|
||||
interpolations(fes.GetInterpolationManager(ordering, type))
|
||||
{
|
||||
if (!build) { return; }
|
||||
x_interp.UseDevice(true);
|
||||
@@ -2202,14 +2213,6 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
PermuteAndSetFaceDofsScatterIndices2(face,f_ind);
|
||||
}
|
||||
if ( face.IsConforming() )
|
||||
{
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
}
|
||||
else // Non-conforming face
|
||||
{
|
||||
interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
|
||||
}
|
||||
f_ind++;
|
||||
}
|
||||
else if ( type==FaceType::Boundary && face.IsBoundary() )
|
||||
@@ -2219,7 +2222,6 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
SetBoundaryDofsScatterIndices2(face,f_ind);
|
||||
}
|
||||
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
|
||||
f_ind++;
|
||||
}
|
||||
}
|
||||
@@ -2232,10 +2234,6 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
|
||||
{
|
||||
gather_offsets[i] += gather_offsets[i - 1];
|
||||
}
|
||||
|
||||
// Transform the interpolation matrix map into a contiguous memory structure.
|
||||
interpolations.LinearizeInterpolatorMapIntoVector();
|
||||
interpolations.InitializeNCInterpConfig();
|
||||
}
|
||||
|
||||
void NCL2FaceRestriction::ComputeGatherIndices()
|
||||
@@ -2278,6 +2276,18 @@ void NCL2FaceRestriction::ComputeGatherIndices()
|
||||
gather_offsets[0] = 0;
|
||||
}
|
||||
|
||||
static int GetSharedVSize(const FiniteElementSpace &fes)
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (auto pfes = dynamic_cast<const ParFiniteElementSpace*>(&fes))
|
||||
{
|
||||
const_cast<ParFiniteElementSpace*>(pfes)->ExchangeFaceNbrData();
|
||||
return pfes->GetFaceNbrVSize();
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
|
||||
const FiniteElementSpace& fes_,
|
||||
const ElementDofOrdering ordering_,
|
||||
@@ -2288,25 +2298,54 @@ L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
|
||||
nfaces(fes.GetNFbyType(type)),
|
||||
vdim(fes.GetVDim()),
|
||||
byvdim(fes.GetOrdering() == Ordering::byVDIM),
|
||||
face_dofs(nfaces > 0 ? fes.GetFaceElement(0)->GetDof() : 0),
|
||||
face_dofs(fes.GetTypicalTraceElement()->GetDof()),
|
||||
nfdofs(face_dofs*nfaces),
|
||||
ndofs(fes.GetNDofs())
|
||||
ndofs(fes.GetNDofs()),
|
||||
nsdofs(GetSharedVSize(fes))
|
||||
{
|
||||
height = nfdofs;
|
||||
width = ndofs;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
auto pfes = dynamic_cast<const ParFiniteElementSpace*>(&fes);
|
||||
#endif
|
||||
|
||||
const Table &face2dof = fes.GetFaceToDofTable();
|
||||
|
||||
const Mesh &mesh = *fes.GetMesh();
|
||||
int face_idx = 0;
|
||||
gather_map.SetSize(nfdofs);
|
||||
for (int f = 0; f < mesh.GetNumFaces(); ++f)
|
||||
scatter_map.SetSize(nfdofs);
|
||||
gather_map.SetSize(ndofs + nsdofs);
|
||||
gather_map = -1;
|
||||
|
||||
Array<int> dofs;
|
||||
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
|
||||
{
|
||||
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
|
||||
if (!face.IsOfFaceType(type)) { continue; }
|
||||
for (int i = 0; i < face_dofs; ++i)
|
||||
if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse()) { continue; }
|
||||
|
||||
if (f < mesh.GetNumFaces())
|
||||
{
|
||||
gather_map[i + face_idx*face_dofs] = face2dof.GetJ()[i + f*face_dofs];
|
||||
// Local face
|
||||
face2dof.GetRow(f, dofs);
|
||||
for (int i = 0; i < face_dofs; ++i)
|
||||
{
|
||||
scatter_map[i + face_idx*face_dofs] = dofs[i];
|
||||
gather_map[dofs[i]] = i + face_idx*face_dofs;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Shared (non-conforming) ghost face
|
||||
#ifdef MFEM_USE_MPI
|
||||
MFEM_ASSERT(pfes != nullptr, "");
|
||||
pfes->GetFaceNbrFaceVDofs(f, dofs);
|
||||
for (int i = 0; i < face_dofs; ++i)
|
||||
{
|
||||
scatter_map[i + face_idx*face_dofs] = ndofs + dofs[i];
|
||||
gather_map[ndofs + dofs[i]] = i + face_idx*face_dofs;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
++face_idx;
|
||||
}
|
||||
@@ -2314,13 +2353,19 @@ L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
|
||||
|
||||
void L2InterfaceFaceRestriction::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
const int NDOFS = ndofs;
|
||||
const int nd = face_dofs;
|
||||
const int nf = nfaces;
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
const int *map = gather_map.Read();
|
||||
const int *map = scatter_map.Read();
|
||||
|
||||
Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, type);
|
||||
MFEM_ASSERT(face_nbr_data.Size() / vd == nsdofs, "");
|
||||
|
||||
const auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
|
||||
const auto d_x_shared = Reshape(face_nbr_data.Read(),
|
||||
t?vd:nsdofs, t?nsdofs:vd);
|
||||
auto d_y = Reshape(y.Write(), nd, vd, nf);
|
||||
|
||||
mfem::forall(nd*nf, [=] MFEM_HOST_DEVICE (int i)
|
||||
@@ -2328,7 +2373,8 @@ void L2InterfaceFaceRestriction::Mult(const Vector &x, Vector &y) const
|
||||
const int j = map[i];
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c);
|
||||
if (j < NDOFS) { d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c); }
|
||||
else { d_y(i % nd, c, i / nd) = d_x_shared(t?c:(j-NDOFS), t?(j-NDOFS):c); }
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2343,15 +2389,39 @@ void L2InterfaceFaceRestriction::AddMultTranspose(
|
||||
const int *map = gather_map.Read();
|
||||
|
||||
const auto d_x = Reshape(x.Read(), nd, vd, nf);
|
||||
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
|
||||
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
|
||||
|
||||
mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i) { d_y[i] = 0.0; });
|
||||
mfem::forall(nd*nf, [=] MFEM_HOST_DEVICE (int i)
|
||||
mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
const int j = map[i];
|
||||
if (j < 0) { return; }
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
d_y(t?c:j, t?j:c) = d_x(i % nd, c, i / nd);
|
||||
d_y(t?c:i, t?i:c) += a*d_x(j % nd, c, j / nd);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void L2InterfaceFaceRestriction::MultTransposeShared(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
const int nd = face_dofs;
|
||||
const int nf = nfaces;
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
const int *map = gather_map.Read();
|
||||
|
||||
const auto d_x = Reshape(x.Read(), nd, vd, nf);
|
||||
auto d_y = Reshape(y.Write(), t?vd:(ndofs+nsdofs), t?(ndofs+nsdofs):vd);
|
||||
y = 0.0;
|
||||
|
||||
mfem::forall(ndofs + nsdofs, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
const int j = map[i];
|
||||
if (j < 0) { return; }
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
d_y(t?c:i, t?i:c) = d_x(j % nd, c, j / nd);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -2361,6 +2431,11 @@ const Array<int> &L2InterfaceFaceRestriction::GatherMap() const
|
||||
return gather_map;
|
||||
}
|
||||
|
||||
const Array<int> &L2InterfaceFaceRestriction::ScatterMap() const
|
||||
{
|
||||
return scatter_map;
|
||||
}
|
||||
|
||||
Vector GetLVectorFaceNbrData(
|
||||
const FiniteElementSpace &fes, const Vector &x, FaceType ftype)
|
||||
{
|
||||
@@ -2382,6 +2457,7 @@ Vector GetLVectorFaceNbrData(
|
||||
{
|
||||
ParGridFunction gf(pfes, const_cast<Vector&>(x));
|
||||
gf.ExchangeFaceNbrData();
|
||||
x.SyncMemory(gf);
|
||||
return std::move(gf.FaceNbrData());
|
||||
}
|
||||
}
|
||||
|
||||
+26
-14
@@ -812,13 +812,12 @@ protected:
|
||||
PointMatrix and a local face identifier. */
|
||||
using Key = std::pair<const DenseMatrix*,int>;
|
||||
/// The temporary map used to store the different interpolators.
|
||||
using Map = std::map<Key, std::pair<int,const DenseMatrix*>>;
|
||||
using Map =
|
||||
std::unordered_map<Key, std::pair<int,const DenseMatrix*>, PairHasher>;
|
||||
Map interp_map; // The temporary map that stores the interpolators.
|
||||
|
||||
public:
|
||||
InterpolationManager() = delete;
|
||||
|
||||
/** @brief main constructor.
|
||||
/** @brief Constructor.
|
||||
|
||||
@param[in] fes The FiniteElementSpace on which this operates
|
||||
@param[in] ordering Request a specific element ordering.
|
||||
@@ -909,7 +908,7 @@ private:
|
||||
class NCL2FaceRestriction : virtual public L2FaceRestriction
|
||||
{
|
||||
protected:
|
||||
InterpolationManager interpolations;
|
||||
const InterpolationManager &interpolations;
|
||||
mutable Vector x_interp;
|
||||
|
||||
/** @brief Constructs an NCL2FaceRestriction, this is a specialization of a
|
||||
@@ -996,9 +995,7 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
default behavior is to disregard those rows. */
|
||||
void FillI(SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
|
||||
@@ -1016,9 +1013,7 @@ public:
|
||||
@param[in] keep_nbr_block When set to true the SparseMatrix will
|
||||
include the rows (in addition to the columns)
|
||||
corresponding to face-neighbor dofs. The
|
||||
default behavior is to disregard those rows.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
default behavior is to disregard those rows. */
|
||||
void FillJAndData(const Vector &fea_data,
|
||||
SparseMatrix &mat,
|
||||
const bool keep_nbr_block = false) const override;
|
||||
@@ -1036,9 +1031,7 @@ public:
|
||||
added the face contributions.
|
||||
The format is: dofs x dofs x ne, where dofs is the
|
||||
number of dofs per element and ne the number of
|
||||
elements.
|
||||
|
||||
@warning This method is not implemented yet. */
|
||||
elements. */
|
||||
void AddFaceMatricesToElementMatrices(const Vector &fea_data,
|
||||
Vector &ea_data) const override;
|
||||
|
||||
@@ -1130,7 +1123,9 @@ protected:
|
||||
const int face_dofs; ///< Number of dofs on each face
|
||||
const int nfdofs; ///< Total number of dofs on the faces (E-vector size)
|
||||
const int ndofs; ///< Number of dofs in the space (L-vector size)
|
||||
const int nsdofs; ///< Number of shared face neighbor (ghost) dofs
|
||||
Array<int> gather_map; ///< Gather map
|
||||
Array<int> scatter_map; ///< Scatter map
|
||||
|
||||
public:
|
||||
/** @brief Constructs an L2InterfaceFaceRestriction.
|
||||
@@ -1168,7 +1163,24 @@ public:
|
||||
void AddMultTranspose(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const override;
|
||||
|
||||
/// @brief Gather degrees of freedom, from face E-vector to L-vector and
|
||||
/// shared (ghost) DOFs.
|
||||
///
|
||||
/// @param[in] x The face E-Vector degrees of freedom with size
|
||||
/// (face_dofs, vdim, nf), where nf is the number of
|
||||
/// interior or boundary faces requested by @a type in the
|
||||
/// constructor. The face_dofs should be ordered according
|
||||
/// to the given ElementDofOrdering
|
||||
/// @param[out] y Vector of length vsize + face neighbor vsize
|
||||
void MultTransposeShared(const Vector &x, Vector &y) const;
|
||||
|
||||
const Array<int> &GatherMap() const override;
|
||||
|
||||
/// @brief Return the low-level mapping from L-dofs to E-dofs.
|
||||
///
|
||||
/// L-dofs that do not correspond to an E-dof (e.g. that lie on a face of a
|
||||
/// different type) are given index -1.
|
||||
const Array<int> &ScatterMap() const;
|
||||
};
|
||||
|
||||
/** @brief Convert a dof face index from Native ordering to lexicographic
|
||||
|
||||
+260
-88
@@ -333,6 +333,12 @@ void L2ProjectionGridTransfer::L2Projection::MixedMassEA(
|
||||
int nel_ho = mesh_ho->GetNE();
|
||||
int nel_lor = mesh_lor->GetNE();
|
||||
|
||||
if (nel_ho == 0)
|
||||
{
|
||||
M_LH.SetSize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
|
||||
|
||||
int nref_max = 0;
|
||||
@@ -831,11 +837,17 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::Mult(
|
||||
void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMult(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
const int nel_ho = fes_ho.GetMesh()->GetNE();
|
||||
|
||||
if (nel_ho == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int iho = 0;
|
||||
const int nref = ho2lor.RowSize(iho);
|
||||
const int ndof_ho = fes_ho.GetFE(iho)->GetDof();
|
||||
const int ndof_lor = fes_lor.GetFE(ho2lor.GetRow(iho)[0])->GetDof();
|
||||
const int nel_ho = fes_ho.GetMesh()->GetNE();
|
||||
|
||||
DenseTensor R_dt;
|
||||
R_dt.NewMemoryAndSize(R.GetMemory(), ndof_lor*nref, ndof_ho, nel_ho, false);
|
||||
@@ -887,11 +899,17 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::MultTranspose(
|
||||
void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMultTranspose(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
const int nel_ho = fes_ho.GetMesh()->GetNE();
|
||||
|
||||
if (nel_ho == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int iho = 0;
|
||||
const int nref = ho2lor.RowSize(iho);
|
||||
const int ndof_ho = fes_ho.GetFE(iho)->GetDof();
|
||||
const int ndof_lor = fes_lor.GetFE(ho2lor.GetRow(iho)[0])->GetDof();
|
||||
const int nel_ho = fes_ho.GetMesh()->GetNE();
|
||||
|
||||
DenseTensor R_dt;
|
||||
R_dt.NewMemoryAndSize(R.GetMemory(), ndof_lor*nref, ndof_ho, nel_ho, false);
|
||||
@@ -901,7 +919,6 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMultTranspose(
|
||||
void L2ProjectionGridTransfer::L2ProjectionL2Space::Prolongate(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
|
||||
if (fes_ho.GetNE() == 0) { return; }
|
||||
|
||||
if (use_ea)
|
||||
@@ -960,14 +977,13 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAProlongate(
|
||||
void L2ProjectionGridTransfer::L2ProjectionL2Space::ProlongateTranspose(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
if (fes_ho.GetNE() == 0) { return; }
|
||||
|
||||
if (use_ea)
|
||||
{
|
||||
return EAProlongateTranspose(x,y);
|
||||
}
|
||||
|
||||
|
||||
if (fes_ho.GetNE() == 0) { return; }
|
||||
MFEM_VERIFY(P.Size() > 0, "Prolongation not supported for these spaces.")
|
||||
int vdim = fes_ho.GetVDim();
|
||||
Array<int> vdofs;
|
||||
@@ -1014,12 +1030,52 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAProlongateTranspose(
|
||||
BatchedLinAlg::MultTranspose(P_dt, x, y);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class H1ConsistentMassOperator : public Operator
|
||||
{
|
||||
private:
|
||||
const Operator &M_LH;
|
||||
const Solver &M_L_solver;
|
||||
|
||||
public:
|
||||
H1ConsistentMassOperator(const Operator &M_LH_, const Solver &M_L_solver_)
|
||||
: Operator(M_LH_.Height(), M_LH_.Width()),
|
||||
M_LH(M_LH_),
|
||||
M_L_solver(M_L_solver_)
|
||||
{
|
||||
MFEM_VERIFY(M_LH.Height() == M_L_solver.Height() &&
|
||||
M_LH.Height() == M_L_solver.Width(),
|
||||
"incompatible consistent mass operator dimensions");
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const override
|
||||
{
|
||||
Vector tmp(M_LH.Height());
|
||||
M_LH.Mult(x, tmp);
|
||||
M_L_solver.Mult(tmp, y);
|
||||
}
|
||||
|
||||
void MultTranspose(const Vector &x, Vector &y) const override
|
||||
{
|
||||
Vector tmp(M_LH.Height());
|
||||
M_L_solver.Mult(x, tmp);
|
||||
M_LH.MultTranspose(tmp, y);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
const FiniteElementSpace& fes_ho_, const FiniteElementSpace& fes_lor_,
|
||||
const bool use_ea_, MemoryType d_mt_)
|
||||
const bool use_ea_, const bool use_consistent_mass_, MemoryType d_mt_)
|
||||
: L2Projection(fes_ho_, fes_lor_, d_mt_),
|
||||
use_ea(use_ea_)
|
||||
use_ea(use_ea_),
|
||||
use_consistent_mass(use_consistent_mass_)
|
||||
{
|
||||
MFEM_VERIFY(!(use_ea && use_consistent_mass),
|
||||
"consistent mass is not supported with element assembly");
|
||||
|
||||
// need scalar to keep dimensions matching (operators are built to apply
|
||||
// individually on each vdim)
|
||||
@@ -1037,7 +1093,7 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
|
||||
std::unique_ptr<SparseMatrix> R_mat, M_LH_mat;
|
||||
|
||||
std::tie(R_mat, M_LH_mat) = ComputeSparseRAndM_LH();
|
||||
std::tie(R_mat, M_LH_mat) = ComputeSparseRAndM_LH(!use_consistent_mass);
|
||||
|
||||
const SparseMatrix *P_ho = fes_ho_scalar->GetConformingProlongation();
|
||||
const SparseMatrix *P_lor = fes_lor_scalar->GetConformingProlongation();
|
||||
@@ -1046,40 +1102,68 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
{
|
||||
if (P_ho && P_lor)
|
||||
{
|
||||
R_mat.reset(RAP(*P_lor, *R_mat, *P_ho));
|
||||
if (R_mat) { R_mat.reset(RAP(*P_lor, *R_mat, *P_ho)); }
|
||||
M_LH_mat.reset(RAP(*P_lor, *M_LH_mat, *P_ho));
|
||||
}
|
||||
else if (P_ho)
|
||||
{
|
||||
R_mat.reset(mfem::Mult(*R_mat, *P_ho));
|
||||
if (R_mat) { R_mat.reset(mfem::Mult(*R_mat, *P_ho)); }
|
||||
M_LH_mat.reset(mfem::Mult(*M_LH_mat, *P_ho));
|
||||
}
|
||||
else // P_lor != nullptr
|
||||
{
|
||||
R_mat.reset(mfem::Mult(*P_lor, *R_mat));
|
||||
if (R_mat) { R_mat.reset(mfem::Mult(*P_lor, *R_mat)); }
|
||||
M_LH_mat.reset(mfem::Mult(*P_lor, *M_LH_mat));
|
||||
}
|
||||
}
|
||||
|
||||
SparseMatrix *RTxM_LH_mat = TransposeMult(*R_mat, *M_LH_mat);
|
||||
precon.reset(new DSmoother(*RTxM_LH_mat));
|
||||
if (use_consistent_mass)
|
||||
{
|
||||
BilinearForm M_lor(fes_lor_scalar.get());
|
||||
M_lor.AddDomainIntegrator(new MassIntegrator);
|
||||
M_lor.Assemble();
|
||||
M_lor.Finalize(0);
|
||||
SparseMatrix *M_L_mat = new SparseMatrix(M_lor.SpMat());
|
||||
|
||||
// Set ownership
|
||||
RTxM_LH.reset(RTxM_LH_mat);
|
||||
R = std::move(R_mat);
|
||||
M_LH = std::move(M_LH_mat);
|
||||
ML_precon.reset(new DSmoother(*M_L_mat));
|
||||
ML_pcg.SetPrintLevel(0);
|
||||
ML_pcg.SetMaxIter(1000);
|
||||
ML_pcg.SetRelTol(1e-13);
|
||||
ML_pcg.SetAbsTol(1e-13);
|
||||
ML_pcg.SetPreconditioner(*ML_precon);
|
||||
ML_pcg.SetOperator(*M_L_mat);
|
||||
|
||||
SetupPCG();
|
||||
M_L.reset(M_L_mat);
|
||||
M_LH = std::move(M_LH_mat);
|
||||
R.reset(new H1ConsistentMassOperator(*M_LH, ML_pcg));
|
||||
}
|
||||
else
|
||||
{
|
||||
SparseMatrix *RTxM_LH_mat = TransposeMult(*R_mat, *M_LH_mat);
|
||||
precon.reset(new DSmoother(*RTxM_LH_mat));
|
||||
|
||||
// Set ownership
|
||||
RTxM_LH.reset(RTxM_LH_mat);
|
||||
R = std::move(R_mat);
|
||||
M_LH = std::move(M_LH_mat);
|
||||
|
||||
SetupPCG();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
const ParFiniteElementSpace& pfes_ho, const ParFiniteElementSpace& pfes_lor,
|
||||
const bool use_ea_, MemoryType d_mt_)
|
||||
const bool use_ea_, const bool use_consistent_mass_, MemoryType d_mt_)
|
||||
: L2Projection(pfes_ho, pfes_lor, d_mt_),
|
||||
use_ea(use_ea_), pcg(pfes_ho.GetComm())
|
||||
use_ea(use_ea_),
|
||||
use_consistent_mass(use_consistent_mass_),
|
||||
pcg(pfes_ho.GetComm()),
|
||||
ML_pcg(pfes_ho.GetComm())
|
||||
{
|
||||
MFEM_VERIFY(!(use_ea && use_consistent_mass),
|
||||
"consistent mass is not supported with element assembly");
|
||||
|
||||
// need scalar to keep dimensions matching (operators are built to apply
|
||||
// individually on each vdim)
|
||||
@@ -1095,26 +1179,58 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
|
||||
return;
|
||||
}
|
||||
|
||||
std::tie(R, M_LH) = ComputeSparseRAndM_LH();
|
||||
std::unique_ptr<SparseMatrix> R_local_sp, M_LH_local_sp;
|
||||
std::tie(R_local_sp, M_LH_local_sp) =
|
||||
ComputeSparseRAndM_LH(!use_consistent_mass);
|
||||
|
||||
M_LH_local_sp->Finalize(0);
|
||||
if (R_local_sp) { R_local_sp->Finalize(0); }
|
||||
|
||||
HypreParMatrix M_LH_local = HypreParMatrix(pfes_ho.GetComm(),
|
||||
pfes_lor_scalar->GlobalVSize(),
|
||||
pfes_ho_scalar->GlobalVSize(),
|
||||
pfes_lor_scalar->GetDofOffsets(),
|
||||
pfes_ho_scalar->GetDofOffsets(),
|
||||
M_LH_local_sp.get());
|
||||
HypreParMatrix *M_LH_mat = RAP(pfes_lor_scalar->Dof_TrueDof_Matrix(),
|
||||
&M_LH_local, pfes_ho_scalar->Dof_TrueDof_Matrix());
|
||||
|
||||
if (use_consistent_mass)
|
||||
{
|
||||
ParBilinearForm M_lor(pfes_lor_scalar.get());
|
||||
M_lor.AddDomainIntegrator(new MassIntegrator);
|
||||
M_lor.Assemble();
|
||||
M_lor.Finalize(0);
|
||||
HypreParMatrix *M_L_mat = M_lor.ParallelAssemble();
|
||||
|
||||
ML_pcg.SetPrintLevel(0);
|
||||
ML_pcg.SetMaxIter(1000);
|
||||
ML_pcg.SetRelTol(1e-13);
|
||||
ML_pcg.SetAbsTol(1e-13);
|
||||
ML_pcg.SetOperator(*M_L_mat);
|
||||
|
||||
M_L.reset(M_L_mat);
|
||||
M_LH.reset(M_LH_mat);
|
||||
HyprePCG *ML_hypre_pcg = new HyprePCG(*M_L_mat);
|
||||
ML_hypre_pcg->SetPrintLevel(0);
|
||||
ML_hypre_pcg->SetMaxIter(1000);
|
||||
ML_hypre_pcg->SetTol(1e-13);
|
||||
ML_hypre_pcg->SetAbsTol(1e-13);
|
||||
ML_hypre_pcg->SetZeroInitialIterate();
|
||||
ML_solver.reset(ML_hypre_pcg);
|
||||
R.reset(new H1ConsistentMassOperator(*M_LH, *ML_solver));
|
||||
return;
|
||||
}
|
||||
|
||||
HypreParMatrix R_local = HypreParMatrix(pfes_ho.GetComm(),
|
||||
pfes_lor_scalar->GlobalVSize(),
|
||||
pfes_ho_scalar->GlobalVSize(),
|
||||
pfes_lor_scalar->GetDofOffsets(),
|
||||
pfes_ho_scalar->GetDofOffsets(),
|
||||
static_cast<SparseMatrix*>(R.get()));
|
||||
HypreParMatrix M_LH_local = HypreParMatrix(pfes_ho.GetComm(),
|
||||
pfes_lor_scalar->GlobalVSize(),
|
||||
pfes_ho_scalar->GlobalVSize(),
|
||||
pfes_lor_scalar->GetDofOffsets(),
|
||||
pfes_ho_scalar->GetDofOffsets(),
|
||||
static_cast<SparseMatrix*>(M_LH.get()));
|
||||
R_local_sp.get());
|
||||
|
||||
HypreParMatrix *R_mat = RAP(pfes_lor_scalar->Dof_TrueDof_Matrix(),
|
||||
&R_local, pfes_ho_scalar->Dof_TrueDof_Matrix());
|
||||
HypreParMatrix *M_LH_mat = RAP(pfes_lor_scalar->Dof_TrueDof_Matrix(),
|
||||
&M_LH_local, pfes_ho_scalar->Dof_TrueDof_Matrix());
|
||||
|
||||
std::unique_ptr<HypreParMatrix> R_T(R_mat->Transpose());
|
||||
HypreParMatrix *RTxM_LH_mat = ParMult(R_T.get(), M_LH_mat, true);
|
||||
@@ -1244,13 +1360,6 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space
|
||||
int ndof_ho = pfes_ho.GetNDofs();
|
||||
int ndof_lor = pfes_lor.GetNDofs();
|
||||
|
||||
|
||||
// If the local mesh is empty, skip all computations
|
||||
if (nel_ho == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
|
||||
|
||||
int nref_max = 0;
|
||||
@@ -1429,6 +1538,8 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::MultTranspose(
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::Prolongate(
|
||||
const Vector& x, Vector& y) const
|
||||
{
|
||||
MFEM_VERIFY(!use_consistent_mass,
|
||||
"BackwardOperator is not supported with consistent mass");
|
||||
|
||||
Vector X(fes_lor.GetTrueVSize());
|
||||
Vector X_dim(M_LH->Height());
|
||||
@@ -1460,6 +1571,9 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::Prolongate(
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::ProlongateTranspose(
|
||||
const Vector& x, Vector& y) const
|
||||
{
|
||||
MFEM_VERIFY(!use_consistent_mass,
|
||||
"BackwardOperator is not supported with consistent mass");
|
||||
|
||||
Vector X(fes_ho.GetTrueVSize());
|
||||
Vector X_dim(pcg.Width());
|
||||
Vector Xbar(pcg.Height());
|
||||
@@ -1490,17 +1604,34 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::ProlongateTranspose(
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetRelTol(real_t p_rtol_)
|
||||
{
|
||||
pcg.SetRelTol(p_rtol_);
|
||||
ML_pcg.SetRelTol(p_rtol_);
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (ML_solver)
|
||||
{
|
||||
HyprePCG *hypre_pcg = dynamic_cast<HyprePCG*>(ML_solver.get());
|
||||
if (hypre_pcg) { hypre_pcg->SetTol(p_rtol_); }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::L2ProjectionH1Space::SetAbsTol(real_t p_atol_)
|
||||
{
|
||||
pcg.SetAbsTol(p_atol_);
|
||||
ML_pcg.SetAbsTol(p_atol_);
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (ML_solver)
|
||||
{
|
||||
HyprePCG *hypre_pcg = dynamic_cast<HyprePCG*>(ML_solver.get());
|
||||
if (hypre_pcg) { hypre_pcg->SetAbsTol(p_atol_); }
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::pair<
|
||||
std::unique_ptr<SparseMatrix>,
|
||||
std::unique_ptr<SparseMatrix>>
|
||||
L2ProjectionGridTransfer::L2ProjectionH1Space::ComputeSparseRAndM_LH()
|
||||
L2ProjectionGridTransfer::L2ProjectionH1Space::ComputeSparseRAndM_LH(
|
||||
bool build_R)
|
||||
{
|
||||
std::pair<std::unique_ptr<SparseMatrix>,
|
||||
std::unique_ptr<SparseMatrix>> r_and_mlh;
|
||||
@@ -1533,69 +1664,76 @@ std::unique_ptr<SparseMatrix>>
|
||||
|
||||
BuildHo2Lor(nel_ho, nel_lor, cf_tr);
|
||||
|
||||
// ML_inv contains the inverse lumped (row sum) mass matrix. Note that the
|
||||
// method will also work with a full (consistent) mass matrix, though this is
|
||||
// not implemented here. L refers to the low-order refined mesh
|
||||
Vector ML_inv(ndof_lor);
|
||||
ML_inv = 0.0;
|
||||
|
||||
// Compute ML_inv
|
||||
for (int iho = 0; iho < nel_ho; ++iho)
|
||||
if (build_R)
|
||||
{
|
||||
Array<int> lor_els;
|
||||
ho2lor.GetRow(iho, lor_els);
|
||||
int nref = ho2lor.RowSize(iho);
|
||||
// ML_inv contains the inverse lumped (row sum) mass matrix. L refers to
|
||||
// the low-order refined mesh.
|
||||
ML_inv = 0.0;
|
||||
|
||||
Geometry::Type geom = mesh_ho->GetElementBaseGeometry(iho);
|
||||
const FiniteElement& fe_lor = *fes_lor.GetFE(lor_els[0]);
|
||||
int nedof_lor = fe_lor.GetDof();
|
||||
|
||||
// Instead of using a MassIntegrator, manually loop over integration
|
||||
// points so we can row sum and store the diagonal as a Vector.
|
||||
Vector ML_el(nedof_lor);
|
||||
Vector shape_lor(nedof_lor);
|
||||
Array<int> dofs_lor(nedof_lor);
|
||||
|
||||
for (int iref = 0; iref < nref; ++iref)
|
||||
// Compute ML_inv
|
||||
for (int iho = 0; iho < nel_ho; ++iho)
|
||||
{
|
||||
int ilor = lor_els[iref];
|
||||
ElementTransformation* el_tr = fes_lor.GetElementTransformation(ilor);
|
||||
Array<int> lor_els;
|
||||
ho2lor.GetRow(iho, lor_els);
|
||||
int nref = ho2lor.RowSize(iho);
|
||||
|
||||
int order = 2 * fe_lor.GetOrder() + el_tr->OrderW();
|
||||
const IntegrationRule* ir = &IntRules.Get(geom, order);
|
||||
ML_el = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); ++i)
|
||||
Geometry::Type geom = mesh_ho->GetElementBaseGeometry(iho);
|
||||
const FiniteElement& fe_lor = *fes_lor.GetFE(lor_els[0]);
|
||||
int nedof_lor = fe_lor.GetDof();
|
||||
|
||||
// Instead of using a MassIntegrator, manually loop over integration
|
||||
// points so we can row sum and store the diagonal as a Vector.
|
||||
Vector ML_el(nedof_lor);
|
||||
Vector shape_lor(nedof_lor);
|
||||
Array<int> dofs_lor(nedof_lor);
|
||||
|
||||
for (int iref = 0; iref < nref; ++iref)
|
||||
{
|
||||
const IntegrationPoint& ip_lor = ir->IntPoint(i);
|
||||
fe_lor.CalcShape(ip_lor, shape_lor);
|
||||
el_tr->SetIntPoint(&ip_lor);
|
||||
ML_el += (shape_lor *= (el_tr->Weight() * ip_lor.weight));
|
||||
int ilor = lor_els[iref];
|
||||
ElementTransformation* el_tr = fes_lor.GetElementTransformation(ilor);
|
||||
|
||||
int order = 2 * fe_lor.GetOrder() + el_tr->OrderW();
|
||||
const IntegrationRule* ir = &IntRules.Get(geom, order);
|
||||
ML_el = 0.0;
|
||||
for (int i = 0; i < ir->GetNPoints(); ++i)
|
||||
{
|
||||
const IntegrationPoint& ip_lor = ir->IntPoint(i);
|
||||
fe_lor.CalcShape(ip_lor, shape_lor);
|
||||
el_tr->SetIntPoint(&ip_lor);
|
||||
ML_el += (shape_lor *= (el_tr->Weight() * ip_lor.weight));
|
||||
}
|
||||
fes_lor.GetElementDofs(ilor, dofs_lor);
|
||||
ML_inv.AddElementVector(dofs_lor, ML_el);
|
||||
}
|
||||
fes_lor.GetElementDofs(ilor, dofs_lor);
|
||||
ML_inv.AddElementVector(dofs_lor, ML_el);
|
||||
}
|
||||
// DOF by DOF inverse of non-zero entries
|
||||
LumpedMassInverse(ML_inv);
|
||||
}
|
||||
// DOF by DOF inverse of non-zero entries
|
||||
LumpedMassInverse(ML_inv);
|
||||
|
||||
// Compute sparsity pattern for R = M_L^(-1) M_LH and allocate
|
||||
r_and_mlh.first = AllocR();
|
||||
std::unique_ptr<SparseMatrix> pattern = AllocR();
|
||||
if (build_R)
|
||||
{
|
||||
r_and_mlh.first = std::move(pattern);
|
||||
}
|
||||
// Allocate M_LH (same sparsity pattern as R)
|
||||
// L refers to the low-order refined mesh (DOFs correspond to rows)
|
||||
// H refers to the higher-order mesh (DOFs correspond to columns)
|
||||
Memory<int> I(r_and_mlh.first->Height() + 1);
|
||||
for (int icol = 0; icol < r_and_mlh.first->Height() + 1; ++icol)
|
||||
SparseMatrix &pattern_mat = build_R ? *r_and_mlh.first : *pattern;
|
||||
Memory<int> I(pattern_mat.Height() + 1);
|
||||
for (int icol = 0; icol < pattern_mat.Height() + 1; ++icol)
|
||||
{
|
||||
I[icol] = r_and_mlh.first->GetI()[icol];
|
||||
I[icol] = pattern_mat.GetI()[icol];
|
||||
}
|
||||
Memory<int> J(r_and_mlh.first->NumNonZeroElems());
|
||||
for (int jcol = 0; jcol < r_and_mlh.first->NumNonZeroElems(); ++jcol)
|
||||
Memory<int> J(pattern_mat.NumNonZeroElems());
|
||||
for (int jcol = 0; jcol < pattern_mat.NumNonZeroElems(); ++jcol)
|
||||
{
|
||||
J[jcol] = r_and_mlh.first->GetJ()[jcol];
|
||||
J[jcol] = pattern_mat.GetJ()[jcol];
|
||||
}
|
||||
r_and_mlh.second = std::unique_ptr<SparseMatrix>(
|
||||
new SparseMatrix(I, J, NULL, r_and_mlh.first->Height(),
|
||||
r_and_mlh.first->Width(), true, true, true));
|
||||
new SparseMatrix(I, J, NULL, pattern_mat.Height(),
|
||||
pattern_mat.Width(), true, true, true));
|
||||
|
||||
IntegrationPointTransformation ip_tr;
|
||||
IsoparametricTransformation& emb_tr = ip_tr.Transf;
|
||||
@@ -1638,15 +1776,21 @@ std::unique_ptr<SparseMatrix>>
|
||||
Array<int> dofs_lor(nedof_lor);
|
||||
fes_lor.GetElementDofs(ilor, dofs_lor);
|
||||
Vector R_row;
|
||||
for (int i = 0; i < nedof_lor; ++i)
|
||||
if (build_R)
|
||||
{
|
||||
M_LH_el.GetRow(i, R_row);
|
||||
R_el.SetRow(i, R_row.Set(ML_inv[dofs_lor[i]], R_row));
|
||||
for (int i = 0; i < nedof_lor; ++i)
|
||||
{
|
||||
M_LH_el.GetRow(i, R_row);
|
||||
R_el.SetRow(i, R_row.Set(ML_inv[dofs_lor[i]], R_row));
|
||||
}
|
||||
}
|
||||
Array<int> dofs_ho(nedof_ho);
|
||||
fes_ho.GetElementDofs(iho, dofs_ho);
|
||||
r_and_mlh.second->AddSubMatrix(dofs_lor, dofs_ho, M_LH_el);
|
||||
r_and_mlh.first->AddSubMatrix(dofs_lor, dofs_ho, R_el);
|
||||
if (build_R)
|
||||
{
|
||||
r_and_mlh.first->AddSubMatrix(dofs_lor, dofs_ho, R_el);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1860,6 +2004,11 @@ L2ProjectionGridTransfer::H1SpaceMixedMassOperator::H1SpaceMixedMassOperator(
|
||||
void L2ProjectionGridTransfer::H1SpaceMixedMassOperator::Mult(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
if (fes_ho->GetNE() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Operator* elem_restrict_ho = fes_ho->GetElementRestriction(
|
||||
ElementDofOrdering::NATIVE);
|
||||
const Operator* elem_restrict_lor = fes_lor->GetElementRestriction(
|
||||
@@ -1906,6 +2055,11 @@ void L2ProjectionGridTransfer::H1SpaceMixedMassOperator::Mult(const Vector &x,
|
||||
void L2ProjectionGridTransfer::H1SpaceMixedMassOperator::MultTranspose(
|
||||
const Vector &x, Vector &y) const
|
||||
{
|
||||
if (fes_ho->GetNE() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Operator* elem_restrict_ho = fes_ho->GetElementRestriction(
|
||||
ElementDofOrdering::NATIVE);
|
||||
const Operator* elem_restrict_lor = fes_lor->GetElementRestriction(
|
||||
@@ -1990,6 +2144,10 @@ const Operator &L2ProjectionGridTransfer::ForwardOperator()
|
||||
|
||||
const Operator &L2ProjectionGridTransfer::BackwardOperator()
|
||||
{
|
||||
MFEM_VERIFY(!(use_consistent_mass && !force_l2_space &&
|
||||
dom_fes.FEColl()->GetContType() ==
|
||||
FiniteElementCollection::CONTINUOUS),
|
||||
"BackwardOperator is not supported with consistent mass");
|
||||
if (!B)
|
||||
{
|
||||
if (!F) { BuildF(); }
|
||||
@@ -1998,15 +2156,24 @@ const Operator &L2ProjectionGridTransfer::BackwardOperator()
|
||||
return *B;
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::UseConsistentMass(bool use_consistent_mass_)
|
||||
{
|
||||
MFEM_VERIFY(!F && !B,
|
||||
"UseConsistentMass must be called before constructing operators");
|
||||
use_consistent_mass = use_consistent_mass_;
|
||||
}
|
||||
|
||||
void L2ProjectionGridTransfer::BuildF()
|
||||
{
|
||||
MFEM_VERIFY(!(use_ea && use_consistent_mass),
|
||||
"consistent mass is not supported with element assembly");
|
||||
if (!force_l2_space &&
|
||||
dom_fes.FEColl()->GetContType() == FiniteElementCollection::CONTINUOUS)
|
||||
{
|
||||
if (!Parallel())
|
||||
{
|
||||
F = new L2ProjectionH1Space(dom_fes, ran_fes,
|
||||
use_ea, d_mt);
|
||||
use_ea, use_consistent_mass, d_mt);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2016,7 +2183,7 @@ void L2ProjectionGridTransfer::BuildF()
|
||||
const mfem::ParFiniteElementSpace& ran_pfes =
|
||||
static_cast<mfem::ParFiniteElementSpace&>(ran_fes);
|
||||
F = new L2ProjectionH1Space(dom_pfes, ran_pfes,
|
||||
use_ea, d_mt);
|
||||
use_ea, use_consistent_mass, d_mt);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -2029,6 +2196,11 @@ void L2ProjectionGridTransfer::BuildF()
|
||||
|
||||
bool L2ProjectionGridTransfer::SupportsBackwardsOperator() const
|
||||
{
|
||||
if (use_consistent_mass && !force_l2_space &&
|
||||
dom_fes.FEColl()->GetContType() == FiniteElementCollection::CONTINUOUS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ran_fes.GetTrueVSize() >= dom_fes.GetTrueVSize();
|
||||
}
|
||||
|
||||
|
||||
+20
-5
@@ -169,8 +169,10 @@ public:
|
||||
is the forward transfer matrix, and M_f is the mass matrix on the coarse
|
||||
element. For L2 spaces, M_f is the mass matrix on the union of all fine
|
||||
elements comprising the coarse element. For H1 spaces, M_f is a diagonal
|
||||
(lumped) mass matrix computed through row-summation. Note that the backward
|
||||
transfer operator, B, is a left inverse of the forward transfer operator, F,
|
||||
(lumped) mass matrix computed through row-summation, unless
|
||||
UseConsistentMass() is enabled for the forward H1 operator. Note that the
|
||||
backward transfer operator, B, is a left inverse of the forward transfer
|
||||
operator, F,
|
||||
i.e. B F = I. Both F and B are defined in physical space and, generally for
|
||||
L2 spaces, vary between different mesh elements.
|
||||
|
||||
@@ -352,16 +354,19 @@ public:
|
||||
class L2ProjectionH1Space : public L2Projection
|
||||
{
|
||||
const bool use_ea;
|
||||
const bool use_consistent_mass;
|
||||
|
||||
public:
|
||||
L2ProjectionH1Space(const FiniteElementSpace &fes_ho_,
|
||||
const FiniteElementSpace &fes_lor_,
|
||||
const bool use_ea_,
|
||||
const bool use_consistent_mass_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
#ifdef MFEM_USE_MPI
|
||||
L2ProjectionH1Space(const ParFiniteElementSpace &pfes_ho_,
|
||||
const ParFiniteElementSpace &pfes_lor_,
|
||||
const bool use_ea_,
|
||||
const bool use_consistent_mass_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
#endif
|
||||
/// Same as above but assembles action of R through 4 parts:
|
||||
@@ -423,7 +428,8 @@ public:
|
||||
/// @brief Computes on-rank R and M_LH matrices. If true, computes mixed mass and/or
|
||||
/// inverse lumped mass matrix error when compared to device implementation.
|
||||
std::pair<std::unique_ptr<SparseMatrix>,
|
||||
std::unique_ptr<SparseMatrix>> ComputeSparseRAndM_LH();
|
||||
std::unique_ptr<SparseMatrix>> ComputeSparseRAndM_LH(
|
||||
bool build_R = true);
|
||||
|
||||
/// @brief Recovers vector of tdofs given a vector of dofs and a finite
|
||||
/// element space
|
||||
@@ -454,7 +460,11 @@ public:
|
||||
std::unique_ptr<SparseMatrix> AllocR();
|
||||
|
||||
CGSolver pcg;
|
||||
CGSolver ML_pcg;
|
||||
std::unique_ptr<Solver> precon;
|
||||
std::unique_ptr<Solver> ML_precon;
|
||||
std::unique_ptr<Solver> ML_solver;
|
||||
std::unique_ptr<Operator> M_L;
|
||||
// The restriction operator is represented as an Operator R. The
|
||||
// prolongation operator is a dense matrix computed as the inverse of (R^T
|
||||
// M_L R), and hence, is not stored.
|
||||
@@ -478,7 +488,6 @@ public:
|
||||
Vector M_LH_ea;
|
||||
// Element Assembled lumped M_L inverse built via EA. Stores diagonal as a Ldof vector.
|
||||
Vector ML_inv_ea;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
std::unique_ptr<ParFiniteElementSpace> pfes_ho_scalar;
|
||||
std::unique_ptr<ParFiniteElementSpace> pfes_lor_scalar;
|
||||
@@ -511,6 +520,7 @@ public:
|
||||
L2Projection *F; ///< Forward, coarse-to-fine, operator
|
||||
L2Prolongation *B; ///< Backward, fine-to-coarse, operator
|
||||
bool force_l2_space;
|
||||
bool use_consistent_mass;
|
||||
|
||||
public:
|
||||
L2ProjectionGridTransfer(FiniteElementSpace &coarse_fes_,
|
||||
@@ -518,10 +528,15 @@ public:
|
||||
bool force_l2_space_ = false,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType()) // move to method
|
||||
: GridTransfer(coarse_fes_, fine_fes_),
|
||||
F(NULL), B(NULL), force_l2_space(force_l2_space_)
|
||||
F(NULL), B(NULL), force_l2_space(force_l2_space_),
|
||||
use_consistent_mass(false)
|
||||
{ }
|
||||
virtual ~L2ProjectionGridTransfer();
|
||||
|
||||
/** Use the consistent low-order mass matrix in H1 non-EA Mult() and
|
||||
MultTranspose(). This option does not support BackwardOperator(). */
|
||||
void UseConsistentMass(bool use_consistent_mass_ = true);
|
||||
|
||||
const Operator &ForwardOperator() override;
|
||||
|
||||
const Operator &BackwardOperator() override;
|
||||
|
||||
@@ -18,6 +18,7 @@ list(APPEND SRCS
|
||||
gecko.cpp
|
||||
globals.cpp
|
||||
hash.cpp
|
||||
hash_util.cpp
|
||||
isockstream.cpp
|
||||
mem_manager.cpp
|
||||
occa.cpp
|
||||
@@ -46,6 +47,7 @@ list(APPEND HDRS
|
||||
globals.hpp
|
||||
zstr.hpp
|
||||
hash.hpp
|
||||
hash_util.hpp
|
||||
isockstream.hpp
|
||||
kdtree.hpp
|
||||
mem_alloc.hpp
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -80,159 +80,4 @@ std::string HashFunction::GetHash() const
|
||||
return hash;
|
||||
}
|
||||
|
||||
constexpr static uint64_t rotl64(uint64_t x, int r)
|
||||
{
|
||||
return (x << r) | (x >> (64 - r));
|
||||
}
|
||||
|
||||
void Hasher::init(uint64_t seed)
|
||||
{
|
||||
data[0] = seed;
|
||||
data[1] = seed;
|
||||
nbytes = 0;
|
||||
}
|
||||
|
||||
void Hasher::add_block(uint64_t k1, uint64_t k2)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] = rotl64(data[0], 27);
|
||||
data[0] += data[1];
|
||||
data[0] = data[0] * 5 + 0x52dce729ull;
|
||||
|
||||
k2 *= c2;
|
||||
k2 = rotl64(k2, 33);
|
||||
k2 *= c1;
|
||||
data[1] ^= k2;
|
||||
|
||||
data[1] = rotl64(data[1], 31);
|
||||
data[1] += data[0];
|
||||
data[1] = data[1] * 5 + 0x38495ab5ull;
|
||||
}
|
||||
|
||||
static uint64_t fmix64(uint64_t k)
|
||||
{
|
||||
// http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
|
||||
// mix13
|
||||
k ^= k >> 30;
|
||||
k *= 0xbf58476d1ce4e5b9ull;
|
||||
k ^= k >> 27;
|
||||
k *= 0x94d049bb133111ebull;
|
||||
k ^= k >> 31;
|
||||
return k;
|
||||
}
|
||||
|
||||
void Hasher::append(const uint8_t *vs, uint64_t bytes)
|
||||
{
|
||||
if (bytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto rem = nbytes % 16;
|
||||
nbytes += bytes;
|
||||
uint8_t *tmp = reinterpret_cast<uint8_t *>(buf_);
|
||||
while (true)
|
||||
{
|
||||
if (bytes + rem >= 16)
|
||||
{
|
||||
std::copy(vs, vs + 16 - rem, tmp + rem);
|
||||
add_block(buf_[0], buf_[1]);
|
||||
vs += (16 - rem);
|
||||
bytes -= (16 - rem);
|
||||
rem = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(vs, vs + bytes, tmp + rem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hasher::finalize()
|
||||
{
|
||||
auto rem = nbytes % 16;
|
||||
if (rem > 0)
|
||||
{
|
||||
nbytes -= rem;
|
||||
if (rem <= 8)
|
||||
{
|
||||
finalize(buf_[0], rem);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalize(buf_[0], buf_[1], rem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
void Hasher::finalize(uint64_t k1, int num)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
nbytes += num;
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
void Hasher::finalize(uint64_t k1, uint64_t k2, int num)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
nbytes += num;
|
||||
k2 *= c2;
|
||||
k2 = rotl64(k2, 33);
|
||||
k2 *= c1;
|
||||
data[1] ^= k2;
|
||||
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+1
-70
@@ -15,8 +15,8 @@
|
||||
#include "../config/config.hpp"
|
||||
#include "array.hpp"
|
||||
#include "globals.hpp"
|
||||
#include "hash_util.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -457,75 +457,6 @@ protected:
|
||||
int BinSize(int idx) const;
|
||||
};
|
||||
|
||||
///
|
||||
/// @brief streaming implementation for murmurhash3 128 (x64).
|
||||
/// Constructs the hash in 3 stages: init, append, finalize.
|
||||
///
|
||||
struct Hasher
|
||||
{
|
||||
/// where the final hash result is stored after finalize. Use data[1] when
|
||||
/// only 64 bits are required.
|
||||
uint64_t data[2] = {0, 0};
|
||||
|
||||
private:
|
||||
uint64_t nbytes = 0;
|
||||
|
||||
uint64_t buf_[2] = {0, 0};
|
||||
|
||||
public:
|
||||
|
||||
/// resets this hasher back to an initial seed
|
||||
void init(uint64_t seed = 0);
|
||||
void append(const uint8_t *vs, uint64_t bytes);
|
||||
|
||||
void finalize();
|
||||
|
||||
private:
|
||||
// add 16 bytes
|
||||
void add_block(uint64_t k1, uint64_t k2);
|
||||
|
||||
// add [1-8] more bytes, then finalize
|
||||
void finalize(uint64_t k1, int num);
|
||||
|
||||
// add [1-15] more bytes, then finalize
|
||||
// 0 < num < 16
|
||||
void finalize(uint64_t k1, uint64_t k2, int num);
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::pair. Usable in place of std::hash<std::pair<T,U>>
|
||||
struct PairHasher
|
||||
{
|
||||
template <class T, class V>
|
||||
size_t operator()(const std::pair<T, V> &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
hash.append(reinterpret_cast<const uint8_t *>(&v.first), sizeof(T));
|
||||
hash.append(reinterpret_cast<const uint8_t *>(&v.second), sizeof(V));
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::array. Usable in place of std::hash<std::array<T,N>>
|
||||
struct ArrayHasher
|
||||
{
|
||||
template <class T, size_t N>
|
||||
size_t operator()(const std::array<T, N> &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
hash.append(reinterpret_cast<const uint8_t *>(&v[i]), sizeof(T));
|
||||
}
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
/// Hash function for data sequences.
|
||||
/** Depends on GnuTLS for SHA-256 hashing. */
|
||||
class HashFunction
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
// 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 "hash_util.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
constexpr static uint64_t rotl64(uint64_t x, int r)
|
||||
{
|
||||
return (x << r) | (x >> (64 - r));
|
||||
}
|
||||
|
||||
void Hasher::init(uint64_t seed)
|
||||
{
|
||||
data[0] = seed;
|
||||
data[1] = seed;
|
||||
nbytes = 0;
|
||||
}
|
||||
|
||||
void Hasher::add_block(uint64_t k1, uint64_t k2)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] = rotl64(data[0], 27);
|
||||
data[0] += data[1];
|
||||
data[0] = data[0] * 5 + 0x52dce729ull;
|
||||
|
||||
k2 *= c2;
|
||||
k2 = rotl64(k2, 33);
|
||||
k2 *= c1;
|
||||
data[1] ^= k2;
|
||||
|
||||
data[1] = rotl64(data[1], 31);
|
||||
data[1] += data[0];
|
||||
data[1] = data[1] * 5 + 0x38495ab5ull;
|
||||
}
|
||||
|
||||
static uint64_t fmix64(uint64_t k)
|
||||
{
|
||||
// http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
|
||||
// mix13
|
||||
k ^= k >> 30;
|
||||
k *= 0xbf58476d1ce4e5b9ull;
|
||||
k ^= k >> 27;
|
||||
k *= 0x94d049bb133111ebull;
|
||||
k ^= k >> 31;
|
||||
return k;
|
||||
}
|
||||
|
||||
void Hasher::append(const std::byte *vs, uint64_t bytes)
|
||||
{
|
||||
if (bytes == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto rem = nbytes % 16;
|
||||
nbytes += bytes;
|
||||
std::byte *tmp = reinterpret_cast<std::byte *>(buf_);
|
||||
while (true)
|
||||
{
|
||||
if (bytes + rem >= 16)
|
||||
{
|
||||
std::copy(vs, vs + 16 - rem, tmp + rem);
|
||||
add_block(buf_[0], buf_[1]);
|
||||
vs += (16 - rem);
|
||||
bytes -= (16 - rem);
|
||||
rem = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(vs, vs + bytes, tmp + rem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Hasher::finalize()
|
||||
{
|
||||
auto rem = nbytes % 16;
|
||||
if (rem > 0)
|
||||
{
|
||||
nbytes -= rem;
|
||||
if (rem <= 8)
|
||||
{
|
||||
finalize(buf_[0], rem);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalize(buf_[0], buf_[1], rem);
|
||||
}
|
||||
return;
|
||||
}
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
void Hasher::finalize(uint64_t k1, int num)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
nbytes += num;
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
void Hasher::finalize(uint64_t k1, uint64_t k2, int num)
|
||||
{
|
||||
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
|
||||
constexpr uint64_t c2 = 0x4cf5ad432745937full;
|
||||
nbytes += num;
|
||||
k2 *= c2;
|
||||
k2 = rotl64(k2, 33);
|
||||
k2 *= c1;
|
||||
data[1] ^= k2;
|
||||
|
||||
k1 *= c1;
|
||||
k1 = rotl64(k1, 31);
|
||||
k1 *= c2;
|
||||
data[0] ^= k1;
|
||||
|
||||
data[0] ^= nbytes;
|
||||
data[1] ^= nbytes;
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
|
||||
data[0] = fmix64(data[0]);
|
||||
data[1] = fmix64(data[1]);
|
||||
|
||||
data[0] += data[1];
|
||||
data[1] += data[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// 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.
|
||||
|
||||
#ifndef MFEM_HASH_UTIL_HPP
|
||||
#define MFEM_HASH_UTIL_HPP
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// @brief streaming implementation for murmurhash3 128 (x64).
|
||||
///
|
||||
/// Constructs the hash in 3 stages: init, append, finalize.
|
||||
struct Hasher
|
||||
{
|
||||
/// @brief Storage for the final hash result after finalize() is called.
|
||||
///
|
||||
/// Use data[1] when only 64 bits are required.
|
||||
uint64_t data[2] = {0, 0};
|
||||
|
||||
private:
|
||||
uint64_t nbytes = 0;
|
||||
uint64_t buf_[2] = {0, 0};
|
||||
|
||||
public:
|
||||
|
||||
/// Resets the Hasher back to an initial seed
|
||||
void init(uint64_t seed = 0);
|
||||
|
||||
/// Append data @a vs of size @a bytes.
|
||||
void append(const std::byte *vs, uint64_t bytes);
|
||||
|
||||
void finalize();
|
||||
|
||||
private:
|
||||
/// Add a block of 16 bytes.
|
||||
void add_block(uint64_t k1, uint64_t k2);
|
||||
|
||||
/// @brief Add [1-8] more bytes, then finalize.
|
||||
///
|
||||
/// @a num must satisfy 0 < num < 9.
|
||||
void finalize(uint64_t k1, int num);
|
||||
|
||||
/// @brief Add [1-15] more bytes, then finalize.
|
||||
///
|
||||
/// @a num must satisfy 0 < num < 16.
|
||||
void finalize(uint64_t k1, uint64_t k2, int num);
|
||||
};
|
||||
|
||||
template <class T> struct ChainedHasher
|
||||
{
|
||||
static void Append(Hasher &hasher, const T &value)
|
||||
{
|
||||
if constexpr (std::is_fundamental_v<T> || std::is_pointer_v<T>)
|
||||
{
|
||||
hasher.append(reinterpret_cast<const std::byte *>(&value), sizeof(T));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::hash<T> h;
|
||||
auto v = h(value);
|
||||
hasher.append(reinterpret_cast<std::byte *>(&v), sizeof(v));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class V> struct ChainedHasher<std::pair<T, V>>
|
||||
{
|
||||
static void Append(Hasher &hasher, const std::pair<T, V> &value)
|
||||
{
|
||||
ChainedHasher<T>::Append(hasher, value.first);
|
||||
ChainedHasher<V>::Append(hasher, value.second);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, size_t N> struct ChainedHasher<std::array<T, N>>
|
||||
{
|
||||
static void Append(Hasher &hasher, const std::array<T, N> &value)
|
||||
{
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
ChainedHasher<T>::Append(hasher, value[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<class... Ts> struct ChainedHasher<std::tuple<Ts...>>
|
||||
{
|
||||
private:
|
||||
template <size_t N>
|
||||
static void AppendImpl(Hasher &hasher, const std::tuple<Ts...> &value)
|
||||
{
|
||||
ChainedHasher<std::decay_t<decltype(std::get<N>(value))>>::Append(
|
||||
hasher, std::get<N>(value));
|
||||
if constexpr (N + 1 < sizeof...(Ts))
|
||||
{
|
||||
AppendImpl<N + 1>(hasher, value);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static void Append(Hasher &hasher, const std::tuple<Ts...> &value)
|
||||
{
|
||||
if constexpr (sizeof...(Ts))
|
||||
{
|
||||
AppendImpl<0>(hasher, value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::pair of hashable types.
|
||||
struct PairHasher
|
||||
{
|
||||
template <class T, class V>
|
||||
size_t operator()(const std::pair<T, V> &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
ChainedHasher<std::pair<T, V>>::Append(hash, v);
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::array of a hashable type.
|
||||
struct ArrayHasher
|
||||
{
|
||||
template <class T, size_t N>
|
||||
size_t operator()(const std::array<T, N> &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
ChainedHasher<std::array<T, N>>::Append(hash, v);
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
/// Helper class for hashing std::tuple of hashable types.
|
||||
struct TupleHasher
|
||||
{
|
||||
template <class T>
|
||||
size_t operator()(const T &v) const noexcept
|
||||
{
|
||||
Hasher hash;
|
||||
// chosen randomly with a 2^64-sided dice
|
||||
hash.init(0xfebd1fe69813c14full);
|
||||
ChainedHasher<T>::Append(hash, v);
|
||||
hash.finalize();
|
||||
return hash.data[1];
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
+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();
|
||||
@@ -3627,12 +3634,25 @@ void HypreSmoother::SetType(HypreSmoother::Type type_, int relax_times_)
|
||||
relax_times = relax_times_;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetType(HypreSmoother::Type &type_, int &relax_times_) const
|
||||
{
|
||||
type_ = static_cast<HypreSmoother::Type>(type);
|
||||
relax_times_ = relax_times;
|
||||
}
|
||||
|
||||
void HypreSmoother::SetSOROptions(real_t relax_weight_, real_t omega_)
|
||||
{
|
||||
relax_weight = relax_weight_;
|
||||
omega = omega_;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetSOROptions(real_t &relax_weight_, real_t &omega_) const
|
||||
{
|
||||
// TODO: are these used for all smoother types?
|
||||
relax_weight_ = relax_weight;
|
||||
omega_ = omega;
|
||||
}
|
||||
|
||||
void HypreSmoother::SetPolyOptions(int poly_order_, real_t poly_fraction_,
|
||||
int eig_est_cg_iter_)
|
||||
{
|
||||
@@ -3641,6 +3661,15 @@ void HypreSmoother::SetPolyOptions(int poly_order_, real_t poly_fraction_,
|
||||
eig_est_cg_iter = eig_est_cg_iter_;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetPolyOptions(int &poly_order_, real_t &poly_fraction_,
|
||||
int &eig_est_cg_iter_) const
|
||||
{
|
||||
// TODO: are these used for all smoother types?
|
||||
poly_order_ = poly_order;
|
||||
poly_fraction_ = poly_fraction;
|
||||
eig_est_cg_iter_ = eig_est_cg_iter;
|
||||
}
|
||||
|
||||
void HypreSmoother::SetTaubinOptions(real_t lambda_, real_t mu_,
|
||||
int taubin_iter_)
|
||||
{
|
||||
@@ -3649,6 +3678,14 @@ void HypreSmoother::SetTaubinOptions(real_t lambda_, real_t mu_,
|
||||
taubin_iter = taubin_iter_;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetTaubinOptions(real_t &lambda_, real_t &mu_,
|
||||
int &taubin_iter_) const
|
||||
{
|
||||
lambda_ = lambda;
|
||||
mu_ = mu;
|
||||
taubin_iter_ = taubin_iter;
|
||||
}
|
||||
|
||||
void HypreSmoother::SetWindowByName(const char* name)
|
||||
{
|
||||
real_t a = -1, b, c;
|
||||
@@ -3671,6 +3708,13 @@ void HypreSmoother::SetWindowParameters(real_t a, real_t b, real_t c)
|
||||
window_params[2] = c;
|
||||
}
|
||||
|
||||
void HypreSmoother::GetWindowParameters(real_t &a, real_t &b, real_t &c) const
|
||||
{
|
||||
a = window_params[0];
|
||||
b = window_params[1];
|
||||
c = window_params[2];
|
||||
}
|
||||
|
||||
void HypreSmoother::SetOperator(const Operator &op)
|
||||
{
|
||||
A = const_cast<HypreParMatrix *>(dynamic_cast<const HypreParMatrix *>(&op));
|
||||
@@ -4166,12 +4210,20 @@ HypreSolver::~HypreSolver()
|
||||
auxX.Delete();
|
||||
}
|
||||
|
||||
void HyprePCG::SetDefaultOptions()
|
||||
{
|
||||
// Explicitly set just in case past/future versions of hypre change the
|
||||
// defaults
|
||||
SetTol(1e-6);
|
||||
SetMaxIter(1000);
|
||||
}
|
||||
|
||||
HyprePCG::HyprePCG(MPI_Comm comm) : precond(NULL)
|
||||
{
|
||||
iterative_mode = true;
|
||||
|
||||
HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
|
||||
SetDefaultOptions();
|
||||
}
|
||||
|
||||
HyprePCG::HyprePCG(const HypreParMatrix &A_) : HypreSolver(&A_), precond(NULL)
|
||||
@@ -4183,6 +4235,7 @@ HyprePCG::HyprePCG(const HypreParMatrix &A_) : HypreSolver(&A_), precond(NULL)
|
||||
HYPRE_ParCSRMatrixGetComm(*A, &comm);
|
||||
|
||||
HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
|
||||
SetDefaultOptions();
|
||||
}
|
||||
|
||||
void HyprePCG::SetOperator(const Operator &op)
|
||||
@@ -4207,21 +4260,54 @@ void HyprePCG::SetOperator(const Operator &op)
|
||||
auxX.Delete(); auxX.Reset();
|
||||
}
|
||||
|
||||
void HyprePCG::SetUseTwoNorm(bool val)
|
||||
{
|
||||
HYPRE_PCGSetTwoNorm(pcg_solver, val);
|
||||
}
|
||||
|
||||
bool HyprePCG::GetUseTwoNorm() const
|
||||
{
|
||||
HYPRE_Int val;
|
||||
HYPRE_PCGGetTwoNorm(pcg_solver, &val);
|
||||
return val != 0;
|
||||
}
|
||||
|
||||
void HyprePCG::SetTol(real_t tol)
|
||||
{
|
||||
HYPRE_PCGSetTol(pcg_solver, tol);
|
||||
}
|
||||
|
||||
real_t HyprePCG::GetTol() const
|
||||
{
|
||||
HYPRE_Real tol;
|
||||
HYPRE_PCGGetTol(pcg_solver, &tol);
|
||||
return tol;
|
||||
}
|
||||
|
||||
void HyprePCG::SetAbsTol(real_t atol)
|
||||
{
|
||||
HYPRE_PCGSetAbsoluteTol(pcg_solver, atol);
|
||||
}
|
||||
|
||||
real_t HyprePCG::GetAbsTol() const
|
||||
{
|
||||
HYPRE_Real atol;
|
||||
hypre_PCGGetAbsoluteTol(pcg_solver, &atol);
|
||||
return atol;
|
||||
}
|
||||
|
||||
void HyprePCG::SetMaxIter(int max_iter)
|
||||
{
|
||||
HYPRE_PCGSetMaxIter(pcg_solver, max_iter);
|
||||
}
|
||||
|
||||
int HyprePCG::GetMaxIter() const
|
||||
{
|
||||
HYPRE_Int max_iter;
|
||||
HYPRE_PCGGetMaxIter(pcg_solver, &max_iter);
|
||||
return max_iter;
|
||||
}
|
||||
|
||||
void HyprePCG::SetLogging(int logging)
|
||||
{
|
||||
HYPRE_PCGSetLogging(pcg_solver, logging);
|
||||
@@ -4337,6 +4423,20 @@ HyprePCG::~HyprePCG()
|
||||
HYPRE_ParCSRPCGDestroy(pcg_solver);
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
HypreParVector HyprePCG::GetResiduals() const
|
||||
{
|
||||
HYPRE_ParVector r;
|
||||
HYPRE_ParCSRPCGGetResidual(pcg_solver, &r);
|
||||
return HypreParVector(r);
|
||||
}
|
||||
|
||||
void HyprePCG::GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p) const
|
||||
{
|
||||
auto r = GetResiduals();
|
||||
ParNormlp(r, p, r.GetComm());
|
||||
}
|
||||
#endif
|
||||
|
||||
HypreGMRES::HypreGMRES(MPI_Comm comm) : precond(NULL)
|
||||
{
|
||||
@@ -4392,26 +4492,69 @@ void HypreGMRES::SetOperator(const Operator &op)
|
||||
auxX.Delete(); auxX.Reset();
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
HypreParVector HypreGMRES::GetResiduals() const
|
||||
{
|
||||
HYPRE_ParVector r;
|
||||
HYPRE_ParCSRGMRESGetResidual(gmres_solver, &r);
|
||||
return HypreParVector(r);
|
||||
}
|
||||
|
||||
void HypreGMRES::GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p) const
|
||||
{
|
||||
auto r = GetResiduals();
|
||||
ParNormlp(r, p, r.GetComm());
|
||||
}
|
||||
#endif
|
||||
|
||||
void HypreGMRES::SetTol(real_t tol)
|
||||
{
|
||||
HYPRE_GMRESSetTol(gmres_solver, tol);
|
||||
}
|
||||
|
||||
real_t HypreGMRES::GetTol()const
|
||||
{
|
||||
HYPRE_Real tol;
|
||||
HYPRE_GMRESGetTol(gmres_solver, &tol);
|
||||
return tol;
|
||||
}
|
||||
|
||||
void HypreGMRES::SetAbsTol(real_t tol)
|
||||
{
|
||||
HYPRE_GMRESSetAbsoluteTol(gmres_solver, tol);
|
||||
}
|
||||
|
||||
real_t HypreGMRES::GetAbsTol() const
|
||||
{
|
||||
HYPRE_Real atol;
|
||||
HYPRE_GMRESGetAbsoluteTol(gmres_solver, &atol);
|
||||
return atol;
|
||||
}
|
||||
|
||||
void HypreGMRES::SetMaxIter(int max_iter)
|
||||
{
|
||||
HYPRE_GMRESSetMaxIter(gmres_solver, max_iter);
|
||||
}
|
||||
|
||||
int HypreGMRES::GetMaxIter() const
|
||||
{
|
||||
HYPRE_Int max_iter;
|
||||
HYPRE_GMRESGetMaxIter(gmres_solver, &max_iter);
|
||||
return max_iter;
|
||||
}
|
||||
|
||||
void HypreGMRES::SetKDim(int k_dim)
|
||||
{
|
||||
HYPRE_GMRESSetKDim(gmres_solver, k_dim);
|
||||
}
|
||||
|
||||
int HypreGMRES::GetKDim() const
|
||||
{
|
||||
HYPRE_Int k_dim;
|
||||
HYPRE_GMRESGetKDim(gmres_solver, &k_dim);
|
||||
return k_dim;
|
||||
}
|
||||
|
||||
void HypreGMRES::SetLogging(int logging)
|
||||
{
|
||||
HYPRE_GMRESSetLogging(gmres_solver, logging);
|
||||
@@ -4569,16 +4712,37 @@ void HypreFGMRES::SetTol(real_t tol)
|
||||
HYPRE_ParCSRFlexGMRESSetTol(fgmres_solver, tol);
|
||||
}
|
||||
|
||||
real_t HypreFGMRES::GetTol() const
|
||||
{
|
||||
HYPRE_Real tol;
|
||||
HYPRE_FlexGMRESGetTol(fgmres_solver, &tol);
|
||||
return tol;
|
||||
}
|
||||
|
||||
void HypreFGMRES::SetMaxIter(int max_iter)
|
||||
{
|
||||
HYPRE_ParCSRFlexGMRESSetMaxIter(fgmres_solver, max_iter);
|
||||
}
|
||||
|
||||
int HypreFGMRES::GetMaxIter() const
|
||||
{
|
||||
HYPRE_Int max_iter;
|
||||
HYPRE_FlexGMRESGetMaxIter(fgmres_solver, &max_iter);
|
||||
return max_iter;
|
||||
}
|
||||
|
||||
void HypreFGMRES::SetKDim(int k_dim)
|
||||
{
|
||||
HYPRE_ParCSRFlexGMRESSetKDim(fgmres_solver, k_dim);
|
||||
}
|
||||
|
||||
int HypreFGMRES::GetKDim() const
|
||||
{
|
||||
HYPRE_Int k_dim;
|
||||
HYPRE_FlexGMRESGetKDim(fgmres_solver, &k_dim);
|
||||
return k_dim;
|
||||
}
|
||||
|
||||
void HypreFGMRES::SetLogging(int logging)
|
||||
{
|
||||
HYPRE_ParCSRFlexGMRESSetLogging(fgmres_solver, logging);
|
||||
@@ -4675,6 +4839,21 @@ HypreFGMRES::~HypreFGMRES()
|
||||
HYPRE_ParCSRFlexGMRESDestroy(fgmres_solver);
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
HypreParVector HypreFGMRES::GetResiduals() const
|
||||
{
|
||||
HYPRE_ParVector r;
|
||||
HYPRE_ParCSRFlexGMRESGetResidual(fgmres_solver, &r);
|
||||
return HypreParVector(r);
|
||||
}
|
||||
|
||||
void HypreFGMRES::GetFinalAbsResidualNorm(real_t &final_res_norm,
|
||||
real_t p) const
|
||||
{
|
||||
auto r = GetResiduals();
|
||||
ParNormlp(r, p, r.GetComm());
|
||||
}
|
||||
#endif
|
||||
|
||||
void HypreDiagScale::SetOperator(const Operator &op)
|
||||
{
|
||||
@@ -5163,6 +5342,13 @@ void HypreBoomerAMG::ResetAMGPrecond()
|
||||
}
|
||||
}
|
||||
|
||||
int HypreBoomerAMG::GetMaxIter() const
|
||||
{
|
||||
HYPRE_Int max_iter;
|
||||
HYPRE_BoomerAMGGetMaxIter(amg_precond, &max_iter);
|
||||
return max_iter;
|
||||
}
|
||||
|
||||
void HypreBoomerAMG::SetOperator(const Operator &op)
|
||||
{
|
||||
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
|
||||
|
||||
+107
-7
@@ -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,
|
||||
@@ -1150,6 +1160,15 @@ public:
|
||||
return HypreUsingGPU() ? l1Jacobi : l1GS;
|
||||
}
|
||||
|
||||
/// Default solver settings:
|
||||
/// type = DefaultType()
|
||||
/// relax_times = 1
|
||||
/// omega = 1.0
|
||||
/// poly_order = 2
|
||||
/// poly_fraction = 0.3
|
||||
/// lambda = 0.5
|
||||
/// mu = -0.5
|
||||
/// taubin_iter = 40
|
||||
HypreSmoother();
|
||||
|
||||
HypreSmoother(const HypreParMatrix &A_, int type = DefaultType(),
|
||||
@@ -1159,20 +1178,28 @@ public:
|
||||
|
||||
/// Set the relaxation type and number of sweeps
|
||||
void SetType(HypreSmoother::Type type, int relax_times = 1);
|
||||
using Operator::GetType;
|
||||
void GetType(HypreSmoother::Type &type, int &relax_times) const;
|
||||
/// Set SOR-related parameters
|
||||
void SetSOROptions(real_t relax_weight, real_t omega);
|
||||
void GetSOROptions(real_t &relax_weight, real_t &omega) const;
|
||||
|
||||
/// Set parameters for polynomial smoothing
|
||||
/** By default, 10 iterations of CG are used to estimate the eigenvalues.
|
||||
Setting eig_est_cg_iter = 0 uses hypre's hypre_ParCSRMaxEigEstimate() instead. */
|
||||
void SetPolyOptions(int poly_order, real_t poly_fraction,
|
||||
int eig_est_cg_iter = 10);
|
||||
void GetPolyOptions(int &poly_order, real_t &poly_fraction,
|
||||
int &eig_est_cg_iter) const;
|
||||
/// Set parameters for Taubin's lambda-mu method
|
||||
void SetTaubinOptions(real_t lambda, real_t mu, int iter);
|
||||
void GetTaubinOptions(real_t &lambda, real_t &mu, int &iter) const;
|
||||
|
||||
/// Convenience function for setting canonical windowing parameters
|
||||
void SetWindowByName(const char* window_name);
|
||||
/// Set parameters for windowing function for FIR smoother.
|
||||
void SetWindowParameters(real_t a, real_t b, real_t c);
|
||||
void GetWindowParameters(real_t &a, real_t &b, real_t &c) const;
|
||||
/// Compute window and Chebyshev coefficients for given polynomial order.
|
||||
void SetFIRCoefficients(real_t max_eig);
|
||||
|
||||
@@ -1180,12 +1207,15 @@ public:
|
||||
/** By default, the l1-norms take their sign from the corresponding diagonal
|
||||
entries in the associated matrix. */
|
||||
void SetPositiveDiagonal(bool pos = true) { pos_l1_norms = pos; }
|
||||
bool IsPositiveDiagonal() const { return pos_l1_norms; };
|
||||
|
||||
/** Explicitly indicate whether the linear system matrix A is symmetric. If A
|
||||
is symmetric, the smoother will also be symmetric. In this case, calling
|
||||
MultTranspose will be redirected to Mult. (This is also done if the
|
||||
smoother is diagonal.) By default, A is assumed to be nonsymmetric. */
|
||||
void SetOperatorSymmetry(bool is_sym) { A_is_symmetric = is_sym; }
|
||||
/// @return true if the smoother assumes A is symmetric, false otherwise
|
||||
bool IsOperatorSymmetric() const { return A_is_symmetric; }
|
||||
|
||||
/** Set/update the associated operator. Must be called after setting the
|
||||
HypreSmoother type and options. */
|
||||
@@ -1317,6 +1347,7 @@ public:
|
||||
#endif
|
||||
|
||||
/// PCG solver in hypre
|
||||
/// Defaults to (relative) tol=1e-6, atol=0, max_iter=1000
|
||||
class HyprePCG : public HypreSolver
|
||||
{
|
||||
private:
|
||||
@@ -1324,6 +1355,9 @@ private:
|
||||
|
||||
HypreSolver * precond;
|
||||
|
||||
/// Default PCG options
|
||||
void SetDefaultOptions();
|
||||
|
||||
public:
|
||||
HyprePCG(MPI_Comm comm);
|
||||
|
||||
@@ -1332,8 +1366,11 @@ public:
|
||||
void SetOperator(const Operator &op) override;
|
||||
|
||||
void SetTol(real_t tol);
|
||||
real_t GetTol() const;
|
||||
void SetAbsTol(real_t atol);
|
||||
real_t GetAbsTol() const;
|
||||
void SetMaxIter(int max_iter);
|
||||
int GetMaxIter() const;
|
||||
void SetLogging(int logging);
|
||||
void SetPrintLevel(int print_lvl);
|
||||
|
||||
@@ -1358,12 +1395,32 @@ public:
|
||||
num_iterations = internal::to_int(num_it);
|
||||
}
|
||||
|
||||
/// Gets the relative residual norm
|
||||
void GetFinalResidualNorm(real_t &final_res_norm) const
|
||||
{
|
||||
HYPRE_ParCSRPCGGetFinalRelativeResidualNorm(pcg_solver,
|
||||
&final_res_norm);
|
||||
}
|
||||
|
||||
/// @param[in] use
|
||||
/// Convergence criterion:
|
||||
/// - when true: (r, r) < max(r_tol^2 (b, b), a_tol^2)
|
||||
/// - when false: (r, A r) < max(r_tol^2 (b, A b), a_tol^2)
|
||||
/// @sa HYPRE_PCGSetTwoNorm
|
||||
void SetUseTwoNorm(bool use);
|
||||
|
||||
/// @sa HYPRE_PCGGetTwoNorm
|
||||
bool GetUseTwoNorm() const;
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
/// Gets the internal Hypre solver residual vector.
|
||||
/// @sa HYPRE_ParCSRPCGGetResidual
|
||||
HypreParVector GetResiduals() const;
|
||||
|
||||
/// Computes the absolute residual p-norm.
|
||||
void GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p = 2) const;
|
||||
#endif
|
||||
|
||||
/// The typecast to HYPRE_Solver returns the internal pcg_solver
|
||||
operator HYPRE_Solver() const override { return pcg_solver; }
|
||||
|
||||
@@ -1381,7 +1438,8 @@ public:
|
||||
virtual ~HyprePCG();
|
||||
};
|
||||
|
||||
/// GMRES solver in hypre
|
||||
/// GMRES solver in hypre.
|
||||
/// Defaults to k=50, (relative) tol=1e-6, atol=0, max_iter=100.
|
||||
class HypreGMRES : public HypreSolver
|
||||
{
|
||||
private:
|
||||
@@ -1400,9 +1458,13 @@ public:
|
||||
void SetOperator(const Operator &op) override;
|
||||
|
||||
void SetTol(real_t tol);
|
||||
real_t GetTol() const;
|
||||
void SetAbsTol(real_t tol);
|
||||
real_t GetAbsTol() const;
|
||||
void SetMaxIter(int max_iter);
|
||||
int GetMaxIter() const;
|
||||
void SetKDim(int dim);
|
||||
int GetKDim() const;
|
||||
void SetLogging(int logging);
|
||||
void SetPrintLevel(int print_lvl);
|
||||
|
||||
@@ -1422,12 +1484,22 @@ public:
|
||||
num_iterations = internal::to_int(num_it);
|
||||
}
|
||||
|
||||
/// Gets the relative residual norm
|
||||
void GetFinalResidualNorm(real_t &final_res_norm) const
|
||||
{
|
||||
HYPRE_ParCSRGMRESGetFinalRelativeResidualNorm(gmres_solver,
|
||||
&final_res_norm);
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
/// Gets the internal Hypre solver residual vector.
|
||||
/// @sa HYPRE_ParCSRGMRESGetResidual
|
||||
HypreParVector GetResiduals() const;
|
||||
|
||||
/// Computes the absolute residual p-norm.
|
||||
void GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p = 2) const;
|
||||
#endif
|
||||
|
||||
/// The typecast to HYPRE_Solver returns the internal gmres_solver
|
||||
operator HYPRE_Solver() const override { return gmres_solver; }
|
||||
|
||||
@@ -1445,7 +1517,8 @@ public:
|
||||
virtual ~HypreGMRES();
|
||||
};
|
||||
|
||||
/// Flexible GMRES solver in hypre
|
||||
/// Flexible GMRES solver in hypre.
|
||||
/// Defaults to k=50, (relative) tol=1e-6, max_iter=100.
|
||||
class HypreFGMRES : public HypreSolver
|
||||
{
|
||||
private:
|
||||
@@ -1464,8 +1537,11 @@ public:
|
||||
void SetOperator(const Operator &op) override;
|
||||
|
||||
void SetTol(real_t tol);
|
||||
real_t GetTol() const;
|
||||
void SetMaxIter(int max_iter);
|
||||
int GetMaxIter() const;
|
||||
void SetKDim(int dim);
|
||||
int GetKDim() const;
|
||||
void SetLogging(int logging);
|
||||
void SetPrintLevel(int print_lvl);
|
||||
|
||||
@@ -1485,12 +1561,22 @@ public:
|
||||
num_iterations = internal::to_int(num_it);
|
||||
}
|
||||
|
||||
/// Gets the relative residual norm
|
||||
void GetFinalResidualNorm(real_t &final_res_norm) const
|
||||
{
|
||||
HYPRE_ParCSRFlexGMRESGetFinalRelativeResidualNorm(fgmres_solver,
|
||||
&final_res_norm);
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21500
|
||||
/// Gets the internal Hypre solver residual vector.
|
||||
/// @sa HYPRE_ParCSRFlexGMRESGetResidual
|
||||
HypreParVector GetResiduals() const;
|
||||
|
||||
/// Computes the absolute residual p-norm.
|
||||
void GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p = 2) const;
|
||||
#endif
|
||||
|
||||
/// The typecast to HYPRE_Solver returns the internal fgmres_solver
|
||||
operator HYPRE_Solver() const override { return fgmres_solver; }
|
||||
|
||||
@@ -1546,7 +1632,8 @@ public:
|
||||
virtual ~HypreDiagScale() { }
|
||||
};
|
||||
|
||||
/// The ParaSails preconditioner in hypre
|
||||
/// The ParaSails preconditioner in hypre.
|
||||
/// See SetDefaultOptions() for default solver options.
|
||||
class HypreParaSails : public HypreSolver
|
||||
{
|
||||
private:
|
||||
@@ -1675,10 +1762,14 @@ public:
|
||||
/**
|
||||
@brief Wrapper for Hypre's native parallel ILU preconditioner.
|
||||
|
||||
The default ILU factorization type is ILU(k). If you need to change this, or
|
||||
any other option, you can use the HYPRE_Solver method to cast the object for use
|
||||
with Hypre's native functions. For example, if want to use natural ordering
|
||||
rather than RCM reordering, you can use the following approach:
|
||||
Default parameters: ILU(k) factorization type, tol=0.0 (for use as a
|
||||
preconditioner), fill level = 1 (for ILU(k)), reverse Cuthill-McKee (RCM)
|
||||
re-ordering.
|
||||
|
||||
If you need to change this, or any other option, you can use the HYPRE_Solver
|
||||
method to cast the object for use with Hypre's native functions. For example, if
|
||||
want to use natural ordering rather than RCM reordering, you can use the
|
||||
following approach:
|
||||
|
||||
@code
|
||||
mfem::HypreILU ilu();
|
||||
@@ -1819,6 +1910,7 @@ public:
|
||||
|
||||
void SetMaxIter(int max_iter)
|
||||
{ HYPRE_BoomerAMGSetMaxIter(amg_precond, max_iter); }
|
||||
int GetMaxIter() const;
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetMaxLevels(int max_levels)
|
||||
@@ -1843,6 +1935,8 @@ public:
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetRelaxType(int relax_type)
|
||||
{ HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type); }
|
||||
// not implemented in hypre
|
||||
// int GetRelaxType() const;
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetCycleType(int cycle_type)
|
||||
@@ -2143,8 +2237,14 @@ public:
|
||||
~HypreLOBPCG();
|
||||
|
||||
void SetTol(real_t tol);
|
||||
// not implemented in HYPRE
|
||||
// real_t GetTol() const;
|
||||
void SetRelTol(real_t rel_tol);
|
||||
// not implemented in HYPRE
|
||||
// real_t GetRelTol() const;
|
||||
void SetMaxIter(int max_iter);
|
||||
// not implemented in HYPRE
|
||||
// int GetMaxIter() const;
|
||||
void SetPrintLevel(int logging);
|
||||
void SetNumModes(int num_eigs) { nev = num_eigs; }
|
||||
void SetPrecondUsageMode(int pcg_mode);
|
||||
|
||||
+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);
|
||||
|
||||
|
||||
@@ -3639,12 +3639,20 @@ void PetscBDDCSolver::BDDCSolverConstructor(const PetscBDDCSolverParams &opts)
|
||||
// make sure ess/nat_dof have been collectively set
|
||||
PetscBool lpr = PETSC_FALSE,pr;
|
||||
if (opts.ess_dof) { lpr = PETSC_TRUE; }
|
||||
#if PETSC_VERSION_LT(3,24,0)
|
||||
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPIU_BOOL,MPI_LOR,comm);
|
||||
#else
|
||||
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPI_C_BOOL,MPI_LOR,comm);
|
||||
#endif
|
||||
CCHKERRQ(comm,mpiierr);
|
||||
MFEM_VERIFY(lpr == pr,"ess_dof should be collectively set");
|
||||
lpr = PETSC_FALSE;
|
||||
if (opts.nat_dof) { lpr = PETSC_TRUE; }
|
||||
#if PETSC_VERSION_LT(3,24,0)
|
||||
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPIU_BOOL,MPI_LOR,comm);
|
||||
#else
|
||||
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPI_C_BOOL,MPI_LOR,comm);
|
||||
#endif
|
||||
CCHKERRQ(comm,mpiierr);
|
||||
MFEM_VERIFY(lpr == pr,"nat_dof should be collectively set");
|
||||
// make sure fields have been collectively set
|
||||
@@ -4058,8 +4066,13 @@ void PetscNonlinearSolver::SetOperator(const Operator &op)
|
||||
ls = (PetscBool)(height == op.Height() && width == op.Width() &&
|
||||
(void*)&op == fctx &&
|
||||
(void*)&op == jctx);
|
||||
#if PETSC_VERSION_LT(3,24,0)
|
||||
mpiierr = MPI_Allreduce(&ls,&gs,1,MPIU_BOOL,MPI_LAND,
|
||||
PetscObjectComm((PetscObject)snes));
|
||||
#else
|
||||
mpiierr = MPI_Allreduce(&ls,&gs,1,MPI_C_BOOL,MPI_LAND,
|
||||
PetscObjectComm((PetscObject)snes));
|
||||
#endif
|
||||
CCHKERRQ(PetscObjectComm((PetscObject)snes),mpiierr);
|
||||
if (!gs)
|
||||
{
|
||||
|
||||
@@ -1066,6 +1066,11 @@ void SparseMatrix::BooleanMultTranspose(const Array<int> &x,
|
||||
y.SetSize(Width());
|
||||
y = 0;
|
||||
|
||||
HostReadI();
|
||||
HostReadJ();
|
||||
x.HostRead();
|
||||
y.HostReadWrite();
|
||||
|
||||
for (int i = 0; i < Height(); i++)
|
||||
{
|
||||
if (x[i])
|
||||
|
||||
+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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -794,7 +794,6 @@ status info:
|
||||
$(info MFEM_MPI_NP = $(MFEM_MPI_NP))
|
||||
@true
|
||||
|
||||
ASTYLE_BIN = astyle
|
||||
ASTYLE = $(ASTYLE_BIN) --options=$(SRC)config/mfem.astylerc
|
||||
ASTYLE_VER = "Artistic Style Version 3.1"
|
||||
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_DIRS) config,$(dir)/*.?pp)
|
||||
|
||||
@@ -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)
|
||||
|
||||
+48
-15
@@ -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.
|
||||
@@ -2075,12 +2078,13 @@ public:
|
||||
contrary to the ones obtained through Mesh::GetFacesElements and can
|
||||
directly be used, e.g., Elem1 and Elem2 indices.
|
||||
Likewise the orientations for Elem1 and Elem2 already take into account
|
||||
special cases and can be used as is.
|
||||
*/
|
||||
special cases and can be used as is. */
|
||||
struct FaceInformation
|
||||
{
|
||||
/// The face topology (boundary, conforming, or nonconforming).
|
||||
FaceTopology topology;
|
||||
|
||||
/// Information about the adjacent elements.
|
||||
struct
|
||||
{
|
||||
ElementLocation location;
|
||||
@@ -2090,8 +2094,13 @@ public:
|
||||
int orientation;
|
||||
} element[2];
|
||||
|
||||
/// Detailed face information (see FaceInfoTag).
|
||||
FaceInfoTag tag;
|
||||
|
||||
/// If the face is nonconforming, the index of the NC face. -1 otherwise.
|
||||
int ncface;
|
||||
|
||||
/// The point matrix for nonconforming faces.
|
||||
const DenseMatrix* point_matrix;
|
||||
|
||||
/** @brief Return true if the face is a local interior face which is NOT
|
||||
@@ -2110,21 +2119,20 @@ public:
|
||||
|
||||
/** @brief return true if the face is an interior face to the computation
|
||||
domain, either a local or shared interior face (not a boundary face)
|
||||
which is NOT a master nonconforming face.
|
||||
*/
|
||||
which is NOT a master nonconforming face. */
|
||||
bool IsInterior() const
|
||||
{
|
||||
return topology == FaceTopology::Conforming ||
|
||||
topology == FaceTopology::Nonconforming;
|
||||
}
|
||||
|
||||
/** @brief Return true if the face is a boundary face. */
|
||||
/// Return true if the face is a boundary face.
|
||||
bool IsBoundary() const
|
||||
{
|
||||
return topology == FaceTopology::Boundary;
|
||||
}
|
||||
|
||||
/// @brief Return true if the face is of the same type as @a type.
|
||||
/// Return true if the face is of the same type as @a type.
|
||||
bool IsOfFaceType(FaceType type) const
|
||||
{
|
||||
switch (type)
|
||||
@@ -2138,13 +2146,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Return true if the face is a conforming face.
|
||||
/// Return true if the face is a conforming face.
|
||||
bool IsConforming() const
|
||||
{
|
||||
return topology == FaceTopology::Conforming;
|
||||
}
|
||||
|
||||
/// @brief Return true if the face is a nonconforming fine face.
|
||||
/// Return true if the face is a nonconforming fine face.
|
||||
bool IsNonconformingFine() const
|
||||
{
|
||||
return topology == FaceTopology::Nonconforming &&
|
||||
@@ -2152,7 +2160,7 @@ public:
|
||||
element[1].conformity == ElementConformity::Superset);
|
||||
}
|
||||
|
||||
/// @brief Return true if the face is a nonconforming coarse face.
|
||||
/// Return true if the face is a nonconforming coarse face.
|
||||
/** Note that ghost nonconforming master faces cannot be clearly
|
||||
identified as such with the currently available information, so this
|
||||
method will return false for such faces. */
|
||||
@@ -2162,7 +2170,7 @@ public:
|
||||
element[1].conformity == ElementConformity::Subset;
|
||||
}
|
||||
|
||||
/// @brief cast operator from FaceInformation to FaceInfo.
|
||||
/// cast operator from FaceInformation to FaceInfo.
|
||||
operator Mesh::FaceInfo() const;
|
||||
};
|
||||
|
||||
@@ -2538,13 +2546,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 +3212,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)
|
||||
{
|
||||
|
||||
+653
-243
File diff suppressed because it is too large
Load Diff
+147
-38
@@ -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;
|
||||
@@ -59,18 +74,28 @@ public:
|
||||
integers are read, for order and number of control points. */
|
||||
KnotVector(std::istream &input);
|
||||
|
||||
/** @brief Create a KnotVector with undefined knots (initialized to -1) of
|
||||
order @a order and number of control points @a NCP. */
|
||||
KnotVector(int order, int NCP);
|
||||
/** @brief Create a KnotVector with order @a order.
|
||||
When @a NCP is not provided the number of control points is set to
|
||||
@a order + 1, and the first @a order + 1 knots are set to 0 and last
|
||||
@a order + 1 knots are set to 1.
|
||||
When @a NCP is given number of control points is @a NCP and
|
||||
the knots are initialized to -1) */
|
||||
KnotVector(int order, int NCP = -1);
|
||||
|
||||
/** @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.
|
||||
|
||||
The intervals refer to spans between unique knot values (not counting
|
||||
zero-size intervals at repeated knots), and the continuity values should
|
||||
be >= -1 (discontinuous) and <= order-1 (maximally-smooth for the given
|
||||
polynomial degree). Periodicity is not supported.
|
||||
*/
|
||||
polynomial degree). Periodicity is not supported.*/
|
||||
KnotVector(int order, const Vector& intervals,
|
||||
const Array<int>& continuity);
|
||||
|
||||
@@ -103,13 +128,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 +217,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 +285,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 +315,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 +694,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 +893,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 +922,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 +1042,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
-4
@@ -1195,14 +1195,22 @@ 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; }
|
||||
|
||||
// enlarge Mesh::faces_info for ghost slaves
|
||||
MFEM_ASSERT(pmesh.faces_info.Size() == nfaces, "");
|
||||
MFEM_ASSERT(pmesh.GetNumFaces() == nfaces, "");
|
||||
pmesh.faces_info.SetSize(nfaces + nghosts);
|
||||
for (int i = nfaces; i < pmesh.faces_info.Size(); i++)
|
||||
@@ -1303,14 +1311,12 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
|
||||
// Mesh::ApplyLocalSlaveTransformation.
|
||||
}
|
||||
|
||||
MFEM_ASSERT(fi.NCFace < 0, "fi.NCFace = " << fi.NCFace);
|
||||
fi.NCFace = pmesh.nc_faces_info.Size();
|
||||
pmesh.nc_faces_info.Append(Mesh::NCFaceInfo(true, sf.master, pm));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user