Compare commits

...
311 Commits
Author SHA1 Message Date
Stowell, Mark L. e2d68264e6 Merge remote-tracking branch 'origin/eigensolvers-dev' into arpack-dev 2026-04-29 12:12:31 -07:00
Mark L. Stowell 8bf2b3f061 Merge branch 'master' into eigensolvers-dev 2026-04-29 12:03:54 -07:00
Tzanio Kolev e52948f9e5 Merge pull request #4714 from mfem/dc-ofstream-fix-minor
Verify that `ofstream` is open
2026-04-29 08:18:43 -06:00
Stowell, Mark L. 933ddce17a Removing ex11 and ex13 from cmake build when ARPACK is not available 2026-04-29 00:22:35 -07:00
Stowell, Mark L. 085557f02b Modifying ex11p to new API 2026-04-28 17:22:36 -07:00
Stowell, Mark L. 2544bab0aa Adapting serial examples to modified API 2026-04-28 17:18:57 -07:00
Stowell, Mark L. bea4a8bae0 Merge remote-tracking branch 'origin/eigensolvers-dev' into arpack-dev 2026-04-28 15:59:48 -07:00
Veselin Dobrev c860bf20ea Merge pull request #5294 from mfem/bugfix/lorentz-test-runs
Fixing typos in lorentz miniapp test runs
2026-04-28 15:47:46 -07:00
Stowell, Mark L. 31dc0b5322 Simplifying inheritance hierarchy 2026-04-28 15:18:12 -07:00
Tzanio Kolev 7ff0bd3bb0 Merge branch 'master' into dc-ofstream-fix-minor 2026-04-26 14:30:17 -06:00
Stowell, Mark L. 18ca696093 Merge remote-tracking branch 'origin/eigensolvers-dev' into arpack-dev
# Conflicts:
#	linalg/eigensolvers.hpp
2026-04-24 13:52:49 -07:00
Stowell, Mark L. c441b28b01 Merge remote-tracking branch 'origin/eigensolvers-dev' into arpack-dev 2026-04-24 13:48:10 -07:00
Stowell, Mark L. 125e182a05 Merge remote-tracking branch 'origin/master' into eigensolvers-dev 2026-04-24 13:47:04 -07:00
Stowell, Mark L. 3dc8e4d98e Provide eight variants of eigensolver for easier inheritance 2026-04-24 13:46:38 -07:00
Stowell, Mark L. b947d34583 Make it stop! 2026-04-24 11:05:08 -07:00
Stowell, Mark L. 862e527539 Disabling ex11 and ex13 builds when ARPACK is not available 2026-04-24 11:01:25 -07:00
Stowell, Mark L. 297d7eabd6 Merge remote-tracking branch 'origin/eigensolvers-dev' into arpack-dev 2026-04-24 10:03:41 -07:00
Stowell, Mark L. 86f214bdc6 When will it end? 2026-04-24 10:02:03 -07:00
Stowell, Mark L. 8420384555 Disabling ex13 when ARPACK is unavailable 2026-04-24 09:48:24 -07:00
Stowell, Mark L. f158717ae5 Disabling ex11 if ARPACK is not available 2026-04-24 07:27:25 -07:00
Stowell, Mark L. 3a645d61b2 Yet another explicit Distribute 2026-04-24 07:17:08 -07:00
Stowell, Mark L. ac86240cd8 Tweaking ex11p 2026-04-23 17:41:55 -07:00
Stowell, Mark L. 8c7f47ee71 Cleanup 2026-04-23 16:13:57 -07:00
Stowell, Mark L. 8a9d4e94cf Merge remote-tracking branch 'origin/eigensolvers-dev' into arpack-dev
# Conflicts:
#	examples/ex11p.cpp
#	linalg/eigensolvers.hpp
#	linalg/hypre.cpp
#	linalg/hypre.hpp
2026-04-23 16:09:06 -07:00
Stowell, Mark L. 224345b00c One more explicit Distribute 2026-04-23 15:59:16 -07:00
Stowell, Mark L. 6ee0947d03 Adding explicit Distribute 2026-04-23 15:14:40 -07:00
Stowell, Mark L. b82f870350 Adding missing overrides 2026-04-23 12:56:05 -07:00
Stowell, Mark L. 819a262bd3 make style 2026-04-23 11:20:13 -07:00
Stowell, Mark L. 10a017a62d Modifying hypre eigensolvers to fit the proposed interface 2026-04-23 11:19:53 -07:00
Stowell, Mark L. b82ec338c5 Adding references to new header 2026-04-23 10:44:25 -07:00
Stowell, Mark L. 90ebbb469c Suggestion for eigensolver base classes 2026-04-23 10:42:55 -07:00
Veselin Dobrev 3e31395f85 Fix another minor compiler warning.
Update the CMake tests in miniapps/electromagnetics to match the makefile.
2026-04-21 23:39:11 -07:00
Tzanio Kolev b2de4c4ba1 Merge pull request #5239 from nmnobre/ir
A few small fixes and feature additions
2026-04-21 12:27:07 -06:00
Stowell, Mark L. 32318eaf75 One more file header 2026-04-19 14:47:30 -07:00
Stowell, Mark L. 98d80620bf Updating file headers 2026-04-19 14:42:28 -07:00
Stowell, Mark L. cec5a119d1 Merge remote-tracking branch 'origin/master' into arpack-dev 2026-04-19 14:39:37 -07:00
Stowell, Mark L. 8744958c2f Attempting to standardize arguments 2026-04-19 14:38:15 -07:00
Tzanio Kolev a713e386c2 Merge pull request #5278 from mfem/fix-umpire-dep
fix CMake umpire build and install
2026-04-18 18:57:23 -06:00
Tzanio Kolev 4155b0bdda Merge pull request #4645 from mfem/ex37
Enhancements, optimization for ex37
2026-04-18 18:55:39 -06:00
Stowell, Mark L. 65b1e0addb Merge remote-tracking branch 'origin/master' into arpack-dev
# Conflicts:
#	config/config.hpp.in
#	config/defaults.mk
#	examples/ex11p.cpp
#	examples/makefile
#	linalg/CMakeLists.txt
#	linalg/hypre.cpp
#	linalg/hypre.hpp
#	linalg/linalg.hpp
#	makefile
2026-04-18 11:38:43 -07:00
Tzanio Kolev dbbd425a22 Merge pull request #5186 from mfem/particles-pic-dev-pr
Electrostatic PIC
2026-04-14 13:54:29 -06:00
Tzanio Kolev 156f338e49 Merge pull request #4626 from mfem/mfem-v13-mesh-reader-fix-issue-4625
[BUG] Fix for mfem v13 mesh format reader
2026-04-14 13:38:27 -06:00
Will Pazner 53581cb5b7 Merge pull request #5300 from mfem/fix-nvcc-static_cast-warnings
Fix nvcc warnings
2026-04-14 11:04:36 -07:00
Andrew Ho 12509fda28 Merge branch 'master' into fix-umpire-dep 2026-04-13 09:29:28 -07:00
Nuno Nobre a9b36b1e5e Merge branch 'master' into test 2026-04-13 12:05:37 +01:00
Veselin Dobrev 7985a225bb Fix nvcc warnings about static_cast<const int> 2026-04-12 18:11:16 -07:00
Veselin Dobrev 72f83edd53 Fix compiler warnings when GSLIB is enabled with some extra warning flags 2026-04-08 18:41:57 -07:00
Andrew Ho 6c5f513eaa Merge branch 'master' into fix-umpire-dep 2026-04-08 16:37:59 -07:00
Mark L. Stowell 75cc8433e9 Merge branch 'master' into bugfix/lorentz-test-runs 2026-04-08 15:19:45 -04:00
Veselin Dobrev f700d97549 Merge pull request #5287 from mfem/pr-4626-tweaks
Proposed tweaks for PR 4626
2026-04-08 11:16:23 -07:00
Veselin Dobrev 449ec725e2 Merge branch 'master' into particles-pic-dev-pr 2026-04-08 11:04:40 -07:00
Stowell, Mark L. 7330aca4e6 Fixing typoes in lorentz miniapp test runs 2026-04-07 21:04:13 -04:00
Veselin Dobrev 9ebfcf05af In the electrostatic PIC miniapp:
* Fix the out-of-source build.
* Use the same test options in CMake as in GNU make.
* Ensure the test is run from the GNU makefile.
2026-04-07 17:42:39 -07:00
thatguynoe be1db1e4b7 initialize y, c, f_c 2026-04-07 20:07:28 -04:00
Noe Reyes 37fcdc1816 Merge branch 'master' into ex37 2026-04-07 19:50:41 -04:00
thatguynoe 0af98d7ff6 parameter c no longer present 2026-04-07 19:43:06 -04:00
thatguynoe cb6192167c correct assert message 2026-04-07 19:39:47 -04:00
thatguynoe bdf6aa6369 ensure root search interval is valid
We now look for a root of the function f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω) within the interval [a,b], where a := -‖αG‖_∞ and b := ‖αG‖_∞, and α and G are as in Step 4 and Step 5. It follows that f(a) ≤ ∫_Ω sigmoid(ψ + αG) dx - θ vol(Ω) ≤ f(b), and the inner quantity equals 0 since ψ_new := ψ_prev - αG in Step 5 and ∫_Ω sigmoid(ψ_new) dx - θ vol(Ω) = 0. This ensures f(a) ≤ 0 ≤ f(b), as required by the Illinois method.
2026-04-07 19:39:30 -04:00
Stowell, Mark L. da40ac4f2d Adding pic subdirectory to CMakeLists.txt as discussed in PR meeting 2026-04-07 16:51:07 -04:00
thatguynoe bff5d5e0cb correct comments 2026-04-06 22:14:46 -04:00
thatguynoe 26a152fb11 set y = 0.0 2026-04-06 22:11:14 -04:00
Nuno Nobre bca03a17af Avoid unneeded overrides of ProjectDiscCoefficient 2026-04-03 18:17:43 +01:00
Ketan Mittal 6479b2607d Merge branch 'master' into particles-pic-dev-pr 2026-04-03 08:57:26 -07:00
Nuno Nobre c1de6939f9 Remove unused ThresholdRefiner member current_sequence 2026-04-03 16:14:37 +01:00
Veselin Dobrev 9300f47c83 Added review suggestions 2026-04-01 19:51:35 -07:00
Mittal, Ketan 75e49b217c modify top level makefile and run make style 2026-04-01 15:45:46 -07:00
Andrew Ho b46baa5f5e Merge branch 'master' into fix-umpire-dep 2026-03-31 15:59:31 -07:00
Veselin Dobrev 610196629e Merge branch 'mfem-v13-mesh-reader-fix-issue-4625' into pr-4626-tweaks 2026-03-30 00:50:29 -07:00
Veselin Dobrev 8453b4008d Merge branch 'master' into mfem-v13-mesh-reader-fix-issue-4625 2026-03-30 00:49:11 -07:00
Veselin Dobrev fab2afd8dc Proposed tweaks for PR 4626 2026-03-30 00:09:55 -07:00
rzhangbq 3e1f10daea incorporating PR #5282 2026-03-24 21:51:15 -07:00
rzhangbq 35778347d0 resolve double-assigning b 2026-03-24 10:51:44 -07:00
Andrew Ho 077954d4b3 fix CMake umpire build and install 2026-03-19 13:43:48 -07:00
thatguynoe c9f7a90f81 store the diffusion global matrix 2026-03-17 13:01:43 -04:00
Noe Reyes 3b35d8210d Merge branch 'master' into ex37 2026-03-17 11:33:05 -04:00
Nuno Nobre 3babbe993b Clarify L2ZienkiewiczZhuEstimator only requires ComputeElementFlux() 2026-03-17 09:21:02 +00:00
thatguynoe 5f5421fde2 rename bool flag 2026-03-16 23:44:11 -04:00
thatguynoe 8644c8a8dd rename boundary dof extraction function 2026-03-16 18:27:26 -04:00
thatguynoe b863dd186f use MPITypeMap<real_t>::mpi_type 2026-03-16 18:15:57 -04:00
Noe ReyesandDohyun Kim 66702d831c correct return type in proj function
Co-authored-by: Dohyun Kim <dhkim.cse@gmail.com>
2026-03-16 18:12:22 -04:00
Nuno Nobre 60ab6ab8f5 New Is(Par)SubMesh methods to determine descendance 2026-03-13 15:37:28 +00:00
Nuno Nobre 878df1fef2 Revert "Allow evals of GridFunctionCoefficient on submeshes"
This reverts commit 8baa46babd.
2026-03-13 11:44:50 +00:00
Tzanio Kolev 9fb2327be9 Merge branch 'master' into ir 2026-03-10 10:59:26 -07:00
Mittal, Ketan 916e0b6acc Merge branch 'master' of https://github.com/mfem/mfem into particles-pic-dev-pr 2026-03-05 14:46:51 -08:00
Mittal, Ketan 65d36906c7 minor 2026-03-04 20:31:06 -08:00
Paul Hilscher 327f104c53 Merge branch 'master' into mfem-v13-mesh-reader-fix-issue-4625 2026-03-05 12:20:50 +09:00
Mittal, Ketan 4f01b485df update gitignore 2026-03-04 18:44:35 -08:00
Mittal, Ketan fc7f3fddfe merge with master, resolve conflicts, and move pic inside plasma 2026-03-04 18:43:34 -08:00
Rushan ZhangandJan Nikl 937651e509 Change test case
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-04 18:23:35 -05:00
Rushan ZhangandJan Nikl 16d9a2c311 Update Energy computation
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-04 14:25:20 -05:00
Rushan ZhangandJan Nikl c652a269ca Update Energy computation
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-04 14:25:07 -05:00
Rushan ZhangandJan Nikl 75bb2016a9 Update Energy computation
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-04 14:24:35 -05:00
Rushan ZhangandJan Nikl 60d5a6cb77 Update Energy computation
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-04 14:24:13 -05:00
Rushan ZhangandJan Nikl 3ee5f840ce Update Energy computation
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-04 14:23:46 -05:00
Rushan ZhangandJan Nikl abbad56994 Update Energy computation
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-04 14:22:58 -05:00
rzhangbq 9e261aeb36 format 2026-02-20 18:06:33 -05:00
rzhangbq 3fe3c00c72 format 2026-02-20 18:05:51 -05:00
rzhangbq 1d925e5b7b format 2026-02-20 16:24:17 -05:00
Rushan ZhangandJan Nikl e779a5d47e Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-20 15:15:03 -05:00
Nuno Nobre 7cd35f97f7 Add ProjectDiscCoefficient based on max attr for scalar coeffs 2026-02-20 10:26:42 +00:00
rzhangbq f69b6204df preconstruct RHS 2026-02-19 23:34:57 -05:00
Nuno Nobre 8baa46babd Allow evals of GridFunctionCoefficient on submeshes 2026-02-19 22:19:36 +00:00
Nuno Nobre 494fc00d34 Change IsParSubMesh to take Mesh ptr instead 2026-02-19 20:35:52 +00:00
Nuno Nobre 4dd3fcf811 Fix Mesh::GetFaceElementType for 1d meshes 2026-02-19 20:35:52 +00:00
Nuno Nobre 33d7cd11a2 Add missing setters for IntegrationPoint 2026-02-19 20:35:52 +00:00
Nuno Nobre fbd80e7493 Allow custom IntegrationRule for DomainLFGradIntegrator 2026-02-19 20:35:52 +00:00
rzhangbq a4fb0daa8e Update comments 2026-02-19 12:29:16 -05:00
rzhangbq 0b36f2adaa limit to 80 2026-02-19 12:27:52 -05:00
Rushan ZhangandJan Nikl 0288a5f146 Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-19 12:26:14 -05:00
Rushan ZhangandJan Nikl a1efd7a514 Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-19 12:25:51 -05:00
Rushan ZhangandJan Nikl e0c69fb83d Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-19 12:25:34 -05:00
Rushan ZhangandJan Nikl 43e88dd04f Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-19 12:25:16 -05:00
rzhangbq 946d4dde84 update comment 2026-02-19 12:24:44 -05:00
rzhangbq e890e9e6a5 update 2026-02-19 12:23:57 -05:00
Rushan ZhangandJan Nikl 7930c675ea Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-19 12:23:21 -05:00
Rushan ZhangandJan Nikl 298b14c82d Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-19 12:23:08 -05:00
Rushan ZhangandJan Nikl abb68a80e6 Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-19 12:22:47 -05:00
rzhangbq aec0b75047 change -oci default 2026-02-19 12:22:18 -05:00
rzhangbq f4e7c56119 change domain length var 2026-02-19 12:20:30 -05:00
rzhangbq eb70410a54 change ic for sample case 2026-02-19 12:18:46 -05:00
rzhangbq 9bccf40eb2 time out every timestep 2026-02-19 12:11:35 -05:00
Rushan ZhangandJan Nikl 3cb7465ab7 Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-19 12:02:51 -05:00
rzhangbq 9b2bc9e57a update comment 2026-02-13 14:41:50 -05:00
rzhangbq 76d2f8fea9 changed verify input 2026-02-13 14:40:33 -05:00
rzhangbq 63f746b8dc changed some verify 2026-02-13 14:38:27 -05:00
rzhangbq 18d64b8b93 change abort to verify 2026-02-13 14:33:49 -05:00
rzhangbq a740225601 update comment 2026-02-13 14:25:28 -05:00
rzhangbq 0d5fc47a73 reduce para to pass 2026-02-13 14:22:43 -05:00
rzhangbq 89974e87b6 split funcs 2026-02-11 16:37:15 -05:00
rzhangbq ec071ad4ab refact 2026-02-11 16:04:51 -05:00
rzhangbq 22c873f097 refact 2026-02-11 16:04:33 -05:00
rzhangbq e57ffb8128 drop the flag neutralizing_const_computed and check if precomputed_neutralizing_lf is set (not null). 2026-02-11 16:04:00 -05:00
Mittal, Ketan 2d7c578033 new line before FindPointsGSLIB constructor, and set default redist interval to 5 2026-02-11 12:59:04 -08:00
rzhangbq b503939955 get rid of HyperParVec Pointer 2026-02-11 15:51:15 -05:00
rzhangbq 8a4a826248 remove redundant 2026-02-11 15:45:18 -05:00
rzhangbq 8011c106ae resolve line width 2026-02-11 15:44:21 -05:00
rzhangbq 11d0d6a7be update desc of rdi 2026-02-11 15:26:42 -05:00
rzhangbq 2cc4bd7285 abort 2026-02-11 15:25:38 -05:00
rzhangbq 7ff38189fb remove redundant codes 2026-02-11 15:04:42 -05:00
rzhangbq dc243c6f7c remove misputted comments 2026-02-11 15:02:55 -05:00
rzhangbq b3508002e1 delete redundant pointpos 2026-02-11 15:01:03 -05:00
rzhangbq 06177ea337 update comment 2026-02-11 14:58:07 -05:00
rzhangbq 526d86489a move reduce global ke to method 2026-02-11 14:52:23 -05:00
Rushan ZhangandJan Nikl e8872fa31f Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-11 14:44:34 -05:00
Rushan ZhangandJan Nikl d547dfc6bf Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-11 14:44:26 -05:00
Rushan ZhangandJan Nikl b68a35d611 Update miniapps/pic/electrostatic-pic.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-02-11 14:44:08 -05:00
rzhangbq d2e381183e remove t_init 2026-02-11 14:43:11 -05:00
rzhangbq d4c37a7c1b make E_gf a static 2026-02-11 14:40:07 -05:00
rzhangbq dee64c36e5 add options to not output csv 2026-02-11 14:34:47 -05:00
rzhangbq 846147efc0 frequency -> interval 2026-02-11 14:29:39 -05:00
rzhangbq b621c9c4a2 rename size (num_ranks) 2026-02-11 14:19:05 -05:00
rzhangbq 5b1295c955 rename fec and fes 2026-02-11 14:17:47 -05:00
rzhangbq 43609b5c35 restyle comments 2026-02-11 14:16:49 -05:00
rzhangbq 64b7fbdeb2 move vis to main() 2026-02-11 14:11:23 -05:00
rzhangbq 44ed485cf1 rename fes 2026-02-11 13:58:20 -05:00
rzhangbq 90d1ed5ae3 remove empty lines 2026-02-11 13:54:59 -05:00
rzhangbq d7614eeb7e adding a few comments 2026-02-11 13:52:37 -05:00
Mittal, Ketan c441299f2b newline in gslib header, and some other minor change 2026-02-10 13:58:11 -08:00
Ketan Mittal 75526f58cc Merge branch 'master' into particles-pic-dev-pr 2026-02-10 13:10:10 -08:00
rzhangbq 422eb8710f change zero-stepping logic 2026-02-02 18:15:35 -05:00
rzhangbq 42c47e9225 apply doxygen style 2026-02-02 16:47:19 -05:00
rzhangbq f3dc010bda update cmake case 2026-02-01 18:08:24 -05:00
rzhangbq fa34b2dc63 update case 2026-01-30 23:28:51 -05:00
rzhangbq 23b4cc62e9 update -nx ny nz of 3d3v case 2026-01-30 19:37:30 -08:00
rzhangbq 08c332c1b0 add 3d3v case 2026-01-30 22:34:25 -05:00
rzhangbq b2ad517e03 rename files 2026-01-30 22:04:27 -05:00
rzhangbq 812a907abe add support for 3D 2026-01-30 21:51:54 -05:00
rzhangbq 3c73c50b29 format 2026-01-30 21:41:37 -05:00
Mittal, Ketan 1fd8301d38 Merge branch 'particles-pic-dev-pr' of https://github.com/mfem/mfem into particles-pic-dev-pr 2026-01-27 11:03:35 -08:00
Mittal, Ketan a013a150c1 include ordering argument in Interpolate 2026-01-27 11:03:27 -08:00
rzhangbq 0c9d63ba7f update comment 2026-01-26 23:47:14 -05:00
rzhangbq a367bcc30d add pre-compute grad-interpolator 2026-01-26 23:44:42 -05:00
Mittal, Ketan d1db3325f2 FindPointsGSLIB documentation for constructor 2026-01-26 19:35:24 -08:00
Mittal, Ketan 0a8b4ad9af use updated FindPointsGSLIB interface 2026-01-26 19:30:28 -08:00
rzhangbq 2283ea838a should not init vis-socket in the beginning 2026-01-26 20:15:05 -05:00
Mittal, Ketan dcc3ba856e Merge branch 'master' of https://github.com/mfem/mfem into particles-pic-dev-pr 2026-01-26 09:32:52 -08:00
rzhangbq 6a4d7db35b remove func call at particle step 2026-01-26 01:01:02 -05:00
rzhangbq e1567e2729 simplify particle step 2026-01-25 23:37:24 -05:00
rzhangbq 2ede430196 bind field solver to FESpace instead 2026-01-25 22:52:59 -05:00
rzhangbq 1e7b7403ff split total energy val 2026-01-25 20:37:23 -05:00
rzhangbq e33690db45 Change to use GradientInterpolator 2026-01-25 18:55:54 -05:00
Paul Hilscher cece1b642b Merge branch 'master' into mfem-v13-mesh-reader-fix-issue-4625 2026-01-26 08:31:46 +09:00
rzhangbq daac9192cc use stopwatch instead 2026-01-24 16:00:29 -05:00
rzhangbq 4699d9c9e1 get rid of fmod usage 2026-01-24 15:24:27 -05:00
rzhangbq 24abcaee7a Update descriptions of simulation paras 2026-01-24 15:16:04 -05:00
rzhangbq 14d59df037 rename class names 2026-01-24 15:12:57 -05:00
rzhangbq 5d23e37b83 remove SIZE 2026-01-24 15:08:25 -05:00
rzhangbq 7f5b68dfbd remove redundant findpoints 2026-01-23 17:28:53 -05:00
Rushan Zhang ac0454f07f Merge branch 'master' into particles-pic-dev-pr 2026-01-21 15:00:08 -05:00
rzhangbq 9e727d568c move func definition all to the bottom 2026-01-21 14:40:44 -05:00
rzhangbq 7fd9af27a5 move up comments 2026-01-21 14:40:27 -05:00
rzhangbq 77646c87dd remove redundant comments 2026-01-21 14:28:22 -05:00
rzhangbq 8531a43aac get rid of ctx.L_x in member funcs 2026-01-21 14:27:09 -05:00
rzhangbq 2b7f4ca792 add descriptions 2026-01-21 14:24:16 -05:00
rzhangbq 8e41393e14 Remove RemoveLostParticles (we use periodic boundary, particles should never move out of the computation space) 2026-01-21 14:17:14 -05:00
rzhangbq 452531e22f avoid use of ctx. out of main() 2026-01-21 14:10:12 -05:00
rzhangbq 6b6e5bf4b8 not hardcoding visport 2026-01-21 13:51:26 -05:00
rzhangbq 274bd5b670 now we can safely remove redundant FindPoints 2026-01-21 13:50:13 -05:00
rzhangbq b8f3571ba1 remove old Init particle declaration 2026-01-21 13:49:43 -05:00
rzhangbq 7f8e9680a6 add member func declaration 2026-01-21 13:49:08 -05:00
rzhangbq 2bebdf7595 make particle init as pic member func, so find particle is called upon particle creation 2026-01-21 13:48:46 -05:00
rzhangbq 759dacf996 using new findpoints 2026-01-21 13:47:38 -05:00
rzhangbq 2e76b94e17 add findparticles func 2026-01-21 13:45:48 -05:00
rzhangbq 128b7a092b add back FindPoints before Interpolate 2026-01-21 13:07:31 -05:00
rzhangbq 491c558a57 change testcase to do -rdf 2 2026-01-21 13:06:59 -05:00
rzhangbq 45bf80a62e format 2026-01-20 23:38:39 -05:00
rzhangbq fdc885ecd2 remove redundant FindPoints 2026-01-20 23:38:15 -05:00
rzhangbq e9b4630d58 changing -np def to total #particle 2026-01-20 23:03:21 -05:00
rzhangbq 5d8442c21c make sure there is only one finder 2026-01-20 22:56:53 -05:00
rzhangbq b31b0e04bd change suggested nx and ny s.t. Debye length is resolved 2026-01-15 23:41:26 -05:00
rzhangbq 838206e6a9 fix vis bug 2026-01-15 23:35:06 -05:00
rzhangbq c681a74f87 rm redundant continue 2026-01-15 21:26:38 -05:00
Rushan ZhangandKetan Mittal b45138e6d7 Use common::VisualizeField instead of my own vis
Co-authored-by: Ketan Mittal <ketan.mittal@gmail.com>
2026-01-15 11:46:24 -05:00
rzhangbq f692d94d08 make finder a member obj 2026-01-15 11:44:45 -05:00
Rushan ZhangandKetan Mittal 6a0e1a7a89 change ip set
Co-authored-by: Ketan Mittal <ketan.mittal@gmail.com>
2026-01-15 11:05:02 -05:00
Mittal, Ketan ec8cd31f32 build for make and cmake 2026-01-14 11:32:52 -08:00
rzhangbq ec1ba64dac use for loop instead of hardcoding dims 2026-01-13 19:34:12 -05:00
rzhangbq a9590b900a remove outdated comments 2026-01-13 19:23:41 -05:00
rzhangbq e7f2083f0b regulating line lengths 2026-01-13 19:20:53 -05:00
rzhangbq 1b93160f5d remove redundant code for b 2026-01-13 19:11:37 -05:00
rzhangbq 74476c8f89 put the sample run in 1 line 2026-01-13 19:05:23 -05:00
rzhangbq 934958771c update csv 2026-01-13 19:03:42 -05:00
rzhangbq 0f827820f6 remove outdated B_gf comments 2026-01-13 19:00:58 -05:00
Rushan Zhang 709a8ca7e4 move .gitignore 2026-01-13 18:58:14 -05:00
Rushan Zhang fea9d2c4ce move gitignore 2026-01-13 18:57:46 -05:00
Ketan Mittal ea9686bdc0 Merge branch 'master' into particles-pic-dev-pr 2026-01-13 12:22:04 -08:00
Rushan Zhang 9f03879386 Merge branch 'master' into particles-pic-dev-pr 2026-01-12 12:54:04 -05:00
rzhangbq 43b26e7a5b update description 2026-01-12 12:49:15 -05:00
rzhangbq 3a1fb995a4 fix argument description 2026-01-12 12:33:23 -05:00
rzhangbq 87cb7170b2 change description 2026-01-12 12:31:33 -05:00
rzhangbq 3165f09e0d update description 2026-01-11 19:01:06 -05:00
rzhangbq 03910bbe86 set vscode formatting 2026-01-11 17:26:55 -05:00
rzhangbq 9532220814 add a high-level summary 2026-01-11 16:44:04 -05:00
rzhangbq f5decb7c9e rename 2026-01-11 16:38:17 -05:00
rzhangbq 4e00bfb158 run astyle 2026-01-11 16:36:02 -05:00
rzhangbq 7b79732a28 make weather reproduce an option 2026-01-10 19:13:37 -05:00
rzhangbq bdf8f6d21b remove double-interpolate E and change redis 2026-01-10 19:03:13 -05:00
rzhangbq cbc63ad344 remove redundant 2026-01-10 18:54:59 -05:00
rzhangbq 844b655c76 add chrono 2026-01-10 17:46:03 -05:00
rzhangbq db6c8f5a9a change sample run 2026-01-10 17:30:36 -05:00
rzhangbq 06331492e5 update discription 2026-01-10 17:28:10 -05:00
rzhangbq dabb5652fe change para name 2026-01-10 16:54:57 -05:00
rzhangbq 4947faca83 update test case 2026-01-10 16:50:40 -05:00
rzhangbq 9d1cb51acc format and update banner 2026-01-10 16:46:16 -05:00
rzhangbq 1ff1f5777f update readme and update pic banner display 2026-01-10 16:39:59 -05:00
rzhangbq 7bc13bf237 finish migration 2026-01-10 16:32:36 -05:00
rzhangbq f65a0f093b remove B-stepping 2026-01-08 14:46:49 -05:00
rzhangbq e7058f6aca remove traj vis 2026-01-08 14:43:31 -05:00
rzhangbq 785afe66cd first commit 2026-01-08 14:42:27 -05:00
Paul Hilscher af834012d0 Merge branch 'master' into mfem-v13-mesh-reader-fix-issue-4625 2025-12-10 06:42:34 +09:00
Tzanio Kolev de3f769f49 Merge branch 'master' into dc-ofstream-fix-minor 2025-10-16 06:51:38 -07:00
Tzanio Kolev e60f43fff3 Merge branch 'master' into ex37 2025-07-01 15:02:18 -07:00
Noe Reyes 5e51751064 Merge branch 'master' into ex37 2025-06-27 19:10:28 -04:00
thatguynoe d87bc4d22c remove signum function 2025-06-27 19:09:47 -04:00
thatguynoe 29dd96acf3 use the Illinois method instead of bisection
Speeds up convergence for the Bregman projection.
2025-06-27 19:09:47 -04:00
Tzanio Kolev e30f5b9c96 Merge branch 'master' into ex37 2025-06-17 08:16:18 -07:00
thatguynoe f6979648e8 move boundary assembly into bilinear form assembly
Prevents the user from accidentally calling these methods in the wrong order.
2025-05-30 09:17:52 -07:00
thatguynoe 2a4decc635 use the bisection method instead of Newton 2025-05-29 12:33:49 -07:00
thatguynoe b9d19d3bb3 Merge branch 'master' into ex37 2025-05-29 11:30:21 -07:00
Brendan Keith d8da041edf Merge branch 'master' into ex37 2025-05-26 13:24:47 -04:00
Paul Hilscher 7994a3df8b fix unsigned signed comparison warning 2025-04-29 07:42:24 +09:00
Paul Hilscher 5bb0c458cd remove unused to address ci failure 2025-04-29 07:18:09 +09:00
Paul Hilscher c5b2f0945a update CMakeList to include correct unit test 2025-04-29 07:02:52 +09:00
Paul Hilscher 1b0425bfe9 add unit test for named mesh attributes 2025-04-29 06:58:39 +09:00
Tzanio Kolev ab52f334e2 Merge branch 'master' into mfem-v13-mesh-reader-fix-issue-4625 2025-04-26 12:25:30 -07:00
Paul Hilscher f8c494e59c fallback to tracking attributes as seek might not be available 2025-04-21 09:25:38 +09:00
Paul Hilscher f6d304864b fix parsing 2025-04-20 19:30:12 +09:00
Paul Hilscher 3593b4cd60 apply style 2025-04-20 09:17:18 +09:00
Paul Hilscher ef557b3fc1 add some more ws 2025-04-20 09:00:01 +09:00
Paul Hilscher 9a94a4b7b8 allow arbitrary white spaces 2025-04-20 08:59:04 +09:00
Paul Hilscher e18518d731 sort ex39 entry 2025-04-20 06:49:12 +09:00
Paul Hilscher e49bf21914 fix mfem v13 mesh format reader 2025-04-20 06:49:12 +09:00
thatguynoe 5808fc6966 use gradient descent step length in proj
See https://github.com/mfem/mfem/pull/4645#discussion_r1898110950.
2025-02-25 19:21:07 -05:00
thatguynoe b40bf6a64d Revert "add option to choose bisection method for roots"
This reverts commit 21778cd9335337a1419af482a7feaf6baac1057f.
2025-02-25 18:31:42 -05:00
thatguynoe 7a73e97922 Revert "rename newton arg for clarity"
This reverts commit d7681c26dc608bfab1ca4c891e22cbc1260340c6.
2025-02-25 18:31:42 -05:00
thatguynoe 3a97122e34 rename newton arg for clarity 2025-02-25 18:31:42 -05:00
thatguynoe 2f89a16314 update sample runs, fix stability 2025-02-25 18:31:42 -05:00
thatguynoe 0cd8c2e273 delete bilinear form when cleaning 2025-02-25 18:31:42 -05:00
thatguynoe b4992673b2 fix ex37 serial
If running ex37 serial with MFEM parallel, a segfault would occur when attempting to run MPI_Allreduce. To fix this, we use the associated FiniteElementSpace and check for MFEM parallel.
2025-02-25 18:31:42 -05:00
thatguynoe 46dce17970 fix ex37 serial
If running ex37 serial with MFEM parallel, a segfault would occur when attempting to run MPI_Allreduce. To fix this, we use the associated FiniteElementSpace and check for MFEM parallel.
2025-02-25 18:31:42 -05:00
thatguynoe f080627cba move ParGridFunction declaration 2025-02-25 18:31:42 -05:00
thatguynoe 85fb20a1d1 set smaller itol for better convergence 2025-02-25 18:31:42 -05:00
thatguynoe 428d203eac add option to choose bisection method for roots 2025-02-25 18:31:42 -05:00
thatguynoe c10ca25f62 assemble boundary, bilinear form outside of solve 2025-02-25 18:31:42 -05:00
thatguynoe 2e8f6f9c28 assemble boundary, bilinear form outside of solve 2025-02-25 18:31:42 -05:00
thatguynoe 11e4c46f25 add growth rate arg for grad descent step length 2025-02-25 18:31:42 -05:00
thatguynoe ff8d8752c7 move proj function into header file 2025-02-25 18:31:42 -05:00
stefanhenneking e3cfc28718 add ofstream.is_open() checks 2025-02-18 23:17:55 -06:00
Stowell, Mark L a0b8427774 Changing more license headers 2020-09-01 16:42:50 -07:00
Stowell, Mark L bd3897a7ec Changing cout to mfem::out 2020-09-01 16:41:12 -07:00
Stowell, Mark L 98039728a7 Changing license header 2020-09-01 16:40:50 -07:00
Stowell, Mark L 537f9ad677 Adding ARPACK option to ex11p 2020-09-01 16:30:27 -07:00
Stowell, Mark L 6081e24e78 make style 2020-09-01 16:03:57 -07:00
Stowell, Mark L 9779145f1a Merge remote-tracking branch 'origin/master' into arpack-dev 2020-09-01 10:43:44 -07:00
Stowell, Mark L e1576f336e Removing old initializations 2020-09-01 10:41:40 -07:00
Stowell, Mark L 673f0364de make style 2020-09-01 10:41:05 -07:00
Stowell, Mark L 90c4e55c40 Adding arpack.?pp files to CMakeLists 2020-09-01 10:34:35 -07:00
Stowell, Mark L 2f610e0170 Small improvements to ARPACK examples 2020-09-01 10:29:06 -07:00
Stowell, Mark L 35598cb6fb Adding SetOperator(Operator) methods 2020-09-01 10:28:02 -07:00
Stowell, Mark L af5003aee2 Adding new examples to make system 2020-09-01 10:27:19 -07:00
Stowell, Mark L 0e3223dd83 Changes suggested in issue #114 2020-09-01 10:23:43 -07:00
Veselin Dobrev b1ac354f59 Merge branch 'master' into arpack-dev 2020-08-27 11:12:48 -07:00
Stowell, Mark L 0147180a8b A possible replacement for ex11p 2017-02-24 01:57:30 -08:00
Stowell, Mark L 2d0a0b6c63 Adding an abstract base class for eigenvalue solvers 2017-02-24 01:56:41 -08:00
Stowell, Mark L 044ac04693 Fixed the parallel example
This needs to be cleaned up a bit or merged with ex11p.
2017-02-22 18:52:38 -08:00
Stowell, Mark L 019a983732 Run through astyle 2017-02-22 18:51:41 -08:00
Stowell, Mark L 8ff51b993c Fixing bugs in parallel implementation
Method overloading was not functioning because various methods were not
declared as ‘virtual’.

