Compare commits

...
172 Commits
Author SHA1 Message Date
Ido Akkerman 73a1ddd2bf Demo of error and fix 2026-05-20 15:21:11 +02:00
Tzanio Kolev 176958144b Merge pull request #5107 from mfem/findpts-surface
FindPointsGSLIB for surface meshes
2026-05-19 13:29:29 -07:00
Tzanio Kolev 9d191edf06 Merge pull request #5269 from mfem/adapt-lim-pa
PA kernels for adaptive limiting in TMOP (aka interface tangential relaxation)
2026-05-19 13:28:21 -07:00
Tzanio Kolev 86609f139b Merge pull request #5137 from mfem/gmsh-v4
Gmsh v4.1 support (ASCII and binary)
2026-05-19 13:27:54 -07:00
Veselin Dobrev 420fcba457 Merge pull request #5316 from lindsayad/move-attribute-names
Fix Mesh::Swap to preserve named attribute sets
2026-05-19 12:07:15 -07:00
Tzanio Kolev d3e43a6423 Merge pull request #5332 from mfem/update-gh-actions-artifacts
Update some GH actions to the latest versions
2026-05-16 14:08:37 -07:00
Veselin Dobrev 1f6d115d78 Update to the latest versions the actions upload-artifact and download-artifact 2026-05-15 12:19:51 -07:00
Ketan Mittal 686c8416c2 Merge branch 'master' into findpts-surface 2026-05-15 09:28:59 -07:00
Veselin Dobrev 4b61294dc2 Merge pull request #5327 from mfem/hotfix-5200
Revert PR 5200
2026-05-13 18:14:16 -07:00
Tzanio Kolev fced53cd29 Revert PR 5020 2026-05-13 15:33:20 -07:00
Vladimir Z Tomov f47447d92d tolerance 2026-05-13 14:01:40 -07:00
Vladimir Z Tomov a523710117 Go to 1st order for the tmop unit tests. 2026-05-13 12:11:32 -07:00
Vladimir Z Tomov 54b0a83ffd added missing .mesh to makefile 2026-05-11 09:12:01 -07:00
Tzanio Kolev 20ba3f3d0c Merge branch 'master' into findpts-surface 2026-05-09 10:37:16 -07:00
Tzanio Kolev 07cd99fc3d Merge branch 'master' into adapt-lim-pa 2026-05-09 10:37:13 -07:00
Tzanio Kolev f6eb88574f Merge branch 'master' into gmsh-v4 2026-05-09 10:37:10 -07:00
Tzanio Kolev 2631ba93ca Merge pull request #5200 from nmnobre/hypremat
Ensure hypre_CSRMatrixSetRownnz() allocs on host if ownership set to -1
2026-05-09 10:36:59 -07:00
Tzanio Kolev 5ea36c8fd6 Merge pull request #5257 from mfem/hypre-init-bug
missing hypre init in parallel miniapps
2026-05-09 10:36:16 -07:00
Mittal, Ketan b07fc2bb8e make style 2026-05-08 10:50:33 -07:00
Mittal, Ketan 53b1b8f9a9 update serial miniapp to also use surface mesh capability 2026-05-08 10:48:55 -07:00
Vladimir Z Tomov a709bdb9ee style 2026-05-06 15:23:07 -07:00
Vladimir Z Tomov 46c01f196a Use ALF and ALFmF0 instead of ALF and ALF0. 2026-05-06 15:20:42 -07:00
Vladimir Z Tomov 240955c2cb optimized alf - alf0 computations as Ketan suggested. 2026-05-06 14:10:18 -07:00
Vladimir Z Tomov da4a8e3412 added comments 2026-05-06 13:37:04 -07:00
John Camier 258bd917ad Merge branch 'master' into adapt-lim-pa 2026-05-05 15:33:01 -07:00
Will Pazner b9cf853dd3 Fix orientation issue in low-order periodic Gmsh meshes 2026-05-05 12:01:00 -07:00
Will Pazner 915967925c Avoid use of tellg in Gmsh reader
With zlib enabled, tellg will not work reliably with ifgzstream
2026-05-05 12:00:07 -07:00
Tzanio Kolev 88bc3b5833 Merge branch 'master' into hypre-init-bug 2026-05-05 09:13:27 -07:00
John Camier 40bcad05c4 Merge branch 'master' into hypremat 2026-05-05 06:25:44 -07:00
John Camier 6c1c98e4fb Merge branch 'master' into adapt-lim-pa 2026-05-05 06:21:23 -07:00
Mittal, Ketan 3888cba7c4 minor 2026-05-04 14:58:49 -07:00
Mittal, Ketan 932b30e163 Merge branch 'findpts-surface' of https://github.com/mfem/mfem into findpts-surface 2026-05-04 14:47:33 -07:00
Mittal, Ketan 395e4b0d0e Merge branch 'master' of https://github.com/mfem/mfem into findpts-surface 2026-05-04 14:34:11 -07:00
Will Pazner fe01ebf36c Fix bug in Gmsh nodes reader 2026-05-04 09:55:09 -07:00
Will Pazner 905de04020 Properly handle files with CRLF in Gmsh reader 2026-05-02 21:15:42 -07:00
camierjs faa3e22816 Include 2.0 * lim_normal in the normal_inv_delta_sq factor for TMOP diag, grad & mult kernels 2026-05-01 08:02:56 -07:00
camierjs 76d225439a Avoid recomputing Jpr_inv in TMOP_AssembleGradPA_AdaptLim_3D 2026-04-29 16:46:17 -07:00
camierjs 5ee3f03902 Avoid recomputing Jpr_inv in TMOP_AssembleGradPA_AdaptLim_2D 2026-04-29 16:31:05 -07:00
camierjs d3a1144d10 Avoid recomputing some constants 2026-04-29 15:45:44 -07:00
camierjs 20e38f3b10 Use same temporary registers for the computation of ralf & ralf0
Remove unused input vector in GetLocalStateEnergyPA_AdaptLim functions
2026-04-29 15:39:41 -07:00
Will Pazner b20f61b3b8 Use friend class for Gmsh reader; improve Doxygen documentation 2026-04-29 09:02:34 -07:00
Ketan Mittal 1d0b49e5dd Merge branch 'master' into adapt-lim-pa 2026-04-29 08:46:14 -07:00
Ketan Mittal a9dcb20e84 Merge branch 'master' into findpts-surface 2026-04-29 08:35:23 -07:00
Mittal, Ketan 68f6ce14a6 move gslib in NOTICE 2026-04-28 11:36:07 -07:00
Mittal, Ketan 78905d471c minor 2026-04-28 09:44:51 -07:00
Mittal, Ketan 61806ff1f7 add gslib to notice and license text to gslib/bb_grid_map 2026-04-28 08:27:09 -07:00
Alex Lindsay 7b1656e19f Don't hard code array comparisons 2026-04-27 15:59:09 -07:00
Mittal, Ketan c228538c17 fix loop range in interpolate_local_1 2026-04-27 10:42:23 -07:00
Mittal, Ketan a8f5fac0bb Merge branch 'findpts-surface' of https://github.com/mfem/mfem into findpts-surface 2026-04-27 10:25:36 -07:00
Mittal, Ketan 206eb51618 remove unused argument from interpolate kernels 2026-04-27 10:25:24 -07:00
Ketan MittalandJohn Camier 02226f934b Fix typos
Co-authored-by: John Camier <camierjs@gmail.com>
2026-04-27 10:16:01 -07:00
Mittal, Ketan 3d0ba2251a fix scratch space size used for bounding box calculation and some other cosmetic changes to the bounding box methods 2026-04-27 09:34:06 -07:00
Mittal, Ketan b400ee6741 fix volume kernels, and add some undef 2026-04-27 09:27:13 -07:00
Mittal, Ketan f40335f9e7 fix include and flags in findpts kernels 2026-04-27 09:21:55 -07:00
Mittal, Ketan f0de33e33b remove unused argument and header include 2026-04-27 08:51:05 -07:00
Alex LindsayandClaude Sonnet 4.6 c982aa2448 Add unit test for Mesh::Swap preserving named attribute sets
Covers the regression where attr_sets maps were not swapped, silently
dropping all named element/boundary sets on any move or swap.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 13:15:09 -07:00
Alex LindsayandClaude Sonnet 4.6 94d1238637 Fix Mesh::Swap to preserve named attribute sets
Mesh::Swap swapped attributes and bdr_attributes but omitted the
attr_sets maps inside attribute_sets and bdr_attribute_sets, causing
all named boundary/element sets to be silently lost on any move or
swap of an mfem::Mesh.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 13:00:00 -07:00
Will Pazner d218d38af3 Factor out ReadBinaryOrASCII and Skip to binaryio.hpp 2026-04-24 12:13:28 -07:00
camierjs 5fa7ab3602 revert back tol_fe to 1e-5 for parallel runs 2026-04-22 20:21:24 -07:00
camierjs 48cb5996b7 Merge branch 'master' into adapt-lim-pa 2026-04-22 18:47:58 -07:00
camierjs 8e36285a98 make style 2026-04-22 18:47:28 -07:00
camierjs 27db27b088 GPU runs w/ 1e-6 tol_fe 2026-04-22 17:56:28 -07:00
camierjs 8339ee0fe9 wip gpu grad limit 2026-04-22 15:26:14 -07:00
Mittal, Ketan 9f18d7e044 documentation, use newt_tol instead of new variable tol, and update surface tolerance based on experiments in other branch 2026-04-22 09:53:52 -07:00
Will Pazner 90820b76cf Don't need PairHash now that general PairHasher is merged 2026-04-21 20:04:39 -07:00
Will Pazner 3adb2add4c Don't pass istream to ReadGmsh2Mesh or ReadGmsh4Mesh 2026-04-21 16:37:26 -07:00
Will Pazner 5979dd1cce Merge remote-tracking branch 'origin/master' into gmsh-v4
# Conflicts:
#	mesh/mesh_readers.cpp
2026-04-21 16:35:38 -07:00
Mittal, Ketan be999694b0 Merge branch 'master' of https://github.com/mfem/mfem into findpts-surface 2026-04-21 15:42:23 -07:00
Mittal, Ketan 5d3b9ea727 reviewer comments, minor documentation fix, and global map empty rank fix 2026-04-21 15:42:09 -07:00
Mittal, Ketan a4d01470d8 initialize interpolation vector, and fix empty ranks for the grid maps, and some documentation 2026-04-21 13:06:37 -07:00
John Camier a055c7ec63 Merge branch 'master' into adapt-lim-pa 2026-04-21 10:11:08 -07:00
Ketan Mittal 4e83a1604c Merge branch 'master' into findpts-surface 2026-04-19 08:40:28 -07:00
Ketan Mittal dd0d879e7b Merge branch 'master' into findpts-surface 2026-04-17 20:56:17 -07:00
John Camier 4195e4ea2f Merge branch 'master' into adapt-lim-pa 2026-04-16 14:26:00 -07:00
John Camier 94828dbdd0 Merge branch 'master' into hypremat 2026-04-16 04:46:18 -07:00
Ketan Mittal 62fbabe3a5 Merge branch 'master' into findpts-surface 2026-04-14 11:49:44 -07:00
John Camier d8fd6d95c0 Merge branch 'master' into adapt-lim-pa 2026-04-09 06:39:09 -07:00
John Camier dbaff07ae9 Merge branch 'master' into adapt-lim-pa 2026-04-04 07:10:04 -07:00
Will Pazner fdfc019cc1 Reviewer feedback 2026-03-31 09:40:16 -07:00
Vladimir Z Tomov 73aceea741 EnableAdaptiveLimiting for ComboIntegrator 2026-03-27 17:46:48 -07:00
John Camier 0e9a9d9f7c Merge branch 'master' into adapt-lim-pa 2026-03-26 13:49:24 -07:00
Ketan Mittal c25be44dd6 Merge branch 'master' into hypre-init-bug 2026-03-24 21:29:12 -07:00
Vladimir Z Tomov 3eac6fe764 copilot review suggestions. 2026-03-19 17:23:49 -07:00
Ketan Mittal f1ed582828 Merge branch 'master' into findpts-surface 2026-03-16 18:15:05 -07:00
Vladimir Z Tomov 07e0d7cd4f minor 2026-03-16 16:42:57 -07:00
Vladimir Z Tomov c68cc62143 Merge branch 'master' into adapt-lim-pa 2026-03-16 16:40:27 -07:00
Vladimir Z Tomov 2c02b41d71 style 2026-03-16 15:59:15 -07:00
Vladimir Z Tomov d71d1005f9 minor 2026-03-16 15:46:01 -07:00
Vladimir Z Tomov b8f1071168 unused variable 2026-03-16 14:08:34 -07:00
Vladimir Z Tomov d88529d632 corrections 2026-03-16 13:57:46 -07:00
Vladimir Z Tomov 7bd028b7fe Corresponding edits in mesh-optimizer 2026-03-16 13:39:33 -07:00
Vladimir Z Tomov 339f20ea7f style 2026-03-16 13:19:13 -07:00
Vladimir Z Tomov 336d80e93a missing function call 2026-03-16 13:15:59 -07:00
Vladimir Z Tomov b64a189215 Unit test fixes. 2026-03-16 13:07:20 -07:00
Vladimir Z Tomov fc76ff8b2f unit test 2026-03-13 10:12:15 -07:00
Vladimir Z Tomov e0a65ffaaf Avoid reassembly of quad poitns grads and hessians. 2026-03-12 18:08:43 -07:00
Vladimir Z Tomov a6bad19b8f style 2026-03-12 10:07:59 -07:00
Vladimir Z Tomov 174d991451 Merge branch 'master' into adapt-lim-pa 2026-03-11 14:46:00 -07:00
Andrew Ho a03095d84d Merge branch 'master' into hypre-init-bug 2026-03-09 09:45:36 -07:00
Vladimir Z Tomov 9ee63d6521 Initial 3D PA for the adaptive limiting + setup 3D problem. 2026-03-06 10:59:11 -08:00
Ketan Mittal d0324074c1 Merge branch 'master' into findpts-surface 2026-03-06 08:33:27 -08:00
Vladimir Z Tomov 983d0f4361 Fixed a bug - missing assembly before the MultPA. 2026-03-05 11:32:47 -08:00
Andrew Ho 30016c83b8 Merge branch 'master' into hypre-init-bug 2026-03-05 09:28:46 -08:00
Andrew Ho cf5d93604e Merge branch 'catch-tests' into hypre-init-bug 2026-03-05 09:28:21 -08:00
Vladimir Z Tomov ada42c9fd8 Diagonal PA assembly 2D of the adaptive limiting. 2026-03-04 14:34:36 -08:00
Andrew Ho 0b802d8fce missing hypre init in parallel miniapps 2026-03-04 08:37:02 -08:00
Vladimir Z Tomov 9cdb604796 Update of PA.ALF after remap.
Fixed lex ordering of maps_nodes.
Improved the PA AdaptLim kernels.
Cleaned debug code.
2026-02-27 13:10:31 -08:00
Vladimir Z Tomov c55e3fa7d2 debug wip 2026-02-25 18:22:42 -08:00
Vladimir Z Tomov 7b47ee4cf5 Merge branch 'master' into findpts-surface 2026-02-24 15:27:19 -08:00
Vladimir Z Tomov 75be9250a9 fixed mesh-optimizer.cpp 2026-02-16 15:46:56 -08:00
Mittal, Ketan 211470966c remove undeclared function definition 2026-02-13 13:00:13 -08:00
Mittal, Ketan be887d05a4 capture some const for lambda 2026-02-13 10:44:06 -08:00
Mittal, Ketan df386413a9 refactor host-device data movement 2026-02-12 15:30:01 -08:00
Mittal, Ketan fa7fbdf36b remove unusued variable to track newton iterations 2026-02-11 18:12:33 -08:00
Mittal, Ketan 30f1ad7c2c Merge branch 'findpts-surface' of https://github.com/mfem/mfem into findpts-surface 2026-02-11 18:05:21 -08:00
Mittal, Ketan 11debd6bf8 merge with master and resolve conflicts 2026-02-11 18:05:08 -08:00
Vladimir Z Tomov 44a783993e Working PA for 2D adaptive limiting. But there's still some diff with FA. 2026-02-11 13:43:48 -08:00
John Camier d9913262df Merge branch 'master' into hypremat 2026-02-07 13:21:01 -08:00
Nuno Nobre 4b9299188a Ensure hypre_CSRMatrixSetRownnz() allocs on host if ownership set to -1 2026-01-19 11:10:50 +00:00
Ketan Mittal e0b2ba5e54 Merge branch 'master' into findpts-surface 2026-01-06 12:54:33 -08:00
Vladimir Z Tomov ee859d044d cleanup 2025-12-22 18:16:52 -08:00
Vladimir Z Tomov cf8d1ddd10 working pa computation for Mult 2025-12-22 18:01:27 -08:00
Vladimir Z Tomov 987f1636aa energy adapt lim 2d 2025-12-17 10:12:40 -08:00
Vladimir Z Tomov 15faf0d225 Adaptive limiting - normalization, assemblePA, wip. 2025-12-17 09:58:28 -08:00
Ketan Mittal 1783050f9a Merge branch 'master' into findpts-surface 2025-12-16 12:57:09 -08:00
Will Pazner 42f2594430 Fix small issues 2025-12-04 14:00:33 -08:00
Will Pazner 5ba3e4de97 Fix sign comparison issues 2025-12-04 13:59:00 -08:00
Will Pazner 02938c9cce Small restructuring 2025-12-04 13:23:30 -08:00
Will Pazner 65313cd7d3 Edits to Doxygen comments 2025-12-04 13:23:30 -08:00
Will Pazner 5246f9dbc3 One more unification in Gmsh reader 2025-12-04 13:23:30 -08:00
Will Pazner 4d352bf726 Small optimizations and improvements for Gmsh mesh reader
- Don't need to finalize the topology twice.
- Use unordered_map instead of map for better performance.
    - Requires introducing hasher for pairs. A more general solution
      is provided in PR #4974. Once that is merged, the PairHasher
      introduced here can be removed.
