Compare commits

...
Author SHA1 Message Date
Ryan Black 2842d0e7f6 Update operator.hpp 2026-07-01 12:08:07 -04:00
Ryan Black e9b24e14cf ConstrainedOperator::AddMultTranspose() 2026-07-01 11:38:30 -04:00
John Camier 7e4df762c8 Merge branch 'master' into vecmasspa 2026-06-10 17:02:05 -07:00
Tzanio Kolev 596b75282d Merge pull request #5356 from mfem/update-codecov-action
Update the versions of the actions from `mfem/github-actions`
2026-06-09 17:55:16 -07:00
Tzanio Kolev 2ed23cec01 Merge pull request #5349 from mfem/raja-launchbounds
Raja launchbounds
2026-06-09 12:53:57 -07:00
Veselin Dobrev 11fbd1f65c In CI, update the actions from mfem/github-actions to v2.6 2026-06-09 12:14:02 -07:00
Will Pazner d3556dc2ac Merge pull request #4412 from mfem/elast-fix-4404
Verify that ordering is byVDIM when using the AMG elasticity solver
2026-06-09 11:24:30 -07:00
John Camier d28e48b82a Merge branch 'master' into vecmasspa 2026-06-09 06:42:29 -07:00
Veselin Dobrev 80c7331a29 In CI, fix the name for the 'upload-coverage' action, to replicate the
complete job name.
2026-06-08 21:36:52 -07:00
Veselin Dobrev 9e31083745 In CI, pass the Codecov token as the environment variable CODECOV_TOKEN 2026-06-08 20:15:57 -07:00
Veselin Dobrev fc8272216b In CI, pass the Codecov token as a secret to the 'upload-coverage' action.
Also, use the CI job name as the name for the 'upload-coverage' action.
2026-06-08 20:01:09 -07:00
Veselin Dobrev 8efa320470 Update codecov action: testing 2026-06-08 14:04:39 -07:00
Tzanio Kolev c6ea42a681 Merge pull request #5045 from mfem/stefanozampini/petsc-requires-hypre
PETSc: Remove ad-hoc code to support PETSc without hypre
2026-06-06 16:52:11 -07:00
camierjs 11def83e2c Merge branch 'master' into vecmasspa 2026-06-06 06:39:37 -07:00
Tzanio Kolev 51955e88ce Merge branch 'master' into raja-launchbounds 2026-06-05 16:32:55 -07:00
Tzanio Kolev 61bb7755a3 Merge pull request #5324 from mfem/multigrid-bcs-fix
Bugfix for GeometricMultigrid with no essential BCs
2026-06-05 16:17:51 -07:00
Tzanio Kolev a7fa61464c Merge pull request #5267 from mfem/print-interfaces-dev
Optional output of material interfaces in parallel
2026-06-05 16:13:18 -07:00
Tzanio Kolev 93329a7ba0 Merge pull request #5124 from yuyangdai/cudss-dev
Add support for parallel NVIDIA's GPU-accelerated direct sparse solver cuDSS solver[cudss-dev]
2026-06-05 16:10:03 -07:00
Andrew Ho 9a69d92756 changelog 2026-06-05 08:20:01 -07:00
yuyangdai fb93e21a0c Update INSTALL file to correct cuDSS library options 2026-06-05 09:16:00 +08:00
Stefano Zampini 19d60f95ec PETSc: Remove ad-hoc code to support PETSc without hypre 2026-06-04 11:27:36 +01:00
Will Pazner 861d318162 Fix Doxygen comment in GeometricMultigrid 2026-06-03 15:48:39 -07:00
Andrew Ho 3c5acae825 missing endif 2026-06-02 22:13:12 -07:00
Andrew Ho 9a749f8936 Merge remote-tracking branch 'base/raja-launchbounds' into raja-launchbounds 2026-06-02 22:11:42 -07:00
Andrew Ho c095d055a1 put the check/define for RAJA default stream in backends 2026-06-02 22:11:11 -07:00
Tzanio Kolev d29e914146 Merge branch 'master' into multigrid-bcs-fix 2026-06-02 18:20:52 -07:00
John Camier 98ccd6fb96 Merge branch 'master' into vecmasspa 2026-06-02 16:06:03 -07:00
Andrew HoandTom Stitt a2491a0e43 Apply suggestion from @tomstitt
Co-authored-by: Tom Stitt <stitt4@llnl.gov>
2026-06-02 11:08:12 -07:00
Tzanio Kolev 4475a8c6d4 minor
Don't disable reorder_space if static condensation is enabled.
2026-06-02 10:18:15 -07:00
yuyangdai b5e99d8d83 Fix CMake script to correctly locate cuDSS library and allocate device memory for csr values when set the cudssMatrix. 2026-06-02 13:31:25 +08:00
daiyuyangandAndrew Ho 91f58c293d Update linalg/cudss.cpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2026-06-02 13:17:09 +08:00
Tzanio Kolev 4836e7cb53 Merge branch 'master' into elast-fix-4404 2026-06-01 17:00:44 -07:00
Tzanio Kolev 6451e64637 Merge branch 'master' into cudss-dev 2026-06-01 15:55:15 -07:00
Tzanio Kolev b7ac1963bb Merge pull request #5340 from mfem/update-contact-miniapp-readme
Update Tribol shared libraries
2026-06-01 15:53:50 -07:00
Veselin Dobrev baefb786fb Correction in CHANGELOG 2026-06-01 08:58:54 -07:00
Veselin Dobrev 99523b3a97 Address reviewer feedback: add/improve Doxygen documentation. 2026-06-01 08:43:12 -07:00
Andrew HoandJohn Camier f67c1e5beb Update general/forall.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2026-06-01 08:31:43 -07:00
Andrew HoandJohn Camier 50af02454c Update general/forall.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2026-06-01 08:31:33 -07:00
Andrew HoandJohn Camier aa94eb11b7 Update general/forall.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2026-06-01 08:31:23 -07:00
Andrew HoandJohn Camier b73cc75635 Update general/forall.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2026-06-01 08:31:09 -07:00
Andrew Ho 7b5f050bdc format to shorter line lengths 2026-05-31 09:47:27 -07:00
Andrew Ho 637fa7cc35 Merge remote-tracking branch 'base/raja-launchbounds' into raja-launchbounds 2026-05-29 11:06:13 -07:00
Andrew Ho 3f332d50b5 some platforms require this to actually be set to 1? 2026-05-29 11:05:20 -07:00
Andrew Ho 782e468325 duplicate code 2026-05-28 17:14:12 -07:00
Andrew Ho 0c59b93ecf flag only needs to be defined, not set to a value
also updated makefile defaults
2026-05-28 16:56:11 -07:00
Andrew Ho 5a0e89cf31 ensure that RAJA/CAMP uses the platform default streams 2026-05-28 16:35:39 -07:00
Andrew Ho 4c3bfce583 Added launch bounds for Raja kernels 2026-05-28 15:38:40 -07:00
Tzanio Kolev 59d6820ff3 Merge branch 'master' into cudss-dev 2026-05-28 14:21:30 -07:00
Tzanio Kolev 9ec379f176 Merge branch 'master' into update-contact-miniapp-readme 2026-05-28 14:19:45 -07:00
Tzanio Kolev 72cc503eb9 Updated CHANGELOG 2026-05-28 12:53:25 -07:00
Veselin Dobrev 5aff935c98 Minor future-proofing suggested by Copilot.
MFEM_ASSERT does not need to explicitly print the name of the function
because that is already done automatically.
2026-05-27 10:18:49 -07:00
John Camier d9c9a35ac9 Merge branch 'master' into vecmasspa 2026-05-27 06:31:03 -07:00
Tzanio KolevandCopilot Autofix powered by AI d56298ba17 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 19:02:02 -07:00
Tzanio KolevandCopilot Autofix powered by AI b9d67dec34 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 19:00:55 -07:00
Tzanio Kolev 5b664e393d Merge branch 'master' into print-interfaces-dev 2026-05-26 18:43:14 -07:00
Veselin Dobrev c582282084 Merge pull request #5345 from mfem/copilot-dev
Updates in developer docs + instructions for the GitHub Copilot reviews
2026-05-26 18:27:49 -07:00
Tzanio Kolev 944ece4090 TODO item for ConduitDataCollection::Save() 2026-05-26 17:35:13 -07:00
Veselin DobrevandTzanio Kolev f225d0c3ef Apply suggestions from code review
Remove FIXME comments -- no actions needed.

Co-authored-by: Tzanio Kolev <tzanio@llnl.gov>
2026-05-26 16:58:36 -07:00
Tzanio Kolev a9c98c2e3a Merge branch 'master' into print-interfaces-dev 2026-05-26 10:35:15 -07:00
Tzanio KolevandCopilot Autofix powered by AI ac8c2948e7 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 10:02:50 -07:00
Tzanio KolevandCopilot Autofix powered by AI f75b4c10d2 Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-26 10:02:10 -07:00
Veselin Dobrev ecf6b0b44f Address some reviewer suggestions and comments 2026-05-26 08:41:10 -07:00
camierjs 15ee172f26 Rework ElasticityAssembleDiagonalPA to avoid using scratch memory 2026-05-25 18:31:31 -07:00
camierjs dfbf0aa0e9 Fix ElasticityIntegrator AssembleDiagonalPA/AddMultPA QVec size 2026-05-25 16:05:04 -07:00
Tzanio Kolev 5a52c676e1 Small changes in Copilot instructions 2026-05-25 15:49:19 -07:00
Tzanio Kolev 5be9693235 Initial Copilot instructions 2026-05-25 15:34:45 -07:00
camierjs dc81a7d3c4 Adjust SmemPAVectorMassAssembleDiagonal3D 2026-05-25 14:23:13 -07:00
camierjs ea1d41596a 2D/3D VectorMassAssembleDiagonalPA specialized on T_Q1D 2026-05-25 14:14:46 -07:00
Tzanio Kolev 5421abc4c4 Fixes and updates in CONTRIBUTING.md 2026-05-25 12:58:57 -07:00
Tzanio Kolev f2d3fb45dc Fixes and updates in INSTALL 2026-05-25 12:04:26 -07:00
John Camier 239c83a742 Merge branch 'master' into cudss-dev 2026-05-24 20:14:04 -07:00
maxpaik16 c315028a09 Merge branch 'master' into update-contact-miniapp-readme 2026-05-24 19:03:34 -07:00
yuyangdai 99b66f4f45 Update CHANGELOG 2026-05-25 09:20:19 +08:00
Tzanio Kolev e7be50eb91 Merge pull request #5333 from nmnobre/gslib
FindPointsGSLIB: use parallel-aware ProjectDiscCoefficient
2026-05-24 10:58:16 -07:00
Tzanio Kolev 2dd3915ad5 Merge branch 'master' into gslib 2026-05-23 10:15:21 -07:00
Tzanio Kolev 29b572819a Merge branch 'master' into cudss-dev 2026-05-23 10:14:37 -07:00
Tzanio Kolev 0a0acfda66 Merge pull request #5329 from nmnobre/host
Fix GetEssentialTrueDofsVar memory allocation
2026-05-22 20:15:51 -07:00
maxpaik16 7dbce44472 Update tribol libraries 2026-05-22 16:36:51 -07:00
maxpaik16 c67bd21219 Update tribol shared libraries 2026-05-22 16:35:29 -07:00
Tzanio Kolev 38f5b93520 Merge branch 'master' into print-interfaces-dev 2026-05-21 09:45:23 -07:00
Tzanio Kolev a078dfd59e Reviewer comments 2026-05-21 09:45:02 -07:00
Tzanio Kolev 66818f5525 Merge branch 'master' into gslib 2026-05-21 07:41:18 -07:00
Tzanio Kolev b810a5e540 Merge branch 'master' into host 2026-05-21 07:41:14 -07:00
Tzanio Kolev e63e421343 Merge branch 'master' into cudss-dev 2026-05-21 07:40:59 -07: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
Nuno Nobre 95acb1f85f Remove unnecessary namespace qualification 2026-05-19 17:15:03 +01:00
camierjs 3163d1b039 Move Specialization to AddMultTransposePA 2026-05-19 06:26:44 -07:00
camierjs 9484b6d688 Fix VectorDivergenceAddMultPA registration 2026-05-19 06:18:50 -07:00
Nuno Nobre 1c7261db07 Fence code using parallel objs w/ MPI-conditional directive 2026-05-18 23:53:35 +01:00
Nuno Nobre e1b678664a FindPointsGSLIB: use parallel-aware ProjectDiscCoefficient 2026-05-18 19:02:30 +01:00
camierjs ea90996f85 Cleanup non-smem vec div pa kernels 2026-05-16 19:34:08 -07:00
camierjs 3fd646f464 VectorDivergenceAddMultTransposePA 2026-05-16 18:00:55 -07:00
camierjs d38fe6fc05 Re-enable transposed LoadMatrix 2026-05-16 17:21:51 -07:00
camierjs 630c79f1ed Add VectorDivergenceIntegrator 3325 specialization 2026-05-16 16:31:11 -07:00
camierjs 1ab621fbe9 AddMultPA with instantiated smem 2026-05-16 16:11:18 -07:00
camierjs 1975811ad4 Use Be directly in vecdiv smem kernels 2026-05-16 16:02:52 -07:00
camierjs e43c6d891f VectorDivergenceAddMultPA Specializations 2026-05-16 15:57:51 -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
camierjs 01c848e838 Init tests unit fem test_pa_vecdiv 2026-05-16 12:18:40 -07:00
Nuno NobreandAndrew Ho 8f06539b6e Do not assume true_ess_dofs is empty nor host-allocated
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2026-05-16 00:32:25 +01: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
Nuno Nobre 30a8eb5ccf Fix GetEssentialTrueDofsVar memory allocation 2026-05-14 23:58:41 +01:00
John Camier 078e59a33b Merge branch 'master' into cudss-dev 2026-05-14 13:20:48 -07:00
John Camier 1d3a723af9 Merge branch 'master' into nlconvpa 2026-05-14 13:20:27 -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
Tzanio KolevandVeselin Dobrev da4e1b5137 Update makefile
Co-authored-by: Veselin Dobrev <v-dobrev@users.noreply.github.com>
2026-05-12 13:54:55 -07:00
Vladimir Z Tomov 54b0a83ffd added missing .mesh to makefile 2026-05-11 09:12:01 -07:00
John Camier 2e8e4a5377 Merge branch 'master' into nlconvpa 2026-05-09 11:31:10 -07:00
Tzanio Kolev b5a1660c2d Merge branch 'master' into cudss-dev 2026-05-09 10:41:10 -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
Will Pazner 5ffc2ef502 Bugfix for GeometricMultigrid with no essential BCs 2026-05-07 18:04:46 -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
yuyangdai 59cef5f9e3 Add a link for communication layer library in cuDSS 2026-05-06 10:07:31 +08:00
John Camier 05be944a86 Merge branch 'master' into cudss-dev 2026-05-05 15:33:09 -07:00
John Camier 258bd917ad Merge branch 'master' into adapt-lim-pa 2026-05-05 15:33:01 -07:00
John Camier 139c3ddaa6 Merge branch 'master' into nlconvpa 2026-05-05 15:32:54 -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
Tzanio Kolev bdd36c8982 Merge pull request #5318 from mfem/ai-policy
AI policy
2026-05-05 07:39:53 -07:00
John Camier c103cfa84a Merge branch 'master' into cudss-dev 2026-05-05 06:34:53 -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
John Camier 24f1022f7d Merge branch 'master' into nlconvpa 2026-05-05 06:19:01 -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
Tzanio Kolev a7988aa845 Merge branch 'master' into ai-policy 2026-05-04 14:30:08 -07:00
Tzanio Kolev 4ec768c82b Merge pull request #5322 from mfem/fix-changelog
Fix CHANGELOG
2026-05-04 14:28:58 -07:00
Mittal, Ketan e32ea54e00 fix changelog 2026-05-04 14:11:52 -07:00
Veselin Dobrev 630a75440f Merge pull request #5299 from mfem/batchmass3d
Add element batching capabilities to 3D MassIntegrator
2026-05-04 13:47:24 -07:00
Veselin Dobrev 3ef3c8e6b4 Merge pull request #5306 from mfem/gslib-gitlab-testing
Include gslib testing on Dane
2026-05-04 13:43:23 -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
Tzanio Kolev 145efc313d Merge pull request #5320 from mfem/fix-cmake-libceed-test
Fix a CMake test of libCEED
2026-05-02 12:55:41 -07:00
Veselin Dobrev 26b2aa5cea In .gitlab/scripts/baseline, use srun to run scripts since salloc
does NOT run the script in the allocation as does srun.

Revert the change in the number of build tasks in dane-baseline.yml.
2026-05-01 11:10:09 -07:00
Veselin Dobrev 476c148949 Adjust the number of build tasks in dane-baseline.yml 2026-05-01 09:13:40 -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
Tzanio KolevandVeselin Dobrev 8ed259be31 Update CONTRIBUTING.md
Co-authored-by: Veselin Dobrev <v-dobrev@users.noreply.github.com>
2026-04-30 11:56:57 -07:00
Tzanio Kolev 67025d49ff AI policy updates based on feedback 2026-04-30 11:56:57 -07:00
Tzanio Kolev de1dea610e AI policy updates based on feedback 2026-04-30 11:56:57 -07:00
Tzanio Kolev 9f3f5c0372 Suggested AI policy 2026-04-30 11:56:56 -07:00
John Camier 29caf08098 Merge branch 'master' into cudss-dev 2026-04-29 17:05:01 -07:00
John Camier edc4d9a187 Merge branch 'master' into nlconvpa 2026-04-29 17:04:08 -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
Tzanio Kolev 9205efab48 Merge pull request #5319 from mfem/gslib-gnu-make-updates
GSLIB related updates to the GNU make build system
2026-04-29 15:04:10 -07:00
Veselin Dobrev 1ccc27226a Fix a CMake test of libCEED 2026-04-29 10:54:49 -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
Veselin Dobrev 0d3195e69b Fix issue #5314 and other tweaks.
* 'make style' now checks if all git source files are selected for formatting.
* In examples/makefile, propagate the target 'test-noclean' to subdirectories.
* In miniapps/plasma/makefile, use logic similar to examples/makefile to
  propagate targets to subdirectories.
* Other small fixes.
2026-04-28 06:32:57 -07:00
daiyuyang e217864f16 Merge branch 'master' into cudss-dev 2026-04-28 15:17:58 +08:00
yuyangdai de8aacddce Replace enum class with enum 2026-04-28 14:59:09 +08: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
Veselin Dobrev f37a596173 Fix a build issue: in the top makefile ensure miniapps/common is built
before building miniapps/gslib.
2026-04-27 07:26:12 -07:00
camierjs 2277decd8c With style 2026-04-25 14:24:05 -07:00
camierjs 36f6ff983a VectorConvectionNLFAddMultGradPA3D fallback checks, fix copilot reviews and Win32 math defines 2026-04-25 14:23:22 -07:00
camierjs 6fc6cf9186 Fix Windows compile-time constant expressions 2026-04-25 13:39:08 -07:00
camierjs bb06604dac Avoid narrowing non-constant-expression in initializer list 2026-04-25 13:24:06 -07:00
camierjs 058c6b2dee Avoid documenting NLVC registered kernels 2026-04-25 13:13:51 -07:00
camierjs 94135f3ed2 Merge branch 'camierjs-NLConvPA' into mfem-NLConvPA 2026-04-25 12:56:25 -07:00
camierjs 47c1d6230a Simplify NLVC diagonal kernels 2026-04-25 12:52:57 -07:00
camierjs a22c2c8d72 Cleanup instantiated NLVC registered kernels 2026-04-25 12:21:08 -07:00
camierjs 65f6ade43d Remove low order 3D VectorConvection kernels 2026-04-25 11:29:10 -07:00
camierjs 64cf121310 Meld back MFEM header 2026-04-25 11:07:13 -07:00
camierjs 92e1eace88 Revert test_nl_convection_nd 2026-04-25 11:06:24 -07:00
camierjs e6a3835983 Meld back toward master, rename nlvc unit tests 2026-04-25 11:01:45 -07:00
camierjs d97c8ec672 Cleanup debug traces 2026-04-25 10:33:20 -07:00
camierjs 6d9f34a3d7 Cleanup debug traces, nlvc benchmarks & use transposed adjugate 2026-04-25 08:50:32 -07:00
camierjs 5b73d20291 Cleanup NLF VConv diagonal 2026-04-25 07:15:07 -07:00
camierjs 4febbb7721 Merge branch 'master' into mfem-NLConvPA 2026-04-25 06:08:49 -07:00
camierjs ab81de5bf5 Merge branch 'master' into camierjs-NLConvPA 2026-04-25 05:49:57 -07:00
camierjs 23814cc1fa nlvc diagonal tests & benchmarks 2026-04-24 20:44:59 -07:00
camierjs 6307cef7cb Merge branch 'NLConvPA' of github.com:camierjs/mfem-NLConvPA into camierjs-NLConvPA 2026-04-24 17:51:37 -07:00
camierjs 156f7f930d NVTX marks 2026-04-24 17:51:35 -07:00
camierjs 0d5f21188d Add missing low order specializations 2026-04-24 17:48:28 -07:00
camierjs a786d4f293 SmemPAConvectionNLGradDiagonal 2026-04-24 17:46:27 -07:00
camierjs 018ab7b974 wip SmemPAConvectionNLGradDiagonalPA2D 2026-04-24 14:21:49 -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
Andrew Ho 04dd962b6d review comments 2026-04-23 15:58:25 -07:00
Mittal, Ketan 383914db9a use MFEM's Mpi class to initialize instead of MPI_Init directly 2026-04-23 14:27:01 -07:00
Andrew HoandJohn Camier f77d238a5d Update fem/dgmassinv_kernels.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2026-04-23 10:52:43 -07:00
John Camier 84996ce32f Merge branch 'master' into batchmass3d 2026-04-23 06:26:09 -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 f2b64de28f Merge branch 'master' of https://github.com/mfem/mfem into gslib-gitlab-testing 2026-04-22 12:27:50 -07:00
Mittal, Ketan 3415b0f3d4 run serial miniapps on 1 run when mfem is built with MPI 2026-04-22 12:26:45 -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
camierjs f66aaa46bd Merge branch 'camierjs-NLConvPA' into nlconvpa 2026-04-20 14:42:09 -07:00
Andrew Ho 6ea799e385 Merge branch 'master' into batchmass3d 2026-04-20 09:04:25 -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
Andrew Ho f1561e47d1 Merge branch 'master' into batchmass3d 2026-04-17 10:07:18 -07:00
Veselin Dobrev f1174bfbf5 Added new methods in class ParFiniteElementSpace: HaveDofSigns and
ApplyDofSigns.

