Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9044a627f | ||
|
|
ce70a6fff0 | ||
|
|
3e4deba10c | ||
|
|
c524be911a | ||
|
|
8fe7bea02f | ||
|
|
1e47f7f633 | ||
|
|
d49881f5e2 | ||
|
|
b27a28040b | ||
|
|
ecaf0c15ba | ||
|
|
37f4c9cc5d | ||
|
|
bebeca1311 | ||
|
|
324320d4b4 | ||
|
|
bb03b99903 | ||
|
|
ddcea536c1 | ||
|
|
4cae939bb4 | ||
|
|
c9e82c3512 | ||
|
|
a9bc9c7eb0 | ||
|
|
e04c8d4230 | ||
|
|
8ee2bb39bb | ||
|
|
2d09fc56f7 | ||
|
|
5380faba38 | ||
|
|
6095628e27 | ||
|
|
8506904200 | ||
|
|
12c7976b2e | ||
|
|
3e5e301263 | ||
|
|
b76734c582 | ||
|
|
1ddd1f0f3b | ||
|
|
b1e84b8127 | ||
|
|
44af467936 | ||
|
|
d404934819 | ||
|
|
e908685036 | ||
|
|
d79f5c6c92 | ||
|
|
5b93ea7484 | ||
|
|
c7c552f56a | ||
|
|
d09bc9b76c | ||
|
|
a637478552 | ||
|
|
827a48e31b | ||
|
|
7feb27958b | ||
|
|
89423a10e7 | ||
|
|
47fa9c6a07 | ||
|
|
4390216bca | ||
|
|
6206c775cc | ||
|
|
a078107c51 | ||
|
|
7121a753ba | ||
|
|
0b065bc850 | ||
|
|
1b97908085 | ||
|
|
f8ca84ecc8 | ||
|
|
772ed7d894 | ||
|
|
4adcc35a20 | ||
|
|
8b03d1ba85 | ||
|
|
c7a2971f0d | ||
|
|
7e48976f2f | ||
|
|
1361e3907d | ||
|
|
a03943e158 | ||
|
|
283f5fe681 | ||
|
|
6f5ceb97e8 | ||
|
|
6086132442 | ||
|
|
6a3e6c5d41 | ||
|
|
c6e7e72fa6 | ||
|
|
9148258da9 | ||
|
|
8aa988ad5d | ||
|
|
3a2ec16125 | ||
|
|
5e6df07adb | ||
|
|
f65f931764 | ||
|
|
76b41b3ec0 | ||
|
|
58dda43f19 | ||
|
|
fbf82e4ff6 | ||
|
|
105f89079e | ||
|
|
945636ed0e | ||
|
|
967a18eb8c | ||
|
|
682584d49c | ||
|
|
fe9454abb6 | ||
|
|
b75f13682a | ||
|
|
8173b468c7 | ||
|
|
f7fa5055c7 | ||
|
|
6699f71e2e | ||
|
|
7de0131815 | ||
|
|
4cd960bd3a | ||
|
|
88082dc136 | ||
|
|
8da76d18d2 | ||
|
|
0a6e5734fe | ||
|
|
18336a2936 | ||
|
|
9af03fc80b | ||
|
|
baf09ea081 | ||
|
|
a23a47b216 | ||
|
|
2129d9a4f1 | ||
|
|
528154d68d | ||
|
|
a8deea5def | ||
|
|
fa7d17e0da | ||
|
|
9f7d5736d3 | ||
|
|
229b94e3fb | ||
|
|
6e149b75e5 | ||
|
|
90d6afb814 | ||
|
|
94aa25e138 | ||
|
|
998f7bd093 | ||
|
|
df093c9c41 | ||
|
|
8f25a46d5c | ||
|
|
1682ade22e | ||
|
|
be9fc95d6f | ||
|
|
5dd706208a | ||
|
|
7735de95d2 | ||
|
|
2f023c6e53 | ||
|
|
4cf47eca13 | ||
|
|
883a5843cd | ||
|
|
ef5dbd0bf6 | ||
|
|
b02eaf34a1 | ||
|
|
f26765afa1 | ||
|
|
d13f3903e6 | ||
|
|
bc0c2a1460 | ||
|
|
e15c28140d | ||
|
|
f81a681203 | ||
|
|
5705de1507 | ||
|
|
1958f8de19 | ||
|
|
23a8f83785 | ||
|
|
aacc159390 | ||
|
|
c91e698800 | ||
|
|
ee776a8d0e | ||
|
|
3b26d08793 | ||
|
|
60082f90b6 | ||
|
|
13892eed2b | ||
|
|
50907081fd | ||
|
|
9409b7ab3b | ||
|
|
86747dd076 | ||
|
|
9d6a7fe30b | ||
|
|
e717f619e7 | ||
|
|
2f2534be10 | ||
|
|
3b8c4323fb | ||
|
|
e786ad65eb | ||
|
|
ef1ddbceea | ||
|
|
f69a26e3df | ||
|
|
ecacadacf2 | ||
|
|
9c97b6cca0 | ||
|
|
98a6ae7baa | ||
|
|
8f28244cab | ||
|
|
8dd6b3d2b7 | ||
|
|
ee5efb4e36 | ||
|
|
26e67ee06c | ||
|
|
f05ef4e164 | ||
|
|
f39cf60559 | ||
|
|
7b26fe1dc8 | ||
|
|
81b7c820bf | ||
|
|
a7cba43e12 | ||
|
|
9e077ffe59 | ||
|
|
2667d3935d | ||
|
|
9148a1d942 | ||
|
|
7859c93931 | ||
|
|
55573cbe4e | ||
|
|
0c68ab8e06 | ||
|
|
18ce336b68 | ||
|
|
ada0a17521 | ||
|
|
94a8232cdc | ||
|
|
e2127ad549 | ||
|
|
ff384ebb03 | ||
|
|
08941bcbe3 | ||
|
|
89301ba3ca | ||
|
|
24195b4502 | ||
|
|
979b4ae736 | ||
|
|
f37a91e99c | ||
|
|
1600263109 | ||
|
|
2ffcea6578 | ||
|
|
1a982e0671 | ||
|
|
fa3642d1b8 | ||
|
|
4a40a3e863 | ||
|
|
d9cd325a92 | ||
|
|
20f2d74e20 | ||
|
|
50097f912c | ||
|
|
25d58bbe16 | ||
|
|
4d789d01ed | ||
|
|
75dae20268 | ||
|
|
b9c3501d37 | ||
|
|
8099e80b5c | ||
|
|
bcf08e1e84 | ||
|
|
ee9347075e | ||
|
|
ec845d006c | ||
|
|
ea8be0b691 | ||
|
|
c9154db2b3 | ||
|
|
0e4dff7f58 | ||
|
|
d2b028b98f | ||
|
|
a1d7a34927 | ||
|
|
3287622a0b | ||
|
|
234d66d56b | ||
|
|
10a0103662 | ||
|
|
e6bd619d6b | ||
|
|
6d83ac94c9 | ||
|
|
c8a53bc4bd | ||
|
|
a88d4e9904 | ||
|
|
e5d0c8fd70 | ||
|
|
8828890cc8 | ||
|
|
a71babd667 | ||
|
|
73ca8408af | ||
|
|
bbc4aea7e9 | ||
|
|
3297173552 | ||
|
|
f104863a21 | ||
|
|
8b17dea125 | ||
|
|
7cad5815c1 | ||
|
|
5da7d11d8b | ||
|
|
a597ba69d7 | ||
|
|
7762b23d0a | ||
|
|
c2f2f0a769 | ||
|
|
ccb0feb4a4 | ||
|
|
b877eee42a | ||
|
|
b2b8407f50 | ||
|
|
d19734afbd | ||
|
|
7756b62a35 | ||
|
|
58a304ebda | ||
|
|
a3d9280b26 | ||
|
|
e74104e4de | ||
|
|
6c687ff50c | ||
|
|
b356865de2 | ||
|
|
81366c926c | ||
|
|
d4a6ac6507 | ||
|
|
4c0e29057c | ||
|
|
6a6a59f612 | ||
|
|
ac9e93485c | ||
|
|
7809f1b5a1 | ||
|
|
e08a9971fb | ||
|
|
6ab84c49ea | ||
|
|
0e6e19c2cf | ||
|
|
0c4277cf73 | ||
|
|
0563913e28 | ||
|
|
858f7f55e0 | ||
|
|
0634911d3e | ||
|
|
6a19948453 | ||
|
|
96e5ac90ba | ||
|
|
309aa9e0d2 | ||
|
|
5c7c3afce2 | ||
|
|
05ce415114 | ||
|
|
e0c66e5907 | ||
|
|
ede8395c35 | ||
|
|
65b257a6b2 | ||
|
|
48135213d6 | ||
|
|
31a05638bf | ||
|
|
ed39966f65 | ||
|
|
d800b55e13 | ||
|
|
3876f77f1f | ||
|
|
d7891e73c0 | ||
|
|
1fc011fa6d | ||
|
|
5f0bfc5770 | ||
|
|
931fc6d919 | ||
|
|
19c69d6f70 | ||
|
|
3ca23bb830 | ||
|
|
9172fc12db | ||
|
|
8e0c0de7ba | ||
|
|
3546747222 | ||
|
|
15a52d57d8 | ||
|
|
cb8566ed91 | ||
|
|
1b16d8fbca | ||
|
|
b942c04bbe | ||
|
|
ab718835c0 | ||
|
|
4fc94b5310 | ||
|
|
95c5e19748 | ||
|
|
491b582a48 | ||
|
|
3d02a0e788 | ||
|
|
d2d8908df3 | ||
|
|
8f2e581db3 | ||
|
|
88bbd78f1c | ||
|
|
731b7c0d41 | ||
|
|
fb32d4fb3d | ||
|
|
e9637d1780 | ||
|
|
4a0fd1525e | ||
|
|
72b58b0ad7 | ||
|
|
d36cf7858f | ||
|
|
f162e36168 | ||
|
|
02402b2860 | ||
|
|
ad79a151ff | ||
|
|
9afefcdf52 | ||
|
|
d4b7c3ca11 | ||
|
|
e974f12ddb | ||
|
|
b836e0ca6b | ||
|
|
6543546dc0 | ||
|
|
addb6529ce | ||
|
|
d57eeb99b0 | ||
|
|
6a12b440bf | ||
|
|
7b2633066f | ||
|
|
3115e2cfa1 | ||
|
|
01a0e88422 | ||
|
|
a588dd33e8 | ||
|
|
38ada18a0e | ||
|
|
4e49041d72 | ||
|
|
a3235c4029 | ||
|
|
95b1bf73f9 | ||
|
|
2d5818daa0 | ||
|
|
ea2f3933da | ||
|
|
7ac7fcf8b5 | ||
|
|
15f839e349 | ||
|
|
8e2c36a109 | ||
|
|
fc0303dc3f | ||
|
|
092605c07e | ||
|
|
1caab5cd44 | ||
|
|
c1c04e5b14 | ||
|
|
2056e9be48 | ||
|
|
641fdaa958 | ||
|
|
d09e3b2647 | ||
|
|
5a3dcb824b | ||
|
|
6a2e44da9c | ||
|
|
1983f3deb7 | ||
|
|
fc564d228c | ||
|
|
c04b7e9425 | ||
|
|
2a2b7922d6 | ||
|
|
bc9e58e420 | ||
|
|
7bc3b5e886 | ||
|
|
9626adb81a | ||
|
|
2730cab765 | ||
|
|
673ab658f9 | ||
|
|
d04c3b366e | ||
|
|
d0facf7fd9 | ||
|
|
2dfda4db70 | ||
|
|
1a9c191e1f | ||
|
|
8a3bb90e67 | ||
|
|
5f9d9ca11c | ||
|
|
ffd2101e02 | ||
|
|
923f5b6051 | ||
|
|
f194891195 | ||
|
|
8b1edd63bb | ||
|
|
b3f3f40df3 | ||
|
|
9cebfc6611 | ||
|
|
91c805ff02 | ||
|
|
bc40d5954b | ||
|
|
b15032407a | ||
|
|
65c10cd5e4 | ||
|
|
898964a6b7 | ||
|
|
8a76a96dd7 | ||
|
|
6665a69afb | ||
|
|
30ea12a346 | ||
|
|
d2763f0d56 | ||
|
|
05e6ad9369 | ||
|
|
a3c1a26fd7 | ||
|
|
d189391502 | ||
|
|
23d3d45547 | ||
|
|
3a497d4698 | ||
|
|
46c7045d79 | ||
|
|
6708ff1b73 | ||
|
|
a7f602fb8f | ||
|
|
94527c2abf | ||
|
|
ad857cea1b | ||
|
|
781a550b3c | ||
|
|
c27716f35a | ||
|
|
00828a3b8f | ||
|
|
845e0a880b | ||
|
|
7e89d0b709 | ||
|
|
d936e5b936 | ||
|
|
acea5fb524 | ||
|
|
a7c648d9ac | ||
|
|
b381b41c65 | ||
|
|
2d25fda0fa | ||
|
|
eb2fa948b5 | ||
|
|
6f27e7b292 | ||
|
|
586f1fb5ab | ||
|
|
767b9d400a | ||
|
|
0a91022bba | ||
|
|
3f8f7a186a | ||
|
|
2927da6955 | ||
|
|
376424035e | ||
|
|
b748dfa9f5 | ||
|
|
f09837f7f7 | ||
|
|
1180bc4798 | ||
|
|
41574d6537 | ||
|
|
5bd2da8d65 | ||
|
|
b9fc33f98c | ||
|
|
0a1c1fe415 | ||
|
|
f164e703ea | ||
|
|
58adbfe9de | ||
|
|
c029b16d34 | ||
|
|
c5a6f654db | ||
|
|
dd6f7dcad6 | ||
|
|
19961d1493 | ||
|
|
9decf3a4aa | ||
|
|
8514657bf5 | ||
|
|
f6828172d5 | ||
|
|
fd5742e3f0 | ||
|
|
a2ea1dd6b5 | ||
|
|
d37f35258e | ||
|
|
7ec5ea089e | ||
|
|
c4791180f9 | ||
|
|
074ba95836 | ||
|
|
0a8127c6a7 | ||
|
|
a1ba70e6a8 | ||
|
|
4cf36c89ba | ||
|
|
c927ccb89b | ||
|
|
a6687d00a2 | ||
|
|
a61f3864f0 | ||
|
|
e8f7bc8e45 | ||
|
|
97e5bae5e8 | ||
|
|
4da7b73965 | ||
|
|
01bf7d6e9e | ||
|
|
0c06ad86fd | ||
|
|
06d0539dca | ||
|
|
e471c02f49 | ||
|
|
035aa608f8 | ||
|
|
25452671fb | ||
|
|
ffa13d0e65 | ||
|
|
0d1399cca2 | ||
|
|
9fb0d69cde | ||
|
|
2251d41f2c | ||
|
|
1e743b7c52 | ||
|
|
34c395cc2d | ||
|
|
c10d72c877 | ||
|
|
f2f12cfdc6 | ||
|
|
886cfc1122 | ||
|
|
4c59e8105d | ||
|
|
603cb4cb28 | ||
|
|
c09baf7d95 | ||
|
|
f370b414d5 | ||
|
|
b806829091 | ||
|
|
a8fde54f20 | ||
|
|
bc22e8a464 | ||
|
|
b55bad2778 | ||
|
|
f4f4df493c | ||
|
|
19725ed3f4 | ||
|
|
08139ab379 | ||
|
|
36eebb4992 | ||
|
|
ef97a8f73d | ||
|
|
32f254b50f | ||
|
|
86c9b0bd37 | ||
|
|
7325eec877 | ||
|
|
d5dfb6c52e | ||
|
|
1cfcb66003 | ||
|
|
f37cda5337 | ||
|
|
69f42498a3 | ||
|
|
0cab2cbcf0 | ||
|
|
5af52493f5 | ||
|
|
f40fb18204 | ||
|
|
2e4d1c58d4 | ||
|
|
e4fb6187a4 | ||
|
|
873bfe9ac4 | ||
|
|
dccd58ebe7 | ||
|
|
746f022a43 | ||
|
|
5e4df7625a | ||
|
|
d407199412 | ||
|
|
06ee1a8c4b | ||
|
|
fb802bab76 | ||
|
|
dbe41d23f9 | ||
|
|
795d2339bd | ||
|
|
1c79ab091b | ||
|
|
0ab9469674 | ||
|
|
27928a3fed | ||
|
|
fac3033bc6 | ||
|
|
93abad38db | ||
|
|
890b865cce | ||
|
|
0cf9feb7f7 | ||
|
|
74e66f6aec | ||
|
|
69c77420e3 | ||
|
|
42a726f5de | ||
|
|
f067ee01c5 | ||
|
|
60ee23b290 | ||
|
|
0a6297b21e | ||
|
|
1f600856ed | ||
|
|
71b01350b7 | ||
|
|
1938b66414 | ||
|
|
f33a43f28a | ||
|
|
95b349f68c | ||
|
|
bbfc1bef1e | ||
|
|
eeff4bef88 | ||
|
|
afaae976be | ||
|
|
5e902dd313 | ||
|
|
c213bab266 | ||
|
|
6de33e89ea | ||
|
|
2b72a7617c | ||
|
|
227d366140 | ||
|
|
fcf59cc559 | ||
|
|
f7e74f190b | ||
|
|
4112e9319a | ||
|
|
94123f82cb | ||
|
|
f8aeeda21c | ||
|
|
fd9f21fad6 | ||
|
|
f002728b9b | ||
|
|
6bd70ae4a5 | ||
|
|
a06bb99cce | ||
|
|
3ee2ab0db4 | ||
|
|
87e1b0d2e5 | ||
|
|
8483beb771 | ||
|
|
1f9a109c30 | ||
|
|
dab06bc0a3 | ||
|
|
6a898e4d32 | ||
|
|
4d51451bfb |
+15
-1
@@ -145,6 +145,14 @@ examples/petsc/velocity.*
|
||||
examples/petsc/elastic_energy.*
|
||||
examples/petsc/mode_*
|
||||
|
||||
examples/arpack/ex11
|
||||
examples/arpack/mode_*
|
||||
examples/arpack/ex11.mesh
|
||||
|
||||
examples/spectra/ex11
|
||||
examples/spectra/mode_*
|
||||
examples/spectra/ex11.mesh
|
||||
|
||||
examples/pumi/ex1
|
||||
examples/pumi/ex[126]p
|
||||
examples/pumi/refined.mesh
|
||||
@@ -310,7 +318,13 @@ tests/convergence/prates
|
||||
tests/par-mesh-format/ex1p
|
||||
|
||||
# VPATH builds
|
||||
build-*/*
|
||||
build-*/
|
||||
|
||||
# User config
|
||||
user-*
|
||||

|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
# PETSc automated build
|
||||
petsc-build/*
|
||||
|
||||
@@ -50,6 +50,7 @@ variables:
|
||||
AUTOTEST_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/autotest.git
|
||||
MFEM_DATA_REPO: https://github.com/mfem/data.git
|
||||
ARTIFACTS_DIR: artifacts
|
||||
SLURM_OVERLAP: 1
|
||||
|
||||
# The pipeline is divided into stages. Usually, jobs in a given stage wait for
|
||||
# the preceding stages to complete before to start. However, we sometimes use
|
||||
|
||||
+1
-1
@@ -30,5 +30,5 @@
|
||||
|
||||
opt_mpi_cuda_xl_16_1_1_8:
|
||||
variables:
|
||||
SPEC: "%xl@16.1.1.8 +mpi +cuda cuda_arch=sm_70"
|
||||
SPEC: "%xl@16.1.1.8 +mpi +cuda cuda_arch=70"
|
||||
extends: .build_and_test_on_lassen
|
||||
|
||||
@@ -10,7 +10,26 @@
|
||||
|
||||
Version 4.3.1 (development)
|
||||
===========================
|
||||
- Added support for hr-adaptivity using TMOP-based error estimator.
|
||||
|
||||
- Adding lowest order Nedelec and Raviart-Thomas basis functions on wedge
|
||||
shaped elements.
|
||||
|
||||
- Added initial support for meshes with pyramidal elements, including several
|
||||
pyramidal meshes in the data/ directory and support for the lowest order H1,
|
||||
Nedelec, Raviart-Thomas, and L2 basis functions on pyramids.
|
||||
|
||||
- Updated the hypre interface according to changes in hypre-2.22.1. The ADS
|
||||
solver is now fully working on GPUs.
|
||||
|
||||
- Tetrahedral meshes no longer need to be reordered to support high order
|
||||
Nedelec basis functions. This will allow future support for Nedelec basis
|
||||
functions on wedges and pyramids which are not amenable to reordering. The
|
||||
ReorientTetMesh method of the Mesh and ParMesh classes has been deprecated.
|
||||
|
||||
- Gmsh meshes where all elements have zero physical tag (the default Gmsh
|
||||
output format if no physical groups are defined) are now successfully loaded,
|
||||
and elements are reassigned attribute number 1.
|
||||
|
||||
Version 4.3, released on July 29, 2021
|
||||
======================================
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
cff-version: 1.2.0
|
||||
message: "If you use MFEM, please cite it as follows."
|
||||
authors:
|
||||
- family-names: "MFEM Team"
|
||||
title: "MFEM: Modular Finite Element Methods [Software]"
|
||||
doi: 10.11578/dc.20171025.1248
|
||||
url: "https://mfem.org"
|
||||
preferred-citation:
|
||||
type: article
|
||||
authors:
|
||||
- family-names: "Anderson"
|
||||
given-names: "Robert"
|
||||
orcid: "https://orcid.org/0000-0002-3508-9944"
|
||||
- family-names: "Andrej"
|
||||
given-names: "Julian"
|
||||
orcid: "https://orcid.org/0000-0001-7661-4840"
|
||||
- family-names: "Barker"
|
||||
given-names: "Andrew"
|
||||
orcid: "https://orcid.org/0000-0003-3572-911X"
|
||||
- family-names: "Bramwell"
|
||||
given-names: "Jamie"
|
||||
- family-names: "Camier"
|
||||
given-names: "Jean-Sylvain"
|
||||
orcid: "https://orcid.org/0000-0003-2421-1999"
|
||||
- family-names: "Cerveny"
|
||||
given-names: "Jakub"
|
||||
orcid: "https://orcid.org/0000-0003-4231-2531"
|
||||
- family-names: "Dobrev"
|
||||
given-names: "Veselin"
|
||||
orcid: "https://orcid.org/0000-0003-1793-5622"
|
||||
- family-names: "Dudouit"
|
||||
given-names: "Yohann"
|
||||
orcid: "https://orcid.org/0000-0001-5831-561X"
|
||||
- family-names: "Fisher"
|
||||
given-names: "Aaron"
|
||||
- family-names: "Kolev"
|
||||
given-names: "Tzanio"
|
||||
orcid: "https://orcid.org/0000-0002-2810-3090"
|
||||
- family-names: "Pazner"
|
||||
given-names: "Will"
|
||||
orcid: "https://orcid.org/0000-0003-4885-2934"
|
||||
- family-names: "Stowell"
|
||||
given-names: "Mark"
|
||||
orcid: "https://orcid.org/0000-0002-5389-7435"
|
||||
- family-names: "Tomov"
|
||||
given-names: "Vladimir"
|
||||
orcid: "https://orcid.org/0000-0002-1846-6816"
|
||||
- family-names: "Akkerman"
|
||||
given-names: "Ido"
|
||||
orcid: "https://orcid.org/0000-0002-5937-0300"
|
||||
- family-names: "Dahm"
|
||||
given-names: "Johann"
|
||||
orcid: "https://orcid.org/0000-0001-9657-3564"
|
||||
- family-names: "Medina"
|
||||
given-names: "David"
|
||||
- family-names: "Zampini"
|
||||
given-names: "Stefano"
|
||||
orcid: "https://orcid.org/0000-0002-0435-0433"
|
||||
doi: "10.1016/j.camwa.2020.06.009"
|
||||
journal: "Computers \\& Mathematics with Applications"
|
||||
month: 1
|
||||
start: 42 # First page number
|
||||
end: 74 # Last page number
|
||||
title: "MFEM: A Modular Finite Element Methods Library"
|
||||
volume: 81
|
||||
year: 2021
|
||||
@@ -549,7 +549,7 @@ The specific libraries and their options are:
|
||||
Options: HYPRE_OPT, HYPRE_LIB.
|
||||
Versions: HYPRE >= 2.10.0b (HYPRE built without CUDA)
|
||||
HYPRE >= 2.20.0 (HYPRE built with '--enable-mixedint')
|
||||
HYPRE >= 2.22.0 (HYPRE built with CUDA)
|
||||
HYPRE >= 2.22.1 (HYPRE built with CUDA)
|
||||
|
||||
- METIS, used when MFEM_USE_METIS = YES. If using METIS 5, set
|
||||
MFEM_USE_METIS_5 = YES (default is to use METIS 4).
|
||||
|
||||
@@ -91,6 +91,12 @@
|
||||
// Enable MFEM functionality based on the SuiteSparse library.
|
||||
// #define MFEM_USE_SUITESPARSE
|
||||
|
||||
// Enable MFEM functionality based on the ARPACK library.
|
||||
// #define MFEM_USE_ARPACK
|
||||
|
||||
// Enable MFEM functionality based on the SPECTRA library.
|
||||
// #define MFEM_USE_SPECTRA
|
||||
|
||||
// Enable MFEM functionality based on the SuperLU library.
|
||||
// #define MFEM_USE_SUPERLU
|
||||
// #define MFEM_USE_SUPERLU5
|
||||
|
||||
@@ -31,6 +31,8 @@ MFEM_TIMER_TYPE = @MFEM_TIMER_TYPE@
|
||||
MFEM_USE_SUNDIALS = @MFEM_USE_SUNDIALS@
|
||||
MFEM_USE_MESQUITE = @MFEM_USE_MESQUITE@
|
||||
MFEM_USE_SUITESPARSE = @MFEM_USE_SUITESPARSE@
|
||||
MFEM_USE_ARPACK = @MFEM_USE_ARPACK@
|
||||
MFEM_USE_SPECTRA = @MFEM_USE_SPECTRA@
|
||||
MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
|
||||
MFEM_USE_SUPERLU5 = @MFEM_USE_SUPERLU5@
|
||||
MFEM_USE_MUMPS = @MFEM_USE_MUMPS@
|
||||
|
||||
@@ -151,6 +151,8 @@ MFEM_USE_UMPIRE = NO
|
||||
MFEM_USE_SIMD = NO
|
||||
MFEM_USE_ADIOS2 = NO
|
||||
MFEM_USE_MKL_CPARDISO = NO
|
||||
MFEM_USE_ARPACK = NO
|
||||
MFEM_USE_SPECTRA = NO
|
||||
|
||||
# MPI library compile and link flags
|
||||
# These settings are used only when building MFEM with MPI + HIP
|
||||
@@ -328,6 +330,19 @@ 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)
|
||||
|
||||
# ARPACK library configuration
|
||||
ARPACK_DIR = @MFEM_DIR@/../ARPACK
|
||||
ARPACK_OPT = -I$(ARPACK_DIR)
|
||||
ARPACK_LIB = -L$(ARPACK_DIR) -lparpack -larpack
|
||||
|
||||
# EIGEN library configuration
|
||||
EIGEN_DIR = @MFEM_DIR@/../eigen
|
||||
EIGEN_OPT = -I$(EIGEN_DIR)
|
||||
|
||||
# SPECTRA library configuration
|
||||
SPECTRA_DIR = @MFEM_DIR@/../spectra/include
|
||||
SPECTRA_OPT = -I$(SPECTRA_DIR) $(EIGEN_OPT)
|
||||
|
||||
# PETSc library configuration (version greater or equal to 3.8 or the dev branch)
|
||||
PETSC_ARCH := arch-linux2-c-debug
|
||||
PETSC_DIR := $(MFEM_DIR)/../petsc/$(PETSC_ARCH)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
MFEM INLINE mesh v1.0
|
||||
|
||||
type = pyramid
|
||||
nx = 4
|
||||
ny = 4
|
||||
nz = 4
|
||||
sx = 1.0
|
||||
sy = 1.0
|
||||
sz = 1.0
|
||||
@@ -0,0 +1,43 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
# PYRAMID = 7
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
2
|
||||
1 7 4 3 2 1 0
|
||||
1 7 1 2 3 4 5
|
||||
|
||||
boundary
|
||||
8
|
||||
1 2 0 2 1
|
||||
2 2 0 3 2
|
||||
3 2 0 4 3
|
||||
4 2 0 1 4
|
||||
5 2 1 2 5
|
||||
6 2 2 3 5
|
||||
7 2 3 4 5
|
||||
8 2 4 1 5
|
||||
|
||||
vertices
|
||||
6
|
||||
3
|
||||
0 0 -1
|
||||
1 0 0
|
||||
0 1 0
|
||||
-1 0 0
|
||||
0 -1 0
|
||||
0 0 1
|
||||
@@ -0,0 +1,38 @@
|
||||
MFEM mesh v1.0
|
||||
|
||||
#
|
||||
# MFEM Geometry Types (see mesh/geom.hpp):
|
||||
#
|
||||
# POINT = 0
|
||||
# SEGMENT = 1
|
||||
# TRIANGLE = 2
|
||||
# SQUARE = 3
|
||||
# TETRAHEDRON = 4
|
||||
# CUBE = 5
|
||||
# PRISM = 6
|
||||
# PYRAMID = 7
|
||||
#
|
||||
|
||||
dimension
|
||||
3
|
||||
|
||||
elements
|
||||
1
|
||||
1 7 0 1 2 3 4
|
||||
|
||||
boundary
|
||||
5
|
||||
1 3 3 2 1 0
|
||||
2 2 0 1 4
|
||||
3 2 1 2 4
|
||||
4 2 2 3 4
|
||||
5 2 3 0 4
|
||||
|
||||
vertices
|
||||
5
|
||||
3
|
||||
0 0 0
|
||||
1 0 0
|
||||
1 1 0
|
||||
0 1 0
|
||||
0 0 1
|
||||
@@ -0,0 +1,47 @@
|
||||
Mesh.Algorithm = 6;
|
||||
|
||||
lc = 0.1;
|
||||
Point(1) = {0.0,0.0,0.0,lc};
|
||||
Point(2) = {1,0.0,0.0,lc};
|
||||
Point(3) = {0,1,0.0,lc};
|
||||
Circle(1) = {2,1,3};
|
||||
Point(4) = {-1,0,0.0,lc};
|
||||
Point(5) = {0,-1,0.0,lc};
|
||||
Circle(2) = {3,1,4};
|
||||
Circle(3) = {4,1,5};
|
||||
Circle(4) = {5,1,2};
|
||||
Point(6) = {0,0,-1,lc};
|
||||
Point(7) = {0,0,1,lc};
|
||||
Circle(5) = {3,1,6};
|
||||
Circle(6) = {6,1,5};
|
||||
Circle(7) = {5,1,7};
|
||||
Circle(8) = {7,1,3};
|
||||
Circle(9) = {2,1,7};
|
||||
Circle(10) = {7,1,4};
|
||||
Circle(11) = {4,1,6};
|
||||
Circle(12) = {6,1,2};
|
||||
Curve Loop(13) = {2,8,-10};
|
||||
Surface(14) = {13};
|
||||
Curve Loop(15) = {10,3,7};
|
||||
Surface(16) = {15};
|
||||
Curve Loop(17) = {-8,-9,1};
|
||||
Surface(18) = {17};
|
||||
Curve Loop(19) = {-11,-2,5};
|
||||
Surface(20) = {19};
|
||||
Curve Loop(21) = {-5,-12,-1};
|
||||
Surface(22) = {21};
|
||||
Curve Loop(23) = {-3,11,6};
|
||||
Surface(24) = {23};
|
||||
Curve Loop(25) = {-7,4,9};
|
||||
Surface(26) = {25};
|
||||
Curve Loop(27) = {-4,12,-6};
|
||||
Surface(28) = {27};
|
||||
Surface Loop(29) = {28,26,16,14,20,24,22,18};
|
||||
Volume(30) = {29};
|
||||
|
||||
Physical Surface(1) = {28,26,16,14,20,24,22,18};
|
||||
Physical Volume(2) = 30;
|
||||
|
||||
// Generate 2D mesh
|
||||
Mesh 2;
|
||||
Mesh.MshFileVersion = 2.2;
|
||||
+4793
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,286 @@
|
||||
// MFEM Example 11 - Serial Version
|
||||
//
|
||||
// Compile with: make ex11
|
||||
//
|
||||
// Sample runs: ex11 -m ../data/square-disc.mesh
|
||||
// ex11 -m ../data/star.mesh
|
||||
// ex11 -m ../data/star-mixed.mesh
|
||||
// ex11 -m ../data/periodic-annulus-sector.msh
|
||||
// ex11 -m ../data/square-disc-p2.vtk -o 2
|
||||
// ex11 -m ../data/square-disc-p3.mesh -o 3
|
||||
// ex11 -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// ex11 -m ../data/disc-nurbs.mesh -o -1 -n 20
|
||||
// ex11 -m ../data/star-surf.mesh
|
||||
// ex11 -m ../data/square-disc-surf.mesh
|
||||
// ex11 -m ../data/inline-segment.mesh
|
||||
// ex11 -m ../data/inline-quad.mesh
|
||||
// ex11 -m ../data/inline-tri.mesh
|
||||
// ex11 -m ../data/amr-quad.mesh
|
||||
// ex11 -m ../data/amr-hex.mesh
|
||||
// ex11 -m ../data/mobius-strip.mesh -n 8
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to solve the
|
||||
// eigenvalue problem -Delta u = lambda u with homogeneous
|
||||
// Dirichlet boundary conditions.
|
||||
//
|
||||
// We compute a number of the lowest eigenmodes by discretizing
|
||||
// the Laplacian and Mass operators using a FE space of the
|
||||
// specified order, or an isoparametric/isogeometric space if
|
||||
// order < 1 (quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of the ARPACK eigenvalue solver
|
||||
// (regular inverse mode). Reusing a single GLVis visualization
|
||||
// window for multiple eigenfunctions is also illustrated.
|
||||
//
|
||||
// We recommend viewing Example 1 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
int ser_ref_levels = 3;
|
||||
int order = 1;
|
||||
int nev = 5;
|
||||
double dbc_eig = 1e3;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&nev, "-n", "--num-eigs",
|
||||
"Number of desired eigenmodes.");
|
||||
args.AddOption(&dbc_eig, "-d", "--dbc-eig",
|
||||
"Eigenvalues associated with Dirichlet BC "
|
||||
"(should be larger than the maximum desired eigenvalue).");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh;
|
||||
ifstream imesh(mesh_file);
|
||||
if (!imesh)
|
||||
{
|
||||
cerr << "\nCan not open mesh file: " << mesh_file << '\n' << endl;
|
||||
return 2;
|
||||
}
|
||||
mesh = new Mesh(imesh, 1, 1);
|
||||
imesh.close();
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 3. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement (2 by default, or
|
||||
// specified on the command line with -rs).
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 4. Define a finite element space on the mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else if (mesh->GetNodes())
|
||||
{
|
||||
fec = mesh->GetNodes()->OwnFEC();
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
}
|
||||
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
||||
int size = fespace->GetVSize();
|
||||
|
||||
cout << "Number of unknowns: " << size << endl;
|
||||
|
||||
// 5. Set up the parallel bilinear forms a(.,.) and m(.,.) on the finite
|
||||
// element space. The first corresponds to the Laplacian operator -Delta,
|
||||
// while the second is a simple mass matrix needed on the right hand side
|
||||
// of the generalized eigenvalue problem below. The boundary conditions
|
||||
// are implemented by elimination with special values on the diagonal to
|
||||
// shift the Dirichlet eigenvalues out of the computational range. After
|
||||
// serial and parallel assembly we extract the corresponding parallel
|
||||
// matrices A and M.
|
||||
ConstantCoefficient one(1.0);
|
||||
Array<int> ess_bdr;
|
||||
if (mesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
}
|
||||
|
||||
BilinearForm *a = new BilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
if (mesh->bdr_attributes.Size() == 0)
|
||||
{
|
||||
// Add a mass term if the mesh has no boundary, e.g. periodic mesh or
|
||||
// closed surface.
|
||||
a->AddDomainIntegrator(new MassIntegrator(one));
|
||||
}
|
||||
a->Assemble();
|
||||
if (mesh->bdr_attributes.Size() != 0)
|
||||
{
|
||||
a->EliminateEssentialBCDiag(ess_bdr, dbc_eig);
|
||||
}
|
||||
a->Finalize();
|
||||
|
||||
BilinearForm *m = new BilinearForm(fespace);
|
||||
m->AddDomainIntegrator(new MassIntegrator(one));
|
||||
m->Assemble();
|
||||
if (mesh->bdr_attributes.Size() != 0)
|
||||
{
|
||||
// shift the eigenvalue corresponding to eliminated dofs to a large value
|
||||
m->EliminateEssentialBCDiag(ess_bdr, 1.0);
|
||||
}
|
||||
m->Finalize();
|
||||
|
||||
// 6. Define and configure the ARPACK eigensolver
|
||||
ArPackSym * arpack = new ArPackSym();
|
||||
Solver * solver = NULL;
|
||||
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// 7. Define a simple symmetric Gauss-Seidel preconditioner and use it to
|
||||
// solve the system A X = B with PCG.
|
||||
cout << "Building CGSolver" << endl;
|
||||
GSSmoother M(m->SpMat());
|
||||
CGSolver * cg_solver = new CGSolver;
|
||||
cg_solver->SetPreconditioner(M);
|
||||
cg_solver->SetRelTol(1.0e-12);
|
||||
solver = cg_solver;
|
||||
#else
|
||||
// 7. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
cout << "Building UMFPackSolver" << endl;
|
||||
UMFPackSolver * umf_solver = new UMFPackSolver;
|
||||
umf_solver->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
solver = umf_solver;
|
||||
#endif
|
||||
solver->SetOperator(m->SpMat());
|
||||
|
||||
arpack->SetNumModes(nev);
|
||||
arpack->SetMaxIter(400);
|
||||
arpack->SetTol(1e-8);
|
||||
arpack->SetMode(2);
|
||||
arpack->SetPrintLevel(2);
|
||||
|
||||
arpack->SetOperator(*a);
|
||||
arpack->SetMassMatrix(*m);
|
||||
arpack->SetSolver(*solver);
|
||||
|
||||
// 8. Compute the eigenmodes and extract the array of eigenvalues. Define a
|
||||
// parallel grid function to represent each of the eigenmodes returned by
|
||||
// the solver.
|
||||
Array<double> eigenvalues;
|
||||
arpack->Solve();
|
||||
arpack->GetEigenvalues(eigenvalues);
|
||||
|
||||
cout << endl;
|
||||
std::ios::fmtflags old_fmt = cout.flags();
|
||||
cout.setf(std::ios::scientific);
|
||||
std::streamsize old_prec = cout.precision(14);
|
||||
for (int i=0; i<nev; i++)
|
||||
{
|
||||
cout << "Eigenvalue lambda " << eigenvalues[i] << endl;
|
||||
}
|
||||
cout.precision(old_prec);
|
||||
cout.flags(old_fmt);
|
||||
cout << endl;
|
||||
|
||||
GridFunction x(fespace);
|
||||
|
||||
// 9. Save the refined mesh and the modes in parallel. This output can be
|
||||
// viewed later using GLVis: "glvis -np <np> -m mesh -g mode".
|
||||
{
|
||||
ostringstream mesh_name, mode_name;
|
||||
mesh_name << "ex11.mesh";
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
mesh->Print(mesh_ofs);
|
||||
|
||||
for (int i=0; i<nev; i++)
|
||||
{
|
||||
// convert eigenvector from HypreParVector to ParGridFunction
|
||||
x = arpack->GetEigenvector(i);
|
||||
|
||||
mode_name << "mode_" << setfill('0') << setw(2) << i;
|
||||
|
||||
ofstream mode_ofs(mode_name.str().c_str());
|
||||
mode_ofs.precision(8);
|
||||
x.Save(mode_ofs);
|
||||
mode_name.str("");
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream mode_sock(vishost, visport);
|
||||
mode_sock.precision(8);
|
||||
|
||||
for (int i=0; i<nev; i++)
|
||||
{
|
||||
cout << "Eigenmode " << i+1 << '/' << nev
|
||||
<< ", Lambda = " << eigenvalues[i] << endl;
|
||||
|
||||
// convert eigenvector from HypreParVector to ParGridFunction
|
||||
x = arpack->GetEigenvector(i);
|
||||
|
||||
mode_sock << "solution\n" << *mesh << x << flush
|
||||
<< "window_title 'Eigenmode " << i+1 << '/' << nev
|
||||
<< ", Lambda = " << eigenvalues[i] << "'" << endl;
|
||||
|
||||
char c;
|
||||
cout << "press (q)uit or (c)ontinue --> " << flush;
|
||||
cin >> c;
|
||||
|
||||
if (c != 'c')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
mode_sock.close();
|
||||
}
|
||||
|
||||
// 11. Free the used memory.
|
||||
delete arpack;
|
||||
delete solver;
|
||||
delete m;
|
||||
delete a;
|
||||
|
||||
delete fespace;
|
||||
if (order > 0)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
# Copyright (c) 2010-2021, 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.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/arpack/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = ex11
|
||||
PAR_EXAMPLES =
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES)
|
||||
endif
|
||||
RC_FILES = $(patsubst $(SRC)%,%,$(wildcard $(SRC)rc_*))
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
# Examples depend on their corresponding rc_* files:
|
||||
make-rc-rule = $(1): | $(filter rc_$(1)%,$(RC_FILES))
|
||||
$(foreach ex,$(EXAMPLES),$(eval $(call make-rc-rule,$(ex))))
|
||||
|
||||
# Rules to copy the rc_* files when building out-of-source:
|
||||
ifneq ($(SRC),)
|
||||
$(RC_FILES): %: $(SRC)%
|
||||
cp -pf $(<) .
|
||||
endif
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -rf mesh.* sol.* sol_p.* sol_u.* Example5*
|
||||
@rm -f ex9-mesh.* ex9-init.* ex9-final.* Example9*
|
||||
@rm -f deformed.* velocity.* elastic_energy.*
|
||||
@@ -206,9 +206,9 @@ int main(int argc, char *argv[])
|
||||
cout << "Size of linear system: " << A->Height() << endl;
|
||||
|
||||
// 11. Solve the linear system A X = B.
|
||||
MFEM_PERF_BEGIN("Solve A X=B");
|
||||
if (!pa)
|
||||
{
|
||||
MFEM_PERF_SCOPE("Solve A X=B (FA)");
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
@@ -223,6 +223,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else // Jacobi preconditioning in partial assembly mode
|
||||
{
|
||||
MFEM_PERF_SCOPE("Solve A X=B (PA)");
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
OperatorJacobiSmoother M(a, ess_tdof_list);
|
||||
@@ -233,7 +234,6 @@ int main(int argc, char *argv[])
|
||||
CG(*A, B, X, 1, 400, 1e-12, 0.0);
|
||||
}
|
||||
}
|
||||
MFEM_PERF_END("Solve A X=B");
|
||||
// 12. Recover the solution as a finite element grid function.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
|
||||
+19
-18
@@ -231,28 +231,29 @@ int main(int argc, char *argv[])
|
||||
// 13. Solve the linear system A X = B.
|
||||
// * With full assembly, use the BoomerAMG preconditioner from hypre.
|
||||
// * With partial assembly, use Jacobi smoothing, for now.
|
||||
MFEM_PERF_BEGIN("Solve A X = B");
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
MFEM_PERF_SCOPE("Solve A X=B");
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
MFEM_PERF_END("Solve A X = B");
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
a.RecoverFEMSolution(X, b, x);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
// ex1 -m ../data/fichera.mesh
|
||||
// ex1 -m ../data/fichera-mixed.mesh
|
||||
// ex1 -m ../data/toroid-wedge.mesh
|
||||
// ex1 -m ../data/octahedron.mesh -o 1
|
||||
// ex1 -m ../data/periodic-annulus-sector.msh
|
||||
// ex1 -m ../data/periodic-torus-sector.msh
|
||||
// ex1 -m ../data/square-disc-p2.vtk -o 2
|
||||
|
||||
@@ -118,7 +118,6 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
|
||||
+7
-4
@@ -24,7 +24,10 @@
|
||||
// class ConductionOperator defining C(u)), as well as their
|
||||
// implicit time integration. Note that implementing the method
|
||||
// ConductionOperator::ImplicitSolve is the only requirement for
|
||||
// high-order implicit (SDIRK) time integration.
|
||||
// high-order implicit (SDIRK) time integration. In this example,
|
||||
// the diffusion operator is linearized by evaluating with the
|
||||
// lagged solution from the previous timestep, so there is only
|
||||
// a linear solve.
|
||||
//
|
||||
// We recommend viewing examples 2, 9 and 10 before viewing this
|
||||
// example.
|
||||
@@ -326,8 +329,8 @@ ConductionOperator::ConductionOperator(FiniteElementSpace &f, double al,
|
||||
void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
{
|
||||
// Compute:
|
||||
// du_dt = M^{-1}*-K(u)
|
||||
// for du_dt
|
||||
// du_dt = M^{-1}*-Ku
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg(); // z = -z
|
||||
M_solver.Mult(z, du_dt);
|
||||
@@ -338,7 +341,7 @@ void ConductionOperator::ImplicitSolve(const double dt,
|
||||
{
|
||||
// Solve the equation:
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
|
||||
+8
-5
@@ -24,8 +24,11 @@
|
||||
// class ConductionOperator defining C(u)), as well as their
|
||||
// implicit time integration. Note that implementing the method
|
||||
// ConductionOperator::ImplicitSolve is the only requirement for
|
||||
// high-order implicit (SDIRK) time integration. Optional saving
|
||||
// with ADIOS2 (adios2.readthedocs.io) is also illustrated.
|
||||
// high-order implicit (SDIRK) time integration. In this example,
|
||||
// the diffusion operator is linearized by evaluating with the
|
||||
// lagged solution from the previous timestep, so there is only
|
||||
// a linear solve. Optional saving with ADIOS2
|
||||
// (adios2.readthedocs.io) is also illustrated.
|
||||
//
|
||||
// We recommend viewing examples 2, 9 and 10 before viewing this
|
||||
// example.
|
||||
@@ -420,8 +423,8 @@ ConductionOperator::ConductionOperator(ParFiniteElementSpace &f, double al,
|
||||
void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
|
||||
{
|
||||
// Compute:
|
||||
// du_dt = M^{-1}*-K(u)
|
||||
// for du_dt
|
||||
// du_dt = M^{-1}*-Ku
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
Kmat.Mult(u, z);
|
||||
z.Neg(); // z = -z
|
||||
M_solver.Mult(z, du_dt);
|
||||
@@ -432,7 +435,7 @@ void ConductionOperator::ImplicitSolve(const double dt,
|
||||
{
|
||||
// Solve the equation:
|
||||
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
|
||||
// for du_dt
|
||||
// for du_dt, where K is linearized by using u from the previous timestep
|
||||
if (!T)
|
||||
{
|
||||
T = Add(1.0, Mmat, dt, Kmat);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
// mpirun -np 4 ex1p -m ../data/fichera.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
|
||||
// mpirun -np 4 ex1p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-annulus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/periodic-torus-sector.msh
|
||||
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 1
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 2
|
||||
// ex22 -m ../data/inline-hex.mesh -o 2 -p 2 -pa
|
||||
// ex22 -m ../data/inline-wedge.mesh -o 1
|
||||
// ex22 -m ../data/inline-pyramid.mesh -o 1
|
||||
// ex22 -m ../data/star.mesh -r 1 -o 2 -sigma 10.0
|
||||
//
|
||||
// Device sample runs:
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2 -p 1
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 2 -p 2
|
||||
// mpirun -np 4 ex22p -m ../data/inline-hex.mesh -o 1 -p 2 -pa
|
||||
// mpirun -np 4 ex22p -m ../data/inline-wedge.mesh -o 1
|
||||
// mpirun -np 4 ex22p -m ../data/inline-pyramid.mesh -o 1
|
||||
// mpirun -np 4 ex22p -m ../data/star.mesh -o 2 -sigma 10.0
|
||||
//
|
||||
// Device sample runs:
|
||||
|
||||
@@ -113,7 +113,6 @@ int main(int argc, char *argv[])
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
mesh->ReorientTetMesh();
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use Nedelec or
|
||||
// Raviart-Thomas finite elements of the specified order.
|
||||
|
||||
@@ -141,7 +141,6 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use Nedelec or Raviart-Thomas finite elements of the specified order.
|
||||
|
||||
+2
-4
@@ -277,10 +277,8 @@ int main(int argc, char *argv[])
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 6. Reorient mesh in case of a tet mesh
|
||||
mesh->ReorientTetMesh();
|
||||
|
||||
// Set element attributes in order to distinguish elements in the PML region
|
||||
// 6. Set element attributes in order to distinguish elements in the
|
||||
// PML region
|
||||
pml->SetAttributes(mesh);
|
||||
|
||||
// 7. Define a finite element space on the mesh. Here we use the Nedelec
|
||||
|
||||
@@ -316,9 +316,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
// 7a. Reorient mesh in case of a tet mesh
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 8. Set element attributes in order to distinguish elements in the PML
|
||||
pml->SetAttributes(pmesh);
|
||||
|
||||
|
||||
+2
-1
@@ -16,6 +16,8 @@
|
||||
// ex3 -m ../data/beam-hex-nurbs.mesh
|
||||
// ex3 -m ../data/amr-hex.mesh
|
||||
// ex3 -m ../data/fichera-amr.mesh
|
||||
// ex3 -m ../data/ref-prism.mesh -o 1
|
||||
// ex3 -m ../data/octahedron.mesh -o 1
|
||||
// ex3 -m ../data/star-surf.mesh -o 1
|
||||
// ex3 -m ../data/mobius-strip.mesh -f 0.1
|
||||
// ex3 -m ../data/klein-bottle.mesh -f 0.1
|
||||
@@ -113,7 +115,6 @@ int main(int argc, char *argv[])
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
mesh->ReorientTetMesh();
|
||||
|
||||
// 5. Define a finite element space on the mesh. Here we use the Nedelec
|
||||
// finite elements of the specified order.
|
||||
|
||||
+3
-4
@@ -16,6 +16,8 @@
|
||||
// mpirun -np 4 ex3p -m ../data/beam-hex-nurbs.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/amr-quad.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/amr-hex.mesh
|
||||
// mpirun -np 4 ex3p -m ../data/ref-prism.mesh -o 1
|
||||
// mpirun -np 4 ex3p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex3p -m ../data/star-surf.mesh -o 2
|
||||
// mpirun -np 4 ex3p -m ../data/mobius-strip.mesh -o 2 -f 0.1
|
||||
// mpirun -np 4 ex3p -m ../data/klein-bottle.mesh -o 2 -f 0.1
|
||||
@@ -139,9 +141,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them.
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
@@ -151,7 +151,6 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
// ex4 -m ../data/amr-hex.mesh
|
||||
// ex4 -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
|
||||
// ex4 -m ../data/ref-prism.mesh -o 1
|
||||
// ex4 -m ../data/octahedron.mesh -o 1
|
||||
// ex4 -m ../data/star-surf.mesh -o 1
|
||||
//
|
||||
// Device sample runs:
|
||||
|
||||
+3
-4
@@ -19,6 +19,8 @@
|
||||
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
|
||||
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
|
||||
// mpirun -np 4 ex4p -m ../data/ref-prism.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/octahedron.mesh -o 1
|
||||
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
|
||||
//
|
||||
// Device sample runs:
|
||||
@@ -135,9 +137,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them (this is needed in the ADS solver below).
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
@@ -147,7 +147,6 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 7. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Raviart-Thomas finite elements of the specified order.
|
||||
|
||||
@@ -106,7 +106,6 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 6. Define the trial, interfacial (trace) and test DPG spaces:
|
||||
// - The trial space, x0_space, contains the non-interfacial unknowns and
|
||||
|
||||
@@ -121,9 +121,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them.
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
@@ -133,7 +131,6 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Nedelec finite elements of the specified order.
|
||||
|
||||
@@ -122,9 +122,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
|
||||
// this mesh further in parallel to increase the resolution. Once the
|
||||
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
|
||||
// meshes need to be reoriented before we can define high-order Nedelec
|
||||
// spaces on them (this is needed in the ADS solver below).
|
||||
// parallel mesh is defined, the serial mesh can be deleted.
|
||||
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
|
||||
delete mesh;
|
||||
{
|
||||
@@ -134,7 +132,6 @@ int main(int argc, char *argv[])
|
||||
pmesh->UniformRefinement();
|
||||
}
|
||||
}
|
||||
pmesh->ReorientTetMesh();
|
||||
|
||||
// 6. Define a parallel finite element space on the parallel mesh. Here we
|
||||
// use the Raviart-Thomas finite elements of the specified order.
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
// MFEM Example 11 - Serial Version
|
||||
//
|
||||
// Compile with: make ex11
|
||||
//
|
||||
// Sample runs: ex11 -m ../data/square-disc.mesh
|
||||
// ex11 -m ../data/star.mesh
|
||||
// ex11 -m ../data/star-mixed.mesh
|
||||
// ex11 -m ../data/periodic-annulus-sector.msh
|
||||
// ex11 -m ../data/square-disc-p2.vtk -o 2
|
||||
// ex11 -m ../data/square-disc-p3.mesh -o 3
|
||||
// ex11 -m ../data/square-disc-nurbs.mesh -o -1
|
||||
// ex11 -m ../data/disc-nurbs.mesh -o -1 -n 20
|
||||
// ex11 -m ../data/star-surf.mesh
|
||||
// ex11 -m ../data/square-disc-surf.mesh
|
||||
// ex11 -m ../data/inline-segment.mesh
|
||||
// ex11 -m ../data/inline-quad.mesh
|
||||
// ex11 -m ../data/inline-tri.mesh
|
||||
// ex11 -m ../data/amr-quad.mesh
|
||||
// ex11 -m ../data/amr-hex.mesh
|
||||
// ex11 -m ../data/mobius-strip.mesh -n 8
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to solve the
|
||||
// eigenvalue problem -Delta u = lambda u with homogeneous
|
||||
// Dirichlet boundary conditions.
|
||||
//
|
||||
// We compute a number of the lowest eigenmodes by discretizing
|
||||
// the Laplacian and Mass operators using a FE space of the
|
||||
// specified order, or an isoparametric/isogeometric space if
|
||||
// order < 1 (quadratic for quadratic curvilinear mesh, NURBS for
|
||||
// NURBS mesh, etc.)
|
||||
//
|
||||
// The example highlights the use of the ARPACK eigenvalue solver
|
||||
// (regular inverse mode). Reusing a single GLVis visualization
|
||||
// window for multiple eigenfunctions is also illustrated.
|
||||
//
|
||||
// We recommend viewing Example 1 before viewing this example.
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// 1. Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
int ser_ref_levels = 1;
|
||||
int order = 1;
|
||||
int nev = 5;
|
||||
double dbc_eig = 1e3;
|
||||
bool visualization = 1;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
"Mesh file to use.");
|
||||
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
|
||||
"Number of times to refine the mesh uniformly in serial.");
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Finite element order (polynomial degree) or -1 for"
|
||||
" isoparametric space.");
|
||||
args.AddOption(&nev, "-n", "--num-eigs",
|
||||
"Number of desired eigenmodes.");
|
||||
args.AddOption(&dbc_eig, "-d", "--dbc-eig",
|
||||
"Eigenvalues associated with Dirichlet BC "
|
||||
"(should be larger than the maximum desired eigenvalue).");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
"Enable or disable GLVis visualization.");
|
||||
args.Parse();
|
||||
if (!args.Good())
|
||||
{
|
||||
args.PrintUsage(cout);
|
||||
return 1;
|
||||
}
|
||||
args.PrintOptions(cout);
|
||||
|
||||
// 2. Read the (serial) mesh from the given mesh file on all processors. We
|
||||
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
|
||||
// and volume meshes with the same code.
|
||||
Mesh *mesh;
|
||||
ifstream imesh(mesh_file);
|
||||
if (!imesh)
|
||||
{
|
||||
cerr << "\nCan not open mesh file: " << mesh_file << '\n' << endl;
|
||||
return 2;
|
||||
}
|
||||
mesh = new Mesh(imesh, 1, 1);
|
||||
imesh.close();
|
||||
int dim = mesh->Dimension();
|
||||
|
||||
// 3. Refine the serial mesh on all processors to increase the resolution. In
|
||||
// this example we do 'ref_levels' of uniform refinement (2 by default, or
|
||||
// specified on the command line with -rs).
|
||||
for (int lev = 0; lev < ser_ref_levels; lev++)
|
||||
{
|
||||
mesh->UniformRefinement();
|
||||
}
|
||||
|
||||
// 4. Define a finite element space on the mesh. Here we
|
||||
// use continuous Lagrange finite elements of the specified order. If
|
||||
// order < 1, we instead use an isoparametric/isogeometric space.
|
||||
FiniteElementCollection *fec;
|
||||
if (order > 0)
|
||||
{
|
||||
fec = new H1_FECollection(order, dim);
|
||||
}
|
||||
else if (mesh->GetNodes())
|
||||
{
|
||||
fec = mesh->GetNodes()->OwnFEC();
|
||||
}
|
||||
else
|
||||
{
|
||||
fec = new H1_FECollection(order = 1, dim);
|
||||
}
|
||||
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
|
||||
int size = fespace->GetVSize();
|
||||
|
||||
cout << "Number of unknowns: " << size << endl;
|
||||
|
||||
// 5. Set up the parallel bilinear forms a(.,.) and m(.,.) on the finite
|
||||
// element space. The first corresponds to the Laplacian operator -Delta,
|
||||
// while the second is a simple mass matrix needed on the right hand side
|
||||
// of the generalized eigenvalue problem below. The boundary conditions
|
||||
// are implemented by elimination with special values on the diagonal to
|
||||
// shift the Dirichlet eigenvalues out of the computational range. After
|
||||
// serial and parallel assembly we extract the corresponding parallel
|
||||
// matrices A and M.
|
||||
ConstantCoefficient one(1.0);
|
||||
Array<int> ess_bdr;
|
||||
if (mesh->bdr_attributes.Size())
|
||||
{
|
||||
ess_bdr.SetSize(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
}
|
||||
|
||||
BilinearForm *a = new BilinearForm(fespace);
|
||||
a->AddDomainIntegrator(new DiffusionIntegrator(one));
|
||||
if (mesh->bdr_attributes.Size() == 0)
|
||||
{
|
||||
// Add a mass term if the mesh has no boundary, e.g. periodic mesh or
|
||||
// closed surface.
|
||||
a->AddDomainIntegrator(new MassIntegrator(one));
|
||||
}
|
||||
a->Assemble();
|
||||
if (mesh->bdr_attributes.Size() != 0)
|
||||
{
|
||||
a->EliminateEssentialBCDiag(ess_bdr, dbc_eig);
|
||||
}
|
||||
a->Finalize();
|
||||
|
||||
BilinearForm *m = new BilinearForm(fespace);
|
||||
m->AddDomainIntegrator(new MassIntegrator(one));
|
||||
m->Assemble();
|
||||
if (mesh->bdr_attributes.Size() != 0)
|
||||
{
|
||||
// shift the eigenvalue corresponding to eliminated dofs to a large value
|
||||
m->EliminateEssentialBCDiag(ess_bdr, 1.0);
|
||||
}
|
||||
m->Finalize();
|
||||
|
||||
// 6. Define and configure the SPECTRA eigensolver and solve problem
|
||||
SpectraEigenSolver spectra;
|
||||
|
||||
spectra.SetNumModes(nev)
|
||||
.SetKrylov(10)
|
||||
.SetMaxIter(5000)
|
||||
.SetTol(1e-5)
|
||||
.SetOperators(*a, *m)
|
||||
.Solve();
|
||||
|
||||
Eigen::VectorXd eigenvalues = spectra.GetEigenvalues(nev);
|
||||
|
||||
// 7. Define a grid function to represent each of the eigenmodes returned by the solver.
|
||||
|
||||
GridFunction x(fespace);
|
||||
|
||||
// 8. Save the refined mesh and the modes in parallel.
|
||||
// This output can be viewed later using GLVis: "glvis -np <np> -m mesh -g mode"
|
||||
{
|
||||
ostringstream mesh_name, mode_name;
|
||||
mesh_name << "ex11.mesh";
|
||||
|
||||
ofstream mesh_ofs(mesh_name.str().c_str());
|
||||
mesh_ofs.precision(8);
|
||||
mesh->Print(mesh_ofs);
|
||||
|
||||
for (int i = 0; i < nev; i++) {
|
||||
// conver Eigen Vector to MFEM Vector
|
||||
Vector eigenvector = VectorConverter<double>::from(spectra.GetEigenvector(i));
|
||||
|
||||
// convert eigenvector from Vector to GridFunction
|
||||
x = eigenvector;
|
||||
|
||||
mode_name << "mode_" << setfill('0') << setw(2) << i;
|
||||
|
||||
ofstream mode_ofs(mode_name.str().c_str());
|
||||
mode_ofs.precision(8);
|
||||
x.Save(mode_ofs);
|
||||
mode_name.str("");
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Send the solution by socket to a GLVis server.
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
socketstream mode_sock(vishost, visport);
|
||||
mode_sock.precision(8);
|
||||
|
||||
for (int i=0; i<nev; i++)
|
||||
{
|
||||
cout << "Eigenmode " << i+1 << '/' << nev
|
||||
<< ", Lambda = " << eigenvalues[i] << endl;
|
||||
|
||||
// convert eigenvector from HypreParVector to ParGridFunction
|
||||
Vector eigenvector = VectorConverter<double>::from(spectra.GetEigenvector(i));
|
||||
x = eigenvector;
|
||||
|
||||
mode_sock << "solution\n" << *mesh << x << flush
|
||||
<< "window_title 'Eigenmode " << i+1 << '/' << nev
|
||||
<< ", Lambda = " << eigenvalues[i] << "'" << endl;
|
||||
|
||||
char c;
|
||||
cout << "press (q)uit or (c)ontinue --> " << flush;
|
||||
cin >> c;
|
||||
|
||||
if (c != 'c')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
mode_sock.close();
|
||||
}
|
||||
|
||||
// 10. Free the used memory.
|
||||
delete m;
|
||||
delete a;
|
||||
|
||||
delete fespace;
|
||||
if (order > 0)
|
||||
{
|
||||
delete fec;
|
||||
}
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
# Copyright (c) 2010-2021, 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.
|
||||
|
||||
# Use the MFEM build directory
|
||||
MFEM_DIR ?= ../..
|
||||
MFEM_BUILD_DIR ?= ../..
|
||||
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/spectra/,)
|
||||
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
|
||||
# Use the MFEM install directory
|
||||
# MFEM_INSTALL_DIR = ../../mfem
|
||||
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
|
||||
|
||||
MFEM_LIB_FILE = mfem_is_not_built
|
||||
-include $(CONFIG_MK)
|
||||
|
||||
SEQ_EXAMPLES = ex11
|
||||
PAR_EXAMPLES =
|
||||
ifeq ($(MFEM_USE_MPI),NO)
|
||||
EXAMPLES = $(SEQ_EXAMPLES)
|
||||
else
|
||||
EXAMPLES = $(PAR_EXAMPLES)
|
||||
endif
|
||||
RC_FILES = $(patsubst $(SRC)%,%,$(wildcard $(SRC)rc_*))
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .o .cpp .mk
|
||||
.PHONY: all clean clean-build clean-exec
|
||||
|
||||
# Remove built-in rule
|
||||
%: %.cpp
|
||||
|
||||
# Replace the default implicit rule for *.cpp files
|
||||
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
|
||||
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
|
||||
|
||||
all: $(EXAMPLES)
|
||||
|
||||
# Examples depend on their corresponding rc_* files:
|
||||
make-rc-rule = $(1): | $(filter rc_$(1)%,$(RC_FILES))
|
||||
$(foreach ex,$(EXAMPLES),$(eval $(call make-rc-rule,$(ex))))
|
||||
|
||||
# Rules to copy the rc_* files when building out-of-source:
|
||||
ifneq ($(SRC),)
|
||||
$(RC_FILES): %: $(SRC)%
|
||||
cp -pf $(<) .
|
||||
endif
|
||||
|
||||
# Generate an error message if the MFEM library is not built and exit
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -rf *.mesh mode_*
|
||||
@@ -39,6 +39,7 @@ set(SRCS
|
||||
complex_fem.cpp
|
||||
convergence.cpp
|
||||
datacollection.cpp
|
||||
doftrans.cpp
|
||||
eltrans.cpp
|
||||
estimators.cpp
|
||||
fe.cpp
|
||||
@@ -105,6 +106,7 @@ set(SRCS
|
||||
tmop/tmop_pa_w3.cpp
|
||||
tmop/tmop_pa_w3_c0.cpp
|
||||
tmop_tools.cpp
|
||||
tmop_amr.cpp
|
||||
gslib.cpp
|
||||
transfer.cpp
|
||||
lor.cpp
|
||||
@@ -118,6 +120,7 @@ set(HDRS
|
||||
complex_fem.hpp
|
||||
convergence.hpp
|
||||
datacollection.hpp
|
||||
doftrans.hpp
|
||||
eltrans.hpp
|
||||
estimators.hpp
|
||||
fe.hpp
|
||||
@@ -164,6 +167,7 @@ set(HDRS
|
||||
tmop.hpp
|
||||
tmop/tmop_pa.hpp
|
||||
tmop_tools.hpp
|
||||
tmop_amr.hpp
|
||||
gslib.hpp
|
||||
transfer.hpp
|
||||
lor.hpp
|
||||
|
||||
+58
-24
@@ -391,6 +391,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
}
|
||||
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation * doftrans;
|
||||
Mesh *mesh = fes -> GetMesh();
|
||||
DenseMatrix elmat, *elmat_p;
|
||||
|
||||
@@ -424,7 +425,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
for (int i = 0; i < fes -> GetNE(); i++)
|
||||
{
|
||||
int elem_attr = fes->GetMesh()->GetAttribute(i);
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
if (element_matrices)
|
||||
{
|
||||
elmat_p = &(*element_matrices)(i);
|
||||
@@ -458,6 +459,11 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
{
|
||||
elmat_p = &elmat;
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elmat);
|
||||
}
|
||||
elmat_p = &elmat;
|
||||
}
|
||||
if (static_cond)
|
||||
{
|
||||
@@ -503,7 +509,7 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
const FiniteElement &be = *fes->GetBE(i);
|
||||
fes -> GetBdrElementVDofs (i, vdofs);
|
||||
doftrans = fes -> GetBdrElementVDofs (i, vdofs);
|
||||
eltrans = fes -> GetBdrElementTransformation (i);
|
||||
int k = 0;
|
||||
for (; k < boundary_integs.Size(); k++)
|
||||
@@ -523,17 +529,22 @@ void BilinearForm::Assemble(int skip_zeros)
|
||||
boundary_integs[k]->AssembleElementMatrix(be, *eltrans, elemmat);
|
||||
elmat += elemmat;
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elmat);
|
||||
}
|
||||
elmat_p = &elmat;
|
||||
if (!static_cond)
|
||||
{
|
||||
mat->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
|
||||
mat->AddSubMatrix(vdofs, vdofs, *elmat_p, skip_zeros);
|
||||
if (hybridization)
|
||||
{
|
||||
hybridization->AssembleBdrMatrix(i, elmat);
|
||||
hybridization->AssembleBdrMatrix(i, *elmat_p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static_cond->AssembleBdrMatrix(i, elmat);
|
||||
static_cond->AssembleBdrMatrix(i, *elmat_p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1318,9 +1329,10 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
return;
|
||||
}
|
||||
|
||||
Array<int> tr_vdofs, te_vdofs;
|
||||
ElementTransformation *eltrans;
|
||||
DenseMatrix elemmat;
|
||||
DofTransformation * dom_dof_trans;
|
||||
DofTransformation * ran_dof_trans;
|
||||
DenseMatrix elmat;
|
||||
|
||||
Mesh *mesh = test_fes -> GetMesh();
|
||||
|
||||
@@ -1333,16 +1345,24 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
{
|
||||
for (int i = 0; i < test_fes -> GetNE(); i++)
|
||||
{
|
||||
trial_fes -> GetElementVDofs (i, tr_vdofs);
|
||||
test_fes -> GetElementVDofs (i, te_vdofs);
|
||||
dom_dof_trans = trial_fes -> GetElementVDofs (i, trial_vdofs);
|
||||
ran_dof_trans = test_fes -> GetElementVDofs (i, test_vdofs);
|
||||
eltrans = test_fes -> GetElementTransformation (i);
|
||||
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
elmat = 0.0;
|
||||
for (int k = 0; k < domain_integs.Size(); k++)
|
||||
{
|
||||
domain_integs[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
|
||||
*test_fes -> GetFE(i),
|
||||
*eltrans, elemmat);
|
||||
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
|
||||
elmat += elemmat;
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
}
|
||||
mat -> AddSubMatrix (test_vdofs, trial_vdofs, elmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1374,9 +1394,12 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
|
||||
trial_fes -> GetBdrElementVDofs (i, tr_vdofs);
|
||||
test_fes -> GetBdrElementVDofs (i, te_vdofs);
|
||||
dom_dof_trans = trial_fes -> GetBdrElementVDofs (i, trial_vdofs);
|
||||
ran_dof_trans = test_fes -> GetBdrElementVDofs (i, test_vdofs);
|
||||
eltrans = test_fes -> GetBdrElementTransformation (i);
|
||||
|
||||
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
|
||||
elmat = 0.0;
|
||||
for (int k = 0; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
if (boundary_integs_marker[k] &&
|
||||
@@ -1385,29 +1408,34 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
boundary_integs[k]->AssembleElementMatrix2 (*trial_fes -> GetBE(i),
|
||||
*test_fes -> GetBE(i),
|
||||
*eltrans, elemmat);
|
||||
mat -> AddSubMatrix (te_vdofs, tr_vdofs, elemmat, skip_zeros);
|
||||
elmat += elemmat;
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformDual(ran_dof_trans, dom_dof_trans, elmat);
|
||||
}
|
||||
mat -> AddSubMatrix (test_vdofs, trial_vdofs, elmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
if (trace_face_integs.Size())
|
||||
{
|
||||
FaceElementTransformations *ftr;
|
||||
Array<int> te_vdofs2;
|
||||
Array<int> test_vdofs2;
|
||||
const FiniteElement *trial_face_fe, *test_fe1, *test_fe2;
|
||||
|
||||
int nfaces = mesh->GetNumFaces();
|
||||
for (int i = 0; i < nfaces; i++)
|
||||
{
|
||||
ftr = mesh->GetFaceElementTransformations(i);
|
||||
trial_fes->GetFaceVDofs(i, tr_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs);
|
||||
trial_fes->GetFaceVDofs(i, trial_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
|
||||
trial_face_fe = trial_fes->GetFaceElement(i);
|
||||
test_fe1 = test_fes->GetFE(ftr->Elem1No);
|
||||
if (ftr->Elem2No >= 0)
|
||||
{
|
||||
test_fes->GetElementVDofs(ftr->Elem2No, te_vdofs2);
|
||||
te_vdofs.Append(te_vdofs2);
|
||||
test_fes->GetElementVDofs(ftr->Elem2No, test_vdofs2);
|
||||
test_vdofs.Append(test_vdofs2);
|
||||
test_fe2 = test_fes->GetFE(ftr->Elem2No);
|
||||
}
|
||||
else
|
||||
@@ -1421,7 +1449,7 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
{
|
||||
trace_face_integs[k]->AssembleFaceMatrix(*trial_face_fe, *test_fe1,
|
||||
*test_fe2, *ftr, elemmat);
|
||||
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
|
||||
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1461,8 +1489,8 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
ftr = mesh->GetBdrFaceTransformations(i);
|
||||
if (ftr)
|
||||
{
|
||||
trial_fes->GetFaceVDofs(ftr->ElementNo, tr_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, te_vdofs);
|
||||
trial_fes->GetFaceVDofs(ftr->ElementNo, trial_vdofs);
|
||||
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
|
||||
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
|
||||
@@ -1479,7 +1507,7 @@ void MixedBilinearForm::Assemble (int skip_zeros)
|
||||
*test_fe1,
|
||||
*test_fe2,
|
||||
*ftr, elemmat);
|
||||
mat->AddSubMatrix(te_vdofs, tr_vdofs, elemmat, skip_zeros);
|
||||
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1841,6 +1869,8 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
|
||||
Array<int> dom_vdofs, ran_vdofs;
|
||||
ElementTransformation *T;
|
||||
DofTransformation * dom_dof_trans;
|
||||
DofTransformation * ran_dof_trans;
|
||||
const FiniteElement *dom_fe, *ran_fe;
|
||||
DenseMatrix totelmat, elmat;
|
||||
|
||||
@@ -1853,8 +1883,8 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
{
|
||||
for (int i = 0; i < test_fes->GetNE(); i++)
|
||||
{
|
||||
trial_fes->GetElementVDofs(i, dom_vdofs);
|
||||
test_fes->GetElementVDofs(i, ran_vdofs);
|
||||
dom_dof_trans = trial_fes->GetElementVDofs(i, dom_vdofs);
|
||||
ran_dof_trans = test_fes->GetElementVDofs(i, ran_vdofs);
|
||||
T = test_fes->GetElementTransformation(i);
|
||||
dom_fe = trial_fes->GetFE(i);
|
||||
ran_fe = test_fes->GetFE(i);
|
||||
@@ -1867,6 +1897,10 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
|
||||
elmat);
|
||||
totelmat += elmat;
|
||||
}
|
||||
if (ran_dof_trans || dom_dof_trans)
|
||||
{
|
||||
TransformPrimal(ran_dof_trans, dom_dof_trans, totelmat);
|
||||
}
|
||||
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,358 @@
|
||||
// Copyright (c) 2010-2021, 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 "fem.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
void DofTransformation::TransformPrimal(Vector &v) const
|
||||
{
|
||||
TransformPrimal(v.GetData());
|
||||
}
|
||||
|
||||
void DofTransformation::TransformPrimalCols(DenseMatrix &V) const
|
||||
{
|
||||
for (int c=0; c<V.Width(); c++)
|
||||
{
|
||||
TransformPrimal(V.GetColumn(c));
|
||||
}
|
||||
}
|
||||
|
||||
void DofTransformation::TransformDual(Vector &v) const
|
||||
{
|
||||
TransformDual(v.GetData());
|
||||
}
|
||||
|
||||
void DofTransformation::TransformDual(DenseMatrix &V) const
|
||||
{
|
||||
TransformDualCols(V);
|
||||
TransformDualRows(V);
|
||||
}
|
||||
|
||||
void DofTransformation::TransformDualRows(DenseMatrix &V) const
|
||||
{
|
||||
Vector row;
|
||||
for (int r=0; r<V.Height(); r++)
|
||||
{
|
||||
V.GetRow(r, row);
|
||||
TransformDual(row);
|
||||
V.SetRow(r, row);
|
||||
}
|
||||
}
|
||||
|
||||
void DofTransformation::TransformDualCols(DenseMatrix &V) const
|
||||
{
|
||||
for (int c=0; c<V.Width(); c++)
|
||||
{
|
||||
TransformDual(V.GetColumn(c));
|
||||
}
|
||||
}
|
||||
|
||||
void DofTransformation::InvTransformPrimal(Vector &v) const
|
||||
{
|
||||
InvTransformPrimal(v.GetData());
|
||||
}
|
||||
|
||||
void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
if (ran_dof_trans && dom_dof_trans)
|
||||
{
|
||||
ran_dof_trans->TransformPrimalCols(elmat);
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
else if (ran_dof_trans)
|
||||
{
|
||||
ran_dof_trans->TransformPrimalCols(elmat);
|
||||
}
|
||||
else if (dom_dof_trans)
|
||||
{
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If both transformations are NULL this function should not be called
|
||||
}
|
||||
}
|
||||
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat)
|
||||
{
|
||||
if (ran_dof_trans && dom_dof_trans)
|
||||
{
|
||||
ran_dof_trans->TransformDualCols(elmat);
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
else if (ran_dof_trans)
|
||||
{
|
||||
ran_dof_trans->TransformDualCols(elmat);
|
||||
}
|
||||
else if (dom_dof_trans)
|
||||
{
|
||||
dom_dof_trans->TransformDualRows(elmat);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If both transformations are NULL this function should not be called
|
||||
}
|
||||
}
|
||||
|
||||
void VDofTransformation::TransformPrimal(double *v) const
|
||||
{
|
||||
int size = doftrans_->Size();
|
||||
|
||||
if ((Ordering::Type)ordering_ == Ordering::byNODES || vdim_ == 1)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
doftrans_->TransformPrimal(&v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
doftrans_->TransformPrimal(vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VDofTransformation::InvTransformPrimal(double *v) const
|
||||
{
|
||||
int size = doftrans_->Height();
|
||||
|
||||
if ((Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
doftrans_->InvTransformPrimal(&v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
doftrans_->InvTransformPrimal(vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VDofTransformation::TransformDual(double *v) const
|
||||
{
|
||||
int size = doftrans_->Size();
|
||||
|
||||
if ((Ordering::Type)ordering_ == Ordering::byNODES)
|
||||
{
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
doftrans_->TransformDual(&v[i*size]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector vec(size);
|
||||
for (int i=0; i<vdim_; i++)
|
||||
{
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
vec(j) = v[j*vdim_+i];
|
||||
}
|
||||
doftrans_->TransformDual(vec);
|
||||
for (int j=0; j<size; j++)
|
||||
{
|
||||
v[j*vdim_+i] = vec(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const double ND_DofTransformation::T_data[24] =
|
||||
{
|
||||
1.0, 0.0, 0.0, 1.0,
|
||||
-1.0, -1.0, 0.0, 1.0,
|
||||
0.0, 1.0, -1.0, -1.0,
|
||||
1.0, 0.0, -1.0, -1.0,
|
||||
-1.0, -1.0, 1.0, 0.0,
|
||||
0.0, 1.0, 1.0, 0.0
|
||||
};
|
||||
|
||||
const DenseTensor ND_DofTransformation
|
||||
::T(const_cast<double*>(ND_DofTransformation::T_data), 2, 2, 6);
|
||||
|
||||
const double ND_DofTransformation::TInv_data[24] =
|
||||
{
|
||||
1.0, 0.0, 0.0, 1.0,
|
||||
-1.0, -1.0, 0.0, 1.0,
|
||||
-1.0, -1.0, 1.0, 0.0,
|
||||
1.0, 0.0, -1.0, -1.0,
|
||||
0.0, 1.0, -1.0, -1.0,
|
||||
0.0, 1.0, 1.0, 0.0
|
||||
};
|
||||
|
||||
const DenseTensor ND_DofTransformation
|
||||
::TInv(const_cast<double*>(TInv_data), 2, 2, 6);
|
||||
|
||||
ND_DofTransformation::ND_DofTransformation(int size, int p)
|
||||
: DofTransformation(size),
|
||||
order(p)
|
||||
{
|
||||
}
|
||||
|
||||
ND_TriDofTransformation::ND_TriDofTransformation(int p)
|
||||
: ND_DofTransformation(p*(p + 2), p)
|
||||
{
|
||||
}
|
||||
|
||||
void ND_TriDofTransformation::TransformPrimal(double *v) const
|
||||
{
|
||||
int nedofs = order; // number of DoFs per edge
|
||||
int nfdofs = order*(order-1); // number of DoFs per face
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<1; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[3*nedofs + f*nfdofs + 2*i];
|
||||
T(Fo[f]).Mult(v2, &v[3*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ND_TriDofTransformation::InvTransformPrimal(double *v) const
|
||||
{
|
||||
int nedofs = order; // number of DoFs per edge
|
||||
int nfdofs = order*(order-1); // number of DoFs per face
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<1; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[3*nedofs + f*nfdofs + 2*i];
|
||||
TInv(Fo[f]).Mult(v2, &v[3*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ND_TriDofTransformation::TransformDual(double *v) const
|
||||
{
|
||||
int nedofs = order; // number of DoFs per edge
|
||||
int nfdofs = order*(order-1); // number of DoFs per face
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<1; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[3*nedofs + f*nfdofs + 2*i];
|
||||
TInv(Fo[f]).MultTranspose(v2, &v[3*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ND_TetDofTransformation::ND_TetDofTransformation(int p)
|
||||
: ND_DofTransformation(p*(p + 2)*(p + 3)/2, p)
|
||||
{
|
||||
}
|
||||
|
||||
void ND_TetDofTransformation::TransformPrimal(double *v) const
|
||||
{
|
||||
int nedofs = order; // number of DoFs per edge
|
||||
int nfdofs = order*(order-1); // number of DoFs per face
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<4; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[6*nedofs + f*nfdofs + 2*i];
|
||||
T(Fo[f]).Mult(v2, &v[6*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ND_TetDofTransformation::InvTransformPrimal(double *v) const
|
||||
{
|
||||
int nedofs = order; // number of DoFs per edge
|
||||
int nfdofs = order*(order-1); // number of DoFs per face
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<4; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[6*nedofs + f*nfdofs + 2*i];
|
||||
TInv(Fo[f]).Mult(v2, &v[6*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ND_TetDofTransformation::TransformDual(double *v) const
|
||||
{
|
||||
int nedofs = order; // number of DoFs per edge
|
||||
int nfdofs = order*(order-1); // number of DoFs per face
|
||||
|
||||
double data[2];
|
||||
Vector v2(data, 2);
|
||||
|
||||
// Transform face DoFs
|
||||
for (int f=0; f<4; f++)
|
||||
{
|
||||
for (int i=0; i<nfdofs/2; i++)
|
||||
{
|
||||
v2 = &v[6*nedofs + f*nfdofs + 2*i];
|
||||
TInv(Fo[f]).MultTranspose(v2, &v[6*nedofs + f*nfdofs + 2*i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,277 @@
|
||||
// Copyright (c) 2010-2021, 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_DOFTRANSFORM
|
||||
#define MFEM_DOFTRANSFORM
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "../linalg/linalg.hpp"
|
||||
#include "intrules.hpp"
|
||||
#include "fe.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** The DofTransformation class is an abstract base class for a family of
|
||||
transformations that map local degrees of freedom (DoFs), contained within
|
||||
individual elements, to global degrees of freedom, stored within
|
||||
GridFunction objects. These transformations are necessary to ensure that
|
||||
basis functions in neighboring elements align correctly. Closely related but
|
||||
complementary transformations are required for the entries stored in
|
||||
LinearForm and BilinearForm objects. The DofTransformation class is designed
|
||||
to apply the action of both of these types of DoF transformations.
|
||||
|
||||
Let the "primal transformation" be given by the operator T. This means that
|
||||
given a local element vector v the data that must be placed into a
|
||||
GridFunction object is v_t = T * v.
|
||||
|
||||
We also need the inverse of the primal transformation T^{-1} so that we can
|
||||
recover the local element vector from data read out of a GridFunction
|
||||
e.g. v = T^{-1} * v_t.
|
||||
|
||||
We need to preserve the action of our linear forms applied to primal
|
||||
vectors. In other words, if f is the local vector computed by a linear
|
||||
form then f * v = f_t * v_t (where "*" represents an inner product of
|
||||
vectors). This requires that f_t = T^{-T} * f i.e. the "dual transform" is
|
||||
given by the transpose of the inverse of the primal transformation.
|
||||
|
||||
For bilinear forms we require that v^T * A * v = v_t^T * A_t * v_t. This
|
||||
implies that A_t = T^{-T} * A * T^{-1}. This can be accomplished by
|
||||
performing dual transformations of the rows and columns of the matrix A.
|
||||
|
||||
For discrete linear operators the range must be modified with the primal
|
||||
transformation rather than the dual transformation because the result is a
|
||||
primal vector rather than a dual vector. This leads to the transformation
|
||||
D_t = T * D * T^{-1}. This can be accomplished by using a primal
|
||||
transformation on the columns of D and a dual transformation on its rows.
|
||||
*/
|
||||
class DofTransformation
|
||||
{
|
||||
protected:
|
||||
int size_;
|
||||
|
||||
Array<int> Fo;
|
||||
|
||||
DofTransformation(int size)
|
||||
: size_(size) {}
|
||||
|
||||
public:
|
||||
|
||||
inline int Size() const { return size_; }
|
||||
inline int Height() const { return size_; }
|
||||
inline int NumRows() const { return size_; }
|
||||
inline int Width() const { return size_; }
|
||||
inline int NumCols() const { return size_; }
|
||||
|
||||
/** @brief Configure the transformation using face orientations for the
|
||||
current element. */
|
||||
/// The face_orientation array can be obtained from Mesh::GetElementFaces.
|
||||
inline void SetFaceOrientations(const Array<int> & face_orientation)
|
||||
{ Fo = face_orientation; }
|
||||
|
||||
inline const Array<int> & GetFaceOrientations() const { return Fo; }
|
||||
|
||||
/** Transform local DoFs to align with the global DoFs. For example, this
|
||||
transformation can be used to map the local vector computed by
|
||||
FiniteElement::Project() to the transformed vector stored within a
|
||||
GridFunction object. */
|
||||
virtual void TransformPrimal(double *v) const = 0;
|
||||
virtual void TransformPrimal(Vector &v) const;
|
||||
|
||||
/// Transform groups of DoFs stored as dense matrices
|
||||
virtual void TransformPrimalCols(DenseMatrix &V) const;
|
||||
|
||||
/** Inverse transform local DoFs. Used to transform DoFs from a global vector
|
||||
back to their element-local form. For example, this must be used to
|
||||
transform the vector obtained using GridFunction::GetSubVector before it
|
||||
can be used to compute a local interpolation.
|
||||
*/
|
||||
virtual void InvTransformPrimal(double *v) const = 0;
|
||||
virtual void InvTransformPrimal(Vector &v) const;
|
||||
|
||||
/** Transform dual DoFs as computed by a LinearFormIntegrator before summing
|
||||
into a LinearForm object. */
|
||||
virtual void TransformDual(double *v) const = 0;
|
||||
virtual void TransformDual(Vector &v) const;
|
||||
|
||||
/** Transform a matrix of dual DoFs entries as computed by a
|
||||
BilinearFormIntegrator before summing into a BilinearForm object. */
|
||||
virtual void TransformDual(DenseMatrix &V) const;
|
||||
|
||||
/// Transform groups of dual DoFs stored as dense matrices
|
||||
virtual void TransformDualRows(DenseMatrix &V) const;
|
||||
virtual void TransformDualCols(DenseMatrix &V) const;
|
||||
|
||||
virtual ~DofTransformation() {}
|
||||
};
|
||||
|
||||
/** Transform a matrix of DoFs entries from different finite element spaces as
|
||||
computed by a DiscreteInterpolator before copying into a
|
||||
DiscreteLinearOperator.
|
||||
*/
|
||||
void TransformPrimal(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** Transform a matrix of dual DoFs entries from different finite element spaces
|
||||
as computed by a BilinearFormIntegrator before summing into a
|
||||
MixedBilinearForm object.
|
||||
*/
|
||||
void TransformDual(const DofTransformation *ran_dof_trans,
|
||||
const DofTransformation *dom_dof_trans,
|
||||
DenseMatrix &elmat);
|
||||
|
||||
/** The VDofTransformation class implements a nested transformation where an
|
||||
arbitrary DofTransformation is replicated with a vdim >= 1.
|
||||
*/
|
||||
class VDofTransformation : public DofTransformation
|
||||
{
|
||||
private:
|
||||
int vdim_;
|
||||
int ordering_;
|
||||
DofTransformation * doftrans_;
|
||||
|
||||
public:
|
||||
/** @brief Default constructor which requires that SetDofTransformation be
|
||||
called before use. */
|
||||
VDofTransformation(int vdim = 1, int ordering = 0)
|
||||
: DofTransformation(0),
|
||||
vdim_(vdim), ordering_(ordering),
|
||||
doftrans_(NULL) {}
|
||||
|
||||
/// Constructor with a known DofTransformation
|
||||
VDofTransformation(DofTransformation & doftrans, int vdim = 1,
|
||||
int ordering = 0)
|
||||
: DofTransformation(vdim * doftrans.Size()),
|
||||
vdim_(vdim), ordering_(ordering),
|
||||
doftrans_(&doftrans) {}
|
||||
|
||||
/// Set or change the vdim parameter
|
||||
inline void SetVDim(int vdim)
|
||||
{
|
||||
vdim_ = vdim;
|
||||
if (doftrans_)
|
||||
{
|
||||
size_ = vdim_ * doftrans_->Size();
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the current vdim value
|
||||
inline int GetVDim() const { return vdim_; }
|
||||
|
||||
/// Set or change the nested DofTransformation object
|
||||
inline void SetDofTransformation(DofTransformation & doftrans)
|
||||
{
|
||||
size_ = vdim_ * doftrans.Size();
|
||||
doftrans_ = &doftrans;
|
||||
}
|
||||
|
||||
/// Return the nested DofTransformation object
|
||||
inline DofTransformation * GetDofTransformation() const { return doftrans_; }
|
||||
|
||||
inline void SetFaceOrientation(const Array<int> & face_orientation)
|
||||
{ Fo = face_orientation; doftrans_->SetFaceOrientations(face_orientation); }
|
||||
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
|
||||
void TransformPrimal(double *v) const;
|
||||
void InvTransformPrimal(double *v) const;
|
||||
void TransformDual(double *v) const;
|
||||
};
|
||||
|
||||
/** Abstract base class for high-order Nedelec spaces on elements with
|
||||
triangular faces.
|
||||
|
||||
The Nedelec DoFs on the interior of triangular faces come in pairs which
|
||||
share an interpolation point but have different vector directions. These
|
||||
directions depend on the orientation of the face and can therefore differ in
|
||||
neighboring elements. The mapping required to transform these DoFs can be
|
||||
implemented as series of 2x2 linear transformations. The raw data for these
|
||||
linear transformations is stored in the T_data and TInv_data arrays and can
|
||||
be accessed as DenseMatrices using the GetFaceTransform() and
|
||||
GetFaceInverseTransform() methods.
|
||||
*/
|
||||
class ND_DofTransformation : public DofTransformation
|
||||
{
|
||||
protected:
|
||||
static const double T_data[24];
|
||||
static const double TInv_data[24];
|
||||
static const DenseTensor T, TInv;
|
||||
int order;
|
||||
|
||||
ND_DofTransformation(int size, int order);
|
||||
|
||||
public:
|
||||
// Return the 2x2 transformation operator for the given face orientation
|
||||
static const DenseMatrix & GetFaceTransform(int ori) { return T(ori); }
|
||||
|
||||
// Return the 2x2 inverse transformation operator
|
||||
static const DenseMatrix & GetFaceInverseTransform(int ori)
|
||||
{ return TInv(ori); }
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on triangles
|
||||
class ND_TriDofTransformation : public ND_DofTransformation
|
||||
{
|
||||
public:
|
||||
ND_TriDofTransformation(int order);
|
||||
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
|
||||
void TransformPrimal(double *v) const;
|
||||
|
||||
void InvTransformPrimal(double *v) const;
|
||||
|
||||
void TransformDual(double *v) const;
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on tetrahedra
|
||||
class ND_TetDofTransformation : public ND_DofTransformation
|
||||
{
|
||||
public:
|
||||
ND_TetDofTransformation(int order);
|
||||
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
|
||||
void TransformPrimal(double *v) const;
|
||||
|
||||
void InvTransformPrimal(double *v) const;
|
||||
|
||||
void TransformDual(double *v) const;
|
||||
};
|
||||
|
||||
/// DoF transformation implementation for the Nedelec basis on wedge elements
|
||||
/** TODO: (Under development) */
|
||||
class ND_WedgeDofTransformation : public ND_DofTransformation
|
||||
{
|
||||
public:
|
||||
ND_WedgeDofTransformation(int order);
|
||||
|
||||
using DofTransformation::TransformPrimal;
|
||||
using DofTransformation::InvTransformPrimal;
|
||||
using DofTransformation::TransformDual;
|
||||
|
||||
void TransformPrimal(double *v) const;
|
||||
|
||||
void InvTransformPrimal(double *v) const;
|
||||
|
||||
void TransformDual(double *v) const;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_DOFTRANSFORM
|
||||
@@ -380,6 +380,7 @@ void IsoparametricTransformation::SetIdentityTransformation(
|
||||
case Geometry::TETRAHEDRON : FElem = &TetrahedronFE; break;
|
||||
case Geometry::CUBE : FElem = &HexahedronFE; break;
|
||||
case Geometry::PRISM : FElem = &WedgeFE; break;
|
||||
case Geometry::PYRAMID : FElem = &PyramidFE; break;
|
||||
default:
|
||||
MFEM_ABORT("unknown Geometry::Type!");
|
||||
}
|
||||
|
||||
+3
-2
@@ -329,7 +329,7 @@ void KellyErrorEstimator::ComputeEstimates()
|
||||
error_estimates(e) = sqrt(factor * error_estimates(e));
|
||||
}
|
||||
|
||||
total_error = error_estimates.Sum();
|
||||
total_error = error_estimates.Norml2();
|
||||
delete flux;
|
||||
return;
|
||||
}
|
||||
@@ -452,9 +452,10 @@ void KellyErrorEstimator::ComputeEstimates()
|
||||
auto pfes = dynamic_cast<ParFiniteElementSpace*>(xfes);
|
||||
MFEM_VERIFY(pfes, "xfes is not a ParFiniteElementSpace pointer");
|
||||
|
||||
double process_local_error = error_estimates.Sum();
|
||||
double process_local_error = pow(error_estimates.Norml2(),2.0);
|
||||
MPI_Allreduce(&process_local_error, &total_error, 1, MPI_DOUBLE,
|
||||
MPI_SUM, pfes->GetComm());
|
||||
total_error = sqrt(total_error);
|
||||
#endif // MFEM_USE_MPI
|
||||
}
|
||||
|
||||
|
||||
+1204
-18
File diff suppressed because it is too large
Load Diff
+219
@@ -1313,6 +1313,64 @@ public:
|
||||
DenseMatrix &dshape) const;
|
||||
};
|
||||
|
||||
/// A linear element defined on a triangular prism
|
||||
class LinearWedgeFiniteElement : public NodalFiniteElement
|
||||
{
|
||||
public:
|
||||
/// Construct the LinearWedgeFiniteElement
|
||||
LinearWedgeFiniteElement();
|
||||
|
||||
/** @brief virtual function which evaluates the values of all
|
||||
shape functions at a given point ip and stores
|
||||
them in the vector shape of dimension Dof (4) */
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
|
||||
/** @brief virtual function which evaluates the values of all
|
||||
partial derivatives of all shape functions at a given
|
||||
point ip and stores them in the matrix dshape (Dof x Dim) (4 x 3)
|
||||
so that each row contains the derivatives of one shape function */
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
|
||||
virtual void ProjectDelta(int vertex, Vector &dofs) const
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
|
||||
/** @brief Get the dofs associated with the given @a face.
|
||||
@a *dofs is set to an internal array of the local dofc on the
|
||||
face, while *ndofs is set to the number of dofs on that face.
|
||||
*/
|
||||
virtual void GetFaceDofs(int face, int **dofs, int *ndofs) const;
|
||||
};
|
||||
|
||||
/// A linear element defined on a square pyramid
|
||||
class LinearPyramidFiniteElement : public NodalFiniteElement
|
||||
{
|
||||
public:
|
||||
/// Construct the LinearPyramidFiniteElement
|
||||
LinearPyramidFiniteElement();
|
||||
|
||||
/** @brief virtual function which evaluates the values of all
|
||||
shape functions at a given point ip and stores
|
||||
them in the vector shape of dimension Dof (4) */
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
|
||||
/** @brief virtual function which evaluates the values of all
|
||||
partial derivatives of all shape functions at a given
|
||||
point ip and stores them in the matrix dshape (Dof x Dim) (4 x 3)
|
||||
so that each row contains the derivatives of one shape function */
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
|
||||
virtual void ProjectDelta(int vertex, Vector &dofs) const
|
||||
{ dofs = 0.0; dofs(vertex) = 1.0; }
|
||||
|
||||
/** @brief Get the dofs associated with the given @a face.
|
||||
@a *dofs is set to an internal array of the local dofc on the
|
||||
face, while *ndofs is set to the number of dofs on that face.
|
||||
*/
|
||||
virtual void GetFaceDofs(int face, int **dofs, int *ndofs) const;
|
||||
};
|
||||
|
||||
/// A 2D constant element on a triangle
|
||||
class P0TriangleFiniteElement : public NodalFiniteElement
|
||||
{
|
||||
@@ -1690,6 +1748,32 @@ public:
|
||||
{ dofs(0) = 1.0; }
|
||||
};
|
||||
|
||||
/// A 3D constant element on a wedge
|
||||
class P0WdgFiniteElement : public NodalFiniteElement
|
||||
{
|
||||
public:
|
||||
/// Construct the P0WdgFiniteElement
|
||||
P0WdgFiniteElement ();
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
virtual void ProjectDelta(int vertex, Vector &dofs) const
|
||||
{ dofs(0) = 1.0; }
|
||||
};
|
||||
|
||||
/// A 3D constant element on a pyramid
|
||||
class P0PyrFiniteElement : public NodalFiniteElement
|
||||
{
|
||||
public:
|
||||
/// Construct the P0PyrFiniteElement
|
||||
P0PyrFiniteElement ();
|
||||
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
|
||||
virtual void CalcDShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &dshape) const;
|
||||
virtual void ProjectDelta(int vertex, Vector &dofs) const
|
||||
{ dofs(0) = 1.0; }
|
||||
};
|
||||
|
||||
/** @brief Tensor products of 1D Lagrange1DFiniteElement
|
||||
(only degree 2 is functional) */
|
||||
class LagrangeHexFiniteElement : public NodalFiniteElement
|
||||
@@ -1828,6 +1912,10 @@ public:
|
||||
using FiniteElement::Project;
|
||||
virtual void Project (VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const;
|
||||
|
||||
virtual void ProjectGrad(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &grad) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -1852,6 +1940,66 @@ public:
|
||||
using FiniteElement::Project;
|
||||
virtual void Project (VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const;
|
||||
|
||||
virtual void ProjectGrad(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &grad) const;
|
||||
};
|
||||
|
||||
|
||||
/// A 3D 1st order Nedelec element on a wedge
|
||||
class Nedelec1WdgFiniteElement : public VectorFiniteElement
|
||||
{
|
||||
private:
|
||||
static const double tk[9][3];
|
||||
|
||||
public:
|
||||
/// Construct the Nedelec1WdgFiniteElement
|
||||
Nedelec1WdgFiniteElement();
|
||||
virtual void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const;
|
||||
virtual void CalcVShape(ElementTransformation &Trans,
|
||||
DenseMatrix &shape) const
|
||||
{ CalcVShape_ND(Trans, shape); }
|
||||
virtual void CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape) const;
|
||||
virtual void GetLocalInterpolation (ElementTransformation &Trans,
|
||||
DenseMatrix &I) const;
|
||||
using FiniteElement::Project;
|
||||
virtual void Project (VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const;
|
||||
|
||||
virtual void ProjectGrad(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &grad) const;
|
||||
};
|
||||
|
||||
|
||||
/// A 3D 1st order Nedelec element on a pyramid
|
||||
class Nedelec1PyrFiniteElement : public VectorFiniteElement
|
||||
{
|
||||
private:
|
||||
static const double tk[8][3];
|
||||
|
||||
public:
|
||||
/// Construct the Nedelec1PyrFiniteElement
|
||||
Nedelec1PyrFiniteElement();
|
||||
virtual void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const;
|
||||
virtual void CalcVShape(ElementTransformation &Trans,
|
||||
DenseMatrix &shape) const
|
||||
{ CalcVShape_ND(Trans, shape); }
|
||||
virtual void CalcCurlShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &curl_shape) const;
|
||||
virtual void GetLocalInterpolation (ElementTransformation &Trans,
|
||||
DenseMatrix &I) const;
|
||||
using FiniteElement::Project;
|
||||
virtual void Project (VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const;
|
||||
|
||||
virtual void ProjectGrad(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &grad) const;
|
||||
};
|
||||
|
||||
|
||||
@@ -1945,6 +2093,77 @@ public:
|
||||
};
|
||||
|
||||
|
||||
/// A 3D 0th order Raviert-Thomas element on a wedge
|
||||
class RT0WdgFiniteElement : public VectorFiniteElement
|
||||
{
|
||||
private:
|
||||
static const double nk[5][3];
|
||||
|
||||
public:
|
||||
/// Construct the RT0WdgFiniteElement
|
||||
RT0WdgFiniteElement();
|
||||
|
||||
virtual void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const;
|
||||
|
||||
virtual void CalcVShape(ElementTransformation &Trans,
|
||||
DenseMatrix &shape) const
|
||||
{ CalcVShape_RT(Trans, shape); }
|
||||
|
||||
virtual void CalcDivShape(const IntegrationPoint &ip,
|
||||
Vector &divshape) const;
|
||||
|
||||
virtual void GetLocalInterpolation (ElementTransformation &Trans,
|
||||
DenseMatrix &I) const;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
virtual void Project (VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const;
|
||||
|
||||
virtual void ProjectCurl(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &curl) const;
|
||||
};
|
||||
|
||||
|
||||
/// A 3D 0th order Raviert-Thomas element on a pyramid
|
||||
class RT0PyrFiniteElement : public VectorFiniteElement
|
||||
{
|
||||
private:
|
||||
static const double nk[5][3];
|
||||
|
||||
// If true match RT0TetFiniteElement rather than RT_TetrahedronElement(0)
|
||||
bool rt0;
|
||||
|
||||
public:
|
||||
/// Construct the RT0PyrFiniteElement
|
||||
RT0PyrFiniteElement(bool rt0tets = true);
|
||||
|
||||
virtual void CalcVShape(const IntegrationPoint &ip,
|
||||
DenseMatrix &shape) const;
|
||||
|
||||
virtual void CalcVShape(ElementTransformation &Trans,
|
||||
DenseMatrix &shape) const
|
||||
{ CalcVShape_RT(Trans, shape); }
|
||||
|
||||
virtual void CalcDivShape(const IntegrationPoint &ip,
|
||||
Vector &divshape) const;
|
||||
|
||||
virtual void GetLocalInterpolation (ElementTransformation &Trans,
|
||||
DenseMatrix &I) const;
|
||||
|
||||
using FiniteElement::Project;
|
||||
|
||||
virtual void Project (VectorCoefficient &vc,
|
||||
ElementTransformation &Trans, Vector &dofs) const;
|
||||
|
||||
virtual void ProjectCurl(const FiniteElement &fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &curl) const;
|
||||
};
|
||||
|
||||
|
||||
class RotTriLinearHexFiniteElement : public NodalFiniteElement
|
||||
{
|
||||
public:
|
||||
|
||||
+122
-16
@@ -33,6 +33,9 @@ int FiniteElementCollection::HasFaceDofs(Geometry::Type geom, int p) const
|
||||
case Geometry::PRISM:
|
||||
return max(GetNumDof(Geometry::TRIANGLE, p),
|
||||
GetNumDof(Geometry::SQUARE, p));
|
||||
case Geometry::PYRAMID:
|
||||
return max(GetNumDof(Geometry::TRIANGLE, p),
|
||||
GetNumDof(Geometry::SQUARE, p));
|
||||
default:
|
||||
MFEM_ABORT("unknown geometry type");
|
||||
}
|
||||
@@ -574,6 +577,7 @@ LinearFECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
case Geometry::TETRAHEDRON: return &TetrahedronFE;
|
||||
case Geometry::CUBE: return &ParallelepipedFE;
|
||||
case Geometry::PRISM: return &WedgeFE;
|
||||
case Geometry::PYRAMID: return &PyramidFE;
|
||||
default:
|
||||
mfem_error ("LinearFECollection: unknown geometry type.");
|
||||
}
|
||||
@@ -591,6 +595,7 @@ int LinearFECollection::DofForGeometry(Geometry::Type GeomType) const
|
||||
case Geometry::TETRAHEDRON: return 0;
|
||||
case Geometry::CUBE: return 0;
|
||||
case Geometry::PRISM: return 0;
|
||||
case Geometry::PYRAMID: return 0;
|
||||
default:
|
||||
mfem_error ("LinearFECollection: unknown geometry type.");
|
||||
}
|
||||
@@ -1240,6 +1245,7 @@ Const3DFECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
case Geometry::TETRAHEDRON: return &TetrahedronFE;
|
||||
case Geometry::CUBE: return &ParallelepipedFE;
|
||||
case Geometry::PRISM: return &WedgeFE;
|
||||
case Geometry::PYRAMID: return &PyramidFE;
|
||||
default:
|
||||
mfem_error ("Const3DFECollection: unknown geometry type.");
|
||||
}
|
||||
@@ -1257,6 +1263,7 @@ int Const3DFECollection::DofForGeometry(Geometry::Type GeomType) const
|
||||
case Geometry::TETRAHEDRON: return 1;
|
||||
case Geometry::CUBE: return 1;
|
||||
case Geometry::PRISM: return 1;
|
||||
case Geometry::PYRAMID: return 1;
|
||||
default:
|
||||
mfem_error ("Const3DFECollection: unknown geometry type.");
|
||||
}
|
||||
@@ -1277,6 +1284,8 @@ LinearDiscont3DFECollection::FiniteElementForGeometry(
|
||||
switch (GeomType)
|
||||
{
|
||||
case Geometry::TETRAHEDRON: return &TetrahedronFE;
|
||||
case Geometry::PYRAMID: return &PyramidFE;
|
||||
case Geometry::PRISM: return &WedgeFE;
|
||||
case Geometry::CUBE: return &ParallelepipedFE;
|
||||
default:
|
||||
mfem_error ("LinearDiscont3DFECollection: unknown geometry type.");
|
||||
@@ -1293,6 +1302,8 @@ int LinearDiscont3DFECollection::DofForGeometry(Geometry::Type GeomType) const
|
||||
case Geometry::TRIANGLE: return 0;
|
||||
case Geometry::SQUARE: return 0;
|
||||
case Geometry::TETRAHEDRON: return 4;
|
||||
case Geometry::PYRAMID: return 5;
|
||||
case Geometry::PRISM: return 6;
|
||||
case Geometry::CUBE: return 8;
|
||||
default:
|
||||
mfem_error ("LinearDiscont3DFECollection: unknown geometry type.");
|
||||
@@ -1394,6 +1405,8 @@ ND1_3DFECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
case Geometry::CUBE: return &HexahedronFE;
|
||||
case Geometry::TETRAHEDRON: return &TetrahedronFE;
|
||||
case Geometry::PRISM: return &WedgeFE;
|
||||
case Geometry::PYRAMID: return &PyramidFE;
|
||||
default:
|
||||
mfem_error ("ND1_3DFECollection: unknown geometry type.");
|
||||
}
|
||||
@@ -1410,6 +1423,8 @@ int ND1_3DFECollection::DofForGeometry(Geometry::Type GeomType) const
|
||||
case Geometry::SQUARE: return 0;
|
||||
case Geometry::TETRAHEDRON: return 0;
|
||||
case Geometry::CUBE: return 0;
|
||||
case Geometry::PRISM: return 0;
|
||||
case Geometry::PYRAMID: return 0;
|
||||
default:
|
||||
mfem_error ("ND1_3DFECollection: unknown geometry type.");
|
||||
}
|
||||
@@ -1439,6 +1454,8 @@ RT0_3DFECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
case Geometry::SQUARE: return &QuadrilateralFE;
|
||||
case Geometry::CUBE: return &HexahedronFE;
|
||||
case Geometry::TETRAHEDRON: return &TetrahedronFE;
|
||||
case Geometry::PRISM: return &WedgeFE;
|
||||
case Geometry::PYRAMID: return &PyramidFE;
|
||||
default:
|
||||
mfem_error ("RT0_3DFECollection: unknown geometry type.");
|
||||
}
|
||||
@@ -1455,6 +1472,8 @@ int RT0_3DFECollection::DofForGeometry(Geometry::Type GeomType) const
|
||||
case Geometry::SQUARE: return 1;
|
||||
case Geometry::TETRAHEDRON: return 0;
|
||||
case Geometry::CUBE: return 0;
|
||||
case Geometry::PRISM: return 0;
|
||||
case Geometry::PYRAMID: return 0;
|
||||
default:
|
||||
mfem_error ("RT0_3DFECollection: unknown geometry type.");
|
||||
}
|
||||
@@ -1730,6 +1749,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
H1_dof[Geometry::TETRAHEDRON] = (TriDof*pm3)/3;
|
||||
H1_dof[Geometry::CUBE] = QuadDof*pm1;
|
||||
H1_dof[Geometry::PRISM] = TriDof*pm1;
|
||||
H1_dof[Geometry::PYRAMID] = 0;
|
||||
if (b_type == BasisType::Positive)
|
||||
{
|
||||
H1_Elements[Geometry::TETRAHEDRON] = new H1Pos_TetrahedronElement(p);
|
||||
@@ -1743,6 +1763,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
H1_Elements[Geometry::CUBE] = new H1_HexahedronElement(p, btype);
|
||||
H1_Elements[Geometry::PRISM] = new H1_WedgeElement(p, btype);
|
||||
}
|
||||
H1_Elements[Geometry::PYRAMID] = new LinearPyramidFiniteElement;
|
||||
|
||||
const int &TetDof = H1_dof[Geometry::TETRAHEDRON];
|
||||
TetDofOrd[0] = new int[24*TetDof];
|
||||
@@ -1837,6 +1858,21 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
|
||||
}
|
||||
}
|
||||
|
||||
const FiniteElement *
|
||||
H1_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 1)
|
||||
{
|
||||
return H1_Elements[GeomType];
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("H1 Pyramid basis functions are not yet supported "
|
||||
"for order > 1.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const int *H1_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
|
||||
int Or) const
|
||||
{
|
||||
@@ -2076,9 +2112,12 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
L2_Elements[Geometry::CUBE] = new L2_HexahedronElement(p, btype);
|
||||
L2_Elements[Geometry::PRISM] = new L2_WedgeElement(p, btype);
|
||||
}
|
||||
L2_Elements[Geometry::PYRAMID] = new P0PyrFiniteElement;
|
||||
|
||||
L2_Elements[Geometry::TETRAHEDRON]->SetMapType(map_type);
|
||||
L2_Elements[Geometry::CUBE]->SetMapType(map_type);
|
||||
L2_Elements[Geometry::PRISM]->SetMapType(map_type);
|
||||
L2_Elements[Geometry::PYRAMID]->SetMapType(map_type);
|
||||
// Trace element use the default Gauss-Legendre nodal points for positive basis
|
||||
if (b_type == BasisType::Positive)
|
||||
{
|
||||
@@ -2199,6 +2238,21 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
|
||||
}
|
||||
}
|
||||
|
||||
const FiniteElement *
|
||||
L2_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 0)
|
||||
{
|
||||
return L2_Elements[GeomType];
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("L2 Pyramid basis functions are not yet supported "
|
||||
"for order > 0.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const int *L2_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
|
||||
int Or) const
|
||||
{
|
||||
@@ -2290,6 +2344,12 @@ RT_FECollection::RT_FECollection(const int order, const int dim,
|
||||
|
||||
RT_Elements[Geometry::CUBE] = new RT_HexahedronElement(p, cb_type, ob_type);
|
||||
RT_dof[Geometry::CUBE] = 3*p*pp1*pp1;
|
||||
|
||||
RT_Elements[Geometry::PRISM] = new RT0WdgFiniteElement;
|
||||
RT_dof[Geometry::PRISM] = 0;
|
||||
|
||||
RT_Elements[Geometry::PYRAMID] = new RT0PyrFiniteElement(false);
|
||||
RT_dof[Geometry::PYRAMID] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2433,6 +2493,22 @@ void RT_FECollection::InitFaces(const int p, const int dim,
|
||||
}
|
||||
}
|
||||
|
||||
const FiniteElement *
|
||||
RT_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if ((GeomType != Geometry::PRISM && GeomType != Geometry::PYRAMID) ||
|
||||
this->GetOrder() == 1)
|
||||
{
|
||||
return RT_Elements[GeomType];
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("RT Wedge and Pyramid basis functions are not yet supported "
|
||||
"for order > 0.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const int *RT_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
|
||||
int Or) const
|
||||
{
|
||||
@@ -2656,18 +2732,31 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
|
||||
{
|
||||
for (int i = 0; i + j <= pm2; i++)
|
||||
{
|
||||
int k1 = p*pm1 - (p - j)*(pm1 - j) + 2*i;
|
||||
int k2 = p*pm1 - (p - i)*(pm1 - i) + 2*j;
|
||||
// (0,1,2)
|
||||
TriDofOrd[0][k1 ] = k1;
|
||||
TriDofOrd[0][k1+1] = k1 + 1;
|
||||
// (0,2,1)
|
||||
TriDofOrd[5][k1 ] = k2 + 1;
|
||||
TriDofOrd[5][k1+1] = k2;
|
||||
int k0 = p*pm1 - (p - j)*(pm1 - j) + 2*i;
|
||||
int k1 = 2*pm2 - 2*i + ((2*p-3)-j)*j;
|
||||
int k2 = 2*pm2 - 2*j + ((2*p-3)-i)*i;
|
||||
int k3 = p*pm1 - 2 - 3*j - i - (i+j)*(i+j);
|
||||
int k4 = p*pm1 - 2 - 3*i - j - (i+j)*(i+j);
|
||||
int k5 = p*pm1 - (p - i)*(pm1 - i) + 2*j;
|
||||
|
||||
// The other orientations can not be supported with the current
|
||||
// interface. The method Mesh::ReorientTetMesh will ensure that
|
||||
// only orientations 0 and 5 are generated.
|
||||
// (0,1,2)
|
||||
TriDofOrd[0][k0 ] = k0;
|
||||
TriDofOrd[0][k0+1] = k0 + 1;
|
||||
// (1,0,2)
|
||||
TriDofOrd[1][k0 ] = k1;
|
||||
TriDofOrd[1][k0+1] = k1 + 1;
|
||||
// (2,0,1)
|
||||
TriDofOrd[2][k0 ] = k2;
|
||||
TriDofOrd[2][k0+1] = k2 + 1;
|
||||
// (2,1,0)
|
||||
TriDofOrd[3][k0 ] = k3;
|
||||
TriDofOrd[3][k0+1] = k3 + 1;
|
||||
// (1,2,0)
|
||||
TriDofOrd[4][k0 ] = k4;
|
||||
TriDofOrd[4][k0+1] = k4 + 1;
|
||||
// (0,2,1)
|
||||
TriDofOrd[5][k0 ] = k5;
|
||||
TriDofOrd[5][k0+1] = k5 + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2680,6 +2769,28 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
|
||||
// TODO: cb_type and ob_type for tets
|
||||
ND_Elements[Geometry::TETRAHEDRON] = new ND_TetrahedronElement(p);
|
||||
ND_dof[Geometry::TETRAHEDRON] = p*pm1*pm2/2;
|
||||
|
||||
ND_Elements[Geometry::PRISM] = new Nedelec1WdgFiniteElement;
|
||||
ND_dof[Geometry::PRISM] = 0;
|
||||
|
||||
ND_Elements[Geometry::PYRAMID] = new Nedelec1PyrFiniteElement;
|
||||
ND_dof[Geometry::PYRAMID] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const FiniteElement *
|
||||
ND_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if ((GeomType != Geometry::PRISM && GeomType != Geometry::PYRAMID) ||
|
||||
this->GetOrder() == 1)
|
||||
{
|
||||
return ND_Elements[GeomType];
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("ND Wedge and Pyramid basis functions are not yet supported "
|
||||
"for order > 1.");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2692,11 +2803,6 @@ const int *ND_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
|
||||
}
|
||||
else if (GeomType == Geometry::TRIANGLE)
|
||||
{
|
||||
if (Or != 0 && Or != 5)
|
||||
{
|
||||
MFEM_ABORT("triangle face orientation " << Or << " is not supported! "
|
||||
"Use Mesh::ReorientTetMesh to fix it.");
|
||||
}
|
||||
return TriDofOrd[Or%6];
|
||||
}
|
||||
else if (GeomType == Geometry::SQUARE)
|
||||
|
||||
+16
-14
@@ -228,8 +228,7 @@ public:
|
||||
const int btype = BasisType::GaussLobatto);
|
||||
|
||||
virtual const FiniteElement *FiniteElementForGeometry(
|
||||
Geometry::Type GeomType) const
|
||||
{ return H1_Elements[GeomType]; }
|
||||
Geometry::Type GeomType) const;
|
||||
virtual int DofForGeometry(Geometry::Type GeomType) const
|
||||
{ return H1_dof[GeomType]; }
|
||||
virtual const int *DofOrderForOrientation(Geometry::Type GeomType,
|
||||
@@ -302,10 +301,7 @@ public:
|
||||
const int map_type = FiniteElement::VALUE);
|
||||
|
||||
virtual const FiniteElement *FiniteElementForGeometry(
|
||||
Geometry::Type GeomType) const
|
||||
{
|
||||
return L2_Elements[GeomType];
|
||||
}
|
||||
Geometry::Type GeomType) const;
|
||||
virtual int DofForGeometry(Geometry::Type GeomType) const
|
||||
{
|
||||
if (L2_Elements[GeomType])
|
||||
@@ -371,8 +367,7 @@ public:
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
virtual const FiniteElement *FiniteElementForGeometry(
|
||||
Geometry::Type GeomType) const
|
||||
{ return RT_Elements[GeomType]; }
|
||||
Geometry::Type GeomType) const;
|
||||
virtual int DofForGeometry(Geometry::Type GeomType) const
|
||||
{ return RT_dof[GeomType]; }
|
||||
virtual const int *DofOrderForOrientation(Geometry::Type GeomType,
|
||||
@@ -430,8 +425,7 @@ public:
|
||||
const int ob_type = BasisType::GaussLegendre);
|
||||
|
||||
virtual const FiniteElement *
|
||||
FiniteElementForGeometry(Geometry::Type GeomType) const
|
||||
{ return ND_Elements[GeomType]; }
|
||||
FiniteElementForGeometry(Geometry::Type GeomType) const;
|
||||
|
||||
virtual int DofForGeometry(Geometry::Type GeomType) const
|
||||
{ return ND_dof[GeomType]; }
|
||||
@@ -529,9 +523,10 @@ private:
|
||||
const BiLinear2DFiniteElement QuadrilateralFE;
|
||||
const Linear3DFiniteElement TetrahedronFE;
|
||||
const TriLinear3DFiniteElement ParallelepipedFE;
|
||||
const H1_WedgeElement WedgeFE;
|
||||
const LinearWedgeFiniteElement WedgeFE;
|
||||
const LinearPyramidFiniteElement PyramidFE;
|
||||
public:
|
||||
LinearFECollection() : FiniteElementCollection(1), WedgeFE(1) { }
|
||||
LinearFECollection() : FiniteElementCollection(1) { }
|
||||
|
||||
virtual const FiniteElement *
|
||||
FiniteElementForGeometry(Geometry::Type GeomType) const;
|
||||
@@ -936,10 +931,11 @@ class Const3DFECollection : public FiniteElementCollection
|
||||
private:
|
||||
const P0TetFiniteElement TetrahedronFE;
|
||||
const P0HexFiniteElement ParallelepipedFE;
|
||||
const L2_WedgeElement WedgeFE;
|
||||
const P0WdgFiniteElement WedgeFE;
|
||||
const P0PyrFiniteElement PyramidFE;
|
||||
|
||||
public:
|
||||
Const3DFECollection() : FiniteElementCollection(0), WedgeFE(0) { }
|
||||
Const3DFECollection() : FiniteElementCollection(0) { }
|
||||
|
||||
virtual const FiniteElement *
|
||||
FiniteElementForGeometry(Geometry::Type GeomType) const;
|
||||
@@ -960,6 +956,8 @@ class LinearDiscont3DFECollection : public FiniteElementCollection
|
||||
{
|
||||
private:
|
||||
const Linear3DFiniteElement TetrahedronFE;
|
||||
const LinearPyramidFiniteElement PyramidFE;
|
||||
const LinearWedgeFiniteElement WedgeFE;
|
||||
const TriLinear3DFiniteElement ParallelepipedFE;
|
||||
|
||||
public:
|
||||
@@ -1036,6 +1034,8 @@ class ND1_3DFECollection : public FiniteElementCollection
|
||||
private:
|
||||
const Nedelec1HexFiniteElement HexahedronFE;
|
||||
const Nedelec1TetFiniteElement TetrahedronFE;
|
||||
const Nedelec1WdgFiniteElement WedgeFE;
|
||||
const Nedelec1PyrFiniteElement PyramidFE;
|
||||
|
||||
public:
|
||||
ND1_3DFECollection() : FiniteElementCollection(1) { }
|
||||
@@ -1061,6 +1061,8 @@ private:
|
||||
const P0QuadFiniteElement QuadrilateralFE;
|
||||
const RT0HexFiniteElement HexahedronFE;
|
||||
const RT0TetFiniteElement TetrahedronFE;
|
||||
const RT0WdgFiniteElement WedgeFE;
|
||||
const RT0PyrFiniteElement PyramidFE;
|
||||
public:
|
||||
RT0_3DFECollection() : FiniteElementCollection(1) { }
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "geom.hpp"
|
||||
#include "fe.hpp"
|
||||
#include "fe_coll.hpp"
|
||||
#include "doftrans.hpp"
|
||||
#include "eltrans.hpp"
|
||||
#include "coefficient.hpp"
|
||||
#include "complex_fem.hpp"
|
||||
@@ -34,6 +35,7 @@
|
||||
#include "staticcond.hpp"
|
||||
#include "tmop.hpp"
|
||||
#include "tmop_tools.hpp"
|
||||
#include "tmop_amr.hpp"
|
||||
#include "gslib.hpp"
|
||||
#include "restriction.hpp"
|
||||
#include "quadinterpolator.hpp"
|
||||
|
||||
+323
-50
@@ -58,9 +58,12 @@ DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim, Array<int> &dofs)
|
||||
|
||||
FiniteElementSpace::FiniteElementSpace()
|
||||
: mesh(NULL), fec(NULL), vdim(0), ordering(Ordering::byNODES),
|
||||
ndofs(0), nvdofs(0), nedofs(0), nfdofs(0), nbdofs(0), bdofs(NULL),
|
||||
elem_dof(NULL), bdr_elem_dof(NULL), face_dof(NULL),
|
||||
ndofs(0), nvdofs(0), nedofs(0), nfdofs(0), nbdofs(0),
|
||||
bdofs(NULL),
|
||||
elem_dof(NULL), elem_fos(NULL), bdr_elem_dof(NULL), bdr_elem_fos(NULL),
|
||||
face_dof(NULL),
|
||||
NURBSext(NULL), own_ext(false),
|
||||
DoFTrans(0), VDoFTrans(vdim, ordering),
|
||||
cP(NULL), cR(NULL), cR_hp(NULL), cP_is_set(false),
|
||||
Th(Operator::ANY_TYPE),
|
||||
sequence(0), mesh_sequence(0), orders_changed(false), relaxed_hp(false)
|
||||
@@ -69,6 +72,7 @@ FiniteElementSpace::FiniteElementSpace()
|
||||
FiniteElementSpace::FiniteElementSpace(const FiniteElementSpace &orig,
|
||||
Mesh *mesh,
|
||||
const FiniteElementCollection *fec)
|
||||
: VDoFTrans(orig.vdim, orig.ordering)
|
||||
{
|
||||
mesh = mesh ? mesh : orig.mesh;
|
||||
fec = fec ? fec : orig.fec;
|
||||
@@ -259,16 +263,36 @@ void FiniteElementSpace::AdjustVDofs (Array<int> &vdofs)
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs) const
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
GetElementDofs(i, vdofs);
|
||||
DofTransformation * doftrans = GetElementDofs(i, vdofs);
|
||||
DofsToVDofs(vdofs);
|
||||
if (vdim == 1 || doftrans == NULL)
|
||||
{
|
||||
return doftrans;
|
||||
}
|
||||
else
|
||||
{
|
||||
VDoFTrans.SetDofTransformation(*doftrans);
|
||||
return &VDoFTrans;
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs) const
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs) const
|
||||
{
|
||||
GetBdrElementDofs(i, vdofs);
|
||||
DofTransformation * doftrans = GetBdrElementDofs(i, vdofs);
|
||||
DofsToVDofs(vdofs);
|
||||
if (vdim == 1 || doftrans == NULL)
|
||||
{
|
||||
return doftrans;
|
||||
}
|
||||
else
|
||||
{
|
||||
VDoFTrans.SetDofTransformation(*doftrans);
|
||||
return &VDoFTrans;
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetFaceVDofs(int i, Array<int> &vdofs) const
|
||||
@@ -307,21 +331,39 @@ void FiniteElementSpace::BuildElementToDofTable() const
|
||||
|
||||
// TODO: can we call GetElementDofs only once per element?
|
||||
Table *el_dof = new Table;
|
||||
Table *el_fos = (mesh->Dimension() > 2) ? (new Table) : NULL;
|
||||
Array<int> dofs;
|
||||
Array<int> F, Fo;
|
||||
el_dof -> MakeI (mesh -> GetNE());
|
||||
if (el_fos) { el_fos -> MakeI (mesh -> GetNE()); }
|
||||
for (int i = 0; i < mesh -> GetNE(); i++)
|
||||
{
|
||||
GetElementDofs (i, dofs);
|
||||
el_dof -> AddColumnsInRow (i, dofs.Size());
|
||||
|
||||
if (el_fos)
|
||||
{
|
||||
mesh->GetElementFaces(i, F, Fo);
|
||||
el_fos -> AddColumnsInRow (i, Fo.Size());
|
||||
}
|
||||
}
|
||||
el_dof -> MakeJ();
|
||||
if (el_fos) { el_fos -> MakeJ(); }
|
||||
for (int i = 0; i < mesh -> GetNE(); i++)
|
||||
{
|
||||
GetElementDofs (i, dofs);
|
||||
el_dof -> AddConnections (i, (int *)dofs, dofs.Size());
|
||||
|
||||
if (el_fos)
|
||||
{
|
||||
mesh->GetElementFaces(i, F, Fo);
|
||||
el_fos -> AddConnections (i, (int *)Fo, Fo.Size());
|
||||
}
|
||||
}
|
||||
el_dof -> ShiftUpI();
|
||||
if (el_fos) { el_fos -> ShiftUpI(); }
|
||||
elem_dof = el_dof;
|
||||
elem_fos = el_fos;
|
||||
}
|
||||
|
||||
void FiniteElementSpace::BuildBdrElementToDofTable() const
|
||||
@@ -375,7 +417,9 @@ void FiniteElementSpace::BuildFaceToDofTable() const
|
||||
void FiniteElementSpace::RebuildElementToDofTable()
|
||||
{
|
||||
delete elem_dof;
|
||||
delete elem_fos;
|
||||
elem_dof = NULL;
|
||||
elem_fos = NULL;
|
||||
BuildElementToDofTable();
|
||||
}
|
||||
|
||||
@@ -1315,8 +1359,10 @@ const FaceQuadratureInterpolator
|
||||
|
||||
SparseMatrix *FiniteElementSpace::RefinementMatrix_main(
|
||||
const int coarse_ndofs, const Table &coarse_elem_dof,
|
||||
const DenseTensor localP[]) const
|
||||
const Table *coarse_elem_fos, const DenseTensor localP[]) const
|
||||
{
|
||||
/// TODO: Implement DofTransformation support
|
||||
|
||||
MFEM_VERIFY(mesh->GetLastOperation() == Mesh::REFINE, "");
|
||||
|
||||
Array<int> dofs, coarse_dofs, coarse_vdofs;
|
||||
@@ -1399,7 +1445,8 @@ void FiniteElementSpace::GetLocalRefinementMatrices(
|
||||
}
|
||||
|
||||
SparseMatrix* FiniteElementSpace::RefinementMatrix(int old_ndofs,
|
||||
const Table* old_elem_dof)
|
||||
const Table* old_elem_dof,
|
||||
const Table* old_elem_fos)
|
||||
{
|
||||
MFEM_VERIFY(GetNE() >= old_elem_dof->Size(),
|
||||
"Previous mesh is not coarser.");
|
||||
@@ -1412,13 +1459,16 @@ SparseMatrix* FiniteElementSpace::RefinementMatrix(int old_ndofs,
|
||||
GetLocalRefinementMatrices(elem_geoms[i], localP[elem_geoms[i]]);
|
||||
}
|
||||
|
||||
return RefinementMatrix_main(old_ndofs, *old_elem_dof, localP);
|
||||
return RefinementMatrix_main(old_ndofs, *old_elem_dof, old_elem_fos,
|
||||
localP);
|
||||
}
|
||||
|
||||
FiniteElementSpace::RefinementOperator::RefinementOperator
|
||||
(const FiniteElementSpace* fespace, Table* old_elem_dof, int old_ndofs)
|
||||
(const FiniteElementSpace* fespace, Table* old_elem_dof, Table* old_elem_fos,
|
||||
int old_ndofs)
|
||||
: fespace(fespace)
|
||||
, old_elem_dof(old_elem_dof)
|
||||
, old_elem_fos(old_elem_fos)
|
||||
{
|
||||
MFEM_VERIFY(fespace->GetNE() >= old_elem_dof->Size(),
|
||||
"Previous mesh is not coarser.");
|
||||
@@ -1432,12 +1482,14 @@ FiniteElementSpace::RefinementOperator::RefinementOperator
|
||||
{
|
||||
fespace->GetLocalRefinementMatrices(elem_geoms[i], localP[elem_geoms[i]]);
|
||||
}
|
||||
|
||||
ConstructDoFTrans();
|
||||
}
|
||||
|
||||
FiniteElementSpace::RefinementOperator::RefinementOperator(
|
||||
const FiniteElementSpace *fespace, const FiniteElementSpace *coarse_fes)
|
||||
: Operator(fespace->GetVSize(), coarse_fes->GetVSize()),
|
||||
fespace(fespace), old_elem_dof(NULL)
|
||||
fespace(fespace), old_elem_dof(NULL), old_elem_fos(NULL)
|
||||
{
|
||||
Mesh::GeometryList elem_geoms(*fespace->GetMesh());
|
||||
|
||||
@@ -1449,11 +1501,50 @@ FiniteElementSpace::RefinementOperator::RefinementOperator(
|
||||
|
||||
// Make a copy of the coarse elem_dof Table.
|
||||
old_elem_dof = new Table(coarse_fes->GetElementToDofTable());
|
||||
|
||||
// Make a copy of the coarse elem_fos Table if it exists.
|
||||
if (coarse_fes->GetElementToFaceOrientationTable())
|
||||
{
|
||||
old_elem_fos = new Table(*coarse_fes->GetElementToFaceOrientationTable());
|
||||
}
|
||||
|
||||
ConstructDoFTrans();
|
||||
}
|
||||
|
||||
FiniteElementSpace::RefinementOperator::~RefinementOperator()
|
||||
{
|
||||
delete old_elem_dof;
|
||||
delete old_elem_fos;
|
||||
}
|
||||
|
||||
void FiniteElementSpace::RefinementOperator
|
||||
::ConstructDoFTrans()
|
||||
{
|
||||
old_DoFTrans.SetSize(Geometry::NUM_GEOMETRIES);
|
||||
for (int i=0; i<old_DoFTrans.Size(); i++)
|
||||
{
|
||||
old_DoFTrans[i] = NULL;
|
||||
}
|
||||
|
||||
const FiniteElementCollection *fec = fespace->FEColl();
|
||||
if (dynamic_cast<const ND_FECollection*>(fec))
|
||||
{
|
||||
const FiniteElement * nd_tri =
|
||||
fec->FiniteElementForGeometry(Geometry::TRIANGLE);
|
||||
if (nd_tri)
|
||||
{
|
||||
old_DoFTrans[Geometry::TRIANGLE] =
|
||||
new ND_TriDofTransformation(nd_tri->GetOrder());
|
||||
}
|
||||
|
||||
const FiniteElement * nd_tet =
|
||||
fec->FiniteElementForGeometry(Geometry::TETRAHEDRON);
|
||||
if (nd_tet)
|
||||
{
|
||||
old_DoFTrans[Geometry::TETRAHEDRON] =
|
||||
new ND_TetDofTransformation(nd_tet->GetOrder());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FiniteElementSpace::RefinementOperator
|
||||
@@ -1462,7 +1553,7 @@ void FiniteElementSpace::RefinementOperator
|
||||
Mesh* mesh = fespace->GetMesh();
|
||||
const CoarseFineTransformations &rtrans = mesh->GetRefinementTransforms();
|
||||
|
||||
Array<int> dofs, vdofs, old_dofs, old_vdofs;
|
||||
Array<int> dofs, vdofs, old_dofs, old_vdofs, old_Fo;
|
||||
|
||||
int vdim = fespace->GetVDim();
|
||||
int old_ndofs = width / vdim;
|
||||
@@ -1477,18 +1568,53 @@ void FiniteElementSpace::RefinementOperator
|
||||
|
||||
subY.SetSize(lP.Height());
|
||||
|
||||
fespace->GetElementDofs(k, dofs);
|
||||
DofTransformation *doftrans = fespace->GetElementDofs(k, dofs);
|
||||
old_elem_dof->GetRow(emb.parent, old_dofs);
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
if (!doftrans)
|
||||
{
|
||||
dofs.Copy(vdofs);
|
||||
fespace->DofsToVDofs(vd, vdofs);
|
||||
old_dofs.Copy(old_vdofs);
|
||||
fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
|
||||
x.GetSubVector(old_vdofs, subX);
|
||||
lP.Mult(subX, subY);
|
||||
y.SetSubVector(vdofs, subY);
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
dofs.Copy(vdofs);
|
||||
fespace->DofsToVDofs(vd, vdofs);
|
||||
old_dofs.Copy(old_vdofs);
|
||||
fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
|
||||
x.GetSubVector(old_vdofs, subX);
|
||||
lP.Mult(subX, subY);
|
||||
y.SetSubVector(vdofs, subY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
old_elem_fos->GetRow(emb.parent, old_Fo);
|
||||
old_DoFTrans[geom]->SetFaceOrientations(old_Fo);
|
||||
|
||||
DofTransformation *new_doftrans = NULL;
|
||||
VDofTransformation *vdoftrans =
|
||||
dynamic_cast<VDofTransformation*>(doftrans);
|
||||
if (vdoftrans)
|
||||
{
|
||||
new_doftrans = doftrans;
|
||||
doftrans = vdoftrans->GetDofTransformation();
|
||||
}
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
dofs.Copy(vdofs);
|
||||
fespace->DofsToVDofs(vd, vdofs);
|
||||
old_dofs.Copy(old_vdofs);
|
||||
fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
|
||||
x.GetSubVector(old_vdofs, subX);
|
||||
old_DoFTrans[geom]->InvTransformPrimal(subX);
|
||||
lP.Mult(subX, subY);
|
||||
doftrans->TransformPrimal(subY);
|
||||
y.SetSubVector(vdofs, subY);
|
||||
}
|
||||
|
||||
if (vdoftrans)
|
||||
{
|
||||
doftrans = new_doftrans;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1504,12 +1630,12 @@ void FiniteElementSpace::RefinementOperator
|
||||
Array<char> processed(fespace->GetVSize());
|
||||
processed = 0;
|
||||
|
||||
Array<int> f_dofs, c_dofs, f_vdofs, c_vdofs;
|
||||
Array<int> f_dofs, c_dofs, f_vdofs, c_vdofs, old_Fo;
|
||||
|
||||
int vdim = fespace->GetVDim();
|
||||
int old_ndofs = width / vdim;
|
||||
|
||||
Vector subY, subX;
|
||||
Vector subY, subX, subYt, subXt;
|
||||
|
||||
for (int k = 0; k < mesh->GetNE(); k++)
|
||||
{
|
||||
@@ -1517,30 +1643,77 @@ void FiniteElementSpace::RefinementOperator
|
||||
const Geometry::Type geom = mesh->GetElementBaseGeometry(k);
|
||||
const DenseMatrix &lP = localP[geom](emb.matrix);
|
||||
|
||||
fespace->GetElementDofs(k, f_dofs);
|
||||
DofTransformation * doftrans = fespace->GetElementDofs(k, f_dofs);
|
||||
old_elem_dof->GetRow(emb.parent, c_dofs);
|
||||
|
||||
subY.SetSize(lP.Width());
|
||||
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
if (!doftrans)
|
||||
{
|
||||
f_dofs.Copy(f_vdofs);
|
||||
fespace->DofsToVDofs(vd, f_vdofs);
|
||||
c_dofs.Copy(c_vdofs);
|
||||
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
|
||||
subY.SetSize(lP.Width());
|
||||
|
||||
x.GetSubVector(f_vdofs, subX);
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
if (processed[DecodeDof(f_dofs[p])])
|
||||
f_dofs.Copy(f_vdofs);
|
||||
fespace->DofsToVDofs(vd, f_vdofs);
|
||||
c_dofs.Copy(c_vdofs);
|
||||
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
|
||||
|
||||
x.GetSubVector(f_vdofs, subX);
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
{
|
||||
subX[p] = 0.0;
|
||||
if (processed[DecodeDof(f_dofs[p])])
|
||||
{
|
||||
subX[p] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
lP.MultTranspose(subX, subY);
|
||||
y.AddElementVector(c_vdofs, subY);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
subYt.SetSize(lP.Width());
|
||||
|
||||
old_elem_fos->GetRow(emb.parent, old_Fo);
|
||||
old_DoFTrans[geom]->SetFaceOrientations(old_Fo);
|
||||
|
||||
DofTransformation *new_doftrans = NULL;
|
||||
VDofTransformation *vdoftrans =
|
||||
dynamic_cast<VDofTransformation*>(doftrans);
|
||||
if (vdoftrans)
|
||||
{
|
||||
new_doftrans = doftrans;
|
||||
doftrans = vdoftrans->GetDofTransformation();
|
||||
}
|
||||
|
||||
lP.MultTranspose(subX, subY);
|
||||
y.AddElementVector(c_vdofs, subY);
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
f_dofs.Copy(f_vdofs);
|
||||
fespace->DofsToVDofs(vd, f_vdofs);
|
||||
c_dofs.Copy(c_vdofs);
|
||||
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
|
||||
|
||||
x.GetSubVector(f_vdofs, subX);
|
||||
old_DoFTrans[geom]->InvTransformPrimal(subX);
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
{
|
||||
if (processed[DecodeDof(f_dofs[p])])
|
||||
{
|
||||
subX[p] = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
lP.MultTranspose(subX, subY);
|
||||
doftrans->TransformPrimal(subY);
|
||||
y.AddElementVector(c_vdofs, subY);
|
||||
}
|
||||
|
||||
if (vdoftrans)
|
||||
{
|
||||
doftrans = new_doftrans;
|
||||
}
|
||||
}
|
||||
|
||||
for (int p = 0; p < f_dofs.Size(); ++p)
|
||||
@@ -1550,6 +1723,7 @@ void FiniteElementSpace::RefinementOperator
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: Implement DofTransformation support
|
||||
FiniteElementSpace::DerefinementOperator::DerefinementOperator(
|
||||
const FiniteElementSpace *f_fes, const FiniteElementSpace *c_fes,
|
||||
BilinearFormIntegrator *mass_integ)
|
||||
@@ -1707,8 +1881,11 @@ void FiniteElementSpace::GetLocalDerefinementMatrices(Geometry::Type geom,
|
||||
}
|
||||
|
||||
SparseMatrix* FiniteElementSpace::DerefinementMatrix(int old_ndofs,
|
||||
const Table* old_elem_dof)
|
||||
const Table* old_elem_dof,
|
||||
const Table* old_elem_fos)
|
||||
{
|
||||
/// TODO: Implement DofTransformation support
|
||||
|
||||
MFEM_VERIFY(Nonconforming(), "Not implemented for conforming meshes.");
|
||||
MFEM_VERIFY(old_ndofs, "Missing previous (finer) space.");
|
||||
MFEM_VERIFY(ndofs <= old_ndofs, "Previous space is not finer.");
|
||||
@@ -1814,6 +1991,7 @@ void FiniteElementSpace::Constructor(Mesh *mesh, NURBSExtension *NURBSext,
|
||||
this->ordering = (Ordering::Type) ordering;
|
||||
|
||||
elem_dof = NULL;
|
||||
elem_fos = NULL;
|
||||
face_dof = NULL;
|
||||
|
||||
sequence = 0;
|
||||
@@ -1841,6 +2019,8 @@ void FiniteElementSpace::Constructor(Mesh *mesh, NURBSExtension *NURBSext,
|
||||
UpdateNURBS();
|
||||
cP = cR = cR_hp = NULL;
|
||||
cP_is_set = false;
|
||||
|
||||
ConstructDoFTrans();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1848,9 +2028,41 @@ void FiniteElementSpace::Constructor(Mesh *mesh, NURBSExtension *NURBSext,
|
||||
own_ext = 0;
|
||||
Construct();
|
||||
}
|
||||
|
||||
BuildElementToDofTable();
|
||||
}
|
||||
|
||||
void FiniteElementSpace::ConstructDoFTrans()
|
||||
{
|
||||
DestroyDoFTrans();
|
||||
|
||||
VDoFTrans.SetVDim(vdim);
|
||||
DoFTrans.SetSize(Geometry::NUM_GEOMETRIES);
|
||||
for (int i=0; i<DoFTrans.Size(); i++)
|
||||
{
|
||||
DoFTrans[i] = NULL;
|
||||
}
|
||||
if (mesh->Dimension() < 3) { return; }
|
||||
if (dynamic_cast<const ND_FECollection*>(fec))
|
||||
{
|
||||
const FiniteElement * nd_tri =
|
||||
fec->FiniteElementForGeometry(Geometry::TRIANGLE);
|
||||
if (nd_tri)
|
||||
{
|
||||
DoFTrans[Geometry::TRIANGLE] =
|
||||
new ND_TriDofTransformation(nd_tri->GetOrder());
|
||||
}
|
||||
|
||||
const FiniteElement * nd_tet =
|
||||
fec->FiniteElementForGeometry(Geometry::TETRAHEDRON);
|
||||
if (nd_tet)
|
||||
{
|
||||
DoFTrans[Geometry::TETRAHEDRON] =
|
||||
new ND_TetDofTransformation(nd_tet->GetOrder());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NURBSExtension *FiniteElementSpace::StealNURBSext()
|
||||
{
|
||||
if (NURBSext && !own_ext)
|
||||
@@ -1946,7 +2158,9 @@ void FiniteElementSpace::Construct()
|
||||
"Variable order space requires a nonconforming mesh.");
|
||||
|
||||
elem_dof = NULL;
|
||||
elem_fos = NULL;
|
||||
bdr_elem_dof = NULL;
|
||||
bdr_elem_fos = NULL;
|
||||
face_dof = NULL;
|
||||
|
||||
ndofs = 0;
|
||||
@@ -2044,6 +2258,8 @@ void FiniteElementSpace::Construct()
|
||||
|
||||
ndofs = nvdofs + nedofs + nfdofs + nbdofs;
|
||||
|
||||
ConstructDoFTrans();
|
||||
|
||||
// record the current mesh sequence number to detect refinement etc.
|
||||
mesh_sequence = mesh->GetSequence();
|
||||
|
||||
@@ -2295,14 +2511,22 @@ int FiniteElementSpace::GetNVariants(int entity, int index) const
|
||||
static const char* msg_orders_changed =
|
||||
"Element orders changed, you need to Update() the space first.";
|
||||
|
||||
void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
if (elem_dof)
|
||||
{
|
||||
elem_dof->GetRow(elem, dofs);
|
||||
return;
|
||||
|
||||
if (DoFTrans[mesh->GetElementBaseGeometry(elem)])
|
||||
{
|
||||
Array<int> Fo;
|
||||
elem_fos -> GetRow (elem, Fo);
|
||||
DoFTrans[mesh->GetElementBaseGeometry(elem)]->SetFaceOrientations(Fo);
|
||||
}
|
||||
return DoFTrans[mesh->GetElementBaseGeometry(elem)];
|
||||
}
|
||||
|
||||
Array<int> V, E, Eo, F, Fo; // TODO: LocalArray
|
||||
@@ -2326,6 +2550,11 @@ void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
|
||||
{
|
||||
nfd += fec->GetNumDof(mesh->GetFaceGeometry(F[i]), order);
|
||||
}
|
||||
if (DoFTrans[mesh->GetElementBaseGeometry(elem)])
|
||||
{
|
||||
DoFTrans[mesh->GetElementBaseGeometry(elem)]
|
||||
-> SetFaceOrientations(Fo);
|
||||
}
|
||||
}
|
||||
|
||||
dofs.SetSize(0);
|
||||
@@ -2383,6 +2612,7 @@ void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
|
||||
dofs.Append(bbase + j);
|
||||
}
|
||||
}
|
||||
return DoFTrans[mesh->GetElementBaseGeometry(elem)];
|
||||
}
|
||||
|
||||
const FiniteElement *FiniteElementSpace::GetFE(int i) const
|
||||
@@ -2415,18 +2645,27 @@ const FiniteElement *FiniteElementSpace::GetFE(int i) const
|
||||
return FE;
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
|
||||
DofTransformation *
|
||||
FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
|
||||
{
|
||||
MFEM_VERIFY(!orders_changed, msg_orders_changed);
|
||||
|
||||
if (bdr_elem_dof)
|
||||
{
|
||||
bdr_elem_dof->GetRow(bel, dofs);
|
||||
return;
|
||||
|
||||
if (DoFTrans[mesh->GetBdrElementBaseGeometry(bel)])
|
||||
{
|
||||
Array<int> Fo;
|
||||
bdr_elem_fos -> GetRow (bel, Fo);
|
||||
DoFTrans[mesh->GetBdrElementBaseGeometry(bel)]->
|
||||
SetFaceOrientations(Fo);
|
||||
}
|
||||
return DoFTrans[mesh->GetBdrElementBaseGeometry(bel)];
|
||||
}
|
||||
|
||||
Array<int> V, E, Eo; // TODO: LocalArray
|
||||
int F, Fo;
|
||||
Array<int> V, E, Eo, Fo; // TODO: LocalArray
|
||||
int F, oF;
|
||||
|
||||
int dim = mesh->Dimension();
|
||||
auto geom = mesh->GetBdrElementGeometry(bel);
|
||||
@@ -2445,7 +2684,17 @@ void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
|
||||
|
||||
if (nv) { mesh->GetBdrElementVertices(bel, V); }
|
||||
if (ne) { mesh->GetBdrElementEdges(bel, E, Eo); }
|
||||
if (nf) { mesh->GetBdrElementFace(bel, &F, &Fo); }
|
||||
if (nf)
|
||||
{
|
||||
mesh->GetBdrElementFace(bel, &F, &oF);
|
||||
|
||||
if (DoFTrans[mesh->GetBdrElementBaseGeometry(bel)])
|
||||
{
|
||||
Fo.Append(oF);
|
||||
DoFTrans[mesh->GetBdrElementBaseGeometry(bel)]->
|
||||
SetFaceOrientations(Fo);
|
||||
}
|
||||
}
|
||||
|
||||
dofs.SetSize(0);
|
||||
dofs.Reserve(nv*V.Size() + ne*E.Size() + nf);
|
||||
@@ -2478,13 +2727,15 @@ void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
|
||||
if (nf) // face DOFs
|
||||
{
|
||||
int fbase = (var_face_dofs.Size() > 0) ? FindFaceDof(F, nf) : F*nf;
|
||||
const int *ind = fec->GetDofOrdering(geom, order, Fo);
|
||||
const int *ind = fec->GetDofOrdering(geom, order, oF);
|
||||
|
||||
for (int j = 0; j < nf; j++)
|
||||
{
|
||||
dofs.Append(EncodeDof(nvdofs + nedofs + fbase, ind[j]));
|
||||
}
|
||||
}
|
||||
|
||||
return DoFTrans[mesh->GetBdrElementBaseGeometry(bel)];
|
||||
}
|
||||
|
||||
int FiniteElementSpace::GetFaceDofs(int face, Array<int> &dofs,
|
||||
@@ -2794,6 +3045,8 @@ void FiniteElementSpace::Destroy()
|
||||
}
|
||||
E2BFQ_array.SetSize(0);
|
||||
|
||||
DestroyDoFTrans();
|
||||
|
||||
dof_elem_array.DeleteAll();
|
||||
dof_ldof_array.DeleteAll();
|
||||
|
||||
@@ -2806,7 +3059,9 @@ void FiniteElementSpace::Destroy()
|
||||
else
|
||||
{
|
||||
delete elem_dof;
|
||||
delete elem_fos;
|
||||
delete bdr_elem_dof;
|
||||
delete bdr_elem_fos;
|
||||
delete face_dof;
|
||||
|
||||
delete [] bdofs;
|
||||
@@ -2814,6 +3069,15 @@ void FiniteElementSpace::Destroy()
|
||||
ceed::RemoveBasisAndRestriction(this);
|
||||
}
|
||||
|
||||
void FiniteElementSpace::DestroyDoFTrans()
|
||||
{
|
||||
for (int i = 0; i < DoFTrans.Size(); i++)
|
||||
{
|
||||
delete DoFTrans[i];
|
||||
}
|
||||
DoFTrans.SetSize(0);
|
||||
}
|
||||
|
||||
void FiniteElementSpace::GetTransferOperator(
|
||||
const FiniteElementSpace &coarse_fes, OperatorHandle &T) const
|
||||
{
|
||||
@@ -2831,6 +3095,8 @@ void FiniteElementSpace::GetTransferOperator(
|
||||
}
|
||||
T.Reset(RefinementMatrix_main(coarse_fes.GetNDofs(),
|
||||
coarse_fes.GetElementToDofTable(),
|
||||
coarse_fes.
|
||||
GetElementToFaceOrientationTable(),
|
||||
localP));
|
||||
}
|
||||
else
|
||||
@@ -2933,6 +3199,7 @@ void FiniteElementSpace::Update(bool want_transform)
|
||||
}
|
||||
|
||||
Table* old_elem_dof = NULL;
|
||||
Table* old_elem_fos = NULL;
|
||||
int old_ndofs;
|
||||
bool old_orders_changed = orders_changed;
|
||||
|
||||
@@ -2940,7 +3207,9 @@ void FiniteElementSpace::Update(bool want_transform)
|
||||
if (want_transform)
|
||||
{
|
||||
old_elem_dof = elem_dof;
|
||||
old_elem_fos = elem_fos;
|
||||
elem_dof = NULL;
|
||||
elem_fos = NULL;
|
||||
old_ndofs = ndofs;
|
||||
}
|
||||
|
||||
@@ -2966,15 +3235,18 @@ void FiniteElementSpace::Update(bool want_transform)
|
||||
{
|
||||
if (Th.Type() != Operator::MFEM_SPARSEMAT)
|
||||
{
|
||||
Th.Reset(new RefinementOperator(this, old_elem_dof, old_ndofs));
|
||||
Th.Reset(new RefinementOperator(this, old_elem_dof,
|
||||
old_elem_fos, old_ndofs));
|
||||
// The RefinementOperator takes ownership of 'old_elem_dof', so
|
||||
// we no longer own it:
|
||||
old_elem_dof = NULL;
|
||||
old_elem_fos = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// calculate fully assembled matrix
|
||||
Th.Reset(RefinementMatrix(old_ndofs, old_elem_dof));
|
||||
Th.Reset(RefinementMatrix(old_ndofs, old_elem_dof,
|
||||
old_elem_fos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2982,7 +3254,7 @@ void FiniteElementSpace::Update(bool want_transform)
|
||||
case Mesh::DEREFINE:
|
||||
{
|
||||
BuildConformingInterpolation();
|
||||
Th.Reset(DerefinementMatrix(old_ndofs, old_elem_dof));
|
||||
Th.Reset(DerefinementMatrix(old_ndofs, old_elem_dof, old_elem_fos));
|
||||
if (cP && cR)
|
||||
{
|
||||
Th.SetOperatorOwner(false);
|
||||
@@ -2997,6 +3269,7 @@ void FiniteElementSpace::Update(bool want_transform)
|
||||
}
|
||||
|
||||
delete old_elem_dof;
|
||||
delete old_elem_fos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-7
@@ -16,6 +16,7 @@
|
||||
#include "../linalg/sparsemat.hpp"
|
||||
#include "../mesh/mesh.hpp"
|
||||
#include "fe_coll.hpp"
|
||||
#include "doftrans.hpp"
|
||||
#include "restriction.hpp"
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
@@ -128,7 +129,9 @@ protected:
|
||||
|
||||
// precalculated DOFs for each element, boundary element, and face
|
||||
mutable Table *elem_dof; // owned (except in NURBS FE space)
|
||||
mutable Table *elem_fos; // face orientations by element index
|
||||
mutable Table *bdr_elem_dof; // owned (except in NURBS FE space)
|
||||
mutable Table *bdr_elem_fos; // bdr face orientations by bdr element index
|
||||
mutable Table *face_dof; // owned; in var-order space contains variant 0 DOFs
|
||||
|
||||
Array<int> dof_elem_array, dof_ldof_array;
|
||||
@@ -137,6 +140,9 @@ protected:
|
||||
int own_ext;
|
||||
mutable Array<int> face_to_be; // NURBS FE space only
|
||||
|
||||
Array<DofTransformation*> DoFTrans;
|
||||
mutable VDofTransformation VDoFTrans;
|
||||
|
||||
/** Matrix representing the prolongation from the global conforming dofs to
|
||||
a set of intermediate partially conforming dofs, e.g. the dofs associated
|
||||
with a "cut" space on a non-conforming mesh. */
|
||||
@@ -189,6 +195,9 @@ protected:
|
||||
void Construct();
|
||||
void Destroy();
|
||||
|
||||
void ConstructDoFTrans();
|
||||
void DestroyDoFTrans();
|
||||
|
||||
void BuildElementToDofTable() const;
|
||||
void BuildBdrElementToDofTable() const;
|
||||
void BuildFaceToDofTable() const;
|
||||
@@ -283,12 +292,19 @@ protected:
|
||||
const FiniteElementSpace* fespace;
|
||||
DenseTensor localP[Geometry::NumGeom];
|
||||
Table* old_elem_dof; // Owned.
|
||||
Table* old_elem_fos; // Owned.
|
||||
|
||||
Array<DofTransformation*> old_DoFTrans;
|
||||
mutable VDofTransformation old_VDoFTrans;
|
||||
|
||||
void ConstructDoFTrans();
|
||||
|
||||
public:
|
||||
/** Construct the operator based on the elem_dof table of the original
|
||||
(coarse) space. The class takes ownership of the table. */
|
||||
RefinementOperator(const FiniteElementSpace* fespace,
|
||||
Table *old_elem_dof/*takes ownership*/, int old_ndofs);
|
||||
Table *old_elem_dof/*takes ownership*/,
|
||||
Table *old_elem_fos/*takes ownership*/, int old_ndofs);
|
||||
RefinementOperator(const FiniteElementSpace *fespace,
|
||||
const FiniteElementSpace *coarse_fes);
|
||||
virtual void Mult(const Vector &x, Vector &y) const;
|
||||
@@ -302,6 +318,7 @@ protected:
|
||||
const FiniteElementSpace *fine_fes; // Not owned.
|
||||
DenseTensor localR[Geometry::NumGeom];
|
||||
Table *coarse_elem_dof; // Owned.
|
||||
// Table *coarse_elem_fos; // Owned.
|
||||
Table coarse_to_fine;
|
||||
Array<int> coarse_to_ref_type;
|
||||
Array<Geometry::Type> ref_type_to_geom;
|
||||
@@ -323,6 +340,7 @@ protected:
|
||||
the same vector dimension, vdim. */
|
||||
SparseMatrix *RefinementMatrix_main(const int coarse_ndofs,
|
||||
const Table &coarse_elem_dof,
|
||||
const Table *coarse_elem_fos,
|
||||
const DenseTensor localP[]) const;
|
||||
|
||||
void GetLocalRefinementMatrices(Geometry::Type geom,
|
||||
@@ -333,10 +351,12 @@ protected:
|
||||
/** Calculate explicit GridFunction interpolation matrix (after mesh
|
||||
refinement). NOTE: consider using the RefinementOperator class instead
|
||||
of the fully assembled matrix, which can take a lot of memory. */
|
||||
SparseMatrix* RefinementMatrix(int old_ndofs, const Table* old_elem_dof);
|
||||
SparseMatrix* RefinementMatrix(int old_ndofs, const Table* old_elem_dof,
|
||||
const Table* old_elem_fos);
|
||||
|
||||
/// Calculate GridFunction restriction matrix after mesh derefinement.
|
||||
SparseMatrix* DerefinementMatrix(int old_ndofs, const Table* old_elem_dof);
|
||||
SparseMatrix* DerefinementMatrix(int old_ndofs, const Table* old_elem_dof,
|
||||
const Table* old_elem_fos);
|
||||
|
||||
/** @brief Return in @a localP the local refinement matrices that map
|
||||
between fespaces after mesh refinement. */
|
||||
@@ -614,10 +634,11 @@ public:
|
||||
int GetBdrAttribute(int i) const { return mesh->GetBdrAttribute(i); }
|
||||
|
||||
/// Returns indices of degrees of freedom of element 'elem'.
|
||||
virtual void GetElementDofs(int elem, Array<int> &dofs) const;
|
||||
virtual DofTransformation *GetElementDofs(int elem, Array<int> &dofs) const;
|
||||
|
||||
/// Returns indices of degrees of freedom for boundary element 'bel'.
|
||||
virtual void GetBdrElementDofs(int bel, Array<int> &dofs) const;
|
||||
virtual DofTransformation *GetBdrElementDofs(int bel,
|
||||
Array<int> &dofs) const;
|
||||
|
||||
/** @brief Returns the indices of the degrees of freedom for the specified
|
||||
face, including the DOFs for the edges and the vertices of the face. */
|
||||
@@ -666,10 +687,10 @@ public:
|
||||
static void AdjustVDofs(Array<int> &vdofs);
|
||||
|
||||
/// Returns indexes of degrees of freedom in array dofs for i'th element.
|
||||
void GetElementVDofs(int i, Array<int> &vdofs) const;
|
||||
DofTransformation *GetElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// Returns indexes of degrees of freedom for i'th boundary element.
|
||||
void GetBdrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
DofTransformation *GetBdrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
|
||||
/// Returns indexes of degrees of freedom for i'th face element (2D and 3D).
|
||||
void GetFaceVDofs(int i, Array<int> &vdofs) const;
|
||||
@@ -695,6 +716,8 @@ public:
|
||||
is preserved. */
|
||||
void ReorderElementToDofTable();
|
||||
|
||||
const Table *GetElementToFaceOrientationTable() const { return elem_fos; }
|
||||
|
||||
/** @brief Return a reference to the internal Table that stores the lists of
|
||||
scalar dofs, for each mesh element, as returned by GetElementDofs(). */
|
||||
const Table &GetElementToDofTable() const { return *elem_dof; }
|
||||
|
||||
+262
-7
@@ -11,15 +11,19 @@
|
||||
|
||||
#include "fem.hpp"
|
||||
#include "../mesh/wedge.hpp"
|
||||
#include "../mesh/pyramid.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
const char *Geometry::Name[NumGeom] =
|
||||
{ "Point", "Segment", "Triangle", "Square", "Tetrahedron", "Cube", "Prism" };
|
||||
{
|
||||
"Point", "Segment", "Triangle", "Square", "Tetrahedron", "Cube", "Prism",
|
||||
"Pyramid"
|
||||
};
|
||||
|
||||
const double Geometry::Volume[NumGeom] =
|
||||
{ 1.0, 1.0, 0.5, 1.0, 1./6, 1.0, 0.5 };
|
||||
{ 1.0, 1.0, 0.5, 1.0, 1./6, 1.0, 0.5, 1./3 };
|
||||
|
||||
Geometry::Geometry()
|
||||
{
|
||||
@@ -139,6 +143,28 @@ Geometry::Geometry()
|
||||
GeomVert[6]->IntPoint(5).y = 1.0;
|
||||
GeomVert[6]->IntPoint(5).z = 1.0;
|
||||
|
||||
// Vertices for Geometry::PYRAMID
|
||||
GeomVert[7] = new IntegrationRule(5);
|
||||
GeomVert[7]->IntPoint(0).x = 0.0;
|
||||
GeomVert[7]->IntPoint(0).y = 0.0;
|
||||
GeomVert[7]->IntPoint(0).z = 0.0;
|
||||
|
||||
GeomVert[7]->IntPoint(1).x = 1.0;
|
||||
GeomVert[7]->IntPoint(1).y = 0.0;
|
||||
GeomVert[7]->IntPoint(1).z = 0.0;
|
||||
|
||||
GeomVert[7]->IntPoint(2).x = 1.0;
|
||||
GeomVert[7]->IntPoint(2).y = 1.0;
|
||||
GeomVert[7]->IntPoint(2).z = 0.0;
|
||||
|
||||
GeomVert[7]->IntPoint(3).x = 0.0;
|
||||
GeomVert[7]->IntPoint(3).y = 1.0;
|
||||
GeomVert[7]->IntPoint(3).z = 0.0;
|
||||
|
||||
GeomVert[7]->IntPoint(4).x = 0.0;
|
||||
GeomVert[7]->IntPoint(4).y = 0.0;
|
||||
GeomVert[7]->IntPoint(4).z = 1.0;
|
||||
|
||||
GeomCenter[POINT].x = 0.0;
|
||||
GeomCenter[POINT].y = 0.0;
|
||||
GeomCenter[POINT].z = 0.0;
|
||||
@@ -167,6 +193,10 @@ Geometry::Geometry()
|
||||
GeomCenter[PRISM].y = 1.0 / 3.0;
|
||||
GeomCenter[PRISM].z = 0.5;
|
||||
|
||||
GeomCenter[PYRAMID].x = 0.375;
|
||||
GeomCenter[PYRAMID].y = 0.375;
|
||||
GeomCenter[PYRAMID].z = 0.25;
|
||||
|
||||
GeomToPerfGeomJac[POINT] = NULL;
|
||||
GeomToPerfGeomJac[SEGMENT] = new DenseMatrix(1);
|
||||
GeomToPerfGeomJac[TRIANGLE] = new DenseMatrix(2);
|
||||
@@ -174,6 +204,7 @@ Geometry::Geometry()
|
||||
GeomToPerfGeomJac[TETRAHEDRON] = new DenseMatrix(3);
|
||||
GeomToPerfGeomJac[CUBE] = new DenseMatrix(3);
|
||||
GeomToPerfGeomJac[PRISM] = new DenseMatrix(3);
|
||||
GeomToPerfGeomJac[PYRAMID] = new DenseMatrix(3);
|
||||
|
||||
PerfGeomToGeomJac[POINT] = NULL;
|
||||
PerfGeomToGeomJac[SEGMENT] = NULL;
|
||||
@@ -182,6 +213,7 @@ Geometry::Geometry()
|
||||
PerfGeomToGeomJac[TETRAHEDRON] = new DenseMatrix(3);
|
||||
PerfGeomToGeomJac[CUBE] = NULL;
|
||||
PerfGeomToGeomJac[PRISM] = new DenseMatrix(3);
|
||||
PerfGeomToGeomJac[PYRAMID] = new DenseMatrix(3);
|
||||
|
||||
GeomToPerfGeomJac[SEGMENT]->Diag(1.0, 1);
|
||||
{
|
||||
@@ -210,6 +242,14 @@ Geometry::Geometry()
|
||||
*GeomToPerfGeomJac[PRISM] = pri_T.Jacobian();
|
||||
CalcInverse(pri_T.Jacobian(), *PerfGeomToGeomJac[PRISM]);
|
||||
}
|
||||
{
|
||||
IsoparametricTransformation pyr_T;
|
||||
pyr_T.SetFE(&PyramidFE);
|
||||
GetPerfPointMat (PYRAMID, pyr_T.GetPointMat());
|
||||
pyr_T.SetIntPoint(&GeomCenter[PYRAMID]);
|
||||
*GeomToPerfGeomJac[PYRAMID] = pyr_T.Jacobian();
|
||||
CalcInverse(pyr_T.Jacobian(), *PerfGeomToGeomJac[PYRAMID]);
|
||||
}
|
||||
}
|
||||
|
||||
Geometry::~Geometry()
|
||||
@@ -233,6 +273,7 @@ const IntegrationRule * Geometry::GetVertices(int GeomType)
|
||||
case Geometry::TETRAHEDRON: return GeomVert[4];
|
||||
case Geometry::CUBE: return GeomVert[5];
|
||||
case Geometry::PRISM: return GeomVert[6];
|
||||
case Geometry::PYRAMID: return GeomVert[7];
|
||||
default:
|
||||
mfem_error ("Geometry::GetVertices(...)");
|
||||
}
|
||||
@@ -310,6 +351,25 @@ void Geometry::GetRandomPoint(int GeomType, IntegrationPoint &ip)
|
||||
ip.y = 1.0 - ip.y;
|
||||
}
|
||||
break;
|
||||
case Geometry::PYRAMID:
|
||||
ip.x = double(rand()) / RAND_MAX;
|
||||
ip.y = double(rand()) / RAND_MAX;
|
||||
ip.z = double(rand()) / RAND_MAX;
|
||||
if (ip.x + ip.z > 1.0 && ip.y < ip.x)
|
||||
{
|
||||
double x = ip.x;
|
||||
ip.x = ip.y;
|
||||
ip.y = 1.0 - ip.z;
|
||||
ip.z = 1.0 - x;
|
||||
}
|
||||
else if (ip.y + ip.z > 1.0)
|
||||
{
|
||||
double z = ip.z;
|
||||
ip.z = 1.0 - ip.y;
|
||||
ip.y = ip.x;
|
||||
ip.x = 1.0 - z;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Unknown type of reference element!");
|
||||
}
|
||||
@@ -371,6 +431,10 @@ bool Geometry::CheckPoint(int GeomType, const IntegrationPoint &ip)
|
||||
if (ip.x < 0.0 || ip.y < 0.0 || ip.x+ip.y > 1.0 ||
|
||||
ip.z < 0.0 || ip.z > 1.0) { return false; }
|
||||
break;
|
||||
case Geometry::PYRAMID:
|
||||
if (ip.x < 0.0 || ip.y < 0.0 || ip.x+ip.z > 1.0 || ip.y+ip.z > 1.0 ||
|
||||
ip.z < 0.0 || ip.z > 1.0) { return false; }
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Unknown type of reference element!");
|
||||
}
|
||||
@@ -441,6 +505,17 @@ bool Geometry::CheckPoint(int GeomType, const IntegrationPoint &ip, double eps)
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Geometry::PYRAMID:
|
||||
if (internal::FuzzyLT(ip.x, 0.0, eps)
|
||||
|| internal::FuzzyLT(ip.y, 0.0, eps)
|
||||
|| internal::FuzzyGT(ip.x+ip.z, 1.0, eps)
|
||||
|| internal::FuzzyGT(ip.y+ip.z, 1.0, eps)
|
||||
|| internal::FuzzyLT(ip.z, 0.0, eps)
|
||||
|| internal::FuzzyGT(ip.z, 1.0, eps) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Unknown type of reference element!");
|
||||
}
|
||||
@@ -555,6 +630,16 @@ bool Geometry::ProjectPoint(int GeomType, const IntegrationPoint &beg,
|
||||
double lbeg[5] = { beg.x, beg.y, beg.z, 1.0-beg.x-beg.y, 1.0-beg.z };
|
||||
return internal::IntersectSegment<5,3>(lbeg, lend, end);
|
||||
}
|
||||
case Geometry::PYRAMID:
|
||||
{
|
||||
double lend[6] = { end.x, end.y, end.z,
|
||||
1.0-end.x-end.z, 1.0-end.y-end.z, 1.0-end.z
|
||||
};
|
||||
double lbeg[6] = { beg.x, beg.y, beg.z,
|
||||
1.0-beg.x-beg.z, 1.0-beg.y-beg.z, 1.0-beg.z
|
||||
};
|
||||
return internal::IntersectSegment<6,3>(lbeg, lend, end);
|
||||
}
|
||||
default:
|
||||
MFEM_ABORT("Unknown type of reference element!");
|
||||
}
|
||||
@@ -652,6 +737,43 @@ bool Geometry::ProjectPoint(int GeomType, IntegrationPoint &ip)
|
||||
return in_tri && in_z;
|
||||
}
|
||||
|
||||
case PYRAMID:
|
||||
{
|
||||
if (ip.x < 0.0)
|
||||
{
|
||||
ip.x = 0.0;
|
||||
internal::ProjectTriangle(ip.y, ip.z);
|
||||
return false;
|
||||
}
|
||||
if (ip.y < 0.0)
|
||||
{
|
||||
ip.y = 0.0;
|
||||
internal::ProjectTriangle(ip.x, ip.z);
|
||||
return false;
|
||||
}
|
||||
if (ip.z < 0.0)
|
||||
{
|
||||
ip.z = 0.0;
|
||||
if (ip.x > 1.0) { ip.x = 1.0; }
|
||||
if (ip.y > 1.0) { ip.y = 1.0; }
|
||||
return false;
|
||||
}
|
||||
if (ip.x >= ip.y)
|
||||
{
|
||||
bool in_y = true;
|
||||
bool in_tri = internal::ProjectTriangle(ip.x, ip.z);
|
||||
if (ip.y > ip.z) { in_y = false; ip.y = ip.z; }
|
||||
return in_tri && in_y;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool in_x = true;
|
||||
bool in_tri = internal::ProjectTriangle(ip.y, ip.z);
|
||||
if (ip.x > ip.z) { in_x = false; ip.x = ip.z; }
|
||||
return in_tri && in_x;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
MFEM_ABORT("Reference element type is not supported!");
|
||||
}
|
||||
@@ -726,6 +848,17 @@ void Geometry::GetPerfPointMat(int GeomType, DenseMatrix &pm)
|
||||
}
|
||||
break;
|
||||
|
||||
case Geometry::PYRAMID:
|
||||
{
|
||||
pm.SetSize (3, 5);
|
||||
pm(0,0) = 0.0; pm(1,0) = 0.0; pm(2,0) = 0.0;
|
||||
pm(0,1) = 1.0; pm(1,1) = 0.0; pm(2,1) = 0.0;
|
||||
pm(0,2) = 1.0; pm(1,2) = 1.0; pm(2,2) = 0.0;
|
||||
pm(0,3) = 0.0; pm(1,3) = 1.0; pm(2,3) = 0.0;
|
||||
pm(0,4) = 0.5; pm(1,4) = 0.5; pm(2,4) = 0.7071067811865475;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
mfem_error ("Geometry::GetPerfPointMat (...)");
|
||||
}
|
||||
@@ -744,13 +877,13 @@ void Geometry::JacToPerfJac(int GeomType, const DenseMatrix &J,
|
||||
}
|
||||
}
|
||||
|
||||
const int Geometry::NumBdrArray[NumGeom] = { 0, 2, 3, 4, 4, 6, 5 };
|
||||
const int Geometry::Dimension[NumGeom] = { 0, 1, 2, 2, 3, 3, 3 };
|
||||
const int Geometry::NumBdrArray[NumGeom] = { 0, 2, 3, 4, 4, 6, 5, 5 };
|
||||
const int Geometry::Dimension[NumGeom] = { 0, 1, 2, 2, 3, 3, 3, 3 };
|
||||
const int Geometry::DimStart[MaxDim+2] =
|
||||
{ POINT, SEGMENT, TRIANGLE, TETRAHEDRON, NUM_GEOMETRIES };
|
||||
const int Geometry::NumVerts[NumGeom] = { 1, 2, 3, 4, 4, 8, 6 };
|
||||
const int Geometry::NumEdges[NumGeom] = { 0, 1, 3, 4, 6, 12, 9 };
|
||||
const int Geometry::NumFaces[NumGeom] = { 0, 0, 1, 1, 4, 6, 5 };
|
||||
const int Geometry::NumVerts[NumGeom] = { 1, 2, 3, 4, 4, 8, 6, 5 };
|
||||
const int Geometry::NumEdges[NumGeom] = { 0, 1, 3, 4, 6, 12, 9, 8 };
|
||||
const int Geometry::NumFaces[NumGeom] = { 0, 0, 1, 1, 4, 6, 5, 5 };
|
||||
|
||||
const int Geometry::
|
||||
Constants<Geometry::POINT>::Orient[1][1] = {{0}};
|
||||
@@ -897,6 +1030,30 @@ Constants<Geometry::PRISM>::VertToVert::J[9][2] =
|
||||
{5, 4} // 4,5:4
|
||||
};
|
||||
|
||||
const int Geometry::
|
||||
Constants<Geometry::PYRAMID>::Edges[8][2] =
|
||||
{{0, 1}, {1, 2}, {3, 2}, {0, 3}, {0, 4}, {1, 4}, {2, 4}, {3, 4}};
|
||||
const int Geometry::
|
||||
Constants<Geometry::PYRAMID>::FaceTypes[5] =
|
||||
{
|
||||
Geometry::SQUARE,
|
||||
Geometry::TRIANGLE, Geometry::TRIANGLE,
|
||||
Geometry::TRIANGLE, Geometry::TRIANGLE
|
||||
};
|
||||
const int Geometry::
|
||||
Constants<Geometry::PYRAMID>::FaceVert[5][4] =
|
||||
{{3, 2, 1, 0}, {0, 1, 4, -1}, {1, 2, 4, -1}, {2, 3, 4, -1}, {3, 0, 4, -1}};
|
||||
const int Geometry::
|
||||
Constants<Geometry::PYRAMID>::VertToVert::I[5] = {0, 3, 5, 7, 8};
|
||||
const int Geometry::
|
||||
Constants<Geometry::PYRAMID>::VertToVert::J[8][2] =
|
||||
{
|
||||
{1, 0}, {3, 3}, {4, 4}, // 0,1:0 0,3:3 0,4:4
|
||||
{2, 1}, {4, 5}, // 1,2:1 1,4:5
|
||||
{3,-3}, {4, 6}, // 2,3:-3 2,4:6
|
||||
{4, 7} // 3,4:7
|
||||
};
|
||||
|
||||
|
||||
GeometryRefiner::GeometryRefiner()
|
||||
{
|
||||
@@ -1262,6 +1419,104 @@ RefinedGeometry * GeometryRefiner::Refine(Geometry::Type Geom,
|
||||
return RG;
|
||||
}
|
||||
|
||||
case Geometry::PYRAMID:
|
||||
{
|
||||
const int n = Times;
|
||||
RG = new RefinedGeometry ((n+1)*(n+2)*(2*n+3)/6,
|
||||
5*n*(2*n-1)*(2*n+1)/3, 0);
|
||||
RG->Times = Times;
|
||||
RG->ETimes = ETimes;
|
||||
RG->Type = type;
|
||||
// enumerate and define the vertices
|
||||
m = 0;
|
||||
for (k = 0; k <= n; k++)
|
||||
{
|
||||
const double *cpij =
|
||||
poly1d.GetPoints(Times - k, BasisType::GetNodalBasis(type));
|
||||
for (j = 0; j <= n - k; j++)
|
||||
for (i = 0; i <= n - k; i++)
|
||||
{
|
||||
IntegrationPoint &ip = RG->RefPts.IntPoint(m);
|
||||
if (type == 0)
|
||||
{
|
||||
ip.x = (n > k) ? (double(i) / (n - k)) : 0.0;
|
||||
ip.y = (n > k) ? (double(j) / (n - k)) : 0.0;
|
||||
ip.z = double(k) / n;
|
||||
}
|
||||
else
|
||||
{
|
||||
ip.x = cpij[i] * (1.0 - cp[k]);
|
||||
ip.y = cpij[j] * (1.0 - cp[k]);
|
||||
ip.z = cp[k];
|
||||
}
|
||||
m++;
|
||||
}
|
||||
}
|
||||
if (m != (n+1)*(n+2)*(2*n+3)/6)
|
||||
{
|
||||
mfem_error("GeometryRefiner::Refine() for PYRAMID #1");
|
||||
}
|
||||
// elements
|
||||
Array<int> &G = RG->RefGeoms;
|
||||
m = 0;
|
||||
for (k = 0; k < n; k++)
|
||||
{
|
||||
int lk = k * (k * (2 * k - 6 * n - 9) + 6 * n * (n + 3) + 13) / 6;
|
||||
int lkp1 = (k + 1) *
|
||||
(k * (2 * k - 6 * n -5) + 6 * n * (n + 2) + 6) / 6;
|
||||
for (j = 0; j < n - k; j++)
|
||||
{
|
||||
for (i = 0; i < n - k; i++)
|
||||
{
|
||||
G[m++] = lk + j * (n - k + 1) + i;
|
||||
G[m++] = lk + j * (n - k + 1) + i + 1;
|
||||
G[m++] = lk + (j + 1) * (n - k + 1) + i + 1;
|
||||
G[m++] = lk + (j + 1) * (n - k + 1) + i;
|
||||
G[m++] = lkp1 + j * (n - k) + i;
|
||||
}
|
||||
}
|
||||
for (j = 0; j < n - k - 1; j++)
|
||||
{
|
||||
for (i = 0; i < n - k - 1; i++)
|
||||
{
|
||||
G[m++] = lkp1 + j * (n - k) + i;
|
||||
G[m++] = lkp1 + (j + 1) * (n - k) + i;
|
||||
G[m++] = lkp1 + (j + 1) * (n - k) + i + 1;
|
||||
G[m++] = lkp1 + j * (n - k) + i + 1;
|
||||
G[m++] = lk + (j + 1) * (n - k + 1) + i + 1;
|
||||
}
|
||||
}
|
||||
for (j = 0; j < n - k; j++)
|
||||
{
|
||||
for (i = 0; i < n - k - 1; i++)
|
||||
{
|
||||
G[m++] = lk + j * (n - k + 1) + i + 1;
|
||||
G[m++] = lk + (j + 1) * (n - k + 1) + i + 1;
|
||||
G[m++] = lkp1 + j * (n - k) + i;
|
||||
G[m++] = lkp1 + j * (n - k) + i + 1;
|
||||
G[m++] = -1;
|
||||
}
|
||||
}
|
||||
for (j = 0; j < n - k - 1; j++)
|
||||
{
|
||||
for (i = 0; i < n - k; i++)
|
||||
{
|
||||
G[m++] = lk + (j + 1) * (n - k + 1) + i;
|
||||
G[m++] = lk + (j + 1) * (n - k + 1) + i + 1;
|
||||
G[m++] = lkp1 + (j + 1) * (n - k) + i;
|
||||
G[m++] = lkp1 + j * (n - k) + i;
|
||||
G[m++] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (m != 5*n*(2*n-1)*(2*n+1)/3)
|
||||
{
|
||||
mfem_error("GeometryRefiner::Refine() for PYRAMID #2");
|
||||
}
|
||||
RGeom[Geometry::PYRAMID].Append(RG);
|
||||
return RG;
|
||||
}
|
||||
|
||||
case Geometry::PRISM:
|
||||
{
|
||||
const int n = Times;
|
||||
|
||||
+22
-2
@@ -27,6 +27,7 @@ namespace mfem
|
||||
Geometry::TETRAHEDRON - w/ vert. (0,0,0),(1,0,0),(0,1,0),(0,0,1)
|
||||
Geometry::CUBE - the unit cube
|
||||
Geometry::PRISM - w/ vert. (0,0,0),(1,0,0),(0,1,0),(0,0,1),(1,0,1),(0,1,1)
|
||||
Geometry::PYRAMID - w/ vert. (0,0,0),(1,0,0),(1,1,0),(0,1,0),(0,0,1)
|
||||
*/
|
||||
class Geometry
|
||||
{
|
||||
@@ -34,7 +35,7 @@ public:
|
||||
enum Type
|
||||
{
|
||||
INVALID = -1,
|
||||
POINT = 0, SEGMENT, TRIANGLE, SQUARE, TETRAHEDRON, CUBE, PRISM,
|
||||
POINT = 0, SEGMENT, TRIANGLE, SQUARE, TETRAHEDRON, CUBE, PRISM, PYRAMID,
|
||||
NUM_GEOMETRIES
|
||||
};
|
||||
|
||||
@@ -251,7 +252,26 @@ template <> struct Geometry::Constants<Geometry::PRISM>
|
||||
};
|
||||
};
|
||||
|
||||
// Defined in fe.cpp to ensure construction after 'mfem::WedgeFE'.
|
||||
template <> struct Geometry::Constants<Geometry::PYRAMID>
|
||||
{
|
||||
static const int Dimension = 3;
|
||||
static const int NumVert = 5;
|
||||
static const int NumEdges = 8;
|
||||
static const int Edges[NumEdges][2];
|
||||
static const int NumFaces = 5;
|
||||
static const int FaceTypes[NumFaces];
|
||||
static const int MaxFaceVert = 4;
|
||||
static const int FaceVert[NumFaces][MaxFaceVert];
|
||||
// Upper-triangular part of the local vertex-to-vertex graph.
|
||||
struct VertToVert
|
||||
{
|
||||
static const int I[NumVert];
|
||||
static const int J[NumEdges][2]; // {end,edge_idx}
|
||||
};
|
||||
};
|
||||
|
||||
// Defined in fe.cpp to ensure construction after 'mfem::TriangleFE' and
|
||||
// `mfem::TetrahedronFE`.
|
||||
extern Geometry Geometries;
|
||||
|
||||
|
||||
|
||||
+166
-54
@@ -255,6 +255,8 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
GridFunction &u = *this;
|
||||
|
||||
ElementTransformation *Transf;
|
||||
DofTransformation *udoftrans;
|
||||
DofTransformation *fdoftrans;
|
||||
|
||||
FiniteElementSpace *ufes = u.FESpace();
|
||||
FiniteElementSpace *ffes = flux.FESpace();
|
||||
@@ -274,15 +276,23 @@ void GridFunction::SumFluxAndCount(BilinearFormIntegrator &blfi,
|
||||
continue;
|
||||
}
|
||||
|
||||
ufes->GetElementVDofs(i, udofs);
|
||||
ffes->GetElementVDofs(i, fdofs);
|
||||
udoftrans = ufes->GetElementVDofs(i, udofs);
|
||||
fdoftrans = ffes->GetElementVDofs(i, fdofs);
|
||||
|
||||
u.GetSubVector(udofs, ul);
|
||||
if (udoftrans)
|
||||
{
|
||||
udoftrans->InvTransformPrimal(ul);
|
||||
}
|
||||
|
||||
Transf = ufes->GetElementTransformation(i);
|
||||
blfi.ComputeElementFlux(*ufes->GetFE(i), *Transf, ul,
|
||||
*ffes->GetFE(i), fl, wcoef);
|
||||
|
||||
if (fdoftrans)
|
||||
{
|
||||
fdoftrans->TransformPrimal(fl);
|
||||
}
|
||||
flux.AddElementVector(fdofs, fl);
|
||||
|
||||
FiniteElementSpace::AdjustVDofs(fdofs);
|
||||
@@ -364,7 +374,7 @@ void GridFunction::GetNodalValues(int i, Array<double> &nval, int vdim) const
|
||||
|
||||
int k;
|
||||
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
const IntegrationRule *ElemVert =
|
||||
Geometries.GetVertices(FElem->GetGeomType());
|
||||
@@ -374,6 +384,10 @@ void GridFunction::GetNodalValues(int i, Array<double> &nval, int vdim) const
|
||||
vdim--;
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
@@ -403,7 +417,7 @@ double GridFunction::GetValue(int i, const IntegrationPoint &ip, int vdim)
|
||||
const
|
||||
{
|
||||
Array<int> dofs;
|
||||
fes->GetElementDofs(i, dofs);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(i, dofs);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
Vector DofVal(dofs.Size()), LocVec;
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
@@ -418,6 +432,10 @@ const
|
||||
fe->CalcPhysShape(*Tr, DofVal);
|
||||
}
|
||||
GetSubVector(dofs, LocVec);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(LocVec);
|
||||
}
|
||||
|
||||
return (DofVal * LocVec);
|
||||
}
|
||||
@@ -428,9 +446,13 @@ void GridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
int dof = FElem->GetDof();
|
||||
Array<int> vdofs;
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
@@ -470,30 +492,35 @@ const
|
||||
Array<int> dofs;
|
||||
int n = ir.GetNPoints();
|
||||
vals.SetSize(n);
|
||||
fes->GetElementDofs(i, dofs);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(i, dofs);
|
||||
fes->DofsToVDofs(vdim-1, dofs);
|
||||
const FiniteElement *FElem = fes->GetFE(i);
|
||||
int dof = FElem->GetDof();
|
||||
Vector DofVal(dof), loc_data(dof);
|
||||
GetSubVector(dofs, loc_data);
|
||||
if (FElem->GetMapType() == FiniteElement::VALUE)
|
||||
if (doftrans)
|
||||
{
|
||||
for (int k = 0; k < n; k++)
|
||||
{
|
||||
FElem->CalcShape(ir.IntPoint(k), DofVal);
|
||||
vals(k) = DofVal * loc_data;
|
||||
}
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
ElementTransformation *Tr = fes->GetElementTransformation(i);
|
||||
for (int k = 0; k < n; k++)
|
||||
for (int k = 0; k < n; k++)
|
||||
if (FElem->GetMapType() == FiniteElement::VALUE)
|
||||
{
|
||||
Tr->SetIntPoint(&ir.IntPoint(k));
|
||||
FElem->CalcPhysShape(*Tr, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::GetValues(int i, const IntegrationRule &ir, Vector &vals,
|
||||
@@ -861,11 +888,12 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
|
||||
Array<int> vdofs;
|
||||
const FiniteElement *fe = NULL;
|
||||
DofTransformation * doftrans = NULL;
|
||||
|
||||
switch (T.ElementType)
|
||||
{
|
||||
case ElementTransformation::ELEMENT:
|
||||
fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
doftrans = fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
fe = fes->GetFE(T.ElementNo);
|
||||
break;
|
||||
case ElementTransformation::EDGE:
|
||||
@@ -956,6 +984,10 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
|
||||
int dof = fe->GetDof();
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
if (fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
@@ -998,10 +1030,14 @@ void GridFunction::GetVectorValues(ElementTransformation &T,
|
||||
int dof = FElem->GetDof();
|
||||
|
||||
Array<int> vdofs;
|
||||
fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(T.ElementNo, vdofs);
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
|
||||
int nip = ir.GetNPoints();
|
||||
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
@@ -1089,6 +1125,8 @@ void GridFunction::GetValuesFrom(const GridFunction &orig_func)
|
||||
// Without averaging ...
|
||||
|
||||
const FiniteElementSpace *orig_fes = orig_func.FESpace();
|
||||
DofTransformation * doftrans;
|
||||
DofTransformation * orig_doftrans;
|
||||
Array<int> vdofs, orig_vdofs;
|
||||
Vector shape, loc_values, orig_loc_values;
|
||||
int i, j, d, ne, dof, odof, vdim;
|
||||
@@ -1097,9 +1135,13 @@ void GridFunction::GetValuesFrom(const GridFunction &orig_func)
|
||||
vdim = fes->GetVDim();
|
||||
for (i = 0; i < ne; i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
orig_fes->GetElementVDofs(i, orig_vdofs);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
orig_doftrans = orig_fes->GetElementVDofs(i, orig_vdofs);
|
||||
orig_func.GetSubVector(orig_vdofs, orig_loc_values);
|
||||
if (orig_doftrans)
|
||||
{
|
||||
orig_doftrans->InvTransformPrimal(orig_loc_values);
|
||||
}
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
const FiniteElement *orig_fe = orig_fes->GetFE(i);
|
||||
dof = fe->GetDof();
|
||||
@@ -1117,6 +1159,10 @@ void GridFunction::GetValuesFrom(const GridFunction &orig_func)
|
||||
shape * ((const double *)orig_loc_values + d * odof) ;
|
||||
}
|
||||
}
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(loc_values);
|
||||
}
|
||||
SetSubVector(vdofs, loc_values);
|
||||
}
|
||||
}
|
||||
@@ -1126,8 +1172,10 @@ void GridFunction::GetBdrValuesFrom(const GridFunction &orig_func)
|
||||
// Without averaging ...
|
||||
|
||||
const FiniteElementSpace *orig_fes = orig_func.FESpace();
|
||||
// DofTransformation * doftrans;
|
||||
// DofTransformation * orig_doftrans;
|
||||
Array<int> vdofs, orig_vdofs;
|
||||
Vector shape, loc_values, orig_loc_values;
|
||||
Vector shape, loc_values, loc_values_t, orig_loc_values, orig_loc_values_t;
|
||||
int i, j, d, nbe, dof, odof, vdim;
|
||||
|
||||
nbe = fes->GetNBE();
|
||||
@@ -1165,37 +1213,33 @@ void GridFunction::GetVectorFieldValues(
|
||||
Array<int> vdofs;
|
||||
ElementTransformation *transf;
|
||||
|
||||
int d, j, k, n, sdim, dof, ind;
|
||||
int d, k, n, sdim, dof;
|
||||
|
||||
n = ir.GetNPoints();
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
const FiniteElement *fe = fes->GetFE(i);
|
||||
dof = fe->GetDof();
|
||||
sdim = fes->GetMesh()->SpaceDimension();
|
||||
int *dofs = &vdofs[comp*dof];
|
||||
// int *dofs = &vdofs[comp*dof];
|
||||
transf = fes->GetElementTransformation(i);
|
||||
transf->Transform(ir, tr);
|
||||
vals.SetSize(n, sdim);
|
||||
DenseMatrix vshape(dof, sdim);
|
||||
double a;
|
||||
Vector loc_data, val(sdim);
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
for (k = 0; k < n; k++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(k);
|
||||
transf->SetIntPoint(&ip);
|
||||
fe->CalcVShape(*transf, vshape);
|
||||
vshape.MultTranspose(loc_data, val);
|
||||
for (d = 0; d < sdim; d++)
|
||||
{
|
||||
a = 0.0;
|
||||
for (j = 0; j < dof; j++)
|
||||
if ( (ind=dofs[j]) >= 0 )
|
||||
{
|
||||
a += vshape(j, d) * data[ind];
|
||||
}
|
||||
else
|
||||
{
|
||||
a -= vshape(j, d) * data[-1-ind];
|
||||
}
|
||||
vals(k, d) = a;
|
||||
vals(k,d) = val(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1365,9 +1409,13 @@ void GridFunction::GetVectorGradientHat(
|
||||
const FiniteElement *FElem = fes->GetFE(elNo);
|
||||
int dim = FElem->GetDim(), dof = FElem->GetDof();
|
||||
Array<int> vdofs;
|
||||
fes->GetElementVDofs(elNo, vdofs);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(elNo, vdofs);
|
||||
Vector loc_data;
|
||||
GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
// assuming scalar FE
|
||||
int vdim = fes->GetVDim();
|
||||
DenseMatrix dshape(dof, dim);
|
||||
@@ -1406,9 +1454,13 @@ double GridFunction::GetDivergence(ElementTransformation &T) const
|
||||
{
|
||||
// Assuming RT-type space
|
||||
Array<int> dofs;
|
||||
fes->GetElementDofs(elNo, dofs);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(elNo, dofs);
|
||||
Vector loc_data, divshape(fe->GetDof());
|
||||
GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
fe->CalcDivShape(T.GetIntPoint(), divshape);
|
||||
return (loc_data * divshape) / T.Weight();
|
||||
}
|
||||
@@ -1499,9 +1551,13 @@ void GridFunction::GetCurl(ElementTransformation &T, Vector &curl) const
|
||||
{
|
||||
// Assuming ND-type space
|
||||
Array<int> dofs;
|
||||
fes->GetElementDofs(elNo, dofs);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(elNo, dofs);
|
||||
Vector loc_data;
|
||||
GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
DenseMatrix curl_shape(fe->GetDof(), fe->GetDim() == 3 ? 3 : 1);
|
||||
fe->CalcCurlShape(T.GetIntPoint(), curl_shape);
|
||||
curl.SetSize(curl_shape.Width());
|
||||
@@ -1643,8 +1699,12 @@ void GridFunction::GetGradients(ElementTransformation &tr,
|
||||
DenseMatrix dshape(fe->GetDof(), fe->GetDim());
|
||||
Vector lval, gh(fe->GetDim()), gcol;
|
||||
Array<int> dofs;
|
||||
fes->GetElementDofs(elNo, dofs);
|
||||
DofTransformation * doftrans = fes->GetElementDofs(elNo, dofs);
|
||||
GetSubVector(dofs, lval);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(lval);
|
||||
}
|
||||
grad.SetSize(fe->GetDim(), ir.GetNPoints());
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
@@ -1724,6 +1784,8 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
{
|
||||
MassIntegrator Mi;
|
||||
DenseMatrix loc_mass;
|
||||
DofTransformation * te_doftrans;
|
||||
DofTransformation * tr_doftrans;
|
||||
Array<int> te_dofs, tr_dofs;
|
||||
Vector loc_avgs, loc_this;
|
||||
Vector int_psi(avgs.Size());
|
||||
@@ -1734,11 +1796,19 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
{
|
||||
Mi.AssembleElementMatrix2(*fes->GetFE(i), *avgs.FESpace()->GetFE(i),
|
||||
*fes->GetElementTransformation(i), loc_mass);
|
||||
fes->GetElementDofs(i, tr_dofs);
|
||||
avgs.FESpace()->GetElementDofs(i, te_dofs);
|
||||
tr_doftrans = fes->GetElementDofs(i, tr_dofs);
|
||||
te_doftrans = avgs.FESpace()->GetElementDofs(i, te_dofs);
|
||||
GetSubVector(tr_dofs, loc_this);
|
||||
if (tr_doftrans)
|
||||
{
|
||||
tr_doftrans->InvTransformPrimal(loc_this);
|
||||
}
|
||||
loc_avgs.SetSize(te_dofs.Size());
|
||||
loc_mass.Mult(loc_this, loc_avgs);
|
||||
if (te_doftrans)
|
||||
{
|
||||
te_doftrans->TransformPrimal(loc_avgs);
|
||||
}
|
||||
avgs.AddElementVector(te_dofs, loc_avgs);
|
||||
loc_this = 1.0; // assume the local basis for 'this' sums to 1
|
||||
loc_mass.Mult(loc_this, loc_avgs);
|
||||
@@ -1753,8 +1823,12 @@ void GridFunction::GetElementAverages(GridFunction &avgs) const
|
||||
void GridFunction::GetElementDofValues(int el, Vector &dof_vals) const
|
||||
{
|
||||
Array<int> dof_idx;
|
||||
fes->GetElementVDofs(el, dof_idx);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(el, dof_idx);
|
||||
GetSubVector(dof_idx, dof_vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(dof_vals);
|
||||
}
|
||||
}
|
||||
|
||||
void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
@@ -1790,13 +1864,21 @@ void GridFunction::ProjectGridFunction(const GridFunction &src)
|
||||
cached_geom = geom;
|
||||
}
|
||||
|
||||
src.fes->GetElementVDofs(i, src_vdofs);
|
||||
DofTransformation * src_doftrans = src.fes->GetElementVDofs(i, src_vdofs);
|
||||
src.GetSubVector(src_vdofs, src_lvec);
|
||||
if (src_doftrans)
|
||||
{
|
||||
src_doftrans->InvTransformPrimal(src_lvec);
|
||||
}
|
||||
for (int vd = 0; vd < vdim; vd++)
|
||||
{
|
||||
P.Mult(&src_lvec[vd*P.Width()], &dest_lvec[vd*P.Height()]);
|
||||
}
|
||||
fes->GetElementVDofs(i, dest_vdofs);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, dest_vdofs);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(dest_lvec);
|
||||
}
|
||||
SetSubVector(dest_vdofs, dest_lvec);
|
||||
}
|
||||
}
|
||||
@@ -1805,10 +1887,15 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
const Vector &lo_, const Vector &hi_)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
int size = vdofs.Size();
|
||||
Vector vals, new_vals(size);
|
||||
|
||||
GetSubVector(vdofs, vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vals);
|
||||
}
|
||||
|
||||
MFEM_ASSERT(weights.Size() == size, "Different # of weights and dofs.");
|
||||
MFEM_ASSERT(lo_.Size() == size, "Different # of lower bounds and dofs.");
|
||||
@@ -1825,6 +1912,10 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
slbqp.SetPrintLevel(0); // print messages only if not converged
|
||||
slbqp.Mult(vals, new_vals);
|
||||
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(new_vals);
|
||||
}
|
||||
SetSubVector(vdofs, new_vals);
|
||||
}
|
||||
|
||||
@@ -1832,10 +1923,14 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
double min_, double max_)
|
||||
{
|
||||
Array<int> vdofs;
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
int size = vdofs.Size();
|
||||
Vector vals, new_vals(size);
|
||||
GetSubVector(vdofs, vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(vals);
|
||||
}
|
||||
|
||||
double max_val = vals.Max();
|
||||
double min_val = vals.Min();
|
||||
@@ -1843,6 +1938,10 @@ void GridFunction::ImposeBounds(int i, const Vector &weights,
|
||||
if (max_val <= min_)
|
||||
{
|
||||
new_vals = min_;
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(new_vals);
|
||||
}
|
||||
SetSubVector(vdofs, new_vals);
|
||||
return;
|
||||
}
|
||||
@@ -2278,6 +2377,7 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
|
||||
void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
{
|
||||
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
|
||||
DofTransformation * doftrans = NULL;
|
||||
|
||||
if (delta_c == NULL)
|
||||
{
|
||||
@@ -2286,9 +2386,13 @@ void GridFunction::ProjectCoefficient(Coefficient &coeff)
|
||||
|
||||
for (int i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2334,11 +2438,17 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff)
|
||||
Array<int> vdofs;
|
||||
Vector vals;
|
||||
|
||||
DofTransformation * doftrans = NULL;
|
||||
|
||||
for (i = 0; i < fes->GetNE(); i++)
|
||||
{
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
vals.SetSize(vdofs.Size());
|
||||
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformPrimal(vals);
|
||||
}
|
||||
SetSubVector(vdofs, vals);
|
||||
}
|
||||
}
|
||||
@@ -2401,6 +2511,7 @@ void GridFunction::ProjectCoefficient(Coefficient *coeff[])
|
||||
double val;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *transf;
|
||||
// DofTransformation * doftrans;
|
||||
Array<int> vdofs;
|
||||
|
||||
vdim = fes->GetVDim();
|
||||
@@ -2410,6 +2521,7 @@ void GridFunction::ProjectCoefficient(Coefficient *coeff[])
|
||||
fdof = fe->GetDof();
|
||||
transf = fes->GetElementTransformation(i);
|
||||
const IntegrationRule &ir = fe->GetNodes();
|
||||
// doftrans = fes->GetElementVDofs(i, vdofs);
|
||||
fes->GetElementVDofs(i, vdofs);
|
||||
for (j = 0; j < fdof; j++)
|
||||
{
|
||||
|
||||
@@ -95,6 +95,12 @@ public:
|
||||
: Vector(data, f->GetVSize())
|
||||
{ fes = f; fec = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
|
||||
|
||||
/** @brief Construct a GridFunction using previously allocated Vector @a base
|
||||
starting at the given offset, @a base_offset. */
|
||||
GridFunction(FiniteElementSpace *f, Vector &base, int base_offset = 0)
|
||||
: Vector(base, base_offset, f->GetVSize())
|
||||
{ fes = f; fec = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
|
||||
|
||||
/// Construct a GridFunction on the given Mesh, using the data from @a input.
|
||||
/** The content of @a input should be in the format created by the method
|
||||
Save(). The reconstructed FiniteElementSpace and FiniteElementCollection
|
||||
|
||||
+6
-1
@@ -610,7 +610,12 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
{
|
||||
if (gsl_code[i] == 1) { indl2.Append(i); }
|
||||
}
|
||||
if (indl2.Size() == 0) { return; } // no points on element borders
|
||||
int borderPts = indl2.Size();
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Allreduce(MPI_IN_PLACE, &borderPts, 1, MPI_INT, MPI_SUM, gsl_comm->c);
|
||||
#endif
|
||||
if (borderPts == 0) { return; } // no points on element borders
|
||||
|
||||
|
||||
Vector field_out_l2(field_out.Size());
|
||||
VectorGridFunctionCoefficient field_in_dg(&field_in);
|
||||
|
||||
@@ -910,6 +910,9 @@ IntegrationRules::IntegrationRules(int Ref, int type_):
|
||||
TetrahedronIntRules.SetSize(32, h_mt);
|
||||
TetrahedronIntRules = NULL;
|
||||
|
||||
PyramidIntRules.SetSize(32, h_mt);
|
||||
PyramidIntRules = NULL;
|
||||
|
||||
PrismIntRules.SetSize(32, h_mt);
|
||||
PrismIntRules = NULL;
|
||||
|
||||
@@ -930,6 +933,7 @@ const IntegrationRule &IntegrationRules::Get(int GeomType, int Order)
|
||||
case Geometry::TETRAHEDRON: ir_array = &TetrahedronIntRules; break;
|
||||
case Geometry::CUBE: ir_array = &CubeIntRules; break;
|
||||
case Geometry::PRISM: ir_array = &PrismIntRules; break;
|
||||
case Geometry::PYRAMID: ir_array = &PyramidIntRules; break;
|
||||
default:
|
||||
mfem_error("IntegrationRules::Get(...) : Unknown geometry type!");
|
||||
ir_array = NULL;
|
||||
@@ -976,6 +980,7 @@ void IntegrationRules::Set(int GeomType, int Order, IntegrationRule &IntRule)
|
||||
case Geometry::TETRAHEDRON: ir_array = &TetrahedronIntRules; break;
|
||||
case Geometry::CUBE: ir_array = &CubeIntRules; break;
|
||||
case Geometry::PRISM: ir_array = &PrismIntRules; break;
|
||||
case Geometry::PYRAMID: ir_array = &PyramidIntRules; break;
|
||||
default:
|
||||
mfem_error("IntegrationRules::Set(...) : Unknown geometry type!");
|
||||
ir_array = NULL;
|
||||
@@ -1019,6 +1024,7 @@ IntegrationRules::~IntegrationRules()
|
||||
DeleteIntRuleArray(TetrahedronIntRules);
|
||||
DeleteIntRuleArray(CubeIntRules);
|
||||
DeleteIntRuleArray(PrismIntRules);
|
||||
DeleteIntRuleArray(PyramidIntRules);
|
||||
}
|
||||
|
||||
|
||||
@@ -1041,6 +1047,8 @@ IntegrationRule *IntegrationRules::GenerateIntegrationRule(int GeomType,
|
||||
return CubeIntegrationRule(Order);
|
||||
case Geometry::PRISM:
|
||||
return PrismIntegrationRule(Order);
|
||||
case Geometry::PYRAMID:
|
||||
return PyramidIntegrationRule(Order);
|
||||
default:
|
||||
mfem_error("IntegrationRules::Set(...) : Unknown geometry type!");
|
||||
return NULL;
|
||||
@@ -1648,6 +1656,30 @@ IntegrationRule *IntegrationRules::TetrahedronIntegrationRule(int Order)
|
||||
}
|
||||
}
|
||||
|
||||
// Integration rules for reference pyramid
|
||||
IntegrationRule *IntegrationRules::PyramidIntegrationRule(int Order)
|
||||
{
|
||||
// This is a simple integration rule adapted from an integration
|
||||
// rule for a cube which seems to be adequate for now. When we
|
||||
// implement high order finite elements for pyramids we should
|
||||
// revisit this and see if we can improve upon it.
|
||||
const IntegrationRule &irc = Get(Geometry::CUBE, Order);
|
||||
int npts = irc.GetNPoints();
|
||||
AllocIntRule(PyramidIntRules, Order);
|
||||
PyramidIntRules[Order] = new IntegrationRule(npts);
|
||||
|
||||
for (int k=0; k<npts; k++)
|
||||
{
|
||||
const IntegrationPoint & ipc = irc.IntPoint(k);
|
||||
IntegrationPoint & ipp = PyramidIntRules[Order]->IntPoint(k);
|
||||
ipp.x = ipc.x * (1.0 - ipc.z);
|
||||
ipp.y = ipc.y * (1.0 - ipc.z);
|
||||
ipp.z = ipc.z;
|
||||
ipp.weight = ipc.weight / 3.0;
|
||||
}
|
||||
return PyramidIntRules[Order];
|
||||
}
|
||||
|
||||
// Integration rules for reference prism
|
||||
IntegrationRule *IntegrationRules::PrismIntegrationRule(int Order)
|
||||
{
|
||||
|
||||
@@ -323,6 +323,7 @@ private:
|
||||
Array<IntegrationRule *> TriangleIntRules;
|
||||
Array<IntegrationRule *> SquareIntRules;
|
||||
Array<IntegrationRule *> TetrahedronIntRules;
|
||||
Array<IntegrationRule *> PyramidIntRules;
|
||||
Array<IntegrationRule *> PrismIntRules;
|
||||
Array<IntegrationRule *> CubeIntRules;
|
||||
|
||||
@@ -351,6 +352,7 @@ private:
|
||||
IntegrationRule *TriangleIntegrationRule(int Order);
|
||||
IntegrationRule *SquareIntegrationRule(int Order);
|
||||
IntegrationRule *TetrahedronIntegrationRule(int Order);
|
||||
IntegrationRule *PyramidIntegrationRule(int Order);
|
||||
IntegrationRule *PrismIntegrationRule(int Order);
|
||||
IntegrationRule *CubeIntegrationRule(int Order);
|
||||
|
||||
|
||||
+11
-2
@@ -103,6 +103,7 @@ void LinearForm::Assemble()
|
||||
{
|
||||
Array<int> vdofs;
|
||||
ElementTransformation *eltrans;
|
||||
DofTransformation *doftrans;
|
||||
Vector elemvect;
|
||||
|
||||
int i;
|
||||
@@ -134,10 +135,14 @@ void LinearForm::Assemble()
|
||||
if ( domain_integs_marker[k] == NULL ||
|
||||
(*(domain_integs_marker[k]))[elem_attr-1] == 1 )
|
||||
{
|
||||
fes -> GetElementVDofs (i, vdofs);
|
||||
doftrans = fes -> GetElementVDofs (i, vdofs);
|
||||
eltrans = fes -> GetElementTransformation (i);
|
||||
domain_integs[k]->AssembleRHSElementVect(*fes->GetFE(i),
|
||||
*eltrans, elemvect);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elemvect);
|
||||
}
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
@@ -174,7 +179,7 @@ void LinearForm::Assemble()
|
||||
{
|
||||
const int bdr_attr = mesh->GetBdrAttribute(i);
|
||||
if (bdr_attr_marker[bdr_attr-1] == 0) { continue; }
|
||||
fes -> GetBdrElementVDofs (i, vdofs);
|
||||
doftrans = fes -> GetBdrElementVDofs (i, vdofs);
|
||||
eltrans = fes -> GetBdrElementTransformation (i);
|
||||
for (int k=0; k < boundary_integs.Size(); k++)
|
||||
{
|
||||
@@ -184,6 +189,10 @@ void LinearForm::Assemble()
|
||||
boundary_integs[k]->AssembleRHSElementVect(*fes->GetBE(i),
|
||||
*eltrans, elemvect);
|
||||
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->TransformDual(elemvect);
|
||||
}
|
||||
AddElementVector (vdofs, elemvect);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,14 +317,11 @@ ParallelEliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
|
||||
void ParBilinearForm::TrueAddMult(const Vector &x, Vector &y, const double a)
|
||||
const
|
||||
{
|
||||
MFEM_VERIFY(interior_face_integs.Size() == 0,
|
||||
"the case of interior face integrators is not"
|
||||
" implemented");
|
||||
|
||||
if (X.ParFESpace() != pfes)
|
||||
{
|
||||
X.SetSpace(pfes);
|
||||
Y.SetSpace(pfes);
|
||||
Ytmp.SetSize(pfes->GetTrueVSize());
|
||||
}
|
||||
|
||||
X.Distribute(&x);
|
||||
@@ -334,9 +331,13 @@ const
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_VERIFY(interior_face_integs.Size() == 0,
|
||||
"the case of interior face integrators is not"
|
||||
" implemented");
|
||||
mat->Mult(X, Y);
|
||||
}
|
||||
pfes->Dof_TrueDof_Matrix()->MultTranspose(a, Y, 1.0, y);
|
||||
pfes->GetProlongationMatrix()->MultTranspose(Y, Ytmp);
|
||||
y.Add(a,Ytmp);
|
||||
}
|
||||
|
||||
void ParBilinearForm::FormLinearSystem(
|
||||
|
||||
@@ -33,6 +33,7 @@ protected:
|
||||
|
||||
/// Auxiliary objects used in TrueAddMult().
|
||||
mutable ParGridFunction X, Y;
|
||||
mutable Vector Ytmp;
|
||||
|
||||
OperatorHandle p_mat, p_mat_e;
|
||||
|
||||
|
||||
+319
-53
@@ -128,6 +128,9 @@ void ParFiniteElementSpace::ParInit(ParMesh *pm)
|
||||
{
|
||||
ApplyLDofSigns(*elem_dof);
|
||||
}
|
||||
|
||||
// Check for shared trianglular faces with interior Nedelec DoFs
|
||||
CheckNDSTriaDofs();
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::Construct()
|
||||
@@ -464,32 +467,53 @@ void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof) const
|
||||
ApplyLDofSigns(all_dofs);
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs) const
|
||||
DofTransformation *
|
||||
ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs) const
|
||||
{
|
||||
if (elem_dof)
|
||||
{
|
||||
elem_dof->GetRow(i, dofs);
|
||||
return;
|
||||
|
||||
if (DoFTrans[mesh->GetElementBaseGeometry(i)])
|
||||
{
|
||||
Array<int> Fo;
|
||||
elem_fos->GetRow(i, Fo);
|
||||
DoFTrans[mesh->GetElementBaseGeometry(i)]->SetFaceOrientations(Fo);
|
||||
return DoFTrans[mesh->GetElementBaseGeometry(i)];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
FiniteElementSpace::GetElementDofs(i, dofs);
|
||||
DofTransformation * doftrans = FiniteElementSpace::GetElementDofs(i, dofs);
|
||||
if (Conforming())
|
||||
{
|
||||
ApplyLDofSigns(dofs);
|
||||
}
|
||||
return doftrans;
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetBdrElementDofs(int i, Array<int> &dofs) const
|
||||
DofTransformation *
|
||||
ParFiniteElementSpace::GetBdrElementDofs(int i, Array<int> &dofs) const
|
||||
{
|
||||
if (bdr_elem_dof)
|
||||
{
|
||||
bdr_elem_dof->GetRow(i, dofs);
|
||||
return;
|
||||
|
||||
if (DoFTrans[mesh->GetBdrElementBaseGeometry(i)])
|
||||
{
|
||||
Array<int> Fo;
|
||||
bdr_elem_fos -> GetRow (i, Fo);
|
||||
DoFTrans[mesh->GetBdrElementBaseGeometry(i)]->SetFaceOrientations(Fo);
|
||||
return DoFTrans[mesh->GetBdrElementBaseGeometry(i)];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
FiniteElementSpace::GetBdrElementDofs(i, dofs);
|
||||
DofTransformation * doftrans =
|
||||
FiniteElementSpace::GetBdrElementDofs(i, dofs);
|
||||
if (Conforming())
|
||||
{
|
||||
ApplyLDofSigns(dofs);
|
||||
}
|
||||
return doftrans;
|
||||
}
|
||||
|
||||
int ParFiniteElementSpace::GetFaceDofs(int i, Array<int> &dofs,
|
||||
@@ -657,59 +681,266 @@ void ParFiniteElementSpace::GenerateGlobalOffsets() const
|
||||
}
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::CheckNDSTriaDofs()
|
||||
{
|
||||
// Check for Nedelec basis
|
||||
bool nd_basis = dynamic_cast<const ND_FECollection*>(fec);
|
||||
if (!nd_basis)
|
||||
{
|
||||
nd_strias = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for interior face dofs on triangles (the use of TETRAHEDRON
|
||||
// is not an error)
|
||||
bool nd_fdof = fec->HasFaceDofs(Geometry::TETRAHEDRON,
|
||||
GetMaxElementOrder());
|
||||
if (!nd_fdof)
|
||||
{
|
||||
nd_strias = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for shared triangle faces
|
||||
bool strias = false;
|
||||
{
|
||||
int ngrps = pmesh->GetNGroups();
|
||||
for (int g = 1; g < ngrps; g++)
|
||||
{
|
||||
strias |= pmesh->GroupNTriangles(g);
|
||||
}
|
||||
}
|
||||
|
||||
// Combine results
|
||||
int loc_nd_strias = strias ? 1 : 0;
|
||||
int glb_nd_strias = 0;
|
||||
MPI_Allreduce(&loc_nd_strias, &glb_nd_strias, 1,
|
||||
MPI_INTEGER, MPI_SUM, MyComm);
|
||||
nd_strias = glb_nd_strias > 0;
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const // matrix P
|
||||
{
|
||||
MFEM_ASSERT(Conforming(), "wrong code path");
|
||||
|
||||
if (P) { return; }
|
||||
|
||||
int ldof = GetVSize();
|
||||
int ltdof = TrueVSize();
|
||||
|
||||
HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
|
||||
HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
|
||||
int diag_counter;
|
||||
|
||||
HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
|
||||
HYPRE_Int *j_offd = Memory<HYPRE_Int>(ldof-ltdof);
|
||||
int offd_counter;
|
||||
|
||||
HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(ldof-ltdof);
|
||||
|
||||
HYPRE_BigInt *col_starts = GetTrueDofOffsets();
|
||||
HYPRE_BigInt *row_starts = GetDofOffsets();
|
||||
|
||||
Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
|
||||
|
||||
i_diag[0] = i_offd[0] = 0;
|
||||
diag_counter = offd_counter = 0;
|
||||
for (int i = 0; i < ldof; i++)
|
||||
if (!nd_strias)
|
||||
{
|
||||
int ltdof = GetLocalTDofNumber(i);
|
||||
if (ltdof >= 0)
|
||||
// Safe to assume 1-1 correspondence between shared dofs
|
||||
int ldof = GetVSize();
|
||||
int ltdof = TrueVSize();
|
||||
|
||||
HYPRE_Int *i_diag = Memory<HYPRE_Int>(ldof+1);
|
||||
HYPRE_Int *j_diag = Memory<HYPRE_Int>(ltdof);
|
||||
int diag_counter;
|
||||
|
||||
HYPRE_Int *i_offd = Memory<HYPRE_Int>(ldof+1);
|
||||
HYPRE_Int *j_offd = Memory<HYPRE_Int>(ldof-ltdof);
|
||||
int offd_counter;
|
||||
|
||||
HYPRE_BigInt *cmap = Memory<HYPRE_BigInt>(ldof-ltdof);
|
||||
|
||||
HYPRE_BigInt *col_starts = GetTrueDofOffsets();
|
||||
HYPRE_BigInt *row_starts = GetDofOffsets();
|
||||
|
||||
Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
|
||||
|
||||
i_diag[0] = i_offd[0] = 0;
|
||||
diag_counter = offd_counter = 0;
|
||||
for (int i = 0; i < ldof; i++)
|
||||
{
|
||||
j_diag[diag_counter++] = ltdof;
|
||||
int ltdof = GetLocalTDofNumber(i);
|
||||
if (ltdof >= 0)
|
||||
{
|
||||
j_diag[diag_counter++] = ltdof;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmap_j_offd[offd_counter].one = GetGlobalTDofNumber(i);
|
||||
cmap_j_offd[offd_counter].two = offd_counter;
|
||||
offd_counter++;
|
||||
}
|
||||
i_diag[i+1] = diag_counter;
|
||||
i_offd[i+1] = offd_counter;
|
||||
}
|
||||
else
|
||||
|
||||
SortPairs<HYPRE_BigInt, int>(cmap_j_offd, offd_counter);
|
||||
|
||||
for (int i = 0; i < offd_counter; i++)
|
||||
{
|
||||
cmap_j_offd[offd_counter].one = GetGlobalTDofNumber(i);
|
||||
cmap_j_offd[offd_counter].two = offd_counter;
|
||||
offd_counter++;
|
||||
cmap[i] = cmap_j_offd[i].one;
|
||||
j_offd[cmap_j_offd[i].two] = i;
|
||||
}
|
||||
i_diag[i+1] = diag_counter;
|
||||
i_offd[i+1] = offd_counter;
|
||||
|
||||
P = new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
|
||||
i_diag, j_diag, i_offd, j_offd,
|
||||
cmap, offd_counter);
|
||||
}
|
||||
|
||||
SortPairs<HYPRE_BigInt, int>(cmap_j_offd, offd_counter);
|
||||
|
||||
for (int i = 0; i < offd_counter; i++)
|
||||
else
|
||||
{
|
||||
cmap[i] = cmap_j_offd[i].one;
|
||||
j_offd[cmap_j_offd[i].two] = i;
|
||||
}
|
||||
// Some shared dofs will be linear combinations of others
|
||||
int ldof = GetVSize();
|
||||
int ltdof = TrueVSize();
|
||||
|
||||
P = new HypreParMatrix(MyComm, MyRank, NRanks, row_starts, col_starts,
|
||||
i_diag, j_diag, i_offd, j_offd, cmap, offd_counter);
|
||||
HYPRE_Int gdof = -1;
|
||||
HYPRE_Int gtdof = -1;
|
||||
|
||||
MPI_Allreduce(&ldof, &gdof, 1, HYPRE_MPI_INT, MPI_SUM, MyComm);
|
||||
MPI_Allreduce(<dof, >dof, 1, HYPRE_MPI_INT, MPI_SUM, MyComm);
|
||||
|
||||
// Ensure face orientations have been communicated
|
||||
pmesh->ExchangeFaceNbrData();
|
||||
|
||||
// Locate and count non-zeros in off-diagonal portion of P
|
||||
int nnz_offd = 0;
|
||||
Array<int> ldsize(ldof); ldsize = 0;
|
||||
Array<int> ltori(ldof); ltori = 0; // Local triangle orientations
|
||||
{
|
||||
int ngrps = pmesh->GetNGroups();
|
||||
int nedofs = fec->DofForGeometry(Geometry::SEGMENT);
|
||||
Array<int> sdofs;
|
||||
for (int g = 1; g < ngrps; g++)
|
||||
{
|
||||
if (pmesh->gtopo.IAmMaster(g))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (int ei=0; ei<pmesh->GroupNEdges(g); ei++)
|
||||
{
|
||||
this->GetSharedEdgeDofs(g, ei, sdofs);
|
||||
for (int i=0; i<sdofs.Size(); i++)
|
||||
{
|
||||
int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
|
||||
if (ldsize[ind] == 0) { nnz_offd++; }
|
||||
ldsize[ind] = 1;
|
||||
}
|
||||
}
|
||||
for (int fi=0; fi<pmesh->GroupNTriangles(g); fi++)
|
||||
{
|
||||
int face, ori, info1, info2;
|
||||
pmesh->GroupTriangle(g, fi, face, ori);
|
||||
pmesh->GetFaceInfos(face, &info1, &info2);
|
||||
this->GetSharedTriangleDofs(g, fi, sdofs);
|
||||
for (int i=0; i<3*nedofs; i++)
|
||||
{
|
||||
int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
|
||||
if (ldsize[ind] == 0) { nnz_offd++; }
|
||||
ldsize[ind] = 1;
|
||||
}
|
||||
for (int i=3*nedofs; i<sdofs.Size(); i++)
|
||||
{
|
||||
if (ldsize[sdofs[i]] == 0) { nnz_offd += 2; }
|
||||
ldsize[sdofs[i]] = 2;
|
||||
ltori[sdofs[i]] = info2 % 64;
|
||||
}
|
||||
}
|
||||
for (int fi=0; fi<pmesh->GroupNQuadrilaterals(g); fi++)
|
||||
{
|
||||
this->GetSharedQuadrilateralDofs(g, fi, sdofs);
|
||||
for (int i=0; i<sdofs.Size(); i++)
|
||||
{
|
||||
int ind = (sdofs[i]>=0) ? sdofs[i] : (-sdofs[i]-1);
|
||||
if (ldsize[ind] == 0) { nnz_offd++; }
|
||||
ldsize[ind] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HYPRE_Int *i_diag = new HYPRE_Int[ldof+1];
|
||||
HYPRE_Int *j_diag = new HYPRE_Int[ltdof];
|
||||
double *d_diag = new double[ltdof];
|
||||
int diag_counter;
|
||||
|
||||
HYPRE_Int *i_offd = new HYPRE_Int[ldof+1];
|
||||
HYPRE_Int *j_offd = new HYPRE_Int[nnz_offd];
|
||||
double *d_offd = new double[nnz_offd];
|
||||
int offd_counter;
|
||||
|
||||
HYPRE_BigInt *cmap = new HYPRE_BigInt[ldof-ltdof];
|
||||
|
||||
HYPRE_BigInt *col_starts = GetTrueDofOffsets();
|
||||
HYPRE_BigInt *row_starts = GetDofOffsets();
|
||||
|
||||
Array<Pair<HYPRE_BigInt, int> > cmap_j_offd(ldof-ltdof);
|
||||
|
||||
i_diag[0] = i_offd[0] = 0;
|
||||
diag_counter = offd_counter = 0;
|
||||
int offd_col_counter = 0;
|
||||
for (int i = 0; i < ldof; i++)
|
||||
{
|
||||
int ltdof = GetLocalTDofNumber(i);
|
||||
if (ltdof >= 0)
|
||||
{
|
||||
j_diag[diag_counter] = ltdof;
|
||||
d_diag[diag_counter++] = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ldsize[i] == 1)
|
||||
{
|
||||
cmap_j_offd[offd_col_counter].one = GetGlobalTDofNumber(i);
|
||||
cmap_j_offd[offd_col_counter].two = offd_counter;
|
||||
offd_counter++;
|
||||
offd_col_counter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmap_j_offd[offd_col_counter].one = GetGlobalTDofNumber(i);
|
||||
cmap_j_offd[offd_col_counter].two = offd_counter;
|
||||
offd_counter += 2;
|
||||
offd_col_counter++;
|
||||
i_diag[i+1] = diag_counter;
|
||||
i_offd[i+1] = offd_counter;
|
||||
i++;
|
||||
cmap_j_offd[offd_col_counter].one = GetGlobalTDofNumber(i);
|
||||
cmap_j_offd[offd_col_counter].two = offd_counter;
|
||||
offd_counter += 2;
|
||||
offd_col_counter++;
|
||||
}
|
||||
}
|
||||
i_diag[i+1] = diag_counter;
|
||||
i_offd[i+1] = offd_counter;
|
||||
}
|
||||
|
||||
SortPairs<HYPRE_BigInt, int>(cmap_j_offd, offd_col_counter);
|
||||
|
||||
for (int i = 0; i < nnz_offd; i++)
|
||||
{
|
||||
j_offd[i] = -1;
|
||||
d_offd[i] = 0.0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < offd_col_counter; i++)
|
||||
{
|
||||
cmap[i] = cmap_j_offd[i].one;
|
||||
j_offd[cmap_j_offd[i].two] = i;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ldof; i++)
|
||||
{
|
||||
if (i_offd[i+1] == i_offd[i] + 1)
|
||||
{
|
||||
d_offd[i_offd[i]] = 1.0;
|
||||
}
|
||||
else if (i_offd[i+1] == i_offd[i] + 2)
|
||||
{
|
||||
const double * T = ND_DofTransformation
|
||||
::GetFaceTransform(ltori[i]).GetData();
|
||||
j_offd[i_offd[i] + 1] = j_offd[i_offd[i]] + 1;
|
||||
d_offd[i_offd[i]] = T[0]; d_offd[i_offd[i] + 1] = T[2];
|
||||
i++;
|
||||
j_offd[i_offd[i] + 1] = j_offd[i_offd[i]];
|
||||
j_offd[i_offd[i]] = j_offd[i_offd[i] + 1] - 1;
|
||||
d_offd[i_offd[i]] = T[1]; d_offd[i_offd[i] + 1] = T[3];
|
||||
}
|
||||
}
|
||||
|
||||
P = new HypreParMatrix(MyComm, gdof, gtdof, row_starts, col_starts,
|
||||
i_diag, j_diag, d_diag, i_offd, j_offd, d_offd,
|
||||
offd_col_counter, cmap);
|
||||
}
|
||||
|
||||
SparseMatrix Pdiag;
|
||||
P->GetDiag(Pdiag);
|
||||
@@ -913,6 +1144,8 @@ const Operator *ParFiniteElementSpace::GetProlongationMatrix() const
|
||||
{
|
||||
if (Pconf) { return Pconf; }
|
||||
|
||||
if (nd_strias) { return Dof_TrueDof_Matrix(); }
|
||||
|
||||
if (NRanks == 1)
|
||||
{
|
||||
Pconf = new IdentityOperator(GetTrueVSize());
|
||||
@@ -1216,10 +1449,29 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
|
||||
delete [] requests;
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
DofTransformation *ParFiniteElementSpace::GetFaceNbrElementVDofs(
|
||||
int i, Array<int> &vdofs) const
|
||||
{
|
||||
face_nbr_element_dof.GetRow(i, vdofs);
|
||||
|
||||
DofTransformation *doftrans = NULL;
|
||||
Geometry::Type geom = GetFaceNbrFE(i)->GetGeomType();
|
||||
if (DoFTrans[geom])
|
||||
{
|
||||
Array<int> F, Fo;
|
||||
pmesh->GetFaceNbrElementFaces(pmesh->GetNE() + i, F, Fo);
|
||||
doftrans = DoFTrans[geom];
|
||||
doftrans->SetFaceOrientations(Fo);
|
||||
}
|
||||
if (vdim == 1 || doftrans == NULL)
|
||||
{
|
||||
return doftrans;
|
||||
}
|
||||
else
|
||||
{
|
||||
VDoFTrans.SetDofTransformation(*doftrans);
|
||||
return &VDoFTrans;
|
||||
}
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
|
||||
@@ -1278,12 +1530,17 @@ const FiniteElement *ParFiniteElementSpace::GetFaceNbrFaceFE(int i) const
|
||||
|
||||
void ParFiniteElementSpace::Lose_Dof_TrueDof_Matrix()
|
||||
{
|
||||
P -> StealData();
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParCSRMatrix *csrP = (hypre_ParCSRMatrix*)(*P);
|
||||
hypre_ParCSRMatrixOwnsRowStarts(csrP) = 1;
|
||||
hypre_ParCSRMatrixOwnsColStarts(csrP) = 1;
|
||||
P -> StealData();
|
||||
dof_offsets.LoseData();
|
||||
tdof_offsets.LoseData();
|
||||
#else
|
||||
dof_offsets.DeleteAll();
|
||||
tdof_offsets.DeleteAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::ConstructTrueDofs()
|
||||
@@ -2526,7 +2783,8 @@ static int_type* make_j_array(int_type* I, int nrows)
|
||||
|
||||
HypreParMatrix*
|
||||
ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
|
||||
const Table* old_elem_dof)
|
||||
const Table* old_elem_dof,
|
||||
const Table* old_elem_fos)
|
||||
{
|
||||
MFEM_VERIFY(Nonconforming(), "Only supported for nonconforming meshes.");
|
||||
MFEM_VERIFY(old_dof_offsets.Size(), "ParFiniteElementSpace::Update needs to "
|
||||
@@ -2651,7 +2909,8 @@ struct DerefDofMessage
|
||||
|
||||
HypreParMatrix*
|
||||
ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
|
||||
const Table* old_elem_dof)
|
||||
const Table* old_elem_dof,
|
||||
const Table *old_elem_fos)
|
||||
{
|
||||
int nrk = HYPRE_AssumedPartitionCheck() ? 2 : NRanks;
|
||||
|
||||
@@ -3007,13 +3266,16 @@ void ParFiniteElementSpace::Update(bool want_transform)
|
||||
}
|
||||
|
||||
Table* old_elem_dof = NULL;
|
||||
Table* old_elem_fos = NULL;
|
||||
int old_ndofs;
|
||||
|
||||
// save old DOF table
|
||||
if (want_transform)
|
||||
{
|
||||
old_elem_dof = elem_dof;
|
||||
old_elem_fos = elem_fos;
|
||||
elem_dof = NULL;
|
||||
elem_fos = NULL;
|
||||
old_ndofs = ndofs;
|
||||
Swap(dof_offsets, old_dof_offsets);
|
||||
}
|
||||
@@ -3035,22 +3297,25 @@ void ParFiniteElementSpace::Update(bool want_transform)
|
||||
{
|
||||
if (Th.Type() != Operator::MFEM_SPARSEMAT)
|
||||
{
|
||||
Th.Reset(new RefinementOperator(this, old_elem_dof, old_ndofs));
|
||||
Th.Reset(new RefinementOperator(this, old_elem_dof,
|
||||
old_elem_fos, old_ndofs));
|
||||
// The RefinementOperator takes ownership of 'old_elem_dofs', so
|
||||
// we no longer own it:
|
||||
old_elem_dof = NULL;
|
||||
old_elem_fos = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// calculate fully assembled matrix
|
||||
Th.Reset(RefinementMatrix(old_ndofs, old_elem_dof));
|
||||
Th.Reset(RefinementMatrix(old_ndofs, old_elem_dof, old_elem_fos));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Mesh::DEREFINE:
|
||||
{
|
||||
Th.Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof));
|
||||
Th.Reset(ParallelDerefinementMatrix(old_ndofs, old_elem_dof,
|
||||
old_elem_fos));
|
||||
if (Nonconforming())
|
||||
{
|
||||
Th.SetOperatorOwner(false);
|
||||
@@ -3062,7 +3327,7 @@ void ParFiniteElementSpace::Update(bool want_transform)
|
||||
|
||||
case Mesh::REBALANCE:
|
||||
{
|
||||
Th.Reset(RebalanceMatrix(old_ndofs, old_elem_dof));
|
||||
Th.Reset(RebalanceMatrix(old_ndofs, old_elem_dof, old_elem_fos));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3071,6 +3336,7 @@ void ParFiniteElementSpace::Update(bool want_transform)
|
||||
}
|
||||
|
||||
delete old_elem_dof;
|
||||
delete old_elem_fos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-5
@@ -87,6 +87,12 @@ private:
|
||||
this is a TransposeOperator wrapping R. */
|
||||
mutable Operator *R_transpose;
|
||||
|
||||
/// Flag indicating the existence of shared triangles with interior ND dofs
|
||||
bool nd_strias;
|
||||
|
||||
/// Resets nd_strias flag at constuction or after rebalancing
|
||||
void CheckNDSTriaDofs();
|
||||
|
||||
ParNURBSExtension *pNURBSext() const
|
||||
{ return dynamic_cast<ParNURBSExtension *>(NURBSext); }
|
||||
|
||||
@@ -174,14 +180,16 @@ private:
|
||||
The result is a parallel permutation matrix that can be used to update
|
||||
all grid functions defined on this space. */
|
||||
HypreParMatrix* RebalanceMatrix(int old_ndofs,
|
||||
const Table* old_elem_dof);
|
||||
const Table* old_elem_dof,
|
||||
const Table* old_elem_fos);
|
||||
|
||||
/** Calculate a GridFunction restriction matrix after mesh derefinement.
|
||||
The matrix is constructed so that the new grid function interpolates
|
||||
the original function, i.e., the original function is evaluated at the
|
||||
nodes of the coarse function. */
|
||||
HypreParMatrix* ParallelDerefinementMatrix(int old_ndofs,
|
||||
const Table *old_elem_dof);
|
||||
const Table *old_elem_dof,
|
||||
const Table *old_elem_fos);
|
||||
|
||||
/// Updates the internal mesh pointer. @warning @a new_mesh must be
|
||||
/// <b>topologically identical</b> to the existing mesh. Used if the address
|
||||
@@ -202,6 +210,8 @@ public:
|
||||
int num_face_nbr_dofs;
|
||||
// Face-neighbor-element to face-neighbor dof
|
||||
Table face_nbr_element_dof;
|
||||
// Face-neighbor-element face orientations
|
||||
Table face_nbr_element_fos;
|
||||
// Face-neighbor to ldof in the face-neighbor numbering
|
||||
Table face_nbr_ldof;
|
||||
// The global ldof indices of the face-neighbor dofs
|
||||
@@ -279,10 +289,10 @@ public:
|
||||
virtual int GetTrueVSize() const { return ltdof_size; }
|
||||
|
||||
/// Returns indexes of degrees of freedom in array dofs for i'th element.
|
||||
virtual void GetElementDofs(int i, Array<int> &dofs) const;
|
||||
virtual DofTransformation *GetElementDofs(int i, Array<int> &dofs) const;
|
||||
|
||||
/// Returns indexes of degrees of freedom for i'th boundary element.
|
||||
virtual void GetBdrElementDofs(int i, Array<int> &dofs) const;
|
||||
virtual DofTransformation *GetBdrElementDofs(int i, Array<int> &dofs) const;
|
||||
|
||||
/** Returns the indexes of the degrees of freedom for i'th face
|
||||
including the dofs for the edges and the vertices of the face. */
|
||||
@@ -382,7 +392,7 @@ public:
|
||||
// Face-neighbor functions
|
||||
void ExchangeFaceNbrData();
|
||||
int GetFaceNbrVSize() const { return num_face_nbr_dofs; }
|
||||
void GetFaceNbrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
DofTransformation *GetFaceNbrElementVDofs(int i, Array<int> &vdofs) const;
|
||||
void GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const;
|
||||
const FiniteElement *GetFaceNbrFE(int i) const;
|
||||
const FiniteElement *GetFaceNbrFaceFE(int i) const;
|
||||
@@ -397,6 +407,8 @@ public:
|
||||
bool Conforming() const { return pmesh->pncmesh == NULL && !nonconf_P; }
|
||||
bool Nonconforming() const { return pmesh->pncmesh != NULL || nonconf_P; }
|
||||
|
||||
bool SharedNDTriangleDofs() const { return nd_strias; }
|
||||
|
||||
// Transfer parallel true-dof data from coarse_fes, defined on a coarse mesh,
|
||||
// to this FE space, defined on a refined mesh. See full documentation in the
|
||||
// base class, FiniteElementSpace::GetTrueTransferOperator.
|
||||
|
||||
+12
-2
@@ -325,9 +325,14 @@ void ParGridFunction::GetVectorValue(int i, const IntegrationPoint &ip,
|
||||
if (nbr_el_no >= 0)
|
||||
{
|
||||
Array<int> dofs;
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, dofs);
|
||||
DofTransformation * doftrans = pfes->GetFaceNbrElementVDofs(nbr_el_no,
|
||||
dofs);
|
||||
Vector loc_data;
|
||||
face_nbr_data.GetSubVector(dofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
const FiniteElement *FElem = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
int dof = FElem->GetDof();
|
||||
if (FElem->GetRangeType() == FiniteElement::SCALAR)
|
||||
@@ -437,12 +442,17 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
|
||||
}
|
||||
|
||||
Array<int> vdofs;
|
||||
pfes->GetFaceNbrElementVDofs(nbr_el_no, vdofs);
|
||||
DofTransformation * doftrans = pfes->GetFaceNbrElementVDofs(nbr_el_no,
|
||||
vdofs);
|
||||
const FiniteElement *fe = pfes->GetFaceNbrFE(nbr_el_no);
|
||||
|
||||
int dof = fe->GetDof();
|
||||
Vector loc_data;
|
||||
face_nbr_data.GetSubVector(vdofs, loc_data);
|
||||
if (doftrans)
|
||||
{
|
||||
doftrans->InvTransformPrimal(loc_data);
|
||||
}
|
||||
if (fe->GetRangeType() == FiniteElement::SCALAR)
|
||||
{
|
||||
Vector shape(dof);
|
||||
|
||||
@@ -65,6 +65,11 @@ public:
|
||||
ParGridFunction(ParFiniteElementSpace *pf, double *data) :
|
||||
GridFunction(pf, data), pfes(pf) { }
|
||||
|
||||
/** @brief Construct a ParGridFunction using previously allocated Vector
|
||||
@a base starting at the given offset, @a base_offset. */
|
||||
ParGridFunction(ParFiniteElementSpace *pf, Vector &base, int base_offset = 0)
|
||||
: GridFunction(pf, base, base_offset), pfes(pf) { }
|
||||
|
||||
/// Construct a ParGridFunction using a GridFunction as external data.
|
||||
/** The parallel space @a *pf and the space used by @a *gf should match. The
|
||||
data from @a *gf is used as the local data of the ParGridFunction on each
|
||||
|
||||
@@ -33,6 +33,10 @@ ParL2FaceRestriction::ParL2FaceRestriction(const ParFiniteElementSpace &fes,
|
||||
// If fespace == L2
|
||||
const ParFiniteElementSpace &pfes =
|
||||
static_cast<const ParFiniteElementSpace&>(this->fes);
|
||||
|
||||
// Ensure the face neighbor data is constructed
|
||||
pfes.GetParMesh()->ExchangeFaceNbrData();
|
||||
|
||||
const FiniteElement *fe = pfes.GetFE(0);
|
||||
const TensorBasisElement *tfe = dynamic_cast<const TensorBasisElement*>(fe);
|
||||
MFEM_VERIFY(tfe != NULL &&
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
#include "../general/forall.hpp"
|
||||
#include <climits>
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "pfespace.hpp"
|
||||
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
@@ -675,6 +681,19 @@ H1FaceRestriction::H1FaceRestriction(const FiniteElementSpace &fes,
|
||||
gather_indices(nf*dof)
|
||||
{
|
||||
if (nf==0) { return; }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
// If the underlying finite element space is parallel, ensure the face
|
||||
// neighbor information is generated.
|
||||
if (const ParFiniteElementSpace *pfes
|
||||
= dynamic_cast<const ParFiniteElementSpace*>(&fes))
|
||||
{
|
||||
pfes->GetParMesh()->ExchangeFaceNbrData();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// If fespace == H1
|
||||
const FiniteElement *fe = fes.GetFE(0);
|
||||
const TensorBasisElement *tfe = dynamic_cast<const TensorBasisElement*>(fe);
|
||||
|
||||
+382
-67
@@ -1314,33 +1314,61 @@ static inline void device_copy(double *d_dest, const double *d_src, int size)
|
||||
} // namespace internal
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void DiscreteAdaptTC::FinalizeParDiscreteTargetSpec(const ParGridFunction
|
||||
&tspec_)
|
||||
void DiscreteAdaptTC::FinalizeParDiscreteTargetSpec(const ParGridFunction &t)
|
||||
{
|
||||
MFEM_VERIFY(adapt_eval, "SetAdaptivityEvaluator() has not been called!")
|
||||
MFEM_VERIFY(ncomp > 0, "No target specifications have been set!");
|
||||
|
||||
ParFiniteElementSpace *ptspec_fes = tspec_.ParFESpace();
|
||||
ParFiniteElementSpace *ptspec_fes = t.ParFESpace();
|
||||
|
||||
adapt_eval->SetParMetaInfo(*ptspec_fes->GetParMesh(),
|
||||
*ptspec_fes->FEColl(), ncomp);
|
||||
adapt_eval->SetInitialField(*tspec_fes->GetMesh()->GetNodes(), tspec);
|
||||
adapt_eval->SetInitialField(*ptspec_fes->GetMesh()->GetNodes(), tspec);
|
||||
|
||||
tspec_sav = tspec;
|
||||
|
||||
delete tspec_fesv;
|
||||
tspec_fesv = new FiniteElementSpace(tspec_fes->GetMesh(),
|
||||
tspec_fes->FEColl(), ncomp);
|
||||
tspec_fesv = new FiniteElementSpace(ptspec_fes->GetMesh(),
|
||||
ptspec_fes->FEColl(), ncomp);
|
||||
|
||||
delete ptspec_fesv;
|
||||
ptspec_fesv = new ParFiniteElementSpace(ptspec_fes->GetParMesh(),
|
||||
ptspec_fes->FEColl(), ncomp);
|
||||
|
||||
delete tspec_pgf;
|
||||
tspec_pgf = new ParGridFunction(ptspec_fesv, tspec);
|
||||
tspec_gf = tspec_pgf;
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::ParUpdateAfterMeshTopologyChange()
|
||||
{
|
||||
ptspec_fesv->Update();
|
||||
if (tspec_fesv)
|
||||
{
|
||||
delete tspec_fesv;
|
||||
tspec_fesv = new FiniteElementSpace(ptspec_fesv->GetMesh(),
|
||||
ptspec_fesv->FEColl(), ncomp);
|
||||
}
|
||||
tspec_pgf->Update();
|
||||
tspec_gf = tspec_pgf;
|
||||
tspec.SetDataAndSize(tspec_pgf->GetData(), tspec_pgf->Size());
|
||||
tspec_sav = tspec;
|
||||
|
||||
adapt_eval->SetParMetaInfo(*ptspec_fesv->GetParMesh(),
|
||||
*ptspec_fesv->FEColl(), ncomp);
|
||||
adapt_eval->SetInitialField(*ptspec_fesv->GetMesh()->GetNodes(), tspec);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetTspecAtIndex(int idx, const ParGridFunction &tspec_)
|
||||
{
|
||||
const int vdim = tspec_.FESpace()->GetVDim(),
|
||||
dof_cnt = tspec_.Size()/vdim;
|
||||
const int vdim = tspec_.FESpace()->GetVDim(),
|
||||
ndof = tspec_.FESpace()->GetNDofs();
|
||||
MFEM_VERIFY(ndof == tspec.Size()/ncomp, "Inconsistency in SetTspecAtIndex.");
|
||||
|
||||
const auto tspec__d = tspec_.Read();
|
||||
auto tspec_d = tspec.ReadWrite();
|
||||
const int offset = idx*dof_cnt;
|
||||
internal::device_copy(tspec_d + offset, tspec__d, dof_cnt*vdim);
|
||||
const int offset = idx*ndof;
|
||||
internal::device_copy(tspec_d + offset, tspec__d, ndof*vdim);
|
||||
FinalizeParDiscreteTargetSpec(tspec_);
|
||||
}
|
||||
|
||||
@@ -1360,78 +1388,71 @@ void DiscreteAdaptTC::SetParDiscreteTargetSkew(const ParGridFunction &tspec_)
|
||||
FinalizeParDiscreteTargetSpec(tspec_);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetParDiscreteTargetAspectRatio(const ParGridFunction
|
||||
&tspec_)
|
||||
void DiscreteAdaptTC::SetParDiscreteTargetAspectRatio(const ParGridFunction &ar)
|
||||
{
|
||||
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, tspec_); return; }
|
||||
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, ar); return; }
|
||||
aspectratioidx = ncomp;
|
||||
SetDiscreteTargetBase(tspec_);
|
||||
FinalizeParDiscreteTargetSpec(tspec_);
|
||||
SetDiscreteTargetBase(ar);
|
||||
FinalizeParDiscreteTargetSpec(ar);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetParDiscreteTargetOrientation(const ParGridFunction
|
||||
&tspec_)
|
||||
void DiscreteAdaptTC::SetParDiscreteTargetOrientation(const ParGridFunction &o)
|
||||
{
|
||||
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, tspec_); return; }
|
||||
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, o); return; }
|
||||
orientationidx = ncomp;
|
||||
SetDiscreteTargetBase(tspec_);
|
||||
FinalizeParDiscreteTargetSpec(tspec_);
|
||||
SetDiscreteTargetBase(o);
|
||||
FinalizeParDiscreteTargetSpec(o);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetParDiscreteTargetSpec(const ParGridFunction &tspec_)
|
||||
{
|
||||
SetParDiscreteTargetSize(tspec_);
|
||||
FinalizeParDiscreteTargetSpec(tspec_);
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
void DiscreteAdaptTC::SetDiscreteTargetBase(const GridFunction &tspec_)
|
||||
{
|
||||
const int vdim = tspec_.FESpace()->GetVDim(),
|
||||
dof_cnt = tspec_.Size()/vdim;
|
||||
const int vdim = tspec_.FESpace()->GetVDim(),
|
||||
ndof = tspec_.FESpace()->GetNDofs();
|
||||
|
||||
ncomp += vdim;
|
||||
|
||||
delete tspec_fes;
|
||||
tspec_fes = new FiniteElementSpace(tspec_.FESpace()->GetMesh(),
|
||||
tspec_.FESpace()->FEColl(), 1);
|
||||
|
||||
// need to append data to tspec
|
||||
// make a copy of tspec->tspec_temp, increase its size, and
|
||||
// copy data from tspec_temp -> tspec, then add new entries
|
||||
Vector tspec_temp = tspec;
|
||||
tspec.UseDevice(true);
|
||||
tspec_sav.UseDevice(true);
|
||||
tspec.SetSize(ncomp*dof_cnt);
|
||||
tspec.SetSize(ncomp*ndof);
|
||||
|
||||
const auto tspec_temp_d = tspec_temp.Read();
|
||||
auto tspec_d = tspec.ReadWrite();
|
||||
internal::device_copy(tspec_d, tspec_temp_d, tspec_temp.Size());
|
||||
|
||||
const auto tspec__d = tspec_.Read();
|
||||
const int offset = (ncomp-vdim)*dof_cnt;
|
||||
internal::device_copy(tspec_d + offset, tspec__d, dof_cnt*vdim);
|
||||
const int offset = (ncomp-vdim)*ndof;
|
||||
internal::device_copy(tspec_d + offset, tspec__d, ndof*vdim);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetTspecAtIndex(int idx, const GridFunction &tspec_)
|
||||
{
|
||||
const int vdim = tspec_.FESpace()->GetVDim(),
|
||||
dof_cnt = tspec_.Size()/vdim;
|
||||
const int vdim = tspec_.FESpace()->GetVDim(),
|
||||
ndof = tspec_.FESpace()->GetNDofs();
|
||||
MFEM_VERIFY(ndof == tspec.Size()/ncomp, "Inconsistency in SetTargetSpec.");
|
||||
|
||||
const auto tspec__d = tspec_.Read();
|
||||
auto tspec_d = tspec.ReadWrite();
|
||||
const int offset = idx*dof_cnt;
|
||||
internal::device_copy(tspec_d + offset, tspec__d, dof_cnt*vdim);
|
||||
FinalizeSerialDiscreteTargetSpec();
|
||||
const int offset = idx*ndof;
|
||||
internal::device_copy(tspec_d + offset, tspec__d, ndof*vdim);
|
||||
FinalizeSerialDiscreteTargetSpec(tspec_);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetSerialDiscreteTargetSize(const GridFunction &tspec_)
|
||||
{
|
||||
|
||||
if (sizeidx > -1) { SetTspecAtIndex(sizeidx, tspec_); return; }
|
||||
sizeidx = ncomp;
|
||||
SetDiscreteTargetBase(tspec_);
|
||||
FinalizeSerialDiscreteTargetSpec();
|
||||
FinalizeSerialDiscreteTargetSpec(tspec_);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetSerialDiscreteTargetSkew(const GridFunction &tspec_)
|
||||
@@ -1439,32 +1460,31 @@ void DiscreteAdaptTC::SetSerialDiscreteTargetSkew(const GridFunction &tspec_)
|
||||
if (skewidx > -1) { SetTspecAtIndex(skewidx, tspec_); return; }
|
||||
skewidx = ncomp;
|
||||
SetDiscreteTargetBase(tspec_);
|
||||
FinalizeSerialDiscreteTargetSpec();
|
||||
FinalizeSerialDiscreteTargetSpec(tspec_);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetSerialDiscreteTargetAspectRatio(
|
||||
const GridFunction &tspec_)
|
||||
void DiscreteAdaptTC::SetSerialDiscreteTargetAspectRatio(const GridFunction &ar)
|
||||
{
|
||||
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, tspec_); return; }
|
||||
if (aspectratioidx > -1) { SetTspecAtIndex(aspectratioidx, ar); return; }
|
||||
aspectratioidx = ncomp;
|
||||
SetDiscreteTargetBase(tspec_);
|
||||
FinalizeSerialDiscreteTargetSpec();
|
||||
SetDiscreteTargetBase(ar);
|
||||
FinalizeSerialDiscreteTargetSpec(ar);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetSerialDiscreteTargetOrientation(
|
||||
const GridFunction &tspec_)
|
||||
void DiscreteAdaptTC::SetSerialDiscreteTargetOrientation(const GridFunction &o)
|
||||
{
|
||||
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, tspec_); return; }
|
||||
if (orientationidx > -1) { SetTspecAtIndex(orientationidx, o); return; }
|
||||
orientationidx = ncomp;
|
||||
SetDiscreteTargetBase(tspec_);
|
||||
FinalizeSerialDiscreteTargetSpec();
|
||||
SetDiscreteTargetBase(o);
|
||||
FinalizeSerialDiscreteTargetSpec(o);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::FinalizeSerialDiscreteTargetSpec()
|
||||
void DiscreteAdaptTC::FinalizeSerialDiscreteTargetSpec(const GridFunction &t)
|
||||
{
|
||||
MFEM_VERIFY(adapt_eval, "SetAdaptivityEvaluator() has not been called!")
|
||||
MFEM_VERIFY(ncomp > 0, "No target specifications have been set!");
|
||||
|
||||
const FiniteElementSpace *tspec_fes = t.FESpace();
|
||||
adapt_eval->SetSerialMetaInfo(*tspec_fes->GetMesh(),
|
||||
*tspec_fes->FEColl(), ncomp);
|
||||
adapt_eval->SetInitialField(*tspec_fes->GetMesh()->GetNodes(), tspec);
|
||||
@@ -1474,12 +1494,40 @@ void DiscreteAdaptTC::FinalizeSerialDiscreteTargetSpec()
|
||||
delete tspec_fesv;
|
||||
tspec_fesv = new FiniteElementSpace(tspec_fes->GetMesh(),
|
||||
tspec_fes->FEColl(), ncomp);
|
||||
|
||||
delete tspec_gf;
|
||||
tspec_gf = new GridFunction(tspec_fesv, tspec);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::GetDiscreteTargetSpec(GridFunction &tspec_, int idx)
|
||||
{
|
||||
if (idx < 0) { return; }
|
||||
const int ndof = tspec_.FESpace()->GetNDofs(),
|
||||
vdim = tspec_.FESpace()->GetVDim();
|
||||
MFEM_VERIFY(ndof == tspec.Size()/ncomp,
|
||||
"Inconsistency in GetSerialDiscreteTargetSpec.");
|
||||
|
||||
for (int i = 0; i < ndof*vdim; i++)
|
||||
{
|
||||
tspec_(i) = tspec(i + idx*ndof);
|
||||
}
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::UpdateAfterMeshTopologyChange()
|
||||
{
|
||||
tspec_fesv->Update();
|
||||
tspec_gf->Update();
|
||||
tspec.SetDataAndSize(tspec_gf->GetData(), tspec_gf->Size());
|
||||
tspec_sav = tspec;
|
||||
|
||||
adapt_eval->SetSerialMetaInfo(*tspec_fesv->GetMesh(),
|
||||
*tspec_fesv->FEColl(), ncomp);
|
||||
adapt_eval->SetInitialField(*tspec_fesv->GetMesh()->GetNodes(), tspec);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetSerialDiscreteTargetSpec(const GridFunction &tspec_)
|
||||
{
|
||||
SetSerialDiscreteTargetSize(tspec_);
|
||||
FinalizeSerialDiscreteTargetSpec();
|
||||
}
|
||||
|
||||
|
||||
@@ -1509,7 +1557,7 @@ void DiscreteAdaptTC::UpdateTargetSpecificationAtNode(const FiniteElement &el,
|
||||
MFEM_VERIFY(tspec.Size() > 0, "Target specification is not set!");
|
||||
|
||||
Array<int> dofs;
|
||||
tspec_fes->GetElementDofs(T.ElementNo, dofs);
|
||||
tspec_fesv->GetElementDofs(T.ElementNo, dofs);
|
||||
const int cnt = tspec.Size()/ncomp; // dofs per scalar-field
|
||||
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
@@ -1524,7 +1572,7 @@ void DiscreteAdaptTC::RestoreTargetSpecificationAtNode(ElementTransformation &T,
|
||||
MFEM_VERIFY(tspec.Size() > 0, "Target specification is not set!");
|
||||
|
||||
Array<int> dofs;
|
||||
tspec_fes->GetElementDofs(T.ElementNo, dofs);
|
||||
tspec_fesv->GetElementDofs(T.ElementNo, dofs);
|
||||
const int cnt = tspec.Size()/ncomp;
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
{
|
||||
@@ -1532,6 +1580,40 @@ void DiscreteAdaptTC::RestoreTargetSpecificationAtNode(ElementTransformation &T,
|
||||
}
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetTspecFromIntRule(int e_id,
|
||||
const IntegrationRule &intrule)
|
||||
{
|
||||
switch (target_type)
|
||||
{
|
||||
case IDEAL_SHAPE_GIVEN_SIZE:
|
||||
case GIVEN_SHAPE_AND_SIZE:
|
||||
{
|
||||
const int ndofs = tspec_fesv->GetFE(e_id)->GetDof(),
|
||||
ntspec_dofs = ndofs*ncomp;
|
||||
|
||||
Vector tspec_vals(ntspec_dofs);
|
||||
|
||||
Array<int> dofs;
|
||||
tspec_fesv->GetElementVDofs(e_id, dofs);
|
||||
tspec.GetSubVector(dofs, tspec_vals);
|
||||
DenseMatrix tr;
|
||||
tspec_gf->GetVectorValues(e_id, intrule, tspec_refine, tr);
|
||||
tspec_refine.Transpose();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MFEM_ABORT("Incompatible target type for discrete adaptation!");
|
||||
}
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::SetTspecDataForDerefinement(FiniteElementSpace *fes)
|
||||
{
|
||||
coarse_tspec_fesv = fes;
|
||||
const Operator *c_op = fes->GetUpdateOperator();
|
||||
tspec_derefine.SetSize(c_op->Height());
|
||||
c_op->Mult(tspec, tspec_derefine);
|
||||
}
|
||||
|
||||
void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
|
||||
const IntegrationRule &ir,
|
||||
const Vector &elfun,
|
||||
@@ -1542,6 +1624,8 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
|
||||
nqp = ir.GetNPoints();
|
||||
Jtrcomp.SetSize(dim, dim, 4*nqp);
|
||||
|
||||
FiniteElementSpace *src_fes = tspec_fesv;
|
||||
|
||||
switch (target_type)
|
||||
{
|
||||
case IDEAL_SHAPE_GIVEN_SIZE:
|
||||
@@ -1550,7 +1634,7 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
|
||||
const DenseMatrix &Wideal =
|
||||
Geometries.GetGeomToPerfGeomJac(fe.GetGeomType());
|
||||
const int dim = Wideal.Height(),
|
||||
ndofs = tspec_fes->GetFE(e_id)->GetDof(),
|
||||
ndofs = tspec_fesv->GetFE(e_id)->GetDof(),
|
||||
ntspec_dofs = ndofs*ncomp;
|
||||
|
||||
Vector shape(ndofs), tspec_vals(ntspec_dofs), par_vals,
|
||||
@@ -1561,11 +1645,29 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
|
||||
tspec_fesv->GetElementVDofs(e_id, dofs);
|
||||
tspec.UseDevice(true);
|
||||
tspec.GetSubVector(dofs, tspec_vals);
|
||||
if (tspec_refine.NumCols() > 0) // Refinement
|
||||
{
|
||||
MFEM_VERIFY(amr_el >= 0, " Target being constructed for an AMR element.");
|
||||
for (int i = 0; i < ncomp; i++)
|
||||
{
|
||||
for (int j = 0; j < ndofs; j++)
|
||||
{
|
||||
tspec_vals(j + i*ndofs) = tspec_refine(j + amr_el*ndofs, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tspec_derefine.Size() > 0) // Derefinement
|
||||
{
|
||||
dofs.SetSize(0);
|
||||
coarse_tspec_fesv->GetElementVDofs(e_id, dofs);
|
||||
tspec_derefine.GetSubVector(dofs, tspec_vals);
|
||||
src_fes = coarse_tspec_fesv;
|
||||
}
|
||||
|
||||
for (int q = 0; q < nqp; q++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(q);
|
||||
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
|
||||
src_fes->GetFE(e_id)->CalcShape(ip, shape);
|
||||
Jtr(q) = Wideal; // Initialize to identity
|
||||
for (int d = 0; d < 4; d++)
|
||||
{
|
||||
@@ -1576,9 +1678,16 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
|
||||
if (sizeidx != -1) // Set size
|
||||
{
|
||||
par_vals.SetDataAndSize(tspec_vals.GetData()+sizeidx*ndofs, ndofs);
|
||||
const double min_size = par_vals.Min();
|
||||
MFEM_VERIFY(min_size > 0.0,
|
||||
"Non-positive size propagated in the target definition.");
|
||||
double min_size = par_vals.Min();//0.001; //
|
||||
if (lim_min_size > 0.)
|
||||
{
|
||||
min_size = lim_min_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_VERIFY(min_size > 0.0,
|
||||
"Non-positive size propagated in the target definition.");
|
||||
}
|
||||
const double size = std::max(shape * par_vals, min_size);
|
||||
Jtr(q).Set(std::pow(size, 1.0/dim), Jtr(q));
|
||||
DenseMatrix Jtrcomp_q(Jtrcomp.GetData(0 + 4*q), dim, dim);
|
||||
@@ -1593,6 +1702,9 @@ void DiscreteAdaptTC::ComputeElementTargets(int e_id, const FiniteElement &fe,
|
||||
{
|
||||
par_vals.SetDataAndSize(tspec_vals.GetData()+
|
||||
aspectratioidx*ndofs, ndofs);
|
||||
const double min_size = par_vals.Min();
|
||||
MFEM_VERIFY(min_size > 0.0,
|
||||
"Non-positive aspect-ratio propagated in the target definition.");
|
||||
|
||||
const double aspectratio = shape * par_vals;
|
||||
D_rho = 0.;
|
||||
@@ -1777,7 +1889,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
|
||||
|
||||
grad_phys.Mult(par_vals, grad_ptr_c1);
|
||||
Vector grad_q(dim);
|
||||
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
|
||||
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
|
||||
grad_e_c1.MultTranspose(shape, grad_q);
|
||||
|
||||
const double min_size = par_vals.Min();
|
||||
@@ -1810,7 +1922,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
|
||||
|
||||
grad_phys.Mult(par_vals, grad_ptr_c1);
|
||||
Vector grad_q(dim);
|
||||
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
|
||||
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
|
||||
grad_e_c1.MultTranspose(shape, grad_q);
|
||||
|
||||
const double aspectratio = shape * par_vals;
|
||||
@@ -1841,7 +1953,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
|
||||
grad_phys.Mult(par_vals_c2, grad_ptr_c2);
|
||||
grad_phys.Mult(par_vals_c3, grad_ptr_c3);
|
||||
Vector grad_q1(dim), grad_q2(dim), grad_q3(dim);
|
||||
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
|
||||
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
|
||||
grad_e_c1.MultTranspose(shape, grad_q1);
|
||||
grad_e_c2.MultTranspose(shape, grad_q2);
|
||||
grad_e_c3.MultTranspose(shape, grad_q3);
|
||||
@@ -1880,7 +1992,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
|
||||
|
||||
grad_phys.Mult(par_vals, grad_ptr_c1);
|
||||
Vector grad_q(dim);
|
||||
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
|
||||
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
|
||||
grad_e_c1.MultTranspose(shape, grad_q);
|
||||
|
||||
const double skew = shape * par_vals;
|
||||
@@ -1913,7 +2025,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
|
||||
grad_phys.Mult(par_vals_c2, grad_ptr_c2);
|
||||
grad_phys.Mult(par_vals_c3, grad_ptr_c3);
|
||||
Vector grad_q1(dim), grad_q2(dim), grad_q3(dim);
|
||||
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
|
||||
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
|
||||
grad_e_c1.MultTranspose(shape, grad_q1);
|
||||
grad_e_c2.MultTranspose(shape, grad_q2);
|
||||
grad_e_c3.MultTranspose(shape, grad_q3);
|
||||
@@ -1960,7 +2072,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
|
||||
|
||||
grad_phys.Mult(par_vals, grad_ptr_c1);
|
||||
Vector grad_q(dim);
|
||||
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
|
||||
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
|
||||
grad_e_c1.MultTranspose(shape, grad_q);
|
||||
|
||||
const double theta = shape * par_vals;
|
||||
@@ -1991,7 +2103,7 @@ void DiscreteAdaptTC::ComputeElementTargetsGradient(const IntegrationRule &ir,
|
||||
grad_phys.Mult(par_vals_c2, grad_ptr_c2);
|
||||
grad_phys.Mult(par_vals_c3, grad_ptr_c3);
|
||||
Vector grad_q1(dim), grad_q2(dim), grad_q3(dim);
|
||||
tspec_fes->GetFE(e_id)->CalcShape(ip, shape);
|
||||
tspec_fesv->GetFE(e_id)->CalcShape(ip, shape);
|
||||
grad_e_c1.MultTranspose(shape, grad_q1);
|
||||
grad_e_c2.MultTranspose(shape, grad_q2);
|
||||
grad_e_c3.MultTranspose(shape, grad_q3);
|
||||
@@ -2071,7 +2183,7 @@ void DiscreteAdaptTC::UpdateGradientTargetSpecification(const Vector &x,
|
||||
{
|
||||
if (use_flag && good_tspec_grad) { return; }
|
||||
|
||||
const int dim = tspec_fes->GetFE(0)->GetDim(),
|
||||
const int dim = tspec_fesv->GetFE(0)->GetDim(),
|
||||
cnt = x.Size()/dim;
|
||||
|
||||
tspec_pert1h.SetSize(x.Size()*ncomp);
|
||||
@@ -2097,7 +2209,7 @@ void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
|
||||
|
||||
if (use_flag && good_tspec_hess) { return; }
|
||||
|
||||
const int dim = tspec_fes->GetFE(0)->GetDim(),
|
||||
const int dim = tspec_fesv->GetFE(0)->GetDim(),
|
||||
cnt = x.Size()/dim,
|
||||
totmix = 1+2*(dim-2);
|
||||
|
||||
@@ -2145,6 +2257,16 @@ void DiscreteAdaptTC::UpdateHessianTargetSpecification(const Vector &x,
|
||||
good_tspec_hess = use_flag;
|
||||
}
|
||||
|
||||
DiscreteAdaptTC::~DiscreteAdaptTC()
|
||||
{
|
||||
delete tspec_gf;
|
||||
delete adapt_eval;
|
||||
delete tspec_fesv;
|
||||
#ifdef MFEM_USE_MPI
|
||||
delete ptspec_fesv;
|
||||
#endif
|
||||
}
|
||||
|
||||
void AdaptivityEvaluator::SetSerialMetaInfo(const Mesh &m,
|
||||
const FiniteElementCollection &fec,
|
||||
int num_comp)
|
||||
@@ -2258,6 +2380,7 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
|
||||
AdaptivityEvaluator &ae)
|
||||
{
|
||||
zeta_0 = &z0;
|
||||
pzeta_0 = &z0;
|
||||
delete zeta;
|
||||
zeta = new GridFunction(z0);
|
||||
coeff_zeta = &coeff;
|
||||
@@ -2270,6 +2393,33 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOP_Integrator::UpdateAfterMeshTopologyChange()
|
||||
{
|
||||
if (zeta)
|
||||
{
|
||||
zeta->Update();
|
||||
adapt_eval->SetSerialMetaInfo(*zeta->FESpace()->GetMesh(),
|
||||
*zeta->FESpace()->FEColl(), 1);
|
||||
adapt_eval->SetInitialField
|
||||
(*zeta->FESpace()->GetMesh()->GetNodes(), *zeta);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOP_Integrator::ParUpdateAfterMeshTopologyChange()
|
||||
{
|
||||
if (zeta)
|
||||
{
|
||||
zeta->Update();
|
||||
adapt_eval->SetParMetaInfo(*pzeta_0->ParFESpace()->GetParMesh(),
|
||||
*pzeta_0->ParFESpace()->FEColl(), 1);
|
||||
adapt_eval->SetInitialField
|
||||
(*zeta->FESpace()->GetMesh()->GetNodes(), *zeta);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun)
|
||||
@@ -2378,6 +2528,145 @@ double TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
|
||||
return energy;
|
||||
}
|
||||
|
||||
double TMOP_Integrator::GetRefinementElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun,
|
||||
const IntegrationRule &irule)
|
||||
{
|
||||
int dof = el.GetDof(), dim = el.GetDim(),
|
||||
NEsplit = elfun.Size() / (dof*dim), el_id = T.ElementNo;
|
||||
double energy = 0.;
|
||||
|
||||
TargetConstructor *tc = const_cast<TargetConstructor *>(targetC);
|
||||
DiscreteAdaptTC *dtc = dynamic_cast<DiscreteAdaptTC *>(tc);
|
||||
// For DiscreteAdaptTC the GridFunctions used to set the targets must be
|
||||
// mapped onto the fine elements.
|
||||
if (dtc) { dtc->SetTspecFromIntRule(el_id, irule); }
|
||||
|
||||
for (int e = 0; e < NEsplit; e++)
|
||||
{
|
||||
DSh.SetSize(dof, dim);
|
||||
Jrt.SetSize(dim);
|
||||
Jpr.SetSize(dim);
|
||||
Jpt.SetSize(dim);
|
||||
Vector elfun_child(dof*dim);
|
||||
for (int i = 0; i < dof; i++)
|
||||
{
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
// elfun is (xe1,xe2,...xen,ye1,ye2...yen) and has nodal coordinates
|
||||
// for all the children element of the parent element being considered.
|
||||
// So we must index and get (xek, yek) i.e. nodal coordinates for
|
||||
// the fine element being considered.
|
||||
elfun_child(i + d*dof) = elfun(i + e*dof + d*dof*NEsplit);
|
||||
}
|
||||
}
|
||||
PMatI.UseExternalData(elfun_child.GetData(), dof, dim);
|
||||
|
||||
const IntegrationRule &ir = EnergyIntegrationRule(el);
|
||||
|
||||
double el_energy = 0;
|
||||
DenseTensor Jtr(dim, dim, ir.GetNPoints());
|
||||
if (dtc)
|
||||
{
|
||||
// This is used to index into the tspec vector inside DiscreteAdaptTC.
|
||||
dtc->SetRefinementSubElement(e);
|
||||
}
|
||||
targetC->ComputeElementTargets(el_id, el, ir, elfun_child, Jtr);
|
||||
|
||||
// Define ref->physical transformation, wn a Coefficient is specified.
|
||||
IsoparametricTransformation *Tpr = NULL;
|
||||
if (coeff1 || coeff0)
|
||||
{
|
||||
Tpr = new IsoparametricTransformation;
|
||||
Tpr->SetFE(&el);
|
||||
Tpr->ElementNo = T.ElementNo;
|
||||
Tpr->ElementType = ElementTransformation::ELEMENT;
|
||||
Tpr->Attribute = T.Attribute;
|
||||
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
const DenseMatrix &Jtr_i = Jtr(i);
|
||||
h_metric->SetTargetJacobian(Jtr_i);
|
||||
CalcInverse(Jtr_i, Jrt);
|
||||
const double weight = ip.weight * Jtr_i.Det();
|
||||
|
||||
el.CalcDShape(ip, DSh);
|
||||
MultAtB(PMatI, DSh, Jpr);
|
||||
Mult(Jpr, Jrt, Jpt);
|
||||
|
||||
double val = metric_normal * h_metric->EvalW(Jpt);
|
||||
if (coeff1) { val *= coeff1->Eval(*Tpr, ip); }
|
||||
|
||||
el_energy += weight * val;
|
||||
delete Tpr;
|
||||
}
|
||||
energy += el_energy;
|
||||
}
|
||||
energy /= NEsplit;
|
||||
|
||||
if (dtc) { dtc->ResetRefinementTspecData(); }
|
||||
|
||||
return energy;
|
||||
}
|
||||
|
||||
double TMOP_Integrator::GetDerefinementElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun)
|
||||
{
|
||||
int dof = el.GetDof(), dim = el.GetDim();
|
||||
double energy = 0.;
|
||||
|
||||
DSh.SetSize(dof, dim);
|
||||
Jrt.SetSize(dim);
|
||||
Jpr.SetSize(dim);
|
||||
Jpt.SetSize(dim);
|
||||
PMatI.UseExternalData(elfun.GetData(), dof, dim);
|
||||
|
||||
const IntegrationRule &ir = EnergyIntegrationRule(el);
|
||||
|
||||
energy = 0.0;
|
||||
DenseTensor Jtr(dim, dim, ir.GetNPoints());
|
||||
targetC->ComputeElementTargets(T.ElementNo, el, ir, elfun, Jtr);
|
||||
|
||||
// Define ref->physical transformation, wn a Coefficient is specified.
|
||||
IsoparametricTransformation *Tpr = NULL;
|
||||
if (coeff1)
|
||||
{
|
||||
Tpr = new IsoparametricTransformation;
|
||||
Tpr->SetFE(&el);
|
||||
Tpr->ElementNo = T.ElementNo;
|
||||
Tpr->ElementType = ElementTransformation::ELEMENT;
|
||||
Tpr->Attribute = T.Attribute;
|
||||
Tpr->GetPointMat().Transpose(PMatI); // PointMat = PMatI^T
|
||||
}
|
||||
|
||||
for (int i = 0; i < ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
const DenseMatrix &Jtr_i = Jtr(i);
|
||||
h_metric->SetTargetJacobian(Jtr_i);
|
||||
CalcInverse(Jtr_i, Jrt);
|
||||
const double weight = ip.weight * Jtr_i.Det();
|
||||
|
||||
el.CalcDShape(ip, DSh);
|
||||
MultAtB(PMatI, DSh, Jpr);
|
||||
Mult(Jpr, Jrt, Jpt);
|
||||
|
||||
double val = metric_normal * h_metric->EvalW(Jpt);
|
||||
if (coeff1) { val *= coeff1->Eval(*Tpr, ip); }
|
||||
|
||||
energy += weight * val;
|
||||
}
|
||||
|
||||
delete Tpr;
|
||||
return energy;
|
||||
}
|
||||
|
||||
void TMOP_Integrator::AssembleElementVector(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun, Vector &elvect)
|
||||
@@ -3039,7 +3328,7 @@ void TMOP_Integrator::ComputeMinJac(const Vector &x,
|
||||
dx = detv_avg_min / dxscale;
|
||||
}
|
||||
|
||||
void TMOP_Integrator::UpdateAfterMeshChange(const Vector &new_x)
|
||||
void TMOP_Integrator::UpdateAfterMeshPositionChange(const Vector &new_x)
|
||||
{
|
||||
if (discr_tc)
|
||||
{
|
||||
@@ -3168,6 +3457,32 @@ void TMOPComboIntegrator::AssembleElementGrad(const FiniteElement &el,
|
||||
}
|
||||
}
|
||||
|
||||
double TMOPComboIntegrator::GetRefinementElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun,
|
||||
const IntegrationRule &irule)
|
||||
{
|
||||
double energy= 0.0;
|
||||
for (int i = 0; i < tmopi.Size(); i++)
|
||||
{
|
||||
energy += tmopi[i]->GetRefinementElementEnergy(el, T, elfun, irule);
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
|
||||
double TMOPComboIntegrator::GetDerefinementElementEnergy(
|
||||
const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun)
|
||||
{
|
||||
double energy= 0.0;
|
||||
for (int i = 0; i < tmopi.Size(); i++)
|
||||
{
|
||||
energy += tmopi[i]->GetDerefinementElementEnergy(el, T, elfun);
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
|
||||
void TMOPComboIntegrator::EnableNormalization(const GridFunction &x)
|
||||
{
|
||||
const int cnt = tmopi.Size();
|
||||
|
||||
+118
-16
@@ -1057,14 +1057,31 @@ protected:
|
||||
// eta1(x+h,y), eta2(x+h,y) ... etan(x+h,y), eta1(x,y+h), eta2(x,y+h) ...
|
||||
// same for tspec_pert2h and tspec_pertmix.
|
||||
|
||||
// DenseMatrix to hold target_spec values for the (children of the)
|
||||
// element being refined to consider for h-refinement.
|
||||
DenseMatrix tspec_refine;
|
||||
// Vector to hold the target_spec values for the coarse version of the
|
||||
// current mesh. Used for derefinement decision with hr-adaptivity.
|
||||
Vector tspec_derefine;
|
||||
|
||||
// Components of Target Jacobian at each quadrature point of an element. This
|
||||
// is required for computation of the derivative using chain rule.
|
||||
mutable DenseTensor Jtrcomp;
|
||||
|
||||
// Note: do not use the Nodes of this space as they may not be on the
|
||||
// positions corresponding to the values of tspec.
|
||||
const FiniteElementSpace *tspec_fes;
|
||||
const FiniteElementSpace *tspec_fesv;
|
||||
FiniteElementSpace *tspec_fesv; //owned
|
||||
FiniteElementSpace *coarse_tspec_fesv; //not owned, derefinement FESpace
|
||||
GridFunction *tspec_gf; //owned, uses tspec and tspec_fes
|
||||
// discrete adaptivity
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParFiniteElementSpace *ptspec_fesv; //owned, needed for derefinement to
|
||||
// get update operator.
|
||||
ParGridFunction *tspec_pgf; // similar to tspec_gf
|
||||
#endif
|
||||
|
||||
int amr_el;
|
||||
double lim_min_size;
|
||||
|
||||
// These flags can be used by outside functions to avoid recomputing the
|
||||
// tspec and tspec_perth fields again on the same mesh.
|
||||
@@ -1076,7 +1093,7 @@ protected:
|
||||
|
||||
void SetDiscreteTargetBase(const GridFunction &tspec_);
|
||||
void SetTspecAtIndex(int idx, const GridFunction &tspec_);
|
||||
void FinalizeSerialDiscreteTargetSpec();
|
||||
void FinalizeSerialDiscreteTargetSpec(const GridFunction &tspec_);
|
||||
#ifdef MFEM_USE_MPI
|
||||
void SetTspecAtIndex(int idx, const ParGridFunction &tspec_);
|
||||
void FinalizeParDiscreteTargetSpec(const ParGridFunction &tspec_);
|
||||
@@ -1088,16 +1105,16 @@ public:
|
||||
ncomp(0),
|
||||
sizeidx(-1), skewidx(-1), aspectratioidx(-1), orientationidx(-1),
|
||||
tspec(), tspec_sav(), tspec_pert1h(), tspec_pert2h(), tspec_pertmix(),
|
||||
tspec_fes(NULL), tspec_fesv(NULL),
|
||||
tspec_refine(), tspec_derefine(),
|
||||
tspec_fesv(NULL), coarse_tspec_fesv(NULL), tspec_gf(NULL),
|
||||
#ifdef MFEM_USE_MPI
|
||||
ptspec_fesv(NULL), tspec_pgf(NULL),
|
||||
#endif
|
||||
amr_el(-1), lim_min_size(-0.1),
|
||||
good_tspec(false), good_tspec_grad(false), good_tspec_hess(false),
|
||||
adapt_eval(NULL) { }
|
||||
|
||||
virtual ~DiscreteAdaptTC()
|
||||
{
|
||||
delete adapt_eval;
|
||||
delete tspec_fes;
|
||||
delete tspec_fesv;
|
||||
}
|
||||
virtual ~DiscreteAdaptTC();
|
||||
|
||||
/** @name Target specification methods.
|
||||
The following methods are used to specify geometric parameters of the
|
||||
@@ -1128,6 +1145,20 @@ public:
|
||||
void ResetUpdateFlags()
|
||||
{ good_tspec = good_tspec_grad = good_tspec_hess = false; }
|
||||
|
||||
/// Get one of the discrete fields from tspec.
|
||||
void GetDiscreteTargetSpec(GridFunction &tspec_, int idx);
|
||||
/// Get the FESpace associated with tspec.
|
||||
FiniteElementSpace *GetTSpecFESpace() { return tspec_fesv; }
|
||||
/// Get the entire tspec.
|
||||
GridFunction *GetTSpecData() { return tspec_gf; }
|
||||
/// Update all discrete fields based on tspec and update for AMR
|
||||
void UpdateAfterMeshTopologyChange();
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParFiniteElementSpace *GetTSpecParFESpace() { return ptspec_fesv; }
|
||||
void ParUpdateAfterMeshTopologyChange();
|
||||
#endif
|
||||
|
||||
/** Used to update the target specification after the mesh has changed. The
|
||||
new mesh positions are given by new_x. If @a use_flags is true, repeated
|
||||
calls won't do anything until ResetUpdateFlags() is called. */
|
||||
@@ -1184,6 +1215,36 @@ public:
|
||||
const Vector &elfun,
|
||||
IsoparametricTransformation &Tpr,
|
||||
DenseTensor &dJtr) const;
|
||||
|
||||
// Generates tspec_vals for target construction using intrule
|
||||
// Used for the refinement component in hr-adaptivity.
|
||||
void SetTspecFromIntRule(int e_id, const IntegrationRule &intrule);
|
||||
|
||||
// Targets based on discrete functions can result in invalid (negative)
|
||||
// size at the quadrature points. This method can be used to set a
|
||||
// minimum target size.
|
||||
void SetMinSizeForTargets(double min_size_) { lim_min_size = min_size_; }
|
||||
|
||||
/// Computes target specification data with respect to the coarse FE space.
|
||||
void SetTspecDataForDerefinement(FiniteElementSpace *fes);
|
||||
|
||||
// Reset refinement data associated with h-adaptivity component.
|
||||
void ResetRefinementTspecData()
|
||||
{
|
||||
tspec_refine.Clear();
|
||||
amr_el = -1;
|
||||
}
|
||||
|
||||
// Reset derefinement data associated with h-adaptivity component.
|
||||
void ResetDerefinementTspecData()
|
||||
{
|
||||
tspec_derefine.Destroy();
|
||||
coarse_tspec_fesv = NULL;
|
||||
}
|
||||
|
||||
// Used to specify the fine element for determining energy of children of a
|
||||
// parent element.
|
||||
void SetRefinementSubElement(int amr_el_) { amr_el = amr_el_; }
|
||||
};
|
||||
|
||||
class TMOPNewtonSolver;
|
||||
@@ -1201,6 +1262,7 @@ protected:
|
||||
friend class TMOPNewtonSolver;
|
||||
friend class TMOPComboIntegrator;
|
||||
|
||||
TMOP_QualityMetric *h_metric;
|
||||
TMOP_QualityMetric *metric; // not owned
|
||||
const TargetConstructor *targetC; // not owned
|
||||
|
||||
@@ -1227,6 +1289,9 @@ protected:
|
||||
|
||||
// Adaptive limiting.
|
||||
const GridFunction *zeta_0; // Not owned.
|
||||
#ifdef MFEM_USE_MPI
|
||||
const ParGridFunction *pzeta_0;
|
||||
#endif
|
||||
GridFunction *zeta; // Owned. Updated by adapt_eval.
|
||||
Coefficient *coeff_zeta; // Not owned.
|
||||
AdaptivityEvaluator *adapt_eval; // Not owned.
|
||||
@@ -1337,7 +1402,7 @@ protected:
|
||||
#endif
|
||||
void ComputeMinJac(const Vector &x, const FiniteElementSpace &fes);
|
||||
|
||||
void UpdateAfterMeshChange(const Vector &new_x);
|
||||
void UpdateAfterMeshPositionChange(const Vector &new_x);
|
||||
|
||||
void DisableLimiting()
|
||||
{
|
||||
@@ -1395,11 +1460,13 @@ protected:
|
||||
void ComputeAllElementTargets(const Vector &xe = Vector()) const;
|
||||
|
||||
public:
|
||||
/** @param[in] m TMOP_QualityMetric that will be integrated (not owned).
|
||||
@param[in] tc Target-matrix construction algorithm to use (not owned). */
|
||||
TMOP_Integrator(TMOP_QualityMetric *m, TargetConstructor *tc)
|
||||
: metric(m), targetC(tc), IntegRules(NULL), integ_order(-1),
|
||||
coeff1(NULL), metric_normal(1.0),
|
||||
/** @param[in] m TMOP_QualityMetric for r-adaptivity (not owned).
|
||||
@param[in] tc Target-matrix construction algorithm to use (not owned).
|
||||
@param[in] hm TMOP_QualityMetric for h-adaptivity (not owned). */
|
||||
TMOP_Integrator(TMOP_QualityMetric *m, TargetConstructor *tc,
|
||||
TMOP_QualityMetric *hm)
|
||||
: h_metric(hm), metric(m), targetC(tc), IntegRules(NULL),
|
||||
integ_order(-1), coeff1(NULL), metric_normal(1.0),
|
||||
nodes0(NULL), coeff0(NULL),
|
||||
lim_dist(NULL), lim_func(NULL), lim_normal(1.0),
|
||||
zeta_0(NULL), zeta(NULL), coeff_zeta(NULL), adapt_eval(NULL),
|
||||
@@ -1407,6 +1474,9 @@ public:
|
||||
fdflag(false), dxscale(1.0e3), fd_call_flag(false), exact_action(false)
|
||||
{ PA.enabled = false; }
|
||||
|
||||
TMOP_Integrator(TMOP_QualityMetric *m, TargetConstructor *tc)
|
||||
: TMOP_Integrator(m, tc, m) { }
|
||||
|
||||
~TMOP_Integrator();
|
||||
|
||||
/// Release the device memory of large PA allocations. This will copy device
|
||||
@@ -1478,6 +1548,22 @@ public:
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun);
|
||||
|
||||
/** @brief Computes the mean of the energies of the given element's children.
|
||||
|
||||
In addition to the inputs for GetElementEnergy, this function requires an
|
||||
IntegrationRule to be specified that will give the decomposition of the
|
||||
given element based on the refinement type being considered. */
|
||||
virtual double GetRefinementElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun,
|
||||
const IntegrationRule &irule);
|
||||
|
||||
/// This function is similar to GetElementEnergy, but ignores components
|
||||
/// such as limiting etc. to compute the element energy.
|
||||
virtual double GetDerefinementElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun);
|
||||
|
||||
virtual void AssembleElementVector(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun, Vector &elvect);
|
||||
@@ -1486,6 +1572,13 @@ public:
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun, DenseMatrix &elmat);
|
||||
|
||||
TMOP_QualityMetric &GetAMRQualityMetric() { return *h_metric; }
|
||||
|
||||
void UpdateAfterMeshTopologyChange();
|
||||
#ifdef MFEM_USE_MPI
|
||||
void ParUpdateAfterMeshTopologyChange();
|
||||
#endif
|
||||
|
||||
// PA extension
|
||||
using NonlinearFormIntegrator::AssemblePA;
|
||||
virtual void AssemblePA(const FiniteElementSpace&);
|
||||
@@ -1564,6 +1657,15 @@ public:
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun, DenseMatrix &elmat);
|
||||
|
||||
virtual double GetRefinementElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun,
|
||||
const IntegrationRule &irule);
|
||||
|
||||
virtual double GetDerefinementElementEnergy(const FiniteElement &el,
|
||||
ElementTransformation &T,
|
||||
const Vector &elfun);
|
||||
|
||||
/// Normalization factor that considers all integrators in the combination.
|
||||
void EnableNormalization(const GridFunction &x);
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
Executable
+896
@@ -0,0 +1,896 @@
|
||||
// Copyright (c) 2010-2021, 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 "tmop_amr.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using namespace mfem;
|
||||
|
||||
void TMOPRefinerEstimator::ComputeEstimates()
|
||||
{
|
||||
bool iso = false;
|
||||
bool aniso = false;
|
||||
if (amrmetric == 1 || amrmetric == 2 || amrmetric == 58)
|
||||
{
|
||||
aniso = true;
|
||||
}
|
||||
if (amrmetric == 55 || amrmetric == 56 || amrmetric == 77 ||
|
||||
amrmetric == 315 || amrmetric == 316 || amrmetric == 321)
|
||||
{
|
||||
iso = true;
|
||||
}
|
||||
if (amrmetric == 7 || amrmetric == 9)
|
||||
{
|
||||
iso = true; aniso = true;
|
||||
}
|
||||
|
||||
MFEM_VERIFY(iso || aniso, "Metric type not supported in hr-adaptivity.");
|
||||
|
||||
const int dim = mesh->Dimension();
|
||||
const int num_ref_types = 3 + 4*(dim-2);
|
||||
const int NEorig = mesh->GetNE();
|
||||
|
||||
aniso_flags.SetSize(NEorig);
|
||||
error_estimates.SetSize(NEorig);
|
||||
Vector amr_base_energy(NEorig), amr_temp_energy(NEorig);
|
||||
error_estimates = 1.*std::numeric_limits<float>::max();
|
||||
aniso_flags = -1;
|
||||
GetTMOPRefinementEnergy(0, amr_base_energy);
|
||||
|
||||
for (int i = 1; i < num_ref_types+1; i++)
|
||||
{
|
||||
if ( dim == 2 && i < 3 && aniso != true ) { continue; }
|
||||
if ( dim == 2 && i == 3 && iso != true ) { continue; }
|
||||
if ( dim == 3 && i < 7 && aniso != true ) { continue; }
|
||||
if ( dim == 3 && i == 7 && iso != true ) { continue; }
|
||||
|
||||
GetTMOPRefinementEnergy(i, amr_temp_energy);
|
||||
|
||||
for (int e = 0; e < NEorig; e++)
|
||||
{
|
||||
if ( amr_temp_energy(e) < error_estimates(e) )
|
||||
{
|
||||
error_estimates(e) = amr_temp_energy(e);
|
||||
aniso_flags[e] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
error_estimates *= energy_scaling_factor;
|
||||
|
||||
if (spat_gf)
|
||||
{
|
||||
L2_FECollection avg_fec(0, mesh->Dimension());
|
||||
FiniteElementSpace avg_fes(spat_gf->FESpace()->GetMesh(), &avg_fec);
|
||||
GridFunction elem_avg(&avg_fes);
|
||||
spat_gf->GetElementAverages(elem_avg);
|
||||
for (int i = 0; i < amr_base_energy.Size(); i++)
|
||||
{
|
||||
if (elem_avg(i) < spat_gf_critical) { amr_base_energy(i) = 0.; }
|
||||
}
|
||||
}
|
||||
|
||||
error_estimates -= amr_base_energy;
|
||||
error_estimates *= -1; // error = E(parent) - scaling_factor*mean(E(children))
|
||||
current_sequence = mesh->GetSequence();
|
||||
}
|
||||
|
||||
void TMOPRefinerEstimator::GetTMOPRefinementEnergy(int reftype,
|
||||
Vector &el_energy_vec)
|
||||
{
|
||||
const FiniteElementSpace *fes = mesh->GetNodalFESpace();
|
||||
const int NE = fes->GetNE();
|
||||
GridFunction *xdof = mesh->GetNodes();
|
||||
xdof->SetTrueVector();
|
||||
xdof->SetFromTrueVector();
|
||||
|
||||
el_energy_vec.SetSize(NE);
|
||||
el_energy_vec = std::numeric_limits<float>::max();
|
||||
|
||||
for (int e = 0; e < NE; e++)
|
||||
{
|
||||
Geometry::Type gtype = fes->GetFE(e)->GetGeomType();
|
||||
DenseMatrix tr, xsplit;
|
||||
IntegrationRule *irule = NULL;
|
||||
|
||||
if ( (gtype == Geometry::TRIANGLE && reftype > 0 && reftype < 3) ||
|
||||
(gtype == Geometry::CUBE && reftype > 0 && reftype < 7) ||
|
||||
(gtype == Geometry::TETRAHEDRON && reftype > 0 && reftype < 7) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (gtype)
|
||||
{
|
||||
case Geometry::TRIANGLE:
|
||||
{
|
||||
int ref_access = reftype == 0 ? 0 : 1;
|
||||
xdof->GetVectorValues(e, *TriIntRule[ref_access], xsplit, tr);
|
||||
irule = TriIntRule[ref_access];
|
||||
break;
|
||||
}
|
||||
case Geometry::TETRAHEDRON:
|
||||
{
|
||||
int ref_access = reftype == 0 ? 0 : 1;
|
||||
xdof->GetVectorValues(e, *TetIntRule[ref_access], xsplit, tr);
|
||||
irule = TetIntRule[ref_access];
|
||||
break;
|
||||
}
|
||||
case Geometry::SQUARE:
|
||||
{
|
||||
MFEM_VERIFY(QuadIntRule[reftype], " Integration rule does not exist.");
|
||||
xdof->GetVectorValues(e, *QuadIntRule[reftype], xsplit, tr);
|
||||
irule = QuadIntRule[reftype];
|
||||
break;
|
||||
}
|
||||
case Geometry::CUBE:
|
||||
{
|
||||
int ref_access = reftype == 0 ? 0 : 1;
|
||||
xdof->GetVectorValues(e, *HexIntRule[ref_access], xsplit, tr);
|
||||
irule = HexIntRule[ref_access];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
MFEM_ABORT("Incompatible geometry type!");
|
||||
}
|
||||
xsplit.Transpose();
|
||||
|
||||
el_energy_vec(e) = 0.; // Re-set to 0
|
||||
|
||||
// The data format is xe1,xe2,..xen,ye1,ye2..yen.
|
||||
// We will reformat it inside GetRefinementElementEnergy
|
||||
Vector elfun(xsplit.GetData(), xsplit.NumCols()*xsplit.NumRows());
|
||||
|
||||
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
|
||||
TMOP_Integrator *ti = NULL;
|
||||
TMOPComboIntegrator *co = NULL;
|
||||
for (int i = 0; i < integs.Size(); i++)
|
||||
{
|
||||
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
|
||||
if (ti)
|
||||
{
|
||||
el_energy_vec(e) = ti->GetRefinementElementEnergy(*fes->GetFE(e),
|
||||
*mesh->GetElementTransformation(e),
|
||||
elfun,
|
||||
*irule);
|
||||
}
|
||||
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
|
||||
if (co)
|
||||
{
|
||||
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
|
||||
for (int j = 0; j < ati.Size(); j++)
|
||||
{
|
||||
el_energy_vec(e) += ati[j]->GetRefinementElementEnergy(*fes->GetFE(e),
|
||||
*mesh->GetElementTransformation(e),
|
||||
elfun,
|
||||
*irule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TMOPRefinerEstimator::SetHexIntRules()
|
||||
{
|
||||
HexIntRule.SetSize(1+1);
|
||||
// Reftype = 0 -> original element
|
||||
Mesh meshsplit = Mesh::MakeCartesian3D(1, 1, 1, Element::HEXAHEDRON);
|
||||
Mesh base_mesh_copy(meshsplit);
|
||||
HexIntRule[0] = SetIntRulesFromMesh(meshsplit);
|
||||
meshsplit.Clear();
|
||||
|
||||
// Reftype = 7
|
||||
for (int i = 7; i < 8; i++)
|
||||
{
|
||||
Array<Refinement> marked_elements;
|
||||
Mesh mesh_ref(base_mesh_copy);
|
||||
for (int e = 0; e < mesh_ref.GetNE(); e++)
|
||||
{
|
||||
marked_elements.Append(Refinement(e, i));
|
||||
}
|
||||
mesh_ref.GeneralRefinement(marked_elements, 1, 0);
|
||||
HexIntRule[1] = SetIntRulesFromMesh(mesh_ref);
|
||||
mesh_ref.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TMOPRefinerEstimator::SetQuadIntRules()
|
||||
{
|
||||
QuadIntRule.SetSize(3+1);
|
||||
|
||||
// Reftype = 0 -> original element
|
||||
Mesh meshsplit = Mesh::MakeCartesian2D(1, 1, Element::QUADRILATERAL);
|
||||
Mesh base_mesh_copy(meshsplit);
|
||||
QuadIntRule[0] = SetIntRulesFromMesh(meshsplit);
|
||||
meshsplit.Clear();
|
||||
|
||||
// Reftype = 1-3
|
||||
for (int i = 1; i < 4; i++)
|
||||
{
|
||||
Array<Refinement> marked_elements;
|
||||
Mesh mesh_ref(base_mesh_copy);
|
||||
for (int e = 0; e < mesh_ref.GetNE(); e++)
|
||||
{
|
||||
marked_elements.Append(Refinement(e, i));
|
||||
}
|
||||
mesh_ref.GeneralRefinement(marked_elements, 1, 0);
|
||||
QuadIntRule[i] = SetIntRulesFromMesh(mesh_ref);
|
||||
mesh_ref.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TMOPRefinerEstimator::SetTriIntRules()
|
||||
{
|
||||
TriIntRule.SetSize(1+1);
|
||||
|
||||
// Reftype = 0 // original element
|
||||
const int Nvert = 3, NEsplit = 1;
|
||||
Mesh meshsplit(2, Nvert, NEsplit, 0 ,2);
|
||||
const double tri_v[3][2] =
|
||||
{
|
||||
{0, 0}, {1, 0}, {0, 1}
|
||||
};
|
||||
const int tri_e[1][3] =
|
||||
{
|
||||
{0, 1, 2}
|
||||
};
|
||||
|
||||
for (int j = 0; j < Nvert; j++)
|
||||
{
|
||||
meshsplit.AddVertex(tri_v[j]);
|
||||
}
|
||||
meshsplit.AddTriangle(tri_e[0], 1);
|
||||
meshsplit.FinalizeTriMesh(1, 1, true);
|
||||
|
||||
Mesh base_mesh_copy(meshsplit);
|
||||
TriIntRule[0] = SetIntRulesFromMesh(meshsplit);
|
||||
meshsplit.Clear();
|
||||
|
||||
// no anisotropic refinements for triangle
|
||||
// Reftype = 3
|
||||
for (int i = 1; i < 2; i++)
|
||||
{
|
||||
Array<Refinement> marked_elements;
|
||||
Mesh mesh_ref(base_mesh_copy);
|
||||
for (int e = 0; e < mesh_ref.GetNE(); e++)
|
||||
{
|
||||
marked_elements.Append(Refinement(e, i));
|
||||
}
|
||||
mesh_ref.GeneralRefinement(marked_elements, 1, 0);
|
||||
TriIntRule[i] = SetIntRulesFromMesh(mesh_ref);
|
||||
mesh_ref.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TMOPRefinerEstimator::SetTetIntRules()
|
||||
{
|
||||
TetIntRule.SetSize(1+1);
|
||||
|
||||
// Reftype = 0 // original element
|
||||
const int Nvert = 4, NEsplit = 1;
|
||||
Mesh meshsplit(3, Nvert, NEsplit, 0, 3);
|
||||
const double tet_v[4][3] =
|
||||
{
|
||||
{0, 0, 0}, {1, 0, 0}, {0, 1, 0}, {0, 0, 1}
|
||||
};
|
||||
const int tet_e[1][4] =
|
||||
{
|
||||
{0, 1, 2, 3}
|
||||
};
|
||||
|
||||
for (int j = 0; j < Nvert; j++)
|
||||
{
|
||||
meshsplit.AddVertex(tet_v[j]);
|
||||
}
|
||||
meshsplit.AddTet(tet_e[0], 1);
|
||||
meshsplit.FinalizeTetMesh(1, 1, true);
|
||||
|
||||
Mesh base_mesh_copy(meshsplit);
|
||||
TetIntRule[0] = SetIntRulesFromMesh(meshsplit);
|
||||
meshsplit.Clear();
|
||||
|
||||
// no anisotropic refinements for triangle
|
||||
// Reftype = 7
|
||||
for (int i = 1; i < 2; i++)
|
||||
{
|
||||
Array<Refinement> marked_elements;
|
||||
Mesh mesh_ref(base_mesh_copy);
|
||||
for (int e = 0; e < mesh_ref.GetNE(); e++)
|
||||
{
|
||||
marked_elements.Append(Refinement(e, i)); //ref_type will default to 7
|
||||
}
|
||||
mesh_ref.GeneralRefinement(marked_elements, 1, 0);
|
||||
TetIntRule[i] = SetIntRulesFromMesh(mesh_ref);
|
||||
mesh_ref.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
IntegrationRule* TMOPRefinerEstimator::SetIntRulesFromMesh(Mesh &meshsplit)
|
||||
{
|
||||
const int dim = meshsplit.Dimension();
|
||||
H1_FECollection fec(order, dim);
|
||||
FiniteElementSpace nodal_fes(&meshsplit, &fec, dim);
|
||||
meshsplit.SetNodalFESpace(&nodal_fes);
|
||||
|
||||
const int NEsplit = meshsplit.GetNE();
|
||||
const int dof_cnt = nodal_fes.GetFE(0)->GetDof(),
|
||||
pts_cnt = NEsplit * dof_cnt;
|
||||
|
||||
DenseMatrix pos(dof_cnt, dim);
|
||||
Vector posV(pos.Data(), dof_cnt * dim);
|
||||
Array<int> xdofs(dof_cnt * dim);
|
||||
|
||||
// Create an IntegrationRule on the nodes of the reference submesh.
|
||||
IntegrationRule *irule = new IntegrationRule(pts_cnt);
|
||||
GridFunction *nodesplit = meshsplit.GetNodes();
|
||||
|
||||
int pt_id = 0;
|
||||
for (int i = 0; i < NEsplit; i++)
|
||||
{
|
||||
nodal_fes.GetElementVDofs(i, xdofs);
|
||||
nodesplit->GetSubVector(xdofs, posV);
|
||||
for (int j = 0; j < dof_cnt; j++)
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
irule->IntPoint(pt_id).Set2(pos(j, 0), pos(j, 1));
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
irule->IntPoint(pt_id).Set3(pos(j, 0), pos(j, 1), pos(j, 2));
|
||||
}
|
||||
pt_id++;
|
||||
}
|
||||
}
|
||||
return irule;
|
||||
}
|
||||
|
||||
bool TMOPDeRefinerEstimator::GetDerefineEnergyForIntegrator(
|
||||
TMOP_Integrator &tmopi,
|
||||
Vector &fine_energy)
|
||||
{
|
||||
DiscreteAdaptTC *tcd = tmopi.GetDiscreteAdaptTC();
|
||||
fine_energy.SetSize(mesh->GetNE());
|
||||
|
||||
if (serial)
|
||||
{
|
||||
Mesh meshcopy(*mesh);
|
||||
FiniteElementSpace *tcdfes = NULL;
|
||||
if (tcd)
|
||||
{
|
||||
tcdfes = new FiniteElementSpace(*tcd->GetTSpecFESpace(), &meshcopy);
|
||||
}
|
||||
|
||||
Vector local_err(meshcopy.GetNE());
|
||||
local_err = 0.;
|
||||
double threshold = std::numeric_limits<float>::max();
|
||||
meshcopy.DerefineByError(local_err, threshold, 0, 1);
|
||||
|
||||
if (meshcopy.GetGlobalNE() == mesh->GetGlobalNE())
|
||||
{
|
||||
delete tcdfes;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tcd)
|
||||
{
|
||||
tcdfes->Update();
|
||||
tcd->SetTspecDataForDerefinement(tcdfes);
|
||||
}
|
||||
|
||||
Vector coarse_energy(meshcopy.GetNE());
|
||||
GetTMOPDerefinementEnergy(meshcopy, tmopi, coarse_energy);
|
||||
if (tcd) { tcd->ResetDerefinementTspecData(); }
|
||||
GetTMOPDerefinementEnergy(*mesh, tmopi, fine_energy);
|
||||
|
||||
const CoarseFineTransformations &dtrans =
|
||||
meshcopy.ncmesh->GetDerefinementTransforms();
|
||||
Table coarse_to_fine;
|
||||
dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine);
|
||||
|
||||
for (int pe = 0; pe < coarse_to_fine.Size(); pe++)
|
||||
{
|
||||
Array<int> tabrow;
|
||||
coarse_to_fine.GetRow(pe, tabrow);
|
||||
int nchild = tabrow.Size();
|
||||
double parent_energy = coarse_energy(pe);
|
||||
for (int fe = 0; fe < nchild; fe++)
|
||||
{
|
||||
int child = tabrow[fe];
|
||||
MFEM_VERIFY(child < mesh->GetNE(), " invalid coarse to fine mapping");
|
||||
fine_energy(child) -= parent_energy;
|
||||
}
|
||||
}
|
||||
delete tcdfes;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh meshcopy(*pmesh);
|
||||
ParFiniteElementSpace *tcdfes = NULL;
|
||||
if (tcd)
|
||||
{
|
||||
tcdfes = new ParFiniteElementSpace(*tcd->GetTSpecParFESpace(), meshcopy);
|
||||
}
|
||||
|
||||
Vector local_err(meshcopy.GetNE());
|
||||
local_err = 0.;
|
||||
double threshold = std::numeric_limits<float>::max();
|
||||
meshcopy.DerefineByError(local_err, threshold, 0, 1);
|
||||
|
||||
if (meshcopy.GetGlobalNE() == pmesh->GetGlobalNE())
|
||||
{
|
||||
delete tcdfes;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tcd)
|
||||
{
|
||||
tcdfes->Update();
|
||||
tcd->SetTspecDataForDerefinement(tcdfes);
|
||||
}
|
||||
|
||||
Vector coarse_energy(meshcopy.GetNE());
|
||||
GetTMOPDerefinementEnergy(meshcopy, tmopi, coarse_energy);
|
||||
if (tcd) { tcd->ResetDerefinementTspecData(); }
|
||||
GetTMOPDerefinementEnergy(*pmesh, tmopi, fine_energy);
|
||||
|
||||
const CoarseFineTransformations &dtrans =
|
||||
meshcopy.pncmesh->GetDerefinementTransforms();
|
||||
Table coarse_to_fine;
|
||||
dtrans.GetCoarseToFineMap(meshcopy, coarse_to_fine);
|
||||
|
||||
for (int pe = 0; pe < meshcopy.GetNE(); pe++)
|
||||
{
|
||||
Array<int> tabrow;
|
||||
coarse_to_fine.GetRow(pe, tabrow);
|
||||
int nchild = tabrow.Size();
|
||||
double parent_energy = coarse_energy(pe);
|
||||
for (int fe = 0; fe < nchild; fe++)
|
||||
{
|
||||
int child = tabrow[fe];
|
||||
MFEM_VERIFY(child < pmesh->GetNE(), " invalid coarse to fine mapping");
|
||||
fine_energy(child) -= parent_energy;
|
||||
}
|
||||
}
|
||||
delete tcdfes;
|
||||
#endif
|
||||
}
|
||||
|
||||
// error_estimate(e) = energy(parent_of_e)-energy(e)
|
||||
// Negative energy means derefinement is desirable.
|
||||
fine_energy *= -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
void TMOPDeRefinerEstimator::ComputeEstimates()
|
||||
{
|
||||
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
|
||||
TMOP_Integrator *ti = NULL;
|
||||
TMOPComboIntegrator *co = NULL;
|
||||
error_estimates.SetSize(mesh->GetNE());
|
||||
error_estimates = 0.;
|
||||
Vector fine_energy(mesh->GetNE());
|
||||
|
||||
for (int i = 0; i < integs.Size(); i++)
|
||||
{
|
||||
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
|
||||
if (ti)
|
||||
{
|
||||
bool deref = GetDerefineEnergyForIntegrator(*ti, fine_energy);
|
||||
if (!deref) { error_estimates = 1; return; }
|
||||
error_estimates += fine_energy;
|
||||
}
|
||||
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
|
||||
if (co)
|
||||
{
|
||||
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
|
||||
for (int j = 0; j < ati.Size(); j++)
|
||||
{
|
||||
bool deref = GetDerefineEnergyForIntegrator(*ati[j], fine_energy);
|
||||
if (!deref) { error_estimates = 1; return; }
|
||||
error_estimates += fine_energy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TMOPDeRefinerEstimator::GetTMOPDerefinementEnergy(Mesh &cmesh,
|
||||
TMOP_Integrator &tmopi,
|
||||
Vector &el_energy_vec)
|
||||
{
|
||||
const int cNE = cmesh.GetNE();
|
||||
el_energy_vec.SetSize(cNE);
|
||||
const FiniteElementSpace *fespace = cmesh.GetNodalFESpace();
|
||||
|
||||
GridFunction *cxdof = cmesh.GetNodes();
|
||||
|
||||
Array<int> vdofs;
|
||||
Vector el_x;
|
||||
const FiniteElement *fe;
|
||||
ElementTransformation *T;
|
||||
|
||||
for (int j = 0; j < cNE; j++)
|
||||
{
|
||||
fe = fespace->GetFE(j);
|
||||
fespace->GetElementVDofs(j, vdofs);
|
||||
T = cmesh.GetElementTransformation(j);
|
||||
cxdof->GetSubVector(vdofs, el_x);
|
||||
el_energy_vec(j) = tmopi.GetDerefinementElementEnergy(*fe, *T, el_x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TMOPHRSolver::TMOPHRSolver(Mesh &mesh_, NonlinearForm &nlf_,
|
||||
TMOPNewtonSolver &tmopns_, GridFunction &x_,
|
||||
bool move_bnd_, bool hradaptivity_,
|
||||
int mesh_poly_deg_, int amr_metric_id_,
|
||||
int hr_iter_, int h_per_r_iter_) :
|
||||
mesh(&mesh_), nlf(&nlf_), tmopns(&tmopns_), x(&x_),
|
||||
gridfuncarr(), fespacearr(),
|
||||
move_bnd(move_bnd_), hradaptivity(hradaptivity_),
|
||||
mesh_poly_deg(mesh_poly_deg_), amr_metric_id(amr_metric_id_),
|
||||
serial(true), hr_iter(hr_iter_), h_per_r_iter(h_per_r_iter_)
|
||||
{
|
||||
if (!hradaptivity) { return; }
|
||||
tmop_r_est = new TMOPRefinerEstimator(*mesh, *nlf, mesh_poly_deg,
|
||||
amr_metric_id);
|
||||
tmop_r = new ThresholdRefiner(*tmop_r_est);
|
||||
tmop_r->SetTotalErrorFraction(0.0);
|
||||
tmop_r_est->SetEnergyScalingFactor(1.);
|
||||
tmop_dr_est= new TMOPDeRefinerEstimator(*mesh, *nlf);
|
||||
tmop_dr = new ThresholdDerefiner(*tmop_dr_est);
|
||||
AddGridFunctionForUpdate(x);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
TMOPHRSolver::TMOPHRSolver(ParMesh &pmesh_, ParNonlinearForm &pnlf_,
|
||||
TMOPNewtonSolver &tmopns_, ParGridFunction &px_,
|
||||
bool move_bnd_, bool hradaptivity_,
|
||||
int mesh_poly_deg_, int amr_metric_id_,
|
||||
int hr_iter_, int h_per_r_iter_) :
|
||||
mesh(&pmesh_), nlf(&pnlf_), tmopns(&tmopns_), x(&px_),
|
||||
gridfuncarr(), fespacearr(),
|
||||
move_bnd(move_bnd_), hradaptivity(hradaptivity_),
|
||||
mesh_poly_deg(mesh_poly_deg_), amr_metric_id(amr_metric_id_),
|
||||
pmesh(&pmesh_), pnlf(&pnlf_), pgridfuncarr(), pfespacearr(),
|
||||
serial(false), hr_iter(hr_iter_), h_per_r_iter(h_per_r_iter_)
|
||||
{
|
||||
if (!hradaptivity) { return; }
|
||||
tmop_r_est = new TMOPRefinerEstimator(*pmesh, *pnlf, mesh_poly_deg,
|
||||
amr_metric_id);
|
||||
tmop_r = new ThresholdRefiner(*tmop_r_est);
|
||||
tmop_r->SetTotalErrorFraction(0.0);
|
||||
tmop_r_est->SetEnergyScalingFactor(1.);
|
||||
tmop_dr_est= new TMOPDeRefinerEstimator(*pmesh, *pnlf);
|
||||
tmop_dr = new ThresholdDerefiner(*tmop_dr_est);
|
||||
AddGridFunctionForUpdate(&px_);
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOPHRSolver::Mult()
|
||||
{
|
||||
Vector b(0);
|
||||
int myid = 0;
|
||||
if (serial)
|
||||
{
|
||||
tmopns->SetOperator(*nlf);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
myid = pnlf->ParFESpace()->GetMyRank();
|
||||
tmopns->SetOperator(*pnlf);
|
||||
#endif
|
||||
}
|
||||
if (!hradaptivity)
|
||||
{
|
||||
tmopns->Mult(b, x->GetTrueVector());
|
||||
if (tmopns->GetConverged() == false)
|
||||
{
|
||||
if (myid == 0) { mfem::out << "Nonlinear solver: rtol not achieved.\n"; }
|
||||
}
|
||||
x->SetFromTrueVector();
|
||||
return;
|
||||
}
|
||||
|
||||
bool radaptivity = true;
|
||||
|
||||
tmop_dr->Reset();
|
||||
tmop_r->Reset();
|
||||
|
||||
if (serial)
|
||||
{
|
||||
for (int i_hr = 0; i_hr < hr_iter; i_hr++)
|
||||
{
|
||||
if (!radaptivity)
|
||||
{
|
||||
break;
|
||||
}
|
||||
mfem::out << i_hr << " r-adaptivity iteration.\n";
|
||||
|
||||
tmopns->SetOperator(*nlf);
|
||||
tmopns->Mult(b, x->GetTrueVector());
|
||||
x->SetFromTrueVector();
|
||||
|
||||
mfem::out << "TMOP energy after r-adaptivity: " <<
|
||||
nlf->GetGridFunctionEnergy(*x)/mesh->GetNE() <<
|
||||
", Elements: " << mesh->GetNE() << std::endl;
|
||||
|
||||
for (int i_h = 0; i_h < h_per_r_iter; i_h++)
|
||||
{
|
||||
// Derefinement step.
|
||||
if (mesh->ncmesh)
|
||||
{
|
||||
tmop_dr->Apply(*mesh);
|
||||
Update();
|
||||
}
|
||||
mfem::out << "TMOP energy after derefinement: " <<
|
||||
nlf->GetGridFunctionEnergy(*x)/mesh->GetNE() <<
|
||||
", Elements: " << mesh->GetNE() << std::endl;
|
||||
|
||||
// Refinement step.
|
||||
tmop_r->Apply(*mesh);
|
||||
Update();
|
||||
mfem::out << "TMOP energy after refinement: " <<
|
||||
nlf->GetGridFunctionEnergy(*x)/mesh->GetNE() <<
|
||||
", Elements: " << mesh->GetNE() << std::endl;
|
||||
|
||||
if (!tmop_dr->Derefined() && tmop_r->Stop())
|
||||
{
|
||||
radaptivity = false;
|
||||
mfem::out << "AMR stopping criterion satisfied. Stop.\n";
|
||||
break;
|
||||
}
|
||||
} //n_h
|
||||
} //n_hr
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
int NEGlob;
|
||||
double tmopenergy;
|
||||
for (int i_hr = 0; i_hr < hr_iter; i_hr++)
|
||||
{
|
||||
if (!radaptivity)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (myid == 0) { mfem::out << i_hr << " r-adaptivity iteration.\n"; }
|
||||
tmopns->SetOperator(*pnlf);
|
||||
tmopns->Mult(b, x->GetTrueVector());
|
||||
x->SetFromTrueVector();
|
||||
|
||||
NEGlob = pmesh->GetGlobalNE();
|
||||
tmopenergy = pnlf->GetParGridFunctionEnergy(*x) / NEGlob;
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "TMOP energy after r-adaptivity: " << tmopenergy <<
|
||||
", Elements: " << NEGlob << std::endl;
|
||||
}
|
||||
|
||||
for (int i_h = 0; i_h < h_per_r_iter; i_h++)
|
||||
{
|
||||
// Derefinement step.
|
||||
if (pmesh->pncmesh)
|
||||
{
|
||||
RebalanceParNCMesh();
|
||||
ParUpdate();
|
||||
|
||||
tmop_dr->Apply(*pmesh);
|
||||
ParUpdate();
|
||||
}
|
||||
NEGlob = pmesh->GetGlobalNE();
|
||||
tmopenergy = pnlf->GetParGridFunctionEnergy(*x) / NEGlob;
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "TMOP energy after derefinement: " << tmopenergy <<
|
||||
", Elements: " << NEGlob << std::endl;
|
||||
}
|
||||
|
||||
// Refinement step.
|
||||
tmop_r->Apply(*pmesh);
|
||||
ParUpdate();
|
||||
NEGlob = pmesh->GetGlobalNE();
|
||||
tmopenergy = pnlf->GetParGridFunctionEnergy(*x) / NEGlob;
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "TMOP energy after refinement: " << tmopenergy <<
|
||||
", Elements: " << NEGlob << std::endl;
|
||||
}
|
||||
|
||||
if (!tmop_dr->Derefined() && tmop_r->Stop())
|
||||
{
|
||||
radaptivity = false;
|
||||
if (myid == 0)
|
||||
{
|
||||
mfem::out << "AMR stopping criterion satisfied. Stop.\n";
|
||||
}
|
||||
break;
|
||||
}
|
||||
} //n_r limit
|
||||
} //n_hr
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOPHRSolver::RebalanceParNCMesh()
|
||||
{
|
||||
ParNCMesh *pncmesh = pmesh->pncmesh;
|
||||
if (pncmesh)
|
||||
{
|
||||
const Table &dreftable = pncmesh->GetDerefinementTable();
|
||||
Array<int> drefs, new_ranks;
|
||||
for (int i = 0; i < dreftable.Size(); i++)
|
||||
{
|
||||
drefs.Append(i);
|
||||
}
|
||||
pncmesh->GetFineToCoarsePartitioning(drefs, new_ranks);
|
||||
pmesh->Rebalance(new_ranks);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOPHRSolver::Update()
|
||||
{
|
||||
// Update FESpace
|
||||
for (int i = 0; i < fespacearr.Size(); i++)
|
||||
{
|
||||
fespacearr[i]->Update();
|
||||
}
|
||||
// Update nodal GF
|
||||
for (int i = 0; i < gridfuncarr.Size(); i++)
|
||||
{
|
||||
gridfuncarr[i]->Update();
|
||||
gridfuncarr[i]->SetTrueVector();
|
||||
gridfuncarr[i]->SetFromTrueVector();
|
||||
}
|
||||
|
||||
// Update Discrete Indicator for all the TMOP_Integrators in NonLinearForm
|
||||
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
|
||||
TMOP_Integrator *ti = NULL;
|
||||
TMOPComboIntegrator *co = NULL;
|
||||
DiscreteAdaptTC *dtc = NULL;
|
||||
for (int i = 0; i < integs.Size(); i++)
|
||||
{
|
||||
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
|
||||
if (ti)
|
||||
{
|
||||
ti->UpdateAfterMeshTopologyChange();
|
||||
dtc = ti->GetDiscreteAdaptTC();
|
||||
if (dtc) { dtc->UpdateAfterMeshTopologyChange(); }
|
||||
}
|
||||
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
|
||||
if (co)
|
||||
{
|
||||
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
|
||||
for (int j = 0; j < ati.Size(); j++)
|
||||
{
|
||||
ati[j]->UpdateAfterMeshTopologyChange();
|
||||
dtc = ati[j]->GetDiscreteAdaptTC();
|
||||
if (dtc) { dtc->UpdateAfterMeshTopologyChange(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the Nonlinear form and set Essential BC.
|
||||
UpdateNonlinearFormAndBC(mesh, nlf);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOPHRSolver::ParUpdate()
|
||||
{
|
||||
// Update FESpace
|
||||
for (int i = 0; i < pfespacearr.Size(); i++)
|
||||
{
|
||||
pfespacearr[i]->Update();
|
||||
}
|
||||
// Update nodal GF
|
||||
for (int i = 0; i < pgridfuncarr.Size(); i++)
|
||||
{
|
||||
pgridfuncarr[i]->Update();
|
||||
pgridfuncarr[i]->SetTrueVector();
|
||||
pgridfuncarr[i]->SetFromTrueVector();
|
||||
}
|
||||
|
||||
// Update Discrete Indicator
|
||||
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
|
||||
TMOP_Integrator *ti = NULL;
|
||||
TMOPComboIntegrator *co = NULL;
|
||||
DiscreteAdaptTC *dtc = NULL;
|
||||
for (int i = 0; i < integs.Size(); i++)
|
||||
{
|
||||
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
|
||||
if (ti)
|
||||
{
|
||||
ti->ParUpdateAfterMeshTopologyChange();
|
||||
dtc = ti->GetDiscreteAdaptTC();
|
||||
if (dtc) { dtc->ParUpdateAfterMeshTopologyChange(); }
|
||||
}
|
||||
co = dynamic_cast<TMOPComboIntegrator *>(integs[i]);
|
||||
if (co)
|
||||
{
|
||||
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
|
||||
for (int j = 0; j < ati.Size(); j++)
|
||||
{
|
||||
ati[j]->ParUpdateAfterMeshTopologyChange();
|
||||
dtc = ati[j]->GetDiscreteAdaptTC();
|
||||
if (dtc) { dtc->ParUpdateAfterMeshTopologyChange(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the Nonlinear form and set Essential BC.
|
||||
UpdateNonlinearFormAndBC(pmesh, pnlf);
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOPHRSolver::UpdateNonlinearFormAndBC(Mesh *mesh, NonlinearForm *nlf)
|
||||
{
|
||||
const FiniteElementSpace &fes = *mesh->GetNodalFESpace();
|
||||
|
||||
// Update Nonlinear form and Set Essential BC
|
||||
nlf->Update();
|
||||
const int dim = fes.GetFE(0)->GetDim();
|
||||
if (move_bnd == false)
|
||||
{
|
||||
Array<int> ess_bdr(mesh->bdr_attributes.Max());
|
||||
ess_bdr = 1;
|
||||
nlf->SetEssentialBC(ess_bdr);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int nd = fes.GetBE(0)->GetDof();
|
||||
int n = 0;
|
||||
for (int i = 0; i < mesh->GetNBE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetBdrElement(i)->GetAttribute();
|
||||
MFEM_VERIFY(!(dim == 2 && attr == 3),
|
||||
"Boundary attribute 3 must be used only for 3D meshes. "
|
||||
"Adjust the attributes (1/2/3/4 for fixed x/y/z/all "
|
||||
"components, rest for free nodes), or use -fix-bnd.");
|
||||
if (attr == 1 || attr == 2 || attr == 3) { n += nd; }
|
||||
if (attr == 4) { n += nd * dim; }
|
||||
}
|
||||
Array<int> ess_vdofs(n), vdofs;
|
||||
n = 0;
|
||||
for (int i = 0; i < mesh->GetNBE(); i++)
|
||||
{
|
||||
const int attr = mesh->GetBdrElement(i)->GetAttribute();
|
||||
fes.GetBdrElementVDofs(i, vdofs);
|
||||
if (attr == 1) // Fix x components.
|
||||
{
|
||||
for (int j = 0; j < nd; j++)
|
||||
{ ess_vdofs[n++] = vdofs[j]; }
|
||||
}
|
||||
else if (attr == 2) // Fix y components.
|
||||
{
|
||||
for (int j = 0; j < nd; j++)
|
||||
{ ess_vdofs[n++] = vdofs[j+nd]; }
|
||||
}
|
||||
else if (attr == 3) // Fix z components.
|
||||
{
|
||||
for (int j = 0; j < nd; j++)
|
||||
{ ess_vdofs[n++] = vdofs[j+2*nd]; }
|
||||
}
|
||||
else if (attr == 4) // Fix all components.
|
||||
{
|
||||
for (int j = 0; j < vdofs.Size(); j++)
|
||||
{ ess_vdofs[n++] = vdofs[j]; }
|
||||
}
|
||||
}
|
||||
nlf->SetEssentialVDofs(ess_vdofs);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
// Copyright (c) 2010-2021, 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_TMOP_AMR_HPP
|
||||
#define MFEM_TMOP_AMR_HPP
|
||||
|
||||
#include "tmop_tools.hpp"
|
||||
#include "nonlinearform.hpp"
|
||||
#include "pnonlinearform.hpp"
|
||||
#include "estimators.hpp"
|
||||
#include "../mesh/mesh_operators.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class TMOPRefinerEstimator : public AnisotropicErrorEstimator
|
||||
{
|
||||
protected:
|
||||
Mesh *mesh; // not owned
|
||||
NonlinearForm *nlf; // not owned
|
||||
int order;
|
||||
int amrmetric;
|
||||
Array<IntegrationRule *> TriIntRule, QuadIntRule, TetIntRule, HexIntRule;
|
||||
long current_sequence;
|
||||
Vector error_estimates;
|
||||
Array<int> aniso_flags;
|
||||
// An element is refined only if
|
||||
// [mean TMOPEnergy(children)]*energy_scaling_factor < TMOPEnergy(parent)
|
||||
double energy_scaling_factor;
|
||||
GridFunction *spat_gf; // If specified, can be used to specify the
|
||||
double spat_gf_critical; // region where hr-adaptivity is done.
|
||||
|
||||
/// Check if the mesh of the solution was modified.
|
||||
bool MeshIsModified()
|
||||
{
|
||||
long mesh_sequence = mesh->GetSequence();
|
||||
MFEM_ASSERT(mesh_sequence >= current_sequence, "");
|
||||
return (mesh_sequence > current_sequence);
|
||||
}
|
||||
|
||||
/// Compute the element error estimates. For an element E in the mesh,
|
||||
/// error(E) = TMOPEnergy(E)*energy_scaling_factor-Mean(TMOPEnergy(ChildofE)),
|
||||
/// where TMOPEnergy of Children of E is obtained by assuming the element E
|
||||
/// is refined using the refinement type being considered based on the TMOP
|
||||
/// mesh quality metric.
|
||||
void ComputeEstimates();
|
||||
|
||||
/// Construct the integration rules to model how each element type is split
|
||||
/// using different refinement types. ref_type = 0 is the original element
|
||||
/// and reftype \ in [1, 7] represent different refinement type based on
|
||||
/// NCMesh class.
|
||||
void SetQuadIntRules(); // supports ref_type = 1 to 3.
|
||||
void SetTriIntRules(); // currently supports only isotropic refinement.
|
||||
void SetHexIntRules(); // currently supports only isotropic refinement.
|
||||
void SetTetIntRules(); // currently supports only isotropic refinement.
|
||||
|
||||
/// Get TMOP energy for each element corresponding to the refinement type
|
||||
/// specified.
|
||||
void GetTMOPRefinementEnergy(int reftype, Vector &el_energy_vec);
|
||||
|
||||
/// Use a mesh to setup an integration rule that will mimic the different
|
||||
/// refinement types.
|
||||
IntegrationRule* SetIntRulesFromMesh(Mesh &meshsplit);
|
||||
public:
|
||||
TMOPRefinerEstimator(Mesh &mesh_, NonlinearForm &nlf_, int order_,
|
||||
int amrmetric_) :
|
||||
mesh(&mesh_), nlf(&nlf_), order(order_), amrmetric(amrmetric_),
|
||||
TriIntRule(0), QuadIntRule(0), TetIntRule(0), HexIntRule(0),
|
||||
current_sequence(-1), error_estimates(), aniso_flags(),
|
||||
energy_scaling_factor(1.), spat_gf(NULL), spat_gf_critical(0.)
|
||||
{
|
||||
if (mesh->Dimension() == 2)
|
||||
{
|
||||
SetQuadIntRules();
|
||||
SetTriIntRules();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetHexIntRules();
|
||||
SetTetIntRules();
|
||||
}
|
||||
}
|
||||
|
||||
~TMOPRefinerEstimator()
|
||||
{
|
||||
for (int i = 0; i < QuadIntRule.Size(); i++) { delete QuadIntRule[i]; }
|
||||
for (int i = 0; i < TriIntRule.Size(); i++) { delete TriIntRule[i]; }
|
||||
for (int i = 0; i < HexIntRule.Size(); i++) { delete HexIntRule[i]; }
|
||||
for (int i = 0; i < TetIntRule.Size(); i++) { delete TetIntRule[i]; }
|
||||
}
|
||||
|
||||
/// Get TMOP-based errors for each element in the mesh computed based on the
|
||||
/// refinement types being considered.
|
||||
virtual const Vector &GetLocalErrors()
|
||||
{
|
||||
if (MeshIsModified()) { ComputeEstimates(); }
|
||||
return error_estimates;
|
||||
}
|
||||
/// For anisotropic refinements, get the refinement type (e.g., x or y)
|
||||
virtual const Array<int> &GetAnisotropicFlags()
|
||||
{
|
||||
if (MeshIsModified()) { ComputeEstimates(); }
|
||||
return aniso_flags;
|
||||
}
|
||||
|
||||
/// Scaling factor for the TMOP refinement energy. An element is refined if
|
||||
/// [mean TMOPEnergy(children)]*energy_scaling_factor < TMOPEnergy(parent)
|
||||
void SetEnergyScalingFactor(double scale) { energy_scaling_factor = scale; }
|
||||
|
||||
/// Spatial indicator function (eta) that can be used to prevent elements
|
||||
/// from being refined even if the energy criterion is met. Using this,
|
||||
/// an element E is not refined if mean(@a spat_gf(E)) < @a spat_gf_critical.
|
||||
void SetSpatialIndicator(GridFunction &spat_gf_,
|
||||
double spat_gf_critical_ = 0.5)
|
||||
{ spat_gf = &spat_gf_; spat_gf_critical = spat_gf_critical_; }
|
||||
void SetSpatialIndicatorCritical(double val_) { spat_gf_critical = val_; }
|
||||
|
||||
/// Reset the error estimator.
|
||||
virtual void Reset() { current_sequence = -1; }
|
||||
};
|
||||
|
||||
class TMOPDeRefinerEstimator : public ErrorEstimator
|
||||
{
|
||||
protected:
|
||||
Mesh *mesh;
|
||||
NonlinearForm *nlf;
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh *pmesh;
|
||||
ParNonlinearForm *pnlf;
|
||||
#endif
|
||||
int order;
|
||||
int amrmetric;
|
||||
long current_sequence;
|
||||
Vector error_estimates;
|
||||
bool serial;
|
||||
|
||||
/// Check if the mesh of the solution was modified.
|
||||
bool MeshIsModified()
|
||||
{
|
||||
long mesh_sequence = mesh->GetSequence();
|
||||
MFEM_ASSERT(mesh_sequence >= current_sequence, "");
|
||||
return (mesh_sequence > current_sequence);
|
||||
}
|
||||
|
||||
/// Compute the element error estimates. For a given element E in the mesh,
|
||||
/// error(E) = TMOPEnergy(parent_of_E)-TMOPEnergy(E). Children element of an
|
||||
/// element are derefined if the mean TMOP energy of children is greated than
|
||||
/// the TMOP energy associated with their parent.
|
||||
void ComputeEstimates();
|
||||
|
||||
void GetTMOPDerefinementEnergy(Mesh &cmesh,
|
||||
TMOP_Integrator &tmopi,
|
||||
Vector &el_energy_vec);
|
||||
|
||||
bool GetDerefineEnergyForIntegrator(TMOP_Integrator &tmopi,
|
||||
Vector &fine_energy);
|
||||
public:
|
||||
TMOPDeRefinerEstimator(Mesh &mesh_, NonlinearForm &nlf_) :
|
||||
mesh(&mesh_), nlf(&nlf_),
|
||||
current_sequence(-1), error_estimates(), serial(true) { }
|
||||
#ifdef MFEM_USE_MPI
|
||||
TMOPDeRefinerEstimator(ParMesh &pmesh_, ParNonlinearForm &pnlf_) :
|
||||
mesh(&pmesh_), nlf(&pnlf_), pmesh(&pmesh_), pnlf(&pnlf_),
|
||||
current_sequence(-1), error_estimates(), serial(false) { }
|
||||
#endif
|
||||
|
||||
~TMOPDeRefinerEstimator() { }
|
||||
|
||||
virtual const Vector &GetLocalErrors()
|
||||
{
|
||||
if (MeshIsModified()) { ComputeEstimates(); }
|
||||
return error_estimates;
|
||||
}
|
||||
|
||||
/// Reset the error estimator.
|
||||
virtual void Reset() { current_sequence = -1; }
|
||||
};
|
||||
|
||||
// hr-adaptivity using TMOP.
|
||||
// If hr-adaptivity is disabled, r-adaptivity is done once using the
|
||||
// TMOPNewtonSolver.
|
||||
// Otherwise, "hr_iter" iterations of r-adaptivity are done followed by
|
||||
// "h_per_r_iter" iterations of h-adaptivity after each r-adaptivity iteration.
|
||||
// The solver terminates early if an h-adaptivity iteration does not
|
||||
// refine/derefine any element in the mesh.
|
||||
class TMOPHRSolver
|
||||
{
|
||||
protected:
|
||||
Mesh *mesh;
|
||||
NonlinearForm *nlf;
|
||||
TMOPNewtonSolver *tmopns;
|
||||
GridFunction *x;
|
||||
Array<GridFunction *> gridfuncarr;
|
||||
Array<FiniteElementSpace *> fespacearr;
|
||||
bool move_bnd, hradaptivity;
|
||||
const int mesh_poly_deg, amr_metric_id;
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParMesh *pmesh;
|
||||
ParNonlinearForm *pnlf;
|
||||
Array<ParGridFunction *> pgridfuncarr;
|
||||
Array<ParFiniteElementSpace *> pfespacearr;
|
||||
#endif
|
||||
bool serial;
|
||||
|
||||
// All are owned.
|
||||
TMOPRefinerEstimator *tmop_r_est;
|
||||
ThresholdRefiner *tmop_r;
|
||||
TMOPDeRefinerEstimator *tmop_dr_est;
|
||||
ThresholdDerefiner *tmop_dr;
|
||||
|
||||
int hr_iter, h_per_r_iter;
|
||||
|
||||
void Update();
|
||||
#ifdef MFEM_USE_MPI
|
||||
void ParUpdate();
|
||||
#endif
|
||||
void UpdateNonlinearFormAndBC(Mesh *mesh, NonlinearForm *nlf);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
// Rebalance ParMesh such that all the children elements are moved to the same
|
||||
// MPI rank where the parent will be if the mesh were to be derefined.
|
||||
void RebalanceParNCMesh();
|
||||
#endif
|
||||
|
||||
public:
|
||||
TMOPHRSolver(Mesh &mesh_, NonlinearForm &nlf_,
|
||||
TMOPNewtonSolver &tmopns_, GridFunction &x_,
|
||||
bool move_bnd_, bool hradaptivity_,
|
||||
int mesh_poly_deg_, int amr_metric_id_,
|
||||
int hr_iter_ = 5, int h_per_r_iter_ = 1);
|
||||
#ifdef MFEM_USE_MPI
|
||||
TMOPHRSolver(ParMesh &pmesh_, ParNonlinearForm &pnlf_,
|
||||
TMOPNewtonSolver &tmopns_, ParGridFunction &x_,
|
||||
bool move_bnd_, bool hradaptivity_,
|
||||
int mesh_poly_deg_, int amr_metric_id_,
|
||||
int hr_iter_ = 5, int h_per_r_iter_ = 1);
|
||||
#endif
|
||||
|
||||
void Mult();
|
||||
|
||||
/// These are used to update spaces and functions that are not owned by the
|
||||
/// TMOPIntegrator or DiscreteAdaptTC. The owned ones are updated in the
|
||||
/// functions UpdateAfterMeshTopologyChange() of both classes.
|
||||
void AddGridFunctionForUpdate(GridFunction *gf) { gridfuncarr.Append(gf); }
|
||||
void AddFESpaceForUpdate(FiniteElementSpace *fes) { fespacearr.Append(fes); }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void AddGridFunctionForUpdate(ParGridFunction *pgf_)
|
||||
{
|
||||
pgridfuncarr.Append(pgf_);
|
||||
}
|
||||
void AddFESpaceForUpdate(ParFiniteElementSpace *pfes_)
|
||||
{
|
||||
pfespacearr.Append(pfes_);
|
||||
}
|
||||
#endif
|
||||
|
||||
~TMOPHRSolver()
|
||||
{
|
||||
if (!hradaptivity) { return; }
|
||||
delete tmop_dr;
|
||||
delete tmop_dr_est;
|
||||
delete tmop_r;
|
||||
delete tmop_r_est;
|
||||
}
|
||||
|
||||
/// Total number of hr-adaptivity iterations. At each iteration, we do an
|
||||
/// r-adaptivity iteration followed by a number of h-adaptivity iterations.
|
||||
void SetHRAdaptivityIterations(int iter) { hr_iter = iter; }
|
||||
|
||||
/// Total number of h-adaptivity iterations per r-adaptivity iteration.
|
||||
void SetHAdaptivityIterations(int iter) { h_per_r_iter = iter; }
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
+6
-4
@@ -407,6 +407,8 @@ double TMOPNewtonSolver::ComputeScalingFactor(const Vector &x,
|
||||
{
|
||||
// Needed for the line search below. The untangling metrics see this
|
||||
// reference to detect deteriorations.
|
||||
MFEM_VERIFY(min_det_ptr != NULL, " Initial mesh was valid, but"
|
||||
" intermediate mesh is invalid. Contact TMOP Developers.");
|
||||
*min_det_ptr = untangle_factor * min_detT_in;
|
||||
}
|
||||
|
||||
@@ -576,7 +578,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
|
||||
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
|
||||
if (ti)
|
||||
{
|
||||
ti->UpdateAfterMeshChange(x_loc);
|
||||
ti->UpdateAfterMeshPositionChange(x_loc);
|
||||
ti->ComputeFDh(x_loc, *pfesc);
|
||||
UpdateDiscreteTC(*ti, x_loc);
|
||||
}
|
||||
@@ -586,7 +588,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
|
||||
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
|
||||
for (int j = 0; j < ati.Size(); j++)
|
||||
{
|
||||
ati[j]->UpdateAfterMeshChange(x_loc);
|
||||
ati[j]->UpdateAfterMeshPositionChange(x_loc);
|
||||
ati[j]->ComputeFDh(x_loc, *pfesc);
|
||||
UpdateDiscreteTC(*ati[j], x_loc);
|
||||
}
|
||||
@@ -613,7 +615,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
|
||||
ti = dynamic_cast<TMOP_Integrator *>(integs[i]);
|
||||
if (ti)
|
||||
{
|
||||
ti->UpdateAfterMeshChange(x_loc);
|
||||
ti->UpdateAfterMeshPositionChange(x_loc);
|
||||
ti->ComputeFDh(x_loc, *fesc);
|
||||
UpdateDiscreteTC(*ti, x_loc);
|
||||
}
|
||||
@@ -623,7 +625,7 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &x) const
|
||||
Array<TMOP_Integrator *> ati = co->GetTMOPIntegrators();
|
||||
for (int j = 0; j < ati.Size(); j++)
|
||||
{
|
||||
ati[j]->UpdateAfterMeshChange(x_loc);
|
||||
ati[j]->UpdateAfterMeshPositionChange(x_loc);
|
||||
ati[j]->ComputeFDh(x_loc, *fesc);
|
||||
UpdateDiscreteTC(*ati[j], x_loc);
|
||||
}
|
||||
|
||||
+2
-1
@@ -175,7 +175,8 @@ const Operator &InterpolationGridTransfer::ForwardOperator()
|
||||
localP[elem_geoms[i]]);
|
||||
}
|
||||
F.Reset(ran_fes.RefinementMatrix_main(
|
||||
dom_fes.GetNDofs(), dom_fes.GetElementToDofTable(), localP));
|
||||
dom_fes.GetNDofs(), dom_fes.GetElementToDofTable(),
|
||||
dom_fes.GetElementToFaceOrientationTable(), localP));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -21,12 +21,16 @@
|
||||
#define MFEM_PERF_FUNCTION CALI_CXX_MARK_FUNCTION
|
||||
#define MFEM_PERF_BEGIN(s) CALI_MARK_BEGIN(s)
|
||||
#define MFEM_PERF_END(s) CALI_MARK_END(s)
|
||||
#define MFEM_PERF_SCOPE(name) \
|
||||
cali::Annotation::Guard cali_autogenerated_guard_name(cali::Annotation("function").begin(std::string(name).c_str()))
|
||||
|
||||
|
||||
#else
|
||||
|
||||
#define MFEM_PERF_FUNCTION
|
||||
#define MFEM_PERF_BEGIN(s)
|
||||
#define MFEM_PERF_END(s)
|
||||
#define MFEM_PERF_SCOPE(name)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define MFEM_FORALL_HPP
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#include "annotation.hpp"
|
||||
#include "error.hpp"
|
||||
#include "backends.hpp"
|
||||
#include "device.hpp"
|
||||
|
||||
+16
-2
@@ -494,8 +494,7 @@ public:
|
||||
/// Copy @a size entries from @a *this to @a dest.
|
||||
/** The given @a size should not exceed the Capacity() of @a *this and the
|
||||
destination, @a dest. */
|
||||
inline void CopyTo(Memory &dest, int size) const
|
||||
{ dest.CopyFrom(*this, size); }
|
||||
inline void CopyTo(Memory &dest, int size) const;
|
||||
|
||||
/// Copy @a size entries from @a *this to the host pointer @a dest.
|
||||
/** The given @a size should not exceed the Capacity() of @a *this. */
|
||||
@@ -923,6 +922,11 @@ inline void Memory<T>::Wrap(T *ptr, T *d_ptr, int size, MemoryType mt, bool own)
|
||||
template <typename T>
|
||||
inline void Memory<T>::MakeAlias(const Memory &base, int offset, int size)
|
||||
{
|
||||
MFEM_ASSERT(0 <= offset, "invalid offset = " << offset);
|
||||
MFEM_ASSERT(0 <= size, "invalid size = " << size);
|
||||
MFEM_ASSERT(offset + size <= base.capacity,
|
||||
"invalid offset + size = " << offset + size
|
||||
<< " > base capacity = " << base.capacity);
|
||||
capacity = size;
|
||||
h_mt = base.h_mt;
|
||||
h_ptr = base.h_ptr + offset;
|
||||
@@ -1136,6 +1140,7 @@ inline bool Memory<T>::DeviceIsValid() const
|
||||
template <typename T>
|
||||
inline void Memory<T>::CopyFrom(const Memory &src, int size)
|
||||
{
|
||||
MFEM_VERIFY(src.capacity>=size && capacity>=size, "Incorrect size");
|
||||
if (!(flags & REGISTERED) && !(src.flags & REGISTERED))
|
||||
{
|
||||
if (h_ptr != src.h_ptr && size != 0)
|
||||
@@ -1155,6 +1160,7 @@ inline void Memory<T>::CopyFrom(const Memory &src, int size)
|
||||
template <typename T>
|
||||
inline void Memory<T>::CopyFromHost(const T *src, int size)
|
||||
{
|
||||
MFEM_VERIFY(capacity>=size, "Incorrect size");
|
||||
if (!(flags & REGISTERED))
|
||||
{
|
||||
if (h_ptr != src && size != 0)
|
||||
@@ -1171,9 +1177,17 @@ inline void Memory<T>::CopyFromHost(const T *src, int size)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::CopyTo(Memory &dest, int size) const
|
||||
{
|
||||
MFEM_VERIFY(capacity>=size, "Incorrect size");
|
||||
dest.CopyFrom(*this, size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Memory<T>::CopyToHost(T *dest, int size) const
|
||||
{
|
||||
MFEM_VERIFY(capacity>=size, "Incorrect size");
|
||||
if (!(flags & REGISTERED))
|
||||
{
|
||||
if (h_ptr != dest && size != 0)
|
||||
|
||||
@@ -118,6 +118,11 @@ int STable3D::Index (int r, int c, int f) const
|
||||
{
|
||||
STable3DNode *node;
|
||||
|
||||
if (r >= Size)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
Sort3 (r, c, f);
|
||||
|
||||
for (node = Rows[r]; node != NULL; node = node->Prev)
|
||||
|
||||
@@ -78,6 +78,16 @@ if (MFEM_USE_MPI)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_ARPACK)
|
||||
list(APPEND SRCS eigensolvers.cpp arpack.cpp)
|
||||
list(APPEND HDRS eigensolvers.hpp arpack.hpp)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_SPECTRA)
|
||||
list(APPEND SRCS spectra.cpp)
|
||||
list(APPEND HDRS eigen.hpp spectra.hpp)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_SUNDIALS)
|
||||
list(APPEND SRCS sundials.cpp)
|
||||
list(APPEND HDRS sundials.hpp)
|
||||
|
||||
+1122
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,240 @@
|
||||
// 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_ARPACK
|
||||
#define MFEM_ARPACK
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_ARPACK
|
||||
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include <mpi.h>
|
||||
#include "hypre.hpp"
|
||||
#endif
|
||||
|
||||
#include "operator.hpp"
|
||||
|
||||
#define DSAUPD dsaupd_
|
||||
#define DSEUPD dseupd_
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#define PDSAUPD pdsaupd_
|
||||
#define PDSEUPD pdseupd_
|
||||
#endif
|
||||
|
||||
extern "C" void DSAUPD(int *ido,char *bmat, int *n,
|
||||
char *which, int *nev,double *tol,double *resid,
|
||||
int *ncv,double *v, int *ldv,
|
||||
int *iparam, int *ipntr,
|
||||
double *workd, double *workl, int *lworkl, int *info);
|
||||
|
||||
extern "C" void DSEUPD(int *, char *,int *, double *,
|
||||
double *,int *, double *,char *, int *, char *,
|
||||
int *,double *,double *,int *, double *,
|
||||
int *, int *,int *, double *,
|
||||
double *,int *, int *);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
extern "C" void PDSAUPD(int *comm, int *ido,char *bmat, int *n,
|
||||
char *which, int *nev,double *tol,double *resid,
|
||||
int *ncv,double *v, int *ldv,
|
||||
int *iparam, int *ipntr,
|
||||
double *workd, double *workl, int *lworkl, int *info);
|
||||
|
||||
extern "C" void PDSEUPD(int *comm, int *, char *,int *, double *,
|
||||
double *,int *, double *,char *, int *, char *,
|
||||
int *,double *,double *,int *, double *,
|
||||
int *, int *,int *, double *,
|
||||
double *,int *, int *);
|
||||
|
||||
#endif
|
||||
|
||||
extern "C" {
|
||||
void arpackgetcommdbg_(int *,int *,int *);
|
||||
void arpacksetcommdbg_(int *,int *,int *);
|
||||
void arpacksymdbg_(int *,int *,int *,int *,int *,int *,int *);
|
||||
void arpacknonsymdbg_(int *,int *,int *,int *,int *,int *,int *);
|
||||
void arpackcmplxdbg_(int *,int *,int *,int *,int *,int *,int *);
|
||||
}
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class ArPackSym : public Eigensolver
|
||||
{
|
||||
public:
|
||||
|
||||
ArPackSym();
|
||||
virtual ~ArPackSym();
|
||||
|
||||
/** ARPACK modes are described in section 3.5 of the ARPACK manual.
|
||||
Mode 1: regular mode to solve A x = lambda x
|
||||
No solver and no mass matrix are needed.
|
||||
Mode 2: regular inverse mode to solve A x = lambda M x
|
||||
Both A and M are needed and the solver should compute M^{-1}.
|
||||
Mode 3: shift-invert mode to solve either A x = lambda x
|
||||
or A x = lambda M x
|
||||
Mass matrix is optional. The solver should compute
|
||||
(A-sigma I)^{-1} or (A-sigma M)^{-1}. The shift parameter,
|
||||
sigma, also needs to be set with SetShift().
|
||||
Mode 4: Buckling mode to solve K x = lambda K_G x
|
||||
K is set using SetMassMatrix(), K_G is set using SetOperator(),
|
||||
and the solver should compute (K-sigma K_G)^{-1}. The shift
|
||||
parameter, sigma, also needs to be set with SetShift().
|
||||
Mode 5: Cayley mode to solve A x = lambda M x
|
||||
Both A and M are needed and the solver should compute
|
||||
(A - sigma M)^{-1}. The shift parameter, sigma, also needs
|
||||
to be set with SetShift().
|
||||
*/
|
||||
void SetMode(int mode);
|
||||
|
||||
inline void SetTol(double tol) { tol_ = tol; }
|
||||
inline void SetMaxIter(int max_iter) { max_iter_ = max_iter; }
|
||||
inline void SetPrintLevel(int logging) { logging_ = logging; }
|
||||
inline void SetShift(double sigma) { sigma_ = sigma; }
|
||||
inline void SetNumModes(int num_eigs) { nev_ = num_eigs; }
|
||||
|
||||
virtual void SetSolver(Solver & solver);
|
||||
virtual void SetOperator(Operator & A);
|
||||
virtual void SetMassMatrix(Operator & M);
|
||||
|
||||
void Solve();
|
||||
|
||||
/// Collect the converged eigenvalues
|
||||
virtual void GetEigenvalues(Array<double> & eigenvalues);
|
||||
|
||||
/// Extract a single eigenvector
|
||||
virtual Vector & GetEigenvector(unsigned int i);
|
||||
|
||||
/// Transfer ownership of the converged eigenvectors
|
||||
Vector ** StealEigenvectors();
|
||||
|
||||
protected:
|
||||
|
||||
int myid_; // Index of this processor
|
||||
int max_iter_;
|
||||
int logging_;
|
||||
|
||||
// The following variables are for ARPACK
|
||||
int nloc_; // number of items stored locally
|
||||
int nev_; // number of requested eigenvalues
|
||||
int ncv_; // number of ritz vectors
|
||||
int rvec_; // boolean to return eigenvectors as well
|
||||
int mode_; // 1 = standard, 2 = generalized, 3 = shift invert,
|
||||
// 4 = buckling, 5 = Cayley
|
||||
int lworkl_; // length of lworkl_ work array
|
||||
int iparam_[12]; // arpack parameters
|
||||
int ipntr_[12]; // arpack pointers
|
||||
|
||||
char bmat_; // I for standard problem, G for generalized
|
||||
char which_[3]; // spectrum portion: LA, SA, LM, SM, BE
|
||||
char hwmny_; // DSEUPD: A for all eigenvalues, S for some
|
||||
|
||||
double tol_; // relative accuracy bound for Ritz values
|
||||
double sigma_; // eigenvalue shift parameter
|
||||
|
||||
int * select_;// workspace used during eigenvalue computation
|
||||
double * dv_; // Ritz values
|
||||
double * v_; // ncv Lanczos basis vectors
|
||||
double * resid_; // residual vector
|
||||
double * workd_; // work array for 3 vectors used in Arnoldi iteration
|
||||
double * workl_; // work array
|
||||
|
||||
// Operators and Vectors needed outside of ARPACK
|
||||
Solver * solver_;
|
||||
Operator * A_;
|
||||
Operator * B_;
|
||||
|
||||
Vector * w_;
|
||||
Vector * x_;
|
||||
Vector * y_;
|
||||
Vector * z_;
|
||||
|
||||
Vector ** eigenvectors_;
|
||||
|
||||
string solverName_;
|
||||
|
||||
void reverseComm();
|
||||
|
||||
int reverseCommMode1();
|
||||
int reverseCommMode2();
|
||||
int reverseCommMode3();
|
||||
int reverseCommMode4();
|
||||
int reverseCommMode5();
|
||||
|
||||
virtual void prepareEigenvectors();
|
||||
|
||||
void printErrors(const int & info, const int iparam[],
|
||||
const char & bmat, const int & n,
|
||||
const char which[],
|
||||
const int & nev, const int & ncv,
|
||||
const int & lworkl );
|
||||
|
||||
private:
|
||||
|
||||
virtual int computeNlocf() { return nloc_; }
|
||||
virtual int computeIter(int & ido);
|
||||
virtual int computeEigs();
|
||||
|
||||
};
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
class ParArPackSym : public ArPackSym
|
||||
{
|
||||
public:
|
||||
ParArPackSym(MPI_Comm comm);
|
||||
virtual ~ParArPackSym() {}
|
||||
|
||||
void SetOperator(Operator & A);
|
||||
void SetMassMatrix(Operator & M);
|
||||
|
||||
/// Collect the converged eigenvalues
|
||||
void GetEigenvalues(Array<double> & eigenvalues);
|
||||
|
||||
/// Extract a single eigenvector
|
||||
Vector & GetEigenvector(unsigned int i);
|
||||
|
||||
/// Transfer ownership of the converged eigenvectors
|
||||
// HypreParVector ** StealEigenvectors();
|
||||
Vector ** StealEigenvectors();
|
||||
|
||||
protected:
|
||||
|
||||
void prepareEigenvectors();
|
||||
|
||||
private:
|
||||
|
||||
MPI_Comm comm_;
|
||||
MPI_Fint commf_; // Fortran style MPI communicator
|
||||
int numProcs_; // Number of processors
|
||||
|
||||
HYPRE_Int * part_; // parallel partitioning for eigenvectors
|
||||
|
||||
int computeNlocf();
|
||||
int computeIter(int & ido);
|
||||
int computeEigs();
|
||||
|
||||
};
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
};
|
||||
|
||||
#endif // MFEM_USE_ARPACK
|
||||
|
||||
#endif // MFEM_ARPACK
|
||||
@@ -787,10 +787,16 @@ HypreParMatrix * ComplexHypreParMatrix::GetSystemMatrix() const
|
||||
2 * num_cols_offd, cmap,
|
||||
true);
|
||||
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
// Give the new matrix ownership of row_starts and col_starts
|
||||
hypre_ParCSRMatrix *hA = (hypre_ParCSRMatrix*)(*A);
|
||||
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(hA,1);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(hA,1);
|
||||
#else
|
||||
mfem_hypre_TFree_host(row_starts);
|
||||
mfem_hypre_TFree_host(col_starts);
|
||||
#endif
|
||||
|
||||
return A;
|
||||
}
|
||||
|
||||
@@ -763,6 +763,13 @@ public:
|
||||
tdata.New(i*j*k);
|
||||
}
|
||||
|
||||
DenseTensor(double *d, int i, int j, int k)
|
||||
: Mk(NULL, i, j)
|
||||
{
|
||||
nk = k;
|
||||
tdata.Wrap(d, i*j*k, false);
|
||||
}
|
||||
|
||||
DenseTensor(int i, int j, int k, MemoryType mt)
|
||||
: Mk(NULL, i, j)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
#ifndef MFEM_EIGEN_HPP
|
||||
#define MFEM_EIGEN_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <Eigen/Sparse>
|
||||
|
||||
#include "vector.hpp"
|
||||
#include "sparsemat.hpp"
|
||||
#include "densemat.hpp"
|
||||
|
||||
namespace mfem{
|
||||
/** @brief Eigen template specialization for vector conversion */
|
||||
template <typename T>
|
||||
struct VectorConverter {
|
||||
static Vector from(const Eigen::Matrix<T, Eigen::Dynamic, 1>& other)
|
||||
{
|
||||
Vector v(other.rows());
|
||||
|
||||
for (size_t i = 0; i < v.Size(); i++)
|
||||
v(i) = other(i);
|
||||
|
||||
return std::move(v);
|
||||
}
|
||||
|
||||
static Eigen::Matrix<T, Eigen::Dynamic, 1> to(const Vector& other)
|
||||
{
|
||||
Eigen::Matrix<T, Eigen::Dynamic, 1> v(other.Size());
|
||||
|
||||
for (size_t i = 0; i < v.Size(); i++)
|
||||
v(i) = other(i);
|
||||
|
||||
return std::move(v);
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief Eigen template specialization for dense matrix conversion */
|
||||
template <typename T>
|
||||
struct DenseMatrixConverter {
|
||||
static DenseMatrix from(const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>& other)
|
||||
{
|
||||
DenseMatrix mat(other.rows(), other.cols());
|
||||
|
||||
for (size_t j = 0; j < mat.Width(); j++)
|
||||
for (size_t i = 0; i < mat.Height(); i++)
|
||||
mat(i, j) = other(i, j);
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
static Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> to(const DenseMatrix& other)
|
||||
{
|
||||
Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic> mat(other.Height(), other.Width());
|
||||
|
||||
for (size_t j = 0; j < mat.cols(); j++)
|
||||
for (size_t i = 0; i < mat.rows(); i++)
|
||||
mat(i, j) = other(i, j);
|
||||
|
||||
return mat;
|
||||
}
|
||||
};
|
||||
|
||||
/** @brief Eigen template specialization for sparse matrix conversion */
|
||||
template <class T>
|
||||
struct SparseMatrixConverter {
|
||||
static SparseMatrix from(const Eigen::SparseMatrix<T, Eigen::RowMajor>& other)
|
||||
{
|
||||
return SparseMatrix(other.outerIndexPtr(), other.innerIndexPtr(), other.valuePtr(), other.rows(), other.cols());
|
||||
}
|
||||
|
||||
static Eigen::SparseMatrix<T, Eigen::RowMajor> to(const SparseMatrix& other)
|
||||
{
|
||||
// MFEM memory info
|
||||
const int *I = other.GetI(), *J = other.GetJ();
|
||||
const T* Data = other.GetData();
|
||||
|
||||
// Eigen triplet
|
||||
std::vector<Eigen::Triplet<double>> tripletList;
|
||||
tripletList.reserve(other.GetMemoryData().Capacity());
|
||||
|
||||
for (size_t i = 0; i < other.Size(); i++) {
|
||||
for (size_t k = I[i], end = I[i + 1]; k < end; k++)
|
||||
tripletList.push_back(Eigen::Triplet<double>(i, J[k], Data[k]));
|
||||
}
|
||||
|
||||
// Create Eigen sparse matrix
|
||||
Eigen::SparseMatrix<T, Eigen::RowMajor> mat(other.Height(), other.Width());
|
||||
mat.setFromTriplets(tripletList.begin(), tripletList.end());
|
||||
|
||||
return mat;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // MFEM_EIGEN_HPP
|
||||
@@ -0,0 +1,23 @@
|
||||
// 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 "linalg.hpp"
|
||||
#include "eigensolver.hpp"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
Eigensolver::Eigensolver()
|
||||
{}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
// 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_EIGENSOLVERS
|
||||
#define MFEM_EIGENSOLVERS
|
||||
|
||||
#include "vector.hpp"
|
||||
#include "operator.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// Abstract Eigensolver
|
||||
class Eigensolver
|
||||
{
|
||||
public:
|
||||
|
||||
Eigensolver();
|
||||
virtual ~Eigensolver() {}
|
||||
|
||||
virtual void SetTol(double tol) = 0;
|
||||
virtual void SetMaxIter(int max_iter) = 0;
|
||||
virtual void SetPrintLevel(int logging) = 0;
|
||||
virtual void SetNumModes(int num_eigs) = 0;
|
||||
|
||||
virtual void SetOperator(Operator & A) = 0;
|
||||
virtual void SetMassMatrix(Operator & M) = 0;
|
||||
|
||||
/// Perform the eigenvalue solve
|
||||
virtual void Solve() = 0;
|
||||
|
||||
/// Collect the converged eigenvalues
|
||||
virtual void GetEigenvalues(Array<double> & eigenvalues) = 0;
|
||||
|
||||
/// Extract a single eigenvector
|
||||
virtual Vector & GetEigenvector(unsigned int i) = 0;
|
||||
|
||||
/// Transfer ownership of the converged eigenvectors
|
||||
virtual Vector ** StealEigenvectors() = 0;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
+63
-1
@@ -88,7 +88,9 @@ HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
|
||||
{
|
||||
x = hypre_ParVectorCreate(comm,glob_size,col);
|
||||
hypre_ParVectorInitialize(x);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParVectorSetPartitioningOwner(x,0);
|
||||
#endif
|
||||
// The data will be destroyed by hypre (this is the default)
|
||||
hypre_ParVectorSetDataOwner(x,1);
|
||||
hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(x),1);
|
||||
@@ -105,7 +107,9 @@ HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
|
||||
hypre_ParVectorSetDataOwner(x,1); // owns the seq vector
|
||||
hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
|
||||
hypre_SeqVectorSetDataOwner(x_loc,0);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParVectorSetPartitioningOwner(x,0);
|
||||
#endif
|
||||
double tmp = 0.0;
|
||||
hypre_VectorData(x_loc) = &tmp;
|
||||
#ifdef HYPRE_USING_CUDA
|
||||
@@ -128,7 +132,9 @@ HypreParVector::HypreParVector(const HypreParVector &y) : Vector()
|
||||
x = hypre_ParVectorCreate(y.x -> comm, y.x -> global_size,
|
||||
y.x -> partitioning);
|
||||
hypre_ParVectorInitialize(x);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParVectorSetPartitioningOwner(x,0);
|
||||
#endif
|
||||
hypre_ParVectorSetDataOwner(x,1);
|
||||
hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(x),1);
|
||||
_SetDataAndSize_();
|
||||
@@ -162,7 +168,9 @@ HypreParVector::HypreParVector(ParFiniteElementSpace *pfes)
|
||||
x = hypre_ParVectorCreate(pfes->GetComm(), pfes->GlobalTrueVSize(),
|
||||
pfes->GetTrueDofOffsets());
|
||||
hypre_ParVectorInitialize(x);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParVectorSetPartitioningOwner(x,0);
|
||||
#endif
|
||||
// The data will be destroyed by hypre (this is the default)
|
||||
hypre_ParVectorSetDataOwner(x,1);
|
||||
hypre_SeqVectorSetDataOwner(hypre_ParVectorLocalVector(x),1);
|
||||
@@ -683,8 +691,10 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm, HYPRE_BigInt glob_size,
|
||||
A = hypre_ParCSRMatrixCreate(comm, glob_size, glob_size, row_starts,
|
||||
row_starts, 0, diag->NumNonZeroElems(), 0);
|
||||
hypre_ParCSRMatrixSetDataOwner(A,1);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(A,0);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(A,0);
|
||||
#endif
|
||||
|
||||
hypre_CSRMatrixSetDataOwner(A->diag,0);
|
||||
diagOwner = CopyCSR(diag, mem_diag, A->diag, false);
|
||||
@@ -726,8 +736,10 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm,
|
||||
row_starts, col_starts,
|
||||
0, diag->NumNonZeroElems(), 0);
|
||||
hypre_ParCSRMatrixSetDataOwner(A,1);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(A,0);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(A,0);
|
||||
#endif
|
||||
|
||||
hypre_CSRMatrixSetDataOwner(A->diag,0);
|
||||
diagOwner = CopyCSR(diag, mem_diag, A->diag, false);
|
||||
@@ -770,8 +782,10 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm,
|
||||
offd->Width(), diag->NumNonZeroElems(),
|
||||
offd->NumNonZeroElems());
|
||||
hypre_ParCSRMatrixSetDataOwner(A,1);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(A,0);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(A,0);
|
||||
#endif
|
||||
|
||||
hypre_CSRMatrixSetDataOwner(A->diag,0);
|
||||
diagOwner = CopyCSR(diag, mem_diag, A->diag, own_diag_offd);
|
||||
@@ -817,8 +831,10 @@ HypreParMatrix::HypreParMatrix(
|
||||
A = hypre_ParCSRMatrixCreate(comm, global_num_rows, global_num_cols,
|
||||
row_starts, col_starts, offd_num_cols, 0, 0);
|
||||
hypre_ParCSRMatrixSetDataOwner(A,1);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(A,0);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(A,0);
|
||||
#endif
|
||||
|
||||
HYPRE_Int local_num_rows = hypre_CSRMatrixNumRows(A->diag);
|
||||
|
||||
@@ -931,8 +947,10 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm,
|
||||
A = hypre_ParCSRMatrixCreate(comm, global_num_rows, global_num_cols,
|
||||
row_starts, col_starts, 0, nnz, 0);
|
||||
hypre_ParCSRMatrixSetDataOwner(A,1);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(A,0);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(A,0);
|
||||
#endif
|
||||
|
||||
hypre_CSRMatrixSetDataOwner(A->diag,0);
|
||||
diagOwner = CopyBoolCSR(diag, mem_diag, A->diag);
|
||||
@@ -989,8 +1007,10 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm, int id, int np,
|
||||
}
|
||||
|
||||
hypre_ParCSRMatrixSetDataOwner(A,1);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(A,0);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(A,0);
|
||||
#endif
|
||||
|
||||
mem_diag.data.New(diag_nnz);
|
||||
for (HYPRE_Int i = 0; i < diag_nnz; i++)
|
||||
@@ -1177,6 +1197,13 @@ HypreParMatrix::HypreParMatrix(MPI_Comm comm, int nrows,
|
||||
{
|
||||
hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
|
||||
}
|
||||
#if MFEM_HYPRE_VERSION > 22200
|
||||
mfem_hypre_TFree_host(row_starts);
|
||||
if (rows != cols)
|
||||
{
|
||||
mfem_hypre_TFree_host(col_starts);
|
||||
}
|
||||
#endif
|
||||
hypre_MatvecCommPkgCreate(A);
|
||||
|
||||
height = GetNumRows();
|
||||
@@ -1263,6 +1290,7 @@ void HypreParMatrix::SetOwnerFlags(signed char diag, signed char offd,
|
||||
|
||||
void HypreParMatrix::CopyRowStarts()
|
||||
{
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
if (!A || hypre_ParCSRMatrixOwnsRowStarts(A) ||
|
||||
(hypre_ParCSRMatrixRowStarts(A) == hypre_ParCSRMatrixColStarts(A) &&
|
||||
hypre_ParCSRMatrixOwnsColStarts(A)))
|
||||
@@ -1297,10 +1325,12 @@ void HypreParMatrix::CopyRowStarts()
|
||||
hypre_ParCSRMatrixColStarts(A) = new_row_starts;
|
||||
hypre_ParCSRMatrixOwnsColStarts(A) = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HypreParMatrix::CopyColStarts()
|
||||
{
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
if (!A || hypre_ParCSRMatrixOwnsColStarts(A) ||
|
||||
(hypre_ParCSRMatrixRowStarts(A) == hypre_ParCSRMatrixColStarts(A) &&
|
||||
hypre_ParCSRMatrixOwnsRowStarts(A)))
|
||||
@@ -1339,6 +1369,7 @@ void HypreParMatrix::CopyColStarts()
|
||||
{
|
||||
hypre_ParCSRMatrixOwnsColStarts(A) = 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void HypreParMatrix::GetDiag(Vector &diag) const
|
||||
@@ -1791,9 +1822,14 @@ HypreParMatrix* HypreParMatrix::LeftDiagMult(const SparseMatrix &D,
|
||||
DA_diag, DA_offd, new_col_map_offd,
|
||||
own_diag_offd);
|
||||
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
// Give ownership of row_starts, col_starts, and col_map_offd to DA
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(DA->A, 1);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(DA->A, 1);
|
||||
#else
|
||||
mfem_hypre_TFree_host(new_row_starts);
|
||||
mfem_hypre_TFree_host(new_col_starts);
|
||||
#endif
|
||||
DA->colMapOwner = 1;
|
||||
|
||||
return DA;
|
||||
@@ -1948,18 +1984,22 @@ void HypreParMatrix::Threshold(double threshold)
|
||||
row_starts = hypre_ParCSRMatrixRowStarts(A);
|
||||
col_starts = hypre_ParCSRMatrixColStarts(A);
|
||||
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
bool old_owns_row = hypre_ParCSRMatrixOwnsRowStarts(A);
|
||||
bool old_owns_col = hypre_ParCSRMatrixOwnsColStarts(A);
|
||||
#endif
|
||||
HYPRE_BigInt global_num_rows = hypre_ParCSRMatrixGlobalNumRows(A);
|
||||
HYPRE_BigInt global_num_cols = hypre_ParCSRMatrixGlobalNumCols(A);
|
||||
parcsr_A_ptr = hypre_ParCSRMatrixCreate(comm, global_num_rows,
|
||||
global_num_cols,
|
||||
row_starts, col_starts,
|
||||
0, 0, 0);
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParCSRMatrixOwnsRowStarts(parcsr_A_ptr) = old_owns_row;
|
||||
hypre_ParCSRMatrixOwnsColStarts(parcsr_A_ptr) = old_owns_col;
|
||||
hypre_ParCSRMatrixOwnsRowStarts(A) = 0;
|
||||
hypre_ParCSRMatrixOwnsColStarts(A) = 0;
|
||||
#endif
|
||||
|
||||
csr_A = hypre_MergeDiagAndOffd(A);
|
||||
|
||||
@@ -1994,7 +2034,12 @@ void HypreParMatrix::Threshold(double threshold)
|
||||
|
||||
hypre_ParCSRMatrixSetNumNonzeros(A);
|
||||
/* Make sure that the first entry in each row is the diagonal one. */
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
if (row_starts == col_starts)
|
||||
#else
|
||||
if ((row_starts[0] == col_starts[0]) &&
|
||||
(row_starts[1] == col_starts[1]))
|
||||
#endif
|
||||
{
|
||||
hypre_CSRMatrixReorder(hypre_ParCSRMatrixDiag(A));
|
||||
}
|
||||
@@ -2503,11 +2548,14 @@ HypreParMatrix * RAP(const HypreParMatrix *A, const HypreParMatrix *P)
|
||||
// hypre_ParCSRMatrixRAPKT
|
||||
}
|
||||
#else
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
HYPRE_Int P_owns_its_col_starts =
|
||||
hypre_ParCSRMatrixOwnsColStarts((hypre_ParCSRMatrix*)(*P));
|
||||
#endif
|
||||
|
||||
hypre_BoomerAMGBuildCoarseOperator(*P,*A,*P,&rap);
|
||||
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
/* Warning: hypre_BoomerAMGBuildCoarseOperator steals the col_starts
|
||||
from P (even if it does not own them)! */
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(rap,0);
|
||||
@@ -2516,6 +2564,7 @@ HypreParMatrix * RAP(const HypreParMatrix *A, const HypreParMatrix *P)
|
||||
{
|
||||
hypre_ParCSRMatrixSetColStartsOwner(*P, 1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
hypre_ParCSRMatrixSetNumNonzeros(rap);
|
||||
@@ -2536,13 +2585,16 @@ HypreParMatrix * RAP(const HypreParMatrix * Rt, const HypreParMatrix *A,
|
||||
hypre_ParCSRMatrixDestroy(Q);
|
||||
}
|
||||
#else
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
HYPRE_Int P_owns_its_col_starts =
|
||||
hypre_ParCSRMatrixOwnsColStarts((hypre_ParCSRMatrix*)(*P));
|
||||
HYPRE_Int Rt_owns_its_col_starts =
|
||||
hypre_ParCSRMatrixOwnsColStarts((hypre_ParCSRMatrix*)(*Rt));
|
||||
#endif
|
||||
|
||||
hypre_BoomerAMGBuildCoarseOperator(*Rt,*A,*P,&rap);
|
||||
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
/* Warning: hypre_BoomerAMGBuildCoarseOperator steals the col_starts
|
||||
from Rt and P (even if they do not own them)! */
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(rap,0);
|
||||
@@ -2555,6 +2607,7 @@ HypreParMatrix * RAP(const HypreParMatrix * Rt, const HypreParMatrix *A,
|
||||
{
|
||||
hypre_ParCSRMatrixSetColStartsOwner(*Rt, 1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
hypre_ParCSRMatrixSetNumNonzeros(rap);
|
||||
@@ -3144,8 +3197,12 @@ void HypreSmoother::SetOperator(const Operator &op)
|
||||
}
|
||||
else
|
||||
{
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
min_eig_est = 0;
|
||||
hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est);
|
||||
#else
|
||||
hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est, &min_eig_est);
|
||||
#endif
|
||||
}
|
||||
Z = new HypreParVector(*A);
|
||||
}
|
||||
@@ -3159,8 +3216,12 @@ void HypreSmoother::SetOperator(const Operator &op)
|
||||
}
|
||||
else
|
||||
{
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
min_eig_est = 0;
|
||||
hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est);
|
||||
#else
|
||||
hypre_ParCSRMaxEigEstimate(*A, poly_scale, &max_eig_est, &min_eig_est);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The Taubin and FIR polynomials are defined on [0, 2]
|
||||
@@ -4997,11 +5058,11 @@ HypreADS::HypreADS(const HypreParMatrix &A, ParFiniteElementSpace *face_fespace)
|
||||
void HypreADS::Init(ParFiniteElementSpace *face_fespace)
|
||||
{
|
||||
int cycle_type = 11;
|
||||
int rlx_type = 2;
|
||||
int rlx_sweeps = 1;
|
||||
double rlx_weight = 1.0;
|
||||
double rlx_omega = 1.0;
|
||||
#ifndef HYPRE_USING_CUDA
|
||||
int rlx_type = 2;
|
||||
int amg_coarsen_type = 10;
|
||||
int amg_agg_levels = 1;
|
||||
int amg_rlx_type = 8;
|
||||
@@ -5009,6 +5070,7 @@ void HypreADS::Init(ParFiniteElementSpace *face_fespace)
|
||||
int amg_interp_type = 6;
|
||||
int amg_Pmax = 4;
|
||||
#else
|
||||
int rlx_type = 1;
|
||||
int amg_coarsen_type = 8;
|
||||
int amg_agg_levels = 0;
|
||||
int amg_rlx_type = 18;
|
||||
|
||||
@@ -508,8 +508,10 @@ void hypre_ParCSRMatrixEliminateAAe(hypre_ParCSRMatrix *A,
|
||||
hypre_ParCSRMatrixColStarts(A),
|
||||
0, 0, 0);
|
||||
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(*Ae, 0);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(*Ae, 0);
|
||||
#endif
|
||||
|
||||
hypre_CSRMatrix *Ae_diag = hypre_ParCSRMatrixDiag(*Ae);
|
||||
hypre_CSRMatrix *Ae_offd = hypre_ParCSRMatrixOffd(*Ae);
|
||||
@@ -1002,10 +1004,17 @@ void hypre_ParCSRMatrixSplit(hypre_ParCSRMatrix *A,
|
||||
|
||||
hypre_ParCSRMatrixOwnsData(blocks[i]) = 1;
|
||||
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
/* only the first block will own the row/col_starts */
|
||||
hypre_ParCSRMatrixOwnsRowStarts(blocks[i]) = !i;
|
||||
hypre_ParCSRMatrixOwnsColStarts(blocks[i]) = !i;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MFEM_HYPRE_VERSION > 22200
|
||||
mfem_hypre_TFree_host(row_starts);
|
||||
mfem_hypre_TFree_host(col_starts);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Based on hypre_CSRMatrixMatvec in hypre's csr_matvec.c */
|
||||
@@ -1916,9 +1925,12 @@ hypre_ParCSRMatrixAdd(hypre_ParCSRMatrix *A,
|
||||
|
||||
/* C owns diag, offd, and cmap. */
|
||||
hypre_ParCSRMatrixSetDataOwner(C, 1);
|
||||
|
||||
#if MFEM_HYPRE_VERSION <= 22200
|
||||
/* C does not own row and column starts. */
|
||||
hypre_ParCSRMatrixSetRowStartsOwner(C, 0);
|
||||
hypre_ParCSRMatrixSetColStartsOwner(C, 0);
|
||||
#endif
|
||||
|
||||
return C;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,16 @@
|
||||
#include "ginkgo.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_ARPACK
|
||||
#include "eigensolver.hpp"
|
||||
#include "arpack.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_SPECTRA
|
||||
#include "eigen.hpp"
|
||||
#include "spectra.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "hypre_parcsr.hpp"
|
||||
#include "hypre.hpp"
|
||||
|
||||
+11
-23
@@ -33,7 +33,12 @@ int SparseMatrix::SparseMatrixCount = 0;
|
||||
cusparseHandle_t SparseMatrix::handle = nullptr;
|
||||
size_t SparseMatrix::bufferSize = 0;
|
||||
void * SparseMatrix::dBuffer = nullptr;
|
||||
#endif
|
||||
# if CUSPARSE_VERSION >= 11400
|
||||
# define MFEM_CUSPARSE_ALG CUSPARSE_SPMV_CSR_ALG1
|
||||
# else
|
||||
# define MFEM_CUSPARSE_ALG CUSPARSE_CSRMV_ALG1
|
||||
# endif // CUSPARSE_VERSION >= 11400
|
||||
#endif // MFEM_USE_CUDA
|
||||
|
||||
void SparseMatrix::InitCuSparse()
|
||||
{
|
||||
@@ -679,25 +684,16 @@ void SparseMatrix::AddMult(const Vector &x, Vector &y, const double a) const
|
||||
cusparseCreateMatDescr(&matA_descr);
|
||||
cusparseSetMatIndexBase(matA_descr, CUSPARSE_INDEX_BASE_ZERO);
|
||||
cusparseSetMatType(matA_descr, CUSPARSE_MATRIX_TYPE_GENERAL);
|
||||
|
||||
#endif
|
||||
|
||||
initBuffers = true;
|
||||
}
|
||||
// Allocate kernel space. Buffer is shared between different sparsemats
|
||||
size_t newBufferSize = 0;
|
||||
|
||||
#if CUDA_VERSION >= 11020
|
||||
cusparseSpMV_bufferSize(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha,
|
||||
matA_descr,
|
||||
vecX_descr, &beta, vecY_descr, CUDA_R_64F,
|
||||
CUSPARSE_SPMV_CSR_ALG1, &newBufferSize);
|
||||
#elif CUDA_VERSION >= 10010
|
||||
cusparseSpMV_bufferSize(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha,
|
||||
matA_descr,
|
||||
vecX_descr, &beta, vecY_descr, CUDA_R_64F,
|
||||
CUSPARSE_CSRMV_ALG1, &newBufferSize);
|
||||
#endif
|
||||
MFEM_CUSPARSE_ALG, &newBufferSize);
|
||||
|
||||
// Check if we need to resize
|
||||
if (newBufferSize > bufferSize)
|
||||
@@ -707,30 +703,22 @@ void SparseMatrix::AddMult(const Vector &x, Vector &y, const double a) const
|
||||
CuMemAlloc(&dBuffer, bufferSize);
|
||||
}
|
||||
|
||||
#if CUDA_VERSION >= 11020
|
||||
#if CUDA_VERSION >= 10010
|
||||
// Update input/output vectors
|
||||
cusparseDnVecSetValues(vecX_descr, const_cast<double *>(d_x));
|
||||
cusparseDnVecSetValues(vecY_descr, d_y);
|
||||
|
||||
// Y = alpha A * X + beta * Y
|
||||
cusparseSpMV(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA_descr,
|
||||
vecX_descr, &beta, vecY_descr, CUDA_R_64F, CUSPARSE_SPMV_CSR_ALG1, dBuffer);
|
||||
#elif CUDA_VERSION >= 10010
|
||||
// Update input/output vectors
|
||||
cusparseDnVecSetValues(vecX_descr, const_cast<double *>(d_x));
|
||||
cusparseDnVecSetValues(vecY_descr, d_y);
|
||||
|
||||
// Y = alpha A * X + beta * Y
|
||||
cusparseSpMV(handle, CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA_descr,
|
||||
vecX_descr, &beta, vecY_descr, CUDA_R_64F, CUSPARSE_CSRMV_ALG1, dBuffer);
|
||||
vecX_descr, &beta, vecY_descr, CUDA_R_64F, MFEM_CUSPARSE_ALG, dBuffer);
|
||||
#else
|
||||
cusparseDcsrmv(handle, CUSPARSE_OPERATION_NON_TRANSPOSE,
|
||||
Height(), Width(), J.Capacity(),
|
||||
&alpha, matA_descr,
|
||||
const_cast<double *>(d_A), const_cast<int *>(d_I), const_cast<int *>(d_J),
|
||||
const_cast<double *>(d_x), &beta, d_y);
|
||||
#endif
|
||||
#endif
|
||||
#endif // CUDA_VERSION >= 10010
|
||||
#endif // MFEM_USE_CUDA
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
#include "spectra.hpp"
|
||||
|
||||
#include "../fem/bilinearform.hpp"
|
||||
|
||||
namespace mfem {
|
||||
SpectraEigenSolver::SpectraEigenSolver()
|
||||
{
|
||||
// Init params
|
||||
_nconv = 0;
|
||||
_nev = 1;
|
||||
_ncv = 1;
|
||||
_max_iter = 1000;
|
||||
_tol = 1e-3;
|
||||
}
|
||||
|
||||
SpectraEigenSolver::~SpectraEigenSolver()
|
||||
{
|
||||
delete _A_s, _B_s, _S, _G;
|
||||
}
|
||||
|
||||
/// Set dimension of Krylov subspace in the Lanczos method
|
||||
SpectraEigenSolver& SpectraEigenSolver::SetKrylov(double ncv)
|
||||
{
|
||||
_ncv = ncv;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Set solver tolerance
|
||||
SpectraEigenSolver& SpectraEigenSolver::SetTol(double tol)
|
||||
{
|
||||
_tol = tol;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Set maximum number of iterations
|
||||
SpectraEigenSolver& SpectraEigenSolver::SetMaxIter(int max_iter)
|
||||
{
|
||||
_max_iter = max_iter;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Set the number of required eigenmodes
|
||||
SpectraEigenSolver& SpectraEigenSolver::SetNumModes(int nev)
|
||||
{
|
||||
_nev = nev;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Set operator for standard eigenvalue problem (A*x = lambda*x)
|
||||
SpectraEigenSolver& SpectraEigenSolver::SetOperator(const Operator& A)
|
||||
{
|
||||
// Set EIGEN operators
|
||||
_A_e = SparseMatrixConverter<double>::to(static_cast<const BilinearForm&>(A).SpMat());
|
||||
|
||||
// Set SPECTRA operators
|
||||
_A_s = new SparseSymMatProd<double>(_A_e);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Set operator for generalized eigenvalue problem (A*x = lambda*B*x)
|
||||
SpectraEigenSolver& SpectraEigenSolver::SetOperators(const Operator& A, const Operator& B)
|
||||
{
|
||||
// Set EIGEN operators
|
||||
_A_e = SparseMatrixConverter<double>::to(static_cast<const BilinearForm&>(A).SpMat());
|
||||
_B_e = SparseMatrixConverter<double>::to(static_cast<const BilinearForm&>(B).SpMat());
|
||||
|
||||
// Set SPECTRA operators
|
||||
_A_s = new SparseSymMatProd<double>(_A_e);
|
||||
_B_s = new SparseCholesky<double>(_B_e);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Solve the eigenvalue problem for the specified number of eigenvalues
|
||||
void SpectraEigenSolver::Solve()
|
||||
{
|
||||
// Set the dimension of the Krilov space equal to the number of requested eigenvalues if necessary
|
||||
if (_ncv < _nev)
|
||||
_ncv = _nev;
|
||||
|
||||
if (!_B_s) {
|
||||
_S = new SymEigsSolver<SparseSymMatProd<double>>(*_A_s, _nev, _ncv);
|
||||
_S->init();
|
||||
_nconv = _S->compute(SortRule::SmallestMagn, _max_iter, _tol, SortRule::SmallestMagn);
|
||||
}
|
||||
else {
|
||||
_G = new SymGEigsSolver<SparseSymMatProd<double>, SparseCholesky<double>, GEigsMode::Cholesky>(*_A_s, *_B_s, _nev, _ncv);
|
||||
_G->init();
|
||||
_nconv = _G->compute(SortRule::SmallestMagn, _max_iter, _tol, SortRule::SmallestMagn);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of converged eigenvalues
|
||||
int SpectraEigenSolver::GetNumConverged()
|
||||
{
|
||||
return _nconv;
|
||||
}
|
||||
|
||||
/// Get the corresponding eigenvalue
|
||||
double SpectraEigenSolver::GetEigenvalue(unsigned int i) const
|
||||
{
|
||||
if (!_B_s) {
|
||||
if (_S->info() == CompInfo::Successful && i < _nconv)
|
||||
return _S->eigenvalues()[i];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
if (_G->info() == CompInfo::Successful && i < _nconv)
|
||||
return _G->eigenvalues()[i];
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Eigen::VectorXd SpectraEigenSolver::GetEigenvalues(unsigned int i) const
|
||||
{
|
||||
if (!_B_s) {
|
||||
if (_S->info() == CompInfo::Successful && i < _nconv)
|
||||
return _S->eigenvalues().segment(0, i);
|
||||
}
|
||||
else {
|
||||
if (_G->info() == CompInfo::Successful && i < _nconv)
|
||||
return _G->eigenvalues().segment(0, i);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the corresponding eigenvector
|
||||
Eigen::VectorXd SpectraEigenSolver::GetEigenvector(unsigned int i) const
|
||||
{
|
||||
if (!_B_s) {
|
||||
if (_S->info() == CompInfo::Successful && i < _nconv)
|
||||
return _S->eigenvectors().col(i);
|
||||
}
|
||||
else {
|
||||
if (_G->info() == CompInfo::Successful && i < _nconv)
|
||||
return _G->eigenvectors().col(i);
|
||||
}
|
||||
}
|
||||
|
||||
Eigen::MatrixXd SpectraEigenSolver::GetEigenvectors(unsigned int i) const
|
||||
{
|
||||
if (!_B_s) {
|
||||
if (_S->info() == CompInfo::Successful && i < _nconv)
|
||||
return _S->eigenvectors().topRows(i);
|
||||
}
|
||||
else {
|
||||
if (_G->info() == CompInfo::Successful && i < _nconv)
|
||||
return _G->eigenvectors().topRows(i);
|
||||
}
|
||||
}
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef MFEM_SPECTRA_HPP
|
||||
#define MFEM_SPECTRA_HPP
|
||||
|
||||
#include <Spectra/GenEigsSolver.h>
|
||||
#include <Spectra/MatOp/SparseCholesky.h>
|
||||
#include <Spectra/MatOp/SparseGenMatProd.h>
|
||||
#include <Spectra/SymEigsSolver.h>
|
||||
#include <Spectra/SymGEigsSolver.h>
|
||||
|
||||
#include "eigen.hpp"
|
||||
|
||||
using namespace Spectra;
|
||||
|
||||
namespace mfem {
|
||||
class SpectraEigenSolver {
|
||||
public:
|
||||
SpectraEigenSolver();
|
||||
|
||||
virtual ~SpectraEigenSolver();
|
||||
|
||||
/// Set dimension of Krylov subspace in the Lanczos method
|
||||
SpectraEigenSolver& SetKrylov(double ncv);
|
||||
|
||||
/// Set solver tolerance
|
||||
SpectraEigenSolver& SetTol(double tol);
|
||||
|
||||
/// Set maximum number of iterations
|
||||
SpectraEigenSolver& SetMaxIter(int max_iter);
|
||||
|
||||
/// Set the number of required eigenmodes
|
||||
SpectraEigenSolver& SetNumModes(int nev);
|
||||
|
||||
/// Set operator for standard eigenvalue problem (A*x = lambda*x)
|
||||
SpectraEigenSolver& SetOperator(const Operator& A);
|
||||
|
||||
/// Set operator for generalized eigenvalue problem (A*x = lambda*B*x)
|
||||
SpectraEigenSolver& SetOperators(const Operator& A, const Operator& B);
|
||||
|
||||
/// Solve the eigenvalue problem for the specified number of eigenvalues
|
||||
void Solve();
|
||||
|
||||
/// Get the number of converged eigenvalues
|
||||
int GetNumConverged();
|
||||
|
||||
/// Get the corresponding eigenvalue
|
||||
double GetEigenvalue(unsigned int i) const;
|
||||
|
||||
Eigen::VectorXd GetEigenvalues(unsigned int i = 0) const;
|
||||
|
||||
/// Get the corresponding eigenvector
|
||||
Eigen::VectorXd GetEigenvector(unsigned int i) const;
|
||||
|
||||
Eigen::MatrixXd GetEigenvectors(unsigned int i) const;
|
||||
|
||||
protected:
|
||||
// Params
|
||||
int _nconv, _nev, _ncv, _max_iter;
|
||||
double _tol;
|
||||
|
||||
// EIGEN Operators
|
||||
Eigen::SparseMatrix<double> _A_e, _B_e;
|
||||
|
||||
// Spectra Operators
|
||||
SparseSymMatProd<double>* _A_s = nullptr;
|
||||
SparseCholesky<double>* _B_s = nullptr;
|
||||
|
||||
// Eigenvalue solution based on Spectra
|
||||
SymEigsSolver<SparseSymMatProd<double>>* _S = nullptr;
|
||||
SymGEigsSolver<SparseSymMatProd<double>, SparseCholesky<double>, GEigsMode::Cholesky>* _G = nullptr;
|
||||
|
||||
// // Eigenvalue solution based on Eigen
|
||||
// Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd>* _S = nullptr;
|
||||
// Eigen::GeneralizedSelfAdjointEigenSolver<Eigen::MatrixXd>* _G = nullptr;
|
||||
};
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_SPECTRA_HPP
|
||||
@@ -82,6 +82,11 @@ public:
|
||||
Vector(double *data_, int size_)
|
||||
{ data.Wrap(data_, size_, false); size = size_; }
|
||||
|
||||
/** @brief Create a Vector referencing a sub-vector of the Vector @a base
|
||||
starting at the given offset, @a base_offset, and size @a size_. */
|
||||
Vector(Vector &base, int base_offset, int size_)
|
||||
: data(base.data, base_offset, size_), size(size_) { }
|
||||
|
||||
/// Create a Vector of size @a size_ using MemoryType @a mt.
|
||||
Vector(int size_, MemoryType mt)
|
||||
: data(size_, mt), size(size_) { }
|
||||
|
||||
@@ -119,7 +119,7 @@ $(if $(word 2,$(SRC)),$(error Spaces in SRC = "$(SRC)" are not supported))
|
||||
MFEM_GIT_STRING = $(shell [ -d $(MFEM_DIR)/.git ] && git -C $(MFEM_DIR) \
|
||||
describe --all --long --abbrev=40 --dirty --always 2> /dev/null)
|
||||
|
||||
EXAMPLE_SUBDIRS = amgx ginkgo hiop petsc pumi sundials superlu
|
||||
EXAMPLE_SUBDIRS = amgx caliper ginkgo hiop petsc pumi sundials superlu
|
||||
EXAMPLE_DIRS := examples $(addprefix examples/,$(EXAMPLE_SUBDIRS))
|
||||
EXAMPLE_TEST_DIRS := examples
|
||||
|
||||
@@ -274,7 +274,7 @@ endif
|
||||
# List of MFEM dependencies, that require the *_LIB variable to be non-empty
|
||||
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS MESQUITE\
|
||||
SUITESPARSE STRUMPACK GINKGO GNUTLS NETCDF PETSC SLEPC MPFR PUMI HIOP GSLIB\
|
||||
OCCA CEED RAJA UMPIRE MKL_CPARDISO AMGX CALIPER
|
||||
OCCA CEED RAJA UMPIRE MKL_CPARDISO AMGX CALIPER ARPACK
|
||||
|
||||
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
|
||||
SLEPC_ERROR_MSG = $(if $(SLEPC_FOUND),,. SLEPC config not found: $(SLEPC_VARS))
|
||||
@@ -292,7 +292,7 @@ ifeq ($(MAKECMDGOALS),config)
|
||||
endif
|
||||
|
||||
# List of MFEM dependencies, processed below
|
||||
MFEM_DEPENDENCIES = $(MFEM_REQ_LIB_DEPS) LIBUNWIND OPENMP CUDA HIP
|
||||
MFEM_DEPENDENCIES = $(MFEM_REQ_LIB_DEPS) SPECTRA LIBUNWIND OPENMP CUDA HIP
|
||||
|
||||
# List of deprecated MFEM dependencies, processed below
|
||||
MFEM_LEGACY_DEPENDENCIES = OPENMP
|
||||
@@ -340,7 +340,7 @@ MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
|
||||
MFEM_USE_PUMI MFEM_USE_HIOP MFEM_USE_GSLIB MFEM_USE_CUDA MFEM_USE_HIP\
|
||||
MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_RAJA MFEM_USE_UMPIRE MFEM_USE_SIMD\
|
||||
MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_AMGX MFEM_USE_MUMPS\
|
||||
MFEM_USE_CALIPER MFEM_SOURCE_DIR MFEM_INSTALL_DIR
|
||||
MFEM_USE_CALIPER MFEM_USE_ARPACK MFEM_USE_SPECTRA MFEM_SOURCE_DIR MFEM_INSTALL_DIR
|
||||
|
||||
# List of makefile variables that will be written to config.mk:
|
||||
MFEM_CONFIG_VARS = MFEM_CXX MFEM_HOST_CXX MFEM_CPPFLAGS MFEM_CXXFLAGS\
|
||||
@@ -652,6 +652,8 @@ status info:
|
||||
$(info MFEM_USE_SUNDIALS = $(MFEM_USE_SUNDIALS))
|
||||
$(info MFEM_USE_MESQUITE = $(MFEM_USE_MESQUITE))
|
||||
$(info MFEM_USE_SUITESPARSE = $(MFEM_USE_SUITESPARSE))
|
||||
$(info MFEM_USE_ARPACK = $(MFEM_USE_ARPACK))
|
||||
$(info MFEM_USE_SPECTRA = $(MFEM_USE_SPECTRA))
|
||||
$(info MFEM_USE_SUPERLU = $(MFEM_USE_SUPERLU))
|
||||
$(info MFEM_USE_MUMPS = $(MFEM_USE_MUMPS))
|
||||
$(info MFEM_USE_STRUMPACK = $(MFEM_USE_STRUMPACK))
|
||||
|
||||
@@ -19,6 +19,7 @@ set(SRCS
|
||||
ncmesh.cpp
|
||||
nurbs.cpp
|
||||
point.cpp
|
||||
pyramid.cpp
|
||||
quadrilateral.cpp
|
||||
segment.cpp
|
||||
tetrahedron.cpp
|
||||
@@ -38,6 +39,7 @@ set(HDRS
|
||||
ncmesh.hpp
|
||||
nurbs.hpp
|
||||
point.hpp
|
||||
pyramid.hpp
|
||||
quadrilateral.hpp
|
||||
segment.hpp
|
||||
tetrahedron.hpp
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ public:
|
||||
|
||||
/// Constants for the classes derived from Element.
|
||||
enum Type { POINT, SEGMENT, TRIANGLE, QUADRILATERAL,
|
||||
TETRAHEDRON, HEXAHEDRON, WEDGE
|
||||
TETRAHEDRON, HEXAHEDRON, WEDGE, PYRAMID
|
||||
};
|
||||
|
||||
/// Default element constructor.
|
||||
|
||||
+311
-12
@@ -75,7 +75,7 @@ void Mesh::GetElementCenter(int i, Vector ¢er)
|
||||
|
||||
double Mesh::GetElementSize(ElementTransformation *T, int type)
|
||||
{
|
||||
DenseMatrix J(Dim);
|
||||
DenseMatrix J(spaceDim,Dim);
|
||||
|
||||
Geometry::Type geom = T->GetGeometryType();
|
||||
T->SetIntPoint(&Geometries.GetCenter(geom));
|
||||
@@ -83,7 +83,7 @@ double Mesh::GetElementSize(ElementTransformation *T, int type)
|
||||
|
||||
if (type == 0)
|
||||
{
|
||||
return pow(fabs(J.Det()), 1./Dim);
|
||||
return pow(fabs(J.Weight()), 1./Dim);
|
||||
}
|
||||
else if (type == 1)
|
||||
{
|
||||
@@ -102,7 +102,7 @@ double Mesh::GetElementSize(int i, int type)
|
||||
|
||||
double Mesh::GetElementSize(int i, const Vector &dir)
|
||||
{
|
||||
DenseMatrix J(Dim);
|
||||
DenseMatrix J(spaceDim,Dim);
|
||||
Vector d_hat(Dim);
|
||||
GetElementJacobian(i, J);
|
||||
J.MultTranspose(dir, d_hat);
|
||||
@@ -335,6 +335,7 @@ FiniteElement *Mesh::GetTransformationFEforElementType(Element::Type ElemType)
|
||||
case Element::TETRAHEDRON : return &TetrahedronFE;
|
||||
case Element::HEXAHEDRON : return &HexahedronFE;
|
||||
case Element::WEDGE : return &WedgeFE;
|
||||
case Element::PYRAMID : return &PyramidFE;
|
||||
default:
|
||||
MFEM_ABORT("Unknown element type \"" << ElemType << "\"");
|
||||
break;
|
||||
@@ -735,6 +736,31 @@ void Mesh::GetLocalTriToWdgTransformation(
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::GetLocalTriToPyrTransformation(
|
||||
IsoparametricTransformation &Transf, int i)
|
||||
{
|
||||
DenseMatrix &locpm = Transf.GetPointMat();
|
||||
|
||||
Transf.SetFE(&TriangleFE);
|
||||
// (i/64) is the local face no. in the pyr
|
||||
MFEM_VERIFY(i >= 64, "Local face index " << i/64
|
||||
<< " is not a triangular face of a pyramid.");
|
||||
const int *pv = pyr_t::FaceVert[i/64];
|
||||
// (i%64) is the orientation of the pyramid face
|
||||
// w.r.t. the face element
|
||||
const int *to = tri_t::Orient[i%64];
|
||||
const IntegrationRule *PyrVert =
|
||||
Geometries.GetVertices(Geometry::PYRAMID);
|
||||
locpm.SetSize(3, 3);
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
const IntegrationPoint &vert = PyrVert->IntPoint(pv[to[j]]);
|
||||
locpm(0, j) = vert.x;
|
||||
locpm(1, j) = vert.y;
|
||||
locpm(2, j) = vert.z;
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::GetLocalQuadToHexTransformation(
|
||||
IsoparametricTransformation &Transf, int i)
|
||||
{
|
||||
@@ -781,6 +807,29 @@ void Mesh::GetLocalQuadToWdgTransformation(
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::GetLocalQuadToPyrTransformation(
|
||||
IsoparametricTransformation &Transf, int i)
|
||||
{
|
||||
DenseMatrix &locpm = Transf.GetPointMat();
|
||||
|
||||
Transf.SetFE(&QuadrilateralFE);
|
||||
// (i/64) is the local face no. in the pyr
|
||||
MFEM_VERIFY(i < 64, "Local face index " << i/64
|
||||
<< " is not a quadrilateral face of a pyramid.");
|
||||
const int *pv = pyr_t::FaceVert[i/64];
|
||||
// (i%64) is the orientation of the quad
|
||||
const int *qo = quad_t::Orient[i%64];
|
||||
const IntegrationRule *PyrVert = Geometries.GetVertices(Geometry::PYRAMID);
|
||||
locpm.SetSize(3, 4);
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
const IntegrationPoint &vert = PyrVert->IntPoint(pv[qo[j]]);
|
||||
locpm(0, j) = vert.x;
|
||||
locpm(1, j) = vert.y;
|
||||
locpm(2, j) = vert.z;
|
||||
}
|
||||
}
|
||||
|
||||
const GeometricFactors* Mesh::GetGeometricFactors(const IntegrationRule& ir,
|
||||
const int flags,
|
||||
MemoryType d_mt)
|
||||
@@ -862,10 +911,19 @@ void Mesh::GetLocalFaceTransformation(
|
||||
{
|
||||
GetLocalTriToTetTransformation(Transf, info);
|
||||
}
|
||||
else if (elem_type == Element::WEDGE)
|
||||
{
|
||||
GetLocalTriToWdgTransformation(Transf, info);
|
||||
}
|
||||
else if (elem_type == Element::PYRAMID)
|
||||
{
|
||||
GetLocalTriToPyrTransformation(Transf, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(elem_type == Element::WEDGE, "");
|
||||
GetLocalTriToWdgTransformation(Transf, info);
|
||||
MFEM_ABORT("Mesh::GetLocalFaceTransformation not defined for "
|
||||
"face type " << face_type
|
||||
<< " and element type " << elem_type << "\n");
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -874,10 +932,19 @@ void Mesh::GetLocalFaceTransformation(
|
||||
{
|
||||
GetLocalQuadToHexTransformation(Transf, info);
|
||||
}
|
||||
else if (elem_type == Element::WEDGE)
|
||||
{
|
||||
GetLocalQuadToWdgTransformation(Transf, info);
|
||||
}
|
||||
else if (elem_type == Element::PYRAMID)
|
||||
{
|
||||
GetLocalQuadToPyrTransformation(Transf, info);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ASSERT(elem_type == Element::WEDGE, "");
|
||||
GetLocalQuadToWdgTransformation(Transf, info);
|
||||
MFEM_ABORT("Mesh::GetLocalFaceTransformation not defined for "
|
||||
"face type " << face_type
|
||||
<< " and element type " << elem_type << "\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1370,6 +1437,20 @@ int Mesh::AddWedge(const int *vi, int attr)
|
||||
return NumOfElements++;
|
||||
}
|
||||
|
||||
int Mesh::AddPyramid(int v1, int v2, int v3, int v4, int v5, int attr)
|
||||
{
|
||||
CheckEnlarge(elements, NumOfElements);
|
||||
elements[NumOfElements] = new Pyramid(v1, v2, v3, v4, v5, attr);
|
||||
return NumOfElements++;
|
||||
}
|
||||
|
||||
int Mesh::AddPyramid(const int *vi, int attr)
|
||||
{
|
||||
CheckEnlarge(elements, NumOfElements);
|
||||
elements[NumOfElements] = new Pyramid(vi, attr);
|
||||
return NumOfElements++;
|
||||
}
|
||||
|
||||
int Mesh::AddHex(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8,
|
||||
int attr)
|
||||
{
|
||||
@@ -1423,6 +1504,25 @@ void Mesh::AddHexAsWedges(const int *vi, int attr)
|
||||
}
|
||||
}
|
||||
|
||||
void Mesh::AddHexAsPyramids(const int *vi, int attr)
|
||||
{
|
||||
static const int hex_to_pyr[6][5] =
|
||||
{
|
||||
{ 0, 1, 2, 3, 8 }, { 0, 4, 5, 1, 8 }, { 1, 5, 6, 2, 8 },
|
||||
{ 2, 6, 7, 3, 8 }, { 3, 7, 4, 0, 8 }, { 7, 6, 5, 4, 8 }
|
||||
};
|
||||
int ti[5];
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
for (int j = 0; j < 5; j++)
|
||||
{
|
||||
ti[j] = vi[hex_to_pyr[i][j]];
|
||||
}
|
||||
AddPyramid(ti, attr);
|
||||
}
|
||||
}
|
||||
|
||||
int Mesh::AddElement(Element *elem)
|
||||
{
|
||||
CheckEnlarge(elements, NumOfElements);
|
||||
@@ -2692,11 +2792,16 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
|
||||
NElem *= 2;
|
||||
NBdrElem += 2*nx*ny;
|
||||
}
|
||||
else if (type == Element::PYRAMID)
|
||||
{
|
||||
NElem *= 6;
|
||||
NVert += nx * ny * nz;
|
||||
}
|
||||
|
||||
InitMesh(3, 3, NVert, NElem, NBdrElem);
|
||||
|
||||
double coord[3];
|
||||
int ind[8];
|
||||
int ind[9];
|
||||
|
||||
// Sets vertices and the corresponding coordinates
|
||||
for (z = 0; z <= nz; z++)
|
||||
@@ -2712,8 +2817,25 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type == Element::PYRAMID)
|
||||
{
|
||||
for (z = 0; z < nz; z++)
|
||||
{
|
||||
coord[2] = (((double) z + 0.5) / nz) * sz;
|
||||
for (y = 0; y < ny; y++)
|
||||
{
|
||||
coord[1] = (((double) y + 0.5 ) / ny) * sy;
|
||||
for (x = 0; x < nx; x++)
|
||||
{
|
||||
coord[0] = (((double) x + 0.5 ) / nx) * sx;
|
||||
AddVertex(coord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define VTX(XC, YC, ZC) ((XC)+((YC)+(ZC)*(ny+1))*(nx+1))
|
||||
#define VTXP(XC, YC, ZC) ((nx+1)*(ny+1)*(nz+1)+(XC)+((YC)+(ZC)*ny)*nx)
|
||||
|
||||
// Sets elements and the corresponding indices of vertices
|
||||
if (sfc_ordering && type == Element::HEXAHEDRON)
|
||||
@@ -2764,6 +2886,11 @@ void Mesh::Make3D(int nx, int ny, int nz, Element::Type type,
|
||||
{
|
||||
AddHexAsWedges(ind, 1);
|
||||
}
|
||||
else if (type == Element::PYRAMID)
|
||||
{
|
||||
ind[8] = VTXP( x, y, z);
|
||||
AddHexAsPyramids(ind, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddHex(ind, 1);
|
||||
@@ -3428,6 +3555,7 @@ Element *Mesh::NewElement(int geom)
|
||||
#endif
|
||||
case Geometry::CUBE: return (new Hexahedron);
|
||||
case Geometry::PRISM: return (new Wedge);
|
||||
case Geometry::PYRAMID: return (new Pyramid);
|
||||
default:
|
||||
MFEM_ABORT("invalid Geometry::Type, geom = " << geom);
|
||||
}
|
||||
@@ -3520,6 +3648,15 @@ void Mesh::SetMeshGen()
|
||||
meshgen |= 4;
|
||||
break;
|
||||
|
||||
case Element::PYRAMID:
|
||||
mesh_geoms |= (1 << Geometry::PYRAMID);
|
||||
mesh_geoms |= (1 << Geometry::SQUARE);
|
||||
mesh_geoms |= (1 << Geometry::TRIANGLE);
|
||||
mesh_geoms |= (1 << Geometry::SEGMENT);
|
||||
mesh_geoms |= (1 << Geometry::POINT);
|
||||
meshgen |= 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
MFEM_ABORT("invalid element type: " << type);
|
||||
break;
|
||||
@@ -3974,6 +4111,12 @@ void Mesh::MakeRefined_(Mesh &orig_mesh, const Array<int> ref_factors,
|
||||
}
|
||||
}
|
||||
|
||||
if (Dim > 2)
|
||||
{
|
||||
GetElementToFaceTable(false);
|
||||
GenerateFaces();
|
||||
}
|
||||
|
||||
// Add refined boundary elements
|
||||
for (int el = 0; el < orig_mesh.GetNBE(); el++)
|
||||
{
|
||||
@@ -5054,6 +5197,19 @@ int Mesh::CheckElementOrientation(bool fix_it)
|
||||
}
|
||||
break;
|
||||
|
||||
case Element::PYRAMID:
|
||||
// only check the Jacobian at the center of the element
|
||||
GetElementJacobian(i, J);
|
||||
if (J.Det() < 0.0)
|
||||
{
|
||||
wo++;
|
||||
if (fix_it)
|
||||
{
|
||||
// how?
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Element::HEXAHEDRON:
|
||||
// only check the Jacobian at the center of the element
|
||||
GetElementJacobian(i, J);
|
||||
@@ -6136,6 +6292,22 @@ void Mesh::GenerateFaces()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Element::PYRAMID:
|
||||
{
|
||||
for (int j = 0; j < 1; j++)
|
||||
{
|
||||
const int *fv = pyr_t::FaceVert[j];
|
||||
AddQuadFaceElement(j, ef[j], i,
|
||||
v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
|
||||
}
|
||||
for (int j = 1; j < 5; j++)
|
||||
{
|
||||
const int *fv = pyr_t::FaceVert[j];
|
||||
AddTriangleFaceElement(j, ef[j], i,
|
||||
v[fv[0]], v[fv[1]], v[fv[2]]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Element::HEXAHEDRON:
|
||||
{
|
||||
for (int j = 0; j < 6; j++)
|
||||
@@ -6228,6 +6400,20 @@ STable3D *Mesh::GetFacesTable()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Element::PYRAMID:
|
||||
{
|
||||
for (int j = 0; j < 1; j++)
|
||||
{
|
||||
const int *fv = pyr_t::FaceVert[j];
|
||||
faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]);
|
||||
}
|
||||
for (int j = 1; j < 5; j++)
|
||||
{
|
||||
const int *fv = pyr_t::FaceVert[j];
|
||||
faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Element::WEDGE:
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
@@ -6302,6 +6488,22 @@ STable3D *Mesh::GetElementToFaceTable(int ret_ftbl)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Element::PYRAMID:
|
||||
{
|
||||
for (int j = 0; j < 1; j++)
|
||||
{
|
||||
const int *fv = pyr_t::FaceVert[j];
|
||||
el_to_face->Push(
|
||||
i, faces_tbl->Push4(v[fv[0]], v[fv[1]], v[fv[2]], v[fv[3]]));
|
||||
}
|
||||
for (int j = 1; j < 5; j++)
|
||||
{
|
||||
const int *fv = pyr_t::FaceVert[j];
|
||||
el_to_face->Push(
|
||||
i, faces_tbl->Push(v[fv[0]], v[fv[1]], v[fv[2]]));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Element::HEXAHEDRON:
|
||||
{
|
||||
// find the face by the vertices with the smallest 3 numbers
|
||||
@@ -7638,8 +7840,22 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
}
|
||||
}
|
||||
|
||||
int pyr_counter = 0;
|
||||
if (HasGeometry(Geometry::PYRAMID))
|
||||
{
|
||||
for (int i = 0; i < elements.Size(); i++)
|
||||
{
|
||||
if (elements[i]->GetType() == Element::PYRAMID)
|
||||
{
|
||||
pyr_counter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Map from edge-index to vertex-index, needed for ReorientTetMesh() for
|
||||
// parallel meshes.
|
||||
// Note: with the removal of ReorientTetMesh() this may no longer
|
||||
// be needed. Unfortunately, it's hard to be sure.
|
||||
Array<int> e2v;
|
||||
if (HasGeometry(Geometry::TETRAHEDRON))
|
||||
{
|
||||
@@ -7695,7 +7911,7 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
Array<Element*> new_boundary;
|
||||
|
||||
vertices.SetSize(oelem + hex_counter);
|
||||
new_elements.SetSize(8 * NumOfElements);
|
||||
new_elements.SetSize(8 * NumOfElements + 2 * pyr_counter);
|
||||
CoarseFineTr.embeddings.SetSize(new_elements.Size());
|
||||
|
||||
hex_counter = 0;
|
||||
@@ -7961,6 +8177,73 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
}
|
||||
break;
|
||||
|
||||
case Element::PYRAMID:
|
||||
{
|
||||
const int *f = el_to_face->GetRow(i);
|
||||
// pyr_counter++;
|
||||
|
||||
for (int fi = 0; fi < 1; fi++)
|
||||
{
|
||||
for (int k = 0; k < 4; k++)
|
||||
{
|
||||
vv[k] = v[pyr_t::FaceVert[fi][k]];
|
||||
}
|
||||
AverageVertices(vv, 4, oface + f2qf[f[fi]]);
|
||||
}
|
||||
|
||||
for (int ei = 0; ei < 8; ei++)
|
||||
{
|
||||
for (int k = 0; k < 2; k++)
|
||||
{
|
||||
vv[k] = v[pyr_t::Edges[ei][k]];
|
||||
}
|
||||
AverageVertices(vv, 2, oedge+e[ei]);
|
||||
}
|
||||
|
||||
const int qf0 = f2qf[f[0]];
|
||||
|
||||
new_elements[j++] =
|
||||
new Pyramid(v[0], oedge+e[0], oface+qf0,
|
||||
oedge+e[3], oedge+e[4], attr);
|
||||
|
||||
new_elements[j++] =
|
||||
new Pyramid(oedge+e[0], v[1], oedge+e[1],
|
||||
oface+qf0, oedge+e[5], attr);
|
||||
|
||||
new_elements[j++] =
|
||||
new Pyramid(oface+qf0, oedge+e[1], v[2],
|
||||
oedge+e[2], oedge+e[6], attr);
|
||||
|
||||
new_elements[j++] =
|
||||
new Pyramid(oedge+e[3], oface+qf0, oedge+e[2],
|
||||
v[3], oedge+e[7], attr);
|
||||
|
||||
new_elements[j++] =
|
||||
new Pyramid(oedge+e[4], oedge+e[5], oedge+e[6],
|
||||
oedge+e[7], v[4], attr);
|
||||
|
||||
new_elements[j++] =
|
||||
new Pyramid(oedge+e[7], oedge+e[6], oedge+e[5],
|
||||
oedge+e[4], oface+qf0, attr);
|
||||
|
||||
new_elements[j++] =
|
||||
new Tetrahedron(oedge+e[0], oedge+e[4], oedge+e[5],
|
||||
oface+qf0, attr);
|
||||
|
||||
new_elements[j++] =
|
||||
new Tetrahedron(oedge+e[1], oedge+e[5], oedge+e[6],
|
||||
oface+qf0, attr);
|
||||
|
||||
new_elements[j++] =
|
||||
new Tetrahedron(oedge+e[2], oedge+e[6], oedge+e[7],
|
||||
oface+qf0, attr);
|
||||
|
||||
new_elements[j++] =
|
||||
new Tetrahedron(oedge+e[3], oedge+e[7], oedge+e[4],
|
||||
oface+qf0, attr);
|
||||
}
|
||||
break;
|
||||
|
||||
case Element::HEXAHEDRON:
|
||||
{
|
||||
const int *f = el_to_face->GetRow(i);
|
||||
@@ -8092,7 +8375,7 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
}
|
||||
mfem::Swap(boundary, new_boundary);
|
||||
|
||||
static const double A = 0.0, B = 0.5, C = 1.0;
|
||||
static const double A = 0.0, B = 0.5, C = 1.0, D = -1.0;
|
||||
static double tet_children[3*4*16] =
|
||||
{
|
||||
A,A,A, B,A,A, A,B,A, A,A,B,
|
||||
@@ -8118,6 +8401,19 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
A,A,B, A,B,B, B,A,B, B,B,A,
|
||||
A,A,B, B,A,B, B,A,A, B,B,A
|
||||
};
|
||||
static double pyr_children[3*5*10] =
|
||||
{
|
||||
A,A,A, B,A,A, B,B,A, A,B,A, A,A,B,
|
||||
B,A,A, C,A,A, C,B,A, B,B,A, B,A,B,
|
||||
B,B,A, C,B,A, C,C,A, B,C,A, B,B,B,
|
||||
A,B,A, B,B,A, B,C,A, A,C,A, A,B,B,
|
||||
A,A,B, B,A,B, B,B,B, A,B,B, A,A,C,
|
||||
A,B,B, B,B,B, B,A,B, A,A,B, B,B,A,
|
||||
B,A,A, A,A,B, B,A,B, B,B,A, D,D,D,
|
||||
C,B,A, B,A,B, B,B,B, B,B,A, D,D,D,
|
||||
B,C,A, B,B,B, A,B,B, B,B,A, D,D,D,
|
||||
A,B,A, A,B,B, A,A,B, B,B,A, D,D,D
|
||||
};
|
||||
static double pri_children[3*6*8] =
|
||||
{
|
||||
A,A,A, B,A,A, A,B,A, A,A,B, B,A,B, A,B,B,
|
||||
@@ -8143,6 +8439,8 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
|
||||
CoarseFineTr.point_matrices[Geometry::TETRAHEDRON]
|
||||
.UseExternalData(tet_children, 3, 4, 16);
|
||||
CoarseFineTr.point_matrices[Geometry::PYRAMID]
|
||||
.UseExternalData(pyr_children, 3, 5, 10);
|
||||
CoarseFineTr.point_matrices[Geometry::PRISM]
|
||||
.UseExternalData(pri_children, 3, 6, 8);
|
||||
CoarseFineTr.point_matrices[Geometry::CUBE]
|
||||
@@ -8159,7 +8457,7 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
|
||||
}
|
||||
|
||||
NumOfVertices = vertices.Size();
|
||||
NumOfElements = 8 * NumOfElements;
|
||||
NumOfElements = 8 * NumOfElements + 2 * pyr_counter;
|
||||
NumOfBdrElements = 4 * NumOfBdrElements;
|
||||
|
||||
GetElementToFaceTable();
|
||||
@@ -8743,7 +9041,7 @@ void Mesh::GeneralRefinement(const Array<Refinement> &refinements,
|
||||
else if (nonconforming < 0)
|
||||
{
|
||||
// determine if nonconforming refinement is suitable
|
||||
if ((meshgen & 2) || (meshgen & 4))
|
||||
if ((meshgen & 2) || (meshgen & 4) || (meshgen & 8))
|
||||
{
|
||||
nonconforming = 1; // tensor product elements and wedges
|
||||
}
|
||||
@@ -9527,6 +9825,7 @@ void Mesh::Printer(std::ostream &out, std::string section_delimiter) const
|
||||
"# TETRAHEDRON = 4\n"
|
||||
"# CUBE = 5\n"
|
||||
"# PRISM = 6\n"
|
||||
"# PYRAMID = 7\n"
|
||||
"#\n";
|
||||
|
||||
out << "\ndimension\n" << Dim;
|
||||
|
||||
+20
-2
@@ -177,6 +177,7 @@ protected:
|
||||
int own_nodes;
|
||||
|
||||
static const int vtk_quadratic_tet[10];
|
||||
static const int vtk_quadratic_pyramid[13];
|
||||
static const int vtk_quadratic_wedge[18];
|
||||
static const int vtk_quadratic_hex[27];
|
||||
|
||||
@@ -195,6 +196,7 @@ public:
|
||||
typedef Geometry::Constants<Geometry::TETRAHEDRON> tet_t;
|
||||
typedef Geometry::Constants<Geometry::CUBE> hex_t;
|
||||
typedef Geometry::Constants<Geometry::PRISM> pri_t;
|
||||
typedef Geometry::Constants<Geometry::PYRAMID> pyr_t;
|
||||
|
||||
enum Operation { NONE, REFINE, DEREFINE, REBALANCE };
|
||||
|
||||
@@ -373,11 +375,17 @@ protected:
|
||||
void GetLocalTriToWdgTransformation (IsoparametricTransformation &loc,
|
||||
int i);
|
||||
/// Used in GetFaceElementTransformations (...)
|
||||
void GetLocalTriToPyrTransformation (IsoparametricTransformation &loc,
|
||||
int i);
|
||||
/// Used in GetFaceElementTransformations (...)
|
||||
void GetLocalQuadToHexTransformation (IsoparametricTransformation &loc,
|
||||
int i);
|
||||
/// Used in GetFaceElementTransformations (...)
|
||||
void GetLocalQuadToWdgTransformation (IsoparametricTransformation &loc,
|
||||
int i);
|
||||
/// Used in GetFaceElementTransformations (...)
|
||||
void GetLocalQuadToPyrTransformation (IsoparametricTransformation &loc,
|
||||
int i);
|
||||
|
||||
/** Used in GetFaceElementTransformations to account for the fact that a
|
||||
slave face occupies only a portion of its master face. */
|
||||
@@ -503,7 +511,7 @@ public:
|
||||
Mesh& operator=(Mesh &&mesh);
|
||||
|
||||
/// Explicitly delete the copy assignment operator.
|
||||
Mesh& operator=(Mesh &mesh) = delete;
|
||||
Mesh& operator=(const Mesh &mesh) = delete;
|
||||
|
||||
/** @name Named mesh constructors.
|
||||
|
||||
@@ -657,11 +665,15 @@ public:
|
||||
int AddWedge(int v1, int v2, int v3, int v4, int v5, int v6, int attr = 1);
|
||||
int AddWedge(const int *vi, int attr = 1);
|
||||
|
||||
int AddPyramid(int v1, int v2, int v3, int v4, int v5, int attr = 1);
|
||||
int AddPyramid(const int *vi, int attr = 1);
|
||||
|
||||
int AddHex(int v1, int v2, int v3, int v4, int v5, int v6, int v7, int v8,
|
||||
int attr = 1);
|
||||
int AddHex(const int *vi, int attr = 1);
|
||||
void AddHexAsTets(const int *vi, int attr = 1);
|
||||
void AddHexAsWedges(const int *vi, int attr = 1);
|
||||
void AddHexAsPyramids(const int *vi, int attr = 1);
|
||||
|
||||
/// The parameter @a elem should be allocated using the NewElement() method
|
||||
int AddElement(Element *elem);
|
||||
@@ -829,10 +841,16 @@ public:
|
||||
|
||||
/** @brief Get the mesh generator/type.
|
||||
|
||||
The purpose of this is to be able to quickly tell what type of elements
|
||||
one has in the mesh. Examination of this bitmask along with knowledge
|
||||
of the mesh dimension can be used to identify which element types are
|
||||
present.
|
||||
|
||||
@return A bitmask:
|
||||
- bit 0 - simplices are present in the mesh (triangles, tets),
|
||||
- bit 1 - tensor product elements are present in the mesh (quads, hexes),
|
||||
- bit 2 - the mesh has wedge elements.
|
||||
- bit 3 - the mesh has pyramid elements.
|
||||
|
||||
In parallel, the result takes into account elements on all processors.
|
||||
*/
|
||||
@@ -1227,7 +1245,7 @@ public:
|
||||
satisfy: v0 < min(v1, v2).
|
||||
|
||||
@note Refinement does not work after a call to this method! */
|
||||
virtual void ReorientTetMesh();
|
||||
MFEM_DEPRECATED virtual void ReorientTetMesh();
|
||||
|
||||
int *CartesianPartitioning(int nxyz[]);
|
||||
int *GeneratePartitioning(int nparts, int part_method = 1);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "mesh_operators.hpp"
|
||||
#include "nurbs.hpp"
|
||||
#include "wedge.hpp"
|
||||
#include "pyramid.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MESQUITE
|
||||
#include "mesquite.hpp"
|
||||
|
||||
+61
-21
@@ -352,6 +352,11 @@ void Mesh::ReadTrueGridMesh(std::istream &input)
|
||||
const int Mesh::vtk_quadratic_tet[10] =
|
||||
{ 0, 1, 2, 3, 4, 7, 5, 6, 8, 9 };
|
||||
|
||||
// see Pyramid::edges & Mesh::GenerateFaces
|
||||
// https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html
|
||||
const int Mesh::vtk_quadratic_pyramid[13] =
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
|
||||
|
||||
// see Wedge::edges & Mesh::GenerateFaces
|
||||
// https://www.vtk.org/doc/nightly/html/classvtkBiQuadraticQuadraticWedge.html
|
||||
const int Mesh::vtk_quadratic_wedge[18] =
|
||||
@@ -514,8 +519,7 @@ void Mesh::CreateVTKMesh(const Vector &points, const Array<int> &cell_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
// Generate faces and edges so that we can define
|
||||
// FE space on the mesh
|
||||
// Generate faces and edges so that we can define FE space on the mesh
|
||||
FinalizeTopology();
|
||||
|
||||
FiniteElementCollection *fec;
|
||||
@@ -546,6 +550,8 @@ void Mesh::CreateVTKMesh(const Vector &points, const Array<int> &cell_data,
|
||||
vtk_mfem = vtk_quadratic_hex; break;
|
||||
case Geometry::PRISM:
|
||||
vtk_mfem = vtk_quadratic_wedge; break;
|
||||
case Geometry::PYRAMID:
|
||||
vtk_mfem = vtk_quadratic_pyramid; break;
|
||||
default:
|
||||
vtk_mfem = NULL; // suppress a warning
|
||||
break;
|
||||
@@ -1394,6 +1400,10 @@ void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
|
||||
{
|
||||
type = Element::WEDGE;
|
||||
}
|
||||
else if (eltype == "pyramid")
|
||||
{
|
||||
type = Element::PYRAMID;
|
||||
}
|
||||
else if (eltype == "tet")
|
||||
{
|
||||
type = Element::TETRAHEDRON;
|
||||
@@ -1448,7 +1458,7 @@ void Mesh::ReadInlineMesh(std::istream &input, bool generate_edges)
|
||||
Make2D(nx, ny, type, sx, sy, generate_edges, true);
|
||||
}
|
||||
else if (type == Element::TETRAHEDRON || type == Element::WEDGE ||
|
||||
type == Element::HEXAHEDRON)
|
||||
type == Element::HEXAHEDRON || type == Element::PYRAMID)
|
||||
{
|
||||
MFEM_VERIFY(nx > 0 && ny > 0 && nz > 0 &&
|
||||
sx > 0.0 && sy > 0.0 && sz > 0.0,
|
||||
@@ -1885,6 +1895,9 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
|
||||
ho_wdg[2] = wdg18; ho_wdg[3] = wdg40;
|
||||
ho_pyr[2] = pyr14; ho_pyr[3] = pyr30;
|
||||
|
||||
bool has_nonpositive_phys_domain = false;
|
||||
bool has_positive_phys_domain = false;
|
||||
|
||||
if (binary)
|
||||
{
|
||||
int n_elem_part = 0; // partial sum of elements that are read
|
||||
@@ -1935,17 +1948,19 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
|
||||
vert_indices[vi] = it->second;
|
||||
}
|
||||
|
||||
// non-positive attributes are not allowed in MFEM
|
||||
// Non-positive attributes are not allowed in MFEM. However,
|
||||
// by default, Gmsh sets the physical domain of all elements
|
||||
// to zero. In the case that all elements have physical domain
|
||||
// zero, we will given them attribute 1. If only some elements
|
||||
// have physical domain zero, we will throw an error.
|
||||
if (phys_domain <= 0)
|
||||
{
|
||||
MFEM_ABORT("Non-positive element attribute in Gmsh mesh!\n"
|
||||
"By default Gmsh sets element tags (attributes)"
|
||||
" to '0' but MFEM requires that they be"
|
||||
" positive integers.\n"
|
||||
"Use \"Physical Curve\", \"Physical Surface\","
|
||||
" or \"Physical Volume\" to set tags/attributes"
|
||||
" for all curves, surfaces, or volumes in your"
|
||||
" Gmsh geometry to values which are >= 1.");
|
||||
has_nonpositive_phys_domain = true;
|
||||
phys_domain = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
has_positive_phys_domain = true;
|
||||
}
|
||||
|
||||
// initialize the mesh element
|
||||
@@ -2162,17 +2177,19 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
|
||||
vert_indices[vi] = it->second;
|
||||
}
|
||||
|
||||
// non-positive attributes are not allowed in MFEM
|
||||
// Non-positive attributes are not allowed in MFEM. However,
|
||||
// by default, Gmsh sets the physical domain of all elements
|
||||
// to zero. In the case that all elements have physical domain
|
||||
// zero, we will given them attribute 1. If only some elements
|
||||
// have physical domain zero, we will throw an error.
|
||||
if (phys_domain <= 0)
|
||||
{
|
||||
MFEM_ABORT("Non-positive element attribute in Gmsh mesh!\n"
|
||||
"By default Gmsh sets element tags (attributes)"
|
||||
" to '0' but MFEM requires that they be"
|
||||
" positive integers.\n"
|
||||
"Use \"Physical Curve\", \"Physical Surface\","
|
||||
" or \"Physical Volume\" to set tags/attributes"
|
||||
" for all curves, surfaces, or volumes in your"
|
||||
" Gmsh geometry to values which are >= 1.");
|
||||
has_nonpositive_phys_domain = true;
|
||||
phys_domain = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
has_positive_phys_domain = true;
|
||||
}
|
||||
|
||||
// initialize the mesh element
|
||||
@@ -2357,6 +2374,24 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
|
||||
} // el (all elements)
|
||||
} // if ASCII
|
||||
|
||||
if (has_positive_phys_domain && has_nonpositive_phys_domain)
|
||||
{
|
||||
MFEM_ABORT("Non-positive element attribute in Gmsh mesh!\n"
|
||||
"By default Gmsh sets element tags (attributes)"
|
||||
" to '0' but MFEM requires that they be"
|
||||
" positive integers.\n"
|
||||
"Use \"Physical Curve\", \"Physical Surface\","
|
||||
" or \"Physical Volume\" to set tags/attributes"
|
||||
" for all curves, surfaces, or volumes in your"
|
||||
" Gmsh geometry to values which are >= 1.");
|
||||
}
|
||||
else if (has_nonpositive_phys_domain)
|
||||
{
|
||||
mfem::out << "\nGmsh reader: all element attributes were zero.\n"
|
||||
<< "MFEM only supports positive element attributes.\n"
|
||||
<< "Setting element attributes to 1.\n\n";
|
||||
}
|
||||
|
||||
if (!elements_3D.empty())
|
||||
{
|
||||
Dim = 3;
|
||||
@@ -2445,6 +2480,10 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
|
||||
// initialize mesh_geoms so we can create Nodes FE space below
|
||||
this->SetMeshGen();
|
||||
|
||||
// Generate faces and edges so that we can define
|
||||
// FE space on the mesh
|
||||
this->FinalizeTopology();
|
||||
|
||||
// Construct GridFunction for uniformly spaced high order coords
|
||||
FiniteElementCollection* nfec;
|
||||
FiniteElementSpace* nfes;
|
||||
@@ -2666,6 +2705,7 @@ void Mesh::ReadGmshMesh(std::istream &input, int &curved, int &read_gf)
|
||||
// Convert nodes to discontinuous GridFunction (if they aren't already)
|
||||
if (mesh_order == 1)
|
||||
{
|
||||
this->FinalizeTopology();
|
||||
this->SetMeshGen();
|
||||
this->SetCurvature(1, true, spaceDim, Ordering::byVDIM);
|
||||
}
|
||||
|
||||
@@ -2283,7 +2283,6 @@ void NCMesh::GetMeshComponents(Mesh &mesh) const
|
||||
// left uninitialized here; they will be initialized later by the Mesh from
|
||||
// Nodes -- here we just make sure mesh.vertices has the correct size.
|
||||
|
||||
mesh.elements.SetSize(NElements);
|
||||
mesh.elements.SetSize(0);
|
||||
|
||||
mesh.boundary.SetSize(0);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user