Compare commits

...
Author SHA1 Message Date
Veselin Dobrev fe728ab440 In class DeviceConformingProlongationOperator, improve the hadling of the
case when GPU-aware MPI is not enabled.
2025-07-12 15:56:14 -07:00
Veselin Dobrev 6c9566253d Merge branch 'master' into device-conf-prolongation-optim 2025-07-12 15:41:12 -07:00
Tzanio Kolev a901754de5 Merge pull request #4841 from mfem/nurbs-surf
NURBS surface interpolation minapp
2025-07-11 09:18:02 -07:00
Tzanio Kolev 03da41c0f5 Merge pull request #4924 from mfem/dfem-elem-restriction-fix
Multiple actions with one dfem `DifferentiableOperator`
2025-07-11 09:17:25 -07:00
Tzanio Kolev c6ec74db41 Merge pull request #4921 from mfem/submesh-small-simplification
Small simplification in `SubMeshUtils::AddBoundaryElements`
2025-07-10 08:55:28 -07:00
Eric B. Chin 8854247f86 Merge branch 'master' into dfem-elem-restriction-fix 2025-07-09 11:17:01 -07:00
Veselin Dobrev 755e4501e1 Merge pull request #4899 from mfem/ubsan
[Github] Sanitizers action
2025-07-08 23:02:02 -07:00
Veselin Dobrev 1ec73c3bf6 Merge branch 'master' into ubsan 2025-07-08 19:36:26 -07:00
E. B. Chin 3a15fe3d96 sum into residual_l 2025-07-08 15:31:36 -07:00
Tzanio Kolev 8b000dd222 Merge pull request #4898 from mfem/dfem-bugfixes
Fix UB in dFEM
2025-07-06 10:51:12 -07:00
Tzanio Kolev a4d6889332 Merge pull request #4913 from mfem/tmop-memory-warning-fix
TMOP uninitialized memory warning fix
2025-07-06 10:50:38 -07:00
Tzanio Kolev e0fbc5e3aa Merge branch 'master' into nurbs-surf 2025-07-06 10:50:17 -07:00
Veselin Dobrev 03da9d7789 Small simplification in SubMeshUtils::AddBoundaryElements 2025-07-05 13:46:56 -07:00
John Camier 9c8874a38c Merge branch 'master' into ubsan 2025-07-03 16:06:57 -07:00
Tzanio Kolev bb2460cbd0 Merge branch 'master' into tmop-memory-warning-fix 2025-07-02 18:50:36 -07:00
Tzanio Kolev 0648e50e70 Merge pull request #4843 from mfem/netcdf-single
Write Exodus meshes with real_t instead of double
2025-07-02 11:45:02 -07:00
camierjs 45e8125fd6 Merge branch 'master' into ubsan 2025-07-02 07:33:56 -07:00
Tzanio Kolev 25056defeb Merge branch 'master' into nurbs-surf 2025-07-01 14:27:00 -07:00
Tzanio Kolev 20cd965ed8 Merge branch 'master' into tmop-memory-warning-fix 2025-07-01 12:54:18 -07:00
Tzanio Kolev 1fda9c2391 Merge branch 'master' into netcdf-single 2025-07-01 12:46:55 -07:00
Tzanio Kolev 2cec0353b1 Merge pull request #4757 from mfem/hip-unit-tests
Added general GPU and CUDA/HIP-specific unit tests
2025-07-01 12:46:18 -07:00
Tzanio Kolev 9d22775395 Merge pull request #4822 from mfem/gpu-thread-direct
Direct threadblock loops
2025-07-01 12:43:38 -07:00
Tzanio Kolev 94625fad8f Merge pull request #4870 from mfem/vtkhdf-chunk-fix
Improve chunking in VTKHDF writer
2025-07-01 12:42:45 -07:00
Tzanio Kolev c9a9c71ff5 Merge pull request #4866 from rfhaque/master
Decompose det.cpp into separate source and header files
2025-07-01 12:41:57 -07:00
Tzanio Kolev 764d9919b5 Merge pull request #4892 from mfem/hughcars/missing-host-read-write-fix
Fix missing HostReadWrite for Nodes
2025-07-01 12:40:40 -07:00
Tzanio Kolev 8efbd4e46f Merge branch 'master' into netcdf-single 2025-07-01 12:00:59 -07:00
John Camier b94ac358e4 Merge branch 'master' into hip-unit-tests 2025-07-01 09:51:43 -07:00
John Camier d4d4b79522 Merge branch 'master' into vtkhdf-chunk-fix 2025-07-01 09:51:12 -07:00
John Camier 120f4cb043 Merge branch 'master' into hughcars/missing-host-read-write-fix 2025-07-01 09:50:46 -07:00
camierjs 4f1597c1bc CHANGELOG: MFEM_FOREACH_THREAD_DIRECT in GPU computing 2025-06-30 18:16:56 -07:00
John Camier 56beedbdcb Merge branch 'master' into gpu-thread-direct 2025-06-30 18:06:23 -07:00
camierjs 520a9c5125 On branch: master, next & remove debug mode 2025-06-29 15:39:12 -07:00
camierjs ed521022cd Fine-grained sanitizer jobs 2025-06-29 15:17:44 -07:00
John Camier 7b35a47626 Merge branch 'master' into ubsan 2025-06-28 21:34:36 -07:00
Veselin Dobrev def35c8a15 In HypreParMatrix::EliminateBC, use stream synchronization instead of
device synchronization and do it only when hypre is using GPU-aware MPI.

Remove device synchronization before CUDA/HIP free which implicitly
perform the same synchronization.
2025-06-28 08:05:30 -07:00
John Camier 4e35b3d8f1 Merge branch 'master' into hughcars/missing-host-read-write-fix 2025-06-28 06:26:57 -07:00
Andrew Ho 29681677a1 Merge branch 'master' into hip-unit-tests 2025-06-27 23:01:04 -07:00
Mittal, Ketan 9d72af995f remove unneeded code 2025-06-27 12:50:27 -07:00
Mittal, Ketan b099252dcf fix 2025-06-27 11:50:06 -07:00
camierjs d696fc2cea Merge branch 'master' into ubsan 2025-06-26 17:23:44 -07:00
Andrew Ho 8dcd0d6349 add device syncs in EliminateBC
This should fix potential race conditions in some situations
2025-06-26 15:57:18 -07:00
Veselin Dobrev 2951d5f98e Fix false positives for testing errors in debug mode 2025-06-26 13:51:34 -07:00
Andrew Ho 4b27589abf missing hostread 2025-06-26 11:38:50 -07:00
Riyaz Haque 8338aa85e6 Merge branch 'master' into master 2025-06-26 10:33:31 -07:00
Veselin Dobrev e20bb381ca In Device::Print, show the GPU-aware MPI configuration only when MPI is
being used.
2025-06-26 08:26:20 -07:00
camierjs 89e23a93f5 Re-wrap Miscellaneous at 80 characters 2025-06-26 06:51:30 -07:00
Veselin Dobrev 4816fa0849 Added the option to enable GPU-aware MPI in MFEM using the environment
variable 'MFEM_GPU_AWARE_MPI' set to any value. Setting this environment
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.

In Device::Print, show the GPU-aware MPI usage status when using a device
backend.

Update some unit tests to better handle failures.
2025-06-25 11:47:31 -07:00
Riyaz Haque 6fc7a8ca5a Merge branch 'master' into master 2025-06-25 10:33:57 -07:00
Tzanio Kolev 22c8b607dc Merge branch 'master' into hughcars/missing-host-read-write-fix 2025-06-25 08:02:02 -07:00
camierjs 91deab3c75 CHANGELOG w/o blanks-around-headings 2025-06-25 05:48:39 -07:00
Tzanio Kolev 87cee25894 Merge branch 'master' into dfem-bugfixes 2025-06-25 03:58:42 -07:00
Tzanio Kolev 1bcddddfdb Merge branch 'master' into gpu-thread-direct 2025-06-25 03:41:32 -07:00
camierjs da68672955 Rename 'unit' to 'tests' 2025-06-24 17:12:07 -07:00
camierjs a5dd4b862b Rename matrix sanitizer 2025-06-24 17:05:33 -07:00
Veselin Dobrev a3fcb89049 Small doxygen and CHANGELOG updates 2025-06-24 16:59:38 -07:00
Veselin Dobrev bd6f3d51c8 Remove checks for c++17 which is now required 2025-06-24 16:05:47 -07:00
camierjs 8d95f71305 Merge branch 'master' into ubsan 2025-06-24 14:33:07 -07:00
camierjs aee0cb1dc6 Split tests: [unit, examples, miniapps] 2025-06-24 14:33:00 -07:00
Dylan Copeland 7dded1fdcf Merge branch 'master' of github.com:mfem/mfem into nurbs-surf 2025-06-24 13:28:06 -07:00
John Camier cc0dcaad09 Merge branch 'master' into vtkhdf-chunk-fix 2025-06-23 16:11:40 -07:00
John Camier 960379a43d Merge branch 'master' into ubsan 2025-06-23 16:11:00 -07:00
Hugh Carson 3782ece6b3 Merge branch 'master' into hughcars/missing-host-read-write-fix 2025-06-23 16:14:44 -04:00
camierjs 823c7a952d Asan strdup for Hypre 2.19.0 2025-06-23 09:03:27 -07:00
camierjs 2bc553633e MFEM ex1p ctest 2025-06-23 08:37:47 -07:00
camierjs 8be00d1115 Reuse actions Hypre & Metis caches 2025-06-23 07:45:57 -07:00
Riyaz Haque bdd2bfbb79 Merge with master 2025-06-22 22:35:34 -07:00
John Camier 1ea4cd14da Merge branch 'master' into hip-unit-tests 2025-06-22 20:26:16 -07:00
John Camier 63b72c4153 Merge branch 'master' into gpu-thread-direct 2025-06-22 20:26:08 -07:00
John Camier 054593bd4d Merge branch 'master' into vtkhdf-chunk-fix 2025-06-22 20:25:57 -07:00
camierjs 298a4bc32a Avoid CopyFrom in DeviceConformingProlongationOperator 2025-06-22 18:18:03 -07:00
camierjs 65866edd70 Force MPICXX 2025-06-22 13:52:40 -07:00
camierjs 85b96208f0 Env CTEST fix 2025-06-22 13:22:40 -07:00
camierjs b825a46061 CTEST fix 2025-06-22 13:20:11 -07:00
camierjs d6cbd4f99a ctests options fix 2025-06-22 13:06:35 -07:00
camierjs c691658232 Switch to ctests 2025-06-22 13:05:08 -07:00
camierjs 157ffad537 Simplify, meld back & ninja default nproc 2025-06-22 12:07:07 -07:00
camierjs cfdb7d2a03 Cleanup 2025-06-22 11:10:43 -07:00
camierjs a95a2dc251 Ninja verbose builds & jobs 2025-06-22 10:12:51 -07:00
camierjs a7737e65ab MPI_LIB to LDFLAGS 2025-06-22 09:56:53 -07:00
camierjs dda669bd70 Cleanup & ninja tests/unit/test 2025-06-22 09:54:25 -07:00
camierjs 802345aa91 config-options force CMAKE_CXX_FLAGS_RELEASE 2025-06-22 09:21:59 -07:00
camierjs 1e84d8a9c5 config-options redundant FLAGS 2025-06-22 09:19:43 -07:00
camierjs ce30630f5f config-options to Release 2025-06-22 09:14:21 -07:00
camierjs 0055de1734 yamllint fix 2025-06-22 09:10:28 -07:00
camierjs 954757f7de Ninja test 2025-06-22 09:08:53 -07:00
camierjs 7ad1da790c config-options strip new line 2025-06-22 08:33:11 -07:00
camierjs 4290365459 CMAKE_CXX_FLAGS escapes 2025-06-22 08:25:07 -07:00
camierjs 04d6fa900d CMAKE_CXX_FLAGS escapes 2025-06-22 08:23:42 -07:00
camierjs 9fec57261b CMAKE_CXX_FLAGS escapes 2025-06-22 08:20:48 -07:00
camierjs 9e8a710c92 CLANG_VER 2025-06-22 08:18:26 -07:00
camierjs c5d2f364ac Clang Local ENV 2025-06-22 08:09:04 -07:00
camierjs fca154fbd4 Clang Local 2025-06-22 08:07:56 -07:00
camierjs 804bdb498a CMAKE_EXE_LINKER_FLAGS 2025-06-22 08:06:48 -07:00
camierjs 1546398ee8 Clang Local 2025-06-22 08:04:30 -07:00
camierjs 9237b4cc2e config-options escapes 2025-06-22 08:00:49 -07:00
camierjs a02927005d config-options fix 2025-06-22 07:59:34 -07:00
camierjs 6256741216 config-options addons 2025-06-22 07:41:04 -07:00
camierjs a5fcfa02e8 CMake config-options 2025-06-22 07:28:40 -07:00
camierjs 2fc9b97cb4 .github/workflows/mfem-sanitizers.yml Hypre version 2.19.0 2025-06-22 07:25:01 -07:00
camierjs a5ca81806b .github/workflows/mfem-sanitizers.yml CMake try 2025-06-22 07:19:01 -07:00
camierjs d4513550f9 [asan] miniapps/solvers/bramble_pasciak fix 2025-06-21 20:33:10 -07:00
camierjs d1befb2ea6 yaml lint & re-enable examples & miniapps 2025-06-21 15:33:15 -07:00
camierjs e11a093e72 Setup DofToQuad information mode 2025-06-21 14:36:25 -07:00
camierjs 943234617b hypre-dir metis-dir fix 2025-06-21 13:41:21 -07:00
camierjs 36389366ac Use dirs 2025-06-21 13:19:04 -07:00
camierjs dbd2b5556b Fix ASan suppression file 2025-06-21 13:12:32 -07:00
camierjs 6570ca9c7a mkdir ASAN_DIR 2025-06-21 13:09:28 -07:00
camierjs 71943e120c Split GITHUB_ENV setup 2025-06-21 13:06:03 -07:00
camierjs ef65351cc9 Use LLVM_DIR 2025-06-21 12:34:36 -07:00
camierjs 5a7807055b GITHUB_WORKSPACE 2025-06-21 12:33:47 -07:00
camierjs 08b46a12dc LLVM_DIR w/o env 2025-06-21 12:32:55 -07:00
camierjs 78124a649d env fix 2025-06-21 12:28:57 -07:00
camierjs 0e9d10c53d LLVM_DIR fix 2025-06-21 12:27:55 -07:00
camierjs 911eb07565 Cleanup workflows mfem-sanitizers.yml 2025-06-21 12:26:52 -07:00
camierjs 2efec6390b Fix mfem-sanitizers.yml env (bis) 2025-06-21 12:08:21 -07:00
camierjs 756bc524ab Fix mfem-sanitizers.yml env 2025-06-21 12:06:01 -07:00
camierjs bade193d79 [asan] tests/unit/mesh/test_ncmesh: TetMemory MFEM_USE_MEMALLOC=OFF 2025-06-21 11:56:40 -07:00
camierjs 4363cd2dc2 [asan] tests/unit/linalg/test_hypre_prec 2025-06-21 10:06:20 -07:00
camierjs cbbe609ff8 Merge branch 'master' into ubsan 2025-06-21 08:51:08 -07:00
camierjs 23caac9573 Cleanup 2025-06-21 08:51:01 -07:00
camierjs 3eb0e321f4 [asan] tests/unit/fem/test_var_order.cpp 2025-06-20 20:38:11 -07:00
camierjs 58c276b1d9 Fix HYPRE_TOP_DIR 2025-06-20 17:45:30 -07:00
camierjs b81c67a061 Switch to LLVM 19.1.7 2025-06-20 17:43:57 -07:00
camierjs fbd6aa17e0 Avoid examples 2025-06-20 17:03:16 -07:00
camierjs 199192c0f6 Bump LLVM and HYPRE versions 2025-06-20 17:02:37 -07:00
camierjs 27989c68bd [ubsan] miniapps/solvers/bramble_pasciak seed 2025-06-20 16:57:31 -07:00
camierjs 8ba6b88ee0 CHANGELOG Miscellaneous update 2025-06-20 14:54:12 -07:00
camierjs df03268c01 [asan] miniapps/solvers/block-solvers and div_free_solver leaks 2025-06-20 14:45:08 -07:00
camierjs 7e7322cb86 WIP non coupled DivFreeSolver 2025-06-20 13:43:22 -07:00
Dylan Copeland d8b9c7881b Transpose argument for banded factorization. 2025-06-20 10:22:47 -07:00
Dylan Copeland 58f0e28453 Merge branch 'master' of github.com:mfem/mfem into nurbs-surf 2025-06-20 10:13:14 -07:00
camierjs 9d48f23dca [asan] miniapps/solvers/bramble_pasciak.cpp leaks 2025-06-20 09:33:22 -07:00
camierjs 1947748db6 [undefined] miniapps/spde/generate_random_field seed 2025-06-20 06:39:41 -07:00
camierjs 7aca441524 Exclude MemorySanitizer w/ MPI 2025-06-19 20:55:34 -07:00
camierjs 689522beb5 Fix parallel UB and Memory 2025-06-19 20:33:31 -07:00
camierjs c0254e3835 Hardcoded asan.supp path 2025-06-19 19:27:42 -07:00
camierjs 85e1855d1b ASan suppression file path fix 2025-06-19 19:02:12 -07:00
camierjs 51eb3c0026 GITHUB_WORKSPACE asan.supp 2025-06-19 18:59:52 -07:00
camierjs c1c348f24f Add ASan suppression file 2025-06-19 18:53:58 -07:00
camierjs 043c6ee860 LLVM_LDFLAGS for get_hypre_version 2025-06-19 18:34:26 -07:00
camierjs 790b4b9bce MPI_LIB for get_hypre_version 2025-06-19 18:24:14 -07:00
camierjs 69aea4ce75 MPI_INC/LIB 2025-06-19 18:10:23 -07:00
camierjs d7d173a215 MPICXX to c++ 2025-06-19 18:02:19 -07:00
camierjs d7ac021db2 MPI workflow debug, env and compiler 2025-06-19 17:54:17 -07:00
camierjs 2b1d4eb30c Parallel sanitizer action 2025-06-19 17:02:26 -07:00
camierjs c134457322 Revert md style for CHANGELOG 2025-06-19 15:50:00 -07:00
camierjs 33bd60f7c7 Updates changelog with sanitizer details 2025-06-19 15:27:38 -07:00
camierjs 8f3e61a4d3 Meld back to master 2025-06-19 11:02:07 -07:00
camierjs 351806ce77 [MemorySanitizer] use-of-uninitialized-value InvTNewtonSolverBase 2025-06-19 10:59:27 -07:00
camierjs d865ac444e [undefined] mesh_readers ReadHeaderEntry 2025-06-19 10:51:25 -07:00
camierjs b14a9b4663 Meld back toward master 2025-06-19 09:42:35 -07:00
camierjs cdd560e6a2 [undefined] lissajous null pointer socketstream
Meld back toward master
2025-06-19 09:23:57 -07:00
camierjs 4c9ddf93e4 [undefined] KnotVector fix 2025-06-19 08:31:46 -07:00
camierjs 8410c205a3 [undefined] test_sedov DeltaCoefficient SetWeight 2025-06-19 07:48:01 -07:00
camierjs f714dcfd57 Hcurl/Hdiv PA Coefficient coeff2 fix 2025-06-19 07:20:12 -07:00
camierjs 53fb6e3977 [MemorySanitizer] TestFDCalcDivShape dim 2 pt.z 2025-06-19 07:06:10 -07:00
camierjs 678a9db016 [MemorySanitizer] TestCalcDivShape dim 2 pt.z
Avoid overflow in dot product test
2025-06-18 16:46:10 -07:00
camierjs c09246351d [MemorySanitizer] dim 2 pt.z 2025-06-18 16:16:45 -07:00
camierjs 86a8d39e54 Cleanup and allow all tests 2025-06-18 15:23:22 -07:00
camierjs 54854b0908 Cleanup, LLVM flags w/o AddressSanitizer 2025-06-18 15:06:24 -07:00
camierjs 8fa48c2425 Cleanup LLVM LIB & INC 2025-06-18 14:45:12 -07:00
camierjs 3e9b8605f9 llvm-project/runtimes fix 2025-06-18 14:18:17 -07:00
camierjs d84aa5a355 Cleanup 2025-06-18 14:12:49 -07:00
camierjs c11c5cf654 Run LLVM Clone 2025-06-18 13:58:33 -07:00
camierjs 1c8d25c6ed LLVM libcxx Build Steps 2025-06-18 13:54:49 -07:00
camierjs be6d5e2b01 memory_sanitizer 2025-06-18 13:30:38 -07:00
camierjs 8abdc6500e MemorySanitizer code fix 2025-06-18 13:18:18 -07:00
camierjs 21df84f320 address_sanitizer 2025-06-18 13:03:02 -07:00
camierjs 9f5860fda2 Add LLVM_VERSION and ex1 trigger tests 2025-06-18 12:53:48 -07:00
camierjs c5a03405cf Rename mfem-sanitizers 2025-06-18 12:20:03 -07:00
camierjs 33d0d7dbfe Fix stdlib 2025-06-18 12:19:12 -07:00
camierjs c085ec6544 Setup LLVM_SANITIZER 2025-06-18 12:14:24 -07:00
camierjs 1f860fbfaf Cleanup GITHUB_WORKSPACE 2025-06-18 12:01:09 -07:00
camierjs c43420c375 MFEM Checkout 2025-06-18 11:57:42 -07:00
camierjs ecbdd73c54 Dump workspaces 2025-06-18 11:53:23 -07:00
camierjs d30e13c543 github.workspace paths 2025-06-18 11:50:17 -07:00
camierjs ba2b8aea52 Build libc++ 2025-06-18 11:39:06 -07:00
camierjs f1353bd6e9 Change mfem-sanitizers.sh path 2025-06-18 11:33:51 -07:00
camierjs 3cb0bee255 Fix both uses and run keys 2025-06-18 10:46:49 -07:00
camierjs bda0b9aba5 Fix github mfem-sanitizers paths 2025-06-18 10:43:21 -07:00
camierjs e85af79c16 Try MemorySanitizer 2025-06-18 10:39:50 -07:00
John Camier 06c4da64af Merge branch 'master' into hip-unit-tests 2025-06-18 10:22:55 -07:00
John Camier b841c9df71 Merge branch 'master' into gpu-thread-direct 2025-06-18 10:22:43 -07:00
John Camier 31a6329964 Merge branch 'master' into vtkhdf-chunk-fix 2025-06-18 10:22:24 -07:00
camierjs 9f83167010 make test 2025-06-18 10:11:32 -07:00
camierjs 2b4085e2dd Try ubuntu-latest & Setup clang 2025-06-18 10:01:06 -07:00
camierjs a69ea6d698 Merge branch 'master' into ubsan 2025-06-18 08:04:59 -07:00
camierjs a617205ee1 Revert check & make test 2025-06-17 21:11:03 -07:00
camierjs 620f906765 make check 2025-06-17 21:02:20 -07:00
camierjs 2620effa65 Avoid optimizations 2025-06-17 21:00:45 -07:00
camierjs 3da9bdc39a global env OPTIONS 2025-06-17 20:46:18 -07:00
camierjs 2ab00899fd Merge branch 'master' into ubsan 2025-06-17 20:23:38 -07:00
camierjs 7009449ef5 make check 2025-06-17 20:22:57 -07:00
camierjs 34482860b0 ASAN_OPTIONS w/o spaces 2025-06-17 20:22:18 -07:00
camierjs 8f4aafdebc matrix env make test 2025-06-17 19:00:01 -07:00
camierjs 26eea4f2fd mfem-sanitizers run tweak 2025-06-17 18:20:14 -07:00
camierjs 313f6856e8 Remove var-tracking-assignments 2025-06-17 17:58:37 -07:00
camierjs bf97e92be2 Use config-options for CXX 2025-06-17 17:57:38 -07:00
camierjs d7a04ca4ac Adjust options and remove symbolizer 2025-06-17 17:29:18 -07:00
camierjs 28b1ac0c9d Try mfem-sanitizers.yml style 2025-06-17 17:18:34 -07:00
camierjs 0eb2d21602 Remove from MemorySanitizer 'include' 2025-06-17 16:35:15 -07:00
camierjs efcf608a5d Remove MemorySanitizer 2025-06-17 16:32:20 -07:00
camierjs f1d56c4068 env matrix 2025-06-17 16:23:45 -07:00
camierjs 94698f27d7 Remove matrix name 2025-06-17 16:19:27 -07:00
camierjs 30dac8986c Sanitizers matrix 2025-06-17 16:18:13 -07:00
John Camier 0691354c84 Update mfem-sanitizers.yml matrix 2025-06-17 16:01:09 -07:00
camierjs 5c0b2a6b62 Add LDFLAGS for all runs
Fix float-conversion warnings
2025-06-17 15:43:41 -07:00
camierjs c25b84fbd8 Add undefined behavior and uninitialized memory use detectors 2025-06-17 14:52:43 -07:00
Tzanio Kolev d6c1edc9a3 Merge branch 'master' into netcdf-single 2025-06-17 08:14:28 -07:00
John Camier 74498373c9 Merge branch 'master' into gpu-thread-direct 2025-06-16 08:42:17 -07:00
John Camier 1cc3d81866 Merge branch 'master' into vtkhdf-chunk-fix 2025-06-16 08:33:56 -07:00
Julian Andrej 233316269a ub fix attempt 2025-06-16 07:50:14 -07:00
Andrew Ho e34c6b6013 Merge branch 'master' into hip-unit-tests 2025-06-14 16:22:47 -07:00
Dylan Copeland ea593def25 Merge branch 'master' of github.com:mfem/mfem into nurbs-surf 2025-06-13 14:15:39 -07:00
Dylan Copeland f316ec7d5e Merge branch 'master' of github.com:mfem/mfem into nurbs-surf 2025-06-13 11:08:05 -07:00
John Camier 800be7971a Merge branch 'master' into hip-unit-tests 2025-06-13 08:49:35 -07:00
John Camier ee06c0eb44 Merge branch 'master' into gpu-thread-direct 2025-06-13 08:49:18 -07:00
Hugh Carson f0e8e6e19a Add in missing HostReadWrite in DoNodeReorder for operator() usage 2025-06-12 11:38:00 -04:00
dylan-copeland ff11a6b572 Merge branch 'master' of github.com:mfem/mfem into nurbs-surf 2025-06-09 13:03:05 -07:00
Andrew Ho a8bbdf4fd4 Merge branch 'master' into hip-unit-tests 2025-06-02 11:33:00 -07:00
Will Pazner 2421b48f56 Enable compression by default in ParaViewHDFDataCollection 2025-05-30 07:19:55 -07:00
Will Pazner 2cd2d11215 In VTKHDF, avoid resizing on initial dataset creation 2025-05-30 07:19:28 -07:00
John Camier e11d19e3a9 Merge branch 'master' into gpu-thread-direct 2025-05-29 07:43:53 -07:00
Will PaznerandJohn Camier b0c30784b6 Address some implicit conversion warnings
Co-authored-by: John Camier <camierjs@gmail.com>
2025-05-28 20:42:16 -07:00
John Camier a7aa6c5a7c Merge branch 'master' into vtkhdf-chunk-fix 2025-05-28 11:15:42 -07:00
Riyaz Haque f6c2f10dee Merge branch 'master' into master 2025-05-28 07:47:25 -07:00
John Camier 5dcf6f7ead Merge branch 'master' into gpu-thread-direct 2025-05-26 20:39:03 -07:00
John Camier e8ed1a4c02 Merge branch 'master' into vtkhdf-chunk-fix 2025-05-26 16:11:23 -07:00
Riyaz Haque 3cfca882af Merge remote-tracking branch 'CEED/master' 2025-05-26 07:40:01 -07:00
Will Pazner 70cb8fcc04 Fix shadow warning in VTKHDF 2025-05-23 09:13:05 -07:00
Will Pazner 0366ad2468 Improve chunking in VTKHDF writer
The data arrays grow only in the first dimension, and their other
dimensions are fixed. Therefore, the chunk size is those dimensions
should be equal to the dataset size. This can greatly reduce the
size of saved datasets.
2025-05-23 09:12:56 -07:00
Riyaz Haque a355f28eae Decompose det.cpp into separate source and header files 2025-05-19 19:36:05 -07:00
John Camier a75b1ca9c0 Merge branch 'master' into hip-unit-tests 2025-05-19 15:32:41 -07:00
John Camier 8ff6d69f74 Merge branch 'master' into gpu-thread-direct 2025-05-19 15:32:18 -07:00
Dylan Copeland 35aeecb5c0 Minor fixes. 2025-05-16 19:15:26 -07:00
Dylan Copeland 44f2a63f16 Merge branch 'master' of github.com:mfem/mfem into nurbs-surf 2025-05-16 19:07:45 -07:00
Andrew Ho eb9022540e Merge branch 'master' into hip-unit-tests 2025-05-12 13:04:43 -07:00
John Camier cf127c8b14 Merge branch 'master' into gpu-thread-direct 2025-05-09 08:05:36 -07:00
Tom Stitt df36d0f352 add check for D1D size 2025-05-05 14:06:44 -07:00
Tom Stitt 765ebcecaa Merge branch 'gpu-thread-direct' of github.com:mfem/mfem into gpu-thread-direct 2025-05-05 13:49:09 -07:00
John Camier b3a08b91d6 Merge branch 'master' into hip-unit-tests 2025-05-04 07:38:47 -07:00
Tzanio Kolev 726b5f99ff Added jittering option (off by default) 2025-05-03 23:27:20 -07:00
Dylan Copeland a943683063 Removed optional nodes argument to Mesh::Print. Refactored miniapp. 2025-05-03 19:47:54 -07:00
Tzanio Kolev b6fb45f384 CI fixes 2025-05-03 18:22:51 -07:00
Tzanio Kolev d119fa7636 CI fixes 2025-05-03 17:43:50 -07:00
Tzanio Kolev 4a7c643f99 Fixed, hacks and improvements in the NURBS Surface miniapp 2025-05-03 17:34:45 -07:00
Tzanio Kolev 2dfd2ccfc5 Merge branch 'master' into gpu-thread-direct 2025-05-03 13:27:33 -07:00
Tzanio Kolev ae9a8b2897 Merge branch 'master' into nurbs-surf 2025-05-02 14:02:23 -07:00
Dylan Copeland 2d3aba5d87 Generalized machine epsilon in KnotVector::FindMaxima. 2025-05-02 10:05:58 -07:00
Andrew Ho f4c66c56d6 updated changelog 2025-05-02 09:56:15 -07:00
Andrew Ho 3c3face72c Merge branch 'master' into hip-unit-tests 2025-05-02 10:31:33 -06:00
dylan-copeland 762551da72 Mac fix. 2025-05-01 21:08:35 -07:00
Dylan Copeland 293a374a74 Minor fixes. 2025-05-01 21:00:07 -07:00
Dylan Copeland 39be93547e Refactoring to simplify the API. 2025-05-01 20:49:50 -07:00
Dylan Copeland 7559d37c58 Label glvis windows. 2025-05-01 20:13:23 -07:00
dylan-copeland 90eed63144 Remove unused variables. 2025-05-01 18:23:39 -07:00
Dylan Copeland e6bc4e5a0e Remove no-vis in tests. 2025-05-01 17:25:08 -07:00
Dylan Copeland 03ec8d78e2 Documentation. New miniapp checklist. 2025-05-01 16:46:32 -07:00
Dylan Copeland 3fbeff1db7 Fix visualization. 2025-05-01 11:50:07 -07:00
Dylan Copeland 3d7ac596da Revert a previous change. Reduce output to 3 meshes. 2025-05-01 11:32:34 -07:00
Dylan Copeland 57e1693fc7 CHANGELOG and some minor edits. 2025-05-01 10:54:42 -07:00
Dylan Copeland 67ca28a501 Merge branch 'nurbs-surf' of github.com:mfem/mfem into nurbs-surf 2025-04-30 12:40:46 -07:00
Dylan Copeland 6d334a925a Merge branch 'master' of github.com:mfem/mfem into nurbs-surf 2025-04-30 12:40:31 -07:00
Dylan Copeland 8183e1729d More optimization by reusing banded matrix factorization. 2025-04-30 12:40:16 -07:00
Tzanio Kolev ef1e0caed1 Merge branch 'master' into nurbs-surf 2025-04-30 09:01:34 -07:00
Will Pazner adbe1bfe3a Write Exodus meshes with real_t instead of double
Also pass std::string by const reference instead of value
2025-04-30 08:59:07 -07:00
John Camier 5590b87f5e Merge branch 'master' into hip-unit-tests 2025-04-30 07:53:13 -07:00
Dylan Copeland f28cd12995 ifdef lapack for banded solver 2025-04-29 22:32:36 -07:00
Dylan Copeland e0aba0647d Banded solver for 1D KnotVector interpolation. 2025-04-29 22:28:49 -07:00
camierjs cd4593bf8e Fix MFEM_TMOP_(PA_)DEVICE and update gitignore 2025-04-29 16:09:23 -07:00
Dylan Copeland 0cc5280e34 New miniapp to fit a NURBS surface to a structured grid of 3D point data. 2025-04-29 11:14:28 -07:00
Tzanio Kolev f12f0efb31 Merge branch 'master' into gpu-thread-direct 2025-04-28 18:35:46 -07:00
Tom Stitt e3c3150958 Merge remote-tracking branch 'origin/master' into gpu-thread-direct 2025-04-25 11:39:44 -07:00
Andrew Ho 911fbfbe82 updated testing readme 2025-04-24 11:36:19 -07:00
Andrew Ho 0b3b21dbb2 Merge remote-tracking branch 'base/hip-unit-tests' into hip-unit-tests 2025-04-24 11:21:52 -07:00
Andrew Ho 823fd86a87 forgot about cmake 2025-04-24 11:18:47 -07:00
Andrew Ho 2d3c1bc79a Merge remote-tracking branch 'base/hip-unit-tests' into hip-unit-tests 2025-04-24 10:48:26 -07:00
Andrew Ho 1d556b93e7 more gpu unit tests 2025-04-24 10:47:57 -07:00
Andrew Ho 00fcb1b37f removed cuda/hip-specific unit test executables 2025-04-24 09:55:55 -07:00
Andrew Ho 9445358bc9 Merge branch 'master' into hip-unit-tests 2025-04-24 09:32:32 -07:00
Andrew Ho e4becc6e02 Merge branch 'master' into hip-unit-tests 2025-04-22 15:28:02 -07:00
Tom Stitt 9b1c3b718d remove check 2025-04-22 12:59:25 -07:00
Tom Stitt e9826f9c69 Adds MFEM_FOREACH_THREAD_DIRECT which uses a conditional instead of a loop for faster GPU kernels when the thread loop bound is less-than-or-equal-to the corresponding block size 2025-04-22 12:53:25 -07:00
Andrew Ho 783136234a Merge branch 'master' into hip-unit-tests 2025-04-13 19:35:44 -07:00
Andrew Ho dd85ae3384 Merge remote-tracking branch 'base/master' into hip-unit-tests 2025-04-07 11:54:06 -07:00
Andrew Ho 294a71c705 Merge remote-tracking branch 'base/hip-unit-tests' into hip-unit-tests 2025-04-04 11:58:14 -07:00
Andrew Ho 0b632bf3d4 Merge remote-tracking branch 'base/master' into hip-unit-tests 2025-04-04 11:39:35 -07:00
Veselin Dobrev 6e7718db30 Optimizations for class DeviceConformingProlongationOperator 2025-04-03 10:07:28 -07:00
Andrew Ho fabce12b76 Merge branch 'master' into hip-unit-tests 2025-03-24 23:55:09 -07:00
Andrew Ho 8fbfdb19fe added general GPU and CUDA/HIP-specific unit tests
added gpu, raja-gpu, etc. for generic GPU device configuration
2025-03-24 13:23:43 -07:00
102 changed files with 3228 additions and 1312 deletions
+154
View File
@@ -0,0 +1,154 @@
# 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.
---
name: Sanitizer Config
description: Sets up environment variables for MFEM sanitizer workflow
inputs:
DEBUG:
description: If true, use intermediate caches to speed up the workflow
by reusing previous builds.
default: false
REPOSITORY:
description: Repository to checkout
default: mfem/mfem
BRANCH:
description: Branch to checkout
default: ubsan
CLANG_VER:
description: CLANG version to use
default: 18
# https://github.com/llvm/llvm-project/releases
LLVM_VER:
description: LLVM version to use
default: 19.1.7
# https://github.com/hypre-space/hypre/releases
HYPRE_VER:
description: HYPRE version to use
default: 2.19.0
METIS_VER:
description: METIS version to use
default: 4.0.3
CTEST:
description: CTest command to use
default: ctest -j --test-load $(nproc)
--schedule-random
--stop-on-failure --output-on-failure
--test-dir
# https://clang.llvm.org/docs/AddressSanitizer.html
ASAN_OPTIONS:
default: detect_leaks=1,
strict_init_order=1,
strict_string_checks=1,
check_initialization_order=1,
detect_stack_use_after_return=1
ASAN_CXXFLAGS:
default: -fsanitize=address
-fsanitize-address-use-after-scope
ASAN_LDFLAGS:
default: -fsanitize=address
# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
UBSAN_OPTIONS:
default: halt_on_error=1, print_stacktrace=1
UBSAN_CXXFLAGS:
default: -fsanitize=undefined
UBSAN_LDFLAGS:
default: -fsanitize=undefined
# https://clang.llvm.org/docs/MemorySanitizer.html
MSAN_OPTIONS:
default: "poison_in_dtor=1"
MSAN_CXXFLAGS:
default: -fsanitize=memory
-fsanitize-memory-track-origins
-fsanitize-memory-use-after-dtor
MSAN_LDFLAGS:
default: -fsanitize=memory
LSAN_DIR:
description: LSAN suppression directory
default: lsan
LSAN_FILE:
description: LSAN suppression file
default: lsan.supp
NO_FLAGS:
description: If true, do not set any CXXFLAGS or LDFLAGS.
default: false
runs:
using: 'composite'
steps:
- name: Env (Inputs)
run: |
echo DEBUG=${{inputs.DEBUG}} >> $GITHUB_ENV
echo REPOSITORY=${{inputs.REPOSITORY}} >> $GITHUB_ENV
echo BRANCH=${{inputs.BRANCH}} >> $GITHUB_ENV
echo CLANG_VER=${{inputs.CLANG_VER}} >> $GITHUB_ENV
echo LLVM_VER=${{inputs.LLVM_VER}} >> $GITHUB_ENV
echo HYPRE_VER=${{inputs.HYPRE_VER}} >> $GITHUB_ENV
echo METIS_VER=${{inputs.METIS_VER}} >> $GITHUB_ENV
echo CTEST=${{inputs.CTEST}} >> $GITHUB_ENV
echo ASAN_OPTIONS=${{inputs.ASAN_OPTIONS}} >> $GITHUB_ENV
echo UBSAN_OPTIONS=${{inputs.UBSAN_OPTIONS}} >> $GITHUB_ENV
echo MSAN_OPTIONS=${{inputs.MSAN_OPTIONS}} >> $GITHUB_ENV
echo LSAN_DIR=${{inputs.LSAN_DIR}} >> $GITHUB_ENV
echo LSAN_FILE=${{inputs.LSAN_FILE}} >> $GITHUB_ENV
echo ASAN_CXXFLAGS=${{inputs.ASAN_CXXFLAGS}} >> $GITHUB_ENV
echo ASAN_LDFLAGS=${{inputs.ASAN_LDFLAGS}} >> $GITHUB_ENV
echo UBSAN_CXXFLAGS=${{inputs.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
echo UBSAN_LDFLAGS=${{inputs.UBSAN_LDFLAGS}} >> $GITHUB_ENV
echo MSAN_CXXFLAGS=${{inputs.MSAN_CXXFLAGS}} >> $GITHUB_ENV
echo MSAN_LDFLAGS=${{inputs.MSAN_LDFLAGS}} >> $GITHUB_ENV
shell: bash
- name: Env (dir)
run: |
echo LLVM_DIR=${{github.workspace}}/llvm >> $GITHUB_ENV
echo HYPRE_DIR=hypre-${{inputs.HYPRE_VER}} >> $GITHUB_ENV
echo METIS_DIR=metis-${{inputs.METIS_VER}} >> $GITHUB_ENV
shell: bash
- name: Env (bis)
run: |
echo CC=clang-${{inputs.CLANG_VER}} >> $GITHUB_ENV
echo CXX=clang++-${{inputs.CLANG_VER}} >> $GITHUB_ENV
echo LLVM_INC=${{env.LLVM_DIR}}/include/c++/v1 >> $GITHUB_ENV
echo LLVM_LIB=${{env.LLVM_DIR}}/lib >> $GITHUB_ENV
echo HYPRE_TGZ=v${{inputs.HYPRE_VER}}.tar.gz >> $GITHUB_ENV
echo METIS_TGZ=metis-${{inputs.METIS_VER}}.tar.gz >> $GITHUB_ENV
LSAN_SUPPRESSIONS="${{github.workspace}}/${{inputs.LSAN_DIR}}/${{inputs.LSAN_FILE}}"
echo "LSAN_OPTIONS=suppressions=$LSAN_SUPPRESSIONS" >> $GITHUB_ENV
shell: bash
- name: Env (ter)
if: ${{ inputs.NO_FLAGS != 'true' }}
run: |
echo LLVM_CXXFLAGS=-stdlib=libc++ -I${{env.LLVM_INC}} -Isystem${{env.LLVM_INC}} >> $GITHUB_ENV
echo LLVM_LDFLAGS=-L${{env.LLVM_LIB}} -lc++abi -Wl,-rpath,${{env.LLVM_LIB}} >> $GITHUB_ENV
shell: bash
- name: Env (quater)
if: ${{ inputs.NO_FLAGS != 'true' }}
run: |
echo CXXFLAGS=${{env.LLVM_CXXFLAGS}} >> $GITHUB_ENV
echo LDFLAGS=${{env.LLVM_LDFLAGS}} >> $GITHUB_ENV
shell: bash
+91
View File
@@ -0,0 +1,91 @@
# 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.
---
name: 'MFEM Compilation'
description: 'MFEM Compilation'
inputs:
par:
description: 'Whether to build for parallel (true/false)'
default: false
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
default: asan
runs:
using: 'composite'
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
if: ${{env.DEBUG == 'true'}}
id: debug
with:
path: mfem/build
key: build-${{inputs.par}}-${{inputs.sanitizer}}
- uses: ./.github/actions/sanitize/setup
if: ${{steps.debug.outputs.cache-hit != 'true'}}
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
- name: Build with ASAN
if: inputs.sanitizer == 'asan'
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.ASAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- name: Build with MSAN
if: inputs.sanitizer == 'msan'
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MSAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- name: Build with UBSAN
if: inputs.sanitizer == 'ubsan'
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- uses: mfem/github-actions/build-mfem@v2.5
if: ${{steps.debug.outputs.cache-hit != 'true'}}
env:
CXXFLAGS: ${{env.CXXFLAGS}}
LDFLAGS: ${{env.LDFLAGS}}
with:
mpi: ${{inputs.par == 'false' && 'seq' || 'par'}}
mfem-dir: mfem
os: ${{runner.os}}
library-only: true
build-system: cmake
hypre-dir: ${{env.HYPRE_DIR}}
metis-dir: ${{env.METIS_DIR}}
config-options: >-
-GNinja
-DMPICXX=${{env.CXX}}
-DCMAKE_CXX_STANDARD=17
-DMFEM_USE_MEMALLOC=OFF
-DCMAKE_BUILD_TYPE=Release
-DCMAKE_VERBOSE_MAKEFILE=ON
-DCMAKE_CXX_COMPILER=${{env.CXX}}
-DCMAKE_CXX_FLAGS_RELEASE='-g -O1 -fno-omit-frame-pointer'
- name: Delete object files
if: ${{steps.debug.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: find . -type f -name '*.o' -delete
shell: bash
- uses: actions/upload-artifact@v4
with:
name: build-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build
if-no-files-found: error
retention-days: 1
overwrite: false
+33
View File
@@ -0,0 +1,33 @@
# 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.
---
name: 'Install MPI'
description: 'Installs MPI and set up its environment variables'
runs:
using: 'composite'
steps:
- name: Install
run: sudo apt-get install openmpi-bin libopenmpi-dev
shell: bash
- name: Env
run: |
echo PRTE_MCA_rmaps_default_mapping_policy=:oversubscribe >> $GITHUB_ENV
echo MPI_INC=$(mpicxx --showme:compile) >> $GITHUB_ENV
echo MPI_LIB=$(mpicxx --showme:link) >> $GITHUB_ENV
shell: bash
- name: Env (bis)
run: |
echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MPI_INC}} >> $GITHUB_ENV
echo LDFLAGS=${{env.LDFLAGS}} ${{env.MPI_LIB}} >> $GITHUB_ENV
shell: bash
@@ -0,0 +1,71 @@
# 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.
---
name: 'Restore state'
description: 'Restore state to be able to run checks, tests'
inputs:
par:
description: 'Whether to build for parallel (true/false)'
default: false
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
default: asan
cache-path:
description: 'path to what needs to be restored'
default: none
cache-skip:
description: 'Skip cache restoration'
default: false
outputs:
cache-hit:
description: 'Output from a specific step'
value: ${{steps.debug.outputs.cache-hit}}
runs:
using: 'composite'
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
id: debug
with:
path: ${{inputs.cache-path}}
key: ${{github.job}}-${{inputs.par}}-${{inputs.sanitizer}}
- uses: ./.github/actions/sanitize/setup
if: ${{steps.debug.outputs.cache-hit != 'true'}}
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
- uses: actions/download-artifact@v4
with:
name: build-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build
- name: Ninja Patch
working-directory: mfem/build
run: |
sed -i -e 's/CXX_STATIC_LIBRARY_LINKER__mfem_Release.*/CUSTOM_COMMAND/' build.ninja
sed -i -e '/build tests\/unit\/all:/ s/tests\/unit\/[^ ]*unit_tests[^ ]*//g' build.ninja
sed -i -e '/^add_test(\[=\[\(unit_tests\|punit_tests\)\]=\]/ s/)/ "--input-file .\/list-test-names-${{matrix.tag}}" "--min-duration 1")/' tests/unit/CTestTestfile.cmake
shell: bash
- name: Copy Data
if: ${{steps.debug.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
ninja cmake_object_order_depends_target_unit_tests
cp -pR ../tests/unit/data tests/unit
shell: bash
+64
View File
@@ -0,0 +1,64 @@
# 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.
---
name: 'Setup state'
description: 'Sets up the state to be able to run build & run'
inputs:
par:
description: 'Whether to build for parallel (true/false)'
default: false
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
default: asan
runs:
using: 'composite'
steps:
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
with:
path: ${{env.LLVM_DIR}}
fail-on-cache-miss: true
key: build-libcxx-${{env.LLVM_VER}}-${{inputs.sanitizer}}
- uses: ./.github/actions/sanitize/mpi
if: ${{inputs.par == 'true'}}
- uses: actions/cache/restore@v4 # Cache for Hypre
if: ${{inputs.par == 'true'}}
with:
path: ${{env.HYPRE_DIR}}
fail-on-cache-miss: true
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
- uses: actions/cache/restore@v4 # Cache for Metis
if: ${{inputs.par == 'true'}}
with:
path: ${{env.METIS_DIR}}
fail-on-cache-miss: true
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
- name: Hypre/Metis links
if: ${{inputs.par == 'true'}}
run: ln -s -f ${{env.HYPRE_DIR}} hypre && ln -s -f ${{env.METIS_DIR}} metis-4.0
shell: bash
- uses: actions/cache/restore@v4 # Cache for LSAN suppression file
with:
path: ${{env.LSAN_DIR}}
fail-on-cache-miss: true
key: build-lsan-suppression-file
- uses: actions/checkout@v4 # Checkout the repository
with:
path: mfem
# ref: ${{env.BRANCH}}
# repository: ${{env.REPOSITORY}}
+26 -7
View File
@@ -7,18 +7,17 @@
https://mfem.org
This directory contains the GitHub CI scripts for MFEM.
Note that some of these scripts use the shared MFEM GitHub Actions from the external mfem/github-actions repository:
https://github.com/mfem/github-actions
<https://github.com/mfem/github-actions>
For a particular action, e.g. `mfem/github-actions/build-mfem@v2.1`, the `v2.1` 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 in the above from which the action is taken.
The current CI workflows are:
### `repo-check.yml`
## `repo-check.yml`
Runs a number of static repository-level sanity checks.
@@ -30,19 +29,39 @@ Runs a number of static repository-level sanity checks.
- `branch-history` guards against accidental commits of large files using the `--history` option of the `config/githooks/pre-push` script.
### `mfem-analysis.yml` (`build-analysis`)
## `mfem-analysis.yml` (`build-analysis`)
Checks if the code builds and satisfies minimal requirements.
- `gitignore` builds hypre, METIS, and MFEM using `mfem/github-actions/build-hypre`, `mfem/github-actions/build-metis`, and `mfem/github-actions/build-mfem` and checks for correct `.gitignore` settings by running the `tests/scripts/gitignore` script.
### `builds-and-tests.yml`
## `builds-and-tests.yml`
Runs a matrix of builds and tests runs with different compilers, OS, mfem/hypre settings, etc. Also processes and upload Codecov reports.
Uses the following GitHub Actions from https://github.com/mfem/github-actions:
Uses the following GitHub Actions from <https://github.com/mfem/github-actions>:
- `mfem/github-actions/build-hypre`
- `mfem/github-actions/build-metis`
- `mfem/github-actions/build-mfem`
- `mfem/github-actions/upload-coverage`
## Sanitizer Workflow for MFEM Verification
This workflow validates MFEM unit tests, examples, and miniapps using sanitizer tools.
- `sanitizers.yml` orchestrates:
- Building and caching dependencies: HYPRE, METIS, LSAN suppression file, and LLVM libcxx.
- Launching fine-grained jobs for serial (ASAN, MSAN, UBSAN) and parallel (ASAN, UBSAN) sanitizers.
- `sanitize-tests.yml` is a reusable workflow accepting `par` mode (`true` for parallel) and `sanitizer` (ASAN, MSAN, or UBSAN) as inputs. It executes the following jobs:
- **Build**: Compiles the MFEM library with specified parallel and sanitizer settings.
- **Check**: Runs verification checks.
- Parallel jobs to test the following: **Examples**, **Miniapps** and **Unit tests**
The workflow leverages composite actions in `.github/actions/sanitize/`:
- `config`: Centralizes settings for the sanitizer workflow.
- `mfem`: Manages the MFEM library build process.
- `mpi`: Installs MPI and applies additional compilation flags.
- `restore`: Restores the testing environment state.
- `setup`: Builds or restores cached dependencies.
-69
View File
@@ -1,69 +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.
name: "Sanitizer"
permissions:
actions: write
on:
push:
branches:
- master
- next
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
Serial:
runs-on: ubuntu-24.04
steps:
- name: MFEM Checkout
uses: actions/checkout@v4
with:
path: mfem
- name: MFEM Build
uses: mfem/github-actions/build-mfem@v2.5
with:
os: ${{ runner.os }}
target: opt
mpi: seq
hypre-dir: unused-hypre-dir
metis-dir: unused-metis-dir
mfem-dir: mfem
build-system: make
library-only: false
config-options:
CXX="clang++-18"
CXXFLAGS="-g -O1 -std=c++17
-fsanitize=address
-fno-omit-frame-pointer
-fsanitize-address-use-after-scope"
- name: MFEM Info
working-directory: mfem
run: make info
- name: MFEM Sanitize
working-directory: mfem
run:
ASAN_OPTIONS="detect_leaks=1,
strict_init_order=1,
strict_string_checks=1,
check_initialization_order=1,
detect_stack_use_after_return=1"
make test
@@ -0,0 +1,39 @@
# 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.
---
name: build-hypre
on:
workflow_call:
jobs:
build-hypre:
runs-on: ubuntu-latest
name: 2.19.0
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.HYPRE_DIR}}
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
uses: ./.github/actions/sanitize/mpi
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.5
with:
archive: ${{env.HYPRE_TGZ}}
dir: ${{env.HYPRE_DIR}}
target: int32
precision: fp64
build-system: make
@@ -0,0 +1,76 @@
# 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.
---
name: build-libcxx
on:
workflow_call:
jobs:
build-llvm-libcxx:
runs-on: ubuntu-latest
strategy:
matrix:
sanitizer: [asan, msan, ubsan]
include:
- sanitizer: asan
llvm_use_sanitizer: "Address"
- sanitizer: msan
llvm_use_sanitizer: "MemoryWithOrigins"
- sanitizer: ubsan
llvm_use_sanitizer: "Undefined"
name: ${{matrix.sanitizer}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
with:
NO_FLAGS: true
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.LLVM_DIR}}
key: build-libcxx-${{env.LLVM_VER}}-${{matrix.sanitizer}}
- name: Clone
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: >
git clone --filter=blob:none --depth=1
--branch llvmorg-${{env.LLVM_VER}}
--no-checkout https://github.com/llvm/llvm-project.git llvm-project
- name: Checkout
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
working-directory: llvm-project
run: |
git sparse-checkout set --cone
git checkout llvmorg-${{env.LLVM_VER}}
git sparse-checkout set cmake llvm/cmake runtimes libcxx libcxxabi
- name: Mkdir
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
run: mkdir ${{env.LLVM_DIR}}
- name: CMake
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
working-directory: ${{env.LLVM_DIR}}
run: >
VERBOSE=1
cmake -GNinja ../llvm-project/runtimes/
-DCMAKE_C_COMPILER=${{env.CC}}
-DCMAKE_CXX_COMPILER=${{env.CXX}}
-DCMAKE_BUILD_TYPE=RelWithDebInfo
-DCMAKE_INSTALL_PREFIX=/usr
-DLLVM_USE_SANITIZER=${{matrix.llvm_use_sanitizer}}
-DLLVM_BUILD_32_BITS=OFF
-DLIBCXXABI_USE_LLVM_UNWINDER=OFF
-DLLVM_INCLUDE_TESTS=OFF
-DLIBCXX_INCLUDE_TESTS=OFF
-DLIBCXX_INCLUDE_BENCHMARKS=OFF
-DLLVM_ENABLE_RUNTIMES='libcxx;libcxxabi'
- name: Build
if: ${{ steps.cache.outputs.cache-hit != 'true' }}
working-directory: ${{env.LLVM_DIR}}
run: cmake --build . -- cxx cxxabi
+38
View File
@@ -0,0 +1,38 @@
# 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.
---
name: build-file-lsan
on:
workflow_call:
jobs:
build-file-lsan:
runs-on: ubuntu-latest
name: lsan.supp
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.LSAN_DIR}}
key: build-lsan-suppression-file
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
run: |
mkdir -p ${{env.LSAN_DIR}}
cat << EOF > ${{env.LSAN_DIR}}/${{env.LSAN_FILE}}
leak:libevent_core-2.1.so
leak:ompi_mpi_finalize
leak:ompi_mpi_init
leak:PMPI_Init
leak:strdup
EOF
@@ -0,0 +1,36 @@
# 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.
---
name: build-metis
on:
workflow_call:
jobs:
build-metis:
runs-on: ubuntu-latest
name: 4.0.3
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
with:
path: ${{env.METIS_DIR}}
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
uses: ./.github/actions/sanitize/mpi
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.5
with:
archive: ${{env.METIS_TGZ}}
dir: ${{env.METIS_DIR}}
+197
View File
@@ -0,0 +1,197 @@
# 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.
---
name: Sanitize
on:
workflow_call:
inputs:
par:
description: 'Whether to build for parallel (true/false)'
required: false
default: false
type: boolean
sanitizer:
description: 'Sanitizer to use (asan, msan, ubsan)'
required: true
default: asan
type: string
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/mfem
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
check:
needs: [build]
runs-on: ubuntu-latest
env:
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/examples/${{env.ex}}
- name: MFEM Check
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v check
examples:
needs: [check]
runs-on: ubuntu-latest
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/examples/ex1
- name: Build Examples
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v examples
- name: Test Examples
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} examples ${{env.exclude}} --show-only
${{env.CTEST}} examples ${{env.exclude}}
miniapps:
needs: [check]
runs-on: ubuntu-latest
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/miniapps/meshing/minimal-surface
- name: Build Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v miniapps
- name: Test Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} miniapps ${{env.exclude}} --show-only
${{env.CTEST}} miniapps ${{env.exclude}}
tests-miniapps:
needs: [check]
runs-on: ubuntu-latest
env:
run: ${{inputs.par && '-R "_cpu_np"' || ''}}
exclude: ${{inputs.par && '"unit_tests|debug"' || '"^unit_tests$|debug"'}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/tests/unit/sedov_tests_cpu
- name: Build Tests Unit Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v tests/unit/all
- name: Run Tests Unit Miniapps
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} tests/unit -E ${{env.exclude}} ${{env.run}} --show-only
${{env.CTEST}} tests/unit -E ${{env.exclude}} ${{env.run}}
tests-unit-build:
needs: [check]
runs-on: ubuntu-latest
env:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/tests/unit/${{env.unit_tests}}
- name: Build Unit Tests
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: ninja -v ${{env.unit_tests}}
- name: Delete object files
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
with:
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build/tests/unit/${{env.unit_tests}}
if-no-files-found: error
retention-days: 1
overwrite: false
tests-unit-run:
needs: [tests-unit-build]
runs-on: ubuntu-latest
strategy:
matrix:
tag: [0, 1, 2, 3]
name: tests-unit-run-${{matrix.tag}}
env:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
np: ${{inputs.par && '_np=2' || ''}}
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/sanitize/restore
id: restore
with:
par: ${{inputs.par}}
sanitizer: ${{inputs.sanitizer}}
cache-path: mfem/build/tests/unit/${{env.unit_tests}}
- uses: actions/download-artifact@v4
if: ${{steps.restore.outputs.cache-hit != 'true'}}
with:
name: tests-${{inputs.par}}-${{inputs.sanitizer}}
path: mfem/build/tests/unit
- name: Split Unit Tests
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build/tests/unit
run: |
chmod 755 ${{env.unit_tests}}
./${{env.unit_tests}} --list-test-names-only | tail -n +2 > list-test-names
shuf list-test-names -o list-test-names
split --verbose -n l/4 -d -a 1 list-test-names list-test-names-
- name: Cat Unit Tests ${{matrix.tag}}
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build/tests/unit
run: cat list-test-names-${{matrix.tag}}
- name: Run Unit Tests ${{matrix.tag}}
if: ${{steps.restore.outputs.cache-hit != 'true'}}
working-directory: mfem/build
run: |
${{env.CTEST}} tests/unit -R "${{env.unit_tests}}${{env.np}}" --show-only
${{env.CTEST}} tests/unit -R "${{env.unit_tests}}${{env.np}}"
+73
View File
@@ -0,0 +1,73 @@
# 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.
---
name: Sanitizers
permissions:
actions: write
on:
push:
branches: ["master", "next"]
pull_request:
workflow_dispatch:
concurrency:
group: ${{github.workflow}}-${{github.ref}}
cancel-in-progress: true
jobs:
# Build steps for dependencies
build-hypre:
uses: ./.github/workflows/sanitize-build-hypre.yml
build-metis:
uses: ./.github/workflows/sanitize-build-metis.yml
build-lsan:
uses: ./.github/workflows/sanitize-build-lsan.yml
build-libcxx:
uses: ./.github/workflows/sanitize-build-libcxx.yml
# Serial sanitizers: asan, msan, ubsan
seq-asan:
needs: [build-libcxx]
uses: ./.github/workflows/sanitize-tests.yml
with:
sanitizer: asan
seq-msan:
needs: [build-libcxx]
uses: ./.github/workflows/sanitize-tests.yml
with:
sanitizer: msan
seq-ubsan:
needs: [build-libcxx]
uses: ./.github/workflows/sanitize-tests.yml
with:
sanitizer: ubsan
# Parallel sanitizers: asan, ubsan
par-asan:
needs: [build-libcxx, build-hypre, build-metis]
uses: ./.github/workflows/sanitize-tests.yml
with:
par: true
sanitizer: asan
par-ubsan:
needs: [build-libcxx, build-hypre, build-metis]
uses: ./.github/workflows/sanitize-tests.yml
with:
par: true
sanitizer: ubsan
+4 -2
View File
@@ -300,6 +300,7 @@ miniapps/nurbs/nurbs_solenoidal
miniapps/nurbs/nurbs_printfunc
miniapps/nurbs/nurbs_patch_ex1
miniapps/nurbs/nurbs_curveint
miniapps/nurbs/nurbs_surface
miniapps/nurbs/refined.mesh
miniapps/nurbs/mesh.*
miniapps/nurbs/sol_?.gf
@@ -318,6 +319,7 @@ miniapps/nurbs/nurbs_naca_cmesh
miniapps/nurbs/naca-cmesh.mesh
miniapps/nurbs/glvis_naca-cmesh.mesh
miniapps/nurbs/Naca_cmesh
miniapps/nurbs/*-Surface.mesh
miniapps/performance/ex1
miniapps/performance/ex1p
@@ -413,8 +415,8 @@ miniapps/diag-smoothers/mg-abs-l1-jacobi
tests/unit/output_meshes
tests/unit/unit_tests
tests/unit/punit_tests
tests/unit/cunit_tests
tests/unit/pcunit_tests
tests/unit/gpu_unit_tests
tests/unit/pgpu_unit_tests
tests/unit/sedov_tests_*
tests/unit/psedov_tests_*
tests/unit/tmop_pa_tests_*
+21 -1
View File
@@ -29,9 +29,14 @@ Discretization improvements
Meshing improvements
--------------------
- Added support for higher order meshes in Mesh::MakeSimplicial and
ParMesh::MakeSimplicial.
- Added a new miniapp for interpolating a surface grid of points in 3D using a
smooth NURBS surface, that can then be sampled at arbitrary resolution while
staying close to the original geometry. See miniapps/nurbs/nurbs_surface.
GPU computing
-------------
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
@@ -39,6 +44,8 @@ GPU computing
set. This is most often used for setting constant essential boundary
conditions. A new function Vector::SetSubVectorHost has been added in cases
where host execution is always needed (e.g. when the DOFs array is small).
- Introduced MFEM_FOREACH_THREAD_DIRECT, which directly maps loop tasks to GPU
threads, assigning one task per thread.
New and updated examples and miniapps
-------------------------------------
@@ -48,13 +55,26 @@ New and updated examples and miniapps
operators as smoothers.
These miniapps can be found in `miniapps/diag-smoothers`.
API changes:
API changes
-----------
- mfem::internal::tensor and mfem::internal::dual have been moved to
mfem::future::tensor and mfem::future::dual.
- API addition: in class `Operator`, added virtual functions: `AbsMult`, and
`AbsMultTranspose`; in class `Vector`, added `Abs` and `Pow`.
Miscellaneous
-------------
- Added the "gpu", "raja-gpu", and "ceed-gpu" backend aliases/shortcuts which
automatically select between CUDA or HIP.
- The CUDA-specific names used by some of the unit tests like 'cunit_tests' and
'pcunit_tests' were replaced by names using 'gpu' instead of 'c' (short for
CUDA) or 'cuda'. These tests automatically run the CUDA/HIP tests based on the
MFEM build configuration.
- Added the option to enable GPU-aware MPI in MFEM using the environment
variable 'MFEM_GPU_AWARE_MPI' set to any value. Setting this environment
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.
- Added parallel Address Sanitizer, serial and parallel Undefined Behavior
Sanitizer and serial Memory Sanitizer GitHub actions tests on Ubuntu.
Version 4.8, released on Apr 9, 2025
====================================
+1
View File
@@ -247,6 +247,7 @@ set(HDRS
nonlinearform_ext.hpp
nonlininteg.hpp
qfunction.hpp
qinterp/det.hpp
qinterp/eval.hpp
qinterp/eval_hdiv.hpp
qinterp/grad.hpp
+1
View File
@@ -515,6 +515,7 @@ struct InvTNewtonSolver<Geometry::SEGMENT, SDim, SType, max_team_x>
phys_tol += pptr[idx + d * npts] * pptr[idx + d * npts];
}
phys_tol = fmax(phys_rtol * phys_rtol, phys_tol * phys_rtol * phys_rtol);
hit_bdr[0] = prev_hit_bdr[0] = false;
}
// for each iteration
while (true)
+12 -10
View File
@@ -764,9 +764,9 @@ ParaViewDataCollectionBase::ParaViewDataCollectionBase(
{
cycle = 0;
#ifdef MFEM_USE_ZLIB
compression = true; // if we have zlib, enable compression
#else
compression = false; // otherwise, disable compression
// If we have zlib, enable compression. Otherwise, compression is disabled in
// the DataCollection base class constructor.
compression = true;
#endif
}
@@ -784,13 +784,8 @@ void ParaViewDataCollectionBase::SetCompressionLevel(int compression_level_)
{
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
"Compression level must be between -1 and 9 (inclusive).");
if (compression_level_ != 0) { SetCompression(true);}
compression_level = compression_level_;
compression = compression_level_ != 0;
}
void ParaViewDataCollectionBase::SetCompression(bool compression_)
{
compression = compression_;
}
int ParaViewDataCollectionBase::GetCompressionLevel() const
@@ -1174,7 +1169,14 @@ const char *ParaViewDataCollection::GetDataTypeString() const
ParaViewHDFDataCollection::ParaViewHDFDataCollection(
const std::string &collection_name, Mesh *mesh)
: ParaViewDataCollectionBase(collection_name, mesh)
{ }
{
compression = true;
}
void ParaViewHDFDataCollection::SetCompression(bool compression_)
{
compression = compression_;
}
void ParaViewHDFDataCollection::EnsureVTKHDF()
{
+6 -7
View File
@@ -537,13 +537,6 @@ public:
/// Any nonzero compression level will enable compression.
void SetCompressionLevel(int compression_level_);
/// @brief Enable or disable zlib compression.
///
/// If the input is true, use the default zlib compression level (unless the
/// compression level has previously been set by calling
/// SetCompressionLevel()).
void SetCompression(bool compression_) override;
/// @brief Sets whether or not to output the data as high-order elements
/// (false by default).
///
@@ -633,6 +626,12 @@ public:
ParaViewHDFDataCollection(const std::string& collection_name,
Mesh *mesh_ = nullptr);
/// @brief Enable or disable compression.
///
/// The compression level can be set with SetCompressionLevel()). VTKHDF
/// compression does not require MFEM to be compiled with zlib support.
void SetCompression(bool compression_) override;
/// Save the collection.
void Save() override;
+1
View File
@@ -241,6 +241,7 @@ public:
{
MFEM_ASSERT(!action_callbacks.empty(), "no integrators have been set");
prolongation(solutions, solutions_t, solutions_l);
residual_l = 0.0;
for (auto &action : action_callbacks)
{
action(solutions_l, parameters_l, residual_l);
+2 -1
View File
@@ -101,10 +101,11 @@ public:
// Setup DofToQuad information
dtq.nqpt = (int)floor(std::pow(ir.GetNPoints(), 1.0 / mesh.Dimension()) + 0.5);
dtq.ndof = dtq.nqpt;
dtq.mode = used_in_tensor_product ? DofToQuad::TENSOR : DofToQuad::FULL;
// Calculate sizes
const int num_qp = used_in_tensor_product ?
std::pow(dtq.nqpt, mesh.Dimension()) :
static_cast<int>(std::pow(dtq.nqpt, mesh.Dimension())) :
ir.GetNPoints();
tsize = vdim * num_qp * mesh.GetNE();
+9 -8
View File
@@ -987,7 +987,7 @@ get_restriction_transpose(
{
auto RT = [=](const Vector &v_e, Vector &v_l)
{
v_l = v_e;
v_l += v_e;
};
return std::make_tuple(RT, 1);
}
@@ -996,7 +996,7 @@ get_restriction_transpose(
const Operator *R = get_restriction<entity_t>(f, o);
std::function<void(const Vector&, Vector&)> RT = [=](const Vector &x, Vector &y)
{
R->MultTranspose(x, y);
R->AddMultTranspose(x, y);
};
return std::make_tuple(RT, R->Height());
}
@@ -1702,12 +1702,13 @@ std::array<DofToQuadMap, N> load_dtq_mem(
std::array<DofToQuadMap, N> f;
for (std::size_t i = 0; i < N; i++)
{
const auto [nqp_b, dim_b, ndof_b] = dtq[i].B.GetShape();
const auto B = Reshape(&dtq[i].B[0], nqp_b, dim_b, ndof_b);
auto mem_Bi = Reshape(reinterpret_cast<real_t *>(mem) + offset, nqp_b, dim_b,
ndof_b);
if (dtq[i].which_input != -1)
{
const auto [nqp_b, dim_b, ndof_b] = dtq[i].B.GetShape();
const auto B = Reshape(&dtq[i].B[0], nqp_b, dim_b, ndof_b);
auto mem_Bi = Reshape(reinterpret_cast<real_t *>(mem) + offset, nqp_b, dim_b,
ndof_b);
MFEM_FOREACH_THREAD(q, x, nqp_b)
{
MFEM_FOREACH_THREAD(d, y, ndof_b)
@@ -2158,7 +2159,7 @@ template <
std::size_t... Is>
std::array<DofToQuadMap, N> create_dtq_maps_impl(
field_operator_ts &fops,
std::vector<const DofToQuad*> dtqs,
std::vector<const DofToQuad*> &dtqs,
const std::array<int, N> &field_map,
std::index_sequence<Is...>)
{
@@ -2243,7 +2244,7 @@ template <
std::size_t num_fields>
std::array<DofToQuadMap, num_fields> create_dtq_maps(
field_operator_ts &fops,
std::vector<const DofToQuad*> dtqmaps,
std::vector<const DofToQuad*> &dtqmaps,
const std::array<int, num_fields> &to_field_map)
{
return create_dtq_maps_impl<entity_t>(
+1 -1
View File
@@ -4334,7 +4334,7 @@ real_t LSZZErrorEstimator(BilinearFormIntegrator &blfi, // input
u.GetSubVector(udofs, ul);
utrans.InvTransformPrimal(ul);
Transf = ufes->GetElementTransformation(ielem);
FiniteElement *dummy = nullptr;
const auto *dummy = ufes->GetFE(ielem);
blfi.ComputeElementFlux(*ufes->GetFE(ielem), *Transf, ul,
*dummy, fl, with_coeff, ir);
+26 -25
View File
@@ -1009,6 +1009,7 @@ inline void SmemPADiffusionApply3D(const int NE,
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_VERIFY(D1D <= Q1D, "THREAD_DIRECT requires D1D <= Q1D");
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
@@ -1038,11 +1039,11 @@ inline void SmemPADiffusionApply3D(const int NE,
real_t (*QDD0)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+0);
real_t (*QDD1)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+1);
real_t (*QDD2)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+2);
MFEM_FOREACH_THREAD(dz,z,D1D)
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
{
X[dz][dy][dx] = x(dx,dy,dz,e);
}
@@ -1050,9 +1051,9 @@ inline void SmemPADiffusionApply3D(const int NE,
}
if (MFEM_THREAD_ID(z) == 0)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
{
B[qx][dy] = b(qx,dy);
G[qx][dy] = g(qx,dy);
@@ -1060,11 +1061,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
{
real_t u = 0.0, v = 0.0;
MFEM_UNROLL(MD1)
@@ -1080,11 +1081,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MD1)
@@ -1101,11 +1102,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MD1)
@@ -1136,9 +1137,9 @@ inline void SmemPADiffusionApply3D(const int NE,
MFEM_SYNC_THREAD;
if (MFEM_THREAD_ID(z) == 0)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qx,x,Q1D)
{
Bt[dy][qx] = b(qx,dy);
Gt[dy][qx] = g(qx,dy);
@@ -1146,11 +1147,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
@@ -1167,11 +1168,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz,z,Q1D)
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(Q1D)
@@ -1188,11 +1189,11 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD_DIRECT(dx,x,D1D)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
+1 -1
View File
@@ -62,7 +62,7 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
const int NE = ne;
const int Q1D = quad1D;
const int NQ = pow(Q1D, dim);
const int NQ = static_cast<int>(std::pow(Q1D, dim));
const bool const_c = coeff.Size() == 1;
const bool by_val = map_type == FiniteElement::VALUE;
const auto W = Reshape(ir->GetWeights().Read(), NQ);
+1 -1
View File
@@ -673,7 +673,7 @@ public:
int myid;
MPI_Comm_rank(comm, &myid);
int seed = (seed_ > 0) ? seed_ + myid : (int)time(0) + myid;
int seed = (seed_ > 0) ? seed_ + myid : time(nullptr) + myid;
SetSeed(seed);
}
#else
+187 -64
View File
@@ -5259,7 +5259,7 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
gc.GetNeighborLTDofTable(nbr_ltdof);
const int nb_connections = nbr_ltdof.Size_of_connections();
shr_ltdof.SetSize(nb_connections);
shr_ltdof.CopyFrom(nbr_ltdof.GetJ());
if (nb_connections > 0) { shr_ltdof.CopyFrom(nbr_ltdof.GetJ()); }
shr_buf.SetSize(nb_connections);
shr_buf.UseDevice(true);
shr_buf_offsets = nbr_ltdof.GetIMemory();
@@ -5288,7 +5288,7 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
gc.GetNeighborLDofTable(nbr_ldof);
const int nb_connections = nbr_ldof.Size_of_connections();
ext_ldof.SetSize(nb_connections);
ext_ldof.CopyFrom(nbr_ldof.GetJ());
if (nb_connections > 0) { ext_ldof.CopyFrom(nbr_ldof.GetJ()); }
ext_ldof.GetMemory().UseDevice(true);
ext_buf.SetSize(nb_connections);
ext_buf.UseDevice(true);
@@ -5309,6 +5309,18 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
if (recv_size > 0) { req_counter++; }
}
requests = new MPI_Request[req_counter];
if (mpi_gpu_aware)
{
#if defined(MFEM_USE_HIP)
MFEM_GPU_CHECK(
hipEventCreateWithFlags(&gpu_event, hipEventDisableTiming));
#elif defined(MFEM_USE_CUDA)
MFEM_GPU_CHECK(
cudaEventCreateWithFlags(&gpu_event, cudaEventDisableTiming));
#else
MFEM_ABORT("not implemented");
#endif
}
}
DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
@@ -5322,16 +5334,27 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
}
static void ExtractSubVector(const Array<int> &indices,
const Vector &vin, Vector &vout)
const Vector &vin, Vector &vout,
real_t a, real_t b)
{
MFEM_ASSERT(indices.Size() == vout.Size(), "incompatible sizes!");
auto y = vout.Write();
auto y = (a == 0) ? vout.Write() : vout.ReadWrite();
const auto x = vin.Read();
const auto I = indices.Read();
mfem::forall(indices.Size(), [=] MFEM_HOST_DEVICE (int i)
if (a == 0)
{
y[i] = x[I[i]];
}); // indices can be repeated
mfem::forall(indices.Size(), [=] MFEM_HOST_DEVICE (int i)
{
y[i] = b*x[I[i]];
}); // indices can be repeated
}
else
{
mfem::forall(indices.Size(), [=] MFEM_HOST_DEVICE (int i)
{
y[i] = a*y[i] + b*x[I[i]];
}); // indices can be repeated
}
}
void DeviceConformingProlongationOperator::BcastBeginCopy(
@@ -5339,10 +5362,7 @@ void DeviceConformingProlongationOperator::BcastBeginCopy(
{
// shr_buf[i] = src[shr_ltdof[i]]
if (shr_ltdof.Size() == 0) { return; }
ExtractSubVector(shr_ltdof, x, shr_buf);
// If the above kernel is executed asynchronously, we should wait for it to
// complete
if (mpi_gpu_aware) { MFEM_STREAM_SYNC; }
ExtractSubVector(shr_ltdof, x, shr_buf, 0, 1);
}
static void SetSubVector(const Array<int> &indices,
@@ -5379,7 +5399,7 @@ void DeviceConformingProlongationOperator::Mult(const Vector &x,
Vector &y) const
{
const GroupTopology &gtopo = gc.GetGroupTopology();
int req_counter = 0;
int req_counter = 0, num_recv_req = 0;
// Make sure 'y' is marked as valid on device and for use on device.
// This ensures that there is no unnecessary host to device copy when the
// input 'y' is valid on host (in 'y.SetSubVector(ext_ldof, 0.0)' when local
@@ -5389,42 +5409,99 @@ void DeviceConformingProlongationOperator::Mult(const Vector &x,
{
// done on device since we've marked ext_ldof for use on device:
y.SetSubVector(ext_ldof, 0.0);
BcastLocalCopy(x, y);
return;
}
else
BcastBeginCopy(x); // copy to 'shr_buf'
if (shr_ltdof.Size() != 0)
{
BcastBeginCopy(x); // copy to 'shr_buf'
if (mpi_gpu_aware)
{
/* record a stream event to wait for later */
#if defined(MFEM_USE_HIP)
MFEM_GPU_CHECK(hipEventRecord(gpu_event, 0));
#elif defined(MFEM_USE_CUDA)
MFEM_GPU_CHECK(cudaEventRecord(gpu_event, 0));
#endif
}
else
{
// Wait for BcastBeginCopy() to finish and copy the result to host.
// We want to do the copy here, before BcastLocalCopy(), so we can
// start MPI sends while BcastLocalCopy() is running asyncronously.
shr_buf.HostRead();
}
}
BcastLocalCopy(x, y);
// Queue all receive communications
if (ext_ldof.Size() != 0) // ext_ldof.Size() == ext_buf.Size()
{
auto recv_buf = mpi_gpu_aware ? ext_buf.Write() : ext_buf.HostWrite();
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
const int recv_offset = ext_buf_offsets[nbr];
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
MPI_Irecv(recv_buf + recv_offset, recv_size,
MPITypeMap<real_t>::mpi_type,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
}
num_recv_req = req_counter;
}
// Queue all send communications
if (shr_ltdof.Size() != 0) // shr_ltdof.Size() == shr_buf.Size()
{
// The BcastBeginCopy kernel is executed asynchronously, we should wait
// for it to complete:
// - when mpi_gpu_aware == false, this was done implicily when we called
// shr_buf.HostRead() above
// - when mpi_gpu_aware == true, we need to wait for BcastBeginCopy to
// complete by waiting for gpu_event.
if (mpi_gpu_aware)
{
/* wait for the stream event recorded above */
#if defined(MFEM_USE_HIP)
MFEM_GPU_CHECK(hipEventSynchronize(gpu_event));
#elif defined(MFEM_USE_CUDA)
MFEM_GPU_CHECK(cudaEventSynchronize(gpu_event));
#endif
}
auto send_buf = mpi_gpu_aware ? shr_buf.Read() : shr_buf.HostRead();
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
const int send_offset = shr_buf_offsets[nbr];
const int send_size = shr_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
auto send_buf = mpi_gpu_aware ? shr_buf.Read() : shr_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPITypeMap<real_t>::mpi_type,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = ext_buf_offsets[nbr];
const int recv_size = ext_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? ext_buf.Write() : ext_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPITypeMap<real_t>::mpi_type,
MPI_Isend(send_buf + send_offset, send_size,
MPITypeMap<real_t>::mpi_type,
gtopo.GetNeighborRank(nbr), 41822,
gtopo.GetComm(), &requests[req_counter++]);
}
}
}
BcastLocalCopy(x, y);
if (!local)
{
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
BcastEndCopy(y); // copy from 'ext_buf'
}
// Wait for all receive requests
MPI_Waitall(num_recv_req, requests, MPI_STATUSES_IGNORE);
BcastEndCopy(y); // copy from 'ext_buf'
// Wait for all send requests
MPI_Waitall(req_counter - num_recv_req, requests + num_recv_req,
MPI_STATUSES_IGNORE);
}
DeviceConformingProlongationOperator::~DeviceConformingProlongationOperator()
{
if (mpi_gpu_aware)
{
#if defined(MFEM_USE_HIP)
MFEM_GPU_CHECK(hipEventDestroy(gpu_event));
#elif defined(MFEM_USE_CUDA)
MFEM_GPU_CHECK(cudaEventDestroy(gpu_event));
#endif
}
delete [] requests;
ext_buf_offsets.Delete();
shr_buf_offsets.Delete();
@@ -5435,25 +5512,23 @@ void DeviceConformingProlongationOperator::ReduceBeginCopy(
{
// ext_buf[i] = src[ext_ldof[i]]
if (ext_ldof.Size() == 0) { return; }
ExtractSubVector(ext_ldof, x, ext_buf);
// If the above kernel is executed asynchronously, we should wait for it to
// complete
if (mpi_gpu_aware) { MFEM_STREAM_SYNC; }
ExtractSubVector(ext_ldof, x, ext_buf, 0, 1);
}
void DeviceConformingProlongationOperator::ReduceLocalCopy(
const Vector &x, Vector &y) const
const Vector &x, Vector &y, real_t a, real_t b) const
{
// dst[i] = src[ltdof_ldof[i]]
if (ltdof_ldof.Size() == 0) { return; }
ExtractSubVector(ltdof_ldof, x, y);
ExtractSubVector(ltdof_ldof, x, y, a, b);
}
static void AddSubVector(const Array<int> &unique_dst_indices,
const Array<int> &unique_to_src_offsets,
const Array<int> &unique_to_src_indices,
const Vector &src,
Vector &dst)
Vector &dst,
real_t b)
{
auto y = dst.ReadWrite();
const auto x = src.Read();
@@ -5463,56 +5538,104 @@ static void AddSubVector(const Array<int> &unique_dst_indices,
mfem::forall(unique_dst_indices.Size(), [=] MFEM_HOST_DEVICE (int i)
{
const int dst_idx = DST_I[i];
real_t sum = y[dst_idx];
real_t sum = 0;
const int end = SRC_O[i+1];
for (int j = SRC_O[i]; j != end; ++j) { sum += x[SRC_I[j]]; }
y[dst_idx] = sum;
y[dst_idx] += b*sum;
});
}
void DeviceConformingProlongationOperator::ReduceEndAssemble(Vector &y) const
void DeviceConformingProlongationOperator::ReduceEndAssemble(
Vector &y, real_t b) const
{
// dst[shr_ltdof[i]] += shr_buf[i]
if (unq_ltdof.Size() == 0) { return; }
AddSubVector(unq_ltdof, unq_shr_i, unq_shr_j, shr_buf, y);
AddSubVector(unq_ltdof, unq_shr_i, unq_shr_j, shr_buf, y, b);
}
void DeviceConformingProlongationOperator::MultTranspose(const Vector &x,
Vector &y) const
void DeviceConformingProlongationOperator::ApplyTranspose(
const Vector &x, Vector &y, real_t a, real_t b) const
{
const GroupTopology &gtopo = gc.GetGroupTopology();
int req_counter = 0;
if (!local)
int req_counter = 0, num_recv_req = 0;
if (local)
{
ReduceBeginCopy(x); // copy to 'ext_buf'
ReduceLocalCopy(x, y, a, b);
return;
}
ReduceBeginCopy(x); // copy to 'ext_buf'
if (ext_ldof.Size() != 0)
{
if (mpi_gpu_aware)
{
/* record a stream event to wait for later */
#if defined(MFEM_USE_HIP)
MFEM_GPU_CHECK(hipEventRecord(gpu_event, 0));
#elif defined(MFEM_USE_CUDA)
MFEM_GPU_CHECK(cudaEventRecord(gpu_event, 0));
#endif
}
else
{
// Wait for ReduceBeginCopy() to finish and copy the result to host.
// We want to do the copy here, before ReduceLocalCopy(), so we can
// start MPI sends while ReduceLocalCopy() is running asyncronously.
ext_buf.HostRead();
}
}
ReduceLocalCopy(x, y, a, b);
// Queue all receive communications
if (unq_ltdof.Size() != 0)
{
auto recv_buf = mpi_gpu_aware ? shr_buf.Write() : shr_buf.HostWrite();
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
const int recv_offset = shr_buf_offsets[nbr];
const int recv_size = shr_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
MPI_Irecv(recv_buf + recv_offset, recv_size,
MPITypeMap<real_t>::mpi_type,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
}
num_recv_req = req_counter;
}
// Queue all send communications
if (ext_ldof.Size() != 0)
{
if (mpi_gpu_aware)
{
/* wait for the stream event recorded above */
#if defined(MFEM_USE_HIP)
MFEM_GPU_CHECK(hipEventSynchronize(gpu_event));
#elif defined(MFEM_USE_CUDA)
MFEM_GPU_CHECK(cudaEventSynchronize(gpu_event));
#endif
}
auto send_buf = mpi_gpu_aware ? ext_buf.Read() : ext_buf.HostRead();
for (int nbr = 1; nbr < gtopo.GetNumNeighbors(); nbr++)
{
const int send_offset = ext_buf_offsets[nbr];
const int send_size = ext_buf_offsets[nbr+1] - send_offset;
if (send_size > 0)
{
auto send_buf = mpi_gpu_aware ? ext_buf.Read() : ext_buf.HostRead();
MPI_Isend(send_buf + send_offset, send_size, MPITypeMap<real_t>::mpi_type,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
const int recv_offset = shr_buf_offsets[nbr];
const int recv_size = shr_buf_offsets[nbr+1] - recv_offset;
if (recv_size > 0)
{
auto recv_buf = mpi_gpu_aware ? shr_buf.Write() : shr_buf.HostWrite();
MPI_Irecv(recv_buf + recv_offset, recv_size, MPITypeMap<real_t>::mpi_type,
MPI_Isend(send_buf + send_offset, send_size,
MPITypeMap<real_t>::mpi_type,
gtopo.GetNeighborRank(nbr), 41823,
gtopo.GetComm(), &requests[req_counter++]);
}
}
}
ReduceLocalCopy(x, y);
if (!local)
{
MPI_Waitall(req_counter, requests, MPI_STATUSES_IGNORE);
ReduceEndAssemble(y); // assemble from 'shr_buf'
}
// Wait for all receive requests
MPI_Waitall(num_recv_req, requests, MPI_STATUSES_IGNORE);
ReduceEndAssemble(y, b); // assemble from 'shr_buf'
// Wait for all send requests
MPI_Waitall(req_counter - num_recv_req, requests + num_recv_req,
MPI_STATUSES_IGNORE);
}
} // namespace mfem
+18 -3
View File
@@ -586,6 +586,7 @@ public:
{ MultTranspose(x,y); }
};
/// Auxiliary device class used by ParFiniteElementSpace.
class DeviceConformingProlongationOperator: public
ConformingProlongationOperator
@@ -598,6 +599,11 @@ protected:
Array<int> ltdof_ldof, unq_ltdof;
Array<int> unq_shr_i, unq_shr_j;
MPI_Request *requests;
#if defined(MFEM_USE_HIP)
hipEvent_t gpu_event;
#elif defined(MFEM_USE_CUDA)
cudaEvent_t gpu_event;
#endif
// Kernel: copy ltdofs from 'src' to 'shr_buf' - prepare for send.
// shr_buf[i] = src[shr_ltdof[i]]
@@ -617,11 +623,12 @@ protected:
// Kernel: copy owned ldofs from 'src' to ltdofs in 'dst'.
// dst[i] = src[ltdof_ldof[i]]
void ReduceLocalCopy(const Vector &src, Vector &dst) const;
void ReduceLocalCopy(const Vector &src, Vector &dst,
real_t a, real_t b) const;
// Kernel: assemble dofs from 'shr_buf' into to 'dst' - after recv.
// dst[shr_ltdof[i]] += shr_buf[i]
void ReduceEndAssemble(Vector &dst) const;
void ReduceEndAssemble(Vector &dst, real_t b) const;
public:
DeviceConformingProlongationOperator(
@@ -632,12 +639,20 @@ public:
virtual ~DeviceConformingProlongationOperator();
void ApplyTranspose(const Vector &x, Vector &y,
real_t a, real_t b) const;
void Mult(const Vector &x, Vector &y) const override;
void AbsMult(const Vector &x, Vector &y) const override
{ Mult(x,y); }
void MultTranspose(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override
{ ApplyTranspose(x, y, 0, 1); }
void AddMultTranspose(const Vector &x, Vector &y,
real_t a = 1) const override
{ ApplyTranspose(x, y, 1, a); }
void AbsMultTranspose(const Vector &x, Vector &y) const override
{ MultTranspose(x,y); }
+3 -280
View File
@@ -9,278 +9,16 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../quadinterpolator.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../fem/kernels.hpp"
#include "../../linalg/kernels.hpp"
using namespace mfem;
#include "det.hpp"
namespace mfem
{
namespace internal
{
namespace quadrature_interpolator
{
static void Det1D(const int NE,
const real_t *b,
const real_t *g,
const real_t *x,
real_t *y,
const int d1d,
const int q1d,
Vector *d_buff = nullptr)
{
MFEM_CONTRACT_VAR(b);
MFEM_CONTRACT_VAR(d_buff);
const auto G = Reshape(g, q1d, d1d);
const auto X = Reshape(x, d1d, NE);
auto Y = Reshape(y, q1d, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int q = 0; q < q1d; q++)
{
real_t u = 0.0;
for (int d = 0; d < d1d; d++)
{
u += G(q, d) * X(d, e);
}
Y(q, e) = u;
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void Det2D(const int NE,
const real_t *b,
const real_t *g,
const real_t *x,
real_t *y,
const int d1d = 0,
const int q1d = 0,
Vector *d_buff = nullptr)
{
MFEM_CONTRACT_VAR(d_buff);
static constexpr int SDIM = 2;
static constexpr int NBZ = 1;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto B = Reshape(b, Q1D, D1D);
const auto G = Reshape(g, Q1D, D1D);
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
auto Y = Reshape(y, Q1D, Q1D, NE);
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_SHARED real_t BG[2][MQ1*MD1];
MFEM_SHARED real_t XY[SDIM][NBZ][MD1*MD1];
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
MFEM_SHARED real_t QQ[2*SDIM][NBZ][MQ1*MQ1];
kernels::internal::LoadX<MD1,NBZ>(e,D1D,X,XY);
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
kernels::internal::GradX<MD1,MQ1,NBZ>(D1D,Q1D,BG,XY,DQ);
kernels::internal::GradY<MD1,MQ1,NBZ>(D1D,Q1D,BG,DQ,QQ);
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t J[4];
kernels::internal::PullGrad<MQ1,NBZ>(Q1D,qx,qy,QQ,J);
Y(qx,qy,e) = kernels::Det<2>(J);
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
static void Det2DSurface(const int NE,
const real_t *b,
const real_t *g,
const real_t *x,
real_t *y,
const int d1d = 0,
const int q1d = 0,
Vector *d_buff = nullptr)
{
MFEM_CONTRACT_VAR(d_buff);
static constexpr int SDIM = 3;
static constexpr int NBZ = 1;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto B = Reshape(b, Q1D, D1D);
const auto G = Reshape(g, Q1D, D1D);
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
auto Y = Reshape(y, Q1D, Q1D, NE);
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int tidz = MFEM_THREAD_ID(z);
MFEM_SHARED real_t BG[2][MQ1*MD1];
MFEM_SHARED real_t XYZ[SDIM][NBZ][MD1*MD1];
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
// Load XYZ components
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
for (int d = 0; d < SDIM; ++d)
{
XYZ[d][tidz][dx + dy*D1D] = X(dx,dy,d,e);
}
}
}
MFEM_SYNC_THREAD;
ConstDeviceMatrix B_mat(BG[0], D1D, Q1D);
ConstDeviceMatrix G_mat(BG[1], D1D, Q1D);
// x contraction
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
for (int d = 0; d < SDIM; ++d)
{
real_t u = 0.0;
real_t v = 0.0;
for (int dx = 0; dx < D1D; ++dx)
{
const real_t xval = XYZ[d][tidz][dx + dy*D1D];
u += xval * G_mat(dx,qx);
v += xval * B_mat(dx,qx);
}
DQ[d][tidz][dy + qx*D1D] = u;
DQ[3 + d][tidz][dy + qx*D1D] = v;
}
}
}
MFEM_SYNC_THREAD;
// y contraction and determinant computation
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t J_[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
for (int d = 0; d < SDIM; ++d)
{
for (int dy = 0; dy < D1D; ++dy)
{
J_[d] += DQ[d][tidz][dy + qx*D1D] * B_mat(dy,qy);
J_[3 + d] += DQ[3 + d][tidz][dy + qx*D1D] * G_mat(dy,qy);
}
}
DeviceTensor<2> J(J_, 3, 2);
const real_t E = J(0,0)*J(0,0) + J(1,0)*J(1,0) + J(2,0)*J(2,0);
const real_t F = J(0,0)*J(0,1) + J(1,0)*J(1,1) + J(2,0)*J(2,1);
const real_t G = J(0,1)*J(0,1) + J(1,1)*J(1,1) + J(2,1)*J(2,1);
Y(qx,qy,e) = std::sqrt(E*G - F*F);
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0, bool SMEM = true>
static void Det3D(const int NE,
const real_t *b,
const real_t *g,
const real_t *x,
real_t *y,
const int d1d = 0,
const int q1d = 0,
Vector *d_buff = nullptr) // used only with SMEM = false
{
constexpr int DIM = 3;
static constexpr int GRID = SMEM ? 0 : 128;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto B = Reshape(b, Q1D, D1D);
const auto G = Reshape(g, Q1D, D1D);
const auto X = Reshape(x, D1D, D1D, D1D, DIM, NE);
auto Y = Reshape(y, Q1D, Q1D, Q1D, NE);
real_t *GM = nullptr;
if (!SMEM)
{
const DeviceDofQuadLimits &limits = DeviceDofQuadLimits::Get();
const int max_q1d = T_Q1D ? T_Q1D : limits.MAX_Q1D;
const int max_d1d = T_D1D ? T_D1D : limits.MAX_D1D;
const int max_qd = std::max(max_q1d, max_d1d);
const int mem_size = max_qd * max_qd * max_qd * 9;
d_buff->SetSize(2*mem_size*GRID);
GM = d_buff->Write();
}
mfem::forall_3D_grid(NE, Q1D, Q1D, Q1D, GRID, [=] MFEM_HOST_DEVICE (int e)
{
static constexpr int MQ1 = T_Q1D ? T_Q1D :
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_Q1D);
static constexpr int MD1 = T_D1D ? T_D1D :
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_D1D);
static constexpr int MDQ = MQ1 > MD1 ? MQ1 : MD1;
static constexpr int MSZ = MDQ * MDQ * MDQ * 9;
const int bid = MFEM_BLOCK_ID(x);
MFEM_SHARED real_t BG[2][MQ1*MD1];
MFEM_SHARED real_t SM0[SMEM?MSZ:1];
MFEM_SHARED real_t SM1[SMEM?MSZ:1];
real_t *lm0 = SMEM ? SM0 : GM + MSZ*bid;
real_t *lm1 = SMEM ? SM1 : GM + MSZ*(GRID+bid);
real_t (*DDD)[MD1*MD1*MD1] = (real_t (*)[MD1*MD1*MD1]) (lm0);
real_t (*DDQ)[MD1*MD1*MQ1] = (real_t (*)[MD1*MD1*MQ1]) (lm1);
real_t (*DQQ)[MD1*MQ1*MQ1] = (real_t (*)[MD1*MQ1*MQ1]) (lm0);
real_t (*QQQ)[MQ1*MQ1*MQ1] = (real_t (*)[MQ1*MQ1*MQ1]) (lm1);
kernels::internal::LoadX<MD1>(e,D1D,X,DDD);
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
kernels::internal::GradX<MD1,MQ1>(D1D,Q1D,BG,DDD,DDQ);
kernels::internal::GradY<MD1,MQ1>(D1D,Q1D,BG,DDQ,DQQ);
kernels::internal::GradZ<MD1,MQ1>(D1D,Q1D,BG,DQQ,QQQ);
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t J[9];
kernels::internal::PullGrad<MQ1>(Q1D, qx,qy,qz, QQQ, J);
Y(qx,qy,qz,e) = kernels::Det<3>(J);
}
}
}
});
}
void InitDetKernels()
{
using k = QuadratureInterpolator::DetKernels;
@@ -302,27 +40,12 @@ void InitDetKernels()
}
} // namespace quadrature_interpolator
} // namespace internal
/// @cond Suppress_Doxygen_warnings
namespace
{
using DetKernel = QuadratureInterpolator::DetKernelType;
}
template<int DIM, int SDIM, int D1D, int Q1D>
DetKernel QuadratureInterpolator::DetKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
else { MFEM_ABORT(""); }
}
DetKernel QuadratureInterpolator::DetKernels::Fallback(
QuadratureInterpolator::DetKernelType
QuadratureInterpolator::DetKernels::Fallback(
int DIM, int SDIM, int D1D, int Q1D)
{
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
+304
View File
@@ -0,0 +1,304 @@
// 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_QUADINTERP_DET_HPP
#define MFEM_QUADINTERP_DET_HPP
#include "../quadinterpolator.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../fem/kernels.hpp"
#include "../../linalg/kernels.hpp"
namespace mfem
{
namespace internal
{
namespace quadrature_interpolator
{
inline void Det1D(const int NE,
const real_t *b,
const real_t *g,
const real_t *x,
real_t *y,
const int d1d,
const int q1d,
Vector *d_buff = nullptr)
{
MFEM_CONTRACT_VAR(b);
MFEM_CONTRACT_VAR(d_buff);
const auto G = Reshape(g, q1d, d1d);
const auto X = Reshape(x, d1d, NE);
auto Y = Reshape(y, q1d, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int q = 0; q < q1d; q++)
{
real_t u = 0.0;
for (int d = 0; d < d1d; d++)
{
u += G(q, d) * X(d, e);
}
Y(q, e) = u;
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
inline void Det2D(const int NE,
const real_t *b,
const real_t *g,
const real_t *x,
real_t *y,
const int d1d = 0,
const int q1d = 0,
Vector *d_buff = nullptr)
{
MFEM_CONTRACT_VAR(d_buff);
static constexpr int SDIM = 2;
static constexpr int NBZ = 1;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto B = Reshape(b, Q1D, D1D);
const auto G = Reshape(g, Q1D, D1D);
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
auto Y = Reshape(y, Q1D, Q1D, NE);
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_SHARED real_t BG[2][MQ1*MD1];
MFEM_SHARED real_t XY[SDIM][NBZ][MD1*MD1];
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
MFEM_SHARED real_t QQ[2*SDIM][NBZ][MQ1*MQ1];
kernels::internal::LoadX<MD1,NBZ>(e,D1D,X,XY);
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
kernels::internal::GradX<MD1,MQ1,NBZ>(D1D,Q1D,BG,XY,DQ);
kernels::internal::GradY<MD1,MQ1,NBZ>(D1D,Q1D,BG,DQ,QQ);
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t J[4];
kernels::internal::PullGrad<MQ1,NBZ>(Q1D,qx,qy,QQ,J);
Y(qx,qy,e) = kernels::Det<2>(J);
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
inline void Det2DSurface(const int NE,
const real_t *b,
const real_t *g,
const real_t *x,
real_t *y,
const int d1d = 0,
const int q1d = 0,
Vector *d_buff = nullptr)
{
MFEM_CONTRACT_VAR(d_buff);
static constexpr int SDIM = 3;
static constexpr int NBZ = 1;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto B = Reshape(b, Q1D, D1D);
const auto G = Reshape(g, Q1D, D1D);
const auto X = Reshape(x, D1D, D1D, SDIM, NE);
auto Y = Reshape(y, Q1D, Q1D, NE);
mfem::forall_2D_batch(NE, Q1D, Q1D, NBZ, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int tidz = MFEM_THREAD_ID(z);
MFEM_SHARED real_t BG[2][MQ1*MD1];
MFEM_SHARED real_t XYZ[SDIM][NBZ][MD1*MD1];
MFEM_SHARED real_t DQ[2*SDIM][NBZ][MD1*MQ1];
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
// Load XYZ components
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
for (int d = 0; d < SDIM; ++d)
{
XYZ[d][tidz][dx + dy*D1D] = X(dx,dy,d,e);
}
}
}
MFEM_SYNC_THREAD;
ConstDeviceMatrix B_mat(BG[0], D1D, Q1D);
ConstDeviceMatrix G_mat(BG[1], D1D, Q1D);
// x contraction
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
for (int d = 0; d < SDIM; ++d)
{
real_t u = 0.0;
real_t v = 0.0;
for (int dx = 0; dx < D1D; ++dx)
{
const real_t xval = XYZ[d][tidz][dx + dy*D1D];
u += xval * G_mat(dx,qx);
v += xval * B_mat(dx,qx);
}
DQ[d][tidz][dy + qx*D1D] = u;
DQ[3 + d][tidz][dy + qx*D1D] = v;
}
}
}
MFEM_SYNC_THREAD;
// y contraction and determinant computation
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t J_[6] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
for (int d = 0; d < SDIM; ++d)
{
for (int dy = 0; dy < D1D; ++dy)
{
J_[d] += DQ[d][tidz][dy + qx*D1D] * B_mat(dy,qy);
J_[3 + d] += DQ[3 + d][tidz][dy + qx*D1D] * G_mat(dy,qy);
}
}
DeviceTensor<2> J(J_, 3, 2);
const real_t E = J(0,0)*J(0,0) + J(1,0)*J(1,0) + J(2,0)*J(2,0);
const real_t F = J(0,0)*J(0,1) + J(1,0)*J(1,1) + J(2,0)*J(2,1);
const real_t G = J(0,1)*J(0,1) + J(1,1)*J(1,1) + J(2,1)*J(2,1);
Y(qx,qy,e) = std::sqrt(E*G - F*F);
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0, bool SMEM = true>
inline void Det3D(const int NE,
const real_t *b,
const real_t *g,
const real_t *x,
real_t *y,
const int d1d = 0,
const int q1d = 0,
Vector *d_buff = nullptr) // used only with SMEM = false
{
constexpr int DIM = 3;
static constexpr int GRID = SMEM ? 0 : 128;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto B = Reshape(b, Q1D, D1D);
const auto G = Reshape(g, Q1D, D1D);
const auto X = Reshape(x, D1D, D1D, D1D, DIM, NE);
auto Y = Reshape(y, Q1D, Q1D, Q1D, NE);
real_t *GM = nullptr;
if (!SMEM)
{
const DeviceDofQuadLimits &limits = DeviceDofQuadLimits::Get();
const int max_q1d = T_Q1D ? T_Q1D : limits.MAX_Q1D;
const int max_d1d = T_D1D ? T_D1D : limits.MAX_D1D;
const int max_qd = std::max(max_q1d, max_d1d);
const int mem_size = max_qd * max_qd * max_qd * 9;
d_buff->SetSize(2*mem_size*GRID);
GM = d_buff->Write();
}
mfem::forall_3D_grid(NE, Q1D, Q1D, Q1D, GRID, [=] MFEM_HOST_DEVICE (int e)
{
static constexpr int MQ1 = T_Q1D ? T_Q1D :
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_Q1D);
static constexpr int MD1 = T_D1D ? T_D1D :
(SMEM ? DofQuadLimits::MAX_DET_1D : DofQuadLimits::MAX_D1D);
static constexpr int MDQ = MQ1 > MD1 ? MQ1 : MD1;
static constexpr int MSZ = MDQ * MDQ * MDQ * 9;
const int bid = MFEM_BLOCK_ID(x);
MFEM_SHARED real_t BG[2][MQ1*MD1];
MFEM_SHARED real_t SM0[SMEM?MSZ:1];
MFEM_SHARED real_t SM1[SMEM?MSZ:1];
real_t *lm0 = SMEM ? SM0 : GM + MSZ*bid;
real_t *lm1 = SMEM ? SM1 : GM + MSZ*(GRID+bid);
real_t (*DDD)[MD1*MD1*MD1] = (real_t (*)[MD1*MD1*MD1]) (lm0);
real_t (*DDQ)[MD1*MD1*MQ1] = (real_t (*)[MD1*MD1*MQ1]) (lm1);
real_t (*DQQ)[MD1*MQ1*MQ1] = (real_t (*)[MD1*MQ1*MQ1]) (lm0);
real_t (*QQQ)[MQ1*MQ1*MQ1] = (real_t (*)[MQ1*MQ1*MQ1]) (lm1);
kernels::internal::LoadX<MD1>(e,D1D,X,DDD);
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,B,G,BG);
kernels::internal::GradX<MD1,MQ1>(D1D,Q1D,BG,DDD,DDQ);
kernels::internal::GradY<MD1,MQ1>(D1D,Q1D,BG,DDQ,DQQ);
kernels::internal::GradZ<MD1,MQ1>(D1D,Q1D,BG,DQQ,QQQ);
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t J[9];
kernels::internal::PullGrad<MQ1>(Q1D, qx,qy,qz, QQQ, J);
Y(qx,qy,qz,e) = kernels::Det<3>(J);
}
}
}
});
}
} // namespace quadrature_interpolator
} // namespace internal
/// @cond Suppress_Doxygen_warnings
template<int DIM, int SDIM, int D1D, int Q1D>
QuadratureInterpolator::DetKernelType
QuadratureInterpolator::DetKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
else { MFEM_ABORT(""); }
}
/// @endcond
} // namespace mfem
#endif // MFEM_QUADINTERP_DET_HPP
+9 -27
View File
@@ -5122,33 +5122,32 @@ real_t TMOP_Integrator::GetSurfaceFittingWeight()
void TMOP_Integrator::EnableNormalization(const GridFunction &x)
{
ComputeNormalizationEnergies(x, metric_normal, lim_normal, surf_fit_normal);
ComputeNormalizationEnergies(x, metric_normal, lim_normal);
metric_normal = 1.0 / metric_normal;
lim_normal = 1.0 / lim_normal;
//if (surf_fit_gf) { surf_fit_normal = 1.0 / surf_fit_normal; }
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
}
#ifdef MFEM_USE_MPI
void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x)
{
real_t loc[3];
ComputeNormalizationEnergies(x, loc[0], loc[1], loc[2]);
real_t rdc[3];
MPI_Allreduce(loc, rdc, 3, MPITypeMap<real_t>::mpi_type, MPI_SUM,
real_t loc[2];
ComputeNormalizationEnergies(x, loc[0], loc[1]);
real_t rdc[2];
MPI_Allreduce(loc, rdc, 2, MPITypeMap<real_t>::mpi_type, MPI_SUM,
x.ParFESpace()->GetComm());
metric_normal = 1.0 / rdc[0];
lim_normal = 1.0 / rdc[1];
// if (surf_fit_gf) { surf_fit_normal = 1.0 / rdc[2]; }
if (surf_fit_gf || surf_fit_pos) { surf_fit_normal = lim_normal; }
}
#endif
void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
real_t &metric_energy,
real_t &lim_energy,
real_t &surf_fit_gf_energy)
real_t &lim_energy)
{
metric_energy = 0.0;
lim_energy = 0.0;
if (PA.enabled)
{
MFEM_VERIFY(PA.E.Size() > 0, "Must be called after AssemblePA!");
@@ -5191,9 +5190,6 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
Jpr.SetSize(dim);
Jpt.SetSize(dim);
metric_energy = 0.0;
lim_energy = 0.0;
surf_fit_gf_energy = 0.0;
for (int i = 0; i < fes->GetNE(); i++)
{
const FiniteElement *fe = fes->GetFE(i);
@@ -5225,21 +5221,7 @@ void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
lim_energy += weight;
}
// Normalization of the surface fitting term.
if (surf_fit_gf)
{
Array<int> dofs;
Vector sigma_e;
surf_fit_gf->FESpace()->GetElementDofs(i, dofs);
surf_fit_gf->GetSubVector(dofs, sigma_e);
for (int s = 0; s < dofs.Size(); s++)
{
if ((*surf_fit_marker)[dofs[s]] == true)
{
surf_fit_gf_energy += sigma_e(s) * sigma_e(s);
}
}
}
// TODO: Normalization of the surface fitting term.
}
// Cases when integration is not over the target element, or when the
+1 -2
View File
@@ -2038,8 +2038,7 @@ protected:
} PA;
void ComputeNormalizationEnergies(const GridFunction &x,
real_t &metric_energy, real_t &lim_energy,
real_t &surf_fit_gf_energy);
real_t &metric_energy, real_t &lim_energy);
void AssembleElementVectorExact(const FiniteElement &el,
ElementTransformation &T,
+5 -1
View File
@@ -326,7 +326,11 @@ public:
the Size to match this Capacity after this.*/
template <typename U>
inline void CopyFrom(const U *src)
{ std::memcpy(begin(), src, MemoryUsage()); }
{
if (!begin() || size == 0) { return; }
MFEM_ASSERT(begin() && src, "Error in Array::CopyFrom");
std::memcpy(begin(), src, MemoryUsage());
}
/// STL-like begin. Returns pointer to the first element of the array.
inline T* begin() { return data; }
+1
View File
@@ -62,6 +62,7 @@
#define MFEM_THREAD_ID(k) 0
#define MFEM_THREAD_SIZE(k) 1
#define MFEM_FOREACH_THREAD(i,k,N) for(int i=0; i<N; i++)
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) MFEM_FOREACH_THREAD(i,k,N)
#endif
// 'double' and 'float' atomicAdd implementation for previous versions of CUDA
+1
View File
@@ -47,6 +47,7 @@
#define MFEM_THREAD_ID(k) threadIdx.k
#define MFEM_THREAD_SIZE(k) blockDim.k
#define MFEM_FOREACH_THREAD(i,k,N) for(int i=threadIdx.k; i<N; i+=blockDim.k)
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) if(const int i=threadIdx.k; i<N)
#endif
namespace mfem
+36
View File
@@ -16,6 +16,7 @@
#include "../fem/ceed/interface/util.hpp"
#endif
#ifdef MFEM_USE_MPI
#include "communication.hpp"
#include "../linalg/hypre.hpp"
#endif
@@ -145,6 +146,11 @@ Device::Device()
Configure(device);
device_env = true;
}
if (GetEnv("MFEM_GPU_AWARE_MPI"))
{
SetGPUAwareMPI(true);
}
}
Device::~Device()
@@ -196,6 +202,29 @@ void Device::Configure(const std::string &device, const int device_id)
{
bmap[internal::backend_name[i]] = internal::backend_list[i];
}
// auto-detect GPU configurations
// assumes only one of HIP or CUDA are available
#ifdef MFEM_USE_HIP
bmap["gpu"] = Backend::HIP;
#ifdef MFEM_USE_RAJA
bmap["raja-gpu"] = Backend::RAJA_HIP;
#endif
#ifdef MFEM_USE_CEED
bmap["ceed-gpu"] = Backend::CEED_HIP;
#endif
// no OCCA+HIP?
#elif defined(MFEM_USE_CUDA)
bmap["gpu"] = Backend::CUDA;
#ifdef MFEM_USE_RAJA
bmap["raja-gpu"] = Backend::RAJA_CUDA;
#endif
#ifdef MFEM_USE_CEED
bmap["ceed-gpu"] = Backend::CEED_CUDA;
#endif
#ifdef MFEM_USE_OCCA
bmap["occa-gpu"] = Backend::OCCA_CUDA;
#endif
#endif
std::string device_option;
std::string::size_type beg = 0, end;
while (1)
@@ -313,6 +342,13 @@ void Device::Print(std::ostream &os)
{
os << ',' << MemoryTypeName[static_cast<int>(device_mem_type)];
}
#ifdef MFEM_USE_MPI
if (Allows(Backend::DEVICE_MASK) &&
Mpi::IsInitialized() && !Mpi::IsFinalized())
{
os << "\nUse GPU-aware MPI: " << (GetGPUAwareMPI() ? "yes" : "no");
}
#endif
os << std::endl;
}
+4
View File
@@ -198,6 +198,10 @@ public:
'ceed-hip', 'hip', 'debug',
'occa-omp', 'raja-omp', 'omp',
'ceed-cpu', 'occa-cpu', 'raja-cpu', 'cpu'.
- The following backend aliases are also available: 'ceed-gpu',
'occa-gpu', 'raja-gpu', and 'gpu' where they alias their respective
'*-cuda' or '*-hip' backends depending on the MFEM build-time
configuration.
- Multiple backends can be configured at the same time.
- Only one 'occa-*' backend can be configured at a time.
- The backend 'occa-cuda' enables the 'cuda' backend unless 'raja-cuda'
+3 -1
View File
@@ -47,7 +47,9 @@
#define MFEM_THREAD_ID(k) hipThreadIdx_ ##k
#define MFEM_THREAD_SIZE(k) hipBlockDim_ ##k
#define MFEM_FOREACH_THREAD(i,k,N) \
for(int i=hipThreadIdx_ ##k; i<N; i+=hipBlockDim_ ##k)
for(int i=hipThreadIdx_ ##k; i<N; i+=hipBlockDim_ ##k)
#define MFEM_FOREACH_THREAD_DIRECT(i,k,N) \
if(const int i=hipThreadIdx_ ##k; i<N)
#endif
namespace mfem
+28
View File
@@ -4405,4 +4405,32 @@ void BatchLUSolve(const DenseTensor &Mlu, const Array<int> &P, Vector &X)
BatchedLinAlg::LUSolve(Mlu, P, X);
}
#ifdef MFEM_USE_LAPACK
void BandedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
Array<int> &ipiv)
{
int LDAB = (2*KL) + KU + 1;
int N = AB.NumCols();
int NRHS = B.NumCols();
int info;
ipiv.SetSize(N);
MFEM_LAPACK_PREFIX(gbsv_)(&N, &KL, &KU, &NRHS, AB.GetData(), &LDAB,
ipiv.GetData(), B.GetData(), &N, &info);
MFEM_ASSERT(info == 0, "BandedSolve failed in LAPACK");
}
void BandedFactorizedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
bool transpose, Array<int> &ipiv)
{
int LDAB = (2*KL) + KU + 1;
int N = AB.NumCols();
int NRHS = B.NumCols();
char trans = transpose ? 'T' : 'N';
int info;
MFEM_LAPACK_PREFIX(gbtrs_)(&trans, &N, &KL, &KU, &NRHS, AB.GetData(), &LDAB,
ipiv.GetData(), B.GetData(), &N, &info);
MFEM_ASSERT(info == 0, "BandedFactorizedSolve failed in LAPACK");
}
#endif
} // namespace mfem
+7
View File
@@ -1329,6 +1329,13 @@ void BatchLUFactor(DenseTensor &Mlu, Array<int> &P, const real_t TOL = 0.0);
dimension m x n. */
void BatchLUSolve(const DenseTensor &Mlu, const Array<int> &P, Vector &X);
#ifdef MFEM_USE_LAPACK
void BandedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
Array<int> &ipiv);
void BandedFactorizedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
bool transpose, Array<int> &ipiv);
#endif
// Inline methods
inline real_t &DenseMatrix::operator()(int i, int j)
+12
View File
@@ -2574,6 +2574,18 @@ void HypreParMatrix::EliminateBC(const Array<int> &ess_dofs,
#if defined(HYPRE_USING_GPU)
if (HypreUsingGPU())
{
#if defined(HYPRE_WITH_GPU_AWARE_MPI) || defined(HYPRE_USING_GPU_AWARE_MPI)
// hypre_GetGpuAwareMPI() was introduced in v2.31.0, however, its value
// is not checked in hypre_ParCSRCommHandleCreate_v2() before v2.33.0,
// instead only HYPRE_WITH_GPU_AWARE_MPI is checked.
#if MFEM_HYPRE_VERSION >= 23300
if (hypre_GetGpuAwareMPI())
#endif
{
// ensure int_buf_data has been computed before sending it
MFEM_STREAM_SYNC;
}
#endif
// Try to use device-aware MPI for the communication if available
comm_handle = hypre_ParCSRCommHandleCreate_v2(
11, comm_pkg, HYPRE_MEMORY_DEVICE, int_buf_data,
+7
View File
@@ -42,6 +42,13 @@ extern "C" void
MFEM_LAPACK_PREFIX(getri_)(int *N, real_t *A, int *LDA, int *IPIV, real_t *WORK,
int *LWORK, int *INFO);
extern "C" void
MFEM_LAPACK_PREFIX(gbsv_)(int *, int *, int *, int *, real_t *, int *, int *,
real_t *, int *, int *);
extern "C" void
MFEM_LAPACK_PREFIX(gbtrs_)(char *, int *, int *, int *, int *, real_t *, int *,
int *, real_t *, int *, int *);
extern "C" void
MFEM_LAPACK_PREFIX(syevr_)(char *JOBZ, char *RANGE, char *UPLO, int *N,
real_t *A, int *LDA, real_t *VL, real_t *VU, int *IL,
int *IU, real_t *ABSTOL, int *M, real_t *W,
+35 -27
View File
@@ -26,6 +26,12 @@
}\
}
#if defined(MFEM_USE_DOUBLE)
#define MFEM_NETCDF_REAL_T NC_DOUBLE
#elif defined(MFEM_USE_SINGLE)
#define MFEM_NETCDF_REAL_T NC_FLOAT
#endif
namespace mfem
{
@@ -135,18 +141,18 @@ public:
/// @brief Writes the mesh to an ExodusII file.
/// @param fpath The path to the file.
/// @param flags NC_CLOBBER will overwrite existing file.
void PrintExodusII(std::string fpath, int flags = NC_CLOBBER);
void PrintExodusII(const std::string &fpath, int flags = NC_CLOBBER);
/// @brief Static method for writing a mesh to an ExodusII file.
/// @param mesh The mesh to write to the file.
/// @param fpath The path to the file.
/// @param flags NetCDF file flags.
static void PrintExodusII(Mesh & mesh, std::string fpath,
static void PrintExodusII(Mesh & mesh, const std::string &fpath,
int flags = NC_CLOBBER);
protected:
/// @brief Closes any open file and creates a NetCDF file using selected flags.
void OpenExodusII(std::string fpath, int flags);
void OpenExodusII(const std::string &fpath, int flags);
/// @brief Closes any open file.
void CloseExodusII();
@@ -167,9 +173,9 @@ protected:
std::unordered_set<int> GenerateUniqueNodeIDs();
/// @brief Populates vectors with x, y, z coordinates from mesh.
void ExtractVertexCoordinates(std::vector<double> & coordx,
std::vector<double> & coordy,
std::vector<double> & coordz);
void ExtractVertexCoordinates(std::vector<real_t> &coordx,
std::vector<real_t> &coordy,
std::vector<real_t> &coordz);
/// @brief Writes node connectivity for a particular block.
/// @param block_id The block to write to the file.
@@ -187,7 +193,7 @@ protected:
/// @brief Writes the number of elements in the mesh.
void WriteNumOfElements();
/// @brief Writes the floating-point word size (4 == float; 8 == double).
/// @brief Writes the floating-point word size (sizeof(real_t)).
void WriteFloatingPointWordSize();
/// @brief Writes the API version.
@@ -291,7 +297,7 @@ private:
std::map<int, std::vector<int>> exodusII_side_ids_for_boundary_id;
};
void Mesh::PrintExodusII(const std::string fpath)
void Mesh::PrintExodusII(const std::string &fpath)
{
ExodusIIWriter::PrintExodusII(*this, fpath);
}
@@ -362,7 +368,7 @@ void ExodusIIWriter::WriteExodusIIMeshInformation()
WriteNodeSets();
}
void ExodusIIWriter::PrintExodusII(std::string fpath, int flags)
void ExodusIIWriter::PrintExodusII(const std::string &fpath, int flags)
{
OpenExodusII(fpath, flags);
@@ -374,7 +380,7 @@ void ExodusIIWriter::PrintExodusII(std::string fpath, int flags)
mfem::out << "Mesh successfully written to Exodus II file" << std::endl;
}
void ExodusIIWriter::PrintExodusII(Mesh & mesh, std::string fpath,
void ExodusIIWriter::PrintExodusII(Mesh &mesh, const std::string &fpath,
int flags)
{
ExodusIIWriter writer(mesh);
@@ -382,7 +388,7 @@ void ExodusIIWriter::PrintExodusII(Mesh & mesh, std::string fpath,
writer.PrintExodusII(fpath, flags);
}
void ExodusIIWriter::OpenExodusII(std::string fpath, int flags)
void ExodusIIWriter::OpenExodusII(const std::string &fpath, int flags)
{
CloseExodusII(); // Close any open files.
@@ -422,7 +428,7 @@ void ExodusIIWriter::WriteNumOfElements()
void ExodusIIWriter::WriteFloatingPointWordSize()
{
const int word_size = 8;
const int word_size = sizeof(real_t);
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_FLOATING_POINT_WORD_SIZE_LABEL,
NC_INT, 1,
&word_size);
@@ -430,13 +436,15 @@ void ExodusIIWriter::WriteFloatingPointWordSize()
void ExodusIIWriter::WriteAPIVersion()
{
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_API_VERSION_LABEL, NC_FLOAT, 1,
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_API_VERSION_LABEL, MFEM_NETCDF_REAL_T,
1,
&ExodusIILabels::EXODUS_API_VERSION);
}
void ExodusIIWriter::WriteDatabaseVersion()
{
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_DATABASE_VERSION_LABEL, NC_FLOAT, 1,
PutAtt(NC_GLOBAL, ExodusIILabels::EXODUS_DATABASE_VERSION_LABEL,
MFEM_NETCDF_REAL_T, 1,
&ExodusIILabels::EXODUS_DATABASE_VERSION);
}
@@ -607,25 +615,25 @@ void ExodusIIWriter::WriteNodalCoordinates()
DefineDimension("num_nodes", num_nodes, &num_nodes_id);
// 3. Extract the nodal coordinates.
// NB: assume doubles (could be floats!); ndims = 1 (vector).
// NB: writes in format real_t (double or float); ndims = 1 (vector).
// https://docs.unidata.ucar.edu/netcdf-c/current/group__variables.html#gac7e8662c51f3bb07d1fc6d6c6d9052c8
std::vector<double> coordx(num_nodes);
std::vector<double> coordy(num_nodes);
std::vector<double> coordz(mesh.Dimension() == 3 ? num_nodes : 0);
std::vector<real_t> coordx(num_nodes);
std::vector<real_t> coordy(num_nodes);
std::vector<real_t> coordz(mesh.Dimension() == 3 ? num_nodes : 0);
ExtractVertexCoordinates(coordx, coordy, coordz);
// 4. Define and put the nodal coordinates.
DefineAndPutVar(ExodusIILabels::EXODUS_COORDX_LABEL, NC_DOUBLE, 1,
DefineAndPutVar(ExodusIILabels::EXODUS_COORDX_LABEL, MFEM_NETCDF_REAL_T, 1,
&num_nodes_id,
coordx.data());
DefineAndPutVar(ExodusIILabels::EXODUS_COORDY_LABEL, NC_DOUBLE, 1,
DefineAndPutVar(ExodusIILabels::EXODUS_COORDY_LABEL, MFEM_NETCDF_REAL_T, 1,
&num_nodes_id,
coordy.data());
if (mesh.Dimension() == 3)
{
DefineAndPutVar(ExodusIILabels::EXODUS_COORDZ_LABEL, NC_DOUBLE, 1,
DefineAndPutVar(ExodusIILabels::EXODUS_COORDZ_LABEL, MFEM_NETCDF_REAL_T, 1,
&num_nodes_id,
coordz.data());
}
@@ -770,9 +778,9 @@ void ExodusIIWriter::WriteNodeConnectivityForBlock(const int block_id)
}
void ExodusIIWriter::ExtractVertexCoordinates(std::vector<double> & coordx,
std::vector<double> & coordy,
std::vector<double> & coordz)
void ExodusIIWriter::ExtractVertexCoordinates(std::vector<real_t> & coordx,
std::vector<real_t> & coordy,
std::vector<real_t> & coordz)
{
if (mesh.GetNodes()) // Higher-order.
{
@@ -782,7 +790,7 @@ void ExodusIIWriter::ExtractVertexCoordinates(std::vector<double> & coordx,
sorted_node_ids.assign(unordered_node_ids.begin(), unordered_node_ids.end());
std::sort(sorted_node_ids.begin(), sorted_node_ids.end());
double coordinates[3];
real_t coordinates[3];
for (size_t i = 0; i < sorted_node_ids.size(); i++)
{
int node_id = sorted_node_ids[i];
@@ -802,7 +810,7 @@ void ExodusIIWriter::ExtractVertexCoordinates(std::vector<double> & coordx,
{
for (int ivertex = 0; ivertex < mesh.GetNV(); ivertex++)
{
double * coordinates = mesh.GetVertex(ivertex);
real_t *coordinates = mesh.GetVertex(ivertex);
coordx[ivertex] = coordinates[0];
coordy[ivertex] = coordinates[1];
@@ -1080,4 +1088,4 @@ void ExodusIIWriter::CheckNodalFESpaceIsSecondOrderH1() const
#endif
}
}
+1
View File
@@ -2994,6 +2994,7 @@ void Mesh::DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
const int num_edge_dofs = old_dofs.Size();
// Save the original nodes
Nodes->HostReadWrite(); // for "(*Nodes)() = "
const Vector onodes = *Nodes;
// vertex dofs do not need to be moved
+11 -8
View File
@@ -588,9 +588,10 @@ protected:
void Loader(std::istream &input, int generate_edges = 0,
std::string parse_tag = "");
/** If NURBS mesh, write NURBS format. If NCMesh, write mfem v1.1 format.
If section_delimiter is empty, write mfem v1.0 format. Otherwise, write
mfem v1.2 format with the given section_delimiter at the end.
/** @brief If NURBS mesh, write NURBS format. If NCMesh, write mfem v1.1
format. If section_delimiter is empty, write mfem v1.0 format. Otherwise,
write mfem v1.2 format with the given section_delimiter at the end.
If @a comments is non-empty, it will be printed after the first line of
the file, and each line should begin with '#'. */
void Printer(std::ostream &os = mfem::out,
@@ -2482,10 +2483,12 @@ public:
/// Print the mesh to the given stream using Netgen/Truegrid format.
virtual void PrintXG(std::ostream &os = mfem::out) const;
/// Print the mesh to the given stream using the default MFEM mesh format.
/// \see mfem::ofgzstream() for on-the-fly compression of ascii outputs. If
/// @a comments is non-empty, it will be printed after the first line of the
/// file, and each line should begin with '#'.
/** @brief Print the mesh to the given stream using the default MFEM mesh
format.
\see mfem::ofgzstream() for on-the-fly compression of ascii outputs. If
@a comments is non-empty, it will be printed after the first line of the
file, and each line should begin with '#'. */
virtual void Print(std::ostream &os = mfem::out,
const std::string &comments = "") const
{ Printer(os, "", comments); }
@@ -2537,7 +2540,7 @@ public:
#ifdef MFEM_USE_NETCDF
/// @brief Export a mesh to an Exodus II file.
void PrintExodusII(const std::string fpath);
void PrintExodusII(const std::string &fpath);
#endif
/** @brief Prints the mesh with boundary elements given by the boundary of
+3 -13
View File
@@ -802,21 +802,11 @@ struct BufferReader : BufferReaderBase
{
// Each "data block" is preceded by a header that is either UInt32 or
// UInt64. The rest of the data follows.
uint64_t data_size;
if (header_type == UINT32_HEADER)
{
uint32_t *data_size_32 = (uint32_t *)header_buf;
data_size = *data_size_32;
}
else
{
uint64_t *data_size_64 = (uint64_t *)header_buf;
data_size = *data_size_64;
}
MFEM_VERIFY(sizeof(F)*n == data_size, "AppendedData: wrong data size");
MFEM_VERIFY(sizeof(F)*n == ReadHeaderEntry(header_buf),
"AppendedData: wrong data size");
}
if (std::is_same<T, F>::value)
if (std::is_same_v<T, F>)
{
// Special case: no type conversions necessary, so can just memcpy
memcpy(dest, buf, sizeof(T)*n);
+99 -27
View File
@@ -9,8 +9,13 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "mesh_headers.hpp"
#include "../fem/fem.hpp"
#include "nurbs.hpp"
#include "point.hpp"
#include "segment.hpp"
#include "quadrilateral.hpp"
#include "hexahedron.hpp"
#include "../fem/gridfunc.hpp"
#include "../general/text.hpp"
#include <fstream>
@@ -33,6 +38,7 @@ KnotVector::KnotVector(istream &input)
knot.Load(input, NumOfControlPoints + Order + 1);
GetElements();
coarse = false;
}
KnotVector::KnotVector(int order, int NCP)
@@ -41,12 +47,13 @@ KnotVector::KnotVector(int order, int NCP)
NumOfControlPoints = NCP;
knot.SetSize(NumOfControlPoints + Order + 1);
NumOfElements = 0;
coarse = false;
knot = -1.;
}
KnotVector::KnotVector(int order, const Vector& intervals,
const Array<int>& continuity )
const Array<int>& continuity)
{
// NOTE: This may need to be generalized to support periodicity
// in the future.
@@ -86,6 +93,7 @@ KnotVector::KnotVector(int order, const Vector& intervals,
++NumOfElements;
}
}
coarse = false;
}
KnotVector &KnotVector::operator=(const KnotVector &kv)
@@ -143,7 +151,7 @@ void KnotVector::UniformRefinement(Vector &newknots, int rf) const
{
for (int m = 1; m < rf; ++m)
{
newknots(j) = m * h * (knot(i) + knot(i+1));
newknots(j) = ((1.0 - (m * h)) * knot(i)) + (m * h * knot(i+1));
j++;
}
}
@@ -332,7 +340,7 @@ void KnotVector::PrintFunctions(std::ostream &os, int samples) const
}
}
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
// Algorithm A2.2 p. 70
void KnotVector::CalcShape(Vector &shape, int i, real_t xi) const
{
@@ -359,7 +367,7 @@ void KnotVector::CalcShape(Vector &shape, int i, real_t xi) const
}
}
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
// Algorithm A2.3 p. 72
void KnotVector::CalcDShape(Vector &grad, int i, real_t xi) const
{
@@ -417,7 +425,7 @@ void KnotVector::CalcDShape(Vector &grad, int i, real_t xi) const
}
}
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
// Algorithm A2.3 p. 72
void KnotVector::CalcDnShape(Vector &gradn, int n, int i, real_t xi) const
{
@@ -537,11 +545,11 @@ void KnotVector::FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const
int i = j - d;
if (isElement(i))
{
arg1 = 1e-16;
arg1 = std::numeric_limits<real_t>::epsilon() / 2_r;
CalcShape(shape, i, arg1);
max1 = shape[d];
arg2 = 1-(1e-16);
arg2 = 1_r - arg1;
CalcShape(shape, i, arg2);
max2 = shape[d];
@@ -579,9 +587,9 @@ void KnotVector::FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const
}
}
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
// Algorithm A9.1 p. 369
void KnotVector::FindInterpolant(Array<Vector*> &x)
void KnotVector::FindInterpolant(Array<Vector*> &x, bool reuse_inverse)
{
int order = GetOrder();
int ncp = GetNCP();
@@ -589,29 +597,93 @@ void KnotVector::FindInterpolant(Array<Vector*> &x)
// Find interpolation points
Vector xi_args, u_args;
Array<int> i_args;
FindMaxima(i_args,xi_args, u_args);
FindMaxima(i_args, xi_args, u_args);
// Assemble collocation matrix
Vector shape(order+1);
DenseMatrix A(ncp,ncp);
A = 0.0;
#ifdef MFEM_USE_LAPACK
// If using LAPACK, we use banded matrix storage (order + 1 nonzeros per row).
// Find banded structure of matrix.
int KL = 0; // Number of subdiagonals
int KU = 0; // Number of superdiagonals
for (int i = 0; i < ncp; i++)
{
CalcShape(shape, i_args[i], xi_args[i]);
for (int p = 0; p < order+1; p++)
{
A(i,i_args[i] + p) = shape[p];
const int col = i_args[i] + p;
if (col < i)
{
KL = std::max(KL, i - col);
}
else if (i < col)
{
KU = std::max(KU, col - i);
}
}
}
// Solve problems
A.Invert();
const int LDAB = (2*KL) + KU + 1;
const int N = ncp;
fact_AB.SetSize(LDAB, N);
#else
// Without LAPACK, we store and invert a DenseMatrix (inefficient).
if (!reuse_inverse)
{
A_coll_inv.SetSize(ncp, ncp);
A_coll_inv = 0.0;
}
#endif
Vector shape(order+1);
if (!reuse_inverse) // Set collocation matrix entries
{
for (int i = 0; i < ncp; i++)
{
CalcShape(shape, i_args[i], xi_args[i]);
for (int p = 0; p < order+1; p++)
{
const int j = i_args[i] + p;
#ifdef MFEM_USE_LAPACK
fact_AB(KL+KU+i-j,j) = shape[p];
#else
A_coll_inv(i,j) = shape[p];
#endif
}
}
}
// Solve the system
#ifdef MFEM_USE_LAPACK
const int NRHS = x.Size();
DenseMatrix B(N, NRHS);
for (int j=0; j<NRHS; ++j)
{
for (int i=0; i<N; ++i) { B(i, j) = (*x[j])[i]; }
}
if (reuse_inverse)
{
BandedFactorizedSolve(KL, KU, fact_AB, B, false, fact_ipiv);
}
else
{
BandedSolve(KL, KU, fact_AB, B, fact_ipiv);
}
for (int j=0; j<NRHS; ++j)
{
for (int i=0; i<N; ++i) { (*x[j])[i] = B(i, j); }
}
#else
if (!reuse_inverse) { A_coll_inv.Invert(); }
Vector tmp;
for (int i= 0; i < x.Size(); i++)
for (int i = 0; i < x.Size(); i++)
{
tmp = *x[i];
A.Mult(tmp,*x[i]);
A_coll_inv.Mult(tmp, *x[i]);
}
#endif
}
int KnotVector::findKnotSpan(real_t u) const
@@ -1413,7 +1485,7 @@ void NURBSPatch::DegreeElevate(int t)
}
}
// Routine from "The NURBS book" - 2nd ed - Piegl and Tiller
// Routine from "The NURBS Book" - 2nd ed - Piegl and Tiller
void NURBSPatch::DegreeElevate(int dir, int t)
{
if (dir >= kv.Size() || dir < 0)
@@ -1431,8 +1503,8 @@ void NURBSPatch::DegreeElevate(int dir, int t)
KnotVector &oldkv = *kv[dir];
oldkv.GetElements();
NURBSPatch *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder() + t,
oldkv.GetNCP() + oldkv.GetNE()*t);
auto *newpatch = new NURBSPatch(this, dir, oldkv.GetOrder() + t,
oldkv.GetNCP() + oldkv.GetNE()*t);
NURBSPatch &newp = *newpatch;
KnotVector &newkv = *newp.GetKV(dir);
@@ -2377,7 +2449,7 @@ NURBSExtension::NURBSExtension(Mesh *mesh_array[], int num_pieces)
}
NURBSExtension::NURBSExtension(const Mesh *patch_topology,
const Array<const NURBSPatch*> patches_)
const Array<const NURBSPatch*> &patches_)
{
// Basic topology checks
MFEM_VERIFY(patches_.Size() > 0, "Must have at least one patch");
@@ -4587,7 +4659,7 @@ void NURBSExtension::KnotInsert(Array<Vector *> &kv)
// Flip vector
int size = pkvc[d]->Size();
int ns = ceil(size/2.0);
int ns = static_cast<int>(ceil(size/2.0));
for (int j = 0; j < ns; j++)
{
real_t tmp = apb - pkvc[d]->Elem(j);
@@ -4647,7 +4719,7 @@ void NURBSExtension::KnotRemove(Array<Vector *> &kv, real_t tol)
// Flip vector
int size = pkvc[d]->Size();
int ns = ceil(size/2.0);
int ns = static_cast<int>(ceil(size/2.0));
for (int j = 0; j < ns; j++)
{
real_t tmp = apb - pkvc[d]->Elem(j);
+20 -7
View File
@@ -22,7 +22,6 @@
#include "../general/communication.hpp"
#endif
#include <iostream>
#include <set>
namespace mfem
{
@@ -55,7 +54,7 @@ protected:
public:
/// Create an empty KnotVector.
KnotVector() { }
KnotVector() = default;
/** @brief Create a KnotVector by reading data from stream @a input. Two
integers are read, for order and number of control points. */
@@ -74,7 +73,7 @@ public:
polynomial degree). Periodicity is not supported.
*/
KnotVector(int order, const Vector& intervals,
const Array<int>& continuity );
const Array<int>& continuity);
/// Copy constructor.
KnotVector(const KnotVector &kv) { (*this) = kv; }
@@ -144,8 +143,13 @@ public:
/** @brief Global curve interpolation through the points @a x (overwritten).
@a x is an array with the length of the spatial dimension containing
vectors with spatial coordinates. The control points of the interpolated
curve are returned in @a x in the same form. */
void FindInterpolant(Array<Vector*> &x);
curve are returned in @a x in the same form.
The inverse of the collocation matrix, used in the interpolation, is
stored for repeated calls and used if @a reuse_inverse is true. Reuse is
valid only if this KnotVector has not changed since the initial call with
@a reuse_inverse false. */
void FindInterpolant(Array<Vector*> &x, bool reuse_inverse = false);
/** Set @a diff, comprised of knots in @a kv not contained in this KnotVector.
@a kv must be of the same order as this KnotVector. The current
@@ -203,6 +207,14 @@ public:
/** Flag to indicate whether the KnotVector has been coarsened, which means
it is ready for non-nested refinement. */
bool coarse;
#ifdef MFEM_USE_LAPACK
// Data for reusing banded matrix factorization in FindInterpolant().
DenseMatrix fact_AB; /// Banded matrix factorization
Array<int> fact_ipiv; /// Row pivot indices
#else
DenseMatrix A_coll_inv; /// Collocation matrix inverse
#endif
};
@@ -286,7 +298,7 @@ public:
includes the weight. The array of control point coordinates stores each
point's coordinates contiguously, and points are ordered in a standard
ijk grid ordering. */
NURBSPatch(Array<const KnotVector *> &kv_, int dim_,
NURBSPatch(Array<const KnotVector *> &kv_, int dim_,
const real_t* control_points);
/// Constructor for a patch of dimension equal to the size of @a kv.
@@ -701,7 +713,8 @@ public:
NURBSExtension(Mesh *mesh_array[], int num_pieces);
NURBSExtension(const Mesh *patch_topology, const Array<const NURBSPatch*> p);
NURBSExtension(const Mesh *patch_topology,
const Array<const NURBSPatch*> &patches_);
/// Copy assignment not supported.
NURBSExtension& operator=(const NURBSExtension&) = delete;
+2 -1
View File
@@ -3132,11 +3132,12 @@ void ParMesh::GetFaceNbrElementTransformation(
pNodes->ParFESpace()->GetFaceNbrElementVDofs(FaceNo, vdofs);
int n = vdofs.Size()/spaceDim;
pointmat.SetSize(spaceDim, n);
pNodes->FaceNbrData().HostRead();
for (int k = 0; k < spaceDim; k++)
{
for (int j = 0; j < n; j++)
{
pointmat(k,j) = (pNodes->FaceNbrData())(vdofs[n*k+j]);
pointmat(k,j) = AsConst(pNodes->FaceNbrData())(vdofs[n*k+j]);
}
}
+1 -9
View File
@@ -257,15 +257,7 @@ template <typename SubMeshT>
void AddBoundaryElements(SubMeshT &mesh,
const std::unordered_map<int,int> &lface_to_boundary_attribute)
{
mesh.Dimension();
const int num_codim_1 = [&mesh]()
{
auto Dim = mesh.Dimension();
if (Dim == 1) { return mesh.GetNV(); }
else if (Dim == 2) { return mesh.GetNEdges(); }
else if (Dim == 3) { return mesh.GetNFaces(); }
else { MFEM_ABORT("Invalid dimension."); return -1; }
}();
const int num_codim_1 = mesh.GetNumFaces();
if (mesh.Dimension() == 3)
{
+44 -42
View File
@@ -84,7 +84,7 @@ void VTKHDF::EnsureSteps()
}
hid_t VTKHDF::EnsureDataset(hid_t f, const std::string &name, hid_t type,
int ndims)
Dims &dims)
{
const char *name_c = name.c_str();
@@ -94,20 +94,23 @@ hid_t VTKHDF::EnsureDataset(hid_t f, const std::string &name, hid_t type,
if (status == 0)
{
// Dataset does not exist, create it.
Dims dims(ndims);
Dims maxdims(ndims, H5S_UNLIMITED);
const hid_t fspace = H5Screate_simple(ndims, dims, maxdims);
const int ndims = dims.ndims;
// The dataset is allowed to grow in the first dimension, but is fixed
// in size in all other dimesions; the maximum dataset size is same as
// dims, but unlimited in first dimension.
Dims max_dims = dims;
max_dims[0] = H5S_UNLIMITED;
const hid_t fspace = H5Screate_simple(ndims, dims, max_dims);
Dims chunk(ndims);
size_t chunk_size_bytes = 1024 * 1024 / 2; // 0.5 MB
const size_t t_bytes = H5Tget_size(type);
for (int i = 1; i < ndims; ++i)
{
chunk[i] = 16;
chunk_size_bytes /= 16;
chunk[i] = dims[i];
chunk_size_bytes /= dims[i];
}
chunk[0] = chunk_size_bytes / t_bytes;
for (int i = 1; i < ndims; ++i) { chunk[i] = 16; }
const hid_t dcpl = H5Pcreate(H5P_DATASET_CREATE);
H5Pset_chunk(dcpl, ndims, chunk);
if (compression_level >= 0)
@@ -124,7 +127,19 @@ hid_t VTKHDF::EnsureDataset(hid_t f, const std::string &name, hid_t type,
else if (status > 0)
{
// Dataset exists, open it.
return H5Dopen2(f, name_c, H5P_DEFAULT);
const hid_t d = H5Dopen2(f, name_c, H5P_DEFAULT);
// Resize the dataset, set dims to its new size.
Dims old_dims(dims.ndims);
const hid_t dspace = H5Dget_space(d);
const int ndims_dset = H5Sget_simple_extent_ndims(dspace);
MFEM_VERIFY(ndims_dset == dims.ndims, "");
H5Sget_simple_extent_dims(dspace, old_dims, NULL);
H5Sclose(dspace);
dims[0] += old_dims[0];
H5Dset_extent(d, dims);
return d;
}
else
{
@@ -160,27 +175,13 @@ void VTKHDF::AppendParData(hid_t f, const std::string &name, hsize_t locsize,
hsize_t offset, Dims globsize, T *data)
{
const int ndims = globsize.ndims;
const hid_t d = EnsureDataset(f, name, GetTypeID<T>(), ndims);
// Resize the dataset, set dims to its new size.
hsize_t old_size;
Dims dims(ndims);
{
const hid_t dspace = H5Dget_space(d);
const int ndims_dset = H5Sget_simple_extent_ndims(dspace);
MFEM_VERIFY(ndims_dset == ndims, "");
H5Sget_simple_extent_dims(dspace, dims, NULL);
H5Sclose(dspace);
old_size = dims[0];
dims[0] += globsize[0];
for (int i = 1; i < ndims; ++i) { dims[i] = globsize[i]; }
H5Dset_extent(d, dims);
}
Dims dims = globsize;
const hid_t d = EnsureDataset(f, name, GetTypeID<T>(), dims);
// Write the new entry.
const hid_t dspace = H5Dget_space(d);
Dims start(ndims);
start[0] = old_size + offset;
start[0] = dims[0] - globsize[0] + offset;
Dims count(ndims);
count[0] = locsize;
for (int i = 1; i < ndims; ++i) { count[i] = globsize[i]; }
@@ -334,14 +335,14 @@ void VTKHDF::Truncate(const real_t t)
}
// Index of found time index (may be 'one-past-the-end' if not found)
const int i = std::distance(tvals.begin(), it);
const ptrdiff_t i = std::distance(tvals.begin(), it);
// Only truncate if needed
const bool truncate = it != tvals.end();
// Number of steps we are keeping
nsteps = i;
H5LTset_attribute_int(vtk, "Steps", "NSteps", &nsteps, 1);
H5LTset_attribute_ulong(vtk, "Steps", "NSteps", &nsteps, 1);
// We want to continue writing immediately after step 'i - 1'. If i = 0,
// then this is at the beginning of the file, and the offsets do not need
@@ -509,7 +510,7 @@ void VTKHDF::UpdateSteps(real_t t)
// Set the NSteps attribute
++nsteps;
H5LTset_attribute_int(steps, ".", "NSteps", &nsteps, 1);
H5LTset_attribute_ulong(steps, ".", "NSteps", &nsteps, 1);
AppendValue(steps, "Values", t);
AppendValue(steps, "PartOffsets", part_offset);
@@ -618,16 +619,16 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
for (int i = 0; i < pmat.Width(); i++)
{
points.push_back(pmat(0,i));
if (pmat.Height() > 1) { points.push_back(pmat(1,i)); }
points.push_back(FP_T(pmat(0,i)));
if (pmat.Height() > 1) { points.push_back(FP_T(pmat(1,i))); }
else { points.push_back(0.0); }
if (pmat.Height() > 2) { points.push_back(pmat(2,i)); }
if (pmat.Height() > 2) { points.push_back(FP_T(pmat(2,i))); }
else { points.push_back(0.0); }
}
}
}
const hsize_t ne_0 = mesh.GetNE();
const int ne_0 = mesh.GetNE();
const hsize_t ne = high_order ? ne_0 : ne_ref;
AppendParData(vtk, "NumberOfPoints", 1, mpi_rank, mpi_dims, &np);
@@ -657,7 +658,7 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
if (high_order)
{
Array<int> local_connectivity;
for (size_t e = 0; e < ne; ++e)
for (int e = 0; e < int(ne); ++e)
{
offsets[e] = off;
const Geometry::Type geom = mesh.GetElementGeometry(e);
@@ -675,7 +676,7 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
{
int off_0 = 0;
int e_ref = 0;
for (hsize_t e = 0; e < ne_0; ++e)
for (int e = 0; e < ne_0; ++e)
{
const Geometry::Type geom = mesh.GetElementGeometry(e);
const int nv = get_nv(e);
@@ -714,12 +715,13 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
const int *vtk_geom_map =
high_order ? VTKGeometry::HighOrderMap : VTKGeometry::Map;
int e_ref = 0;
for (hsize_t e = 0; e < ne_0; ++e)
for (int e = 0; e < ne_0; ++e)
{
const int ne_ref = get_ne_ref(e, ref_0);
for (int i = 0; i < ne_ref; ++i, ++e_ref)
const int ne_ref_e = get_ne_ref(e, ref_0);
for (int i = 0; i < ne_ref_e; ++i, ++e_ref)
{
cell_types[e_ref] = vtk_geom_map[mesh.GetElementGeometry(e)];
cell_types[e_ref] = static_cast<unsigned char>(
vtk_geom_map[mesh.GetElementGeometry(e)]);
}
}
AppendParData(vtk, "Types", ne, e_offset, Dims({ne_total}),
@@ -732,11 +734,11 @@ void VTKHDF::SaveMesh(const Mesh &mesh, bool high_order, int ref)
EnsureGroup("CellData", cell_data);
std::vector<int> attributes(ne);
hsize_t e_ref = 0;
for (hsize_t e = 0; e < ne_0; ++e)
for (int e = 0; e < ne_0; ++e)
{
const int attr = mesh.GetAttribute(e);
const int ne_ref = get_ne_ref(e, ref_0);
for (int i = 0; i < ne_ref; ++i, ++e_ref)
const int ne_ref_e = get_ne_ref(e, ref_0);
for (int i = 0; i < ne_ref_e; ++i, ++e_ref)
{
attributes[e_ref] = attr;
}
@@ -772,7 +774,7 @@ void VTKHDF::SaveGridFunction(const GridFunction &gf, const std::string &name)
{
for (int vd = 0; vd < vdim; ++vd)
{
point_values[off] = vec_val(vd, i);
point_values[off] = FP_T(vec_val(vd, i));
++off;
}
}
+9 -7
View File
@@ -76,14 +76,14 @@ private:
/// Wrapper for storing dataset dimensions (max ndims is 2D in VTKHDF).
struct Dims
{
static constexpr int MAX_NDIMS = 2;
static constexpr size_t MAX_NDIMS = 2;
std::array<hsize_t, MAX_NDIMS> data = { }; // Zero initialized
int ndims = 0;
Dims() = default;
Dims(int ndims_) : ndims(ndims_) { MFEM_ASSERT(ndims <= MAX_NDIMS, ""); }
Dims(int ndims_, hsize_t val) : Dims(ndims_) { data.fill(val); }
template <typename T>
Dims(std::initializer_list<T> data_) : Dims(data_.size())
Dims(std::initializer_list<T> data_) : Dims(int(data_.size()))
{ std::copy(data_.begin(), data_.end(), data.begin()); }
operator hsize_t*() { return data.data(); }
hsize_t &operator[](int i) { return data[i]; }
@@ -97,7 +97,7 @@ private:
hid_t steps = H5I_INVALID_HID;
/// Number of time steps saved.
int nsteps = 0;
unsigned long nsteps = 0;
/// Keep track of the offsets into the data arrays at each time step.
struct Offsets
@@ -123,8 +123,8 @@ private:
class MeshId
{
const Mesh *mesh_ptr = nullptr;
int sequence = -1;
int nodes_sequence = -1;
long sequence = -1;
long nodes_sequence = -1;
bool high_order = true;
int ref = -1;
public:
@@ -187,8 +187,10 @@ private:
/// The rank (number of dimensions) of the dataset is given by @a ndims and
/// its data type is given by @a type.
///
/// The dataset will initially have zero size and unlimited maximum size.
hid_t EnsureDataset(hid_t f, const std::string &name, hid_t type, int ndims);
/// If the dataset does not exist, it will initially have size @a dims.
/// Otherwise, it will be resized to append data of size @a dims, and @a dims
/// will be set to the new total size.
hid_t EnsureDataset(hid_t f, const std::string &name, hid_t type, Dims &dims);
/// @brief Ensure the named group is open, creating it if needed. Set @a
/// group to the ID.
+1
View File
@@ -224,5 +224,6 @@ int main (int argc, char *argv[])
}
delete metric;
delete fec_mesh;
return 0;
}
+12
View File
@@ -80,6 +80,10 @@ add_mfem_miniapp(nurbs_solenoidal
LIBRARIES mfem)
add_dependencies(nurbs_solenoidal copy_miniapps_nurbs_data)
add_mfem_miniapp(nurbs_surface
MAIN nurbs_surface.cpp
LIBRARIES mfem)
if (MFEM_ENABLE_TESTING)
add_test(NAME nurbs_ex1_1d_r1_o2_ser
COMMAND $<TARGET_FILE:nurbs_ex1> -no-vis
@@ -247,6 +251,14 @@ if (MFEM_ENABLE_TESTING)
COMMAND $<TARGET_FILE:nurbs_solenoidal> -no-vis
-m ${PROJECT_SOURCE_DIR}/data/cube-nurbs.mesh -r 1 -o 2)
add_test(NAME nurbs_surface_10_10_10_10_ex1_o3_ser
COMMAND $<TARGET_FILE:nurbs_surface> -no-vis
-o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 1 -orig)
add_test(NAME nurbs_surface_10_10_40_40_ex1_o3_ser
COMMAND $<TARGET_FILE:nurbs_surface> -no-vis
-o 3 -nx 10 -ny 10 -fnx 40 -fny 14 -ex 1)
endif()
if (MFEM_USE_MPI)
+9 -2
View File
@@ -21,7 +21,7 @@ MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_ex3 nurbs_ex5 nurbs_ex24 \
nurbs_curveint nurbs_printfunc nurbs_solenoidal nurbs_naca_cmesh
nurbs_curveint nurbs_printfunc nurbs_solenoidal nurbs_naca_cmesh nurbs_surface
PAR_MINIAPPS = nurbs_ex1p nurbs_ex11p
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
@@ -158,6 +158,13 @@ nurbs_naca_cmesh-test-seq: nurbs_naca_cmesh
nurbs_printfunc-test-seq: nurbs_printfunc
@$(call mfem-test,$<,, NURBS miniapp)
SURF_ARGS_1 := -o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 1 -orig
SURF_ARGS_2 := -o 3 -nx 10 -ny 10 -fnx 40 -fny 40 -ex 1
nurbs_surface-test-seq: nurbs_surface
@$(call mfem-test,$<,, NURBS miniapp,$(SURF_ARGS_1))
@$(call mfem-test,$<,, NURBS miniapp,$(SURF_ARGS_2))
EX1P_ARGS_1 :=
EX1P_ARGS_2 := -m ../../data/pipe-nurbs-2d.mesh -o 2 -no-ibp
EX1P_ARGS_3 := -m ../../data/ball-nurbs.mesh -o 2 --weak-bc -r 0
@@ -192,6 +199,6 @@ clean-build:
clean-exec:
@rm -f refined.mesh sin-fit.mesh ex5.mesh exsol.mesh mesh.* sol.* mode_*
@rm -f naca-cmesh.mesh sol_?.gf
@rm -f naca-cmesh.mesh sol_?.gf *-Surface.mesh
@rm -rf Example1* Example3* Example5* Solenoidal_* ParaView
@rm -rf CurveInt Naca_cmesh glvis_naca-cmesh.mesh solution.dat
+655
View File
@@ -0,0 +1,655 @@
// 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.
//
// --------------------------------------------------------
// NURBS Surface: Interpolate a 3D Surface in a NURBS Patch
// --------------------------------------------------------
//
// Compile with: make nurbs_surface
//
// Sample runs: nurbs_surface -o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 1 -orig
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 40 -fny 40 -ex 1
// nurbs_surface -o 3 -nx 20 -ny 20 -fnx 10 -fny 10 -ex 1
// nurbs_surface -o 3 -nx 20 -ny 20 -fnx 40 -fny 40 -ex 1 -j 0.5
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 2 -orig
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 40 -fny 40 -ex 2
// nurbs_surface -o 3 -nx 20 -ny 20 -fnx 10 -fny 10 -ex 2
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 10 -fny 10 -ex 3 -orig
// nurbs_surface -o 3 -nx 10 -ny 10 -fnx 40 -fny 40 -ex 3
// nurbs_surface -o 3 -nx 20 -ny 20 -fnx 10 -fny 10 -ex 3
// nurbs_surface -o 3 -nx 20 -ny 10 -fnx 20 -fny 10 -ex 4 -orig
// * nurbs_surface -o 3 -nx 20 -ny 10 -fnx 80 -fny 40 -ex 4
// * nurbs_surface -o 3 -nx 40 -ny 20 -fnx 20 -fny 10 -ex 4
// * nurbs_surface -o 3 -nx 100 -ny 100 -fnx 100 -fny 100 -ex 5 -orig
// * nurbs_surface -o 3 -nx 100 -ny 100 -fnx 400 -fny 400 -ex 5
// * nurbs_surface -o 3 -nx 200 -ny 200 -fnx 100 -fny 100 -ex 5
//
// Description: This example demonstrates the use of MFEM to interpolate an
// input surface point grid in 3D using a NURBS surface. The NURBS
// surface can then be sampled to generate an output mesh of
// arbitrary resolution while staying close to the input geometry.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
// Example data for 3D point grid on surface, given by an analytic function.
void SurfaceGridExample(int example, int nx, int ny, Array3D<real_t> &vertices,
real_t jitter);
// Write a linear surface mesh with given vertex positions in v.
void WriteLinearMesh(int nx, int ny, const Array3D<real_t> &v,
const std::string &basename, bool visualization = false,
int x = 0, int y = 0, int w = 500, int h = 500);
// Given an input grid of 3D points on a surface, this class computes a NURBS
// surface of given order that interpolates the vertices of the input grid.
class SurfaceInterpolator
{
public:
/// Constructor for a given 2D point grid size and NURBS order.
SurfaceInterpolator(int num_elem_x, int num_elem_y, int order);
/// Create a surface interpolating the 2D grid of 3D points in @a input3D.
void CreateSurface(const Array3D<real_t> &input3D);
/// Sample the surface with the given grid size, storing points in
/// @a output3D.
void SampleSurface(int num_elem_x, int num_elem_y, bool compareOriginal,
Array3D<real_t> &output3D);
/** @brief Write the NURBS surface mesh to file, defined coordinate-wise by
the entries of @a cmesh. */
void WriteNURBSMesh(const std::string &basename, bool visualization = false,
int x = 0, int y = 0, int w = 500, int h = 500);
protected:
/** @brief Compute the NURBS mesh interpolating the given coordinate of the
grid of 3D points in @a input3D. */
void ComputeNURBS(int coordinate, const Array3D<real_t> &input3D);
private:
int nx, ny; // Number of elements in two directions of the surface grid
int orderNURBS; // NURBS degree
real_t hx, hy, hz; // Grid size in reference space
Array3D<real_t> initial3D; // Initial grid of points
static constexpr int dim = 3;
Array<int> ncp; // Number of control points in each direction
Array<int> nks; // Number of knot-spans in each direction
std::vector<Vector> ugrid; // Parameter space [0,1]^2 grid point coordinates
std::vector<KnotVector> kv; // KnotVectors in each direction
std::unique_ptr<NURBSPatch> patch; // Pointer to the only patch in the mesh
Mesh mesh; // NURBS mesh representing the surface
std::vector<Mesh> cmesh; // NURBS meshes representing point components
};
int main(int argc, char *argv[])
{
// Parse command-line options
int nx = 4;
int ny = 4;
int fnx = 40;
int fny = 40;
int order = 3;
int example = 1;
bool visualization = true;
bool compareOriginal = false;
real_t jitter = 0.0;
OptionsParser args(argc, argv);
args.AddOption(&example, "-ex", "--example",
"Example data");
args.AddOption(&nx, "-nx", "--nx",
"Number of elements in x");
args.AddOption(&ny, "-ny", "--ny",
"Number of elements in y");
args.AddOption(&fnx, "-fnx", "--fnx",
"Number of resampled elements in x");
args.AddOption(&fny, "-fny", "--fny",
"Number of resampled elements in y");
args.AddOption(&order, "-o", "--order",
"NURBS finite element order (polynomial degree)");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&compareOriginal, "-orig", "--compare-original", "-no-orig",
"--no-compare-original",
"Compare to the original mesh?");
args.AddOption(&jitter, "-j", "--jitter",
"Relative jittering in (0,1) to add to the input point "
"coordinates on a uniform nx x ny grid (0 by default)");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
if (compareOriginal && (fnx != nx || fny != ny))
{
cout << "Comparing to the original mesh requires the same number of "
<< "samples!\n";
return 1;
}
// Dimensions of the 3 surfaces (Input, NURBS, Output)
cout << "Input Surface: " << nx << " x " << ny << " linear elements\n";
cout << "NURBS Surface: " << nx + 1 - order << " x " << ny + 1 - order
<< " knot elements of order " << order << "\n";
cout << "Output Surface: " << fnx << " x " << fny << " linear elements\n";
// Set the vertex coordinates of the initial linear mesh
constexpr int dim = 3;
Array3D<real_t> input3D(nx + 1, ny + 1, dim);
SurfaceGridExample(example, nx, ny, input3D, jitter);
// Create a NURBS surface for the given nx, ny and order parameters that
// interpolates the input vertex coordinates
SurfaceInterpolator surf(nx, ny, order);
surf.CreateSurface(input3D);
// Compute the vertex coordinates of the output linear mesh by sampling the
// values from the NURBS surface
Array3D<real_t> output3D(fnx + 1, fny + 1, dim);
surf.SampleSurface(fnx, fny, compareOriginal, output3D);
// Save and optionally visualize the 3 surfaces (Input, NURBS, Output)
WriteLinearMesh(nx, ny, input3D, "Input-Surface", visualization, 0, 0);
surf.WriteNURBSMesh("NURBS-Surface", visualization, 502, 0);
WriteLinearMesh(fnx, fny, output3D, "Output-Surface", visualization, 1004, 0);
return 0;
}
// f(x,y) = sin(2 * pi * x) * sin(2 * pi * y)
void Function1(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
{
x = u;
y = v;
z = sin(2.0 * M_PI * u) * sin(2.0 * M_PI * v);
}
// Part of the parametric surface of a sphere, using spherical coordinates.
void Function2(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
{
constexpr real_t r = 1.0;
constexpr real_t pi_4 = M_PI * 0.25;
constexpr real_t phi0 = -3*pi_4;
constexpr real_t phi1 = 3*pi_4;
constexpr real_t theta0 = pi_4;
constexpr real_t theta1 = 3 * pi_4;
const real_t phi = (phi0 * (1.0 - v)) + (phi1 * v);
const real_t theta = (theta0 * (1.0 - u)) + (theta1 * u);
x = r * sin(theta) * cos(phi);
y = r * sin(theta) * sin(phi);
z = r * cos(theta);
}
// Helicoid surface
void Function3(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
{
x = u * cos(2.0 * M_PI * v);
y = u * sin(2.0 * M_PI * v);
z = v;
}
// Mobius strip
void Function4(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
{
constexpr int twists = 1;
const real_t a = 1.0 + 0.5 * ((2.0 * v) - 1.0) * cos(2.0 * M_PI * twists * u);
x = a * cos(2.0 * M_PI * u);
y = a * sin(2.0 * M_PI * u);
z = 0.5 * (2.0 * v - 1.0) * sin(2.0 * M_PI * twists * u);
}
// Breather surface
void Function5(real_t u, real_t v, real_t &x, real_t &y, real_t &z)
{
const real_t m = 13.2 * ((2.0 * u) - 1.0);
const real_t n = 37.4 * ((2.0 * v) - 1.0);
constexpr real_t b = 0.4;
constexpr real_t r = 1.0 - (b*b);
const real_t w = sqrt(r);
const real_t denom = b * (pow(w*cosh(b*m),2) + pow(b*sin(w*n),2));
x = -m + (2*r*cosh(b*m)*sinh(b*m)) / denom;
y = (2*w*cosh(b*m)*(-(w*cos(n)*cos(w*n)) - sin(n)*sin(w*n))) / denom;
z = (2*w*cosh(b*m)*(-(w*sin(n)*cos(w*n)) + cos(n)*sin(w*n))) / denom;
}
void SurfaceFunction(int example, real_t u, real_t v,
real_t &x, real_t &y, real_t &z)
{
switch (example)
{
case 1:
Function1(u, v, x, y, z);
break;
case 2:
Function2(u, v, x, y, z);
break;
case 3:
Function3(u, v, x, y, z);
break;
case 4:
Function4(u, v, x, y, z);
break;
default:
Function5(u, v, x, y, z);
};
}
// Example data for 3D point grid on surface, given by an analytic function.
void SurfaceExample(int example, const std::vector<Vector> &grid,
Array3D<real_t> &v3D, real_t jitter)
{
int seed = (int)time(0);
srand((unsigned)seed);
real_t h0 = grid[0][1]-grid[0][0], h1 = grid[1][1]-grid[1][0];
for (int i = 0; i < grid[0].Size(); i++)
{
for (int j = 0; j < grid[1].Size(); j++)
{
if (i != 0 && i != grid[0].Size()-1 && j != 0 && j != grid[1].Size()-1)
{
SurfaceFunction(example, grid[0][i] + rand_real()*h0*jitter,
grid[1][j] + rand_real()*h1*jitter,
v3D(i, j, 0), v3D(i, j, 1), v3D(i, j, 2));
}
else
{
SurfaceFunction(example, grid[0][i], grid[1][j],
v3D(i, j, 0), v3D(i, j, 1), v3D(i, j, 2));
}
}
}
}
void SurfaceGridExample(int example, int nx, int ny, Array3D<real_t> &vertices,
real_t jitter = 0)
{
// Define a uniform grid of the reference parameter space [0,1]^2
std::vector<Vector> uniformGrid(2);
for (int i = 0; i < 2; ++i)
{
const int n = (i == 0) ? nx : ny;
const real_t h = 1.0 / n;
uniformGrid[i].SetSize(n + 1);
for (int j = 0; j <= n; ++j) { uniformGrid[i][j] = j * h; }
}
SurfaceExample(example, uniformGrid, vertices, jitter);
}
// Write a linear surface mesh with given vertex positions in v.
void WriteLinearMesh(int nx, int ny, const Array3D<real_t> &v,
const std::string &basename, bool visualization,
int x, int y, int w, int h)
{
const int nv = (nx + 1) * (ny + 1);
const int nelem = nx * ny;
constexpr int dim = 3; // Spatial dimension
Mesh lmesh(2, nv, nelem, 0, dim);
Vector vertex(dim);
for (int i = 0; i <= nx; ++i)
{
for (int j = 0; j <= ny; ++j)
{
for (int k = 0; k < dim; ++k) { vertex[k] = v(i, j, k); }
lmesh.AddVertex(vertex);
}
}
Array<int> verts(4);
auto vID = [&](int i, int j)
{
return j + (i * (ny + 1));
};
for (int i = 0; i < nx; ++i)
{
for (int j = 0; j < ny; ++j)
{
verts[0] = vID(i, j);
verts[1] = vID(i+1, j);
verts[2] = vID(i+1, j+1);
verts[3] = vID(i, j+1);
Element* el = lmesh.NewElement(Element::QUADRILATERAL);
el->SetVertices(verts);
lmesh.AddElement(el);
}
}
lmesh.FinalizeTopology();
ofstream mesh_ofs(basename + ".mesh");
mesh_ofs.precision(8);
lmesh.Print(mesh_ofs);
if (visualization)
{
char vishost[] = "localhost";
constexpr int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "mesh\n" << lmesh
<< "window_title '" << basename << "'"
<< "window_geometry "
<< x << " " << y << " " << w << " " << h << "\n"
<< "keys PPPPPPPPAattttt******\n"
<< flush;
}
}
// Compute error of interpolation with respect to an input grid of point data.
void CheckError(const Array3D<real_t> &a, const Array3D<real_t> &b, int c,
int nx, int ny)
{
real_t maxErr = 0.0;
for (int i = 0; i <= nx; ++i)
{
for (int j = 0; j <= ny; ++j)
{
const real_t err_ij = std::abs(a(i, j, c) - b(i, j, 2));
maxErr = std::max(maxErr, err_ij);
}
}
cout << "Max error: " << maxErr << " for coordinate " << c << endl;
}
// Sample a NURBS mesh to generate a first-order mesh.
void SampleNURBS(bool uniform, int nx, int ny, const Mesh &mesh,
const Array<int> &nks, const std::vector<Vector> &ugrid,
Array3D<real_t> &vpos)
{
const GridFunction *nodes = mesh.GetNodes();
const real_t hx = 1.0 / (real_t) nx;
const real_t hy = 1.0 / (real_t) ny;
const real_t hxks = 1.0 / (real_t) nks[0];
const real_t hyks = 1.0 / (real_t) nks[1];
Vector vertex;
IntegrationPoint ip;
ip.z = 1.0;
for (int i = 0; i <= nx; ++i)
{
const real_t xref = uniform ? i * hx : ugrid[0][i];
const int nurbsElem0 = std::min((int) (xref / hxks), nks[0] - 1);
const real_t ipx = (xref - (nurbsElem0 * hxks)) / hxks;
ip.x = ipx;
for (int j = 0; j <= ny; ++j)
{
const real_t yref = uniform ? j * hy : ugrid[1][j];
const int nurbsElem1 = std::min((int) (yref / hyks), nks[1] - 1);
const real_t ipy = (yref - (nurbsElem1 * hyks)) / hyks;
ip.y = ipy;
const int nurbsElem = nurbsElem0 + (nurbsElem1 * nks[0]);
nodes->GetVectorValue(nurbsElem, ip, vertex);
for (int k = 0; k < 3; ++k)
{
vpos(i, j, k) = vertex[k];
}
}
}
}
SurfaceInterpolator::SurfaceInterpolator(int num_elem_x, int num_elem_y,
int order) :
nx(num_elem_x), ny(num_elem_y), orderNURBS(order),
ncp(dim), nks(dim), ugrid(dim - 1)
{
ncp[0] = nx + 1;
ncp[1] = ny + 1;
ncp[2] = order + 1;
for (int i = 0; i < dim; ++i)
{
nks[i] = ncp[i] - order;
Vector intervals(nks[i]);
Array<int> continuity(nks[i] + 1);
intervals = 1.0 / (real_t) nks[i];
continuity = order - 1;
continuity[0] = -1;
continuity[nks[i]] = -1;
kv.emplace_back(order, intervals, continuity);
}
patch.reset(new NURBSPatch(&kv[0], &kv[1], &kv[2], dim + 1));
hx = 1.0 / (real_t) (ncp[0] - 1);
hy = 1.0 / (real_t) (ncp[1] - 1);
hz = 1.0 / (real_t) (ncp[2] - 1);
Vector xi_args;
Array<int> i_args;
for (int i = 0; i < 2; ++i)
{
kv[i].FindMaxima(i_args, xi_args, ugrid[i]);
}
}
void SurfaceInterpolator::CreateSurface(const Array3D<real_t> &input3D)
{
cmesh.clear();
for (int c = 0; c < dim; ++c) // Loop over coordinates
{
ComputeNURBS(c, input3D);
cmesh.emplace_back(mesh);
}
initial3D = input3D;
}
void SurfaceInterpolator::SampleSurface(int num_elem_x, int num_elem_y,
bool compareOriginal,
Array3D<real_t> &output3D)
{
Array3D<real_t> vpos(num_elem_x + 1, num_elem_y + 1, dim);
for (int c = 0; c < dim; ++c) // Loop over coordinates
{
SampleNURBS(true, num_elem_x, num_elem_y, cmesh[c], nks, ugrid, vpos);
if (compareOriginal)
{
SampleNURBS(false, num_elem_x, num_elem_y, cmesh[c], nks, ugrid, vpos);
CheckError(initial3D, vpos, c, nx, ny);
}
for (int i = 0; i <= num_elem_x; ++i)
{
for (int j = 0; j <= num_elem_y; ++j)
{
output3D(i,j,c) = vpos(i,j,2);
}
}
}
}
void SurfaceInterpolator::ComputeNURBS(int coordinate,
const Array3D<real_t> &input3D)
{
Array<Vector*> x;
for (int i = 0; i < dim; ++i) { x.Append(new Vector(ncp[0])); }
for (int k = 0; k < ncp[2]; ++k)
{
const real_t z = k * hz;
// For each horizontal slice (fixed k), interpolate a 2D surface by
// sweeping curve interpolations in each direction. See Algorithm A9.4 of
// "The NURBS Book" - 2nd ed - Piegl and Tiller.
// Resize for sweep in first direction
for (int i = 0; i < dim; ++i) { x[i]->SetSize(ncp[0]); }
// Sweep in the first direction
for (int j = 0; j < ncp[1]; ++j)
{
for (int i = 0; i < ncp[0]; i++)
{
(*x[0])[i] = ugrid[0][i];
(*x[1])[i] = ugrid[1][j];
const real_t s_ij = input3D(i, j, coordinate);
(*x[2])[i] = -1.0 + z + s_ij;
}
const bool reuse_factorization = j > 0;
kv[0].FindInterpolant(x, reuse_factorization);
for (int i = 0; i < ncp[0]; i++)
{
(*patch)(i,j,k,0) = (*x[0])[i];
(*patch)(i,j,k,1) = (*x[1])[i];
(*patch)(i,j,k,2) = (*x[2])[i];
(*patch)(i,j,k,3) = 1.0; // weight
}
}
// Resize for sweep in second direction
for (int i = 0; i < dim; ++i) { x[i]->SetSize(ncp[1]); }
// Do another sweep in the second direction
for (int i = 0; i < ncp[0]; i++)
{
for (int j = 0; j < ncp[1]; ++j)
{
(*x[0])[j] = (*patch)(i,j,k,0);
(*x[1])[j] = (*patch)(i,j,k,1);
(*x[2])[j] = (*patch)(i,j,k,2);
}
const bool reuse_factorization = i > 0;
kv[1].FindInterpolant(x, reuse_factorization);
for (int j = 0; j < ncp[1]; ++j)
{
(*patch)(i,j,k,0) = (*x[0])[j];
(*patch)(i,j,k,1) = (*x[1])[j];
(*patch)(i,j,k,2) = (*x[2])[j];
}
}
}
for (auto p : x) { delete p; }
Array<const NURBSPatch*> patches(1);
patches[0] = patch.get();
Mesh patch_topology = Mesh::MakeCartesian3D(1, 1, 1, Element::HEXAHEDRON);
NURBSExtension nurbsExt(&patch_topology, patches);
mesh = Mesh(nurbsExt);
}
void SurfaceInterpolator::WriteNURBSMesh(const std::string &basename,
bool visualization,
int x, int y, int w, int h)
{
GridFunction *nodes = cmesh[0].GetNodes();
NURBSPatch patch2D(&kv[0], &kv[1], dim);
Array<const NURBSPatch*> patches(1);
patches[0] = &patch2D;
Mesh patch_topology = Mesh::MakeCartesian2D(1, 1, Element::QUADRILATERAL);
Array<int> dofs;
cmesh[0].NURBSext->GetPatchDofs(0, dofs);
MFEM_VERIFY(dofs.Size() == (nx + 1) * (ny + 1) * (orderNURBS + 1), "");
for (int j = 0; j < ncp[1]; ++j)
{
for (int i = 0; i < ncp[0]; i++)
{
const int dof = dofs[i + (ncp[0] * (j + (ncp[1] * orderNURBS)))];
for (int k = 0; k < 2; ++k) { patch2D(i,j,k) = (*nodes)[dim*dof + k]; }
patch2D(i,j,2) = 1.0; // weight
}
}
NURBSExtension nurbsExt(&patch_topology, patches);
Mesh mesh2D(nurbsExt);
FiniteElementCollection *fec = nodes->OwnFEC();
FiniteElementSpace fespace(&mesh2D, fec, dim, Ordering::byVDIM);
GridFunction nodes2D(&fespace);
const int n = mesh2D.GetNodes()->Size() / (dim - 1);
MFEM_VERIFY((dim - 1) * n == mesh2D.GetNodes()->Size(), "");
MFEM_VERIFY(dim * n == nodes2D.Size(), "");
Array<int> dofs2D;
mesh2D.NURBSext->GetPatchDofs(0, dofs2D);
for (int k = 0; k < dim; ++k)
{
const GridFunction &nodes_k = *cmesh[k].GetNodes();
for (int j = 0; j < ncp[1]; ++j)
{
for (int i = 0; i < ncp[0]; i++)
{
const int dof = dofs[i + (ncp[0] * (j + (ncp[1] * orderNURBS)))];
const int dof2D = dofs2D[i + (ncp[0] * j)];
nodes2D[(dim*dof2D) + k] = nodes_k[dim*dof + 2];
}
}
}
// Make mesh2D into a surface mesh with nodes given by nodes2D
mesh2D.NewNodes(nodes2D);
ofstream mesh_ofs(basename + ".mesh");
mesh_ofs.precision(8);
mesh2D.Print(mesh_ofs);
if (visualization)
{
char vishost[] = "localhost";
constexpr int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "mesh\n" << mesh2D
<< "window_title '" << basename << "'"
<< "window_geometry "
<< x << " " << y << " " << w << " " << h << "\n"
<< "keys PPPPPPPPAattttt******\n"
<< flush;
}
}
+45 -42
View File
@@ -52,12 +52,13 @@
// (respectively 0), essential (respectively natural) boundary condition
// will be imposed on boundary with the i-th attribute.
#include <fstream>
#include <iostream>
#include <functional>
#include "mfem.hpp"
#include "bramble_pasciak.hpp"
#include "div_free_solver.hpp"
#include <fstream>
#include <iostream>
#include <memory>
using namespace std;
using namespace mfem;
@@ -83,48 +84,54 @@ real_t natural_bc(const Vector & x);
D: subset of the boundary where natural boundary condition is imposed. */
class DarcyProblem
{
OperatorPtr M_;
OperatorPtr B_;
Vector rhs_;
Vector ess_data_;
ParGridFunction u_;
ParGridFunction p_;
OperatorPtr M_, B_;
Vector rhs_, ess_data_;
ParGridFunction u_, p_;
ParMesh mesh_;
ParBilinearForm *mVarf_;
ParMixedBilinearForm *bVarf_;
DFSSpaces dfs_spaces_;
std::function<bool (int)> refine_fn = [&](int num_refs)
{
for (int l = 0; l < num_refs; l++)
{
mesh_.UniformRefinement();
dfs_spaces_.CollectDFSData();
}
return true;
};
const bool dfs_refine_;
ParBilinearForm mVarf_;
ParMixedBilinearForm bVarf_;
VectorFunctionCoefficient ucoeff_;
FunctionCoefficient pcoeff_;
DFSSpaces dfs_spaces_;
PWConstCoefficient mass_coeff;
const IntegrationRule *irs_[Geometry::NumGeom];
public:
DarcyProblem(Mesh &mesh, int num_refines, int order, const char *coef_file,
Array<int> &ess_bdr, DFSParameters param);
HypreParMatrix& GetM() { return *M_.As<HypreParMatrix>(); }
HypreParMatrix& GetB() { return *B_.As<HypreParMatrix>(); }
const HypreParMatrix& GetM() const { return *M_.As<HypreParMatrix>(); }
const HypreParMatrix& GetB() const { return *B_.As<HypreParMatrix>(); }
const Vector& GetRHS() { return rhs_; }
const Vector& GetEssentialBC() { return ess_data_; }
const DFSData& GetDFSData() const { return dfs_spaces_.GetDFSData(); }
void ShowError(const Vector &sol, bool verbose);
void VisualizeSolution(const Vector &sol, std::string tag, int visport = 19916);
ParBilinearForm* GetMform() const { return mVarf_; }
ParMixedBilinearForm* GetBform() const { return bVarf_; }
ParBilinearForm& GetMform() { return mVarf_; }
ParMixedBilinearForm& GetBform() { return bVarf_; }
};
DarcyProblem::DarcyProblem(Mesh &mesh, int num_refs, int order,
const char *coef_file, Array<int> &ess_bdr,
DFSParameters dfs_param)
: mesh_(MPI_COMM_WORLD, mesh), ucoeff_(mesh.Dimension(), u_exact),
pcoeff_(p_exact), dfs_spaces_(order, num_refs, &mesh_, ess_bdr, dfs_param),
: mesh_(MPI_COMM_WORLD, mesh),
dfs_spaces_(order, num_refs, &mesh_, ess_bdr, dfs_param),
dfs_refine_(refine_fn(num_refs)),
mVarf_(dfs_spaces_.GetHdivFES()),
bVarf_(dfs_spaces_.GetHdivFES(), dfs_spaces_.GetL2FES()),
ucoeff_(mesh.Dimension(), u_exact),
pcoeff_(p_exact),
mass_coeff()
{
for (int l = 0; l < num_refs; l++)
{
mesh_.UniformRefinement();
dfs_spaces_.CollectDFSData();
}
Vector coef_vector(mesh.GetNE());
coef_vector = 1.0;
if (std::strcmp(coef_file, ""))
@@ -153,24 +160,20 @@ DarcyProblem::DarcyProblem(Mesh &mesh, int num_refs, int order,
gform.AddDomainIntegrator(new DomainLFIntegrator(gcoeff));
gform.Assemble();
mVarf_ = new ParBilinearForm(dfs_spaces_.GetHdivFES());
bVarf_ = new ParMixedBilinearForm(dfs_spaces_.GetHdivFES(),
dfs_spaces_.GetL2FES());
mVarf_.AddDomainIntegrator(new VectorFEMassIntegrator(mass_coeff));
mVarf_.ComputeElementMatrices();
mVarf_.Assemble();
mVarf_.EliminateEssentialBC(ess_bdr, u_, fform);
mVarf_->AddDomainIntegrator(new VectorFEMassIntegrator(mass_coeff));
mVarf_->ComputeElementMatrices();
mVarf_->Assemble();
mVarf_->EliminateEssentialBC(ess_bdr, u_, fform);
mVarf_.Finalize();
M_.Reset(mVarf_.ParallelAssemble());
mVarf_->Finalize();
M_.Reset(mVarf_->ParallelAssemble());
bVarf_->AddDomainIntegrator(new VectorFEDivergenceIntegrator);
bVarf_->Assemble();
bVarf_->SpMat() *= -1.0;
bVarf_->EliminateTrialEssentialBC(ess_bdr, u_, gform);
bVarf_->Finalize();
B_.Reset(bVarf_->ParallelAssemble());
bVarf_.AddDomainIntegrator(new VectorFEDivergenceIntegrator);
bVarf_.Assemble();
bVarf_.SpMat() *= -1.0;
bVarf_.EliminateTrialEssentialBC(ess_bdr, u_, gform);
bVarf_.Finalize();
B_.Reset(bVarf_.ParallelAssemble());
rhs_.SetSize(M_->NumRows() + B_->NumRows());
Vector rhs_block0(rhs_.GetData(), M_->NumRows());
@@ -341,8 +344,8 @@ int main(int argc, char *argv[])
// Generate components of the saddle point problem
DarcyProblem darcy(*mesh, par_ref_levels, order, coef_file, ess_bdr, param);
HypreParMatrix& M = darcy.GetM();
HypreParMatrix& B = darcy.GetB();
const HypreParMatrix &M = darcy.GetM();
const HypreParMatrix &B = darcy.GetB();
const DFSData& DFS_data = darcy.GetDFSData();
delete mesh;
+12 -15
View File
@@ -14,29 +14,27 @@
namespace mfem
{
BlockFESpaceOperator::BlockFESpaceOperator(const
std::vector<const FiniteElementSpace*> &fespaces):
BlockFESpaceOperator::BlockFESpaceOperator(const FESVector &fespaces):
Operator(GetHeight(fespaces)),
offsets(GetBlockOffsets(fespaces)),
prolongColOffsets(GetProColBlockOffsets(fespaces)),
restrictRowOffsets(GetResRowBlockOffsets(fespaces)),
A(offsets),
prolongation(offsets,prolongColOffsets),
prolongation(offsets, prolongColOffsets),
restriction(restrictRowOffsets, offsets)
{
for (size_t i = 0; i <fespaces.size(); i++)
{
// Since const_cast is required here, be sure to avoid using
// BlockOperator::GetBlock on restriction or prolongation.
prolongation.SetDiagonalBlock(i,
const_cast<Operator *>(fespaces[i]->GetProlongationMatrix()));
restriction.SetDiagonalBlock(i,
const_cast<Operator *>(fespaces[i]->GetRestrictionOperator()));
auto prolongation_matrix = fespaces[i]->GetProlongationMatrix();
auto restriction_matrix = fespaces[i]->GetRestrictionOperator();
prolongation.SetDiagonalBlock(i, const_cast<Operator *>(prolongation_matrix));
restriction.SetDiagonalBlock(i, const_cast<Operator *>(restriction_matrix));
}
}
int BlockFESpaceOperator::GetHeight(const std::vector<const FiniteElementSpace*>
&fespaces)
int BlockFESpaceOperator::GetHeight(const FESVector &fespaces)
{
int height = 0;
for (size_t i = 0; i < fespaces.size(); i++)
@@ -46,8 +44,7 @@ int BlockFESpaceOperator::GetHeight(const std::vector<const FiniteElementSpace*>
return height;
}
Array<int> BlockFESpaceOperator::GetBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces)
Array<int> BlockFESpaceOperator::GetBlockOffsets(const FESVector &fespaces)
{
Array<int> offsets(fespaces.size()+1);
offsets[0] = 0;
@@ -60,8 +57,8 @@ Array<int> BlockFESpaceOperator::GetBlockOffsets(const
return offsets;
}
Array<int> BlockFESpaceOperator::GetProColBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces)
Array<int> BlockFESpaceOperator::GetProColBlockOffsets(const FESVector
&fespaces)
{
Array<int> offsets(fespaces.size()+1);
offsets[0] = 0;
@@ -83,8 +80,8 @@ Array<int> BlockFESpaceOperator::GetProColBlockOffsets(const
return offsets;
}
Array<int> BlockFESpaceOperator::GetResRowBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces)
Array<int> BlockFESpaceOperator::GetResRowBlockOffsets(const FESVector
&fespaces)
{
Array<int> offsets(fespaces.size()+1);
std::cout << "fespaces.size() = " << fespaces.size() << std::endl;
+10 -15
View File
@@ -25,7 +25,8 @@ namespace mfem
/// L-Vectors. For example, a block may be a BilinearForm.
class BlockFESpaceOperator : public Operator
{
private:
using FESVector = std::vector<const FiniteElementSpace*>;
/// Offsets for the square "A" operator.
Array<int> offsets;
/// Column offsets for the prolongation operator.
@@ -39,33 +40,27 @@ private:
/// Maps true dofs of each block to local dofs.
BlockOperator restriction;
/// Computes height for parent operator.
static int GetHeight(const std::vector<const FiniteElementSpace*>
&fespaces);
static int GetHeight(const FESVector &fespaces);
/// Computes offsets for A BlockOperator.
static Array<int> GetBlockOffsets(const std::vector<const FiniteElementSpace*>
&fespaces);
static Array<int> GetBlockOffsets(const FESVector &fespaces);
/// Computes col_offsets for prolongation operator.
static Array<int> GetProColBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces);
static Array<int> GetProColBlockOffsets(const FESVector &fespaces);
/// Computes row_offsets for restriction operator.
static Array<int> GetResRowBlockOffsets(const
std::vector<const FiniteElementSpace*> &fespaces);
static Array<int> GetResRowBlockOffsets(const FESVector &fespaces);
public:
/// @brief Constructor for BlockFESpaceOperator.
/// @param[in] fespaces Finite element spaces for diagonal blocks. Spaces are not owned.
BlockFESpaceOperator(const std::vector<const FiniteElementSpace*> &fespaces);
BlockFESpaceOperator(const FESVector &fespaces);
const Operator* GetProlongation () const override;
const Operator* GetRestriction () const override;
void Mult(const Vector &x, Vector &y) const override {A.Mult(x,y);};
/// @brief Wraps BlockOperator::SetBlock. Eventually would like this class to inherit
/// from BlockOperator instead, but can't easily due to ownership of offset data
/// in BlockOperator being by reference.
void SetBlock( int iRow,
int iCol,
Operator * op,
real_t c = 1.0) {A.SetBlock(iRow, iCol, op, c);};
void SetBlock(int iRow, int iCol, Operator *op, real_t c = 1.0) { A.SetBlock(iRow, iCol, op, c); };
};
} // namespace mfem
#endif
#endif // MFEM_BLOCK_FESPACE_OPERATOR
+41 -49
View File
@@ -9,70 +9,65 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "bramble_pasciak.hpp"
using namespace std;
namespace mfem
{
namespace blocksolvers
namespace mfem::blocksolvers
{
/// Bramble-Pasciak Solver
BramblePasciakSolver::BramblePasciakSolver(
ParBilinearForm *mVarf,
ParMixedBilinearForm *bVarf,
const BPSParameters &param)
: DarcySolver(mVarf->ParFESpace()->GetTrueVSize(),
bVarf->TestFESpace()->GetTrueVSize())
BramblePasciakSolver::BramblePasciakSolver(ParBilinearForm &mVarf,
ParMixedBilinearForm &bVarf,
const BPSParameters &param)
: DarcySolver(mVarf.ParFESpace()->GetTrueVSize(),
bVarf.TestFESpace()->GetTrueVSize())
{
M_.reset(mVarf->ParallelAssemble());
B_.reset(bVarf->ParallelAssemble());
Q_.reset(ConstructMassPreconditioner(*mVarf, param.q_scaling));
M_.reset(mVarf.ParallelAssemble());
B_.reset(bVarf.ParallelAssemble());
Q_.reset(ConstructMassPreconditioner(mVarf, param.q_scaling));
Vector diagM;
M_->GetDiag(diagM);
auto BT = B_->Transpose();
auto invDBt = new HypreParMatrix(*BT);
std::unique_ptr<HypreParMatrix> invDBt(B_->Transpose());
invDBt->InvScaleRows(diagM);
auto S = ParMult(B_.get(), invDBt);
S_.reset(ParMult(B_.get(), invDBt.get(), true));
M0_.Reset(new HypreDiagScale(*M_));
M1_.Reset(new HypreBoomerAMG(*S));
M1_.Reset(new HypreBoomerAMG(*S_));
M1_.As<HypreBoomerAMG>()->SetPrintLevel(0);
Init(*M_, *B_, *Q_, *M0_.As<Solver>(), *M1_.As<Solver>(), param);
}
BramblePasciakSolver::BramblePasciakSolver(
HypreParMatrix &M, HypreParMatrix &B, HypreParMatrix &Q,
Solver &M0, Solver &M1,
const BPSParameters &param)
BramblePasciakSolver::BramblePasciakSolver(HypreParMatrix &M,
HypreParMatrix &B,
HypreParMatrix &Q,
Solver &M0, Solver &M1,
const BPSParameters &param)
: DarcySolver(M.NumRows(), B.NumRows())
{
Init(M, B, Q, M0, M1, param);
}
void BramblePasciakSolver::Init(
HypreParMatrix &M, HypreParMatrix &B, HypreParMatrix &Q,
Solver &M0, Solver &M1,
const BPSParameters &param)
void BramblePasciakSolver::Init(HypreParMatrix &M,
HypreParMatrix &B,
HypreParMatrix &Q,
Solver &M0, Solver &M1,
const BPSParameters &param)
{
auto Bt = new TransposeOperator(&B);
Bt_ = std::make_unique<TransposeOperator>(&B);
auto invQ = new HypreDiagScale(Q);
use_bpcg = param.use_bpcg;
if (use_bpcg)
{
oop_ = new BlockOperator(offsets_);
oop_->owns_blocks = false;
oop_ = std::make_unique<BlockOperator>(offsets_);
oop_->SetBlock(0, 0, &M);
oop_->SetBlock(0, 1, Bt);
oop_->SetBlock(0, 1, Bt_.get());
oop_->SetBlock(1, 0, &B);
// cpc_ unused in bpcg
auto temp_cpc = new BlockDiagonalPreconditioner(offsets_);
temp_cpc->owns_blocks = true;
temp_cpc->SetDiagonalBlock(0, invQ);
temp_cpc->SetDiagonalBlock(1, &M1);
// tri(1,0) = B M0 = B invQ
@@ -81,51 +76,48 @@ void BramblePasciakSolver::Init(
auto BinvQ = new ProductOperator(&B, invQ, false, false);
// tri
auto temp_tri = new BlockOperator(offsets_);
temp_tri->owns_blocks = true;
temp_tri->SetBlock(0, 0, id_m);
temp_tri->SetBlock(1, 1, id_b, -1.0);
temp_tri->SetBlock(1, 0, BinvQ);
temp_tri->owns_blocks = 1;
ppc_ = new ProductOperator(temp_cpc, temp_tri, true, true);
ppc_ = std::make_unique<ProductOperator>(temp_cpc, temp_tri, true, true);
ipc_ = new BlockOperator(offsets_);
ipc_->owns_blocks = false;
ipc_ = std::make_unique<BlockOperator>(offsets_);
ipc_->SetDiagonalBlock(0, invQ);
ipc_->owns_blocks = 1;
// bpcg
solver_.reset(new BPCGSolver(M.GetComm(), *ipc_, *ppc_));
solver_ = std::make_unique<BPCGSolver>(M.GetComm(), ipc_.get(), ppc_.get());
solver_->SetOperator(*oop_);
}
else
{
// oop_ unused in cg
auto temp_oop = new BlockOperator(offsets_);
temp_oop->owns_blocks = false;
temp_oop->SetBlock(0, 0, &M);
temp_oop->SetBlock(0, 1, Bt);
temp_oop->SetBlock(0, 1, Bt_.get());
temp_oop->SetBlock(1, 0, &B);
// ipc_ unused in cg
auto temp_ipc = new BlockOperator(offsets_);
temp_ipc->owns_blocks = false;
temp_ipc->SetDiagonalBlock(0, invQ);
temp_ipc->owns_blocks = 1;
// temp_AN = temp_oop * temp_ipc
auto temp_AN = new ProductOperator(temp_oop, temp_ipc, true, true);
// Required for updating the RHS
auto id = new IdentityOperator(M.NumRows()+B.NumRows());
map_ = new SumOperator(temp_AN, 1.0, id, -1.0, true, true);
map_ = std::make_unique<SumOperator>(temp_AN, 1.0, id, -1.0, true, true);
mop_ = std::make_unique<ProductOperator>(map_.get(), temp_oop, false, false);
mop_ = new ProductOperator(map_, temp_oop, false, true);
cpc_ = new BlockDiagonalPreconditioner(offsets_);
cpc_->owns_blocks = true;
cpc_ = std::make_unique<BlockDiagonalPreconditioner>(offsets_);
cpc_->SetDiagonalBlock(0, &M0);
cpc_->SetDiagonalBlock(1, &M1);
// (P)CG
solver_.reset(new CGSolver(M.GetComm()));
solver_ = std::make_unique<CGSolver>(M.GetComm());
solver_->SetOperator(*mop_);
solver_->SetPreconditioner(*cpc_);
}
@@ -133,7 +125,7 @@ void BramblePasciakSolver::Init(
}
HypreParMatrix *BramblePasciakSolver::ConstructMassPreconditioner(
ParBilinearForm &mVarf, real_t q_scaling)
const ParBilinearForm &mVarf, real_t q_scaling)
{
MFEM_ASSERT((q_scaling > 0.0) && (q_scaling < 1.0),
"Invalid Q-scaling factor: q_scaling = " << q_scaling );
@@ -167,7 +159,7 @@ HypreParMatrix *BramblePasciakSolver::ConstructMassPreconditioner(
Vector x(M_i.Height()), Mx(M_i.Height()), diff(M_i.Height());
real_t eval_prev = 0.0;
int iter = 0;
x.Randomize(696383552+779345*i);
x.Randomize(static_cast<int>(696383552LL+779345LL*i));
#if defined(MFEM_USE_DOUBLE)
const real_t rel_tol = 1e-12;
#elif defined(MFEM_USE_SINGLE)
@@ -400,5 +392,5 @@ void BPCGSolver::Mult(const Vector &b, Vector &x) const
final_norm = sqrt(delta);
Monitor(final_iter, final_norm, r, x, true);
}
} // namespace blocksolvers
} // namespace mfem
} // namespace mfem::blocksolvers
+19 -27
View File
@@ -49,9 +49,7 @@
#include "darcy_solver.hpp"
#include <memory>
namespace mfem
{
namespace blocksolvers
namespace mfem::blocksolvers
{
/// Parameters for the BramblePasciakSolver method
@@ -70,11 +68,11 @@ protected:
void UpdateVectors();
public:
BPCGSolver(const Operator &ipc, const Operator &ppc) { pprec = &ppc; iprec = &ipc; }
BPCGSolver(const Operator *ipc, const Operator *ppc): iprec(ipc), pprec(ppc) {}
#ifdef MFEM_USE_MPI
BPCGSolver(MPI_Comm comm_, const Operator &ipc, const Operator &ppc)
: IterativeSolver(comm_) { pprec = &ppc; iprec = &ipc; }
BPCGSolver(MPI_Comm comm_, const Operator *ipc, const Operator *ppc)
: IterativeSolver(comm_), iprec(ipc), pprec(ppc) { }
#endif
void SetOperator(const Operator &op) override
@@ -83,11 +81,9 @@ public:
void SetPreconditioner(Solver &pc) override
{ if (Mpi::Root()) { MFEM_WARNING("SetPreconditioner has no effect on BPCGSolver.\n"); } }
virtual void SetIncompletePreconditioner(const Operator &ipc)
{ iprec = &ipc; }
virtual void SetIncompletePreconditioner(const Operator *ipc) { iprec = ipc; }
virtual void SetParticularPreconditioner(const Operator &ppc)
{ pprec = &ppc; }
virtual void SetParticularPreconditioner(const Operator *ppc) { pprec = ppc; }
void Mult(const Vector &b, Vector &x) const override;
};
@@ -116,23 +112,20 @@ public:
1. P. Vassilevski, Multilevel Block Factorization Preconditioners (Appendix
F.3), Springer, 2008.
2. J. Bramble and J. Pasciak. A Preconditioning Technique for Indefinite
2. J. Bramble and J. Pasciak. A Preconditioning Technique for Indefinite
Systems Resulting From Mixed Approximations of Elliptic Problems,
Mathematics of Computation, 50:1-17, 1988. */
class BramblePasciakSolver : public DarcySolver
{
mutable bool use_bpcg;
std::unique_ptr<IterativeSolver> solver_;
BlockOperator *oop_, *ipc_;
ProductOperator *mop_;
SumOperator *map_;
ProductOperator *ppc_;
BlockDiagonalPreconditioner *cpc_;
std::unique_ptr<HypreParMatrix> M_;
std::unique_ptr<HypreParMatrix> B_;
std::unique_ptr<HypreParMatrix> Q_;
OperatorPtr M0_;
OperatorPtr M1_;
std::unique_ptr<BlockOperator> oop_, ipc_;
std::unique_ptr<ProductOperator> mop_, ppc_;
std::unique_ptr<SumOperator> map_;
std::unique_ptr<BlockDiagonalPreconditioner> cpc_;
std::unique_ptr<HypreParMatrix> M_, B_, Q_, S_;
std::unique_ptr<TransposeOperator> Bt_;
OperatorPtr M0_, M1_;
Array<int> ess_zero_dofs_;
void Init(HypreParMatrix &M, HypreParMatrix &B,
@@ -142,8 +135,8 @@ class BramblePasciakSolver : public DarcySolver
public:
/// System and mass preconditioner are constructed from bilinear forms
BramblePasciakSolver(
ParBilinearForm *mVarf,
ParMixedBilinearForm *bVarf,
ParBilinearForm &mVarf,
ParMixedBilinearForm &bVarf,
const BPSParameters &param);
/// System and mass preconditioner are user-provided
@@ -158,8 +151,8 @@ public:
element T:
M_T x_T = lambda_T diag(M_T) x_T.
We set Q_T = alpha * min(lambda_T) * diag(M_T), 0 < alpha < 1. */
static HypreParMatrix *ConstructMassPreconditioner(ParBilinearForm &mVarf,
real_t alpha = 0.5);
static HypreParMatrix *ConstructMassPreconditioner(const ParBilinearForm &mVarf,
const real_t alpha = 0.5);
void Mult(const Vector &x, Vector &y) const override;
void SetOperator(const Operator &op) override { }
@@ -167,7 +160,6 @@ public:
int GetNumIterations() const override { return solver_->GetNumIterations(); }
};
} // namespace blocksolvers
} // namespace mfem
} // namespace mfem::blocksolvers
#endif // MFEM_BP_SOLVER_HPP
+5 -6
View File
@@ -13,10 +13,9 @@
using namespace std;
namespace mfem
{
namespace blocksolvers
namespace mfem::blocksolvers
{
void SetOptions(IterativeSolver& solver, const IterSolveParameters& param)
{
solver.SetPrintLevel(param.print_level);
@@ -49,7 +48,7 @@ BDPMinresSolver::BDPMinresSolver(const HypreParMatrix& M,
prec_.SetDiagonalBlock(0, new HypreDiagScale(M));
prec_.SetDiagonalBlock(1, new HypreBoomerAMG(*S_.As<HypreParMatrix>()));
static_cast<HypreBoomerAMG&>(prec_.GetDiagonalBlock(1)).SetPrintLevel(0);
prec_.owns_blocks = true;
prec_.owns_blocks = 1;
SetOptions(solver_, param);
solver_.SetOperator(op_);
@@ -61,5 +60,5 @@ void BDPMinresSolver::Mult(const Vector & x, Vector & y) const
solver_.Mult(x, y);
for (int dof : ess_zero_dofs_) { y[dof] = 0.0; }
}
} // namespace blocksolvers
} // namespace mfem
} // namespace mfem::blocksolvers
+4 -9
View File
@@ -13,13 +13,10 @@
#define MFEM_DARCY_SOLVER_HPP
#include "mfem.hpp"
#include <memory>
#include <vector>
namespace mfem
{
namespace blocksolvers
namespace mfem::blocksolvers
{
struct IterSolveParameters
{
int print_level = 0;
@@ -32,8 +29,6 @@ struct IterSolveParameters
real_t rel_tol = 1e-5;
#else
#error "Only single and double precision are supported!"
real_t abs_tol = 1e-12;
real_t rel_tol = 1e-9;
#endif
};
@@ -68,7 +63,7 @@ public:
void SetEssZeroDofs(const Array<int>& dofs) { dofs.Copy(ess_zero_dofs_); }
int GetNumIterations() const override { return solver_.GetNumIterations(); }
};
} // namespace blocksolvers
} // namespace mfem
} // namespace mfem::blocksolvers
#endif // MFEM_DARCY_SOLVER_HPP
+106 -107
View File
@@ -13,16 +13,16 @@
using namespace std;
namespace mfem
namespace mfem::blocksolvers
{
namespace blocksolvers
static HypreParMatrix* TwoStepsRAP(const HypreParMatrix *Rt,
const HypreParMatrix *A,
const HypreParMatrix *P)
{
HypreParMatrix* TwoStepsRAP(const HypreParMatrix& Rt, const HypreParMatrix& A,
const HypreParMatrix& P)
{
OperatorPtr R(Rt.Transpose());
OperatorPtr RA(ParMult(R.As<HypreParMatrix>(), &A));
return ParMult(RA.As<HypreParMatrix>(), &P, true);
OperatorPtr R(Rt->Transpose());
OperatorPtr RA(ParMult(R.As<HypreParMatrix>(), A));
return ParMult(RA.As<HypreParMatrix>(), P, true);
}
void GetRowColumnsRef(const SparseMatrix& A, int row, Array<int>& cols)
@@ -59,34 +59,36 @@ DFSSpaces::DFSSpaces(int order, int num_refine, ParMesh *mesh,
if (mesh->Dimension() == 3)
{
hcurl_fec_.reset(new ND_FECollection(order+1, mesh->Dimension()));
hcurl_fec_ = std::make_unique<ND_FECollection>(order+1, mesh->Dimension());
}
else
{
hcurl_fec_.reset(new H1_FECollection(order+1, mesh->Dimension()));
hcurl_fec_ = std::make_unique<H1_FECollection>(order+1, mesh->Dimension());
}
all_bdr_attr_.SetSize(ess_attr.Size(), 1);
hdiv_fes_.reset(new ParFiniteElementSpace(mesh, &hdiv_fec_));
l2_fes_.reset(new ParFiniteElementSpace(mesh, &l2_fec_));
coarse_hdiv_fes_.reset(new ParFiniteElementSpace(*hdiv_fes_));
coarse_l2_fes_.reset(new ParFiniteElementSpace(*l2_fes_));
l2_0_fes_.reset(new ParFiniteElementSpace(mesh, &l2_0_fec_));
hdiv_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, &hdiv_fec_);
l2_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, &l2_fec_);
coarse_hdiv_fes_ = std::make_unique<ParFiniteElementSpace>(*hdiv_fes_);
coarse_l2_fes_ = std::make_unique<ParFiniteElementSpace>(*l2_fes_);
l2_0_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, &l2_0_fec_);
l2_0_fes_->SetUpdateOperatorType(Operator::MFEM_SPARSEMAT);
el_l2dof_.reserve(num_refine+1);
el_l2dof_.push_back(ElemToDof(*coarse_l2_fes_));
data_.agg_hdivdof.resize(num_refine);
data_.agg_l2dof.resize(num_refine);
data_.P_hdiv.resize(num_refine, OperatorPtr(Operator::Hypre_ParCSR));
data_.P_l2.resize(num_refine, OperatorPtr(Operator::Hypre_ParCSR));
data_.P_hdiv.resize(num_refine);
data_.P_l2.resize(num_refine);
data_.Q_l2.resize(num_refine);
hdiv_fes_->GetEssentialTrueDofs(ess_attr, data_.coarsest_ess_hdivdofs);
data_.C.resize(num_refine+1);
data_.Ae.resize(num_refine+1);
hcurl_fes_.reset(new ParFiniteElementSpace(mesh, hcurl_fec_.get()));
coarse_hcurl_fes_.reset(new ParFiniteElementSpace(*hcurl_fes_));
data_.P_hcurl.resize(num_refine, OperatorPtr(Operator::Hypre_ParCSR));
hcurl_fes_ = std::make_unique<ParFiniteElementSpace>(mesh, hcurl_fec_.get());
coarse_hcurl_fes_ = std::make_unique<ParFiniteElementSpace>(*hcurl_fes_);
data_.P_hcurl.resize(num_refine);
}
SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
@@ -104,8 +106,8 @@ SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
agg_tdof_T.As<HypreParMatrix>()->GetDiag(tdof_agg);
agg_tdof_T.As<HypreParMatrix>()->GetOffd(is_shared, trash);
int * I = new int [tdof_agg.NumRows()+1]();
int * J = new int[tdof_agg.NumNonZeroElems()];
int *I = new int[tdof_agg.NumRows()+1]();
int *J = new int[tdof_agg.NumNonZeroElems()];
Array<int> is_bdr;
FiniteElementSpace::ListToMarker(bdr_truedofs, tdof_agg.NumRows(), is_bdr);
@@ -119,7 +121,7 @@ SparseMatrix* AggToInteriorDof(const Array<int>& bdr_truedofs,
J[counter++] = tdof_agg.GetRowColumns(i)[0];
}
real_t * D = new real_t[I[tdof_agg.NumRows()]];
auto *D = new real_t[I[tdof_agg.NumRows()]];
std::fill_n(D, I[tdof_agg.NumRows()], 1.0);
SparseMatrix intdof_agg(I, J, D, tdof_agg.NumRows(), tdof_agg.NumCols());
@@ -146,20 +148,21 @@ void DFSSpaces::MakeDofRelationTables(int level)
void DFSSpaces::CollectDFSData()
{
auto GetP = [this](OperatorPtr& P, unique_ptr<ParFiniteElementSpace>& cfes,
ParFiniteElementSpace& fes, bool remove_zero)
auto GetP = [&](std::unique_ptr<OperatorPtr> &P,
std::unique_ptr<ParFiniteElementSpace> &cfes,
ParFiniteElementSpace& fes, const bool remove_zero)
{
fes.Update();
fes.GetTrueTransferOperator(*cfes, P);
if (remove_zero)
{
P.As<HypreParMatrix>()->DropSmallEntries(1e-16);
}
auto T = new OperatorHandle(Operator::Hypre_ParCSR);
fes.GetTrueTransferOperator(*cfes, *T);
P.reset(T);
if (remove_zero) { P->As<HypreParMatrix>()->DropSmallEntries(1e-16); }
(level_ < (int)data_.P_l2.size()-1) ? cfes->Update() : cfes.reset();
};
GetP(data_.P_hdiv[level_], coarse_hdiv_fes_, *hdiv_fes_, true);
GetP(data_.P_l2[level_], coarse_l2_fes_, *l2_fes_, false);
MakeDofRelationTables(level_);
GetP(data_.P_hcurl[level_], coarse_hcurl_fes_, *hcurl_fes_, true);
@@ -171,7 +174,9 @@ void DFSSpaces::CollectDFSData()
data_.C[level_+1].Reset(curl.ParallelAssemble());
mfem::Array<int> ess_hcurl_tdof;
hcurl_fes_->GetEssentialTrueDofs(ess_bdr_attr_, ess_hcurl_tdof);
data_.C[level_+1].As<HypreParMatrix>()->EliminateCols(ess_hcurl_tdof);
data_.Ae[level_+1].reset(
data_.C[level_+1].As<HypreParMatrix>()
->EliminateCols(ess_hcurl_tdof));
++level_;
@@ -189,7 +194,7 @@ void DFSSpaces::DataFinalize()
SparseMatrix P_l2;
for (int l = (int)data_.P_l2.size()-1; l >= 0; --l)
{
data_.P_l2[l].As<HypreParMatrix>()->GetDiag(P_l2);
data_.P_l2[l]->As<HypreParMatrix>()->GetDiag(P_l2);
OperatorPtr PT_l2(Transpose(P_l2));
auto PTW = Mult(*PT_l2.As<SparseMatrix>(), *W.As<SparseMatrix>());
auto cW = Mult(*PTW, P_l2);
@@ -245,7 +250,7 @@ SaddleSchwarzSmoother::SaddleSchwarzSmoother(const HypreParMatrix& M,
const SparseMatrix& agg_hdivdof,
const SparseMatrix& agg_l2dof,
const HypreParMatrix& P_l2,
const HypreParMatrix& Q_l2)
const ProductOperator& Q_l2)
: Solver(M.NumRows() + B.NumRows()), agg_hdivdof_(agg_hdivdof),
agg_l2dof_(agg_l2dof), solvers_loc_(agg_l2dof.NumRows())
{
@@ -312,23 +317,27 @@ void SaddleSchwarzSmoother::Mult(const Vector & x, Vector & y) const
blk_y.GetBlock(1) -= coarse_l2_projection;
}
DivFreeSolver::DivFreeSolver(const HypreParMatrix &M, const HypreParMatrix& B,
DivFreeSolver::DivFreeSolver(const HypreParMatrix &M,
const HypreParMatrix &B,
const DFSData& data)
: DarcySolver(M.NumRows(), B.NumRows()), data_(data), param_(data.param),
BT_(B.Transpose()), BBT_solver_(B, param_.BBT_solve_param),
ops_offsets_(data.P_l2.size()+1), ops_(ops_offsets_.size()),
blk_Ps_(ops_.Size()-1), smoothers_(ops_.Size())
BT_(B.Transpose()),
BBT_solver_(B, param_.BBT_solve_param),
ops_offsets_(data.P_l2.size()+1),
ops_(ops_offsets_.size()),
blk_Ps_(ops_.size()-1),
smoothers_(ops_.size())
{
ops_offsets_.back().MakeRef(DarcySolver::offsets_);
ops_.Last() = new BlockOperator(ops_offsets_.back());
ops_.Last()->SetBlock(0, 0, const_cast<HypreParMatrix*>(&M));
ops_.Last()->SetBlock(1, 0, const_cast<HypreParMatrix*>(&B));
ops_.Last()->SetBlock(0, 1, BT_.Ptr());
ops_.back() = std::make_unique<BlockOperator>(ops_offsets_.back());
ops_.back()->SetBlock(0, 0, const_cast<HypreParMatrix*>(&M));
ops_.back()->SetBlock(1, 0, const_cast<HypreParMatrix*>(&B));
ops_.back()->SetBlock(0, 1, BT_.Ptr());
for (int l = data.P_l2.size(); l >= 0; --l)
{
auto& M_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(0, 0));
auto& B_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(1, 0));
auto &M_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(0, 0));
auto &B_f = static_cast<const HypreParMatrix&>(ops_[l]->GetBlock(1, 0));
if (l == 0)
{
@@ -343,123 +352,112 @@ DivFreeSolver::DivFreeSolver(const HypreParMatrix &M, const HypreParMatrix& B,
const IterSolveParameters& param = param_.coarse_solve_param;
auto coarse_solver = new BDPMinresSolver(M_f, B_f, param);
if (ops_.Size() > 1)
if (ops_.size() > 1)
{
coarse_solver->SetEssZeroDofs(data.coarsest_ess_hdivdofs);
}
smoothers_[l] = coarse_solver;
smoothers_[l].reset(coarse_solver);
continue;
}
HypreParMatrix& P_hdiv_l = *data.P_hdiv[l-1].As<HypreParMatrix>();
HypreParMatrix& P_l2_l = *data.P_l2[l-1].As<HypreParMatrix>();
auto P_hdiv_l = data.P_hdiv[l-1]->As<HypreParMatrix>();
auto P_l2_l = data.P_l2[l-1]->As<HypreParMatrix>();
SparseMatrix& agg_hdivdof_l = *data.agg_hdivdof[l-1].As<SparseMatrix>();
SparseMatrix& agg_l2dof_l = *data.agg_l2dof[l-1].As<SparseMatrix>();
HypreParMatrix& Q_l2_l = *data.Q_l2[l-1].As<HypreParMatrix>();
HypreParMatrix* C_l = data.C[l].As<HypreParMatrix>();
ProductOperator& Q_l2_l = *data.Q_l2[l-1].As<ProductOperator>();
auto* C_l = data.C[l].As<HypreParMatrix>();
auto S0 = new SaddleSchwarzSmoother(M_f, B_f, agg_hdivdof_l,
agg_l2dof_l, P_l2_l, Q_l2_l);
agg_l2dof_l, *P_l2_l, Q_l2_l);
if (param_.coupled_solve)
{
auto S1 = new BlockDiagonalPreconditioner(ops_offsets_[l]);
S1->SetDiagonalBlock(0, new AuxSpaceSmoother(M_f, C_l));
S1->owns_blocks = true;
smoothers_[l] = new ProductSolver(ops_[l], S0, S1, false, true, true);
S1->owns_blocks = 1;
smoothers_[l] =
std::make_unique<ProductSolver>(ops_[l].get(), S0, S1, false, true, true);
}
else
{
smoothers_[l] = S0;
smoothers_[l].reset(S0);
}
HypreParMatrix* M_c = TwoStepsRAP(P_hdiv_l, M_f, P_hdiv_l);
HypreParMatrix* B_c = TwoStepsRAP(P_l2_l, B_f, P_hdiv_l);
HypreParMatrix* M_c = TwoStepsRAP(P_hdiv_l, &M_f, P_hdiv_l);
HypreParMatrix* B_c = TwoStepsRAP(P_l2_l, &B_f, P_hdiv_l);
ops_offsets_[l-1].SetSize(3, 0);
ops_offsets_[l-1][1] = M_c->NumRows();
ops_offsets_[l-1][2] = M_c->NumRows() + B_c->NumRows();
blk_Ps_[l-1] = new BlockOperator(ops_offsets_[l], ops_offsets_[l-1]);
blk_Ps_[l-1]->SetBlock(0, 0, &P_hdiv_l);
blk_Ps_[l-1]->SetBlock(1, 1, &P_l2_l);
blk_Ps_[l-1] =
std::make_unique<BlockOperator>(ops_offsets_[l], ops_offsets_[l-1]);
blk_Ps_[l-1]->SetBlock(0, 0, P_hdiv_l);
blk_Ps_[l-1]->SetBlock(1, 1, P_l2_l);
ops_[l-1] = new BlockOperator(ops_offsets_[l-1]);
ops_[l-1] =
std::make_unique<BlockOperator>(ops_offsets_[l-1]);
ops_[l-1]->SetBlock(0, 0, M_c);
ops_[l-1]->SetBlock(1, 0, B_c);
ops_[l-1]->SetBlock(0, 1, B_c->Transpose());
ops_[l-1]->owns_blocks = true;
ops_[l-1]->owns_blocks = 1;
}
Array<bool> own_ops(ops_.Size());
Array<bool> own_smoothers(smoothers_.Size());
Array<bool> own_Ps(blk_Ps_.Size());
own_ops = true;
own_smoothers = true;
own_Ps = true;
if (data_.P_l2.size() == 0) { return; }
Array<bool> own_ops(ops_.size());
Array<bool> own_smoothers(smoothers_.size());
Array<bool> own_blk_Ps(blk_Ps_.size());
own_ops = false, own_smoothers = false, own_blk_Ps = false;
Array<Solver*> smoothers(smoothers_.size());
if (param_.coupled_solve)
{
solver_.Reset(new GMRESSolver(B.GetComm()));
solver_.As<GMRESSolver>()->SetOperator(*(ops_.Last()));
prec_.Reset(new Multigrid(ops_, smoothers_, blk_Ps_,
own_ops, own_smoothers, own_Ps));
solver_.As<GMRESSolver>()->SetOperator(*(ops_.back()));
Array<BlockOperator*> ops(ops_.size()), blk_Ps(blk_Ps_.size());
for (size_t i = 0; i < ops_.size(); ++i) { ops[i] = ops_[i].get(); }
for (size_t i = 0; i < blk_Ps_.size(); ++i) { blk_Ps[i] = blk_Ps_[i].get(); }
for (size_t i = 0; i < smoothers_.size(); ++i) { smoothers[i] = smoothers_[i].get(); }
prec_.Reset(new Multigrid(ops, smoothers, blk_Ps,
own_ops, own_smoothers, own_blk_Ps));
}
else
{
Array<HypreParMatrix*> ops(data_.P_hcurl.size()+1);
Array<Solver*> smoothers(ops.Size());
Array<HypreParMatrix*> Ps(data_.P_hcurl.size());
own_Ps = false;
HypreParMatrix& C_finest = *data.C.back().As<HypreParMatrix>();
ops.Last() = TwoStepsRAP(C_finest, M, C_finest);
auto C_finest = data.C.back().As<HypreParMatrix>();
ops.Last() = TwoStepsRAP(C_finest, &M, C_finest);
ops.Last()->EliminateZeroRows();
ops.Last()->DropSmallEntries(1e-14);
solver_.Reset(new CGSolver(B.GetComm()));
solver_.As<CGSolver>()->SetOperator(*ops.Last());
smoothers.Last() = new HypreSmoother(*ops.Last());
static_cast<HypreSmoother*>(smoothers.Last())->SetOperatorSymmetry(true);
for (int l = Ps.Size()-1; l >= 0; --l)
{
Ps[l] = data_.P_hcurl[l].As<HypreParMatrix>();
ops[l] = TwoStepsRAP(*Ps[l], *ops[l+1], *Ps[l]);
Ps[l] = data_.P_hcurl[l]->As<HypreParMatrix>();
ops[l] = TwoStepsRAP(Ps[l], ops[l+1], Ps[l]);
ops[l]->DropSmallEntries(1e-14);
smoothers[l] = new HypreSmoother(*ops[l]);
static_cast<HypreSmoother*>(smoothers[l])->SetOperatorSymmetry(true);
}
prec_.Reset(new Multigrid(ops, smoothers, Ps, own_ops, own_smoothers, own_Ps));
own_ops = true, own_smoothers = true;
prec_.Reset(new Multigrid(ops, smoothers, Ps,
own_ops, own_smoothers, own_blk_Ps));
}
solver_.As<IterativeSolver>()->SetPreconditioner(*prec_.As<Solver>());
SetOptions(*solver_.As<IterativeSolver>(), param_);
}
DivFreeSolver::~DivFreeSolver()
{
if (param_.coupled_solve) { return; }
for (int i = 0; i < ops_.Size(); ++i)
{
delete ops_[i];
delete smoothers_[i];
if (i == ops_.Size() - 1) { break; }
delete blk_Ps_[i];
}
}
void DivFreeSolver::SolveParticular(const Vector& rhs, Vector& sol) const
{
std::vector<Vector> rhss(smoothers_.Size());
std::vector<Vector> sols(smoothers_.Size());
std::vector<Vector> rhss(smoothers_.size()), sols(smoothers_.size());
rhss.back().SetDataAndSize(const_cast<real_t*>(rhs.HostRead()), rhs.Size());
sols.back().SetDataAndSize(sol.HostWrite(), sol.Size());
for (int l = blk_Ps_.Size()-1; l >= 0; --l)
for (int l = blk_Ps_.size()-1; l >= 0; --l)
{
rhss[l].SetSize(blk_Ps_[l]->NumCols());
sols[l].SetSize(blk_Ps_[l]->NumCols());
@@ -470,12 +468,12 @@ void DivFreeSolver::SolveParticular(const Vector& rhs, Vector& sol) const
blk_Ps_[l]->MultTranspose(rhss[l+1], rhss[l]);
}
for (int l = 0; l < smoothers_.Size(); ++l)
for (size_t l = 0; l < smoothers_.size(); ++l)
{
smoothers_[l]->Mult(rhss[l], sols[l]);
}
for (int l = 0; l < blk_Ps_.Size(); ++l)
for (size_t l = 0; l < blk_Ps_.size(); ++l)
{
Vector P_sol(blk_Ps_[l]->NumRows());
blk_Ps_[l]->Mult(sols[l], P_sol);
@@ -507,12 +505,12 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
MFEM_VERIFY(x.Size() == offsets_[2], "MLDivFreeSolver: x size is invalid");
MFEM_VERIFY(y.Size() == offsets_[2], "MLDivFreeSolver: y size is invalid");
if (ops_.Size() == 1) { smoothers_[0]->Mult(x, y); return; }
if (ops_.size() == 1) { smoothers_[0]->Mult(x, y); return; }
BlockVector blk_y(y, offsets_);
BlockVector resid(offsets_);
ops_.Last()->Mult(y, resid);
ops_.back()->Mult(y, resid);
add(1.0, x, -1.0, resid, resid);
BlockVector correction(offsets_);
@@ -539,7 +537,7 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
ch.Clear();
ch.Start();
ops_.Last()->Mult(y, resid);
ops_.back()->Mult(y, resid);
add(1.0, x, -1.0, resid, resid);
SolveDivFree(resid.GetBlock(0), correction.GetBlock(0));
@@ -553,7 +551,7 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
ch.Clear();
ch.Start();
auto& M = dynamic_cast<const HypreParMatrix&>(ops_.Last()->GetBlock(0, 0));
auto& M = dynamic_cast<const HypreParMatrix&>(ops_.back()->GetBlock(0, 0));
M.Mult(-1.0, correction.GetBlock(0), 1.0, resid.GetBlock(0));
SolvePotential(resid.GetBlock(0), correction.GetBlock(1));
blk_y.GetBlock(1) += correction.GetBlock(1);
@@ -567,11 +565,12 @@ void DivFreeSolver::Mult(const Vector & x, Vector & y) const
int DivFreeSolver::GetNumIterations() const
{
if (ops_.Size() == 1)
if (ops_.size() == 1)
{
return static_cast<BDPMinresSolver*>(smoothers_[0])->GetNumIterations();
return static_cast<BDPMinresSolver*>
(smoothers_.at(0).get())->GetNumIterations();
}
return solver_.As<IterativeSolver>()->GetNumIterations();
}
} // namespace blocksolvers
} // namespace mfem
} // namespace mfem::blocksolvers
+24 -28
View File
@@ -13,11 +13,11 @@
#define MFEM_DIVFREE_SOLVER_HPP
#include "darcy_solver.hpp"
#include <memory>
namespace mfem
{
namespace blocksolvers
namespace mfem::blocksolvers
{
/// Parameters for the divergence free solver
struct DFSParameters : IterSolveParameters
{
@@ -35,14 +35,18 @@ struct DFSParameters : IterSolveParameters
/// Data for the divergence free solver
struct DFSData
{
std::vector<OperatorPtr> agg_hdivdof; // agglomerates to H(div) dofs table
std::vector<OperatorPtr> agg_l2dof; // agglomerates to L2 dofs table
std::vector<OperatorPtr> P_hdiv; // Interpolation matrix for H(div) space
std::vector<OperatorPtr> P_l2; // Interpolation matrix for L2 space
std::vector<OperatorPtr> P_hcurl; // Interpolation for kernel space of div
std::vector<OperatorPtr> Q_l2; // Q_l2[l] = (W_{l+1})^{-1} P_l2[l]^T W_l
Array<int> coarsest_ess_hdivdofs; // coarsest level essential H(div) dofs
std::vector<OperatorPtr> C; // discrete curl: ND -> RT, map to Null(B)
using UniqueOperatorPtr = std::unique_ptr<OperatorPtr>;
using UniqueHypreParMatrix = std::unique_ptr<HypreParMatrix>;
std::vector<OperatorPtr> agg_hdivdof; // agglomerates to H(div) dofs table
std::vector<OperatorPtr> agg_l2dof; // agglomerates to L2 dofs table
std::vector<UniqueOperatorPtr> P_hdiv; // Interpolation matrix for H(div) space
std::vector<UniqueOperatorPtr> P_l2; // Interpolation matrix for L2 space
std::vector<UniqueOperatorPtr> P_hcurl; // Interpolation for kernel space of div
std::vector<OperatorPtr> Q_l2; // Q_l2[l] = (W_{l+1})^{-1} P_l2[l]^T W_l
Array<int> coarsest_ess_hdivdofs; // coarsest level essential H(div) dofs
std::vector<OperatorPtr> C; // discrete curl: ND -> RT, map to Null(B)
std::vector<UniqueHypreParMatrix> Ae;
DFSParameters param;
};
@@ -92,8 +96,7 @@ public:
/// Compute the product B * B^T and solve it with CG preconditioned by BoomerAMG
class BBTSolver : public Solver
{
OperatorPtr BBT_;
OperatorPtr BBT_prec_;
OperatorPtr BBT_, BBT_prec_;
CGSolver BBT_solver_;
public:
BBTSolver(const HypreParMatrix &B, IterSolveParameters param);
@@ -115,14 +118,11 @@ public:
/// [ B 0 ]
class SaddleSchwarzSmoother : public Solver
{
const SparseMatrix& agg_hdivdof_;
const SparseMatrix& agg_l2dof_;
const SparseMatrix &agg_hdivdof_, &agg_l2dof_;
OperatorPtr coarse_l2_projector_;
Array<int> offsets_;
mutable Array<int> offsets_loc_;
mutable Array<int> hdivdofs_loc_;
mutable Array<int> l2dofs_loc_;
mutable Array<int> offsets_loc_, hdivdofs_loc_, l2dofs_loc_;
std::vector<OperatorPtr> solvers_loc_;
public:
/** SaddleSchwarzSmoother solves local saddle point problems defined on a
@@ -140,7 +140,7 @@ public:
const SparseMatrix& agg_hdivdof,
const SparseMatrix& agg_l2dof,
const HypreParMatrix& P_l2,
const HypreParMatrix& Q_l2);
const ProductOperator& Q_l2);
void Mult(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override { Mult(x, y); }
void SetOperator(const Operator &op) override { }
@@ -178,11 +178,10 @@ class DivFreeSolver : public DarcySolver
OperatorPtr BT_;
BBTSolver BBT_solver_;
std::vector<Array<int>> ops_offsets_;
Array<BlockOperator*> ops_;
Array<BlockOperator*> blk_Ps_;
Array<Solver*> smoothers_;
OperatorPtr prec_;
OperatorPtr solver_;
std::vector<std::unique_ptr<BlockOperator>> ops_;
std::vector<std::unique_ptr<BlockOperator>> blk_Ps_;
std::vector<std::unique_ptr<Solver>> smoothers_;
OperatorPtr prec_, solver_;
void SolveParticular(const Vector& rhs, Vector& sol) const;
void SolveDivFree(const Vector& rhs, Vector& sol) const;
@@ -190,14 +189,11 @@ class DivFreeSolver : public DarcySolver
public:
DivFreeSolver(const HypreParMatrix& M, const HypreParMatrix &B,
const DFSData& data);
~DivFreeSolver();
void Mult(const Vector &x, Vector &y) const override;
void SetOperator(const Operator &op) override { }
int GetNumIterations() const override;
};
} // namespace blocksolvers
} // namespace mfem
} // namespace mfem::blocksolvers
#endif // MFEM_DIVFREE_SOLVER_HPP
+2 -3
View File
@@ -33,8 +33,6 @@
// (2D random field with anisotropy)
// mpirun -np 4 generate_random_field -o 1 -r 3 -rp 3 -nu 4 -l1 0.09 -l2 0.03 -l3 0.05 -s 0.01 -t 0.08 -top 1 -no-rs -m ../../data/ref-square.mesh
#include <math.h>
#include <fstream>
#include <iostream>
#include <string>
#include "mfem.hpp"
@@ -260,7 +258,8 @@ int main(int argc, char *argv[])
// III.3 Solve the SPDE problem
spde::SPDESolver solver(nu, bc, &fespace, l1, l2, l3, e1, e2,
e3);
const int seed = (random_seed) ? 0 : std::numeric_limits<int>::max();
const int seed = (random_seed) ? 0 :
std::numeric_limits<int>::max() - Mpi::WorldRank();
solver.SetupRandomFieldGenerator(seed);
solver.GenerateRandomField(u);
+5 -2
View File
@@ -133,8 +133,11 @@ int main(int argc, char *argv[])
u.Save(sol_ofs);
}
soutv << "keys '.0" << std::string((int)b, '0') << "'\n" << flush;
south << "keys '.0" << std::string((int)a, '0') << "'\n" << flush;
if (visualization)
{
soutv << "keys '.0" << std::string((int)b, '0') << "'\n" << flush;
south << "keys '.0" << std::string((int)a, '0') << "'\n" << flush;
}
cout << "Which direction(s) are the two curves spinning in?\n";
+80 -45
View File
@@ -179,25 +179,42 @@ if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
endif()
#-----------------------------------------------------------
# SERIAL CUDA TESTS: cunit_tests
# SERIAL CUDA TESTS: gpu_unit_tests
#-----------------------------------------------------------
# Create CUDA 'cunit_tests' executable and test
# Create CUDA executable and test
if (MFEM_USE_CUDA)
set(CUNIT_TESTS_SRCS cunit_test_main.cpp)
set_property(SOURCE ${CUNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
mfem_add_executable(cunit_tests ${CUNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
target_link_libraries(cunit_tests mfem)
add_dependencies(cunit_tests copy_data)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} cunit_tests)
# gpu_unit_tests
set(GPU_UNIT_TESTS_SRCS gpu_unit_test_main.cpp)
set_property(SOURCE ${GPU_UNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
mfem_add_executable(gpu_unit_tests ${GPU_UNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
target_link_libraries(gpu_unit_tests mfem)
add_dependencies(gpu_unit_tests copy_data)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} gpu_unit_tests)
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME cunit_tests COMMAND cunit_tests)
add_test(NAME gpu_unit_tests COMMAND gpu_unit_tests)
endif()
endif()
#-----------------------------------------------------------
# SERIAL HIP TESTS: gpu_unit_tests
#-----------------------------------------------------------
# Create HIP 'gpu_unit_tests' executable and test
if (MFEM_USE_HIP)
# gpu_unit_tests
set(GPU_UNIT_TESTS_SRCS gpu_unit_test_main.cpp)
mfem_add_executable(gpu_unit_tests ${GPU_UNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
target_link_libraries(gpu_unit_tests mfem)
add_dependencies(gpu_unit_tests copy_data)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} gpu_unit_tests)
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME gpu_unit_tests COMMAND gpu_unit_tests)
endif()
endif()
#-----------------------------------------------------------
# SERIAL SEDOV + TMOP TESTS:
# sedov_tests_{cpu,debug,cuda,cuda_uvm}
# tmop_pa_tests_{cpu,debug,cuda}
# sedov_tests_{cpu,debug,gpu,gpu_uvm}
# tmop_pa_tests_{cpu,debug,gpu}
#-----------------------------------------------------------
# Function to add one device serial test from the tests/unit/miniapp directory.
# All device unit tests are built into a separate executable, in order to be
@@ -226,27 +243,27 @@ function(add_serial_miniapp_test name test_uvm)
add_test(NAME ${name}_tests_debug COMMAND ${name}_tests_debug)
endif()
if (MFEM_USE_CUDA)
mfem_add_executable(${name}_tests_cuda ${${NAME}_TESTS_SRCS})
target_compile_definitions(${name}_tests_cuda PUBLIC MFEM_${NAME}_DEVICE="cuda")
target_link_libraries(${name}_tests_cuda mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_cuda)
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
mfem_add_executable(${name}_tests_gpu ${${NAME}_TESTS_SRCS})
target_compile_definitions(${name}_tests_gpu PUBLIC MFEM_${NAME}_DEVICE="gpu")
target_link_libraries(${name}_tests_gpu mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_gpu)
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME ${name}_tests_cuda COMMAND ${name}_tests_cuda)
add_test(NAME ${name}_tests_gpu COMMAND ${name}_tests_gpu)
endif()
if (test_uvm)
mfem_add_executable(${name}_tests_cuda_uvm ${${NAME}_TESTS_SRCS})
target_compile_definitions(${name}_tests_cuda_uvm PUBLIC
MFEM_${NAME}_DEVICE="cuda:uvm")
target_link_libraries(${name}_tests_cuda_uvm mfem)
mfem_add_executable(${name}_tests_gpu_uvm ${${NAME}_TESTS_SRCS})
target_compile_definitions(${name}_tests_gpu_uvm PUBLIC
MFEM_${NAME}_DEVICE="gpu:uvm")
target_link_libraries(${name}_tests_gpu_uvm mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME}
${name}_tests_cuda_uvm)
${name}_tests_gpu_uvm)
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME ${name}_tests_cuda_uvm COMMAND ${name}_tests_cuda_uvm)
add_test(NAME ${name}_tests_gpu_uvm COMMAND ${name}_tests_gpu_uvm)
endif()
endif()
endif(MFEM_USE_CUDA)
endif()
endfunction(add_serial_miniapp_test)
add_serial_miniapp_test(sedov ON) # UVM ON
@@ -282,10 +299,11 @@ if (MFEM_USE_CEED)
endif()
#-----------------------------------------------------------
# PARALLEL CPU AND CUDA TESTS: {p,pc}unit_tests
# PARALLEL CPU AND CUDA TESTS: {p,pc}unit_tests and pgpu_unit_tests
#-----------------------------------------------------------
# Define executables and tests 'punit_tests' and 'pcunit_tests'
# Define executables and tests
if (MFEM_USE_MPI)
# punit_tests
if (MFEM_USE_CUDA)
set_property(SOURCE punit_test_main.cpp PROPERTY LANGUAGE CUDA)
endif()
@@ -301,27 +319,44 @@ if (MFEM_USE_MPI)
endif()
endforeach()
if (MFEM_USE_CUDA)
set(PCUNIT_TESTS_SRCS pcunit_test_main.cpp)
set_property(SOURCE ${PCUNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
mfem_add_executable(pcunit_tests ${PCUNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
add_dependencies(pcunit_tests copy_data)
target_link_libraries(pcunit_tests mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pcunit_tests)
foreach(np 1 ${MFEM_MPI_NP})
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME pcunit_tests_np=${np}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${np}
${MPIEXEC_PREFLAGS} $<TARGET_FILE:pcunit_tests>
${MPIEXEC_POSTFLAGS})
endif()
endforeach()
# pgpu_unit_tests
set(PGPU_UNIT_TESTS_SRCS pgpu_unit_test_main.cpp)
set_property(SOURCE ${PGPU_UNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
mfem_add_executable(pgpu_unit_tests ${PGPU_UNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
add_dependencies(pgpu_unit_tests copy_data)
target_link_libraries(pgpu_unit_tests mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pgpu_unit_tests)
foreach(np 1 ${MFEM_MPI_NP})
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME pgpu_unit_tests_np=${np}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${np}
${MPIEXEC_PREFLAGS} $<TARGET_FILE:pgpu_unit_tests>
${MPIEXEC_POSTFLAGS})
endif()
endforeach()
endif()
if (MFEM_USE_HIP)
# pgpu_unit_tests
set(PGPU_UNIT_TESTS_SRCS pgpu_unit_test_main.cpp)
mfem_add_executable(pgpu_unit_tests ${PGPU_UNIT_TESTS_SRCS} ${UNIT_TESTS_SRCS})
add_dependencies(pgpu_unit_tests copy_data)
target_link_libraries(pgpu_unit_tests mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pgpu_unit_tests)
foreach(np 1 ${MFEM_MPI_NP})
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME pgpu_unit_tests_np=${np}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${np}
${MPIEXEC_PREFLAGS} $<TARGET_FILE:pgpu_unit_tests>
${MPIEXEC_POSTFLAGS})
endif()
endforeach()
endif()
endif(MFEM_USE_MPI)
#-----------------------------------------------------------
# PARALLEL SEDOV + TMOP TESTS:
# psedov_tests_{cpu,debug,cuda,cuda_uvm}
# ptmop_pa_tests_{cpu,cuda}
# psedov_tests_{cpu,debug,gpu,gpu_uvm}
# ptmop_pa_tests_{cpu,gpu}
#-----------------------------------------------------------
# Function to add one MPI executable for a test.
function(add_mpi_executable_test name dev)
@@ -371,10 +406,10 @@ function(add_parallel_miniapp_test name HYPRE_MM)
list(APPEND backends debug)
endif()
endif()
if (MFEM_USE_CUDA)
list(APPEND backends cuda)
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
list(APPEND backends gpu)
if (HYPRE_MM)
list(APPEND backends cuda_uvm)
list(APPEND backends gpu_uvm)
endif()
endif()
+9 -9
View File
@@ -8,10 +8,10 @@ This directory contains MFEM's suite of unit tests, using the
MFEM's unit test suite includes a number of executables:
* `unit_tests`
* `cunit_tests` if MFEM is compiled with CUDA support
* `sedov_tests_cpu`, `sedov_tests_debug` (and `sedov_tests_cuda` and
`sedov_tests_cuda_uvm` if CUDA is enabled), testing a Sedov hydrodynamics case
* `tmop_pa_tests_cpu`, `tmop_pa_tests_debug` (and `tmop_pa_tests_cuda` if CUDA
* `gpu_unit_tests` if MFEM is compiled with CUDA/HIP support
* `sedov_tests_cpu`, `sedov_tests_debug` (and `sedov_tests_gpu` and
`sedov_tests_gpu_uvm` if GPU is enabled), testing a Sedov hydrodynamics case
* `tmop_pa_tests_cpu`, `tmop_pa_tests_debug` (and `tmop_pa_tests_gpu` if GPU
is enabled), testing TMOP with partial assembly
There are also parallel versions of these executables (prefixed with `p`), which
@@ -67,11 +67,11 @@ and those are:
serial test executables, and will only be tested with the parallel executable
(e.g. `punit_tests`). `punit_tests` will only run tests marked with
`[Parallel]`.
* `[CUDA]`, which indicates that a test will be tested with the CUDA executables
(e.g. `cunit_tests`). These tests will still be run by the standard (CPU)
executables. `cunit_tests` will only run tests marked with `[CUDA]`, and its
parallel version `pcunit_tests` will only run tests marked with _both_
`[CUDA]` and `[Parallel]`.
* `[GPU]`, which indicates that a test will be tested with the GPU executables
(e.g. `gpu_unit_tests`). These tests will still be run by the standard (CPU)
executables. `gpu_unit_tests` will only run tests marked with `[GPU]`, and its
parallel version `pgpu_unit_tests` will only run tests marked with _both_
`[GPU]` and `[Parallel]`.
* `[MFEMData]`, which indicates that a test requires access to a clone of the
MFEM data repository (see the `--data` flag below), in order to run tests on
some larger mesh files. By default, tests tagged with this tag are skipped,
+3 -3
View File
@@ -185,7 +185,7 @@ TEST_CASE("Diffusion Diagonal PA", "[PartialAssembly][AssembleDiagonal]")
{
for (int ne = 1; ne < 3; ++ne)
{
const int n_elements = pow(ne, dimension);
const int n_elements = static_cast<int>(pow(ne, dimension));
CAPTURE(dimension, n_elements);
for (int order = 1; order < 5; ++order)
@@ -359,7 +359,7 @@ TEST_CASE("Vector Diffusion Diagonal PA",
}
TEST_CASE("Hcurl/Hdiv diagonal PA",
"[CUDA][PartialAssembly][AssembleDiagonal]")
"[GPU][PartialAssembly][AssembleDiagonal]")
{
for (int dimension = 2; dimension < 4; ++dimension)
{
@@ -404,7 +404,7 @@ TEST_CASE("Hcurl/Hdiv diagonal PA",
{
for (int ne = 1; ne < 3; ++ne)
{
const int n_elements = std::pow(ne, dimension);
const int n_elements = static_cast<int>(std::pow(ne, dimension));
CAPTURE(dimension, spaceType, integrator, coeffType, n_elements);
int max_order = (dimension == 3) ? 2 : 3;
+58 -21
View File
@@ -195,7 +195,7 @@ void test_assembly_level(const char *meshname,
REQUIRE(y_test.Norml2() < 1.e-12);
}
TEST_CASE("H1 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [CUDA]")
TEST_CASE("H1 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [GPU]")
{
const bool all_tests = launch_all_non_regression_tests;
@@ -251,7 +251,7 @@ TEST_CASE("H1 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [CUDA]")
}
} // H1 Assembly Levels test case
TEST_CASE("H(div) Element Assembly", "[AssemblyLevel][CUDA]")
TEST_CASE("H(div) Element Assembly", "[AssemblyLevel][GPU]")
{
const auto fname = GENERATE(
"../../data/inline-quad.mesh",
@@ -316,7 +316,7 @@ TEST_CASE("H(div) Element Assembly", "[AssemblyLevel][CUDA]")
}
}
TEST_CASE("NormalTraceJumpIntegrator Element Assembly", "[AssemblyLevel][CUDA]")
TEST_CASE("NormalTraceJumpIntegrator Element Assembly", "[AssemblyLevel][GPU]")
{
const auto fname = GENERATE(
"../../data/inline-quad.mesh",
@@ -387,7 +387,7 @@ TEST_CASE("NormalTraceJumpIntegrator Element Assembly", "[AssemblyLevel][CUDA]")
}
}
TEST_CASE("L2 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [CUDA]")
TEST_CASE("L2 Assembly Levels", "[AssemblyLevel], [PartialAssembly], [GPU]")
{
const bool dg = true;
auto pb = GENERATE(Problem::Mass, Problem::Convection);
@@ -454,7 +454,16 @@ void CompareMatricesNonZeros(SparseMatrix &A1, const SparseMatrix &A2,
HYPRE_BigInt *cmap1=nullptr,
std::unordered_map<HYPRE_BigInt,int> *cmap2inv=nullptr)
{
REQUIRE(A1.Height() == A2.Height());
bool A1_Heigh_equals_A2_Height = A1.Height() == A2.Height();
#ifdef MFEM_USE_MPI
if (Mpi::IsInitialized() && !Mpi::IsFinalized())
{
const bool in = A1_Heigh_equals_A2_Height;
MPI_Allreduce(&in, &A1_Heigh_equals_A2_Height, 1, MPI_C_BOOL, MPI_LAND,
MPI_COMM_WORLD);
}
#endif
REQUIRE(A1_Heigh_equals_A2_Height);
int n = A1.Height();
const int *I1 = A1.HostReadI();
@@ -488,6 +497,14 @@ void CompareMatricesNonZeros(SparseMatrix &A1, const SparseMatrix &A2,
}
}
#ifdef MFEM_USE_MPI
if (Mpi::IsInitialized() && !Mpi::IsFinalized())
{
const real_t in = error;
MPI_Allreduce(&in, &error, 1, MPITypeMap<real_t>::mpi_type, MPI_MAX,
MPI_COMM_WORLD);
}
#endif
REQUIRE(error == MFEM_Approx(0.0, 1e-10));
}
@@ -559,7 +576,7 @@ void TestH1FullAssembly(Mesh &mesh, int order)
REQUIRE(B1.Normlinf() == MFEM_Approx(0.0));
}
TEST_CASE("Serial H1 Full Assembly", "[AssemblyLevel], [CUDA]")
TEST_CASE("Serial H1 Full Assembly", "[AssemblyLevel], [GPU]")
{
auto order = GENERATE(1, 2, 3);
auto mesh_fname = GENERATE(
@@ -570,7 +587,7 @@ TEST_CASE("Serial H1 Full Assembly", "[AssemblyLevel], [CUDA]")
TestH1FullAssembly(mesh, order);
}
TEST_CASE("Full Assembly Connectivity", "[AssemblyLevel], [CUDA]")
TEST_CASE("Full Assembly Connectivity", "[AssemblyLevel], [GPU]")
{
const int order = GENERATE(1, 2, 3);
const int ne = GENERATE(4, 8, 16, 32);
@@ -638,7 +655,7 @@ void TestSameHypreMatrices(OperatorHandle &A1, OperatorHandle &A2)
CompareMatricesNonZeros(*M2, *M1);
}
TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [CUDA]")
TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [GPU]")
{
auto order = GENERATE(1, 2, 3);
auto mesh_fname = GENERATE(
@@ -646,6 +663,8 @@ TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [CUDA]")
"../../data/fichera.mesh"
);
// CAPTURE(order, mesh_fname);
Mesh serial_mesh(mesh_fname);
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
serial_mesh.Clear();
@@ -675,17 +694,25 @@ TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [CUDA]")
OperatorHandle A_fa, A_legacy;
// Test that ParallelAssemble gives the same result
A_fa.Reset(a_fa.ParallelAssemble());
A_legacy.Reset(a_legacy.ParallelAssemble());
DYNAMIC_SECTION("[order: " << order << ", dim: " << dim
<< "]: (1) ParallelAssemble")
{
// Test that ParallelAssemble gives the same result
A_fa.Reset(a_fa.ParallelAssemble());
A_legacy.Reset(a_legacy.ParallelAssemble());
TestSameHypreMatrices(A_fa, A_legacy);
TestSameHypreMatrices(A_fa, A_legacy);
}
// Test that FormSystemMatrix gives the same result
a_fa.FormSystemMatrix(ess_tdof_list, A_fa);
a_legacy.FormSystemMatrix(ess_tdof_list, A_legacy);
DYNAMIC_SECTION("[order: " << order << ", dim: " << dim
<< "]: (2) FormSystemMatrix")
{
// Test that FormSystemMatrix gives the same result
a_fa.FormSystemMatrix(ess_tdof_list, A_fa);
a_legacy.FormSystemMatrix(ess_tdof_list, A_legacy);
TestSameHypreMatrices(A_fa, A_legacy);
TestSameHypreMatrices(A_fa, A_legacy);
}
// Test that FormLinearSystem gives the same result
ParGridFunction x1(&fespace);
@@ -701,13 +728,23 @@ TEST_CASE("Parallel H1 Full Assembly", "[AssemblyLevel], [Parallel], [CUDA]")
a_fa.Assemble();
a_fa.FormLinearSystem(ess_tdof_list, x1, b1, A_fa, X1, B1);
a_legacy.FormLinearSystem(ess_tdof_list, x2, b2, A_legacy, X2, B2);
DYNAMIC_SECTION("[order: " << order << ", dim: " << dim
<< "]: (3) FormLinearSystem")
{
a_fa.FormLinearSystem(ess_tdof_list, x1, b1, A_fa, X1, B1);
a_legacy.FormLinearSystem(ess_tdof_list, x2, b2, A_legacy, X2, B2);
TestSameHypreMatrices(A_fa, A_legacy);
TestSameHypreMatrices(A_fa, A_legacy);
}
B1 -= B2;
REQUIRE(B1.Normlinf() == MFEM_Approx(0.0));
DYNAMIC_SECTION("[order: " << order << ", dim: " << dim
<< "]: (4) FormLinearSystem - RHS")
{
B1 -= B2;
const real_t B_err = GlobalLpNorm(infinity(), B1.Normlinf(),
MPI_COMM_WORLD);
REQUIRE(B_err == MFEM_Approx(0.0));
}
}
#endif
+1 -1
View File
@@ -69,7 +69,7 @@ TEST_CASE("Test order of boundary integrators",
TEST_CASE("FormLinearSystem/SolutionScope",
"[BilinearForm]"
"[CUDA]")
"[GPU]")
{
// Create a simple mesh and FE space
int dim = 2, nx = 2, ny = 2, order = 2;
+1 -1
View File
@@ -14,7 +14,7 @@
using namespace mfem;
TEST_CASE("BlockOperators", "[BlockOperators], [CUDA]")
TEST_CASE("BlockOperators", "[BlockOperators], [GPU]")
{
const int dim = 2, nx = 3, ny = 3, order = 2;
Element::Type e_type = Element::QUADRILATERAL;
+1 -1
View File
@@ -231,7 +231,7 @@ void TestFDCalcCurlShape(FiniteElement* fe, ElementTransformation * T,
IntegrationPoint pt = ir->IntPoint(i);
fe->CalcCurlShape(pt, dshape);
CAPTURE(pt.x, pt.y, pt.z);
CAPTURE(pt.x, pt.y, dim == 3 ? pt.z : 0_r);
fdshape = 0.0;
for (int d=0; d<dim; d++)
+2 -2
View File
@@ -94,7 +94,7 @@ void TestCalcDivShape(FiniteElement* fe, ElementTransformation * T, int res)
if (fe->GetGeomType() == Geometry::PYRAMID &&
(ip.z >= 1.0 || ip.y > 1.0 - ip.z || ip.x > 1.0 - ip.z)) { continue; }
CAPTURE(ip.x, ip.y, ip.z);
CAPTURE(ip.x, ip.y, dim == 3 ? ip.z : 0_r);
fe->CalcDivShape(ip, weights);
@@ -215,7 +215,7 @@ void TestFDCalcDivShape(FiniteElement* fe, ElementTransformation * T, int order)
IntegrationPoint pt = ir->IntPoint(i);
fe->CalcDivShape(pt, dshape);
CAPTURE(pt.x, pt.y, pt.z);
CAPTURE(pt.x, pt.y, dim == 3 ? pt.z : 0_r);
fdshape = 0.0;
for (int d=0; d<dim; d++)
+1 -1
View File
@@ -14,7 +14,7 @@
using namespace mfem;
TEST_CASE("DG Mass Inverse", "[CUDA]")
TEST_CASE("DG Mass Inverse", "[GPU]")
{
auto mesh_filename = GENERATE(
"../../data/inline-segment.mesh",
+1 -1
View File
@@ -14,7 +14,7 @@
using namespace mfem;
TEST_CASE("FA Determinism", "[PartialAssembly][CUDA]")
TEST_CASE("FA Determinism", "[PartialAssembly][GPU]")
{
const int order = 3;
const char *mesh_filename = "../../data/star-q3.mesh";
+2 -1
View File
@@ -221,7 +221,8 @@ TEST_CASE("FE Symmetry",
const int ne = order; // Num DoFs per edge
const int nt = order * (order - 1); // Num DoF per tri face
const int nq = 2 * nt; // Num DoF per quad face
const int ni = order * pow(order - 1, 2); // Num DoF per interior dir
// Num DoF per interior dir
const int ni = order * (static_cast<int>(pow(order-1, 2)));
const int oq = 8 * ne; // Offset to first quad DoF
const int ot = oq + nq; // Offset to first tri DoF
const int oi = ot + 4 * nt; // Offset to first interior DoF
+33 -24
View File
@@ -190,7 +190,7 @@ TEST_CASE("InverseElementTransformation",
}
TEST_CASE("BatchInverseElementTransformation",
"[InverseElementTransformation], [CUDA]")
"[InverseElementTransformation], [GPU]")
{
const real_t tol = 4e-13;
@@ -268,13 +268,14 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (res_type[i] == InverseElementTransformation::Inside)
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -369,13 +370,14 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (res_type[i] == InverseElementTransformation::Inside)
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -471,13 +473,14 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (res_type[i] == InverseElementTransformation::Inside)
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -575,13 +578,14 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (res_type[i] == InverseElementTransformation::Inside)
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -675,13 +679,14 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (res_type[i] == InverseElementTransformation::Inside)
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts] -
orig_ref_space[i + d * npts]));
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts] -
orig_ref_space[i + d * npts]));
}
}
}
@@ -758,12 +763,13 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (res_type[i] == InverseElementTransformation::Inside)
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts]));
}
}
}
@@ -846,12 +852,13 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (res_type[i] == InverseElementTransformation::Inside)
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts]));
}
}
}
@@ -925,12 +932,13 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (res_type[i] == InverseElementTransformation::Inside)
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts]));
}
}
}
@@ -1010,12 +1018,13 @@ TEST_CASE("BatchInverseElementTransformation",
real_t max_err = 0;
for (int i = 0; i < npts; ++i)
{
if (res_type[i] == InverseElementTransformation::Inside)
if (AsConst(res_type)[i] == InverseElementTransformation::Inside)
{
++pts_found;
for (int d = 0; d < dim; ++d)
{
max_err = fmax(max_err, fabs(res_ref_space[i + d * npts]));
max_err = fmax(max_err,
fabs(AsConst(res_ref_space)[i + d * npts]));
}
}
}
+2 -2
View File
@@ -201,7 +201,7 @@ struct LinearFormExtTest
}
};
TEST_CASE("Linear Form Extension", "[LinearFormExtension], [CUDA]")
TEST_CASE("Linear Form Extension", "[LinearFormExtension], [GPU]")
{
const bool all = launch_all_non_regression_tests;
@@ -328,7 +328,7 @@ TEST_CASE("Linear Form Extension", "[LinearFormExtension], [CUDA]")
}
}
TEST_CASE("H(div) Linear Form Extension", "[LinearFormExtension], [CUDA]")
TEST_CASE("H(div) Linear Form Extension", "[LinearFormExtension], [GPU]")
{
const bool all = launch_all_non_regression_tests;
+10 -8
View File
@@ -140,17 +140,17 @@ void TestBatchedLOR()
TestSameMatrices(A2, A1);
}
TEST_CASE("LOR Batched H1", "[LOR][BatchedLOR][CUDA]")
TEST_CASE("LOR Batched H1", "[LOR][BatchedLOR][GPU]")
{
TestBatchedLOR<H1_FECollection,MassIntegrator,DiffusionIntegrator>();
}
TEST_CASE("LOR Batched ND", "[LOR][BatchedLOR][CUDA]")
TEST_CASE("LOR Batched ND", "[LOR][BatchedLOR][GPU]")
{
TestBatchedLOR<ND_FECollection,VectorFEMassIntegrator,CurlCurlIntegrator>();
}
TEST_CASE("LOR Batched RT", "[LOR][BatchedLOR][CUDA]")
TEST_CASE("LOR Batched RT", "[LOR][BatchedLOR][GPU]")
{
TestBatchedLOR<RT_FECollection,VectorFEMassIntegrator,DivDivIntegrator>();
}
@@ -192,6 +192,8 @@ void ParTestBatchedLOR()
"../../data/fichera-q3.mesh"
);
CAPTURE(order, mesh_fname);
Mesh serial_mesh = Mesh::LoadFromFile(mesh_fname);
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
@@ -226,22 +228,22 @@ void ParTestBatchedLOR()
TestSameMatrices(A2, A1);
}
TEST_CASE("Parallel LOR Batched H1", "[LOR][BatchedLOR][Parallel][CUDA]")
TEST_CASE("Parallel LOR Batched H1", "[LOR][BatchedLOR][Parallel][GPU]")
{
ParTestBatchedLOR<H1_FECollection,MassIntegrator,DiffusionIntegrator>();
}
TEST_CASE("Parallel LOR Batched ND", "[LOR][BatchedLOR][Parallel][CUDA]")
TEST_CASE("Parallel LOR Batched ND", "[LOR][BatchedLOR][Parallel][GPU]")
{
ParTestBatchedLOR<ND_FECollection,VectorFEMassIntegrator,CurlCurlIntegrator>();
}
TEST_CASE("Parallel LOR Batched RT", "[LOR][BatchedLOR][Parallel][CUDA]")
TEST_CASE("Parallel LOR Batched RT", "[LOR][BatchedLOR][Parallel][GPU]")
{
ParTestBatchedLOR<RT_FECollection,VectorFEMassIntegrator,DivDivIntegrator>();
}
TEST_CASE("LOR AMS", "[LOR][BatchedLOR][AMS][Parallel][CUDA]")
TEST_CASE("LOR AMS", "[LOR][BatchedLOR][AMS][Parallel][GPU]")
{
enum SpaceType { ND, RT };
auto space_type = GENERATE(ND, RT);
@@ -313,7 +315,7 @@ TEST_CASE("LOR AMS", "[LOR][BatchedLOR][AMS][Parallel][CUDA]")
}
}
TEST_CASE("LOR ADS", "[LOR][BatchedLOR][ADS][Parallel][CUDA]")
TEST_CASE("LOR ADS", "[LOR][BatchedLOR][ADS][Parallel][GPU]")
{
// Only need to test ADS in 3D
auto mesh_fname = GENERATE("../../data/fichera-q3.mesh");
@@ -20,7 +20,7 @@ TEST_CASE("OperatorJacobiSmoother", "[OperatorJacobiSmoother]")
{
for (int ne = 1; ne < 3; ++ne)
{
const int n_elements = std::pow(ne, dimension);
const int n_elements = static_cast<int>(std::pow(ne, dimension));
for (int order = 1; order < 5; ++order)
{
CAPTURE(dimension, n_elements, order);
+6 -6
View File
@@ -263,7 +263,7 @@ TEST_CASE("H1 PA Coefficient", "[PartialAssembly][Coefficient]")
}
TEST_CASE("Hcurl/Hdiv PA Coefficient",
"[CUDA][PartialAssembly][Coefficient]")
"[GPU][PartialAssembly][Coefficient]")
{
const bool all_tests = launch_all_non_regression_tests;
enum MixedSpaces {Hcurl, Hdiv, HcurlHdiv, HdivHcurl, NumSpaceTypes};
@@ -397,13 +397,13 @@ TEST_CASE("Hcurl/Hdiv PA Coefficient",
{
if (space_type == HcurlHdiv)
{
pa_form.AddDomainIntegrator(new MixedVectorCurlIntegrator(*coeff));
fa_form.AddDomainIntegrator(new MixedVectorCurlIntegrator(*coeff));
pa_form.AddDomainIntegrator(new MixedVectorCurlIntegrator(*coeff2));
fa_form.AddDomainIntegrator(new MixedVectorCurlIntegrator(*coeff2));
}
else
{
pa_form.AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(*coeff));
fa_form.AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(*coeff));
pa_form.AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(*coeff2));
fa_form.AddDomainIntegrator(new MixedVectorWeakCurlIntegrator(*coeff2));
}
}
}
@@ -510,7 +510,7 @@ TEST_CASE("Hcurl/Hdiv PA Coefficient",
}
TEST_CASE("Hcurl/Hdiv Mixed PA Coefficient",
"[CUDA][PartialAssembly][Coefficient]")
"[GPU][PartialAssembly][Coefficient]")
{
const real_t tol = 4e-12;
+1 -1
View File
@@ -113,7 +113,7 @@ real_t compare_pa_assembly(int dim, int num_elements, int order, bool transpose)
return error;
}
TEST_CASE("PAGradient", "[CUDA]")
TEST_CASE("PAGradient", "[GPU]")
{
auto transpose = GENERATE(true, false);
auto order = GENERATE(1, 2, 3, 4);
+1 -1
View File
@@ -110,7 +110,7 @@ real_t compare_pa_id_assembly(int dim, int num_elements, int order,
return error;
}
TEST_CASE("PAIdentityInterp", "[CUDA]")
TEST_CASE("PAIdentityInterp", "[GPU]")
{
auto transpose = GENERATE(true, false);
auto order = GENERATE(1, 2, 3, 4);
+13 -13
View File
@@ -188,7 +188,7 @@ void pa_divergence_transpose_testnd(int dim)
pa_mixed_transpose_test<VectorDivergenceIntegrator>(fes1, fes2);
}
TEST_CASE("PA VectorDivergence", "[PartialAssembly], [CUDA]")
TEST_CASE("PA VectorDivergence", "[PartialAssembly], [GPU]")
{
SECTION("2D")
{
@@ -280,7 +280,7 @@ void pa_gradient_transpose_testnd(int dim, FECType fec_type)
pa_mixed_transpose_test<GradientIntegrator>(fes1, fes2);
}
TEST_CASE("PA Gradient", "[PartialAssembly], [CUDA]")
TEST_CASE("PA Gradient", "[PartialAssembly], [GPU]")
{
auto fec_type = GENERATE(FECType::H1, FECType::L2_VALUE,
FECType::L2_INTEGRAL);
@@ -329,7 +329,7 @@ real_t test_nl_convection_nd(int dim)
return difference;
}
TEST_CASE("Nonlinear Convection", "[PartialAssembly], [NonlinearPA], [CUDA]")
TEST_CASE("Nonlinear Convection", "[PartialAssembly], [NonlinearPA], [GPU]")
{
SECTION("2D")
{
@@ -371,7 +371,7 @@ real_t test_vector_pa_integrator(int dim)
return difference;
}
TEST_CASE("PA Vector Mass", "[PartialAssembly], [VectorPA], [CUDA]")
TEST_CASE("PA Vector Mass", "[PartialAssembly], [VectorPA], [GPU]")
{
SECTION("2D")
{
@@ -384,7 +384,7 @@ TEST_CASE("PA Vector Mass", "[PartialAssembly], [VectorPA], [CUDA]")
}
}
TEST_CASE("PA Vector Diffusion", "[PartialAssembly], [VectorPA], [CUDA]")
TEST_CASE("PA Vector Diffusion", "[PartialAssembly], [VectorPA], [GPU]")
{
SECTION("2D")
{
@@ -507,7 +507,7 @@ void test_pa_convection(const std::string &meshname, int order, int prob,
}
// Basic unit tests for convection
TEST_CASE("PA Convection", "[PartialAssembly], [CUDA]")
TEST_CASE("PA Convection", "[PartialAssembly], [GPU]")
{
// prob:
// - 0: CG,
@@ -533,7 +533,7 @@ TEST_CASE("PA Convection", "[PartialAssembly], [CUDA]")
} // test case
// Advanced unit tests for convection
TEST_CASE("PA Convection advanced", "[PartialAssembly], [MFEMData], [CUDA]")
TEST_CASE("PA Convection advanced", "[PartialAssembly], [MFEMData], [GPU]")
{
if (launch_all_non_regression_tests)
{
@@ -612,17 +612,17 @@ static void test_pa_integrator()
REQUIRE(y_fa.Normlinf() == MFEM_Approx(0.0));
}
TEST_CASE("PA Mass", "[PartialAssembly], [CUDA]")
TEST_CASE("PA Mass", "[PartialAssembly], [GPU]")
{
test_pa_integrator<MassIntegrator>();
} // PA Mass test case
TEST_CASE("PA Diffusion", "[PartialAssembly], [CUDA]")
TEST_CASE("PA Diffusion", "[PartialAssembly], [GPU]")
{
test_pa_integrator<DiffusionIntegrator>();
} // PA Diffusion test case
TEST_CASE("PA Markers", "[PartialAssembly], [CUDA]")
TEST_CASE("PA Markers", "[PartialAssembly], [GPU]")
{
const bool all_tests = launch_all_non_regression_tests;
auto fname = GENERATE("../../data/star.mesh", "../../data/star-q3.mesh",
@@ -678,7 +678,7 @@ TEST_CASE("PA Markers", "[PartialAssembly], [CUDA]")
REQUIRE(y_fa.Normlinf() == MFEM_Approx(0.0));
}
TEST_CASE("PA Boundary Mass", "[PartialAssembly], [CUDA]")
TEST_CASE("PA Boundary Mass", "[PartialAssembly], [GPU]")
{
const bool all_tests = launch_all_non_regression_tests;
@@ -792,7 +792,7 @@ std::vector<std::string> get_dg_test_meshes()
return mesh_filenames;
}
TEST_CASE("PA DG Diffusion", "[PartialAssembly], [CUDA]")
TEST_CASE("PA DG Diffusion", "[PartialAssembly], [GPU]")
{
const auto mesh_fname = GENERATE_COPY(from_range(get_dg_test_meshes()));
const int order = GENERATE(1, 2);
@@ -809,7 +809,7 @@ TEST_CASE("PA DG Diffusion", "[PartialAssembly], [CUDA]")
#ifdef MFEM_USE_MPI
TEST_CASE("Parallel PA DG Diffusion", "[PartialAssembly][Parallel][CUDA]")
TEST_CASE("Parallel PA DG Diffusion", "[PartialAssembly][Parallel][GPU]")
{
const auto mesh_fname = GENERATE_COPY(from_range(get_dg_test_meshes()));
const int order = GENERATE(1, 2);
+1 -1
View File
@@ -155,7 +155,7 @@ TEST_CASE("Quadrature Function Coefficients",
}
}
TEST_CASE("Quadrature Function Integration", "[QuadratureFunction][CUDA]")
TEST_CASE("Quadrature Function Integration", "[QuadratureFunction][GPU]")
{
auto fname = GENERATE(
"../../data/star.mesh",
+1 -1
View File
@@ -236,7 +236,7 @@ static bool testQuadratureInterpolator(const int dim,
return true;
}
TEST_CASE("QuadratureInterpolator", "[QuadratureInterpolator][CUDA]")
TEST_CASE("QuadratureInterpolator", "[QuadratureInterpolator][GPU]")
{
SECTION("H1 tensor elements: compare tensor and non-tensor evaluations")
{
+52 -51
View File
@@ -345,8 +345,9 @@ TEST_CASE("Variable Order FiniteElementSpace",
#ifdef MFEM_USE_MPI
TEST_CASE("Parallel Variable Order FiniteElementSpace",
"[FiniteElementCollection], [FiniteElementSpace], [NCMesh]"
"[Parallel]")
"[FiniteElementCollection]"
"[FiniteElementSpace]"
"[NCMesh][Parallel]")
{
SECTION("Quad mesh")
{
@@ -359,33 +360,33 @@ TEST_CASE("Parallel Variable Order FiniteElementSpace",
// Standard H1 space with order 1 elements
H1_FECollection fe_coll(1, pmesh.Dimension());
ParFiniteElementSpace fespace(&pmesh, &fe_coll);
ParFiniteElementSpace pfes(&pmesh, &fe_coll);
REQUIRE(fespace.GlobalTrueVSize() == 9);
REQUIRE(pfes.GlobalTrueVSize() == 9);
// Convert to variable order space by p-refinement
// Increase order on all elements
for (int i = 0; i < pmesh.GetNE(); i++)
{
fespace.SetElementOrder(i, fespace.GetElementOrder(i) + 1);
pfes.SetElementOrder(i, pfes.GetElementOrder(i) + 1);
}
fespace.Update(false);
pfes.Update(false);
// DOFs for vertices + edges + elements = 9 + 12 + 4 = 25
REQUIRE(fespace.GlobalTrueVSize() == 25);
REQUIRE(pfes.GlobalTrueVSize() == 25);
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) { fespace.SetElementOrder(0, 4); }
fespace.Update(false);
if (rank == 0) { pfes.SetElementOrder(0, 4); }
pfes.Update(false);
Array<Refinement> refs;
if (rank == 0) { refs.Append(Refinement(0)); }
pmesh.GeneralRefinement(refs);
fespace.Update(false);
pfes.Update(false);
TestSolvePar(fespace);
TestSolvePar(pfes);
}
SECTION("Hex mesh")
@@ -399,32 +400,32 @@ TEST_CASE("Parallel Variable Order FiniteElementSpace",
// Standard H1 space with order 1 elements
H1_FECollection fe_coll(1, pmesh.Dimension());
ParFiniteElementSpace fespace(&pmesh, &fe_coll);
ParFiniteElementSpace pfes(&pmesh, &fe_coll);
REQUIRE(fespace.GlobalTrueVSize() == 27); // 3^3
REQUIRE(pfes.GlobalTrueVSize() == 27); // 3^3
// Convert to variable order space by p-refinement
for (int i = 0; i < pmesh.GetNE(); i++)
{
fespace.SetElementOrder(i, fespace.GetElementOrder(i) + 1);
pfes.SetElementOrder(i, pfes.GetElementOrder(i) + 1);
}
fespace.Update(false);
pfes.Update(false);
// DOFs for vertices + edges + faces + elements = 27 + 54 + 36 + 8 = 125
REQUIRE(fespace.GlobalTrueVSize() == 125); // 5^3
REQUIRE(pfes.GlobalTrueVSize() == 125); // 5^3
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) { fespace.SetElementOrder(0, 4); }
fespace.Update(false);
if (rank == 0) { pfes.SetElementOrder(0, 4); }
pfes.Update(false);
Array<Refinement> refs;
if (rank == 0) { refs.Append(Refinement(0)); }
pmesh.GeneralRefinement(refs);
fespace.Update(false);
pfes.Update(false);
TestSolvePar(fespace);
TestSolvePar(pfes);
}
SECTION("Hex mesh with intermediate orders")
@@ -579,8 +580,9 @@ TEST_CASE("Parallel Variable Order FiniteElementSpace",
}
TEST_CASE("Serial-parallel Comparison for Variable Order FiniteElementSpace",
"[FiniteElementCollection], [FiniteElementSpace], [NCMesh]"
"[Parallel]")
"[FiniteElementCollection]"
"[FiniteElementSpace]"
"[NCMesh][Parallel]")
{
int dimension = GENERATE(2, 3);
@@ -725,9 +727,9 @@ static void TestSolveVec(FiniteElementSpace &fespace)
}
#ifdef MFEM_USE_MPI
static void TestSolvePar(ParFiniteElementSpace &fespace)
static void TestSolvePar(ParFiniteElementSpace &pfes)
{
ParMesh *pmesh = fespace.GetParMesh();
ParMesh *pmesh = pfes.GetParMesh();
// exact solution and RHS for the problem -\Delta u = 1
FunctionCoefficient exsol(exact_sln);
@@ -738,19 +740,19 @@ static void TestSolvePar(ParFiniteElementSpace &fespace)
ess_attr = 1;
Array<int> ess_tdof_list;
fespace.GetEssentialTrueDofs(ess_attr, ess_tdof_list);
pfes.GetEssentialTrueDofs(ess_attr, ess_tdof_list);
ParGridFunction x(&fespace);
ParGridFunction x(&pfes);
x = 0.0;
x.ProjectBdrCoefficient(exsol, ess_attr);
// assemble the linear form
ParLinearForm lf(&fespace);
ParLinearForm lf(&pfes);
lf.AddDomainIntegrator(new DomainLFIntegrator(rhs));
lf.Assemble();
// assemble the bilinear form.
ParBilinearForm bf(&fespace);
ParBilinearForm bf(&pfes);
bf.AddDomainIntegrator(new DiffusionIntegrator());
bf.Assemble();
@@ -760,7 +762,7 @@ static void TestSolvePar(ParFiniteElementSpace &fespace)
// solve
HypreBoomerAMG prec;
CGSolver cg(fespace.GetComm());
CGSolver cg(pfes.GetComm());
cg.SetRelTol(1e-30);
cg.SetMaxIter(100);
cg.SetPrintLevel(1);
@@ -809,24 +811,24 @@ void TestSolveSerial1(const Mesh & mesh, GridFunction & x)
bf.RecoverFEMSolution(X, lf, x);
}
void TestSolveParallel1(ParMesh & mesh, ParGridFunction & x)
void TestSolveParallel1(ParMesh &pmesh, ParGridFunction &x)
{
ParFiniteElementSpace *fespace = x.ParFESpace();
ParFiniteElementSpace *pfes = x.ParFESpace();
Array<int> ess_attr(mesh.bdr_attributes.Max());
Array<int> ess_attr(pmesh.bdr_attributes.Max());
ess_attr = 1; // Dirichlet BC
Array<int> ess_tdof_list;
fespace->GetEssentialTrueDofs(ess_attr, ess_tdof_list);
pfes->GetEssentialTrueDofs(ess_attr, ess_tdof_list);
// assemble the linear form
ParLinearForm lf(fespace);
ParLinearForm lf(pfes);
ConstantCoefficient one(1.0);
lf.AddDomainIntegrator(new DomainLFIntegrator(one));
lf.Assemble();
// assemble the bilinear form.
ParBilinearForm bf(fespace);
ParBilinearForm bf(pfes);
bf.AddDomainIntegrator(new DiffusionIntegrator());
bf.Assemble();
@@ -849,8 +851,8 @@ void TestSolveParallel1(ParMesh & mesh, ParGridFunction & x)
GridFunction *TestRandomPRefinement_serial(Mesh & mesh)
{
// standard H1 space with order 1 elements
H1_FECollection *fec = new H1_FECollection(1, mesh.Dimension());
FiniteElementSpace *fespace = new FiniteElementSpace(&mesh, fec);
auto *fec = new H1_FECollection(1, mesh.Dimension());
auto *fespace = new FiniteElementSpace(&mesh, fec);
for (int i=0; i<mesh.GetNE(); ++i)
{
@@ -860,7 +862,8 @@ GridFunction *TestRandomPRefinement_serial(Mesh & mesh)
fespace->Update(false);
GridFunction *sol = new GridFunction(fespace);
auto *sol = new GridFunction(fespace);
sol->MakeOwner(fec);
*sol = 0.0; // Essential DOF value
TestSolveSerial1(mesh, *sol);
return sol;
@@ -870,21 +873,22 @@ ParGridFunction *TestRandomPRefinement_parallel(Mesh & mesh)
{
// standard H1 space with order 1 elements
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, mesh);
H1_FECollection *pfec = new H1_FECollection(1, mesh.Dimension());
ParFiniteElementSpace *pfespace = new ParFiniteElementSpace(pmesh, pfec);
auto *pmsh = new ParMesh(MPI_COMM_WORLD, mesh);
auto *pfec = new H1_FECollection(1, mesh.Dimension());
auto *pfes = new ParFiniteElementSpace(pmsh, pfec);
for (int i=0; i<pmesh->GetNE(); ++i)
for (int i=0; i<pmsh->GetNE(); ++i)
{
const int p = pmesh->GetAttribute(i);
if (p > 1) { pfespace->SetElementOrder(i, p); }
const int p = pmsh->GetAttribute(i);
if (p > 1) { pfes->SetElementOrder(i, p); }
}
pfespace->Update(false);
pfes->Update(false);
ParGridFunction *sol = new ParGridFunction(pfespace);
auto *sol = new ParGridFunction(pfes);
sol->MakeOwner(pfec);
*sol = 0.0; // Essential DOF value
TestSolveParallel1(*pmesh, *sol);
TestSolveParallel1(*pmsh, *sol);
return sol;
}
@@ -1056,12 +1060,9 @@ static void TestRandomPRefinement(Mesh & mesh)
const real_t discontinuity = CheckH1Continuity(*solParallel);
REQUIRE(discontinuity == MFEM_Approx(0.0));
FiniteElementSpace *fespace = solSerial->FESpace();
ParFiniteElementSpace *pfespace = solParallel->ParFESpace();
delete solParallel->ParFESpace()->GetParMesh();
delete solSerial;
delete solParallel;
delete fespace;
delete pfespace;
}
static void TestSolveParVec(ParFiniteElementSpace &fespace)
+1 -1
View File
@@ -16,7 +16,7 @@ using namespace mfem;
TEST_CASE("MemoryManager/Scopes",
"[MemoryManager]"
"[CUDA]")
"[GPU]")
{
SECTION("WithNewMemoryAndSize")
{
+16 -16
View File
@@ -20,7 +20,7 @@
using namespace mfem;
TEST_CASE("Reduce Sum", "[Reduction],[CUDA]")
TEST_CASE("Reduce Sum", "[Reduction],[GPU]")
{
Array<int> workspace;
Array<int> a(1000);
@@ -38,16 +38,16 @@ TEST_CASE("Reduce Sum", "[Reduction],[CUDA]")
a.Size(), res, [=] MFEM_HOST_DEVICE(int i, int &r) { r += dptr[i]; },
SumReducer<int> {}, use_dev, workspace);
// correct for even-length summations
int expected = (a[0] + a[a.Size() - 1]) * a.Size() / 2;
int expected = (AsConst(a)[0] + AsConst(a)[a.Size() - 1]) * a.Size() / 2;
CAPTURE(use_dev);
REQUIRE(res == expected);
}
}
TEST_CASE("Reduce Mult", "[Reduction],[CUDA]")
TEST_CASE("Reduce Mult", "[Reduction],[GPU]")
{
Array<long long> workspace;
Array<long long> a(100);
Array<long long> a(64);
a.HostReadWrite();
for (int i = 0; i < a.Size(); ++i)
{
@@ -77,14 +77,14 @@ TEST_CASE("Reduce Mult", "[Reduction],[CUDA]")
a.Size(), res,
[=] MFEM_HOST_DEVICE(int i, long long &r) { r *= dptr[i]; },
MultReducer<long long> {}, use_dev, workspace);
long long expected = 5527454985320660992;
long long expected = 21936950640377856;
CAPTURE(use_dev);
REQUIRE(res == expected);
}
}
}
TEST_CASE("Reduce BAnd", "[Reduction],[CUDA]")
TEST_CASE("Reduce BAnd", "[Reduction],[GPU]")
{
Array<unsigned> workspace;
Array<unsigned> a(10);
@@ -139,7 +139,7 @@ TEST_CASE("Reduce BAnd", "[Reduction],[CUDA]")
}
}
TEST_CASE("Reduce BOr", "[Reduction],[CUDA]")
TEST_CASE("Reduce BOr", "[Reduction],[GPU]")
{
Array<unsigned> workspace;
Array<unsigned> a(0x210);
@@ -163,7 +163,7 @@ TEST_CASE("Reduce BOr", "[Reduction],[CUDA]")
}
}
TEST_CASE("Reduce Min", "[Reduction],[CUDA]")
TEST_CASE("Reduce Min", "[Reduction],[GPU]")
{
Array<int> workspace;
Array<int> a(1000);
@@ -196,7 +196,7 @@ TEST_CASE("Reduce Min", "[Reduction],[CUDA]")
}
}
TEST_CASE("Reduce Max", "[Reduction],[CUDA]")
TEST_CASE("Reduce Max", "[Reduction],[GPU]")
{
Array<int> workspace;
Array<int> a(1000);
@@ -229,7 +229,7 @@ TEST_CASE("Reduce Max", "[Reduction],[CUDA]")
}
}
TEST_CASE("Reduce MinMax", "[Reduction],[CUDA]")
TEST_CASE("Reduce MinMax", "[Reduction],[GPU]")
{
Array<DevicePair<int, int>> workspace;
Array<int> a(1000);
@@ -269,7 +269,7 @@ TEST_CASE("Reduce MinMax", "[Reduction],[CUDA]")
}
}
TEST_CASE("Reduce ArgMin", "[Reduction],[CUDA]")
TEST_CASE("Reduce ArgMin", "[Reduction],[GPU]")
{
Array<DevicePair<double, int>> workspace;
Array<double> a(1000);
@@ -306,7 +306,7 @@ TEST_CASE("Reduce ArgMin", "[Reduction],[CUDA]")
}
}
TEST_CASE("Reduce ArgMax", "[Reduction],[CUDA]")
TEST_CASE("Reduce ArgMax", "[Reduction],[GPU]")
{
Array<DevicePair<double, int>> workspace;
Array<double> a(1000);
@@ -342,11 +342,11 @@ TEST_CASE("Reduce ArgMax", "[Reduction],[CUDA]")
REQUIRE(res.first == a.Size() - 11);
REQUIRE(res.second >= 0);
REQUIRE(res.second < a.Size());
REQUIRE(a[res.second] == res.first);
REQUIRE(AsConst(a)[res.second] == res.first);
}
}
TEST_CASE("Reduce ArgMinMax", "[Reduction],[CUDA]")
TEST_CASE("Reduce ArgMinMax", "[Reduction],[GPU]")
{
Array<MinMaxLocScalar<double, int>> workspace;
Array<double> a(1000);
@@ -388,11 +388,11 @@ TEST_CASE("Reduce ArgMinMax", "[Reduction],[CUDA]")
REQUIRE(res.min_val == -10);
REQUIRE(res.min_loc >= 0);
REQUIRE(res.min_loc < a.Size());
REQUIRE(a[res.min_loc] == res.min_val);
REQUIRE(AsConst(a)[res.min_loc] == res.min_val);
REQUIRE(res.max_val == a.Size() - 11);
REQUIRE(res.max_loc >= 0);
REQUIRE(res.max_loc < a.Size());
REQUIRE(a[res.max_loc] == res.max_val);
REQUIRE(AsConst(a)[res.max_loc] == res.max_val);
}
}
@@ -16,13 +16,13 @@
int main(int argc, char *argv[])
{
#ifdef MFEM_USE_SINGLE
std::cout << "\nThe serial CUDA unit tests are not supported in single"
std::cout << "\nThe serial GPU unit tests are not supported in single"
" precision.\n\n";
return MFEM_SKIP_RETURN_VALUE;
#endif
mfem::Device device("cuda");
mfem::Device device("gpu");
// Include only tests labeled with CUDA. Exclude parallel tests.
return RunCatchSession(argc, argv, {"[CUDA]", "~[Parallel]"});
// Include only tests labeled with GPU. Exclude parallel tests.
return RunCatchSession(argc, argv, {"[GPU]", "~[Parallel]"});
}
+9 -4
View File
@@ -98,7 +98,7 @@ double fexact(const Vector &x) // returns -\Delta u
#ifdef DIRECT_SOLVE_SERIAL
TEST_CASE("Serial Direct Solvers", "[CUDA]")
TEST_CASE("Serial Direct Solvers", "[GPU]")
{
const int ne = 2;
for (int dim = 1; dim < 4; ++dim)
@@ -186,13 +186,15 @@ TEST_CASE("Serial Direct Solvers", "[CUDA]")
#ifdef DIRECT_SOLVE_PARALLEL
TEST_CASE("Parallel Direct Solvers", "[Parallel], [CUDA]")
TEST_CASE("Parallel Direct Solvers", "[Parallel], [GPU]")
{
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
const int ne = 4;
for (int dim = 1; dim < 4; ++dim)
{
CAPTURE(dim);
Mesh mesh;
if (dim == 1)
{
@@ -251,6 +253,7 @@ TEST_CASE("Parallel Direct Solvers", "[Parallel], [CUDA]")
XX[1] = &X1;
#ifdef MFEM_USE_MUMPS
SECTION("MUMPSSolver")
{
MUMPSSolver mumps(MPI_COMM_WORLD);
mumps.SetPrintLevel(0);
@@ -278,8 +281,9 @@ TEST_CASE("Parallel Direct Solvers", "[Parallel], [CUDA]")
}
#endif
#ifdef MFEM_USE_SUPERLU
// Transform to monolithic HypreParMatrix
SECTION("SuperLUSolver")
{
// Transform to monolithic HypreParMatrix
SuperLURowLocMatrix SA(*A.As<HypreParMatrix>());
SuperLUSolver superlu(MPI_COMM_WORLD);
superlu.SetPrintStatistics(false);
@@ -316,8 +320,9 @@ TEST_CASE("Parallel Direct Solvers", "[Parallel], [CUDA]")
}
#endif
#ifdef MFEM_USE_STRUMPACK
// Transform to monolithic HypreParMatrix
SECTION("STRUMPACKSolver")
{
// Transform to monolithic HypreParMatrix
STRUMPACKRowLocMatrix SA(*A.As<HypreParMatrix>());
STRUMPACKSolver strumpack(MPI_COMM_WORLD);
strumpack.SetPrintFactorStatistics(false);
+13 -12
View File
@@ -11,6 +11,7 @@
#include "unit_tests.hpp"
#include "mfem.hpp"
#include <memory>
namespace mfem
{
@@ -85,7 +86,7 @@ void GeneratePart(PartType part_type, int nelems, int world_size,
}
}
TEST_CASE("HypreBoomerAMG", "[Parallel], [HypreBoomerAMG]")
TEST_CASE("HypreBoomerAMG", "[Parallel][HypreBoomerAMG]")
{
int world_size, rank;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
@@ -98,14 +99,14 @@ TEST_CASE("HypreBoomerAMG", "[Parallel], [HypreBoomerAMG]")
Mesh mesh = Mesh::MakeCartesian3D(n, n, n, Element::HEXAHEDRON);
int nelems = mesh.GetNE();
int *partitioning = new int[nelems];
auto partitioning = std::make_unique<int[]>(nelems);
PartType last_type = (world_size == 1) ? ALL : ALL_BUT_FIRST;
for (int part_type = ALL; part_type <= last_type; part_type++)
{
GeneratePart((PartType)part_type, nelems, world_size, partitioning);
GeneratePart((PartType)part_type, nelems, world_size, partitioning.get());
ParMesh pmesh(MPI_COMM_WORLD, mesh, partitioning);
ParMesh pmesh(MPI_COMM_WORLD, mesh, partitioning.get());
H1_FECollection fec(order, dim);
ParFiniteElementSpace fespace(&pmesh, &fec);
@@ -150,7 +151,7 @@ TEST_CASE("HypreBoomerAMG", "[Parallel], [HypreBoomerAMG]")
}
}
TEST_CASE("HypreAMS", "[Parallel], [HypreAMS]")
TEST_CASE("HypreAMS", "[Parallel][HypreAMS]")
{
int world_size, rank;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
@@ -165,14 +166,14 @@ TEST_CASE("HypreAMS", "[Parallel], [HypreAMS]")
Mesh::MakeCartesian3D(n, n, n, Element::HEXAHEDRON);
int nelems = mesh.GetNE();
int *partitioning = new int[nelems];
auto partitioning = std::make_unique<int[]>(nelems);
PartType last_type = (world_size == 1) ? ALL : ALL_BUT_FIRST;
for (int part_type = ALL; part_type <= last_type; part_type++)
{
GeneratePart((PartType)part_type, nelems, world_size, partitioning);
GeneratePart((PartType)part_type, nelems, world_size, partitioning.get());
ParMesh pmesh(MPI_COMM_WORLD, mesh, partitioning);
ParMesh pmesh(MPI_COMM_WORLD, mesh, partitioning.get());
ND_FECollection fec(order, dim);
ParFiniteElementSpace fespace(&pmesh, &fec);
@@ -218,7 +219,7 @@ TEST_CASE("HypreAMS", "[Parallel], [HypreAMS]")
}
}
TEST_CASE("HypreADS", "[Parallel], [HypreADS]")
TEST_CASE("HypreADS", "[Parallel][HypreADS]")
{
int world_size, rank;
MPI_Comm_size(MPI_COMM_WORLD, &world_size);
@@ -231,14 +232,14 @@ TEST_CASE("HypreADS", "[Parallel], [HypreADS]")
Mesh mesh = Mesh::MakeCartesian3D(n, n, n, Element::HEXAHEDRON);
int nelems = mesh.GetNE();
int *partitioning = new int[nelems];
auto partitioning = std::make_unique<int[]>(nelems);
PartType last_type = (world_size == 1) ? ALL : ALL_BUT_FIRST;
for (int part_type = ALL; part_type <= last_type; part_type++)
{
GeneratePart((PartType)part_type, nelems, world_size, partitioning);
GeneratePart((PartType)part_type, nelems, world_size, partitioning.get());
ParMesh pmesh(MPI_COMM_WORLD, mesh, partitioning);
ParMesh pmesh(MPI_COMM_WORLD, mesh, partitioning.get());
RT_FECollection fec(order, dim);
ParFiniteElementSpace fespace(&pmesh, &fec);
+1 -1
View File
@@ -359,7 +359,7 @@ TEST_CASE("LUFactors RightSolve", "[DenseMatrix]")
}
TEST_CASE("Batched Linear Algebra",
"[DenseMatrix][CUDA]")
"[DenseMatrix][GPU]")
{
auto backend = GENERATE(BatchedLinAlg::NATIVE,
BatchedLinAlg::GPU_BLAS,
+1 -1
View File
@@ -18,7 +18,7 @@ namespace mfem
#ifdef MFEM_USE_MPI
TEST_CASE("HypreParMatrixWrapConstructors-SyncChecks", "[Parallel], [CUDA]")
TEST_CASE("HypreParMatrixWrapConstructors-SyncChecks", "[Parallel], [GPU]")
{
const int dim = 2;
const int n1d = 6;
+2 -2
View File
@@ -23,7 +23,7 @@ TEST_CASE("FormLinearSystem", "[FormLinearSystem]")
{
for (int ne = 1; ne <= 4; ++ne)
{
const int n_elements = std::pow(ne, dim);
const int n_elements = static_cast<int>(std::pow(ne, dim));
CAPTURE(dim, n_elements);
for (int order = 1; order <= 3; ++order)
{
@@ -91,7 +91,7 @@ TEST_CASE("ParallelFormLinearSystem", "[Parallel], [ParallelFormLinearSystem]")
{
for (int ne = 4; ne <= 5; ++ne)
{
const int n_elements = std::pow(ne, dim);
const int n_elements = static_cast<int>(std::pow(ne, dim));
CAPTURE(dim, n_elements);
for (int order = 1; order <= 3; ++order)
{
+1 -1
View File
@@ -235,7 +235,7 @@ TEST_CASE("Vector Tests", "[Vector]")
}
}
TEST_CASE("Vector Sum", "[Vector],[CUDA]")
TEST_CASE("Vector Sum", "[Vector],[GPU]")
{
Vector x(1024);
x.Randomize(1);
+35 -33
View File
@@ -38,51 +38,53 @@ DATA_DIR = data
SEQ_MAIN_OBJ = unit_test_main.o
PAR_MAIN_OBJ = punit_test_main.o
CUDA_MAIN_OBJ = cunit_test_main.o
PCUDA_MAIN_OBJ = pcunit_test_main.o
GPU_MAIN_OBJ = gpu_unit_test_main.o
PGPU_MAIN_OBJ = pgpu_unit_test_main.o
# Sedov numerical seq/par files and tests
SEDOV_FILES = $(SRC)miniapps/test_sedov.cpp
USE_CUDA := $(MFEM_USE_CUDA:NO=)
USE_HIP := $(MFEM_USE_HIP:NO=)
USE_GPU := $(or $(USE_CUDA),$(USE_HIP))
SEQ_SEDOV_TESTS = sedov_tests_cpu sedov_tests_debug
SEQ_SEDOV_TESTS += $(if $(USE_CUDA),sedov_tests_cuda)
SEQ_SEDOV_TESTS += $(if $(USE_CUDA),sedov_tests_cuda_uvm)
SEQ_SEDOV_TESTS += $(if $(USE_GPU),sedov_tests_gpu)
SEQ_SEDOV_TESTS += $(if $(USE_GPU),sedov_tests_gpu_uvm)
PAR_SEDOV_TESTS = $(SEQ_SEDOV_TESTS:%=p%)
SEQ_SEDOV_CPU_OBJ_FILES = $(SEDOV_FILES:$(SRC)%.cpp=%.cpu.o)
SEQ_SEDOV_DEBUG_OBJ_FILES = $(SEDOV_FILES:$(SRC)%.cpp=%.debug.o)
SEQ_SEDOV_CUDA_OBJ_FILES = $(if $(USE_CUDA),$(SEDOV_FILES:$(SRC)%.cpp=%.cuda.o))
SEQ_SEDOV_CUDA_UVM_OBJ_FILES = $(if $(USE_CUDA),$(SEDOV_FILES:$(SRC)%.cpp=%.cuda_uvm.o))
SEQ_SEDOV_GPU_OBJ_FILES = $(if $(USE_GPU),$(SEDOV_FILES:$(SRC)%.cpp=%.gpu.o))
SEQ_SEDOV_GPU_UVM_OBJ_FILES = $(if $(USE_GPU),$(SEDOV_FILES:$(SRC)%.cpp=%.gpu_uvm.o))
PAR_SEDOV_CPU_OBJ_FILES = $(SEDOV_FILES:$(SRC)%.cpp=%.pcpu.o)
PAR_SEDOV_DEBUG_OBJ_FILES = $(SEDOV_FILES:$(SRC)%.cpp=%.pdebug.o)
PAR_SEDOV_CUDA_OBJ_FILES = $(if $(USE_CUDA),$(SEDOV_FILES:$(SRC)%.cpp=%.pcuda.o))
PAR_SEDOV_CUDA_UVM_OBJ_FILES = $(if $(USE_CUDA),$(SEDOV_FILES:$(SRC)%.cpp=%.pcuda_uvm.o))
PAR_SEDOV_GPU_OBJ_FILES = $(if $(USE_GPU),$(SEDOV_FILES:$(SRC)%.cpp=%.pgpu.o))
PAR_SEDOV_GPU_UVM_OBJ_FILES = $(if $(USE_GPU),$(SEDOV_FILES:$(SRC)%.cpp=%.pgpu_uvm.o))
# TMOP numerical seq/par files and tests
TMOP_FILES = $(SRC)miniapps/test_tmop_pa.cpp
SEQ_TMOP_TESTS = tmop_pa_tests_cpu tmop_pa_tests_debug
SEQ_TMOP_TESTS += $(if $(USE_CUDA),tmop_pa_tests_cuda)
# SEQ_TMOP_TESTS += $(if $(USE_CUDA),tmop_tests_cuda_uvm)
SEQ_TMOP_TESTS += $(if $(USE_GPU),tmop_pa_tests_gpu)
# SEQ_TMOP_TESTS += $(if $(USE_GPU),tmop_tests_gpu_uvm)
PAR_TMOP_TESTS = ptmop_pa_tests_cpu
PAR_TMOP_TESTS += $(if $(USE_CUDA),ptmop_pa_tests_cuda)
PAR_TMOP_TESTS += $(if $(USE_GPU),ptmop_pa_tests_gpu)
SEQ_TMOP_CPU_OBJ_FILES = $(TMOP_FILES:$(SRC)%.cpp=%.cpu.o)
SEQ_TMOP_DEBUG_OBJ_FILES = $(TMOP_FILES:$(SRC)%.cpp=%.debug.o)
SEQ_TMOP_CUDA_OBJ_FILES = $(if $(USE_CUDA),$(TMOP_FILES:$(SRC)%.cpp=%.cuda.o))
SEQ_TMOP_CUDA_UVM_OBJ_FILES = $(if $(USE_CUDA),$(TMOP_FILES:$(SRC)%.cpp=%.cuda_uvm.o))
SEQ_TMOP_GPU_OBJ_FILES = $(if $(USE_GPU),$(TMOP_FILES:$(SRC)%.cpp=%.gpu.o))
SEQ_TMOP_GPU_UVM_OBJ_FILES = $(if $(USE_GPU),$(TMOP_FILES:$(SRC)%.cpp=%.gpu_uvm.o))
PAR_TMOP_CPU_OBJ_FILES = $(TMOP_FILES:$(SRC)%.cpp=%.pcpu.o)
PAR_TMOP_DEBUG_OBJ_FILES = $(TMOP_FILES:$(SRC)%.cpp=%.pdebug.o)
PAR_TMOP_CUDA_OBJ_FILES = $(if $(USE_CUDA),$(TMOP_FILES:$(SRC)%.cpp=%.pcuda.o))
PAR_TMOP_CUDA_UVM_OBJ_FILES = $(if $(USE_CUDA),$(TMOP_FILES:$(SRC)%.cpp=%.pcuda_uvm.o))
PAR_TMOP_GPU_OBJ_FILES = $(if $(USE_GPU),$(TMOP_FILES:$(SRC)%.cpp=%.pgpu.o))
PAR_TMOP_GPU_UVM_OBJ_FILES = $(if $(USE_GPU),$(TMOP_FILES:$(SRC)%.cpp=%.pgpu_uvm.o))
# seq/par files and tests
SEQ_UNIT_TESTS = unit_tests $(if $(USE_CUDA),cunit_tests)
SEQ_UNIT_TESTS = unit_tests $(if $(USE_GPU),gpu_unit_tests)
SEQ_UNIT_TESTS += $(SEQ_SEDOV_TESTS) $(SEQ_TMOP_TESTS)
PAR_UNIT_TESTS = punit_tests $(if $(USE_CUDA),pcunit_tests)
PAR_UNIT_TESTS = punit_tests $(if $(USE_GPU),pgpu_unit_tests)
PAR_UNIT_TESTS += $(PAR_SEDOV_TESTS) $(PAR_TMOP_TESTS)
# Ceed tests
@@ -114,11 +116,11 @@ unit_tests: $(SEQ_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_D
punit_tests: $(PAR_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(PAR_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
cunit_tests: $(CUDA_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(CUDA_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
gpu_unit_tests: $(GPU_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(GPU_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
pcunit_tests: $(PCUDA_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(PCUDA_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
pgpu_unit_tests: $(PGPU_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(PGPU_MAIN_OBJ) $(LIBTESTS_O) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
ceed_tests: $(CEED_OBJ) $(MFEM_LIB_FILE) $(CONFIG_MK) $(DATA_DIR)
$(CCC) $(CEED_OBJ) $(MFEM_LINK_FLAGS) $(MFEM_LIBS) -o $(@)
@@ -131,8 +133,8 @@ $(LIBTESTS_O): $(OBJECT_FILES)
# Note: in this rule, we always use the full path to the source file as a
# workaround for an issue with coveralls.
$(OBJECT_FILES) $(SEQ_MAIN_OBJ) $(PAR_MAIN_OBJ) $(CUDA_MAIN_OBJ) \
$(PCUDA_MAIN_OBJ) $(DEBUG_DEVICE_OBJ): %.o: $(SRC)%.cpp $(HEADER_FILES) \
$(OBJECT_FILES) $(SEQ_MAIN_OBJ) $(PAR_MAIN_OBJ) $(GPU_MAIN_OBJ) \
$(PGPU_MAIN_OBJ) $(DEBUG_DEVICE_OBJ): %.o: $(SRC)%.cpp $(HEADER_FILES) \
$(CONFIG_MK)
@mkdir -p $(@D)
$(CCC) $(MFEM_FLAGS) $(INCLUDES) -c $(abspath $(<)) -o $(@)
@@ -159,8 +161,8 @@ $$(SEQ_SEDOV_$(2)_OBJ_FILES): %.$(1).o: $$(SRC)%.cpp $$(HEADER_FILES) $$(CONFIG_
endef
$(eval $(call sedov_tests,cpu,CPU,cpu))
$(eval $(call sedov_tests,debug,DEBUG,debug))
$(eval $(call sedov_tests,cuda,CUDA,cuda))
$(eval $(call sedov_tests,cuda_uvm,CUDA_UVM,cuda:uvm))
$(eval $(call sedov_tests,gpu,GPU,gpu))
$(eval $(call sedov_tests,gpu_uvm,GPU_UVM,gpu:uvm))
define psedov_tests
psedov_tests_$(1): SEDOV_TESTS_FLAGS =-DMFEM_SEDOV_MPI
@@ -176,8 +178,8 @@ $$(PAR_SEDOV_$(2)_OBJ_FILES): %.p$(1).o: $$(SRC)%.cpp $$(HEADER_FILES) $$(CONFIG
endef
$(eval $(call psedov_tests,cpu,CPU,cpu))
$(eval $(call psedov_tests,debug,DEBUG,debug))
$(eval $(call psedov_tests,cuda,CUDA,cuda))
$(eval $(call psedov_tests,cuda_uvm,CUDA_UVM,cuda:uvm))
$(eval $(call psedov_tests,gpu,GPU,gpu))
$(eval $(call psedov_tests,gpu_uvm,GPU_UVM,gpu:uvm))
# For out-of-source builds, copy the meshes in ../../miniapps/multidomain from
# the source location; these are used by 'punit_tests'.
@@ -204,7 +206,7 @@ copy-miniapps-meshing-data:
# 3: configuration string for the MFEM device, separated with '$(comma)'
define tmop_pa_tests
tmop_pa_tests_$(1): | $(if $(SRC),copy-miniapps-meshing-data)
tmop_pa_tests_$(1): TMOP_TESTS_FLAGS=-DMFEM_TMOP_DEVICE='"$(3)"'
tmop_pa_tests_$(1): TMOP_TESTS_FLAGS=-DMFEM_TMOP_PA_DEVICE='"$(3)"'
tmop_pa_tests_$(1): $$(SEQ_TMOP_$(2)_OBJ_FILES) \
$$(MFEM_LIB_FILE) $$(CONFIG_MK) $$(DATA_DIR)
$$(CCC) $$(SEQ_TMOP_$(2)_OBJ_FILES) \
@@ -216,13 +218,13 @@ $$(SEQ_TMOP_$(2)_OBJ_FILES): %.$(1).o: $$(SRC)%.cpp $$(HEADER_FILES) $$(CONFIG_M
endef
$(eval $(call tmop_pa_tests,cpu,CPU,cpu))
$(eval $(call tmop_pa_tests,debug,DEBUG,debug))
$(eval $(call tmop_pa_tests,cuda,CUDA,cuda))
# $(eval $(call tmop_pa_tests,cuda_uvm,CUDA_UVM,cuda:uvm))
$(eval $(call tmop_pa_tests,gpu,GPU,gpu))
# $(eval $(call tmop_pa_tests,gpu_uvm,GPU_UVM,gpu:uvm))
define ptmop_pa_tests
ptmop_pa_tests_$(1): | $(if $(SRC),copy-miniapps-meshing-data)
ptmop_pa_tests_$(1): TMOP_TESTS_FLAGS =-DMFEM_TMOP_MPI
ptmop_pa_tests_$(1): TMOP_TESTS_FLAGS+=-DMFEM_TMOP_DEVICE='"$(3)"'
ptmop_pa_tests_$(1): TMOP_TESTS_FLAGS+=-DMFEM_TMOP_PA_DEVICE='"$(3)"'
ptmop_pa_tests_$(1): $$(PAR_TMOP_$(2)_OBJ_FILES) \
$$(MFEM_LIB_FILE) $$(CONFIG_MK) $$(DATA_DIR)
$$(CCC) $$(PAR_TMOP_$(2)_OBJ_FILES) \
@@ -234,8 +236,8 @@ $$(PAR_TMOP_$(2)_OBJ_FILES): %.p$(1).o: $$(SRC)%.cpp $$(HEADER_FILES) $$(CONFIG_
endef
$(eval $(call ptmop_pa_tests,cpu,CPU,cpu))
#$(eval $(call ptmop_pa_tests,debug,DEBUG,debug))
$(eval $(call ptmop_pa_tests,cuda,CUDA,cuda))
#$(eval $(call ptmop_pa_tests,cuda_uvm,CUDA_UVM,cuda:uvm))
$(eval $(call ptmop_pa_tests,gpu,GPU,gpu))
#$(eval $(call ptmop_pa_tests,gpu_uvm,GPU_UVM,gpu:uvm))
$(DATA_DIR):
ln -s $(SRC)$(DATA_DIR) .
+76 -81
View File
@@ -111,8 +111,7 @@ void kSmemForceMult2D(const int NE,
auto b = Reshape(B_.Read(), Q1D, L1D);
auto bt = Reshape(Bt_.Read(), H1D, Q1D);
auto gt = Reshape(Gt_.Read(), H1D, Q1D);
auto sJit = Reshape(Read(sJit_.GetMemory(), Q1D*Q1D*NE*2*2),
Q1D,Q1D,NE,2,2);
auto sJit = Reshape(Read(sJit_.GetMemory(), Q1D*Q1D*NE*2*2), Q1D,Q1D,NE,2,2);
auto energy = Reshape(e_.Read(), L1D, L1D, NE);
const real_t eps1 = std::numeric_limits<real_t>::epsilon();
const real_t eps2 = eps1*eps1;
@@ -124,14 +123,14 @@ void kSmemForceMult2D(const int NE,
MFEM_SHARED real_t Bt[H1D][Q1D];
MFEM_SHARED real_t Gt[H1D][Q1D];
MFEM_SHARED real_t Ez[NBZ][L1D][L1D];
real_t (*E)[L1D] = (real_t (*)[L1D])(Ez + z);
auto E = (real_t (*)[L1D])(Ez + z);
MFEM_SHARED real_t LQz[2][NBZ][H1D][Q1D];
real_t (*LQ0)[Q1D] = (real_t (*)[Q1D])(LQz[0] + z);
real_t (*LQ1)[Q1D] = (real_t (*)[Q1D])(LQz[1] + z);
auto LQ0 = (real_t (*)[Q1D])(LQz[0] + z);
auto LQ1 = (real_t (*)[Q1D])(LQz[1] + z);
MFEM_SHARED real_t QQz[3][NBZ][Q1D][Q1D];
real_t (*QQ)[Q1D] = (real_t (*)[Q1D])(QQz[0] + z);
real_t (*QQ0)[Q1D] = (real_t (*)[Q1D])(QQz[1] + z);
real_t (*QQ1)[Q1D] = (real_t (*)[Q1D])(QQz[2] + z);
auto QQ = (real_t (*)[Q1D])(QQz[0] + z);
auto QQ0 = (real_t (*)[Q1D])(QQz[1] + z);
auto QQ1 = (real_t (*)[Q1D])(QQz[2] + z);
if (z == 0)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
@@ -269,16 +268,16 @@ void kSmemForceMult3D(const int NE,
MFEM_SHARED real_t E[L1D][L1D][L1D];
MFEM_SHARED real_t sm0[3][Q1D*Q1D*Q1D];
MFEM_SHARED real_t sm1[3][Q1D*Q1D*Q1D];
real_t (*MMQ0)[D1D][Q1D] = (real_t (*)[D1D][Q1D]) (sm0+0);
real_t (*MMQ1)[D1D][Q1D] = (real_t (*)[D1D][Q1D]) (sm0+1);
real_t (*MMQ2)[D1D][Q1D] = (real_t (*)[D1D][Q1D]) (sm0+2);
real_t (*MQQ0)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm1+0);
real_t (*MQQ1)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm1+1);
real_t (*MQQ2)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm1+2);
auto MMQ0 = (real_t (*)[D1D][Q1D]) (sm0+0);
auto MMQ1 = (real_t (*)[D1D][Q1D]) (sm0+1);
auto MMQ2 = (real_t (*)[D1D][Q1D]) (sm0+2);
auto MQQ0 = (real_t (*)[Q1D][Q1D]) (sm1+0);
auto MQQ1 = (real_t (*)[Q1D][Q1D]) (sm1+1);
auto MQQ2 = (real_t (*)[Q1D][Q1D]) (sm1+2);
MFEM_SHARED real_t QQQ[Q1D][Q1D][Q1D];
real_t (*QQQ0)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm0+0);
real_t (*QQQ1)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm0+1);
real_t (*QQQ2)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm0+2);
auto QQQ0 = (real_t (*)[Q1D][Q1D]) (sm0+0);
auto QQQ1 = (real_t (*)[Q1D][Q1D]) (sm0+1);
auto QQQ2 = (real_t (*)[Q1D][Q1D]) (sm0+2);
if (z == 0)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
@@ -455,13 +454,13 @@ void kSmemForceMult3D(const int NE,
});
}
typedef void (*fForceMult)(const int E,
const Array<real_t> &B,
const Array<real_t> &Bt,
const Array<real_t> &Gt,
const DenseTensor &stressJinvT,
const Vector &e,
Vector &v);
using fForceMult = void (*)(const int E,
const Array<real_t> &B,
const Array<real_t> &Bt,
const Array<real_t> &Gt,
const DenseTensor &stressJinvT,
const Vector &e,
Vector &v);
static void kForceMult(const int DIM,
const int D1D,
@@ -517,16 +516,16 @@ void kSmemForceMultTranspose2D(const int NE,
MFEM_SHARED real_t B[Q1D][H1D];
MFEM_SHARED real_t G[Q1D][H1D];
MFEM_SHARED real_t Vz[NBZ][D1D*D1D];
real_t (*V)[D1D] = (real_t (*)[D1D])(Vz + z);
auto V = (real_t (*)[D1D])(Vz + z);
MFEM_SHARED real_t DQz[2][NBZ][D1D*Q1D];
real_t (*DQ0)[Q1D] = (real_t (*)[Q1D])(DQz[0] + z);
real_t (*DQ1)[Q1D] = (real_t (*)[Q1D])(DQz[1] + z);
auto DQ0 = (real_t (*)[Q1D])(DQz[0] + z);
auto DQ1 = (real_t (*)[Q1D])(DQz[1] + z);
MFEM_SHARED real_t QQz[3][NBZ][Q1D*Q1D];
real_t (*QQ)[Q1D] = (real_t (*)[Q1D])(QQz[0] + z);
real_t (*QQ0)[Q1D] = (real_t (*)[Q1D])(QQz[1] + z);
real_t (*QQ1)[Q1D] = (real_t (*)[Q1D])(QQz[2] + z);
auto QQ = (real_t (*)[Q1D])(QQz[0] + z);
auto QQ0 = (real_t (*)[Q1D])(QQz[1] + z);
auto QQ1 = (real_t (*)[Q1D])(QQz[2] + z);
MFEM_SHARED real_t QLz[NBZ][Q1D*L1D];
real_t (*QL)[L1D] = (real_t (*)[L1D]) (QLz + z);
auto QL = (real_t (*)[L1D]) (QLz + z);
if (z == 0)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
@@ -658,15 +657,15 @@ void kSmemForceMultTranspose3D(const int NE,
MFEM_SHARED real_t G[Q1D][H1D];
MFEM_SHARED real_t sm0[3][Q1D*Q1D*Q1D];
MFEM_SHARED real_t sm1[3][Q1D*Q1D*Q1D];
real_t (*V)[D1D][D1D] = (real_t (*)[D1D][D1D]) (sm0+0);
real_t (*MMQ0)[D1D][Q1D] = (real_t (*)[D1D][Q1D]) (sm0+1);
real_t (*MMQ1)[D1D][Q1D] = (real_t (*)[D1D][Q1D]) (sm0+2);
real_t (*MQQ0)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm1+0);
real_t (*MQQ1)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm1+1);
real_t (*MQQ2)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm1+2);
real_t (*QQQ0)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm0+0);
real_t (*QQQ1)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm0+1);
real_t (*QQQ2)[Q1D][Q1D] = (real_t (*)[Q1D][Q1D]) (sm0+2);
auto V = (real_t (*)[D1D][D1D]) (sm0+0);
auto MMQ0 = (real_t (*)[D1D][Q1D]) (sm0+1);
auto MMQ1 = (real_t (*)[D1D][Q1D]) (sm0+2);
auto MQQ0 = (real_t (*)[Q1D][Q1D]) (sm1+0);
auto MQQ1 = (real_t (*)[Q1D][Q1D]) (sm1+1);
auto MQQ2 = (real_t (*)[Q1D][Q1D]) (sm1+2);
auto QQQ0 = (real_t (*)[Q1D][Q1D]) (sm0+0);
auto QQQ1 = (real_t (*)[Q1D][Q1D]) (sm0+1);
auto QQQ2 = (real_t (*)[Q1D][Q1D]) (sm0+2);
MFEM_SHARED real_t QQQ[Q1D][Q1D][Q1D];
if (z == 0)
{
@@ -837,13 +836,13 @@ void kSmemForceMultTranspose3D(const int NE,
});
}
typedef void (*fForceMultTranspose)(const int nzones,
const Array<real_t> &Bt,
const Array<real_t> &B,
const Array<real_t> &G,
const DenseTensor &sJit,
const Vector &v,
Vector &e);
using fForceMultTranspose = void (*)(const int nzones,
const Array<real_t> &Bt,
const Array<real_t> &B,
const Array<real_t> &G,
const DenseTensor &sJit,
const Vector &v,
Vector &e);
static void kForceMultTranspose(const int DIM,
const int D1D,
@@ -949,8 +948,7 @@ static void ComputeDiagonal2D(const int height, const int nzones,
const Tensors1D *tensors1D,
Vector &diag)
{
const TensorBasisElement *fe_H1 =
dynamic_cast<const TensorBasisElement *>(FESpace.GetTypicalFE());
auto fe_H1 = dynamic_cast<const TensorBasisElement *>(FESpace.GetTypicalFE());
const Array<int> &dof_map = fe_H1->GetDofMap();
const DenseMatrix &HQs = tensors1D->HQshape1D;
const int ndof1D = HQs.Height(), nqp1D = HQs.Width(), nqp = nqp1D * nqp1D;
@@ -984,8 +982,7 @@ static void ComputeDiagonal3D(const int height, const int nzones,
const Tensors1D *tensors1D,
Vector &diag)
{
const TensorBasisElement *fe_H1 =
dynamic_cast<const TensorBasisElement *>(FESpace.GetTypicalFE());
auto fe_H1 = dynamic_cast<const TensorBasisElement *>(FESpace.GetTypicalFE());
const Array<int> &dof_map = fe_H1->GetDofMap();
const DenseMatrix &HQs = tensors1D->HQshape1D;
const int ndof1D = HQs.Height(), nqp1D = HQs.Width(),
@@ -1043,7 +1040,7 @@ private:
FiniteElementSpace &FESpace;
ParBilinearForm pabf;
int ess_tdofs_count;
Array<int> ess_tdofs;
Array<int> ess_tdofs, empty;
OperatorPtr massOperator;
Tensors1D *tensors1D;
public:
@@ -1062,13 +1059,12 @@ public:
FESpace(pfes),
pabf(&pfes),
ess_tdofs_count(0),
ess_tdofs(0),
tensors1D(t1D)
{
pabf.SetAssemblyLevel(AssemblyLevel::PARTIAL);
pabf.AddDomainIntegrator(new mfem::MassIntegrator(Q,&ir));
pabf.AddDomainIntegrator(new mfem::MassIntegrator(Q, &ir));
pabf.Assemble();
pabf.FormSystemMatrix(mfem::Array<int>(), massOperator);
pabf.FormSystemMatrix(empty, massOperator);
}
void Mult(const Vector &x, Vector &y) const override
@@ -1138,15 +1134,14 @@ public:
void SetDiagonal(Vector &d)
{
const Operator *P = FESpace.GetProlongationMatrix();
if (P == NULL) { diag = d; return; }
if (P == nullptr) { diag = d; return; }
diag.SetSize(P->Width());
P->MultTranspose(d, diag);
}
void Mult(const Vector &x, Vector &y) const override
{
const int N = x.Size();
auto d_diag = diag.Read();
auto d_x = x.Read();
const auto d_diag = diag.Read(), d_x = x.Read();
auto d_y = y.Write();
mfem::forall(N, [=] MFEM_HOST_DEVICE (int i) { d_y[i] = d_x[i] / d_diag[i]; });
}
@@ -1217,10 +1212,10 @@ void ComputeRho0DetJ0AndVolume(const int dim,
rho0Q.UseDevice(true);
const QuadratureInterpolator *qi = l2_fes.GetQuadratureInterpolator(ir);
qi->Values(rho0, rho0Q);
auto W = ir.GetWeights().Read();
auto R = Reshape(rho0Q.Read(), NQ, NE);
auto J = Reshape(geom->J.Read(), NQ, dim, dim, NE);
auto detJ = Reshape(geom->detJ.Read(), NQ, NE);
const auto W = ir.GetWeights().Read();
const auto R = Reshape(rho0Q.Read(), NQ, NE);
const auto J = Reshape(geom->J.Read(), NQ, dim, dim, NE);
const auto detJ = Reshape(geom->detJ.Read(), NQ, NE);
auto V = Reshape(quad_data.rho0DetJ0w.Write(), NQ, NE);
Memory<real_t> &Jinv_m = quad_data.Jac0inv.GetMemory();
auto invJ = Reshape(Jinv_m.Write(Device::GetDeviceMemoryClass(),
@@ -1430,16 +1425,16 @@ void QKernel(const int nzones,
Vector &dt_est,
DenseTensor &stressJinvT)
{
auto d_weights = weights.Read();
auto d_Jacobians = Jacobians.Read();
auto d_rho0DetJ0w = rho0DetJ0w.Read();
auto d_e_quads = e_quads.Read();
auto d_grad_v_ext = grad_v_ext.Read();
const auto d_weights = weights.Read();
const auto d_Jacobians = Jacobians.Read();
const auto d_rho0DetJ0w = rho0DetJ0w.Read();
const auto d_e_quads = e_quads.Read();
const auto d_grad_v_ext = grad_v_ext.Read();
auto d_Jac0inv = Read(Jac0inv.GetMemory(), Jac0inv.TotalSize());
auto d_dt_est = dt_est.ReadWrite();
auto d_stressJinvT = Write(stressJinvT.GetMemory(),
stressJinvT.TotalSize());
if (dim==2)
if (dim == 2)
{
mfem::forall_2D(nzones, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int z)
{
@@ -1516,7 +1511,7 @@ void QUpdate::UpdateQuadratureData(const Vector &S,
Vector* S_p = const_cast<Vector*>(&S);
const int H1_size = H1.GetVSize();
const int nqp1D = tensors1D->LQshape1D.Width();
const real_t h1order = (real_t) H1.GetElementOrder(0);
const real_t h1order = H1.GetElementOrder(0);
const real_t infinity = std::numeric_limits<real_t>::infinity();
GridFunction d_x, d_v, d_e;
d_x.MakeRef(&H1,*S_p, 0);
@@ -1531,15 +1526,15 @@ void QUpdate::UpdateQuadratureData(const Vector &S,
q2->Values(d_e, d_l2_e_quads_data);
d_dt_est = quad_data.dt_est;
const int id = (dim<<4) | nqp1D;
typedef void (*fQKernel)(const int NE, const int NQ,
const real_t gamma, const bool use_viscosity,
const real_t h0, const real_t h1order,
const real_t cfl, const real_t infinity,
const Array<real_t> &weights,
const Vector &Jacobians, const Vector &rho0DetJ0w,
const Vector &e_quads, const Vector &grad_v_ext,
const DenseTensor &Jac0inv,
Vector &dt_est, DenseTensor &stressJinvT);
using fQKernel = void (*)(const int NE, const int NQ,
const real_t gamma, const bool use_viscosity,
const real_t h0, const real_t h1order,
const real_t cfl, const real_t infinity,
const Array<real_t> &weights,
const Vector &Jacobians, const Vector &rho0DetJ0w,
const Vector &e_quads, const Vector &grad_v_ext,
const DenseTensor &Jac0inv,
Vector &dt_est, DenseTensor &stressJinvT);
static std::unordered_map<int, fQKernel> qupdate =
{
{0x24,&QKernel<2,4>}, //{0x26,&QKernel<2,6>}, {0x28,&QKernel<2,8>},
@@ -2030,6 +2025,7 @@ int sedov(int myid, int argc, char *argv[])
rho.ProjectGridFunction(l2_rho);
DeltaCoefficient e_coeff(blast_position[0], blast_position[1],
blast_position[2], blast_energy);
e_coeff.SetWeight(new ConstantCoefficient(1.0));
l2_e.ProjectCoefficient(e_coeff);
e_gf.ProjectGridFunction(l2_e);
e_gf.SyncAliasMemory(S);
@@ -2038,13 +2034,13 @@ int sedov(int myid, int argc, char *argv[])
ParGridFunction mat_gf(&mat_fes);
FunctionCoefficient mat_coeff(gamma);
mat_gf.ProjectCoefficient(mat_coeff);
GridFunctionCoefficient *mat_gf_coeff = new GridFunctionCoefficient(&mat_gf);
GridFunctionCoefficient mat_gf_coeff(&mat_gf);
const int source = 0; bool visc = true;
mfem::hydrodynamics::LagrangianHydroOperator oper(rho_coeff, S.Size(),
H1FESpace, L2FESpace,
ess_tdofs, rho, source,
cfl, mat_gf_coeff,
cfl, &mat_gf_coeff,
visc, cg_tol, cg_max_iter,
ftz_tol, order_q,
gamma(S),
@@ -2148,7 +2144,6 @@ int sedov(int myid, int argc, char *argv[])
//oper.PrintTimingData(myid, steps, fom);
delete ode_solver;
delete pmesh;
delete mat_gf_coeff;
return 0;
}
} // namespace mfem

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