- Simplify interface since 'curved' and 'read_gf' are not needed.
- Read the version number as string instead of floating point number.
2025-12-04 13:23:30 -08:00
Will Pazner eed3bc067f Combine common features in Gmsh 2.2 and 4.1 2025-12-04 13:23:30 -08:00
Will Pazner 7bd256e17c Refactor Gmsh 2.2 reader 2025-12-04 13:23:30 -08:00
Will Pazner 7dbad4da3e Add Gmsh v4 reader 2025-12-04 13:23:30 -08:00
Will Pazner fb85c34ca4 Simplify Gmsh mesh reader 2025-12-04 13:23:30 -08:00
Will Pazner 6401ca5847 Remove gmsh.hpp header 2025-12-04 13:23:30 -08:00
Will Pazner 496e240837 Move Mesh::ReadGmshMesh to separate file 2025-12-04 13:23:30 -08:00
Ketan Mittal 016ebe62cc Merge branch 'master' into findpts-surface 2025-12-03 11:32:09 -08:00
Ketan Mittal 28ab39cf96 Merge branch 'master' into findpts-surface 2025-12-02 09:41:11 -08:00
Mittal, Ketan 9b90a7980b minor 2025-11-21 15:36:26 -08:00
Mittal, Ketan cf2c43b5c7 clean up documentation 2025-11-21 15:35:36 -08:00
Mittal, Ketan 2cb6a6e899 minor 2025-11-21 15:25:56 -08:00
Mittal, Ketan 3ec6292520 document map 2025-11-21 15:24:14 -08:00
Mittal, Ketan 7011d623d3 fix shadow declaration 2025-11-21 15:09:21 -08:00
Mittal, Ketan d4326eddd3 add unit test for the grid maps 2025-11-21 14:47:19 -08:00
Mittal, Ketan 39dcdb18e2 remove some stuff from testing 2025-11-21 14:00:30 -08:00
Mittal, Ketan fb0abff1c2 clean up pfindpts 2025-11-21 12:59:55 -08:00
Mittal, Ketan 8a615b8742 make style 2025-11-21 12:56:10 -08:00
Mittal, Ketan f24d9d8c0d rename some methods 2025-11-21 12:55:51 -08:00
Mittal, Ketan 85cf7b41d5 Merge branch 'master' of https://github.com/mfem/mfem into findpts-surface 2025-11-21 12:36:56 -08:00
Mittal, Ketan dff07dd1e3 Merge branch 'findpts-surface' of https://github.com/mfem/mfem into findpts-surface 2025-11-21 12:36:48 -08:00
Mittal, Ketan f17c25caf0 documentation and minor refactor to reuse gridrange etc methods 2025-11-21 12:36:35 -08:00
Ketan Mittal 929c7baf16 Merge branch 'master' into findpts-surface 2025-11-13 13:07:38 -08:00
Ketan Mittal dfa845a91c Merge branch 'master' into findpts-surface 2025-11-12 14:51:51 -08:00
Mittal, Ketan 7fe9733e4d fix minor bug and add new file to mesh_headers 2025-11-11 12:11:33 -08:00
Mittal, Ketan 1b3c326784 document new class and add ifdef mpi guards 2025-11-11 11:59:48 -08:00
Mittal, Ketan 0fd42364ca fix lmap_nd usage 2025-11-11 10:59:50 -08:00
Mittal, Ketan 7e2c9641c2 merge with master and resolve conflicts 2025-11-11 09:38:10 -08:00
Mittal, Ketan a0d18d4d52 move some definitions to before they are used 2025-11-11 09:20:21 -08:00
Mittal, Ketan 7adea0556c get rid of some macros 2025-11-11 09:15:40 -08:00
Mittal, Ketan 1e8efef66b copyright 2025-11-10 13:13:41 -08:00
Mittal, Ketan f14747eead make style 2025-11-10 12:20:41 -08:00
Mittal, Ketan d1151c09a3 add release version checks 2025-11-10 12:20:32 -08:00
Mittal, Ketan a9501ed65f clean up, make style, and unify interpolate local since it depends on r-dim only 2025-11-09 15:46:48 -08:00
Mittal, Ketan d16deb42f5 Merge branch 'findpts-surface' of https://github.com/mfem/mfem into findpts-surface 2025-11-06 13:25:12 -08:00
Mittal, Ketan b8f88a6560 interpolation kernels 2025-11-06 13:24:56 -08:00
Ketan Mittal 1653781a9d minor 2025-11-03 13:16:07 -08:00
Mittal, Ketan fcff34045e 3D edges 2025-11-03 09:39:58 -08:00
Mittal, Ketan ae675a05ef clean up 2D edge meshes 2025-11-02 20:36:56 -08:00
Ketan Mittal 7f370e8193 fix bug in nodal coordinate memory assignment for 3D surface 2025-10-27 16:09:59 -07:00
Mittal, Ketan 870732a5aa minor 2025-10-27 13:55:07 -07:00
Mittal, Ketan 2bf4de6db4 initial commit 2025-10-27 10:58:50 -07:00
56 changed files with 9874 additions and 1877 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+2 -2
View File
@@ -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}}
+2
View File
@@ -15,6 +15,8 @@ Version 4.9.1 (development)
Discretization improvements
---------------------------
- 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,
+1
View File
@@ -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
+26
View File
@@ -137,6 +137,32 @@ int main(int argc, char *argv[])
}
}
mesh.EnsureNodes();
GridFunction *nodes = mesh.GetNodes();
GridFunction nodes2(nodes->FESpace());
nodes2 = *nodes; // 1-to-1 copy
VisItDataCollection vdc("check", &mesh);
vdc.RegisterField("d", nodes);
vdc.RegisterField("d2", &nodes2);
vdc.SetCycle(0);
vdc.Save();
// byNODES byVDIm shuffle
int nnode = nodes2.Size()/2;
for (int i = 0; i < nnode; i++)
{
for (int j = 0; j < dim; j++)
{
int xi = i + j*nnode;
int ni = j + i*dim;
nodes2[xi] = nodes->Elem(ni);
}
}
vdc.SetCycle(1);
vdc.Save();
// 5. Define a finite element space on the mesh. Here we use continuous
// Lagrange finite elements of the specified order. If order < 1, we
// instead use an isoparametric/isogeometric space.
+4
View File
@@ -171,8 +171,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
+3 -3
View File
@@ -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,
+2362 -144
View File
File diff suppressed because it is too large Load Diff
+343 -67
View File
@@ -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
+14 -12
View File
@@ -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
+14 -14
View File
@@ -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);
}
+725
View File
@@ -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
+733
View File
@@ -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
+157
View File
@@ -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
+19 -19
View File
@@ -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
+18 -19
View File
@@ -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
+66 -10
View File
@@ -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
View File
@@ -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);
+119
View File
@@ -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
+159
View File
@@ -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
+197
View File
@@ -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
+248
View File
@@ -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
+120
View File
@@ -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
+131
View File
@@ -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
+86
View File
@@ -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
+91
View File
@@ -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
View File
@@ -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;
+77
View File
@@ -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
+78
View File
@@ -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
View File
@@ -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>;
+39
View File
@@ -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
+2 -1
View File
@@ -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)
+414
View File
@@ -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
+199
View File
@@ -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
View File
@@ -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
-55
View File
@@ -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
+7 -2
View File
@@ -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 &&
@@ -7033,7 +7036,7 @@ void Mesh::EnsureNodes()
}
else // First order H1 mesh
{
SetCurvature(1, false, -1, Ordering::byVDIM);
SetCurvature(1, false, -1, Ordering::byNODES);//VDIM);
}
}
@@ -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);
+4 -1
View File
@@ -60,12 +60,15 @@ class CubitBlock;
}
#endif
namespace gmsh { class GmshReader; }
/// Mesh data type
class Mesh
{
friend class NCMesh;
friend class NURBSExtension;
friend class NCNURBSExtension;
friend class gmsh::GmshReader;
#ifdef MFEM_USE_MPI
friend class ParMesh;
friend class ParNCMesh;
@@ -362,7 +365,7 @@ protected:
void ReadNURBSMesh(std::istream &input, int &curved, int &read_gf,
bool spacing=false, bool nc=false);
void ReadInlineMesh(std::istream &input, bool generate_edges = false);
void ReadGmshMesh(std::istream &input, int &curved, int &read_gf);
void ReadGmshMesh(std::istream &input);
/* Note NetCDF (optional library) is used for reading cubit files */
#ifdef MFEM_USE_NETCDF
+1
View File
@@ -33,6 +33,7 @@
#include "nurbs.hpp"
#include "wedge.hpp"
#include "pyramid.hpp"
#include "bb_grid_map.hpp"
#ifdef MFEM_USE_MPI
#include "pncmesh.hpp"
File diff suppressed because it is too large Load Diff
+130 -66
View File
@@ -45,6 +45,10 @@
// findpts -m ../../data/fichera-mixed.mesh -o 3 -mo 2
// findpts -m ../../data/inline-pyramid.mesh -o 1 -mo 1
// findpts -m ../../data/tinyzoo-3d.mesh -o 1 -mo 1
// Surface meshes:
// findpts -m ../../data/ref-square.mesh -o 2 -mo 1 -random 1 -surf
// findpts -m ../../data/ref-cube.mesh -o 2 -mo 1 -random 1 -surf
// findpts -m ../../data/square-disc-p2.mesh -o 4 -mo 2 -random 1 -surf
#include "mfem.hpp"
#include "../common/mfem-common.hpp"
@@ -102,6 +106,9 @@ int main (int argc, char *argv[])
int point_ordering = 0;
int gf_ordering = 0;
bool mesh_prefinement = false;
int randomization = 0;
int npt = 100;
bool surface = false;
// Parse command-line options.
OptionsParser args(argc, argv);
@@ -135,6 +142,14 @@ int main (int argc, char *argv[])
args.AddOption(&mesh_prefinement, "-mpr", "--mesh-p-refinement", "-no-mpr",
"--no-mesh-p-refinement",
"Do random p refinements to mesh Nodes.");
args.AddOption(&randomization, "-random", "--random",
"0: generate points randomly in the bounding box of domain, "
"1: generate points randomly inside each element in mesh.");
args.AddOption(&npt, "-npt", "--npt",
"Number of points per element when -random 1 is used.");
args.AddOption(&surface, "-surf", "--surface", "-no-surf",
"--no-surface",
"Extract surface mesh from volume mesh.");
args.Parse();
if (!args.Good())
@@ -146,49 +161,72 @@ int main (int argc, char *argv[])
func_order = std::min(order, 2);
// Initialize and refine the starting mesh.
Mesh mesh(mesh_file, 1, 1, false);
for (int lev = 0; lev < rs_levels; lev++) { mesh.UniformRefinement(); }
const int dim = mesh.Dimension();
// Initialize and optionally extract the surface mesh.
Mesh input_mesh(mesh_file, 1, 1, false);
Mesh *mesh = surface ? nullptr : &input_mesh;
if (surface)
{
MFEM_VERIFY(input_mesh.bdr_attributes.Size() > 0,
"--surface requires a mesh with boundary attributes.");
mesh = new Mesh(SubMesh::CreateFromBoundary(input_mesh,
input_mesh.bdr_attributes));
}
MFEM_VERIFY(!(surface && prefinement),
"Surface interpolation does not support variable-order "
"solution spaces; use -no-pr with --surface.");
for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); }
const int dim = mesh->Dimension(),
sdim = mesh->SpaceDimension();
cout << "Mesh curvature of the original mesh: ";
if (mesh.GetNodes()) { cout << mesh.GetNodes()->OwnFEC()->Name(); }
if (mesh->GetNodes()) { cout << mesh->GetNodes()->OwnFEC()->Name(); }
else { cout << "(NONE)"; }
cout << endl;
// Mesh bounding box.
Vector pos_min, pos_max;
MFEM_VERIFY(mesh_poly_deg > 0, "The order of the mesh must be positive.");
mesh.GetBoundingBox(pos_min, pos_max, mesh_poly_deg);
if (hrefinement || prefinement || mesh_prefinement) { mesh.EnsureNCMesh(true); }
cout << "--- Generating equidistant point for:\n"
<< "x in [" << pos_min(0) << ", " << pos_max(0) << "]\n"
<< "y in [" << pos_min(1) << ", " << pos_max(1) << "]\n";
if (dim == 3)
mesh->GetBoundingBox(pos_min, pos_max, mesh_poly_deg);
if (hrefinement || prefinement || mesh_prefinement)
{
mesh->EnsureNCMesh(true);
}
cout << "--- Generating points for:\n"
<< "x in [" << pos_min(0) << ", " << pos_max(0) << "]\n";
if (sdim >= 2)
{
cout << "y in [" << pos_min(1) << ", " << pos_max(1) << "]" << std::endl;
}
if (sdim == 3)
{
cout << "z in [" << pos_min(2) << ", " << pos_max(2) << "]\n";
}
// Random h-refinements to mesh
if (hrefinement) { mesh.RandomRefinement(0.5); }
if (hrefinement) { mesh->RandomRefinement(0.5); }
// Curve the mesh based on the chosen polynomial degree.
H1_FECollection fecm(mesh_poly_deg, dim);
FiniteElementSpace fespace(&mesh, &fecm, dim);
mesh.SetNodalFESpace(&fespace);
FiniteElementSpace fespace(mesh, &fecm, sdim);
mesh->SetNodalFESpace(&fespace);
GridFunction Nodes(&fespace);
mesh.SetNodalGridFunction(&Nodes);
mesh->SetNodalGridFunction(&Nodes);
cout << "Mesh curvature of the curved mesh: " << fecm.Name() << endl;
if (mesh_prefinement)
{
Array<pRefinement> refs;
for (int e = 0; e < mesh.GetNE(); e++)
for (int e = 0; e < mesh->GetNE(); e++)
{
if ((double) rand() / RAND_MAX < 0.2)
{
refs.Append(pRefinement(e,1));
}
}
std::cout << refs.Size() << " elements will be p-refined for the mesh." <<
std::endl;
fespace.PRefineAndUpdate(refs);
Nodes.Update();
}
@@ -210,28 +248,28 @@ int main (int argc, char *argv[])
{
fec = new RT_FECollection(order, dim);
ncomp = 1;
vec_dim = dim;
vec_dim = sdim;
cout << "H(div)-GridFunction\n";
}
else if (fieldtype == 3)
{
fec = new ND_FECollection(order, dim);
ncomp = 1;
vec_dim = dim;
vec_dim = sdim;
cout << "H(curl)-GridFunction\n";
}
else
{
MFEM_ABORT("Invalid field type.");
}
FiniteElementSpace sc_fes(&mesh, fec, ncomp, gf_ordering);
FiniteElementSpace sc_fes(mesh, fec, ncomp, gf_ordering);
GridFunction field_vals(&sc_fes);
// Random p-refinements to the solution field
if (prefinement)
{
Array<pRefinement> refs;
for (int e = 0; e < mesh.GetNE(); e++)
for (int e = 0; e < mesh->GetNE(); e++)
{
if ((double) rand() / RAND_MAX < 0.5)
{
@@ -249,16 +287,16 @@ int main (int argc, char *argv[])
if (mesh_prefinement && visualization)
{
mesh.SetNodalGridFunction(mesh_nodes_pref);
mesh->SetNodalGridFunction(mesh_nodes_pref);
VisualizeFESpacePolynomialOrder(fespace, "Mesh Polynomial Order", 400);
mesh.SetNodalGridFunction(&Nodes);
mesh->SetNodalGridFunction(&Nodes);
}
if (prefinement && visualization)
{
mesh.SetNodalGridFunction(mesh_nodes_pref);
mesh->SetNodalGridFunction(mesh_nodes_pref);
VisualizeFESpacePolynomialOrder(sc_fes, "Solution Polynomial Order", 800);
mesh.SetNodalGridFunction(&Nodes);
mesh->SetNodalGridFunction(&Nodes);
}
// Project the GridFunction using VectorFunctionCoefficient.
@@ -273,64 +311,81 @@ int main (int argc, char *argv[])
// Display the mesh and the field through glvis.
if (visualization)
{
if (mesh_prefinement) { mesh.SetNodalGridFunction(mesh_nodes_pref); }
if (mesh_prefinement) { mesh->SetNodalGridFunction(mesh_nodes_pref); }
socketstream vis1;
common::VisualizeField(vis1, "localhost", 19916, *field_vals_pref,
"Solution",
0, 0, 400, 400, "RmjA*****");
if (mesh_prefinement) { mesh.SetNodalGridFunction(&Nodes); }
if (mesh_prefinement) { mesh->SetNodalGridFunction(&Nodes); }
}
// Generate equidistant points in physical coordinates over the whole mesh.
// Note that some points might be outside, if the mesh is not a box. Note
// also that all tasks search the same points (not mandatory).
const int pts_cnt_1D = 25;
int pts_cnt = pow(pts_cnt_1D, dim);
Vector vxyz(pts_cnt * dim);
if (dim == 2)
// Generate random points in physical coordinates over the whole mesh.
// Note that some points might be outside if the mesh is not a box.
int pts_cnt = npt;
Vector vxyz;
int npt_face_per_elem = 4; // number of pts on faces when randomization != 0
int npt_total_face = 0;
if (randomization == 0)
{
L2_QuadrilateralElement el(pts_cnt_1D - 1, BasisType::ClosedUniform);
const IntegrationRule &ir = el.GetNodes();
for (int i = 0; i < ir.GetNPoints(); i++)
vxyz.SetSize(pts_cnt * sdim);
vxyz.Randomize(1);
// Scale based on min/max dimensions
for (int i = 0; i < pts_cnt; i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
if (point_ordering == Ordering::byNODES)
for (int d = 0; d < sdim; d++)
{
vxyz(i) = pos_min(0) + ip.x * (pos_max(0)-pos_min(0));
vxyz(pts_cnt + i) = pos_min(1) + ip.y * (pos_max(1)-pos_min(1));
}
else
{
vxyz(i*dim + 0) = pos_min(0) + ip.x * (pos_max(0)-pos_min(0));
vxyz(i*dim + 1) = pos_min(1) + ip.y * (pos_max(1)-pos_min(1));
if (point_ordering == Ordering::byNODES)
{
vxyz(i + d*pts_cnt) =
pos_min(d) + vxyz(i + d*pts_cnt) * (pos_max(d) - pos_min(d));
}
else
{
vxyz(i*sdim + d) =
pos_min(d) + vxyz(i*sdim + d) * (pos_max(d) - pos_min(d));
}
}
}
}
else
else // randomization == 1
{
L2_HexahedronElement el(pts_cnt_1D - 1, BasisType::ClosedUniform);
const IntegrationRule &ir = el.GetNodes();
for (int i = 0; i < ir.GetNPoints(); i++)
pts_cnt = npt * mesh->GetNE();
vxyz.SetSize(pts_cnt * sdim);
for (int i = 0; i < mesh->GetNE(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
if (point_ordering == Ordering::byNODES)
ElementTransformation *transf = mesh->GetElementTransformation(i);
const Geometry::Type geom = mesh->GetElementGeometry(i);
for (int j = 0; j < npt; j++)
{
vxyz(i) = pos_min(0) + ip.x * (pos_max(0)-pos_min(0));
vxyz(pts_cnt + i) = pos_min(1) + ip.y * (pos_max(1)-pos_min(1));
vxyz(2*pts_cnt + i) = pos_min(2) + ip.z * (pos_max(2)-pos_min(2));
}
else
{
vxyz(i*dim + 0) = pos_min(0) + ip.x * (pos_max(0)-pos_min(0));
vxyz(i*dim + 1) = pos_min(1) + ip.y * (pos_max(1)-pos_min(1));
vxyz(i*dim + 2) = pos_min(2) + ip.z * (pos_max(2)-pos_min(2));
IntegrationPoint ip;
Geometry::GetRandomPoint(geom, ip);
if (j < npt_face_per_elem)
{
ip.x = 0.0; // force point to be on the face
npt_total_face++;
}
Vector pos_i(sdim);
transf->Transform(ip, pos_i);
for (int d = 0; d < sdim; d++)
{
if (point_ordering == Ordering::byNODES)
{
vxyz(j + npt*i + d*pts_cnt) = pos_i(d);
}
else
{
vxyz((j + npt*i)*sdim + d) = pos_i(d);
}
}
}
}
}
// Find and Interpolate FE function values on the desired points.
Vector interp_vals(pts_cnt*vec_dim);
FindPointsGSLIB finder(mesh);
FindPointsGSLIB finder(*mesh);
finder.SetDistanceToleranceForPointsFoundOnBoundary(10);
finder.SetL2AvgType(FindPointsGSLIB::NONE);
finder.Interpolate(vxyz, field_vals, interp_vals, point_ordering);
Array<unsigned int> code_out = finder.GetCode();
@@ -338,7 +393,7 @@ int main (int argc, char *argv[])
int face_pts = 0, not_found = 0, found = 0;
double error = 0.0, max_err = 0.0, max_dist = 0.0;
Vector pos(dim);
Vector pos(sdim);
for (int j = 0; j < vec_dim; j++)
{
for (int i = 0; i < pts_cnt; i++)
@@ -346,11 +401,11 @@ int main (int argc, char *argv[])
if (code_out[i] < 2)
{
if (j == 0) { found++; }
for (int d = 0; d < dim; d++)
for (int d = 0; d < sdim; d++)
{
pos(d) = point_ordering == Ordering::byNODES ?
vxyz(d*pts_cnt + i) :
vxyz(i*dim + d);
vxyz(i*sdim + d);
}
Vector exact_val(vec_dim);
F_exact(pos, exact_val);
@@ -370,10 +425,19 @@ int main (int argc, char *argv[])
<< "\nFound points: " << found
<< "\nMax interp error: " << max_err
<< "\nMax dist (of found): " << max_dist
<< "\nPoints not found: " << not_found
<< "\nPoints on faces: " << face_pts << endl;
<< "\nPoints not found: " << not_found;
if (randomization == 1)
{
cout << "\nPoints on faces: " << face_pts << " out of "
<< npt_total_face << endl;
}
else
{
cout << "\nPoints on faces: " << face_pts << endl;
}
delete fec;
if (surface) { delete mesh; }
return 0;
}
+77 -53
View File
@@ -49,7 +49,10 @@
// mpirun -np 2 pfindpts -m ../../data/inline-quad.mesh -o 3 -mo 2 -random 1 -d debug
// mpirun -np 2 pfindpts -m ../../data/amr-quad.mesh -rs 1 -o 4 -mo 2 -random 1 -npt 100 -d debug
// mpirun -np 2 pfindpts -m ../../data/inline-hex.mesh -o 3 -mo 2 -random 1 -d debug
// Surface meshes:
// mpirun -np 4 pfindpts -m ../../data/square-disc-p2.mesh -o 4 -mo 2 -vis -random 1 -surf
// mpirun -np 4 pfindpts -m ../../data/star-q3.mesh -o 6 -mo 3 -vis -random 1 -surf
// mpirun -np 4 pfindpts -m ../../data/fichera-q2.mesh -o 6 -mo 3 -vis -random 1 -surf
#include "mfem.hpp"
#include "../common/mfem-common.hpp"
@@ -98,7 +101,7 @@ int main (int argc, char *argv[])
const char *devopt = "cpu";
int randomization = 0;
int npt = 100; //points per proc
int visport = 19916;
bool surface = false;
// Parse command-line options.
OptionsParser args(argc, argv);
@@ -139,7 +142,9 @@ int main (int argc, char *argv[])
"1: generate points randomly inside each element in mesh.");
args.AddOption(&npt, "-npt", "--npt",
"# points / rank initialized on entire mesh (random = 0) or every element (random = 1).");
args.AddOption(&visport, "-p", "--send-port", "Socket for GLVis.");
args.AddOption(&surface, "-surf", "--surface", "-no-surf",
"--no-surface",
"Extract surface mesh from volume mesh.");
args.Parse();
if (!args.Good())
@@ -155,17 +160,19 @@ int main (int argc, char *argv[])
func_order = std::min(order, 2);
// Initialize and refine the starting mesh.
Mesh *mesh = new Mesh(mesh_file, 1, 1, false);
for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); }
const int dim = mesh->Dimension();
if (mesh->GetNumGeometries(dim) != 1 ||
(mesh->GetElementType(0)!=Element::QUADRILATERAL &&
mesh->GetElementType(0) != Element::HEXAHEDRON))
// Initialize and extract surface mesh if requested.
Mesh *input_mesh = new Mesh(mesh_file, 1, 1, false);
Mesh *mesh = surface ? nullptr : input_mesh;
if (surface)
{
randomization = 0;
MFEM_VERIFY(input_mesh->bdr_attributes.Size() > 0,
"--surface requires a mesh with boundary attributes.");
mesh = new Mesh(SubMesh::CreateFromBoundary(*input_mesh,
input_mesh->bdr_attributes));
}
for (int lev = 0; lev < rs_levels; lev++) { mesh->UniformRefinement(); }
const int dim = mesh->Dimension(),
sdim = mesh->SpaceDimension();
if (myid == 0)
{
@@ -182,9 +189,12 @@ int main (int argc, char *argv[])
if (myid == 0)
{
cout << "--- Generating points for:\n"
<< "x in [" << pos_min(0) << ", " << pos_max(0) << "]\n"
<< "y in [" << pos_min(1) << ", " << pos_max(1) << "]" << std::endl;
if (dim == 3)
<< "x in [" << pos_min(0) << ", " << pos_max(0) << "]\n";
if (sdim >= 2)
{
cout << "y in [" << pos_min(1) << ", " << pos_max(1) << "]" << std::endl;
}
if (sdim == 3)
{
cout << "z in [" << pos_min(2) << ", " << pos_max(2) << "]" << std::endl;
}
@@ -192,8 +202,13 @@ int main (int argc, char *argv[])
// Distribute the mesh.
if (hrefinement) { mesh->EnsureNCMesh(); }
ParMesh pmesh(MPI_COMM_WORLD, *mesh);
if (randomization == 0) { delete mesh; }
ParMesh pmesh(MPI_COMM_WORLD, *mesh, nullptr,
(dim == 1 && sdim == 3) ? 0 : 1);
if (randomization == 0)
{
delete mesh;
if (surface) { delete input_mesh; }
}
else
{
// we will need mesh nodal space later
@@ -206,7 +221,7 @@ int main (int argc, char *argv[])
// Curve the mesh based on the chosen polynomial degree.
H1_FECollection fecm(mesh_poly_deg, dim);
ParFiniteElementSpace pfespace(&pmesh, &fecm, dim);
ParFiniteElementSpace pfespace(&pmesh, &fecm, sdim);
pmesh.SetNodalFESpace(&pfespace);
ParGridFunction x(&pfespace);
pmesh.SetNodalGridFunction(&x);
@@ -234,14 +249,14 @@ int main (int argc, char *argv[])
{
fec = new RT_FECollection(order, dim);
ncomp = 1;
vec_dim = dim;
vec_dim = sdim;
if (myid == 0) { cout << "H(div)-GridFunction" << std::endl; }
}
else if (fieldtype == 3)
{
fec = new ND_FECollection(order, dim);
ncomp = 1;
vec_dim = dim;
vec_dim = sdim;
if (myid == 0) { cout << "H(curl)-GridFunction" << std::endl; }
}
else
@@ -260,13 +275,13 @@ int main (int argc, char *argv[])
{
char vishost[] = "localhost";
socketstream sout;
sout.open(vishost, visport);
sout.open(vishost, 19916);
if (!sout)
{
if (myid == 0)
{
cout << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << endl;
<< vishost << ':' << 19916 << endl;
}
}
else
@@ -274,8 +289,11 @@ int main (int argc, char *argv[])
sout << "parallel " << num_procs << " " << myid << "\n";
sout.precision(8);
sout << "solution\n" << pmesh << field_vals;
if (dim == 2) { sout << "keys RmjA*****\n"; }
if (dim == 3) { sout << "keys mA\n"; }
if (sdim == 2) { sout << "keys RmjA*****\n"; }
if (sdim == 3) { sout << "keys mA\n"; }
sout << "window_title 'Solution'\n"
<< "window_geometry "
<< 0 << " " << 0 << " " << 400 << " " << 400 << "\n";
sout << flush;
}
}
@@ -285,16 +303,17 @@ int main (int argc, char *argv[])
int pts_cnt = npt;
Vector vxyz;
vxyz.UseDevice(!cpu_mode);
int npt_face_per_elem = 4; // number of pts on el faces for randomization != 0
int npt_face_per_elem = 4; // number of pts on faces when randomization != 0
int npt_total_face = 0;
if (randomization == 0)
{
vxyz.SetSize(pts_cnt * dim);
vxyz.SetSize(pts_cnt * sdim);
vxyz.Randomize(myid+1);
// Scale based on min/max dimensions
for (int i = 0; i < pts_cnt; i++)
{
for (int d = 0; d < dim; d++)
for (int d = 0; d < sdim; d++)
{
if (point_ordering == Ordering::byNODES)
{
@@ -303,8 +322,8 @@ int main (int argc, char *argv[])
}
else
{
vxyz(i*dim + d) =
pos_min(d) + vxyz(i*dim + d) * (pos_max(d) - pos_min(d));
vxyz(i*sdim + d) =
pos_min(d) + vxyz(i*sdim + d) * (pos_max(d) - pos_min(d));
}
}
}
@@ -312,30 +331,24 @@ int main (int argc, char *argv[])
else // randomization == 1
{
pts_cnt = npt*nelemglob;
vxyz.SetSize(pts_cnt * dim);
vxyz.SetSize(pts_cnt * sdim);
for (int i=0; i<mesh->GetNE(); i++)
{
const FiniteElementSpace *s_fespace = mesh->GetNodalFESpace();
ElementTransformation *transf = s_fespace->GetElementTransformation(i);
Vector pos_ref1(npt*dim);
pos_ref1.Randomize((myid+1)*17.0);
const Geometry::Type geom = mesh->GetElementGeometry(i);
for (int j=0; j<npt; j++)
{
IntegrationPoint ip;
ip.x = pos_ref1(j*dim + 0);
ip.y = pos_ref1(j*dim + 1);
if (dim == 3)
{
ip.z = pos_ref1(j*dim + 2);
}
Geometry::GetRandomPoint(geom, ip);
if (j < npt_face_per_elem)
{
ip.x = 0.0; // force point to be on the face
npt_total_face++;
}
Vector pos_i(dim);
Vector pos_i(sdim);
transf->Transform(ip, pos_i);
for (int d=0; d<dim; d++)
for (int d=0; d<sdim; d++)
{
if (point_ordering == Ordering::byNODES)
{
@@ -343,18 +356,20 @@ int main (int argc, char *argv[])
}
else
{
vxyz((j + npt*i)*dim + d) = pos_i(d);
vxyz((j + npt*i)*sdim + d) = pos_i(d);
}
}
}
}
}
if ( (myid != 0) && (search_on_rank_0) )
{
pts_cnt = 0;
vxyz.Destroy();
npt_total_face = 0;
}
MPI_Allreduce(MPI_IN_PLACE, &npt_total_face, 1, MPI_INT, MPI_SUM,
pmesh.GetComm());
// Find and Interpolate FE function values on the desired points.
Vector interp_vals(pts_cnt*vec_dim);
@@ -375,28 +390,31 @@ int main (int argc, char *argv[])
Array<unsigned int> code_out = finder.GetCode();
Array<unsigned int> task_id_out = finder.GetProc();
Vector dist_p_out = finder.GetDist();
Vector rst = finder.GetReferencePosition();
auto h_code_out = code_out.HostRead();
auto h_task_id_out = task_id_out.HostRead();
auto h_dist_p_out = dist_p_out.HostRead();
int face_pts = 0, not_found = 0, found_loc = 0, found_away = 0;
double error = 0.0, max_error = 0.0, max_dist = 0.0;
Vector pos(dim);
Vector pos(sdim);
for (int j = 0; j < vec_dim; j++)
{
for (int i = 0; i < pts_cnt; i++)
{
if (j == 0)
{
(task_id_out[i] == (unsigned)myid) ? found_loc++ : found_away++;
(h_task_id_out[i] == (unsigned)myid) ? found_loc++ : found_away++;
}
if (code_out[i] < 2)
if (h_code_out[i] < 2)
{
for (int d = 0; d < dim; d++)
for (int d = 0; d < sdim; d++)
{
pos(d) = point_ordering == Ordering::byNODES ?
vxyz(d*pts_cnt + i) :
vxyz(i*dim + d);
vxyz(i*sdim + d);
}
Vector exact_val(vec_dim);
F_exact(pos, exact_val);
@@ -404,8 +422,8 @@ int main (int argc, char *argv[])
fabs(exact_val(j) - interp_vals[i + j*pts_cnt]) :
fabs(exact_val(j) - interp_vals[i*vec_dim + j]);
max_error = std::max(max_error, error);
max_dist = std::max(max_dist, dist_p_out(i));
if (code_out[i] == 1 && j == 0) { face_pts++; }
max_dist = std::max(max_dist, h_dist_p_out[i]);
if (h_code_out[i] == 1 && j == 0) { face_pts++; }
}
else { if (j == 0) { not_found++; } }
}
@@ -435,14 +453,20 @@ int main (int argc, char *argv[])
<< "\nFound locally on ranks: " << found_loc
<< "\nFound on other tasks: " << found_away
<< "\nPoints not found: " << not_found
<< "\nPoints on faces: " << face_pts
<< "\nPoints on faces: " << face_pts << " out of "
<< npt_total_face
<< "\nMax interp error: " << max_error
<< "\nMax dist (of found): " << max_dist
<< endl;
}
delete fec;
if (randomization != 0) { delete mesh; }
if (randomization != 0)
{
delete mesh;
if (surface) { delete input_mesh; }
}
return 0;
}
+1
View File
@@ -62,6 +62,7 @@ int main(int argc, char *argv[])
Mpi::Init(argc, argv);
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
int order = 1;
const char *device_config = "cpu";
+1
View File
@@ -19,6 +19,7 @@ set(MESH_FILES
square01.mesh
square01-tri.mesh
stretched2D.mesh
stretched3D.mesh
)
# Add a target to copy the mesh files from the source directory; used by sample
+1
View File
@@ -36,6 +36,7 @@ int main (int argc, char *argv[])
// Initialize MPI.
Mpi::Init();
int myid = Mpi::WorldRank();
Hypre::Init();
const char *mesh_file = "square01.mesh";
int rs_levels = 2;
+1 -1
View File
@@ -58,7 +58,7 @@ lib-common:
# building out-of-source:
ifneq ($(SRC),)
MESH_FILES = amr-quad-q2.mesh blade.mesh cube.mesh cube-tet.mesh icf.mesh\
jagged.mesh square01.mesh square01-tri.mesh stretched2D.mesh
jagged.mesh square01.mesh square01-tri.mesh stretched2D.mesh stretched3D.mesh
$(MESH_FILES): %: $(SRC)%
ln -sf $(<) .
mesh-optimizer pmesh-optimizer pmesh-fitting fit-node-position: | $(MESH_FILES)
+7 -8
View File
@@ -73,7 +73,8 @@
// * mesh-optimizer -m ../../../data/periodic/per-amr-square.mesh -o 2 -mid 94 -tid 5 -ni 50 -qo 4 -nor -pa
//
// Adaptive limiting:
// mesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5
// mesh-optimizer -m stretched2D.mesh -rs 1 -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 1.0
// mesh-optimizer -m stretched3D.mesh -rs 2 -o 2 -mid 302 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 2.0 -pa
// Adaptive limiting through the L-BFGS solver:
// mesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 400 -qo 5 -nor -vl 1 -alc 0.5 -st 1 -rtol 1e-8
//
@@ -876,8 +877,6 @@ int main(int argc, char *argv[])
AdaptivityEvaluator *adapt_lim_eval = NULL;
if (adapt_lim_const > 0.0)
{
MFEM_VERIFY(pa == false, "PA is not implemented for adaptive limiting");
FunctionCoefficient adapt_lim_gf0_coeff(adapt_lim_fun);
adapt_lim_gf0.ProjectCoefficient(adapt_lim_gf0_coeff);
@@ -893,12 +892,12 @@ int main(int argc, char *argv[])
else { MFEM_ABORT("Bad interpolation option."); }
tmop_integ->EnableAdaptiveLimiting(adapt_lim_gf0, adapt_lim_coeff,
*adapt_lim_eval);
*adapt_lim_eval, 1.0);
if (visualization)
{
socketstream vis1;
common::VisualizeField(vis1, "localhost", 19916, adapt_lim_gf0, "Zeta 0",
300, 600, 300, 300);
common::VisualizeField(vis1, "localhost", 19916, adapt_lim_gf0,
"Zeta 0 - initial mesh", 300, 600, 300, 300);
}
}
@@ -1223,8 +1222,8 @@ int main(int argc, char *argv[])
if (adapt_lim_const > 0.0 && visualization)
{
socketstream vis0;
common::VisualizeField(vis0, "localhost", 19916, adapt_lim_gf0, "Xi 0",
600, 600, 300, 300);
common::VisualizeField(vis0, "localhost", 19916, adapt_lim_gf0,
"Zeta 0 - final mesh", 600, 600, 300, 300);
}
// Visualize the mesh displacement.
+13 -4
View File
@@ -436,11 +436,20 @@ real_t weight_fun(const Vector &x)
// Used for the adaptive limiting examples.
real_t adapt_lim_fun(const Vector &x)
{
const real_t xc = x(0) - 0.1, yc = x(1) - 0.2;
const real_t r = sqrt(xc*xc + yc*yc);
real_t r1 = 0.45; real_t r2 = 0.55; real_t sf=30.0;
real_t val = 0.5*(1+std::tanh(sf*(r-r1))) - 0.5*(1+std::tanh(sf*(r-r2)));
// Bump between these rad values, with sf sharpness.
real_t r1 = 0.45, r2 = 0.55, sf=30.0, r;
if (x.Size() == 2)
{
const real_t xc = x(0) - 0.1, yc = x(1) - 0.2;
r = sqrt(xc*xc + yc*yc);
}
else
{
const real_t xc = x(0) - 0.9, yc = x(1) - 0.2, zc = x(2) - 0.0;
r = sqrt(xc*xc + yc*yc + zc*zc);
}
real_t val = 0.5*(1+std::tanh(sf*(r-r1))) - 0.5*(1+std::tanh(sf*(r-r2)));
val = std::max((real_t) 0.,val);
val = std::min((real_t) 1.,val);
return val;
+8 -9
View File
@@ -75,9 +75,10 @@
// * mpirun -np 4 pmesh-optimizer -m ../../../data/periodic/per-amr-square.mesh -o 2 -mid 94 -tid 5 -ni 50 -qo 4 -nor -pa
//
// Adaptive limiting:
// mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 0.5
// mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -rs 1 -o 2 -mid 2 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 1.0
// mpirun -np 8 pmesh-optimizer -m stretched3D.mesh -rs 2 -o 2 -mid 302 -tid 1 -ni 50 -qo 5 -nor -vl 1 -alc 2.0 -pa
// Adaptive limiting through the L-BFGS solver:
// mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 400 -qo 5 -nor -vl 1 -alc 0.5 -st 1 -rtol 1e-8
// mpirun -np 4 pmesh-optimizer -m stretched2D.mesh -o 2 -mid 2 -tid 1 -ni 400 -qo 5 -nor -vl 1 -alc 1.0 -st 1 -rtol 1e-8
//
// Blade shape:
// mpirun -np 4 pmesh-optimizer -m blade.mesh -o 4 -mid 2 -tid 1 -ni 30 -ls 3 -art 1 -bnd -qt 1 -qo 8
@@ -913,8 +914,6 @@ int main (int argc, char *argv[])
AdaptivityEvaluator *adapt_lim_eval = NULL;
if (adapt_lim_const > 0.0)
{
MFEM_VERIFY(pa == false, "PA is not implemented for adaptive limiting");
FunctionCoefficient adapt_lim_gf0_coeff(adapt_lim_fun);
adapt_lim_gf0.ProjectCoefficient(adapt_lim_gf0_coeff);
@@ -930,12 +929,12 @@ int main (int argc, char *argv[])
else { MFEM_ABORT("Bad interpolation option."); }
tmop_integ->EnableAdaptiveLimiting(adapt_lim_gf0, adapt_lim_coeff,
*adapt_lim_eval);
*adapt_lim_eval, 1.0);
if (visualization)
{
socketstream vis1;
common::VisualizeField(vis1, "localhost", 19916, adapt_lim_gf0, "Zeta 0",
300, 600, 300, 300);
common::VisualizeField(vis1, "localhost", 19916, adapt_lim_gf0,
"Zeta 0 - initial mesh", 300, 600, 300, 300);
}
}
@@ -1275,8 +1274,8 @@ int main (int argc, char *argv[])
if (adapt_lim_const > 0.0 && visualization)
{
socketstream vis0;
common::VisualizeField(vis0, "localhost", 19916, adapt_lim_gf0, "Xi 0",
600, 600, 300, 300);
common::VisualizeField(vis0, "localhost", 19916, adapt_lim_gf0,
"Zeta 0 - final mesh", 600, 600, 300, 300);
}
// Visualize the mesh displacement.
+568
View File
@@ -0,0 +1,568 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see fem/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
# PYRAMID = 7
#
dimension
3
elements
64
1 5 0 27 81 30 35 82 117 85
1 5 27 1 28 81 82 36 83 117
1 5 81 28 4 29 117 83 37 84
1 5 30 81 29 3 85 117 84 38
1 5 35 82 117 85 9 31 86 34
1 5 82 36 83 117 31 10 32 86
1 5 117 83 37 84 86 32 13 33
1 5 85 117 84 38 34 86 33 12
1 5 3 29 87 41 38 84 118 90
1 5 29 4 39 87 84 37 88 118
1 5 87 39 7 40 118 88 45 89
1 5 41 87 40 6 90 118 89 46
1 5 38 84 118 90 12 33 91 44
1 5 84 37 88 118 33 13 42 91
1 5 118 88 45 89 91 42 16 43
1 5 90 118 89 46 44 91 43 15
1 5 12 33 91 44 51 92 119 95
1 5 33 13 42 91 92 52 93 119
1 5 91 42 16 43 119 93 53 94
1 5 44 91 43 15 95 119 94 54
1 5 51 92 119 95 21 47 96 50
1 5 92 52 93 119 47 22 48 96
1 5 119 93 53 94 96 48 25 49
1 5 95 119 94 54 50 96 49 24
1 5 9 31 86 34 58 97 120 99
1 5 31 10 32 86 97 59 98 120
1 5 86 32 13 33 120 98 52 92
1 5 34 86 33 12 99 120 92 51
1 5 58 97 120 99 18 55 100 57
1 5 97 59 98 120 55 19 56 100
1 5 120 98 52 92 100 56 22 47
1 5 99 120 92 51 57 100 47 21
1 5 10 60 101 32 59 102 121 98
1 5 60 11 61 101 102 66 103 121
1 5 101 61 14 62 121 103 67 104
1 5 32 101 62 13 98 121 104 52
1 5 59 102 121 98 19 63 105 56
1 5 102 66 103 121 63 20 64 105
1 5 121 103 67 104 105 64 23 65
1 5 98 121 104 52 56 105 65 22
1 5 13 62 106 42 52 104 122 93
1 5 62 14 68 106 104 67 107 122
1 5 106 68 17 69 122 107 72 108
1 5 42 106 69 16 93 122 108 53
1 5 52 104 122 93 22 65 109 48
1 5 104 67 107 122 65 23 70 109
1 5 122 107 72 108 109 70 26 71
1 5 93 122 108 53 48 109 71 25
1 5 4 73 110 39 37 111 123 88
1 5 73 5 74 110 111 76 112 123
1 5 110 74 8 75 123 112 77 113
1 5 39 110 75 7 88 123 113 45
1 5 37 111 123 88 13 62 106 42
1 5 111 76 112 123 62 14 68 106
1 5 123 112 77 113 106 68 17 69
1 5 88 123 113 45 42 106 69 16
1 5 1 78 114 28 36 115 124 83
1 5 78 2 79 114 115 80 116 124
1 5 114 79 5 73 124 116 76 111
1 5 28 114 73 4 83 124 111 37
1 5 36 115 124 83 10 60 101 32
1 5 115 80 116 124 60 11 61 101
1 5 124 116 76 111 101 61 14 62
1 5 83 124 111 37 32 101 62 13
boundary
96
3 3 0 30 81 27
3 3 30 3 29 81
3 3 81 29 4 28
3 3 27 81 28 1
3 3 1 28 114 78
3 3 28 4 73 114
3 3 114 73 5 79
3 3 78 114 79 2
3 3 3 41 87 29
3 3 41 6 40 87
3 3 87 40 7 39
3 3 29 87 39 4
3 3 4 39 110 73
3 3 39 7 75 110
3 3 110 75 8 74
3 3 73 110 74 5
3 3 18 55 100 57
3 3 55 19 56 100
3 3 100 56 22 47
3 3 57 100 47 21
3 3 19 63 105 56
3 3 63 20 64 105
3 3 105 64 23 65
3 3 56 105 65 22
3 3 21 47 96 50
3 3 47 22 48 96
3 3 96 48 25 49
3 3 50 96 49 24
3 3 22 65 109 48
3 3 65 23 70 109
3 3 109 70 26 71
3 3 48 109 71 25
1 3 0 35 85 30
1 3 35 9 34 85
1 3 85 34 12 38
1 3 30 85 38 3
1 3 3 38 90 41
1 3 38 12 44 90
1 3 90 44 15 46
1 3 41 90 46 6
1 3 9 58 99 34
1 3 58 18 57 99
1 3 99 57 21 51
1 3 34 99 51 12
1 3 12 51 95 44
1 3 51 21 50 95
1 3 95 50 24 54
1 3 44 95 54 15
1 3 2 79 116 80
1 3 79 5 76 116
1 3 116 76 14 61
1 3 80 116 61 11
1 3 5 74 112 76
1 3 74 8 77 112
1 3 112 77 17 68
1 3 76 112 68 14
1 3 11 61 103 66
1 3 61 14 67 103
1 3 103 67 23 64
1 3 66 103 64 20
1 3 14 68 107 67
1 3 68 17 72 107
1 3 107 72 26 70
1 3 67 107 70 23
2 3 0 27 82 35
2 3 27 1 36 82
2 3 82 36 10 31
2 3 35 82 31 9
2 3 9 31 97 58
2 3 31 10 59 97
2 3 97 59 19 55
2 3 58 97 55 18
2 3 1 78 115 36
2 3 78 2 80 115
2 3 115 80 11 60
2 3 36 115 60 10
2 3 10 60 102 59
2 3 60 11 66 102
2 3 102 66 20 63
2 3 59 102 63 19
2 3 6 46 89 40
2 3 46 15 43 89
2 3 89 43 16 45
2 3 40 89 45 7
2 3 15 54 94 43
2 3 54 24 49 94
2 3 94 49 25 53
2 3 43 94 53 16
2 3 7 45 113 75
2 3 45 16 69 113
2 3 113 69 17 77
2 3 75 113 77 8
2 3 16 53 108 69
2 3 53 25 71 108
2 3 108 71 26 72
2 3 69 108 72 17
vertices
125
nodes
FiniteElementSpace
FiniteElementCollection: H1_3D_P1
VDim: 3
Ordering: 0
0
0.5
1
0
0.5
1
0
0.5
1
0
0.5
1
0
0.5
1
0
0.5
1
0
0.5
1
0
0.5
1
0
0.5
1
0.25
0.5
0.25
0
0.25
0.5
0.25
0
0
0.5
0.5
0
0.5
0.25
0
0.5
0.25
0
0.5
0
0.25
0.5
0.25
0
0
0.5
0.5
0
0.25
0.5
0
0
0.5
0.75
1
0.75
0.75
1
0.75
1
1
1
0.75
1
0.75
1
0.75
1
0.75
1
1
0.75
1
1
0.25
0.25
0.5
0.25
0
0.25
0.25
0.5
0.25
0
0.25
0.25
0.5
0.25
0
0.25
0.25
0.5
0
0.25
0.75
0.75
1
0.75
0.75
0.75
1
0.75
0.75
0.75
0.75
1
0.75
0.75
0.75
1
0.25
0.25
0.25
0.25
0.75
0.75
0.75
0.75
0
0
0
0.25
0.25
0.25
1
1
1
0
0
0
0.25
0.25
0.25
1
1
1
0
0
0
0.25
0.25
0.25
1
1
1
0
0.0625
0.25
0.0625
0
0.0625
0.25
0.0625
0
0
0.25
0.25
0.5625
1
0.5625
0.5625
1
0.5625
1
1
0.25
0.5625
1
0.5625
0.25
0.25
1
1
0
0.0625
0.0625
0
0
0
0.0625
0.25
0
0.0625
0.25
0
0.25
0.5625
1
0.5625
1
1
0.25
0.5625
1
0.25
1
0
0.0625
0
0.0625
0
0.0625
0.25
0.0625
0.0625
0.5625
0.5625
1
0.5625
0.5625
0.25
0.5625
1
0.5625
0.5625
0
0.0625
0.0625
0.0625
0.0625
0
0.0625
0.25
0.0625
0.5625
0.5625
1
0.5625
0.5625
0.25
0.5625
1
0.0625
0
0.0625
0.0625
0.5625
0.5625
0.0625
0.0625
0.5625
0.5625
0.0625
0
0
0
0
0
0
0
0
0
0.25
0.25
0.25
0.25
0.25
0.25
0.25
0.25
0.25
1
1
1
1
1
1
1
1
1
0
0
0
0
0.25
0.25
0.25
0.25
0.0625
0.0625
0.0625
0.0625
0
0
0
0.25
0.25
0.25
0.0625
0.0625
1
1
1
1
0.5625
0.5625
0.5625
0.5625
1
1
1
0.5625
0.5625
0.25
0.25
0.25
1
1
1
0.5625
0.5625
0.25
0.25
1
1
0.5625
0
0
0
0.0625
0.0625
0
0
0.0625
0
0.0625
0.0625
0.0625
0.0625
0.25
0
0.0625
0.0625
0.0625
0.25
0.5625
0.5625
0.5625
0.5625
1
0.5625
0.5625
0.5625
1
0.25
0.5625
0.5625
0.5625
1
0.25
0.5625
0.5625
1
0
0.0625
0.0625
0.0625
0
0.0625
0.0625
0.0625
0.0625
0.5625
0.5625
0.5625
0.5625
0.0625
0.0625
+1
View File
@@ -77,6 +77,7 @@ set(UNIT_TESTS_SRCS
mesh/mesh_test_utils.cpp
mesh/test_exodus_reader.cpp
mesh/test_mfem_mesh_reader.cpp
mesh/test_bb_grid_map.cpp
mesh/test_exodus_writer.cpp
mesh/test_face_orientations.cpp
mesh/test_fms.cpp
+164
View File
@@ -0,0 +1,164 @@
// 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 "mfem.hpp"
#include "unit_tests.hpp"
using namespace mfem;
TEST_CASE("BBoxTensorGridMap nD", "[BBoxTensorGridMap]")
{
// Create a Cartesian mesh on [0,1]^D.
int dim = GENERATE(1, 2, 3);
CAPTURE(dim);
Mesh mesh;
if (dim == 1)
{
mesh = Mesh::MakeCartesian1D(2);
}
else if (dim == 2)
{
mesh = Mesh::MakeCartesian2D(2, 2, Element::QUADRILATERAL);
}
else if (dim == 3)
{
mesh = Mesh::MakeCartesian3D(2, 2, 2, Element::HEXAHEDRON);
}
// Create map with a 4^dim tensor grid.
int nx = 4;
BBoxTensorGridMap map(mesh, nx);
// Test each element's center
Vector center(dim);
for (int e = 0; e < mesh.GetNE(); e++)
{
mesh.GetElementCenter(e, center);
Array<int> elems = map.MapPointToElements(center);
REQUIRE(elems.Size() > 0);
REQUIRE(elems.Find(e) >= 0);
}
// Test point outside
Vector pt(dim);
pt = 0.0;
pt(0) = 1.5;
Array<int> elems = map.MapPointToElements(pt);
REQUIRE(elems.Size() == 0);
}
TEST_CASE("BBoxTensorGridMap Boundary", "[BBoxTensorGridMap]")
{
// Create a 1x1 quad mesh on [0,1]x[0,1]
Mesh mesh = Mesh::MakeCartesian2D(2, 2, Element::QUADRILATERAL);
// Create map with 2x2 grid
int nx = 2;
BBoxTensorGridMap map(mesh, nx);
// Point exactly on the boundary of all 4 tensor grid cells
Vector pt(2);
pt(0) = 0.5; pt(1) = 0.5;
int grid_cell = map.GetGridCellFromPoint(pt);
REQUIRE(grid_cell == 3); // cell index should be 3 (top-right)
// This should include mesh element 2 (top-right) in the candidate list.
Array<int> elems = map.MapPointToElements(pt);
REQUIRE(elems.Size() > 0);
REQUIRE(elems.Find(2) >= 0);
}
TEST_CASE("BBoxTensorGridMap Empty", "[BBoxTensorGridMap]")
{
int dim = GENERATE(1, 2, 3);
bool by_max_size = GENERATE(false, true);
CAPTURE(dim);
CAPTURE(by_max_size);
Vector elmin, elmax;
Array<int> nx(dim);
nx = 4;
const int n = 4;
BBoxTensorGridMap map_array(elmin, elmax, 0, dim, nx, by_max_size);
BBoxTensorGridMap map_scalar(elmin, elmax, 0, dim, n, by_max_size);
Vector pt(dim);
pt = 0.5;
Array<int> elems = map_array.MapPointToElements(pt);
REQUIRE(elems.Size() == 0);
elems = map_scalar.MapPointToElements(pt);
REQUIRE(elems.Size() == 0);
}
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
TEST_CASE("GlobalBBoxTensorGridMap Parallel",
"[GlobalBBoxTensorGridMap][Parallel]")
{
int num_procs;
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
int myid;
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// Create a mesh on [0,1]^D
int dim = GENERATE(2, 3);
CAPTURE(dim);
Mesh mesh;
if (dim == 2)
{
mesh = Mesh::MakeCartesian2D(4, 4, Element::QUADRILATERAL);
}
else if (dim == 3)
{
mesh = Mesh::MakeCartesian3D(4, 4, 4, Element::HEXAHEDRON);
}
int nel = mesh.GetNE();
Array<int> partitioning(mesh.GetNE());
for (int e = 0; e < mesh.GetNE(); e++)
{
partitioning[e] = e % num_procs;
}
ParMesh pmesh(MPI_COMM_WORLD, mesh, partitioning.GetData());
// Setup a list of points to find - center of each element, plus one point
// outside the global bounding box.
Vector centers((nel + 1)*dim);
for (int e = 0; e < nel; e++)
{
Vector center_el(centers.GetData() + e*dim, dim);
mesh.GetElementCenter(e, center_el);
}
for (int d = 0; d < dim; d++)
{
centers(nel*dim + d) = 2.0;
}
// Create map with 4x4x4 global grid
int nx = 4;
GlobalBBoxTensorGridMap map(pmesh, nx);
// Test MapPointsToProcs - each point should map to the processor owning the
// element, and outside points should have an empty candidate list.
std::map<int, std::vector<int>> pt_to_procs;
map.MapPointsToProcs(centers, 1, pt_to_procs);
REQUIRE(pt_to_procs.size() == nel + 1);
for (int i = 0; i < nel; i++)
{
std::vector<int> procs = pt_to_procs[i];
REQUIRE(procs.size() > 0);
REQUIRE(procs[0] == i % num_procs);
}
REQUIRE(pt_to_procs[nel].empty());
}
#endif
+39
View File
@@ -478,3 +478,42 @@ TEST_CASE("NURBS 1D curve in 2D from patches", "[Mesh]")
}
}
}
TEST_CASE("Mesh::Swap preserves named attribute sets", "[Mesh]")
{
// Regression test for a bug where Mesh::Swap swapped attributes and
// bdr_attributes but omitted the attr_sets maps, silently losing all named
// element/boundary sets on any move or swap of an mfem::Mesh.
Mesh a = Mesh::MakeCartesian2D(2, 2, Element::QUADRILATERAL);
Mesh b = Mesh::MakeCartesian2D(3, 3, Element::QUADRILATERAL);
Array<int> a_elem_attrs({1});
Array<int> b_elem_attrs({1});
Array<int> a_bdr_attrs({1, 2});
Array<int> b_bdr_attrs({3, 4});
a.attribute_sets.SetAttributeSet("elem_set_a", a_elem_attrs);
a.bdr_attribute_sets.SetAttributeSet("bdr_set_a", a_bdr_attrs);
b.attribute_sets.SetAttributeSet("elem_set_b", b_elem_attrs);
b.bdr_attribute_sets.SetAttributeSet("bdr_set_b", b_bdr_attrs);
a.Swap(b, true);
// After swap, a should hold b's sets and b should hold a's sets.
REQUIRE(a.attribute_sets.AttributeSetExists("elem_set_b"));
REQUIRE(!a.attribute_sets.AttributeSetExists("elem_set_a"));
REQUIRE(a.bdr_attribute_sets.AttributeSetExists("bdr_set_b"));
REQUIRE(!a.bdr_attribute_sets.AttributeSetExists("bdr_set_a"));
REQUIRE(b.attribute_sets.AttributeSetExists("elem_set_a"));
REQUIRE(!b.attribute_sets.AttributeSetExists("elem_set_b"));
REQUIRE(b.bdr_attribute_sets.AttributeSetExists("bdr_set_a"));
REQUIRE(!b.bdr_attribute_sets.AttributeSetExists("bdr_set_b"));
// Verify set contents survived the swap.
Array<int> a_bdr_result = a.bdr_attribute_sets.GetAttributeSet("bdr_set_b");
Array<int> b_bdr_result = b.bdr_attribute_sets.GetAttributeSet("bdr_set_a");
REQUIRE(a_bdr_result == b_bdr_attrs);
REQUIRE(b_bdr_result == a_bdr_attrs);
}
+100 -18
View File
@@ -84,6 +84,7 @@ int tmop(int id, Req &res, int argc, char *argv[])
int lin_solver = 2;
int max_lin_iter = 100;
real_t lim_const = 0.0;
real_t adapt_lim_const = 0.0;
int lim_type = 0;
bool normalization = false;
real_t jitter = 0.0;
@@ -117,6 +118,7 @@ int tmop(int id, Req &res, int argc, char *argv[])
args.AddOption(&lin_solver, "-ls", "--lin-solver", "");
args.AddOption(&max_lin_iter, "-li", "--lin-iter", "");
args.AddOption(&lim_const, "-lc", "--limit-const", "");
args.AddOption(&adapt_lim_const, "-alc", "--adapt-limit-const", "");
args.AddOption(&lim_type, "-lt", "--limit-type", "");
args.AddOption(&normalization, "-nor", "--normalization",
"-no-nor", "--no-normalization", "");
@@ -394,6 +396,22 @@ int tmop(int id, Req &res, int argc, char *argv[])
}
}
// Adaptive limiting.
std::unique_ptr<AdaptivityEvaluator> adapt_lim_eval = nullptr;
std::unique_ptr<Coefficient> adapt_lim_coeff = nullptr;
std::unique_ptr<ParGridFunction> adapt_lim_gf0 = nullptr;
if (adapt_lim_const > 0.0)
{
adapt_lim_gf0.reset(new ParGridFunction(&ind_fes));
FunctionCoefficient adapt_lim_gf0_coeff(adapt_lim_fun);
adapt_lim_gf0->ProjectCoefficient(adapt_lim_gf0_coeff);
adapt_lim_coeff.reset(new ConstantCoefficient(adapt_lim_const));
adapt_lim_eval.reset(new AdvectorCG(al));
tmop_integ->EnableAdaptiveLimiting(*adapt_lim_gf0, *adapt_lim_coeff,
*adapt_lim_eval, 1.0);
}
// Setup the NonlinearForm which defines the integral of interest.
ParNonlinearForm a(&fes_h1);
a.SetAssemblyLevel(pa ? AssemblyLevel::PARTIAL : AssemblyLevel::LEGACY);
@@ -495,6 +513,9 @@ int tmop(int id, Req &res, int argc, char *argv[])
if (pa) { a.GetGradient(xt).AssembleDiagonal(d); }
else
{
std::unique_ptr<AdaptivityEvaluator> adapt_lim_eval_diag = nullptr;
std::unique_ptr<Coefficient> adapt_lim_coeff_diag = nullptr;
ParNonlinearForm nlf_fa(&fes_h1);
auto *nlfi_fa = new TMOP_Integrator(metric.get(), target_c.get());
nlfi_fa->SetIntegrationRules(*irules, quad_order);
@@ -508,6 +529,13 @@ int tmop(int id, Req &res, int argc, char *argv[])
new TMOP_ExponentialLimiter);
}
}
if (adapt_lim_const > 0.0)
{
adapt_lim_eval_diag.reset(new AdvectorCG(al));
adapt_lim_coeff_diag.reset(new ConstantCoefficient(adapt_lim_const));
nlfi_fa->EnableAdaptiveLimiting(*adapt_lim_gf0, *adapt_lim_coeff_diag,
*adapt_lim_eval_diag, 1.0);
}
nlf_fa.AddDomainIntegrator(nlfi_fa);
nlf_fa.SetEssentialBC(ess_bdr);
dynamic_cast<GradientClass &>(nlf_fa.GetGradient(xt)).GetDiag(d);
@@ -612,6 +640,15 @@ int tmop(int id, Req &res, int argc, char *argv[])
}
}
// Adaptive limiting evolves adapt_lim_gf as the mesh moves. When running
// repeated solves from the same initial mesh (newton_loop > 1), reset the
// adaptive limiting state so each solve is comparable.
if (adapt_lim_const > 0.0)
{
tmop_integ->EnableAdaptiveLimiting(*adapt_lim_gf0, *adapt_lim_coeff,
*adapt_lim_eval, 1.0);
}
a.Setup();
if (normalization) { tmop_integ->ParEnableNormalization(x); }
@@ -660,40 +697,48 @@ static inline void req_tmop(int id, const char *args[], Req &res)
#define DEFAULT_ARGS const char *args[] = { "tmop_pa_tests", "-pa", "-m", "mesh", \
"-o", "0", "-rs", "0", "-mid", "0", "-tid", "0", "-qt", "1", "-qo", "0", \
"-ni", "10", "-nl", "1", "-nrtol", "1e-8", "-lrtol", "1e-12", "-ls", "2", "-li", "100", "-lc", "0", \
"-lt", "0", "-no-nor", "-ji", "0", "-diag", "-cmb", "0", "-no-bec", "-no-per", nullptr }
"-alc", "0", "-lt", "0", "-no-nor", "-ji", "0", "-diag", "-cmb", "0", "-no-bec", "-no-per", nullptr }
constexpr int ALV = 1, MSH = 3, POR = 5, RS = 7, MID = 9, TID = 11, QTY = 13,
QOR = 15, NI = 17, NL = 19, NRTOL = 21, LRTOL = 23, LS = 25, LI = 27, LC = 29,
LT = 31, NOR = 32, JI = 34, DIAG = 35, CMB = 37, BEC = 38, PER = 39;
ALC = 31, LT = 33, NOR = 34, JI = 36, DIAG = 37, CMB = 39, BEC = 40, PER = 41;
static inline void dump_args(int id, const char *args[])
{
if (id != 0) { return; }
const char *format =
"tmop_pa_tests %6.6s -m %s -o %s -rs %s -mid %s -tid %s -qt %s -qo %s "
"-ni %s -nl %s -nrtol %s -lrtol %s -ls %s -li %s -lc %s -lt %s %s -ji %s "
"-ni %s -nl %s -nrtol %s -lrtol %s -ls %s -li %s -lc %s -alc %s -lt %s %s -ji %s "
"%s -cmb %s %s %s\n";
printf(format, args[ALV], args[MSH], args[POR], args[RS], args[MID], args[TID],
args[QTY], args[QOR], args[NI], args[NL], args[NRTOL], args[LRTOL],
args[LS], args[LI], args[LC], args[LT], args[NOR], args[JI], args[DIAG],
args[CMB], args[BEC], args[PER]);
printf(format, args[ALV], args[MSH], args[POR], args[RS], args[MID],
args[TID], args[QTY], args[QOR], args[NI], args[NL], args[NRTOL],
args[LRTOL], args[LS], args[LI], args[LC], args[ALC], args[LT],
args[NOR], args[JI], args[DIAG], args[CMB], args[BEC], args[PER]);
fflush(nullptr);
}
static inline void tmop_require(int id, const char *args[])
{
real_t tol_fe = 4e-12;
const bool has_adapt_lim = std::atof(args[ALC]) > 0.0;
if (has_adapt_lim) { tol_fe = 1e-7; }
Req res[2];
constexpr real_t eps = 4e-12;
(args[ALV] = "-pa", dump_args(id, args), req_tmop(id, args, res[0]));
(args[ALV] = "-no-pa", dump_args(id, args), req_tmop(id, args, res[1]));
REQUIRE(res[0].dot == MFEM_Approx(res[1].dot));
REQUIRE(res[0].diag == MFEM_Approx(res[1].diag));
REQUIRE(res[0].min_detJ == MFEM_Approx(res[1].min_detJ));
REQUIRE(res[0].met_normal == MFEM_Approx(res[1].met_normal));
REQUIRE(res[0].lim_normal == MFEM_Approx(res[1].lim_normal));
REQUIRE(res[0].bal_weights == MFEM_Approx(res[1].bal_weights));
REQUIRE(res[0].init_energy == MFEM_Approx(res[1].init_energy));
REQUIRE(res[0].final_energy == MFEM_Approx(res[1].final_energy, eps));
if (has_adapt_lim == false)
{
REQUIRE(res[0].dot == MFEM_Approx(res[1].dot));
}
REQUIRE(res[0].diag == MFEM_Approx(res[1].diag));
REQUIRE(res[0].min_detJ == MFEM_Approx(res[1].min_detJ));
REQUIRE(res[0].met_normal == MFEM_Approx(res[1].met_normal));
REQUIRE(res[0].lim_normal == MFEM_Approx(res[1].lim_normal));
REQUIRE(res[0].bal_weights == MFEM_Approx(res[1].bal_weights));
REQUIRE(res[0].init_energy == MFEM_Approx(res[1].init_energy));
REQUIRE(res[0].final_energy == MFEM_Approx(res[1].final_energy, tol_fe));
}
static constexpr int SZ = 32;
@@ -737,6 +782,7 @@ public:
bool normalization = false;
bool periodic = false;
real_t lim_const = 0.0;
real_t adapt_lim_const = 0.0;
int lim_type = 0;
real_t jitter = 0.0;
list_t order = { 1, 2, 3, 4 };
@@ -764,6 +810,7 @@ public:
Args &NEWTON_RTOLERANCE(const real_t arg) { newton_rtol = arg; return *this; }
Args &LINSOL_RTOLERANCE(const real_t arg) { linsol_rtol = arg; return *this; }
Args &LIMITING(const real_t arg) { lim_const = arg; return *this; }
Args &ADAPT_LIMITING(const real_t arg) { adapt_lim_const = arg; return *this; }
Args &JI(const real_t arg) { jitter = arg; return *this; }
// lists
Args &POR(list_t arg) { order = arg; return *this; }
@@ -776,7 +823,7 @@ public:
const char *name, *mesh;
int NEWTON_ITERATIONS, LINSOL_ITERATIONS, REFINE, COMBO, LIMIT_TYPE;
bool NORMALIZATION, DIAGONAL, BAL_EXPL_COMBO, PERIODIC;
real_t NEWTON_RTOLERANCE, LINSOL_RTOLERANCE, LIMITING, JITTER;
real_t NEWTON_RTOLERANCE, LINSOL_RTOLERANCE, LIMITING, ADAPT_LIMITING, JITTER;
list_t P_ORDERS, TARGET_IDS, METRIC_IDS, Q_ORDERS, LINEAR_SOLVERS, NEWTON_LOOPS;
public:
@@ -795,6 +842,7 @@ public:
NEWTON_RTOLERANCE(a.newton_rtol),
LINSOL_RTOLERANCE(a.linsol_rtol),
LIMITING(a.lim_const),
ADAPT_LIMITING(a.adapt_lim_const),
JITTER(a.jitter),
// lists
P_ORDERS(a.order),
@@ -809,7 +857,7 @@ public:
if ((id == 0) && name) { mfem::out << "[" << name << "]" << std::endl; }
DEFAULT_ARGS;
char ni[SZ] {}, nrtol[SZ] {}, lrtol[SZ] {}, rs[SZ] {}, li[SZ] {},
lc[SZ] {}, lt[SZ] {}, ji[SZ] {}, cmb[SZ] {};
lc[SZ] {}, alc[SZ] {}, lt[SZ] {}, ji[SZ] {}, cmb[SZ] {};
args[MSH] = mesh;
// int
args[NI] = itoa(NEWTON_ITERATIONS, ni);
@@ -826,6 +874,7 @@ public:
args[NRTOL] = dtoa(NEWTON_RTOLERANCE, nrtol);
args[LRTOL] = dtoa(LINSOL_RTOLERANCE, lrtol);
args[LC] = dtoa(LIMITING, lc);
args[ALC] = dtoa(ADAPT_LIMITING, alc);
args[JI] = dtoa(JITTER, ji);
for (int p : P_ORDERS)
@@ -926,6 +975,39 @@ static void tmop_tests(int id = 0, bool all = false)
const real_t jitter = 1. / (M_PI * M_PI);
Launch(Launch::Args("2D + adaptive limiting")
.MESH("../../miniapps/meshing/stretched2D.mesh")
.REFINE(0)
.NORMALIZATION()
.MID({ 2 })
.TID({ 1 })
.LS({ 3 })
.POR({ 1 })
.QOR({ 5 })
.NEWTON_ITERATIONS(50)
.NEWTON_RTOLERANCE(1e-6)
.LINSOL_RTOLERANCE(1e-10)
.LINSOL_ITERATIONS(100)
.ADAPT_LIMITING(1.0)
.DIAGONAL(true))
.Run(id, all);
Launch(Launch::Args("3D + adaptive limiting")
.MESH("../../miniapps/meshing/stretched3D.mesh")
.REFINE(0)
.NORMALIZATION()
.MID({ 302 })
.TID({ 1 })
.LS({ 3 })
.POR({ 1 })
.QOR({ 5 })
.NEWTON_ITERATIONS(50)
.NEWTON_RTOLERANCE(1e-6)
.LINSOL_RTOLERANCE(1e-10)
.ADAPT_LIMITING(1.0)
.DIAGONAL(true))
.Run(id, all);
Launch(Launch::Args("2D Periodic + adapted discrete size")
.MESH("../../data/periodic-square.mesh")
.PERIODIC()