Used the new methods to fix bugs in:
* the ParGridFunction constructor that reads input from a stream
* the method ParGridFunction::SaveAsOne
2026-04-17 09:08:19 -07:00
Mittal, Ketan abf5fedc5b include hypre with cuda on matrix 2026-04-16 21:03:11 -07:00
John Camier 4195e4ea2f Merge branch 'master' into adapt-lim-pa 2026-04-16 14:26:00 -07:00
Mittal, Ketan d183f43c96 Merge branch 'gslib-gitlab-testing' of https://github.com/mfem/mfem into gslib-gitlab-testing 2026-04-16 12:56:36 -07:00
Mittal, Ketan a545b94ad7 enable testing on matrix as well 2026-04-16 12:56:08 -07:00
camierjs eff6bc5abc Probe for ConstantCoefficient first 2026-04-16 10:52:14 -07:00
John Camier 94828dbdd0 Merge branch 'master' into hypremat 2026-04-16 04:46:18 -07:00
camierjs baf29bff27 Add NVTX marks and fmt::fmt 2026-04-15 16:12:35 -07:00
camierjs 9969e42270 Split LOVectorConvectionNLFAddMultGradPA3DType 2026-04-15 16:02:13 -07:00
camierjs 4936834c5e Split LO/HO VectorConvectionNLF Grad kernels 2026-04-15 15:46:30 -07:00
camierjs da51f42c90 H100x runs 2026-04-15 15:33:31 -07:00
camierjs a7dd90466e Register VectorConvectionNLFAddMultPA, benchmarks 2026-04-15 14:35:32 -07:00
camierjs d231431ca7 LOSmemPAConvectionNLGradApply3D 2026-04-15 09:50:20 -07:00
camierjs e55b49b932 Ini kernels LO 2026-04-15 06:52:21 -07:00
camierjs e480c5f37b Register VectorConvectionNLFAddMultGradPA 2026-04-14 13:07:16 -07:00
Ketan Mittal 12eefe3c41 Merge branch 'master' into gslib-gitlab-testing 2026-04-14 12:58:08 -07:00
Mittal, Ketan 8e33891c07 initial commit 2026-04-14 12:13:15 -07:00
camierjs 3911f44906 SmemPAConvectionNLGradApply3D and FunctionCoefficient 2026-04-14 11:59:05 -07:00
Ketan Mittal 62fbabe3a5 Merge branch 'master' into findpts-surface 2026-04-14 11:49:44 -07:00
camierjs 6fa3bc57eb SmemPAConvectionNLGradApply2D running 2026-04-13 22:45:16 -07:00
camierjs 157a1f04f9 wip SmemPAConvectionNLGradApply2D 2nd part 2026-04-13 22:20:31 -07:00
camierjs 7bc531ba39 wip SmemPAConvectionNLGradApply2D 2026-04-13 21:47:37 -07:00
camierjs 9343b54c89 Added SmemPAConvectionNLApply2D 2026-04-13 18:28:46 -07:00
camierjs 0ec3e1d21a wip SmemPAConvectionNLApply3D new kernels 2026-04-13 17:12:57 -07:00
camierjs 4aa44a9b39 Merge remote-tracking branch 'refs/remotes/origin/NLConvPA' into NLConvPA 2026-04-13 13:20:34 -07:00
camierjs d191d332f8 Merge remote-tracking branch 'refs/remotes/origin/NLConvPA' into NLConvPA 2026-04-13 13:19:06 -07:00
camierjs 9dd104c211 fix fmt_FOUND 2026-04-13 13:18:33 -07:00
camierjs e62d26a450 wip Q_adj 2026-04-13 13:18:10 -07:00
camierjs 7ee86d6e75 wip SmemPAConvectionNLGradApply2D 2026-04-13 12:48:49 -07:00
Andrew Ho 7b4df2d374 Merge branch 'master' into batchmass3d 2026-04-13 09:29:35 -07:00
camierjs 3d0878ded5 Setup PA NLConv tests 2026-04-12 15:24:11 -07:00
Andrew Ho 2d7460bde1 fixed bug in how tidz was set
128 seems to offer a slightly better balance for low and high orders
2026-04-11 10:21:10 -07:00
Veselin Dobrev 65acd08d38 Small tweak in ParMesh::Print 2026-04-10 19:50:10 -07:00
Veselin Dobrev 0acc85d962 In ParMesh::PrintAsOne() fix the interface attribute to make it different
from all real boundary attributes.
2026-04-10 17:41:26 -07:00
Andrew Ho 3c45d59813 cap CPU version to batch size 1 2026-04-10 17:23:48 -07:00
Andrew Ho 63acbeb8c0 use the same batching pattern as elsewhere, hopefully fixes bugs 2026-04-10 14:39:20 -07:00
Andrew Ho 9bf6819f7a Merge remote-tracking branch 'base/fix-umpire-dep' into batchmass3d 2026-04-10 13:53:32 -07:00
Andrew Ho bed2cc5735 implemented 3D element batching for mass integrator 2026-04-10 13:48:16 -07:00
Veselin Dobrev 4e35641c28 Fix GCC warning 2026-04-10 12:57:48 -07:00
Veselin Dobrev 8594867ab6 Modify ParMesh::PrintAsOne() to use the settings of SetPrintShared() and
SetPrintInterfaces().

