Compare commits
425
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2842d0e7f6 | ||
|
|
e9b24e14cf | ||
|
|
7e4df762c8 | ||
|
|
596b75282d | ||
|
|
2ed23cec01 | ||
|
|
11fbd1f65c | ||
|
|
d3556dc2ac | ||
|
|
d28e48b82a | ||
|
|
80c7331a29 | ||
|
|
9e31083745 | ||
|
|
fc8272216b | ||
|
|
8efa320470 | ||
|
|
c6ea42a681 | ||
|
|
11def83e2c | ||
|
|
51955e88ce | ||
|
|
61bb7755a3 | ||
|
|
a7fa61464c | ||
|
|
93329a7ba0 | ||
|
|
9a69d92756 | ||
|
|
fb93e21a0c | ||
|
|
19d60f95ec | ||
|
|
861d318162 | ||
|
|
3c5acae825 | ||
|
|
9a749f8936 | ||
|
|
c095d055a1 | ||
|
|
d29e914146 | ||
|
|
98ccd6fb96 | ||
|
|
a2491a0e43 | ||
|
|
4475a8c6d4 | ||
|
|
b5e99d8d83 | ||
|
|
91f58c293d | ||
|
|
4836e7cb53 | ||
|
|
6451e64637 | ||
|
|
b7ac1963bb | ||
|
|
baefb786fb | ||
|
|
99523b3a97 | ||
|
|
f67c1e5beb | ||
|
|
50af02454c | ||
|
|
aa94eb11b7 | ||
|
|
b73cc75635 | ||
|
|
7b5f050bdc | ||
|
|
637fa7cc35 | ||
|
|
3f332d50b5 | ||
|
|
782e468325 | ||
|
|
0c59b93ecf | ||
|
|
5a0e89cf31 | ||
|
|
4c3bfce583 | ||
|
|
59d6820ff3 | ||
|
|
9ec379f176 | ||
|
|
72cc503eb9 | ||
|
|
5aff935c98 | ||
|
|
d9c9a35ac9 | ||
|
|
d56298ba17 | ||
|
|
b9d67dec34 | ||
|
|
5b664e393d | ||
|
|
c582282084 | ||
|
|
944ece4090 | ||
|
|
f225d0c3ef | ||
|
|
a9c98c2e3a | ||
|
|
ac8c2948e7 | ||
|
|
f75b4c10d2 | ||
|
|
ecf6b0b44f | ||
|
|
15ee172f26 | ||
|
|
dfbf0aa0e9 | ||
|
|
5a52c676e1 | ||
|
|
5be9693235 | ||
|
|
dc81a7d3c4 | ||
|
|
ea1d41596a | ||
|
|
5421abc4c4 | ||
|
|
f2d3fb45dc | ||
|
|
239c83a742 | ||
|
|
c315028a09 | ||
|
|
99b66f4f45 | ||
|
|
e7be50eb91 | ||
|
|
2dd3915ad5 | ||
|
|
29b572819a | ||
|
|
0a0acfda66 | ||
|
|
7dbce44472 | ||
|
|
c67bd21219 | ||
|
|
38f5b93520 | ||
|
|
a078dfd59e | ||
|
|
66818f5525 | ||
|
|
b810a5e540 | ||
|
|
e63e421343 | ||
|
|
176958144b | ||
|
|
9d191edf06 | ||
|
|
86609f139b | ||
|
|
420fcba457 | ||
|
|
95acb1f85f | ||
|
|
3163d1b039 | ||
|
|
9484b6d688 | ||
|
|
1c7261db07 | ||
|
|
e1b678664a | ||
|
|
ea90996f85 | ||
|
|
3fd646f464 | ||
|
|
d38fe6fc05 | ||
|
|
630c79f1ed | ||
|
|
1ab621fbe9 | ||
|
|
1975811ad4 | ||
|
|
e43c6d891f | ||
|
|
d3e43a6423 | ||
|
|
01c848e838 | ||
|
|
8f06539b6e | ||
|
|
1f6d115d78 | ||
|
|
686c8416c2 | ||
|
|
30a8eb5ccf | ||
|
|
078e59a33b | ||
|
|
1d3a723af9 | ||
|
|
4b61294dc2 | ||
|
|
fced53cd29 | ||
|
|
f47447d92d | ||
|
|
a523710117 | ||
|
|
da4e1b5137 | ||
|
|
54b0a83ffd | ||
|
|
2e8e4a5377 | ||
|
|
b5a1660c2d | ||
|
|
20ba3f3d0c | ||
|
|
07cd99fc3d | ||
|
|
f6eb88574f | ||
|
|
2631ba93ca | ||
|
|
5ea36c8fd6 | ||
|
|
b07fc2bb8e | ||
|
|
53b1b8f9a9 | ||
|
|
5ffc2ef502 | ||
|
|
a709bdb9ee | ||
|
|
46c01f196a | ||
|
|
240955c2cb | ||
|
|
da4a8e3412 | ||
|
|
59cef5f9e3 | ||
|
|
05be944a86 | ||
|
|
258bd917ad | ||
|
|
139c3ddaa6 | ||
|
|
b9cf853dd3 | ||
|
|
915967925c | ||
|
|
88bc3b5833 | ||
|
|
bdd36c8982 | ||
|
|
c103cfa84a | ||
|
|
40bcad05c4 | ||
|
|
6c1c98e4fb | ||
|
|
24f1022f7d | ||
|
|
3888cba7c4 | ||
|
|
932b30e163 | ||
|
|
395e4b0d0e | ||
|
|
a7988aa845 | ||
|
|
4ec768c82b | ||
|
|
e32ea54e00 | ||
|
|
630a75440f | ||
|
|
3ef3c8e6b4 | ||
|
|
fe01ebf36c | ||
|
|
905de04020 | ||
|
|
145efc313d | ||
|
|
26b2aa5cea | ||
|
|
476c148949 | ||
|
|
faa3e22816 | ||
|
|
8ed259be31 | ||
|
|
67025d49ff | ||
|
|
de1dea610e | ||
|
|
9f3f5c0372 | ||
|
|
29caf08098 | ||
|
|
edc4d9a187 | ||
|
|
76d225439a | ||
|
|
5ee3f03902 | ||
|
|
d3a1144d10 | ||
|
|
20e38f3b10 | ||
|
|
9205efab48 | ||
|
|
1ccc27226a | ||
|
|
b20f61b3b8 | ||
|
|
1d0b49e5dd | ||
|
|
a9dcb20e84 | ||
|
|
68f6ce14a6 | ||
|
|
78905d471c | ||
|
|
61806ff1f7 | ||
|
|
0d3195e69b | ||
|
|
e217864f16 | ||
|
|
de8aacddce | ||
|
|
7b1656e19f | ||
|
|
c228538c17 | ||
|
|
a8f5fac0bb | ||
|
|
206eb51618 | ||
|
|
02226f934b | ||
|
|
3d0ba2251a | ||
|
|
b400ee6741 | ||
|
|
f40335f9e7 | ||
|
|
f0de33e33b | ||
|
|
f37a596173 | ||
|
|
2277decd8c | ||
|
|
36f6ff983a | ||
|
|
6fc6cf9186 | ||
|
|
bb06604dac | ||
|
|
058c6b2dee | ||
|
|
94135f3ed2 | ||
|
|
47c1d6230a | ||
|
|
a22c2c8d72 | ||
|
|
65f6ade43d | ||
|
|
64cf121310 | ||
|
|
92e1eace88 | ||
|
|
e6a3835983 | ||
|
|
d97c8ec672 | ||
|
|
6d9f34a3d7 | ||
|
|
5b73d20291 | ||
|
|
4febbb7721 | ||
|
|
ab81de5bf5 | ||
|
|
23814cc1fa | ||
|
|
6307cef7cb | ||
|
|
156f7f930d | ||
|
|
0d5f21188d | ||
|
|
a786d4f293 | ||
|
|
018ab7b974 | ||
|
|
c982aa2448 | ||
|
|
94d1238637 | ||
|
|
d218d38af3 | ||
|
|
04dd962b6d | ||
|
|
383914db9a | ||
|
|
f77d238a5d | ||
|
|
84996ce32f | ||
|
|
5fa7ab3602 | ||
|
|
48cb5996b7 | ||
|
|
8e36285a98 | ||
|
|
27db27b088 | ||
|
|
8339ee0fe9 | ||
|
|
f2b64de28f | ||
|
|
3415b0f3d4 | ||
|
|
9f18d7e044 | ||
|
|
90820b76cf | ||
|
|
3adb2add4c | ||
|
|
5979dd1cce | ||
|
|
be999694b0 | ||
|
|
5d3b9ea727 | ||
|
|
a4d01470d8 | ||
|
|
a055c7ec63 | ||
|
|
f66aaa46bd | ||
|
|
6ea799e385 | ||
|
|
4e83a1604c | ||
|
|
dd0d879e7b | ||
|
|
f1561e47d1 | ||
|
|
f1174bfbf5 | ||
|
|
abf5fedc5b | ||
|
|
4195e4ea2f | ||
|
|
d183f43c96 | ||
|
|
a545b94ad7 | ||
|
|
eff6bc5abc | ||
|
|
94828dbdd0 | ||
|
|
baf29bff27 | ||
|
|
9969e42270 | ||
|
|
4936834c5e | ||
|
|
da51f42c90 | ||
|
|
a7dd90466e | ||
|
|
d231431ca7 | ||
|
|
e55b49b932 | ||
|
|
e480c5f37b | ||
|
|
12eefe3c41 | ||
|
|
8e33891c07 | ||
|
|
3911f44906 | ||
|
|
62fbabe3a5 | ||
|
|
6fa3bc57eb | ||
|
|
157a1f04f9 | ||
|
|
7bc531ba39 | ||
|
|
9343b54c89 | ||
|
|
0ec3e1d21a | ||
|
|
4aa44a9b39 | ||
|
|
d191d332f8 | ||
|
|
9dd104c211 | ||
|
|
e62d26a450 | ||
|
|
7ee86d6e75 | ||
|
|
7b4df2d374 | ||
|
|
3d0878ded5 | ||
|
|
2d7460bde1 | ||
|
|
65acd08d38 | ||
|
|
0acc85d962 | ||
|
|
3c45d59813 | ||
|
|
63acbeb8c0 | ||
|
|
9bf6819f7a | ||
|
|
bed2cc5735 | ||
|
|
4e35641c28 | ||
|
|
8594867ab6 | ||
|
|
6e0fbbd3bf | ||
|
|
d8fd6d95c0 | ||
|
|
a2b34ab650 | ||
|
|
f478f687ff | ||
|
|
0bf998510f | ||
|
|
dbaff07ae9 | ||
|
|
d916299a49 | ||
|
|
10840ac6b4 | ||
|
|
b437016f6e | ||
|
|
5170bbe010 | ||
|
|
f8fcdf6a97 | ||
|
|
fdfc019cc1 | ||
|
|
d10c908b38 | ||
|
|
73aceea741 | ||
|
|
89f85cee21 | ||
|
|
0e9a9d9f7c | ||
|
|
c25be44dd6 | ||
|
|
00da00b93a | ||
|
|
4eca673111 | ||
|
|
4507a02249 | ||
|
|
3eac6fe764 | ||
|
|
f1ed582828 | ||
|
|
07e0d7cd4f | ||
|
|
c68cc62143 | ||
|
|
2c02b41d71 | ||
|
|
d71d1005f9 | ||
|
|
b8f1071168 | ||
|
|
d88529d632 | ||
|
|
7bd028b7fe | ||
|
|
339f20ea7f | ||
|
|
336d80e93a | ||
|
|
b64a189215 | ||
|
|
fc76ff8b2f | ||
|
|
e0a65ffaaf | ||
|
|
61c7187a86 | ||
|
|
a6bad19b8f | ||
|
|
174d991451 | ||
|
|
4fbefc6987 | ||
|
|
8dd75d2548 | ||
|
|
b0c2ec505f | ||
|
|
a03095d84d | ||
|
|
9ee63d6521 | ||
|
|
d0324074c1 | ||
|
|
983d0f4361 | ||
|
|
30016c83b8 | ||
|
|
cf5d93604e | ||
|
|
ada42c9fd8 | ||
|
|
0b802d8fce | ||
|
|
9cdb604796 | ||
|
|
c55e3fa7d2 | ||
|
|
7b47ee4cf5 | ||
|
|
75be9250a9 | ||
|
|
211470966c | ||
|
|
be887d05a4 | ||
|
|
df386413a9 | ||
|
|
fa7fbdf36b | ||
|
|
30f1ad7c2c | ||
|
|
11debd6bf8 | ||
|
|
44a783993e | ||
|
|
a3e229ef82 | ||
|
|
00b6dcdd37 | ||
|
|
d9913262df | ||
|
|
19f444489f | ||
|
|
10c637837c | ||
|
|
665ba30f65 | ||
|
|
d6de2c1a1d | ||
|
|
c4b4cfdc2f | ||
|
|
3435475ae0 | ||
|
|
e43b58fa02 | ||
|
|
27c8412439 | ||
|
|
4b9299188a | ||
|
|
5aaef22cc0 | ||
|
|
f9df36a6de | ||
|
|
b3a75a9295 | ||
|
|
b8c90d24f1 | ||
|
|
e0b2ba5e54 | ||
|
|
5cdc7499ca | ||
|
|
ee859d044d | ||
|
|
cf8d1ddd10 | ||
|
|
987f1636aa | ||
|
|
15faf0d225 | ||
|
|
a8eba594d2 | ||
|
|
1783050f9a | ||
|
|
52291cadbf | ||
|
|
1865b430b0 | ||
|
|
d8734b4b18 | ||
|
|
eae1fa217b | ||
|
|
7dc8b0d9fe | ||
|
|
3f236b406f | ||
|
|
47dcdd8159 | ||
|
|
d93d36d6d3 | ||
|
|
6bcad07a5c | ||
|
|
d56493c2c5 | ||
|
|
42f2594430 | ||
|
|
5ba3e4de97 | ||
|
|
02938c9cce | ||
|
|
65313cd7d3 | ||
|
|
5246f9dbc3 | ||
|
|
4d352bf726 | ||
|
|
eed3bc067f | ||
|
|
7bd256e17c | ||
|
|
7dbad4da3e | ||
|
|
fb85c34ca4 | ||
|
|
6401ca5847 | ||
|
|
496e240837 | ||
|
|
016ebe62cc | ||
|
|
28ab39cf96 | ||
|
|
ec67fe536e | ||
|
|
eff793d6fa | ||
|
|
e5e7280a17 | ||
|
|
773dc57712 | ||
|
|
98a043b195 | ||
|
|
c2a3c83099 | ||
|
|
3ea90aff27 | ||
|
|
23a362d6ad | ||
|
|
9b90a7980b | ||
|
|
cf2c43b5c7 | ||
|
|
2cb6a6e899 | ||
|
|
3ec6292520 | ||
|
|
7011d623d3 | ||
|
|
d4326eddd3 | ||
|
|
39dcdb18e2 | ||
|
|
fb0abff1c2 | ||
|
|
8a615b8742 | ||
|
|
f24d9d8c0d | ||
|
|
85cf7b41d5 | ||
|
|
dff07dd1e3 | ||
|
|
f17c25caf0 | ||
|
|
929c7baf16 | ||
|
|
dfa845a91c | ||
|
|
7fe9733e4d | ||
|
|
1b3c326784 | ||
|
|
0fd42364ca | ||
|
|
7e2c9641c2 | ||
|
|
a0d18d4d52 | ||
|
|
7adea0556c | ||
|
|
1e8efef66b | ||
|
|
f14747eead | ||
|
|
d1151c09a3 | ||
|
|
a9501ed65f | ||
|
|
d16deb42f5 | ||
|
|
b8f88a6560 | ||
|
|
1653781a9d | ||
|
|
fcff34045e | ||
|
|
ae675a05ef | ||
|
|
7f370e8193 | ||
|
|
870732a5aa | ||
|
|
2bf4de6db4 | ||
|
|
44ca1fbf8f | ||
|
|
27b84d79b8 |
@@ -53,7 +53,7 @@ runs:
|
||||
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
|
||||
shell: bash
|
||||
|
||||
- uses: mfem/github-actions/build-mfem@v2.5
|
||||
- uses: mfem/github-actions/build-mfem@v2.6
|
||||
if: ${{steps.debug.outputs.cache-hit != 'true'}}
|
||||
env:
|
||||
CXXFLAGS: ${{env.CXXFLAGS}}
|
||||
@@ -82,7 +82,7 @@ runs:
|
||||
run: find . -type f -name '*.o' -delete
|
||||
shell: bash
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: build-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build
|
||||
|
||||
@@ -49,7 +49,7 @@ runs:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: build-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# MFEM Pull Request Review Agent Guide
|
||||
|
||||
## Purpose and scope
|
||||
Review MFEM PRs for correctness, maintainability, performance, portability, test coverage, and MFEM consistency. Use the diff and PR context; reference source files, tests, and CI results when available. Follow `CONTRIBUTING.md`, especially Developer Guidelines, PR rules, checklist, and testing.
|
||||
|
||||
## Critical review pillars
|
||||
- Correctness and numerical behavior
|
||||
- API and user-facing impact
|
||||
- Performance implications
|
||||
- Maintainability and portability
|
||||
|
||||
## Review workflow
|
||||
1. Read the PR description, linked issues, and intended behavior.
|
||||
2. Inspect the diff before commenting.
|
||||
3. Identify affected MFEM components, examples, tests, build or docs changes, and downstream APIs.
|
||||
4. Analyze the code against the critical review pillars.
|
||||
5. Compare the change against nearby code and MFEM patterns; flag unmotivated deviations.
|
||||
6. Check whether tests and documentation were updated appropriately.
|
||||
7. Review CI results and suggest actions.
|
||||
8. Produce a structured review with prioritized findings.
|
||||
9. Always limit conclusions to available evidence.
|
||||
|
||||
## MFEM-specific review checklist
|
||||
- Component-aware scope: identify the touched subsystem (FEM, solvers, preconditioners, linear algebra, mesh, examples, miniapps, build, or docs) and assess its impact against the review pillars.
|
||||
- Numerical and algorithmic behavior: assess issues in convergence, stability, tolerances, precision, iteration limits, and failure handling. If clear opportunities exist to improve the algorithmic approach, call them out with expected impact.
|
||||
- API and user-facing impact: assess backward compatibility, user-visible behavior and default changes, migration impact, deprecations, and whether documentation clearly explains user-facing API changes.
|
||||
- Data structure and memory semantics: assess ownership, lifetime, aliasing, container behavior, and device-host synchronization.
|
||||
- Parallel and serial behavior: assess whether the change preserves equivalent semantics in serial and parallel modes where applicable; if logic is currently mode-specific, check whether extension to the other mode is straightforward (clear abstractions, no hard-wired assumptions), document constraints, and call out expected behavior differences explicitly.
|
||||
- Backend and portability impact: assess likely cross-backend risks in CPU, CUDA, HIP, OCCA, RAJA, partial assembly, fallback paths, compiler compatibility, and platform assumptions.
|
||||
- Build, dependency, and configuration impact: assess CMake or make changes, optional dependency behavior, and feature-flag interactions.
|
||||
- Tests and docs alignment: check available regression or unit coverage evidence for changed behavior, and ensure docs are updated for new flags, APIs, options, or behavior changes.
|
||||
- MFEM developer-guideline fit: keep code lean, simple, general, logically separated, and portable; suggest C++17 improvements when they clearly improve safety, clarity, or maintainability.
|
||||
- New source files, examples, or miniapps: if a PR adds source/header files, verify they are properly wired into the relevant `makefile` and `CMakeLists.txt`, referenced in docs where applicable (including `doc/CodeDocumentation.dox`), and added to top-level `.gitignore` only when generated artifacts require it.
|
||||
- Changelog: verify `CHANGELOG` is updated if the PR introduces significant new features or user-facing changes.
|
||||
- MFEM conventions: use `real_t`; use `mfem::out`/`mfem::err` instead of `std::cout`/`std::cerr` in library code; flag large/binary files; if AI assistance is apparent but undisclosed, suggest following `CONTRIBUTING.md`.
|
||||
- Edge cases: if the PR touches complex or error-prone areas, suggest additional tests for edge cases, failure modes, and parallel behavior.
|
||||
|
||||
## Commenting guidelines
|
||||
- Keep comments concise, actionable, and grounded in the diff.
|
||||
- Focus on correctness, behavior changes, and user impact over style nits.
|
||||
- Be professional, concise, collaborative, technically precise, and avoid unsupported assumptions.
|
||||
|
||||
@@ -13,7 +13,7 @@ Note that some of these scripts use the shared MFEM GitHub Actions from the exte
|
||||
|
||||
<https://github.com/mfem/github-actions>
|
||||
|
||||
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch in the above from which the action is taken.
|
||||
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch (or tag) in the above from which the action is taken.
|
||||
|
||||
The current CI workflows are:
|
||||
|
||||
|
||||
@@ -232,7 +232,7 @@ jobs:
|
||||
|
||||
- name: get hypre
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
uses: mfem/github-actions/build-hypre@v2.6
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -242,7 +242,7 @@ jobs:
|
||||
|
||||
- name: get hypre (Windows)
|
||||
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
uses: mfem/github-actions/build-hypre@v2.6
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
|
||||
- name: install metis
|
||||
if: matrix.mpi == 'par' && matrix.os != 'windows-latest' && steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
uses: mfem/github-actions/build-metis@v2.6
|
||||
with:
|
||||
archive: ${{ matrix.os != 'macos-latest' && env.METIS_ARCHIVE || env.METIS_ARCHIVE_MAC }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
@@ -304,7 +304,7 @@ jobs:
|
||||
|
||||
# MFEM build and test
|
||||
- name: build
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
uses: mfem/github-actions/build-mfem@v2.6
|
||||
env:
|
||||
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
|
||||
with:
|
||||
@@ -375,8 +375,10 @@ jobs:
|
||||
# Code coverage (process and upload reports)
|
||||
- name: codecov
|
||||
if: matrix.codecov == 'YES'
|
||||
uses: mfem/github-actions/upload-coverage@v2.5
|
||||
uses: mfem/github-actions/upload-coverage@v2.6
|
||||
with:
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
|
||||
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
|
||||
project_dir: ${{ env.MFEM_TOP_DIR }}
|
||||
directories: "fem general linalg mesh"
|
||||
env:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
- name: Get Hypre
|
||||
if: steps.hypre-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
uses: mfem/github-actions/build-hypre@v2.6
|
||||
with:
|
||||
archive: ${{ env.HYPRE_ARCHIVE }}
|
||||
dir: ${{ env.HYPRE_TOP_DIR }}
|
||||
@@ -72,14 +72,14 @@ jobs:
|
||||
|
||||
- name: Install Metis
|
||||
if: steps.metis-cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
uses: mfem/github-actions/build-metis@v2.6
|
||||
with:
|
||||
archive: ${{ env.METIS_ARCHIVE }}
|
||||
dir: ${{ env.METIS_TOP_DIR }}
|
||||
|
||||
# MFEM build and test
|
||||
- name: build-mfem
|
||||
uses: mfem/github-actions/build-mfem@v2.5
|
||||
uses: mfem/github-actions/build-mfem@v2.6
|
||||
with:
|
||||
os: ${{ runner.os }}
|
||||
target: opt
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-hypre@v2.5
|
||||
uses: mfem/github-actions/build-hypre@v2.6
|
||||
with:
|
||||
archive: ${{env.HYPRE_TGZ}}
|
||||
dir: ${{env.HYPRE_DIR}}
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
uses: ./.github/actions/sanitize/mpi
|
||||
- name: Build
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: mfem/github-actions/build-metis@v2.5
|
||||
uses: mfem/github-actions/build-metis@v2.6
|
||||
with:
|
||||
archive: ${{env.METIS_TGZ}}
|
||||
dir: ${{env.METIS_DIR}}
|
||||
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
working-directory: mfem/build/tests/unit
|
||||
run: find . -type f -name '*.o' -delete
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
path: mfem/build/tests/unit/${{env.unit_tests}}
|
||||
@@ -172,7 +172,7 @@ jobs:
|
||||
par: ${{inputs.par}}
|
||||
sanitizer: ${{inputs.sanitizer}}
|
||||
cache-path: mfem/build/tests/unit/${{env.unit_tests}}
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v8
|
||||
if: ${{steps.restore.outputs.cache-hit != 'true'}}
|
||||
with:
|
||||
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
|
||||
|
||||
@@ -85,3 +85,8 @@ opt_par_gcc_10_pumi:
|
||||
extends: .mfem_job_on_dane
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +pumi"
|
||||
|
||||
opt_par_gcc_10_gslib:
|
||||
extends: .mfem_job_on_dane
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +gslib"
|
||||
|
||||
@@ -63,3 +63,8 @@ opt_mpi_cuda_hypre_cuda_gcc:
|
||||
extends: .mfem_job_on_matrix
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +mpi +cuda cuda_arch=90 ^hypre+cuda"
|
||||
|
||||
opt_mpi_cuda_gcc_gslib:
|
||||
extends: .mfem_job_on_matrix
|
||||
variables:
|
||||
SPEC: "%gcc@10.3.1 +mpi +cuda +gslib cuda_arch=90 ^hypre+cuda"
|
||||
|
||||
@@ -32,9 +32,9 @@ mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
|
||||
|
||||
# run
|
||||
if [[ "${MACHINE_NAME}" == "dane" ]]; then
|
||||
salloc --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
srun --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
elif [[ ${MACHINE_NAME} == "corona" ]]; then
|
||||
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
srun --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
|
||||
else
|
||||
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
|
||||
exit 1
|
||||
|
||||
@@ -8,11 +8,19 @@
|
||||
https://mfem.org
|
||||
|
||||
|
||||
Version 4.10 (development)
|
||||
==========================
|
||||
Version 4.9.1 (development)
|
||||
===========================
|
||||
|
||||
- Policy for AI-assisted contribution added to CONTRIBUTING.md
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Added NVIDIA cuDSS library interface. Implementation examples have been
|
||||
added to ex1 and ex1p. See https://developer.nvidia.com/cudss for more
|
||||
details. Supported versions >= 0.6.0.
|
||||
|
||||
- Extend FindPointsGSLIB to support surface meshes.
|
||||
|
||||
- Replaced legacy simplex quadrature rules with symmetric positive-weight
|
||||
rules for triangles (orders 0-25) and tetrahedra (orders 0-20). These
|
||||
rules guarantee all-positive weights and interior quadrature points,
|
||||
@@ -23,12 +31,6 @@ Discretization improvements
|
||||
Tet rules (d=14-20): Chuluunbaatar et al., Comput. Math. Appl. 124:89-97,
|
||||
2022.
|
||||
|
||||
|
||||
Version 4.9.1 (development)
|
||||
===========================
|
||||
|
||||
Discretization improvements
|
||||
---------------------------
|
||||
- Improved the gridfunction projection routines. Projections work for Scalar,
|
||||
Vector and VectorFE, also NURBS versions. Optionally different types of
|
||||
projections can be selected, default behaviour has not changed.
|
||||
@@ -41,11 +43,28 @@ Meshing improvements
|
||||
- Improved support for 1D NURBS meshes with variable order, including using
|
||||
the patches construct for 1D NURBS meshes.
|
||||
|
||||
- Added the option to include material interfaces (faces separating elements
|
||||
with different element attributes) as additional boundary elements, for
|
||||
parallel visualization, e.g. with GLVis. This is supported by both the Print
|
||||
and PrintAsOne methods of ParMesh. See ParMesh::SetPrintInterfaces().
|
||||
|
||||
New and updated examples and miniapps
|
||||
-------------------------------------
|
||||
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
|
||||
capability.
|
||||
|
||||
GPU computing
|
||||
-------------
|
||||
- Allow specifying GPU kernel launch bounds for native and RAJA GPU backends.
|
||||
|
||||
Miscellaneous
|
||||
-------------
|
||||
- Fixed signed DOF handling in parallel grid-function reading (read constructor)
|
||||
and saving via ParGridFunction::SaveAsOne(). Simplified the process of
|
||||
applying the DOF signs by using the new method ApplyDofSigns() in class
|
||||
ParFiniteElementSpace -- the method will return immediately if no sign flips
|
||||
are needed.
|
||||
|
||||
|
||||
Version 4.9, released on Dec 11, 2025
|
||||
=====================================
|
||||
|
||||
+10
-1
@@ -433,6 +433,15 @@ if (MFEM_USE_STRUMPACK)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# cuDSS can only be enabled in CUDA
|
||||
if (MFEM_USE_CUDSS)
|
||||
if (MFEM_USE_CUDA)
|
||||
find_package(CUDSS REQUIRED)
|
||||
else()
|
||||
message(FATAL_ERROR " *** cuDSS requires that CUDA be enabled.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# GnuTLS
|
||||
if (MFEM_USE_GNUTLS)
|
||||
find_package(_GnuTLS REQUIRED)
|
||||
@@ -631,7 +640,7 @@ find_package(Threads REQUIRED)
|
||||
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
|
||||
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB HDF5
|
||||
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
|
||||
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CUDSS CALIPER CODIPACK
|
||||
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
|
||||
ALGOIM ENZYME CUDA::cudart)
|
||||
|
||||
|
||||
+73
-65
@@ -3,12 +3,13 @@
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-brightgreen.svg"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Arepo-check+branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuild-analysis+branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuilds-and-tests+branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-blue.svg"></a>
|
||||
<a href="https://github.com/mfem/mfem/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/v/release/mfem/mfem"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/repo-check.yml?query=branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml?query=branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml?query=branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
|
||||
<a href="https://ci.appveyor.com/project/mfem/mfem"><img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/19non9sqm6msi2wy?svg=true"></a>
|
||||
<a href="https://docs.mfem.org/html/index.html"><img alt="Doxygen" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
<a href="https://docs.mfem.org/html/index.html"><img alt="Documentation" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
|
||||
</p>
|
||||
|
||||
|
||||
@@ -24,6 +25,14 @@ must be made under this license.
|
||||
Note also that MFEM has a [Code of Conduct](CODE_OF_CONDUCT.md). By participating
|
||||
in the MFEM community, you agree to abide by its rules.
|
||||
|
||||
## AI Policy
|
||||
- Use of AI code generation in MFEM is allowed but must be disclosed, e.g. by
|
||||
selecting the `AI-assisted` label on the PR.
|
||||
- By submitting a PR, the author acknowledges that they have reviewed and
|
||||
understand the changes they are proposing.
|
||||
- PR authors are still responsible for correctness, licensing, and attribution
|
||||
of all changes.
|
||||
|
||||
If you plan on contributing to MFEM, consider reviewing the
|
||||
[issue tracker](https://github.com/mfem/mfem/issues) first to check if a thread
|
||||
already exists for your desired feature or the bug you ran into. Use a pull
|
||||
@@ -76,7 +85,7 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
|
||||
follow the [MFEM PR Rules](#mfem-pr-rules).
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
the `ready-for-review` label.
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning two
|
||||
- PRs are treated similarly to journal submission, with an "editor" assigning two
|
||||
reviewers to evaluate the changes.
|
||||
- The reviewers have 3 weeks to evaluate the PR and work with the author to
|
||||
fix issues and implement improvements.
|
||||
@@ -117,7 +126,7 @@ The MFEM source code has the following structure:
|
||||
│ ├── petsc
|
||||
│ ├── pumi
|
||||
│ ├── sundials
|
||||
| └── superlu
|
||||
│ └── superlu
|
||||
├── fem
|
||||
│ ├── ceed
|
||||
│ ├── dfem
|
||||
@@ -129,10 +138,6 @@ The MFEM source code has the following structure:
|
||||
│ ├── moonolith
|
||||
│ ├── qinterp
|
||||
│ └── tmop
|
||||
│ | ├── assemble
|
||||
│ | ├── metrics
|
||||
│ | ├── mult
|
||||
│ | └── tools
|
||||
├── general
|
||||
├── linalg
|
||||
│ ├── batched
|
||||
@@ -145,11 +150,10 @@ The MFEM source code has the following structure:
|
||||
│ ├── common
|
||||
│ ├── contact
|
||||
│ ├── dfem
|
||||
│ ├── diag-smoothers
|
||||
│ ├── dpg
|
||||
│ ├── electromagnetics
|
||||
│ ├── fluids
|
||||
│ │ ├── navier
|
||||
│ │ └── schrodinger-flow
|
||||
│ ├── gslib
|
||||
│ ├── hdiv-linear-solver
|
||||
│ ├── hooke
|
||||
@@ -159,6 +163,7 @@ The MFEM source code has the following structure:
|
||||
│ ├── nurbs
|
||||
│ ├── parelag
|
||||
│ ├── performance
|
||||
│ ├── plasma
|
||||
│ ├── shifted
|
||||
│ ├── solvers
|
||||
│ ├── spde
|
||||
@@ -189,15 +194,15 @@ respectively.
|
||||
|
||||
- The main finite element classes are:
|
||||
+ [`FiniteElement`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
|
||||
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
|
||||
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElementCollection.html)
|
||||
+ [`FiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1FiniteElementSpace.html)
|
||||
+ [`GridFunction`](https://docs.mfem.org/html/classmfem_1_1GridFunction.html)
|
||||
+ [`BilinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1BilinearFormIntegrator.html) and [`LinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html)
|
||||
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
|
||||
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
|
||||
|
||||
- The main linear algebra classes and sources are
|
||||
+ [`Operator`](https://docs.mfem.org/html/classmfem_1_1Operator.html) and [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html)
|
||||
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
|
||||
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1Vector.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
|
||||
+ [`DenseMatrix`](https://docs.mfem.org/html/classmfem_1_1DenseMatrix.html) and [`SparseMatrix`](https://docs.mfem.org/html/classmfem_1_1SparseMatrix.html)
|
||||
+ Sparse [smoothers](https://docs.mfem.org/html/sparsesmoothers_8hpp.html) and linear [solvers](https://docs.mfem.org/html/solvers_8hpp.html)
|
||||
|
||||
@@ -209,8 +214,8 @@ shared geometric entities between different tasks. The parallel source files
|
||||
have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
|
||||
|
||||
- The main parallel classes are
|
||||
+ [`ParMesh`](https://docs.mfem.org/html/solvers_8hpp.html)
|
||||
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
|
||||
+ [`ParMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
|
||||
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParNCMesh.html)
|
||||
+ [`ParFiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1ParFiniteElementSpace.html)
|
||||
+ [`ParGridFunction`](https://docs.mfem.org/html/classmfem_1_1ParGridFunction.html)
|
||||
+ [`ParBilinearForm`](https://docs.mfem.org/html/classmfem_1_1ParBilinearForm.html) and [`ParLinearForm`](https://docs.mfem.org/html/classmfem_1_1ParLinearForm.html)
|
||||
@@ -220,14 +225,14 @@ have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
|
||||
#### GPU and general device support
|
||||
|
||||
GPU and multi-core CPU support is based on device kernels supporting different
|
||||
backends (CUDA, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
|
||||
backends (CUDA, HIP, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
|
||||
device/host memory manager.
|
||||
|
||||
- The main device-relevant classes and sources are:
|
||||
+ [`Device`](https://docs.mfem.org/html/device_8hpp.html)
|
||||
+ [`MemoryManager`](https://docs.mfem.org/html/mem_manager_8hpp.html)
|
||||
+ the [`mfem::forall`](https://docs.mfem.org/html/forall_8hpp.html) function
|
||||
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
|
||||
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html), [`hip.hpp`](https://docs.mfem.org/html/hip_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
|
||||
|
||||
#### Utilities, building and documentation
|
||||
- The `general/` directory contains C++ classes that serve as utilities for
|
||||
@@ -241,8 +246,8 @@ device/host memory manager.
|
||||
- `examples` and `miniapps` respectively gather simple and more fully-featured
|
||||
demonstrations of the usage on MFEM. They both rely on `data/` for the
|
||||
collection of meshes.
|
||||
- The `tests/` directory contains a unit test suite and will later contain more
|
||||
tests that run example codes.
|
||||
- The `tests/` directory contains a unit test suite, additional tests, and
|
||||
benchmarks.
|
||||
|
||||
See also the [code overview](https://mfem.org/code-overview/) section on the MFEM
|
||||
website.
|
||||
@@ -276,8 +281,8 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
the top of https://github.com/mfem.
|
||||
- Consider making your membership public by going to https://github.com/orgs/mfem/people
|
||||
and clicking on the organization visibility drop box next to your name.
|
||||
- Project discussions and announcements will be posted at
|
||||
https://github.com/orgs/mfem/teams/everyone.
|
||||
- Project discussions and announcements will be posted at https://github.com/orgs/mfem/discussions,
|
||||
tagging the `@mfem/everyone` team when appropriate.
|
||||
|
||||
#### Structure
|
||||
- The MFEM source code is in the [mfem](https://github.com/mfem/mfem)
|
||||
@@ -337,11 +342,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- Well-designed simple code is frequently more general and powerful.
|
||||
- Lean code base is easier to understand by new collaborators.
|
||||
- New features should be added only if they are necessary or generally useful.
|
||||
- Introduction of language constructions not currently used in MFEM should be
|
||||
- Introduction of language constructs not currently used in MFEM should be
|
||||
justified and generally avoided (to maintain portability to various systems
|
||||
and compilers, including early access hardware).
|
||||
- We prefer basic C++ and the C++03 standard, to keep the code readable by
|
||||
a large audience and to make sure it compiles anywhere.
|
||||
- We prefer basic C++. Use C++17 features judiciously, prioritizing readability,
|
||||
consistency with existing MFEM code, and portability to different systems,
|
||||
compilers and device backends.
|
||||
|
||||
- *Keep the code general and reasonably efficient*
|
||||
- The main goal is fast prototyping for research and application development.
|
||||
@@ -384,7 +390,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- When your branch is ready for other developers to review / comment on
|
||||
the code, create a pull request towards `mfem:master`.
|
||||
|
||||
- Pull request typically have titles like:
|
||||
- Pull requests typically have titles like:
|
||||
|
||||
`Description [new-feature-dev]`
|
||||
|
||||
@@ -405,12 +411,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
- Add a description, appropriate labels and assign yourself to the PR. The MFEM
|
||||
team will add reviewers as appropriate.
|
||||
|
||||
- List outstanding TODO items in the description, see PR #222 for an example.
|
||||
- List outstanding TODO items in the description.
|
||||
|
||||
- When your contribution is fully working and ready to be reviewed, add
|
||||
the `ready-for-review` label.
|
||||
or request the `ready-for-review` label.
|
||||
|
||||
- PRs are treated similarly to journal submission with an "editor" assigning
|
||||
- PRs are treated similarly to journal submission, with an "editor" assigning
|
||||
two reviewers to evaluate the changes. The reviewers have 3 weeks to evaluate
|
||||
the PR and work with the author to implement improvements and fix issues.
|
||||
|
||||
@@ -436,7 +442,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
|
||||
checks in GitHub Actions enforce MFEM-specific rules which are explained in
|
||||
the error messages and the `tests/scripts` directory.
|
||||
|
||||
- Also note that the tests `branch-history` and `repos-checks` found in GitHub
|
||||
- Also note that the tests `branch-history` and `repo-check` found in GitHub
|
||||
Actions can be triggered automatically before each push using git hooks. See
|
||||
the [git hooks README](config/githooks/README.md) for a detailed explanation.
|
||||
|
||||
@@ -493,15 +499,15 @@ Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their a
|
||||
|
||||
3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc.
|
||||
|
||||
3. To seek help from the editors in case of difficulties.
|
||||
4. To seek help from the editors in case of difficulties.
|
||||
|
||||
4. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
5. To complete the review in a timely manner: 3 weeks from assignment.
|
||||
|
||||
5. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
6. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
|
||||
|
||||
6. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
7. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
|
||||
|
||||
7. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
8. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
|
||||
|
||||
#### Responsibilities of Authors
|
||||
|
||||
@@ -527,30 +533,30 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Code builds.
|
||||
- [ ] Code passes `make style`.
|
||||
- [ ] Update `CHANGELOG`:
|
||||
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
|
||||
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
|
||||
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
|
||||
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
|
||||
- [ ] Update `INSTALL`:
|
||||
- [ ] Had a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
|
||||
- [ ] Have the version ranges for any required or optional libraries changed?
|
||||
- [ ] Does `make` or `cmake` have a new target?
|
||||
- [ ] Did the requirements or the installation process change? *(rare)*
|
||||
- [ ] Has a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
|
||||
- [ ] Have the version ranges for any required or optional libraries changed?
|
||||
- [ ] Does `make` or `cmake` have a new target?
|
||||
- [ ] Did the requirements or the installation process change? *(rare)*
|
||||
- [ ] Update continuous integration server configurations if necessary (e.g. with new version requirements for each of MFEM's dependencies)
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] `.github`
|
||||
- [ ] `.appveyor.yml`
|
||||
- [ ] Update `.gitignore`:
|
||||
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
|
||||
- [ ] Add new patterns (just for the new files above) and re-run the above test.
|
||||
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
|
||||
- [ ] Add new patterns (just for the new files above) and re-run the above test.
|
||||
- [ ] New examples:
|
||||
- [ ] All sample runs at the top of the example source file work.
|
||||
- [ ] Update `examples/makefile`:
|
||||
- [ ] All sample runs at the top of the example source file work.
|
||||
- [ ] Update `examples/makefile`:
|
||||
- [ ] Add the example code to the appropriate `SEQ_EXAMPLES` and `PAR_EXAMPLES` variables.
|
||||
- [ ] Add any files generated by it to the `clean` target.
|
||||
- [ ] Add the example binary and any files generated by it to the top-level `.gitignore` file.
|
||||
- [ ] Update `examples/CMakeLists.txt`:
|
||||
- [ ] Update `examples/CMakeLists.txt`:
|
||||
- [ ] Add the example code to the `ALL_EXE_SRCS` variable.
|
||||
- [ ] Make sure `THIS_TEST_OPTIONS` is set correctly for the new example.
|
||||
- [ ] List the new example in `doc/CodeDocumentation.dox`.
|
||||
- [ ] If new examples directory (e.g.`examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] If new examples directory (e.g. `examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
|
||||
- [ ] Update or add example-specific documentation, see e.g. the `src/examples.md`.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
@@ -567,13 +573,13 @@ Before a PR can be merged, it should satisfy the following:
|
||||
- [ ] Add/update the `CMakeLists.txt` file in the new miniapp directory.
|
||||
- [ ] Consider adding a new test for the new miniapp.
|
||||
- [ ] List the new miniapp in `doc/CodeDocumentation.dox`
|
||||
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
|
||||
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
|
||||
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
|
||||
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
|
||||
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
|
||||
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
|
||||
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
|
||||
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
|
||||
- [ ] New capability:
|
||||
- [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions.
|
||||
- [ ] Pointer arguments and return values must specify whether ownership is being transferred or lent with the call.
|
||||
@@ -675,7 +681,7 @@ MFEM uses a `master`/`next`-branch workflow as described below:
|
||||
- [ ] Update URL shortlinks:
|
||||
- [ ] Create a shortlink at [http://bit.ly/](http://bit.ly/) for the release tarball, e.g. https://mfem.github.io/releases/mfem-3.1.tgz.
|
||||
- [ ] (LLNL only) Add and commit the new shortlink in the `links` and `links-mfem` files of the internal `mfem/downloads` repo.
|
||||
- [ ] Add the new shortlinks to the MFEM packages in `spack`, `homebrew/science`, `VisIt`, etc.
|
||||
- [ ] Add the new shortlinks to the MFEM package in `spack`.
|
||||
- [ ] Update website in `mfem/web` repo:
|
||||
- Update version and shortlinks in `src/index.md` and `src/download.md`.
|
||||
- Use [cloc-1.62.pl](http://cloc.sourceforge.net/) and `ls -lh` to estimate the SLOC and the tarball size in `src/download.md`.
|
||||
@@ -727,22 +733,24 @@ commit or push, see the [README](config/githooks/README.md) in the `config/githo
|
||||
directory.
|
||||
|
||||
|
||||
### Linux and Mac smoke tests
|
||||
### GitHub Actions smoke tests
|
||||
|
||||
We use GitHub Actions to drive the default tests on the `master` and `next`
|
||||
branches. See the `.github/workflows` files and the logs at
|
||||
[https://github.com/mfem/mfem/actions](https://github.com/mfem/mfem/actions).
|
||||
|
||||
Testing using GitHub Actions should be kept lightweight, as there is a time
|
||||
constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux.
|
||||
GitHub Actions testing should be kept lightweight, as there is a time
|
||||
constraint on jobs. The current workflows cover Linux, macOS, and Windows
|
||||
configurations.
|
||||
|
||||
- Tests on the `master` branch are triggered whenever a PR is issued on this branch.
|
||||
- Tests on the `next` branch are currently scheduled to run each night.
|
||||
|
||||
### Additional Windows smoke test
|
||||
|
||||
### Windows smoke test
|
||||
We use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor` file and the
|
||||
build logs at
|
||||
We also use Appveyor to test building with the MS Visual C++ compiler in a Windows
|
||||
environment, as well as to test the CMake build. See the `.appveyor.yml` file
|
||||
and the build logs at
|
||||
[https://ci.appveyor.com/project/mfem/mfem](https://ci.appveyor.com/project/mfem/mfem).
|
||||
|
||||
CMake is used to generate the MSVC Project files and drive the build. A release
|
||||
|
||||
@@ -38,14 +38,13 @@ the option MFEM_USE_METIS.
|
||||
MFEM also includes support for devices such as GPUs, and programming models such
|
||||
as CUDA, HIP, OCCA, OpenMP and RAJA.
|
||||
|
||||
- Starting with version 4.0, MFEM requires a C++11 compiler. We recommend using
|
||||
a newer compiler, e.g. GCC version 4.9 or higher.
|
||||
- Starting with version 4.9, MFEM requires a C++17 compiler.
|
||||
|
||||
- CUDA support requires an NVIDIA GPU and an installation of the CUDA Toolkit
|
||||
https://developer.nvidia.com/cuda-toolkit
|
||||
|
||||
- HIP support requires an AMD GPU and an installation of the ROCm software stack
|
||||
https://rocmdocs.amd.com
|
||||
https://rocm.docs.amd.com
|
||||
|
||||
- OCCA support requires the OCCA library
|
||||
https://libocca.org
|
||||
@@ -83,9 +82,9 @@ Serial build:
|
||||
Parallel build:
|
||||
(download hypre and METIS 4 from above URLs)
|
||||
(build METIS 4 in ../metis-4.0 relative to mfem/)
|
||||
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
(build hypre in ../hypre relative to mfem/)
|
||||
make parallel -j 4
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
CUDA build:
|
||||
make cuda -j 4
|
||||
@@ -115,14 +114,14 @@ Serial build:
|
||||
Parallel build:
|
||||
(download hypre and METIS 4 from above URLs)
|
||||
(build METIS 4 in ../metis-4.0 relative to mfem/)
|
||||
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
(build hypre in ../hypre relative to mfem/)
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES
|
||||
make -j 4
|
||||
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
|
||||
|
||||
Parallel build with fetching of hypre and METIS:
|
||||
mkdir <mfem-buil-dir> ; cd <mfem-build-dir>
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DMFEM_FETCH_TPLS=YES
|
||||
make -j 4
|
||||
|
||||
@@ -134,7 +133,8 @@ CUDA build:
|
||||
|
||||
HIP build:
|
||||
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
|
||||
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 -DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
|
||||
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 \
|
||||
-DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
|
||||
make -j 4
|
||||
|
||||
Example codes (serial/parallel, depending on the build):
|
||||
@@ -269,6 +269,7 @@ Compilers:
|
||||
CXX - C++ compiler, serial build
|
||||
MPICXX - MPI C++ compiler, parallel build
|
||||
CUDA_CXX - The CUDA compiler, 'nvcc' or 'clang++'
|
||||
HIP_CXX - The HIP compiler, e.g. 'hipcc'
|
||||
|
||||
Compiler options:
|
||||
OPTIM_FLAGS - Options for optimized build
|
||||
@@ -395,6 +396,11 @@ MFEM_USE_STRUMPACK = YES/NO
|
||||
classes. When enabled, this option uses the STRUMPACK_* library options, see
|
||||
below.
|
||||
|
||||
MFEM_USE_CUDSS = YES/NO
|
||||
Enable MFEM functionality based on the cuDSS library. When using cuDSS, CUDA
|
||||
support must be also enabled in MFEM, i.e. MFEM_USE_CUDA=YES must be set.
|
||||
When enabled, this option uses the CUDSS_* library options, see below.
|
||||
|
||||
MFEM_USE_GINKGO = YES/NO
|
||||
Enable MFEM functionality based on the Ginkgo library, which provides
|
||||
iterative linear solvers and preconditioners with OpenMP, CUDA backends, see
|
||||
@@ -554,13 +560,13 @@ MFEM_USE_RAJA = YES/NO
|
||||
MFEM_USE_OCCA = YES/NO
|
||||
Enables support for the OCCA library in MFEM. OCCA is an open-source library
|
||||
which aims to make it easy to program different types of devices (e.g. CPU,
|
||||
GPU, FPGA) by providing an unified API for interacting with JIT-compiled
|
||||
GPU, FPGA) by providing a unified API for interacting with JIT-compiled
|
||||
backends. In order to use the OCCA CUDA backend, CUDA support must be enabled
|
||||
in MFEM as well, i.e. MFEM_USE_CUDA=YES must be set.
|
||||
|
||||
MFEM_USE_GSLIB = YES/NO
|
||||
Enables MFEM functionality based on the GSLIB library, and specifically its
|
||||
FindPoints component, which provides a robust algorithms to evaluate finite
|
||||
FindPoints component, which provides robust algorithms to evaluate finite
|
||||
element functions in a collection of points in physical space. When enabled,
|
||||
the user can use the GSLIB-FindPoints methods as shown in miniapps/gslib.
|
||||
|
||||
@@ -719,9 +725,18 @@ The specific libraries and their options are:
|
||||
Options: STRUMPACK_OPT, STRUMPACK_LIB.
|
||||
Versions: STRUMPACK >= 3.0.0.
|
||||
|
||||
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Note that Ginkgo needs a
|
||||
C++ compiler that supports the C++-17 standard. For additional requirements
|
||||
and dependencies of specific modules, see the Ginkgo webpage below.
|
||||
- CUDSS (optional), used when MFEM_USE_CUDSS = YES. Note that CUDSS requires
|
||||
CUDA 12.x toolkit and the cuDSS libraries. The supported communication backend
|
||||
is OpenMPI 4.x (default), and OpenMPI 4.x or a later version must be pre-built.
|
||||
The source files in the cuDSS tarball provide guidance for developing custom
|
||||
MPI implementations.
|
||||
URL: https://developer.nvidia.com/cudss
|
||||
https://docs.nvidia.com/cuda/cudss/advanced_features.html#communication-layer-library-in-cudss
|
||||
Options: CUDSS_OPT, CUDSS_LIB.
|
||||
Versions: cuDSS >= 0.6.0.
|
||||
|
||||
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Ginkgo may have additional
|
||||
requirements and module-specific dependencies; see the webpage below.
|
||||
URL: https://ginkgo-project.github.io
|
||||
Options: GINKGO_OPT, GINKGO_LIB, GINKGO_DIR, GINKGO_BUILD_TYPE (Release or
|
||||
Debug).
|
||||
@@ -793,7 +808,7 @@ The specific libraries and their options are:
|
||||
Options: CONDUIT_OPT, CONDUIT_LIB.
|
||||
Versions: Conduit >= 0.3.1.
|
||||
|
||||
- ADIOS2 (optional) used when MFEM_USE_ADIOS2 = YES.
|
||||
- ADIOS2 (optional), used when MFEM_USE_ADIOS2 = YES.
|
||||
URL: https://adios2.readthedocs.io/
|
||||
Versions: ADIOS >= 2.5.0.
|
||||
|
||||
@@ -869,7 +884,7 @@ The specific libraries and their options are:
|
||||
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
|
||||
Versions: RAJA >= 2022.10.3.
|
||||
|
||||
- Moonolith (optional), use when MFEM_USE_MOONOLITH = YES.
|
||||
- Moonolith (optional), used when MFEM_USE_MOONOLITH = YES.
|
||||
URL: https://bitbucket.org/zulianp/par_moonolith
|
||||
Options: MOONOLITH_DIR
|
||||
Versions: MOONOLITH >= 1.1.0.
|
||||
@@ -957,7 +972,7 @@ CMAKE_BUILD_TYPE which can be set to standard values like "Debug", and "Release"
|
||||
To use a specific generator use the "-G <generator>" option of cmake:
|
||||
|
||||
cmake <mfem-source-dir> -G "Xcode"
|
||||
cmake <mfem-source-dir> -G "Visual Studio 12 2013"
|
||||
cmake <mfem-source-dir> -G "Visual Studio 17 2022"
|
||||
cmake <mfem-source-dir> -G "MinGW Makefiles"
|
||||
|
||||
With CMake it is possible to build MFEM as a shared library using the standard
|
||||
@@ -1202,7 +1217,7 @@ larger problems, there are two options:
|
||||
Specific options for HIP
|
||||
========================
|
||||
MFEM expects the `ROCM_PATH` environment variable to be set to the path of the
|
||||
ROCM install, as well as having `$ROCM_PATH/bin` in `PATH`.
|
||||
ROCm install, as well as having `$ROCM_PATH/bin` in `PATH`.
|
||||
|
||||
Specific options for RAJA+HIP+MPI
|
||||
=================================
|
||||
|
||||
@@ -28,6 +28,7 @@ license files. These software products and their licenses are as follows:
|
||||
* AmgXWrapper (linalg/amgxsolver.{hpp,cpp}) -- MIT license
|
||||
* Catch++ (tests/unit/catch.hpp) -- Boost 1.0 license
|
||||
* Gecko (general/gecko.{cpp,hpp}) -- BSD 3-clause license
|
||||
* gslib (fem/gslib.{cpp,hpp}, mesh/bb_grid_map.{cpp,hpp}) -- BSD 3-clause license
|
||||
* Picojson (fem/picojson.h) -- Custom 2-clause license
|
||||
* TinyXML2 (general/tinyxml2.{cpp,h}) -- zlib license
|
||||
* Zstr (general/zstr.hpp) -- MIT license
|
||||
|
||||
@@ -35,6 +35,7 @@ set(MFEM_USE_SUITESPARSE @MFEM_USE_SUITESPARSE@)
|
||||
set(MFEM_USE_SUPERLU @MFEM_USE_SUPERLU@)
|
||||
set(MFEM_USE_MUMPS @MFEM_USE_MUMPS@)
|
||||
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
|
||||
set(MFEM_USE_CUDSS @MFEM_USE_CUDSS@)
|
||||
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
|
||||
set(MFEM_USE_AMGX @MFEM_USE_AMGX@)
|
||||
set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
|
||||
@@ -109,6 +110,10 @@ if (MFEM_USE_RAJA)
|
||||
find_dependency(RAJA)
|
||||
endif()
|
||||
|
||||
if (MFEM_USE_CUDSS)
|
||||
find_dependency(cudss)
|
||||
endif (MFEM_USE_CUDSS)
|
||||
|
||||
if (MFEM_USE_UMPIRE)
|
||||
find_dependency(umpire)
|
||||
endif()
|
||||
|
||||
@@ -108,6 +108,15 @@
|
||||
// Enable MFEM functionality based on the STRUMPACK library.
|
||||
#cmakedefine MFEM_USE_STRUMPACK
|
||||
|
||||
// Enable MFEM functionality based on the cuDSS library.
|
||||
#cmakedefine MFEM_USE_CUDSS
|
||||
|
||||
// CUDSS communication layer library path
|
||||
#cmakedefine MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
|
||||
|
||||
// CUDSS threading layer library path
|
||||
#cmakedefine MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
|
||||
|
||||
// Enable functionality based on the Ginkgo library.
|
||||
#cmakedefine MFEM_USE_GINKGO
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
if (NOT cudss_DIR AND CUDSS_DIR)
|
||||
set(cudss_DIR ${CUDSS_DIR}/lib/cmake/cudss)
|
||||
endif()
|
||||
message(STATUS "Looking for CUDSS ...")
|
||||
message(STATUS " in CUDSS_DIR = ${CUDSS_DIR}")
|
||||
message(STATUS " cudss_DIR = ${cudss_DIR}")
|
||||
find_package(cudss)
|
||||
set(CUDSS_FOUND ${cudss_FOUND})
|
||||
set(CUDSS_LIBRARIES "cudss")
|
||||
if (CUDSS_FOUND)
|
||||
message(STATUS
|
||||
"Found CUDSS target: ${CUDSS_LIBRARIES} (version: ${cudss_VERSION})")
|
||||
else()
|
||||
set(msg STATUS)
|
||||
if (CUDSS_FIND_REQUIRED)
|
||||
set(msg FATAL_ERROR)
|
||||
endif()
|
||||
message(${msg}
|
||||
"CUDSS not found. Please set CUDSS_DIR to the install prefix.")
|
||||
endif()
|
||||
|
||||
if(CUDSS_FOUND AND TARGET cudss)
|
||||
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION)
|
||||
if(NOT CUDSS_LIBRARY_LOCATION)
|
||||
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION_RELEASE)
|
||||
endif()
|
||||
if(CUDSS_LIBRARY_LOCATION)
|
||||
get_filename_component(CUDSS_LIBRARY_DIR "${CUDSS_LIBRARY_LOCATION}" DIRECTORY)
|
||||
else()
|
||||
message(WARNING "Could not determine the location of the cuDSS library.")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "cuDSS target not available; cannot determine library directory.")
|
||||
endif()
|
||||
|
||||
# Set the full name of the cuDSS threading library if OpenMP is enabled.
|
||||
# The threading layer library (libcudss_mtlayer_gomp.so) is located under the
|
||||
# cuDSS library directory by default.
|
||||
if (MFEM_USE_OPENMP)
|
||||
find_file(
|
||||
CUDSS_THREADING_LIB
|
||||
NAMES libcudss_mtlayer_gomp.so
|
||||
PATHS ${CUDSS_LIBRARY_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if (NOT DEFINED MFEM_CUDSS_THREADING_LIB AND CUDSS_THREADING_LIB)
|
||||
set(MFEM_CUDSS_THREADING_LIB "${CUDSS_THREADING_LIB}")
|
||||
endif()
|
||||
message(STATUS "CUDSS threading layer library: ${MFEM_CUDSS_THREADING_LIB}")
|
||||
endif()
|
||||
|
||||
# Set the full name of the cuDSS communication library if MFEM use OpenMPI.
|
||||
# The communication layer library (libcudss_commlayer_mpi.so) is located under the
|
||||
# cuDSS library directory by default.
|
||||
# The communication layer library is used pre-built communication layers for OpenMPI
|
||||
# by default.
|
||||
if (MFEM_USE_MPI)
|
||||
find_file(
|
||||
CUDSS_COMM_LIB
|
||||
NAMES libcudss_commlayer_openmpi.so
|
||||
PATHS ${CUDSS_LIBRARY_DIR}
|
||||
NO_DEFAULT_PATH
|
||||
)
|
||||
if (NOT DEFINED MFEM_CUDSS_COMM_LIB AND CUDSS_COMM_LIB)
|
||||
set(MFEM_CUDSS_COMM_LIB "${CUDSS_COMM_LIB}")
|
||||
endif()
|
||||
message(STATUS "CUDSS communication layer library: ${MFEM_CUDSS_COMM_LIB}")
|
||||
endif()
|
||||
@@ -157,4 +157,28 @@ constexpr real_t operator""_r(unsigned long long v)
|
||||
#endif
|
||||
#endif // MFEM_USE_MPI not defined
|
||||
|
||||
#ifndef MFEM_USE_CUDA
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
#error Building with cuDSS (MFEM_USE_CUDSS=YES) requires CUDA (MFEM_USE_CUDA=YES)
|
||||
#endif
|
||||
#endif // MFEM_USE_CUDSS not defined
|
||||
|
||||
#ifdef NVTX_DBG_HPP
|
||||
#include NVTX_DBG_HPP // IWYU pragma: keep
|
||||
#else
|
||||
#define db1(...)
|
||||
#define dbg(...)
|
||||
#define dbl(...)
|
||||
#define dba(...)
|
||||
#define dbc(...)
|
||||
#define NVTX_MARK_FUNCTION
|
||||
#define NVTX_MARK_BEGIN(...)
|
||||
#define NVTX_MARK_INI(...)
|
||||
#define NVTX_MARK_END(...)
|
||||
#define NVTX_MARK(...)
|
||||
#define NVTX_INI(...)
|
||||
#define NVTX_END(...)
|
||||
#define NVTX(...)
|
||||
#endif
|
||||
|
||||
#endif // MFEM_CONFIG_HPP
|
||||
|
||||
@@ -108,6 +108,15 @@
|
||||
// Enable MFEM functionality based on the STRUMPACK library.
|
||||
// #define MFEM_USE_STRUMPACK
|
||||
|
||||
// Enable MFEM functionality based on the cuDSS library.
|
||||
// #define MFEM_USE_CUDSS
|
||||
|
||||
// CUDSS communication layer library path
|
||||
// #define MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
|
||||
|
||||
// CUDSS threading layer library path
|
||||
// #define MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
|
||||
|
||||
// Enable MFEM features based on the Ginkgo library.
|
||||
// #define MFEM_USE_GINKGO
|
||||
|
||||
|
||||
@@ -36,6 +36,9 @@ MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
|
||||
MFEM_USE_SUPERLU5 = @MFEM_USE_SUPERLU5@
|
||||
MFEM_USE_MUMPS = @MFEM_USE_MUMPS@
|
||||
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
|
||||
MFEM_USE_CUDSS = @MFEM_USE_CUDSS@
|
||||
MFEM_CUDSS_COMM_LIB = @MFEM_CUDSS_COMM_LIB@
|
||||
MFEM_CUDSS_THREADING_LIB = @MFEM_CUDSS_THREADING_LIB@
|
||||
MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
|
||||
MFEM_USE_AMGX = @MFEM_USE_AMGX@
|
||||
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
|
||||
|
||||
@@ -38,6 +38,7 @@ option(MFEM_USE_SUPERLU "Enable SuperLU_DIST usage" OFF)
|
||||
option(MFEM_USE_SUPERLU5 "Use the old SuperLU_DIST 5.1 version" OFF)
|
||||
option(MFEM_USE_MUMPS "Enable MUMPS usage" OFF)
|
||||
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
|
||||
option(MFEM_USE_CUDSS "Enable cuDSS usage" OFF)
|
||||
option(MFEM_USE_GINKGO "Enable Ginkgo usage" OFF)
|
||||
option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
|
||||
option(MFEM_USE_MAGMA "Enable MAGMA usage" OFF)
|
||||
|
||||
+15
-1
@@ -153,6 +153,7 @@ MFEM_USE_SUPERLU = NO
|
||||
MFEM_USE_SUPERLU5 = NO
|
||||
MFEM_USE_MUMPS = NO
|
||||
MFEM_USE_STRUMPACK = NO
|
||||
MFEM_USE_CUDSS = NO
|
||||
MFEM_USE_GINKGO = NO
|
||||
MFEM_USE_AMGX = NO
|
||||
MFEM_USE_MAGMA = NO
|
||||
@@ -368,6 +369,19 @@ STRUMPACK_OPT = -I$(STRUMPACK_DIR)/include $(SCOTCH_OPT)
|
||||
STRUMPACK_LIB = -L$(STRUMPACK_DIR)/lib -lstrumpack $(MPI_FORTRAN_LIB)\
|
||||
$(SCOTCH_LIB) $(SCALAPACK_LIB)
|
||||
|
||||
# CUDSS library configuration
|
||||
CUDSS_DIR = @MFEM_DIR@/../cudss
|
||||
CUDSS_INCLUDE_DIR = $(CUDSS_DIR)/include
|
||||
CUDSS_LIBRARY_DIR = $(CUDSS_DIR)/lib
|
||||
CUDSS_OPT = -I$(CUDSS_INCLUDE_DIR)
|
||||
CUDSS_LIB = \
|
||||
$(XLINKER)-rpath,$(CUDSS_LIBRARY_DIR) -L$(CUDSS_LIBRARY_DIR) -lcudss
|
||||
# The cuDSS communication and threading libraries.
|
||||
MFEM_CUDSS_COMM_LIB = $(abspath $(wildcard $(or $(CUDSS_COMM_LIB),\
|
||||
$(subst @MFEM_DIR@,$(MFEM_DIR), $(CUDSS_LIBRARY_DIR)/libcudss_commlayer_openmpi.so))))
|
||||
MFEM_CUDSS_THREADING_LIB = $(abspath $(wildcard $(or $(CUDSS_THREADING_LIB),\
|
||||
$(subst @MFEM_DIR@,$(MFEM_DIR),$(CUDSS_LIBRARY_DIR)/libcudss_mtlayer_gomp.so))))
|
||||
|
||||
# Ginkgo library configuration
|
||||
GINKGO_DIR = @MFEM_DIR@/../ginkgo/install
|
||||
GINKGO_SEARCH_DIR = $(subst @MFEM_DIR@,$(MFEM_DIR),$(GINKGO_DIR))
|
||||
@@ -621,7 +635,7 @@ PARELAG_LIB = -L$(PARELAG_DIR)/build/src -lParELAG
|
||||
AXOM_DIR = @MFEM_DIR@/../axom
|
||||
TRIBOL_DIR = @MFEM_DIR@/../tribol
|
||||
TRIBOL_OPT = -I$(TRIBOL_DIR)/include -I$(AXOM_DIR)/include
|
||||
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
|
||||
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -ltribol_shared -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
|
||||
-laxom_slam -laxom_slic -laxom_core
|
||||
|
||||
# Enzyme configuration
|
||||
|
||||
@@ -215,7 +215,7 @@ if (MFEM_ENABLE_TESTING)
|
||||
add_test(NAME ex1p_ceed_np=${MFEM_MPI_NP}
|
||||
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
|
||||
${MPIEXEC_PREFLAGS}
|
||||
$<TARGET_FILE:ex1p> "-no-vis" "-d ceed-cpu" "-pa" "-a"
|
||||
$<TARGET_FILE:ex1p> "-no-vis" "-d" "ceed-cpu" "-pa" "-a"
|
||||
${MPIEXEC_POSTFLAGS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -64,7 +64,7 @@ PARALLEL_NAME := Parallel AMGX example
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not build)
|
||||
|
||||
clean: clean-build
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
|
||||
@@ -64,12 +64,12 @@ ex1p-test-par: ex1p
|
||||
$(MFEM_LIB_FILE):
|
||||
$(error The MFEM library is not built)
|
||||
|
||||
clean: clean-build clean-exec $(SUBDIRS_CLEAN)
|
||||
clean: clean-build clean-exec
|
||||
|
||||
clean-build:
|
||||
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh
|
||||
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
|
||||
@rm -f refined.mesh mesh.*
|
||||
@rm -f sol.*
|
||||
|
||||
+23
-11
@@ -224,17 +224,29 @@ int main(int argc, char *argv[])
|
||||
// 11. Solve the linear system A X = B.
|
||||
if (!pa)
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
|
||||
#else
|
||||
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(*A);
|
||||
umf_solver.Mult(B, X);
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
if (Device::Allows(Backend::CUDA_MASK))
|
||||
{
|
||||
// Use cuDSS to solve the system.
|
||||
CuDSSSolver cudss_solver;
|
||||
cudss_solver.SetOperator(*A);
|
||||
cudss_solver.Mult(B, X);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
#ifndef MFEM_USE_SUITESPARSE
|
||||
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
|
||||
GSSmoother M((SparseMatrix&)(*A));
|
||||
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
|
||||
#else
|
||||
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
|
||||
UMFPackSolver umf_solver;
|
||||
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
|
||||
umf_solver.SetOperator(*A);
|
||||
umf_solver.Mult(B, X);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -273,7 +285,7 @@ int main(int argc, char *argv[])
|
||||
if (visualization)
|
||||
{
|
||||
char vishost[] = "localhost";
|
||||
int visport = 19916;
|
||||
int visport = 19916;
|
||||
socketstream sol_sock(vishost, visport);
|
||||
sol_sock.precision(8);
|
||||
sol_sock << "solution\n" << mesh << x << flush;
|
||||
|
||||
+47
-22
@@ -83,6 +83,9 @@ int main(int argc, char *argv[])
|
||||
const char *device_config = "cpu";
|
||||
bool visualization = true;
|
||||
bool algebraic_ceed = false;
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
bool cudss_solver = false;
|
||||
#endif
|
||||
|
||||
OptionsParser args(argc, argv);
|
||||
args.AddOption(&mesh_file, "-m", "--mesh",
|
||||
@@ -102,6 +105,10 @@ int main(int argc, char *argv[])
|
||||
args.AddOption(&algebraic_ceed, "-a", "--algebraic",
|
||||
"-no-a", "--no-algebraic",
|
||||
"Use algebraic Ceed solver");
|
||||
#endif
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
args.AddOption(&cudss_solver, "-cudss", "--cudss-solver", "-no-cudss",
|
||||
"--no-cudss-solver", "Use the cuDSS Solver.");
|
||||
#endif
|
||||
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
|
||||
"--no-visualization",
|
||||
@@ -248,33 +255,51 @@ int main(int argc, char *argv[])
|
||||
// 13. Solve the linear system A X = B.
|
||||
// * With full assembly, use the BoomerAMG preconditioner from hypre.
|
||||
// * With partial assembly, use Jacobi smoothing, for now.
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
if (!pa && (Device::Allows(Backend::CUDA_MASK) && cudss_solver))
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
if (algebraic_ceed)
|
||||
{
|
||||
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
// Solve using a direct solver with cuDSS
|
||||
CuDSSSolver cudss_solver(MPI_COMM_WORLD);
|
||||
cudss_solver.SetMatrixSymType(
|
||||
CuDSSSolver::SYMMETRIC_POSITIVE_DEFINITE);
|
||||
cudss_solver.SetMatrixViewType(CuDSSSolver::UPPER);
|
||||
cudss_solver.SetOperator(*A);
|
||||
cudss_solver.Mult(B, X);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
Solver *prec = NULL;
|
||||
if (pa)
|
||||
{
|
||||
if (UsesTensorBasis(fespace))
|
||||
{
|
||||
if (algebraic_ceed)
|
||||
{
|
||||
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prec = new HypreBoomerAMG;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec)
|
||||
{
|
||||
cg.SetPreconditioner(*prec);
|
||||
}
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
}
|
||||
CGSolver cg(MPI_COMM_WORLD);
|
||||
cg.SetRelTol(1e-12);
|
||||
cg.SetMaxIter(2000);
|
||||
cg.SetPrintLevel(1);
|
||||
if (prec) { cg.SetPreconditioner(*prec); }
|
||||
cg.SetOperator(*A);
|
||||
cg.Mult(B, X);
|
||||
delete prec;
|
||||
|
||||
// 14. Recover the parallel grid function corresponding to X. This is the
|
||||
// local finite element solution on each processor.
|
||||
|
||||
@@ -95,6 +95,15 @@ int main(int argc, char *argv[])
|
||||
args.PrintOptions(cout);
|
||||
}
|
||||
|
||||
if (amg_elast && !static_cond && reorder_space)
|
||||
{
|
||||
if (myid == 0)
|
||||
cerr << "\nThe AMG elasticity solver requires ordering byVDIM! "
|
||||
<< "Ignoring the specified option -nodes/--by-nodes.\n"
|
||||
<< endl;
|
||||
reorder_space = false;
|
||||
}
|
||||
|
||||
// 3. Enable hardware devices such as GPUs, and programming models such as
|
||||
// CUDA, OCCA, RAJA and OpenMP based on command line options.
|
||||
Device device(device_config);
|
||||
|
||||
@@ -76,4 +76,4 @@ clean-build:
|
||||
rm -rf *.dSYM *.TVD.*breakpoints
|
||||
|
||||
clean-exec:
|
||||
@rm -f refined.mesh sol.gf
|
||||
@rm -f refined.mesh sol.gf mesh.* sol.*
|
||||
|
||||
+5
-2
@@ -71,6 +71,7 @@ endif
|
||||
|
||||
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
|
||||
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
|
||||
SUBDIRS_TEST_NOCLEAN = $(addsuffix /test-noclean,$(SUBDIRS))
|
||||
SUBDIRS_CLEAN = $(addsuffix /clean,$(SUBDIRS))
|
||||
SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
|
||||
|
||||
@@ -87,8 +88,9 @@ SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
|
||||
|
||||
all: $(EXAMPLES) $(SUBDIRS_ALL)
|
||||
|
||||
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
|
||||
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN):
|
||||
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) \
|
||||
$(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
|
||||
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) $(SUBDIRS_CLEAN):
|
||||
$(MAKE) -C $(@D) $(@F)
|
||||
$(SUBDIRS_TPRINT):
|
||||
@$(MAKE) -C $(@D) $(@F)
|
||||
@@ -107,6 +109,7 @@ endif
|
||||
MFEM_TESTS = EXAMPLES
|
||||
include $(MFEM_TEST_MK)
|
||||
test: $(SUBDIRS_TEST)
|
||||
test-noclean: $(SUBDIRS_TEST_NOCLEAN)
|
||||
test-print: $(SUBDIRS_TPRINT)
|
||||
|
||||
# Testing: Parallel vs. serial runs
|
||||
|
||||
@@ -57,6 +57,8 @@ set(SRCS
|
||||
integ/lininteg_domain_grad.cpp
|
||||
integ/lininteg_domain_vectorfe.cpp
|
||||
integ/nonlininteg_vecconvection_pa.cpp
|
||||
integ/nonlininteg_vecconvection_pa_diag.cpp
|
||||
integ/nonlininteg_vecconvection_pa_grad.cpp
|
||||
integ/nonlininteg_vecconvection_mf.cpp
|
||||
coefficient.cpp
|
||||
complex_fem.cpp
|
||||
@@ -171,8 +173,12 @@ set(SRCS
|
||||
tmop_tools.cpp
|
||||
tmop_amr.cpp
|
||||
gslib.cpp
|
||||
gslib/findptsedge_local_2.cpp
|
||||
gslib/findptsedge_local_3.cpp
|
||||
gslib/findptssurf_local_3.cpp
|
||||
gslib/findpts_local_2.cpp
|
||||
gslib/findpts_local_3.cpp
|
||||
gslib/interpolate_local_1.cpp
|
||||
gslib/interpolate_local_2.cpp
|
||||
gslib/interpolate_local_3.cpp
|
||||
transfer.cpp
|
||||
|
||||
+27
-1
@@ -2651,14 +2651,22 @@ public:
|
||||
void AddMultMF(const Vector &x, Vector &y) const override;
|
||||
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
|
||||
|
||||
// PA AddMultPA kernels
|
||||
using VectorMassAddMultPAType =
|
||||
void(*)(const int, const int,
|
||||
const Array<real_t>&, const Vector&,
|
||||
const Vector&, Vector&, const int, const int);
|
||||
|
||||
MFEM_REGISTER_KERNELS(VectorMassAddMultPA,
|
||||
VectorMassAddMultPAType,
|
||||
(int, int, int));
|
||||
|
||||
// PA DiagonalPA kernels
|
||||
using VectorMassAssembleDiagonalPAType =
|
||||
void(*)(const int, const int, const int,
|
||||
const real_t*, const real_t*, real_t*);
|
||||
MFEM_REGISTER_KERNELS(VectorMassAssembleDiagonalPA,
|
||||
VectorMassAssembleDiagonalPAType,
|
||||
(int /*dim*/, int /*q1d*/));
|
||||
};
|
||||
|
||||
|
||||
@@ -3060,6 +3068,24 @@ public:
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
void AddMultTransposePA(const Vector &x, Vector &y) const override;
|
||||
|
||||
using VectorDivergenceAddMultPAType =
|
||||
void (*)(const int ne,
|
||||
const Array<real_t> &b, const Array<real_t> &g, const Array<real_t> &bt,
|
||||
const Vector &op, const Vector &x, Vector &y,
|
||||
const int tr_d1d, const int te_d1d, const int q1d);
|
||||
MFEM_REGISTER_KERNELS(VectorDivergenceAddMultPA,
|
||||
VectorDivergenceAddMultPAType,
|
||||
(int, int, int, int));
|
||||
|
||||
using VectorDivergenceAddMultTransposePAType =
|
||||
void (*)(const int ne,
|
||||
const Array<real_t> &bt, const Array<real_t> >, const Array<real_t> &b,
|
||||
const Vector &q, const Vector &x, Vector &y,
|
||||
const int tr_d1d, const int te_d1d, const int q1d);
|
||||
MFEM_REGISTER_KERNELS(VectorDivergenceAddMultTransposePA,
|
||||
VectorDivergenceAddMultTransposePAType,
|
||||
(int, int, int, int));
|
||||
|
||||
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
|
||||
const FiniteElement &test_fe,
|
||||
const ElementTransformation &Trans);
|
||||
|
||||
+13
-26
@@ -830,15 +830,9 @@ ParComplexGridFunction::ParComplexGridFunction(ParMesh *m, std::istream &input)
|
||||
int vsize = pfes->GetVSize();
|
||||
Vector::Load(input, 2*vsize);
|
||||
|
||||
real_t *data_ = const_cast<real_t*>(HostRead());
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0)
|
||||
{
|
||||
data_[i] = -data_[i];
|
||||
data_[i+vsize] = -data_[i+vsize];
|
||||
}
|
||||
}
|
||||
real_t *h_data = HostReadWrite();
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
|
||||
|
||||
// if the mesh is a legacy (v1.1) NC mesh, it has old vertex ordering
|
||||
@@ -1051,15 +1045,14 @@ void ParComplexGridFunction::Save(std::ostream &os) const
|
||||
os << '\n';
|
||||
|
||||
int vsize = pfes->GetVSize();
|
||||
real_t *data_ = const_cast<real_t*>(HostRead());
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0)
|
||||
{
|
||||
data_[i] = -data_[i];
|
||||
data_[i+vsize] = -data_[i+vsize];
|
||||
}
|
||||
}
|
||||
// We use const_cast + HostRead (instead of HostReadWrite) because we only
|
||||
// need to change the host data temporarily and this way we do not invalidate
|
||||
// the data if it is on device. If we use HostReadWrite here, later calls to
|
||||
// Read or ReadWrite will need to copy the data from host to device. With the
|
||||
// approach used here, the host-to-device copy is avoided.
|
||||
real_t *h_data = const_cast<real_t*>(HostRead());
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
|
||||
if (pfes->GetOrdering() == Ordering::byNODES)
|
||||
{
|
||||
@@ -1070,14 +1063,8 @@ void ParComplexGridFunction::Save(std::ostream &os) const
|
||||
Vector::Print(os, pfes->GetVDim());
|
||||
}
|
||||
|
||||
for (int i = 0; i < vsize; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0)
|
||||
{
|
||||
data_[i] = -data_[i];
|
||||
data_[i+vsize] = -data_[i+vsize];
|
||||
}
|
||||
}
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
pfes->ApplyDofSigns(h_data + vsize);
|
||||
|
||||
os.flush();
|
||||
}
|
||||
|
||||
@@ -114,6 +114,10 @@ void ConduitDataCollection::Save()
|
||||
n_mesh["fields"][name]);
|
||||
}
|
||||
|
||||
// TODO: in parallel, we need to call ParFiniteElementSpace::ApplyDofSigns
|
||||
// for all ParGridFunction objects before and after saving, see
|
||||
// ParGridFunction::Save.
|
||||
|
||||
// save mesh data
|
||||
SaveMeshAndFields(myid,
|
||||
n_mesh,
|
||||
|
||||
@@ -57,7 +57,7 @@ void DGMassApply(const int e,
|
||||
}
|
||||
else if (DIM == 3)
|
||||
{
|
||||
SmemPAMassApply3D_Element<TD1D,TQ1D,ACCUM>(e, NE, B, pa_data, x, y);
|
||||
SmemPAMassApply3D_Element<TD1D,TQ1D,NBZ,ACCUM>(e, NE, B, pa_data, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+3
-3
@@ -1767,8 +1767,8 @@ public:
|
||||
const int ref_factor=1, const int vdim=-1) const;
|
||||
|
||||
/// Computes the \ref PLBound for the gridfunction with number of control
|
||||
/// points based on \p ref_factor, and returns the bounds for each element
|
||||
/// ordered byNodes:
|
||||
/// points based on @a ref_factor, and returns the bounds for each element
|
||||
/// ordered byNODES:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}. We also return the
|
||||
/// PLBound object used to compute the bounds.
|
||||
@@ -1802,7 +1802,7 @@ public:
|
||||
const int vdim = -1) const;
|
||||
|
||||
/// Compute bounds on the grid function for all the elements. The bounds
|
||||
/// are returned in @b lower and @b upper, ordered byNodes:
|
||||
/// are returned in @b lower and @b upper, ordered byNODES:
|
||||
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
|
||||
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}
|
||||
void GetElementBounds(const PLBound &plb, Vector &lower, Vector &upper,
|
||||
|
||||
+2390
-158
File diff suppressed because it is too large
Load Diff
+343
-67
@@ -21,6 +21,45 @@
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
/* gslib license and copyright statement for code adapted from gslib:
|
||||
|
||||
Copyright (c) 2008-2024, UCHICAGO ARGONNE, LLC.
|
||||
|
||||
The UChicago Argonne, LLC as Operator of Argonne National
|
||||
Laboratory holds copyright in the Software. The copyright holder
|
||||
reserves all rights except those expressly granted to licensees,
|
||||
and U.S. Government license rights.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the disclaimer below.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the disclaimer (as noted below)
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. Neither the name of ANL nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
UCHICAGO ARGONNE, LLC, THE U.S. DEPARTMENT OF
|
||||
ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace gslib
|
||||
{
|
||||
struct comm;
|
||||
@@ -86,7 +125,7 @@ protected:
|
||||
void *fdataD;
|
||||
struct gslib::crystal *cr; // gslib's internal data
|
||||
struct gslib::comm *gsl_comm; // gslib's internal data
|
||||
int dim, points_cnt; // mesh dimension and number of points
|
||||
int dim, spacedim, points_cnt; // mesh dimension and number of points
|
||||
Array<unsigned int> gsl_code, gsl_proc, gsl_elem, gsl_mfem_elem;
|
||||
Vector gsl_mesh, gsl_ref, gsl_dist, gsl_mfem_ref;
|
||||
Array<unsigned int> recv_proc, recv_index; // data for custom interpolation
|
||||
@@ -104,18 +143,23 @@ protected:
|
||||
bool gpu_to_cpu_fallback = false;
|
||||
|
||||
// Device specific data used for FindPoints
|
||||
struct
|
||||
struct DEV_STRUCT
|
||||
{
|
||||
bool setup_device = false;
|
||||
bool find_device = false;
|
||||
int local_hash_size, dof1d, dof1d_sol, h_o_size, h_nx;
|
||||
int local_hash_size, dof1d, dof1d_sol, lh_nx, gh_nx;
|
||||
double newt_tol; // Tolerance specified during setup for Newton solve
|
||||
struct gslib::crystal *cr;
|
||||
struct gslib::hash_data_3 *hash3;
|
||||
struct gslib::hash_data_2 *hash2;
|
||||
mutable Vector bb, wtend, gll1d, lagcoeff, gll1d_sol, lagcoeff_sol;
|
||||
mutable Array<unsigned int> loc_hash_offset;
|
||||
mutable Vector loc_hash_min, loc_hash_fac;
|
||||
mutable Array<unsigned int> lh_offset, gh_offset;
|
||||
mutable Vector lh_min, lh_fac, gh_min, gh_fac;
|
||||
// Tolerance to mark points found on the surface as CODE_INTERNAL
|
||||
// or CODE_BORDER. This is needed because we cannot only use reference
|
||||
// space coordinates to determine if a point is located inside the
|
||||
// element or not.
|
||||
mutable double surf_dist_tol;
|
||||
} DEV;
|
||||
|
||||
/// Use GSLIB for communication and interpolation
|
||||
@@ -127,80 +171,143 @@ protected:
|
||||
Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
|
||||
/// Since GSLIB is designed to work with quads/hexes, we split every
|
||||
/// triangle/tet/prism/pyramid element into quads/hexes.
|
||||
/** @brief Since GSLIB is designed to work with quads/hexes, we split every
|
||||
* triangle/tet/prism/pyramid element into quads/hexes. */
|
||||
virtual void SetupSplitMeshes();
|
||||
|
||||
/// Setup integration points that will be used to interpolate the nodal
|
||||
/// location at points expected by GSLIB.
|
||||
/** @brief Setup integration points that will be used to interpolate the
|
||||
* nodal location at points expected by GSLIB. */
|
||||
virtual void SetupIntegrationRuleForSplitMesh(Mesh *mesh,
|
||||
IntegrationRule *irule,
|
||||
int order);
|
||||
|
||||
/// Helper function that calls \ref SetupSplitMeshes and
|
||||
/// \ref SetupIntegrationRuleForSplitMesh.
|
||||
/** @brief Helper function that calls \ref SetupSplitMeshes and
|
||||
* \ref SetupIntegrationRuleForSplitMesh. */
|
||||
virtual void SetupSplitMeshesAndIntegrationRules(const int order);
|
||||
|
||||
/// Get GridFunction value at the points expected by GSLIB.
|
||||
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals) const;
|
||||
|
||||
/// Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For simplices,
|
||||
/// find the original element number (that was split into micro quads/hexes)
|
||||
/// during the setup phase.
|
||||
/** @brief Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For
|
||||
* simplices, find the original element number (that was split into
|
||||
* micro quads/hexes) during the setup phase. */
|
||||
virtual void MapRefPosAndElemIndices();
|
||||
|
||||
// Device functions
|
||||
// FindPoints locally on device for 3D.
|
||||
/// FindPoints locally on device for 3D.
|
||||
void FindPointsLocal3(const Vector &point_pos, int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l, Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l, int npt);
|
||||
|
||||
// FindPoints locally on device for 2D.
|
||||
/// FindPoints locally on device for 2D.
|
||||
void FindPointsLocal2(const Vector &point_pos, int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l, Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l, int npt);
|
||||
|
||||
// Interpolate on device for 3D.
|
||||
/// FindPoints locally on device for 3D surface elements.
|
||||
void FindPointsSurfLocal3(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l,
|
||||
int npt);
|
||||
|
||||
/// FindPoints locally on device for 3D edge elements.
|
||||
void FindPointsEdgeLocal3(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l,
|
||||
int npt);
|
||||
|
||||
/// FindPoints locally on device for 2D edge elements.
|
||||
void FindPointsEdgeLocal2(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &gsl_code_dev_l,
|
||||
Array<unsigned int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &gsl_dist_l,
|
||||
int npt);
|
||||
|
||||
/// Interpolate on device for 3D.
|
||||
void InterpolateLocal3(const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int nel, int dof1dsol);
|
||||
// Interpolate on device for 2D.
|
||||
int dof1dsol);
|
||||
|
||||
/// Interpolate on device for 2D.
|
||||
void InterpolateLocal2(const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int nel, int dof1dsol);
|
||||
int dof1dsol);
|
||||
|
||||
// Prepare data for device functions.
|
||||
/// Interpolate on device for 1D.
|
||||
void InterpolateLocal1(const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp, int dof1dsol);
|
||||
|
||||
/// Prepare data for device execution for volume meshes.
|
||||
void SetupDevice();
|
||||
|
||||
/** Searches positions given in physical space by @a point_pos.
|
||||
/** @brief Searches positions given in physical space by @a point_pos.
|
||||
These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) specified by @a point_pos_ordering. */
|
||||
void FindPointsOnDevice(const Vector &point_pos,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/** Interpolation of field values at prescribed reference space positions.
|
||||
@param[in] field_in_evec E-vector of grid function to be interpolated.
|
||||
Assumed ordering is NDOFSxVDIMxNEL
|
||||
@param[in] nel Number of elements in the mesh.
|
||||
@param[in] ncomp Number of components in the field.
|
||||
@param[in] dof1dsol Number of degrees of freedom in each reference
|
||||
space direction.
|
||||
@param[in] ordering Ordering of the out field values: byNodes/byVDIM
|
||||
|
||||
@param[out] field_out Interpolated values. For points that are not found
|
||||
the value is set to #default_interp_value. */
|
||||
/** @brief Interpolation of field values at prescribed reference space
|
||||
* positions.
|
||||
* @param[in] field_in_evec E-vector of grid function to be interpolated.
|
||||
* Assumed ordering is NDOFSxVDIMxNEL
|
||||
* @param[in] nel Number of elements in the mesh.
|
||||
* @param[in] ncomp Number of components in the field.
|
||||
* @param[in] dof1dsol Number of degrees of freedom in each reference
|
||||
* space direction.
|
||||
* @param[in] ordering Ordering of the out field values: byNodes/byVDIM
|
||||
*
|
||||
* @param[out] field_out Interpolated values. For points that are not
|
||||
* found the value is set to
|
||||
* #default_interp_value. */
|
||||
void InterpolateOnDevice(const Vector &field_in_evec, Vector &field_out,
|
||||
const int nel, const int ncomp,
|
||||
const int dof1dsol, const int ordering);
|
||||
|
||||
/** @brief Interpolation of field values at prescribed reference space
|
||||
* positions for surface meshes. */
|
||||
void InterpolateSurfBase(const Vector &field_in, Vector &field_out,
|
||||
const int nel, const int ncomp,
|
||||
const int dof1dsol, const int field_out_ordering);
|
||||
|
||||
/// Preprocess 2D surface mesh needed for FindPoints.
|
||||
void findptsedge_setup_2(DEV_STRUCT &devs,
|
||||
const double *const elx[2],
|
||||
const unsigned n,
|
||||
const uint nel,
|
||||
const unsigned m,
|
||||
const double bbox_tol,
|
||||
const uint local_hash_size,
|
||||
const uint global_hash_size);
|
||||
|
||||
/// Preprocess 3D surface mesh needed for FindPoints.
|
||||
void findptssurf_setup_3(DEV_STRUCT &devs,
|
||||
const double *const elx[3],
|
||||
const unsigned n,
|
||||
const uint nel,
|
||||
const unsigned m,
|
||||
const double bbox_tol,
|
||||
const uint local_hash_size,
|
||||
const uint global_hash_size,
|
||||
const int rD);
|
||||
|
||||
public:
|
||||
/// Serial constructor
|
||||
FindPointsGSLIB();
|
||||
@@ -224,8 +331,10 @@ public:
|
||||
FindPointsGSLIB(const FindPointsGSLIB&) = delete;
|
||||
FindPointsGSLIB& operator=(const FindPointsGSLIB&) = delete;
|
||||
|
||||
/** Initializes the internal mesh in gslib, by sending the positions of the
|
||||
Gauss-Lobatto nodes of the input Mesh object \p m.
|
||||
/** @brief Preprocess the internal mesh in gslib.
|
||||
|
||||
@details Initializes the internal mesh in gslib, by sending the
|
||||
positions of the Gauss-Lobatto nodes of the input Mesh object \p m.
|
||||
Note: not tested with periodic (L2).
|
||||
Note: the input mesh \p m must have Nodes set.
|
||||
|
||||
@@ -236,13 +345,22 @@ public:
|
||||
search methods.
|
||||
@param[in] npt_max (Optional) Number of points for simultaneous
|
||||
iteration. This alters performance and
|
||||
memory footprint.*/
|
||||
|
||||
memory footprint.
|
||||
*/
|
||||
void Setup(Mesh &m, const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
/** Searches positions given in physical space by \p point_pos.
|
||||
These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
|
||||
|
||||
/// Preprocess the surface mesh to compute data for FindPoints.
|
||||
void SetupSurf(Mesh &m,
|
||||
const double bb_t = 0.1,
|
||||
const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
/** @brief Searches positions given in physical space by \p point_pos.
|
||||
|
||||
@details These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
|
||||
byVDim: (XYZ,XYZ,....XYZ) specified by \p point_pos_ordering.
|
||||
|
||||
This function populates the following member variables:
|
||||
#gsl_code Return codes for each point: inside element (0),
|
||||
element boundary (1), not found (2).
|
||||
@@ -261,19 +379,34 @@ public:
|
||||
#gsl_dist Distance between the sought and the found point
|
||||
in physical space. */
|
||||
void FindPoints(const Vector &point_pos,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/// Convenience function when point positions are in a ParticleVector
|
||||
void FindPoints(const ParticleVector &point_pos)
|
||||
{
|
||||
FindPoints(point_pos, point_pos.GetOrdering());
|
||||
}
|
||||
|
||||
/** @brief Searches positions given in physical space by \p point_pos on
|
||||
* surface mesh. */
|
||||
void FindPointsSurf(const Vector &point_pos,
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/// Convenience function when point positions are in a ParticleVector
|
||||
void FindPointsSurf(const ParticleVector &point_pos)
|
||||
{
|
||||
FindPointsSurf(point_pos, point_pos.GetOrdering());
|
||||
}
|
||||
|
||||
/// Setup FindPoints and search positions
|
||||
void FindPoints(Mesh &m, const Vector &point_pos,
|
||||
const int point_pos_ordering = Ordering::byNODES,
|
||||
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
|
||||
const int npt_max = 256);
|
||||
|
||||
/** Interpolation of field values at prescribed reference space positions.
|
||||
/** @brief Interpolation of field values at prescribed reference space
|
||||
* positions.
|
||||
|
||||
@param[in] field_in Function values that will be interpolated on the
|
||||
reference positions. Note: it is assumed that
|
||||
\p field_in is in H1 and in the same space as the
|
||||
@@ -282,19 +415,36 @@ public:
|
||||
the value is set to #default_interp_value.
|
||||
The output ordering is determined from field_in.*/
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out);
|
||||
|
||||
/// Interpolation of field values, with output ordering specification.
|
||||
virtual void Interpolate(const GridFunction &field_in, Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
/** Search positions and interpolate. The ordering (byNODES or byVDIM) of
|
||||
the output values in \p field_out corresponds to the ordering used
|
||||
in the input GridFunction \p field_in. */
|
||||
|
||||
/** @brief Same as Interpolate but for surface meshes */
|
||||
virtual void InterpolateSurf(const GridFunction &field_in,
|
||||
Vector &field_out);
|
||||
|
||||
/** @brief Same as Interpolate but for surface meshes with specified output
|
||||
ordering */
|
||||
virtual void InterpolateSurf(const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int field_out_ordering);
|
||||
|
||||
/** @brief Search positions and interpolate.
|
||||
*
|
||||
* @details The ordering (byNODES or byVDIM) of the output values in
|
||||
* \p field_out corresponds to the ordering used in the input
|
||||
* GridFunction \p field_in.
|
||||
*/
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/// Search positions and interpolate with given point and output ordering.
|
||||
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
|
||||
Vector &field_out, const int point_pos_ordering,
|
||||
const int field_out_ordering);
|
||||
|
||||
/** Setup FindPoints, search positions and interpolate. The ordering (byNODES
|
||||
or byVDIM) of the output values in \p field_out corresponds to the
|
||||
ordering used in the input GridFunction \p field_in. */
|
||||
@@ -302,32 +452,36 @@ public:
|
||||
const GridFunction &field_in, Vector &field_out,
|
||||
const int point_pos_ordering = Ordering::byNODES);
|
||||
|
||||
/// Average type to be used for L2 functions in-case a point is located at
|
||||
/// an element boundary where the function might be multi-valued.
|
||||
/** @brief Average type to be used for L2 functions in-case a point is
|
||||
* located at an element boundary where the function might be multi-valued.
|
||||
*/
|
||||
virtual void SetL2AvgType(AvgType avgtype_) { avgtype = avgtype_; }
|
||||
|
||||
/// Set the default interpolation value for points that are not found in the
|
||||
/// mesh.
|
||||
/** @brief Set the default interpolation value for points that are not found in the mesh. */
|
||||
virtual void SetDefaultInterpolationValue(double interp_value_)
|
||||
{
|
||||
default_interp_value = interp_value_;
|
||||
}
|
||||
|
||||
/// Set the tolerance for detecting points outside the 'curvilinear' boundary
|
||||
/// that gslib may return as found on the boundary. Points found on boundary
|
||||
/// with distance greater than @ bdr_tol are marked as not found.
|
||||
/** @brief Tolerance for detecting points outside the 'curvilinear' boundary.
|
||||
*
|
||||
* @details When using FindPoints, gslib may return points as found on the
|
||||
* boundary even when they are slightly outside the domain. This tolerance
|
||||
* is used to filter such points based on the distance^2 value and mark them
|
||||
* as not found.*/
|
||||
virtual void SetDistanceToleranceForPointsFoundOnBoundary(double bdr_tol_)
|
||||
{
|
||||
bdr_tol = bdr_tol_;
|
||||
}
|
||||
|
||||
/// Enable/Disable use of CPU functions for GPU data if the gslib version
|
||||
/// is older.
|
||||
/** @brief Enable/Disable use of CPU functions for GPU data if the gslib
|
||||
* version is older. */
|
||||
virtual void SetGPUtoCPUFallback(bool mode) { gpu_to_cpu_fallback = mode; }
|
||||
|
||||
/** Cleans up memory allocated internally by gslib.
|
||||
Note that in parallel, this must be called before MPI_Finalize(), as it
|
||||
calls MPI_Comm_free() for internal gslib communicators. FreeData is
|
||||
/** @brief Cleans up memory allocated internally by gslib.
|
||||
|
||||
@details Note that in parallel, this must be called before MPI_Finalize,
|
||||
as it calls MPI_Comm_free() for internal gslib communicators. FreeData is
|
||||
also called by the class destructor and there are no memory leaks if the
|
||||
destructor is called before MPI_Finalize(). If the destructor is called
|
||||
after MPI_Finalize(), there will be an error because gslib will try to
|
||||
@@ -335,8 +489,8 @@ public:
|
||||
*/
|
||||
virtual void FreeData();
|
||||
|
||||
/// Return code for each point searched by FindPoints: inside element (0), on
|
||||
/// element boundary (1), or not found (2).
|
||||
/** @brief Return code for each point searched by FindPoints:
|
||||
* inside element (0), element boundary (1), or not found (2). */
|
||||
virtual const Array<unsigned int> &GetCode() const { return gsl_code; }
|
||||
/// Return element number for each point found by FindPoints.
|
||||
virtual const Array<unsigned int> &GetElem() const { return gsl_mfem_elem; }
|
||||
@@ -344,15 +498,15 @@ public:
|
||||
virtual const Array<unsigned int> &GetProc() const { return gsl_proc; }
|
||||
/// Return reference coordinates for each point found by FindPoints.
|
||||
virtual const Vector &GetReferencePosition() const { return gsl_mfem_ref; }
|
||||
/// Return distance between the sought and the found point in physical space,
|
||||
/// for each point found by FindPoints.
|
||||
/// Return distance between the sought and the found point in physical space.
|
||||
virtual const Vector &GetDist() const { return gsl_dist; }
|
||||
|
||||
/// Return element number for each point found by FindPoints corresponding to
|
||||
/// GSLIB mesh. gsl_mfem_elem != gsl_elem for mesh with simplices.
|
||||
/** @brief Return element number for each point found by FindPoints
|
||||
* corresponding to GSLIB mesh. gsl_mfem_elem != gsl_elem for mesh with
|
||||
* simplices. */
|
||||
virtual const Array<unsigned int> &GetGSLIBElem() const { return gsl_elem; }
|
||||
/// Return reference coordinates in [-1,1] (internal range in GSLIB) for each
|
||||
/// point found by FindPoints.
|
||||
/** @brief Return reference coordinates in [-1,1] (internal range in GSLIB)
|
||||
* for each point found by FindPoints. */
|
||||
virtual const Vector &GetGSLIBReferencePosition() const { return gsl_ref; }
|
||||
|
||||
/// Get array of indices of not-found points.
|
||||
@@ -395,7 +549,7 @@ public:
|
||||
|
||||
/// Return the axis-aligned bounding boxes (AABB) computed during \ref Setup.
|
||||
/// The size of the returned vector is (nel x nverts x dim), where nel is the
|
||||
/// number of elements (after splitting for simplcies), nverts is number of
|
||||
/// number of elements (after splitting for simplicies), nverts is number of
|
||||
/// vertices (4 in 2D, 8 in 3D), and dim is the spatial dimension.
|
||||
void GetAxisAlignedBoundingBoxes(Vector &aabb) const;
|
||||
|
||||
@@ -409,6 +563,18 @@ public:
|
||||
/// \p obbV, a vector of size (nel x nverts x dim) .
|
||||
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
|
||||
Vector &obbV) const;
|
||||
|
||||
/** @brief Return the bounding boxes as a mesh on rank 0.
|
||||
*
|
||||
* @param[in] type Bounding-box type: 0 - AABB, 1 - OBB.
|
||||
*
|
||||
* @return On rank 0, returns a newly allocated mesh containing the
|
||||
* bounding boxes. The caller owns the returned pointer and is responsible
|
||||
* for deleting it. On other ranks, returns nullptr.
|
||||
*/
|
||||
Mesh *GetBoundingBoxMesh(int type);
|
||||
|
||||
virtual const Vector &GetGLLMesh() const { return gsl_mesh; }
|
||||
};
|
||||
|
||||
/** \brief OversetFindPointsGSLIB enables use of findpts for arbitrary number of
|
||||
@@ -536,6 +702,116 @@ public:
|
||||
void GS(Vector &senddata, GSOp op);
|
||||
};
|
||||
|
||||
#if defined(MFEM_USE_MPI)
|
||||
/** \brief Class to map a point in physical space to candidate ranks.
|
||||
*
|
||||
* This class builds a Cartesian-aligned tensor grid that covers the entire
|
||||
* domain and precomputes which ranks have elements intersecting each
|
||||
* grid cell. Given a point in physical space, the grid cell containing
|
||||
* the point is determined, and the list of candidate ranks whose
|
||||
* elements intersect that cell is returned. This yields a fast, conservative
|
||||
* point-to-rank candidate query. This is used internally by FindPointsGSLIB
|
||||
* to speed up point searches in parallel.
|
||||
*
|
||||
* See Mittal et al., "General Field Evaluation in High-Order Meshes on GPUs".
|
||||
* (2025). Computers & Fluids. for technical details.
|
||||
*
|
||||
*/
|
||||
class GlobalBBoxTensorGridMap
|
||||
{
|
||||
private:
|
||||
struct gslib::crystal *cr = nullptr; // gslib's internal data
|
||||
struct gslib::comm *gsl_comm = nullptr; // gslib's internal data
|
||||
int sdim, n_local_cells, num_procs;
|
||||
Array<int> gmap_n;
|
||||
Vector gmap_bnd_min, gmap_bnd_max;
|
||||
Vector gmap_fac;
|
||||
Array<int> ggrid_map;
|
||||
|
||||
void SetupCrystal(const MPI_Comm &comm);
|
||||
public:
|
||||
/// Constructor for a given mesh and number of tensor grid divisions
|
||||
GlobalBBoxTensorGridMap(ParMesh &pmesh, int nx);
|
||||
|
||||
/** @brief Constructor for given element bounds and spatial dimension.
|
||||
*
|
||||
* @details This constructor must be called collectively on \a comm.
|
||||
* Supports spatial dimensions 1, 2, and 3, and accepts nel == 0 on a rank.
|
||||
*
|
||||
* Assumes elmin, elmax Ordering::byNodes:
|
||||
* elmin -> [x_{0,min},x_{1,min},... ,y_{0,min},y_{1,min},..,z_{nel-1,min}]
|
||||
* elmax -> [x_{0,max},x_{1,max},... ,y_{0,max},y_{1,max},..,z_{nel-1,max}]
|
||||
* Note elmin, elmax can be obtained using GridFunction::GetElementBounds()
|
||||
*
|
||||
* When by_max_size=false, n gives the number of tensor-grid divisions in
|
||||
* each direction. When by_max_size=true, n is a per-rank size hint used to
|
||||
* derive a uniform global resolution. The communicator-wide sum of n is
|
||||
* converted to nx = ceil(pow(sum(n), 1./sdim)) in each direction, so n is
|
||||
* not a hard cap on ggrid_map.Size().
|
||||
*/
|
||||
GlobalBBoxTensorGridMap(const MPI_Comm &comm, Vector &elmin,
|
||||
Vector &elmax, int nel, int sdim, int n,
|
||||
bool by_max_size);
|
||||
|
||||
/** @brief Constructor for given element bounds, spatial dimension, and
|
||||
* tensor-grid divisions in each direction.
|
||||
*
|
||||
* @details This constructor must be called collectively on \a comm.
|
||||
* Supports spatial dimensions 1, 2, and 3, and accepts nel == 0 on a rank.
|
||||
* Requires nx.Size() == sdim and positive entries in nx.
|
||||
*
|
||||
* Assumes elmin, elmax Ordering::byNodes:
|
||||
* elmin -> [x_{0,min},x_{1,min},... ,y_{0,min},y_{1,min},..,z_{nel-1,min}]
|
||||
* elmax -> [x_{0,max},x_{1,max},... ,y_{0,max},y_{1,max},..,z_{nel-1,max}]
|
||||
* Note elmin, elmax can be obtained using GridFunction::GetElementBounds()
|
||||
*/
|
||||
GlobalBBoxTensorGridMap(const MPI_Comm &comm, Vector &elmin,
|
||||
Vector &elmax, int nel, int sdim, Array<int> &nx);
|
||||
|
||||
~GlobalBBoxTensorGridMap();
|
||||
|
||||
/** @brief Get list of procs corresponding to the list of points.
|
||||
*
|
||||
* @details This method must be called collectively on the communicator
|
||||
* used to construct the map. The input points can be ordered byNodes:
|
||||
* (XXX...,YYY...,ZZZ) or byVDIM: (XYZ,XYZ,...), as specified by
|
||||
* \a ordering.
|
||||
*
|
||||
* The output map contains one entry for each input point, keyed by the
|
||||
* point's local index in \a xyz. Points with no candidate ranks, including
|
||||
* points outside the global bounding box, have an empty list of candidate
|
||||
* ranks.
|
||||
*/
|
||||
void MapPointsToProcs(Vector &xyz, int ordering,
|
||||
std::map<int, std::vector<int>> &pt_to_procs) const;
|
||||
|
||||
// Some getters
|
||||
const Array<int> &GetGridMap() const { return ggrid_map; }
|
||||
const Vector &GetGridFac() const { return gmap_fac; }
|
||||
const Vector &GetGridMin() const { return gmap_bnd_min; }
|
||||
const Vector &GetGridMax() const { return gmap_bnd_max; }
|
||||
const Array<int> &GetGridN() const { return gmap_n; }
|
||||
|
||||
private:
|
||||
/// Setup the map given element bounds and number of tensor grid divisions.
|
||||
void Setup(const MPI_Comm &comm, Vector &elmin, Vector &elmax,
|
||||
int nel, Array<int> &nx);
|
||||
|
||||
/// Get global hash cell index for a given point.
|
||||
int GetGlobalGridCellFromPoint(Vector &xyz) const;
|
||||
|
||||
/** @brief Get owning proc and local index on that proc for given global
|
||||
* grid cell index. */
|
||||
void GlobalGridCellToProcAndLocalIndex(int i, int &proc, int &idx) const;
|
||||
|
||||
/// Map a point to proc and local index of the corresponding grid cell
|
||||
void GetProcAndLocalIndexFromPoint(Vector &xyz, int &proc, int &idx) const;
|
||||
|
||||
/// Given local cell index, return list of procs saved in the map
|
||||
Array<int> MapCellToProcs(int l_idx) const;
|
||||
};
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_GSLIB
|
||||
|
||||
@@ -562,7 +562,7 @@ newton_area_fin:
|
||||
int f = flags >> (2 * dd) & 3u;
|
||||
res->r[dd] = f == 0 ? r0[dd] + dr[dd] : (f == 1 ? -1 : 1);
|
||||
}
|
||||
res->flags = flags | (p->flags << 5);
|
||||
res->flags = flags | ((p->flags & FLAG_MASK) << 5);
|
||||
}
|
||||
|
||||
// Full Newton solve on the face. One of r/s/t is constrained.
|
||||
@@ -635,7 +635,8 @@ newton_edge_fin:
|
||||
res->r[de] = nr;
|
||||
res->r[dn]=p->r[dn];
|
||||
res->dist2p = -v;
|
||||
res->flags = flags | new_flags | (p->flags << 5);
|
||||
res->flags = flags | new_flags | ((p->flags & FLAG_MASK) << 5);
|
||||
#undef EVAL
|
||||
}
|
||||
|
||||
// Find closest mesh node to the sought point.
|
||||
@@ -714,7 +715,6 @@ static void FindPointsLocal2D_Kernel(const int npt,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
{
|
||||
#define MAX_CONST(a, b) (((a) > (b)) ? (a) : (b))
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
const int p_NE = D1D*D1D;
|
||||
@@ -729,7 +729,7 @@ static void FindPointsLocal2D_Kernel(const int npt,
|
||||
// 3D1D for seed, 10D1D+6 for area, 3D1D+9 for edge
|
||||
constexpr int size1 = 10*MD1 + 6;
|
||||
constexpr int size2 = MD1*4; // edge constraints
|
||||
constexpr int size3 = MD1*MD1*MD1*DIM; // local element coordinates
|
||||
constexpr int size3 = MD1*MD1*DIM; // local element coordinates
|
||||
|
||||
MFEM_SHARED double r_workspace[size1];
|
||||
MFEM_SHARED findptsElementPoint_t el_pts[2];
|
||||
@@ -1162,9 +1162,9 @@ void FindPointsGSLIB::FindPointsLocal2(const Vector &point_pos,
|
||||
auto pgslm = gsl_mesh.Read();
|
||||
auto pwt = DEV.wtend.Read();
|
||||
auto pbb = DEV.bb.Read();
|
||||
auto plhm = DEV.loc_hash_min.Read();
|
||||
auto plhf = DEV.loc_hash_fac.Read();
|
||||
auto plho = DEV.loc_hash_offset.ReadWrite();
|
||||
auto plhm = DEV.lh_min.Read();
|
||||
auto plhf = DEV.lh_fac.Read();
|
||||
auto plho = DEV.lh_offset.ReadWrite();
|
||||
auto pcode = code.Write();
|
||||
auto pelem = elem.Write();
|
||||
auto pref = ref.Write();
|
||||
@@ -1177,30 +1177,32 @@ void FindPointsGSLIB::FindPointsLocal2(const Vector &point_pos,
|
||||
case 2:
|
||||
return FindPointsLocal2D_Kernel<2>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.h_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
case 3:
|
||||
return FindPointsLocal2D_Kernel<3>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.h_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
case 4:
|
||||
return FindPointsLocal2D_Kernel<4>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.h_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
case 5:
|
||||
return FindPointsLocal2D_Kernel<5>(
|
||||
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
|
||||
pbb, DEV.h_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
|
||||
pgll1d, plc);
|
||||
default:
|
||||
return FindPointsLocal2D_Kernel(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx,
|
||||
plhm, plhf, plho, pcode, pelem,
|
||||
pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
}
|
||||
}
|
||||
#undef DIM2
|
||||
#undef DIM
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
|
||||
@@ -706,7 +706,7 @@ newton_vol_fin:
|
||||
int f = flags >> (2*dd) & 3u;
|
||||
res->r[dd] = f == 0 ? r0[dd]+dr[dd] : (f == 1 ? -1 : 1);
|
||||
}
|
||||
res->flags = flags | (p->flags << 7);
|
||||
res->flags = flags | ((p->flags & FLAG_MASK) << 7);
|
||||
}
|
||||
|
||||
// Full Newton solve on the face. One of r/s/t is constrained.
|
||||
@@ -889,7 +889,7 @@ newton_face_fin:
|
||||
res->r[dn] = p->r[dn];
|
||||
res->r[d1] = r[0];
|
||||
res->r[d2] = r[1];
|
||||
res->flags = new_flags | (p->flags << 7);
|
||||
res->flags = new_flags | ((p->flags & FLAG_MASK) << 7);
|
||||
}
|
||||
|
||||
// Full Newton solve on the edge. Two of r/s/t are constrained.
|
||||
@@ -973,7 +973,8 @@ newton_edge_fin:
|
||||
res->r[dn1] = p->r[dn1];
|
||||
res->r[dn2] = p->r[dn2];
|
||||
res->dist2p = -v;
|
||||
res->flags = flags | new_flags | (p->flags << 7);
|
||||
res->flags = flags | new_flags | ((p->flags & FLAG_MASK) << 7);
|
||||
#undef EVAL
|
||||
}
|
||||
|
||||
// Find closest mesh node to the sought point.
|
||||
@@ -1252,7 +1253,6 @@ static void FindPointsLocal3DKernel(const int npt,
|
||||
case 0: // findpt_vol
|
||||
{
|
||||
double *wtr = r_workspace_ptr;
|
||||
|
||||
double *resid = wtr+6*D1D;
|
||||
double *jac = resid+3;
|
||||
double *resid_temp = jac+9;
|
||||
@@ -1503,7 +1503,7 @@ static void FindPointsLocal3DKernel(const int npt,
|
||||
// Hes_T is transposed version (i.e. in col major)
|
||||
// n1*[2, 1, 1, 0, 0]
|
||||
// j==1 => wt_j = wt+n1
|
||||
double *wt_j = wt+D1D*(2-(row+1) / 2);
|
||||
double *wt_j = wt+D1D*(2 - (row+1)/2);
|
||||
const double *x = e_x[row+1][d];
|
||||
hes_T[j] = 0.0;
|
||||
for (int k = 0; k < D1D; ++k)
|
||||
@@ -1522,7 +1522,6 @@ static void FindPointsLocal3DKernel(const int npt,
|
||||
hes[j] += resid[d]*hes_T[j*3+d];
|
||||
}
|
||||
}
|
||||
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(l,x,1)
|
||||
@@ -1780,6 +1779,7 @@ static void FindPointsLocal3DKernel(const int npt,
|
||||
} //findpts_local
|
||||
} //elp
|
||||
});
|
||||
#undef MAXC
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPointsLocal3(const Vector &point_pos,
|
||||
@@ -1796,9 +1796,9 @@ void FindPointsGSLIB::FindPointsLocal3(const Vector &point_pos,
|
||||
auto pgslm = gsl_mesh.Read();
|
||||
auto pwt = DEV.wtend.Read();
|
||||
auto pbb = DEV.bb.Read();
|
||||
auto plhm = DEV.loc_hash_min.Read();
|
||||
auto plhf = DEV.loc_hash_fac.Read();
|
||||
auto plho = DEV.loc_hash_offset.ReadWrite();
|
||||
auto plhm = DEV.lh_min.Read();
|
||||
auto plhf = DEV.lh_fac.Read();
|
||||
auto plho = DEV.lh_offset.ReadWrite();
|
||||
auto pcode = code.Write();
|
||||
auto pelem = elem.Write();
|
||||
auto pref = ref.Write();
|
||||
@@ -1809,31 +1809,31 @@ void FindPointsGSLIB::FindPointsLocal3(const Vector &point_pos,
|
||||
{
|
||||
case 2:
|
||||
FindPointsLocal3DKernel<2>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx, plhm,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
break;
|
||||
case 3:
|
||||
FindPointsLocal3DKernel<3>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx, plhm,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
break;
|
||||
case 4:
|
||||
FindPointsLocal3DKernel<4>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx, plhm,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
break;
|
||||
case 5:
|
||||
FindPointsLocal3DKernel<5>(npt, DEV.newt_tol, pp, point_pos_ordering,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.h_nx, plhm,
|
||||
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
|
||||
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
|
||||
plc);
|
||||
break;
|
||||
default:
|
||||
FindPointsLocal3DKernel(npt, DEV.newt_tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.h_nx, plhm, plhf,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc,
|
||||
DEV.dof1d);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,725 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
#include "gslib.h"
|
||||
#ifndef GSLIB_RELEASE_VERSION //gslib v1.0.7
|
||||
#define GSLIB_RELEASE_VERSION 10007
|
||||
#endif
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
#if GSLIB_RELEASE_VERSION >= 10009
|
||||
#define CODE_INTERNAL 0
|
||||
#define CODE_BORDER 1
|
||||
#define CODE_NOT_FOUND 2
|
||||
#define sDIM 2
|
||||
#define sDIM2 4
|
||||
#define rDIM 1
|
||||
|
||||
struct findptsElementPoint_t
|
||||
{
|
||||
double x[sDIM], r, oldr, dist2, dist2p, tr;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct findptsElementGEdge_t
|
||||
{
|
||||
double *x[sDIM];
|
||||
};
|
||||
|
||||
struct findptsElementGPT_t
|
||||
{
|
||||
double x[sDIM], jac[sDIM*rDIM], hes[sDIM*rDIM];
|
||||
};
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[sDIM], A[sDIM*sDIM];
|
||||
dbl_range_t x[sDIM];
|
||||
};
|
||||
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[sDIM];
|
||||
double fac[sDIM];
|
||||
unsigned int *offset;
|
||||
};
|
||||
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j = 0; j < pN; ++j)
|
||||
{
|
||||
if (i != j)
|
||||
{
|
||||
double d_j = 2 * (x-z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
double *p1 = p0 + pN, *p2 = p0 + 2 * pN;
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p1[i] = 2.0 * lCoeff[i] * u1;
|
||||
p2[i] = 8.0 * lCoeff[i] * u2;
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double obbox_axis_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
double b_d;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
|
||||
if (b_d < 0) // if outside in any dimension
|
||||
{
|
||||
return b_d;
|
||||
}
|
||||
}
|
||||
return b_d; // only positive if inside
|
||||
}
|
||||
|
||||
/* positive when given point is possibly inside given obbox b */
|
||||
static MFEM_HOST_DEVICE inline double obbox_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const double bxyz = obbox_axis_test(b,x);
|
||||
if (bxyz<0) // test if point is in AABB
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
else // test OBB only if inside AABB
|
||||
{
|
||||
double dxyz[sDIM];
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dxyz[d] = x[d] - b->c0[d];
|
||||
}
|
||||
double test = 1;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
double rst = 0;
|
||||
for (int e=0; e<sDIM; ++e)
|
||||
{
|
||||
rst += b->A[d*2 + e] * dxyz[e];
|
||||
}
|
||||
double brst = (rst+1)*(1-rst);
|
||||
test = test<0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash index in the hash table to the elements that possibly contain the point x */
|
||||
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
|
||||
const double x[2])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d=sDIM-1; d>=0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
|
||||
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline double l2norm2(const double x[2])
|
||||
{
|
||||
return x[0] * x[0] + x[1] * x[1];
|
||||
}
|
||||
|
||||
/* the bit structure of flags is CRR
|
||||
the C bit --- 1<<2 --- is set when the point is converged
|
||||
RR is 0 = 00b if r is unconstrained,
|
||||
1 = 01b if r is constrained at -1, i.e., rmin
|
||||
2 = 10b if r is constrained at +1, i.e., rmax
|
||||
*/
|
||||
|
||||
#define CONVERGED_FLAG (1u<<2)
|
||||
#define FLAG_MASK 0x07u // = 111b
|
||||
|
||||
/* returns 1 if r direction (the only free direction in 2D) is constrained.
|
||||
returns 1 if either 1st or 2nd bit of flags is set.
|
||||
*/
|
||||
static MFEM_HOST_DEVICE inline int num_constrained(const int flags)
|
||||
{
|
||||
return ((flags | flags>>1) & 1u);
|
||||
}
|
||||
|
||||
/* pi=0, r=-1; pi=1, r=+1 */
|
||||
static MFEM_HOST_DEVICE inline int point_index(const int x)
|
||||
{
|
||||
return ((x>>1) & 1u);
|
||||
}
|
||||
|
||||
/* check reduction in objective against prediction, and adjust
|
||||
trust region radius (p->tr) accordingly;
|
||||
may reject the prior step, returning 1; otherwise returns 0
|
||||
sets out->dist2, out->index, out->x, out->oldr in any event,
|
||||
leaving out->r, out->dr, out->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
const double resid[2],
|
||||
const findptsElementPoint_t *p,
|
||||
const double tol)
|
||||
{
|
||||
const double dist2 = l2norm2(resid);
|
||||
const double decr = p->dist2 - dist2;
|
||||
const double pred = p->dist2p;
|
||||
out->x[0] = p->x[0];
|
||||
out->x[1] = p->x[1];
|
||||
out->oldr = p->r;
|
||||
out->dist2 = dist2;
|
||||
if (decr >= 0.01*pred)
|
||||
{
|
||||
if (decr >= 0.9*pred) // very good iteration
|
||||
{
|
||||
out->tr = p->tr*2;
|
||||
}
|
||||
else // somewhat good iteration
|
||||
{
|
||||
out->tr = p->tr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* reject step; note: the point will pass through this routine
|
||||
again, and we set things up here so it gets classed as a
|
||||
"very good iteration" --- this doubles the trust radius,
|
||||
which is why we divide by 4 below */
|
||||
double v0 = fabs(p->r - p->oldr);
|
||||
out->tr = v0/4.0;
|
||||
out->dist2 = p->dist2;
|
||||
out->r = p->oldr;
|
||||
out->flags = p->flags>>3;
|
||||
out->dist2p = -HUGE_VAL;
|
||||
if (pred < dist2*tol)
|
||||
{
|
||||
out->flags |= CONVERGED_FLAG;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline void newton_edge( findptsElementPoint_t *const
|
||||
out,
|
||||
const double jac[2],
|
||||
const double rhess,
|
||||
const double resid[2],
|
||||
int flags,
|
||||
const findptsElementPoint_t *const p,
|
||||
const double tol )
|
||||
{
|
||||
const double tr = p->tr;
|
||||
const double A = jac[0] * jac[0] + jac[1] * jac[1] -
|
||||
rhess; // A = J^T J - resid_d H_d
|
||||
const double y = jac[0]*resid[0] + jac[1]*resid[1]; // y = J^T resid
|
||||
|
||||
const double oldr = p->r;
|
||||
double dr, newr, tdr, tnewr, v, tv;
|
||||
int new_flags=0, tnew_flags=0;
|
||||
|
||||
#define EVAL(dr) ( (dr*A - 2*y) * dr )
|
||||
if (A>0)
|
||||
{
|
||||
dr = y/A;
|
||||
if (fabs(dr)<tol)
|
||||
{
|
||||
dr=0.0;
|
||||
newr = oldr;
|
||||
}
|
||||
else
|
||||
{
|
||||
newr = oldr+dr;
|
||||
}
|
||||
|
||||
if (fabs(dr)<tr && fabs(newr)<1)
|
||||
{
|
||||
v = EVAL(dr);
|
||||
goto newton_edge_fin;
|
||||
}
|
||||
}
|
||||
|
||||
if ((newr=oldr-tr) > -1)
|
||||
{
|
||||
dr = -tr;
|
||||
}
|
||||
else
|
||||
{
|
||||
newr = -1, dr = -1-oldr, new_flags = flags|1u;
|
||||
}
|
||||
v = EVAL(dr);
|
||||
|
||||
if ((tnewr=oldr+tr) < 1)
|
||||
{
|
||||
tdr = tr;
|
||||
}
|
||||
else
|
||||
{
|
||||
tnewr = 1, tdr = 1-oldr, tnew_flags = flags|2u;
|
||||
}
|
||||
tv = EVAL(tdr);
|
||||
|
||||
if (tv<v)
|
||||
{
|
||||
newr = tnewr, dr = tdr, v = tv, new_flags = tnew_flags;
|
||||
}
|
||||
#undef EVAL
|
||||
|
||||
newton_edge_fin:
|
||||
// check convergence by testing if change in r is less than tol
|
||||
if (fabs(dr)<tol)
|
||||
{
|
||||
new_flags |= CONVERGED_FLAG;
|
||||
}
|
||||
out->r = newr;
|
||||
out->dist2p = -v;
|
||||
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE void seed_j( const double *elx[sDIM],
|
||||
const double x[sDIM],
|
||||
const double *z,
|
||||
double *dist2,
|
||||
double *r,
|
||||
const int ir,
|
||||
const int pN )
|
||||
{
|
||||
double dx[sDIM];
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dx[d] = x[d] - elx[d][ir];
|
||||
}
|
||||
dist2[ir] = HUGE_VAL;
|
||||
const double dist2_rs = l2norm2(dx);
|
||||
if (dist2[ir]>dist2_rs)
|
||||
{
|
||||
dist2[ir] = dist2_rs;
|
||||
r[ir] = z[ir];
|
||||
}
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void FindPointsEdgeLocal2D_Kernel( const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0 )
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
const int p_NEL = nel*D1D;
|
||||
MFEM_VERIFY(MD1<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(pN<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(D1D!=0, "Polynomial order not specified.");
|
||||
const int nThreads = D1D*sDIM;
|
||||
|
||||
mfem::forall_2D(npt, nThreads, 1, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
// 2D1D for seed, 3D1D + 7 for edge
|
||||
constexpr int size1 = 3*MD1 + 7;
|
||||
// edge coordinates = D1D*2
|
||||
constexpr int size2 = 2*MD1;
|
||||
// local element coordinates in shared memory
|
||||
constexpr int size3 = MD1*sDIM;
|
||||
|
||||
MFEM_SHARED findptsElementPoint_t el_pts[2];
|
||||
MFEM_SHARED double r_workspace[size1];
|
||||
|
||||
MFEM_SHARED double constraint_workspace[size2];
|
||||
|
||||
MFEM_SHARED double elem_coords[MD1 <= 6 ? size3 : 1];
|
||||
|
||||
double *r_workspace_ptr = r_workspace;
|
||||
findptsElementPoint_t *fpt, *tmp;
|
||||
fpt = el_pts + 0;
|
||||
tmp = el_pts + 1;
|
||||
|
||||
// x and y coord index within point_pos for point i
|
||||
int id_x = point_pos_ordering == 0 ? i : i*sDIM;
|
||||
int id_y = point_pos_ordering == 0 ? i+npt : i*sDIM+1;
|
||||
double x_i[2] = {x[id_x], x[id_y]};
|
||||
|
||||
unsigned int *code_i = code_base + i;
|
||||
double *dist2_i = dist2_base + i;
|
||||
|
||||
//---------------- map_points_to_els --------------------
|
||||
findptsLocalHashData_t hash;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
hash.bnd[d].min = hashMin[d];
|
||||
hash.fac[d] = hashFac[d];
|
||||
}
|
||||
hash.hash_n = hash_n;
|
||||
hash.offset = hashOffset;
|
||||
|
||||
const int hi = hash_index(&hash, x_i);
|
||||
const unsigned int *elp = hash.offset + hash.offset[hi];
|
||||
const unsigned int *const ele = hash.offset + hash.offset[hi+1];
|
||||
*code_i = CODE_NOT_FOUND;
|
||||
*dist2_i = HUGE_VAL;
|
||||
|
||||
for (; elp!=ele; ++elp)
|
||||
{
|
||||
const unsigned int el = *elp;
|
||||
|
||||
obbox_t box;
|
||||
int n_box_ents = 3*sDIM + sDIM2;
|
||||
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
}
|
||||
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
}
|
||||
|
||||
if (obbox_test(&box,x_i)>=0)
|
||||
{
|
||||
//------------ findpts_local ------------------
|
||||
{
|
||||
if (MD1 <= 6)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,D1D*sDIM)
|
||||
{
|
||||
const int qp = j % D1D;
|
||||
const int d = j / D1D;
|
||||
elem_coords[qp + d*D1D] =
|
||||
xElemCoord[qp + el*D1D + d*p_NEL];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
const double *elx[sDIM];
|
||||
for (int d=0; d<sDIM; d++)
|
||||
{
|
||||
elx[d] = MD1<= 6 ? &elem_coords[d*D1D] :
|
||||
xElemCoord + d*p_NEL + el*D1D;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
//// findpts_el ////
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
fpt->dist2 = HUGE_VAL;
|
||||
fpt->dist2p = 0;
|
||||
fpt->tr = 1;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
fpt->x[j] = x_i[j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
{
|
||||
double *dist2_temp = r_workspace_ptr;
|
||||
double *r_temp = dist2_temp + D1D;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
seed_j(elx, x_i, gll1D, dist2_temp, r_temp, j, D1D);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
for (int ir=0; ir<D1D; ++ir)
|
||||
{
|
||||
if (dist2_temp[ir]<fpt->dist2)
|
||||
{
|
||||
fpt->dist2 = dist2_temp[ir];
|
||||
fpt->r = r_temp[ir];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
} //seed done
|
||||
|
||||
// Initialize tmp struct with fpt values before starting Newton iterations
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
tmp->dist2 = HUGE_VAL;
|
||||
tmp->dist2p = 0;
|
||||
tmp->tr = 1;
|
||||
tmp->flags = 0;
|
||||
tmp->r = fpt->r;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
tmp->x[j] = fpt->x[j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
|
||||
for (int step=0; step<50; step++)
|
||||
{
|
||||
int nc = num_constrained(tmp->flags & FLAG_MASK);
|
||||
switch (nc)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
double *wt = r_workspace_ptr;
|
||||
double *resid = wt + 3*D1D;
|
||||
double *jac = resid + sDIM;
|
||||
double *hess = jac + sDIM*rDIM;
|
||||
|
||||
findptsElementGEdge_t edge;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = constraint_workspace + d*D1D;
|
||||
edge.x[d][j] = elx[d][j];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// compute basis function info upto 2nd derivative
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
lag_eval_second_der(wt, tmp->r, j, gll1D,
|
||||
lagcoeff, D1D);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
resid[j] = tmp->x[j];
|
||||
jac[j] = 0.0;
|
||||
hess[j] = 0.0;
|
||||
for (int k=0; k<D1D; ++k)
|
||||
{
|
||||
resid[j] -= wt[ k]*edge.x[j][k];
|
||||
jac[j] += wt[D1D+k]*edge.x[j][k];
|
||||
hess[j] += wt[2*D1D+k]*edge.x[j][k];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
hess[2] = resid[0]*hess[0] + resid[1]*hess[1];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
if (!reject_prior_step_q(fpt, resid, tmp, tol))
|
||||
{
|
||||
newton_edge(fpt, jac, hess[2], resid,
|
||||
tmp->flags & FLAG_MASK, tmp, tol);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
break;
|
||||
}
|
||||
case 1: // r is constrained to either -1 or 1
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
const int pi = point_index(tmp->flags &
|
||||
FLAG_MASK);
|
||||
const double *wt = wtend + pi*3*D1D;
|
||||
findptsElementGPT_t gpt;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
gpt.x[d] = elx[d][pi*(D1D-1)];
|
||||
gpt.jac[d] = 0.0;
|
||||
gpt.hes[d] = 0.0;
|
||||
for (int k=0; k<D1D; ++k)
|
||||
{
|
||||
gpt.jac[d] += wt[D1D +k]*elx[d][k];
|
||||
gpt.hes[d] += wt[2*D1D+k]*elx[d][k];
|
||||
}
|
||||
}
|
||||
|
||||
const double *const pt_x = gpt.x;
|
||||
const double *const jac = gpt.jac;
|
||||
const double *const hes = gpt.hes;
|
||||
double resid[sDIM], steep, sr;
|
||||
resid[0] = fpt->x[0] - pt_x[0];
|
||||
resid[1] = fpt->x[1] - pt_x[1];
|
||||
steep = jac[0]*resid[0] + jac[1]*resid[1];
|
||||
sr = steep*tmp->r;
|
||||
if ( !reject_prior_step_q(fpt, resid, tmp, tol) )
|
||||
{
|
||||
if (sr<0)
|
||||
{
|
||||
const double rhess = resid[0]*hes[0] +
|
||||
resid[1]*hes[1];
|
||||
newton_edge(fpt, jac, rhess,
|
||||
resid, 0, tmp, tol);
|
||||
}
|
||||
else // sr==0
|
||||
{
|
||||
fpt->r = tmp->r;
|
||||
fpt->dist2p = 0;
|
||||
fpt->flags = tmp->flags | CONVERGED_FLAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
break;
|
||||
} // case 1
|
||||
} //switch
|
||||
if (fpt->flags & CONVERGED_FLAG)
|
||||
{
|
||||
break;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
*tmp = *fpt;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
} //for int step<50
|
||||
} //findpts_el
|
||||
|
||||
bool converged_internal =
|
||||
((fpt->flags&FLAG_MASK) == CONVERGED_FLAG) &&
|
||||
(fpt->dist2<dist2tol);
|
||||
|
||||
if (*code_i == CODE_NOT_FOUND || converged_internal ||
|
||||
fpt->dist2 < *dist2_i)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
*(el_base+i) = el;
|
||||
*code_i = converged_internal ? CODE_INTERNAL : CODE_BORDER;
|
||||
*dist2_i = fpt->dist2;
|
||||
*(r_base+i) = fpt->r;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (converged_internal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
} //findpts_local
|
||||
} //obbox_test
|
||||
} //elp
|
||||
});
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPointsEdgeLocal2( const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &code,
|
||||
Array<unsigned int> &elem,
|
||||
Vector &ref,
|
||||
Vector &dist,
|
||||
int npt )
|
||||
{
|
||||
if (npt==0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
MFEM_VERIFY(dim==1 && spacedim==2,"Function for 2D edges only");
|
||||
bool use_dev = point_pos.UseDevice();
|
||||
auto pp = point_pos.Read(use_dev);
|
||||
auto pgslm = gsl_mesh.Read(use_dev);
|
||||
auto pwt = DEV.wtend.Read(use_dev);
|
||||
auto pbb = DEV.bb.Read(use_dev);
|
||||
auto plhm = DEV.lh_min.Read(use_dev);
|
||||
auto plhf = DEV.lh_fac.Read(use_dev);
|
||||
auto plho = DEV.lh_offset.ReadWrite(use_dev);
|
||||
auto pcode = code.Write(use_dev);
|
||||
auto pelem = elem.Write(use_dev);
|
||||
auto pref = ref.Write(use_dev);
|
||||
auto pdist = dist.Write(use_dev);
|
||||
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
|
||||
auto plc = DEV.lagcoeff.Read(use_dev);
|
||||
double dist2tol = DEV.surf_dist_tol;
|
||||
switch (DEV.dof1d)
|
||||
{
|
||||
case 2:
|
||||
return FindPointsEdgeLocal2D_Kernel<2>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
case 3:
|
||||
return FindPointsEdgeLocal2D_Kernel<3>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
case 4:
|
||||
return FindPointsEdgeLocal2D_Kernel<4>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
default:
|
||||
return FindPointsEdgeLocal2D_Kernel(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
}
|
||||
}
|
||||
#undef sDIM
|
||||
#undef rDIM
|
||||
#undef sDIM2
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
#else
|
||||
void FindPointsGSLIB::FindPointsEdgeLocal2( const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &code,
|
||||
Array<unsigned int> &elem,
|
||||
Vector &ref,
|
||||
Vector &dist,
|
||||
int npt ) {} ;
|
||||
#endif
|
||||
} // namespace mfem
|
||||
|
||||
#endif //ifdef MFEM_USE_GSLIB
|
||||
@@ -0,0 +1,733 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
#include "gslib.h"
|
||||
#ifndef GSLIB_RELEASE_VERSION //gslib v1.0.7
|
||||
#define GSLIB_RELEASE_VERSION 10007
|
||||
#endif
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
#if GSLIB_RELEASE_VERSION >= 10009
|
||||
#define CODE_INTERNAL 0
|
||||
#define CODE_BORDER 1
|
||||
#define CODE_NOT_FOUND 2
|
||||
#define sDIM 3
|
||||
#define rDIM 1
|
||||
#define sDIM2 (sDIM*sDIM)
|
||||
#define rDIM2 (rDIM*rDIM)
|
||||
|
||||
struct findptsElementPoint_t
|
||||
{
|
||||
double x[sDIM], r, oldr, dist2, dist2p, tr;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct findptsElementGEdge_t
|
||||
{
|
||||
double *x[sDIM], *dxdn[sDIM], *d2xdn[sDIM];
|
||||
};
|
||||
|
||||
struct findptsElementGPT_t
|
||||
{
|
||||
double x[sDIM], jac[sDIM], hes[sDIM*(1+1)];
|
||||
};
|
||||
|
||||
struct dbl_range_t
|
||||
{
|
||||
double min, max;
|
||||
};
|
||||
|
||||
struct obbox_t
|
||||
{
|
||||
double c0[sDIM], A[sDIM*sDIM];
|
||||
dbl_range_t x[sDIM];
|
||||
};
|
||||
|
||||
struct findptsLocalHashData_t
|
||||
{
|
||||
int hash_n;
|
||||
dbl_range_t bnd[sDIM];
|
||||
double fac[sDIM];
|
||||
unsigned int *offset;
|
||||
};
|
||||
|
||||
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
|
||||
int i, const double *z,
|
||||
const double *lCoeff,
|
||||
int pN)
|
||||
{
|
||||
double u0 = 1, u1 = 0, u2 = 0;
|
||||
for (int j=0; j<pN; ++j)
|
||||
{
|
||||
if (i!=j)
|
||||
{
|
||||
double d_j = 2 * (x-z[j]);
|
||||
u2 = d_j * u2 + u1;
|
||||
u1 = d_j * u1 + u0;
|
||||
u0 = d_j * u0;
|
||||
}
|
||||
}
|
||||
double *p1 = p0 + pN, *p2 = p0 + 2 * pN;
|
||||
p0[i] = lCoeff[i] * u0;
|
||||
p1[i] = 2.0 * lCoeff[i] * u1;
|
||||
p2[i] = 8.0 * lCoeff[i] * u2;
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double obbox_axis_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
double b_d;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
|
||||
if (b_d < 0) // if outside in any dimension
|
||||
{
|
||||
return b_d;
|
||||
}
|
||||
}
|
||||
return b_d; // only positive if inside in all dimensions
|
||||
}
|
||||
|
||||
/* positive when possibly inside */
|
||||
static MFEM_HOST_DEVICE inline double obbox_test(const obbox_t *const b,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const double bxyz = obbox_axis_test(b, x);
|
||||
if (bxyz<0)
|
||||
{
|
||||
return bxyz;
|
||||
}
|
||||
else
|
||||
{
|
||||
double dxyz[3];
|
||||
// dxyz: distance of the point from the center of the OBB
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dxyz[d] = x[d] - b->c0[d];
|
||||
}
|
||||
// transform dxyz to the local coordinate system of the OBB,
|
||||
// and check if the point is inside the OBB [-1,1]^sDIM
|
||||
double test = 1;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
double rst = 0;
|
||||
for (int e=0; e<sDIM; ++e)
|
||||
{
|
||||
rst += b->A[d*sDIM + e] * dxyz[e];
|
||||
}
|
||||
double brst = (rst+1)*(1-rst);
|
||||
test = test<0 ? test : brst;
|
||||
}
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hash index in the hash table to the elements that possibly contain the point x */
|
||||
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
|
||||
const double x[sDIM])
|
||||
{
|
||||
const int n = p->hash_n;
|
||||
int sum = 0;
|
||||
for (int d=sDIM-1; d>=0; --d)
|
||||
{
|
||||
sum *= n;
|
||||
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
|
||||
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
|
||||
static MFEM_HOST_DEVICE inline double norm2(const double x[sDIM])
|
||||
{
|
||||
return ( x[0]*x[0] + x[1]*x[1] + x[2]*x[2] );
|
||||
}
|
||||
|
||||
/* the bit structure of flags is CRR
|
||||
the C bit --- 1<<2 --- is set when the point is converged
|
||||
RR is 0 = 00b if r is unconstrained,
|
||||
1 = 01b if r is constrained at -1, i.e., rmin
|
||||
2 = 10b if r is constrained at +1, i.e., rmax
|
||||
*/
|
||||
#define CONVERGED_FLAG (1u<<2)
|
||||
#define FLAG_MASK 0x07u
|
||||
|
||||
/* returns the number of constrained reference coordinates, max 2
|
||||
*/
|
||||
static MFEM_HOST_DEVICE inline int num_constrained(const int flags)
|
||||
{
|
||||
const int y = (flags | flags>>1);
|
||||
return (y & 1u) + (y>>2 & 1u);
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline int point_index(const int x)
|
||||
{
|
||||
return ((x>>1)&1u) | ((x>>2)&2u);
|
||||
}
|
||||
|
||||
/* check reduction in objective against prediction, and adjust
|
||||
trust region radius (p->tr) accordingly;
|
||||
may reject the prior step, returning 1; otherwise returns 0
|
||||
sets out->dist2, out->index, out->x, out->oldr in any event,
|
||||
leaving out->r, out->dr, out->flags to be set when returning 0 */
|
||||
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
|
||||
const double resid[3],
|
||||
const findptsElementPoint_t *p,
|
||||
const double tol)
|
||||
{
|
||||
const double dist2 = norm2(resid);
|
||||
const double decr = p->dist2 - dist2;
|
||||
const double pred = p->dist2p;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
out->x[d] = p->x[d];
|
||||
}
|
||||
out->oldr = p->r;
|
||||
out->dist2 = dist2;
|
||||
if (decr>=0.01*pred)
|
||||
{
|
||||
if (decr>=0.9*pred) // very good iteration
|
||||
{
|
||||
out->tr = 2*p->tr;
|
||||
}
|
||||
else // good iteration
|
||||
{
|
||||
out->tr = p->tr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else // if the iteration in not good
|
||||
{
|
||||
/* reject step; note: the point will pass through this routine
|
||||
again, and we set things up here so it gets classed as a
|
||||
"very good iteration" --- this doubles the trust radius,
|
||||
which is why we divide by 4 below */
|
||||
double v0 = fabs(p->r - p->oldr);
|
||||
out->tr = v0/4.0;
|
||||
out->dist2 = p->dist2;
|
||||
out->r = p->oldr;
|
||||
out->flags = p->flags>>3;
|
||||
out->dist2p = -HUGE_VAL;
|
||||
if (pred<dist2*tol)
|
||||
{
|
||||
out->flags |= CONVERGED_FLAG;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE inline void newton_edge(findptsElementPoint_t *const
|
||||
out,
|
||||
const double jac[sDIM*rDIM],
|
||||
const double rhes,
|
||||
const double resid[sDIM],
|
||||
int flags,
|
||||
const findptsElementPoint_t *const p,
|
||||
const double tol)
|
||||
{
|
||||
const double tr = p->tr;
|
||||
/* A = J^T J - resid_d H_d */
|
||||
const double A = jac[0]*jac[0]+ jac[1] * jac[1] + jac[2] * jac[2]
|
||||
- rhes;
|
||||
/* y = J^T r */
|
||||
const double y = jac[0]*resid[0] + jac[1]*resid[1] + jac[0+2]*resid[2];
|
||||
|
||||
const double oldr = p->r;
|
||||
double dr, nr, tdr, tnr;
|
||||
double v, tv;
|
||||
int new_flags = 0, tnew_flags = 0;
|
||||
|
||||
#define EVAL(dr) (dr*A - 2*y)*dr
|
||||
|
||||
/* if A is not SPD, quadratic model has no minimum */
|
||||
if (A>0)
|
||||
{
|
||||
dr = y/A;
|
||||
|
||||
if (fabs(dr)<tol)
|
||||
{
|
||||
dr=0.0;
|
||||
nr = oldr;
|
||||
}
|
||||
else
|
||||
{
|
||||
nr = oldr+dr;
|
||||
}
|
||||
if ( fabs(dr)<tr && fabs(nr)<1 )
|
||||
{
|
||||
v = EVAL(dr);
|
||||
goto newton_edge_fin;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (nr=oldr-tr)>-1 )
|
||||
{
|
||||
dr = -tr;
|
||||
}
|
||||
else
|
||||
{
|
||||
nr = -1, dr = -1-oldr, new_flags = flags | 1u;
|
||||
}
|
||||
v = EVAL(dr);
|
||||
|
||||
if ( (tnr = oldr+tr)<1 )
|
||||
{
|
||||
tdr = tr;
|
||||
}
|
||||
else
|
||||
{
|
||||
tnr = 1, tdr = 1-oldr, tnew_flags = flags | 2u;
|
||||
}
|
||||
tv = EVAL(tdr);
|
||||
|
||||
if (tv<v)
|
||||
{
|
||||
nr = tnr, dr = tdr, v = tv, new_flags = tnew_flags;
|
||||
}
|
||||
|
||||
newton_edge_fin:
|
||||
/* check convergence */
|
||||
if ( fabs(dr)<tol )
|
||||
{
|
||||
new_flags |= CONVERGED_FLAG;
|
||||
}
|
||||
out->r = nr;
|
||||
out->dist2p = -v;
|
||||
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
|
||||
#undef EVAL
|
||||
}
|
||||
|
||||
static MFEM_HOST_DEVICE void seed_j(const double *elx[sDIM],
|
||||
const double x[sDIM],
|
||||
const double *z,
|
||||
double *dist2,
|
||||
double *r,
|
||||
const int ir,
|
||||
const int pN)
|
||||
{
|
||||
if (ir>=pN)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double dx[sDIM];
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
dx[d] = x[d] - elx[d][ir];
|
||||
}
|
||||
dist2[ir] = norm2(dx);;
|
||||
r[ir] = z[ir];
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void FindPointsEdgeLocal3D_Kernel(const int npt,
|
||||
const double tol,
|
||||
const double dist2tol,
|
||||
const double *x,
|
||||
const int point_pos_ordering,
|
||||
const double *xElemCoord,
|
||||
const int nel,
|
||||
const double *wtend,
|
||||
const double *boxinfo,
|
||||
const int hash_n,
|
||||
const double *hashMin,
|
||||
const double *hashFac,
|
||||
unsigned int *hashOffset,
|
||||
unsigned int *const code_base,
|
||||
unsigned int *const el_base,
|
||||
double *const r_base,
|
||||
double *const dist2_base,
|
||||
const double *gll1D,
|
||||
const double *lagcoeff,
|
||||
const int pN = 0)
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
const int p_NEL = nel*D1D;
|
||||
MFEM_VERIFY(MD1<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(pN<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(D1D!=0, "Polynomial order not specified.");
|
||||
const int nThreads = D1D*sDIM;
|
||||
|
||||
mfem::forall_2D(npt, nThreads, 1, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
constexpr int size1 = 3*MD1 + 13;
|
||||
constexpr int size2 = 3*MD1;
|
||||
constexpr int size3 = MD1*sDIM;
|
||||
|
||||
MFEM_SHARED findptsElementPoint_t el_pts[2];
|
||||
MFEM_SHARED double r_workspace[size1];
|
||||
|
||||
MFEM_SHARED double constraint_workspace[size2];
|
||||
|
||||
MFEM_SHARED double elem_coords[MD1 <= 6 ? size3 : 1];
|
||||
|
||||
double *r_workspace_ptr = r_workspace;
|
||||
findptsElementPoint_t *fpt, *tmp;
|
||||
fpt = el_pts + 0;
|
||||
tmp = el_pts + 1;
|
||||
|
||||
int id_x = point_pos_ordering==0 ? i : i*sDIM;
|
||||
int id_y = point_pos_ordering==0 ? npt+i : 1+i*sDIM;
|
||||
int id_z = point_pos_ordering==0 ? 2*npt+i : 2+i*sDIM;
|
||||
double x_i[3] = {x[id_x], x[id_y], x[id_z]};
|
||||
|
||||
unsigned int *code_i = code_base + i;
|
||||
double *dist2_i = dist2_base + i;
|
||||
|
||||
//// map_points_to_els ////
|
||||
findptsLocalHashData_t hash;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
hash.bnd[d].min = hashMin[d];
|
||||
hash.fac[d] = hashFac[d];
|
||||
}
|
||||
hash.hash_n = hash_n;
|
||||
hash.offset = hashOffset;
|
||||
|
||||
const unsigned int hi = hash_index(&hash, x_i);
|
||||
const unsigned int *elp = hash.offset + hash.offset[hi];
|
||||
const unsigned int *const ele = hash.offset + hash.offset[hi+1];
|
||||
*code_i = CODE_NOT_FOUND;
|
||||
*dist2_i = HUGE_VAL;
|
||||
|
||||
for (; elp!=ele; ++elp)
|
||||
{
|
||||
const unsigned int el = *elp;
|
||||
obbox_t box;
|
||||
int n_box_ents = 3*sDIM + sDIM2;
|
||||
|
||||
for (int idx = 0; idx < sDIM; ++idx)
|
||||
{
|
||||
box.c0[idx] = boxinfo[n_box_ents*el + idx];
|
||||
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
|
||||
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
|
||||
}
|
||||
for (int idx = 0; idx < sDIM2; ++idx)
|
||||
{
|
||||
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
|
||||
}
|
||||
|
||||
if (obbox_test(&box, x_i)>=0)
|
||||
{
|
||||
//// findpts_local ////
|
||||
{
|
||||
if (MD1 <= 6)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,D1D*sDIM)
|
||||
{
|
||||
const int qp = j % D1D;
|
||||
const int d = j / D1D;
|
||||
elem_coords[qp + d*D1D] =
|
||||
xElemCoord[qp + el*D1D + d*p_NEL];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
const double *elx[sDIM];
|
||||
for (int d=0; d<sDIM; d++)
|
||||
{
|
||||
elx[d] = MD1<= 6 ? &elem_coords[d*D1D] :
|
||||
xElemCoord + d*p_NEL + el*D1D;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
//// findpts_el ////
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
fpt->dist2 = HUGE_VAL;
|
||||
fpt->dist2p = 0;
|
||||
fpt->tr = 1.0;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
fpt->x[j] = x_i[j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
//// seed ////
|
||||
{
|
||||
double *dist2_temp = r_workspace_ptr;
|
||||
double *r_temp = dist2_temp + D1D;
|
||||
MFEM_FOREACH_THREAD(j,x,nThreads)
|
||||
{
|
||||
seed_j(elx, x_i, gll1D, dist2_temp, r_temp, j, D1D);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
fpt->dist2 = HUGE_VAL;
|
||||
for (int ir=0; ir<D1D; ++ir)
|
||||
{
|
||||
if (dist2_temp[ir] < fpt->dist2)
|
||||
{
|
||||
fpt->dist2 = dist2_temp[ir];
|
||||
fpt->r = r_temp[ir];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
} //seed done
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
tmp->dist2 = HUGE_VAL;
|
||||
tmp->dist2p = 0;
|
||||
tmp->tr = 1;
|
||||
tmp->flags = 0;
|
||||
tmp->r = fpt->r;
|
||||
}
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
tmp->x[j] = fpt->x[j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
for (int step=0; step<50; step++)
|
||||
{
|
||||
switch (num_constrained(tmp->flags & FLAG_MASK))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
double *wt = r_workspace_ptr;
|
||||
double *resid = wt + 3*D1D;
|
||||
double *jac = resid + sDIM;
|
||||
double *hess = jac + sDIM*rDIM;
|
||||
|
||||
findptsElementGEdge_t edge;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
edge.x[d] = constraint_workspace + d*D1D;
|
||||
edge.x[d][j] = elx[d][j];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
lag_eval_second_der(wt, tmp->r, j, gll1D,
|
||||
lagcoeff, D1D);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,sDIM)
|
||||
{
|
||||
resid[j] = tmp->x[j];
|
||||
jac[j] = 0.0;
|
||||
hess[j] = 0.0;
|
||||
for (int k=0; k<D1D; ++k)
|
||||
{
|
||||
resid[j] -= wt[ k]*edge.x[j][k];
|
||||
jac[j] += wt[D1D+k]*edge.x[j][k];
|
||||
hess[j] += wt[2*D1D+k]*edge.x[j][k];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
hess[3] = resid[0]*hess[0] + resid[1]*hess[1] +
|
||||
resid[2]*hess[2];
|
||||
}
|
||||
|
||||
MFEM_FOREACH_THREAD(l,x,1)
|
||||
{
|
||||
if (!reject_prior_step_q(fpt,resid,tmp,tol))
|
||||
{
|
||||
newton_edge(fpt,jac,hess[3],resid,
|
||||
tmp->flags&FLAG_MASK,tmp,tol);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
const int pi = point_index(tmp->flags &
|
||||
FLAG_MASK);
|
||||
const double *wt = wtend + pi*3*D1D;
|
||||
findptsElementGPT_t gpt;
|
||||
for (int d=0; d<sDIM; ++d)
|
||||
{
|
||||
gpt.x[d] = elx[d][pi*(D1D-1)];
|
||||
gpt.jac[d] = 0.0;
|
||||
gpt.hes[d] = 0.0;
|
||||
for (int k=0; k<D1D; ++k)
|
||||
{
|
||||
gpt.jac[d] += wt[D1D +k]*elx[d][k];
|
||||
gpt.hes[d] += wt[2*D1D+k]*elx[d][k];
|
||||
}
|
||||
}
|
||||
|
||||
const double *const pt_x = gpt.x;
|
||||
const double *const jac = gpt.jac;
|
||||
const double *const hes = gpt.hes;
|
||||
double resid[sDIM], steep, sr;
|
||||
resid[0] = fpt->x[0] - pt_x[0];
|
||||
resid[1] = fpt->x[1] - pt_x[1];
|
||||
resid[2] = fpt->x[2] - pt_x[2];
|
||||
steep = jac[0]*resid[0] + jac[1]*resid[1] +
|
||||
jac[2]*resid[2];
|
||||
sr = steep*tmp->r;
|
||||
if (!reject_prior_step_q(fpt, resid, tmp, tol))
|
||||
{
|
||||
if (sr<0)
|
||||
{
|
||||
const double rhess = resid[0]*hes[0] +
|
||||
resid[1]*hes[1] +
|
||||
resid[2]*hes[2];
|
||||
newton_edge(fpt, jac, rhess,
|
||||
resid, 0, tmp, tol);
|
||||
}
|
||||
else // sr==0
|
||||
{
|
||||
fpt->r = tmp->r;
|
||||
fpt->dist2p = 0;
|
||||
fpt->flags = tmp->flags | CONVERGED_FLAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
break;
|
||||
} // case 1
|
||||
} //switch
|
||||
if (fpt->flags & CONVERGED_FLAG)
|
||||
{
|
||||
break;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
*tmp = *fpt;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
} // for step<50
|
||||
} // findpts_el
|
||||
|
||||
bool converged_internal =
|
||||
((fpt->flags&FLAG_MASK) == CONVERGED_FLAG) &&
|
||||
(fpt->dist2<dist2tol);
|
||||
if (*code_i==CODE_NOT_FOUND || converged_internal ||
|
||||
fpt->dist2<*dist2_i)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
*(el_base+i) = el;
|
||||
*code_i = converged_internal?CODE_INTERNAL:CODE_BORDER;
|
||||
*dist2_i = fpt->dist2;
|
||||
*(r_base+i) = fpt->r;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
if (converged_internal)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
} // findpts_local
|
||||
} // obbox_test
|
||||
} // elp
|
||||
});
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::FindPointsEdgeLocal3(const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &code,
|
||||
Array<unsigned int> &elem,
|
||||
Vector &ref,
|
||||
Vector &dist,
|
||||
int npt)
|
||||
{
|
||||
if (npt == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
MFEM_VERIFY(spacedim==3 && dim == 1,"Function for 3D edges only");
|
||||
bool use_dev = point_pos.UseDevice();
|
||||
auto pp = point_pos.Read(use_dev);
|
||||
auto pgslm = gsl_mesh.Read(use_dev);
|
||||
auto pwt = DEV.wtend.Read(use_dev);
|
||||
auto pbb = DEV.bb.Read(use_dev);
|
||||
auto plhm = DEV.lh_min.Read(use_dev);
|
||||
auto plhf = DEV.lh_fac.Read(use_dev);
|
||||
auto plho = DEV.lh_offset.ReadWrite(use_dev);
|
||||
auto pcode = code.Write(use_dev);
|
||||
auto pelem = elem.Write(use_dev);
|
||||
auto pref = ref.Write(use_dev);
|
||||
auto pdist = dist.Write(use_dev);
|
||||
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
|
||||
auto plc = DEV.lagcoeff.Read(use_dev);
|
||||
double dist2tol = DEV.surf_dist_tol;
|
||||
switch (DEV.dof1d)
|
||||
{
|
||||
case 2:
|
||||
return FindPointsEdgeLocal3D_Kernel<2>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
case 3:
|
||||
return FindPointsEdgeLocal3D_Kernel<3>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
case 4:
|
||||
return FindPointsEdgeLocal3D_Kernel<4>(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc);
|
||||
default:
|
||||
return FindPointsEdgeLocal3D_Kernel(
|
||||
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
|
||||
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
|
||||
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
|
||||
}
|
||||
}
|
||||
#undef rDIM2
|
||||
#undef sDIM2
|
||||
#undef rDIM
|
||||
#undef sDIM
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
#else
|
||||
void FindPointsGSLIB::FindPointsEdgeLocal3( const Vector &point_pos,
|
||||
int point_pos_ordering,
|
||||
Array<unsigned int> &code,
|
||||
Array<unsigned int> &elem,
|
||||
Vector &ref,
|
||||
Vector &dist,
|
||||
int npt ) {} ;
|
||||
#endif
|
||||
} // namespace mfem
|
||||
|
||||
#endif //ifdef MFEM_USE_GSLIB
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../gslib.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
#include "../../linalg/kernels.hpp"
|
||||
|
||||
#ifdef MFEM_USE_GSLIB
|
||||
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
#include "gslib.h"
|
||||
#ifndef GSLIB_RELEASE_VERSION //gslib v1.0.7
|
||||
#define GSLIB_RELEASE_VERSION 10007
|
||||
#endif
|
||||
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
namespace mfem
|
||||
{
|
||||
#if GSLIB_RELEASE_VERSION >= 10009
|
||||
#define CODE_INTERNAL 0
|
||||
#define CODE_BORDER 1
|
||||
#define CODE_NOT_FOUND 2
|
||||
|
||||
static MFEM_HOST_DEVICE void lagrange_eval(double *p0, double x,
|
||||
int i, int p_Nq,
|
||||
double *z, double *lagrangeCoeff)
|
||||
{
|
||||
double p_i = (1 << (p_Nq - 1));
|
||||
for (int j=0; j<p_Nq; ++j)
|
||||
{
|
||||
p_i *= j==i ? 1 : x-z[j];
|
||||
}
|
||||
p0[i] = lagrangeCoeff[i] * p_i;
|
||||
}
|
||||
|
||||
template<int T_D1D = 0>
|
||||
static void InterpolateLocal1DKernel(const double *const gf_in,
|
||||
int *const el,
|
||||
double *const r,
|
||||
double *const int_out,
|
||||
const int npt,
|
||||
const int nfields,
|
||||
double *gll1D,
|
||||
double *lagcoeff,
|
||||
const int pN = 0)
|
||||
{
|
||||
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
const int D1D = T_D1D ? T_D1D : pN;
|
||||
const int p_Nq = D1D;
|
||||
MFEM_VERIFY(MD1 <= DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(pN<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(D1D != 0, "Polynomial order not specified.");
|
||||
// for each point of the npt points, create a thread block of size dof1Dsol
|
||||
mfem::forall_2D(npt, D1D, 1, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
MFEM_SHARED double wtr[MD1];
|
||||
MFEM_SHARED double sums[MD1];
|
||||
|
||||
// Evaluate basis functions at the reference space coordinates
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
lagrange_eval(wtr, r[i], j, p_Nq, gll1D, lagcoeff);
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
for (int fld=0; fld<nfields; ++fld)
|
||||
{
|
||||
// If using GetNodalValues, ordering is NDOFS x NEL x VDIM and the
|
||||
// offset would be `el[i] * p_Nq + fld * gf_offset`.
|
||||
// R->Mult produces element vectors in NDOFS x VDIM x NEL layout.
|
||||
const int elemOffset = el[i]*nfields*p_Nq + fld*p_Nq;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
sums[j] = wtr[j] * gf_in[elemOffset + j];
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD(j,x,1)
|
||||
{
|
||||
double sumv = 0.0;
|
||||
// sum the contributions of each lagrange polynomial
|
||||
for (int jj=0; jj<D1D; ++jj)
|
||||
{
|
||||
sumv += sums[jj];
|
||||
}
|
||||
int_out[fld*npt + i] = sumv;
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FindPointsGSLIB::InterpolateLocal1( const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt,
|
||||
int ncomp,
|
||||
int dof1Dsol )
|
||||
{
|
||||
MFEM_VERIFY(dim == 1, "Kernel for edges only.");
|
||||
if (npt == 0) { return; }
|
||||
bool use_dev = field_in.UseDevice();
|
||||
auto pfin = field_in.Read(use_dev);
|
||||
auto pgsl = gsl_elem_dev_l.ReadWrite(use_dev);
|
||||
auto pgslr = gsl_ref_l.ReadWrite(use_dev);
|
||||
auto pfout = field_out.Write(use_dev);
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite(use_dev);
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
|
||||
switch (dof1Dsol)
|
||||
{
|
||||
case 2: return InterpolateLocal1DKernel<2>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 3: return InterpolateLocal1DKernel<3>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 4: return InterpolateLocal1DKernel<4>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 5: return InterpolateLocal1DKernel<5>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
default: return InterpolateLocal1DKernel(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp,
|
||||
pgll, plcf, dof1Dsol);
|
||||
}
|
||||
}
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
#else
|
||||
void FindPointsGSLIB::InterpolateLocal1(const Vector &field_in,
|
||||
Array<int> &gsl_elem_dev_l,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int dof1Dsol) {};
|
||||
#endif
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif //ifdef MFEM_USE_GSLIB
|
||||
@@ -52,8 +52,6 @@ static void InterpolateLocal2DKernel(const double *const gf_in,
|
||||
double *const int_out,
|
||||
const int npt,
|
||||
const int ncomp,
|
||||
const int nel,
|
||||
const int gf_offset,
|
||||
double *gll1D,
|
||||
double *lagcoeff,
|
||||
const int pN = 0)
|
||||
@@ -64,6 +62,8 @@ static void InterpolateLocal2DKernel(const double *const gf_in,
|
||||
const int p_Np = D1D*D1D;
|
||||
MFEM_VERIFY(MD1 <= DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(pN<=DofQuadLimits::MAX_D1D,
|
||||
"Increase Max allowable polynomial order.");
|
||||
MFEM_VERIFY(D1D != 0, "Polynomial order not specified.");
|
||||
mfem::forall_2D(npt, D1D, D1D, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
@@ -82,9 +82,9 @@ static void InterpolateLocal2DKernel(const double *const gf_in,
|
||||
|
||||
for (int fld = 0; fld < Nfields; ++fld)
|
||||
{
|
||||
// If using GetNodalValues, ordering is NDOFSxNELxVDIM
|
||||
// const int elemOffset = el[i] * p_Np + fld * gf_offset;
|
||||
//if using R->Mult for L -> E-Vec use below: NDOFSxVDIMxNEL
|
||||
// If using GetNodalValues, ordering is NDOFS x NEL x VDIM and the
|
||||
// offset would be `el[i] * p_Np + fld * gf_offset`.
|
||||
// R->Mult produces element vectors in NDOFS x VDIM x NEL layout.
|
||||
const int elemOffset = el[i] * p_Np * Nfields + fld * p_Np;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
@@ -120,32 +120,32 @@ void FindPointsGSLIB::InterpolateLocal2(const Vector &field_in,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int nel, int dof1Dsol)
|
||||
int dof1Dsol)
|
||||
{
|
||||
if (npt == 0) { return; }
|
||||
const int gf_offset = field_in.Size()/ncomp;
|
||||
auto pfin = field_in.Read();
|
||||
auto pgsl = gsl_elem_dev_l.ReadWrite();
|
||||
auto pgslr = gsl_ref_l.ReadWrite();
|
||||
auto pfout = field_out.Write();
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite();
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite();
|
||||
bool use_dev = field_in.UseDevice();
|
||||
auto pfin = field_in.Read(use_dev);
|
||||
auto pgsl = gsl_elem_dev_l.ReadWrite(use_dev);
|
||||
auto pgslr = gsl_ref_l.ReadWrite(use_dev);
|
||||
auto pfout = field_out.Write(use_dev);
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite(use_dev);
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
|
||||
switch (dof1Dsol)
|
||||
{
|
||||
case 2: return InterpolateLocal2DKernel<2>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 3: return InterpolateLocal2DKernel<3>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 4: return InterpolateLocal2DKernel<4>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 5: return InterpolateLocal2DKernel<5>(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
default: return InterpolateLocal2DKernel(pfin, pgsl, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf, dof1Dsol);
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,7 @@ void FindPointsGSLIB::InterpolateLocal2(const Vector &field_in,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int nel, int dof1Dsol) {};
|
||||
int dof1Dsol) {};
|
||||
#endif
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
@@ -52,8 +52,6 @@ static void InterpolateLocal3DKernel(const double *const gf_in,
|
||||
double *const int_out,
|
||||
const int npt,
|
||||
const int ncomp,
|
||||
const int nel,
|
||||
const int gf_offset,
|
||||
double *gll1D,
|
||||
double *lagcoeff,
|
||||
const int pN = 0)
|
||||
@@ -84,9 +82,9 @@ static void InterpolateLocal3DKernel(const double *const gf_in,
|
||||
|
||||
for (int fld = 0; fld < Nfields; ++fld)
|
||||
{
|
||||
// If using GetNodalValues, ordering is NDOFSxNELxVDIM
|
||||
// const int elemOffset = el[i] * p_Np + fld * gf_offset;
|
||||
//if using R->Mult for L -> E-Vec use below.
|
||||
// If using GetNodalValues, ordering is NDOFS x NEL x VDIM and the
|
||||
// offset would be `el[i] * p_Np + fld * gf_offset`.
|
||||
// R->Mult produces element vectors in NDOFS x VDIM x NEL layout.
|
||||
const int elemOffset = el[i] * p_Np * Nfields + fld * p_Np;
|
||||
MFEM_FOREACH_THREAD(j,x,D1D)
|
||||
{
|
||||
@@ -125,37 +123,38 @@ void FindPointsGSLIB::InterpolateLocal3(const Vector &field_in,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int nel, int dof1Dsol)
|
||||
int dof1Dsol)
|
||||
{
|
||||
if (npt == 0) { return; }
|
||||
const int gf_offset = field_in.Size()/ncomp;
|
||||
auto pfin = field_in.Read();
|
||||
auto pgsle = gsl_elem_dev_l.ReadWrite();
|
||||
auto pgslr = gsl_ref_l.ReadWrite();
|
||||
auto pfout = field_out.Write();
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite();
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite();
|
||||
bool use_dev = field_in.UseDevice();
|
||||
auto pfin = field_in.Read(use_dev);
|
||||
auto pgsle = gsl_elem_dev_l.ReadWrite(use_dev);
|
||||
auto pgslr = gsl_ref_l.ReadWrite(use_dev);
|
||||
auto pfout = field_out.Write(use_dev);
|
||||
auto pgll = DEV.gll1d_sol.ReadWrite(use_dev);
|
||||
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
|
||||
switch (dof1Dsol)
|
||||
{
|
||||
case 2: return InterpolateLocal3DKernel<2>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 3: return InterpolateLocal3DKernel<3>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 4: return InterpolateLocal3DKernel<4>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
case 5: return InterpolateLocal3DKernel<5>(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf);
|
||||
default: return InterpolateLocal3DKernel(pfin, pgsle, pgslr, pfout,
|
||||
npt, ncomp, nel, gf_offset,
|
||||
npt, ncomp,
|
||||
pgll, plcf, dof1Dsol);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#undef MAXC
|
||||
#undef CODE_INTERNAL
|
||||
#undef CODE_BORDER
|
||||
#undef CODE_NOT_FOUND
|
||||
@@ -165,7 +164,7 @@ void FindPointsGSLIB::InterpolateLocal3(const Vector &field_in,
|
||||
Vector &gsl_ref_l,
|
||||
Vector &field_out,
|
||||
int npt, int ncomp,
|
||||
int nel, int dof1Dsol) {};
|
||||
int dof1Dsol) {};
|
||||
#endif
|
||||
} // namespace mfem
|
||||
|
||||
|
||||
@@ -91,15 +91,15 @@ void ElasticityAddMultPA(const int dim, const int nDofs,
|
||||
void ElasticityAssembleDiagonalPA(const int dim, const int nDofs,
|
||||
const CoefficientVector &lambda,
|
||||
const CoefficientVector &mu, const GeometricFactors &geom,
|
||||
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
|
||||
const DofToQuad &maps, const IntegrationRule &ir, Vector &diag)
|
||||
{
|
||||
switch (dim)
|
||||
{
|
||||
case 2:
|
||||
ElasticityAssembleDiagonalPA_<2>(nDofs, lambda, mu, geom, maps, QVec, diag);
|
||||
ElasticityAssembleDiagonalPA_<2>(nDofs, lambda, mu, geom, maps, ir, diag);
|
||||
break;
|
||||
case 3:
|
||||
ElasticityAssembleDiagonalPA_<3>(nDofs, lambda, mu, geom, maps, QVec, diag);
|
||||
ElasticityAssembleDiagonalPA_<3>(nDofs, lambda, mu, geom, maps, ir, diag);
|
||||
break;
|
||||
default:
|
||||
MFEM_ABORT("Only dimensions 2 and 3 supported.");
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include "../../linalg/vector.hpp"
|
||||
#include "../../linalg/tensor.hpp"
|
||||
#include "../quadinterpolator.hpp"
|
||||
#include "../bilininteg.hpp"
|
||||
#include "../coefficient.hpp"
|
||||
#include "../qfunction.hpp"
|
||||
|
||||
@@ -133,12 +132,12 @@ void ElasticityAssembleEA(const int dim, const int i_block, const int j_block,
|
||||
/// @param[in] mu Quadrature function for second Lame param.
|
||||
/// @param[in] geom Geometric factors corresponding to fespace.
|
||||
/// @param[in] maps DofToQuad maps for one element (assume elements all same).
|
||||
/// @param QVec Scratch Q-Vector. nQuad x dim x dim x dim x dim x numEls.
|
||||
/// @param[in] ir Integration rule.
|
||||
/// @param[out] diag diagonal of A. nDofs x dim x numEls.
|
||||
void ElasticityAssembleDiagonalPA(const int dim, const int nDofs,
|
||||
const CoefficientVector &lambda,
|
||||
const CoefficientVector &mu, const GeometricFactors &geom,
|
||||
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag);
|
||||
const DofToQuad &maps, const IntegrationRule &ir, Vector &diag);
|
||||
|
||||
/// Templated implementation of ElasticityAddMultPA.
|
||||
template<int dim, int i_block = -1, int j_block = -1>
|
||||
@@ -280,77 +279,62 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
|
||||
template<int dim>
|
||||
void ElasticityAssembleDiagonalPA_(const int nDofs,
|
||||
const CoefficientVector &lambda,
|
||||
const CoefficientVector &mu, const GeometricFactors &geom,
|
||||
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
|
||||
const CoefficientVector &mu,
|
||||
const GeometricFactors &geom,
|
||||
const DofToQuad &maps,
|
||||
const IntegrationRule &ir,
|
||||
Vector &diag)
|
||||
{
|
||||
using future::tensor;
|
||||
using future::make_tensor;
|
||||
using future::det;
|
||||
using future::inv;
|
||||
using future::make_tensor;
|
||||
using future::tensor;
|
||||
|
||||
// Assuming all elements are the same
|
||||
const auto &ir = QVec.GetIntRule(0);
|
||||
static constexpr int d = dim;
|
||||
const int numPoints = ir.GetNPoints();
|
||||
const int numEls = lambda.Size()/numPoints;
|
||||
const int numEls = lambda.Size() / numPoints;
|
||||
|
||||
const auto lamDev = Reshape(lambda.Read(), numPoints, numEls);
|
||||
const auto muDev = Reshape(mu.Read(), numPoints, numEls);
|
||||
const auto J = Reshape(geom.J.Read(), numPoints, d, d, numEls);
|
||||
auto Q = Reshape(QVec.ReadWrite(), numPoints, d,d, d, numEls);
|
||||
const real_t *ipWeights = ir.GetWeights().Read();
|
||||
mfem::forall_2D(numEls, numPoints,1, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(p, x,numPoints)
|
||||
{
|
||||
auto invJ = inv(make_tensor<d, d>(
|
||||
[&](int i, int j) { return J(p, i, j, e); }));
|
||||
const real_t w = ipWeights[p] /det(invJ);
|
||||
for (int n = 0; n < d; n++)
|
||||
{
|
||||
for (int m = 0; m < d; m++)
|
||||
{
|
||||
for (int q = 0; q < d; q++)
|
||||
{
|
||||
// compute contraction of 4*sym(grad(u))sym(grad(v)) term.
|
||||
// this contraction could be made slightly cheaper using Voigt
|
||||
// notation, but repeated entries are summed for simplicity.
|
||||
real_t contraction = 0.;
|
||||
for (int a = 0; a < d; a++)
|
||||
{
|
||||
for (int b = 0; b < d; b++)
|
||||
{
|
||||
contraction += ((a == q)*invJ(m,b) + (b==q)*invJ(m,a))*((a == q)
|
||||
*invJ(n, b) + (b==q)*invJ(n,a));
|
||||
}
|
||||
}
|
||||
// lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
|
||||
// contraction = 4*sym(grad(u))sym(grad(v))
|
||||
Q(p,m,n,q,e) = w*(lamDev(p, e)*invJ(m,q)*invJ(n,q)
|
||||
+ 0.5*muDev(p, e)*contraction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Reduce quadrature function to an E-Vector
|
||||
const auto QRead = Reshape(QVec.Read(), numPoints, d, d, d, numEls);
|
||||
auto diagDev = Reshape(diag.Write(), nDofs, d, numEls);
|
||||
const auto G = Reshape(maps.G.Read(), numPoints, d, nDofs);
|
||||
auto diagDev = Reshape(diag.Write(), nDofs, d, numEls);
|
||||
|
||||
mfem::forall_2D(numEls, d, nDofs, [=] MFEM_HOST_DEVICE (int e)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(i, y, nDofs)
|
||||
MFEM_FOREACH_THREAD_DIRECT(i, y, nDofs)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q, x, d)
|
||||
MFEM_FOREACH_THREAD_DIRECT(q, x, d)
|
||||
{
|
||||
real_t sum = 0.;
|
||||
for (int n = 0; n < d; n++)
|
||||
real_t sum = 0.0;
|
||||
for (int p = 0; p < numPoints; p++)
|
||||
{
|
||||
for (int m = 0; m < d; m++)
|
||||
auto invJ = inv(make_tensor<d, d>([&](int r, int c) { return J(p, r, c, e); }));
|
||||
const real_t w = ipWeights[p] / det(invJ);
|
||||
|
||||
for (int n = 0; n < d; n++)
|
||||
{
|
||||
for (int p = 0; p < numPoints; p++ )
|
||||
for (int m = 0; m < d; m++)
|
||||
{
|
||||
sum += QRead(p,m,n,q,e)*G(p,m,i)*G(p,n,i);
|
||||
// compute contraction of 4*sym(grad(u))sym(grad(v)) term.
|
||||
// this contraction could be made slightly cheaper using Voigt
|
||||
// notation, but repeated entries are summed for simplicity.
|
||||
real_t contraction = 0.;
|
||||
for (int a = 0; a < d; a++)
|
||||
{
|
||||
for (int b = 0; b < d; b++)
|
||||
{
|
||||
contraction += ((a == q) * invJ(m, b) + (b == q) * invJ(m, a)) *
|
||||
((a == q) * invJ(n, b) + (b == q) * invJ(n, a));
|
||||
}
|
||||
}
|
||||
// lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
|
||||
// contraction = 4*sym(grad(u))sym(grad(v))
|
||||
const real_t Q = w *
|
||||
(lamDev(p, e) * invJ(m, q) * invJ(n, q) + 0.5 * muDev(p, e) * contraction);
|
||||
sum += Q * G(p, m, i) * G(p, n, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "../bilininteg.hpp"
|
||||
#include "../gridfunc.hpp"
|
||||
#include "../qfunction.hpp"
|
||||
#include "bilininteg_elasticity_kernels.hpp"
|
||||
|
||||
@@ -59,9 +58,8 @@ void ElasticityIntegrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
|
||||
void ElasticityIntegrator::AssembleDiagonalPA(Vector &diag)
|
||||
{
|
||||
q_vec->SetVDim(vdim*vdim*vdim*vdim);
|
||||
internal::ElasticityAssembleDiagonalPA(vdim, ndofs, *lambda_quad, *mu_quad,
|
||||
*geom, *maps, *q_vec, diag);
|
||||
*geom, *maps, *IntRule, diag);
|
||||
}
|
||||
|
||||
void ElasticityIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
|
||||
@@ -181,6 +181,12 @@ constexpr int NBZ(int D1D)
|
||||
{
|
||||
return ipow(2, D(D1D) >= 0 ? D(D1D) : 0);
|
||||
}
|
||||
constexpr int NBZ3D(int MDQ)
|
||||
{
|
||||
return MDQ > 0 ? std::min<int>(
|
||||
(128 + MDQ * MDQ * MDQ - 1) / (MDQ * MDQ * MDQ), 64)
|
||||
: 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Shared memory PA Mass Diagonal 2D kernel
|
||||
@@ -804,19 +810,23 @@ void PAMassApply3D_Element(const int e,
|
||||
}
|
||||
}
|
||||
|
||||
template<int T_D1D, int T_Q1D, bool ACCUMULATE = true>
|
||||
MFEM_HOST_DEVICE inline
|
||||
void SmemPAMassApply3D_Element(const int e,
|
||||
const int NE,
|
||||
const real_t *b_,
|
||||
const real_t *d_,
|
||||
const real_t *x_,
|
||||
real_t *y_,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
template <int T_D1D, int T_Q1D, int TBATCH, bool ACCUMULATE = true>
|
||||
MFEM_HOST_DEVICE inline void
|
||||
SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
|
||||
const real_t *d_, const real_t *x_, real_t *y_,
|
||||
int d1d = 0, int q1d = 0)
|
||||
{
|
||||
constexpr int D1D = T_D1D ? T_D1D : d1d;
|
||||
constexpr int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
static_assert(TBATCH > 0, "TBATCH must be positive");
|
||||
#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
|
||||
constexpr int tbatch = TBATCH;
|
||||
const int tidz = MFEM_THREAD_ID(z);
|
||||
#else
|
||||
// host always batch size 1
|
||||
constexpr int tbatch = 1;
|
||||
constexpr int tidz = 0;
|
||||
#endif
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
|
||||
@@ -829,33 +839,37 @@ void SmemPAMassApply3D_Element(const int e,
|
||||
MFEM_SHARED real_t sDQ[MQ1*MD1];
|
||||
real_t (*B)[MD1] = (real_t (*)[MD1]) sDQ;
|
||||
real_t (*Bt)[MQ1] = (real_t (*)[MQ1]) sDQ;
|
||||
MFEM_SHARED real_t sm0[MDQ*MDQ*MDQ];
|
||||
MFEM_SHARED real_t sm1[MDQ*MDQ*MDQ];
|
||||
real_t (*X)[MD1][MD1] = (real_t (*)[MD1][MD1]) sm0;
|
||||
real_t (*DDQ)[MD1][MQ1] = (real_t (*)[MD1][MQ1]) sm1;
|
||||
real_t (*DQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) sm0;
|
||||
real_t (*QQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) sm1;
|
||||
real_t (*QQD)[MQ1][MD1] = (real_t (*)[MQ1][MD1]) sm0;
|
||||
real_t (*QDD)[MD1][MD1] = (real_t (*)[MD1][MD1]) sm1;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_SHARED real_t sm0[tbatch][MDQ*MDQ*MDQ];
|
||||
MFEM_SHARED real_t sm1[tbatch][MDQ*MDQ*MDQ];
|
||||
real_t (*X)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+tidz);
|
||||
real_t (*DDQ)[MD1][MQ1] = (real_t (*)[MD1][MQ1]) (sm1+tidz);
|
||||
real_t (*DQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm0+tidz);
|
||||
real_t (*QQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm1+tidz);
|
||||
real_t (*QQD)[MQ1][MD1] = (real_t (*)[MQ1][MD1]) (sm0+tidz);
|
||||
real_t (*QDD)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm1+tidz);
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD(dx, x, D1D)
|
||||
{
|
||||
MFEM_UNROLL(MD1)
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
X[dz][dy][dx] = x(dx,dy,dz,e);
|
||||
X[dz][dy][dx] = x(dx, dy, dz, e);
|
||||
}
|
||||
}
|
||||
MFEM_FOREACH_THREAD(dx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
|
||||
}
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
{
|
||||
B[dx][dy] = b(dx,dy);
|
||||
MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD(qx, x, Q1D)
|
||||
{
|
||||
real_t u[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -880,9 +894,9 @@ void SmemPAMassApply3D_Element(const int e,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD(qx, x, Q1D)
|
||||
{
|
||||
real_t u[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -907,9 +921,9 @@ void SmemPAMassApply3D_Element(const int e,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(qx,x,Q1D)
|
||||
MFEM_FOREACH_THREAD(qx, x, Q1D)
|
||||
{
|
||||
real_t u[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
@@ -929,22 +943,22 @@ void SmemPAMassApply3D_Element(const int e,
|
||||
MFEM_UNROLL(MQ1)
|
||||
for (int qz = 0; qz < Q1D; qz++)
|
||||
{
|
||||
QQQ[qz][qy][qx] = u[qz] * d(qx,qy,qz,e);
|
||||
QQQ[qz][qy][qx] = u[qz] * d(qx, qy, qz, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(di,y,D1D)
|
||||
if (tidz == 0)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(q,x,Q1D)
|
||||
MFEM_FOREACH_THREAD(di, y, D1D)
|
||||
{
|
||||
Bt[di][q] = b(q,di);
|
||||
MFEM_FOREACH_THREAD(q, x, Q1D) { Bt[di][q] = b(q, di); }
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(qy,y,Q1D)
|
||||
MFEM_FOREACH_THREAD(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD(dx, x, D1D)
|
||||
{
|
||||
real_t u[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
@@ -969,9 +983,9 @@ void SmemPAMassApply3D_Element(const int e,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD(dx, x, D1D)
|
||||
{
|
||||
real_t u[Q1D];
|
||||
MFEM_UNROLL(MQ1)
|
||||
@@ -996,9 +1010,9 @@ void SmemPAMassApply3D_Element(const int e,
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
MFEM_FOREACH_THREAD(dy,y,D1D)
|
||||
MFEM_FOREACH_THREAD(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD(dx,x,D1D)
|
||||
MFEM_FOREACH_THREAD(dx, x, D1D)
|
||||
{
|
||||
real_t u[D1D];
|
||||
MFEM_UNROLL(MD1)
|
||||
@@ -1020,11 +1034,11 @@ void SmemPAMassApply3D_Element(const int e,
|
||||
{
|
||||
if (ACCUMULATE)
|
||||
{
|
||||
y(dx,dy,dz,e) += u[dz];
|
||||
y(dx, dy, dz, e) += u[dz];
|
||||
}
|
||||
else
|
||||
{
|
||||
y(dx,dy,dz,e) = u[dz];
|
||||
y(dx, dy, dz, e) = u[dz];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1115,8 +1129,8 @@ inline void PAMassApply3D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
// Shared memory PA Mass Apply 2D kernel
|
||||
template<int T_D1D = 0, int T_Q1D = 0>
|
||||
// Shared memory PA Mass Apply 3D kernel
|
||||
template<int T_D1D = 0, int T_Q1D = 0, int TBATCH=1>
|
||||
inline void SmemPAMassApply3D(const int NE,
|
||||
const Array<real_t> &b_,
|
||||
const Array<real_t> &bt_,
|
||||
@@ -1126,6 +1140,9 @@ inline void SmemPAMassApply3D(const int NE,
|
||||
const int d1d = 0,
|
||||
const int q1d = 0)
|
||||
{
|
||||
static_assert(T_D1D > 0, "T_D1D must be positive");
|
||||
static_assert(T_Q1D > 0, "T_Q1D must be positive");
|
||||
static_assert(TBATCH > 0, "TBATCH must be positive");
|
||||
MFEM_CONTRACT_VAR(bt_);
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
@@ -1137,9 +1154,11 @@ inline void SmemPAMassApply3D(const int NE,
|
||||
const auto d = d_.Read();
|
||||
const auto x = x_.Read();
|
||||
auto y = y_.ReadWrite();
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
|
||||
mfem::forall_2D_batch<T_Q1D * T_Q1D * TBATCH>(NE, Q1D, Q1D, TBATCH,
|
||||
[=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
internal::SmemPAMassApply3D_Element<T_D1D,T_Q1D>(e, NE, b, d, x, y, d1d, q1d);
|
||||
internal::SmemPAMassApply3D_Element<T_D1D, T_Q1D, TBATCH>(e, NE, b, d, x,
|
||||
y, d1d, q1d);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1394,7 +1413,16 @@ ApplyKernelType MassIntegrator::ApplyPAKernels::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 1) { return internal::PAMassApply1D; }
|
||||
else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<T_D1D,T_Q1D>; }
|
||||
else if constexpr (DIM == 3) { return internal::SmemPAMassApply3D<T_D1D, T_Q1D>; }
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
constexpr int MDQ = T_D1D >= T_Q1D ? T_D1D : T_Q1D;
|
||||
// max 64 threads in z limit in cuda and hip
|
||||
if constexpr (MDQ > 0)
|
||||
{
|
||||
return internal::SmemPAMassApply3D<T_D1D, T_Q1D,
|
||||
internal::mass::NBZ3D(MDQ)>;
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("");
|
||||
}
|
||||
|
||||
|
||||
+323
-834
File diff suppressed because it is too large
Load Diff
@@ -205,157 +205,40 @@ void VectorMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
|
||||
|
||||
}
|
||||
|
||||
template <const int T_D1D = 0, const int T_Q1D = 0>
|
||||
static void PAVectorMassAssembleDiagonal2D(const int NE,
|
||||
const Array<real_t> &b,
|
||||
const Vector &pa_data, Vector &diag,
|
||||
const int d1d = 0, const int q1d = 0)
|
||||
{
|
||||
constexpr int VDIM = 2;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
const auto B = Reshape(b.Read(), Q1D, D1D);
|
||||
const auto D = Reshape(pa_data.Read(), Q1D, Q1D, NE);
|
||||
auto Y = Reshape(diag.ReadWrite(), D1D, D1D, VDIM, NE);
|
||||
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
|
||||
real_t temp[max_Q1D][max_D1D];
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
temp[qx][dy] = 0.0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
temp[qx][dy] += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
real_t temp1 = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
temp1 += B(qx, dx) * B(qx, dx) * temp[qx][dy];
|
||||
}
|
||||
Y(dx, dy, 0, e) = temp1;
|
||||
Y(dx, dy, 1, e) = temp1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <const int T_D1D = 0, const int T_Q1D = 0>
|
||||
static void PAVectorMassAssembleDiagonal3D(const int NE,
|
||||
const Array<real_t> &B_,
|
||||
const Vector &pa_data, Vector &diag,
|
||||
const int d1d = 0, const int q1d = 0)
|
||||
{
|
||||
constexpr int VDIM = 3;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
const auto B = Reshape(B_.Read(), Q1D, D1D);
|
||||
MFEM_VERIFY(pa_data.Size() == Q1D * Q1D * Q1D * NE, "pa_data size error");
|
||||
const auto D = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, NE);
|
||||
auto Y = Reshape(diag.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
|
||||
mfem::forall(NE, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
// the following variables are evaluated at compile time
|
||||
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
|
||||
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
|
||||
|
||||
real_t temp[max_Q1D][max_Q1D][max_D1D];
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
temp[qx][qy][dz] = 0.0;
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
temp[qx][qy][dz] +=
|
||||
B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
real_t temp2[max_Q1D][max_D1D][max_D1D];
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
temp2[qx][dy][dz] = 0.0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
temp2[qx][dy][dz] +=
|
||||
B(qy, dy) * B(qy, dy) * temp[qx][qy][dz];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
for (int dy = 0; dy < D1D; ++dy)
|
||||
{
|
||||
for (int dx = 0; dx < D1D; ++dx)
|
||||
{
|
||||
real_t temp3 = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
temp3 += B(qx, dx) * B(qx, dx) * temp2[qx][dy][dz];
|
||||
}
|
||||
Y(dx, dy, dz, 0, e) = temp3;
|
||||
Y(dx, dy, dz, 1, e) = temp3;
|
||||
Y(dx, dy, dz, 2, e) = temp3;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void PAVectorMassAssembleDiagonal(const int dim, const int D1D,
|
||||
const int Q1D, const int NE,
|
||||
const Array<real_t> &B,
|
||||
const Vector &pa_data,
|
||||
Vector &diag)
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
return PAVectorMassAssembleDiagonal2D(NE, B, pa_data, diag, D1D, Q1D);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
return PAVectorMassAssembleDiagonal3D(NE, B, pa_data, diag, D1D, Q1D);
|
||||
}
|
||||
MFEM_ABORT("Dimension not implemented.");
|
||||
}
|
||||
|
||||
void VectorMassIntegrator::AssembleDiagonalPA(Vector &diag)
|
||||
{
|
||||
if (DeviceCanUseCeed()) { ceedOp->GetDiagonal(diag); }
|
||||
else
|
||||
{
|
||||
MFEM_VERIFY(coeff_vdim == 1, "coeff_vdim != 1");
|
||||
MFEM_VERIFY(!VQ && !MQ, "VQ and MQ not supported");
|
||||
PAVectorMassAssembleDiagonal(dim, dofs1D, quad1D, ne, maps->B, pa_data, diag);
|
||||
}
|
||||
if (DeviceCanUseCeed()) { return ceedOp->GetDiagonal(diag); }
|
||||
|
||||
MFEM_VERIFY(coeff_vdim == 1, "coeff_vdim != 1");
|
||||
MFEM_VERIFY(!VQ && !MQ, "VQ and MQ not supported");
|
||||
|
||||
// Add the VectorMassAssembleDiagonalPA specializations
|
||||
static const auto vector_mass_assemble_diagonal_kernel_specializations =
|
||||
( // 2D
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 2>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 3>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 4>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 5>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 6>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 7>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 8>::Add(),
|
||||
// 3D
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 2>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 3>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 4>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 5>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 6>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 7>::Add(),
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 8>::Add(),
|
||||
true);
|
||||
MFEM_CONTRACT_VAR(vector_mass_assemble_diagonal_kernel_specializations);
|
||||
|
||||
VectorMassAssembleDiagonalPA::Run(dim, quad1D, // templated arguments
|
||||
ne, dofs1D, quad1D,
|
||||
maps->B.Read(),
|
||||
pa_data.Read(),
|
||||
diag.ReadWrite());
|
||||
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -176,8 +176,145 @@ void SmemPAVectorMassApply3D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
template <int T_Q1D = 0, int T_MDQ = 16>
|
||||
static void SmemPAVectorMassAssembleDiagonal2D(const int ne,
|
||||
const int d1d,
|
||||
const int q1d,
|
||||
const real_t *b_r,
|
||||
const real_t *d_r,
|
||||
real_t *y_rw)
|
||||
{
|
||||
constexpr int VDIM = 2;
|
||||
|
||||
const int D1D = d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
MFEM_VERIFY(Q1D <= T_MDQ && D1D <= Q1D, "");
|
||||
|
||||
const auto B = Reshape(b_r, Q1D, D1D);
|
||||
const auto D = Reshape(d_r, Q1D, Q1D, ne);
|
||||
auto Y = Reshape(y_rw, D1D, D1D, VDIM, ne);
|
||||
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(
|
||||
ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
|
||||
|
||||
MFEM_SHARED real_t sm[MQ1][MQ1];
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
u += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
|
||||
}
|
||||
sm[qx][dy] = u;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
u += B(qx, dx) * B(qx, dx) * sm[qx][dy];
|
||||
}
|
||||
Y(dx, dy, 0, e) = u;
|
||||
Y(dx, dy, 1, e) = u;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <int T_Q1D = 0, int T_MDQ = 12>
|
||||
static void SmemPAVectorMassAssembleDiagonal3D(const int ne,
|
||||
const int d1d,
|
||||
const int q1d,
|
||||
const real_t *b_r,
|
||||
const real_t *d_r,
|
||||
real_t *y_rw)
|
||||
{
|
||||
constexpr int VDIM = 3;
|
||||
|
||||
const int D1D = d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
MFEM_VERIFY(Q1D <= T_MDQ && D1D <= Q1D, "");
|
||||
|
||||
const auto B = Reshape(b_r, Q1D, D1D);
|
||||
const auto D = Reshape(d_r, Q1D, Q1D, Q1D, ne);
|
||||
auto Y = Reshape(y_rw, D1D, D1D, D1D, VDIM, ne);
|
||||
|
||||
mfem::forall_3D<T_Q1D*T_Q1D*T_Q1D>(
|
||||
ne, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
|
||||
|
||||
MFEM_SHARED real_t sm[2][MQ1][MQ1][MQ1];
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz, z, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
u += B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
|
||||
}
|
||||
sm[0][dz][qy][qx] = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz, z, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
u += B(qy, dy) * B(qy, dy) * sm[0][dz][qy][qx];
|
||||
}
|
||||
sm[1][dz][dy][qx] = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dz, z, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
u += B(qx, dx) * B(qx, dx) * sm[1][dz][dy][qx];
|
||||
}
|
||||
Y(dx, dy, dz, 0, e) = u;
|
||||
Y(dx, dy, dz, 1, e) = u;
|
||||
Y(dx, dy, dz, 2, e) = u;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// AddMultPA kernels
|
||||
template<int DIM, int T_D1D, int T_Q1D>
|
||||
VectorMassIntegrator::VectorMassAddMultPAType
|
||||
VectorMassIntegrator::VectorMassAddMultPA::Kernel()
|
||||
@@ -190,11 +327,11 @@ VectorMassIntegrator::VectorMassAddMultPA::Kernel()
|
||||
{
|
||||
return internal::SmemPAVectorMassApply3D<T_D1D, T_Q1D>;
|
||||
}
|
||||
MFEM_ABORT("Unsupported kernel");
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
}
|
||||
|
||||
inline VectorMassIntegrator::VectorMassAddMultPAType
|
||||
VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int d1d, int q1d)
|
||||
VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int, int)
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
@@ -207,6 +344,36 @@ VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int d1d, int q1d)
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
}
|
||||
|
||||
// DiagonalPA kernels
|
||||
template<int DIM, int T_Q1D>
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPAType
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Kernel()
|
||||
{
|
||||
if constexpr (DIM == 2)
|
||||
{
|
||||
return internal::SmemPAVectorMassAssembleDiagonal2D<T_Q1D>;
|
||||
}
|
||||
else if constexpr (DIM == 3)
|
||||
{
|
||||
return internal::SmemPAVectorMassAssembleDiagonal3D<T_Q1D>;
|
||||
}
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
}
|
||||
|
||||
inline VectorMassIntegrator::VectorMassAssembleDiagonalPAType
|
||||
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Fallback(int dim, int)
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
return internal::SmemPAVectorMassAssembleDiagonal2D;
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
return internal::SmemPAVectorMassAssembleDiagonal3D;
|
||||
}
|
||||
else { MFEM_ABORT("Unsupported kernel"); }
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,351 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "../kernels.hpp"
|
||||
#include "../nonlininteg.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, int T_MDQ = 16>
|
||||
static void SmemPAConvectionNLGradDiagonal2D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *a,
|
||||
const real_t *u,
|
||||
real_t *de,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
static constexpr int VDIM = 2, DIM = 2;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, NE);
|
||||
const auto U = Reshape(u, D1D, D1D, VDIM, NE);
|
||||
auto D = Reshape(de, D1D, D1D, VDIM, NE);
|
||||
|
||||
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D ? T_D1D : T_MDQ;
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
|
||||
|
||||
MFEM_SHARED real_t sM[3][MQ1][MQ1], sQ[3][MQ1][MQ1];
|
||||
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
|
||||
|
||||
kernels::internal::v_regs2d_t<VDIM, MQ1> r0, r1;
|
||||
kernels::internal::vd_regs2d_t<VDIM, DIM, MQ1> g0, g1;
|
||||
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
|
||||
|
||||
kernels::internal::LoadDofs2d(e, D1D, U, r0);
|
||||
kernels::internal::Eval2d(D1D, Q1D, sM[0], sB, r0, r1);
|
||||
|
||||
kernels::internal::LoadDofs2d(e, D1D, U, g0);
|
||||
kernels::internal::Grad2d(D1D, Q1D, sM[0], sB, sG, g0, g1);
|
||||
|
||||
for (int v = 0; v < VDIM; ++v)
|
||||
{
|
||||
future::tensor<real_t, VDIM> e_v = {};
|
||||
e_v[v] = real_t(1);
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
const future::tensor<real_t, VDIM> u_val =
|
||||
{
|
||||
r1[0][qy][qx], r1[1][qy][qx]
|
||||
};
|
||||
const future::tensor<real_t, VDIM, DIM> Q_adj =
|
||||
{
|
||||
{ { A(0, 0, qx, qy, e), A(1, 0, qx, qy, e) },
|
||||
{ A(0, 1, qx, qy, e), A(1, 1, qx, qy, e) }
|
||||
}
|
||||
};
|
||||
const future::tensor<real_t, VDIM, DIM> grad_U =
|
||||
{
|
||||
{ { g1[0][0][qy][qx], g1[1][0][qy][qx] },
|
||||
{ g1[0][1][qy][qx], g1[1][1][qy][qx] }
|
||||
}
|
||||
};
|
||||
const auto one = Q_adj * u_val;
|
||||
const auto two = transpose(grad_U) * (Q_adj * e_v);
|
||||
sQ[0][qx][qy] = one[0];
|
||||
sQ[1][qx][qy] = one[1];
|
||||
sQ[2][qx][qy] = two[v];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
real_t s[3] = {};
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const real_t By = sB[dy][qy], Gy = sG[dy][qy];
|
||||
s[0] += By * By * sQ[0][qx][qy];
|
||||
s[1] += Gy * By * sQ[1][qx][qy];
|
||||
s[2] += By * By * sQ[2][qx][qy];
|
||||
}
|
||||
sM[0][qx][dy] = s[0];
|
||||
sM[1][qx][dy] = s[1];
|
||||
sM[2][qx][dy] = s[2];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
real_t d = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
const real_t Bx = sB[dx][qx], Gx = sG[dx][qx];
|
||||
d += Gx * Bx * sM[0][qx][dy] +
|
||||
Bx * Bx * sM[1][qx][dy] +
|
||||
Bx * Bx * sM[2][qx][dy];
|
||||
}
|
||||
D(dx, dy, v, e) += d;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, int T_MDQ = 16>
|
||||
static void SmemPAConvectionNLGradDiagonal3D(const int NE,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *a,
|
||||
const real_t *u,
|
||||
real_t *de,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
static constexpr int VDIM = 3, DIM = 3;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, Q1D, NE);
|
||||
const auto U = Reshape(u, D1D, D1D, D1D, VDIM, NE);
|
||||
auto D = Reshape(de, D1D, D1D, D1D, VDIM, NE);
|
||||
|
||||
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D ? T_D1D : T_MDQ;
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
|
||||
|
||||
MFEM_SHARED real_t sM[4][MQ1][MQ1], sQ[4][MQ1][MQ1];
|
||||
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
|
||||
|
||||
kernels::internal::v_regs3d_t<VDIM, MQ1> r0, r1;
|
||||
kernels::internal::vd_regs3d_t<VDIM, DIM, MQ1> g0, g1;
|
||||
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
|
||||
|
||||
kernels::internal::LoadDofs3d(e, D1D, U, r0);
|
||||
kernels::internal::Eval3d(D1D, Q1D, sM[0], sB, r0, r1);
|
||||
|
||||
kernels::internal::LoadDofs3d(e, D1D, U, g0);
|
||||
kernels::internal::Grad3d(D1D, Q1D, sM[0], sB, sG, g0, g1);
|
||||
|
||||
for (int v = 0; v < VDIM; ++v)
|
||||
{
|
||||
future::tensor<real_t, VDIM> e_v = {};
|
||||
e_v[v] = real_t(1);
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
real_t s[4] = {};
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
const future::tensor<real_t, VDIM> u_val =
|
||||
{
|
||||
r1[0][qz][qy][qx], r1[1][qz][qy][qx], r1[2][qz][qy][qx]
|
||||
};
|
||||
const future::tensor<real_t, VDIM, DIM> Q_adj = {{
|
||||
{A(0,0,qx,qy,qz,e), A(1,0,qx,qy,qz,e), A(2,0,qx,qy,qz,e)},
|
||||
{A(0,1,qx,qy,qz,e), A(1,1,qx,qy,qz,e), A(2,1,qx,qy,qz,e)},
|
||||
{A(0,2,qx,qy,qz,e), A(1,2,qx,qy,qz,e), A(2,2,qx,qy,qz,e)}
|
||||
}
|
||||
};
|
||||
const future::tensor<real_t, VDIM, DIM> grad_U = {{
|
||||
{g1[0][0][qz][qy][qx], g1[1][0][qz][qy][qx], g1[2][0][qz][qy][qx]},
|
||||
{g1[0][1][qz][qy][qx], g1[1][1][qz][qy][qx], g1[2][1][qz][qy][qx]},
|
||||
{g1[0][2][qz][qy][qx], g1[1][2][qz][qy][qx], g1[2][2][qz][qy][qx]}
|
||||
}
|
||||
};
|
||||
const auto one = Q_adj * u_val;
|
||||
const auto two = transpose(grad_U) * (Q_adj * e_v);
|
||||
|
||||
const real_t Bz = sB[dz][qz], Gz = sG[dz][qz];
|
||||
s[0] += one[0] * Bz * Bz;
|
||||
s[1] += one[1] * Bz * Bz;
|
||||
s[2] += one[2] * Bz * Gz;
|
||||
s[3] += two[v] * Bz * Bz;
|
||||
}
|
||||
sQ[0][qx][qy] = s[0];
|
||||
sQ[1][qx][qy] = s[1];
|
||||
sQ[2][qx][qy] = s[2];
|
||||
sQ[3][qx][qy] = s[3];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
real_t s[4] = {};
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const real_t By = sB[dy][qy], Gy = sG[dy][qy];
|
||||
s[0] += By * By * sQ[0][qx][qy];
|
||||
s[1] += Gy * By * sQ[1][qx][qy];
|
||||
s[2] += By * By * sQ[2][qx][qy];
|
||||
s[3] += By * By * sQ[3][qx][qy];
|
||||
}
|
||||
sM[0][dy][qx] = s[0];
|
||||
sM[1][dy][qx] = s[1];
|
||||
sM[2][dy][qx] = s[2];
|
||||
sM[3][dy][qx] = s[3];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
real_t d = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
const real_t Bx = sB[dx][qx], Gx = sG[dx][qx];
|
||||
d += Gx * Bx * sM[0][dy][qx];
|
||||
d += Bx * Bx * sM[1][dy][qx];
|
||||
d += Bx * Bx * sM[2][dy][qx];
|
||||
d += Bx * Bx * sM[3][dy][qx];
|
||||
}
|
||||
D(dx, dy, dz, v, e) += d;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void VectorConvectionNLFIntegrator::AssembleGradDiagonalPA(Vector &de) const
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
if (static auto ini = false; !std::exchange(ini, true))
|
||||
{
|
||||
VectorConvectionNLFGradDiagPA2D::Specialization<2, 2>::Add();
|
||||
VectorConvectionNLFGradDiagPA2D::Specialization<2, 3>::Add();
|
||||
VectorConvectionNLFGradDiagPA2D::Specialization<3, 4>::Add();
|
||||
VectorConvectionNLFGradDiagPA2D::Specialization<3, 5>::Add();
|
||||
VectorConvectionNLFGradDiagPA2D::Specialization<4, 5>::Add();
|
||||
VectorConvectionNLFGradDiagPA2D::Specialization<4, 6>::Add();
|
||||
VectorConvectionNLFGradDiagPA2D::Specialization<5, 7>::Add();
|
||||
VectorConvectionNLFGradDiagPA2D::Specialization<5, 8>::Add();
|
||||
}
|
||||
VectorConvectionNLFGradDiagPA2D::Run(d1d, q1d, ne,
|
||||
maps->B.Read(),
|
||||
maps->G.Read(),
|
||||
pa_adj.Read(),
|
||||
pa_u.Read(),
|
||||
de.ReadWrite(),
|
||||
d1d, q1d);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
if (static auto ini = false; !std::exchange(ini, true))
|
||||
{
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<2, 3>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<2, 4>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<2, 5>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<3, 4>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<3, 5>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<3, 6>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<4, 6>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<4, 7>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<4, 8>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<5, 7>::Add();
|
||||
VectorConvectionNLFGradDiagPA3D::Specialization<5, 8>::Add();
|
||||
}
|
||||
VectorConvectionNLFGradDiagPA3D::Run(d1d, q1d, ne,
|
||||
maps->B.Read(),
|
||||
maps->G.Read(),
|
||||
pa_adj.Read(),
|
||||
pa_u.Read(),
|
||||
de.ReadWrite(),
|
||||
d1d, q1d);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported dimension");
|
||||
}
|
||||
}
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
|
||||
template<int T_D1D, int T_Q1D>
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPAType
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPA2D::Kernel()
|
||||
{
|
||||
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
|
||||
return SmemPAConvectionNLGradDiagonal2D<T_D1D, T_Q1D>;
|
||||
}
|
||||
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPAType
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPA2D::Fallback
|
||||
(int d1d, int q1d)
|
||||
{
|
||||
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
|
||||
MFEM_VERIFY(d1d <= 16, "d1d > 16 is not supported");
|
||||
MFEM_VERIFY(q1d <= 16, "q1d > 16 is not supported");
|
||||
return SmemPAConvectionNLGradDiagonal2D<>;
|
||||
}
|
||||
|
||||
template<int T_D1D, int T_Q1D>
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPAType
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPA3D::Kernel()
|
||||
{
|
||||
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
|
||||
return SmemPAConvectionNLGradDiagonal3D<T_D1D, T_Q1D>;
|
||||
}
|
||||
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPAType
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPA3D::Fallback
|
||||
(int d1d, int q1d)
|
||||
{
|
||||
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
|
||||
MFEM_VERIFY(d1d <= 16, "d1d > 16 is not supported");
|
||||
MFEM_VERIFY(q1d <= 16, "q1d > 16 is not supported");
|
||||
return SmemPAConvectionNLGradDiagonal3D<>;
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,319 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "../kernels.hpp"
|
||||
#include "../nonlininteg.hpp"
|
||||
#include "../../general/forall.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
void VectorConvectionNLFIntegrator::AssembleGradPA(
|
||||
const Vector &u, const FiniteElementSpace &fes)
|
||||
{
|
||||
this->pa_u = u;
|
||||
AssemblePA(fes);
|
||||
|
||||
if (static auto done = false; !std::exchange(done, true))
|
||||
{
|
||||
// 2D
|
||||
VectorConvectionNLFAddMultGradPA2D::Specialization<2, 2>::Add();
|
||||
VectorConvectionNLFAddMultGradPA2D::Specialization<2, 3>::Add();
|
||||
VectorConvectionNLFAddMultGradPA2D::Specialization<3, 4>::Add();
|
||||
VectorConvectionNLFAddMultGradPA2D::Specialization<3, 5>::Add();
|
||||
VectorConvectionNLFAddMultGradPA2D::Specialization<4, 5>::Add();
|
||||
VectorConvectionNLFAddMultGradPA2D::Specialization<4, 6>::Add();
|
||||
VectorConvectionNLFAddMultGradPA2D::Specialization<5, 7>::Add();
|
||||
VectorConvectionNLFAddMultGradPA2D::Specialization<5, 8>::Add();
|
||||
VectorConvectionNLFAddMultGradPA2D::Specialization<6, 8>::Add();
|
||||
// 3D
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<2, 3>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<2, 4>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<2, 5>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<3, 4>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<3, 5>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<3, 6>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<4, 5>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<4, 6>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<5, 6>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<4, 7>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<4, 8>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<5, 7>::Add();
|
||||
VectorConvectionNLFAddMultGradPA3D::Specialization<5, 8>::Add();
|
||||
}
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, int T_MDQ = 16>
|
||||
static void SmemPAConvectionNLGradApply2D(const int ne,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *a,
|
||||
const real_t *u,
|
||||
const real_t *du,
|
||||
real_t *y,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
static constexpr int VDIM = 2, DIM = 2;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, ne);
|
||||
const auto U = Reshape(u, D1D, D1D, VDIM, ne);
|
||||
const auto dU = Reshape(du, D1D, D1D, VDIM, ne);
|
||||
auto Y = Reshape(y, D1D, D1D, VDIM, ne);
|
||||
|
||||
mfem::forall_2D<T_Q1D * T_Q1D>(ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D ? T_D1D : T_MDQ;
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
|
||||
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
|
||||
|
||||
kernels::internal::vd_regs2d_t<VDIM, DIM, MQ1> g0, g1, g2;
|
||||
kernels::internal::v_regs2d_t<DIM, MQ1> r0, r1, r2;
|
||||
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
|
||||
|
||||
kernels::internal::LoadDofs2d(e, D1D, dU, g0);
|
||||
kernels::internal::Grad2d(D1D, Q1D, smem, sB, sG, g0, g1); // δu gradient
|
||||
|
||||
kernels::internal::LoadDofs2d(e, D1D, U, r0);
|
||||
kernels::internal::Eval2d(D1D, Q1D, smem, sB, r0, r2); // u value
|
||||
|
||||
kernels::internal::LoadDofs2d(e, D1D, dU, r0);
|
||||
kernels::internal::Eval2d(D1D, Q1D, smem, sB, r0, r1); // δu value
|
||||
|
||||
kernels::internal::LoadDofs2d(e, D1D, U, g0);
|
||||
kernels::internal::Grad2d(D1D, Q1D, smem, sB, sG, g0, g2); // u gradient
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
// First part of the Jacobian: u·∇δu
|
||||
const future::tensor<real_t, DIM> u_val =
|
||||
{
|
||||
r2[0][qy][qx], r2[1][qy][qx]
|
||||
};
|
||||
const future::tensor<real_t, VDIM, DIM> Q_adj =
|
||||
{
|
||||
{ { A(0, 0, qx, qy, e), A(1, 0, qx, qy, e) },
|
||||
{ A(0, 1, qx, qy, e), A(1, 1, qx, qy, e) }
|
||||
}
|
||||
};
|
||||
const future::tensor<real_t, VDIM, DIM> grad_dU =
|
||||
{
|
||||
{ { g1[0][0][qy][qx], g1[1][0][qy][qx] },
|
||||
{ g1[0][1][qy][qx], g1[1][1][qy][qx] }
|
||||
}
|
||||
};
|
||||
const auto one = transpose(grad_dU) * (Q_adj * u_val);
|
||||
|
||||
// Second part of the Jacobian: δu·∇u
|
||||
const future::tensor<real_t, DIM> du_val =
|
||||
{
|
||||
r1[0][qy][qx], r1[1][qy][qx]
|
||||
};
|
||||
const future::tensor<real_t, VDIM, DIM> grad_U =
|
||||
{
|
||||
{ { g2[0][0][qy][qx], g2[1][0][qy][qx] },
|
||||
{ g2[0][1][qy][qx], g2[1][1][qy][qx] }
|
||||
}
|
||||
};
|
||||
const auto two = transpose(grad_U) * (Q_adj * du_val);
|
||||
|
||||
// u⋅∇δu + δu⋅∇u
|
||||
r0[0][qy][qx] = one[0] + two[0];
|
||||
r0[1][qy][qx] = one[1] + two[1];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
kernels::internal::EvalTranspose2d(D1D, Q1D, smem, sB, r0, r1);
|
||||
kernels::internal::WriteDofs2d(e, D1D, r1, Y);
|
||||
});
|
||||
}
|
||||
|
||||
template<int T_D1D = 0, int T_Q1D = 0, int T_MDQ = 16>
|
||||
static void SmemPAConvectionNLGradApply3D(const int ne,
|
||||
const real_t *b,
|
||||
const real_t *g,
|
||||
const real_t *a,
|
||||
const real_t *u,
|
||||
const real_t *du,
|
||||
real_t *y,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
static constexpr int VDIM = 3, DIM = 3;
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, Q1D, ne);
|
||||
const auto U = Reshape(u, D1D, D1D, D1D, VDIM, ne);
|
||||
const auto dU = Reshape(du, D1D, D1D, D1D, VDIM, ne);
|
||||
auto Y = Reshape(y, D1D, D1D, D1D, VDIM, ne);
|
||||
|
||||
mfem::forall_2D<T_Q1D * T_Q1D>(ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
constexpr int MD1 = T_D1D ? T_D1D : T_MDQ;
|
||||
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
|
||||
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
|
||||
|
||||
kernels::internal::v_regs3d_t<VDIM, MQ1> r0, r1, r2;
|
||||
kernels::internal::vd_regs3d_t<VDIM, DIM, MQ1> g0, g1, g2;
|
||||
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
|
||||
|
||||
kernels::internal::LoadDofs3d(e, D1D, dU, g0);
|
||||
kernels::internal::Grad3d(D1D, Q1D, smem, sB, sG, g0, g1); // δu gradient
|
||||
|
||||
kernels::internal::LoadDofs3d(e, D1D, U, r0);
|
||||
kernels::internal::Eval3d(D1D, Q1D, smem, sB, r0, r2); // u value
|
||||
|
||||
kernels::internal::LoadDofs3d(e, D1D, dU, r0);
|
||||
kernels::internal::Eval3d(D1D, Q1D, smem, sB, r0, r1); // δu value
|
||||
|
||||
kernels::internal::LoadDofs3d(e, D1D, U, g0);
|
||||
kernels::internal::Grad3d(D1D, Q1D, smem, sB, sG, g0, g2); // u gradient
|
||||
|
||||
for (int qz = 0; qz < Q1D; qz++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
// First part of the Jacobian: u·∇δu
|
||||
const future::tensor<real_t, DIM> u_val =
|
||||
{
|
||||
r2[0][qz][qy][qx],
|
||||
r2[1][qz][qy][qx],
|
||||
r2[2][qz][qy][qx]
|
||||
};
|
||||
const future::tensor<real_t, VDIM, DIM> Q_adj = {{
|
||||
{A(0,0,qx,qy,qz,e), A(1,0,qx,qy,qz,e), A(2,0,qx,qy,qz,e)},
|
||||
{A(0,1,qx,qy,qz,e), A(1,1,qx,qy,qz,e), A(2,1,qx,qy,qz,e)},
|
||||
{A(0,2,qx,qy,qz,e), A(1,2,qx,qy,qz,e), A(2,2,qx,qy,qz,e)}
|
||||
}
|
||||
};
|
||||
const future::tensor<real_t, DIM, DIM> grad_dU = {{
|
||||
{g1[0][0][qz][qy][qx], g1[1][0][qz][qy][qx], g1[2][0][qz][qy][qx]},
|
||||
{g1[0][1][qz][qy][qx], g1[1][1][qz][qy][qx], g1[2][1][qz][qy][qx]},
|
||||
{g1[0][2][qz][qy][qx], g1[1][2][qz][qy][qx], g1[2][2][qz][qy][qx]}
|
||||
}
|
||||
};
|
||||
const auto one = transpose(grad_dU) * (Q_adj * u_val);
|
||||
|
||||
// Second part of the Jacobian: δu·∇u
|
||||
const future::tensor<real_t, DIM> du_val =
|
||||
{
|
||||
r1[0][qz][qy][qx], r1[1][qz][qy][qx], r1[2][qz][qy][qx]
|
||||
};
|
||||
const future::tensor<real_t, VDIM, DIM> grad_U = {{
|
||||
{g2[0][0][qz][qy][qx], g2[1][0][qz][qy][qx], g2[2][0][qz][qy][qx]},
|
||||
{g2[0][1][qz][qy][qx], g2[1][1][qz][qy][qx], g2[2][1][qz][qy][qx]},
|
||||
{g2[0][2][qz][qy][qx], g2[1][2][qz][qy][qx], g2[2][2][qz][qy][qx]}
|
||||
}
|
||||
};
|
||||
const auto two = transpose(grad_U) * (Q_adj * du_val);
|
||||
|
||||
// u⋅∇δu + δu⋅∇u
|
||||
r0[0][qz][qy][qx] = one[0] + two[0];
|
||||
r0[1][qz][qy][qx] = one[1] + two[1];
|
||||
r0[2][qz][qy][qx] = one[2] + two[2];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
kernels::internal::EvalTranspose3d(D1D, Q1D, smem, sB, r0, r1);
|
||||
kernels::internal::WriteDofs3d(e, D1D, r1, Y);
|
||||
});
|
||||
}
|
||||
|
||||
void VectorConvectionNLFIntegrator::AddMultGradPA(const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
if (dim == 2)
|
||||
{
|
||||
VectorConvectionNLFAddMultGradPA2D::Run(d1d, q1d, ne,
|
||||
maps->B.Read(),
|
||||
maps->G.Read(),
|
||||
pa_adj.Read(),
|
||||
pa_u.Read(),
|
||||
x.Read(),
|
||||
y.ReadWrite(),
|
||||
d1d, q1d);
|
||||
}
|
||||
else if (dim == 3)
|
||||
{
|
||||
VectorConvectionNLFAddMultGradPA3D::Run(d1d, q1d, ne,
|
||||
maps->B.Read(),
|
||||
maps->G.Read(),
|
||||
pa_adj.Read(),
|
||||
pa_u.Read(),
|
||||
x.Read(),
|
||||
y.ReadWrite(),
|
||||
d1d, q1d);
|
||||
}
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported dimension");
|
||||
}
|
||||
}
|
||||
|
||||
/// \cond DO_NOT_DOCUMENT
|
||||
|
||||
template<int T_D1D, int T_Q1D>
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPAType
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPA2D::Kernel()
|
||||
{
|
||||
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
|
||||
return SmemPAConvectionNLGradApply2D<T_D1D, T_Q1D>;
|
||||
}
|
||||
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPAType
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPA2D::Fallback
|
||||
(int d1d, int q1d)
|
||||
{
|
||||
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
|
||||
MFEM_VERIFY(d1d <= 16, "d1d > 16 is not supported");
|
||||
MFEM_VERIFY(q1d <= 16, "q1d > 16 is not supported");
|
||||
return SmemPAConvectionNLGradApply2D<>;
|
||||
}
|
||||
|
||||
template<int T_D1D, int T_Q1D>
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPAType
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPA3D::Kernel()
|
||||
{
|
||||
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
|
||||
return SmemPAConvectionNLGradApply3D<T_D1D, T_Q1D>;
|
||||
}
|
||||
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPAType
|
||||
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPA3D::Fallback
|
||||
(int d1d, int q1d)
|
||||
{
|
||||
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
|
||||
MFEM_VERIFY(d1d <= 16, "d1d > 16 is not supported");
|
||||
MFEM_VERIFY(q1d <= 16, "q1d > 16 is not supported");
|
||||
return SmemPAConvectionNLGradApply3D<>;
|
||||
}
|
||||
|
||||
/// \endcond DO_NOT_DOCUMENT
|
||||
|
||||
} // namespace mfem
|
||||
+9
-2
@@ -83,7 +83,7 @@ constexpr int SetMaxOf(int n) { return NextMultipleOf<4>(n); }
|
||||
#endif // CUDA/HIP && DEVICE_COMPILE
|
||||
|
||||
/// Load 2D matrix into shared memory
|
||||
template <int MQ1>
|
||||
template <int MQ1, bool TRANSPOSE = false>
|
||||
inline MFEM_HOST_DEVICE void LoadMatrix(const int d1d, const int q1d,
|
||||
const real_t *M, real_t (*N)[MQ1])
|
||||
{
|
||||
@@ -91,7 +91,14 @@ inline MFEM_HOST_DEVICE void LoadMatrix(const int d1d, const int q1d,
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
|
||||
{
|
||||
N[dy][qx] = M[dy * q1d + qx];
|
||||
if constexpr (TRANSPOSE)
|
||||
{
|
||||
N[dy][qx] = M[qx * d1d + dy];
|
||||
}
|
||||
else
|
||||
{
|
||||
N[dy][qx] = M[dy * q1d + qx];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
+4
-4
@@ -284,12 +284,12 @@ GeometricMultigrid::GeometricMultigrid(
|
||||
ownedProlongations.SetSize(nlevels - 1);
|
||||
ownedProlongations = have_ess_bdr;
|
||||
|
||||
if (have_ess_bdr)
|
||||
essentialTrueDofs.SetSize(nlevels);
|
||||
for (int level = 0; level < nlevels; ++level)
|
||||
{
|
||||
essentialTrueDofs.SetSize(nlevels);
|
||||
for (int level = 0; level < nlevels; ++level)
|
||||
essentialTrueDofs[level] = new Array<int>;
|
||||
if (have_ess_bdr)
|
||||
{
|
||||
essentialTrueDofs[level] = new Array<int>;
|
||||
fespaces.GetFESpaceAtLevel(level).GetEssentialTrueDofs(
|
||||
ess_bdr, *essentialTrueDofs[level]);
|
||||
}
|
||||
|
||||
+1
-2
@@ -187,8 +187,7 @@ public:
|
||||
/// mesh boundary element attributes that define the essential DOFs.
|
||||
///
|
||||
/// If @a ess_bdr is empty, or all its entries are 0, then no essential
|
||||
/// boundary conditions are imposed and the protected array essentialTrueDofs
|
||||
/// remains empty.
|
||||
/// boundary conditions are imposed.
|
||||
GeometricMultigrid(const FiniteElementSpaceHierarchy& fespaces_,
|
||||
const Array<int> &ess_bdr);
|
||||
|
||||
|
||||
+44
-4
@@ -18,6 +18,7 @@
|
||||
#include "fespace.hpp"
|
||||
#include "ceed/interface/operator.hpp"
|
||||
#include "integrator.hpp"
|
||||
#include "kernel_dispatch.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -384,10 +385,10 @@ private:
|
||||
DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
|
||||
Vector shape;
|
||||
// PA extension
|
||||
Vector pa_data;
|
||||
int dim, ne, nq, d1d, q1d;
|
||||
Vector pa_adj, pa_u;
|
||||
const DofToQuad *maps; ///< Not owned
|
||||
const GeometricFactors *geom; ///< Not owned
|
||||
int dim, ne, nq;
|
||||
|
||||
public:
|
||||
VectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }
|
||||
@@ -411,12 +412,51 @@ public:
|
||||
|
||||
void AssemblePA(const FiniteElementSpace &fes) override;
|
||||
|
||||
void AssembleMF(const FiniteElementSpace &fes) override;
|
||||
void AssembleGradPA(const Vector &x, const FiniteElementSpace &fes) override;
|
||||
|
||||
void AddMultPA(const Vector &x, Vector &y) const override;
|
||||
|
||||
void AddMultMF(const Vector &x, Vector &y) const override;
|
||||
using VectorConvectionNLFAddMultPAType =
|
||||
void(*)(const int ne, const real_t *B, const real_t *G, const real_t *A,
|
||||
const real_t *x, real_t *y,
|
||||
const int d1d, const int q1d);
|
||||
MFEM_REGISTER_KERNELS(VectorConvectionNLFAddMultPA,
|
||||
VectorConvectionNLFAddMultPAType,
|
||||
(int, int, int));
|
||||
|
||||
void AddMultGradPA(const Vector &x, Vector &y) const override;
|
||||
|
||||
using VectorConvectionNLFAddMultGradPAType =
|
||||
void(*)(const int ne, const real_t *B, const real_t *G, const real_t *A,
|
||||
const real_t *u, const real_t *x, real_t *y,
|
||||
const int d1d, const int q1d);
|
||||
|
||||
MFEM_REGISTER_KERNELS(VectorConvectionNLFAddMultGradPA2D,
|
||||
VectorConvectionNLFAddMultGradPAType,
|
||||
(int, int));
|
||||
|
||||
MFEM_REGISTER_KERNELS(VectorConvectionNLFAddMultGradPA3D,
|
||||
VectorConvectionNLFAddMultGradPAType,
|
||||
(int, int));
|
||||
|
||||
void AssembleGradDiagonalPA(Vector &) const override;
|
||||
|
||||
using VectorConvectionNLFGradDiagPAType =
|
||||
void (*)(const int ne, const real_t *B, const real_t *G, const real_t *A,
|
||||
const real_t *u, real_t *y,
|
||||
const int d1d, const int q1d);
|
||||
|
||||
MFEM_REGISTER_KERNELS(VectorConvectionNLFGradDiagPA2D,
|
||||
VectorConvectionNLFGradDiagPAType,
|
||||
(int, int));
|
||||
|
||||
MFEM_REGISTER_KERNELS(VectorConvectionNLFGradDiagPA3D,
|
||||
VectorConvectionNLFGradDiagPAType,
|
||||
(int, int));
|
||||
|
||||
void AssembleMF(const FiniteElementSpace &fes) override;
|
||||
|
||||
void AddMultMF(const Vector &x, Vector &y) const override;
|
||||
|
||||
protected:
|
||||
const IntegrationRule* GetDefaultIntegrationRule(
|
||||
|
||||
+31
-3
@@ -349,6 +349,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
}
|
||||
}
|
||||
|
||||
bool have_sign_flips = false;
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
g_ldof_sign->SetSize(GetNDofs());
|
||||
@@ -428,6 +429,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
have_sign_flips = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -466,6 +468,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
have_sign_flips = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -504,6 +507,7 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
if (g_ldof_sign)
|
||||
{
|
||||
(*g_ldof_sign)[dofs[l]] = -1;
|
||||
have_sign_flips = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -527,12 +531,18 @@ void ParFiniteElementSpace::GetGroupComm(
|
||||
group_ldof.GetI()[gr+1] = group_ldof_counter;
|
||||
}
|
||||
|
||||
if (g_ldof_sign && have_sign_flips == false)
|
||||
{
|
||||
g_ldof_sign->DeleteAll();
|
||||
}
|
||||
|
||||
gc.Finalize();
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs) const
|
||||
{
|
||||
MFEM_ASSERT(Conforming(), "wrong code path");
|
||||
if (!HaveDofSigns()) { return; }
|
||||
|
||||
for (int i = 0; i < dofs.Size(); i++)
|
||||
{
|
||||
@@ -559,6 +569,24 @@ void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof) const
|
||||
ApplyLDofSigns(all_dofs);
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::ApplyDofSigns(real_t *h_data) const
|
||||
{
|
||||
if (!HaveDofSigns()) { return; }
|
||||
|
||||
const bool byvdim = (ordering == Ordering::byVDIM);
|
||||
for (int i = 0; i < ndofs; i++)
|
||||
{
|
||||
if (ldof_sign[i] < 0)
|
||||
{
|
||||
for (int d = 0; d < vdim; d++)
|
||||
{
|
||||
const int idx = byvdim ? d+vdim*i : i+ndofs*d;
|
||||
h_data[idx] = -h_data[idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs,
|
||||
DofTransformation &doftrans) const
|
||||
{
|
||||
@@ -1193,15 +1221,15 @@ void ParFiniteElementSpace::GetEssentialTrueDofsVar(const Array<int>
|
||||
MFEM_VERIFY(IsVariableOrder() && R,
|
||||
"GetEssentialTrueDofsVar is only for variable-order spaces");
|
||||
|
||||
true_ess_dofs.SetSize(R->Height(), Device::GetDeviceMemoryType());
|
||||
true_ess_dofs.SetSize(R->Height());
|
||||
true_ess_dofs.HostWrite();
|
||||
true_ess_dofs = 0;
|
||||
|
||||
const int ntdofs = tdof2ldof.Size();
|
||||
MFEM_VERIFY(vdim * ntdofs == R->NumRows() &&
|
||||
vdim * ntdofs == true_ess_dofs.Size(), "");
|
||||
MFEM_VERIFY(ldof_ltdof.Size() == ndofs && ess_dofs.Size() == vdim * ndofs, "");
|
||||
|
||||
true_ess_dofs = 0;
|
||||
|
||||
const bool bynodes = (ordering == Ordering::byNODES);
|
||||
const int vdim_factor = bynodes ? 1 : vdim;
|
||||
const int num_true_dofs = R->NumRows() / vdim;
|
||||
|
||||
+14
-2
@@ -340,8 +340,20 @@ public:
|
||||
|
||||
inline ParMesh *GetParMesh() const { return pmesh; }
|
||||
|
||||
int GetDofSign(int i)
|
||||
{ return NURBSext || Nonconforming() ? 1 : ldof_sign[VDofToDof(i)]; }
|
||||
/** @brief Return true if the parallel FE space has DOFs with signs opposite
|
||||
of the DOFs in the respective serial FE space. */
|
||||
bool HaveDofSigns() const { return ldof_sign.Size() != 0; }
|
||||
|
||||
/** @brief Apply the DOF signs to the given host data @a h_data which must be
|
||||
of size GetVSize() if HaveDofSigns() is true. If HaveDofSigns() is false,
|
||||
this method is no-op and returns immediately. */
|
||||
void ApplyDofSigns(real_t *h_data) const;
|
||||
|
||||
/** @brief Return -1 if the given (vector) DOF @a i has a sign opposite of
|
||||
the DOF in the respecive serial FE space. Otherwise, return 1. */
|
||||
int GetDofSign(int i) const
|
||||
{ return !HaveDofSigns() ? 1 : ldof_sign[VDofToDof(i)]; }
|
||||
|
||||
HYPRE_BigInt *GetDofOffsets() const { return dof_offsets; }
|
||||
HYPRE_BigInt *GetTrueDofOffsets() const { return tdof_offsets; }
|
||||
HYPRE_BigInt GlobalVSize() const
|
||||
|
||||
+18
-9
@@ -80,6 +80,8 @@ ParGridFunction::ParGridFunction(ParMesh *pmesh, std::istream &input)
|
||||
fes->GetOrdering());
|
||||
delete fes;
|
||||
fes = pfes;
|
||||
|
||||
pfes->ApplyDofSigns(HostReadWrite());
|
||||
}
|
||||
|
||||
void ParGridFunction::Update()
|
||||
@@ -1082,18 +1084,17 @@ real_t ParGridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
|
||||
|
||||
void ParGridFunction::Save(std::ostream &os) const
|
||||
{
|
||||
real_t *data_ = const_cast<real_t*>(HostRead());
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
|
||||
}
|
||||
// We use const_cast + HostRead (instead of HostReadWrite) because we only
|
||||
// need to change the host data temporarily and this way we do not invalidate
|
||||
// the data if it is on device. If we use HostReadWrite here, later calls to
|
||||
// Read or ReadWrite will need to copy the data from host to device. With the
|
||||
// approach used here, the host-to-device copy is avoided.
|
||||
real_t *h_data = const_cast<real_t*>(HostRead());
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
|
||||
GridFunction::Save(os);
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
|
||||
}
|
||||
pfes->ApplyDofSigns(h_data);
|
||||
}
|
||||
|
||||
void ParGridFunction::Save(const char *fname, int precision) const
|
||||
@@ -1264,7 +1265,13 @@ void ParGridFunction::SaveAsOne(std::ostream &os) const
|
||||
int *nfdofs = new int[NRanks];
|
||||
int *nrdofs = new int[NRanks];
|
||||
|
||||
// We use const_cast + HostRead (instead of HostReadWrite) because we only
|
||||
// need to change the host data temporarily and this way we do not invalidate
|
||||
// the data if it is on device. If we use HostReadWrite here, later calls to
|
||||
// Read or ReadWrite will need to copy the data from host to device. With the
|
||||
// approach used here, the host-to-device copy is avoided.
|
||||
real_t * h_data = const_cast<real_t *>(this->HostRead());
|
||||
pfes->ApplyDofSigns(h_data); // temporarily flip the dof signs
|
||||
|
||||
values[0] = h_data;
|
||||
nv[0] = pfes -> GetVSize();
|
||||
@@ -1371,6 +1378,8 @@ void ParGridFunction::SaveAsOne(std::ostream &os) const
|
||||
MPI_Send(h_data, nv[0], MPITypeMap<real_t>::mpi_type, 0, 460, MyComm);
|
||||
}
|
||||
|
||||
pfes->ApplyDofSigns(h_data); // restore the original h_data
|
||||
|
||||
delete [] values;
|
||||
delete [] nv;
|
||||
delete [] nvdofs;
|
||||
|
||||
+66
-10
@@ -3797,13 +3797,18 @@ void TMOP_Integrator::EnableLimiting(const GridFunction &n0, Coefficient &w0,
|
||||
|
||||
void TMOP_Integrator::EnableAdaptiveLimiting(const GridFunction &z0,
|
||||
Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae)
|
||||
AdaptivityEvaluator &ae,
|
||||
real_t delta_max)
|
||||
{
|
||||
MFEM_VERIFY(delta_max > 0.0,
|
||||
"EnableAdaptiveLimiting requires delta_max > 0.0.");
|
||||
|
||||
adapt_lim_gf0 = &z0;
|
||||
delete adapt_lim_gf;
|
||||
adapt_lim_gf = new GridFunction(z0);
|
||||
adapt_lim_coeff = &coeff;
|
||||
adapt_lim_eval = &ae;
|
||||
adapt_lim_delta_max = delta_max;
|
||||
|
||||
adapt_lim_eval->SetSerialMetaInfo(*z0.FESpace()->GetMesh(),
|
||||
*z0.FESpace());
|
||||
@@ -3814,14 +3819,19 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const GridFunction &z0,
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
|
||||
Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae)
|
||||
AdaptivityEvaluator &ae,
|
||||
real_t delta_max)
|
||||
{
|
||||
MFEM_VERIFY(delta_max > 0.0,
|
||||
"EnableAdaptiveLimiting requires delta_max > 0.0.");
|
||||
|
||||
adapt_lim_gf0 = &z0;
|
||||
adapt_lim_pgf0 = &z0;
|
||||
delete adapt_lim_gf;
|
||||
adapt_lim_gf = new GridFunction(z0);
|
||||
adapt_lim_coeff = &coeff;
|
||||
adapt_lim_eval = &ae;
|
||||
adapt_lim_delta_max = delta_max;
|
||||
|
||||
adapt_lim_eval->SetParMetaInfo(*z0.ParFESpace()->GetParMesh(),
|
||||
*z0.ParFESpace());
|
||||
@@ -4297,7 +4307,8 @@ real_t TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
|
||||
// Contribution from the adaptive limiting term.
|
||||
if (adaptive_limiting)
|
||||
{
|
||||
const real_t diff = adapt_lim_gf_q(i) - adapt_lim_gf0_q(i);
|
||||
const real_t diff = (adapt_lim_gf_q(i) - adapt_lim_gf0_q(i)) /
|
||||
adapt_lim_delta_max;
|
||||
val += adapt_lim_coeff->Eval(*Tpr, ip) * lim_normal * diff * diff;
|
||||
}
|
||||
|
||||
@@ -4848,14 +4859,16 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el,
|
||||
grad_phys.Mult(adapt_lim_gf_e, grad_ptr);
|
||||
|
||||
Vector adapt_lim_gf_grad_q(dim);
|
||||
|
||||
for (int q = 0; q < nqp; q++)
|
||||
{
|
||||
const IntegrationPoint &ip = ir.IntPoint(q);
|
||||
el.CalcShape(ip, shape);
|
||||
|
||||
adapt_lim_gf_grad_e.MultTranspose(shape, adapt_lim_gf_grad_q);
|
||||
adapt_lim_gf_grad_q *= 2.0 * (adapt_lim_gf_q(q) - adapt_lim_gf0_q(q));
|
||||
adapt_lim_gf_grad_q *= 2.0 * (adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) /
|
||||
adapt_lim_delta_max / adapt_lim_delta_max;
|
||||
adapt_lim_gf_grad_q *= weights(q) * lim_normal * adapt_lim_coeff->Eval(Tpr, ip);
|
||||
|
||||
AddMultVWt(shape, adapt_lim_gf_grad_q, mat);
|
||||
}
|
||||
}
|
||||
@@ -4902,7 +4915,11 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
|
||||
Vector gg_ptr(adapt_lim_gf_hess_q.GetData(), dim*dim);
|
||||
adapt_lim_gf_hess_e.MultTranspose(shape, gg_ptr);
|
||||
|
||||
const real_t w = weights(q) * lim_normal * adapt_lim_coeff->Eval(Tpr, ip);
|
||||
const real_t coeff = adapt_lim_coeff->Eval(Tpr, ip);
|
||||
const real_t factor =
|
||||
weights(q) * lim_normal * coeff * 2.0 /
|
||||
(adapt_lim_delta_max * adapt_lim_delta_max);
|
||||
|
||||
for (int i = 0; i < dof * dim; i++)
|
||||
{
|
||||
const int idof = i % dof, idim = i / dof;
|
||||
@@ -4910,10 +4927,11 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
|
||||
{
|
||||
const int jdof = j % dof, jdim = j / dof;
|
||||
const real_t entry =
|
||||
w * ( 2.0 * adapt_lim_gf_grad_q(idim) * shape(idof) *
|
||||
/* */ adapt_lim_gf_grad_q(jdim) * shape(jdof) +
|
||||
2.0 * (adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) *
|
||||
adapt_lim_gf_hess_q(idim, jdim) * shape(idof) * shape(jdof));
|
||||
factor *
|
||||
(adapt_lim_gf_grad_q(idim) * shape(idof) *
|
||||
adapt_lim_gf_grad_q(jdim) * shape(jdof) +
|
||||
(adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) *
|
||||
adapt_lim_gf_hess_q(idim, jdim) * shape(idof) * shape(jdof));
|
||||
mat(i, j) += entry;
|
||||
if (i != j) { mat(j, i) += entry; }
|
||||
}
|
||||
@@ -5671,6 +5689,22 @@ UpdateAfterMeshPositionChange(const Vector &d, const FiniteElementSpace &d_fes)
|
||||
if (adapt_lim_gf)
|
||||
{
|
||||
adapt_lim_eval->ComputeAtNewPosition(x_loc, *adapt_lim_gf, ordering);
|
||||
if (PA.enabled)
|
||||
{
|
||||
PA.AL_grads_assembled = false;
|
||||
|
||||
// Step 1 of PA.ALFmF0 update: subtract the old ALF.
|
||||
PA.ALFmF0 -= PA.ALF;
|
||||
|
||||
// Refresh PA.ALF from the updated adapt_lim_gf.
|
||||
const ElementDofOrdering ord = ElementDofOrdering::LEXICOGRAPHIC;
|
||||
const Operator *alf_R =
|
||||
adapt_lim_gf->FESpace()->GetElementRestriction(ord);
|
||||
alf_R->Mult(*adapt_lim_gf, PA.ALF);
|
||||
|
||||
// Step 2 of PA.ALFmF0 update: add the new ALF.
|
||||
PA.ALFmF0 += PA.ALF;
|
||||
}
|
||||
}
|
||||
|
||||
// Update surf_fit_gf (and optionally its gradients) if surface
|
||||
@@ -5931,6 +5965,28 @@ void TMOPComboIntegrator::EnableLimiting(const GridFunction &n0,
|
||||
for (int i = 1; i < tmopi.Size(); i++) { tmopi[i]->DisableLimiting(); }
|
||||
}
|
||||
|
||||
void TMOPComboIntegrator::EnableAdaptiveLimiting(const GridFunction &z0,
|
||||
Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae,
|
||||
real_t delta_max)
|
||||
{
|
||||
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
|
||||
|
||||
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void TMOPComboIntegrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
|
||||
Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae,
|
||||
real_t delta_max)
|
||||
{
|
||||
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
|
||||
|
||||
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
|
||||
}
|
||||
#endif
|
||||
|
||||
void TMOPComboIntegrator::SetLimitingNodes(const GridFunction &n0)
|
||||
{
|
||||
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
|
||||
|
||||
+62
-11
@@ -1440,6 +1440,7 @@ public:
|
||||
void Eval_d2(const Vector &x, const Vector &x0, real_t dist,
|
||||
DenseMatrix &d2) const override
|
||||
{
|
||||
MFEM_CONTRACT_VAR(x0);
|
||||
MFEM_ASSERT(x.Size() == x0.Size(), "Bad input.");
|
||||
|
||||
d2.Diag(1.0 / (dist * dist), x.Size());
|
||||
@@ -2044,6 +2045,7 @@ protected:
|
||||
GridFunction *adapt_lim_gf; // Owned. Updated by adapt_lim_eval.
|
||||
Coefficient *adapt_lim_coeff; // Not owned.
|
||||
AdaptivityEvaluator *adapt_lim_eval; // Not owned.
|
||||
real_t adapt_lim_delta_max = 1.0;
|
||||
|
||||
// Surface fitting.
|
||||
const Array<bool> *surf_fit_marker; // Not owned. Nodes to fit.
|
||||
@@ -2110,9 +2112,20 @@ protected:
|
||||
// Updated by every call to PANonlinearFormExtension::GetGradient().
|
||||
// MC: Q-Vector for the metric Coefficient.
|
||||
// Updated when the mesh nodes change.
|
||||
// ALC: Q-Vector for spatial weight used for the adaptive limiting term.
|
||||
// Updated when the mesh nodes change.
|
||||
// ALF: E-Vector constructed using adaptive limiting GF zeta.
|
||||
// The zeta is remapped when the mesh nodes change.
|
||||
// ALFmF0: E-Vector constructed using adaptive limiting GF zeta.
|
||||
// It stores difference zeta-zeta0, as all computations use this.
|
||||
// ALFG: Q-Vector for gradient of ALF at quadrature points.
|
||||
// Updated by every call to PANonlinearFormExtension::GetGradient().
|
||||
// ALFH: Q-Vector for Hessian of ALF at quadrature points.
|
||||
// Updated by every call to PANonlinearFormExtension::GetGradient().
|
||||
//
|
||||
// maps: Dof2Quad map for fes associated with the nodal coordinates.
|
||||
// maps_lim: Dof2Quad map for fes associated with the limiting dist GridFunc.
|
||||
// maps: Dof2Quad map for fes associated with the nodal coordinates.
|
||||
// maps_lim: Dof2Quad map for fes associated with the limiting dist GF.
|
||||
// maps_nodes: like maps, but the quad points are the FE nodes.
|
||||
//
|
||||
// Jtr_debug_grad
|
||||
// We keep track if Jtr was set by AssembleGradPA() in Jtr_debug_grad: it
|
||||
@@ -2131,9 +2144,13 @@ protected:
|
||||
mutable DenseTensor Jtr;
|
||||
mutable bool Jtr_needs_update;
|
||||
mutable bool Jtr_debug_grad;
|
||||
mutable Vector E, O, X0, XL, H, C0, LD, H0, MC;
|
||||
mutable Vector E, O, X0, XL, H, C0, LD, H0, MC, ALC,
|
||||
ALF, ALFmF0, ALFG, ALFH;
|
||||
mutable bool AL_grads_assembled;
|
||||
real_t al_delta;
|
||||
const DofToQuad *maps;
|
||||
const DofToQuad *maps_lim = nullptr;
|
||||
const DofToQuad *maps_nodes = nullptr;
|
||||
const GeometricFactors *geom;
|
||||
const FiniteElementSpace *fes;
|
||||
const IntegrationRule *ir;
|
||||
@@ -2216,16 +2233,25 @@ protected:
|
||||
return EnergyIntegrationRule(el);
|
||||
}
|
||||
|
||||
//
|
||||
// Auxiliary PA methods
|
||||
//
|
||||
|
||||
// PA quadrature data computation - metric term / limiting / adapt limiting.
|
||||
void AssembleGradPA_2D(const Vector&) const;
|
||||
void AssembleGradPA_3D(const Vector&) const;
|
||||
void AssembleGradPA_C0_2D(const Vector&) const;
|
||||
void AssembleGradPA_C0_3D(const Vector&) const;
|
||||
void AssembleGradPA_AdaptLim_2D(const Vector&) const;
|
||||
void AssembleGradPA_AdaptLim_3D(const Vector&) const;
|
||||
|
||||
// PA energy computation - metric term / limiting / adaptive limiting.
|
||||
void GetLocalStateEnergyPA_2D(const Vector &x, real_t &energy) const;
|
||||
void GetLocalStateEnergyPA_3D(const Vector&, real_t &energy) const;
|
||||
void GetLocalStateEnergyPA_3D(const Vector &x, real_t &energy) const;
|
||||
real_t GetLocalStateEnergyPA_C0_2D(const Vector&) const;
|
||||
real_t GetLocalStateEnergyPA_C0_3D(const Vector&) const;
|
||||
real_t GetLocalStateEnergyPA_AdaptLim_2D() const;
|
||||
real_t GetLocalStateEnergyPA_AdaptLim_3D() const;
|
||||
void GetLocalNormalizationEnergiesPA_2D(const Vector &x,
|
||||
real_t &met_energy,
|
||||
real_t &lim_energy) const;
|
||||
@@ -2233,22 +2259,35 @@ protected:
|
||||
real_t &met_energy,
|
||||
real_t &lim_energy) const;
|
||||
|
||||
// PA gradient computation - metric term / limiting / adaptive limiting.
|
||||
void AddMultPA_2D(const Vector&, Vector&) const;
|
||||
void AddMultPA_3D(const Vector&, Vector&) const;
|
||||
void AddMultPA_C0_2D(const Vector&, Vector&) const;
|
||||
void AddMultPA_C0_3D(const Vector&, Vector&) const;
|
||||
void AddMultPA_AdaptLim_2D(const Vector&, Vector&) const;
|
||||
void AddMultPA_AdaptLim_3D(const Vector&, Vector&) const;
|
||||
|
||||
// PA Hessian AddMult - metric term / limiting / adaptive limiting.
|
||||
void AddMultGradPA_2D(const Vector&, Vector&) const;
|
||||
void AddMultGradPA_3D(const Vector&, Vector&) const;
|
||||
void AddMultGradPA_C0_2D(const Vector&, Vector&) const;
|
||||
void AddMultGradPA_C0_3D(const Vector&, Vector&) const;
|
||||
void AddMultGradPA_AdaptLim_2D(const Vector&, Vector&) const;
|
||||
void AddMultGradPA_AdaptLim_3D(const Vector&, Vector&) const;
|
||||
|
||||
// PA diagonal assemblies - metric term / limiting / adaptive limiting.
|
||||
void AssembleDiagonalPA_2D(Vector&) const;
|
||||
void AssembleDiagonalPA_3D(Vector&) const;
|
||||
void AssembleDiagonalPA_C0_2D(Vector&) const;
|
||||
void AssembleDiagonalPA_C0_3D(Vector&) const;
|
||||
void AssembleDiagonalPA_AdaptLim_2D(Vector&) const;
|
||||
void AssembleDiagonalPA_AdaptLim_3D(Vector&) const;
|
||||
|
||||
// Setup of PA data structures related to the limiting term.
|
||||
void AssemblePA_Limiting();
|
||||
// Setup of PA data structures related to the adaptive limiting term.
|
||||
void AssemblePA_AdaptLim();
|
||||
// Compute reference->target Jacobians for all quad points.
|
||||
void ComputeAllElementTargets(const Vector &xe = Vector()) const;
|
||||
// Updates the Q-vectors for the metric_coeff and lim_coeff, based on the
|
||||
// new physical positions of the quadrature points.
|
||||
@@ -2351,21 +2390,23 @@ public:
|
||||
|
||||
/** @brief Restriction of the node positions to certain regions.
|
||||
|
||||
Adds the term $ \int c (z(x) - z_0(x_0))^2 $, where z0(x0) is a given
|
||||
function on the starting mesh, and z(x) is its image on the new mesh.
|
||||
Minimizing this term means that a node at x0 is allowed to move to a
|
||||
position x(x0) only if z(x) ~ z0(x0).
|
||||
Adds the term $ \int c (z(x) - z_0(x_0))^2 / delta_max^2 $, where z0(x0)
|
||||
is a given function on the starting mesh, and z(x) is its image on the
|
||||
new mesh. Minimizing this term means that a node at x0 is allowed to
|
||||
move to a position x(x0) only if z(x) ~ z0(x0).
|
||||
Such term can be used for tangential mesh relaxation.
|
||||
|
||||
@param[in] z0 Function z0 that controls the adaptive limiting.
|
||||
@param[in] coeff Coefficient c for the above integral.
|
||||
@param[in] ae AdaptivityEvaluator to compute z(x) from z0(x0). */
|
||||
@param[in] ae AdaptivityEvaluator to compute z(x) from z0(x0).
|
||||
@param[in] delta_max Controls the allowable deviation from z0.
|
||||
Smaller values activate the term faster. */
|
||||
void EnableAdaptiveLimiting(const GridFunction &z0, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae);
|
||||
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Parallel support for adaptive limiting.
|
||||
void EnableAdaptiveLimiting(const ParGridFunction &z0, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae);
|
||||
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
|
||||
#endif
|
||||
|
||||
/** @brief Fitting of certain DOFs to the zero level set of a function.
|
||||
@@ -2588,6 +2629,16 @@ public:
|
||||
void EnableLimiting(const GridFunction &n0, Coefficient &w0,
|
||||
TMOP_LimiterFunction *lfunc = NULL);
|
||||
|
||||
/// Adds the adaptive limiting term to the first integrator.
|
||||
void EnableAdaptiveLimiting(const GridFunction &z0, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
|
||||
#ifdef MFEM_USE_MPI
|
||||
/// Parallel support for adaptive limiting.
|
||||
void EnableAdaptiveLimiting(const ParGridFunction &z0, Coefficient &coeff,
|
||||
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
|
||||
#endif
|
||||
|
||||
|
||||
/// Update the original/reference nodes used for limiting.
|
||||
void SetLimitingNodes(const GridFunction &n0);
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
#include "../pa.hpp"
|
||||
#include "../../tmop.hpp"
|
||||
#include "../../kernels.hpp"
|
||||
#include "../../../general/forall.hpp"
|
||||
#include "../../../linalg/kernels.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -64,6 +66,93 @@ void TMOP_AssembleDiagPA_C0_2D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
// Diagonal assembly for AdaptLim limiting (2D)
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AssembleDiagPA_AdaptLim_2D(const real_t lim_normal,
|
||||
const real_t adapt_lim_delta_max,
|
||||
const bool const_coeff,
|
||||
const DeviceTensor<3, const real_t> &ALC,
|
||||
const int NE,
|
||||
const DeviceTensor<5, const real_t> &J,
|
||||
const ConstDeviceMatrix &W,
|
||||
const real_t *b,
|
||||
const DeviceTensor<4, const real_t> &ALF_grad,
|
||||
const DeviceTensor<5, const real_t> &ALF_hess,
|
||||
const ConstDeviceCube &ALFmF0,
|
||||
DeviceTensor<4> &D,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const real_t normal_inv_delta_sq =
|
||||
2.0 * lim_normal / (adapt_lim_delta_max * adapt_lim_delta_max);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
MFEM_SHARED real_t sB[MD1][MQ1];
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
|
||||
// ALF and ALF0 values at quad points.
|
||||
kernels::internal::s_regs2d_t<MQ1> alf_dof, alf_quad;
|
||||
kernels::internal::LoadDofs2d(e, D1D, ALFmF0, alf_dof);
|
||||
kernels::internal::Eval2d(D1D, Q1D, smem, sB, alf_dof, alf_quad);
|
||||
|
||||
MFEM_SHARED real_t qd[MQ1 * MD1];
|
||||
DeviceTensor<2, real_t> QD(qd, MQ1, MD1);
|
||||
|
||||
for (int v = 0; v < 2; v++)
|
||||
{
|
||||
// Contract in y.
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
QD(qx, dy) = 0.0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const real_t By = sB[dy][qy];
|
||||
const real_t bb = By * By;
|
||||
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, e);
|
||||
const real_t detJtr = kernels::Det<2>(Jtr);
|
||||
const real_t weight = W(qx, qy) * detJtr;
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0) : ALC(qx, qy, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq;
|
||||
|
||||
const real_t diff = alf_quad(qy, qx);
|
||||
const real_t grad_v = ALF_grad(v, qx, qy, e);
|
||||
const real_t hess_vv = ALF_hess(v, v, qx, qy, e);
|
||||
const real_t hdiag = factor * (grad_v * grad_v + diff * hess_vv);
|
||||
|
||||
QD(qx, dy) += bb * hdiag;
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// Contract in x.
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
real_t d = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
const real_t Bx = sB[dx][qx];
|
||||
const real_t bb = Bx * Bx;
|
||||
d += bb * QD(qx, dy);
|
||||
}
|
||||
D(dx, dy, v, e) += d;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPAssembleDiagCoef2D, TMOP_AssembleDiagPA_C0_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiagCoef2D);
|
||||
|
||||
@@ -80,4 +169,34 @@ void TMOP_Integrator::AssembleDiagonalPA_C0_2D(Vector &diagonal) const
|
||||
TMOPAssembleDiagCoef2D::Run(d, q, NE, B, H0, D, d, q);
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPAssembleDiagAdaptLim2D,
|
||||
TMOP_AssembleDiagPA_AdaptLim_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiagAdaptLim2D);
|
||||
|
||||
void TMOP_Integrator::AssembleDiagonalPA_AdaptLim_2D(Vector &diagonal) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, NE);
|
||||
|
||||
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 2, q, q, NE);
|
||||
const auto ALF_hess = Reshape(PA.ALFH.Read(), 2, 2, q, q, NE);
|
||||
auto D = Reshape(diagonal.ReadWrite(), d, d, 2, NE);
|
||||
|
||||
TMOPAssembleDiagAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
|
||||
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "../../tmop.hpp"
|
||||
#include "../../kernels.hpp"
|
||||
#include "../../../general/forall.hpp"
|
||||
#include "../../../linalg/kernels.hpp"
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
@@ -128,4 +129,162 @@ void TMOP_Integrator::AssembleDiagonalPA_C0_3D(Vector &diagonal) const
|
||||
TMOPAssembleDiagCoef3D::Run(d, q, NE, B, H0, D, d, q);
|
||||
}
|
||||
|
||||
// Diagonal assembly for AdaptLim limiting (3D)
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AssembleDiagPA_AdaptLim_3D(const real_t lim_normal,
|
||||
const real_t adapt_lim_delta_max,
|
||||
const bool const_coeff,
|
||||
const DeviceTensor<4, const real_t> &ALC,
|
||||
const int NE,
|
||||
const DeviceTensor<6, const real_t> &J,
|
||||
const ConstDeviceCube &W,
|
||||
const real_t *b,
|
||||
const DeviceTensor<5, const real_t> &ALF_grad,
|
||||
const DeviceTensor<6, const real_t> &ALF_hess,
|
||||
const DeviceTensor<4, const real_t> &ALFmF0,
|
||||
DeviceTensor<5> &D,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const real_t normal_inv_delta_sq =
|
||||
2.0 * lim_normal / (adapt_lim_delta_max * adapt_lim_delta_max);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
MFEM_SHARED real_t sB[MD1][MQ1];
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
|
||||
// ALF and ALF0 values at quad points.
|
||||
kernels::internal::s_regs3d_t<MQ1> alf_dof, alf_quad;
|
||||
kernels::internal::LoadDofs3d(e, D1D, ALFmF0, alf_dof);
|
||||
kernels::internal::Eval3d(D1D, Q1D, smem, sB, alf_dof, alf_quad);
|
||||
|
||||
kernels::internal::s_regs3d_t<MQ1> r0, r1;
|
||||
|
||||
for (int v = 0; v < 3; ++v)
|
||||
{
|
||||
// Contract in z.
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
const real_t Bz = sB[dz][qz];
|
||||
const real_t bb = Bz * Bz;
|
||||
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, qz, e);
|
||||
const real_t detJtr = kernels::Det<3>(Jtr);
|
||||
const real_t weight = W(qx, qy, qz) * detJtr;
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq;
|
||||
|
||||
const real_t diff = alf_quad(qz, qy, qx);
|
||||
const real_t grad_v = ALF_grad(v, qx, qy, qz, e);
|
||||
const real_t hess_vv = ALF_hess(v, v, qx, qy, qz, e);
|
||||
const real_t hdiag = factor * (grad_v * grad_v + diff * hess_vv);
|
||||
|
||||
u += bb * hdiag;
|
||||
}
|
||||
r0[dz][qy][qx] = u;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
// Contract in y.
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
smem[qy][qx] = r0[dz][qy][qx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int qy = 0; qy < Q1D; ++qy)
|
||||
{
|
||||
const real_t By = sB[dy][qy];
|
||||
u += (By * By) * smem[qy][qx];
|
||||
}
|
||||
r1[dz][dy][qx] = u;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
// Contract in x.
|
||||
for (int dz = 0; dz < D1D; ++dz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
smem[dy][qx] = r1[dz][dy][qx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
real_t u = 0.0;
|
||||
for (int qx = 0; qx < Q1D; ++qx)
|
||||
{
|
||||
const real_t Bx = sB[dx][qx];
|
||||
u += (Bx * Bx) * smem[dy][qx];
|
||||
}
|
||||
D(dx, dy, dz, v, e) += u;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPAssembleDiagAdaptLim3D,
|
||||
TMOP_AssembleDiagPA_AdaptLim_3D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiagAdaptLim3D);
|
||||
|
||||
void TMOP_Integrator::AssembleDiagonalPA_AdaptLim_3D(Vector &diagonal) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 3, q, q, q, NE);
|
||||
const auto ALF_hess = Reshape(PA.ALFH.Read(), 3, 3, q, q, q, NE);
|
||||
auto D = Reshape(diagonal.ReadWrite(), d, d, d, 3, NE);
|
||||
|
||||
TMOPAssembleDiagAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
|
||||
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -113,6 +113,178 @@ void TMOP_AssembleGradPA_C0_2D(const real_t lim_normal,
|
||||
});
|
||||
}
|
||||
|
||||
// Assemble gradient and Hessian of ALF field at quadrature points for AdaptLim (2D)
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AssembleGradPA_AdaptLim_2D(const int NE,
|
||||
const real_t *B_nodes,
|
||||
const real_t *G_nodes,
|
||||
const real_t *B,
|
||||
const DeviceTensor<4, const real_t> &X,
|
||||
const ConstDeviceCube &ALF,
|
||||
DeviceTensor<4> &ALF_grad,
|
||||
DeviceTensor<5> &ALF_hess,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
// MD1 x MD1 or MQ1 x MQ1 shared memory block.
|
||||
MFEM_SHARED union { real_t d[MD1][MD1]; real_t q[MQ1][MQ1]; } smem;
|
||||
MFEM_SHARED real_t sB_nodes[MD1][MD1], sG_nodes[MD1][MD1];
|
||||
MFEM_SHARED real_t sB_q[MD1][MQ1];
|
||||
|
||||
kernels::internal::s_regs2d_t<MD1> grad_c;
|
||||
kernels::internal::v_regs2d_t<2, MD1> hess_c;
|
||||
|
||||
// Maps nodes - nodes.
|
||||
kernels::internal::LoadMatrix(D1D, D1D, B_nodes, sB_nodes);
|
||||
kernels::internal::LoadMatrix(D1D, D1D, G_nodes, sG_nodes);
|
||||
// Map nodes - quads.
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, B, sB_q);
|
||||
|
||||
// Compute the physical Jacobian at DOF nodes.
|
||||
kernels::internal::vd_regs2d_t<2, 2, MD1> r_X, r_J;
|
||||
kernels::internal::LoadDofs2d(e, D1D, X, r_X);
|
||||
kernels::internal::Grad2d(D1D, D1D, smem.d, sB_nodes, sG_nodes, r_X, r_J);
|
||||
|
||||
// Compute the reference derivatives of ALF at DOF nodes.
|
||||
kernels::internal::s_regs2d_t<MD1> alf_n, dalf_dx_n, dalf_dy_n;
|
||||
kernels::internal::LoadDofs2d(e, D1D, ALF, alf_n);
|
||||
kernels::internal::Contract2d<false, MD1>(D1D, D1D, smem.d,
|
||||
sG_nodes, sB_nodes,
|
||||
alf_n, dalf_dx_n);
|
||||
kernels::internal::LoadDofs2d(e, D1D, ALF, alf_n);
|
||||
kernels::internal::Contract2d<false, MD1>(D1D, D1D, smem.d,
|
||||
sB_nodes, sG_nodes,
|
||||
alf_n, dalf_dy_n);
|
||||
|
||||
// Interpolation workspaces.
|
||||
kernels::internal::s_regs2d_t<MQ1> r0, r1;
|
||||
|
||||
// Precompute the inverse of the physical Jacobian.
|
||||
kernels::internal::vd_regs2d_t<2, 2, MD1> Jpr_inv;
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
const real_t Jpr[4] =
|
||||
{
|
||||
r_J[0][0][dy][dx], r_J[1][0][dy][dx],
|
||||
r_J[0][1][dy][dx], r_J[1][1][dy][dx]
|
||||
};
|
||||
real_t Jpri[4];
|
||||
kernels::CalcInverse<2>(Jpr, Jpri);
|
||||
Jpr_inv(0, 0, dx, dy) = Jpri[0];
|
||||
Jpr_inv(1, 0, dx, dy) = Jpri[1];
|
||||
Jpr_inv(0, 1, dx, dy) = Jpri[2];
|
||||
Jpr_inv(1, 1, dx, dy) = Jpri[3];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// Compute/interpolate gradient and Hessian one vector component at a time.
|
||||
for (int c = 0; c < 2; c++)
|
||||
{
|
||||
kernels::internal::s_regs2d_t<MD1> rgrad_nodes, ddalf_dx_n, ddalf_dy_n;
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
grad_c[dy][dx] =
|
||||
Jpr_inv(0, c, dx, dy) * dalf_dx_n[dy][dx] +
|
||||
Jpr_inv(1, c, dx, dy) * dalf_dy_n[dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// Compute ALF_grad with intermediate workspaces
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
r0[dy][dx] = grad_c[dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
kernels::internal::Eval2d<MQ1>(D1D, Q1D, smem.q, sB_q, r0, r1);
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
ALF_grad(c, qx, qy, e) = r1[qy][qx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// Compute ddalf_dx_n.
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
rgrad_nodes[dy][dx] = grad_c[dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
kernels::internal::Contract2d<false, MD1>(D1D, D1D, smem.d,
|
||||
sG_nodes, sB_nodes,
|
||||
rgrad_nodes, ddalf_dx_n);
|
||||
// Compute ddalf_dy_n.
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
rgrad_nodes[dy][dx] = grad_c[dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
kernels::internal::Contract2d<false, MD1>(D1D, D1D, smem.d,
|
||||
sB_nodes, sG_nodes,
|
||||
rgrad_nodes, ddalf_dy_n);
|
||||
// Compute hess_c with ddalf_[dx, dy]_n.
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
const real_t ddalf_dx = ddalf_dx_n[dy][dx];
|
||||
const real_t ddalf_dy = ddalf_dy_n[dy][dx];
|
||||
const real_t ddx = Jpr_inv(0, 0, dy, dx) * ddalf_dx +
|
||||
Jpr_inv(1, 0, dy, dx) * ddalf_dy;
|
||||
const real_t ddy = Jpr_inv(0, 1, dy, dx) * ddalf_dx +
|
||||
Jpr_inv(1, 1, dy, dx) * ddalf_dy;
|
||||
hess_c[0][dy][dx] = ddx;
|
||||
hess_c[1][dy][dx] = ddy;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
r0[dy][dx] = hess_c[j][dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
kernels::internal::Eval2d<MQ1>(D1D, Q1D, smem.q, sB_q, r0, r1);
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
ALF_hess(c, j, qx, qy, e) = r1[qy][qx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPAssembleGradCoef2D, TMOP_AssembleGradPA_C0_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleGradCoef2D);
|
||||
|
||||
@@ -142,4 +314,29 @@ void TMOP_Integrator::AssembleGradPA_C0_2D(const Vector &x) const
|
||||
J, W, b, bld, XL, X, H0, exp_lim, d, q);
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPAssembleGradAdaptLim2D,
|
||||
TMOP_AssembleGradPA_AdaptLim_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleGradAdaptLim2D);
|
||||
|
||||
void TMOP_Integrator::AssembleGradPA_AdaptLim_2D(const Vector &x) const
|
||||
{
|
||||
if (PA.AL_grads_assembled) { return; }
|
||||
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const auto *B_nodes = PA.maps_nodes->B.Read(),
|
||||
*G_nodes = PA.maps_nodes->G.Read();
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto X = Reshape(x.Read(), d, d, 2, NE);
|
||||
const auto ALF = Reshape(PA.ALF.Read(), d, d, NE);
|
||||
auto ALF_grad = Reshape(PA.ALFG.Write(), 2, q, q, NE);
|
||||
auto ALF_hess = Reshape(PA.ALFH.Write(), 2, 2, q, q, NE);
|
||||
|
||||
TMOPAssembleGradAdaptLim2D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
|
||||
ALF_grad, ALF_hess, d, q);
|
||||
PA.AL_grads_assembled = true;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -164,4 +164,252 @@ void TMOP_Integrator::AssembleGradPA_C0_3D(const Vector &x) const
|
||||
J, W, b, bld, XL, X, H0, exp_lim, d, q);
|
||||
}
|
||||
|
||||
// Assemble gradient and Hessian of ALF field at quadrature points for AdaptLim (3D)
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AssembleGradPA_AdaptLim_3D(const int NE,
|
||||
const real_t *B_nodes,
|
||||
const real_t *G_nodes,
|
||||
const real_t *B,
|
||||
const DeviceTensor<5, const real_t> &X,
|
||||
const DeviceTensor<4, const real_t> &ALF,
|
||||
DeviceTensor<5> &ALF_grad,
|
||||
DeviceTensor<6> &ALF_hess,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
// MD1 x MD1 or MQ1 x MQ1 shared memory block.
|
||||
MFEM_SHARED union { real_t d[MD1][MD1]; real_t q[MQ1][MQ1]; } smem;
|
||||
MFEM_SHARED real_t sB_nodes[MD1][MD1], sG_nodes[MD1][MD1];
|
||||
MFEM_SHARED real_t sB_q[MD1][MQ1];
|
||||
|
||||
kernels::internal::s_regs3d_t<MD1> grad_c;
|
||||
kernels::internal::v_regs3d_t<3, MD1> hess_c;
|
||||
|
||||
// Maps nodes - nodes.
|
||||
kernels::internal::LoadMatrix(D1D, D1D, B_nodes, sB_nodes);
|
||||
kernels::internal::LoadMatrix(D1D, D1D, G_nodes, sG_nodes);
|
||||
// Map nodes - quads.
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, B, sB_q);
|
||||
|
||||
// Compute the physical Jacobian at DOF nodes.
|
||||
kernels::internal::vd_regs3d_t<3, 3, MD1> r_X, r_J;
|
||||
kernels::internal::LoadDofs3d(e, D1D, X, r_X);
|
||||
kernels::internal::Grad3d(D1D, D1D, smem.d, sB_nodes, sG_nodes, r_X, r_J);
|
||||
|
||||
// Compute the reference derivatives of ALF at DOF nodes.
|
||||
kernels::internal::s_regs3d_t<MD1> alf_n, dalf_dxi_n, dalf_deta_n, dalf_dzeta_n;
|
||||
kernels::internal::LoadDofs3d(e, D1D, ALF, alf_n);
|
||||
kernels::internal::Contract3d<false, MD1>(D1D, D1D, smem.d,
|
||||
sG_nodes, sB_nodes, sB_nodes,
|
||||
alf_n, dalf_dxi_n);
|
||||
kernels::internal::LoadDofs3d(e, D1D, ALF, alf_n);
|
||||
kernels::internal::Contract3d<false, MD1>(D1D, D1D, smem.d,
|
||||
sB_nodes, sG_nodes, sB_nodes,
|
||||
alf_n, dalf_deta_n);
|
||||
kernels::internal::LoadDofs3d(e, D1D, ALF, alf_n);
|
||||
kernels::internal::Contract3d<false, MD1>(D1D, D1D, smem.d,
|
||||
sB_nodes, sB_nodes, sG_nodes,
|
||||
alf_n, dalf_dzeta_n);
|
||||
|
||||
// Interpolation workspaces.
|
||||
kernels::internal::s_regs3d_t<MQ1> r0, r1;
|
||||
|
||||
// Compute/interpolate gradient and Hessian one vector component at a time.
|
||||
for (int c = 0; c < 3; c++)
|
||||
{
|
||||
kernels::internal::s_regs3d_t<MD1> rgrad_nodes, dd_dxi_n, dd_deta_n, dd_dzeta_n;
|
||||
|
||||
// Precompute the inverse of the physical Jacobian.
|
||||
kernels::internal::vd_regs3d_t<3, 3, MD1> Jpr_inv;
|
||||
for (int dz = 0; dz < D1D; dz++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
const real_t Jpr[9] =
|
||||
{
|
||||
r_J[0][0][dz][dy][dx], r_J[1][0][dz][dy][dx], r_J[2][0][dz][dy][dx],
|
||||
r_J[0][1][dz][dy][dx], r_J[1][1][dz][dy][dx], r_J[2][1][dz][dy][dx],
|
||||
r_J[0][2][dz][dy][dx], r_J[1][2][dz][dy][dx], r_J[2][2][dz][dy][dx]
|
||||
};
|
||||
real_t Jpri[9];
|
||||
kernels::CalcInverse<3>(Jpr, Jpri);
|
||||
Jpr_inv(0, 0, dx, dy, dz) = Jpri[0];
|
||||
Jpr_inv(1, 0, dx, dy, dz) = Jpri[1];
|
||||
Jpr_inv(2, 0, dx, dy, dz) = Jpri[2];
|
||||
Jpr_inv(0, 1, dx, dy, dz) = Jpri[3];
|
||||
Jpr_inv(1, 1, dx, dy, dz) = Jpri[4];
|
||||
Jpr_inv(2, 1, dx, dy, dz) = Jpri[5];
|
||||
Jpr_inv(0, 2, dx, dy, dz) = Jpri[6];
|
||||
Jpr_inv(1, 2, dx, dy, dz) = Jpri[7];
|
||||
Jpr_inv(2, 2, dx, dy, dz) = Jpri[8];
|
||||
|
||||
grad_c[dz][dy][dx] =
|
||||
Jpr_inv(0, c, dx, dy, dz) * dalf_dxi_n[dz][dy][dx] +
|
||||
Jpr_inv(1, c, dx, dy, dz) * dalf_deta_n[dz][dy][dx] +
|
||||
Jpr_inv(2, c, dx, dy, dz) * dalf_dzeta_n[dz][dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
// Compute ALF_grad with intermediate workspaces.
|
||||
for (int dz = 0; dz < D1D; dz++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
r0[dz][dy][dx] = grad_c[dz][dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
kernels::internal::Eval3d<MQ1>(D1D, Q1D, smem.q, sB_q, r0, r1);
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
ALF_grad(c, qx, qy, qz, e) = r1[qz][qy][qx];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
// Compute dd_dxi_n.
|
||||
for (int dz = 0; dz < D1D; dz++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
rgrad_nodes[dz][dy][dx] = grad_c[dz][dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
kernels::internal::Contract3d<false, MD1>(D1D, D1D, smem.d,
|
||||
sG_nodes, sB_nodes, sB_nodes,
|
||||
rgrad_nodes, dd_dxi_n);
|
||||
// Compute dd_deta_n.
|
||||
for (int dz = 0; dz < D1D; dz++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
rgrad_nodes[dz][dy][dx] = grad_c[dz][dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
kernels::internal::Contract3d<false, MD1>(D1D, D1D, smem.d,
|
||||
sB_nodes, sG_nodes, sB_nodes,
|
||||
rgrad_nodes, dd_deta_n);
|
||||
// Compute dd_dzeta_n.
|
||||
for (int dz = 0; dz < D1D; dz++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
rgrad_nodes[dz][dy][dx] = grad_c[dz][dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
kernels::internal::Contract3d<false, MD1>(D1D, D1D, smem.d,
|
||||
sB_nodes, sB_nodes, sG_nodes,
|
||||
rgrad_nodes, dd_dzeta_n);
|
||||
|
||||
// Compute hess_c with dd_[dxi, deta, dzeta]_n.
|
||||
for (int dz = 0; dz < D1D; dz++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
const real_t dd_dxi = dd_dxi_n[dz][dy][dx];
|
||||
const real_t dd_deta = dd_deta_n[dz][dy][dx];
|
||||
const real_t dd_dzeta = dd_dzeta_n[dz][dy][dx];
|
||||
const real_t ddx = Jpr_inv(0, 0, dx, dy, dz) * dd_dxi +
|
||||
Jpr_inv(1, 0, dx, dy, dz) * dd_deta +
|
||||
Jpr_inv(2, 0, dx, dy, dz) * dd_dzeta;
|
||||
const real_t ddy = Jpr_inv(0, 1, dx, dy, dz) * dd_dxi +
|
||||
Jpr_inv(1, 1, dx, dy, dz) * dd_deta +
|
||||
Jpr_inv(2, 1, dx, dy, dz) * dd_dzeta;
|
||||
const real_t ddz = Jpr_inv(0, 2, dx, dy, dz) * dd_dxi +
|
||||
Jpr_inv(1, 2, dx, dy, dz) * dd_deta +
|
||||
Jpr_inv(2, 2, dx, dy, dz) * dd_dzeta;
|
||||
hess_c[0][dz][dy][dx] = ddx;
|
||||
hess_c[1][dz][dy][dx] = ddy;
|
||||
hess_c[2][dz][dy][dx] = ddz;
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
for (int dz = 0; dz < D1D; dz++)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
|
||||
{
|
||||
r0[dz][dy][dx] = hess_c[j][dz][dy][dx];
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
kernels::internal::Eval3d<MQ1>(D1D, Q1D, smem.q, sB_q, r0, r1);
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
ALF_hess(c, j, qx, qy, qz, e) = r1[qz][qy][qx];
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPAssembleGradAdaptLim3D,
|
||||
TMOP_AssembleGradPA_AdaptLim_3D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleGradAdaptLim3D);
|
||||
|
||||
void TMOP_Integrator::AssembleGradPA_AdaptLim_3D(const Vector &x) const
|
||||
{
|
||||
if (PA.AL_grads_assembled) { return; }
|
||||
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const auto *B_nodes = PA.maps_nodes->B.Read(),
|
||||
*G_nodes = PA.maps_nodes->G.Read();
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto X = Reshape(x.Read(), d, d, d, 3, NE);
|
||||
const auto ALF = Reshape(PA.ALF.Read(), d, d, d, NE);
|
||||
auto ALF_grad = Reshape(PA.ALFG.Write(), 3, q, q, q, NE);
|
||||
auto ALF_hess = Reshape(PA.ALFH.Write(), 3, 3, q, q, q, NE);
|
||||
|
||||
TMOPAssembleGradAdaptLim3D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
|
||||
ALF_grad, ALF_hess, d, q);
|
||||
PA.AL_grads_assembled = true;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -67,6 +67,96 @@ void TMOP_AddMultGradPA_C0_2D(const int NE,
|
||||
});
|
||||
}
|
||||
|
||||
// Gradient action for AdaptLim limiting (2D)
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AddMultGradPA_AdaptLim_2D(const real_t lim_normal,
|
||||
const real_t adapt_lim_delta_max,
|
||||
const bool const_coeff,
|
||||
const DeviceTensor<3, const real_t> &ALC,
|
||||
const int NE,
|
||||
const DeviceTensor<5, const real_t> &J,
|
||||
const ConstDeviceMatrix &W,
|
||||
const real_t *b,
|
||||
const DeviceTensor<4, const real_t> &R,
|
||||
const DeviceTensor<4, const real_t> &ALF_grad,
|
||||
const DeviceTensor<5, const real_t> &ALF_hess,
|
||||
const ConstDeviceCube &ALFmF0,
|
||||
DeviceTensor<4> &Y,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const real_t normal_inv_delta_sq =
|
||||
2.0 * lim_normal / (adapt_lim_delta_max * adapt_lim_delta_max);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
MFEM_SHARED real_t sB[MD1][MQ1];
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
|
||||
// ALF and ALF0 values at quad points.
|
||||
kernels::internal::s_regs2d_t<MQ1> alf_dof, alf_quad;
|
||||
kernels::internal::LoadDofs2d(e, D1D, ALFmF0, alf_dof);
|
||||
kernels::internal::Eval2d(D1D, Q1D, smem, sB, alf_dof, alf_quad);
|
||||
|
||||
// Input vector R at quad points.
|
||||
kernels::internal::v_regs2d_t<2,MQ1> r_R_dof, r_R_quad;
|
||||
kernels::internal::LoadDofs2d(e, D1D, R, r_R_dof);
|
||||
kernels::internal::Eval2d(D1D, Q1D, smem, sB, r_R_dof, r_R_quad);
|
||||
|
||||
kernels::internal::v_regs2d_t<2,MQ1> r00, r01;
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, e);
|
||||
const real_t detJtr = kernels::Det<2>(Jtr);
|
||||
const real_t weight = W(qx, qy) * detJtr;
|
||||
const real_t diff = alf_quad(qy, qx);
|
||||
|
||||
// Load precomputed gradient at this quad point.
|
||||
real_t grad_alf[2] =
|
||||
{
|
||||
ALF_grad(0, qx, qy, e),
|
||||
ALF_grad(1, qx, qy, e)
|
||||
};
|
||||
|
||||
// Load precomputed Hessian at this quad point.
|
||||
real_t hess_alf[2][2];
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
hess_alf[i][j] = ALF_hess(i, j, qx, qy, e);
|
||||
}
|
||||
}
|
||||
|
||||
// Get input vector at this quad point.
|
||||
const real_t R_q[2] = { r_R_quad(0, qy, qx), r_R_quad(1, qy, qx) };
|
||||
|
||||
// Hessian action:
|
||||
// H = factor * (grad x grad + (gf - gf0) * hess)
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0) : ALC(qx, qy, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq;
|
||||
const real_t grad_dot_R = grad_alf[0] * R_q[0] + grad_alf[1] * R_q[1];
|
||||
real_t hess_R[2];
|
||||
hess_R[0] = hess_alf[0][0] * R_q[0] + hess_alf[0][1] * R_q[1];
|
||||
hess_R[1] = hess_alf[1][0] * R_q[0] + hess_alf[1][1] * R_q[1];
|
||||
|
||||
r00(0, qy, qx) = factor * (grad_alf[0] * grad_dot_R + diff * hess_R[0]);
|
||||
r00(1, qy, qx) = factor * (grad_alf[1] * grad_dot_R + diff * hess_R[1]);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
kernels::internal::EvalTranspose2d(D1D, Q1D, smem, sB, r00, r01);
|
||||
kernels::internal::WriteDofs2d(e, D1D, r01, Y);
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPMultGradCoefKernels, TMOP_AddMultGradPA_C0_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPMultGradCoefKernels);
|
||||
|
||||
@@ -85,4 +175,34 @@ void TMOP_Integrator::AddMultGradPA_C0_2D(const Vector &R, Vector &C) const
|
||||
TMOPMultGradCoefKernels::Run(d, q, NE, b, H0, X, Y, d, q);
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPMultGradAdaptLim, TMOP_AddMultGradPA_AdaptLim_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPMultGradAdaptLim);
|
||||
|
||||
void TMOP_Integrator::AddMultGradPA_AdaptLim_2D(const Vector &R,
|
||||
Vector &C) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
|
||||
const auto RR = Reshape(R.Read(), d, d, 2, NE);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 2, q, q, NE);
|
||||
const auto ALF_hess = Reshape(PA.ALFH.Read(), 2, 2, q, q, NE);
|
||||
auto Y = Reshape(C.ReadWrite(), d, d, 2, NE);
|
||||
|
||||
TMOPMultGradAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, B,
|
||||
RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -98,4 +98,135 @@ void TMOP_Integrator::AddMultGradPA_C0_3D(const Vector &R, Vector &C) const
|
||||
TMOPMultGradCoefKernels3D::Run(d, q, NE, b, H0, X, Y, d, q);
|
||||
}
|
||||
|
||||
// Gradient action for AdaptLim limiting (3D)
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AddMultGradPA_AdaptLim_3D(const real_t lim_normal,
|
||||
const real_t adapt_lim_delta_max,
|
||||
const bool const_coeff,
|
||||
const DeviceTensor<4, const real_t> &ALC,
|
||||
const int NE,
|
||||
const DeviceTensor<6, const real_t> &J,
|
||||
const ConstDeviceCube &W,
|
||||
const real_t *b,
|
||||
const DeviceTensor<5, const real_t> &R,
|
||||
const DeviceTensor<5, const real_t> &ALF_grad,
|
||||
const DeviceTensor<6, const real_t> &ALF_hess,
|
||||
const DeviceTensor<4, const real_t> &ALFmF0,
|
||||
DeviceTensor<5> &Y,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const real_t normal_inv_delta_sq =
|
||||
2.0 * lim_normal / (adapt_lim_delta_max * adapt_lim_delta_max);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
MFEM_SHARED real_t sB[MD1][MQ1];
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
|
||||
// ALF and ALF0 values at quad points.
|
||||
kernels::internal::s_regs3d_t<MQ1> alf_dof, alf_quad;
|
||||
kernels::internal::LoadDofs3d(e, D1D, ALFmF0, alf_dof);
|
||||
kernels::internal::Eval3d(D1D, Q1D, smem, sB, alf_dof, alf_quad);
|
||||
|
||||
// Input vector R at quad points.
|
||||
kernels::internal::v_regs3d_t<3, MQ1> r_R_dof, r_R_quad;
|
||||
kernels::internal::LoadDofs3d(e, D1D, R, r_R_dof);
|
||||
kernels::internal::Eval3d(D1D, Q1D, smem, sB, r_R_dof, r_R_quad);
|
||||
|
||||
kernels::internal::v_regs3d_t<3, MQ1> r00, r01;
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, qz, e);
|
||||
const real_t detJtr = kernels::Det<3>(Jtr);
|
||||
const real_t weight = W(qx, qy, qz) * detJtr;
|
||||
const real_t diff = alf_quad(qz, qy, qx);
|
||||
|
||||
// Load precomputed gradient at this quad point.
|
||||
const real_t grad_alf[3] =
|
||||
{
|
||||
ALF_grad(0, qx, qy, qz, e),
|
||||
ALF_grad(1, qx, qy, qz, e),
|
||||
ALF_grad(2, qx, qy, qz, e)
|
||||
};
|
||||
|
||||
// Get input vector at this quad point.
|
||||
const real_t R_q[3] =
|
||||
{
|
||||
r_R_quad(0, qz, qy, qx),
|
||||
r_R_quad(1, qz, qy, qx),
|
||||
r_R_quad(2, qz, qy, qx)
|
||||
};
|
||||
|
||||
// Hessian action:
|
||||
// H = factor * (grad x grad + (gf - gf0) * hess)
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq;
|
||||
const real_t grad_dot_R =
|
||||
grad_alf[0] * R_q[0] + grad_alf[1] * R_q[1] + grad_alf[2] * R_q[2];
|
||||
real_t hess_R[3];
|
||||
hess_R[0] =
|
||||
ALF_hess(0, 0, qx, qy, qz, e) * R_q[0] +
|
||||
ALF_hess(0, 1, qx, qy, qz, e) * R_q[1] +
|
||||
ALF_hess(0, 2, qx, qy, qz, e) * R_q[2];
|
||||
hess_R[1] =
|
||||
ALF_hess(1, 0, qx, qy, qz, e) * R_q[0] +
|
||||
ALF_hess(1, 1, qx, qy, qz, e) * R_q[1] +
|
||||
ALF_hess(1, 2, qx, qy, qz, e) * R_q[2];
|
||||
hess_R[2] =
|
||||
ALF_hess(2, 0, qx, qy, qz, e) * R_q[0] +
|
||||
ALF_hess(2, 1, qx, qy, qz, e) * R_q[1] +
|
||||
ALF_hess(2, 2, qx, qy, qz, e) * R_q[2];
|
||||
|
||||
r00(0, qz, qy, qx) = factor * (grad_alf[0] * grad_dot_R + diff * hess_R[0]);
|
||||
r00(1, qz, qy, qx) = factor * (grad_alf[1] * grad_dot_R + diff * hess_R[1]);
|
||||
r00(2, qz, qy, qx) = factor * (grad_alf[2] * grad_dot_R + diff * hess_R[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
kernels::internal::EvalTranspose3d(D1D, Q1D, smem, sB, r00, r01);
|
||||
kernels::internal::WriteDofs3d(e, D1D, r01, Y);
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPMultGradAdaptLim3D, TMOP_AddMultGradPA_AdaptLim_3D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPMultGradAdaptLim3D);
|
||||
|
||||
void TMOP_Integrator::AddMultGradPA_AdaptLim_3D(const Vector &R,
|
||||
Vector &C) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
|
||||
const auto RR = Reshape(R.Read(), d, d, d, 3, NE);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 3, q, q, q, NE);
|
||||
const auto ALF_hess = Reshape(PA.ALFH.Read(), 3, 3, q, q, q, NE);
|
||||
auto Y = Reshape(C.ReadWrite(), d, d, d, 3, NE);
|
||||
|
||||
TMOPMultGradAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, B,
|
||||
RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -108,6 +108,64 @@ void TMOP_AddMultPA_C0_2D(const real_t lim_normal,
|
||||
});
|
||||
}
|
||||
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AddMultPA_AdaptLim_2D(const real_t lim_normal,
|
||||
const real_t adapt_lim_delta_max,
|
||||
const bool const_coeff,
|
||||
const DeviceTensor<3, const real_t> &ALC,
|
||||
const int NE,
|
||||
const DeviceTensor<5, const real_t> &J,
|
||||
const ConstDeviceMatrix &W,
|
||||
const real_t *b,
|
||||
const DeviceTensor<4, const real_t> &ALF_grad,
|
||||
const ConstDeviceCube &ALFmF0,
|
||||
DeviceTensor<4> &Y,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const real_t normal_inv_delta_sq =
|
||||
2.0 * lim_normal / (adapt_lim_delta_max * adapt_lim_delta_max);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
MFEM_SHARED real_t sB[MD1][MQ1];
|
||||
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
|
||||
// Evaluate ALF and ALF0 at the quad points.
|
||||
kernels::internal::s_regs2d_t<MQ1> alf_dof, alf_quad;
|
||||
kernels::internal::LoadDofs2d(e, D1D, ALFmF0, alf_dof);
|
||||
kernels::internal::Eval2d(D1D, Q1D, smem, sB,
|
||||
alf_dof, alf_quad);
|
||||
|
||||
kernels::internal::v_regs2d_t<2,MQ1> r00, r01;
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, e);
|
||||
const real_t detJtr = kernels::Det<2>(Jtr);
|
||||
const real_t weight = W(qx, qy) * detJtr;
|
||||
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0) : ALC(qx, qy, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq *
|
||||
alf_quad(qy, qx);
|
||||
|
||||
r00(0, qy, qx) = factor * ALF_grad(0, qx, qy, e);
|
||||
r00(1, qy, qx) = factor * ALF_grad(1, qx, qy, e);
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
kernels::internal::EvalTranspose2d(D1D, Q1D, smem, sB, r00, r01);
|
||||
kernels::internal::WriteDofs2d(e, D1D, r01, Y);
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPMultCoefKernels, TMOP_AddMultPA_C0_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPMultCoefKernels);
|
||||
|
||||
@@ -140,4 +198,32 @@ void TMOP_Integrator::AddMultPA_C0_2D(const Vector &x, Vector &y) const
|
||||
Y, exp_lim, d, q);
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPMultAdaptLim, TMOP_AddMultPA_AdaptLim_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPMultAdaptLim);
|
||||
|
||||
void TMOP_Integrator::AddMultPA_AdaptLim_2D([[maybe_unused]] const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 2, q, q, NE);
|
||||
auto Y = Reshape(y.ReadWrite(), d, d, 2, NE);
|
||||
|
||||
TMOPMultAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
|
||||
B, ALF_grad, ALFmF0, Y, d, q);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -148,4 +148,95 @@ void TMOP_Integrator::AddMultPA_C0_3D(const Vector &x, Vector &y) const
|
||||
X, Y, exp_lim, d, q);
|
||||
}
|
||||
|
||||
// Residual term for AdaptLim limiting (3D)
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_AddMultPA_AdaptLim_3D(const real_t lim_normal,
|
||||
const real_t adapt_lim_delta_max,
|
||||
const bool const_coeff,
|
||||
const DeviceTensor<4, const real_t> &ALC,
|
||||
const int NE,
|
||||
const DeviceTensor<6, const real_t> &J,
|
||||
const ConstDeviceCube &W,
|
||||
const real_t *b,
|
||||
const DeviceTensor<5, const real_t> &ALF_grad,
|
||||
const DeviceTensor<4, const real_t> &ALFmF0,
|
||||
DeviceTensor<5> &Y,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
const real_t normal_inv_delta_sq =
|
||||
2.0 * lim_normal / (adapt_lim_delta_max * adapt_lim_delta_max);
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
MFEM_SHARED real_t sB[MD1][MQ1];
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
|
||||
// Evaluate ALF and ALF0 at the quad points.
|
||||
kernels::internal::s_regs3d_t<MQ1> alf_dof, alf_quad;
|
||||
kernels::internal::LoadDofs3d(e, D1D, ALFmF0, alf_dof);
|
||||
kernels::internal::Eval3d(D1D, Q1D, smem, sB, alf_dof, alf_quad);
|
||||
|
||||
kernels::internal::v_regs3d_t<3, MQ1> r00, r01;
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, qz, e);
|
||||
const real_t detJtr = kernels::Det<3>(Jtr);
|
||||
const real_t weight = W(qx, qy, qz) * detJtr;
|
||||
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
const real_t factor = weight * coeff * normal_inv_delta_sq *
|
||||
alf_quad(qz, qy, qx);
|
||||
|
||||
r00(0, qz, qy, qx) = factor * ALF_grad(0, qx, qy, qz, e);
|
||||
r00(1, qz, qy, qx) = factor * ALF_grad(1, qx, qy, qz, e);
|
||||
r00(2, qz, qy, qx) = factor * ALF_grad(2, qx, qy, qz, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
MFEM_SYNC_THREAD;
|
||||
|
||||
kernels::internal::EvalTranspose3d(D1D, Q1D, smem, sB, r00, r01);
|
||||
kernels::internal::WriteDofs3d(e, D1D, r01, Y);
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPMultAdaptLim3D, TMOP_AddMultPA_AdaptLim_3D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPMultAdaptLim3D);
|
||||
|
||||
void TMOP_Integrator::AddMultPA_AdaptLim_3D([[maybe_unused]] const Vector &x,
|
||||
Vector &y) const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
|
||||
const auto *B = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
|
||||
const auto ALF_grad = Reshape(PA.ALFG.Read(), 3, q, q, q, NE);
|
||||
auto Y = Reshape(y.ReadWrite(), d, d, d, 3, NE);
|
||||
|
||||
TMOPMultAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
|
||||
B, ALF_grad, ALFmF0, Y, d, q);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+121
-3
@@ -46,12 +46,14 @@ void TMOP_Integrator::AssembleGradPA(const Vector &de,
|
||||
{
|
||||
AssembleGradPA_2D(xe);
|
||||
if (lim_coeff) { AssembleGradPA_C0_2D(xe); }
|
||||
if (adapt_lim_gf) { AssembleGradPA_AdaptLim_2D(xe); }
|
||||
}
|
||||
|
||||
if (PA.dim == 3)
|
||||
{
|
||||
AssembleGradPA_3D(xe);
|
||||
if (lim_coeff) { AssembleGradPA_C0_3D(xe); }
|
||||
if (adapt_lim_gf) { AssembleGradPA_AdaptLim_3D(xe); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,12 +199,14 @@ void TMOP_Integrator::UpdateCoefficientsPA(const Vector &d_loc)
|
||||
add(*x_0, d_loc, x_loc);
|
||||
}
|
||||
|
||||
// Both are constant or not specified.
|
||||
if (PA.MC.Size() == 1 && PA.C0.Size() == 1) { return; }
|
||||
|
||||
// All are constant or not specified.
|
||||
if (PA.MC.Size() == 1 && PA.C0.Size() <= 1 && PA.ALC.Size() <= 1) { return; }
|
||||
|
||||
// Coefficients are always evaluated on the CPU for now.
|
||||
PA.MC.HostWrite();
|
||||
PA.C0.HostWrite();
|
||||
PA.ALC.HostWrite();
|
||||
|
||||
const IntegrationRule &ir = *PA.ir;
|
||||
auto T = new IsoparametricTransformation;
|
||||
@@ -226,6 +230,14 @@ void TMOP_Integrator::UpdateCoefficientsPA(const Vector &d_loc)
|
||||
PA.C0(q + e * PA.nq) = lim_coeff->Eval(*T, ir.IntPoint(q));
|
||||
}
|
||||
}
|
||||
|
||||
if (PA.ALC.Size() > 1)
|
||||
{
|
||||
for (int q = 0; q < PA.nq; ++q)
|
||||
{
|
||||
PA.ALC(q + e * PA.nq) = adapt_lim_coeff->Eval(*T, ir.IntPoint(q));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete T;
|
||||
@@ -321,7 +333,93 @@ void TMOP_Integrator::AssemblePA(const FiniteElementSpace &fes)
|
||||
PA.Jtr_debug_grad = false;
|
||||
|
||||
// Limiting: lim_coeff -> PA.C0, lim_nodes0 -> PA.XL, lim_dist -> PA.LD, PA.H0
|
||||
if (lim_coeff) { AssemblePA_Limiting(); }
|
||||
if (lim_coeff) { AssemblePA_Limiting(); }
|
||||
// Adaptive limiting: adapt_lim_coeff -> PA.ALC, adapt_lim_gf -> PA.ALF,
|
||||
// adapt_lim_gf0 -> PA.ALF0, adapt_lim_delta_max -> PA.ALD
|
||||
if (adapt_lim_gf) { AssemblePA_AdaptLim(); }
|
||||
}
|
||||
|
||||
void TMOP_Integrator::AssemblePA_AdaptLim()
|
||||
{
|
||||
const FiniteElementSpace *alfes = adapt_lim_gf->FESpace();
|
||||
|
||||
MFEM_VERIFY(strcmp(alfes->FEColl()->Name(), PA.fes->FEColl()->Name()) == 0 &&
|
||||
alfes->FEColl()->GetOrder() == PA.fes->FEColl()->GetOrder(),
|
||||
"The PA code assumes the same FE spaces for mesh and limiting.");
|
||||
|
||||
PA.AL_grads_assembled = false;
|
||||
|
||||
// adapt_lim_coeff -> PA.ALC (Q-vector).
|
||||
PA.ALC.UseDevice(true);
|
||||
if (auto *cQ = dynamic_cast<ConstantCoefficient *>(adapt_lim_coeff))
|
||||
{
|
||||
PA.ALC.SetSize(1, Device::GetMemoryType());
|
||||
PA.ALC.HostWrite();
|
||||
PA.ALC(0) = cQ->constant;
|
||||
}
|
||||
else
|
||||
{
|
||||
PA.ALC.SetSize(PA.nq * PA.ne, Device::GetMemoryType());
|
||||
auto ALC = Reshape(PA.ALC.HostWrite(), PA.nq, PA.ne);
|
||||
for (int e = 0; e < PA.ne; ++e)
|
||||
{
|
||||
ElementTransformation &T = *PA.fes->GetElementTransformation(e);
|
||||
for (int q = 0; q < PA.ir->GetNPoints(); ++q)
|
||||
{
|
||||
ALC(q, e) = adapt_lim_coeff->Eval(T, PA.ir->IntPoint(q));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC;
|
||||
|
||||
const FiniteElement *fe_n = PA.fes->GetTypicalFE();
|
||||
// GetNodes() for tensor H1 elements with H1_DOF_MAP is stored in NATIVE
|
||||
// order (via dof_map), while DofToQuad::TENSOR assumes LEXICOGRAPHIC
|
||||
// ordering of the integration points.
|
||||
const IntegrationRule &nodes = fe_n->GetNodes();
|
||||
const auto *nfe = dynamic_cast<const NodalFiniteElement *>(fe_n);
|
||||
const Array<int> *lex = (nfe && nfe->GetLexicographicOrdering().Size() > 0)
|
||||
? &nfe->GetLexicographicOrdering() : nullptr;
|
||||
if (!lex)
|
||||
{
|
||||
PA.maps_nodes = &fe_n->GetDofToQuad(nodes, DofToQuad::TENSOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
IntegrationRule lex_nodes(nodes.GetNPoints());
|
||||
MFEM_VERIFY(lex->Size() == nodes.GetNPoints(), "");
|
||||
for (int i = 0; i < nodes.GetNPoints(); i++)
|
||||
{
|
||||
lex_nodes.IntPoint(i) = nodes.IntPoint((*lex)[i]);
|
||||
}
|
||||
PA.maps_nodes = &fe_n->GetDofToQuad(lex_nodes, DofToQuad::TENSOR);
|
||||
}
|
||||
|
||||
// adapt_lim_gf -> PA.ALF (E-vector, same pattern as LD).
|
||||
const FiniteElement &fe = *alfes->GetTypicalFE();
|
||||
PA.ALF.SetSize(PA.ne * fe.GetDof(), Device::GetMemoryType());
|
||||
PA.ALF.UseDevice(true);
|
||||
const Operator *alf_R = alfes->GetElementRestriction(ordering);
|
||||
alf_R->Mult(*adapt_lim_gf, PA.ALF);
|
||||
// adapt_lim_gf - adapt_lim_gf0 -> PA.ALFmF0
|
||||
PA.ALFmF0.SetSize(PA.ne * fe.GetDof(), Device::GetMemoryType());
|
||||
PA.ALFmF0.UseDevice(true);
|
||||
alf_R->Mult(*adapt_lim_gf0, PA.ALFmF0);
|
||||
PA.ALFmF0 *= -1.0;
|
||||
PA.ALFmF0 += PA.ALF;
|
||||
|
||||
// adapt_lim_delta_max -> PA.al_delta.
|
||||
PA.al_delta = adapt_lim_delta_max;
|
||||
|
||||
// Allocate storage for gradient and Hessian of ALF at quadrature points
|
||||
// These will be filled during AssembleGradPA
|
||||
const int dim = PA.dim;
|
||||
PA.ALFG.UseDevice(true);
|
||||
PA.ALFG.SetSize(dim * PA.nq * PA.ne, Device::GetMemoryType());
|
||||
PA.ALFH.UseDevice(true);
|
||||
PA.ALFH.SetSize(dim * dim * PA.nq * PA.ne, Device::GetMemoryType());
|
||||
|
||||
}
|
||||
|
||||
void TMOP_Integrator::AssembleGradDiagonalPA(Vector &de) const
|
||||
@@ -341,12 +439,14 @@ void TMOP_Integrator::AssembleGradDiagonalPA(Vector &de) const
|
||||
{
|
||||
AssembleDiagonalPA_2D(de);
|
||||
if (lim_coeff) { AssembleDiagonalPA_C0_2D(de); }
|
||||
if (adapt_lim_gf) { AssembleDiagonalPA_AdaptLim_2D(de); }
|
||||
}
|
||||
|
||||
if (PA.dim == 3)
|
||||
{
|
||||
AssembleDiagonalPA_3D(de);
|
||||
if (lim_coeff) { AssembleDiagonalPA_C0_3D(de); }
|
||||
if (adapt_lim_gf) { AssembleDiagonalPA_AdaptLim_3D(de); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,12 +473,26 @@ void TMOP_Integrator::AddMultPA(const Vector &de, Vector &ye) const
|
||||
{
|
||||
AddMultPA_2D(xe, ye);
|
||||
if (lim_coeff) { AddMultPA_C0_2D(xe, ye); }
|
||||
if (adapt_lim_gf)
|
||||
{
|
||||
// AddMultPA_AdaptLim_2D uses the precomputed AdaptLim field gradient
|
||||
// at quadrature points (PA.ALFG). Ensure it is up-to-date for the
|
||||
// current mesh configuration.
|
||||
AssembleGradPA_AdaptLim_2D(xe);
|
||||
AddMultPA_AdaptLim_2D(xe, ye);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (PA.dim == 3)
|
||||
{
|
||||
AddMultPA_3D(xe, ye);
|
||||
if (lim_coeff) { AddMultPA_C0_3D(xe, ye); }
|
||||
if (adapt_lim_gf)
|
||||
{
|
||||
AssembleGradPA_AdaptLim_3D(xe);
|
||||
AddMultPA_AdaptLim_3D(xe, ye);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,12 +513,14 @@ void TMOP_Integrator::AddMultGradPA(const Vector &re, Vector &ce) const
|
||||
{
|
||||
AddMultGradPA_2D(re, ce);
|
||||
if (lim_coeff) { AddMultGradPA_C0_2D(re, ce); }
|
||||
if (adapt_lim_gf) { AddMultGradPA_AdaptLim_2D(re, ce); }
|
||||
}
|
||||
|
||||
if (PA.dim == 3)
|
||||
{
|
||||
AddMultGradPA_3D(re, ce);
|
||||
if (lim_coeff) { AddMultGradPA_C0_3D(re, ce); }
|
||||
if (adapt_lim_gf) { AddMultGradPA_AdaptLim_3D(re, ce); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,12 +549,14 @@ real_t TMOP_Integrator::GetLocalStateEnergyPA(const Vector &de) const
|
||||
{
|
||||
GetLocalStateEnergyPA_2D(xe, energy);
|
||||
if (lim_coeff) { energy += GetLocalStateEnergyPA_C0_2D(xe); }
|
||||
if (adapt_lim_gf) { energy += GetLocalStateEnergyPA_AdaptLim_2D(); }
|
||||
}
|
||||
|
||||
if (PA.dim == 3)
|
||||
{
|
||||
GetLocalStateEnergyPA_3D(xe, energy);
|
||||
if (lim_coeff) { energy += GetLocalStateEnergyPA_C0_3D(xe); }
|
||||
if (adapt_lim_gf) { energy += GetLocalStateEnergyPA_AdaptLim_3D(); }
|
||||
}
|
||||
|
||||
return energy;
|
||||
|
||||
@@ -92,6 +92,55 @@ void TMOP_EnergyPA_C0_2D(const real_t lim_normal,
|
||||
});
|
||||
}
|
||||
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_EnergyPA_AdaptLim_2D(const real_t lim_normal,
|
||||
const real_t adapt_lim_delta_max,
|
||||
const bool const_coeff,
|
||||
const DeviceTensor<3, const real_t> &ALC,
|
||||
const int NE,
|
||||
const DeviceTensor<5, const real_t> &J,
|
||||
const ConstDeviceMatrix &W,
|
||||
const real_t *b,
|
||||
const ConstDeviceCube &ALFmF0,
|
||||
DeviceTensor<3> &E,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
MFEM_SHARED real_t sB[MD1][MQ1];
|
||||
|
||||
// Load basis functions for ALF/ALF0.
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
|
||||
// Load ALF and ALF0 (scalar pattern).
|
||||
kernels::internal::s_regs2d_t<MQ1> rtmp, ralf;
|
||||
kernels::internal::LoadDofs2d(e, D1D, ALFmF0, rtmp);
|
||||
kernels::internal::Eval2d(D1D, Q1D, smem, sB, rtmp, ralf);
|
||||
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, e);
|
||||
const real_t detJtr = kernels::Det<2>(Jtr);
|
||||
const real_t weight = W(qx, qy) * detJtr;
|
||||
|
||||
const real_t diff = ralf(qy, qx) / adapt_lim_delta_max;
|
||||
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0) : ALC(qx, qy, e);
|
||||
|
||||
// Energy: coeff * lim_normal * diff^2
|
||||
E(qx, qy, e) = weight * coeff * lim_normal * diff * diff;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPEnergyCoef2D, TMOP_EnergyPA_C0_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPEnergyCoef2D);
|
||||
|
||||
@@ -127,4 +176,32 @@ real_t TMOP_Integrator::GetLocalStateEnergyPA_C0_2D(const Vector &x) const
|
||||
return PA.E * PA.O;
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPEnergyAdaptLim2D, TMOP_EnergyPA_AdaptLim_2D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPEnergyAdaptLim2D);
|
||||
|
||||
real_t TMOP_Integrator::GetLocalStateEnergyPA_AdaptLim_2D() const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
|
||||
const auto *b = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
|
||||
auto E = Reshape(PA.E.Write(), q, q, NE);
|
||||
|
||||
TMOPEnergyAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
|
||||
ALFmF0, E, d, q);
|
||||
|
||||
return PA.E * PA.O;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -107,6 +107,56 @@ void TMOP_EnergyPA_C0_3D(const real_t lim_normal,
|
||||
});
|
||||
}
|
||||
|
||||
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
|
||||
void TMOP_EnergyPA_AdaptLim_3D(const real_t lim_normal,
|
||||
const real_t adapt_lim_delta_max,
|
||||
const bool const_coeff,
|
||||
const DeviceTensor<4, const real_t> &ALC,
|
||||
const int NE,
|
||||
const DeviceTensor<6, const real_t> &J,
|
||||
const ConstDeviceCube &W,
|
||||
const real_t *b,
|
||||
const DeviceTensor<4, const real_t> &ALFmF0,
|
||||
DeviceTensor<4> &E,
|
||||
const int d1d,
|
||||
const int q1d)
|
||||
{
|
||||
const int D1D = T_D1D ? T_D1D : d1d;
|
||||
const int Q1D = T_Q1D ? T_Q1D : q1d;
|
||||
|
||||
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
|
||||
{
|
||||
MFEM_SHARED real_t smem[MQ1][MQ1];
|
||||
MFEM_SHARED real_t sB[MD1][MQ1];
|
||||
|
||||
// Load basis functions for ALF/ALF0.
|
||||
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
|
||||
|
||||
// Load ALF and ALF0 (scalar pattern).
|
||||
kernels::internal::s_regs3d_t<MQ1> rtmp, ralf;
|
||||
kernels::internal::LoadDofs3d(e, D1D, ALFmF0, rtmp);
|
||||
kernels::internal::Eval3d(D1D, Q1D, smem, sB, rtmp, ralf);
|
||||
|
||||
for (int qz = 0; qz < Q1D; ++qz)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
|
||||
{
|
||||
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
|
||||
{
|
||||
const real_t *Jtr = &J(0, 0, qx, qy, qz, e);
|
||||
const real_t detJtr = kernels::Det<3>(Jtr);
|
||||
const real_t weight = W(qx, qy, qz) * detJtr;
|
||||
|
||||
const real_t diff = ralf(qz, qy, qx) / adapt_lim_delta_max;
|
||||
|
||||
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
|
||||
E(qx, qy, qz, e) = weight * coeff * lim_normal * diff * diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPEnergyPAC03D, TMOP_EnergyPA_C0_3D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPEnergyPAC03D);
|
||||
|
||||
@@ -142,4 +192,32 @@ real_t TMOP_Integrator::GetLocalStateEnergyPA_C0_3D(const Vector &x) const
|
||||
return PA.E * PA.O;
|
||||
}
|
||||
|
||||
MFEM_TMOP_MDQ_REGISTER(TMOPEnergyAdaptLim3D, TMOP_EnergyPA_AdaptLim_3D);
|
||||
MFEM_TMOP_MDQ_SPECIALIZE(TMOPEnergyAdaptLim3D);
|
||||
|
||||
real_t TMOP_Integrator::GetLocalStateEnergyPA_AdaptLim_3D() const
|
||||
{
|
||||
const real_t ln = lim_normal;
|
||||
const real_t delta_max = PA.al_delta;
|
||||
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
|
||||
|
||||
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
|
||||
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
|
||||
|
||||
const bool const_coeff = PA.ALC.Size() == 1;
|
||||
const auto ALC = const_coeff
|
||||
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
|
||||
: Reshape(PA.ALC.Read(), q, q, q, NE);
|
||||
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
|
||||
const auto *b = PA.maps->B.Read();
|
||||
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
|
||||
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
|
||||
auto E = Reshape(PA.E.Write(), q, q, q, NE);
|
||||
|
||||
TMOPEnergyAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
|
||||
ALFmF0, E, d, q);
|
||||
|
||||
return PA.E * PA.O;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+21
-4
@@ -111,6 +111,25 @@ void Array<T>::PartialSum()
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
MFEM_HOST_DEVICE inline U abs_signed(U v) { return (v < U(0)) ? -v : v; }
|
||||
|
||||
template <typename U>
|
||||
void AbsImpl(std::true_type /*signed*/, U* y, int N, bool useDevice)
|
||||
{
|
||||
mfem::forall_switch(useDevice, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
y[i] = abs_signed(y[i]);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void AbsImpl(std::false_type /*unsigned*/, U* /*y*/, int /*N*/,
|
||||
bool /*useDevice*/)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Array<T>::Abs()
|
||||
{
|
||||
@@ -118,10 +137,7 @@ void Array<T>::Abs()
|
||||
const bool useDevice = UseDevice();
|
||||
const int N = size;
|
||||
auto y = ReadWrite(useDevice);
|
||||
mfem::forall_switch(useDevice, N, [=] MFEM_HOST_DEVICE (int i)
|
||||
{
|
||||
y[i] = std::abs(y[i]);
|
||||
});
|
||||
AbsImpl<T>(std::is_signed<T> {}, y, N, useDevice);
|
||||
}
|
||||
|
||||
// Sum
|
||||
@@ -207,6 +223,7 @@ void Array2D<T>::Print(std::ostream &os, int width_)
|
||||
template class Array<char>;
|
||||
template class Array<int>;
|
||||
template class Array<long long>;
|
||||
template class Array<unsigned int>;
|
||||
template class Array<real_t>;
|
||||
template class Array2D<int>;
|
||||
template class Array2D<real_t>;
|
||||
|
||||
@@ -37,6 +37,16 @@
|
||||
// removed in a future release).
|
||||
#define CUB_IGNORE_DEPRECATED_CPP_DIALECT
|
||||
#define THRUST_IGNORE_DEPRECATED_CPP_DIALECT
|
||||
|
||||
// MFEM only supports using RAJA/CAMP backends in default stream mode because
|
||||
// memory calls are performed outside of the RAJA ecosystem
|
||||
#ifndef CAMP_USE_PLATFORM_DEFAULT_STREAM
|
||||
#define CAMP_USE_PLATFORM_DEFAULT_STREAM 1
|
||||
#else
|
||||
#if !CAMP_USE_PLATFORM_DEFAULT_STREAM
|
||||
#error "MFEM only supports RAJA/CAMP with the default platform stream."
|
||||
#endif
|
||||
#endif
|
||||
#include "RAJA/RAJA.hpp"
|
||||
#if defined(RAJA_ENABLE_CUDA) && !defined(MFEM_USE_CUDA)
|
||||
#error When RAJA is built with CUDA, MFEM_USE_CUDA=YES is required
|
||||
|
||||
@@ -25,6 +25,13 @@ namespace mfem
|
||||
namespace bin_io
|
||||
{
|
||||
|
||||
/// Enum to specify if values should be read in binary or ASCII format.
|
||||
enum BinaryOrASCII : bool
|
||||
{
|
||||
ASCII = false,
|
||||
BINARY = true
|
||||
};
|
||||
|
||||
/// Write 'value' to stream.
|
||||
template<typename T>
|
||||
inline void write(std::ostream& os, T value)
|
||||
@@ -73,6 +80,38 @@ void DecodeBase64(const char *src, size_t len, std::vector<char> &buf);
|
||||
/// This is equal to 4*nbytes/3, rounded up to the nearest multiple of 4.
|
||||
size_t NumBase64Chars(size_t nbytes);
|
||||
|
||||
/// @brief Read and return a value of type @a T from the input stream, in either
|
||||
/// binary or ASCII format, depending on the value of @a binary.
|
||||
template <typename T>
|
||||
T ReadBinaryOrASCII(std::istream &input, BinaryOrASCII binary)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
return read<T>(input);
|
||||
}
|
||||
else
|
||||
{
|
||||
T val;
|
||||
input >> val;
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Skip @a num values of type @a T from the input stream, in either
|
||||
/// binary or ASCII format, depending on the value of @a binary.
|
||||
template <typename T>
|
||||
void Skip(std::istream &input, int num, BinaryOrASCII binary)
|
||||
{
|
||||
if (binary)
|
||||
{
|
||||
input.ignore(sizeof(T) * num);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < num; ++i) { ReadBinaryOrASCII<T>(input, ASCII); }
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem::bin_io
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
+156
-12
@@ -277,6 +277,9 @@ void OmpWrap3D(const int Nx, const int Ny, const int Nz, HBODY &&h_body)
|
||||
|
||||
/// RAJA Cuda and Hip backends
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA) && defined(__CUDACC__)
|
||||
template <int LB>
|
||||
using cuda_launch_bounds_policy =
|
||||
RAJA::LaunchPolicy<RAJA::cuda_launch_t<true, LB>>;
|
||||
using cuda_launch_policy =
|
||||
RAJA::LaunchPolicy<RAJA::cuda_launch_t<true>>;
|
||||
using cuda_teams_x =
|
||||
@@ -288,6 +291,9 @@ using cuda_threads_z =
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP) && defined(__HIP__)
|
||||
using hip_launch_policy =
|
||||
RAJA::LaunchPolicy<RAJA::hip_launch_t<true>>;
|
||||
template <int LB>
|
||||
using hip_launch_bounds_policy =
|
||||
RAJA::LaunchPolicy<RAJA::hip_launch_t<true, LB>>;
|
||||
using hip_teams_x =
|
||||
RAJA::LoopPolicy<RAJA::hip_block_x_direct>;
|
||||
using hip_threads_z =
|
||||
@@ -337,6 +343,33 @@ void RajaCuWrap2D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <int LB, typename DBODY>
|
||||
void RajaCuWrap2DLaunchBounds(const int N, DBODY &&d_body, const int X,
|
||||
const int Y, const int BZ)
|
||||
{
|
||||
MFEM_VERIFY(BZ>0, "");
|
||||
const int G = (N+BZ-1)/BZ;
|
||||
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<cuda_launch_bounds_policy<LB> >
|
||||
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
loop<cuda_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
|
||||
{
|
||||
loop<cuda_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
|
||||
{
|
||||
const int k = n*BZ + tz;
|
||||
if (k >= N) { return; }
|
||||
d_body(k);
|
||||
});
|
||||
});
|
||||
});
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
void RajaCuWrap3D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
@@ -357,11 +390,28 @@ void RajaCuWrap3D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim>
|
||||
template <int LB, typename DBODY>
|
||||
void RajaCuWrap3DLaunchBounds(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
const int GRID = G == 0 ? N : G;
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<cuda_launch_bounds_policy<LB> >
|
||||
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body);
|
||||
});
|
||||
MFEM_GPU_CHECK(cudaGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim, int MAX_THREADS_PER_BLOCK>
|
||||
struct RajaCuWrap;
|
||||
|
||||
template <>
|
||||
struct RajaCuWrap<1>
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct RajaCuWrap<1, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
@@ -372,7 +422,7 @@ struct RajaCuWrap<1>
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaCuWrap<2>
|
||||
struct RajaCuWrap<2, 0>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
@@ -382,8 +432,19 @@ struct RajaCuWrap<2>
|
||||
}
|
||||
};
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct RajaCuWrap<2, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaCuWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaCuWrap<3>
|
||||
struct RajaCuWrap<3, 0>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
@@ -393,6 +454,17 @@ struct RajaCuWrap<3>
|
||||
}
|
||||
};
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct RajaCuWrap<3, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaCuWrap3DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP) && defined(__HIP__)
|
||||
@@ -438,6 +510,33 @@ void RajaHipWrap2D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <int LB, typename DBODY>
|
||||
void RajaHipWrap2DLaunchBounds(const int N, DBODY &&d_body, const int X,
|
||||
const int Y, const int BZ)
|
||||
{
|
||||
MFEM_VERIFY(BZ>0, "");
|
||||
const int G = (N+BZ-1)/BZ;
|
||||
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<hip_launch_bounds_policy<LB> >
|
||||
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
loop<hip_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
|
||||
{
|
||||
loop<hip_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
|
||||
{
|
||||
const int k = n*BZ + tz;
|
||||
if (k >= N) { return; }
|
||||
d_body(k);
|
||||
});
|
||||
});
|
||||
});
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <typename DBODY>
|
||||
void RajaHipWrap3D(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
@@ -458,11 +557,28 @@ void RajaHipWrap3D(const int N, DBODY &&d_body,
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim>
|
||||
template <int LB, typename DBODY>
|
||||
void RajaHipWrap3DLaunchBounds(const int N, DBODY &&d_body, const int X,
|
||||
const int Y, const int Z, const int G)
|
||||
{
|
||||
const int GRID = G == 0 ? N : G;
|
||||
using namespace RAJA;
|
||||
using RAJA::RangeSegment;
|
||||
|
||||
launch<hip_launch_bounds_policy<LB> >
|
||||
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
|
||||
[=] RAJA_DEVICE(LaunchContext ctx)
|
||||
{
|
||||
loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body);
|
||||
});
|
||||
MFEM_GPU_CHECK(hipGetLastError());
|
||||
}
|
||||
|
||||
template <int Dim, int MAX_THREADS_PER_BLOCK>
|
||||
struct RajaHipWrap;
|
||||
|
||||
template <>
|
||||
struct RajaHipWrap<1>
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct RajaHipWrap<1, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
@@ -473,7 +589,7 @@ struct RajaHipWrap<1>
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaHipWrap<2>
|
||||
struct RajaHipWrap<2, 0>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
@@ -483,8 +599,19 @@ struct RajaHipWrap<2>
|
||||
}
|
||||
};
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct RajaHipWrap<2, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaHipWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct RajaHipWrap<3>
|
||||
struct RajaHipWrap<3, 0>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
@@ -494,6 +621,17 @@ struct RajaHipWrap<3>
|
||||
}
|
||||
};
|
||||
|
||||
template <int MAX_THREADS_PER_BLOCK>
|
||||
struct RajaHipWrap<3, MAX_THREADS_PER_BLOCK>
|
||||
{
|
||||
template <typename DBODY>
|
||||
static void run(const int N, DBODY &&d_body,
|
||||
const int X, const int Y, const int Z, const int G)
|
||||
{
|
||||
RajaHipWrap3DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
/// RAJA OpenMP backend
|
||||
@@ -913,7 +1051,7 @@ inline void ForallWrap(const bool use_dev, const int N,
|
||||
// If Backend::RAJA_CUDA is allowed, use it
|
||||
if (Device::Allows(Backend::RAJA_CUDA))
|
||||
{
|
||||
return RajaCuWrap<DIM>::run(N, d_body, X, Y, Z, G);
|
||||
return RajaCuWrap<DIM, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -921,7 +1059,7 @@ inline void ForallWrap(const bool use_dev, const int N,
|
||||
// If Backend::RAJA_HIP is allowed, use it
|
||||
if (Device::Allows(Backend::RAJA_HIP))
|
||||
{
|
||||
return RajaHipWrap<DIM>::run(N, d_body, X, Y, Z, G);
|
||||
return RajaHipWrap<DIM, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1090,6 +1228,12 @@ inline void forall_2D_batch(int N, int X, int Y, int BZ, lambda &&body)
|
||||
ForallWrap<2>(true, N, body, X, Y, BZ);
|
||||
}
|
||||
|
||||
template<int MAX_THREADS_PER_BLOCK, typename lambda>
|
||||
inline void forall_2D_batch(int N, int X, int Y, int BZ, lambda &&body)
|
||||
{
|
||||
ForallWrap<2, MAX_THREADS_PER_BLOCK>(true, N, body, X, Y, BZ);
|
||||
}
|
||||
|
||||
template<typename lambda>
|
||||
inline void forall_3D(int N, int X, int Y, int Z, lambda &&body)
|
||||
{
|
||||
|
||||
@@ -148,6 +148,12 @@ if (MFEM_USE_MKL_PARDISO)
|
||||
list(APPEND HDRS pardiso.hpp)
|
||||
endif()
|
||||
|
||||
# cudss solver
|
||||
if (MFEM_USE_CUDSS)
|
||||
list(APPEND SRCS cudss.cpp)
|
||||
list(APPEND HDRS cudss.hpp)
|
||||
endif()
|
||||
|
||||
convert_filenames_to_full_paths(SRCS)
|
||||
convert_filenames_to_full_paths(HDRS)
|
||||
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "cudss.hpp"
|
||||
#include "../general/communication.hpp"
|
||||
#include <string>
|
||||
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
|
||||
#ifdef MFEM_USE_SINGLE
|
||||
#define CUDA_REAL_T CUDA_R_32F
|
||||
#else
|
||||
#define CUDA_REAL_T CUDA_R_64F
|
||||
#endif
|
||||
|
||||
// Define a cuDSS error check macro, MFEM_CUDSS_CHECK(x), where x returns/is of
|
||||
// type 'cudssStatus_t'. This macro evaluates 'x' and raises an error if the
|
||||
// result is not CUDSS_STATUS_SUCCESS.
|
||||
#define MFEM_CUDSS_CHECK(x) \
|
||||
do { \
|
||||
cudssStatus_t mfem_err_internal_var_name = (x); \
|
||||
if (mfem_err_internal_var_name != CUDSS_STATUS_SUCCESS) { \
|
||||
::mfem::mfem_cudss_error(mfem_err_internal_var_name, #x, \
|
||||
_MFEM_FUNC_NAME, __FILE__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
// Function used by the macro MFEM_CUDSS_CHECK.
|
||||
void mfem_cudss_error(cudssStatus_t status, const char *expr, const char *func,
|
||||
const char *file, int line)
|
||||
{
|
||||
mfem::err << "\n\nCUDSS error: (" << expr << ") failed with error:\n --> "
|
||||
<< "CUDSS call ended unsuccessfully"
|
||||
<< " [code: " << static_cast<int>(status) << ']'
|
||||
<< "\n ... in function: " << func << "\n ... in file: " << file
|
||||
<< ':' << line << '\n';
|
||||
mfem_error();
|
||||
}
|
||||
|
||||
CuDSSSolver::CuDSSSolver() { InitCuDSS(); }
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
CuDSSSolver::CuDSSSolver(MPI_Comm comm_) : mpi_comm(comm_)
|
||||
{
|
||||
InitCuDSS();
|
||||
|
||||
// NOTE: Set the communication layer to NULL so that cuDSS picks it
|
||||
// from the environment variable "CUDSS_COMM_LIB"
|
||||
const char* comm_lib = GetEnv("CUDSS_COMM_LIB");
|
||||
#ifdef MFEM_CUDSS_COMM_LIB
|
||||
if (comm_lib == nullptr)
|
||||
{
|
||||
comm_lib = MFEM_CUDSS_COMM_LIB;
|
||||
}
|
||||
#endif
|
||||
MFEM_CUDSS_CHECK(cudssSetCommLayer(handle, comm_lib));
|
||||
|
||||
MFEM_CUDSS_CHECK(cudssDataSet(handle, solverData, CUDSS_DATA_COMM,
|
||||
&mpi_comm, sizeof(MPI_Comm *)));
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
CuDSSSolver::~CuDSSSolver()
|
||||
{
|
||||
// Destroy the system Matrix, RHS vector and solution vector
|
||||
if (Ac)
|
||||
{
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(xc));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(yc));
|
||||
}
|
||||
|
||||
// Destroy the cuDSS handle, solver config and solver data
|
||||
MFEM_CUDSS_CHECK(cudssDataDestroy(handle, solverData));
|
||||
MFEM_CUDSS_CHECK(cudssConfigDestroy(solverConfig));
|
||||
|
||||
|
||||
MFEM_CUDSS_CHECK(cudssDestroy(handle));
|
||||
handle = nullptr;
|
||||
|
||||
|
||||
if (csr_offsets_d != NULL)
|
||||
{
|
||||
CuMemFree(csr_offsets_d);
|
||||
}
|
||||
|
||||
if (csr_columns_d != NULL)
|
||||
{
|
||||
CuMemFree(csr_columns_d);
|
||||
}
|
||||
|
||||
if (csr_values_d != NULL)
|
||||
{
|
||||
CuMemFree(csr_values_d);
|
||||
}
|
||||
}
|
||||
|
||||
void CuDSSSolver::InitCuDSS()
|
||||
{
|
||||
// Create the cuDSS handle
|
||||
MFEM_CUDSS_CHECK(cudssCreate(&handle));
|
||||
|
||||
#ifdef MFEM_USE_OPENMP
|
||||
// NOTE: Set the threading layer library name to NULL so that cuDSS picks
|
||||
// it from the environment variable "CUDSS_THREADING_LIB"
|
||||
const char* threading_lib = GetEnv("CUDSS_THREADING_LIB");
|
||||
#ifdef MFEM_CUDSS_THREADING_LIB
|
||||
if (threading_lib == nullptr)
|
||||
{
|
||||
threading_lib = MFEM_CUDSS_THREADING_LIB;
|
||||
}
|
||||
#endif
|
||||
MFEM_CUDSS_CHECK(cudssSetThreadingLayer(handle, threading_lib));
|
||||
#endif // MFEM_USE_OPENMP
|
||||
|
||||
// Create the solver configuration and data objects
|
||||
MFEM_CUDSS_CHECK(cudssConfigCreate(&solverConfig));
|
||||
MFEM_CUDSS_CHECK(cudssDataCreate(handle, &solverData));
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetMatrixSymType(MatType mtype_)
|
||||
{
|
||||
switch (mtype_)
|
||||
{
|
||||
case MatType::SYMMETRIC_INDEFINITE:
|
||||
mat_type = CUDSS_MTYPE_SYMMETRIC;
|
||||
break;
|
||||
case MatType::SYMMETRIC_POSITIVE_DEFINITE:
|
||||
mat_type = CUDSS_MTYPE_SPD;
|
||||
break;
|
||||
default:
|
||||
mat_type = CUDSS_MTYPE_GENERAL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetMatrixViewType(MatViewType mvtype_)
|
||||
{
|
||||
// If the MatType is NONSYMMETRIC, the matrix view type must be FULL.
|
||||
if (mat_type == CUDSS_MTYPE_GENERAL)
|
||||
{
|
||||
mview = CUDSS_MVIEW_FULL;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the matrix is symmetric, the following view type will be optional.
|
||||
switch (mvtype_)
|
||||
{
|
||||
case MatViewType::LOWER:
|
||||
mview = CUDSS_MVIEW_LOWER;
|
||||
break;
|
||||
case MatViewType::UPPER:
|
||||
mview = CUDSS_MVIEW_UPPER;
|
||||
break;
|
||||
default:
|
||||
mview = CUDSS_MVIEW_FULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetReorderingReuse(bool reuse)
|
||||
{
|
||||
MFEM_VERIFY(Ac == nullptr,
|
||||
"Set reordering reuse before setting the operator!");
|
||||
reorder_reuse = reuse;
|
||||
}
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
void CuDSSSolver::SetMatrix(const HypreParMatrix &op)
|
||||
{
|
||||
bool cuDSSObjectInitialized = (Ac != nullptr);
|
||||
|
||||
hypre_ParCSRMatrix *parcsr_op = op;
|
||||
op.HypreRead();
|
||||
hypre_CSRMatrix *csr_op = hypre_MergeDiagAndOffd(parcsr_op);
|
||||
op.HypreRead();
|
||||
#if MFEM_HYPRE_VERSION >= 21600
|
||||
hypre_CSRMatrixBigJtoJ(csr_op);
|
||||
#endif
|
||||
|
||||
// Parameters of the Operator
|
||||
n_loc = height; // Equal to the csr_op->num_rows
|
||||
n_global = internal::to_int(parcsr_op->global_num_rows);
|
||||
row_start = parcsr_op->first_row_index;
|
||||
row_end = row_start + n_loc - 1;
|
||||
MFEM_VERIFY(!cuDSSObjectInitialized || !reorder_reuse ||
|
||||
(reorder_reuse && (nnz == csr_op->num_nonzeros)),
|
||||
"Inconsistent new matrix pattern!");
|
||||
nnz = csr_op->num_nonzeros;
|
||||
|
||||
SetMatrixCuDSS(csr_op->i, csr_op->j, csr_op->data);
|
||||
hypre_CSRMatrixDestroy(csr_op);
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
void CuDSSSolver::SetMatrix(const SparseMatrix &op)
|
||||
{
|
||||
bool cuDSSObjectInitialized = (Ac != nullptr);
|
||||
|
||||
// Parameters of the Operator
|
||||
MFEM_VERIFY(!cuDSSObjectInitialized || !reorder_reuse ||
|
||||
(reorder_reuse && (nnz == op.NumNonZeroElems())),
|
||||
"Inconsistent new matrix pattern!");
|
||||
|
||||
SparseMatrix *A = const_cast<SparseMatrix *>(&op);
|
||||
|
||||
nnz = A->NumNonZeroElems();
|
||||
n_global = height; // Equal to the height in serial
|
||||
n_loc = height; // Equal to the height in serial
|
||||
|
||||
int *csr_offsets = const_cast<int *>(A->ReadI());
|
||||
int *csr_columns = const_cast<int *>(A->ReadJ());
|
||||
real_t *csr_values = const_cast<real_t *>(A->ReadData());
|
||||
|
||||
SetMatrixCuDSS(csr_offsets, csr_columns, csr_values);
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetMatrixCuDSS(int *csr_offsets, int *csr_columns,
|
||||
real_t *csr_values)
|
||||
{
|
||||
bool cuDSSObjectInitialized = (Ac != nullptr);
|
||||
// Initial the cudssMatrix objects
|
||||
if (!cuDSSObjectInitialized)
|
||||
{
|
||||
// Set the cudssMatrix object of csr operator
|
||||
Ac = std::make_unique<cudssMatrix_t>();
|
||||
// Create empty RHS and solution vectors
|
||||
SetNumRHS(1);
|
||||
// Allocate device memory for csr values
|
||||
CuMemAlloc(&csr_values_d, nnz * sizeof(real_t));
|
||||
}
|
||||
|
||||
CuMemcpyDtoD(csr_values_d, csr_values, nnz * sizeof(real_t));
|
||||
|
||||
// New cuDSS CSR matrix object and analysis or reuse the one from a previous
|
||||
// matrix
|
||||
if (!cuDSSObjectInitialized || !reorder_reuse)
|
||||
{
|
||||
if (reorder_reuse) // !cuDSSObjectInitialized && reorder_reuse
|
||||
{
|
||||
// NOTE: For CuDSS solver to reuse the reordering (skipping analysis
|
||||
// phase), it needs to access the I and J arrays of the **initial**
|
||||
// matrix. Therefore, we need to copy and keep I and J in device memory.
|
||||
CuMemAlloc(&csr_offsets_d, (n_loc + 1) * sizeof(int));
|
||||
CuMemAlloc(&csr_columns_d, nnz * sizeof(int));
|
||||
|
||||
CuMemcpyDtoD(csr_offsets_d, csr_offsets, (n_loc + 1) * sizeof(int));
|
||||
CuMemcpyDtoD(csr_columns_d, csr_columns, nnz * sizeof(int));
|
||||
|
||||
MFEM_CUDSS_CHECK(
|
||||
cudssMatrixCreateCsr(
|
||||
Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
|
||||
csr_columns_d, csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
|
||||
CUDSS_BASE_ZERO));
|
||||
}
|
||||
else // !reorder_reuse
|
||||
{
|
||||
if (cuDSSObjectInitialized)
|
||||
{
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
|
||||
}
|
||||
MFEM_CUDSS_CHECK(
|
||||
cudssMatrixCreateCsr(
|
||||
Ac.get(), n_global, n_global, nnz, csr_offsets, NULL, csr_columns,
|
||||
csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
|
||||
CUDSS_BASE_ZERO));
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
if (Mpi::IsInitialized())
|
||||
{
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(*Ac, row_start, row_end));
|
||||
}
|
||||
#endif
|
||||
// Analysis
|
||||
MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_ANALYSIS, solverConfig,
|
||||
solverData, *Ac, yc, xc));
|
||||
}
|
||||
else // cuDSSObjectInitialized && reorder_reuse
|
||||
{
|
||||
// NOTE: When reusing analysis result, we only update the Data array,
|
||||
// without changing the I and J arrays.
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetValues(*Ac, csr_values_d));
|
||||
}
|
||||
|
||||
// Factorization
|
||||
MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_FACTORIZATION, solverConfig,
|
||||
solverData, *Ac, yc, xc));
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetOperator(const Operator &op)
|
||||
{
|
||||
bool cuDSSObjectInitialized = (Ac != nullptr);
|
||||
MFEM_VERIFY(
|
||||
!cuDSSObjectInitialized || (height == op.Height() && width == op.Width()),
|
||||
"Inconsistent new matrix size!");
|
||||
height = op.Height();
|
||||
width = op.Width();
|
||||
if (const SparseMatrix *A = dynamic_cast<const SparseMatrix *>(&op))
|
||||
{
|
||||
SetMatrix(*A);
|
||||
}
|
||||
#ifdef MFEM_USE_MPI
|
||||
else if (const HypreParMatrix *A =
|
||||
dynamic_cast<const HypreParMatrix *>(&op))
|
||||
{
|
||||
SetMatrix(*A);
|
||||
}
|
||||
#endif // MFEM_USE_MPI
|
||||
else
|
||||
{
|
||||
MFEM_ABORT("Unsupported Operator Type \n");
|
||||
}
|
||||
}
|
||||
|
||||
void CuDSSSolver::SetNumRHS(int nrhs_) const
|
||||
{
|
||||
if (nrhs != nrhs_)
|
||||
{
|
||||
if (nrhs > 0)
|
||||
{
|
||||
// Destroy the previous RHS vector and solution vector
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(xc));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixDestroy(yc));
|
||||
}
|
||||
// Create empty RHS and solution vectors
|
||||
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&xc, n_global, nrhs_, n_global, NULL,
|
||||
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
|
||||
|
||||
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&yc, n_global, nrhs_, n_global, NULL,
|
||||
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(xc, row_start, row_end));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(yc, row_start, row_end));
|
||||
#endif // MFEM_USE_MPI
|
||||
}
|
||||
nrhs = nrhs_;
|
||||
}
|
||||
|
||||
void CuDSSSolver::Mult(const Vector &x, Vector &y) const
|
||||
{
|
||||
Array<const Vector *> X(1);
|
||||
Array<Vector *> Y(1);
|
||||
X[0] = &x;
|
||||
Y[0] = &y;
|
||||
ArrayMult(X, Y);
|
||||
}
|
||||
|
||||
void CuDSSSolver::ArrayMult(const Array<const Vector *> &X,
|
||||
Array<Vector *> &Y) const
|
||||
{
|
||||
SetNumRHS(X.Size());
|
||||
|
||||
Vector RHS, SOL;
|
||||
|
||||
if (nrhs == 1)
|
||||
{
|
||||
RHS.MakeRef(*(const_cast<Vector *>(X[0])), 0, X[0]->Size());
|
||||
SOL.MakeRef(*Y[0], 0, Y[0]->Size());
|
||||
}
|
||||
else
|
||||
{
|
||||
// NOTE: RHS must have **global** num_rows and nrhs columns
|
||||
RHS.SetSize(nrhs * n_global, *X[0]);
|
||||
for (int i = 0; i < nrhs; i++)
|
||||
{
|
||||
Vector s(RHS, i * n_global, n_loc);
|
||||
s = *X[i];
|
||||
}
|
||||
|
||||
// NOTE: SOL must have **global** num_rows and nrhs columns
|
||||
SOL.SetSize(nrhs * n_global, *Y[0]);
|
||||
}
|
||||
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetValues(xc, const_cast<real_t *>(RHS.Read())));
|
||||
MFEM_CUDSS_CHECK(cudssMatrixSetValues(yc, SOL.Write()));
|
||||
|
||||
// Solve
|
||||
MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_SOLVE, solverConfig,
|
||||
solverData, *Ac, yc, xc));
|
||||
|
||||
if (nrhs == 1)
|
||||
{
|
||||
SOL.SyncAliasMemory(*Y[0]);
|
||||
}
|
||||
|
||||
if (nrhs > 1)
|
||||
{
|
||||
// Get solution for each right-hand side
|
||||
for (int i = 0; i < nrhs; i++)
|
||||
{
|
||||
Vector s(SOL, i * n_global, n_loc);
|
||||
*Y[i] = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
#endif // MFEM_USE_CUDSS
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_CUDSS
|
||||
#define MFEM_CUDSS
|
||||
|
||||
#include "../config/config.hpp"
|
||||
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
|
||||
#include "cudss.h"
|
||||
#include <memory>
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include <mpi.h>
|
||||
#include "hypre.hpp"
|
||||
#else
|
||||
#include "operator.hpp"
|
||||
#include "sparsemat.hpp"
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
/**
|
||||
* @brief cuDSS: A high-performance CUDA Library for Direct Sparse Solvers
|
||||
*
|
||||
* Interface for the distributed cuDSS solver
|
||||
*/
|
||||
class CuDSSSolver : public Solver
|
||||
{
|
||||
public:
|
||||
/// Specify the type of matrix we are applying the solver to
|
||||
enum MatType
|
||||
{
|
||||
/// CUDSS_MTYPE_GENERAL: General matrix [default].
|
||||
NONSYMMETRIC = 0,
|
||||
/// CUDSS_MTYPE_SYMMETRIC: Real symmetric matrix.
|
||||
SYMMETRIC_INDEFINITE = 1,
|
||||
/// CUDSS_MTYPE_SPD: Symmetric positive-definite matrix.
|
||||
SYMMETRIC_POSITIVE_DEFINITE = 2,
|
||||
};
|
||||
|
||||
/// Specify the view type of matrix we are applying the solver to
|
||||
enum MatViewType
|
||||
{
|
||||
/// CUDSS_MVIEW_FULL: Full matrix [default]
|
||||
FULL = 0,
|
||||
/// CUDSS_MVIEW_LOWER: Lower-triangular matrix (including the diagonal).
|
||||
LOWER = 1,
|
||||
/// CUDSS_MVIEW_UPPER: Upper-triangular matrix (including the diagonal).
|
||||
UPPER = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Constructor.
|
||||
*/
|
||||
CuDSSSolver();
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/**
|
||||
* @brief Constructor with MPI_Comm parameter.
|
||||
*/
|
||||
CuDSSSolver(MPI_Comm comm);
|
||||
#endif
|
||||
|
||||
// Note: CuDSSSolver disables the move copy constructor and move assignment
|
||||
// operator
|
||||
CuDSSSolver(CuDSSSolver &&) = delete;
|
||||
CuDSSSolver &operator=(CuDSSSolver &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Set the matrix type
|
||||
*
|
||||
* Supported matrix types:
|
||||
* CuDSSSolver::NONSYMMETRIC,
|
||||
* CuDSSSolver::SYMMETRIC_INDEFINITE,
|
||||
* and CuDSSSolver::SYMMETRIC_POSITIVE_DEFINITE
|
||||
*
|
||||
* @param mtype_ Matrix type
|
||||
*
|
||||
* @note This method has to be called before SetOperator
|
||||
*/
|
||||
void SetMatrixSymType(MatType mtype_);
|
||||
|
||||
/**
|
||||
* @brief Set the matrix view type
|
||||
*
|
||||
* Supported matrix types:
|
||||
* CuDSSSolver::FULL,
|
||||
* CuDSSSolver::LOWER,
|
||||
* and CuDSSSolver::UPPER
|
||||
*
|
||||
* @param mvtype Matrix view type
|
||||
*
|
||||
* @note This method has to be called before SetOperator
|
||||
*/
|
||||
void SetMatrixViewType(MatViewType mvtype);
|
||||
|
||||
/**
|
||||
* @brief Set the flag controlling reuse of the symbolic factorization
|
||||
* for multiple operators
|
||||
*
|
||||
* @param reuse Flag to reuse symbolic factorization
|
||||
*
|
||||
* @note This method has to be called before repeated calls to SetOperator
|
||||
*/
|
||||
void SetReorderingReuse(bool reuse);
|
||||
|
||||
void SetOperator(const Operator &op) override;
|
||||
|
||||
/**
|
||||
* @brief Solve $ y = Op^{-1} x $
|
||||
*
|
||||
* @param x RHS vector
|
||||
* @param y Solution vector
|
||||
*/
|
||||
void Mult(const Vector &x, Vector &y) const override;
|
||||
|
||||
/**
|
||||
* @brief Solve $ Y_i = Op^{-1} X_i $
|
||||
*
|
||||
* @param X Array of RHS vectors
|
||||
* @param Y Array of Solution vectors
|
||||
*/
|
||||
void ArrayMult(const Array<const Vector *> &X,
|
||||
Array<Vector *> &Y) const override;
|
||||
|
||||
~CuDSSSolver();
|
||||
|
||||
private:
|
||||
#ifdef MFEM_USE_MPI
|
||||
// MPI_Comm
|
||||
MPI_Comm mpi_comm = MPI_COMM_NULL;
|
||||
|
||||
int row_start = 0; // the first row index in CSR matrix operator
|
||||
int row_end = 0; // the end row index in CSR matrix operator
|
||||
#endif
|
||||
|
||||
// Parameter controlling whether or not to reuse the symbolic factorization
|
||||
// for multiple calls to SetOperator
|
||||
bool reorder_reuse = false;
|
||||
|
||||
// Parameter controlling the matrix type
|
||||
cudssMatrixType_t mat_type = CUDSS_MTYPE_GENERAL;
|
||||
|
||||
int n_global = 0; // global number of rows
|
||||
int n_loc = 0; // the number of the rows in CSR matrix operator
|
||||
|
||||
mutable int nrhs = 0; // the number of the RHSs
|
||||
int nnz = 0; // the number of non zeros
|
||||
|
||||
// copy and keep the I and J arrays in device memory when skipping analysis
|
||||
// phase
|
||||
void *csr_offsets_d = NULL; // copy and keep I in device
|
||||
void *csr_columns_d = NULL; // copy and keep J in device
|
||||
void *csr_values_d = NULL; // copy and keep csr data in device
|
||||
|
||||
// cuDSS object specifies available matrix types for sparse matrices
|
||||
cudssMatrixViewType_t mview = CUDSS_MVIEW_FULL;
|
||||
|
||||
// cuDSS objects storage for sparse matrix Ac, RHS yc and solution xc
|
||||
std::unique_ptr<cudssMatrix_t> Ac;
|
||||
mutable cudssMatrix_t xc, yc;
|
||||
|
||||
// common for all cuDSS solver instances.
|
||||
// cuDSS object holds the cuDSS library context
|
||||
cudssHandle_t handle;
|
||||
|
||||
// cuDSS object stores configuration settings for the solver
|
||||
mutable cudssConfig_t solverConfig;
|
||||
// cuDSS object holds internal data
|
||||
mutable cudssData_t solverData;
|
||||
|
||||
/// Method for configuring storage for distributed/centralized RHS and
|
||||
/// solution
|
||||
void SetNumRHS(int nrhs_) const;
|
||||
|
||||
#ifdef MFEM_USE_MPI
|
||||
/**
|
||||
* @brief Set the HypreParMatrix object
|
||||
*
|
||||
* @param op HypreParMatrix object
|
||||
*
|
||||
* @note This method is called inside SetOperator
|
||||
*/
|
||||
void SetMatrix(const HypreParMatrix &op);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Set the SparseMatrix object
|
||||
*
|
||||
* @param op SparseMatrix object
|
||||
*
|
||||
* @note This method is called inside SetOperator
|
||||
*/
|
||||
void SetMatrix(const SparseMatrix &op);
|
||||
|
||||
/**
|
||||
* @brief Set the matrix values for cuDSS
|
||||
*
|
||||
* @param csr_offsets Row offsets of the CSR matrix
|
||||
* @param csr_columns Column indices of the CSR matrix
|
||||
* @param csr_values Non-zero values of the CSR matrix
|
||||
*
|
||||
* @note This method is called inside SetMatrix.
|
||||
*/
|
||||
void SetMatrixCuDSS(int* csr_offsets, int* csr_columns, real_t* csr_values);
|
||||
|
||||
/// Method for initializing the cuDSS library and creating the cuDSS handle
|
||||
void InitCuDSS();
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_USE_CUDSS
|
||||
#endif // MFEM_CUDSS
|
||||
+4
-1
@@ -5511,9 +5511,12 @@ void HypreBoomerAMG::SetElasticityOptions(ParFiniteElementSpace *fespace_,
|
||||
// Save the finite element space to support multiple calls to SetOperator()
|
||||
this->fespace = fespace_;
|
||||
|
||||
MFEM_VERIFY(fespace->GetOrdering() == Ordering::byVDIM,
|
||||
"The elasticity version of BoomerAMG requires Ordering::byVDIM");
|
||||
|
||||
// Make sure the systems AMG options are set
|
||||
int dim = fespace_->GetParMesh()->Dimension();
|
||||
SetSystemsOptions(dim, fespace->GetOrdering() == Ordering::byNODES);
|
||||
SetSystemsOptions(dim); // elasticity solver only works for Ordering::byVDIM
|
||||
|
||||
// Nodal coarsening options (nodal coarsening is required for this solver)
|
||||
// See hypre's new_ij driver and the paper for descriptions.
|
||||
|
||||
+5
-2
@@ -1860,8 +1860,11 @@ public:
|
||||
geometric rigid body modes and could perform better on some problems, see
|
||||
"Improving algebraic multigrid interpolation operators for linear
|
||||
elasticity problems", Baker, Kolev, Yang, NLAA 2009, DOI:10.1002/nla.688.
|
||||
The optional argument @ interp_refine is used to enable/disable pre-processing
|
||||
of the interpolation matrix through iterative weight refinement */
|
||||
The optional argument @a interp_refine is used to enable/disable internal
|
||||
pre-processing of the interpolation matrix through iterative weight
|
||||
refinement, which could perform better but is more expensive.
|
||||
@warning This solver assumes Ordering::byVDIM in the FiniteElementSpace
|
||||
used to construct A.*/
|
||||
void SetElasticityOptions(ParFiniteElementSpace *fespace,
|
||||
bool interp_refine = true);
|
||||
|
||||
|
||||
@@ -91,4 +91,8 @@
|
||||
|
||||
#endif // MFEM_USE_MPI
|
||||
|
||||
#ifdef MFEM_USE_CUDSS
|
||||
#include "cudss.hpp"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -738,6 +738,13 @@ void ConstrainedOperator::AddMult(const Vector &x, Vector &y,
|
||||
y.Add(a, w);
|
||||
}
|
||||
|
||||
void ConstrainedOperator::AddMultTranspose(const Vector &x, Vector &y,
|
||||
const real_t a) const
|
||||
{
|
||||
MultTranspose(x, w);
|
||||
y.Add(a, w);
|
||||
}
|
||||
|
||||
RectangularConstrainedOperator::RectangularConstrainedOperator(
|
||||
Operator *A,
|
||||
const Array<int> &trial_list,
|
||||
|
||||
@@ -1107,6 +1107,9 @@ public:
|
||||
|
||||
void MultTranspose(const Vector &x, Vector &y) const override;
|
||||
|
||||
void AddMultTranspose(const Vector &x, Vector &y,
|
||||
const real_t a = 1.0) const override;
|
||||
|
||||
void AbsMultTranspose(const Vector &x, Vector &y) const override;
|
||||
|
||||
/** @brief Implementation of Mult or MultTranspose.
|
||||
|
||||
+3
-237
@@ -20,9 +20,7 @@
|
||||
#include "../fem/fem.hpp"
|
||||
|
||||
#include "petsc.h"
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
#include "petscmathypre.h"
|
||||
#endif
|
||||
|
||||
// Backward compatibility
|
||||
#if PETSC_VERSION_LT(3,11,0)
|
||||
@@ -105,14 +103,6 @@ static PetscErrorCode MakeShellPC(PC,mfem::Solver&,bool);
|
||||
static PetscErrorCode MakeShellPCWithFactory(PC,
|
||||
mfem::PetscPreconditionerFactory*);
|
||||
|
||||
// Equivalent functions are present in PETSc source code
|
||||
// if PETSc has been compiled with hypre support
|
||||
// We provide them here in case PETSC_HAVE_HYPRE is not defined
|
||||
#if !defined(PETSC_HAVE_HYPRE)
|
||||
static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix*,Mat*);
|
||||
static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix*,Mat*);
|
||||
#endif
|
||||
|
||||
#if PETSC_VERSION_GE(3,15,0) && defined(PETSC_HAVE_DEVICE)
|
||||
#if defined(MFEM_USE_CUDA) && defined(PETSC_HAVE_CUDA)
|
||||
#define _USE_DEVICE
|
||||
@@ -1149,11 +1139,10 @@ PetscParMatrix& PetscParMatrix::operator=(const HypreParMatrix& B)
|
||||
}
|
||||
height = B.Height();
|
||||
width = B.Width();
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
|
||||
ierr = MatCreateFromParCSR(B,MATAIJ,PETSC_USE_POINTER,&A);
|
||||
#else
|
||||
ierr = MatConvert_hypreParCSR_AIJ(B,&A); CCHKERRQ(B.GetComm(),ierr);
|
||||
#endif
|
||||
CCHKERRQ(B.GetComm(),ierr);
|
||||
|
||||
SetUpForDevice();
|
||||
return *this;
|
||||
}
|
||||
@@ -1529,7 +1518,6 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
}
|
||||
else if (tid == PETSC_MATHYPRE)
|
||||
{
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
if (istrans)
|
||||
{
|
||||
Mat B;
|
||||
@@ -1541,9 +1529,6 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
{
|
||||
ierr = MatConvert(pA->A,MATHYPRE,MAT_INITIAL_MATRIX,A); PCHKERRQ(pA->A,ierr);
|
||||
}
|
||||
#else
|
||||
MFEM_ABORT("Reconfigure PETSc with --download-hypre or --with-hypre")
|
||||
#endif
|
||||
}
|
||||
else if (tid == PETSC_MATSHELL)
|
||||
{
|
||||
@@ -1558,33 +1543,21 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
|
||||
{
|
||||
if (tid == PETSC_MATAIJ)
|
||||
{
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATAIJ,
|
||||
PETSC_USE_POINTER,A);
|
||||
#else
|
||||
ierr = MatConvert_hypreParCSR_AIJ(const_cast<HypreParMatrix&>(*pH),A);
|
||||
#endif
|
||||
CCHKERRQ(pH->GetComm(),ierr);
|
||||
}
|
||||
else if (tid == PETSC_MATIS)
|
||||
{
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATIS,
|
||||
PETSC_USE_POINTER,A);
|
||||
#else
|
||||
ierr = MatConvert_hypreParCSR_IS(const_cast<HypreParMatrix&>(*pH),A);
|
||||
#endif
|
||||
CCHKERRQ(pH->GetComm(),ierr);
|
||||
}
|
||||
else if (tid == PETSC_MATHYPRE || tid == ANY_TYPE)
|
||||
{
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATHYPRE,
|
||||
PETSC_USE_POINTER,A);
|
||||
CCHKERRQ(pH->GetComm(),ierr);
|
||||
#else
|
||||
MFEM_ABORT("Reconfigure PETSc with --download-hypre or --with-hypre")
|
||||
#endif
|
||||
}
|
||||
else if (tid == PETSC_MATSHELL)
|
||||
{
|
||||
@@ -2238,11 +2211,7 @@ PetscParMatrix * RAP(PetscParMatrix *A, PetscParMatrix *P)
|
||||
PetscParMatrix * RAP(HypreParMatrix *hA, PetscParMatrix *P)
|
||||
{
|
||||
PetscParMatrix *out,*A;
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
A = new PetscParMatrix(hA,Operator::PETSC_MATHYPRE);
|
||||
#else
|
||||
A = new PetscParMatrix(hA);
|
||||
#endif
|
||||
out = RAP(P,A,P);
|
||||
delete A;
|
||||
return out;
|
||||
@@ -2349,10 +2318,8 @@ Operator::Type PetscParMatrix::GetType() const
|
||||
if (ok == PETSC_TRUE) { return PETSC_MATSHELL; }
|
||||
ierr = PetscObjectTypeCompare(oA, MATNEST, &ok); PCHKERRQ(A,ierr);
|
||||
if (ok == PETSC_TRUE) { return PETSC_MATNEST; }
|
||||
#if defined(PETSC_HAVE_HYPRE)
|
||||
ierr = PetscObjectTypeCompare(oA, MATHYPRE, &ok); PCHKERRQ(A,ierr);
|
||||
if (ok == PETSC_TRUE) { return PETSC_MATHYPRE; }
|
||||
#endif
|
||||
return PETSC_MATGENERIC;
|
||||
}
|
||||
|
||||
@@ -5527,207 +5494,6 @@ static PetscErrorCode Convert_Vmarks_IS(MPI_Comm comm,
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
#if !defined(PETSC_HAVE_HYPRE)
|
||||
|
||||
#if defined(HYPRE_MIXEDINT)
|
||||
#error "HYPRE_MIXEDINT not supported"
|
||||
#endif
|
||||
|
||||
#include "_hypre_parcsr_mv.h"
|
||||
static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix* hA,Mat* pA)
|
||||
{
|
||||
MPI_Comm comm = hypre_ParCSRMatrixComm(hA);
|
||||
hypre_CSRMatrix *hdiag,*hoffd;
|
||||
PetscScalar *da,*oa,*aptr;
|
||||
PetscInt *dii,*djj,*oii,*ojj,*iptr;
|
||||
PetscInt i,dnnz,onnz,m,n;
|
||||
PetscMPIInt size;
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
hdiag = hypre_ParCSRMatrixDiag(hA);
|
||||
hoffd = hypre_ParCSRMatrixOffd(hA);
|
||||
m = hypre_CSRMatrixNumRows(hdiag);
|
||||
n = hypre_CSRMatrixNumCols(hdiag);
|
||||
dnnz = hypre_CSRMatrixNumNonzeros(hdiag);
|
||||
onnz = hypre_CSRMatrixNumNonzeros(hoffd);
|
||||
ierr = PetscMalloc1(m+1,&dii); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(dnnz,&djj); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(dnnz,&da); CHKERRQ(ierr);
|
||||
ierr = PetscMemcpy(dii,hypre_CSRMatrixI(hdiag),(m+1)*sizeof(PetscInt));
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscMemcpy(djj,hypre_CSRMatrixJ(hdiag),dnnz*sizeof(PetscInt));
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscMemcpy(da,hypre_CSRMatrixData(hdiag),dnnz*sizeof(PetscScalar));
|
||||
CHKERRQ(ierr);
|
||||
iptr = djj;
|
||||
aptr = da;
|
||||
for (i=0; i<m; i++)
|
||||
{
|
||||
PetscInt nc = dii[i+1]-dii[i];
|
||||
ierr = PetscSortIntWithScalarArray(nc,iptr,aptr); CHKERRQ(ierr);
|
||||
iptr += nc;
|
||||
aptr += nc;
|
||||
}
|
||||
ierr = MPI_Comm_size(comm,&size); CHKERRQ(ierr);
|
||||
if (size > 1)
|
||||
{
|
||||
PetscInt *offdj,*coffd;
|
||||
|
||||
ierr = PetscMalloc1(m+1,&oii); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(onnz,&ojj); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(onnz,&oa); CHKERRQ(ierr);
|
||||
ierr = PetscMemcpy(oii,hypre_CSRMatrixI(hoffd),(m+1)*sizeof(PetscInt));
|
||||
CHKERRQ(ierr);
|
||||
offdj = hypre_CSRMatrixJ(hoffd);
|
||||
coffd = hypre_ParCSRMatrixColMapOffd(hA);
|
||||
for (i=0; i<onnz; i++) { ojj[i] = coffd[offdj[i]]; }
|
||||
ierr = PetscMemcpy(oa,hypre_CSRMatrixData(hoffd),onnz*sizeof(PetscScalar));
|
||||
CHKERRQ(ierr);
|
||||
iptr = ojj;
|
||||
aptr = oa;
|
||||
for (i=0; i<m; i++)
|
||||
{
|
||||
PetscInt nc = oii[i+1]-oii[i];
|
||||
ierr = PetscSortIntWithScalarArray(nc,iptr,aptr); CHKERRQ(ierr);
|
||||
iptr += nc;
|
||||
aptr += nc;
|
||||
}
|
||||
ierr = MatCreateMPIAIJWithSplitArrays(comm,m,n,PETSC_DECIDE,PETSC_DECIDE,dii,
|
||||
djj,da,oii,ojj,oa,pA); CHKERRQ(ierr);
|
||||
}
|
||||
else
|
||||
{
|
||||
oii = ojj = NULL;
|
||||
oa = NULL;
|
||||
ierr = MatCreateSeqAIJWithArrays(comm,m,n,dii,djj,da,pA); CHKERRQ(ierr);
|
||||
}
|
||||
/* We are responsible to free the CSR arrays. However, since we can take
|
||||
references of a PetscParMatrix but we cannot take reference of PETSc
|
||||
arrays, we need to create a PetscContainer object to take reference of
|
||||
these arrays in reference objects */
|
||||
void *ptrs[6] = {dii,djj,da,oii,ojj,oa};
|
||||
const char *names[6] = {"_mfem_csr_dii",
|
||||
"_mfem_csr_djj",
|
||||
"_mfem_csr_da",
|
||||
"_mfem_csr_oii",
|
||||
"_mfem_csr_ojj",
|
||||
"_mfem_csr_oa"
|
||||
};
|
||||
for (i=0; i<6; i++)
|
||||
{
|
||||
PetscContainer c;
|
||||
|
||||
ierr = PetscContainerCreate(comm,&c); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)(*pA),names[i],(PetscObject)c);
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscContainerDestroy(&c); CHKERRQ(ierr);
|
||||
}
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
|
||||
static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix* hA,Mat* pA)
|
||||
{
|
||||
Mat lA;
|
||||
ISLocalToGlobalMapping rl2g,cl2g;
|
||||
IS is;
|
||||
hypre_CSRMatrix *hdiag,*hoffd;
|
||||
MPI_Comm comm = hypre_ParCSRMatrixComm(hA);
|
||||
void *ptrs[2];
|
||||
const char *names[2] = {"_mfem_csr_aux",
|
||||
"_mfem_csr_data"
|
||||
};
|
||||
PetscScalar *hdd,*hod,*aa,*data;
|
||||
PetscInt *col_map_offd,*hdi,*hdj,*hoi,*hoj;
|
||||
PetscInt *aux,*ii,*jj;
|
||||
PetscInt cum,dr,dc,oc,str,stc,nnz,i,jd,jo;
|
||||
PetscErrorCode ierr;
|
||||
|
||||
PetscFunctionBeginUser;
|
||||
/* access relevant information in ParCSR */
|
||||
str = hypre_ParCSRMatrixFirstRowIndex(hA);
|
||||
stc = hypre_ParCSRMatrixFirstColDiag(hA);
|
||||
hdiag = hypre_ParCSRMatrixDiag(hA);
|
||||
hoffd = hypre_ParCSRMatrixOffd(hA);
|
||||
dr = hypre_CSRMatrixNumRows(hdiag);
|
||||
dc = hypre_CSRMatrixNumCols(hdiag);
|
||||
nnz = hypre_CSRMatrixNumNonzeros(hdiag);
|
||||
hdi = hypre_CSRMatrixI(hdiag);
|
||||
hdj = hypre_CSRMatrixJ(hdiag);
|
||||
hdd = hypre_CSRMatrixData(hdiag);
|
||||
oc = hypre_CSRMatrixNumCols(hoffd);
|
||||
nnz += hypre_CSRMatrixNumNonzeros(hoffd);
|
||||
hoi = hypre_CSRMatrixI(hoffd);
|
||||
hoj = hypre_CSRMatrixJ(hoffd);
|
||||
hod = hypre_CSRMatrixData(hoffd);
|
||||
|
||||
/* generate l2g maps for rows and cols */
|
||||
ierr = ISCreateStride(comm,dr,str,1,&is); CHKERRQ(ierr);
|
||||
ierr = ISLocalToGlobalMappingCreateIS(is,&rl2g); CHKERRQ(ierr);
|
||||
ierr = ISDestroy(&is); CHKERRQ(ierr);
|
||||
col_map_offd = hypre_ParCSRMatrixColMapOffd(hA);
|
||||
ierr = PetscMalloc1(dc+oc,&aux); CHKERRQ(ierr);
|
||||
for (i=0; i<dc; i++) { aux[i] = i+stc; }
|
||||
for (i=0; i<oc; i++) { aux[i+dc] = col_map_offd[i]; }
|
||||
ierr = ISCreateGeneral(comm,dc+oc,aux,PETSC_OWN_POINTER,&is); CHKERRQ(ierr);
|
||||
ierr = ISLocalToGlobalMappingCreateIS(is,&cl2g); CHKERRQ(ierr);
|
||||
ierr = ISDestroy(&is); CHKERRQ(ierr);
|
||||
|
||||
/* create MATIS object */
|
||||
ierr = MatCreate(comm,pA); CHKERRQ(ierr);
|
||||
ierr = MatSetSizes(*pA,dr,dc,PETSC_DECIDE,PETSC_DECIDE); CHKERRQ(ierr);
|
||||
ierr = MatSetType(*pA,MATIS); CHKERRQ(ierr);
|
||||
ierr = MatSetLocalToGlobalMapping(*pA,rl2g,cl2g); CHKERRQ(ierr);
|
||||
ierr = ISLocalToGlobalMappingDestroy(&rl2g); CHKERRQ(ierr);
|
||||
ierr = ISLocalToGlobalMappingDestroy(&cl2g); CHKERRQ(ierr);
|
||||
|
||||
/* merge local matrices */
|
||||
ierr = PetscMalloc1(nnz+dr+1,&aux); CHKERRQ(ierr);
|
||||
ierr = PetscMalloc1(nnz,&data); CHKERRQ(ierr);
|
||||
ii = aux;
|
||||
jj = aux+dr+1;
|
||||
aa = data;
|
||||
*ii = *(hdi++) + *(hoi++);
|
||||
for (jd=0,jo=0,cum=0; *ii<nnz; cum++)
|
||||
{
|
||||
PetscScalar *aold = aa;
|
||||
PetscInt *jold = jj,nc = jd+jo;
|
||||
for (; jd<*hdi; jd++) { *jj++ = *hdj++; *aa++ = *hdd++; }
|
||||
for (; jo<*hoi; jo++) { *jj++ = *hoj++ + dc; *aa++ = *hod++; }
|
||||
*(++ii) = *(hdi++) + *(hoi++);
|
||||
ierr = PetscSortIntWithScalarArray(jd+jo-nc,jold,aold); CHKERRQ(ierr);
|
||||
}
|
||||
for (; cum<dr; cum++) { *(++ii) = nnz; }
|
||||
ii = aux;
|
||||
jj = aux+dr+1;
|
||||
aa = data;
|
||||
ierr = MatCreateSeqAIJWithArrays(PETSC_COMM_SELF,dr,dc+oc,ii,jj,aa,&lA);
|
||||
CHKERRQ(ierr);
|
||||
ptrs[0] = aux;
|
||||
ptrs[1] = data;
|
||||
for (i=0; i<2; i++)
|
||||
{
|
||||
PetscContainer c;
|
||||
|
||||
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
|
||||
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscObjectCompose((PetscObject)lA,names[i],(PetscObject)c);
|
||||
CHKERRQ(ierr);
|
||||
ierr = PetscContainerDestroy(&c); CHKERRQ(ierr);
|
||||
}
|
||||
ierr = MatISSetLocalMat(*pA,lA); CHKERRQ(ierr);
|
||||
ierr = MatDestroy(&lA); CHKERRQ(ierr);
|
||||
ierr = MatAssemblyBegin(*pA,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
||||
ierr = MatAssemblyEnd(*pA,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
|
||||
PetscFunctionReturn(PETSC_SUCCESS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#include <petsc/private/matimpl.h>
|
||||
|
||||
static PetscErrorCode __mfem_MatCreateDummy(MPI_Comm comm, PetscInt m,
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
#if !defined(PETSC_USE_64BIT_INDICES) && (defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT))
|
||||
#error "Mismatch between HYPRE (64bit) and PETSc (32bit) integer types"
|
||||
#endif
|
||||
#if !defined(PETSC_HAVE_HYPRE)
|
||||
#error "MFEM requires PETSc built with HYPRE support"
|
||||
#endif
|
||||
|
||||
#include "petscversion.h"
|
||||
#if PETSC_VERSION_GE(3,12,0)
|
||||
|
||||
@@ -123,15 +123,20 @@ EXAMPLE_SUBDIRS = amgx caliper ginkgo hiop petsc pumi sundials superlu moonolith
|
||||
EXAMPLE_DIRS := examples $(addprefix examples/,$(EXAMPLE_SUBDIRS))
|
||||
EXAMPLE_TEST_DIRS := examples
|
||||
|
||||
MINIAPP_SUBDIRS = common electromagnetics meshing performance tools \
|
||||
MINIAPP_ALL_SUBDIRS = common electromagnetics meshing performance tools \
|
||||
toys nurbs gslib adjoint solvers shifted mtop parelag tribol autodiff dfem \
|
||||
hooke multidomain dpg hdiv-linear-solver spde diag-smoothers contact \
|
||||
fluids/navier fluids/schrodinger-flow plasma plasma/pic
|
||||
MINIAPP_RECURSIVE_SUBDIRS = plasma/pic
|
||||
MINIAPP_SUBDIRS := $(filter-out \
|
||||
$(MINIAPP_RECURSIVE_SUBDIRS),$(MINIAPP_ALL_SUBDIRS))
|
||||
MINIAPP_ALL_DIRS := $(addprefix miniapps/,$(MINIAPP_ALL_SUBDIRS))
|
||||
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
|
||||
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
|
||||
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
|
||||
toys shifted dpg diag-smoothers fluids/navier plasma plasma/pic)
|
||||
toys gslib shifted dpg diag-smoothers fluids/navier plasma plasma/pic)
|
||||
|
||||
EM_ALL_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_ALL_DIRS)
|
||||
EM_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_DIRS)
|
||||
|
||||
TEST_SUBDIRS = unit
|
||||
@@ -146,7 +151,7 @@ MFEM_BUILD_DIR ?= .
|
||||
BUILD_DIR := $(MFEM_BUILD_DIR)
|
||||
BUILD_REAL_DIR := $(abspath $(BUILD_DIR))
|
||||
ifneq ($(BUILD_REAL_DIR),$(MFEM_REAL_DIR))
|
||||
BUILD_SUBDIRS = $(DIRS) config $(EM_DIRS) doc $(TEST_DIRS)
|
||||
BUILD_SUBDIRS = $(DIRS) config $(EM_ALL_DIRS) doc $(TEST_DIRS)
|
||||
CONFIG_FILE_DEF = -DMFEM_CONFIG_FILE='"$(BUILD_REAL_DIR)/config/_config.hpp"'
|
||||
BLD := $(if $(BUILD_REAL_DIR:$(CURDIR)=),$(BUILD_DIR)/,)
|
||||
$(if $(word 2,$(BLD)),$(error Spaces in BLD = "$(BLD)" are not supported))
|
||||
@@ -302,7 +307,7 @@ endif
|
||||
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS\
|
||||
SUITESPARSE STRUMPACK GINKGO GNUTLS HDF5 NETCDF SLEPC PETSC MPFR PUMI HIOP\
|
||||
GSLIB OCCA CEED RAJA UMPIRE MKL_CPARDISO MKL_PARDISO AMGX MAGMA CALIPER PARELAG\
|
||||
TRIBOL BENCHMARK MOONOLITH ALGOIM
|
||||
TRIBOL BENCHMARK MOONOLITH ALGOIM CUDSS
|
||||
|
||||
|
||||
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
|
||||
@@ -371,7 +376,8 @@ MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
|
||||
MFEM_USE_SIMD MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO MFEM_USE_AMGX\
|
||||
MFEM_USE_MAGMA MFEM_USE_MUMPS MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_CALIPER\
|
||||
MFEM_USE_BENCHMARK MFEM_USE_PARELAG MFEM_USE_TRIBOL MFEM_USE_ALGOIM MFEM_USE_ENZYME\
|
||||
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_SHARED_BUILD MFEM_USE_DOUBLE MFEM_USE_SINGLE
|
||||
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_SHARED_BUILD MFEM_USE_DOUBLE MFEM_USE_SINGLE\
|
||||
MFEM_USE_CUDSS MFEM_CUDSS_COMM_LIB MFEM_CUDSS_THREADING_LIB
|
||||
|
||||
# List of makefile variables that will be written to config.mk:
|
||||
MFEM_CONFIG_VARS = MFEM_CXX MFEM_HOST_CXX MFEM_CPPFLAGS MFEM_CXXFLAGS\
|
||||
@@ -406,7 +412,7 @@ MFEM_INSTALL_DIR = $(abspath $(MFEM_PREFIX))
|
||||
# If we have 'config' target, export variables used by config/makefile
|
||||
ifneq (,$(filter config,$(MAKECMDGOALS)))
|
||||
export $(MFEM_DEFINES) MFEM_DEFINES $(MFEM_CONFIG_VARS) MFEM_CONFIG_VARS
|
||||
export VERBOSE HYPRE_OPT PUMI_DIR MUMPS_OPT GSLIB_OPT
|
||||
export VERBOSE HYPRE_OPT PUMI_DIR MUMPS_OPT GSLIB_OPT CUDSS_OPT
|
||||
endif
|
||||
|
||||
# If we have 'install' target, export variables used by config/makefile
|
||||
@@ -483,10 +489,10 @@ $(OBJECT_FILES): $(BLD)%.o: $(SRC)%.cpp $(CONFIG_MK)
|
||||
|
||||
all: examples miniapps $(TEST_DIRS)
|
||||
|
||||
.PHONY: miniapps $(EM_DIRS) $(TEST_DIRS)
|
||||
.PHONY: miniapps $(EM_ALL_DIRS) $(TEST_DIRS)
|
||||
miniapps: $(MINIAPP_DIRS)
|
||||
$(MINIAPP_USE_COMMON): miniapps/common
|
||||
$(EM_DIRS) $(TEST_DIRS): lib
|
||||
$(EM_ALL_DIRS) $(TEST_DIRS): lib
|
||||
$(MAKE) -C $(BLD)$(@)
|
||||
|
||||
.PHONY: doc
|
||||
@@ -694,7 +700,7 @@ local-config:
|
||||
.PHONY: build-config
|
||||
build-config:
|
||||
for d in $(BUILD_SUBDIRS); do mkdir -p $(BLD)$${d}; done
|
||||
for dir in "" $(addsuffix /,config $(EM_DIRS) doc $(TEST_DIRS)); do \
|
||||
for dir in "" $(addsuffix /,config $(EM_ALL_DIRS) doc $(TEST_DIRS)); do\
|
||||
printf "# Auto-generated file.\n%s\n%s\n" \
|
||||
"MFEM_DIR = $(MFEM_REAL_DIR)" \
|
||||
"include \$$(MFEM_DIR)/$${dir}makefile" \
|
||||
@@ -737,6 +743,7 @@ status info:
|
||||
$(info MFEM_USE_SUPERLU5 = $(MFEM_USE_SUPERLU5))
|
||||
$(info MFEM_USE_MUMPS = $(MFEM_USE_MUMPS))
|
||||
$(info MFEM_USE_STRUMPACK = $(MFEM_USE_STRUMPACK))
|
||||
$(info MFEM_USE_CUDSS = $(MFEM_USE_CUDSS))
|
||||
$(info MFEM_USE_GINKGO = $(MFEM_USE_GINKGO))
|
||||
$(info MFEM_USE_AMGX = $(MFEM_USE_AMGX))
|
||||
$(info MFEM_USE_MAGMA = $(MFEM_USE_MAGMA))
|
||||
@@ -796,13 +803,15 @@ status info:
|
||||
|
||||
ASTYLE = $(ASTYLE_BIN) --options=$(SRC)config/mfem.astylerc
|
||||
ASTYLE_VER = "Artistic Style Version 3.1"
|
||||
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_DIRS) config,$(dir)/*.?pp)
|
||||
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_ALL_DIRS) config,$(dir)/*.?pp)
|
||||
TESTS_SUBDIRS = unit benchmarks convergence mem_manager par-mesh-format
|
||||
UNIT_TESTS_SUBDIRS = general linalg mesh fem miniapps ceed enzyme
|
||||
MINIAPPS_SUBDIRS = dpg/util hooke/operators hooke/preconditioners hooke/materials hooke/kernels
|
||||
UNIT_TESTS_SUBDIRS = general linalg mesh fem miniapps ceed enzyme dfem
|
||||
MINIAPPS_SUBDIRS = dpg/util hooke/operators hooke/preconditioners \
|
||||
hooke/materials hooke/kernels
|
||||
FORMAT_FILES += $(foreach dir,$(TESTS_SUBDIRS),tests/$(dir)/*.?pp)
|
||||
FORMAT_FILES += $(foreach dir,$(UNIT_TESTS_SUBDIRS),tests/unit/$(dir)/*.?pp)
|
||||
FORMAT_FILES += $(foreach dir,$(MINIAPPS_SUBDIRS),miniapps/$(dir)/*.?pp)
|
||||
FORMAT_FILES += config/cmake/config.hpp.in config/config.hpp.in mfem*.hpp
|
||||
FORMAT_EXCLUDE = general/tinyxml2.cpp tests/unit/catch.hpp
|
||||
FORMAT_LIST = $(filter-out $(FORMAT_EXCLUDE),$(wildcard $(FORMAT_FILES)))
|
||||
|
||||
@@ -833,14 +842,29 @@ mfem_check_command = \
|
||||
# Verify the C++ code styling in MFEM and check that std::cout and std::cerr are
|
||||
# not used in the library (use mfem::out and mfem::err instead).
|
||||
style:
|
||||
@echo "Applying C++ code style..."
|
||||
@astyle_version="$$($(ASTYLE_BIN) --version)";\
|
||||
if [ "$$astyle_version" != $(ASTYLE_VER) ]; then\
|
||||
printf "%s\n" "Invalid astyle version: '$$astyle_version'"\
|
||||
"Please use: '"$(ASTYLE_VER)"'";\
|
||||
exit 1;\
|
||||
fi
|
||||
@err_code=0;\
|
||||
@err_code=0; \
|
||||
if command -v git 2>&1 > /dev/null && [ -d $(MFEM_DIR)/.git ]; then \
|
||||
echo "Checking if all git files are selected for formatting ..."; \
|
||||
ls -1 $(FORMAT_FILES) | sort > format-files-make.txt; \
|
||||
git -C $(MFEM_DIR) ls-files '*.[ch]pp*' | sort \
|
||||
> format-files-git.txt; \
|
||||
cat format-files-make.txt format-files-git.txt | sort | uniq \
|
||||
> format-files-make-plus-git.txt; \
|
||||
rm -f format-files-git.txt; \
|
||||
$(call mfem_check_command,\
|
||||
diff format-files-make.txt format-files-make-plus-git.txt | \
|
||||
grep "^> ",\
|
||||
"All git files are selected for formatting",\
|
||||
"The above git files are NOT selected for formatting"); \
|
||||
rm -f format-files-make.txt format-files-make-plus-git.txt; \
|
||||
fi; \
|
||||
echo "Applying C++ code style...";\
|
||||
$(call mfem_check_command,\
|
||||
$(ASTYLE) $(FORMAT_LIST) | grep Formatted,\
|
||||
"No source files were changed",\
|
||||
|
||||
+2
-1
@@ -37,13 +37,13 @@ set(SRCS
|
||||
submesh/ncsubmesh.cpp
|
||||
submesh/submesh_utils.cpp
|
||||
submesh/transfermap.cpp
|
||||
bb_grid_map.cpp
|
||||
)
|
||||
|
||||
set(HDRS
|
||||
attribute_sets.hpp
|
||||
element.hpp
|
||||
face_nbr_geom.hpp
|
||||
gmsh.hpp
|
||||
hexahedron.hpp
|
||||
mesh.hpp
|
||||
mesh_headers.hpp
|
||||
@@ -68,6 +68,7 @@ set(HDRS
|
||||
submesh/submesh_utils.hpp
|
||||
submesh/transfer_category.hpp
|
||||
submesh/transfermap.hpp
|
||||
bb_grid_map.hpp
|
||||
)
|
||||
|
||||
if (MFEM_USE_MPI)
|
||||
|
||||
@@ -0,0 +1,414 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
/* The BBoxTensorGridMap class is adapted from similar functionality in the
|
||||
gslib library. Below is the gslib license and copyright statement:
|
||||
|
||||
Copyright (c) 2008-2024, UCHICAGO ARGONNE, LLC.
|
||||
|
||||
The UChicago Argonne, LLC as Operator of Argonne National
|
||||
Laboratory holds copyright in the Software. The copyright holder
|
||||
reserves all rights except those expressly granted to licensees,
|
||||
and U.S. Government license rights.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the disclaimer below.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the disclaimer (as noted below)
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. Neither the name of ANL nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
UCHICAGO ARGONNE, LLC, THE U.S. DEPARTMENT OF
|
||||
ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "bb_grid_map.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
using namespace std;
|
||||
|
||||
BBoxTensorGridMap::BBoxTensorGridMap(Mesh &mesh, int nx)
|
||||
{
|
||||
GridFunction *nodes = mesh.GetNodes();
|
||||
const int nel = mesh.GetNE();
|
||||
sdim = mesh.SpaceDimension();
|
||||
Vector elmin(nel*sdim), elmax(nel*sdim);
|
||||
elmin = numeric_limits<real_t>::max();
|
||||
elmax = -numeric_limits<real_t>::max();
|
||||
if (!nodes)
|
||||
{
|
||||
Array<int> verts;
|
||||
real_t *coord;
|
||||
// create bounding boxes from vertex coordinates
|
||||
for (int e = 0; e < nel; e++)
|
||||
{
|
||||
mesh.GetElementVertices(e, verts);
|
||||
for (int v = 0; v < verts.Size(); v++)
|
||||
{
|
||||
coord = mesh.GetVertex(verts[v]);
|
||||
for (int d = 0; d < sdim; d++)
|
||||
{
|
||||
elmin(d*nel + e) = min(elmin(d*nel + e), coord[d]);
|
||||
elmax(d*nel + e) = max(elmax(d*nel + e), coord[d]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int nref = 3;
|
||||
nodes->GetElementBounds(elmin, elmax, nref);
|
||||
}
|
||||
Array<int> nx_arr(sdim);
|
||||
nx_arr = nx;
|
||||
Setup(elmin, elmax, nel, nx_arr, false);
|
||||
}
|
||||
|
||||
BBoxTensorGridMap::BBoxTensorGridMap(Vector &elmin,
|
||||
Vector &elmax,
|
||||
int nel,
|
||||
int sdim_,
|
||||
int n,
|
||||
bool by_max_size)
|
||||
{
|
||||
sdim = sdim_;
|
||||
MFEM_VERIFY(0 < sdim && sdim <= 3,
|
||||
"BBoxTensorGridMap only supports spatial dimensions 1, 2, and 3.");
|
||||
if (nel > 0)
|
||||
{
|
||||
MFEM_VERIFY(elmin.Size() == sdim * nel && elmax.Size() == sdim * nel,
|
||||
"Element bounds size must match dim * nel.");
|
||||
}
|
||||
Array<int> nx_arr(sdim);
|
||||
nx_arr = n;
|
||||
Setup(elmin, elmax, nel, nx_arr, by_max_size);
|
||||
}
|
||||
|
||||
BBoxTensorGridMap::BBoxTensorGridMap(Vector &elmin, Vector &elmax,
|
||||
int nel, int sdim_,
|
||||
Array<int> &nx,
|
||||
bool by_max_size)
|
||||
{
|
||||
sdim = sdim_;
|
||||
Setup(elmin, elmax, nel, nx, by_max_size);
|
||||
}
|
||||
|
||||
void BBoxTensorGridMap::Setup(Vector &elmin, Vector &elmax,
|
||||
int nel, Array<int> &nx, bool by_max_size)
|
||||
{
|
||||
MFEM_VERIFY(0 < sdim && sdim <= 3,
|
||||
"BBoxTensorGridMap only supports spatial dimensions 1, 2, and 3.");
|
||||
MFEM_VERIFY(nx.Size() == sdim,
|
||||
"BBoxTensorGridMap requires nx to have the same size as the number of dimensions.");
|
||||
if (nel > 0)
|
||||
{
|
||||
MFEM_VERIFY(elmin.Size() == sdim * nel && elmax.Size() == sdim * nel,
|
||||
"Element bounds size must match dim * nel.");
|
||||
}
|
||||
lmap_bnd_min.SetSize(sdim);
|
||||
lmap_bnd_max.SetSize(sdim);
|
||||
lmap_fac.SetSize(sdim);
|
||||
lmap_nx.SetSize(sdim);
|
||||
lmap_nx = nx;
|
||||
|
||||
if (by_max_size)
|
||||
{
|
||||
MFEM_VERIFY(nx[0] >= 0,
|
||||
"BBoxTensorGridMap requires a nonnegative max-size hint.");
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int d = 0; d < nx.Size(); d++)
|
||||
{
|
||||
MFEM_VERIFY(nx[d] > 0,
|
||||
"BBoxTensorGridMap requires positive number of divisions in each dimension.");
|
||||
}
|
||||
}
|
||||
if (nel == 0)
|
||||
{
|
||||
lmap_bnd_min = 0.0;
|
||||
lmap_bnd_max = 1.0;
|
||||
if (by_max_size) { lmap_nx = 1; }
|
||||
SetGridFac(lmap_fac, lmap_nx, lmap_bnd_min, lmap_bnd_max);
|
||||
|
||||
lmap_nxd = lmap_nx[0];
|
||||
for (int d = 1; d < sdim; d++)
|
||||
{
|
||||
lmap_nxd *= lmap_nx[d];
|
||||
}
|
||||
|
||||
lgrid_map.SetSize(lmap_nxd + 1);
|
||||
lgrid_map = lmap_nxd + 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for (int d = 0; d < sdim; d++)
|
||||
{
|
||||
Vector elmind(elmin.GetData() + d*nel, nel);
|
||||
Vector elmaxd(elmax.GetData() + d*nel, nel);
|
||||
lmap_bnd_min[d] = elmind.Min();
|
||||
lmap_bnd_max[d] = elmaxd.Max();
|
||||
}
|
||||
|
||||
Array<int> elmin_h, elmax_h;
|
||||
unsigned int store_size;
|
||||
if (by_max_size)
|
||||
{
|
||||
int nmax = nx[0];
|
||||
int nlow = 1, nhigh = nmax > nel ? ceil(pow(nmax - nel, 1.0 / sdim)) : 1;
|
||||
int size_low = 2 + nel;
|
||||
int size = 0;
|
||||
while (nhigh - nlow > 1)
|
||||
{
|
||||
int nmid = nlow + (nhigh - nlow) / 2;
|
||||
int nmd = nmid;
|
||||
for (int d = 1; d < sdim; d++)
|
||||
{
|
||||
nmd *= nmid;
|
||||
}
|
||||
lmap_nx = nmid;
|
||||
SetGridFac(lmap_fac, lmap_nx, lmap_bnd_min, lmap_bnd_max);
|
||||
size = nmd + 1 + GetGridCountAndRange(lmap_nx, lmap_fac,
|
||||
lmap_bnd_min, lmap_bnd_max,
|
||||
elmin, elmax,
|
||||
elmin_h, elmax_h);
|
||||
if (size <= nmax) { nlow = nmid; size_low = size; }
|
||||
else { nhigh = nmid; }
|
||||
}
|
||||
lmap_nx = nlow;
|
||||
lmap_nxd = nlow;
|
||||
for (int d = 1; d < sdim; d++)
|
||||
{
|
||||
lmap_nxd *= nlow;
|
||||
}
|
||||
store_size = size_low;
|
||||
SetGridFac(lmap_fac, lmap_nx, lmap_bnd_min, lmap_bnd_max);
|
||||
if (size != size_low)
|
||||
{
|
||||
GetGridCountAndRange(lmap_nx, lmap_fac,
|
||||
lmap_bnd_min, lmap_bnd_max,
|
||||
elmin, elmax,
|
||||
elmin_h, elmax_h);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SetGridFac(lmap_fac, lmap_nx, lmap_bnd_min, lmap_bnd_max);
|
||||
|
||||
lmap_nxd = lmap_nx[0];
|
||||
for (int d = 1; d < sdim; d++)
|
||||
{
|
||||
lmap_nxd *= lmap_nx[d];
|
||||
}
|
||||
|
||||
// Grid cell ranges for each element in each direction
|
||||
store_size = lmap_nxd + 1 + GetGridCountAndRange(lmap_nx, lmap_fac,
|
||||
lmap_bnd_min,
|
||||
lmap_bnd_max,
|
||||
elmin, elmax,
|
||||
elmin_h, elmax_h);
|
||||
}
|
||||
|
||||
lgrid_map.SetSize(store_size);
|
||||
lgrid_map[0] = lmap_nxd + 1;
|
||||
|
||||
Array<unsigned int> grid_el_count(lmap_nxd);
|
||||
grid_el_count = 0;
|
||||
|
||||
for (int e = 0; e < nel; e++)
|
||||
{
|
||||
int klim = sdim < 3 ? 1 : (elmax_h[2*nel+e]-elmin_h[2*nel+e]);
|
||||
int jlim = sdim < 2 ? 1 : (elmax_h[1*nel+e]-elmin_h[1*nel+e]);
|
||||
int ilim = (elmax_h[0*nel+e]-elmin_h[0*nel+e]);
|
||||
for (int k = 0; k < klim; k++)
|
||||
{
|
||||
int koff = sdim < 3 ? 0 :
|
||||
(elmin_h[2*nel + e] + k) * lmap_nx[0] * lmap_nx[1];
|
||||
for (int j = 0; j < jlim; j++)
|
||||
{
|
||||
int joff = sdim < 2 ? 0 : (elmin_h[1*nel + e] + j) * lmap_nx[0];
|
||||
for (int i = 0; i < ilim; i++)
|
||||
{
|
||||
int ioff = elmin_h[e] + i;
|
||||
int idx = ioff + joff + koff;
|
||||
grid_el_count[idx]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned int e = 0; e < lmap_nxd; e++)
|
||||
{
|
||||
lgrid_map[e + 1] = lgrid_map[e] + grid_el_count[e];
|
||||
}
|
||||
|
||||
for (int e = 0; e < nel; e++)
|
||||
{
|
||||
int klim = sdim < 3 ? 1 : (elmax_h[2*nel+e]-elmin_h[2*nel+e]);
|
||||
int jlim = sdim < 2 ? 1 : (elmax_h[1*nel+e]-elmin_h[1*nel+e]);
|
||||
int ilim = (elmax_h[0*nel+e]-elmin_h[0*nel+e]);
|
||||
for (int k = 0; k < klim; k++)
|
||||
{
|
||||
int koff = sdim < 3 ? 0 :
|
||||
(elmin_h[2*nel+e] + k) * lmap_nx[0] * lmap_nx[1];
|
||||
for (int j = 0; j < jlim; j++)
|
||||
{
|
||||
int joff = sdim < 2 ? 0 : (elmin_h[1*nel + e] + j) * lmap_nx[0];
|
||||
for (int i = 0; i < ilim; i++)
|
||||
{
|
||||
int ioff = elmin_h[e] + i;
|
||||
int idx = ioff + joff + koff;
|
||||
lgrid_map[lgrid_map[idx+1]-grid_el_count[idx]]=e;
|
||||
grid_el_count[idx]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Array<int> BBoxTensorGridMap::GridCellToElements(int i) const
|
||||
{
|
||||
MFEM_ASSERT(i >= 0 && (unsigned int)i < lmap_nxd,
|
||||
"Access element " << i << " of local grid with cells = "
|
||||
<< lmap_nxd);
|
||||
int start = lgrid_map[i];
|
||||
int end = lgrid_map[i + 1];
|
||||
Array<int> elements(end - start);
|
||||
for (int j = start; j < end; j++)
|
||||
{
|
||||
elements[j - start] = lgrid_map[j];
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
int BBoxTensorGridMap::GetGridCellFromPoint(Vector &xyz) const
|
||||
{
|
||||
MFEM_ASSERT(xyz.Size() == sdim,
|
||||
"Point must have the same dimension as the grid.");
|
||||
int sum = 0;
|
||||
for (int d = sdim-1; d >= 0; --d)
|
||||
{
|
||||
if (xyz(d) < lmap_bnd_min(d) || xyz(d) > lmap_bnd_max(d))
|
||||
{
|
||||
return -1; // Point is outside the bounds of the grid
|
||||
}
|
||||
sum *= lmap_nx[d];
|
||||
int i = (int)floor((xyz(d) - lmap_bnd_min(d)) * lmap_fac[d]);
|
||||
sum += i < 0 ? 0 : (lmap_nx[d] - 1 < i ? lmap_nx[d] - 1 : i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
Array<int> BBoxTensorGridMap::MapPointToElements(Vector &xyz) const
|
||||
{
|
||||
MFEM_ASSERT(xyz.Size() == sdim,
|
||||
"Point must have the same dimension as the grid.");
|
||||
int cell = GetGridCellFromPoint(xyz);
|
||||
if (cell < 0)
|
||||
{
|
||||
return Array<int>(); // Point is outside the bounds of the tensor grid
|
||||
}
|
||||
return GridCellToElements(cell);
|
||||
}
|
||||
|
||||
void BBoxTensorGridMap::GetGridRange(const int d, const Array<int> &lh_n,
|
||||
const Vector &lh_fac,
|
||||
const Vector &lh_bnd_min,
|
||||
const real_t &xmin, const real_t &xmax,
|
||||
int &imin, int &imax)
|
||||
{
|
||||
// Use a half-open interval [imin, imax) for the covered grid-cell range.
|
||||
// If xmin is exactly on a grid boundary, use the cell on the right/high
|
||||
// side. If xmax is exactly on a grid boundary, stop before the cell on the
|
||||
// right/high side.
|
||||
int i0 = floor( (xmin - lh_bnd_min[d]) * lh_fac[d] );
|
||||
int i1 = ceil ( (xmax - lh_bnd_min[d]) * lh_fac[d] );
|
||||
imin = i0 < 0 ? 0 : i0;
|
||||
imax = i1 < lh_n[d] ? i1 : lh_n[d];
|
||||
if (imax == imin) { ++imax; }
|
||||
}
|
||||
|
||||
void BBoxTensorGridMap::SetGridFac(Vector &lh_fac, const Array<int> &nx,
|
||||
const Vector &lh_bnd_min,
|
||||
const Vector &lh_bnd_max)
|
||||
{
|
||||
int dim = lh_bnd_min.Size();
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
real_t length = lh_bnd_max[d] - lh_bnd_min[d];
|
||||
if (length > 0.0)
|
||||
{
|
||||
lh_fac[d] = nx[d] / length;
|
||||
}
|
||||
else
|
||||
{
|
||||
lh_fac[d] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int BBoxTensorGridMap::GetGridCountAndRange(const Array<int> &lh_n,
|
||||
const Vector &lh_fac,
|
||||
const Vector &lh_bnd_min,
|
||||
const Vector &lh_bnd_max,
|
||||
const Vector &elmin,
|
||||
const Vector &elmax,
|
||||
Array<int> &elmin_h,
|
||||
Array<int> &elmax_h)
|
||||
{
|
||||
int count = 0;
|
||||
const int dim = lh_bnd_min.Size();
|
||||
const int nel = elmin.Size()/dim;
|
||||
elmin_h.SetSize(dim * nel);
|
||||
elmax_h.SetSize(dim * nel);
|
||||
for (int i = 0; i < nel; i++)
|
||||
{
|
||||
int count_el = 1;
|
||||
for (int d = 0; d < dim; d++)
|
||||
{
|
||||
GetGridRange(d, lh_n, lh_fac, lh_bnd_min,
|
||||
elmin[d*nel + i], elmax[d*nel + i],
|
||||
elmin_h[d*nel + i], elmax_h[d*nel + i]);
|
||||
int imax = elmax_h[d*nel + i];
|
||||
int imin = elmin_h[d*nel + i];
|
||||
count_el *= (imax - imin);
|
||||
}
|
||||
count += count_el;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
/* The BBoxTensorGridMap class is adapted from similar functionality in the
|
||||
gslib library. Below is the gslib license and copyright statement:
|
||||
|
||||
Copyright (c) 2008-2024, UCHICAGO ARGONNE, LLC.
|
||||
|
||||
The UChicago Argonne, LLC as Operator of Argonne National
|
||||
Laboratory holds copyright in the Software. The copyright holder
|
||||
reserves all rights except those expressly granted to licensees,
|
||||
and U.S. Government license rights.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the disclaimer below.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the disclaimer (as noted below)
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. Neither the name of ANL nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
UCHICAGO ARGONNE, LLC, THE U.S. DEPARTMENT OF
|
||||
ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MFEM_BB_GRID_MAP
|
||||
#define MFEM_BB_GRID_MAP
|
||||
|
||||
#include "../config/config.hpp"
|
||||
#ifdef MFEM_USE_MPI
|
||||
#include "../fem/pgridfunc.hpp"
|
||||
#else
|
||||
#include "../fem/gridfunc.hpp"
|
||||
#endif
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
/** \brief Map a point in physical space to candidate elements of a curved mesh.
|
||||
*
|
||||
* This class builds a Cartesian-aligned tensor grid that covers the domain
|
||||
* and precomputes, for each grid cell, the set of curved mesh elements whose
|
||||
* axis-aligned bounding boxes (AABBs) intersect that cell. Given a point (xyz)
|
||||
* in physical coordinates, the Cartesian grid cell containing the point is
|
||||
* determined, and the list of candidate element indices whose AABBs are
|
||||
* intersecting that cell is returned. This yields a fast, conservative
|
||||
* point-to-element candidate query.
|
||||
*
|
||||
* The mapping procedure uses a half-open interval convention in each
|
||||
* dimension. If an element bounding-box minimum lies exactly on a grid-cell
|
||||
* boundary, it is assigned to the cell on the right/high side of that
|
||||
* boundary. If an element bounding-box maximum lies exactly on a grid-cell
|
||||
* boundary, it is assigned to the cell on the left/low side.
|
||||
*
|
||||
* The map itself is stored as a single array CSR structure where the offsets
|
||||
* and values are stored in the same array. For a tensor grid with a total of
|
||||
* N cells, the first N+1 entries store the offsets and the remaining entries
|
||||
* store the values.
|
||||
*
|
||||
* The "lgrid_map" looks something like this:
|
||||
*
|
||||
* Index: 0 1 ... N N+1 ...
|
||||
* Value: [start_0] [start_1] ... [Length(Map)] [elem_A] [elem_B] [elem_C]...
|
||||
* | | ^ ^
|
||||
* | |__________________________|_________________|
|
||||
* |_____________________________________|
|
||||
*
|
||||
* For grid cell index i, the element indices are stored in
|
||||
* lgrid_map[j], where lgrid_map[i] <= j < lgrid_map[i+1].
|
||||
*
|
||||
* If lgrid_map[i] = lgrid_map[i+1], the grid cell i does not intersect any
|
||||
* elements.
|
||||
*
|
||||
* See Mittal et al., "General Field Evaluation in High-Order Meshes on GPUs".
|
||||
* (2025). Computers & Fluids. for technical details.
|
||||
*/
|
||||
class BBoxTensorGridMap
|
||||
{
|
||||
private:
|
||||
int sdim; // spatial dimension
|
||||
Array<int> lmap_nx; // grid resolution in each direction
|
||||
Vector lmap_bnd_min, lmap_bnd_max; // min and max extend of grid in x/y/z
|
||||
Vector lmap_fac; // number of cells per unit extent
|
||||
Array<unsigned int> lgrid_map; // actual map from grid cell to mesh elements.
|
||||
unsigned int lmap_nxd; // total number of grid cells
|
||||
|
||||
public:
|
||||
/// Constructor for a given mesh and resolution of Cartesian grid.
|
||||
BBoxTensorGridMap(Mesh &mesh, int nx);
|
||||
|
||||
/** @brief Constructor with mesh element bounding boxes and spatial dimension.
|
||||
*
|
||||
* @details When by_max_size=false, nx gives the Cartesian grid resolution
|
||||
* in each direction. When by_max_size=true, nx[0] gives the requested
|
||||
* maximum size of lgrid_map. If nx[0] < 2 + nel, lgrid_map is resized to
|
||||
* the minimum feasible size 2 + nel.
|
||||
*
|
||||
* Assumes elmin, elmax Ordering::byNodes:
|
||||
* elmin -> [x_{0,min},x_{1,min},... ,y_{0,min},y_{1,min},..,z_{nel-1,min}]
|
||||
* elmax -> [x_{0,max},x_{1,max},... ,y_{0,max},y_{1,max},..,z_{nel-1,max}]
|
||||
* Note elmin, elmax can be obtained using GridFunction::GetElementBounds()
|
||||
*/
|
||||
BBoxTensorGridMap(Vector &elmin, Vector &elmax,
|
||||
int nel, int sdim, Array<int> &nx,
|
||||
bool by_max_size=false);
|
||||
|
||||
/** @brief Constructor for given element bounds and spatial dimension.
|
||||
*
|
||||
* @details The user can either specify the max size of map
|
||||
* (by_max_size=true) or the number of divisions (by_max_size=false).
|
||||
*
|
||||
* @details When by_max_size=true, n gives the requested maximum size of
|
||||
* lgrid_map. If n >= 2 + nel, then lgrid_map.Size() <= n. Otherwise,
|
||||
* lgrid_map is resized to the minimum feasible size 2 + nel.
|
||||
*
|
||||
* Assumes elmin, elmax Ordering::byNodes:
|
||||
* elmin -> [x_{0,min},x_{1,min},... ,y_{0,min},y_{1,min},..,z_{nel-1,min}]
|
||||
* elmax -> [x_{0,max},x_{1,max},... ,y_{0,max},y_{1,max},..,z_{nel-1,max}]
|
||||
* Note elmin, elmax can be obtained using GridFunction::GetElementBounds()
|
||||
*/
|
||||
BBoxTensorGridMap(Vector &elmin, Vector &elmax,
|
||||
int nel, int sdim, int n, bool by_max_size=false);
|
||||
|
||||
/// Map a point to possible overlapping elements.
|
||||
Array<int> MapPointToElements(Vector &xyz) const;
|
||||
|
||||
/// Get grid cell index for a given point.
|
||||
int GetGridCellFromPoint(Vector &xyz) const;
|
||||
|
||||
/// Get list of elements corresponding to a grid cell.
|
||||
Array<int> GridCellToElements(int i) const;
|
||||
|
||||
// Some getters
|
||||
const Array<unsigned int> &GetGridMap() const { return lgrid_map; }
|
||||
const Vector &GetGridFac() const { return lmap_fac; }
|
||||
const Vector &GetGridMin() const { return lmap_bnd_min; }
|
||||
const Vector &GetGridMax() const { return lmap_bnd_max; }
|
||||
const Array<int> &GetGridN() const { return lmap_nx; }
|
||||
private:
|
||||
/** @brief Setup using the element-wise bounding boxes.
|
||||
*
|
||||
* @details When by_max_size = false, nx gives number of cells in each
|
||||
* direction. When by_max_size = true, nx[0] gives the requested maximum
|
||||
* size of lgrid_map. If nx[0] < 2 + nel, lgrid_map is resized to the
|
||||
* minimum feasible size 2 + nel. */
|
||||
void Setup(Vector &elmin, Vector &elmax,
|
||||
int nel, Array<int> &nx, bool by_max_size);
|
||||
|
||||
public:
|
||||
/** @brief Get local (1D) indices for cells of tensor grid that intersect
|
||||
* with the given bounding box. */
|
||||
static void GetGridRange(const int d, const Array<int> &lh_n,
|
||||
const Vector &lh_fac,
|
||||
const Vector &lh_bnd_min,
|
||||
const real_t &xmin, const real_t &xmax,
|
||||
int &imin, int &imax);
|
||||
|
||||
/// Set grid fac - number of grid cells per unit grid extent.
|
||||
static void SetGridFac(Vector &lh_fac, const Array<int> &nx,
|
||||
const Vector &lh_bnd_min, const Vector &lh_bnd_max);
|
||||
|
||||
/** @brief Get grid count and range - total number of grid cells that
|
||||
* intersect with all elements of the mesh and get corresponding ranges. */
|
||||
static int GetGridCountAndRange(const Array<int> &lh_n, const Vector &lh_fac,
|
||||
const Vector &lh_bnd_min,
|
||||
const Vector &lh_bnd_max,
|
||||
const Vector &elmin, const Vector &elmax,
|
||||
Array<int> &elmin_h, Array<int> &elmax_h);
|
||||
};
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif // MFEM_BB_GRID_MAP
|
||||
+839
-14
@@ -9,13 +9,22 @@
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#include "gmsh.hpp"
|
||||
#include "mesh_headers.hpp"
|
||||
#include "vtk.hpp"
|
||||
#include "../general/hash_util.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace mfem::bin_io;
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
int BarycentricToGmshTet(int *b, int ref)
|
||||
namespace gmsh
|
||||
{
|
||||
|
||||
/// Given barycentric indices @a b of a node in a tetrahedral element of degree
|
||||
/// @a ref, return its Gmsh index.
|
||||
static int BarycentricToGmshTet(int *b, int ref)
|
||||
{
|
||||
int i = b[0];
|
||||
int j = b[1];
|
||||
@@ -117,7 +126,9 @@ int BarycentricToGmshTet(int *b, int ref)
|
||||
}
|
||||
}
|
||||
|
||||
int CartesianToGmshQuad(int idx_in[], int ref)
|
||||
/// Given the Cartesian indices @a idx_in of a node in a quadrilateral of order
|
||||
/// @a ref, return its Gmsh index.
|
||||
static int CartesianToGmshQuad(int idx_in[], int ref)
|
||||
{
|
||||
int i = idx_in[0];
|
||||
int j = idx_in[1];
|
||||
@@ -147,7 +158,9 @@ int CartesianToGmshQuad(int idx_in[], int ref)
|
||||
}
|
||||
}
|
||||
|
||||
int CartesianToGmshHex(int idx_in[], int ref)
|
||||
/// Given the Cartesian indices @a idx_in of a node in a hexahedron of order
|
||||
/// @a ref, return its Gmsh index.
|
||||
static int CartesianToGmshHex(int idx_in[], int ref)
|
||||
{
|
||||
int i = idx_in[0];
|
||||
int j = idx_in[1];
|
||||
@@ -213,7 +226,9 @@ int CartesianToGmshHex(int idx_in[], int ref)
|
||||
}
|
||||
}
|
||||
|
||||
int WedgeToGmshPri(int idx_in[], int ref)
|
||||
/// Given the indices @a idx_in of a node in a prism of order @a ref, return its
|
||||
/// Gmsh index.
|
||||
static int WedgeToGmshPrism(int idx_in[], int ref)
|
||||
{
|
||||
int i = idx_in[0];
|
||||
int j = idx_in[1];
|
||||
@@ -308,7 +323,9 @@ int WedgeToGmshPri(int idx_in[], int ref)
|
||||
}
|
||||
}
|
||||
|
||||
int CartesianToGmshPyramid(int idx_in[], int ref)
|
||||
/// Given the Cartesian indices @a idx_in of a node in a pyramid of order @a ref
|
||||
/// return its Gmsh index.
|
||||
static int CartesianToGmshPyramid(int idx_in[], int ref)
|
||||
{
|
||||
int i = idx_in[0];
|
||||
int j = idx_in[1];
|
||||
@@ -375,7 +392,8 @@ int CartesianToGmshPyramid(int idx_in[], int ref)
|
||||
}
|
||||
}
|
||||
|
||||
void GmshHOSegmentMapping(int order, int *map)
|
||||
/// Form the mapping from MFEM to Gmsh segment indices.
|
||||
static void HOSegmentMapping(int order, int *map)
|
||||
{
|
||||
map[0] = 0;
|
||||
map[order] = 1;
|
||||
@@ -385,7 +403,8 @@ void GmshHOSegmentMapping(int order, int *map)
|
||||
}
|
||||
}
|
||||
|
||||
void GmshHOTriangleMapping(int order, int *map)
|
||||
/// Form the mapping from MFEM to Gmsh triangle indices.
|
||||
static void HOTriangleMapping(int order, int *map)
|
||||
{
|
||||
int b[3];
|
||||
int o = 0;
|
||||
@@ -400,7 +419,8 @@ void GmshHOTriangleMapping(int order, int *map)
|
||||
}
|
||||
}
|
||||
|
||||
void GmshHOQuadrilateralMapping(int order, int *map)
|
||||
/// Form the mapping from MFEM to Gmsh quadrilateral indices.
|
||||
static void HOQuadrilateralMapping(int order, int *map)
|
||||
{
|
||||
int b[2];
|
||||
int o = 0;
|
||||
@@ -414,7 +434,8 @@ void GmshHOQuadrilateralMapping(int order, int *map)
|
||||
}
|
||||
}
|
||||
|
||||
void GmshHOTetrahedronMapping(int order, int *map)
|
||||
/// Form the mapping from MFEM to Gmsh tetrahedron indices.
|
||||
static void HOTetrahedronMapping(int order, int *map)
|
||||
{
|
||||
int b[4];
|
||||
int o = 0;
|
||||
@@ -433,7 +454,8 @@ void GmshHOTetrahedronMapping(int order, int *map)
|
||||
}
|
||||
}
|
||||
|
||||
void GmshHOHexahedronMapping(int order, int *map)
|
||||
/// Form the mapping from MFEM to Gmsh hexahedron indices.
|
||||
static void HOHexahedronMapping(int order, int *map)
|
||||
{
|
||||
int b[3];
|
||||
int o = 0;
|
||||
@@ -450,7 +472,8 @@ void GmshHOHexahedronMapping(int order, int *map)
|
||||
}
|
||||
}
|
||||
|
||||
void GmshHOWedgeMapping(int order, int *map)
|
||||
/// Form the mapping from MFEM to Gmsh prism indices.
|
||||
static void HOPrismMapping(int order, int *map)
|
||||
{
|
||||
int b[3];
|
||||
int o = 0;
|
||||
@@ -460,14 +483,15 @@ void GmshHOWedgeMapping(int order, int *map)
|
||||
{
|
||||
for (b[0]=0; b[0]<=order - b[1]; b[0]++)
|
||||
{
|
||||
map[o] = WedgeToGmshPri(b, order);
|
||||
map[o] = WedgeToGmshPrism(b, order);
|
||||
o++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GmshHOPyramidMapping(int order, int *map)
|
||||
/// Form the mapping from MFEM to Gmsh pyramid indices.
|
||||
static void HOPyramidMapping(int order, int *map)
|
||||
{
|
||||
int b[3];
|
||||
int o = 0;
|
||||
@@ -484,4 +508,805 @@ void GmshHOPyramidMapping(int order, int *map)
|
||||
}
|
||||
}
|
||||
|
||||
/// Number of nodes in an element of type @a geom with order @a order.
|
||||
static int NumNodesInElement(Geometry::Type geom, int order)
|
||||
{
|
||||
return GlobGeometryRefiner.Refine(geom, order, 1)->RefPts.GetNPoints();
|
||||
}
|
||||
|
||||
/// @brief Return the space dimension (at least 1) given a 3D bounding box.
|
||||
///
|
||||
/// If some of the sides of the box have zero (or very small) sides, then that
|
||||
/// dimension is not counted.
|
||||
static int GetSpaceDimension(double bb_min[3], double bb_max[3])
|
||||
{
|
||||
static constexpr double bb_tol = 1e-14;
|
||||
const double bb_size = max(bb_max[0] - bb_min[0],
|
||||
max(bb_max[1] - bb_min[1],
|
||||
bb_max[2] - bb_min[2]));
|
||||
int sd = 1;
|
||||
if (bb_max[1] - bb_min[1] > bb_size * bb_tol)
|
||||
{
|
||||
sd += 1;
|
||||
}
|
||||
if (bb_max[2] - bb_min[2] > bb_size * bb_tol)
|
||||
{
|
||||
sd += 1;
|
||||
}
|
||||
return sd;
|
||||
}
|
||||
|
||||
/// Skip ahead in the input stream until the next section, which opens on a new
|
||||
/// line beginning with $ (but not beginning with $End, which ends the previous
|
||||
/// section).
|
||||
static string GoToNextSection(istream &input)
|
||||
{
|
||||
string line;
|
||||
while (getline(input, line))
|
||||
{
|
||||
filter_dos(line);
|
||||
// Find the next line that starts with '$', but does not start with "$End"
|
||||
if (line.size() >= 1 &&
|
||||
line[0] == '$' &&
|
||||
(line.size() < 4 || line.compare(1, 3, "End") != 0))
|
||||
{
|
||||
return line.substr(1, string::npos);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// Read a double-quoted string from the input stream, and return the result
|
||||
/// (without the enclosing quotes).
|
||||
static string ReadQuotedString(istream &input)
|
||||
{
|
||||
char c;
|
||||
// Find opening quote
|
||||
while (input.get(c))
|
||||
{
|
||||
if (c == '"') { break; }
|
||||
}
|
||||
MFEM_VERIFY(input, "Error reading string.");
|
||||
|
||||
string result;
|
||||
while (input.get(c))
|
||||
{
|
||||
// Find closing quote
|
||||
if (c == '"')
|
||||
{
|
||||
return result;
|
||||
}
|
||||
result.push_back(c);
|
||||
}
|
||||
MFEM_ABORT("Failed to read string.");
|
||||
}
|
||||
|
||||
void ChompNewline(istream &input)
|
||||
{
|
||||
if (input.peek() == '\r') { input.get(); }
|
||||
MFEM_VERIFY(input.get() == '\n', "Inconsistent newlines.");
|
||||
};
|
||||
|
||||
/// Enum for supported Gmsh mesh file versions.
|
||||
enum class GmshVersion { V2_2, V4_1 };
|
||||
|
||||
/// @brief Helper class for reading Gmsh meshes.
|
||||
///
|
||||
/// This is an internal helper class that is not intended for use by the
|
||||
/// end-user; see Mesh::ReadGmshMesh for its usage.
|
||||
///
|
||||
/// This class implements common functionality and state needed to read Gmsh
|
||||
/// meshes in version 2.2 and 4.1 format.
|
||||
class GmshReader
|
||||
{
|
||||
/// List of supported Gmsh element types. types[geom][order-1] contains the
|
||||
/// Gmsh element type number for the element of the given geometry and order.
|
||||
vector<vector<int>> types =
|
||||
{
|
||||
{15}, // point
|
||||
{1, 8, 26, 27, 28, 62, 63, 64, 65, 66}, // segment
|
||||
{2, 9, 21, 23, 25, 42, 43, 44, 45, 46}, // triangle
|
||||
{3, 10, 36, 37, 38, 47, 48, 49, 50, 51}, // quadrilateral
|
||||
{4, 11, 29, 30, 31, 71, 72, 73, 74, 75}, // tetrahedron
|
||||
{5, 12, 92, 93, 94, 95, 96, 97, 98}, // hexahedron
|
||||
{6, 13, 90, 91, 106, 107, 108, 109, 110}, // prism
|
||||
{7, 14, 118, 119, 120, 121, 122, 123, 124} // pyramid
|
||||
};
|
||||
/// Permutations mapping from MFEM lexicographic ordering to Gmsh ordering,
|
||||
/// for a given element type and order. Constructed lazily.
|
||||
unordered_map<pair<Geometry::Type, int>, vector<int>, PairHasher> node_maps;
|
||||
|
||||
bool has_positive_attrs = false;
|
||||
bool has_non_positive_attrs = false;
|
||||
|
||||
istream &input; ///< The input stream to read from.
|
||||
|
||||
BinaryOrASCII is_binary; ///< Is the file in binary or ASCII format?
|
||||
int data_size; ///< Data size in bytes (meaning depends on file format).
|
||||
GmshVersion version; ///< The version of Gmsh format.
|
||||
|
||||
/// A map between a serial number of the vertex and its number in the file
|
||||
/// (there may be gaps in the numbering, and also Gmsh enumerates vertices
|
||||
/// starting from 1, not 0)
|
||||
unordered_map<int, int> vertex_map;
|
||||
|
||||
/// A map containing names of physical curves, surfaces, and volumes. The
|
||||
/// first index is the dimension of the physical manifold, the second index is
|
||||
/// the element attribute number of the set, and the string is the assigned
|
||||
/// name.
|
||||
unordered_map<int,unordered_map<int,string> > phys_names_by_dim;
|
||||
|
||||
/// Gmsh always outputs coordinates in 3D, but MFEM distinguishes between the
|
||||
/// mesh element dimension (Dim) and the dimension of the space in which the
|
||||
/// mesh is embedded (spaceDim). For example, a 2D MFEM mesh has Dim = 2 and
|
||||
/// spaceDim = 2, while a 2D surface mesh in 3D has Dim = 2 but spaceDim = 3.
|
||||
/// We set spaceDim by measuring the mesh bounding box and checking for a
|
||||
/// lower dimensional subspace. The assumption is that the mesh is at least
|
||||
/// 2D if the y-dimension of the box is non-trivial and 3D if the z-dimension
|
||||
/// is non-trivial. Note that with these assumptions a 2D mesh parallel to
|
||||
/// the yz plane will be considered a surface mesh embedded in 3D whereas the
|
||||
/// same 2D mesh parallel to the xy plane will be considered a 2D mesh.
|
||||
///@{
|
||||
const double inf = numeric_limits<double>::infinity();
|
||||
double bb_min[3] = {inf, inf, inf};
|
||||
double bb_max[3] = {-inf, -inf, -inf};
|
||||
///@}
|
||||
|
||||
int mesh_order = -1; ///< Mesh order. Variable order meshes are not supported.
|
||||
bool periodic = false; ///< Is the mesh periodic?
|
||||
|
||||
/// Node indices of high-order elements, such that ho_el_nodes[dim][e][i] is
|
||||
/// the i-th node index of the e-th element of dimension dim.
|
||||
vector<vector<vector<int>>> ho_el_nodes{4};
|
||||
|
||||
vector<int> v2v; ///< Periodic vertex mapping (for periodic meshes only).
|
||||
|
||||
/// Get the geometry type and polynomial degree for a given Gmsh element
|
||||
/// type.
|
||||
pair<Geometry::Type, int> GetGeometryAndOrder(int element_type) const
|
||||
{
|
||||
for (int g = Geometry::POINT; g < Geometry::NUM_GEOMETRIES; ++g)
|
||||
{
|
||||
const vector<int> &types_g = types[g];
|
||||
const auto it = lower_bound(types_g.begin(), types_g.end(), element_type);
|
||||
if (it != types_g.end() && *it == element_type)
|
||||
{
|
||||
return {Geometry::Type(g), int(distance(types_g.begin(), it) + 1)};
|
||||
}
|
||||
}
|
||||
MFEM_ABORT("Unknown Gmsh element type.");
|
||||
}
|
||||
|
||||
/// Return node map if it exists, otherwise lazily construct it.
|
||||
const vector<int> &GetNodeMap(Geometry::Type geom, int order)
|
||||
{
|
||||
auto it = node_maps.find(make_pair(geom, order));
|
||||
if (it == node_maps.end())
|
||||
{
|
||||
const int n_nodes = NumNodesInElement(geom, order);
|
||||
auto ret = node_maps.emplace(piecewise_construct,
|
||||
forward_as_tuple(geom, order),
|
||||
forward_as_tuple(n_nodes));
|
||||
auto &map = ret.first->second;
|
||||
auto data = map.data();
|
||||
switch (geom)
|
||||
{
|
||||
case Geometry::SEGMENT: HOSegmentMapping(order, data); break;
|
||||
case Geometry::TRIANGLE: HOTriangleMapping(order, data); break;
|
||||
case Geometry::SQUARE: HOQuadrilateralMapping(order, data); break;
|
||||
case Geometry::TETRAHEDRON: HOTetrahedronMapping(order, data); break;
|
||||
case Geometry::CUBE: HOHexahedronMapping(order, data); break;
|
||||
case Geometry::PRISM: HOPrismMapping(order, data); break;
|
||||
case Geometry::PYRAMID: HOPyramidMapping(order, data); break;
|
||||
default: MFEM_ABORT("Unsupported element type.");
|
||||
}
|
||||
return map;
|
||||
}
|
||||
else
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
/// Add the physical names (in @a phys_names_by_dim) to the mesh's attribute
|
||||
/// sets and boundary attribute sets.
|
||||
void AddPhysicalNames(Mesh &mesh)
|
||||
{
|
||||
// Process boundary attribute set names
|
||||
for (auto const &bdr_attr : phys_names_by_dim[mesh.Dimension() - 1])
|
||||
{
|
||||
if (!mesh.bdr_attribute_sets.AttributeSetExists(bdr_attr.second))
|
||||
{
|
||||
mesh.bdr_attribute_sets.CreateAttributeSet(bdr_attr.second);
|
||||
}
|
||||
mesh.bdr_attribute_sets.AddToAttributeSet(bdr_attr.second, bdr_attr.first);
|
||||
}
|
||||
|
||||
// Process element attribute set names
|
||||
for (auto const &attr : phys_names_by_dim[mesh.Dimension()])
|
||||
{
|
||||
if (!mesh.attribute_sets.AttributeSetExists(attr.second))
|
||||
{
|
||||
mesh.attribute_sets.CreateAttributeSet(attr.second);
|
||||
}
|
||||
mesh.attribute_sets.AddToAttributeSet(attr.second, attr.first);
|
||||
}
|
||||
}
|
||||
|
||||
/// In the periodic vertex mapping @a v2v, there may be chains or cycles.
|
||||
/// This will simplify all chains so that they are one link only, and break
|
||||
/// any cycles.
|
||||
void SimplifyPeriodicLinks()
|
||||
{
|
||||
// Follow existing long chains of duplicate->primary in v2v array. Upon
|
||||
// completion of this loop, each v2v[duplicate] will point to a true
|
||||
// primary vertex. This algorithm is useful for periodicity defined in
|
||||
// multiple directions.
|
||||
for (int duplicate = 0; duplicate < int(v2v.size()); duplicate++)
|
||||
{
|
||||
int primary = v2v[duplicate];
|
||||
if (primary != duplicate)
|
||||
{
|
||||
// This loop will end if it finds a circular dependency.
|
||||
while (v2v[primary] != primary && primary != duplicate)
|
||||
{
|
||||
primary = v2v[primary];
|
||||
}
|
||||
if (primary == duplicate)
|
||||
{
|
||||
// If primary and duplicate are the same vertex, circular
|
||||
// dependency exists. We need to fix the problem, we choose
|
||||
// duplicate.
|
||||
v2v[duplicate] = duplicate;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The long chain has ended on the true primary vertex.
|
||||
v2v[duplicate] = primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// In the list of Elements @a els, replace periodic vertices using the
|
||||
/// periodic identification map @a v2v.
|
||||
void ReplacePeriodicVertices(Array<Element*> &els) const
|
||||
{
|
||||
for (int i = 0; i < els.Size(); i++)
|
||||
{
|
||||
Element *e = els[i];
|
||||
int *v = e->GetVertices();
|
||||
for (int j = 0; j < e->GetNVertices(); j++)
|
||||
{
|
||||
v[j] = v2v[v[j]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the attribute of element @a e to @a attribute. If the attribute is
|
||||
/// non-positive, set it to 1. Keep track if non-positive or positive
|
||||
/// attributes are encountered to potentially report errors to the user.
|
||||
void SetAttribute(Element *e, int attribute)
|
||||
{
|
||||
if (attribute < 1)
|
||||
{
|
||||
has_non_positive_attrs = true;
|
||||
attribute = 1; // Resetting non-positive attributes to be 1.
|
||||
}
|
||||
else
|
||||
{
|
||||
has_positive_attrs = true;
|
||||
}
|
||||
e->SetAttribute(attribute);
|
||||
}
|
||||
|
||||
/// Create and return a new Element of the given geometry, with specified
|
||||
/// attribute. If the element is higher-order, store the high-order node
|
||||
/// indices.
|
||||
template <typename I>
|
||||
Element *NewElement(Mesh &mesh, Geometry::Type geom, int el_order,
|
||||
const vector<I> &el_nodes, int attribute)
|
||||
{
|
||||
auto e = mesh.NewElement(geom);
|
||||
int *v = e->GetVertices();
|
||||
for (int i = 0; i < e->GetNVertices(); ++i)
|
||||
{
|
||||
v[i] = vertex_map[el_nodes[i]];
|
||||
}
|
||||
SetAttribute(e, attribute);
|
||||
|
||||
// Store high-order node locations
|
||||
const int dim = Geometry::Dimension[geom];
|
||||
if (el_order > 1)
|
||||
{
|
||||
const int n_elem_nodes = NumNodesInElement(geom, el_order);
|
||||
const vector<int> &map = GetNodeMap(geom, el_order);
|
||||
auto &nodes = ho_el_nodes[dim].emplace_back(n_elem_nodes);
|
||||
for (int i = 0; i < n_elem_nodes; ++i)
|
||||
{
|
||||
nodes[i] = vertex_map[el_nodes[map[i]]];
|
||||
}
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/// Check that all attributes are positive (or, if none are positive, give a
|
||||
/// warning that they have been replaced by 1).
|
||||
void CheckAttributes() const
|
||||
{
|
||||
if (has_non_positive_attrs)
|
||||
{
|
||||
// If mesh has a mix of positive and non-positive attributes, this is
|
||||
// a user error. All attributes should be positive.
|
||||
MFEM_VERIFY(!has_positive_attrs,
|
||||
"Non-positive element attribute in Gmsh mesh!\n"
|
||||
"By default Gmsh sets element tags (attributes)"
|
||||
" to '0' but MFEM requires that they be"
|
||||
" positive integers.\n"
|
||||
"Use \"Physical Curve\", \"Physical Surface\","
|
||||
" or \"Physical Volume\" to set tags/attributes"
|
||||
" for all curves, surfaces, or volumes in your"
|
||||
" Gmsh geometry to values which are >= 1.");
|
||||
// If the mesh has only non-positive attributes, this could be because
|
||||
// Gmsh by default will set zero attributes if no physical entities are
|
||||
// defined. In this case, we warn the user, and set attributes to 1.
|
||||
MFEM_WARNING("Gmsh reader: all element attributes were zero.\n"
|
||||
"MFEM only supports positive element attributes.\n"
|
||||
"Setting all element attributes to 1.\n");
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Read the mesh in Gmsh 4.1 format from the input stream into the
|
||||
/// Mesh @a mesh.
|
||||
void ReadGmsh4Mesh(Mesh &mesh)
|
||||
{
|
||||
MFEM_VERIFY(data_size == sizeof(size_t), "Incompatible Gmsh mesh.");
|
||||
|
||||
const auto b = is_binary;
|
||||
unordered_map<pair<int,int>, int, PairHasher> entity_physical_tag;
|
||||
|
||||
string section;
|
||||
do
|
||||
{
|
||||
section = GoToNextSection(input);
|
||||
if (section == "PhysicalNames")
|
||||
{
|
||||
// $PhysicalNames is always encoded in ASCII
|
||||
const int n_phys_names = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
for (int i = 0; i < n_phys_names; ++i)
|
||||
{
|
||||
const int phys_name_dim = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
const int phys_name_tag = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
const string phys_name = ReadQuotedString(input);
|
||||
|
||||
phys_names_by_dim[phys_name_dim][phys_name_tag] = phys_name;
|
||||
}
|
||||
}
|
||||
else if (section == "Entities")
|
||||
{
|
||||
const size_t n_points = ReadBinaryOrASCII<size_t>(input, b);
|
||||
const size_t n_curves = ReadBinaryOrASCII<size_t>(input, b);
|
||||
const size_t n_surfaces = ReadBinaryOrASCII<size_t>(input, b);
|
||||
const size_t n_volumes = ReadBinaryOrASCII<size_t>(input, b);
|
||||
|
||||
const size_t n_entities[4] = {n_points, n_curves, n_surfaces, n_volumes};
|
||||
|
||||
if (n_volumes > 0) { mesh.Dim = 3; }
|
||||
else if (n_surfaces > 0) { mesh.Dim = 2; }
|
||||
else { mesh.Dim = 1; }
|
||||
|
||||
for (int d = 0; d <= 3; ++d)
|
||||
{
|
||||
for (size_t i = 0; i < n_entities[d]; ++i)
|
||||
{
|
||||
const int tag = ReadBinaryOrASCII<int>(input, b);
|
||||
Skip<double>(input, d == 0 ? 3 : 6, b); // Skip X, Y, Z
|
||||
const size_t n_phys_tags = ReadBinaryOrASCII<size_t>(input, b);
|
||||
for (size_t iphys = 0; iphys < n_phys_tags; ++iphys)
|
||||
{
|
||||
const int phys_tag = ReadBinaryOrASCII<int>(input, b);
|
||||
// Keep track of codim-0 and codim-1 entities.
|
||||
if (d == mesh.Dim || d == mesh.Dim - 1)
|
||||
{
|
||||
entity_physical_tag[ {d, tag}] = phys_tag;
|
||||
}
|
||||
}
|
||||
if (d > 0)
|
||||
{
|
||||
const size_t n_bounding = ReadBinaryOrASCII<size_t>(input, b);
|
||||
Skip<int>(input, n_bounding, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (section == "Nodes")
|
||||
{
|
||||
const size_t n_blocks = ReadBinaryOrASCII<size_t>(input, b);
|
||||
const size_t n_nodes = ReadBinaryOrASCII<size_t>(input, b);
|
||||
Skip<size_t>(input, 2, b); // Skip min and max tags
|
||||
|
||||
mesh.NumOfVertices = n_nodes;
|
||||
mesh.vertices.SetSize(n_nodes);
|
||||
size_t vertex_counter = 0;
|
||||
|
||||
double c[3];
|
||||
|
||||
for (size_t iblock = 0; iblock < n_blocks; ++iblock)
|
||||
{
|
||||
Skip<int>(input, 2, b); // Skip entity dim and ta
|
||||
const int is_parametric = ReadBinaryOrASCII<int>(input, b);
|
||||
const size_t n_nodes_in_block = ReadBinaryOrASCII<size_t>(input, b);
|
||||
|
||||
MFEM_VERIFY(!is_parametric, "Parametric nodes not supported.");
|
||||
|
||||
vector<size_t> node_tags(n_nodes_in_block);
|
||||
for (size_t i = 0; i < n_nodes_in_block; ++i)
|
||||
{
|
||||
const size_t node_tag = ReadBinaryOrASCII<size_t>(input, b);
|
||||
node_tags[i] = node_tag;
|
||||
}
|
||||
for (size_t i = 0; i < n_nodes_in_block; ++i)
|
||||
{
|
||||
for (int d = 0; d < 3; ++d)
|
||||
{
|
||||
c[d] = ReadBinaryOrASCII<double>(input, b);
|
||||
bb_min[d] = min(bb_min[d], c[d]);
|
||||
bb_max[d] = max(bb_max[d], c[d]);
|
||||
}
|
||||
vertex_map[node_tags[i]] = vertex_counter;
|
||||
mesh.vertices[vertex_counter] = Vertex(c[0], c[1], c[2]);
|
||||
vertex_counter += 1;
|
||||
}
|
||||
}
|
||||
mesh.spaceDim = GetSpaceDimension(bb_min, bb_max);
|
||||
}
|
||||
else if (section == "Elements")
|
||||
{
|
||||
const size_t n_blocks = ReadBinaryOrASCII<size_t>(input, b);
|
||||
Skip<size_t>(input, 3, b); // Skip n_elements and min/max tags.
|
||||
|
||||
for (size_t iblock = 0; iblock < n_blocks; ++iblock)
|
||||
{
|
||||
const int entity_dim = ReadBinaryOrASCII<int>(input, b);
|
||||
const int entity_tag = ReadBinaryOrASCII<int>(input, b);
|
||||
const int element_type = ReadBinaryOrASCII<int>(input, b);
|
||||
const size_t n_elements = ReadBinaryOrASCII<size_t>(input, b);
|
||||
|
||||
for (size_t ie = 0; ie < n_elements; ++ie)
|
||||
{
|
||||
Skip<size_t>(input, 1, b); // Skip element tag
|
||||
const auto [geom, el_order] = GetGeometryAndOrder(element_type);
|
||||
|
||||
if (mesh_order < 0) { mesh_order = el_order; }
|
||||
MFEM_VERIFY(mesh_order == el_order,
|
||||
"Variable order Gmsh meshes are not supported");
|
||||
|
||||
const int n_elem_nodes = NumNodesInElement(geom, el_order);
|
||||
vector<size_t> node_tags(n_elem_nodes);
|
||||
for (int inode = 0; inode < n_elem_nodes; ++inode)
|
||||
{
|
||||
node_tags[inode] = ReadBinaryOrASCII<size_t>(input, b);
|
||||
}
|
||||
|
||||
// We only add codim-0 and codim-1 elements.
|
||||
if (entity_dim != mesh.Dim && entity_dim != mesh.Dim - 1) { continue; }
|
||||
|
||||
const int attribute = entity_physical_tag[ {entity_dim, entity_tag}];
|
||||
auto e = NewElement(mesh, geom, el_order, node_tags, attribute);
|
||||
if (entity_dim == mesh.Dim) { mesh.elements.Append(e); }
|
||||
else if (entity_dim == mesh.Dim - 1) { mesh.boundary.Append(e); }
|
||||
}
|
||||
}
|
||||
mesh.NumOfElements = mesh.elements.Size();
|
||||
mesh.NumOfBdrElements = mesh.boundary.Size();
|
||||
}
|
||||
else if (section == "Periodic")
|
||||
{
|
||||
const size_t n_periodic = ReadBinaryOrASCII<size_t>(input, b);
|
||||
if (n_periodic == 0) { continue; }
|
||||
|
||||
periodic = true;
|
||||
v2v.resize(mesh.NumOfVertices);
|
||||
for (int i = 0; i < mesh.NumOfVertices; i++) { v2v[i] = i; }
|
||||
|
||||
for (size_t i = 0; i < n_periodic; ++i)
|
||||
{
|
||||
Skip<int>(input, 3, b); // Skip entity information
|
||||
const size_t n_affine = ReadBinaryOrASCII<size_t>(input, b);
|
||||
Skip<double>(input, n_affine, b); // Skip affine information
|
||||
const size_t n_nodes = ReadBinaryOrASCII<size_t>(input, b);
|
||||
for (size_t j = 0; j < n_nodes; ++j)
|
||||
{
|
||||
const size_t node_num = ReadBinaryOrASCII<size_t>(input, b);
|
||||
const size_t primary_node_num = ReadBinaryOrASCII<size_t>(input, b);
|
||||
v2v[node_num - 1] = int(primary_node_num - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!section.empty());
|
||||
}
|
||||
|
||||
/// @brief Read the mesh in Gmsh 2.2 format from the input stream into the
|
||||
/// Mesh @a mesh.
|
||||
void ReadGmsh2Mesh(Mesh &mesh)
|
||||
{
|
||||
const auto b = is_binary;
|
||||
MFEM_VERIFY(data_size == sizeof(double), "Incompatible data size.");
|
||||
|
||||
string section;
|
||||
do
|
||||
{
|
||||
section = GoToNextSection(input);
|
||||
if (section == "Nodes")
|
||||
{
|
||||
mesh.NumOfVertices = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
ChompNewline(input);
|
||||
mesh.vertices.SetSize(mesh.NumOfVertices);
|
||||
double c[3];
|
||||
for (int v = 0; v < mesh.NumOfVertices; ++v)
|
||||
{
|
||||
const int node_num = ReadBinaryOrASCII<int>(input, b);
|
||||
for (int d = 0; d < 3; ++d)
|
||||
{
|
||||
c[d] = ReadBinaryOrASCII<double>(input, b);
|
||||
bb_min[d] = min(bb_min[d], c[d]);
|
||||
bb_max[d] = max(bb_max[d], c[d]);
|
||||
}
|
||||
mesh.vertices[v] = Vertex(c[0], c[1], c[2]);
|
||||
vertex_map[node_num] = v;
|
||||
}
|
||||
mesh.spaceDim = GetSpaceDimension(bb_min, bb_max);
|
||||
MFEM_VERIFY(vertex_map.size() == size_t(mesh.NumOfVertices),
|
||||
"Gmsh node indices are not unique.");
|
||||
}
|
||||
else if (section == "Elements")
|
||||
{
|
||||
const int num_elements = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
ChompNewline(input);
|
||||
int num_el_read = 0;
|
||||
|
||||
vector<vector<unique_ptr<Element>>> elems_by_dim(4);
|
||||
|
||||
while (num_el_read < num_elements)
|
||||
{
|
||||
auto add_element = [&](int el_type, int el_phys_tag, Geometry::Type geom,
|
||||
int el_order, const vector<int> &el_nodes)
|
||||
{
|
||||
if (mesh_order < 0) { mesh_order = el_order; }
|
||||
MFEM_VERIFY(mesh_order == el_order,
|
||||
"Variable order Gmsh meshes are not supported");
|
||||
Element *e = NewElement(mesh, geom, el_order, el_nodes, el_phys_tag);
|
||||
elems_by_dim[Geometry::Dimension[geom]].emplace_back(e);
|
||||
};
|
||||
|
||||
if (b)
|
||||
{
|
||||
// Header
|
||||
const int el_type = ReadBinaryOrASCII<int>(input, BINARY);
|
||||
const int n_els = ReadBinaryOrASCII<int>(input, BINARY);
|
||||
const int n_tags = ReadBinaryOrASCII<int>(input, BINARY);
|
||||
const auto [geom, el_order] = GetGeometryAndOrder(el_type);
|
||||
const int n_el_nodes = NumNodesInElement(geom, el_order);
|
||||
vector<int> el_nodes(n_el_nodes);
|
||||
// Element blocks
|
||||
for (int e = 0; e < n_els; ++e)
|
||||
{
|
||||
Skip<int>(input, 1, BINARY); // Skip element number
|
||||
int el_phys_tag = 0;
|
||||
if (n_tags > 0)
|
||||
{
|
||||
el_phys_tag = ReadBinaryOrASCII<int>(input, BINARY);
|
||||
Skip<int>(input, n_tags - 1, BINARY);
|
||||
}
|
||||
for (int i = 0; i < n_el_nodes; ++i)
|
||||
{
|
||||
el_nodes[i] = ReadBinaryOrASCII<int>(input, BINARY);
|
||||
}
|
||||
add_element(el_type, el_phys_tag, geom, el_order, el_nodes);
|
||||
num_el_read += 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Skip<int>(input, 1, ASCII); // Skip element number
|
||||
const int el_type = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
const int n_tags = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
int el_phys_tag = 0;
|
||||
if (n_tags > 0)
|
||||
{
|
||||
el_phys_tag = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
Skip<int>(input, n_tags - 1, ASCII);
|
||||
}
|
||||
const auto [geom, el_order] = GetGeometryAndOrder(el_type);
|
||||
const int n_el_nodes = NumNodesInElement(geom, el_order);
|
||||
vector<int> el_nodes(n_el_nodes);
|
||||
for (int i = 0; i < n_el_nodes; ++i)
|
||||
{
|
||||
el_nodes[i] = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
}
|
||||
add_element(el_type, el_phys_tag, geom, el_order, el_nodes);
|
||||
num_el_read += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (elems_by_dim[3].size() > 0) { mesh.Dim = 3; }
|
||||
else if (elems_by_dim[2].size() > 0) { mesh.Dim = 2; }
|
||||
else { mesh.Dim = 1; }
|
||||
|
||||
mesh.NumOfElements = elems_by_dim[mesh.Dim].size();
|
||||
mesh.elements.SetSize(mesh.NumOfElements);
|
||||
for (int i = 0; i < mesh.NumOfElements; ++i)
|
||||
{
|
||||
mesh.elements[i] = elems_by_dim[mesh.Dim][i].release();
|
||||
}
|
||||
mesh.NumOfBdrElements = elems_by_dim[mesh.Dim - 1].size();
|
||||
mesh.boundary.SetSize(mesh.NumOfBdrElements);
|
||||
for (int i = 0; i < mesh.NumOfBdrElements; ++i)
|
||||
{
|
||||
mesh.boundary[i] = elems_by_dim[mesh.Dim - 1][i].release();
|
||||
}
|
||||
}
|
||||
else if (section == "PhysicalNames")
|
||||
{
|
||||
const int num_names = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
for (int i = 0; i < num_names; ++i)
|
||||
{
|
||||
const int phys_dim = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
const int phys_tag = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
phys_names_by_dim[phys_dim][phys_tag] = ReadQuotedString(input);
|
||||
}
|
||||
}
|
||||
else if (section == "Periodic")
|
||||
{
|
||||
const int n_periodic_entities = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
if (n_periodic_entities == 0) { continue; }
|
||||
|
||||
periodic = true;
|
||||
v2v.resize(mesh.NumOfVertices);
|
||||
for (int i = 0; i < mesh.NumOfVertices; i++) { v2v[i] = i; }
|
||||
|
||||
for (int i = 0; i < n_periodic_entities; i++)
|
||||
{
|
||||
Skip<int>(input, 3, ASCII); // Skip dimension, tag, and master tag
|
||||
ChompNewline(input);
|
||||
// Next section might be "Affine"; if so, skip.
|
||||
if (input.peek() == 'A')
|
||||
{
|
||||
MFEM_VERIFY(ReadBinaryOrASCII<string>(input, ASCII) == "Affine",
|
||||
"Cannot find Affine transformation");
|
||||
string line;
|
||||
getline(input, line);
|
||||
}
|
||||
const int n_nodes = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
for (int j = 0; j < n_nodes; ++j)
|
||||
{
|
||||
const int node_num = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
const int primary_node_num = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
v2v[node_num - 1] = primary_node_num - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
while (section != "");
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// @brief Read the mesh from the input stream @a input_ into mesh @a mesh.
|
||||
///
|
||||
/// Meshes in Gmsh format 2.2 or 4.1 and in either binary or ASCII can be
|
||||
/// read; the format is determined automatically.
|
||||
GmshReader(istream &input_, Mesh &mesh) : input(input_)
|
||||
{
|
||||
const string version_str = ReadBinaryOrASCII<string>(input, ASCII);
|
||||
MFEM_VERIFY(version_str == "2.2" || version_str == "4.1",
|
||||
"Unsupported Gmsh file version. Supported versions: 2.2 and 4.1");
|
||||
version = version_str == "2.2" ? GmshVersion::V2_2 : GmshVersion::V4_1;
|
||||
is_binary = BinaryOrASCII(ReadBinaryOrASCII<bool>(input, ASCII));
|
||||
data_size = ReadBinaryOrASCII<int>(input, ASCII);
|
||||
ChompNewline(input);
|
||||
if (is_binary)
|
||||
{
|
||||
const int one = ReadBinaryOrASCII<int>(input, BINARY);
|
||||
MFEM_VERIFY(one == 1, "Incompatible endianness.");
|
||||
}
|
||||
|
||||
if (version == GmshVersion::V4_1)
|
||||
{
|
||||
ReadGmsh4Mesh(mesh);
|
||||
}
|
||||
else if (version == GmshVersion::V2_2)
|
||||
{
|
||||
ReadGmsh2Mesh(mesh);
|
||||
}
|
||||
|
||||
// Make sure all element and boundary attributes are positive.
|
||||
CheckAttributes();
|
||||
|
||||
// Merge periodic vertices
|
||||
if (periodic)
|
||||
{
|
||||
// If the mesh is low-order, we need to populate ho_el_nodes before
|
||||
// periodic vertices are identified in order to set the L2 nodes grid
|
||||
// function.
|
||||
if (mesh_order == 1)
|
||||
{
|
||||
ho_el_nodes[mesh.Dim].resize(mesh.NumOfElements);
|
||||
for (int ie = 0; ie < mesh.NumOfElements; ++ie)
|
||||
{
|
||||
const Element *e = mesh.elements[ie];
|
||||
const int nv = e->GetNVertices();
|
||||
const int *v = e->GetVertices();
|
||||
ho_el_nodes[mesh.Dim][ie].resize(nv);
|
||||
const vector<int> &map = GetNodeMap(e->GetGeometryType(), 1);
|
||||
for (int i = 0; i < nv; ++i)
|
||||
{
|
||||
ho_el_nodes[mesh.Dim][ie][i] = v[map[i]];
|
||||
}
|
||||
}
|
||||
}
|
||||
SimplifyPeriodicLinks();
|
||||
ReplacePeriodicVertices(mesh.elements);
|
||||
ReplacePeriodicVertices(mesh.boundary);
|
||||
}
|
||||
|
||||
// If the elements are high-order, keep a copy of the nodes before removing
|
||||
// unused vertices.
|
||||
Array<Vertex> ho_vertices;
|
||||
if (mesh_order > 1 || periodic) { ho_vertices = mesh.vertices; }
|
||||
|
||||
AddPhysicalNames(mesh);
|
||||
mesh.RemoveUnusedVertices();
|
||||
mesh.FinalizeTopology();
|
||||
|
||||
// Now that the mesh topology has been fully created, set the high-order
|
||||
// nodal information (if needed). For periodic meshes, we need to set the
|
||||
// L2 grid function.
|
||||
if (mesh_order > 1 || periodic)
|
||||
{
|
||||
// Gmsh uses uniform nodal points
|
||||
const int bt = BasisType::ClosedUniform;
|
||||
FiniteElementCollection *fec;
|
||||
if (periodic) { fec = new L2_FECollection(mesh_order, mesh.Dim, bt); }
|
||||
else { fec = new H1_FECollection(mesh_order, mesh.Dim, bt); }
|
||||
FiniteElementSpace *fes = new FiniteElementSpace(
|
||||
&mesh, fec, mesh.spaceDim, Ordering::byVDIM);
|
||||
GridFunction *nodes_gf = new GridFunction(fes);
|
||||
// The nodal grid function, owned by mesh, will own fec and fec
|
||||
nodes_gf->MakeOwner(fec);
|
||||
mesh.SetNodalGridFunction(nodes_gf, true);
|
||||
Array<int> vdofs;
|
||||
for (int e = 0; e < mesh.NumOfElements; ++e)
|
||||
{
|
||||
const FiniteElement *fe = fes->GetFE(e);
|
||||
auto *nfe = dynamic_cast<const NodalFiniteElement*>(fe);
|
||||
MFEM_ASSERT(nfe, "Invalid FE");
|
||||
const Array<int> &lex = nfe->GetLexicographicOrdering();
|
||||
fes->GetElementVDofs(e, vdofs);
|
||||
const int n = vdofs.Size() / mesh.spaceDim;
|
||||
for (int i = 0; i < n; ++i)
|
||||
{
|
||||
const int ii = lex.IsEmpty() ? i : lex[i];
|
||||
Vertex v = ho_vertices[ho_el_nodes[mesh.Dim][e][i]];
|
||||
for (int d = 0; d < mesh.spaceDim; ++d)
|
||||
{
|
||||
(*nodes_gf)[vdofs[ii + d*n]] = v(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set curvature to use Gauss-Lobatto instead of uniform basis
|
||||
mesh.SetCurvature(mesh_order, periodic, mesh.spaceDim, Ordering::byVDIM);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace gmsh
|
||||
|
||||
void Mesh::ReadGmshMesh(istream &input)
|
||||
{
|
||||
gmsh::GmshReader(input, *this);
|
||||
}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
||||
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
||||
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
||||
//
|
||||
// This file is part of the MFEM library. For more information and source code
|
||||
// availability visit https://mfem.org.
|
||||
//
|
||||
// MFEM is free software; you can redistribute it and/or modify it under the
|
||||
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
||||
// CONTRIBUTING.md for details.
|
||||
|
||||
#ifndef MFEM_GMSH
|
||||
#define MFEM_GMSH
|
||||
|
||||
namespace mfem
|
||||
{
|
||||
|
||||
// Helpers for reading high order elements in Gmsh format
|
||||
|
||||
/** @name Gmsh High-Order Vertex Mappings
|
||||
|
||||
These functions generate the mappings needed to translate the order of
|
||||
Gmsh's high-order vertices into MFEM's L2 degree of freedom ordering. The
|
||||
mapping is defined so that MFEM_DoF[i] = Gmsh_Vert[map[i]]. The @a map
|
||||
array must already be allocated with the proper number of entries for the
|
||||
element type at the given element @a order.
|
||||
*/
|
||||
///@{
|
||||
|
||||
/// @brief Generate Gmsh vertex mapping for a Segment
|
||||
void GmshHOSegmentMapping(int order, int *map);
|
||||
|
||||
/// @brief Generate Gmsh vertex mapping for a Triangle
|
||||
void GmshHOTriangleMapping(int order, int *map);
|
||||
|
||||
/// @brief Generate Gmsh vertex mapping for a Quadrilateral
|
||||
void GmshHOQuadrilateralMapping(int order, int *map);
|
||||
|
||||
/// @brief Generate Gmsh vertex mapping for a Tetrahedron
|
||||
void GmshHOTetrahedronMapping(int order, int *map);
|
||||
|
||||
/// @brief Generate Gmsh vertex mapping for a Hexahedron
|
||||
void GmshHOHexahedronMapping(int order, int *map);
|
||||
|
||||
/// @brief Generate Gmsh vertex mapping for a Wedge
|
||||
void GmshHOWedgeMapping(int order, int *map);
|
||||
|
||||
/// @brief Generate Gmsh vertex mapping for a Pyramid
|
||||
void GmshHOPyramidMapping(int order, int *map);
|
||||
|
||||
///@}
|
||||
|
||||
} // namespace mfem
|
||||
|
||||
#endif
|
||||
+6
-1
@@ -5069,7 +5069,10 @@ void Mesh::Loader(std::istream &input, int generate_edges,
|
||||
}
|
||||
else if (mesh_type == "$MeshFormat") // Gmsh
|
||||
{
|
||||
ReadGmshMesh(input, curved, read_gf);
|
||||
ReadGmshMesh(input);
|
||||
finalize_topo = false; // Gmsh mesh reader already finalizes the topology
|
||||
curved = Nodes != nullptr;
|
||||
read_gf = false;
|
||||
}
|
||||
else if
|
||||
((mesh_type.size() > 2 &&
|
||||
@@ -11332,6 +11335,8 @@ void Mesh::Swap(Mesh& other, bool non_geometry)
|
||||
|
||||
mfem::Swap(attributes, other.attributes);
|
||||
mfem::Swap(bdr_attributes, other.bdr_attributes);
|
||||
mfem::Swap(attribute_sets.attr_sets, other.attribute_sets.attr_sets);
|
||||
mfem::Swap(bdr_attribute_sets.attr_sets, other.bdr_attribute_sets.attr_sets);
|
||||
|
||||
mfem::Swap(geom_factors, other.geom_factors);
|
||||
mfem::Swap(face_geom_factors, other.face_geom_factors);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user