Compare commits
458
Commits
adform-dev
...
complex3p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83bf172ebc | ||
|
|
1e367f7970 | ||
|
|
20de0f0a09 | ||
|
|
b901ee0fa6 | ||
|
|
ed0af223e2 | ||
|
|
de1e969abd | ||
|
|
819648a334 | ||
|
|
6c5831d15e | ||
|
|
eb149e7385 | ||
|
|
6799097db9 | ||
|
|
466743498e | ||
|
|
53be0a3c9f | ||
|
|
afcce33a07 | ||
|
|
dee92b1497 | ||
|
|
9b847ddcff | ||
|
|
4eda8d4fa0 | ||
|
|
656f1c146e | ||
|
|
1155c003a8 | ||
|
|
20f92199d2 | ||
|
|
841769637b | ||
|
|
ecef55a712 | ||
|
|
759987ef31 | ||
|
|
316bb189c9 | ||
|
|
b5682f808f | ||
|
|
a77fb59626 | ||
|
|
36796e6d55 | ||
|
|
83669def7d | ||
|
|
2399f47207 | ||
|
|
5345d5e766 | ||
|
|
2e6b9e024b | ||
|
|
35882669b9 | ||
|
|
c0ae2e8da5 | ||
|
|
3f9443d497 | ||
|
|
9794db95c5 | ||
|
|
63925e8ddf | ||
|
|
928d66caba | ||
|
|
d09826e403 | ||
|
|
bbe4b002f1 | ||
|
|
0ae9e69567 | ||
|
|
718e9b2ce3 | ||
|
|
17a8e525a4 | ||
|
|
2faf0ae640 | ||
|
|
92a87e1253 | ||
|
|
f4c2758e82 | ||
|
|
9535299c2a | ||
|
|
9d87c4efcd | ||
|
|
c5ceee3ef5 | ||
|
|
3d946569a1 | ||
|
|
a8ebd745a8 | ||
|
|
3191cfa1c3 | ||
|
|
388e07a373 | ||
|
|
320deb43ec | ||
|
|
04444b15d3 | ||
|
|
caf5d7b471 | ||
|
|
736b2c79b2 | ||
|
|
92748c1c70 | ||
|
|
7c27b32e90 | ||
|
|
f442f6cee1 | ||
|
|
1342a59943 | ||
|
|
fe23fcd39b | ||
|
|
de9fb83efe | ||
|
|
60b1997858 | ||
|
|
de990a5fdc | ||
|
|
3ee90e4c0e | ||
|
|
b4c53cff15 | ||
|
|
69981e1996 | ||
|
|
f84a8d80e0 | ||
|
|
50197feee0 | ||
|
|
116ae7bbed | ||
|
|
ba69eadeac | ||
|
|
aa1da8bee2 | ||
|
|
476e95305d | ||
|
|
d5e291e989 | ||
|
|
6d0f5971ad | ||
|
|
3a21476b5c | ||
|
|
9974c0c6a9 | ||
|
|
55e117bb82 | ||
|
|
0eaa5824ee | ||
|
|
ae5a84f2ef | ||
|
|
a1a0aeb6af | ||
|
|
241a5a2544 | ||
|
|
b9e26ee081 | ||
|
|
54111e4fd5 | ||
|
|
da7c353df6 | ||
|
|
9b25209337 | ||
|
|
58e6db4118 | ||
|
|
d226010d4e | ||
|
|
805d51a2eb | ||
|
|
6e7e1092db | ||
|
|
95e7dd3df8 | ||
|
|
ecd61941d6 | ||
|
|
8f010f3c0c | ||
|
|
bdf0e9b1d0 | ||
|
|
84dcb16949 | ||
|
|
01df630912 | ||
|
|
80ff56cfca | ||
|
|
58bf8f54e3 | ||
|
|
2552255ffe | ||
|
|
8a130243fb | ||
|
|
3acc834f1d | ||
|
|
e06097b9af | ||
|
|
09372aea2d | ||
|
|
502b95fb86 | ||
|
|
777d222577 | ||
|
|
be1b800b61 | ||
|
|
4f5619c88a | ||
|
|
5231ed302c | ||
|
|
b796dae62c | ||
|
|
7bcba91890 | ||
|
|
33dfee978b | ||
|
|
73ff58db37 | ||
|
|
7d1bf5a748 | ||
|
|
921f4aaab6 | ||
|
|
a5806ea9eb | ||
|
|
ca721dd1ae | ||
|
|
04f44f2a50 | ||
|
|
de36574fa7 | ||
|
|
0e2735128c | ||
|
|
83c9d7289a | ||
|
|
0bbbc43ebf | ||
|
|
216d50b0ad | ||
|
|
428b515668 | ||
|
|
72924b927b | ||
|
|
cf6ce3c823 | ||
|
|
a712d0ecb5 | ||
|
|
165b7910ec | ||
|
|
83181083e9 | ||
|
|
aa1127fdf0 | ||
|
|
aba640498e | ||
|
|
908b60547a | ||
|
|
c059fe7f41 | ||
|
|
8c55d3aa1f | ||
|
|
b420256c2f | ||
|
|
9b12b445d5 | ||
|
|
d21b9d2caf | ||
|
|
51cf18da38 | ||
|
|
d0c3ddf470 | ||
|
|
b8922a8988 | ||
|
|
d0fd04c7dd | ||
|
|
7a3376213c | ||
|
|
08de600931 | ||
|
|
3ac5c3a4b7 | ||
|
|
2e53605ae4 | ||
|
|
6dac4554c0 | ||
|
|
648017615c | ||
|
|
6168948232 | ||
|
|
906e82f099 | ||
|
|
36e34533e5 | ||
|
|
9a1616ac3f | ||
|
|
4b4cde0da5 | ||
|
|
ecace58420 | ||
|
|
736c2a486d | ||
|
|
a74010deb5 | ||
|
|
4d5fdfd553 | ||
|
|
789e51f2fb | ||
|
|
64a961cbf8 | ||
|
|
f58b69b805 | ||
|
|
8b63108110 | ||
|
|
12363d84a1 | ||
|
|
533b45cabd | ||
|
|
4f32a52915 | ||
|
|
1b2f6d2872 | ||
|
|
4660886599 | ||
|
|
48f2b999e7 | ||
|
|
a0cdaca8ce | ||
|
|
5f57038f02 | ||
|
|
85a38a3134 | ||
|
|
a617d51b8d | ||
|
|
ffc0a9ee7d | ||
|
|
7083d53dd7 | ||
|
|
1b8fd5466e | ||
|
|
4e81be2155 | ||
|
|
95adb0d115 | ||
|
|
4a0142a1c6 | ||
|
|
69a7d67ad3 | ||
|
|
5dfa41aaf9 | ||
|
|
1cbecbc0cf | ||
|
|
c942da97a6 | ||
|
|
7e9d652aa9 | ||
|
|
9828a5b78d | ||
|
|
51d46e8775 | ||
|
|
34e4dd9a5d | ||
|
|
ff1c2deb4f | ||
|
|
7b3e7ee2fe | ||
|
|
cfead6a03b | ||
|
|
41b4f7183b | ||
|
|
fbb9b4938d | ||
|
|
78a4889fd0 | ||
|
|
336ee95d26 | ||
|
|
4051ee21f8 | ||
|
|
3d314e419a | ||
|
|
d28d6e4641 | ||
|
|
647fda9c97 | ||
|
|
0c12c29663 | ||
|
|
ea01d71fb0 | ||
|
|
1b91c0c678 | ||
|
|
72fe130e7e | ||
|
|
e0efc56aa2 | ||
|
|
5594557196 | ||
|
|
64eb077e50 | ||
|
|
40510f07ca | ||
|
|
b2a61eb9dc | ||
|
|
e4a71969a3 | ||
|
|
916d7e7a24 | ||
|
|
982b8417e5 | ||
|
|
df6cea3833 | ||
|
|
65f2add11d | ||
|
|
3bb3974bcb | ||
|
|
326cbadf13 | ||
|
|
19634570f7 | ||
|
|
020a97f0fc | ||
|
|
f82d078562 | ||
|
|
dab294dabf | ||
|
|
474cfafb95 | ||
|
|
4d23cd820b | ||
|
|
3face5a823 | ||
|
|
5d085f9710 | ||
|
|
7428c2053f | ||
|
|
b7b6d8142f | ||
|
|
8bd7b14714 | ||
|
|
cb78aa6c4a | ||
|
|
0e47d1297f | ||
|
|
a722d2b704 | ||
|
|
d87c45a95e | ||
|
|
10cecd5134 | ||
|
|
34070ab952 | ||
|
|
f5830b612a | ||
|
|
78a6152900 | ||
|
|
28667f27f6 | ||
|
|
1d1bacf965 | ||
|
|
0239bd42c0 | ||
|
|
343e45e749 | ||
|
|
ee7b17f18a | ||
|
|
0c315483e7 | ||
|
|
3f7f0e9933 | ||
|
|
43ce4436d8 | ||
|
|
81bbf1c998 | ||
|
|
1b52f0fb50 | ||
|
|
44d95614f5 | ||
|
|
05c8ee6f80 | ||
|
|
c60500466b | ||
|
|
a12bc35350 | ||
|
|
c5896c35a1 | ||
|
|
4645f97f0f | ||
|
|
8ec9a6deda | ||
|
|
092b808022 | ||
|
|
455b92546e | ||
|
|
e1bea63afb | ||
|
|
162ac70998 | ||
|
|
908f98d8a6 | ||
|
|
c5d1b3701c | ||
|
|
b65c390271 | ||
|
|
40ebc18e97 | ||
|
|
a4ced201d0 | ||
|
|
e3d6a0e867 | ||
|
|
42b3b64c8e | ||
|
|
9a238d002f | ||
|
|
a5d5c1ba32 | ||
|
|
4c8c8bea49 | ||
|
|
af941c7aec | ||
|
|
9345635954 | ||
|
|
19fda404f6 | ||
|
|
de5a4928d4 | ||
|
|
ddad761fd0 | ||
|
|
29a3609ff4 | ||
|
|
0626694f33 | ||
|
|
1de94adf90 | ||
|
|
a3b8ebf2db | ||
|
|
d5310e7ac3 | ||
|
|
42658ec9f8 | ||
|
|
0680fd802b | ||
|
|
eb3bb90760 | ||
|
|
4364dfd1f4 | ||
|
|
7b8ef12161 | ||
|
|
3b4565773c | ||
|
|
76f7c4e698 | ||
|
|
1ed00f4896 | ||
|
|
dfc50efa35 | ||
|
|
924e321351 | ||
|
|
a2f5eb54cb | ||
|
|
ec2df64626 | ||
|
|
ccbdf2b1eb | ||
|
|
39ff9c78c2 | ||
|
|
520c63b56b | ||
|
|
4bc672906c | ||
|
|
b11168cfe0 | ||
|
|
111357a964 | ||
|
|
c313f8356b | ||
|
|
d691346f3c | ||
|
|
980074c4ac | ||
|
|
605ed11b31 | ||
|
|
d9d7510f44 | ||
|
|
7b0fb2c641 | ||
|
|
f5ac6c97d4 | ||
|
|
4012ca1df6 | ||
|
|
d732a479aa | ||
|
|
4235a22838 | ||
|
|
ad363ada13 | ||
|
|
db4504a0f8 | ||
|
|
e069d9b034 | ||
|
|
021e7baa29 | ||
|
|
e6ceb405e0 | ||
|
|
71c947d966 | ||
|
|
5db9988a41 | ||
|
|
a19b34b403 | ||
|
|
e55fbc7af5 | ||
|
|
8f5d0ef084 | ||
|
|
0cdbcd647e | ||
|
|
35159031ee | ||
|
|
240b2b811c | ||
|
|
1e58fca11e | ||
|
|
78bc8176e7 | ||
|
|
2237a20063 | ||
|
|
514a0bae58 | ||
|
|
45b2f40d98 | ||
|
|
817e9247e9 | ||
|
|
6994291f15 | ||
|
|
a3a99b0345 | ||
|
|
80a3f24731 | ||
|
|
9a84630556 | ||
|
|
ba6d89b4cc | ||
|
|
9d07fea8f4 | ||
|
|
2886dcc849 | ||
|
|
749f3da7cc | ||
|
|
4079f31447 | ||
|
|
90ce59a56b | ||
|
|
1f1b545ec0 | ||
|
|
b3b63da0c2 | ||
|
|
b2f542bd84 | ||
|
|
22f2591c9e | ||
|
|
98d7e056ad | ||
|
|
72b919e633 | ||
|
|
a6bac0be4c | ||
|
|
872c4dba86 | ||
|
|
2ea3200f0c | ||
|
|
32683f180b | ||
|
|
68c7351757 | ||
|
|
fe1e1a4128 | ||
|
|
7e3d262c63 | ||
|
|
374d71f68c | ||
|
|
61bd7dcc8d | ||
|
|
222d13eabf | ||
|
|
3dbbfdbdd6 | ||
|
|
7a29a36c78 | ||
|
|
e5e58f8328 | ||
|
|
60369fec8f | ||
|
|
7a9f2f966f | ||
|
|
20134f9213 | ||
|
|
cd3745046d | ||
|
|
d220d72015 | ||
|
|
8958460dbf | ||
|
|
c7480fd97e | ||
|
|
f5b648fd4d | ||
|
|
1d609acb2b | ||
|
|
382bf2b8b6 | ||
|
|
b213d80b12 | ||
|
|
fdf86706ea | ||
|
|
2c7f6300e0 | ||
|
|
d11c17cba2 | ||
|
|
573e1ab7f4 | ||
|
|
c0ca09165f | ||
|
|
17142d2b36 | ||
|
|
bbe9a15202 | ||
|
|
0f6555e9ae | ||
|
|
1bac4f7c19 | ||
|
|
cc21811d1a | ||
|
|
a0615bbaef | ||
|
|
df0a751dc9 | ||
|
|
38eeac6cb3 | ||
|
|
e9b865f2a0 | ||
|
|
8f3883ce1a | ||
|
|
cb637556c3 | ||
|
|
bc5a051d8c | ||
|
|
0b4861a28f | ||
|
|
8fe9ecf433 | ||
|
|
48d77aa7d1 | ||
|
|
ca9f9193c4 | ||
|
|
0425d658e2 | ||
|
|
9a8258e65e | ||
|
|
8e02a5f475 | ||
|
|
9d8490a4bf | ||
|
|
010a2e2246 | ||
|
|
af958ec46c | ||
|
|
3365b91583 | ||
|
|
46666d6c40 | ||
|
|
d21c52603f | ||
|
|
bdf5a2bdc5 | ||
|
|
b319fbb1a6 | ||
|
|
1551bf8858 | ||
|
|
c28a082a0d | ||
|
|
f1dff5b830 | ||
|
|
88c70ecb61 | ||
|
|
356c3034c4 | ||
|
|
78c5229b2a | ||
|
|
b280a5c1bc | ||
|
|
30914c9001 | ||
|
|
372409764a | ||
|
|
dc33b2f048 | ||
|
|
9624d9de6f | ||
|
|
9ba4ce9312 | ||
|
|
c346d4601d | ||
|
|
d1b2b6eabf | ||
|
|
fd45550d7d | ||
|
|
602f9522be | ||
|
|
f02d161457 | ||
|
|
8228f99711 | ||
|
|
8d87e4a93a | ||
|
|
793cf0c173 | ||
|
|
68e930cc3b | ||
|
|
f17d263064 | ||
|
|
05b0a7897c | ||
|
|
3645f47cc1 | ||
|
|
3da3f275bf | ||
|
|
58e23e3b2d | ||
|
|
5260e5b971 | ||
|
|
9f544e448e | ||
|
|
902889abe5 | ||
|
|
30fde8d98c | ||
|
|
f98a93ea81 | ||
|
|
f919ab31ba | ||
|
|
bbc29bcf9b | ||
|
|
fd59cceda3 | ||
|
|
9d9b126cc8 | ||
|
|
11e5037e3c | ||
|
|
b968557873 | ||
|
|
dca9990bb9 | ||
|
|
685f274044 | ||
|
|
5c437ce96c | ||
|
|
b98932091c | ||
|
|
1d8cc71777 | ||
|
|
1454353e2c | ||
|
|
4f5b6a7495 | ||
|
|
5f7462ec8d | ||
|
|
68139116f8 | ||
|
|
f1ccc896f9 | ||
|
|
d7abdfded3 | ||
|
|
20aa7b4c6a | ||
|
|
9bf820a023 | ||
|
|
478dbd99c7 | ||
|
|
50ee9ae8d0 | ||
|
|
1f85594b4f | ||
|
|
08c7b33344 | ||
|
|
15ae2763d3 | ||
|
|
a407d30cfa | ||
|
|
1b2e165c89 | ||
|
|
60350c6ebb | ||
|
|
287d8ec540 | ||
|
|
51cf866693 | ||
|
|
5097dd2e3d | ||
|
|
2bb926094a | ||
|
|
05c74af854 | ||
|
|
13f39b38a5 | ||
|
|
ed7d4d4c44 | ||
|
|
bd3961ba0a | ||
|
|
bf90f2b687 | ||
|
|
f67482e8d3 | ||
|
|
1d220ef410 | ||
|
|
156a93937f |
@@ -215,6 +215,7 @@ miniapps/meshing/polar-nc.mesh
|
||||
|
||||
miniapps/navier/navier_mms
|
||||
miniapps/navier/navier_kovasznay
|
||||
miniapps/navier/navier_kovasznay_vs
|
||||
miniapps/navier/navier_tgv
|
||||
miniapps/navier/navier_shear
|
||||
miniapps/navier/navier_3dfoc
|
||||
|
||||
@@ -11,6 +11,12 @@
|
||||
Version 4.2.1 (development)
|
||||
===========================
|
||||
|
||||
- Added high-order matrix-free auxiliary Maxwell solver for H(curl) problems,
|
||||
as described in Barker and Kolev 2020 (https://doi.org/10.1002/nla.2348).
|
||||
|
||||
- Added matrix-free GPU-enabled implementations of GradientInterpolator and
|
||||
IdentityInterpolator.
|
||||
|
||||
- Added interface to MUMPS direct solver. Its usage is demonstrated in ex25p.
|
||||
See http://mumps.enseeiht.fr/ for more details. Supported versions >= 5.1.1.
|
||||
|
||||
@@ -20,11 +26,41 @@ Version 4.2.1 (development)
|
||||
- Added a "scaled Jacobian" visualization option in the Mesh Explorer miniapp to
|
||||
help identify elements with poor mesh quality.
|
||||
|
||||
- Added support for the "BR2" discontinuous Galerkin discretization for
|
||||
diffusion via DGDiffusionBR2Integrator (see Example 14/14p).
|
||||
|
||||
- Generalized the Multigrid class to support non-geometric multigrid. The
|
||||
previous functionality, based on FiniteElementSpaceHierarchy, is now available
|
||||
in the derived class GeometricMultigrid.
|
||||
|
||||
- Upgraded the Catch unit test framework from version 2.13.0 to version 2.13.2.
|
||||
|
||||
- Implemented a filter method for the Navier miniapp to stabilize highly
|
||||
turbulent flows in direct numerical simulation.
|
||||
|
||||
- Added partial assembly and device support to Example 25/25p, with diagonal
|
||||
preconditioning.
|
||||
|
||||
- Implemented a variable step-size IMEX (VSSIMEX) method for the Navier miniapp.
|
||||
|
||||
- Added new mesh quality metrics and improved the untangling capabilities of the
|
||||
TMOP-based mesh optimization algorithms.
|
||||
|
||||
- Changed the interface for the error estimator.
|
||||
|
||||
- Implemented the parallel Kelly error indicator for scalar-valued problems.
|
||||
|
||||
- Added new classes DenseSymmetricMatrix and SymmetricMatrixCoefficient for
|
||||
efficient evaluation of symmetric matrix coefficients. This replaces the now
|
||||
deprecated EvalSymmetric in MatrixCoefficient. Added DiagonalMatrixCoefficient
|
||||
for clarity, which is a typedef of VectorCoefficient.
|
||||
|
||||
- Implemented an adaptive linear solver tolerance option for NewtonSolver based
|
||||
on the algorithm of Eisenstat and Walker.
|
||||
|
||||
- Extending support for L2 basis functions using MapTypes VALUE and INTEGRAL in
|
||||
linear interpolators and GridFunction "GetValue" methods.
|
||||
|
||||
|
||||
Version 4.2, released on October 30, 2020
|
||||
=========================================
|
||||
|
||||
+2
-1
@@ -9,7 +9,8 @@
|
||||
# terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.11)
|
||||
# The variable CMAKE_CXX_STANDARD and related were introduced in CMake v3.1
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
|
||||
"Path to optional user configuration file.")
|
||||
|
||||
|
||||
@@ -710,10 +710,10 @@ The specific libraries and their options are:
|
||||
Versions: libCEED >= 0.7.
|
||||
|
||||
- RAJA (optional), used when MFEM_USE_RAJA = YES.
|
||||
Beginning with MFEM v4.1, only RAJA v0.10.0+ is supported.
|
||||
Beginning with MFEM v4.3, only RAJA v0.13.0+ is supported.
|
||||
URL: https://github.com/LLNL/RAJA
|
||||
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
|
||||
Versions: RAJA >= 0.10.0.
|
||||
Versions: RAJA >= 0.13.0.
|
||||
|
||||
- Umpire, used when MFEM_USE_UMPIRE = YES.
|
||||
URL: https://github.com/LLNL/Umpire
|
||||
|
||||
@@ -25,16 +25,16 @@ mfem_find_package(METIS METIS METIS_DIR "include;Lib" "metis.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
int n = 10;
|
||||
int nparts = 5;
|
||||
int edgecut;
|
||||
int* partitioning = new int[10];
|
||||
int* I = partitioning,
|
||||
idx_t n = 10;
|
||||
idx_t nparts = 5;
|
||||
idx_t edgecut;
|
||||
idx_t* partitioning = new idx_t[10];
|
||||
idx_t* I = partitioning,
|
||||
* J = partitioning;
|
||||
|
||||
int ncon = 1;
|
||||
idx_t ncon = 1;
|
||||
int err;
|
||||
int options[40];
|
||||
idx_t options[40];
|
||||
|
||||
METIS_SetDefaultOptions(options);
|
||||
options[10] = 1; // set METIS_OPTION_CONTIG
|
||||
|
||||
@@ -754,7 +754,13 @@ function(mfem_export_mk_files)
|
||||
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
|
||||
set(MFEM_HOST_CXX ${MFEM_CXX})
|
||||
set(MFEM_CPPFLAGS "")
|
||||
string(STRIP "${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
|
||||
get_target_property(cxx_std mfem CXX_STANDARD)
|
||||
# For now, we ignore the setting of the CXX_EXTENSIONS property. If this
|
||||
# property is set, then we need to use a variable like:
|
||||
# CMAKE_CXX11_EXTENSION_COMPILE_OPTION
|
||||
set(cxx_std_flag ${CMAKE_CXX${cxx_std}_STANDARD_COMPILE_OPTION})
|
||||
string(STRIP
|
||||
"${cxx_std_flag} ${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
|
||||
MFEM_CXXFLAGS)
|
||||
set(MFEM_TPLFLAGS "")
|
||||
foreach(dir ${MFEM_TPL_INCLUDE_DIRS})
|
||||
|
||||
@@ -34,6 +34,12 @@
|
||||
// Macro needed to get defines like M_PI from <cmath>. (Visual Studio C++ only?)
|
||||
#define _USE_MATH_DEFINES
|
||||
#endif
|
||||
// On Cygwin the option -std=c++11 prevents the definition of M_PI. Defining
|
||||
// the following macro allows us to get M_PI and some needed functions, e.g.
|
||||
// posix_memalign(), strdup(), strerror_r().
|
||||
#ifdef __CYGWIN__
|
||||
#define _XOPEN_SOURCE 600
|
||||
#endif
|
||||
|
||||
// Check dependencies:
|
||||
|
||||
|
||||
+27
-23
@@ -161,7 +161,7 @@ endif
|
||||
ZLIB_DIR =
|
||||
ZLIB_OPT = $(if $(ZLIB_DIR),-I$(ZLIB_DIR)/include)
|
||||
ZLIB_LIB = $(if $(ZLIB_DIR),$(ZLIB_RPATH) -L$(ZLIB_DIR)/lib ,)-lz
|
||||
ZLIB_RPATH = -Wl,-rpath,$(ZLIB_DIR)/lib
|
||||
ZLIB_RPATH = $(XLINKER)-rpath,$(ZLIB_DIR)/lib
|
||||
|
||||
LIBUNWIND_OPT = -g
|
||||
LIBUNWIND_LIB = $(if $(NOTMAC),-lunwind -ldl,)
|
||||
@@ -231,19 +231,21 @@ MESQUITE_LIB = -L$(MESQUITE_DIR)/lib -lmesquite
|
||||
LIB_RT = $(if $(NOTMAC),-lrt,)
|
||||
SUITESPARSE_DIR = @MFEM_DIR@/../SuiteSparse
|
||||
SUITESPARSE_OPT = -I$(SUITESPARSE_DIR)/include
|
||||
SUITESPARSE_LIB = -Wl,-rpath,$(SUITESPARSE_DIR)/lib -L$(SUITESPARSE_DIR)/lib\
|
||||
-lklu -lbtf -lumfpack -lcholmod -lcolamd -lamd -lcamd -lccolamd\
|
||||
-lsuitesparseconfig $(LIB_RT) $(METIS_LIB) $(LAPACK_LIB)
|
||||
SUITESPARSE_LIB = $(XLINKER)-rpath,$(SUITESPARSE_DIR)/lib\
|
||||
-L$(SUITESPARSE_DIR)/lib -lklu -lbtf -lumfpack -lcholmod -lcolamd -lamd -lcamd\
|
||||
-lccolamd -lsuitesparseconfig $(LIB_RT) $(METIS_LIB) $(LAPACK_LIB)
|
||||
|
||||
# SuperLU library configuration
|
||||
ifeq ($(MFEM_USE_SUPERLU5),YES)
|
||||
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_5.1.0
|
||||
SUPERLU_OPT = -I$(SUPERLU_DIR)/include
|
||||
SUPERLU_LIB = -Wl,-rpath,$(SUPERLU_DIR)/lib -L$(SUPERLU_DIR)/lib -lsuperlu_dist_5.1.0
|
||||
SUPERLU_LIB = $(XLINKER)-rpath,$(SUPERLU_DIR)/lib -L$(SUPERLU_DIR)/lib\
|
||||
-lsuperlu_dist_5.1.0
|
||||
else
|
||||
SUPERLU_DIR = @MFEM_DIR@/../SuperLU_DIST_6.3.1
|
||||
SUPERLU_OPT = -I$(SUPERLU_DIR)/include
|
||||
SUPERLU_LIB = -Wl,-rpath,$(SUPERLU_DIR)/lib64 -L$(SUPERLU_DIR)/lib64 -lsuperlu_dist -lblas
|
||||
SUPERLU_LIB = $(XLINKER)-rpath,$(SUPERLU_DIR)/lib64 -L$(SUPERLU_DIR)/lib64\
|
||||
-lsuperlu_dist -lblas
|
||||
endif
|
||||
|
||||
# SCOTCH library configuration (required by STRUMPACK <= v2.1.0, optional in
|
||||
@@ -269,7 +271,7 @@ MPI_FORTRAN_LIB = -lmpifort
|
||||
# MUMPS library configuration
|
||||
MUMPS_DIR = @MFEM_DIR@/../MUMPS_5.2.0
|
||||
MUMPS_OPT = -I$(MUMPS_DIR)/include
|
||||
MUMPS_LIB = -Wl,-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib -ldmumps\
|
||||
MUMPS_LIB = $(XLINKER)-rpath,$(MUMPS_DIR)/lib -L$(MUMPS_DIR)/lib -ldmumps\
|
||||
-lmumps_common -lpord $(SCALAPACK_LIB) $(LAPACK_LIB) $(MPI_FORTRAN_LIB)
|
||||
|
||||
# STRUMPACK library configuration
|
||||
@@ -299,8 +301,8 @@ GNUTLS_LIB = -lgnutls
|
||||
NETCDF_DIR = $(HOME)/local
|
||||
HDF5_DIR = $(HOME)/local
|
||||
NETCDF_OPT = -I$(NETCDF_DIR)/include -I$(HDF5_DIR)/include $(ZLIB_OPT)
|
||||
NETCDF_LIB = -Wl,-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
|
||||
-Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
|
||||
NETCDF_LIB = $(XLINKER)-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
|
||||
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
|
||||
-lnetcdf -lhdf5_hl -lhdf5 $(ZLIB_LIB)
|
||||
|
||||
# PETSc library configuration (version greater or equal to 3.8 or the dev branch)
|
||||
@@ -312,9 +314,10 @@ PETSC_INC_VAR = PETSC_CC_INCLUDES
|
||||
PETSC_LIB_VAR = PETSC_EXTERNAL_LIB_BASIC
|
||||
ifeq ($(PETSC_FOUND),YES)
|
||||
PETSC_OPT := $(shell sed -n "s/$(PETSC_INC_VAR) = *//p" $(PETSC_VARS))
|
||||
PETSC_LIB := $(shell sed -n "s/$(PETSC_LIB_VAR) = *//p" $(PETSC_VARS))
|
||||
PETSC_LIB := -Wl,-rpath,$(abspath $(PETSC_DIR))/lib\
|
||||
-L$(abspath $(PETSC_DIR))/lib -lpetsc $(PETSC_LIB)
|
||||
PETSC_DEP := $(shell sed -n "s/$(PETSC_LIB_VAR) = *//p" $(PETSC_VARS))
|
||||
PETSC_LIB = $(XLINKER)-rpath,$(abspath $(PETSC_DIR))/lib\
|
||||
-L$(abspath $(PETSC_DIR))/lib -lpetsc\
|
||||
$(subst $(CXX_XLINKER),$(XLINKER),$(PETSC_DEP))
|
||||
endif
|
||||
|
||||
SLEPC_DIR := $(MFEM_DIR)/../slepc
|
||||
@@ -326,9 +329,10 @@ ifeq ($(SLEPC_FOUND),YES)
|
||||
SLEPC_OPT := $(shell sed -n "s/$(SLEPC_INC_VAR) *= *//p" $(SLEPC_VARS))
|
||||
# Some additional external libraries might be defined in this file
|
||||
-include ${SLEPC_DIR}/${PETSC_ARCH}/lib/slepc/conf/slepcvariables
|
||||
SLEPC_LIB := $(shell sed -n "s/$(SLEPC_LIB_VAR) *= *//p" $(SLEPC_VARS))
|
||||
SLEPC_LIB := -Wl,-rpath,$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib\
|
||||
-L$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib -lslepc $(SLEPC_LIB)
|
||||
SLEPC_DEP := $(shell sed -n "s/$(SLEPC_LIB_VAR) *= *//p" $(SLEPC_VARS))
|
||||
SLEPC_LIB = $(XLINKER)-rpath,$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib\
|
||||
-L$(abspath $(SLEPC_DIR))/$(PETSC_ARCH)/lib -lslepc\
|
||||
$(subst $(CXX_XLINKER),$(XLINKER),$(SLEPC_DEP))
|
||||
endif
|
||||
|
||||
# MPFR library configuration
|
||||
@@ -339,7 +343,7 @@ MPFR_LIB = -lmpfr
|
||||
CONDUIT_DIR = @MFEM_DIR@/../conduit
|
||||
CONDUIT_OPT = -I$(CONDUIT_DIR)/include/conduit
|
||||
CONDUIT_LIB = \
|
||||
-Wl,-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
|
||||
$(XLINKER)-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
|
||||
-lconduit -lconduit_relay -lconduit_blueprint -ldl
|
||||
|
||||
# Check if Conduit was built with hdf5 support, by looking
|
||||
@@ -347,7 +351,7 @@ CONDUIT_LIB = \
|
||||
CONDUIT_HDF5_HEADER=$(CONDUIT_DIR)/include/conduit/conduit_relay_hdf5.hpp
|
||||
ifneq (,$(wildcard $(CONDUIT_HDF5_HEADER)))
|
||||
CONDUIT_OPT += -I$(HDF5_DIR)/include
|
||||
CONDUIT_LIB += -Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
|
||||
CONDUIT_LIB += $(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
|
||||
-lhdf5 $(ZLIB_LIB)
|
||||
endif
|
||||
|
||||
@@ -357,9 +361,9 @@ SIDRE_DIR = @MFEM_DIR@/../axom
|
||||
SIDRE_OPT = -I$(SIDRE_DIR)/include -I$(CONDUIT_DIR)/include/conduit\
|
||||
-I$(HDF5_DIR)/include
|
||||
SIDRE_LIB = \
|
||||
-Wl,-rpath,$(SIDRE_DIR)/lib -L$(SIDRE_DIR)/lib \
|
||||
-Wl,-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
|
||||
-Wl,-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
|
||||
$(XLINKER)-rpath,$(SIDRE_DIR)/lib -L$(SIDRE_DIR)/lib \
|
||||
$(XLINKER)-rpath,$(CONDUIT_DIR)/lib -L$(CONDUIT_DIR)/lib \
|
||||
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib \
|
||||
-laxom -lconduit -lconduit_relay -lconduit_blueprint -lhdf5 $(ZLIB_LIB) -ldl
|
||||
|
||||
# PUMI
|
||||
@@ -415,9 +419,9 @@ MKL_CPARDISO_DIR ?=
|
||||
MKL_MPI_WRAPPER ?= mkl_blacs_mpich_lp64
|
||||
MKL_LIBRARY_SUBDIR ?= lib
|
||||
MKL_CPARDISO_OPT = -I$(MKL_CPARDISO_DIR)/include
|
||||
MKL_CPARDISO_LIB = -Wl,-rpath,$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR)\
|
||||
-L$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR) -l$(MKL_MPI_WRAPPER)\
|
||||
-lmkl_intel_lp64 -lmkl_sequential -lmkl_core
|
||||
MKL_CPARDISO_LIB = $(XLINKER)-rpath,$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR)\
|
||||
-L$(MKL_CPARDISO_DIR)/$(MKL_LIBRARY_SUBDIR) -l$(MKL_MPI_WRAPPER)\
|
||||
-lmkl_intel_lp64 -lmkl_sequential -lmkl_core
|
||||
|
||||
# If YES, enable some informational messages
|
||||
VERBOSE = NO
|
||||
|
||||
+8
-8
@@ -31,14 +31,14 @@ POINTS 27 double
|
||||
7 0.5 1
|
||||
8 0.5 1
|
||||
CELLS 8 56
|
||||
6 0 9 18 1 10 19
|
||||
6 1 10 19 2 11 20
|
||||
6 2 11 20 3 12 21
|
||||
6 3 12 21 4 13 22
|
||||
6 4 13 22 5 14 23
|
||||
6 5 14 23 6 15 24
|
||||
6 6 15 24 7 16 25
|
||||
6 7 16 25 8 17 26
|
||||
6 0 18 9 1 19 10
|
||||
6 1 19 10 2 20 11
|
||||
6 2 20 11 3 21 12
|
||||
6 3 21 12 4 22 13
|
||||
6 4 22 13 5 23 14
|
||||
6 5 23 14 6 24 15
|
||||
6 6 24 15 7 25 16
|
||||
6 7 25 16 8 26 17
|
||||
CELL_TYPES 8
|
||||
13
|
||||
13
|
||||
|
||||
@@ -205,10 +205,14 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (amgx_solver)
|
||||
{
|
||||
amgx.SetConvergenceCheck(true);
|
||||
amgx.Mult(B,X);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Omit convergence check at the AmgX level when using as a
|
||||
// preconditioner.
|
||||
amgx.SetConvergenceCheck(false);
|
||||
PCG(*A.As<SparseMatrix>(), amgx, B, X, 3, 40, 1e-12, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,6 +264,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
amgx.SetOperator(*A.As<HypreParMatrix>());
|
||||
amgx.SetConvergenceCheck(true);
|
||||
amgx.Mult(B, X);
|
||||
|
||||
// Release MPI communicators and resources created by AmgX
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
// Device sample runs:
|
||||
// ex1 -pa -d cuda
|
||||
// ex1 -pa -d raja-cuda
|
||||
// * ex1 -pa -d raja-hip
|
||||
// ex1 -pa -d occa-cuda
|
||||
// ex1 -pa -d raja-omp
|
||||
// ex1 -pa -d occa-omp
|
||||
|
||||
@@ -178,6 +178,7 @@ int main(int argc, char *argv[])
|
||||
double visc = 1e-2;
|
||||
double mu = 0.25;
|
||||
double K = 5.0;
|
||||
bool adaptive_lin_rtol = true;
|
||||
bool visualization = true;
|
||||
int vis_steps = 1;
|
||||
|
||||
@@ -206,6 +207,9 @@ int main(int argc, char *argv[])
|
||||
"Shear modulus in the Neo-Hookean hyperelastic model.");
|
||||
args.AddOption(&K, "-K", "--bulk-modulus",
|
||||
"Bulk modulus in the Neo-Hookean hyperelastic model.");
|
||||
args.AddOption(&adaptive_lin_rtol, "-alrtol", "--adaptive-lin-rtol",
|
||||
"-no-alrtol", "--no-adaptive-lin-rtol",
|
||||
"Enable or disable adaptive linear solver rtol.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -574,6 +578,7 @@ HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
|
||||
newton_solver.SetPrintLevel(1); // print Newton iterations
|
||||
newton_solver.SetRelTol(rel_tol);
|
||||
newton_solver.SetAbsTol(0.0);
|
||||
newton_solver.SetAdaptiveLinRtol(2, 0.5, 0.9);
|
||||
newton_solver.SetMaxIter(10);
|
||||
}
|
||||
|
||||
|
||||
+10
-2
@@ -5,6 +5,7 @@
|
||||
// Sample runs: ex14 -m ../data/inline-quad.mesh -o 0
|
||||
// ex14 -m ../data/star.mesh -r 4 -o 2
|
||||
// ex14 -m ../data/star-mixed.mesh -r 4 -o 2
|
||||
// ex14 -m ../data/star-mixed.mesh -r 2 -o 2 -k 0 -e 1
|
||||
// ex14 -m ../data/escher.mesh -s 1
|
||||
// ex14 -m ../data/fichera.mesh -s 1 -k 1
|
||||
// ex14 -m ../data/fichera-mixed.mesh -s 1 -k 1
|
||||
@@ -44,6 +45,7 @@ int main(int argc, char *argv[])
|
||||
int order = 1;
|
||||
double sigma = -1.0;
|
||||
double kappa = -1.0;
|
||||
double eta = 0.0;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -54,11 +56,12 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) >= 0.");
|
||||
args.AddOption(&sigma, "-s", "--sigma",
|
||||
"One of the two DG penalty parameters, typically +1/-1."
|
||||
"One of the three DG penalty parameters, typically +1/-1."
|
||||
" See the documentation of class DGDiffusionIntegrator.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"One of the two DG penalty parameters, should be positive."
|
||||
"One of the three DG penalty parameters, should be positive."
|
||||
" Negative values are replaced with (order+1)^2.");
|
||||
args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -130,6 +133,11 @@ int main(int argc, char *argv[])
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
if (eta > 0)
|
||||
{
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
}
|
||||
a->Assemble();
|
||||
a->Finalize();
|
||||
const SparseMatrix &A = a->SpMat();
|
||||
|
||||
+10
-2
@@ -5,6 +5,7 @@
|
||||
// Sample runs: mpirun -np 4 ex14p -m ../data/inline-quad.mesh -o 0
|
||||
// mpirun -np 4 ex14p -m ../data/star.mesh -o 2
|
||||
// mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2
|
||||
// mpirun -np 4 ex14p -m ../data/star-mixed.mesh -o 2 -k 0 -e 1
|
||||
// mpirun -np 4 ex14p -m ../data/escher.mesh -s 1
|
||||
// mpirun -np 4 ex14p -m ../data/fichera.mesh -s 1 -k 1
|
||||
// mpirun -np 4 ex14p -m ../data/fichera-mixed.mesh -s 1 -k 1
|
||||
@@ -82,6 +83,7 @@ int main(int argc, char *argv[])
|
||||
int order = 1;
|
||||
double sigma = -1.0;
|
||||
double kappa = -1.0;
|
||||
double eta = 0.0;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
@@ -95,11 +97,12 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) >= 0.");
|
||||
args.AddOption(&sigma, "-s", "--sigma",
|
||||
"One of the two DG penalty parameters, typically +1/-1."
|
||||
"One of the three DG penalty parameters, typically +1/-1."
|
||||
" See the documentation of class DGDiffusionIntegrator.");
|
||||
args.AddOption(&kappa, "-k", "--kappa",
|
||||
"One of the two DG penalty parameters, should be positive."
|
||||
"One of the three DG penalty parameters, should be positive."
|
||||
" Negative values are replaced with (order+1)^2.");
|
||||
args.AddOption(&eta, "-e", "--eta", "BR2 penalty parameter.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -194,6 +197,11 @@ int main(int argc, char *argv[])
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(one, sigma, kappa));
|
||||
if (eta > 0)
|
||||
{
|
||||
a->AddInteriorFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
a->AddBdrFaceIntegrator(new DGDiffusionBR2Integrator(fespace, eta));
|
||||
}
|
||||
a->Assemble();
|
||||
a->Finalize();
|
||||
|
||||
|
||||
+50
-8
@@ -19,6 +19,13 @@
|
||||
// mpirun -np 4 ex15p -m ../data/square-disc.mesh
|
||||
// mpirun -np 4 ex15p -m ../data/escher.mesh -r 2 -tf 0.3
|
||||
//
|
||||
// Different estimators:
|
||||
//
|
||||
// mpirun -np 4 ex15p -est 0 -e 1e-4
|
||||
// mpirun -np 4 ex15p -est 1 -e 1e-6
|
||||
// mpirun -np 4 ex15p -est 1 -o 3 -tf 0.3
|
||||
// mpirun -np 4 ex15p -est 2 -o 2
|
||||
//
|
||||
// Description: Building on Example 6, this example demonstrates dynamic AMR.
|
||||
// The mesh is adapted to a time-dependent solution by refinement
|
||||
// as well as by derefinement. For simplicity, the solution is
|
||||
@@ -28,8 +35,11 @@
|
||||
// At each outer iteration the right hand side function is changed
|
||||
// to mimic a time dependent problem. Within each inner iteration
|
||||
// the problem is solved on a sequence of meshes which are locally
|
||||
// refined according to a simple ZZ error estimator. At the end
|
||||
// of the inner iteration the error estimates are also used to
|
||||
// refined according to a chosen error estimator. Currently there
|
||||
// are three error estimators supported: A L2 formulation of the
|
||||
// Zienkiewicz-Zhu error estimator (0), a Kelly error indicator (1)
|
||||
// and a traditional Zienkiewicz-Zhu error estimator (2). At the
|
||||
// end of the inner iteration the error estimates are also used to
|
||||
// identify any elements which may be over-refined and a single
|
||||
// derefinement step is performed. After each refinement or
|
||||
// derefinement step a rebalance operation is performed to keep
|
||||
@@ -87,6 +97,7 @@ int main(int argc, char *argv[])
|
||||
int nc_limit = 3; // maximum level of hanging nodes
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int which_estimator = 0;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -107,6 +118,9 @@ int main(int argc, char *argv[])
|
||||
"Maximum level of hanging nodes.");
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&which_estimator, "-est", "--estimator",
|
||||
"Which estimator to use: "
|
||||
"0 = L2ZZ, 1 = Kelly, 2 = ZZ. Defaults to L2ZZ.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -214,17 +228,43 @@ int main(int argc, char *argv[])
|
||||
// provide the method ComputeElementFlux. We supply an L2 space for the
|
||||
// discontinuous flux and an H(div) space for the smoothed flux.
|
||||
L2_FECollection flux_fec(order, dim);
|
||||
ParFiniteElementSpace flux_fes(&pmesh, &flux_fec, sdim);
|
||||
RT_FECollection smooth_flux_fec(order-1, dim);
|
||||
ParFiniteElementSpace smooth_flux_fes(&pmesh, &smooth_flux_fec);
|
||||
L2ZienkiewiczZhuEstimator estimator(*integ, x, flux_fes, smooth_flux_fes);
|
||||
ErrorEstimator* estimator;
|
||||
switch (which_estimator)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
auto flux_fes = new ParFiniteElementSpace(&pmesh, &flux_fec, sdim);
|
||||
estimator = new KellyErrorEstimator(*integ, x, flux_fes);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
auto flux_fes = new ParFiniteElementSpace(&pmesh, &fec, sdim);
|
||||
estimator = new ZienkiewiczZhuEstimator(*integ, x, flux_fes);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if (myid == 0)
|
||||
{
|
||||
std::cout << "Unkown estimator. Falling back to L2ZZ." << std::endl;
|
||||
}
|
||||
case 0:
|
||||
{
|
||||
auto flux_fes = new ParFiniteElementSpace(&pmesh, &flux_fec, sdim);
|
||||
auto smooth_flux_fes = new ParFiniteElementSpace(&pmesh, &smooth_flux_fec);
|
||||
estimator = new L2ZienkiewiczZhuEstimator(*integ, x, flux_fes, smooth_flux_fes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 11. As in Example 6p, we also need a refiner. This time the refinement
|
||||
// strategy is based on a fixed threshold that is applied locally to each
|
||||
// element. The global threshold is turned off by setting the total error
|
||||
// fraction to zero. We also enforce a maximum refinement ratio between
|
||||
// adjacent elements.
|
||||
ThresholdRefiner refiner(estimator);
|
||||
ThresholdRefiner refiner(*estimator);
|
||||
refiner.SetTotalErrorFraction(0.0); // use purely local threshold
|
||||
refiner.SetLocalErrorGoal(max_elem_error);
|
||||
refiner.PreferConformingRefinement();
|
||||
@@ -233,7 +273,7 @@ int main(int argc, char *argv[])
|
||||
// 12. A derefiner selects groups of elements that can be coarsened to form
|
||||
// a larger element. A conservative enough threshold needs to be set to
|
||||
// prevent derefining elements that would immediately be refined again.
|
||||
ThresholdDerefiner derefiner(estimator);
|
||||
ThresholdDerefiner derefiner(*estimator);
|
||||
derefiner.SetThreshold(hysteresis * max_elem_error);
|
||||
derefiner.SetNCLimit(nc_limit);
|
||||
|
||||
@@ -316,7 +356,7 @@ int main(int argc, char *argv[])
|
||||
refiner.Apply(pmesh);
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << ", total error: " << estimator.GetTotalError() << endl;
|
||||
cout << ", total error: " << estimator->GetTotalError() << endl;
|
||||
}
|
||||
|
||||
// 21. Quit the AMR loop if the termination criterion has been met
|
||||
@@ -346,6 +386,8 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
delete estimator;
|
||||
|
||||
// 25. Exit
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
// MFEM Example 3 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex3p_complex
|
||||
//
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Exact solution, E, and r.h.s., f. See below for implementation.
|
||||
double E_exact(const Vector &);
|
||||
void gradE_exact(const Vector &, Vector &);
|
||||
double f_exact(const Vector &);
|
||||
double freq = 1.0, kappa;
|
||||
int dim;
|
||||
|
||||
|
||||
#define COMPLEX_VERSION
|
||||
#define NEUMANN
|
||||
|
||||
const double omega = 1.4;
|
||||
const double eps = 1.0e-8;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/beam-tet.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 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).");
|
||||
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
|
||||
" solution.");
|
||||
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(&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.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 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);
|
||||
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 = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 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 1,000 elements.
|
||||
{
|
||||
int ref_levels = (int)floor(log(1000./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. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
int par_ref_levels = 0;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new H1_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int 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;
|
||||
|
||||
#ifndef NEUMANN
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
#endif
|
||||
|
||||
//const double imscale = 0.0;
|
||||
const double imscale = -omega;
|
||||
|
||||
Coefficient *im = new ConstantCoefficient(imscale); // im part
|
||||
//Coefficient *im = new ConstantCoefficient(0.0); // im part
|
||||
|
||||
FunctionCoefficient E_coef(E_exact);
|
||||
VectorFunctionCoefficient grad_E(sdim, gradE_exact);
|
||||
ProductCoefficient omegaE(imscale, E_coef);
|
||||
|
||||
// 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
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
FunctionCoefficient f(f_exact);
|
||||
#ifdef COMPLEX_VERSION
|
||||
ParComplexLinearForm *b = new ParComplexLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new DomainLFIntegrator(f), NULL);
|
||||
#ifdef NEUMANN
|
||||
b->AddBoundaryIntegrator(NULL, new BoundaryNormalLFIntegrator(grad_E));
|
||||
#endif
|
||||
b->AddBoundaryIntegrator(NULL, new BoundaryLFIntegrator(omegaE)); // im part
|
||||
#endif
|
||||
|
||||
b->Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary edges will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
/*
|
||||
ParGridFunction x(fespace);
|
||||
VectorFunctionCoefficient E(sdim, E_exact);
|
||||
x.ProjectCoefficient(E);
|
||||
*/
|
||||
|
||||
#ifdef COMPLEX_VERSION
|
||||
// Complex version
|
||||
ParComplexGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
ConstantCoefficient E_im(0.0);
|
||||
//x.ProjectBdrCoefficientTangent(E_Re, E_Im, ess_bdr);
|
||||
x.ProjectCoefficient(E_coef, E_im);
|
||||
#endif
|
||||
|
||||
// 11. Set up the parallel bilinear form corresponding to the EM diffusion
|
||||
// operator curl muinv curl + sigma I, by adding the curl-curl and the
|
||||
// mass domain integrators.
|
||||
Coefficient *muinv = new ConstantCoefficient(1.0);
|
||||
Coefficient *epscoef = new ConstantCoefficient(eps);
|
||||
Coefficient *imabs = new ConstantCoefficient(fabs(imscale)); // im part
|
||||
|
||||
#ifdef COMPLEX_VERSION
|
||||
// Complex version
|
||||
ParSesquilinearForm *a = new ParSesquilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(*muinv), NULL);
|
||||
a->AddDomainIntegrator(new MassIntegrator(*epscoef), NULL);
|
||||
a->AddBoundaryIntegrator(NULL, new MassIntegrator(*im)); // im part
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
|
||||
ParBilinearForm a_Re(fespace);
|
||||
a_Re.AddDomainIntegrator(new DiffusionIntegrator(*muinv));
|
||||
a_Re.AddDomainIntegrator(new MassIntegrator(*epscoef));
|
||||
|
||||
if (pa) { a_Re.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a_Re.Assemble();
|
||||
|
||||
OperatorPtr A_Re;
|
||||
a_Re.FormSystemMatrix(ess_tdof_list, A_Re);
|
||||
|
||||
ParBilinearForm a_Im(fespace);
|
||||
a_Im.AddBoundaryIntegrator(new MassIntegrator(*imabs));
|
||||
a_Im.Assemble();
|
||||
|
||||
OperatorPtr A_Im;
|
||||
a_Im.FormSystemMatrix(ess_tdof_list, A_Im);
|
||||
|
||||
// 13. Solve the system AX=B using PCG with the AMS preconditioner from hypre
|
||||
// (in the full assembly case) or CG with Jacobi preconditioner (in the
|
||||
// partial assembly case).
|
||||
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = fespace->GetTrueVSize();
|
||||
offsets[2] = fespace->GetTrueVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
//OperatorJacobiSmoother massJacobi(a_Im, ess_tdof_list);
|
||||
|
||||
StopWatch sw;
|
||||
sw.Clear();
|
||||
sw.Start();
|
||||
|
||||
if (pa) // Jacobi preconditioning in partial assembly mode
|
||||
{
|
||||
MFEM_VERIFY(false, "TODO");
|
||||
//OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(1000);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetOperator(*A);
|
||||
//cg.SetPreconditioner(Jacobi);
|
||||
cg.Mult(B, X);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: "
|
||||
<< A.As<HypreParMatrix>()->GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
HypreBoomerAMG amg(*A_Re.As<HypreParMatrix>());
|
||||
|
||||
#ifdef COMPLEX_VERSION
|
||||
BlockDiagonalPreconditioner BlockDP(offsets);
|
||||
BlockDP.SetDiagonalBlock(0, &amg);
|
||||
BlockDP.SetDiagonalBlock(1, &amg);
|
||||
|
||||
Complex_PMHSS PMHSS(A_Re.Ptr(), A_Im.Ptr(), &BlockDP, NULL, 1.0);
|
||||
|
||||
ComplexOperator AspdComplex(A_Re.Ptr(), A_Im.Ptr(), false, false);
|
||||
|
||||
GMRESSolver PMHSSgmres(MPI_COMM_WORLD);
|
||||
PMHSSgmres.SetPrintLevel(1);
|
||||
PMHSSgmres.SetKDim(100);
|
||||
PMHSSgmres.SetMaxIter(100);
|
||||
PMHSSgmres.SetRelTol(1e-6);
|
||||
PMHSSgmres.SetAbsTol(0.0);
|
||||
PMHSSgmres.SetOperator(AspdComplex);
|
||||
PMHSSgmres.SetPreconditioner(PMHSS);
|
||||
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetKDim(1000);
|
||||
gmres.SetMaxIter(100);
|
||||
gmres.SetRelTol(1e-8);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetOperator(*A);
|
||||
//gmres.SetPreconditioner(BlockDP);
|
||||
gmres.SetPreconditioner(PMHSS);
|
||||
//gmres.SetPreconditioner(PMHSSgmres);
|
||||
#else
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetKDim(1000);
|
||||
gmres.SetMaxIter(100);
|
||||
gmres.SetRelTol(1e-8);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetOperator(*A);
|
||||
gmres.SetPreconditioner(ams);
|
||||
#endif
|
||||
|
||||
gmres.Mult(B, X);
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
mfem::out << "Total solve time " <<sw.RealTime() << endl;
|
||||
|
||||
// 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. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
#ifdef COMPLEX_VERSION
|
||||
double err = x.real().ComputeL2Error(E_coef);
|
||||
#else
|
||||
double err = x.ComputeL2Error(E_coef);
|
||||
#endif
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n|| E_h - E ||_{L^2} = " << err << '\n' << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 16. 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);
|
||||
#ifdef COMPLEX_VERSION
|
||||
x.real().Save(sol_ofs);
|
||||
#else
|
||||
x.Save(sol_ofs);
|
||||
#endif
|
||||
}
|
||||
|
||||
// 17. 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);
|
||||
#ifdef COMPLEX_VERSION
|
||||
sol_sock << "solution\n" << *pmesh << x.real() << flush;
|
||||
#else
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
#endif
|
||||
}
|
||||
|
||||
// 18. Free the used memory.
|
||||
delete a;
|
||||
delete muinv;
|
||||
delete b;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define VERSION_COS
|
||||
|
||||
double E_exact(const Vector &x)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
#ifdef VERSION_COS
|
||||
return cos(kappa * x(0)) * cos(kappa * x(1)) * cos(kappa * x(2));
|
||||
#else
|
||||
return sin(kappa * x(0)) * sin(kappa * x(1)) * sin(kappa * x(2));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
void gradE_exact(const Vector &x, Vector &grad)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
#ifdef VERSION_COS
|
||||
grad(0) = -kappa * sin(kappa * x(0)) * cos(kappa * x(1)) * cos(kappa * x(2));
|
||||
grad(1) = -kappa * sin(kappa * x(1)) * cos(kappa * x(0)) * cos(kappa * x(2));
|
||||
grad(2) = -kappa * sin(kappa * x(2)) * cos(kappa * x(0)) * cos(kappa * x(1));
|
||||
#else
|
||||
grad(0) = kappa * cos(kappa * x(0)) * sin(kappa * x(1)) * sin(kappa * x(2));
|
||||
grad(1) = kappa * cos(kappa * x(1)) * sin(kappa * x(0)) * sin(kappa * x(2));
|
||||
grad(2) = kappa * cos(kappa * x(2)) * sin(kappa * x(0)) * sin(kappa * x(1));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_VERIFY(false, "");
|
||||
}
|
||||
}
|
||||
|
||||
// (grad u, grad v) + eps (u, v) = <grad u . n, v> - (div grad u, v) + eps (u, v)
|
||||
double f_exact(const Vector &x)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
const double c = 3.0 * kappa * kappa;
|
||||
#ifdef VERSION_COS
|
||||
return (eps + c) * cos(kappa * x(0)) * cos(kappa * x(1)) * cos(kappa * x(2));
|
||||
#else
|
||||
return (eps + c) * sin(kappa * x(0)) * sin(kappa * x(1)) * sin(kappa * x(2));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
+81
-33
@@ -10,6 +10,10 @@
|
||||
// ex25 -o 2 -f 8.0 -ref 3 -prob 4 -m ../data/inline-quad.mesh
|
||||
// ex25 -o 2 -f 2.0 -ref 1 -prob 4 -m ../data/inline-hex.mesh
|
||||
//
|
||||
// Device sample runs:
|
||||
// ex25 -o 2 -f 8.0 -ref 3 -prob 4 -m ../data/inline-quad.mesh -pa -d cuda
|
||||
// ex25 -o 2 -f 2.0 -ref 1 -prob 4 -m ../data/inline-hex.mesh -pa -d cuda
|
||||
//
|
||||
// Description: This example code solves a simple electromagnetic wave
|
||||
// propagation problem corresponding to the second order
|
||||
// indefinite Maxwell equation
|
||||
@@ -157,7 +161,10 @@ int main(int argc, char *argv[])
|
||||
int iprob = 4;
|
||||
double freq = 5.0;
|
||||
bool herm_conv = true;
|
||||
bool umf_solver = false;
|
||||
bool visualization = 1;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -176,15 +183,28 @@ int main(int argc, char *argv[])
|
||||
"Frequency (in Hz).");
|
||||
args.AddOption(&herm_conv, "-herm", "--hermitian", "-no-herm",
|
||||
"--no-hermitian", "Use convention for Hermitian operators.");
|
||||
#ifdef MFEM_USE_SUITESPARSE
|
||||
args.AddOption(&umf_solver, "-umf", "--umfpack", "-no-umf",
|
||||
"--no-umfpack", "Use the UMFPack Solver.");
|
||||
#endif
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.Parse();
|
||||
|
||||
if (iprob > 4) { iprob = 4; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
// 2. Setup the mesh
|
||||
// 2. 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.Print();
|
||||
|
||||
// 3. Setup the mesh
|
||||
if (!mesh_file)
|
||||
{
|
||||
exact_known = true;
|
||||
@@ -225,7 +245,7 @@ int main(int argc, char *argv[])
|
||||
// Setup PML length
|
||||
Array2D<double> length(dim, 2); length = 0.0;
|
||||
|
||||
// 3. Setup the Cartesian PML region.
|
||||
// 4. Setup the Cartesian PML region.
|
||||
switch (prob)
|
||||
{
|
||||
case disc:
|
||||
@@ -251,19 +271,19 @@ int main(int argc, char *argv[])
|
||||
comp_domain_bdr = pml->GetCompDomainBdr();
|
||||
domain_bdr = pml->GetDomainBdr();
|
||||
|
||||
// 4. Refine the mesh to increase the resolution.
|
||||
// 5. Refine the mesh to increase the resolution.
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 5. Reorient mesh in case of a tet mesh
|
||||
// 6. Reorient mesh in case of a tet mesh
|
||||
mesh->ReorientTetMesh();
|
||||
|
||||
// Set element attributes in order to distinguish elements in the PML region
|
||||
pml->SetAttributes(mesh);
|
||||
|
||||
// 6. Define a finite element space on the mesh. Here we use the Nedelec
|
||||
// 7. Define a finite element space on the mesh. Here we use the Nedelec
|
||||
// finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
||||
@@ -271,7 +291,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
|
||||
// 7. Determine the list of true essential boundary dofs. In this example,
|
||||
// 8. Determine the list of true essential boundary dofs. In this example,
|
||||
// the boundary conditions are defined based on the specific mesh and the
|
||||
// problem type.
|
||||
Array<int> ess_tdof_list;
|
||||
@@ -313,12 +333,12 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
// 8. Setup Complex Operator convention
|
||||
// 9. Setup Complex Operator convention
|
||||
ComplexOperator::Convention conv =
|
||||
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
|
||||
|
||||
// 9. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// the FEM linear system.
|
||||
// 10. Set up the linear form b(.) which corresponds to the right-hand side of
|
||||
// the FEM linear system.
|
||||
VectorFunctionCoefficient f(dim, source);
|
||||
ComplexLinearForm b(fespace, conv);
|
||||
if (prob == load_src)
|
||||
@@ -328,7 +348,7 @@ int main(int argc, char *argv[])
|
||||
b.Vector::operator=(0.0);
|
||||
b.Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a complex finite element grid function
|
||||
// 11. Define the solution vector x as a complex finite element grid function
|
||||
// corresponding to fespace.
|
||||
ComplexGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
@@ -336,7 +356,7 @@ int main(int argc, char *argv[])
|
||||
VectorFunctionCoefficient E_Im(dim, E_bdr_data_Im);
|
||||
x.ProjectBdrCoefficientTangent(E_Re, E_Im, ess_bdr);
|
||||
|
||||
// 11. Set up the sesquilinear form a(.,.)
|
||||
// 12. Set up the sesquilinear form a(.,.)
|
||||
//
|
||||
// In Comp
|
||||
// Domain: 1/mu (Curl E, Curl F) - omega^2 * epsilon (E,F)
|
||||
@@ -390,32 +410,35 @@ int main(int argc, char *argv[])
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_Re),
|
||||
new VectorFEMassIntegrator(restr_c2_Im));
|
||||
|
||||
// 12. Assemble the bilinear form and the corresponding linear system,
|
||||
// 13. Assemble the bilinear form and the corresponding linear system,
|
||||
// applying any necessary transformations such as: assembly, eliminating
|
||||
// boundary conditions, applying conforming constraints for
|
||||
// non-conforming AMR, etc.
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a.Assemble(0);
|
||||
|
||||
OperatorPtr A;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
|
||||
|
||||
// 13. Solve using a direct or an iterative solver
|
||||
// 14. Solve using a direct or an iterative solver
|
||||
#ifdef MFEM_USE_SUITESPARSE
|
||||
if (!pa && umf_solver)
|
||||
{
|
||||
ComplexUMFPackSolver csolver(*A.As<ComplexSparseMatrix>());
|
||||
csolver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
csolver.SetPrintLevel(1);
|
||||
csolver.Mult(B, X);
|
||||
}
|
||||
#else
|
||||
// 13a. Set up the Bilinear form a(.,.) for the preconditioner
|
||||
#endif
|
||||
// 14a. Set up the Bilinear form a(.,.) for the preconditioner
|
||||
//
|
||||
// In Comp
|
||||
// Domain: 1/mu (Curl E, Curl F) + omega^2 * epsilon (E,F)
|
||||
//
|
||||
// In PML: 1/mu (abs(1/det(J) J^T J) Curl E, Curl F)
|
||||
// + omega^2 * epsilon (abs(det(J) * (J^T J)^-1) * E, F)
|
||||
if (pa || !umf_solver)
|
||||
{
|
||||
ConstantCoefficient absomeg(pow(omega, 2) * epsilon);
|
||||
RestrictedCoefficient restr_absomeg(absomeg,attr);
|
||||
@@ -435,39 +458,57 @@ int main(int argc, char *argv[])
|
||||
prec.AddDomainIntegrator(new CurlCurlIntegrator(restr_c1_abs));
|
||||
prec.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_abs));
|
||||
|
||||
if (pa) { prec.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
prec.Assemble();
|
||||
|
||||
OperatorPtr PCOpAh;
|
||||
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
|
||||
|
||||
// 13b. Define and apply a GMRES solver for AU=B with a block diagonal
|
||||
// preconditioner based on the Gauss-Seidel sparse smoother.
|
||||
// 14b. Define and apply a GMRES solver for AU=B with a block diagonal
|
||||
// preconditioner based on the Gauss-Seidel or Jacobi sparse smoother.
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = fespace->GetTrueVSize();
|
||||
offsets[2] = fespace->GetTrueVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
GSSmoother gs00(*PCOpAh.As<SparseMatrix>());
|
||||
BlockDiagonalPreconditioner BlockGS(offsets);
|
||||
ScaledOperator gs11(&gs00,
|
||||
(conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0);
|
||||
BlockGS.SetDiagonalBlock(0,&gs00);
|
||||
BlockGS.SetDiagonalBlock(1,&gs11);
|
||||
Operator *pc_r = nullptr;
|
||||
Operator *pc_i = nullptr;
|
||||
int s = (conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0;
|
||||
if (pa)
|
||||
{
|
||||
// Jacobi Smoother
|
||||
OperatorJacobiSmoother *d00 = new OperatorJacobiSmoother(prec, ess_tdof_list);
|
||||
ScaledOperator *d11 = new ScaledOperator(d00, s);
|
||||
pc_r = d00;
|
||||
pc_i = d11;
|
||||
}
|
||||
else
|
||||
{
|
||||
OperatorPtr PCOpAh;
|
||||
prec.SetDiagonalPolicy(mfem::Operator::DIAG_ONE);
|
||||
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
|
||||
|
||||
// Gauss-Seidel Smoother
|
||||
GSSmoother *gs00 = new GSSmoother(*PCOpAh.As<SparseMatrix>());
|
||||
ScaledOperator *gs11 = new ScaledOperator(gs00, s);
|
||||
pc_r = gs00;
|
||||
pc_i = gs11;
|
||||
}
|
||||
|
||||
BlockDiagonalPreconditioner BlockDP(offsets);
|
||||
BlockDP.SetDiagonalBlock(0, pc_r);
|
||||
BlockDP.SetDiagonalBlock(1, pc_i);
|
||||
|
||||
GMRESSolver gmres;
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetKDim(200);
|
||||
gmres.SetMaxIter(2000);
|
||||
gmres.SetMaxIter(pa ? 5000 : 2000);
|
||||
gmres.SetRelTol(1e-5);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetOperator(*A);
|
||||
gmres.SetPreconditioner(BlockGS);
|
||||
gmres.SetPreconditioner(BlockDP);
|
||||
gmres.Mult(B, X);
|
||||
}
|
||||
#endif
|
||||
|
||||
// 14. Recover the solution as a finite element grid function and compute the
|
||||
// 15. Recover the solution as a finite element grid function and compute the
|
||||
// errors if the exact solution is known.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
@@ -504,7 +545,7 @@ int main(int argc, char *argv[])
|
||||
<< sqrt(L2Error_Re*L2Error_Re + L2Error_Im*L2Error_Im) << "\n\n";
|
||||
}
|
||||
|
||||
// 15. Save the refined mesh and the solution. This output can be viewed
|
||||
// 16. Save the refined mesh and the solution. This output can be viewed
|
||||
// later using GLVis: "glvis -m mesh -g sol".
|
||||
{
|
||||
ofstream mesh_ofs("ex25.mesh");
|
||||
@@ -519,7 +560,7 @@ int main(int argc, char *argv[])
|
||||
x.imag().Save(sol_i_ofs);
|
||||
}
|
||||
|
||||
// 16. Send the solution by socket to a GLVis server.
|
||||
// 17. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
// Define visualization keys for GLVis (see GLVis documentation)
|
||||
@@ -570,7 +611,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 17. Free the used memory.
|
||||
// 18. Free the used memory.
|
||||
delete pml;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
@@ -916,7 +957,14 @@ void CartesianPML::SetBoundaries()
|
||||
|
||||
void CartesianPML::SetAttributes(Mesh *mesh_)
|
||||
{
|
||||
// Initialize bdr attributes
|
||||
for (int i = 0; i < mesh_->GetNBE(); ++i)
|
||||
{
|
||||
mesh_->GetBdrElement(i)->SetAttribute(i+1);
|
||||
}
|
||||
|
||||
int nrelem = mesh_->GetNE();
|
||||
|
||||
elems.SetSize(nrelem);
|
||||
|
||||
// Loop through the elements and identify which of them are in the PML
|
||||
|
||||
+70
-32
@@ -10,6 +10,10 @@
|
||||
// mpirun -np 4 ex25p -o 2 -f 8.0 -rs 2 -rp 2 -prob 4 -m ../data/inline-quad.mesh
|
||||
// mpirun -np 4 ex25p -o 2 -f 2.0 -rs 1 -rp 1 -prob 4 -m ../data/inline-hex.mesh
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex25p -o 1 -f 3.0 -rs 3 -rp 1 -prob 2 -pa -d cuda
|
||||
// mpirun -np 4 ex25p -o 2 -f 1.0 -rs 1 -rp 1 -prob 3 -pa -d cuda
|
||||
//
|
||||
// Description: This example code solves a simple electromagnetic wave
|
||||
// propagation problem corresponding to the second order
|
||||
// indefinite Maxwell equation
|
||||
@@ -167,6 +171,8 @@ int main(int argc, char *argv[])
|
||||
bool slu_solver = false;
|
||||
bool mumps_solver = false;
|
||||
bool visualization = 1;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -198,6 +204,10 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
|
||||
"--no-partial-assembly", "Enable Partial Assembly.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.Parse();
|
||||
if (slu_solver && mumps_solver)
|
||||
{
|
||||
@@ -211,7 +221,12 @@ int main(int argc, char *argv[])
|
||||
if (iprob > 4) { iprob = 4; }
|
||||
prob = (prob_type)iprob;
|
||||
|
||||
// 3. Setup the (serial) mesh on all processors.
|
||||
// 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);
|
||||
if (myid == 0) { device.Print(); }
|
||||
|
||||
// 4. Setup the (serial) mesh on all processors.
|
||||
if (!mesh_file)
|
||||
{
|
||||
exact_known = true;
|
||||
@@ -259,7 +274,7 @@ int main(int argc, char *argv[])
|
||||
// Setup PML length
|
||||
Array2D<double> length(dim, 2); length = 0.0;
|
||||
|
||||
// 4. Setup the Cartesian PML region.
|
||||
// 5. Setup the Cartesian PML region.
|
||||
switch (prob)
|
||||
{
|
||||
case disc:
|
||||
@@ -285,13 +300,13 @@ int main(int argc, char *argv[])
|
||||
comp_domain_bdr = pml->GetCompDomainBdr();
|
||||
domain_bdr = pml->GetDomainBdr();
|
||||
|
||||
// 5. Refine the serial mesh on all processors to increase the resolution.
|
||||
// 6. Refine the serial mesh on all processors to increase the resolution.
|
||||
for (int l = 0; l < ref_levels; l++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh.
|
||||
// 7. Define a parallel mesh by a partitioning of the serial mesh.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
@@ -301,13 +316,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 6a. Reorient mesh in case of a tet mesh
|
||||
// 7a. Reorient mesh in case of a tet mesh
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Set element attributes in order to distinguish elements in the PML
|
||||
// 8. Set element attributes in order to distinguish elements in the PML
|
||||
pml->SetAttributes(pmesh);
|
||||
|
||||
// 8. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// 9. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
@@ -317,9 +332,9 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of finite element unknowns: " << size << endl;
|
||||
}
|
||||
|
||||
// 9. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// based on the specific mesh and the problem type.
|
||||
// 10. Determine the list of true (i.e. parallel conforming) essential
|
||||
// boundary dofs. In this example, the boundary conditions are defined
|
||||
// based on the specific mesh and the problem type.
|
||||
Array<int> ess_tdof_list;
|
||||
Array<int> ess_bdr;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
@@ -359,11 +374,11 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
|
||||
// 10. Setup Complex Operator convention
|
||||
// 11. Setup Complex Operator convention
|
||||
ComplexOperator::Convention conv =
|
||||
herm_conv ? ComplexOperator::HERMITIAN : ComplexOperator::BLOCK_SYMMETRIC;
|
||||
|
||||
// 11. Set up the parallel linear form b(.) which corresponds to the
|
||||
// 12. Set up the parallel linear form b(.) which corresponds to the
|
||||
// right-hand side of the FEM linear system.
|
||||
VectorFunctionCoefficient f(dim, source);
|
||||
ParComplexLinearForm b(fespace, conv);
|
||||
@@ -374,7 +389,7 @@ int main(int argc, char *argv[])
|
||||
b.Vector::operator=(0.0);
|
||||
b.Assemble();
|
||||
|
||||
// 12. Define the solution vector x as a parallel complex finite element grid
|
||||
// 13. Define the solution vector x as a parallel complex finite element grid
|
||||
// function corresponding to fespace.
|
||||
ParComplexGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
@@ -382,7 +397,7 @@ int main(int argc, char *argv[])
|
||||
VectorFunctionCoefficient E_Im(dim, E_bdr_data_Im);
|
||||
x.ProjectBdrCoefficientTangent(E_Re, E_Im, ess_bdr);
|
||||
|
||||
// 13. Set up the parallel sesquilinear form a(.,.)
|
||||
// 14. Set up the parallel sesquilinear form a(.,.)
|
||||
//
|
||||
// In Comp
|
||||
// Domain: 1/mu (Curl E, Curl F) - omega^2 * epsilon (E,F)
|
||||
@@ -436,19 +451,20 @@ int main(int argc, char *argv[])
|
||||
a.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_Re),
|
||||
new VectorFEMassIntegrator(restr_c2_Im));
|
||||
|
||||
// 14. Assemble the parallel bilinear form and the corresponding linear
|
||||
// 15. 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, etc.
|
||||
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a.Assemble();
|
||||
|
||||
OperatorPtr Ah;
|
||||
Vector B, X;
|
||||
a.FormLinearSystem(ess_tdof_list, x, b, Ah, X, B);
|
||||
|
||||
// 15. Solve using a direct or an iterative solver
|
||||
// 16. Solve using a direct or an iterative solver
|
||||
#ifdef MFEM_USE_SUPERLU
|
||||
if (slu_solver)
|
||||
if (!pa && slu_solver)
|
||||
{
|
||||
// Transform to monolithic HypreParMatrix
|
||||
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
|
||||
@@ -463,7 +479,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
#endif
|
||||
#ifdef MFEM_USE_MUMPS
|
||||
if (mumps_solver)
|
||||
if (!pa && mumps_solver)
|
||||
{
|
||||
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
|
||||
MUMPSSolver mumps;
|
||||
@@ -481,7 +497,7 @@ int main(int argc, char *argv[])
|
||||
//
|
||||
// In PML: 1/mu (abs(1/det(J) J^T J) Curl E, Curl F)
|
||||
// + omega^2 * epsilon (abs(det(J) * (J^T J)^-1) * E, F)
|
||||
if (!slu_solver && !mumps_solver)
|
||||
if (pa || (!slu_solver && !mumps_solver))
|
||||
{
|
||||
ConstantCoefficient absomeg(pow(omega, 2) * epsilon);
|
||||
RestrictedCoefficient restr_absomeg(absomeg,attr);
|
||||
@@ -501,11 +517,9 @@ int main(int argc, char *argv[])
|
||||
prec.AddDomainIntegrator(new CurlCurlIntegrator(restr_c1_abs));
|
||||
prec.AddDomainIntegrator(new VectorFEMassIntegrator(restr_c2_abs));
|
||||
|
||||
if (pa) { prec.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
prec.Assemble();
|
||||
|
||||
OperatorPtr PCOpAh;
|
||||
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
|
||||
|
||||
// 16b. Define and apply a parallel GMRES solver for AU=B with a block
|
||||
// diagonal preconditioner based on hypre's AMS preconditioner.
|
||||
Array<int> offsets(3);
|
||||
@@ -514,21 +528,41 @@ int main(int argc, char *argv[])
|
||||
offsets[2] = fespace->GetTrueVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
HypreAMS ams00(*PCOpAh.As<HypreParMatrix>(),fespace);
|
||||
BlockDiagonalPreconditioner BlockAMS(offsets);
|
||||
ScaledOperator ams11(&ams00,
|
||||
(conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0);
|
||||
BlockAMS.SetDiagonalBlock(0,&ams00);
|
||||
BlockAMS.SetDiagonalBlock(1,&ams11);
|
||||
Operator *pc_r = nullptr;
|
||||
Operator *pc_i = nullptr;
|
||||
int s = (conv == ComplexOperator::HERMITIAN) ? -1.0 : 1.0;
|
||||
if (pa)
|
||||
{
|
||||
// Jacobi Smoother
|
||||
OperatorJacobiSmoother *d00 = new OperatorJacobiSmoother(prec, ess_tdof_list);
|
||||
ScaledOperator *d11 = new ScaledOperator(d00, s);
|
||||
pc_r = d00;
|
||||
pc_i = d11;
|
||||
}
|
||||
else
|
||||
{
|
||||
OperatorPtr PCOpAh;
|
||||
prec.FormSystemMatrix(ess_tdof_list, PCOpAh);
|
||||
|
||||
// Hypre AMS
|
||||
HypreAMS *ams00 = new HypreAMS(*PCOpAh.As<HypreParMatrix>(), fespace);
|
||||
ScaledOperator *ams11 = new ScaledOperator(ams00, s);
|
||||
pc_r = ams00;
|
||||
pc_i = ams11;
|
||||
}
|
||||
|
||||
BlockDiagonalPreconditioner BlockDP(offsets);
|
||||
BlockDP.SetDiagonalBlock(0, pc_r);
|
||||
BlockDP.SetDiagonalBlock(1, pc_i);
|
||||
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetKDim(200);
|
||||
gmres.SetMaxIter(2000);
|
||||
gmres.SetMaxIter(pa ? 5000 : 2000);
|
||||
gmres.SetRelTol(1e-5);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetOperator(*Ah);
|
||||
gmres.SetPreconditioner(BlockAMS);
|
||||
gmres.SetPreconditioner(BlockDP);
|
||||
gmres.Mult(B, X);
|
||||
}
|
||||
|
||||
@@ -1003,8 +1037,12 @@ void CartesianPML::SetBoundaries()
|
||||
|
||||
void CartesianPML::SetAttributes(ParMesh *pmesh)
|
||||
{
|
||||
int myid;
|
||||
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
|
||||
// Initialize bdr attributes
|
||||
for (int i = 0; i < pmesh->GetNBE(); ++i)
|
||||
{
|
||||
pmesh->GetBdrElement(i)->SetAttribute(i+1);
|
||||
}
|
||||
|
||||
int nrelem = pmesh->GetNE();
|
||||
|
||||
// Initialize list with 1
|
||||
|
||||
+2
-2
@@ -40,7 +40,7 @@ using namespace mfem;
|
||||
// in the FiniteElementSpaceHierarchy. The preconditioner uses a CG solver on
|
||||
// the coarsest level and second order Chebyshev accelerated smoothers on the
|
||||
// other levels.
|
||||
class DiffusionMultigrid : public Multigrid
|
||||
class DiffusionMultigrid : public GeometricMultigrid
|
||||
{
|
||||
private:
|
||||
ConstantCoefficient one;
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
// Constructs a diffusion multigrid for the given FiniteElementSpaceHierarchy
|
||||
// and the array of essential boundaries
|
||||
DiffusionMultigrid(FiniteElementSpaceHierarchy& fespaces, Array<int>& ess_bdr)
|
||||
: Multigrid(fespaces), one(1.0)
|
||||
: GeometricMultigrid(fespaces), one(1.0)
|
||||
{
|
||||
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr);
|
||||
|
||||
|
||||
+2
-2
@@ -37,7 +37,7 @@ using namespace mfem;
|
||||
// all spaces except the coarsest one in the ParFiniteElementSpaceHierarchy.
|
||||
// The multigrid uses a PCG solver preconditioned with AMG on the coarsest level
|
||||
// and second order Chebyshev accelerated smoothers on the other levels.
|
||||
class DiffusionMultigrid : public Multigrid
|
||||
class DiffusionMultigrid : public GeometricMultigrid
|
||||
{
|
||||
private:
|
||||
ConstantCoefficient one;
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
// and the array of essential boundaries
|
||||
DiffusionMultigrid(ParFiniteElementSpaceHierarchy& fespaces,
|
||||
Array<int>& ess_bdr)
|
||||
: Multigrid(fespaces), one(1.0)
|
||||
: GeometricMultigrid(fespaces), one(1.0)
|
||||
{
|
||||
ConstructCoarseOperatorAndSolver(fespaces.GetFESpaceAtLevel(0), ess_bdr);
|
||||
|
||||
|
||||
+20
-10
@@ -22,6 +22,7 @@
|
||||
//
|
||||
// Device sample runs:
|
||||
// mpirun -np 4 ex3p -m ../data/star.mesh -pa -d cuda
|
||||
// mpirun -np 4 ex3p -m ../data/star.mesh -no-pa -d cuda
|
||||
// mpirun -np 4 ex3p -m ../data/star.mesh -pa -d raja-cuda
|
||||
// mpirun -np 4 ex3p -m ../data/star.mesh -pa -d raja-omp
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh -pa -d cuda
|
||||
@@ -68,7 +69,10 @@ int main(int argc, char *argv[])
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 1;
|
||||
bool visualization = true;
|
||||
#ifdef MFEM_USE_AMGX
|
||||
bool useAmgX = false;
|
||||
#endif
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -86,6 +90,11 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
#ifdef MFEM_USE_AMGX
|
||||
args.AddOption(&useAmgX, "-amgx", "--useAmgX", "-no-amgx",
|
||||
"--no-useAmgX",
|
||||
"Enable or disable AmgX in MatrixFreeAMS.");
|
||||
#endif
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
@@ -158,9 +167,10 @@ int main(int argc, char *argv[])
|
||||
// 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;
|
||||
Array<int> ess_bdr;
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
Array<int> ess_bdr(pmesh->bdr_attributes.Max());
|
||||
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
@@ -204,20 +214,20 @@ int main(int argc, char *argv[])
|
||||
Vector B, X;
|
||||
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
|
||||
|
||||
// 13. Solve the system AX=B using PCG with the AMS preconditioner from hypre
|
||||
// (in the full assembly case) or CG with Jacobi preconditioner (in the
|
||||
// partial assembly case).
|
||||
|
||||
if (pa) // Jacobi preconditioning in partial assembly mode
|
||||
// 13. Solve the system AX=B using PCG with an AMS preconditioner.
|
||||
if (pa)
|
||||
{
|
||||
OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
|
||||
|
||||
#ifdef MFEM_USE_AMGX
|
||||
MatrixFreeAMS ams(*a, *A, *fespace, muinv, sigma, NULL, ess_bdr, useAmgX);
|
||||
#else
|
||||
MatrixFreeAMS ams(*a, *A, *fespace, muinv, sigma, NULL, ess_bdr);
|
||||
#endif
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(1000);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetOperator(*A);
|
||||
cg.SetPreconditioner(Jacobi);
|
||||
cg.SetPreconditioner(ams);
|
||||
cg.Mult(B, X);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -0,0 +1,517 @@
|
||||
// MFEM Example 3 - Parallel Version
|
||||
//
|
||||
// Compile with: make ex3p_complex
|
||||
//
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
// Exact solution, E, and r.h.s., f. See below for implementation.
|
||||
void E_exact(const Vector &, Vector &);
|
||||
void curlE_exact(const Vector &, Vector &);
|
||||
void f_exact(const Vector &, Vector &);
|
||||
double freq = 1.0, kappa;
|
||||
int dim;
|
||||
|
||||
|
||||
#define COMPLEX_VERSION
|
||||
#define NEUMANN
|
||||
#define INDEFINITE
|
||||
|
||||
const double omega = 1.4;
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Initialize MPI.
|
||||
int num_procs, myid;
|
||||
MPI_Init(&argc, &argv);
|
||||
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
|
||||
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
|
||||
|
||||
// 2. Parse command-line options.
|
||||
const char *mesh_file = "../data/beam-tet.mesh";
|
||||
int order = 1;
|
||||
bool static_cond = false;
|
||||
bool pa = false;
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = 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).");
|
||||
args.AddOption(&freq, "-f", "--frequency", "Set the frequency for the exact"
|
||||
" solution.");
|
||||
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(&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.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
kappa = freq * M_PI;
|
||||
|
||||
// 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);
|
||||
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 = new Mesh(mesh_file, 1, 1);
|
||||
dim = mesh->Dimension();
|
||||
int sdim = mesh->SpaceDimension();
|
||||
|
||||
// 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 1,000 elements.
|
||||
{
|
||||
int ref_levels = (int)floor(log(1000./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. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
int par_ref_levels = 0;
|
||||
for (int l = 0; l < par_ref_levels; l++)
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
FiniteElementCollection *fec = new ND_FECollection(order, dim);
|
||||
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
|
||||
HYPRE_Int 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;
|
||||
Array<int> ess_bdr;
|
||||
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
|
||||
ess_bdr = 0;
|
||||
|
||||
#ifndef NEUMANN
|
||||
if (pmesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr = 1;
|
||||
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
|
||||
}
|
||||
#endif
|
||||
|
||||
//const double imscale = 0.0;
|
||||
const double imscale = omega;
|
||||
|
||||
Coefficient *im = new ConstantCoefficient(imscale); // im part
|
||||
//Coefficient *im = new ConstantCoefficient(0.0); // im part
|
||||
|
||||
VectorFunctionCoefficient E_Re(sdim, E_exact);
|
||||
VectorFunctionCoefficient curlE_Re(sdim, curlE_exact);
|
||||
|
||||
ScalarVectorProductCoefficient omegaE(imscale, E_Re); // im part
|
||||
//ScalarVectorProductCoefficient omegaE(0.0, E_Re); // im part
|
||||
|
||||
// 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
|
||||
// (f,phi_i) where f is given by the function f_exact and phi_i are the
|
||||
// basis functions in the finite element fespace.
|
||||
VectorFunctionCoefficient f(sdim, f_exact);
|
||||
#ifdef COMPLEX_VERSION
|
||||
ParComplexLinearForm *b = new ParComplexLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f), NULL);
|
||||
b->AddBoundaryIntegrator(NULL,
|
||||
new VectorFEDomainLFIntegrator(omegaE)); // im part
|
||||
#else
|
||||
// Real version
|
||||
ParLinearForm *b = new ParLinearForm(fespace);
|
||||
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
|
||||
#endif
|
||||
|
||||
#ifdef NEUMANN
|
||||
b->AddBoundaryIntegrator(new VectorFEBoundaryTangentLFIntegrator(curlE_Re),
|
||||
NULL);
|
||||
#endif
|
||||
|
||||
b->Assemble();
|
||||
|
||||
// 10. Define the solution vector x as a parallel finite element grid function
|
||||
// corresponding to fespace. Initialize x by projecting the exact
|
||||
// solution. Note that only values from the boundary edges will be used
|
||||
// when eliminating the non-homogeneous boundary condition to modify the
|
||||
// r.h.s. vector b.
|
||||
/*
|
||||
ParGridFunction x(fespace);
|
||||
VectorFunctionCoefficient E(sdim, E_exact);
|
||||
x.ProjectCoefficient(E);
|
||||
*/
|
||||
|
||||
#ifdef COMPLEX_VERSION
|
||||
// Complex version
|
||||
ParComplexGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
Vector zero(sdim);
|
||||
zero = 0.0;
|
||||
VectorConstantCoefficient E_Im(zero);
|
||||
//x.ProjectBdrCoefficientTangent(E_Re, E_Im, ess_bdr);
|
||||
x.ProjectCoefficient(E_Re, E_Im);
|
||||
#else
|
||||
ParGridFunction x(fespace);
|
||||
x = 0.0;
|
||||
x.ProjectCoefficient(E_Re);
|
||||
#endif
|
||||
|
||||
// 11. Set up the parallel bilinear form corresponding to the EM diffusion
|
||||
// operator curl muinv curl + sigma I, by adding the curl-curl and the
|
||||
// mass domain integrators.
|
||||
Coefficient *muinv = new ConstantCoefficient(1.0);
|
||||
#ifdef INDEFINITE
|
||||
Coefficient *sigma = new ConstantCoefficient(
|
||||
-omega*omega); // indefinite -, definite +
|
||||
#else
|
||||
Coefficient *sigma = new ConstantCoefficient(
|
||||
omega*omega); // indefinite -, definite +
|
||||
#endif
|
||||
Coefficient *abssigma = new ConstantCoefficient(omega*omega);
|
||||
Coefficient *imabs = new ConstantCoefficient(imscale); // im part
|
||||
//Coefficient *imabs = new ConstantCoefficient(0.0); // im part
|
||||
//Coefficient *im = new ConstantCoefficient(0.0);
|
||||
|
||||
#ifdef COMPLEX_VERSION
|
||||
// Complex version
|
||||
ParSesquilinearForm *a = new ParSesquilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv), NULL);
|
||||
//a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma), new VectorFEMassIntegrator(*im));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma), NULL);
|
||||
a->AddBoundaryIntegrator(NULL, new VectorFEMassIntegrator(*im)); // im part
|
||||
#else
|
||||
// Real version
|
||||
ParBilinearForm *a = new ParBilinearForm(fespace);
|
||||
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
|
||||
//a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma), new VectorFEMassIntegrator(*im));
|
||||
a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
|
||||
ParBilinearForm a_Re(fespace);
|
||||
a_Re.AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
|
||||
a_Re.AddDomainIntegrator(new VectorFEMassIntegrator(*abssigma));
|
||||
|
||||
//if (pa) { a_Re.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
|
||||
a_Re.SetAssemblyLevel(AssemblyLevel::PARTIAL);
|
||||
a_Re.Assemble();
|
||||
|
||||
OperatorPtr A_Re;
|
||||
a_Re.FormSystemMatrix(ess_tdof_list, A_Re);
|
||||
|
||||
ParBilinearForm a_Im(fespace);
|
||||
a_Im.AddBoundaryIntegrator(new VectorFEMassIntegrator(*imabs));
|
||||
a_Im.Assemble();
|
||||
|
||||
OperatorPtr A_Im;
|
||||
a_Im.FormSystemMatrix(ess_tdof_list, A_Im);
|
||||
|
||||
// 13. Solve the system AX=B using PCG with the AMS preconditioner from hypre
|
||||
// (in the full assembly case) or CG with Jacobi preconditioner (in the
|
||||
// partial assembly case).
|
||||
|
||||
Array<int> offsets(3);
|
||||
offsets[0] = 0;
|
||||
offsets[1] = fespace->GetTrueVSize();
|
||||
offsets[2] = fespace->GetTrueVSize();
|
||||
offsets.PartialSum();
|
||||
|
||||
//OperatorJacobiSmoother massJacobi(a_Im, ess_tdof_list);
|
||||
|
||||
StopWatch sw;
|
||||
sw.Clear();
|
||||
sw.Start();
|
||||
|
||||
if (pa) // Jacobi preconditioning in partial assembly mode
|
||||
{
|
||||
MFEM_VERIFY(false, "TODO");
|
||||
//OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
|
||||
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(1000);
|
||||
cg.SetPrintLevel(1);
|
||||
cg.SetOperator(*A);
|
||||
//cg.SetPreconditioner(Jacobi);
|
||||
cg.Mult(B, X);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Size of linear system: "
|
||||
<< A.As<HypreParMatrix>()->GetGlobalNumRows() << endl;
|
||||
}
|
||||
|
||||
//HypreAMS ams(*A_Re.As<HypreParMatrix>(), fespace);
|
||||
|
||||
// One option is to use the standard real-valued MatrixFreeAMS to precondition
|
||||
// the real part of the complex system in the PMHSS preconditioner (BlockDiagonalPreconditioner).
|
||||
// Another option is to use complex MatrixFreeAMS to precondition the
|
||||
// complex system without PMHSS and without a BlockDiagonalPreconditioner.
|
||||
//#define COMPLEX_AMS
|
||||
|
||||
#ifdef MFEM_USE_AMGX
|
||||
bool useAmgX = false;
|
||||
cout << "Built with AMGX, using AMGX " << useAmgX << endl;
|
||||
MatrixFreeAMS ams(a_Re, *A_Re, *fespace, muinv, abssigma, im, imabs, NULL,
|
||||
ess_bdr, useAmgX);
|
||||
MatrixFreeAMS ams(a_Re, *A_Re, *fespace, muinv, abssigma, NULL, NULL, ess_bdr,
|
||||
useAmgX);
|
||||
#ifdef COMPLEX_AMS
|
||||
MFEM_VERIFY(false, "TODO");
|
||||
#endif
|
||||
|
||||
#else
|
||||
cout << "Not built with AMGX" << endl;
|
||||
#ifdef COMPLEX_AMS
|
||||
MatrixFreeAMS ams(a_Re, *A_Re, A.Ptr(), *fespace, muinv, abssigma, im, imabs,
|
||||
NULL, ess_bdr);
|
||||
#else
|
||||
MatrixFreeAMS ams(a_Re, *A_Re, NULL, *fespace, muinv, abssigma, NULL, NULL,
|
||||
NULL, ess_bdr);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef COMPLEX_VERSION
|
||||
|
||||
#ifdef COMPLEX_AMS
|
||||
//MFEM_VERIFY(false, "TODO");
|
||||
#else
|
||||
BlockDiagonalPreconditioner BlockDP(offsets);
|
||||
BlockDP.SetDiagonalBlock(0, &ams);
|
||||
BlockDP.SetDiagonalBlock(1, &ams);
|
||||
|
||||
/*
|
||||
BlockDiagonalPreconditioner BlockDP_Im(offsets);
|
||||
BlockDP_Im.SetDiagonalBlock(0, &massJacobi); // TODO: this won't work if it has zeros on diagonal
|
||||
BlockDP_Im.SetDiagonalBlock(1, &massJacobi);
|
||||
*/
|
||||
|
||||
//Complex_PMHSS PMHSS(A_Re, A_Im, &BlockDP, &BlockDP_Im);
|
||||
//Complex_PMHSS PMHSS(A_Re, A_Im, &BlockDP, NULL, 2.0 * omega);
|
||||
//Complex_PMHSS PMHSS(A_Re, A_Im, &BlockDP, NULL, omega);
|
||||
Complex_PMHSS PMHSS(A_Re.Ptr(), A_Im.Ptr(), &BlockDP, NULL, 1.0);
|
||||
|
||||
ComplexOperator AspdComplex(A_Re.Ptr(), A_Im.Ptr(), false, false);
|
||||
|
||||
GMRESSolver PMHSSgmres(MPI_COMM_WORLD);
|
||||
PMHSSgmres.SetPrintLevel(1);
|
||||
PMHSSgmres.SetKDim(100);
|
||||
PMHSSgmres.SetMaxIter(100);
|
||||
PMHSSgmres.SetRelTol(1e-6);
|
||||
PMHSSgmres.SetAbsTol(0.0);
|
||||
PMHSSgmres.SetOperator(AspdComplex);
|
||||
PMHSSgmres.SetPreconditioner(PMHSS);
|
||||
#endif
|
||||
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetKDim(1000);
|
||||
gmres.SetMaxIter(100);
|
||||
gmres.SetRelTol(1e-8);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetOperator(*A);
|
||||
//gmres.SetPreconditioner(BlockDP);
|
||||
#ifdef COMPLEX_AMS
|
||||
//MFEM_VERIFY(false, "TODO");
|
||||
gmres.SetPreconditioner(ams);
|
||||
#else
|
||||
gmres.SetPreconditioner(PMHSS);
|
||||
//gmres.SetPreconditioner(PMHSSgmres);
|
||||
#endif
|
||||
|
||||
#else
|
||||
GMRESSolver gmres(MPI_COMM_WORLD);
|
||||
gmres.SetPrintLevel(1);
|
||||
gmres.SetKDim(1000);
|
||||
gmres.SetMaxIter(100);
|
||||
gmres.SetRelTol(1e-8);
|
||||
gmres.SetAbsTol(0.0);
|
||||
gmres.SetOperator(*A);
|
||||
gmres.SetPreconditioner(ams);
|
||||
#endif
|
||||
|
||||
gmres.Mult(B, X);
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
mfem::out << "Total solve time " <<sw.RealTime() << endl;
|
||||
|
||||
// 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. Compute and print the L^2 norm of the error.
|
||||
{
|
||||
#ifdef COMPLEX_VERSION
|
||||
double err = x.real().ComputeL2Error(E_Re);
|
||||
#else
|
||||
double err = x.ComputeL2Error(E_Re);
|
||||
#endif
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "\n|| E_h - E ||_{L^2} = " << err << '\n' << endl;
|
||||
}
|
||||
}
|
||||
|
||||
// 16. 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);
|
||||
#ifdef COMPLEX_VERSION
|
||||
x.real().Save(sol_ofs);
|
||||
#else
|
||||
x.Save(sol_ofs);
|
||||
#endif
|
||||
}
|
||||
|
||||
// 17. 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);
|
||||
#ifdef COMPLEX_VERSION
|
||||
sol_sock << "solution\n" << *pmesh << x.real() << flush;
|
||||
#else
|
||||
sol_sock << "solution\n" << *pmesh << x << flush;
|
||||
#endif
|
||||
}
|
||||
|
||||
// 18. Free the used memory.
|
||||
delete a;
|
||||
delete sigma;
|
||||
delete muinv;
|
||||
delete b;
|
||||
delete fespace;
|
||||
delete fec;
|
||||
delete pmesh;
|
||||
|
||||
MPI_Finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void E_exact(const Vector &x, Vector &E)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(2));
|
||||
E(2) = sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
E(0) = sin(kappa * x(1));
|
||||
E(1) = sin(kappa * x(0));
|
||||
if (x.Size() == 3) { E(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
|
||||
void curlE_exact(const Vector &x, Vector &curl)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
curl(0) = kappa * cos(kappa * x(2));
|
||||
curl(1) = kappa * cos(kappa * x(0));
|
||||
curl(2) = kappa * cos(kappa * x(1));
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_VERIFY(false, "");
|
||||
}
|
||||
}
|
||||
|
||||
void f_exact(const Vector &x, Vector &f)
|
||||
{
|
||||
if (dim == 3)
|
||||
{
|
||||
// indefinite -m, definite +m
|
||||
const double c = kappa * kappa;
|
||||
#ifdef INDEFINITE
|
||||
const double m = -omega * omega;
|
||||
#else
|
||||
const double m = omega * omega;
|
||||
#endif
|
||||
f(0) = (c + m) * sin(kappa * x(1));
|
||||
f(1) = (c + m) * sin(kappa * x(2));
|
||||
f(2) = (c + m) * sin(kappa * x(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
f(0) = (1. + kappa * kappa) * sin(kappa * x(1));
|
||||
f(1) = (1. + kappa * kappa) * sin(kappa * x(0));
|
||||
if (x.Size() == 3) { f(2) = 0.0; }
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ set(SRCS
|
||||
bilinearform.cpp
|
||||
bilinearform_ext.cpp
|
||||
bilininteg.cpp
|
||||
bilininteg_br2.cpp
|
||||
bilininteg_convection_pa.cpp
|
||||
bilininteg_convection_ea.cpp
|
||||
bilininteg_dgtrace_pa.cpp
|
||||
|
||||
+34
-2
@@ -1437,9 +1437,9 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
ftr = mesh->GetBdrFaceTransformations(i);
|
||||
if (ftr)
|
||||
{
|
||||
trial_fes->GetFaceVDofs(i, tr_vdofs);
|
||||
trial_fes->GetFaceVDofs(ftr->ElementNo, tr_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs);
|
||||
trial_face_fe = trial_fes->GetFaceElement(i);
|
||||
trial_face_fe = trial_fes->GetFaceElement(ftr->ElementNo);
|
||||
test_fe1 = test_fes->GetFE(ftr->Elem1No);
|
||||
// The test_fe2 object is really a dummy and not used on the
|
||||
// boundaries, but we can't dereference a NULL pointer, and we don't
|
||||
@@ -1770,9 +1770,41 @@ MixedBilinearForm::~MixedBilinearForm()
|
||||
delete ext;
|
||||
}
|
||||
|
||||
void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
MFEM_ABORT("the assembly level has already been set!");
|
||||
}
|
||||
assembly = assembly_level;
|
||||
switch (assembly)
|
||||
{
|
||||
case AssemblyLevel::LEGACYFULL:
|
||||
case AssemblyLevel::FULL:
|
||||
// Use the original implementation for now
|
||||
break;
|
||||
case AssemblyLevel::ELEMENT:
|
||||
mfem_error("Element assembly not supported yet... stay tuned!");
|
||||
break;
|
||||
case AssemblyLevel::PARTIAL:
|
||||
ext = new PADiscreteLinearOperatorExtension(this);
|
||||
break;
|
||||
case AssemblyLevel::NONE:
|
||||
mfem_error("Matrix-free action not supported yet... stay tuned!");
|
||||
break;
|
||||
default:
|
||||
mfem_error("Unknown assembly level");
|
||||
}
|
||||
}
|
||||
|
||||
void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
ext->Assemble();
|
||||
return;
|
||||
}
|
||||
|
||||
Array<int> dom_vdofs, ran_vdofs;
|
||||
ElementTransformation *T;
|
||||
const FiniteElement *dom_fe, *ran_fe;
|
||||
|
||||
@@ -376,6 +376,13 @@ public:
|
||||
/// Get the output finite element space prolongation matrix
|
||||
virtual const Operator *GetOutputProlongation() const
|
||||
{ return GetProlongation(); }
|
||||
/** @brief Returns the output fe space restriction matrix, transposed
|
||||
|
||||
Logically, this is the transpose of GetOutputRestriction, but in
|
||||
practice it is convenient to have it in transposed form for
|
||||
construction of RAP operators in matrix-free methods. */
|
||||
virtual const Operator *GetOutputRestrictionTranspose() const
|
||||
{ return GetOutputProlongation(); }
|
||||
/// Get the output finite element space restriction matrix
|
||||
virtual const Operator *GetOutputRestriction() const
|
||||
{ return GetRestriction(); }
|
||||
@@ -977,9 +984,18 @@ public:
|
||||
/// Access all interpolators added with AddDomainInterpolator().
|
||||
Array<BilinearFormIntegrator*> *GetDI() { return &dbfi; }
|
||||
|
||||
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
|
||||
/** This method must be called before assembly. */
|
||||
void SetAssemblyLevel(AssemblyLevel assembly_level);
|
||||
|
||||
/** @brief Construct the internal matrix representation of the discrete
|
||||
linear operator. */
|
||||
virtual void Assemble(int skip_zeros = 1);
|
||||
|
||||
/** @brief Get the output finite element space restriction matrix in
|
||||
transposed form. */
|
||||
virtual const Operator *GetOutputRestrictionTranspose() const
|
||||
{ return test_fes->GetRestrictionTransposeOperator(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+130
-1
@@ -1021,7 +1021,6 @@ void PAMixedBilinearFormExtension::Update()
|
||||
localTrial.UseDevice(true);
|
||||
localTrial.SetSize(elem_restrict_trial->Height(),
|
||||
Device::GetMemoryType());
|
||||
|
||||
}
|
||||
if (elem_restrict_test)
|
||||
{
|
||||
@@ -1221,4 +1220,134 @@ void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
|
||||
}
|
||||
}
|
||||
|
||||
PADiscreteLinearOperatorExtension::PADiscreteLinearOperatorExtension(
|
||||
DiscreteLinearOperator *linop) :
|
||||
PAMixedBilinearFormExtension(linop)
|
||||
{
|
||||
}
|
||||
|
||||
const
|
||||
Operator *PADiscreteLinearOperatorExtension::GetOutputRestrictionTranspose()
|
||||
const
|
||||
{
|
||||
return a->GetOutputRestrictionTranspose();
|
||||
}
|
||||
|
||||
void PADiscreteLinearOperatorExtension::Assemble()
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
const int integratorCount = integrators.Size();
|
||||
for (int i = 0; i < integratorCount; ++i)
|
||||
{
|
||||
integrators[i]->AssemblePA(*trialFes, *testFes);
|
||||
}
|
||||
|
||||
test_multiplicity.UseDevice(true);
|
||||
test_multiplicity.SetSize(elem_restrict_test->Width()); // l-vector
|
||||
Vector ones(elem_restrict_test->Height()); // e-vector
|
||||
ones = 1.0;
|
||||
|
||||
const ElementRestriction* elem_restrict =
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
|
||||
if (elem_restrict)
|
||||
{
|
||||
elem_restrict->MultTransposeUnsigned(ones, test_multiplicity);
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("A real ElementRestriction is required in this setting!");
|
||||
}
|
||||
|
||||
auto tm = test_multiplicity.ReadWrite();
|
||||
MFEM_FORALL(i, test_multiplicity.Size(),
|
||||
{
|
||||
tm[i] = 1.0 / tm[i];
|
||||
});
|
||||
}
|
||||
|
||||
void PADiscreteLinearOperatorExtension::AddMult(
|
||||
const Vector &x, Vector &y, const double c) const
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
const int iSz = integrators.Size();
|
||||
|
||||
// * G operation
|
||||
SetupMultInputs(elem_restrict_trial, x, localTrial,
|
||||
elem_restrict_test, y, localTest, c);
|
||||
|
||||
// * B^TDB operation
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
integrators[i]->AddMultPA(localTrial, localTest);
|
||||
}
|
||||
|
||||
// do a kind of "set" rather than "add" in the below
|
||||
// operation as compared to the BilinearForm case
|
||||
// * G^T operation (kind of...)
|
||||
const ElementRestriction* elem_restrict =
|
||||
dynamic_cast<const ElementRestriction*>(elem_restrict_test);
|
||||
if (elem_restrict)
|
||||
{
|
||||
tempY.SetSize(y.Size());
|
||||
elem_restrict->MultLeftInverse(localTest, tempY);
|
||||
y += tempY;
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("In this setting you need a real ElementRestriction!");
|
||||
}
|
||||
}
|
||||
|
||||
void PADiscreteLinearOperatorExtension::AddMultTranspose(
|
||||
const Vector &x, Vector &y, const double c) const
|
||||
{
|
||||
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
|
||||
const int iSz = integrators.Size();
|
||||
|
||||
// do a kind of "set" rather than "add" in the below
|
||||
// operation as compared to the BilinearForm case
|
||||
// * G operation (kinda)
|
||||
Vector xscaled(x);
|
||||
MFEM_VERIFY(x.Size() == test_multiplicity.Size(), "Input vector of wrong size");
|
||||
auto xs = xscaled.ReadWrite();
|
||||
auto tm = test_multiplicity.Read();
|
||||
MFEM_FORALL(i, x.Size(),
|
||||
{
|
||||
xs[i] *= tm[i];
|
||||
});
|
||||
SetupMultInputs(elem_restrict_test, xscaled, localTest,
|
||||
elem_restrict_trial, y, localTrial, c);
|
||||
|
||||
// * B^TD^TB operation
|
||||
for (int i = 0; i < iSz; ++i)
|
||||
{
|
||||
integrators[i]->AddMultTransposePA(localTest, localTrial);
|
||||
}
|
||||
|
||||
// * G^T operation
|
||||
if (elem_restrict_trial)
|
||||
{
|
||||
tempY.SetSize(y.Size());
|
||||
elem_restrict_trial->MultTranspose(localTrial, tempY);
|
||||
y += tempY;
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("Trial ElementRestriction not defined");
|
||||
}
|
||||
}
|
||||
|
||||
void PADiscreteLinearOperatorExtension::FormRectangularSystemOperator(
|
||||
const Array<int>& ess1, const Array<int>& ess2, OperatorHandle &A)
|
||||
{
|
||||
const Operator *Pi = this->GetProlongation();
|
||||
const Operator *RoT = this->GetOutputRestrictionTranspose();
|
||||
Operator *rap = SetupRAP(Pi, RoT);
|
||||
|
||||
RectangularConstrainedOperator *Arco
|
||||
= new RectangularConstrainedOperator(rap, ess1, ess2, rap != this);
|
||||
|
||||
A.Reset(Arco);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -21,6 +21,7 @@ namespace mfem
|
||||
|
||||
class BilinearForm;
|
||||
class MixedBilinearForm;
|
||||
class DiscreteLinearOperator;
|
||||
|
||||
/// Class extending the BilinearForm class to support different AssemblyLevels.
|
||||
/** FA - Full Assembly
|
||||
@@ -212,7 +213,7 @@ protected:
|
||||
mutable Vector localTrial, localTest, tempY;
|
||||
const Operator *elem_restrict_trial; // Not owned
|
||||
const Operator *elem_restrict_test; // Not owned
|
||||
private:
|
||||
|
||||
/// Helper function to set up inputs/outputs for Mult or MultTranspose
|
||||
void SetupMultInputs(const Operator *elem_restrict_x,
|
||||
const Vector &x, Vector &localX,
|
||||
@@ -258,6 +259,35 @@ public:
|
||||
void Update();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Partial assembly extension for DiscreteLinearOperator
|
||||
|
||||
This acts very much like PAMixedBilinearFormExtension, but its
|
||||
FormRectangularSystemOperator implementation emulates 'Set' rather than
|
||||
'Add' in the assembly case.
|
||||
*/
|
||||
class PADiscreteLinearOperatorExtension : public PAMixedBilinearFormExtension
|
||||
{
|
||||
public:
|
||||
PADiscreteLinearOperatorExtension(DiscreteLinearOperator *linop);
|
||||
|
||||
/// Partial assembly of all internal integrators
|
||||
void Assemble();
|
||||
|
||||
void AddMult(const Vector &x, Vector &y, const double c) const;
|
||||
|
||||
void AddMultTranspose(const Vector &x, Vector &y, const double c=1.0) const;
|
||||
|
||||
void FormRectangularSystemOperator(const Array<int>&, const Array<int>&,
|
||||
OperatorHandle& A);
|
||||
|
||||
const Operator * GetOutputRestrictionTranspose() const;
|
||||
|
||||
private:
|
||||
Vector test_multiplicity;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+56
-15
@@ -22,14 +22,14 @@ namespace mfem
|
||||
|
||||
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
|
||||
mfem_error ("BilinearFormIntegrator::AssemblePA(fes)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
void BilinearFormIntegrator::AssemblePA(const FiniteElementSpace&,
|
||||
const FiniteElementSpace&)
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::AssemblePA(...)\n"
|
||||
mfem_error ("BilinearFormIntegrator::AssemblePA(fes, fes)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ void BilinearFormIntegrator::AddMultPA(const Vector &, Vector &) const
|
||||
|
||||
void BilinearFormIntegrator::AddMultTransposePA(const Vector &, Vector &) const
|
||||
{
|
||||
mfem_error ("BilinearFormIntegrator::MultAssembledTranspose(...)\n"
|
||||
mfem_error ("BilinearFormIntegrator::AddMultTransposePA(...)\n"
|
||||
" is not implemented for this class.");
|
||||
}
|
||||
|
||||
@@ -1913,12 +1913,12 @@ void VectorFEMassIntegrator::AssembleElementMatrix(
|
||||
double w;
|
||||
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
Vector D(VQ ? VQ->GetVDim() : 0);
|
||||
Vector D(DQ ? DQ->GetVDim() : 0);
|
||||
DenseMatrix trial_vshape(dof, spaceDim);
|
||||
DenseMatrix K(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
|
||||
#else
|
||||
trial_vshape.SetSize(dof, spaceDim);
|
||||
D.SetSize(VQ ? VQ->GetVDim() : 0);
|
||||
D.SetSize(DQ ? DQ->GetVDim() : 0);
|
||||
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
|
||||
#endif
|
||||
DenseMatrix tmp(trial_vshape.Height(), K.Width());
|
||||
@@ -1950,9 +1950,9 @@ void VectorFEMassIntegrator::AssembleElementMatrix(
|
||||
Mult(trial_vshape,K,tmp);
|
||||
AddMultABt(tmp,trial_vshape,elmat);
|
||||
}
|
||||
else if (VQ)
|
||||
else if (DQ)
|
||||
{
|
||||
VQ->Eval(D, Trans, ip);
|
||||
DQ->Eval(D, Trans, ip);
|
||||
D *= w;
|
||||
AddMultADAt(trial_vshape, D, elmat);
|
||||
}
|
||||
@@ -1984,12 +1984,12 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix trial_vshape(trial_dof, spaceDim);
|
||||
Vector shape(test_dof);
|
||||
Vector D(VQ ? VQ->GetVDim() : 0);
|
||||
Vector D(DQ ? DQ->GetVDim() : 0);
|
||||
DenseMatrix K(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
|
||||
#else
|
||||
trial_vshape.SetSize(trial_dof, spaceDim);
|
||||
shape.SetSize(test_dof);
|
||||
D.SetSize(VQ ? VQ->GetVDim() : 0);
|
||||
D.SetSize(DQ ? DQ->GetVDim() : 0);
|
||||
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
|
||||
#endif
|
||||
|
||||
@@ -2013,9 +2013,9 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
|
||||
test_fe.CalcShape(ip, shape);
|
||||
|
||||
w = ip.weight * Trans.Weight();
|
||||
if (VQ)
|
||||
if (DQ)
|
||||
{
|
||||
VQ->Eval(D, Trans, ip);
|
||||
DQ->Eval(D, Trans, ip);
|
||||
D *= w;
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
@@ -2081,12 +2081,12 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
|
||||
#ifdef MFEM_THREAD_SAFE
|
||||
DenseMatrix trial_vshape(trial_dof,spaceDim);
|
||||
DenseMatrix test_vshape(test_dof,spaceDim);
|
||||
Vector D(VQ ? VQ->GetVDim() : 0);
|
||||
Vector D(DQ ? DQ->GetVDim() : 0);
|
||||
DenseMatrix K(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
|
||||
#else
|
||||
trial_vshape.SetSize(trial_dof,spaceDim);
|
||||
test_vshape.SetSize(test_dof,spaceDim);
|
||||
D.SetSize(VQ ? VQ->GetVDim() : 0);
|
||||
D.SetSize(DQ ? DQ->GetVDim() : 0);
|
||||
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
|
||||
#endif
|
||||
DenseMatrix tmp(test_vshape.Height(), K.Width());
|
||||
@@ -2118,9 +2118,9 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
|
||||
Mult(test_vshape,K,tmp);
|
||||
AddMultABt(tmp,trial_vshape,elmat);
|
||||
}
|
||||
else if (VQ)
|
||||
else if (DQ)
|
||||
{
|
||||
VQ->Eval(D, Trans, ip);
|
||||
DQ->Eval(D, Trans, ip);
|
||||
D *= w;
|
||||
AddMultADBt(test_vshape,D,trial_vshape,elmat);
|
||||
}
|
||||
@@ -3515,6 +3515,47 @@ VectorScalarProductInterpolator::AssembleElementMatrix2(
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ScalarCrossProductInterpolator::AssembleElementMatrix2(
|
||||
const FiniteElement &dom_fe,
|
||||
const FiniteElement &ran_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
// Vector coefficient product with vector shape functions
|
||||
struct VCrossVShapeCoefficient : public VectorCoefficient
|
||||
{
|
||||
VectorCoefficient &VQ;
|
||||
const FiniteElement &fe;
|
||||
DenseMatrix vshape;
|
||||
Vector vc;
|
||||
|
||||
VCrossVShapeCoefficient(VectorCoefficient &vq, const FiniteElement &fe_)
|
||||
: VectorCoefficient(fe_.GetDof()), VQ(vq), fe(fe_),
|
||||
vshape(vdim, vq.GetVDim()), vc(vq.GetVDim()) { }
|
||||
|
||||
virtual void Eval(Vector &V, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
V.SetSize(vdim);
|
||||
VQ.Eval(vc, T, ip);
|
||||
fe.CalcPhysVShape(T, vshape);
|
||||
for (int k = 0; k < vdim; k++)
|
||||
{
|
||||
V(k) = vc(0) * vshape(k,1) - vc(1) * vshape(k,0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VCrossVShapeCoefficient dom_shape_coeff(*VQ, dom_fe);
|
||||
|
||||
elmat.SetSize(ran_fe.GetDof(),dom_fe.GetDof());
|
||||
|
||||
Vector elmat_as_vec(elmat.Data(), elmat.Height()*elmat.Width());
|
||||
|
||||
ran_fe.Project(dom_shape_coeff, Trans, elmat_as_vec);
|
||||
}
|
||||
|
||||
void
|
||||
VectorCrossProductInterpolator::AssembleElementMatrix2(
|
||||
const FiniteElement &dom_fe,
|
||||
|
||||
+179
-39
@@ -465,8 +465,8 @@ protected:
|
||||
: same_calc_shape(false), Q(NULL), VQ(NULL), DQ(NULL), MQ(NULL) {}
|
||||
MixedVectorIntegrator(Coefficient &q)
|
||||
: same_calc_shape(false), Q(&q), VQ(NULL), DQ(NULL), MQ(NULL) {}
|
||||
MixedVectorIntegrator(VectorCoefficient &dq, bool diag = true)
|
||||
: same_calc_shape(false), Q(NULL), VQ(diag?NULL:&dq), DQ(diag?&dq:NULL),
|
||||
MixedVectorIntegrator(VectorCoefficient &vq, bool diag = true)
|
||||
: same_calc_shape(false), Q(NULL), VQ(diag?NULL:&vq), DQ(diag?&vq:NULL),
|
||||
MQ(NULL) {}
|
||||
MixedVectorIntegrator(MatrixCoefficient &mq)
|
||||
: same_calc_shape(false), Q(NULL), VQ(NULL), DQ(NULL), MQ(&mq) {}
|
||||
@@ -503,7 +503,7 @@ protected:
|
||||
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
VectorCoefficient *DQ;
|
||||
DiagonalMatrixCoefficient *DQ;
|
||||
MatrixCoefficient *MQ;
|
||||
|
||||
private:
|
||||
@@ -901,7 +901,7 @@ public:
|
||||
MixedVectorMassIntegrator() { same_calc_shape = true; }
|
||||
MixedVectorMassIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) { same_calc_shape = true; }
|
||||
MixedVectorMassIntegrator(VectorCoefficient &dq)
|
||||
MixedVectorMassIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) { same_calc_shape = true; }
|
||||
MixedVectorMassIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) { same_calc_shape = true; }
|
||||
@@ -1019,7 +1019,7 @@ public:
|
||||
MixedGradGradIntegrator() { same_calc_shape = true; }
|
||||
MixedGradGradIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) { same_calc_shape = true; }
|
||||
MixedGradGradIntegrator(VectorCoefficient &dq)
|
||||
MixedGradGradIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) { same_calc_shape = true; }
|
||||
MixedGradGradIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) { same_calc_shape = true; }
|
||||
@@ -1107,7 +1107,7 @@ public:
|
||||
MixedCurlCurlIntegrator() { same_calc_shape = true; }
|
||||
MixedCurlCurlIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) { same_calc_shape = true; }
|
||||
MixedCurlCurlIntegrator(VectorCoefficient &dq)
|
||||
MixedCurlCurlIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) { same_calc_shape = true; }
|
||||
MixedCurlCurlIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) { same_calc_shape = true; }
|
||||
@@ -1651,7 +1651,7 @@ public:
|
||||
MixedVectorGradientIntegrator() {}
|
||||
MixedVectorGradientIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) {}
|
||||
MixedVectorGradientIntegrator(VectorCoefficient &dq)
|
||||
MixedVectorGradientIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) {}
|
||||
MixedVectorGradientIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) {}
|
||||
@@ -1705,7 +1705,7 @@ public:
|
||||
MixedVectorCurlIntegrator() {}
|
||||
MixedVectorCurlIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) {}
|
||||
MixedVectorCurlIntegrator(VectorCoefficient &dq)
|
||||
MixedVectorCurlIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) {}
|
||||
MixedVectorCurlIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) {}
|
||||
@@ -1760,7 +1760,7 @@ public:
|
||||
MixedVectorWeakCurlIntegrator() {}
|
||||
MixedVectorWeakCurlIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) {}
|
||||
MixedVectorWeakCurlIntegrator(VectorCoefficient &dq)
|
||||
MixedVectorWeakCurlIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) {}
|
||||
MixedVectorWeakCurlIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) {}
|
||||
@@ -1813,7 +1813,7 @@ public:
|
||||
MixedVectorWeakDivergenceIntegrator() {}
|
||||
MixedVectorWeakDivergenceIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) {}
|
||||
MixedVectorWeakDivergenceIntegrator(VectorCoefficient &dq)
|
||||
MixedVectorWeakDivergenceIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) {}
|
||||
MixedVectorWeakDivergenceIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) {}
|
||||
@@ -1844,8 +1844,10 @@ protected:
|
||||
};
|
||||
|
||||
/** Class for integrating the bilinear form a(u,v) := (Q grad u, v) where Q is a
|
||||
scalar coefficient, and v is a vector with components v_i in the same space
|
||||
as u. */
|
||||
scalar coefficient, and v is a vector with components v_i in the same (H1) space
|
||||
as u.
|
||||
|
||||
See also MixedVectorGradientIntegrator when v is in H(curl). */
|
||||
class GradientIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
@@ -1900,6 +1902,7 @@ protected:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
MatrixCoefficient *MQ;
|
||||
SymmetricMatrixCoefficient *SMQ;
|
||||
|
||||
private:
|
||||
Vector vec, pointflux, shape;
|
||||
@@ -1922,19 +1925,28 @@ private:
|
||||
public:
|
||||
/// Construct a diffusion integrator with coefficient Q = 1
|
||||
DiffusionIntegrator()
|
||||
: Q(NULL), VQ(NULL), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
|
||||
: Q(NULL), VQ(NULL), MQ(NULL), SMQ(NULL), maps(NULL), geom(NULL),
|
||||
ceedDataPtr(NULL) { }
|
||||
|
||||
/// Construct a diffusion integrator with a scalar coefficient q
|
||||
DiffusionIntegrator(Coefficient &q)
|
||||
: Q(&q), VQ(NULL), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
|
||||
: Q(&q), VQ(NULL), MQ(NULL), SMQ(NULL), maps(NULL), geom(NULL),
|
||||
ceedDataPtr(NULL) { }
|
||||
|
||||
/// Construct a diffusion integrator with a vector coefficient q
|
||||
DiffusionIntegrator(VectorCoefficient &q)
|
||||
: Q(NULL), VQ(&q), MQ(NULL), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
|
||||
: Q(NULL), VQ(&q), MQ(NULL), SMQ(NULL), maps(NULL), geom(NULL),
|
||||
ceedDataPtr(NULL) { }
|
||||
|
||||
/// Construct a diffusion integrator with a matrix coefficient q
|
||||
DiffusionIntegrator(MatrixCoefficient &q)
|
||||
: Q(NULL), VQ(NULL), MQ(&q), maps(NULL), geom(NULL), ceedDataPtr(NULL) { }
|
||||
: Q(NULL), VQ(NULL), MQ(&q), SMQ(NULL), maps(NULL), geom(NULL),
|
||||
ceedDataPtr(NULL) { }
|
||||
|
||||
/// Construct a diffusion integrator with a symmetric matrix coefficient q
|
||||
DiffusionIntegrator(SymmetricMatrixCoefficient &q)
|
||||
: Q(NULL), VQ(NULL), MQ(NULL), SMQ(&q), maps(NULL), geom(NULL),
|
||||
ceedDataPtr(NULL) { }
|
||||
|
||||
virtual ~DiffusionIntegrator()
|
||||
{
|
||||
@@ -2050,8 +2062,6 @@ public:
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans);
|
||||
|
||||
void SetupPA(const FiniteElementSpace &fes);
|
||||
};
|
||||
|
||||
/** Mass integrator (u, v) restricted to the boundary of a domain */
|
||||
@@ -2335,8 +2345,9 @@ private:
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *DQ;
|
||||
DiagonalMatrixCoefficient *DQ;
|
||||
MatrixCoefficient *MQ;
|
||||
SymmetricMatrixCoefficient *SMQ;
|
||||
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
@@ -2347,14 +2358,18 @@ protected:
|
||||
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
|
||||
|
||||
public:
|
||||
CurlCurlIntegrator() { Q = NULL; DQ = NULL; MQ = NULL; }
|
||||
CurlCurlIntegrator() { Q = NULL; DQ = NULL; MQ = NULL; SMQ = NULL; }
|
||||
/// Construct a bilinear form integrator for Nedelec elements
|
||||
CurlCurlIntegrator(Coefficient &q, const IntegrationRule *ir = NULL) :
|
||||
BilinearFormIntegrator(ir), Q(&q) { DQ = NULL; MQ = NULL; }
|
||||
CurlCurlIntegrator(VectorCoefficient &dq, const IntegrationRule *ir = NULL) :
|
||||
BilinearFormIntegrator(ir), DQ(&dq) { Q = NULL; MQ = NULL; }
|
||||
BilinearFormIntegrator(ir), Q(&q), DQ(NULL), MQ(NULL), SMQ(NULL) { }
|
||||
CurlCurlIntegrator(DiagonalMatrixCoefficient &dq,
|
||||
const IntegrationRule *ir = NULL) :
|
||||
BilinearFormIntegrator(ir), Q(NULL), DQ(&dq), MQ(NULL), SMQ(NULL) { }
|
||||
CurlCurlIntegrator(MatrixCoefficient &mq, const IntegrationRule *ir = NULL) :
|
||||
BilinearFormIntegrator(ir), MQ(&mq) { Q = NULL; DQ = NULL; }
|
||||
BilinearFormIntegrator(ir), Q(NULL), DQ(NULL), MQ(&mq), SMQ(NULL) { }
|
||||
CurlCurlIntegrator(SymmetricMatrixCoefficient &smq,
|
||||
const IntegrationRule *ir = NULL) :
|
||||
BilinearFormIntegrator(ir), Q(NULL), DQ(NULL), MQ(NULL), SMQ(&smq) { }
|
||||
|
||||
/* Given a particular Finite Element, compute the
|
||||
element curl-curl matrix elmat */
|
||||
@@ -2411,8 +2426,9 @@ public:
|
||||
class VectorFEMassIntegrator: public BilinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
void Init(Coefficient *q, VectorCoefficient *vq, MatrixCoefficient *mq)
|
||||
{ Q = q; VQ = vq; MQ = mq; }
|
||||
void Init(Coefficient *q, DiagonalMatrixCoefficient *dq, MatrixCoefficient *mq,
|
||||
SymmetricMatrixCoefficient *smq)
|
||||
{ Q = q; DQ = dq; MQ = mq; SMQ = smq; }
|
||||
|
||||
#ifndef MFEM_THREAD_SAFE
|
||||
Vector shape;
|
||||
@@ -2425,8 +2441,9 @@ private:
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
DiagonalMatrixCoefficient *DQ;
|
||||
MatrixCoefficient *MQ;
|
||||
SymmetricMatrixCoefficient *SMQ;
|
||||
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
@@ -2439,13 +2456,15 @@ protected:
|
||||
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
|
||||
|
||||
public:
|
||||
VectorFEMassIntegrator() { Init(NULL, NULL, NULL); }
|
||||
VectorFEMassIntegrator(Coefficient *_q) { Init(_q, NULL, NULL); }
|
||||
VectorFEMassIntegrator(Coefficient &q) { Init(&q, NULL, NULL); }
|
||||
VectorFEMassIntegrator(VectorCoefficient *_vq) { Init(NULL, _vq, NULL); }
|
||||
VectorFEMassIntegrator(VectorCoefficient &vq) { Init(NULL, &vq, NULL); }
|
||||
VectorFEMassIntegrator(MatrixCoefficient *_mq) { Init(NULL, NULL, _mq); }
|
||||
VectorFEMassIntegrator(MatrixCoefficient &mq) { Init(NULL, NULL, &mq); }
|
||||
VectorFEMassIntegrator() { Init(NULL, NULL, NULL, NULL); }
|
||||
VectorFEMassIntegrator(Coefficient *_q) { Init(_q, NULL, NULL, NULL); }
|
||||
VectorFEMassIntegrator(Coefficient &q) { Init(&q, NULL, NULL, NULL); }
|
||||
VectorFEMassIntegrator(DiagonalMatrixCoefficient *_dq) { Init(NULL, _dq, NULL, NULL); }
|
||||
VectorFEMassIntegrator(DiagonalMatrixCoefficient &dq) { Init(NULL, &dq, NULL, NULL); }
|
||||
VectorFEMassIntegrator(MatrixCoefficient *_mq) { Init(NULL, NULL, _mq, NULL); }
|
||||
VectorFEMassIntegrator(MatrixCoefficient &mq) { Init(NULL, NULL, &mq, NULL); }
|
||||
VectorFEMassIntegrator(SymmetricMatrixCoefficient &smq) { Init(NULL, NULL, NULL, &smq); }
|
||||
VectorFEMassIntegrator(SymmetricMatrixCoefficient *smq) { Init(NULL, NULL, NULL, smq); }
|
||||
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Trans,
|
||||
@@ -2654,11 +2673,28 @@ public:
|
||||
/** Integrator for the DG form:
|
||||
alpha < rho_u (u.n) {v},[w] > + beta < rho_u |u.n| [v],[w] >,
|
||||
where v and w are the trial and test variables, respectively, and rho/u are
|
||||
given scalar/vector coefficients. The vector coefficient, u, is assumed to
|
||||
be continuous across the faces and when given the scalar coefficient, rho,
|
||||
is assumed to be discontinuous. The integrator uses the upwind value of rho,
|
||||
rho_u, which is value from the side into which the vector coefficient, u,
|
||||
points. */
|
||||
given scalar/vector coefficients. {v} represents the average value of v on
|
||||
the face and [v] is the jump such that {v}=(v1+v2)/2 and [v]=(v1-v2) for the
|
||||
face between elements 1 and 2. For boundary elements, v2=0. The vector
|
||||
coefficient, u, is assumed to be continuous across the faces and when given
|
||||
the scalar coefficient, rho, is assumed to be discontinuous. The integrator
|
||||
uses the upwind value of rho, rho_u, which is value from the side into which
|
||||
the vector coefficient, u, points.
|
||||
|
||||
One use case for this integrator is to discretize the operator -u.grad(v)
|
||||
with a DG formulation. The resulting formulation uses the
|
||||
ConvectionIntegrator (with coefficient u, and parameter alpha = -1) and the
|
||||
transpose of the DGTraceIntegrator (with coefficient u, and parameters
|
||||
alpha = 1, beta = -1/2 to use the upwind face flux). This discretization and
|
||||
the handling of the inflow and outflow boundaries is illustrated in Example
|
||||
9/9p.
|
||||
|
||||
Another use case for this integrator is to discretize the operator -div(u v)
|
||||
with a DG formulation. The resulting formulation is conservative and
|
||||
consists of the transpose of the ConvectionIntegrator (with coefficient u,
|
||||
and parameter alpha = 1) plus the DGTraceIntegrator (with coefficient u, and
|
||||
parameters alpha = -1, beta = -1/2 to use the upwind face flux).
|
||||
*/
|
||||
class DGTraceIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
@@ -2752,6 +2788,51 @@ public:
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
/** Integrator for the "BR2" diffusion stabilization term
|
||||
|
||||
sum_e eta (r_e([u]), r_e([v]))
|
||||
|
||||
where r_e is the lifting operator defined on each edge e. The parameter eta
|
||||
can be chosen to be one to obtain a stable discretization. The constructor
|
||||
for this integrator requires the finite element space because the lifting
|
||||
operator depends on the element-wise inverse mass matrix.
|
||||
|
||||
BR2 stands for the second method of Bassi and Rebay:
|
||||
|
||||
- F. Bassi and S. Rebay. A high order discontinuous Galerkin method for
|
||||
compressible turbulent flows. In B. Cockburn, G. E. Karniadakis, and
|
||||
C.-W. Shu, editors, Discontinuous Galerkin Methods, pages 77–88. Springer
|
||||
Berlin Heidelberg, 2000.
|
||||
- D. N. Arnold, F. Brezzi, B. Cockburn, and L. D. Marini. Unified analysis
|
||||
of discontinuous Galerkin methods for elliptic problems. SIAM Journal on
|
||||
Numerical Analysis, 39(5):1749–1779, 2002.
|
||||
*/
|
||||
class DGDiffusionBR2Integrator : public BilinearFormIntegrator
|
||||
{
|
||||
protected:
|
||||
double eta;
|
||||
|
||||
// Block factorizations of local mass matrices, with offsets for the case of
|
||||
// not equally sized blocks (mixed meshes, p-refinement)
|
||||
Array<double> Minv;
|
||||
Array<int> ipiv;
|
||||
Array<int> ipiv_offsets, Minv_offsets;
|
||||
|
||||
Vector shape1, shape2;
|
||||
|
||||
DenseMatrix R11, R12, R21, R22;
|
||||
DenseMatrix MinvR11, MinvR12, MinvR21, MinvR22;
|
||||
DenseMatrix Re, MinvRe;
|
||||
|
||||
public:
|
||||
DGDiffusionBR2Integrator(class FiniteElementSpace *fes, double e = 1.0);
|
||||
using BilinearFormIntegrator::AssembleFaceMatrix;
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
};
|
||||
|
||||
/** Integrator for the DG elasticity form, for the formulations see:
|
||||
- PhD Thesis of Jonas De Basabe, High-Order Finite %Element Methods for
|
||||
Seismic Wave Propagation, UT Austin, 2009, p. 23, and references therein
|
||||
@@ -2910,11 +2991,36 @@ class DiscreteInterpolator : public BilinearFormIntegrator { };
|
||||
class GradientInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
public:
|
||||
GradientInterpolator() : dofquad_fe(NULL) { }
|
||||
virtual ~GradientInterpolator() { delete dofquad_fe; }
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &h1_fe,
|
||||
const FiniteElement &nd_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{ nd_fe.ProjectGrad(h1_fe, Trans, elmat); }
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
|
||||
/** @brief Setup method for PA data.
|
||||
|
||||
@param[in] trial_fes H1 Lagrange space
|
||||
@param[in] test_fes H(curl) Nedelec space
|
||||
*/
|
||||
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes);
|
||||
|
||||
virtual void AddMultPA(const Vector &x, Vector &y) const;
|
||||
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
|
||||
|
||||
private:
|
||||
/// 1D finite element that generates and owns the 1D DofToQuad maps below
|
||||
FiniteElement * dofquad_fe;
|
||||
|
||||
bool B_id; // is the B basis operator (maps_C_C) the identity?
|
||||
const DofToQuad *maps_C_C; // one-d map with Lobatto rows, Lobatto columns
|
||||
const DofToQuad *maps_O_C; // one-d map with Legendre rows, Lobatto columns
|
||||
int dim, ne, o_dofs1D, c_dofs1D;
|
||||
};
|
||||
|
||||
|
||||
@@ -2929,6 +3035,24 @@ public:
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat)
|
||||
{ ran_fe.Project(dom_fe, Trans, elmat); }
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
|
||||
virtual void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
const FiniteElementSpace &test_fes);
|
||||
|
||||
virtual void AddMultPA(const Vector &x, Vector &y) const;
|
||||
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
|
||||
|
||||
private:
|
||||
/// 1D finite element that generates and owns the 1D DofToQuad maps below
|
||||
FiniteElement * dofquad_fe;
|
||||
|
||||
const DofToQuad *maps_C_C; // one-d map with Lobatto rows, Lobatto columns
|
||||
const DofToQuad *maps_O_C; // one-d map with Legendre rows, Lobatto columns
|
||||
int dim, ne, o_dofs1D, c_dofs1D;
|
||||
|
||||
Vector pa_data;
|
||||
};
|
||||
|
||||
|
||||
@@ -3028,6 +3152,22 @@ protected:
|
||||
VectorCoefficient *VQ;
|
||||
};
|
||||
|
||||
/** Interpolator of the 2D cross product between a vector coefficient and an
|
||||
H(curl)-conforming field onto an L2-conforming field. */
|
||||
class ScalarCrossProductInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
public:
|
||||
ScalarCrossProductInterpolator(VectorCoefficient & vc)
|
||||
: VQ(&vc) { }
|
||||
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &nd_fe,
|
||||
const FiniteElement &l2_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
protected:
|
||||
VectorCoefficient *VQ;
|
||||
};
|
||||
|
||||
/** Interpolator of the cross product between a vector coefficient and an
|
||||
H(curl)-conforming field onto an H(div)-conforming field. The range space
|
||||
can also be vector L2. */
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
// Copyright (c) 2010-2020, 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 "bilininteg.hpp"
|
||||
#include "pfespace.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
DGDiffusionBR2Integrator::DGDiffusionBR2Integrator(FiniteElementSpace *fes,
|
||||
double e) : eta(e)
|
||||
{
|
||||
// Precompute local mass matrix inverses needed for the lifting operators
|
||||
// First compute offsets and total size needed (e.g. for mixed meshes or
|
||||
// p-refinement)
|
||||
int nel = fes->GetNE();
|
||||
Minv_offsets.SetSize(nel+1);
|
||||
ipiv_offsets.SetSize(nel+1);
|
||||
ipiv_offsets[0] = 0;
|
||||
Minv_offsets[0] = 0;
|
||||
for (int i=0; i<nel; ++i)
|
||||
{
|
||||
int dof = fes->GetFE(i)->GetDof();
|
||||
ipiv_offsets[i+1] = ipiv_offsets[i] + dof;
|
||||
Minv_offsets[i+1] = Minv_offsets[i] + dof*dof;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// When running in parallel, we also need to compute the local mass matrices
|
||||
// of face neighbor elements
|
||||
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace *>(fes);
|
||||
if (pfes != NULL)
|
||||
{
|
||||
ParMesh *pmesh = pfes->GetParMesh();
|
||||
pfes->ExchangeFaceNbrData();
|
||||
int nel_nbr = pmesh->GetNFaceNeighborElements();
|
||||
Minv_offsets.SetSize(nel+nel_nbr+1);
|
||||
ipiv_offsets.SetSize(nel+nel_nbr+1);
|
||||
for (int i=0; i<nel_nbr; ++i)
|
||||
{
|
||||
int dof = pfes->GetFaceNbrFE(i)->GetDof();
|
||||
ipiv_offsets[nel+i+1] = ipiv_offsets[nel+i] + dof;
|
||||
Minv_offsets[nel+i+1] = Minv_offsets[nel+i] + dof*dof;
|
||||
}
|
||||
nel += nel_nbr;
|
||||
}
|
||||
#endif
|
||||
// The final "offset" is the total size of all the blocks
|
||||
Minv.SetSize(Minv_offsets[nel]);
|
||||
ipiv.SetSize(ipiv_offsets[nel]);
|
||||
|
||||
// Assemble the local mass matrices and compute LU factorization
|
||||
MassIntegrator mi;
|
||||
for (int i=0; i<nel; ++i)
|
||||
{
|
||||
const FiniteElement *fe = NULL;
|
||||
ElementTransformation *tr = NULL;
|
||||
if (i < fes->GetNE())
|
||||
{
|
||||
fe = fes->GetFE(i);
|
||||
tr = fes->GetElementTransformation(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
int inbr = i - fes->GetNE();
|
||||
fe = pfes->GetFaceNbrFE(inbr);
|
||||
tr = pfes->GetParMesh()->GetFaceNbrElementTransformation(inbr);
|
||||
#endif
|
||||
}
|
||||
int dof = fe->GetDof();
|
||||
double *Minv_el = &Minv[Minv_offsets[i]];
|
||||
int *ipiv_el = &ipiv[ipiv_offsets[i]];
|
||||
DenseMatrix Me(Minv_el, dof, dof);
|
||||
mi.AssembleElementMatrix(*fe, *tr, Me);
|
||||
LUFactors lu(Minv_el, ipiv_el);
|
||||
lu.Factor(dof);
|
||||
}
|
||||
}
|
||||
|
||||
void DGDiffusionBR2Integrator::AssembleFaceMatrix(
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
int ndof1 = el1.GetDof();
|
||||
shape1.SetSize(ndof1);
|
||||
|
||||
R11.SetSize(ndof1, ndof1);
|
||||
R11 = 0.0;
|
||||
LUFactors M1inv(&Minv[Minv_offsets[Trans.Elem1No]],
|
||||
&ipiv[ipiv_offsets[Trans.Elem1No]]);
|
||||
LUFactors M2inv;
|
||||
|
||||
double factor = Geometries.NumBdr(Trans.Elem1->GetGeometryType());
|
||||
|
||||
int ndof2;
|
||||
if (Trans.Elem2No >= 0)
|
||||
{
|
||||
ndof2 = el2.GetDof();
|
||||
shape2.SetSize(ndof2);
|
||||
R12.SetSize(ndof1, ndof2);
|
||||
R21.SetSize(ndof2, ndof1);
|
||||
R22.SetSize(ndof2, ndof2);
|
||||
M2inv.data = &Minv[Minv_offsets[Trans.Elem2No]];
|
||||
M2inv.ipiv = &ipiv[ipiv_offsets[Trans.Elem2No]];
|
||||
|
||||
R12 = 0.0;
|
||||
R21 = 0.0;
|
||||
R22 = 0.0;
|
||||
|
||||
Geometry::Type geom2 = Trans.Elem2->GetGeometryType();
|
||||
factor = std::max(factor, double(Geometries.NumBdr(geom2)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ndof2 = 0;
|
||||
}
|
||||
|
||||
int ndofs = ndof1 + ndof2;
|
||||
|
||||
Re.SetSize(ndofs, ndofs);
|
||||
MinvRe.SetSize(ndofs, ndofs);
|
||||
|
||||
elmat.SetSize(ndofs);
|
||||
elmat = 0.0;
|
||||
|
||||
const IntegrationRule *ir = IntRule;
|
||||
if (ir == NULL)
|
||||
{
|
||||
int order;
|
||||
if (ndof2)
|
||||
{
|
||||
order = 2*std::max(el1.GetOrder(), el2.GetOrder());
|
||||
}
|
||||
else
|
||||
{
|
||||
order = 2*el1.GetOrder();
|
||||
}
|
||||
ir = &IntRules.Get(Trans.FaceGeom, order);
|
||||
}
|
||||
|
||||
for (int p = 0; p < ir->GetNPoints(); p++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(p);
|
||||
IntegrationPoint eip1, eip2;
|
||||
|
||||
Trans.Loc1.Transform(ip, eip1);
|
||||
el1.CalcShape(eip1, shape1);
|
||||
if (ndof2)
|
||||
{
|
||||
Trans.Loc2.Transform(ip, eip2);
|
||||
el2.CalcShape(eip2, shape2);
|
||||
}
|
||||
|
||||
double w = factor*sqrt(eta)*ip.weight*Trans.Face->Weight();
|
||||
if (ndof2)
|
||||
{
|
||||
w /= 2;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ndof1; i++)
|
||||
{
|
||||
const double wsi = w*shape1(i);
|
||||
for (int j = 0; j < ndof1; j++)
|
||||
{
|
||||
R11(i, j) += wsi*shape1(j);
|
||||
}
|
||||
}
|
||||
|
||||
if (ndof2)
|
||||
{
|
||||
for (int i = 0; i < ndof2; i++)
|
||||
{
|
||||
const double wsi = w*shape2(i);
|
||||
for (int j = 0; j < ndof1; j++)
|
||||
{
|
||||
R21(i, j) += wsi*shape1(j);
|
||||
R12(j, i) -= wsi*shape1(j);
|
||||
}
|
||||
for (int j = 0; j < ndof2; j++)
|
||||
{
|
||||
R22(i, j) -= wsi*shape2(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MinvR11 = R11;
|
||||
M1inv.Solve(ndof1, ndof1, MinvR11.Data());
|
||||
for (int i = 0; i < ndof1; i++)
|
||||
{
|
||||
for (int j = 0; j < ndof1; j++)
|
||||
{
|
||||
Re(i, j) = R11(i, j);
|
||||
MinvRe(i, j) = MinvR11(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
if (ndof2)
|
||||
{
|
||||
MinvR12 = R12;
|
||||
MinvR21 = R21;
|
||||
MinvR22 = R22;
|
||||
M1inv.Solve(ndof1, ndof2, MinvR12.Data());
|
||||
M2inv.Solve(ndof2, ndof1, MinvR21.Data());
|
||||
M2inv.Solve(ndof2, ndof2, MinvR22.Data());
|
||||
|
||||
for (int i = 0; i < ndof2; i++)
|
||||
{
|
||||
for (int j = 0; j < ndof1; j++)
|
||||
{
|
||||
Re(ndof1 + i, j) = R21(i, j);
|
||||
MinvRe(ndof1 + i, j) = MinvR21(i, j);
|
||||
|
||||
Re(j, ndof1 + i) = R12(j, i);
|
||||
MinvRe(j, ndof1 + i) = MinvR12(j, i);
|
||||
}
|
||||
for (int j = 0; j < ndof2; j++)
|
||||
{
|
||||
Re(ndof1 + i, ndof1 + j) = R22(i, j);
|
||||
MinvRe(ndof1 + i, ndof1 + j) = MinvR22(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the matrix associated with (r_e([u]), r_e([u])).
|
||||
// The matrix for r_e([u]) is `MinvRe`, and so we need to form the product
|
||||
// `(MinvRe)^T M MinvRe`. Using `Minv^T M = Minv M = I`, we obtain
|
||||
// `Re^T MinvRe`.
|
||||
MultAtB(Re, MinvRe, elmat);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -379,51 +379,54 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
const int MQfullDim = MQ ? MQ->GetHeight() * MQ->GetWidth() : 0;
|
||||
if (MQ)
|
||||
{
|
||||
symmetric = false;
|
||||
MFEM_VERIFY(MQ->GetHeight() == dim && MQ->GetWidth() == dim, "");
|
||||
const int MQsymmDim = MQ->GetWidth() * (MQ->GetWidth() + 1) / 2;
|
||||
|
||||
const int MQdim = MQ->IsSymmetric() ? MQsymmDim : MQfullDim;
|
||||
coeffDim = MQdim;
|
||||
coeffDim = MQfullDim;
|
||||
|
||||
coeff.SetSize(MQdim * nq * ne);
|
||||
symmetric = MQ ? MQ->IsSymmetric() : true;
|
||||
coeff.SetSize(MQfullDim * nq * ne);
|
||||
|
||||
DenseMatrix M;
|
||||
Vector Msymm;
|
||||
if (symmetric)
|
||||
{
|
||||
Msymm.SetSize(MQsymmDim);
|
||||
}
|
||||
else
|
||||
{
|
||||
M.SetSize(dim);
|
||||
}
|
||||
M.SetSize(dim);
|
||||
|
||||
auto C = Reshape(coeff.HostWrite(), MQdim, nq, ne);
|
||||
auto C = Reshape(coeff.HostWrite(), MQfullDim, nq, ne);
|
||||
for (int e=0; e<ne; ++e)
|
||||
{
|
||||
ElementTransformation *tr = mesh->GetElementTransformation(e);
|
||||
for (int p=0; p<nq; ++p)
|
||||
{
|
||||
if (MQ->IsSymmetric())
|
||||
{
|
||||
MQ->EvalSymmetric(Msymm, *tr, ir->IntPoint(p));
|
||||
|
||||
for (int i=0; i<MQsymmDim; ++i)
|
||||
MQ->Eval(M, *tr, ir->IntPoint(p));
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=0; j<dim; ++j)
|
||||
{
|
||||
C(i, p, e) = Msymm[i];
|
||||
C(j+(i*dim), p, e) = M(i,j);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MQ->Eval(M, *tr, ir->IntPoint(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (SMQ)
|
||||
{
|
||||
MFEM_VERIFY(SMQ->GetSize() == dim, "");
|
||||
coeffDim = symmDims;
|
||||
coeff.SetSize(symmDims * nq * ne);
|
||||
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=0; j<dim; ++j)
|
||||
{
|
||||
C(j+(i*dim), p, e) = M(i,j);
|
||||
}
|
||||
}
|
||||
DenseSymmetricMatrix M;
|
||||
M.SetSize(dim);
|
||||
|
||||
auto C = Reshape(coeff.HostWrite(), symmDims, nq, ne);
|
||||
|
||||
for (int e=0; e<ne; ++e)
|
||||
{
|
||||
ElementTransformation *tr = mesh->GetElementTransformation(e);
|
||||
for (int p=0; p<nq; ++p)
|
||||
{
|
||||
SMQ->Eval(M, *tr, ir->IntPoint(p));
|
||||
int cnt = 0;
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=i; j<dim; ++j, ++cnt)
|
||||
{
|
||||
C(cnt, p, e) = M(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ static void PAGradientApplyTranspose2D(const int NE,
|
||||
const int q1d = 0)
|
||||
{
|
||||
// TODO
|
||||
MFEM_ASSERT(false, "GradientPAApplyTranspose 3D not implemented.");
|
||||
MFEM_ASSERT(false, "PAGradientApplyTranspose2D not implemented.");
|
||||
}
|
||||
|
||||
// PA Gradient Apply 3D kernel
|
||||
|
||||
+1952
-37
File diff suppressed because it is too large
Load Diff
@@ -106,7 +106,7 @@ void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
template<const int T_D1D = 0,
|
||||
const int T_Q1D = 0>
|
||||
static void PAVectorMassApply2D(const int NE,
|
||||
const Array<double> &_B,
|
||||
const Array<double> &B_,
|
||||
const Array<double> &_Bt,
|
||||
const Vector &_op,
|
||||
const Vector &_x,
|
||||
@@ -119,7 +119,7 @@ static void PAVectorMassApply2D(const int NE,
|
||||
constexpr int VDIM = 2;
|
||||
MFEM_VERIFY(D1D <= MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
|
||||
auto B = Reshape(_B.Read(), Q1D, D1D);
|
||||
auto B = Reshape(B_.Read(), Q1D, D1D);
|
||||
auto Bt = Reshape(_Bt.Read(), D1D, Q1D);
|
||||
auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
|
||||
auto x = Reshape(_x.Read(), D1D, D1D, VDIM, NE);
|
||||
@@ -203,7 +203,7 @@ static void PAVectorMassApply2D(const int NE,
|
||||
template<const int T_D1D = 0,
|
||||
const int T_Q1D = 0>
|
||||
static void PAVectorMassApply3D(const int NE,
|
||||
const Array<double> &_B,
|
||||
const Array<double> &B_,
|
||||
const Array<double> &_Bt,
|
||||
const Vector &_op,
|
||||
const Vector &_x,
|
||||
@@ -216,7 +216,7 @@ static void PAVectorMassApply3D(const int NE,
|
||||
constexpr int VDIM = 3;
|
||||
MFEM_VERIFY(D1D <= MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
|
||||
auto B = Reshape(_B.Read(), Q1D, D1D);
|
||||
auto B = Reshape(B_.Read(), Q1D, D1D);
|
||||
auto Bt = Reshape(_Bt.Read(), D1D, Q1D);
|
||||
auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
|
||||
auto x = Reshape(_x.Read(), D1D, D1D, D1D, VDIM, NE);
|
||||
@@ -381,7 +381,7 @@ void VectorMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
|
||||
template<const int T_D1D = 0, const int T_Q1D = 0>
|
||||
static void PAVectorMassAssembleDiagonal2D(const int NE,
|
||||
const Array<double> &_B,
|
||||
const Array<double> &B_,
|
||||
const Array<double> &_Bt,
|
||||
const Vector &_op,
|
||||
Vector &_diag,
|
||||
@@ -393,7 +393,7 @@ static void PAVectorMassAssembleDiagonal2D(const int NE,
|
||||
constexpr int VDIM = 2;
|
||||
MFEM_VERIFY(D1D <= MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
|
||||
auto B = Reshape(_B.Read(), Q1D, D1D);
|
||||
auto B = Reshape(B_.Read(), Q1D, D1D);
|
||||
auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
|
||||
auto y = Reshape(_diag.ReadWrite(), D1D, D1D, VDIM, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
@@ -433,7 +433,7 @@ static void PAVectorMassAssembleDiagonal2D(const int NE,
|
||||
|
||||
template<const int T_D1D = 0, const int T_Q1D = 0>
|
||||
static void PAVectorMassAssembleDiagonal3D(const int NE,
|
||||
const Array<double> &_B,
|
||||
const Array<double> &B_,
|
||||
const Array<double> &_Bt,
|
||||
const Vector &_op,
|
||||
Vector &_diag,
|
||||
@@ -445,7 +445,7 @@ static void PAVectorMassAssembleDiagonal3D(const int NE,
|
||||
constexpr int VDIM = 3;
|
||||
MFEM_VERIFY(D1D <= MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= MAX_Q1D, "");
|
||||
auto B = Reshape(_B.Read(), Q1D, D1D);
|
||||
auto B = Reshape(B_.Read(), Q1D, D1D);
|
||||
auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
|
||||
auto y = Reshape(_diag.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
|
||||
MFEM_FORALL(e, NE,
|
||||
|
||||
+33
-40
@@ -761,12 +761,12 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
trial_fetype = trial_el->GetDerivType();
|
||||
test_fetype = test_el->GetDerivType();
|
||||
|
||||
const int MQsymmDim = MQ ? (MQ->GetWidth() * (MQ->GetWidth() + 1)) / 2 : 0;
|
||||
const int MQsymmDim = SMQ ? (SMQ->GetSize() * (SMQ->GetSize() + 1)) / 2 : 0;
|
||||
const int MQfullDim = MQ ? (MQ->GetHeight() * MQ->GetWidth()) : 0;
|
||||
const int MQdim = MQ ? (MQ->IsSymmetric() ? MQsymmDim : MQfullDim) : 0;
|
||||
const int coeffDim = MQ ? MQdim : (VQ ? VQ->GetVDim() : 1);
|
||||
const int MQdim = MQ ? MQfullDim : MQsymmDim;
|
||||
const int coeffDim = (MQ || SMQ) ? MQdim : (DQ ? DQ->GetVDim() : 1);
|
||||
|
||||
symmetric = MQ ? MQ->IsSymmetric() : true;
|
||||
symmetric = (MQ == NULL);
|
||||
|
||||
const bool trial_curl = (trial_fetype == mfem::FiniteElement::CURL);
|
||||
const bool trial_div = (trial_fetype == mfem::FiniteElement::DIV);
|
||||
@@ -783,24 +783,13 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
Vector coeff(coeffDim * ne * nq);
|
||||
coeff = 1.0;
|
||||
auto coeffh = Reshape(coeff.HostWrite(), coeffDim, nq, ne);
|
||||
if (Q || VQ || MQ)
|
||||
if (Q || DQ || MQ || SMQ)
|
||||
{
|
||||
Vector D(VQ ? coeffDim : 0);
|
||||
Vector D(DQ ? coeffDim : 0);
|
||||
DenseMatrix M;
|
||||
Vector Msymm;
|
||||
if (MQ)
|
||||
{
|
||||
if (symmetric)
|
||||
{
|
||||
Msymm.SetSize(MQsymmDim);
|
||||
}
|
||||
else
|
||||
{
|
||||
M.SetSize(dim);
|
||||
}
|
||||
}
|
||||
DenseSymmetricMatrix SM;
|
||||
|
||||
if (VQ)
|
||||
if (DQ)
|
||||
{
|
||||
MFEM_VERIFY(coeffDim == dim, "");
|
||||
}
|
||||
@@ -808,6 +797,12 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
{
|
||||
MFEM_VERIFY(coeffDim == MQdim, "");
|
||||
MFEM_VERIFY(MQ->GetHeight() == dim && MQ->GetWidth() == dim, "");
|
||||
M.SetSize(dim);
|
||||
}
|
||||
if (SMQ)
|
||||
{
|
||||
MFEM_VERIFY(SMQ->GetSize() == dim, "");
|
||||
SM.SetSize(dim);
|
||||
}
|
||||
|
||||
for (int e=0; e<ne; ++e)
|
||||
@@ -817,29 +812,27 @@ void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
{
|
||||
if (MQ)
|
||||
{
|
||||
if (MQ->IsSymmetric())
|
||||
{
|
||||
MQ->EvalSymmetric(Msymm, *tr, ir->IntPoint(p));
|
||||
MQ->Eval(M, *tr, ir->IntPoint(p));
|
||||
|
||||
for (int i=0; i<MQsymmDim; ++i)
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=0; j<dim; ++j)
|
||||
{
|
||||
coeffh(i, p, e) = Msymm[i];
|
||||
coeffh(j+(i*dim), p, e) = M(i,j);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MQ->Eval(M, *tr, ir->IntPoint(p));
|
||||
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=0; j<dim; ++j)
|
||||
{
|
||||
coeffh(j+(i*dim), p, e) = M(i,j);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (VQ)
|
||||
else if (SMQ)
|
||||
{
|
||||
VQ->Eval(D, *tr, ir->IntPoint(p));
|
||||
SMQ->Eval(SM, *tr, ir->IntPoint(p));
|
||||
int cnt = 0;
|
||||
for (int i=0; i<dim; ++i)
|
||||
for (int j=i; j<dim; ++j, ++cnt)
|
||||
{
|
||||
coeffh(cnt, p, e) = SM(i,j);
|
||||
}
|
||||
}
|
||||
else if (DQ)
|
||||
{
|
||||
DQ->Eval(D, *tr, ir->IntPoint(p));
|
||||
for (int i=0; i<coeffDim; ++i)
|
||||
{
|
||||
coeffh(i, p, e) = D[i];
|
||||
@@ -1007,14 +1000,14 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
else if (trial_curl && test_div)
|
||||
{
|
||||
const bool scalarCoeff = !(VQ || MQ);
|
||||
const bool scalarCoeff = !(DQ || MQ || SMQ);
|
||||
PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
|
||||
true, mapsO->B, mapsC->B, mapsOtest->Bt,
|
||||
mapsCtest->Bt, pa_data, x, y);
|
||||
}
|
||||
else if (trial_div && test_curl)
|
||||
{
|
||||
const bool scalarCoeff = !(VQ || MQ);
|
||||
const bool scalarCoeff = !(DQ || MQ || SMQ);
|
||||
PAHcurlHdivMassApply3D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
|
||||
false, mapsO->B, mapsC->B, mapsOtest->Bt,
|
||||
mapsCtest->Bt, pa_data, x, y);
|
||||
@@ -1038,7 +1031,7 @@ void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
else if ((trial_curl && test_div) || (trial_div && test_curl))
|
||||
{
|
||||
const bool scalarCoeff = !(VQ || MQ);
|
||||
const bool scalarCoeff = !(DQ || MQ || SMQ);
|
||||
PAHcurlHdivMassApply2D(dofs1D, dofs1Dtest, quad1D, ne, scalarCoeff,
|
||||
trial_curl, mapsO->B, mapsC->B, mapsOtest->Bt,
|
||||
mapsCtest->Bt, pa_data, x, y);
|
||||
|
||||
+43
-7
@@ -301,7 +301,7 @@ void MatrixFunctionCoefficient::Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
|
||||
K.SetSize(height, width);
|
||||
|
||||
if (symmetric) // Use SymmFunction
|
||||
if (symmetric) // Use SymmFunction (deprecated version)
|
||||
{
|
||||
MFEM_VERIFY(height == width && SymmFunction,
|
||||
"MatrixFunctionCoefficient is not symmetric");
|
||||
@@ -371,6 +371,36 @@ void MatrixFunctionCoefficient::EvalSymmetric(Vector &K,
|
||||
}
|
||||
}
|
||||
|
||||
void SymmetricMatrixFunctionCoefficient::Eval(DenseSymmetricMatrix &K,
|
||||
ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{
|
||||
double x[3];
|
||||
Vector transip(x, 3);
|
||||
|
||||
T.Transform(ip, transip);
|
||||
|
||||
K.SetSize(dim);
|
||||
|
||||
if (Function)
|
||||
{
|
||||
Function(transip, K);
|
||||
}
|
||||
else if (TDFunction)
|
||||
{
|
||||
TDFunction(transip, GetTime(), K);
|
||||
}
|
||||
else
|
||||
{
|
||||
K = mat;
|
||||
}
|
||||
|
||||
if (Q)
|
||||
{
|
||||
K *= Q->Eval(T, ip, GetTime());
|
||||
}
|
||||
}
|
||||
|
||||
MatrixArrayCoefficient::MatrixArrayCoefficient (int dim)
|
||||
: MatrixCoefficient (dim)
|
||||
{
|
||||
@@ -485,32 +515,32 @@ VectorSumCoefficient::VectorSumCoefficient(int dim)
|
||||
}
|
||||
|
||||
VectorSumCoefficient::VectorSumCoefficient(VectorCoefficient &_A,
|
||||
VectorCoefficient &_B,
|
||||
VectorCoefficient &B_,
|
||||
double _alpha, double _beta)
|
||||
: VectorCoefficient(_A.GetVDim()),
|
||||
ACoef(&_A), BCoef(&_B),
|
||||
ACoef(&_A), BCoef(&B_),
|
||||
A(_A.GetVDim()), B(_A.GetVDim()),
|
||||
alphaCoef(NULL), betaCoef(NULL),
|
||||
alpha(_alpha), beta(_beta)
|
||||
{
|
||||
MFEM_ASSERT(_A.GetVDim() == _B.GetVDim(),
|
||||
MFEM_ASSERT(_A.GetVDim() == B_.GetVDim(),
|
||||
"VectorSumCoefficient: "
|
||||
"Arguments must have the same dimension.");
|
||||
}
|
||||
|
||||
VectorSumCoefficient::VectorSumCoefficient(VectorCoefficient &_A,
|
||||
VectorCoefficient &_B,
|
||||
VectorCoefficient &B_,
|
||||
Coefficient &_alpha,
|
||||
Coefficient &_beta)
|
||||
: VectorCoefficient(_A.GetVDim()),
|
||||
ACoef(&_A), BCoef(&_B),
|
||||
ACoef(&_A), BCoef(&B_),
|
||||
A(_A.GetVDim()),
|
||||
B(_A.GetVDim()),
|
||||
alphaCoef(&_alpha),
|
||||
betaCoef(&_beta),
|
||||
alpha(0.0), beta(0.0)
|
||||
{
|
||||
MFEM_ASSERT(_A.GetVDim() == _B.GetVDim(),
|
||||
MFEM_ASSERT(_A.GetVDim() == B_.GetVDim(),
|
||||
"VectorSumCoefficient: "
|
||||
"Arguments must have the same dimension.");
|
||||
}
|
||||
@@ -595,6 +625,7 @@ void MatrixVectorProductCoefficient::Eval(Vector &V, ElementTransformation &T,
|
||||
{
|
||||
a->Eval(ma, T, ip);
|
||||
b->Eval(vb, T, ip);
|
||||
V.SetSize(vdim);
|
||||
ma.Mult(vb, V);
|
||||
}
|
||||
|
||||
@@ -697,6 +728,11 @@ void OuterProductCoefficient::Eval(DenseMatrix &M, ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
CrossCrossCoefficient::CrossCrossCoefficient(double A, VectorCoefficient &K)
|
||||
: MatrixCoefficient(K.GetVDim(), K.GetVDim()), aConst(A), a(NULL), k(&K),
|
||||
vk(K.GetVDim())
|
||||
{}
|
||||
|
||||
CrossCrossCoefficient::CrossCrossCoefficient(Coefficient &A,
|
||||
VectorCoefficient &K)
|
||||
: MatrixCoefficient(K.GetVDim(), K.GetVDim()), aConst(0.0), a(&A), k(&K),
|
||||
|
||||
+124
-19
@@ -688,6 +688,7 @@ public:
|
||||
const IntegrationRule &ir);
|
||||
};
|
||||
|
||||
typedef VectorCoefficient DiagonalMatrixCoefficient;
|
||||
|
||||
/// Base class for Matrix Coefficients that optionally depend on time and space.
|
||||
class MatrixCoefficient
|
||||
@@ -695,7 +696,7 @@ class MatrixCoefficient
|
||||
protected:
|
||||
int height, width;
|
||||
double time;
|
||||
bool symmetric;
|
||||
bool symmetric; // deprecated
|
||||
|
||||
public:
|
||||
/// Construct a dim x dim matrix coefficient.
|
||||
@@ -721,6 +722,7 @@ public:
|
||||
/// For backward compatibility get the width of the matrix.
|
||||
int GetVDim() const { return width; }
|
||||
|
||||
/** @deprecated Use SymmetricMatrixCoefficient instead */
|
||||
bool IsSymmetric() const { return symmetric; }
|
||||
|
||||
/** @brief Evaluate the matrix coefficient in the element described by @a T
|
||||
@@ -731,11 +733,13 @@ public:
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) = 0;
|
||||
|
||||
/// (DEPRECATED) Evaluate a symmetric matrix coefficient.
|
||||
/** @brief Evaluate the upper triangular entries of the matrix coefficient
|
||||
in the symmetric case, similarly to Eval. Matrix entry (i,j) is stored
|
||||
in K[j - i + os_i] for 0 <= i <= j < width, os_0 = 0,
|
||||
os_{i+1} = os_i + width - i. That is, K = {M(0,0), ..., M(0,w-1),
|
||||
M(1,1), ..., M(1,w-1), ..., M(w-1,w-1) with w = width. */
|
||||
M(1,1), ..., M(1,w-1), ..., M(w-1,w-1) with w = width.
|
||||
@deprecated Use Eval() instead. */
|
||||
virtual void EvalSymmetric(Vector &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip)
|
||||
{ mfem_error("MatrixCoefficient::EvalSymmetric"); }
|
||||
@@ -767,7 +771,7 @@ class MatrixFunctionCoefficient : public MatrixCoefficient
|
||||
{
|
||||
private:
|
||||
std::function<void(const Vector &, DenseMatrix &)> Function;
|
||||
std::function<void(const Vector &, Vector &)> SymmFunction;
|
||||
std::function<void(const Vector &, Vector &)> SymmFunction; // deprecated
|
||||
std::function<void(const Vector &, double, DenseMatrix &)> TDFunction;
|
||||
|
||||
Coefficient *Q;
|
||||
@@ -791,6 +795,18 @@ public:
|
||||
: MatrixCoefficient(m.Height(), m.Width()), Q(&q), mat(m)
|
||||
{ }
|
||||
|
||||
/** @brief Define a time-independent symmetric square matrix coefficient from
|
||||
a std function */
|
||||
/** \param dim - the size of the matrix
|
||||
\param SymmF - function used in EvalSymmetric
|
||||
\param q - optional scalar Coefficient to scale the matrix coefficient
|
||||
@deprecated Use another constructor without setting SymmFunction. */
|
||||
MatrixFunctionCoefficient(int dim,
|
||||
std::function<void(const Vector &, Vector &)> SymmF,
|
||||
Coefficient *q = NULL)
|
||||
: MatrixCoefficient(dim, true), SymmFunction(std::move(SymmF)), Q(q), mat(0)
|
||||
{ }
|
||||
|
||||
/// Define a time-dependent square matrix coefficient from a std function
|
||||
/** \param dim - the size of the matrix
|
||||
\param TDF - time-dependent function
|
||||
@@ -801,22 +817,12 @@ public:
|
||||
: MatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
|
||||
{ }
|
||||
|
||||
/** @brief Define a time-independent symmetric square matrix coefficient from
|
||||
a std function */
|
||||
/** \param dim - the size of the matrix
|
||||
\param SymmF - function used in EvalSymmetric
|
||||
\param q - optional scalar Coefficient to scale the matrix coefficient */
|
||||
MatrixFunctionCoefficient(int dim,
|
||||
std::function<void(const Vector &, Vector &)> SymmF,
|
||||
Coefficient *q = NULL)
|
||||
: MatrixCoefficient(dim, true), SymmFunction(std::move(SymmF)), Q(q), mat(0)
|
||||
{ }
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
/// Evaluate the symmetric matrix coefficient at @a ip.
|
||||
/// (DEPRECATED) Evaluate the symmetric matrix coefficient at @a ip.
|
||||
/** @deprecated Use Eval() instead. */
|
||||
virtual void EvalSymmetric(Vector &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
@@ -824,7 +830,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** @brief Matrix coefficient defined by a matrix of scalar coefficients.
|
||||
Coefficients that are not set will evaluate to zero in the vector. The
|
||||
coefficient is stored as a flat Array with indexing (i,j) -> i*width+j. */
|
||||
@@ -940,6 +945,106 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Base class for symmetric matrix coefficients that optionally depend on time and space.
|
||||
class SymmetricMatrixCoefficient
|
||||
{
|
||||
protected:
|
||||
int dim;
|
||||
double time;
|
||||
|
||||
public:
|
||||
/// Construct a dim x dim matrix coefficient.
|
||||
explicit SymmetricMatrixCoefficient(int dimension)
|
||||
{ dim = dimension; time = 0.; }
|
||||
|
||||
/// Set the time for time dependent coefficients
|
||||
void SetTime(double t) { time = t; }
|
||||
|
||||
/// Get the time for time dependent coefficients
|
||||
double GetTime() { return time; }
|
||||
|
||||
/// Get the size of the matrix.
|
||||
int GetSize() const { return dim; }
|
||||
|
||||
/** @brief Evaluate the matrix coefficient in the element described by @a T
|
||||
at the point @a ip, storing the result in @a K. */
|
||||
/** @note When this method is called, the caller must make sure that the
|
||||
IntegrationPoint associated with @a T is the same as @a ip. This can be
|
||||
achieved by calling T.SetIntPoint(&ip). */
|
||||
virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) = 0;
|
||||
|
||||
virtual ~SymmetricMatrixCoefficient() { }
|
||||
};
|
||||
|
||||
|
||||
/// A matrix coefficient that is constant in space and time.
|
||||
class SymmetricMatrixConstantCoefficient : public SymmetricMatrixCoefficient
|
||||
{
|
||||
private:
|
||||
DenseSymmetricMatrix mat;
|
||||
|
||||
public:
|
||||
///Construct using matrix @a m for the constant.
|
||||
SymmetricMatrixConstantCoefficient(const DenseSymmetricMatrix &m)
|
||||
: SymmetricMatrixCoefficient(m.Height()), mat(m) { }
|
||||
using SymmetricMatrixCoefficient::Eval;
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseSymmetricMatrix &M, ElementTransformation &T,
|
||||
const IntegrationPoint &ip) { M = mat; }
|
||||
};
|
||||
|
||||
|
||||
/** @brief A matrix coefficient with an optional scalar coefficient multiplier
|
||||
\a q. The matrix function can either be represented by a std function or
|
||||
a constant matrix provided when constructing this object. */
|
||||
class SymmetricMatrixFunctionCoefficient : public SymmetricMatrixCoefficient
|
||||
{
|
||||
private:
|
||||
std::function<void(const Vector &, DenseSymmetricMatrix &)> Function;
|
||||
std::function<void(const Vector &, double, DenseSymmetricMatrix &)> TDFunction;
|
||||
|
||||
Coefficient *Q;
|
||||
DenseSymmetricMatrix mat;
|
||||
|
||||
public:
|
||||
/// Define a time-independent symmetric matrix coefficient from a std function
|
||||
/** \param dim - the size of the matrix
|
||||
\param F - time-independent function
|
||||
\param q - optional scalar Coefficient to scale the matrix coefficient */
|
||||
SymmetricMatrixFunctionCoefficient(int dim,
|
||||
std::function<void(const Vector &, DenseSymmetricMatrix &)> F,
|
||||
Coefficient *q = nullptr)
|
||||
: SymmetricMatrixCoefficient(dim), Function(std::move(F)), Q(q), mat(0)
|
||||
{ }
|
||||
|
||||
/// Define a constant matrix coefficient times a scalar Coefficient
|
||||
/** \param m - constant matrix
|
||||
\param q - optional scalar Coefficient to scale the matrix coefficient */
|
||||
SymmetricMatrixFunctionCoefficient(const DenseSymmetricMatrix &m,
|
||||
Coefficient &q)
|
||||
: SymmetricMatrixCoefficient(m.Height()), Q(&q), mat(m)
|
||||
{ }
|
||||
|
||||
/// Define a time-dependent square matrix coefficient from a std function
|
||||
/** \param dim - the size of the matrix
|
||||
\param TDF - time-dependent function
|
||||
\param q - optional scalar Coefficient to scale the matrix coefficient */
|
||||
SymmetricMatrixFunctionCoefficient(int dim,
|
||||
std::function<void(const Vector &, double, DenseSymmetricMatrix &)> TDF,
|
||||
Coefficient *q = nullptr)
|
||||
: SymmetricMatrixCoefficient(dim), TDFunction(std::move(TDF)), Q(q)
|
||||
{ }
|
||||
|
||||
/// Evaluate the matrix coefficient at @a ip.
|
||||
virtual void Eval(DenseSymmetricMatrix &K, ElementTransformation &T,
|
||||
const IntegrationPoint &ip);
|
||||
|
||||
virtual ~SymmetricMatrixFunctionCoefficient() { }
|
||||
};
|
||||
|
||||
|
||||
/** @brief Scalar coefficient defined as the product of two scalar coefficients
|
||||
or a scalar and a scalar coefficient. */
|
||||
class ProductCoefficient : public Coefficient
|
||||
@@ -1171,8 +1276,8 @@ public:
|
||||
double _alpha = 1.0, double _beta = 1.0);
|
||||
|
||||
/** Constructor with scalar coefficients.
|
||||
Result is _alpha * _A + _beta * _B */
|
||||
VectorSumCoefficient(VectorCoefficient &_A, VectorCoefficient &_B,
|
||||
Result is _alpha * _A + _beta * B_ */
|
||||
VectorSumCoefficient(VectorCoefficient &_A, VectorCoefficient &B_,
|
||||
Coefficient &_alpha, Coefficient &_beta);
|
||||
|
||||
/// Reset the first vector coefficient
|
||||
@@ -1201,7 +1306,7 @@ public:
|
||||
const Vector & GetA() const { return A; }
|
||||
|
||||
/// Reset the second vector as a constant
|
||||
void SetB(const Vector &_B) { B = _B; BCoef = NULL; }
|
||||
void SetB(const Vector &B_) { B = B_; BCoef = NULL; }
|
||||
/// Return the second vector constant
|
||||
const Vector & GetB() const { return B; }
|
||||
|
||||
|
||||
@@ -1204,6 +1204,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
});
|
||||
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
|
||||
// with standard essential BC treatment
|
||||
ess_tdof_list.HostRead();
|
||||
if (A_i.Type() == Operator::Hypre_ParCSR)
|
||||
{
|
||||
HypreParMatrix * Ah;
|
||||
|
||||
@@ -981,6 +981,11 @@ ConduitDataCollection::SaveRootFile(int num_domains,
|
||||
n_root["file_pattern"] = MeshFilePattern(relay_protocol);
|
||||
n_root["tree_pattern"] = "";
|
||||
|
||||
// Add the time, time step, and cycle
|
||||
n_root["blueprint_index/mesh/state/time"] = time;
|
||||
n_root["blueprint_index/mesh/state/time_step"] = time_step;
|
||||
n_root["blueprint_index/mesh/state/cycle"] = cycle;
|
||||
|
||||
relay::io::save(n_root, RootFileName(), root_proto);
|
||||
}
|
||||
|
||||
|
||||
+21
-20
@@ -110,7 +110,8 @@ void ConvergenceStudy::AddL2Error(GridFunction *gf,
|
||||
|
||||
void ConvergenceStudy::AddGf(GridFunction *gf, Coefficient *scalar_u,
|
||||
VectorCoefficient *grad,
|
||||
Coefficient *ell_coeff, double Nu)
|
||||
Coefficient *ell_coeff,
|
||||
JumpScaling jump_scaling)
|
||||
{
|
||||
cont_type = gf->FESpace()->FEColl()->GetContType();
|
||||
|
||||
@@ -140,7 +141,7 @@ void ConvergenceStudy::AddGf(GridFunction *gf, Coefficient *scalar_u,
|
||||
|
||||
if (cont_type == mfem::FiniteElementCollection::DISCONTINUOUS && ell_coeff)
|
||||
{
|
||||
double DGErr = gf->ComputeDGFaceJumpError(scalar_u,ell_coeff,Nu);
|
||||
double DGErr = gf->ComputeDGFaceJumpError(scalar_u,ell_coeff,jump_scaling);
|
||||
DGFaceErrors.Append(DGErr);
|
||||
// Compute the rate of convergence by:
|
||||
// rate = log (||u - u_h|| / ||u - u_{h/2}||)/log(2)
|
||||
@@ -270,26 +271,26 @@ void ConvergenceStudy::Print(bool relative, std::ostream &out)
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
if (cont_type == 3 && fcounter)
|
||||
}
|
||||
if (cont_type == 3 && fcounter)
|
||||
{
|
||||
out << " -------------------------------------------" << "\n";
|
||||
out << " DG Face Jump Error " << "\n";
|
||||
out << " -------------------------------------------"
|
||||
<< "\n";
|
||||
out << std::right<< std::setw(11)<< "DOFs "<< std::setw(13);
|
||||
out << "Error ";
|
||||
out << std::setw(15) << "Rate " << "\n";
|
||||
out << " -------------------------------------------"
|
||||
<< "\n";
|
||||
out << std::setprecision(4);
|
||||
for (int i =0; i<fcounter; i++)
|
||||
{
|
||||
out << " -------------------------------------------" << "\n";
|
||||
out << " DG Face Jump Error " << "\n";
|
||||
out << " -------------------------------------------"
|
||||
<< "\n";
|
||||
out << std::right<< std::setw(11)<< "DOFs "<< std::setw(13);
|
||||
out << "Error ";
|
||||
out << std::setw(15) << "Rate " << "\n";
|
||||
out << " -------------------------------------------"
|
||||
<< "\n";
|
||||
out << std::setprecision(4);
|
||||
for (int i =0; i<fcounter; i++)
|
||||
{
|
||||
out << std::right << std::setw(10)<< ndofs[i] << std::setw(16)
|
||||
<< std::scientific << DGFaceErrors[i] << std::setw(13)
|
||||
<< std::fixed << DGFaceRates[i] << "\n";
|
||||
}
|
||||
out << "\n";
|
||||
out << std::right << std::setw(10)<< ndofs[i] << std::setw(16)
|
||||
<< std::scientific << DGFaceErrors[i] << std::setw(13)
|
||||
<< std::fixed << DGFaceRates[i] << "\n";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -59,7 +59,8 @@ private:
|
||||
VectorCoefficient *vector_u);
|
||||
void AddGf(GridFunction *gf, Coefficient *scalar_u,
|
||||
VectorCoefficient *grad=nullptr,
|
||||
Coefficient *ell_coeff=nullptr, double Nu=1.0);
|
||||
Coefficient *ell_coeff=nullptr,
|
||||
JumpScaling jump_scaling = {1.0, JumpScaling::ONE_OVER_H});
|
||||
void AddGf(GridFunction *gf, VectorCoefficient *vector_u,
|
||||
VectorCoefficient *curl, Coefficient *div);
|
||||
// returns the L2-norm of scalar_u or vector_u
|
||||
@@ -75,9 +76,10 @@ public:
|
||||
/// DG face jumps parameters
|
||||
void AddL2GridFunction(GridFunction *gf, Coefficient *scalar_u,
|
||||
VectorCoefficient *grad=nullptr,
|
||||
Coefficient *ell_coeff=nullptr, double Nu=1.0)
|
||||
Coefficient *ell_coeff=nullptr,
|
||||
JumpScaling jump_scaling = {1.0, JumpScaling::ONE_OVER_H})
|
||||
{
|
||||
AddGf(gf, scalar_u, grad, ell_coeff, Nu);
|
||||
AddGf(gf, scalar_u, grad, ell_coeff, jump_scaling);
|
||||
}
|
||||
|
||||
/// Add H1 GridFunction, the exact solution and possibly its gradient
|
||||
|
||||
@@ -48,8 +48,373 @@ void L2ZienkiewiczZhuEstimator::ComputeEstimates()
|
||||
current_sequence = solution->FESpace()->GetMesh()->GetSequence();
|
||||
}
|
||||
|
||||
|
||||
KellyErrorEstimator::KellyErrorEstimator(BilinearFormIntegrator& di_,
|
||||
ParGridFunction& sol_,
|
||||
ParFiniteElementSpace& flux_fespace_,
|
||||
const Array<int> &attributes_)
|
||||
: attributes(attributes_)
|
||||
, flux_integrator(&di_)
|
||||
, solution(&sol_)
|
||||
, flux_space(&flux_fespace_)
|
||||
, own_flux_fespace(false)
|
||||
{
|
||||
ResetCoefficientFunctions();
|
||||
}
|
||||
|
||||
KellyErrorEstimator::KellyErrorEstimator(BilinearFormIntegrator& di_,
|
||||
ParGridFunction& sol_,
|
||||
ParFiniteElementSpace* flux_fespace_,
|
||||
const Array<int> &attributes_)
|
||||
: attributes(attributes_)
|
||||
, flux_integrator(&di_)
|
||||
, solution(&sol_)
|
||||
, flux_space(flux_fespace_)
|
||||
, own_flux_fespace(true)
|
||||
{
|
||||
ResetCoefficientFunctions();
|
||||
}
|
||||
|
||||
KellyErrorEstimator::~KellyErrorEstimator()
|
||||
{
|
||||
if (own_flux_fespace)
|
||||
{
|
||||
delete flux_space;
|
||||
}
|
||||
}
|
||||
|
||||
void KellyErrorEstimator::ResetCoefficientFunctions()
|
||||
{
|
||||
compute_element_coefficient = [](ParMesh* pmesh, const int e)
|
||||
{
|
||||
return 1.0;
|
||||
};
|
||||
|
||||
compute_face_coefficient = [](ParMesh* pmesh, const int f,
|
||||
const bool shared_face)
|
||||
{
|
||||
auto FT = [&]()
|
||||
{
|
||||
if (shared_face)
|
||||
{
|
||||
return pmesh->GetSharedFaceTransformations(f);
|
||||
}
|
||||
return pmesh->GetFaceElementTransformations(f);
|
||||
}();
|
||||
const auto order = FT->GetFE()->GetOrder();
|
||||
|
||||
// Poor man's face diameter.
|
||||
double diameter = 0.0;
|
||||
|
||||
Vector p1(pmesh->SpaceDimension());
|
||||
Vector p2(pmesh->SpaceDimension());
|
||||
// NOTE: We have no direct access to vertices for shared faces,
|
||||
// so we fall back to compute the positions from the element.
|
||||
// This can also be modified to compute the diameter for non-linear
|
||||
// geometries by sampling along geometry-specific lines.
|
||||
auto vtx_intrule = Geometries.GetVertices(FT->GetGeometryType());
|
||||
const auto nip = vtx_intrule->GetNPoints();
|
||||
for (int i = 0; i < nip; i++)
|
||||
{
|
||||
// Evaluate flux vector at integration point
|
||||
auto fip1 = vtx_intrule->IntPoint(i);
|
||||
FT->Transform(fip1, p1);
|
||||
|
||||
for (int j = 0; j < nip; j++)
|
||||
{
|
||||
auto fip2 = vtx_intrule->IntPoint(j);
|
||||
FT->Transform(fip2, p2);
|
||||
|
||||
diameter = std::max<double>(diameter, p2.DistanceTo(p1));
|
||||
}
|
||||
}
|
||||
return diameter/(2.0*order);
|
||||
};
|
||||
}
|
||||
|
||||
void KellyErrorEstimator::ComputeEstimates()
|
||||
{
|
||||
// Remarks:
|
||||
// For some context you may have to consult the documentation of
|
||||
// the FaceInfo class [1]. Also, the FaceElementTransformations
|
||||
// documentation [2] may be helpful to grasp what is going on. Note
|
||||
// that the FaceElementTransformations also works in the non-
|
||||
// conforming case to transfer the gauss points from the slave to
|
||||
// the master element.
|
||||
// [1]
|
||||
// https://github.com/mfem/mfem/blob/02d0bfe9c18ce049c3c93a6a4208080fcfc96991/mesh/mesh.hpp#L94
|
||||
// [2]
|
||||
// https://github.com/mfem/mfem/blob/02d0bfe9c18ce049c3c93a6a4208080fcfc96991/fem/eltrans.hpp#L435
|
||||
|
||||
flux_space->Update(false);
|
||||
|
||||
auto xfes = solution->ParFESpace();
|
||||
MFEM_ASSERT(xfes->GetVDim() == 1,
|
||||
"Estimation for vector-valued problems not implemented yet.");
|
||||
auto pmesh = xfes->GetParMesh();
|
||||
|
||||
this->error_estimates.SetSize(xfes->GetNE());
|
||||
this->error_estimates = 0.0;
|
||||
|
||||
// 1. Compute fluxes in discontinuous space
|
||||
ParGridFunction flux(flux_space);
|
||||
flux = 0.0;
|
||||
|
||||
// We pre-sort the array to speed up the search in the following loops.
|
||||
if (attributes.Size())
|
||||
{
|
||||
attributes.Sort();
|
||||
}
|
||||
|
||||
Array<int> xdofs, fdofs;
|
||||
Vector el_x, el_f;
|
||||
for (int e = 0; e < xfes->GetNE(); e++)
|
||||
{
|
||||
auto attr = xfes->GetAttribute(e);
|
||||
if (attributes.Size() && attributes.FindSorted(attr) == -1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
xfes->GetElementVDofs(e, xdofs);
|
||||
solution->GetSubVector(xdofs, el_x);
|
||||
|
||||
ElementTransformation* Transf = xfes->GetElementTransformation(e);
|
||||
flux_integrator->ComputeElementFlux(*xfes->GetFE(e), *Transf, el_x,
|
||||
*flux_space->GetFE(e), el_f, true);
|
||||
|
||||
flux_space->GetElementVDofs(e, fdofs);
|
||||
flux.AddElementVector(fdofs, el_f);
|
||||
}
|
||||
|
||||
// 2. Add error contribution from local interior faces
|
||||
for (int f = 0; f < pmesh->GetNumFaces(); f++)
|
||||
{
|
||||
auto FT = pmesh->GetFaceElementTransformations(f);
|
||||
|
||||
auto &int_rule = IntRules.Get(FT->FaceGeom, 2 * xfes->GetFaceOrder(f));
|
||||
const auto nip = int_rule.GetNPoints();
|
||||
|
||||
if (pmesh->FaceIsInterior(f))
|
||||
{
|
||||
int Inf1, Inf2, NCFace;
|
||||
pmesh->GetFaceInfos(f, &Inf1, &Inf2, &NCFace);
|
||||
|
||||
// Convention
|
||||
// * Conforming face: Face side with smaller element id handles
|
||||
// the integration
|
||||
// * Non-conforming face: The slave handles the integration.
|
||||
// See FaceInfo documentation for details.
|
||||
bool isNCSlave = FT->Elem2No >= 0 && NCFace >= 0;
|
||||
bool isConforming = FT->Elem2No >= 0 && NCFace == -1;
|
||||
if ((FT->Elem1No < FT->Elem2No && isConforming) || isNCSlave)
|
||||
{
|
||||
if (attributes.Size() &&
|
||||
(attributes.FindSorted(FT->Elem1->Attribute) == -1
|
||||
|| attributes.FindSorted(FT->Elem2->Attribute) == -1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IntegrationRule eir;
|
||||
Vector jumps(nip);
|
||||
|
||||
// Integral over local half face on the side of e₁
|
||||
// i.e. the numerical integration of ∫ flux ⋅ n dS₁
|
||||
for (int i = 0; i < nip; i++)
|
||||
{
|
||||
// Evaluate flux at IP
|
||||
auto &fip = int_rule.IntPoint(i);
|
||||
IntegrationPoint ip;
|
||||
FT->Loc1.Transform(fip, ip);
|
||||
|
||||
Vector val(flux_space->GetVDim());
|
||||
flux.GetVectorValue(FT->Elem1No, ip, val);
|
||||
|
||||
// And build scalar product with normal
|
||||
Vector normal(pmesh->SpaceDimension());
|
||||
FT->Face->SetIntPoint(&fip);
|
||||
if (pmesh->Dimension() == pmesh->SpaceDimension())
|
||||
{
|
||||
CalcOrtho(FT->Face->Jacobian(), normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector ref_normal(pmesh->Dimension());
|
||||
FT->Loc1.Transf.SetIntPoint(&fip);
|
||||
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
|
||||
auto &e1 = FT->GetElement1Transformation();
|
||||
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
|
||||
normal /= e1.Weight();
|
||||
}
|
||||
jumps(i) = val * normal * fip.weight * FT->Face->Weight();
|
||||
}
|
||||
|
||||
// Subtract integral over half face of e₂
|
||||
// i.e. the numerical integration of ∫ flux ⋅ n dS₂
|
||||
for (int i = 0; i < nip; i++)
|
||||
{
|
||||
// Evaluate flux vector at IP
|
||||
auto &fip = int_rule.IntPoint(i);
|
||||
IntegrationPoint ip;
|
||||
FT->Loc2.Transform(fip, ip);
|
||||
|
||||
Vector val(flux_space->GetVDim());
|
||||
flux.GetVectorValue(FT->Elem2No, ip, val);
|
||||
|
||||
// And build scalar product with normal
|
||||
Vector normal(pmesh->SpaceDimension());
|
||||
FT->Face->SetIntPoint(&fip);
|
||||
if (pmesh->Dimension() == pmesh->SpaceDimension())
|
||||
{
|
||||
CalcOrtho(FT->Face->Jacobian(), normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector ref_normal(pmesh->Dimension());
|
||||
FT->Loc1.Transf.SetIntPoint(&fip);
|
||||
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
|
||||
auto &e1 = FT->GetElement1Transformation();
|
||||
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
|
||||
normal /= e1.Weight();
|
||||
}
|
||||
|
||||
jumps(i) -= val * normal * fip.weight * FT->Face->Weight();
|
||||
}
|
||||
|
||||
// Finalize "local" L₂ contribution
|
||||
for (int i = 0; i < nip; i++)
|
||||
{
|
||||
jumps(i) *= jumps(i);
|
||||
}
|
||||
auto h_k_face = compute_face_coefficient(pmesh, f, false);
|
||||
double jump_integral = h_k_face*jumps.Sum();
|
||||
|
||||
// A local face is shared between two local elements, so we
|
||||
// can get away with integrating the jump only once and add
|
||||
// it to both elements. To minimize communication, the jump
|
||||
// of shared faces is computed locally by each process.
|
||||
error_estimates(FT->Elem1No) += jump_integral;
|
||||
error_estimates(FT->Elem2No) += jump_integral;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Add error contribution from shared interior faces
|
||||
// Synchronize face data.
|
||||
flux.ExchangeFaceNbrData();
|
||||
|
||||
for (int sf = 0; sf < pmesh->GetNSharedFaces(); sf++)
|
||||
{
|
||||
auto FT = pmesh->GetSharedFaceTransformations(sf, true);
|
||||
if (attributes.Size() &&
|
||||
(attributes.FindSorted(FT->Elem1->Attribute) == -1
|
||||
|| attributes.FindSorted(FT->Elem2->Attribute) == -1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &int_rule = IntRules.Get(FT->FaceGeom, 2 * xfes->GetFaceOrder(0));
|
||||
const auto nip = int_rule.GetNPoints();
|
||||
|
||||
IntegrationRule eir;
|
||||
Vector jumps(nip);
|
||||
|
||||
// Integral over local half face on the side of e₁
|
||||
// i.e. the numerical integration of ∫ flux ⋅ n dS₁
|
||||
for (int i = 0; i < nip; i++)
|
||||
{
|
||||
// Evaluate flux vector at integration point
|
||||
auto &fip = int_rule.IntPoint(i);
|
||||
IntegrationPoint ip;
|
||||
FT->Loc1.Transform(fip, ip);
|
||||
|
||||
Vector val(flux_space->GetVDim());
|
||||
flux.GetVectorValue(FT->Elem1No, ip, val);
|
||||
|
||||
Vector normal(pmesh->SpaceDimension());
|
||||
FT->Face->SetIntPoint(&fip);
|
||||
if (pmesh->Dimension() == pmesh->SpaceDimension())
|
||||
{
|
||||
CalcOrtho(FT->Face->Jacobian(), normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector ref_normal(pmesh->Dimension());
|
||||
FT->Loc1.Transf.SetIntPoint(&fip);
|
||||
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
|
||||
auto &e1 = FT->GetElement1Transformation();
|
||||
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
|
||||
normal /= e1.Weight();
|
||||
}
|
||||
|
||||
jumps(i) = val * normal * fip.weight * FT->Face->Weight();
|
||||
}
|
||||
|
||||
// Subtract integral over non-local half face of e₂
|
||||
// i.e. the numerical integration of ∫ flux ⋅ n dS₂
|
||||
for (int i = 0; i < nip; i++)
|
||||
{
|
||||
// Evaluate flux vector at integration point
|
||||
auto &fip = int_rule.IntPoint(i);
|
||||
IntegrationPoint ip;
|
||||
FT->Loc2.Transform(fip, ip);
|
||||
|
||||
Vector val(flux_space->GetVDim());
|
||||
flux.GetVectorValue(FT->Elem2No, ip, val);
|
||||
|
||||
// Evaluate gauss point
|
||||
Vector normal(pmesh->SpaceDimension());
|
||||
FT->Face->SetIntPoint(&fip);
|
||||
if (pmesh->Dimension() == pmesh->SpaceDimension())
|
||||
{
|
||||
CalcOrtho(FT->Face->Jacobian(), normal);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector ref_normal(pmesh->Dimension());
|
||||
CalcOrtho(FT->Loc1.Transf.Jacobian(), ref_normal);
|
||||
auto &e1 = FT->GetElement1Transformation();
|
||||
e1.AdjugateJacobian().MultTranspose(ref_normal, normal);
|
||||
normal /= e1.Weight();
|
||||
}
|
||||
|
||||
jumps(i) -= val * normal * fip.weight * FT->Face->Weight();
|
||||
}
|
||||
|
||||
// Finalize "local" L₂ contribution
|
||||
for (int i = 0; i < nip; i++)
|
||||
{
|
||||
jumps(i) *= jumps(i);
|
||||
}
|
||||
auto h_k_face = compute_face_coefficient(pmesh, sf, true);
|
||||
double jump_integral = h_k_face*jumps.Sum();
|
||||
|
||||
error_estimates(FT->Elem1No) += jump_integral;
|
||||
// We skip "error_estimates(FT->Elem2No) += jump_integral"
|
||||
// because the error is stored on the remote process and
|
||||
// recomputed there.
|
||||
}
|
||||
|
||||
// Finalize element errors
|
||||
for (int e = 0; e < xfes->GetNE(); e++)
|
||||
{
|
||||
auto factor = compute_element_coefficient(pmesh, e);
|
||||
// The sqrt belongs to the norm and hₑ to the indicator.
|
||||
error_estimates(e) = sqrt(factor * error_estimates(e));
|
||||
}
|
||||
|
||||
current_sequence = solution->FESpace()->GetMesh()->GetSequence();
|
||||
|
||||
// Finish by computing the global error.
|
||||
double process_local_error = error_estimates.Sum();
|
||||
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
|
||||
MPI_SUM, xfes->GetComm());
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
|
||||
void LpErrorEstimator::ComputeEstimates()
|
||||
{
|
||||
MFEM_VERIFY(coef != NULL || vcoef != NULL,
|
||||
@@ -64,6 +429,17 @@ void LpErrorEstimator::ComputeEstimates()
|
||||
{
|
||||
sol->ComputeElementLpErrors(local_norm_p, *vcoef, error_estimates);
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
total_error = error_estimates.Sum();
|
||||
auto pfes = dynamic_cast<ParFiniteElementSpace*>(sol->FESpace());
|
||||
if (pfes)
|
||||
{
|
||||
auto process_local_error = total_error;
|
||||
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
|
||||
MPI_SUM, pfes->GetComm());
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
total_error = pow(total_error, 1.0/local_norm_p);
|
||||
current_sequence = sol->FESpace()->GetMesh()->GetSequence();
|
||||
}
|
||||
|
||||
|
||||
+189
-11
@@ -12,6 +12,8 @@
|
||||
#ifndef MFEM_ERROR_ESTIMATORS
|
||||
#define MFEM_ERROR_ESTIMATORS
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../linalg/vector.hpp"
|
||||
#include "bilinearform.hpp"
|
||||
@@ -39,6 +41,11 @@ public:
|
||||
class ErrorEstimator : public AbstractErrorEstimator
|
||||
{
|
||||
public:
|
||||
/// Return the total error from the last error estimate.
|
||||
/** @note This method is optional for derived classes to override and the
|
||||
base class implementation simply returns 0. */
|
||||
virtual double GetTotalError() const { return 0.0; }
|
||||
|
||||
/// Get a Vector with all element errors.
|
||||
virtual const Vector &GetLocalErrors() = 0;
|
||||
|
||||
@@ -148,8 +155,8 @@ public:
|
||||
own_flux_fes(false)
|
||||
{ }
|
||||
|
||||
/** @brief Consider the coefficient in BilinearFormIntegrator to calculate the
|
||||
fluxes for the error estimator.*/
|
||||
/** @brief Consider the coefficient in BilinearFormIntegrator to calculate
|
||||
the fluxes for the error estimator.*/
|
||||
void SetWithCoeff(bool w_coeff = true) { with_coeff = w_coeff; }
|
||||
|
||||
/** @brief Enable/disable anisotropic estimates. To enable this option, the
|
||||
@@ -166,10 +173,10 @@ public:
|
||||
void SetFluxAveraging(int fa) { flux_averaging = fa; }
|
||||
|
||||
/// Return the total error from the last error estimate.
|
||||
double GetTotalError() const { return total_error; }
|
||||
virtual double GetTotalError() const override { return total_error; }
|
||||
|
||||
/// Get a Vector with all element errors.
|
||||
virtual const Vector &GetLocalErrors()
|
||||
virtual const Vector &GetLocalErrors() override
|
||||
{
|
||||
if (MeshIsModified()) { ComputeEstimates(); }
|
||||
return error_estimates;
|
||||
@@ -178,14 +185,14 @@ public:
|
||||
/** @brief Get an Array<int> with anisotropic flags for all mesh elements.
|
||||
Return an empty array when anisotropic estimates are not available or
|
||||
enabled. */
|
||||
virtual const Array<int> &GetAnisotropicFlags()
|
||||
virtual const Array<int> &GetAnisotropicFlags() override
|
||||
{
|
||||
if (MeshIsModified()) { ComputeEstimates(); }
|
||||
return aniso_flags;
|
||||
}
|
||||
|
||||
/// Reset the error estimator.
|
||||
virtual void Reset() { current_sequence = -1; }
|
||||
virtual void Reset() override { current_sequence = -1; }
|
||||
|
||||
/** @brief Destroy a ZienkiewiczZhuEstimator object. Destroys, if owned, the
|
||||
FiniteElementSpace, flux_space. */
|
||||
@@ -292,17 +299,17 @@ public:
|
||||
void SetLocalErrorNormP(int p) { local_norm_p = p; }
|
||||
|
||||
/// Return the total error from the last error estimate.
|
||||
double GetTotalError() const { return total_error; }
|
||||
virtual double GetTotalError() const override { return total_error; }
|
||||
|
||||
/// Get a Vector with all element errors.
|
||||
virtual const Vector &GetLocalErrors()
|
||||
virtual const Vector &GetLocalErrors() override
|
||||
{
|
||||
if (MeshIsModified()) { ComputeEstimates(); }
|
||||
return error_estimates;
|
||||
}
|
||||
|
||||
/// Reset the error estimator.
|
||||
virtual void Reset() { current_sequence = -1; }
|
||||
virtual void Reset() override { current_sequence = -1; }
|
||||
|
||||
/** @brief Destroy a L2ZienkiewiczZhuEstimator object. Destroys, if owned,
|
||||
the FiniteElementSpace, flux_space. */
|
||||
@@ -314,6 +321,7 @@ public:
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
|
||||
/** @brief The LpErrorEstimator class compares the solution to a known
|
||||
coefficient.
|
||||
|
||||
@@ -332,6 +340,8 @@ protected:
|
||||
int local_norm_p;
|
||||
Vector error_estimates;
|
||||
|
||||
double total_error = 0.0;
|
||||
|
||||
Coefficient * coef;
|
||||
VectorCoefficient * vcoef;
|
||||
GridFunction * sol;
|
||||
@@ -383,10 +393,10 @@ public:
|
||||
void SetCoef(VectorCoefficient &A) { vcoef = &A; }
|
||||
|
||||
/// Reset the error estimator.
|
||||
virtual void Reset() { current_sequence = -1; }
|
||||
virtual void Reset() override { current_sequence = -1; }
|
||||
|
||||
/// Get a Vector with all element errors.
|
||||
virtual const Vector &GetLocalErrors()
|
||||
virtual const Vector &GetLocalErrors() override
|
||||
{
|
||||
if (MeshIsModified()) { ComputeEstimates(); }
|
||||
return error_estimates;
|
||||
@@ -396,6 +406,174 @@ public:
|
||||
virtual ~LpErrorEstimator() {}
|
||||
};
|
||||
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/** @brief The KellyErrorEstimator class provides a fast error indication
|
||||
strategy for smooth scalar parallel problems.
|
||||
|
||||
The Kelly error indicator is based on the following papers:
|
||||
|
||||
Kelly, D. W., et al. "A posteriori error analysis and adaptive processes in
|
||||
the finite element method: Part I—Error analysis." International journal for
|
||||
numerical methods in engineering 19.11 (1983): 1593-1619.
|
||||
|
||||
De SR Gago, J. P., et al. "A posteriori error analysis and adaptive
|
||||
processes in the finite element method: Part II—Adaptive mesh refinement."
|
||||
International journal for numerical methods in engineering 19.11 (1983):
|
||||
1621-1656.
|
||||
|
||||
It can be roughly described by:
|
||||
||∇(u-uₕ)||ₑ ≦ √( C hₑ ∑ₖ (hₖ ∫ |J[∇uₕ]|²) dS )
|
||||
where "e" denotes an element, ||⋅||ₑ the corresponding local norm and k the
|
||||
corresponding faces. u is the analytic solution and uₕ the discretized
|
||||
solution. hₖ and hₑ are factors dependend on the face and element geometry.
|
||||
J is the jump function, i.e. the difference between the limits at each point
|
||||
for each side of the face. A custom method to compute hₖ can be provided. It
|
||||
is also possible to estimate the error only on a subspace by feeding this
|
||||
class an attribute array describing the subspace.
|
||||
|
||||
@note This algorithm is only for Poisson problems a proper error esimator.
|
||||
The current implementation does not reflect this, because the "C" factor is
|
||||
not included.
|
||||
It further assumes that the approximation error at the boundary is small
|
||||
enough, as the implementation ignores boundary faces.
|
||||
*/
|
||||
class KellyErrorEstimator final : public ErrorEstimator
|
||||
{
|
||||
public:
|
||||
/// Function type to compute the local coefficient hₑ of an element.
|
||||
using ElementCoefficientFunction =
|
||||
std::function<double(ParMesh*, const int)>;
|
||||
/** @brief Function type to compute the local coefficient hₖ of a face. The
|
||||
third argument is true for shared faces and false for local faces. */
|
||||
using FaceCoefficientFunction =
|
||||
std::function<double(ParMesh*, const int, const bool)>;
|
||||
|
||||
private:
|
||||
int current_sequence = -1;
|
||||
|
||||
Vector error_estimates;
|
||||
|
||||
double total_error = 0.0;
|
||||
|
||||
Array<int> attributes;
|
||||
|
||||
/** @brief A method to compute hₑ on per-element basis.
|
||||
|
||||
This method weights the error approximation on the element level.
|
||||
|
||||
Defaults to hₑ=1.0.
|
||||
*/
|
||||
ElementCoefficientFunction compute_element_coefficient;
|
||||
|
||||
/** @brief A method to compute hₖ on per-face basis.
|
||||
|
||||
This method weights the error approximation on the face level. The
|
||||
background here is that classical Kelly error estimator implementations
|
||||
approximate the geometrical characteristic hₖ with the face diameter,
|
||||
which should be also be a possibility in this implementation.
|
||||
|
||||
Defaults to hₖ=diameter/2p.
|
||||
*/
|
||||
FaceCoefficientFunction compute_face_coefficient;
|
||||
|
||||
BilinearFormIntegrator* flux_integrator; ///< Not owned.
|
||||
ParGridFunction* solution; ///< Not owned.
|
||||
|
||||
ParFiniteElementSpace*
|
||||
flux_space; /**< @brief Ownership based on own_flux_fes. */
|
||||
bool own_flux_fespace; ///< Ownership flag for flux_space.
|
||||
|
||||
/// Check if the mesh of the solution was modified.
|
||||
bool MeshIsModified()
|
||||
{
|
||||
long mesh_sequence = solution->FESpace()->GetMesh()->GetSequence();
|
||||
MFEM_ASSERT(mesh_sequence >= current_sequence,
|
||||
"improper mesh update sequence");
|
||||
return (mesh_sequence > current_sequence);
|
||||
}
|
||||
|
||||
/** @brief Compute the element error estimates.
|
||||
|
||||
Algorithm outline:
|
||||
1. Compute flux field for each element
|
||||
2. Add error contribution from local interior faces
|
||||
3. Add error contribution from shared interior faces
|
||||
4. Finalize by computing hₖ and scale errors.
|
||||
*/
|
||||
void ComputeEstimates();
|
||||
|
||||
public:
|
||||
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
|
||||
@param di_ The bilinearform to compute the interface flux.
|
||||
@param sol_ The solution field whose error is to be estimated.
|
||||
@param flux_fes_ The finite element space for the interface flux.
|
||||
@param attributes_ The attributes of the subdomain(s) for which the
|
||||
error should be estimated. An empty array results in
|
||||
estimating the error over the complete domain.
|
||||
*/
|
||||
KellyErrorEstimator(BilinearFormIntegrator& di_, ParGridFunction& sol_,
|
||||
ParFiniteElementSpace& flux_fes_,
|
||||
const Array<int> &attributes_ = Array<int>());
|
||||
|
||||
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
|
||||
@param di_ The bilinearform to compute the interface flux.
|
||||
@param sol_ The solution field whose error is to be estimated.
|
||||
@param flux_fes_ The finite element space for the interface flux.
|
||||
@param attributes_ The attributes of the subdomain(s) for which the
|
||||
error should be estimated. An empty array results in
|
||||
estimating the error over the complete domain.
|
||||
*/
|
||||
KellyErrorEstimator(BilinearFormIntegrator& di_, ParGridFunction& sol_,
|
||||
ParFiniteElementSpace* flux_fes_,
|
||||
const Array<int> &attributes_ = Array<int>());
|
||||
|
||||
~KellyErrorEstimator();
|
||||
|
||||
/// Get a Vector with all element errors.
|
||||
const Vector& GetLocalErrors() override
|
||||
{
|
||||
if (MeshIsModified())
|
||||
{
|
||||
ComputeEstimates();
|
||||
}
|
||||
return error_estimates;
|
||||
}
|
||||
|
||||
/// Reset the error estimator.
|
||||
void Reset() override { current_sequence = -1; };
|
||||
|
||||
virtual double GetTotalError() const override { return total_error; }
|
||||
|
||||
/** @brief Change the method to compute hₑ on a per-element basis.
|
||||
@param compute_element_coefficient_
|
||||
A function taking a mesh and an element index to
|
||||
compute the local hₑ for the element.
|
||||
*/
|
||||
void SetElementCoefficientFunction(ElementCoefficientFunction
|
||||
compute_element_coefficient_)
|
||||
{
|
||||
compute_element_coefficient = compute_element_coefficient_;
|
||||
}
|
||||
|
||||
/** @brief Change the method to compute hₖ on a per-element basis.
|
||||
@param compute_face_coefficient_
|
||||
A function taking a mesh and a face index to
|
||||
compute the local hₖ for the face.
|
||||
*/
|
||||
void SetFaceCoefficientFunction(
|
||||
FaceCoefficientFunction
|
||||
compute_face_coefficient_)
|
||||
{
|
||||
compute_face_coefficient = compute_face_coefficient_;
|
||||
}
|
||||
|
||||
/// Change the coefficients back to default as described above.
|
||||
void ResetCoefficientFunctions();
|
||||
};
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_ERROR_ESTIMATORS
|
||||
|
||||
+126
-12
@@ -435,7 +435,7 @@ void ScalarFiniteElement::ScalarLocalInterpolation(
|
||||
IntegrationPoint f_ip;
|
||||
|
||||
const int fs = fine_fe.GetDof(), cs = this->GetDof();
|
||||
I.SetSize(fs, cs );
|
||||
I.SetSize(fs, cs);
|
||||
Vector fine_shape(fs), coarse_shape(cs);
|
||||
DenseMatrix fine_mass(fs), fine_coarse_mass(fs, cs); // initialized with 0
|
||||
const int ir_order = GetOrder() + fine_fe.GetOrder();
|
||||
@@ -464,6 +464,44 @@ void ScalarFiniteElement::ScalarLocalInterpolation(
|
||||
}
|
||||
}
|
||||
|
||||
void ScalarFiniteElement::ScalarLocalRestriction(
|
||||
ElementTransformation &Trans, DenseMatrix &R,
|
||||
const ScalarFiniteElement &coarse_fe) const
|
||||
{
|
||||
// General "restriction", defined by L2 projection
|
||||
double v[Geometry::MaxDim];
|
||||
Vector vv (v, dim);
|
||||
IntegrationPoint f_ip;
|
||||
|
||||
const int cs = coarse_fe.GetDof(), fs = this->GetDof();
|
||||
R.SetSize(cs, fs);
|
||||
Vector fine_shape(fs), coarse_shape(cs);
|
||||
DenseMatrix coarse_mass(cs), coarse_fine_mass(cs, fs); // initialized with 0
|
||||
const int ir_order = GetOrder() + coarse_fe.GetOrder();
|
||||
const IntegrationRule &ir = IntRules.Get(coarse_fe.GetGeomType(), ir_order);
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
this->CalcShape(ip, fine_shape);
|
||||
Trans.Transform(ip, vv);
|
||||
f_ip.Set(v, dim);
|
||||
coarse_fe.CalcShape(f_ip, coarse_shape);
|
||||
|
||||
AddMult_a_VVt(ip.weight, coarse_shape, coarse_mass);
|
||||
AddMult_a_VWt(ip.weight, coarse_shape, fine_shape, coarse_fine_mass);
|
||||
}
|
||||
|
||||
DenseMatrixInverse coarse_mass_inv(coarse_mass);
|
||||
coarse_mass_inv.Mult(coarse_fine_mass, R);
|
||||
|
||||
if (map_type == INTEGRAL)
|
||||
{
|
||||
// assuming Trans is linear; this should be ok for all refinement types
|
||||
Trans.SetIntPoint(&Geometries.GetCenter(geom_type));
|
||||
R *= 1.0 / Trans.Weight();
|
||||
}
|
||||
}
|
||||
const DofToQuad &ScalarFiniteElement::GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const
|
||||
{
|
||||
@@ -558,17 +596,22 @@ void NodalFiniteElement::ProjectCurl_2D(
|
||||
const FiniteElement &fe, ElementTransformation &Trans,
|
||||
DenseMatrix &curl) const
|
||||
{
|
||||
MFEM_ASSERT(GetMapType() == FiniteElement::INTEGRAL, "");
|
||||
|
||||
DenseMatrix curl_shape(fe.GetDof(), 1);
|
||||
|
||||
curl.SetSize(dof, fe.GetDof());
|
||||
for (int i = 0; i < dof; i++)
|
||||
{
|
||||
fe.CalcCurlShape(Nodes.IntPoint(i), curl_shape);
|
||||
|
||||
double w = 1.0;
|
||||
if (GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
Trans.SetIntPoint(&Nodes.IntPoint(i));
|
||||
w /= Trans.Weight();
|
||||
}
|
||||
for (int j = 0; j < fe.GetDof(); j++)
|
||||
{
|
||||
curl(i,j) = curl_shape(j,0);
|
||||
curl(i,j) = w * curl_shape(j,0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -684,17 +727,34 @@ void NodalFiniteElement::Project(
|
||||
{
|
||||
if (fe.GetRangeType() == SCALAR)
|
||||
{
|
||||
MFEM_ASSERT(map_type == fe.GetMapType(), "");
|
||||
|
||||
Vector shape(fe.GetDof());
|
||||
|
||||
I.SetSize(dof, fe.GetDof());
|
||||
for (int k = 0; k < dof; k++)
|
||||
if (map_type == fe.GetMapType())
|
||||
{
|
||||
fe.CalcShape(Nodes.IntPoint(k), shape);
|
||||
for (int j = 0; j < shape.Size(); j++)
|
||||
for (int k = 0; k < dof; k++)
|
||||
{
|
||||
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
|
||||
fe.CalcShape(Nodes.IntPoint(k), shape);
|
||||
for (int j = 0; j < shape.Size(); j++)
|
||||
{
|
||||
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int k = 0; k < dof; k++)
|
||||
{
|
||||
Trans.SetIntPoint(&Nodes.IntPoint(k));
|
||||
fe.CalcPhysShape(Trans, shape);
|
||||
if (map_type == INTEGRAL)
|
||||
{
|
||||
shape *= Trans.Weight();
|
||||
}
|
||||
for (int j = 0; j < shape.Size(); j++)
|
||||
{
|
||||
I(k,j) = (fabs(shape(j)) < 1e-12) ? 0.0 : shape(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -993,6 +1053,8 @@ void VectorFiniteElement::Project_RT(
|
||||
|
||||
fe.CalcShape(ip, shape);
|
||||
Trans.SetIntPoint(&ip);
|
||||
// Transform RT face normals from reference to physical space
|
||||
// vk = adj(J)^T nk
|
||||
Trans.AdjugateJacobian().MultTranspose(nk + d2n[k]*dim, vk);
|
||||
if (fe.GetMapType() == INTEGRAL)
|
||||
{
|
||||
@@ -1010,6 +1072,8 @@ void VectorFiniteElement::Project_RT(
|
||||
{
|
||||
s = 0.0;
|
||||
}
|
||||
// Project scalar basis function multiplied by each coordinate
|
||||
// direction onto the transformed face normals
|
||||
for (int d = 0; d < sdim; d++)
|
||||
{
|
||||
I(k,j+d*shape.Size()) = s*vk[d];
|
||||
@@ -1019,7 +1083,31 @@ void VectorFiniteElement::Project_RT(
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("VectorFiniteElement::Project_RT (fe version)");
|
||||
int sdim = Trans.GetSpaceDim();
|
||||
double vk[Geometry::MaxDim];
|
||||
DenseMatrix vshape(fe.GetDof(), sdim);
|
||||
Vector vshapenk(fe.GetDof());
|
||||
const bool square_J = (dim == sdim);
|
||||
|
||||
I.SetSize(dof, fe.GetDof());
|
||||
for (int k = 0; k < dof; k++)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(k);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
// Transform RT face normals from reference to physical space
|
||||
// vk = adj(J)^T nk
|
||||
Trans.AdjugateJacobian().MultTranspose(nk + d2n[k]*dim, vk);
|
||||
// Compute fe basis functions in physical space
|
||||
fe.CalcVShape(Trans, vshape);
|
||||
// Project fe basis functions onto transformed face normals
|
||||
vshape.Mult(vk, vshapenk);
|
||||
if (!square_J) { vshapenk /= Trans.Weight(); }
|
||||
for (int j=0; j<vshapenk.Size(); j++)
|
||||
{
|
||||
I(k,j) = vshapenk(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1180,6 +1268,8 @@ void VectorFiniteElement::Project_ND(
|
||||
|
||||
fe.CalcShape(ip, shape);
|
||||
Trans.SetIntPoint(&ip);
|
||||
// Transform ND edge tengents from reference to physical space
|
||||
// vk = J tk
|
||||
Trans.Jacobian().Mult(tk + d2t[k]*dim, vk);
|
||||
if (fe.GetMapType() == INTEGRAL)
|
||||
{
|
||||
@@ -1197,6 +1287,8 @@ void VectorFiniteElement::Project_ND(
|
||||
{
|
||||
s = 0.0;
|
||||
}
|
||||
// Project scalar basis function multiplied by each coordinate
|
||||
// direction onto the transformed edge tangents
|
||||
for (int d = 0; d < sdim; d++)
|
||||
{
|
||||
I(k, j + d*shape.Size()) = s*vk[d];
|
||||
@@ -1206,7 +1298,29 @@ void VectorFiniteElement::Project_ND(
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem_error("VectorFiniteElement::Project_ND (fe version)");
|
||||
int sdim = Trans.GetSpaceDim();
|
||||
double vk[Geometry::MaxDim];
|
||||
DenseMatrix vshape(fe.GetDof(), sdim);
|
||||
Vector vshapetk(fe.GetDof());
|
||||
|
||||
I.SetSize(dof, fe.GetDof());
|
||||
for (int k = 0; k < dof; k++)
|
||||
{
|
||||
const IntegrationPoint &ip = Nodes.IntPoint(k);
|
||||
|
||||
Trans.SetIntPoint(&ip);
|
||||
// Transform ND edge tangents from reference to physical space
|
||||
// vk = J tk
|
||||
Trans.Jacobian().Mult(tk + d2t[k]*dim, vk);
|
||||
// Compute fe basis functions in physical space
|
||||
fe.CalcVShape(Trans, vshape);
|
||||
// Project fe basis functions onto transformed edge tangents
|
||||
vshape.Mult(vk, vshapetk);
|
||||
for (int j=0; j<vshapetk.Size(); j++)
|
||||
{
|
||||
I(k, j) = vshapetk(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+74
-1
@@ -504,12 +504,18 @@ public:
|
||||
/** @brief Given a coefficient and a transformation, compute its projection
|
||||
(approximation) in the local finite dimensional space in terms
|
||||
of the degrees of freedom. */
|
||||
/** The approximation used to project is usually local interpolation of
|
||||
degrees of freedom. The derived class could use other methods not
|
||||
implemented yet, e.g. local L2 projection. */
|
||||
virtual void Project(Coefficient &coeff,
|
||||
ElementTransformation &Trans, Vector &dofs) const;
|
||||
|
||||
/** @brief Given a vector coefficient and a transformation, compute its
|
||||
projection (approximation) in the local finite dimensional space
|
||||
in terms of the degrees of freedom. (VectorFiniteElements) */
|
||||
/** The approximation used to project is usually local interpolation of
|
||||
degrees of freedom. The derived class could use other methods not
|
||||
implemented yet, e.g. local L2 projection. */
|
||||
virtual void Project(VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const;
|
||||
|
||||
@@ -666,7 +672,7 @@ public:
|
||||
const ScalarFiniteElement &fine_fe) const;
|
||||
|
||||
/** @brief Get matrix @a I "Interpolation" defined through local
|
||||
L2-projection in the space defined by the @a fine_fe. */
|
||||
L2-projection in the space defined by the @a fine_fe. */
|
||||
/** If the "fine" elements cannot represent all basis functions of the
|
||||
"coarse" element, then boundary values from different sub-elements are
|
||||
generally different. */
|
||||
@@ -674,6 +680,15 @@ public:
|
||||
DenseMatrix &I,
|
||||
const ScalarFiniteElement &fine_fe) const;
|
||||
|
||||
/** @brief Get restriction matrix @a R defined through local L2-projection
|
||||
in the space defined by the @a coarse_fe. */
|
||||
/** If the "fine" elements cannot represent all basis functions of the
|
||||
"coarse" element, then boundary values from different sub-elements are
|
||||
generally different. */
|
||||
void ScalarLocalRestriction(ElementTransformation &Trans,
|
||||
DenseMatrix &R,
|
||||
const ScalarFiniteElement &coarse_fe) const;
|
||||
|
||||
virtual const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
|
||||
DofToQuad::Mode mode) const;
|
||||
};
|
||||
@@ -754,6 +769,10 @@ public:
|
||||
DenseMatrix &I) const
|
||||
{ ScalarLocalInterpolation(Trans, I, *this); }
|
||||
|
||||
virtual void GetLocalRestriction(ElementTransformation &Trans,
|
||||
DenseMatrix &R) const
|
||||
{ ScalarLocalRestriction(Trans, R, *this); }
|
||||
|
||||
virtual void GetTransferMatrix(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &I) const
|
||||
@@ -800,11 +819,25 @@ protected:
|
||||
void CalcVShape_ND(ElementTransformation &Trans,
|
||||
DenseMatrix &shape) const;
|
||||
|
||||
/** @brief Project a vector coefficient onto the RT basis functions
|
||||
@param nk Face normal vectors for this element type
|
||||
@param d2n Offset into nk for each degree of freedom
|
||||
@param vc Vector coefficient to be projected
|
||||
@param Trans Transformation from reference to physical coordinates
|
||||
@param dofs Expansion coefficients for the approximation of vc
|
||||
*/
|
||||
void Project_RT(const double *nk, const Array<int> &d2n,
|
||||
VectorCoefficient &vc, ElementTransformation &Trans,
|
||||
Vector &dofs) const;
|
||||
|
||||
/// Projects the vector of values given at FE nodes to RT space
|
||||
/** Project vector values onto the RT basis functions
|
||||
@param nk Face normal vectors for this element type
|
||||
@param d2n Offset into nk for each degree of freedom
|
||||
@param vc Vector values at each interpolation point
|
||||
@param Trans Transformation from reference to physical coordinates
|
||||
@param dofs Expansion coefficients for the approximation of vc
|
||||
*/
|
||||
void Project_RT(const double *nk, const Array<int> &d2n,
|
||||
Vector &vc, ElementTransformation &Trans,
|
||||
Vector &dofs) const;
|
||||
@@ -814,6 +847,19 @@ protected:
|
||||
const double *nk, const Array<int> &d2n,
|
||||
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const;
|
||||
|
||||
/** @brief Project vector-valued basis functions onto the RT basis functions
|
||||
@param nk Face normal vectors for this element type
|
||||
@param d2n Offset into nk for each degree of freedom
|
||||
@param fe Vector-valued finite element basis
|
||||
@param Trans Transformation from reference to physical coordinates
|
||||
@param I Expansion coefficients for the approximation of each basis
|
||||
function
|
||||
|
||||
Note: If the FiniteElement, fe, is scalar-valued the projection will
|
||||
assume that a FiniteElementSpace is being used to define a vector
|
||||
field using the scalar basis functions for each component of the
|
||||
vector field.
|
||||
*/
|
||||
void Project_RT(const double *nk, const Array<int> &d2n,
|
||||
const FiniteElement &fe, ElementTransformation &Trans,
|
||||
DenseMatrix &I) const;
|
||||
@@ -833,11 +879,25 @@ protected:
|
||||
const FiniteElement &fe, ElementTransformation &Trans,
|
||||
DenseMatrix &curl) const;
|
||||
|
||||
/** @brief Project a vector coefficient onto the ND basis functions
|
||||
@param tk Edge tangent vectors for this element type
|
||||
@param d2t Offset into tk for each degree of freedom
|
||||
@param vc Vector coefficient to be projected
|
||||
@param Trans Transformation from reference to physical coordinates
|
||||
@param dofs Expansion coefficients for the approximation of vc
|
||||
*/
|
||||
void Project_ND(const double *tk, const Array<int> &d2t,
|
||||
VectorCoefficient &vc, ElementTransformation &Trans,
|
||||
Vector &dofs) const;
|
||||
|
||||
/// Projects the vector of values given at FE nodes to ND space
|
||||
/** Project vector values onto the ND basis functions
|
||||
@param tk Edge tangent vectors for this element type
|
||||
@param d2t Offset into tk for each degree of freedom
|
||||
@param vc Vector values at each interpolation point
|
||||
@param Trans Transformation from reference to physical coordinates
|
||||
@param dofs Expansion coefficients for the approximation of vc
|
||||
*/
|
||||
void Project_ND(const double *tk, const Array<int> &d2t,
|
||||
Vector &vc, ElementTransformation &Trans,
|
||||
Vector &dofs) const;
|
||||
@@ -847,6 +907,19 @@ protected:
|
||||
const double *tk, const Array<int> &d2t,
|
||||
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const;
|
||||
|
||||
/** @brief Project vector-valued basis functions onto the ND basis functions
|
||||
@param tk Edge tangent vectors for this element type
|
||||
@param d2t Offset into tk for each degree of freedom
|
||||
@param fe Vector-valued finite element basis
|
||||
@param Trans Transformation from reference to physical coordinates
|
||||
@param I Expansion coefficients for the approximation of each basis
|
||||
function
|
||||
|
||||
Note: If the FiniteElement, fe, is scalar-valued the projection will
|
||||
assume that a FiniteElementSpace is being used to define a vector
|
||||
field using the scalar basis functions for each component of the
|
||||
vector field.
|
||||
*/
|
||||
void Project_ND(const double *tk, const Array<int> &d2t,
|
||||
const FiniteElement &fe, ElementTransformation &Trans,
|
||||
DenseMatrix &I) const;
|
||||
|
||||
+2
-1
@@ -2608,9 +2608,9 @@ const Operator &GridTransfer::MakeTrueOperator(
|
||||
else // Parallel() == true
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
const SparseMatrix *out_R = fes_out.GetRestrictionMatrix();
|
||||
if (oper_type == Operator::Hypre_ParCSR)
|
||||
{
|
||||
const SparseMatrix *out_R = fes_out.GetRestrictionMatrix();
|
||||
const ParFiniteElementSpace *pfes_in =
|
||||
dynamic_cast<const ParFiniteElementSpace *>(&fes_in);
|
||||
const ParFiniteElementSpace *pfes_out =
|
||||
@@ -2638,6 +2638,7 @@ const Operator &GridTransfer::MakeTrueOperator(
|
||||
}
|
||||
else if (oper_type == Operator::ANY_TYPE)
|
||||
{
|
||||
const Operator *out_R = fes_out.GetRestrictionOperator();
|
||||
t_oper.Reset(new TripleProductOperator(
|
||||
out_R, &oper, fes_in.GetProlongationMatrix(),
|
||||
false, false, false));
|
||||
|
||||
+16
-1
@@ -330,6 +330,18 @@ public:
|
||||
virtual const Operator *GetProlongationMatrix() const
|
||||
{ return GetConformingProlongation(); }
|
||||
|
||||
/// Return an operator that performs the transpose of GetRestrictionOperator
|
||||
/** The returned operator is owned by the FiniteElementSpace. In serial this
|
||||
is the same as GetProlongationMatrix() */
|
||||
virtual const Operator *GetRestrictionTransposeOperator() const
|
||||
{ return GetConformingProlongation(); }
|
||||
|
||||
/// An abstract operator that performs the same action as GetRestrictionMatrix
|
||||
/** In some cases this is an optimized matrix-free implementation. The
|
||||
returned operator is owned by the FiniteElementSpace. */
|
||||
virtual const Operator *GetRestrictionOperator() const
|
||||
{ return GetConformingRestriction(); }
|
||||
|
||||
/// The returned SparseMatrix is owned by the FiniteElementSpace.
|
||||
virtual const SparseMatrix *GetRestrictionMatrix() const
|
||||
{ return GetConformingRestriction(); }
|
||||
@@ -571,7 +583,7 @@ public:
|
||||
|
||||
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
|
||||
associated with i'th element in the mesh object. */
|
||||
const FiniteElement *GetFE(int i) const;
|
||||
virtual const FiniteElement *GetFE(int i) const;
|
||||
|
||||
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
|
||||
associated with i'th boundary face in the mesh object. */
|
||||
@@ -756,6 +768,9 @@ public:
|
||||
/// Return the total number of quadrature points.
|
||||
int GetSize() const { return size; }
|
||||
|
||||
/// Return the order of the quadrature rule(s) used by all elements.
|
||||
int GetOrder() const { return order; }
|
||||
|
||||
/// Returns the mesh
|
||||
inline Mesh *GetMesh() const { return mesh; }
|
||||
|
||||
|
||||
+70
-23
@@ -15,6 +15,10 @@
|
||||
#include "../mesh/nurbs.hpp"
|
||||
#include "../general/text.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pfespace.hpp"
|
||||
#endif
|
||||
|
||||
#include <limits>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
@@ -22,6 +26,7 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -462,15 +467,26 @@ const
|
||||
fes->GetElementDofs(i, dofs);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
MFEM_ASSERT(FElem->GetMapType() == FiniteElement::VALUE,
|
||||
"invalid FE map type");
|
||||
int dof = FElem->GetDof();
|
||||
Vector DofVal(dof), loc_data(dof);
|
||||
GetSubVector(dofs, loc_data);
|
||||
for (int k = 0; k < n; k++)
|
||||
if (FElem->GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
FElem->CalcShape(ir.IntPoint(k), DofVal);
|
||||
vals(k) = DofVal * loc_data;
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
FElem->CalcShape(ir.IntPoint(k), DofVal);
|
||||
vals(k) = DofVal * loc_data;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ElementTransformation *Tr = fes->GetElementTransformation(i);
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
Tr->SetIntPoint(&ir.IntPoint(k));
|
||||
FElem->CalcPhysShape(*Tr, DofVal);
|
||||
vals(k) = DofVal * loc_data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -984,15 +1000,14 @@ void GridFunction::GetVectorValues(ElementTransformation &T,
|
||||
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
MFEM_ASSERT(FElem->GetMapType() == FiniteElement::VALUE,
|
||||
"invalid FE map type");
|
||||
Vector shape(dof);
|
||||
int vdim = fes->GetVDim();
|
||||
vals.SetSize(vdim, nip);
|
||||
for (int j = 0; j < nip; j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(j);
|
||||
FElem->CalcShape(ip, shape);
|
||||
T.SetIntPoint(&ip);
|
||||
FElem->CalcPhysShape(T, shape);
|
||||
|
||||
for (int k = 0; k < vdim; k++)
|
||||
{
|
||||
@@ -1550,18 +1565,16 @@ void GridFunction::GetGradient(ElementTransformation &T, Vector &grad) const
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
{
|
||||
const FiniteElement * fe = fes->GetFE(T.ElementNo);
|
||||
const FiniteElement *fe = fes->GetFE(T.ElementNo);
|
||||
MFEM_ASSERT(fe->GetMapType() == FiniteElement::VALUE,
|
||||
"invalid FE map type");
|
||||
int spaceDim = fes->GetMesh()->SpaceDimension();
|
||||
int dim = fe->GetDim(), dof = fe->GetDof();
|
||||
DenseMatrix dshape(dof, dim);
|
||||
Vector lval, gh(dim);
|
||||
Array<int> dofs;
|
||||
|
||||
grad.SetSize(spaceDim);
|
||||
fes->GetElementDofs(T.ElementNo, dofs);
|
||||
GetSubVector(dofs, lval);
|
||||
GetElementDofValues(T.ElementNo, lval);
|
||||
fe->CalcDShape(T.GetIntPoint(), dshape);
|
||||
dshape.MultTranspose(lval, gh);
|
||||
T.InverseJacobian().MultTranspose(gh, grad);
|
||||
@@ -1731,6 +1744,13 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetElementDofValues(int el, Vector &dof_vals) const
|
||||
{
|
||||
Array<int> dof_idx;
|
||||
fes->GetElementVDofs(el, dof_idx);
|
||||
GetSubVector(dof_idx, dof_vals);
|
||||
}
|
||||
|
||||
void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
{
|
||||
Mesh *mesh = fes->GetMesh();
|
||||
@@ -2777,10 +2797,11 @@ double GridFunction::ComputeDivError(
|
||||
}
|
||||
|
||||
double GridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
Coefficient *ell_coeff, double Nu,
|
||||
Coefficient *ell_coeff,
|
||||
class JumpScaling jump_scaling,
|
||||
const IntegrationRule *irs[]) const
|
||||
{
|
||||
int fdof, dim, intorder, k;
|
||||
int fdof, intorder, k;
|
||||
Mesh *mesh;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *transf;
|
||||
@@ -2791,20 +2812,24 @@ double GridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
double error = 0.0;
|
||||
|
||||
mesh = fes->GetMesh();
|
||||
dim = mesh->Dimension();
|
||||
|
||||
for (int i = 0; i < mesh->GetNumFaces(); i++)
|
||||
{
|
||||
face_elem_transf = mesh->GetFaceElementTransformations(i, 5);
|
||||
int i1 = face_elem_transf->Elem1No;
|
||||
int i2 = face_elem_transf->Elem2No;
|
||||
int i1, i2;
|
||||
mesh->GetFaceElements(i, &i1, &i2);
|
||||
double h = mesh->GetElementSize(i1);
|
||||
intorder = fes->GetFE(i1)->GetOrder();
|
||||
if (i2 >= 0)
|
||||
{
|
||||
if ( (k = fes->GetFE(i2)->GetOrder()) > intorder )
|
||||
{
|
||||
intorder = k;
|
||||
}
|
||||
h = std::min(h, mesh->GetElementSize(i2));
|
||||
}
|
||||
int p = intorder;
|
||||
intorder = 2 * intorder; // <-------------
|
||||
face_elem_transf = mesh->GetFaceElementTransformations(i, 5);
|
||||
const IntegrationRule *ir;
|
||||
if (irs)
|
||||
{
|
||||
@@ -2875,8 +2900,9 @@ double GridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
transf->SetIntPoint(&ip);
|
||||
error += (ip.weight * Nu * ell_coeff_val(j) *
|
||||
pow(transf->Weight(), 1.0-1.0/(dim-1)) *
|
||||
double nu = jump_scaling.Eval(h, p);
|
||||
error += (ip.weight * nu * ell_coeff_val(j) *
|
||||
transf->Weight() *
|
||||
err_val(j) * err_val(j));
|
||||
}
|
||||
}
|
||||
@@ -2884,6 +2910,15 @@ double GridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
return (error < 0.0) ? -sqrt(-error) : sqrt(error);
|
||||
}
|
||||
|
||||
double GridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
Coefficient *ell_coeff,
|
||||
double Nu,
|
||||
const IntegrationRule *irs[]) const
|
||||
{
|
||||
return ComputeDGFaceJumpError(
|
||||
exsol, ell_coeff, {Nu, JumpScaling::ONE_OVER_H}, irs);
|
||||
}
|
||||
|
||||
double GridFunction::ComputeH1Error(Coefficient *exsol,
|
||||
VectorCoefficient *exgrad,
|
||||
Coefficient *ell_coef, double Nu,
|
||||
@@ -2892,7 +2927,11 @@ double GridFunction::ComputeH1Error(Coefficient *exsol,
|
||||
double error1 = 0.0;
|
||||
double error2 = 0.0;
|
||||
if (norm_type & 1) { error1 = GridFunction::ComputeGradError(exgrad); }
|
||||
if (norm_type & 2) { error2 = GridFunction::ComputeDGFaceJumpError(exsol,ell_coef,Nu); }
|
||||
if (norm_type & 2)
|
||||
{
|
||||
error2 = GridFunction::ComputeDGFaceJumpError(
|
||||
exsol, ell_coef, {Nu, JumpScaling::ONE_OVER_H});
|
||||
}
|
||||
|
||||
return sqrt(error1 * error1 + error2 * error2);
|
||||
}
|
||||
@@ -3670,7 +3709,7 @@ QuadratureFunction & QuadratureFunction::operator=(double value)
|
||||
|
||||
QuadratureFunction & QuadratureFunction::operator=(const Vector &v)
|
||||
{
|
||||
MFEM_ASSERT(qspace && v.Size() == qspace->GetSize(), "");
|
||||
MFEM_ASSERT(qspace && v.Size() == this->Size(), "");
|
||||
Vector::operator=(v);
|
||||
return *this;
|
||||
}
|
||||
@@ -3774,7 +3813,15 @@ double ZZErrorEstimator(BilinearFormIntegrator &blfi,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
auto pfes = dynamic_cast<ParFiniteElementSpace*>(ufes);
|
||||
if (pfes)
|
||||
{
|
||||
auto process_local_error = total_error;
|
||||
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
|
||||
MPI_SUM, pfes->GetComm());
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
return std::sqrt(total_error);
|
||||
}
|
||||
|
||||
|
||||
+59
-3
@@ -325,6 +325,10 @@ public:
|
||||
Both FE spaces should be scalar and on the same mesh. */
|
||||
void GetElementAverages(GridFunction &avgs) const;
|
||||
|
||||
/** Sets the output vector @a dof_vals to the values of the degrees of
|
||||
freedom of element @a el. */
|
||||
virtual void GetElementDofValues(int el, Vector &dof_vals) const;
|
||||
|
||||
/** Impose the given bounds on the function's DOFs while preserving its local
|
||||
* integral (described in terms of the given weights) on the i'th element
|
||||
* through SLBPQ optimization.
|
||||
@@ -345,14 +349,30 @@ public:
|
||||
projection matrix. */
|
||||
void ProjectGridFunction(const GridFunction &src);
|
||||
|
||||
/** @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). */
|
||||
virtual void ProjectCoefficient(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
|
||||
that element. */
|
||||
void ProjectCoefficient(Coefficient &coeff, Array<int> &dofs, int vd = 0);
|
||||
|
||||
/** @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).*/
|
||||
void ProjectCoefficient(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
|
||||
on that element. */
|
||||
void ProjectCoefficient(VectorCoefficient &vcoeff, Array<int> &dofs);
|
||||
|
||||
/** @brief Analogous to the version with argument @a vcoeff VectorCoefficient
|
||||
but using an array of scalar coefficients for each component. */
|
||||
void ProjectCoefficient(Coefficient *coeff[]);
|
||||
|
||||
/** @brief Project a discontinuous vector coefficient as a grid function on
|
||||
@@ -451,13 +471,22 @@ public:
|
||||
virtual double ComputeDivError(Coefficient *exdiv,
|
||||
const IntegrationRule *irs[] = NULL) const;
|
||||
|
||||
/// Returns the Face Jumps error for L2 elements
|
||||
/// Returns the Face Jumps error for L2 elements. The error can be weighted
|
||||
/// by a constant nu, by nu/h, or nu*p^2/h, depending on the value of
|
||||
/// @a jump_scaling.
|
||||
virtual double ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
Coefficient *ell_coeff,
|
||||
double Nu,
|
||||
class JumpScaling jump_scaling,
|
||||
const IntegrationRule *irs[] = NULL)
|
||||
const;
|
||||
|
||||
/// Returns the Face Jumps error for L2 elements, with 1/h scaling.
|
||||
MFEM_DEPRECATED
|
||||
double ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
Coefficient *ell_coeff,
|
||||
double Nu,
|
||||
const IntegrationRule *irs[] = NULL) const;
|
||||
|
||||
/** This method is kept for backward compatibility.
|
||||
|
||||
Returns either the H1-seminorm, or the DG face jumps error, or both
|
||||
@@ -664,6 +693,32 @@ public:
|
||||
derived class ParGridFunction */
|
||||
std::ostream &operator<<(std::ostream &out, const GridFunction &sol);
|
||||
|
||||
/// Class used to specify how the jump terms in
|
||||
/// GridFunction::ComputeDGFaceJumpError are scaled.
|
||||
class JumpScaling
|
||||
{
|
||||
public:
|
||||
enum JumpScalingType
|
||||
{
|
||||
CONSTANT,
|
||||
ONE_OVER_H,
|
||||
P_SQUARED_OVER_H
|
||||
};
|
||||
private:
|
||||
double nu;
|
||||
JumpScalingType type;
|
||||
public:
|
||||
JumpScaling(double nu_=1.0, JumpScalingType type_=CONSTANT)
|
||||
: nu(nu_), type(type_) { }
|
||||
double Eval(double h, int p) const
|
||||
{
|
||||
double val = nu;
|
||||
if (type != CONSTANT) { val /= h; }
|
||||
if (type == P_SQUARED_OVER_H) { val *= p*p; }
|
||||
return val;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** @brief Class representing a function through its values (scalar or vector)
|
||||
at quadrature points. */
|
||||
@@ -752,7 +807,8 @@ public:
|
||||
|
||||
/// Copy the data from @a v.
|
||||
/** The size of @a v must be equal to the size of the associated
|
||||
QuadratureSpace #qspace. */
|
||||
QuadratureSpace #qspace times the QuadratureFunction dimension
|
||||
i.e. QuadratureFunction::Size(). */
|
||||
QuadratureFunction &operator=(const Vector &v);
|
||||
|
||||
/// Copy assignment. Only the data of the base class Vector is copied.
|
||||
|
||||
+1
-1
@@ -10,8 +10,8 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
|
||||
#include <cmath>
|
||||
#include "fem.hpp"
|
||||
#include <cmath>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
+75
-24
@@ -14,11 +14,40 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
Multigrid::Multigrid(const FiniteElementSpaceHierarchy& fespaces_)
|
||||
: fespaces(fespaces_), cycleType(CycleType::VCYCLE), preSmoothingSteps(1),
|
||||
postSmoothingSteps(1)
|
||||
Multigrid::Multigrid()
|
||||
: cycleType(CycleType::VCYCLE), preSmoothingSteps(1), postSmoothingSteps(1)
|
||||
{}
|
||||
|
||||
Multigrid::Multigrid(const Array<Operator*>& operators_,
|
||||
const Array<Solver*>& smoothers_,
|
||||
const Array<Operator*>& prolongations_,
|
||||
const Array<bool>& ownedOperators_,
|
||||
const Array<bool>& ownedSmoothers_,
|
||||
const Array<bool>& ownedProlongations_)
|
||||
: Solver(operators_.Last()->NumRows()), cycleType(CycleType::VCYCLE),
|
||||
preSmoothingSteps(1), postSmoothingSteps(1),
|
||||
X(operators_.Size()), Y(X.Size()), R(X.Size()), Z(X.Size())
|
||||
{
|
||||
operators_.Copy(operators);
|
||||
smoothers_.Copy(smoothers);
|
||||
prolongations_.Copy(prolongations);
|
||||
ownedOperators_.Copy(ownedOperators);
|
||||
ownedSmoothers_.Copy(ownedSmoothers);
|
||||
ownedProlongations_.Copy(ownedProlongations);
|
||||
|
||||
for (int level = 0; level < operators.Size(); ++level)
|
||||
{
|
||||
X[level] = new Vector(operators[level]->NumRows());
|
||||
*X[level] = 0.0;
|
||||
Y[level] = new Vector(operators[level]->NumRows());
|
||||
*Y[level] = 0.0;
|
||||
R[level] = new Vector(operators[level]->NumRows());
|
||||
*R[level] = 0.0;
|
||||
Z[level] = new Vector(operators[level]->NumRows());
|
||||
*Z[level] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
Multigrid::~Multigrid()
|
||||
{
|
||||
for (int i = 0; i < operators.Size(); ++i)
|
||||
@@ -37,26 +66,21 @@ Multigrid::~Multigrid()
|
||||
delete Z[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < prolongations.Size(); ++i)
|
||||
{
|
||||
if (ownedProlongations[i])
|
||||
{
|
||||
delete prolongations[i];
|
||||
}
|
||||
}
|
||||
|
||||
operators.DeleteAll();
|
||||
smoothers.DeleteAll();
|
||||
prolongations.DeleteAll();
|
||||
X.DeleteAll();
|
||||
Y.DeleteAll();
|
||||
R.DeleteAll();
|
||||
Z.DeleteAll();
|
||||
|
||||
for (int i = 0; i < bfs.Size(); ++i)
|
||||
{
|
||||
delete bfs[i];
|
||||
}
|
||||
|
||||
bfs.DeleteAll();
|
||||
|
||||
for (int i = 0; i < essentialTrueDofs.Size(); ++i)
|
||||
{
|
||||
delete essentialTrueDofs[i];
|
||||
}
|
||||
|
||||
essentialTrueDofs.DeleteAll();
|
||||
}
|
||||
|
||||
void Multigrid::AddLevel(Operator* opr, Solver* smoother, bool ownOperator,
|
||||
@@ -168,8 +192,7 @@ void Multigrid::Cycle(int level) const
|
||||
subtract(*X[level], *R[level], *R[level]);
|
||||
|
||||
// Restrict residual
|
||||
fespaces.GetProlongationAtLevel(level - 1)->MultTranspose(*R[level],
|
||||
*X[level - 1]);
|
||||
GetProlongationAtLevel(level - 1)->MultTranspose(*R[level], *X[level - 1]);
|
||||
|
||||
// Init zeros
|
||||
*Y[level - 1] = 0.0;
|
||||
@@ -186,7 +209,7 @@ void Multigrid::Cycle(int level) const
|
||||
}
|
||||
|
||||
// Prolongate
|
||||
fespaces.GetProlongationAtLevel(level - 1)->Mult(*Y[level - 1], *R[level]);
|
||||
GetProlongationAtLevel(level - 1)->Mult(*Y[level - 1], *R[level]);
|
||||
|
||||
// Add update
|
||||
*Y[level] += *R[level];
|
||||
@@ -198,16 +221,44 @@ void Multigrid::Cycle(int level) const
|
||||
}
|
||||
}
|
||||
|
||||
void Multigrid::FormFineLinearSystem(Vector& x, Vector& b, OperatorHandle& A,
|
||||
Vector& X, Vector& B)
|
||||
const Operator* Multigrid::GetProlongationAtLevel(int level) const
|
||||
{
|
||||
return prolongations[level];
|
||||
}
|
||||
|
||||
GeometricMultigrid::~GeometricMultigrid()
|
||||
{
|
||||
for (int i = 0; i < bfs.Size(); ++i)
|
||||
{
|
||||
delete bfs[i];
|
||||
}
|
||||
|
||||
bfs.DeleteAll();
|
||||
|
||||
for (int i = 0; i < essentialTrueDofs.Size(); ++i)
|
||||
{
|
||||
delete essentialTrueDofs[i];
|
||||
}
|
||||
|
||||
essentialTrueDofs.DeleteAll();
|
||||
}
|
||||
|
||||
void GeometricMultigrid::FormFineLinearSystem(Vector& x, Vector& b,
|
||||
OperatorHandle& A,
|
||||
Vector& X, Vector& B)
|
||||
{
|
||||
bfs.Last()->FormLinearSystem(*essentialTrueDofs.Last(), x, b, A, X, B);
|
||||
}
|
||||
|
||||
void Multigrid::RecoverFineFEMSolution(const Vector& X, const Vector& b,
|
||||
Vector& x)
|
||||
void GeometricMultigrid::RecoverFineFEMSolution(const Vector& X,
|
||||
const Vector& b, Vector& x)
|
||||
{
|
||||
bfs.Last()->RecoverFEMSolution(X, b, x);
|
||||
}
|
||||
|
||||
const Operator* GeometricMultigrid::GetProlongationAtLevel(int level) const
|
||||
{
|
||||
return fespaces.GetProlongationAtLevel(level);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+45
-14
@@ -32,16 +32,13 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
const FiniteElementSpaceHierarchy& fespaces;
|
||||
Array<Array<int>*> essentialTrueDofs;
|
||||
Array<BilinearForm*> bfs;
|
||||
|
||||
private:
|
||||
Array<Operator*> operators;
|
||||
Array<Solver*> smoothers;
|
||||
Array<Operator*> prolongations;
|
||||
|
||||
Array<bool> ownedOperators;
|
||||
Array<bool> ownedSmoothers;
|
||||
Array<bool> ownedProlongations;
|
||||
|
||||
CycleType cycleType;
|
||||
int preSmoothingSteps;
|
||||
@@ -53,8 +50,16 @@ private:
|
||||
mutable Array<Vector*> Z;
|
||||
|
||||
public:
|
||||
/// Constructs an empty multigrid for the given FiniteElementSpaceHierarchy
|
||||
Multigrid(const FiniteElementSpaceHierarchy& fespaces_);
|
||||
/// Constructs an empty multigrid hierarchy.
|
||||
Multigrid();
|
||||
|
||||
/// Constructs a multigrid hierarchy from the given inputs.
|
||||
/** Inputs include operators and smoothers on all levels, prolongation
|
||||
operators that go from coarser to finer levels, and ownership of the
|
||||
given operators, smoothers, and prolongations. */
|
||||
Multigrid(const Array<Operator*>& operators_, const Array<Solver*>& smoothers_,
|
||||
const Array<Operator*>& prolongations_, const Array<bool>& ownedOperators_,
|
||||
const Array<bool>& ownedSmoothers_, const Array<bool>& ownedProlongations_);
|
||||
|
||||
/// Destructor
|
||||
virtual ~Multigrid();
|
||||
@@ -89,7 +94,7 @@ public:
|
||||
/// Returns smoother at given level
|
||||
Solver* GetSmootherAtLevel(int level);
|
||||
|
||||
/// Set the cycle type and number of pre- and post-smoothing steps used by Mult
|
||||
/// Set cycle type and number of pre- and post-smoothing steps used by Mult
|
||||
void SetCycleType(CycleType cycleType_, int preSmoothingSteps_,
|
||||
int postSmoothingSteps_);
|
||||
|
||||
@@ -99,7 +104,36 @@ public:
|
||||
/// Not supported for multigrid
|
||||
virtual void SetOperator(const Operator& op) override;
|
||||
|
||||
/// Form the linear system A X = B, corresponding to the operator on the finest level
|
||||
private:
|
||||
/// Application of a smoothing step at particular level
|
||||
void SmoothingStep(int level, bool transpose) const;
|
||||
|
||||
/// Application of a multigrid cycle at particular level
|
||||
void Cycle(int level) const;
|
||||
|
||||
/// Returns prolongation operator at given level
|
||||
virtual const Operator* GetProlongationAtLevel(int level) const;
|
||||
};
|
||||
|
||||
/// Geometric multigrid associated with a hierarchy of finite element spaces
|
||||
class GeometricMultigrid : public Multigrid
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpaceHierarchy& fespaces;
|
||||
Array<Array<int>*> essentialTrueDofs;
|
||||
Array<BilinearForm*> bfs;
|
||||
|
||||
public:
|
||||
/** Construct an empty multigrid object for the given finite element space
|
||||
hierarchy @a fespaces_ */
|
||||
GeometricMultigrid(const FiniteElementSpaceHierarchy& fespaces_)
|
||||
: Multigrid(), fespaces(fespaces_) { }
|
||||
|
||||
/// Destructor
|
||||
virtual ~GeometricMultigrid();
|
||||
|
||||
/** Form the linear system A X = B, corresponding to the operator on the
|
||||
finest level of the geometric multigrid hierarchy */
|
||||
void FormFineLinearSystem(Vector& x, Vector& b, OperatorHandle& A, Vector& X,
|
||||
Vector& B);
|
||||
|
||||
@@ -107,11 +141,8 @@ public:
|
||||
void RecoverFineFEMSolution(const Vector& X, const Vector& b, Vector& x);
|
||||
|
||||
private:
|
||||
/// Application of a smoothing step at particular level
|
||||
void SmoothingStep(int level, bool transpose) const;
|
||||
|
||||
/// Application of a cycle at particular level
|
||||
void Cycle(int level) const;
|
||||
/// Returns prolongation operator at given level
|
||||
virtual const Operator* GetProlongationAtLevel(int level) const override;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -565,6 +565,38 @@ HypreParMatrix* ParDiscreteLinearOperator::ParallelAssemble() const
|
||||
return RAP;
|
||||
}
|
||||
|
||||
void ParDiscreteLinearOperator::ParallelAssemble(OperatorHandle &A)
|
||||
{
|
||||
// construct the rectangular block-diagonal matrix dA
|
||||
OperatorHandle dA(A.Type());
|
||||
dA.MakeRectangularBlockDiag(domain_fes->GetComm(),
|
||||
range_fes->GlobalVSize(),
|
||||
domain_fes->GlobalVSize(),
|
||||
range_fes->GetDofOffsets(),
|
||||
domain_fes->GetDofOffsets(),
|
||||
mat);
|
||||
|
||||
OperatorHandle R_test_transpose(A.Type()), P_trial(A.Type());
|
||||
|
||||
// TODO - construct the Dof_TrueDof_Matrix directly in the required format.
|
||||
R_test_transpose.ConvertFrom(range_fes->Dof_TrueDof_Matrix());
|
||||
P_trial.ConvertFrom(domain_fes->Dof_TrueDof_Matrix());
|
||||
|
||||
A.MakeRAP(R_test_transpose, dA, P_trial);
|
||||
}
|
||||
|
||||
void ParDiscreteLinearOperator::FormRectangularSystemMatrix(OperatorHandle &A)
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
Array<int> empty;
|
||||
ext->FormRectangularSystemOperator(empty, empty, A);
|
||||
return;
|
||||
}
|
||||
|
||||
mfem_error("not implemented!");
|
||||
}
|
||||
|
||||
void ParDiscreteLinearOperator::GetParBlocks(Array2D<HypreParMatrix *> &blocks)
|
||||
const
|
||||
{
|
||||
|
||||
@@ -160,6 +160,9 @@ public:
|
||||
/// Get the parallel finite element space prolongation matrix
|
||||
virtual const Operator *GetProlongation() const
|
||||
{ return pfes->GetProlongationMatrix(); }
|
||||
/// Get the transpose of GetRestriction, useful for matrix-free RAP
|
||||
virtual const Operator *GetRestrictionTranspose() const
|
||||
{ return pfes->GetRestrictionTransposeOperator(); }
|
||||
/// Get the parallel finite element space restriction matrix
|
||||
virtual const Operator *GetRestriction() const
|
||||
{ return pfes->GetRestrictionMatrix(); }
|
||||
@@ -301,10 +304,18 @@ public:
|
||||
/// Returns the matrix "assembled" on the true dofs
|
||||
HypreParMatrix *ParallelAssemble() const;
|
||||
|
||||
/** @brief Returns the matrix assembled on the true dofs, i.e.
|
||||
@a A = R_test A_local P_trial, in the format (type id) specified by
|
||||
@a A. */
|
||||
void ParallelAssemble(OperatorHandle &A);
|
||||
|
||||
/** Extract the parallel blocks corresponding to the vector dimensions of the
|
||||
domain and range parallel finite element spaces */
|
||||
void GetParBlocks(Array2D<HypreParMatrix *> &blocks) const;
|
||||
|
||||
/** @brief Return in @a A a parallel (on truedofs) version of this operator. */
|
||||
virtual void FormRectangularSystemMatrix(OperatorHandle &A);
|
||||
|
||||
virtual ~ParDiscreteLinearOperator() { }
|
||||
};
|
||||
|
||||
|
||||
+132
-49
@@ -101,6 +101,8 @@ void ParFiniteElementSpace::ParInit(ParMesh *pm)
|
||||
|
||||
P = NULL;
|
||||
Pconf = NULL;
|
||||
Rconf = NULL;
|
||||
R_transpose = NULL;
|
||||
R = NULL;
|
||||
|
||||
num_face_nbr_dofs = -1;
|
||||
@@ -499,6 +501,12 @@ void ParFiniteElementSpace::GetFaceDofs(int i, Array<int> &dofs) const
|
||||
}
|
||||
}
|
||||
|
||||
const FiniteElement *ParFiniteElementSpace::GetFE(int i) const
|
||||
{
|
||||
int ne = mesh->GetNE();
|
||||
if (i >= ne) { return GetFaceNbrFE(i - ne); }
|
||||
else { return FiniteElementSpace::GetFE(i); }
|
||||
}
|
||||
|
||||
const Operator *ParFiniteElementSpace::GetFaceRestriction(
|
||||
ElementDofOrdering e_ordering, FaceType type, L2FaceValues mul) const
|
||||
@@ -921,6 +929,45 @@ const Operator *ParFiniteElementSpace::GetProlongationMatrix() const
|
||||
}
|
||||
}
|
||||
|
||||
const Operator *ParFiniteElementSpace::GetRestrictionOperator() const
|
||||
{
|
||||
if (Conforming())
|
||||
{
|
||||
if (Rconf) { return Rconf; }
|
||||
|
||||
if (NRanks == 1)
|
||||
{
|
||||
R_transpose = new IdentityOperator(GetTrueVSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!Device::Allows(Backend::DEVICE_MASK))
|
||||
{
|
||||
R_transpose = new ConformingProlongationOperator(*this, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
R_transpose =
|
||||
new DeviceConformingProlongationOperator(*this, true);
|
||||
}
|
||||
}
|
||||
Rconf = new TransposeOperator(R_transpose);
|
||||
return Rconf;
|
||||
}
|
||||
else
|
||||
{
|
||||
Dof_TrueDof_Matrix();
|
||||
R_transpose = new TransposeOperator(R);
|
||||
return R;
|
||||
}
|
||||
}
|
||||
|
||||
const Operator *ParFiniteElementSpace::GetRestrictionTransposeOperator() const
|
||||
{
|
||||
GetRestrictionOperator();
|
||||
return R_transpose;
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
{
|
||||
if (num_face_nbr_dofs >= 0) { return; }
|
||||
@@ -2834,6 +2881,8 @@ void ParFiniteElementSpace::Destroy()
|
||||
|
||||
delete P; P = NULL;
|
||||
delete Pconf; Pconf = NULL;
|
||||
delete Rconf; Rconf = NULL;
|
||||
delete R_transpose; R_transpose = NULL;
|
||||
delete R; R = NULL;
|
||||
|
||||
delete gcomm; gcomm = NULL;
|
||||
@@ -2959,12 +3008,12 @@ void ParFiniteElementSpace::Update(bool want_transform)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ConformingProlongationOperator::ConformingProlongationOperator(
|
||||
const ParFiniteElementSpace &pfes)
|
||||
const ParFiniteElementSpace &pfes, bool local_)
|
||||
: Operator(pfes.GetVSize(), pfes.GetTrueVSize()),
|
||||
external_ldofs(),
|
||||
gc(pfes.GroupComm())
|
||||
gc(pfes.GroupComm()),
|
||||
local(local_)
|
||||
{
|
||||
MFEM_VERIFY(pfes.Conforming(), "");
|
||||
const Table &group_ldof = gc.GroupLDofTable();
|
||||
@@ -3013,7 +3062,14 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
|
||||
const int m = external_ldofs.Size();
|
||||
|
||||
const int in_layout = 2; // 2 - input is ltdofs array
|
||||
gc.BcastBegin(const_cast<double*>(xdata), in_layout);
|
||||
if (local)
|
||||
{
|
||||
y = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
gc.BcastBegin(const_cast<double*>(xdata), in_layout);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < m; i++)
|
||||
@@ -3025,7 +3081,10 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
|
||||
std::copy(xdata+j-m, xdata+Width(), ydata+j);
|
||||
|
||||
const int out_layout = 0; // 0 - output is ldofs array
|
||||
gc.BcastEnd(ydata, out_layout);
|
||||
if (!local)
|
||||
{
|
||||
gc.BcastEnd(ydata, out_layout);
|
||||
}
|
||||
}
|
||||
|
||||
void ConformingProlongationOperator::MultTranspose(
|
||||
@@ -3038,7 +3097,10 @@ void ConformingProlongationOperator::MultTranspose(
|
||||
double *ydata = y.HostWrite();
|
||||
const int m = external_ldofs.Size();
|
||||
|
||||
gc.ReduceBegin(xdata);
|
||||
if (!local)
|
||||
{
|
||||
gc.ReduceBegin(xdata);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
for (int i = 0; i < m; i++)
|
||||
@@ -3050,13 +3112,18 @@ void ConformingProlongationOperator::MultTranspose(
|
||||
std::copy(xdata+j, xdata+Height(), ydata+j-m);
|
||||
|
||||
const int out_layout = 2; // 2 - output is an array on all ltdofs
|
||||
gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
|
||||
if (!local)
|
||||
{
|
||||
gc.ReduceEnd<double>(ydata, out_layout, GroupCommunicator::Sum);
|
||||
}
|
||||
}
|
||||
|
||||
DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
|
||||
const ParFiniteElementSpace &pfes) :
|
||||
const ParFiniteElementSpace &pfes,
|
||||
bool local_) :
|
||||
ConformingProlongationOperator(pfes),
|
||||
mpi_gpu_aware(Device::GetGPUAwareMPI())
|
||||
mpi_gpu_aware(Device::GetGPUAwareMPI()),
|
||||
local(local_)
|
||||
{
|
||||
MFEM_ASSERT(pfes.Conforming(), "internal error");
|
||||
const SparseMatrix *R = pfes.GetRestrictionMatrix();
|
||||
@@ -3173,32 +3240,42 @@ void DeviceConformingProlongationOperator::Mult(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
const GroupTopology >opo = gc.GetGroupTopology();
|
||||
BcastBeginCopy(x); // copy to 'shr_buf'
|
||||
int req_counter = 0;
|
||||
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
|
||||
if (local)
|
||||
{
|
||||
const int send_offset = shr_buf_offsets[nbr];
|
||||
const int send_size = shr_buf_offsets[nbr+1] - send_offset;
|
||||
if (send_size > 0)
|
||||
y = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
BcastBeginCopy(x); // copy to 'shr_buf'
|
||||
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
|
||||
{
|
||||
auto send_buf = mpi_gpu_aware ? shr_buf.Read() : shr_buf.HostRead();
|
||||
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
|
||||
gtopo.GetNeighborRank(nbr), 41822,
|
||||
gtopo.GetComm(), &requests[req_counter++]);
|
||||
}
|
||||
const int recv_offset = ext_buf_offsets[nbr];
|
||||
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
|
||||
if (recv_size > 0)
|
||||
{
|
||||
auto recv_buf = mpi_gpu_aware ? ext_buf.Write() : ext_buf.HostWrite();
|
||||
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
|
||||
gtopo.GetNeighborRank(nbr), 41822,
|
||||
gtopo.GetComm(), &requests[req_counter++]);
|
||||
const int send_offset = shr_buf_offsets[nbr];
|
||||
const int send_size = shr_buf_offsets[nbr+1] - send_offset;
|
||||
if (send_size > 0)
|
||||
{
|
||||
auto send_buf = mpi_gpu_aware ? shr_buf.Read() : shr_buf.HostRead();
|
||||
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
|
||||
gtopo.GetNeighborRank(nbr), 41822,
|
||||
gtopo.GetComm(), &requests[req_counter++]);
|
||||
}
|
||||
const int recv_offset = ext_buf_offsets[nbr];
|
||||
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
|
||||
if (recv_size > 0)
|
||||
{
|
||||
auto recv_buf = mpi_gpu_aware ? ext_buf.Write() : ext_buf.HostWrite();
|
||||
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
|
||||
gtopo.GetNeighborRank(nbr), 41822,
|
||||
gtopo.GetComm(), &requests[req_counter++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
BcastLocalCopy(x, y);
|
||||
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
|
||||
BcastEndCopy(y); // copy from 'ext_buf'
|
||||
if (!local)
|
||||
{
|
||||
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
|
||||
BcastEndCopy(y); // copy from 'ext_buf'
|
||||
}
|
||||
}
|
||||
|
||||
DeviceConformingProlongationOperator::~DeviceConformingProlongationOperator()
|
||||
@@ -3261,32 +3338,38 @@ void DeviceConformingProlongationOperator::MultTranspose(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
const GroupTopology >opo = gc.GetGroupTopology();
|
||||
ReduceBeginCopy(x); // copy to 'ext_buf'
|
||||
int req_counter = 0;
|
||||
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
|
||||
if (!local)
|
||||
{
|
||||
const int send_offset = ext_buf_offsets[nbr];
|
||||
const int send_size = ext_buf_offsets[nbr+1] - send_offset;
|
||||
if (send_size > 0)
|
||||
ReduceBeginCopy(x); // copy to 'ext_buf'
|
||||
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
|
||||
{
|
||||
auto send_buf = mpi_gpu_aware ? ext_buf.Read() : ext_buf.HostRead();
|
||||
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
|
||||
gtopo.GetNeighborRank(nbr), 41823,
|
||||
gtopo.GetComm(), &requests[req_counter++]);
|
||||
}
|
||||
const int recv_offset = shr_buf_offsets[nbr];
|
||||
const int recv_size = shr_buf_offsets[nbr+1] - recv_offset;
|
||||
if (recv_size > 0)
|
||||
{
|
||||
auto recv_buf = mpi_gpu_aware ? shr_buf.Write() : shr_buf.HostWrite();
|
||||
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
|
||||
gtopo.GetNeighborRank(nbr), 41823,
|
||||
gtopo.GetComm(), &requests[req_counter++]);
|
||||
const int send_offset = ext_buf_offsets[nbr];
|
||||
const int send_size = ext_buf_offsets[nbr+1] - send_offset;
|
||||
if (send_size > 0)
|
||||
{
|
||||
auto send_buf = mpi_gpu_aware ? ext_buf.Read() : ext_buf.HostRead();
|
||||
MPI_Isend(send_buf + send_offset, send_size, MPI_DOUBLE,
|
||||
gtopo.GetNeighborRank(nbr), 41823,
|
||||
gtopo.GetComm(), &requests[req_counter++]);
|
||||
}
|
||||
const int recv_offset = shr_buf_offsets[nbr];
|
||||
const int recv_size = shr_buf_offsets[nbr+1] - recv_offset;
|
||||
if (recv_size > 0)
|
||||
{
|
||||
auto recv_buf = mpi_gpu_aware ? shr_buf.Write() : shr_buf.HostWrite();
|
||||
MPI_Irecv(recv_buf + recv_offset, recv_size, MPI_DOUBLE,
|
||||
gtopo.GetNeighborRank(nbr), 41823,
|
||||
gtopo.GetComm(), &requests[req_counter++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
ReduceLocalCopy(x, y);
|
||||
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
|
||||
ReduceEndAssemble(y); // assemble from 'shr_buf'
|
||||
if (!local)
|
||||
{
|
||||
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
|
||||
ReduceEndAssemble(y); // assemble from 'shr_buf'
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+29
-2
@@ -75,6 +75,12 @@ private:
|
||||
|
||||
/// The (block-diagonal) matrix R (restriction of dof to true dof). Owned.
|
||||
mutable SparseMatrix *R;
|
||||
/// Optimized action-only restriction operator for conforming meshes. Owned.
|
||||
mutable Operator *Rconf;
|
||||
/** Transpose of R or Rconf. For conforming mesh, this is a matrix-free
|
||||
(Device)ConformingProlongationOperator, for a non-conforming mesh
|
||||
this is a TransposeOperator wrapping R. */
|
||||
mutable Operator *R_transpose;
|
||||
|
||||
ParNURBSExtension *pNURBSext() const
|
||||
{ return dynamic_cast<ParNURBSExtension *>(NURBSext); }
|
||||
@@ -264,6 +270,12 @@ public:
|
||||
including the dofs for the edges and the vertices of the face. */
|
||||
virtual void GetFaceDofs(int i, Array<int> &dofs) const;
|
||||
|
||||
/** Returns pointer to the FiniteElement in the FiniteElementCollection
|
||||
associated with i'th element in the mesh object. If @a i is greater than
|
||||
or equal to the number of local mesh elements, @a i will be interpreted
|
||||
as a shifted index of a face neigbor element. */
|
||||
virtual const FiniteElement *GetFE(int i) const;
|
||||
|
||||
/** Returns an Operator that converts L-vectors to E-vectors on each face.
|
||||
The parallel version is different from the serial one because of the
|
||||
presence of shared faces. Shared faces are treated as interior faces,
|
||||
@@ -335,6 +347,16 @@ public:
|
||||
HYPRE_Int GetMyTDofOffset() const;
|
||||
|
||||
virtual const Operator *GetProlongationMatrix() const;
|
||||
/** @brief Return logical transpose of restriction matrix, but in
|
||||
non-assembled optimized matrix-free form.
|
||||
|
||||
The implementation is like GetProlongationMatrix, but it sets local
|
||||
DOFs to the true DOF values if owned locally, otherwise zero. */
|
||||
virtual const Operator *GetRestrictionTransposeOperator() const;
|
||||
/** Get an Operator that performs the action of GetRestrictionMatrix(),
|
||||
but potentially with a non-assembled optimized matrix-free
|
||||
implementation. */
|
||||
virtual const Operator *GetRestrictionOperator() const;
|
||||
/// Get the R matrix which restricts a local dof vector to true dof vector.
|
||||
virtual const SparseMatrix *GetRestrictionMatrix() const
|
||||
{ Dof_TrueDof_Matrix(); return R; }
|
||||
@@ -389,9 +411,11 @@ class ConformingProlongationOperator : public Operator
|
||||
protected:
|
||||
Array<int> external_ldofs;
|
||||
const GroupCommunicator &gc;
|
||||
bool local;
|
||||
|
||||
public:
|
||||
ConformingProlongationOperator(const ParFiniteElementSpace &pfes);
|
||||
ConformingProlongationOperator(const ParFiniteElementSpace &pfes,
|
||||
bool local_=false);
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
@@ -410,6 +434,8 @@ protected:
|
||||
Array<int> ltdof_ldof, unq_ltdof;
|
||||
Array<int> unq_shr_i, unq_shr_j;
|
||||
MPI_Request *requests;
|
||||
bool local;
|
||||
|
||||
// Kernel: copy ltdofs from 'src' to 'shr_buf' - prepare for send.
|
||||
// shr_buf[i] = src[shr_ltdof[i]]
|
||||
void BcastBeginCopy(const Vector &src) const;
|
||||
@@ -435,7 +461,8 @@ protected:
|
||||
void ReduceEndAssemble(Vector &dst) const;
|
||||
|
||||
public:
|
||||
DeviceConformingProlongationOperator(const ParFiniteElementSpace &pfes);
|
||||
DeviceConformingProlongationOperator(const ParFiniteElementSpace &pfes,
|
||||
bool local_=false);
|
||||
|
||||
virtual ~DeviceConformingProlongationOperator();
|
||||
|
||||
|
||||
+29
-7
@@ -471,6 +471,25 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
|
||||
{
|
||||
int ne = fes->GetNE();
|
||||
if (el >= ne)
|
||||
{
|
||||
MFEM_ASSERT(face_nbr_data.Size() > 0,
|
||||
"ParGridFunction::GetElementDofValues: ExchangeFaceNbrData "
|
||||
"must be called before accessing face neighbor elements.");
|
||||
// Face neighbor element
|
||||
Array<int> dof_idx;
|
||||
pfes->GetFaceNbrElementVDofs(el - ne, dof_idx);
|
||||
face_nbr_data.GetSubVector(dof_idx, dof_vals);
|
||||
}
|
||||
else
|
||||
{
|
||||
GridFunction::GetElementDofValues(el, dof_vals);
|
||||
}
|
||||
}
|
||||
|
||||
void ParGridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
{
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
@@ -657,12 +676,12 @@ void ParGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
|
||||
|
||||
double ParGridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
Coefficient *ell_coeff,
|
||||
double Nu,
|
||||
JumpScaling jump_scaling,
|
||||
const IntegrationRule *irs[]) const
|
||||
{
|
||||
const_cast<ParGridFunction *>(this)->ExchangeFaceNbrData();
|
||||
|
||||
int fdof, dim, intorder, k;
|
||||
int fdof, intorder, k;
|
||||
ElementTransformation *transf;
|
||||
Vector shape, el_dofs, err_val, ell_coeff_val;
|
||||
Array<int> vdofs;
|
||||
@@ -670,7 +689,6 @@ double ParGridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
double error = 0.0;
|
||||
|
||||
ParMesh *mesh = pfes->GetParMesh();
|
||||
dim = mesh->Dimension();
|
||||
|
||||
std::map<int,int> local_to_shared;
|
||||
for (int i = 0; i < mesh->GetNSharedFaces(); ++i)
|
||||
@@ -687,6 +705,7 @@ double ParGridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
mesh->GetFaceElements(i, &iel1, &iel2);
|
||||
mesh->GetFaceInfos(i, &info1, &info2);
|
||||
|
||||
double h = mesh->GetElementSize(iel1);
|
||||
intorder = fes->GetFE(iel1)->GetOrder();
|
||||
|
||||
FaceElementTransformations *face_elem_transf;
|
||||
@@ -703,11 +722,10 @@ double ParGridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
}
|
||||
shared_face = true;
|
||||
shared_face_factor = 0.5;
|
||||
h = std::min(h, mesh->GetFaceNbrElementSize(iel2));
|
||||
}
|
||||
else
|
||||
{
|
||||
face_elem_transf = mesh->GetFaceElementTransformations(i);
|
||||
|
||||
if (iel2 >= 0)
|
||||
{
|
||||
fe2 = pfes->GetFE(iel2);
|
||||
@@ -715,12 +733,15 @@ double ParGridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
{
|
||||
intorder = k;
|
||||
}
|
||||
h = std::min(h, mesh->GetElementSize(iel2));
|
||||
}
|
||||
else
|
||||
{
|
||||
fe2 = NULL;
|
||||
}
|
||||
face_elem_transf = mesh->GetFaceElementTransformations(i);
|
||||
}
|
||||
int p = intorder;
|
||||
|
||||
intorder = 2 * intorder; // <-------------
|
||||
const IntegrationRule *ir;
|
||||
@@ -806,8 +827,9 @@ double ParGridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
transf->SetIntPoint(&ip);
|
||||
error += shared_face_factor*(ip.weight * Nu * ell_coeff_val(j) *
|
||||
pow(transf->Weight(), 1.0-1.0/(dim-1)) *
|
||||
double nu = jump_scaling.Eval(h, p);
|
||||
error += shared_face_factor*(ip.weight * nu * ell_coeff_val(j) *
|
||||
transf->Weight() *
|
||||
err_val(j) * err_val(j));
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -221,6 +221,12 @@ public:
|
||||
const IntegrationPoint &ip,
|
||||
Vector &val, Vector *tr = NULL) const;
|
||||
|
||||
/** Sets the output vector @a dof_vals to the values of the degrees of
|
||||
freedom of element @a el. If @a el is greater than or equal to the number
|
||||
of local elements, it will be interpreted as a shifted index of a face
|
||||
neighbor element. */
|
||||
virtual void GetElementDofValues(int el, Vector &dof_vals) const;
|
||||
|
||||
using GridFunction::ProjectCoefficient;
|
||||
virtual void ProjectCoefficient(Coefficient &coeff);
|
||||
|
||||
@@ -310,7 +316,7 @@ public:
|
||||
/// Returns the Face Jumps error for L2 elements
|
||||
virtual double ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
Coefficient *ell_coeff,
|
||||
double Nu,
|
||||
JumpScaling jump_scaling,
|
||||
const IntegrationRule *irs[]=NULL)
|
||||
const;
|
||||
|
||||
|
||||
@@ -420,6 +420,35 @@ void QuadratureInterpolator::MultTranspose(
|
||||
MFEM_ABORT("this method is not implemented yet");
|
||||
}
|
||||
|
||||
static void D2QValues1D(const int NE,
|
||||
const Array<double> &b_,
|
||||
const Vector &x_,
|
||||
Vector &y_,
|
||||
const int vdim = 1,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
auto b = Reshape(b_.Read(), q1d, d1d);
|
||||
auto x = Reshape(x_.Read(), d1d, vdim, NE);
|
||||
auto y = Reshape(y_.Write(), vdim, q1d, NE);
|
||||
|
||||
MFEM_FORALL(e, NE,
|
||||
{
|
||||
for (int c = 0; c < vdim; c++)
|
||||
{
|
||||
for (int q = 0; q < q1d; ++q)
|
||||
{
|
||||
double val = 0.0;
|
||||
for (int d = 0; d < d1d; ++d)
|
||||
{
|
||||
val += b(q, d) * x(d, c, e);
|
||||
}
|
||||
y(c, q, e) = val;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
template<int T_VDIM = 0, int T_D1D = 0, int T_Q1D = 0, int T_NBZ = 0>
|
||||
static void D2QValues2D(const int NE,
|
||||
@@ -631,6 +660,15 @@ static void D2QValues(const FiniteElementSpace &fes,
|
||||
const int Q1D = maps->nqpt;
|
||||
const int id = (vdim<<8) | (D1D<<4) | Q1D;
|
||||
|
||||
if (dim == 1)
|
||||
{
|
||||
MFEM_VERIFY(D1D <= MAX_D1D, "Orders higher than " << MAX_D1D-1
|
||||
<< " are not supported!");
|
||||
MFEM_VERIFY(Q1D <= MAX_Q1D, "Quadrature rules with more than "
|
||||
<< MAX_Q1D << " 1D points are not supported!");
|
||||
D2QValues1D(NE, maps->B, e_vec, q_val, vdim, D1D, Q1D);
|
||||
return;
|
||||
}
|
||||
if (dim == 2)
|
||||
{
|
||||
switch (id)
|
||||
|
||||
@@ -195,6 +195,31 @@ void ElementRestriction::MultTransposeUnsigned(const Vector& x, Vector& y) const
|
||||
});
|
||||
}
|
||||
|
||||
void ElementRestriction::MultLeftInverse(const Vector& x, Vector& y) const
|
||||
{
|
||||
// Assumes all elements have the same number of dofs
|
||||
const int nd = dof;
|
||||
const int vd = vdim;
|
||||
const bool t = byvdim;
|
||||
auto d_offsets = offsets.Read();
|
||||
auto d_indices = indices.Read();
|
||||
auto d_x = Reshape(x.Read(), nd, vd, ne);
|
||||
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
|
||||
MFEM_FORALL(i, ndofs,
|
||||
{
|
||||
const int nextOffset = d_offsets[i + 1];
|
||||
for (int c = 0; c < vd; ++c)
|
||||
{
|
||||
double dofValue = 0;
|
||||
const int j = nextOffset - 1;
|
||||
const int idx_j = (d_indices[j] >= 0) ? d_indices[j] : -1 - d_indices[j];
|
||||
dofValue = (d_indices[j] >= 0) ? d_x(idx_j % nd, c, idx_j / nd) :
|
||||
-d_x(idx_j % nd, c, idx_j / nd);
|
||||
d_y(t?c:i,t?i:c) = dofValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ElementRestriction::BooleanMask(Vector& y) const
|
||||
{
|
||||
// Assumes all elements have the same number of dofs
|
||||
|
||||
@@ -57,6 +57,10 @@ public:
|
||||
/// Compute MultTranspose without applying signs based on DOF orientations.
|
||||
void MultTransposeUnsigned(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Compute MultTranspose by setting (rather than adding) element
|
||||
/// contributions; this is a left inverse of the Mult() operation
|
||||
void MultLeftInverse(const Vector &x, Vector &y) const;
|
||||
|
||||
/// @brief Fills the E-vector y with `boolean` values 0.0 and 1.0 such that each
|
||||
/// each entry of the L-vector is uniquely represented in `y`.
|
||||
/** This means, the sum of the E-vector `y` is equal to the sum of the
|
||||
|
||||
+229
-22
@@ -19,6 +19,42 @@ namespace mfem
|
||||
|
||||
// Target-matrix optimization paradigm (TMOP) mesh quality metrics.
|
||||
|
||||
double TMOP_Combo_QualityMetric::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
double metric = 0.;
|
||||
for (int i = 0; i < tmop_q_arr.Size(); i++)
|
||||
{
|
||||
metric += wt_arr[i]*tmop_q_arr[i]->EvalW(Jpt);
|
||||
}
|
||||
return metric;
|
||||
}
|
||||
|
||||
void TMOP_Combo_QualityMetric::EvalP(const DenseMatrix &Jpt,
|
||||
DenseMatrix &P) const
|
||||
{
|
||||
DenseMatrix Pt(P.Size());
|
||||
for (int i = 0; i < tmop_q_arr.Size(); i++)
|
||||
{
|
||||
tmop_q_arr[i]->EvalP(Jpt, Pt);
|
||||
Pt *= wt_arr[i];
|
||||
P += Pt;
|
||||
}
|
||||
}
|
||||
|
||||
void TMOP_Combo_QualityMetric::AssembleH(const DenseMatrix &Jpt,
|
||||
const DenseMatrix &DS,
|
||||
const double weight,
|
||||
DenseMatrix &A) const
|
||||
{
|
||||
DenseMatrix At(A.Size());
|
||||
for (int i = 0; i < tmop_q_arr.Size(); i++)
|
||||
{
|
||||
tmop_q_arr[i]->AssembleH(Jpt, DS, weight, At);
|
||||
At *= wt_arr[i];
|
||||
A += At;
|
||||
}
|
||||
}
|
||||
|
||||
double TMOP_Metric_001::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
ie.SetJacobian(Jpt.GetData());
|
||||
@@ -160,23 +196,6 @@ double TMOP_Metric_aspratio3D::EvalW(const DenseMatrix &Jpt) const
|
||||
) / 3.0;
|
||||
}
|
||||
|
||||
// mu_14 = |T-I|^2
|
||||
double TMOP_Metric_SSA2D::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
MFEM_VERIFY(Jtr != NULL,
|
||||
"Requires a target Jacobian, use SetTargetJacobian().");
|
||||
|
||||
DenseMatrix Id(2,2);
|
||||
|
||||
Id(0,0) = 1; Id(0,1) = 0;
|
||||
Id(1,0) = 0; Id(1,1) = 1;
|
||||
|
||||
DenseMatrix Mat(2,2);
|
||||
Mat = Jpt;
|
||||
Mat.Add(-1,Id);
|
||||
return Mat.FNorm2();
|
||||
}
|
||||
|
||||
double TMOP_Metric_002::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
ie.SetJacobian(Jpt.GetData());
|
||||
@@ -272,13 +291,41 @@ void TMOP_Metric_009::AssembleH(const DenseMatrix &Jpt,
|
||||
ie.Assemble_ddI1b(weight, A.GetData());
|
||||
}
|
||||
|
||||
// mu_14 = |T-I|^2
|
||||
double TMOP_Metric_014::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
MFEM_VERIFY(Jtr != NULL,
|
||||
"Requires a target Jacobian, use SetTargetJacobian().");
|
||||
|
||||
DenseMatrix Id(2,2);
|
||||
|
||||
Id(0,0) = 1; Id(0,1) = 0;
|
||||
Id(1,0) = 0; Id(1,1) = 1;
|
||||
|
||||
DenseMatrix Mat(2,2);
|
||||
Mat = Jpt;
|
||||
Mat.Add(-1,Id);
|
||||
return Mat.FNorm2();
|
||||
}
|
||||
|
||||
double TMOP_Metric_022::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
// mu_22 = (0.5*|J|^2 - det(J)) / (det(J) - tau0)
|
||||
// = (0.5*I1 - I2b) / (I2b - tau0)
|
||||
ie.SetJacobian(Jpt.GetData());
|
||||
const double I2b = ie.Get_I2b();
|
||||
return (0.5*ie.Get_I1() - I2b) / (I2b - tau0);
|
||||
|
||||
double d = I2b - min_detT;
|
||||
if (d < 0.0 && min_detT == 0.0)
|
||||
{
|
||||
// The mesh has been untangled, but it's still possible to get negative
|
||||
// detJ in FD calculations, as they move the nodes around with some small
|
||||
// increments and can produce negative determinants. Thus we put a small
|
||||
// value in the denominator. Note that here I2b < 0.
|
||||
d = - I2b * 0.1;
|
||||
}
|
||||
|
||||
return (0.5*ie.Get_I1() - I2b) / d;
|
||||
}
|
||||
|
||||
void TMOP_Metric_022::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
@@ -287,8 +334,8 @@ void TMOP_Metric_022::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
// P = 1/(I2b - tau0)*(0.5*dI1 - dI2b) - (0.5*I1 - I2b)/(I2b - tau0)^2*dI2b
|
||||
// = 0.5/(I2b - tau0)*dI1 + (tau0 - 0.5*I1)/(I2b - tau0)^2*dI2b
|
||||
ie.SetJacobian(Jpt.GetData());
|
||||
const double c1 = 1.0/(ie.Get_I2b() - tau0);
|
||||
Add(c1/2, ie.Get_dI1(), (tau0 - ie.Get_I1()/2)*c1*c1, ie.Get_dI2b(), P);
|
||||
const double c1 = 1.0/(ie.Get_I2b() - min_detT);
|
||||
Add(c1/2, ie.Get_dI1(), (min_detT - ie.Get_I1()/2)*c1*c1, ie.Get_dI2b(), P);
|
||||
}
|
||||
|
||||
void TMOP_Metric_022::AssembleH(const DenseMatrix &Jpt,
|
||||
@@ -308,10 +355,10 @@ void TMOP_Metric_022::AssembleH(const DenseMatrix &Jpt,
|
||||
// +0.5/(I2b - tau0)*ddI1 + z*ddI2b
|
||||
ie.SetJacobian(Jpt.GetData());
|
||||
ie.SetDerivativeMatrix(DS.Height(), DS.GetData());
|
||||
const double c1 = 1.0/(ie.Get_I2b() - tau0);
|
||||
const double c1 = 1.0/(ie.Get_I2b() - min_detT);
|
||||
const double c2 = weight*c1/2;
|
||||
const double c3 = c1*c2;
|
||||
const double c4 = (2*tau0 - ie.Get_I1())*c3; // weight*z
|
||||
const double c4 = (2*min_detT - ie.Get_I1())*c3; // weight*z
|
||||
ie.Assemble_TProd(-c3, ie.Get_dI1(), ie.Get_dI2b(), A.GetData());
|
||||
ie.Assemble_TProd(-2*c1*c4, ie.Get_dI2b(), A.GetData());
|
||||
ie.Assemble_ddI1(c2, A.GetData());
|
||||
@@ -484,6 +531,23 @@ double TMOP_Metric_085::EvalW(const DenseMatrix &Jpt) const
|
||||
return Mat.FNorm2();
|
||||
}
|
||||
|
||||
// mu_98 = 1/(tau)|T-I|^2
|
||||
double TMOP_Metric_098::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
MFEM_VERIFY(Jtr != NULL,
|
||||
"Requires a target Jacobian, use SetTargetJacobian().");
|
||||
|
||||
DenseMatrix Id(2,2);
|
||||
|
||||
Id(0,0) = 1; Id(0,1) = 0;
|
||||
Id(1,0) = 0; Id(1,1) = 1;
|
||||
|
||||
DenseMatrix Mat(2,2);
|
||||
Mat = Jpt;
|
||||
Mat.Add(-1,Id);
|
||||
return Mat.FNorm2()/Jtr->Det();
|
||||
}
|
||||
|
||||
double TMOP_Metric_211::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
// mu_211 = (det(J) - 1)^2 - det(J) + (det(J)^2 + eps)^{1/2}
|
||||
@@ -650,6 +714,71 @@ void TMOP_Metric_303::AssembleH(const DenseMatrix &Jpt,
|
||||
ie.Assemble_ddI1b(weight/3., A.GetData());
|
||||
}
|
||||
|
||||
double TMOP_Metric_311::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
// mu_311 = (det(J) - 1)^2 - det(J) + (det(J)^2 + eps)^{1/2}
|
||||
// = (I3b - 1)^2 - I3b + sqrt(I3b^2 + eps)
|
||||
ie.SetJacobian(Jpt.GetData());
|
||||
const double I3b = ie.Get_I3b();
|
||||
return (I3b - 1.0)*(I3b - 1.0) - I3b + std::sqrt(I3b*I3b + eps);
|
||||
}
|
||||
|
||||
void TMOP_Metric_311::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{
|
||||
ie.SetJacobian(Jpt.GetData());
|
||||
const double I3b = ie.Get_I3b();
|
||||
const double c = 2*I3b-3+(I3b)/(std::pow((I3b*I3b+eps),0.5));
|
||||
P.Set(c, ie.Get_dI3b());
|
||||
}
|
||||
|
||||
void TMOP_Metric_311::AssembleH(const DenseMatrix &Jpt,
|
||||
const DenseMatrix &DS,
|
||||
const double weight,
|
||||
DenseMatrix &A) const
|
||||
{
|
||||
ie.SetJacobian(Jpt.GetData());
|
||||
ie.SetDerivativeMatrix(DS.Height(), DS.GetData());
|
||||
const double I3b = ie.Get_I3b();
|
||||
const double c0 = I3b*I3b+eps;
|
||||
const double c1 = 2 + 1/(pow(c0,0.5)) - I3b*I3b/(pow(c0,1.5));
|
||||
const double c2 = 2*I3b - 3 + I3b/(pow(c0,0.5));
|
||||
ie.Assemble_TProd(weight*c1, ie.Get_dI3b(), A.GetData());
|
||||
ie.Assemble_ddI3b(c2*weight, A.GetData());
|
||||
}
|
||||
|
||||
double TMOP_Metric_313::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
ie.SetJacobian(Jpt.GetData());
|
||||
|
||||
const double I3b = ie.Get_I3b();
|
||||
double d = I3b - min_detT;
|
||||
if (d < 0.0 && min_detT == 0.0)
|
||||
{
|
||||
// The mesh has been untangled, but it's still possible to get negative
|
||||
// detJ in FD calculations, as they move the nodes around with some small
|
||||
// increments and can produce negative determinants. Thus we put a small
|
||||
// value in the denominator. Note that here I3b < 0.
|
||||
d = - I3b * 0.1;
|
||||
}
|
||||
|
||||
const double c = std::pow(d, -2.0/3.0);
|
||||
|
||||
return ie.Get_I1() * c / 3.0;
|
||||
}
|
||||
|
||||
void TMOP_Metric_313::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{
|
||||
MFEM_ABORT("Metric not implemented yet.");
|
||||
}
|
||||
|
||||
void TMOP_Metric_313::AssembleH(const DenseMatrix &Jpt,
|
||||
const DenseMatrix &DS,
|
||||
const double weight,
|
||||
DenseMatrix &A) const
|
||||
{
|
||||
MFEM_ABORT("Metric not implemented yet.");
|
||||
}
|
||||
|
||||
double TMOP_Metric_315::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
// mu_315 = mu_15_3D = (det(J) - 1)^2
|
||||
@@ -800,6 +929,84 @@ void TMOP_Metric_352::AssembleH(const DenseMatrix &Jpt,
|
||||
ie.Assemble_ddI3b(weight*(c - 0.5*c*c), A.GetData());
|
||||
}
|
||||
|
||||
double TMOP_AMetric_011::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
MFEM_VERIFY(Jtr != NULL,
|
||||
"Requires a target Jacobian, use SetTargetJacobian().");
|
||||
|
||||
int dim = Jpt.Size();
|
||||
|
||||
DenseMatrix Jpr(dim, dim);
|
||||
Mult(Jpt, *Jtr, Jpr);
|
||||
|
||||
double alpha = Jpr.Det(),
|
||||
omega = Jtr->Det();
|
||||
|
||||
DenseMatrix AdjAt(dim), WtW(dim), WRK(dim), Jtrt(dim);
|
||||
CalcAdjugateTranspose(Jpr, AdjAt);
|
||||
Jtrt.Transpose(*Jtr);
|
||||
MultAAt(Jtrt, WtW);
|
||||
WtW *= 1./omega;
|
||||
Mult(AdjAt, WtW, WRK);
|
||||
|
||||
WRK -= Jpr;
|
||||
WRK *= -1.;
|
||||
|
||||
return (0.25/alpha)*WRK.FNorm2();
|
||||
}
|
||||
|
||||
double TMOP_AMetric_014a::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
MFEM_VERIFY(Jtr != NULL,
|
||||
"Requires a target Jacobian, use SetTargetJacobian().");
|
||||
|
||||
int dim = Jpt.Size();
|
||||
|
||||
DenseMatrix Jpr(dim, dim);
|
||||
Mult(Jpt, *Jtr, Jpr);
|
||||
|
||||
double sqalpha = pow(Jpr.Det(), 0.5),
|
||||
sqomega = pow(Jtr->Det(), 0.5);
|
||||
|
||||
return 0.5*pow(sqalpha/sqomega - sqomega/sqalpha, 2.);
|
||||
}
|
||||
|
||||
double TMOP_AMetric_036::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
MFEM_VERIFY(Jtr != NULL,
|
||||
"Requires a target Jacobian, use SetTargetJacobian().");
|
||||
|
||||
int dim = Jpt.Size();
|
||||
|
||||
DenseMatrix Jpr(dim, dim);
|
||||
Mult(Jpt, *Jtr, Jpr); // T*W = A
|
||||
|
||||
double alpha = Jpr.Det(); // det(A)
|
||||
Jpr -= *Jtr; // A-W
|
||||
|
||||
return (1./alpha)*(Jpr.FNorm2()); //(1/alpha)*(|A-W|^2)
|
||||
}
|
||||
|
||||
double TMOP_AMetric_107a::EvalW(const DenseMatrix &Jpt) const
|
||||
{
|
||||
MFEM_VERIFY(Jtr != NULL,
|
||||
"Requires a target Jacobian, use SetTargetJacobian().");
|
||||
|
||||
int dim = Jpt.Size();
|
||||
|
||||
DenseMatrix Jpr(dim, dim);
|
||||
Mult(Jpt, *Jtr, Jpr);
|
||||
|
||||
double alpha = Jpr.Det(),
|
||||
aw = Jpr.FNorm()/Jtr->FNorm();
|
||||
|
||||
DenseMatrix W = *Jtr;
|
||||
W *= aw;
|
||||
Jpr -= W;
|
||||
|
||||
return (0.5/alpha)*Jpr.FNorm2();
|
||||
}
|
||||
|
||||
|
||||
void TargetConstructor::ComputeAvgVolume() const
|
||||
{
|
||||
|
||||
+237
-41
@@ -40,7 +40,7 @@ public:
|
||||
The specified Jacobian matrix, #Jtr, can be used by metrics that cannot
|
||||
be written just as a function of the target->physical Jacobian matrix,
|
||||
Jpt. */
|
||||
void SetTargetJacobian(const DenseMatrix &_Jtr) { Jtr = &_Jtr; }
|
||||
virtual void SetTargetJacobian(const DenseMatrix &_Jtr) { Jtr = &_Jtr; }
|
||||
|
||||
/** @brief Evaluate the strain energy density function, W = W(Jpt).
|
||||
@param[in] Jpt Represents the target->physical transformation
|
||||
@@ -70,8 +70,37 @@ public:
|
||||
const double weight, DenseMatrix &A) const = 0;
|
||||
};
|
||||
|
||||
/// Abstract class used to define combination of metrics with constant coefficients.
|
||||
class TMOP_Combo_QualityMetric : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
Array<TMOP_QualityMetric *> tmop_q_arr; //not owned
|
||||
Array<double> wt_arr;
|
||||
|
||||
/// Metric without a type, 2D
|
||||
public:
|
||||
virtual void AddQualityMetric(TMOP_QualityMetric *tq, double wt = 1.0)
|
||||
{
|
||||
tmop_q_arr.Append(tq);
|
||||
wt_arr.Append(wt);
|
||||
}
|
||||
|
||||
virtual void SetTargetJacobian(const DenseMatrix &_Jtr)
|
||||
{
|
||||
for (int i = 0; i < tmop_q_arr.Size(); i++)
|
||||
{
|
||||
tmop_q_arr[i]->SetTargetJacobian(_Jtr);
|
||||
}
|
||||
}
|
||||
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// 2D non-barrier metric without a type.
|
||||
class TMOP_Metric_001 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -87,7 +116,7 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Skew metric, 2D.
|
||||
/// 2D non-barrier Skew metric.
|
||||
class TMOP_Metric_skew2D : public TMOP_QualityMetric
|
||||
{
|
||||
public:
|
||||
@@ -102,7 +131,7 @@ public:
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// Skew metric, 3D.
|
||||
/// 3D non-barrier Skew metric.
|
||||
class TMOP_Metric_skew3D : public TMOP_QualityMetric
|
||||
{
|
||||
public:
|
||||
@@ -117,7 +146,7 @@ public:
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// Aspect ratio metric, 2D.
|
||||
/// 2D non-barrier Aspect ratio metric.
|
||||
class TMOP_Metric_aspratio2D : public TMOP_QualityMetric
|
||||
{
|
||||
public:
|
||||
@@ -132,7 +161,7 @@ public:
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// Aspect ratio metric, 3D.
|
||||
/// 3D non-barrier Aspect ratio metric.
|
||||
class TMOP_Metric_aspratio3D : public TMOP_QualityMetric
|
||||
{
|
||||
public:
|
||||
@@ -147,22 +176,7 @@ public:
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// Shape+Size+Orientation metric, 2D.
|
||||
class TMOP_Metric_SSA2D : public TMOP_QualityMetric
|
||||
{
|
||||
public:
|
||||
// W = 0.5 (1 - cos(theta_Jpr - theta_Jtr)).
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// Shape, ideal barrier metric, 2D
|
||||
/// 2D barrier shape (S) metric (polyconvex).
|
||||
class TMOP_Metric_002 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -178,7 +192,7 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Shape & area, ideal barrier metric, 2D
|
||||
/// 2D barrier Shape+Size (VS) metric (not polyconvex).
|
||||
class TMOP_Metric_007 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -194,7 +208,7 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Shape & area metric, 2D
|
||||
/// 2D barrier Shape+Size (VS) metric (not polyconvex).
|
||||
class TMOP_Metric_009 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -210,15 +224,30 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Shifted barrier form of metric 2 (shape, ideal barrier metric), 2D
|
||||
/// 2D non-barrier Shape+Size+Orientation (VOS) metric (polyconvex).
|
||||
class TMOP_Metric_014 : public TMOP_QualityMetric
|
||||
{
|
||||
public:
|
||||
// W = |T-I|^2.
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// 2D Shifted barrier form of shape metric (mu_2).
|
||||
class TMOP_Metric_022 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
double &tau0;
|
||||
double &min_detT;
|
||||
mutable InvariantsEvaluator2D<double> ie;
|
||||
|
||||
public:
|
||||
TMOP_Metric_022(double &t0): tau0(t0) {}
|
||||
TMOP_Metric_022(double &t0): min_detT(t0) {}
|
||||
|
||||
// W = 0.5(|J|^2 - 2det(J)) / (det(J) - tau0).
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
@@ -229,7 +258,7 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Shape, ideal barrier metric, 2D
|
||||
/// 2D barrier (not a shape) metric (polyconvex).
|
||||
class TMOP_Metric_050 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -245,7 +274,7 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Area metric, 2D
|
||||
/// 2D non-barrier size (V) metric (not polyconvex).
|
||||
class TMOP_Metric_055 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -262,7 +291,7 @@ public:
|
||||
|
||||
};
|
||||
|
||||
/// Area, ideal barrier metric, 2D
|
||||
/// 2D barrier size (V) metric (polyconvex).
|
||||
class TMOP_Metric_056 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -281,7 +310,7 @@ public:
|
||||
|
||||
};
|
||||
|
||||
/// Shape, ideal barrier metric, 2D
|
||||
/// 2D barrier shape (S) metric (not polyconvex).
|
||||
class TMOP_Metric_058 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -299,7 +328,7 @@ public:
|
||||
|
||||
};
|
||||
|
||||
/// Area, ideal barrier metric, 2D
|
||||
/// 2D barrier size (V) metric (polyconvex).
|
||||
class TMOP_Metric_077 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -316,7 +345,28 @@ public:
|
||||
|
||||
};
|
||||
|
||||
/// Shape & orientation metric, 2D.
|
||||
/// 2D barrier Shape+Size (VS) metric (polyconvex).
|
||||
class TMOP_Metric_080 : public TMOP_Combo_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator2D<double> ie;
|
||||
double gamma;
|
||||
TMOP_QualityMetric *sh_metric, *sz_metric;
|
||||
|
||||
public:
|
||||
TMOP_Metric_080(double gamma_) : gamma(gamma_),
|
||||
sh_metric(new TMOP_Metric_002),
|
||||
sz_metric(new TMOP_Metric_077)
|
||||
{
|
||||
// (1-gamma) mu_2 + gamma mu_77
|
||||
AddQualityMetric(sh_metric, 1.-gamma_);
|
||||
AddQualityMetric(sz_metric, gamma_);
|
||||
}
|
||||
|
||||
virtual ~TMOP_Metric_080() { delete sh_metric; delete sz_metric; }
|
||||
};
|
||||
|
||||
/// 2D barrier Shape+Orientation (OS) metric (polyconvex).
|
||||
class TMOP_Metric_085 : public TMOP_QualityMetric
|
||||
{
|
||||
public:
|
||||
@@ -331,7 +381,22 @@ public:
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// Untangling metric, 2D
|
||||
/// 2D barrier Shape+Size+Orientation (VOS) metric (polyconvex).
|
||||
class TMOP_Metric_098 : public TMOP_QualityMetric
|
||||
{
|
||||
public:
|
||||
// W = 1/tau |T-I|^2.
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// 2D untangling metric.
|
||||
class TMOP_Metric_211 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -370,7 +435,7 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Shape, ideal barrier metric, 3D
|
||||
/// 3D barrier Shape (S) metric.
|
||||
class TMOP_Metric_301 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -386,7 +451,7 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Shape, ideal barrier metric, 3D
|
||||
/// 3D barrier Shape (S) metric.
|
||||
class TMOP_Metric_302 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -402,14 +467,14 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Shape, ideal barrier metric, 3D
|
||||
/// 3D barrier Shape (S) metric.
|
||||
class TMOP_Metric_303 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator3D<double> ie;
|
||||
|
||||
public:
|
||||
// W = |J|^2 / 3 * det(J)^(2/3) - 1.
|
||||
// W = |J|^2 / 3 * det(J)^(-2/3) - 1.
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
|
||||
@@ -418,7 +483,45 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Volume metric, 3D
|
||||
/// 3D Size (V) untangling metric.
|
||||
class TMOP_Metric_311 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
const double eps;
|
||||
mutable InvariantsEvaluator3D<double> ie;
|
||||
|
||||
public:
|
||||
TMOP_Metric_311(double epsilon = 1e-4) : eps(epsilon) { }
|
||||
|
||||
// W = (det(J) - 1)^2 - det(J) + (det(J)^2 + eps)^(1/2).
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// 3D Shape (S) metric, untangling version of 303.
|
||||
class TMOP_Metric_313 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
double &min_detT;
|
||||
mutable InvariantsEvaluator3D<double> ie;
|
||||
|
||||
public:
|
||||
TMOP_Metric_313(double &mindet) : min_detT(mindet) { }
|
||||
|
||||
// W = 1/3 |J|^2 / [det(J)-tau0]^(-2/3).
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// 3D non-barrier Size (V) metric.
|
||||
class TMOP_Metric_315 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -434,7 +537,7 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Volume, ideal barrier metric, 3D
|
||||
/// 3D barrier Size (V) metric.
|
||||
class TMOP_Metric_316 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -452,7 +555,7 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// Shape & volume, ideal barrier metric, 3D
|
||||
/// 3D barrier Shape+Size (VS) metric.
|
||||
class TMOP_Metric_321 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
@@ -487,6 +590,99 @@ public:
|
||||
const double weight, DenseMatrix &A) const;
|
||||
};
|
||||
|
||||
/// A-metrics
|
||||
/// 2D barrier Shape (S) metric (polyconvex).
|
||||
class TMOP_AMetric_011 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator3D<double> ie;
|
||||
|
||||
public:
|
||||
// (1/4 alpha) | A - (adj A)^t W^t W / omega |^2
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// 2D barrier Size (V) metric (polyconvex).
|
||||
class TMOP_AMetric_014a : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator3D<double> ie;
|
||||
|
||||
public:
|
||||
// 0.5 * ( sqrt(alpha/omega) - sqrt(omega/alpha) )^2
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// 2D barrier Shape+Size+Orientation (VOS) metric (polyconvex).
|
||||
class TMOP_AMetric_036 : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator3D<double> ie;
|
||||
|
||||
public:
|
||||
// (1/alpha) | A - W |^2
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// 2D barrier Shape+Orientation (OS) metric (polyconvex).
|
||||
class TMOP_AMetric_107a : public TMOP_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator3D<double> ie;
|
||||
|
||||
public:
|
||||
// (1/2 alpha) | A - (|A|/|W|) W |^2
|
||||
virtual double EvalW(const DenseMatrix &Jpt) const;
|
||||
|
||||
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
|
||||
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
|
||||
const double weight, DenseMatrix &A) const
|
||||
{ MFEM_ABORT("Not implemented"); }
|
||||
};
|
||||
|
||||
/// 2D barrier Shape+Size (VS) metric (polyconvex).
|
||||
class TMOP_AMetric_126 : public TMOP_Combo_QualityMetric
|
||||
{
|
||||
protected:
|
||||
mutable InvariantsEvaluator2D<double> ie;
|
||||
double gamma;
|
||||
TMOP_QualityMetric *sh_metric, *sz_metric;
|
||||
|
||||
public:
|
||||
TMOP_AMetric_126(double gamma_) : gamma(gamma_),
|
||||
sh_metric(new TMOP_AMetric_011),
|
||||
sz_metric(new TMOP_AMetric_014a)
|
||||
{
|
||||
// (1-gamma) nu_11 + gamma nu_14
|
||||
AddQualityMetric(sh_metric, 1.-gamma_);
|
||||
AddQualityMetric(sz_metric, gamma_);
|
||||
}
|
||||
|
||||
virtual ~TMOP_AMetric_126() { delete sh_metric; delete sz_metric; }
|
||||
};
|
||||
|
||||
/// Base class for limiting functions to be used in class TMOP_Integrator.
|
||||
/** This class represents a scalar function f(x, x0, d), where x and x0 are
|
||||
|
||||
+115
-103
@@ -331,10 +331,6 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
energy_in = nlf->GetEnergy(x);
|
||||
}
|
||||
|
||||
const int NE = fes->GetMesh()->GetNE(), dim = fes->GetMesh()->Dimension();
|
||||
Array<int> xdofs;
|
||||
DenseMatrix Jpr(dim);
|
||||
|
||||
// Get the local prolongation of the solution vector.
|
||||
Vector x_out_loc(fes->GetVSize());
|
||||
if (serial)
|
||||
@@ -350,51 +346,32 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check if the starting mesh (given by x) is inverted.
|
||||
// Note that x hasn't been modified by the Newton update yet.
|
||||
double min_detJ = infinity();
|
||||
for (int i = 0; i < NE; i++)
|
||||
// Check if the starting mesh (given by x) is inverted. Note that x hasn't
|
||||
// been modified by the Newton update yet.
|
||||
const double min_detT_in = ComputeMinDet(x_out_loc, *fes);
|
||||
const bool untangling = (min_detT_in <= 0.0) ? true : false;
|
||||
const double untangle_factor = 1.5;
|
||||
if (untangling)
|
||||
{
|
||||
const int dof = fes->GetFE(i)->GetDof();
|
||||
DenseMatrix dshape(dof, dim), pos(dof, dim);
|
||||
Vector posV(pos.Data(), dof * dim);
|
||||
|
||||
fes->GetElementVDofs(i, xdofs);
|
||||
x_out_loc.GetSubVector(xdofs, posV);
|
||||
|
||||
const IntegrationRule &irule = GetIntegrationRule(*fes->GetFE(i));
|
||||
const int nsp = irule.GetNPoints();
|
||||
for (int j = 0; j < nsp; j++)
|
||||
{
|
||||
fes->GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
|
||||
MultAtB(pos, dshape, Jpr);
|
||||
min_detJ = std::min(min_detJ, Jpr.Det());
|
||||
}
|
||||
// Needed for the line search below. The untangling metrics see this
|
||||
// reference to detect deteriorations.
|
||||
*min_det_ptr = untangle_factor * min_detT_in;
|
||||
}
|
||||
double min_detJ_all = min_detJ;
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
MPI_Allreduce(&min_detJ, &min_detJ_all, 1, MPI_DOUBLE, MPI_MIN,
|
||||
p_nlf->ParFESpace()->GetComm());
|
||||
}
|
||||
#endif
|
||||
const bool untangling = (min_detJ_all <= 0) ? true : false;
|
||||
|
||||
const bool have_b = (b.Size() == Height());
|
||||
|
||||
Vector x_out(x.Size());
|
||||
bool x_out_ok = false;
|
||||
double scale = 1.0, energy_out = 0.0;
|
||||
const double norm0 = Norm(r);
|
||||
double scale = 1.0, energy_out = 0.0, min_detT_out;
|
||||
const double norm_in = Norm(r);
|
||||
|
||||
const double detJ_factor = (solver_type == 1) ? 0.25 : 0.5;
|
||||
|
||||
// Perform the line search.
|
||||
for (int i = 0; i < 12; i++)
|
||||
{
|
||||
// Update the mesh and get the L-vector in x_out_loc.
|
||||
add(x, -scale, c, x_out);
|
||||
|
||||
if (serial)
|
||||
{
|
||||
const SparseMatrix *cP = fes->GetConformingProlongation();
|
||||
@@ -408,47 +385,29 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check det(Jpr) > 0.
|
||||
if (!untangling)
|
||||
// Check the changes in detJ.
|
||||
min_detT_out = ComputeMinDet(x_out_loc, *fes);
|
||||
if (untangling == false && min_detT_out < 0.0)
|
||||
{
|
||||
int jac_ok = 1;
|
||||
for (int i = 0; i < NE; i++)
|
||||
{
|
||||
const int dof = fes->GetFE(i)->GetDof();
|
||||
DenseMatrix dshape(dof, dim), pos(dof, dim);
|
||||
Vector posV(pos.Data(), dof * dim);
|
||||
// No untangling, and detJ got negative -- no good.
|
||||
if (print_level >= 0)
|
||||
{ mfem::out << "Scale = " << scale << " Neg det(J) found.\n"; }
|
||||
scale *= detJ_factor; continue;
|
||||
}
|
||||
if (untangling == true && min_detT_out < *min_det_ptr)
|
||||
{
|
||||
// Untangling, and detJ got even more negative -- no good.
|
||||
if (print_level >= 0)
|
||||
{ mfem::out << "Scale = " << scale << " Neg det(J) decreased.\n"; }
|
||||
scale *= detJ_factor; continue;
|
||||
}
|
||||
|
||||
fes->GetElementVDofs(i, xdofs);
|
||||
x_out_loc.GetSubVector(xdofs, posV);
|
||||
|
||||
const IntegrationRule &irule = GetIntegrationRule(*fes->GetFE(i));
|
||||
const int nsp = irule.GetNPoints();
|
||||
for (int j = 0; j < nsp; j++)
|
||||
{
|
||||
fes->GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
|
||||
MultAtB(pos, dshape, Jpr);
|
||||
if (Jpr.Det() <= 0.0) { jac_ok = 0; goto break2; }
|
||||
}
|
||||
}
|
||||
|
||||
break2:
|
||||
int jac_ok_all = jac_ok;
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
MPI_Allreduce(&jac_ok, &jac_ok_all, 1, MPI_INT, MPI_LAND,
|
||||
p_nlf->ParFESpace()->GetComm());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (jac_ok_all == 0)
|
||||
{
|
||||
if (print_level >= 0)
|
||||
{ mfem::out << "Scale = " << scale << " Neg det(J) found.\n"; }
|
||||
scale *= detJ_factor; continue;
|
||||
}
|
||||
} // endif(!untangling)
|
||||
// Skip the energy and residual checks when we're untangling. The
|
||||
// untangling metrics change their denominators, which can affect the
|
||||
// energy and residual, so their increase/decrease is not relevant.
|
||||
if (untangling) { x_out_ok = true; break; }
|
||||
|
||||
// Check the changes in total energy.
|
||||
ProcessNewState(x_out);
|
||||
if (serial)
|
||||
{
|
||||
@@ -460,43 +419,55 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
energy_out = p_nlf->GetParGridFunctionEnergy(x_out_loc);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (untangling)
|
||||
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
|
||||
{
|
||||
if (energy_out > energy_in || std::isnan(energy_out) != 0)
|
||||
if (print_level >= 0)
|
||||
{
|
||||
scale *= 0.5;
|
||||
mfem::out << "Scale = " << scale << " Increasing energy.\n";
|
||||
}
|
||||
else { x_out_ok = true; break; }
|
||||
scale *= 0.5; continue;
|
||||
}
|
||||
else
|
||||
|
||||
// Check the changes in the Newton residual.
|
||||
oper->Mult(x_out, r);
|
||||
if (have_b) { r -= b; }
|
||||
double norm_out = Norm(r);
|
||||
|
||||
if (norm_out > 1.2*norm_in)
|
||||
{
|
||||
if (energy_out > 1.2*energy_in || std::isnan(energy_out) != 0)
|
||||
{
|
||||
if (print_level >= 0)
|
||||
{ mfem::out << "Scale = " << scale << " Increasing energy.\n"; }
|
||||
scale *= 0.5; continue;
|
||||
}
|
||||
if (print_level >= 0)
|
||||
{ mfem::out << "Scale = " << scale << " Norm increased.\n"; }
|
||||
scale *= 0.5; continue;
|
||||
}
|
||||
else { x_out_ok = true; break; }
|
||||
} // end line search
|
||||
|
||||
oper->Mult(x_out, r);
|
||||
if (have_b) { r -= b; }
|
||||
double norm = Norm(r);
|
||||
|
||||
if (norm > 1.2*norm0)
|
||||
{
|
||||
if (print_level >= 0)
|
||||
{ mfem::out << "Scale = " << scale << " Norm increased.\n"; }
|
||||
scale *= 0.5; continue;
|
||||
}
|
||||
else { x_out_ok = true; break; }
|
||||
} // endif (untangling)
|
||||
} // enddo (i)
|
||||
if (untangling)
|
||||
{
|
||||
// Update the global min detJ. Untangling metrics see this min_det_ptr.
|
||||
if (min_detT_out > 0.0)
|
||||
{
|
||||
*min_det_ptr = 0.0;
|
||||
if (print_level >= 0)
|
||||
{ mfem::out << "The mesh has been untangled at the used points!\n"; }
|
||||
}
|
||||
else { *min_det_ptr = untangle_factor * min_detT_out; }
|
||||
}
|
||||
|
||||
if (print_level >= 0)
|
||||
{
|
||||
mfem::out << "Energy decrease: "
|
||||
<< (energy_in - energy_out) / energy_in * 100.0
|
||||
<< "% with " << scale << " scaling.\n";
|
||||
if (untangling)
|
||||
{
|
||||
mfem::out << "Min det(T) change: "
|
||||
<< min_detT_in << " -> " << min_detT_out
|
||||
<< " with " << scale << " scaling.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::out << "Energy decrease: "
|
||||
<< (energy_in - energy_out) / energy_in * 100.0
|
||||
<< "% with " << scale << " scaling.\n";
|
||||
}
|
||||
}
|
||||
|
||||
if (x_out_ok == false) { scale = 0.0; }
|
||||
@@ -508,8 +479,8 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
|
||||
const NonlinearForm *nlf = dynamic_cast<const NonlinearForm *>(oper);
|
||||
const Array<NonlinearFormIntegrator*> &integs = *nlf->GetDNFI();
|
||||
|
||||
// Reset the update flags of all TargetConstructors.
|
||||
// This is done to avoid repeated updates of shared TargetConstructors.
|
||||
// Reset the update flags of all TargetConstructors. This is done to avoid
|
||||
// repeated updates of shared TargetConstructors.
|
||||
TMOP_Integrator *ti = NULL;
|
||||
TMOPComboIntegrator *co = NULL;
|
||||
DiscreteAdaptTC *dtc = NULL;
|
||||
@@ -617,6 +588,47 @@ void TMOPNewtonSolver::UpdateDiscreteTC(const TMOP_Integrator &ti,
|
||||
}
|
||||
}
|
||||
|
||||
double TMOPNewtonSolver::ComputeMinDet(const Vector &x_loc,
|
||||
const FiniteElementSpace &fes) const
|
||||
{
|
||||
double min_detJ = infinity();
|
||||
const int NE = fes.GetNE(), dim = fes.GetMesh()->Dimension();
|
||||
Array<int> xdofs;
|
||||
DenseMatrix Jpr(dim);
|
||||
for (int i = 0; i < NE; i++)
|
||||
{
|
||||
const int dof = fes.GetFE(i)->GetDof();
|
||||
DenseMatrix dshape(dof, dim), pos(dof, dim);
|
||||
Vector posV(pos.Data(), dof * dim);
|
||||
|
||||
fes.GetElementVDofs(i, xdofs);
|
||||
x_loc.GetSubVector(xdofs, posV);
|
||||
|
||||
const IntegrationRule &irule = GetIntegrationRule(*fes.GetFE(i));
|
||||
const int nsp = irule.GetNPoints();
|
||||
for (int j = 0; j < nsp; j++)
|
||||
{
|
||||
fes.GetFE(i)->CalcDShape(irule.IntPoint(j), dshape);
|
||||
MultAtB(pos, dshape, Jpr);
|
||||
min_detJ = std::min(min_detJ, Jpr.Det());
|
||||
}
|
||||
}
|
||||
double min_detT_all = min_detJ;
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (parallel)
|
||||
{
|
||||
auto p_nlf = dynamic_cast<const ParNonlinearForm *>(oper);
|
||||
MPI_Allreduce(&min_detJ, &min_detT_all, 1, MPI_DOUBLE, MPI_MIN,
|
||||
p_nlf->ParFESpace()->GetComm());
|
||||
}
|
||||
#endif
|
||||
const DenseMatrix &Wideal =
|
||||
Geometries.GetGeomToPerfGeomJac(fes.GetFE(0)->GetGeomType());
|
||||
min_detT_all /= Wideal.Det();
|
||||
|
||||
return min_detT_all;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Metric values are visualized by creating an L2 finite element functions and
|
||||
// computing the metric values at the nodes.
|
||||
|
||||
@@ -114,6 +114,9 @@ protected:
|
||||
int solver_type;
|
||||
bool parallel;
|
||||
|
||||
// Minimum determinant over the whole mesh. Used for mesh untangling.
|
||||
double *min_det_ptr = nullptr;
|
||||
|
||||
// Quadrature points that are checked for negative Jacobians etc.
|
||||
const IntegrationRule &ir;
|
||||
// These fields are relevant for mixed meshes.
|
||||
@@ -131,6 +134,9 @@ protected:
|
||||
|
||||
void UpdateDiscreteTC(const TMOP_Integrator &ti, const Vector &x_new) const;
|
||||
|
||||
double ComputeMinDet(const Vector &x_loc,
|
||||
const FiniteElementSpace &fes) const;
|
||||
|
||||
public:
|
||||
#ifdef MFEM_USE_MPI
|
||||
TMOPNewtonSolver(MPI_Comm comm, const IntegrationRule &irule, int type = 0)
|
||||
@@ -150,6 +156,8 @@ public:
|
||||
integ_order = order;
|
||||
}
|
||||
|
||||
void SetMinDetPtr(double *md_ptr) { min_det_ptr = md_ptr; }
|
||||
|
||||
virtual double ComputeScalingFactor(const Vector &x, const Vector &b) const;
|
||||
|
||||
virtual void ProcessNewState(const Vector &x) const;
|
||||
|
||||
+6
-3
@@ -43,7 +43,7 @@ CeedRestrMap ceed_restr_map;
|
||||
static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
|
||||
{
|
||||
Backend::CEED_CUDA, Backend::OCCA_CUDA, Backend::RAJA_CUDA, Backend::CUDA,
|
||||
Backend::CEED_HIP, Backend::HIP, Backend::DEBUG_DEVICE,
|
||||
Backend::CEED_HIP, Backend::RAJA_HIP, Backend::HIP, Backend::DEBUG_DEVICE,
|
||||
Backend::OCCA_OMP, Backend::RAJA_OMP, Backend::OMP,
|
||||
Backend::CEED_CPU, Backend::OCCA_CPU, Backend::RAJA_CPU, Backend::CPU
|
||||
};
|
||||
@@ -52,7 +52,7 @@ static const Backend::Id backend_list[Backend::NUM_BACKENDS] =
|
||||
static const char *backend_name[Backend::NUM_BACKENDS] =
|
||||
{
|
||||
"ceed-cuda", "occa-cuda", "raja-cuda", "cuda",
|
||||
"ceed-hip", "hip", "debug",
|
||||
"ceed-hip", "raja-hip", "hip", "debug",
|
||||
"occa-omp", "raja-omp", "omp",
|
||||
"ceed-cpu", "occa-cpu", "raja-cpu", "cpu"
|
||||
};
|
||||
@@ -394,6 +394,8 @@ static void RajaDeviceSetup(const int dev, int &ngpu)
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
if (ngpu <= 0) { DeviceSetup(dev, ngpu); }
|
||||
#elif defined(MFEM_USE_HIP)
|
||||
HipDeviceSetup(dev, ngpu);
|
||||
#else
|
||||
MFEM_CONTRACT_VAR(dev);
|
||||
MFEM_CONTRACT_VAR(ngpu);
|
||||
@@ -507,7 +509,8 @@ void Device::Setup(const int device)
|
||||
#endif
|
||||
if (Allows(Backend::CUDA)) { CudaDeviceSetup(dev, ngpu); }
|
||||
if (Allows(Backend::HIP)) { HipDeviceSetup(dev, ngpu); }
|
||||
if (Allows(Backend::RAJA_CUDA)) { RajaDeviceSetup(dev, ngpu); }
|
||||
if (Allows(Backend::RAJA_CUDA) || Allows(Backend::RAJA_HIP))
|
||||
{ RajaDeviceSetup(dev, ngpu); }
|
||||
// The check for MFEM_USE_OCCA is in the function OccaDeviceSetup().
|
||||
if (Allows(Backend::OCCA_MASK)) { OccaDeviceSetup(dev); }
|
||||
if (Allows(Backend::CEED_CPU))
|
||||
|
||||
+13
-10
@@ -46,30 +46,33 @@ struct Backend
|
||||
/** @brief [device] RAJA CUDA backend. Enabled when MFEM_USE_RAJA = YES
|
||||
and MFEM_USE_CUDA = YES. */
|
||||
RAJA_CUDA = 1 << 6,
|
||||
/** @brief [device] RAJA HIP backend. Enabled when MFEM_USE_RAJA = YES
|
||||
and MFEM_USE_HIP = YES. */
|
||||
RAJA_HIP = 1 << 7,
|
||||
/** @brief [host] OCCA CPU backend: sequential execution on each MPI rank.
|
||||
Enabled when MFEM_USE_OCCA = YES. */
|
||||
OCCA_CPU = 1 << 7,
|
||||
OCCA_CPU = 1 << 8,
|
||||
/// [host] OCCA OpenMP backend. Enabled when MFEM_USE_OCCA = YES.
|
||||
OCCA_OMP = 1 << 8,
|
||||
OCCA_OMP = 1 << 9,
|
||||
/** @brief [device] OCCA CUDA backend. Enabled when MFEM_USE_OCCA = YES
|
||||
and MFEM_USE_CUDA = YES. */
|
||||
OCCA_CUDA = 1 << 9,
|
||||
OCCA_CUDA = 1 << 10,
|
||||
/** @brief [host] CEED CPU backend. GPU backends can still be used, but
|
||||
with expensive memory transfers. Enabled when MFEM_USE_CEED = YES. */
|
||||
CEED_CPU = 1 << 10,
|
||||
CEED_CPU = 1 << 11,
|
||||
/** @brief [device] CEED CUDA backend working together with the CUDA
|
||||
backend. Enabled when MFEM_USE_CEED = YES and MFEM_USE_CUDA = YES.
|
||||
NOTE: The current default libCEED CUDA backend is non-deterministic! */
|
||||
CEED_CUDA = 1 << 11,
|
||||
CEED_CUDA = 1 << 12,
|
||||
/** @brief [device] CEED HIP backend working together with the HIP
|
||||
backend. Enabled when MFEM_USE_CEED = YES and MFEM_USE_HIP = YES. */
|
||||
CEED_HIP = 1 << 12,
|
||||
CEED_HIP = 1 << 13,
|
||||
/** @brief [device] Debug backend: host memory is READ/WRITE protected
|
||||
while a device is in use. It allows to test the "device" code-path
|
||||
(using separate host/device memory pools and host <-> device
|
||||
transfers) without any GPU hardware. As 'DEBUG' is sometimes used
|
||||
as a macro, `_DEVICE` has been added to avoid conflicts. */
|
||||
DEBUG_DEVICE = 1 << 13
|
||||
DEBUG_DEVICE = 1 << 14
|
||||
};
|
||||
|
||||
/** @brief Additional useful constants. For example, the *_MASK constants can
|
||||
@@ -77,14 +80,14 @@ struct Backend
|
||||
enum
|
||||
{
|
||||
/// Number of backends: from (1 << 0) to (1 << (NUM_BACKENDS-1)).
|
||||
NUM_BACKENDS = 14,
|
||||
NUM_BACKENDS = 15,
|
||||
|
||||
/// Biwise-OR of all CPU backends
|
||||
CPU_MASK = CPU | RAJA_CPU | OCCA_CPU | CEED_CPU,
|
||||
/// Biwise-OR of all CUDA backends
|
||||
CUDA_MASK = CUDA | RAJA_CUDA | OCCA_CUDA | CEED_CUDA,
|
||||
/// Biwise-OR of all HIP backends
|
||||
HIP_MASK = HIP | CEED_HIP,
|
||||
HIP_MASK = HIP | RAJA_HIP | CEED_HIP,
|
||||
/// Biwise-OR of all OpenMP backends
|
||||
OMP_MASK = OMP | RAJA_OMP | OCCA_OMP,
|
||||
/// Bitwise-OR of all CEED backends
|
||||
@@ -93,7 +96,7 @@ struct Backend
|
||||
DEVICE_MASK = CUDA_MASK | HIP_MASK | DEBUG_DEVICE,
|
||||
|
||||
/// Biwise-OR of all RAJA backends
|
||||
RAJA_MASK = RAJA_CPU | RAJA_OMP | RAJA_CUDA,
|
||||
RAJA_MASK = RAJA_CPU | RAJA_OMP | RAJA_CUDA | RAJA_HIP,
|
||||
/// Biwise-OR of all OCCA backends
|
||||
OCCA_MASK = OCCA_CPU | OCCA_OMP | OCCA_CUDA
|
||||
};
|
||||
|
||||
+134
-44
@@ -86,77 +86,157 @@ void OmpWrap(const int N, HBODY &&h_body)
|
||||
}
|
||||
|
||||
|
||||
/// RAJA Cuda backend
|
||||
/// RAJA Cuda and Hip backends
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA)
|
||||
|
||||
#if RAJA_VERSION_MAJOR == 0 && RAJA_VERSION_MINOR < 12
|
||||
using RAJA::statement::Segs;
|
||||
#else
|
||||
using RAJA::Segs;
|
||||
using cuda_launch_policy =
|
||||
RAJA::expt::LaunchPolicy<RAJA::expt::null_launch_t, RAJA::expt::cuda_launch_t<false>>;
|
||||
using cuda_teams_x =
|
||||
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::cuda_block_x_direct>;
|
||||
using cuda_threads_z =
|
||||
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::cuda_thread_z_direct>;
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
|
||||
using hip_launch_policy =
|
||||
RAJA::expt::LaunchPolicy<RAJA::expt::null_launch_t, RAJA::expt::hip_launch_t<false>>;
|
||||
using hip_teams_x =
|
||||
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::hip_block_x_direct>;
|
||||
using hip_threads_z =
|
||||
RAJA::expt::LoopPolicy<RAJA::loop_exec,RAJA::hip_thread_z_direct>;
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA)
|
||||
template <const int BLOCKS = MFEM_CUDA_BLOCKS, typename DBODY>
|
||||
void RajaCudaWrap1D(const int N, DBODY &&d_body)
|
||||
void RajaCuWrap1D(const int N, DBODY &&d_body)
|
||||
{
|
||||
// true denotes asynchronous kernel
|
||||
//true denotes asynchronous kernel
|
||||
RAJA::forall<RAJA::cuda_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
void RajaCudaWrap2D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int BZ)
|
||||
void RajaCuWrap2D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int BZ)
|
||||
{
|
||||
MFEM_VERIFY(N>0, "");
|
||||
MFEM_VERIFY(BZ>0, "");
|
||||
const int G = (N+BZ-1)/BZ;
|
||||
RAJA::kernel<RAJA::KernelPolicy<
|
||||
RAJA::statement::CudaKernelAsync<
|
||||
RAJA::statement::For<0, RAJA::cuda_block_x_direct,
|
||||
RAJA::statement::For<1, RAJA::cuda_thread_x_direct,
|
||||
RAJA::statement::For<2, RAJA::cuda_thread_y_direct,
|
||||
RAJA::statement::For<3, RAJA::cuda_thread_z_direct,
|
||||
RAJA::statement::Lambda<0, Segs<0>>>>>>>>>
|
||||
(RAJA::make_tuple(RAJA::RangeSegment(0,G), RAJA::RangeSegment(0,X),
|
||||
RAJA::RangeSegment(0,Y), RAJA::RangeSegment(0,BZ)),
|
||||
[=] RAJA_DEVICE (const int n)
|
||||
|
||||
using namespace RAJA::expt;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<cuda_launch_policy>
|
||||
(DEVICE, Resources(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE (LaunchContext ctx)
|
||||
{
|
||||
const int k = n*BZ + threadIdx.z;
|
||||
if (k >= N) { return; }
|
||||
d_body(k);
|
||||
|
||||
loop<cuda_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
|
||||
{
|
||||
|
||||
loop<cuda_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
|
||||
{
|
||||
|
||||
const int k = n*BZ + tz;
|
||||
if (k >= N) { return; }
|
||||
d_body(k);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
void RajaCudaWrap3D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z)
|
||||
void RajaCuWrap3D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z)
|
||||
{
|
||||
MFEM_VERIFY(N>0, "");
|
||||
RAJA::kernel<RAJA::KernelPolicy<
|
||||
RAJA::statement::CudaKernelAsync<
|
||||
RAJA::statement::For<0, RAJA::cuda_block_x_direct,
|
||||
RAJA::statement::For<1, RAJA::cuda_thread_x_direct,
|
||||
RAJA::statement::For<2, RAJA::cuda_thread_y_direct,
|
||||
RAJA::statement::For<3, RAJA::cuda_thread_z_direct,
|
||||
RAJA::statement::Lambda<0, Segs<0>>>>>>>>>
|
||||
(RAJA::make_tuple(RAJA::RangeSegment(0,N), RAJA::RangeSegment(0,X),
|
||||
RAJA::RangeSegment(0,Y), RAJA::RangeSegment(0,Z)),
|
||||
[=] RAJA_DEVICE (const int k) { d_body(k); });
|
||||
using namespace RAJA::expt;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<cuda_launch_policy>
|
||||
(DEVICE, Resources(Teams(N), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE (LaunchContext ctx)
|
||||
{
|
||||
|
||||
loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body);
|
||||
|
||||
});
|
||||
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
|
||||
template <const int BLOCKS = MFEM_HIP_BLOCKS, typename DBODY>
|
||||
void RajaHipWrap1D(const int N, DBODY &&d_body)
|
||||
{
|
||||
//true denotes asynchronous kernel
|
||||
RAJA::forall<RAJA::hip_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
void RajaHipWrap2D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int BZ)
|
||||
{
|
||||
MFEM_VERIFY(N>0, "");
|
||||
MFEM_VERIFY(BZ>0, "");
|
||||
const int G = (N+BZ-1)/BZ;
|
||||
|
||||
using namespace RAJA::expt;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<hip_launch_policy>
|
||||
(DEVICE, Resources(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE (LaunchContext ctx)
|
||||
{
|
||||
|
||||
loop<hip_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
|
||||
{
|
||||
|
||||
loop<hip_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
|
||||
{
|
||||
|
||||
const int k = n*BZ + tz;
|
||||
if (k >= N) { return; }
|
||||
d_body(k);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
void RajaHipWrap3D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z)
|
||||
{
|
||||
MFEM_VERIFY(N>0, "");
|
||||
using namespace RAJA::expt;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<hip_launch_policy>
|
||||
(DEVICE, Resources(Teams(N), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE (LaunchContext ctx)
|
||||
{
|
||||
|
||||
loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body);
|
||||
|
||||
});
|
||||
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
#endif
|
||||
|
||||
/// RAJA OpenMP backend
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_OPENMP)
|
||||
|
||||
#if RAJA_VERSION_MAJOR == 0 && RAJA_VERSION_MINOR < 12
|
||||
using RAJA::statement::Segs;
|
||||
#else
|
||||
using RAJA::Segs;
|
||||
#endif
|
||||
|
||||
template <typename HBODY>
|
||||
void RajaOmpWrap(const int N, HBODY &&h_body)
|
||||
{
|
||||
@@ -319,9 +399,19 @@ inline void ForallWrap(const bool use_dev, const int N,
|
||||
// If Backend::RAJA_CUDA is allowed, use it
|
||||
if (Device::Allows(Backend::RAJA_CUDA))
|
||||
{
|
||||
if (DIM == 1) { return RajaCudaWrap1D(N, d_body); }
|
||||
if (DIM == 2) { return RajaCudaWrap2D(N, d_body, X, Y, Z); }
|
||||
if (DIM == 3) { return RajaCudaWrap3D(N, d_body, X, Y, Z); }
|
||||
if (DIM == 1) { return RajaCuWrap1D(N, d_body); }
|
||||
if (DIM == 2) { return RajaCuWrap2D(N, d_body, X, Y, Z); }
|
||||
if (DIM == 3) { return RajaCuWrap3D(N, d_body, X, Y, Z); }
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP)
|
||||
// If Backend::RAJA_HIP is allowed, use it
|
||||
if (Device::Allows(Backend::RAJA_HIP))
|
||||
{
|
||||
if (DIM == 1) { return RajaHipWrap1D(N, d_body); }
|
||||
if (DIM == 2) { return RajaHipWrap2D(N, d_body, X, Y, Z); }
|
||||
if (DIM == 3) { return RajaHipWrap3D(N, d_body, X, Y, Z); }
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ void* HipMemcpyDtoDAsync(void* dst, const void *src, size_t bytes)
|
||||
void* HipMemcpyDtoH(void *dst, const void *src, size_t bytes)
|
||||
{
|
||||
#ifdef MFEM_USE_HIP
|
||||
#ifdef MFEM_TRACK_HPI_MEM
|
||||
#ifdef MFEM_TRACK_HIP_MEM
|
||||
mfem::out << "HipMemcpyDtoH(): copying " << bytes << " bytes from "
|
||||
<< src << " to " << dst << " ... " << std::flush;
|
||||
#endif
|
||||
|
||||
@@ -482,7 +482,8 @@ public:
|
||||
HostMemorySpace(),
|
||||
name(mm.GetUmpireAllocatorHostName()),
|
||||
rm(umpire::ResourceManager::getInstance()),
|
||||
h_allocator(rm.isAllocator(name)? rm.getAllocator(name):
|
||||
h_allocator((!std::strcmp(name, "HOST") || rm.isAllocator(name)) ?
|
||||
rm.getAllocator(name) :
|
||||
rm.makeAllocator<umpire::strategy::DynamicPool>
|
||||
(name, rm.getAllocator("HOST"))),
|
||||
strat(h_allocator.getAllocationStrategy()) { }
|
||||
@@ -506,7 +507,8 @@ public:
|
||||
DeviceMemorySpace(),
|
||||
name(mm.GetUmpireAllocatorDeviceName()),
|
||||
rm(umpire::ResourceManager::getInstance()),
|
||||
d_allocator(rm.isAllocator(name)? rm.getAllocator(name):
|
||||
d_allocator((!std::strcmp(name, "DEVICE") || rm.isAllocator(name)) ?
|
||||
rm.getAllocator(name) :
|
||||
rm.makeAllocator<umpire::strategy::DynamicPool>
|
||||
(name, rm.getAllocator("DEVICE"))) { }
|
||||
void Alloc(Memory &base) { base.d_ptr = d_allocator.allocate(base.bytes); }
|
||||
|
||||
+3
-12
@@ -12,6 +12,7 @@
|
||||
#ifndef MFEM_TEXT
|
||||
#define MFEM_TEXT
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include <istream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
@@ -24,6 +25,8 @@ namespace mfem
|
||||
|
||||
// Utilities for text parsing
|
||||
|
||||
using std::to_string;
|
||||
|
||||
/// Check if the stream starts with @a comment_char. If so skip it.
|
||||
inline void skip_comment_lines(std::istream &is, const char comment_char)
|
||||
{
|
||||
@@ -47,18 +50,6 @@ inline void filter_dos(std::string &line)
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an integer to an std::string.
|
||||
inline std::string to_string(int i)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << i;
|
||||
|
||||
// trim leading spaces
|
||||
std::string out_str = ss.str();
|
||||
out_str = out_str.substr(out_str.find_first_not_of(" \t"));
|
||||
return out_str;
|
||||
}
|
||||
|
||||
/// Convert an integer to a 0-padded string with the given number of @a digits
|
||||
inline std::string to_padded_string(int i, int digits)
|
||||
{
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ static std::string strerror()
|
||||
}
|
||||
#elif (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE || \
|
||||
defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
|
||||
defined(__NetBSD__) || defined(__DragonFly__)
|
||||
defined(__NetBSD__) || defined(__DragonFly__) || defined(__EMSCRIPTEN__)
|
||||
// XSI-compliant strerror_r()
|
||||
if (strerror_r(errno, &buff[0], buff.size()) != 0)
|
||||
{
|
||||
|
||||
@@ -10,11 +10,13 @@
|
||||
# CONTRIBUTING.md for details.
|
||||
|
||||
list(APPEND SRCS
|
||||
auxiliary.cpp
|
||||
blockmatrix.cpp
|
||||
blockoperator.cpp
|
||||
blockvector.cpp
|
||||
complex_operator.cpp
|
||||
densemat.cpp
|
||||
symmat.cpp
|
||||
handle.cpp
|
||||
matrix.cpp
|
||||
ode.cpp
|
||||
@@ -26,11 +28,13 @@ list(APPEND SRCS
|
||||
)
|
||||
|
||||
list(APPEND HDRS
|
||||
auxiliary.hpp
|
||||
blockmatrix.hpp
|
||||
blockoperator.hpp
|
||||
blockvector.hpp
|
||||
complex_operator.hpp
|
||||
densemat.hpp
|
||||
symmat.hpp
|
||||
dtensor.hpp
|
||||
handle.hpp
|
||||
invariants.hpp
|
||||
|
||||
+20
-3
@@ -30,10 +30,16 @@ int AmgXSolver::count = 0;
|
||||
|
||||
AMGX_resources_handle AmgXSolver::rsrc = nullptr;
|
||||
|
||||
AmgXSolver::AmgXSolver()
|
||||
: ConvergenceCheck(false) {};
|
||||
|
||||
AmgXSolver::AmgXSolver(const AMGX_MODE amgxMode_, const bool verbose)
|
||||
{
|
||||
amgxMode = amgxMode_;
|
||||
|
||||
if (amgxMode == AmgXSolver::SOLVER) { ConvergenceCheck = true;}
|
||||
else { ConvergenceCheck = false;}
|
||||
|
||||
DefaultParameters(amgxMode, verbose);
|
||||
|
||||
InitSerial();
|
||||
@@ -47,6 +53,9 @@ AmgXSolver::AmgXSolver(const MPI_Comm &comm,
|
||||
std::string config;
|
||||
amgxMode = amgxMode_;
|
||||
|
||||
if (amgxMode == AmgXSolver::SOLVER) { ConvergenceCheck = true;}
|
||||
else { ConvergenceCheck = false;}
|
||||
|
||||
DefaultParameters(amgxMode, verbose);
|
||||
|
||||
InitExclusiveGPU(comm);
|
||||
@@ -58,6 +67,9 @@ AmgXSolver::AmgXSolver(const MPI_Comm &comm, const int nDevs,
|
||||
std::string config;
|
||||
amgxMode = amgxMode_;
|
||||
|
||||
if (amgxMode == AmgXSolver::SOLVER) { ConvergenceCheck = true;}
|
||||
else { ConvergenceCheck = false;}
|
||||
|
||||
DefaultParameters(amgxMode_, verbose);
|
||||
|
||||
InitMPITeams(comm, nDevs);
|
||||
@@ -178,6 +190,11 @@ void AmgXSolver::ReadParameters(const std::string config,
|
||||
configSrc = source;
|
||||
}
|
||||
|
||||
void AmgXSolver::SetConvergenceCheck(bool setConvergenceCheck_)
|
||||
{
|
||||
ConvergenceCheck = setConvergenceCheck_;
|
||||
}
|
||||
|
||||
void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
|
||||
const bool verbose)
|
||||
{
|
||||
@@ -201,8 +218,8 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
|
||||
{
|
||||
amgx_config = amgx_config + ",\n"
|
||||
" \"obtain_timings\": 1, \n"
|
||||
" \"monitor_residual\": 1, \n"
|
||||
" \"print_grid_stats\": 1, \n"
|
||||
" \"monitor_residual\": 1, \n"
|
||||
" \"print_solve_stats\": 1 \n";
|
||||
}
|
||||
else
|
||||
@@ -238,12 +255,12 @@ void AmgXSolver::DefaultParameters(const AMGX_MODE amgxMode_,
|
||||
" \"convergence\": \"RELATIVE_MAX\", \n"
|
||||
" \"scope\": \"main\", \n"
|
||||
" \"tolerance\": 1e-12, \n"
|
||||
" \"monitor_residual\": 1, \n"
|
||||
" \"norm\": \"L2\" ";
|
||||
if (verbose)
|
||||
{
|
||||
amgx_config = amgx_config + ", \n"
|
||||
" \"obtain_timings\": 1, \n"
|
||||
" \"monitor_residual\": 1, \n"
|
||||
" \"print_grid_stats\": 1, \n"
|
||||
" \"print_solve_stats\": 1 \n";
|
||||
}
|
||||
@@ -884,7 +901,7 @@ void AmgXSolver::Mult(const Vector& B, Vector& X) const
|
||||
|
||||
AMGX_SOLVE_STATUS status;
|
||||
AMGX_solver_get_status(solver, &status);
|
||||
if (status != AMGX_SOLVE_SUCCESS && amgxMode == SOLVER)
|
||||
if (status != AMGX_SOLVE_SUCCESS && ConvergenceCheck)
|
||||
{
|
||||
if (status == AMGX_SOLVE_DIVERGED)
|
||||
{
|
||||
|
||||
@@ -73,13 +73,16 @@ public:
|
||||
/// Flags to configure AmgXSolver as a solver or preconditioner
|
||||
enum AMGX_MODE {SOLVER, PRECONDITIONER};
|
||||
|
||||
/// Flag to check for convergence
|
||||
bool ConvergenceCheck;
|
||||
|
||||
/**
|
||||
Flags to determine whether user solver settings are defined internally in
|
||||
the source code or will be read through an external JSON file.
|
||||
*/
|
||||
enum CONFIG_SRC {INTERNAL, EXTERNAL, UNDEFINED};
|
||||
|
||||
AmgXSolver() = default;
|
||||
AmgXSolver();
|
||||
|
||||
/**
|
||||
Configures AmgX with a default configuration based on the AmgX mode, and
|
||||
@@ -162,6 +165,9 @@ public:
|
||||
*/
|
||||
void DefaultParameters(const AMGX_MODE amgxMode_, const bool verbose);
|
||||
|
||||
/// Add a check for convergence after applying Mult.
|
||||
void SetConvergenceCheck(bool setConvergenceCheck_=true);
|
||||
|
||||
~AmgXSolver();
|
||||
|
||||
void Finalize();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,285 @@
|
||||
// Copyright (c) 2010-2020, 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_AUXILIARY
|
||||
#define MFEM_AUXILIARY
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "../general/tic_toc.hpp"
|
||||
#include "solvers.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
// forward declarations
|
||||
class Coefficient;
|
||||
class MatrixCoefficient;
|
||||
class ParMesh;
|
||||
class ParBilinearForm;
|
||||
class ParDiscreteLinearOperator;
|
||||
|
||||
/** @brief Auxiliary space solvers for MatrixFreeAMS preconditioner
|
||||
|
||||
Given an operator A and a transfer G, this will create a solver
|
||||
that approximates (G^T A G)^{-1}. Used for two different
|
||||
auxiliary spaces in the AMS cycle.
|
||||
|
||||
The produced solver is based on a low-order refined discretization
|
||||
for the high-order H1 problem. */
|
||||
class MatrixFreeAuxiliarySpace : public Solver
|
||||
{
|
||||
public:
|
||||
/** @brief Pi space constructor
|
||||
|
||||
In the AMS framework this auxiliary space has two coefficients.
|
||||
|
||||
@param mesh_lor Low-order refined auxiliary mesh
|
||||
@param alpha_coeff coefficient on curl-curl term (1 if null)
|
||||
@param beta_coeff coefficient on mass term (1 if null)
|
||||
@param beta_mcoeff matrix coefficient on mass term
|
||||
@param ess_bdr attributes for essential boundaries
|
||||
@param curlcurl_oper High-order operator for the system
|
||||
@param pi Intentity interpolation operator
|
||||
@param useAmgX_ Use AmgX instead of hypre for auxiliary solves
|
||||
@param cg_iterations number of CG iterations used to invert
|
||||
auxiliary system, choosing 0 means to use a
|
||||
single V-cycle
|
||||
*/
|
||||
MatrixFreeAuxiliarySpace(
|
||||
ParMesh& mesh_lor, Coefficient* alpha_coeff, Coefficient* beta_coeff,
|
||||
MatrixCoefficient* beta_mcoeff,
|
||||
Array<int>& ess_bdr, Operator& curlcurl_oper, Operator& pi,
|
||||
#ifdef MFEM_USE_AMGX
|
||||
bool useAmgX_,
|
||||
#endif
|
||||
int cg_iterations = 0);
|
||||
|
||||
// Complex Pi space constructor
|
||||
MatrixFreeAuxiliarySpace(
|
||||
ParMesh& mesh_lor, Coefficient* alpha_coeff, Coefficient* beta_coeff,
|
||||
Coefficient* beta_imag, Coefficient* abs_beta_imag,
|
||||
MatrixCoefficient* beta_mcoeff,
|
||||
Array<int>& ess_bdr, Operator& curlcurl_oper, Operator *oper_complex,
|
||||
Operator& pi,
|
||||
#ifdef MFEM_USE_AMGX
|
||||
bool useAmgX_,
|
||||
#endif
|
||||
int cg_iterations = 0);
|
||||
|
||||
/** @brief G space constructor
|
||||
|
||||
This has one coefficient in the AMS framework.
|
||||
|
||||
@param mesh_lor Low-order refined auxiliary mesh
|
||||
@param beta_coeff coefficient on mass term (1 if null)
|
||||
@param beta_mcoeff matrix coefficient on mass term
|
||||
@param ess_bdr attributes for essential boundaries
|
||||
@param curlcurl_oper High-order operator for the system
|
||||
@param g Gradient interpolation operator
|
||||
@param useAmgX_ Use AmgX instead of hypre for auxiliary solves
|
||||
@param cg_iterations number of CG iterations used to invert
|
||||
auxiliary system, choosing 0 means to
|
||||
use a single V-cycle
|
||||
*/
|
||||
MatrixFreeAuxiliarySpace(
|
||||
ParMesh& mesh_lor, Coefficient* beta_coeff,
|
||||
MatrixCoefficient* beta_mcoeff, Array<int>& ess_bdr,
|
||||
Operator& curlcurl_oper, Operator& g,
|
||||
#ifdef MFEM_USE_AMGX
|
||||
bool useAmgX_,
|
||||
#endif
|
||||
int cg_iterations = 1);
|
||||
|
||||
// Complex G space constructor
|
||||
MatrixFreeAuxiliarySpace(
|
||||
ParMesh& mesh_lor, Coefficient* beta_coeff, Coefficient* beta_imag,
|
||||
Coefficient* abs_beta_imag,
|
||||
MatrixCoefficient* beta_mcoeff, Array<int>& ess_bdr,
|
||||
Operator& curlcurl_oper, Operator *oper_complex, Operator& g,
|
||||
#ifdef MFEM_USE_AMGX
|
||||
bool useAmgX_,
|
||||
#endif
|
||||
int cg_iterations = 1);
|
||||
|
||||
~MatrixFreeAuxiliarySpace();
|
||||
|
||||
void Mult(const Vector& x, Vector& y) const;
|
||||
|
||||
void SetOperator(const Operator& op) {}
|
||||
|
||||
private:
|
||||
/** @brief Helper routine for constructors.
|
||||
|
||||
@param system_dimension is passed to HypreBoomerAMG::SetSystemsOptions
|
||||
*/
|
||||
void SetupAMG(int system_dimension);
|
||||
void SetupVCycle();
|
||||
|
||||
/// inner_cg_iterations > 99 applies an exact solve here
|
||||
void SetupCG(Operator& curlcurl_oper, Operator& conn,
|
||||
int inner_cg_iterations);
|
||||
|
||||
void SetupGMRES(Operator& curlcurl_oper, Operator& conn);
|
||||
|
||||
void SetupPMHSS();
|
||||
|
||||
MPI_Comm comm;
|
||||
Array<int> ess_tdof_list;
|
||||
HypreParMatrix * aspacematrix;
|
||||
HypreParMatrix * aspacematrix_complex;
|
||||
HypreParMatrix * aspacematrix_imag;
|
||||
Solver * aspacepc;
|
||||
Operator* matfree;
|
||||
CGSolver* cg;
|
||||
GMRESSolver* gmres;
|
||||
GMRESSolver* gmres_PMHSS;
|
||||
Operator* aspacewrapper;
|
||||
#ifdef MFEM_USE_AMGX
|
||||
const bool useAmgX;
|
||||
#endif
|
||||
mutable int inner_aux_iterations;
|
||||
|
||||
const bool imagBdry;
|
||||
|
||||
Complex_PMHSS *PMHSS = NULL;
|
||||
|
||||
Array<int> offsets;
|
||||
Array<int> offsets_nd;
|
||||
BlockDiagonalPreconditioner *BlockDP;
|
||||
|
||||
BlockOperator *conn_block;
|
||||
};
|
||||
|
||||
|
||||
/** @brief Perform AMS cycle with generic Operator objects.
|
||||
|
||||
Most users should use MatrixFreeAMS, which wraps this. */
|
||||
class GeneralAMS : public Solver
|
||||
{
|
||||
public:
|
||||
/** @brief Constructor.
|
||||
|
||||
Most of these arguments just need a Mult() operation,
|
||||
but pi and g also require MultTranspose() */
|
||||
GeneralAMS(const Operator& curlcurl_op_,
|
||||
Operator *oper_complex,
|
||||
const Operator& pi_,
|
||||
const Operator& gradient_,
|
||||
const Operator& pispacesolver_,
|
||||
const Operator& gspacesolver_,
|
||||
const Operator& smoother_,
|
||||
const Array<int>& ess_tdof_list_);
|
||||
virtual ~GeneralAMS();
|
||||
|
||||
/// in principle this should set A_ = op;
|
||||
void SetOperator(const Operator &op) {}
|
||||
|
||||
virtual void Mult(const Vector& x, Vector& y) const;
|
||||
|
||||
private:
|
||||
const Operator& curlcurl_op;
|
||||
Operator *oper_complex;
|
||||
const Operator& pi;
|
||||
const Operator& gradient;
|
||||
const Operator& pispacesolver;
|
||||
const Operator& gspacesolver;
|
||||
const Operator& smoother;
|
||||
const Array<int> ess_tdof_list;
|
||||
|
||||
void FormResidual(const Vector& rhs, const Vector& x,
|
||||
Vector& residual) const;
|
||||
};
|
||||
|
||||
|
||||
/** @brief An auxiliary Maxwell solver for a high-order curl-curl
|
||||
system without high-order assembly.
|
||||
|
||||
The auxiliary space solves are done using a low-order refined approach,
|
||||
but all the interpolation operators, residuals, etc. are done in a
|
||||
matrix-free manner.
|
||||
|
||||
See Barker and Kolev, Matrix-free preconditioning for high-order H(curl)
|
||||
discretizations (https://doi.org/10.1002/nla.2348) */
|
||||
class MatrixFreeAMS : public Solver
|
||||
{
|
||||
public:
|
||||
/** @brief Construct matrix-free AMS preconditioner
|
||||
|
||||
@param aform BilinearForm for curl-curl problem, generally will
|
||||
have a CurlCurlIntegrator and possibly a
|
||||
VectorFEMassIntegrator.
|
||||
@param oper Operator to precondition.
|
||||
@param nd_fespace Underlying Nedelec finite element space.
|
||||
@param alpha_coeff coefficient on curl-curl term in Maxwell problem
|
||||
(can be null, in which case constant 1 is assumed)
|
||||
@param beta_coeff (scalar) coefficient on mass term in Maxwell problem
|
||||
@param beta_mcoeff (matrix) coefficient on mass term
|
||||
@param ess_bdr boundary *attributes* that are marked essential. In
|
||||
contrast to other MFEM cases, these are *attributes*
|
||||
not dofs, because we need to apply these boundary
|
||||
conditions to different bilinear forms.
|
||||
@param useAmgX use AmgX (instead of hypre) for LOR problems
|
||||
@param inner_pi_its number of CG iterations on auxiliary pi space,
|
||||
may need more for difficult coefficients
|
||||
@param inner_g_its number of CG iterations on auxiliary g space,
|
||||
may need more for difficult coefficients
|
||||
@param nd_smoother optional user-provided smoother for Nedelec space,
|
||||
this object takes ownership and will delete.
|
||||
*/
|
||||
MatrixFreeAMS(ParBilinearForm& aform, Operator& oper, Operator *oper_complex,
|
||||
ParFiniteElementSpace& nd_fespace, Coefficient* alpha_coeff,
|
||||
Coefficient* beta_coeff, Coefficient* beta_imag,
|
||||
Coefficient* abs_beta_imag, MatrixCoefficient* beta_mcoeff,
|
||||
Array<int>& ess_bdr,
|
||||
#ifdef MFEM_USE_AMGX
|
||||
bool useAmgX = false,
|
||||
#endif
|
||||
int inner_pi_its = 0, int inner_g_its = 1,
|
||||
Solver* nd_smoother = NULL);
|
||||
|
||||
~MatrixFreeAMS();
|
||||
|
||||
void SetOperator(const Operator &op) {}
|
||||
|
||||
void Mult(const Vector& x, Vector& y) const { general_ams->Mult(x, y); }
|
||||
|
||||
private:
|
||||
GeneralAMS * general_ams;
|
||||
|
||||
Solver * smoother;
|
||||
ParDiscreteLinearOperator * pa_grad;
|
||||
OperatorPtr Gradient;
|
||||
ParDiscreteLinearOperator * pa_interp;
|
||||
OperatorPtr Pi;
|
||||
|
||||
Solver * Gspacesolver;
|
||||
Solver * Pispacesolver;
|
||||
|
||||
ParFiniteElementSpace * h1_fespace;
|
||||
ParFiniteElementSpace * h1_fespace_d;
|
||||
|
||||
Array<int> offsets_nd;
|
||||
Array<int> offsets_vector;
|
||||
Array<int> offsets_scalar;
|
||||
|
||||
BlockOperator *Pi_block;
|
||||
BlockOperator *Gradient_block;
|
||||
BlockOperator *smoother_block;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
#endif
|
||||
@@ -101,6 +101,7 @@ void BlockVector::Update(Vector & data, const Array<int> & bOffsets)
|
||||
{
|
||||
blocks[i].MakeRef(data, blockOffsets[i], BlockSize(i));
|
||||
}
|
||||
MakeRef(data, 0, blockOffsets[numBlocks]);
|
||||
}
|
||||
|
||||
void BlockVector::Update(const Array<int> &bOffsets)
|
||||
|
||||
+19
-15
@@ -287,10 +287,7 @@ void ComplexUMFPackSolver::Init()
|
||||
|
||||
void ComplexUMFPackSolver::SetOperator(const Operator &op)
|
||||
{
|
||||
int *Ap, *Ai;
|
||||
void *Symbolic;
|
||||
double *Ax;
|
||||
double *Az;
|
||||
|
||||
if (Numeric)
|
||||
{
|
||||
@@ -322,10 +319,11 @@ void ComplexUMFPackSolver::SetOperator(const Operator &op)
|
||||
width = mat->real().Width();
|
||||
MFEM_VERIFY(width == height, "not a square matrix");
|
||||
|
||||
Ap = mat->real().GetI(); // assuming real and imag have the same sparsity
|
||||
Ai = mat->real().GetJ();
|
||||
Ax = mat->real().GetData();
|
||||
Az = mat->imag().GetData();
|
||||
const int * Ap =
|
||||
mat->real().HostReadI(); // assuming real and imag have the same sparsity
|
||||
const int * Ai = mat->real().HostReadJ();
|
||||
const double * Ax = mat->real().HostReadData();
|
||||
const double * Az = mat->imag().HostReadData();
|
||||
|
||||
if (!use_long_ints)
|
||||
{
|
||||
@@ -395,6 +393,10 @@ void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const
|
||||
if (mat == NULL)
|
||||
mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!"
|
||||
" Call SetOperator first!");
|
||||
|
||||
b.HostRead();
|
||||
x.HostReadWrite();
|
||||
|
||||
int n = b.Size()/2;
|
||||
double * datax = x.GetData();
|
||||
double * datab = b.GetData();
|
||||
@@ -413,8 +415,8 @@ void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const
|
||||
if (!use_long_ints)
|
||||
{
|
||||
int status =
|
||||
umfpack_zi_solve(UMFPACK_Aat, mat->real().GetI(), mat->real().GetJ(),
|
||||
mat->real().GetData(), mat->imag().GetData(),
|
||||
umfpack_zi_solve(UMFPACK_Aat, mat->real().HostReadI(), mat->real().HostReadJ(),
|
||||
mat->real().HostReadData(), mat->imag().HostReadData(),
|
||||
datax, &datax[n], datab, &datab[n], Numeric, Control, Info);
|
||||
umfpack_zi_report_info(Control, Info);
|
||||
if (status < 0)
|
||||
@@ -426,8 +428,8 @@ void ComplexUMFPackSolver::Mult(const Vector &b, Vector &x) const
|
||||
else
|
||||
{
|
||||
SuiteSparse_long status =
|
||||
umfpack_zl_solve(UMFPACK_Aat,AI,AJ,mat->real().GetData(),
|
||||
mat->imag().GetData(),
|
||||
umfpack_zl_solve(UMFPACK_Aat,AI,AJ,mat->real().HostReadData(),
|
||||
mat->imag().HostReadData(),
|
||||
datax,&datax[n],datab,&datab[n],Numeric,Control,Info);
|
||||
|
||||
umfpack_zl_report_info(Control, Info);
|
||||
@@ -448,6 +450,8 @@ void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
|
||||
if (mat == NULL)
|
||||
mfem_error("ComplexUMFPackSolver::Mult : matrix is not set!"
|
||||
" Call SetOperator first!");
|
||||
b.HostRead();
|
||||
x.HostReadWrite();
|
||||
int n = b.Size()/2;
|
||||
double * datax = x.GetData();
|
||||
double * datab = b.GetData();
|
||||
@@ -467,8 +471,8 @@ void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
|
||||
if (!use_long_ints)
|
||||
{
|
||||
int status =
|
||||
umfpack_zi_solve(UMFPACK_A, mat->real().GetI(), mat->real().GetJ(),
|
||||
mat->real().GetData(), mat->imag().GetData(),
|
||||
umfpack_zi_solve(UMFPACK_A, mat->real().HostReadI(), mat->real().HostReadJ(),
|
||||
mat->real().HostReadData(), mat->imag().HostReadData(),
|
||||
datax, &datax[n], datab, &datab[n], Numeric, Control, Info);
|
||||
umfpack_zi_report_info(Control, Info);
|
||||
if (status < 0)
|
||||
@@ -480,8 +484,8 @@ void ComplexUMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
|
||||
else
|
||||
{
|
||||
SuiteSparse_long status =
|
||||
umfpack_zl_solve(UMFPACK_A,AI,AJ,mat->real().GetData(),
|
||||
mat->imag().GetData(),
|
||||
umfpack_zl_solve(UMFPACK_A,AI,AJ,mat->real().HostReadData(),
|
||||
mat->imag().HostReadData(),
|
||||
datax,&datax[n],datab,&datab[n],Numeric,Control,Info);
|
||||
|
||||
umfpack_zl_report_info(Control, Info);
|
||||
|
||||
+35
-11
@@ -1279,6 +1279,7 @@ void HypreParMatrix::operator*=(double s)
|
||||
static void get_sorted_rows_cols(const Array<int> &rows_cols,
|
||||
Array<HYPRE_Int> &hypre_sorted)
|
||||
{
|
||||
rows_cols.HostRead();
|
||||
hypre_sorted.SetSize(rows_cols.Size());
|
||||
bool sorted = true;
|
||||
for (int i = 0; i < rows_cols.Size(); i++)
|
||||
@@ -1995,6 +1996,8 @@ void EliminateBC(HypreParMatrix &A, HypreParMatrix &Ae,
|
||||
double *data_offd = hypre_CSRMatrixData(A_offd);
|
||||
#endif
|
||||
|
||||
ess_dof_list.HostRead();
|
||||
|
||||
for (int i = 0; i < ess_dof_list.Size(); i++)
|
||||
{
|
||||
int r = ess_dof_list[i];
|
||||
@@ -2161,6 +2164,7 @@ HypreSmoother::HypreSmoother() : Solver()
|
||||
B = X = V = Z = NULL;
|
||||
X0 = X1 = NULL;
|
||||
fir_coeffs = NULL;
|
||||
A_is_symmetric = false;
|
||||
}
|
||||
|
||||
HypreSmoother::HypreSmoother(HypreParMatrix &_A, int _type,
|
||||
@@ -2180,6 +2184,7 @@ HypreSmoother::HypreSmoother(HypreParMatrix &_A, int _type,
|
||||
B = X = V = Z = NULL;
|
||||
X0 = X1 = NULL;
|
||||
fir_coeffs = NULL;
|
||||
A_is_symmetric = false;
|
||||
|
||||
SetOperator(_A);
|
||||
}
|
||||
@@ -2467,6 +2472,16 @@ void HypreSmoother::Mult(const Vector &b, Vector &x) const
|
||||
Mult(*B, *X);
|
||||
}
|
||||
|
||||
void HypreSmoother::MultTranspose(const Vector &b, Vector &x) const
|
||||
{
|
||||
if (A_is_symmetric || type == 0 || type == 1 || type == 5)
|
||||
{
|
||||
Mult(b, x);
|
||||
return;
|
||||
}
|
||||
mfem_error("HypreSmoother::MultTranspose (...) : undefined!\n");
|
||||
}
|
||||
|
||||
HypreSmoother::~HypreSmoother()
|
||||
{
|
||||
if (B) { delete B; }
|
||||
@@ -2511,6 +2526,14 @@ void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
|
||||
mfem_error("HypreSolver::Mult (...) : HypreParMatrix A is missing");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iterative_mode)
|
||||
{
|
||||
x = 0.0;
|
||||
}
|
||||
|
||||
b.HostRead();
|
||||
x.HostReadWrite();
|
||||
if (!setup_called)
|
||||
{
|
||||
err = SetupFcn()(*this, *A, b, x);
|
||||
@@ -2526,10 +2549,6 @@ void HypreSolver::Mult(const HypreParVector &b, HypreParVector &x) const
|
||||
setup_called = 1;
|
||||
}
|
||||
|
||||
if (!iterative_mode)
|
||||
{
|
||||
x = 0.0;
|
||||
}
|
||||
err = SolveFcn()(*this, *A, b, x);
|
||||
if (error_mode == WARN_HYPRE_ERRORS)
|
||||
{
|
||||
@@ -2550,7 +2569,7 @@ void HypreSolver::Mult(const Vector &b, Vector &x) const
|
||||
return;
|
||||
}
|
||||
auto b_data = b.HostRead();
|
||||
auto x_data = x.HostWrite();
|
||||
auto x_data = iterative_mode ? x.HostReadWrite() : x.HostWrite();
|
||||
if (B == NULL)
|
||||
{
|
||||
B = new HypreParVector(A->GetComm(),
|
||||
@@ -2673,6 +2692,11 @@ void HyprePCG::Mult(const HypreParVector &b, HypreParVector &x) const
|
||||
|
||||
HYPRE_ParCSRMatrixGetComm(*A, &comm);
|
||||
|
||||
if (!iterative_mode)
|
||||
{
|
||||
x = 0.0;
|
||||
}
|
||||
|
||||
if (!setup_called)
|
||||
{
|
||||
if (print_level > 0 && print_level < 3)
|
||||
@@ -2681,6 +2705,8 @@ void HyprePCG::Mult(const HypreParVector &b, HypreParVector &x) const
|
||||
hypre_BeginTiming(time_index);
|
||||
}
|
||||
|
||||
b.HostRead();
|
||||
x.HostReadWrite();
|
||||
HYPRE_ParCSRPCGSetup(pcg_solver, *A, b, x);
|
||||
setup_called = 1;
|
||||
|
||||
@@ -2699,14 +2725,8 @@ void HyprePCG::Mult(const HypreParVector &b, HypreParVector &x) const
|
||||
hypre_BeginTiming(time_index);
|
||||
}
|
||||
|
||||
if (!iterative_mode)
|
||||
{
|
||||
x = 0.0;
|
||||
}
|
||||
|
||||
b.HostRead();
|
||||
x.HostReadWrite();
|
||||
|
||||
HYPRE_ParCSRPCGSolve(pcg_solver, *A, b, x);
|
||||
|
||||
if (print_level > 0)
|
||||
@@ -3703,6 +3723,9 @@ void HypreAMS::Init(ParFiniteElementSpace *edge_fespace)
|
||||
}
|
||||
x = x_coord.ParallelProject();
|
||||
y = y_coord.ParallelProject();
|
||||
|
||||
x->HostReadWrite();
|
||||
y->HostReadWrite();
|
||||
if (sdim == 2)
|
||||
{
|
||||
z = NULL;
|
||||
@@ -3711,6 +3734,7 @@ void HypreAMS::Init(ParFiniteElementSpace *edge_fespace)
|
||||
else
|
||||
{
|
||||
z = z_coord.ParallelProject();
|
||||
z->HostReadWrite();
|
||||
HYPRE_AMSSetCoordinateVectors(ams, *x, *y, *z);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,6 +638,9 @@ protected:
|
||||
/// Combined coefficients for windowing and Chebyshev polynomials.
|
||||
double* fir_coeffs;
|
||||
|
||||
/// A flag that indicates whether the linear system matrix A is symmetric
|
||||
bool A_is_symmetric;
|
||||
|
||||
public:
|
||||
/** Hypre smoother types:
|
||||
0 = Jacobi
|
||||
@@ -684,6 +687,12 @@ public:
|
||||
entries in the associated matrix. */
|
||||
void SetPositiveDiagonal(bool pos = true) { pos_l1_norms = pos; }
|
||||
|
||||
/** 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; }
|
||||
|
||||
/** Set/update the associated operator. Must be called after setting the
|
||||
HypreSmoother type and options. */
|
||||
virtual void SetOperator(const Operator &op);
|
||||
@@ -692,6 +701,9 @@ public:
|
||||
virtual void Mult(const HypreParVector &b, HypreParVector &x) const;
|
||||
virtual void Mult(const Vector &b, Vector &x) const;
|
||||
|
||||
/// Apply transpose of the smoother to relax the linear system Ax=b
|
||||
virtual void MultTranspose(const Vector &b, Vector &x) const;
|
||||
|
||||
virtual ~HypreSmoother();
|
||||
};
|
||||
|
||||
|
||||
+15
-22
@@ -62,8 +62,6 @@ protected:
|
||||
const scalar_t *D; // Always points to external data or is empty
|
||||
scalar_t *DaJ, *DJt, *DXt, *DYt;
|
||||
|
||||
scalar_t sign_detJ;
|
||||
|
||||
enum EvalMasks
|
||||
{
|
||||
HAVE_I1 = 1,
|
||||
@@ -96,8 +94,7 @@ protected:
|
||||
{
|
||||
eval_state |= HAVE_I2b;
|
||||
const scalar_t det = J[0]*J[3] - J[1]*J[2];
|
||||
sign_detJ = scalar_ops::sign(det);
|
||||
I2b = sign_detJ*det;
|
||||
I2b = det;
|
||||
}
|
||||
void Eval_dI1()
|
||||
{
|
||||
@@ -136,10 +133,10 @@ protected:
|
||||
// I2b = det(J)
|
||||
// dI2b = adj(J)^T
|
||||
Get_I2b();
|
||||
dI2b[0] = sign_detJ*J[3];
|
||||
dI2b[1] = -sign_detJ*J[2];
|
||||
dI2b[2] = -sign_detJ*J[1];
|
||||
dI2b[3] = sign_detJ*J[0];
|
||||
dI2b[0] = J[3];
|
||||
dI2b[1] = -J[2];
|
||||
dI2b[2] = -J[1];
|
||||
dI2b[3] = J[0];
|
||||
}
|
||||
void Eval_DaJ() // D adj(J) = D dI2b^t
|
||||
{
|
||||
@@ -516,8 +513,6 @@ protected:
|
||||
const scalar_t *D; // Always points to external data or is empty
|
||||
scalar_t *DaJ, *DJt, *DdI2t, *DXt, *DYt;
|
||||
|
||||
scalar_t sign_detJ;
|
||||
|
||||
enum EvalMasks
|
||||
{
|
||||
HAVE_I1 = 1,
|
||||
@@ -585,8 +580,6 @@ protected:
|
||||
eval_state |= HAVE_I3b;
|
||||
I3b = J[0]*(J[4]*J[8] - J[7]*J[5]) - J[1]*(J[3]*J[8] - J[5]*J[6]) +
|
||||
J[2]*(J[3]*J[7] - J[4]*J[6]);
|
||||
sign_detJ = scalar_ops::sign(I3b);
|
||||
I3b = sign_detJ*I3b;
|
||||
}
|
||||
scalar_t Get_I3b_p() // I3b^{-2/3}
|
||||
{
|
||||
@@ -594,7 +587,7 @@ protected:
|
||||
{
|
||||
eval_state |= HAVE_I3b_p;
|
||||
const scalar_t i3b = Get_I3b();
|
||||
I3b_p = sign_detJ*scalar_ops::pow(i3b, -2, 3);
|
||||
I3b_p = scalar_ops::pow(i3b, -2, 3);
|
||||
}
|
||||
return I3b_p;
|
||||
}
|
||||
@@ -680,15 +673,15 @@ protected:
|
||||
eval_state |= HAVE_dI3b;
|
||||
// I3b = det(J)
|
||||
// dI3b = adj(J)^T
|
||||
dI3b[0] = sign_detJ*(J[4]*J[8] - J[5]*J[7]); // 0 3 6
|
||||
dI3b[1] = sign_detJ*(J[5]*J[6] - J[3]*J[8]); // 1 4 7
|
||||
dI3b[2] = sign_detJ*(J[3]*J[7] - J[4]*J[6]); // 2 5 8
|
||||
dI3b[3] = sign_detJ*(J[2]*J[7] - J[1]*J[8]);
|
||||
dI3b[4] = sign_detJ*(J[0]*J[8] - J[2]*J[6]);
|
||||
dI3b[5] = sign_detJ*(J[1]*J[6] - J[0]*J[7]);
|
||||
dI3b[6] = sign_detJ*(J[1]*J[5] - J[2]*J[4]);
|
||||
dI3b[7] = sign_detJ*(J[2]*J[3] - J[0]*J[5]);
|
||||
dI3b[8] = sign_detJ*(J[0]*J[4] - J[1]*J[3]);
|
||||
dI3b[0] = J[4]*J[8] - J[5]*J[7]; // 0 3 6
|
||||
dI3b[1] = J[5]*J[6] - J[3]*J[8]; // 1 4 7
|
||||
dI3b[2] = J[3]*J[7] - J[4]*J[6]; // 2 5 8
|
||||
dI3b[3] = J[2]*J[7] - J[1]*J[8];
|
||||
dI3b[4] = J[0]*J[8] - J[2]*J[6];
|
||||
dI3b[5] = J[1]*J[6] - J[0]*J[7];
|
||||
dI3b[6] = J[1]*J[5] - J[2]*J[4];
|
||||
dI3b[7] = J[2]*J[3] - J[0]*J[5];
|
||||
dI3b[8] = J[0]*J[4] - J[1]*J[3];
|
||||
}
|
||||
void Eval_DZt(const scalar_t *Z, scalar_t **DZt_ptr)
|
||||
{
|
||||
|
||||
@@ -12,11 +12,6 @@
|
||||
#ifndef MFEM_KERNELS_HPP
|
||||
#define MFEM_KERNELS_HPP
|
||||
|
||||
#ifdef _WIN32
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
#endif
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/backends.hpp"
|
||||
#include "../general/globals.hpp"
|
||||
|
||||
@@ -24,10 +24,12 @@
|
||||
#include "blockoperator.hpp"
|
||||
#include "sparsesmoothers.hpp"
|
||||
#include "densemat.hpp"
|
||||
#include "symmat.hpp"
|
||||
#include "ode.hpp"
|
||||
#include "solvers.hpp"
|
||||
#include "handle.hpp"
|
||||
#include "invariants.hpp"
|
||||
#include "auxiliary.hpp"
|
||||
|
||||
#ifdef MFEM_USE_AMGX
|
||||
#include "amgxsolver.hpp"
|
||||
|
||||
@@ -196,6 +196,8 @@ void MUMPSSolver::SetOperator(const Operator &op)
|
||||
|
||||
void MUMPSSolver::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
x.HostRead();
|
||||
y.HostReadWrite();
|
||||
#if MFEM_MUMPS_VERSION >= 530
|
||||
|
||||
id->nloc_rhs = x.Size();
|
||||
|
||||
+49
-8
@@ -37,7 +37,8 @@ protected:
|
||||
const Array<int> &test_tdof_list,
|
||||
RectangularConstrainedOperator* &Aout);
|
||||
|
||||
/// Returns RAP Operator of this, taking in input/output Prolongation matrices
|
||||
/** @brief Returns RAP Operator of this, using input/output Prolongation matrices
|
||||
@a Pi corresponds to "P", @a Po corresponds to "Rt" */
|
||||
Operator *SetupRAP(const Operator *Pi, const Operator *Po);
|
||||
|
||||
public:
|
||||
@@ -112,6 +113,11 @@ public:
|
||||
{
|
||||
return GetProlongation(); // Assume square unless specialized
|
||||
}
|
||||
/** @brief Transpose of GetOutputRestriction, directly available in this
|
||||
form to facilitate matrix-free RAP-type operators.
|
||||
|
||||
`NULL` means identity. */
|
||||
virtual const Operator *GetOutputRestrictionTranspose() const { return NULL; }
|
||||
/** @brief Restriction operator from output vectors for the operator to linear
|
||||
algebra (linear system) vectors. `NULL` means identity. */
|
||||
virtual const Operator *GetOutputRestriction() const
|
||||
@@ -606,23 +612,22 @@ public:
|
||||
|
||||
using TimeDependentOperator::ImplicitSolve;
|
||||
/** @brief Solve the equation:
|
||||
@a k = f(@a x + 1/2 @a dt0^2 @a k, @a dxdt + @a dt1 @a k, t), for the
|
||||
@a k = f(@a x + @a fac0 @a k, @a dxdt + @a fac1 @a k, t), for the
|
||||
unknown @a k at the current time t.
|
||||
|
||||
For general F and G, the equation for @a k becomes:
|
||||
F(@a x + 1/2 @a dt0^2 @a k, @a dxdt + @a dt1 @a k, t)
|
||||
= G(@a x + 1/2 @a dt0^2 @a k, @a dxdt + @a dt1 @a k, t).
|
||||
F(@a x + @a fac0 @a k, @a dxdt + @a fac1 @a k, t)
|
||||
= G(@a x + @a fac0 @a k, @a dxdt + @a fac1 @a k, t).
|
||||
|
||||
The input vector @a x corresponds to time index (or cycle) n, while the
|
||||
The input vectors @a x and @a dxdt corresponds to time index (or cycle) n, while the
|
||||
currently set time, #t, and the result vector @a k correspond to time
|
||||
index n+1. The time step @a dt corresponds to the time interval between
|
||||
cycles n and n+1.
|
||||
index n+1.
|
||||
|
||||
This method allows for the abstract implementation of some time
|
||||
integration methods.
|
||||
|
||||
If not re-implemented, this method simply generates an error. */
|
||||
virtual void ImplicitSolve(const double dt0, const double dt1,
|
||||
virtual void ImplicitSolve(const double fac0, const double fac1,
|
||||
const Vector &x, const Vector &dxdt, Vector &k);
|
||||
|
||||
|
||||
@@ -691,6 +696,42 @@ public:
|
||||
{ A_.Mult(x, y); y *= a_; }
|
||||
};
|
||||
|
||||
/// General sum operator: x -> A(x)+B(x)
|
||||
class SumOperator : public Operator
|
||||
{
|
||||
const Operator *A, *B;
|
||||
bool ownA, ownB;
|
||||
mutable Vector z, w;
|
||||
double cA, cB;
|
||||
|
||||
public:
|
||||
SumOperator(const Operator *A_, const Operator *B_,
|
||||
bool ownA_, bool ownB_, double cA_, double cB_)
|
||||
: Operator(A_->Height(), B_->Width()),
|
||||
A(A_), B(B_), ownA(ownA_), ownB(ownB_), z(A_->Height()), w(A_->Width()),
|
||||
cA(cA_), cB(cB_)
|
||||
{
|
||||
MFEM_VERIFY(A->Width() == B->Width() && A->Height() == B->Height(),
|
||||
"incompatible Operators: A->Width() = " << A->Width()
|
||||
<< ", B->Height() = " << B->Height());
|
||||
|
||||
z.UseDevice(true);
|
||||
w.UseDevice(true);
|
||||
}
|
||||
|
||||
~SumOperator()
|
||||
{
|
||||
if (ownA) { delete A; }
|
||||
if (ownB) { delete B; }
|
||||
}
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const
|
||||
{ B->Mult(x, z); A->Mult(x, y); y *= cA; z *= cB; y += z;}
|
||||
|
||||
virtual void MultTranspose(const Vector &x, Vector &y) const
|
||||
{ B->MultTranspose(x, w); A->MultTranspose(x, y); y *= cA; w *= cB; y += w;}
|
||||
|
||||
};
|
||||
|
||||
/** @brief The transpose of a given operator. Switches the roles of the methods
|
||||
Mult() and MultTranspose(). */
|
||||
|
||||
+107
-15
@@ -1556,6 +1556,7 @@ void NewtonSolver::SetOperator(const Operator &op)
|
||||
width = op.Width();
|
||||
MFEM_ASSERT(height == width, "square Operator is required.");
|
||||
|
||||
xcur.SetSize(width);
|
||||
r.SetSize(width);
|
||||
c.SetSize(width);
|
||||
}
|
||||
@@ -1615,9 +1616,20 @@ void NewtonSolver::Mult(const Vector &b, Vector &x) const
|
||||
break;
|
||||
}
|
||||
|
||||
prec->SetOperator(oper->GetGradient(x));
|
||||
grad = &oper->GetGradient(x);
|
||||
prec->SetOperator(*grad);
|
||||
|
||||
prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b]
|
||||
if (lin_rtol_type)
|
||||
{
|
||||
AdaptiveLinRtolPreSolve(x, it, norm);
|
||||
}
|
||||
|
||||
prec->Mult(r, c); // c = [DF(x_i)]^{-1} [F(x_i)-b]
|
||||
|
||||
if (lin_rtol_type)
|
||||
{
|
||||
AdaptiveLinRtolPostSolve(c, r, it, norm);
|
||||
}
|
||||
|
||||
const double c_scale = ComputeScalingFactor(x, b);
|
||||
if (c_scale == 0.0)
|
||||
@@ -1641,6 +1653,86 @@ void NewtonSolver::Mult(const Vector &b, Vector &x) const
|
||||
final_norm = norm;
|
||||
}
|
||||
|
||||
void NewtonSolver::SetAdaptiveLinRtol(const int type,
|
||||
const double rtol0,
|
||||
const double rtol_max,
|
||||
const double alpha,
|
||||
const double gamma)
|
||||
{
|
||||
lin_rtol_type = type;
|
||||
lin_rtol0 = rtol0;
|
||||
lin_rtol_max = rtol_max;
|
||||
this->alpha = alpha;
|
||||
this->gamma = gamma;
|
||||
}
|
||||
|
||||
void NewtonSolver::AdaptiveLinRtolPreSolve(const Vector &x,
|
||||
const int it,
|
||||
const double fnorm) const
|
||||
{
|
||||
// Assume that when adaptive linear solver relative tolerance is activated,
|
||||
// we are working with an iterative solver.
|
||||
auto iterative_solver = static_cast<IterativeSolver *>(prec);
|
||||
// Adaptive linear solver relative tolerance
|
||||
double eta;
|
||||
// Safeguard threshold
|
||||
double sg_threshold = 0.1;
|
||||
|
||||
if (it == 0)
|
||||
{
|
||||
eta = lin_rtol0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lin_rtol_type == 1)
|
||||
{
|
||||
// eta = gamma * abs(||F(x1)|| - ||F(x0) + DF(x0) s0||) / ||F(x0)||
|
||||
eta = gamma * abs(fnorm - lnorm_last) / fnorm_last;
|
||||
}
|
||||
else if (lin_rtol_type == 2)
|
||||
{
|
||||
// eta = gamma * (||F(x1)|| / ||F(x0)||)^alpha
|
||||
eta = gamma * pow(fnorm / fnorm_last, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unknown adaptive linear solver rtol version");
|
||||
}
|
||||
|
||||
// Safeguard rtol from "oversolving" ?!
|
||||
const double sg_eta = gamma * pow(eta_last, alpha);
|
||||
if (sg_eta > sg_threshold) { eta = std::max(eta, sg_eta); }
|
||||
}
|
||||
|
||||
eta = std::min(eta, lin_rtol_max);
|
||||
iterative_solver->SetRelTol(eta);
|
||||
eta_last = eta;
|
||||
if (print_level >= 0)
|
||||
{
|
||||
mfem::out << "Eisenstat-Walker rtol = " << eta << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void NewtonSolver::AdaptiveLinRtolPostSolve(const Vector &x,
|
||||
const Vector &b,
|
||||
const int it,
|
||||
const double fnorm) const
|
||||
{
|
||||
fnorm_last = fnorm;
|
||||
|
||||
// If version 1 is chosen, the true linear residual norm has to be computed
|
||||
// and in most cases we can only retrieve the preconditioned linear residual
|
||||
// norm.
|
||||
if (lin_rtol_type == 1)
|
||||
{
|
||||
// lnorm_last = ||F(x0) + DF(x0) s0||
|
||||
Vector linres(x.Size());
|
||||
grad->Mult(x, linres);
|
||||
linres -= b;
|
||||
lnorm_last = Norm(linres);
|
||||
}
|
||||
}
|
||||
|
||||
void LBFGSSolver::Mult(const Vector &b, Vector &x) const
|
||||
{
|
||||
MFEM_VERIFY(oper != NULL, "the Operator is not set (use SetOperator).");
|
||||
@@ -2720,9 +2812,7 @@ void UMFPackSolver::Init()
|
||||
|
||||
void UMFPackSolver::SetOperator(const Operator &op)
|
||||
{
|
||||
int *Ap, *Ai;
|
||||
void *Symbolic;
|
||||
double *Ax;
|
||||
|
||||
if (Numeric)
|
||||
{
|
||||
@@ -2748,9 +2838,9 @@ void UMFPackSolver::SetOperator(const Operator &op)
|
||||
width = mat->Width();
|
||||
MFEM_VERIFY(width == height, "not a square matrix");
|
||||
|
||||
Ap = mat->GetI();
|
||||
Ai = mat->GetJ();
|
||||
Ax = mat->GetData();
|
||||
const int * Ap = mat->HostReadI();
|
||||
const int * Ai = mat->HostReadJ();
|
||||
const double * Ax = mat->HostReadData();
|
||||
|
||||
if (!use_long_ints)
|
||||
{
|
||||
@@ -2820,12 +2910,13 @@ void UMFPackSolver::Mult(const Vector &b, Vector &x) const
|
||||
if (mat == NULL)
|
||||
mfem_error("UMFPackSolver::Mult : matrix is not set!"
|
||||
" Call SetOperator first!");
|
||||
|
||||
b.HostRead();
|
||||
x.HostReadWrite();
|
||||
if (!use_long_ints)
|
||||
{
|
||||
int status =
|
||||
umfpack_di_solve(UMFPACK_At, mat->GetI(), mat->GetJ(),
|
||||
mat->GetData(), x, b, Numeric, Control, Info);
|
||||
umfpack_di_solve(UMFPACK_At, mat->HostReadI(), mat->HostReadJ(),
|
||||
mat->HostReadData(), x, b, Numeric, Control, Info);
|
||||
umfpack_di_report_info(Control, Info);
|
||||
if (status < 0)
|
||||
{
|
||||
@@ -2836,7 +2927,7 @@ void UMFPackSolver::Mult(const Vector &b, Vector &x) const
|
||||
else
|
||||
{
|
||||
SuiteSparse_long status =
|
||||
umfpack_dl_solve(UMFPACK_At, AI, AJ, mat->GetData(), x, b,
|
||||
umfpack_dl_solve(UMFPACK_At, AI, AJ, mat->HostReadData(), x, b,
|
||||
Numeric, Control, Info);
|
||||
umfpack_dl_report_info(Control, Info);
|
||||
if (status < 0)
|
||||
@@ -2852,12 +2943,13 @@ void UMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
|
||||
if (mat == NULL)
|
||||
mfem_error("UMFPackSolver::MultTranspose : matrix is not set!"
|
||||
" Call SetOperator first!");
|
||||
|
||||
b.HostRead();
|
||||
x.HostReadWrite();
|
||||
if (!use_long_ints)
|
||||
{
|
||||
int status =
|
||||
umfpack_di_solve(UMFPACK_A, mat->GetI(), mat->GetJ(),
|
||||
mat->GetData(), x, b, Numeric, Control, Info);
|
||||
umfpack_di_solve(UMFPACK_A, mat->HostReadI(), mat->HostReadJ(),
|
||||
mat->HostReadData(), x, b, Numeric, Control, Info);
|
||||
umfpack_di_report_info(Control, Info);
|
||||
if (status < 0)
|
||||
{
|
||||
@@ -2869,7 +2961,7 @@ void UMFPackSolver::MultTranspose(const Vector &b, Vector &x) const
|
||||
else
|
||||
{
|
||||
SuiteSparse_long status =
|
||||
umfpack_dl_solve(UMFPACK_A, AI, AJ, mat->GetData(), x, b,
|
||||
umfpack_dl_solve(UMFPACK_A, AI, AJ, mat->HostReadData(), x, b,
|
||||
Numeric, Control, Info);
|
||||
umfpack_dl_report_info(Control, Info);
|
||||
if (status < 0)
|
||||
|
||||
+217
-1
@@ -406,7 +406,40 @@ void MINRES(const Operator &A, Solver &B, const Vector &b, Vector &x,
|
||||
class NewtonSolver : public IterativeSolver
|
||||
{
|
||||
protected:
|
||||
mutable Vector r, c;
|
||||
mutable Vector xcur, r, c;
|
||||
mutable Operator *grad;
|
||||
|
||||
// Adaptive linear solver rtol variables
|
||||
|
||||
// Method to determine rtol, 0 means the adaptive algorithm is deactivated.
|
||||
int lin_rtol_type = 0;
|
||||
// rtol to use in first iteration
|
||||
double lin_rtol0;
|
||||
// Maximum rtol
|
||||
double lin_rtol_max;
|
||||
// Function norm ||F(x)|| of the previous iterate
|
||||
mutable double fnorm_last = 0.0;
|
||||
// Linear residual norm of the previous iterate
|
||||
mutable double lnorm_last = 0.0;
|
||||
// Forcing term (linear residual rtol) from the previous iterate
|
||||
mutable double eta_last = 0.0;
|
||||
// Eisenstat-Walker factor gamma
|
||||
double gamma;
|
||||
// Eisenstat-Walker factor alpha
|
||||
double alpha;
|
||||
|
||||
/** @brief Method for the adaptive linear solver rtol invoked before the
|
||||
linear solve. */
|
||||
void AdaptiveLinRtolPreSolve(const Vector &x,
|
||||
const int it,
|
||||
const double fnorm) const;
|
||||
|
||||
/** @brief Method for the adaptive linear solver rtol invoked after the
|
||||
linear solve. */
|
||||
void AdaptiveLinRtolPostSolve(const Vector &x,
|
||||
const Vector &b,
|
||||
const int it,
|
||||
const double fnorm) const;
|
||||
|
||||
public:
|
||||
NewtonSolver() { }
|
||||
@@ -434,6 +467,26 @@ public:
|
||||
/** @brief This method can be overloaded in derived classes to perform
|
||||
computations that need knowledge of the newest Newton state. */
|
||||
virtual void ProcessNewState(const Vector &x) const { }
|
||||
|
||||
const Vector &GetCurrentResidual() const { return r; }
|
||||
const Vector &GetCurrentIterate() const { return xcur; }
|
||||
|
||||
/// Enable adaptive linear solver relative tolerance algorithm.
|
||||
/** Compute a relative tolerance for the Krylov method after each nonlinear
|
||||
iteration, based on the algorithm presented in [1].
|
||||
|
||||
The maximum linear solver relative tolerance @a rtol_max should be < 1. For
|
||||
@a type 1 the parameters @a alpha and @a gamma are ignored. For @a type 2
|
||||
@a alpha has to be between 0 and 1 and @a gamma between 1 and 2.
|
||||
|
||||
[1] Eisenstat, Stanley C., and Homer F. Walker. "Choosing the forcing terms
|
||||
in an inexact Newton method."
|
||||
*/
|
||||
void SetAdaptiveLinRtol(const int type = 2,
|
||||
const double rtol0 = 0.5,
|
||||
const double rtol_max = 0.9,
|
||||
const double alpha = 0.5 * (1.0 + sqrt(5.0)),
|
||||
const double gamma = 1.0);
|
||||
};
|
||||
|
||||
/** L-BFGS method for solving F(x)=b for a given operator F, by minimizing
|
||||
@@ -799,6 +852,169 @@ public:
|
||||
|
||||
#endif // MFEM_USE_SUITESPARSE
|
||||
|
||||
class Complex_PMHSS : public Solver
|
||||
{
|
||||
public:
|
||||
Complex_PMHSS(Operator *Re, Operator *Im, Solver *prec_Re, Solver *prec_Im,
|
||||
double a_)
|
||||
: Solver(2*Re->Height()), a(a_), A(Re, Im, false, false),
|
||||
A_Re(Re, NULL, false, false),
|
||||
A_Im(Im, NULL, false, false), u(2*Re->Height()), rhs(2*Re->Height()),
|
||||
n(Re->Height())
|
||||
{
|
||||
MFEM_VERIFY(Re->Height() == Im->Height() && Re->Height() == Re->Width() &&
|
||||
Im->Height() == Im->Width(), "");
|
||||
MFEM_VERIFY(this->Height() == A.Height(), "");
|
||||
|
||||
// Create CG solver for real operator aV + A_Re in complex space.
|
||||
|
||||
V = useIdentityV ? (Operator*) new IdentityOperator(this->Height()) :
|
||||
(Operator*) &A_Re;
|
||||
|
||||
// In the case V = A_Re, it is faster to use a scaled operator than a SumOperator
|
||||
Operator *sumOpRe = useIdentityV ? (Operator*) new SumOperator(V, &A_Re, false,
|
||||
false, a, 1.0)
|
||||
: (Operator*) new ScaledOperator(&A_Re, a + 1.0);
|
||||
|
||||
SumOperator *sumOpIm = new SumOperator(V, &A_Im, false, false, a, 1.0);
|
||||
|
||||
CGSolver *cg = new CGSolver(MPI_COMM_WORLD);
|
||||
cg->SetRelTol(1e-6);
|
||||
cg->SetMaxIter(1000);
|
||||
cg->SetPrintLevel(0);
|
||||
cg->SetOperator(*sumOpRe);
|
||||
cg->SetPreconditioner(*prec_Re);
|
||||
cg->iterative_mode = false;
|
||||
|
||||
SRe = cg;
|
||||
|
||||
CGSolver *cgi = new CGSolver(MPI_COMM_WORLD);
|
||||
cgi->SetRelTol(1e-6);
|
||||
cgi->SetMaxIter(1000);
|
||||
cgi->SetPrintLevel(0);
|
||||
cgi->SetOperator(*sumOpIm);
|
||||
if (prec_Im && useIdentityV) { cgi->SetPreconditioner(*prec_Im); }
|
||||
if (!useIdentityV) { cgi->SetPreconditioner(*prec_Re); }
|
||||
cgi->iterative_mode = false;
|
||||
|
||||
/*
|
||||
// For negative definite imaginary part, but then PMHSS does not work?
|
||||
MINRESSolver *cgi = new MINRESSolver(MPI_COMM_WORLD);
|
||||
cgi->SetRelTol(1e-12);
|
||||
cgi->SetMaxIter(1000);
|
||||
cgi->SetPrintLevel(0);
|
||||
cgi->SetOperator(*sumOpIm);
|
||||
if (prec_Im) cgi->SetPreconditioner(*prec_Im);
|
||||
*/
|
||||
|
||||
SIm = cgi;
|
||||
}
|
||||
|
||||
void SetOperator(const Operator &op)
|
||||
{
|
||||
MFEM_VERIFY(false, "Don't call SetOperator");
|
||||
}
|
||||
|
||||
void ComputeResidual(const Vector &b, const Vector &sol, Vector &res) const
|
||||
{
|
||||
A.Mult(sol, res);
|
||||
res -= b;
|
||||
}
|
||||
|
||||
void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (!(x.Size() == Height() && y.Size() == Height()))
|
||||
{
|
||||
std::cout << "bug";
|
||||
}
|
||||
|
||||
MFEM_VERIFY(x.Size() == Height() && y.Size() == Height(), "");
|
||||
|
||||
const double initNorm = x.Norml2();
|
||||
mfem::out << "MHSS RHS norm " << initNorm << '\n';
|
||||
|
||||
// With V = I, use modified HSS (MHSS) from Bai, Benzi, Chen 2010.
|
||||
y = 0.0;
|
||||
|
||||
for (int it=0; it<maxiter; ++it)
|
||||
{
|
||||
// Solve (aI + Re) u = (aI - i Im) y + x
|
||||
|
||||
if (it == 0)
|
||||
{
|
||||
// Optimize the first iteration, when the initial guess is y=0.
|
||||
SRe->Mult(x, u);
|
||||
}
|
||||
else
|
||||
{
|
||||
A_Im.Mult(y, u); // u = Im y
|
||||
// Set rhs = -i Im y = -i u
|
||||
for (int j=0; j<n; ++j)
|
||||
{
|
||||
rhs[j] = u[n+j];
|
||||
rhs[n+j] = -u[j];
|
||||
}
|
||||
|
||||
rhs += x;
|
||||
|
||||
V->Mult(y, u);
|
||||
rhs.Add(a, u);
|
||||
|
||||
SRe->Mult(rhs, u);
|
||||
}
|
||||
|
||||
// Solve (aI + Im) y = (aI + i Re) u - i x
|
||||
|
||||
A_Re.Mult(u, y); // y = Re u
|
||||
// Set rhs = i (Re u - x) = i (y - x)
|
||||
for (int j=0; j<n; ++j)
|
||||
{
|
||||
rhs[j] = -(y[n+j] - x[n+j]);
|
||||
rhs[n+j] = y[j] - x[j];
|
||||
}
|
||||
|
||||
if (useIdentityV)
|
||||
{
|
||||
//V->Mult(u, y);
|
||||
//rhs.Add(a, y);
|
||||
rhs.Add(a, u);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Using V = A_Re
|
||||
rhs.Add(a, y);
|
||||
}
|
||||
|
||||
SIm->Mult(rhs, y);
|
||||
|
||||
ComputeResidual(x, y, rhs);
|
||||
const double resNorm = rhs.Norml2();
|
||||
mfem::out << "MHSS iter " << it << " residual norm " << resNorm << '\n';
|
||||
|
||||
if (resNorm / initNorm < tol)
|
||||
{
|
||||
mfem::out << "MHSS converged\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const double a;
|
||||
const int maxiter = 1;
|
||||
ComplexOperator A, A_Re, A_Im;
|
||||
mutable Vector u, rhs;
|
||||
const int n;
|
||||
|
||||
const double tol = 1.0e-8;
|
||||
|
||||
const bool useIdentityV = false;
|
||||
Operator *V = NULL;
|
||||
|
||||
Solver *SRe = NULL;
|
||||
Solver *SIm = NULL;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // MFEM_SOLVERS
|
||||
|
||||
+15
-11
@@ -415,10 +415,14 @@ void SparseMatrix::SortColumnIndices()
|
||||
return;
|
||||
}
|
||||
|
||||
const int * Ip=HostReadI();
|
||||
HostReadWriteJ();
|
||||
HostReadWriteData();
|
||||
|
||||
Array<Pair<int,double> > row;
|
||||
for (int j = 0, i = 0; i < height; i++)
|
||||
{
|
||||
int end = I[i+1];
|
||||
int end = Ip[i+1];
|
||||
row.SetSize(end - j);
|
||||
for (int k = 0; k < row.Size(); k++)
|
||||
{
|
||||
@@ -3358,12 +3362,12 @@ SparseMatrix *Mult (const SparseMatrix &A, const SparseMatrix &B,
|
||||
"number of columns of A (" << ncolsA
|
||||
<< ") must equal number of rows of B (" << nrowsB << ")");
|
||||
|
||||
A_i = A.GetI();
|
||||
A_j = A.GetJ();
|
||||
A_data = A.GetData();
|
||||
B_i = B.GetI();
|
||||
B_j = B.GetJ();
|
||||
B_data = B.GetData();
|
||||
A_i = A.HostReadI();
|
||||
A_j = A.HostReadJ();
|
||||
A_data = A.HostReadData();
|
||||
B_i = B.HostReadI();
|
||||
B_j = B.HostReadJ();
|
||||
B_data = B.HostReadData();
|
||||
|
||||
B_marker = new int[ncolsB];
|
||||
|
||||
@@ -3409,16 +3413,16 @@ SparseMatrix *Mult (const SparseMatrix &A, const SparseMatrix &B,
|
||||
{
|
||||
C = OAB;
|
||||
|
||||
MFEM_VERIFY(nrowsA == C -> Height() && ncolsB == C -> Width(),
|
||||
MFEM_VERIFY(nrowsA == C->Height() && ncolsB == C->Width(),
|
||||
"Input matrix sizes do not match output sizes"
|
||||
<< " nrowsA = " << nrowsA
|
||||
<< ", C->Height() = " << C->Height()
|
||||
<< " ncolsB = " << ncolsB
|
||||
<< ", C->Width() = " << C->Width());
|
||||
|
||||
// C_i = C -> GetI(); // not used
|
||||
C_j = C -> GetJ();
|
||||
C_data = C -> GetData();
|
||||
// C_i = C->HostReadI(); // not used
|
||||
C_j = C->HostWriteJ();
|
||||
C_data = C->HostWriteData();
|
||||
}
|
||||
|
||||
counter = 0;
|
||||
|
||||
+3
-2
@@ -523,9 +523,10 @@ void SuperLUSolver::Mult( const Vector & x, Vector & y ) const
|
||||
// SuperLU overwrites x with y, so copy x to y and pass that to the solve
|
||||
// routine.
|
||||
|
||||
y = x;
|
||||
const double *xPtr = x.HostRead();
|
||||
y = xPtr;
|
||||
double * yPtr = y.HostReadWrite();
|
||||
|
||||
double* yPtr = (double*)y;
|
||||
int info = -1, locSize = y.Size();
|
||||
|
||||
// Solve the system
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2010-2020, 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.
|
||||
|
||||
|
||||
// Implementation of data type DenseSymmetricMatrix
|
||||
|
||||
#include "symmat.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
DenseSymmetricMatrix::DenseSymmetricMatrix() : Matrix(0)
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
|
||||
DenseSymmetricMatrix::DenseSymmetricMatrix(int s) : Matrix(s)
|
||||
{
|
||||
MFEM_ASSERT(s >= 0, "invalid DenseSymmetricMatrix size: " << s);
|
||||
if (s > 0)
|
||||
{
|
||||
data.New((s*(s+1))/2);
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
void DenseSymmetricMatrix::SetSize(int s)
|
||||
{
|
||||
MFEM_ASSERT(s >= 0,
|
||||
"invalid DenseSymmetricMatrix size: " << s);
|
||||
if (Height() == s)
|
||||
{
|
||||
return;
|
||||
}
|
||||
height = s;
|
||||
width = s;
|
||||
const int s2 = (s*(s+1))/2;
|
||||
if (s2 > data.Capacity())
|
||||
{
|
||||
data.Delete();
|
||||
data.New(s2);
|
||||
*this = 0.0; // init with zeroes
|
||||
}
|
||||
}
|
||||
|
||||
DenseSymmetricMatrix &DenseSymmetricMatrix::operator=(double c)
|
||||
{
|
||||
const int s = (Height()*(Height()+1))/2;
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] = c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
double &DenseSymmetricMatrix::Elem(int i, int j)
|
||||
{
|
||||
return (*this)(i,j);
|
||||
}
|
||||
|
||||
const double &DenseSymmetricMatrix::Elem(int i, int j) const
|
||||
{
|
||||
return (*this)(i,j);
|
||||
}
|
||||
|
||||
DenseSymmetricMatrix &DenseSymmetricMatrix::operator*=(double c)
|
||||
{
|
||||
int s = Height()*(Height()+1)/2;
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
data[i] *= c;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DenseSymmetricMatrix::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
mfem_error("DenseSymmetricMatrix::Mult() not implemented!");
|
||||
}
|
||||
|
||||
MatrixInverse *DenseSymmetricMatrix::Inverse() const
|
||||
{
|
||||
mfem_error("DenseSymmetricMatrix::Inverse() not implemented!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void DenseSymmetricMatrix::Print (std::ostream & out, int width_) const
|
||||
{
|
||||
mfem_error("DenseSymmetricMatrix::Print() not implemented!");
|
||||
}
|
||||
|
||||
DenseSymmetricMatrix::~DenseSymmetricMatrix()
|
||||
{
|
||||
data.Delete();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright (c) 2010-2020, 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_SYMMETRICMAT
|
||||
#define MFEM_SYMMETRICMAT
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../general/globals.hpp"
|
||||
#include "matrix.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Dense symmetric matrix storing the upper triangular part. This class so far
|
||||
/// has little functionality beyond storage.
|
||||
class DenseSymmetricMatrix : public Matrix
|
||||
{
|
||||
private:
|
||||
Memory<double> data;
|
||||
|
||||
public:
|
||||
|
||||
/** Default constructor for DenseSymmetricMatrix.
|
||||
Sets data = NULL and height = width = 0. */
|
||||
DenseSymmetricMatrix();
|
||||
|
||||
/// Creates square matrix of size s.
|
||||
explicit DenseSymmetricMatrix(int s);
|
||||
|
||||
/// Construct a DenseSymmetricMatrix using an existing data array.
|
||||
/** The DenseSymmetricMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the array. */
|
||||
DenseSymmetricMatrix(double *d, int s)
|
||||
: Matrix(s, s) { UseExternalData(d, s); }
|
||||
|
||||
/// Change the data array and the size of the DenseSymmetricMatrix.
|
||||
/** The DenseSymmetricMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the data array @a d. This method should not be used with
|
||||
DenseSymmetricMatrix that owns its current data array. */
|
||||
void UseExternalData(double *d, int s)
|
||||
{
|
||||
data.Wrap(d, (s*(s+1))/2, false);
|
||||
height = s; width = s;
|
||||
}
|
||||
|
||||
/// Change the data array and the size of the DenseSymmetricMatrix.
|
||||
/** The DenseSymmetricMatrix does not assume ownership of the data array, i.e. it will
|
||||
not delete the new array @a d. This method will delete the current data
|
||||
array, if owned. */
|
||||
void Reset(double *d, int s)
|
||||
{ if (OwnsData()) { data.Delete(); } UseExternalData(d, s); }
|
||||
|
||||
/** Clear the data array and the dimensions of the DenseSymmetricMatrix. This method
|
||||
should not be used with DenseSymmetricMatrix that owns its current data array. */
|
||||
void ClearExternalData() { data.Reset(); height = width = 0; }
|
||||
|
||||
/// Delete the matrix data array (if owned) and reset the matrix state.
|
||||
void Clear()
|
||||
{ if (OwnsData()) { data.Delete(); } ClearExternalData(); }
|
||||
|
||||
/// Change the size of the DenseSymmetricMatrix to s x s.
|
||||
void SetSize(int s);
|
||||
|
||||
/// Returns the matrix data array.
|
||||
inline double *Data() const
|
||||
{ return const_cast<double*>((const double*)data);}
|
||||
|
||||
/// Returns the matrix data array.
|
||||
inline double *GetData() const { return Data(); }
|
||||
|
||||
Memory<double> &GetMemory() { return data; }
|
||||
const Memory<double> &GetMemory() const { return data; }
|
||||
|
||||
/// Return the DenseSymmetricMatrix data (host pointer) ownership flag.
|
||||
inline bool OwnsData() const { return data.OwnsHostPtr(); }
|
||||
|
||||
/// Returns reference to a_{ij}.
|
||||
inline double &operator()(int i, int j);
|
||||
|
||||
/// Returns constant reference to a_{ij}.
|
||||
inline const double &operator()(int i, int j) const;
|
||||
|
||||
/// Returns reference to a_{ij}.
|
||||
virtual double &Elem(int i, int j);
|
||||
|
||||
/// Returns constant reference to a_{ij}.
|
||||
virtual const double &Elem(int i, int j) const;
|
||||
|
||||
/// Sets the matrix elements equal to constant c
|
||||
DenseSymmetricMatrix &operator=(double c);
|
||||
|
||||
DenseSymmetricMatrix &operator*=(double c);
|
||||
|
||||
long MemoryUsage() const { return data.Capacity() * sizeof(double); }
|
||||
|
||||
/// Shortcut for mfem::Read( GetMemory(), TotalSize(), on_dev).
|
||||
const double *Read(bool on_dev = true) const
|
||||
{ return mfem::Read(data, Height()*Width(), on_dev); }
|
||||
|
||||
/// Shortcut for mfem::Read(GetMemory(), TotalSize(), false).
|
||||
const double *HostRead() const
|
||||
{ return mfem::Read(data, Height()*Width(), false); }
|
||||
|
||||
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), on_dev).
|
||||
double *Write(bool on_dev = true)
|
||||
{ return mfem::Write(data, Height()*Width(), on_dev); }
|
||||
|
||||
/// Shortcut for mfem::Write(GetMemory(), TotalSize(), false).
|
||||
double *HostWrite()
|
||||
{ return mfem::Write(data, Height()*Width(), false); }
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), on_dev).
|
||||
double *ReadWrite(bool on_dev = true)
|
||||
{ return mfem::ReadWrite(data, Height()*Width(), on_dev); }
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(GetMemory(), TotalSize(), false).
|
||||
double *HostReadWrite()
|
||||
{ return mfem::ReadWrite(data, Height()*Width(), false); }
|
||||
|
||||
/// Matrix vector multiplication.
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Returns a pointer to (an approximation) of the matrix inverse.
|
||||
virtual MatrixInverse *Inverse() const;
|
||||
|
||||
/// Prints matrix to stream out.
|
||||
virtual void Print (std::ostream & out = mfem::out, int width_ = 4) const;
|
||||
|
||||
/// Destroys the symmetric matrix.
|
||||
virtual ~DenseSymmetricMatrix();
|
||||
};
|
||||
|
||||
// Inline methods
|
||||
|
||||
// The number of entries stored in rows 1,...,k is
|
||||
// n + n-1 + n-2 + ... + n-k+1, where there are k terms. This equals
|
||||
// kn - sum_{i=1}^{k-1} i = kn - (k-1)k/2
|
||||
// This formula is used for the offset for each row.
|
||||
inline double &DenseSymmetricMatrix::operator()(int i, int j)
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
|
||||
if (i > j) // reverse i and j
|
||||
{
|
||||
return data[(j*height) - (((j-1)*j)/2) + i - j];
|
||||
}
|
||||
else
|
||||
{
|
||||
return data[(i*height) - (((i-1)*i)/2) + j - i];
|
||||
}
|
||||
}
|
||||
|
||||
inline const double &DenseSymmetricMatrix::operator()(int i, int j) const
|
||||
{
|
||||
MFEM_ASSERT(data && i >= 0 && i < height && j >= 0 && j < width, "");
|
||||
if (i > j) // reverse i and j
|
||||
{
|
||||
return data[(j*height) - (((j-1)*j)/2) + i - j];
|
||||
}
|
||||
else
|
||||
{
|
||||
return data[(i*height) - (((i-1)*i)/2) + j - i];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
+30
-1
@@ -22,6 +22,10 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
@@ -1076,6 +1080,30 @@ double Vector::operator*(const Vector &v) const
|
||||
#ifdef MFEM_USE_OPENMP
|
||||
if (Device::Allows(Backend::OMP_MASK))
|
||||
{
|
||||
#define MFEM_USE_OPENMP_DETERMINISTIC_DOT
|
||||
#ifdef MFEM_USE_OPENMP_DETERMINISTIC_DOT
|
||||
// By default, use a deterministic way of computing the dot product
|
||||
static Vector th_dot;
|
||||
#pragma omp parallel
|
||||
{
|
||||
const int nt = omp_get_num_threads();
|
||||
#pragma omp master
|
||||
th_dot.SetSize(nt);
|
||||
const int tid = omp_get_thread_num();
|
||||
const int stride = (size + nt - 1)/nt;
|
||||
const int start = tid*stride;
|
||||
const int stop = std::min(start + stride, size);
|
||||
double my_dot = 0.0;
|
||||
for (int i = start; i < stop; i++)
|
||||
{
|
||||
my_dot += m_data[i] * v_data[i];
|
||||
}
|
||||
#pragma omp barrier
|
||||
th_dot(tid) = my_dot;
|
||||
}
|
||||
return th_dot.Sum();
|
||||
#else
|
||||
// The standard way of computing the dot product is non-deterministic
|
||||
double prod = 0.0;
|
||||
#pragma omp parallel for reduction(+:prod)
|
||||
for (int i = 0; i < size; i++)
|
||||
@@ -1083,8 +1111,9 @@ double Vector::operator*(const Vector &v) const
|
||||
prod += m_data[i] * v_data[i];
|
||||
}
|
||||
return prod;
|
||||
#endif // MFEM_USE_OPENMP_DETERMINISTIC_DOT
|
||||
}
|
||||
#endif
|
||||
#endif // MFEM_USE_OPENMP
|
||||
if (Device::Allows(Backend::DEBUG_DEVICE))
|
||||
{
|
||||
const int N = size;
|
||||
|
||||
@@ -42,10 +42,19 @@ namespace mfem
|
||||
inline int CheckFinite(const double *v, const int n);
|
||||
|
||||
/// Define a shortcut for std::numeric_limits<double>::infinity()
|
||||
#ifndef __CYGWIN__
|
||||
inline double infinity()
|
||||
{
|
||||
return std::numeric_limits<double>::infinity();
|
||||
}
|
||||
#else
|
||||
// On Cygwin math.h defines a function 'infinity()' which will conflict with the
|
||||
// above definition if we have 'using namespace mfem;' and try to use something
|
||||
// like 'double a = infinity();'. This 'infinity()' function is non-standard and
|
||||
// is defined by the Newlib C standard library implementation used by Cygwin,
|
||||
// see https://en.wikipedia.org/wiki/Newlib, http://www.sourceware.org/newlib.
|
||||
using ::infinity;
|
||||
#endif
|
||||
|
||||
/// Vector data type.
|
||||
class Vector
|
||||
|
||||
@@ -536,7 +536,7 @@ clean: $(addsuffix /clean,$(EM_DIRS) $(TEST_DIRS))
|
||||
distclean: clean config/clean doc/clean
|
||||
rm -rf mfem/
|
||||
|
||||
INSTALL_SHARED_LIB = $(MFEM_CXX) $(MFEM_BUILD_FLAGS) $(INSTALL_SOFLAGS)\
|
||||
INSTALL_SHARED_LIB = $(MFEM_CXX) $(MFEM_LINK_FLAGS) $(INSTALL_SOFLAGS)\
|
||||
$(OBJECT_FILES) $(EXT_LIBS) -o $(PREFIX_LIB)/libmfem.$(SO_VER) && \
|
||||
cd $(PREFIX_LIB) && ln -sf libmfem.$(SO_VER) libmfem.$(SO_EXT)
|
||||
|
||||
|
||||
+31
-4
@@ -71,10 +71,14 @@ void Mesh::GetElementCenter(int i, Vector ¢er)
|
||||
eltransf->Transform(Geometries.GetCenter(geom), center);
|
||||
}
|
||||
|
||||
double Mesh::GetElementSize(int i, int type)
|
||||
double Mesh::GetElementSize(ElementTransformation *T, int type)
|
||||
{
|
||||
DenseMatrix J(Dim);
|
||||
GetElementJacobian(i, J);
|
||||
|
||||
Geometry::Type geom = T->GetGeometryType();
|
||||
T->SetIntPoint(&Geometries.GetCenter(geom));
|
||||
Geometries.JacToPerfJac(geom, T->Jacobian(), J);
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
return pow(fabs(J.Det()), 1./Dim);
|
||||
@@ -89,6 +93,11 @@ double Mesh::GetElementSize(int i, int type)
|
||||
}
|
||||
}
|
||||
|
||||
double Mesh::GetElementSize(int i, int type)
|
||||
{
|
||||
return GetElementSize(GetElementTransformation(i), type);
|
||||
}
|
||||
|
||||
double Mesh::GetElementSize(int i, const Vector &dir)
|
||||
{
|
||||
DenseMatrix J(Dim);
|
||||
@@ -1040,6 +1049,13 @@ void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2) const
|
||||
*Inf2 = faces_info[Face].Elem2Inf;
|
||||
}
|
||||
|
||||
void Mesh::GetFaceInfos(int Face, int *Inf1, int *Inf2, int *NCFace) const
|
||||
{
|
||||
*Inf1 = faces_info[Face].Elem1Inf;
|
||||
*Inf2 = faces_info[Face].Elem2Inf;
|
||||
*NCFace = faces_info[Face].NCFace;
|
||||
}
|
||||
|
||||
Geometry::Type Mesh::GetFaceGeometryType(int Face) const
|
||||
{
|
||||
switch (Dim)
|
||||
@@ -1463,6 +1479,13 @@ void Mesh::AddBdrQuadAsTriangles(const int *vi, int attr)
|
||||
}
|
||||
}
|
||||
|
||||
int Mesh::AddBdrPoint(int v, int attr)
|
||||
{
|
||||
CheckEnlarge(boundary, NumOfBdrElements);
|
||||
boundary[NumOfBdrElements] = new Point(&v, attr);
|
||||
return NumOfBdrElements++;
|
||||
}
|
||||
|
||||
void Mesh::GenerateBoundaryElements()
|
||||
{
|
||||
int i, j;
|
||||
@@ -5967,6 +5990,7 @@ int *Mesh::GeneratePartitioning(int nparts, int part_method)
|
||||
el_to_el = NULL;
|
||||
|
||||
// Check for empty partitionings (a "feature" in METIS)
|
||||
if (nparts > 1 && NumOfElements > nparts)
|
||||
{
|
||||
Array< Pair<int,int> > psize(nparts);
|
||||
int empty_parts;
|
||||
@@ -8913,9 +8937,11 @@ void Mesh::PrintVTK(std::ostream &out)
|
||||
const int *v = elements[i]->GetVertices();
|
||||
const int nv = elements[i]->GetNVertices();
|
||||
out << nv;
|
||||
Geometry::Type geom = elements[i]->GetGeometryType();
|
||||
const int *perm = (geom == Geometry::PRISM) ? vtk_prism_perm : NULL;
|
||||
for (int j = 0; j < nv; j++)
|
||||
{
|
||||
out << ' ' << v[j];
|
||||
out << ' ' << v[perm ? perm[j] : j];
|
||||
}
|
||||
out << '\n';
|
||||
}
|
||||
@@ -9243,9 +9269,10 @@ void Mesh::PrintVTU(std::ostream &out, int ref, VTKFormat format,
|
||||
{
|
||||
coff = coff+nv;
|
||||
offset.push_back(coff);
|
||||
const int *p = (geom == Geometry::PRISM) ? vtk_prism_perm : NULL;
|
||||
for (int k = 0; k < nv; k++, j++)
|
||||
{
|
||||
WriteBinaryOrASCII(out, buf, np + RG[j], " ", format);
|
||||
WriteBinaryOrASCII(out, buf, np + RG[p ? p[j] : j], " ", format);
|
||||
}
|
||||
if (format == VTKFormat::ASCII) { out << '\n'; }
|
||||
}
|
||||
|
||||
@@ -471,6 +471,8 @@ protected:
|
||||
void GetElementData(const Array<Element*> &elem_array, int geom,
|
||||
Array<int> &elem_vtx, Array<int> &attr) const;
|
||||
|
||||
double GetElementSize(ElementTransformation *T, int type = 0);
|
||||
|
||||
public:
|
||||
|
||||
Mesh() { SetEmpty(); }
|
||||
@@ -555,6 +557,8 @@ public:
|
||||
int AddBdrQuad(const int *vi, int attr = 1);
|
||||
void AddBdrQuadAsTriangles(const int *vi, int attr = 1);
|
||||
|
||||
int AddBdrPoint(int v, int attr = 1);
|
||||
|
||||
void GenerateBoundaryElements();
|
||||
/// Finalize the construction of a triangular Mesh.
|
||||
void FinalizeTriMesh(int generate_edges = 0, int refine = 0,
|
||||
@@ -1032,6 +1036,7 @@ public:
|
||||
}
|
||||
void GetFaceElements (int Face, int *Elem1, int *Elem2) const;
|
||||
void GetFaceInfos (int Face, int *Inf1, int *Inf2) const;
|
||||
void GetFaceInfos (int Face, int *Inf1, int *Inf2, int *NCFace) const;
|
||||
|
||||
Geometry::Type GetFaceGeometryType(int Face) const;
|
||||
Element::Type GetFaceElementType(int Face) const;
|
||||
|
||||
@@ -1735,6 +1735,11 @@ void ParMesh::GetFaceNbrElementTransformation(
|
||||
}
|
||||
}
|
||||
|
||||
double ParMesh::GetFaceNbrElementSize(int i, int type)
|
||||
{
|
||||
return GetElementSize(GetFaceNbrElementTransformation(i), type);
|
||||
}
|
||||
|
||||
void ParMesh::DeleteFaceNbrData()
|
||||
{
|
||||
if (!have_face_nbr_data)
|
||||
|
||||
@@ -284,6 +284,7 @@ public:
|
||||
int ordering = 1);
|
||||
|
||||
int GetNFaceNeighbors() const { return face_nbr_group.Size(); }
|
||||
int GetNFaceNeighborElements() const { return face_nbr_elements.Size(); }
|
||||
int GetFaceNbrGroup(int fn) const { return face_nbr_group[fn]; }
|
||||
int GetFaceNbrRank(int fn) const;
|
||||
|
||||
@@ -305,6 +306,10 @@ public:
|
||||
return &FaceNbrTransformation;
|
||||
}
|
||||
|
||||
/// Get the size of the i-th face neighbor element relative to the reference
|
||||
/// element.
|
||||
double GetFaceNbrElementSize(int i, int type=0);
|
||||
|
||||
/// Return the number of shared faces (3D), edges (2D), vertices (1D)
|
||||
int GetNSharedFaces() const;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user