Added some suggestions/questions as FIXME comments.
2026-04-10 12:21:02 -07:00
John Camier 6e0fbbd3bf Merge branch 'master' into cudss-dev 2026-04-09 06:43:58 -07:00
John Camier d8fd6d95c0 Merge branch 'master' into adapt-lim-pa 2026-04-09 06:39:09 -07:00
yuyangdai a2b34ab650 Add conditional compilation for cudss_solver in ex1p 2026-04-08 15:05:50 +08:00
yuyangdai f478f687ff Update cuDSS solver integration:
- Use the full name of default communication library and threading library.
- Check the `CUDSS_COMM_LIB` and `CUDSS_THREADING_LIB` in environment first.
- Add the `cudss-solver` option in ex1p
2026-04-08 13:48:56 +08:00
daiyuyangandWill Pazner 0bf998510f Update config/defaults.mk
Co-authored-by: Will Pazner <11493037+pazner@users.noreply.github.com>
2026-04-08 09:45:40 +08:00
John Camier dbaff07ae9 Merge branch 'master' into adapt-lim-pa 2026-04-04 07:10:04 -07:00
yuyangdai d916299a49 Update INSTALL documentation for CUDSS requirements and backend support 2026-04-03 14:33:59 +08:00
yuyangdai 10840ac6b4 Refactor CuDSSSolver initialization. 2026-04-03 13:56:20 +08:00
yuyangdai b437016f6e Refactor cuDSS library path resolution in defaults.mk for improved handling of missing libraries 2026-04-02 15:01:00 +08:00
yuyangdai 5170bbe010 Fix CUDSS library checks and improve status output in CMake and makefile 2026-04-01 16:16:01 +08:00
yuyangdai f8fcdf6a97 Update cuDSS configuration and library paths in CMake and makefiles; refactor cuDSS solver integration.
- Add `MFEM_CUDSS_COMM_LIB` for OpenMPI communication library
- Add 'MFEM_CUDSS_THREADING_LIB' for threading library
- Add SetMatrixCuDSS() to set matrix values for cudss
- Remove SetMatrixSortRow()
- Remove unused options and simplify conditions in ex1.cpp and ex1p.cpp.
2026-04-01 13:49:02 +08:00
Will Pazner fdfc019cc1 Reviewer feedback 2026-03-31 09:40:16 -07:00
John Camier d10c908b38 Merge branch 'master' into cudss-dev 2026-03-29 17:51:58 -07:00
Vladimir Z Tomov 73aceea741 EnableAdaptiveLimiting for ComboIntegrator 2026-03-27 17:46:48 -07:00
John Camier 89f85cee21 Merge branch 'master' into cudss-dev 2026-03-26 13:49:44 -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
camierjs 00da00b93a Add Copyright headers 2026-03-24 08:42:34 -07:00
camierjs 4eca673111 Cosmetic trailing spaces 2026-03-24 08:33:06 -07:00
John Camier 4507a02249 Merge branch 'master' into cudss-dev 2026-03-24 08:01:00 -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
Tzanio Kolev 61c7187a86 Review comments 2026-03-12 11:17:14 -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
Tzanio KolevandCopilot 4fbefc6987 Update mesh/pmesh.cpp
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-03-11 12:20:13 -07:00
Tzanio Kolev 8dd75d2548 typo 2026-03-11 11:32:06 -07:00
Tzanio Kolev b0c2ec505f Added optional output of material interfaces in parallel 2026-03-11 11:20:59 -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
yuyangdai a3e229ef82 Make SetMatrix methods private and remove Init method
- SetMatrix methods are called inside SetOperator
- Init method has been removed as it is unnecessary
2026-02-09 16:40:39 +08:00
John Camier 00b6dcdd37 Merge branch 'master' into cudss-dev 2026-02-07 13:21:22 -08:00
John Camier d9913262df Merge branch 'master' into hypremat 2026-02-07 13:21:01 -08:00
Tzanio Kolev 19f444489f Merge branch 'master' into cudss-dev 2026-02-05 11:03:38 -08:00
daiyuyang 10c637837c Add dependency check for CUDSS in MFEMConfig.cmake.in 2026-01-22 16:56:17 +08:00
daiyuyangandAndrew Ho 665ba30f65 Update linalg/cudss.hpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2026-01-22 09:38:05 +08:00
daiyuyangandAndrew Ho d6de2c1a1d Update examples/ex1p.cpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2026-01-22 09:35:32 +08:00
daiyuyangandAndrew Ho c4b4cfdc2f Update examples/ex1p.cpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2026-01-22 09:35:18 +08:00
daiyuyangandAndrew Ho 3435475ae0 Update examples/ex1.cpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2026-01-22 09:34:32 +08:00
daiyuyangandAndrew Ho e43b58fa02 Update config/cmake/modules/FindCUDSS.cmake
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2026-01-22 09:32:27 +08:00
John Camier 27c8412439 Merge branch 'master' into cudss-dev 2026-01-21 08:23:31 -08:00
Nuno Nobre 4b9299188a Ensure hypre_CSRMatrixSetRownnz() allocs on host if ownership set to -1 2026-01-19 11:10:50 +00:00
daiyuyang 5aaef22cc0 Add conditional support for cuDSS solver in ex1.cpp 2026-01-19 15:16:31 +08:00
daiyuyang f9df36a6de Update parameter names in documentation for clarity in cudss.hpp 2026-01-19 14:21:19 +08:00
daiyuyang b3a75a9295 Add cuDSS solver option in ex1.cpp 2026-01-19 13:42:00 +08:00
daiyuyang b8c90d24f1 Make MPI optional and add OpenMP support in CuDSSSolver
- Make the MPI optional: the CuDSSSolver now does not need MPI necessary
- Add the OpenMP supports by cudssSetThreadingLayer() API
2026-01-19 13:40:56 +08:00
Ketan Mittal e0b2ba5e54 Merge branch 'master' into findpts-surface 2026-01-06 12:54:33 -08:00
Will Pazner 5cdc7499ca Merge remote-tracking branch 'origin/master' into elast-fix-4404 2025-12-29 13:00:52 -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
daiyuyang a8eba594d2 Update linalg/cudss.hpp and linalg/cudss.cpp; Revert general/device.cpp
- Move ` CUDA_REAL_T` to the cpp file.
- Remove `CuDSSHandle` singleton and replace it with a static cudssHandle_t variable.
- Delete `CuDSSHandle::Init()` from ex1p
2025-12-17 09:52:36 +08:00
Ketan Mittal 1783050f9a Merge branch 'master' into findpts-surface 2025-12-16 12:57:09 -08:00
yuyangdai 52291cadbf Revert code docs; add constructor comment; update the .gitignore 2025-12-15 13:09:45 +08:00
yuyangdai 1865b430b0 Revert makefile and example/CMakeLists.txt; delete examples/cudss 2025-12-15 11:01:42 +08:00
yuyangdai d8734b4b18 Update the examples/ex1p.cpp
- Add the global cudss handle before using the cudss solver.
2025-12-15 10:27:47 +08:00
yuyangdai eae1fa217b Add CuDSSHandle singleton and update CuDSSSolver
- Remove unused variable `myid`.
- Move `MFEM_CUDSS_CHECK` and `mfem_cudss_error` into linalg/cudss.cpp.
- Disable move copy constructor and move assignment.
- Introduce `CuDSSHandle` singleton to manage cuDSS handle lifetime.
- Rename `InitHandle` to `Init`
2025-12-15 10:27:22 +08:00
gengyan.zgy 7dc8b0d9fe Fix CuDSSSolver::ArrayMult() for single RHS 2025-12-11 14:32:20 +08:00
yuyangdai 3f236b406f Update examples/ex1p.cpp
- Add cudss solver into the ex1p.cpp
2025-12-11 13:55:16 +08:00
yuyangdai 47dcdd8159 Update config/cmake/modules/FindCUDSS.cmake and examples/cudss/CMakeLists.txt
- Add a newline at end of file
2025-12-11 13:53:49 +08:00
yuyangdai d93d36d6d3 Update linalg/cudss.cpp and linalg/cudss.hpp
- Define a cuDSS error check macro, MFEM_CUDSS_CHECK(x);
- Rename the method from InitRhsSol to SetNumRHS;
- Utilize CuMemAlloc/ CuMemcpyDtoD instead of cudaMalloc/cudaMemcpy;
- Update MPI_Comm usage.
2025-12-11 13:52:57 +08:00
yuyangdai 6bcad07a5c Update the n_global variable name for global number of rows 2025-12-05 13:50:36 +08:00
yuyangdai d56493c2c5 Refactored enum classes MatType and MatViewType, updated variable names, and update some comments. 2025-12-05 11:01:29 +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
daiyuyang ec67fe536e Merge branch 'master' into cudss-dev 2025-12-02 14:04:17 +08:00
daiyuyang eff793d6fa Merge branch 'master' into cudss-dev 2025-11-28 13:53:59 +08:00
yuyangdai e5e7280a17 Ignore the solution files of cudss sample run 2025-11-24 15:18:53 +08:00
yuyangdai 773dc57712 Using the make style to format the class CuDSSSolver and example/cudss/ex1p codes 2025-11-24 15:16:48 +08:00
yuyangdai 98a043b195 Update the doc files for CuDSSSolver 2025-11-24 15:15:22 +08:00
yuyangdai c2a3c83099 Update the makefiles for CuDSSSolver 2025-11-24 15:14:51 +08:00
daiyuyang 3ea90aff27 Add cudss solver 2025-11-24 15:10:45 +08:00
gengyan.zgy 23a362d6ad Update cmake files for cuDSS 2025-11-24 15:10:30 +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
Tzanio Kolev 44ca1fbf8f minor 2024-07-18 09:36:14 -07:00
Tzanio Kolev 27b84d79b8 Verify that ordering is byVDIM when using the AMG elasticity solver 2024-07-18 09:30:52 -07:00
128 changed files with 13788 additions and 4266 deletions
+2 -2
View File
@@ -53,7 +53,7 @@ runs:
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- uses: mfem/github-actions/build-mfem@v2.5
- uses: mfem/github-actions/build-mfem@v2.6
if: ${{steps.debug.outputs.cache-hit != 'true'}}
env:
CXXFLAGS: ${{env.CXXFLAGS}}
@@ -82,7 +82,7 @@ runs:
run: find . -type f -name '*.o' -delete
shell: bash
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v7
with:
name: build-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build
+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
+42
View File
@@ -0,0 +1,42 @@
# MFEM Pull Request Review Agent Guide
## Purpose and scope
Review MFEM PRs for correctness, maintainability, performance, portability, test coverage, and MFEM consistency. Use the diff and PR context; reference source files, tests, and CI results when available. Follow `CONTRIBUTING.md`, especially Developer Guidelines, PR rules, checklist, and testing.
## Critical review pillars
- Correctness and numerical behavior
- API and user-facing impact
- Performance implications
- Maintainability and portability
## Review workflow
1. Read the PR description, linked issues, and intended behavior.
2. Inspect the diff before commenting.
3. Identify affected MFEM components, examples, tests, build or docs changes, and downstream APIs.
4. Analyze the code against the critical review pillars.
5. Compare the change against nearby code and MFEM patterns; flag unmotivated deviations.
6. Check whether tests and documentation were updated appropriately.
7. Review CI results and suggest actions.
8. Produce a structured review with prioritized findings.
9. Always limit conclusions to available evidence.
## MFEM-specific review checklist
- Component-aware scope: identify the touched subsystem (FEM, solvers, preconditioners, linear algebra, mesh, examples, miniapps, build, or docs) and assess its impact against the review pillars.
- Numerical and algorithmic behavior: assess issues in convergence, stability, tolerances, precision, iteration limits, and failure handling. If clear opportunities exist to improve the algorithmic approach, call them out with expected impact.
- API and user-facing impact: assess backward compatibility, user-visible behavior and default changes, migration impact, deprecations, and whether documentation clearly explains user-facing API changes.
- Data structure and memory semantics: assess ownership, lifetime, aliasing, container behavior, and device-host synchronization.
- Parallel and serial behavior: assess whether the change preserves equivalent semantics in serial and parallel modes where applicable; if logic is currently mode-specific, check whether extension to the other mode is straightforward (clear abstractions, no hard-wired assumptions), document constraints, and call out expected behavior differences explicitly.
- Backend and portability impact: assess likely cross-backend risks in CPU, CUDA, HIP, OCCA, RAJA, partial assembly, fallback paths, compiler compatibility, and platform assumptions.
- Build, dependency, and configuration impact: assess CMake or make changes, optional dependency behavior, and feature-flag interactions.
- Tests and docs alignment: check available regression or unit coverage evidence for changed behavior, and ensure docs are updated for new flags, APIs, options, or behavior changes.
- MFEM developer-guideline fit: keep code lean, simple, general, logically separated, and portable; suggest C++17 improvements when they clearly improve safety, clarity, or maintainability.
- New source files, examples, or miniapps: if a PR adds source/header files, verify they are properly wired into the relevant `makefile` and `CMakeLists.txt`, referenced in docs where applicable (including `doc/CodeDocumentation.dox`), and added to top-level `.gitignore` only when generated artifacts require it.
- Changelog: verify `CHANGELOG` is updated if the PR introduces significant new features or user-facing changes.
- MFEM conventions: use `real_t`; use `mfem::out`/`mfem::err` instead of `std::cout`/`std::cerr` in library code; flag large/binary files; if AI assistance is apparent but undisclosed, suggest following `CONTRIBUTING.md`.
- Edge cases: if the PR touches complex or error-prone areas, suggest additional tests for edge cases, failure modes, and parallel behavior.
## Commenting guidelines
- Keep comments concise, actionable, and grounded in the diff.
- Focus on correctness, behavior changes, and user impact over style nits.
- Be professional, concise, collaborative, technically precise, and avoid unsupported assumptions.
+1 -1
View File
@@ -13,7 +13,7 @@ Note that some of these scripts use the shared MFEM GitHub Actions from the exte
<https://github.com/mfem/github-actions>
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch in the above from which the action is taken.
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.5`, the `v2.5` suffix denotes the branch (or tag) in the above from which the action is taken.
The current CI workflows are:
+8 -6
View File
@@ -232,7 +232,7 @@ jobs:
- name: get hypre
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
uses: mfem/github-actions/build-hypre@v2.5
uses: mfem/github-actions/build-hypre@v2.6
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
@@ -242,7 +242,7 @@ jobs:
- name: get hypre (Windows)
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
uses: mfem/github-actions/build-hypre@v2.5
uses: mfem/github-actions/build-hypre@v2.6
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
@@ -262,7 +262,7 @@ jobs:
- name: install metis
if: matrix.mpi == 'par' && matrix.os != 'windows-latest' && steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.5
uses: mfem/github-actions/build-metis@v2.6
with:
archive: ${{ matrix.os != 'macos-latest' && env.METIS_ARCHIVE || env.METIS_ARCHIVE_MAC }}
dir: ${{ env.METIS_TOP_DIR }}
@@ -304,7 +304,7 @@ jobs:
# MFEM build and test
- name: build
uses: mfem/github-actions/build-mfem@v2.5
uses: mfem/github-actions/build-mfem@v2.6
env:
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
with:
@@ -375,8 +375,10 @@ jobs:
# Code coverage (process and upload reports)
- name: codecov
if: matrix.codecov == 'YES'
uses: mfem/github-actions/upload-coverage@v2.5
uses: mfem/github-actions/upload-coverage@v2.6
with:
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
project_dir: ${{ env.MFEM_TOP_DIR }}
directories: "fem general linalg mesh"
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+3 -3
View File
@@ -57,7 +57,7 @@ jobs:
- name: Get Hypre
if: steps.hypre-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.5
uses: mfem/github-actions/build-hypre@v2.6
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
@@ -72,14 +72,14 @@ jobs:
- name: Install Metis
if: steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.5
uses: mfem/github-actions/build-metis@v2.6
with:
archive: ${{ env.METIS_ARCHIVE }}
dir: ${{ env.METIS_TOP_DIR }}
# MFEM build and test
- name: build-mfem
uses: mfem/github-actions/build-mfem@v2.5
uses: mfem/github-actions/build-mfem@v2.6
with:
os: ${{ runner.os }}
target: opt
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
uses: ./.github/actions/sanitize/mpi
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.5
uses: mfem/github-actions/build-hypre@v2.6
with:
archive: ${{env.HYPRE_TGZ}}
dir: ${{env.HYPRE_DIR}}
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
uses: ./.github/actions/sanitize/mpi
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.5
uses: mfem/github-actions/build-metis@v2.6
with:
archive: ${{env.METIS_TGZ}}
dir: ${{env.METIS_DIR}}
+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}}
+5
View File
@@ -85,3 +85,8 @@ opt_par_gcc_10_pumi:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 +pumi"
opt_par_gcc_10_gslib:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 +gslib"
+5
View File
@@ -63,3 +63,8 @@ opt_mpi_cuda_hypre_cuda_gcc:
extends: .mfem_job_on_matrix
variables:
SPEC: "%gcc@10.3.1 +mpi +cuda cuda_arch=90 ^hypre+cuda"
opt_mpi_cuda_gcc_gslib:
extends: .mfem_job_on_matrix
variables:
SPEC: "%gcc@10.3.1 +mpi +cuda +gslib cuda_arch=90 ^hypre+cuda"
+2 -2
View File
@@ -32,9 +32,9 @@ mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
# run
if [[ "${MACHINE_NAME}" == "dane" ]]; then
salloc --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
srun --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
elif [[ ${MACHINE_NAME} == "corona" ]]; then
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
srun --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
else
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
exit 1
+27 -8
View File
@@ -8,11 +8,19 @@
https://mfem.org
Version 4.10 (development)
==========================
Version 4.9.1 (development)
===========================
- Policy for AI-assisted contribution added to CONTRIBUTING.md
Discretization improvements
---------------------------
- Added NVIDIA cuDSS library interface. Implementation examples have been
added to ex1 and ex1p. See https://developer.nvidia.com/cudss for more
details. Supported versions >= 0.6.0.
- Extend FindPointsGSLIB to support surface meshes.
- Replaced legacy simplex quadrature rules with symmetric positive-weight
rules for triangles (orders 0-25) and tetrahedra (orders 0-20). These
rules guarantee all-positive weights and interior quadrature points,
@@ -23,12 +31,6 @@ Discretization improvements
Tet rules (d=14-20): Chuluunbaatar et al., Comput. Math. Appl. 124:89-97,
2022.
Version 4.9.1 (development)
===========================
Discretization improvements
---------------------------
- Improved the gridfunction projection routines. Projections work for Scalar,
Vector and VectorFE, also NURBS versions. Optionally different types of
projections can be selected, default behaviour has not changed.
@@ -41,11 +43,28 @@ Meshing improvements
- Improved support for 1D NURBS meshes with variable order, including using
the patches construct for 1D NURBS meshes.
- Added the option to include material interfaces (faces separating elements
with different element attributes) as additional boundary elements, for
parallel visualization, e.g. with GLVis. This is supported by both the Print
and PrintAsOne methods of ParMesh. See ParMesh::SetPrintInterfaces().
New and updated examples and miniapps
-------------------------------------
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
capability.
GPU computing
-------------
- Allow specifying GPU kernel launch bounds for native and RAJA GPU backends.
Miscellaneous
-------------
- Fixed signed DOF handling in parallel grid-function reading (read constructor)
and saving via ParGridFunction::SaveAsOne(). Simplified the process of
applying the DOF signs by using the new method ApplyDofSigns() in class
ParFiniteElementSpace -- the method will return immediately if no sign flips
are needed.
Version 4.9, released on Dec 11, 2025
=====================================
+10 -1
View File
@@ -433,6 +433,15 @@ if (MFEM_USE_STRUMPACK)
endif()
endif()
# cuDSS can only be enabled in CUDA
if (MFEM_USE_CUDSS)
if (MFEM_USE_CUDA)
find_package(CUDSS REQUIRED)
else()
message(FATAL_ERROR " *** cuDSS requires that CUDA be enabled.")
endif()
endif()
# GnuTLS
if (MFEM_USE_GNUTLS)
find_package(_GnuTLS REQUIRED)
@@ -631,7 +640,7 @@ find_package(Threads REQUIRED)
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB HDF5
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CUDSS CALIPER CODIPACK
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
ALGOIM ENZYME CUDA::cudart)
+73 -65
View File
@@ -3,12 +3,13 @@
</p>
<p align="center">
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-brightgreen.svg"></a>
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Arepo-check+branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuild-analysis+branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/actions?query=workflow%3Abuilds-and-tests+branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-blue.svg"></a>
<a href="https://github.com/mfem/mfem/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/v/release/mfem/mfem"></a>
<a href="https://github.com/mfem/mfem/actions/workflows/repo-check.yml?query=branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml?query=branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml?query=branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
<a href="https://ci.appveyor.com/project/mfem/mfem"><img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/19non9sqm6msi2wy?svg=true"></a>
<a href="https://docs.mfem.org/html/index.html"><img alt="Doxygen" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
<a href="https://docs.mfem.org/html/index.html"><img alt="Documentation" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
</p>
@@ -24,6 +25,14 @@ must be made under this license.
Note also that MFEM has a [Code of Conduct](CODE_OF_CONDUCT.md). By participating
in the MFEM community, you agree to abide by its rules.
## AI Policy
- Use of AI code generation in MFEM is allowed but must be disclosed, e.g. by
selecting the `AI-assisted` label on the PR.
- By submitting a PR, the author acknowledges that they have reviewed and
understand the changes they are proposing.
- PR authors are still responsible for correctness, licensing, and attribution
of all changes.
If you plan on contributing to MFEM, consider reviewing the
[issue tracker](https://github.com/mfem/mfem/issues) first to check if a thread
already exists for your desired feature or the bug you ran into. Use a pull
@@ -76,7 +85,7 @@ Origin](#developers-certificate-of-origin-11) at the end of this file.*
follow the [MFEM PR Rules](#mfem-pr-rules).
- When your contribution is fully working and ready to be reviewed, add
the `ready-for-review` label.
- PRs are treated similarly to journal submission with an "editor" assigning two
- PRs are treated similarly to journal submission, with an "editor" assigning two
reviewers to evaluate the changes.
- The reviewers have 3 weeks to evaluate the PR and work with the author to
fix issues and implement improvements.
@@ -117,7 +126,7 @@ The MFEM source code has the following structure:
│ ├── petsc
│ ├── pumi
│ ├── sundials
| └── superlu
└── superlu
├── fem
│ ├── ceed
│ ├── dfem
@@ -129,10 +138,6 @@ The MFEM source code has the following structure:
│ ├── moonolith
│ ├── qinterp
│ └── tmop
│ | ├── assemble
│ | ├── metrics
│ | ├── mult
│ | └── tools
├── general
├── linalg
│ ├── batched
@@ -145,11 +150,10 @@ The MFEM source code has the following structure:
│ ├── common
│ ├── contact
│ ├── dfem
│ ├── diag-smoothers
│ ├── dpg
│ ├── electromagnetics
│ ├── fluids
│ │ ├── navier
│ │ └── schrodinger-flow
│ ├── gslib
│ ├── hdiv-linear-solver
│ ├── hooke
@@ -159,6 +163,7 @@ The MFEM source code has the following structure:
│ ├── nurbs
│ ├── parelag
│ ├── performance
│ ├── plasma
│ ├── shifted
│ ├── solvers
│ ├── spde
@@ -189,15 +194,15 @@ respectively.
- The main finite element classes are:
+ [`FiniteElement`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElement.html)
+ [`FiniteElementCollection`](https://docs.mfem.org/html/classmfem_1_1FiniteElementCollection.html)
+ [`FiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1FiniteElementSpace.html)
+ [`GridFunction`](https://docs.mfem.org/html/classmfem_1_1GridFunction.html)
+ [`BilinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1BilinearFormIntegrator.html) and [`LinearFormIntegrator`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html)
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearFormIntegrator.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
+ [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html), [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`MixedBilinearForm`](https://docs.mfem.org/html/classmfem_1_1MixedBilinearForm.html)
- The main linear algebra classes and sources are
+ [`Operator`](https://docs.mfem.org/html/classmfem_1_1Operator.html) and [`BilinearForm`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html)
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1BilinearForm.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
+ [`Vector`](https://docs.mfem.org/html/classmfem_1_1Vector.html) and [`LinearForm`](https://docs.mfem.org/html/classmfem_1_1LinearForm.html)
+ [`DenseMatrix`](https://docs.mfem.org/html/classmfem_1_1DenseMatrix.html) and [`SparseMatrix`](https://docs.mfem.org/html/classmfem_1_1SparseMatrix.html)
+ Sparse [smoothers](https://docs.mfem.org/html/sparsesmoothers_8hpp.html) and linear [solvers](https://docs.mfem.org/html/solvers_8hpp.html)
@@ -209,8 +214,8 @@ shared geometric entities between different tasks. The parallel source files
have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
- The main parallel classes are
+ [`ParMesh`](https://docs.mfem.org/html/solvers_8hpp.html)
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
+ [`ParMesh`](https://docs.mfem.org/html/classmfem_1_1ParMesh.html)
+ [`ParNCMesh`](https://docs.mfem.org/html/classmfem_1_1ParNCMesh.html)
+ [`ParFiniteElementSpace`](https://docs.mfem.org/html/classmfem_1_1ParFiniteElementSpace.html)
+ [`ParGridFunction`](https://docs.mfem.org/html/classmfem_1_1ParGridFunction.html)
+ [`ParBilinearForm`](https://docs.mfem.org/html/classmfem_1_1ParBilinearForm.html) and [`ParLinearForm`](https://docs.mfem.org/html/classmfem_1_1ParLinearForm.html)
@@ -220,14 +225,14 @@ have a `p` prefix, e.g. `pmesh.cpp` vs. the serial `mesh.cpp`.
#### GPU and general device support
GPU and multi-core CPU support is based on device kernels supporting different
backends (CUDA, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
backends (CUDA, HIP, OCCA, RAJA, OpenMP, etc.) and an internal lightweight
device/host memory manager.
- The main device-relevant classes and sources are:
+ [`Device`](https://docs.mfem.org/html/device_8hpp.html)
+ [`MemoryManager`](https://docs.mfem.org/html/mem_manager_8hpp.html)
+ the [`mfem::forall`](https://docs.mfem.org/html/forall_8hpp.html) function
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
+ the [`cuda.hpp`](https://docs.mfem.org/html/cuda_8hpp.html), [`hip.hpp`](https://docs.mfem.org/html/hip_8hpp.html) and [`occa.hpp`](https://docs.mfem.org/html/occa_8hpp.html) files
#### Utilities, building and documentation
- The `general/` directory contains C++ classes that serve as utilities for
@@ -241,8 +246,8 @@ device/host memory manager.
- `examples` and `miniapps` respectively gather simple and more fully-featured
demonstrations of the usage on MFEM. They both rely on `data/` for the
collection of meshes.
- The `tests/` directory contains a unit test suite and will later contain more
tests that run example codes.
- The `tests/` directory contains a unit test suite, additional tests, and
benchmarks.
See also the [code overview](https://mfem.org/code-overview/) section on the MFEM
website.
@@ -276,8 +281,8 @@ Before you can start, you need a GitHub account, here are a few suggestions:
the top of https://github.com/mfem.
- Consider making your membership public by going to https://github.com/orgs/mfem/people
and clicking on the organization visibility drop box next to your name.
- Project discussions and announcements will be posted at
https://github.com/orgs/mfem/teams/everyone.
- Project discussions and announcements will be posted at https://github.com/orgs/mfem/discussions,
tagging the `@mfem/everyone` team when appropriate.
#### Structure
- The MFEM source code is in the [mfem](https://github.com/mfem/mfem)
@@ -337,11 +342,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
- Well-designed simple code is frequently more general and powerful.
- Lean code base is easier to understand by new collaborators.
- New features should be added only if they are necessary or generally useful.
- Introduction of language constructions not currently used in MFEM should be
- Introduction of language constructs not currently used in MFEM should be
justified and generally avoided (to maintain portability to various systems
and compilers, including early access hardware).
- We prefer basic C++ and the C++03 standard, to keep the code readable by
a large audience and to make sure it compiles anywhere.
- We prefer basic C++. Use C++17 features judiciously, prioritizing readability,
consistency with existing MFEM code, and portability to different systems,
compilers and device backends.
- *Keep the code general and reasonably efficient*
- The main goal is fast prototyping for research and application development.
@@ -384,7 +390,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
- When your branch is ready for other developers to review / comment on
the code, create a pull request towards `mfem:master`.
- Pull request typically have titles like:
- Pull requests typically have titles like:
`Description [new-feature-dev]`
@@ -405,12 +411,12 @@ Before you can start, you need a GitHub account, here are a few suggestions:
- Add a description, appropriate labels and assign yourself to the PR. The MFEM
team will add reviewers as appropriate.
- List outstanding TODO items in the description, see PR #222 for an example.
- List outstanding TODO items in the description.
- When your contribution is fully working and ready to be reviewed, add
the `ready-for-review` label.
or request the `ready-for-review` label.
- PRs are treated similarly to journal submission with an "editor" assigning
- PRs are treated similarly to journal submission, with an "editor" assigning
two reviewers to evaluate the changes. The reviewers have 3 weeks to evaluate
the PR and work with the author to implement improvements and fix issues.
@@ -436,7 +442,7 @@ Before you can start, you need a GitHub account, here are a few suggestions:
checks in GitHub Actions enforce MFEM-specific rules which are explained in
the error messages and the `tests/scripts` directory.
- Also note that the tests `branch-history` and `repos-checks` found in GitHub
- Also note that the tests `branch-history` and `repo-check` found in GitHub
Actions can be triggered automatically before each push using git hooks. See
the [git hooks README](config/githooks/README.md) for a detailed explanation.
@@ -493,15 +499,15 @@ Everyone on the MFEM team can be asked to serve as a reviewer on a PR in their a
3. To ensure the quality of the PR by making sure that the code adheres to the [Developer Guidelines](#developer-guidelines), e.g. all methods, data members, and functions have documentation, including data ownership and lifetime, new examples/miniapps have a corresponding PR in mfem/web, major features have `CHANGELOG` entries, etc.
3. To seek help from the editors in case of difficulties.
4. To seek help from the editors in case of difficulties.
4. To complete the review in a timely manner: 3 weeks from assignment.
5. To complete the review in a timely manner: 3 weeks from assignment.
5. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
6. To test the PR thoroughly before merging in *next*. The PR author is also encouraged to perform testing and inform the reviewers about the results.
6. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
7. To monitor the PR impact on the testing in the *next* branch and alert the editors that the PR is ready for merging in *master*.
7. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
8. The review of bugfixes should be expedited proportional to their importance. The review window can be much less than three weeks in such cases.
#### Responsibilities of Authors
@@ -527,30 +533,30 @@ Before a PR can be merged, it should satisfy the following:
- [ ] Code builds.
- [ ] Code passes `make style`.
- [ ] Update `CHANGELOG`:
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
- [ ] Is this a new feature users need to be aware of? New or updated example or miniapp?
- [ ] Does it make sense to create a new section in the `CHANGELOG` to group with other related features?
- [ ] Update `INSTALL`:
- [ ] Had a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
- [ ] Have the version ranges for any required or optional libraries changed?
- [ ] Does `make` or `cmake` have a new target?
- [ ] Did the requirements or the installation process change? *(rare)*
- [ ] Has a new optional library been added? If so, what range of versions of this library are required? (*Make sure the external library is compatible with our BSD license, e.g. it is not licensed under GPL!*)
- [ ] Have the version ranges for any required or optional libraries changed?
- [ ] Does `make` or `cmake` have a new target?
- [ ] Did the requirements or the installation process change? *(rare)*
- [ ] Update continuous integration server configurations if necessary (e.g. with new version requirements for each of MFEM's dependencies)
- [ ] `.github`
- [ ] `.appveyor.yml`
- [ ] `.github`
- [ ] `.appveyor.yml`
- [ ] Update `.gitignore`:
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
- [ ] Add new patterns (just for the new files above) and re-run the above test.
- [ ] Check if `make distclean; git status` shows any files that were generated from the source by the project (not an IDE) but we don't want to track in the repository.
- [ ] Add new patterns (just for the new files above) and re-run the above test.
- [ ] New examples:
- [ ] All sample runs at the top of the example source file work.
- [ ] Update `examples/makefile`:
- [ ] All sample runs at the top of the example source file work.
- [ ] Update `examples/makefile`:
- [ ] Add the example code to the appropriate `SEQ_EXAMPLES` and `PAR_EXAMPLES` variables.
- [ ] Add any files generated by it to the `clean` target.
- [ ] Add the example binary and any files generated by it to the top-level `.gitignore` file.
- [ ] Update `examples/CMakeLists.txt`:
- [ ] Update `examples/CMakeLists.txt`:
- [ ] Add the example code to the `ALL_EXE_SRCS` variable.
- [ ] Make sure `THIS_TEST_OPTIONS` is set correctly for the new example.
- [ ] List the new example in `doc/CodeDocumentation.dox`.
- [ ] If new examples directory (e.g.`examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
- [ ] If new examples directory (e.g. `examples/pumi`), list it in `doc/CodeDocumentation.conf.in`
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
- [ ] Update or add example-specific documentation, see e.g. the `src/examples.md`.
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
@@ -567,13 +573,13 @@ Before a PR can be merged, it should satisfy the following:
- [ ] Add/update the `CMakeLists.txt` file in the new miniapp directory.
- [ ] Consider adding a new test for the new miniapp.
- [ ] List the new miniapp in `doc/CodeDocumentation.dox`
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
- [ ] If new miniapps directory (e.g.`miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), add it to `MINIAPP_SUBDIRS` in the `makefile`.
- [ ] If new miniapps directory (e.g. `miniapps/nurbs`), list it in `doc/CodeDocumentation.conf.in`
- [ ] Companion pull request for documentation in [mfem/web](https://github.com/mfem/web) repo:
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
- [ ] Update or add miniapp-specific documentation, see e.g. the `src/meshing.md` and `src/electromagnetics.md` files.
- [ ] Add the description, labels and screenshots in `src/examples.md` and `src/img`.
- [ ] The miniapps go at the end of the page, and are usually listed only under a specific "Application (PDE)" category.
- [ ] Add a short description of the miniapp in the "Extensive Examples" section of `features.md`.
- [ ] New capability:
- [ ] All new public, protected, and private classes, methods, data members, and functions have full Doxygen-style documentation in source comments. Documentation should include descriptions of member data, function arguments and return values, template parameters, and prerequisites for calling new functions.
- [ ] Pointer arguments and return values must specify whether ownership is being transferred or lent with the call.
@@ -675,7 +681,7 @@ MFEM uses a `master`/`next`-branch workflow as described below:
- [ ] Update URL shortlinks:
- [ ] Create a shortlink at [http://bit.ly/](http://bit.ly/) for the release tarball, e.g. https://mfem.github.io/releases/mfem-3.1.tgz.
- [ ] (LLNL only) Add and commit the new shortlink in the `links` and `links-mfem` files of the internal `mfem/downloads` repo.
- [ ] Add the new shortlinks to the MFEM packages in `spack`, `homebrew/science`, `VisIt`, etc.
- [ ] Add the new shortlinks to the MFEM package in `spack`.
- [ ] Update website in `mfem/web` repo:
- Update version and shortlinks in `src/index.md` and `src/download.md`.
- Use [cloc-1.62.pl](http://cloc.sourceforge.net/) and `ls -lh` to estimate the SLOC and the tarball size in `src/download.md`.
@@ -727,22 +733,24 @@ commit or push, see the [README](config/githooks/README.md) in the `config/githo
directory.
### Linux and Mac smoke tests
### GitHub Actions smoke tests
We use GitHub Actions to drive the default tests on the `master` and `next`
branches. See the `.github/workflows` files and the logs at
[https://github.com/mfem/mfem/actions](https://github.com/mfem/mfem/actions).
Testing using GitHub Actions should be kept lightweight, as there is a time
constraint on jobs. Two virtual machines are configured - Mac (OS X) and Linux.
GitHub Actions testing should be kept lightweight, as there is a time
constraint on jobs. The current workflows cover Linux, macOS, and Windows
configurations.
- Tests on the `master` branch are triggered whenever a PR is issued on this branch.
- Tests on the `next` branch are currently scheduled to run each night.
### Additional Windows smoke test
### Windows smoke test
We use Appveyor to test building with the MS Visual C++ compiler in a Windows
environment, as well as to test the CMake build. See the `.appveyor` file and the
build logs at
We also use Appveyor to test building with the MS Visual C++ compiler in a Windows
environment, as well as to test the CMake build. See the `.appveyor.yml` file
and the build logs at
[https://ci.appveyor.com/project/mfem/mfem](https://ci.appveyor.com/project/mfem/mfem).
CMake is used to generate the MSVC Project files and drive the build. A release
+31 -16
View File
@@ -38,14 +38,13 @@ the option MFEM_USE_METIS.
MFEM also includes support for devices such as GPUs, and programming models such
as CUDA, HIP, OCCA, OpenMP and RAJA.
- Starting with version 4.0, MFEM requires a C++11 compiler. We recommend using
a newer compiler, e.g. GCC version 4.9 or higher.
- Starting with version 4.9, MFEM requires a C++17 compiler.
- CUDA support requires an NVIDIA GPU and an installation of the CUDA Toolkit
https://developer.nvidia.com/cuda-toolkit
- HIP support requires an AMD GPU and an installation of the ROCm software stack
https://rocmdocs.amd.com
https://rocm.docs.amd.com
- OCCA support requires the OCCA library
https://libocca.org
@@ -83,9 +82,9 @@ Serial build:
Parallel build:
(download hypre and METIS 4 from above URLs)
(build METIS 4 in ../metis-4.0 relative to mfem/)
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
(build hypre in ../hypre relative to mfem/)
make parallel -j 4
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
CUDA build:
make cuda -j 4
@@ -115,14 +114,14 @@ Serial build:
Parallel build:
(download hypre and METIS 4 from above URLs)
(build METIS 4 in ../metis-4.0 relative to mfem/)
(for METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
(build hypre in ../hypre relative to mfem/)
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES
make -j 4
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
Parallel build with fetching of hypre and METIS:
mkdir <mfem-buil-dir> ; cd <mfem-build-dir>
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DMFEM_FETCH_TPLS=YES
make -j 4
@@ -134,7 +133,8 @@ CUDA build:
HIP build:
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 -DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 \
-DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
make -j 4
Example codes (serial/parallel, depending on the build):
@@ -269,6 +269,7 @@ Compilers:
CXX - C++ compiler, serial build
MPICXX - MPI C++ compiler, parallel build
CUDA_CXX - The CUDA compiler, 'nvcc' or 'clang++'
HIP_CXX - The HIP compiler, e.g. 'hipcc'
Compiler options:
OPTIM_FLAGS - Options for optimized build
@@ -395,6 +396,11 @@ MFEM_USE_STRUMPACK = YES/NO
classes. When enabled, this option uses the STRUMPACK_* library options, see
below.
MFEM_USE_CUDSS = YES/NO
Enable MFEM functionality based on the cuDSS library. When using cuDSS, CUDA
support must be also enabled in MFEM, i.e. MFEM_USE_CUDA=YES must be set.
When enabled, this option uses the CUDSS_* library options, see below.
MFEM_USE_GINKGO = YES/NO
Enable MFEM functionality based on the Ginkgo library, which provides
iterative linear solvers and preconditioners with OpenMP, CUDA backends, see
@@ -554,13 +560,13 @@ MFEM_USE_RAJA = YES/NO
MFEM_USE_OCCA = YES/NO
Enables support for the OCCA library in MFEM. OCCA is an open-source library
which aims to make it easy to program different types of devices (e.g. CPU,
GPU, FPGA) by providing an unified API for interacting with JIT-compiled
GPU, FPGA) by providing a unified API for interacting with JIT-compiled
backends. In order to use the OCCA CUDA backend, CUDA support must be enabled
in MFEM as well, i.e. MFEM_USE_CUDA=YES must be set.
MFEM_USE_GSLIB = YES/NO
Enables MFEM functionality based on the GSLIB library, and specifically its
FindPoints component, which provides a robust algorithms to evaluate finite
FindPoints component, which provides robust algorithms to evaluate finite
element functions in a collection of points in physical space. When enabled,
the user can use the GSLIB-FindPoints methods as shown in miniapps/gslib.
@@ -719,9 +725,18 @@ The specific libraries and their options are:
Options: STRUMPACK_OPT, STRUMPACK_LIB.
Versions: STRUMPACK >= 3.0.0.
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Note that Ginkgo needs a
C++ compiler that supports the C++-17 standard. For additional requirements
and dependencies of specific modules, see the Ginkgo webpage below.
- CUDSS (optional), used when MFEM_USE_CUDSS = YES. Note that CUDSS requires
CUDA 12.x toolkit and the cuDSS libraries. The supported communication backend
is OpenMPI 4.x (default), and OpenMPI 4.x or a later version must be pre-built.
The source files in the cuDSS tarball provide guidance for developing custom
MPI implementations.
URL: https://developer.nvidia.com/cudss
https://docs.nvidia.com/cuda/cudss/advanced_features.html#communication-layer-library-in-cudss
Options: CUDSS_OPT, CUDSS_LIB.
Versions: cuDSS >= 0.6.0.
- Ginkgo (optional), used when MFEM_USE_GINKGO = YES. Ginkgo may have additional
requirements and module-specific dependencies; see the webpage below.
URL: https://ginkgo-project.github.io
Options: GINKGO_OPT, GINKGO_LIB, GINKGO_DIR, GINKGO_BUILD_TYPE (Release or
Debug).
@@ -793,7 +808,7 @@ The specific libraries and their options are:
Options: CONDUIT_OPT, CONDUIT_LIB.
Versions: Conduit >= 0.3.1.
- ADIOS2 (optional) used when MFEM_USE_ADIOS2 = YES.
- ADIOS2 (optional), used when MFEM_USE_ADIOS2 = YES.
URL: https://adios2.readthedocs.io/
Versions: ADIOS >= 2.5.0.
@@ -869,7 +884,7 @@ The specific libraries and their options are:
Options: RAJA_DIR, RAJA_OPT, RAJA_LIB.
Versions: RAJA >= 2022.10.3.
- Moonolith (optional), use when MFEM_USE_MOONOLITH = YES.
- Moonolith (optional), used when MFEM_USE_MOONOLITH = YES.
URL: https://bitbucket.org/zulianp/par_moonolith
Options: MOONOLITH_DIR
Versions: MOONOLITH >= 1.1.0.
@@ -957,7 +972,7 @@ CMAKE_BUILD_TYPE which can be set to standard values like "Debug", and "Release"
To use a specific generator use the "-G <generator>" option of cmake:
cmake <mfem-source-dir> -G "Xcode"
cmake <mfem-source-dir> -G "Visual Studio 12 2013"
cmake <mfem-source-dir> -G "Visual Studio 17 2022"
cmake <mfem-source-dir> -G "MinGW Makefiles"
With CMake it is possible to build MFEM as a shared library using the standard
@@ -1202,7 +1217,7 @@ larger problems, there are two options:
Specific options for HIP
========================
MFEM expects the `ROCM_PATH` environment variable to be set to the path of the
ROCM install, as well as having `$ROCM_PATH/bin` in `PATH`.
ROCm install, as well as having `$ROCM_PATH/bin` in `PATH`.
Specific options for RAJA+HIP+MPI
=================================
+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
+5
View File
@@ -35,6 +35,7 @@ set(MFEM_USE_SUITESPARSE @MFEM_USE_SUITESPARSE@)
set(MFEM_USE_SUPERLU @MFEM_USE_SUPERLU@)
set(MFEM_USE_MUMPS @MFEM_USE_MUMPS@)
set(MFEM_USE_STRUMPACK @MFEM_USE_STRUMPACK@)
set(MFEM_USE_CUDSS @MFEM_USE_CUDSS@)
set(MFEM_USE_GINKGO @MFEM_USE_GINKGO@)
set(MFEM_USE_AMGX @MFEM_USE_AMGX@)
set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
@@ -109,6 +110,10 @@ if (MFEM_USE_RAJA)
find_dependency(RAJA)
endif()
if (MFEM_USE_CUDSS)
find_dependency(cudss)
endif (MFEM_USE_CUDSS)
if (MFEM_USE_UMPIRE)
find_dependency(umpire)
endif()
+9
View File
@@ -108,6 +108,15 @@
// Enable MFEM functionality based on the STRUMPACK library.
#cmakedefine MFEM_USE_STRUMPACK
// Enable MFEM functionality based on the cuDSS library.
#cmakedefine MFEM_USE_CUDSS
// CUDSS communication layer library path
#cmakedefine MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
// CUDSS threading layer library path
#cmakedefine MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
// Enable functionality based on the Ginkgo library.
#cmakedefine MFEM_USE_GINKGO
+68
View File
@@ -0,0 +1,68 @@
if (NOT cudss_DIR AND CUDSS_DIR)
set(cudss_DIR ${CUDSS_DIR}/lib/cmake/cudss)
endif()
message(STATUS "Looking for CUDSS ...")
message(STATUS " in CUDSS_DIR = ${CUDSS_DIR}")
message(STATUS " cudss_DIR = ${cudss_DIR}")
find_package(cudss)
set(CUDSS_FOUND ${cudss_FOUND})
set(CUDSS_LIBRARIES "cudss")
if (CUDSS_FOUND)
message(STATUS
"Found CUDSS target: ${CUDSS_LIBRARIES} (version: ${cudss_VERSION})")
else()
set(msg STATUS)
if (CUDSS_FIND_REQUIRED)
set(msg FATAL_ERROR)
endif()
message(${msg}
"CUDSS not found. Please set CUDSS_DIR to the install prefix.")
endif()
if(CUDSS_FOUND AND TARGET cudss)
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION)
if(NOT CUDSS_LIBRARY_LOCATION)
get_target_property(CUDSS_LIBRARY_LOCATION cudss IMPORTED_LOCATION_RELEASE)
endif()
if(CUDSS_LIBRARY_LOCATION)
get_filename_component(CUDSS_LIBRARY_DIR "${CUDSS_LIBRARY_LOCATION}" DIRECTORY)
else()
message(WARNING "Could not determine the location of the cuDSS library.")
endif()
else()
message(WARNING "cuDSS target not available; cannot determine library directory.")
endif()
# Set the full name of the cuDSS threading library if OpenMP is enabled.
# The threading layer library (libcudss_mtlayer_gomp.so) is located under the
# cuDSS library directory by default.
if (MFEM_USE_OPENMP)
find_file(
CUDSS_THREADING_LIB
NAMES libcudss_mtlayer_gomp.so
PATHS ${CUDSS_LIBRARY_DIR}
NO_DEFAULT_PATH
)
if (NOT DEFINED MFEM_CUDSS_THREADING_LIB AND CUDSS_THREADING_LIB)
set(MFEM_CUDSS_THREADING_LIB "${CUDSS_THREADING_LIB}")
endif()
message(STATUS "CUDSS threading layer library: ${MFEM_CUDSS_THREADING_LIB}")
endif()
# Set the full name of the cuDSS communication library if MFEM use OpenMPI.
# The communication layer library (libcudss_commlayer_mpi.so) is located under the
# cuDSS library directory by default.
# The communication layer library is used pre-built communication layers for OpenMPI
# by default.
if (MFEM_USE_MPI)
find_file(
CUDSS_COMM_LIB
NAMES libcudss_commlayer_openmpi.so
PATHS ${CUDSS_LIBRARY_DIR}
NO_DEFAULT_PATH
)
if (NOT DEFINED MFEM_CUDSS_COMM_LIB AND CUDSS_COMM_LIB)
set(MFEM_CUDSS_COMM_LIB "${CUDSS_COMM_LIB}")
endif()
message(STATUS "CUDSS communication layer library: ${MFEM_CUDSS_COMM_LIB}")
endif()
+24
View File
@@ -157,4 +157,28 @@ constexpr real_t operator""_r(unsigned long long v)
#endif
#endif // MFEM_USE_MPI not defined
#ifndef MFEM_USE_CUDA
#ifdef MFEM_USE_CUDSS
#error Building with cuDSS (MFEM_USE_CUDSS=YES) requires CUDA (MFEM_USE_CUDA=YES)
#endif
#endif // MFEM_USE_CUDSS not defined
#ifdef NVTX_DBG_HPP
#include NVTX_DBG_HPP // IWYU pragma: keep
#else
#define db1(...)
#define dbg(...)
#define dbl(...)
#define dba(...)
#define dbc(...)
#define NVTX_MARK_FUNCTION
#define NVTX_MARK_BEGIN(...)
#define NVTX_MARK_INI(...)
#define NVTX_MARK_END(...)
#define NVTX_MARK(...)
#define NVTX_INI(...)
#define NVTX_END(...)
#define NVTX(...)
#endif
#endif // MFEM_CONFIG_HPP
+9
View File
@@ -108,6 +108,15 @@
// Enable MFEM functionality based on the STRUMPACK library.
// #define MFEM_USE_STRUMPACK
// Enable MFEM functionality based on the cuDSS library.
// #define MFEM_USE_CUDSS
// CUDSS communication layer library path
// #define MFEM_CUDSS_COMM_LIB "@MFEM_CUDSS_COMM_LIB@"
// CUDSS threading layer library path
// #define MFEM_CUDSS_THREADING_LIB "@MFEM_CUDSS_THREADING_LIB@"
// Enable MFEM features based on the Ginkgo library.
// #define MFEM_USE_GINKGO
+3
View File
@@ -36,6 +36,9 @@ MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
MFEM_USE_SUPERLU5 = @MFEM_USE_SUPERLU5@
MFEM_USE_MUMPS = @MFEM_USE_MUMPS@
MFEM_USE_STRUMPACK = @MFEM_USE_STRUMPACK@
MFEM_USE_CUDSS = @MFEM_USE_CUDSS@
MFEM_CUDSS_COMM_LIB = @MFEM_CUDSS_COMM_LIB@
MFEM_CUDSS_THREADING_LIB = @MFEM_CUDSS_THREADING_LIB@
MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
MFEM_USE_AMGX = @MFEM_USE_AMGX@
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
+1
View File
@@ -38,6 +38,7 @@ option(MFEM_USE_SUPERLU "Enable SuperLU_DIST usage" OFF)
option(MFEM_USE_SUPERLU5 "Use the old SuperLU_DIST 5.1 version" OFF)
option(MFEM_USE_MUMPS "Enable MUMPS usage" OFF)
option(MFEM_USE_STRUMPACK "Enable STRUMPACK usage" OFF)
option(MFEM_USE_CUDSS "Enable cuDSS usage" OFF)
option(MFEM_USE_GINKGO "Enable Ginkgo usage" OFF)
option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
option(MFEM_USE_MAGMA "Enable MAGMA usage" OFF)
+15 -1
View File
@@ -153,6 +153,7 @@ MFEM_USE_SUPERLU = NO
MFEM_USE_SUPERLU5 = NO
MFEM_USE_MUMPS = NO
MFEM_USE_STRUMPACK = NO
MFEM_USE_CUDSS = NO
MFEM_USE_GINKGO = NO
MFEM_USE_AMGX = NO
MFEM_USE_MAGMA = NO
@@ -368,6 +369,19 @@ STRUMPACK_OPT = -I$(STRUMPACK_DIR)/include $(SCOTCH_OPT)
STRUMPACK_LIB = -L$(STRUMPACK_DIR)/lib -lstrumpack $(MPI_FORTRAN_LIB)\
$(SCOTCH_LIB) $(SCALAPACK_LIB)
# CUDSS library configuration
CUDSS_DIR = @MFEM_DIR@/../cudss
CUDSS_INCLUDE_DIR = $(CUDSS_DIR)/include
CUDSS_LIBRARY_DIR = $(CUDSS_DIR)/lib
CUDSS_OPT = -I$(CUDSS_INCLUDE_DIR)
CUDSS_LIB = \
$(XLINKER)-rpath,$(CUDSS_LIBRARY_DIR) -L$(CUDSS_LIBRARY_DIR) -lcudss
# The cuDSS communication and threading libraries.
MFEM_CUDSS_COMM_LIB = $(abspath $(wildcard $(or $(CUDSS_COMM_LIB),\
$(subst @MFEM_DIR@,$(MFEM_DIR), $(CUDSS_LIBRARY_DIR)/libcudss_commlayer_openmpi.so))))
MFEM_CUDSS_THREADING_LIB = $(abspath $(wildcard $(or $(CUDSS_THREADING_LIB),\
$(subst @MFEM_DIR@,$(MFEM_DIR),$(CUDSS_LIBRARY_DIR)/libcudss_mtlayer_gomp.so))))
# Ginkgo library configuration
GINKGO_DIR = @MFEM_DIR@/../ginkgo/install
GINKGO_SEARCH_DIR = $(subst @MFEM_DIR@,$(MFEM_DIR),$(GINKGO_DIR))
@@ -621,7 +635,7 @@ PARELAG_LIB = -L$(PARELAG_DIR)/build/src -lParELAG
AXOM_DIR = @MFEM_DIR@/../axom
TRIBOL_DIR = @MFEM_DIR@/../tribol
TRIBOL_OPT = -I$(TRIBOL_DIR)/include -I$(AXOM_DIR)/include
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -ltribol_shared -lredecomp -L$(AXOM_DIR)/lib -laxom_mint\
-laxom_slam -laxom_slic -laxom_core
# Enzyme configuration
+1 -1
View File
@@ -215,7 +215,7 @@ if (MFEM_ENABLE_TESTING)
add_test(NAME ex1p_ceed_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:ex1p> "-no-vis" "-d ceed-cpu" "-pa" "-a"
$<TARGET_FILE:ex1p> "-no-vis" "-d" "ceed-cpu" "-pa" "-a"
${MPIEXEC_POSTFLAGS})
endif()
endif()
+1 -1
View File
@@ -64,7 +64,7 @@ PARALLEL_NAME := Parallel AMGX example
$(MFEM_LIB_FILE):
$(error The MFEM library is not build)
clean: clean-build
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
+3 -3
View File
@@ -64,12 +64,12 @@ ex1p-test-par: ex1p
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec $(SUBDIRS_CLEAN)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
@rm -f refined.mesh mesh.*
@rm -f sol.*
+23 -11
View File
@@ -224,17 +224,29 @@ int main(int argc, char *argv[])
// 11. Solve the linear system A X = B.
if (!pa)
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
GSSmoother M((SparseMatrix&)(*A));
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
#else
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
#ifdef MFEM_USE_CUDSS
if (Device::Allows(Backend::CUDA_MASK))
{
// Use cuDSS to solve the system.
CuDSSSolver cudss_solver;
cudss_solver.SetOperator(*A);
cudss_solver.Mult(B, X);
}
else
#endif
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
GSSmoother M((SparseMatrix&)(*A));
PCG(*A, M, B, X, 1, 200, 1e-12, 0.0);
#else
// If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(*A);
umf_solver.Mult(B, X);
#endif
}
}
else
{
@@ -273,7 +285,7 @@ int main(int argc, char *argv[])
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << mesh << x << flush;
+47 -22
View File
@@ -83,6 +83,9 @@ int main(int argc, char *argv[])
const char *device_config = "cpu";
bool visualization = true;
bool algebraic_ceed = false;
#ifdef MFEM_USE_CUDSS
bool cudss_solver = false;
#endif
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -102,6 +105,10 @@ int main(int argc, char *argv[])
args.AddOption(&algebraic_ceed, "-a", "--algebraic",
"-no-a", "--no-algebraic",
"Use algebraic Ceed solver");
#endif
#ifdef MFEM_USE_CUDSS
args.AddOption(&cudss_solver, "-cudss", "--cudss-solver", "-no-cudss",
"--no-cudss-solver", "Use the cuDSS Solver.");
#endif
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
@@ -248,33 +255,51 @@ int main(int argc, char *argv[])
// 13. Solve the linear system A X = B.
// * With full assembly, use the BoomerAMG preconditioner from hypre.
// * With partial assembly, use Jacobi smoothing, for now.
Solver *prec = NULL;
if (pa)
#ifdef MFEM_USE_CUDSS
if (!pa && (Device::Allows(Backend::CUDA_MASK) && cudss_solver))
{
if (UsesTensorBasis(fespace))
{
if (algebraic_ceed)
{
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
}
else
{
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
}
}
// Solve using a direct solver with cuDSS
CuDSSSolver cudss_solver(MPI_COMM_WORLD);
cudss_solver.SetMatrixSymType(
CuDSSSolver::SYMMETRIC_POSITIVE_DEFINITE);
cudss_solver.SetMatrixViewType(CuDSSSolver::UPPER);
cudss_solver.SetOperator(*A);
cudss_solver.Mult(B, X);
}
else
#endif
{
prec = new HypreBoomerAMG;
Solver *prec = NULL;
if (pa)
{
if (UsesTensorBasis(fespace))
{
if (algebraic_ceed)
{
prec = new ceed::AlgebraicSolver(a, ess_tdof_list);
}
else
{
prec = new OperatorJacobiSmoother(a, ess_tdof_list);
}
}
}
else
{
prec = new HypreBoomerAMG;
}
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
if (prec)
{
cg.SetPreconditioner(*prec);
}
cg.SetOperator(*A);
cg.Mult(B, X);
delete prec;
}
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
cg.SetMaxIter(2000);
cg.SetPrintLevel(1);
if (prec) { cg.SetPreconditioner(*prec); }
cg.SetOperator(*A);
cg.Mult(B, X);
delete prec;
// 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
+9
View File
@@ -95,6 +95,15 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
if (amg_elast && !static_cond && reorder_space)
{
if (myid == 0)
cerr << "\nThe AMG elasticity solver requires ordering byVDIM! "
<< "Ignoring the specified option -nodes/--by-nodes.\n"
<< endl;
reorder_space = false;
}
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
+1 -1
View File
@@ -76,4 +76,4 @@ clean-build:
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -f refined.mesh sol.gf
@rm -f refined.mesh sol.gf mesh.* sol.*
+5 -2
View File
@@ -71,6 +71,7 @@ endif
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
SUBDIRS_TEST_NOCLEAN = $(addsuffix /test-noclean,$(SUBDIRS))
SUBDIRS_CLEAN = $(addsuffix /clean,$(SUBDIRS))
SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
@@ -87,8 +88,9 @@ SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
all: $(EXAMPLES) $(SUBDIRS_ALL)
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN):
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) \
$(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) $(SUBDIRS_CLEAN):
$(MAKE) -C $(@D) $(@F)
$(SUBDIRS_TPRINT):
@$(MAKE) -C $(@D) $(@F)
@@ -107,6 +109,7 @@ endif
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
test: $(SUBDIRS_TEST)
test-noclean: $(SUBDIRS_TEST_NOCLEAN)
test-print: $(SUBDIRS_TPRINT)
# Testing: Parallel vs. serial runs
+6
View File
@@ -57,6 +57,8 @@ set(SRCS
integ/lininteg_domain_grad.cpp
integ/lininteg_domain_vectorfe.cpp
integ/nonlininteg_vecconvection_pa.cpp
integ/nonlininteg_vecconvection_pa_diag.cpp
integ/nonlininteg_vecconvection_pa_grad.cpp
integ/nonlininteg_vecconvection_mf.cpp
coefficient.cpp
complex_fem.cpp
@@ -171,8 +173,12 @@ set(SRCS
tmop_tools.cpp
tmop_amr.cpp
gslib.cpp
gslib/findptsedge_local_2.cpp
gslib/findptsedge_local_3.cpp
gslib/findptssurf_local_3.cpp
gslib/findpts_local_2.cpp
gslib/findpts_local_3.cpp
gslib/interpolate_local_1.cpp
gslib/interpolate_local_2.cpp
gslib/interpolate_local_3.cpp
transfer.cpp
+27 -1
View File
@@ -2651,14 +2651,22 @@ public:
void AddMultMF(const Vector &x, Vector &y) const override;
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
// PA AddMultPA kernels
using VectorMassAddMultPAType =
void(*)(const int, const int,
const Array<real_t>&, const Vector&,
const Vector&, Vector&, const int, const int);
MFEM_REGISTER_KERNELS(VectorMassAddMultPA,
VectorMassAddMultPAType,
(int, int, int));
// PA DiagonalPA kernels
using VectorMassAssembleDiagonalPAType =
void(*)(const int, const int, const int,
const real_t*, const real_t*, real_t*);
MFEM_REGISTER_KERNELS(VectorMassAssembleDiagonalPA,
VectorMassAssembleDiagonalPAType,
(int /*dim*/, int /*q1d*/));
};
@@ -3060,6 +3068,24 @@ public:
void AddMultPA(const Vector &x, Vector &y) const override;
void AddMultTransposePA(const Vector &x, Vector &y) const override;
using VectorDivergenceAddMultPAType =
void (*)(const int ne,
const Array<real_t> &b, const Array<real_t> &g, const Array<real_t> &bt,
const Vector &op, const Vector &x, Vector &y,
const int tr_d1d, const int te_d1d, const int q1d);
MFEM_REGISTER_KERNELS(VectorDivergenceAddMultPA,
VectorDivergenceAddMultPAType,
(int, int, int, int));
using VectorDivergenceAddMultTransposePAType =
void (*)(const int ne,
const Array<real_t> &bt, const Array<real_t> &gt, const Array<real_t> &b,
const Vector &q, const Vector &x, Vector &y,
const int tr_d1d, const int te_d1d, const int q1d);
MFEM_REGISTER_KERNELS(VectorDivergenceAddMultTransposePA,
VectorDivergenceAddMultTransposePAType,
(int, int, int, int));
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
const ElementTransformation &Trans);
+13 -26
View File
@@ -830,15 +830,9 @@ ParComplexGridFunction::ParComplexGridFunction(ParMesh *m, std::istream &input)
int vsize = pfes->GetVSize();
Vector::Load(input, 2*vsize);
real_t *data_ = const_cast<real_t*>(HostRead());
for (int i = 0; i < vsize; i++)
{
if (pfes->GetDofSign(i) < 0)
{
data_[i] = -data_[i];
data_[i+vsize] = -data_[i+vsize];
}
}
real_t *h_data = HostReadWrite();
pfes->ApplyDofSigns(h_data);
pfes->ApplyDofSigns(h_data + vsize);
// if the mesh is a legacy (v1.1) NC mesh, it has old vertex ordering
@@ -1051,15 +1045,14 @@ void ParComplexGridFunction::Save(std::ostream &os) const
os << '\n';
int vsize = pfes->GetVSize();
real_t *data_ = const_cast<real_t*>(HostRead());
for (int i = 0; i < vsize; i++)
{
if (pfes->GetDofSign(i) < 0)
{
data_[i] = -data_[i];
data_[i+vsize] = -data_[i+vsize];
}
}
// We use const_cast + HostRead (instead of HostReadWrite) because we only
// need to change the host data temporarily and this way we do not invalidate
// the data if it is on device. If we use HostReadWrite here, later calls to
// Read or ReadWrite will need to copy the data from host to device. With the
// approach used here, the host-to-device copy is avoided.
real_t *h_data = const_cast<real_t*>(HostRead());
pfes->ApplyDofSigns(h_data);
pfes->ApplyDofSigns(h_data + vsize);
if (pfes->GetOrdering() == Ordering::byNODES)
{
@@ -1070,14 +1063,8 @@ void ParComplexGridFunction::Save(std::ostream &os) const
Vector::Print(os, pfes->GetVDim());
}
for (int i = 0; i < vsize; i++)
{
if (pfes->GetDofSign(i) < 0)
{
data_[i] = -data_[i];
data_[i+vsize] = -data_[i+vsize];
}
}
pfes->ApplyDofSigns(h_data);
pfes->ApplyDofSigns(h_data + vsize);
os.flush();
}
+4
View File
@@ -114,6 +114,10 @@ void ConduitDataCollection::Save()
n_mesh["fields"][name]);
}
// TODO: in parallel, we need to call ParFiniteElementSpace::ApplyDofSigns
// for all ParGridFunction objects before and after saving, see
// ParGridFunction::Save.
// save mesh data
SaveMeshAndFields(myid,
n_mesh,
+1 -1
View File
@@ -57,7 +57,7 @@ void DGMassApply(const int e,
}
else if (DIM == 3)
{
SmemPAMassApply3D_Element<TD1D,TQ1D,ACCUM>(e, NE, B, pa_data, x, y);
SmemPAMassApply3D_Element<TD1D,TQ1D,NBZ,ACCUM>(e, NE, B, pa_data, x, y);
}
else
{
+3 -3
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,
+2390 -158
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
+3 -3
View File
@@ -91,15 +91,15 @@ void ElasticityAddMultPA(const int dim, const int nDofs,
void ElasticityAssembleDiagonalPA(const int dim, const int nDofs,
const CoefficientVector &lambda,
const CoefficientVector &mu, const GeometricFactors &geom,
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
const DofToQuad &maps, const IntegrationRule &ir, Vector &diag)
{
switch (dim)
{
case 2:
ElasticityAssembleDiagonalPA_<2>(nDofs, lambda, mu, geom, maps, QVec, diag);
ElasticityAssembleDiagonalPA_<2>(nDofs, lambda, mu, geom, maps, ir, diag);
break;
case 3:
ElasticityAssembleDiagonalPA_<3>(nDofs, lambda, mu, geom, maps, QVec, diag);
ElasticityAssembleDiagonalPA_<3>(nDofs, lambda, mu, geom, maps, ir, diag);
break;
default:
MFEM_ABORT("Only dimensions 2 and 3 supported.");
+39 -55
View File
@@ -38,7 +38,6 @@
#include "../../linalg/vector.hpp"
#include "../../linalg/tensor.hpp"
#include "../quadinterpolator.hpp"
#include "../bilininteg.hpp"
#include "../coefficient.hpp"
#include "../qfunction.hpp"
@@ -133,12 +132,12 @@ void ElasticityAssembleEA(const int dim, const int i_block, const int j_block,
/// @param[in] mu Quadrature function for second Lame param.
/// @param[in] geom Geometric factors corresponding to fespace.
/// @param[in] maps DofToQuad maps for one element (assume elements all same).
/// @param QVec Scratch Q-Vector. nQuad x dim x dim x dim x dim x numEls.
/// @param[in] ir Integration rule.
/// @param[out] diag diagonal of A. nDofs x dim x numEls.
void ElasticityAssembleDiagonalPA(const int dim, const int nDofs,
const CoefficientVector &lambda,
const CoefficientVector &mu, const GeometricFactors &geom,
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag);
const DofToQuad &maps, const IntegrationRule &ir, Vector &diag);
/// Templated implementation of ElasticityAddMultPA.
template<int dim, int i_block = -1, int j_block = -1>
@@ -280,77 +279,62 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
template<int dim>
void ElasticityAssembleDiagonalPA_(const int nDofs,
const CoefficientVector &lambda,
const CoefficientVector &mu, const GeometricFactors &geom,
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
const CoefficientVector &mu,
const GeometricFactors &geom,
const DofToQuad &maps,
const IntegrationRule &ir,
Vector &diag)
{
using future::tensor;
using future::make_tensor;
using future::det;
using future::inv;
using future::make_tensor;
using future::tensor;
// Assuming all elements are the same
const auto &ir = QVec.GetIntRule(0);
static constexpr int d = dim;
const int numPoints = ir.GetNPoints();
const int numEls = lambda.Size()/numPoints;
const int numEls = lambda.Size() / numPoints;
const auto lamDev = Reshape(lambda.Read(), numPoints, numEls);
const auto muDev = Reshape(mu.Read(), numPoints, numEls);
const auto J = Reshape(geom.J.Read(), numPoints, d, d, numEls);
auto Q = Reshape(QVec.ReadWrite(), numPoints, d,d, d, numEls);
const real_t *ipWeights = ir.GetWeights().Read();
mfem::forall_2D(numEls, numPoints,1, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(p, x,numPoints)
{
auto invJ = inv(make_tensor<d, d>(
[&](int i, int j) { return J(p, i, j, e); }));
const real_t w = ipWeights[p] /det(invJ);
for (int n = 0; n < d; n++)
{
for (int m = 0; m < d; m++)
{
for (int q = 0; q < d; q++)
{
// compute contraction of 4*sym(grad(u))sym(grad(v)) term.
// this contraction could be made slightly cheaper using Voigt
// notation, but repeated entries are summed for simplicity.
real_t contraction = 0.;
for (int a = 0; a < d; a++)
{
for (int b = 0; b < d; b++)
{
contraction += ((a == q)*invJ(m,b) + (b==q)*invJ(m,a))*((a == q)
*invJ(n, b) + (b==q)*invJ(n,a));
}
}
// lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
// contraction = 4*sym(grad(u))sym(grad(v))
Q(p,m,n,q,e) = w*(lamDev(p, e)*invJ(m,q)*invJ(n,q)
+ 0.5*muDev(p, e)*contraction);
}
}
}
}
});
// Reduce quadrature function to an E-Vector
const auto QRead = Reshape(QVec.Read(), numPoints, d, d, d, numEls);
auto diagDev = Reshape(diag.Write(), nDofs, d, numEls);
const auto G = Reshape(maps.G.Read(), numPoints, d, nDofs);
auto diagDev = Reshape(diag.Write(), nDofs, d, numEls);
mfem::forall_2D(numEls, d, nDofs, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(i, y, nDofs)
MFEM_FOREACH_THREAD_DIRECT(i, y, nDofs)
{
MFEM_FOREACH_THREAD(q, x, d)
MFEM_FOREACH_THREAD_DIRECT(q, x, d)
{
real_t sum = 0.;
for (int n = 0; n < d; n++)
real_t sum = 0.0;
for (int p = 0; p < numPoints; p++)
{
for (int m = 0; m < d; m++)
auto invJ = inv(make_tensor<d, d>([&](int r, int c) { return J(p, r, c, e); }));
const real_t w = ipWeights[p] / det(invJ);
for (int n = 0; n < d; n++)
{
for (int p = 0; p < numPoints; p++ )
for (int m = 0; m < d; m++)
{
sum += QRead(p,m,n,q,e)*G(p,m,i)*G(p,n,i);
// compute contraction of 4*sym(grad(u))sym(grad(v)) term.
// this contraction could be made slightly cheaper using Voigt
// notation, but repeated entries are summed for simplicity.
real_t contraction = 0.;
for (int a = 0; a < d; a++)
{
for (int b = 0; b < d; b++)
{
contraction += ((a == q) * invJ(m, b) + (b == q) * invJ(m, a)) *
((a == q) * invJ(n, b) + (b == q) * invJ(n, a));
}
}
// lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
// contraction = 4*sym(grad(u))sym(grad(v))
const real_t Q = w *
(lamDev(p, e) * invJ(m, q) * invJ(n, q) + 0.5 * muDev(p, e) * contraction);
sum += Q * G(p, m, i) * G(p, n, i);
}
}
}
+1 -3
View File
@@ -10,7 +10,6 @@
// CONTRIBUTING.md for details.
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "bilininteg_elasticity_kernels.hpp"
@@ -59,9 +58,8 @@ void ElasticityIntegrator::AssemblePA(const FiniteElementSpace &fes)
void ElasticityIntegrator::AssembleDiagonalPA(Vector &diag)
{
q_vec->SetVDim(vdim*vdim*vdim*vdim);
internal::ElasticityAssembleDiagonalPA(vdim, ndofs, *lambda_quad, *mu_quad,
*geom, *maps, *q_vec, diag);
*geom, *maps, *IntRule, diag);
}
void ElasticityIntegrator::AddMultPA(const Vector &x, Vector &y) const
+76 -48
View File
@@ -181,6 +181,12 @@ constexpr int NBZ(int D1D)
{
return ipow(2, D(D1D) >= 0 ? D(D1D) : 0);
}
constexpr int NBZ3D(int MDQ)
{
return MDQ > 0 ? std::min<int>(
(128 + MDQ * MDQ * MDQ - 1) / (MDQ * MDQ * MDQ), 64)
: 1;
}
}
// Shared memory PA Mass Diagonal 2D kernel
@@ -804,19 +810,23 @@ void PAMassApply3D_Element(const int e,
}
}
template<int T_D1D, int T_Q1D, bool ACCUMULATE = true>
MFEM_HOST_DEVICE inline
void SmemPAMassApply3D_Element(const int e,
const int NE,
const real_t *b_,
const real_t *d_,
const real_t *x_,
real_t *y_,
const int d1d = 0,
const int q1d = 0)
template <int T_D1D, int T_Q1D, int TBATCH, bool ACCUMULATE = true>
MFEM_HOST_DEVICE inline void
SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
const real_t *d_, const real_t *x_, real_t *y_,
int d1d = 0, int q1d = 0)
{
constexpr int D1D = T_D1D ? T_D1D : d1d;
constexpr int Q1D = T_Q1D ? T_Q1D : q1d;
static_assert(TBATCH > 0, "TBATCH must be positive");
#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
constexpr int tbatch = TBATCH;
const int tidz = MFEM_THREAD_ID(z);
#else
// host always batch size 1
constexpr int tbatch = 1;
constexpr int tidz = 0;
#endif
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
@@ -829,33 +839,37 @@ void SmemPAMassApply3D_Element(const int e,
MFEM_SHARED real_t sDQ[MQ1*MD1];
real_t (*B)[MD1] = (real_t (*)[MD1]) sDQ;
real_t (*Bt)[MQ1] = (real_t (*)[MQ1]) sDQ;
MFEM_SHARED real_t sm0[MDQ*MDQ*MDQ];
MFEM_SHARED real_t sm1[MDQ*MDQ*MDQ];
real_t (*X)[MD1][MD1] = (real_t (*)[MD1][MD1]) sm0;
real_t (*DDQ)[MD1][MQ1] = (real_t (*)[MD1][MQ1]) sm1;
real_t (*DQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) sm0;
real_t (*QQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) sm1;
real_t (*QQD)[MQ1][MD1] = (real_t (*)[MQ1][MD1]) sm0;
real_t (*QDD)[MD1][MD1] = (real_t (*)[MD1][MD1]) sm1;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_SHARED real_t sm0[tbatch][MDQ*MDQ*MDQ];
MFEM_SHARED real_t sm1[tbatch][MDQ*MDQ*MDQ];
real_t (*X)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+tidz);
real_t (*DDQ)[MD1][MQ1] = (real_t (*)[MD1][MQ1]) (sm1+tidz);
real_t (*DQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm0+tidz);
real_t (*QQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm1+tidz);
real_t (*QQD)[MQ1][MD1] = (real_t (*)[MQ1][MD1]) (sm0+tidz);
real_t (*QDD)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm1+tidz);
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dx, x, D1D)
{
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
X[dz][dy][dx] = x(dx,dy,dz,e);
X[dz][dy][dx] = x(dx, dy, dz, e);
}
}
MFEM_FOREACH_THREAD(dx,x,Q1D)
MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
}
if (tidz == 0)
{
MFEM_FOREACH_THREAD(dy, y, D1D)
{
B[dx][dy] = b(dx,dy);
MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
real_t u[D1D];
MFEM_UNROLL(MD1)
@@ -880,9 +894,9 @@ void SmemPAMassApply3D_Element(const int e,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
real_t u[D1D];
MFEM_UNROLL(MD1)
@@ -907,9 +921,9 @@ void SmemPAMassApply3D_Element(const int e,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
real_t u[Q1D];
MFEM_UNROLL(MQ1)
@@ -929,22 +943,22 @@ void SmemPAMassApply3D_Element(const int e,
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; qz++)
{
QQQ[qz][qy][qx] = u[qz] * d(qx,qy,qz,e);
QQQ[qz][qy][qx] = u[qz] * d(qx, qy, qz, e);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(di,y,D1D)
if (tidz == 0)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
MFEM_FOREACH_THREAD(di, y, D1D)
{
Bt[di][q] = b(q,di);
MFEM_FOREACH_THREAD(q, x, Q1D) { Bt[di][q] = b(q, di); }
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dx, x, D1D)
{
real_t u[Q1D];
MFEM_UNROLL(MQ1)
@@ -969,9 +983,9 @@ void SmemPAMassApply3D_Element(const int e,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dx, x, D1D)
{
real_t u[Q1D];
MFEM_UNROLL(MQ1)
@@ -996,9 +1010,9 @@ void SmemPAMassApply3D_Element(const int e,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dx, x, D1D)
{
real_t u[D1D];
MFEM_UNROLL(MD1)
@@ -1020,11 +1034,11 @@ void SmemPAMassApply3D_Element(const int e,
{
if (ACCUMULATE)
{
y(dx,dy,dz,e) += u[dz];
y(dx, dy, dz, e) += u[dz];
}
else
{
y(dx,dy,dz,e) = u[dz];
y(dx, dy, dz, e) = u[dz];
}
}
}
@@ -1115,8 +1129,8 @@ inline void PAMassApply3D(const int NE,
});
}
// Shared memory PA Mass Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
// Shared memory PA Mass Apply 3D kernel
template<int T_D1D = 0, int T_Q1D = 0, int TBATCH=1>
inline void SmemPAMassApply3D(const int NE,
const Array<real_t> &b_,
const Array<real_t> &bt_,
@@ -1126,6 +1140,9 @@ inline void SmemPAMassApply3D(const int NE,
const int d1d = 0,
const int q1d = 0)
{
static_assert(T_D1D > 0, "T_D1D must be positive");
static_assert(T_Q1D > 0, "T_Q1D must be positive");
static_assert(TBATCH > 0, "TBATCH must be positive");
MFEM_CONTRACT_VAR(bt_);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -1137,9 +1154,11 @@ inline void SmemPAMassApply3D(const int NE,
const auto d = d_.Read();
const auto x = x_.Read();
auto y = y_.ReadWrite();
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
mfem::forall_2D_batch<T_Q1D * T_Q1D * TBATCH>(NE, Q1D, Q1D, TBATCH,
[=] MFEM_HOST_DEVICE(int e)
{
internal::SmemPAMassApply3D_Element<T_D1D,T_Q1D>(e, NE, b, d, x, y, d1d, q1d);
internal::SmemPAMassApply3D_Element<T_D1D, T_Q1D, TBATCH>(e, NE, b, d, x,
y, d1d, q1d);
});
}
@@ -1394,7 +1413,16 @@ ApplyKernelType MassIntegrator::ApplyPAKernels::Kernel()
{
if constexpr (DIM == 1) { return internal::PAMassApply1D; }
else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<T_D1D,T_Q1D>; }
else if constexpr (DIM == 3) { return internal::SmemPAMassApply3D<T_D1D, T_Q1D>; }
else if constexpr (DIM == 3)
{
constexpr int MDQ = T_D1D >= T_Q1D ? T_D1D : T_Q1D;
// max 64 threads in z limit in cuda and hip
if constexpr (MDQ > 0)
{
return internal::SmemPAMassApply3D<T_D1D, T_Q1D,
internal::mass::NBZ3D(MDQ)>;
}
}
MFEM_ABORT("");
}
File diff suppressed because it is too large Load Diff
+32 -149
View File
@@ -205,157 +205,40 @@ void VectorMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
template <const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal2D(const int NE,
const Array<real_t> &b,
const Vector &pa_data, Vector &diag,
const int d1d = 0, const int q1d = 0)
{
constexpr int VDIM = 2;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = Reshape(b.Read(), Q1D, D1D);
const auto D = Reshape(pa_data.Read(), Q1D, Q1D, NE);
auto Y = Reshape(diag.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE(int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
real_t temp[max_Q1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dy = 0; dy < D1D; ++dy)
{
temp[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp[qx][dy] += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
}
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
real_t temp1 = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp1 += B(qx, dx) * B(qx, dx) * temp[qx][dy];
}
Y(dx, dy, 0, e) = temp1;
Y(dx, dy, 1, e) = temp1;
}
}
});
}
template <const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal3D(const int NE,
const Array<real_t> &B_,
const Vector &pa_data, Vector &diag,
const int d1d = 0, const int q1d = 0)
{
constexpr int VDIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = Reshape(B_.Read(), Q1D, D1D);
MFEM_VERIFY(pa_data.Size() == Q1D * Q1D * Q1D * NE, "pa_data size error");
const auto D = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, NE);
auto Y = Reshape(diag.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE(int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
// the following variables are evaluated at compile time
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
real_t temp[max_Q1D][max_Q1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int dz = 0; dz < D1D; ++dz)
{
temp[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
temp[qx][qy][dz] +=
B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
}
}
}
}
real_t temp2[max_Q1D][max_D1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
temp2[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp2[qx][dy][dz] +=
B(qy, dy) * B(qy, dy) * temp[qx][qy][dz];
}
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
real_t temp3 = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp3 += B(qx, dx) * B(qx, dx) * temp2[qx][dy][dz];
}
Y(dx, dy, dz, 0, e) = temp3;
Y(dx, dy, dz, 1, e) = temp3;
Y(dx, dy, dz, 2, e) = temp3;
}
}
}
});
}
static void PAVectorMassAssembleDiagonal(const int dim, const int D1D,
const int Q1D, const int NE,
const Array<real_t> &B,
const Vector &pa_data,
Vector &diag)
{
if (dim == 2)
{
return PAVectorMassAssembleDiagonal2D(NE, B, pa_data, diag, D1D, Q1D);
}
else if (dim == 3)
{
return PAVectorMassAssembleDiagonal3D(NE, B, pa_data, diag, D1D, Q1D);
}
MFEM_ABORT("Dimension not implemented.");
}
void VectorMassIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed()) { ceedOp->GetDiagonal(diag); }
else
{
MFEM_VERIFY(coeff_vdim == 1, "coeff_vdim != 1");
MFEM_VERIFY(!VQ && !MQ, "VQ and MQ not supported");
PAVectorMassAssembleDiagonal(dim, dofs1D, quad1D, ne, maps->B, pa_data, diag);
}
if (DeviceCanUseCeed()) { return ceedOp->GetDiagonal(diag); }
MFEM_VERIFY(coeff_vdim == 1, "coeff_vdim != 1");
MFEM_VERIFY(!VQ && !MQ, "VQ and MQ not supported");
// Add the VectorMassAssembleDiagonalPA specializations
static const auto vector_mass_assemble_diagonal_kernel_specializations =
( // 2D
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 2>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 3>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 4>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 5>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 6>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 7>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 8>::Add(),
// 3D
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 2>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 3>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 4>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 5>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 6>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 7>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 8>::Add(),
true);
MFEM_CONTRACT_VAR(vector_mass_assemble_diagonal_kernel_specializations);
VectorMassAssembleDiagonalPA::Run(dim, quad1D, // templated arguments
ne, dofs1D, quad1D,
maps->B.Read(),
pa_data.Read(),
diag.ReadWrite());
}
} // namespace mfem
+169 -2
View File
@@ -176,8 +176,145 @@ void SmemPAVectorMassApply3D(const int NE,
});
}
template <int T_Q1D = 0, int T_MDQ = 16>
static void SmemPAVectorMassAssembleDiagonal2D(const int ne,
const int d1d,
const int q1d,
const real_t *b_r,
const real_t *d_r,
real_t *y_rw)
{
constexpr int VDIM = 2;
const int D1D = d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(Q1D <= T_MDQ && D1D <= Q1D, "");
const auto B = Reshape(b_r, Q1D, D1D);
const auto D = Reshape(d_r, Q1D, Q1D, ne);
auto Y = Reshape(y_rw, D1D, D1D, VDIM, ne);
mfem::forall_2D<T_Q1D*T_Q1D>(
ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
MFEM_SHARED real_t sm[MQ1][MQ1];
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
}
sm[qx][dy] = u;
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += B(qx, dx) * B(qx, dx) * sm[qx][dy];
}
Y(dx, dy, 0, e) = u;
Y(dx, dy, 1, e) = u;
}
}
});
}
template <int T_Q1D = 0, int T_MDQ = 12>
static void SmemPAVectorMassAssembleDiagonal3D(const int ne,
const int d1d,
const int q1d,
const real_t *b_r,
const real_t *d_r,
real_t *y_rw)
{
constexpr int VDIM = 3;
const int D1D = d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(Q1D <= T_MDQ && D1D <= Q1D, "");
const auto B = Reshape(b_r, Q1D, D1D);
const auto D = Reshape(d_r, Q1D, Q1D, Q1D, ne);
auto Y = Reshape(y_rw, D1D, D1D, D1D, VDIM, ne);
mfem::forall_3D<T_Q1D*T_Q1D*T_Q1D>(
ne, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
MFEM_SHARED real_t sm[2][MQ1][MQ1][MQ1];
MFEM_FOREACH_THREAD_DIRECT(dz, z, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
real_t u = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u += B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
}
sm[0][dz][qy][qx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dz, z, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += B(qy, dy) * B(qy, dy) * sm[0][dz][qy][qx];
}
sm[1][dz][dy][qx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dz, z, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += B(qx, dx) * B(qx, dx) * sm[1][dz][dy][qx];
}
Y(dx, dy, dz, 0, e) = u;
Y(dx, dy, dz, 1, e) = u;
Y(dx, dy, dz, 2, e) = u;
}
}
}
});
}
} // namespace internal
// AddMultPA kernels
template<int DIM, int T_D1D, int T_Q1D>
VectorMassIntegrator::VectorMassAddMultPAType
VectorMassIntegrator::VectorMassAddMultPA::Kernel()
@@ -190,11 +327,11 @@ VectorMassIntegrator::VectorMassAddMultPA::Kernel()
{
return internal::SmemPAVectorMassApply3D<T_D1D, T_Q1D>;
}
MFEM_ABORT("Unsupported kernel");
else { MFEM_ABORT("Unsupported kernel"); }
}
inline VectorMassIntegrator::VectorMassAddMultPAType
VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int d1d, int q1d)
VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int, int)
{
if (dim == 2)
{
@@ -207,6 +344,36 @@ VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int d1d, int q1d)
else { MFEM_ABORT("Unsupported kernel"); }
}
// DiagonalPA kernels
template<int DIM, int T_Q1D>
VectorMassIntegrator::VectorMassAssembleDiagonalPAType
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Kernel()
{
if constexpr (DIM == 2)
{
return internal::SmemPAVectorMassAssembleDiagonal2D<T_Q1D>;
}
else if constexpr (DIM == 3)
{
return internal::SmemPAVectorMassAssembleDiagonal3D<T_Q1D>;
}
else { MFEM_ABORT("Unsupported kernel"); }
}
inline VectorMassIntegrator::VectorMassAssembleDiagonalPAType
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Fallback(int dim, int)
{
if (dim == 2)
{
return internal::SmemPAVectorMassAssembleDiagonal2D;
}
else if (dim == 3)
{
return internal::SmemPAVectorMassAssembleDiagonal3D;
}
else { MFEM_ABORT("Unsupported kernel"); }
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,351 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include <utility>
#include "../kernels.hpp"
#include "../nonlininteg.hpp"
#include "../../general/forall.hpp"
namespace mfem
{
template<int T_D1D = 0, int T_Q1D = 0, int T_MDQ = 16>
static void SmemPAConvectionNLGradDiagonal2D(const int NE,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *u,
real_t *de,
const int d1d,
const int q1d)
{
static constexpr int VDIM = 2, DIM = 2;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, NE);
const auto U = Reshape(u, D1D, D1D, VDIM, NE);
auto D = Reshape(de, D1D, D1D, VDIM, NE);
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : T_MDQ;
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
MFEM_SHARED real_t sM[3][MQ1][MQ1], sQ[3][MQ1][MQ1];
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::v_regs2d_t<VDIM, MQ1> r0, r1;
kernels::internal::vd_regs2d_t<VDIM, DIM, MQ1> g0, g1;
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
kernels::internal::LoadDofs2d(e, D1D, U, r0);
kernels::internal::Eval2d(D1D, Q1D, sM[0], sB, r0, r1);
kernels::internal::LoadDofs2d(e, D1D, U, g0);
kernels::internal::Grad2d(D1D, Q1D, sM[0], sB, sG, g0, g1);
for (int v = 0; v < VDIM; ++v)
{
future::tensor<real_t, VDIM> e_v = {};
e_v[v] = real_t(1);
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
const future::tensor<real_t, VDIM> u_val =
{
r1[0][qy][qx], r1[1][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> Q_adj =
{
{ { A(0, 0, qx, qy, e), A(1, 0, qx, qy, e) },
{ A(0, 1, qx, qy, e), A(1, 1, qx, qy, e) }
}
};
const future::tensor<real_t, VDIM, DIM> grad_U =
{
{ { g1[0][0][qy][qx], g1[1][0][qy][qx] },
{ g1[0][1][qy][qx], g1[1][1][qy][qx] }
}
};
const auto one = Q_adj * u_val;
const auto two = transpose(grad_U) * (Q_adj * e_v);
sQ[0][qx][qy] = one[0];
sQ[1][qx][qy] = one[1];
sQ[2][qx][qy] = two[v];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
real_t s[3] = {};
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t By = sB[dy][qy], Gy = sG[dy][qy];
s[0] += By * By * sQ[0][qx][qy];
s[1] += Gy * By * sQ[1][qx][qy];
s[2] += By * By * sQ[2][qx][qy];
}
sM[0][qx][dy] = s[0];
sM[1][qx][dy] = s[1];
sM[2][qx][dy] = s[2];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t d = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Bx = sB[dx][qx], Gx = sG[dx][qx];
d += Gx * Bx * sM[0][qx][dy] +
Bx * Bx * sM[1][qx][dy] +
Bx * Bx * sM[2][qx][dy];
}
D(dx, dy, v, e) += d;
}
}
MFEM_SYNC_THREAD;
}
});
}
template<int T_D1D = 0, int T_Q1D = 0, int T_MDQ = 16>
static void SmemPAConvectionNLGradDiagonal3D(const int NE,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *u,
real_t *de,
const int d1d,
const int q1d)
{
static constexpr int VDIM = 3, DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, Q1D, NE);
const auto U = Reshape(u, D1D, D1D, D1D, VDIM, NE);
auto D = Reshape(de, D1D, D1D, D1D, VDIM, NE);
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : T_MDQ;
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
MFEM_SHARED real_t sM[4][MQ1][MQ1], sQ[4][MQ1][MQ1];
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::v_regs3d_t<VDIM, MQ1> r0, r1;
kernels::internal::vd_regs3d_t<VDIM, DIM, MQ1> g0, g1;
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
kernels::internal::LoadDofs3d(e, D1D, U, r0);
kernels::internal::Eval3d(D1D, Q1D, sM[0], sB, r0, r1);
kernels::internal::LoadDofs3d(e, D1D, U, g0);
kernels::internal::Grad3d(D1D, Q1D, sM[0], sB, sG, g0, g1);
for (int v = 0; v < VDIM; ++v)
{
future::tensor<real_t, VDIM> e_v = {};
e_v[v] = real_t(1);
for (int dz = 0; dz < D1D; ++dz)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
real_t s[4] = {};
for (int qz = 0; qz < Q1D; ++qz)
{
const future::tensor<real_t, VDIM> u_val =
{
r1[0][qz][qy][qx], r1[1][qz][qy][qx], r1[2][qz][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> Q_adj = {{
{A(0,0,qx,qy,qz,e), A(1,0,qx,qy,qz,e), A(2,0,qx,qy,qz,e)},
{A(0,1,qx,qy,qz,e), A(1,1,qx,qy,qz,e), A(2,1,qx,qy,qz,e)},
{A(0,2,qx,qy,qz,e), A(1,2,qx,qy,qz,e), A(2,2,qx,qy,qz,e)}
}
};
const future::tensor<real_t, VDIM, DIM> grad_U = {{
{g1[0][0][qz][qy][qx], g1[1][0][qz][qy][qx], g1[2][0][qz][qy][qx]},
{g1[0][1][qz][qy][qx], g1[1][1][qz][qy][qx], g1[2][1][qz][qy][qx]},
{g1[0][2][qz][qy][qx], g1[1][2][qz][qy][qx], g1[2][2][qz][qy][qx]}
}
};
const auto one = Q_adj * u_val;
const auto two = transpose(grad_U) * (Q_adj * e_v);
const real_t Bz = sB[dz][qz], Gz = sG[dz][qz];
s[0] += one[0] * Bz * Bz;
s[1] += one[1] * Bz * Bz;
s[2] += one[2] * Bz * Gz;
s[3] += two[v] * Bz * Bz;
}
sQ[0][qx][qy] = s[0];
sQ[1][qx][qy] = s[1];
sQ[2][qx][qy] = s[2];
sQ[3][qx][qy] = s[3];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
real_t s[4] = {};
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t By = sB[dy][qy], Gy = sG[dy][qy];
s[0] += By * By * sQ[0][qx][qy];
s[1] += Gy * By * sQ[1][qx][qy];
s[2] += By * By * sQ[2][qx][qy];
s[3] += By * By * sQ[3][qx][qy];
}
sM[0][dy][qx] = s[0];
sM[1][dy][qx] = s[1];
sM[2][dy][qx] = s[2];
sM[3][dy][qx] = s[3];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t d = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Bx = sB[dx][qx], Gx = sG[dx][qx];
d += Gx * Bx * sM[0][dy][qx];
d += Bx * Bx * sM[1][dy][qx];
d += Bx * Bx * sM[2][dy][qx];
d += Bx * Bx * sM[3][dy][qx];
}
D(dx, dy, dz, v, e) += d;
}
}
MFEM_SYNC_THREAD;
}
}
});
}
void VectorConvectionNLFIntegrator::AssembleGradDiagonalPA(Vector &de) const
{
if (dim == 2)
{
if (static auto ini = false; !std::exchange(ini, true))
{
VectorConvectionNLFGradDiagPA2D::Specialization<2, 2>::Add();
VectorConvectionNLFGradDiagPA2D::Specialization<2, 3>::Add();
VectorConvectionNLFGradDiagPA2D::Specialization<3, 4>::Add();
VectorConvectionNLFGradDiagPA2D::Specialization<3, 5>::Add();
VectorConvectionNLFGradDiagPA2D::Specialization<4, 5>::Add();
VectorConvectionNLFGradDiagPA2D::Specialization<4, 6>::Add();
VectorConvectionNLFGradDiagPA2D::Specialization<5, 7>::Add();
VectorConvectionNLFGradDiagPA2D::Specialization<5, 8>::Add();
}
VectorConvectionNLFGradDiagPA2D::Run(d1d, q1d, ne,
maps->B.Read(),
maps->G.Read(),
pa_adj.Read(),
pa_u.Read(),
de.ReadWrite(),
d1d, q1d);
}
else if (dim == 3)
{
if (static auto ini = false; !std::exchange(ini, true))
{
VectorConvectionNLFGradDiagPA3D::Specialization<2, 3>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<2, 4>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<2, 5>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<3, 4>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<3, 5>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<3, 6>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<4, 6>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<4, 7>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<4, 8>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<5, 7>::Add();
VectorConvectionNLFGradDiagPA3D::Specialization<5, 8>::Add();
}
VectorConvectionNLFGradDiagPA3D::Run(d1d, q1d, ne,
maps->B.Read(),
maps->G.Read(),
pa_adj.Read(),
pa_u.Read(),
de.ReadWrite(),
d1d, q1d);
}
else
{
MFEM_ABORT("Unsupported dimension");
}
}
/// \cond DO_NOT_DOCUMENT
template<int T_D1D, int T_Q1D>
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPAType
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPA2D::Kernel()
{
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
return SmemPAConvectionNLGradDiagonal2D<T_D1D, T_Q1D>;
}
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPAType
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPA2D::Fallback
(int d1d, int q1d)
{
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
MFEM_VERIFY(d1d <= 16, "d1d > 16 is not supported");
MFEM_VERIFY(q1d <= 16, "q1d > 16 is not supported");
return SmemPAConvectionNLGradDiagonal2D<>;
}
template<int T_D1D, int T_Q1D>
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPAType
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPA3D::Kernel()
{
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
return SmemPAConvectionNLGradDiagonal3D<T_D1D, T_Q1D>;
}
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPAType
VectorConvectionNLFIntegrator::VectorConvectionNLFGradDiagPA3D::Fallback
(int d1d, int q1d)
{
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
MFEM_VERIFY(d1d <= 16, "d1d > 16 is not supported");
MFEM_VERIFY(q1d <= 16, "q1d > 16 is not supported");
return SmemPAConvectionNLGradDiagonal3D<>;
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
@@ -0,0 +1,319 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include <utility>
#include "../kernels.hpp"
#include "../nonlininteg.hpp"
#include "../../general/forall.hpp"
namespace mfem
{
void VectorConvectionNLFIntegrator::AssembleGradPA(
const Vector &u, const FiniteElementSpace &fes)
{
this->pa_u = u;
AssemblePA(fes);
if (static auto done = false; !std::exchange(done, true))
{
// 2D
VectorConvectionNLFAddMultGradPA2D::Specialization<2, 2>::Add();
VectorConvectionNLFAddMultGradPA2D::Specialization<2, 3>::Add();
VectorConvectionNLFAddMultGradPA2D::Specialization<3, 4>::Add();
VectorConvectionNLFAddMultGradPA2D::Specialization<3, 5>::Add();
VectorConvectionNLFAddMultGradPA2D::Specialization<4, 5>::Add();
VectorConvectionNLFAddMultGradPA2D::Specialization<4, 6>::Add();
VectorConvectionNLFAddMultGradPA2D::Specialization<5, 7>::Add();
VectorConvectionNLFAddMultGradPA2D::Specialization<5, 8>::Add();
VectorConvectionNLFAddMultGradPA2D::Specialization<6, 8>::Add();
// 3D
VectorConvectionNLFAddMultGradPA3D::Specialization<2, 3>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<2, 4>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<2, 5>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<3, 4>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<3, 5>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<3, 6>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<4, 5>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<4, 6>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<5, 6>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<4, 7>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<4, 8>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<5, 7>::Add();
VectorConvectionNLFAddMultGradPA3D::Specialization<5, 8>::Add();
}
}
template<int T_D1D = 0, int T_Q1D = 0, int T_MDQ = 16>
static void SmemPAConvectionNLGradApply2D(const int ne,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *u,
const real_t *du,
real_t *y,
const int d1d,
const int q1d)
{
static constexpr int VDIM = 2, DIM = 2;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, ne);
const auto U = Reshape(u, D1D, D1D, VDIM, ne);
const auto dU = Reshape(du, D1D, D1D, VDIM, ne);
auto Y = Reshape(y, D1D, D1D, VDIM, ne);
mfem::forall_2D<T_Q1D * T_Q1D>(ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : T_MDQ;
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
MFEM_SHARED real_t smem[MQ1][MQ1];
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::vd_regs2d_t<VDIM, DIM, MQ1> g0, g1, g2;
kernels::internal::v_regs2d_t<DIM, MQ1> r0, r1, r2;
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
kernels::internal::LoadDofs2d(e, D1D, dU, g0);
kernels::internal::Grad2d(D1D, Q1D, smem, sB, sG, g0, g1); // δu gradient
kernels::internal::LoadDofs2d(e, D1D, U, r0);
kernels::internal::Eval2d(D1D, Q1D, smem, sB, r0, r2); // u value
kernels::internal::LoadDofs2d(e, D1D, dU, r0);
kernels::internal::Eval2d(D1D, Q1D, smem, sB, r0, r1); // δu value
kernels::internal::LoadDofs2d(e, D1D, U, g0);
kernels::internal::Grad2d(D1D, Q1D, smem, sB, sG, g0, g2); // u gradient
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
// First part of the Jacobian: u·∇δu
const future::tensor<real_t, DIM> u_val =
{
r2[0][qy][qx], r2[1][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> Q_adj =
{
{ { A(0, 0, qx, qy, e), A(1, 0, qx, qy, e) },
{ A(0, 1, qx, qy, e), A(1, 1, qx, qy, e) }
}
};
const future::tensor<real_t, VDIM, DIM> grad_dU =
{
{ { g1[0][0][qy][qx], g1[1][0][qy][qx] },
{ g1[0][1][qy][qx], g1[1][1][qy][qx] }
}
};
const auto one = transpose(grad_dU) * (Q_adj * u_val);
// Second part of the Jacobian: δu·∇u
const future::tensor<real_t, DIM> du_val =
{
r1[0][qy][qx], r1[1][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> grad_U =
{
{ { g2[0][0][qy][qx], g2[1][0][qy][qx] },
{ g2[0][1][qy][qx], g2[1][1][qy][qx] }
}
};
const auto two = transpose(grad_U) * (Q_adj * du_val);
// u⋅∇δu + δu⋅∇u
r0[0][qy][qx] = one[0] + two[0];
r0[1][qy][qx] = one[1] + two[1];
}
}
MFEM_SYNC_THREAD;
kernels::internal::EvalTranspose2d(D1D, Q1D, smem, sB, r0, r1);
kernels::internal::WriteDofs2d(e, D1D, r1, Y);
});
}
template<int T_D1D = 0, int T_Q1D = 0, int T_MDQ = 16>
static void SmemPAConvectionNLGradApply3D(const int ne,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *u,
const real_t *du,
real_t *y,
const int d1d,
const int q1d)
{
static constexpr int VDIM = 3, DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, Q1D, ne);
const auto U = Reshape(u, D1D, D1D, D1D, VDIM, ne);
const auto dU = Reshape(du, D1D, D1D, D1D, VDIM, ne);
auto Y = Reshape(y, D1D, D1D, D1D, VDIM, ne);
mfem::forall_2D<T_Q1D * T_Q1D>(ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : T_MDQ;
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
MFEM_SHARED real_t smem[MQ1][MQ1];
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::v_regs3d_t<VDIM, MQ1> r0, r1, r2;
kernels::internal::vd_regs3d_t<VDIM, DIM, MQ1> g0, g1, g2;
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
kernels::internal::LoadDofs3d(e, D1D, dU, g0);
kernels::internal::Grad3d(D1D, Q1D, smem, sB, sG, g0, g1); // δu gradient
kernels::internal::LoadDofs3d(e, D1D, U, r0);
kernels::internal::Eval3d(D1D, Q1D, smem, sB, r0, r2); // u value
kernels::internal::LoadDofs3d(e, D1D, dU, r0);
kernels::internal::Eval3d(D1D, Q1D, smem, sB, r0, r1); // δu value
kernels::internal::LoadDofs3d(e, D1D, U, g0);
kernels::internal::Grad3d(D1D, Q1D, smem, sB, sG, g0, g2); // u gradient
for (int qz = 0; qz < Q1D; qz++)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
// First part of the Jacobian: u·∇δu
const future::tensor<real_t, DIM> u_val =
{
r2[0][qz][qy][qx],
r2[1][qz][qy][qx],
r2[2][qz][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> Q_adj = {{
{A(0,0,qx,qy,qz,e), A(1,0,qx,qy,qz,e), A(2,0,qx,qy,qz,e)},
{A(0,1,qx,qy,qz,e), A(1,1,qx,qy,qz,e), A(2,1,qx,qy,qz,e)},
{A(0,2,qx,qy,qz,e), A(1,2,qx,qy,qz,e), A(2,2,qx,qy,qz,e)}
}
};
const future::tensor<real_t, DIM, DIM> grad_dU = {{
{g1[0][0][qz][qy][qx], g1[1][0][qz][qy][qx], g1[2][0][qz][qy][qx]},
{g1[0][1][qz][qy][qx], g1[1][1][qz][qy][qx], g1[2][1][qz][qy][qx]},
{g1[0][2][qz][qy][qx], g1[1][2][qz][qy][qx], g1[2][2][qz][qy][qx]}
}
};
const auto one = transpose(grad_dU) * (Q_adj * u_val);
// Second part of the Jacobian: δu·∇u
const future::tensor<real_t, DIM> du_val =
{
r1[0][qz][qy][qx], r1[1][qz][qy][qx], r1[2][qz][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> grad_U = {{
{g2[0][0][qz][qy][qx], g2[1][0][qz][qy][qx], g2[2][0][qz][qy][qx]},
{g2[0][1][qz][qy][qx], g2[1][1][qz][qy][qx], g2[2][1][qz][qy][qx]},
{g2[0][2][qz][qy][qx], g2[1][2][qz][qy][qx], g2[2][2][qz][qy][qx]}
}
};
const auto two = transpose(grad_U) * (Q_adj * du_val);
// u⋅∇δu + δu⋅∇u
r0[0][qz][qy][qx] = one[0] + two[0];
r0[1][qz][qy][qx] = one[1] + two[1];
r0[2][qz][qy][qx] = one[2] + two[2];
}
}
}
MFEM_SYNC_THREAD;
kernels::internal::EvalTranspose3d(D1D, Q1D, smem, sB, r0, r1);
kernels::internal::WriteDofs3d(e, D1D, r1, Y);
});
}
void VectorConvectionNLFIntegrator::AddMultGradPA(const Vector &x,
Vector &y) const
{
if (dim == 2)
{
VectorConvectionNLFAddMultGradPA2D::Run(d1d, q1d, ne,
maps->B.Read(),
maps->G.Read(),
pa_adj.Read(),
pa_u.Read(),
x.Read(),
y.ReadWrite(),
d1d, q1d);
}
else if (dim == 3)
{
VectorConvectionNLFAddMultGradPA3D::Run(d1d, q1d, ne,
maps->B.Read(),
maps->G.Read(),
pa_adj.Read(),
pa_u.Read(),
x.Read(),
y.ReadWrite(),
d1d, q1d);
}
else
{
MFEM_ABORT("Unsupported dimension");
}
}
/// \cond DO_NOT_DOCUMENT
template<int T_D1D, int T_Q1D>
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPAType
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPA2D::Kernel()
{
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
return SmemPAConvectionNLGradApply2D<T_D1D, T_Q1D>;
}
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPAType
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPA2D::Fallback
(int d1d, int q1d)
{
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
MFEM_VERIFY(d1d <= 16, "d1d > 16 is not supported");
MFEM_VERIFY(q1d <= 16, "q1d > 16 is not supported");
return SmemPAConvectionNLGradApply2D<>;
}
template<int T_D1D, int T_Q1D>
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPAType
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPA3D::Kernel()
{
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
return SmemPAConvectionNLGradApply3D<T_D1D, T_Q1D>;
}
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPAType
VectorConvectionNLFIntegrator::VectorConvectionNLFAddMultGradPA3D::Fallback
(int d1d, int q1d)
{
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
MFEM_VERIFY(d1d <= 16, "d1d > 16 is not supported");
MFEM_VERIFY(q1d <= 16, "q1d > 16 is not supported");
return SmemPAConvectionNLGradApply3D<>;
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
+9 -2
View File
@@ -83,7 +83,7 @@ constexpr int SetMaxOf(int n) { return NextMultipleOf<4>(n); }
#endif // CUDA/HIP && DEVICE_COMPILE
/// Load 2D matrix into shared memory
template <int MQ1>
template <int MQ1, bool TRANSPOSE = false>
inline MFEM_HOST_DEVICE void LoadMatrix(const int d1d, const int q1d,
const real_t *M, real_t (*N)[MQ1])
{
@@ -91,7 +91,14 @@ inline MFEM_HOST_DEVICE void LoadMatrix(const int d1d, const int q1d,
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
N[dy][qx] = M[dy * q1d + qx];
if constexpr (TRANSPOSE)
{
N[dy][qx] = M[qx * d1d + dy];
}
else
{
N[dy][qx] = M[dy * q1d + qx];
}
}
}
MFEM_SYNC_THREAD;
+4 -4
View File
@@ -284,12 +284,12 @@ GeometricMultigrid::GeometricMultigrid(
ownedProlongations.SetSize(nlevels - 1);
ownedProlongations = have_ess_bdr;
if (have_ess_bdr)
essentialTrueDofs.SetSize(nlevels);
for (int level = 0; level < nlevels; ++level)
{
essentialTrueDofs.SetSize(nlevels);
for (int level = 0; level < nlevels; ++level)
essentialTrueDofs[level] = new Array<int>;
if (have_ess_bdr)
{
essentialTrueDofs[level] = new Array<int>;
fespaces.GetFESpaceAtLevel(level).GetEssentialTrueDofs(
ess_bdr, *essentialTrueDofs[level]);
}
+1 -2
View File
@@ -187,8 +187,7 @@ public:
/// mesh boundary element attributes that define the essential DOFs.
///
/// If @a ess_bdr is empty, or all its entries are 0, then no essential
/// boundary conditions are imposed and the protected array essentialTrueDofs
/// remains empty.
/// boundary conditions are imposed.
GeometricMultigrid(const FiniteElementSpaceHierarchy& fespaces_,
const Array<int> &ess_bdr);
+44 -4
View File
@@ -18,6 +18,7 @@
#include "fespace.hpp"
#include "ceed/interface/operator.hpp"
#include "integrator.hpp"
#include "kernel_dispatch.hpp"
namespace mfem
{
@@ -384,10 +385,10 @@ private:
DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
Vector shape;
// PA extension
Vector pa_data;
int dim, ne, nq, d1d, q1d;
Vector pa_adj, pa_u;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq;
public:
VectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }
@@ -411,12 +412,51 @@ public:
void AssemblePA(const FiniteElementSpace &fes) override;
void AssembleMF(const FiniteElementSpace &fes) override;
void AssembleGradPA(const Vector &x, const FiniteElementSpace &fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AddMultMF(const Vector &x, Vector &y) const override;
using VectorConvectionNLFAddMultPAType =
void(*)(const int ne, const real_t *B, const real_t *G, const real_t *A,
const real_t *x, real_t *y,
const int d1d, const int q1d);
MFEM_REGISTER_KERNELS(VectorConvectionNLFAddMultPA,
VectorConvectionNLFAddMultPAType,
(int, int, int));
void AddMultGradPA(const Vector &x, Vector &y) const override;
using VectorConvectionNLFAddMultGradPAType =
void(*)(const int ne, const real_t *B, const real_t *G, const real_t *A,
const real_t *u, const real_t *x, real_t *y,
const int d1d, const int q1d);
MFEM_REGISTER_KERNELS(VectorConvectionNLFAddMultGradPA2D,
VectorConvectionNLFAddMultGradPAType,
(int, int));
MFEM_REGISTER_KERNELS(VectorConvectionNLFAddMultGradPA3D,
VectorConvectionNLFAddMultGradPAType,
(int, int));
void AssembleGradDiagonalPA(Vector &) const override;
using VectorConvectionNLFGradDiagPAType =
void (*)(const int ne, const real_t *B, const real_t *G, const real_t *A,
const real_t *u, real_t *y,
const int d1d, const int q1d);
MFEM_REGISTER_KERNELS(VectorConvectionNLFGradDiagPA2D,
VectorConvectionNLFGradDiagPAType,
(int, int));
MFEM_REGISTER_KERNELS(VectorConvectionNLFGradDiagPA3D,
VectorConvectionNLFGradDiagPAType,
(int, int));
void AssembleMF(const FiniteElementSpace &fes) override;
void AddMultMF(const Vector &x, Vector &y) const override;
protected:
const IntegrationRule* GetDefaultIntegrationRule(
+31 -3
View File
@@ -349,6 +349,7 @@ void ParFiniteElementSpace::GetGroupComm(
}
}
bool have_sign_flips = false;
if (g_ldof_sign)
{
g_ldof_sign->SetSize(GetNDofs());
@@ -428,6 +429,7 @@ void ParFiniteElementSpace::GetGroupComm(
if (g_ldof_sign)
{
(*g_ldof_sign)[dofs[l]] = -1;
have_sign_flips = true;
}
}
else
@@ -466,6 +468,7 @@ void ParFiniteElementSpace::GetGroupComm(
if (g_ldof_sign)
{
(*g_ldof_sign)[dofs[l]] = -1;
have_sign_flips = true;
}
}
else
@@ -504,6 +507,7 @@ void ParFiniteElementSpace::GetGroupComm(
if (g_ldof_sign)
{
(*g_ldof_sign)[dofs[l]] = -1;
have_sign_flips = true;
}
}
else
@@ -527,12 +531,18 @@ void ParFiniteElementSpace::GetGroupComm(
group_ldof.GetI()[gr+1] = group_ldof_counter;
}
if (g_ldof_sign && have_sign_flips == false)
{
g_ldof_sign->DeleteAll();
}
gc.Finalize();
}
void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs) const
{
MFEM_ASSERT(Conforming(), "wrong code path");
if (!HaveDofSigns()) { return; }
for (int i = 0; i < dofs.Size(); i++)
{
@@ -559,6 +569,24 @@ void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof) const
ApplyLDofSigns(all_dofs);
}
void ParFiniteElementSpace::ApplyDofSigns(real_t *h_data) const
{
if (!HaveDofSigns()) { return; }
const bool byvdim = (ordering == Ordering::byVDIM);
for (int i = 0; i < ndofs; i++)
{
if (ldof_sign[i] < 0)
{
for (int d = 0; d < vdim; d++)
{
const int idx = byvdim ? d+vdim*i : i+ndofs*d;
h_data[idx] = -h_data[idx];
}
}
}
}
void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs,
DofTransformation &doftrans) const
{
@@ -1193,15 +1221,15 @@ void ParFiniteElementSpace::GetEssentialTrueDofsVar(const Array<int>
MFEM_VERIFY(IsVariableOrder() && R,
"GetEssentialTrueDofsVar is only for variable-order spaces");
true_ess_dofs.SetSize(R->Height(), Device::GetDeviceMemoryType());
true_ess_dofs.SetSize(R->Height());
true_ess_dofs.HostWrite();
true_ess_dofs = 0;
const int ntdofs = tdof2ldof.Size();
MFEM_VERIFY(vdim * ntdofs == R->NumRows() &&
vdim * ntdofs == true_ess_dofs.Size(), "");
MFEM_VERIFY(ldof_ltdof.Size() == ndofs && ess_dofs.Size() == vdim * ndofs, "");
true_ess_dofs = 0;
const bool bynodes = (ordering == Ordering::byNODES);
const int vdim_factor = bynodes ? 1 : vdim;
const int num_true_dofs = R->NumRows() / vdim;
+14 -2
View File
@@ -340,8 +340,20 @@ public:
inline ParMesh *GetParMesh() const { return pmesh; }
int GetDofSign(int i)
{ return NURBSext || Nonconforming() ? 1 : ldof_sign[VDofToDof(i)]; }
/** @brief Return true if the parallel FE space has DOFs with signs opposite
of the DOFs in the respective serial FE space. */
bool HaveDofSigns() const { return ldof_sign.Size() != 0; }
/** @brief Apply the DOF signs to the given host data @a h_data which must be
of size GetVSize() if HaveDofSigns() is true. If HaveDofSigns() is false,
this method is no-op and returns immediately. */
void ApplyDofSigns(real_t *h_data) const;
/** @brief Return -1 if the given (vector) DOF @a i has a sign opposite of
the DOF in the respecive serial FE space. Otherwise, return 1. */
int GetDofSign(int i) const
{ return !HaveDofSigns() ? 1 : ldof_sign[VDofToDof(i)]; }
HYPRE_BigInt *GetDofOffsets() const { return dof_offsets; }
HYPRE_BigInt *GetTrueDofOffsets() const { return tdof_offsets; }
HYPRE_BigInt GlobalVSize() const
+18 -9
View File
@@ -80,6 +80,8 @@ ParGridFunction::ParGridFunction(ParMesh *pmesh, std::istream &input)
fes->GetOrdering());
delete fes;
fes = pfes;
pfes->ApplyDofSigns(HostReadWrite());
}
void ParGridFunction::Update()
@@ -1082,18 +1084,17 @@ real_t ParGridFunction::ComputeDGFaceJumpError(Coefficient *exsol,
void ParGridFunction::Save(std::ostream &os) const
{
real_t *data_ = const_cast<real_t*>(HostRead());
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
// We use const_cast + HostRead (instead of HostReadWrite) because we only
// need to change the host data temporarily and this way we do not invalidate
// the data if it is on device. If we use HostReadWrite here, later calls to
// Read or ReadWrite will need to copy the data from host to device. With the
// approach used here, the host-to-device copy is avoided.
real_t *h_data = const_cast<real_t*>(HostRead());
pfes->ApplyDofSigns(h_data);
GridFunction::Save(os);
for (int i = 0; i < size; i++)
{
if (pfes->GetDofSign(i) < 0) { data_[i] = -data_[i]; }
}
pfes->ApplyDofSigns(h_data);
}
void ParGridFunction::Save(const char *fname, int precision) const
@@ -1264,7 +1265,13 @@ void ParGridFunction::SaveAsOne(std::ostream &os) const
int *nfdofs = new int[NRanks];
int *nrdofs = new int[NRanks];
// We use const_cast + HostRead (instead of HostReadWrite) because we only
// need to change the host data temporarily and this way we do not invalidate
// the data if it is on device. If we use HostReadWrite here, later calls to
// Read or ReadWrite will need to copy the data from host to device. With the
// approach used here, the host-to-device copy is avoided.
real_t * h_data = const_cast<real_t *>(this->HostRead());
pfes->ApplyDofSigns(h_data); // temporarily flip the dof signs
values[0] = h_data;
nv[0] = pfes -> GetVSize();
@@ -1371,6 +1378,8 @@ void ParGridFunction::SaveAsOne(std::ostream &os) const
MPI_Send(h_data, nv[0], MPITypeMap<real_t>::mpi_type, 0, 460, MyComm);
}
pfes->ApplyDofSigns(h_data); // restore the original h_data
delete [] values;
delete [] nv;
delete [] nvdofs;
+66 -10
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>;
+10
View File
@@ -37,6 +37,16 @@
// removed in a future release).
#define CUB_IGNORE_DEPRECATED_CPP_DIALECT
#define THRUST_IGNORE_DEPRECATED_CPP_DIALECT
// MFEM only supports using RAJA/CAMP backends in default stream mode because
// memory calls are performed outside of the RAJA ecosystem
#ifndef CAMP_USE_PLATFORM_DEFAULT_STREAM
#define CAMP_USE_PLATFORM_DEFAULT_STREAM 1
#else
#if !CAMP_USE_PLATFORM_DEFAULT_STREAM
#error "MFEM only supports RAJA/CAMP with the default platform stream."
#endif
#endif
#include "RAJA/RAJA.hpp"
#if defined(RAJA_ENABLE_CUDA) && !defined(MFEM_USE_CUDA)
#error When RAJA is built with CUDA, MFEM_USE_CUDA=YES is required
+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
+156 -12
View File
@@ -277,6 +277,9 @@ void OmpWrap3D(const int Nx, const int Ny, const int Nz, HBODY &&h_body)
/// RAJA Cuda and Hip backends
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA) && defined(__CUDACC__)
template <int LB>
using cuda_launch_bounds_policy =
RAJA::LaunchPolicy<RAJA::cuda_launch_t<true, LB>>;
using cuda_launch_policy =
RAJA::LaunchPolicy<RAJA::cuda_launch_t<true>>;
using cuda_teams_x =
@@ -288,6 +291,9 @@ using cuda_threads_z =
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP) && defined(__HIP__)
using hip_launch_policy =
RAJA::LaunchPolicy<RAJA::hip_launch_t<true>>;
template <int LB>
using hip_launch_bounds_policy =
RAJA::LaunchPolicy<RAJA::hip_launch_t<true, LB>>;
using hip_teams_x =
RAJA::LoopPolicy<RAJA::hip_block_x_direct>;
using hip_threads_z =
@@ -337,6 +343,33 @@ void RajaCuWrap2D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(cudaGetLastError());
}
template <int LB, typename DBODY>
void RajaCuWrap2DLaunchBounds(const int N, DBODY &&d_body, const int X,
const int Y, const int BZ)
{
MFEM_VERIFY(BZ>0, "");
const int G = (N+BZ-1)/BZ;
using namespace RAJA;
using RAJA::RangeSegment;
launch<cuda_launch_bounds_policy<LB> >
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<cuda_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
{
loop<cuda_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
{
const int k = n*BZ + tz;
if (k >= N) { return; }
d_body(k);
});
});
});
MFEM_GPU_CHECK(cudaGetLastError());
}
template <typename DBODY>
void RajaCuWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
@@ -357,11 +390,28 @@ void RajaCuWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(cudaGetLastError());
}
template <int Dim>
template <int LB, typename DBODY>
void RajaCuWrap3DLaunchBounds(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
const int GRID = G == 0 ? N : G;
using namespace RAJA;
using RAJA::RangeSegment;
launch<cuda_launch_bounds_policy<LB> >
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body);
});
MFEM_GPU_CHECK(cudaGetLastError());
}
template <int Dim, int MAX_THREADS_PER_BLOCK>
struct RajaCuWrap;
template <>
struct RajaCuWrap<1>
template <int MAX_THREADS_PER_BLOCK>
struct RajaCuWrap<1, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
@@ -372,7 +422,7 @@ struct RajaCuWrap<1>
};
template <>
struct RajaCuWrap<2>
struct RajaCuWrap<2, 0>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
@@ -382,8 +432,19 @@ struct RajaCuWrap<2>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct RajaCuWrap<2, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaCuWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
}
};
template <>
struct RajaCuWrap<3>
struct RajaCuWrap<3, 0>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
@@ -393,6 +454,17 @@ struct RajaCuWrap<3>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct RajaCuWrap<3, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaCuWrap3DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z, G);
}
};
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP) && defined(__HIP__)
@@ -438,6 +510,33 @@ void RajaHipWrap2D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(hipGetLastError());
}
template <int LB, typename DBODY>
void RajaHipWrap2DLaunchBounds(const int N, DBODY &&d_body, const int X,
const int Y, const int BZ)
{
MFEM_VERIFY(BZ>0, "");
const int G = (N+BZ-1)/BZ;
using namespace RAJA;
using RAJA::RangeSegment;
launch<hip_launch_bounds_policy<LB> >
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
{
loop<hip_threads_z>(ctx, RangeSegment(0, BZ), [&] (const int tz)
{
const int k = n*BZ + tz;
if (k >= N) { return; }
d_body(k);
});
});
});
MFEM_GPU_CHECK(hipGetLastError());
}
template <typename DBODY>
void RajaHipWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
@@ -458,11 +557,28 @@ void RajaHipWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(hipGetLastError());
}
template <int Dim>
template <int LB, typename DBODY>
void RajaHipWrap3DLaunchBounds(const int N, DBODY &&d_body, const int X,
const int Y, const int Z, const int G)
{
const int GRID = G == 0 ? N : G;
using namespace RAJA;
using RAJA::RangeSegment;
launch<hip_launch_bounds_policy<LB> >
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body);
});
MFEM_GPU_CHECK(hipGetLastError());
}
template <int Dim, int MAX_THREADS_PER_BLOCK>
struct RajaHipWrap;
template <>
struct RajaHipWrap<1>
template <int MAX_THREADS_PER_BLOCK>
struct RajaHipWrap<1, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
@@ -473,7 +589,7 @@ struct RajaHipWrap<1>
};
template <>
struct RajaHipWrap<2>
struct RajaHipWrap<2, 0>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
@@ -483,8 +599,19 @@ struct RajaHipWrap<2>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct RajaHipWrap<2, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaHipWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
}
};
template <>
struct RajaHipWrap<3>
struct RajaHipWrap<3, 0>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
@@ -494,6 +621,17 @@ struct RajaHipWrap<3>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct RajaHipWrap<3, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaHipWrap3DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z, G);
}
};
#endif
/// RAJA OpenMP backend
@@ -913,7 +1051,7 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::RAJA_CUDA is allowed, use it
if (Device::Allows(Backend::RAJA_CUDA))
{
return RajaCuWrap<DIM>::run(N, d_body, X, Y, Z, G);
return RajaCuWrap<DIM, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
}
#endif
@@ -921,7 +1059,7 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::RAJA_HIP is allowed, use it
if (Device::Allows(Backend::RAJA_HIP))
{
return RajaHipWrap<DIM>::run(N, d_body, X, Y, Z, G);
return RajaHipWrap<DIM, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
}
#endif
@@ -1090,6 +1228,12 @@ inline void forall_2D_batch(int N, int X, int Y, int BZ, lambda &&body)
ForallWrap<2>(true, N, body, X, Y, BZ);
}
template<int MAX_THREADS_PER_BLOCK, typename lambda>
inline void forall_2D_batch(int N, int X, int Y, int BZ, lambda &&body)
{
ForallWrap<2, MAX_THREADS_PER_BLOCK>(true, N, body, X, Y, BZ);
}
template<typename lambda>
inline void forall_3D(int N, int X, int Y, int Z, lambda &&body)
{
+6
View File
@@ -148,6 +148,12 @@ if (MFEM_USE_MKL_PARDISO)
list(APPEND HDRS pardiso.hpp)
endif()
# cudss solver
if (MFEM_USE_CUDSS)
list(APPEND SRCS cudss.cpp)
list(APPEND HDRS cudss.hpp)
endif()
convert_filenames_to_full_paths(SRCS)
convert_filenames_to_full_paths(HDRS)
+409
View File
@@ -0,0 +1,409 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "cudss.hpp"
#include "../general/communication.hpp"
#include <string>
#ifdef MFEM_USE_CUDSS
#ifdef MFEM_USE_SINGLE
#define CUDA_REAL_T CUDA_R_32F
#else
#define CUDA_REAL_T CUDA_R_64F
#endif
// Define a cuDSS error check macro, MFEM_CUDSS_CHECK(x), where x returns/is of
// type 'cudssStatus_t'. This macro evaluates 'x' and raises an error if the
// result is not CUDSS_STATUS_SUCCESS.
#define MFEM_CUDSS_CHECK(x) \
do { \
cudssStatus_t mfem_err_internal_var_name = (x); \
if (mfem_err_internal_var_name != CUDSS_STATUS_SUCCESS) { \
::mfem::mfem_cudss_error(mfem_err_internal_var_name, #x, \
_MFEM_FUNC_NAME, __FILE__, __LINE__); \
} \
} while (0)
namespace mfem
{
// Function used by the macro MFEM_CUDSS_CHECK.
void mfem_cudss_error(cudssStatus_t status, const char *expr, const char *func,
const char *file, int line)
{
mfem::err << "\n\nCUDSS error: (" << expr << ") failed with error:\n --> "
<< "CUDSS call ended unsuccessfully"
<< " [code: " << static_cast<int>(status) << ']'
<< "\n ... in function: " << func << "\n ... in file: " << file
<< ':' << line << '\n';
mfem_error();
}
CuDSSSolver::CuDSSSolver() { InitCuDSS(); }
#ifdef MFEM_USE_MPI
CuDSSSolver::CuDSSSolver(MPI_Comm comm_) : mpi_comm(comm_)
{
InitCuDSS();
// NOTE: Set the communication layer to NULL so that cuDSS picks it
// from the environment variable "CUDSS_COMM_LIB"
const char* comm_lib = GetEnv("CUDSS_COMM_LIB");
#ifdef MFEM_CUDSS_COMM_LIB
if (comm_lib == nullptr)
{
comm_lib = MFEM_CUDSS_COMM_LIB;
}
#endif
MFEM_CUDSS_CHECK(cudssSetCommLayer(handle, comm_lib));
MFEM_CUDSS_CHECK(cudssDataSet(handle, solverData, CUDSS_DATA_COMM,
&mpi_comm, sizeof(MPI_Comm *)));
}
#endif // MFEM_USE_MPI
CuDSSSolver::~CuDSSSolver()
{
// Destroy the system Matrix, RHS vector and solution vector
if (Ac)
{
MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
MFEM_CUDSS_CHECK(cudssMatrixDestroy(xc));
MFEM_CUDSS_CHECK(cudssMatrixDestroy(yc));
}
// Destroy the cuDSS handle, solver config and solver data
MFEM_CUDSS_CHECK(cudssDataDestroy(handle, solverData));
MFEM_CUDSS_CHECK(cudssConfigDestroy(solverConfig));
MFEM_CUDSS_CHECK(cudssDestroy(handle));
handle = nullptr;
if (csr_offsets_d != NULL)
{
CuMemFree(csr_offsets_d);
}
if (csr_columns_d != NULL)
{
CuMemFree(csr_columns_d);
}
if (csr_values_d != NULL)
{
CuMemFree(csr_values_d);
}
}
void CuDSSSolver::InitCuDSS()
{
// Create the cuDSS handle
MFEM_CUDSS_CHECK(cudssCreate(&handle));
#ifdef MFEM_USE_OPENMP
// NOTE: Set the threading layer library name to NULL so that cuDSS picks
// it from the environment variable "CUDSS_THREADING_LIB"
const char* threading_lib = GetEnv("CUDSS_THREADING_LIB");
#ifdef MFEM_CUDSS_THREADING_LIB
if (threading_lib == nullptr)
{
threading_lib = MFEM_CUDSS_THREADING_LIB;
}
#endif
MFEM_CUDSS_CHECK(cudssSetThreadingLayer(handle, threading_lib));
#endif // MFEM_USE_OPENMP
// Create the solver configuration and data objects
MFEM_CUDSS_CHECK(cudssConfigCreate(&solverConfig));
MFEM_CUDSS_CHECK(cudssDataCreate(handle, &solverData));
}
void CuDSSSolver::SetMatrixSymType(MatType mtype_)
{
switch (mtype_)
{
case MatType::SYMMETRIC_INDEFINITE:
mat_type = CUDSS_MTYPE_SYMMETRIC;
break;
case MatType::SYMMETRIC_POSITIVE_DEFINITE:
mat_type = CUDSS_MTYPE_SPD;
break;
default:
mat_type = CUDSS_MTYPE_GENERAL;
break;
}
}
void CuDSSSolver::SetMatrixViewType(MatViewType mvtype_)
{
// If the MatType is NONSYMMETRIC, the matrix view type must be FULL.
if (mat_type == CUDSS_MTYPE_GENERAL)
{
mview = CUDSS_MVIEW_FULL;
return;
}
// If the matrix is symmetric, the following view type will be optional.
switch (mvtype_)
{
case MatViewType::LOWER:
mview = CUDSS_MVIEW_LOWER;
break;
case MatViewType::UPPER:
mview = CUDSS_MVIEW_UPPER;
break;
default:
mview = CUDSS_MVIEW_FULL;
break;
}
}
void CuDSSSolver::SetReorderingReuse(bool reuse)
{
MFEM_VERIFY(Ac == nullptr,
"Set reordering reuse before setting the operator!");
reorder_reuse = reuse;
}
#ifdef MFEM_USE_MPI
void CuDSSSolver::SetMatrix(const HypreParMatrix &op)
{
bool cuDSSObjectInitialized = (Ac != nullptr);
hypre_ParCSRMatrix *parcsr_op = op;
op.HypreRead();
hypre_CSRMatrix *csr_op = hypre_MergeDiagAndOffd(parcsr_op);
op.HypreRead();
#if MFEM_HYPRE_VERSION >= 21600
hypre_CSRMatrixBigJtoJ(csr_op);
#endif
// Parameters of the Operator
n_loc = height; // Equal to the csr_op->num_rows
n_global = internal::to_int(parcsr_op->global_num_rows);
row_start = parcsr_op->first_row_index;
row_end = row_start + n_loc - 1;
MFEM_VERIFY(!cuDSSObjectInitialized || !reorder_reuse ||
(reorder_reuse && (nnz == csr_op->num_nonzeros)),
"Inconsistent new matrix pattern!");
nnz = csr_op->num_nonzeros;
SetMatrixCuDSS(csr_op->i, csr_op->j, csr_op->data);
hypre_CSRMatrixDestroy(csr_op);
}
#endif // MFEM_USE_MPI
void CuDSSSolver::SetMatrix(const SparseMatrix &op)
{
bool cuDSSObjectInitialized = (Ac != nullptr);
// Parameters of the Operator
MFEM_VERIFY(!cuDSSObjectInitialized || !reorder_reuse ||
(reorder_reuse && (nnz == op.NumNonZeroElems())),
"Inconsistent new matrix pattern!");
SparseMatrix *A = const_cast<SparseMatrix *>(&op);
nnz = A->NumNonZeroElems();
n_global = height; // Equal to the height in serial
n_loc = height; // Equal to the height in serial
int *csr_offsets = const_cast<int *>(A->ReadI());
int *csr_columns = const_cast<int *>(A->ReadJ());
real_t *csr_values = const_cast<real_t *>(A->ReadData());
SetMatrixCuDSS(csr_offsets, csr_columns, csr_values);
}
void CuDSSSolver::SetMatrixCuDSS(int *csr_offsets, int *csr_columns,
real_t *csr_values)
{
bool cuDSSObjectInitialized = (Ac != nullptr);
// Initial the cudssMatrix objects
if (!cuDSSObjectInitialized)
{
// Set the cudssMatrix object of csr operator
Ac = std::make_unique<cudssMatrix_t>();
// Create empty RHS and solution vectors
SetNumRHS(1);
// Allocate device memory for csr values
CuMemAlloc(&csr_values_d, nnz * sizeof(real_t));
}
CuMemcpyDtoD(csr_values_d, csr_values, nnz * sizeof(real_t));
// New cuDSS CSR matrix object and analysis or reuse the one from a previous
// matrix
if (!cuDSSObjectInitialized || !reorder_reuse)
{
if (reorder_reuse) // !cuDSSObjectInitialized && reorder_reuse
{
// NOTE: For CuDSS solver to reuse the reordering (skipping analysis
// phase), it needs to access the I and J arrays of the **initial**
// matrix. Therefore, we need to copy and keep I and J in device memory.
CuMemAlloc(&csr_offsets_d, (n_loc + 1) * sizeof(int));
CuMemAlloc(&csr_columns_d, nnz * sizeof(int));
CuMemcpyDtoD(csr_offsets_d, csr_offsets, (n_loc + 1) * sizeof(int));
CuMemcpyDtoD(csr_columns_d, csr_columns, nnz * sizeof(int));
MFEM_CUDSS_CHECK(
cudssMatrixCreateCsr(
Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
csr_columns_d, csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
CUDSS_BASE_ZERO));
}
else // !reorder_reuse
{
if (cuDSSObjectInitialized)
{
MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
}
MFEM_CUDSS_CHECK(
cudssMatrixCreateCsr(
Ac.get(), n_global, n_global, nnz, csr_offsets, NULL, csr_columns,
csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
CUDSS_BASE_ZERO));
}
#ifdef MFEM_USE_MPI
if (Mpi::IsInitialized())
{
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(*Ac, row_start, row_end));
}
#endif
// Analysis
MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_ANALYSIS, solverConfig,
solverData, *Ac, yc, xc));
}
else // cuDSSObjectInitialized && reorder_reuse
{
// NOTE: When reusing analysis result, we only update the Data array,
// without changing the I and J arrays.
MFEM_CUDSS_CHECK(cudssMatrixSetValues(*Ac, csr_values_d));
}
// Factorization
MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_FACTORIZATION, solverConfig,
solverData, *Ac, yc, xc));
}
void CuDSSSolver::SetOperator(const Operator &op)
{
bool cuDSSObjectInitialized = (Ac != nullptr);
MFEM_VERIFY(
!cuDSSObjectInitialized || (height == op.Height() && width == op.Width()),
"Inconsistent new matrix size!");
height = op.Height();
width = op.Width();
if (const SparseMatrix *A = dynamic_cast<const SparseMatrix *>(&op))
{
SetMatrix(*A);
}
#ifdef MFEM_USE_MPI
else if (const HypreParMatrix *A =
dynamic_cast<const HypreParMatrix *>(&op))
{
SetMatrix(*A);
}
#endif // MFEM_USE_MPI
else
{
MFEM_ABORT("Unsupported Operator Type \n");
}
}
void CuDSSSolver::SetNumRHS(int nrhs_) const
{
if (nrhs != nrhs_)
{
if (nrhs > 0)
{
// Destroy the previous RHS vector and solution vector
MFEM_CUDSS_CHECK(cudssMatrixDestroy(xc));
MFEM_CUDSS_CHECK(cudssMatrixDestroy(yc));
}
// Create empty RHS and solution vectors
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&xc, n_global, nrhs_, n_global, NULL,
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&yc, n_global, nrhs_, n_global, NULL,
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
#ifdef MFEM_USE_MPI
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(xc, row_start, row_end));
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(yc, row_start, row_end));
#endif // MFEM_USE_MPI
}
nrhs = nrhs_;
}
void CuDSSSolver::Mult(const Vector &x, Vector &y) const
{
Array<const Vector *> X(1);
Array<Vector *> Y(1);
X[0] = &x;
Y[0] = &y;
ArrayMult(X, Y);
}
void CuDSSSolver::ArrayMult(const Array<const Vector *> &X,
Array<Vector *> &Y) const
{
SetNumRHS(X.Size());
Vector RHS, SOL;
if (nrhs == 1)
{
RHS.MakeRef(*(const_cast<Vector *>(X[0])), 0, X[0]->Size());
SOL.MakeRef(*Y[0], 0, Y[0]->Size());
}
else
{
// NOTE: RHS must have **global** num_rows and nrhs columns
RHS.SetSize(nrhs * n_global, *X[0]);
for (int i = 0; i < nrhs; i++)
{
Vector s(RHS, i * n_global, n_loc);
s = *X[i];
}
// NOTE: SOL must have **global** num_rows and nrhs columns
SOL.SetSize(nrhs * n_global, *Y[0]);
}
MFEM_CUDSS_CHECK(cudssMatrixSetValues(xc, const_cast<real_t *>(RHS.Read())));
MFEM_CUDSS_CHECK(cudssMatrixSetValues(yc, SOL.Write()));
// Solve
MFEM_CUDSS_CHECK(cudssExecute(handle, CUDSS_PHASE_SOLVE, solverConfig,
solverData, *Ac, yc, xc));
if (nrhs == 1)
{
SOL.SyncAliasMemory(*Y[0]);
}
if (nrhs > 1)
{
// Get solution for each right-hand side
for (int i = 0; i < nrhs; i++)
{
Vector s(SOL, i * n_global, n_loc);
*Y[i] = s;
}
}
}
} // namespace mfem
#endif // MFEM_USE_CUDSS
+224
View File
@@ -0,0 +1,224 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_CUDSS
#define MFEM_CUDSS
#include "../config/config.hpp"
#ifdef MFEM_USE_CUDSS
#include "cudss.h"
#include <memory>
#ifdef MFEM_USE_MPI
#include <mpi.h>
#include "hypre.hpp"
#else
#include "operator.hpp"
#include "sparsemat.hpp"
#endif
namespace mfem
{
/**
* @brief cuDSS: A high-performance CUDA Library for Direct Sparse Solvers
*
* Interface for the distributed cuDSS solver
*/
class CuDSSSolver : public Solver
{
public:
/// Specify the type of matrix we are applying the solver to
enum MatType
{
/// CUDSS_MTYPE_GENERAL: General matrix [default].
NONSYMMETRIC = 0,
/// CUDSS_MTYPE_SYMMETRIC: Real symmetric matrix.
SYMMETRIC_INDEFINITE = 1,
/// CUDSS_MTYPE_SPD: Symmetric positive-definite matrix.
SYMMETRIC_POSITIVE_DEFINITE = 2,
};
/// Specify the view type of matrix we are applying the solver to
enum MatViewType
{
/// CUDSS_MVIEW_FULL: Full matrix [default]
FULL = 0,
/// CUDSS_MVIEW_LOWER: Lower-triangular matrix (including the diagonal).
LOWER = 1,
/// CUDSS_MVIEW_UPPER: Upper-triangular matrix (including the diagonal).
UPPER = 2,
};
/**
* @brief Constructor.
*/
CuDSSSolver();
#ifdef MFEM_USE_MPI
/**
* @brief Constructor with MPI_Comm parameter.
*/
CuDSSSolver(MPI_Comm comm);
#endif
// Note: CuDSSSolver disables the move copy constructor and move assignment
// operator
CuDSSSolver(CuDSSSolver &&) = delete;
CuDSSSolver &operator=(CuDSSSolver &&) = delete;
/**
* @brief Set the matrix type
*
* Supported matrix types:
* CuDSSSolver::NONSYMMETRIC,
* CuDSSSolver::SYMMETRIC_INDEFINITE,
* and CuDSSSolver::SYMMETRIC_POSITIVE_DEFINITE
*
* @param mtype_ Matrix type
*
* @note This method has to be called before SetOperator
*/
void SetMatrixSymType(MatType mtype_);
/**
* @brief Set the matrix view type
*
* Supported matrix types:
* CuDSSSolver::FULL,
* CuDSSSolver::LOWER,
* and CuDSSSolver::UPPER
*
* @param mvtype Matrix view type
*
* @note This method has to be called before SetOperator
*/
void SetMatrixViewType(MatViewType mvtype);
/**
* @brief Set the flag controlling reuse of the symbolic factorization
* for multiple operators
*
* @param reuse Flag to reuse symbolic factorization
*
* @note This method has to be called before repeated calls to SetOperator
*/
void SetReorderingReuse(bool reuse);
void SetOperator(const Operator &op) override;
/**
* @brief Solve $ y = Op^{-1} x $
*
* @param x RHS vector
* @param y Solution vector
*/
void Mult(const Vector &x, Vector &y) const override;
/**
* @brief Solve $ Y_i = Op^{-1} X_i $
*
* @param X Array of RHS vectors
* @param Y Array of Solution vectors
*/
void ArrayMult(const Array<const Vector *> &X,
Array<Vector *> &Y) const override;
~CuDSSSolver();
private:
#ifdef MFEM_USE_MPI
// MPI_Comm
MPI_Comm mpi_comm = MPI_COMM_NULL;
int row_start = 0; // the first row index in CSR matrix operator
int row_end = 0; // the end row index in CSR matrix operator
#endif
// Parameter controlling whether or not to reuse the symbolic factorization
// for multiple calls to SetOperator
bool reorder_reuse = false;
// Parameter controlling the matrix type
cudssMatrixType_t mat_type = CUDSS_MTYPE_GENERAL;
int n_global = 0; // global number of rows
int n_loc = 0; // the number of the rows in CSR matrix operator
mutable int nrhs = 0; // the number of the RHSs
int nnz = 0; // the number of non zeros
// copy and keep the I and J arrays in device memory when skipping analysis
// phase
void *csr_offsets_d = NULL; // copy and keep I in device
void *csr_columns_d = NULL; // copy and keep J in device
void *csr_values_d = NULL; // copy and keep csr data in device
// cuDSS object specifies available matrix types for sparse matrices
cudssMatrixViewType_t mview = CUDSS_MVIEW_FULL;
// cuDSS objects storage for sparse matrix Ac, RHS yc and solution xc
std::unique_ptr<cudssMatrix_t> Ac;
mutable cudssMatrix_t xc, yc;
// common for all cuDSS solver instances.
// cuDSS object holds the cuDSS library context
cudssHandle_t handle;
// cuDSS object stores configuration settings for the solver
mutable cudssConfig_t solverConfig;
// cuDSS object holds internal data
mutable cudssData_t solverData;
/// Method for configuring storage for distributed/centralized RHS and
/// solution
void SetNumRHS(int nrhs_) const;
#ifdef MFEM_USE_MPI
/**
* @brief Set the HypreParMatrix object
*
* @param op HypreParMatrix object
*
* @note This method is called inside SetOperator
*/
void SetMatrix(const HypreParMatrix &op);
#endif
/**
* @brief Set the SparseMatrix object
*
* @param op SparseMatrix object
*
* @note This method is called inside SetOperator
*/
void SetMatrix(const SparseMatrix &op);
/**
* @brief Set the matrix values for cuDSS
*
* @param csr_offsets Row offsets of the CSR matrix
* @param csr_columns Column indices of the CSR matrix
* @param csr_values Non-zero values of the CSR matrix
*
* @note This method is called inside SetMatrix.
*/
void SetMatrixCuDSS(int* csr_offsets, int* csr_columns, real_t* csr_values);
/// Method for initializing the cuDSS library and creating the cuDSS handle
void InitCuDSS();
};
} // namespace mfem
#endif // MFEM_USE_CUDSS
#endif // MFEM_CUDSS
+4 -1
View File
@@ -5511,9 +5511,12 @@ void HypreBoomerAMG::SetElasticityOptions(ParFiniteElementSpace *fespace_,
// Save the finite element space to support multiple calls to SetOperator()
this->fespace = fespace_;
MFEM_VERIFY(fespace->GetOrdering() == Ordering::byVDIM,
"The elasticity version of BoomerAMG requires Ordering::byVDIM");
// Make sure the systems AMG options are set
int dim = fespace_->GetParMesh()->Dimension();
SetSystemsOptions(dim, fespace->GetOrdering() == Ordering::byNODES);
SetSystemsOptions(dim); // elasticity solver only works for Ordering::byVDIM
// Nodal coarsening options (nodal coarsening is required for this solver)
// See hypre's new_ij driver and the paper for descriptions.
+5 -2
View File
@@ -1860,8 +1860,11 @@ public:
geometric rigid body modes and could perform better on some problems, see
"Improving algebraic multigrid interpolation operators for linear
elasticity problems", Baker, Kolev, Yang, NLAA 2009, DOI:10.1002/nla.688.
The optional argument @ interp_refine is used to enable/disable pre-processing
of the interpolation matrix through iterative weight refinement */
The optional argument @a interp_refine is used to enable/disable internal
pre-processing of the interpolation matrix through iterative weight
refinement, which could perform better but is more expensive.
@warning This solver assumes Ordering::byVDIM in the FiniteElementSpace
used to construct A.*/
void SetElasticityOptions(ParFiniteElementSpace *fespace,
bool interp_refine = true);
+4
View File
@@ -91,4 +91,8 @@
#endif // MFEM_USE_MPI
#ifdef MFEM_USE_CUDSS
#include "cudss.hpp"
#endif
#endif
+7
View File
@@ -738,6 +738,13 @@ void ConstrainedOperator::AddMult(const Vector &x, Vector &y,
y.Add(a, w);
}
void ConstrainedOperator::AddMultTranspose(const Vector &x, Vector &y,
const real_t a) const
{
MultTranspose(x, w);
y.Add(a, w);
}
RectangularConstrainedOperator::RectangularConstrainedOperator(
Operator *A,
const Array<int> &trial_list,
+3
View File
@@ -1107,6 +1107,9 @@ public:
void MultTranspose(const Vector &x, Vector &y) const override;
void AddMultTranspose(const Vector &x, Vector &y,
const real_t a = 1.0) const override;
void AbsMultTranspose(const Vector &x, Vector &y) const override;
/** @brief Implementation of Mult or MultTranspose.
+3 -237
View File
@@ -20,9 +20,7 @@
#include "../fem/fem.hpp"
#include "petsc.h"
#if defined(PETSC_HAVE_HYPRE)
#include "petscmathypre.h"
#endif
// Backward compatibility
#if PETSC_VERSION_LT(3,11,0)
@@ -105,14 +103,6 @@ static PetscErrorCode MakeShellPC(PC,mfem::Solver&,bool);
static PetscErrorCode MakeShellPCWithFactory(PC,
mfem::PetscPreconditionerFactory*);
// Equivalent functions are present in PETSc source code
// if PETSc has been compiled with hypre support
// We provide them here in case PETSC_HAVE_HYPRE is not defined
#if !defined(PETSC_HAVE_HYPRE)
static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix*,Mat*);
static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix*,Mat*);
#endif
#if PETSC_VERSION_GE(3,15,0) && defined(PETSC_HAVE_DEVICE)
#if defined(MFEM_USE_CUDA) && defined(PETSC_HAVE_CUDA)
#define _USE_DEVICE
@@ -1149,11 +1139,10 @@ PetscParMatrix& PetscParMatrix::operator=(const HypreParMatrix& B)
}
height = B.Height();
width = B.Width();
#if defined(PETSC_HAVE_HYPRE)
ierr = MatCreateFromParCSR(B,MATAIJ,PETSC_USE_POINTER,&A);
#else
ierr = MatConvert_hypreParCSR_AIJ(B,&A); CCHKERRQ(B.GetComm(),ierr);
#endif
CCHKERRQ(B.GetComm(),ierr);
SetUpForDevice();
return *this;
}
@@ -1529,7 +1518,6 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
}
else if (tid == PETSC_MATHYPRE)
{
#if defined(PETSC_HAVE_HYPRE)
if (istrans)
{
Mat B;
@@ -1541,9 +1529,6 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
{
ierr = MatConvert(pA->A,MATHYPRE,MAT_INITIAL_MATRIX,A); PCHKERRQ(pA->A,ierr);
}
#else
MFEM_ABORT("Reconfigure PETSc with --download-hypre or --with-hypre")
#endif
}
else if (tid == PETSC_MATSHELL)
{
@@ -1558,33 +1543,21 @@ void PetscParMatrix::ConvertOperator(MPI_Comm comm, const Operator &op, Mat* A,
{
if (tid == PETSC_MATAIJ)
{
#if defined(PETSC_HAVE_HYPRE)
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATAIJ,
PETSC_USE_POINTER,A);
#else
ierr = MatConvert_hypreParCSR_AIJ(const_cast<HypreParMatrix&>(*pH),A);
#endif
CCHKERRQ(pH->GetComm(),ierr);
}
else if (tid == PETSC_MATIS)
{
#if defined(PETSC_HAVE_HYPRE)
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATIS,
PETSC_USE_POINTER,A);
#else
ierr = MatConvert_hypreParCSR_IS(const_cast<HypreParMatrix&>(*pH),A);
#endif
CCHKERRQ(pH->GetComm(),ierr);
}
else if (tid == PETSC_MATHYPRE || tid == ANY_TYPE)
{
#if defined(PETSC_HAVE_HYPRE)
ierr = MatCreateFromParCSR(const_cast<HypreParMatrix&>(*pH),MATHYPRE,
PETSC_USE_POINTER,A);
CCHKERRQ(pH->GetComm(),ierr);
#else
MFEM_ABORT("Reconfigure PETSc with --download-hypre or --with-hypre")
#endif
}
else if (tid == PETSC_MATSHELL)
{
@@ -2238,11 +2211,7 @@ PetscParMatrix * RAP(PetscParMatrix *A, PetscParMatrix *P)
PetscParMatrix * RAP(HypreParMatrix *hA, PetscParMatrix *P)
{
PetscParMatrix *out,*A;
#if defined(PETSC_HAVE_HYPRE)
A = new PetscParMatrix(hA,Operator::PETSC_MATHYPRE);
#else
A = new PetscParMatrix(hA);
#endif
out = RAP(P,A,P);
delete A;
return out;
@@ -2349,10 +2318,8 @@ Operator::Type PetscParMatrix::GetType() const
if (ok == PETSC_TRUE) { return PETSC_MATSHELL; }
ierr = PetscObjectTypeCompare(oA, MATNEST, &ok); PCHKERRQ(A,ierr);
if (ok == PETSC_TRUE) { return PETSC_MATNEST; }
#if defined(PETSC_HAVE_HYPRE)
ierr = PetscObjectTypeCompare(oA, MATHYPRE, &ok); PCHKERRQ(A,ierr);
if (ok == PETSC_TRUE) { return PETSC_MATHYPRE; }
#endif
return PETSC_MATGENERIC;
}
@@ -5527,207 +5494,6 @@ static PetscErrorCode Convert_Vmarks_IS(MPI_Comm comm,
PetscFunctionReturn(PETSC_SUCCESS);
}
#if !defined(PETSC_HAVE_HYPRE)
#if defined(HYPRE_MIXEDINT)
#error "HYPRE_MIXEDINT not supported"
#endif
#include "_hypre_parcsr_mv.h"
static PetscErrorCode MatConvert_hypreParCSR_AIJ(hypre_ParCSRMatrix* hA,Mat* pA)
{
MPI_Comm comm = hypre_ParCSRMatrixComm(hA);
hypre_CSRMatrix *hdiag,*hoffd;
PetscScalar *da,*oa,*aptr;
PetscInt *dii,*djj,*oii,*ojj,*iptr;
PetscInt i,dnnz,onnz,m,n;
PetscMPIInt size;
PetscErrorCode ierr;
PetscFunctionBeginUser;
hdiag = hypre_ParCSRMatrixDiag(hA);
hoffd = hypre_ParCSRMatrixOffd(hA);
m = hypre_CSRMatrixNumRows(hdiag);
n = hypre_CSRMatrixNumCols(hdiag);
dnnz = hypre_CSRMatrixNumNonzeros(hdiag);
onnz = hypre_CSRMatrixNumNonzeros(hoffd);
ierr = PetscMalloc1(m+1,&dii); CHKERRQ(ierr);
ierr = PetscMalloc1(dnnz,&djj); CHKERRQ(ierr);
ierr = PetscMalloc1(dnnz,&da); CHKERRQ(ierr);
ierr = PetscMemcpy(dii,hypre_CSRMatrixI(hdiag),(m+1)*sizeof(PetscInt));
CHKERRQ(ierr);
ierr = PetscMemcpy(djj,hypre_CSRMatrixJ(hdiag),dnnz*sizeof(PetscInt));
CHKERRQ(ierr);
ierr = PetscMemcpy(da,hypre_CSRMatrixData(hdiag),dnnz*sizeof(PetscScalar));
CHKERRQ(ierr);
iptr = djj;
aptr = da;
for (i=0; i<m; i++)
{
PetscInt nc = dii[i+1]-dii[i];
ierr = PetscSortIntWithScalarArray(nc,iptr,aptr); CHKERRQ(ierr);
iptr += nc;
aptr += nc;
}
ierr = MPI_Comm_size(comm,&size); CHKERRQ(ierr);
if (size > 1)
{
PetscInt *offdj,*coffd;
ierr = PetscMalloc1(m+1,&oii); CHKERRQ(ierr);
ierr = PetscMalloc1(onnz,&ojj); CHKERRQ(ierr);
ierr = PetscMalloc1(onnz,&oa); CHKERRQ(ierr);
ierr = PetscMemcpy(oii,hypre_CSRMatrixI(hoffd),(m+1)*sizeof(PetscInt));
CHKERRQ(ierr);
offdj = hypre_CSRMatrixJ(hoffd);
coffd = hypre_ParCSRMatrixColMapOffd(hA);
for (i=0; i<onnz; i++) { ojj[i] = coffd[offdj[i]]; }
ierr = PetscMemcpy(oa,hypre_CSRMatrixData(hoffd),onnz*sizeof(PetscScalar));
CHKERRQ(ierr);
iptr = ojj;
aptr = oa;
for (i=0; i<m; i++)
{
PetscInt nc = oii[i+1]-oii[i];
ierr = PetscSortIntWithScalarArray(nc,iptr,aptr); CHKERRQ(ierr);
iptr += nc;
aptr += nc;
}
ierr = MatCreateMPIAIJWithSplitArrays(comm,m,n,PETSC_DECIDE,PETSC_DECIDE,dii,
djj,da,oii,ojj,oa,pA); CHKERRQ(ierr);
}
else
{
oii = ojj = NULL;
oa = NULL;
ierr = MatCreateSeqAIJWithArrays(comm,m,n,dii,djj,da,pA); CHKERRQ(ierr);
}
/* We are responsible to free the CSR arrays. However, since we can take
references of a PetscParMatrix but we cannot take reference of PETSc
arrays, we need to create a PetscContainer object to take reference of
these arrays in reference objects */
void *ptrs[6] = {dii,djj,da,oii,ojj,oa};
const char *names[6] = {"_mfem_csr_dii",
"_mfem_csr_djj",
"_mfem_csr_da",
"_mfem_csr_oii",
"_mfem_csr_ojj",
"_mfem_csr_oa"
};
for (i=0; i<6; i++)
{
PetscContainer c;
ierr = PetscContainerCreate(comm,&c); CHKERRQ(ierr);
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
CHKERRQ(ierr);
ierr = PetscObjectCompose((PetscObject)(*pA),names[i],(PetscObject)c);
CHKERRQ(ierr);
ierr = PetscContainerDestroy(&c); CHKERRQ(ierr);
}
PetscFunctionReturn(PETSC_SUCCESS);
}
static PetscErrorCode MatConvert_hypreParCSR_IS(hypre_ParCSRMatrix* hA,Mat* pA)
{
Mat lA;
ISLocalToGlobalMapping rl2g,cl2g;
IS is;
hypre_CSRMatrix *hdiag,*hoffd;
MPI_Comm comm = hypre_ParCSRMatrixComm(hA);
void *ptrs[2];
const char *names[2] = {"_mfem_csr_aux",
"_mfem_csr_data"
};
PetscScalar *hdd,*hod,*aa,*data;
PetscInt *col_map_offd,*hdi,*hdj,*hoi,*hoj;
PetscInt *aux,*ii,*jj;
PetscInt cum,dr,dc,oc,str,stc,nnz,i,jd,jo;
PetscErrorCode ierr;
PetscFunctionBeginUser;
/* access relevant information in ParCSR */
str = hypre_ParCSRMatrixFirstRowIndex(hA);
stc = hypre_ParCSRMatrixFirstColDiag(hA);
hdiag = hypre_ParCSRMatrixDiag(hA);
hoffd = hypre_ParCSRMatrixOffd(hA);
dr = hypre_CSRMatrixNumRows(hdiag);
dc = hypre_CSRMatrixNumCols(hdiag);
nnz = hypre_CSRMatrixNumNonzeros(hdiag);
hdi = hypre_CSRMatrixI(hdiag);
hdj = hypre_CSRMatrixJ(hdiag);
hdd = hypre_CSRMatrixData(hdiag);
oc = hypre_CSRMatrixNumCols(hoffd);
nnz += hypre_CSRMatrixNumNonzeros(hoffd);
hoi = hypre_CSRMatrixI(hoffd);
hoj = hypre_CSRMatrixJ(hoffd);
hod = hypre_CSRMatrixData(hoffd);
/* generate l2g maps for rows and cols */
ierr = ISCreateStride(comm,dr,str,1,&is); CHKERRQ(ierr);
ierr = ISLocalToGlobalMappingCreateIS(is,&rl2g); CHKERRQ(ierr);
ierr = ISDestroy(&is); CHKERRQ(ierr);
col_map_offd = hypre_ParCSRMatrixColMapOffd(hA);
ierr = PetscMalloc1(dc+oc,&aux); CHKERRQ(ierr);
for (i=0; i<dc; i++) { aux[i] = i+stc; }
for (i=0; i<oc; i++) { aux[i+dc] = col_map_offd[i]; }
ierr = ISCreateGeneral(comm,dc+oc,aux,PETSC_OWN_POINTER,&is); CHKERRQ(ierr);
ierr = ISLocalToGlobalMappingCreateIS(is,&cl2g); CHKERRQ(ierr);
ierr = ISDestroy(&is); CHKERRQ(ierr);
/* create MATIS object */
ierr = MatCreate(comm,pA); CHKERRQ(ierr);
ierr = MatSetSizes(*pA,dr,dc,PETSC_DECIDE,PETSC_DECIDE); CHKERRQ(ierr);
ierr = MatSetType(*pA,MATIS); CHKERRQ(ierr);
ierr = MatSetLocalToGlobalMapping(*pA,rl2g,cl2g); CHKERRQ(ierr);
ierr = ISLocalToGlobalMappingDestroy(&rl2g); CHKERRQ(ierr);
ierr = ISLocalToGlobalMappingDestroy(&cl2g); CHKERRQ(ierr);
/* merge local matrices */
ierr = PetscMalloc1(nnz+dr+1,&aux); CHKERRQ(ierr);
ierr = PetscMalloc1(nnz,&data); CHKERRQ(ierr);
ii = aux;
jj = aux+dr+1;
aa = data;
*ii = *(hdi++) + *(hoi++);
for (jd=0,jo=0,cum=0; *ii<nnz; cum++)
{
PetscScalar *aold = aa;
PetscInt *jold = jj,nc = jd+jo;
for (; jd<*hdi; jd++) { *jj++ = *hdj++; *aa++ = *hdd++; }
for (; jo<*hoi; jo++) { *jj++ = *hoj++ + dc; *aa++ = *hod++; }
*(++ii) = *(hdi++) + *(hoi++);
ierr = PetscSortIntWithScalarArray(jd+jo-nc,jold,aold); CHKERRQ(ierr);
}
for (; cum<dr; cum++) { *(++ii) = nnz; }
ii = aux;
jj = aux+dr+1;
aa = data;
ierr = MatCreateSeqAIJWithArrays(PETSC_COMM_SELF,dr,dc+oc,ii,jj,aa,&lA);
CHKERRQ(ierr);
ptrs[0] = aux;
ptrs[1] = data;
for (i=0; i<2; i++)
{
PetscContainer c;
ierr = PetscContainerCreate(PETSC_COMM_SELF,&c); CHKERRQ(ierr);
ierr = PetscContainerSetPointer(c,ptrs[i]); CHKERRQ(ierr);
ierr = PetscContainerSetCtxDestroy(c,__mfem_array_container_destroy);
CHKERRQ(ierr);
ierr = PetscObjectCompose((PetscObject)lA,names[i],(PetscObject)c);
CHKERRQ(ierr);
ierr = PetscContainerDestroy(&c); CHKERRQ(ierr);
}
ierr = MatISSetLocalMat(*pA,lA); CHKERRQ(ierr);
ierr = MatDestroy(&lA); CHKERRQ(ierr);
ierr = MatAssemblyBegin(*pA,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
ierr = MatAssemblyEnd(*pA,MAT_FINAL_ASSEMBLY); CHKERRQ(ierr);
PetscFunctionReturn(PETSC_SUCCESS);
}
#endif
#include <petsc/private/matimpl.h>
static PetscErrorCode __mfem_MatCreateDummy(MPI_Comm comm, PetscInt m,
+3
View File
@@ -42,6 +42,9 @@
#if !defined(PETSC_USE_64BIT_INDICES) && (defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT))
#error "Mismatch between HYPRE (64bit) and PETSc (32bit) integer types"
#endif
#if !defined(PETSC_HAVE_HYPRE)
#error "MFEM requires PETSc built with HYPRE support"
#endif
#include "petscversion.h"
#if PETSC_VERSION_GE(3,12,0)
+38 -14
View File
@@ -123,15 +123,20 @@ EXAMPLE_SUBDIRS = amgx caliper ginkgo hiop petsc pumi sundials superlu moonolith
EXAMPLE_DIRS := examples $(addprefix examples/,$(EXAMPLE_SUBDIRS))
EXAMPLE_TEST_DIRS := examples
MINIAPP_SUBDIRS = common electromagnetics meshing performance tools \
MINIAPP_ALL_SUBDIRS = common electromagnetics meshing performance tools \
toys nurbs gslib adjoint solvers shifted mtop parelag tribol autodiff dfem \
hooke multidomain dpg hdiv-linear-solver spde diag-smoothers contact \
fluids/navier fluids/schrodinger-flow plasma plasma/pic
MINIAPP_RECURSIVE_SUBDIRS = plasma/pic
MINIAPP_SUBDIRS := $(filter-out \
$(MINIAPP_RECURSIVE_SUBDIRS),$(MINIAPP_ALL_SUBDIRS))
MINIAPP_ALL_DIRS := $(addprefix miniapps/,$(MINIAPP_ALL_SUBDIRS))
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
toys shifted dpg diag-smoothers fluids/navier plasma plasma/pic)
toys gslib shifted dpg diag-smoothers fluids/navier plasma plasma/pic)
EM_ALL_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_ALL_DIRS)
EM_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_DIRS)
TEST_SUBDIRS = unit
@@ -146,7 +151,7 @@ MFEM_BUILD_DIR ?= .
BUILD_DIR := $(MFEM_BUILD_DIR)
BUILD_REAL_DIR := $(abspath $(BUILD_DIR))
ifneq ($(BUILD_REAL_DIR),$(MFEM_REAL_DIR))
BUILD_SUBDIRS = $(DIRS) config $(EM_DIRS) doc $(TEST_DIRS)
BUILD_SUBDIRS = $(DIRS) config $(EM_ALL_DIRS) doc $(TEST_DIRS)
CONFIG_FILE_DEF = -DMFEM_CONFIG_FILE='"$(BUILD_REAL_DIR)/config/_config.hpp"'
BLD := $(if $(BUILD_REAL_DIR:$(CURDIR)=),$(BUILD_DIR)/,)
$(if $(word 2,$(BLD)),$(error Spaces in BLD = "$(BLD)" are not supported))
@@ -302,7 +307,7 @@ endif
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS\
SUITESPARSE STRUMPACK GINKGO GNUTLS HDF5 NETCDF SLEPC PETSC MPFR PUMI HIOP\
GSLIB OCCA CEED RAJA UMPIRE MKL_CPARDISO MKL_PARDISO AMGX MAGMA CALIPER PARELAG\
TRIBOL BENCHMARK MOONOLITH ALGOIM
TRIBOL BENCHMARK MOONOLITH ALGOIM CUDSS
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
@@ -371,7 +376,8 @@ MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
MFEM_USE_SIMD MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO MFEM_USE_AMGX\
MFEM_USE_MAGMA MFEM_USE_MUMPS MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_CALIPER\
MFEM_USE_BENCHMARK MFEM_USE_PARELAG MFEM_USE_TRIBOL MFEM_USE_ALGOIM MFEM_USE_ENZYME\
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_SHARED_BUILD MFEM_USE_DOUBLE MFEM_USE_SINGLE
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_SHARED_BUILD MFEM_USE_DOUBLE MFEM_USE_SINGLE\
MFEM_USE_CUDSS MFEM_CUDSS_COMM_LIB MFEM_CUDSS_THREADING_LIB
# List of makefile variables that will be written to config.mk:
MFEM_CONFIG_VARS = MFEM_CXX MFEM_HOST_CXX MFEM_CPPFLAGS MFEM_CXXFLAGS\
@@ -406,7 +412,7 @@ MFEM_INSTALL_DIR = $(abspath $(MFEM_PREFIX))
# If we have 'config' target, export variables used by config/makefile
ifneq (,$(filter config,$(MAKECMDGOALS)))
export $(MFEM_DEFINES) MFEM_DEFINES $(MFEM_CONFIG_VARS) MFEM_CONFIG_VARS
export VERBOSE HYPRE_OPT PUMI_DIR MUMPS_OPT GSLIB_OPT
export VERBOSE HYPRE_OPT PUMI_DIR MUMPS_OPT GSLIB_OPT CUDSS_OPT
endif
# If we have 'install' target, export variables used by config/makefile
@@ -483,10 +489,10 @@ $(OBJECT_FILES): $(BLD)%.o: $(SRC)%.cpp $(CONFIG_MK)
all: examples miniapps $(TEST_DIRS)
.PHONY: miniapps $(EM_DIRS) $(TEST_DIRS)
.PHONY: miniapps $(EM_ALL_DIRS) $(TEST_DIRS)
miniapps: $(MINIAPP_DIRS)
$(MINIAPP_USE_COMMON): miniapps/common
$(EM_DIRS) $(TEST_DIRS): lib
$(EM_ALL_DIRS) $(TEST_DIRS): lib
$(MAKE) -C $(BLD)$(@)
.PHONY: doc
@@ -694,7 +700,7 @@ local-config:
.PHONY: build-config
build-config:
for d in $(BUILD_SUBDIRS); do mkdir -p $(BLD)$${d}; done
for dir in "" $(addsuffix /,config $(EM_DIRS) doc $(TEST_DIRS)); do \
for dir in "" $(addsuffix /,config $(EM_ALL_DIRS) doc $(TEST_DIRS)); do\
printf "# Auto-generated file.\n%s\n%s\n" \
"MFEM_DIR = $(MFEM_REAL_DIR)" \
"include \$$(MFEM_DIR)/$${dir}makefile" \
@@ -737,6 +743,7 @@ status info:
$(info MFEM_USE_SUPERLU5 = $(MFEM_USE_SUPERLU5))
$(info MFEM_USE_MUMPS = $(MFEM_USE_MUMPS))
$(info MFEM_USE_STRUMPACK = $(MFEM_USE_STRUMPACK))
$(info MFEM_USE_CUDSS = $(MFEM_USE_CUDSS))
$(info MFEM_USE_GINKGO = $(MFEM_USE_GINKGO))
$(info MFEM_USE_AMGX = $(MFEM_USE_AMGX))
$(info MFEM_USE_MAGMA = $(MFEM_USE_MAGMA))
@@ -796,13 +803,15 @@ status info:
ASTYLE = $(ASTYLE_BIN) --options=$(SRC)config/mfem.astylerc
ASTYLE_VER = "Artistic Style Version 3.1"
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_DIRS) config,$(dir)/*.?pp)
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_ALL_DIRS) config,$(dir)/*.?pp)
TESTS_SUBDIRS = unit benchmarks convergence mem_manager par-mesh-format
UNIT_TESTS_SUBDIRS = general linalg mesh fem miniapps ceed enzyme
MINIAPPS_SUBDIRS = dpg/util hooke/operators hooke/preconditioners hooke/materials hooke/kernels
UNIT_TESTS_SUBDIRS = general linalg mesh fem miniapps ceed enzyme dfem
MINIAPPS_SUBDIRS = dpg/util hooke/operators hooke/preconditioners \
hooke/materials hooke/kernels
FORMAT_FILES += $(foreach dir,$(TESTS_SUBDIRS),tests/$(dir)/*.?pp)
FORMAT_FILES += $(foreach dir,$(UNIT_TESTS_SUBDIRS),tests/unit/$(dir)/*.?pp)
FORMAT_FILES += $(foreach dir,$(MINIAPPS_SUBDIRS),miniapps/$(dir)/*.?pp)
FORMAT_FILES += config/cmake/config.hpp.in config/config.hpp.in mfem*.hpp
FORMAT_EXCLUDE = general/tinyxml2.cpp tests/unit/catch.hpp
FORMAT_LIST = $(filter-out $(FORMAT_EXCLUDE),$(wildcard $(FORMAT_FILES)))
@@ -833,14 +842,29 @@ mfem_check_command = \
# Verify the C++ code styling in MFEM and check that std::cout and std::cerr are
# not used in the library (use mfem::out and mfem::err instead).
style:
@echo "Applying C++ code style..."
@astyle_version="$$($(ASTYLE_BIN) --version)";\
if [ "$$astyle_version" != $(ASTYLE_VER) ]; then\
printf "%s\n" "Invalid astyle version: '$$astyle_version'"\
"Please use: '"$(ASTYLE_VER)"'";\
exit 1;\
fi
@err_code=0;\
@err_code=0; \
if command -v git 2>&1 > /dev/null && [ -d $(MFEM_DIR)/.git ]; then \
echo "Checking if all git files are selected for formatting ..."; \
ls -1 $(FORMAT_FILES) | sort > format-files-make.txt; \
git -C $(MFEM_DIR) ls-files '*.[ch]pp*' | sort \
> format-files-git.txt; \
cat format-files-make.txt format-files-git.txt | sort | uniq \
> format-files-make-plus-git.txt; \
rm -f format-files-git.txt; \
$(call mfem_check_command,\
diff format-files-make.txt format-files-make-plus-git.txt | \
grep "^> ",\
"All git files are selected for formatting",\
"The above git files are NOT selected for formatting"); \
rm -f format-files-make.txt format-files-make-plus-git.txt; \
fi; \
echo "Applying C++ code style...";\
$(call mfem_check_command,\
$(ASTYLE) $(FORMAT_LIST) | grep Formatted,\
"No source files were changed",\
+2 -1
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
+6 -1
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 &&
@@ -11332,6 +11335,8 @@ void Mesh::Swap(Mesh& other, bool non_geometry)
mfem::Swap(attributes, other.attributes);
mfem::Swap(bdr_attributes, other.bdr_attributes);
mfem::Swap(attribute_sets.attr_sets, other.attribute_sets.attr_sets);
mfem::Swap(bdr_attribute_sets.attr_sets, other.bdr_attribute_sets.attr_sets);
mfem::Swap(geom_factors, other.geom_factors);
mfem::Swap(face_geom_factors, other.face_geom_factors);

Some files were not shown because too many files have changed in this diff Show More