The partiitioning was computed incorrectly which lead to incorrectly
sized eigenvectors.
2017-02-22 18:51:09 -08:00
Stowell, Mark L 5d20efdbbd Adding an ARPACK version of ex11p for parallel testing
This version compares well to both ex11 and ex11p when run in serial.
However there is a bug which produces very poor solutions in parallel.
2017-02-22 02:35:08 -08:00
Stowell, Mark L eed944d75f Adding serial ARPACK examples 2017-02-21 17:22:33 -08:00
Stowell, Mark L 826f041d7f Adding ARPACK wrapper 2017-02-21 17:22:12 -08:00
Stowell, Mark L 97d4558da0 Adding ARPACK to config files 2017-02-21 17:20:55 -08:00
63 changed files with 4678 additions and 371 deletions
+4
View File
@@ -443,6 +443,10 @@ miniapps/diag-smoothers/mg-abs-l1-jacobi
miniapps/contact/contact
miniapps/contact/ParaView
miniapps/plasma/pic/electrostatic-*
!miniapps/plasma/pic/electrostatic-*.cpp
miniapps/plasma/pic/*.csv
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
+4
View File
@@ -109,6 +109,10 @@ if (MFEM_USE_RAJA)
find_dependency(RAJA)
endif()
if (MFEM_USE_UMPIRE)
find_dependency(umpire)
endif()
if (NOT TARGET mfem)
include(${CMAKE_CURRENT_LIST_DIR}/MFEMTargets.cmake)
endif (NOT TARGET mfem)
+3 -3
View File
@@ -14,12 +14,12 @@
# - UMPIRE_LIBRARIES
# - UMPIRE_INCLUDE_DIRS
if (NOT umpire_DIR AND UMPIRE_DIR)
set(umpire_DIR ${UMPIRE_DIR}/lib/cmake/umpire)
if (NOT umpire_ROOT AND UMPIRE_DIR)
set(umpire_ROOT ${UMPIRE_DIR})
endif()
message(STATUS "Looking for UMPIRE ...")
message(STATUS " in UMPIRE_DIR = ${UMPIRE_DIR}")
message(STATUS " umpire_DIR = ${umpire_DIR}")
message(STATUS " umpire_ROOT = ${umpire_ROOT}")
find_package(umpire CONFIG)
set(UMPIRE_FOUND ${umpire_FOUND})
set(UMPIRE_LIBRARIES "umpire")
+3
View File
@@ -97,6 +97,9 @@
// Enable MFEM functionality based on the SuiteSparse library.
// #define MFEM_USE_SUITESPARSE
// Enable MFEM functionality based on the ARPACK library.
// #define MFEM_USE_ARPACK
// Enable MFEM functionality based on the SuperLU_DIST library.
// #define MFEM_USE_SUPERLU
// #define MFEM_USE_SUPERLU5
+1
View File
@@ -32,6 +32,7 @@ MFEM_USE_MEMALLOC = @MFEM_USE_MEMALLOC@
MFEM_TIMER_TYPE = @MFEM_TIMER_TYPE@
MFEM_USE_SUNDIALS = @MFEM_USE_SUNDIALS@
MFEM_USE_SUITESPARSE = @MFEM_USE_SUITESPARSE@
MFEM_USE_ARPACK = @MFEM_USE_ARPACK@
MFEM_USE_SUPERLU = @MFEM_USE_SUPERLU@
MFEM_USE_SUPERLU5 = @MFEM_USE_SUPERLU5@
MFEM_USE_MUMPS = @MFEM_USE_MUMPS@
+9
View File
@@ -178,6 +178,7 @@ MFEM_USE_ALGOIM = NO
MFEM_USE_UMPIRE = NO
MFEM_USE_SIMD = NO
MFEM_USE_ADIOS2 = NO
MFEM_USE_ARPACK = NO
MFEM_USE_MKL_CPARDISO = NO
MFEM_USE_MKL_PARDISO = NO
MFEM_USE_MOONOLITH = NO
@@ -427,6 +428,14 @@ NETCDF_LIB = $(XLINKER)-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
-lnetcdf -lhdf5_hl -lhdf5 $(ZLIB_LIB)
# ARPACK library configuration
ARPACK_DIR = @MFEM_DIR@/../ARPACK
ifeq ($(MFEM_USE_MPI),YES)
ARPACK_LIB = -L$(ARPACK_DIR) -lparpack -larpack
else
ARPACK_LIB = -L$(ARPACK_DIR) -larpack
endif
# PETSc library configuration (version greater or equal to 3.8 or the dev branch)
PETSC_ARCH := arch-linux2-c-debug
PETSC_DIR := $(MFEM_DIR)/../petsc/$(PETSC_ARCH)
+7
View File
@@ -49,6 +49,13 @@ list(APPEND ALL_EXE_SRCS
ex41.cpp
)
if (MFEM_USE_ARPACK)
list(APPEND ALL_EXE_SRCS
ex11.pp
ex13.pp
)
endif()
if (MFEM_USE_MPI)
list(APPEND ALL_EXE_SRCS
ex0p.cpp
+298
View File
@@ -0,0 +1,298 @@
// MFEM Example 11 - Serial Version
//
// Compile with: make ex11
//
// Sample runs: ex11 -m ../data/square-disc.mesh
// ex11 -m ../data/star.mesh
// ex11 -m ../data/star-mixed.mesh
// ex11 -m ../data/periodic-annulus-sector.msh
// ex11 -m ../data/square-disc-p2.vtk -o 2
// ex11 -m ../data/square-disc-p3.mesh -o 3
// ex11 -m ../data/square-disc-nurbs.mesh -o -1
// ex11 -m ../data/disc-nurbs.mesh -o -1 -n 20
// ex11 -m ../data/star-surf.mesh
// ex11 -m ../data/square-disc-surf.mesh
// ex11 -m ../data/inline-segment.mesh
// ex11 -m ../data/inline-quad.mesh
// ex11 -m ../data/inline-tri.mesh
// ex11 -m ../data/amr-quad.mesh
// ex11 -m ../data/amr-hex.mesh
// ex11 -m ../data/mobius-strip.mesh -n 8
//
// Description: This example code demonstrates the use of MFEM to solve the
// eigenvalue problem -Delta u = lambda u with homogeneous
// Dirichlet boundary conditions.
//
// We compute a number of the lowest eigenmodes by discretizing
// the Laplacian and Mass operators using a FE space of the
// specified order, or an isoparametric/isogeometric space if
// order < 1 (quadratic for quadratic curvilinear mesh, NURBS for
// NURBS mesh, etc.)
//
// The example highlights the use of the ARPACK eigenvalue solver
// (regular inverse mode). Reusing a single GLVis visualization
// window for multiple eigenfunctions is also illustrated.
//
// We recommend viewing Example 1 before viewing this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
#ifdef MFEM_USE_ARPACK
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int ser_ref_levels = 3;
int order = 1;
int nev = 5;
double dbc_eig = 1e3;
bool visualization = 1;
bool arp_solver = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&nev, "-n", "--num-eigs",
"Number of desired eigenmodes.");
args.AddOption(&dbc_eig, "-d", "--dbc-eig",
"Eigenvalues associated with Dirichlet BC "
"(should be larger than the maximum desired eigenvalue).");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
// 2. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh;
ifstream imesh(mesh_file);
if (!imesh)
{
cerr << "\nCan not open mesh file: " << mesh_file << '\n' << endl;
return 2;
}
mesh = new Mesh(imesh, 1, 1);
imesh.close();
int dim = mesh->Dimension();
// 3. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement (2 by default, or
// specified on the command line with -rs).
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
// 4. Define a finite element space on the mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
}
else if (mesh->GetNodes())
{
fec = mesh->GetNodes()->OwnFEC();
}
else
{
fec = new H1_FECollection(order = 1, dim);
}
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
int size = fespace->GetVSize();
cout << "Number of unknowns: " << size << endl;
// 5. Set up the parallel bilinear forms a(.,.) and m(.,.) on the finite
// element space. The first corresponds to the Laplacian operator -Delta,
// while the second is a simple mass matrix needed on the right hand side
// of the generalized eigenvalue problem below. The boundary conditions
// are implemented by elimination with special values on the diagonal to
// shift the Dirichlet eigenvalues out of the computational range. After
// serial and parallel assembly we extract the corresponding parallel
// matrices A and M.
ConstantCoefficient one(1.0);
Array<int> ess_bdr;
if (mesh->bdr_attributes.Size())
{
ess_bdr.SetSize(mesh->bdr_attributes.Max());
ess_bdr = 1;
}
BilinearForm *a = new BilinearForm(fespace);
a->AddDomainIntegrator(new DiffusionIntegrator(one));
if (mesh->bdr_attributes.Size() == 0)
{
// Add a mass term if the mesh has no boundary, e.g. periodic mesh or
// closed surface.
a->AddDomainIntegrator(new MassIntegrator(one));
}
a->Assemble();
if (mesh->bdr_attributes.Size() != 0)
{
a->EliminateEssentialBCDiag(ess_bdr, dbc_eig);
}
a->Finalize();
BilinearForm *m = new BilinearForm(fespace);
m->AddDomainIntegrator(new MassIntegrator(one));
m->Assemble();
if (mesh->bdr_attributes.Size() != 0)
{
// shift the eigenvalue corresponding to eliminated dofs to a large value
m->EliminateEssentialBCDiag(ess_bdr, 1.0);
}
m->Finalize();
Solver * solver = NULL;
#ifndef MFEM_USE_SUITESPARSE
// 6. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the system A X = B with PCG.
cout << "Building CGSolver" << endl;
GSSmoother M(m->SpMat());
CGSolver * cg_solver = new CGSolver;
cg_solver->SetPreconditioner(M);
cg_solver->SetRelTol(1.0e-12);
solver = cg_solver;
#else
// 7. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
cout << "Building UMFPackSolver" << endl;
UMFPackSolver * umf_solver = new UMFPackSolver;
umf_solver->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
solver = umf_solver;
#endif
solver->SetOperator(m->SpMat());
// 7. Define and configure the ARPACK eigensolver
SymGenEigensolver * eig_solver = NULL;
if (arp_solver)
{
// ArPackSymGen * arpack = new ArPackSymGen();
ArPackSAUPD * arpack = new ArPackSAUPD();
arpack->SetMode(2);
arpack->SetNumModes(nev);
arpack->SetMaxIter(400);
arpack->SetTol(1e-8);
arpack->SetPrintLevel(2);
arpack->SetSolver(*solver);
eig_solver = arpack;
}
eig_solver->SetOperators(*a, *m);
// 8. Compute the eigenmodes and extract the array of eigenvalues. Define a
// parallel grid function to represent each of the eigenmodes returned by
// the solver.
Array<double> eigenvalues;
eig_solver->Solve();
eig_solver->GetEigenvalues(eigenvalues);
cout << endl;
std::ios::fmtflags old_fmt = cout.flags();
cout.setf(std::ios::scientific);
std::streamsize old_prec = cout.precision(14);
for (int i=0; i<nev; i++)
{
cout << "Eigenvalue lambda " << eigenvalues[i] << endl;
}
cout.precision(old_prec);
cout.flags(old_fmt);
cout << endl;
GridFunction x(fespace);
// 9. Save the refined mesh and the modes in parallel. This output can be
// viewed later using GLVis: "glvis -np <np> -m mesh -g mode".
{
ostringstream mesh_name, mode_name;
mesh_name << "ex11.mesh";
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
for (int i=0; i<nev; i++)
{
// convert eigenvector from Vector to GridFunction
x = eig_solver->GetEigenvector(i);
mode_name << "mode_" << setfill('0') << setw(2) << i;
ofstream mode_ofs(mode_name.str().c_str());
mode_ofs.precision(8);
x.Save(mode_ofs);
mode_name.str("");
}
}
// 10. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream mode_sock(vishost, visport);
mode_sock.precision(8);
for (int i=0; i<nev; i++)
{
cout << "Eigenmode " << i+1 << '/' << nev
<< ", Lambda = " << eigenvalues[i] << endl;
// convert eigenvector from Vector to GridFunction
x = eig_solver->GetEigenvector(i);
mode_sock << "solution\n" << *mesh << x << flush
<< "window_title 'Eigenmode " << i+1 << '/' << nev
<< ", Lambda = " << eigenvalues[i] << "'" << endl;
char c;
cout << "press (q)uit or (c)ontinue --> " << flush;
cin >> c;
if (c != 'c')
{
break;
}
}
mode_sock.close();
}
// 11. Free the used memory.
delete eig_solver;
delete solver;
delete m;
delete a;
delete fespace;
if (order > 0)
{
delete fec;
}
delete mesh;
return 0;
}
#endif // MFEM_USE_ARPACK
+104 -43
View File
@@ -72,6 +72,8 @@ int main(int argc, char *argv[])
int seed = 75;
bool slu_solver = false;
bool sp_solver = false;
bool lob_solver = true;
bool arp_solver = false;
bool cpardiso_solver = false;
bool visualization = 1;
@@ -97,6 +99,10 @@ int main(int argc, char *argv[])
args.AddOption(&sp_solver, "-sp", "--strumpack", "-no-sp",
"--no-strumpack", "Use the STRUMPACK Solver.");
#endif
#ifdef MFEM_USE_ARPACK
args.AddOption(&arp_solver, "-arp", "--arpack", "-no-arp",
"--no-arpack", "Use the Parallel ARPACK Solver.");
#endif
#ifdef MFEM_USE_MKL_CPARDISO
args.AddOption(&cpardiso_solver, "-cpardiso", "--cpardiso", "-no-cpardiso",
"--no-cpardiso", "Use the MKL CPardiso Solver.");
@@ -113,6 +119,11 @@ int main(int argc, char *argv[])
<< " Defaulting to SuperLU." << endl;
sp_solver = false;
}
if (arp_solver)
{
lob_solver = false;
}
// The command line options are also passed to the STRUMPACK
// solver. So do not exit if some options are not recognized.
if (!sp_solver)
@@ -243,70 +254,119 @@ int main(int argc, char *argv[])
// 8. Define and configure the LOBPCG eigensolver and the BoomerAMG
// preconditioner for A to be used within the solver. Set the matrices
// which define the generalized eigenproblem A x = lambda M x.
Solver * solver = NULL;
Solver * precond = NULL;
if (!slu_solver && !sp_solver && !cpardiso_solver)
{
HypreBoomerAMG * amg = new HypreBoomerAMG(*A);
amg->SetPrintLevel(0);
precond = amg;
}
else
{
#ifdef MFEM_USE_SUPERLU
if (slu_solver)
if (arp_solver)
{
HyprePCG * pcg = new HyprePCG(*A);
pcg->SetTol(1e-12);
pcg->SetPreconditioner(*amg);
solver = pcg;
}
}
#ifdef MFEM_USE_SUPERLU
else if (slu_solver)
{
SuperLUSolver * superlu = new SuperLUSolver(MPI_COMM_WORLD);
superlu->SetPrintStatistics(false);
superlu->SetSymmetricPattern(true);
superlu->SetColumnPermutation(superlu::PARMETIS);
superlu->SetOperator(*Arow);
if (arp_solver)
{
solver = superlu;
}
else
{
SuperLUSolver * superlu = new SuperLUSolver(MPI_COMM_WORLD);
superlu->SetPrintStatistics(false);
superlu->SetSymmetricPattern(true);
superlu->SetColumnPermutation(superlu::PARMETIS);
superlu->SetOperator(*Arow);
precond = superlu;
}
}
#endif
#ifdef MFEM_USE_STRUMPACK
if (sp_solver)
else if (sp_solver)
{
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv,
MPI_COMM_WORLD);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->SetMatching(strumpack::MatchingJob::NONE);
strumpack->SetCompression(strumpack::CompressionType::NONE);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
if (arp_solver)
{
solver = strumpack;
}
else
{
STRUMPACKSolver * strumpack = new STRUMPACKSolver(MPI_COMM_WORLD, argc, argv);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->SetMatching(strumpack::MatchingJob::NONE);
strumpack->SetCompression(strumpack::CompressionType::NONE);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
precond = strumpack;
}
}
#endif
#ifdef MFEM_USE_MKL_CPARDISO
if (cpardiso_solver)
else if (cpardiso_solver)
{
auto cpardiso = new CPardisoSolver(A->GetComm());
cpardiso->SetMatrixType(CPardisoSolver::MatType::REAL_STRUCTURE_SYMMETRIC);
cpardiso->SetPrintLevel(1);
cpardiso->SetOperator(*A);
if (arp_solver)
{
solver = cpardiso;
}
else
{
auto cpardiso = new CPardisoSolver(A->GetComm());
cpardiso->SetMatrixType(CPardisoSolver::MatType::REAL_STRUCTURE_SYMMETRIC);
cpardiso->SetPrintLevel(1);
cpardiso->SetOperator(*A);
precond = cpardiso;
}
#endif
}
#endif
HypreLOBPCG * lobpcg = new HypreLOBPCG(MPI_COMM_WORLD);
lobpcg->SetNumModes(nev);
lobpcg->SetRandomSeed(seed);
lobpcg->SetPreconditioner(*precond);
lobpcg->SetMaxIter(200);
lobpcg->SetTol(1e-8);
lobpcg->SetPrecondUsageMode(1);
lobpcg->SetPrintLevel(1);
lobpcg->SetMassMatrix(*M);
lobpcg->SetOperator(*A);
SymGenEigensolver * eig_solver = NULL;
if (lob_solver)
{
HypreLOBPCG * lobpcg = new HypreLOBPCG(MPI_COMM_WORLD);
lobpcg->SetNumModes(nev);
lobpcg->SetRandomSeed(seed);
lobpcg->SetPreconditioner(*precond);
lobpcg->SetMaxIter(200);
lobpcg->SetTol(1e-8);
lobpcg->SetPrecondUsageMode(1);
lobpcg->SetPrintLevel(1);
eig_solver = lobpcg;
}
#ifdef MFEM_USE_ARPACK
else if (arp_solver)
{
ArPackPSAUPD * arpack = new ArPackPSAUPD(MPI_COMM_WORLD);
arpack->SetNumModes(nev);
arpack->SetMaxIter(400);
arpack->SetTol(1e-8);
arpack->SetMode(3);
arpack->SetPrintLevel(2);
arpack->SetSolver(*solver);
eig_solver = arpack;
}
#endif
eig_solver->SetOperators(*A, *M);
// 9. Compute the eigenmodes and extract the array of eigenvalues. Define a
// parallel grid function to represent each of the eigenmodes returned by
// the solver.
Array<real_t> eigenvalues;
lobpcg->Solve();
lobpcg->GetEigenvalues(eigenvalues);
eig_solver->Solve();
eig_solver->GetEigenvalues(eigenvalues);
ParGridFunction x(fespace);
// 10. Save the refined mesh and the modes in parallel. This output can be
@@ -321,8 +381,8 @@ int main(int argc, char *argv[])
for (int i=0; i<nev; i++)
{
// convert eigenvector from HypreParVector to ParGridFunction
x = lobpcg->GetEigenvector(i);
// convert eigenvector from Vector to ParGridFunction
x.Distribute(eig_solver->GetEigenvector(i));
mode_name << "mode_" << setfill('0') << setw(2) << i << "."
<< setfill('0') << setw(6) << myid;
@@ -350,8 +410,8 @@ int main(int argc, char *argv[])
<< ", Lambda = " << eigenvalues[i] << endl;
}
// convert eigenvector from HypreParVector to ParGridFunction
x = lobpcg->GetEigenvector(i);
// convert eigenvector from Vector to ParGridFunction
x.Distribute(eig_solver->GetEigenvector(i));
mode_sock << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << x << flush
@@ -375,7 +435,8 @@ int main(int argc, char *argv[])
}
// 12. Free the used memory.
delete lobpcg;
delete eig_solver;
delete solver;
delete precond;
delete M;
delete A;
+381
View File
@@ -0,0 +1,381 @@
// MFEM Example 11 - Parallel Version
//
// Compile with: make ex11p
//
// Sample runs: mpirun -np 4 ex11p -m ../data/square-disc.mesh
// mpirun -np 4 ex11p -m ../data/star.mesh
// mpirun -np 4 ex11p -m ../data/escher.mesh
// mpirun -np 4 ex11p -m ../data/fichera.mesh
// mpirun -np 4 ex11p -m ../data/square-disc-p2.vtk -o 2
// mpirun -np 4 ex11p -m ../data/square-disc-p3.mesh -o 3
// mpirun -np 4 ex11p -m ../data/square-disc-nurbs.mesh -o -1
// mpirun -np 4 ex11p -m ../data/disc-nurbs.mesh -o -1 -n 20
// mpirun -np 4 ex11p -m ../data/pipe-nurbs.mesh -o -1
// mpirun -np 4 ex11p -m ../data/ball-nurbs.mesh -o 2
// mpirun -np 4 ex11p -m ../data/star-surf.mesh
// mpirun -np 4 ex11p -m ../data/square-disc-surf.mesh
// mpirun -np 4 ex11p -m ../data/inline-segment.mesh
// mpirun -np 4 ex11p -m ../data/amr-quad.mesh
// mpirun -np 4 ex11p -m ../data/amr-hex.mesh
// mpirun -np 4 ex11p -m ../data/mobius-strip.mesh -n 8
// mpirun -np 4 ex11p -m ../data/klein-bottle.mesh -n 10
//
// Description: This example code demonstrates the use of MFEM to solve the
// eigenvalue problem -Delta u = lambda u with homogeneous
// Dirichlet boundary conditions.
//
// We compute a number of the lowest eigenmodes by discretizing
// the Laplacian and Mass operators using a FE space of the
// specified order, or an isoparametric/isogeometric space if
// order < 1 (quadratic for quadratic curvilinear mesh, NURBS for
// NURBS mesh, etc.)
//
// The example highlights the use of the LOBPCG and ARPACK
// eigenvalue solvers together with the BoomerAMG preconditioner
// in HYPRE, as well as optionally the SuperLU parallel direct
// solver. Reusing a single GLVis visualization window for
// multiple eigenfunctions is also illustrated.
//
// We recommend viewing Example 1 before viewing this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
int num_procs, myid;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
MPI_Comm_rank(MPI_COMM_WORLD, &myid);
// 2. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int ser_ref_levels = 2;
int par_ref_levels = 1;
int order = 1;
int nev = 5;
bool slu_solver = false;
bool use_arpack = false;
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&nev, "-n", "--num-eigs",
"Number of desired eigenmodes.");
#ifdef MFEM_USE_SUPERLU
args.AddOption(&slu_solver, "-slu", "--superlu", "-no-slu",
"--no-superlu", "Use the SuperLU Solver.");
#endif
#ifdef MFEM_USE_ARPACK
args.AddOption(&use_arpack, "-arpack", "--use-arpack", "-no-arpack",
"--no-arpack",
"Enable or disable the use of ARPACK.");
#endif
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
MPI_Finalize();
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 3. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh *mesh;
ifstream imesh(mesh_file);
if (!imesh)
{
if (myid == 0)
{
cerr << "\nCan not open mesh file: " << mesh_file << '\n' << endl;
}
MPI_Finalize();
return 2;
}
mesh = new Mesh(imesh, 1, 1);
imesh.close();
int dim = mesh->Dimension();
// 4. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement (2 by default, or
// specified on the command line with -rs).
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
// 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution (1 time by
// default, or specified on the command line with -rp). Once the parallel
// mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int lev = 0; lev < par_ref_levels; lev++)
{
pmesh->UniformRefinement();
}
// 6. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
}
else if (pmesh->GetNodes())
{
fec = pmesh->GetNodes()->OwnFEC();
}
else
{
fec = new H1_FECollection(order = 1, dim);
}
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
HYPRE_Int size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of unknowns: " << size << endl;
}
// 7. Set up the parallel bilinear forms a(.,.) and m(.,.) on the finite
// element space. The first corresponds to the Laplacian operator -Delta,
// while the second is a simple mass matrix needed on the right hand side
// of the generalized eigenvalue problem below. The boundary conditions
// are implemented by elimination with special values on the diagonal to
// shift the Dirichlet eigenvalues out of the computational range. After
// serial and parallel assembly we extract the corresponding parallel
// matrices A and M.
ConstantCoefficient one(1.0);
Array<int> ess_bdr;
if (pmesh->bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
ess_bdr = 1;
}
ParBilinearForm *a = new ParBilinearForm(fespace);
a->AddDomainIntegrator(new DiffusionIntegrator(one));
if (pmesh->bdr_attributes.Size() == 0)
{
// Add a mass term if the mesh has no boundary, e.g. periodic mesh or
// closed surface.
a->AddDomainIntegrator(new MassIntegrator(one));
}
a->Assemble();
a->EliminateEssentialBCDiag(ess_bdr, 1.0);
a->Finalize();
ParBilinearForm *m = new ParBilinearForm(fespace);
m->AddDomainIntegrator(new MassIntegrator(one));
m->Assemble();
// shift the eigenvalue corresponding to eliminated dofs to a large value
m->EliminateEssentialBCDiag(ess_bdr, numeric_limits<double>::min());
m->Finalize();
HypreParMatrix *A = a->ParallelAssemble();
HypreParMatrix *M = m->ParallelAssemble();
#ifdef MFEM_USE_SUPERLU
Operator * Arow = NULL;
if (slu_solver)
{
Arow = new SuperLURowLocMatrix(*A);
}
#endif
delete a;
delete m;
// 8. Define and configure the LOBPCG eigensolver and the BoomerAMG
// preconditioner for A to be used within the solver. Set the matrices
// which define the generalized eigenproblem A x = lambda M x.
Eigensolver * esolver = NULL;
Solver * solver = NULL;
Solver * precond = NULL;
if (!slu_solver)
{
HypreBoomerAMG * amg = new HypreBoomerAMG(*A);
amg->SetPrintLevel(0);
precond = amg;
#ifdef MFEM_USE_ARPACK
if ( use_arpack )
{
HyprePCG * pcg = new HyprePCG(*A);
pcg->SetTol(1e-12);
pcg->SetMaxIter(200);
pcg->SetPreconditioner(*amg);
pcg->SetPrintLevel(0);
solver = pcg;
}
#endif
}
#ifdef MFEM_USE_SUPERLU
else
{
SuperLUSolver * superlu = new SuperLUSolver(MPI_COMM_WORLD);
superlu->SetPrintStatistics(false);
superlu->SetSymmetricPattern(true);
superlu->SetColumnPermutation(superlu::PARMETIS);
superlu->SetOperator(*Arow);
solver = use_arpack?superlu:NULL;
precond = use_arpack?NULL:superlu;
}
#endif
if ( use_arpack )
{
ParArPackSym * arpack = new ParArPackSym(MPI_COMM_WORLD);
arpack->SetMode(3);
arpack->SetPrintLevel(2);
arpack->SetSolver(*solver);
esolver = arpack;
}
else
{
HypreLOBPCG * lobpcg = new HypreLOBPCG(MPI_COMM_WORLD);
lobpcg->SetPreconditioner(*precond);
lobpcg->SetPrecondUsageMode(1);
lobpcg->SetPrintLevel(1);
esolver = lobpcg;
}
esolver->SetNumModes(nev);
esolver->SetMaxIter(100);
esolver->SetTol(1e-8);
esolver->SetMassMatrix(*M);
esolver->SetOperator(*A);
// 9. Compute the eigenmodes and extract the array of eigenvalues. Define a
// parallel grid function to represent each of the eigenmodes returned by
// the solver.
Array<double> eigenvalues;
esolver->Solve();
esolver->GetEigenvalues(eigenvalues);
if ( myid == 0 && use_arpack )
{
cout << endl;
for (int i=0; i<eigenvalues.Size(); i++)
{
cout << "Eigenvalue lambda " << eigenvalues[i] << endl;
}
cout << endl;
}
ParGridFunction x(fespace);
// 10. Save the refined mesh and the modes in parallel. This output can be
// viewed later using GLVis: "glvis -np <np> -m mesh -g mode".
{
ostringstream mesh_name, mode_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
for (int i=0; i<nev; i++)
{
// convert eigenvector from HypreParVector to ParGridFunction
x.Distribute(esolver->GetEigenvector(i));
mode_name << "mode_" << setfill('0') << setw(2) << i << "."
<< setfill('0') << setw(6) << myid;
ofstream mode_ofs(mode_name.str().c_str());
mode_ofs.precision(8);
x.Save(mode_ofs);
mode_name.str("");
}
}
// 11. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream mode_sock(vishost, visport);
mode_sock.precision(8);
for (int i=0; i<nev; i++)
{
if ( myid == 0 )
{
cout << "Eigenmode " << i+1 << '/' << nev
<< ", Lambda = " << eigenvalues[i] << endl;
}
// convert eigenvector from HypreParVector to ParGridFunction
x.Distribute(esolver->GetEigenvector(i));
mode_sock << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << x << flush
<< "window_title 'Eigenmode " << i+1 << '/' << nev
<< ", Lambda = " << eigenvalues[i] << "'" << endl;
char c;
if (myid == 0)
{
cout << "press (q)uit or (c)ontinue --> " << flush;
cin >> c;
}
MPI_Bcast(&c, 1, MPI_CHAR, 0, MPI_COMM_WORLD);
if (c != 'c')
{
break;
}
}
mode_sock.close();
}
// 12. Free the used memory.
delete esolver;
delete solver;
delete precond;
delete M;
delete A;
delete fespace;
if (order > 0)
{
delete fec;
}
delete pmesh;
MPI_Finalize();
return 0;
}
+5 -5
View File
@@ -276,8 +276,8 @@ int main(int argc, char *argv[])
for (int i=0; i<nev; i++)
{
// convert eigenvector from HypreParVector to ParGridFunction
x = lobpcg->GetEigenvector(i);
// convert eigenvector from Vector to ParGridFunction
x.Distribute(lobpcg->GetEigenvector(i));
mode_name << "mode_" << setfill('0') << setw(2) << i << "."
<< setfill('0') << setw(6) << myid;
@@ -303,7 +303,7 @@ int main(int argc, char *argv[])
pmesh->Print(adios2output);
for (int i=0; i<nev; i++)
{
x = lobpcg->GetEigenvector(i);
x.Distribute(lobpcg->GetEigenvector(i));
// x is a temporary that must be saved immediately
x.Save(adios2output, "mode_" + std::to_string(i));
}
@@ -326,8 +326,8 @@ int main(int argc, char *argv[])
<< ", Lambda = " << eigenvalues[i] << endl;
}
// convert eigenvector from HypreParVector to ParGridFunction
x = lobpcg->GetEigenvector(i);
// convert eigenvector from Vector to ParGridFunction
x.Distribute(lobpcg->GetEigenvector(i));
mode_sock << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << x << flush
+282
View File
@@ -0,0 +1,282 @@
// MFEM Example 13
//
// Compile with: make ex3p
//
// Sample runs: ex13 -m ../data/star.mesh -s 5
// ex13 -m ../data/square-disc.mesh -o 2 -n 4 // minres fails to conv.
// ex13 -m ../data/beam-hex.mesh
// ex13 -m ../data/square-disc.mesh -rs 1 -s 26
// ex13 -m ../data/square-disc-nurbs.mesh -rs 3 -s 26
// ex13 -m ../data/amr-quad.mesh -o 2 // minres fails to conv.
// ex13 -m ../data/mobius-strip.mesh -n 8
//
// Description: This example code solves a simple 3D electromagnetic
// eigenmode problem corresponding to the second order
// Maxwell equation curl curl E = lambda E with boundary
// condition E x n = 0. We discretize with Nedelec finite
// elements.
//
// The example demonstrates the use of H(curl) finite element
// spaces with the curl-curl and the (vector finite element) mass
// bilinear form, as well as the use of the ARPACK eigenmode
// solver for symmetric matrices using the shift-invert mode.
//
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
#ifdef MFEM_USE_ARPACK
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/beam-tet.mesh";
int order = 1;
int nev = 5;
int sr = 2;
double sigma = 11.0;
bool visualization = 1;
bool arp_solver = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&nev, "-n", "--num-eigs",
"Number of desired eigenmodes.");
args.AddOption(&sr, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&sigma, "-s", "--shift",
"Average of the desired eigenvalue range.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume meshes
// with the same code.
Mesh *mesh;
ifstream imesh(mesh_file);
if (!imesh)
{
cerr << "\nCan not open mesh file: " << mesh_file << '\n' << endl;
return 2;
}
mesh = new Mesh(imesh, 1, 1);
imesh.close();
int dim = mesh->Dimension();
// 3. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement.
{
int ref_levels = sr;
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
}
}
// 4. Define a finite element space on the mesh. Here we use the lowest
// order Nedelec finite elements, but we can easily switch
// to higher-order spaces by changing the value of p.
FiniteElementCollection *fec = new ND_FECollection(order, dim);
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
int size = fespace->GetVSize();
cout << "Number of unknowns: " << size << endl;
cout << "Number of boundary attributes: " << mesh->bdr_attributes.Max()
<< endl;
// 5. Set up the parallel bilinear form corresponding to the EM diffusion
// operator curl muinv curl - sigma I, by adding the curl-curl and the
// mass domain integrators and finally imposing homogeneous Dirichlet
// boundary conditions. The boundary conditions are implemented by
// marking all the boundary attributes from the mesh as essential
// (Dirichlet). After serial and parallel assembly we extract the
// parallel matrices A and M.
Coefficient *muinv = new ConstantCoefficient(1.0);
Coefficient *negSigma = new ConstantCoefficient(-sigma);
BilinearForm *a = new BilinearForm(fespace);
a->AddDomainIntegrator(new CurlCurlIntegrator(*muinv));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*negSigma));
a->Assemble();
Array<int> ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
a->EliminateEssentialBC(ess_bdr);
a->Finalize();
BilinearForm *m = new BilinearForm(fespace);
m->AddDomainIntegrator(new VectorFEMassIntegrator());
m->Assemble();
m->EliminateEssentialBCDiag(ess_bdr, sqrt(numeric_limits<double>::min()));
m->Finalize();
// 6. Define a parallel grid function to approximate each of the
// eigenmodes returned by the solver. Use this as a template to
// create a special multi-vector object needed by the eigensolver
// which is then initialized with random values.
GridFunction x(fespace);
x = 0.0;
// 7. Define and configure the GMRES
// solver to be used within the eigensolver.
Solver * solver = NULL;
if ( false )
{
GMRESSolver * gmres = new GMRESSolver();
gmres->SetOperator(*a);
gmres->SetRelTol(1e-8);
gmres->SetMaxIter(1000);
gmres->SetPrintLevel(0);
solver = gmres;
}
else
{
#ifndef MFEM_USE_SUITESPARSE
cout << "Building MINRESSolver" << endl;
MINRESSolver * minres = new MINRESSolver();
minres->SetRelTol(1e-12);
minres->SetMaxIter(1000);
minres->SetPrintLevel(0);
solver = minres;
#else
// 7. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
cout << "Building UMFPackSolver" << endl;
UMFPackSolver * umf_solver = new UMFPackSolver;
umf_solver->Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
solver = umf_solver;
#endif
}
solver->SetOperator(a->SpMat());
// 7. Define and configure the ARPACK eigensolver
SymGenEigensolver * eig_solver = NULL;
if (arp_solver)
{
ArPackSAUPD * arpack = new ArPackSAUPD();
arpack->SetNumModes(nev);
arpack->SetMaxIter(400);
arpack->SetTol(1e-8);
arpack->SetShift(sigma);
arpack->SetMode(3);
arpack->SetPrintLevel(2);
arpack->SetSolver(*solver);
eig_solver = arpack;
}
eig_solver->SetOperators(*a, *m);
// Obtain the eigenvalues and eigenvectors
Array<double> eigenvalues(nev);
eigenvalues = -1.0;
// arpack->Solve(eigenvalues, *eigenvectors);
eig_solver->Solve();
eig_solver->GetEigenvalues(eigenvalues);
cout << endl;
std::ios::fmtflags old_fmt = cout.flags();
cout.setf(std::ios::scientific);
std::streamsize old_prec = cout.precision(14);
for (int i=0; i<min(nev,eigenvalues.Size()); i++)
{
cout << "Eigenvalue lambda " << eigenvalues[i] << endl;
}
cout.precision(old_prec);
cout.flags(old_fmt);
cout << endl;
VisItDataCollection visit_dc("Example13", mesh);
GridFunction ** mode = new GridFunction*[min(nev,eigenvalues.Size())];
for (int i=0; i<min(nev,eigenvalues.Size()); i++)
{
mode[i] = new GridFunction(fespace);
*mode[i] = eig_solver->GetEigenvector(i);
ostringstream modeName;
modeName << "mode_" << setfill('0') << setw(2) << i;
visit_dc.RegisterField(modeName.str().c_str(),mode[i]);
}
visit_dc.Save();
// 8. Save the refined mesh and the modes. This output can
// be viewed later using GLVis: "glvis -m mesh -g mode".
{
ofstream mesh_ofs("refined.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
for (int i=0; i<min(nev,eigenvalues.Size()); i++)
{
x = eig_solver->GetEigenvector(i);
ostringstream modeName;
modeName << "mode_" << setfill('0') << setw(2) << i;
ofstream mode_ofs(modeName.str().c_str());
mode_ofs.precision(8);
x.Save(mode_ofs);
modeName.str("");
}
}
// 9. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream mode_sock(vishost, visport);
mode_sock.precision(8);
for (int i=0; i<min(nev,eigenvalues.Size()); i++)
{
x = eig_solver->GetEigenvector(i);
mode_sock << "solution\n" << *mesh << x << flush;
char c;
cout << "press (q)uit or (c)ontinue --> " << flush;
cin >> c;
if (c != 'c')
{
break;
}
}
mode_sock.close();
}
// 10. Free the used memory.
delete a;
delete m;
delete negSigma;
delete muinv;
delete eig_solver;
delete solver;
// delete X;
delete fespace;
delete fec;
delete mesh;
return 0;
}
#endif // MFEM_USE_ARPACK
+4 -4
View File
@@ -215,8 +215,8 @@ int main(int argc, char *argv[])
for (int i=0; i<nev; i++)
{
// convert eigenvector from HypreParVector to ParGridFunction
x = ame->GetEigenvector(i);
// convert eigenvector from Vector to ParGridFunction
x.Distribute(ame->GetEigenvector(i));
mode_name << "mode_" << setfill('0') << setw(2) << i << "."
<< setfill('0') << setw(6) << myid;
@@ -244,8 +244,8 @@ int main(int argc, char *argv[])
<< ", Lambda = " << eigenvalues[i] << endl;
}
// convert eigenvector from HypreParVector to ParGridFunction
x = ame->GetEigenvector(i);
// convert eigenvector from Vector to ParGridFunction
x.Distribute(ame->GetEigenvector(i));
mode_sock << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << x << flush
+4 -4
View File
@@ -228,7 +228,7 @@ int main(int argc, char *argv[])
for (int i=0; i<nev; i++)
{
// convert eigenvector from HypreParVector to ParGridFunction
x = ame->GetEigenvector(i);
x.Distribute(ame->GetEigenvector(i));
curl.Mult(x, dx);
mode_name << "mode_" << setfill('0') << setw(2) << i << "."
@@ -295,7 +295,7 @@ int main(int argc, char *argv[])
}
// convert eigenvector from HypreParVector to ParGridFunction
x = ame->GetEigenvector(i);
x.Distribute(ame->GetEigenvector(i));
curl.Mult(x, dx);
{
@@ -469,7 +469,7 @@ int main(int argc, char *argv[])
}
// convert eigenvector from HypreParVector to ParGridFunction
x = ame->GetEigenvector(i);
x.Distribute(ame->GetEigenvector(i));
curl.Mult(x, dx);
{
@@ -599,7 +599,7 @@ int main(int argc, char *argv[])
}
// convert eigenvector from HypreParVector to ParGridFunction
x = ame->GetEigenvector(i);
x.Distribute(ame->GetEigenvector(i));
curl.Mult(x, dx);
mode_sock << "parallel " << num_procs << " " << myid << "\n"
+3 -3
View File
@@ -658,7 +658,7 @@ void ScalarWaveGuide(int mode, ParGridFunction &x)
lobpcg.SetOperator(*A);
lobpcg.Solve();
x = lobpcg.GetEigenvector(mode);
x.Distribute(lobpcg.GetEigenvector(mode));
delete A;
delete M;
@@ -714,7 +714,7 @@ void VectorWaveGuide(int mode, ParGridFunction &x)
ame.SetOperator(*A);
ame.Solve();
x = ame.GetEigenvector(mode);
x.Distribute(ame.GetEigenvector(mode));
delete A;
delete M;
@@ -780,7 +780,7 @@ void PseudoScalarWaveGuide(int mode, ParGridFunction &x_l2)
lobpcg.SetOperator(*A);
lobpcg.Solve();
x = lobpcg.GetEigenvector(mode);
x.Distribute(lobpcg.GetEigenvector(mode));
x_l2.ProjectCoefficient(xCoef);
+11 -52
View File
@@ -5,8 +5,8 @@
// Sample runs:
// ex37 -alpha 10
// ex37 -alpha 10 -pv
// ex37 -lambda 0.1 -mu 0.1
// ex37 -o 2 -alpha 5.0 -mi 50 -vf 0.4 -ntol 1e-5
// ex37 -lambda 0.1 -mu 0.1 -growth 1
// ex37 -o 2 -alpha 10.0 -mi 50 -vf 0.4 -ntol 1e-5 -growth 1.5
// ex37 -r 6 -o 1 -alpha 25.0 -epsilon 0.02 -mi 50 -ntol 1e-5
//
// Description: This example code demonstrates the use of MFEM to solve a
@@ -55,53 +55,6 @@
using namespace std;
using namespace mfem;
/**
* @brief Bregman projection of ρ = sigmoid(ψ) onto the subspace
* ∫_Ω ρ dx = θ vol(Ω) as follows:
*
* 1. Compute the root of the R → R function
* f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω)
* 2. Set ψ ← ψ + c.
*
* @param psi a GridFunction to be updated
* @param target_volume θ vol(Ω)
* @param tol Newton iteration tolerance
* @param max_its Newton maximum iteration number
* @return real_t Final volume, ∫_Ω sigmoid(ψ)
*/
real_t proj(GridFunction &psi, real_t target_volume, real_t tol=1e-12,
int max_its=10)
{
MappedGridFunctionCoefficient sigmoid_psi(&psi, sigmoid);
MappedGridFunctionCoefficient der_sigmoid_psi(&psi, der_sigmoid);
LinearForm int_sigmoid_psi(psi.FESpace());
int_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(sigmoid_psi));
LinearForm int_der_sigmoid_psi(psi.FESpace());
int_der_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(
der_sigmoid_psi));
bool done = false;
for (int k=0; k<max_its; k++) // Newton iteration
{
int_sigmoid_psi.Assemble(); // Recompute f(c) with updated ψ
const real_t f = int_sigmoid_psi.Sum() - target_volume;
int_der_sigmoid_psi.Assemble(); // Recompute df(c) with updated ψ
const real_t df = int_der_sigmoid_psi.Sum();
const real_t dc = -f/df;
psi += dc;
if (abs(dc) < tol) { done = true; break; }
}
if (!done)
{
mfem_warning("Projection reached maximum iteration without converging. "
"Result may not be accurate.");
}
int_sigmoid_psi.Assemble();
return int_sigmoid_psi.Sum();
}
/*
* ---------------------------------------------------------------
* ALGORITHM PREAMBLE
@@ -180,10 +133,11 @@ int main(int argc, char *argv[])
int ref_levels = 5;
int order = 2;
real_t alpha = 1.0;
real_t growth = 2;
real_t epsilon = 0.01;
real_t vol_fraction = 0.5;
int max_it = 1e3;
real_t itol = 1e-1;
real_t itol = 1e-2;
real_t ntol = 1e-4;
real_t rho_min = 1e-6;
real_t lambda = 1.0;
@@ -198,6 +152,8 @@ int main(int argc, char *argv[])
"Order (degree) of the finite elements.");
args.AddOption(&alpha, "-alpha", "--alpha-step-length",
"Step length for gradient descent.");
args.AddOption(&growth, "-growth", "--alpha-growth-rate",
"Growth rate of step length for gradient descent.");
args.AddOption(&epsilon, "-epsilon", "--epsilon-thickness",
"Length scale for ρ.");
args.AddOption(&max_it, "-mi", "--max-it",
@@ -332,6 +288,7 @@ int main(int argc, char *argv[])
}
FilterSolver->SetEssentialBoundary(ess_bdr_filter);
FilterSolver->SetupFEM();
FilterSolver->AssembleDiffusionBilinear();
BilinearForm mass(&control_fes);
mass.AddDomainIntegrator(new InverseIntegrator(new MassIntegrator(one)));
@@ -385,7 +342,7 @@ int main(int argc, char *argv[])
// 11. Iterate:
for (int k = 1; k <= max_it; k++)
{
if (k > 1) { alpha *= ((real_t) k) / ((real_t) k-1); }
if (k > 1) { alpha = std::pow((real_t) k,growth); }
mfem::out << "\nStep = " << k << std::endl;
@@ -422,7 +379,9 @@ int main(int argc, char *argv[])
// Step 5 - Update design variable ψ ← proj(ψ - αG)
psi.Add(-alpha, grad);
const real_t material_volume = proj(psi, target_volume);
GridFunction alpha_grad(grad);
alpha_grad *= alpha;
const real_t material_volume = proj(psi, alpha_grad, target_volume);
// Compute ||ρ - ρ_old|| in control fes.
real_t norm_increment = zerogf.ComputeL1Error(succ_diff_rho);
+189 -29
View File
@@ -137,7 +137,7 @@ public:
exponent(exponent_), rho_min(rho_min_)
{
MFEM_ASSERT(rho_min_ >= 0.0, "rho_min must be >= 0");
MFEM_ASSERT(rho_min_ < 1.0, "rho_min must be > 1");
MFEM_ASSERT(rho_min_ < 1.0, "rho_min must be < 1");
MFEM_ASSERT(u, "displacement field is not set");
MFEM_ASSERT(rho_filter, "density field is not set");
}
@@ -231,9 +231,12 @@ private:
FiniteElementCollection * fec = nullptr;
FiniteElementSpace * fes = nullptr;
Array<int> ess_bdr;
Array<int> ess_tdof_list;
Array<int> neumann_bdr;
GridFunction * u = nullptr;
LinearForm * b = nullptr;
BilinearForm * a = nullptr;
OperatorPtr A;
bool parallel;
#ifdef MFEM_USE_MPI
ParMesh * pmesh = nullptr;
@@ -267,6 +270,8 @@ public:
void ResetFEM();
void SetupFEM();
void UpdateEssentialTDofs();
void AssembleDiffusionBilinear(bool update_ess_tdofs=true);
void Solve();
GridFunction * GetFEMSolution();
LinearForm * GetLinearForm() {return b;}
@@ -371,6 +376,130 @@ public:
};
/**
* @brief Bregman projection of ρ = sigmoid(ψ) onto the subspace
* ∫_Ω ρ dx = θ vol(Ω) as follows:
*
* 1. Compute the root of the R → R function
* f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω)
* using the Illinois method
* 2. Set ψ ← ψ + c.
*
* @param psi a GridFunction to be updated
* @param alpha_grad alpha multiplied by gradient
* @param target_volume θ vol(Ω)
* @param tol Illinois iteration tolerance
* @param max_its Illinois maximum iteration number
* @return real_t Final volume (∫_Ω sigmoid(ψ) dx)
*/
real_t proj(GridFunction &psi, GridFunction &alpha_grad, real_t target_volume,
real_t tol = 1e-12, int max_its = 100)
{
#ifdef MFEM_USE_MPI
FiniteElementSpace *fes = psi.FESpace();
ParFiniteElementSpace *pfes = dynamic_cast<ParFiniteElementSpace*>(fes);
#endif
ConstantCoefficient zero_cf(0.0);
real_t a = -alpha_grad.ComputeMaxError(zero_cf);
real_t b = -a;
real_t y = 0.0;
MappedGridFunctionCoefficient sigmoid_psi(
&psi, [&y](const real_t x) { return sigmoid(x + y); });
std::unique_ptr<LinearForm> int_sigmoid_psi;
#ifdef MFEM_USE_MPI
ParGridFunction *par_psi = dynamic_cast<ParGridFunction *>(&psi);
if (par_psi)
{
int_sigmoid_psi.reset(new ParLinearForm(par_psi->ParFESpace()));
}
else
{
int_sigmoid_psi.reset(new LinearForm(psi.FESpace()));
}
#else
int_sigmoid_psi.reset(new LinearForm(psi.FESpace()));
#endif
int_sigmoid_psi->AddDomainIntegrator(new DomainLFIntegrator(sigmoid_psi));
y = a;
int_sigmoid_psi->Assemble();
real_t f_a = int_sigmoid_psi->Sum(); // f_a := f(a) + θ vol(Ω)
y = b;
int_sigmoid_psi->Assemble();
real_t f_b = int_sigmoid_psi->Sum(); // f_b := f(b) + θ vol(Ω)
#ifdef MFEM_USE_MPI
if (pfes)
{
MPI_Allreduce(MPI_IN_PLACE, &f_a, 1, MPITypeMap<real_t>::mpi_type,
MPI_SUM, MPI_COMM_WORLD);
MPI_Allreduce(MPI_IN_PLACE, &f_b, 1, MPITypeMap<real_t>::mpi_type,
MPI_SUM, MPI_COMM_WORLD);
}
#endif
f_a -= target_volume; // f_a := f(a)
f_b -= target_volume; // f_b := f(b)
real_t c = 0.0;
real_t f_c = 0.0;
int side = 0;
bool done = false;
for (int k=0; k < max_its; k++)
{
c = (f_a * b - f_b * a) / (f_a - f_b);
if (abs(b - a) < tol * abs(b + a)) { done = true; break; }
y = c;
int_sigmoid_psi->Assemble();
f_c = int_sigmoid_psi->Sum(); // f_c := f(c) + θ vol(Ω)
#ifdef MFEM_USE_MPI
if (pfes)
{
MPI_Allreduce(MPI_IN_PLACE, &f_c, 1, MPITypeMap<real_t>::mpi_type,
MPI_SUM, MPI_COMM_WORLD);
}
#endif
f_c -= target_volume; // f_c := f(c)
if (f_c * f_b > 0)
{
b = c;
f_b = f_c;
if (side == -1) { f_a /= 2.0; }
side = -1;
}
else if (f_c * f_a > 0)
{
a = c;
f_a = f_c;
if (side == 1) { f_b /= 2.0; }
side = 1;
}
else
{
done = true; break;
}
}
if (!done)
{
mfem_warning("Projection reached maximum iteration without converging. "
"Result may not be accurate.");
}
y = 0.0;
psi += c;
int_sigmoid_psi->Assemble();
real_t material_volume = int_sigmoid_psi->Sum();
#ifdef MFEM_USE_MPI
if (pfes)
{
MPI_Allreduce(MPI_IN_PLACE, &material_volume, 1,
MPITypeMap<real_t>::mpi_type, MPI_SUM, MPI_COMM_WORLD);
}
#endif
return material_volume;
}
// Poisson solver
@@ -422,12 +551,8 @@ void DiffusionSolver::SetupFEM()
}
}
void DiffusionSolver::Solve()
void DiffusionSolver::UpdateEssentialTDofs()
{
OperatorPtr A;
Vector B, X;
Array<int> ess_tdof_list;
#ifdef MFEM_USE_MPI
if (parallel)
{
@@ -440,7 +565,39 @@ void DiffusionSolver::Solve()
#else
fes->GetEssentialTrueDofs(ess_bdr,ess_tdof_list);
#endif
*u=0.0;
}
void DiffusionSolver::AssembleDiffusionBilinear(bool update_ess_tdofs)
{
if (update_ess_tdofs)
{
UpdateEssentialTDofs();
}
#ifdef MFEM_USE_MPI
if (parallel)
{
a = new ParBilinearForm(pfes);
}
else
{
a = new BilinearForm(fes);
}
#else
a = new BilinearForm(fes);
#endif
a->AddDomainIntegrator(new DiffusionIntegrator(*diffcf));
if (masscf)
{
a->AddDomainIntegrator(new MassIntegrator(*masscf));
}
a->Assemble();
a->FormSystemMatrix(ess_tdof_list, A);
}
void DiffusionSolver::Solve()
{
Vector B, X;
if (b)
{
delete b;
@@ -475,31 +632,33 @@ void DiffusionSolver::Solve()
b->Assemble();
BilinearForm * a = nullptr;
#ifdef MFEM_USE_MPI
if (parallel)
{
a = new ParBilinearForm(pfes);
}
else
{
a = new BilinearForm(fes);
}
#else
a = new BilinearForm(fes);
#endif
a->AddDomainIntegrator(new DiffusionIntegrator(*diffcf));
if (masscf)
{
a->AddDomainIntegrator(new MassIntegrator(*masscf));
}
a->Assemble();
*u=0.0;
if (essbdr_cf)
{
u->ProjectBdrCoefficient(*essbdr_cf,ess_bdr);
}
a->FormLinearSystem(ess_tdof_list, *u, *b, A, X, B);
#ifdef MFEM_USE_MPI
if (parallel)
{
X.SetSize(pfes->TrueVSize());
B.SetSize(pfes->TrueVSize());
dynamic_cast<ParGridFunction*>(u)->ParallelAssemble(X);
dynamic_cast<ParLinearForm*>(b)->ParallelAssemble(B);
dynamic_cast<ParBilinearForm*>(a)->ParallelEliminateTDofsInRHS(
ess_tdof_list, X, B);
}
else
{
X.NewDataAndSize(u->GetData(), u->Size());
B.NewDataAndSize(b->GetData(), b->Size());
a->EliminateVDofsInRHS(ess_tdof_list, X, B);
}
#else
X.NewDataAndSize(u->GetData(), u->Size());
B.NewDataAndSize(b->GetData(), b->Size());
a->EliminateVDofsInRHS(ess_tdof_list, X, B);
#endif
CGSolver * cg = nullptr;
Solver * M = nullptr;
@@ -528,7 +687,6 @@ void DiffusionSolver::Solve()
delete M;
delete cg;
a->RecoverFEMSolution(X, *b, *u);
delete a;
}
GridFunction * DiffusionSolver::GetFEMSolution()
@@ -560,6 +718,8 @@ DiffusionSolver::~DiffusionSolver()
#endif
delete fec; fec = nullptr;
delete b;
A.Clear();
delete a;
}
+11 -60
View File
@@ -4,8 +4,8 @@
//
// Sample runs:
// mpirun -np 4 ex37p -alpha 10 -pv
// mpirun -np 4 ex37p -lambda 0.1 -mu 0.1
// mpirun -np 4 ex37p -o 2 -alpha 5.0 -mi 50 -vf 0.4 -ntol 1e-5
// mpirun -np 4 ex37p -lambda 0.1 -mu 0.1 -growth 1
// mpirun -np 4 ex37p -o 2 -alpha 10.0 -mi 50 -vf 0.4 -ntol 1e-5 -growth 1.5
// mpirun -np 4 ex37p -r 6 -o 2 -alpha 10.0 -epsilon 0.02 -mi 50 -ntol 1e-5
//
// Description: This example code demonstrates the use of MFEM to solve a
@@ -54,61 +54,6 @@
using namespace std;
using namespace mfem;
/**
* @brief Bregman projection of ρ = sigmoid(ψ) onto the subspace
* ∫_Ω ρ dx = θ vol(Ω) as follows:
*
* 1. Compute the root of the R → R function
* f(c) = ∫_Ω sigmoid(ψ + c) dx - θ vol(Ω)
* 2. Set ψ ← ψ + c.
*
* @param psi a GridFunction to be updated
* @param target_volume θ vol(Ω)
* @param tol Newton iteration tolerance
* @param max_its Newton maximum iteration number
* @return real_t Final volume, ∫_Ω sigmoid(ψ)
*/
real_t proj(ParGridFunction &psi, real_t target_volume, real_t tol=1e-12,
int max_its=10)
{
MappedGridFunctionCoefficient sigmoid_psi(&psi, sigmoid);
MappedGridFunctionCoefficient der_sigmoid_psi(&psi, der_sigmoid);
ParLinearForm int_sigmoid_psi(psi.ParFESpace());
int_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(sigmoid_psi));
ParLinearForm int_der_sigmoid_psi(psi.ParFESpace());
int_der_sigmoid_psi.AddDomainIntegrator(new DomainLFIntegrator(
der_sigmoid_psi));
bool done = false;
for (int k=0; k<max_its; k++) // Newton iteration
{
int_sigmoid_psi.Assemble(); // Recompute f(c) with updated ψ
real_t f = int_sigmoid_psi.Sum();
MPI_Allreduce(MPI_IN_PLACE, &f, 1, MPITypeMap<real_t>::mpi_type,
MPI_SUM, MPI_COMM_WORLD);
f -= target_volume;
int_der_sigmoid_psi.Assemble(); // Recompute df(c) with updated ψ
real_t df = int_der_sigmoid_psi.Sum();
MPI_Allreduce(MPI_IN_PLACE, &df, 1, MPITypeMap<real_t>::mpi_type,
MPI_SUM, MPI_COMM_WORLD);
const real_t dc = -f/df;
psi += dc;
if (abs(dc) < tol) { done = true; break; }
}
if (!done)
{
mfem_warning("Projection reached maximum iteration without converging. "
"Result may not be accurate.");
}
int_sigmoid_psi.Assemble();
real_t material_volume = int_sigmoid_psi.Sum();
MPI_Allreduce(MPI_IN_PLACE, &material_volume, 1,
MPITypeMap<real_t>::mpi_type, MPI_SUM, MPI_COMM_WORLD);
return material_volume;
}
/*
* ---------------------------------------------------------------
* ALGORITHM PREAMBLE
@@ -193,10 +138,11 @@ int main(int argc, char *argv[])
int ref_levels = 5;
int order = 2;
real_t alpha = 1.0;
real_t growth = 2;
real_t epsilon = 0.01;
real_t vol_fraction = 0.5;
int max_it = 1e3;
real_t itol = 1e-1;
real_t itol = 1e-2;
real_t ntol = 1e-4;
real_t rho_min = 1e-6;
real_t lambda = 1.0;
@@ -211,6 +157,8 @@ int main(int argc, char *argv[])
"Order (degree) of the finite elements.");
args.AddOption(&alpha, "-alpha", "--alpha-step-length",
"Step length for gradient descent.");
args.AddOption(&growth, "-growth", "--alpha-growth-rate",
"Growth rate of step length for gradient descent.");
args.AddOption(&epsilon, "-epsilon", "--epsilon-thickness",
"Length scale for ρ.");
args.AddOption(&max_it, "-mi", "--max-it",
@@ -359,6 +307,7 @@ int main(int argc, char *argv[])
}
FilterSolver->SetEssentialBoundary(ess_bdr_filter);
FilterSolver->SetupFEM();
FilterSolver->AssembleDiffusionBilinear();
ParBilinearForm mass(&control_fes);
mass.AddDomainIntegrator(new InverseIntegrator(new MassIntegrator(one)));
@@ -412,7 +361,7 @@ int main(int argc, char *argv[])
// 11. Iterate:
for (int k = 1; k <= max_it; k++)
{
if (k > 1) { alpha *= ((real_t) k) / ((real_t) k-1); }
if (k > 1) { alpha = std::pow((real_t) k,growth); }
if (myid == 0)
{
@@ -452,7 +401,9 @@ int main(int argc, char *argv[])
// Step 5 - Update design variable ψ ← proj(ψ - αG)
psi.Add(-alpha, grad);
const real_t material_volume = proj(psi, target_volume);
ParGridFunction alpha_grad(grad);
alpha_grad *= alpha;
const real_t material_volume = proj(psi, alpha_grad, target_volume);
// Compute ||ρ - ρ_old|| in control fes.
real_t norm_increment = zerogf.ComputeL1Error(succ_diff_rho);
+5
View File
@@ -31,6 +31,9 @@ SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p \
ex22p ex24p ex25p ex26p ex34p ex35p
ifeq ($(MFEM_USE_ARPACK),YES)
SEQ_EXAMPLES += ex11 ex13
endif
ifeq ($(MFEM_USE_LAPACK),YES)
SEQ_EXAMPLES += ex38
endif
@@ -157,6 +160,8 @@ ex37-test-seq: ex37
@$(call mfem-test,$<,, Serial example,-mi 3)
ex37p-test-par: ex37p
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-mi 3)
ex39-test-seq: ex39
@$(call mfem-test,$<,, Serial example,-m ../data/compass.mesh)
ex41-test-seq: ex41
@$(call mfem-test,$<,, Serial example,-tf 1.0)
ex41p-test-par: ex41p
+3
View File
@@ -52,6 +52,9 @@ public:
/// Get the time for time dependent coefficients
real_t GetTime() { return time; }
/// Returns dimension of the vector.
int GetVDim() { return 1; }
/** @brief Evaluate the coefficient in the element described by @a T at the
point @a ip. */
/** @note When this method is called, the caller must make sure that the
+18 -5
View File
@@ -492,6 +492,8 @@ void VisItDataCollection::SaveRootFile()
to_padded_string(cycle, pad_digits_cycle) +
".mfem_root";
std::ofstream root_file(root_name);
MFEM_VERIFY(root_file.is_open(),
"Failed to open ofstream " << root_name);
root_file << GetVisItRootString();
if (!root_file)
{
@@ -977,7 +979,10 @@ void ParaViewDataCollection::Save()
// Save the local part of the mesh and grid functions fields to the local
// VTU file. Also save coefficient fields.
{
std::ofstream os(vtu_prefix + GenerateVTUFileName("proc", myid));
std::string os_str = vtu_prefix + GenerateVTUFileName("proc", myid);
std::ofstream os(os_str);
MFEM_VERIFY(os.is_open(),
"Failed to open ofstream " << os_str);
os.precision(precision);
SaveDataVTU(os, levels_of_detail);
}
@@ -989,7 +994,10 @@ void ParaViewDataCollection::Save()
"QuadratureFunction output is not supported for "
"ParaViewDataCollection on domain boundary!");
const std::string &field_name = qfield.first;
std::ofstream os(vtu_prefix + GenerateVTUFileName(field_name, myid));
std::string os_str = vtu_prefix + GenerateVTUFileName(field_name, myid);
std::ofstream os(os_str);
MFEM_VERIFY(os.is_open(),
"Failed to open ofstream " << os_str);
qfield.second->SaveVTU(os, pv_data_format, GetCompressionLevel(), field_name);
}
@@ -1000,7 +1008,10 @@ void ParaViewDataCollection::Save()
{
// Create the main PVTU file
{
std::ofstream pvtu_out(vtu_prefix + GeneratePVTUFileName("data"));
std::string os_str = vtu_prefix + GeneratePVTUFileName("data");
std::ofstream pvtu_out(os_str);
MFEM_VERIFY(pvtu_out.is_open(),
"Failed to open ofstream " << os_str);
WritePVTUHeader(pvtu_out);
// Grid function fields and coefficient fields
@@ -1055,8 +1066,10 @@ void ParaViewDataCollection::Save()
const std::string &q_field_name = q_field.first;
std::string q_fname = GeneratePVTUPath() + "/"
+ GeneratePVTUFileName(q_field_name);
std::ofstream pvtu_out(col_path + "/" + q_fname);
std::string os_str = col_path + "/" + q_fname;
std::ofstream pvtu_out(os_str);
MFEM_VERIFY(pvtu_out.is_open(),
"Failed to open ofstream " << os_str);
WritePVTUHeader(pvtu_out);
int vec_dim = q_field.second->GetVDim();
pvtu_out << "<PPointData>\n";
+6 -6
View File
@@ -320,8 +320,8 @@ public:
error estimation procedure where the flux averaging is replaced by a global
L2 projection (requiring a mass matrix solve).
The required BilinearFormIntegrator must implement the methods
ComputeElementFlux() and ComputeFluxEnergy().
The required BilinearFormIntegrator must implement the method
ComputeElementFlux().
Implemented for the parallel case only.
*/
@@ -357,8 +357,8 @@ protected:
public:
/** @brief Construct a new L2ZienkiewiczZhuEstimator object.
@param integ This BilinearFormIntegrator must implement the methods
ComputeElementFlux() and ComputeFluxEnergy().
@param integ This BilinearFormIntegrator must implement the method
ComputeElementFlux().
@param sol The solution field whose error is to be estimated.
@param flux_fes The L2ZienkiewiczZhuEstimator assumes ownership of this
FiniteElementSpace and will call its Update() method when
@@ -382,8 +382,8 @@ public:
{ }
/** @brief Construct a new L2ZienkiewiczZhuEstimator object.
@param integ This BilinearFormIntegrator must implement the methods
ComputeElementFlux() and ComputeFluxEnergy().
@param integ This BilinearFormIntegrator must implement the method
ComputeElementFlux().
@param sol The solution field whose error is to be estimated.
@param flux_fes The L2ZienkiewiczZhuEstimator does NOT assume ownership
of this FiniteElementSpace; will call its Update() method
+11 -11
View File
@@ -3030,10 +3030,14 @@ void GridFunction::ProjectCoefficient(Coefficient *coeff[])
}
}
void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
Array<int> &dof_attr)
void GridFunction::ProjectDiscCoefficient(
std::variant<Coefficient*, VectorCoefficient*> coeff, Array<int> &dof_attr)
{
MFEM_VERIFY(VectorDim() == coeff.GetVDim(), "coeff vdim != VectorDim()");
std::visit([&](auto* c)
{
MFEM_VERIFY(VectorDim() == c->GetVDim(), "coeff vdim != VectorDim()");
}, coeff);
Array<int> vdofs;
Vector vals;
@@ -3047,7 +3051,10 @@ void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
{
fes->GetElementVDofs(i, vdofs);
vals.SetSize(vdofs.Size());
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
std::visit([&](auto* c)
{
fes->GetFE(i)->Project(*c, *fes->GetElementTransformation(i), vals);
}, coeff);
// the values in shared dofs are determined from the element with maximal
// attribute
@@ -3063,13 +3070,6 @@ void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
}
}
void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff)
{
MFEM_VERIFY(VectorDim() == coeff.GetVDim(), "coeff vdim != VectorDim()");
Array<int> dof_attr;
ProjectDiscCoefficient(coeff, dof_attr);
}
void GridFunction::ProjectDiscCoefficient(Coefficient &coeff, AvgType type)
{
// Harmonic (x1 ... xn) = [ (1/x1 + ... + 1/xn) / n ]^-1.
+21 -5
View File
@@ -23,6 +23,7 @@
#include <limits>
#include <ostream>
#include <string>
#include <variant>
namespace mfem
{
@@ -79,10 +80,18 @@ protected:
bool wcoef,
int subdomain);
/** Project a discontinuous vector coefficient in a continuous space and
return in dof_attr the maximal attribute of the elements containing each
degree of freedom. */
void ProjectDiscCoefficient(VectorCoefficient &coeff, Array<int> &dof_attr);
/** @brief Project a discontinuous (vector) coefficient as a grid function on
a continuous finite element space. Return in dof_attr the maximal
attribute of the elements containing each degree of freedom. */
virtual void ProjectDiscCoefficient(
std::variant<Coefficient*, VectorCoefficient*> coeff, Array<int> &dof_attr);
/** @brief Project a discontinuous (vector) coefficient as a grid function on
a continuous finite element space. The values in shared dofs are
determined from the element with maximal attribute. */
virtual void ProjectDiscCoefficient(
std::variant<Coefficient*, VectorCoefficient*> coeff)
{ Array<int> dof_attr; ProjectDiscCoefficient(coeff, dof_attr); };
/** Helper function for ProjectCoefficientElementL2 */
void ProjectCoefficientElementL2_(Coefficient &coeff, Vector &sol, Vector &Va);
@@ -515,10 +524,17 @@ public:
but using an array of scalar coefficients for each component. */
void ProjectCoefficient(Coefficient *coeff[]);
/** @brief Project a discontinuous coefficient as a grid function on
a continuous finite element space. The values in shared dofs are
determined from the element with maximal attribute. */
virtual void ProjectDiscCoefficient(Coefficient &coeff)
{ ProjectDiscCoefficient(&coeff); }
/** @brief Project a discontinuous vector coefficient as a grid function on
a continuous finite element space. The values in shared dofs are
determined from the element with maximal attribute. */
virtual void ProjectDiscCoefficient(VectorCoefficient &coeff);
virtual void ProjectDiscCoefficient(VectorCoefficient &coeff)
{ ProjectDiscCoefficient(&coeff); }
enum AvgType {ARITHMETIC, HARMONIC};
/** @brief Projects a discontinuous coefficient so that the values in shared
+6 -5
View File
@@ -490,7 +490,7 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
}
DEV.find_device = true;
const int id = gsl_comm->id, np = gsl_comm->np;
const unsigned int id = gsl_comm->id, np = gsl_comm->np;
gsl_mfem_ref.SetSize(points_cnt * dim);
gsl_mfem_elem.SetSize(points_cnt);
@@ -652,7 +652,7 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
{
const int pp = hash_offset[i];
/* don't send back to where it just came from */
if (pp == p->proc)
if (static_cast<unsigned>(pp) == p->proc)
{
continue;
}
@@ -1068,7 +1068,7 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
sarray_transfer(struct evalOutPt_t, &outpt, proc, 1, cr);
opt = (evalOutPt_t *)outpt.ptr;
for (int index = 0; index < outpt.n; index++)
for (size_t index = 0; index < outpt.n; index++)
{
int idx = ordering == Ordering::byNODES ?
opt->index + i*points_cnt :
@@ -1413,7 +1413,7 @@ void FindPointsGSLIB::SetupSplitMeshesAndIntegrationRules(const int order)
{
MFEM_VERIFY(mesh, "Setup FindPointsGSLIB with mesh first.");
const int dof1D = order+1;
const int dim = mesh->Dimension();
dim = mesh->Dimension();
SetupSplitMeshes();
if (dim == 2)
@@ -2254,7 +2254,8 @@ void FindPointsGSLIB::DistributeInterpolatedValues(const Vector &int_vals,
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
// Store received data
MFEM_VERIFY(outpt->n == points_cnt, "Incompatible size. Number of points "
MFEM_VERIFY(outpt->n == static_cast<size_t>(points_cnt),
"Incompatible size. Number of points "
"received does not match the number of points originally "
"found using FindPoints.");
+6
View File
@@ -202,13 +202,19 @@ protected:
const int dof1dsol, const int ordering);
public:
/// Serial constructor
FindPointsGSLIB();
/// Serial constructor + setup with given Mesh (see \ref Setup)
FindPointsGSLIB(Mesh &mesh_in, const double bb_t = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
#ifdef MFEM_USE_MPI
/// Constructor for ParMesh
FindPointsGSLIB(MPI_Comm comm_);
/// Constructor + setup with given ParMesh (see \ref Setup)
FindPointsGSLIB(ParMesh &mesh_in, const double bb_t = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
+1 -1
View File
@@ -254,7 +254,7 @@ get_edge(const double *elx[2], const double *wtend, int ei,
edge.dxdn[d] = workspace + (2 + d) * pN; //dxdn and dydn at DOFs along edge
}
if (side_init != (1u << ei))
if (static_cast<unsigned>(side_init) != (1u << ei))
{
#define ELX(d, j, k) elx[d][j + k * pN] // assumes lexicographic ordering
for (int d = 0; d < 2; ++d)
+2 -2
View File
@@ -294,7 +294,7 @@ get_face(const double *elx[3], const double *wtend, int fi, double *workspace,
face.dxdn[d] = workspace+(3+d)*p_Nfr;
}
if (side_init != (1u << fi))
if (static_cast<unsigned>(side_init) != (1u << fi))
{
const int e_stride[3] = {1, pN, pN*pN};
#define ELX(d, j, k, l) elx[d][j*e_stride[d1]+k*e_stride[d2]+l*e_stride[dn]]
@@ -342,7 +342,7 @@ get_edge(const double *elx[3], const double *wtend, int ei, double *workspace,
if (jidx >= 3*pN) { return edge; }
if (side_init != (64u << ei))
if (static_cast<unsigned>(side_init) != (64u << ei))
{
const int e_stride[3] = {1, pN, pN*pN};
#define ELX(d, j, k, l) elx[d][j*e_stride[de]+k*e_stride[dn1]+l*e_stride[dn2]]
+33 -37
View File
@@ -43,56 +43,52 @@ public:
index = i;
}
void Set3w(const real_t x1, const real_t x2, const real_t x3, const real_t w)
{ x = x1; y = x2; z = x3; weight = w; }
void Set2w(const real_t x1, const real_t x2, const real_t w)
{ x = x1; y = x2; weight = w; }
void Set1w(const real_t x1, const real_t w)
{ x = x1; weight = w; }
void Set3w(const real_t *p) { Set3w(p[0], p[1], p[2], p[3]); }
void Set2w(const real_t *p) { Set2w(p[0], p[1], p[2]); }
void Set1w(const real_t *p) { Set1w(p[0], p[1]); }
void Set3(const real_t x1, const real_t x2, const real_t x3)
{ x = x1; y = x2; z = x3; }
void Set2(const real_t x1, const real_t x2)
{ x = x1; y = x2; }
void Set1(const real_t x1)
{ x = x1; }
void Set3(const real_t *p) { Set3(p[0], p[1], p[2]); }
void Set2(const real_t *p) { Set2(p[0], p[1]); }
void Set1(const real_t *p) { Set1(p[0]); }
void Set(const real_t x1, const real_t x2, const real_t x3, const real_t w)
{ Set3w(x1, x2, x3, w); }
void Set(const real_t *p, const int dim)
{
MFEM_ASSERT(1 <= dim && dim <= 3, "invalid dim: " << dim);
x = p[0];
if (dim > 1)
switch (dim)
{
y = p[1];
if (dim > 2)
{
z = p[2];
}
case 3: Set3(p); break;
case 2: Set2(p); break;
case 1: Set1(p); break;
}
}
void Get(real_t *p, const int dim) const
{
MFEM_ASSERT(1 <= dim && dim <= 3, "invalid dim: " << dim);
p[0] = x;
if (dim > 1)
switch (dim)
{
p[1] = y;
if (dim > 2)
{
p[2] = z;
}
case 3: p[2] = z;
case 2: p[1] = y;
case 1: p[0] = x;
}
}
void Set(const real_t x1, const real_t x2, const real_t x3, const real_t w)
{ x = x1; y = x2; z = x3; weight = w; }
void Set3w(const real_t *p) { x = p[0]; y = p[1]; z = p[2]; weight = p[3]; }
void Set3(const real_t x1, const real_t x2, const real_t x3)
{ x = x1; y = x2; z = x3; }
void Set3(const real_t *p) { x = p[0]; y = p[1]; z = p[2]; }
void Set2w(const real_t x1, const real_t x2, const real_t w)
{ x = x1; y = x2; weight = w; }
void Set2w(const real_t *p) { x = p[0]; y = p[1]; weight = p[2]; }
void Set2(const real_t x1, const real_t x2) { x = x1; y = x2; }
void Set2(const real_t *p) { x = p[0]; y = p[1]; }
void Set1w(const real_t x1, const real_t w) { x = x1; weight = w; }
void Set1w(const real_t *p) { x = p[0]; weight = p[1]; }
};
/// Class for an integration rule - an Array of IntegrationPoint.
+2 -2
View File
@@ -164,8 +164,8 @@ private:
public:
/// Constructs the domain integrator $ (Q, \nabla v) $
DomainLFGradIntegrator(VectorCoefficient &QF)
: DeltaLFIntegrator(QF), Q(QF) { }
DomainLFGradIntegrator(VectorCoefficient &QF, const IntegrationRule *ir = NULL)
: DeltaLFIntegrator(QF, ir), Q(QF) { }
bool SupportsDevice() const override { return true; }
+2 -2
View File
@@ -717,9 +717,9 @@ void ParGridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
}
void ParGridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff)
void ParGridFunction::ProjectDiscCoefficient(
std::variant<Coefficient*, VectorCoefficient*> coeff)
{
MFEM_VERIFY(VectorDim() == coeff.GetVDim(), "coeff vdim != VectorDim()");
// local maximal element attribute for each dof
Array<int> ldof_attr;
+6 -5
View File
@@ -63,6 +63,12 @@ protected:
void ProjectBdrCoefficient(Coefficient *coeff[], VectorCoefficient *vcoeff,
const Array<int> &attr);
/** @brief Project a discontinuous (vector) coefficient as a grid function on
a continuous finite element space. The values in shared dofs are
determined from the element with maximal attribute. */
virtual void ProjectDiscCoefficient(
std::variant<Coefficient*, VectorCoefficient*> coeff) override;
public:
ParGridFunction() { pfes = NULL; }
@@ -268,11 +274,6 @@ public:
ProjectType type = ProjectType::DEFAULT) override;
using GridFunction::ProjectDiscCoefficient;
/** @brief Project a discontinuous vector coefficient as a grid function on
a continuous finite element space. The values in shared dofs are
determined from the element with maximal attribute. */
void ProjectDiscCoefficient(VectorCoefficient &coeff) override;
void ProjectDiscCoefficient(Coefficient &coeff, AvgType type) override;
void ProjectDiscCoefficient(VectorCoefficient &vcoeff, AvgType type) override;
+25 -27
View File
@@ -14,6 +14,7 @@
#include "../config/config.hpp"
#include "array.hpp"
#include "text.hpp"
#include <iostream>
#include <map>
@@ -247,7 +248,8 @@ inline void ArraysByName<T>::Print(std::ostream &os, int width) const
os << data.size() << '\n';
for (auto const &it : data)
{
os << '"' << it.first << '"' << '\n' << it.second.Size() << '\n';
// Note: The method Load() can read any string formatted with std::quoted.
os << std::quoted(it.first) << '\n' << it.second.Size() << '\n';
it.second.Print(os, width > 0 ? width : it.second.Size());
}
}
@@ -258,40 +260,36 @@ void ArraysByName<T>::Load(std::istream &in)
int NumArrays;
in >> NumArrays;
std::string ArrayLine, ArrayName;
for (int i=0; i < NumArrays; i++)
for (int i = 0; i < NumArrays; i++)
{
in >> std::ws;
getline(in, ArrayLine);
std::size_t q0 = ArrayLine.find('"');
std::size_t q1 = ArrayLine.rfind('"');
if (q0 != std::string::npos && q1 > q0)
// Read the name:
// - If the stream 'in' starts with " then parse it with the function
// parse_quoted_string() from text.hpp. In this case, the name can be
// empty. Note: this case allows for reading any string formatted using
// std::quoted, e.g. as in the method Print().
// - If the name does not start with " then the name ends with the first
// white space character (and the white space character is not included
// in the name). Since white space characters are skipped before reading
// the name, there will be at least one non-white-space character in the
// name in this case.
std::string ArrayName;
if (in.peek() == '"')
{
// Locate set name between first and last double quote
ArrayName = ArrayLine.substr(q0+1,q1-q0-1);
if (parse_quoted_string(ArrayName, in) != 0)
{
MFEM_ABORT("error parsing input!");
}
}
else
{
// If no double quotes found locate set name using white space
q1 = ArrayLine.find(' ');
ArrayName = ArrayLine.substr(0,q1-1);
}
if (q1+2 < ArrayLine.size())
{
// Read the remainder of the line which contains the array data
std::istringstream ArrayDataStream(ArrayLine.substr(q1+2,
ArrayLine.size()));
data[ArrayName].Load(ArrayDataStream, 0);
}
else
{
// Read the array data starting on the next line
data[ArrayName].Load(in, 0);
in >> ArrayName;
MFEM_VERIFY(in.good(), "error parsing input!");
}
// Read the array
data[ArrayName].Load(in);
}
}
}
+42
View File
@@ -50,6 +50,48 @@ inline void filter_dos(std::string &line)
}
}
/** @brief Read a string formatted using std::quoted. Return nonzero on error.
The stream @a in must begin with @a delim. After clearing @a result and
extracting the opening @a delim, characters are extracted from @a in and
processed as follows:
- if the character is @a delim, return 0;
- if the character is different from @a escape, it is appended to @a result;
- if the character is @a escape, the next character from @a in is extracted
and if it is one of @a delim or @a escape, it is appended to @a result;
otherwise, both @a escape and the character after it are appended to
@a result; note that the latter case is not possible if the input was
formatted with std::quoted with the same @a delim and @a escape
characters.
If the stream @a in does not begin with @a delim, error code 1 is returned.
If reading the stream fails, error code 2 is returned. On success, zero is
returned and the closing @a delim character is the last character extracted
from @a in. */
inline int parse_quoted_string(std::string &result, std::istream &in,
char delim = '"', char escape = '\\')
{
using tt = std::string::traits_type; // std::char_traits<char>
auto equal = [](tt::int_type c1, tt::char_type c2) -> bool
{
return tt::eq_int_type(c1, tt::to_int_type(c2));
};
result.clear();
if (!equal(in.peek(), delim)) { return 1; }
in.get(); // extract delim
for (auto c = in.get(); !equal(c, delim); c = in.get())
{
if (equal(c, escape))
{
c = in.get();
if (!equal(c, escape) && !equal(c, delim)) { result += escape; }
}
if (!in) { return 2; }
result += tt::to_char_type(c);
}
return 0;
}
/// Convert an integer to a 0-padded string with the given number of @a digits
inline std::string to_padded_string(int i, int digits)
{
+7
View File
@@ -23,6 +23,7 @@ list(APPEND SRCS
complex_operator.cpp
constraints.cpp
densemat.cpp
eigensolvers.cpp
filteredsolver.cpp
handle.cpp
matrix.cpp
@@ -55,6 +56,7 @@ list(APPEND HDRS
dinvariants.hpp
dtensor.hpp
dual.hpp
eigensolvers.hpp
filteredsolver.hpp
handle.hpp
invariants.hpp
@@ -101,6 +103,11 @@ if (MFEM_USE_MPI)
endif()
endif()
if (MFEM_USE_ARPACK)
list(APPEND SRCS arpack.cpp)
list(APPEND HDRS arpack.hpp)
endif()
if (MFEM_USE_SUNDIALS)
list(APPEND SRCS sundials.cpp)
list(APPEND HDRS sundials.hpp)
+1122
View File
File diff suppressed because it is too large Load Diff
+271
View File
@@ -0,0 +1,271 @@
// 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_ARPACK
#define MFEM_ARPACK
#include "../config/config.hpp"
#ifdef MFEM_USE_ARPACK
#include <string>
#ifdef MFEM_USE_MPI
#include <mpi.h>
#include "hypre.hpp"
#endif
#include "operator.hpp"
#define SSAUPD ssaupd_
#define SSEUPD sseupd_
#define DSAUPD dsaupd_
#define DSEUPD dseupd_
#ifdef MFEM_USE_MPI
#define PSSAUPD pssaupd_
#define PSSEUPD psseupd_
#define PDSAUPD pdsaupd_
#define PDSEUPD pdseupd_
#endif
extern "C" void SSAUPD(int *ido, char *bmat, int *n,
char *which, int *nev, float *tol, float *resid,
int *ncv, float *v, int *ldv,
int *iparam, int *ipntr,
float *workd, float *workl, int *lworkl, int *info);
extern "C" void SSEUPD(int *, char *, int *, float *,
float *, int *, float *, char *, int *, char *,
int *, float *, float *, int *, float *,
int *, int *, int *, float *,
float *, int *, int *);
extern "C" void DSAUPD(int *ido, char *bmat, int *n,
char *which, int *nev, double *tol, double *resid,
int *ncv, double *v, int *ldv,
int *iparam, int *ipntr,
double *workd, double *workl, int *lworkl, int *info);
extern "C" void DSEUPD(int *, char *, int *, double *,
double *, int *, double *, char *, int *, char *,
int *, double *, double *, int *, double *,
int *, int *, int *, double *,
double *, int *, int *);
#ifdef MFEM_USE_MPI
extern "C" void PSSAUPD(int *comm, int *ido, char *bmat, int *n,
char *which, int *nev, float *tol, float *resid,
int *ncv, float *v, int *ldv,
int *iparam, int *ipntr,
float *workd, float *workl, int *lworkl, int *info);
extern "C" void PSSEUPD(int *comm, int *, char *, int *, float *,
float *, int *, float *, char *, int *, char *,
int *, float *, float *, int *, float *,
int *, int *, int *, float *,
float *, int *, int *);
extern "C" void PDSAUPD(int *comm, int *ido, char *bmat, int *n,
char *which, int *nev, double *tol, double *resid,
int *ncv, double *v, int *ldv,
int *iparam, int *ipntr,
double *workd, double *workl, int *lworkl, int *info);
extern "C" void PDSEUPD(int *comm, int *, char *, int *, double *,
double *, int *, double *, char *, int *, char *,
int *, double *, double *, int *, double *,
int *, int *, int *, double *,
double *, int *, int *);
#endif
extern "C" {
void arpackgetcommdbg_(int *,int *,int *);
void arpacksetcommdbg_(int *,int *,int *);
void arpacksymdbg_(int *,int *,int *,int *,int *,int *,int *);
void arpacknonsymdbg_(int *,int *,int *,int *,int *,int *,int *);
void arpackcmplxdbg_(int *,int *,int *,int *,int *,int *,int *);
}
namespace mfem
{
/// Wrapper for the ARPACK routine SSAUPD or DSAUPD
class ArPackSAUPD : public SymEigensolver, public SymGenEigensolver
{
public:
ArPackSAUPD();
virtual ~ArPackSAUPD();
/** ARPACK modes are described in section 3.5 of the ARPACK manual.
Mode 1: regular mode to solve A x = lambda x
No solver and no mass matrix are needed.
Mode 2: regular inverse mode to solve A x = lambda M x
Both A and M are needed and the solver should compute M^{-1}.
Mode 3: shift-invert mode to solve either A x = lambda x
or A x = lambda M x
Mass matrix is optional. The solver should compute
(A-sigma I)^{-1} or (A-sigma M)^{-1}. The shift parameter,
sigma, also needs to be set with SetShift().
Mode 4: Buckling mode to solve K x = lambda K_G x
K is set using SetMassMatrix(), K_G is set using SetOperator(),
and the solver should compute (K-sigma K_G)^{-1}. The shift
parameter, sigma, also needs to be set with SetShift().
Mode 5: Cayley mode to solve A x = lambda M x
Both A and M are needed and the solver should compute
(A - sigma M)^{-1}. The shift parameter, sigma, also needs
to be set with SetShift().
*/
void SetMode(int mode);
inline void SetTol(real_t tol) override { tol_ = tol; }
inline void SetMaxIter(int max_iter) override { max_iter_ = max_iter; }
inline void SetPrintLevel(int logging) override { logging_ = logging; }
inline void SetShift(real_t sigma) { sigma_ = sigma; }
inline void SetNumModes(int num_eigs) override { nev_ = num_eigs; }
virtual void SetSolver(Solver & solver);
virtual void SetOperator(const Operator & A) override;
virtual void SetMassMatrix(const Operator & M);
virtual void SetOperators(const Operator & A, const Operator & B) override
{ SetOperator(A); SetMassMatrix(B); }
void Solve() override;
virtual int GetNumConverged() const override { return iparam_[4]; }
/// Collect the converged eigenvalues
virtual void GetEigenvalues(Array<real_t> & eigenvalues) const override;
/// Extract a single eigenvector
virtual const Vector & GetEigenvector(unsigned int i) const override;
/// Transfer ownership of the converged eigenvectors
Vector ** StealEigenvectors() override;
protected:
int myid_; // Index of this processor
int max_iter_;
int logging_;
// The following variables are for ARPACK
int nloc_; // number of items stored locally
int nev_; // number of requested eigenvalues
int ncv_; // number of ritz vectors
int rvec_; // boolean to return eigenvectors as well
int mode_; // 1 = standard, 2 = generalized, 3 = shift invert,
// 4 = buckling, 5 = Cayley
int lworkl_; // length of lworkl_ work array
int iparam_[12]; // arpack parameters
int ipntr_[12]; // arpack pointers
char bmat_; // I for standard problem, G for generalized
char which_[3]; // spectrum portion: LA, SA, LM, SM, BE
char hwmny_; // DSEUPD: A for all eigenvalues, S for some
real_t tol_; // relative accuracy bound for Ritz values
real_t sigma_; // eigenvalue shift parameter
int * select_;// workspace used during eigenvalue computation
real_t * dv_; // Ritz values
real_t * v_; // ncv Lanczos basis vectors
real_t * resid_; // residual vector
real_t * workd_; // work array for 3 vectors used in Arnoldi iteration
real_t * workl_; // work array
// Operators and Vectors needed outside of ARPACK
Solver * solver_;
const Operator * A_;
const Operator * B_;
Vector * w_;
Vector * x_;
Vector * y_;
Vector * z_;
mutable Vector ** eigenvectors_;
std::string solverName_;
void reverseComm();
int reverseCommMode1();
int reverseCommMode2();
int reverseCommMode3();
int reverseCommMode4();
int reverseCommMode5();
virtual void prepareEigenvectors() const;
void printErrors(const int & info, const int iparam[],
const char & bmat, const int & n,
const char which[],
const int & nev, const int & ncv,
const int & lworkl );
private:
virtual int computeNlocf() { return nloc_; }
virtual int computeIter(int & ido);
virtual int computeEigs();
};
#ifdef MFEM_USE_MPI
class ArPackPSAUPD : public ArPackSAUPD
{
public:
ArPackPSAUPD(MPI_Comm comm);
virtual ~ArPackPSAUPD() {}
void SetOperator(const Operator & A);
void SetMassMatrix(const Operator & M);
/// Collect the converged eigenvalues
void GetEigenvalues(Array<real_t> & eigenvalues) const;
/// Extract a single eigenvector
const Vector & GetEigenvector(unsigned int i) const;
/// Transfer ownership of the converged eigenvectors
// HypreParVector ** StealEigenvectors();
Vector ** StealEigenvectors();
protected:
void prepareEigenvectors() const;
private:
MPI_Comm comm_;
MPI_Fint commf_; // Fortran style MPI communicator
int numProcs_; // Number of processors
mutable HYPRE_Int * part_; // parallel partitioning for eigenvectors
int computeNlocf();
int computeIter(int & ido);
int computeEigs();
};
#endif // MFEM_USE_MPI
};
#endif // MFEM_USE_ARPACK
#endif // MFEM_ARPACK
+20
View File
@@ -0,0 +1,20 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "linalg.hpp"
#include "eigensolvers.hpp"
using namespace std;
namespace mfem
{
};
+396
View File
@@ -0,0 +1,396 @@
// 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_EIGENSOLVERS
#define MFEM_EIGENSOLVERS
#include "vector.hpp"
namespace mfem
{
/// Abstract Eigenequation
/// Defines the operator of the linear eigenvalue equation
/// A x_i = lambda_i x_i
/// Where A is a real-valued operator, the lambda_i are the eigenvalues,
/// and x_i are the eigenvectors.
class Eigenequation
{
protected:
Eigenequation() = default;
public:
virtual ~Eigenequation() = default;
/// @brief Set the operator A of the eigenvalue equation
virtual void SetOperator(const Operator & A) = 0;
};
/// Abstract Complex-valued Eigenequation
/// Defines the operator of the linear eigenvalue equation
/// A x_i = lambda_i x_i
/// Where A is a complex-valued operator, the lambda_i are the eigenvalues,
/// and x_i are the eigenvectors.
class ComplexEigenequation
{
protected:
ComplexEigenequation() = default;
public:
virtual ~ComplexEigenequation() = default;
/// @brief Set the real and imaginary parts of the operator A
virtual void SetOperator(const Operator & Ar, const Operator & Ai) = 0;
};
/// Abstract Generalized Eigenequation
/// Defines the operator of the linear eigenvalue equation
/// A x_i = lambda_i B x_i
/// Where A and B are real-valued operators, the lambda_i are the eigenvalues,
/// and x_i are the eigenvectors.
class GenEigenequation
{
protected:
GenEigenequation() = default;
public:
virtual ~GenEigenequation() = default;
/// @brief Set the operators A and B of the generalized eigenvalue equation
virtual void SetOperators(const Operator & A, const Operator & B) = 0;
};
/// Abstract Complex-valued Generalized Eigenequation
/// Defines the operator of the linear eigenvalue equation
/// A x_i = lambda_i B x_i
/// Where A and B are complex-valued operators, the lambda_i are the
/// eigenvalues, and x_i are the eigenvectors.
class ComplexGenEigenequation
{
protected:
ComplexGenEigenequation() = default;
public:
virtual ~ComplexGenEigenequation() = default;
/// @brief Set the real and imaginary parts of the operators A and B
virtual void SetOperators(const Operator & Ar, const Operator & Ai,
const Operator & Br, const Operator & Bi) = 0;
};
/// Abstract Eigensolver
/// Computes eigenvalue/eigenvector pairs for the linear system
/// A x_i = lambda_i x_i
/// Where the lambda_i are the eigenvalues and x_i are the eigenvectors.
class EigensolverBase
{
protected:
EigensolverBase() = default;
public:
virtual ~EigensolverBase() = default;
/// @brief Stopping criteria based on numerical tolerance
///
/// @note This may be defined differently by different solvers.
virtual void SetTol(real_t tol) = 0;
/// @brief Stopping criteria based on number of iterations required to
/// reach convergence.
///
/// @note This may also be defined differently in different solvers.
virtual void SetMaxIter(int max_iter) = 0;
/// @brief Controls the type and amount of information printed to
/// standard output.
virtual void SetPrintLevel(int logging) = 0;
/// @brief Set the number of desired eigenmodes to compute
virtual void SetNumModes(int num_eigs) = 0;
/// @brief Get the number of converged eigenmodes
virtual int GetNumConverged() const = 0;
/// @brief Perform the eigenvalue solve
virtual void Solve() = 0;
};
/// Symmetric Eigensolver
/// If A^T = A the linear system must have real-valued eigenvalues
/// and eigenvectors.
class SymEigensolver : public EigensolverBase, public Eigenequation
{
protected:
SymEigensolver() = default;
public:
virtual ~SymEigensolver() = default;
/// @brief Collect the converged eigenvalues
///
/// The length of the array should equal the number of converged eigenvalues.
virtual void GetEigenvalues(Array<real_t> & eigenvalues) const = 0;
/// @brief Extract a single eigenvector
///
/// The index i should be in the range [0, numConverged). The
virtual const Vector & GetEigenvector(unsigned int i) const = 0;
/// @brief Transfer ownership of the converged eigenvectors
///
/// The array should contain numConverged vectors.
virtual Vector ** StealEigenvectors() = 0;
};
/// Symmetric Generalized Eigensolver
/// If A^T = A and M^T = M the linear system must have real-valued eigenvalues
/// and eigenvectors.
class SymGenEigensolver : public EigensolverBase, public GenEigenequation
{
protected:
SymGenEigensolver() = default;
public:
virtual ~SymGenEigensolver() = default;
/// @brief Collect the converged eigenvalues
///
/// The length of the array should equal the number of converged eigenvalues.
virtual void GetEigenvalues(Array<real_t> & eigenvalues) const = 0;
/// @brief Extract a single eigenvector
///
/// The index i should be in the range [0, numConverged). The
virtual const Vector & GetEigenvector(unsigned int i) const = 0;
/// @brief Transfer ownership of the converged eigenvectors
///
/// The array should contain numConverged vectors.
virtual Vector ** StealEigenvectors() = 0;
};
/// Hermetian Eigensolver
/// If A^H = A the linear system must have real-valued eigenvalues
/// but may have complex-valued eigenvectors.
class HermEigensolver : public EigensolverBase, public ComplexEigenequation
{
protected:
HermEigensolver() = default;
public:
virtual ~HermEigensolver() = default;
/// @brief Collect the converged eigenvalues
///
/// The length of the array should equal the number of converged eigenvalues.
virtual void GetEigenvalues(Array<real_t> & eigenvalues) const = 0;
/// @brief Extract a single eigenvector
///
/// The index i should be in the range [0, 2*numConverged). The
/// vectors corresponding to even indices are the real parts of the
/// converged eigenvectors and the odd indices correspond to the
/// imaginary parts.
virtual const Vector & GetEigenvector(unsigned int i) const = 0;
/// @brief Transfer ownership of the converged eigenvectors
///
/// The array should contain 2*numConverged vectors with the even
/// indices corresponding to the real parts of the converged
/// eigenvectors and the odd indices corresponding to the imaginary
/// parts.
virtual Vector ** StealEigenvectors() = 0;
};
/// Hermetian Generalized Eigensolver
/// If A^H = A and M^H = M the linear system must have real-valued eigenvalues
/// but may have complex-valued eigenvectors.
class HermGenEigensolver :
public EigensolverBase, public ComplexGenEigenequation
{
protected:
HermGenEigensolver() = default;
public:
virtual ~HermGenEigensolver() = default;
/// @brief Collect the converged eigenvalues
///
/// The length of the array should equal the number of converged eigenvalues.
virtual void GetEigenvalues(Array<real_t> & eigenvalues) const = 0;
/// @brief Extract a single eigenvector
///
/// The index i should be in the range [0, 2*numConverged). The
/// vectors corresponding to even indices are the real parts of the
/// converged eigenvectors and the odd indices correspond to the
/// imaginary parts.
virtual const Vector & GetEigenvector(unsigned int i) const = 0;
/// @brief Transfer ownership of the converged eigenvectors
///
/// The array should contain 2*numConverged vectors with the even
/// indices corresponding to the real parts of the converged
/// eigenvectors and the odd indices corresponding to the imaginary
/// parts.
virtual Vector ** StealEigenvectors() = 0;
};
/// Non-Symmetric Eigensolver
/// For general real-valued operators A the linear system must have
/// eigenvalues and eigenvectors which form complex conjugate pairs.
class NonSymEigensolver : public EigensolverBase, public Eigenequation
{
protected:
NonSymEigensolver() = default;
public:
virtual ~NonSymEigensolver() = default;
/// @brief Collect the converged eigenvalues
///
/// The length of the array should be the number of converged
/// eigenvalues. The complex-valued eigenvalues can be constructed
/// as: lambda_{2*j} = eig[2*j]+i*eig[2*j+1] and
/// lambda_{2*j+1} = eig[2*j]-i*eig[2*j+1]
/// With j in the range [0, numConverged/2)
virtual void GetEigenvalues(Array<real_t> & eig) const = 0;
/// @brief Extract a single eigenvector
///
/// The index i should be in the range [0, numConverged). The
/// vectors corresponding to even indices are the real parts of the
/// converged eigenvectors and the odd indices correspond to the
/// imaginary parts. If needed, the complex conjugate pairs of
/// eigenvectors can be constructed in the same manner described
/// for the eigenvalues.
virtual const Vector & GetEigenvector(unsigned int i) const = 0;
/// @brief Transfer ownership of the converged eigenvectors
///
/// The array should contain numConverged vectors with the even
/// indices corresponding to the real parts of the converged
/// eigenvectors and the odd indices corresponding to the imaginary
/// parts.
virtual Vector ** StealEigenvectors() = 0;
};
/// Non-Symmetric Eigensolver
/// For general real-valued operators A and M the linear system must have
/// eigenvalues and eigenvectors which form complex conjugate pairs.
class NonSymGenEigensolver : public EigensolverBase, public GenEigenequation
{
protected:
NonSymGenEigensolver() = default;
public:
virtual ~NonSymGenEigensolver() = default;
/// @brief Collect the converged eigenvalues
///
/// The length of the array should be the number of converged
/// eigenvalues. The complex-valued eigenvalues can be constructed
/// as: lambda_{2*j} = eig[2*j]+i*eig[2*j+1] and
/// lambda_{2*j+1} = eig[2*j]-i*eig[2*j+1]
/// With j in the range [0, numConverged/2)
virtual void GetEigenvalues(Array<real_t> & eig) const = 0;
/// @brief Extract a single eigenvector
///
/// The index i should be in the range [0, numConverged). The
/// vectors corresponding to even indices are the real parts of the
/// converged eigenvectors and the odd indices correspond to the
/// imaginary parts. If needed, the complex conjugate pairs of
/// eigenvectors can be constructed in the same manner described
/// for the eigenvalues.
virtual const Vector & GetEigenvector(unsigned int i) const = 0;
/// @brief Transfer ownership of the converged eigenvectors
///
/// The array should contain numConverged vectors with the even
/// indices corresponding to the real parts of the converged
/// eigenvectors and the odd indices corresponding to the imaginary
/// parts.
virtual Vector ** StealEigenvectors() = 0;
};
/// Complex Eigensolver
/// Can have arbitrary complex-valued eigenvalues and eigenvectors
class ComplexEigensolver : public EigensolverBase, public ComplexEigenequation
{
protected:
ComplexEigensolver() = default;
public:
virtual ~ComplexEigensolver() = default;
/// @brief Collect the converged eigenvalues
///
/// The length of the array should be twice the number of converged
/// eigenvalues. The complex-valued eigenvalues can be constructed
/// as: lambda_j = eig[2*j]+i*eig[2*j+1]
virtual void GetEigenvalues(Array<real_t> & eig) const = 0;
/// @brief Extract a single eigenvector
///
/// The index i should be in the range [0, 2*numConverged). The
/// vectors corresponding to even indices are the real parts of the
/// converged eigenvectors and the odd indices correspond to the
/// imaginary parts.
virtual const Vector & GetEigenvector(unsigned int i) const = 0;
/// @brief Transfer ownership of the converged eigenvectors
///
/// The array should contain 2*numConverged vectors with the even
/// indices corresponding to the real parts of the converged
/// eigenvectors and the odd indices corresponding to the imaginary
/// parts.
virtual Vector ** StealEigenvectors() = 0;
};
/// Complex Generalized Eigensolver
/// Can have arbitrary complex-valued eigenvalues and eigenvectors
class ComplexGenEigensolver :
public EigensolverBase, public ComplexGenEigenequation
{
protected:
ComplexGenEigensolver() = default;
public:
virtual ~ComplexGenEigensolver() = default;
/// @brief Collect the converged eigenvalues
///
/// The length of the array should be twice the number of converged
/// eigenvalues. The complex-valued eigenvalues can be constructed
/// as: lambda_j = eig[2*j]+i*eig[2*j+1]
virtual void GetEigenvalues(Array<real_t> & eig) const = 0;
/// @brief Extract a single eigenvector
///
/// The index i should be in the range [0, 2*numConverged). The
/// vectors corresponding to even indices are the real parts of the
/// converged eigenvectors and the odd indices correspond to the
/// imaginary parts.
virtual const Vector & GetEigenvector(unsigned int i) const = 0;
/// @brief Transfer ownership of the converged eigenvectors
///
/// The array should contain 2*numConverged vectors with the even
/// indices corresponding to the real parts of the converged
/// eigenvectors and the odd indices corresponding to the imaginary
/// parts.
virtual Vector ** StealEigenvectors() = 0;
};
}
#endif
+25 -7
View File
@@ -6556,7 +6556,7 @@ HypreLOBPCG::SetPreconditioner(Solver & precond)
}
void
HypreLOBPCG::SetOperator(Operator & A)
HypreLOBPCG::SetOperator(const Operator & A)
{
HYPRE_BigInt locSize = A.Width();
@@ -6603,7 +6603,7 @@ HypreLOBPCG::SetOperator(Operator & A)
}
void
HypreLOBPCG::SetMassMatrix(Operator & M)
HypreLOBPCG::SetMassMatrix(const Operator & M)
{
matvec_fn.MatvecCreate = this->OperatorMatvecCreate;
matvec_fn.Matvec = this->OperatorMatvec;
@@ -6624,7 +6624,7 @@ HypreLOBPCG::GetEigenvalues(Array<real_t> & eigs) const
}
}
const HypreParVector &
const Vector &
HypreLOBPCG::GetEigenvector(unsigned int i) const
{
return multi_vec->GetVector(i);
@@ -6866,6 +6866,24 @@ HypreAME::SetPreconditioner(HypreSolver & precond)
ams_precond = &precond;
}
void
HypreAME::SetOperators(const Operator & opA, const Operator & opB)
{
const HypreParMatrix * A = dynamic_cast<const HypreParMatrix *>(&opA);
if (A == NULL)
{
mfem_error("HypreAME::SetOperator : first operator not HypreParMatrix!");
}
SetOperator(*A);
const HypreParMatrix * B = dynamic_cast<const HypreParMatrix *>(&opB);
if (B == NULL)
{
mfem_error("HypreAME::SetOperator : second operator not HypreParMatrix!");
}
SetMassMatrix(*B);
}
void
HypreAME::SetOperator(const HypreParMatrix & A)
{
@@ -6924,7 +6942,7 @@ HypreAME::createDummyVectors() const
}
}
const HypreParVector &
const Vector &
HypreAME::GetEigenvector(unsigned int i) const
{
if ( eigenvectors == NULL )
@@ -6935,7 +6953,7 @@ HypreAME::GetEigenvector(unsigned int i) const
return *eigenvectors[i];
}
HypreParVector **
Vector **
HypreAME::StealEigenvectors()
{
if ( eigenvectors == NULL )
@@ -6944,11 +6962,11 @@ HypreAME::StealEigenvectors()
}
// Set the local pointers to NULL so that they won't be deleted later
HypreParVector ** vecs = eigenvectors;
Vector ** vecs = (Vector**)eigenvectors;
eigenvectors = NULL;
multi_vec = NULL;
return vecs;
return (Vector**)vecs;
}
}
+30 -20
View File
@@ -18,7 +18,9 @@
#include "../general/globals.hpp"
#include "sparsemat.hpp"
#include "eigensolvers.hpp"
#include "hypre_parcsr.hpp"
#include "eigensolvers.hpp"
#include <mpi.h>
// Enable internal hypre timing routines
@@ -2146,7 +2148,7 @@ public:
A. Knyazev, M. Argentati, I. Lashuk, and E. Ovtchinnikov, SISC, 29(5),
2224-2239, 2007.
*/
class HypreLOBPCG
class HypreLOBPCG : public SymGenEigensolver
{
private:
MPI_Comm comm;
@@ -2236,38 +2238,43 @@ public:
HypreLOBPCG(MPI_Comm comm);
~HypreLOBPCG();
void SetTol(real_t tol);
void SetTol(real_t tol) override;
// not implemented in HYPRE
// real_t GetTol() const;
void SetRelTol(real_t rel_tol);
// not implemented in HYPRE
// real_t GetRelTol() const;
void SetMaxIter(int max_iter);
void SetMaxIter(int max_iter) override;
// not implemented in HYPRE
// int GetMaxIter() const;
void SetPrintLevel(int logging);
void SetNumModes(int num_eigs) { nev = num_eigs; }
void SetPrintLevel(int logging) override;
void SetNumModes(int num_eigs) override { nev = num_eigs; }
void SetPrecondUsageMode(int pcg_mode);
void SetRandomSeed(int s) { seed = s; }
void SetInitialVectors(int num_vecs, HypreParVector ** vecs);
// The following four methods support general operators
void SetPreconditioner(Solver & precond);
void SetOperator(Operator & A);
void SetMassMatrix(Operator & M);
void SetOperators(const Operator & A, const Operator & B) override
{ SetOperator(A); SetMassMatrix(B); }
void SetOperator(const Operator & A);
void SetMassMatrix(const Operator & M);
void SetSubSpaceProjector(Operator & proj) { subSpaceProj = &proj; }
/// Solve the eigenproblem
void Solve();
void Solve() override;
int GetNumConverged() const override { return nev; }
/// Collect the converged eigenvalues
void GetEigenvalues(Array<real_t> & eigenvalues) const;
void GetEigenvalues(Array<real_t> & eigenvalues) const override;
/// Extract a single eigenvector
const HypreParVector & GetEigenvector(unsigned int i) const;
const Vector & GetEigenvector(unsigned int i) const override;
/// Transfer ownership of the converged eigenvectors
HypreParVector ** StealEigenvectors() { return multi_vec->StealVectors(); }
Vector ** StealEigenvectors() override
{ return (Vector**)multi_vec->StealVectors(); }
};
/** AME eigenvalue solver in hypre
@@ -2292,7 +2299,7 @@ public:
mass matrix but it seems unlikely that this would be useful so it is not the
default behavior.
*/
class HypreAME
class HypreAME : public SymGenEigensolver
{
private:
int myid;
@@ -2321,28 +2328,31 @@ public:
HypreAME(MPI_Comm comm);
~HypreAME();
void SetTol(real_t tol);
void SetTol(real_t tol) override;
void SetRelTol(real_t rel_tol);
void SetMaxIter(int max_iter);
void SetPrintLevel(int logging);
void SetNumModes(int num_eigs);
void SetMaxIter(int max_iter) override;
void SetPrintLevel(int logging) override;
void SetNumModes(int num_eigs) override;
// The following four methods support operators of type HypreParMatrix.
void SetPreconditioner(HypreSolver & precond);
void SetOperators(const Operator & opA, const Operator & opB) override;
void SetOperator(const HypreParMatrix & A);
void SetMassMatrix(const HypreParMatrix & M);
/// Solve the eigenproblem
void Solve();
void Solve() override;
int GetNumConverged() const override { return nev; }
/// Collect the converged eigenvalues
void GetEigenvalues(Array<real_t> & eigenvalues) const;
void GetEigenvalues(Array<real_t> & eigenvalues) const override;
/// Extract a single eigenvector
const HypreParVector & GetEigenvector(unsigned int i) const;
const Vector & GetEigenvector(unsigned int i) const override;
/// Transfer ownership of the converged eigenvectors
HypreParVector ** StealEigenvectors();
Vector ** StealEigenvectors() override;
};
}
+5
View File
@@ -28,6 +28,7 @@
#include "symmat.hpp"
#include "ode.hpp"
#include "solvers.hpp"
#include "eigensolvers.hpp"
#include "handle.hpp"
#include "invariants.hpp"
#include "constraints.hpp"
@@ -57,6 +58,10 @@
#include "ginkgo.hpp"
#endif
#ifdef MFEM_USE_ARPACK
#include "arpack.hpp"
#endif
#ifdef MFEM_USE_MKL_PARDISO
#include "pardiso.hpp"
#endif
+16
View File
@@ -844,6 +844,22 @@ public:
};
/// Zero Operator N: x -> 0.
class ZeroOperator : public Operator
{
public:
/// Create an zero operator of size @a n.
explicit ZeroOperator(int n) : Operator(n) { }
/// Operator application
void Mult(const Vector &x, Vector &y) const override
{ y.SetSize(width); y = 0_r; }
/// Application of the transpose
void MultTranspose(const Vector &x, Vector &y) const override
{ y.SetSize(width); y = 0_r; }
};
/// Identity Operator I: x -> x.
class IdentityOperator : public Operator
{
+6 -4
View File
@@ -126,11 +126,11 @@ EXAMPLE_TEST_DIRS := examples
MINIAPP_SUBDIRS = common electromagnetics meshing performance tools \
toys nurbs gslib adjoint solvers shifted mtop parelag tribol autodiff dfem \
hooke multidomain dpg hdiv-linear-solver spde diag-smoothers contact \
fluids/navier fluids/schrodinger-flow plasma
fluids/navier fluids/schrodinger-flow plasma plasma/pic
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
toys shifted dpg diag-smoothers fluids/navier plasma)
toys shifted dpg diag-smoothers fluids/navier plasma plasma/pic)
EM_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_DIRS)
@@ -302,7 +302,7 @@ endif
MFEM_REQ_LIB_DEPS = SUPERLU MUMPS METIS FMS CONDUIT SIDRE LAPACK SUNDIALS\
SUITESPARSE STRUMPACK GINKGO GNUTLS HDF5 NETCDF SLEPC PETSC MPFR PUMI HIOP\
GSLIB OCCA CEED RAJA UMPIRE MKL_CPARDISO MKL_PARDISO AMGX MAGMA CALIPER PARELAG\
TRIBOL BENCHMARK MOONOLITH ALGOIM
TRIBOL BENCHMARK MOONOLITH ALGOIM ARPACK
PETSC_ERROR_MSG = $(if $(PETSC_FOUND),,. PETSC config not found: $(PETSC_VARS))
@@ -371,7 +371,8 @@ MFEM_DEFINES = MFEM_VERSION MFEM_VERSION_STRING MFEM_GIT_STRING MFEM_USE_MPI\
MFEM_USE_SIMD MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO MFEM_USE_AMGX\
MFEM_USE_MAGMA MFEM_USE_MUMPS MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_CALIPER\
MFEM_USE_BENCHMARK MFEM_USE_PARELAG MFEM_USE_TRIBOL MFEM_USE_ALGOIM MFEM_USE_ENZYME\
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_SHARED_BUILD MFEM_USE_DOUBLE MFEM_USE_SINGLE
MFEM_SOURCE_DIR MFEM_INSTALL_DIR MFEM_SHARED_BUILD MFEM_USE_DOUBLE MFEM_USE_SINGLE\
MFEM_USE_ARPACK
# List of makefile variables that will be written to config.mk:
MFEM_CONFIG_VARS = MFEM_CXX MFEM_HOST_CXX MFEM_CPPFLAGS MFEM_CXXFLAGS\
@@ -733,6 +734,7 @@ status info:
$(info MFEM_TIMER_TYPE = $(MFEM_TIMER_TYPE))
$(info MFEM_USE_SUNDIALS = $(MFEM_USE_SUNDIALS))
$(info MFEM_USE_SUITESPARSE = $(MFEM_USE_SUITESPARSE))
$(info MFEM_USE_ARPACK = $(MFEM_USE_ARPACK))
$(info MFEM_USE_SUPERLU = $(MFEM_USE_SUPERLU))
$(info MFEM_USE_SUPERLU5 = $(MFEM_USE_SUPERLU5))
$(info MFEM_USE_MUMPS = $(MFEM_USE_MUMPS))
+3 -1
View File
@@ -1616,7 +1616,9 @@ Element::Type Mesh::GetFaceElementType(int Face) const
Array<int> Mesh::GetFaceToBdrElMap() const
{
Array<int> face_to_be(Dim == 2 ? NumOfEdges : NumOfFaces);
Array<int> face_to_be(Dim == 1 ? NumOfVertices :
Dim == 2 ? NumOfEdges :
Dim == 3 ? NumOfFaces : 0);
face_to_be = -1;
for (int i = 0; i < NumOfBdrElements; i++)
{
-3
View File
@@ -63,7 +63,6 @@ ThresholdRefiner::ThresholdRefiner(ErrorEstimator &est)
threshold = 0.0;
num_marked_elements = 0LL;
current_sequence = -1;
non_conforming = -1;
nc_limit = 0;
@@ -87,7 +86,6 @@ int ThresholdRefiner::MarkWithoutRefining(Mesh & mesh,
threshold = 0.0;
num_marked_elements = 0LL;
refinements.SetSize(0);
current_sequence = mesh.GetSequence();
const long long num_elements = mesh.GetGlobalNE();
if (num_elements >= max_elements) { return STOP; }
@@ -149,7 +147,6 @@ int ThresholdRefiner::ApplyImpl(Mesh &mesh)
void ThresholdRefiner::Reset()
{
estimator.Reset();
current_sequence = -1;
num_marked_elements = 0LL;
// marked_elements.SetSize(0); // not necessary
}
-1
View File
@@ -188,7 +188,6 @@ protected:
long long num_marked_elements;
Array<Refinement> marked_elements;
long current_sequence;
int non_conforming;
int nc_limit;
+17 -3
View File
@@ -227,15 +227,29 @@ public:
const ParGridFunction &dst);
/**
* @brief Check if ParMesh @a m is a ParSubMesh.
* @brief Check if Mesh @a m is a ParSubMesh.
*
* @param m The input ParMesh
* @param m The input Mesh
*/
static bool IsParSubMesh(const ParMesh *m)
static bool IsParSubMesh(const Mesh *m)
{
return dynamic_cast<const ParSubMesh *>(m) != nullptr;
}
/**
* @brief Check if Mesh @a sub is a ParSubMesh of Mesh @a parent.
*
* @param sub The potential submesh Mesh
* @param parent The potential parent Mesh
*/
static bool IsParSubMesh(const Mesh* sub, const Mesh* parent)
{
while (IsParSubMesh(sub) &&
(sub = static_cast<const ParSubMesh *>(sub)->GetParent()) &&
sub != parent);
return sub == parent;
}
private:
ParSubMesh(const ParMesh &parent, SubMesh::From from,
const Array<int> &attributes);
+14
View File
@@ -225,6 +225,20 @@ public:
return dynamic_cast<const SubMesh *>(m) != nullptr;
}
/**
* @brief Check if Mesh @a sub is a SubMesh of Mesh @a parent.
*
* @param sub The potential submesh Mesh
* @param parent The potential parent Mesh
*/
static bool IsSubMesh(const Mesh* sub, const Mesh* parent)
{
while (IsSubMesh(sub) &&
(sub = static_cast<const SubMesh *>(sub)->GetParent()) &&
sub != parent);
return sub == parent;
}
private:
/// Private constructor
SubMesh(const Mesh &parent, From from, const Array<int> &attributes);
+55 -6
View File
@@ -43,19 +43,39 @@ endif()
# Add the corresponding tests to the "test" target
if (MFEM_ENABLE_TESTING)
add_test(NAME tesla_np=4
add_test(NAME tesla_1_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:tesla> -no-vis -maxit 2 -cr "0 0 -0.2 0 0 0.2 0.2 0.4 1"
${MPIEXEC_POSTFLAGS})
add_test(NAME volta_np=4
add_test(NAME tesla_2_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:volta> -no-vis -maxit 2 -dbcs 1 -dbcg -ds "0.0 0.0 0.0 0.2 8.0"
$<TARGET_FILE:tesla>
-no-vis -maxit 2 -m ../../data/inline-hex.mesh -ubbc "0 0 1"
${MPIEXEC_POSTFLAGS})
add_test(NAME joule_np=4
add_test(NAME volta_1_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:volta>
-no-vis -maxit 2 -dbcs 1 -dbcg -ds "0.0 0.0 0.0 0.2 8.0"
${MPIEXEC_POSTFLAGS})
add_test(NAME volta_2_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:volta>
-no-vis -maxit 2 -m ../../data/square-disc.mesh -dbcs "1 2 3 4 5 6 7 8"
-dbcv "0 0 0 0 1 1 1 1"
${MPIEXEC_POSTFLAGS})
add_test(NAME volta_3_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:volta>
-no-vis -maxit 2 -m ../../data/inline-hex.mesh -dbcs "1 6" -dbcv "0 1"
${MPIEXEC_POSTFLAGS})
add_test(NAME joule_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:joule>
@@ -63,12 +83,41 @@ endif()
${MPIEXEC_POSTFLAGS})
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME maxwell_np=4
add_test(NAME maxwell_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:maxwell>
-no-vis -abcs "-1" -dp "-0.3 0.0 0.0 0.3 0.0 0.0 0.1 1 .5 .5"
${MPIEXEC_POSTFLAGS})
endif()
if (MFEM_USE_GSLIB)
add_test(NAME lorentz_1_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:lorentz>
-no-vis -er Volta-AMR-Parallel -ec 2 -npt 100 -xmin "0.0 0.0 0.0"
-xmax "1.0 1.0 1.0" -pmin "1 0 0" -pmax "1 0 0" -rdf 0 -vt 0 -nt 100
${MPIEXEC_POSTFLAGS})
# Setup dependency on volta_3_np=<np>
set_tests_properties(volta_3_np=${MFEM_MPI_NP}
PROPERTIES FIXTURES_SETUP Volta3)
set_tests_properties(lorentz_1_np=${MFEM_MPI_NP}
PROPERTIES FIXTURES_REQUIRED Volta3)
add_test(NAME lorentz_2_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:lorentz>
-no-vis -br Tesla-AMR-Parallel -bc 2 -npt 10 -xmin "0.0 0.0 0.0"
-xmax "1.0 1.0 1.0" -pmin "0 0.1 0.05" -pmax "0 0.4 0.1" -nt 1000 -rdf 0
-vt 0
${MPIEXEC_POSTFLAGS})
# Setup dependency on tesla_2_np=<np>
set_tests_properties(tesla_2_np=${MFEM_MPI_NP}
PROPERTIES FIXTURES_SETUP Tesla2)
set_tests_properties(lorentz_2_np=${MFEM_MPI_NP}
PROPERTIES FIXTURES_REQUIRED Tesla2)
endif()
endif()
endif()
+2 -2
View File
@@ -117,10 +117,10 @@ joule-test-par: joule
lorentz-test-par: lorentz-test-1 lorentz-test-2
lorentz-test-1: lorentz volta-test-3
@$(call mfem-test,$<, $(RUN_MPI), Electromagnetic miniapp,\
-er Volta-AMR-Parallel -ec 2 -npt 100 -xmin '0.0 0.0 0.0' -xmax '1.0 1.0 1.0' -pmin '1 0 0' -pmax '1 0 0' -rdf 0 -vt 0 -nt 100')
-er Volta-AMR-Parallel -ec 2 -npt 100 -xmin '0.0 0.0 0.0' -xmax '1.0 1.0 1.0' -pmin '1 0 0' -pmax '1 0 0' -rdf 0 -vt 0 -nt 100)
lorentz-test-2: lorentz tesla-test-2
@$(call mfem-test,$<, $(RUN_MPI), Electromagnetic miniapp,\
-br Tesla-AMR-Parallel -bc 2 -br Tesla-AMR-Parallel -npt 10 -xmin '0.0 0.0 0.0' -xmax '1.0 1.0 1.0' -pmin '0 0.1 0.05' -pmax '0 0.4 0.1' -nt 1000 -rdf 0 -vt 0)
-br Tesla-AMR-Parallel -bc 2 -npt 10 -xmin '0.0 0.0 0.0' -xmax '1.0 1.0 1.0' -pmin '0 0.1 0.05' -pmax '0 0.4 0.1' -nt 1000 -rdf 0 -vt 0)
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
+2 -2
View File
@@ -22,7 +22,7 @@ void ComputeInverse(const Array<real_t> &A, Array<real_t> &Ainv)
{
Array<real_t> A2 = A;
const int n2 = A.Size();
const int n = static_cast<const int>(sqrt(n2));
const int n = static_cast<int>(sqrt(n2));
Array<int> ipiv(n);
LUFactors lu(A2.GetData(), ipiv.GetData());
lu.Factor(n);
@@ -58,7 +58,7 @@ void SubcellIntegrals(int n, const Poly_1D::Basis &basis, Array<real_t> &B)
void Transpose(const Array<real_t> &B, Array<real_t> &Bt)
{
const int n = static_cast<const int>(sqrt(B.Size()));
const int n = static_cast<int>(sqrt(B.Size()));
Bt.SetSize(n*n);
for (int i=0; i<n; ++i) for (int j=0; j<n; ++j) { Bt[i+j*n] = B[j+i*n]; }
}
+4 -4
View File
@@ -329,8 +329,8 @@ int main(int argc, char *argv[])
for (int i=0; i<nev; i++)
{
// convert eigenvector from HypreParVector to ParGridFunction
x = lobpcg->GetEigenvector(i);
// convert eigenvector from Vector to ParGridFunction
x.Distribute(lobpcg->GetEigenvector(i));
mode_name << "mode_" << setfill('0') << setw(2) << i << "."
<< setfill('0') << setw(6) << myid;
@@ -357,8 +357,8 @@ int main(int argc, char *argv[])
<< ", Lambda = " << eigenvalues[i] << endl;
}
// convert eigenvector from HypreParVector to ParGridFunction
x = lobpcg->GetEigenvector(i);
// convert eigenvector from Vector to ParGridFunction
x.Distribute(lobpcg->GetEigenvector(i));
mode_sock << "parallel " << num_procs << " " << myid << "\n"
<< "solution\n" << *pmesh << x << flush
+2
View File
@@ -23,3 +23,5 @@ if (MFEM_USE_MPI)
EXTRA_HEADERS ${PLASMA_COMMON_HEADERS})
endif()
add_subdirectory(pic)
+14 -6
View File
@@ -14,9 +14,6 @@ MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/plasma/,)
CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
# Use the MFEM install directory
# MFEM_INSTALL_DIR = ../../mfem
# CONFIG_MK = $(MFEM_INSTALL_DIR)/share/mfem/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
@@ -29,6 +26,8 @@ else
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
endif
PLASMA_SUBDIRS = pic
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all lib-common clean clean-build clean-exec
@@ -47,7 +46,12 @@ COMMON_O=
%: %.cpp
%.o: %.cpp
all: $(MINIAPPS)
all: $(MINIAPPS) subdirs
.PHONY: subdirs $(PLASMA_SUBDIRS)
subdirs: $(PLASMA_SUBDIRS)
$(PLASMA_SUBDIRS): lib-common
$(MAKE) -C $(BLD)$(@)
# Rules for building the miniapps
%: $(SRC)%.cpp $(COMMON_O) $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common
@@ -75,11 +79,15 @@ RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
ALL_CLEAN_SUBDIRS = $(addsuffix /clean,$(PLASMA_SUBDIRS))
.PHONY: $(ALL_CLEAN_SUBDIRS)
$(ALL_CLEAN_SUBDIRS):
$(MAKE) -C $(BLD)$(@D) $(@F)
clean: clean-build clean-exec
clean-build:
clean-build: $(addsuffix /clean,$(PLASMA_SUBDIRS))
rm -f *.o *~ $(SEQ_MINIAPPS) $(PAR_MINIAPPS)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
+28
View File
@@ -0,0 +1,28 @@
# 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.
if (MFEM_USE_MPI AND MFEM_USE_GSLIB)
add_mfem_miniapp(electrostatic-pic
MAIN electrostatic-pic.cpp
EXTRA_HEADERS ${MFEM_MINIAPPS_COMMON_HEADERS}
LIBRARIES mfem-common)
# Add the corresponding tests to the "test" target
if (MFEM_ENABLE_TESTING)
add_test(NAME electrostatic-pic_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:electrostatic-pic> -rdi 2 -npt 40960 -k 0.2855993321 -a 0.05
-nt 200 -nx 16 -ny 16 -O 1 -q 0.01181640625 -m 0.01181640625 -oci 1000
-dt 0.1
${MPIEXEC_POSTFLAGS})
endif()
endif()
+788
View File
@@ -0,0 +1,788 @@
// 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.
//
// -----------------------------------------------------
// Particle-In-Cell (PIC) Simulation (2D/3D)
// -----------------------------------------------------
//
// This miniapp performs a Particle-In-Cell simulation (supports 2D or 3D
// spatial dimensions) of multiple charged particles subject to electric
// field forces.
//
// dp/dt = q E
//
// The method used is explicit time integration with a leap-frog scheme.
//
// The electric field is computed from the particle charge distribution using
// a Poisson solver. The particle trajectories are computed within a periodic
// domain (2D or 3D).
//
// Solution process (per timestep, repeating steps 1-6):
// (1) Deposit charge from particles to grid via Dirac delta function
// to form the RHS of the Poisson equation
// (2) Solve Poisson equation (-Δφ = ρ - ρ_0) to compute potential φ, where
// ρ_0 is a constant neutralizing term that enforces global charge
// neutrality.
// (3) Compute electric field E = -∇φ from the potential
// (4) Interpolate E-field to particle positions
// (5) Push particles using leap-frog scheme (update momentum and position)
// (6) Redistribute particles across processors
//
// Compile with: make electrostatic-pic
//
// Sample runs:
//
// 2D2V Linear Landau damping test case (Ricketson & Hu, 2025):
// mpirun -n 4 ./electrostatic-pic -rdi 1 -npt 409600 -k 0.2855993321 -a 0.05 -nt 200 -nx 32 -ny 32 -O 1 -q 0.001181640625 -m 0.001181640625 -oci 1000 -dt 0.1
// 3D3V Linear Landau damping test case (Zheng et al., 2025):
// * mpirun -n 128 ./electrostatic-pic -dim 3 -rdi 1 -npt 40960000 -k 0.5 -a 0.01 -nt 100 -nx 32 -ny 32 -nz 32 -O 1 -q 0.00004844730731 -m 0.00004844730731 -oci 1000 -dt 0.02 -no-vis
#include "mfem.hpp"
#include "../../../general/text.hpp"
#include "../../common/fem_extras.hpp"
#include "../../common/particles_extras.hpp"
#include "../../common/pfem_extras.hpp"
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <random>
#include <string>
#include <vector>
#define EPSILON 1 // ε_0
using namespace std;
using namespace mfem;
using namespace mfem::common;
struct PICContext
{
int dim = 2; ///< Spatial dimension.
int order = 1; ///< FE order for spatial discretization.
int nx = 100; ///< Number of grid cells in x-direction.
int ny = 100; ///< Number of grid cells in y-direction.
int nz = 100; ///< Number of grid cells in z-direction.
real_t L = 1.0; ///< Domain length.
int ordering = 1; ///< Ordering of particles.
int npt = 1000; ///< Number of particles.
real_t q = 1.0; ///< Particle charge.
real_t m = 1.0; ///< Particle mass.
real_t k = 1.0; ///< Wave number (Landau damping init).
real_t alpha = 0.1; ///< Perturbation amplitude (Landau damping init).
real_t dt = 1e-2; ///< Time step size.
int nt = 1000; ///< Number of time steps to run.
int redist_interval = 5; ///< Redistribution and update E_gf interval.
int output_csv_interval = 1000; ///< Interval for outputting CSV data files.
bool visualization = true; ///< Enable visualization.
int visport = 19916; ///< Port number for visualization server.
bool reproduce = true; ///< Enable reproducible results.
} ctx;
/** This class implements explicit time integration for charged particles
in an electric field using ParticleSet. */
class ParticleMover
{
public:
enum Fields
{
MASS, // vdim = 1
CHARGE, // vdim = 1
MOM, // vdim = dim
EFIELD // vdim = dim
};
protected:
/// Pointers to E field GridFunctions
ParGridFunction* E_gf;
/// FindPointsGSLIB object for E field mesh
FindPointsGSLIB& E_finder;
/// ParticleSet of charged particles
std::unique_ptr<ParticleSet> charged_particles;
/// Temporary vectors for particle computation
mutable Vector pm_, pp_;
public:
ParticleMover(MPI_Comm comm, ParGridFunction* E_gf_,
FindPointsGSLIB& E_finder_, int num_particles,
Ordering::Type pdata_ordering);
/// Initialize charged particles with given parameters
void InitializeChargedParticles(const real_t& k, const real_t& alpha,
real_t m, real_t q, real_t L,
bool reproduce = false);
/// Find Particles in mesh corresponding to E and field
void FindParticles();
/// Advance particles one time step using Boris algorithm
void Step(real_t& t, real_t dt, real_t L, bool first_step = false);
/// Redistribute particles across processors
void Redistribute();
/// Get reference to ParticleSet
ParticleSet& GetParticles() { return *charged_particles; }
/// Compute (global) kinetic energy from particles
/** Optionally, advance the particle momenta by time step @a dt. */
real_t ComputeKineticEnergy(real_t dt = 0.) const;
};
/** Field solver responsible for updating the electrostatic potential and field
from the particle charge density. Assembles and solves the periodic Poisson
problem, computes the electric field via a discrete gradient operator, and
provides utilities for field diagnostics (e.g. global field energy). */
class FieldSolver
{
private:
real_t domain_volume;
real_t neutralizing_const;
ParLinearForm* precomputed_neutralizing_lf = nullptr;
bool precompute_neutralizing_const = false;
// Diffusion matrix
HypreParMatrix* diffusion_matrix;
// Gradient operator for computing E = -∇φ
ParDiscreteLinearOperator* grad_interpolator;
FindPointsGSLIB& E_finder;
ParLinearForm b;
protected:
/** Compute neutralizing constant and initialize with the constant.
Returns a reference to the precomputed neutralizing ParLinearForm. */
const ParLinearForm& ComputeNeutralizingRHS(ParFiniteElementSpace* pfes,
const ParticleVector& Q,
MPI_Comm comm);
/** Deposit charge from particles into a ParLinearForm (RHS b).
b_i = sum_p q_p * φ_i(x_p) */
void DepositCharge(ParFiniteElementSpace* pfes, const ParticleVector& Q);
public:
FieldSolver(ParFiniteElementSpace* phi_fes, ParFiniteElementSpace* E_fes,
FindPointsGSLIB& E_finder_,
bool precompute_neutralizing_const_ = false);
~FieldSolver();
/** Update the phi_gf grid function from the particles.
Solve periodic Poisson: diffusion_matrix * phi = (rho - <rho>)
with zero-mean enforcement via OrthoSolver. */
void UpdatePhiGridFunction(ParticleSet& particles, ParGridFunction& phi_gf);
/** Update E_gf grid function from phi_gf grid function.
Compute the gradient: E = -φ. */
void UpdateEGridFunction(ParGridFunction& phi_gf, ParGridFunction& E_gf);
/// Compute (global) field energy: 0.5 * ∫ ||E||^2 dx
real_t ComputeFieldEnergy(const ParGridFunction& E_gf) const;
};
/// Prints the program's logo to the given output stream
void display_banner(ostream& os);
int main(int argc, char* argv[])
{
Mpi::Init(argc, argv);
int num_ranks = Mpi::WorldSize();
int rank = Mpi::WorldRank();
Hypre::Init();
if (Mpi::Root()) { display_banner(cout); }
OptionsParser args(argc, argv);
args.AddOption(&ctx.dim, "-dim", "--dimension",
"Spatial dimension (2 or 3)");
args.AddOption(&ctx.order, "-O", "--order",
"Finite element polynomial degree");
args.AddOption(&ctx.nx, "-nx", "--num-x",
"Number of elements in the x direction.");
args.AddOption(&ctx.ny, "-ny", "--num-y",
"Number of elements in the y direction.");
args.AddOption(&ctx.nz, "-nz", "--num-z",
"Number of elements in the z direction.");
args.AddOption(&ctx.q, "-q", "--charge", "Particle charge.");
args.AddOption(&ctx.m, "-m", "--mass", "Particle mass.");
args.AddOption(&ctx.dt, "-dt", "--time-step", "Time Step.");
args.AddOption(&ctx.nt, "-nt", "--num-timesteps", "Number of timesteps.");
args.AddOption(&ctx.npt, "-npt", "--num-particles",
"Total number of particles.");
args.AddOption(&ctx.k, "-k", "--k", "Wave number for initial distribution.");
args.AddOption(&ctx.alpha, "-a", "--alpha",
"Perturbation amplitude for initial distribution.");
args.AddOption(&ctx.ordering, "-o", "--ordering",
"Ordering of particle data. 0 = byNODES, 1 = byVDIM.");
args.AddOption(&ctx.redist_interval, "-rdi", "--redist-interval",
"Redistribution and update E_gf interval. Disabled if < 0.");
args.AddOption(&ctx.output_csv_interval, "-oci", "--output-csv-interval",
"Output CSV interval. Disabled if < 0.");
args.AddOption(&ctx.visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&ctx.visport, "-p", "--send-port", "Socket for GLVis.");
args.AddOption(&ctx.reproduce, "-rep", "--reproduce", "-no-rep",
"--no-reproduce",
"Enable or disable reproducible random seed.");
args.Parse();
if (!args.Good())
{
if (Mpi::Root()) { args.PrintUsage(cout); }
return 1;
}
if (Mpi::Root()) { args.PrintOptions(cout); }
// Assert that dimension is 2 or 3
MFEM_VERIFY(ctx.dim == 2 || ctx.dim == 3,
"Dimension must be 2 or 3, got " << ctx.dim);
MFEM_VERIFY(ctx.alpha >= -1.0 && ctx.alpha < 1.0,
"Alpha should be in range [-1, 1).");
MFEM_VERIFY(ctx.k > 0.0,
"k must be nonzero for displacement initialization.");
ctx.L = 2.0 * M_PI / ctx.k;
// 1. make a Cartesian Mesh (2D or 3D)
Mesh serial_mesh;
std::vector<Vector> translations;
if (ctx.dim == 2)
{
serial_mesh = Mesh(Mesh::MakeCartesian2D(
ctx.nx, ctx.ny, Element::QUADRILATERAL, false, ctx.L, ctx.L));
translations = {Vector({ctx.L, 0.0}), Vector({0.0, ctx.L})};
}
else // ctx.dim == 3
{
serial_mesh = Mesh(Mesh::MakeCartesian3D(
ctx.nx, ctx.ny, ctx.nz, Element::HEXAHEDRON, ctx.L, ctx.L, ctx.L));
translations = {Vector({ctx.L, 0.0, 0.0}), Vector({0.0, ctx.L, 0.0}),
Vector({0.0, 0.0, ctx.L})
};
}
Mesh periodic_mesh(Mesh::MakePeriodic(
serial_mesh, serial_mesh.CreatePeriodicVertexMapping(translations)));
// 2. Partition and distribute the mesh
ParMesh mesh(MPI_COMM_WORLD, periodic_mesh);
serial_mesh.Clear(); // the serial mesh is no longer needed
periodic_mesh.Clear(); // the periodic mesh is no longer needed
// 3. Build the interpolator of E field
mesh.EnsureNodes();
FindPointsGSLIB E_finder(mesh);
// 4. Define finite element spaces on the parallel mesh
H1_FECollection phi_fec(ctx.order, ctx.dim);
ParFiniteElementSpace phi_fespace(&mesh, &phi_fec);
ND_FECollection E_fec(ctx.order, ctx.dim);
ParFiniteElementSpace E_fespace(&mesh, &E_fec);
// 5. Initialize the grid functions for the electric field and potential
ParGridFunction phi_gf(&phi_fespace);
ParGridFunction E_gf(&E_fespace);
phi_gf = 0.0; // Initialize phi_gf to zero
E_gf = 0.0; // Initialize E_gf to zero
// 6. Construct the field solver
FieldSolver field_solver(&phi_fespace, &E_fespace, E_finder, true);
// 7. Initialize ParticleMover
Ordering::Type ordering_type =
ctx.ordering == 0 ? Ordering::byNODES : Ordering::byVDIM;
int num_particles =
ctx.npt / num_ranks + (rank < (ctx.npt % num_ranks) ? 1 : 0);
ParticleMover particle_mover(MPI_COMM_WORLD, &E_gf, E_finder, num_particles,
ordering_type);
particle_mover.InitializeChargedParticles(ctx.k, ctx.alpha, ctx.m, ctx.q,
ctx.L, ctx.reproduce);
// 8. Start the main loop
real_t t = 0;
real_t dt = ctx.dt;
mfem::StopWatch sw;
sw.Start();
for (int step = 1; step <= ctx.nt; step++)
{
// Step the FieldSolver
if (ctx.redist_interval > 0 &&
(step % ctx.redist_interval == 0 || step == 1) &&
particle_mover.GetParticles().GetGlobalNParticles() > 0)
{
// Redistribute
particle_mover.Redistribute();
// Update phi_gf from particles
field_solver.UpdatePhiGridFunction(particle_mover.GetParticles(),
phi_gf);
// Update E_gf from phi_gf
field_solver.UpdateEGridFunction(phi_gf, E_gf);
// Visualize fields if requested
if (ctx.visualization)
{
static socketstream vis_e, vis_phi;
common::VisualizeField(vis_e, "localhost", ctx.visport, E_gf,
"E_field", 0, 0, 500, 500);
common::VisualizeField(vis_phi, "localhost", ctx.visport, phi_gf,
"Potential", 500, 0, 500, 500);
}
}
// Step the ParticleMover
particle_mover.Step(t, dt, ctx.L, step == 1);
if (Mpi::Root())
{
mfem::out << "Step: " << step << " | Time: " << t;
mfem::out << " | Time per step: " << sw.RealTime() / step;
mfem::out << endl;
}
// Output particle data to CSV
if (ctx.output_csv_interval > 0 &&
(step % ctx.output_csv_interval == 0 || step == 1))
{
std::string csv_prefix = "PIC_Part_";
Array<int> field_idx{2}, tag_idx;
std::string file_name =
csv_prefix + mfem::to_padded_string(step, 6) + ".csv";
particle_mover.GetParticles().PrintCSV(file_name.c_str(), field_idx,
tag_idx);
}
if (ctx.redist_interval > 0 &&
(step % ctx.redist_interval == 0 || step == 1) &&
particle_mover.GetParticles().GetGlobalNParticles() > 0)
{
// Compute energies
// Note that particle momenta are a half time step ahead of the field
// after particle_mover.Step(). Therefore they are returned to the
// time level of the field for calculation of kinetic energy.
real_t kinetic_energy = particle_mover.ComputeKineticEnergy(-dt/2.);
real_t field_energy = field_solver.ComputeFieldEnergy(E_gf);
// Output energies
if (Mpi::Root())
{
cout << "Kinetic energy: " << kinetic_energy << "\t"
<< "Field energy: " << field_energy << "\t"
<< "Total energy: " << kinetic_energy + field_energy
<< endl;
}
// Write energies to a CSV file
if (Mpi::Root())
{
std::ofstream energy_file("energy.csv", std::ios::app);
energy_file << setprecision(10) << kinetic_energy << ","
<< field_energy << "," << kinetic_energy + field_energy
<< "\n";
}
}
}
}
ParticleMover::ParticleMover(MPI_Comm comm, ParGridFunction* E_gf_,
FindPointsGSLIB& E_finder_, int num_particles,
Ordering::Type pdata_ordering)
: E_gf(E_gf_), E_finder(E_finder_)
{
MFEM_ASSERT(E_gf, "Must pass an E field to ParticleMover.");
int dim = E_gf->ParFESpace()->GetMesh()->SpaceDimension();
pm_.SetSize(dim);
pp_.SetSize(dim);
// Create particle set: 2 scalars of mass and charge,
// 2 vectors of size space dim for momentum and e field
Array<int> field_vdims({1, 1, dim, dim});
charged_particles = std::make_unique<ParticleSet>(
comm, num_particles, dim, field_vdims, 1, pdata_ordering);
}
void ParticleMover::InitializeChargedParticles(const real_t& k,
const real_t& alpha, real_t m,
real_t q, real_t L,
bool reproduce)
{
int rank;
MPI_Comm_rank(charged_particles->GetComm(), &rank);
// use time-based seed for randomness
std::mt19937 gen(
reproduce ? rank : (rank + static_cast<unsigned int>(time(nullptr))));
std::uniform_real_distribution<> real_dist(0.0, 1.0);
std::normal_distribution<> norm_dist(0.0, 1.0);
int dim = charged_particles->Coords().GetVDim();
ParticleVector& X = charged_particles->Coords();
ParticleVector& P = charged_particles->Field(ParticleMover::MOM);
ParticleVector& M = charged_particles->Field(ParticleMover::MASS);
ParticleVector& Q = charged_particles->Field(ParticleMover::CHARGE);
for (int i = 0; i < charged_particles->GetNParticles(); i++)
{
// Initialize momentum
for (int d = 0; d < dim; d++) { P(i, d) = m * norm_dist(gen); }
// Uniform positions (no accept-reject)
for (int d = 0; d < dim; d++) { X(i, d) = real_dist(gen) * L; }
// Displacement along x for perturbation ~ cos(k x)
for (int d = 0; d < dim; d++)
{
real_t x = X(i, d);
x -= (alpha / k) * std::sin(k * x);
// periodic wrap to [0, L)
x = std::fmod(x, L);
if (x < 0) { x += L; }
X(i, d) = x;
}
// Initialize mass + charge
M(i) = m;
Q(i) = q;
}
FindParticles();
}
void ParticleMover::FindParticles()
{
E_finder.FindPoints(charged_particles->Coords());
}
void ParticleMover::Step(real_t& t, real_t dt, real_t L, bool first_step)
{
// Update E field at particles
ParticleVector& E = charged_particles->Field(EFIELD);
E_finder.Interpolate(*E_gf, E, E.GetOrdering());
// Extract particle data
ParticleVector& X = charged_particles->Coords();
ParticleVector& P = charged_particles->Field(MOM);
ParticleVector& M = charged_particles->Field(MASS);
ParticleVector& Q = charged_particles->Field(CHARGE);
// Accelerate the particles by the electric field
const int npt = charged_particles->GetNParticles();
const int dim = X.GetVDim();
for (int particle = 0; particle < npt; ++particle)
{
for (int d = 0; d < dim; ++d)
{
P(particle, d) +=
(first_step ? dt / 2.0 : dt) * Q(particle) * E(particle, d);
}
}
// Periodic boundary: wrap coordinates to [0, L)
for (int particle = 0; particle < npt; ++particle)
{
for (int d = 0; d < dim; ++d)
{
X(particle, d) += dt / M(particle) * P(particle, d);
while (X(particle, d) > L) { X(particle, d) -= L; }
while (X(particle, d) < 0.0) { X(particle, d) += L; }
}
}
FindParticles();
// Update time
t += dt;
}
void ParticleMover::Redistribute()
{
charged_particles->Redistribute(E_finder.GetProc());
FindParticles();
}
real_t ParticleMover::ComputeKineticEnergy(real_t dt) const
{
const ParticleVector& P = charged_particles->Field(MOM);
const ParticleVector& M = charged_particles->Field(MASS);
const ParticleVector& Q = charged_particles->Field(CHARGE);
const ParticleVector& E = charged_particles->Field(EFIELD);
// Note the electric field is not reinterpolated here and the last
// update from Step() is used directly.
real_t kinetic_energy = 0.0;
for (int p = 0; p < charged_particles->GetNParticles(); ++p)
{
real_t p_square_p = 0.0;
for (int d = 0; d < P.GetVDim(); ++d)
{
const real_t P_m = P(p, d) + dt * Q(p) * E(p, d);
p_square_p += P_m * P_m;
}
kinetic_energy += 0.5 * p_square_p / M(p);
}
real_t global_kinetic_energy = 0.0;
MPI_Allreduce(&kinetic_energy, &global_kinetic_energy, 1, MPI_DOUBLE,
MPI_SUM, charged_particles->GetComm());
return global_kinetic_energy;
}
FieldSolver::FieldSolver(ParFiniteElementSpace* phi_fes,
ParFiniteElementSpace* E_fes,
FindPointsGSLIB& E_finder_,
bool precompute_neutralizing_const_)
: precompute_neutralizing_const(precompute_neutralizing_const_),
E_finder(E_finder_),
b(phi_fes)
{
// compute domain volume
ParMesh* pmesh = phi_fes->GetParMesh();
real_t local_domain_volume = 0.0;
for (int i = 0; i < pmesh->GetNE(); i++)
{
local_domain_volume += pmesh->GetElementVolume(i);
}
MPI_Allreduce(&local_domain_volume, &domain_volume, 1, MPI_DOUBLE, MPI_SUM,
phi_fes->GetParMesh()->GetComm());
{
// Par bilinear form for the gradgrad matrix
ParBilinearForm dm(phi_fes);
ConstantCoefficient epsilon(EPSILON); // ε_0
dm.AddDomainIntegrator(
new DiffusionIntegrator(epsilon)); // ∫ ∇φ_i · ∇φ_j
dm.Assemble();
dm.Finalize();
diffusion_matrix = dm.ParallelAssemble(); // global gradgrad matrix
}
{
// Compute E = -∇φ using DiscreteLinearOperator
grad_interpolator = new ParDiscreteLinearOperator(phi_fes, E_fes);
grad_interpolator->AddDomainInterpolator(new GradientInterpolator);
grad_interpolator->Assemble();
}
}
FieldSolver::~FieldSolver()
{
delete diffusion_matrix;
delete precomputed_neutralizing_lf;
delete grad_interpolator;
}
const ParLinearForm& FieldSolver::ComputeNeutralizingRHS(
ParFiniteElementSpace* pfes, const ParticleVector& Q, MPI_Comm comm)
{
int npt = Q.Size();
// Get E_finder references
const Array<unsigned int>& code = E_finder.GetCode();
if (!precompute_neutralizing_const || precomputed_neutralizing_lf == nullptr)
{
// compute neutralizing constant
real_t local_sum = 0.0;
for (int p = 0; p < npt; ++p)
{
// Skip particles not successfully found
MFEM_ASSERT(code[p] != 2, "Particle " << p << " not found.");
local_sum += Q(p);
}
real_t global_sum = 0.0;
MPI_Allreduce(&local_sum, &global_sum, 1, MPI_DOUBLE, MPI_SUM, comm);
neutralizing_const = -global_sum / domain_volume;
if (Mpi::Root())
{
cout << "Total charge: " << global_sum
<< ", Domain volume: " << domain_volume
<< ", Neutralizing constant: " << neutralizing_const << endl;
if (precompute_neutralizing_const)
{
cout << "Further updates will use this precomputed neutralizing "
"constant."
<< endl;
}
}
delete precomputed_neutralizing_lf;
precomputed_neutralizing_lf = new ParLinearForm(pfes);
*precomputed_neutralizing_lf = 0.0;
ConstantCoefficient neutralizing_coeff(neutralizing_const);
precomputed_neutralizing_lf->AddDomainIntegrator(
new DomainLFIntegrator(neutralizing_coeff));
precomputed_neutralizing_lf->Assemble();
}
return *precomputed_neutralizing_lf;
}
void FieldSolver::DepositCharge(ParFiniteElementSpace* pfes,
const ParticleVector& Q)
{
int npt = Q.Size();
ParMesh* pmesh = pfes->GetParMesh();
int dim = pmesh->SpaceDimension();
int curr_rank;
MPI_Comm_rank(pmesh->GetComm(), &curr_rank);
// Get E_finder references
// 0: inside, 1: boundary, 2: not found
const Array<unsigned int>& code = E_finder.GetCode();
const Array<unsigned int>& proc = E_finder.GetProc(); // owning MPI rank
const Array<unsigned int>& elem = E_finder.GetElem(); // local element id
const Vector& rref = E_finder.GetReferencePosition(); // (r,s,t) byVDIM
Array<int> dofs;
for (int p = 0; p < npt; ++p)
{
// Skip particles not successfully found
MFEM_ASSERT(code[p] != 2, "Particle " << p << " not found.");
// Assert particle is on the current rank
MFEM_ASSERT((int)proc[p] == curr_rank,
"Particle " << p << " found in element owned by rank "
<< proc[p] << " but current rank is " << curr_rank
<< "." << endl
<< "You must call redistribute everytime before "
"updating the density grid function.");
const int e = elem[p];
// Reference coordinates for this particle (r,s[,t]) with byVDIM layout
IntegrationPoint ip;
ip.Set(rref.GetData() + dim * p, dim);
const FiniteElement& fe = *pfes->GetFE(e);
const int ldofs = fe.GetDof();
Vector shape(ldofs);
fe.CalcShape(ip, shape); // φ_i(x_p) in this element
pfes->GetElementDofs(e, dofs); // local dof indices
const real_t q_p = Q(p);
// Add q_p * φ_i(x_p) to b_i
b.AddElementVector(dofs, q_p, shape);
}
}
void FieldSolver::UpdatePhiGridFunction(ParticleSet& particles,
ParGridFunction& phi_gf)
{
// FE space / mesh
ParFiniteElementSpace* pfes = phi_gf.ParFESpace();
// Particle data: Q - charges (npt x 1)
ParticleVector& Q = particles.Field(ParticleMover::CHARGE);
// --------------------------------------------------------
// 1) Make RHS and pre-subtract averaged charge density for zero-mean RHS
// --------------------------------------------------------
MPI_Comm comm = pfes->GetComm();
b = ComputeNeutralizingRHS(pfes, Q, comm);
// --------------------------------------------------------
// 2) Deposit q_p * phi_i(x_p) into a ParLinearForm (RHS b)
// b_i = sum_p q_p * φ_i(x_p)
// --------------------------------------------------------
DepositCharge(pfes, Q);
// Assemble to a global true-dof RHS vector compatible with MassMatrix
HypreParVector B(pfes);
b.ParallelAssemble(B);
// ------------------------------------------------------------------
// 3) Solve A * phi = B with zero-mean enforcement via OrthoSolver
// ------------------------------------------------------------------
phi_gf = 0.0;
HypreParVector Phi_true(pfes);
Phi_true = 0.0;
HyprePCG solver(diffusion_matrix->GetComm());
solver.SetOperator(*diffusion_matrix);
solver.SetTol(1e-12);
solver.SetMaxIter(200);
solver.SetPrintLevel(0);
HypreBoomerAMG prec(*diffusion_matrix);
prec.SetPrintLevel(0);
solver.SetPreconditioner(prec);
OrthoSolver ortho(comm);
ortho.SetSolver(solver);
ortho.Mult(B, Phi_true);
// Map true-dof solution back to the ParGridFunction
phi_gf.Distribute(Phi_true);
}
void FieldSolver::UpdateEGridFunction(ParGridFunction& phi_gf,
ParGridFunction& E_gf)
{
// Compute ∇φ using precomputed gradient operator
grad_interpolator->Mult(phi_gf, E_gf);
// Scale by -1 to get E = -∇φ
E_gf.Neg();
}
real_t FieldSolver::ComputeFieldEnergy(const ParGridFunction& E_gf) const
{
// ---- Field energy: 0.5 * ∫ ||E||^2 dx ----
const ParFiniteElementSpace* fes = E_gf.ParFESpace();
const ParMesh* pmesh = fes->GetParMesh();
const int order = fes->GetMaxElementOrder();
const int qorder = std::max(2, 2 * order + 1);
const IntegrationRule* irs[Geometry::NumGeom];
for (int g = 0; g < Geometry::NumGeom; g++)
{
irs[g] = &IntRules.Get(g, qorder);
}
real_t field_energy = 0.0;
Vector zero(pmesh->Dimension());
zero = 0.0;
VectorConstantCoefficient zero_vec(zero);
const real_t E_l2 = E_gf.ComputeL2Error(zero_vec, irs);
field_energy = 0.5 * EPSILON * E_l2 * E_l2;
return field_energy;
}
void display_banner(ostream& os)
{
os << R"(
)"
<< endl
<< flush;
}
+85
View File
@@ -0,0 +1,85 @@
# 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.
# Use the MFEM build directory
MFEM_DIR ?= ../../..
MFEM_BUILD_DIR ?= ../../..
MFEM_INSTALL_DIR ?= ../../../mfem
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/miniapps/plasma/pic/,)
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
PAR_MINIAPPS =
ifeq ($(MFEM_USE_GSLIB),YES)
PAR_MINIAPPS += electrostatic-pic
endif
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS =
else
MINIAPPS = $(PAR_MINIAPPS)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all lib-common clean clean-build clean-exec
.PRECIOUS: %.o
COMMON_LIB = -L$(MFEM_BUILD_DIR)/miniapps/common -lmfem-common
# If MFEM_SHARED is set, add the ../common rpath
COMMON_LIB += $(if $(MFEM_SHARED:YES=),,\
$(MFEM_XLINKER)-rpath,$(abspath $(MFEM_BUILD_DIR)/miniapps/common))
# Remove built-in rules
%: %.cpp
%.o: %.cpp
all: $(MINIAPPS)
# Rules for building the miniapps
electrostatic-pic: electrostatic-pic.cpp $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common
$(MFEM_CXX) $(MFEM_FLAGS) -c $<
$(MFEM_CXX) $(MFEM_LINK_FLAGS) -o $@ $@.o $(COMMON_LIB) $(MFEM_LIBS)
# Rule for building lib-common
lib-common:
$(MAKE) -C $(MFEM_BUILD_DIR)/miniapps/common
MFEM_TESTS = MINIAPPS
include $(MFEM_TEST_MK)
# Testing: "test" target and mfem-test* variables are defined in config/test.mk
# Testing: Specific execution options
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
electrostatic-pic-test-par: electrostatic-pic
@$(call mfem-test,$<, $(RUN_MPI), PIC miniapp,\
-rdi 2 -npt 40960 -k 0.2855993321 -a 0.05 -nt 200 -nx 16 -ny 16\
-O 1 -q 0.01181640625 -m 0.01181640625 -oci 1000 -dt 0.1)
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_MINIAPPS) $(PAR_MINIAPPS)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -rf electrostatic-pic_* *.csv energy.csv
+1
View File
@@ -76,6 +76,7 @@ set(UNIT_TESTS_SRCS
linalg/test_vector.cpp
mesh/mesh_test_utils.cpp
mesh/test_exodus_reader.cpp
mesh/test_mfem_mesh_reader.cpp
mesh/test_exodus_writer.cpp
mesh/test_face_orientations.cpp
mesh/test_fms.cpp
+118
View File
@@ -0,0 +1,118 @@
MFEM mesh v1.3
#
# MFEM Geometry Types (see mesh/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
#
dimension
2
elements
12
10 2 7 0 1
11 2 0 7 2
12 2 9 0 2
13 2 0 9 3
14 2 11 0 3
15 2 0 11 4
16 2 5 0 4
17 2 0 5 1
9 3 1 5 6 7
9 3 2 7 8 9
9 3 3 9 10 11
9 3 4 11 12 5
attribute_sets
16
"Base" 1 9
"E Even" 1 16
"E Odd" 1 17
"East"
2
16
17
"N Even" 1 10
"N Odd" 1 11
"North" 2 10 11
"Rose" 8 10 11 12
13 14
15 16 17
"Rose Even" 4
10
12
14
16
"Rose Odd"
4
11
13
15
17
"S Even" 1 14
"S Odd" 1 15
South 2
14
15
"W Even" 1 12
"W Odd" 1 13
West 2 12 13
boundary
8
1 1 5 6
2 1 6 7
3 1 7 8
4 1 8 9
5 1 9 10
6 1 10 11
7 1 11 12
8 1 12 5
bdr_attribute_sets
13
"Boundary" 8 1 2 3 4 5 6 7 8
"ENE" 1 1
"ESE" 1 8
"Eastern Boundary" 2 1 8
"NNE" 1 2
"NNW" 1 3
"Northern Boundary"
2
2
3
"SSE" 1 7
"SSW" 1 6
"Southern Boundary" 2
6
7
"WNW" 1 4
"WSW" 1 5
"Western Boundary" 2 4
5
vertices
13
2
0 0
0.14142136 0.14142136
-0.14142136 0.14142136
-0.14142136 -0.14142136
0.14142136 -0.14142136
1 0
0.70710678 0.70710678
0 1
-0.70710678 0.70710678
-1 0
-0.70710678 -0.70710678
0 -1
0.70710678 -0.70710678
mfem_mesh_end
+1 -1
View File
@@ -296,7 +296,7 @@ void TestRedistribute(Ordering::Type ordering)
int wrong_proc_count = 0;
for (int i = 0; i < procs.Size(); i++)
{
if (rank != procs[i])
if (static_cast<unsigned>(rank) != procs[i])
{
wrong_proc_count++;
}
+26
View File
@@ -30,3 +30,29 @@ TEST_CASE("String Manipulation", "[General]")
}
}
}
TEST_CASE("Quoted String Input", "[General]")
{
const auto test_strings =
{
"Test",
"Test with spaces",
"Test with \"quoted text\"",
"Test string ending with \\",
"\nTest with\tvarious white\v\rspace characters.",
"Test with some unicode characters: ∆, ∉, ∑, 🍎."
};
for (const auto c_str : test_strings)
{
CAPTURE(c_str);
const std::string str(c_str);
std::stringstream ss;
ss << std::quoted(str);
std::string read_str;
int error = parse_quoted_string(read_str, ss);
CHECK(error == 0);
CHECK(read_str == str);
}
}
+108
View File
@@ -0,0 +1,108 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "mfem.hpp"
#include "unit_tests.hpp"
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
using namespace mfem;
TEST_CASE("MFEM Mesh Named Attributes", "[Mesh]")
{
// Path relative to the directory tests/unit
Mesh mesh("data/compass-testing.mesh");
REQUIRE(mesh.Dimension() == 2);
REQUIRE(mesh.GetNE() == 12);
REQUIRE(mesh.GetNV() == 13);
REQUIRE(mesh.attribute_sets.attr_sets.Size() == 16);
REQUIRE(mesh.bdr_attribute_sets.attr_sets.Size() == 13);
std::vector<std::pair<std::string, std::vector<int>>> expected_attr_sets =
{
{"Base", {9}},
{"E Even", {16}},
{"E Odd", {17}},
{"East", {16, 17}},
{"N Even", {10}},
{"N Odd", {11}},
{"North", {10, 11}},
{"Rose", {10, 11, 12, 13, 14, 15, 16, 17}},
{"Rose Even", {10, 12, 14, 16}},
{"Rose Odd", {11, 13, 15, 17}},
{"S Even", {14}},
{"S Odd", {15}},
{"South", {14, 15}},
{"W Even", {12}},
{"W Odd", {13}},
{"West", {12, 13}}
};
for (auto const &attr_name_index_pair: expected_attr_sets )
{
REQUIRE(mesh.attribute_sets.AttributeSetExists(
attr_name_index_pair.first));
auto const &attr_set = mesh.attribute_sets.GetAttributeSet(
attr_name_index_pair.first);
auto const &expected_attr_set = attr_name_index_pair.second;
REQUIRE(static_cast<std::size_t>(attr_set.Size()) ==
expected_attr_set.size());
bool const elements_equal = std::equal(attr_set.begin(), attr_set.end(),
expected_attr_set.begin());
REQUIRE(elements_equal);
}
std::vector<std::pair<std::string, std::vector<int>>> expected_bdr_attr_sets
=
{
{"Boundary", {1, 2, 3, 4, 5, 6, 7, 8}},
{"ENE", { 1}},
{"ESE", { 8}},
{"Eastern Boundary", {1, 8}},
{"NNE", { 2}},
{"NNW", { 3}},
{"Northern Boundary", {2, 3}},
{"SSE", { 7}},
{"SSW", { 6}},
{"Southern Boundary", {6,7}},
{"WNW", { 4}},
{"WSW", { 5}},
{"Western Boundary", {4,5}}
};
for (auto const &attr_bdr_name_index_pair: expected_bdr_attr_sets )
{
REQUIRE(mesh.bdr_attribute_sets.AttributeSetExists(
attr_bdr_name_index_pair.first));
auto const &bdr_attr_set = mesh.bdr_attribute_sets.GetAttributeSet(
attr_bdr_name_index_pair.first);
auto const &expected_bdr_attr_set = attr_bdr_name_index_pair.second;
REQUIRE(static_cast<std::size_t>(bdr_attr_set.Size()) ==
expected_bdr_attr_set.size());
bool const elements_equal = std::equal(bdr_attr_set.begin(),
bdr_attr_set.end(),
expected_bdr_attr_set.begin());
REQUIRE(elements_equal);
}
}