Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
934f2b94e0 | ||
|
|
302be130d1 | ||
|
|
9d7696d803 | ||
|
|
1e0bb496e8 | ||
|
|
81ee483d2a | ||
|
|
6eb86d9fbb | ||
|
|
59f8bb30ef | ||
|
|
561b172a8b | ||
|
|
b8547cb430 | ||
|
|
caf98db468 | ||
|
|
20cd0b2a9f | ||
|
|
a2bbf76926 | ||
|
|
109baa5448 | ||
|
|
e6fb9d0ff9 | ||
|
|
6a167c938f | ||
|
|
a24370a2ed | ||
|
|
9a28ca5e6b | ||
|
|
5a1d69837d | ||
|
|
7876439a7d | ||
|
|
64907e2ab1 | ||
|
|
ee3f9fe97b | ||
|
|
564e2ff58e | ||
|
|
6105a4e1fa | ||
|
|
759ca48add | ||
|
|
c214533826 | ||
|
|
199f3616b6 | ||
|
|
3b4825675b | ||
|
|
8b8abfb611 | ||
|
|
8989feda0d | ||
|
|
784b74e1b3 | ||
|
|
cea760b1e6 | ||
|
|
4b6e48e915 | ||
|
|
73efadce18 | ||
|
|
ba9de3252b | ||
|
|
9dc7331d9c | ||
|
|
d5d2cbba19 | ||
|
|
db92a8950b | ||
|
|
2464a9d1ca | ||
|
|
f6db201a39 | ||
|
|
def4df1313 | ||
|
|
c3ac1e2222 | ||
|
|
031f8ad04a | ||
|
|
2cb4f5fd65 | ||
|
|
9f708051da | ||
|
|
3a64973722 | ||
|
|
6cedf73dd5 | ||
|
|
bd6c2ab61a | ||
|
|
1c90b1fd4f | ||
|
|
41cc480ba8 | ||
|
|
b1fc0a039f | ||
|
|
a714c4e881 | ||
|
|
8d189be921 | ||
|
|
82b0035ba2 | ||
|
|
a8db9de5a3 | ||
|
|
c086d8362d | ||
|
|
cf78d5d9f5 | ||
|
|
447d2ccd52 | ||
|
|
323cd8a2ae | ||
|
|
cbb95d2a8e | ||
|
|
1767d82a92 | ||
|
|
4c33502069 | ||
|
|
a74eed1083 | ||
|
|
3de18c65ab | ||
|
|
8451125d53 | ||
|
|
d9c65990d2 | ||
|
|
c1a796c60f | ||
|
|
d7d0e802a0 | ||
|
|
dee95c1d72 | ||
|
|
5099277778 | ||
|
|
3a9a131967 | ||
|
|
4491140db5 | ||
|
|
17955e1140 | ||
|
|
89eb2213fc | ||
|
|
f4675033a9 | ||
|
|
1662cbb045 | ||
|
|
d703c8c7a4 | ||
|
|
2bac83dc34 | ||
|
|
e3c0420c09 | ||
|
|
e46a3dc294 | ||
|
|
7f2ee0b7c9 | ||
|
|
324a86d4e7 | ||
|
|
d7c3190a68 | ||
|
|
d98eb5b5a0 | ||
|
|
14e5114039 | ||
|
|
235ab39d4a | ||
|
|
55efebfa00 | ||
|
|
fc13ff8f6c | ||
|
|
08cf03fc7f | ||
|
|
3bb7ea788f | ||
|
|
f1146c13b9 | ||
|
|
81fc677483 | ||
|
|
28f9bc4dd7 | ||
|
|
1886e25382 | ||
|
|
e5cd8378dc | ||
|
|
be35149113 | ||
|
|
808560ba72 | ||
|
|
30164b2db9 | ||
|
|
feeebaacc1 | ||
|
|
ad46b5217f | ||
|
|
854b2c5ed8 | ||
|
|
de70ec68cf | ||
|
|
7c37e06cd5 | ||
|
|
0cdfebca48 | ||
|
|
78ddcfee78 | ||
|
|
a92dc14745 | ||
|
|
718204a3c3 | ||
|
|
569a756b11 | ||
|
|
56381a3706 | ||
|
|
645bf01441 | ||
|
|
4eaa589c1f | ||
|
|
6205ec2b12 | ||
|
|
027681ff54 | ||
|
|
4acd812daa | ||
|
|
a1024d3645 | ||
|
|
6df6f1f199 | ||
|
|
29d97f356e | ||
|
|
e91f98c2c1 | ||
|
|
af9695d8f2 | ||
|
|
f2f0b17d91 | ||
|
|
36f15e5784 | ||
|
|
db1cb03562 | ||
|
|
b0cfba73ff | ||
|
|
8ebcd5b6d3 | ||
|
|
5c1d4f24a3 | ||
|
|
cf4e79b0e5 | ||
|
|
d8a9621dd5 | ||
|
|
0c42c16022 | ||
|
|
81f936aad5 | ||
|
|
5faf1e83bb | ||
|
|
b0acea4560 | ||
|
|
829af3b6d4 | ||
|
|
dc142f33b4 | ||
|
|
b9c143a3fe | ||
|
|
e15a3fabfc | ||
|
|
7249c9dd22 | ||
|
|
bb1f48cc06 | ||
|
|
bc13b805da | ||
|
|
b42f27fe48 | ||
|
|
ee59eb5cc0 | ||
|
|
6672424982 | ||
|
|
449ae5e8c3 | ||
|
|
7f962b0ee6 | ||
|
|
f0ca6327cc | ||
|
|
82b0b3c9e5 | ||
|
|
a130d17bc9 | ||
|
|
445e41bb77 | ||
|
|
6c17447045 | ||
|
|
ada0e37801 | ||
|
|
4cf617347f | ||
|
|
a854669092 | ||
|
|
77f2bcb3af | ||
|
|
e58ec7d6e9 | ||
|
|
7b5497868d | ||
|
|
f5e5f62194 | ||
|
|
8d6557fa59 | ||
|
|
2c7f088dc7 | ||
|
|
22a7948a1b | ||
|
|
2aa283d3ec | ||
|
|
a6d067bc86 | ||
|
|
9ddb323911 | ||
|
|
3fba6c6c64 | ||
|
|
2bbf2a757f | ||
|
|
970521a17a | ||
|
|
a42ef2644e | ||
|
|
4f69b9bca4 | ||
|
|
c9a11a7a5b | ||
|
|
5d6d96b5b1 | ||
|
|
676a94dcc5 | ||
|
|
ecf167ca37 | ||
|
|
ea8fd50995 | ||
|
|
24ed3a7fe2 | ||
|
|
3cedb47e7c | ||
|
|
c24baff0b9 | ||
|
|
8bdd414658 | ||
|
|
d4b0014407 | ||
|
|
e822520d5d | ||
|
|
4c952181c9 | ||
|
|
6d4f7570ad | ||
|
|
5d27d4c77f | ||
|
|
e8a7f2136a | ||
|
|
0432b1c47c | ||
|
|
167b591d01 | ||
|
|
75452f6e30 | ||
|
|
58bd809f87 | ||
|
|
708a8d92f8 | ||
|
|
f3add08ae3 | ||
|
|
96860ce2a6 | ||
|
|
cf530e9029 | ||
|
|
2f6871e449 | ||
|
|
8a2ef8aa36 | ||
|
|
f0bc536820 | ||
|
|
f052af3d6b | ||
|
|
a3be873907 | ||
|
|
d0c358ab48 | ||
|
|
982f7a1729 | ||
|
|
96ef25817c | ||
|
|
7f0e9c8801 | ||
|
|
f96f8af545 | ||
|
|
4edd730ce7 | ||
|
|
06955c17d9 | ||
|
|
06a8b35570 | ||
|
|
92ff8c744f | ||
|
|
c269b1ea54 | ||
|
|
cc70734bc0 | ||
|
|
013eace8a6 | ||
|
|
8237b9212d | ||
|
|
009837f4c8 | ||
|
|
b991cb755e | ||
|
|
5e3359a805 | ||
|
|
df0c066e43 | ||
|
|
8d01267e28 | ||
|
|
8130e46a06 | ||
|
|
b82b7dc1a2 | ||
|
|
bb055a83c1 | ||
|
|
93225fa096 | ||
|
|
95c7b8b692 | ||
|
|
dddfd64c04 | ||
|
|
3a03d25d8a | ||
|
|
644e0540b1 | ||
|
|
4cbc97ae6b | ||
|
|
00cccf157f | ||
|
|
da8efca267 | ||
|
|
347d5f3861 | ||
|
|
4a022b3393 | ||
|
|
db8304e311 | ||
|
|
cf49036582 | ||
|
|
69b9e48cd6 | ||
|
|
c786caef28 | ||
|
|
4abe31b237 | ||
|
|
2c723d8a21 | ||
|
|
9a742299b5 | ||
|
|
2b2b141281 | ||
|
|
e554d15416 | ||
|
|
1e19cc58d4 | ||
|
|
937390c63b | ||
|
|
74f8617a9e | ||
|
|
77ee552475 | ||
|
|
efa4308b0c | ||
|
|
84caff9dd6 | ||
|
|
0d663eb13f | ||
|
|
c1261461c5 | ||
|
|
256e513b02 | ||
|
|
9bf8f03d47 | ||
|
|
45716da3e8 | ||
|
|
093be3733b | ||
|
|
7c6e4c48ae | ||
|
|
ac36c05f4b | ||
|
|
065c7dfd2b | ||
|
|
e8d76b6887 | ||
|
|
f69981012a | ||
|
|
2ebbe59aee | ||
|
|
bd7218a2e4 | ||
|
|
8726340bc1 | ||
|
|
f0de5ade98 | ||
|
|
77b88173b4 | ||
|
|
bff6755a36 | ||
|
|
b914196334 | ||
|
|
1b484707ab | ||
|
|
72a520df8b | ||
|
|
e44d7cdea7 | ||
|
|
423edcc540 | ||
|
|
9e71343031 | ||
|
|
02194f6ecb | ||
|
|
09b0b3eacc | ||
|
|
8bbf7a87c2 | ||
|
|
432d290af5 | ||
|
|
a2ffe892c8 | ||
|
|
ab8e18be26 | ||
|
|
84b49f507a | ||
|
|
7aa36f82d9 | ||
|
|
5032ec9d7d | ||
|
|
1c655cbdc5 | ||
|
|
170ebdc372 | ||
|
|
b2c45912f7 | ||
|
|
b758c68930 | ||
|
|
5067891393 | ||
|
|
297877bbef | ||
|
|
f0e9b0b55f | ||
|
|
69bfdf44c7 | ||
|
|
acd07bc6bd | ||
|
|
6a21cb7eba | ||
|
|
8d78dcaf9e | ||
|
|
a69e1c69ef | ||
|
|
4bafc17358 | ||
|
|
dfcb153e52 | ||
|
|
eea4386fcf | ||
|
|
ebc27b9511 | ||
|
|
da07b29b6d | ||
|
|
0942a818ca | ||
|
|
959b07ce39 | ||
|
|
ddb518df90 | ||
|
|
84575c6423 | ||
|
|
5e6727225b | ||
|
|
5c8dae8e8e | ||
|
|
aa9886365f | ||
|
|
38e48d89b1 | ||
|
|
64e2b26824 | ||
|
|
3137379e1d | ||
|
|
901a85abf2 | ||
|
|
aaf861460e | ||
|
|
19500f3c0b | ||
|
|
3f359156ed | ||
|
|
67372245c4 | ||
|
|
074f28ccba | ||
|
|
76cbcfd3be | ||
|
|
48a170c57d | ||
|
|
782b0266f2 | ||
|
|
7bd7f08fe9 | ||
|
|
c2dc013de5 | ||
|
|
208f36f354 | ||
|
|
3a4fe0bfe7 | ||
|
|
4230779792 | ||
|
|
7a4bd3fb51 | ||
|
|
9e0f9cabc0 | ||
|
|
b8a5c6e1a6 | ||
|
|
d168ea86ec | ||
|
|
60b4414fd9 | ||
|
|
59ec6ace17 | ||
|
|
521b044d61 | ||
|
|
852da98c50 | ||
|
|
03904a8bb0 | ||
|
|
3cf792a66b | ||
|
|
6bdaa12c88 | ||
|
|
6787428ae8 | ||
|
|
68a9a070a5 | ||
|
|
12927b36f0 | ||
|
|
43533b51d5 | ||
|
|
cfbedc494a | ||
|
|
1b1d9ca272 | ||
|
|
b48574391e | ||
|
|
cd6919ce29 | ||
|
|
15f7f9dd1a | ||
|
|
2305d501db | ||
|
|
e125207781 | ||
|
|
8701290a72 | ||
|
|
35e3930fd7 | ||
|
|
f5cf13076b | ||
|
|
2bb6c55d66 | ||
|
|
08085843db | ||
|
|
3123099784 | ||
|
|
4e3df5a144 | ||
|
|
6baa0e29db | ||
|
|
612b7aa4f7 | ||
|
|
80702006f2 | ||
|
|
6ac7f61224 | ||
|
|
098063f588 | ||
|
|
c76b58fb12 | ||
|
|
4849471dc2 | ||
|
|
4c57ba72a6 | ||
|
|
0a44db4850 | ||
|
|
04691f4c16 | ||
|
|
3e1aec8bf8 | ||
|
|
ab71be1272 | ||
|
|
d7352fbfd4 | ||
|
|
ab7028ed7e | ||
|
|
793dd5e4c2 | ||
|
|
3920872a08 | ||
|
|
94fd002fab | ||
|
|
282e5b1a3d | ||
|
|
f49f007eda | ||
|
|
a2943f213f | ||
|
|
9c35bfbfda | ||
|
|
e488fc158d | ||
|
|
1d194da30d | ||
|
|
77d3c64a0d | ||
|
|
cd0ef98f79 | ||
|
|
fcb853fd6c | ||
|
|
5416209e9d | ||
|
|
448a395e5b | ||
|
|
baacaa9726 | ||
|
|
6fa0fb9efd | ||
|
|
f5580dd673 | ||
|
|
4d7c1c959f | ||
|
|
3c4e2a65a6 | ||
|
|
9c51412156 | ||
|
|
1cfbc0b1bb | ||
|
|
bd56e90473 | ||
|
|
a0682753a4 | ||
|
|
97990ae027 | ||
|
|
7369d788cb | ||
|
|
b8c7313496 | ||
|
|
ea352b71ba | ||
|
|
1b7ca16ce1 | ||
|
|
4008b3534a | ||
|
|
9d9949ca28 | ||
|
|
ed954237ef | ||
|
|
24ef091537 | ||
|
|
34e6be463e | ||
|
|
acc18720f2 | ||
|
|
5d3b0e656c | ||
|
|
a727d9052c | ||
|
|
fce1e105fe | ||
|
|
e117a21117 | ||
|
|
81daf02d20 | ||
|
|
53c40fcdce | ||
|
|
a7f97c469b | ||
|
|
3c53fbf767 | ||
|
|
68b0c3c55f | ||
|
|
3fb8003f06 | ||
|
|
dc0f236629 | ||
|
|
830eebd35e | ||
|
|
abd3e78ab1 | ||
|
|
e36aaf91ed | ||
|
|
29e2b929f4 | ||
|
|
932b7dfa81 | ||
|
|
36464a6ea5 | ||
|
|
5c88b180e9 | ||
|
|
b9e6521123 | ||
|
|
1f564b2674 | ||
|
|
53e85b8bf4 | ||
|
|
2a797169aa | ||
|
|
1479cd6001 | ||
|
|
adabfbc465 | ||
|
|
de0113b01f | ||
|
|
a688e8fa6a | ||
|
|
9a13525047 | ||
|
|
e83bd2cc55 | ||
|
|
98f7556b21 | ||
|
|
e185360c24 | ||
|
|
66cff6eeb4 | ||
|
|
a400c72ab6 | ||
|
|
27b920d975 | ||
|
|
058ad32dd9 | ||
|
|
c0476063a2 | ||
|
|
53f203ee79 | ||
|
|
85f8b9b07f | ||
|
|
336b82fafe | ||
|
|
222945b705 | ||
|
|
4d7e19e5e7 | ||
|
|
87d20a162c | ||
|
|
8e1b422b61 | ||
|
|
965d8f7d4b | ||
|
|
470479f17a | ||
|
|
fe8bd7c1bd | ||
|
|
9314475e22 | ||
|
|
f5687330c7 |
@@ -1,61 +0,0 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 30
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request with the stale
|
||||
# label is closed. Set to false to disable. If disabled, issues still need to
|
||||
# be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 7
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale.
|
||||
# Defaults to `[]` (disabled)
|
||||
onlyLabels: []
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set
|
||||
# to `[]` to disable
|
||||
exemptLabels:
|
||||
- bug
|
||||
- WIP
|
||||
- ready-for-review
|
||||
- in-review
|
||||
- in-next
|
||||
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: false
|
||||
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: false
|
||||
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: false
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
:warning: This issue or PR has been automatically marked as stale because it has not
|
||||
had any activity in the last month. *If no activity occurs in the next week, it will
|
||||
be automatically closed.* Thank you for your contributions.
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 30
|
||||
|
||||
# Limit to only `issues` or `pulls`
|
||||
# only: issues
|
||||
|
||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
||||
# pulls:
|
||||
# daysUntilStale: 30
|
||||
# markComment: >
|
||||
# This pull request has been automatically marked as stale because it has not had
|
||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
||||
# for your contributions.
|
||||
|
||||
# issues:
|
||||
# exemptLabels:
|
||||
# - confirmed
|
||||
@@ -0,0 +1,31 @@
|
||||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
# For more information, see: https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
actions: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: ':warning: This issue has been automatically marked as stale because it has not had any activity in the last month. *If no activity occurs in the next week, it will be automatically closed.* Thank you for your contributions.'
|
||||
stale-pr-message: ':warning: This PR has been automatically marked as stale because it has not had any activity in the last month. *If no activity occurs in the next week, it will be automatically closed.* Thank you for your contributions.'
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
stale-issue-label: 'stale'
|
||||
stale-pr-label: 'stale'
|
||||
operations-per-run: 500
|
||||
exempt-issue-labels: "bug,WIP,ready-for-review,in-review,in-next"
|
||||
exempt-pr-labels: "bug,WIP,ready-for-review,in-review,in-next"
|
||||
@@ -10,6 +10,8 @@
|
||||
|
||||
Version 4.7.1 (development)
|
||||
===========================
|
||||
- Refactored ALGOIM cut integration rules. The interface is unified with
|
||||
the interface for moment based cut integration rules.
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
@@ -18,6 +20,8 @@ Discretization improvements
|
||||
|
||||
- Added support for boundary constraints to the hybridization class.
|
||||
|
||||
- Added support for external boundary submeshes with nonconformal mesh adaptation.
|
||||
|
||||
Meshing improvements
|
||||
--------------------
|
||||
- The ExodusII reader now handles pyramid and wedge element types. Mixed meshes
|
||||
@@ -50,8 +54,15 @@ GPU computing
|
||||
or by explicitly calling `KernelReporter::Enable`. Users can then add
|
||||
specializations for these kernels to achieve higher performance.
|
||||
|
||||
- Element assembly kernels have been added for low-order refined to
|
||||
high-order transfer operators. New kernels can be offloaded as device
|
||||
kernels. Example usage may be found in lor-transfer.cpp under miniapps/tools.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Added support for SUNDIALS v7. See the section "API changes" for some small
|
||||
changes related to this new version.
|
||||
|
||||
- Refactored the `ARKStepSolver` class (ARKODE interface) to use
|
||||
`TimeDependentOperator::Mult` only when the associated ODE operator is
|
||||
expressed in explicit form (i.e., `TimeDependentOperator::isExplicit()`),
|
||||
@@ -68,6 +79,18 @@ API changes
|
||||
-----------
|
||||
- API change: in class GridFunction, 'fec' was renamed to 'fec_owned'.
|
||||
|
||||
- API change: support for SUNDIALS v7:
|
||||
* the SUNDIALS types `realtype` and `booleantype` are no longer defined by v7
|
||||
and therefore MFEM now uses the new type names `sunrealtype` and
|
||||
`sunbooleantype`, respectively, which MFEM defines when using SUNDIALS < v6
|
||||
where these types were not defined.
|
||||
* The SUNDIALS macro `SUNLS_SUCCESS` and some other `*_SUCCESS` macros were
|
||||
removed and replaced by `SUN_SUCCESS` in v7, so to avoid tedious checks for
|
||||
SUNDIALS versions, MFEM now defines and uses the constant `SUN_SUCCESS` when
|
||||
using SUNDIALS < v7.
|
||||
* The constants `SUN_PREC_*`, introduced by SUNDIALS v6 are now introduced by
|
||||
MFEM when using SUNDIALS < v6 to avoid tedious version checks.
|
||||
|
||||
|
||||
Version 4.7, released on May 7, 2024
|
||||
====================================
|
||||
@@ -154,6 +177,15 @@ New and updated examples and miniapps
|
||||
- Added two new example codes: 38 and 39/39p described above. Substantially
|
||||
updated Example 18/18p.
|
||||
|
||||
- Added ODE solvers selection routines. This creates a uniformity across examples,
|
||||
miniapps and other executables in regard to ODE(time-integrator) selection.
|
||||
|
||||
- Added new mechanism for retrieving and setting state vectors in ODE solvers.
|
||||
This is relevant for AB/AM and gen-alpha solvers.
|
||||
|
||||
- Added ODEsolver/ODEsolver2 unit tests to verify order of convergence and
|
||||
read/write functionality.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Updated the Doxygen documentation style, which now requires Doxygen version
|
||||
|
||||
+4
-1
@@ -340,7 +340,10 @@ if (MFEM_USE_SUNDIALS)
|
||||
if (MFEM_USE_HIP)
|
||||
list(APPEND SUNDIALS_COMPONENTS NVector_Hip)
|
||||
endif()
|
||||
find_package(SUNDIALS REQUIRED ${SUNDIALS_COMPONENTS})
|
||||
# The Core component was added in SUNDIALS v7, so we treat it as optional in
|
||||
# order to support older versions.
|
||||
find_package(SUNDIALS REQUIRED ${SUNDIALS_COMPONENTS}
|
||||
OPTIONAL_COMPONENTS Core)
|
||||
endif()
|
||||
|
||||
# SuperLU_DIST can only be enabled in parallel
|
||||
|
||||
@@ -502,10 +502,14 @@ MFEM_USE_CODIPACK = YES/NO
|
||||
MFEM_USE_ALGOIM = YES/NO
|
||||
Enable the usage of Algoim - a collection of high-order accurate numerical
|
||||
methods and C++ algorithms for working with implicitly-defined geometry and
|
||||
level set methods. The Algoim library requires the Blitz++ library. The MFEM
|
||||
provides interface to Algoim v1. Thus, to check out the specific state use:
|
||||
level set methods, see https://algoim.github.io. MFEM provides interface to
|
||||
Algoim v1. To check out the specific Algoim state use:
|
||||
https://github.com/algoim/algoim
|
||||
git checkout 9c9ca0ef094d8ab0390ed36367a1151b459bbe0a
|
||||
https://algoim.github.io
|
||||
The Algoim library requires the Blitz++ library. To use the latest state of
|
||||
Blitz++ that has been tested with MFEM, use:
|
||||
https://github.com/blitzpp/blitz
|
||||
git checkout f24a250a43dff88c31ad92916da828b7ea9a98b7
|
||||
|
||||
MFEM_USE_ADFORWARD = YES/NO
|
||||
Enable forward mode for AD packages. This option is valid
|
||||
|
||||
@@ -31,4 +31,5 @@ mfem_find_package(SUNDIALS SUNDIALS SUNDIALS_DIR
|
||||
ADD_COMPONENT CVODE "include" cvode/cvode.h "lib" sundials_cvode
|
||||
ADD_COMPONENT CVODES "include" cvodes/cvodes.h "lib" sundials_cvodes
|
||||
ADD_COMPONENT ARKODE "include" arkode/arkode.h "lib" sundials_arkode
|
||||
ADD_COMPONENT KINSOL "include" kinsol/kinsol.h "lib" sundials_kinsol)
|
||||
ADD_COMPONENT KINSOL "include" kinsol/kinsol.h "lib" sundials_kinsol
|
||||
ADD_COMPONENT Core "include" sundials/sundials_core.h "lib" sundials_core)
|
||||
|
||||
+10
-1
@@ -289,6 +289,13 @@ endif
|
||||
ifeq ($(MFEM_USE_HIP),YES)
|
||||
SUNDIALS_LIB += -lsundials_nvechip
|
||||
endif
|
||||
SUNDIALS_CORE_PAT = $(subst\
|
||||
@MFEM_DIR@,$(MFEM_DIR),$(SUNDIALS_DIR))/lib*/libsundials_core.*
|
||||
ifeq ($(MFEM_USE_SUNDIALS),YES)
|
||||
ifneq ($(wildcard $(SUNDIALS_CORE_PAT)),)
|
||||
SUNDIALS_LIB += -lsundials_core
|
||||
endif
|
||||
endif
|
||||
# If SUNDIALS was built with KLU:
|
||||
# MFEM_USE_SUITESPARSE = YES
|
||||
|
||||
@@ -533,8 +540,10 @@ ifdef GOTCHA_DIR
|
||||
endif
|
||||
|
||||
# BLITZ library configuration
|
||||
BLITZ_DIR = @MFEM_DIR@/../blitz
|
||||
# BLITZ_DIR must be the custom installation folder (-DCMAKE_INSTALL_PREFIX).
|
||||
BLITZ_DIR = @MFEM_DIR@/../blitz/install
|
||||
BLITZ_OPT = -I$(BLITZ_DIR)/include
|
||||
# On intel machines, use /lib64 instead of /lib.
|
||||
BLITZ_LIB = $(XLINKER)-rpath,$(BLITZ_DIR)/lib -L$(BLITZ_DIR)/lib -lblitz
|
||||
|
||||
# ALGOIM library configuration
|
||||
|
||||
+11
-37
@@ -3,14 +3,14 @@
|
||||
// Compile with: make ex10
|
||||
//
|
||||
// Sample runs:
|
||||
// ex10 -m ../data/beam-quad.mesh -s 3 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-tri.mesh -s 3 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-hex.mesh -s 2 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-tet.mesh -s 2 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-wedge.mesh -s 2 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-quad.mesh -s 14 -r 2 -o 2 -dt 0.03 -vs 20
|
||||
// ex10 -m ../data/beam-hex.mesh -s 14 -r 1 -o 2 -dt 0.05 -vs 20
|
||||
// ex10 -m ../data/beam-quad-amr.mesh -s 3 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-quad.mesh -s 23 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-tri.mesh -s 23 -r 2 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-hex.mesh -s 22 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-tet.mesh -s 22 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-wedge.mesh -s 22 -r 1 -o 2 -dt 3
|
||||
// ex10 -m ../data/beam-quad.mesh -s 4 -r 2 -o 2 -dt 0.03 -vs 20
|
||||
// ex10 -m ../data/beam-hex.mesh -s 4 -r 1 -o 2 -dt 0.05 -vs 20
|
||||
// ex10 -m ../data/beam-quad-amr.mesh -s 23 -r 2 -o 2 -dt 3
|
||||
//
|
||||
// Description: This examples solves a time dependent nonlinear elasticity
|
||||
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
|
||||
@@ -160,7 +160,7 @@ int main(int argc, char *argv[])
|
||||
const char *mesh_file = "../data/beam-quad.mesh";
|
||||
int ref_levels = 2;
|
||||
int order = 2;
|
||||
int ode_solver_type = 3;
|
||||
int ode_solver_type = 23;
|
||||
real_t t_final = 300.0;
|
||||
real_t dt = 3.0;
|
||||
real_t visc = 1e-2;
|
||||
@@ -177,11 +177,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
|
||||
" 11 - Forward Euler, 12 - RK2,\n\t"
|
||||
" 13 - RK3 SSP, 14 - RK4."
|
||||
" 22 - Implicit Midpoint Method,\n\t"
|
||||
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
|
||||
ODESolver::Types.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -213,28 +209,7 @@ int main(int argc, char *argv[])
|
||||
// 3. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 1: ode_solver = new BackwardEulerSolver; break;
|
||||
case 2: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 3: ode_solver = new SDIRK33Solver; break;
|
||||
// Explicit methods
|
||||
case 11: ode_solver = new ForwardEulerSolver; break;
|
||||
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 13: ode_solver = new RK3SSPSolver; break;
|
||||
case 14: ode_solver = new RK4Solver; break;
|
||||
case 15: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
@@ -371,7 +346,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 10. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
|
||||
+11
-40
@@ -3,14 +3,14 @@
|
||||
// Compile with: make ex10p
|
||||
//
|
||||
// Sample runs:
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad.mesh -s 3 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-tri.mesh -s 3 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-hex.mesh -s 2 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-tet.mesh -s 2 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-wedge.mesh -s 2 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad.mesh -s 14 -rs 2 -dt 0.03 -vs 20
|
||||
// mpirun -np 4 ex10p -m ../data/beam-hex.mesh -s 14 -rs 1 -dt 0.05 -vs 20
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad-amr.mesh -s 3 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad.mesh -s 23 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-tri.mesh -s 23 -rs 2 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-hex.mesh -s 22 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-tet.mesh -s 22 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-wedge.mesh -s 22 -rs 1 -dt 3
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad.mesh -s 4 -rs 2 -dt 0.03 -vs 20
|
||||
// mpirun -np 4 ex10p -m ../data/beam-hex.mesh -s 4 -rs 1 -dt 0.05 -vs 20
|
||||
// mpirun -np 4 ex10p -m ../data/beam-quad-amr.mesh -s 23 -rs 2 -dt 3
|
||||
//
|
||||
// Description: This examples solves a time dependent nonlinear elasticity
|
||||
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
|
||||
@@ -172,7 +172,7 @@ int main(int argc, char *argv[])
|
||||
int ser_ref_levels = 2;
|
||||
int par_ref_levels = 0;
|
||||
int order = 2;
|
||||
int ode_solver_type = 3;
|
||||
int ode_solver_type = 23;
|
||||
real_t t_final = 300.0;
|
||||
real_t dt = 3.0;
|
||||
real_t visc = 1e-2;
|
||||
@@ -192,11 +192,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
|
||||
" 11 - Forward Euler, 12 - RK2,\n\t"
|
||||
" 13 - RK3 SSP, 14 - RK4."
|
||||
" 22 - Implicit Midpoint Method,\n\t"
|
||||
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
|
||||
ODESolver::Types.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -238,31 +234,7 @@ int main(int argc, char *argv[])
|
||||
// 4. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 1: ode_solver = new BackwardEulerSolver; break;
|
||||
case 2: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 3: ode_solver = new SDIRK33Solver; break;
|
||||
// Explicit methods
|
||||
case 11: ode_solver = new ForwardEulerSolver; break;
|
||||
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 13: ode_solver = new RK3SSPSolver; break;
|
||||
case 14: ode_solver = new RK4Solver; break;
|
||||
case 15: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
if (myid == 0)
|
||||
{
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
}
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
// 5. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
@@ -433,7 +405,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 12. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete pmesh;
|
||||
|
||||
return 0;
|
||||
|
||||
+9
-30
@@ -5,10 +5,10 @@
|
||||
// Sample runs: ex16
|
||||
// ex16 -m ../data/inline-tri.mesh
|
||||
// ex16 -m ../data/disc-nurbs.mesh -tf 2
|
||||
// ex16 -s 1 -a 0.0 -k 1.0
|
||||
// ex16 -s 2 -a 1.0 -k 0.0
|
||||
// ex16 -s 3 -a 0.5 -k 0.5 -o 4
|
||||
// ex16 -s 14 -dt 1.0e-4 -tf 4.0e-2 -vs 40
|
||||
// ex16 -s 21 -a 0.0 -k 1.0
|
||||
// ex16 -s 22 -a 1.0 -k 0.0
|
||||
// ex16 -s 23 -a 0.5 -k 0.5 -o 4
|
||||
// ex16 -s 4 -dt 1.0e-4 -tf 4.0e-2 -vs 40
|
||||
// ex16 -m ../data/fichera-q2.mesh
|
||||
// ex16 -m ../data/fichera-mixed.mesh
|
||||
// ex16 -m ../data/escher.mesh
|
||||
@@ -95,11 +95,13 @@ int main(int argc, char *argv[])
|
||||
const char *mesh_file = "../data/star.mesh";
|
||||
int ref_levels = 2;
|
||||
int order = 2;
|
||||
int ode_solver_type = 3;
|
||||
|
||||
int ode_solver_type = 23; // SDIRK33Solver
|
||||
real_t t_final = 0.5;
|
||||
real_t dt = 1.0e-2;
|
||||
real_t alpha = 1.0e-2;
|
||||
real_t kappa = 0.5;
|
||||
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
@@ -115,8 +117,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
|
||||
"\t 11 - Forward Euler, 12 - RK2, 13 - RK3 SSP, 14 - RK4.");
|
||||
ODESolver::Types.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -149,28 +150,7 @@ int main(int argc, char *argv[])
|
||||
// 3. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 1: ode_solver = new BackwardEulerSolver; break;
|
||||
case 2: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 3: ode_solver = new SDIRK33Solver; break;
|
||||
// Explicit methods
|
||||
case 11: ode_solver = new ForwardEulerSolver; break;
|
||||
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 13: ode_solver = new RK3SSPSolver; break;
|
||||
case 14: ode_solver = new RK4Solver; break;
|
||||
case 15: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
@@ -287,7 +267,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 10. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete mesh;
|
||||
|
||||
return 0;
|
||||
|
||||
+9
-30
@@ -5,10 +5,10 @@
|
||||
// Sample runs: mpirun -np 4 ex16p
|
||||
// mpirun -np 4 ex16p -m ../data/inline-tri.mesh
|
||||
// mpirun -np 4 ex16p -m ../data/disc-nurbs.mesh -tf 2
|
||||
// mpirun -np 4 ex16p -s 1 -a 0.0 -k 1.0
|
||||
// mpirun -np 4 ex16p -s 2 -a 1.0 -k 0.0
|
||||
// mpirun -np 8 ex16p -s 3 -a 0.5 -k 0.5 -o 4
|
||||
// mpirun -np 4 ex16p -s 14 -dt 1.0e-4 -tf 4.0e-2 -vs 40
|
||||
// mpirun -np 4 ex16p -s 21 -a 0.0 -k 1.0
|
||||
// mpirun -np 4 ex16p -s 22 -a 1.0 -k 0.0
|
||||
// mpirun -np 8 ex16p -s 23 -a 0.5 -k 0.5 -o 4
|
||||
// mpirun -np 4 ex16p -s 4 -dt 1.0e-4 -tf 4.0e-2 -vs 40
|
||||
// mpirun -np 16 ex16p -m ../data/fichera-q2.mesh
|
||||
// mpirun -np 16 ex16p -m ../data/fichera-mixed.mesh
|
||||
// mpirun -np 16 ex16p -m ../data/escher-p2.mesh
|
||||
@@ -104,11 +104,13 @@ int main(int argc, char *argv[])
|
||||
int ser_ref_levels = 2;
|
||||
int par_ref_levels = 1;
|
||||
int order = 2;
|
||||
int ode_solver_type = 3;
|
||||
|
||||
int ode_solver_type = 23; // SDIRK33Solver
|
||||
real_t t_final = 0.5;
|
||||
real_t dt = 1.0e-2;
|
||||
real_t alpha = 1.0e-2;
|
||||
real_t kappa = 0.5;
|
||||
|
||||
bool visualization = true;
|
||||
bool visit = false;
|
||||
int vis_steps = 5;
|
||||
@@ -127,8 +129,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Backward Euler, 2 - SDIRK2, 3 - SDIRK3,\n\t"
|
||||
"\t 11 - Forward Euler, 12 - RK2, 13 - RK3 SSP, 14 - RK4.");
|
||||
ODESolver::Types.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -169,28 +170,7 @@ int main(int argc, char *argv[])
|
||||
// 4. Define the ODE solver used for time integration. Several implicit
|
||||
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
|
||||
// explicit Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 1: ode_solver = new BackwardEulerSolver; break;
|
||||
case 2: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 3: ode_solver = new SDIRK33Solver; break;
|
||||
// Explicit methods
|
||||
case 11: ode_solver = new ForwardEulerSolver; break;
|
||||
case 12: ode_solver = new RK2Solver(0.5); break; // midpoint method
|
||||
case 13: ode_solver = new RK3SSPSolver; break;
|
||||
case 14: ode_solver = new RK4Solver; break;
|
||||
case 15: ode_solver = new GeneralizedAlphaSolver(0.5); break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
// 5. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
@@ -376,7 +356,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 12. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete pmesh;
|
||||
|
||||
return 0;
|
||||
|
||||
+2
-17
@@ -90,8 +90,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Forward Euler,\n\t"
|
||||
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6.");
|
||||
ODESolver::ExplicitTypes.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final", "Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step. Positive number skips CFL timestep calculation.");
|
||||
@@ -125,18 +124,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(1.0); break;
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 6: ode_solver = new RK6Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
return 3;
|
||||
}
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::SelectExplicit(ode_solver_type);
|
||||
|
||||
// 4. Define the discontinuous DG finite element space of the given
|
||||
// polynomial order on the refined mesh.
|
||||
@@ -304,8 +292,5 @@ int main(int argc, char *argv[])
|
||||
const real_t error = sol.ComputeLpError(2, u0);
|
||||
cout << "Solution error: " << error << endl;
|
||||
|
||||
// Free the used memory.
|
||||
delete ode_solver;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+2
-17
@@ -99,8 +99,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Forward Euler,\n\t"
|
||||
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6.");
|
||||
ODESolver::ExplicitTypes.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final", "Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
"Time step. Positive number skips CFL timestep calculation.");
|
||||
@@ -148,18 +147,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(1.0); break;
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 6: ode_solver = new RK6Solver; break;
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
return 3;
|
||||
}
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::SelectExplicit(ode_solver_type);
|
||||
|
||||
// 4. Define the discontinuous DG finite element space of the given
|
||||
// polynomial order on the refined mesh.
|
||||
@@ -360,8 +348,5 @@ int main(int argc, char *argv[])
|
||||
cout << "Solution error: " << error << endl;
|
||||
}
|
||||
|
||||
// Free the used memory.
|
||||
delete ode_solver;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+2
-29
@@ -201,9 +201,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&order, "-o", "--order",
|
||||
"Order (degree) of the finite elements.");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: [0--10] - GeneralizedAlpha(0.1 * s),\n\t"
|
||||
"\t 11 - Average Acceleration, 12 - Linear Acceleration\n"
|
||||
"\t 13 - CentralDifference, 14 - FoxGoodwin");
|
||||
SecondOrderODESolver::Types.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -238,32 +236,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several second order
|
||||
// time integrators are available.
|
||||
SecondOrderODESolver *ode_solver;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit methods
|
||||
case 0: ode_solver = new GeneralizedAlpha2Solver(0.0); break;
|
||||
case 1: ode_solver = new GeneralizedAlpha2Solver(0.1); break;
|
||||
case 2: ode_solver = new GeneralizedAlpha2Solver(0.2); break;
|
||||
case 3: ode_solver = new GeneralizedAlpha2Solver(0.3); break;
|
||||
case 4: ode_solver = new GeneralizedAlpha2Solver(0.4); break;
|
||||
case 5: ode_solver = new GeneralizedAlpha2Solver(0.5); break;
|
||||
case 6: ode_solver = new GeneralizedAlpha2Solver(0.6); break;
|
||||
case 7: ode_solver = new GeneralizedAlpha2Solver(0.7); break;
|
||||
case 8: ode_solver = new GeneralizedAlpha2Solver(0.8); break;
|
||||
case 9: ode_solver = new GeneralizedAlpha2Solver(0.9); break;
|
||||
case 10: ode_solver = new GeneralizedAlpha2Solver(1.0); break;
|
||||
|
||||
case 11: ode_solver = new AverageAccelerationSolver(); break;
|
||||
case 12: ode_solver = new LinearAccelerationSolver(); break;
|
||||
case 13: ode_solver = new CentralDifferenceSolver(); break;
|
||||
case 14: ode_solver = new FoxGoodwinSolver(); break;
|
||||
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
SecondOrderODESolver *ode_solver= SecondOrderODESolver::Select(ode_solver_type);
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
|
||||
+115
-81
@@ -3,18 +3,18 @@
|
||||
// Compile with: make ex38
|
||||
//
|
||||
// Sample runs:
|
||||
// (since all sample runs require LAPACK, the * symbol is used to exclude them
|
||||
// from the automatically generated internal MFEM tests).
|
||||
// (since all sample runs require LAPACK or ALGOIM, the * symbol is used to
|
||||
// exclude them from the automatically generated internal MFEM tests).
|
||||
// * ex38
|
||||
// * ex38 -i volumetric1d
|
||||
// * ex38 -i surface2d
|
||||
// * ex38 -i surface2d -o 4 -r 5
|
||||
// * ex38 -i surface2d -o 4 -r 5 -m 1
|
||||
// * ex38 -i volumetric2d
|
||||
// * ex38 -i volumetric2d -o 4 -r 5
|
||||
// * ex38 -i volumetric2d -o 4 -r 5 -m 1
|
||||
// * ex38 -i surface3d
|
||||
// * ex38 -i surface3d -o 4 -r 5
|
||||
// * ex38 -i surface3d -o 3 -r 4 -m 1
|
||||
// * ex38 -i volumetric3d
|
||||
// * ex38 -i volumetric3d -o 4 -r 5
|
||||
// * ex38 -i volumetric3d -o 3 -r 4 -m 1
|
||||
//
|
||||
// Description: This example code demonstrates the use of MFEM to integrate
|
||||
// functions over implicit interfaces and subdomains bounded by
|
||||
@@ -71,7 +71,7 @@ real_t integrand(const Vector& X)
|
||||
switch (itype)
|
||||
{
|
||||
case IntegrationType::Volumetric1D:
|
||||
return 1.;
|
||||
return pow(X(0), 2.);
|
||||
case IntegrationType::Surface2D:
|
||||
return 3. * pow(X(0), 2.) - pow(X(1), 2.);
|
||||
case IntegrationType::Volumetric2D:
|
||||
@@ -91,7 +91,7 @@ real_t Surface()
|
||||
switch (itype)
|
||||
{
|
||||
case IntegrationType::Volumetric1D:
|
||||
return 1.;
|
||||
return .3025;
|
||||
case IntegrationType::Surface2D:
|
||||
return 2. * M_PI;
|
||||
case IntegrationType::Volumetric2D:
|
||||
@@ -111,7 +111,7 @@ real_t Volume()
|
||||
switch (itype)
|
||||
{
|
||||
case IntegrationType::Volumetric1D:
|
||||
return .55;
|
||||
return pow(.55, 3.) / 3.;
|
||||
case IntegrationType::Surface2D:
|
||||
return NAN;
|
||||
case IntegrationType::Volumetric2D:
|
||||
@@ -125,7 +125,6 @@ real_t Volume()
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
/**
|
||||
@brief Class for surface IntegrationRule
|
||||
|
||||
@@ -135,11 +134,14 @@ real_t Volume()
|
||||
class SIntegrationRule : public IntegrationRule
|
||||
{
|
||||
protected:
|
||||
/// @brief Space Dimension of the IntegrationRule
|
||||
/// method 0 is moments-based, 1 is Algoim.
|
||||
int method, ir_order, ls_order;
|
||||
Coefficient &level_set;
|
||||
/// Space Dimension of the IntegrationRule
|
||||
int dim;
|
||||
/// @brief Column-wise matrix of the quadtrature weights
|
||||
/// Column-wise matrix of the quadtrature weights
|
||||
DenseMatrix Weights;
|
||||
/// @brief Column-wise matrix of the transformation weights of the normal
|
||||
/// Column-wise matrix of the transformation weights of the normal
|
||||
DenseMatrix SurfaceWeights;
|
||||
|
||||
public:
|
||||
@@ -153,15 +155,21 @@ public:
|
||||
@param [in] lsOrder Polynomial degree for approx of level-set function
|
||||
@param [in] mesh Pointer to the mesh that is used
|
||||
*/
|
||||
SIntegrationRule(int Order, Coefficient& LvlSet, int lsOrder, Mesh* mesh)
|
||||
SIntegrationRule(int method_, int Order,
|
||||
Coefficient& LvlSet, int lsOrder, Mesh* mesh)
|
||||
: method(method_), ir_order(Order), ls_order(lsOrder),
|
||||
level_set(LvlSet), dim(mesh->Dimension())
|
||||
{
|
||||
dim = mesh->Dimension();
|
||||
// Nothing gets pre-computed for Algoim.
|
||||
if (method == 1) { return; }
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
MomentFittingIntRules mf_ir(ir_order, level_set, ls_order);
|
||||
|
||||
IsoparametricTransformation Tr;
|
||||
MomentFittingIntRules MFIRs(Order, LvlSet, lsOrder);
|
||||
mesh->GetElementTransformation(0, &Tr);
|
||||
IntegrationRule ir;
|
||||
MFIRs.GetSurfaceIntegrationRule(Tr, ir);
|
||||
mf_ir.GetSurfaceIntegrationRule(Tr, ir);
|
||||
if (dim >1)
|
||||
{
|
||||
Weights.SetSize(ir.GetNPoints(), mesh->GetNE());
|
||||
@@ -172,7 +180,7 @@ public:
|
||||
}
|
||||
SurfaceWeights.SetSize(ir.GetNPoints(), mesh->GetNE());
|
||||
Vector w;
|
||||
MFIRs.GetSurfaceWeights(Tr, ir, w);
|
||||
mf_ir.GetSurfaceWeights(Tr, ir, w);
|
||||
SurfaceWeights.SetCol(0, w);
|
||||
SetSize(ir.GetNPoints());
|
||||
|
||||
@@ -198,8 +206,8 @@ public:
|
||||
for (int elem = 1; elem < mesh->GetNE(); elem++)
|
||||
{
|
||||
mesh->GetElementTransformation(elem, &Tr);
|
||||
MFIRs.GetSurfaceIntegrationRule(Tr, ir);
|
||||
MFIRs.GetSurfaceWeights(Tr, ir, w);
|
||||
mf_ir.GetSurfaceIntegrationRule(Tr, ir);
|
||||
mf_ir.GetSurfaceWeights(Tr, ir, w);
|
||||
SurfaceWeights.SetCol(elem, w);
|
||||
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
@@ -215,48 +223,48 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
MFEM_ABORT("Moment-fitting requires MFEM to be built with LAPACK!");
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set the weights for the given element and multiply them with the
|
||||
transformation of the interface
|
||||
*/
|
||||
void SetElementinclSurfaceWeight(int Element)
|
||||
void SetElementAndSurfaceWeight(ElementTransformation &Tr)
|
||||
{
|
||||
if (dim == 1)
|
||||
if (method == 1)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(0);
|
||||
intp.x = Weights(0, Element);
|
||||
intp.weight = Weights(1, Element);
|
||||
cout << intp.x << " " << Element << endl;
|
||||
}
|
||||
else
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
AlgoimIntegrationRules a_ir(ir_order, level_set, ls_order);
|
||||
a_ir.GetSurfaceIntegrationRule(Tr, *this);
|
||||
Vector w;
|
||||
a_ir.GetSurfaceWeights(Tr, *this, w);
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
intp.weight = Weights(ip, Element) * SurfaceWeights(ip, Element);
|
||||
IntPoint(ip).weight *= w(ip);
|
||||
}
|
||||
}
|
||||
return;
|
||||
#else
|
||||
MFEM_ABORT("MFEM is not built with Algoim support!");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Set the weights for the given element
|
||||
void SetElement(int Element)
|
||||
{
|
||||
if (dim == 1)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(0);
|
||||
intp.x = Weights(0, Element);
|
||||
intp.weight = Weights(1, Element);
|
||||
IntPoint(0).x = Weights(0, Tr.ElementNo);
|
||||
IntPoint(0).weight = Weights(1, Tr.ElementNo);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
intp.weight = Weights(ip, Element);
|
||||
IntPoint(ip).weight = Weights(ip, Tr.ElementNo) *
|
||||
SurfaceWeights(ip, Tr.ElementNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Destructor of SIntegrationRule
|
||||
~SIntegrationRule() {}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -268,9 +276,12 @@ public:
|
||||
class CIntegrationRule : public IntegrationRule
|
||||
{
|
||||
protected:
|
||||
/// @brief Space Dimension of the IntegrationRule
|
||||
/// method 0 is moments-based, 1 is Algoim.
|
||||
int method, ir_order, ls_order;
|
||||
Coefficient &level_set;
|
||||
/// Space Dimension of the IntegrationRule
|
||||
int dim;
|
||||
/// @brief Column-wise matrix of the quadtrature weights
|
||||
/// Column-wise matrix of the quadtrature positions and weights.
|
||||
DenseMatrix Weights;
|
||||
|
||||
public:
|
||||
@@ -284,15 +295,21 @@ public:
|
||||
@param [in] lsOrder Polynomial degree for approx of level-set function
|
||||
@param [in] mesh Pointer to the mesh that is used
|
||||
*/
|
||||
CIntegrationRule(int Order, Coefficient& LvlSet, int lsOrder, Mesh* mesh)
|
||||
CIntegrationRule(int method_, int Order,
|
||||
Coefficient &LvlSet, int lsOrder, Mesh *mesh)
|
||||
: method(method_), ir_order(Order), ls_order(lsOrder),
|
||||
level_set(LvlSet), dim(mesh->Dimension())
|
||||
{
|
||||
dim = mesh->Dimension();
|
||||
// Nothing gets pre-computed for Algoim.
|
||||
if (method == 1) { return; }
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
MomentFittingIntRules mf_ir(ir_order, level_set, ls_order);
|
||||
|
||||
IsoparametricTransformation Tr;
|
||||
MomentFittingIntRules MFIRs(Order, LvlSet, lsOrder);
|
||||
mesh->GetElementTransformation(0, &Tr);
|
||||
IntegrationRule ir;
|
||||
MFIRs.GetVolumeIntegrationRule(Tr, ir);
|
||||
mf_ir.GetVolumeIntegrationRule(Tr, ir);
|
||||
if (dim > 1)
|
||||
{
|
||||
Weights.SetSize(ir.GetNPoints(), mesh->GetNE());
|
||||
@@ -324,9 +341,9 @@ public:
|
||||
for (int elem = 1; elem < mesh->GetNE(); elem++)
|
||||
{
|
||||
mesh->GetElementTransformation(elem, &Tr);
|
||||
MFIRs.GetVolumeIntegrationRule(Tr, ir);
|
||||
mf_ir.GetVolumeIntegrationRule(Tr, ir);
|
||||
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
for (int ip = 0; ip < ir.GetNPoints(); ip++)
|
||||
{
|
||||
if (dim > 1)
|
||||
{
|
||||
@@ -339,29 +356,39 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
MFEM_ABORT("Moment-fitting requires MFEM to be built with LAPACK!");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Set the weights for the given element
|
||||
void SetElement(int Element)
|
||||
void SetElement(ElementTransformation &Tr)
|
||||
{
|
||||
if (dim == 1)
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
intp.x = Weights(2 * ip, Element);
|
||||
intp.weight = Weights(2 * ip + 1, Element);
|
||||
}
|
||||
else
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
intp.weight = Weights(ip, Element);
|
||||
}
|
||||
}
|
||||
if (method == 1)
|
||||
{
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
AlgoimIntegrationRules a_ir(ir_order, level_set, ls_order);
|
||||
a_ir.GetVolumeIntegrationRule(Tr, *this);
|
||||
return;
|
||||
#else
|
||||
MFEM_ABORT("MFEM is not built with Algoim support!");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @brief Destructor of CIntegrationRule
|
||||
~CIntegrationRule() {}
|
||||
for (int ip = 0; ip < GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = IntPoint(ip);
|
||||
if (dim == 1)
|
||||
{
|
||||
intp.x = Weights(2 * ip, Tr.ElementNo);
|
||||
intp.weight = Weights(2 * ip + 1, Tr.ElementNo);
|
||||
}
|
||||
else { intp.weight = Weights(ip, Tr.ElementNo); }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@brief Class for surface linearform integrator
|
||||
|
||||
@@ -418,7 +445,7 @@ public:
|
||||
elvect = 0.;
|
||||
|
||||
// Update the surface integration rule for the current element
|
||||
SIntRule->SetElementinclSurfaceWeight(Tr.ElementNo);
|
||||
SIntRule->SetElementAndSurfaceWeight(Tr);
|
||||
|
||||
for (int ip = 0; ip < SIntRule->GetNPoints(); ip++)
|
||||
{
|
||||
@@ -428,6 +455,8 @@ public:
|
||||
add(elvect, SIntRule->IntPoint(ip).weight * val, shape, elvect);
|
||||
}
|
||||
}
|
||||
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -486,7 +515,7 @@ public:
|
||||
elvect = 0.;
|
||||
|
||||
// Update the subdomain integration rule
|
||||
CIntRule->SetElement(Tr.ElementNo);
|
||||
CIntRule->SetElement(Tr);
|
||||
|
||||
for (int ip = 0; ip < CIntRule->GetNPoints(); ip++)
|
||||
{
|
||||
@@ -497,18 +526,17 @@ public:
|
||||
add(elvect, CIntRule->IntPoint(ip).weight * val, shape, elvect);
|
||||
}
|
||||
}
|
||||
|
||||
using LinearFormIntegrator::AssembleRHSElementVect;
|
||||
};
|
||||
#endif // MFEM_USE_LAPACK
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifndef MFEM_USE_LAPACK
|
||||
cout << "MFEM must be built with LAPACK for this example." << endl;
|
||||
return MFEM_SKIP_RETURN_VALUE;
|
||||
#else
|
||||
#if defined(MFEM_USE_LAPACK) || defined(MFEM_USE_ALGOIM)
|
||||
// 1. Parse he command-line options.
|
||||
int ref_levels = 3;
|
||||
int order = 2;
|
||||
int method = 0;
|
||||
const char *inttype = "surface2d";
|
||||
bool visualization = true;
|
||||
itype = IntegrationType::Surface2D;
|
||||
@@ -516,6 +544,8 @@ int main(int argc, char *argv[])
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&order, "-o", "--order", "Order of quadrature rule");
|
||||
args.AddOption(&ref_levels, "-r", "--refine", "Number of meh refinements");
|
||||
args.AddOption(&method, "-m", "--method",
|
||||
"Cut integration method: 0 for moments-based, 1 for Algoim.");
|
||||
args.AddOption(&inttype, "-i", "--integrationtype",
|
||||
"IntegrationType to demonstrate");
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
@@ -550,7 +580,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 2. Construct and refine the mesh.
|
||||
Mesh *mesh;
|
||||
Mesh *mesh = nullptr;
|
||||
if (itype == IntegrationType::Volumetric1D)
|
||||
{
|
||||
mesh = new Mesh("../data/inline-segment.mesh");
|
||||
@@ -598,13 +628,14 @@ int main(int argc, char *argv[])
|
||||
// 5. Define the necessary Integration rules on element 0.
|
||||
IsoparametricTransformation Tr;
|
||||
mesh->GetElementTransformation(0, &Tr);
|
||||
SIntegrationRule* sir = new SIntegrationRule(order, levelset, 2, mesh);
|
||||
SIntegrationRule* sir = new SIntegrationRule(method, order,
|
||||
levelset, 2, mesh);
|
||||
CIntegrationRule* cir = NULL;
|
||||
if (itype == IntegrationType::Volumetric1D
|
||||
|| itype == IntegrationType::Volumetric2D
|
||||
|| itype == IntegrationType::Volumetric3D)
|
||||
{
|
||||
cir = new CIntegrationRule(order, levelset, 2, mesh);
|
||||
cir = new CIntegrationRule(method, order, levelset, 2, mesh);
|
||||
}
|
||||
|
||||
// 6. Define and assemble the linear forms on the finite element space.
|
||||
@@ -647,11 +678,11 @@ int main(int argc, char *argv[])
|
||||
cout << "Number of div free basis functions: " << nbasis << endl;
|
||||
cout << "Number of quadrature points: " << ir.GetNPoints() << endl;
|
||||
}
|
||||
cout << scientific << setprecision(2);
|
||||
cout << scientific << setprecision(10);
|
||||
cout << "============================================" << endl;
|
||||
cout << "Computed value of surface integral: " << surface.Sum() << endl;
|
||||
cout << "True value of surface integral: " << Surface() << endl;
|
||||
cout << "Absolute Error (Surface): ";
|
||||
cout << "Absolute Error (Surface): ";
|
||||
cout << abs(surface.Sum() - Surface()) << endl;
|
||||
cout << "Relative Error (Surface): ";
|
||||
cout << abs(surface.Sum() - Surface()) / Surface() << endl;
|
||||
@@ -662,7 +693,7 @@ int main(int argc, char *argv[])
|
||||
cout << "--------------------------------------------" << endl;
|
||||
cout << "Computed value of volume integral: " << volume.Sum() << endl;
|
||||
cout << "True value of volume integral: " << Volume() << endl;
|
||||
cout << "Absolute Error (Volume): ";
|
||||
cout << "Absolute Error (Volume): ";
|
||||
cout << abs(volume.Sum() - Volume()) << endl;
|
||||
cout << "Relative Error (Volume): ";
|
||||
cout << abs(volume.Sum() - Volume()) / Volume() << endl;
|
||||
@@ -691,5 +722,8 @@ int main(int argc, char *argv[])
|
||||
delete fespace;
|
||||
delete mesh;
|
||||
return EXIT_SUCCESS;
|
||||
#endif //MFEM_USE_LAPACK
|
||||
#else
|
||||
cout << "MFEM must be built with LAPACK or ALGOIM for this example." << endl;
|
||||
return MFEM_SKIP_RETURN_VALUE;
|
||||
#endif // MFEM_USE_LAPACK
|
||||
}
|
||||
|
||||
+3
-30
@@ -9,7 +9,7 @@
|
||||
// ex9 -m ../data/periodic-square.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex9 -m ../data/periodic-hexagon.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.002 -tf 9
|
||||
// ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.02 -s 13 -tf 9
|
||||
// ex9 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.02 -s 23 -tf 9
|
||||
// ex9 -m ../data/star-q3.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex9 -m ../data/star-mixed.mesh -p 1 -r 2 -dt 0.005 -tf 9
|
||||
// ex9 -m ../data/disc-nurbs.mesh -p 1 -r 3 -dt 0.005 -tf 9
|
||||
@@ -182,12 +182,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Forward Euler,\n\t"
|
||||
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
|
||||
" 11 - Backward Euler,\n\t"
|
||||
" 12 - SDIRK23 (L-stable), 13 - SDIRK33,\n\t"
|
||||
" 22 - Implicit Midpoint Method,\n\t"
|
||||
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
|
||||
ODESolver::Types.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -224,28 +219,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 3. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Explicit methods
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(1.0); break;
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 6: ode_solver = new RK6Solver; break;
|
||||
// Implicit (L-stable) methods
|
||||
case 11: ode_solver = new BackwardEulerSolver; break;
|
||||
case 12: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 13: ode_solver = new SDIRK33Solver; break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
|
||||
default:
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
return 3;
|
||||
}
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
// 4. Refine the mesh to increase the resolution. In this example we do
|
||||
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
|
||||
@@ -440,7 +414,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
// 10. Free the used memory.
|
||||
delete ode_solver;
|
||||
delete pd;
|
||||
delete dc;
|
||||
|
||||
|
||||
+3
-33
@@ -9,7 +9,7 @@
|
||||
// mpirun -np 4 ex9p -m ../data/periodic-square.mesh -p 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/periodic-hexagon.mesh -p 1 -dt 0.005 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/amr-quad.mesh -p 1 -rp 1 -dt 0.002 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/amr-quad.mesh -p 1 -rp 1 -dt 0.02 -s 13 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/amr-quad.mesh -p 1 -rp 1 -dt 0.02 -s 23 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/star-q3.mesh -p 1 -rp 1 -dt 0.004 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/star-mixed.mesh -p 1 -rp 1 -dt 0.004 -tf 9
|
||||
// mpirun -np 4 ex9p -m ../data/disc-nurbs.mesh -p 1 -rp 1 -dt 0.005 -tf 9
|
||||
@@ -285,12 +285,7 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
|
||||
"ODE solver: 1 - Forward Euler,\n\t"
|
||||
" 2 - RK2 SSP, 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
|
||||
" 11 - Backward Euler,\n\t"
|
||||
" 12 - SDIRK23 (L-stable), 13 - SDIRK33,\n\t"
|
||||
" 22 - Implicit Midpoint Method,\n\t"
|
||||
" 23 - SDIRK23 (A-stable), 24 - SDIRK34");
|
||||
ODESolver::Types.c_str());
|
||||
args.AddOption(&t_final, "-tf", "--t-final",
|
||||
"Final time; start time is 0.");
|
||||
args.AddOption(&dt, "-dt", "--time-step",
|
||||
@@ -338,31 +333,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
// 4. Define the ODE solver used for time integration. Several explicit
|
||||
// Runge-Kutta methods are available.
|
||||
ODESolver *ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Explicit methods
|
||||
case 1: ode_solver = new ForwardEulerSolver; break;
|
||||
case 2: ode_solver = new RK2Solver(1.0); break;
|
||||
case 3: ode_solver = new RK3SSPSolver; break;
|
||||
case 4: ode_solver = new RK4Solver; break;
|
||||
case 6: ode_solver = new RK6Solver; break;
|
||||
// Implicit (L-stable) methods
|
||||
case 11: ode_solver = new BackwardEulerSolver; break;
|
||||
case 12: ode_solver = new SDIRK23Solver(2); break;
|
||||
case 13: ode_solver = new SDIRK33Solver; break;
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 22: ode_solver = new ImplicitMidpointSolver; break;
|
||||
case 23: ode_solver = new SDIRK23Solver; break;
|
||||
case 24: ode_solver = new SDIRK34Solver; break;
|
||||
default:
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "Unknown ODE solver type: " << ode_solver_type << '\n';
|
||||
}
|
||||
delete mesh;
|
||||
return 3;
|
||||
}
|
||||
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
|
||||
|
||||
// 5. Refine the mesh in serial to increase the resolution. In this example
|
||||
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
|
||||
@@ -642,7 +613,6 @@ int main(int argc, char *argv[])
|
||||
delete m;
|
||||
delete fes;
|
||||
delete pmesh;
|
||||
delete ode_solver;
|
||||
delete pd;
|
||||
#ifdef MFEM_USE_ADIOS2
|
||||
if (adios2)
|
||||
|
||||
@@ -486,7 +486,11 @@ int main(int argc, char *argv[])
|
||||
arkode = new ARKStepSolver(ARKStepSolver::IMPLICIT);
|
||||
arkode->Init(*oper);
|
||||
arkode->SetSStolerances(reltol, abstol);
|
||||
#if MFEM_SUNDIALS_VERSION < 70100
|
||||
ARKStepSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
|
||||
#else
|
||||
ARKodeSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
|
||||
#endif
|
||||
arkode->SetMaxStep(dt);
|
||||
if (ode_solver_type == 15)
|
||||
{
|
||||
|
||||
@@ -541,7 +541,11 @@ int main(int argc, char *argv[])
|
||||
arkode = new ARKStepSolver(MPI_COMM_WORLD, ARKStepSolver::IMPLICIT);
|
||||
arkode->Init(*oper);
|
||||
arkode->SetSStolerances(reltol, abstol);
|
||||
#if MFEM_SUNDIALS_VERSION < 70100
|
||||
ARKStepSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
|
||||
#else
|
||||
ARKodeSetNonlinConvCoef(arkode->GetMem(), arkode_eps_nonlin);
|
||||
#endif
|
||||
arkode->SetMaxStep(dt);
|
||||
if (ode_solver_type == 15)
|
||||
{
|
||||
|
||||
@@ -447,7 +447,7 @@ ConductionOperator::ConductionOperator(FiniteElementSpace &fes,
|
||||
const Vector &u,
|
||||
const Type &ode_expression_type)
|
||||
: TimeDependentOperator(fes.GetTrueVSize(), 0.0, ode_expression_type),
|
||||
fespace(fes), alpha(alpha), kappa(kappa), M(&fespace), z(height)
|
||||
fespace(fes), M(&fespace), alpha(alpha), kappa(kappa), z(height)
|
||||
{
|
||||
// specify a relative tolerance for all solves with MFEM integrators
|
||||
const real_t rel_tol = 1e-8;
|
||||
@@ -522,7 +522,7 @@ int ConductionOperator::SUNImplicitSetup(const Vector &u, const Vector &fu,
|
||||
T = std::unique_ptr<SparseMatrix>(Add(1.0, Mmat, gam, Kmat));
|
||||
T_solver.SetOperator(*T);
|
||||
*jcur = SUNTRUE; // this should eventually only be set true if K(u) is used
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
|
||||
int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
@@ -544,7 +544,7 @@ int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
}
|
||||
if (T_solver.GetConverged())
|
||||
{
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -555,7 +555,7 @@ int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
int ConductionOperator::SUNMassSetup()
|
||||
{
|
||||
// Do nothing b/c mass solver was setup in constructor.
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
|
||||
int ConductionOperator::SUNMassSolve(const Vector &b, Vector &x, real_t tol)
|
||||
@@ -565,7 +565,7 @@ int ConductionOperator::SUNMassSolve(const Vector &b, Vector &x, real_t tol)
|
||||
M_solver.Mult(b, x);
|
||||
if (M_solver.GetConverged())
|
||||
{
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -577,6 +577,6 @@ int ConductionOperator::SUNMassMult(const Vector &x, Vector &v)
|
||||
{
|
||||
// Compute M x.
|
||||
Mmat.Mult(x, v);
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
@@ -499,7 +499,7 @@ ConductionOperator::ConductionOperator(ParFiniteElementSpace &fes,
|
||||
const Vector &u,
|
||||
const Type &ode_expression_type)
|
||||
: TimeDependentOperator(fes.GetTrueVSize(), 0.0, ode_expression_type),
|
||||
fespace(fes), alpha(alpha), kappa(kappa), M(&fespace),
|
||||
fespace(fes), M(&fespace), alpha(alpha), kappa(kappa),
|
||||
M_solver(fes.GetComm()), T_solver(fes.GetComm()), z(height)
|
||||
{
|
||||
// specify a relative tolerance for all solves with MFEM integrators
|
||||
@@ -576,7 +576,7 @@ int ConductionOperator::SUNImplicitSetup(const Vector &u, const Vector &fu,
|
||||
T = std::unique_ptr<HypreParMatrix>(Add(1.0, Mmat, gam, Kmat));
|
||||
T_solver.SetOperator(*T);
|
||||
*jcur = SUNTRUE; // this should eventually only be set true if K(u) is used
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
|
||||
int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
@@ -598,7 +598,7 @@ int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
}
|
||||
if (T_solver.GetConverged())
|
||||
{
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -609,7 +609,7 @@ int ConductionOperator::SUNImplicitSolve(const Vector &r, Vector &dk,
|
||||
int ConductionOperator::SUNMassSetup()
|
||||
{
|
||||
// Do nothing b/c mass solver was setup in constructor.
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
|
||||
int ConductionOperator::SUNMassSolve(const Vector &b, Vector &x, real_t tol)
|
||||
@@ -619,7 +619,7 @@ int ConductionOperator::SUNMassSolve(const Vector &b, Vector &x, real_t tol)
|
||||
M_solver.Mult(b, x);
|
||||
if (M_solver.GetConverged())
|
||||
{
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -631,5 +631,5 @@ int ConductionOperator::SUNMassMult(const Vector &x, Vector &v)
|
||||
{
|
||||
// Compute M x.
|
||||
Mmat.Mult(x, v);
|
||||
return SUNLS_SUCCESS;
|
||||
return SUN_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@ set(SRCS
|
||||
lor/lor_ads.cpp
|
||||
lor/lor_ams.cpp
|
||||
lor/lor_batched.cpp
|
||||
mdgridfunc.hpp
|
||||
multigrid.cpp
|
||||
nonlinearform.cpp
|
||||
nonlinearform_ext.cpp
|
||||
|
||||
+36
-5
@@ -3722,14 +3722,37 @@ private:
|
||||
the range space. Otherwise, a dof projection matrix is constructed. */
|
||||
class IdentityInterpolator : public DiscreteInterpolator
|
||||
{
|
||||
protected:
|
||||
const int vdim;
|
||||
|
||||
public:
|
||||
IdentityInterpolator(): dofquad_fe(NULL) { }
|
||||
/** @brief Construct an identity interpolator.
|
||||
|
||||
@param[in] vdim_ Vector dimension (number of components) in the domain
|
||||
and range FE spaces.
|
||||
*/
|
||||
IdentityInterpolator(int vdim_ = 1) : vdim(vdim_) { }
|
||||
|
||||
void AssembleElementMatrix2(const FiniteElement &dom_fe,
|
||||
const FiniteElement &ran_fe,
|
||||
ElementTransformation &Trans,
|
||||
DenseMatrix &elmat) override
|
||||
{ ran_fe.Project(dom_fe, Trans, elmat); }
|
||||
{
|
||||
if (vdim == 1)
|
||||
{
|
||||
ran_fe.Project(dom_fe, Trans, elmat);
|
||||
return;
|
||||
}
|
||||
DenseMatrix elmat_block;
|
||||
ran_fe.Project(dom_fe, Trans, elmat_block);
|
||||
elmat.SetSize(vdim*elmat_block.Height(), vdim*elmat_block.Width());
|
||||
elmat = 0_r;
|
||||
for (int i = 0; i < vdim; i++)
|
||||
{
|
||||
elmat.SetSubMatrix(i*elmat_block.Height(), i*elmat_block.Width(),
|
||||
elmat_block);
|
||||
}
|
||||
}
|
||||
|
||||
using BilinearFormIntegrator::AssemblePA;
|
||||
void AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
@@ -3738,11 +3761,9 @@ public:
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AddMultTransposePA(const Vector &x, Vector &y) const override;
|
||||
|
||||
virtual ~IdentityInterpolator() { delete dofquad_fe; }
|
||||
|
||||
private:
|
||||
/// 1D finite element that generates and owns the 1D DofToQuad maps below
|
||||
FiniteElement *dofquad_fe;
|
||||
std::unique_ptr<FiniteElement> dofquad_fe;
|
||||
|
||||
const DofToQuad *maps_C_C; // one-d map with Lobatto rows, Lobatto columns
|
||||
const DofToQuad *maps_O_C; // one-d map with Legendre rows, Lobatto columns
|
||||
@@ -3752,6 +3773,16 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/** @brief Class identical to IdentityInterpolator with the exception that it
|
||||
requires the vector dimension (number of components) to be specified during
|
||||
construction. */
|
||||
class VectorIdentityInterpolator : public IdentityInterpolator
|
||||
{
|
||||
public:
|
||||
VectorIdentityInterpolator(int vdim_) : IdentityInterpolator(vdim_) { }
|
||||
};
|
||||
|
||||
|
||||
/** Class for constructing the (local) discrete curl matrix which can be used
|
||||
as an integrator in a DiscreteLinearOperator object to assemble the global
|
||||
discrete curl matrix. */
|
||||
|
||||
@@ -798,6 +798,12 @@ public:
|
||||
/// Sets coefficient in the vector.
|
||||
void Set(int i, Coefficient *c, bool own=true);
|
||||
|
||||
/// Set ownership of the i'th coefficient
|
||||
void SetOwnership(int i, bool own) { ownCoeff[i] = own; }
|
||||
|
||||
/// Get ownership of the i'th coefficient
|
||||
bool GetOwnership(int i) const { return ownCoeff[i]; }
|
||||
|
||||
/// Evaluates i'th component of the vector of coefficients and returns the
|
||||
/// value.
|
||||
real_t Eval(int i, ElementTransformation &T, const IntegrationPoint &ip)
|
||||
@@ -1320,6 +1326,12 @@ public:
|
||||
can be overridden with the @a own parameter. */
|
||||
void Set(int i, int j, Coefficient * c, bool own=true);
|
||||
|
||||
/// Set ownership of the coefficient at (i,j) in the matrix
|
||||
void SetOwnership(int i, int j, bool own) { ownCoeff[i*width+j] = own; }
|
||||
|
||||
/// Get ownership of the coefficient at (i,j) in the matrix
|
||||
bool GetOwnership(int i, int j) const { return ownCoeff[i*width+j]; }
|
||||
|
||||
using MatrixCoefficient::Eval;
|
||||
|
||||
/// Evaluate coefficient located at (i,j) in the matrix using integration
|
||||
@@ -1360,6 +1372,12 @@ public:
|
||||
can be overridden with the @a own parameter. */
|
||||
void Set(int i, VectorCoefficient * c, bool own=true);
|
||||
|
||||
/// Set ownership of the i'th coefficient
|
||||
void SetOwnership(int i, bool own) { ownCoeff[i] = own; }
|
||||
|
||||
/// Get ownership of the i'th coefficient
|
||||
bool GetOwnership(int i) const { return ownCoeff[i]; }
|
||||
|
||||
using MatrixCoefficient::Eval;
|
||||
|
||||
/// Evaluate coefficient located at the i-th row of the matrix using integration
|
||||
|
||||
+1
-1
@@ -1245,7 +1245,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
|
||||
hypre_ParCSRMatrix *Aih = *Ah;
|
||||
Ah->HypreReadWrite();
|
||||
const int *d_ess_tdof_list =
|
||||
ess_tdof_list.GetMemory().Read(GetHypreMemoryClass(), n);
|
||||
ess_tdof_list.GetMemory().Read(GetHypreForallMemoryClass(), n);
|
||||
HYPRE_Int *d_diag_i = Aih->diag->i;
|
||||
real_t *d_diag_data = Aih->diag->data;
|
||||
mfem::hypre_forall(n, [=] MFEM_HOST_DEVICE (int k)
|
||||
|
||||
+7
-5
@@ -37,7 +37,7 @@ FindPointsGSLIB::FindPointsGSLIB()
|
||||
: mesh(NULL),
|
||||
fec_map_lin(NULL),
|
||||
fdata2D(NULL), fdata3D(NULL), cr(NULL), gsl_comm(NULL),
|
||||
dim(-1), points_cnt(0), setupflag(false), default_interp_value(0),
|
||||
dim(-1), points_cnt(-1), setupflag(false), default_interp_value(0),
|
||||
avgtype(AvgType::ARITHMETIC), bdr_tol(1e-8)
|
||||
{
|
||||
mesh_split.SetSize(4);
|
||||
@@ -85,7 +85,7 @@ FindPointsGSLIB::FindPointsGSLIB(MPI_Comm comm_)
|
||||
: mesh(NULL),
|
||||
fec_map_lin(NULL),
|
||||
fdata2D(NULL), fdata3D(NULL), cr(NULL), gsl_comm(NULL),
|
||||
dim(-1), points_cnt(0), setupflag(false), default_interp_value(0),
|
||||
dim(-1), points_cnt(-1), setupflag(false), default_interp_value(0),
|
||||
avgtype(AvgType::ARITHMETIC), bdr_tol(1e-8)
|
||||
{
|
||||
mesh_split.SetSize(4);
|
||||
@@ -307,6 +307,7 @@ void FindPointsGSLIB::FreeData()
|
||||
}
|
||||
if (fec_map_lin) { delete fec_map_lin; fec_map_lin = NULL; }
|
||||
setupflag = false;
|
||||
points_cnt = -1;
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::SetupSplitMeshes()
|
||||
@@ -897,7 +898,8 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
int gf_order_h1 = std::max(gf_order, 1); // H1 should be at least order 1
|
||||
H1_FECollection fec(gf_order_h1, dim);
|
||||
const int ncomp = field_in.FESpace()->GetVDim();
|
||||
FiniteElementSpace fes(mesh, &fec, ncomp);
|
||||
FiniteElementSpace fes(mesh, &fec, ncomp,
|
||||
field_in.FESpace()->GetOrdering());
|
||||
GridFunction field_in_h1(&fes);
|
||||
|
||||
if (avgtype == AvgType::ARITHMETIC)
|
||||
@@ -927,7 +929,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
|
||||
{
|
||||
for (int i = 0; i < indl2.Size(); i++)
|
||||
{
|
||||
int idx = field_in.FESpace()->GetOrdering() == Ordering::byNODES ?
|
||||
int idx = field_in_h1.FESpace()->GetOrdering() == Ordering::byNODES?
|
||||
indl2[i] + j*points_cnt:
|
||||
indl2[i]*ncomp + j;
|
||||
field_out(idx) = field_out_l2(idx);
|
||||
@@ -1172,7 +1174,7 @@ void FindPointsGSLIB::DistributePointInfoToOwningMPIRanks(
|
||||
Array<unsigned int> &recv_elem, Vector &recv_ref,
|
||||
Array<unsigned int> &recv_code)
|
||||
{
|
||||
MFEM_VERIFY(points_cnt,
|
||||
MFEM_VERIFY(points_cnt >= 0,
|
||||
"Invalid size. Please make sure to call FindPoints method "
|
||||
"before calling this function.");
|
||||
|
||||
|
||||
@@ -1819,10 +1819,12 @@ void IdentityInterpolator::AssemblePA(const FiniteElementSpace &trial_fes,
|
||||
|
||||
MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(), "");
|
||||
|
||||
MFEM_VERIFY(vdim == 1, "vdim != 1 with PA is not supported yet!");
|
||||
|
||||
ne = trial_fes.GetNE();
|
||||
|
||||
const int order = trial_el->GetOrder();
|
||||
dofquad_fe = new H1_SegmentElement(order);
|
||||
dofquad_fe.reset(new H1_SegmentElement(order));
|
||||
mfem::QuadratureFunctions1D qf1d;
|
||||
mfem::IntegrationRule closed_ir;
|
||||
closed_ir.SetSize(order + 1);
|
||||
|
||||
+240
-22
@@ -31,6 +31,172 @@ void CutIntegrationRules::SetLevelSetProjectionOrder(int order)
|
||||
lsOrder = order;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
void AlgoimIntegrationRules::GetSurfaceIntegrationRule(ElementTransformation
|
||||
&Tr,
|
||||
IntegrationRule &result)
|
||||
{
|
||||
GenerateLSVector(Tr,LvlSet);
|
||||
|
||||
const int dim=pe->GetDim();
|
||||
int np1d=CutIntegrationRules::Order/2+1;
|
||||
if (dim==2)
|
||||
{
|
||||
LevelSet2D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<2>(ls,Algoim::BoundingBox<real_t,2>(0.0,1.0),
|
||||
2, -1, np1d);
|
||||
result.SetSize(q.nodes.size());
|
||||
result.SetOrder(CutIntegrationRules::Order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=result.IntPoint(i);
|
||||
ip.Set2w(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LevelSet3D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<3>(ls,Algoim::BoundingBox<real_t,3>(0.0,1.0),
|
||||
3, -1, np1d);
|
||||
|
||||
result.SetSize(q.nodes.size());
|
||||
result.SetOrder(CutIntegrationRules::Order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=result.IntPoint(i);
|
||||
ip.Set(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].x(2),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AlgoimIntegrationRules::GetVolumeIntegrationRule(ElementTransformation &Tr,
|
||||
IntegrationRule &result,
|
||||
const IntegrationRule *sir)
|
||||
{
|
||||
GenerateLSVector(Tr,LvlSet);
|
||||
|
||||
const int dim=pe->GetDim();
|
||||
int np1d=CutIntegrationRules::Order/2+1;
|
||||
if (dim==2)
|
||||
{
|
||||
LevelSet2D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<2>(ls,Algoim::BoundingBox<real_t,2>(0.0,1.0),
|
||||
-1, -1, np1d);
|
||||
result.SetSize(q.nodes.size());
|
||||
result.SetOrder(CutIntegrationRules::Order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=result.IntPoint(i);
|
||||
ip.Set2w(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LevelSet3D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<3>(ls,Algoim::BoundingBox<real_t,3>(0.0,1.0),
|
||||
-1, -1, np1d);
|
||||
|
||||
result.SetSize(q.nodes.size());
|
||||
result.SetOrder(CutIntegrationRules::Order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=result.IntPoint(i);
|
||||
ip.Set(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].x(2),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AlgoimIntegrationRules::GetSurfaceWeights(ElementTransformation &Tr,
|
||||
const IntegrationRule &sir,
|
||||
Vector &weights)
|
||||
{
|
||||
GenerateLSVector(Tr,LvlSet);
|
||||
|
||||
DenseMatrix bmat; // gradients of the shape functions in isoparametric space
|
||||
DenseMatrix pmat; // gradients of the shape functions in physical space
|
||||
Vector inormal; // normal to the level set in isoparametric space
|
||||
Vector tnormal; // normal to the level set in physical space
|
||||
bmat.SetSize(pe->GetDof(),pe->GetDim());
|
||||
pmat.SetSize(pe->GetDof(),pe->GetDim());
|
||||
inormal.SetSize(pe->GetDim());
|
||||
tnormal.SetSize(pe->GetDim());
|
||||
|
||||
weights.SetSize(sir.GetNPoints());
|
||||
|
||||
for (int j = 0; j < sir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = sir.IntPoint(j);
|
||||
Tr.SetIntPoint(&ip);
|
||||
pe->CalcDShape(ip,bmat);
|
||||
Mult(bmat, Tr.InverseJacobian(), pmat);
|
||||
// compute the normal to the LS in isoparametric space
|
||||
bmat.MultTranspose(lsvec,inormal);
|
||||
// compute the normal to the LS in physical space
|
||||
pmat.MultTranspose(lsvec,tnormal);
|
||||
weights[j]= tnormal.Norml2() / inormal.Norml2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AlgoimIntegrationRules::GenerateLSVector(ElementTransformation &Tr,
|
||||
Coefficient* lvlset)
|
||||
{
|
||||
//check if the coefficient is already projected
|
||||
if (currentElementNo==Tr.ElementNo)
|
||||
{
|
||||
if (currentLvlSet==lvlset)
|
||||
{
|
||||
if (currentGeometry==Tr.GetGeometryType())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentElementNo=Tr.ElementNo;
|
||||
|
||||
if (currentGeometry!=Tr.GetGeometryType())
|
||||
{
|
||||
delete le;
|
||||
delete pe;
|
||||
currentGeometry=Tr.GetGeometryType();
|
||||
if (Tr.GetGeometryType()==Geometry::Type::SQUARE)
|
||||
{
|
||||
pe=new H1Pos_QuadrilateralElement(lsOrder);
|
||||
le=new H1_QuadrilateralElement(lsOrder);
|
||||
}
|
||||
else if (Tr.GetGeometryType()==Geometry::Type::CUBE)
|
||||
{
|
||||
pe=new H1Pos_HexahedronElement(lsOrder);
|
||||
le=new H1_HexahedronElement(lsOrder);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Currently MFEM + Algoim supports only quads and hexes.");
|
||||
}
|
||||
|
||||
T.SetSize(pe->GetDof());
|
||||
pe->Project(*le,Tr,T);
|
||||
//The transformation matrix depends only on the geometry for change of basis
|
||||
}
|
||||
|
||||
currentLvlSet=lvlset;
|
||||
const IntegrationRule &ir=le->GetNodes();
|
||||
lsvec.SetSize(ir.GetNPoints());
|
||||
lsfun.SetSize(ir.GetNPoints());
|
||||
for (int i=0; i<ir.GetNPoints(); i++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(i);
|
||||
Tr.SetIntPoint(&ip);
|
||||
lsfun(i)=lvlset->Eval(Tr,ip);
|
||||
}
|
||||
T.Mult(lsfun,lsvec);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
|
||||
void MomentFittingIntRules::InitSurface(int order, Coefficient& levelset,
|
||||
@@ -175,6 +341,7 @@ void MomentFittingIntRules::ComputeFaceWeights(ElementTransformation& Tr)
|
||||
local_mesh.GetElementTransformation(0, &faceTrafo);
|
||||
|
||||
// The 3D face integrals are computed as 2D volumetric integrals.
|
||||
// The 2D face integrals are computed as 1D volumetric integrals.
|
||||
MomentFittingIntRules FaceRules(Order, *LvlSet, lsOrder);
|
||||
IntegrationRule FaceRule;
|
||||
FaceRules.GetVolumeIntegrationRule(faceTrafo, FaceRule);
|
||||
@@ -254,8 +421,56 @@ void MomentFittingIntRules::ComputeSurfaceWeights1D(ElementTransformation& Tr)
|
||||
}
|
||||
}
|
||||
|
||||
void MomentFittingIntRules::ComputeVolumeWeights1D(ElementTransformation& Tr,
|
||||
const IntegrationRule* sir)
|
||||
double bisect(ElementTransformation &Tr, Coefficient *LvlSet)
|
||||
{
|
||||
IntegrationPoint intp;
|
||||
|
||||
IntegrationPoint ip0;
|
||||
ip0.x = 0.;
|
||||
IntegrationPoint ip1;
|
||||
ip1.x = 1.;
|
||||
Tr.SetIntPoint(&ip0);
|
||||
if (LvlSet->Eval(Tr, ip0) * LvlSet->Eval(Tr, ip1) < 0.)
|
||||
{
|
||||
IntegrationPoint ip2;
|
||||
ip2.x = .5;
|
||||
while (LvlSet->Eval(Tr, ip2) > 1e-12
|
||||
|| LvlSet->Eval(Tr, ip2) < -1e-12)
|
||||
{
|
||||
if (LvlSet->Eval(Tr, ip0) * LvlSet->Eval(Tr, ip2) < 0.)
|
||||
{
|
||||
ip1.x = ip2.x;
|
||||
}
|
||||
else
|
||||
{
|
||||
ip0.x = ip2.x;
|
||||
}
|
||||
|
||||
ip2.x = (ip1.x + ip0.x) / 2.;
|
||||
}
|
||||
intp.x = ip2.x;
|
||||
intp.weight = 1. / Tr.Weight();
|
||||
}
|
||||
else if (LvlSet->Eval(Tr, ip0) > 0. && LvlSet->Eval(Tr, ip1) <= 1e-12)
|
||||
{
|
||||
intp.x = 1.;
|
||||
intp.weight = 1. / Tr.Weight();
|
||||
}
|
||||
else if (LvlSet->Eval(Tr, ip1) > 0. && LvlSet->Eval(Tr, ip0) <= 1e-12)
|
||||
{
|
||||
intp.x = 0.;
|
||||
intp.weight = 1. / Tr.Weight();
|
||||
}
|
||||
else
|
||||
{
|
||||
intp.x = .5;
|
||||
intp.weight = 0.;
|
||||
}
|
||||
|
||||
return intp.x;
|
||||
}
|
||||
|
||||
void MomentFittingIntRules::ComputeVolumeWeights1D(ElementTransformation& Tr)
|
||||
{
|
||||
IntegrationRules irs(0, Quadrature1D::GaussLegendre);
|
||||
IntegrationRule ir2 = irs.Get(Geometry::SEGMENT, ir.GetOrder());
|
||||
@@ -271,7 +486,7 @@ void MomentFittingIntRules::ComputeVolumeWeights1D(ElementTransformation& Tr,
|
||||
real_t length;
|
||||
if (LvlSet->Eval(Tr, ip0) > 0.)
|
||||
{
|
||||
length = sir->IntPoint(0).x;
|
||||
length = bisect(Tr, LvlSet);
|
||||
for (int ip = 0; ip < ir.GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = ir.IntPoint(ip);
|
||||
@@ -281,11 +496,11 @@ void MomentFittingIntRules::ComputeVolumeWeights1D(ElementTransformation& Tr,
|
||||
}
|
||||
else
|
||||
{
|
||||
length = 1. - sir->IntPoint(0).x;
|
||||
length = 1. - bisect(Tr, LvlSet);
|
||||
for (int ip = 0; ip < ir.GetNPoints(); ip++)
|
||||
{
|
||||
IntegrationPoint &intp = ir.IntPoint(ip);
|
||||
intp.x = sir->IntPoint(ip).x + ir2.IntPoint(ip).x * length;
|
||||
intp.x = bisect(Tr, LvlSet) + ir2.IntPoint(ip).x * length;
|
||||
intp.weight = ir2.IntPoint(ip).weight * length;
|
||||
}
|
||||
}
|
||||
@@ -1491,26 +1706,29 @@ void MomentFittingIntRules::GetVolumeIntegrationRule(ElementTransformation& Tr,
|
||||
}
|
||||
|
||||
IntegrationRule SIR;
|
||||
if (sir == NULL)
|
||||
{
|
||||
Order++;
|
||||
GetSurfaceIntegrationRule(Tr, SIR);
|
||||
Order--;
|
||||
}
|
||||
else if ((sir->GetOrder() - 1) != ir.GetOrder())
|
||||
{
|
||||
Order++;
|
||||
GetSurfaceIntegrationRule(Tr, SIR);
|
||||
Order--;
|
||||
}
|
||||
else
|
||||
{
|
||||
SIR = *sir;
|
||||
}
|
||||
|
||||
if (Tr.GetDimension() == 1)
|
||||
{
|
||||
ComputeVolumeWeights1D(Tr, &SIR);
|
||||
Clear();
|
||||
InitVolume(Order, *LvlSet, lsOrder, Tr);
|
||||
}
|
||||
else if (sir == NULL)
|
||||
{
|
||||
Order++;
|
||||
GetSurfaceIntegrationRule(Tr, SIR);
|
||||
Order--;
|
||||
}
|
||||
else if (sir->GetOrder() - 1 != ir.GetOrder())
|
||||
{
|
||||
Order++;
|
||||
GetSurfaceIntegrationRule(Tr, SIR);
|
||||
Order--;
|
||||
}
|
||||
else { SIR = *sir; }
|
||||
|
||||
if (Tr.GetDimension() == 1)
|
||||
{
|
||||
ComputeVolumeWeights1D(Tr);
|
||||
}
|
||||
else if (Tr.GetDimension() == 2)
|
||||
{
|
||||
|
||||
+354
-3
@@ -18,6 +18,16 @@
|
||||
#include "eltrans.hpp"
|
||||
#include "coefficient.hpp"
|
||||
|
||||
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
#include <algoim_quad.hpp>
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/**
|
||||
@@ -116,6 +126,349 @@ public:
|
||||
virtual ~CutIntegrationRules() {}
|
||||
};
|
||||
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
// define templated element bases
|
||||
namespace TmplPoly_1D
|
||||
{
|
||||
|
||||
/// Templated version of CalcBinomTerms
|
||||
template<typename float_type>
|
||||
void CalcBinomTerms(const int p, const float_type x, const float_type y,
|
||||
float_type* u)
|
||||
{
|
||||
if (p == 0)
|
||||
{
|
||||
u[0] = float_type(1.);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
const int *b = Poly_1D::Binom(p);
|
||||
float_type z = x;
|
||||
for (i = 1; i < p; i++)
|
||||
{
|
||||
u[i] = b[i]*z;
|
||||
z *= x;
|
||||
}
|
||||
u[p] = z;
|
||||
z = y;
|
||||
for (i--; i > 0; i--)
|
||||
{
|
||||
u[i] *= z;
|
||||
z *= y;
|
||||
}
|
||||
u[0] = z;
|
||||
}
|
||||
}
|
||||
|
||||
/// Templated version of CalcBinomTerms
|
||||
template<typename float_type>
|
||||
void CalcBinomTerms(const int p, const float_type x, const float_type y,
|
||||
float_type* u, float_type* d)
|
||||
{
|
||||
if (p == 0)
|
||||
{
|
||||
u[0] = float_type(1.);
|
||||
d[0] = float_type(0.);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
const int *b = Poly_1D::Binom(p);
|
||||
const float_type xpy = x + y, ptx = p*x;
|
||||
float_type z = float_type(1.);
|
||||
|
||||
for (i = 1; i < p; i++)
|
||||
{
|
||||
d[i] = b[i]*z*(i*xpy - ptx);
|
||||
z *= x;
|
||||
u[i] = b[i]*z;
|
||||
}
|
||||
d[p] = p*z;
|
||||
u[p] = z*x;
|
||||
z = float_type(1.);
|
||||
for (i--; i > 0; i--)
|
||||
{
|
||||
d[i] *= z;
|
||||
z *= y;
|
||||
u[i] *= z;
|
||||
}
|
||||
d[0] = -p*z;
|
||||
u[0] = z*y;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Templated evaluation of Bernstein basis
|
||||
template <typename float_type>
|
||||
void CalcBernstein(const int p, const float_type x, float_type *u)
|
||||
{
|
||||
CalcBinomTerms(p, x, 1. - x, u);
|
||||
}
|
||||
|
||||
|
||||
/// Templated evaluation of Bernstein basis
|
||||
template <typename float_type>
|
||||
void CalcBernstein(const int p, const float_type x,
|
||||
float_type *u, float_type *d)
|
||||
{
|
||||
CalcBinomTerms(p, x, 1. - x, u, d);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class AlgoimIntegrationRules : public CutIntegrationRules
|
||||
{
|
||||
public:
|
||||
|
||||
/** @brief Constructor to set up the generated cut IntegrationRules.
|
||||
|
||||
@param [in] order Order of the constructed IntegrationRule.
|
||||
@param [in] lvlset Coefficient whose zero level set specifies the cut.
|
||||
@param [in] lsO Polynomial degree for projecting the level-set
|
||||
Coefficient to a GridFunction, which is used to
|
||||
compute gradients and normals. */
|
||||
AlgoimIntegrationRules(int order, Coefficient &lvlset, int lsO = 2)
|
||||
: CutIntegrationRules(order, lvlset, lsO)
|
||||
{
|
||||
pe=nullptr;
|
||||
le=nullptr;
|
||||
currentLvlSet=nullptr;
|
||||
currentGeometry=Geometry::Type::INVALID;
|
||||
currentElementNo = -1;
|
||||
}
|
||||
|
||||
virtual ~AlgoimIntegrationRules()
|
||||
{
|
||||
delete pe;
|
||||
delete le;
|
||||
}
|
||||
|
||||
virtual void SetOrder(int order) override
|
||||
{
|
||||
MFEM_VERIFY(order > 0, "Invalid input");
|
||||
Order = order;
|
||||
delete pe;
|
||||
delete le;
|
||||
pe=nullptr;
|
||||
le=nullptr;
|
||||
currentLvlSet=nullptr;
|
||||
currentGeometry=Geometry::Type::INVALID;
|
||||
currentElementNo=-1;
|
||||
}
|
||||
|
||||
virtual void SetLevelSetProjectionOrder(int order) override
|
||||
{
|
||||
MFEM_VERIFY(order > 0, "Invalid input");
|
||||
lsOrder = order;
|
||||
delete pe;
|
||||
delete le;
|
||||
pe=nullptr;
|
||||
le=nullptr;
|
||||
currentLvlSet=nullptr;
|
||||
currentGeometry=Geometry::Type::INVALID;
|
||||
currentElementNo=-1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Construct a cut-surface IntegrationRule.
|
||||
|
||||
Construct an IntegrationRule to integrate on the surface given by the
|
||||
already specified level set function, for the element given by @a Tr.
|
||||
|
||||
@param [in] Tr Specifies the IntegrationRule's associated mesh element.
|
||||
@param [out] result IntegrationRule on the cut-surface
|
||||
*/
|
||||
virtual
|
||||
void GetSurfaceIntegrationRule(ElementTransformation &Tr,
|
||||
IntegrationRule &result) override;
|
||||
|
||||
/**
|
||||
@brief Construct a cut-volume IntegrationRule.
|
||||
|
||||
Construct an IntegrationRule to integrate in the subdomain given by the
|
||||
positive values of the already specified level set function, for the element
|
||||
given by @a Tr.
|
||||
|
||||
@param [in] Tr Specifies the IntegrationRule's associated mesh element.
|
||||
@param [out] result IntegrationRule for the cut-volume
|
||||
@param [in] sir Corresponding IntegrationRule for the surface, which can
|
||||
be used to avoid computations.
|
||||
*/
|
||||
virtual
|
||||
void GetVolumeIntegrationRule(ElementTransformation &Tr,
|
||||
IntegrationRule &result,
|
||||
const IntegrationRule *sir = nullptr) override;
|
||||
|
||||
|
||||
/**
|
||||
@brief Compute transformation quadrature weights for surface integration.
|
||||
|
||||
Compute the transformation weights for integration over the cut-surface in
|
||||
reference space.
|
||||
|
||||
@param [in] Tr Specifies the IntegrationRule's associated element.
|
||||
@param [in] sir IntegrationRule defining the IntegrationPoints
|
||||
@param [out] weights Vector containing the transformation weights.
|
||||
*/
|
||||
virtual
|
||||
void GetSurfaceWeights(ElementTransformation &Tr,
|
||||
const IntegrationRule &sir,
|
||||
Vector &weights) override;
|
||||
|
||||
private:
|
||||
|
||||
/// projects the lvlset coefficient onto the lsvec,
|
||||
/// i.e., represent the level-set using Bernstein bases
|
||||
void GenerateLSVector(ElementTransformation &Tr, Coefficient* lvlset);
|
||||
|
||||
|
||||
/// Lagrange finite element used for converting coefficients to positive basis
|
||||
FiniteElement* le;
|
||||
PositiveTensorFiniteElement *pe;
|
||||
DenseMatrix T; //Projection matrix from nodal basis to positive basis
|
||||
Vector lsvec; // level-set in Bernstein basis
|
||||
Vector lsfun; // level-set in nodal basis
|
||||
Geometry::Type currentGeometry; // the current element geometry
|
||||
Coefficient* currentLvlSet; //the current level-set coefficient
|
||||
int currentElementNo; //the current element No
|
||||
|
||||
/// 3D level-set function object required by Algoim.
|
||||
struct LevelSet3D
|
||||
{
|
||||
/// Constructor for 3D level-set function object required by Algoim.
|
||||
LevelSet3D(PositiveTensorFiniteElement* el_, Vector& lsfun_)
|
||||
: el(el_), lsfun(lsfun_) { }
|
||||
|
||||
/// Returns the value of the LSF for point x.
|
||||
template<typename T>
|
||||
T operator() (const blitz::TinyVector<T,3>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T u3[el_order+1];
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[2], u3);
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
T res=T(0.0);
|
||||
for (int oo = 0, kk = 0; kk <= el_order; kk++)
|
||||
for (int jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res=res-u1[ii]*u2[jj]*u3[kk]*lsfun(dof_map[oo++]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns the gradients of the LSF for point x.
|
||||
template<typename T>
|
||||
blitz::TinyVector<T,3> grad(const blitz::TinyVector<T,3>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T u3[el_order+1];
|
||||
T d1[el_order+1];
|
||||
T d2[el_order+1];
|
||||
T d3[el_order+1];
|
||||
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[2], u3, d3);
|
||||
|
||||
blitz::TinyVector<T,3> res(T(0.0),T(0.0),T(0.0));
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
for (int oo = 0, kk = 0; kk <= el_order; kk++)
|
||||
for (int jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res[0]=res[0]-d1[ii]*u2[jj]*u3[kk]*lsfun(dof_map[oo]);
|
||||
res[1]=res[1]-u1[ii]*d2[jj]*u3[kk]*lsfun(dof_map[oo]);
|
||||
res[2]=res[2]-u1[ii]*u2[jj]*d3[kk]*lsfun(dof_map[oo]);
|
||||
oo++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
PositiveTensorFiniteElement* el;
|
||||
Vector& lsfun;
|
||||
};
|
||||
|
||||
/// 2D level-set function object required by Algoim.
|
||||
struct LevelSet2D
|
||||
{
|
||||
/// Constructor for 2D level-set function object required by Algoim.
|
||||
LevelSet2D(PositiveTensorFiniteElement* el_, Vector& lsfun_)
|
||||
:el(el_), lsfun(lsfun_) { }
|
||||
|
||||
/// Returns the value of the LSF for point x.
|
||||
template<typename T>
|
||||
T operator() (const blitz::TinyVector<T,2>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
T res=T(0.0);
|
||||
|
||||
for (int oo = 0, jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res=res-u1[ii]*u2[jj]*lsfun(dof_map[oo++]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns the gradients of the LSF for point x.
|
||||
template<typename T>
|
||||
blitz::TinyVector<T,2> grad(const blitz::TinyVector<T,2>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T d1[el_order+1];
|
||||
T d2[el_order+1];
|
||||
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
|
||||
|
||||
blitz::TinyVector<T,2> res(T(0.0),T(0.0));
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
for (int oo = 0, jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res[0]=res[0]-(d1[ii]*u2[jj])*lsfun(dof_map[oo]);
|
||||
res[1]=res[1]-(u1[ii]*d2[jj])*lsfun(dof_map[oo]);
|
||||
oo++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
PositiveTensorFiniteElement* el;
|
||||
Vector& lsfun;
|
||||
};
|
||||
};
|
||||
#endif //MFEM_USE_ALGOIM
|
||||
|
||||
#ifdef MFEM_USE_LAPACK
|
||||
|
||||
/**
|
||||
@@ -212,10 +565,8 @@ protected:
|
||||
rule.
|
||||
|
||||
@param [in] Tr ElementTransformation of the current element
|
||||
@param [in] sir corresponding IntegrationRule on surface
|
||||
*/
|
||||
void ComputeVolumeWeights1D(ElementTransformation& Tr,
|
||||
const IntegrationRule* sir);
|
||||
void ComputeVolumeWeights1D(ElementTransformation& Tr);
|
||||
|
||||
/**
|
||||
@brief Compute 2D quadrature weights
|
||||
|
||||
@@ -162,7 +162,7 @@ public:
|
||||
{
|
||||
std::tuple<Params...> param_tuple(PARAMS...);
|
||||
Kernels::Get().table[param_tuple] =
|
||||
Kernels:: template Kernel<PARAMS...>();
|
||||
Kernels:: template Kernel<PARAMS..., OptParams{}...>();
|
||||
};
|
||||
// Version with optional parameters
|
||||
template <OptParams... OPT_PARAMS>
|
||||
|
||||
+7
-13
@@ -242,13 +242,13 @@ void BatchedLOR_AMS::FormGradientMatrix()
|
||||
template <typename T>
|
||||
static inline const T *HypreRead(const Memory<T> &mem)
|
||||
{
|
||||
return mem.Read(GetHypreMemoryClass(), mem.Capacity());
|
||||
return mem.Read(GetHypreForallMemoryClass(), mem.Capacity());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T *HypreWrite(Memory<T> &mem)
|
||||
{
|
||||
return mem.Write(GetHypreMemoryClass(), mem.Capacity());
|
||||
return mem.Write(GetHypreForallMemoryClass(), mem.Capacity());
|
||||
}
|
||||
|
||||
void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
@@ -278,10 +278,7 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
const int sdim = vert_fes.GetMesh()->SpaceDimension();
|
||||
const int ntdofs = R->Height();
|
||||
|
||||
const MemoryClass mc = GetHypreMemoryClass();
|
||||
bool dev = (mc == MemoryClass::DEVICE);
|
||||
|
||||
xyz_tvec = new Vector(ntdofs*sdim);
|
||||
xyz_tvec = new Vector(ntdofs*sdim, GetHypreMemoryType());
|
||||
|
||||
auto xyz_tv = Reshape(HypreWrite(xyz_tvec->GetMemory()), ntdofs, sdim);
|
||||
const auto xyz_e =
|
||||
@@ -304,15 +301,12 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
|
||||
// Make x, y, z HypreParVectors point to T-vector data
|
||||
HYPRE_BigInt glob_size = vert_fes.GlobalTrueVSize();
|
||||
HYPRE_BigInt *cols = vert_fes.GetTrueDofOffsets();
|
||||
|
||||
real_t *d_x_ptr = xyz_tv + 0*ntdofs;
|
||||
x = new HypreParVector(vert_fes.GetComm(), glob_size, d_x_ptr, cols, dev);
|
||||
real_t *d_y_ptr = xyz_tv + 1*ntdofs;
|
||||
y = new HypreParVector(vert_fes.GetComm(), glob_size, d_y_ptr, cols, dev);
|
||||
MPI_Comm comm = vert_fes.GetComm();
|
||||
x = new HypreParVector(comm, glob_size, *xyz_tvec, 0*ntdofs, cols);
|
||||
y = new HypreParVector(comm, glob_size, *xyz_tvec, 1*ntdofs, cols);
|
||||
if (sdim == 3)
|
||||
{
|
||||
real_t *d_z_ptr = xyz_tv + 2*ntdofs;
|
||||
z = new HypreParVector(vert_fes.GetComm(), glob_size, d_z_ptr, cols, dev);
|
||||
z = new HypreParVector(comm, glob_size, *xyz_tvec, 2*ntdofs, cols);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2010-2023, 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_MDGRIDFUNC
|
||||
#define MFEM_MDGRIDFUNC
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#include "fem/gridfunc.hpp"
|
||||
#include "general/mdspan.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template<int N, class Layout = MDLayoutLeft<N>>
|
||||
class MDGridFunction : public MDSpan<GridFunction, N, Layout>
|
||||
{
|
||||
using base_t = MDSpan<GridFunction, N, Layout>;
|
||||
using base_t::Nd;
|
||||
using base_t::Sd;
|
||||
using GridFunction::data;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief MDGridFunction default constructor (recursion)
|
||||
*/
|
||||
MDGridFunction(): base_t() { }
|
||||
|
||||
/**
|
||||
* @brief MDGridFunction recursion constructor
|
||||
* @param[in] fes Finite element space to use
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <typename... Ts>
|
||||
MDGridFunction(FiniteElementSpace *fes, Ts... args): MDGridFunction(args...)
|
||||
{
|
||||
SetSpace(fes);
|
||||
MFEM_VERIFY(fes->GetVDim() == 1,
|
||||
"Only FiniteElementSpace with vdim of 1 are supported");
|
||||
base_t::Setup(fes->GetNDofs(), args...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MDGridFunction recursion constructor
|
||||
* @param[in] dim Dimension indice
|
||||
* @param[in] args Rest of dimension indices or finite element space to use
|
||||
*/
|
||||
template <typename... Ts>
|
||||
MDGridFunction(int dim, Ts... args): MDGridFunction(args...)
|
||||
{
|
||||
base_t::Setup(dim, args...);
|
||||
}
|
||||
|
||||
/// Move constructor not supported
|
||||
MDGridFunction(MDGridFunction&&) = delete;
|
||||
|
||||
/// Copy constructor not supported
|
||||
MDGridFunction(const MDGridFunction&) = delete;
|
||||
|
||||
/// Move assignment not supported
|
||||
MDGridFunction& operator=(MDGridFunction&&) = delete;
|
||||
|
||||
/// Copy assignment not supported
|
||||
MDGridFunction& operator=(const MDGridFunction&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Returns the specific GridFunction from dimension indices
|
||||
* @param[out] gf Returned GridFunction
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <int n = 1, typename... Ts>
|
||||
void GetScalarGridFunction(GridFunction &gf, Ts... args) const
|
||||
{
|
||||
FiniteElementSpace *fes = GridFunction::fes;
|
||||
MFEM_VERIFY(fes->GetNDofs() == Nd[n-1], "Error in dofs size!");
|
||||
gf.SetSpace(fes);
|
||||
for (int s = 0; s < Nd[n-1]; s++)
|
||||
{
|
||||
gf[s] = data[get_vdofs_offset +
|
||||
MDOffset<n,N,int,Ts...>::offset(Sd, s, args...)];
|
||||
}
|
||||
get_vdofs_offset = 0; // re-init for next calls
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the specific GridFunction from dimension indices
|
||||
* @param[in] dim Dimension indice
|
||||
* @param args Rest of dimension indices or GridFunction to be returned
|
||||
*/
|
||||
template <int n = 1, typename... Ts>
|
||||
void GetScalarGridFunction(int dim, Ts&&... args) const
|
||||
{
|
||||
get_vdofs_offset += dim * Sd[n-1];
|
||||
MDGridFunction::GetScalarGridFunction<n+1>(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the given GridFunction at the specific dimension indices
|
||||
* @param[in] gf GridFunction to set
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <int n = 1, typename... Ts>
|
||||
void SetScalarGridFunction(const GridFunction &gf, Ts... args)
|
||||
{
|
||||
MFEM_VERIFY(GridFunction::fes->GetNDofs() == Nd[n-1], "Error in dofs size!");
|
||||
for (int s = 0; s < Nd[n-1]; s++)
|
||||
{
|
||||
data[get_vdofs_offset +
|
||||
MDOffset<n,N,int,Ts...>::offset(Sd, s, args...)] = gf[s];
|
||||
}
|
||||
get_vdofs_offset = 0; // re-init for next calls
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the given GridFunction at the specific dimension indices
|
||||
* @param[in] dim Dimension indice
|
||||
* @param args Rest of dimension indices or given GridFunction to be used
|
||||
*/
|
||||
template <int n = 1, typename... Ts>
|
||||
void SetScalarGridFunction(int dim, Ts... args)
|
||||
{
|
||||
get_vdofs_offset += dim * Sd[n-1];
|
||||
MDGridFunction::SetScalarGridFunction<n+1>(args...);
|
||||
}
|
||||
|
||||
using GridFunction::Read;
|
||||
using GridFunction::Write;
|
||||
using GridFunction::ReadWrite;
|
||||
using GridFunction::HostRead;
|
||||
using GridFunction::HostWrite;
|
||||
using GridFunction::HostReadWrite;
|
||||
|
||||
using GridFunction::GetData;
|
||||
using GridFunction::SetData;
|
||||
using GridFunction::SetSpace;
|
||||
|
||||
using Vector::operator=;
|
||||
|
||||
private:
|
||||
mutable int get_vdofs_offset = 0;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_MDGRIDFUNC
|
||||
+229
-9
@@ -43,12 +43,13 @@ static void Derivatives1D(const int NE,
|
||||
const int q1d)
|
||||
{
|
||||
MFEM_CONTRACT_VAR(b_);
|
||||
const int SDIM = GRAD_PHYS ? sdim : 1;
|
||||
const auto g = Reshape(g_, q1d, d1d);
|
||||
const auto j = Reshape(j_, q1d, sdim, NE);
|
||||
const auto j = Reshape(j_, q1d, SDIM, NE);
|
||||
const auto x = Reshape(x_, d1d, vdim, NE);
|
||||
auto y = Q_LAYOUT == QVectorLayout::byNODES ?
|
||||
Reshape(y_, q1d, vdim, sdim, NE):
|
||||
Reshape(y_, vdim, sdim, q1d, NE);
|
||||
Reshape(y_, q1d, vdim, SDIM, NE):
|
||||
Reshape(y_, vdim, SDIM, q1d, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
@@ -63,8 +64,8 @@ static void Derivatives1D(const int NE,
|
||||
}
|
||||
if (GRAD_PHYS)
|
||||
{
|
||||
if (sdim == 1) { du[0] /= j(q, 0, e); }
|
||||
else if (sdim == 2)
|
||||
if (SDIM == 1) { du[0] /= j(q, 0, e); }
|
||||
else if (SDIM == 2)
|
||||
{
|
||||
const real_t Jloc[2] = {j(q,0,e), j(q,1,e)};
|
||||
real_t Jinv[3];
|
||||
@@ -74,7 +75,7 @@ static void Derivatives1D(const int NE,
|
||||
du[0] = U;
|
||||
du[1] = V;
|
||||
}
|
||||
else // sdim == 3
|
||||
else // SDIM == 3
|
||||
{
|
||||
const real_t Jloc[3] = {j(q,0,e), j(q,1,e), j(q,2,e)};
|
||||
real_t Jinv[3];
|
||||
@@ -87,7 +88,7 @@ static void Derivatives1D(const int NE,
|
||||
du[2] = W;
|
||||
}
|
||||
}
|
||||
for (int d = 0; d < sdim; ++d)
|
||||
for (int d = 0; d < SDIM; ++d)
|
||||
{
|
||||
if (Q_LAYOUT == QVectorLayout::byVDIM) { y(c, d, q, e) = du[d]; }
|
||||
if (Q_LAYOUT == QVectorLayout::byNODES) { y(q, c, d, e) = du[d]; }
|
||||
@@ -372,14 +373,222 @@ static void Derivatives3D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS>
|
||||
static void CollocatedDerivatives1D(const int NE,
|
||||
const real_t *g_,
|
||||
const real_t *j_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int sdim,
|
||||
const int vdim,
|
||||
const int d1d)
|
||||
{
|
||||
Derivatives1D<Q_LAYOUT, GRAD_PHYS>(
|
||||
NE, nullptr, g_, j_, x_, y_, sdim, vdim, d1d, d1d);
|
||||
}
|
||||
|
||||
// Template compute kernel for derivatives in 2D: tensor product version.
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS,
|
||||
int T_VDIM = 0, int T_D1D = 0,
|
||||
int T_NBZ = 1>
|
||||
static void CollocatedDerivatives2D(const int NE,
|
||||
const real_t *g_,
|
||||
const real_t *j_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int sdim = 2,
|
||||
const int vdim = 0,
|
||||
const int d1d = 0)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
const int SDIM = GRAD_PHYS ? sdim : 2;
|
||||
static constexpr int NBZ = T_NBZ ? T_NBZ : 1;
|
||||
|
||||
const auto g = Reshape(g_, D1D, D1D);
|
||||
const auto j = Reshape(j_, D1D, D1D, SDIM, 2, NE);
|
||||
const auto x = Reshape(x_, D1D, D1D, VDIM, NE);
|
||||
auto y = Q_LAYOUT == QVectorLayout:: byNODES ?
|
||||
Reshape(y_, D1D, D1D, VDIM, SDIM, NE):
|
||||
Reshape(y_, VDIM, SDIM, D1D, D1D, NE);
|
||||
|
||||
mfem::forall_2D_batch(NE, D1D, D1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
|
||||
MFEM_SHARED real_t XY[NBZ][MD1*MD1];
|
||||
DeviceTensor<2> X((real_t*)(XY+tidz), D1D, D1D);
|
||||
|
||||
for (int c = 0; c < VDIM; ++c)
|
||||
{
|
||||
kernels::internal::LoadX<MD1,NBZ>(e,D1D,c,x,XY);
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
real_t v = 0.0;
|
||||
real_t w = 0.0;
|
||||
for (int dxy = 0; dxy < D1D; ++dxy)
|
||||
{
|
||||
u += X(dxy, dy) * g(dx,dxy);
|
||||
v += X(dx, dxy) * g(dy,dxy);
|
||||
}
|
||||
|
||||
if (GRAD_PHYS)
|
||||
{
|
||||
if (SDIM == 2)
|
||||
{
|
||||
real_t Jloc[4], Jinv[4];
|
||||
Jloc[0] = j(dx,dy,0,0,e);
|
||||
Jloc[1] = j(dx,dy,1,0,e);
|
||||
Jloc[2] = j(dx,dy,0,1,e);
|
||||
Jloc[3] = j(dx,dy,1,1,e);
|
||||
kernels::CalcInverse<2>(Jloc, Jinv);
|
||||
const real_t U = Jinv[0]*u + Jinv[1]*v;
|
||||
const real_t V = Jinv[2]*u + Jinv[3]*v;
|
||||
u = U;
|
||||
v = V;
|
||||
}
|
||||
else
|
||||
{
|
||||
real_t Jloc[6], Jinv[6];
|
||||
Jloc[0] = j(dx,dy,0,0,e);
|
||||
Jloc[1] = j(dx,dy,1,0,e);
|
||||
Jloc[2] = j(dx,dy,2,0,e);
|
||||
Jloc[3] = j(dx,dy,0,1,e);
|
||||
Jloc[4] = j(dx,dy,1,1,e);
|
||||
Jloc[5] = j(dx,dy,2,1,e);
|
||||
kernels::CalcLeftInverse<3,2>(Jloc, Jinv);
|
||||
const real_t U = Jinv[0]*u + Jinv[1]*v;
|
||||
const real_t V = Jinv[2]*u + Jinv[3]*v;
|
||||
const real_t W = Jinv[4]*u + Jinv[5]*v;
|
||||
u = U;
|
||||
v = V;
|
||||
w = W;
|
||||
}
|
||||
}
|
||||
|
||||
if (Q_LAYOUT == QVectorLayout::byVDIM)
|
||||
{
|
||||
y(c,0,dx,dy,e) = u;
|
||||
y(c,1,dx,dy,e) = v;
|
||||
if (SDIM == 3) { y(c,2,dx,dy,e) = w; }
|
||||
}
|
||||
if (Q_LAYOUT == QVectorLayout::byNODES)
|
||||
{
|
||||
y(dx,dy,c,0,e) = u;
|
||||
y(dx,dy,c,1,e) = v;
|
||||
if (SDIM == 3) { y(dx,dy,c,2,e) = w; }
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Template compute kernel for derivatives in 3D: tensor product version.
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS,
|
||||
int T_VDIM = 0, int T_D1D = 0>
|
||||
static void CollocatedDerivatives3D(const int NE,
|
||||
const real_t *g_,
|
||||
const real_t *j_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int sdim = 3,
|
||||
const int vdim = 0,
|
||||
const int d1d = 0)
|
||||
{
|
||||
MFEM_VERIFY(sdim == 3, "");
|
||||
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
|
||||
const auto g = Reshape(g_, D1D, D1D);
|
||||
const auto j = Reshape(j_, D1D, D1D, D1D, 3, 3, NE);
|
||||
const auto x = Reshape(x_, D1D, D1D, D1D, VDIM, NE);
|
||||
auto y = Q_LAYOUT == QVectorLayout:: byNODES ?
|
||||
Reshape(y_, D1D, D1D, D1D, VDIM, 3, NE):
|
||||
Reshape(y_, VDIM, 3, D1D, D1D, D1D, NE);
|
||||
|
||||
mfem::forall_3D(NE, D1D, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int VDIM = T_VDIM ? T_VDIM : vdim;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_INTERP_1D;
|
||||
|
||||
MFEM_SHARED real_t uvw[MD1*MD1*MD1];
|
||||
DeviceTensor<3> X(uvw, D1D, D1D, D1D);
|
||||
|
||||
for (int c = 0; c < VDIM; ++c)
|
||||
{
|
||||
kernels::internal::LoadX(e,D1D,c,x,X);
|
||||
MFEM_FOREACH_THREAD(dz,z,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
real_t v = 0.0;
|
||||
real_t w = 0.0;
|
||||
for (int dxyz = 0; dxyz < D1D; ++dxyz)
|
||||
{
|
||||
u += X(dxyz, dy, dz) * g(dx,dxyz);
|
||||
v += X(dx, dxyz, dz) * g(dy,dxyz);
|
||||
w += X(dx, dy, dxyz) * g(dz,dxyz);
|
||||
}
|
||||
|
||||
if (GRAD_PHYS)
|
||||
{
|
||||
real_t Jloc[9], Jinv[9];
|
||||
for (int col = 0; col < 3; col++)
|
||||
{
|
||||
for (int row = 0; row < 3; row++)
|
||||
{
|
||||
Jloc[row+3*col] = j(dx,dy,dz,row,col,e);
|
||||
}
|
||||
}
|
||||
kernels::CalcInverse<3>(Jloc, Jinv);
|
||||
const real_t U = Jinv[0]*u + Jinv[1]*v + Jinv[2]*w;
|
||||
const real_t V = Jinv[3]*u + Jinv[4]*v + Jinv[5]*w;
|
||||
const real_t W = Jinv[6]*u + Jinv[7]*v + Jinv[8]*w;
|
||||
u = U; v = V; w = W;
|
||||
}
|
||||
if (Q_LAYOUT == QVectorLayout::byVDIM)
|
||||
{
|
||||
y(c,0,dx,dy,dz,e) = u;
|
||||
y(c,1,dx,dy,dz,e) = v;
|
||||
y(c,2,dx,dy,dz,e) = w;
|
||||
}
|
||||
if (Q_LAYOUT == QVectorLayout::byNODES)
|
||||
{
|
||||
y(dx,dy,dz,c,0,e) = u;
|
||||
y(dx,dy,dz,c,1,e) = v;
|
||||
y(dx,dy,dz,c,2,e) = w;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace quadrature_interpolator
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// @cond Suppress_Doxygen_warnings
|
||||
|
||||
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS,
|
||||
int VDIM, int D1D, int Q1D, int NBZ>
|
||||
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
int Q1D, int NBZ>
|
||||
QuadratureInterpolator::GradKernelType
|
||||
QuadratureInterpolator::GradKernels::Kernel()
|
||||
{
|
||||
@@ -389,6 +598,17 @@ QuadratureInterpolator::GradKernels::Kernel()
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
|
||||
int NBZ>
|
||||
QuadratureInterpolator::CollocatedGradKernelType
|
||||
QuadratureInterpolator::CollocatedGradKernels::Kernel()
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -23,50 +23,73 @@ template <bool P>
|
||||
void InitGradByNodesKernels()
|
||||
{
|
||||
using k = QuadratureInterpolator::GradKernels;
|
||||
constexpr auto L = QVectorLayout::byNODES;
|
||||
// 2D
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,1,3,3>::template Opt<16>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,1,3,4>::template Opt<16>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,1,4,3>::template Opt<16>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,1,4,4>::template Opt<16>::Add();
|
||||
k::Specialization<2,L,P,1,3,3>::template Opt<16>::Add();
|
||||
k::Specialization<2,L,P,1,3,4>::template Opt<16>::Add();
|
||||
k::Specialization<2,L,P,1,4,3>::template Opt<16>::Add();
|
||||
k::Specialization<2,L,P,1,4,4>::template Opt<16>::Add();
|
||||
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,2>::template Opt<16>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,3>::template Opt<8>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,4>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,5>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,2,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,2,2>::template Opt<16>::Add();
|
||||
k::Specialization<2,L,P,2,2,3>::template Opt<8>::Add();
|
||||
k::Specialization<2,L,P,2,2,4>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,2,5>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,2,6>::template Opt<2>::Add();
|
||||
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,3,3>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,3,4>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,3>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,3,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,3,3>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,3,4>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,4,3>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,3,6>::template Opt<2>::Add();
|
||||
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,4>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,5>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,4,7>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,4,4>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,4,5>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,4,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,4,7>::template Opt<2>::Add();
|
||||
|
||||
k::Specialization<2,QVectorLayout::byNODES,P,2,5,6>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,5,6>::template Opt<2>::Add();
|
||||
// 3D
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,2,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,3,3>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,3,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,3,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,4,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,1,4,8>::template Opt<1>::Add();
|
||||
k::Specialization<3,L,P,1,2,4>::Add();
|
||||
k::Specialization<3,L,P,1,3,3>::Add();
|
||||
k::Specialization<3,L,P,1,3,4>::Add();
|
||||
k::Specialization<3,L,P,1,3,6>::Add();
|
||||
k::Specialization<3,L,P,1,4,4>::Add();
|
||||
k::Specialization<3,L,P,1,4,8>::Add();
|
||||
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,2,3>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,2,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,2,5>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,2,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,L,P,3,2,3>::Add();
|
||||
k::Specialization<3,L,P,3,2,4>::Add();
|
||||
k::Specialization<3,L,P,3,2,5>::Add();
|
||||
k::Specialization<3,L,P,3,2,6>::Add();
|
||||
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,3,3>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,3,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,3,5>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,3,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,4,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,4,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,4,7>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byNODES,P,3,4,8>::template Opt<1>::Add();
|
||||
k::Specialization<3,L,P,3,3,3>::Add();
|
||||
k::Specialization<3,L,P,3,3,4>::Add();
|
||||
k::Specialization<3,L,P,3,3,5>::Add();
|
||||
k::Specialization<3,L,P,3,3,6>::Add();
|
||||
k::Specialization<3,L,P,3,4,4>::Add();
|
||||
k::Specialization<3,L,P,3,4,6>::Add();
|
||||
k::Specialization<3,L,P,3,4,7>::Add();
|
||||
k::Specialization<3,L,P,3,4,8>::Add();
|
||||
|
||||
using k2 = QuadratureInterpolator::CollocatedGradKernels;
|
||||
|
||||
// 2D
|
||||
k2::Specialization<2,L,P,1,2>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,1,3>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,1,4>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,2,2>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,2,3>::template Opt<4>::Add();
|
||||
k2::Specialization<2,L,P,2,4>::template Opt<2>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,1,2>::Add();
|
||||
k2::Specialization<3,L,P,1,3>::Add();
|
||||
k2::Specialization<3,L,P,1,4>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,2,2>::Add();
|
||||
k2::Specialization<3,L,P,2,3>::Add();
|
||||
k2::Specialization<3,L,P,2,4>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,3,2>::Add();
|
||||
k2::Specialization<3,L,P,3,3>::Add();
|
||||
k2::Specialization<3,L,P,3,4>::Add();
|
||||
}
|
||||
|
||||
template void InitGradByNodesKernels<true>();
|
||||
|
||||
@@ -23,22 +23,46 @@ template <bool P>
|
||||
void InitGradByVDimKernels()
|
||||
{
|
||||
using k = QuadratureInterpolator::GradKernels;
|
||||
constexpr auto L = QVectorLayout::byVDIM;
|
||||
// 2D
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,1,3,4>::template Opt<8>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,1,4,6>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,1,5,8>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,1,3,4>::template Opt<8>::Add();
|
||||
k::Specialization<2,L,P,1,4,6>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,1,5,8>::template Opt<2>::Add();
|
||||
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,2,3,3>::template Opt<8>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,2,3,4>::template Opt<8>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,2,4,6>::template Opt<4>::Add();
|
||||
k::Specialization<2,QVectorLayout::byVDIM,P,2,5,8>::template Opt<2>::Add();
|
||||
k::Specialization<2,L,P,2,3,3>::template Opt<8>::Add();
|
||||
k::Specialization<2,L,P,2,3,4>::template Opt<8>::Add();
|
||||
k::Specialization<2,L,P,2,4,6>::template Opt<4>::Add();
|
||||
k::Specialization<2,L,P,2,5,8>::template Opt<2>::Add();
|
||||
// 3D
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,1,3,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,1,4,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,1,5,8>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,3,3,4>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,3,4,6>::template Opt<1>::Add();
|
||||
k::Specialization<3,QVectorLayout::byVDIM,P,3,5,8>::template Opt<1>::Add();
|
||||
k::Specialization<3,L,P,1,3,4>::Add();
|
||||
k::Specialization<3,L,P,1,4,6>::Add();
|
||||
k::Specialization<3,L,P,1,5,8>::Add();
|
||||
k::Specialization<3,L,P,3,3,4>::Add();
|
||||
k::Specialization<3,L,P,3,4,6>::Add();
|
||||
k::Specialization<3,L,P,3,5,8>::Add();
|
||||
|
||||
using k2 = QuadratureInterpolator::CollocatedGradKernels;
|
||||
// 2D
|
||||
k2::Specialization<2,L,P,1,2>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,1,3>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,1,4>::template Opt<16>::Add();
|
||||
|
||||
k2::Specialization<2,L,P,2,2>::template Opt<16>::Add();
|
||||
k2::Specialization<2,L,P,2,3>::template Opt<4>::Add();
|
||||
k2::Specialization<2,L,P,2,4>::template Opt<2>::Add();
|
||||
|
||||
// 3D
|
||||
k2::Specialization<3,L,P,1,2>::Add();
|
||||
k2::Specialization<3,L,P,1,3>::Add();
|
||||
k2::Specialization<3,L,P,1,4>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,2,2>::Add();
|
||||
k2::Specialization<3,L,P,2,3>::Add();
|
||||
k2::Specialization<3,L,P,2,4>::Add();
|
||||
|
||||
k2::Specialization<3,L,P,3,2>::Add();
|
||||
k2::Specialization<3,L,P,3,3>::Add();
|
||||
k2::Specialization<3,L,P,3,4>::Add();
|
||||
}
|
||||
|
||||
template void InitGradByVDimKernels<true>();
|
||||
|
||||
@@ -600,34 +600,55 @@ void QuadratureInterpolator::Determinants(const Vector &e_vec,
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace internal::quadrature_interpolator;
|
||||
|
||||
using EvalKernel = QuadratureInterpolator::EvalKernelType;
|
||||
using TensorEvalKernel = QuadratureInterpolator::TensorEvalKernelType;
|
||||
using GradKernel = QuadratureInterpolator::GradKernelType;
|
||||
using CollocatedGradKernel = QuadratureInterpolator::CollocatedGradKernelType;
|
||||
|
||||
template <QVectorLayout Q_LAYOUT>
|
||||
TensorEvalKernel FallbackTensorEvalKernel(int DIM)
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT>; }
|
||||
if (DIM == 1) { return Values1D<Q_LAYOUT>; }
|
||||
else if (DIM == 2) { return Values2D<Q_LAYOUT>; }
|
||||
else if (DIM == 3) { return Values3D<Q_LAYOUT>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS>
|
||||
GradKernel GetGradKernel(int DIM)
|
||||
{
|
||||
if (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
if (DIM == 1) { return Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return Derivatives2D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 3) { return Derivatives3D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
|
||||
template<QVectorLayout Q_LAYOUT>
|
||||
GradKernel GetGradKernel(int DIM, bool GRAD_PHYS)
|
||||
{
|
||||
if (GRAD_PHYS) { return GetGradKernel<Q_LAYOUT, true>(DIM); }
|
||||
else { return GetGradKernel<Q_LAYOUT, false>(DIM); }
|
||||
}
|
||||
|
||||
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS>
|
||||
CollocatedGradKernel GetCollocatedGradKernel(int DIM)
|
||||
{
|
||||
if (DIM == 1) { return CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 2) { return CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else if (DIM == 3) { return CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS>; }
|
||||
else { MFEM_ABORT(""); }
|
||||
}
|
||||
|
||||
template<QVectorLayout Q_LAYOUT>
|
||||
CollocatedGradKernel GetCollocatedGradKernel(int DIM, bool GRAD_PHYS)
|
||||
{
|
||||
if (GRAD_PHYS) { return GetCollocatedGradKernel<Q_LAYOUT, true>(DIM); }
|
||||
else { return GetCollocatedGradKernel<Q_LAYOUT, false>(DIM); }
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <int DIM, int VDIM, int ND, int NQ>
|
||||
@@ -673,6 +694,13 @@ GradKernel QuadratureInterpolator::GradKernels::Fallback(
|
||||
else { return GetGradKernel<QVectorLayout::byVDIM>(DIM, GRAD_PHYS); }
|
||||
}
|
||||
|
||||
CollocatedGradKernel QuadratureInterpolator::CollocatedGradKernels::Fallback(
|
||||
int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int, int)
|
||||
{
|
||||
if (Q_LAYOUT == QVectorLayout::byNODES) { return GetCollocatedGradKernel<QVectorLayout::byNODES>(DIM, GRAD_PHYS); }
|
||||
else { return GetCollocatedGradKernel<QVectorLayout::byVDIM>(DIM, GRAD_PHYS); }
|
||||
}
|
||||
|
||||
/// @endcond
|
||||
|
||||
namespace internal
|
||||
|
||||
@@ -138,6 +138,10 @@ public:
|
||||
using GradKernelType = void(*)(const int, const real_t *, const real_t *,
|
||||
const real_t *, const real_t *, real_t *,
|
||||
const int, const int, const int, const int);
|
||||
using CollocatedGradKernelType = void(*)(const int, const real_t *,
|
||||
const real_t *, const real_t *,
|
||||
real_t *, const int, const int,
|
||||
const int);
|
||||
using DetKernelType = void(*)(const int NE, const real_t *, const real_t *,
|
||||
const real_t *, real_t *, const int, const int,
|
||||
Vector *);
|
||||
@@ -152,6 +156,8 @@ public:
|
||||
(int, QVectorLayout, bool, int, int, int), (int));
|
||||
MFEM_REGISTER_KERNELS(DetKernels, DetKernelType, (int, int, int, int));
|
||||
MFEM_REGISTER_KERNELS(EvalKernels, EvalKernelType, (int, int, int, int));
|
||||
MFEM_REGISTER_KERNELS(CollocatedGradKernels, CollocatedGradKernelType,
|
||||
(int, QVectorLayout, bool, int, int), (int));
|
||||
|
||||
static struct Kernels { Kernels(); } kernels;
|
||||
};
|
||||
|
||||
+911
-40
File diff suppressed because it is too large
Load Diff
+156
-19
@@ -40,6 +40,10 @@ protected:
|
||||
OperatorHandle fw_t_oper; ///< Forward true-dof operator
|
||||
OperatorHandle bw_t_oper; ///< Backward true-dof operator
|
||||
|
||||
bool use_ea;
|
||||
|
||||
MemoryType d_mt;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
bool parallel;
|
||||
#endif
|
||||
@@ -59,14 +63,23 @@ protected:
|
||||
|
||||
public:
|
||||
/** Construct a transfer algorithm between the domain, @a dom_fes_, and
|
||||
range, @a ran_fes_, FE spaces. */
|
||||
GridTransfer(FiniteElementSpace &dom_fes_, FiniteElementSpace &ran_fes_);
|
||||
range, @a ran_fes_, FE spaces, d_mt_ will specify memory space for
|
||||
large data structures */
|
||||
GridTransfer(FiniteElementSpace &dom_fes_,
|
||||
FiniteElementSpace &ran_fes_);
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~GridTransfer() { }
|
||||
|
||||
/** Uses device friendly element assembly versions for L2Projection
|
||||
transfers, L2, H1 FEM spaces currently supported */
|
||||
void UseEA(bool use_ea_) { use_ea = use_ea_;}
|
||||
|
||||
/** Set memory type for large data structures */
|
||||
void SetMemType(MemoryType d_mt_) {d_mt = d_mt_;}
|
||||
|
||||
/** @brief Set the desired Operator::Type for the construction of all
|
||||
operators defined by the underlying transfer algorithm. */
|
||||
operators defined by the underlying transfer algorithm. */
|
||||
/** The default value is Operator::ANY_TYPE which typically corresponds to a
|
||||
matrix-free operator representation. Note that derived classes are not
|
||||
required to support this setting and can ignore it. */
|
||||
@@ -169,7 +182,8 @@ public:
|
||||
smaller than the number of coarse dofs. */
|
||||
class L2ProjectionGridTransfer : public GridTransfer
|
||||
{
|
||||
protected:
|
||||
// Must be public due to host device lambdas
|
||||
public:
|
||||
/** Abstract class representing projection operator between a high-order
|
||||
finite element space on a coarse mesh, and a low-order finite element
|
||||
space on a refined mesh (LOR). We assume that the low-order space,
|
||||
@@ -194,10 +208,13 @@ protected:
|
||||
const FiniteElementSpace& fes_ho;
|
||||
const FiniteElementSpace& fes_lor;
|
||||
|
||||
MemoryType d_mt;
|
||||
Array<int> offsets;
|
||||
Table ho2lor;
|
||||
|
||||
L2Projection(const FiniteElementSpace& fes_ho_,
|
||||
const FiniteElementSpace& fes_lor_);
|
||||
const FiniteElementSpace& fes_lor_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
|
||||
void BuildHo2Lor(int nel_ho, int nel_lor,
|
||||
const CoarseFineTransformations& cf_tr);
|
||||
@@ -207,6 +224,50 @@ protected:
|
||||
ElementTransformation* tr_lor,
|
||||
IntegrationPointTransformation& ip_tr,
|
||||
DenseMatrix& M_mixed_el) const;
|
||||
|
||||
void ElemMixedMass(Geometry::Type geom, const FiniteElement& fe_ho,
|
||||
const FiniteElement& fe_lor,
|
||||
ElementTransformation* el_tr,
|
||||
IntegrationPointTransformation& ip_tr,
|
||||
DenseMatrix& B_L, DenseMatrix& B_H) const;
|
||||
public:
|
||||
/* Returns the Mixed Mass M_LH via device element assembly by building the
|
||||
basis functions and data at the quadrature points. */
|
||||
void MixedMassEA(const FiniteElementSpace& fes_ho_,
|
||||
const FiniteElementSpace& fes_lor_,
|
||||
Vector &M_LH,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
};
|
||||
|
||||
// Class below must be public as we now have device code
|
||||
public:
|
||||
class H1SpaceMixedMassOperator : public Operator
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace* fes_ho;
|
||||
const FiniteElementSpace* fes_lor;
|
||||
Table* ho2lor;
|
||||
Vector* M_LH_ea;
|
||||
public:
|
||||
H1SpaceMixedMassOperator(const FiniteElementSpace* fes_ho_,
|
||||
const FiniteElementSpace* fes_lor_,
|
||||
Table* ho2lor_, Vector* M_LH_ea_);
|
||||
void Mult(const Vector& x, Vector& y) const;
|
||||
void MultTranspose(const Vector& x, Vector& y) const;
|
||||
};
|
||||
|
||||
class H1SpaceLumpedMassOperator : public Operator
|
||||
{
|
||||
protected:
|
||||
const FiniteElementSpace* fes_ho;
|
||||
const FiniteElementSpace* fes_lor;
|
||||
Vector* ML_inv; // inverse of lumped M_L
|
||||
public:
|
||||
H1SpaceLumpedMassOperator(const FiniteElementSpace* fes_ho_,
|
||||
const FiniteElementSpace* fes_lor_,
|
||||
Vector& ML_inv_);
|
||||
void Mult(const Vector& x, Vector& y) const;
|
||||
void MultTranspose(const Vector& x, Vector& y) const;
|
||||
};
|
||||
|
||||
/** Class for projection operator between a L2 high-order finite element
|
||||
@@ -214,17 +275,24 @@ protected:
|
||||
refined mesh (LOR). */
|
||||
class L2ProjectionL2Space : public L2Projection
|
||||
{
|
||||
// The restriction and prolongation operators are represented as dense
|
||||
// elementwise matrices (of potentially different sizes, because of mixed
|
||||
// meshes or p-refinement). The matrix entries are stored in the R and P
|
||||
// arrays. The entries of the i'th high-order element are stored at the
|
||||
// index given by offsets[i].
|
||||
/// The restriction and prolongation operators are represented as dense
|
||||
/// elementwise matrices (of potentially different sizes, because of mixed
|
||||
/// meshes or p-refinement). The matrix entries are stored in the R and P
|
||||
/// arrays. The entries of the i'th high-order element are stored at the
|
||||
/// index given by offsets[i].
|
||||
mutable Array<real_t> R, P;
|
||||
Array<int> offsets;
|
||||
|
||||
const bool use_ea;
|
||||
|
||||
public:
|
||||
L2ProjectionL2Space(const FiniteElementSpace& fes_ho_,
|
||||
const FiniteElementSpace& fes_lor_);
|
||||
const FiniteElementSpace& fes_lor_,
|
||||
const bool use_ea_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
|
||||
/*Same as above but assembles and stores R_ea, P_ea */
|
||||
void EAL2ProjectionL2Space();
|
||||
|
||||
/// Maps <tt>x</tt>, primal field coefficients defined on a coarse mesh
|
||||
/// with a higher order L2 finite element space, to <tt>y</tt>, primal
|
||||
/// field coefficients defined on a refined mesh with a low order L2
|
||||
@@ -232,6 +300,10 @@ protected:
|
||||
/// the coarse mesh. Coefficients are computed through minimization of L2
|
||||
/// error between the fields.
|
||||
void Mult(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Perform mult on the device (same as above)
|
||||
void EAMult(const Vector& x, Vector& y) const;
|
||||
|
||||
/// Maps <tt>x</tt>, dual field coefficients defined on a refined mesh
|
||||
/// with a low order L2 finite element space, to <tt>y</tt>, dual field
|
||||
/// coefficients defined on a coarse mesh with a higher order L2 finite
|
||||
@@ -240,6 +312,9 @@ protected:
|
||||
/// error between the primal fields. Note, if the <tt>x</tt>-coefficients
|
||||
/// come from ProlongateTranspose, then mass is conserved.
|
||||
void MultTranspose(const Vector& x, Vector& y) const override;
|
||||
|
||||
void EAMultTranspose(const Vector& x, Vector& y) const;
|
||||
|
||||
/// Maps <tt>x</tt>, primal field coefficients defined on a refined mesh
|
||||
/// with a low order L2 finite element space, to <tt>y</tt>, primal field
|
||||
/// coefficients defined on a coarse mesh with a higher order L2 finite
|
||||
@@ -248,6 +323,9 @@ protected:
|
||||
/// left-inverse prolongation operation. This functionality is also
|
||||
/// provided as an Operator by L2Prolongation.
|
||||
void Prolongate(const Vector& x, Vector& y) const override;
|
||||
|
||||
void EAProlongate(const Vector& x, Vector& y) const;
|
||||
|
||||
/// Maps <tt>x</tt>, dual field coefficients defined on a coarse mesh with
|
||||
/// a higher order L2 finite element space, to <tt>y</tt>, dual field
|
||||
/// coefficients defined on a refined mesh with a low order L2 finite
|
||||
@@ -256,21 +334,46 @@ protected:
|
||||
/// conservative left-inverse prolongation operation. This functionality
|
||||
/// is also provided as an Operator by L2Prolongation.
|
||||
void ProlongateTranspose(const Vector& x, Vector& y) const override;
|
||||
|
||||
void EAProlongateTranspose(const Vector& x, Vector& y) const;
|
||||
|
||||
void SetRelTol(real_t p_rtol_) override { } ///< No-op.
|
||||
void SetAbsTol(real_t p_atol_) override { } ///< No-op.
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
/// Class below must be public as we now have device code
|
||||
public:
|
||||
|
||||
/** Projection operator between a H1 high-order finite element space on a
|
||||
coarse mesh, and a H1 low-order finite element space on a refined mesh
|
||||
(LOR). */
|
||||
class L2ProjectionH1Space : public L2Projection
|
||||
{
|
||||
const bool use_ea;
|
||||
|
||||
public:
|
||||
L2ProjectionH1Space(const FiniteElementSpace &fes_ho_,
|
||||
const FiniteElementSpace &fes_lor_);
|
||||
const FiniteElementSpace &fes_lor_,
|
||||
const bool use_ea_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
#ifdef MFEM_USE_MPI
|
||||
L2ProjectionH1Space(const ParFiniteElementSpace &pfes_ho_,
|
||||
const ParFiniteElementSpace &pfes_lor_);
|
||||
const ParFiniteElementSpace &pfes_lor_,
|
||||
const bool use_ea_,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType());
|
||||
#endif
|
||||
/// Same as above but assembles action of R through 4 parts:
|
||||
/// ( ) inv( lumped(M_L) ), which is a diagonal matrix (essentially a vector)
|
||||
/// ( ) ElementRestrictionOperator for LOR space
|
||||
/// ( ) mixed mass matrix M_{LH}
|
||||
/// ( ) ElementRestrictionOperator for HO space
|
||||
void EAL2ProjectionH1Space();
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void EAL2ProjectionH1Space(const ParFiniteElementSpace &pfes_ho_,
|
||||
const ParFiniteElementSpace &pfes_lor_);
|
||||
#endif
|
||||
/// Maps <tt>x</tt>, primal field coefficients defined on a coarse mesh
|
||||
/// with a higher order H1 finite element space, to <tt>y</tt>, primal
|
||||
@@ -279,6 +382,7 @@ protected:
|
||||
/// the coarse mesh. Coefficients are computed through minimization of L2
|
||||
/// error between the fields.
|
||||
void Mult(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Maps <tt>x</tt>, dual field coefficients defined on a refined mesh
|
||||
/// with a low order H1 finite element space, to <tt>y</tt>, dual field
|
||||
/// coefficients defined on a coarse mesh with a higher order H1 finite
|
||||
@@ -287,6 +391,7 @@ protected:
|
||||
/// error between the primal fields. Note, if the <tt>x</tt>-coefficients
|
||||
/// come from ProlongateTranspose, then mass is conserved.
|
||||
void MultTranspose(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Maps <tt>x</tt>, primal field coefficients defined on a refined mesh
|
||||
/// with a low order H1 finite element space, to <tt>y</tt>, primal field
|
||||
/// coefficients defined on a coarse mesh with a higher order H1 finite
|
||||
@@ -295,6 +400,7 @@ protected:
|
||||
/// left-inverse prolongation operation. This functionality is also
|
||||
/// provided as an Operator by L2Prolongation.
|
||||
void Prolongate(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Maps <tt>x</tt>, dual field coefficients defined on a coarse mesh with
|
||||
/// a higher order H1 finite element space, to <tt>y</tt>, dual field
|
||||
/// coefficients defined on a refined mesh with a low order H1 finite
|
||||
@@ -303,14 +409,22 @@ protected:
|
||||
/// conservative left-inverse prolongation operation. This functionality
|
||||
/// is also provided as an Operator by L2Prolongation.
|
||||
void ProlongateTranspose(const Vector& x, Vector& y) const override;
|
||||
|
||||
/// Returns the inverse of an on-rank lumped mass matrix
|
||||
void LumpedMassInverse(Vector& ML_inv) const;
|
||||
|
||||
void SetRelTol(real_t p_rtol_) override;
|
||||
void SetAbsTol(real_t p_atol_) override;
|
||||
|
||||
protected:
|
||||
/// Sets up the PCG solver (sets parameters, operator, and preconditioner)
|
||||
void SetupPCG();
|
||||
/// Computes on-rank R and M_LH matrices.
|
||||
|
||||
/// @brief Computes on-rank R and M_LH matrices. If true, computes mixed mass and/or
|
||||
/// inverse lumped mass matrix error when compared to device implementation.
|
||||
std::pair<std::unique_ptr<SparseMatrix>,
|
||||
std::unique_ptr<SparseMatrix>> ComputeSparseRAndM_LH();
|
||||
|
||||
/// @brief Recovers vector of tdofs given a vector of dofs and a finite
|
||||
/// element space
|
||||
void GetTDofs(const FiniteElementSpace& fes, const Vector& x, Vector& X) const;
|
||||
@@ -333,10 +447,8 @@ protected:
|
||||
void TDofsListByVDim(const FiniteElementSpace& fes,
|
||||
int vdim,
|
||||
Array<int>& vdofs_list) const;
|
||||
/// Returns the inverse of an on-rank lumped mass matrix
|
||||
void LumpedMassInverse(Vector& ML_inv) const;
|
||||
|
||||
/// @brief Computes sparsity pattern and initializes R matrix.
|
||||
///
|
||||
/// Based on BilinearForm::AllocMat(), except maps between coarse HO
|
||||
/// elements and refined LOR elements.
|
||||
std::unique_ptr<SparseMatrix> AllocR();
|
||||
@@ -346,10 +458,34 @@ protected:
|
||||
// The restriction operator is represented as an Operator R. The
|
||||
// prolongation operator is a dense matrix computed as the inverse of (R^T
|
||||
// M_L R), and hence, is not stored.
|
||||
// If element assembly is enabled
|
||||
std::unique_ptr<Operator> R;
|
||||
// Used to compute P = (RT*M_LH)^(-1) M_LH^T
|
||||
std::unique_ptr<Operator> M_LH;
|
||||
// Inverted operator in P = (RT*M_LH)^(-1) M_LH^T. Used to compute P via PCG.
|
||||
std::unique_ptr<Operator> RTxM_LH;
|
||||
// Lumped M_L inverse operator built via EA. Wrapped with restriction maps
|
||||
// to multiply with scalar TDof LOR vectors.
|
||||
std::unique_ptr<Operator> ML_inv_vea;
|
||||
// LDof Mixed mass operator built via EA. Wrapped with restrition maps to send
|
||||
// scalar LDof HO vectors to LDof LOR vectors.
|
||||
Operator *M_LH_local_op;
|
||||
|
||||
// Scalar finite element spaces for stored Tdof-to-and-from-LDof maps.
|
||||
FiniteElementSpace* fes_ho_scalar;
|
||||
FiniteElementSpace* fes_lor_scalar;
|
||||
// Element Assembled mixed mass
|
||||
Vector M_LH_ea;
|
||||
// Element Assembled lumped M_L inverse built via EA. Stores diagonal as a Ldof vector.
|
||||
Vector ML_inv_ea;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
ParFiniteElementSpace* pfes_ho_scalar;
|
||||
ParFiniteElementSpace* pfes_lor_scalar;
|
||||
Vector RML_inv;
|
||||
#endif
|
||||
|
||||
friend class L2ProjectionL2Space;
|
||||
};
|
||||
|
||||
/** Mass-conservative prolongation operator going in the opposite direction
|
||||
@@ -379,7 +515,8 @@ protected:
|
||||
public:
|
||||
L2ProjectionGridTransfer(FiniteElementSpace &coarse_fes_,
|
||||
FiniteElementSpace &fine_fes_,
|
||||
bool force_l2_space_ = false)
|
||||
bool force_l2_space_ = false,
|
||||
MemoryType d_mt_ = Device::GetHostMemoryType()) //move to method
|
||||
: GridTransfer(coarse_fes_, fine_fes_),
|
||||
F(NULL), B(NULL), force_l2_space(force_l2_space_)
|
||||
{ }
|
||||
|
||||
@@ -47,6 +47,8 @@ list(APPEND HDRS
|
||||
zstr.hpp
|
||||
hash.hpp
|
||||
isockstream.hpp
|
||||
mdarray.hpp
|
||||
mdspan.hpp
|
||||
kdtree.hpp
|
||||
mem_alloc.hpp
|
||||
mem_manager.hpp
|
||||
|
||||
+69
-5
@@ -45,6 +45,8 @@ template <class T>
|
||||
class Array
|
||||
{
|
||||
protected:
|
||||
template<typename mfem_type, int N, typename L> friend class MDSpan;
|
||||
|
||||
/// Pointer to data
|
||||
Memory<T> data;
|
||||
/// Size of the array
|
||||
@@ -52,10 +54,7 @@ protected:
|
||||
|
||||
inline void GrowSize(int minsize);
|
||||
|
||||
static inline void TypeAssert()
|
||||
{
|
||||
static_assert(std::is_trivial<T>::value, "type T must be trivial");
|
||||
}
|
||||
static_assert(std::is_trivial<T>::value, "type T must be trivial");
|
||||
|
||||
public:
|
||||
friend void Swap<T>(Array<T> &, Array<T> &);
|
||||
@@ -95,11 +94,26 @@ public:
|
||||
template <typename CT, int N>
|
||||
explicit inline Array(const CT (&values)[N]);
|
||||
|
||||
/**
|
||||
* @brief Construct a new Array object from an initializer list.
|
||||
*
|
||||
* @param init_list List of entities to construct from.
|
||||
*/
|
||||
Array(const std::initializer_list<T> &init_list)
|
||||
: Array(static_cast<int>(init_list.size()))
|
||||
{
|
||||
auto * it = GetData();
|
||||
for (auto value : init_list)
|
||||
{
|
||||
*it++ = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Move constructor ("steals" data from 'src')
|
||||
inline Array(Array<T> &&src) { Swap(src, *this); }
|
||||
|
||||
/// Destructor
|
||||
inline ~Array() { TypeAssert(); data.Delete(); }
|
||||
inline ~Array() { data.Delete(); }
|
||||
|
||||
/// Assignment operator: deep copy from 'src'.
|
||||
Array<T> &operator=(const Array<T> &src) { src.Copy(*this); return *this; }
|
||||
@@ -204,6 +218,8 @@ public:
|
||||
/// Delete the whole array.
|
||||
inline void DeleteAll();
|
||||
|
||||
/// Reduces the capacity of the array to exactly match the current size.
|
||||
inline void ShrinkToFit();
|
||||
|
||||
/// Create a copy of the internal array to the provided @a copy.
|
||||
inline void Copy(Array ©) const;
|
||||
@@ -221,6 +237,18 @@ public:
|
||||
/// Make this Array a reference to 'master'.
|
||||
inline void MakeRef(const Array &master);
|
||||
|
||||
/**
|
||||
* @brief Permute the array using the provided indices. Sorts the indices
|
||||
* variable in the process, thereby destroying the permutation. The rvalue
|
||||
* reference is to be used when this destruction is allowed, whilst the const
|
||||
* reference preserves at the cost of duplication.
|
||||
*
|
||||
* @param indices The indices of the ordering. data[i] = data[indices[i]].
|
||||
*/
|
||||
template <typename I>
|
||||
inline void Permute(I &&indices);
|
||||
template <typename I>
|
||||
inline void Permute(const I &indices) { Permute(I(indices)); }
|
||||
|
||||
/// Copy sub array starting from @a offset out to the provided @a sa.
|
||||
inline void GetSubArray(int offset, int sa_size, Array<T> &sa) const;
|
||||
@@ -275,6 +303,9 @@ public:
|
||||
/// Return 1 if the array is sorted from lowest to highest. Otherwise return 0.
|
||||
int IsSorted() const;
|
||||
|
||||
/// Does the Array have Size zero.
|
||||
bool IsEmpty() const { return Size() == 0; }
|
||||
|
||||
/// Fill the entries of the array with the cumulative sum of the entries.
|
||||
void PartialSum();
|
||||
|
||||
@@ -492,6 +523,8 @@ public:
|
||||
BlockArray(int block_size = 16*1024);
|
||||
BlockArray(const BlockArray<T> &other); // deep copy
|
||||
BlockArray& operator=(const BlockArray&) = delete; // not supported
|
||||
BlockArray(BlockArray<T> &&other) = default;
|
||||
BlockArray& operator=(BlockArray<T> &&other) = default;
|
||||
~BlockArray() { Destroy(); }
|
||||
|
||||
/// Allocate and construct a new item in the array, return its index.
|
||||
@@ -613,6 +646,8 @@ public:
|
||||
|
||||
iterator begin() { return size ? iterator(this) : iterator(true); }
|
||||
iterator end() { return iterator(); }
|
||||
const_iterator begin() const { return cbegin(); }
|
||||
const_iterator end() const { return cend(); }
|
||||
|
||||
const_iterator cbegin() const
|
||||
{ return size ? const_iterator(this) : const_iterator(true); }
|
||||
@@ -685,6 +720,35 @@ inline void Array<T>::GrowSize(int minsize)
|
||||
data = p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void Array<T>::ShrinkToFit()
|
||||
{
|
||||
if (Capacity() == size) { return; }
|
||||
Memory<T> p(size, data.GetMemoryType());
|
||||
p.CopyFrom(data, size);
|
||||
p.UseDevice(data.UseDevice());
|
||||
data.Delete();
|
||||
data = p;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename I>
|
||||
inline void Array<T>::Permute(I &&indices)
|
||||
{
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
auto current = i;
|
||||
while (i != indices[current])
|
||||
{
|
||||
auto next = indices[current];
|
||||
std::swap(data[current], data[next]);
|
||||
indices[current] = current;
|
||||
current = next;
|
||||
}
|
||||
indices[current] = current;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> template <typename CT>
|
||||
inline Array<T> &Array<T>::operator=(const Array<CT> &src)
|
||||
{
|
||||
|
||||
@@ -838,6 +838,16 @@ inline void hypre_forall(int N, lambda &&body)
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return the most general MemoryClass that can be used with mfem::hypre_forall
|
||||
// kernels. The returned MemoryClass is the same as the one returned by
|
||||
// GerHypreMemoryClass() except when hypre is configured to use UVM, in which
|
||||
// case this function returns MemoryClass::HOST or MemoryClass::DEVICE depending
|
||||
// on the result of HypreUsingGPU().
|
||||
inline MemoryClass GetHypreForallMemoryClass()
|
||||
{
|
||||
return HypreUsingGPU() ? MemoryClass::DEVICE : MemoryClass::HOST;
|
||||
}
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) 2010-2023, 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_MDARRAY
|
||||
#define MFEM_MDARRAY
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#include "array.hpp"
|
||||
#include "mdspan.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template<typename T, int N, typename Layout = MDLayoutLeft<N>>
|
||||
struct MDArray : public MDSpan<Array<T>, N, Layout>
|
||||
{
|
||||
using base_t = MDSpan<Array<T>, N, Layout>;
|
||||
|
||||
/**
|
||||
* @brief MDArray default constructor (recursion)
|
||||
*/
|
||||
MDArray(): base_t() { }
|
||||
|
||||
/**
|
||||
* @brief MDArray recursion constructor
|
||||
* @param[in] n Dimension indice
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <typename... Ts>
|
||||
MDArray(int n, Ts... args): MDArray(args...) { base_t::Setup(n, args...); }
|
||||
|
||||
/// Move constructor not supported
|
||||
MDArray(MDArray&&) = delete;
|
||||
|
||||
/// Copy constructor not supported
|
||||
MDArray(const MDArray&) = delete;
|
||||
|
||||
/// Move assignment not supported
|
||||
MDArray& operator=(MDArray&&) = delete;
|
||||
|
||||
/// Copy assignment not supported
|
||||
MDArray& operator=(const MDArray&) = delete;
|
||||
|
||||
using Array<T>::Read;
|
||||
using Array<T>::Write;
|
||||
using Array<T>::ReadWrite;
|
||||
using Array<T>::HostRead;
|
||||
using Array<T>::HostWrite;
|
||||
using Array<T>::HostReadWrite;
|
||||
|
||||
using Array<T>::Assign;
|
||||
using Array<T>::Print;
|
||||
|
||||
using Array<T>::GetData;
|
||||
|
||||
using Array<T>::operator=;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_MDARRAY
|
||||
@@ -0,0 +1,417 @@
|
||||
// Copyright (c) 2010-2023, 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_MDSPAN_HPP
|
||||
#define MFEM_MDSPAN_HPP
|
||||
|
||||
#include <list>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "device.hpp"
|
||||
#include "backends.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace internal // experimental helper functions for mfem::MDLayout
|
||||
{
|
||||
|
||||
// md_sequence represents a compile-time sequence of integers
|
||||
template <typename T, T... args> struct md_sequence { };
|
||||
|
||||
template <typename T, int N, bool left> struct make_md_sequence;
|
||||
|
||||
// make_sequence, specialized for left (default) and right layout
|
||||
template <typename T, int N, bool left = true>
|
||||
using make_sequence = typename make_md_sequence<T, N, left>::type;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// @brief The MDOffset class computes the multi-dimensional offsets
|
||||
template <int n, int N, typename T, typename... Ts>
|
||||
struct MDOffset
|
||||
{
|
||||
static MFEM_HOST_DEVICE inline
|
||||
T offset(const int (&Sd)[N], T nd, Ts... args)
|
||||
{ return nd * Sd[n-1] + MDOffset<n+1, N, Ts...>::offset(Sd, args...); }
|
||||
};
|
||||
|
||||
template <int N, typename T, typename... Ts>
|
||||
struct MDOffset<N, N, T, Ts...>
|
||||
{
|
||||
static MFEM_HOST_DEVICE inline
|
||||
T offset(const int (&Sd)[N], T nd) { return nd * Sd[N-1]; }
|
||||
};
|
||||
|
||||
/// @brief The MDTensor class holds the pointer and strides for each dimension
|
||||
template<int N, typename T> class MDTensor
|
||||
{
|
||||
T *ptr;
|
||||
int Sd[N];
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
MDTensor() = delete;
|
||||
|
||||
/// Copy constructor (default)
|
||||
MDTensor(const MDTensor&) = default;
|
||||
|
||||
/// Copy assignment (default)
|
||||
MDTensor& operator=(const MDTensor&) = default;
|
||||
|
||||
/// Constructor to initialize a tensor from a pointer and strides
|
||||
template <typename... Args> MFEM_HOST_DEVICE
|
||||
MDTensor(T *ptr, const int (&sd)[N]): ptr(ptr)
|
||||
{ for (int i = 0; i < N; ++i) { Sd[i] = sd[i]; } }
|
||||
|
||||
/// Accessor for the data
|
||||
template <typename... Ts> MFEM_HOST_DEVICE inline
|
||||
T& operator()(Ts... args) { return ptr[Offset(args...)]; }
|
||||
|
||||
/// Const accessor for the data
|
||||
template <typename... Ts> MFEM_HOST_DEVICE inline
|
||||
T& operator()(Ts... args) const { return ptr[Offset(args...)]; }
|
||||
|
||||
/// Offset computation
|
||||
template <typename... Ts> MFEM_HOST_DEVICE inline
|
||||
int Offset(Ts... args) const
|
||||
{
|
||||
static_assert(sizeof...(args) == N, "Wrong number of dimensions");
|
||||
return MDOffset<1, N, Ts...>::offset(Sd, args...);
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief The MDLayout class, defaulted to a column-major (left) ordering
|
||||
template<int N, bool left = true> struct MDLayout
|
||||
{
|
||||
/// Create a layout with the internal::md_sequence
|
||||
template <int... args>
|
||||
static constexpr auto Make(internal::md_sequence<int, args...>)
|
||||
-> std::array<int, sizeof...(args)> { return {(static_cast<int>(args))...}; }
|
||||
|
||||
/// Array holding the layout permutation
|
||||
using perm_type = std::array<int, N>;
|
||||
perm_type perm = Make(internal::make_sequence<int, N, left> {});
|
||||
|
||||
/// Default constructor
|
||||
MDLayout() = default;
|
||||
|
||||
/// Copy constructor (default)
|
||||
MDLayout(const MDLayout&) = default;
|
||||
|
||||
/// Copy assignment (default)
|
||||
MDLayout& operator=(const MDLayout&) = default;
|
||||
|
||||
/// Constructor to initialize a layout from an array of indices
|
||||
template <typename... Ts>
|
||||
MDLayout(int n, Ts... args) noexcept: MDLayout(args...)
|
||||
{
|
||||
constexpr int k = N - sizeof...(args) - 1;
|
||||
static_assert(0 <= k && k < N, "Index out of bounds!");
|
||||
perm[k] = n;
|
||||
}
|
||||
|
||||
/// Access layout entries using operator()
|
||||
inline int operator()(int i) const
|
||||
{ return Assert(i), perm[static_cast<typename perm_type::size_type>(i)]; }
|
||||
|
||||
/// Access layout entries using operator[]
|
||||
inline int operator[](int i) const
|
||||
{ return Assert(i), perm[static_cast<typename perm_type::size_type>(i)]; }
|
||||
|
||||
/// Asserts the given index is valid (only in MFEM_DEBUG)
|
||||
inline void Assert(const int k) const
|
||||
{
|
||||
MFEM_CONTRACT_VAR(k);
|
||||
MFEM_ASSERT(0 <= k && k < N, "Index should be in [0," << (N-1) << "]");
|
||||
}
|
||||
};
|
||||
|
||||
/// Left (Column-major (Fortran)) and Right (Row-major (C/C++)) layouts
|
||||
template<int N> using MDLayoutLeft = MDLayout<N, true>;
|
||||
template<int N> using MDLayoutRight = MDLayout<N, false>;
|
||||
|
||||
/// \brief The MDSpan base class is a generic non-owning mfem_type's view
|
||||
/// that reinterprets it as a multidimensional type.
|
||||
template<typename mfem_type, int N, class layout_type = MDLayoutLeft<N>>
|
||||
class MDSpan : protected mfem_type
|
||||
{
|
||||
protected:
|
||||
using T = typename std::remove_pointer<decltype(mfem_type::data.h_ptr)>::type;
|
||||
|
||||
int Nd[N], Sd[N]; // dimension sizes and strides, once the layout is set
|
||||
layout_type layout; // stored layout, useful for reshapes
|
||||
|
||||
/// Set the dimensions (Nd) and strides (Sd) during contruction.
|
||||
/// When all the arguments have been processed, SetSize is called on the
|
||||
/// mfem_type with Device::GetMemoryType() as memory type and SetLayout is
|
||||
/// called using the layout.
|
||||
template <typename... Ts> void Setup(int dim, Ts... args)
|
||||
{
|
||||
constexpr int k = N - sizeof...(args) - 1;
|
||||
Sd[k] = Nd[k] = dim;
|
||||
if (k > 0) { return; }
|
||||
int psize = 1;
|
||||
for (int i = 0; i < N; i++) { psize *= Nd[i]; }
|
||||
mfem_type::SetSize(static_cast<int>(psize), Device::GetMemoryType());
|
||||
SetLayout(layout);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// Default constructor (recursion)
|
||||
MDSpan() noexcept: mfem_type() { }
|
||||
|
||||
/// Recursion constructor
|
||||
template <typename... Ts>
|
||||
MDSpan(int n, Ts... args): MDSpan(args...) { Setup(n, args...); }
|
||||
|
||||
/// Move constructor (delete)
|
||||
MDSpan(MDSpan&&) = delete;
|
||||
|
||||
/// Copy constructor (delete)
|
||||
MDSpan(const MDSpan&) = delete;
|
||||
|
||||
/// Move assignment (delete)
|
||||
MDSpan& operator=(MDSpan&&) = delete;
|
||||
|
||||
/// Copy assignment (delete)
|
||||
MDSpan& operator=(const MDSpan&) = delete;
|
||||
|
||||
/// Return the ith dimension
|
||||
int Extent(int i) const { return Nd[i]; }
|
||||
|
||||
/// Return the size of the span.
|
||||
int Size() const { return mfem_type::Size(); }
|
||||
|
||||
/// Store and use the given layout to update the strides
|
||||
template<typename Layout> void SetLayout(const Layout &l)
|
||||
{
|
||||
layout = l;
|
||||
Sd[l[0]] = 1;
|
||||
for (int i = 1; i < N; i++) { Sd[l[i]] = Nd[l[i-1]] * Sd[l[i-1]]; }
|
||||
}
|
||||
|
||||
/// Variadic resize the mfem_type
|
||||
template <typename... Ts> inline void SetSize(int size, Ts... args)
|
||||
{
|
||||
constexpr int k = N - sizeof...(args) - 1;
|
||||
Sd[k] = Nd[k] = size;
|
||||
const int msize = mfem_type::Size();
|
||||
MFEM_VERIFY(size > 0, "Size should be positive!");
|
||||
mfem_type::SetSize(msize > 0 ? msize*size : size, Device::GetMemoryType());
|
||||
MDSpan::SetSize(args...);
|
||||
}
|
||||
|
||||
/// Variadic terminal case of the mfem_type resize
|
||||
inline void SetSize(int size)
|
||||
{
|
||||
Sd[N-1] = Nd[N-1] = size;
|
||||
const int msize = mfem_type::Size();
|
||||
MFEM_VERIFY(size > 0, "Size should be positive!");
|
||||
mfem_type::SetSize(msize > 0 ? msize*size : size, Device::GetMemoryType());
|
||||
SetLayout(layout);
|
||||
}
|
||||
|
||||
/// Access mfem_type data entries using operator()
|
||||
template <typename... Ts> inline
|
||||
T& operator()(Ts... args) { return mfem_type::data[Offset(args...)]; }
|
||||
|
||||
/// Const access mfem_type data entries using operator()
|
||||
template <typename... Ts> inline const T& operator()(Ts... args) const
|
||||
{
|
||||
return mfem_type::data[Offset(args...)];
|
||||
}
|
||||
|
||||
/// Offset computation
|
||||
template <typename... Ts> inline int Offset(Ts... args) const
|
||||
{
|
||||
static_assert(sizeof...(args) == N, "Wrong number of dimensions");
|
||||
return MDOffset<1,N,Ts...>::offset(Sd, args...);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::Read(mfem_type::data, mfem_type::size, on_dev)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
const MDTensor<N,const T> MDRead(bool on_dev = true) const
|
||||
{
|
||||
const T *ptr = mfem::Read(mfem_type::data, mfem_type::size, on_dev);
|
||||
return MDTensor<N,const T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::Read(mfem_type::data, mfem_type::size, false)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
const MDTensor<N,const T> MDHostRead() const
|
||||
{
|
||||
const T *ptr = mfem::Read(mfem_type::data, mfem_type::size, false);
|
||||
return MDTensor<N,const T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::Write(mfem_type::data, mfem_type::size, on_dev)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
MDTensor<N,T> MDWrite(bool on_dev = true)
|
||||
{
|
||||
T *ptr = mfem::Write(mfem_type::data, mfem_type::size, on_dev);
|
||||
return MDTensor<N,T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::Write(mfem_type::data, mfem_type::size, false)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
MDTensor<N,T> MDHostWrite()
|
||||
{
|
||||
T *ptr = mfem::Write(mfem_type::data, mfem_type::size, false);
|
||||
return MDTensor<N,T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(mfem_type::data, mfem_type::size, on_dev)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
MDTensor<N,T> MDReadWrite(bool on_dev = true)
|
||||
{
|
||||
T *ptr = mfem::ReadWrite(mfem_type::data, mfem_type::size, on_dev);
|
||||
return MDTensor<N,T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// Shortcut for mfem::ReadWrite(mfem_type::data, mfem_type::size, false)
|
||||
/// and return an MDTensor with the MDSpan's pointer and strides
|
||||
MDTensor<N,T> MDHostReadWrite()
|
||||
{
|
||||
T *ptr = mfem::ReadWrite(mfem_type::data, mfem_type::size, false);
|
||||
return MDTensor<N,T>(ptr, Sd);
|
||||
}
|
||||
|
||||
/// The MDReshape function allows to reshape the multi-dimentional view
|
||||
/// into a new multi-dimentional one, by the use of std::array blocks.
|
||||
/// For example, if 'this' has three dimensions {N1, N2, N3}, it could handle
|
||||
/// this->MDReshape<4>(ptr, N1, std::array<int,2> {2, N2/2}, N3);
|
||||
|
||||
// Parameter R could be omitted with c++14 standard's deduced return types
|
||||
|
||||
// first method with given data pointer and rest of arguments
|
||||
template <int R, int m = 0, int M = 0, typename... Ts>
|
||||
inline auto MDReshape(T *ptr, Ts&&... args) -> MDTensor<R,T>
|
||||
{
|
||||
rNd.clear();
|
||||
reshape_ptr = ptr;
|
||||
reshape_offset = 1, reshape_shifts[0] = reshape_shifts[1] = 0;
|
||||
return MDReshape<R,m,M>(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
// variadic method, where a new block of reshape is given in argument
|
||||
template <int R, int m = 0, int M = 0, size_t P, typename... Ts>
|
||||
inline auto MDReshape(std::array<int,P> list, Ts&&... args) -> MDTensor<R,T>
|
||||
{
|
||||
reshape_shifts[0] = layout.perm[m]; // store layout shift begin
|
||||
int shifted_layout = reshape_shifts[1] + layout.perm[m];
|
||||
for (int dim: list)
|
||||
{
|
||||
rNd.push_back(dim);
|
||||
rLt[m].push_back(sub_layout_pair{shifted_layout,-1});
|
||||
shifted_layout += 1; // default left layout
|
||||
}
|
||||
reshape_shifts[1] += P-1; // update end
|
||||
return MDReshape<R,m+1,M+P>(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
// variadic method, where a new dimension of reshape is given
|
||||
template <int R, int m = 0, int M = 0, typename... Ts>
|
||||
inline auto MDReshape(int dim, Ts&&... args) -> MDTensor<R,T>
|
||||
{
|
||||
rNd.push_back(dim);
|
||||
const int shift =
|
||||
reshape_shifts[0] < layout.perm[m] ? reshape_shifts[1] : 0;
|
||||
rLt[m].push_back(sub_layout_pair{layout.perm[m] + shift,-1});
|
||||
return MDReshape<R,m+1,M+1>(std::forward<Ts>(args)...);
|
||||
}
|
||||
|
||||
// terminal case which returns the resulting MDTensor
|
||||
template <int R, int m = 0, int M = 0>
|
||||
inline MDTensor<R,T> MDReshape()
|
||||
{
|
||||
int k = 0, rLt_idx[M], rSd[M];
|
||||
// initialize sub_layout_pair's second
|
||||
for (sub_layout_type &sub: rLt)
|
||||
{
|
||||
for (sub_layout_pair &p: sub) { p.second = k++; }
|
||||
}
|
||||
// scan with the previous layout (N) order the reshaped layout (M)
|
||||
for (int i = 0, j = 0; i < N; i++)
|
||||
{
|
||||
for (sub_layout_pair &p: rLt[layout[i]])
|
||||
{
|
||||
rLt_idx[j++] = p.second;
|
||||
}
|
||||
}
|
||||
// apply the reshaped layout (M)
|
||||
rSd[rLt_idx[0]] = 1;
|
||||
for (int i = 1; i < M; i++)
|
||||
{
|
||||
rSd[rLt_idx[i]] = rNd[rLt_idx[i-1]] * rSd[rLt_idx[i-1]];
|
||||
}
|
||||
// construct the MDTensor with the given pointer and reshaped sizes
|
||||
static_assert(R == M, "R != M");
|
||||
return MDTensor<R,T>(reshape_ptr, rSd);
|
||||
}
|
||||
|
||||
private:
|
||||
T *reshape_ptr;
|
||||
std::vector<int> rNd; // reshape sizes
|
||||
int reshape_offset, reshape_shifts[2];// shift begin & end
|
||||
using sub_layout_pair = std::pair<int,int>;
|
||||
using sub_layout_type = std::list<sub_layout_pair>;
|
||||
std::array<sub_layout_type,N> rLt; // layout
|
||||
};
|
||||
|
||||
// md_sequence, md_extend and make_md_sequence implementation
|
||||
namespace internal
|
||||
{
|
||||
|
||||
template <typename T, int N, int mod, bool left> struct md_extend;
|
||||
|
||||
template <typename T, T... args, int N>
|
||||
struct md_extend<md_sequence<T, args...>, N, 0, true>
|
||||
{
|
||||
using type = md_sequence<T, args..., (args + N)...>;
|
||||
};
|
||||
|
||||
template <typename T, T... args, int N>
|
||||
struct md_extend<md_sequence<T, args...>, N, 1, true>
|
||||
{
|
||||
using type = md_sequence<T, args..., (args + N)..., 2*N>;
|
||||
};
|
||||
|
||||
template <typename T, T... args, int N>
|
||||
struct md_extend<md_sequence<T, args...>, N, 0, false>
|
||||
{
|
||||
using type = md_sequence<T, (args + N)..., args...>;
|
||||
};
|
||||
|
||||
template <typename T, T... args, int N>
|
||||
struct md_extend<md_sequence<T, args...>, N, 1, false>
|
||||
{
|
||||
using type = md_sequence<T, 2*N, (args + N)..., args...>;
|
||||
};
|
||||
|
||||
template <typename T, int N, bool L> struct make_md_sequence
|
||||
{
|
||||
using sequence_type = typename make_md_sequence<T,N/2,L>::type;
|
||||
using type = typename md_extend<sequence_type, N/2, N%2, L>::type;
|
||||
};
|
||||
|
||||
template <typename T, bool L>
|
||||
struct make_md_sequence<T,0,L> { using type = md_sequence<T>; };
|
||||
|
||||
} // namespace internal
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_MDSPAN_HPP
|
||||
+58
-10
@@ -408,8 +408,26 @@ class UvmHostMemorySpace : public HostMemorySpace
|
||||
{
|
||||
public:
|
||||
UvmHostMemorySpace(): HostMemorySpace() { }
|
||||
void Alloc(void **ptr, size_t bytes) override { CuMallocManaged(ptr, bytes == 0 ? 8 : bytes); }
|
||||
void Dealloc(void *ptr) override { CuMemFree(ptr); }
|
||||
|
||||
void Alloc(void **ptr, size_t bytes) override
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
CuMallocManaged(ptr, bytes == 0 ? 8 : bytes);
|
||||
#endif
|
||||
#ifdef MFEM_USE_HIP
|
||||
HipMallocManaged(ptr, bytes == 0 ? 8 : bytes);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Dealloc(void *ptr) override
|
||||
{
|
||||
#ifdef MFEM_USE_CUDA
|
||||
CuMemFree(ptr);
|
||||
#endif
|
||||
#ifdef MFEM_USE_HIP
|
||||
HipMemFree(ptr);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/// The 'No' device memory space
|
||||
@@ -504,6 +522,25 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class UvmHipMemorySpace : public DeviceMemorySpace
|
||||
{
|
||||
public:
|
||||
void Alloc(Memory &base) { base.d_ptr = base.h_ptr; }
|
||||
void Dealloc(Memory&) { }
|
||||
void *HtoD(void *dst, const void *src, size_t bytes)
|
||||
{
|
||||
if (dst == src) { MFEM_STREAM_SYNC; return dst; }
|
||||
return HipMemcpyHtoD(dst, src, bytes);
|
||||
}
|
||||
void *DtoD(void* dst, const void* src, size_t bytes)
|
||||
{ return HipMemcpyDtoD(dst, src, bytes); }
|
||||
void *DtoH(void *dst, const void *src, size_t bytes)
|
||||
{
|
||||
if (dst == src) { MFEM_STREAM_SYNC; return dst; }
|
||||
return HipMemcpyDtoH(dst, src, bytes);
|
||||
}
|
||||
};
|
||||
|
||||
/// The MMU device memory space
|
||||
class MmuDeviceMemorySpace : public DeviceMemorySpace
|
||||
{
|
||||
@@ -661,7 +698,15 @@ public:
|
||||
|
||||
// Filling the device memory backends, shifting with the device size
|
||||
constexpr int shift = DeviceMemoryType;
|
||||
#if defined(MFEM_USE_CUDA)
|
||||
device[static_cast<int>(MT::MANAGED)-shift] = new UvmCudaMemorySpace();
|
||||
#elif defined(MFEM_USE_HIP)
|
||||
device[static_cast<int>(MT::MANAGED)-shift] = new UvmHipMemorySpace();
|
||||
#else
|
||||
// this re-creates the original behavior, but should this be nullptr instead?
|
||||
device[static_cast<int>(MT::MANAGED)-shift] = new UvmCudaMemorySpace();
|
||||
#endif
|
||||
|
||||
// All other devices controllers are delayed
|
||||
device[static_cast<int>(MemoryType::DEVICE)-shift] = nullptr;
|
||||
device[static_cast<int>(MT::DEVICE_DEBUG)-shift] = nullptr;
|
||||
@@ -1193,8 +1238,9 @@ void MemoryManager::Copy_(void *dst_h_ptr, const void *src_h_ptr,
|
||||
{
|
||||
if (dst_h_ptr != src_d_ptr && bytes != 0)
|
||||
{
|
||||
internal::Memory &src_d_base = maps->memories.at(src_h_ptr);
|
||||
MemoryType src_d_mt = src_d_base.d_mt;
|
||||
MemoryType src_d_mt = (src_flags & Mem::ALIAS) ?
|
||||
maps->aliases.at(src_h_ptr).mem->d_mt :
|
||||
maps->memories.at(src_h_ptr).d_mt;
|
||||
ctrl->Device(src_d_mt)->DtoH(dst_h_ptr, src_d_ptr, bytes);
|
||||
}
|
||||
}
|
||||
@@ -1254,9 +1300,10 @@ void MemoryManager::CopyToHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
const void *src_d_ptr = (src_flags & Mem::ALIAS) ?
|
||||
mm.GetAliasDevicePtr(src_h_ptr, bytes, false) :
|
||||
mm.GetDevicePtr(src_h_ptr, bytes, false);
|
||||
const internal::Memory &base = maps->memories.at(dest_h_ptr);
|
||||
const MemoryType d_mt = base.d_mt;
|
||||
ctrl->Device(d_mt)->DtoH(dest_h_ptr, src_d_ptr, bytes);
|
||||
MemoryType src_d_mt = (src_flags & Mem::ALIAS) ?
|
||||
maps->aliases.at(src_h_ptr).mem->d_mt :
|
||||
maps->memories.at(src_h_ptr).d_mt;
|
||||
ctrl->Device(src_d_mt)->DtoH(dest_h_ptr, src_d_ptr, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1283,9 +1330,10 @@ void MemoryManager::CopyFromHost_(void *dest_h_ptr, const void *src_h_ptr,
|
||||
void *dest_d_ptr = (dest_flags & Mem::ALIAS) ?
|
||||
mm.GetAliasDevicePtr(dest_h_ptr, bytes, false) :
|
||||
mm.GetDevicePtr(dest_h_ptr, bytes, false);
|
||||
const internal::Memory &base = maps->memories.at(dest_h_ptr);
|
||||
const MemoryType d_mt = base.d_mt;
|
||||
ctrl->Device(d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
|
||||
MemoryType dest_d_mt = (dest_flags & Mem::ALIAS) ?
|
||||
maps->aliases.at(dest_h_ptr).mem->d_mt :
|
||||
maps->memories.at(dest_h_ptr).d_mt;
|
||||
ctrl->Device(dest_d_mt)->HtoD(dest_d_ptr, src_h_ptr, bytes);
|
||||
}
|
||||
dest_flags = dest_flags &
|
||||
~(dest_on_host ? Mem::VALID_DEVICE : Mem::VALID_HOST);
|
||||
|
||||
@@ -169,6 +169,7 @@ class Memory
|
||||
protected:
|
||||
friend class MemoryManager;
|
||||
friend void MemoryPrintFlags(unsigned flags);
|
||||
template<typename mfem_type, int N, typename L> friend class MDSpan;
|
||||
|
||||
enum FlagMask: unsigned
|
||||
{
|
||||
|
||||
+13
-48
@@ -15,80 +15,45 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
IntegerSet::IntegerSet(IntegerSet &s)
|
||||
: me(s.me.Size())
|
||||
int IntegerSet::PickRandomElement() const
|
||||
{
|
||||
for (int i = 0; i < me.Size(); i++)
|
||||
{
|
||||
me[i] = s.me[i];
|
||||
}
|
||||
}
|
||||
|
||||
IntegerSet& IntegerSet::operator=(const IntegerSet &s)
|
||||
{
|
||||
me.SetSize(s.me.Size());
|
||||
for (int i = 0; i < me.Size(); i++)
|
||||
{
|
||||
me[i] = s.me[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
int IntegerSet::operator== (IntegerSet &s)
|
||||
{
|
||||
if (me.Size() != s.me.Size())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < me.Size(); i++)
|
||||
if (me[i] != s.me[i])
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int IntegerSet::PickRandomElement()
|
||||
{
|
||||
int i, size = me.Size();
|
||||
int i, size = Size();
|
||||
unsigned int seed = 0;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
seed += me[i];
|
||||
seed += data[i];
|
||||
}
|
||||
|
||||
srand(seed);
|
||||
|
||||
return me[rand()/(RAND_MAX/size)];
|
||||
return data[rand()/(RAND_MAX/size)];
|
||||
}
|
||||
|
||||
void IntegerSet::Recreate(const int n, const int *p)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
me.SetSize(n);
|
||||
SetSize(n);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
me[i] = p[i];
|
||||
data[i] = p[i];
|
||||
}
|
||||
|
||||
me.Sort();
|
||||
Sort();
|
||||
|
||||
for (j = 0, i = 1; i < n; i++)
|
||||
if (me[i] != me[j])
|
||||
if (data[i] != data[j])
|
||||
{
|
||||
me[++j] = me[i];
|
||||
data[++j] = data[i];
|
||||
}
|
||||
|
||||
me.SetSize(j+1);
|
||||
SetSize(j+1);
|
||||
}
|
||||
|
||||
|
||||
int ListOfIntegerSets::Insert(IntegerSet &s)
|
||||
int ListOfIntegerSets::Insert(const IntegerSet &s)
|
||||
{
|
||||
for (int i = 0; i < TheList.Size(); i++)
|
||||
if (*TheList[i] == s)
|
||||
@@ -101,7 +66,7 @@ int ListOfIntegerSets::Insert(IntegerSet &s)
|
||||
return TheList.Size()-1;
|
||||
}
|
||||
|
||||
int ListOfIntegerSets::Lookup(IntegerSet &s)
|
||||
int ListOfIntegerSets::Lookup(const IntegerSet &s) const
|
||||
{
|
||||
for (int i = 0; i < TheList.Size(); i++)
|
||||
if (*TheList[i] == s)
|
||||
@@ -113,7 +78,7 @@ int ListOfIntegerSets::Lookup(IntegerSet &s)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ListOfIntegerSets::AsTable(Table & t)
|
||||
void ListOfIntegerSets::AsTable(Table & t) const
|
||||
{
|
||||
int i;
|
||||
|
||||
|
||||
+17
-29
@@ -20,38 +20,26 @@ namespace mfem
|
||||
{
|
||||
|
||||
/// A set of integers
|
||||
class IntegerSet
|
||||
class IntegerSet : public Array<int>
|
||||
{
|
||||
private:
|
||||
Array<int> me;
|
||||
|
||||
public:
|
||||
/// Create an empty set.
|
||||
IntegerSet() { }
|
||||
|
||||
/// Create a copy of set 's'.
|
||||
IntegerSet(IntegerSet &s);
|
||||
using Array<int>::Array; ///< Inherit all Array constructors.
|
||||
// MSVC fails to recognize that rule of zero applies after using base class
|
||||
// constructors.
|
||||
IntegerSet() = default; ///< Default construct and empty set.
|
||||
IntegerSet(const IntegerSet &) = default; ///< Copy constructor.
|
||||
IntegerSet(IntegerSet &&) = default; ///< Move constructor.
|
||||
IntegerSet& operator=(const IntegerSet &) = default; ///< Copy assignment.
|
||||
IntegerSet& operator=(IntegerSet &&) = default; ///< Move assignment.
|
||||
|
||||
/// Create an integer set from C-array 'p' of 'n' integers.
|
||||
IntegerSet(const int n, const int *p) { Recreate(n, p); }
|
||||
|
||||
/// Return the size of the set.
|
||||
int Size() { return me.Size(); }
|
||||
|
||||
/// Return a reference to the sorted array of all the set entries.
|
||||
operator Array<int>& () { return me; }
|
||||
|
||||
/// Return the value of the lowest element of the set.
|
||||
int PickElement() { return me[0]; }
|
||||
int PickElement() const { return data[0]; }
|
||||
|
||||
/// Return the value of a random element of the set.
|
||||
int PickRandomElement();
|
||||
|
||||
/// Create a copy of set 's'.
|
||||
IntegerSet& operator=(const IntegerSet &s);
|
||||
|
||||
/// Return 1 if the sets are equal and 0 otherwise.
|
||||
int operator==(IntegerSet &s);
|
||||
int PickRandomElement() const;
|
||||
|
||||
/** @brief Create an integer set from C-array 'p' of 'n' integers.
|
||||
Overwrites any existing set data. */
|
||||
@@ -67,25 +55,25 @@ private:
|
||||
public:
|
||||
|
||||
/// Return the number of integer sets in the list.
|
||||
int Size() { return TheList.Size(); }
|
||||
int Size() const { return TheList.Size(); }
|
||||
|
||||
/// Return the value of the first element of the ith set.
|
||||
int PickElementInSet(int i) { return TheList[i]->PickElement(); }
|
||||
int PickElementInSet(int i) const { return TheList[i]->PickElement(); }
|
||||
|
||||
/// Return a random value from the ith set in the list.
|
||||
int PickRandomElementInSet(int i) { return TheList[i]->PickRandomElement(); }
|
||||
int PickRandomElementInSet(int i) const { return TheList[i]->PickRandomElement(); }
|
||||
|
||||
/** @brief Check to see if set 's' is in the list. If not append it to the
|
||||
end of the list. Returns the index of the list where set 's' can be
|
||||
found. */
|
||||
int Insert(IntegerSet &s);
|
||||
int Insert(const IntegerSet &s);
|
||||
|
||||
/** Return the index of the list where set 's' can be found. Returns -1 if
|
||||
not found. */
|
||||
int Lookup(IntegerSet &s);
|
||||
int Lookup(const IntegerSet &s) const;
|
||||
|
||||
/// Write the list of sets into table 't'.
|
||||
void AsTable(Table &t);
|
||||
void AsTable(Table &t) const;
|
||||
|
||||
~ListOfIntegerSets();
|
||||
};
|
||||
|
||||
@@ -57,6 +57,7 @@ list(APPEND HDRS
|
||||
lapack.hpp
|
||||
linalg.hpp
|
||||
matrix.hpp
|
||||
mdvector.hpp
|
||||
ode.hpp
|
||||
operator.hpp
|
||||
solvers.hpp
|
||||
|
||||
@@ -52,9 +52,9 @@ BatchedLinAlg &BatchedLinAlg::Instance()
|
||||
}
|
||||
|
||||
void BatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha, real_t beta)
|
||||
real_t alpha, real_t beta, Op op)
|
||||
{
|
||||
Get(Instance().active_backend).AddMult(A, x, y, alpha, beta);
|
||||
Get(Instance().active_backend).AddMult(A, x, y, alpha, beta, op);
|
||||
}
|
||||
|
||||
void BatchedLinAlg::Mult(const DenseTensor &A, const Vector &x, Vector &y)
|
||||
@@ -62,6 +62,12 @@ void BatchedLinAlg::Mult(const DenseTensor &A, const Vector &x, Vector &y)
|
||||
Get(Instance().active_backend).Mult(A, x, y);
|
||||
}
|
||||
|
||||
void BatchedLinAlg::MultTranspose(const DenseTensor &A, const Vector &x,
|
||||
Vector &y)
|
||||
{
|
||||
Get(Instance().active_backend).MultTranspose(A, x, y);
|
||||
}
|
||||
|
||||
void BatchedLinAlg::Invert(DenseTensor &A)
|
||||
{
|
||||
Get(Instance().active_backend).Invert(A);
|
||||
@@ -107,4 +113,10 @@ void BatchedLinAlgBase::Mult(const DenseTensor &A, const Vector &x,
|
||||
AddMult(A, x, y, 1.0, 0.0);
|
||||
}
|
||||
|
||||
void BatchedLinAlgBase::MultTranspose(const DenseTensor &A, const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
AddMult(A, x, y, 1.0, 0.0, Op::T);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -48,6 +48,14 @@ public:
|
||||
/// Counter for the number of backends.
|
||||
NUM_BACKENDS
|
||||
};
|
||||
|
||||
/// Operation type (transposed or not transposed)
|
||||
enum Op
|
||||
{
|
||||
N, ///< Not transposed.
|
||||
T ///< Transposed.
|
||||
};
|
||||
|
||||
private:
|
||||
/// All available backends. Unavailble backends will be nullptr.
|
||||
std::array<std::unique_ptr<class BatchedLinAlgBase>,
|
||||
@@ -58,15 +66,19 @@ private:
|
||||
/// Return the singleton instance.
|
||||
static BatchedLinAlg &Instance();
|
||||
public:
|
||||
/// @brief Computes $y = \alpha A x + \beta y$.
|
||||
/// @brief Computes $y = \alpha A^{op} x + \beta y$.
|
||||
///
|
||||
/// $A^{op}$ is either $A$ or $A^T$ depending on the value of @a op.
|
||||
/// $A$ is a block diagonal matrix, represented by the DenseTensor @a A with
|
||||
/// shape (m, n, n_mat). $x$ has shape (n, k, n_mat), and $y$ has shape
|
||||
/// (m, k, n_mat).
|
||||
/// shape (m, n, n_mat). $x$ has shape (tr?m:n, k, n_mat), and $y$ has shape
|
||||
/// (tr?n:m, k, n_mat), where 'tr' is true in the transposed case.
|
||||
static void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha = 1.0, real_t beta = 1.0);
|
||||
/// Computes $y = A x$ (e.g. by calling @ref AddMult "AddMult(A,x,y,1,0)").
|
||||
real_t alpha = 1.0, real_t beta = 1.0,
|
||||
Op op = Op::N);
|
||||
/// Computes $y = A x$ (e.g. by calling @ref AddMult "AddMult(A,x,y,1,0,Op::N)").
|
||||
static void Mult(const DenseTensor &A, const Vector &x, Vector &y);
|
||||
/// Computes $y = A^T x$ (e.g. by calling @ref AddMult "AddMult(A,x,y,1,0,Op::T)").
|
||||
static void MultTranspose(const DenseTensor &A, const Vector &x, Vector &y);
|
||||
/// @brief Replaces the block diagonal matrix $A$ with its inverse $A^{-1}$.
|
||||
///
|
||||
/// $A$ is represented by the DenseTensor @a A with shape (m, m, n_mat).
|
||||
@@ -109,11 +121,16 @@ public:
|
||||
class BatchedLinAlgBase
|
||||
{
|
||||
public:
|
||||
using Op = BatchedLinAlg::Op;
|
||||
/// See BatchedLinAlg::AddMult.
|
||||
virtual void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha = 1.0, real_t beta = 1.0) const = 0;
|
||||
real_t alpha = 1.0, real_t beta = 1.0,
|
||||
Op op = Op::N) const = 0;
|
||||
/// See BatchedLinAlg::Mult.
|
||||
virtual void Mult(const DenseTensor &A, const Vector &x, Vector &y) const;
|
||||
/// See BatchedLinAlg::MultTranspose.
|
||||
virtual void MultTranspose(const DenseTensor &A, const Vector &x,
|
||||
Vector &y) const;
|
||||
/// See BatchedLinAlg::Invert.
|
||||
virtual void Invert(DenseTensor &A) const = 0;
|
||||
/// See BatchedLinAlg::LUFactor.
|
||||
|
||||
+14
-10
@@ -82,23 +82,27 @@ void GPUBlas::DisableAtomics()
|
||||
}
|
||||
|
||||
void GPUBlasBatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x,
|
||||
Vector &y, real_t alpha, real_t beta) const
|
||||
Vector &y, real_t alpha, real_t beta,
|
||||
Op op) const
|
||||
{
|
||||
const int m = A.SizeI();
|
||||
const int n = A.SizeJ();
|
||||
const bool tr = (op == Op::T);
|
||||
|
||||
const int m = tr ? A.SizeJ() : A.SizeI();
|
||||
const int n = tr ? A.SizeI() : A.SizeJ();
|
||||
const int n_mat = A.SizeK();
|
||||
const int k = x.Size() / n / n_mat;
|
||||
|
||||
auto d_A = mfem::Reshape(A.Read(), m, n, n_mat);
|
||||
auto d_x = mfem::Reshape(x.Read(), n, k, n_mat);
|
||||
auto d_y = mfem::Reshape(beta == 0.0 ? y.Write() : y.ReadWrite(), m, k, n_mat);
|
||||
auto d_A = A.Read();
|
||||
auto d_x = x.Read(); // Shape: (n, k, n_mat)
|
||||
auto d_y = beta == 0.0 ? y.Write() : y.ReadWrite(); // Shape (m, k, n_mat)
|
||||
|
||||
const auto op = MFEM_CU_or_HIP(BLAS_OP_N);
|
||||
const auto op_A = tr ? MFEM_CU_or_HIP(BLAS_OP_T) : MFEM_CU_or_HIP(BLAS_OP_N);
|
||||
const auto op_B = MFEM_CU_or_HIP(BLAS_OP_N);
|
||||
|
||||
const blasStatus_t status = MFEM_GPUBLAS_PREFIX(gemmStridedBatched)(
|
||||
GPUBlas::Handle(), op, op, m, k, n, &alpha,
|
||||
d_A, m, m*n, d_x, n, n*k, &beta, d_y, m, m*k,
|
||||
n_mat);
|
||||
GPUBlas::Handle(), op_A, op_B, m, k, n,
|
||||
&alpha, d_A, m, m*n, d_x, n, n*k, &beta, d_y,
|
||||
m, m*k, n_mat);
|
||||
MFEM_VERIFY(status == MFEM_BLAS_SUCCESS, "GPU BLAS error.");
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,8 @@ class GPUBlasBatchedLinAlg : public BatchedLinAlgBase
|
||||
{
|
||||
public:
|
||||
void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha = 1.0, real_t beta = 1.0) const override;
|
||||
real_t alpha = 1.0, real_t beta = 1.0,
|
||||
Op op = Op::N) const override;
|
||||
void Invert(DenseTensor &A) const override;
|
||||
void LUFactor(DenseTensor &A, Array<int> &P) const override;
|
||||
void LUSolve(const DenseTensor &LU, const Array<int> &P,
|
||||
|
||||
@@ -54,19 +54,24 @@ magma_queue_t Magma::Queue()
|
||||
}
|
||||
|
||||
void MagmaBatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x,
|
||||
Vector &y, real_t alpha, real_t beta) const
|
||||
Vector &y, real_t alpha, real_t beta,
|
||||
Op op) const
|
||||
{
|
||||
const int m = A.SizeI();
|
||||
const int n = A.SizeJ();
|
||||
const bool tr = (op == Op::T);
|
||||
|
||||
const int m = tr ? A.SizeJ() : A.SizeI();
|
||||
const int n = tr ? A.SizeI() : A.SizeJ();
|
||||
const int n_mat = A.SizeK();
|
||||
const int k = x.Size() / n / n_mat;
|
||||
|
||||
auto d_A = mfem::Reshape(A.Read(), m, n, n_mat);
|
||||
auto d_x = mfem::Reshape(x.Read(), n, k, n_mat);
|
||||
auto d_y = mfem::Reshape(beta == 0.0 ? y.Write() : y.ReadWrite(), m, k, n_mat);
|
||||
auto d_A = A.Read();
|
||||
auto d_x = x.Read(); // Shape (n, k, n_mat);
|
||||
auto d_y = beta == 0.0 ? y.Write() : y.ReadWrite(); // Shape (m, k, n_mat);
|
||||
|
||||
magma_trans_t magma_op = tr ? MagmaNoTrans : MagmaTrans;
|
||||
|
||||
MFEM_MAGMABLAS_PREFIX(gemm_batched_strided)(
|
||||
MagmaNoTrans, MagmaNoTrans, m, k, n, alpha, d_A, m, m*n, d_x, n, n*k,
|
||||
magma_op, MagmaNoTrans, m, k, n, alpha, d_A, m, m*n, d_x, n, n*k,
|
||||
beta, d_y, m, m*k, n_mat, Magma::Queue());
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ class MagmaBatchedLinAlg : public BatchedLinAlgBase
|
||||
{
|
||||
public:
|
||||
void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha = 1.0, real_t beta = 1.0) const override;
|
||||
real_t alpha = 1.0, real_t beta = 1.0,
|
||||
Op op = Op::N) const override;
|
||||
void Invert(DenseTensor &A) const override;
|
||||
void LUFactor(DenseTensor &A, Array<int> &P) const override;
|
||||
void LUSolve(const DenseTensor &A, const Array<int> &P,
|
||||
|
||||
+110
-17
@@ -18,22 +18,37 @@ namespace mfem
|
||||
{
|
||||
|
||||
void NativeBatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x,
|
||||
Vector &y, real_t alpha, real_t beta) const
|
||||
Vector &y, real_t alpha, real_t beta,
|
||||
Op op) const
|
||||
{
|
||||
const bool tr = (op == Op::T);
|
||||
|
||||
const int m = A.SizeI();
|
||||
const int n = A.SizeJ();
|
||||
const int n_mat = A.SizeK();
|
||||
const int k = x.Size() / n / n_mat;
|
||||
const int k = x.Size() / (tr ? m : n) / n_mat;
|
||||
|
||||
auto d_A = mfem::Reshape(A.Read(), m, n, n_mat);
|
||||
auto d_x = mfem::Reshape(x.Read(), n, k, n_mat);
|
||||
auto d_y = mfem::Reshape(beta == 0.0 ? y.Write() : y.ReadWrite(), m, k, n_mat);
|
||||
auto d_A = Reshape(A.Read(), m, n, n_mat);
|
||||
auto d_x = Reshape(x.Read(), (tr ? m : n), k, n_mat);
|
||||
auto d_y = Reshape(beta == 0.0 ? y.Write() : y.ReadWrite(),
|
||||
(tr ? n : m), k, n_mat);
|
||||
|
||||
mfem::forall(n_mat, [=] MFEM_HOST_DEVICE (int i)
|
||||
if (tr)
|
||||
{
|
||||
kernels::AddMult(m, k, n, &d_A(0,0,i), &d_x(0,0,i), &d_y(0,0,i),
|
||||
alpha, beta);
|
||||
});
|
||||
mfem::forall(n_mat, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
kernels::AddMultAtB(m, n, k, &d_A(0,0,i), &d_x(0,0,i), &d_y(0,0,i),
|
||||
alpha, beta);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
mfem::forall(n_mat, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
kernels::AddMult(m, k, n, &d_A(0,0,i), &d_x(0,0,i), &d_y(0,0,i),
|
||||
alpha, beta);
|
||||
});
|
||||
}
|
||||
|
||||
// Alternative approach, threading also over the second index. Which one is
|
||||
// better?
|
||||
@@ -48,7 +63,85 @@ void NativeBatchedLinAlg::AddMult(const DenseTensor &A, const Vector &x,
|
||||
|
||||
void NativeBatchedLinAlg::Invert(DenseTensor &A) const
|
||||
{
|
||||
MFEM_ABORT("");
|
||||
const int m = A.SizeI();
|
||||
const int NE = A.SizeK();
|
||||
DenseTensor LU = A;
|
||||
Array<int> P(m*NE);
|
||||
|
||||
LUFactor(LU, P);
|
||||
|
||||
auto data_all = Reshape(LU.Read(), m, m, NE);
|
||||
auto piv_all = Reshape(P.Read(), m, NE);
|
||||
auto inv_all = Reshape(A.Write(), m, m, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
// A^{-1} = U^{-1} L^{-1} P
|
||||
// X <- U^{-1} (set only the upper triangular part of X)
|
||||
real_t *X = &inv_all(0, 0, e);
|
||||
real_t *x = X;
|
||||
const real_t *data = &data_all(0, 0, e);
|
||||
const int *ipiv = &piv_all(0, e);
|
||||
|
||||
for (int k = 0; k < m; k++)
|
||||
{
|
||||
const real_t minus_x_k = -(x[k] = 1.0 / data[k + k * m]);
|
||||
for (int i = 0; i < k; i++)
|
||||
{
|
||||
x[i] = data[i + k * m] * minus_x_k;
|
||||
}
|
||||
for (int j = k - 1; j >= 0; j--)
|
||||
{
|
||||
const real_t x_j = (x[j] /= data[j + j * m]);
|
||||
for (int i = 0; i < j; i++)
|
||||
{
|
||||
x[i] -= data[i + j * m] * x_j;
|
||||
}
|
||||
}
|
||||
x += m;
|
||||
}
|
||||
|
||||
// X <- X L^{-1} (use input only from the upper triangular part of X)
|
||||
{
|
||||
int k = m - 1;
|
||||
for (int j = 0; j < k; j++)
|
||||
{
|
||||
const real_t minus_L_kj = -data[k + j * m];
|
||||
for (int i = 0; i <= j; i++)
|
||||
{
|
||||
X[i + j * m] += X[i + k * m] * minus_L_kj;
|
||||
}
|
||||
for (int i = j + 1; i < m; i++)
|
||||
{
|
||||
X[i + j * m] = X[i + k * m] * minus_L_kj;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int k = m - 2; k >= 0; k--)
|
||||
{
|
||||
for (int j = 0; j < k; j++)
|
||||
{
|
||||
const real_t L_kj = data[k + j * m];
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
X[i + j * m] -= X[i + k * m] * L_kj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// X <- X P
|
||||
for (int k = m - 1; k >= 0; k--)
|
||||
{
|
||||
const int piv_k = ipiv[k];
|
||||
if (k != piv_k)
|
||||
{
|
||||
for (int i = 0; i < m; i++)
|
||||
{
|
||||
kernels::internal::Swap(X[i + k * m], X[i + piv_k * m]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void NativeBatchedLinAlg::LUFactor(DenseTensor &A, Array<int> &P) const
|
||||
@@ -58,8 +151,8 @@ void NativeBatchedLinAlg::LUFactor(DenseTensor &A, Array<int> &P) const
|
||||
const int NE = A.SizeK();
|
||||
P.SetSize(m*NE);
|
||||
|
||||
auto data_all = mfem::Reshape(A.ReadWrite(), m, m, NE);
|
||||
auto ipiv_all = mfem::Reshape(P.Write(), m, NE);
|
||||
auto data_all = Reshape(A.ReadWrite(), m, m, NE);
|
||||
auto ipiv_all = Reshape(P.Write(), m, NE);
|
||||
Array<bool> pivot_flag(1);
|
||||
pivot_flag[0] = true;
|
||||
bool *d_pivot_flag = pivot_flag.ReadWrite();
|
||||
@@ -87,12 +180,12 @@ void NativeBatchedLinAlg::LUFactor(DenseTensor &A, Array<int> &P) const
|
||||
// swap rows i and piv in both L and U parts
|
||||
for (int j = 0; j < m; j++)
|
||||
{
|
||||
mfem::kernels::internal::Swap<real_t>(data_all(i,j,e), data_all(piv,j,e));
|
||||
kernels::internal::Swap<real_t>(data_all(i,j,e), data_all(piv,j,e));
|
||||
}
|
||||
}
|
||||
} // pivot end
|
||||
|
||||
if (abs(data_all(i,i,e)) <= tol)
|
||||
if (std::abs(data_all(i,i,e)) <= tol)
|
||||
{
|
||||
d_pivot_flag[0] = false;
|
||||
}
|
||||
@@ -124,9 +217,9 @@ void NativeBatchedLinAlg::LUSolve(const DenseTensor &LU, const Array<int> &P,
|
||||
const int n_mat = LU.SizeK();
|
||||
const int n_rhs = x.Size() / m / n_mat;
|
||||
|
||||
auto d_LU = mfem::Reshape(LU.Read(), m, m, n_mat);
|
||||
auto d_P = mfem::Reshape(P.Read(), m, n_mat);
|
||||
auto d_x = mfem::Reshape(x.Write(), m, n_rhs, n_mat);
|
||||
auto d_LU = Reshape(LU.Read(), m, m, n_mat);
|
||||
auto d_P = Reshape(P.Read(), m, n_mat);
|
||||
auto d_x = Reshape(x.Write(), m, n_rhs, n_mat);
|
||||
|
||||
mfem::forall(n_mat * n_rhs, [=] MFEM_HOST_DEVICE (int idx)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ class NativeBatchedLinAlg : public BatchedLinAlgBase
|
||||
{
|
||||
public:
|
||||
void AddMult(const DenseTensor &A, const Vector &x, Vector &y,
|
||||
real_t alpha, real_t beta) const override;
|
||||
real_t alpha, real_t beta, Op op) const override;
|
||||
void Invert(DenseTensor &A) const override;
|
||||
void LUFactor(DenseTensor &A, Array<int> &P) const override;
|
||||
void LUSolve(const DenseTensor &LU, const Array<int> &P,
|
||||
|
||||
@@ -1174,6 +1174,31 @@ public:
|
||||
tdata.Wrap(ext_data, i*j*k, false);
|
||||
}
|
||||
|
||||
/// @brief Reset the DenseTensor to use the given external Memory @a mem and
|
||||
/// dimensions @a i, @a j, and @a k.
|
||||
///
|
||||
/// If @a own_mem is false, the DenseTensor will not own any of the pointers
|
||||
/// of @a mem.
|
||||
///
|
||||
/// Note that when @a own_mem is true, the @a mem object can be destroyed
|
||||
/// immediately by the caller but `mem.Delete()` should NOT be called since
|
||||
/// the DenseTensor object takes ownership of all pointers owned by @a mem.
|
||||
void NewMemoryAndSize(const Memory<real_t> &mem, int i, int j, int k,
|
||||
bool own_mem)
|
||||
{
|
||||
tdata.Delete();
|
||||
Mk.UseExternalData(NULL, i, j);
|
||||
nk = k;
|
||||
if (own_mem)
|
||||
{
|
||||
tdata = mem;
|
||||
}
|
||||
else
|
||||
{
|
||||
tdata.MakeAlias(mem, 0, i*j*k);
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the tensor elements equal to constant c
|
||||
DenseTensor &operator=(real_t c);
|
||||
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@
|
||||
|
||||
// Make sure that hypre and PETSc use the same size indices.
|
||||
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_PETSC)
|
||||
#if (defined(HYPRE_BIGINT) && !defined(PETSC_USE_64BIT_INDICES)) || \
|
||||
(!defined(HYPRE_BIGINT) && defined(PETSC_USE_64BIT_INDICES))
|
||||
#if ((defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT)) && !defined(PETSC_USE_64BIT_INDICES)) || \
|
||||
(!defined(HYPRE_BIGINT) && !defined(HYPRE_MIXEDINT) && defined(PETSC_USE_64BIT_INDICES))
|
||||
#error HYPRE and PETSC do not use the same size integers!
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+48
-5
@@ -211,6 +211,24 @@ HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
|
||||
own_ParVector = 1;
|
||||
}
|
||||
|
||||
HypreParVector::HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size,
|
||||
Vector &base, int offset, HYPRE_BigInt *col)
|
||||
: HypreParVector(comm, glob_size, nullptr, col, false)
|
||||
{
|
||||
MFEM_ASSERT(CanShallowCopy(base.GetMemory(), GetHypreMemoryClass()),
|
||||
"the MemoryTypes of 'base' are incompatible with Hypre!");
|
||||
MFEM_ASSERT(offset + size <= base.Size(),
|
||||
"the size of 'base' is too small!");
|
||||
|
||||
data.Delete();
|
||||
data.MakeAlias(base.GetMemory(), offset, size);
|
||||
hypre_Vector *x_loc = hypre_ParVectorLocalVector(x);
|
||||
hypre_VectorData(x_loc) = data.ReadWrite(GetHypreMemoryClass(), size);
|
||||
#ifdef HYPRE_USING_GPU
|
||||
hypre_VectorMemoryLocation(x_loc) = GetHypreMemoryLocation();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Call the move constructor on the "compatible" temp vector
|
||||
HypreParVector::HypreParVector(const HypreParVector &y) : HypreParVector(
|
||||
y.CreateCompatibleVector())
|
||||
@@ -1580,14 +1598,12 @@ void HypreParMatrix::GetDiag(Vector &diag) const
|
||||
{
|
||||
const int size = Height();
|
||||
diag.SetSize(size);
|
||||
auto hypre_ml = GetHypreMemoryLocation();
|
||||
// Avoid using GetHypreMemoryClass() since it may be MemoryClass::MANAGED and
|
||||
// that may not play well with the memory types used by 'diag'.
|
||||
MemoryClass hypre_mc = (hypre_ml == HYPRE_MEMORY_HOST) ?
|
||||
MemoryClass::HOST : MemoryClass::DEVICE;
|
||||
MemoryClass hypre_mc = GetHypreForallMemoryClass();
|
||||
real_t *diag_hd = diag.GetMemory().Write(hypre_mc, size);
|
||||
#if MFEM_HYPRE_VERSION >= 21800
|
||||
MFEM_VERIFY(A->diag->memory_location == hypre_ml,
|
||||
MFEM_VERIFY(A->diag->memory_location == GetHypreMemoryLocation(),
|
||||
"unexpected HypreParMatrix memory location!");
|
||||
#endif
|
||||
const HYPRE_Int *A_diag_i = A->diag->i;
|
||||
@@ -2494,7 +2510,7 @@ void HypreParMatrix::EliminateBC(const Array<int> &ess_dofs,
|
||||
|
||||
const int n_ess_dofs = ess_dofs.Size();
|
||||
const auto ess_dofs_d = ess_dofs.GetMemory().Read(
|
||||
GetHypreMemoryClass(), n_ess_dofs);
|
||||
GetHypreForallMemoryClass(), n_ess_dofs);
|
||||
|
||||
// Start communication to figure out which columns need to be eliminated in
|
||||
// the off-diagonal block
|
||||
@@ -2777,6 +2793,33 @@ void HypreParMatrix::PrintHash(std::ostream &os) const
|
||||
os << "col map offd hash : " << hf.GetHash() << '\n';
|
||||
}
|
||||
|
||||
real_t HypreParMatrix::FNorm() const
|
||||
{
|
||||
real_t norm_fro = 0.0;
|
||||
if (A != NULL)
|
||||
#if MFEM_HYPRE_VERSION >= 21900
|
||||
{
|
||||
const int ierr = hypre_ParCSRMatrixNormFro(A, &norm_fro);
|
||||
MFEM_VERIFY(ierr == 0, "");
|
||||
}
|
||||
#else
|
||||
{
|
||||
// HYPRE_USING_GPU is not defined for
|
||||
// MFEM_HYPRE_VERSION < 22100 and so here it is
|
||||
// guaranteed that the matrix is in "host" memory
|
||||
Vector Avec_diag(A->diag->data, A->diag->num_nonzeros);
|
||||
real_t normsqr_fro = InnerProduct(Avec_diag, Avec_diag);
|
||||
Vector Avec_offd(A->offd->data, A->offd->num_nonzeros);
|
||||
normsqr_fro += InnerProduct(Avec_offd, Avec_offd);
|
||||
MPI_Allreduce(MPI_IN_PLACE, &normsqr_fro, 1, MPITypeMap<real_t>::mpi_type,
|
||||
MPI_SUM, hypre_ParCSRMatrixComm(A));
|
||||
norm_fro = sqrt(normsqr_fro);
|
||||
}
|
||||
#endif
|
||||
return norm_fro;
|
||||
}
|
||||
|
||||
|
||||
inline void delete_hypre_ParCSRMatrixColMapOffd(hypre_ParCSRMatrix *A)
|
||||
{
|
||||
HYPRE_BigInt *A_col_map_offd = hypre_ParCSRMatrixColMapOffd(A);
|
||||
|
||||
+16
-5
@@ -247,6 +247,12 @@ public:
|
||||
allocated in the memory location HYPRE_MEMORY_DEVICE. */
|
||||
HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size, real_t *data_,
|
||||
HYPRE_BigInt *col, bool is_device_ptr = false);
|
||||
/** @brief Creates a vector that uses the data of the Vector @a base,
|
||||
starting at the given @a offset. */
|
||||
/** The @a base Vector must have memory types compatible with the MemoryClass
|
||||
returned by GetHypreMemoryClass(). */
|
||||
HypreParVector(MPI_Comm comm, HYPRE_BigInt glob_size, Vector &base,
|
||||
int offset, HYPRE_BigInt *col);
|
||||
/// Creates a deep copy of @a y
|
||||
HypreParVector(const HypreParVector &y);
|
||||
/// Move constructor for HypreParVector. "Steals" data from its argument.
|
||||
@@ -312,7 +318,8 @@ public:
|
||||
/// Sets the data of the Vector and the hypre_ParVector to @a data_.
|
||||
/** Must be used only for HypreParVector%s that do not own the data,
|
||||
e.g. created with the constructor:
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, double *, HYPRE_BigInt *). */
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, real_t *, HYPRE_BigInt *, bool).
|
||||
*/
|
||||
void SetData(real_t *data_);
|
||||
|
||||
/** @brief Prepare the HypreParVector for read access in hypre's device
|
||||
@@ -332,7 +339,7 @@ public:
|
||||
HYPRE_MEMORY_DEVICE. */
|
||||
/** This method must be used with HypreParVector%s that do not own the data,
|
||||
e.g. created with the constructor:
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, double *, HYPRE_BigInt *).
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, real_t *, HYPRE_BigInt *, bool).
|
||||
|
||||
The Memory @a mem must be accessible with the hypre MemoryClass defined
|
||||
by GetHypreMemoryClass(). */
|
||||
@@ -343,7 +350,7 @@ public:
|
||||
space, HYPRE_MEMORY_DEVICE. */
|
||||
/** This method must be used with HypreParVector%s that do not own the data,
|
||||
e.g. created with the constructor:
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, double *, HYPRE_BigInt *).
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, real_t *, HYPRE_BigInt *, bool).
|
||||
|
||||
The Memory @a mem must be accessible with the hypre MemoryClass defined
|
||||
by GetHypreMemoryClass(). */
|
||||
@@ -354,7 +361,7 @@ public:
|
||||
HYPRE_MEMORY_DEVICE. */
|
||||
/** This method must be used with HypreParVector%s that do not own the data,
|
||||
e.g. created with the constructor:
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, double *, HYPRE_BigInt *).
|
||||
HypreParVector(MPI_Comm, HYPRE_BigInt, real_t *, HYPRE_BigInt *, bool).
|
||||
|
||||
The Memory @a mem must be accessible with the hypre MemoryClass defined
|
||||
by GetHypreMemoryClass(). */
|
||||
@@ -393,7 +400,7 @@ private:
|
||||
/// Auxiliary vectors for typecasting
|
||||
mutable HypreParVector *X, *Y;
|
||||
/** @brief Auxiliary buffers for the case when the input or output arrays in
|
||||
methods like Mult(double, const Vector &, double, Vector &) need to be
|
||||
methods like Mult(real_t, const Vector &, real_t, Vector &) need to be
|
||||
deep copied in order to be used by hypre. */
|
||||
mutable Memory<real_t> auxX, auxY;
|
||||
|
||||
@@ -938,6 +945,10 @@ public:
|
||||
without the need to save the whole matrix. */
|
||||
void PrintHash(std::ostream &out) const;
|
||||
|
||||
/// @brief Return the Frobenius norm of the matrix (or 0 if the underlying
|
||||
/// hypre matrix is NULL)
|
||||
real_t FNorm() const;
|
||||
|
||||
/// Calls hypre's destroy function
|
||||
virtual ~HypreParMatrix() { Destroy(); }
|
||||
|
||||
|
||||
+38
-14
@@ -402,6 +402,43 @@ void MultABt(const int Aheight, const int Awidth, const int Bheight,
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Compute C = alpha*At*B + beta*C.
|
||||
|
||||
Multiply the transpose of a matrix of size @a Aheight x @a Awidth and data
|
||||
@a Adata with a matrix of size @a Aheight x @a Bwidth and data @a Bdata. */
|
||||
template<typename TA, typename TB, typename TC>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void AddMultAtB(const int Aheight, const int Awidth, const int Bwidth,
|
||||
const TA *Adata, const TB *Bdata, TC *Cdata, const TB alpha,
|
||||
const TA beta)
|
||||
{
|
||||
const int aw_x_bw = Awidth * Bwidth;
|
||||
|
||||
if (beta == 0.0)
|
||||
{
|
||||
for (int i = 0; i < aw_x_bw; i++) { Cdata[i] = 0.0; }
|
||||
}
|
||||
else if (beta != 1.0)
|
||||
{
|
||||
for (int i = 0; i < aw_x_bw; i++) { Cdata[i] *= beta; }
|
||||
}
|
||||
|
||||
TC *c = Cdata;
|
||||
for (int i = 0; i < Bwidth; ++i)
|
||||
{
|
||||
for (int j = 0; j < Awidth; ++j)
|
||||
{
|
||||
TC val = 0.0;
|
||||
for (int k = 0; k < Aheight; ++k)
|
||||
{
|
||||
val += alpha * Adata[j * Aheight + k] * Bdata[i * Aheight + k];
|
||||
}
|
||||
*c += val;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Multiply the transpose of a matrix of size @a Aheight x @a Awidth
|
||||
and data @a Adata with a matrix of size @a Aheight x @a Bwidth and data @a
|
||||
Bdata: At * B. Return the result in a matrix with data @a AtBdata. */
|
||||
@@ -410,20 +447,7 @@ MFEM_HOST_DEVICE inline
|
||||
void MultAtB(const int Aheight, const int Awidth, const int Bwidth,
|
||||
const TA *Adata, const TB *Bdata, TC *AtBdata)
|
||||
{
|
||||
TC *c = AtBdata;
|
||||
for (int i = 0; i < Bwidth; ++i)
|
||||
{
|
||||
for (int j = 0; j < Awidth; ++j)
|
||||
{
|
||||
TC val = 0.0;
|
||||
for (int k = 0; k < Aheight; ++k)
|
||||
{
|
||||
val += Adata[j * Aheight + k] * Bdata[i * Aheight + k];
|
||||
}
|
||||
*c = val;
|
||||
c++;
|
||||
}
|
||||
}
|
||||
AddMultAtB(Aheight, Awidth, Bwidth, Adata, Bdata, AtBdata, TB(1.0), TA(0.0));
|
||||
}
|
||||
|
||||
/// Given a matrix of size 2x1, 3x1, or 3x2, compute the left inverse.
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2010-2023, 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_MDVECTOR
|
||||
#define MFEM_MDVECTOR
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#include "vector.hpp"
|
||||
#include "general/mdspan.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template<int N, typename Layout = MDLayoutLeft<N>>
|
||||
struct MDVector : public MDSpan<Vector, N, Layout>
|
||||
{
|
||||
using base_t = MDSpan<Vector, N, Layout>;
|
||||
|
||||
/**
|
||||
* @brief MDVector default constructor (recursion)
|
||||
*/
|
||||
MDVector(): base_t() { }
|
||||
|
||||
/**
|
||||
* @brief MDVector recursion constructor
|
||||
* @param[in] n Dimension indice
|
||||
* @param[in] args Rest of dimension indices
|
||||
*/
|
||||
template <typename... Ts>
|
||||
MDVector(int n, Ts... args): MDVector(args...) { base_t::Setup(n, args...); }
|
||||
|
||||
/// Move constructor not supported
|
||||
MDVector(MDVector&&) = delete;
|
||||
|
||||
/// Copy constructor not supported
|
||||
MDVector(const MDVector&) = delete;
|
||||
|
||||
/// Move assignment not supported
|
||||
MDVector& operator=(MDVector&&) = delete;
|
||||
|
||||
/// Copy assignment not supported
|
||||
MDVector& operator=(const MDVector&) = delete;
|
||||
|
||||
using Vector::Read;
|
||||
using Vector::Write;
|
||||
using Vector::ReadWrite;
|
||||
using Vector::HostRead;
|
||||
using Vector::HostWrite;
|
||||
using Vector::HostReadWrite;
|
||||
|
||||
using Vector::GetData;
|
||||
using Vector::SetData;
|
||||
|
||||
using Vector::operator=;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_MDVECTOR
|
||||
+315
-235
@@ -9,12 +9,155 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../general/communication.hpp"
|
||||
#include "operator.hpp"
|
||||
#include "ode.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
std::string ODESolver::ExplicitTypes =
|
||||
"\n\tExplicit solver: \n\t"
|
||||
" RK : 1 - Forward Euler, 2 - RK2(0.5), 3 - RK3 SSP, 4 - RK4, 6 - RK6,\n\t"
|
||||
" AB : 11 - AB1, 12 - AB2, 13 - AB3, 14 - AB4, 15 - AB5\n";
|
||||
|
||||
std::string ODESolver::ImplicitTypes =
|
||||
"\n\tImplicit solver: \n\t"
|
||||
" (L-Stab): 21 - Backward Euler, 22 - SDIRK23(2), 23 - SDIRK33,\n\t"
|
||||
" (A-Stab): 32 - Implicit Midpoint, 33 - SDIRK23, 34 - SDIRK34,\n\t"
|
||||
" GA : 40 -- 50 - Generalized-alpha,\n\t"
|
||||
" AM : 51 - AM1, 52 - AM2, 53 - AM3, 54 - AM4\n";
|
||||
|
||||
std::string ODESolver::Types = ODESolver::ExplicitTypes +
|
||||
ODESolver::ImplicitTypes;
|
||||
|
||||
std::unique_ptr<ODESolver> ODESolver::Select(int ode_solver_type)
|
||||
{
|
||||
if (ode_solver_type < 20)
|
||||
{
|
||||
return SelectExplicit(ode_solver_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
return SelectImplicit(ode_solver_type);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ODESolver> ODESolver::SelectExplicit(int ode_solver_type)
|
||||
{
|
||||
using ode_ptr = std::unique_ptr<ODESolver>;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Explicit RK methods
|
||||
case 1: return ode_ptr(new ForwardEulerSolver);
|
||||
case 2: return ode_ptr(new RK2Solver(0.5)); // midpoint method
|
||||
case 3: return ode_ptr(new RK3SSPSolver);
|
||||
case 4: return ode_ptr(new RK4Solver);
|
||||
case 6: return ode_ptr(new RK6Solver);
|
||||
|
||||
// Explicit AB methods
|
||||
case 11: return ode_ptr(new AB1Solver);
|
||||
case 12: return ode_ptr(new AB2Solver);
|
||||
case 13: return ode_ptr(new AB3Solver);
|
||||
case 14: return ode_ptr(new AB4Solver);
|
||||
case 15: return ode_ptr(new AB5Solver);
|
||||
|
||||
default:
|
||||
MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ODESolver> ODESolver::SelectImplicit(int ode_solver_type)
|
||||
{
|
||||
using ode_ptr = std::unique_ptr<ODESolver>;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit L-stable methods
|
||||
case 21: return ode_ptr(new BackwardEulerSolver);
|
||||
case 22: return ode_ptr(new SDIRK23Solver(2));
|
||||
case 23: return ode_ptr(new SDIRK33Solver);
|
||||
|
||||
// Implicit A-stable methods (not L-stable)
|
||||
case 32: return ode_ptr(new ImplicitMidpointSolver);
|
||||
case 33: return ode_ptr(new SDIRK23Solver);
|
||||
case 34: return ode_ptr(new SDIRK34Solver);
|
||||
|
||||
// Implicit generalized alpha
|
||||
case 40: return ode_ptr(new GeneralizedAlphaSolver(0.0));
|
||||
case 41: return ode_ptr(new GeneralizedAlphaSolver(0.1));
|
||||
case 42: return ode_ptr(new GeneralizedAlphaSolver(0.2));
|
||||
case 43: return ode_ptr(new GeneralizedAlphaSolver(0.3));
|
||||
case 44: return ode_ptr(new GeneralizedAlphaSolver(0.4));
|
||||
case 45: return ode_ptr(new GeneralizedAlphaSolver(0.5));
|
||||
case 46: return ode_ptr(new GeneralizedAlphaSolver(0.6));
|
||||
case 47: return ode_ptr(new GeneralizedAlphaSolver(0.7));
|
||||
case 48: return ode_ptr(new GeneralizedAlphaSolver(0.8));
|
||||
case 49: return ode_ptr(new GeneralizedAlphaSolver(0.9));
|
||||
case 50: return ode_ptr(new GeneralizedAlphaSolver(1.0));
|
||||
|
||||
// Implicit AM methods
|
||||
case 51: return ode_ptr(new AM1Solver);
|
||||
case 52: return ode_ptr(new AM2Solver);
|
||||
case 53: return ode_ptr(new AM3Solver);
|
||||
case 54: return ode_ptr(new AM4Solver);
|
||||
|
||||
default:
|
||||
MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ODEStateDataVector::SetSize( int vsize, MemoryType m_t)
|
||||
{
|
||||
mem_type = m_t;
|
||||
for (int i = 0; i < smax; i++)
|
||||
{
|
||||
idx[i] = smax - i - 1;
|
||||
data[i].SetSize(vsize, mem_type);
|
||||
}
|
||||
|
||||
ss = 0;
|
||||
}
|
||||
|
||||
const Vector &ODEStateDataVector::Get(int i) const
|
||||
{
|
||||
MFEM_ASSERT_INDEX_IN_RANGE(i,0,ss);
|
||||
return data[idx[i]];
|
||||
}
|
||||
|
||||
Vector &ODEStateDataVector::Get(int i)
|
||||
{
|
||||
MFEM_ASSERT_INDEX_IN_RANGE(i,0,ss);
|
||||
return data[idx[i]];
|
||||
}
|
||||
|
||||
void ODEStateDataVector::Get(int i, Vector &vec) const
|
||||
{
|
||||
MFEM_ASSERT_INDEX_IN_RANGE(i,0,ss);
|
||||
vec = data[idx[i]];
|
||||
}
|
||||
|
||||
void ODEStateDataVector::Set(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT_INDEX_IN_RANGE(i,0,smax);
|
||||
data[idx[i]] = state;
|
||||
}
|
||||
|
||||
void ODEStateDataVector::Append(Vector &state)
|
||||
{
|
||||
ShiftStages();
|
||||
data[idx[0]] = state;
|
||||
Increment();
|
||||
}
|
||||
|
||||
void ODEStateDataVector::Print(std::ostream &os) const
|
||||
{
|
||||
os << ss <<"/" <<smax<<std::endl;
|
||||
idx.Print(os);
|
||||
for (int i = 0; i < ss; i++) { data[idx[i]].Print(os); }
|
||||
}
|
||||
|
||||
|
||||
void ODESolver::Init(TimeDependentOperator &f_)
|
||||
{
|
||||
this->f = &f_;
|
||||
@@ -344,104 +487,65 @@ const real_t RK8Solver::c[] =
|
||||
};
|
||||
|
||||
|
||||
AdamsBashforthSolver::AdamsBashforthSolver(int s_, const real_t *a_)
|
||||
AdamsBashforthSolver::AdamsBashforthSolver(int s_, const real_t *a_):
|
||||
stages(s_), state(s_)
|
||||
{
|
||||
smax = std::min(s_,5);
|
||||
a = a_;
|
||||
k = new Vector[5];
|
||||
dt_ = -1.0;
|
||||
|
||||
if (smax <= 2)
|
||||
{
|
||||
RKsolver = new RK2Solver();
|
||||
}
|
||||
else if (smax == 3)
|
||||
{
|
||||
RKsolver = new RK3SSPSolver();
|
||||
}
|
||||
else
|
||||
{
|
||||
RKsolver = new RK4Solver();
|
||||
}
|
||||
}
|
||||
|
||||
void AdamsBashforthSolver::GetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < s ),
|
||||
" AdamsBashforthSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
|
||||
state = k[idx[i]];
|
||||
}
|
||||
|
||||
const Vector &AdamsBashforthSolver::GetStateVector(int i)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < s ),
|
||||
" AdamsBashforthSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
|
||||
return k[idx[i]];
|
||||
}
|
||||
|
||||
|
||||
void AdamsBashforthSolver::SetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < smax ),
|
||||
" AdamsBashforthSolver::SetStateVector \n" <<
|
||||
" - Tried to set non-existent state "<<i);
|
||||
k[idx[i]] = state;
|
||||
s = std::max(i,s);
|
||||
}
|
||||
|
||||
void AdamsBashforthSolver::Init(TimeDependentOperator &f_)
|
||||
{
|
||||
ODESolver::Init(f_);
|
||||
RKsolver->Init(f_);
|
||||
idx.SetSize(smax);
|
||||
for (int i = 0; i < smax; i++)
|
||||
{
|
||||
idx[i] = (smax-i)%smax;
|
||||
k[i].SetSize(f->Width());
|
||||
}
|
||||
s = 0;
|
||||
if (RKsolver) { RKsolver->Init(f_); }
|
||||
state.SetSize(f->Width(), mem_type);
|
||||
dt_ = -1.0;
|
||||
}
|
||||
|
||||
void AdamsBashforthSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
{
|
||||
if ( (dt_ > 0.0) && (fabs(dt-dt_) >10*std::numeric_limits<real_t>::epsilon()))
|
||||
CheckTimestep(dt);
|
||||
|
||||
if (state.Size() >= stages -1)
|
||||
{
|
||||
s = 0;
|
||||
f->SetTime(t);
|
||||
f->Mult(x, state[0]);
|
||||
state.Increment();
|
||||
for (int i = 0; i < stages; i++)
|
||||
{
|
||||
x.Add(a[i]*dt, state[i]);
|
||||
}
|
||||
t += dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->Mult(x,state[0]);
|
||||
RKsolver->Step(x,t,dt);
|
||||
state.Increment();
|
||||
}
|
||||
|
||||
state.ShiftStages();
|
||||
}
|
||||
|
||||
void AdamsBashforthSolver::CheckTimestep(real_t dt)
|
||||
{
|
||||
if (dt_ < 0.0)
|
||||
{
|
||||
dt_ = dt;
|
||||
return;
|
||||
}
|
||||
else if (fabs(dt-dt_) >10*std::numeric_limits<real_t>::epsilon())
|
||||
{
|
||||
state.Reset();
|
||||
dt_ = dt;
|
||||
|
||||
if (print())
|
||||
{
|
||||
mfem::out << "WARNING:" << std::endl;
|
||||
mfem::out << " - Time step changed" << std::endl;
|
||||
mfem::out << " - Purging Adams-Bashforth history" << std::endl;
|
||||
mfem::out << " - Purging time stepping history" << std::endl;
|
||||
mfem::out << " - Will run Runge-Kutta to rebuild history" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
s++;
|
||||
s = std::min(s, smax);
|
||||
if (s == smax)
|
||||
{
|
||||
f->SetTime(t);
|
||||
f->Mult(x, k[idx[0]]);
|
||||
for (int i = 0; i < s; i++)
|
||||
{
|
||||
x.Add(a[i]*dt, k[idx[i]]);
|
||||
}
|
||||
t += dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->Mult(x,k[idx[0]]);
|
||||
RKsolver->Step(x,t,dt);
|
||||
}
|
||||
|
||||
// Shift the index
|
||||
for (int i = 0; i < smax; i++) { idx[i] = ++idx[i]%smax; }
|
||||
}
|
||||
|
||||
const real_t AB1Solver::a[] =
|
||||
@@ -455,110 +559,68 @@ const real_t AB4Solver::a[] =
|
||||
const real_t AB5Solver::a[] =
|
||||
{1901.0/720.0,-2774.0/720.0, 2616.0/720.0,-1274.0/720.0, 251.0/720.0};
|
||||
|
||||
AdamsMoultonSolver::AdamsMoultonSolver(int s_, const real_t *a_)
|
||||
|
||||
AdamsMoultonSolver::AdamsMoultonSolver(int s_, const real_t *a_):
|
||||
stages(s_), state(s_)
|
||||
{
|
||||
s = 0;
|
||||
smax = std::min(s_+1,5);
|
||||
a = a_;
|
||||
k = new Vector[5];
|
||||
dt_ = -1.0;
|
||||
|
||||
if (smax <= 3)
|
||||
{
|
||||
RKsolver = new SDIRK23Solver();
|
||||
}
|
||||
else
|
||||
{
|
||||
RKsolver = new SDIRK34Solver();
|
||||
}
|
||||
}
|
||||
|
||||
const Vector &AdamsMoultonSolver::GetStateVector(int i)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < s ),
|
||||
" AdamsMoultonSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
return k[idx[i+1]];
|
||||
}
|
||||
|
||||
void AdamsMoultonSolver::GetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < s ),
|
||||
" AdamsMoultonSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
state = k[idx[i+1]];
|
||||
}
|
||||
|
||||
void AdamsMoultonSolver::SetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i >= 0) && ( i < smax ),
|
||||
" AdamsMoultonSolver::SetStateVector \n" <<
|
||||
" - Tried to set non-existent state "<<i);
|
||||
k[idx[i+1]] = state;
|
||||
s = std::max(i,s);
|
||||
}
|
||||
|
||||
void AdamsMoultonSolver::Init(TimeDependentOperator &f_)
|
||||
{
|
||||
ODESolver::Init(f_);
|
||||
RKsolver->Init(f_);
|
||||
int n = f->Width();
|
||||
idx.SetSize(smax);
|
||||
for (int i = 0; i < smax; i++)
|
||||
{
|
||||
idx[i] = (smax-i)%smax;
|
||||
k[i].SetSize(n);
|
||||
}
|
||||
s = 0;
|
||||
if (RKsolver) { RKsolver->Init(f_); }
|
||||
state.SetSize(f->Width(), mem_type);
|
||||
dt_ = -1.0;
|
||||
}
|
||||
|
||||
void AdamsMoultonSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
{
|
||||
if ( (dt_ > 0.0) && (fabs(dt-dt_) >10*std::numeric_limits<real_t>::epsilon()))
|
||||
if (dt_ < 0.0)
|
||||
{
|
||||
s = 0;
|
||||
dt_ = dt;
|
||||
}
|
||||
else if (fabs(dt-dt_) > 10*std::numeric_limits<real_t>::epsilon())
|
||||
{
|
||||
state.Reset();
|
||||
dt_ = dt;
|
||||
|
||||
if (print())
|
||||
{
|
||||
mfem::out << "WARNING:" << std::endl;
|
||||
mfem::out << " - Time step changed" << std::endl;
|
||||
mfem::out << " - Purging Adams-Moulton history" << std::endl;
|
||||
mfem::out << " - Purging time stepping history" << std::endl;
|
||||
mfem::out << " - Will run Runge-Kutta to rebuild history" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if ((s == 0)&&(smax>1))
|
||||
if ((state.Size() == 0)&&(stages>1))
|
||||
{
|
||||
f->Mult(x,k[idx[1]]);
|
||||
f->Mult(x,state[0]);
|
||||
state.Increment();
|
||||
}
|
||||
s++;
|
||||
s = std::min(s, smax);
|
||||
|
||||
if (s >= smax-1)
|
||||
if (state.Size() >= stages )
|
||||
{
|
||||
f->SetTime(t);
|
||||
for (int i = 1; i < smax; i++)
|
||||
for (int i = 0; i < stages; i++)
|
||||
{
|
||||
x.Add(a[i]*dt, k[idx[i]]);
|
||||
x.Add(a[i+1]*dt, state[i]);
|
||||
}
|
||||
f->ImplicitSolve(a[0]*dt, x, k[idx[0]]);
|
||||
x.Add(a[0]*dt, k[idx[0]]);
|
||||
state.ShiftStages();
|
||||
f->ImplicitSolve(a[0]*dt, x, state[0]);
|
||||
x.Add(a[0]*dt, state[0]);
|
||||
t += dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.ShiftStages();
|
||||
RKsolver->Step(x,t,dt);
|
||||
f->Mult(x,k[idx[0]]);
|
||||
f->Mult(x,state[0]);
|
||||
state.Increment();
|
||||
}
|
||||
|
||||
|
||||
// Shift the index
|
||||
for (int i = 0; i < smax; i++) { idx[i] = ++idx[i]%smax; }
|
||||
}
|
||||
|
||||
const real_t AM0Solver::a[] =
|
||||
{1.0};
|
||||
const real_t AM1Solver::a[] =
|
||||
{0.5, 0.5};
|
||||
const real_t AM2Solver::a[] =
|
||||
@@ -817,34 +879,7 @@ void GeneralizedAlphaSolver::Init(TimeDependentOperator &f_)
|
||||
ODESolver::Init(f_);
|
||||
k.SetSize(f->Width(), mem_type);
|
||||
y.SetSize(f->Width(), mem_type);
|
||||
xdot.SetSize(f->Width(), mem_type);
|
||||
xdot = 0.0;
|
||||
nstate = 0;
|
||||
}
|
||||
|
||||
const Vector &GeneralizedAlphaSolver::GetStateVector(int i)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0) && (nstate == 1),
|
||||
"GeneralizedAlphaSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
return xdot;
|
||||
}
|
||||
|
||||
void GeneralizedAlphaSolver::GetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0) && (nstate == 1),
|
||||
"GeneralizedAlphaSolver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
state = xdot;
|
||||
}
|
||||
|
||||
void GeneralizedAlphaSolver::SetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0),
|
||||
"GeneralizedAlphaSolver::SetStateVector \n" <<
|
||||
" - Tried to set non-existent state "<<i);
|
||||
xdot = state;
|
||||
nstate = 1;
|
||||
state.SetSize(f->Width(), mem_type);
|
||||
}
|
||||
|
||||
void GeneralizedAlphaSolver::SetRhoInf(real_t rho_inf)
|
||||
@@ -884,17 +919,17 @@ void GeneralizedAlphaSolver::PrintProperties(std::ostream &os)
|
||||
}
|
||||
}
|
||||
|
||||
// This routine assumes xdot is initialized.
|
||||
// This routine state[0] represents xdot
|
||||
void GeneralizedAlphaSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
{
|
||||
if (nstate == 0)
|
||||
if (state.Size() == 0)
|
||||
{
|
||||
f->Mult(x,xdot);
|
||||
nstate = 1;
|
||||
f->Mult(x,state[0]);
|
||||
state.Increment();
|
||||
}
|
||||
|
||||
// Set y = x + alpha_f*(1.0 - (gamma/alpha_m))*dt*xdot
|
||||
add(x, alpha_f*(1.0 - (gamma/alpha_m))*dt, xdot, y);
|
||||
add(x, alpha_f*(1.0 - (gamma/alpha_m))*dt, state[0], y);
|
||||
|
||||
// Solve k = f(y + dt_eff*k)
|
||||
real_t dt_eff = (gamma*alpha_f/alpha_m)*dt;
|
||||
@@ -902,11 +937,11 @@ void GeneralizedAlphaSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
f->ImplicitSolve(dt_eff, y, k);
|
||||
|
||||
// Update x and xdot
|
||||
x.Add((1.0 - (gamma/alpha_m))*dt, xdot);
|
||||
x.Add((1.0 - (gamma/alpha_m))*dt, state[0]);
|
||||
x.Add( (gamma/alpha_m) *dt, k);
|
||||
|
||||
xdot *= (1.0-(1.0/alpha_m));
|
||||
xdot.Add((1.0/alpha_m),k);
|
||||
state[0] *= (1.0-(1.0/alpha_m));
|
||||
state[0].Add((1.0/alpha_m),k);
|
||||
|
||||
t += dt;
|
||||
}
|
||||
@@ -1017,18 +1052,75 @@ SIAVSolver::Step(Vector &q, Vector &p, real_t &t, real_t &dt)
|
||||
}
|
||||
}
|
||||
|
||||
std::string SecondOrderODESolver::Types =
|
||||
"ODE solver: \n\t"
|
||||
" [0--10] - GeneralizedAlpha(0.1 * s),\n\t"
|
||||
" 11 - Average Acceleration, 12 - Linear Acceleration\n\t"
|
||||
" 13 - CentralDifference, 14 - FoxGoodwin";
|
||||
|
||||
SecondOrderODESolver* SecondOrderODESolver::Select(int ode_solver_type)
|
||||
{
|
||||
SecondOrderODESolver* ode_solver = NULL;
|
||||
switch (ode_solver_type)
|
||||
{
|
||||
// Implicit methods
|
||||
case 0: ode_solver = new GeneralizedAlpha2Solver(0.0); break;
|
||||
case 1: ode_solver = new GeneralizedAlpha2Solver(0.1); break;
|
||||
case 2: ode_solver = new GeneralizedAlpha2Solver(0.2); break;
|
||||
case 3: ode_solver = new GeneralizedAlpha2Solver(0.3); break;
|
||||
case 4: ode_solver = new GeneralizedAlpha2Solver(0.4); break;
|
||||
case 5: ode_solver = new GeneralizedAlpha2Solver(0.5); break;
|
||||
case 6: ode_solver = new GeneralizedAlpha2Solver(0.6); break;
|
||||
case 7: ode_solver = new GeneralizedAlpha2Solver(0.7); break;
|
||||
case 8: ode_solver = new GeneralizedAlpha2Solver(0.8); break;
|
||||
case 9: ode_solver = new GeneralizedAlpha2Solver(0.9); break;
|
||||
case 10: ode_solver = new GeneralizedAlpha2Solver(1.0); break;
|
||||
|
||||
case 11: ode_solver = new AverageAccelerationSolver(); break;
|
||||
case 12: ode_solver = new LinearAccelerationSolver(); break;
|
||||
case 13: ode_solver = new CentralDifferenceSolver(); break;
|
||||
case 14: ode_solver = new FoxGoodwinSolver(); break;
|
||||
|
||||
default:
|
||||
MFEM_ABORT("Unknown ODE solver type: " << ode_solver_type);
|
||||
}
|
||||
return ode_solver;
|
||||
}
|
||||
|
||||
// In this routine state[0] represents d2xdt2
|
||||
void SecondOrderODESolver::EulerStep(Vector &x, Vector &dxdt, real_t &t,
|
||||
real_t &dt)
|
||||
{
|
||||
x.Add(dt, dxdt);
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(0.5*dt*dt, dt, x, dxdt, state[0]);
|
||||
|
||||
x .Add(0.5*dt*dt, state[0]);
|
||||
dxdt.Add(dt, state[0]);
|
||||
t += dt;
|
||||
}
|
||||
|
||||
// In this routine state[0] represents d2xdt2
|
||||
void SecondOrderODESolver::MidPointStep(Vector &x, Vector &dxdt, real_t &t,
|
||||
real_t &dt)
|
||||
{
|
||||
x.Add(0.5*dt, dxdt);
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(0.25*dt*dt, 0.5*dt, x, dxdt, state[0]);
|
||||
|
||||
x.Add(0.5*dt, dxdt);
|
||||
x.Add(0.5*dt*dt, state[0]);
|
||||
dxdt.Add(dt, state[0]);
|
||||
t += dt;
|
||||
}
|
||||
|
||||
void SecondOrderODESolver::Init(SecondOrderTimeDependentOperator &f_)
|
||||
{
|
||||
this->f = &f_;
|
||||
mem_type = GetMemoryType(f_.GetMemoryClass());
|
||||
}
|
||||
|
||||
void NewmarkSolver::Init(SecondOrderTimeDependentOperator &f_)
|
||||
{
|
||||
SecondOrderODESolver::Init(f_);
|
||||
d2xdt2.SetSize(f->Width());
|
||||
d2xdt2 = 0.0;
|
||||
first = true;
|
||||
state.SetSize(f->Width(), mem_type);
|
||||
}
|
||||
|
||||
void NewmarkSolver::PrintProperties(std::ostream &os)
|
||||
@@ -1060,6 +1152,7 @@ void NewmarkSolver::PrintProperties(std::ostream &os)
|
||||
}
|
||||
}
|
||||
|
||||
// In this routine state[0] represents d2xdt2
|
||||
void NewmarkSolver::Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt)
|
||||
{
|
||||
real_t fac0 = 0.5 - beta;
|
||||
@@ -1068,60 +1161,38 @@ void NewmarkSolver::Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt)
|
||||
real_t fac4 = gamma;
|
||||
|
||||
// In the first pass compute d2xdt2 directly from operator.
|
||||
if (first)
|
||||
if (state.Size() == 0)
|
||||
{
|
||||
f->Mult(x, dxdt, d2xdt2);
|
||||
first = false;
|
||||
if (no_mult)
|
||||
{
|
||||
MidPointStep(x, dxdt, t, dt);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->Mult(x, dxdt, state[0]);
|
||||
}
|
||||
}
|
||||
f->SetTime(t + dt);
|
||||
|
||||
x.Add(dt, dxdt);
|
||||
x.Add(fac0*dt*dt, d2xdt2);
|
||||
dxdt.Add(fac2*dt, d2xdt2);
|
||||
x.Add(fac0*dt*dt, state[0]);
|
||||
dxdt.Add(fac2*dt, state[0]);
|
||||
|
||||
f->SetTime(t + dt);
|
||||
f->ImplicitSolve(fac3*dt*dt, fac4*dt, x, dxdt, d2xdt2);
|
||||
f->ImplicitSolve(fac3*dt*dt, fac4*dt, x, dxdt, state[0]);
|
||||
|
||||
x .Add(fac3*dt*dt, d2xdt2);
|
||||
dxdt.Add(fac4*dt, d2xdt2);
|
||||
x .Add(fac3*dt*dt, state[0]);
|
||||
dxdt.Add(fac4*dt, state[0]);
|
||||
t += dt;
|
||||
}
|
||||
|
||||
void GeneralizedAlpha2Solver::Init(SecondOrderTimeDependentOperator &f_)
|
||||
{
|
||||
SecondOrderODESolver::Init(f_);
|
||||
xa.SetSize(f->Width());
|
||||
va.SetSize(f->Width());
|
||||
aa.SetSize(f->Width());
|
||||
d2xdt2.SetSize(f->Width());
|
||||
d2xdt2 = 0.0;
|
||||
nstate = 0;
|
||||
}
|
||||
|
||||
const Vector &GeneralizedAlpha2Solver::GetStateVector(int i)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0) && (nstate == 1),
|
||||
"GeneralizedAlpha2Solver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
return d2xdt2;
|
||||
}
|
||||
|
||||
|
||||
void GeneralizedAlpha2Solver::GetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0) && (nstate == 1),
|
||||
"GeneralizedAlpha2Solver::GetStateVector \n" <<
|
||||
" - Tried to get non-existent state "<<i);
|
||||
state = d2xdt2;
|
||||
}
|
||||
|
||||
void GeneralizedAlpha2Solver::SetStateVector(int i, Vector &state)
|
||||
{
|
||||
MFEM_ASSERT( (i == 0),
|
||||
"GeneralizedAlpha2Solver::SetStateVector \n" <<
|
||||
" - Tried to set non-existent state "<<i);
|
||||
d2xdt2 = state;
|
||||
nstate = 1;
|
||||
xa.SetSize(f->Width(), mem_type);
|
||||
va.SetSize(f->Width(), mem_type);
|
||||
aa.SetSize(f->Width(), mem_type);
|
||||
}
|
||||
|
||||
void GeneralizedAlpha2Solver::PrintProperties(std::ostream &os)
|
||||
@@ -1153,6 +1224,7 @@ void GeneralizedAlpha2Solver::PrintProperties(std::ostream &os)
|
||||
}
|
||||
}
|
||||
|
||||
// In this routine state[0] represents d2xdt2
|
||||
void GeneralizedAlpha2Solver::Step(Vector &x, Vector &dxdt,
|
||||
real_t &t, real_t &dt)
|
||||
{
|
||||
@@ -1164,16 +1236,24 @@ void GeneralizedAlpha2Solver::Step(Vector &x, Vector &dxdt,
|
||||
real_t fac5 = alpha_m;
|
||||
|
||||
// In the first pass compute d2xdt2 directly from operator.
|
||||
if (nstate == 0)
|
||||
if (state.Size() == 0)
|
||||
{
|
||||
f->Mult(x, dxdt, d2xdt2);
|
||||
nstate = 1;
|
||||
if (no_mult)
|
||||
{
|
||||
MidPointStep(x, dxdt, t, dt);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
f->Mult(x, dxdt, state[0]);
|
||||
}
|
||||
state.Increment();
|
||||
}
|
||||
|
||||
// Predict alpha levels
|
||||
add(dxdt, fac0*dt, d2xdt2, va);
|
||||
add(dxdt, fac0*dt, state[0], va);
|
||||
add(x, fac1*dt, va, xa);
|
||||
add(dxdt, fac2*dt, d2xdt2, va);
|
||||
add(dxdt, fac2*dt, state[0], va);
|
||||
|
||||
// Solve alpha levels
|
||||
f->SetTime(t + dt);
|
||||
@@ -1190,8 +1270,8 @@ void GeneralizedAlpha2Solver::Step(Vector &x, Vector &dxdt,
|
||||
dxdt *= 1.0 - 1.0/fac1;
|
||||
dxdt.Add (1.0/fac1, va);
|
||||
|
||||
d2xdt2 *= 1.0 - 1.0/fac5;
|
||||
d2xdt2.Add (1.0/fac5, aa);
|
||||
state[0] *= 1.0 - 1.0/fac5;
|
||||
state[0].Add (1.0/fac5, aa);
|
||||
|
||||
t += dt;
|
||||
}
|
||||
|
||||
+319
-246
@@ -12,13 +12,99 @@
|
||||
#ifndef MFEM_ODE
|
||||
#define MFEM_ODE
|
||||
|
||||
#include "../general/communication.hpp"
|
||||
#include "../config/config.hpp"
|
||||
#include "operator.hpp"
|
||||
#include "../general/communication.hpp"
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/// An interface for storing state of previous timesteps
|
||||
class ODEStateData
|
||||
{
|
||||
public:
|
||||
/// Get the maximum number of stored stages
|
||||
virtual int MaxSize() const = 0;
|
||||
|
||||
/// Get the current number of stored stages
|
||||
virtual int Size() const = 0;
|
||||
|
||||
/// Get the ith state vector
|
||||
virtual const Vector &Get(int i) const = 0;
|
||||
|
||||
/// Get the ith state vector - non-const version
|
||||
virtual Vector &Get(int i) = 0;
|
||||
|
||||
/// Get the ith state vector - with a copy
|
||||
virtual void Get(int i, Vector &vec) const = 0;
|
||||
|
||||
/// Set the ith state vector
|
||||
virtual void Set(int i, Vector &state) = 0;
|
||||
|
||||
/// Add state vector and increment state size
|
||||
virtual void Append(Vector &state) = 0;
|
||||
|
||||
/// Virtual destructor
|
||||
virtual ~ODEStateData() = default;
|
||||
};
|
||||
|
||||
/// An implementation of ODEStateData that stores states in an std::vector<Vector>
|
||||
class ODEStateDataVector : public ODEStateData
|
||||
{
|
||||
private:
|
||||
MemoryType mem_type;
|
||||
int ss, smax;
|
||||
std::vector<Vector> data;
|
||||
Array<int> idx;
|
||||
|
||||
public:
|
||||
ODEStateDataVector (int smax): smax(smax)
|
||||
{
|
||||
data.resize(smax);
|
||||
idx.SetSize(smax);
|
||||
ss = 0;
|
||||
};
|
||||
|
||||
/// Set the number of stages and the size of the vectors
|
||||
void SetSize(int vsize, MemoryType mem_type);
|
||||
|
||||
/// Shift the stage counter for the next timestep
|
||||
inline void ShiftStages()
|
||||
{
|
||||
for (int i = 0; i < smax; i++) { idx[i] = (++idx[i])%smax; }
|
||||
};
|
||||
|
||||
/// Increment the stage counter
|
||||
void Increment() { ss++; ss = std::min(ss,smax); };
|
||||
|
||||
/// Reset the stage counter
|
||||
void Reset() { ss = 0; };
|
||||
|
||||
/// Reference access to the ith vector.
|
||||
inline Vector & operator[](int i) { return data[idx[i]]; };
|
||||
|
||||
/// Const reference access to the ith vector.
|
||||
inline const Vector &operator[](int i) const { return data[idx[i]]; };
|
||||
|
||||
/// Print state data
|
||||
void Print(std::ostream &os = mfem::out) const ;
|
||||
|
||||
int MaxSize() const override { return smax; };
|
||||
|
||||
int Size() const override { return ss; };
|
||||
|
||||
const Vector &Get(int i) const override;
|
||||
Vector &Get(int i) override;
|
||||
void Get(int i, Vector &vec) const override;
|
||||
|
||||
void Set(int i, Vector &state) override;
|
||||
|
||||
void Append(Vector &state) override;
|
||||
};
|
||||
|
||||
|
||||
/// Abstract class for solving systems of ODEs: dx/dt = f(x,t)
|
||||
class ODESolver
|
||||
{
|
||||
@@ -92,26 +178,48 @@ public:
|
||||
while (t < tf) { Step(x, t, dt); }
|
||||
}
|
||||
|
||||
/// Function for getting and setting the state vectors
|
||||
virtual int GetMaxStateSize() { return 0; }
|
||||
virtual int GetStateSize() { return 0; }
|
||||
virtual const Vector &GetStateVector(int i)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
Vector *s = NULL; return *s; // Make some compiler happy
|
||||
}
|
||||
virtual void GetStateVector(int i, Vector &state)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
}
|
||||
virtual void SetStateVector(int i, Vector &state)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
}
|
||||
/// Returns how many State vectors the ODE requires
|
||||
virtual int GetStateSize() { return 0; };
|
||||
|
||||
// Help info for ODESolver options
|
||||
static MFEM_EXPORT std::string ExplicitTypes;
|
||||
static MFEM_EXPORT std::string ImplicitTypes;
|
||||
static MFEM_EXPORT std::string Types;
|
||||
|
||||
/// Function for selecting the desired ODESolver (Explicit and Implicit)
|
||||
/// Returns an ODESolver pointer based on an type
|
||||
/// Caller gets ownership of the object and is responsible for its deletion
|
||||
static MFEM_EXPORT std::unique_ptr<ODESolver> Select(const int ode_solver_type);
|
||||
|
||||
/// Function for selecting the desired Explicit ODESolver
|
||||
/// Returns an ODESolver pointer based on an type
|
||||
/// Caller gets ownership of the object and is responsible for its deletion
|
||||
static MFEM_EXPORT std::unique_ptr<ODESolver> SelectExplicit(
|
||||
const int ode_solver_type);
|
||||
|
||||
/// Function for selecting the desired Implicit ODESolver
|
||||
/// Returns an ODESolver pointer based on an type
|
||||
/// Caller gets ownership of the object and is responsible for its deletion
|
||||
static MFEM_EXPORT std::unique_ptr<ODESolver> SelectImplicit(
|
||||
const int ode_solver_type);
|
||||
|
||||
virtual ~ODESolver() { }
|
||||
};
|
||||
|
||||
/// Abstract class for an ODESolver that has state history implemented as ODEStateData
|
||||
class ODESolverWithStates : public ODESolver
|
||||
{
|
||||
public:
|
||||
/// Returns the StateData
|
||||
virtual ODEStateData& GetState() = 0;
|
||||
|
||||
/// Returns the StateData
|
||||
virtual const ODEStateData& GetState() const = 0;
|
||||
|
||||
/// Returns how many State vectors the ODE requires
|
||||
virtual int GetStateSize() { return GetState().MaxSize(); };
|
||||
};
|
||||
|
||||
|
||||
/// The classical forward Euler method
|
||||
class ForwardEulerSolver : public ODESolver
|
||||
@@ -217,196 +325,13 @@ public:
|
||||
class RK8Solver : public ExplicitRKSolver
|
||||
{
|
||||
private:
|
||||
static const real_t a[66], b[12], c[11];
|
||||
static MFEM_EXPORT const real_t a[66], b[12], c[11];
|
||||
|
||||
public:
|
||||
RK8Solver() : ExplicitRKSolver(12, a, b, c) { }
|
||||
};
|
||||
|
||||
|
||||
/** An explicit Adams-Bashforth method. */
|
||||
class AdamsBashforthSolver : public ODESolver
|
||||
{
|
||||
private:
|
||||
int s, smax;
|
||||
const real_t *a;
|
||||
Vector *k;
|
||||
Array<int> idx;
|
||||
ODESolver *RKsolver;
|
||||
real_t dt_;
|
||||
|
||||
inline bool print()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
return Mpi::IsInitialized() ? Mpi::Root() : true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
AdamsBashforthSolver(int s_, const real_t *a_);
|
||||
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
int GetMaxStateSize() override { return smax; };
|
||||
int GetStateSize() override { return s; };
|
||||
const Vector &GetStateVector(int i) override;
|
||||
void GetStateVector(int i, Vector &state) override;
|
||||
void SetStateVector(int i, Vector &state) override;
|
||||
|
||||
~AdamsBashforthSolver()
|
||||
{
|
||||
if (RKsolver) { delete RKsolver; }
|
||||
delete [] k;
|
||||
}
|
||||
};
|
||||
|
||||
/** A 1-stage, 1st order AB method. */
|
||||
class AB1Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[1];
|
||||
|
||||
public:
|
||||
AB1Solver() : AdamsBashforthSolver(1, a) { }
|
||||
};
|
||||
|
||||
/** A 2-stage, 2nd order AB method. */
|
||||
class AB2Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[2];
|
||||
|
||||
public:
|
||||
AB2Solver() : AdamsBashforthSolver(2, a) { }
|
||||
};
|
||||
|
||||
/** A 3-stage, 3rd order AB method. */
|
||||
class AB3Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[3];
|
||||
|
||||
public:
|
||||
AB3Solver() : AdamsBashforthSolver(3, a) { }
|
||||
};
|
||||
|
||||
/** A 4-stage, 4th order AB method. */
|
||||
class AB4Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[4];
|
||||
|
||||
public:
|
||||
AB4Solver() : AdamsBashforthSolver(4, a) { }
|
||||
};
|
||||
|
||||
/** A 5-stage, 5th order AB method. */
|
||||
class AB5Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[5];
|
||||
|
||||
public:
|
||||
AB5Solver() : AdamsBashforthSolver(5, a) { }
|
||||
};
|
||||
|
||||
|
||||
/** An implicit Adams-Moulton method. */
|
||||
class AdamsMoultonSolver : public ODESolver
|
||||
{
|
||||
private:
|
||||
int s, smax;
|
||||
const real_t *a;
|
||||
Vector *k;
|
||||
Array<int> idx;
|
||||
ODESolver *RKsolver;
|
||||
real_t dt_;
|
||||
|
||||
inline bool print()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
return Mpi::IsInitialized() ? Mpi::Root() : true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
public:
|
||||
AdamsMoultonSolver(int s_, const real_t *a_);
|
||||
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
int GetMaxStateSize() override { return smax-1; };
|
||||
int GetStateSize() override { return s-1; };
|
||||
const Vector &GetStateVector(int i) override;
|
||||
void GetStateVector(int i, Vector &state) override;
|
||||
void SetStateVector(int i, Vector &state) override;
|
||||
|
||||
~AdamsMoultonSolver()
|
||||
{
|
||||
if (RKsolver) { delete RKsolver; }
|
||||
delete [] k;
|
||||
};
|
||||
};
|
||||
|
||||
/** A 0-stage, 1st order AM method. */
|
||||
class AM0Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[1];
|
||||
|
||||
public:
|
||||
AM0Solver() : AdamsMoultonSolver(0, a) { }
|
||||
};
|
||||
|
||||
|
||||
/** A 1-stage, 2nd order AM method. */
|
||||
class AM1Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[2];
|
||||
|
||||
public:
|
||||
AM1Solver() : AdamsMoultonSolver(1, a) { }
|
||||
};
|
||||
|
||||
/** A 2-stage, 3rd order AM method. */
|
||||
class AM2Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[3];
|
||||
|
||||
public:
|
||||
AM2Solver() : AdamsMoultonSolver(2, a) { }
|
||||
};
|
||||
|
||||
/** A 3-stage, 4th order AM method. */
|
||||
class AM3Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[4];
|
||||
|
||||
public:
|
||||
AM3Solver() : AdamsMoultonSolver(3, a) { }
|
||||
};
|
||||
|
||||
/** A 4-stage, 5th order AM method. */
|
||||
class AM4Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[5];
|
||||
|
||||
public:
|
||||
AM4Solver() : AdamsMoultonSolver(4, a) { }
|
||||
};
|
||||
|
||||
|
||||
/// Backward Euler ODE solver. L-stable.
|
||||
class BackwardEulerSolver : public ODESolver
|
||||
{
|
||||
@@ -527,31 +452,183 @@ public:
|
||||
/// Generalized-alpha ODE solver from "A generalized-α method for integrating
|
||||
/// the filtered Navier-Stokes equations with a stabilized finite element
|
||||
/// method" by K.E. Jansen, C.H. Whiting and G.M. Hulbert.
|
||||
class GeneralizedAlphaSolver : public ODESolver
|
||||
class GeneralizedAlphaSolver : public ODESolverWithStates
|
||||
{
|
||||
ODEStateDataVector state;
|
||||
|
||||
protected:
|
||||
mutable Vector xdot,k,y;
|
||||
|
||||
mutable Vector k,y;
|
||||
real_t alpha_f, alpha_m, gamma;
|
||||
int nstate;
|
||||
|
||||
void SetRhoInf(real_t rho_inf);
|
||||
void PrintProperties(std::ostream &out = mfem::out);
|
||||
void PrintProperties(std::ostream &os = mfem::out);
|
||||
public:
|
||||
|
||||
GeneralizedAlphaSolver(real_t rho = 1.0) { SetRhoInf(rho); };
|
||||
|
||||
GeneralizedAlphaSolver(real_t rho = 1.0) : state(1) { SetRhoInf(rho); };
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
int GetMaxStateSize() override { return 1; };
|
||||
int GetStateSize() override { return nstate; };
|
||||
const Vector &GetStateVector(int i) override;
|
||||
void GetStateVector(int i, Vector &state) override;
|
||||
void SetStateVector(int i, Vector &state) override;
|
||||
ODEStateData& GetState() override { return state; }
|
||||
const ODEStateData& GetState() const override { return state; }
|
||||
};
|
||||
|
||||
|
||||
/** An explicit Adams-Bashforth method. */
|
||||
class AdamsBashforthSolver : public ODESolverWithStates
|
||||
{
|
||||
private:
|
||||
const real_t *a;
|
||||
const int stages;
|
||||
real_t dt_;
|
||||
ODEStateDataVector state;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<ODESolver> RKsolver;
|
||||
|
||||
inline bool print()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
return Mpi::IsInitialized() ? Mpi::Root() : true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CheckTimestep(real_t dt);
|
||||
|
||||
public:
|
||||
AdamsBashforthSolver(int s_, const real_t *a_);
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
ODEStateData& GetState() override { return state; }
|
||||
const ODEStateData& GetState() const override { return state; }
|
||||
};
|
||||
|
||||
/** A 1-stage, 1st order AB method. */
|
||||
class AB1Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[1];
|
||||
|
||||
public:
|
||||
AB1Solver() : AdamsBashforthSolver(1, a) { }
|
||||
};
|
||||
|
||||
/** A 2-stage, 2nd order AB method. */
|
||||
class AB2Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[2];
|
||||
|
||||
public:
|
||||
AB2Solver() : AdamsBashforthSolver(2, a) { RKsolver.reset(new RK2Solver()); }
|
||||
};
|
||||
|
||||
/** A 3-stage, 3rd order AB method. */
|
||||
class AB3Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[3];
|
||||
|
||||
public:
|
||||
AB3Solver() : AdamsBashforthSolver(3, a) { RKsolver.reset(new RK3SSPSolver()); }
|
||||
};
|
||||
|
||||
/** A 4-stage, 4th order AB method. */
|
||||
class AB4Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[4];
|
||||
|
||||
public:
|
||||
AB4Solver() : AdamsBashforthSolver(4, a) { RKsolver.reset(new RK4Solver()); }
|
||||
};
|
||||
|
||||
/** A 5-stage, 5th order AB method. */
|
||||
class AB5Solver : public AdamsBashforthSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[5];
|
||||
|
||||
public:
|
||||
AB5Solver() : AdamsBashforthSolver(5, a) { RKsolver.reset(new RK6Solver()); }
|
||||
};
|
||||
|
||||
|
||||
/** An implicit Adams-Moulton method. */
|
||||
class AdamsMoultonSolver : public ODESolverWithStates
|
||||
{
|
||||
private:
|
||||
const real_t *a;
|
||||
const int stages;
|
||||
real_t dt_;
|
||||
ODEStateDataVector state;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<ODESolver> RKsolver;
|
||||
|
||||
inline bool print()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
return Mpi::IsInitialized() ? Mpi::Root() : true;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CheckTimestep(real_t dt);
|
||||
|
||||
public:
|
||||
AdamsMoultonSolver(int s_, const real_t *a_);
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
void Step(Vector &x, real_t &t, real_t &dt) override;
|
||||
|
||||
ODEStateData& GetState() override { return state; }
|
||||
const ODEStateData& GetState() const override { return state; }
|
||||
};
|
||||
|
||||
/** A 1-stage, 2nd order AM method. */
|
||||
class AM1Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[2];
|
||||
|
||||
public:
|
||||
AM1Solver() : AdamsMoultonSolver(1, a) { RKsolver.reset(new SDIRK23Solver()); }
|
||||
};
|
||||
|
||||
/** A 2-stage, 3rd order AM method. */
|
||||
class AM2Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[3];
|
||||
|
||||
public:
|
||||
AM2Solver() : AdamsMoultonSolver(2, a) { RKsolver.reset(new SDIRK23Solver()); }
|
||||
};
|
||||
|
||||
/** A 3-stage, 4th order AM method. */
|
||||
class AM3Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[4];
|
||||
|
||||
public:
|
||||
AM3Solver() : AdamsMoultonSolver(3, a) { RKsolver.reset(new SDIRK23Solver()); }
|
||||
};
|
||||
|
||||
/** A 4-stage, 5th order AM method. */
|
||||
class AM4Solver : public AdamsMoultonSolver
|
||||
{
|
||||
private:
|
||||
static MFEM_EXPORT const real_t a[5];
|
||||
|
||||
public:
|
||||
AM4Solver() : AdamsMoultonSolver(4, a) { RKsolver.reset(new SDIRK34Solver()); }
|
||||
};
|
||||
|
||||
/// The SIASolver class is based on the Symplectic Integration Algorithm
|
||||
/// described in "A Symplectic Integration Algorithm for Separable Hamiltonian
|
||||
/// Functions" by J. Candy and W. Rozmus, Journal of Computational Physics,
|
||||
@@ -630,9 +707,10 @@ protected:
|
||||
/// Pointer to the associated TimeDependentOperator.
|
||||
SecondOrderTimeDependentOperator *f; // f(.,.,t) : R^n x R^n --> R^n
|
||||
MemoryType mem_type;
|
||||
ODEStateDataVector state;
|
||||
|
||||
public:
|
||||
SecondOrderODESolver() : f(NULL) { mem_type = MemoryType::HOST; }
|
||||
SecondOrderODESolver() : f(NULL), state(1) { mem_type = MemoryType::HOST; }
|
||||
|
||||
/// Associate a TimeDependentOperator with the ODE solver.
|
||||
/** This method has to be called:
|
||||
@@ -680,6 +758,8 @@ public:
|
||||
sequence, then the ODE solver must be re-initialized by calling Init()
|
||||
between the two Step() calls. */
|
||||
virtual void Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt) = 0;
|
||||
void EulerStep(Vector &x, Vector &dxdt, real_t &t, real_t &dt);
|
||||
void MidPointStep(Vector &x, Vector &dxdt, real_t &t, real_t &dt);
|
||||
|
||||
/// Perform time integration from time @a t [in] to time @a tf [in].
|
||||
/** @param[in,out] x Approximate solution.
|
||||
@@ -705,22 +785,18 @@ public:
|
||||
while (t < tf) { Step(x, dxdt, t, dt); }
|
||||
}
|
||||
|
||||
/// Function for getting and setting the state vectors
|
||||
virtual int GetMaxStateSize() { return 0; };
|
||||
virtual int GetStateSize() { return 0; }
|
||||
virtual const Vector &GetStateVector(int i)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
Vector *s = NULL; return *s; // Make some compiler happy
|
||||
}
|
||||
virtual void GetStateVector(int i, Vector &state)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
}
|
||||
virtual void SetStateVector(int i, Vector &state)
|
||||
{
|
||||
mfem_error("ODESolver has no state vectors");
|
||||
}
|
||||
/// Functions for getting the state vectors
|
||||
ODEStateData& GetState() { return state; }
|
||||
const ODEStateData& GetState() const { return state; }
|
||||
|
||||
/// Returns how many State vectors the ODE requires
|
||||
int GetStateSize() { return GetState().MaxSize(); };
|
||||
|
||||
/// Help info for SecondOrderODESolver options
|
||||
static MFEM_EXPORT std::string Types;
|
||||
|
||||
/// Function selecting the desired SecondOrderODESolver
|
||||
static MFEM_EXPORT SecondOrderODESolver *Select(const int ode_solver_type);
|
||||
|
||||
virtual ~SecondOrderODESolver() { }
|
||||
};
|
||||
@@ -731,17 +807,18 @@ public:
|
||||
class NewmarkSolver : public SecondOrderODESolver
|
||||
{
|
||||
private:
|
||||
Vector d2xdt2;
|
||||
|
||||
real_t beta, gamma;
|
||||
bool first;
|
||||
bool no_mult;
|
||||
|
||||
public:
|
||||
NewmarkSolver(real_t beta_ = 0.25, real_t gamma_ = 0.5) { beta = beta_; gamma = gamma_; };
|
||||
NewmarkSolver(real_t beta_ = 0.25, real_t gamma_ = 0.5, bool no_mult_ = false)
|
||||
{
|
||||
beta = beta_;
|
||||
gamma = gamma_;
|
||||
no_mult = no_mult_;
|
||||
};
|
||||
|
||||
void PrintProperties(std::ostream &out = mfem::out);
|
||||
|
||||
void Init(SecondOrderTimeDependentOperator &f_) override;
|
||||
void PrintProperties(std::ostream &os = mfem::out);
|
||||
|
||||
void Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt) override;
|
||||
};
|
||||
@@ -773,13 +850,14 @@ public:
|
||||
class GeneralizedAlpha2Solver : public SecondOrderODESolver
|
||||
{
|
||||
protected:
|
||||
Vector xa,va,aa,d2xdt2;
|
||||
Vector xa,va,aa;
|
||||
real_t alpha_f, alpha_m, beta, gamma;
|
||||
int nstate;
|
||||
bool no_mult;
|
||||
|
||||
public:
|
||||
GeneralizedAlpha2Solver(real_t rho_inf = 1.0)
|
||||
GeneralizedAlpha2Solver(real_t rho_inf = 1.0, bool no_mult_ = false)
|
||||
{
|
||||
no_mult = no_mult_;
|
||||
rho_inf = (rho_inf > 1.0) ? 1.0 : rho_inf;
|
||||
rho_inf = (rho_inf < 0.0) ? 0.0 : rho_inf;
|
||||
|
||||
@@ -789,17 +867,12 @@ public:
|
||||
gamma = 0.5 + alpha_m - alpha_f;
|
||||
};
|
||||
|
||||
void PrintProperties(std::ostream &out = mfem::out);
|
||||
void PrintProperties(std::ostream &os = mfem::out);
|
||||
|
||||
void Init(SecondOrderTimeDependentOperator &f_) override;
|
||||
|
||||
void Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt) override;
|
||||
|
||||
int GetMaxStateSize() override { return 1; };
|
||||
int GetStateSize() override { return nstate; };
|
||||
const Vector &GetStateVector(int i) override;
|
||||
void GetStateVector(int i, Vector &state) override;
|
||||
void SetStateVector(int i, Vector &state) override;
|
||||
};
|
||||
|
||||
/// The classical midpoint method.
|
||||
|
||||
+2
-2
@@ -36,10 +36,10 @@
|
||||
#if defined(PETSC_USE_COMPLEX)
|
||||
#error "MFEM does not work with PETSc compiled with complex numbers support"
|
||||
#endif
|
||||
#if defined(PETSC_USE_64BIT_INDICES) && !defined(HYPRE_BIGINT)
|
||||
#if defined(PETSC_USE_64BIT_INDICES) && !defined(HYPRE_BIGINT) && !defined(HYPRE_MIXEDINT)
|
||||
#error "Mismatch between HYPRE (32bit) and PETSc (64bit) integer types"
|
||||
#endif
|
||||
#if !defined(PETSC_USE_64BIT_INDICES) && defined(HYPRE_BIGINT)
|
||||
#if !defined(PETSC_USE_64BIT_INDICES) && (defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT))
|
||||
#error "Mismatch between HYPRE (64bit) and PETSc (32bit) integer types"
|
||||
#endif
|
||||
|
||||
|
||||
+133
-80
@@ -95,7 +95,7 @@ MFEM_DEPRECATED void* CVodeCreate(int lmm, SUNContext)
|
||||
|
||||
/// (DEPRECATED) Wrapper function for backwards compatibility with SUNDIALS
|
||||
/// version < 6
|
||||
MFEM_DEPRECATED void* ARKStepCreate(ARKRhsFn fe, ARKRhsFn fi, realtype t0,
|
||||
MFEM_DEPRECATED void* ARKStepCreate(ARKRhsFn fe, ARKRhsFn fi, sunrealtype t0,
|
||||
N_Vector y0, SUNContext)
|
||||
{
|
||||
return ARKStepCreate(fe, fi, t0, y0);
|
||||
@@ -127,7 +127,7 @@ MFEM_DEPRECATED N_Vector N_VNewEmpty_Parallel(MPI_Comm comm,
|
||||
/// (DEPRECATED) Wrapper function for backwards compatibility with SUNDIALS
|
||||
/// version < 6
|
||||
MFEM_DEPRECATED N_Vector SUN_Hip_OR_Cuda(N_VNewWithMemHelp)(sunindextype length,
|
||||
booleantype use_managed_mem,
|
||||
sunbooleantype use_managed_mem,
|
||||
SUNMemoryHelper helper,
|
||||
SUNContext)
|
||||
{
|
||||
@@ -157,6 +157,16 @@ MFEM_DEPRECATED N_Vector N_VMake_MPIPlusX(MPI_Comm comm, N_Vector local_vector,
|
||||
|
||||
#endif // SUNDIALS_VERSION_MAJOR < 6
|
||||
|
||||
#if MFEM_SUNDIALS_VERSION < 70100
|
||||
#define MFEM_ARKode(FUNC) ARKStep##FUNC
|
||||
#else
|
||||
#define MFEM_ARKode(FUNC) ARKode##FUNC
|
||||
#endif
|
||||
|
||||
// Macro STR(): expand the argument and add double quotes
|
||||
#define STR1(s) #s
|
||||
#define STR(s) STR1(s)
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -187,11 +197,21 @@ SundialsMemHelper &Sundials::GetMemHelper()
|
||||
Sundials::Sundials()
|
||||
{
|
||||
#ifdef MFEM_USE_MPI
|
||||
MPI_Comm communicator = MPI_COMM_WORLD;
|
||||
int mpi_initialized = 0;
|
||||
MPI_Initialized(&mpi_initialized);
|
||||
MPI_Comm communicator = mpi_initialized ? MPI_COMM_WORLD : MPI_COMM_NULL;
|
||||
#if SUNDIALS_VERSION_MAJOR < 7
|
||||
int return_val = SUNContext_Create((void*) &communicator, &context);
|
||||
#else
|
||||
int return_val = SUNContext_Create(nullptr, &context);
|
||||
int return_val = SUNContext_Create(communicator, &context);
|
||||
#endif
|
||||
#else // #ifdef MFEM_USE_MPI
|
||||
#if SUNDIALS_VERSION_MAJOR < 7
|
||||
int return_val = SUNContext_Create(nullptr, &context);
|
||||
#else
|
||||
int return_val = SUNContext_Create((SUNComm)(0), &context);
|
||||
#endif
|
||||
#endif // #ifdef MFEM_USE_MPI
|
||||
MFEM_VERIFY(return_val == 0, "Call to SUNContext_Create failed");
|
||||
SundialsMemHelper actual_helper(context);
|
||||
memHelper = std::move(actual_helper);
|
||||
@@ -250,7 +270,11 @@ int SundialsMemHelper::SundialsMemHelper_Alloc(SUNMemoryHelper helper,
|
||||
#endif
|
||||
)
|
||||
{
|
||||
#if (SUNDIALS_VERSION_MAJOR < 7)
|
||||
SUNMemory sunmem = SUNMemoryNewEmpty();
|
||||
#else
|
||||
SUNMemory sunmem = SUNMemoryNewEmpty(helper->sunctx);
|
||||
#endif
|
||||
|
||||
sunmem->ptr = NULL;
|
||||
sunmem->own = SUNTRUE;
|
||||
@@ -631,7 +655,7 @@ static int LSFree(SUNLinearSolver LS)
|
||||
// ---------------------------------------------------------------------------
|
||||
// CVODE interface
|
||||
// ---------------------------------------------------------------------------
|
||||
int CVODESolver::RHS(realtype t, const N_Vector y, N_Vector ydot,
|
||||
int CVODESolver::RHS(sunrealtype t, const N_Vector y, N_Vector ydot,
|
||||
void *user_data)
|
||||
{
|
||||
// At this point the up-to-date data for N_Vector y and ydot is on the device.
|
||||
@@ -648,7 +672,8 @@ int CVODESolver::RHS(realtype t, const N_Vector y, N_Vector ydot,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int CVODESolver::root(realtype t, N_Vector y, realtype *gout, void *user_data)
|
||||
int CVODESolver::root(sunrealtype t, N_Vector y, sunrealtype *gout,
|
||||
void *user_data)
|
||||
{
|
||||
CVODESolver *self = static_cast<CVODESolver*>(user_data);
|
||||
|
||||
@@ -668,8 +693,9 @@ void CVODESolver::SetRootFinder(int components, RootFunction func)
|
||||
MFEM_VERIFY(flag == CV_SUCCESS, "error in SetRootFinder()");
|
||||
}
|
||||
|
||||
int CVODESolver::LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
booleantype jok, booleantype *jcur, realtype gamma,
|
||||
int CVODESolver::LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy,
|
||||
SUNMatrix A, sunbooleantype jok,
|
||||
sunbooleantype *jcur, sunrealtype gamma,
|
||||
void*, N_Vector, N_Vector, N_Vector)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
@@ -683,7 +709,7 @@ int CVODESolver::LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
}
|
||||
|
||||
int CVODESolver::LinSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector x,
|
||||
N_Vector b, realtype tol)
|
||||
N_Vector b, sunrealtype tol)
|
||||
{
|
||||
SundialsNVector mfem_x(x);
|
||||
const SundialsNVector mfem_b(b);
|
||||
@@ -859,7 +885,7 @@ void CVODESolver::UseSundialsLinearSolver()
|
||||
if (LSA != NULL) { SUNLinSolFree(LSA); LSA = NULL; }
|
||||
|
||||
// Create linear solver
|
||||
LSA = SUNLinSol_SPGMR(*Y, PREC_NONE, 0, Sundials::GetContext());
|
||||
LSA = SUNLinSol_SPGMR(*Y, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSA, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
// Attach linear solver
|
||||
@@ -1150,7 +1176,7 @@ void CVODESSolver::UseSundialsLinearSolverB()
|
||||
if (LSB != NULL) { SUNLinSolFree(LSB); LSB = NULL; }
|
||||
|
||||
// Set default linear solver (Newton is the default Nonlinear Solver)
|
||||
LSB = SUNLinSol_SPGMR(*yB, PREC_NONE, 0, Sundials::GetContext());
|
||||
LSB = SUNLinSol_SPGMR(*yB, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSB, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
/* Attach the matrix and linear solver */
|
||||
@@ -1158,11 +1184,11 @@ void CVODESSolver::UseSundialsLinearSolverB()
|
||||
MFEM_VERIFY(flag == CV_SUCCESS, "error in CVodeSetLinearSolverB()");
|
||||
}
|
||||
|
||||
int CVODESSolver::LinSysSetupB(realtype t, N_Vector y, N_Vector yB,
|
||||
int CVODESSolver::LinSysSetupB(sunrealtype t, N_Vector y, N_Vector yB,
|
||||
N_Vector fyB, SUNMatrix AB,
|
||||
booleantype jokB, booleantype *jcurB,
|
||||
realtype gammaB, void *user_data, N_Vector tmp1,
|
||||
N_Vector tmp2, N_Vector tmp3)
|
||||
sunbooleantype jokB, sunbooleantype *jcurB,
|
||||
sunrealtype gammaB, void *user_data,
|
||||
N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
const SundialsNVector mfem_y(y);
|
||||
@@ -1178,7 +1204,7 @@ int CVODESSolver::LinSysSetupB(realtype t, N_Vector y, N_Vector yB,
|
||||
}
|
||||
|
||||
int CVODESSolver::LinSysSolveB(SUNLinearSolver LS, SUNMatrix AB, N_Vector yB,
|
||||
N_Vector Rb, realtype tol)
|
||||
N_Vector Rb, sunrealtype tol)
|
||||
{
|
||||
SundialsNVector mfem_yB(yB);
|
||||
const SundialsNVector mfem_Rb(Rb);
|
||||
@@ -1216,7 +1242,7 @@ void CVODESSolver::SetWFTolerances(EWTFunction func)
|
||||
|
||||
// CVODESSolver static functions
|
||||
|
||||
int CVODESSolver::RHSQ(realtype t, const N_Vector y, N_Vector qdot,
|
||||
int CVODESSolver::RHSQ(sunrealtype t, const N_Vector y, N_Vector qdot,
|
||||
void *user_data)
|
||||
{
|
||||
CVODESSolver *self = static_cast<CVODESSolver*>(user_data);
|
||||
@@ -1229,7 +1255,7 @@ int CVODESSolver::RHSQ(realtype t, const N_Vector y, N_Vector qdot,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CVODESSolver::RHSQB(realtype t, N_Vector y, N_Vector yB, N_Vector qBdot,
|
||||
int CVODESSolver::RHSQB(sunrealtype t, N_Vector y, N_Vector yB, N_Vector qBdot,
|
||||
void *user_dataB)
|
||||
{
|
||||
CVODESSolver *self = static_cast<CVODESSolver*>(user_dataB);
|
||||
@@ -1243,7 +1269,7 @@ int CVODESSolver::RHSQB(realtype t, N_Vector y, N_Vector yB, N_Vector qBdot,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CVODESSolver::RHSB(realtype t, N_Vector y, N_Vector yB, N_Vector yBdot,
|
||||
int CVODESSolver::RHSB(sunrealtype t, N_Vector y, N_Vector yB, N_Vector yBdot,
|
||||
void *user_dataB)
|
||||
{
|
||||
CVODESSolver *self = static_cast<CVODESSolver*>(user_dataB);
|
||||
@@ -1341,7 +1367,7 @@ CVODESSolver::~CVODESSolver()
|
||||
// ARKStep interface
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
int ARKStepSolver::RHS1(realtype t, const N_Vector y, N_Vector result,
|
||||
int ARKStepSolver::RHS1(sunrealtype t, const N_Vector y, N_Vector result,
|
||||
void *user_data)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
@@ -1373,7 +1399,7 @@ int ARKStepSolver::RHS1(realtype t, const N_Vector y, N_Vector result,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int ARKStepSolver::RHS2(realtype t, const N_Vector y, N_Vector result,
|
||||
int ARKStepSolver::RHS2(sunrealtype t, const N_Vector y, N_Vector result,
|
||||
void *user_data)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
@@ -1399,9 +1425,9 @@ int ARKStepSolver::RHS2(realtype t, const N_Vector y, N_Vector result,
|
||||
return (0);
|
||||
}
|
||||
|
||||
int ARKStepSolver::LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
SUNMatrix, booleantype jok, booleantype *jcur,
|
||||
realtype gamma,
|
||||
int ARKStepSolver::LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy,
|
||||
SUNMatrix A, SUNMatrix, sunbooleantype jok,
|
||||
sunbooleantype *jcur, sunrealtype gamma,
|
||||
void*, N_Vector, N_Vector, N_Vector)
|
||||
{
|
||||
// Get data from N_Vectors
|
||||
@@ -1419,7 +1445,7 @@ int ARKStepSolver::LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
}
|
||||
|
||||
int ARKStepSolver::LinSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector x,
|
||||
N_Vector b, realtype tol)
|
||||
N_Vector b, sunrealtype tol)
|
||||
{
|
||||
SundialsNVector mfem_x(x);
|
||||
const SundialsNVector mfem_b(b);
|
||||
@@ -1433,7 +1459,7 @@ int ARKStepSolver::LinSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector x,
|
||||
return (self->f->SUNImplicitSolve(mfem_b, mfem_x, tol));
|
||||
}
|
||||
|
||||
int ARKStepSolver::MassSysSetup(realtype t, SUNMatrix M,
|
||||
int ARKStepSolver::MassSysSetup(sunrealtype t, SUNMatrix M,
|
||||
void*, N_Vector, N_Vector, N_Vector)
|
||||
{
|
||||
ARKStepSolver *self = static_cast<ARKStepSolver*>(GET_CONTENT(M));
|
||||
@@ -1444,7 +1470,7 @@ int ARKStepSolver::MassSysSetup(realtype t, SUNMatrix M,
|
||||
}
|
||||
|
||||
int ARKStepSolver::MassSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector x,
|
||||
N_Vector b, realtype tol)
|
||||
N_Vector b, sunrealtype tol)
|
||||
{
|
||||
SundialsNVector mfem_x(x);
|
||||
const SundialsNVector mfem_b(b);
|
||||
@@ -1464,7 +1490,7 @@ int ARKStepSolver::MassMult1(SUNMatrix M, N_Vector x, N_Vector v)
|
||||
return (self->f->SUNMassMult(mfem_x, mfem_v));
|
||||
}
|
||||
|
||||
int ARKStepSolver::MassMult2(N_Vector x, N_Vector v, realtype t,
|
||||
int ARKStepSolver::MassMult2(N_Vector x, N_Vector v, sunrealtype t,
|
||||
void* mtimes_data)
|
||||
{
|
||||
const SundialsNVector mfem_x(x);
|
||||
@@ -1535,7 +1561,7 @@ void ARKStepSolver::Init(TimeDependentOperator &f_)
|
||||
// Free existing solver memory and re-create with new vector size
|
||||
if (resize)
|
||||
{
|
||||
ARKStepFree(&sundials_mem);
|
||||
MFEM_ARKode(Free)(&sundials_mem);
|
||||
sundials_mem = NULL;
|
||||
}
|
||||
}
|
||||
@@ -1573,12 +1599,15 @@ void ARKStepSolver::Init(TimeDependentOperator &f_)
|
||||
MFEM_VERIFY(sundials_mem, "error in ARKStepCreate()");
|
||||
|
||||
// Attach the ARKStepSolver as user-defined data
|
||||
flag = ARKStepSetUserData(sundials_mem, this);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetUserData()");
|
||||
flag = MFEM_ARKode(SetUserData)(sundials_mem, this);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetUserData)) "()");
|
||||
|
||||
// Set default tolerances
|
||||
flag = ARKStepSStolerances(sundials_mem, default_rel_tol, default_abs_tol);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetSStolerances()");
|
||||
flag = MFEM_ARKode(SStolerances)(sundials_mem, default_rel_tol,
|
||||
default_abs_tol);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SStolerances)) "()");
|
||||
|
||||
// If implicit, attach MFEM linear solver by default
|
||||
if (use_implicit) { UseMFEMLinearSolver(); }
|
||||
@@ -1617,15 +1646,16 @@ void ARKStepSolver::Step(Vector &x, real_t &t, real_t &dt)
|
||||
|
||||
// Integrate the system
|
||||
double tout = t + dt;
|
||||
flag = ARKStepEvolve(sundials_mem, tout, *Y, &t, step_mode);
|
||||
MFEM_VERIFY(flag >= 0, "error in ARKStepEvolve()");
|
||||
flag = MFEM_ARKode(Evolve)(sundials_mem, tout, *Y, &t, step_mode);
|
||||
MFEM_VERIFY(flag >= 0, "error in " STR(MFEM_ARKode(Evolve)) "()");
|
||||
|
||||
// Make sure host is up to date
|
||||
Y->HostRead();
|
||||
|
||||
// Return the last incremental step size
|
||||
flag = ARKStepGetLastStep(sundials_mem, &dt);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepGetLastStep()");
|
||||
flag = MFEM_ARKode(GetLastStep)(sundials_mem, &dt);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(GetLastStep)) "()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::UseMFEMLinearSolver()
|
||||
@@ -1651,12 +1681,14 @@ void ARKStepSolver::UseMFEMLinearSolver()
|
||||
A->ops->destroy = MatDestroy;
|
||||
|
||||
// Attach the linear solver and matrix
|
||||
flag = ARKStepSetLinearSolver(sundials_mem, LSA, A);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetLinearSolver()");
|
||||
flag = MFEM_ARKode(SetLinearSolver)(sundials_mem, LSA, A);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetLinearSolver)) "()");
|
||||
|
||||
// Set the linear system evaluation function
|
||||
flag = ARKStepSetLinSysFn(sundials_mem, ARKStepSolver::LinSysSetup);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetLinSysFn()");
|
||||
flag = MFEM_ARKode(SetLinSysFn)(sundials_mem, ARKStepSolver::LinSysSetup);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetLinSysFn)) "()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::UseSundialsLinearSolver()
|
||||
@@ -1666,12 +1698,13 @@ void ARKStepSolver::UseSundialsLinearSolver()
|
||||
if (LSA != NULL) { SUNLinSolFree(LSA); LSA = NULL; }
|
||||
|
||||
// Create linear solver
|
||||
LSA = SUNLinSol_SPGMR(*Y, PREC_NONE, 0, Sundials::GetContext());
|
||||
LSA = SUNLinSol_SPGMR(*Y, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSA, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
// Attach linear solver
|
||||
flag = ARKStepSetLinearSolver(sundials_mem, LSA, NULL);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetLinearSolver()");
|
||||
flag = MFEM_ARKode(SetLinearSolver)(sundials_mem, LSA, NULL);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetLinearSolver)) "()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::UseMFEMMassLinearSolver(int tdep)
|
||||
@@ -1698,12 +1731,14 @@ void ARKStepSolver::UseMFEMMassLinearSolver(int tdep)
|
||||
M->ops->destroy = MatDestroy;
|
||||
|
||||
// Attach the linear solver and matrix
|
||||
flag = ARKStepSetMassLinearSolver(sundials_mem, LSM, M, tdep);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetLinearSolver()");
|
||||
flag = MFEM_ARKode(SetMassLinearSolver)(sundials_mem, LSM, M, tdep);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMassLinearSolver)) "()");
|
||||
|
||||
// Set the linear system function
|
||||
flag = ARKStepSetMassFn(sundials_mem, ARKStepSolver::MassSysSetup);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetMassFn()");
|
||||
flag = MFEM_ARKode(SetMassFn)(sundials_mem, ARKStepSolver::MassSysSetup);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMassFn)) "()");
|
||||
|
||||
// Check that the ODE is not expressed in EXPLICIT form
|
||||
MFEM_VERIFY(!f->isExplicit(), "ODE operator is expressed in EXPLICIT form")
|
||||
@@ -1716,17 +1751,19 @@ void ARKStepSolver::UseSundialsMassLinearSolver(int tdep)
|
||||
if (LSM != NULL) { SUNLinSolFree(LSM); LSM = NULL; }
|
||||
|
||||
// Create linear solver
|
||||
LSM = SUNLinSol_SPGMR(*Y, PREC_NONE, 0, Sundials::GetContext());
|
||||
LSM = SUNLinSol_SPGMR(*Y, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSM, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
// Attach linear solver
|
||||
flag = ARKStepSetMassLinearSolver(sundials_mem, LSM, NULL, tdep);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetMassLinearSolver()");
|
||||
flag = MFEM_ARKode(SetMassLinearSolver)(sundials_mem, LSM, NULL, tdep);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMassLinearSolver)) "()");
|
||||
|
||||
// Attach matrix multiplication function
|
||||
flag = ARKStepSetMassTimes(sundials_mem, NULL, ARKStepSolver::MassMult2,
|
||||
this);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetMassTimes()");
|
||||
flag = MFEM_ARKode(SetMassTimes)(sundials_mem, NULL,
|
||||
ARKStepSolver::MassMult2, this);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMassTimes)) "()");
|
||||
|
||||
// Check that the ODE is not expressed in EXPLICIT form
|
||||
MFEM_VERIFY(!f->isExplicit(), "ODE operator is expressed in EXPLICIT form")
|
||||
@@ -1739,20 +1776,23 @@ void ARKStepSolver::SetStepMode(int itask)
|
||||
|
||||
void ARKStepSolver::SetSStolerances(double reltol, double abstol)
|
||||
{
|
||||
flag = ARKStepSStolerances(sundials_mem, reltol, abstol);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSStolerances()");
|
||||
flag = MFEM_ARKode(SStolerances)(sundials_mem, reltol, abstol);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SStolerances)) "()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::SetMaxStep(double dt_max)
|
||||
{
|
||||
flag = ARKStepSetMaxStep(sundials_mem, dt_max);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetMaxStep()");
|
||||
flag = MFEM_ARKode(SetMaxStep)(sundials_mem, dt_max);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetMaxStep)) "()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::SetOrder(int order)
|
||||
{
|
||||
flag = ARKStepSetOrder(sundials_mem, order);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetOrder()");
|
||||
flag = MFEM_ARKode(SetOrder)(sundials_mem, order);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetOrder)) "()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::SetERKTableNum(ARKODE_ERKTableID table_id)
|
||||
@@ -1776,8 +1816,9 @@ void ARKStepSolver::SetIMEXTableNum(ARKODE_ERKTableID etable_id,
|
||||
|
||||
void ARKStepSolver::SetFixedStep(double dt)
|
||||
{
|
||||
flag = ARKStepSetFixedStep(sundials_mem, dt);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepSetFixedStep()");
|
||||
flag = MFEM_ARKode(SetFixedStep)(sundials_mem, dt);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(SetFixedStep)) "()");
|
||||
}
|
||||
|
||||
void ARKStepSolver::PrintInfo() const
|
||||
@@ -1799,18 +1840,19 @@ void ARKStepSolver::PrintInfo() const
|
||||
&netfails);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepGetTimestepperStats()");
|
||||
|
||||
flag = ARKStepGetStepStats(sundials_mem,
|
||||
&nsteps,
|
||||
&hinused,
|
||||
&hlast,
|
||||
&hcur,
|
||||
&tcur);
|
||||
flag = MFEM_ARKode(GetStepStats)(sundials_mem,
|
||||
&nsteps,
|
||||
&hinused,
|
||||
&hlast,
|
||||
&hcur,
|
||||
&tcur);
|
||||
|
||||
// Get nonlinear solver stats
|
||||
flag = ARKStepGetNonlinSolvStats(sundials_mem,
|
||||
&nniters,
|
||||
&nncfails);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS, "error in ARKStepGetNonlinSolvStats()");
|
||||
flag = MFEM_ARKode(GetNonlinSolvStats)(sundials_mem,
|
||||
&nniters,
|
||||
&nncfails);
|
||||
MFEM_VERIFY(flag == ARK_SUCCESS,
|
||||
"error in " STR(MFEM_ARKode(GetNonlinSolvStats)) "()");
|
||||
|
||||
mfem::out <<
|
||||
"ARKStep:\n"
|
||||
@@ -1838,7 +1880,7 @@ ARKStepSolver::~ARKStepSolver()
|
||||
SUNMatDestroy(A);
|
||||
SUNLinSolFree(LSA);
|
||||
SUNNonlinSolFree(NLS);
|
||||
ARKStepFree(&sundials_mem);
|
||||
MFEM_ARKode(Free)(&sundials_mem);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1861,7 +1903,7 @@ int KINSolver::Mult(const N_Vector u, N_Vector fu, void *user_data)
|
||||
|
||||
// Wrapper for computing Jacobian-vector products
|
||||
int KINSolver::GradientMult(N_Vector v, N_Vector Jv, N_Vector u,
|
||||
booleantype *new_u, void *user_data)
|
||||
sunbooleantype *new_u, void *user_data)
|
||||
{
|
||||
const SundialsNVector mfem_v(v);
|
||||
SundialsNVector mfem_Jv(Jv);
|
||||
@@ -1901,7 +1943,7 @@ int KINSolver::LinSysSetup(N_Vector u, N_Vector, SUNMatrix J,
|
||||
|
||||
// Wrapper for solving linear systems J u = b
|
||||
int KINSolver::LinSysSolve(SUNLinearSolver LS, SUNMatrix, N_Vector u,
|
||||
N_Vector b, realtype)
|
||||
N_Vector b, sunrealtype)
|
||||
{
|
||||
SundialsNVector mfem_u(u), mfem_b(b);
|
||||
KINSolver *self = static_cast<KINSolver*>(GET_CONTENT(LS));
|
||||
@@ -1960,7 +2002,11 @@ KINSolver::KINSolver(int strategy, bool oper_grad)
|
||||
f_scale = new SundialsNVector();
|
||||
|
||||
// Default abs_tol and print_level
|
||||
#if MFEM_SUNDIALS_VERSION < 70000
|
||||
abs_tol = pow(UNIT_ROUNDOFF, 1.0/3.0);
|
||||
#else
|
||||
abs_tol = pow(SUN_UNIT_ROUNDOFF, 1.0/3.0);
|
||||
#endif
|
||||
print_level = 0;
|
||||
}
|
||||
|
||||
@@ -1974,7 +2020,11 @@ KINSolver::KINSolver(MPI_Comm comm, int strategy, bool oper_grad)
|
||||
f_scale = new SundialsNVector(comm);
|
||||
|
||||
// Default abs_tol and print_level
|
||||
#if MFEM_SUNDIALS_VERSION < 70000
|
||||
abs_tol = pow(UNIT_ROUNDOFF, 1.0/3.0);
|
||||
#else
|
||||
abs_tol = pow(SUN_UNIT_ROUNDOFF, 1.0/3.0);
|
||||
#endif
|
||||
print_level = 0;
|
||||
}
|
||||
#endif
|
||||
@@ -2086,7 +2136,7 @@ void KINSolver::SetOperator(const Operator &op)
|
||||
if (A != NULL) { SUNMatDestroy(A); A = NULL; }
|
||||
if (LSA != NULL) { SUNLinSolFree(LSA); LSA = NULL; }
|
||||
|
||||
LSA = SUNLinSol_SPGMR(*Y, PREC_NONE, 0, Sundials::GetContext());
|
||||
LSA = SUNLinSol_SPGMR(*Y, SUN_PREC_NONE, 0, Sundials::GetContext());
|
||||
MFEM_VERIFY(LSA, "error in SUNLinSol_SPGMR()");
|
||||
|
||||
flag = KINSetLinearSolver(sundials_mem, LSA, NULL);
|
||||
@@ -2155,12 +2205,12 @@ void KINSolver::SetJFNKSolver(Solver &solver)
|
||||
if (LSA != NULL) { SUNLinSolFree(LSA); LSA = NULL; }
|
||||
|
||||
// Setup FGMRES
|
||||
LSA = SUNLinSol_SPFGMR(*Y, prec ? PREC_RIGHT : PREC_NONE, maxli,
|
||||
LSA = SUNLinSol_SPFGMR(*Y, prec ? SUN_PREC_RIGHT : SUN_PREC_NONE, maxli,
|
||||
Sundials::GetContext());
|
||||
MFEM_VERIFY(LSA, "error in SUNLinSol_SPFGMR()");
|
||||
|
||||
flag = SUNLinSol_SPFGMRSetMaxRestarts(LSA, maxlrs);
|
||||
MFEM_VERIFY(flag == SUNLS_SUCCESS, "error in SUNLinSol_SPFGMR()");
|
||||
MFEM_VERIFY(flag == SUN_SUCCESS, "error in SUNLinSol_SPFGMR()");
|
||||
|
||||
flag = KINSetLinearSolver(sundials_mem, LSA, NULL);
|
||||
MFEM_VERIFY(flag == KIN_SUCCESS, "error in KINSetLinearSolver()");
|
||||
@@ -2317,18 +2367,21 @@ void KINSolver::Mult(Vector &x,
|
||||
|
||||
if (rank == 0)
|
||||
{
|
||||
#if MFEM_SUNDIALS_VERSION < 70000
|
||||
flag = KINSetPrintLevel(sundials_mem, print_level);
|
||||
MFEM_VERIFY(flag == KIN_SUCCESS, "KINSetPrintLevel() failed!");
|
||||
#endif
|
||||
// NOTE: there is no KINSetPrintLevel in SUNDIALS v7!
|
||||
|
||||
#ifdef SUNDIALS_BUILD_WITH_MONITORING
|
||||
if (jfnk && print_level)
|
||||
{
|
||||
flag = SUNLinSolSetInfoFile_SPFGMR(LSA, stdout);
|
||||
MFEM_VERIFY(flag == SUNLS_SUCCESS,
|
||||
MFEM_VERIFY(flag == SUN_SUCCESS,
|
||||
"error in SUNLinSolSetInfoFile_SPFGMR()");
|
||||
|
||||
flag = SUNLinSolSetPrintLevel_SPFGMR(LSA, 1);
|
||||
MFEM_VERIFY(flag == SUNLS_SUCCESS,
|
||||
MFEM_VERIFY(flag == SUN_SUCCESS,
|
||||
"error in SUNLinSolSetPrintLevel_SPFGMR()");
|
||||
}
|
||||
#endif
|
||||
|
||||
+65
-31
@@ -54,6 +54,10 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#define MFEM_SUNDIALS_VERSION \
|
||||
(SUNDIALS_VERSION_MAJOR*10000 + SUNDIALS_VERSION_MINOR*100 + \
|
||||
SUNDIALS_VERSION_PATCH)
|
||||
|
||||
#if (SUNDIALS_VERSION_MAJOR < 6)
|
||||
|
||||
/// (DEPRECATED) Map SUNDIALS version >= 6 datatypes and constants to
|
||||
@@ -68,13 +72,30 @@ constexpr ARKODE_ERKTableID ARKODE_FEHLBERG_13_7_8 = FEHLBERG_13_7_8;
|
||||
/// arbitrary type for more compact backwards compatibility
|
||||
using SUNContext = void*;
|
||||
|
||||
/// 'sunrealtype' was first introduced in v6.0.0
|
||||
typedef realtype sunrealtype;
|
||||
/// 'sunbooleantype' was first introduced in v6.0.0
|
||||
typedef booleantype sunbooleantype;
|
||||
|
||||
/// New constant names introduced in v6.0.0
|
||||
enum { SUN_PREC_NONE, SUN_PREC_LEFT, SUN_PREC_RIGHT, SUN_PREC_BOTH };
|
||||
|
||||
// KIN_ORTH_MGS was introduced in SUNDIALS v6; here, we define it just so that
|
||||
// it can be used as the default option in the second parameter of
|
||||
// KINSolver::EnableAndersonAcc -- the actual value of the parameter will be
|
||||
// ignored when using SUNDIALS < v6.
|
||||
#define KIN_ORTH_MGS 0
|
||||
|
||||
#endif // SUNDIALS_VERSION_MAJOR < 6
|
||||
#endif // #if SUNDIALS_VERSION_MAJOR < 6
|
||||
|
||||
#if (SUNDIALS_VERSION_MAJOR < 7)
|
||||
|
||||
/** @brief The enum constant SUN_SUCCESS was added in v7 as a replacement of
|
||||
various *_SUCCESS macros that were removed in v7. */
|
||||
enum { SUN_SUCCESS = 0 };
|
||||
|
||||
#endif // #if SUNDIALS_VERSION_MAJOR < 7
|
||||
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -244,7 +265,14 @@ public:
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Returns the MPI communicator for the internal N_Vector x.
|
||||
inline MPI_Comm GetComm() const { return *static_cast<MPI_Comm*>(N_VGetCommunicator(x)); }
|
||||
inline MPI_Comm GetComm() const
|
||||
{
|
||||
#if SUNDIALS_VERSION_MAJOR < 7
|
||||
return *static_cast<MPI_Comm*>(N_VGetCommunicator(x));
|
||||
#else
|
||||
return N_VGetCommunicator(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Returns the MPI global length for the internal N_Vector x.
|
||||
inline long GlobalSize() const { return N_VGetLength(x); }
|
||||
@@ -396,24 +424,26 @@ protected:
|
||||
int root_components; /// Number of components in gout
|
||||
|
||||
/// Wrapper to compute the ODE rhs function.
|
||||
static int RHS(realtype t, const N_Vector y, N_Vector ydot, void *user_data);
|
||||
static int RHS(sunrealtype t, const N_Vector y, N_Vector ydot,
|
||||
void *user_data);
|
||||
|
||||
/// Setup the linear system $ A x = b $.
|
||||
static int LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
booleantype jok, booleantype *jcur,
|
||||
realtype gamma, void *user_data, N_Vector tmp1,
|
||||
static int LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
sunbooleantype jok, sunbooleantype *jcur,
|
||||
sunrealtype gamma, void *user_data, N_Vector tmp1,
|
||||
N_Vector tmp2, N_Vector tmp3);
|
||||
|
||||
/// Solve the linear system $ A x = b $.
|
||||
static int LinSysSolve(SUNLinearSolver LS, SUNMatrix A, N_Vector x,
|
||||
N_Vector b, realtype tol);
|
||||
N_Vector b, sunrealtype tol);
|
||||
|
||||
/// Prototype to define root finding for CVODE
|
||||
static int root(realtype t, N_Vector y, realtype *gout, void *user_data);
|
||||
static int root(sunrealtype t, N_Vector y, sunrealtype *gout,
|
||||
void *user_data);
|
||||
|
||||
/// Typedef for root finding functions
|
||||
typedef std::function<int(realtype t, Vector y, Vector gout, CVODESolver *)>
|
||||
RootFunction;
|
||||
typedef std::function<int(sunrealtype t, Vector y, Vector gout,
|
||||
CVODESolver *)> RootFunction;
|
||||
|
||||
/// A class member to facilitate pointing to a user-specified root function
|
||||
RootFunction root_func;
|
||||
@@ -421,7 +451,8 @@ protected:
|
||||
/// Typedef declaration for error weight functions
|
||||
typedef std::function<int(Vector y, Vector w, CVODESolver*)> EWTFunction;
|
||||
|
||||
/// A class member to facilitate pointing to a user-specified error weight function
|
||||
/** @brief A class member to facilitate pointing to a user-specified error
|
||||
weight function */
|
||||
EWTFunction ewt_func;
|
||||
|
||||
public:
|
||||
@@ -455,7 +486,7 @@ public:
|
||||
@note If this method is called a second time with a different problem
|
||||
size, then any non-default user-set options will be lost and will need
|
||||
to be set again. */
|
||||
void Init(TimeDependentOperator &f_);
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
/// Integrate the ODE with CVODE using the specified step mode.
|
||||
/** @param[in,out] x On output, the solution vector at the requested output
|
||||
@@ -531,14 +562,15 @@ protected:
|
||||
int indexB; ///< backward problem index
|
||||
|
||||
/// Wrapper to compute the ODE RHS Quadrature function.
|
||||
static int RHSQ(realtype t, const N_Vector y, N_Vector qdot, void *user_data);
|
||||
static int RHSQ(sunrealtype t, const N_Vector y, N_Vector qdot,
|
||||
void *user_data);
|
||||
|
||||
/// Wrapper to compute the ODE RHS backward function.
|
||||
static int RHSB(realtype t, N_Vector y,
|
||||
static int RHSB(sunrealtype t, N_Vector y,
|
||||
N_Vector yB, N_Vector yBdot, void *user_dataB);
|
||||
|
||||
/// Wrapper to compute the ODE RHS Backwards Quadrature function.
|
||||
static int RHSQB(realtype t, N_Vector y, N_Vector yB,
|
||||
static int RHSQB(sunrealtype t, N_Vector y, N_Vector yB,
|
||||
N_Vector qBdot, void *user_dataB);
|
||||
|
||||
/// Error control function
|
||||
@@ -654,15 +686,15 @@ public:
|
||||
void SetSVtolerancesB(double reltol, Vector abstol);
|
||||
|
||||
/// Setup the linear system A x = b
|
||||
static int LinSysSetupB(realtype t, N_Vector y, N_Vector yB, N_Vector fyB,
|
||||
static int LinSysSetupB(sunrealtype t, N_Vector y, N_Vector yB, N_Vector fyB,
|
||||
SUNMatrix A,
|
||||
booleantype jok, booleantype *jcur,
|
||||
realtype gamma, void *user_data, N_Vector tmp1,
|
||||
sunbooleantype jok, sunbooleantype *jcur,
|
||||
sunrealtype gamma, void *user_data, N_Vector tmp1,
|
||||
N_Vector tmp2, N_Vector tmp3);
|
||||
|
||||
/// Solve the linear system A x = b
|
||||
static int LinSysSolveB(SUNLinearSolver LS, SUNMatrix A, N_Vector x,
|
||||
N_Vector b, realtype tol);
|
||||
N_Vector b, sunrealtype tol);
|
||||
|
||||
|
||||
/// Destroy the associated CVODES memory and SUNDIALS objects.
|
||||
@@ -695,33 +727,35 @@ protected:
|
||||
RHS1 is explicit RHS and RHS2 the implicit RHS for IMEX integration. When
|
||||
purely implicit or explicit only RHS1 is used. */
|
||||
///@{
|
||||
static int RHS1(realtype t, const N_Vector y, N_Vector ydot, void *user_data);
|
||||
static int RHS2(realtype t, const N_Vector y, N_Vector ydot, void *user_data);
|
||||
static int RHS1(sunrealtype t, const N_Vector y, N_Vector ydot,
|
||||
void *user_data);
|
||||
static int RHS2(sunrealtype t, const N_Vector y, N_Vector ydot,
|
||||
void *user_data);
|
||||
///@}
|
||||
|
||||
/// Setup the linear system $ A x = b $.
|
||||
static int LinSysSetup(realtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
SUNMatrix M, booleantype jok, booleantype *jcur,
|
||||
realtype gamma, void *user_data, N_Vector tmp1,
|
||||
static int LinSysSetup(sunrealtype t, N_Vector y, N_Vector fy, SUNMatrix A,
|
||||
SUNMatrix M, sunbooleantype jok, sunbooleantype *jcur,
|
||||
sunrealtype gamma, void *user_data, N_Vector tmp1,
|
||||
N_Vector tmp2, N_Vector tmp3);
|
||||
|
||||
/// Solve the linear system $ A x = b $.
|
||||
static int LinSysSolve(SUNLinearSolver LS, SUNMatrix A, N_Vector x,
|
||||
N_Vector b, realtype tol);
|
||||
N_Vector b, sunrealtype tol);
|
||||
|
||||
/// Setup the linear system $ M x = b $.
|
||||
static int MassSysSetup(realtype t, SUNMatrix M, void *user_data,
|
||||
static int MassSysSetup(sunrealtype t, SUNMatrix M, void *user_data,
|
||||
N_Vector tmp1, N_Vector tmp2, N_Vector tmp3);
|
||||
|
||||
/// Solve the linear system $ M x = b $.
|
||||
static int MassSysSolve(SUNLinearSolver LS, SUNMatrix M, N_Vector x,
|
||||
N_Vector b, realtype tol);
|
||||
N_Vector b, sunrealtype tol);
|
||||
|
||||
/// Compute the matrix-vector product $ v = M x $.
|
||||
static int MassMult1(SUNMatrix M, N_Vector x, N_Vector v);
|
||||
|
||||
/// Compute the matrix-vector product $v = M_t x $ at time t.
|
||||
static int MassMult2(N_Vector x, N_Vector v, realtype t,
|
||||
static int MassMult2(N_Vector x, N_Vector v, sunrealtype t,
|
||||
void* mtimes_data);
|
||||
|
||||
public:
|
||||
@@ -757,7 +791,7 @@ public:
|
||||
@note If this method is called a second time with a different problem
|
||||
size, then any non-default user-set options will be lost and will need
|
||||
to be set again. */
|
||||
void Init(TimeDependentOperator &f_);
|
||||
void Init(TimeDependentOperator &f_) override;
|
||||
|
||||
/// Integrate the ODE with ARKode using the specified step mode.
|
||||
/**
|
||||
@@ -871,7 +905,7 @@ protected:
|
||||
|
||||
/// Wrapper to compute the Jacobian-vector product $ J(u) v = Jv $.
|
||||
static int GradientMult(N_Vector v, N_Vector Jv, N_Vector u,
|
||||
booleantype *new_u, void *user_data);
|
||||
sunbooleantype *new_u, void *user_data);
|
||||
|
||||
/// Setup the linear system $ J u = b $.
|
||||
static int LinSysSetup(N_Vector u, N_Vector fu, SUNMatrix J,
|
||||
@@ -879,7 +913,7 @@ protected:
|
||||
|
||||
/// Solve the linear system $ J u = b $.
|
||||
static int LinSysSolve(SUNLinearSolver LS, SUNMatrix J, N_Vector u,
|
||||
N_Vector b, realtype tol);
|
||||
N_Vector b, sunrealtype tol);
|
||||
|
||||
/// Setup the preconditioner.
|
||||
static int PrecSetup(N_Vector uu,
|
||||
|
||||
@@ -79,6 +79,7 @@ inline real_t rand_real()
|
||||
class Vector
|
||||
{
|
||||
protected:
|
||||
template<typename mfem_type, int N, typename L> friend class MDSpan;
|
||||
|
||||
Memory<real_t> data;
|
||||
int size;
|
||||
|
||||
+8
-4
@@ -32,6 +32,7 @@ set(SRCS
|
||||
vtk.cpp
|
||||
wedge.cpp
|
||||
submesh/submesh.cpp
|
||||
submesh/ncsubmesh.cpp
|
||||
submesh/submesh_utils.cpp
|
||||
submesh/transfermap.cpp
|
||||
)
|
||||
@@ -58,6 +59,7 @@ set(HDRS
|
||||
vertex.hpp
|
||||
vtk.hpp
|
||||
wedge.hpp
|
||||
submesh/ncsubmesh.hpp
|
||||
submesh/submesh.hpp
|
||||
submesh/submesh_utils.hpp
|
||||
submesh/transfer_category.hpp
|
||||
@@ -68,15 +70,17 @@ if (MFEM_USE_MPI)
|
||||
list(APPEND SRCS
|
||||
pmesh.cpp
|
||||
pncmesh.cpp
|
||||
submesh/ptransfermap.cpp
|
||||
submesh/psubmesh.cpp)
|
||||
submesh/pncsubmesh.cpp
|
||||
submesh/psubmesh.cpp
|
||||
submesh/ptransfermap.cpp)
|
||||
# If this list (HDRS -> HEADERS) is used for install, we probably want the
|
||||
# headers added all the time.
|
||||
list(APPEND HDRS
|
||||
pmesh.hpp
|
||||
pncmesh.hpp
|
||||
submesh/ptransfermap.hpp
|
||||
submesh/psubmesh.hpp)
|
||||
submesh/pncsubmesh.hpp
|
||||
submesh/psubmesh.hpp
|
||||
submesh/ptransfermap.hpp)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_PUMI)
|
||||
|
||||
+18
-1
@@ -2033,6 +2033,18 @@ int Mesh::AddBdrElement(Element *elem)
|
||||
return NumOfBdrElements++;
|
||||
}
|
||||
|
||||
void Mesh::AddBdrElements(Array<Element *> &bdr_elems,
|
||||
const Array<int> &new_be_to_face)
|
||||
{
|
||||
boundary.Reserve(boundary.Size() + bdr_elems.Size());
|
||||
MFEM_ASSERT(bdr_elems.Size() == new_be_to_face.Size(), "wrong size");
|
||||
for (int i = 0; i < bdr_elems.Size(); i++)
|
||||
{
|
||||
AddBdrElement(bdr_elems[i]);
|
||||
}
|
||||
be_to_face.Append(new_be_to_face);
|
||||
}
|
||||
|
||||
int Mesh::AddBdrSegment(int v1, int v2, int attr)
|
||||
{
|
||||
CheckEnlarge(boundary, NumOfBdrElements);
|
||||
@@ -7346,6 +7358,12 @@ void Mesh::GetBdrElementAdjacentElement2(
|
||||
info = fi.Elem1Inf + ori;
|
||||
}
|
||||
|
||||
void Mesh::SetAttribute(int i, int attr)
|
||||
{
|
||||
elements[i]->SetAttribute(attr);
|
||||
if (ncmesh) ncmesh->SetAttribute(i, attr);
|
||||
}
|
||||
|
||||
Element::Type Mesh::GetElementType(int i) const
|
||||
{
|
||||
return elements[i]->GetType();
|
||||
@@ -7672,7 +7690,6 @@ void Mesh::AddQuadFaceElement(int lf, int gf, int el,
|
||||
void Mesh::GenerateFaces()
|
||||
{
|
||||
int nfaces = GetNumFaces();
|
||||
|
||||
for (auto &f : faces)
|
||||
{
|
||||
FreeElement(f);
|
||||
|
||||
+21
-1
@@ -993,6 +993,17 @@ public:
|
||||
/// @note Ownership of @a elem will pass to the Mesh object
|
||||
int AddBdrElement(Element *elem);
|
||||
|
||||
/**
|
||||
* @brief Add an array of boundary elements to the mesh, along with map from
|
||||
* the elements to their faces
|
||||
* @param[in] bdr_elems The set of boundary element pointers, ownership of
|
||||
* the pointers will be transferred to the Mesh object
|
||||
* @param[in] be_to_face The map from the boundary element index to the face
|
||||
* index
|
||||
*/
|
||||
void AddBdrElements(Array<Element *> &bdr_elems,
|
||||
const Array<int> &be_to_face);
|
||||
|
||||
int AddBdrSegment(int v1, int v2, int attr = 1);
|
||||
int AddBdrSegment(const int *vi, int attr = 1);
|
||||
|
||||
@@ -1102,6 +1113,15 @@ public:
|
||||
have two adjacent faces in 3D, or edges in 2D. */
|
||||
void RemoveInternalBoundaries();
|
||||
|
||||
/**
|
||||
* @brief Clear the boundary element to edge map.
|
||||
*/
|
||||
void DeleteBoundaryElementToEdge()
|
||||
{
|
||||
delete bel_to_edge;
|
||||
bel_to_edge = nullptr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
/// @name Element ordering methods
|
||||
@@ -1366,7 +1386,7 @@ public:
|
||||
int GetAttribute(int i) const { return elements[i]->GetAttribute(); }
|
||||
|
||||
/// Set the attribute of element i.
|
||||
void SetAttribute(int i, int attr) { elements[i]->SetAttribute(attr); }
|
||||
void SetAttribute(int i, int attr);
|
||||
|
||||
/// Return the attribute of boundary element i.
|
||||
int GetBdrAttribute(int i) const { return boundary[i]->GetAttribute(); }
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "ncmesh.hpp"
|
||||
#include "mesh.hpp"
|
||||
#include "mesh_operators.hpp"
|
||||
#include "submesh/ncsubmesh.hpp"
|
||||
#include "submesh/submesh.hpp"
|
||||
#include "submesh/submesh_utils.hpp"
|
||||
#include "submesh/transfermap.hpp"
|
||||
@@ -36,6 +37,7 @@
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "pncmesh.hpp"
|
||||
#include "pmesh.hpp"
|
||||
#include "submesh/pncsubmesh.hpp"
|
||||
#include "submesh/psubmesh.hpp"
|
||||
#include "submesh/ptransfermap.hpp"
|
||||
#endif
|
||||
|
||||
+249
-65
@@ -58,23 +58,25 @@ void NCMesh::GeomInfo::InitGeom(Geometry::Type geom)
|
||||
{
|
||||
if (initialized) { return; }
|
||||
|
||||
mfem::Element *elem = NULL;
|
||||
switch (geom)
|
||||
auto elem = [&]()
|
||||
{
|
||||
case Geometry::CUBE: elem = new Hexahedron; break;
|
||||
case Geometry::PRISM: elem = new Wedge; break;
|
||||
case Geometry::TETRAHEDRON: elem = new Tetrahedron; break;
|
||||
case Geometry::PYRAMID: elem = new Pyramid; break;
|
||||
case Geometry::SQUARE: elem = new Quadrilateral; break;
|
||||
case Geometry::TRIANGLE: elem = new Triangle; break;
|
||||
case Geometry::SEGMENT: elem = new Segment; break;
|
||||
default: MFEM_ABORT("unsupported geometry " << geom);
|
||||
}
|
||||
switch (geom)
|
||||
{
|
||||
case Geometry::CUBE: return std::unique_ptr<mfem::Element>(new Hexahedron);
|
||||
case Geometry::PRISM: return std::unique_ptr<mfem::Element>(new Wedge);
|
||||
case Geometry::TETRAHEDRON: return std::unique_ptr<mfem::Element>
|
||||
(new Tetrahedron);
|
||||
case Geometry::PYRAMID: return std::unique_ptr<mfem::Element>(new Pyramid);
|
||||
case Geometry::SQUARE: return std::unique_ptr<mfem::Element>(new Quadrilateral);
|
||||
case Geometry::TRIANGLE: return std::unique_ptr<mfem::Element>(new Triangle);
|
||||
case Geometry::SEGMENT: return std::unique_ptr<mfem::Element>(new Segment);
|
||||
default: MFEM_ABORT("unsupported geometry " << geom);
|
||||
}
|
||||
}();
|
||||
|
||||
nv = elem->GetNVertices();
|
||||
ne = elem->GetNEdges();
|
||||
nf = elem->GetNFaces();
|
||||
|
||||
for (int i = 0; i < ne; i++)
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
@@ -119,19 +121,9 @@ void NCMesh::GeomInfo::InitGeom(Geometry::Type geom)
|
||||
}
|
||||
}
|
||||
|
||||
delete elem;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
static void CheckSupportedGeom(Geometry::Type geom)
|
||||
{
|
||||
MFEM_VERIFY(geom == Geometry::SEGMENT ||
|
||||
geom == Geometry::TRIANGLE || geom == Geometry::SQUARE ||
|
||||
geom == Geometry::CUBE || geom == Geometry::PRISM ||
|
||||
geom == Geometry::PYRAMID || geom == Geometry::TETRAHEDRON,
|
||||
"Element type " << geom << " is not supported by NCMesh.");
|
||||
}
|
||||
|
||||
NCMesh::NCMesh(const Mesh *mesh)
|
||||
: shadow(1024, 2048)
|
||||
{
|
||||
@@ -157,7 +149,7 @@ NCMesh::NCMesh(const Mesh *mesh)
|
||||
}
|
||||
|
||||
// create NCMesh::Element for this mfem::Element
|
||||
int root_id = AddElement(Element(geom, elem->GetAttribute()));
|
||||
int root_id = AddElement(geom, elem->GetAttribute());
|
||||
MFEM_ASSERT(root_id == i, "");
|
||||
Element &root_elem = elements[root_id];
|
||||
|
||||
@@ -248,11 +240,18 @@ NCMesh::NCMesh(const NCMesh &other)
|
||||
, nodes(other.nodes)
|
||||
, faces(other.faces)
|
||||
, elements(other.elements)
|
||||
, free_element_ids(other.free_element_ids)
|
||||
, root_state(other.root_state)
|
||||
, coordinates(other.coordinates)
|
||||
, NEdges(other.NEdges)
|
||||
, NFaces(other.NFaces)
|
||||
, NGhostEdges(other.NGhostEdges)
|
||||
, NGhostFaces(other.NGhostFaces)
|
||||
, boundary_faces(other.boundary_faces)
|
||||
, face_geom(other.face_geom)
|
||||
, element_vertex(other.element_vertex)
|
||||
, shadow(1024, 2048)
|
||||
{
|
||||
other.free_element_ids.Copy(free_element_ids);
|
||||
other.root_state.Copy(root_state);
|
||||
other.coordinates.Copy(coordinates);
|
||||
Update();
|
||||
}
|
||||
|
||||
@@ -351,8 +350,8 @@ int NCMesh::GetMidFaceNode(int en1, int en2, int en3, int en4)
|
||||
|
||||
void NCMesh::ReferenceElement(int elem)
|
||||
{
|
||||
Element &el = elements[elem];
|
||||
int* node = el.node;
|
||||
const Element &el = elements[elem];
|
||||
const int* node = el.node;
|
||||
GeomInfo& gi = GI[el.Geom()];
|
||||
|
||||
// reference all vertices
|
||||
@@ -507,7 +506,7 @@ int NCMesh::NewHexahedron(int n0, int n1, int n2, int n3,
|
||||
int fattr3, int fattr4, int fattr5)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Element(Geometry::CUBE, attr));
|
||||
int new_id = AddElement(Geometry::CUBE, attr);
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
|
||||
@@ -537,7 +536,7 @@ int NCMesh::NewWedge(int n0, int n1, int n2,
|
||||
int fattr2, int fattr3, int fattr4)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Element(Geometry::PRISM, attr));
|
||||
int new_id = AddElement(Geometry::PRISM, attr);
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2;
|
||||
@@ -566,7 +565,7 @@ int NCMesh::NewTetrahedron(int n0, int n1, int n2, int n3, int attr,
|
||||
int fattr0, int fattr1, int fattr2, int fattr3)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Element(Geometry::TETRAHEDRON, attr));
|
||||
int new_id = AddElement(Geometry::TETRAHEDRON, attr);
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
|
||||
@@ -592,7 +591,7 @@ int NCMesh::NewPyramid(int n0, int n1, int n2, int n3, int n4, int attr,
|
||||
int fattr4)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Element(Geometry::PYRAMID, attr));
|
||||
int new_id = AddElement(Geometry::PYRAMID, attr);
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
|
||||
@@ -622,7 +621,7 @@ int NCMesh::NewQuadrilateral(int n0, int n1, int n2, int n3,
|
||||
int eattr0, int eattr1, int eattr2, int eattr3)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Element(Geometry::SQUARE, attr));
|
||||
int new_id = AddElement(Geometry::SQUARE, attr);
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2, el.node[3] = n3;
|
||||
@@ -647,7 +646,7 @@ int NCMesh::NewTriangle(int n0, int n1, int n2,
|
||||
int attr, int eattr0, int eattr1, int eattr2)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Element(Geometry::TRIANGLE, attr));
|
||||
int new_id = AddElement(Geometry::TRIANGLE, attr);
|
||||
Element &el = elements[new_id];
|
||||
|
||||
el.node[0] = n0, el.node[1] = n1, el.node[2] = n2;
|
||||
@@ -672,7 +671,7 @@ int NCMesh::NewTriangle(int n0, int n1, int n2,
|
||||
int NCMesh::NewSegment(int n0, int n1, int attr, int vattr1, int vattr2)
|
||||
{
|
||||
// create new element, initialize nodes
|
||||
int new_id = AddElement(Element(Geometry::SEGMENT, attr));
|
||||
int new_id = AddElement(Geometry::SEGMENT, attr);
|
||||
Element &el = elements[new_id];
|
||||
el.node[0] = n0, el.node[1] = n1;
|
||||
|
||||
@@ -2167,7 +2166,6 @@ void NCMesh::UpdateLeafElements()
|
||||
// final (Mesh) indices of leaves
|
||||
leaf_elements.Append(ghosts);
|
||||
leaf_sfc_index.SetSize(leaf_elements.Size());
|
||||
|
||||
for (int i = 0; i < leaf_elements.Size(); i++)
|
||||
{
|
||||
Element &el = elements[leaf_elements[i]];
|
||||
@@ -2234,7 +2232,6 @@ void NCMesh::UpdateVertices()
|
||||
}
|
||||
|
||||
// STEP 2: assign indices of top-level local vertices, in original order
|
||||
|
||||
NVertices = 0;
|
||||
for (auto &node : nodes)
|
||||
{
|
||||
@@ -2246,7 +2243,6 @@ void NCMesh::UpdateVertices()
|
||||
|
||||
// STEP 3: go over all elements (local and ghost) in SFC order and assign
|
||||
// remaining local vertices in that order.
|
||||
|
||||
Array<int> sfc_order(leaf_elements.Size());
|
||||
for (int i = 0; i < sfc_order.Size(); i++)
|
||||
{
|
||||
@@ -2264,7 +2260,6 @@ void NCMesh::UpdateVertices()
|
||||
}
|
||||
|
||||
// STEP 4: create the mapping from Mesh vertex index to NCMesh node index
|
||||
|
||||
vertex_nodeId.SetSize(NVertices);
|
||||
for (auto node = nodes.begin(); node != nodes.end(); ++node)
|
||||
{
|
||||
@@ -2277,7 +2272,6 @@ void NCMesh::UpdateVertices()
|
||||
|
||||
// STEP 5: assign remaining ghost vertices, ignore vertices beyond the ghost
|
||||
// layer
|
||||
|
||||
NGhostVertices = 0;
|
||||
for (int i = 0; i < sfc_order.Size(); i++)
|
||||
{
|
||||
@@ -2361,6 +2355,8 @@ void NCMesh::InitRootState(int root_count)
|
||||
root_state.SetSize(root_count);
|
||||
root_state = 0;
|
||||
|
||||
if (elements.Size() == 0) { return; }
|
||||
|
||||
char* node_order;
|
||||
int nch;
|
||||
|
||||
@@ -2610,11 +2606,10 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
|
||||
{
|
||||
const int *ev = edge_vertex->GetRow(i);
|
||||
Node* node = nodes.Find(vertex_nodeId[ev[0]], vertex_nodeId[ev[1]]);
|
||||
|
||||
MFEM_ASSERT(node && node->HasEdge(),
|
||||
"edge (" << ev[0] << "," << ev[1] << ") not found, "
|
||||
"node = " << node);
|
||||
|
||||
"node = " << node << " node->HasEdge() "
|
||||
<< (node != nullptr ? node->HasEdge() : false));
|
||||
node->edge_index = i;
|
||||
}
|
||||
|
||||
@@ -2709,7 +2704,6 @@ void NCMesh::OnMeshUpdated(Mesh *mesh)
|
||||
if (face.index < 0)
|
||||
{
|
||||
face.index = NFaces + (nghosts++);
|
||||
|
||||
// store the face geometry
|
||||
static const Geometry::Type types[5] =
|
||||
{
|
||||
@@ -2793,10 +2787,186 @@ bool NCMesh::TriFaceSplit(int v1, int v2, int v3, int mid[3]) const
|
||||
if (mid) { mid[0] = e1, mid[1] = e2, mid[2] = e3; }
|
||||
|
||||
// This is necessary but not sufficient to determine if a face has been
|
||||
// split.
|
||||
// split. All edges might have been split due to edge attached faces being
|
||||
// refined. Need to check for existence of face made up of midpoints.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool contains_node(const std::array<int, 4> &nodes, int n)
|
||||
{
|
||||
return std::find(nodes.begin(), nodes.end(), n) != nodes.end();
|
||||
};
|
||||
|
||||
int NCMesh::ParentFaceNodes(std::array<int, 4> &face_nodes) const
|
||||
{
|
||||
const bool is_tri = face_nodes[3] == -1;
|
||||
const bool is_segment = (face_nodes[0] == face_nodes[1] &&
|
||||
face_nodes[2] == face_nodes[3]);
|
||||
const bool is_quad = *std::min_element(face_nodes.begin(),
|
||||
face_nodes.end()) >= 0;
|
||||
|
||||
MFEM_ASSERT((is_tri && !is_segment && !is_quad)
|
||||
|| (!is_tri && is_segment && !is_quad) || (!is_tri && !is_segment &&
|
||||
is_quad), "Inconsistent node geometry");
|
||||
|
||||
bool all_nodes_root = true;
|
||||
for (auto x : face_nodes)
|
||||
{
|
||||
all_nodes_root = all_nodes_root && (x < 0 || (nodes[x].p1 == nodes[x].p2));
|
||||
}
|
||||
// This face is a root face -> nothing to do.
|
||||
if (all_nodes_root) { return -1; }
|
||||
|
||||
int child = -1; // The index into parent.child that this face corresponds to.
|
||||
auto parent_nodes = face_nodes;
|
||||
if (is_quad)
|
||||
{
|
||||
// Logic for coarsening anisotropic faces is more complex, needs
|
||||
// identification and handling of multiple "crux" points. Will require
|
||||
// inspection of edge nodes.
|
||||
MFEM_VERIFY(Iso,
|
||||
"ParentFaceNodes does not support anisotropic refinement yet!");
|
||||
|
||||
// Finds the first node whose parents aren't in the face_nodes. This is
|
||||
// also the index of the child location in the parent face. Treated
|
||||
// separately as ultimately multiple crux will need to be handled for
|
||||
// anisotropic faces.
|
||||
const auto crux = [&]()
|
||||
{
|
||||
for (int i = 0; i < static_cast<int>(face_nodes.size()); i++)
|
||||
{
|
||||
if ((!contains_node(face_nodes, nodes[face_nodes[i]].p1)
|
||||
&& !contains_node(face_nodes, nodes[face_nodes[i]].p2))
|
||||
|| (nodes[face_nodes[i]].p1 == nodes[face_nodes[i]].p2) /* top level node */)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}();
|
||||
MFEM_ASSERT(crux != -1, "A root face should have been returned early");
|
||||
|
||||
// Loop over nodes, starting from diagonal to child, wrapping and skipping
|
||||
// child. This will visit the node opposite child twice, thereby
|
||||
// coarsening to the diagonally opposite. NOTE: This assumes that the
|
||||
// nodes for a square are numbered (0 -> 1 -> 2 -> 3 -> 0).
|
||||
for (int i = 0; i < static_cast<int>(face_nodes.size()) + 1; i++)
|
||||
{
|
||||
int ind = (crux + i + 2) %
|
||||
4; // Start and end with coarsening of the diagonally opposite
|
||||
if (ind == crux) { continue; }
|
||||
auto &x = parent_nodes[ind];
|
||||
|
||||
// Check against parent_nodes rather than face_nodes so on second lap
|
||||
// the node opposite crux will coarsen again to the diagonally across
|
||||
// in the parent face. A top level node has p1 == p2, thus these
|
||||
// modifications do nothing.
|
||||
if (contains_node(parent_nodes, nodes[x].p1))
|
||||
{
|
||||
MFEM_ASSERT(nodes[x].p2 == nodes[x].p1 ||
|
||||
!contains_node(parent_nodes, nodes[x].p2), "!");
|
||||
x = nodes[x].p2;
|
||||
}
|
||||
else if (contains_node(parent_nodes, nodes[x].p2))
|
||||
{
|
||||
MFEM_ASSERT(nodes[x].p2 == nodes[x].p1 ||
|
||||
!contains_node(parent_nodes, nodes[x].p1), "!");
|
||||
x = nodes[x].p1;
|
||||
}
|
||||
else { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
else if (is_tri)
|
||||
{
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
auto x = face_nodes[i];
|
||||
if (x == -1) { continue; }
|
||||
if (contains_node(face_nodes, nodes[x].p1))
|
||||
{
|
||||
MFEM_ASSERT(nodes[x].p2 == nodes[x].p1 ||
|
||||
!contains_node(face_nodes, nodes[x].p2), "!");
|
||||
parent_nodes[i] = nodes[x].p2;
|
||||
}
|
||||
else if (contains_node(face_nodes, nodes[x].p2))
|
||||
{
|
||||
MFEM_ASSERT(nodes[x].p2 == nodes[x].p1 ||
|
||||
!contains_node(face_nodes, nodes[x].p1), "!");
|
||||
parent_nodes[i] = nodes[x].p1;
|
||||
}
|
||||
else { /* do nothing */ }
|
||||
}
|
||||
|
||||
if (std::equal(face_nodes.begin(), face_nodes.end(), parent_nodes.begin()))
|
||||
{
|
||||
// Having excluded root faces, this must be an interior face. We need
|
||||
// to handle the special case of the interior face of the parent face.
|
||||
std::array<std::array<int, 2>, 6> parent_pairs;
|
||||
for (std::size_t i = 0; i < face_nodes.size() - 1; i++)
|
||||
{
|
||||
parent_pairs[i][0] = nodes[face_nodes[i]].p1;
|
||||
parent_pairs[i][1] = nodes[face_nodes[i]].p2;
|
||||
}
|
||||
// Each node gets mapped to the common node from its parents and the
|
||||
// predecessor node's parents.
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
// Parenting convention here assumes parent face has the SAME
|
||||
// orientation as the original. This is true on exterior boundaries,
|
||||
// but for an interior boundary the master face will have an
|
||||
// opposing orientation. TODO: Possibly fix for interior boundaries.
|
||||
const auto &prev = parent_pairs[(i - 1 + 3) % 3]; // (0 -> 2, 1 -> 0, 2 -> 1)
|
||||
const auto &next = parent_pairs[(i + 1 + 3) % 3]; // (0 -> 1, 1 -> 2, 2 -> 0)
|
||||
for (auto x : next)
|
||||
{
|
||||
if (std::find(prev.begin(), prev.end(), x) != prev.end()) { parent_nodes[i] = x; }
|
||||
}
|
||||
}
|
||||
child = 3; // The interior face is the final child.
|
||||
}
|
||||
}
|
||||
else if (is_segment)
|
||||
{
|
||||
// Given this isn't a root face, one node must be the parent of the other.
|
||||
if (face_nodes[0] == nodes[face_nodes[1]].p1)
|
||||
{
|
||||
face_nodes[1] = nodes[face_nodes[1]].p2;
|
||||
}
|
||||
else if (face_nodes[0] == nodes[face_nodes[1]].p2)
|
||||
{
|
||||
face_nodes[1] = nodes[face_nodes[1]].p1;
|
||||
}
|
||||
else if (face_nodes[1] == nodes[face_nodes[0]].p1)
|
||||
{
|
||||
face_nodes[0] = nodes[face_nodes[0]].p2;
|
||||
}
|
||||
else if (face_nodes[1] == nodes[face_nodes[0]].p2)
|
||||
{
|
||||
face_nodes[0] = nodes[face_nodes[0]].p1;
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Internal logic error!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unrecognized face geometry!");
|
||||
}
|
||||
for (int i = 0; i < 4 && face_nodes[i] >= 0; i++)
|
||||
{
|
||||
if (face_nodes[i] == parent_nodes[i])
|
||||
{
|
||||
MFEM_ASSERT(child == -1,
|
||||
"This face cannot be more than one child of the parent face!");
|
||||
child = i;
|
||||
}
|
||||
}
|
||||
MFEM_ASSERT(child != -1, "Root elements must have exited early!");
|
||||
std::swap(face_nodes, parent_nodes);
|
||||
return child;
|
||||
}
|
||||
|
||||
int NCMesh::find_node(const Element &el, int node)
|
||||
{
|
||||
for (int i = 0; i < MaxElemNodes; i++)
|
||||
@@ -3556,7 +3726,8 @@ NCMesh::NCList::BuildIndex() const
|
||||
int max_master_index = max_master != nullptr ? max_master->index : -1;
|
||||
int max_slave_index = max_slave != nullptr ? max_slave->index : -1;
|
||||
|
||||
inv_index.reserve(std::max({max_conforming_index, max_master_index, max_slave_index}));
|
||||
inv_index.reserve(max(max_conforming_index, max_master_index, max_slave_index,
|
||||
0));
|
||||
for (int i = 0; i < conforming.Size(); i++)
|
||||
{
|
||||
inv_index.emplace(conforming[i].index, std::make_pair(MeshIdType::CONFORMING,
|
||||
@@ -3571,8 +3742,6 @@ NCMesh::NCList::BuildIndex() const
|
||||
inv_index.emplace(slaves[i].index, std::make_pair(MeshIdType::SLAVE, i));
|
||||
}
|
||||
}
|
||||
MFEM_ASSERT(inv_index.size() > 0,
|
||||
"Empty inverse index, member lists must be populated before BuildIndex is called!");
|
||||
}
|
||||
|
||||
//// Neighbors /////////////////////////////////////////////////////////////////
|
||||
@@ -5260,12 +5429,21 @@ void NCMesh::GetElementFacesAttributes(int leaf_elem,
|
||||
face_attribs[i] = face->attribute;
|
||||
}
|
||||
}
|
||||
|
||||
void NCMesh::FindFaceNodes(int face, int node[4]) const
|
||||
{
|
||||
auto tmp = FindFaceNodes(face);
|
||||
std::copy(tmp.begin(), tmp.end(), node);
|
||||
}
|
||||
|
||||
std::array<int, 4> NCMesh::FindFaceNodes(int face) const
|
||||
{
|
||||
return FindFaceNodes(faces[face]);
|
||||
}
|
||||
|
||||
std::array<int, 4> NCMesh::FindFaceNodes(const Face &fa) const
|
||||
{
|
||||
// Obtain face nodes from one of its elements (note that face->p1, p2, p3
|
||||
// cannot be used directly since they are not in order and p4 is missing).
|
||||
const Face &fa = faces[face];
|
||||
int elem = fa.elem[0];
|
||||
if (elem < 0) { elem = fa.elem[1]; }
|
||||
MFEM_ASSERT(elem >= 0, "Face has no elements?");
|
||||
@@ -5277,10 +5455,12 @@ void NCMesh::FindFaceNodes(int face, int node[4]) const
|
||||
find_node(el, fa.p3));
|
||||
|
||||
const int* fv = GI[el.Geom()].faces[f];
|
||||
std::array<int, 4> node;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
node[i] = el.node[fv[i]];
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
void NCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
|
||||
@@ -5294,13 +5474,11 @@ void NCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
|
||||
if (Dim == 3)
|
||||
{
|
||||
GetFaceList(); // make sure 'boundary_faces' is up to date
|
||||
|
||||
for (int f : boundary_faces)
|
||||
{
|
||||
if (bdr_attr_is_ess[faces[f].attribute - 1])
|
||||
{
|
||||
int node[4];
|
||||
FindFaceNodes(f, node);
|
||||
auto node = FindFaceNodes(f);
|
||||
int nfv = (node[3] < 0) ? 3 : 4;
|
||||
|
||||
for (int j = 0; j < nfv; j++)
|
||||
@@ -5334,6 +5512,7 @@ void NCMesh::GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
|
||||
}
|
||||
else if (Dim == 2)
|
||||
{
|
||||
GetFaceList();
|
||||
GetEdgeList(); // make sure 'boundary_faces' is up to date
|
||||
|
||||
for (int f : boundary_faces)
|
||||
@@ -5554,9 +5733,7 @@ void NCMesh::LimitNCLevel(int max_nc_level)
|
||||
{
|
||||
Array<Refinement> refinements;
|
||||
GetLimitRefinements(refinements, max_nc_level);
|
||||
|
||||
if (!refinements.Size()) { break; }
|
||||
|
||||
Refine(refinements);
|
||||
}
|
||||
}
|
||||
@@ -5847,12 +6024,15 @@ void NCMesh::InitRootElements()
|
||||
|
||||
// count the root elements
|
||||
int nroots = 0;
|
||||
while (nroots < elements.Size() &&
|
||||
elements[nroots].parent == -1)
|
||||
{
|
||||
nroots++;
|
||||
}
|
||||
MFEM_VERIFY(nroots, "invalid mesh file: no root elements found.");
|
||||
for (const auto &e : elements)
|
||||
if (e.parent == -1)
|
||||
{
|
||||
++nroots;
|
||||
}
|
||||
MFEM_VERIFY(nroots > 0 ||
|
||||
elements.Size() == 0,
|
||||
"invalid mesh file: no root elements in non-empty mesh found.");
|
||||
|
||||
|
||||
// check that only the first 'nroot' elements are roots (have no parent)
|
||||
for (int i = nroots; i < elements.Size(); i++)
|
||||
@@ -5892,6 +6072,9 @@ NCMesh::NCMesh(std::istream &input, int version, int &curved, int &is_nc)
|
||||
std::string ident;
|
||||
int count;
|
||||
|
||||
// Skip the version string
|
||||
skip_comment_lines(input, 'M');
|
||||
|
||||
// load dimension
|
||||
skip_comment_lines(input, '#');
|
||||
input >> ident;
|
||||
@@ -6018,9 +6201,10 @@ NCMesh::NCMesh(std::istream &input, int version, int &curved, int &is_nc)
|
||||
{
|
||||
LoadCoordinates(input);
|
||||
|
||||
MFEM_VERIFY(coordinates.Size()/3 >= CountTopLevelNodes(),
|
||||
MFEM_VERIFY(coordinates.Size() >= 3*CountTopLevelNodes(),
|
||||
"Invalid mesh file: not all top-level nodes are covered by "
|
||||
"the 'coordinates' section of the mesh file.");
|
||||
"the 'coordinates' section of the mesh file: " << coordinates.Size() << ' ' <<
|
||||
3*CountTopLevelNodes());
|
||||
curved = 0;
|
||||
}
|
||||
else if (ident == "nodes")
|
||||
@@ -6082,7 +6266,7 @@ void NCMesh::LoadCoarseElements(std::istream &input)
|
||||
int ref_type;
|
||||
input >> ref_type;
|
||||
|
||||
int elem = AddElement(Element(Geometry::INVALID, 0));
|
||||
int elem = AddElement(Geometry::INVALID, 0);
|
||||
Element &el = elements[elem];
|
||||
el.ref_type = ref_type;
|
||||
|
||||
@@ -6169,7 +6353,7 @@ void NCMesh::LoadLegacyFormat(std::istream &input, int &curved, int &is_nc)
|
||||
CheckSupportedGeom(type);
|
||||
GI[geom].InitGeom(type);
|
||||
|
||||
int eid = AddElement(Element(type, attr));
|
||||
int eid = AddElement(type, attr);
|
||||
MFEM_ASSERT(eid == i, "");
|
||||
|
||||
Element &el = elements[eid];
|
||||
|
||||
+222
-119
@@ -29,10 +29,10 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** Represents the index of an element to refine, plus a refinement type.
|
||||
The refinement type is needed for anisotropic refinement of quads and hexes.
|
||||
Bits 0,1 and 2 of 'ref_type' specify whether the element should be split
|
||||
in the X, Y and Z directions, respectively (Z is ignored for quads). */
|
||||
/** Represents the index of an element to refine, plus a refinement type. The
|
||||
refinement type is needed for anisotropic refinement of quads and hexes.
|
||||
Bits 0,1 and 2 of 'ref_type' specify whether the element should be split in
|
||||
the X, Y and Z directions, respectively (Z is ignored for quads). */
|
||||
struct Refinement
|
||||
{
|
||||
enum : char { X = 1, Y = 2, Z = 4, XY = 3, XZ = 5, YZ = 6, XYZ = 7 };
|
||||
@@ -45,7 +45,6 @@ struct Refinement
|
||||
: index(index), ref_type(type) {}
|
||||
};
|
||||
|
||||
|
||||
/// Defines the position of a fine element within a coarse element.
|
||||
struct Embedding
|
||||
{
|
||||
@@ -54,7 +53,8 @@ struct Embedding
|
||||
|
||||
/** The (geom, matrix) pair determines the sub-element transformation for the
|
||||
fine element: CoarseFineTransformations::point_matrices[geom](matrix) is
|
||||
the point matrix of the region within the coarse element reference domain.*/
|
||||
the point matrix of the region within the coarse element reference
|
||||
domain.*/
|
||||
unsigned geom : 4;
|
||||
unsigned matrix : 27;
|
||||
|
||||
@@ -66,7 +66,6 @@ struct Embedding
|
||||
: parent(elem), geom(geom), matrix(matrix), ghost(ghost) {}
|
||||
};
|
||||
|
||||
|
||||
/// Defines the coarse-fine transformations of all fine elements.
|
||||
struct CoarseFineTransformations
|
||||
{
|
||||
@@ -96,24 +95,23 @@ void Swap(CoarseFineTransformations &a, CoarseFineTransformations &b);
|
||||
|
||||
struct MatrixMap; // for internal use
|
||||
|
||||
|
||||
/** \brief A class for non-conforming AMR. The class is not used directly
|
||||
* by the user, rather it is an extension of the Mesh class.
|
||||
/** \brief A class for non-conforming AMR. The class is not used directly by the
|
||||
* user, rather it is an extension of the Mesh class.
|
||||
*
|
||||
* In general, the class is used by MFEM as follows:
|
||||
*
|
||||
* 1. NCMesh is constructed from elements of an existing Mesh. The elements
|
||||
* are copied and become roots of the refinement hierarchy.
|
||||
* 1. NCMesh is constructed from elements of an existing Mesh. The elements are
|
||||
* copied and become roots of the refinement hierarchy.
|
||||
*
|
||||
* 2. Some elements are refined with the Refine() method. Both isotropic and
|
||||
* anisotropic refinements of quads/hexes are supported.
|
||||
*
|
||||
* 3. A new Mesh is created from NCMesh containing the leaf elements.
|
||||
* This new Mesh may have non-conforming (hanging) edges and faces and
|
||||
* is the one seen by the user.
|
||||
* 3. A new Mesh is created from NCMesh containing the leaf elements. This new
|
||||
* Mesh may have non-conforming (hanging) edges and faces and is the one
|
||||
* seen by the user.
|
||||
*
|
||||
* 4. FiniteElementSpace asks NCMesh for a list of conforming, master and
|
||||
* slave edges/faces and creates the conforming interpolation matrix P.
|
||||
* 4. FiniteElementSpace asks NCMesh for a list of conforming, master and slave
|
||||
* edges/faces and creates the conforming interpolation matrix P.
|
||||
*
|
||||
* 5. A continuous/conforming solution is obtained by solving P'*A*P x = P'*b.
|
||||
*
|
||||
@@ -121,8 +119,10 @@ struct MatrixMap; // for internal use
|
||||
*/
|
||||
class NCMesh
|
||||
{
|
||||
protected:
|
||||
NCMesh() = default;
|
||||
public:
|
||||
//// Initialize with elements from an existing 'mesh'.
|
||||
//// Initialize with elements from an existing Mesh.
|
||||
explicit NCMesh(const Mesh *mesh);
|
||||
|
||||
/** Load from a stream. The id header is assumed to have been read already
|
||||
@@ -155,8 +155,8 @@ public:
|
||||
virtual int GetNGhostElements() const { return 0; }
|
||||
|
||||
/** Perform the given batch of refinements. Please note that in the presence
|
||||
of anisotropic splits additional refinements may be necessary to keep
|
||||
the mesh consistent. However, the function always performs at least the
|
||||
of anisotropic splits additional refinements may be necessary to keep the
|
||||
mesh consistent. However, the function always performs at least the
|
||||
requested refinements. */
|
||||
virtual void Refine(const Array<Refinement> &refinements);
|
||||
|
||||
@@ -172,14 +172,16 @@ public:
|
||||
const Table &GetDerefinementTable();
|
||||
|
||||
/** Check derefinements returned by GetDerefinementTable and mark those that
|
||||
can be done safely so that the maximum NC level condition is not violated.
|
||||
On return, level_ok.Size() == deref_table.Size() and contains 0/1s. */
|
||||
can be done safely so that the maximum NC level condition is not
|
||||
violated. On return, level_ok.Size() == deref_table.Size() and contains
|
||||
0/1s. */
|
||||
virtual void CheckDerefinementNCLevel(const Table &deref_table,
|
||||
Array<int> &level_ok, int max_nc_level);
|
||||
|
||||
/** Perform a subset of the possible derefinements (see GetDerefinementTable).
|
||||
Note that if anisotropic refinements are present in the mesh, some of the
|
||||
derefinements may have to be skipped to preserve mesh consistency. */
|
||||
/** Perform a subset of the possible derefinements (see
|
||||
GetDerefinementTable). Note that if anisotropic refinements are present
|
||||
in the mesh, some of the derefinements may have to be skipped to preserve
|
||||
mesh consistency. */
|
||||
virtual void Derefine(const Array<int> &derefs);
|
||||
|
||||
// master/slave lists
|
||||
@@ -340,9 +342,9 @@ public:
|
||||
const CoarseFineTransformations& GetRefinementTransforms() const;
|
||||
|
||||
/** After derefinement, calculate the relations of previous fine elements
|
||||
(some of which may no longer exist) to the current leaf elements.
|
||||
Unlike for refinement, Derefine() may only be called once before this
|
||||
function so there is no MarkFineLevel(). */
|
||||
(some of which may no longer exist) to the current leaf elements. Unlike
|
||||
for refinement, Derefine() may only be called once before this function
|
||||
so there is no MarkFineLevel(). */
|
||||
const CoarseFineTransformations& GetDerefinementTransforms() const;
|
||||
|
||||
/// Free all internal data created by the above three functions.
|
||||
@@ -359,8 +361,8 @@ public:
|
||||
static void GridSfcOrdering2D(int width, int height,
|
||||
Array<int> &coords);
|
||||
|
||||
/** Return a space filling curve for a 3D rectangular grid of elements.
|
||||
The Hilbert-curve-like algorithm works well for even dimensions. For odd
|
||||
/** Return a space filling curve for a 3D rectangular grid of elements. The
|
||||
Hilbert-curve-like algorithm works well for even dimensions. For odd
|
||||
width/height/depth it tends to produce some diagonal (edge-neighbor)
|
||||
steps. Even dimensions are recommended. */
|
||||
static void GridSfcOrdering3D(int width, int height, int depth,
|
||||
@@ -428,17 +430,20 @@ public:
|
||||
/// Return the number of root elements.
|
||||
int GetNumRootElements() { return root_state.Size(); }
|
||||
|
||||
/// Return the distance of leaf 'i' from the root.
|
||||
/// Return the distance of leaf @a i from the root.
|
||||
int GetElementDepth(int i) const;
|
||||
|
||||
/** Return the size reduction compared to the root element (ignoring local
|
||||
stretching and curvature). */
|
||||
int GetElementSizeReduction(int i) const;
|
||||
|
||||
/// Return the faces and face attributes of leaf element 'i'.
|
||||
/// Return the faces and face attributes of leaf element @a i.
|
||||
void GetElementFacesAttributes(int i, Array<int> &faces,
|
||||
Array<int> &fattr) const;
|
||||
|
||||
/// Set the attribute of leaf element @a i, which is a Mesh element index.
|
||||
void SetAttribute(int i, int attr)
|
||||
{ elements[leaf_elements[i]].attribute = attr; }
|
||||
|
||||
/** I/O: Print the mesh in "MFEM NC mesh v1.0" format. If @a comments is
|
||||
non-empty, it will be printed after the first line of the file, and each
|
||||
@@ -459,8 +464,26 @@ public:
|
||||
|
||||
int PrintMemoryDetail() const;
|
||||
|
||||
typedef std::int64_t RefCoord;
|
||||
using RefCoord = std::int64_t;
|
||||
|
||||
static constexpr int MaxElemNodes =
|
||||
8; ///< Number of nodes an element can have
|
||||
static constexpr int MaxElemEdges =
|
||||
12; ///< Number of edges an element can have
|
||||
static constexpr int MaxElemFaces =
|
||||
6; ///< Number of faces an element can have
|
||||
static constexpr int MaxElemChildren =
|
||||
10; ///< Number of children an element can have
|
||||
static constexpr int MaxFaceNodes =
|
||||
4; ///< Number of faces an element can have
|
||||
|
||||
/**
|
||||
* @brief Given a node index, return the vertex index associated
|
||||
*
|
||||
* @param node
|
||||
* @return int
|
||||
*/
|
||||
int GetNodeVertex(int node) { return nodes[node].vert_index; }
|
||||
|
||||
protected: // non-public interface for the Mesh class
|
||||
|
||||
@@ -473,8 +496,8 @@ protected: // non-public interface for the Mesh class
|
||||
Face::index) after a new mesh was created from us. */
|
||||
void OnMeshUpdated(Mesh *mesh);
|
||||
|
||||
/** Delete top-level vertex coordinates if the Mesh became curved, e.g.,
|
||||
by calling Mesh::SetCurvature or otherwise setting the Nodes. */
|
||||
/** Delete top-level vertex coordinates if the Mesh became curved, e.g., by
|
||||
calling Mesh::SetCurvature or otherwise setting the Nodes. */
|
||||
void MakeTopologyOnly() { coordinates.DeleteAll(); }
|
||||
|
||||
protected: // implementation
|
||||
@@ -485,23 +508,15 @@ protected: // implementation
|
||||
int Geoms; ///< bit mask of element geometries present, see InitGeomFlags()
|
||||
bool Legacy; ///< true if the mesh was loaded from the legacy v1.1 format
|
||||
|
||||
static const int MaxElemNodes =
|
||||
8; ///< Number of nodes of an element can have
|
||||
static const int MaxElemEdges =
|
||||
12; ///< Number of edges of an element can have
|
||||
static const int MaxElemFaces =
|
||||
6; ///< Number of faces of an element can have
|
||||
static const int MaxElemChildren =
|
||||
10; ///< Number of children of an element can have
|
||||
|
||||
/** A Node can hold a vertex, an edge, or both. Elements directly point to
|
||||
their corner nodes, but edge nodes also exist and can be accessed using
|
||||
a hash-table given their two end-point node IDs. All nodes can be
|
||||
accessed in this way, with the exception of top-level vertex nodes.
|
||||
When an element is being refined, the mid-edge nodes are readily
|
||||
available with this mechanism. The new elements "sign in" to the nodes
|
||||
by increasing the reference counts of their vertices and edges. The
|
||||
parent element "signs off" its nodes by decrementing the ref counts. */
|
||||
their corner nodes, but edge nodes also exist and can be accessed using a
|
||||
hash-table given their two end-point node IDs. All nodes can be accessed
|
||||
in this way, with the exception of top-level vertex nodes. When an
|
||||
element is being refined, the mid-edge nodes are readily available with
|
||||
this mechanism. The new elements "sign in" to the nodes by increasing the
|
||||
reference counts of their vertices and edges. The parent element "signs
|
||||
off" its nodes by decrementing the ref counts. */
|
||||
struct Node : public Hashed2
|
||||
{
|
||||
char vert_refc, edge_refc;
|
||||
@@ -519,9 +534,9 @@ protected: // implementation
|
||||
};
|
||||
|
||||
/** Similarly to nodes, faces can be accessed by hashing their four vertex
|
||||
node IDs. A face knows about the one or two elements that are using it.
|
||||
A face that is not on the boundary and only has one element referencing
|
||||
it is either a master or a slave face. */
|
||||
node IDs. A face knows about the one or two elements that are using it. A
|
||||
face that is not on the boundary and only has one element referencing it
|
||||
is either a master or a slave face. */
|
||||
struct Face : public Hashed4
|
||||
{
|
||||
int attribute; ///< boundary element attribute, -1 if internal face
|
||||
@@ -539,11 +554,12 @@ protected: // implementation
|
||||
|
||||
/// Return one of elem[0] or elem[1] and make sure the other is -1.
|
||||
int GetSingleElement() const;
|
||||
int GetAttribute() const { return attribute; }
|
||||
};
|
||||
|
||||
/** This is an element in the refinement hierarchy. Each element has
|
||||
either been refined and points to its children, or is a leaf and points
|
||||
to its vertex nodes. */
|
||||
/** This is an element in the refinement hierarchy. Each element has either
|
||||
been refined and points to its children, or is a leaf and points to its
|
||||
vertex nodes. */
|
||||
struct Element
|
||||
{
|
||||
char geom; ///< Geometry::Type of the element (char for storage only)
|
||||
@@ -559,46 +575,114 @@ protected: // implementation
|
||||
int child[MaxElemChildren]; ///< 2-10 children (if ref_type != 0)
|
||||
};
|
||||
int parent; ///< parent element, -1 if this is a root element, -2 if free'd
|
||||
|
||||
Element(Geometry::Type geom, int attr);
|
||||
|
||||
Geometry::Type Geom() const { return Geometry::Type(geom); }
|
||||
bool IsLeaf() const { return !ref_type && (parent != -2); }
|
||||
int GetAttribute() const { return attribute; }
|
||||
};
|
||||
|
||||
|
||||
// primary data
|
||||
|
||||
HashTable<Node> nodes; // associative container holding all Nodes
|
||||
HashTable<Face> faces; // associative container holding all Faces
|
||||
|
||||
BlockArray<Element> elements; // storage for all Elements
|
||||
Array<int> free_element_ids; // unused element ids - indices into 'elements'
|
||||
public:
|
||||
/**
|
||||
* @brief The number of Nodes.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int GetNumNodes() const { return nodes.Size(); }
|
||||
/**
|
||||
* @brief Access a Node
|
||||
*
|
||||
* @param i Index of the node
|
||||
* @return const Node&
|
||||
*/
|
||||
const Node& GetNode(int i) const {return nodes[i]; }
|
||||
/**
|
||||
* @brief The number of faces
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int GetNumFaces() const { return faces.Size(); }
|
||||
/**
|
||||
* @brief Access a Face
|
||||
*
|
||||
* @param i Index of the face
|
||||
* @return const Face&
|
||||
*/
|
||||
const Face& GetFace(int i) const {return faces[i]; }
|
||||
/**
|
||||
* @brief The number of elements
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
int GetNumElements() const { return elements.Size(); }
|
||||
/**
|
||||
* @brief Access an Element
|
||||
*
|
||||
* @param i Index of the element
|
||||
* @return const Element&
|
||||
*/
|
||||
const Element& GetElement(int i) const { return elements[i]; }
|
||||
|
||||
/**
|
||||
* @brief Given a set of nodes defining a face, traverse the nodes structure
|
||||
* to find the nodes that make up the parent face and replace the input nodes
|
||||
* with the parent nodes. Additionally return the child index that the child
|
||||
* face would be, relative to the discovered parent face.
|
||||
* @details This method is concerned with the construction of an NCMesh
|
||||
* structure for a d-1 manifold of an existing NCMesh. It forms a key element
|
||||
* in a leaf -> root traversal of the parent ncmesh elements structure.
|
||||
*
|
||||
* @param[out] nodes The collection of nodes whose parent we are searching
|
||||
* for
|
||||
* @return int The child index corresponding to placing the face for the
|
||||
* original nodes within the face defined by the returned parent nodes. If
|
||||
* child index is -1, then the face is made up of root nodes, and nodes is
|
||||
* unchanged.
|
||||
*/
|
||||
int ParentFaceNodes(std::array<int, 4> &nodes) const;
|
||||
|
||||
/**
|
||||
* @brief Method for finding the nodes associated to a @a face
|
||||
* @return Nodes making up the face
|
||||
*/
|
||||
std::array<int, 4> FindFaceNodes(int face) const;
|
||||
std::array<int, 4> FindFaceNodes(const Face &fa) const;
|
||||
/**
|
||||
* @brief Backwards compatible method for finding the @a node associated to a
|
||||
* @a face
|
||||
*/
|
||||
MFEM_DEPRECATED void FindFaceNodes(int face, int node[4]) const;
|
||||
protected:
|
||||
|
||||
/** Initial traversal state (~ element orientation) for each root element
|
||||
NOTE: M = root_state.Size() is the number of root elements.
|
||||
NOTE: the first M items of 'elements' is the coarse mesh. */
|
||||
NOTE: M = root_state.Size() is the number of root elements. NOTE: the
|
||||
first M items of 'elements' is the coarse mesh. */
|
||||
Array<int> root_state;
|
||||
|
||||
/** Coordinates of top-level vertices (organized as triples). If empty,
|
||||
the Mesh is curved (Nodes != NULL) and NCMesh is topology-only. */
|
||||
/** Coordinates of top-level vertices (organized as triples). If empty, the
|
||||
Mesh is curved (Nodes != NULL) and NCMesh is topology-only. */
|
||||
Array<real_t> coordinates;
|
||||
|
||||
|
||||
// secondary data
|
||||
|
||||
/** Apart from the primary data structure, which is the element/node/face
|
||||
hierarchy, there is secondary data that is derived from the primary
|
||||
data and needs to be updated when the primary data changes. Update()
|
||||
takes care of that and needs to be called after each refinement and
|
||||
hierarchy, there is secondary data that is derived from the primary data
|
||||
and needs to be updated when the primary data changes. Update() takes
|
||||
care of that and needs to be called after each refinement and
|
||||
derefinement. */
|
||||
virtual void Update();
|
||||
|
||||
// set by UpdateLeafElements, UpdateVertices and OnMeshUpdated
|
||||
int NElements, NVertices, NEdges, NFaces;
|
||||
|
||||
// NOTE: the serial code understands the bare minimum about ghost elements and
|
||||
// other ghost entities in order to be able to load parallel partial meshes
|
||||
// NOTE: the serial code understands the bare minimum about ghost elements
|
||||
// and other ghost entities in order to be able to load parallel partial
|
||||
// meshes
|
||||
int NGhostElements, NGhostVertices, NGhostEdges, NGhostFaces;
|
||||
|
||||
Array<int> leaf_elements; ///< finest elements, in Mesh ordering (+ ghosts)
|
||||
@@ -623,19 +707,19 @@ protected: // implementation
|
||||
We must be careful to:
|
||||
1. Stay compatible with the conforming code, which expects top-level
|
||||
(original) vertices to be indexed first, otherwise GridFunctions
|
||||
defined on a conforming mesh would no longer be valid when the
|
||||
mesh is converted to an NC mesh.
|
||||
defined on a conforming mesh would no longer be valid when the mesh is
|
||||
converted to an NC mesh.
|
||||
|
||||
2. Make sure serial NCMesh is compatible with the parallel ParNCMesh,
|
||||
so it is possible to read parallel partial solutions in serial code
|
||||
2. Make sure serial NCMesh is compatible with the parallel ParNCMesh, so
|
||||
it is possible to read parallel partial solutions in serial code
|
||||
(e.g., serial GLVis). This means handling ghost elements, if present.
|
||||
|
||||
3. Assign vertices in a globally consistent order for parallel meshes:
|
||||
if two vertices i,j are shared by two ranks r1,r2, and i<j on r1,
|
||||
then i<j on r2 as well. This is true for top-level vertices but also
|
||||
for the remaining shared vertices thanks to the globally consistent
|
||||
SFC ordering of the leaf elements. This property reduces communication
|
||||
and simplifies ParNCMesh. */
|
||||
3. Assign vertices in a globally consistent order for parallel meshes: if
|
||||
two vertices i,j are shared by two ranks r1,r2, and i<j on r1, then
|
||||
i<j on r2 as well. This is true for top-level vertices but also for
|
||||
the remaining shared vertices thanks to the globally consistent SFC
|
||||
ordering of the leaf elements. This property reduces communication and
|
||||
simplifies ParNCMesh. */
|
||||
void UpdateVertices(); ///< update Vertex::index and vertex_nodeId
|
||||
|
||||
/** Collect the leaf elements in leaf_elements, and the ghost elements in
|
||||
@@ -646,8 +730,8 @@ protected: // implementation
|
||||
int &counter);
|
||||
|
||||
/** Try to find a space-filling curve friendly orientation of the root
|
||||
elements: set 'root_state' based on the ordering of coarse elements.
|
||||
Note that the coarse mesh itself must be ordered as an SFC by e.g.
|
||||
elements: set 'root_state' based on the ordering of coarse elements. Note
|
||||
that the coarse mesh itself must be ordered as an SFC by e.g.
|
||||
Mesh::GetGeckoElementOrdering. */
|
||||
void InitRootState(int root_count);
|
||||
|
||||
@@ -667,7 +751,6 @@ protected: // implementation
|
||||
/// Return true if the Element @a el is a ghost element.
|
||||
bool IsGhost(const Element &el) const { return el.rank != MyRank; }
|
||||
|
||||
|
||||
// refinement/derefinement
|
||||
|
||||
Array<Refinement> ref_stack; ///< stack of scheduled refinements (temporary)
|
||||
@@ -676,8 +759,8 @@ protected: // implementation
|
||||
|
||||
Table derefinements; ///< possible derefinements, see GetDerefinementTable
|
||||
|
||||
/** Refine the element @a elem with the refinement @a ref_type
|
||||
(c.f. Refinement::enum) */
|
||||
/** Refine the element @a elem with the refinement @a ref_type (c.f.
|
||||
Refinement::enum) */
|
||||
void RefineElement(int elem, char ref_type);
|
||||
|
||||
/// Derefine the element @a elem, does nothing on leaf elements.
|
||||
@@ -695,6 +778,7 @@ protected: // implementation
|
||||
}
|
||||
return elements.Append(el);
|
||||
}
|
||||
int AddElement(Geometry::Type geom, int attr) { return AddElement(Element(geom,attr)); }
|
||||
|
||||
// Free the element with index @a id.
|
||||
void FreeElement(int id)
|
||||
@@ -826,6 +910,11 @@ protected: // implementation
|
||||
|
||||
int GetMidFaceNode(int en1, int en2, int en3, int en4);
|
||||
|
||||
/**
|
||||
* @brief Add references to all nodes, edges and faces of the element
|
||||
*
|
||||
* @param elem index into elements
|
||||
*/
|
||||
void ReferenceElement(int elem);
|
||||
void UnreferenceElement(int elem, Array<int> &elemFaces);
|
||||
|
||||
@@ -882,28 +971,28 @@ protected: // implementation
|
||||
|
||||
// neighbors / element_vertex table
|
||||
|
||||
/** Return all vertex-, edge- and face-neighbors of a set of elements.
|
||||
The neighbors are returned as a list (neighbors != NULL), as a set
|
||||
/** Return all vertex-, edge- and face-neighbors of a set of elements. The
|
||||
neighbors are returned as a list (neighbors != NULL), as a set
|
||||
(neighbor_set != NULL), or both. The sizes of the set arrays must match
|
||||
that of leaf_elements. The function is intended to be used for large
|
||||
sets of elements and its complexity is linear in the number of leaf
|
||||
elements in the mesh. */
|
||||
that of leaf_elements. The function is intended to be used for large sets
|
||||
of elements and its complexity is linear in the number of leaf elements
|
||||
in the mesh. */
|
||||
void FindSetNeighbors(const Array<char> &elem_set,
|
||||
Array<int> *neighbors, /* append */
|
||||
Array<char> *neighbor_set = NULL);
|
||||
|
||||
/** Return all vertex-, edge- and face-neighbors of a single element.
|
||||
You can limit the number of elements being checked using 'search_set'.
|
||||
The complexity of the function is linear in the size of the search set.*/
|
||||
/** Return all vertex-, edge- and face-neighbors of a single element. You can
|
||||
limit the number of elements being checked using 'search_set'. The
|
||||
complexity of the function is linear in the size of the search set.*/
|
||||
void FindNeighbors(int elem,
|
||||
Array<int> &neighbors, /* append */
|
||||
const Array<int> *search_set = NULL);
|
||||
|
||||
/** Expand a set of elements by all vertex-, edge- and face-neighbors.
|
||||
The output array 'expanded' will contain all items from 'elems'
|
||||
(provided they are in 'search_set') plus their neighbors. The neighbor
|
||||
search can be limited to the optional search set. The complexity is
|
||||
linear in the sum of the sizes of 'elems' and 'search_set'. */
|
||||
/** Expand a set of elements by all vertex-, edge- and face-neighbors. The
|
||||
output array 'expanded' will contain all items from 'elems' (provided
|
||||
they are in 'search_set') plus their neighbors. The neighbor search can
|
||||
be limited to the optional search set. The complexity is linear in the
|
||||
sum of the sizes of 'elems' and 'search_set'. */
|
||||
void NeighborExpand(const Array<int> &elems,
|
||||
Array<int> &expanded,
|
||||
const Array<int> *search_set = NULL);
|
||||
@@ -981,18 +1070,17 @@ protected: // implementation
|
||||
/** @brief The PointMatrix stores the coordinates of the slave face using the
|
||||
master face coordinate as reference.
|
||||
|
||||
In 2D, the point matrix has the orientation of the parent
|
||||
edge, so its columns need to be flipped when applying it, see
|
||||
In 2D, the point matrix has the orientation of the parent edge, so its
|
||||
columns need to be flipped when applying it, see
|
||||
ApplyLocalSlaveTransformation.
|
||||
|
||||
In 3D, the orientation part of Elem2Inf is encoded in the point
|
||||
matrix.
|
||||
In 3D, the orientation part of Elem2Inf is encoded in the point matrix.
|
||||
|
||||
The following transformation gives the relation between the
|
||||
reference quad face coordinates (xi, eta) in [0,1]^2, and the fine quad
|
||||
face coordinates (x, y):
|
||||
x = a0*(1-xi)*(1-eta) + a1*xi*(1-eta) + a2*xi*eta + a3*(1-xi)*eta
|
||||
y = b0*(1-xi)*(1-eta) + b1*xi*(1-eta) + b2*xi*eta + b3*(1-xi)*eta
|
||||
The following transformation gives the relation between the reference
|
||||
quad face coordinates (xi, eta) in [0,1]^2, and the fine quad face
|
||||
coordinates (x, y):
|
||||
x = a0*(1-xi)*(1-eta) + a1*xi*(1-eta) + a2*xi*eta + a3*(1-xi)*eta
|
||||
y = b0*(1-xi)*(1-eta) + b1*xi*(1-eta) + b2*xi*eta + b3*(1-xi)*eta
|
||||
*/
|
||||
struct PointMatrix
|
||||
{
|
||||
@@ -1054,7 +1142,7 @@ protected: // implementation
|
||||
void GetPointMatrix(Geometry::Type geom, const char* ref_path,
|
||||
DenseMatrix& matrix) const;
|
||||
|
||||
typedef std::map<std::string, int> RefPathMap;
|
||||
using RefPathMap = std::map<std::string, int>;
|
||||
|
||||
void TraverseRefinements(int elem, int coarse_index,
|
||||
std::string &ref_path, RefPathMap &map) const;
|
||||
@@ -1085,15 +1173,15 @@ protected: // implementation
|
||||
|
||||
int GetEdgeMaster(int node) const;
|
||||
|
||||
void FindFaceNodes(int face, int node[4]) const;
|
||||
|
||||
/**
|
||||
* @brief Return the number of splits of this edge that have occurred in the
|
||||
* NCMesh. If zero, this means the segment is not the master of any other segments.
|
||||
* NCMesh. If zero, this means the segment is not the master of any other
|
||||
* segments.
|
||||
*
|
||||
* @param vn1 The first vertex making up the segment
|
||||
* @param vn2 The second vertex making up the segment
|
||||
* @return int The depth of splits of this segment that are present in the mesh.
|
||||
* @return int The depth of splits of this segment that are present in the
|
||||
* mesh.
|
||||
*/
|
||||
int EdgeSplitLevel(int vn1, int vn2) const;
|
||||
/**
|
||||
@@ -1104,13 +1192,14 @@ protected: // implementation
|
||||
* @param vn1 The first vertex making up the triangle
|
||||
* @param vn2 The second vertex making up the triangle
|
||||
* @param vn3 The third vertex making up the triangle
|
||||
* @return int The depth of splits of this triangle that are present in the mesh.
|
||||
* @return int The depth of splits of this triangle that are present in the
|
||||
* mesh.
|
||||
*/
|
||||
int TriFaceSplitLevel(int vn1, int vn2, int vn3) const;
|
||||
/**
|
||||
* @brief Computes the number of horizontal and vertical splits of this quad
|
||||
* that have occurred in the NCMesh. If zero, this means the quad is not
|
||||
* the master of any other quad.
|
||||
* that have occurred in the NCMesh. If zero, this means the quad is not the
|
||||
* master of any other quad.
|
||||
*
|
||||
* @param vn1 The first vertex making up the quad
|
||||
* @param vn2 The second vertex making up the quad
|
||||
@@ -1123,8 +1212,8 @@ protected: // implementation
|
||||
int& h_level, int& v_level) const;
|
||||
/**
|
||||
* @brief Returns the total number of splits of this quad that have occurred
|
||||
* in the NCMesh. If zero, this means the quad is not
|
||||
* the master of any other quad.
|
||||
* in the NCMesh. If zero, this means the quad is not the master of any other
|
||||
* quad.
|
||||
* @details This is a convenience wrapper that sums the horizontal and
|
||||
* vertical levels from the full method.
|
||||
*
|
||||
@@ -1141,6 +1230,17 @@ protected: // implementation
|
||||
void CountSplits(int elem, int splits[3]) const;
|
||||
void GetLimitRefinements(Array<Refinement> &refinements, int max_level);
|
||||
|
||||
// Checker helpers
|
||||
|
||||
static void CheckSupportedGeom(Geometry::Type geom)
|
||||
{
|
||||
MFEM_VERIFY(geom == Geometry::SEGMENT ||
|
||||
geom == Geometry::TRIANGLE || geom == Geometry::SQUARE ||
|
||||
geom == Geometry::CUBE || geom == Geometry::PRISM ||
|
||||
geom == Geometry::PYRAMID || geom == Geometry::TETRAHEDRON,
|
||||
"Element type " << geom << " is not supported by NCMesh.");
|
||||
}
|
||||
|
||||
|
||||
// I/O
|
||||
|
||||
@@ -1149,8 +1249,8 @@ protected: // implementation
|
||||
/// Load the vertex parent hierarchy from a mesh file.
|
||||
void LoadVertexParents(std::istream &input);
|
||||
|
||||
/** Print the "boundary" section of the mesh file.
|
||||
If out == NULL, only return the number of boundary elements. */
|
||||
/** Print the "boundary" section of the mesh file. If out == NULL, only
|
||||
return the number of boundary elements. */
|
||||
int PrintBoundary(std::ostream *out) const;
|
||||
/// Load the "boundary" section of the mesh file.
|
||||
void LoadBoundary(std::istream &input);
|
||||
@@ -1185,6 +1285,7 @@ protected: // implementation
|
||||
|
||||
bool initialized;
|
||||
GeomInfo() : initialized(false) {}
|
||||
GeomInfo(Geometry::Type geom) : GeomInfo() { InitGeom(geom); }
|
||||
void InitGeom(Geometry::Type geom);
|
||||
};
|
||||
|
||||
@@ -1199,6 +1300,8 @@ public:
|
||||
friend class ParNCMesh; // for ParNCMesh::ElementSet
|
||||
friend struct MatrixMap;
|
||||
friend struct PointMatrixHash;
|
||||
friend class NCSubMesh; // for faces, nodes
|
||||
friend class ParNCSubMesh; // for faces, nodes
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+65
-68
@@ -9,14 +9,13 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_NCMESH_TABLES
|
||||
#define MFEM_NCMESH_TABLES
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
namespace // make everything static
|
||||
{
|
||||
|
||||
const int ref_type_num_children[8] = { 0, 2, 2, 4, 2, 4, 4, 8 };
|
||||
|
||||
static constexpr int ref_type_num_children[8] = { 0, 2, 2, 4, 2, 4, 4, 8 };
|
||||
|
||||
// derefinement tables
|
||||
// The first n numbers in each line are the refined elements that contain
|
||||
@@ -24,14 +23,14 @@ const int ref_type_num_children[8] = { 0, 2, 2, 4, 2, 4, 4, 8 };
|
||||
// are the refined elements that contain the faces attributes of the parent
|
||||
// element.
|
||||
|
||||
const int quad_deref_table[3][4 + 4] =
|
||||
static constexpr int quad_deref_table[3][4 + 4] =
|
||||
{
|
||||
{ 0, 1, 1, 0, /**/ 1, 1, 0, 0 }, // 1 - X
|
||||
{ 0, 0, 1, 1, /**/ 0, 0, 1, 1 }, // 2 - Y
|
||||
{ 0, 1, 2, 3, /**/ 1, 1, 3, 3 } // 3 - iso
|
||||
};
|
||||
|
||||
const int hex_deref_table[7][8 + 6] =
|
||||
static constexpr int hex_deref_table[7][8 + 6] =
|
||||
{
|
||||
{ 0, 1, 1, 0, 0, 1, 1, 0, /**/ 1, 1, 1, 0, 0, 0 }, // 1 - X
|
||||
{ 0, 0, 1, 1, 0, 0, 1, 1, /**/ 0, 0, 0, 1, 1, 1 }, // 2 - Y
|
||||
@@ -42,7 +41,7 @@ const int hex_deref_table[7][8 + 6] =
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, /**/ 1, 1, 1, 7, 7, 7 } // 7 - iso
|
||||
};
|
||||
|
||||
const int prism_deref_table[7][6 + 5] =
|
||||
static constexpr int prism_deref_table[7][6 + 5] =
|
||||
{
|
||||
{-1,-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 1
|
||||
{-1,-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 2
|
||||
@@ -53,7 +52,7 @@ const int prism_deref_table[7][6 + 5] =
|
||||
{ 0, 1, 2, 4, 5, 6, /**/ 0, 5, 0, 5, 0 } // 7 - iso
|
||||
};
|
||||
|
||||
const int pyramid_deref_table[7][5 + 5] =
|
||||
static constexpr int pyramid_deref_table[7][5 + 5] =
|
||||
{
|
||||
{-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 1
|
||||
{-1,-1,-1,-1,-1, /**/ -1,-1,-1,-1,-1 }, // 2
|
||||
@@ -66,19 +65,19 @@ const int pyramid_deref_table[7][5 + 5] =
|
||||
|
||||
// child ordering tables
|
||||
|
||||
const char quad_hilbert_child_order[8][4] =
|
||||
static constexpr char quad_hilbert_child_order[8][4] =
|
||||
{
|
||||
{0,1,2,3}, {0,3,2,1}, {1,2,3,0}, {1,0,3,2},
|
||||
{2,3,0,1}, {2,1,0,3}, {3,0,1,2}, {3,2,1,0}
|
||||
};
|
||||
|
||||
const char quad_hilbert_child_state[8][4] =
|
||||
static constexpr char quad_hilbert_child_state[8][4] =
|
||||
{
|
||||
{1,0,0,5}, {0,1,1,4}, {3,2,2,7}, {2,3,3,6},
|
||||
{5,4,4,1}, {4,5,5,0}, {7,6,6,3}, {6,7,7,2}
|
||||
};
|
||||
|
||||
const char hex_hilbert_child_order[24][8] =
|
||||
static constexpr char hex_hilbert_child_order[24][8] =
|
||||
{
|
||||
{0,1,2,3,7,6,5,4}, {0,3,7,4,5,6,2,1}, {0,4,5,1,2,6,7,3},
|
||||
{1,0,3,2,6,7,4,5}, {1,2,6,5,4,7,3,0}, {1,5,4,0,3,7,6,2},
|
||||
@@ -90,7 +89,7 @@ const char hex_hilbert_child_order[24][8] =
|
||||
{7,3,2,6,5,1,0,4}, {7,4,0,3,2,1,5,6}, {7,6,5,4,0,1,2,3}
|
||||
};
|
||||
|
||||
const char hex_hilbert_child_state[24][8] =
|
||||
static constexpr char hex_hilbert_child_state[24][8] =
|
||||
{
|
||||
{1,2,2,7,7,21,21,17}, {2,0,0,22,22,16,16,8}, {0,1,1,15,15,6,6,23},
|
||||
{4,5,5,10,10,18,18,14}, {5,3,3,19,19,13,13,11}, {3,4,4,12,12,9,9,20},
|
||||
@@ -104,27 +103,26 @@ const char hex_hilbert_child_state[24][8] =
|
||||
|
||||
|
||||
// child/parent reference domain transforms
|
||||
|
||||
typedef NCMesh::RefCoord RefCoord;
|
||||
using RefCoord = NCMesh::RefCoord;
|
||||
|
||||
// reference domain coordinates as fixed point numbers
|
||||
const RefCoord T_HALF = (1ll << 59);
|
||||
const RefCoord T_ONE = (1ll << 60);
|
||||
const RefCoord T_TWO = (1ll << 61);
|
||||
static constexpr RefCoord T_HALF = (1ll << 59);
|
||||
static constexpr RefCoord T_ONE = (1ll << 60);
|
||||
static constexpr RefCoord T_TWO = (1ll << 61);
|
||||
|
||||
// (scaling factors have a different fixed point multiplier)
|
||||
const RefCoord S_HALF = 1;
|
||||
const RefCoord S_ONE = 2;
|
||||
const RefCoord S_TWO = 4;
|
||||
static constexpr RefCoord S_HALF = 1;
|
||||
static constexpr RefCoord S_ONE = 2;
|
||||
static constexpr RefCoord S_TWO = 4;
|
||||
|
||||
const RefCoord tri_corners[3][3] =
|
||||
static constexpr RefCoord tri_corners[3][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
{ 0, T_ONE, 0}
|
||||
};
|
||||
|
||||
const RefCoord quad_corners[4][3] =
|
||||
static constexpr RefCoord quad_corners[4][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
@@ -132,7 +130,7 @@ const RefCoord quad_corners[4][3] =
|
||||
{ 0, T_ONE, 0}
|
||||
};
|
||||
|
||||
const RefCoord hex_corners[8][3] =
|
||||
static constexpr RefCoord hex_corners[8][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
@@ -144,7 +142,7 @@ const RefCoord hex_corners[8][3] =
|
||||
{ 0, T_ONE, T_ONE}
|
||||
};
|
||||
|
||||
const RefCoord prism_corners[6][3] =
|
||||
static constexpr RefCoord prism_corners[6][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
@@ -154,7 +152,7 @@ const RefCoord prism_corners[6][3] =
|
||||
{ 0, T_ONE, T_ONE}
|
||||
};
|
||||
|
||||
const RefCoord pyramid_corners[5][3] =
|
||||
static constexpr RefCoord pyramid_corners[5][3] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{T_ONE, 0, 0},
|
||||
@@ -164,7 +162,7 @@ const RefCoord pyramid_corners[5][3] =
|
||||
};
|
||||
|
||||
typedef RefCoord RefPoint[3];
|
||||
const RefPoint* geom_corners[8] =
|
||||
static const RefPoint* geom_corners[8] =
|
||||
{
|
||||
NULL, // point
|
||||
NULL, // segment
|
||||
@@ -190,31 +188,31 @@ struct RefTrf
|
||||
}
|
||||
};
|
||||
|
||||
const RefTrf quad_parent_rt1[2] =
|
||||
static constexpr RefTrf quad_parent_rt1[2] =
|
||||
{
|
||||
{ {S_HALF, S_ONE, 0}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_ONE, 0}, {T_HALF, 0, 0} }
|
||||
};
|
||||
|
||||
const RefTrf quad_child_rt1[2] =
|
||||
static constexpr RefTrf quad_child_rt1[2] =
|
||||
{
|
||||
{ {S_TWO, S_ONE, 0}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_ONE, 0}, {-T_ONE, 0, 0} }
|
||||
};
|
||||
|
||||
const RefTrf quad_parent_rt2[2] =
|
||||
static constexpr RefTrf quad_parent_rt2[2] =
|
||||
{
|
||||
{ {S_ONE, S_HALF, 0}, {0, 0, 0} },
|
||||
{ {S_ONE, S_HALF, 0}, {0, T_HALF, 0} }
|
||||
};
|
||||
|
||||
const RefTrf quad_child_rt2[2] =
|
||||
static constexpr RefTrf quad_child_rt2[2] =
|
||||
{
|
||||
{ {S_ONE, S_TWO, 0}, {0, 0, 0} },
|
||||
{ {S_ONE, S_TWO, 0}, {0, -T_ONE, 0} }
|
||||
};
|
||||
|
||||
const RefTrf quad_parent_rt3[4] =
|
||||
static constexpr RefTrf quad_parent_rt3[4] =
|
||||
{
|
||||
{ {S_HALF, S_HALF, 0}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_HALF, 0}, {T_HALF, 0, 0} },
|
||||
@@ -222,7 +220,7 @@ const RefTrf quad_parent_rt3[4] =
|
||||
{ {S_HALF, S_HALF, 0}, { 0, T_HALF, 0} }
|
||||
};
|
||||
|
||||
const RefTrf quad_child_rt3[4] =
|
||||
static constexpr RefTrf quad_child_rt3[4] =
|
||||
{
|
||||
{ {S_TWO, S_TWO, 0}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_TWO, 0}, {-T_ONE, 0, 0} },
|
||||
@@ -230,7 +228,7 @@ const RefTrf quad_child_rt3[4] =
|
||||
{ {S_TWO, S_TWO, 0}, { 0, -T_ONE, 0} }
|
||||
};
|
||||
|
||||
const RefTrf* quad_parent[4] =
|
||||
static const RefTrf* quad_parent[4] =
|
||||
{
|
||||
NULL,
|
||||
quad_parent_rt1,
|
||||
@@ -238,7 +236,7 @@ const RefTrf* quad_parent[4] =
|
||||
quad_parent_rt3
|
||||
};
|
||||
|
||||
const RefTrf* quad_child[4] =
|
||||
static const RefTrf* quad_child[4] =
|
||||
{
|
||||
NULL,
|
||||
quad_child_rt1,
|
||||
@@ -246,31 +244,31 @@ const RefTrf* quad_child[4] =
|
||||
quad_child_rt3
|
||||
};
|
||||
|
||||
const RefTrf hex_parent_rt1[2] =
|
||||
static constexpr RefTrf hex_parent_rt1[2] =
|
||||
{
|
||||
{ {S_HALF, S_ONE, S_ONE}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_ONE, S_ONE}, {T_HALF, 0, 0} }
|
||||
};
|
||||
|
||||
const RefTrf hex_child_rt1[2] =
|
||||
static constexpr RefTrf hex_child_rt1[2] =
|
||||
{
|
||||
{ {S_TWO, S_ONE, S_ONE}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_ONE, S_ONE}, {-T_ONE, 0, 0} }
|
||||
};
|
||||
|
||||
const RefTrf hex_parent_rt2[2] =
|
||||
static constexpr RefTrf hex_parent_rt2[2] =
|
||||
{
|
||||
{ {S_ONE, S_HALF, S_ONE}, {0, 0, 0} },
|
||||
{ {S_ONE, S_HALF, S_ONE}, {0, T_HALF, 0} }
|
||||
};
|
||||
|
||||
const RefTrf hex_child_rt2[2] =
|
||||
static constexpr RefTrf hex_child_rt2[2] =
|
||||
{
|
||||
{ {S_ONE, S_TWO, S_ONE}, {0, 0, 0} },
|
||||
{ {S_ONE, S_TWO, S_ONE}, {0, -T_ONE, 0} }
|
||||
};
|
||||
|
||||
const RefTrf hex_parent_rt3[4] =
|
||||
static constexpr RefTrf hex_parent_rt3[4] =
|
||||
{
|
||||
{ {S_HALF, S_HALF, S_ONE}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_HALF, S_ONE}, {T_HALF, 0, 0} },
|
||||
@@ -278,7 +276,7 @@ const RefTrf hex_parent_rt3[4] =
|
||||
{ {S_HALF, S_HALF, S_ONE}, { 0, T_HALF, 0} }
|
||||
};
|
||||
|
||||
const RefTrf hex_child_rt3[4] =
|
||||
static constexpr RefTrf hex_child_rt3[4] =
|
||||
{
|
||||
{ {S_TWO, S_TWO, S_ONE}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_TWO, S_ONE}, {-T_ONE, 0, 0} },
|
||||
@@ -286,19 +284,19 @@ const RefTrf hex_child_rt3[4] =
|
||||
{ {S_TWO, S_TWO, S_ONE}, { 0, -T_ONE, 0} }
|
||||
};
|
||||
|
||||
const RefTrf hex_parent_rt4[2] =
|
||||
static constexpr RefTrf hex_parent_rt4[2] =
|
||||
{
|
||||
{ {S_ONE, S_ONE, S_HALF}, {0, 0, 0} },
|
||||
{ {S_ONE, S_ONE, S_HALF}, {0, 0, T_HALF} }
|
||||
};
|
||||
|
||||
const RefTrf hex_child_rt4[2] =
|
||||
static constexpr RefTrf hex_child_rt4[2] =
|
||||
{
|
||||
{ {S_ONE, S_ONE, S_TWO}, {0, 0, 0} },
|
||||
{ {S_ONE, S_ONE, S_TWO}, {0, 0, -T_ONE} }
|
||||
};
|
||||
|
||||
const RefTrf hex_parent_rt5[4] =
|
||||
static constexpr RefTrf hex_parent_rt5[4] =
|
||||
{
|
||||
{ {S_HALF, S_ONE, S_HALF}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_ONE, S_HALF}, {T_HALF, 0, 0} },
|
||||
@@ -306,7 +304,7 @@ const RefTrf hex_parent_rt5[4] =
|
||||
{ {S_HALF, S_ONE, S_HALF}, { 0, 0, T_HALF} }
|
||||
};
|
||||
|
||||
const RefTrf hex_child_rt5[4] =
|
||||
static constexpr RefTrf hex_child_rt5[4] =
|
||||
{
|
||||
{ {S_TWO, S_ONE, S_TWO}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_ONE, S_TWO}, {-T_ONE, 0, 0} },
|
||||
@@ -314,7 +312,7 @@ const RefTrf hex_child_rt5[4] =
|
||||
{ {S_TWO, S_ONE, S_TWO}, { 0, 0, -T_ONE} }
|
||||
};
|
||||
|
||||
const RefTrf hex_parent_rt6[4] =
|
||||
static constexpr RefTrf hex_parent_rt6[4] =
|
||||
{
|
||||
{ {S_ONE, S_HALF, S_HALF}, {0, 0, 0} },
|
||||
{ {S_ONE, S_HALF, S_HALF}, {0, T_HALF, 0} },
|
||||
@@ -322,7 +320,7 @@ const RefTrf hex_parent_rt6[4] =
|
||||
{ {S_ONE, S_HALF, S_HALF}, {0, T_HALF, T_HALF} }
|
||||
};
|
||||
|
||||
const RefTrf hex_child_rt6[4] =
|
||||
static constexpr RefTrf hex_child_rt6[4] =
|
||||
{
|
||||
{ {S_ONE, S_TWO, S_TWO}, {0, 0, 0} },
|
||||
{ {S_ONE, S_TWO, S_TWO}, {0, -T_ONE, 0} },
|
||||
@@ -330,7 +328,7 @@ const RefTrf hex_child_rt6[4] =
|
||||
{ {S_ONE, S_TWO, S_TWO}, {0, -T_ONE, -T_ONE} }
|
||||
};
|
||||
|
||||
const RefTrf hex_parent_rt7[8] =
|
||||
static constexpr RefTrf hex_parent_rt7[8] =
|
||||
{
|
||||
{ {S_HALF, S_HALF, S_HALF}, { 0, 0, 0} },
|
||||
{ {S_HALF, S_HALF, S_HALF}, {T_HALF, 0, 0} },
|
||||
@@ -342,7 +340,7 @@ const RefTrf hex_parent_rt7[8] =
|
||||
{ {S_HALF, S_HALF, S_HALF}, { 0, T_HALF, T_HALF} }
|
||||
};
|
||||
|
||||
const RefTrf hex_child_rt7[8] =
|
||||
static constexpr RefTrf hex_child_rt7[8] =
|
||||
{
|
||||
{ {S_TWO, S_TWO, S_TWO}, { 0, 0, 0} },
|
||||
{ {S_TWO, S_TWO, S_TWO}, {-T_ONE, 0, 0} },
|
||||
@@ -354,7 +352,7 @@ const RefTrf hex_child_rt7[8] =
|
||||
{ {S_TWO, S_TWO, S_TWO}, { 0, -T_ONE, -T_ONE} }
|
||||
};
|
||||
|
||||
const RefTrf* hex_parent[8] =
|
||||
static const RefTrf* hex_parent[8] =
|
||||
{
|
||||
NULL,
|
||||
hex_parent_rt1,
|
||||
@@ -366,7 +364,7 @@ const RefTrf* hex_parent[8] =
|
||||
hex_parent_rt7
|
||||
};
|
||||
|
||||
const RefTrf* hex_child[8] =
|
||||
static const RefTrf* hex_child[8] =
|
||||
{
|
||||
NULL,
|
||||
hex_child_rt1,
|
||||
@@ -378,7 +376,7 @@ const RefTrf* hex_child[8] =
|
||||
hex_child_rt7
|
||||
};
|
||||
|
||||
const RefTrf tri_parent_rt3[4] =
|
||||
static constexpr RefTrf tri_parent_rt3[4] =
|
||||
{
|
||||
{ { S_HALF, S_HALF, 0}, { 0, 0, 0} },
|
||||
{ { S_HALF, S_HALF, 0}, {T_HALF, 0, 0} },
|
||||
@@ -386,7 +384,7 @@ const RefTrf tri_parent_rt3[4] =
|
||||
{ {-S_HALF, -S_HALF, 0}, {T_HALF, T_HALF, 0} }
|
||||
};
|
||||
|
||||
const RefTrf tri_child_rt3[4] =
|
||||
static constexpr RefTrf tri_child_rt3[4] =
|
||||
{
|
||||
{ { S_TWO, S_TWO, 0}, { 0, 0, 0} },
|
||||
{ { S_TWO, S_TWO, 0}, {-T_ONE, 0, 0} },
|
||||
@@ -394,19 +392,19 @@ const RefTrf tri_child_rt3[4] =
|
||||
{ {-S_TWO, -S_TWO, 0}, { T_ONE, T_ONE, 0} }
|
||||
};
|
||||
|
||||
const RefTrf* tri_parent[4] =
|
||||
static const RefTrf* tri_parent[4] =
|
||||
{
|
||||
NULL, NULL, NULL,
|
||||
tri_parent_rt3
|
||||
};
|
||||
|
||||
const RefTrf* tri_child[4] =
|
||||
static const RefTrf* tri_child[4] =
|
||||
{
|
||||
NULL, NULL, NULL,
|
||||
tri_child_rt3
|
||||
};
|
||||
|
||||
const RefTrf prism_parent_rt3[4] =
|
||||
static constexpr RefTrf prism_parent_rt3[4] =
|
||||
{
|
||||
{ { S_HALF, S_HALF, S_ONE}, { 0, 0, 0} },
|
||||
{ { S_HALF, S_HALF, S_ONE}, {T_HALF, 0, 0} },
|
||||
@@ -414,7 +412,7 @@ const RefTrf prism_parent_rt3[4] =
|
||||
{ {-S_HALF, -S_HALF, S_ONE}, {T_HALF, T_HALF, 0} }
|
||||
};
|
||||
|
||||
const RefTrf prism_child_rt3[4] =
|
||||
static constexpr RefTrf prism_child_rt3[4] =
|
||||
{
|
||||
{ { S_TWO, S_TWO, S_ONE}, { 0, 0, 0} },
|
||||
{ { S_TWO, S_TWO, S_ONE}, {-T_ONE, 0, 0} },
|
||||
@@ -422,19 +420,19 @@ const RefTrf prism_child_rt3[4] =
|
||||
{ {-S_TWO, -S_TWO, S_ONE}, { T_ONE, T_ONE, 0} }
|
||||
};
|
||||
|
||||
const RefTrf prism_parent_rt4[2] =
|
||||
static constexpr RefTrf prism_parent_rt4[2] =
|
||||
{
|
||||
{ {S_ONE, S_ONE, S_HALF}, {0, 0, 0} },
|
||||
{ {S_ONE, S_ONE, S_HALF}, {0, 0, T_HALF} }
|
||||
};
|
||||
|
||||
const RefTrf prism_child_rt4[2] =
|
||||
static constexpr RefTrf prism_child_rt4[2] =
|
||||
{
|
||||
{ {S_ONE, S_ONE, S_TWO}, {0, 0, 0} },
|
||||
{ {S_ONE, S_ONE, S_TWO}, {0, 0, -T_ONE} }
|
||||
};
|
||||
|
||||
const RefTrf prism_parent_rt7[8] =
|
||||
static constexpr RefTrf prism_parent_rt7[8] =
|
||||
{
|
||||
{ { S_HALF, S_HALF, S_HALF}, { 0, 0, 0} },
|
||||
{ { S_HALF, S_HALF, S_HALF}, {T_HALF, 0, 0} },
|
||||
@@ -446,7 +444,7 @@ const RefTrf prism_parent_rt7[8] =
|
||||
{ {-S_HALF, -S_HALF, S_HALF}, {T_HALF, T_HALF, T_HALF} }
|
||||
};
|
||||
|
||||
const RefTrf prism_child_rt7[8] =
|
||||
static constexpr RefTrf prism_child_rt7[8] =
|
||||
{
|
||||
{ { S_TWO, S_TWO, S_TWO}, { 0, 0, 0} },
|
||||
{ { S_TWO, S_TWO, S_TWO}, {-T_ONE, 0, 0} },
|
||||
@@ -458,7 +456,7 @@ const RefTrf prism_child_rt7[8] =
|
||||
{ {-S_TWO, -S_TWO, S_TWO}, { T_ONE, T_ONE, -T_ONE} }
|
||||
};
|
||||
|
||||
const RefTrf* prism_parent[8] =
|
||||
static const RefTrf* prism_parent[8] =
|
||||
{
|
||||
NULL, NULL, NULL,
|
||||
prism_parent_rt3,
|
||||
@@ -467,7 +465,7 @@ const RefTrf* prism_parent[8] =
|
||||
prism_parent_rt7
|
||||
};
|
||||
|
||||
const RefTrf* prism_child[8] =
|
||||
static const RefTrf* prism_child[8] =
|
||||
{
|
||||
NULL, NULL, NULL,
|
||||
prism_child_rt3,
|
||||
@@ -476,7 +474,7 @@ const RefTrf* prism_child[8] =
|
||||
prism_child_rt7
|
||||
};
|
||||
|
||||
const RefTrf** geom_parent[7] =
|
||||
static const RefTrf** geom_parent[7] =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
@@ -487,7 +485,7 @@ const RefTrf** geom_parent[7] =
|
||||
prism_parent
|
||||
};
|
||||
|
||||
const RefTrf** geom_child[7] =
|
||||
static const RefTrf** geom_child[7] =
|
||||
{
|
||||
NULL,
|
||||
NULL,
|
||||
@@ -498,7 +496,6 @@ const RefTrf** geom_child[7] =
|
||||
prism_child
|
||||
};
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_NCMESH_TABLES
|
||||
+65
-38
@@ -109,8 +109,8 @@ protected:
|
||||
// Determine sedge_ledge and sface_lface.
|
||||
void FinalizeParTopo();
|
||||
|
||||
// Mark all tets to ensure consistency across MPI tasks; also mark the
|
||||
// shared and boundary triangle faces using the consistently marked tets.
|
||||
// Mark all tets to ensure consistency across MPI tasks; also mark the shared
|
||||
// and boundary triangle faces using the consistently marked tets.
|
||||
void MarkTetMeshForRefinement(const DSTable &v_to_v) override;
|
||||
|
||||
/// Return a number(0-1) identifying how the given edge has been split
|
||||
@@ -337,12 +337,12 @@ public:
|
||||
have_face_nbr_data(false), pncmesh(NULL) { }
|
||||
|
||||
/// Create a parallel mesh by partitioning a serial Mesh.
|
||||
/** The mesh is partitioned automatically or using external partitioning
|
||||
data (the optional parameter 'partitioning_[i]' contains the desired MPI
|
||||
rank for element 'i'). Automatic partitioning uses METIS for conforming
|
||||
meshes and quick space-filling curve equipartitioning for nonconforming
|
||||
meshes (elements of nonconforming meshes should ideally be ordered as a
|
||||
sequence of face-neighbors). */
|
||||
/** The mesh is partitioned automatically or using external partitioning data
|
||||
(the optional parameter 'partitioning_[i]' contains the desired MPI rank
|
||||
for element 'i'). Automatic partitioning uses METIS for conforming meshes
|
||||
and quick space-filling curve equipartitioning for nonconforming meshes
|
||||
(elements of nonconforming meshes should ideally be ordered as a sequence
|
||||
of face-neighbors). */
|
||||
ParMesh(MPI_Comm comm, Mesh &mesh, const int *partitioning_ = nullptr,
|
||||
int part_method = 1);
|
||||
|
||||
@@ -446,11 +446,42 @@ public:
|
||||
int GroupNTriangles(int group) const { return group_stria.RowSize(group-1); }
|
||||
int GroupNQuadrilaterals(int group) const { return group_squad.RowSize(group-1); }
|
||||
|
||||
/**
|
||||
* @brief Accessors for entities within a shared group structure.
|
||||
* @details For all vertex/edge/face the two argument version returns the
|
||||
* local index, for those entities with an orientation. The two out parameter
|
||||
* version additionally returns an orientation to use in manipulating the
|
||||
* entity.
|
||||
*
|
||||
* @param group The communicator group's indices
|
||||
* @param i the index within the group
|
||||
* @return int The local index of the entity
|
||||
*/
|
||||
int GroupVertex(int group, int i) const
|
||||
{ return svert_lvert[group_svert.GetRow(group-1)[i]]; }
|
||||
void GroupEdge(int group, int i, int &edge, int &o) const;
|
||||
void GroupTriangle(int group, int i, int &face, int &o) const;
|
||||
void GroupQuadrilateral(int group, int i, int &face, int &o) const;
|
||||
int GroupEdge(int group, int i) const
|
||||
{
|
||||
int e, o;
|
||||
GroupEdge(group, i, e, o);
|
||||
return e;
|
||||
}
|
||||
int GroupTriangle(int group, int i) const
|
||||
{
|
||||
int f, o;
|
||||
GroupTriangle(group, i, f, o);
|
||||
return f;
|
||||
}
|
||||
int GroupQuadrilateral(int group, int i) const
|
||||
{
|
||||
int f, o;
|
||||
GroupQuadrilateral(group, i, f, o);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
///@}
|
||||
|
||||
/**
|
||||
@@ -496,18 +527,15 @@ public:
|
||||
void GenerateOffsets(int N, HYPRE_BigInt loc_sizes[],
|
||||
Array<HYPRE_BigInt> *offsets[]) const;
|
||||
|
||||
/** Return true if the face is interior or shared. In parallel, this
|
||||
method only works if the face neighbor data is exchanged. */
|
||||
inline bool FaceIsTrueInterior(int FaceNo) const { return Mesh::FaceIsTrueInterior(FaceNo); }
|
||||
|
||||
using Mesh::FaceIsTrueInterior;
|
||||
void ExchangeFaceNbrData();
|
||||
void ExchangeFaceNbrNodes();
|
||||
|
||||
void SetCurvature(int order, bool discont = false, int space_dim = -1,
|
||||
int ordering = 1) override;
|
||||
|
||||
/** Replace the internal node GridFunction with a new GridFunction defined
|
||||
on the given FiniteElementSpace. The new node coordinates are projected
|
||||
/** Replace the internal node GridFunction with a new GridFunction defined on
|
||||
the given FiniteElementSpace. The new node coordinates are projected
|
||||
(derived) from the current nodes/vertices. */
|
||||
void SetNodalFESpace(FiniteElementSpace *nfes) override;
|
||||
void SetNodalFESpace(ParFiniteElementSpace *npfes);
|
||||
@@ -571,15 +599,15 @@ public:
|
||||
IsoparametricTransformation &ElTr2,
|
||||
int mask = 31) const override;
|
||||
|
||||
/// @brief Get the FaceElementTransformations for the given shared face
|
||||
/// (edge 2D) using the shared face index @a sf. @a fill2 specify if the
|
||||
/// information for elem2 of the face should be computed or not.
|
||||
/// In the returned object, 1 and 2 refer to the local and the neighbor
|
||||
/// elements, respectively.
|
||||
/// @brief Get the FaceElementTransformations for the given shared face (edge
|
||||
/// 2D) using the shared face index @a sf. @a fill2 specify if the
|
||||
/// information for elem2 of the face should be computed or not. In the
|
||||
/// returned object, 1 and 2 refer to the local and the neighbor elements,
|
||||
/// respectively.
|
||||
///
|
||||
/// @note The returned object is owned by the class and is shared, i.e.,
|
||||
/// calling this function resets pointers obtained from previous calls.
|
||||
/// Also, the returned object should NOT be deleted by the caller.
|
||||
/// calling this function resets pointers obtained from previous calls. Also,
|
||||
/// the returned object should NOT be deleted by the caller.
|
||||
FaceElementTransformations *
|
||||
GetSharedFaceTransformations(int sf, bool fill2 = true);
|
||||
|
||||
@@ -591,15 +619,14 @@ public:
|
||||
IsoparametricTransformation &ElTr2,
|
||||
bool fill2 = true) const;
|
||||
|
||||
/// @brief Get the FaceElementTransformations for the given shared face
|
||||
/// (edge 2D) using the face index @a FaceNo. @a fill2 specify if the
|
||||
/// information for elem2 of the face should be computed or not.
|
||||
/// In the returned object, 1 and 2 refer to the local and the neighbor
|
||||
/// elements, respectively.
|
||||
/// @brief Get the FaceElementTransformations for the given shared face (edge
|
||||
/// 2D) using the face index @a FaceNo. @a fill2 specify if the information
|
||||
/// for elem2 of the face should be computed or not. In the returned object,
|
||||
/// 1 and 2 refer to the local and the neighbor elements, respectively.
|
||||
///
|
||||
/// @note The returned object is owned by the class and is shared, i.e.,
|
||||
/// calling this function resets pointers obtained from previous calls.
|
||||
/// Also, the returned object should NOT be deleted by the caller.
|
||||
/// calling this function resets pointers obtained from previous calls. Also,
|
||||
/// the returned object should NOT be deleted by the caller.
|
||||
FaceElementTransformations *
|
||||
GetSharedFaceTransformationsByLocalIndex(int FaceNo, bool fill2 = true);
|
||||
|
||||
@@ -615,8 +642,8 @@ public:
|
||||
/// neighbor.
|
||||
///
|
||||
/// @note The returned object is owned by the class and is shared, i.e.,
|
||||
/// calling this function resets pointers obtained from previous calls.
|
||||
/// Also, the returned object should NOT be deleted by the caller.
|
||||
/// calling this function resets pointers obtained from previous calls. Also,
|
||||
/// the returned object should NOT be deleted by the caller.
|
||||
ElementTransformation *GetFaceNbrElementTransformation(int FaceNo);
|
||||
|
||||
/// @brief Variant of GetFaceNbrElementTransformation using a user allocated
|
||||
@@ -637,11 +664,11 @@ public:
|
||||
/** @brief Returns the number of local faces according to the requested type,
|
||||
does not count master non-conforming faces.
|
||||
|
||||
If type==Boundary returns only the number of true boundary faces
|
||||
contrary to GetNBE() that returns all "boundary" elements which may
|
||||
include actual interior faces.
|
||||
Similarly, if type==Interior, only the true interior faces (including
|
||||
shared faces) are counted excluding all master non-conforming faces. */
|
||||
If type==Boundary returns only the number of true boundary faces contrary
|
||||
to GetNBE() that returns all "boundary" elements which may include actual
|
||||
interior faces. Similarly, if type==Interior, only the true interior
|
||||
faces (including shared faces) are counted excluding all master
|
||||
non-conforming faces. */
|
||||
int GetNFbyType(FaceType type) const override;
|
||||
|
||||
void GenerateBoundaryElements() override
|
||||
@@ -657,9 +684,9 @@ public:
|
||||
sequence of elements. Works for nonconforming meshes only. */
|
||||
void Rebalance();
|
||||
|
||||
/** Load balance a nonconforming mesh using a user-defined partition.
|
||||
Each local element 'i' is migrated to processor rank 'partition[i]',
|
||||
for 0 <= i < GetNE(). */
|
||||
/** Load balance a nonconforming mesh using a user-defined partition. Each
|
||||
local element 'i' is migrated to processor rank 'partition[i]', for 0 <=
|
||||
i < GetNE(). */
|
||||
void Rebalance(const Array<int> &partition);
|
||||
|
||||
/** Save the mesh in a parallel mesh format. If @a comments is non-empty, it
|
||||
|
||||
@@ -63,6 +63,8 @@ class FiniteElementSpace;
|
||||
*/
|
||||
class ParNCMesh : public NCMesh
|
||||
{
|
||||
protected:
|
||||
ParNCMesh() = default;
|
||||
public:
|
||||
/// Construct by partitioning a serial NCMesh.
|
||||
/** SFC partitioning is used by default. A user-specified partition can be
|
||||
@@ -252,6 +254,7 @@ public:
|
||||
protected: // interface for ParMesh
|
||||
|
||||
friend class ParMesh;
|
||||
friend class ParSubMesh;
|
||||
|
||||
/** For compatibility with conforming code in ParMesh and ParFESpace.
|
||||
Initializes shared structures in ParMesh: gtopo, shared_*, group_s*,
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright (c) 2010-2024, 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 "ncsubmesh.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include "submesh_utils.hpp"
|
||||
#include "submesh.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using namespace SubMeshUtils;
|
||||
|
||||
NCSubMesh::NCSubMesh(SubMesh& submesh, const NCMesh &parent, From from,
|
||||
const Array<int> &attributes)
|
||||
: NCMesh(), parent_(&parent)
|
||||
{
|
||||
Dim = submesh.Dimension();
|
||||
spaceDim = submesh.SpaceDimension();
|
||||
MyRank = 0;
|
||||
Iso = true;
|
||||
Legacy = false;
|
||||
|
||||
if (from == From::Domain)
|
||||
{
|
||||
SubMeshUtils::ConstructVolumeTree(*this, attributes);
|
||||
}
|
||||
else if (from == From::Boundary)
|
||||
{
|
||||
SubMeshUtils::ConstructFaceTree(*this, attributes);
|
||||
}
|
||||
|
||||
// Loop over all nodes, and reparent based on the node relations of the
|
||||
// parent
|
||||
for (int i = 0; i < parent_node_ids_.Size(); i++)
|
||||
{
|
||||
const auto &parent_node = parent.nodes[parent_node_ids_[i]];
|
||||
const int submesh_p1 = parent_to_submesh_node_ids_[parent_node.p1];
|
||||
const int submesh_p2 = parent_to_submesh_node_ids_[parent_node.p2];
|
||||
nodes.Reparent(i, submesh_p1, submesh_p2);
|
||||
}
|
||||
|
||||
nodes.UpdateUnused();
|
||||
for (int i = 0; i < elements.Size(); i++)
|
||||
{
|
||||
if (elements[i].IsLeaf())
|
||||
{
|
||||
// Register all faces
|
||||
RegisterFaces(i);
|
||||
}
|
||||
}
|
||||
|
||||
InitRootElements();
|
||||
InitRootState(root_state.Size());
|
||||
InitGeomFlags();
|
||||
Update(); // Fills in secondary information based off of elements, nodes and faces.
|
||||
|
||||
// If parent has coordinates defined, copy the relevant portion
|
||||
if (parent.coordinates.Size() > 0)
|
||||
{
|
||||
coordinates.SetSize(3*parent_node_ids_.Size());
|
||||
parent.tmp_vertex = new TmpVertex[parent.nodes.NumIds()];
|
||||
for (int n = 0; n < parent_node_ids_.Size(); n++)
|
||||
{
|
||||
std::memcpy(&coordinates[3*n], parent.CalcVertexPos(parent_node_ids_[n]),
|
||||
3*sizeof(real_t));
|
||||
}
|
||||
delete [] parent.tmp_vertex;
|
||||
}
|
||||
|
||||
// The element indexing was changed as part of generation of leaf elements.
|
||||
// We need to update the map.
|
||||
if (from == From::Domain)
|
||||
{
|
||||
// The element indexing was changed as part of generation of leaf
|
||||
// elements. We need to update the map.
|
||||
submesh.parent_to_submesh_element_ids_ = -1;
|
||||
for (int i = 0; i < submesh.parent_element_ids_.Size(); i++)
|
||||
{
|
||||
submesh.parent_element_ids_[i] =
|
||||
parent.elements[parent_element_ids_[leaf_elements[i]]].index;
|
||||
submesh.parent_to_submesh_element_ids_[submesh.parent_element_ids_[i]] = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
submesh.parent_to_submesh_element_ids_ = -1;
|
||||
// parent elements are BOUNDARY elements, need to map face index to be.
|
||||
const auto &parent_face_to_be = submesh.GetParent()->GetFaceToBdrElMap();
|
||||
MFEM_ASSERT(NElements == submesh.GetNE(), "!");
|
||||
auto new_parent_to_submesh_element_ids = submesh.parent_to_submesh_element_ids_;
|
||||
Array<int> new_parent_element_ids;
|
||||
new_parent_element_ids.Reserve(submesh.parent_element_ids_.Size());
|
||||
for (int i = 0; i < submesh.parent_element_ids_.Size(); i++)
|
||||
{
|
||||
new_parent_element_ids.Append(
|
||||
parent_face_to_be[parent.faces[parent_element_ids_[leaf_elements[i]]].index]);
|
||||
new_parent_to_submesh_element_ids[new_parent_element_ids[i]] = i;
|
||||
}
|
||||
|
||||
MFEM_ASSERT(new_parent_element_ids.Size() == submesh.parent_element_ids_.Size(),
|
||||
"!");
|
||||
#ifdef MFEM_DEBUG
|
||||
for (auto x : new_parent_element_ids)
|
||||
{
|
||||
MFEM_ASSERT(std::find(submesh.parent_element_ids_.begin(),
|
||||
submesh.parent_element_ids_.end(), x)
|
||||
!= submesh.parent_element_ids_.end(),
|
||||
x << " not found in submesh.parent_element_ids_");
|
||||
}
|
||||
for (auto x : submesh.parent_element_ids_)
|
||||
{
|
||||
MFEM_ASSERT(std::find(new_parent_element_ids.begin(),
|
||||
new_parent_element_ids.end(), x)
|
||||
!= new_parent_element_ids.end(), x << " not found in new_parent_element_ids_");
|
||||
}
|
||||
#endif
|
||||
submesh.parent_element_ids_ = std::move(new_parent_element_ids);
|
||||
submesh.parent_to_submesh_element_ids_ =
|
||||
std::move(new_parent_to_submesh_element_ids);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2010-2024, 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_NCSUBMESH
|
||||
#define MFEM_NCSUBMESH
|
||||
|
||||
#include "../ncmesh.hpp"
|
||||
#include "submesh.hpp"
|
||||
#include "submesh_utils.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Class representing a Nonconformal SubMesh. This is only used by
|
||||
* SubMesh.
|
||||
*/
|
||||
class NCSubMesh : public NCMesh
|
||||
{
|
||||
friend class SubMesh; ///< Only SubMesh can use methods in this class
|
||||
public:
|
||||
using From = SubMesh::From; ///< Convenience type alias
|
||||
/// Get the parent NCMesh object
|
||||
const NCMesh* GetParent() const
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if NCMesh @a m is a NCSubMesh.
|
||||
*
|
||||
* @param m The input NCMesh
|
||||
*/
|
||||
static bool IsNCSubMesh(const NCMesh *m)
|
||||
{
|
||||
return dynamic_cast<const NCSubMesh *>(m) != nullptr;
|
||||
}
|
||||
private:
|
||||
|
||||
/// Private constructor
|
||||
NCSubMesh(SubMesh& submesh, const NCMesh &parent, From from,
|
||||
const Array<int> &attributes);
|
||||
|
||||
/// The parent NCMesh. Not owned.
|
||||
const NCMesh *parent_;
|
||||
|
||||
/// Mapping from submesh element nc ids (index of the array), to the parent
|
||||
/// element ids. If from a boundary, these map to faces in the parent.
|
||||
Array<int> parent_element_ids_;
|
||||
|
||||
/// Mapping from NCSubMesh node ids (index of the array), to the parent
|
||||
/// NCMesh node ids.
|
||||
Array<int> parent_node_ids_;
|
||||
|
||||
/// Mapping from parent NCMesh node ids to submesh NCMesh node ids.
|
||||
// Inverse map of parent_node_ids_.
|
||||
std::unordered_map<int, int> parent_to_submesh_node_ids_;
|
||||
|
||||
/// Mapping from parent NCMesh element ids to submesh NCMesh element ids.
|
||||
// Inverse map of parent_element_ids_.
|
||||
std::unordered_map<int, int> parent_to_submesh_element_ids_;
|
||||
|
||||
// Helper friend methods for construction.
|
||||
friend void SubMeshUtils::ConstructFaceTree<NCSubMesh>(NCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
friend void SubMeshUtils::ConstructVolumeTree<NCSubMesh>(NCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Accessor for parent nodes
|
||||
* @details Required to bypass access protection in parent class.
|
||||
*
|
||||
* @return const HashTable<Node>&
|
||||
*/
|
||||
const HashTable<Node> &ParentNodes() const { return parent_->nodes; }
|
||||
|
||||
/**
|
||||
* @brief Accessor for parent faces
|
||||
* @details Required to bypass access protection in parent class.
|
||||
*
|
||||
* @return const HashTable<Face>&
|
||||
*/
|
||||
const HashTable<Face> &ParentFaces() const { return parent_->faces; }
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_NCSUBMESH
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "pncsubmesh.hpp"
|
||||
|
||||
#include <numeric>
|
||||
#include <unordered_map>
|
||||
#include "submesh_utils.hpp"
|
||||
#include "psubmesh.hpp"
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using namespace SubMeshUtils;
|
||||
|
||||
|
||||
ParNCSubMesh::ParNCSubMesh(ParSubMesh& submesh, const ParNCMesh &parent,
|
||||
From from, const Array<int> &attributes)
|
||||
: ParNCMesh(), parent_(&parent)
|
||||
{
|
||||
MyComm = submesh.GetComm();
|
||||
NRanks = submesh.GetNRanks();
|
||||
MyRank = submesh.GetMyRank();
|
||||
|
||||
Dim = submesh.Dimension();
|
||||
spaceDim = submesh.SpaceDimension();
|
||||
Iso = true;
|
||||
Legacy = false;
|
||||
|
||||
// Loop over parent leaf elements and add nodes for all vertices. Register as
|
||||
// top level nodes, will reparent when looping over edges. Cannot add edge
|
||||
// nodes at same time because top level vertex nodes must be contiguous and
|
||||
// first in node list (see coordinates).
|
||||
if (from == From::Domain)
|
||||
{
|
||||
SubMeshUtils::ConstructVolumeTree(*this, attributes);
|
||||
}
|
||||
else if (from == From::Boundary)
|
||||
{
|
||||
SubMeshUtils::ConstructFaceTree(*this, attributes);
|
||||
}
|
||||
|
||||
// Loop over all nodes, and reparent based on the node relations of the
|
||||
// parent
|
||||
for (int i = 0; i < parent_node_ids_.Size(); i++)
|
||||
{
|
||||
const auto &parent_node = parent.nodes[parent_node_ids_[i]];
|
||||
const int submesh_p1 = parent_to_submesh_node_ids_[parent_node.p1];
|
||||
const int submesh_p2 = parent_to_submesh_node_ids_[parent_node.p2];
|
||||
nodes.Reparent(i, submesh_p1, submesh_p2);
|
||||
}
|
||||
|
||||
nodes.UpdateUnused();
|
||||
for (int i = 0; i < elements.Size(); i++)
|
||||
{
|
||||
if (elements[i].IsLeaf())
|
||||
{
|
||||
// Register all faces
|
||||
RegisterFaces(i);
|
||||
}
|
||||
}
|
||||
|
||||
InitRootElements();
|
||||
InitRootState(root_state.Size());
|
||||
InitGeomFlags();
|
||||
Update(); // Fills in secondary information based off of elements, nodes and faces.
|
||||
#ifdef MFEM_DEBUG
|
||||
// Check all processors have the same number of roots
|
||||
{
|
||||
int p[2] = {root_state.Size(), -root_state.Size()};
|
||||
MPI_Allreduce(MPI_IN_PLACE, p, 2, MPI_INT, MPI_MIN, submesh.GetComm());
|
||||
MFEM_ASSERT(p[0] == -p[1], "Ranks must agree on number of root elements: min "
|
||||
<< p[0] << " max " << -p[1] << " local " << root_state.Size() << " MyRank " <<
|
||||
submesh.GetMyRank());
|
||||
}
|
||||
#endif
|
||||
|
||||
// If parent has coordinates defined, copy the relevant portion
|
||||
if (parent.coordinates.Size() > 0)
|
||||
{
|
||||
// Loop over new_nodes -> coordinates is indexed by node.
|
||||
coordinates.SetSize(3*parent_node_ids_.Size());
|
||||
parent.tmp_vertex = new TmpVertex[parent.nodes.NumIds()];
|
||||
for (int n = 0; n < parent_node_ids_.Size(); n++)
|
||||
{
|
||||
std::memcpy(&coordinates[3*n], parent.CalcVertexPos(parent_node_ids_[n]),
|
||||
3*sizeof(real_t));
|
||||
}
|
||||
delete [] parent.tmp_vertex;
|
||||
}
|
||||
|
||||
// The element indexing was changed as part of generation of leaf elements.
|
||||
// We need to update the map.
|
||||
if (from == From::Domain)
|
||||
{
|
||||
// The element indexing was changed as part of generation of leaf
|
||||
// elements. We need to update the map.
|
||||
submesh.parent_to_submesh_element_ids_ = -1;
|
||||
for (int i = 0; i < submesh.parent_element_ids_.Size(); i++)
|
||||
{
|
||||
submesh.parent_element_ids_[i] =
|
||||
parent.elements[parent_element_ids_[leaf_elements[i]]].index;
|
||||
submesh.parent_to_submesh_element_ids_[submesh.parent_element_ids_[i]] = i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
submesh.parent_to_submesh_element_ids_ = -1;
|
||||
// parent elements are BOUNDARY elements, need to map face index to be.
|
||||
const auto &parent_face_to_be = submesh.GetParent()->GetFaceToBdrElMap();
|
||||
MFEM_ASSERT(NElements == submesh.GetNE(), NElements << ' ' << submesh.GetNE());
|
||||
auto new_parent_to_submesh_element_ids = submesh.parent_to_submesh_element_ids_;
|
||||
Array<int> new_parent_element_ids;
|
||||
new_parent_element_ids.Reserve(submesh.parent_element_ids_.Size());
|
||||
for (int i = 0; i < submesh.parent_element_ids_.Size(); i++)
|
||||
{
|
||||
new_parent_element_ids.Append(
|
||||
parent_face_to_be[parent.faces[parent_element_ids_[leaf_elements[i]]].index]);
|
||||
new_parent_to_submesh_element_ids[new_parent_element_ids[i]] = i;
|
||||
}
|
||||
|
||||
MFEM_ASSERT(new_parent_element_ids.Size() == submesh.parent_element_ids_.Size(),
|
||||
new_parent_element_ids.Size() << ' ' << submesh.parent_element_ids_.Size());
|
||||
#ifdef MFEM_DEBUG
|
||||
for (auto x : new_parent_element_ids)
|
||||
{
|
||||
MFEM_ASSERT(std::find(submesh.parent_element_ids_.begin(),
|
||||
submesh.parent_element_ids_.end(), x)
|
||||
!= submesh.parent_element_ids_.end(),
|
||||
x << " not found in submesh.parent_element_ids_");
|
||||
}
|
||||
for (auto x : submesh.parent_element_ids_)
|
||||
{
|
||||
MFEM_ASSERT(std::find(new_parent_element_ids.begin(),
|
||||
new_parent_element_ids.end(), x)
|
||||
!= new_parent_element_ids.end(), x << " not found in new_parent_element_ids_");
|
||||
}
|
||||
#endif
|
||||
submesh.parent_element_ids_ = new_parent_element_ids;
|
||||
submesh.parent_to_submesh_element_ids_ = new_parent_to_submesh_element_ids;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2010-2024, 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_PNCSUBMESH
|
||||
#define MFEM_PNCSUBMESH
|
||||
|
||||
#include "../../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
|
||||
#include "../pncmesh.hpp"
|
||||
#include "psubmesh.hpp"
|
||||
#include "submesh_utils.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Class representing a Parallel Nonconformal SubMesh. This is only used
|
||||
* by ParSubMesh.
|
||||
*/
|
||||
class ParNCSubMesh : public ParNCMesh
|
||||
{
|
||||
friend class ParSubMesh; ///< Only ParSubMesh can use methods in this class
|
||||
public:
|
||||
using From = SubMesh::From; ///< Convenience type alias
|
||||
/**
|
||||
* @brief Check if NCMesh @a m is a ParNCSubMesh.
|
||||
*
|
||||
* @param m The input Mesh
|
||||
*/
|
||||
static bool IsParNCSubMesh(const NCMesh *m)
|
||||
{
|
||||
return dynamic_cast<const ParNCSubMesh *>(m) != nullptr;
|
||||
}
|
||||
/// Get the parent ParNCMesh object
|
||||
const ParNCMesh* GetParent() const
|
||||
{
|
||||
return parent_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// protected constructor
|
||||
ParNCSubMesh(ParSubMesh& submesh, const ParNCMesh &parent, From from,
|
||||
const Array<int> &attributes);
|
||||
|
||||
/// The parent ParNCMesh. Not owned.
|
||||
const ParNCMesh *parent_;
|
||||
|
||||
/// Mapping from submesh element nc ids (index of the array), to the parent
|
||||
/// element ids. If from a boundary, these map to faces in the parent.
|
||||
Array<int> parent_element_ids_;
|
||||
|
||||
/// Mapping from ParNCSubMesh node ids (index of the array), to the parent
|
||||
/// NCMesh node ids.
|
||||
Array<int> parent_node_ids_;
|
||||
|
||||
/// Mapping from parent NCMesh node ids to submesh NCMesh node ids.
|
||||
// Inverse map of parent_node_ids_.
|
||||
std::unordered_map<int, int> parent_to_submesh_node_ids_;
|
||||
|
||||
/// Mapping from parent NCMesh element ids to submesh NCMesh element ids.
|
||||
// Inverse map of parent_element_ids_.
|
||||
std::unordered_map<int, int> parent_to_submesh_element_ids_;
|
||||
|
||||
// Helper friend methods for construction.
|
||||
friend void SubMeshUtils::ConstructFaceTree<ParNCSubMesh>
|
||||
(ParNCSubMesh &submesh, const Array<int> &attributes);
|
||||
friend void SubMeshUtils::ConstructVolumeTree<ParNCSubMesh>
|
||||
(ParNCSubMesh &submesh, const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Accessor for parent nodes
|
||||
* @details Required to bypass access protection in parent class.
|
||||
*
|
||||
* @return const HashTable<Node>&
|
||||
*/
|
||||
const HashTable<Node> &ParentNodes() const { return parent_->nodes; }
|
||||
|
||||
/**
|
||||
* @brief Accessor for parent faces
|
||||
* @details Required to bypass access protection in parent class.
|
||||
*
|
||||
* @return const HashTable<Face>&
|
||||
*/
|
||||
const HashTable<Face> &ParentFaces() const { return parent_->faces; }
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
#endif // MFEM_PNCSUBMESH
|
||||
+371
-327
@@ -17,6 +17,7 @@
|
||||
#include <unordered_set>
|
||||
#include <algorithm>
|
||||
#include "psubmesh.hpp"
|
||||
#include "pncsubmesh.hpp"
|
||||
#include "submesh_utils.hpp"
|
||||
#include "../segment.hpp"
|
||||
|
||||
@@ -24,33 +25,29 @@ namespace mfem
|
||||
{
|
||||
|
||||
ParSubMesh ParSubMesh::CreateFromDomain(const ParMesh &parent,
|
||||
Array<int> &domain_attributes)
|
||||
const Array<int> &domain_attributes)
|
||||
{
|
||||
return ParSubMesh(parent, SubMesh::From::Domain, domain_attributes);
|
||||
}
|
||||
|
||||
ParSubMesh ParSubMesh::CreateFromBoundary(const ParMesh &parent,
|
||||
Array<int> &boundary_attributes)
|
||||
const Array<int> &boundary_attributes)
|
||||
{
|
||||
return ParSubMesh(parent, SubMesh::From::Boundary, boundary_attributes);
|
||||
}
|
||||
|
||||
ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
Array<int> &attributes) : parent_(parent), from_(from), attributes_(attributes)
|
||||
const Array<int> &attributes) : parent_(parent), from_(from),
|
||||
attributes_(attributes)
|
||||
{
|
||||
if (Nonconforming())
|
||||
{
|
||||
MFEM_ABORT("SubMesh does not support non-conforming meshes");
|
||||
}
|
||||
|
||||
MyComm = parent.GetComm();
|
||||
NRanks = parent.GetNRanks();
|
||||
MyRank = parent.GetMyRank();
|
||||
|
||||
// This violation of const-ness may be justified in this instance because
|
||||
// the exchange of face neighbor information only establishes or updates
|
||||
// derived information without altering the primary mesh information,
|
||||
// i.e., the topology, geometry, or region attributes.
|
||||
// This violation of const-ness may be justified in this instance because the
|
||||
// exchange of face neighbor information only establishes or updates derived
|
||||
// information without altering the primary mesh information, i.e., the
|
||||
// topology, geometry, or region attributes.
|
||||
const_cast<ParMesh&>(parent).ExchangeFaceNbrData();
|
||||
|
||||
if (from == SubMesh::From::Domain)
|
||||
@@ -70,11 +67,6 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
attributes_, true);
|
||||
}
|
||||
|
||||
// Don't let boundary elements get generated automatically. This would
|
||||
// generate boundary elements on each rank locally, which is topologically
|
||||
// wrong for the distributed SubMesh.
|
||||
FinalizeTopology(false);
|
||||
|
||||
parent_to_submesh_vertex_ids_.SetSize(parent_.GetNV());
|
||||
parent_to_submesh_vertex_ids_ = -1;
|
||||
for (int i = 0; i < parent_vertex_ids_.Size(); i++)
|
||||
@@ -82,6 +74,43 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
parent_to_submesh_vertex_ids_[parent_vertex_ids_[i]] = i;
|
||||
}
|
||||
|
||||
parent_to_submesh_element_ids_.SetSize(from == From::Boundary ? parent.GetNBE()
|
||||
: parent.GetNE());
|
||||
parent_to_submesh_element_ids_ = -1;
|
||||
for (int i = 0; i < parent_element_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_element_ids_[parent_element_ids_[i]] = i;
|
||||
}
|
||||
|
||||
// Don't let boundary elements get generated automatically. This would
|
||||
// generate boundary elements on each rank locally, which is topologically
|
||||
// wrong for the distributed SubMesh.
|
||||
FinalizeTopology(false);
|
||||
|
||||
if (parent.Nonconforming())
|
||||
{
|
||||
pncmesh = new ParNCSubMesh(*this, *parent.pncmesh, from, attributes);
|
||||
pncsubmesh_ = dynamic_cast<ParNCSubMesh*>(pncmesh);
|
||||
ncmesh = pncmesh;
|
||||
InitFromNCMesh(*pncmesh);
|
||||
pncmesh->OnMeshUpdated(this);
|
||||
|
||||
// Update the submesh to parent vertex mapping, NCSubMesh reordered the
|
||||
// vertices so the map to parent is no longer valid.
|
||||
parent_to_submesh_vertex_ids_ = -1;
|
||||
for (int i = 0; i < parent_vertex_ids_.Size(); i++)
|
||||
{
|
||||
// vertex -> node -> parent node -> parent vertex
|
||||
auto node = pncsubmesh_->vertex_nodeId[i];
|
||||
auto parent_node = pncsubmesh_->parent_node_ids_[node];
|
||||
auto parent_vertex = parent.pncmesh->GetNodeVertex(parent_node);
|
||||
parent_vertex_ids_[i] = parent_vertex;
|
||||
parent_to_submesh_vertex_ids_[parent_vertex] = i;
|
||||
}
|
||||
GenerateNCFaceInfo();
|
||||
SetAttributes();
|
||||
}
|
||||
|
||||
DSTable v2v(parent_.GetNV());
|
||||
parent_.GetVertexToVertexTable(v2v);
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
@@ -115,7 +144,6 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
}
|
||||
|
||||
parent_face_ori_.SetSize(NumOfFaces);
|
||||
|
||||
for (int i = 0; i < NumOfFaces; i++)
|
||||
{
|
||||
Array<int> sub_vert;
|
||||
@@ -191,7 +219,6 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
// Every rank containing elements of the ParSubMesh attributes now has a
|
||||
// local ParSubMesh. We have to connect the local meshes and assign global
|
||||
// boundaries correctly.
|
||||
|
||||
Array<int> rhvtx;
|
||||
FindSharedVerticesRanks(rhvtx);
|
||||
AppendSharedVerticesGroups(groups, rhvtx);
|
||||
@@ -207,6 +234,7 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
AppendSharedFacesGroups(groups, rht, rhq);
|
||||
}
|
||||
|
||||
|
||||
// Build the group communication topology
|
||||
gtopo.SetComm(MyComm);
|
||||
gtopo.Create(groups, 822);
|
||||
@@ -239,113 +267,17 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
|
||||
ExchangeFaceNbrData();
|
||||
|
||||
// Add boundaries
|
||||
SubMeshUtils::AddBoundaryElements(*this,
|
||||
(from == SubMesh::From::Domain)
|
||||
? FindGhostBoundaryElementAttributes()
|
||||
: std::unordered_map<int,int> {});
|
||||
|
||||
if (Dim > 1)
|
||||
{
|
||||
const int num_codim_1 = [this]()
|
||||
{
|
||||
if (Dim == 1) { return NumOfVertices; }
|
||||
else if (Dim == 2) { return NumOfEdges; }
|
||||
else if (Dim == 3) { return NumOfFaces; }
|
||||
else { MFEM_ABORT("Invalid dimension."); return -1; }
|
||||
}();
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
// In 3D we check for `bel_to_edge`. It shouldn't have been set
|
||||
// previously.
|
||||
delete bel_to_edge;
|
||||
bel_to_edge = nullptr;
|
||||
}
|
||||
|
||||
NumOfBdrElements = 0;
|
||||
for (int i = 0; i < num_codim_1; i++)
|
||||
{
|
||||
if (GetFaceInformation(i).IsBoundary())
|
||||
{
|
||||
NumOfBdrElements++;
|
||||
}
|
||||
}
|
||||
|
||||
boundary.SetSize(NumOfBdrElements);
|
||||
be_to_face.SetSize(NumOfBdrElements);
|
||||
Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
|
||||
int max_bdr_attr = parent.bdr_attributes.Max();
|
||||
|
||||
for (int i = 0, j = 0; i < num_codim_1; i++)
|
||||
{
|
||||
if (GetFaceInformation(i).IsBoundary())
|
||||
{
|
||||
boundary[j] = faces[i]->Duplicate(this);
|
||||
be_to_face[j] = i;
|
||||
|
||||
if (from == SubMesh::From::Domain && Dim >= 2)
|
||||
{
|
||||
int pbeid = Dim == 3 ? parent_face_to_be[parent_face_ids_[i]] :
|
||||
parent_face_to_be[parent_edge_ids_[i]];
|
||||
if (pbeid != -1)
|
||||
{
|
||||
boundary[j]->SetAttribute(parent.GetBdrAttribute(pbeid));
|
||||
}
|
||||
else
|
||||
{
|
||||
boundary[j]->SetAttribute(max_bdr_attr + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boundary[j]->SetAttribute(SubMesh::GENERATED_ATTRIBUTE);
|
||||
}
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
if (from == SubMesh::From::Domain && Dim >= 2)
|
||||
{
|
||||
// Search for and count interior boundary elements
|
||||
int InteriorBdrElems = 0;
|
||||
for (int i=0; i<parent.GetNBE(); i++)
|
||||
{
|
||||
const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const int submeshFaceIdx =
|
||||
Dim == 3 ?
|
||||
parent_to_submesh_face_ids_[parentFaceIdx] :
|
||||
parent_to_submesh_edge_ids_[parentFaceIdx];
|
||||
|
||||
if (submeshFaceIdx == -1) { continue; }
|
||||
if (GetFaceInformation(submeshFaceIdx).IsBoundary()) { continue; }
|
||||
|
||||
InteriorBdrElems++;
|
||||
}
|
||||
|
||||
if (InteriorBdrElems > 0)
|
||||
{
|
||||
const int OldNumOfBdrElements = NumOfBdrElements;
|
||||
NumOfBdrElements += InteriorBdrElems;
|
||||
boundary.SetSize(NumOfBdrElements);
|
||||
be_to_face.SetSize(NumOfBdrElements);
|
||||
|
||||
// Search for and transfer interior boundary elements
|
||||
for (int i=0, j = OldNumOfBdrElements; i<parent.GetNBE(); i++)
|
||||
{
|
||||
const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const int submeshFaceIdx =
|
||||
parent_to_submesh_face_ids_[parentFaceIdx];
|
||||
|
||||
if (submeshFaceIdx == -1) { continue; }
|
||||
if (GetFaceInformation(submeshFaceIdx).IsBoundary())
|
||||
{ continue; }
|
||||
|
||||
boundary[j] = faces[submeshFaceIdx]->Duplicate(this);
|
||||
be_to_face[j] = submeshFaceIdx;
|
||||
boundary[j]->SetAttribute(parent.GetBdrAttribute(i));
|
||||
|
||||
++j;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!el_to_edge) { el_to_edge = new Table; }
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
|
||||
if (Dim == 3)
|
||||
if (Dim > 2)
|
||||
{
|
||||
GetElementToFaceTable();
|
||||
}
|
||||
@@ -376,84 +308,6 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
|
||||
Transfer(*pn, *n);
|
||||
}
|
||||
|
||||
if (Dim > 1)
|
||||
{
|
||||
if (!el_to_edge) { el_to_edge = new Table; }
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
|
||||
if (Dim > 1 && from == SubMesh::From::Domain)
|
||||
{
|
||||
// Order 0 Raviart-Thomas space will have precisely 1 DoF per face.
|
||||
// We can use this DoF to communicate boundary attribute numbers.
|
||||
RT_FECollection fec_rt(0, Dim);
|
||||
ParFiniteElementSpace parent_fes_rt(const_cast<ParMesh*>(&parent),
|
||||
&fec_rt);
|
||||
|
||||
ParGridFunction parent_bdr_attr_gf(&parent_fes_rt);
|
||||
parent_bdr_attr_gf = 0.0;
|
||||
|
||||
Array<int> vdofs;
|
||||
DofTransformation doftrans;
|
||||
int dof, faceIdx;
|
||||
real_t sign, w;
|
||||
|
||||
// Copy boundary attribute numbers into local portion of a parallel
|
||||
// grid function
|
||||
parent_bdr_attr_gf.HostReadWrite(); // not modifying all entries
|
||||
for (int i=0; i<parent.GetNBE(); i++)
|
||||
{
|
||||
faceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const FaceInformation &faceInfo = parent.GetFaceInformation(faceIdx);
|
||||
parent_fes_rt.GetBdrElementDofs(i, vdofs, doftrans);
|
||||
dof = ParFiniteElementSpace::DecodeDof(vdofs[0], sign);
|
||||
|
||||
// Shared interior boundary elements are not duplicated across
|
||||
// processor boundaries but ParGridFunction::ParallelAverage will
|
||||
// assume both processors contribute to the averaged DoF value. So,
|
||||
// we multiply shared boundary values by 2 so that the average
|
||||
// produces the desired value.
|
||||
w = faceInfo.IsShared() ? 2.0 : 1.0;
|
||||
|
||||
// The DoF sign is needed to ensure that non-shared interior
|
||||
// boundary values sum properly rather than canceling.
|
||||
parent_bdr_attr_gf[dof] = sign * w * parent.GetBdrAttribute(i);
|
||||
}
|
||||
|
||||
Vector parent_bdr_attr(parent_fes_rt.GetTrueVSize());
|
||||
|
||||
// Compute the average of the attribute numbers
|
||||
parent_bdr_attr_gf.ParallelAverage(parent_bdr_attr);
|
||||
// Distribute boundary attributes to neighboring processors
|
||||
parent_bdr_attr_gf.Distribute(parent_bdr_attr);
|
||||
|
||||
ParFiniteElementSpace submesh_fes_rt(this,
|
||||
&fec_rt);
|
||||
|
||||
ParGridFunction submesh_bdr_attr_gf(&submesh_fes_rt);
|
||||
|
||||
// Transfer the averaged boundary attribute values to the submesh
|
||||
auto transfer_map = ParSubMesh::CreateTransferMap(parent_bdr_attr_gf,
|
||||
submesh_bdr_attr_gf);
|
||||
transfer_map.Transfer(parent_bdr_attr_gf, submesh_bdr_attr_gf);
|
||||
|
||||
// Extract the boundary attribute numbers from the local portion
|
||||
// of the ParGridFunction and set the corresponding boundary element
|
||||
// attributes.
|
||||
int attr;
|
||||
for (int i=0; i<NumOfBdrElements; i++)
|
||||
{
|
||||
submesh_fes_rt.GetBdrElementDofs(i, vdofs, doftrans);
|
||||
dof = ParFiniteElementSpace::DecodeDof(vdofs[0], sign);
|
||||
attr = (int)std::round(std::abs(submesh_bdr_attr_gf[dof]));
|
||||
if (attr != 0)
|
||||
{
|
||||
SetBdrAttribute(i, attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetAttributes();
|
||||
Finalize();
|
||||
}
|
||||
@@ -494,6 +348,7 @@ void ParSubMesh::FindSharedVerticesRanks(Array<int> &rhvtx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
svert_comm.Reduce(rhvtx, GroupCommunicator::Sum);
|
||||
svert_comm.Bcast<int>(rhvtx, 0);
|
||||
@@ -511,8 +366,8 @@ void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
|
||||
rhe.SetSize(nsedges);
|
||||
rhe = 0;
|
||||
|
||||
// On each rank of the group, locally determine if the shared edge is in
|
||||
// the SubMesh.
|
||||
// On each rank of the group, locally determine if the shared edge is in the
|
||||
// SubMesh.
|
||||
for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
const int group_sz = parent_.gtopo.GetGroupSize(g);
|
||||
@@ -528,8 +383,7 @@ void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
|
||||
|
||||
for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
|
||||
{
|
||||
int ple, o;
|
||||
parent_.GroupEdge(g, ge, ple, o);
|
||||
int ple = parent_.GroupEdge(g, ge);
|
||||
int submesh_edge_id = parent_to_submesh_edge_ids_[ple];
|
||||
if (submesh_edge_id != -1)
|
||||
{
|
||||
@@ -538,6 +392,7 @@ void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
sedge_comm.Reduce(rhe, GroupCommunicator::Sum);
|
||||
sedge_comm.Bcast<int>(rhe, 0);
|
||||
@@ -545,50 +400,21 @@ void ParSubMesh::FindSharedEdgesRanks(Array<int> &rhe)
|
||||
|
||||
void ParSubMesh::FindSharedFacesRanks(Array<int>& rht, Array<int> &rhq)
|
||||
{
|
||||
GroupCommunicator squad_comm(parent_.gtopo);
|
||||
parent_.GetSharedQuadCommunicator(squad_comm);
|
||||
|
||||
int nsquad = squad_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
rhq.SetSize(nsquad);
|
||||
rhq = 0;
|
||||
|
||||
for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
|
||||
int plq, o;
|
||||
parent_.GroupQuadrilateral(g, gq, plq, o);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plq];
|
||||
if (submesh_face_id != -1)
|
||||
{
|
||||
rhq[sq] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
squad_comm.Reduce(rhq, GroupCommunicator::Sum);
|
||||
squad_comm.Bcast<int>(rhq, 0);
|
||||
|
||||
GroupCommunicator stria_comm(parent_.gtopo);
|
||||
parent_.GetSharedTriCommunicator(stria_comm);
|
||||
|
||||
int nstria = stria_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
rht.SetSize(nstria);
|
||||
rht = 0;
|
||||
|
||||
for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
MFEM_ASSERT(parent_.gtopo.GetGroupSize(g) == 2
|
||||
|| parent_.GroupNTriangles(g) == 0,
|
||||
parent_.gtopo.GetGroupSize(g) << ' ' << parent_.GroupNTriangles(g));
|
||||
for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
|
||||
int plt, o;
|
||||
parent_.GroupTriangle(g, gt, plt, o);
|
||||
int plt = parent_.GroupTriangle(g, gt);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plt];
|
||||
if (submesh_face_id != -1)
|
||||
{
|
||||
@@ -600,6 +426,33 @@ void ParSubMesh::FindSharedFacesRanks(Array<int>& rht, Array<int> &rhq)
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
stria_comm.Reduce(rht, GroupCommunicator::Sum);
|
||||
stria_comm.Bcast<int>(rht, 0);
|
||||
|
||||
GroupCommunicator squad_comm(parent_.gtopo);
|
||||
parent_.GetSharedQuadCommunicator(squad_comm);
|
||||
int nsquad = squad_comm.GroupLDofTable().Size_of_connections();
|
||||
rhq.SetSize(nsquad);
|
||||
rhq = 0;
|
||||
|
||||
for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
MFEM_ASSERT(parent_.gtopo.GetGroupSize(g) == 2
|
||||
|| parent_.GroupNQuadrilaterals(g) == 0,
|
||||
parent_.gtopo.GetGroupSize(g) << ' ' << parent_.GroupNQuadrilaterals(g));
|
||||
for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
int plq = parent_.GroupQuadrilateral(g, gq);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plq];
|
||||
if (submesh_face_id != -1)
|
||||
{
|
||||
rhq[sq] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the sum on the root rank and broadcast the result to all ranks.
|
||||
squad_comm.Reduce(rhq, GroupCommunicator::Sum);
|
||||
squad_comm.Bcast<int>(rhq, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -608,6 +461,7 @@ void ParSubMesh::AppendSharedVerticesGroups(ListOfIntegerSets &groups,
|
||||
{
|
||||
IntegerSet group;
|
||||
|
||||
// g = 0 corresponds to the singleton group of each rank alone.
|
||||
for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
const int group_sz = parent_.gtopo.GetGroupSize(g);
|
||||
@@ -679,8 +533,7 @@ void ParSubMesh::AppendSharedEdgesGroups(ListOfIntegerSets &groups,
|
||||
|
||||
for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
|
||||
{
|
||||
int ple, o;
|
||||
parent_.GroupEdge(g, ge, ple, o);
|
||||
int ple = parent_.GroupEdge(g, ge);
|
||||
int submesh_edge = parent_to_submesh_edge_ids_[ple];
|
||||
|
||||
// Reusing the `rhe` array as shared edge to group array.
|
||||
@@ -729,8 +582,7 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
const int group_sz = parent_.gtopo.GetGroupSize(g);
|
||||
MFEM_ASSERT(group_sz == 2, "internal error");
|
||||
|
||||
int plq, o;
|
||||
parent_.GroupQuadrilateral(g, gq, plq, o);
|
||||
int plq = parent_.GroupQuadrilateral(g, gq);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plq];
|
||||
|
||||
// Reusing the `rhq` array as shared face to group array.
|
||||
@@ -743,8 +595,8 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
{
|
||||
// shared face is present on this rank and others
|
||||
|
||||
// There can only be two ranks in this group sharing faces. Add
|
||||
// all ranks to a new communication group.
|
||||
// There can only be two ranks in this group sharing faces. Add all
|
||||
// ranks to a new communication group.
|
||||
Array<int> &ranks = quad_group;
|
||||
ranks.SetSize(0);
|
||||
ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
|
||||
@@ -770,8 +622,7 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
const int group_sz = parent_.gtopo.GetGroupSize(g);
|
||||
MFEM_ASSERT(group_sz == 2, "internal error");
|
||||
|
||||
int plt, o;
|
||||
parent_.GroupTriangle(g, gt, plt, o);
|
||||
int plt = parent_.GroupTriangle(g, gt);
|
||||
int submesh_face_id = parent_to_submesh_face_ids_[plt];
|
||||
|
||||
// Reusing the `rht` array as shared face to group array.
|
||||
@@ -784,8 +635,8 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
{
|
||||
// shared face is present on this rank and others
|
||||
|
||||
// There can only be two ranks in this group sharing faces. Add
|
||||
// all ranks to a new communication group.
|
||||
// There can only be two ranks in this group sharing faces. Add all
|
||||
// ranks to a new communication group.
|
||||
Array<int> &ranks = tria_group;
|
||||
ranks.SetSize(0);
|
||||
ranks.Append(parent_.gtopo.GetNeighborRank(group_lproc[0]));
|
||||
@@ -802,96 +653,46 @@ void ParSubMesh::AppendSharedFacesGroups(ListOfIntegerSets &groups,
|
||||
}
|
||||
}
|
||||
|
||||
void ParSubMesh::BuildVertexGroup(int ngroups, const Array<int>& rhvtx,
|
||||
int& nsverts)
|
||||
void BuildGroup(Table &group, int ngroups, const Array<int>& rh, int &ns)
|
||||
{
|
||||
group_svert.MakeI(ngroups);
|
||||
for (int i = 0; i < rhvtx.Size(); i++)
|
||||
group.MakeI(ngroups);
|
||||
for (int i = 0; i < rh.Size(); i++)
|
||||
{
|
||||
if (rhvtx[i] >= 0)
|
||||
if (rh[i] >= 0)
|
||||
{
|
||||
group_svert.AddAColumnInRow(rhvtx[i]);
|
||||
group.AddAColumnInRow(rh[i]);
|
||||
}
|
||||
}
|
||||
|
||||
group_svert.MakeJ();
|
||||
nsverts = 0;
|
||||
for (int i = 0; i < rhvtx.Size(); i++)
|
||||
group.MakeJ();
|
||||
ns = 0;
|
||||
for (int i = 0; i < rh.Size(); i++)
|
||||
{
|
||||
if (rhvtx[i] >= 0)
|
||||
if (rh[i] >= 0)
|
||||
{
|
||||
group_svert.AddConnection(rhvtx[i], nsverts++);
|
||||
group.AddConnection(rh[i], ns++);
|
||||
}
|
||||
}
|
||||
group_svert.ShiftUpI();
|
||||
group.ShiftUpI();
|
||||
}
|
||||
|
||||
void ParSubMesh::BuildVertexGroup(int ngroups, const Array<int>& rhvtx,
|
||||
int& nsverts)
|
||||
{
|
||||
BuildGroup(group_svert, ngroups, rhvtx, nsverts);
|
||||
}
|
||||
|
||||
void ParSubMesh::BuildEdgeGroup(int ngroups, const Array<int>& rhe,
|
||||
int& nsedges)
|
||||
{
|
||||
group_sedge.MakeI(ngroups);
|
||||
for (int i = 0; i < rhe.Size(); i++)
|
||||
{
|
||||
if (rhe[i] >= 0)
|
||||
{
|
||||
group_sedge.AddAColumnInRow(rhe[i]);
|
||||
}
|
||||
}
|
||||
|
||||
group_sedge.MakeJ();
|
||||
nsedges = 0;
|
||||
for (int i = 0; i < rhe.Size(); i++)
|
||||
{
|
||||
if (rhe[i] >= 0)
|
||||
{
|
||||
group_sedge.AddConnection(rhe[i], nsedges++);
|
||||
}
|
||||
}
|
||||
group_sedge.ShiftUpI();
|
||||
BuildGroup(group_sedge, ngroups, rhe, nsedges);
|
||||
}
|
||||
|
||||
void ParSubMesh::BuildFaceGroup(int ngroups, const Array<int>& rht,
|
||||
int& nstrias, const Array<int>& rhq, int& nsquads)
|
||||
{
|
||||
group_squad.MakeI(ngroups);
|
||||
for (int i = 0; i < rhq.Size(); i++)
|
||||
{
|
||||
if (rhq[i] >= 0)
|
||||
{
|
||||
group_squad.AddAColumnInRow(rhq[i]);
|
||||
}
|
||||
}
|
||||
|
||||
group_squad.MakeJ();
|
||||
nsquads = 0;
|
||||
for (int i = 0; i < rhq.Size(); i++)
|
||||
{
|
||||
if (rhq[i] >= 0)
|
||||
{
|
||||
group_squad.AddConnection(rhq[i], nsquads++);
|
||||
}
|
||||
}
|
||||
group_squad.ShiftUpI();
|
||||
|
||||
group_stria.MakeI(ngroups);
|
||||
for (int i = 0; i < rht.Size(); i++)
|
||||
{
|
||||
if (rht[i] >= 0)
|
||||
{
|
||||
group_stria.AddAColumnInRow(rht[i]);
|
||||
}
|
||||
}
|
||||
|
||||
group_stria.MakeJ();
|
||||
nstrias = 0;
|
||||
for (int i = 0; i < rht.Size(); i++)
|
||||
{
|
||||
if (rht[i] >= 0)
|
||||
{
|
||||
group_stria.AddConnection(rht[i], nstrias++);
|
||||
}
|
||||
}
|
||||
group_stria.ShiftUpI();
|
||||
BuildGroup(group_squad, ngroups, rhq, nsquads);
|
||||
BuildGroup(group_stria, ngroups, rht, nstrias);
|
||||
}
|
||||
|
||||
void ParSubMesh::BuildSharedVerticesMapping(const int nsverts,
|
||||
@@ -943,8 +744,8 @@ void ParSubMesh::BuildSharedEdgesMapping(const int sedges_ct,
|
||||
int v0 = parent_to_submesh_vertex_ids_[vert[(1-o)/2]];
|
||||
int v1 = parent_to_submesh_vertex_ids_[vert[(1+o)/2]];
|
||||
|
||||
// The orienation of the shared edge relative to the local edge
|
||||
// will be determined by whether v0 < v1 or v1 < v0
|
||||
// The orienation of the shared edge relative to the local edge will
|
||||
// be determined by whether v0 < v1 or v1 < v0
|
||||
shared_edges.Append(new Segment(v0, v1, 1));
|
||||
sedge_ledge.Append(submesh_edge_id);
|
||||
}
|
||||
@@ -960,9 +761,8 @@ void ParSubMesh::BuildSharedFacesMapping(const int nstrias,
|
||||
shared_quads.Reserve(nsquads);
|
||||
sface_lface.Reserve(nstrias + nsquads);
|
||||
|
||||
// sface_lface should list the triangular shared faces first
|
||||
// followed by the quadrilateral shared faces.
|
||||
|
||||
// sface_lface should list the triangular shared faces first followed by the
|
||||
// quadrilateral shared faces.
|
||||
for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
|
||||
@@ -1028,7 +828,7 @@ void ParSubMesh::BuildSharedFacesMapping(const int nstrias,
|
||||
int v2 = vert[2];
|
||||
int v3 = vert[3];
|
||||
|
||||
// See Mesh::GetQuadOrientation for info on interpretting "o"
|
||||
// See Mesh::GetQuadOrientation for info on interpreting "o"
|
||||
switch (o)
|
||||
{
|
||||
case 1:
|
||||
@@ -1057,10 +857,254 @@ void ParSubMesh::BuildSharedFacesMapping(const int nstrias,
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<int, int>
|
||||
ParSubMesh::FindGhostBoundaryElementAttributes() const
|
||||
{
|
||||
// Loop over shared faces in the parent mesh, find their attributes if they
|
||||
// exist, and map to local faces in the submesh.
|
||||
std::unordered_map<int,int> lface_boundary_attribute;
|
||||
const auto &face_to_be = parent_.GetFaceToBdrElMap();
|
||||
if (Dim == 3)
|
||||
{
|
||||
GroupCommunicator squad_comm(parent_.gtopo);
|
||||
parent_.GetSharedQuadCommunicator(squad_comm);
|
||||
int nsquad = squad_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
GroupCommunicator stria_comm(parent_.gtopo);
|
||||
parent_.GetSharedTriCommunicator(stria_comm);
|
||||
int nstria = stria_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
Array<int> stba(nstria), sqba(nsquad);
|
||||
Array<int> parent_ltface(nstria), parent_lqface(nsquad);
|
||||
stba = 0; sqba = 0;
|
||||
parent_ltface = -1; parent_lqface = -1;
|
||||
for (int g = 1, st = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gt = 0; gt < parent_.GroupNTriangles(g); gt++, st++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
int plt = parent_.GroupTriangle(g, gt);
|
||||
auto pbe = face_to_be[plt];
|
||||
if (pbe >= 0)
|
||||
{
|
||||
stba[st] = parent_.GetBdrAttribute(pbe);
|
||||
}
|
||||
parent_ltface[st] = plt;
|
||||
}
|
||||
}
|
||||
for (int g = 1, sq = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gq = 0; gq < parent_.GroupNQuadrilaterals(g); gq++, sq++)
|
||||
{
|
||||
// Group size of a shared face is always 2
|
||||
int plq = parent_.GroupQuadrilateral(g, gq);
|
||||
auto pbe = face_to_be[plq];
|
||||
if (pbe >= 0)
|
||||
{
|
||||
sqba[sq] = parent_.GetBdrAttribute(pbe);
|
||||
}
|
||||
parent_lqface[sq] = plq;
|
||||
}
|
||||
}
|
||||
#ifdef MFEM_DEBUG
|
||||
auto pre_stba = stba;
|
||||
auto pre_sqba = sqba;
|
||||
#endif
|
||||
stria_comm.Reduce(stba, GroupCommunicator::Sum);
|
||||
stria_comm.Bcast<int>(stba, 0);
|
||||
squad_comm.Reduce(sqba, GroupCommunicator::Sum);
|
||||
squad_comm.Bcast<int>(sqba, 0);
|
||||
#ifdef MFEM_DEBUG
|
||||
{
|
||||
Array<int> fail_indices;
|
||||
fail_indices.Reserve(stba.Size());
|
||||
for (int i = 0; i < stba.Size(); i++)
|
||||
if (pre_stba[i] != 0 && pre_stba[i] != stba[i])
|
||||
{
|
||||
fail_indices.Append(i);
|
||||
}
|
||||
MFEM_ASSERT(fail_indices.Size() == 0, [&]()
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "More than one rank found attribute on shared tri face: ";
|
||||
for (auto x : fail_indices)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
return msg.str();
|
||||
}());
|
||||
}
|
||||
|
||||
{
|
||||
Array<int> fail_indices;
|
||||
fail_indices.Reserve(sqba.Size());
|
||||
for (int i = 0; i < sqba.Size(); i++)
|
||||
if (pre_sqba[i] != 0 && pre_sqba[i] != sqba[i])
|
||||
{
|
||||
fail_indices.Append(i);
|
||||
}
|
||||
MFEM_ASSERT(fail_indices.Size() == 0, [&]()
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "More than one rank found attribute on shared quad face: ";
|
||||
for (auto x : fail_indices)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
return msg.str();
|
||||
}());
|
||||
}
|
||||
#endif
|
||||
int nghost = 0;
|
||||
for (auto x : stba)
|
||||
if (x > 0) { ++nghost; }
|
||||
|
||||
for (auto x : sqba)
|
||||
if (x > 0) { ++nghost; }
|
||||
|
||||
lface_boundary_attribute.reserve(nghost);
|
||||
for (int i = 0; i < stba.Size(); i++)
|
||||
if (stba[i] > 0)
|
||||
{
|
||||
MFEM_ASSERT(parent_ltface[i] > -1, i);
|
||||
lface_boundary_attribute[parent_ltface[i]] = stba[i];
|
||||
}
|
||||
for (int i = 0; i < sqba.Size(); i++)
|
||||
if (sqba[i] > 0)
|
||||
{
|
||||
MFEM_ASSERT(parent_lqface[i] > -1, i);
|
||||
lface_boundary_attribute[parent_lqface[i]] = sqba[i];
|
||||
}
|
||||
}
|
||||
else if (Dim == 2)
|
||||
{
|
||||
GroupCommunicator sedge_comm(parent_.gtopo);
|
||||
parent_.GetSharedEdgeCommunicator(sedge_comm);
|
||||
int nsedge = sedge_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
Array<int> seba(nsedge), parent_ledge(nsedge);
|
||||
seba = 0; parent_ledge = -1;
|
||||
for (int g = 1, se = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int ge = 0; ge < parent_.GroupNEdges(g); ge++, se++)
|
||||
{
|
||||
// Group size of a shared edge is always 2
|
||||
int ple = parent_.GroupEdge(g, ge);
|
||||
auto pbe = face_to_be[ple];
|
||||
if (pbe >= 0)
|
||||
{
|
||||
seba[se] = parent_.GetBdrAttribute(pbe);
|
||||
}
|
||||
parent_ledge[se] = ple;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
auto pre_seba = seba;
|
||||
#endif
|
||||
sedge_comm.Reduce(seba, GroupCommunicator::Sum);
|
||||
sedge_comm.Bcast<int>(seba, 0);
|
||||
#ifdef MFEM_DEBUG
|
||||
{
|
||||
Array<int> fail_indices;
|
||||
fail_indices.Reserve(seba.Size());
|
||||
for (int i = 0; i < seba.Size(); i++)
|
||||
if (pre_seba[i] != 0 && pre_seba[i] != seba[i])
|
||||
{
|
||||
fail_indices.Append(i);
|
||||
}
|
||||
MFEM_ASSERT(fail_indices.Size() == 0, [&]()
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "More than one rank found attribute on shared edge: ";
|
||||
for (auto x : fail_indices)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
return msg.str();
|
||||
}());
|
||||
}
|
||||
#endif
|
||||
int nghost = 0;
|
||||
for (auto x : seba)
|
||||
if (x > 0) { ++nghost; }
|
||||
|
||||
lface_boundary_attribute.reserve(nghost);
|
||||
for (int i = 0; i < seba.Size(); i++)
|
||||
if (seba[i] > 0)
|
||||
{
|
||||
MFEM_ASSERT(parent_ledge[i] > -1, i);
|
||||
lface_boundary_attribute[parent_ledge[i]] = seba[i];
|
||||
}
|
||||
}
|
||||
else if (Dim == 1)
|
||||
{
|
||||
GroupCommunicator svert_comm(parent_.gtopo);
|
||||
parent_.GetSharedVertexCommunicator(svert_comm);
|
||||
int nsvtx = svert_comm.GroupLDofTable().Size_of_connections();
|
||||
|
||||
Array<int> svba(nsvtx), parent_lvtx(nsvtx);
|
||||
svba = 0; parent_lvtx = -1;
|
||||
for (int g = 1, sv = 0; g < parent_.GetNGroups(); g++)
|
||||
{
|
||||
for (int gv = 0; gv < parent_.GroupNVertices(g); gv++, sv++)
|
||||
{
|
||||
// Group size of a shared vertex is always 2
|
||||
int plv = parent_.GroupVertex(g, gv);
|
||||
auto pbe = face_to_be[plv];
|
||||
if (pbe >= 0)
|
||||
{
|
||||
svba[sv] = parent_.GetBdrAttribute(pbe);
|
||||
}
|
||||
parent_lvtx[sv] = plv;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
auto pre_svba = svba;
|
||||
#endif
|
||||
svert_comm.Reduce(svba, GroupCommunicator::Sum);
|
||||
svert_comm.Bcast<int>(svba, 0);
|
||||
#ifdef MFEM_DEBUG
|
||||
{
|
||||
Array<int> fail_indices;
|
||||
fail_indices.Reserve(svba.Size());
|
||||
for (int i = 0; i < svba.Size(); i++)
|
||||
if (pre_svba[i] != 0 && pre_svba[i] != svba[i])
|
||||
{
|
||||
fail_indices.Append(i);
|
||||
}
|
||||
MFEM_ASSERT(fail_indices.Size() == 0, [&]()
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << "More than one rank found attribute on shared vertex: ";
|
||||
for (auto x : fail_indices)
|
||||
{
|
||||
msg << x << ' ';
|
||||
}
|
||||
return msg.str();
|
||||
}());
|
||||
}
|
||||
#endif
|
||||
int nghost = 0;
|
||||
for (auto x : svba)
|
||||
if (x > 0) { ++nghost; }
|
||||
|
||||
lface_boundary_attribute.reserve(nghost);
|
||||
for (int i = 0; i < svba.Size(); i++)
|
||||
if (svba[i] > 0)
|
||||
{
|
||||
MFEM_ASSERT(parent_lvtx[i] > -1, i);
|
||||
lface_boundary_attribute[parent_lvtx[i]] = svba[i];
|
||||
}
|
||||
}
|
||||
return lface_boundary_attribute;
|
||||
}
|
||||
|
||||
|
||||
void ParSubMesh::Transfer(const ParGridFunction &src, ParGridFunction &dst)
|
||||
{
|
||||
ParTransferMap map(src, dst);
|
||||
map.Transfer(src, dst);
|
||||
CreateTransferMap(src, dst).Transfer(src, dst);
|
||||
}
|
||||
|
||||
ParTransferMap ParSubMesh::CreateTransferMap(const ParGridFunction &src,
|
||||
|
||||
+85
-20
@@ -24,6 +24,8 @@
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class ParNCSubMesh;
|
||||
|
||||
/**
|
||||
* @brief Subdomain representation of a topological parent in another ParMesh.
|
||||
*
|
||||
@@ -50,11 +52,13 @@ namespace mfem
|
||||
|
||||
class ParSubMesh : public ParMesh
|
||||
{
|
||||
friend class ParNCSubMesh;
|
||||
public:
|
||||
using From = SubMesh::From; ///< Convenience type-alias.
|
||||
ParSubMesh() = delete;
|
||||
|
||||
/**
|
||||
* @brief Create a domain ParSubMesh from it's parent.
|
||||
* @brief Create a domain ParSubMesh from its parent.
|
||||
*
|
||||
* The ParSubMesh object expects the parent ParMesh object to be valid for
|
||||
* the entire object lifetime. The @a domain_attributes have to mark exactly
|
||||
@@ -64,10 +68,10 @@ public:
|
||||
* @param[in] domain_attributes Domain attributes to extract
|
||||
*/
|
||||
static ParSubMesh CreateFromDomain(const ParMesh &parent,
|
||||
Array<int> &domain_attributes);
|
||||
const Array<int> &domain_attributes);
|
||||
|
||||
/**
|
||||
* @brief Create a surface ParSubMesh from it's parent.
|
||||
* @brief Create a surface ParSubMesh from its parent.
|
||||
*
|
||||
* The ParSubMesh object expects the parent ParMesh object to be valid for the
|
||||
* entire object lifetime. The @a boundary_attributes have to mark exactly one
|
||||
@@ -77,7 +81,7 @@ public:
|
||||
* @param[in] boundary_attributes Boundary attributes to extract
|
||||
*/
|
||||
static ParSubMesh CreateFromBoundary(const ParMesh &parent,
|
||||
Array<int> &boundary_attributes);
|
||||
const Array<int> &boundary_attributes);
|
||||
|
||||
/**
|
||||
* @brief Get the parent ParMesh object
|
||||
@@ -118,6 +122,16 @@ public:
|
||||
return parent_vertex_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the parent edge id map
|
||||
*
|
||||
* Submesh edge id (array index) to parent Mesh edge id.
|
||||
*/
|
||||
const Array<int>& GetParentEdgeIDMap() const
|
||||
{
|
||||
return parent_edge_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the parent face id map.
|
||||
*
|
||||
@@ -139,13 +153,51 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the ParSubMesh face id map.
|
||||
*
|
||||
* ParMesh face id (array index) to ParSubMesh face id.
|
||||
* @brief Get the submesh element corresponding to a parent element. -1 ==
|
||||
* not present.
|
||||
* @param pe The parent element id.
|
||||
* @return int
|
||||
*/
|
||||
const Array<int>& GetParentToSubMeshFaceIDMap() const
|
||||
int GetSubMeshElementFromParent(int pe) const
|
||||
{
|
||||
return parent_to_submesh_face_ids_;
|
||||
return (pe == -1 || pe >= parent_to_submesh_element_ids_.Size())
|
||||
? -1 : parent_to_submesh_element_ids_[pe];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submesh vertex corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pv The parent vertex id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshVertexFromParent(int pv) const
|
||||
{
|
||||
return (pv == -1 || pv >= parent_to_submesh_vertex_ids_.Size())
|
||||
? -1 : parent_to_submesh_vertex_ids_[pv];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submesh edge corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pe The parent edge id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshEdgeFromParent(int pe) const
|
||||
{
|
||||
return (pe == -1 || pe >= parent_to_submesh_edge_ids_.Size())
|
||||
? pe : parent_to_submesh_edge_ids_[pe];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submesh face corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pf The parent face id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshFaceFromParent(int pf) const
|
||||
{
|
||||
return (pf == -1 || pf >= parent_to_submesh_face_ids_.Size())
|
||||
? pf : parent_to_submesh_face_ids_[pf];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +235,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
ParSubMesh(const ParMesh &parent, SubMesh::From from, Array<int> &attributes);
|
||||
ParSubMesh(const ParMesh &parent, SubMesh::From from,
|
||||
const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Find shared vertices on the ParSubMesh.
|
||||
@@ -223,8 +276,8 @@ private:
|
||||
/**
|
||||
* @brief Find shared edges on the ParSubMesh.
|
||||
*
|
||||
* Uses the parent GroupCommunicator to determine shared edges.
|
||||
* Collective. Limited to 32 ranks.
|
||||
* Uses the parent GroupCommunicator to determine shared edges. Collective.
|
||||
* Limited to groups containing less than 32 ranks.
|
||||
*
|
||||
* See FindSharedVerticesRanks for the encoding for @a rhe.
|
||||
*
|
||||
@@ -232,6 +285,7 @@ private:
|
||||
*/
|
||||
void FindSharedEdgesRanks(Array<int> &rhe);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Find shared faces on the ParSubMesh.
|
||||
*
|
||||
@@ -275,10 +329,10 @@ private:
|
||||
* @param[in,out] groups
|
||||
* @param[in,out] rht Encoding of which rank contains which face triangle.
|
||||
* The output is reused s.t. the array index i (the face triangle id) is the
|
||||
* associated group.
|
||||
* associated group. "Rank Has Triangle"
|
||||
* @param[in,out] rhq Encoding of which rank contains which face
|
||||
* quadrilateral. The output is reused s.t. the array index i (the face
|
||||
* quadrilateral id) is the associated group.
|
||||
* quadrilateral id) is the associated group. "Rank Has Quad"
|
||||
*/
|
||||
void AppendSharedFacesGroups(ListOfIntegerSets &groups, Array<int>& rht,
|
||||
Array<int> &rhq);
|
||||
@@ -342,15 +396,22 @@ private:
|
||||
void BuildSharedFacesMapping(const int nstrias, const Array<int>& rht,
|
||||
const int nsquads, const Array<int>& rhq);
|
||||
|
||||
|
||||
std::unordered_map<int, int>
|
||||
FindGhostBoundaryElementAttributes() const;
|
||||
|
||||
/// The parent Mesh
|
||||
const ParMesh &parent_;
|
||||
|
||||
/// Indicator from which part of the parent ParMesh the ParSubMesh is going to
|
||||
/// be created.
|
||||
/// Optional nonconformal submesh. Managed via pncmesh pointer in base class.
|
||||
ParNCSubMesh *pncsubmesh_;
|
||||
|
||||
/// Indicator from which part of the parent ParMesh the ParSubMesh is going
|
||||
/// to be created.
|
||||
SubMesh::From from_;
|
||||
|
||||
/// Attributes on the parent ParMesh on which the ParSubMesh is created. Could
|
||||
/// either be domain or boundary attributes (determined by from_).
|
||||
/// Attributes on the parent ParMesh on which the ParSubMesh is created.
|
||||
/// Could either be domain or boundary attributes (determined by from_).
|
||||
Array<int> attributes_;
|
||||
|
||||
/// Mapping from ParSubMesh element ids (index of the array), to the parent
|
||||
@@ -369,10 +430,14 @@ private:
|
||||
/// ParMesh face ids.
|
||||
Array<int> parent_face_ids_;
|
||||
|
||||
/// Mapping from SubMesh face ids (index of the array), to the orientation
|
||||
/// of the face relative to the parent face.
|
||||
/// Mapping from SubMesh face ids (index of the array), to the orientation of
|
||||
/// the face relative to the parent face.
|
||||
Array<int> parent_face_ori_;
|
||||
|
||||
/// Mapping from parent ParMesh element ids (index of the array), to the
|
||||
/// ParSubMesh element ids. Inverse map of parent_element_ids_.
|
||||
Array<int> parent_to_submesh_element_ids_;
|
||||
|
||||
/// Mapping from parent ParMesh vertex ids (index of the array), to the
|
||||
/// ParSubMesh vertex ids. Inverse map of parent_vertex_ids_.
|
||||
Array<int> parent_to_submesh_vertex_ids_;
|
||||
|
||||
@@ -104,10 +104,10 @@ private:
|
||||
std::unique_ptr<const ParFiniteElementSpace> root_fes_;
|
||||
|
||||
/// Pointer to the supplemental FiniteElementCollection used with root_fes_.
|
||||
/// This is only used if this TransferMap represents a SubMesh to
|
||||
/// SubMesh transfer where the root requires a different type of collection
|
||||
/// than the SubMesh objects. For example, when the subpaces are L2 on
|
||||
/// boundaries of the parent mesh and the root space can be RT.
|
||||
/// This is only used if this TransferMap represents a SubMesh to SubMesh
|
||||
/// transfer where the root requires a different type of collection than the
|
||||
/// SubMesh objects. For example, when the subpaces are L2 on boundaries of
|
||||
/// the parent mesh and the root space can be RT.
|
||||
std::unique_ptr<const FiniteElementCollection> root_fec_;
|
||||
|
||||
const GroupCommunicator *root_gc_ = nullptr;
|
||||
|
||||
+97
-35
@@ -12,36 +12,34 @@
|
||||
#include "submesh.hpp"
|
||||
#include "submesh_utils.hpp"
|
||||
#include "../../fem/gridfunc.hpp"
|
||||
#include "../ncmesh.hpp"
|
||||
#include "ncsubmesh.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
SubMesh SubMesh::CreateFromDomain(const Mesh &parent,
|
||||
Array<int> domain_attributes)
|
||||
const Array<int> &domain_attributes)
|
||||
{
|
||||
return SubMesh(parent, From::Domain, domain_attributes);
|
||||
}
|
||||
|
||||
SubMesh SubMesh::CreateFromBoundary(const Mesh &parent,
|
||||
Array<int> boundary_attributes)
|
||||
const Array<int> &boundary_attributes)
|
||||
{
|
||||
return SubMesh(parent, From::Boundary, boundary_attributes);
|
||||
}
|
||||
|
||||
SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
Array<int> attributes) : parent_(parent), from_(from), attributes_(attributes)
|
||||
const Array<int> &attributes) : parent_(&parent), from_(from),
|
||||
attributes_(attributes)
|
||||
{
|
||||
if (parent.Nonconforming())
|
||||
{
|
||||
MFEM_ABORT("SubMesh does not support non-conforming meshes");
|
||||
}
|
||||
|
||||
if (from == From::Domain)
|
||||
{
|
||||
InitMesh(parent.Dimension(), parent.SpaceDimension(), 0, 0, 0);
|
||||
|
||||
std::tie(parent_vertex_ids_,
|
||||
parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
|
||||
parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent, *this,
|
||||
attributes_);
|
||||
}
|
||||
else if (from == From::Boundary)
|
||||
@@ -49,39 +47,83 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
InitMesh(parent.Dimension() - 1, parent.SpaceDimension(), 0, 0, 0);
|
||||
|
||||
std::tie(parent_vertex_ids_,
|
||||
parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent_, *this,
|
||||
parent_element_ids_) = SubMeshUtils::AddElementsToMesh(parent, *this,
|
||||
attributes_, true);
|
||||
}
|
||||
|
||||
FinalizeTopology(true);
|
||||
parent_to_submesh_vertex_ids_.SetSize(parent.GetNV());
|
||||
parent_to_submesh_vertex_ids_ = -1;
|
||||
for (int i = 0; i < parent_vertex_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_vertex_ids_[parent_vertex_ids_[i]] = i;
|
||||
}
|
||||
|
||||
parent_to_submesh_element_ids_.SetSize(from == From::Boundary ? parent.GetNBE()
|
||||
: parent.GetNE());
|
||||
parent_to_submesh_element_ids_ = -1;
|
||||
for (int i = 0; i < parent_element_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_element_ids_[parent_element_ids_[i]] = i;
|
||||
}
|
||||
|
||||
FinalizeTopology(false);
|
||||
|
||||
if (parent.Nonconforming())
|
||||
{
|
||||
ncmesh = new NCSubMesh(*this, *parent.ncmesh, from, attributes);
|
||||
ncsubmesh_ = dynamic_cast<NCSubMesh*>(ncmesh);
|
||||
InitFromNCMesh(*ncsubmesh_);
|
||||
ncsubmesh_->OnMeshUpdated(this);
|
||||
|
||||
// Update the submesh to parent vertex mapping, ncsubmesh_ reordered the
|
||||
// vertices so the map to parent is no longer valid.
|
||||
parent_to_submesh_vertex_ids_ = -1;
|
||||
for (int i = 0; i < parent_vertex_ids_.Size(); i++)
|
||||
{
|
||||
// vertex -> node -> parent node -> parent vertex
|
||||
auto node = ncsubmesh_->vertex_nodeId[i];
|
||||
auto parent_node = ncsubmesh_->parent_node_ids_[node];
|
||||
auto parent_vertex = parent.ncmesh->GetNodeVertex(parent_node);
|
||||
parent_vertex_ids_[i] = parent_vertex;
|
||||
parent_to_submesh_vertex_ids_[parent_vertex] = i;
|
||||
}
|
||||
GenerateNCFaceInfo();
|
||||
SetAttributes();
|
||||
}
|
||||
|
||||
DSTable v2v(parent_->GetNV());
|
||||
parent_->GetVertexToVertexTable(v2v);
|
||||
for (int i = 0; i < NumOfEdges; i++)
|
||||
{
|
||||
Array<int> lv;
|
||||
GetEdgeVertices(i, lv);
|
||||
|
||||
// Find vertices/edge in parent mesh
|
||||
int parent_edge_id = v2v(parent_vertex_ids_[lv[0]],
|
||||
parent_vertex_ids_[lv[1]]);
|
||||
parent_edge_ids_.Append(parent_edge_id);
|
||||
}
|
||||
|
||||
parent_to_submesh_edge_ids_.SetSize(parent.GetNEdges());
|
||||
parent_to_submesh_edge_ids_ = -1;
|
||||
for (int i = 0; i < parent_edge_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_edge_ids_[parent_edge_ids_[i]] = i;
|
||||
}
|
||||
|
||||
if (Dim == 3)
|
||||
{
|
||||
parent_face_ids_ = SubMeshUtils::BuildFaceMap(parent, *this,
|
||||
parent_element_ids_);
|
||||
|
||||
Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
|
||||
int max_bdr_attr = parent.bdr_attributes.Max();
|
||||
|
||||
for (int i = 0; i < NumOfBdrElements; i++)
|
||||
parent_to_submesh_face_ids_.SetSize(parent.GetNFaces());
|
||||
parent_to_submesh_face_ids_ = -1;
|
||||
for (int i = 0; i < parent_face_ids_.Size(); i++)
|
||||
{
|
||||
int pbeid = parent_face_to_be[parent_face_ids_[GetBdrElementFaceIndex(i)]];
|
||||
if (pbeid != -1)
|
||||
{
|
||||
int attr = parent.GetBdrElement(pbeid)->GetAttribute();
|
||||
GetBdrElement(i)->SetAttribute(attr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This case happens when a domain is extracted, but the root parent
|
||||
// mesh didn't have a boundary element on the surface that defined
|
||||
// it's boundary. It still creates a valid mesh, so we allow it.
|
||||
GetBdrElement(i)->SetAttribute(max_bdr_attr + 1);
|
||||
}
|
||||
parent_to_submesh_face_ids_[parent_face_ids_[i]] = i;
|
||||
}
|
||||
|
||||
parent_face_ori_.SetSize(NumOfFaces);
|
||||
|
||||
for (int i = 0; i < NumOfFaces; i++)
|
||||
{
|
||||
Array<int> sub_vert;
|
||||
@@ -95,7 +137,6 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
|
||||
Array<int> par_vert;
|
||||
parent.GetFaceVertices(parent_face_ids_[i], par_vert);
|
||||
|
||||
if (par_vert.Size() == 3)
|
||||
{
|
||||
parent_face_ori_[i] = GetTriOrientation(par_vert, sub_par_vert);
|
||||
@@ -112,6 +153,14 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
{
|
||||
parent_edge_ids_ = SubMeshUtils::BuildFaceMap(parent, *this,
|
||||
parent_element_ids_);
|
||||
|
||||
parent_to_submesh_edge_ids_.SetSize(parent.GetNEdges());
|
||||
parent_to_submesh_edge_ids_ = -1;
|
||||
for (int i = 0; i < parent_edge_ids_.Size(); i++)
|
||||
{
|
||||
parent_to_submesh_edge_ids_[parent_edge_ids_[i]] = i;
|
||||
}
|
||||
|
||||
Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
|
||||
int max_bdr_attr = parent.bdr_attributes.Max();
|
||||
|
||||
@@ -125,9 +174,10 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
}
|
||||
else
|
||||
{
|
||||
// This case happens when a domain is extracted, but the root parent
|
||||
// mesh didn't have a boundary element on the surface that defined
|
||||
// it's boundary. It still creates a valid mesh, so we allow it.
|
||||
// This case happens when a domain is extracted, but the root
|
||||
// parent mesh didn't have a boundary element on the surface that
|
||||
// defined it's boundary. It still creates a valid mesh, so we
|
||||
// allow it.
|
||||
GetBdrElement(i)->SetAttribute(max_bdr_attr + 1);
|
||||
}
|
||||
}
|
||||
@@ -172,6 +222,19 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
}
|
||||
}
|
||||
|
||||
SubMeshUtils::AddBoundaryElements(*this);
|
||||
|
||||
if (Dim > 1)
|
||||
{
|
||||
delete el_to_edge;
|
||||
el_to_edge = new Table;
|
||||
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
|
||||
}
|
||||
if (Dim > 2)
|
||||
{
|
||||
GetElementToFaceTable();
|
||||
}
|
||||
|
||||
// If the parent Mesh has nodes and therefore is defined on a higher order
|
||||
// geometry, we define this SubMesh as a curved Mesh and transfer the
|
||||
// GridFunction from the parent Mesh to the SubMesh.
|
||||
@@ -195,8 +258,7 @@ SubMesh::SubMesh(const Mesh &parent, From from,
|
||||
|
||||
void SubMesh::Transfer(const GridFunction &src, GridFunction &dst)
|
||||
{
|
||||
TransferMap map(src, dst);
|
||||
map.Transfer(src, dst);
|
||||
CreateTransferMap(src, dst).Transfer(src, dst);
|
||||
}
|
||||
|
||||
TransferMap SubMesh::CreateTransferMap(const GridFunction &src,
|
||||
|
||||
+88
-22
@@ -14,11 +14,12 @@
|
||||
|
||||
#include "../mesh.hpp"
|
||||
#include "transfermap.hpp"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
class NCSubMesh;
|
||||
|
||||
/**
|
||||
* @brief Subdomain representation of a topological parent in another Mesh.
|
||||
*
|
||||
@@ -41,17 +42,18 @@ namespace mfem
|
||||
*/
|
||||
class SubMesh : public Mesh
|
||||
{
|
||||
friend class NCSubMesh;
|
||||
public:
|
||||
/// Indicator from which part of the parent Mesh the SubMesh is created.
|
||||
enum From
|
||||
enum class From
|
||||
{
|
||||
Domain,
|
||||
Boundary
|
||||
};
|
||||
|
||||
static const int GENERATED_ATTRIBUTE = 900;
|
||||
|
||||
SubMesh() = delete;
|
||||
SubMesh(SubMesh &&) = default;
|
||||
SubMesh &operator=(SubMesh &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Create a domain SubMesh from its parent.
|
||||
@@ -64,7 +66,7 @@ public:
|
||||
* @param[in] domain_attributes Domain attributes to extract
|
||||
*/
|
||||
static SubMesh CreateFromDomain(const Mesh &parent,
|
||||
Array<int> domain_attributes);
|
||||
const Array<int> &domain_attributes);
|
||||
|
||||
/**
|
||||
* @brief Create a surface SubMesh from its parent.
|
||||
@@ -78,22 +80,18 @@ public:
|
||||
|
||||
*/
|
||||
static SubMesh CreateFromBoundary(const Mesh &parent,
|
||||
Array<int> boundary_attributes);
|
||||
const Array<int> &boundary_attributes);
|
||||
|
||||
/**
|
||||
* @brief Get the parent Mesh object
|
||||
*
|
||||
*/
|
||||
///Get the parent Mesh object
|
||||
const Mesh* GetParent() const
|
||||
{
|
||||
return &parent_;
|
||||
return parent_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the From indicator.
|
||||
*
|
||||
* Indicates whether the SubMesh has been created from a domain or
|
||||
* surface.
|
||||
* Indicates whether the SubMesh has been created from a domain or surface.
|
||||
*/
|
||||
From GetFrom() const
|
||||
{
|
||||
@@ -113,13 +111,23 @@ public:
|
||||
/**
|
||||
* @brief Get the face id map
|
||||
*
|
||||
* SubMesh element id (array index) to parent Mesh face id.
|
||||
* SubMesh face id (array index) to parent Mesh face id.
|
||||
*/
|
||||
const Array<int>& GetParentFaceIDMap() const
|
||||
{
|
||||
return parent_face_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the edge id map
|
||||
*
|
||||
* Submesh edge id (array index) to parent Mesh edge id.
|
||||
*/
|
||||
const Array<int>& GetParentEdgeIDMap() const
|
||||
{
|
||||
return parent_edge_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the relative face orientations
|
||||
*
|
||||
@@ -140,6 +148,47 @@ public:
|
||||
return parent_vertex_ids_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the submesh element corresponding to a parent element. -1 ==
|
||||
* not present.
|
||||
* @param pe The parent element id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshElementFromParent(int pe) const
|
||||
{
|
||||
return pe == -1 ? pe : parent_to_submesh_element_ids_[pe];
|
||||
}
|
||||
/**
|
||||
* @brief Get the submesh vertex corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pv The parent vertex id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshVertexFromParent(int pv) const
|
||||
{
|
||||
return pv == -1 ? pv : parent_to_submesh_vertex_ids_[pv];
|
||||
}
|
||||
/**
|
||||
* @brief Get the submesh edge corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pe The parent edge id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshEdgeFromParent(int pe) const
|
||||
{
|
||||
return pe == -1 ? pe : parent_to_submesh_edge_ids_[pe];
|
||||
}
|
||||
/**
|
||||
* @brief Get the submesh face corresponding to a parent element. -1 == not
|
||||
* present.
|
||||
* @param pf The parent face id.
|
||||
* @return int
|
||||
*/
|
||||
int GetSubMeshFaceFromParent(int pf) const
|
||||
{
|
||||
return pf == -1 ? pf : parent_to_submesh_face_ids_[pf];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Transfer the dofs of a GridFunction.
|
||||
*
|
||||
@@ -156,8 +205,8 @@ public:
|
||||
/**
|
||||
* @brief Create a Transfer Map object.
|
||||
*
|
||||
* The @a src GridFunction can either be defined on a Mesh or a
|
||||
* SubMesh and is transferred appropriately.
|
||||
* The @a src GridFunction can either be defined on a Mesh or a SubMesh and
|
||||
* is transferred appropriately.
|
||||
*
|
||||
* @note Either @a src or @a dst has to be defined on a SubMesh.
|
||||
*/
|
||||
@@ -176,10 +225,13 @@ public:
|
||||
|
||||
private:
|
||||
/// Private constructor
|
||||
SubMesh(const Mesh &parent, From from, Array<int> attributes);
|
||||
SubMesh(const Mesh &parent, From from, const Array<int> &attributes);
|
||||
|
||||
/// The parent Mesh
|
||||
const Mesh &parent_;
|
||||
/// The parent Mesh. Not owned.
|
||||
const Mesh *parent_;
|
||||
|
||||
/// Optional nonconformal submesh. Managed via ncmesh pointer in base class.
|
||||
NCSubMesh *ncsubmesh_;
|
||||
|
||||
/// Indicator from which part of the parent ParMesh the ParSubMesh is going
|
||||
/// to be created.
|
||||
@@ -205,11 +257,25 @@ private:
|
||||
/// face ids.
|
||||
Array<int> parent_face_ids_;
|
||||
|
||||
/// Mapping from SubMesh face ids (index of the array), to the orientation
|
||||
/// of the face relative to the parent face.
|
||||
/// Mapping from SubMesh face ids (index of the array), to the orientation of
|
||||
/// the face relative to the parent face.
|
||||
Array<int> parent_face_ori_;
|
||||
|
||||
Array<int> face_to_be;
|
||||
/// Mapping from parent Mesh vertex ids (index of the array), to the SubMesh
|
||||
/// vertex ids. Inverse map of parent_element_ids_.
|
||||
Array<int> parent_to_submesh_element_ids_;
|
||||
|
||||
/// Mapping from parent Mesh vertex ids (index of the array), to the SubMesh
|
||||
/// vertex ids. Inverse map of parent_vertex_ids_.
|
||||
Array<int> parent_to_submesh_vertex_ids_;
|
||||
|
||||
/// Mapping from parent Mesh edge ids (index of the array), to the SubMesh
|
||||
/// edge ids. Inverse map of parent_edge_ids_.
|
||||
Array<int> parent_to_submesh_edge_ids_;
|
||||
|
||||
/// Mapping from parent Mesh face ids (index of the array), to the SubMesh
|
||||
/// face ids. Inverse map of parent_face_ids_.
|
||||
Array<int> parent_to_submesh_face_ids_;
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+644
-16
@@ -10,6 +10,12 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "submesh_utils.hpp"
|
||||
#include "ncsubmesh.hpp"
|
||||
#include "submesh.hpp"
|
||||
#include "pncsubmesh.hpp"
|
||||
#include "psubmesh.hpp"
|
||||
|
||||
#include <numeric>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -31,7 +37,8 @@ int UniqueIndexGenerator::Get(int i, bool &new_index)
|
||||
}
|
||||
}
|
||||
|
||||
bool ElementHasAttribute(const Element &el, const Array<int> &attributes)
|
||||
template <typename ElementT>
|
||||
bool ElementHasAttribute(const ElementT &el, const Array<int> &attributes)
|
||||
{
|
||||
for (int a = 0; a < attributes.Size(); a++)
|
||||
{
|
||||
@@ -49,41 +56,38 @@ AddElementsToMesh(const Mesh& parent,
|
||||
const Array<int> &attributes,
|
||||
bool from_boundary)
|
||||
{
|
||||
Array<int> parent_vertex_ids, parent_element_ids;
|
||||
UniqueIndexGenerator vertex_ids;
|
||||
Array<int> parent_vertex_ids, parent_element_ids;
|
||||
Array<int> vert, submesh_vert;
|
||||
|
||||
const int ne = from_boundary ? parent.GetNBE() : parent.GetNE();
|
||||
for (int i = 0; i < ne; i++)
|
||||
{
|
||||
const Element *pel = from_boundary ?
|
||||
parent.GetBdrElement(i) : parent.GetElement(i);
|
||||
if (!ElementHasAttribute(*pel, attributes)) { continue; }
|
||||
|
||||
Array<int> v;
|
||||
pel->GetVertices(v);
|
||||
Array<int> submesh_v(v.Size());
|
||||
|
||||
for (int iv = 0; iv < v.Size(); iv++)
|
||||
if (!HasAttribute(*pel, attributes)) { continue; }
|
||||
pel->GetVertices(vert);
|
||||
submesh_vert.SetSize(vert.Size());
|
||||
for (int iv = 0; iv < vert.Size(); iv++)
|
||||
{
|
||||
bool new_vertex;
|
||||
int mesh_vertex_id = v[iv];
|
||||
int mesh_vertex_id = vert[iv];
|
||||
int submesh_vertex_id = vertex_ids.Get(mesh_vertex_id, new_vertex);
|
||||
if (new_vertex)
|
||||
{
|
||||
mesh.AddVertex(parent.GetVertex(mesh_vertex_id));
|
||||
parent_vertex_ids.Append(mesh_vertex_id);
|
||||
}
|
||||
submesh_v[iv] = submesh_vertex_id;
|
||||
submesh_vert[iv] = submesh_vertex_id;
|
||||
}
|
||||
|
||||
Element *el = mesh.NewElement(from_boundary ?
|
||||
parent.GetBdrElementType(i) : parent.GetElementType(i));
|
||||
el->SetVertices(submesh_v);
|
||||
el->SetVertices(submesh_vert);
|
||||
el->SetAttribute(pel->GetAttribute());
|
||||
mesh.AddElement(el);
|
||||
parent_element_ids.Append(i);
|
||||
}
|
||||
return std::tuple<Array<int>, Array<int>>(parent_vertex_ids,
|
||||
parent_element_ids);
|
||||
return {parent_vertex_ids, parent_element_ids};
|
||||
}
|
||||
|
||||
void BuildVdofToVdofMap(const FiniteElementSpace& subfes,
|
||||
@@ -94,7 +98,6 @@ void BuildVdofToVdofMap(const FiniteElementSpace& subfes,
|
||||
{
|
||||
auto *m = subfes.GetMesh();
|
||||
vdof_to_vdof_map.SetSize(subfes.GetVSize());
|
||||
|
||||
const int vdim = parentfes.GetVDim();
|
||||
|
||||
IntegrationPointTransformation Tr;
|
||||
@@ -188,6 +191,29 @@ void BuildVdofToVdofMap(const FiniteElementSpace& subfes,
|
||||
(sub_sign * parent_sign > 0.0) ? parent_vdof : (-1-parent_vdof);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MFEM_DEBUG
|
||||
auto tmp = vdof_to_vdof_map;
|
||||
tmp.Sort();
|
||||
tmp.Unique();
|
||||
|
||||
if (tmp.Size() != vdof_to_vdof_map.Size())
|
||||
{
|
||||
std::stringstream msg;
|
||||
for (int i = 0; i < vdof_to_vdof_map.Size(); i++)
|
||||
for (int j = i + 1; j < vdof_to_vdof_map.Size(); j++)
|
||||
{
|
||||
auto x = vdof_to_vdof_map[i];
|
||||
auto y = vdof_to_vdof_map[j];
|
||||
if (x == y)
|
||||
{
|
||||
msg << "i " << i << " (" << x << ") j " << j << " (" << y << ")\n";
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("vdof_to_vdof_map should be 1 to 1:\n" << msg.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
Array<int> BuildFaceMap(const Mesh& pm, const Mesh& sm,
|
||||
@@ -227,5 +253,607 @@ Array<int> BuildFaceMap(const Mesh& pm, const Mesh& sm,
|
||||
return pfids;
|
||||
}
|
||||
|
||||
template <typename SubMeshT>
|
||||
void AddBoundaryElements(SubMeshT &mesh,
|
||||
const std::unordered_map<int,int> &lface_to_boundary_attribute)
|
||||
{
|
||||
mesh.Dimension();
|
||||
const int num_codim_1 = [&mesh]()
|
||||
{
|
||||
auto Dim = mesh.Dimension();
|
||||
if (Dim == 1) { return mesh.GetNV(); }
|
||||
else if (Dim == 2) { return mesh.GetNEdges(); }
|
||||
else if (Dim == 3) { return mesh.GetNFaces(); }
|
||||
else { MFEM_ABORT("Invalid dimension."); return -1; }
|
||||
}();
|
||||
|
||||
if (mesh.Dimension() == 3)
|
||||
{
|
||||
// In 3D we check for `bel_to_edge`. It shouldn't have been set
|
||||
// previously.
|
||||
mesh.DeleteBoundaryElementToEdge();
|
||||
}
|
||||
int NumOfBdrElements = 0;
|
||||
for (int i = 0; i < num_codim_1; i++)
|
||||
{
|
||||
if (mesh.GetFaceInformation(i).IsBoundary())
|
||||
{
|
||||
NumOfBdrElements++;
|
||||
}
|
||||
}
|
||||
|
||||
Array<Element *> boundary;
|
||||
Array<int> be_to_face;
|
||||
boundary.Reserve(NumOfBdrElements);
|
||||
be_to_face.Reserve(NumOfBdrElements);
|
||||
|
||||
const auto &parent = *mesh.GetParent();
|
||||
const auto &parent_face_ids = mesh.GetParentFaceIDMap();
|
||||
const auto &parent_edge_ids = mesh.GetParentEdgeIDMap();
|
||||
const auto &parent_vertex_ids = mesh.GetParentVertexIDMap();
|
||||
const auto &parent_face_to_be = parent.GetFaceToBdrElMap();
|
||||
const auto &face_to_be = mesh.GetFaceToBdrElMap();
|
||||
int max_bdr_attr = parent.bdr_attributes.Max();
|
||||
for (int i = 0; i < num_codim_1; i++)
|
||||
{
|
||||
auto pfid = [&](int i)
|
||||
{
|
||||
switch (mesh.Dimension())
|
||||
{
|
||||
case 3: return parent_face_ids[i];
|
||||
case 2: return parent_edge_ids[i];
|
||||
case 1: return parent_vertex_ids[i];
|
||||
}
|
||||
MFEM_ABORT("!");
|
||||
return -1;
|
||||
};
|
||||
if (mesh.GetFaceInformation(i).IsBoundary()
|
||||
&& (face_to_be.IsEmpty() || face_to_be[i] == -1))
|
||||
{
|
||||
auto * be = mesh.GetFace(i)->Duplicate(&mesh);
|
||||
|
||||
if (mesh.GetFrom() == SubMesh::From::Domain && mesh.Dimension() >= 2)
|
||||
{
|
||||
int pbeid = parent_face_to_be[pfid(i)];
|
||||
if (pbeid != -1)
|
||||
{
|
||||
be->SetAttribute(parent.GetBdrAttribute(pbeid));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ghost_attr = lface_to_boundary_attribute.find(pfid(i));
|
||||
int battr = ghost_attr != lface_to_boundary_attribute.end() ?
|
||||
ghost_attr->second : max_bdr_attr + 1;
|
||||
be->SetAttribute(battr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ghost_attr = lface_to_boundary_attribute.find(pfid(i));
|
||||
int battr = ghost_attr != lface_to_boundary_attribute.end() ?
|
||||
ghost_attr->second : max_bdr_attr + 1;
|
||||
be->SetAttribute(battr);
|
||||
}
|
||||
be_to_face.Append(i);
|
||||
boundary.Append(be);
|
||||
}
|
||||
}
|
||||
|
||||
if (mesh.GetFrom() == SubMesh::From::Domain && mesh.Dimension() >= 2)
|
||||
{
|
||||
// Search for and count interior boundary elements
|
||||
int InteriorBdrElems = 0;
|
||||
for (int i=0; i<parent.GetNBE(); i++)
|
||||
{
|
||||
const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const int submeshFaceIdx =
|
||||
mesh.Dimension() == 3 ?
|
||||
mesh.GetSubMeshFaceFromParent(parentFaceIdx) :
|
||||
mesh.GetSubMeshEdgeFromParent(parentFaceIdx);
|
||||
|
||||
if (submeshFaceIdx == -1) { continue; }
|
||||
if (mesh.GetFaceInformation(submeshFaceIdx).IsBoundary()) { continue; }
|
||||
InteriorBdrElems++;
|
||||
}
|
||||
|
||||
if (InteriorBdrElems > 0)
|
||||
{
|
||||
NumOfBdrElements += InteriorBdrElems;
|
||||
boundary.Reserve(NumOfBdrElements);
|
||||
be_to_face.Reserve(NumOfBdrElements);
|
||||
|
||||
// Search for and transfer interior boundary elements
|
||||
for (int i = 0; i < parent.GetNBE(); i++)
|
||||
{
|
||||
const int parentFaceIdx = parent.GetBdrElementFaceIndex(i);
|
||||
const int submeshFaceIdx =
|
||||
mesh.GetSubMeshFaceFromParent(parentFaceIdx);
|
||||
|
||||
if (submeshFaceIdx == -1) { continue; }
|
||||
if (mesh.GetFaceInformation(submeshFaceIdx).IsBoundary())
|
||||
{ continue; }
|
||||
|
||||
auto * be = mesh.GetFace(submeshFaceIdx)->Duplicate(&mesh);
|
||||
be->SetAttribute(parent.GetBdrAttribute(i));
|
||||
boundary.Append(be);
|
||||
be_to_face.Append(submeshFaceIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
mesh.AddBdrElements(boundary, be_to_face);
|
||||
}
|
||||
|
||||
// Explicit instantiations
|
||||
template void AddBoundaryElements(SubMesh &mesh,
|
||||
const std::unordered_map<int,int> &);
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
template void AddBoundaryElements(ParSubMesh &mesh,
|
||||
const std::unordered_map<int,int> &);
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* @brief Helper class for storing and comparing arrays of face nodes.
|
||||
* @details The comparison operator uses the sorted nodes and a lexicographic
|
||||
* compare so that two different orientations of the same set of nodes will be
|
||||
* identical. The actual nodes are stored unsorted as the ordering is important
|
||||
* for constructing the leaf-root relations.
|
||||
*/
|
||||
struct FaceNodes
|
||||
{
|
||||
std::array<int, NCMesh::MaxFaceNodes> nodes;
|
||||
bool operator<(FaceNodes t2) const
|
||||
{
|
||||
std::array<int, NCMesh::MaxFaceNodes> t1 = nodes;
|
||||
std::sort(t1.begin(), t1.end());
|
||||
std::sort(t2.nodes.begin(), t2.nodes.end());
|
||||
return std::lexicographical_compare(t1.begin(), t1.end(),
|
||||
t2.nodes.begin(), t2.nodes.end());
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Establish the Geometry::Type from an array of nodes
|
||||
*
|
||||
* @param nodes
|
||||
* @return Geometry::Type
|
||||
*/
|
||||
Geometry::Type FaceGeomFromNodes(const std::array<int, NCMesh::MaxFaceNodes>
|
||||
&nodes)
|
||||
{
|
||||
if (nodes[3] == -1) { return Geometry::Type::TRIANGLE; }
|
||||
if (nodes[0] == nodes[1] && nodes[2] == nodes[3]) { return Geometry::Type::SEGMENT; }
|
||||
return Geometry::Type::SQUARE;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
template<typename NCSubMeshT>
|
||||
void ConstructFaceTree(NCSubMeshT &submesh, const Array<int> &attributes)
|
||||
{
|
||||
// Convenience references to avoid `submesh.` repeatedly.
|
||||
auto &parent_node_ids = submesh.parent_node_ids_;
|
||||
auto &parent_element_ids = submesh.parent_element_ids_;
|
||||
auto &parent_to_submesh_node_ids = submesh.parent_to_submesh_node_ids_;
|
||||
auto &parent_to_submesh_element_ids = submesh.parent_to_submesh_element_ids_;
|
||||
const auto &parent = *submesh.GetParent();
|
||||
|
||||
// Collect parent vertex nodes to add in sequence. Map from parent nodes to
|
||||
// the new element in the ncsubmesh.
|
||||
UniqueIndexGenerator node_ids;
|
||||
std::map<FaceNodes, int> pnodes_new_elem;
|
||||
std::set<int> new_nodes;
|
||||
parent_to_submesh_element_ids.reserve(parent.GetNumFaces());
|
||||
parent_element_ids.Reserve(parent.GetNumFaces());
|
||||
// Base class cast then const cast because GetFaceList uses just in time
|
||||
// construction.
|
||||
const auto &face_list = const_cast<NCMesh&>(static_cast<const NCMesh&>
|
||||
(parent)).GetFaceList();
|
||||
// Double indexing loop because begin() and end() do not align with index 0
|
||||
// and size-1.
|
||||
for (int i = 0, ipe = 0; ipe < parent.GetNumFaces(); i++)
|
||||
{
|
||||
const auto &face = parent.GetFace(i);
|
||||
if (face.Unused()) { continue; }
|
||||
ipe++; // actual possible parent element.
|
||||
if (!HasAttribute(face, attributes)
|
||||
|| face_list.GetMeshIdType(face.index) == NCMesh::NCList::MeshIdType::MASTER
|
||||
) { continue; }
|
||||
|
||||
FaceNodes fn{submesh.parent_->FindFaceNodes(face)};
|
||||
if (pnodes_new_elem.find(fn) != pnodes_new_elem.end()) { continue; }
|
||||
|
||||
// TODO: Internal nc submesh can be constructed and solved on, but the
|
||||
// transfer to the parent mesh can be erroneous, this is likely due to not
|
||||
// treating the changing orientation of internal faces for ncmesh within
|
||||
// the ptransfermap.
|
||||
MFEM_ASSERT(face.elem[0] < 0 || face.elem[1] < 0,
|
||||
"Internal nonconforming boundaries are not reliably supported yet.");
|
||||
auto face_geom = FaceGeomFromNodes(fn.nodes);
|
||||
int new_elem_id = submesh.AddElement(face_geom, face.attribute);
|
||||
|
||||
// Rank needs to be established by presence (or lack of) in the submesh.
|
||||
submesh.elements[new_elem_id].rank = [&parent, &face]()
|
||||
{
|
||||
auto rank0 = face.elem[0] >= 0 ? parent.GetElement(face.elem[0]).rank : -1;
|
||||
auto rank1 = face.elem[1] >= 0 ? parent.GetElement(face.elem[1]).rank : -1;
|
||||
if (rank0 < 0) { return rank1; }
|
||||
if (rank1 < 0) { return rank0; }
|
||||
return rank0 < rank1 ? rank0 : rank1;
|
||||
}();
|
||||
pnodes_new_elem[fn] = new_elem_id;
|
||||
parent_element_ids.Append(i);
|
||||
parent_to_submesh_element_ids[i] = new_elem_id;
|
||||
|
||||
// Copy in the parent nodes. These will be relabeled once the tree is
|
||||
// built.
|
||||
std::copy(fn.nodes.begin(), fn.nodes.end(), submesh.elements[new_elem_id].node);
|
||||
for (auto x : fn.nodes)
|
||||
if (x != -1)
|
||||
{
|
||||
new_nodes.insert(x);
|
||||
}
|
||||
auto &gi = submesh.GI[face_geom];
|
||||
gi.InitGeom(face_geom);
|
||||
for (int e = 0; e < gi.ne; e++)
|
||||
{
|
||||
new_nodes.insert(submesh.ParentNodes().FindId(fn.nodes[gi.edges[e][0]],
|
||||
fn.nodes[gi.edges[e][1]]));
|
||||
}
|
||||
|
||||
/*
|
||||
- Check not top level face
|
||||
- Check for parent of the newly entered element
|
||||
- if not present, add in
|
||||
- if present but different order and this path is non-ambiguous,
|
||||
reorder so consistent with child elements.
|
||||
- Set .parent in the newly entered element
|
||||
Break if top level face or joined existing branch (without reordering).
|
||||
|
||||
child element indices will be set afterwards because the orientation can change
|
||||
during traversal.
|
||||
*/
|
||||
bool root_path_is_ambiguous=false;
|
||||
bool fix_parent = false, tri_face = (face_geom == Geometry::TRIANGLE);
|
||||
while (true)
|
||||
{
|
||||
int child = submesh.parent_->ParentFaceNodes(fn.nodes);
|
||||
if (tri_face && child == 3)
|
||||
{
|
||||
// Traversing a central triangle face involves flipping the face orientation.
|
||||
// Do not use this pathway for reordering any parent face's nodes.
|
||||
root_path_is_ambiguous = true;
|
||||
}
|
||||
|
||||
if (child == -1) // A root face
|
||||
{
|
||||
submesh.elements[new_elem_id].parent = -1;
|
||||
break;
|
||||
}
|
||||
auto pelem = pnodes_new_elem.find(fn);
|
||||
bool new_parent = pelem == pnodes_new_elem.end();
|
||||
if (new_parent)
|
||||
{
|
||||
// Add in this parent
|
||||
int pelem_id = submesh.AddElement(FaceGeomFromNodes(fn.nodes), face.attribute);
|
||||
pelem = pnodes_new_elem.emplace(fn, pelem_id).first;
|
||||
auto parent_face_id = submesh.ParentFaces().FindId(fn.nodes[0], fn.nodes[1],
|
||||
fn.nodes[2],
|
||||
fn.nodes[3]);
|
||||
parent_element_ids.Append(parent_face_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are two scenarios where the parent nodes should be
|
||||
// rearranged:
|
||||
// 1. The found face is a slave, then the master might have been
|
||||
// added in reverse orientation
|
||||
// 2. The parent face was added from the central face of a triangle,
|
||||
// the orientation of the parent face is only fixed relative to
|
||||
// the outer child faces not the interior. If either of these
|
||||
// scenarios, and there's a mismatch, then reorder the parent and
|
||||
// all ancestors if necessary.
|
||||
if (!root_path_is_ambiguous &&
|
||||
!std::equal(fn.nodes.begin(), fn.nodes.end(), pelem->first.nodes.begin()))
|
||||
{
|
||||
fix_parent = true;
|
||||
auto pelem_id = pelem->second;
|
||||
MFEM_ASSERT(!submesh.elements[pelem_id].IsLeaf(), pelem_id);
|
||||
|
||||
// Re-key the map, the existing entry is inconsistent with the tree.
|
||||
pnodes_new_elem.erase(pelem->first);
|
||||
pelem = pnodes_new_elem.emplace(fn, pelem_id).first;
|
||||
}
|
||||
}
|
||||
// Ensure parent element is marked as non-leaf, and attach to the child.
|
||||
submesh.elements[pelem->second].ref_type = submesh.Dim == 2 ? Refinement::XY :
|
||||
Refinement::X;
|
||||
submesh.elements[new_elem_id].parent = pelem->second;
|
||||
|
||||
// If this was neither new nor a fixed parent, the higher levels of the
|
||||
// tree have been built, otherwise we recurse up the tree to add more parents, or
|
||||
// to potentially fix any ambiguously added FaceNodes.
|
||||
if (!new_parent && !fix_parent) { break; }
|
||||
|
||||
new_elem_id = pelem->second;
|
||||
}
|
||||
}
|
||||
parent_element_ids.ShrinkToFit();
|
||||
MFEM_ASSERT(parent_element_ids.Size() == submesh.elements.Size(),
|
||||
parent_element_ids.Size() << ' ' << submesh.elements.Size());
|
||||
|
||||
// All elements have been added, with their parents, and the nodal orientation of parents is
|
||||
// consistent with children, but the children indices have not been marked. Traverse the
|
||||
// tree from root to leaf to fill the child arrays.
|
||||
for (const auto & fn_elem : pnodes_new_elem)
|
||||
{
|
||||
auto fn = fn_elem.first;
|
||||
const auto &child_elem = submesh.elements[fn_elem.second];
|
||||
if (child_elem.parent == -1) { continue; }
|
||||
int child = submesh.parent_->ParentFaceNodes(fn.nodes);
|
||||
MFEM_ASSERT(pnodes_new_elem[fn] == child_elem.parent,
|
||||
pnodes_new_elem[fn] << ' ' << child_elem.parent);
|
||||
MFEM_ASSERT(submesh.elements[child_elem.parent].ref_type != char(0),
|
||||
int(submesh.elements[child_elem.parent].ref_type));
|
||||
submesh.elements[child_elem.parent].child[child] = fn_elem.second;
|
||||
}
|
||||
|
||||
/*
|
||||
All elements have been added into the tree but a) The nodes are all from
|
||||
the parent ncmesh b) The nodes do not know their parents c) The element
|
||||
ordering is wrong, root elements are not first d) The parent and child
|
||||
element numbers reflect the incorrect ordering
|
||||
|
||||
1. Add in nodes in the same order from the parent ncmesh
|
||||
2. Compute reordering of elements with parent elements first, that is
|
||||
stable across processors.
|
||||
*/
|
||||
// Build an inverse (and consecutive) map.
|
||||
Array<FaceNodes> new_elem_to_parent_face_nodes(pnodes_new_elem.size());
|
||||
for (const auto &kv : pnodes_new_elem)
|
||||
{
|
||||
new_elem_to_parent_face_nodes[kv.second] = kv.first;
|
||||
}
|
||||
pnodes_new_elem.clear(); // no longer needed
|
||||
|
||||
// Add new nodes preserving parent mesh ordering
|
||||
parent_node_ids.Reserve(static_cast<int>(new_nodes.size()));
|
||||
parent_to_submesh_node_ids.reserve(new_nodes.size());
|
||||
for (auto n : new_nodes)
|
||||
{
|
||||
bool new_node;
|
||||
auto new_node_id = node_ids.Get(n, new_node);
|
||||
MFEM_ASSERT(new_node, "!");
|
||||
submesh.nodes.Alloc(new_node_id, new_node_id, new_node_id);
|
||||
parent_node_ids.Append(n);
|
||||
parent_to_submesh_node_ids[n] = new_node_id;
|
||||
}
|
||||
parent_node_ids.ShrinkToFit();
|
||||
new_nodes.clear(); // not needed any more.
|
||||
|
||||
// Comparator for deciding order of elements. Building the ordering from the
|
||||
// parent ncmesh ensures the root ordering is common across ranks.
|
||||
auto comp_elements = [&](int l, int r)
|
||||
{
|
||||
const auto &elem_l = submesh.elements[l];
|
||||
const auto &elem_r = submesh.elements[r];
|
||||
if (elem_l.parent == elem_r.parent)
|
||||
{
|
||||
const auto &fnl = new_elem_to_parent_face_nodes[l].nodes;
|
||||
const auto &fnr = new_elem_to_parent_face_nodes[r].nodes;
|
||||
return std::lexicographical_compare(fnl.begin(), fnl.end(), fnr.begin(),
|
||||
fnr.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
return elem_l.parent < elem_r.parent;
|
||||
}
|
||||
};
|
||||
Array<int> indices(submesh.elements.Size());
|
||||
auto parental_sorted = [&]()
|
||||
{
|
||||
std::iota(indices.begin(), indices.end(), 0);
|
||||
return std::is_sorted(indices.begin(), indices.end(), comp_elements);
|
||||
};
|
||||
|
||||
Array<int> new_to_old(submesh.elements.Size()),
|
||||
old_to_new(submesh.elements.Size());
|
||||
while (!parental_sorted())
|
||||
{
|
||||
// Stably reorder elements in order of refinement, and by parental nodes
|
||||
// within a nuclear family.
|
||||
new_to_old.SetSize(submesh.elements.Size()),
|
||||
old_to_new.SetSize(submesh.elements.Size());
|
||||
std::iota(new_to_old.begin(), new_to_old.end(), 0);
|
||||
std::stable_sort(new_to_old.begin(), new_to_old.end(), comp_elements);
|
||||
// Build the inverse relation for converting the old elements to new
|
||||
for (int i = 0; i < submesh.elements.Size(); i++)
|
||||
{
|
||||
old_to_new[new_to_old[i]] = i;
|
||||
}
|
||||
|
||||
// Permute whilst reordering new_to_old. Avoids unnecessary copies.
|
||||
Permute(std::move(new_to_old), submesh.elements, parent_element_ids,
|
||||
new_elem_to_parent_face_nodes);
|
||||
parent_to_submesh_element_ids.clear();
|
||||
for (int i = 0; i < parent_element_ids.Size(); i++)
|
||||
{
|
||||
if (parent_element_ids[i] == -1) {continue;}
|
||||
parent_to_submesh_element_ids[parent_element_ids[i]] = i;
|
||||
}
|
||||
|
||||
// Apply the new ordering to child and parent elements
|
||||
for (auto &elem : submesh.elements)
|
||||
{
|
||||
if (!elem.IsLeaf())
|
||||
{
|
||||
// Parent rank is minimum of child ranks.
|
||||
elem.rank = std::numeric_limits<int>::max();
|
||||
for (int c = 0; c < NCMesh::MaxElemChildren && elem.child[c] >= 0; c++)
|
||||
{
|
||||
elem.child[c] = old_to_new[elem.child[c]];
|
||||
elem.rank = std::min(elem.rank, submesh.elements[elem.child[c]].rank);
|
||||
}
|
||||
}
|
||||
elem.parent = elem.parent == -1 ? -1 : old_to_new[elem.parent];
|
||||
}
|
||||
}
|
||||
|
||||
// Apply new node ordering to relations, and sign in on edges/vertices
|
||||
for (auto &elem : submesh.elements)
|
||||
{
|
||||
if (elem.IsLeaf())
|
||||
{
|
||||
bool new_id;
|
||||
auto &gi = submesh.GI[elem.Geom()];
|
||||
gi.InitGeom(elem.Geom());
|
||||
for (int e = 0; e < gi.ne; e++)
|
||||
{
|
||||
const int pid = submesh.ParentNodes().FindId(
|
||||
elem.node[gi.edges[e][0]], elem.node[gi.edges[e][1]]);
|
||||
MFEM_ASSERT(pid >= 0,
|
||||
elem.node[gi.edges[e][0]] << ' ' << elem.node[gi.edges[e][1]]);
|
||||
auto submesh_node_id = node_ids.Get(pid, new_id);
|
||||
MFEM_ASSERT(!new_id, "!");
|
||||
submesh.nodes[submesh_node_id].edge_refc++;
|
||||
}
|
||||
for (int n = 0; n < gi.nv; n++)
|
||||
{
|
||||
MFEM_ASSERT(parent_to_submesh_node_ids.find(elem.node[n]) !=
|
||||
parent_to_submesh_node_ids.end(), "!");
|
||||
elem.node[n] = parent_to_submesh_node_ids[elem.node[n]];
|
||||
submesh.nodes[elem.node[n]].vert_refc++;
|
||||
}
|
||||
// Register faces
|
||||
for (int f = 0; f < gi.nf; f++)
|
||||
{
|
||||
auto *face = submesh.faces.Get(
|
||||
elem.node[gi.faces[f][0]],
|
||||
elem.node[gi.faces[f][1]],
|
||||
elem.node[gi.faces[f][2]],
|
||||
elem.node[gi.faces[f][3]]);
|
||||
face->attribute = -1;
|
||||
face->index = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit instantiations
|
||||
template void ConstructFaceTree(NCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
#ifdef MFEM_USE_MPI
|
||||
template void ConstructFaceTree(ParNCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
#endif
|
||||
|
||||
template <typename NCSubMeshT>
|
||||
void ConstructVolumeTree(NCSubMeshT &submesh, const Array<int> &attributes)
|
||||
{
|
||||
// Convenience references to avoid `submesh.` repeatedly.
|
||||
auto &parent_node_ids = submesh.parent_node_ids_;
|
||||
auto &parent_element_ids = submesh.parent_element_ids_;
|
||||
auto &parent_to_submesh_node_ids = submesh.parent_to_submesh_node_ids_;
|
||||
auto &parent_to_submesh_element_ids = submesh.parent_to_submesh_element_ids_;
|
||||
const auto &parent = *submesh.GetParent();
|
||||
|
||||
UniqueIndexGenerator node_ids;
|
||||
parent_to_submesh_element_ids.reserve(parent.GetNumElements());
|
||||
std::set<int> new_nodes;
|
||||
for (int ipe = 0; ipe < parent.GetNumElements(); ipe++)
|
||||
{
|
||||
const auto& pe = parent.GetElement(ipe);
|
||||
if (!HasAttribute(pe, attributes)) { continue; }
|
||||
const int elem_id = submesh.AddElement(pe);
|
||||
auto &el = submesh.elements[elem_id];
|
||||
parent_element_ids.Append(ipe); // submesh -> parent
|
||||
parent_to_submesh_element_ids[ipe] = elem_id; // parent -> submesh
|
||||
if (!pe.IsLeaf()) { continue; }
|
||||
const auto gi = submesh.GI[pe.Geom()];
|
||||
for (int n = 0; n < gi.nv; n++)
|
||||
{
|
||||
new_nodes.insert(el.node[n]);
|
||||
}
|
||||
for (int e = 0; e < gi.ne; e++)
|
||||
{
|
||||
new_nodes.insert(submesh.ParentNodes().FindId(el.node[gi.edges[e][0]],
|
||||
el.node[gi.edges[e][1]]));
|
||||
}
|
||||
}
|
||||
|
||||
parent_node_ids.Reserve(static_cast<int>(new_nodes.size()));
|
||||
parent_to_submesh_node_ids.reserve(new_nodes.size());
|
||||
for (const auto &n : new_nodes)
|
||||
{
|
||||
bool new_node;
|
||||
auto new_node_id = node_ids.Get(n, new_node);
|
||||
MFEM_ASSERT(new_node, "!");
|
||||
submesh.nodes.Alloc(new_node_id, new_node_id, new_node_id);
|
||||
parent_node_ids.Append(n);
|
||||
parent_to_submesh_node_ids[n] = new_node_id;
|
||||
}
|
||||
|
||||
// Loop over elements and reference edges and faces (creating any nodes on
|
||||
// first encounter).
|
||||
for (auto &el : submesh.elements)
|
||||
{
|
||||
if (el.IsLeaf())
|
||||
{
|
||||
const auto gi = submesh.GI[el.Geom()];
|
||||
bool new_id = false;
|
||||
|
||||
for (int n = 0; n < gi.nv; n++)
|
||||
{
|
||||
// Relabel nodes from parent to submesh.
|
||||
el.node[n] = node_ids.Get(el.node[n], new_id);
|
||||
MFEM_ASSERT(new_id == false, "Should not be new.");
|
||||
submesh.nodes[el.node[n]].vert_refc++;
|
||||
}
|
||||
for (int e = 0; e < gi.ne; e++)
|
||||
{
|
||||
const int pid = submesh.ParentNodes().FindId(
|
||||
parent_node_ids[el.node[gi.edges[e][0]]],
|
||||
parent_node_ids[el.node[gi.edges[e][1]]]);
|
||||
MFEM_ASSERT(pid >= 0, "Edge not found");
|
||||
auto submesh_node_id = node_ids.Get(pid, new_id);
|
||||
MFEM_ASSERT(new_id == false, "Should not be new.");
|
||||
submesh.nodes[submesh_node_id].edge_refc++; // Register the edge
|
||||
}
|
||||
for (int f = 0; f < gi.nf; f++)
|
||||
{
|
||||
const int *fv = gi.faces[f];
|
||||
const int pid = submesh.ParentFaces().FindId(
|
||||
parent_node_ids[el.node[fv[0]]],
|
||||
parent_node_ids[el.node[fv[1]]],
|
||||
parent_node_ids[el.node[fv[2]]],
|
||||
el.node[fv[3]] >= 0 ? parent_node_ids[el.node[fv[3]]]: - 1);
|
||||
MFEM_ASSERT(pid >= 0, "Face not found");
|
||||
const int id = submesh.faces.GetId(
|
||||
el.node[fv[0]], el.node[fv[1]], el.node[fv[2]], el.node[fv[3]]);
|
||||
submesh.faces[id].attribute = submesh.ParentFaces()[pid].attribute;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// All elements have been collected, remap the child ids.
|
||||
for (int i = 0; i < NCMesh::MaxElemChildren && el.child[i] >= 0; i++)
|
||||
{
|
||||
el.child[i] = parent_to_submesh_element_ids[el.child[i]];
|
||||
}
|
||||
}
|
||||
el.parent = el.parent < 0 ? el.parent
|
||||
: parent_to_submesh_element_ids.at(el.parent);
|
||||
}
|
||||
}
|
||||
|
||||
// Explicit instantiations
|
||||
template void ConstructVolumeTree(NCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
#ifdef MFEM_USE_MPI
|
||||
template void ConstructVolumeTree(ParNCSubMesh &submesh,
|
||||
const Array<int> &attributes);
|
||||
#endif
|
||||
} // namespace SubMeshUtils
|
||||
} // namespace mfem
|
||||
|
||||
+154
-12
@@ -19,6 +19,9 @@
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
class NCSubMesh;
|
||||
class ParNCSubMesh;
|
||||
|
||||
namespace SubMeshUtils
|
||||
{
|
||||
|
||||
@@ -40,15 +43,6 @@ struct UniqueIndexGenerator
|
||||
int Get(int i, bool &new_index);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Given an element @a el and a list of @a attributes, determine if that
|
||||
* element is in at least one attribute of @a attributes.
|
||||
*
|
||||
* @param el The element
|
||||
* @param attributes The attributes
|
||||
*/
|
||||
bool ElementHasAttribute(const Element &el, const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Given a Mesh @a parent and another Mesh @a mesh using the list of
|
||||
* attributes in @a attributes, this function adds matching elements with those
|
||||
@@ -111,10 +105,10 @@ void BuildVdofToVdofMap(const FiniteElementSpace& subfes,
|
||||
* @tparam T The type of the input object which has to fulfill the
|
||||
* SubMesh::GetParent() interface.
|
||||
*/
|
||||
template <class T, class RT = decltype(std::declval<T>().GetParent())>
|
||||
RT GetRootParent(const T &m)
|
||||
template <class T>
|
||||
auto GetRootParent(const T &m) -> decltype(std::declval<T>().GetParent())
|
||||
{
|
||||
RT parent = m.GetParent();
|
||||
auto parent = m.GetParent();
|
||||
while (true)
|
||||
{
|
||||
const T* next = dynamic_cast<const T*>(parent);
|
||||
@@ -123,6 +117,154 @@ RT GetRootParent(const T &m)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add boundary elements to the SubMesh.
|
||||
* @details An attempt to call this function for anything other than SubMesh or
|
||||
* ParSubMesh will result in a linker error as the template is only explicitly
|
||||
* instantiated for those types.
|
||||
* @param mesh The SubMesh to add boundary elements to.
|
||||
* @param lface_to_boundary_attribute Map from local faces in the submesh to
|
||||
* boundary attributes. Only necessary for interior boundary attributes of
|
||||
* volume submeshes, where the face owning the attribute might be on a
|
||||
* neighboring rank.
|
||||
* @tparam SubMeshT The SubMesh type, options SubMesh and ParSubMesh.
|
||||
*/
|
||||
template <typename SubMeshT>
|
||||
void AddBoundaryElements(SubMeshT &mesh,
|
||||
const std::unordered_map<int,int> &lface_to_boundary_attribute = {});
|
||||
|
||||
/**
|
||||
* @brief Construct a nonconformal mesh (serial or parallel) for a surface
|
||||
* submesh, from an existing nonconformal volume mesh (serial or parallel).
|
||||
* @details This function is only instantiated for NCSubMesh and ParNCSubMesh
|
||||
* Attempting to use it with other classes will result in a linker error.
|
||||
* @tparam NCSubMeshT The NCSubMesh type
|
||||
* @param[out] submesh The surface submesh to be filled.
|
||||
* @param attributes The set of attributes defining the submesh.
|
||||
*/
|
||||
template<typename NCSubMeshT>
|
||||
void ConstructFaceTree(NCSubMeshT &submesh, const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Construct a nonconformal mesh (serial or parallel) for a volume
|
||||
* submesh, from an existing nonconformal volume mesh (serial or parallel).
|
||||
* @details This function is only instantiated for NCSubMesh and ParNCSubMesh
|
||||
* Attempting to use it with other classes will result in a linker error.
|
||||
* @tparam NCSubMeshT The NCSubMesh type
|
||||
* @param[out] submesh The volume submesh to be filled from parent.
|
||||
* @param attributes The set of attributes defining the submesh.
|
||||
*/
|
||||
template <typename NCSubMeshT>
|
||||
void ConstructVolumeTree(NCSubMeshT &submesh, const Array<int> &attributes);
|
||||
|
||||
/**
|
||||
* @brief Helper for checking if an object's attributes match a list
|
||||
*
|
||||
* @tparam T Object Type
|
||||
* @param el Instance of T, requires method `GetAttribute()`
|
||||
* @param attributes Set of attributes to match against
|
||||
* @return true The attribute of el is contained within attributes
|
||||
* @return false
|
||||
*/
|
||||
template <typename T>
|
||||
bool HasAttribute(const T &el, const Array<int> &attributes)
|
||||
{
|
||||
for (int a = 0; a < attributes.Size(); a++)
|
||||
{
|
||||
if (el.GetAttribute() == attributes[a])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwarding dispatch to HasAttribute for backwards compatability
|
||||
*
|
||||
* @param el Instance of T, requires method `GetAttribute()`
|
||||
* @param attributes Set of attributes to match against
|
||||
* @return true The attribute of el is contained within attributes
|
||||
* @return false
|
||||
*/
|
||||
MFEM_DEPRECATED inline bool ElementHasAttribute(const Element &el,
|
||||
const Array<int> &attributes)
|
||||
{
|
||||
return HasAttribute(el,attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Apply permutation to a container type
|
||||
*
|
||||
* @tparam T1 Container type 1
|
||||
* @tparam T2 Container type 2
|
||||
* @tparam T3 Container type 3
|
||||
* @param indices Set of indices that define the permutation
|
||||
* @param t1 First collection to be permuted
|
||||
* @param t2 Second collection to be permuted
|
||||
* @param t3 Third collection to be permuted
|
||||
*/
|
||||
template <typename T1, typename T2, typename T3>
|
||||
void Permute(const Array<int>& indices, T1& t1, T2& t2, T3& t3)
|
||||
{
|
||||
Permute(Array<int>(indices), t1, t2, t3);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Apply permutation to a container type
|
||||
* @details Sorts the indices variable in the process, thereby destroying the
|
||||
* permutation.
|
||||
*
|
||||
* @tparam T1 Container type 1
|
||||
* @tparam T2 Container type 2
|
||||
* @tparam T3 Container type 3
|
||||
* @param indices Set of indices that define the permutation
|
||||
* @param t1 First collection to be permuted
|
||||
* @param t2 Second collection to be permuted
|
||||
* @param t3 Third collection to be permuted
|
||||
*/
|
||||
template <typename T1, typename T2, typename T3>
|
||||
void Permute(Array<int>&& indices, T1& t1, T2& t2, T3& t3)
|
||||
{
|
||||
/*
|
||||
TODO: In c++17 can replace this with a parameter pack expansion technique to
|
||||
operate on arbitrary collections of reference accessible containers of
|
||||
arbitrary type.
|
||||
template <typename ...T> void Permute(Array<int>&&indices, T&... t)
|
||||
{
|
||||
for (int i = 0; i < indices.Size(); i++)
|
||||
{
|
||||
auto current = i;
|
||||
while (i != indices[current])
|
||||
{
|
||||
auto next = indices[current];
|
||||
// Lambda allows iteration over expansion in c++17
|
||||
// https://stackoverflow.com/a/60136761
|
||||
([&]{std::swap(t[current], t[next]);} (), ...);
|
||||
current = next;
|
||||
}
|
||||
indices[current] = current;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
for (int i = 0; i < indices.Size(); i++)
|
||||
{
|
||||
auto current = i;
|
||||
while (i != indices[current])
|
||||
{
|
||||
auto next = indices[current];
|
||||
std::swap(t1[current], t1[next]);
|
||||
std::swap(t2[current], t2[next]);
|
||||
std::swap(t3[current], t3[next]);
|
||||
indices[current] = current;
|
||||
current = next;
|
||||
}
|
||||
indices[current] = current;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace SubMeshUtils
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
@@ -323,11 +323,10 @@ int main(int argc, char *argv[])
|
||||
// Perform time-integration for the problem (looping over the time
|
||||
// iterations, ti, with a time-step dt).
|
||||
bool done = false;
|
||||
for (int ti = 0; !done; )
|
||||
for ( ; !done; )
|
||||
{
|
||||
real_t dt_real = max(dt, t_final - t);
|
||||
cvodes->Step(*U, t, dt_real);
|
||||
ti++;
|
||||
|
||||
done = (t >= t_final - 1e-8*dt);
|
||||
|
||||
|
||||
@@ -221,11 +221,10 @@ int main(int argc, char *argv[])
|
||||
// Perform time-integration (looping over the time iterations, ti, with a
|
||||
// time-step dt).
|
||||
bool done = false;
|
||||
for (int ti = 0; !done; )
|
||||
while (!done)
|
||||
{
|
||||
real_t dt_real = max(dt, t_final - t);
|
||||
cvodes->Step(u, t, dt_real);
|
||||
ti++;
|
||||
|
||||
done = (t >= t_final - 1e-8*dt);
|
||||
|
||||
|
||||
@@ -143,6 +143,9 @@ if (MFEM_USE_MPI)
|
||||
)
|
||||
# Meshing miniapps that return MFEM_SKIP_RETURN_VALUE in some cases:
|
||||
set(SKIP_TESTS)
|
||||
if (HYPRE_USING_CUDA OR HYPRE_USING_HIP)
|
||||
list(APPEND SKIP_TESTS pmesh-fitting)
|
||||
endif()
|
||||
|
||||
foreach(test ${PARALLEL_TESTS})
|
||||
if (test IN_LIST SKIP_TESTS)
|
||||
|
||||
@@ -13,14 +13,12 @@ if (MFEM_USE_MPI)
|
||||
list(APPEND DIST_COMMON_SOURCES
|
||||
sbm_solver.cpp
|
||||
marking.cpp
|
||||
extrapolator.cpp
|
||||
integ_algoim.cpp)
|
||||
extrapolator.cpp)
|
||||
list(APPEND DIST_COMMON_HEADERS
|
||||
sbm_solver.hpp
|
||||
sbm_aux.hpp
|
||||
marking.hpp
|
||||
extrapolator.hpp
|
||||
integ_algoim.hpp)
|
||||
extrapolator.hpp)
|
||||
|
||||
convert_filenames_to_full_paths(DIST_COMMON_SOURCES)
|
||||
convert_filenames_to_full_paths(DIST_COMMON_HEADERS)
|
||||
|
||||
@@ -1,128 +0,0 @@
|
||||
// Copyright (c) 2010-2024, 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 "integ_algoim.hpp"
|
||||
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
AlgoimIntegrationRule::AlgoimIntegrationRule(int o, const FiniteElement &el,
|
||||
ElementTransformation &trans,
|
||||
const Vector &lsfun)
|
||||
{
|
||||
int_order=o;
|
||||
vir=nullptr;
|
||||
sir=nullptr;
|
||||
|
||||
if (el.GetGeomType()==Geometry::Type::SQUARE)
|
||||
{
|
||||
pe=new H1Pos_QuadrilateralElement(el.GetOrder());
|
||||
}
|
||||
else if (el.GetGeomType()==Geometry::Type::CUBE)
|
||||
{
|
||||
pe=new H1Pos_HexahedronElement(el.GetOrder());
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Currently MFEM + Algoim supports only quads and hexes.");
|
||||
}
|
||||
|
||||
// change the basis of the level-set function
|
||||
// from Lagrangian to Bernstein (positive)
|
||||
lsvec.SetSize(pe->GetDof());
|
||||
DenseMatrix T(pe->GetDof());
|
||||
pe->Project(el,trans,T);
|
||||
T.Mult(lsfun,lsvec);
|
||||
}
|
||||
|
||||
const IntegrationRule* AlgoimIntegrationRule::GetVolumeIntegrationRule()
|
||||
{
|
||||
if (vir!=nullptr) {return vir;}
|
||||
|
||||
const int dim=pe->GetDim();
|
||||
int np1d=int_order/2+1;
|
||||
if (dim==2)
|
||||
{
|
||||
LevelSet2D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<2>(ls,Algoim::BoundingBox<real_t,2>(0.0,1.0),
|
||||
-1, -1, np1d);
|
||||
|
||||
vir=new IntegrationRule(q.nodes.size());
|
||||
vir->SetOrder(int_order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=vir->IntPoint(i);
|
||||
ip.Set2w(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LevelSet3D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<3>(ls,Algoim::BoundingBox<real_t,3>(0.0,1.0),
|
||||
-1, -1, np1d);
|
||||
|
||||
vir=new IntegrationRule(q.nodes.size());
|
||||
vir->SetOrder(int_order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=vir->IntPoint(i);
|
||||
ip.Set(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].x(2),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
|
||||
return vir;
|
||||
}
|
||||
|
||||
const IntegrationRule* AlgoimIntegrationRule::GetSurfaceIntegrationRule()
|
||||
{
|
||||
if (sir!=nullptr) {return sir;}
|
||||
|
||||
int np1d=int_order/2+1;
|
||||
const int dim=pe->GetDim();
|
||||
if (dim==2)
|
||||
{
|
||||
LevelSet2D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<2>(ls,Algoim::BoundingBox<real_t,2>(0.0,1.0),
|
||||
2, -1, np1d);
|
||||
|
||||
sir=new IntegrationRule(q.nodes.size());
|
||||
sir->SetOrder(int_order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=sir->IntPoint(i);
|
||||
ip.Set2w(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LevelSet3D ls(pe,lsvec);
|
||||
auto q = Algoim::quadGen<3>(ls,Algoim::BoundingBox<real_t,3>(0.0,1.0),
|
||||
3, -1, np1d);
|
||||
|
||||
sir=new IntegrationRule(q.nodes.size());
|
||||
sir->SetOrder(int_order);
|
||||
for (size_t i=0; i<q.nodes.size(); i++)
|
||||
{
|
||||
IntegrationPoint& ip=sir->IntPoint(i);
|
||||
ip.Set(q.nodes[i].x(0),q.nodes[i].x(1),q.nodes[i].x(2),q.nodes[i].w);
|
||||
}
|
||||
}
|
||||
|
||||
return sir;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,292 +0,0 @@
|
||||
// Copyright (c) 2010-2024, 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 INTEG_ALGOIM_HPP
|
||||
#define INTEG_ALGOIM_HPP
|
||||
|
||||
#include <mfem.hpp>
|
||||
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
#include <algoim_quad.hpp>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
// define templated element bases
|
||||
namespace TmplPoly_1D
|
||||
{
|
||||
|
||||
/// Templated version of CalcBinomTerms
|
||||
template<typename float_type>
|
||||
void CalcBinomTerms(const int p, const float_type x, const float_type y,
|
||||
float_type* u)
|
||||
{
|
||||
if (p == 0)
|
||||
{
|
||||
u[0] = float_type(1.);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
const int *b = Poly_1D::Binom(p);
|
||||
float_type z = x;
|
||||
for (i = 1; i < p; i++)
|
||||
{
|
||||
u[i] = b[i]*z;
|
||||
z *= x;
|
||||
}
|
||||
u[p] = z;
|
||||
z = y;
|
||||
for (i--; i > 0; i--)
|
||||
{
|
||||
u[i] *= z;
|
||||
z *= y;
|
||||
}
|
||||
u[0] = z;
|
||||
}
|
||||
}
|
||||
|
||||
/// Templated version of CalcBinomTerms
|
||||
template<typename float_type>
|
||||
void CalcBinomTerms(const int p, const float_type x, const float_type y,
|
||||
float_type* u, float_type* d)
|
||||
{
|
||||
if (p == 0)
|
||||
{
|
||||
u[0] = float_type(1.);
|
||||
d[0] = float_type(0.);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i;
|
||||
const int *b = Poly_1D::Binom(p);
|
||||
const float_type xpy = x + y, ptx = p*x;
|
||||
float_type z = float_type(1.);
|
||||
|
||||
for (i = 1; i < p; i++)
|
||||
{
|
||||
d[i] = b[i]*z*(i*xpy - ptx);
|
||||
z *= x;
|
||||
u[i] = b[i]*z;
|
||||
}
|
||||
d[p] = p*z;
|
||||
u[p] = z*x;
|
||||
z = float_type(1.);
|
||||
for (i--; i > 0; i--)
|
||||
{
|
||||
d[i] *= z;
|
||||
z *= y;
|
||||
u[i] *= z;
|
||||
}
|
||||
d[0] = -p*z;
|
||||
u[0] = z*y;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Templated evaluation of Bernstein basis
|
||||
template <typename float_type>
|
||||
void CalcBernstein(const int p, const float_type x, float_type *u)
|
||||
{
|
||||
CalcBinomTerms(p, x, 1. - x, u);
|
||||
}
|
||||
|
||||
|
||||
/// Templated evaluation of Bernstein basis
|
||||
template <typename float_type>
|
||||
void CalcBernstein(const int p, const float_type x,
|
||||
float_type *u, float_type *d)
|
||||
{
|
||||
CalcBinomTerms(p, x, 1. - x, u, d);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// Construct volumetric and surface integration rules for a given element
|
||||
/// using the Algoim library. The volume is define as the positive part of
|
||||
/// a level-set function(LSF) (lsfun argument in the constructor). The surface
|
||||
/// is defined as the zero level-set of the LSF.
|
||||
class AlgoimIntegrationRule
|
||||
{
|
||||
public:
|
||||
|
||||
/// Construct Algoim object using a finite element, its transformation
|
||||
/// and level-set function defined over the element using Lagrangian
|
||||
/// bases. The argument o provides the order of the of the 1D Gaussian
|
||||
/// integration rule used for deriving the vol/surface integration rules.
|
||||
AlgoimIntegrationRule(int o, const FiniteElement &el,
|
||||
ElementTransformation &trans, const Vector &lsfun);
|
||||
|
||||
|
||||
/// Destructor of the Algoim object
|
||||
~AlgoimIntegrationRule()
|
||||
{
|
||||
delete pe;
|
||||
delete vir;
|
||||
delete sir;
|
||||
}
|
||||
|
||||
/// Returns volumetric integration rule based on the provided
|
||||
/// level-set function.
|
||||
const IntegrationRule* GetVolumeIntegrationRule();
|
||||
|
||||
/// Returns surface integration rule based on the provided
|
||||
/// level-set function.
|
||||
const IntegrationRule* GetSurfaceIntegrationRule();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/// 3D level-set function object required by Algoim.
|
||||
struct LevelSet3D
|
||||
{
|
||||
/// Constructor for 3D level-set function object required by Algoim.
|
||||
LevelSet3D(PositiveTensorFiniteElement* el_, Vector& lsfun_)
|
||||
: el(el_), lsfun(lsfun_) { }
|
||||
|
||||
/// Returns the value of the LSF for point x.
|
||||
template<typename T>
|
||||
T operator() (const blitz::TinyVector<T,3>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T u3[el_order+1];
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[2], u3);
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
T res=T(0.0);
|
||||
for (int oo = 0, kk = 0; kk <= el_order; kk++)
|
||||
for (int jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res=res-u1[ii]*u2[jj]*u3[kk]*lsfun(dof_map[oo++]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns the gradients of the LSF for point x.
|
||||
template<typename T>
|
||||
blitz::TinyVector<T,3> grad(const blitz::TinyVector<T,3>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T u3[el_order+1];
|
||||
T d1[el_order+1];
|
||||
T d2[el_order+1];
|
||||
T d3[el_order+1];
|
||||
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[2], u3, d3);
|
||||
|
||||
blitz::TinyVector<T,3> res(T(0.0),T(0.0),T(0.0));
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
for (int oo = 0, kk = 0; kk <= el_order; kk++)
|
||||
for (int jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res[0]=res[0]-d1[ii]*u2[jj]*u3[kk]*lsfun(dof_map[oo]);
|
||||
res[1]=res[1]-u1[ii]*d2[jj]*u3[kk]*lsfun(dof_map[oo]);
|
||||
res[2]=res[2]-u1[ii]*u2[jj]*d3[kk]*lsfun(dof_map[oo]);
|
||||
oo++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
PositiveTensorFiniteElement* el;
|
||||
Vector& lsfun;
|
||||
};
|
||||
|
||||
/// 2D level-set function object required by Algoim.
|
||||
struct LevelSet2D
|
||||
{
|
||||
/// Constructor for 2D level-set function object required by Algoim.
|
||||
LevelSet2D(PositiveTensorFiniteElement* el_, Vector& lsfun_)
|
||||
:el(el_), lsfun(lsfun_) { }
|
||||
|
||||
/// Returns the value of the LSF for point x.
|
||||
template<typename T>
|
||||
T operator() (const blitz::TinyVector<T,2>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[0], u1);
|
||||
TmplPoly_1D::CalcBernstein(el_order, x[1], u2);
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
T res=T(0.0);
|
||||
|
||||
for (int oo = 0, jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res=res-u1[ii]*u2[jj]*lsfun(dof_map[oo++]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Returns the gradients of the LSF for point x.
|
||||
template<typename T>
|
||||
blitz::TinyVector<T,2> grad(const blitz::TinyVector<T,2>& x) const
|
||||
{
|
||||
int el_order=el->GetOrder();
|
||||
T u1[el_order+1];
|
||||
T u2[el_order+1];
|
||||
T d1[el_order+1];
|
||||
T d2[el_order+1];
|
||||
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[0], u1, d1);
|
||||
TmplPoly_1D::CalcBernstein(el_order,x[1], u2, d2);
|
||||
|
||||
blitz::TinyVector<T,2> res(T(0.0),T(0.0));
|
||||
|
||||
const Array<int>& dof_map=el->GetDofMap();
|
||||
|
||||
for (int oo = 0, jj = 0; jj <= el_order; jj++)
|
||||
for (int ii = 0; ii <= el_order; ii++)
|
||||
{
|
||||
res[0]=res[0]-(d1[ii]*u2[jj])*lsfun(dof_map[oo]);
|
||||
res[1]=res[1]-(u1[ii]*d2[jj])*lsfun(dof_map[oo]);
|
||||
oo++;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
PositiveTensorFiniteElement* el;
|
||||
Vector& lsfun;
|
||||
};
|
||||
|
||||
|
||||
IntegrationRule* sir; // Surface integration rule. Owned.
|
||||
IntegrationRule* vir; // Volumetric integration rule. Owned.
|
||||
PositiveTensorFiniteElement *pe;
|
||||
Vector lsvec; // level-set in Bernstein bases
|
||||
int int_order; // integration order
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -30,7 +30,6 @@
|
||||
// lsf_integral -ls 2 -m ../../data/inline-quad.mesh -rs 2 -o 3 -ao 3
|
||||
|
||||
#include "mfem.hpp"
|
||||
#include "integ_algoim.hpp"
|
||||
|
||||
using namespace mfem;
|
||||
using namespace std;
|
||||
@@ -183,58 +182,38 @@ int main(int argc, char *argv[])
|
||||
|
||||
#ifdef MFEM_USE_ALGOIM
|
||||
real_t area=0.0;
|
||||
DenseMatrix bmat; // gradients of the shape functions in isoparametric space
|
||||
DenseMatrix pmat; // gradients of the shape functions in physical space
|
||||
Vector inormal; // normal to the level set in isoparametric space
|
||||
Vector tnormal; // normal to the level set in physical space
|
||||
Vector lsfun; // level set function restricted to an element
|
||||
DofTransformation *doftrans;
|
||||
Array<int> vdofs;
|
||||
|
||||
AlgoimIntegrationRules* air=new AlgoimIntegrationRules(aorder,*ls_coeff,order);
|
||||
|
||||
IntegrationRule eir;
|
||||
Vector sweights;
|
||||
|
||||
for (int i=0; i<fespace.GetNE(); i++)
|
||||
{
|
||||
const FiniteElement* el=fespace.GetFE(i);
|
||||
|
||||
// get the element transformation
|
||||
trans = fespace.GetElementTransformation(i);
|
||||
|
||||
// extract the element vector from the level-set
|
||||
doftrans = fespace.GetElementVDofs(i,vdofs);
|
||||
x.GetSubVector(vdofs, lsfun);
|
||||
|
||||
// construct Algoim integration object
|
||||
AlgoimIntegrationRule air(aorder, *el, *trans, lsfun);
|
||||
|
||||
// compute the volume contribution from the element
|
||||
ir = air.GetVolumeIntegrationRule();
|
||||
for (int j = 0; j < ir->GetNPoints(); j++)
|
||||
air->GetVolumeIntegrationRule(*trans,eir);
|
||||
for (int j = 0; j < eir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
const IntegrationPoint &ip = eir.IntPoint(j);
|
||||
trans->SetIntPoint(&ip);
|
||||
vol += ip.weight * trans->Weight();
|
||||
}
|
||||
|
||||
// compute the perimeter/area contribution from the element
|
||||
bmat.SetSize(el->GetDof(),el->GetDim());
|
||||
pmat.SetSize(el->GetDof(),el->GetDim());
|
||||
inormal.SetSize(el->GetDim());
|
||||
tnormal.SetSize(el->GetDim());
|
||||
|
||||
ir = air.GetSurfaceIntegrationRule();
|
||||
for (int j = 0; j < ir->GetNPoints(); j++)
|
||||
air->GetSurfaceIntegrationRule(*trans,eir);
|
||||
air->GetSurfaceWeights(*trans,eir,sweights);
|
||||
for (int j = 0; j < eir.GetNPoints(); j++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir->IntPoint(j);
|
||||
const IntegrationPoint &ip = eir.IntPoint(j);
|
||||
trans->SetIntPoint(&ip);
|
||||
|
||||
el->CalcDShape(ip,bmat);
|
||||
Mult(bmat, trans->AdjugateJacobian(), pmat);
|
||||
// compute the normal to the LS in isoparametric space
|
||||
bmat.MultTranspose(lsfun,inormal);
|
||||
// compute the normal to the LS in physical space
|
||||
pmat.MultTranspose(lsfun,tnormal);
|
||||
area += ip.weight * tnormal.Norml2() / inormal.Norml2();
|
||||
area += ip.weight * sweights(j) * trans->Weight();
|
||||
}
|
||||
}
|
||||
|
||||
delete air;
|
||||
|
||||
if (exact_volume > 0)
|
||||
{
|
||||
std::cout<<"Algoim Volume="<<vol<<" Error="<<vol-exact_volume<<std::endl;
|
||||
|
||||
@@ -30,7 +30,7 @@ DISTANCE_SRC = distance.cpp
|
||||
DISTANCE_OBJ = $(DISTANCE_SRC:.cpp=.o)
|
||||
EXTRAPOLATE_SRC = extrapolate.cpp extrapolator.cpp marking.cpp
|
||||
EXTRAPOLATE_OBJ = $(EXTRAPOLATE_SRC:.cpp=.o)
|
||||
ALGOIM_SRC = integ_algoim.cpp lsf_integral.cpp
|
||||
ALGOIM_SRC = lsf_integral.cpp
|
||||
ALGOIM_OBJ = $(ALGOIM_SRC:.cpp=.o)
|
||||
|
||||
PAR_MINIAPPS = distance diffusion extrapolate lsf_integral
|
||||
|
||||
@@ -76,6 +76,8 @@ int main(int argc, char *argv[])
|
||||
bool useH1 = false;
|
||||
int visport = 19916;
|
||||
bool use_pointwise_transfer = false;
|
||||
const char *device_config = "cpu";
|
||||
bool use_ea = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -96,8 +98,15 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&use_pointwise_transfer, "-t", "--use-pointwise-transfer",
|
||||
"-no-t", "--dont-use-pointwise-transfer",
|
||||
"Use pointwise transfer operators instead of L2 projection.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&use_ea, "-ea", "--ea-version", "-no-ea",
|
||||
"--no-ea-version", "Use element assembly version.");
|
||||
args.ParseCheck();
|
||||
|
||||
// Configure device
|
||||
Device device(device_config);
|
||||
|
||||
// Read the mesh from the given mesh file.
|
||||
Mesh mesh(mesh_file, 1, 1);
|
||||
int dim = mesh.Dimension();
|
||||
@@ -168,6 +177,10 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
gt = new L2ProjectionGridTransfer(fespace, fespace_lor);
|
||||
}
|
||||
|
||||
// Configure element assembly for device acceleration
|
||||
gt->UseEA(use_ea);
|
||||
|
||||
const Operator &R = gt->ForwardOperator();
|
||||
|
||||
// HO->LOR restriction
|
||||
|
||||
@@ -73,13 +73,15 @@ int main(int argc, char *argv[])
|
||||
|
||||
// Parse command-line options.
|
||||
const char *mesh_file = "../../data/star.mesh";
|
||||
int order = 3;
|
||||
int order = 2;
|
||||
int lref = order+1;
|
||||
int lorder = 0;
|
||||
bool vis = true;
|
||||
bool useH1 = false;
|
||||
int visport = 19916;
|
||||
bool use_pointwise_transfer = false;
|
||||
const char *device_config = "cpu";
|
||||
bool use_ea = false;
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -100,14 +102,28 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&use_pointwise_transfer, "-t", "--use-pointwise-transfer",
|
||||
"-no-t", "--dont-use-pointwise-transfer",
|
||||
"Use pointwise transfer operators instead of L2 projection.");
|
||||
args.AddOption(&device_config, "-d", "--device",
|
||||
"Device configuration string, see Device::Configure().");
|
||||
args.AddOption(&use_ea, "-ea", "--ea-version", "-no-ea",
|
||||
"--no-ea-version", "Use element assembly version.");
|
||||
args.ParseCheck();
|
||||
|
||||
// Configure device
|
||||
Device device(device_config);
|
||||
if (Mpi::Root()) { device.Print(); }
|
||||
|
||||
// Read the mesh from the given mesh file.
|
||||
Mesh serial_mesh(mesh_file, 1, 1);
|
||||
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
|
||||
serial_mesh.Clear();
|
||||
int dim = mesh.Dimension();
|
||||
|
||||
// Make initial refinement on serial mesh.
|
||||
for (int l = 0; l < 4; l++)
|
||||
{
|
||||
mesh.UniformRefinement();
|
||||
}
|
||||
|
||||
// Create the low-order refined mesh
|
||||
int basis_lor = BasisType::GaussLobatto; // BasisType::ClosedUniform;
|
||||
ParMesh mesh_lor = ParMesh::MakeRefined(mesh, lref, basis_lor);
|
||||
@@ -179,6 +195,10 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
gt = new L2ProjectionGridTransfer(fespace, fespace_lor);
|
||||
}
|
||||
|
||||
// Configure element assembly for device acceleration
|
||||
gt->UseEA(use_ea);
|
||||
|
||||
const Operator &R = gt->ForwardOperator();
|
||||
|
||||
// HO->LOR restriction
|
||||
@@ -284,11 +304,10 @@ int main(int argc, char *argv[])
|
||||
real_t ho_dual_mass = global_sum(M_rho);
|
||||
real_t lor_dual_mass = global_sum(M_rho_lor);
|
||||
|
||||
cout << lor_dual_mass << '\n';
|
||||
cout << ho_dual_mass << '\n';
|
||||
|
||||
if (Mpi::Root())
|
||||
{
|
||||
cout << "lor dual mass = " << lor_dual_mass << '\n';
|
||||
cout << "ho dual mass = " << ho_dual_mass << '\n';
|
||||
cout << "LOR -> HO dual field: " << abs(ho_dual_mass - lor_dual_mass) << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
# for d in general linalg mesh fem enzyme; do ls -1 $d/*.cpp; done
|
||||
set(UNIT_TESTS_SRCS
|
||||
general/test_array.cpp
|
||||
general/test_mdspan.cpp
|
||||
general/test_arrays_by_name.cpp
|
||||
general/test_error.cpp
|
||||
general/test_mem.cpp
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user