Compare commits
488
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd6eca6ada | ||
|
|
e823646017 | ||
|
|
7ae181ba6a | ||
|
|
8e1c02db18 | ||
|
|
cbbef06624 | ||
|
|
0f641e344f | ||
|
|
0d9146d364 | ||
|
|
9d236c575c | ||
|
|
998f4b5578 | ||
|
|
ebf7d3e1ef | ||
|
|
06054386c0 | ||
|
|
b09086725d | ||
|
|
1155c003a8 | ||
|
|
20f92199d2 | ||
|
|
841769637b | ||
|
|
ecef55a712 | ||
|
|
5d8cf0d37e | ||
|
|
759987ef31 | ||
|
|
316bb189c9 | ||
|
|
b5682f808f | ||
|
|
a77fb59626 | ||
|
|
2550df844d | ||
|
|
e46d6bec93 | ||
|
|
1e21230bc9 | ||
|
|
36796e6d55 | ||
|
|
83669def7d | ||
|
|
2399f47207 | ||
|
|
35882669b9 | ||
|
|
3f9443d497 | ||
|
|
9794db95c5 | ||
|
|
928d66caba | ||
|
|
d09826e403 | ||
|
|
bbe4b002f1 | ||
|
|
0ae9e69567 | ||
|
|
718e9b2ce3 | ||
|
|
17a8e525a4 | ||
|
|
dbd5cfcb4c | ||
|
|
23074f0e35 | ||
|
|
423ca596d8 | ||
|
|
57975eb915 | ||
|
|
46899fcbb5 | ||
|
|
41c5dafc2a | ||
|
|
0fa2698cc7 | ||
|
|
fb2e25d068 | ||
|
|
2faf0ae640 | ||
|
|
eae4e1128c | ||
|
|
b31f086d3c | ||
|
|
01e8b1da07 | ||
|
|
db53ce67bb | ||
|
|
7dd8e562d5 | ||
|
|
92a87e1253 | ||
|
|
f4c2758e82 | ||
|
|
9535299c2a | ||
|
|
9d87c4efcd | ||
|
|
c5ceee3ef5 | ||
|
|
3d946569a1 | ||
|
|
a8ebd745a8 | ||
|
|
3191cfa1c3 | ||
|
|
c0b3dc0097 | ||
|
|
388e07a373 | ||
|
|
320deb43ec | ||
|
|
04444b15d3 | ||
|
|
caf5d7b471 | ||
|
|
736b2c79b2 | ||
|
|
92748c1c70 | ||
|
|
7c27b32e90 | ||
|
|
f442f6cee1 | ||
|
|
1342a59943 | ||
|
|
fe23fcd39b | ||
|
|
de9fb83efe | ||
|
|
60b1997858 | ||
|
|
de990a5fdc | ||
|
|
3ee90e4c0e | ||
|
|
b4c53cff15 | ||
|
|
69981e1996 | ||
|
|
f84a8d80e0 | ||
|
|
50197feee0 | ||
|
|
108956ea49 | ||
|
|
b581a194e2 | ||
|
|
116ae7bbed | ||
|
|
ba69eadeac | ||
|
|
7e93c72948 | ||
|
|
876fafd76b | ||
|
|
aa1da8bee2 | ||
|
|
d738070ee2 | ||
|
|
c775a8a1a4 | ||
|
|
621ad50df6 | ||
|
|
152b02df4f | ||
|
|
b6ea922b35 | ||
|
|
476e95305d | ||
|
|
d5e291e989 | ||
|
|
6d0f5971ad | ||
|
|
3a21476b5c | ||
|
|
dc044d8e93 | ||
|
|
9974c0c6a9 | ||
|
|
55e117bb82 | ||
|
|
0eaa5824ee | ||
|
|
ae5a84f2ef | ||
|
|
a1a0aeb6af | ||
|
|
241a5a2544 | ||
|
|
b9e26ee081 | ||
|
|
54111e4fd5 | ||
|
|
da7c353df6 | ||
|
|
9b25209337 | ||
|
|
58e6db4118 | ||
|
|
d226010d4e | ||
|
|
805d51a2eb | ||
|
|
6e7e1092db | ||
|
|
a614cac02c | ||
|
|
95e7dd3df8 | ||
|
|
ecd61941d6 | ||
|
|
8f010f3c0c | ||
|
|
bdf0e9b1d0 | ||
|
|
84dcb16949 | ||
|
|
01df630912 | ||
|
|
80ff56cfca | ||
|
|
58bf8f54e3 | ||
|
|
2552255ffe | ||
|
|
8a130243fb | ||
|
|
3acc834f1d | ||
|
|
63a40bf1e9 | ||
|
|
e06097b9af | ||
|
|
09372aea2d | ||
|
|
502b95fb86 | ||
|
|
777d222577 | ||
|
|
be1b800b61 | ||
|
|
b01b4956cf | ||
|
|
4f5619c88a | ||
|
|
5231ed302c | ||
|
|
b796dae62c | ||
|
|
7bcba91890 | ||
|
|
33dfee978b | ||
|
|
73ff58db37 | ||
|
|
7d1bf5a748 | ||
|
|
921f4aaab6 | ||
|
|
a5806ea9eb | ||
|
|
ca721dd1ae | ||
|
|
04f44f2a50 | ||
|
|
de36574fa7 | ||
|
|
0e2735128c | ||
|
|
83c9d7289a | ||
|
|
0bbbc43ebf | ||
|
|
216d50b0ad | ||
|
|
428b515668 | ||
|
|
72924b927b | ||
|
|
bac26a7422 | ||
|
|
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 | ||
|
|
90301913e7 | ||
|
|
2851ca57b7 | ||
|
|
12363d84a1 | ||
|
|
da1df498cb | ||
|
|
b73a913dd2 | ||
|
|
533b45cabd | ||
|
|
4f32a52915 | ||
|
|
1b2f6d2872 | ||
|
|
f0f3dccb79 | ||
|
|
75c206f360 | ||
|
|
c10c41e170 | ||
|
|
02aee8bbbf | ||
|
|
b35a29a82c | ||
|
|
224ea084d8 | ||
|
|
9cda70d880 | ||
|
|
4660886599 | ||
|
|
2c5a2a6045 | ||
|
|
acb253ca96 | ||
|
|
8300ab8fa1 | ||
|
|
883011bc85 | ||
|
|
ab28d29fea | ||
|
|
7deaae57b3 | ||
|
|
4a1ae4af84 | ||
|
|
48f2b999e7 | ||
|
|
7ca85ffcef | ||
|
|
b1ae8d6807 | ||
|
|
a0cdaca8ce | ||
|
|
5f57038f02 | ||
|
|
85a38a3134 | ||
|
|
a617d51b8d | ||
|
|
ffc0a9ee7d | ||
|
|
7083d53dd7 | ||
|
|
1b8fd5466e | ||
|
|
4e81be2155 | ||
|
|
95adb0d115 | ||
|
|
4a0142a1c6 | ||
|
|
69a7d67ad3 | ||
|
|
5dfa41aaf9 | ||
|
|
1cbecbc0cf | ||
|
|
c942da97a6 | ||
|
|
7e9d652aa9 | ||
|
|
9828a5b78d | ||
|
|
51d46e8775 | ||
|
|
34e4dd9a5d | ||
|
|
ff1c2deb4f | ||
|
|
7b3e7ee2fe | ||
|
|
9dc9c9bcf5 | ||
|
|
9250600e11 | ||
|
|
cfead6a03b | ||
|
|
41b4f7183b | ||
|
|
fbb9b4938d | ||
|
|
38d30f034a | ||
|
|
78a4889fd0 | ||
|
|
30e4f991ad | ||
|
|
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 | ||
|
|
e069d9b034 | ||
|
|
021e7baa29 | ||
|
|
e6ceb405e0 | ||
|
|
71c947d966 | ||
|
|
5db9988a41 | ||
|
|
a19b34b403 | ||
|
|
e55fbc7af5 | ||
|
|
8f5d0ef084 | ||
|
|
0cdbcd647e | ||
|
|
35159031ee | ||
|
|
78bc8176e7 | ||
|
|
2237a20063 | ||
|
|
514a0bae58 | ||
|
|
45b2f40d98 | ||
|
|
817e9247e9 | ||
|
|
6994291f15 | ||
|
|
9a84630556 | ||
|
|
ba6d89b4cc | ||
|
|
9d07fea8f4 | ||
|
|
2886dcc849 | ||
|
|
749f3da7cc | ||
|
|
4079f31447 | ||
|
|
90ce59a56b | ||
|
|
1f1b545ec0 | ||
|
|
b3b63da0c2 | ||
|
|
72b919e633 | ||
|
|
a6bac0be4c | ||
|
|
872c4dba86 | ||
|
|
fe1e1a4128 | ||
|
|
374d71f68c | ||
|
|
7a29a36c78 | ||
|
|
e5e58f8328 | ||
|
|
60369fec8f | ||
|
|
7a9f2f966f | ||
|
|
20134f9213 | ||
|
|
cd3745046d | ||
|
|
d220d72015 | ||
|
|
8958460dbf | ||
|
|
c7480fd97e | ||
|
|
f5b648fd4d | ||
|
|
1d609acb2b | ||
|
|
382bf2b8b6 | ||
|
|
b213d80b12 | ||
|
|
fdf86706ea | ||
|
|
2c7f6300e0 | ||
|
|
d11c17cba2 | ||
|
|
c0ca09165f | ||
|
|
17142d2b36 | ||
|
|
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 | ||
|
|
e2ded13c1e | ||
|
|
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,9 @@
|
||||
Version 4.2.1 (development)
|
||||
===========================
|
||||
|
||||
- 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 +23,52 @@ 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 support for reading high-order Lagrange meshes in VTK format. Arbitrary-
|
||||
orders and all element types are supported. See the VTK blog for more info:
|
||||
https://blog.kitware.com/wp-content/uploads/2018/09/Source_Issue_43.pdf
|
||||
|
||||
- Added support for reading VTK meshes in XML format.
|
||||
|
||||
- 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.
|
||||
|
||||
- Added support for AMG preconditioners for non-symmetric systems (e.g.
|
||||
advection-dominated problems) using hypre's approximate ideal restriction
|
||||
(AIR) AMG. Requires hypre version 2.14.0 or newer. Usage is illustrated in
|
||||
example 9/9p.
|
||||
|
||||
- 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
|
||||
=========================================
|
||||
|
||||
+3
-2
@@ -9,12 +9,13 @@
|
||||
# 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.")
|
||||
|
||||
# Require C++11 and disable compiler-specific extensions
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -29,4 +29,5 @@ license files. These software products and their licenses are as follows:
|
||||
* Catch++ (tests/unit/catch.hpp) -- Boost 1.0 license
|
||||
* Gecko (general/gecko.{cpp,hpp}) -- BSD 3-clause license
|
||||
* Picojson (fem/picojson.h) -- Custom 2-clause license
|
||||
* TinyXML2 (general/tinyxml2.{cpp,h}) -- zlib license
|
||||
* Zstr (general/zstr.hpp) -- MIT license
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -819,7 +819,9 @@ RECURSIVE = NO
|
||||
# run.
|
||||
|
||||
EXCLUDE = @MFEM_SOURCE_DIR@/config/_config.hpp \
|
||||
@MFEM_SOURCE_DIR@/config/get_hypre_version.cpp
|
||||
@MFEM_SOURCE_DIR@/config/get_hypre_version.cpp \
|
||||
@MFEM_SOURCE_DIR@/general/tinyxml2.h \
|
||||
@MFEM_SOURCE_DIR@/general/tinyxml2.cpp
|
||||
|
||||
# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
|
||||
# directories that are symbolic links (a Unix file system feature) are excluded
|
||||
|
||||
@@ -34,6 +34,7 @@ list(APPEND ALL_EXE_SRCS
|
||||
ex25.cpp
|
||||
ex26.cpp
|
||||
ex27.cpp
|
||||
ex91.cpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
|
||||
@@ -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;
|
||||
|
||||
+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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
+5
-4
@@ -290,15 +290,16 @@ int main(int argc, char *argv[])
|
||||
k.SetAssemblyLevel(AssemblyLevel::FULL);
|
||||
}
|
||||
m.AddDomainIntegrator(new MassIntegrator);
|
||||
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
|
||||
constexpr double alpha = -1.0;
|
||||
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, alpha));
|
||||
k.AddInteriorFaceIntegrator(
|
||||
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
|
||||
new NonconservativeDGTraceIntegrator(velocity, alpha));
|
||||
k.AddBdrFaceIntegrator(
|
||||
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
|
||||
new NonconservativeDGTraceIntegrator(velocity, alpha));
|
||||
|
||||
LinearForm b(&fes);
|
||||
b.AddBdrFaceIntegrator(
|
||||
new BoundaryFlowIntegrator(inflow, velocity, -1.0, -0.5));
|
||||
new BoundaryFlowIntegrator(inflow, velocity, alpha));
|
||||
|
||||
m.Assemble();
|
||||
int skip_zeros = 0;
|
||||
|
||||
@@ -0,0 +1,672 @@
|
||||
#include"mfem.hpp"
|
||||
|
||||
#include<memory>
|
||||
#include<iostream>
|
||||
#include<fstream>
|
||||
|
||||
namespace mfem {
|
||||
|
||||
|
||||
class LinDiffQFunc
|
||||
{
|
||||
public:
|
||||
LinDiffQFunc(mfem::Coefficient& dd, mfem::Coefficient& ll, double gg_,
|
||||
double pp0_, double pp1_):diff(dd),load(ll),gg(gg_),pp0(pp0_),pp1(pp1_)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
double QEnergy(ElementTransformation &T,
|
||||
const IntegrationPoint &ip,
|
||||
mfem::Vector& param, mfem::Vector& uu)
|
||||
{
|
||||
double dd=diff.Eval(T,ip);
|
||||
double ll=load.Eval(T,ip);
|
||||
|
||||
double rho0=param[0];
|
||||
double rho1=param[1];
|
||||
double fd=dd*std::pow(rho0,pp0)*std::pow(rho1,pp1);
|
||||
|
||||
double rez = 0.5*(uu[0]*uu[0]+uu[1]*uu[1]+uu[2]*uu[2])*fd
|
||||
+ 0.5*gg*uu[3]*uu[3] -uu[3]*ll;
|
||||
|
||||
return rez;
|
||||
}
|
||||
|
||||
void QResidual(ElementTransformation &T,
|
||||
const IntegrationPoint &ip,
|
||||
mfem::Vector& param, mfem::Vector& uu, mfem::Vector& rr)
|
||||
{
|
||||
rr.SetSize(4);
|
||||
double dd=diff.Eval(T,ip);
|
||||
double ll=load.Eval(T,ip);
|
||||
|
||||
double rho0=param[0];
|
||||
double rho1=param[1];
|
||||
|
||||
double fd=dd*std::pow(rho0,pp0)*std::pow(rho1,pp1);
|
||||
|
||||
rr[0]=uu[0]*fd;
|
||||
rr[1]=uu[1]*fd;
|
||||
rr[2]=uu[2]*fd;
|
||||
rr[3]=gg*uu[3]-ll;
|
||||
}
|
||||
|
||||
void AQResidual(ElementTransformation &T,
|
||||
const IntegrationPoint &ip,
|
||||
mfem::Vector& param,
|
||||
mfem::Vector& uu, mfem::Vector& aa, mfem::Vector& rr)
|
||||
{
|
||||
rr.SetSize(2);
|
||||
double dd=diff.Eval(T,ip);
|
||||
double ll=load.Eval(T,ip);
|
||||
|
||||
double rho0=param[0];
|
||||
double rho1=param[1];
|
||||
|
||||
double fd0=dd*pp0*std::pow(rho0,pp0-1.0)*std::pow(rho1,pp1);
|
||||
double fd1=dd*std::pow(rho0,pp0)*pp1*std::pow(rho1,pp1-1.0);
|
||||
|
||||
rr[0] = (aa[0]*uu[0]+aa[1]*uu[1]+aa[2]*uu[2])*fd0;
|
||||
rr[1] = (aa[0]*uu[0]+aa[1]*uu[1]+aa[2]*uu[2])*fd1;
|
||||
|
||||
}
|
||||
|
||||
void QGradResidual(ElementTransformation &T,
|
||||
const IntegrationPoint &ip,
|
||||
mfem::Vector& param, mfem::Vector& uu, mfem::DenseMatrix& hh)
|
||||
{
|
||||
hh.SetSize(4);
|
||||
double dd=diff.Eval(T,ip);
|
||||
//double ll=load.Eval(T,ip);
|
||||
|
||||
|
||||
double rho0=param[0];
|
||||
double rho1=param[1];
|
||||
|
||||
double fd=dd*std::pow(rho0,pp0)*std::pow(rho1,pp1);
|
||||
hh=0.0;
|
||||
|
||||
hh(0,0)=fd;
|
||||
hh(1,1)=fd;
|
||||
hh(2,2)=fd;
|
||||
hh(3,3)=gg;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
mfem::Coefficient& diff;
|
||||
mfem::Coefficient& load;
|
||||
double gg;
|
||||
double pp0;
|
||||
double pp1;
|
||||
};
|
||||
|
||||
|
||||
class PrmBlockLSFEMDiffusion: public PrmBlockNonlinearFormIntegrator
|
||||
{
|
||||
public:
|
||||
PrmBlockLSFEMDiffusion(LinDiffQFunc& qfun_)
|
||||
{
|
||||
qfunc=&qfun_;
|
||||
}
|
||||
|
||||
/// Compute the local energy
|
||||
virtual double GetElementEnergy(const Array<const FiniteElement *>&el,
|
||||
const Array<const FiniteElement *>&pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *>&elfun,
|
||||
const Array<const Vector *>&pelfun)
|
||||
{
|
||||
int dof_u0 = el[0]->GetDof();
|
||||
int dof_r0 = pel[0]->GetDof();
|
||||
int dof_r1 = pel[1]->GetDof();
|
||||
|
||||
int dim = el[0]->GetDim();
|
||||
int spaceDim = Tr.GetSpaceDim();
|
||||
if (dim != spaceDim)
|
||||
{
|
||||
mfem::mfem_error(" PrmBlockLSFEMDiffusion::GetElementEnergy"
|
||||
" is not defined on manifold meshes");
|
||||
}
|
||||
|
||||
//shape functions
|
||||
Vector shu0(dof_u0);
|
||||
Vector shr0(dof_r0);
|
||||
Vector shr1(dof_r1);
|
||||
DenseMatrix dsu0(dof_u0,dim);
|
||||
DenseMatrix B(dof_u0, 4);
|
||||
B=0.0;
|
||||
|
||||
double w;
|
||||
|
||||
Vector param(2); param=0.0;
|
||||
Vector uu(4); uu=0.0;
|
||||
|
||||
double energy =0.0;
|
||||
|
||||
const IntegrationRule *ir = nullptr;
|
||||
if(ir==nullptr){
|
||||
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
|
||||
+pel[0]->GetOrder()+pel[1]->GetOrder();
|
||||
ir=&IntRules.Get(Tr.GetGeometryType(),order);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
Tr.SetIntPoint(&ip);
|
||||
w=Tr.Weight();
|
||||
w = ip.weight * w;
|
||||
|
||||
el[0]->CalcPhysDShape(Tr,dsu0);
|
||||
el[0]->CalcPhysShape(Tr,shu0);
|
||||
pel[0]->CalcPhysShape(Tr,shr0);
|
||||
pel[1]->CalcPhysShape(Tr,shr1);
|
||||
|
||||
param[0]=shr0*(*pelfun[0]);
|
||||
param[1]=shr1*(*pelfun[1]);
|
||||
|
||||
//set the matrix B
|
||||
for(int jj=0;jj<dim;jj++)
|
||||
{
|
||||
B.SetCol(jj,dsu0.GetColumn(jj));
|
||||
}
|
||||
B.SetCol(3,shu0);
|
||||
B.MultTranspose(*elfun[0],uu);
|
||||
energy=energy+w * qfunc->QEnergy(Tr,ip,param,uu);
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
|
||||
/// Perform the local action of the BlockNonlinearFormIntegrator
|
||||
virtual void AssembleElementVector(const Array<const FiniteElement *> &el,
|
||||
const Array<const FiniteElement *>&pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array<Vector *> &elvec)
|
||||
{
|
||||
int dof_u0 = el[0]->GetDof();
|
||||
int dof_r0 = pel[0]->GetDof();
|
||||
int dof_r1 = pel[1]->GetDof();
|
||||
|
||||
int dim = el[0]->GetDim();
|
||||
|
||||
elvec[0]->SetSize(dof_u0);
|
||||
*elvec[0]=0.0;
|
||||
int spaceDim = Tr.GetSpaceDim();
|
||||
if (dim != spaceDim)
|
||||
{
|
||||
mfem::mfem_error(" PrmBlockLSFEMDiffusion::AssembleElementVector"
|
||||
" is not defined on manifold meshes");
|
||||
}
|
||||
|
||||
//shape functions
|
||||
Vector shu0(dof_u0);
|
||||
Vector shr0(dof_r0);
|
||||
Vector shr1(dof_r1);
|
||||
DenseMatrix dsu0(dof_u0,dim);
|
||||
DenseMatrix B(dof_u0, 4);
|
||||
B=0.0;
|
||||
|
||||
double w;
|
||||
|
||||
Vector param(2); param=0.0;
|
||||
Vector uu(4); uu=0.0;
|
||||
Vector rr;
|
||||
Vector lvec; lvec.SetSize(dof_u0);
|
||||
|
||||
const IntegrationRule *ir = nullptr;
|
||||
if(ir==nullptr){
|
||||
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
|
||||
+pel[0]->GetOrder()+pel[1]->GetOrder();
|
||||
ir=&IntRules.Get(Tr.GetGeometryType(),order);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
Tr.SetIntPoint(&ip);
|
||||
w=Tr.Weight();
|
||||
w = ip.weight * w;
|
||||
|
||||
el[0]->CalcPhysDShape(Tr,dsu0);
|
||||
el[0]->CalcPhysShape(Tr,shu0);
|
||||
pel[0]->CalcPhysShape(Tr,shr0);
|
||||
pel[1]->CalcPhysShape(Tr,shr1);
|
||||
|
||||
param[0]=shr0*(*pelfun[0]);
|
||||
param[1]=shr1*(*pelfun[1]);
|
||||
|
||||
//set the matrix B
|
||||
for(int jj=0;jj<dim;jj++)
|
||||
{
|
||||
B.SetCol(jj,dsu0.GetColumn(jj));
|
||||
}
|
||||
B.SetCol(3,shu0);
|
||||
B.MultTranspose(*elfun[0],uu);
|
||||
qfunc->QResidual(Tr,ip,param, uu, rr);
|
||||
|
||||
B.Mult(rr,lvec);
|
||||
elvec[0]->Add(w,lvec);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual void AssembleFaceVector(const Array<const FiniteElement *> &el1,
|
||||
const Array<const FiniteElement *> &el2,
|
||||
const Array<const FiniteElement *> &pel1,
|
||||
const Array<const FiniteElement *> &pel2,
|
||||
FaceElementTransformations &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array<Vector *> &elvect)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// Assemble the local gradient matrix
|
||||
virtual void AssembleElementGrad(const Array<const FiniteElement*> &el,
|
||||
const Array<const FiniteElement *>&pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array2D<DenseMatrix *> &elmats)
|
||||
{
|
||||
int dof_u0 = el[0]->GetDof();
|
||||
int dof_r0 = pel[0]->GetDof();
|
||||
int dof_r1 = pel[1]->GetDof();
|
||||
|
||||
int dim = el[0]->GetDim();
|
||||
|
||||
//elmats[0]->Size(dof_u0, dof_u0);
|
||||
//*elmats[0]=0.0;
|
||||
|
||||
DenseMatrix* K=elmats(0,0);
|
||||
K->SetSize(dof_u0,dof_u0);
|
||||
(*K)=0.0;
|
||||
|
||||
int spaceDim = Tr.GetSpaceDim();
|
||||
if (dim != spaceDim)
|
||||
{
|
||||
mfem::mfem_error(" PrmBlockLSFEMDiffusion::AssembleElementVector"
|
||||
" is not defined on manifold meshes");
|
||||
}
|
||||
|
||||
//shape functions
|
||||
Vector shu0(dof_u0);
|
||||
Vector shr0(dof_r0);
|
||||
Vector shr1(dof_r1);
|
||||
DenseMatrix dsu0(dof_u0,dim);
|
||||
DenseMatrix B(dof_u0, 4);
|
||||
DenseMatrix A(dof_u0, 4);
|
||||
B=0.0;
|
||||
|
||||
double w;
|
||||
|
||||
Vector param(2); param=0.0;
|
||||
Vector uu(4); uu=0.0;
|
||||
DenseMatrix hh;
|
||||
Vector lvec; lvec.SetSize(dof_u0);
|
||||
|
||||
const IntegrationRule *ir = nullptr;
|
||||
if(ir==nullptr){
|
||||
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
|
||||
+pel[0]->GetOrder()+pel[1]->GetOrder();
|
||||
ir=&IntRules.Get(Tr.GetGeometryType(),order);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
Tr.SetIntPoint(&ip);
|
||||
w = Tr.Weight();
|
||||
w = ip.weight * w;
|
||||
|
||||
el[0]->CalcPhysDShape(Tr,dsu0);
|
||||
el[0]->CalcPhysShape(Tr,shu0);
|
||||
pel[0]->CalcPhysShape(Tr,shr0);
|
||||
pel[1]->CalcPhysShape(Tr,shr1);
|
||||
|
||||
param[0]=shr0*(*pelfun[0]);
|
||||
param[1]=shr1*(*pelfun[1]);
|
||||
|
||||
//set the matrix B
|
||||
for(int jj=0;jj<dim;jj++)
|
||||
{
|
||||
B.SetCol(jj,dsu0.GetColumn(jj));
|
||||
}
|
||||
B.SetCol(3,shu0);
|
||||
B.MultTranspose(*elfun[0],uu);
|
||||
qfunc->QGradResidual(Tr,ip,param,uu,hh);
|
||||
Mult(B,hh,A);
|
||||
AddMult_a_ABt(w,A,B,*K);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void AssembleFaceGrad(const Array<const FiniteElement *>&el1,
|
||||
const Array<const FiniteElement *>&el2,
|
||||
const Array<const FiniteElement *> &pel1,
|
||||
const Array<const FiniteElement *> &pel2,
|
||||
FaceElementTransformations &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array2D<DenseMatrix *> &elmats)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void AssemblePrmElementVector(const Array<const FiniteElement *> &el,
|
||||
const Array<const FiniteElement *> &pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &alfun,
|
||||
const Array<const Vector *> &pelfun,
|
||||
const Array<Vector *> &elvec)
|
||||
{
|
||||
int dof_u0 = el[0]->GetDof();
|
||||
int dof_r0 = pel[0]->GetDof();
|
||||
int dof_r1 = pel[1]->GetDof();
|
||||
|
||||
int dim = el[0]->GetDim();
|
||||
|
||||
Vector& e0 = *(elvec[0]);
|
||||
Vector& e1 = *(elvec[1]);
|
||||
|
||||
e0.SetSize(dof_r0);
|
||||
e0=0.0;
|
||||
e1.SetSize(dof_r1);
|
||||
e1=0.0;
|
||||
|
||||
int spaceDim = Tr.GetSpaceDim();
|
||||
if (dim != spaceDim)
|
||||
{
|
||||
mfem::mfem_error(" PrmBlockLSFEMDiffusion::AssembleElementVector"
|
||||
" is not defined on manifold meshes");
|
||||
}
|
||||
|
||||
//shape functions
|
||||
Vector shu0(dof_u0);
|
||||
Vector shr0(dof_r0);
|
||||
Vector shr1(dof_r1);
|
||||
DenseMatrix dsu0(dof_u0,dim);
|
||||
DenseMatrix B(dof_u0, 4);
|
||||
B=0.0;
|
||||
|
||||
double w;
|
||||
|
||||
Vector param(2); param=0.0;
|
||||
Vector uu(4); uu=0.0;
|
||||
Vector aa(4); aa=0.0;
|
||||
Vector rr;
|
||||
Vector lvec0; lvec0.SetSize(dof_r0);
|
||||
Vector lvec1; lvec1.SetSize(dof_r1);
|
||||
|
||||
const IntegrationRule *ir = nullptr;
|
||||
if(ir==nullptr){
|
||||
int order= 2 * el[0]->GetOrder() + Tr.OrderGrad(el[0])
|
||||
+pel[0]->GetOrder()+pel[1]->GetOrder();
|
||||
ir=&IntRules.Get(Tr.GetGeometryType(),order);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir->GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(i);
|
||||
Tr.SetIntPoint(&ip);
|
||||
w=Tr.Weight();
|
||||
w = ip.weight * w;
|
||||
|
||||
el[0]->CalcPhysDShape(Tr,dsu0);
|
||||
el[0]->CalcPhysShape(Tr,shu0);
|
||||
pel[0]->CalcPhysShape(Tr,shr0);
|
||||
pel[1]->CalcPhysShape(Tr,shr1);
|
||||
|
||||
param[0]=shr0*(*pelfun[0]);
|
||||
param[1]=shr1*(*pelfun[1]);
|
||||
|
||||
//set the matrix B
|
||||
for(int jj=0;jj<dim;jj++)
|
||||
{
|
||||
B.SetCol(jj,dsu0.GetColumn(jj));
|
||||
}
|
||||
B.SetCol(3,shu0);
|
||||
B.MultTranspose(*elfun[0],uu);
|
||||
B.MultTranspose(*alfun[0],aa);
|
||||
|
||||
qfunc->AQResidual(Tr, ip, param, uu, aa, rr);
|
||||
|
||||
lvec0=shr0;
|
||||
lvec0*=rr[0];
|
||||
lvec1=shr1;
|
||||
lvec1*=rr[1];
|
||||
|
||||
e0.Add(w,lvec0);
|
||||
e1.Add(w,lvec1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual void AssemblePrmFaceVector(const Array<const FiniteElement *> &el1,
|
||||
const Array<const FiniteElement *> &el2,
|
||||
const Array<const FiniteElement *> &pel1,
|
||||
const Array<const FiniteElement *> &pel2,
|
||||
FaceElementTransformations &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &alfun,
|
||||
const Array<const Vector *> &pelfun,
|
||||
const Array<Vector *> &elvect)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
LinDiffQFunc* qfunc;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *mesh_file = "../../data/beam-tet.mesh";
|
||||
int ser_ref_levels = 1;
|
||||
int order = 2;
|
||||
bool visualization = true;
|
||||
double newton_rel_tol = 1e-4;
|
||||
double newton_abs_tol = 1e-6;
|
||||
int newton_iter = 10;
|
||||
int print_level = 0;
|
||||
|
||||
mfem::OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
|
||||
args.AddOption(&ser_ref_levels,
|
||||
"-rs",
|
||||
"--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&order,
|
||||
"-o",
|
||||
"--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&visualization,
|
||||
"-vis",
|
||||
"--visualization",
|
||||
"-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.AddOption(&newton_rel_tol,
|
||||
"-rel",
|
||||
"--relative-tolerance",
|
||||
"Relative tolerance for the Newton solve.");
|
||||
args.AddOption(&newton_abs_tol,
|
||||
"-abs",
|
||||
"--absolute-tolerance",
|
||||
"Absolute tolerance for the Newton solve.");
|
||||
args.AddOption(&newton_iter,
|
||||
"-it",
|
||||
"--newton-iterations",
|
||||
"Maximum iterations for the Newton solve.");
|
||||
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(std::cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(std::cout);
|
||||
// 3. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral and hexahedral meshes
|
||||
// with the same code.
|
||||
mfem::Mesh *mesh = new mfem::Mesh(mesh_file, 1, 1);
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 4. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
// a command-line parameter.
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
/// Define the q-function
|
||||
mfem::ConstantCoefficient* dc=new mfem::ConstantCoefficient(1.0);
|
||||
mfem::ConstantCoefficient* lc=new mfem::ConstantCoefficient(1.0);
|
||||
mfem::LinDiffQFunc* qfun=new mfem::LinDiffQFunc(*dc,*lc,1.0,1.0,1.0);
|
||||
|
||||
mfem::H1_FECollection fec00(order, dim);
|
||||
mfem::L2_FECollection fec01(order, dim);
|
||||
mfem::FiniteElementSpace* bfes00=new mfem::FiniteElementSpace(mesh,&fec00,1,mfem::Ordering::byVDIM);
|
||||
mfem::FiniteElementSpace* pfes00=new mfem::FiniteElementSpace(mesh,&fec00,1,mfem::Ordering::byVDIM);
|
||||
mfem::FiniteElementSpace* pfes01=new mfem::FiniteElementSpace(mesh,&fec01,1,mfem::Ordering::byVDIM);
|
||||
|
||||
/// Define parametric nonlinear form
|
||||
mfem::Array<mfem::FiniteElementSpace*> bfes;
|
||||
mfem::Array<mfem::FiniteElementSpace*> pfes;
|
||||
|
||||
bfes.Append(bfes00);
|
||||
pfes.Append(pfes00);
|
||||
pfes.Append(pfes01);
|
||||
|
||||
mfem::PrmBlockNonlinearForm* nf=new mfem::PrmBlockNonlinearForm(bfes,pfes);
|
||||
nf->AddDomainIntegrator(new mfem::PrmBlockLSFEMDiffusion(*qfun));
|
||||
|
||||
/// Define the grid functions
|
||||
mfem::GridFunction* bgf00=new mfem::GridFunction(bfes00);
|
||||
mfem::GridFunction* pgf00=new mfem::GridFunction(pfes00);
|
||||
mfem::GridFunction* pgf01=new mfem::GridFunction(pfes01);
|
||||
mfem::GridFunction* ggf00=new mfem::GridFunction(pfes00);
|
||||
mfem::GridFunction* ggf01=new mfem::GridFunction(pfes01);
|
||||
|
||||
*bgf00=0.0;
|
||||
*pgf00=1.0;
|
||||
*pgf01=1.0;
|
||||
|
||||
mfem::BlockVector solbv; solbv.Update(nf->GetBlockTrueOffsets()); solbv=0.0;
|
||||
mfem::BlockVector resbv; resbv.Update(nf->GetBlockTrueOffsets()); resbv=0.0;
|
||||
mfem::BlockVector adjbv; adjbv.Update(nf->GetBlockTrueOffsets()); adjbv=0.0;
|
||||
mfem::BlockVector prmbv; prmbv.Update(nf->PrmGetBlockTrueOffsets()); prmbv=1.0;
|
||||
mfem::BlockVector grdbv; grdbv.Update(nf->PrmGetBlockTrueOffsets()); grdbv=0.0;
|
||||
|
||||
bgf00->SetFromTrueDofs(solbv.GetBlock(0));
|
||||
pgf00->SetFromTrueDofs(prmbv.GetBlock(0));
|
||||
pgf01->SetFromTrueDofs(prmbv.GetBlock(1));
|
||||
|
||||
nf->SetPrmFields(prmbv);
|
||||
double energy = nf->GetEnergy(solbv);
|
||||
|
||||
nf->Mult(solbv,resbv);
|
||||
std::cout<<"Norm res="<<resbv.Norml2()<<std::endl;
|
||||
|
||||
//mfem::Operator& K=nf->GetGradient(solbv);
|
||||
std::cout<<"energy ="<< energy<<std::endl;
|
||||
|
||||
nf->SetStateFields(solbv);
|
||||
nf->SetAdjointFields(adjbv);
|
||||
nf->PrmMult(prmbv,grdbv);
|
||||
|
||||
|
||||
//set the BC for the physics
|
||||
mfem::Array<mfem::Array<int> *> ess_bdr;
|
||||
mfem::Array<mfem::Vector*> ess_rhs;
|
||||
ess_bdr.Append(new mfem::Array<int>(mesh->bdr_attributes.Max()));
|
||||
ess_rhs.Append(nullptr);
|
||||
(*ess_bdr[0]) = 1;
|
||||
nf->SetEssentialBC(ess_bdr,ess_rhs);
|
||||
|
||||
//define the solvers
|
||||
mfem::UMFPackSolver* umfsolv=new mfem::UMFPackSolver();
|
||||
|
||||
mfem::GMRESSolver *gmres;
|
||||
gmres = new mfem::GMRESSolver();
|
||||
gmres->SetAbsTol(newton_abs_tol/10);
|
||||
gmres->SetRelTol(newton_rel_tol/10);
|
||||
gmres->SetMaxIter(100);
|
||||
gmres->SetPrintLevel(print_level);
|
||||
//gmres->SetPreconditioner(*prec);
|
||||
|
||||
|
||||
mfem::NewtonSolver *ns;
|
||||
ns = new mfem::NewtonSolver();
|
||||
ns->iterative_mode = true;
|
||||
ns->SetSolver(*gmres);
|
||||
ns->SetOperator(*nf);
|
||||
ns->SetPrintLevel(print_level);
|
||||
ns->SetRelTol(newton_rel_tol);
|
||||
ns->SetAbsTol(newton_abs_tol);
|
||||
ns->SetMaxIter(newton_iter);
|
||||
|
||||
mfem::Vector b; //RHS is zero
|
||||
solbv=0.0;
|
||||
ns->Mult(b, solbv);
|
||||
|
||||
|
||||
nf->SetStateFields(solbv);
|
||||
nf->SetAdjointFields(solbv);
|
||||
nf->PrmMult(prmbv,grdbv);
|
||||
|
||||
|
||||
|
||||
|
||||
mfem::ParaViewDataCollection *dacol = new mfem::ParaViewDataCollection("Example91",
|
||||
mesh);
|
||||
|
||||
ggf00->SetFromTrueDofs(grdbv.GetBlock(0));
|
||||
ggf01->SetFromTrueDofs(grdbv.GetBlock(1));
|
||||
pgf00->SetFromTrueDofs(solbv.GetBlock(0));
|
||||
|
||||
dacol->SetLevelsOfDetail(order);
|
||||
dacol->RegisterField("sol", pgf00);
|
||||
dacol->RegisterField("grad00", ggf00);
|
||||
dacol->RegisterField("grad01", ggf01);
|
||||
|
||||
dacol->SetTime(1.0);
|
||||
dacol->SetCycle(1);
|
||||
dacol->Save();
|
||||
|
||||
delete dacol;
|
||||
|
||||
delete ns;
|
||||
delete umfsolv;
|
||||
delete gmres;
|
||||
delete ess_bdr[0];
|
||||
|
||||
delete bgf00;
|
||||
delete pgf00;
|
||||
delete pgf01;
|
||||
delete ggf00;
|
||||
delete ggf01;
|
||||
|
||||
|
||||
delete nf;
|
||||
delete pfes01;
|
||||
delete pfes00;
|
||||
delete bfes00;
|
||||
|
||||
delete qfun;
|
||||
delete lc;
|
||||
delete dc;
|
||||
|
||||
delete mesh;
|
||||
|
||||
}
|
||||
+114
-32
@@ -64,6 +64,66 @@ double inflow_function(const Vector &x);
|
||||
// Mesh bounding box
|
||||
Vector bb_min, bb_max;
|
||||
|
||||
// Type of preconditioner for implicit time integrator
|
||||
enum class PrecType : int
|
||||
{
|
||||
ILU = 0,
|
||||
AIR = 1
|
||||
};
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
// Algebraic multigrid preconditioner for advective problems based on
|
||||
// approximate ideal restriction (AIR). Most effective when matrix is
|
||||
// first scaled by DG block inverse, and AIR applied to scaled matrix.
|
||||
// See https://doi.org/10.1137/17M1144350.
|
||||
class AIR_prec : public Solver
|
||||
{
|
||||
private:
|
||||
const HypreParMatrix *A;
|
||||
// Copy of A scaled by block-diagonal inverse
|
||||
HypreParMatrix A_s;
|
||||
|
||||
HypreBoomerAMG *AIR_solver;
|
||||
int blocksize;
|
||||
|
||||
public:
|
||||
AIR_prec(int blocksize_) : AIR_solver(NULL), blocksize(blocksize_) { }
|
||||
|
||||
void SetOperator(const Operator &op)
|
||||
{
|
||||
width = op.Width();
|
||||
height = op.Height();
|
||||
|
||||
A = dynamic_cast<const HypreParMatrix *>(&op);
|
||||
MFEM_VERIFY(A != NULL, "AIR_prec requires a HypreParMatrix.")
|
||||
|
||||
// Scale A by block-diagonal inverse
|
||||
BlockInverseScale(A, &A_s, NULL, NULL, blocksize,
|
||||
BlockInverseScaleJob::MATRIX_ONLY);
|
||||
delete AIR_solver;
|
||||
AIR_solver = new HypreBoomerAMG(A_s);
|
||||
AIR_solver->SetAdvectiveOptions(1, "", "FA");
|
||||
AIR_solver->SetPrintLevel(0);
|
||||
AIR_solver->SetMaxLevels(50);
|
||||
}
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Scale the rhs by block inverse and solve system
|
||||
HypreParVector z_s;
|
||||
BlockInverseScale(A, NULL, &x, &z_s, blocksize,
|
||||
BlockInverseScaleJob::RHS_ONLY);
|
||||
AIR_solver->Mult(z_s, y);
|
||||
}
|
||||
|
||||
~AIR_prec()
|
||||
{
|
||||
delete AIR_solver;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
class DG_Solver : public Solver
|
||||
{
|
||||
private:
|
||||
@@ -71,24 +131,37 @@ private:
|
||||
SparseMatrix M_diag;
|
||||
HypreParMatrix *A;
|
||||
GMRESSolver linear_solver;
|
||||
BlockILU prec;
|
||||
Solver *prec;
|
||||
double dt;
|
||||
public:
|
||||
DG_Solver(HypreParMatrix &M_, HypreParMatrix &K_, const FiniteElementSpace &fes)
|
||||
DG_Solver(HypreParMatrix &M_, HypreParMatrix &K_, const FiniteElementSpace &fes,
|
||||
PrecType prec_type)
|
||||
: M(M_),
|
||||
K(K_),
|
||||
A(NULL),
|
||||
linear_solver(M.GetComm()),
|
||||
prec(fes.GetFE(0)->GetDof(),
|
||||
BlockILU::Reordering::MINIMUM_DISCARDED_FILL),
|
||||
dt(-1.0)
|
||||
{
|
||||
int block_size = fes.GetFE(0)->GetDof();
|
||||
if (prec_type == PrecType::ILU)
|
||||
{
|
||||
prec = new BlockILU(block_size,
|
||||
BlockILU::Reordering::MINIMUM_DISCARDED_FILL);
|
||||
}
|
||||
else if (prec_type == PrecType::AIR)
|
||||
{
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
prec = new AIR_prec(block_size);
|
||||
#else
|
||||
MFEM_ABORT("Must have MFEM_HYPRE_VERSION >= 21800 to use AIR.\n");
|
||||
#endif
|
||||
}
|
||||
linear_solver.iterative_mode = false;
|
||||
linear_solver.SetRelTol(1e-9);
|
||||
linear_solver.SetAbsTol(0.0);
|
||||
linear_solver.SetMaxIter(100);
|
||||
linear_solver.SetPrintLevel(0);
|
||||
linear_solver.SetPreconditioner(prec);
|
||||
linear_solver.SetPreconditioner(*prec);
|
||||
|
||||
M.GetDiag(M_diag);
|
||||
}
|
||||
@@ -121,10 +194,12 @@ public:
|
||||
|
||||
~DG_Solver()
|
||||
{
|
||||
delete prec;
|
||||
delete A;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/** A time-dependent operator for the right-hand side of the ODE. The DG weak
|
||||
form of du/dt = -v.grad(u) is M du/dt = K u + b, where M and K are the mass
|
||||
and advection matrices, and b describes the flow on the boundary. This can
|
||||
@@ -142,7 +217,8 @@ private:
|
||||
mutable Vector z;
|
||||
|
||||
public:
|
||||
FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K, const Vector &_b);
|
||||
FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K, const Vector &_b,
|
||||
PrecType prec_type);
|
||||
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
virtual void ImplicitSolve(const double dt, const Vector &x, Vector &k);
|
||||
@@ -154,10 +230,9 @@ public:
|
||||
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);
|
||||
MPI_Session mpi;
|
||||
int num_procs = mpi.WorldSize();
|
||||
int myid = mpi.WorldRank();
|
||||
|
||||
// 2. Parse command-line options.
|
||||
problem = 0;
|
||||
@@ -178,7 +253,11 @@ int main(int argc, char *argv[])
|
||||
bool adios2 = false;
|
||||
bool binary = false;
|
||||
int vis_steps = 5;
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
PrecType prec_type = PrecType::AIR;
|
||||
#else
|
||||
PrecType prec_type = PrecType::ILU;
|
||||
#endif
|
||||
int precision = 8;
|
||||
cout.precision(precision);
|
||||
|
||||
@@ -212,6 +291,8 @@ int main(int argc, char *argv[])
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step.");
|
||||
args.AddOption((int *)&prec_type, "-pt", "--prec-type", "Preconditioner for "
|
||||
"implicit solves. 0 for ILU, 1 for pAIR-AMG.");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
@@ -232,20 +313,19 @@ int main(int argc, char *argv[])
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
if (myid == 0)
|
||||
if (mpi.Root())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
}
|
||||
MPI_Finalize();
|
||||
return 1;
|
||||
}
|
||||
if (myid == 0)
|
||||
if (mpi.Root())
|
||||
{
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
Device device(device_config);
|
||||
if (myid == 0) { device.Print(); }
|
||||
if (mpi.Root()) { device.Print(); }
|
||||
|
||||
// 3. Read the serial mesh from the given mesh file on all processors. We can
|
||||
// handle geometrically periodic meshes in this code.
|
||||
@@ -272,12 +352,11 @@ int main(int argc, char *argv[])
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
if (myid == 0)
|
||||
if (mpi.Root())
|
||||
{
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
}
|
||||
delete mesh;
|
||||
MPI_Finalize();
|
||||
return 3;
|
||||
}
|
||||
|
||||
@@ -311,7 +390,7 @@ int main(int argc, char *argv[])
|
||||
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, &fec);
|
||||
|
||||
HYPRE_Int global_vSize = fes->GlobalTrueVSize();
|
||||
if (myid == 0)
|
||||
if (mpi.Root())
|
||||
{
|
||||
cout << "Number of unknowns: " << global_vSize << endl;
|
||||
}
|
||||
@@ -342,15 +421,16 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
m->AddDomainIntegrator(new MassIntegrator);
|
||||
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
|
||||
constexpr double alpha = -1.0;
|
||||
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, alpha));
|
||||
k->AddInteriorFaceIntegrator(
|
||||
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
|
||||
new NonconservativeDGTraceIntegrator(velocity, alpha));
|
||||
k->AddBdrFaceIntegrator(
|
||||
new TransposeIntegrator(new DGTraceIntegrator(velocity, 1.0, -0.5)));
|
||||
new NonconservativeDGTraceIntegrator(velocity, alpha));
|
||||
|
||||
ParLinearForm *b = new ParLinearForm(fes);
|
||||
b->AddBdrFaceIntegrator(
|
||||
new BoundaryFlowIntegrator(inflow, velocity, -1.0, -0.5));
|
||||
new BoundaryFlowIntegrator(inflow, velocity, alpha));
|
||||
|
||||
int skip_zeros = 0;
|
||||
m->Assemble();
|
||||
@@ -451,11 +531,11 @@ int main(int argc, char *argv[])
|
||||
sout.open(vishost, visport);
|
||||
if (!sout)
|
||||
{
|
||||
if (myid == 0)
|
||||
if (mpi.Root())
|
||||
cout << "Unable to connect to GLVis server at "
|
||||
<< vishost << ':' << visport << endl;
|
||||
visualization = false;
|
||||
if (myid == 0)
|
||||
if (mpi.Root())
|
||||
{
|
||||
cout << "GLVis visualization disabled.\n";
|
||||
}
|
||||
@@ -467,7 +547,7 @@ int main(int argc, char *argv[])
|
||||
sout << "solution\n" << *pmesh << *u;
|
||||
sout << "pause\n";
|
||||
sout << flush;
|
||||
if (myid == 0)
|
||||
if (mpi.Root())
|
||||
cout << "GLVis visualization paused."
|
||||
<< " Press space (in the GLVis window) to resume it.\n";
|
||||
}
|
||||
@@ -476,7 +556,7 @@ int main(int argc, char *argv[])
|
||||
// 10. Define the time-dependent evolution operator describing the ODE
|
||||
// right-hand side, and perform time-integration (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
FE_Evolution adv(*m, *k, *B);
|
||||
FE_Evolution adv(*m, *k, *B, prec_type);
|
||||
|
||||
double t = 0.0;
|
||||
adv.SetTime(t);
|
||||
@@ -493,7 +573,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (done || ti % vis_steps == 0)
|
||||
{
|
||||
if (myid == 0)
|
||||
if (mpi.Root())
|
||||
{
|
||||
cout << "time step: " << ti << ", time: " << t << endl;
|
||||
}
|
||||
@@ -564,16 +644,14 @@ int main(int argc, char *argv[])
|
||||
#endif
|
||||
delete dc;
|
||||
|
||||
MPI_Finalize();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Implementation of class FE_Evolution
|
||||
FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
|
||||
const Vector &_b)
|
||||
: TimeDependentOperator(_M.Height()),
|
||||
b(_b),
|
||||
const Vector &_b, PrecType prec_type)
|
||||
: TimeDependentOperator(_M.Height()), b(_b),
|
||||
M_solver(_M.ParFESpace()->GetComm()),
|
||||
z(_M.Height())
|
||||
{
|
||||
@@ -598,7 +676,7 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
|
||||
HypreSmoother *hypre_prec = new HypreSmoother(M_mat, HypreSmoother::Jacobi);
|
||||
M_prec = hypre_prec;
|
||||
|
||||
dg_solver = new DG_Solver(M_mat, K_mat, *_M.FESpace());
|
||||
dg_solver = new DG_Solver(M_mat, K_mat, *_M.FESpace(), prec_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -614,6 +692,10 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
|
||||
M_solver.SetPrintLevel(0);
|
||||
}
|
||||
|
||||
// Solve the equation:
|
||||
// u_t = M^{-1}(Ku + b),
|
||||
// by solving associated linear system
|
||||
// (M - dt*K) d = K*u + b
|
||||
void FE_Evolution::ImplicitSolve(const double dt, const Vector &x, Vector &k)
|
||||
{
|
||||
K->Mult(x, z);
|
||||
|
||||
+7
-2
@@ -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
|
||||
@@ -65,6 +66,7 @@ set(SRCS
|
||||
tmop_tools.cpp
|
||||
gslib.cpp
|
||||
transfer.cpp
|
||||
prmnonlinearform.cpp
|
||||
)
|
||||
|
||||
set(HDRS
|
||||
@@ -111,6 +113,7 @@ set(HDRS
|
||||
tmop_tools.hpp
|
||||
gslib.hpp
|
||||
transfer.hpp
|
||||
prmnonlinearform.hpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_SIDRE)
|
||||
@@ -135,7 +138,8 @@ if (MFEM_USE_MPI)
|
||||
pgridfunc.cpp
|
||||
plinearform.cpp
|
||||
pnonlinearform.cpp
|
||||
prestriction.cpp)
|
||||
prestriction.cpp
|
||||
pprmnonlinearform.cpp)
|
||||
# If this list (HDRS -> HEADERS) is used for install, we probably want the
|
||||
# headers added all the time.
|
||||
list(APPEND HDRS
|
||||
@@ -144,7 +148,8 @@ if (MFEM_USE_MPI)
|
||||
pgridfunc.hpp
|
||||
plinearform.hpp
|
||||
pnonlinearform.hpp
|
||||
prestriction.hpp)
|
||||
prestriction.hpp
|
||||
pprmnonlinearform.hpp)
|
||||
endif()
|
||||
|
||||
convert_filenames_to_full_paths(SRCS)
|
||||
|
||||
+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;
|
||||
|
||||
+24
-7
@@ -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(); }
|
||||
@@ -847,9 +854,9 @@ public:
|
||||
|
||||
This returns the same operator as FormRectangularLinearSystem(), but does
|
||||
without the transformations of the right-hand side. */
|
||||
void FormRectangularSystemMatrix(const Array<int> &trial_tdof_list,
|
||||
const Array<int> &test_tdof_list,
|
||||
OperatorHandle &A);
|
||||
virtual void FormRectangularSystemMatrix(const Array<int> &trial_tdof_list,
|
||||
const Array<int> &test_tdof_list,
|
||||
OperatorHandle &A);
|
||||
|
||||
/** @brief Form the column-constrained linear system matrix A.
|
||||
See FormRectangularSystemMatrix() for details.
|
||||
@@ -876,10 +883,11 @@ public:
|
||||
Return in @a A a *reference* to the system matrix that is column-constrained.
|
||||
The reference will be invalidated when SetOperatorType(), Update(), or the
|
||||
destructor is called. */
|
||||
void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
|
||||
const Array<int> &test_tdof_list,
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A, Vector &X, Vector &B);
|
||||
virtual void FormRectangularLinearSystem(const Array<int> &trial_tdof_list,
|
||||
const Array<int> &test_tdof_list,
|
||||
Vector &x, Vector &b,
|
||||
OperatorHandle &A, Vector &X,
|
||||
Vector &B);
|
||||
|
||||
/** @brief Form the linear system A X = B, corresponding to this bilinear
|
||||
form and the linear form @a b(.).
|
||||
@@ -977,9 +985,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
|
||||
|
||||
+209
-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.");
|
||||
}
|
||||
|
||||
@@ -229,6 +229,159 @@ void SumIntegrator::AssembleElementMatrix(
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleElementMatrix2(
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
ElementTransformation &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_ASSERT(integrators.Size() > 0, "empty SumIntegrator.");
|
||||
|
||||
integrators[0]->AssembleElementMatrix2(el1, el2, Trans, elmat);
|
||||
for (int i = 1; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssembleElementMatrix2(el1, el2, Trans, elem_mat);
|
||||
elmat += elem_mat;
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &el1, const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_ASSERT(integrators.Size() > 0, "empty SumIntegrator.");
|
||||
|
||||
integrators[0]->AssembleFaceMatrix(el1, el2, Trans, elmat);
|
||||
for (int i = 1; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssembleFaceMatrix(el1, el2, Trans, elem_mat);
|
||||
elmat += elem_mat;
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleFaceMatrix(
|
||||
const FiniteElement &tr_fe,
|
||||
const FiniteElement &te_fe1, const FiniteElement &te_fe2,
|
||||
FaceElementTransformations &Trans, DenseMatrix &elmat)
|
||||
{
|
||||
MFEM_ASSERT(integrators.Size() > 0, "empty SumIntegrator.");
|
||||
|
||||
integrators[0]->AssembleFaceMatrix(tr_fe, te_fe1, te_fe2, Trans, elmat);
|
||||
for (int i = 1; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssembleFaceMatrix(tr_fe, te_fe1, te_fe2, Trans, elem_mat);
|
||||
elmat += elem_mat;
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssemblePA(const FiniteElementSpace& fes)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssemblePA(fes);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleDiagonalPA(Vector &diag)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssembleDiagonalPA(diag);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssemblePAInteriorFaces(const FiniteElementSpace &fes)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssemblePAInteriorFaces(fes);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssemblePABoundaryFaces(const FiniteElementSpace &fes)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssemblePABoundaryFaces(fes);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AddMultPA(const Vector& x, Vector& y) const
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AddMultPA(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AddMultTransposePA(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleMF(const FiniteElementSpace &fes)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssembleMF(fes);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AddMultMF(const Vector& x, Vector& y) const
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AddMultTransposeMF(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AddMultTransposeMF(const Vector &x, Vector &y) const
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AddMultMF(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleDiagonalMF(Vector &diag)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssembleDiagonalMF(diag);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssembleEA(fes, emat, add);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
const bool add)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssembleEAInteriorFaces(fes,ea_data_int,ea_data_ext,add);
|
||||
}
|
||||
}
|
||||
|
||||
void SumIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_bdr,
|
||||
const bool add)
|
||||
{
|
||||
for (int i = 0; i < integrators.Size(); i++)
|
||||
{
|
||||
integrators[i]->AssembleEABoundaryFaces(fes, ea_data_bdr, add);
|
||||
}
|
||||
}
|
||||
|
||||
SumIntegrator::~SumIntegrator()
|
||||
{
|
||||
if (own_integrators)
|
||||
@@ -1913,12 +2066,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 +2103,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 +2137,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 +2166,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 +2234,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 +2271,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 +3668,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,
|
||||
|
||||
+277
-45
@@ -355,7 +355,7 @@ class SumIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
private:
|
||||
int own_integrators;
|
||||
DenseMatrix elem_mat;
|
||||
mutable DenseMatrix elem_mat;
|
||||
Array<BilinearFormIntegrator*> integrators;
|
||||
|
||||
public:
|
||||
@@ -367,6 +367,55 @@ public:
|
||||
virtual void AssembleElementMatrix(const FiniteElement &el,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
virtual void AssembleElementMatrix2(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
using BilinearFormIntegrator::AssembleFaceMatrix;
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
const FiniteElement &el2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &trial_face_fe,
|
||||
const FiniteElement &test_fe1,
|
||||
const FiniteElement &test_fe2,
|
||||
FaceElementTransformations &Trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
virtual void AssemblePA(const FiniteElementSpace& fes);
|
||||
|
||||
virtual void AssembleDiagonalPA(Vector &diag);
|
||||
|
||||
virtual void AssemblePAInteriorFaces(const FiniteElementSpace &fes);
|
||||
|
||||
virtual void AssemblePABoundaryFaces(const FiniteElementSpace &fes);
|
||||
|
||||
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual void AddMultPA(const Vector& x, Vector& y) const;
|
||||
|
||||
virtual void AssembleMF(const FiniteElementSpace &fes);
|
||||
|
||||
virtual void AddMultMF(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual void AddMultTransposeMF(const Vector &x, Vector &y) const;
|
||||
|
||||
virtual void AssembleDiagonalMF(Vector &diag);
|
||||
|
||||
virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
|
||||
const bool add);
|
||||
|
||||
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_int,
|
||||
Vector &ea_data_ext,
|
||||
const bool add);
|
||||
|
||||
virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
|
||||
Vector &ea_data_bdr,
|
||||
const bool add);
|
||||
|
||||
virtual ~SumIntegrator();
|
||||
};
|
||||
@@ -465,8 +514,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 +552,7 @@ protected:
|
||||
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
VectorCoefficient *DQ;
|
||||
DiagonalMatrixCoefficient *DQ;
|
||||
MatrixCoefficient *MQ;
|
||||
|
||||
private:
|
||||
@@ -901,7 +950,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 +1068,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 +1156,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 +1700,7 @@ public:
|
||||
MixedVectorGradientIntegrator() {}
|
||||
MixedVectorGradientIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) {}
|
||||
MixedVectorGradientIntegrator(VectorCoefficient &dq)
|
||||
MixedVectorGradientIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) {}
|
||||
MixedVectorGradientIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) {}
|
||||
@@ -1705,7 +1754,7 @@ public:
|
||||
MixedVectorCurlIntegrator() {}
|
||||
MixedVectorCurlIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) {}
|
||||
MixedVectorCurlIntegrator(VectorCoefficient &dq)
|
||||
MixedVectorCurlIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) {}
|
||||
MixedVectorCurlIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) {}
|
||||
@@ -1760,7 +1809,7 @@ public:
|
||||
MixedVectorWeakCurlIntegrator() {}
|
||||
MixedVectorWeakCurlIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) {}
|
||||
MixedVectorWeakCurlIntegrator(VectorCoefficient &dq)
|
||||
MixedVectorWeakCurlIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) {}
|
||||
MixedVectorWeakCurlIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) {}
|
||||
@@ -1813,7 +1862,7 @@ public:
|
||||
MixedVectorWeakDivergenceIntegrator() {}
|
||||
MixedVectorWeakDivergenceIntegrator(Coefficient &q)
|
||||
: MixedVectorIntegrator(q) {}
|
||||
MixedVectorWeakDivergenceIntegrator(VectorCoefficient &dq)
|
||||
MixedVectorWeakDivergenceIntegrator(DiagonalMatrixCoefficient &dq)
|
||||
: MixedVectorIntegrator(dq, true) {}
|
||||
MixedVectorWeakDivergenceIntegrator(MatrixCoefficient &mq)
|
||||
: MixedVectorIntegrator(mq) {}
|
||||
@@ -1844,8 +1893,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 +1951,7 @@ protected:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
MatrixCoefficient *MQ;
|
||||
SymmetricMatrixCoefficient *SMQ;
|
||||
|
||||
private:
|
||||
Vector vec, pointflux, shape;
|
||||
@@ -1922,19 +1974,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()
|
||||
{
|
||||
@@ -1984,6 +2045,8 @@ public:
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
|
||||
virtual void AddMultTransposePA(const Vector&, Vector&) const;
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe);
|
||||
};
|
||||
@@ -2047,11 +2110,11 @@ public:
|
||||
|
||||
virtual void AddMultPA(const Vector&, Vector&) const;
|
||||
|
||||
virtual void AddMultTransposePA(const Vector&, Vector&) const;
|
||||
|
||||
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 */
|
||||
@@ -2110,6 +2173,17 @@ public:
|
||||
ElementTransformation &Trans);
|
||||
};
|
||||
|
||||
// Alias for @ConvectionIntegrator.
|
||||
using NonconservativeConvectionIntegrator = ConvectionIntegrator;
|
||||
|
||||
/// -alpha (u, q . grad v), negative transpose of ConvectionIntegrator
|
||||
class ConservativeConvectionIntegrator : public TransposeIntegrator
|
||||
{
|
||||
public:
|
||||
ConservativeConvectionIntegrator(VectorCoefficient &q, double a = 1.0)
|
||||
: TransposeIntegrator(new ConvectionIntegrator(q, -a)) { }
|
||||
};
|
||||
|
||||
/// alpha (q . grad u, v) using the "group" FE discretization
|
||||
class GroupConvectionIntegrator : public BilinearFormIntegrator
|
||||
{
|
||||
@@ -2335,8 +2409,9 @@ private:
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *DQ;
|
||||
DiagonalMatrixCoefficient *DQ;
|
||||
MatrixCoefficient *MQ;
|
||||
SymmetricMatrixCoefficient *SMQ;
|
||||
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
@@ -2347,14 +2422,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 +2490,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 +2505,9 @@ private:
|
||||
|
||||
protected:
|
||||
Coefficient *Q;
|
||||
VectorCoefficient *VQ;
|
||||
DiagonalMatrixCoefficient *DQ;
|
||||
MatrixCoefficient *MQ;
|
||||
SymmetricMatrixCoefficient *SMQ;
|
||||
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
@@ -2439,13 +2520,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 +2737,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, see also
|
||||
NonconservativeDGTraceIntegrator). 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 ConservativeConvectionIntegrator (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:
|
||||
@@ -2675,13 +2775,17 @@ private:
|
||||
Vector shape1, shape2;
|
||||
|
||||
public:
|
||||
/// Construct integrator with rho = 1.
|
||||
DGTraceIntegrator(VectorCoefficient &_u, double a, double b)
|
||||
{ rho = NULL; u = &_u; alpha = a; beta = b; }
|
||||
/// Construct integrator with rho = 1, b = 0.5*a.
|
||||
DGTraceIntegrator(VectorCoefficient &u_, double a)
|
||||
{ rho = NULL; u = &u_; alpha = a; beta = 0.5*a; }
|
||||
|
||||
DGTraceIntegrator(Coefficient &_rho, VectorCoefficient &_u,
|
||||
/// Construct integrator with rho = 1.
|
||||
DGTraceIntegrator(VectorCoefficient &u_, double a, double b)
|
||||
{ rho = NULL; u = &u_; alpha = a; beta = b; }
|
||||
|
||||
DGTraceIntegrator(Coefficient &_rho, VectorCoefficient &u_,
|
||||
double a, double b)
|
||||
{ rho = &_rho; u = &_u; alpha = a; beta = b; }
|
||||
{ rho = &_rho; u = &u_; alpha = a; beta = b; }
|
||||
|
||||
using BilinearFormIntegrator::AssembleFaceMatrix;
|
||||
virtual void AssembleFaceMatrix(const FiniteElement &el1,
|
||||
@@ -2715,6 +2819,30 @@ private:
|
||||
void SetupPA(const FiniteElementSpace &fes, FaceType type);
|
||||
};
|
||||
|
||||
// Alias for @a DGTraceIntegrator.
|
||||
using ConservativeDGTraceIntegrator = DGTraceIntegrator;
|
||||
|
||||
/** Integrator that represents the face terms used for the non-conservative
|
||||
DG discretization of the convection equation:
|
||||
-alpha < rho_u (u.n) {v},[w] > + beta < rho_u |u.n| [v],[w] >.
|
||||
|
||||
This integrator can be used with together with ConvectionIntegrator to
|
||||
implement an upwind DG discretization in non-conservative form, see ex9 and
|
||||
ex9p. */
|
||||
class NonconservativeDGTraceIntegrator : public TransposeIntegrator
|
||||
{
|
||||
public:
|
||||
NonconservativeDGTraceIntegrator(VectorCoefficient &u, double a)
|
||||
: TransposeIntegrator(new DGTraceIntegrator(u, -a, 0.5*a)) { }
|
||||
|
||||
NonconservativeDGTraceIntegrator(VectorCoefficient &u, double a, double b)
|
||||
: TransposeIntegrator(new DGTraceIntegrator(u, -a, b)) { }
|
||||
|
||||
NonconservativeDGTraceIntegrator(Coefficient &rho, VectorCoefficient &u,
|
||||
double a, double b)
|
||||
: TransposeIntegrator(new DGTraceIntegrator(rho, u, -a, b)) { }
|
||||
};
|
||||
|
||||
/** Integrator for the DG form:
|
||||
|
||||
- < {(Q grad(u)).n}, [v] > + sigma < [u], {(Q grad(v)).n} >
|
||||
@@ -2752,6 +2880,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 +3083,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 +3127,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 +3244,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1901,4 +1904,17 @@ void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
void DiffusionIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
|
||||
{
|
||||
if (symmetric)
|
||||
{
|
||||
AddMultPA(x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("DiffusionIntegrator::AddMultTransposePA only implemented in "
|
||||
"the symmetric case.")
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -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
@@ -1226,4 +1226,10 @@ void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
}
|
||||
}
|
||||
|
||||
void MassIntegrator::AddMultTransposePA(const Vector &x, Vector &y) const
|
||||
{
|
||||
// Mass integrator is symmetric
|
||||
AddMultPA(x, y);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -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
|
||||
|
||||
+204
-15
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7683,7 +7797,10 @@ NodalTensorFiniteElement::NodalTensorFiniteElement(const int dims,
|
||||
const DofMapType dmtype)
|
||||
: NodalFiniteElement(dims, GetTensorProductGeometry(dims), Pow(p + 1, dims),
|
||||
p, dims > 1 ? FunctionSpace::Qk : FunctionSpace::Pk),
|
||||
TensorBasisElement(dims, p, VerifyNodal(btype), dmtype) { }
|
||||
TensorBasisElement(dims, p, VerifyNodal(btype), dmtype)
|
||||
{
|
||||
lex_ordering = dof_map;
|
||||
}
|
||||
|
||||
|
||||
PositiveTensorFiniteElement::PositiveTensorFiniteElement(
|
||||
@@ -8367,23 +8484,33 @@ H1_TriangleElement::H1_TriangleElement(const int p, const int btype)
|
||||
Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1);
|
||||
#endif
|
||||
|
||||
int p2p3 = 2*p + 3;
|
||||
auto idx = [p2p3](int i, int j) { return ((p2p3-j)*j)/2+i; };
|
||||
lex_ordering.SetSize(dof);
|
||||
|
||||
// vertices
|
||||
lex_ordering[idx(0,0)] = 0;
|
||||
Nodes.IntPoint(0).Set2(cp[0], cp[0]);
|
||||
lex_ordering[idx(p,0)] = 1;
|
||||
Nodes.IntPoint(1).Set2(cp[p], cp[0]);
|
||||
lex_ordering[idx(0,p)] = 2;
|
||||
Nodes.IntPoint(2).Set2(cp[0], cp[p]);
|
||||
|
||||
// edges
|
||||
int o = 3;
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
lex_ordering[idx(i,0)] = o;
|
||||
Nodes.IntPoint(o++).Set2(cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
lex_ordering[idx(p-i,i)] = o;
|
||||
Nodes.IntPoint(o++).Set2(cp[p-i], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++)
|
||||
{
|
||||
lex_ordering[idx(0,p-i)] = o;
|
||||
Nodes.IntPoint(o++).Set2(cp[0], cp[p-i]);
|
||||
}
|
||||
|
||||
@@ -8392,6 +8519,7 @@ H1_TriangleElement::H1_TriangleElement(const int p, const int btype)
|
||||
for (int i = 1; i + j < p; i++)
|
||||
{
|
||||
const double w = cp[i] + cp[j] + cp[p-i-j];
|
||||
lex_ordering[idx(i,j)] = o;
|
||||
Nodes.IntPoint(o++).Set2(cp[i]/w, cp[j]/w);
|
||||
}
|
||||
|
||||
@@ -8525,36 +8653,56 @@ H1_TetrahedronElement::H1_TetrahedronElement(const int p, const int btype)
|
||||
Vector shape_x(p + 1), shape_y(p + 1), shape_z(p + 1), shape_l(p + 1);
|
||||
#endif
|
||||
|
||||
auto tri = [](int k) { return (k*(k + 1))/2; };
|
||||
auto tet = [](int k) { return (k*(k + 1)*(k + 2))/6; };
|
||||
int ndof = tet(p+1);
|
||||
auto idx = [tri, tet, p, ndof](int i, int j, int k)
|
||||
{
|
||||
return ndof - tet(p - k) - tri(p + 1 - k - j) + i;
|
||||
};
|
||||
|
||||
lex_ordering.SetSize(dof);
|
||||
|
||||
// vertices
|
||||
lex_ordering[idx(0,0,0)] = 0;
|
||||
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
|
||||
lex_ordering[idx(p,0,0)] = 1;
|
||||
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
|
||||
lex_ordering[idx(0,p,0)] = 2;
|
||||
Nodes.IntPoint(2).Set3(cp[0], cp[p], cp[0]);
|
||||
lex_ordering[idx(0,0,p)] = 3;
|
||||
Nodes.IntPoint(3).Set3(cp[0], cp[0], cp[p]);
|
||||
|
||||
// edges (see Tetrahedron::edges in mesh/tetrahedron.cpp)
|
||||
int o = 4;
|
||||
for (int i = 1; i < p; i++) // (0,1)
|
||||
{
|
||||
lex_ordering[idx(i,0,0)] = o;
|
||||
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,2)
|
||||
{
|
||||
lex_ordering[idx(0,i,0)] = o;
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (0,3)
|
||||
{
|
||||
lex_ordering[idx(0,0,i)] = o;
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,2)
|
||||
{
|
||||
lex_ordering[idx(p-i,i,0)] = o;
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i], cp[i], cp[0]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (1,3)
|
||||
{
|
||||
lex_ordering[idx(p-i,0,i)] = o;
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
|
||||
}
|
||||
for (int i = 1; i < p; i++) // (2,3)
|
||||
{
|
||||
lex_ordering[idx(0,p-i,i)] = o;
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
|
||||
}
|
||||
|
||||
@@ -8562,24 +8710,28 @@ H1_TetrahedronElement::H1_TetrahedronElement(const int p, const int btype)
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (1,2,3)
|
||||
{
|
||||
lex_ordering[idx(p-i-j,i,j)] = o;
|
||||
double w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[p-i-j]/w, cp[i]/w, cp[j]/w);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (0,3,2)
|
||||
{
|
||||
lex_ordering[idx(0,j,i)] = o;
|
||||
double w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[0], cp[j]/w, cp[i]/w);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (0,1,3)
|
||||
{
|
||||
lex_ordering[idx(i,0,j)] = o;
|
||||
double w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
|
||||
}
|
||||
for (int j = 1; j < p; j++)
|
||||
for (int i = 1; i + j < p; i++) // (0,2,1)
|
||||
{
|
||||
lex_ordering[idx(j,i,0)] = o;
|
||||
double w = cp[i] + cp[j] + cp[p-i-j];
|
||||
Nodes.IntPoint(o++).Set3(cp[j]/w, cp[i]/w, cp[0]);
|
||||
}
|
||||
@@ -8589,6 +8741,7 @@ H1_TetrahedronElement::H1_TetrahedronElement(const int p, const int btype)
|
||||
for (int j = 1; j + k < p; j++)
|
||||
for (int i = 1; i + j + k < p; i++)
|
||||
{
|
||||
lex_ordering[idx(i,j,k)] = o;
|
||||
double w = cp[i] + cp[j] + cp[k] + cp[p-i-j-k];
|
||||
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[j]/w, cp[k]/w);
|
||||
}
|
||||
@@ -9133,7 +9286,22 @@ H1_WedgeElement::H1_WedgeElement(const int p,
|
||||
t_dof.SetSize(dof);
|
||||
s_dof.SetSize(dof);
|
||||
|
||||
int p2p3 = 2*p + 3, ntri = ((p + 1)*(p + 2))/2;
|
||||
auto idx = [p2p3,ntri](int i, int j, int k)
|
||||
{
|
||||
return k*ntri + ((p2p3-j)*j)/2+i;
|
||||
};
|
||||
|
||||
lex_ordering.SetSize(dof);
|
||||
int o = 0;
|
||||
|
||||
// Nodal DoFs
|
||||
lex_ordering[idx(0,0,0)] = o++;
|
||||
lex_ordering[idx(p,0,0)] = o++;
|
||||
lex_ordering[idx(0,p,0)] = o++;
|
||||
lex_ordering[idx(0,0,p)] = o++;
|
||||
lex_ordering[idx(p,0,p)] = o++;
|
||||
lex_ordering[idx(0,p,p)] = o++;
|
||||
t_dof[0] = 0; s_dof[0] = 0;
|
||||
t_dof[1] = 1; s_dof[1] = 0;
|
||||
t_dof[2] = 2; s_dof[2] = 0;
|
||||
@@ -9142,9 +9310,19 @@ H1_WedgeElement::H1_WedgeElement(const int p,
|
||||
t_dof[5] = 2; s_dof[5] = 1;
|
||||
|
||||
// Edge DoFs
|
||||
int k = 0;
|
||||
int ne = p-1;
|
||||
for (int i=1; i<p; i++)
|
||||
{
|
||||
lex_ordering[idx(i,0,0)] = o + 0*ne + k;
|
||||
lex_ordering[idx(p-i,i,0)] = o + 1*ne + k;
|
||||
lex_ordering[idx(0,p-i,0)] = o + 2*ne + k;
|
||||
lex_ordering[idx(i,0,p)] = o + 3*ne + k;
|
||||
lex_ordering[idx(p-i,i,p)] = o + 4*ne + k;
|
||||
lex_ordering[idx(0,p-i,p)] = o + 5*ne + k;
|
||||
lex_ordering[idx(0,0,i)] = o + 6*ne + k;
|
||||
lex_ordering[idx(p,0,i)] = o + 7*ne + k;
|
||||
lex_ordering[idx(0,p,i)] = o + 8*ne + k;
|
||||
t_dof[5 + 0 * ne + i] = 2 + 0 * ne + i; s_dof[5 + 0 * ne + i] = 0;
|
||||
t_dof[5 + 1 * ne + i] = 2 + 1 * ne + i; s_dof[5 + 1 * ne + i] = 0;
|
||||
t_dof[5 + 2 * ne + i] = 2 + 2 * ne + i; s_dof[5 + 2 * ne + i] = 0;
|
||||
@@ -9154,21 +9332,26 @@ H1_WedgeElement::H1_WedgeElement(const int p,
|
||||
t_dof[5 + 6 * ne + i] = 0; s_dof[5 + 6 * ne + i] = i + 1;
|
||||
t_dof[5 + 7 * ne + i] = 1; s_dof[5 + 7 * ne + i] = i + 1;
|
||||
t_dof[5 + 8 * ne + i] = 2; s_dof[5 + 8 * ne + i] = i + 1;
|
||||
++k;
|
||||
}
|
||||
o += 9*ne;
|
||||
|
||||
// Triangular Face DoFs
|
||||
int k=0;
|
||||
k=0;
|
||||
int nt = (p-1)*(p-2)/2;
|
||||
for (int j=1; j<p; j++)
|
||||
{
|
||||
for (int i=1; i<p-j; i++)
|
||||
{
|
||||
int l = j - p + (((2 * p - 1) - i) * i) / 2;
|
||||
lex_ordering[idx(i,j,0)] = o+l;
|
||||
lex_ordering[idx(i,j,p)] = o+nt+k;
|
||||
t_dof[6 + 9 * ne + k] = 3 * p + l; s_dof[6 + 9 * ne + k] = 0;
|
||||
t_dof[6 + 9 * ne + nt + k] = 3 * p + k; s_dof[6 + 9 * ne + nt + k] = 1;
|
||||
k++;
|
||||
}
|
||||
}
|
||||
o += 2*nt;
|
||||
|
||||
// Quadrilateral Face DoFs
|
||||
k=0;
|
||||
@@ -9177,6 +9360,10 @@ H1_WedgeElement::H1_WedgeElement(const int p,
|
||||
{
|
||||
for (int i=1; i<p; i++)
|
||||
{
|
||||
lex_ordering[idx(i,0,j)] = o+k;
|
||||
lex_ordering[idx(p-i,i,j)] = o+nq+k;
|
||||
lex_ordering[idx(0,p-i,j)] = o+2*nq+k;
|
||||
|
||||
t_dof[6 + 9 * ne + 2 * nt + 0 * nq + k] = 2 + 0 * ne + i;
|
||||
t_dof[6 + 9 * ne + 2 * nt + 1 * nq + k] = 2 + 1 * ne + i;
|
||||
t_dof[6 + 9 * ne + 2 * nt + 2 * nq + k] = 2 + 2 * ne + i;
|
||||
@@ -9188,6 +9375,7 @@ H1_WedgeElement::H1_WedgeElement(const int p,
|
||||
k++;
|
||||
}
|
||||
}
|
||||
o += 3*nq;
|
||||
|
||||
// Interior DoFs
|
||||
int m=0;
|
||||
@@ -9196,8 +9384,9 @@ H1_WedgeElement::H1_WedgeElement(const int p,
|
||||
int l=0;
|
||||
for (int j=1; j<p; j++)
|
||||
{
|
||||
for (int i=1; i<j; i++)
|
||||
for (int i=1; i+j<p; i++)
|
||||
{
|
||||
lex_ordering[idx(i,j,k)] = o++;
|
||||
t_dof[6 + 9 * ne + 2 * nt + 3 * nq + m] = 3 * p + l;
|
||||
s_dof[6 + 9 * ne + 2 * nt + 3 * nq + m] = 1 + k;
|
||||
l++; m++;
|
||||
|
||||
+98
-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;
|
||||
};
|
||||
@@ -683,6 +698,7 @@ public:
|
||||
class NodalFiniteElement : public ScalarFiniteElement
|
||||
{
|
||||
protected:
|
||||
Array<int> lex_ordering;
|
||||
void ProjectCurl_2D(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &curl) const;
|
||||
@@ -731,6 +747,29 @@ public:
|
||||
virtual void ProjectDiv(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &div) const;
|
||||
|
||||
/** @brief Get an Array<int> that maps lexicographically ordered indices to
|
||||
the indices of the respective nodes/dofs/basis functions. Lexicographic
|
||||
ordering of nodes is defined in terms of reference-space coordinates
|
||||
(x,y,z). Lexicographically ordered nodes are listed first in order of
|
||||
increasing x-coordinate, and then in order of increasing y-coordinate,
|
||||
and finally in order of increasing z-coordinate.
|
||||
|
||||
For example, the six nodes of a quadratic triangle are lexicographically
|
||||
ordered as follows:
|
||||
|
||||
5
|
||||
|\
|
||||
3 4
|
||||
| \
|
||||
0-1-2
|
||||
|
||||
The resulting array may be empty if the DOFs are already ordered
|
||||
lexicographically, or if the finite element does not support creating
|
||||
this permutation. The array returned is the same as the array given by
|
||||
TensorBasisElement::GetDofMap, but it is also available for non-tensor
|
||||
elements. */
|
||||
const Array<int> &GetLexicographicOrdering() const { return lex_ordering; }
|
||||
};
|
||||
|
||||
/** @brief Class for finite elements utilizing the
|
||||
@@ -754,6 +793,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 +843,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 +871,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 +903,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 +931,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;
|
||||
|
||||
+9
-12
@@ -1854,19 +1854,16 @@ const int *H1_FECollection::GetDofMap(Geometry::Type GeomType) const
|
||||
{
|
||||
const int *dof_map = NULL;
|
||||
const FiniteElement *fe = H1_Elements[GeomType];
|
||||
switch (GeomType)
|
||||
const NodalFiniteElement *nodal_fe =
|
||||
dynamic_cast<const NodalFiniteElement*>(fe);
|
||||
if (nodal_fe)
|
||||
{
|
||||
case Geometry::SEGMENT:
|
||||
case Geometry::SQUARE:
|
||||
case Geometry::CUBE:
|
||||
dof_map = dynamic_cast<const TensorBasisElement *>(fe)
|
||||
->GetDofMap().GetData();
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Geometry type " << Geometry::Name[GeomType] << " is not "
|
||||
"implemented");
|
||||
// The "Cartesian" ordering for other geometries is defined by the
|
||||
// class GeometryRefiner.
|
||||
dof_map = nodal_fe->GetLexicographicOrdering().GetData();
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Geometry type " << Geometry::Name[GeomType] << " is not "
|
||||
"implemented");
|
||||
}
|
||||
return dof_map;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "transfer.hpp"
|
||||
#include "fespacehierarchy.hpp"
|
||||
#include "multigrid.hpp"
|
||||
#include "prmnonlinearform.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pfespace.hpp"
|
||||
@@ -48,6 +49,7 @@
|
||||
#include "plinearform.hpp"
|
||||
#include "pbilinearform.hpp"
|
||||
#include "pnonlinearform.hpp"
|
||||
#include "pprmnonlinearform.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_SIDRE
|
||||
|
||||
+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; }
|
||||
|
||||
|
||||
+22
-24
@@ -1165,24 +1165,31 @@ RefinedGeometry * GeometryRefiner::Refine(Geometry::Type Geom,
|
||||
Array<int> vi((n+1)*(n+1)*(n+1));
|
||||
vi = -1;
|
||||
m = 0;
|
||||
for (k = 0; k <= n; k++)
|
||||
for (j = 0; j <= k; j++)
|
||||
for (i = 0; i <= j; i++)
|
||||
|
||||
// vertices are given in lexicographic ordering on the reference
|
||||
// element
|
||||
for (int kk = 0; kk <= n; kk++)
|
||||
for (int jj = 0; jj <= n-kk; jj++)
|
||||
for (int ii = 0; ii <= n-jj-kk; ii++)
|
||||
{
|
||||
IntegrationPoint &ip = RG->RefPts.IntPoint(m);
|
||||
// map the coordinates to the reference tetrahedron
|
||||
// (0,0,0) -> (0,0,0)
|
||||
// (0,0,1) -> (1,0,0)
|
||||
// (1,1,1) -> (0,1,0)
|
||||
// (0,1,1) -> (0,0,1)
|
||||
double w = cp[k-j] + cp[i] + cp[j-i] + cp[Times-k];
|
||||
ip.x = cp[k-j]/w;
|
||||
ip.y = cp[i]/w;
|
||||
ip.z = cp[j-i]/w;
|
||||
double w = cp[ii] + cp[jj] + cp[kk] + cp[Times-ii-jj-kk];
|
||||
ip.x = cp[ii]/w;
|
||||
ip.y = cp[jj]/w;
|
||||
ip.z = cp[kk]/w;
|
||||
// (ii,jj,kk) are coordinates in the reference tetrahedron,
|
||||
// transform to coordinates (i,j,k) in the auxiliary
|
||||
// tetrahedron defined by (0,0,0), (0,0,1), (1,1,1), (0,1,1)
|
||||
int i = jj;
|
||||
int j = jj+kk;
|
||||
int k = ii+jj+kk;
|
||||
l = i + (j + k * (n+1)) * (n+1);
|
||||
// map from linear Cartesian hex index in the auxiliary tet
|
||||
// to lexicographic in the reference tet
|
||||
vi[l] = m;
|
||||
m++;
|
||||
}
|
||||
|
||||
if (m != (n+3)*(n+2)*(n+1)/6)
|
||||
{
|
||||
mfem_error("GeometryRefiner::Refine() for TETRAHEDRON #1");
|
||||
@@ -1269,18 +1276,9 @@ RefinedGeometry * GeometryRefiner::Refine(Geometry::Type Geom,
|
||||
for (i = 0; i <= n-j; i++, l++)
|
||||
{
|
||||
IntegrationPoint &ip = RG->RefPts.IntPoint(l);
|
||||
if (type == 0)
|
||||
{
|
||||
ip.x = double(i) / n;
|
||||
ip.y = double(j) / n;
|
||||
ip.z = double(k) / n;
|
||||
}
|
||||
else
|
||||
{
|
||||
ip.x = cp[i]/(cp[i] + cp[j] + cp[n-i-j]);
|
||||
ip.y = cp[j]/(cp[i] + cp[j] + cp[n-i-j]);
|
||||
ip.z = cp[k];
|
||||
}
|
||||
ip.x = cp[i]/(cp[i] + cp[j] + cp[n-i-j]);
|
||||
ip.y = cp[j]/(cp[i] + cp[j] + cp[n-i-j]);
|
||||
ip.z = cp[k];
|
||||
m++;
|
||||
}
|
||||
if (m != (n+1)*(n+1)*(n+2)/2)
|
||||
|
||||
+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
|
||||
{
|
||||
|
||||
@@ -406,6 +406,10 @@ private:
|
||||
Vector shape;
|
||||
|
||||
public:
|
||||
BoundaryFlowIntegrator(Coefficient &_f, VectorCoefficient &_u,
|
||||
double a)
|
||||
{ f = &_f; u = &_u; alpha = a; beta = 0.5*a; }
|
||||
|
||||
BoundaryFlowIntegrator(Coefficient &_f, VectorCoefficient &_u,
|
||||
double a, double b)
|
||||
{ f = &_f; u = &_u; alpha = a; beta = b; }
|
||||
|
||||
+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
|
||||
|
||||
@@ -128,6 +128,92 @@ double BlockNonlinearFormIntegrator::GetElementEnergy(
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double PrmBlockNonlinearFormIntegrator::GetElementEnergy(const Array<const FiniteElement *> &el,
|
||||
const Array<const FiniteElement *> &pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &pelfun)
|
||||
{
|
||||
mfem_error("PrmBlockNonlinearFormIntegrator::GetElementEnergy"
|
||||
" is not overloaded!");
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void PrmBlockNonlinearFormIntegrator::AssembleFaceGrad(const Array<const FiniteElement *> &el1,
|
||||
const Array<const FiniteElement *> &el2,
|
||||
const Array<const FiniteElement *> &pel1,
|
||||
const Array<const FiniteElement *> &pel2,
|
||||
FaceElementTransformations &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &pelfun,
|
||||
const Array2D<DenseMatrix *> &elmats)
|
||||
{
|
||||
mfem_error("PrmBlockNonlinearFormIntegrator::AssembleFaceGrad"
|
||||
" is not overloaded!");
|
||||
}
|
||||
|
||||
void PrmBlockNonlinearFormIntegrator::AssembleElementGrad(const Array<const FiniteElement *> &el,
|
||||
const Array<const FiniteElement *> &pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &pelfun,
|
||||
const Array2D<DenseMatrix *> &elmats)
|
||||
{
|
||||
mfem_error("PrmBlockNonlinearFormIntegrator::AssembleElementGrad"
|
||||
" is not overloaded!");
|
||||
}
|
||||
|
||||
void PrmBlockNonlinearFormIntegrator::AssembleElementVector(const Array<const FiniteElement *> &el,
|
||||
const Array<const FiniteElement *> &pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &pelfun,
|
||||
const Array<Vector *> &elvec)
|
||||
{
|
||||
mfem_error("PrmBlockNonlinearFormIntegrator::AssembleElementVector"
|
||||
" is not overloaded!");
|
||||
}
|
||||
|
||||
void PrmBlockNonlinearFormIntegrator::AssembleFaceVector(const Array<const FiniteElement *> &el1,
|
||||
const Array<const FiniteElement *> &el2,
|
||||
const Array<const FiniteElement *> &pel1,
|
||||
const Array<const FiniteElement *> &pel2,
|
||||
FaceElementTransformations &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &pelfun,
|
||||
const Array<Vector *> &elvect)
|
||||
{
|
||||
mfem_error("PrmBlockNonlinearFormIntegrator::AssembleFaceVector"
|
||||
" is not overloaded!");
|
||||
}
|
||||
|
||||
|
||||
void PrmBlockNonlinearFormIntegrator::AssemblePrmElementVector(const Array<const FiniteElement *> &el,
|
||||
const Array<const FiniteElement *> &pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &alfun,
|
||||
const Array<const Vector *> &pelfun,
|
||||
const Array<Vector *> &elvec)
|
||||
{
|
||||
mfem_error("PrmBlockNonlinearFormIntegrator::AssemblePrmElementVector"
|
||||
" is not overloaded!");
|
||||
}
|
||||
|
||||
void PrmBlockNonlinearFormIntegrator::AssemblePrmFaceVector(const Array<const FiniteElement *> &el1,
|
||||
const Array<const FiniteElement *> &el2,
|
||||
const Array<const FiniteElement *> &pel1,
|
||||
const Array<const FiniteElement *> &pel2,
|
||||
FaceElementTransformations &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &alfun,
|
||||
const Array<const Vector *> &pelfun,
|
||||
const Array<Vector *> &elvect)
|
||||
{
|
||||
mfem_error("PrmBlockNonlinearFormIntegrator::AssemblePrmFaceVector"
|
||||
" is not overloaded!");
|
||||
}
|
||||
|
||||
|
||||
double InverseHarmonicModel::EvalW(const DenseMatrix &J) const
|
||||
{
|
||||
|
||||
@@ -130,6 +130,80 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/** The abstract base class PrmBlockNonlinearFormIntegrator is
|
||||
a generalization of the BlockNonlinearFormIntegrator class suitable
|
||||
for block state and parameter vectors. */
|
||||
class PrmBlockNonlinearFormIntegrator
|
||||
{
|
||||
public:
|
||||
/// Compute the local energy
|
||||
virtual double GetElementEnergy(const Array<const FiniteElement *>&el,
|
||||
const Array<const FiniteElement *>&pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *>&elfun,
|
||||
const Array<const Vector *>&pelfun);
|
||||
|
||||
/// Perform the local action of the BlockNonlinearFormIntegrator
|
||||
virtual void AssembleElementVector(const Array<const FiniteElement *> &el,
|
||||
const Array<const FiniteElement *>&pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array<Vector *> &elvec);
|
||||
|
||||
virtual void AssembleFaceVector(const Array<const FiniteElement *> &el1,
|
||||
const Array<const FiniteElement *> &el2,
|
||||
const Array<const FiniteElement *> &pel1,
|
||||
const Array<const FiniteElement *> &pel2,
|
||||
FaceElementTransformations &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array<Vector *> &elvect);
|
||||
|
||||
/// Perform the local action on the parameters of the BlockNonlinearFormIntegrator
|
||||
virtual void AssemblePrmElementVector(const Array<const FiniteElement *> &el,
|
||||
const Array<const FiniteElement *>&pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &alfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array<Vector *> &elvec);
|
||||
|
||||
virtual void AssemblePrmFaceVector(const Array<const FiniteElement *> &el1,
|
||||
const Array<const FiniteElement *> &el2,
|
||||
const Array<const FiniteElement *> &pel1,
|
||||
const Array<const FiniteElement *> &pel2,
|
||||
FaceElementTransformations &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *> &alfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array<Vector *> &elvect);
|
||||
|
||||
|
||||
|
||||
/// Assemble the local gradient matrix
|
||||
virtual void AssembleElementGrad(const Array<const FiniteElement*> &el,
|
||||
const Array<const FiniteElement *>&pel,
|
||||
ElementTransformation &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array2D<DenseMatrix *> &elmats);
|
||||
|
||||
virtual void AssembleFaceGrad(const Array<const FiniteElement *>&el1,
|
||||
const Array<const FiniteElement *>&el2,
|
||||
const Array<const FiniteElement *> &pel1,
|
||||
const Array<const FiniteElement *> &pel2,
|
||||
FaceElementTransformations &Tr,
|
||||
const Array<const Vector *> &elfun,
|
||||
const Array<const Vector *>&pelfun,
|
||||
const Array2D<DenseMatrix *> &elmats);
|
||||
|
||||
|
||||
virtual ~PrmBlockNonlinearFormIntegrator() { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
/// Abstract class for hyperelastic models
|
||||
class HyperelasticModel
|
||||
{
|
||||
|
||||
@@ -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(); }
|
||||
@@ -246,6 +249,9 @@ public:
|
||||
@a A. */
|
||||
void ParallelAssemble(OperatorHandle &A);
|
||||
|
||||
using MixedBilinearForm::FormRectangularSystemMatrix;
|
||||
using MixedBilinearForm::FormRectangularLinearSystem;
|
||||
|
||||
/** @brief Return in @a A a parallel (on truedofs) version of this operator.
|
||||
|
||||
This returns the same operator as FormRectangularLinearSystem(), but does
|
||||
@@ -301,10 +307,20 @@ 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;
|
||||
|
||||
using MixedBilinearForm::FormRectangularSystemMatrix;
|
||||
|
||||
/** @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;
|
||||
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
// 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 "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "fem.hpp"
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
ParPrmBlockNonlinearForm::ParPrmBlockNonlinearForm(Array<ParFiniteElementSpace *> &pf,
|
||||
Array<ParFiniteElementSpace *> &ppf)
|
||||
:PrmBlockNonlinearForm()
|
||||
{
|
||||
pBlockGrad = nullptr;
|
||||
SetParSpaces(pf,ppf);
|
||||
}
|
||||
|
||||
void ParPrmBlockNonlinearForm::SetParSpaces(Array<ParFiniteElementSpace *> &pf,
|
||||
Array<ParFiniteElementSpace *> &pprmf)
|
||||
{
|
||||
delete pBlockGrad;
|
||||
pBlockGrad = nullptr;
|
||||
|
||||
for (int s1=0; s1<fes.Size(); ++s1)
|
||||
{
|
||||
for (int s2=0; s2<fes.Size(); ++s2)
|
||||
{
|
||||
delete phBlockGrad(s1,s2);
|
||||
}
|
||||
}
|
||||
|
||||
Array<FiniteElementSpace *> serialSpaces(pf.Size());
|
||||
Array<FiniteElementSpace *> prmserialSpaces(pprmf.Size());
|
||||
for (int s=0; s<pf.Size(); s++)
|
||||
{
|
||||
serialSpaces[s] = (FiniteElementSpace *) pf[s];
|
||||
}
|
||||
for (int s=0; s<pprmf.Size(); s++)
|
||||
{
|
||||
prmserialSpaces[s] = (FiniteElementSpace *) pprmf[s];
|
||||
}
|
||||
|
||||
SetSpaces(serialSpaces,prmserialSpaces);
|
||||
|
||||
phBlockGrad.SetSize(fes.Size(), fes.Size());
|
||||
|
||||
for (int s1=0; s1<fes.Size(); ++s1)
|
||||
{
|
||||
for (int s2=0; s2<fes.Size(); ++s2)
|
||||
{
|
||||
phBlockGrad(s1,s2) = new OperatorHandle(Operator::Hypre_ParCSR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ParFiniteElementSpace * ParPrmBlockNonlinearForm::ParFESpace(int k)
|
||||
{
|
||||
return (ParFiniteElementSpace *)fes[k];
|
||||
}
|
||||
|
||||
const ParFiniteElementSpace *ParPrmBlockNonlinearForm::ParFESpace(int k) const
|
||||
{
|
||||
return (const ParFiniteElementSpace *)fes[k];
|
||||
}
|
||||
|
||||
|
||||
ParFiniteElementSpace * ParPrmBlockNonlinearForm::ParPrmFESpace(int k)
|
||||
{
|
||||
return (ParFiniteElementSpace *)prmfes[k];
|
||||
}
|
||||
|
||||
const ParFiniteElementSpace *ParPrmBlockNonlinearForm::ParPrmFESpace(int k) const
|
||||
{
|
||||
return (const ParFiniteElementSpace *)prmfes[k];
|
||||
}
|
||||
|
||||
// Here, rhs is a true dof vector
|
||||
void ParPrmBlockNonlinearForm::SetEssentialBC(const
|
||||
Array<Array<int> *>&bdr_attr_is_ess,
|
||||
Array<Vector *> &rhs)
|
||||
{
|
||||
Array<Vector *> nullarray(fes.Size());
|
||||
nullarray = NULL;
|
||||
|
||||
PrmBlockNonlinearForm::SetEssentialBC(bdr_attr_is_ess, nullarray);
|
||||
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
if (rhs[s])
|
||||
{
|
||||
rhs[s]->SetSubVector(*ess_tdofs[s], 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParPrmBlockNonlinearForm::SetPrmEssentialBC(const
|
||||
Array<Array<int> *>&bdr_attr_is_ess,
|
||||
Array<Vector *> &rhs)
|
||||
{
|
||||
Array<Vector *> nullarray(fes.Size());
|
||||
nullarray = NULL;
|
||||
|
||||
PrmBlockNonlinearForm::SetPrmEssentialBC(bdr_attr_is_ess, nullarray);
|
||||
|
||||
for (int s = 0; s < prmfes.Size(); ++s)
|
||||
{
|
||||
if (rhs[s])
|
||||
{
|
||||
rhs[s]->SetSubVector(*prmess_tdofs[s], 0.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double ParPrmBlockNonlinearForm::GetEnergy(const Vector &x) const
|
||||
{
|
||||
xs_true.Update(x.GetData(), block_trueOffsets);
|
||||
xs.Update(block_offsets);
|
||||
|
||||
for (int s = 0; s < fes.Size(); ++s)
|
||||
{
|
||||
fes[s]->GetProlongationMatrix()->Mult(xs_true.GetBlock(s), xs.GetBlock(s));
|
||||
}
|
||||
|
||||
double enloc = PrmBlockNonlinearForm::GetEnergyBlocked(xs,xdv);
|
||||
double englo = 0.0;
|
||||
|
||||
MPI_Allreduce(&enloc, &englo, 1, MPI_DOUBLE, MPI_SUM,
|
||||
ParFESpace(0)->GetComm());
|
||||
|
||||
return englo;
|
||||
}
|
||||
|
||||
void ParPrmBlockNonlinearForm::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
xs_true.Update(x.GetData(), block_trueOffsets);
|
||||
ys_true.Update(y.GetData(), block_trueOffsets);
|
||||
xs.Update(block_offsets);
|
||||
ys.Update(block_offsets);
|
||||
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
fes[s]->GetProlongationMatrix()->Mult(
|
||||
xs_true.GetBlock(s), xs.GetBlock(s));
|
||||
}
|
||||
|
||||
PrmBlockNonlinearForm::MultBlocked(xs, xdv, ys);
|
||||
|
||||
if (fnfi.Size() > 0)
|
||||
{
|
||||
MFEM_ABORT("TODO: assemble contributions from shared face terms");
|
||||
}
|
||||
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
fes[s]->GetProlongationMatrix()->MultTranspose(
|
||||
ys.GetBlock(s), ys_true.GetBlock(s));
|
||||
|
||||
ys_true.GetBlock(s).SetSubVector(*ess_tdofs[s], 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Block T-Vector to Block T-Vector
|
||||
void ParPrmBlockNonlinearForm::PrmMult(const Vector &x, Vector &y) const
|
||||
{
|
||||
xs_true.Update(x.GetData(), prmblock_trueOffsets);
|
||||
ys_true.Update(y.GetData(), prmblock_trueOffsets);
|
||||
prmxs.Update(prmblock_offsets);
|
||||
prmys.Update(prmblock_offsets);
|
||||
|
||||
for (int s=0; s<prmfes.Size(); ++s)
|
||||
{
|
||||
prmfes[s]->GetProlongationMatrix()->Mult(
|
||||
xs_true.GetBlock(s), prmxs.GetBlock(s));
|
||||
}
|
||||
|
||||
PrmBlockNonlinearForm::MultPrmBlocked(xsv,adv,xdv,prmys);
|
||||
|
||||
if (fnfi.Size() > 0)
|
||||
{
|
||||
MFEM_ABORT("TODO: assemble contributions from shared face terms");
|
||||
}
|
||||
|
||||
for (int s=0; s<prmfes.Size(); ++s)
|
||||
{
|
||||
prmfes[s]->GetProlongationMatrix()->MultTranspose(
|
||||
prmys.GetBlock(s), ys_true.GetBlock(s));
|
||||
|
||||
ys_true.GetBlock(s).SetSubVector(*prmess_tdofs[s], 0.0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Return the local gradient matrix for the given true-dof vector x
|
||||
const BlockOperator & ParPrmBlockNonlinearForm::GetLocalGradient(
|
||||
const Vector &x) const
|
||||
{
|
||||
xs_true.Update(x.GetData(), block_trueOffsets);
|
||||
xs.Update(block_offsets);
|
||||
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
fes[s]->GetProlongationMatrix()->Mult(
|
||||
xs_true.GetBlock(s), xs.GetBlock(s));
|
||||
}
|
||||
|
||||
PrmBlockNonlinearForm::ComputeGradientBlocked(xs,xdv); // (re)assemble Grad with b.c.
|
||||
|
||||
delete BlockGrad;
|
||||
BlockGrad = new BlockOperator(block_offsets);
|
||||
|
||||
for (int i = 0; i < fes.Size(); ++i)
|
||||
{
|
||||
for (int j = 0; j < fes.Size(); ++j)
|
||||
{
|
||||
BlockGrad->SetBlock(i, j, Grads(i, j));
|
||||
}
|
||||
}
|
||||
return *BlockGrad;
|
||||
}
|
||||
|
||||
// Set the operator type id for the parallel gradient matrix/operator.
|
||||
void ParPrmBlockNonlinearForm::SetGradientType(Operator::Type tid)
|
||||
{
|
||||
for (int s1=0; s1<fes.Size(); ++s1)
|
||||
{
|
||||
for (int s2=0; s2<fes.Size(); ++s2)
|
||||
{
|
||||
phBlockGrad(s1,s2)->SetType(tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlockOperator & ParPrmBlockNonlinearForm::GetGradient(const Vector &x) const
|
||||
{
|
||||
if (pBlockGrad == NULL)
|
||||
{
|
||||
pBlockGrad = new BlockOperator(block_trueOffsets);
|
||||
}
|
||||
|
||||
Array<const ParFiniteElementSpace *> pfes(fes.Size());
|
||||
|
||||
for (int s1=0; s1<fes.Size(); ++s1)
|
||||
{
|
||||
pfes[s1] = ParFESpace(s1);
|
||||
|
||||
for (int s2=0; s2<fes.Size(); ++s2)
|
||||
{
|
||||
phBlockGrad(s1,s2)->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
GetLocalGradient(x); // gradients are stored in 'Grads'
|
||||
|
||||
if (fnfi.Size() > 0)
|
||||
{
|
||||
MFEM_ABORT("TODO: assemble contributions from shared face terms");
|
||||
}
|
||||
|
||||
for (int s1=0; s1<fes.Size(); ++s1)
|
||||
{
|
||||
for (int s2=0; s2<fes.Size(); ++s2)
|
||||
{
|
||||
OperatorHandle dA(phBlockGrad(s1,s2)->Type()),
|
||||
Ph(phBlockGrad(s1,s2)->Type()),
|
||||
Rh(phBlockGrad(s1,s2)->Type());
|
||||
|
||||
if (s1 == s2)
|
||||
{
|
||||
dA.MakeSquareBlockDiag(pfes[s1]->GetComm(), pfes[s1]->GlobalVSize(),
|
||||
pfes[s1]->GetDofOffsets(), Grads(s1,s1));
|
||||
Ph.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
|
||||
phBlockGrad(s1,s1)->MakePtAP(dA, Ph);
|
||||
|
||||
OperatorHandle Ae;
|
||||
Ae.EliminateRowsCols(*phBlockGrad(s1,s1), *ess_tdofs[s1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
dA.MakeRectangularBlockDiag(pfes[s1]->GetComm(),
|
||||
pfes[s1]->GlobalVSize(),
|
||||
pfes[s2]->GlobalVSize(),
|
||||
pfes[s1]->GetDofOffsets(),
|
||||
pfes[s2]->GetDofOffsets(),
|
||||
Grads(s1,s2));
|
||||
Rh.ConvertFrom(pfes[s1]->Dof_TrueDof_Matrix());
|
||||
Ph.ConvertFrom(pfes[s2]->Dof_TrueDof_Matrix());
|
||||
|
||||
phBlockGrad(s1,s2)->MakeRAP(Rh, dA, Ph);
|
||||
|
||||
phBlockGrad(s1,s2)->EliminateRows(*ess_tdofs[s1]);
|
||||
phBlockGrad(s1,s2)->EliminateCols(*ess_tdofs[s2]);
|
||||
}
|
||||
|
||||
pBlockGrad->SetBlock(s1, s2, phBlockGrad(s1,s2)->Ptr());
|
||||
}
|
||||
}
|
||||
|
||||
return *pBlockGrad;
|
||||
}
|
||||
|
||||
ParPrmBlockNonlinearForm::~ParPrmBlockNonlinearForm()
|
||||
{
|
||||
delete pBlockGrad;
|
||||
for (int s1=0; s1<fes.Size(); ++s1)
|
||||
{
|
||||
for (int s2=0; s2<fes.Size(); ++s2)
|
||||
{
|
||||
delete phBlockGrad(s1,s2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParPrmBlockNonlinearForm::SetStateFields(const Vector &xv) const
|
||||
{
|
||||
xs_true.Update(xv.GetData(), block_trueOffsets);
|
||||
xsv.Update(block_offsets);
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
fes[s]->GetProlongationMatrix()->Mult(
|
||||
xs_true.GetBlock(s), xsv.GetBlock(s));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParPrmBlockNonlinearForm::SetAdjointFields(const Vector &av) const
|
||||
{
|
||||
xs_true.Update(av.GetData(), block_trueOffsets);
|
||||
adv.Update(block_offsets);
|
||||
for (int s=0; s<fes.Size(); ++s)
|
||||
{
|
||||
fes[s]->GetProlongationMatrix()->Mult(
|
||||
xs_true.GetBlock(s), adv.GetBlock(s));
|
||||
}
|
||||
}
|
||||
|
||||
void ParPrmBlockNonlinearForm::SetPrmFields(const Vector &dv) const
|
||||
{
|
||||
xs_true.Update(dv.GetData(),prmblock_trueOffsets);
|
||||
xdv.Update(prmblock_offsets);
|
||||
for (int s=0; s<prmfes.Size(); ++s)
|
||||
{
|
||||
prmfes[s]->GetProlongationMatrix()->Mult(
|
||||
xs_true.GetBlock(s), xdv.GetBlock(s));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
// 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_PPRMNONLINEARFORM
|
||||
#define MFEM_PPRMNONLINEARFORM
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "pgridfunc.hpp"
|
||||
#include "prmnonlinearform.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** @brief A class representing a general parametric parallel block nonlinear operator
|
||||
defined on the Cartesian product of multiple ParFiniteElementSpace%s. */
|
||||
/** The ParPrmBlockNonlinearForm takes as input, and returns as output, vectors on
|
||||
the true dofs. */
|
||||
class ParPrmBlockNonlinearForm : public PrmBlockNonlinearForm
|
||||
{
|
||||
protected:
|
||||
mutable BlockVector xs_true, ys_true;
|
||||
mutable Array2D<OperatorHandle *> phBlockGrad;
|
||||
mutable BlockOperator *pBlockGrad;
|
||||
|
||||
public:
|
||||
/// Computes the energy of the system
|
||||
virtual double GetEnergy(const Vector &x) const;
|
||||
|
||||
/// Construct an empty ParPrmBlockNonlinearForm. Initialize with SetParSpaces().
|
||||
ParPrmBlockNonlinearForm() : pBlockGrad(nullptr) { }
|
||||
|
||||
/** @brief Construct a ParPrmBlockNonlinearForm on the given set of
|
||||
parametric and state ParFiniteElementSpace%s. */
|
||||
ParPrmBlockNonlinearForm(Array<ParFiniteElementSpace *> &pf, Array<ParFiniteElementSpace *> &ppf );
|
||||
|
||||
/// Return the @a k-th parallel FE state space of the ParPrmBlockNonlinearForm.
|
||||
ParFiniteElementSpace *ParFESpace(int k);
|
||||
/** @brief Return the @a k-th parallel FE state space of the ParPrmBlockNonlinearForm
|
||||
(const version). */
|
||||
const ParFiniteElementSpace *ParFESpace(int k) const;
|
||||
|
||||
/// Return the @a k-th parallel FE parameters space of the ParPrmBlockNonlinearForm.
|
||||
ParFiniteElementSpace *ParPrmFESpace(int k);
|
||||
/** @brief Return the @a k-th parallel FE parameters space of the ParPrmBlockNonlinearForm
|
||||
(const version). */
|
||||
const ParFiniteElementSpace *ParPrmFESpace(int k) const;
|
||||
|
||||
|
||||
/** @brief After a call to SetParSpaces(), the essential b.c. and the
|
||||
gradient-type (if different from the default) must be set again. */
|
||||
void SetParSpaces(Array<ParFiniteElementSpace *> &pf, Array<ParFiniteElementSpace *> &pprmf);
|
||||
|
||||
// Here, rhs is a true dof vector
|
||||
virtual void SetEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
|
||||
Array<Vector *> &rhs);
|
||||
|
||||
// Here, rhs is a true dof vector
|
||||
virtual void SetPrmEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
|
||||
Array<Vector *> &rhs);
|
||||
|
||||
|
||||
/// Block T-Vector to Block T-Vector
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Block T-Vector to Block T-Vector
|
||||
virtual void PrmMult(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Return the local block gradient matrix for the given true-dof vector x
|
||||
const BlockOperator &GetLocalGradient(const Vector &x) const;
|
||||
|
||||
virtual BlockOperator &GetGradient(const Vector &x) const;
|
||||
|
||||
/** @brief Set the operator type id for the blocks of the parallel gradient
|
||||
matrix/operator. The default type is Operator::Hypre_ParCSR. */
|
||||
void SetGradientType(Operator::Type tid);
|
||||
|
||||
/// Destructor.
|
||||
virtual ~ParPrmBlockNonlinearForm();
|
||||
|
||||
/// Set the state fields
|
||||
virtual void SetStateFields(const Vector &xv) const;
|
||||
|
||||
/// Set the adjoint fields
|
||||
virtual void SetAdjointFields(const Vector &av) const;
|
||||
|
||||
/// Set the parameters/design fields
|
||||
virtual void SetPrmFields(const Vector &dv) const;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,231 @@
|
||||
// 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_PRMNONLINEARFORM
|
||||
#define MFEM_PRMNONLINEARFORM
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "nonlininteg.hpp"
|
||||
#include "nonlinearform_ext.hpp"
|
||||
#include "bilinearform.hpp"
|
||||
#include "gridfunc.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** @brief A class representing a general parametric block nonlinear operator
|
||||
defined on the Cartesian product of multiple FiniteElementSpace%s. */
|
||||
class PrmBlockNonlinearForm : public Operator
|
||||
{
|
||||
protected:
|
||||
/// FE spaces on which the form lives.
|
||||
Array<FiniteElementSpace*> fes;
|
||||
|
||||
/// FE spaces for the parametric fields
|
||||
Array<FiniteElementSpace*> prmfes;
|
||||
|
||||
int prmheight;
|
||||
int prmwidth;
|
||||
|
||||
|
||||
/// Set of Domain Integrators to be assembled (added).
|
||||
Array<PrmBlockNonlinearFormIntegrator*> dnfi;
|
||||
|
||||
/// Set of interior face Integrators to be assembled (added).
|
||||
Array<PrmBlockNonlinearFormIntegrator*> fnfi;
|
||||
|
||||
/// Set of Boundary Face Integrators to be assembled (added).
|
||||
Array<PrmBlockNonlinearFormIntegrator*> bfnfi;
|
||||
Array<Array<int>*> bfnfi_marker;
|
||||
|
||||
/** Auxiliary block-vectors for wrapping input and output vectors or holding
|
||||
GridFunction-like block-vector data (e.g. in parallel). */
|
||||
mutable BlockVector xs, ys;
|
||||
mutable BlockVector prmxs, prmys;
|
||||
|
||||
|
||||
/** Auxiliary block-vectors for holding
|
||||
GridFunction-like block-vector data (e.g. in parallel). */
|
||||
mutable BlockVector xsv;
|
||||
|
||||
/** Auxiliary block-vectors for holding
|
||||
GridFunction-like block-vector data for the parameter fields
|
||||
(e.g. in parallel). */
|
||||
mutable BlockVector xdv;
|
||||
/** Auxiliary block-vectors for holding
|
||||
GridFunction-like block-vector data for the adjoint fields
|
||||
(e.g. in parallel). */
|
||||
mutable BlockVector adv;
|
||||
|
||||
|
||||
mutable Array2D<SparseMatrix*> Grads, cGrads;
|
||||
mutable BlockOperator *BlockGrad;
|
||||
|
||||
// A list of the offsets
|
||||
Array<int> block_offsets;
|
||||
Array<int> block_trueOffsets;
|
||||
// A list with the offsets for the parametric fields
|
||||
Array<int> prmblock_offsets;
|
||||
Array<int> prmblock_trueOffsets;
|
||||
|
||||
// Array of Arrays of tdofs for each space in 'fes'
|
||||
Array<Array<int> *> ess_tdofs;
|
||||
|
||||
// Array of Arrays of tdofs for each space in 'prmfes'
|
||||
Array<Array<int> *> prmess_tdofs;
|
||||
|
||||
/// Array of pointers to the prolongation matrix of fes, may be NULL
|
||||
Array<const Operator *> P;
|
||||
|
||||
/// Array of pointers to the prolongation matrix of prmfes, may be NULL
|
||||
Array<const Operator *> Pprm;
|
||||
|
||||
/// Array of results of dynamic-casting P to SparseMatrix pointer
|
||||
Array<const SparseMatrix *> cP;
|
||||
|
||||
/// Array of results of dynamic-casting Pprm to SparseMatrix pointer
|
||||
Array<const SparseMatrix *> cPprm;
|
||||
|
||||
|
||||
/// Indicator if the Operator is part of a parallel run
|
||||
bool is_serial = true;
|
||||
|
||||
/// Indicator if the Operator needs prolongation on assembly
|
||||
bool needs_prolongation = false;
|
||||
|
||||
/// Indicator if the Operator needs prolongation on assembly
|
||||
bool prmneeds_prolongation = false;
|
||||
|
||||
|
||||
mutable BlockVector aux1, aux2;
|
||||
|
||||
mutable BlockVector prmaux1, prmaux2;
|
||||
|
||||
const BlockVector &Prolongate(const BlockVector &bx) const;
|
||||
|
||||
const BlockVector &PrmProlongate(const BlockVector &bx) const;
|
||||
|
||||
/// Specialized version of GetEnergy() for BlockVectors
|
||||
//double GetEnergyBlocked(const BlockVector &bx) const;
|
||||
double GetEnergyBlocked(const BlockVector &bx, const BlockVector &dx) const;
|
||||
|
||||
|
||||
/// Specialized version of Mult() for BlockVector%s
|
||||
/// Block L-Vector to Block L-Vector
|
||||
void MultBlocked(const BlockVector &bx, const BlockVector &dx, BlockVector &by) const;
|
||||
|
||||
/// Specialized version of Mult() for BlockVector%s
|
||||
/// Block L-Vector to Block L-Vector
|
||||
/// bx - state vector, ax - adjoint vector, dx - parametric fields
|
||||
/// dy = ax' d(residual(bx))/d(dx)
|
||||
void MultPrmBlocked(const BlockVector &bx, const BlockVector & ax, const BlockVector &dx, BlockVector &dy) const;
|
||||
|
||||
|
||||
/// Specialized version of GetGradient() for BlockVector
|
||||
//void ComputeGradientBlocked(const BlockVector &bx) const;
|
||||
void ComputeGradientBlocked(const BlockVector &bx, const BlockVector &dx) const;
|
||||
|
||||
public:
|
||||
/// Construct an empty BlockNonlinearForm. Initialize with SetSpaces().
|
||||
PrmBlockNonlinearForm();
|
||||
|
||||
/// Construct a BlockNonlinearForm on the given set of FiniteElementSpace%s.
|
||||
PrmBlockNonlinearForm(Array<FiniteElementSpace *> &f, Array<FiniteElementSpace *> &pf );
|
||||
|
||||
/// Return the @a k-th FE space of the PrmBlockNonlinearForm.
|
||||
FiniteElementSpace *FESpace(int k) { return fes[k]; }
|
||||
|
||||
/// Return the @a k-th parametric FE space of the PrmBlockNonlinearForm.
|
||||
FiniteElementSpace *PrmFESpace(int k) { return prmfes[k]; }
|
||||
|
||||
|
||||
/// Return the @a k-th FE space of the BlockNonlinearForm (const version).
|
||||
const FiniteElementSpace *FESpace(int k) const { return fes[k]; }
|
||||
|
||||
/// Return the @a k-th parametric FE space of the BlockNonlinearForm (const version).
|
||||
const FiniteElementSpace *PrmFESpace(int k) const { return prmfes[k]; }
|
||||
|
||||
Array<PrmBlockNonlinearFormIntegrator*>& GetDNFI(){ return dnfi;}
|
||||
|
||||
|
||||
/// (Re)initialize the PrmBlockNonlinearForm.
|
||||
/** After a call to SetSpaces(), the essential b.c. must be set again. */
|
||||
void SetSpaces(Array<FiniteElementSpace *> &f, Array<FiniteElementSpace *> &prmf);
|
||||
|
||||
/// Return the regular dof offsets.
|
||||
const Array<int> &GetBlockOffsets() const { return block_offsets; }
|
||||
/// Return the true-dof offsets.
|
||||
const Array<int> &GetBlockTrueOffsets() const { return block_trueOffsets; }
|
||||
|
||||
/// Return the regular dof offsets for the parameters.
|
||||
const Array<int> &PrmGetBlockOffsets() const { return prmblock_offsets; }
|
||||
/// Return the true-dof offsets for the parameters.
|
||||
const Array<int> &PrmGetBlockTrueOffsets() const { return prmblock_trueOffsets; }
|
||||
|
||||
/// Adds new Domain Integrator.
|
||||
void AddDomainIntegrator(PrmBlockNonlinearFormIntegrator *nlfi)
|
||||
{ dnfi.Append(nlfi); }
|
||||
|
||||
/// Adds new Interior Face Integrator.
|
||||
void AddInteriorFaceIntegrator(PrmBlockNonlinearFormIntegrator *nlfi)
|
||||
{ fnfi.Append(nlfi); }
|
||||
|
||||
/// Adds new Boundary Face Integrator.
|
||||
void AddBdrFaceIntegrator(PrmBlockNonlinearFormIntegrator *nlfi)
|
||||
{ bfnfi.Append(nlfi); bfnfi_marker.Append(NULL); }
|
||||
|
||||
/** @brief Adds new Boundary Face Integrator, restricted to specific boundary
|
||||
attributes. */
|
||||
void AddBdrFaceIntegrator(PrmBlockNonlinearFormIntegrator *nlfi,
|
||||
Array<int> &bdr_marker);
|
||||
|
||||
virtual void SetEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
|
||||
Array<Vector *> &rhs);
|
||||
|
||||
virtual void SetPrmEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
|
||||
Array<Vector *> &rhs);
|
||||
|
||||
|
||||
virtual double GetEnergy(const Vector &x) const;
|
||||
|
||||
/// Method is only called in serial, the parallel version calls MultBlocked
|
||||
/// directly.
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
|
||||
/// Method is only called in serial, the parallel version calls MultBlocked
|
||||
/// directly.
|
||||
virtual void PrmMult(const Vector &x, Vector &t) const;
|
||||
|
||||
/// Method is only called in serial, the parallel version calls
|
||||
/// GetGradientBlocked directly.
|
||||
virtual Operator &GetGradient(const Vector &x) const;
|
||||
|
||||
/// Set the state fields
|
||||
virtual void SetStateFields(const Vector &xv) const;
|
||||
|
||||
/// Set the adjoint fields
|
||||
virtual void SetAdjointFields(const Vector &av) const;
|
||||
|
||||
/// Set the parameters/design fields
|
||||
virtual void SetPrmFields(const Vector &dv) const;
|
||||
|
||||
|
||||
|
||||
/// Destructor.
|
||||
virtual ~PrmBlockNonlinearForm();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -27,6 +27,7 @@ list(APPEND SRCS
|
||||
stable3d.cpp
|
||||
table.cpp
|
||||
tic_toc.cpp
|
||||
tinyxml2.cpp
|
||||
version.cpp
|
||||
)
|
||||
|
||||
@@ -55,6 +56,7 @@ list(APPEND HDRS
|
||||
table.hpp
|
||||
tassign.hpp
|
||||
tic_toc.hpp
|
||||
tinyxml2.h
|
||||
text.hpp
|
||||
version.hpp
|
||||
)
|
||||
|
||||
+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)
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+2380
File diff suppressed because it is too large
Load Diff
+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)
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ list(APPEND SRCS
|
||||
blockvector.cpp
|
||||
complex_operator.cpp
|
||||
densemat.cpp
|
||||
symmat.cpp
|
||||
handle.cpp
|
||||
matrix.cpp
|
||||
ode.cpp
|
||||
@@ -31,6 +32,7 @@ list(APPEND HDRS
|
||||
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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
+238
-11
@@ -128,6 +128,15 @@ HypreParVector::HypreParVector(ParFiniteElementSpace *pfes)
|
||||
own_ParVector = 1;
|
||||
}
|
||||
|
||||
void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
|
||||
{
|
||||
if (own_ParVector) { hypre_ParVectorDestroy(x); }
|
||||
Destroy();
|
||||
x = y;
|
||||
_SetDataAndSize_();
|
||||
own_ParVector = owner;
|
||||
}
|
||||
|
||||
Vector * HypreParVector::GlobalVector() const
|
||||
{
|
||||
hypre_Vector *hv = hypre_ParVectorToVectorAll(*this);
|
||||
@@ -926,6 +935,15 @@ void HypreParMatrix::GetOffd(SparseMatrix &offd, HYPRE_Int* &cmap) const
|
||||
cmap = A->col_map_offd;
|
||||
}
|
||||
|
||||
void HypreParMatrix::MergeDiagAndOffd(SparseMatrix &merged)
|
||||
{
|
||||
SparseMatrix tmp_wrapper;
|
||||
hypre_CSRMatrix *hypre_merged = hypre_MergeDiagAndOffd(A);
|
||||
MakeWrapper(hypre_merged, tmp_wrapper);
|
||||
merged = tmp_wrapper;
|
||||
hypre_CSRMatrixDestroy(hypre_merged);
|
||||
}
|
||||
|
||||
void HypreParMatrix::GetBlocks(Array2D<HypreParMatrix*> &blocks,
|
||||
bool interleaved_rows,
|
||||
bool interleaved_cols) const
|
||||
@@ -966,6 +984,46 @@ HypreParMatrix * HypreParMatrix::Transpose() const
|
||||
return new HypreParMatrix(At);
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
HypreParMatrix *HypreParMatrix::ExtractSubmatrix(const Array<int> &indices,
|
||||
double threshhold) const
|
||||
{
|
||||
if (!(A->comm))
|
||||
{
|
||||
hypre_MatvecCommPkgCreate(A);
|
||||
}
|
||||
|
||||
hypre_ParCSRMatrix *submat;
|
||||
|
||||
// Get number of rows stored on this processor
|
||||
int local_num_vars = hypre_CSRMatrixNumRows(hypre_ParCSRMatrixDiag(A));
|
||||
|
||||
// Form hypre CF-splitting array designating submatrix as F-points (-1)
|
||||
Array<int> CF_marker(local_num_vars);
|
||||
CF_marker = 1;
|
||||
for (int j=0; j<indices.Size(); j++)
|
||||
{
|
||||
if (indices[j] > local_num_vars)
|
||||
{
|
||||
MFEM_WARNING("WARNING : " << indices[j] << " > " << local_num_vars);
|
||||
}
|
||||
CF_marker[indices[j]] = -1;
|
||||
}
|
||||
|
||||
// Construct cpts_global array on hypre matrix structure
|
||||
HYPRE_BigInt *cpts_global;
|
||||
hypre_BoomerAMGCoarseParms(MPI_COMM_WORLD, local_num_vars, 1, NULL,
|
||||
CF_marker, NULL, &cpts_global);
|
||||
|
||||
// Extract submatrix into *submat
|
||||
hypre_ParCSRMatrixExtractSubmatrixFC(A, CF_marker, cpts_global,
|
||||
"FF", &submat, threshhold);
|
||||
|
||||
mfem_hypre_TFree(cpts_global);
|
||||
return new HypreParMatrix(submat);
|
||||
}
|
||||
#endif
|
||||
|
||||
HYPRE_Int HypreParMatrix::Mult(HypreParVector &x, HypreParVector &y,
|
||||
double a, double b)
|
||||
{
|
||||
@@ -1279,6 +1337,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++)
|
||||
@@ -1568,6 +1627,36 @@ void HypreParMatrix::Destroy()
|
||||
}
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
|
||||
void BlockInverseScale(const HypreParMatrix *A, HypreParMatrix *C,
|
||||
const Vector *b, HypreParVector *d,
|
||||
int blocksize, BlockInverseScaleJob job)
|
||||
{
|
||||
if (job == BlockInverseScaleJob::MATRIX_ONLY ||
|
||||
job == BlockInverseScaleJob::MATRIX_AND_RHS)
|
||||
{
|
||||
hypre_ParCSRMatrix *C_hypre;
|
||||
hypre_ParcsrBdiagInvScal(*A, blocksize, &C_hypre);
|
||||
hypre_ParCSRMatrixDropSmallEntries(C_hypre, 1e-15, 1);
|
||||
C->WrapHypreParCSRMatrix(C_hypre);
|
||||
}
|
||||
|
||||
if (job == BlockInverseScaleJob::RHS_ONLY ||
|
||||
job == BlockInverseScaleJob::MATRIX_AND_RHS)
|
||||
{
|
||||
HypreParVector b_Hypre(A->GetComm(),
|
||||
A->GetGlobalNumRows(),
|
||||
b->GetData(), A->GetRowStarts());
|
||||
hypre_ParVector *d_hypre;
|
||||
hypre_ParvecBdiagInvScal(b_Hypre, blocksize, &d_hypre, *A);
|
||||
|
||||
d->WrapHypreParVector(d_hypre, true);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MFEM_HYPRE_VERSION < 21400
|
||||
|
||||
HypreParMatrix *Add(double alpha, const HypreParMatrix &A,
|
||||
@@ -1995,6 +2084,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 +2252,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 +2272,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 +2560,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 +2614,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 +2637,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 +2657,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 +2780,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 +2793,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 +2813,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)
|
||||
@@ -2797,6 +2905,11 @@ void HypreGMRES::SetTol(double tol)
|
||||
HYPRE_GMRESSetTol(gmres_solver, tol);
|
||||
}
|
||||
|
||||
void HypreGMRES::SetAbsTol(double tol)
|
||||
{
|
||||
HYPRE_GMRESSetAbsoluteTol(gmres_solver, tol);
|
||||
}
|
||||
|
||||
void HypreGMRES::SetMaxIter(int max_iter)
|
||||
{
|
||||
HYPRE_GMRESSetMaxIter(gmres_solver, max_iter);
|
||||
@@ -3600,6 +3713,116 @@ void HypreBoomerAMG::SetElasticityOptions(ParFiniteElementSpace *fespace)
|
||||
error_mode = IGNORE_HYPRE_ERRORS;
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
|
||||
void HypreBoomerAMG::SetAdvectiveOptions(int distanceR,
|
||||
const std::string &prerelax,
|
||||
const std::string &postrelax)
|
||||
{
|
||||
// Hypre parameters
|
||||
int Sabs = 0;
|
||||
int interp_type = 100;
|
||||
int relax_type = 10;
|
||||
int coarsen_type = 6;
|
||||
double strength_tolC = 0.1;
|
||||
double strength_tolR = 0.01;
|
||||
double filter_tolR = 0.0;
|
||||
double filterA_tol = 0.0;
|
||||
|
||||
// Set relaxation on specified grid points
|
||||
int ns_down, ns_up, ns_coarse;
|
||||
if (distanceR > 0)
|
||||
{
|
||||
ns_down = prerelax.length();
|
||||
ns_up = postrelax.length();
|
||||
ns_coarse = 1;
|
||||
|
||||
// Array to store relaxation scheme and pass to Hypre
|
||||
HYPRE_Int **grid_relax_points = mfem_hypre_TAlloc(HYPRE_Int*, 4);
|
||||
grid_relax_points[0] = NULL;
|
||||
grid_relax_points[1] = mfem_hypre_TAlloc(HYPRE_Int, ns_down);
|
||||
grid_relax_points[2] = mfem_hypre_TAlloc(HYPRE_Int, ns_up);
|
||||
grid_relax_points[3] = mfem_hypre_TAlloc(HYPRE_Int, 1);
|
||||
grid_relax_points[3][0] = 0;
|
||||
|
||||
// set down relax scheme
|
||||
for (int i = 0; i<ns_down; i++)
|
||||
{
|
||||
if (prerelax[i] == 'F')
|
||||
{
|
||||
grid_relax_points[1][i] = -1;
|
||||
}
|
||||
else if (prerelax[i] == 'C')
|
||||
{
|
||||
grid_relax_points[1][i] = 1;
|
||||
}
|
||||
else if (prerelax[i] == 'A')
|
||||
{
|
||||
grid_relax_points[1][i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// set up relax scheme
|
||||
for (int i = 0; i<ns_up; i++)
|
||||
{
|
||||
if (postrelax[i] == 'F')
|
||||
{
|
||||
grid_relax_points[2][i] = -1;
|
||||
}
|
||||
else if (postrelax[i] == 'C')
|
||||
{
|
||||
grid_relax_points[2][i] = 1;
|
||||
}
|
||||
else if (postrelax[i] == 'A')
|
||||
{
|
||||
grid_relax_points[2][i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
HYPRE_BoomerAMGSetRestriction(amg_precond, distanceR);
|
||||
|
||||
HYPRE_BoomerAMGSetGridRelaxPoints(amg_precond, grid_relax_points);
|
||||
|
||||
HYPRE_BoomerAMGSetInterpType(amg_precond, interp_type);
|
||||
}
|
||||
|
||||
if (Sabs)
|
||||
{
|
||||
HYPRE_BoomerAMGSetSabs(amg_precond, Sabs);
|
||||
}
|
||||
|
||||
HYPRE_BoomerAMGSetCoarsenType(amg_precond, coarsen_type);
|
||||
|
||||
// does not support aggressive coarsening
|
||||
HYPRE_BoomerAMGSetAggNumLevels(amg_precond, 0);
|
||||
|
||||
HYPRE_BoomerAMGSetStrongThreshold(amg_precond, strength_tolC);
|
||||
|
||||
if (distanceR > 0)
|
||||
{
|
||||
HYPRE_BoomerAMGSetStrongThresholdR(amg_precond, strength_tolR);
|
||||
HYPRE_BoomerAMGSetFilterThresholdR(amg_precond, filter_tolR);
|
||||
}
|
||||
|
||||
if (relax_type > -1)
|
||||
{
|
||||
HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type);
|
||||
}
|
||||
|
||||
if (distanceR > 0)
|
||||
{
|
||||
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, ns_coarse, 3);
|
||||
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, ns_down, 1);
|
||||
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, ns_up, 2);
|
||||
|
||||
HYPRE_BoomerAMGSetADropTol(amg_precond, filterA_tol);
|
||||
// type = -1: drop based on row inf-norm
|
||||
HYPRE_BoomerAMGSetADropType(amg_precond, -1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
HypreBoomerAMG::~HypreBoomerAMG()
|
||||
{
|
||||
for (int i = 0; i < rbms.Size(); i++)
|
||||
@@ -3703,6 +3926,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 +3937,7 @@ void HypreAMS::Init(ParFiniteElementSpace *edge_fespace)
|
||||
else
|
||||
{
|
||||
z = z_coord.ParallelProject();
|
||||
z->HostReadWrite();
|
||||
HYPRE_AMSSetCoordinateVectors(ams, *x, *y, *z);
|
||||
}
|
||||
}
|
||||
|
||||
+179
-5
@@ -81,6 +81,14 @@ private:
|
||||
inline void _SetDataAndSize_();
|
||||
|
||||
public:
|
||||
|
||||
/// Default constructor, no underlying @a hypre_ParVector is created.
|
||||
HypreParVector()
|
||||
{
|
||||
own_ParVector = false;
|
||||
x = NULL;
|
||||
}
|
||||
|
||||
/** @brief Creates vector with given global size and parallel partitioning of
|
||||
the rows/columns given by @a col. */
|
||||
/** @anchor hypre_partitioning_descr
|
||||
@@ -113,6 +121,9 @@ public:
|
||||
/// MPI communicator
|
||||
MPI_Comm GetComm() { return x->comm; }
|
||||
|
||||
/// Converts hypre's format to HypreParVector
|
||||
void WrapHypreParVector(hypre_ParVector *y, bool owner=true);
|
||||
|
||||
/// Returns the parallel row/column partitioning
|
||||
/** See @ref hypre_partitioning_descr "here" for a description of the
|
||||
partitioning array. */
|
||||
@@ -228,15 +239,24 @@ public:
|
||||
/// An empty matrix to be used as a reference to an existing matrix
|
||||
HypreParMatrix();
|
||||
|
||||
/// Converts hypre's format to HypreParMatrix
|
||||
/** If @a owner is false, ownership of @a a is not transferred */
|
||||
void WrapHypreParCSRMatrix(hypre_ParCSRMatrix *a, bool owner = true)
|
||||
{
|
||||
Destroy();
|
||||
Init();
|
||||
A = a;
|
||||
ParCSROwner = owner;
|
||||
height = GetNumRows();
|
||||
width = GetNumCols();
|
||||
}
|
||||
|
||||
/// Converts hypre's format to HypreParMatrix
|
||||
/** If @a owner is false, ownership of @a a is not transferred */
|
||||
explicit HypreParMatrix(hypre_ParCSRMatrix *a, bool owner = true)
|
||||
{
|
||||
Init();
|
||||
A = a;
|
||||
if (!owner) { ParCSROwner = 0; }
|
||||
height = GetNumRows();
|
||||
width = GetNumCols();
|
||||
WrapHypreParCSRMatrix(a, owner);
|
||||
}
|
||||
|
||||
/// Creates block-diagonal square parallel matrix.
|
||||
@@ -387,6 +407,13 @@ public:
|
||||
void GetDiag(SparseMatrix &diag) const;
|
||||
/// Get the local off-diagonal block. NOTE: 'offd' will not own any data.
|
||||
void GetOffd(SparseMatrix &offd, HYPRE_Int* &cmap) const;
|
||||
/** @brief Get a single SparseMatrix containing all rows from this processor,
|
||||
merged from the diagonal and off-diagonal blocks stored by the
|
||||
HypreParMatrix. */
|
||||
/** @note The number of columns in the SparseMatrix will be the global number
|
||||
of columns in the parallel matrix, so using this method may result in an
|
||||
integer overflow in the column indices. */
|
||||
void MergeDiagAndOffd(SparseMatrix &merged);
|
||||
|
||||
/** Split the matrix into M x N equally sized blocks of parallel matrices.
|
||||
The size of 'blocks' must already be set to M x N. */
|
||||
@@ -397,6 +424,13 @@ public:
|
||||
/// Returns the transpose of *this
|
||||
HypreParMatrix * Transpose() const;
|
||||
|
||||
/** Returns principle submatrix given by array of indices of connections
|
||||
with relative size > @a threshold in *this. */
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
HypreParMatrix *ExtractSubmatrix(const Array<int> &indices,
|
||||
double threshhold=0.0) const;
|
||||
#endif
|
||||
|
||||
/// Returns the number of rows in the diagonal block of the ParCSRMatrix
|
||||
int GetNumRows() const
|
||||
{
|
||||
@@ -549,6 +583,23 @@ public:
|
||||
Type GetType() const { return Hypre_ParCSR; }
|
||||
};
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
|
||||
enum class BlockInverseScaleJob
|
||||
{
|
||||
MATRIX_ONLY,
|
||||
RHS_ONLY,
|
||||
MATRIX_AND_RHS
|
||||
};
|
||||
|
||||
/** Constructs and applies block diagonal inverse of HypreParMatrix.
|
||||
The enum @a job specifies whether the matrix or the RHS should be
|
||||
scaled (or both). */
|
||||
void BlockInverseScale(const HypreParMatrix *A, HypreParMatrix *C,
|
||||
const Vector *b, HypreParVector *d,
|
||||
int blocksize, BlockInverseScaleJob job);
|
||||
#endif
|
||||
|
||||
/** @brief Return a new matrix `C = alpha*A + beta*B`, assuming that both `A`
|
||||
and `B` use the same row and column partitions and the same `col_map_offd`
|
||||
arrays. */
|
||||
@@ -638,6 +689,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
|
||||
@@ -646,11 +700,12 @@ public:
|
||||
4 = truncated l1-scaled block Gauss-Seidel/SSOR
|
||||
5 = lumped Jacobi
|
||||
6 = Gauss-Seidel
|
||||
10 = On-processor forward solve for matrix w/ triangular structure
|
||||
16 = Chebyshev
|
||||
1001 = Taubin polynomial smoother
|
||||
1002 = FIR polynomial smoother. */
|
||||
enum Type { Jacobi = 0, l1Jacobi = 1, l1GS = 2, l1GStr = 4, lumpedJacobi = 5,
|
||||
GS = 6, Chebyshev = 16, Taubin = 1001, FIR = 1002
|
||||
GS = 6, OPFS = 10, Chebyshev = 16, Taubin = 1001, FIR = 1002
|
||||
};
|
||||
|
||||
HypreSmoother();
|
||||
@@ -684,6 +739,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 +753,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();
|
||||
};
|
||||
|
||||
@@ -755,6 +819,28 @@ public:
|
||||
virtual ~HypreSolver();
|
||||
};
|
||||
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
/** Preconditioner for HypreParMatrices that are triangular in some ordering.
|
||||
Finds correct ordering and performs forward substitution on processor
|
||||
as approximate inverse. Exact on one processor. */
|
||||
class HypreTriSolve : public HypreSolver
|
||||
{
|
||||
public:
|
||||
HypreTriSolve() : HypreSolver() { }
|
||||
explicit HypreTriSolve(HypreParMatrix &A) : HypreSolver(&A) { }
|
||||
virtual operator HYPRE_Solver() const { return NULL; }
|
||||
|
||||
virtual HYPRE_PtrToParSolverFcn SetupFcn() const
|
||||
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSROnProcTriSetup; }
|
||||
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
|
||||
{ return (HYPRE_PtrToParSolverFcn) HYPRE_ParCSROnProcTriSolve; }
|
||||
|
||||
HypreParMatrix* GetData() { return A; }
|
||||
virtual ~HypreTriSolve() { }
|
||||
};
|
||||
#endif
|
||||
|
||||
/// PCG solver in hypre
|
||||
class HyprePCG : public HypreSolver
|
||||
{
|
||||
@@ -832,6 +918,7 @@ public:
|
||||
virtual void SetOperator(const Operator &op);
|
||||
|
||||
void SetTol(double tol);
|
||||
void SetAbsTol(double tol);
|
||||
void SetMaxIter(int max_iter);
|
||||
void SetKDim(int dim);
|
||||
void SetLogging(int logging);
|
||||
@@ -1116,9 +1203,94 @@ public:
|
||||
construct A. */
|
||||
void SetElasticityOptions(ParFiniteElementSpace *fespace);
|
||||
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
/** Hypre parameters to use AIR AMG solve for advection-dominated problems.
|
||||
See "Nonsymmetric Algebraic Multigrid Based on Local Approximate Ideal
|
||||
Restriction (AIR)," Manteuffel, Ruge, Southworth, SISC (2018),
|
||||
DOI:/10.1137/17M1144350. Options: "distanceR" -> distance of neighbor
|
||||
DOFs to buld restriction operator; options include 1, 2, and 15 (1.5).
|
||||
Strings "prerelax" and "postrelax" indicate points to relax on:
|
||||
F = F-points, C = C-points, A = all points. E.g., FFC -> relax on
|
||||
F-points, relax again on F-points, then relax on C-points. */
|
||||
void SetAdvectiveOptions(int distance=15, const std::string &prerelax="",
|
||||
const std::string &postrelax="FFC");
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetStrongThresholdR(double strengthR)
|
||||
{ HYPRE_BoomerAMGSetStrongThresholdR(amg_precond, strengthR); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetFilterThresholdR(double filterR)
|
||||
{ HYPRE_BoomerAMGSetFilterThresholdR(amg_precond, filterR); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetRestriction(int restrict_type)
|
||||
{ HYPRE_BoomerAMGSetRestriction(amg_precond, restrict_type); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetIsTriangular()
|
||||
{ HYPRE_BoomerAMGSetIsTriangular(amg_precond, 1); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetGMRESSwitchR(int gmres_switch)
|
||||
{ HYPRE_BoomerAMGSetGMRESSwitchR(amg_precond, gmres_switch); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetCycleNumSweeps(int prerelax, int postrelax)
|
||||
{
|
||||
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, prerelax, 1);
|
||||
HYPRE_BoomerAMGSetCycleNumSweeps(amg_precond, postrelax, 2);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetPrintLevel(int print_level)
|
||||
{ HYPRE_BoomerAMGSetPrintLevel(amg_precond, print_level); }
|
||||
|
||||
void SetMaxIter(int max_iter)
|
||||
{ HYPRE_BoomerAMGSetMaxIter(amg_precond, max_iter); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetMaxLevels(int max_levels)
|
||||
{ HYPRE_BoomerAMGSetMaxLevels(amg_precond, max_levels); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetTol(double tol)
|
||||
{ HYPRE_BoomerAMGSetTol(amg_precond, tol); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetStrengthThresh(double strength)
|
||||
{ HYPRE_BoomerAMGSetStrongThreshold(amg_precond, strength); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetInterpolation(int interp_type)
|
||||
{ HYPRE_BoomerAMGSetInterpType(amg_precond, interp_type); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetCoarsening(int coarsen_type)
|
||||
{ HYPRE_BoomerAMGSetCoarsenType(amg_precond, coarsen_type); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetRelaxType(int relax_type)
|
||||
{ HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetCycleType(int cycle_type)
|
||||
{ HYPRE_BoomerAMGSetCycleType(amg_precond, cycle_type); }
|
||||
|
||||
void GetNumIterations(int &num_it)
|
||||
{ HYPRE_BoomerAMGGetNumIterations(amg_precond, &num_it); }
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetNodal(int blocksize)
|
||||
{
|
||||
HYPRE_BoomerAMGSetNumFunctions(amg_precond, blocksize);
|
||||
HYPRE_BoomerAMGSetNodal(amg_precond, 1);
|
||||
}
|
||||
|
||||
/// Expert option - consult hypre documentation/team
|
||||
void SetAggressiveCoarsening(int num_levels)
|
||||
{ HYPRE_BoomerAMGSetAggNumLevels(amg_precond, num_levels); }
|
||||
|
||||
/// The typecast to HYPRE_Solver returns the internal amg_precond
|
||||
virtual operator HYPRE_Solver() const { return amg_precond; }
|
||||
|
||||
@@ -1127,6 +1299,8 @@ public:
|
||||
virtual HYPRE_PtrToParSolverFcn SolveFcn() const
|
||||
{ return (HYPRE_PtrToParSolverFcn) HYPRE_BoomerAMGSolve; }
|
||||
|
||||
using HypreSolver::Mult;
|
||||
|
||||
virtual ~HypreBoomerAMG();
|
||||
};
|
||||
|
||||
|
||||
+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"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user