Compare commits

..
471 Commits
Author SHA1 Message Date
Mittal, Ketan adaf2bbec6 minor 2026-05-08 13:11:31 -07:00
Mittal, Ketan b8aa60060b 2D 2026-05-08 09:58:33 -07:00
Mittal, Ketan 38c243ab05 initial commit 2026-05-06 15:05:01 -07:00
Tzanio Kolev bdd36c8982 Merge pull request #5318 from mfem/ai-policy
AI policy
2026-05-05 07:39:53 -07:00
Tzanio Kolev a7988aa845 Merge branch 'master' into ai-policy 2026-05-04 14:30:08 -07:00
Tzanio Kolev 4ec768c82b Merge pull request #5322 from mfem/fix-changelog
Fix CHANGELOG
2026-05-04 14:28:58 -07:00
Mittal, Ketan e32ea54e00 fix changelog 2026-05-04 14:11:52 -07:00
Veselin Dobrev 630a75440f Merge pull request #5299 from mfem/batchmass3d
Add element batching capabilities to 3D MassIntegrator
2026-05-04 13:47:24 -07:00
Veselin Dobrev 3ef3c8e6b4 Merge pull request #5306 from mfem/gslib-gitlab-testing
Include gslib testing on Dane
2026-05-04 13:43:23 -07:00
Tzanio Kolev 145efc313d Merge pull request #5320 from mfem/fix-cmake-libceed-test
Fix a CMake test of libCEED
2026-05-02 12:55:41 -07:00
Veselin Dobrev 26b2aa5cea In .gitlab/scripts/baseline, use srun to run scripts since salloc
does NOT run the script in the allocation as does srun.

Revert the change in the number of build tasks in dane-baseline.yml.
2026-05-01 11:10:09 -07:00
Veselin Dobrev 476c148949 Adjust the number of build tasks in dane-baseline.yml 2026-05-01 09:13:40 -07:00
Tzanio KolevandVeselin Dobrev 8ed259be31 Update CONTRIBUTING.md
Co-authored-by: Veselin Dobrev <v-dobrev@users.noreply.github.com>
2026-04-30 11:56:57 -07:00
Tzanio Kolev 67025d49ff AI policy updates based on feedback 2026-04-30 11:56:57 -07:00
Tzanio Kolev de1dea610e AI policy updates based on feedback 2026-04-30 11:56:57 -07:00
Tzanio Kolev 9f3f5c0372 Suggested AI policy 2026-04-30 11:56:56 -07:00
Tzanio Kolev 9205efab48 Merge pull request #5319 from mfem/gslib-gnu-make-updates
GSLIB related updates to the GNU make build system
2026-04-29 15:04:10 -07:00
Veselin Dobrev 1ccc27226a Fix a CMake test of libCEED 2026-04-29 10:54:49 -07:00
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
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
Veselin Dobrev 0d3195e69b Fix issue #5314 and other tweaks.
* 'make style' now checks if all git source files are selected for formatting.
* In examples/makefile, propagate the target 'test-noclean' to subdirectories.
* In miniapps/plasma/makefile, use logic similar to examples/makefile to
  propagate targets to subdirectories.
* Other small fixes.
2026-04-28 06:32:57 -07:00
Veselin Dobrev f37a596173 Fix a build issue: in the top makefile ensure miniapps/common is built
before building miniapps/gslib.
2026-04-27 07:26:12 -07:00
Tzanio Kolev 7ff0bd3bb0 Merge branch 'master' into dc-ofstream-fix-minor 2026-04-26 14:30:17 -06:00
Andrew Ho 04dd962b6d review comments 2026-04-23 15:58:25 -07:00
Mittal, Ketan 383914db9a use MFEM's Mpi class to initialize instead of MPI_Init directly 2026-04-23 14:27:01 -07:00
Andrew HoandJohn Camier f77d238a5d Update fem/dgmassinv_kernels.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2026-04-23 10:52:43 -07:00
John Camier 84996ce32f Merge branch 'master' into batchmass3d 2026-04-23 06:26:09 -07:00
Mittal, Ketan f2b64de28f Merge branch 'master' of https://github.com/mfem/mfem into gslib-gitlab-testing 2026-04-22 12:27:50 -07:00
Mittal, Ketan 3415b0f3d4 run serial miniapps on 1 run when mfem is built with MPI 2026-04-22 12:26:45 -07:00
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
Andrew Ho 6ea799e385 Merge branch 'master' into batchmass3d 2026-04-20 09:04:25 -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
Andrew Ho f1561e47d1 Merge branch 'master' into batchmass3d 2026-04-17 10:07:18 -07:00
Mittal, Ketan abf5fedc5b include hypre with cuda on matrix 2026-04-16 21:03:11 -07:00
Mittal, Ketan d183f43c96 Merge branch 'gslib-gitlab-testing' of https://github.com/mfem/mfem into gslib-gitlab-testing 2026-04-16 12:56:36 -07:00
Mittal, Ketan a545b94ad7 enable testing on matrix as well 2026-04-16 12:56:08 -07:00
Ketan Mittal 12eefe3c41 Merge branch 'master' into gslib-gitlab-testing 2026-04-14 12:58:08 -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
Mittal, Ketan 8e33891c07 initial commit 2026-04-14 12:13:15 -07: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 7b4df2d374 Merge branch 'master' into batchmass3d 2026-04-13 09:29:35 -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 64ef39bbe6 Merge pull request #5291 from lindsayad/fix-petsc-b-allocation
Don't attempt to allocate B if b is non-empty in PetscNonlinearSolver
2026-04-12 19:03:40 -07:00
Veselin Dobrev 7985a225bb Fix nvcc warnings about static_cast<const int> 2026-04-12 18:11:16 -07:00
Andrew Ho 2d7460bde1 fixed bug in how tidz was set
128 seems to offer a slightly better balance for low and high orders
2026-04-11 10:21:10 -07:00
Andrew Ho 3c45d59813 cap CPU version to batch size 1 2026-04-10 17:23:48 -07:00
Andrew Ho 63acbeb8c0 use the same batching pattern as elsewhere, hopefully fixes bugs 2026-04-10 14:39:20 -07:00
Andrew Ho 9bf6819f7a Merge remote-tracking branch 'base/fix-umpire-dep' into batchmass3d 2026-04-10 13:53:32 -07:00
Andrew Ho bed2cc5735 implemented 3D element batching for mass integrator 2026-04-10 13:48:16 -07:00
Veselin Dobrev 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 ec39b3509c Merge pull request #5292 from mfem/saveAsOneAttrs
Propagate named attribute sets in ParMesh::GetSerialMesh
2026-04-08 11:13:00 -07:00
Veselin Dobrev 449ec725e2 Merge branch 'master' into particles-pic-dev-pr 2026-04-08 11:04:40 -07:00
Will Pazner 399d8e1e9b Merge pull request #5293 from mfem/ci-fix-brew-info
GitHub CI fix
2026-04-08 11:00:36 -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
Veselin Dobrev 10dbed9658 In GitHub CI, fix the parsing for the new formatting of 'brew info' 2026-04-07 17:23:31 -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
Will Pazner f09a062c04 Merge pull request #4800 from mfem/warn-gridfunc
Add VectorDim error checks for projecting coefficients onto a GridFunction
2026-04-07 11:13:31 -07:00
Alex Lindsay 0c97d6f375 Never allocate B 2026-04-06 20:21:41 -07: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
Veselin Dobrev aed9c8ef4a Merge pull request #5285 from mfem/uuid-fix
Fix signed char issue in Device::GetUUID
2026-04-06 15:52:32 -07:00
Veselin Dobrev e4e85e28ef Merge pull request #5289 from adam-sim-dev/fix_missing_parentheses
Fixed missing parenthesis in the comment
2026-04-06 15:43:19 -07:00
Veselin Dobrev fff973f192 Merge pull request #5246 from mfem/hughcars/simplex-quadrature-dev
Add positive-weight simplex quadrature rules for orders 0-20
2026-04-06 15:39:49 -07:00
Veselin Dobrev 775f06c43b Adjust seed values in sample runs in ex12p to ensure LOBPCG convergence in
older hypre versions.
2026-04-03 14:55:08 -07:00
schnmich 18ff1d8289 linting 2026-04-03 11:21:06 -06:00
Nuno Nobre bca03a17af Avoid unneeded overrides of ProjectDiscCoefficient 2026-04-03 18:17:43 +01:00
schnmich 5a0962c674 add attributes to GetSerialMesh 2026-04-03 11:05:03 -06: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 0d999709e6 Merge pull request #4917 from Sbozzolo/master
Improve error message for gmsh versions != 2.2
2026-04-01 20:20:15 -07: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
Tzanio Kolev c2649eb998 Merge pull request #5245 from mfem/bugfix/chapman39/bilininteg-no-mod
bilininteg: eliminate usage of modulus to avoid llvm backend bug
2026-04-01 11:35:17 -07:00
Will Pazner a1ce49fb57 Merge pull request #5290 from mfem/ci-update-action-versions-2
Update the action `actions/cache/restore` to `v5`
2026-03-31 20:36:07 -07:00
Alex Lindsay f58cfc8170 Should not allocate B if b non-empty
Otherwise there will be an error in PlaceMemory
2026-03-31 20:25:34 -07:00
Alex Lindsay 6c837d2954 Add test of PetscNonlinearSolver with non-empty RHS 2026-03-31 20:24:22 -07:00
Veselin Dobrev 5b37c3b595 Update the action actions/cache/restore to v5 2026-03-31 16:45:21 -07:00
Andrew Ho b46baa5f5e Merge branch 'master' into fix-umpire-dep 2026-03-31 15:59:31 -07:00
Will Pazner e49f9f7988 Merge pull request #5288 from mfem/ci-update-action-versions
Update some GitHub actions to new versions
2026-03-31 11:03:48 -07:00
adam-sim-dev 7c36b55628 Fixed missing parenthesis in the comment 2026-03-31 14:29:39 +08:00
Veselin Dobrev faa73ef554 Updated the github/codeql-action/* actions to the latest, v4 2026-03-30 11:50:40 -07:00
Veselin Dobrev ecb6b06aa0 Updated actions/checkout to the latest major version, v6 2026-03-30 11:44:41 -07:00
Veselin Dobrev af4649a088 Update actions/{checkout,cache} to v5
Update github/codeql-action/* to v3
2026-03-30 10:30:31 -07:00
Andrew Ho a9f58f3982 check for null 2026-03-30 09:31:37 -07:00
Andrew Ho 6de6675783 fix compiler complaints 2026-03-30 09:04:18 -07:00
Andrew Ho 085ee02a29 compile error 2026-03-30 08:56:18 -07:00
Andrew Ho 9a124335a7 redundant checks 2026-03-30 08:53:36 -07:00
Andrew Ho 91d5e490aa Merge branch 'master' into warn-gridfunc 2026-03-30 08:53:08 -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
Tzanio Kolev dd931b2584 Merge pull request #5219 from mfem/najlkin/project-bdr-coeff-rtnd
Projection of  scalar coefficients on RT grid functions
2026-03-29 12:53:11 -07:00
Tzanio Kolev 8a42ea2834 Merge pull request #5198 from mfem/najlkin/fix-ex22p-glvis
[BUG] Fixed visualization in example 22
2026-03-29 12:52:42 -07:00
Tzanio Kolev 24e5d5fc0a Merge pull request #4781 from mfem/najlkin/extrd-1d-vec
Extrusion of vector 1D grid functions
2026-03-29 12:51:54 -07:00
Tzanio Kolev 6722dd7a70 Merge pull request #5276 from mfem/bugfix/arrays-by-name-load
Adding bugfix and unit test which would have caught the bug
2026-03-29 12:51:15 -07:00
Tzanio Kolev cb862cbfa1 Merge pull request #5252 from mfem/assemble-face-integrator-fix
Added VDOFs transformation for boundary integration
2026-03-29 12:50:40 -07:00
Will Pazner f7445844ba Fix signed char issue in Device::GetUUID 2026-03-26 15:23:21 -07:00
Hugh Carson 672e2a442b Address PR feedback
- Use [IntegrationRules] test tag instead of [PositiveWeightRules]
- Remove redundant case 21: (default branch handles it via the overwrite guard)
- Remove trailing blank line
2026-03-26 12:18:59 -04:00
rzhangbq 3e1f10daea incorporating PR #5282 2026-03-24 21:51:15 -07:00
Veselin Dobrev 416536eb9d Merge pull request #4941 from mfem/globalvec_debug
GlobalVector bug fix
2026-03-24 12:04:48 -07:00
rzhangbq 35778347d0 resolve double-assigning b 2026-03-24 10:51:44 -07:00
Andrew Ho f557e348da removed comments 2026-03-23 14:02:52 -07:00
Andrew Ho 881598e5da also ensure boundary element is a scalar range type 2026-03-23 13:29:46 -07:00
Andrew Ho 564b7ab4ec fixed error message 2026-03-23 13:12:53 -07:00
Andrew HoandJan Nikl 3f2f925400 Update fem/gridfunc.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-23 13:09:37 -07:00
Jan Nikl 463e34dc7f Fixed spelling of transverse. 2026-03-23 10:33:51 -07:00
Tzanio Kolev 55e42eeefe Merge pull request #5238 from mfem/face-nbr-restr-vdim-bugfix
Fix bug in ParL2FaceRestriction with vdim > 1
2026-03-22 10:24:53 -07:00
Andrew Ho 077954d4b3 fix CMake umpire build and install 2026-03-19 13:43:48 -07:00
Stowell, Mark L. 9a456b908e Adding bugfix and unit test which would have caught the bug 2026-03-18 15:50:59 -07:00
Andrew HoandJan Nikl 616839388a Update tests/unit/fem/test_var_order.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-17 14:14:17 -07:00
Andrew HoandJan Nikl 2fda3db982 Update tests/unit/fem/test_var_order.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-03-17 14:13:57 -07:00
Andrew Ho 4823a33a6a fixed not checking the correct rangedim 2026-03-17 12:17:13 -07:00
Veselin Dobrev a96319e0be Small change in error message + formatting. 2026-03-17 12:16:46 -07:00
Andrew Ho 5f4283f512 fixed comment; vector coefficient is still spacedims 2026-03-17 11:37:02 -07:00
Andrew Ho 8735d28561 fixed GetPhysRangeDim and GetPhysCurlDim not being virtual 2026-03-17 11:33:55 -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
Andrew Ho a10c7a943b Merge branch 'master' into warn-gridfunc 2026-03-13 09:47:50 -07:00
Nuno Nobre 60ab6ab8f5 New Is(Par)SubMesh methods to determine descendance 2026-03-13 15:37:28 +00:00
Tzanio Kolev fa89c5e98c Merge pull request #4856 from mfem/phys-range-dim
Range and curl dimension in physical space
2026-03-13 07:44:40 -07:00
Tzanio Kolev 0980bda63b Merge pull request #5215 from balay/barry/update-for-petsc-v3.25-PetscCtx
Update to change in PETSc API (in v3.25) for PetscCtx and PetscCtxRt
2026-03-13 07:44:06 -07:00
Nuno Nobre 878df1fef2 Revert "Allow evals of GridFunctionCoefficient on submeshes"
This reverts commit 8baa46babd.
2026-03-13 11:44:50 +00:00
Will Pazner a1758e51e5 Merge remote-tracking branch 'origin/master' into face-nbr-restr-vdim-bugfix 2026-03-12 18:02:19 -07:00
Will Pazner ccf84aab7c Use constexpr in unit test 2026-03-12 18:01:40 -07:00
Hugh Carson 96eff4684f Remove unused private helper methods from IntegrationRule
AddTriPoints3R, AddTetPoints4b, and AddTetPoints12bc are no longer
called after the legacy simplex rules were removed.
2026-03-11 16:58:36 -04:00
Hugh Carson b6255fc825 Use exact fractions for trivial quadrature weights and coordinates
For rules where the mathematical value is an exact simple fraction
(midpoint weights, equal-weight symmetric rules), use the fraction
directly rather than the Polyquad decimal expansion. Cleaner to read
and avoids any rounding from decimal-to-double conversion.
2026-03-11 16:55:28 -04:00
Hugh Carson 18d27f6ffb Restore original function order in intrules.cpp
Move TriangleIntegrationRule before SquareIntegrationRule to match
the original file layout, reducing diff noise against master.
2026-03-11 16:38:01 -04:00
Hugh Carson 7bfb57ef17 Remove legacy simplex rules; positive-weight rules are now the default
The positive-weight rules now cover the full tabulated range for both
triangles (0-25) and tetrahedra (0-20), so the old rules with negative
weights are no longer needed. Remove the SimplexQuadrature enum,
simplex_type member, and legacy rule functions — all simplex quadrature
now uses positive-weight rules by default, with Grundmann-Moller
fallback for higher orders.
2026-03-11 15:25:29 -04:00
Hugh Carson ab394d795e Add existing order 21-25 triangle rule to positive-weight rules
The 126-point degree-25 rule already has all positive weights.
Copy it into TrianglePositiveIntegrationRule so the positive-weight
path covers orders 0-25.
2026-03-11 15:25:25 -04:00
Will Pazner 82abd48bba Merge pull request #5080 from mfem/cmake-config
CMake config.mk for CUDA and HIP
2026-03-11 12:18:25 -04:00
Andrew Ho cad9cc4c82 fixed checks 2026-03-10 20:56:14 -07:00
Andrew Ho 4dc741ca48 fix merge with master
still need to update bdr project coefficient checks
2026-03-10 17:11:17 -07:00
Andrew Ho 918eb114d3 Merge branch 'phys-range-dim' into warn-gridfunc 2026-03-10 16:42:15 -07:00
chapman39 3341acf0f7 add comments showing each modulus replacement 2026-03-10 15:10:51 -07:00
Tucker Hartland 287cb24d0a Merge branch 'master' into globalvec_debug 2026-03-10 11:35:19 -07:00
Andrew Ho 70370b6241 Merge branch 'master' into warn-gridfunc 2026-03-10 11:21:36 -07:00
Tzanio Kolev d4374a9d5f Merge branch 'master' into cmake-config 2026-03-10 11:08:23 -07:00
Tzanio Kolev dcd3a25730 Merge branch 'master' into barry/update-for-petsc-v3.25-PetscCtx 2026-03-10 11:01:46 -07:00
Tzanio Kolev 9fb2327be9 Merge branch 'master' into ir 2026-03-10 10:59:26 -07:00
Wouter Tonnon ea291fb157 Merge branch 'master' into assemble-face-integrator-fix 2026-03-10 11:02:32 +01:00
Wouter Tonnon fce4ae7bb0 extended to MixedBilinearForm 2026-03-10 11:00:51 +01:00
Alex Tyler Chapman ef44f047aa Merge branch 'master' into bugfix/chapman39/bilininteg-no-mod 2026-03-09 16:41:43 -07:00
chapman39 ae002f7369 added comment 2026-03-09 14:36:47 -07:00
Jan Nikl e4cd3f9e18 Merge branch 'master' into najlkin/project-bdr-coeff-rtnd 2026-03-05 14:48:38 -08: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
Andrew Ho 0f99528c62 Merge branch 'master' into phys-range-dim 2026-03-05 12:45:42 -08:00
Veselin Dobrev ddfd74e899 Merge pull request #5255 from mfem/catch-tests
fix clang compiler warning for __COUNTER__
2026-03-05 12:39:29 -08:00
Mark L. Stowell 0248720eeb Merge branch 'master' into phys-range-dim 2026-03-05 09:35:14 -08:00
Andrew Ho feded39641 Merge branch 'master' into catch-tests 2026-03-05 09:28:31 -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
Tzanio Kolev 09128b9a5d Merge pull request #5240 from mfem/bugfix/chapan39/use-mfem-abort-kernel-in-device
dfem integrate: use mfem abort kernel in device code
2026-03-04 09:55:23 -08:00
Tzanio Kolev 68383b462b Merge pull request #5231 from mfem/plasma-dir-dev
Plasma Miniapp Directory
2026-03-04 09:54:48 -08:00
Tzanio Kolev 24d5609585 Merge pull request #5212 from mfem/najlkin/fix-cmplx-assign
[BUG] Complex grid function copy assignment
2026-03-04 09:54:25 -08:00
Jan Nikl abdcf82d70 Added scalar unit test of ProjectBdrCoefficientNormal(). 2026-03-03 12:59:53 -08:00
Jan Nikl ad93d526b7 Added a unit test for vector ProjectBdrCoefficientNormal(). 2026-03-03 12:05:57 -08:00
Andrew Ho 670a3f9a45 comment on why TPL_LIBRARIES is reversed twice 2026-03-03 11:43:02 -08:00
Jan Nikl 87c1a5cb77 Made the ProjectBdrCoefficientNormal check non-debug. 2026-03-03 11:26:25 -08:00
Andrew Ho 7baae02d65 Merge remote-tracking branch 'base/cmake-config' into cmake-config 2026-03-02 16:31:18 -08:00
Andrew Ho 728a0f313b move cudart to MFEM_EXT_LIBS 2026-03-02 16:30:31 -08:00
Andrew Ho 1bb624e2a8 fix clang compiler warning for __COUNTER__ 2026-03-02 14:05:28 -08:00
Tzanio Kolev ee7ccd6464 Merge branch 'master' into plasma-dir-dev 2026-03-02 11:56:51 -08:00
Stowell, Mark L. a3ae5a6f01 Changing copyright date to pass CI checks 2026-03-02 09:22:29 -08:00
Andrew HoandNuno Nobre 9243d00549 Update config/cmake/modules/MfemCmakeUtilities.cmake
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2026-02-28 14:53:22 -08:00
Andrew HoandNuno Nobre 4fe3db5a5f Update config/cmake/modules/MfemCmakeUtilities.cmake
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2026-02-28 14:53:15 -08:00
Andrew Ho 55bb710cba fixed wrong dir being marked as system 2026-02-27 14:02:24 -08:00
Andrew HoandNuno Nobre 7ad6939454 Update config/cmake/modules/MfemCmakeUtilities.cmake
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2026-02-27 07:06:16 -08:00
Wouter Tonnon 89ad250940 Merge branch 'master' into assemble-face-integrator-fix 2026-02-27 14:44:45 +01:00
60cc94e5a1 Update to use PetscCtxRt from (3,25,0), and cleanup duplicate code
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
Co-authored-by: Satish Balay <balay@mcs.anl.gov>
2026-02-26 11:57:27 -06:00
Satish Balay 9122ac1839 update KSPMonitorFn usage for < (3,24,0) 2026-02-26 11:57:22 -06:00
Satish Balay 864186117d update PetscCtxDestroyFn usage for < (3,23,0) 2026-02-26 11:56:15 -06:00
Ketan Mittal 35de169fd0 Merge branch 'master' into plasma-dir-dev 2026-02-26 09:42:51 -08:00
Hugh Carson d5dec97d23 Fix memory leak 2026-02-25 11:46:21 -05:00
Hugh Carson 2d401bcb74 Add positive-weight simplex quadrature rules for orders 0-20
Triangle rules from Witherden & Vincent (2015), tet rules d=0-13
from Witherden & Vincent, tet rules d=14-20 from Chuluunbaatar et al.
(2022). All rules have strictly positive weights and interior points,
replacing the legacy rules which use negative weights at several
orders and fall back to Grundmann-Moller (negative weights, high
point counts) for tets at d>=9.
2026-02-24 20:21:49 -05:00
Andrew Ho 0a3184ab31 MFEM_EXPORT_GPU_CONFIG should export CPU config.mk when set to off 2026-02-24 11:56:38 -08:00
Wouter Tonnon 4f383f4b19 added missing face orientation 2026-02-24 20:27:14 +01:00
Mark L. Stowell a438e09caf Merge branch 'master' into plasma-dir-dev 2026-02-24 11:02:04 -08:00
chapman39 7f35ecb8f5 eliminate usage of modulus to avoid llvm backend bug 2026-02-24 10:50:52 -08:00
Alex Tyler Chapman ea03a86df2 Merge branch 'master' into bugfix/chapan39/use-mfem-abort-kernel-in-device 2026-02-24 10:32:30 -08:00
chapman39 6ef7a9e6fb 80 chars/ line 2026-02-24 10:32:18 -08:00
Alex Tyler Chapman db7dd30d32 Merge branch 'master' into bugfix/chapan39/use-mfem-abort-kernel-in-device 2026-02-23 09:33:23 -08: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
chapman39 a1fe3a19b1 dfem integrate: use mfem abort kernel in device code 2026-02-19 17:45:11 -08: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
Satish Balay 213ccd7a4e rework PetscContainerSetCtxDestroy() usage for < (3,23,0) 2026-02-18 15:51:42 -06:00
Will Pazner 8e78471fdf Add comment about the shape of FaceNbrData 2026-02-18 09:13:16 -08:00
Will Pazner c0f8501950 Add unit test for parallel L2 face restriction with vdim > 1 2026-02-18 09:13:03 -08:00
Will Pazner c31510289f Fix bug in ParL2FaceRestriction with vdim > 1
The layout of the FaceNbrData vector was not handled properly
2026-02-17 21:07:17 -08:00
Tzanio Kolev 2b14134496 Merge branch 'master' into najlkin/fix-cmplx-assign 2026-02-17 08:27:01 -08: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
Stowell, Mark L. 794a5fbfc2 Adding miniapps/plasma subdirectory to build system 2026-02-11 11:56:06 -08:00
Stowell, Mark L. 746a62f017 Adding plasma miniapp directory 2026-02-11 11:53:01 -08: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
Gabriele Bozzola 9e4d9799dc Improve error message for gmsh versions != 2.2
I am a new user of [palace](https://github.com/awslabs/palace). As I was
trying to set a simple mesh up (with gmsh), I kept getting indexing
errors that I could not decipher. I eventually
[learned](https://mfem.org/mesh-formats/) that supported version for
gmsh meshes is 2.2.

This commit catches this and adds an informative error.
2026-02-06 16:12:27 -08:00
Jan Nikl ac4e558164 Minor unification of docstrings. 2026-02-04 15:21:02 -08:00
Jan Nikl 691cd8a687 Generalized RT normal projection. 2026-02-04 15:20:03 -08:00
Jan Nikl cdc327a511 Removed unused code. 2026-02-04 13:10:45 -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
Andrew Ho 26e9057f02 revert change, updated comment to why libdl gets special treatment 2026-01-30 07:27:04 -08:00
Jan Nikl 0d2e8f93e6 Fixed vis of the initial exact solution. 2026-01-28 18:14:44 -08:00
Jan Nikl 16dfa11f27 Minor docstring correction. 2026-01-28 15:45:20 -08:00
Jan Nikl c7774e3c1c Fixed complex grid function copy assignment. 2026-01-28 15:34:55 -08: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
Andrew Ho 194f3d8140 suggestions from Veselin 2026-01-21 11:45:20 -08: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
Mark L. Stowell 3f9b44a9cd Merge branch 'master' into phys-range-dim 2026-01-21 10:38:56 -08: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
Jan Nikl 47c9ad2e34 Fixed visulization in ex22p. 2026-01-16 11:20:13 -08: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
John Camier caa973d6a0 Merge branch 'master' into cmake-config 2026-01-13 08:13:47 -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
Andrew Ho ad40704e20 Merge branch 'master' into cmake-config 2026-01-06 11:55:40 -08:00
Andrew Ho d3470c07c9 Merge branch 'master' into cmake-config 2025-12-16 12:05:27 -08:00
Paul Hilscher af834012d0 Merge branch 'master' into mfem-v13-mesh-reader-fix-issue-4625 2025-12-10 06:42:34 +09:00
Andrew Ho 06a15cb7a9 missed one old unsetting of shared_link_flag 2025-12-01 17:02:05 -08:00
Andrew Ho d19ff6c676 Merge branch 'master' into cmake-config 2025-12-01 12:33:24 -08:00
Andrew Ho d85fbc6504 review suggestions 2025-11-25 14:35:38 -08:00
Andrew Ho 29346a87b6 Merge branch 'master' into cmake-config 2025-11-25 14:31:21 -05:00
Andrew Ho 3464f7a004 Merge branch 'master' into cmake-config 2025-10-28 11:08:38 -07:00
Andrew Ho 7de48e47ad Merge branch 'master' into cmake-config 2025-10-24 09:24:50 -07:00
Andrew Ho 70814c640b fixes for hip 2025-10-20 14:01:50 -07:00
Andrew Ho e9d3ae80f7 remove debug printout 2025-10-20 13:30:20 -07:00
Andrew Ho c8efc23c12 seems to be building external laghos now 2025-10-20 13:26:04 -07:00
Andrew Ho f26eb33252 Merge remote-tracking branch 'base/cmake-gpu' into cmake-config 2025-10-20 10:25:58 -07:00
Andrew Ho 05e622f837 improving config.mk file generated by cmake to work with hip/cuda
Still need to export compiler flags
2025-10-20 08:33:17 -07:00
Tzanio Kolev de3f769f49 Merge branch 'master' into dc-ofstream-fix-minor 2025-10-16 06:51:38 -07:00
Mark L. Stowell e9f84b033f Merge branch 'master' into phys-range-dim 2025-10-15 06:49:33 -07:00
Andrew Ho ed862050b2 Merge branch 'master' into warn-gridfunc 2025-09-17 10:51:29 -07:00
Tzanio Kolev 3c6c1eb634 Merge branch 'master' into najlkin/extrd-1d-vec 2025-09-17 03:30:23 -07:00
Jan Nikl 22851a9463 Merge branch 'master' into najlkin/extrd-1d-vec 2025-09-16 15:30:43 -07:00
Jan Nikl 38df8156b9 Added documentation and checks to the extrusion classes. 2025-09-16 01:46:22 -07:00
Veselin Dobrev 542467fd6a Merge branch 'master' into globalvec_debug 2025-08-16 16:26:36 -07:00
thartland 5986542e3d VERIFY instead of ASSERT 2025-07-16 16:02:21 -07:00
Tucker Hartland 5163313285 style 2025-07-16 15:46:46 -07:00
thartland 2201f3354a adding a check to make sure that each process owns at least one entry of the HypreParVector prior to calling GlobalVector 2025-07-16 15:36:57 -07:00
Tzanio Kolev e60f43fff3 Merge branch 'master' into ex37 2025-07-01 15:02:18 -07:00
Mark L. Stowell 83fd119b95 Merge branch 'master' into phys-range-dim 2025-07-01 10:16:58 -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
Andrew Ho f5b03af9d6 Merge branch 'master' into warn-gridfunc 2025-06-16 12:20:24 -07:00
Andrew Ho 80c7823ac7 Merge branch 'master' into warn-gridfunc 2025-06-03 11:26:09 -07:00
Andrew Ho a443f003bb Merge pull request #4851 from mfem/najlkin/warn-gridfunc
Vector dimension for bdr/face elements
2025-06-02 11:45:47 -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
Mark L. Stowell 4aecb86d71 Merge branch 'master' into phys-range-dim 2025-05-19 17:51:08 -07:00
Andrew Ho 1730b05078 Merge branch 'master' into warn-gridfunc 2025-05-12 14:04:42 -07:00
Stowell, Mark L. 776a4c1815 Updating unit tests 2025-05-11 14:40:05 -07:00
Stowell, Mark L. c870d7dc1c Using new MapType entries and implementing new GetPhys*Dim methods 2025-05-11 14:39:49 -07:00
Stowell, Mark L. 8519889074 Adding new MapType entries for R2D and R1D classes 2025-05-11 14:38:50 -07:00
Jan Nikl 8a522f5e7d Fixed submesh unit test. 2025-05-07 16:26:34 -07:00
Jan Nikl fcbd105b82 Fixed ParGridFunction projection checks. 2025-05-07 15:28:54 -07:00
Jan Nikl b82dcf1387 Fixed variable order vector element unit tests. 2025-05-07 15:16:36 -07:00
Jan Nikl d3471aef59 Cosmetic change in FiniteElementSpace::GetTypicalBE(). 2025-05-07 14:35:50 -07:00
Jan Nikl 822555df0b Fixed boundary projection checks in GridFunction. 2025-05-07 14:23:33 -07:00
Jan Nikl 4626d65ac1 Added *VectorDim shortcuts to GridFunction. 2025-05-07 13:37:35 -07:00
Jan Nikl 38a80ea0e4 Added methods for typical elements and vector dimension for bdr/face. 2025-05-07 13:28:46 -07:00
Jan Nikl 590f954d6f Replaced the special case for trace spaces in GetVectorDim() by a check. 2025-05-07 11:58:01 -07:00
Jan Nikl bc5fc2b0f3 Revert "fixed ProjectBdrCoefficient vdim verify check"
This reverts commit feecd75ff3.
2025-05-07 11:55:25 -07: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
Tzanio Kolev 0f78d8aa5c Merge branch 'master' into najlkin/extrd-1d-vec 2025-04-15 13:46:39 -07:00
Andrew Ho 3f98aa1cfb Merge branch 'master' into warn-gridfunc 2025-04-14 16:15:59 -07:00
Andrew Ho feecd75ff3 fixed ProjectBdrCoefficient vdim verify check 2025-04-11 22:39:33 -07:00
Andrew Ho 248bdcc149 revert change in GridFunction::GetGradient 2025-04-11 20:49:28 -07:00
Andrew HoandVeselin Dobrev e4e354834d Update fem/gridfunc.cpp
Co-authored-by: Veselin Dobrev <v-dobrev@users.noreply.github.com>
2025-04-11 20:47:15 -07:00
Andrew Ho d64a6d6255 Change GetVectorDim so it works for trace spaces.
Re-added boundary projection vector dim checks
2025-04-11 18:58:09 -07:00
Andrew Ho 510387a605 remove checks in Bdr projections
I think something more clever needs to be done here for projecting
onto trace elements
2025-04-11 17:23:44 -07:00
Andrew Ho e99b2a8410 Switched to VectorDim(), added checks for VectorCoefficient projection 2025-04-11 16:07:31 -07:00
Andrew Ho 6608111315 Added error checks for scalar coefficient projection onto a vector GridFunction 2025-04-11 14:39:06 -07:00
Jan Nikl b7253275fc Added extrusion of vector 1D grid functions. 2025-04-01 17:49:16 -07: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
79 changed files with 3034 additions and 1764 deletions
+2 -1
View File
@@ -295,7 +295,8 @@ jobs:
export HOMEBREW_NO_INSTALL_CLEANUP=1
brew update
brew install enzyme
ENZYME_LLVM=$(brew info enzyme | sed -n 's/^Required:.*\(llvm[^ ]*\).*/\1/p')
ENZYME_LLVM=$(brew info enzyme | sed -n 's/^Required.*:.*\(llvm[^ ]*\).*/\1/p')
echo "ENZYME_LLVM=$ENZYME_LLVM"
LLVM_PREFIX=$(brew --prefix $ENZYME_LLVM)
echo "LLVM_PREFIX=$LLVM_PREFIX" >> $GITHUB_ENV
echo "OMPI_CC=$LLVM_PREFIX/bin/clang" >> $GITHUB_ENV
+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
+5
View File
@@ -85,3 +85,8 @@ opt_par_gcc_10_pumi:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 +pumi"
opt_par_gcc_10_gslib:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 +gslib"
+5
View File
@@ -63,3 +63,8 @@ opt_mpi_cuda_hypre_cuda_gcc:
extends: .mfem_job_on_matrix
variables:
SPEC: "%gcc@10.3.1 +mpi +cuda cuda_arch=90 ^hypre+cuda"
opt_mpi_cuda_gcc_gslib:
extends: .mfem_job_on_matrix
variables:
SPEC: "%gcc@10.3.1 +mpi +cuda +gslib cuda_arch=90 ^hypre+cuda"
+2 -2
View File
@@ -32,9 +32,9 @@ mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
# run
if [[ "${MACHINE_NAME}" == "dane" ]]; then
salloc --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
srun --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
elif [[ ${MACHINE_NAME} == "corona" ]]; then
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
srun --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
else
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
exit 1
+4 -8
View File
@@ -8,8 +8,10 @@
https://mfem.org
Version 4.10 (development)
==========================
Version 4.9.1 (development)
===========================
- Policy for AI-assisted contribution added to CONTRIBUTING.md
Discretization improvements
---------------------------
@@ -23,12 +25,6 @@ Discretization improvements
Tet rules (d=14-20): Chuluunbaatar et al., Comput. Math. Appl. 124:89-97,
2022.
Version 4.9.1 (development)
===========================
Discretization improvements
---------------------------
- Improved the gridfunction projection routines. Projections work for Scalar,
Vector and VectorFE, also NURBS versions. Optionally different types of
projections can be selected, default behaviour has not changed.
+8
View File
@@ -24,6 +24,14 @@ must be made under this license.
Note also that MFEM has a [Code of Conduct](CODE_OF_CONDUCT.md). By participating
in the MFEM community, you agree to abide by its rules.
## AI Policy
- Use of AI code generation in MFEM is allowed but must be disclosed, e.g. by
selecting the `AI-assisted` label on the PR.
- By submitting a PR, the author acknowledges that they have reviewed and
understand the changes they are proposing.
- PR authors are still responsible for correctness, licensing, and attribution
of all changes.
If you plan on contributing to MFEM, consider reviewing the
[issue tracker](https://github.com/mfem/mfem/issues) first to check if a thread
already exists for your desired feature or the bug you ran into. Use a pull
+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")
+1 -1
View File
@@ -215,7 +215,7 @@ if (MFEM_ENABLE_TESTING)
add_test(NAME ex1p_ceed_np=${MFEM_MPI_NP}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MFEM_MPI_NP}
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:ex1p> "-no-vis" "-d ceed-cpu" "-pa" "-a"
$<TARGET_FILE:ex1p> "-no-vis" "-d" "ceed-cpu" "-pa" "-a"
${MPIEXEC_POSTFLAGS})
endif()
endif()
+1 -1
View File
@@ -64,7 +64,7 @@ PARALLEL_NAME := Parallel AMGX example
$(MFEM_LIB_FILE):
$(error The MFEM library is not build)
clean: clean-build
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
+3 -3
View File
@@ -64,12 +64,12 @@ ex1p-test-par: ex1p
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec $(SUBDIRS_CLEAN)
clean: clean-build clean-exec
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
@rm -f refined.mesh mesh.*
@rm -f sol.*
+8 -2
View File
@@ -97,7 +97,13 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.ParseCheck();
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
// 2. Read the mesh from the mesh file.
const char *mesh_file = "../data/disc-nurbs.mesh";
@@ -122,7 +128,7 @@ int main(int argc, char *argv[])
*nodes /= scale;
// 4. Define the necessary finite element spaces on the mesh.
H1Bubble_FECollection H1fec(order, order - 1, dim);
H1_FECollection H1fec(order+1, dim);
FiniteElementSpace H1fes(&mesh, &H1fec);
L2_FECollection L2fec(order-1, dim);
+14 -2
View File
@@ -103,7 +103,19 @@ int main(int argc, char *argv[])
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.ParseCheck();
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 2. Read the mesh from the mesh file.
const char *mesh_file = "../data/disc-nurbs.mesh";
@@ -131,7 +143,7 @@ int main(int argc, char *argv[])
mesh.Clear();
// 4. Define the necessary finite element spaces on the mesh.
H1Bubble_FECollection H1fec(order, order - 1, dim);
H1_FECollection H1fec(order+1, dim);
ParFiniteElementSpace H1fes(&pmesh, &H1fec);
L2_FECollection L2fec(order-1, dim);
+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);
+1 -1
View File
@@ -76,4 +76,4 @@ clean-build:
rm -rf *.dSYM *.TVD.*breakpoints
clean-exec:
@rm -f refined.mesh sol.gf
@rm -f refined.mesh sol.gf mesh.* sol.*
+7 -2
View File
@@ -71,6 +71,7 @@ endif
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
SUBDIRS_TEST_NOCLEAN = $(addsuffix /test-noclean,$(SUBDIRS))
SUBDIRS_CLEAN = $(addsuffix /clean,$(SUBDIRS))
SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
@@ -87,8 +88,9 @@ SUBDIRS_TPRINT = $(addsuffix /test-print,$(SUBDIRS))
all: $(EXAMPLES) $(SUBDIRS_ALL)
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_CLEAN):
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) \
$(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) $(SUBDIRS_CLEAN):
$(MAKE) -C $(@D) $(@F)
$(SUBDIRS_TPRINT):
@$(MAKE) -C $(@D) $(@F)
@@ -107,6 +109,7 @@ endif
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
test: $(SUBDIRS_TEST)
test-noclean: $(SUBDIRS_TEST_NOCLEAN)
test-print: $(SUBDIRS_TPRINT)
# Testing: Parallel vs. serial runs
@@ -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
+1 -3
View File
@@ -73,7 +73,6 @@ set(SRCS
fe/fe_base.cpp
fe/fe_fixed_order.cpp
fe/fe_h1.cpp
fe/fe_h1_bubble.cpp
fe/fe_l2.cpp
fe/fe_nd.cpp
fe/fe_nurbs.cpp
@@ -134,7 +133,7 @@ set(SRCS
tmop/assemble/diag2.cpp
tmop/assemble/grad2_limit.cpp
tmop/assemble/grad2.cpp
tmop/assemble/diag3_limit.cpp
tmop/assemble/diag3_limit.cpp
tmop/assemble/diag3.cpp
tmop/assemble/grad3_limit.cpp
tmop/assemble/grad3.cpp
@@ -222,7 +221,6 @@ set(HDRS
fe/fe_base.hpp
fe/fe_fixed_order.hpp
fe/fe_h1.hpp
fe/fe_h1_bubble.hpp
fe/fe_l2.hpp
fe/fe_nd.hpp
fe/fe_nurbs.hpp
+17 -1
View File
@@ -41,9 +41,14 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
tol = tol_i;
lbound.SetSize(ncp, nb);
ubound.SetSize(ncp, nb);
lbound_t.SetSize(nb, ncp);
ubound_t.SetSize(nb, ncp);
nodes.SetSize(nb);
weights.SetSize(nb);
control_points.SetSize(ncp);
xhat.SetSize(nb);
what.SetSize(nb);
cphat.SetSize(ncp);
auto scalenodes = [](const Vector &in, const real_t a, const real_t b) -> Vector
{
@@ -90,6 +95,10 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
MFEM_ABORT("Unsupported interval points. Use [0,1].\n");
}
control_points = scalenodes(control_points, 0.0, 1.0); // rescale to [0,1]
for (int i = 0; i < ncp; i++)
{
cphat(i) = 2.0*control_points(i) - 1.0;
}
Poly_1D::Basis &basis1d(poly1d.GetBasis(nb-1, b_type));
@@ -145,6 +154,8 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
lbound(j,i) = std::max(lbound(j,i),0_r);
}
}
lbound_t(i,j) = lbound(j,i);
ubound_t(i,j) = ubound(j,i);
}
}
@@ -176,6 +187,11 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
nodes(i) = irule.IntPoint(i).x;
}
}
for (int i = 0; i < nb; i++)
{
xhat(i) = 2.0*nodes(i) - 1.0;
what(i) = 2.0*weights(i);
}
if (b_type == 2)
{
@@ -755,4 +771,4 @@ void PLBound::Print(std::ostream &outp) const
ubound.Print(outp);
}
}
}
+615 -1
View File
@@ -13,6 +13,7 @@
#define MFEM_BOUNDS
#include "../config/config.hpp"
#include "../general/forall.hpp"
#include "fespace.hpp"
namespace mfem
@@ -60,7 +61,9 @@ private:
bool proj = true; // Use linear projection to compute bounds.
real_t tol = 0.0; // offset bounds to avoid round-off errors
Vector nodes, weights, control_points;
Vector xhat, what, cphat;
DenseMatrix lbound, ubound; // ncp x nb matrices with bounds of all bases
DenseMatrix lbound_t, ubound_t; // nb x ncp transposes for device kernel
// Some auxillary storage for computing the bounds with Bernstein
DenseMatrix basisMatNodes; // Bernstein bases at equispaced nodes
DenseMatrix basisMatInt; // Bernstein bases at GLL nodes
@@ -113,7 +116,10 @@ public:
* @details This projection increases the computational cost but results in
* tighter bounds.
*/
void SetProjectionFlagForBounding(bool proj_) { proj = proj_; }
void SetProjectionFlagForBounding(bool proj_)
{
proj = proj_;
}
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
* nodal coefficients in @a coeff in 1D/2D/3D.
@@ -137,9 +143,23 @@ public:
/// Get number of control points used to compute the bounds.
int GetNControlPoints() const { return ncp; }
/// Get the underlying 1D basis type.
int GetBasisType() const { return b_type; }
/// Get 1D control point locations (lexicographic order) in [0,1].
const Vector &GetControlPoints() const { return control_points; }
/** @brief Compute element-wise bounds from a lexicographic E-vector.
*
* @details The expected layout of @a e_vec is `ND x VDIM x NE`, where
* `ND = nb^rdim`, `VDIM = fes_vdim`, and `NE` is the number of elements.
* The output layout matches GridFunction::GetElementBounds:
* `NE x active_vdim`, with the element index varying fastest.
*/
void GetElementBoundsKernel(const int rdim, const int fes_vdim,
const Vector &e_vec, Vector &lower,
Vector &upper, const int vdim = 0) const;
/** @brief Get lower and upper bounding matrix (ncp^dim x nb^dim)
*
* @details The matrices can be used to compute the bounds at control points
@@ -183,6 +203,600 @@ private:
const int cp_type_i, const real_t tol_i);
};
namespace internal
{
struct PLBoundDeviceData
{
int nb;
int ncp;
const real_t *xhat;
const real_t *what;
const real_t *cphat;
const real_t *lbound;
const real_t *ubound;
};
template<int T_NB = 0, bool T_PROJ = true>
inline void GetElementBoundsKernel1D(const PLBoundDeviceData &data,
const int fes_vdim,
const int ne,
const Vector &e_vec,
Vector &lower,
Vector &upper,
const int comp0,
const int ncomp)
{
constexpr int GENERIC_MAX_ND = 32;
constexpr int MAX_ND = T_NB ? T_NB : GENERIC_MAX_ND;
constexpr int BLOCK_X = 2*MAX_ND;
const int nd = T_NB ? T_NB : data.nb;
MFEM_VERIFY(nd <= MAX_ND,
"Device element bounds kernel supports up to 32 "
"1D degrees of freedom.");
const auto E = Reshape(e_vec.Read(), nd, fes_vdim, ne);
auto L = Reshape(lower.Write(), ne, ncomp);
auto U = Reshape(upper.Write(), ne, ncomp);
mfem::forall_2D<BLOCK_X>(ne*ncomp, BLOCK_X, 1,
[=] MFEM_HOST_DEVICE (int ec)
{
const int e = ec % ne;
const int c = ec / ne;
const int vc = comp0 + c;
const real_t *coeff = &E(0, vc, e);
const int tid = MFEM_THREAD_ID(x);
MFEM_SHARED real_t sproj[MAX_ND];
MFEM_SHARED real_t ssum0[MAX_ND];
MFEM_SHARED real_t ssum1[MAX_ND];
MFEM_SHARED real_t smin[BLOCK_X];
MFEM_SHARED real_t smax[BLOCK_X];
MFEM_SHARED real_t sa0;
MFEM_SHARED real_t sa1;
MFEM_FOREACH_THREAD(i, x, nd)
{
if constexpr (T_PROJ)
{
const real_t x = data.xhat[i];
const real_t w = data.what[i];
ssum0[i] = 0.5*coeff[i]*w;
ssum1[i] = 1.5*coeff[i]*w*x;
}
else
{
ssum0[i] = 0.0;
ssum1[i] = 0.0;
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(ii, x, 1)
{
sa0 = 0.0;
sa1 = 0.0;
for (int i = 0; i < nd; i++)
{
sa0 += ssum0[i];
sa1 += ssum1[i];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(i, x, nd)
{
if constexpr (T_PROJ)
{
const real_t x = data.xhat[i];
sproj[i] = coeff[i] - sa0 - sa1*x;
}
else
{
sproj[i] = coeff[i];
}
}
MFEM_SYNC_THREAD;
real_t lower_local = HUGE_VAL;
real_t upper_local = -HUGE_VAL;
MFEM_FOREACH_THREAD(j, x, data.ncp)
{
real_t lo = 0.0;
real_t hi = 0.0;
if constexpr (T_PROJ)
{
const real_t xcp = data.cphat[j];
lo = sa0 + sa1*xcp;
hi = lo;
}
for (int i = 0; i < nd; i++)
{
const real_t val = sproj[i];
const real_t lv = data.lbound[j + i*data.ncp]*val;
const real_t uv = data.ubound[j + i*data.ncp]*val;
lo += lv < uv ? lv : uv;
hi += lv > uv ? lv : uv;
}
lower_local = lower_local < lo ? lower_local : lo;
upper_local = upper_local > hi ? upper_local : hi;
}
smin[tid] = lower_local;
smax[tid] = upper_local;
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(ii, x, 1)
{
real_t lower_ec = smin[0];
real_t upper_ec = smax[0];
const int nthreads = MFEM_THREAD_SIZE(x);
const int nactive = data.ncp < nthreads ? data.ncp : nthreads;
for (int t = 1; t < nactive; t++)
{
lower_ec = lower_ec < smin[t] ? lower_ec : smin[t];
upper_ec = upper_ec > smax[t] ? upper_ec : smax[t];
}
L(e, c) = lower_ec;
U(e, c) = upper_ec;
}
});
}
template<int T_NB = 0, int T_NCP = 0, bool T_PROJ = true>
inline void GetElementBoundsKernel2D(const PLBoundDeviceData &data,
const int fes_vdim,
const int ne,
const Vector &e_vec,
Vector &lower,
Vector &upper,
const int comp0,
const int ncomp)
{
constexpr int DEFAULT_MAX_NB = 8;
constexpr int DEFAULT_MAX_CP = 3*DEFAULT_MAX_NB;
constexpr int MAX_NB = T_NB ? T_NB : DEFAULT_MAX_NB;
constexpr int MAX_CP = T_NCP ? T_NCP : DEFAULT_MAX_CP;
constexpr int MAX_THREADS = MAX_CP*MAX_CP;
const int nb = data.nb;
const int ncp = data.ncp;
const int nd = nb*nb;
MFEM_VERIFY(nb <= MAX_NB,
"Device 2D element bounds kernel exceeds its compile-time "
"1D degree bound.");
MFEM_VERIFY(ncp <= MAX_CP,
"Device 2D element bounds kernel exceeds its compile-time "
"control-point bound.");
MFEM_VERIFY(ncp*ncp <= MAX_THREADS,
"Device 2D element bounds kernel exceeds its compile-time "
"thread-block bound.");
const auto E = Reshape(e_vec.Read(), nd, fes_vdim, ne);
auto L = Reshape(lower.Write(), ne, ncomp);
auto U = Reshape(upper.Write(), ne, ncomp);
mfem::forall_2D<MAX_THREADS>(ne*ncomp, ncp, ncp,
[=] MFEM_HOST_DEVICE (int ec)
{
const int e = ec % ne;
const int c = ec / ne;
const int vc = comp0 + c;
const real_t *coeff = &E(0, vc, e);
const int tx = MFEM_THREAD_ID(x);
const int ty = MFEM_THREAD_ID(y);
MFEM_SHARED real_t sproj[MAX_NB*MAX_NB];
MFEM_SHARED real_t srow_min[MAX_NB*MAX_CP];
MFEM_SHARED real_t srow_max[MAX_NB*MAX_CP];
MFEM_SHARED real_t srow_a0[MAX_NB];
MFEM_SHARED real_t srow_a1[MAX_NB];
MFEM_SHARED real_t sa0[MAX_CP];
MFEM_SHARED real_t sa1[MAX_CP];
MFEM_SHARED real_t smin[MAX_THREADS];
MFEM_SHARED real_t smax[MAX_THREADS];
// Stage 1a: for each nodal row, form the per-node contributions to the
// row-wise linear fit used by the first 1D bounding solve.
MFEM_FOREACH_THREAD(jrow, y, nb)
{
const real_t *row_coeff = coeff + jrow*nb;
const int row_ncp_off = jrow*MAX_CP;
MFEM_FOREACH_THREAD(i, x, nb)
{
if constexpr (T_PROJ)
{
const real_t x = data.xhat[i];
const real_t w = data.what[i];
srow_min[row_ncp_off + i] = 0.5*row_coeff[i]*w;
srow_max[row_ncp_off + i] = 1.5*row_coeff[i]*w*x;
}
else
{
srow_min[row_ncp_off + i] = 0.0;
srow_max[row_ncp_off + i] = 0.0;
}
}
}
MFEM_SYNC_THREAD;
// Stage 1b: reduce the row-wise projection coefficients a0/a1.
if constexpr (T_PROJ)
{
MFEM_FOREACH_THREAD(jrow, y, nb)
{
const int row_ncp_off = jrow*MAX_CP;
real_t a0 = 0.0;
real_t a1 = 0.0;
MFEM_FOREACH_THREAD(ii, x, 1)
{
for (int i = 0; i < nb; i++)
{
a0 += srow_min[row_ncp_off + i];
a1 += srow_max[row_ncp_off + i];
}
srow_a0[jrow] = a0;
srow_a1[jrow] = a1;
}
}
MFEM_SYNC_THREAD;
}
// Stage 1c: subtract the row-wise linear fit once and cache the
// projected row coefficients for reuse across all x-control points.
MFEM_FOREACH_THREAD(jrow, y, nb)
{
const real_t *row_coeff = coeff + jrow*nb;
MFEM_FOREACH_THREAD(i, x, nb)
{
if constexpr (T_PROJ)
{
const real_t x = data.xhat[i];
sproj[jrow*MAX_NB + i] = row_coeff[i]
- srow_a0[jrow] - srow_a1[jrow]*x;
}
else
{
sproj[jrow*MAX_NB + i] = row_coeff[i];
}
}
}
MFEM_SYNC_THREAD;
// Stage 1d: solve the first 1D bounding problem along each nodal row and
// store bounds at every x-direction control point.
MFEM_FOREACH_THREAD(icp, x, ncp)
{
MFEM_FOREACH_THREAD(jrow, y, nb)
{
const int row_cp_off = jrow*ncp;
real_t lo = 0.0;
real_t hi = 0.0;
if constexpr (T_PROJ)
{
const real_t xcp = data.cphat[icp];
lo = srow_a0[jrow] + srow_a1[jrow]*xcp;
hi = lo;
}
for (int i = 0; i < nb; i++)
{
const real_t val = sproj[jrow*MAX_NB + i];
const real_t lv = data.lbound[icp + i*data.ncp]*val;
const real_t uv = data.ubound[icp + i*data.ncp]*val;
lo += lv < uv ? lv : uv;
hi += lv > uv ? lv : uv;
}
srow_min[row_cp_off + icp] = lo;
srow_max[row_cp_off + icp] = hi;
}
}
MFEM_SYNC_THREAD;
// Stage 2a: from the row bounds, form the per-row contributions to the
// second 1D projection solve in the y-direction.
MFEM_FOREACH_THREAD(icp, x, ncp)
{
MFEM_FOREACH_THREAD(jrow, y, nb)
{
const int row_cp_off = jrow*ncp;
if constexpr (T_PROJ)
{
const real_t x = data.xhat[jrow];
const real_t w = data.what[jrow];
const real_t t = 0.5*(srow_min[row_cp_off + icp] +
srow_max[row_cp_off + icp]);
smin[row_cp_off + icp] = 0.5*t*w;
smax[row_cp_off + icp] = 1.5*t*w*x;
}
else
{
smin[row_cp_off + icp] = 0.0;
smax[row_cp_off + icp] = 0.0;
}
}
}
MFEM_SYNC_THREAD;
// Stage 2b: reduce the y-direction projection coefficients for each
// x-control-point column.
MFEM_FOREACH_THREAD(jj, y, 1)
{
MFEM_FOREACH_THREAD(icp, x, ncp)
{
real_t a0 = 0.0;
real_t a1 = 0.0;
for (int jrow = 0; jrow < nb; jrow++)
{
a0 += smin[jrow*ncp + icp];
a1 += smax[jrow*ncp + icp];
}
sa0[icp] = a0;
sa1[icp] = a1;
}
}
MFEM_SYNC_THREAD;
// Stage 2c: subtract the y-direction linear fit from the intermediate
// row bounds so the final tensor-product bound uses the perturbation.
if constexpr (T_PROJ)
{
MFEM_FOREACH_THREAD(icp, x, ncp)
{
MFEM_FOREACH_THREAD(jrow, y, nb)
{
const int row_cp_off = jrow*ncp;
const real_t x = data.xhat[jrow];
const real_t t = sa0[icp] + sa1[icp]*x;
srow_min[row_cp_off + icp] -= t;
srow_max[row_cp_off + icp] -= t;
}
}
}
MFEM_SYNC_THREAD;
// Stage 3: each thread now owns one 2D control point (icp, kcp) and
// accumulates its final lower/upper bound from the row-bound data.
MFEM_FOREACH_THREAD(icp, x, ncp)
{
MFEM_FOREACH_THREAD(kcp, y, ncp)
{
real_t lo = 0.0;
real_t hi = 0.0;
if constexpr (T_PROJ)
{
const real_t xcp = data.cphat[kcp];
lo = sa0[icp] + sa1[icp]*xcp;
hi = lo;
}
for (int jrow = 0; jrow < nb; jrow++)
{
const real_t w0 = srow_min[jrow*ncp + icp];
const real_t w1 = srow_max[jrow*ncp + icp];
const real_t lb = data.lbound[kcp + jrow*data.ncp];
const real_t ub = data.ubound[kcp + jrow*data.ncp];
const real_t v0 = lb*w0;
const real_t v1 = ub*w0;
const real_t v2 = lb*w1;
const real_t v3 = ub*w1;
real_t vlo = v0 < v1 ? v0 : v1;
real_t vhi = v0 > v1 ? v0 : v1;
vlo = vlo < v2 ? vlo : v2;
vlo = vlo < v3 ? vlo : v3;
vhi = vhi > v2 ? vhi : v2;
vhi = vhi > v3 ? vhi : v3;
lo += vlo;
hi += vhi;
}
const int slot = kcp*ncp + icp;
smin[slot] = lo;
smax[slot] = hi;
}
}
MFEM_SYNC_THREAD;
const int lane = ty*ncp + tx;
const int nactive = ncp*ncp;
const int nthreads = MFEM_THREAD_SIZE(x)*MFEM_THREAD_SIZE(y);
// Reduce all 2D control-point bounds to one lower/upper pair per
// (element, component).
if (nthreads == 1)
{
if (tx == 0 && ty == 0)
{
real_t lower_ec = smin[0];
real_t upper_ec = smax[0];
for (int t = 1; t < nactive; t++)
{
lower_ec = lower_ec < smin[t] ? lower_ec : smin[t];
upper_ec = upper_ec > smax[t] ? upper_ec : smax[t];
}
L(e, c) = lower_ec;
U(e, c) = upper_ec;
}
}
else
{
for (int stride = (nactive + 1)/2; stride > 0;
stride = (stride + 1)/2)
{
if (lane < stride && lane + stride < nactive)
{
smin[lane] = smin[lane] < smin[lane + stride] ?
smin[lane] : smin[lane + stride];
smax[lane] = smax[lane] > smax[lane + stride] ?
smax[lane] : smax[lane + stride];
}
MFEM_SYNC_THREAD;
if (stride == 1) { break; }
}
if (lane == 0)
{
L(e, c) = smin[0];
U(e, c) = smax[0];
}
}
});
}
} // namespace internal
inline void PLBound::GetElementBoundsKernel(const int rdim, const int fes_vdim,
const Vector &e_vec,
Vector &lower, Vector &upper,
const int vdim) const
{
MFEM_VERIFY(b_type != BasisType::Positive,
"Bernstein device bounds are not implemented.");
if (rdim == 3)
{
MFEM_ABORT("Device element bounds kernel currently only supports 1D/2D.");
}
MFEM_VERIFY(rdim == 1 || rdim == 2, "Invalid element dimension.");
MFEM_VERIFY(vdim >= -1 && vdim <= fes_vdim, "Invalid vector component.");
const int nd = static_cast<int>(std::pow(nb, rdim));
const int ne = e_vec.Size()/(nd*fes_vdim);
const int ncomp = (vdim > 0) ? 1 : fes_vdim;
lower.SetSize(ne*ncomp, e_vec);
upper.SetSize(ne*ncomp, e_vec);
lower.UseDevice(true);
upper.UseDevice(true);
if (!proj)
{
MFEM_ABORT("Device element bounds kernel currently requires projection "
"enabled.");
}
const real_t *dxhat = xhat.Read();
const real_t *dwhat = what.Read();
const real_t *dcphat = cphat.Read();
const real_t *dlbound = lbound.Read();
const real_t *dubound = ubound.Read();
internal::PLBoundDeviceData data
{
nb,
ncp,
dxhat,
dwhat,
dcphat,
dlbound,
dubound
};
const int comp0 = (vdim > 0) ? (vdim - 1) : 0;
if (rdim == 1)
{
switch (nb)
{
case 2: return internal::GetElementBoundsKernel1D<2, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
case 3: return internal::GetElementBoundsKernel1D<3, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
case 4: return internal::GetElementBoundsKernel1D<4, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
case 5: return internal::GetElementBoundsKernel1D<5, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
case 6: return internal::GetElementBoundsKernel1D<6, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
case 7: return internal::GetElementBoundsKernel1D<7, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
case 8: return internal::GetElementBoundsKernel1D<8, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
case 9: return internal::GetElementBoundsKernel1D<9, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
case 10: return internal::GetElementBoundsKernel1D<10, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
default: return internal::GetElementBoundsKernel1D<0, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
}
}
#define MFEM_PLBOUND_2D_DISPATCH(NB, NCP) \
return internal::GetElementBoundsKernel2D<NB, NCP, true>(data, fes_vdim, ne, \
e_vec, lower, upper, \
comp0, ncomp)
switch (nb)
{
case 2:
switch (ncp)
{
case 4: MFEM_PLBOUND_2D_DISPATCH(2, 4);
case 6: MFEM_PLBOUND_2D_DISPATCH(2, 6);
case 8: MFEM_PLBOUND_2D_DISPATCH(2, 8);
}
break;
case 3:
switch (ncp)
{
case 6: MFEM_PLBOUND_2D_DISPATCH(3, 6);
case 9: MFEM_PLBOUND_2D_DISPATCH(3, 9);
case 12: MFEM_PLBOUND_2D_DISPATCH(3, 12);
}
break;
case 4:
switch (ncp)
{
case 8: MFEM_PLBOUND_2D_DISPATCH(4, 8);
case 12: MFEM_PLBOUND_2D_DISPATCH(4, 12);
case 16: MFEM_PLBOUND_2D_DISPATCH(4, 16);
}
break;
case 5:
switch (ncp)
{
case 10: MFEM_PLBOUND_2D_DISPATCH(5, 10);
case 15: MFEM_PLBOUND_2D_DISPATCH(5, 15);
case 20: MFEM_PLBOUND_2D_DISPATCH(5, 20);
}
break;
case 6:
switch (ncp)
{
case 12: MFEM_PLBOUND_2D_DISPATCH(6, 12);
case 18: MFEM_PLBOUND_2D_DISPATCH(6, 18);
case 24: MFEM_PLBOUND_2D_DISPATCH(6, 24);
}
break;
case 7:
switch (ncp)
{
case 14: MFEM_PLBOUND_2D_DISPATCH(7, 14);
case 21: MFEM_PLBOUND_2D_DISPATCH(7, 21);
case 28: MFEM_PLBOUND_2D_DISPATCH(7, 28);
}
break;
case 8:
switch (ncp)
{
case 16: MFEM_PLBOUND_2D_DISPATCH(8, 16);
case 24: MFEM_PLBOUND_2D_DISPATCH(8, 24);
case 32: MFEM_PLBOUND_2D_DISPATCH(8, 32);
}
break;
}
#undef MFEM_PLBOUND_2D_DISPATCH
return internal::GetElementBoundsKernel2D<0, 0, true>(data, fes_vdim, ne,
e_vec, lower, upper,
comp0, ncomp);
}
} // namespace mfem
#endif // MFEM_BOUNDS
+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";
+1 -1
View File
@@ -57,7 +57,7 @@ void DGMassApply(const int e,
}
else if (DIM == 3)
{
SmemPAMassApply3D_Element<TD1D,TQ1D,ACCUM>(e, NE, B, pa_data, x, y);
SmemPAMassApply3D_Element<TD1D,TQ1D,NBZ,ACCUM>(e, NE, B, pa_data, x, y);
}
else
{
+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
-1
View File
@@ -20,7 +20,6 @@
#include "fe/fe_base.hpp"
#include "fe/fe_fixed_order.hpp"
#include "fe/fe_h1.hpp"
#include "fe/fe_h1_bubble.hpp"
#include "fe/fe_nd.hpp"
#include "fe/fe_rt.hpp"
#include "fe/fe_l2.hpp"
+3 -3
View File
@@ -349,7 +349,7 @@ public:
vector-valued finite elements, which is also the width of the
DenseMatrix argument in
CalcPhysVShape(ElementTransformation &Trans, DenseMatrix &shape). */
int GetPhysRangeDim(int /* space_dim */) const { return vdim; }
virtual int GetPhysRangeDim(int /* space_dim */) const { return vdim; }
/** Returns the dimension of the curl for vector-valued finite elements,
which is also the width of the DenseMatrix argument in
@@ -360,7 +360,7 @@ public:
finite elements, which is also the width of the DenseMatrix argument in
CalcPhysCurlShape(ElementTransformation &Trans, DenseMatrix &curl_shape).
*/
int GetPhysCurlDim(int /* space_dim */) const { return cdim; }
virtual int GetPhysCurlDim(int /* space_dim */) const { return cdim; }
/// Returns the Geometry::Type of the reference element.
Geometry::Type GetGeomType() const { return geom_type; }
@@ -1017,7 +1017,7 @@ public:
VectorFiniteElement(int D, Geometry::Type G, int Do, int O, int M,
int F = FunctionSpace::Pk);
int GetPhysRangeDim(int space_dim) const { return space_dim; }
int GetPhysRangeDim(int space_dim) const override { return space_dim; }
};
/// @brief Class for computing 1D special polynomials and their associated basis
-973
View File
@@ -1,973 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
// H1 Finite Element classes
#include "fe_h1_bubble.hpp"
namespace mfem
{
using namespace std;
H1Bubble_TriangleElement::H1Bubble_TriangleElement(int p, int q, int btype)
: NodalFiniteElement(2, Geometry::TRIANGLE, 3*p + ((q+1)*(q+2))/2,
max(p, 3 + q), FunctionSpace::Pk),
base_order(p), bubble_order(q)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
const real_t *cp2 = poly1d.ClosedPoints(
q + 3, VerifyNodal(VerifyClosed(btype)));
const int n1d = max(p + 1, q + 1);
const int npq = ((p+1)*(p+2))/2 + ((q+1)*(q+2))/2;
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(n1d);
shape_y.SetSize(n1d);
shape_l.SetSize(n1d);
dshape_x.SetSize(n1d);
dshape_y.SetSize(n1d);
dshape_l.SetSize(n1d);
u.SetSize(npq);
du.SetSize(npq, dim);
#endif
// vertices
Nodes.IntPoint(0).Set2(cp[0], cp[0]);
Nodes.IntPoint(1).Set2(cp[p], cp[0]);
Nodes.IntPoint(2).Set2(cp[0], cp[p]);
// edges
int o = 3;
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[i], cp[0]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[p-i], cp[i]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[0], cp[p-i]);
}
// Interior P_{q+3} nodes
for (int j = 1; j < q + 3; j++)
{
for (int i = 1; i + j < q + 3; i++)
{
const real_t w = cp2[i] + cp2[j] + cp2[q+3-i-j];
Nodes.IntPoint(o++).Set2(cp2[i]/w, cp2[j]/w);
}
}
#ifdef MFEM_THREAD_SAFE
Vector shape_x(n1d), shape_y(n1d), shape_l(n1d);
#endif
DenseMatrix Tt(dof, npq);
for (int k = 0; k < dof; ++k)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, 1. - ip.x - ip.y, shape_l);
o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i + j <= p; i++)
{
Tt(k, o++) = shape_x[i]*shape_y[j]*shape_l[p-i-j];
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, 1. - ip.x - ip.y, shape_l);
const real_t b_T = ip.x * ip.y * (1 - ip.x - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i + j <= q; i++)
{
Tt(k, o++) = b_T*shape_x[i]*shape_y[j]*shape_l[q-i-j];
}
}
}
// Compute left inverse of T (given Tt = T^T).
DenseMatrix TtT(dof, dof);
MultAAt(Tt, TtT);
DenseMatrixInverse TtT_inv(TtT);
T_pinv.SetSize(dof, dof);
TtT_inv.Mult(Tt, T_pinv);
}
void H1Bubble_TriangleElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = ((p+1)*(p+2))/2 + ((q+1)*(q+2))/2;
Vector shape_x(n1d), shape_y(n1d), shape_l(n1d), u(npq);
#endif
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, 1. - ip.x - ip.y, shape_l);
int o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i + j <= p; i++)
{
u(o++) = shape_x[i]*shape_y[j]*shape_l[p-i-j];
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, 1. - ip.x - ip.y, shape_l);
const real_t b_T = ip.x * ip.y * (1 - ip.x - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i + j <= q; i++)
{
u(o++) = b_T*shape_x[i]*shape_y[j]*shape_l[q-i-j];
}
}
T_pinv.Mult(u, shape);
}
void H1Bubble_TriangleElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = ((p+1)*(p+2))/2 + ((q+1)*(q+2))/2;
Vector shape_x(n1d), shape_y(n1d), shape_l(n1d);
Vector dshape_x(n1d), dshape_y(n1d), dshape_l(n1d);
DenseMatrix du(npq, dim);
#endif
const real_t lambda = 1.0 - ip.x - ip.y;
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(p, lambda, shape_l, dshape_l);
int o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i + j <= p; i++)
{
int k = p - i - j;
du(o,0) = (dshape_x[i]*shape_l[k] - shape_x[i]*dshape_l[k])*shape_y[j];
du(o,1) = (dshape_y[j]* shape_l[k] - shape_y[j]*dshape_l[k])*shape_x[i];
o++;
}
}
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(q, lambda, shape_l, dshape_l);
const real_t b_T = ip.x * ip.y * lambda;
const real_t dxb_T = ip.y * (lambda - ip.x);
const real_t dyb_T = ip.x * (lambda - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i + j <= q; i++)
{
int k = q - i - j;
du(o,0) = shape_y[j]*(dxb_T*shape_x[i]*shape_l[k]
+ b_T*dshape_x[i]*shape_l[k]
- b_T*shape_x[i]*dshape_l[k]);
du(o,1) = shape_x[i]*(dyb_T*shape_y[j]*shape_l[k]
+ b_T*dshape_y[j]*shape_l[k]
- b_T*shape_y[j]*dshape_l[k]);
o++;
}
}
Mult(T_pinv, du, dshape);
}
H1Bubble_QuadrilateralElement::H1Bubble_QuadrilateralElement(
int p, int q, int btype)
: NodalFiniteElement(2, Geometry::SQUARE, 4*p + (q+1)*(q+1),
max(p, 2 + q), FunctionSpace::Qk),
base_order(p), bubble_order(q)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
const real_t *cp2 = poly1d.ClosedPoints(
q + 2, VerifyNodal(VerifyClosed(btype)));
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1) + (q+1)*(q+1);
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(n1d);
shape_y.SetSize(n1d);
dshape_x.SetSize(n1d);
dshape_y.SetSize(n1d);
u.SetSize(npq);
du.SetSize(npq, dim);
#endif
// vertices
Nodes.IntPoint(0).Set2(cp[0], cp[0]);
Nodes.IntPoint(1).Set2(cp[p], cp[0]);
Nodes.IntPoint(2).Set2(cp[p], cp[p]);
Nodes.IntPoint(3).Set2(cp[0], cp[p]);
// edges
int o = 4;
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[i], cp[0]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[p], cp[i]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[p-i], cp[p]);
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set2(cp[0], cp[p-i]);
}
// interior P_{q+2} nodes
for (int j = 1; j < q+2; j++)
{
for (int i = 1; i < q+2; i++)
{
Nodes.IntPoint(o++).Set2(cp2[i], cp2[j]);
}
}
#ifdef MFEM_THREAD_SAFE
Vector shape_x(n1d), shape_y(n1d);
#endif
DenseMatrix Tt(dof, npq);
for (int k = 0; k < dof; ++k)
{
const IntegrationPoint &ip = Nodes.IntPoint(k);
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
Tt(k, o++) = shape_x[i]*shape_y[j];
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
Tt(k, o++) = b_T*shape_x[i]*shape_y[j];
}
}
}
// Compute left inverse of T (given Tt = T^T).
DenseMatrix TtT(dof, dof);
MultAAt(Tt, TtT);
DenseMatrixInverse TtT_inv(TtT);
T_pinv.SetSize(dof, dof);
TtT_inv.Mult(Tt, T_pinv);
}
void H1Bubble_QuadrilateralElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1) + (q+1)*(q+1);
Vector shape_x(n1d), shape_y(n1d), u(npq);
#endif
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
int o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
u(o++) = shape_x[i]*shape_y[j];
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
u(o++) = b_T*shape_x[i]*shape_y[j];
}
}
T_pinv.Mult(u, shape);
}
void H1Bubble_QuadrilateralElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1) + (q+1)*(q+1);
Vector shape_x(n1d), shape_y(n1d), dshape_x(n1d), dshape_y(n1d);
DenseMatrix du(npq, dim);
#endif
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
int o = 0;
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
du(o,0) = dshape_x[i]*shape_y[j];
du(o,1) = shape_x[i]*dshape_y[j];
o += 1;
}
}
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y);
const real_t dxb_T = (1.0 - 2*ip.x)*ip.y*(1.0 - ip.y);
const real_t dyb_T = ip.x*(1.0 - ip.x)*(1.0 - 2*ip.y);
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
du(o,0) = (dxb_T*shape_x[i] + b_T*dshape_x[i])*shape_y[j];
du(o,1) = (dyb_T*shape_y[j] + b_T*dshape_y[j])*shape_x[i];
o += 1;
}
}
Mult(T_pinv, du, dshape);
}
H1Bubble_TetrahedronElement::H1Bubble_TetrahedronElement(
int p, int q, int btype)
: NodalFiniteElement(3, Geometry::TETRAHEDRON,
2*(p*p + 1) + ((q+1)*(q+2)*(q+3))/6,
max(p, 4 + q), FunctionSpace::Pk),
base_order(p), bubble_order(q)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
const real_t *cp2 = poly1d.ClosedPoints(
q + 4, VerifyNodal(VerifyClosed(btype)));
const int n1d = max(p+1, q+1);
const int npq = ((p+1)*(p+2)*(p+3))/6 + ((q+1)*(q+2)*(q+3))/6;
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(n1d);
shape_y.SetSize(n1d);
shape_z.SetSize(n1d);
shape_l.SetSize(n1d);
dshape_x.SetSize(n1d);
dshape_y.SetSize(n1d);
dshape_z.SetSize(n1d);
dshape_l.SetSize(n1d);
u.SetSize(npq);
du.SetSize(npq, dim);
#else
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), shape_l(n1d);
#endif
// vertices
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
Nodes.IntPoint(2).Set3(cp[0], cp[p], cp[0]);
Nodes.IntPoint(3).Set3(cp[0], cp[0], cp[p]);
// edges (see Tetrahedron::edges in mesh/tetrahedron.cpp)
int o = 4;
for (int i = 1; i < p; i++) // (0,1)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
}
for (int i = 1; i < p; i++) // (0,2)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (0,3)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (1,2)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (1,3)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (2,3)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
}
// faces (see Mesh::GenerateFaces in mesh/mesh.cpp)
for (int j = 1; j < p; j++)
{
for (int i = 1; i + j < p; i++) // (1,2,3)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[p-i-j]/w, cp[i]/w, cp[j]/w);
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i + j < p; i++) // (0,3,2)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[0], cp[j]/w, cp[i]/w);
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i + j < p; i++) // (0,1,3)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i + j < p; i++) // (0,2,1)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[j]/w, cp[i]/w, cp[0]);
}
}
// Interior P_{q+4} nodes
for (int k = 1; k < q + 4; k++)
{
for (int j = 1; j + k < q + 4; j++)
{
for (int i = 1; i + j + k < q + 4; i++)
{
real_t w = cp2[i] + cp2[j] + cp2[k] + cp2[q+4-i-j-k];
Nodes.IntPoint(o++).Set3(cp2[i]/w, cp2[j]/w, cp2[k]/w);
}
}
}
DenseMatrix Tt(dof, npq);
for (int m = 0; m < dof; ++m)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, ip.z, shape_z);
poly1d.CalcBasis(p, 1. - ip.x - ip.y - ip.z, shape_l);
o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j + k <= p; j++)
{
for (int i = 0; i + j + k <= p; i++)
{
Tt(m, o++) = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k];
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, ip.z, shape_z);
poly1d.CalcBasis(q, 1. - ip.x - ip.y - ip.z, shape_l);
const real_t b_T = ip.x * ip.y * ip.z * (1 - ip.x - ip.y - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j + k <= q; j++)
{
for (int i = 0; i + j + k <= q; i++)
{
Tt(m, o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k]*shape_l[q-i-j-k];
}
}
}
}
// Compute left inverse of T (given Tt = T^T).
DenseMatrix TtT(dof, dof);
MultAAt(Tt, TtT);
DenseMatrixInverse TtT_inv(TtT);
T_pinv.SetSize(dof, dof);
TtT_inv.Mult(Tt, T_pinv);
}
void H1Bubble_TetrahedronElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = ((p+1)*(p+2)*(p+3))/6 + ((q+1)*(q+2)*(q+3))/6;
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), shape_l(n1d), u(npq);
#endif
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, ip.z, shape_z);
poly1d.CalcBasis(p, 1. - ip.x - ip.y - ip.z, shape_l);
int o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j + k <= p; j++)
{
for (int i = 0; i + j + k <= p; i++)
{
u[o++] = shape_x[i]*shape_y[j]*shape_z[k]*shape_l[p-i-j-k];
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, ip.z, shape_z);
poly1d.CalcBasis(q, 1. - ip.x - ip.y - ip.z, shape_l);
const real_t b_T = ip.x * ip.y * ip.z * (1 - ip.x - ip.y - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j + k <= q; j++)
{
for (int i = 0; i + j + k <= q; i++)
{
u(o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k]*shape_l[q-i-j-k];
}
}
}
T_pinv.Mult(u, shape);
}
void H1Bubble_TetrahedronElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p+1, q+1);
const int npq = ((p+1)*(p+2)*(p+3))/6 + ((q+1)*(q+2)*(q+3))/6;
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), shape_l(n1d);
Vector dshape_x(n1d), dshape_y(n1d), dshape_z(n1d), dshape_l(n1d);
DenseMatrix du(npq, dim);
#endif
const real_t lambda = 1.0 - ip.x - ip.y - ip.z;
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(p, ip.z, shape_z, dshape_z);
poly1d.CalcBasis(p, lambda, shape_l, dshape_l);
int o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j + k <= p; j++)
{
for (int i = 0; i + j + k <= p; i++)
{
int l = p - i - j - k;
du(o,0) = (dshape_x[i]*shape_l[l] - shape_x[i]*dshape_l[l])
*shape_y[j]*shape_z[k];
du(o,1) = (dshape_y[j]*shape_l[l] - shape_y[j]*dshape_l[l])
*shape_x[i]*shape_z[k];
du(o,2) = (dshape_z[k]*shape_l[l] - shape_z[k]*dshape_l[l])
*shape_x[i]*shape_y[j];
o++;
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(q, ip.z, shape_z, dshape_z);
poly1d.CalcBasis(q, lambda, shape_l, dshape_l);
const real_t b_T = ip.x * ip.y * ip.z * (1 - ip.x - ip.y - ip.z);
const real_t dxb_T = ip.y * ip.z * (lambda - ip.x);
const real_t dyb_T = ip.x * ip.z * (lambda - ip.y);
const real_t dzb_T = ip.x * ip.y * (lambda - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j + k <= q; j++)
{
for (int i = 0; i + j + k <= q; i++)
{
int l = q - i - j - k;
du(o,0) = shape_y[j]*shape_z[k]*(dxb_T*shape_x[i]*shape_l[l]
+ b_T*dshape_x[i]*shape_l[l]
- b_T*shape_x[i]*dshape_l[l]);
du(o,1) = shape_x[i]*shape_z[k]*(dyb_T*shape_y[j]*shape_l[l]
+ b_T*dshape_y[j]*shape_l[l]
- b_T*shape_y[j]*dshape_l[l]);
du(o,2) = shape_x[i]*shape_y[j]*(dzb_T*shape_z[k]*shape_l[l]
+ b_T*dshape_z[k]*shape_l[l]
- b_T*shape_z[k]*dshape_l[l]);
o++;
}
}
}
Mult(T_pinv, du, dshape);
}
H1Bubble_HexahedronElement::H1Bubble_HexahedronElement(
int p, int q, int btype)
: NodalFiniteElement(3, Geometry::CUBE, (2 + 6*p*p) + (q+1)*(q+1)*(q+1),
max(p, 2 + q), FunctionSpace::Qk),
base_order(p), bubble_order(q)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
const real_t *cp2 = poly1d.ClosedPoints(
q + 2, VerifyNodal(VerifyClosed(btype)));
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1)*(p+1) + (q+1)*(q+1)*(q+1);
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(n1d);
shape_y.SetSize(n1d);
shape_z.SetSize(n1d);
dshape_x.SetSize(n1d);
dshape_y.SetSize(n1d);
dshape_z.SetSize(n1d);
u.SetSize(npq);
du.SetSize(npq, dim);
#endif
// vertices
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
Nodes.IntPoint(2).Set3(cp[p], cp[p], cp[0]);
Nodes.IntPoint(3).Set3(cp[0], cp[p], cp[0]);
Nodes.IntPoint(4).Set3(cp[0], cp[0], cp[p]);
Nodes.IntPoint(5).Set3(cp[p], cp[0], cp[p]);
Nodes.IntPoint(6).Set3(cp[p], cp[p], cp[p]);
Nodes.IntPoint(7).Set3(cp[0], cp[p], cp[p]);
int o = 8;
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]); // (0,1)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[0]); // (1,2)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[0]); // (3,2)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]); // (0,3)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[p]); // (4,5)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[p]); // (5,6)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[p]); // (7,6)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[p]); // (4,7)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]); // (0,4)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[0], cp[i]); // (1,5)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[p], cp[i]); // (2,6)
}
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p], cp[i]); // (3,7)
}
// faces
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p-j], cp[0]); // (3,2,1,0)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[j]); // (0,1,5,4)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[j]); // (1,2,6,5)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[p], cp[j]); // (2,3,7,6)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[j]); // (3,0,4,7)
}
}
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[j], cp[p]); // (4,5,6,7)
}
}
// interior P_{q+2} nodes
for (int k = 1; k < q+2; k++)
{
for (int j = 1; j < q+2; j++)
{
for (int i = 1; i < q+2; i++)
{
Nodes.IntPoint(o++).Set3(cp2[i], cp2[j], cp2[k]);
}
}
}
#ifdef MFEM_THREAD_SAFE
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d);
#endif
DenseMatrix Tt(dof, npq);
for (int m = 0; m < dof; ++m)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, ip.z, shape_z);
o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
Tt(m, o++) = shape_x[i]*shape_y[j]*shape_z[k];
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, ip.z, shape_z);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
Tt(m, o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k];
}
}
}
}
// Compute left inverse of T (given Tt = T^T).
DenseMatrix TtT(dof, dof);
MultAAt(Tt, TtT);
DenseMatrixInverse TtT_inv(TtT);
T_pinv.SetSize(dof, dof);
TtT_inv.Mult(Tt, T_pinv);
}
void H1Bubble_HexahedronElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1)*(p+1) + (q+1)*(q+1)*(q+1);
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), u(npq);
#endif
poly1d.CalcBasis(p, ip.x, shape_x);
poly1d.CalcBasis(p, ip.y, shape_y);
poly1d.CalcBasis(p, ip.z, shape_z);
int o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
u(o++) = shape_x[i]*shape_y[j]*shape_z[k];
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x);
poly1d.CalcBasis(q, ip.y, shape_y);
poly1d.CalcBasis(q, ip.z, shape_z);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
u(o++) = b_T*shape_x[i]*shape_y[j]*shape_z[k];
}
}
}
T_pinv.Mult(u, shape);
}
void H1Bubble_HexahedronElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = base_order;
const int q = bubble_order;
#ifdef MFEM_THREAD_SAFE
const int n1d = max(p + 1, q + 1);
const int npq = (p+1)*(p+1)*(p+1) + (q+1)*(q+1)*(q+1);
Vector shape_x(n1d), shape_y(n1d), shape_z(n1d), dshape_x(n1d),
dshape_y(n1d), dshape_z(n1d);
DenseMatrix du(npq, dim);
#endif
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(p, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(p, ip.z, shape_z, dshape_z);
int o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++)
{
du(o,0) = dshape_x[i]*shape_y[j]*shape_z[k];
du(o,1) = shape_x[i]*dshape_y[j]*shape_z[k];
du(o,2) = shape_x[i]*shape_y[j]*dshape_z[k];
o += 1;
}
}
}
poly1d.CalcBasis(q, ip.x, shape_x, dshape_x);
poly1d.CalcBasis(q, ip.y, shape_y, dshape_y);
poly1d.CalcBasis(q, ip.z, shape_z, dshape_z);
const real_t b_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
const real_t dxb_T = (1.0 - 2*ip.x)*ip.y*(1.0 - ip.y)*ip.z*(1.0 - ip.z);
const real_t dyb_T = ip.x*(1.0 - ip.x)*(1.0 - 2*ip.y)*ip.z*(1.0 - ip.z);
const real_t dzb_T = ip.x*(1.0 - ip.x)*ip.y*(1.0 - ip.y)*(1.0 - 2*ip.z);
for (int k = 0; k <= q; k++)
{
for (int j = 0; j <= q; j++)
{
for (int i = 0; i <= q; i++)
{
du(o,0) = (dxb_T*shape_x[i] + b_T*dshape_x[i])*shape_y[j]*shape_z[k];
du(o,1) = (dyb_T*shape_y[j] + b_T*dshape_y[j])*shape_x[i]*shape_z[k];
du(o,2) = (dzb_T*shape_z[k] + b_T*dshape_z[k])*shape_x[i]*shape_y[j];
o += 1;
}
}
}
Mult(T_pinv, du, dshape);
}
}
-109
View File
@@ -1,109 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_FE_H1_BUBBLE
#define MFEM_FE_H1_BUBBLE
#include "fe_base.hpp"
namespace mfem
{
/// Arbitrary order H1 plus bubble elements in 2D on a triangle
class H1Bubble_TriangleElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_l, dshape_x, dshape_y, dshape_l, u;
mutable DenseMatrix du;
#endif
int base_order;
int bubble_order;
DenseMatrix T_pinv;
public:
/// @brief Construct the triangular bubble element with degree-p polynomials,
/// enriched with cubic bubble times degree q polynomial.
H1Bubble_TriangleElement(int p, int q, int btype = BasisType::GaussLobatto);
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
};
/// Arbitrary order H1 plus bubble elements in 2D on a quadrilateral
class H1Bubble_QuadrilateralElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, dshape_x, dshape_y, u;
mutable DenseMatrix du;
#endif
int base_order;
int bubble_order;
DenseMatrix T_pinv;
public:
/// @brief Construct the quadrilateral bubble element with degree-p
/// polynomials, enriched with biquadratic bubble times degree q polynomial.
H1Bubble_QuadrilateralElement(
int p, int q, int btype = BasisType::GaussLobatto);
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
};
/// Arbitrary order H1 plus bubble elements in 3D on a tetrahedron
class H1Bubble_TetrahedronElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z, shape_l;
mutable Vector dshape_x, dshape_y, dshape_z, dshape_l, u;
mutable DenseMatrix du;
#endif
int base_order;
int bubble_order;
DenseMatrix T_pinv;
public:
/// @brief Construct the tetrahedral bubble element with degree-p
/// polynomials, enriched with quartic bubble times degree q polynomial.
H1Bubble_TetrahedronElement(int p, int q, int btype = BasisType::GaussLobatto);
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
};
/// Arbitrary order H1 plus bubble elements in 3D on a hexahedron
class H1Bubble_HexahedronElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z;
mutable Vector dshape_x, dshape_y, dshape_z, u;
mutable DenseMatrix du;
#endif
int base_order;
int bubble_order;
DenseMatrix T_pinv;
public:
/// @brief Construct the hexahedral bubble element with degree-p polynomials,
/// enriched with triquadratic bubble times degree q polynomial.
H1Bubble_HexahedronElement(int p, int q, int btype = BasisType::GaussLobatto);
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
};
} // namespace mfem
#endif
+4 -4
View File
@@ -663,8 +663,8 @@ public:
const int cb_type = BasisType::GaussLobatto,
const int ob_type = BasisType::GaussLegendre);
int GetPhysRangeDim(int space_dim) const { return 2; }
int GetPhysCurlDim(int space_dim) const { return 1; }
int GetPhysRangeDim(int space_dim) const override { return 2; }
int GetPhysCurlDim(int space_dim) const override { return 1; }
void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const override;
@@ -708,8 +708,8 @@ private:
DenseMatrix &I) const;
public:
int GetPhysRangeDim(int space_dim) const { return 3; }
int GetPhysCurlDim(int space_dim) const { return 3; }
int GetPhysRangeDim(int space_dim) const override { return 3; }
int GetPhysCurlDim(int space_dim) const override { return 3; }
using FiniteElement::CalcVShape;
using FiniteElement::CalcPhysCurlShape;
+4 -4
View File
@@ -510,8 +510,8 @@ public:
RT_R2D_SegmentElement(const int p,
const int ob_type = BasisType::GaussLegendre);
int GetPhysRangeDim(int space_dim) const { return 2; }
int GetPhysCurlDim(int space_dim) const { return 0; }
int GetPhysRangeDim(int space_dim) const override { return 2; }
int GetPhysCurlDim(int space_dim) const override { return 0; }
void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const override;
@@ -550,8 +550,8 @@ private:
DenseMatrix &I) const;
public:
int GetPhysRangeDim(int space_dim) const { return 3; }
int GetPhysCurlDim(int space_dim) const { return 0; }
int GetPhysRangeDim(int space_dim) const override { return 3; }
int GetPhysCurlDim(int space_dim) const override { return 0; }
using FiniteElement::CalcVShape;
-177
View File
@@ -243,21 +243,11 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
{
fec = new H1Ser_FECollection(atoi(name + 10), atoi(name + 6));
}
else if (!strncmp(name, "H1Bubble_", 9))
{
fec = new H1Bubble_FECollection(atoi(name + 13), atoi(name + 16),
atoi(name + 9));
}
else if (!strncmp(name, "H1@", 3))
{
fec = new H1_FECollection(atoi(name + 9), atoi(name + 5),
BasisType::GetType(name[3]));
}
else if (!strncmp(name, "H1Bubble@", 9))
{
fec = new H1Bubble_FECollection(atoi(name + 15), atoi(name + 18),
atoi(name + 11), BasisType::GetType(name[9]));
}
else if (!strncmp(name, "L2_T", 4))
fec = new L2_FECollection(atoi(name + 10), atoi(name + 6),
atoi(name + 4));
@@ -2132,173 +2122,6 @@ H1_FECollection::~H1_FECollection()
}
}
static int GetBubbleSpaceOrder(int p, int q, int dim)
{
switch (dim)
{
case 0: return 0;
case 1: return std::max(p, q + 2);
case 2: return std::max(p, q + 3);
case 3: return std::max(p, q + 4);
}
MFEM_ABORT("Unsupported dimension.");
}
H1Bubble_FECollection::H1Bubble_FECollection(const int p, const int q,
const int dim, const int btype)
: FiniteElementCollection(GetBubbleSpaceOrder(p, q, dim)),
dim(dim),
b_type(BasisType::Check(btype)),
h1_order(p),
bubble_order(q)
{
MFEM_VERIFY(p >= 1, "H1Bubble_FECollection requires order >= 1.");
MFEM_VERIFY(dim >= 0 && dim <= 3, "Unsupported dimension.");
switch (btype)
{
case BasisType::GaussLobatto:
{
snprintf(fec_name, 32, "H1Bubble_%dD_P%d_P%d", dim, p, q);
break;
}
default:
{
const int pt_type = BasisType::GetQuadrature1D(btype);
MFEM_VERIFY(Quadrature1D::CheckClosed(pt_type) != Quadrature1D::Invalid,
"unsupported BasisType: " << BasisType::Name(btype));
snprintf(fec_name, 32, "H1Bubble@%c_%dD_P%d_P%d",
(int)BasisType::GetChar(btype), dim, p, q);
}
}
dofs[Geometry::POINT] = 1;
elements[Geometry::POINT] = make_unique<PointFiniteElement>();
if (dim >= 1)
{
dofs[Geometry::SEGMENT] = p - 1;
elements[Geometry::SEGMENT] = make_unique<H1_SegmentElement>(p, btype);
}
if (dim == 2)
{
dofs[Geometry::TRIANGLE] = ((q+1)*(q+2))/2;
dofs[Geometry::SQUARE] = (q+1)*(q+1);
elements[Geometry::TRIANGLE] =
make_unique<H1Bubble_TriangleElement>(p, q, btype);
elements[Geometry::SQUARE] =
make_unique<H1Bubble_QuadrilateralElement>(p, q, btype);
}
if (dim == 3)
{
dofs[Geometry::TRIANGLE] = ((p-1)*(p-2))/2;
dofs[Geometry::SQUARE] = (p-1)*(p-1);
dofs[Geometry::TETRAHEDRON] = ((q+1)*(q+2)*(q+3))/6;
dofs[Geometry::CUBE] = (q+1)*(q+1)*(q+1);
elements[Geometry::TRIANGLE] = make_unique<H1_TriangleElement>(p, btype);
elements[Geometry::SQUARE] = make_unique<H1_QuadrilateralElement>(p, btype);
elements[Geometry::TETRAHEDRON] =
make_unique<H1Bubble_TetrahedronElement>(p, q, btype);
elements[Geometry::CUBE] =
make_unique<H1Bubble_HexahedronElement>(p, q, btype);
}
// DOF orderings. Need only for lower-dimensional entities.
// Segment DOF orderings in 2D.
if (dim >= 2)
{
seg_dof_ord[0].resize(p - 1);
seg_dof_ord[1].resize(p - 1);
for (int i = 0; i < p - 1; i++)
{
seg_dof_ord[0][i] = i;
seg_dof_ord[1][i] = p - 2 - i;
}
}
// Face (triangle or quadrilateral) DOF orderings in 3D.
if (dim == 3)
{
const int n_tri_dof = dofs[Geometry::TRIANGLE];
for (int i = 0; i < 6; i++)
{
tri_dof_ord[i].resize(n_tri_dof);
}
// see Mesh::GetTriOrientation in mesh/mesh.cpp
const int pm1 = p - 1;
const int pm2 = p - 2;
for (int j = 0; j < pm2; j++)
{
for (int i = 0; i + j < pm2; i++)
{
int o = n_tri_dof - ((pm1 - j)*(pm2 - j))/2 + i;
int k = (p - 3) - j - i;
tri_dof_ord[0][o] = o; // (0,1,2)
tri_dof_ord[1][o] = n_tri_dof - ((pm1-j)*(pm2-j))/2 + k; // (1,0,2)
tri_dof_ord[2][o] = n_tri_dof - ((pm1-i)*(pm2-i))/2 + k; // (2,0,1)
tri_dof_ord[3][o] = n_tri_dof - ((pm1-k)*(pm2-k))/2 + i; // (2,1,0)
tri_dof_ord[4][o] = n_tri_dof - ((pm1-k)*(pm2-k))/2 + j; // (1,2,0)
tri_dof_ord[5][o] = n_tri_dof - ((pm1-i)*(pm2-i))/2 + j; // (0,2,1)
}
}
const int n_quad_dof = dofs[Geometry::SQUARE];
for (int i = 0; i < 8; i++)
{
quad_dof_ord[i].resize(n_quad_dof);
}
for (int j = 0; j < pm1; j++)
{
for (int i = 0; i < pm1; i++)
{
int o = i + j*pm1;
quad_dof_ord[0][o] = i + j*pm1; // (0,1,2,3)
quad_dof_ord[1][o] = j + i*pm1; // (0,3,2,1)
quad_dof_ord[2][o] = j + (pm2 - i)*pm1; // (1,2,3,0)
quad_dof_ord[3][o] = (pm2 - i) + j*pm1; // (1,0,3,2)
quad_dof_ord[4][o] = (pm2 - i) + (pm2 - j)*pm1; // (2,3,0,1)
quad_dof_ord[5][o] = (pm2 - j) + (pm2 - i)*pm1; // (2,1,0,3)
quad_dof_ord[6][o] = (pm2 - j) + i*pm1; // (3,0,1,2)
quad_dof_ord[7][o] = i + (pm2 - j)*pm1; // (3,2,1,0)
}
}
}
}
const FiniteElement *
H1Bubble_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
{
return elements[GeomType].get();
}
const int *H1Bubble_FECollection::DofOrderForOrientation(
Geometry::Type GeomType, int Or) const
{
if (GeomType == Geometry::SEGMENT)
{
return (Or > 0) ? seg_dof_ord[0].data() : seg_dof_ord[1].data();
}
else if (GeomType == Geometry::TRIANGLE)
{
return tri_dof_ord[Or%6].data();
}
else if (GeomType == Geometry::SQUARE)
{
return quad_dof_ord[Or%8].data();
}
return nullptr;
}
FiniteElementCollection *H1Bubble_FECollection::GetTraceCollection() const
{
return (dim < 0) ? NULL : new H1_Trace_FECollection(h1_order, dim, b_type);
}
H1_Trace_FECollection::H1_Trace_FECollection(const int p, const int dim,
const int btype)
-55
View File
@@ -111,8 +111,6 @@ public:
| :------: | :---: | :---: | :-------: | :-----: | :---: |
| H1_[DIM]_[ORDER] | H1 | * | 1 | VALUE | H1 nodal elements |
| H1@[BTYPE]_[DIM]_[ORDER] | H1 | * | * | VALUE | H1 nodal elements |
| H1Bubble_[DIM]_[ORDER]_[BUBBLE_ORDER] | H1 | * | 1 | VALUE | H1 nodal elements enriched with bubble functions |
| H1Bubble@[BTYPE]_[DIM]_[ORDER]_[BUBBLE_ORDER] | H1 | * | 1 | VALUE | H1 nodal elements enriched with bubble functions |
| H1Pos_[DIM]_[ORDER] | H1 | * | 2 | VALUE | H1 nodal elements |
| H1Pos_Trace_[DIM]_[ORDER] | H^{1/2} | * | 2 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
| H1_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
@@ -319,59 +317,6 @@ public:
virtual ~H1_FECollection();
};
/// @brief Arbitrary order $H^1$-conforming (continuous) finite elements
/// enriched with bubble functions.
///
/// The bubble space consists of the standard $P_p$ or $Q_p$ space, enriched
/// with bubble functions, which are degree-$q$ polynomials times $b$, where $b$
/// is the lowest-order bubble function.
///
/// The traces are the same as the standard $H^1$ traces.
class H1Bubble_FECollection : public FiniteElementCollection
{
protected:
int dim;
int b_type;
int h1_order;
int bubble_order;
char fec_name[32];
std::array<int, Geometry::NumGeom> dofs{}; // zero initialize
std::array<std::unique_ptr<FiniteElement>, Geometry::NumGeom> elements;
std::array<std::vector<int>, 2> seg_dof_ord;
std::array<std::vector<int>, 6> tri_dof_ord;
std::array<std::vector<int>, 8> quad_dof_ord;
std::array<std::vector<int>, 24> tet_dof_ord;
public:
/// Construct the $H^1$ bubble collection consisting of degree-$p$
/// polynomials enriched with the bubble function times degree-$q$
/// polynomials.
explicit H1Bubble_FECollection(const int p, const int q, const int dim = 3,
const int btype = BasisType::GaussLobatto);
const FiniteElement *
FiniteElementForGeometry(Geometry::Type GeomType) const override;
int DofForGeometry(Geometry::Type GeomType) const override
{ return dofs[GeomType]; }
const int *DofOrderForOrientation(Geometry::Type GeomType,
int Or) const override;
const char *Name() const override { return fec_name; }
int GetContType() const override { return CONTINUOUS; }
int GetBasisType() const { return b_type; }
FiniteElementCollection *GetTraceCollection() const override;
FiniteElementCollection *Clone(int p) const override
{ return new H1Bubble_FECollection(p, bubble_order, dim, b_type); }
};
/** @brief Arbitrary order H1-conforming (continuous) finite elements with
positive basis functions. */
class H1Pos_FECollection : public H1_FECollection
+18 -6
View File
@@ -3877,12 +3877,9 @@ const FiniteElement *FiniteElementSpace::GetFE(int i) const
else
{
#ifdef MFEM_DEBUG
// Consistency check: fec->GetOrder() and FE->GetOrder() should return
// the same value (for standard, constant-order spaces). Skip this check
// even for constant-order bubble spaces, since the bubble functions on
// different geometries have different orders.
if (!IsVariableOrder() && FE->GetDim() > 0 &&
dynamic_cast<const H1Bubble_FECollection*>(fec) == nullptr)
// consistency check: fec->GetOrder() and FE->GetOrder() should return
// the same value (for standard, constant-order spaces)
if (!IsVariableOrder() && FE->GetDim() > 0)
{
MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
"internal error: " <<
@@ -3937,6 +3934,16 @@ const FiniteElement *FiniteElementSpace::GetBE(int i) const
return BE;
}
const FiniteElement *FiniteElementSpace::GetTypicalBE() const
{
if (mesh->GetNBE() > 0) { return GetBE(0); }
Geometry::Type geom = mesh->GetTypicalFaceGeometry();
const FiniteElement *be = fec->FiniteElementForGeometry(geom);
MFEM_VERIFY(be != nullptr, "Could not determine a typical BE!");
return be;
}
const FiniteElement *FiniteElementSpace::GetFaceElement(int i) const
{
MFEM_VERIFY(!IsVariableOrder(), "not implemented");
@@ -3967,6 +3974,11 @@ const FiniteElement *FiniteElementSpace::GetFaceElement(int i) const
return fe;
}
const FiniteElement *FiniteElementSpace::GetTypicalFaceElement() const
{
return fec->FiniteElementForGeometry(mesh->GetTypicalFaceGeometry());
}
const FiniteElement *FiniteElementSpace::GetEdgeElement(int i,
int variant) const
{
+13 -1
View File
@@ -839,7 +839,7 @@ public:
Note: For vector-valued elements, the results pads up the range dimension
to the spatial dimension. E.g., consider a stack of 5 vector-valued
elements each representing 2D vectors, living in a 3 dimensional space.
Then this fucntion would give 15, not 10.
Then this function would give 15, not 10.
*/
int GetVectorDim() const;
@@ -1323,12 +1323,24 @@ public:
associated with i'th boundary face in the mesh object. */
const FiniteElement *GetBE(int i) const;
/// @brief Return a "typical" boundary element.
///
/// This can be used in situations where the local mesh partition may be
/// empty.
const FiniteElement *GetTypicalBE() const;
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th face in the mesh object. Faces in this case refer
to the MESHDIM-1 primitive so in 2D they are segments and in 1D they are
points.*/
const FiniteElement *GetFaceElement(int i) const;
/// @brief Return a "typical" face element.
///
/// This can be used in situations where the local mesh partition may be
/// empty.
const FiniteElement *GetTypicalFaceElement() const;
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th edge in the mesh object. */
const FiniteElement *GetEdgeElement(int i, int variant = 0) const;
+71 -36
View File
@@ -345,27 +345,6 @@ void GridFunction::ComputeFlux(BilinearFormIntegrator &blfi,
}
}
int GridFunction::VectorDim() const
{
const FiniteElement *fe = fes->GetTypicalFE();
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
{
return fes->GetVDim();
}
return fes->GetVDim()*std::max(fes->GetMesh()->SpaceDimension(),
fe->GetRangeDim());
}
int GridFunction::CurlDim() const
{
const FiniteElement *fe = fes->GetTypicalFE();
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
{
return 2 * fes->GetMesh()->SpaceDimension() - 3;
}
return fes->GetVDim()*fe->GetCurlDim();
}
void GridFunction::GetTrueDofs(Vector &tv) const
{
const SparseMatrix *R = fes->GetRestrictionMatrix();
@@ -2050,6 +2029,18 @@ void GridFunction::AccumulateAndCountBdrValues(
Coefficient *coeff[], VectorCoefficient *vcoeff, const Array<int> &attr,
Array<int> &values_counter)
{
if (vcoeff)
{
MFEM_VERIFY(fes->GetVDim() == vcoeff->GetVDim(),
"vcoeff vdim != fes VDim");
MFEM_VERIFY(fes->GetTypicalBE()->GetMapType() == FiniteElement::VALUE &&
fes->GetTypicalBE()->GetRangeType() ==
FiniteElement::SCALAR,
"Can only call ProjectBdrCoefficient on scalar value-type "
"boundary elements. "
"Did you intended to call ProjectBdrCoefficientNormal or "
"ProjectBdrCoefficientTangent for vector finite elements?");
}
Array<int> vdofs;
Vector vc;
@@ -2202,6 +2193,9 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
VectorCoefficient &vcoeff, const Array<int> &bdr_attr,
Array<int> &values_counter)
{
MFEM_VERIFY(fes->GetTypicalBE()->GetPhysRangeDim(
fes->GetMesh()->SpaceDimension()) == vcoeff.GetVDim(),
"vcoeff vdim != PhysRangeDim");
const FiniteElement *fe;
ElementTransformation *T;
Array<int> dofs;
@@ -2355,6 +2349,9 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
void GridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
{
MFEM_VERIFY(
VectorDim() == 1,
"Cannot project scalar Coefficient onto vector GridFunction");
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
DofTransformation doftrans;
Array<int> vdofs;
@@ -2630,6 +2627,7 @@ void GridFunction::ProjectCoefficient(
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
ProjectType type)
{
MFEM_VERIFY(VectorDim() == vcoeff.GetVDim(), "vcoeff vdim != VectorDim()");
Array<int> vdofs;
Vector vals;
DofTransformation doftrans;
@@ -2945,6 +2943,7 @@ void GridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
void GridFunction::ProjectCoefficient(
VectorCoefficient &vcoeff, Array<int> &dofs)
{
MFEM_VERIFY(VectorDim() == vcoeff.GetVDim(), "vcoeff vdim != VectorDim()");
int el = -1;
ElementTransformation *T = NULL;
const FiniteElement *fe = NULL;
@@ -2974,6 +2973,7 @@ void GridFunction::ProjectCoefficient(
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff, int attribute)
{
MFEM_VERIFY(VectorDim() == vcoeff.GetVDim(), "vcoeff vdim != VectorDim()");
int i;
Array<int> vdofs;
Vector vals;
@@ -3030,9 +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)
{
std::visit([&](auto* c)
{
MFEM_VERIFY(VectorDim() == c->GetVDim(), "coeff vdim != VectorDim()");
}, coeff);
Array<int> vdofs;
Vector vals;
@@ -3046,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
@@ -3062,17 +3070,15 @@ void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
}
}
void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff)
{
Array<int> dof_attr;
ProjectDiscCoefficient(coeff, dof_attr);
}
void GridFunction::ProjectDiscCoefficient(Coefficient &coeff, AvgType type)
{
// Harmonic (x1 ... xn) = [ (1/x1 + ... + 1/xn) / n ]^-1.
// Arithmetic(x1 ... xn) = (x1 + ... + xn) / n.
MFEM_VERIFY(
VectorDim() == 1,
"Cannot project a scalar coefficient onto a vector GridFunction");
Array<int> zones_per_vdof;
AccumulateAndCountZones(coeff, type, zones_per_vdof);
@@ -3082,6 +3088,7 @@ void GridFunction::ProjectDiscCoefficient(Coefficient &coeff, AvgType type)
void GridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff,
AvgType type)
{
MFEM_VERIFY(VectorDim() == coeff.GetVDim(), "coeff vdim != VectorDim()");
Array<int> zones_per_vdof;
AccumulateAndCountZones(coeff, type, zones_per_vdof);
@@ -3139,12 +3146,16 @@ void GridFunction::ProjectBdrCoefficient(Coefficient *coeff[],
void GridFunction::ProjectBdrCoefficientNormal(
Coefficient *coeff, VectorCoefficient *vcoeff, const Array<int> &bdr_attr)
{
if (fes->GetNBE() > 0)
MFEM_VERIFY(fes->GetVDim() == 1, "fespace VDim != 1");
MFEM_VERIFY(fes->GetTypicalBE()->GetRangeType() == FiniteElement::SCALAR &&
fes->GetTypicalBE()->GetMapType() == FiniteElement::INTEGRAL,
"Not an RT FE space!");
if (vcoeff)
{
// TODO: Replace this by GetTypicalBdrElement() once implemented
const FiniteElement *be = fes->GetBE(0);
MFEM_VERIFY(be->GetRangeType() == FiniteElement::SCALAR &&
be->GetMapType() == FiniteElement::INTEGRAL, "Not an RT FE space!");
MFEM_VERIFY(vcoeff->GetVDim() == fes->GetMesh()->SpaceDimension(),
"vcoeff vdim (" << vcoeff->GetVDim()
<< ") != SpaceDimension ("
<< fes->GetMesh()->SpaceDimension() << ")");
}
// implementation for the case when the face dofs are scaled point
@@ -5241,6 +5252,30 @@ void GridFunction::GetElementBounds(const PLBound &plb,
Vector &lower, Vector &upper,
const int vdim) const
{
if (UseDevice() && Device::Allows(Backend::DEVICE_MASK) &&
plb.GetBasisType() != BasisType::Positive &&
UsesTensorBasis(*fes))
{
const FiniteElement &fe = *fes->GetTypicalFE();
const int rdim = fe.GetDim();
const int fes_dim = fes->GetVDim();
const int nel = fes->GetNE();
const int nd = fe.GetDof();
Vector e_vec(nd*fes_dim*nel, Device::GetDeviceMemoryType());
e_vec.UseDevice(true);
const ElementRestrictionOperator *elem_restr =
fes->GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
MFEM_VERIFY(elem_restr != nullptr,
"Element restriction is required for device bounds.");
elem_restr->Mult(*this, e_vec);
plb.GetElementBoundsKernel(rdim, fes_dim, e_vec, lower, upper, vdim);
lower.HostRead();
upper.HostRead();
return;
}
int nel = fes->GetNE();
int fes_dim = fes->GetVDim();
lower.SetSize(nel*(vdim > 0 ? 1 :fes_dim));
@@ -5757,4 +5792,4 @@ std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
return std::make_pair(global_max_lower, global_max_upper);
}
}
}
+28 -9
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);
@@ -150,11 +159,13 @@ public:
FiniteElementCollection *OwnFEC() { return fec_owned; }
/// Shortcut for calling FiniteElementSpace::GetVectorDim() on the underlying #fes
int VectorDim() const;
/** @brief Shortcut for calling FiniteElementSpace::GetVectorDim() on the
underlying #fes */
int VectorDim() const { return fes->GetVectorDim(); }
/// Shortcut for calling FiniteElementSpace::GetCurlDim() on the underlying #fes
int CurlDim() const;
/** @brief Shortcut for calling FiniteElementSpace::GetCurlDim() on the
underlying #fes */
int CurlDim() const { return fes->GetCurlDim(); }
/// Read only access to the (optional) internal true-dof Vector.
const Vector &GetTrueVector() const
@@ -513,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
@@ -1971,6 +1989,7 @@ public:
void Eval(Vector &v, ElementTransformation &T,
const IntegrationPoint &ip) override;
using VectorCoefficient::Eval;
virtual ~VectorExtrudeCoefficient() { }
};
+8 -11
View File
@@ -106,9 +106,7 @@ FindPointsGSLIB::FindPointsGSLIB()
gsl_comm = new gslib::comm;
cr = new gslib::crystal;
#ifdef MFEM_USE_MPI
int initialized = 0;
MPI_Initialized(&initialized);
if (!initialized) { MPI_Init(NULL, NULL); }
if (!Mpi::IsInitialized()) { Mpi::Init(); }
MPI_Comm comm = MPI_COMM_WORLD;
comm_init(gsl_comm, comm);
#else
@@ -490,7 +488,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 +650,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 +1066,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 +1411,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 +2252,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.");
@@ -2623,9 +2622,7 @@ GSOPGSLIB::GSOPGSLIB(Array<long long> &ids)
gsl_comm = new gslib::comm;
cr = new gslib::crystal;
#ifdef MFEM_USE_MPI
int initialized;
MPI_Initialized(&initialized);
if (!initialized) { MPI_Init(NULL, NULL); }
if (!Mpi::IsInitialized()) { Mpi::Init(); }
MPI_Comm comm = MPI_COMM_WORLD;
comm_init(gsl_comm, comm);
#else
+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]]
+76 -48
View File
@@ -181,6 +181,12 @@ constexpr int NBZ(int D1D)
{
return ipow(2, D(D1D) >= 0 ? D(D1D) : 0);
}
constexpr int NBZ3D(int MDQ)
{
return MDQ > 0 ? std::min<int>(
(128 + MDQ * MDQ * MDQ - 1) / (MDQ * MDQ * MDQ), 64)
: 1;
}
}
// Shared memory PA Mass Diagonal 2D kernel
@@ -804,19 +810,23 @@ void PAMassApply3D_Element(const int e,
}
}
template<int T_D1D, int T_Q1D, bool ACCUMULATE = true>
MFEM_HOST_DEVICE inline
void SmemPAMassApply3D_Element(const int e,
const int NE,
const real_t *b_,
const real_t *d_,
const real_t *x_,
real_t *y_,
const int d1d = 0,
const int q1d = 0)
template <int T_D1D, int T_Q1D, int TBATCH, bool ACCUMULATE = true>
MFEM_HOST_DEVICE inline void
SmemPAMassApply3D_Element(const int e, const int NE, const real_t *b_,
const real_t *d_, const real_t *x_, real_t *y_,
int d1d = 0, int q1d = 0)
{
constexpr int D1D = T_D1D ? T_D1D : d1d;
constexpr int Q1D = T_Q1D ? T_Q1D : q1d;
static_assert(TBATCH > 0, "TBATCH must be positive");
#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
constexpr int tbatch = TBATCH;
const int tidz = MFEM_THREAD_ID(z);
#else
// host always batch size 1
constexpr int tbatch = 1;
constexpr int tidz = 0;
#endif
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
@@ -829,33 +839,37 @@ void SmemPAMassApply3D_Element(const int e,
MFEM_SHARED real_t sDQ[MQ1*MD1];
real_t (*B)[MD1] = (real_t (*)[MD1]) sDQ;
real_t (*Bt)[MQ1] = (real_t (*)[MQ1]) sDQ;
MFEM_SHARED real_t sm0[MDQ*MDQ*MDQ];
MFEM_SHARED real_t sm1[MDQ*MDQ*MDQ];
real_t (*X)[MD1][MD1] = (real_t (*)[MD1][MD1]) sm0;
real_t (*DDQ)[MD1][MQ1] = (real_t (*)[MD1][MQ1]) sm1;
real_t (*DQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) sm0;
real_t (*QQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) sm1;
real_t (*QQD)[MQ1][MD1] = (real_t (*)[MQ1][MD1]) sm0;
real_t (*QDD)[MD1][MD1] = (real_t (*)[MD1][MD1]) sm1;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_SHARED real_t sm0[tbatch][MDQ*MDQ*MDQ];
MFEM_SHARED real_t sm1[tbatch][MDQ*MDQ*MDQ];
real_t (*X)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm0+tidz);
real_t (*DDQ)[MD1][MQ1] = (real_t (*)[MD1][MQ1]) (sm1+tidz);
real_t (*DQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm0+tidz);
real_t (*QQQ)[MQ1][MQ1] = (real_t (*)[MQ1][MQ1]) (sm1+tidz);
real_t (*QQD)[MQ1][MD1] = (real_t (*)[MQ1][MD1]) (sm0+tidz);
real_t (*QDD)[MD1][MD1] = (real_t (*)[MD1][MD1]) (sm1+tidz);
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dx, x, D1D)
{
MFEM_UNROLL(MD1)
for (int dz = 0; dz < D1D; ++dz)
{
X[dz][dy][dx] = x(dx,dy,dz,e);
X[dz][dy][dx] = x(dx, dy, dz, e);
}
}
MFEM_FOREACH_THREAD(dx,x,Q1D)
MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
}
if (tidz == 0)
{
MFEM_FOREACH_THREAD(dy, y, D1D)
{
B[dx][dy] = b(dx,dy);
MFEM_FOREACH_THREAD(dx, x, Q1D) { B[dx][dy] = b(dx, dy); }
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
real_t u[D1D];
MFEM_UNROLL(MD1)
@@ -880,9 +894,9 @@ void SmemPAMassApply3D_Element(const int e,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
real_t u[D1D];
MFEM_UNROLL(MD1)
@@ -907,9 +921,9 @@ void SmemPAMassApply3D_Element(const int e,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
real_t u[Q1D];
MFEM_UNROLL(MQ1)
@@ -929,22 +943,22 @@ void SmemPAMassApply3D_Element(const int e,
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < Q1D; qz++)
{
QQQ[qz][qy][qx] = u[qz] * d(qx,qy,qz,e);
QQQ[qz][qy][qx] = u[qz] * d(qx, qy, qz, e);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(di,y,D1D)
if (tidz == 0)
{
MFEM_FOREACH_THREAD(q,x,Q1D)
MFEM_FOREACH_THREAD(di, y, D1D)
{
Bt[di][q] = b(q,di);
MFEM_FOREACH_THREAD(q, x, Q1D) { Bt[di][q] = b(q, di); }
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qy,y,Q1D)
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dx, x, D1D)
{
real_t u[Q1D];
MFEM_UNROLL(MQ1)
@@ -969,9 +983,9 @@ void SmemPAMassApply3D_Element(const int e,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dx, x, D1D)
{
real_t u[Q1D];
MFEM_UNROLL(MQ1)
@@ -996,9 +1010,9 @@ void SmemPAMassApply3D_Element(const int e,
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dy,y,D1D)
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
MFEM_FOREACH_THREAD(dx, x, D1D)
{
real_t u[D1D];
MFEM_UNROLL(MD1)
@@ -1020,11 +1034,11 @@ void SmemPAMassApply3D_Element(const int e,
{
if (ACCUMULATE)
{
y(dx,dy,dz,e) += u[dz];
y(dx, dy, dz, e) += u[dz];
}
else
{
y(dx,dy,dz,e) = u[dz];
y(dx, dy, dz, e) = u[dz];
}
}
}
@@ -1115,8 +1129,8 @@ inline void PAMassApply3D(const int NE,
});
}
// Shared memory PA Mass Apply 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
// Shared memory PA Mass Apply 3D kernel
template<int T_D1D = 0, int T_Q1D = 0, int TBATCH=1>
inline void SmemPAMassApply3D(const int NE,
const Array<real_t> &b_,
const Array<real_t> &bt_,
@@ -1126,6 +1140,9 @@ inline void SmemPAMassApply3D(const int NE,
const int d1d = 0,
const int q1d = 0)
{
static_assert(T_D1D > 0, "T_D1D must be positive");
static_assert(T_Q1D > 0, "T_Q1D must be positive");
static_assert(TBATCH > 0, "TBATCH must be positive");
MFEM_CONTRACT_VAR(bt_);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -1137,9 +1154,11 @@ inline void SmemPAMassApply3D(const int NE,
const auto d = d_.Read();
const auto x = x_.Read();
auto y = y_.ReadWrite();
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
mfem::forall_2D_batch<T_Q1D * T_Q1D * TBATCH>(NE, Q1D, Q1D, TBATCH,
[=] MFEM_HOST_DEVICE(int e)
{
internal::SmemPAMassApply3D_Element<T_D1D,T_Q1D>(e, NE, b, d, x, y, d1d, q1d);
internal::SmemPAMassApply3D_Element<T_D1D, T_Q1D, TBATCH>(e, NE, b, d, x,
y, d1d, q1d);
});
}
@@ -1394,7 +1413,16 @@ ApplyKernelType MassIntegrator::ApplyPAKernels::Kernel()
{
if constexpr (DIM == 1) { return internal::PAMassApply1D; }
else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<T_D1D,T_Q1D>; }
else if constexpr (DIM == 3) { return internal::SmemPAMassApply3D<T_D1D, T_Q1D>; }
else if constexpr (DIM == 3)
{
constexpr int MDQ = T_D1D >= T_Q1D ? T_D1D : T_Q1D;
// max 64 threads in z limit in cuda and hip
if constexpr (MDQ > 0)
{
return internal::SmemPAMassApply3D<T_D1D, T_Q1D,
internal::mass::NBZ3D(MDQ)>;
}
}
MFEM_ABORT("");
}
+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; }
+15 -1
View File
@@ -545,6 +545,8 @@ void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
void ParGridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
{
MFEM_VERIFY(VectorDim() == 1,
"Cannot project scalar coefficient onto vector ParGridFunction");
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
if (delta_c == NULL)
@@ -715,7 +717,8 @@ void ParGridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
}
void ParGridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff)
void ParGridFunction::ProjectDiscCoefficient(
std::variant<Coefficient*, VectorCoefficient*> coeff)
{
// local maximal element attribute for each dof
Array<int> ldof_attr;
@@ -761,6 +764,9 @@ void ParGridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff)
void ParGridFunction::ProjectDiscCoefficient(Coefficient &coeff, AvgType type)
{
MFEM_VERIFY(
VectorDim() == 1,
"Cannot project scalar coefficient onto a vector ParGridFunction");
// Harmonic (x1 ... xn) = [ (1/x1 + ... + 1/xn) / n ]^-1.
// Arithmetic(x1 ... xn) = (x1 + ... + xn) / n.
@@ -786,6 +792,8 @@ void ParGridFunction::ProjectDiscCoefficient(VectorCoefficient &vcoeff,
// Harmonic (x1 ... xn) = [ (1/x1 + ... + 1/xn) / n ]^-1.
// Arithmetic(x1 ... xn) = (x1 + ... + xn) / n.
MFEM_VERIFY(VectorDim() == vcoeff.GetVDim(), "vcoeff vdim != VectorDim()");
// Number of zones that contain a given dof.
Array<int> zones_per_vdof;
AccumulateAndCountZones(vcoeff, type, zones_per_vdof);
@@ -858,6 +866,12 @@ void ParGridFunction::ProjectBdrCoefficient(
#endif
}
void ParGridFunction::ProjectBdrCoefficient(VectorCoefficient &vcoeff,
const Array<int> &attr)
{
ProjectBdrCoefficient(NULL, &vcoeff, attr);
}
void ParGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
const Array<int> &bdr_attr)
{
+7 -7
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;
@@ -280,8 +281,7 @@ public:
using GridFunction::ProjectBdrCoefficient;
void ProjectBdrCoefficient(VectorCoefficient &vcoeff,
const Array<int> &attr) override
{ ProjectBdrCoefficient(NULL, &vcoeff, attr); }
const Array<int> &attr) override;
void ProjectBdrCoefficient(Coefficient *coeff[],
const Array<int> &attr) 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);
}
}
}
+6
View File
@@ -1090,6 +1090,12 @@ inline void forall_2D_batch(int N, int X, int Y, int BZ, lambda &&body)
ForallWrap<2>(true, N, body, X, Y, BZ);
}
template<int MAX_THREADS_PER_BLOCK, typename lambda>
inline void forall_2D_batch(int N, int X, int Y, int BZ, lambda &&body)
{
ForallWrap<2, MAX_THREADS_PER_BLOCK>(true, N, body, X, Y, BZ);
}
template<typename lambda>
inline void forall_3D(int N, int X, int Y, int Z, lambda &&body)
{
+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)
{
+17 -6
View File
@@ -4156,20 +4156,31 @@ void PetscNonlinearSolver::SetUpdate(void (*update)(Operator *,int,
void PetscNonlinearSolver::Mult(const Vector &b, Vector &x) const
{
SNES snes = (SNES)obj;
MPI_Comm comm = PetscObjectComm(obj);
bool b_nonempty = b.Size();
if (!B) { B = new PetscParVector(PetscObjectComm(obj), *this, true); }
if (!X) { X = new PetscParVector(PetscObjectComm(obj), *this, false, false); }
// Reduction needed: some processes may have null local size while others don't,
// and VecPlaceArray (used by PlaceMemory) is a logically collective operation.
PetscBool b_nonempty = b.Size() ? PETSC_TRUE : PETSC_FALSE;
#if PETSC_VERSION_LT(3,24,0)
mpiierr = MPI_Allreduce(MPI_IN_PLACE,&b_nonempty,1,MPIU_BOOL,MPI_LOR,comm);
#else
mpiierr = MPI_Allreduce(MPI_IN_PLACE,&b_nonempty,1,MPI_C_BOOL,MPI_LOR,comm);
#endif
CCHKERRQ(comm,mpiierr);
// Always create B with allocate=false so that PlaceMemory can be called on
// it regardless of whether b was empty on a previous call.
if (!B) { B = new PetscParVector(comm, *this, true, false); }
if (!X) { X = new PetscParVector(comm, *this, false, false); }
X->PlaceMemory(x.GetMemory(),iterative_mode);
if (b_nonempty) { B->PlaceMemory(b.GetMemory()); }
else { *B = 0.0; }
Customize();
if (!iterative_mode) { *X = 0.; }
// Solve the system.
ierr = SNESSolve(snes, B->x, X->x); PCHKERRQ(snes, ierr);
// Solve the system. Pass nullptr for b when empty (PETSc treats it as zero RHS).
ierr = SNESSolve(snes, b_nonempty ? B->x : nullptr, X->x); PCHKERRQ(snes, ierr);
X->ResetMemory();
if (b_nonempty) { B->ResetMemory(); }
}
+34 -12
View File
@@ -123,15 +123,20 @@ EXAMPLE_SUBDIRS = amgx caliper ginkgo hiop petsc pumi sundials superlu moonolith
EXAMPLE_DIRS := examples $(addprefix examples/,$(EXAMPLE_SUBDIRS))
EXAMPLE_TEST_DIRS := examples
MINIAPP_SUBDIRS = common electromagnetics meshing performance tools \
MINIAPP_ALL_SUBDIRS = common electromagnetics meshing performance tools \
toys nurbs gslib adjoint solvers shifted mtop parelag tribol autodiff dfem \
hooke multidomain dpg hdiv-linear-solver spde diag-smoothers contact \
fluids/navier fluids/schrodinger-flow plasma
fluids/navier fluids/schrodinger-flow plasma plasma/pic
MINIAPP_RECURSIVE_SUBDIRS = plasma/pic
MINIAPP_SUBDIRS := $(filter-out \
$(MINIAPP_RECURSIVE_SUBDIRS),$(MINIAPP_ALL_SUBDIRS))
MINIAPP_ALL_DIRS := $(addprefix miniapps/,$(MINIAPP_ALL_SUBDIRS))
MINIAPP_DIRS := $(addprefix miniapps/,$(MINIAPP_SUBDIRS))
MINIAPP_TEST_DIRS := $(filter-out %/common,$(MINIAPP_DIRS))
MINIAPP_USE_COMMON := $(addprefix miniapps/,electromagnetics meshing tools \
toys shifted dpg diag-smoothers fluids/navier plasma)
toys gslib shifted dpg diag-smoothers fluids/navier plasma plasma/pic)
EM_ALL_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_ALL_DIRS)
EM_DIRS = $(EXAMPLE_DIRS) $(MINIAPP_DIRS)
TEST_SUBDIRS = unit
@@ -146,7 +151,7 @@ MFEM_BUILD_DIR ?= .
BUILD_DIR := $(MFEM_BUILD_DIR)
BUILD_REAL_DIR := $(abspath $(BUILD_DIR))
ifneq ($(BUILD_REAL_DIR),$(MFEM_REAL_DIR))
BUILD_SUBDIRS = $(DIRS) config $(EM_DIRS) doc $(TEST_DIRS)
BUILD_SUBDIRS = $(DIRS) config $(EM_ALL_DIRS) doc $(TEST_DIRS)
CONFIG_FILE_DEF = -DMFEM_CONFIG_FILE='"$(BUILD_REAL_DIR)/config/_config.hpp"'
BLD := $(if $(BUILD_REAL_DIR:$(CURDIR)=),$(BUILD_DIR)/,)
$(if $(word 2,$(BLD)),$(error Spaces in BLD = "$(BLD)" are not supported))
@@ -483,10 +488,10 @@ $(OBJECT_FILES): $(BLD)%.o: $(SRC)%.cpp $(CONFIG_MK)
all: examples miniapps $(TEST_DIRS)
.PHONY: miniapps $(EM_DIRS) $(TEST_DIRS)
.PHONY: miniapps $(EM_ALL_DIRS) $(TEST_DIRS)
miniapps: $(MINIAPP_DIRS)
$(MINIAPP_USE_COMMON): miniapps/common
$(EM_DIRS) $(TEST_DIRS): lib
$(EM_ALL_DIRS) $(TEST_DIRS): lib
$(MAKE) -C $(BLD)$(@)
.PHONY: doc
@@ -694,7 +699,7 @@ local-config:
.PHONY: build-config
build-config:
for d in $(BUILD_SUBDIRS); do mkdir -p $(BLD)$${d}; done
for dir in "" $(addsuffix /,config $(EM_DIRS) doc $(TEST_DIRS)); do \
for dir in "" $(addsuffix /,config $(EM_ALL_DIRS) doc $(TEST_DIRS)); do\
printf "# Auto-generated file.\n%s\n%s\n" \
"MFEM_DIR = $(MFEM_REAL_DIR)" \
"include \$$(MFEM_DIR)/$${dir}makefile" \
@@ -796,13 +801,15 @@ status info:
ASTYLE = $(ASTYLE_BIN) --options=$(SRC)config/mfem.astylerc
ASTYLE_VER = "Artistic Style Version 3.1"
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_DIRS) config,$(dir)/*.?pp)
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_ALL_DIRS) config,$(dir)/*.?pp)
TESTS_SUBDIRS = unit benchmarks convergence mem_manager par-mesh-format
UNIT_TESTS_SUBDIRS = general linalg mesh fem miniapps ceed enzyme
MINIAPPS_SUBDIRS = dpg/util hooke/operators hooke/preconditioners hooke/materials hooke/kernels
UNIT_TESTS_SUBDIRS = general linalg mesh fem miniapps ceed enzyme dfem
MINIAPPS_SUBDIRS = dpg/util hooke/operators hooke/preconditioners \
hooke/materials hooke/kernels
FORMAT_FILES += $(foreach dir,$(TESTS_SUBDIRS),tests/$(dir)/*.?pp)
FORMAT_FILES += $(foreach dir,$(UNIT_TESTS_SUBDIRS),tests/unit/$(dir)/*.?pp)
FORMAT_FILES += $(foreach dir,$(MINIAPPS_SUBDIRS),miniapps/$(dir)/*.?pp)
FORMAT_FILES += config/cmake/config.hpp.in config/config.hpp.in mfem*.hpp
FORMAT_EXCLUDE = general/tinyxml2.cpp tests/unit/catch.hpp
FORMAT_LIST = $(filter-out $(FORMAT_EXCLUDE),$(wildcard $(FORMAT_FILES)))
@@ -833,14 +840,29 @@ mfem_check_command = \
# Verify the C++ code styling in MFEM and check that std::cout and std::cerr are
# not used in the library (use mfem::out and mfem::err instead).
style:
@echo "Applying C++ code style..."
@astyle_version="$$($(ASTYLE_BIN) --version)";\
if [ "$$astyle_version" != $(ASTYLE_VER) ]; then\
printf "%s\n" "Invalid astyle version: '$$astyle_version'"\
"Please use: '"$(ASTYLE_VER)"'";\
exit 1;\
fi
@err_code=0;\
@err_code=0; \
if command -v git 2>&1 > /dev/null && [ -d $(MFEM_DIR)/.git ]; then \
echo "Checking if all git files are selected for formatting ..."; \
ls -1 $(FORMAT_FILES) | sort > format-files-make.txt; \
git -C $(MFEM_DIR) ls-files '*.[ch]pp*' | sort \
> format-files-git.txt; \
cat format-files-make.txt format-files-git.txt | sort | uniq \
> format-files-make-plus-git.txt; \
rm -f format-files-git.txt; \
$(call mfem_check_command,\
diff format-files-make.txt format-files-make-plus-git.txt | \
grep "^> ",\
"All git files are selected for formatting",\
"The above git files are NOT selected for formatting"); \
rm -f format-files-make.txt format-files-make-plus-git.txt; \
fi; \
echo "Applying C++ code style...";\
$(call mfem_check_command,\
$(ASTYLE) $(FORMAT_LIST) | grep Formatted,\
"No source files were changed",\
+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;
+6
View File
@@ -5639,6 +5639,12 @@ Mesh ParMesh::GetSerialMesh(int save_rank) const
}
}
if (MyRank == save_rank)
{
attribute_sets.Copy(serialmesh.attribute_sets);
bdr_attribute_sets.Copy(serialmesh.bdr_attribute_sets);
}
MPI_Barrier(MyComm);
return serialmesh;
}
+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
+10 -2
View File
@@ -46,8 +46,16 @@ if (MFEM_USE_GSLIB)
if (MFEM_ENABLE_TESTING)
foreach (test "schwarz_ex1" "field-diff" "findpts" "field-interp")
add_test(NAME ${test}
COMMAND $<TARGET_FILE:${test}> -no-vis)
if (MFEM_USE_MPI)
add_test(NAME ${test}
COMMAND ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} 1
${MPIEXEC_PREFLAGS}
$<TARGET_FILE:${test}> -no-vis
${MPIEXEC_POSTFLAGS})
else()
add_test(NAME ${test}
COMMAND $<TARGET_FILE:${test}> -no-vis)
endif()
endforeach()
endif()
+6 -1
View File
@@ -83,11 +83,16 @@ include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
ifeq ($(MFEM_USE_MPI),YES)
RUN_MPI_SER = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) 1
else
RUN_MPI_SER =
endif
TEST_NAME := GSLIB miniapp
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), $(TEST_NAME))
%-test-seq: %
@$(call mfem-test,$<,, $(TEST_NAME))
@$(call mfem-test,$<, $(RUN_MPI_SER), $(TEST_NAME))
# 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]; }
}
+2
View File
@@ -23,3 +23,5 @@ if (MFEM_USE_MPI)
EXTRA_HEADERS ${PLASMA_COMMON_HEADERS})
endif()
add_subdirectory(pic)
+23 -18
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,14 @@ else
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_MINIAPPS)
endif
PLASMA_SUBDIRS = pic
SUBDIRS_ALL = $(addsuffix /all,$(PLASMA_SUBDIRS))
SUBDIRS_TEST = $(addsuffix /test,$(PLASMA_SUBDIRS))
SUBDIRS_TEST_NOCLEAN = $(addsuffix /test-noclean,$(PLASMA_SUBDIRS))
SUBDIRS_CLEAN = $(addsuffix /clean,$(PLASMA_SUBDIRS))
SUBDIRS_TPRINT = $(addsuffix /test-print,$(PLASMA_SUBDIRS))
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all lib-common clean clean-build clean-exec
@@ -38,26 +43,24 @@ 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=),,\
$(if $(MFEM_USE_CUDA:YES=),$(CXX_XLINKER),$(CUDA_XLINKER))-rpath,$(abspath\
$(MFEM_BUILD_DIR)/miniapps/common))
COMMON_O=
$(MFEM_XLINKER)-rpath,$(abspath $(MFEM_BUILD_DIR)/miniapps/common))
# Remove built-in rules
%: %.cpp
%.o: %.cpp
all: $(MINIAPPS)
all: $(MINIAPPS) $(SUBDIRS_ALL)
.PHONY: $(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) \
$(SUBDIRS_CLEAN) $(SUBDIRS_TPRINT)
$(SUBDIRS_ALL) $(SUBDIRS_TEST) $(SUBDIRS_TEST_NOCLEAN) $(SUBDIRS_CLEAN):
$(MAKE) -C $(@D) $(@F)
$(SUBDIRS_TPRINT):
@$(MAKE) -C $(@D) $(@F)
# Rules for building the miniapps
%: $(SRC)%.cpp $(COMMON_O) $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common
$(MFEM_CXX) $(MFEM_LINK_FLAGS) $< -o $@ $(COMMON_O) $(COMMON_LIB) \
$(MFEM_LIBS)
# Rules for compiling miniapp dependencies
$(COMMON_O) $(addsuffix _solver.o,$(MINIAPPS)): \
%.o: $(SRC)%.cpp $(SRC)%.hpp $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) -c $(<) -o $(@)
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK) | lib-common
$(MFEM_CXX) $(MFEM_LINK_FLAGS) $< -o $@ $(COMMON_LIB) $(MFEM_LIBS)
# Rule for building lib-common
lib-common:
@@ -65,6 +68,9 @@ lib-common:
MFEM_TESTS = MINIAPPS
include $(MFEM_TEST_MK)
test: $(SUBDIRS_TEST)
test-noclean: $(SUBDIRS_TEST_NOCLEAN)
test-print: $(SUBDIRS_TPRINT)
# Testing: Specific execution options
RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
@@ -75,11 +81,10 @@ RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build clean-exec
clean: clean-build clean-exec $(SUBDIRS_CLEAN)
clean-build:
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;
}
+86
View File
@@ -0,0 +1,86 @@
# 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)
SEQ_MINIAPPS =
PAR_MINIAPPS =
ifeq ($(MFEM_USE_GSLIB),YES)
PAR_MINIAPPS += electrostatic-pic
endif
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
else
MINIAPPS = $(PAR_MINIAPPS) $(SEQ_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
%: $(SRC)%.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
+4
View File
@@ -61,6 +61,10 @@ if (MFEM_USE_MPI)
LIBRARIES mfem-common)
add_dependencies(gridfunction-bounds copy_miniapps_tools_data)
add_mfem_miniapp(random-gridfunction-bounds
MAIN random-gridfunction-bounds.cpp
LIBRARIES mfem)
add_mfem_miniapp(plor-transfer
MAIN plor-transfer.cpp LIBRARIES mfem)
+3 -2
View File
@@ -23,7 +23,8 @@ MFEM_LIB_FILE = mfem_is_not_built
SEQ_MINIAPPS = display-basis load-dc convert-dc get-values lor-transfer \
tmop-check-metric tmop-metric-magnitude compare-dc
PAR_MINIAPPS = nodal-transfer plor-transfer gridfunction-bounds
PAR_MINIAPPS = nodal-transfer plor-transfer gridfunction-bounds \
random-gridfunction-bounds
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
@@ -79,7 +80,7 @@ RUN_MPI = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP) $(MFEM_MPI_NP)
# Do not test: display-basis, load-dc, convert-dc, get-values, lor-transfer, plor-transfer
NO_TEST_APPS = display-basis load-dc convert-dc get-values lor-transfer \
plor-transfer tmop-check-metric tmop-metric-magnitude gridfunction-bounds \
compare-dc
random-gridfunction-bounds compare-dc
$(foreach app,$(NO_TEST_APPS),$(app)-test-seq $(app)-test-par):
@true
@@ -0,0 +1,263 @@
// 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.
//
// ---------------------------------------------------------------------
// Compute bounds of a random grid function on a generated tensor mesh
// ---------------------------------------------------------------------
//
// This miniapp generates a 1D segment mesh or 2D quad mesh, builds a random
// discontinuous grid function, computes element-wise piecewise linear bounds,
// and visualizes the input field together with the lower and upper bounds.
//
// Compile with: make random-gridfunction-bounds
//
// Sample runs:
// mpirun -np 4 random-gridfunction-bounds
// mpirun -np 4 random-gridfunction-bounds -nx 64 -o 6 -ref 3 -d hip
#include "mfem.hpp"
#include <algorithm>
#include <type_traits>
using namespace mfem;
using namespace std;
void VisualizeField(ParMesh &pmesh, ParGridFunction &input,
char *title, int pos_x, int pos_y);
int main(int argc, char *argv[])
{
Mpi::Init(argc, argv);
Hypre::Init();
int dim = 2;
int nx = 16;
int order = 4;
int num_comp = 2;
int ref = 2;
int niter = 1000;
int seed = 12345;
bool kernel_only = true;
bool visualization = false;
const char *device_config = "cpu";
OptionsParser args(argc, argv);
args.AddOption(&dim, "-dim", "--dimension",
"Dimension of the generated tensor-product mesh (1 or 2).");
args.AddOption(&nx, "-nx", "--num-elements",
"Number of elements in each mesh direction.");
args.AddOption(&order, "-o", "--order",
"Polynomial degree of the random discontinuous field.");
args.AddOption(&num_comp, "-nc", "--num-components",
"Number of vector components in the ParFiniteElementSpace.");
args.AddOption(&ref, "-ref", "--piecewise-linear-ref-factor",
"Scaling factor for the resolution of the piecewise linear "
"bounds. If less than 2, the resolution is picked "
"automatically.");
args.AddOption(&niter, "-ni", "--num-iters",
"Number of times to evaluate the bounds.");
args.AddOption(&seed, "-rs", "--random-seed",
"Random seed used to initialize the field.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&kernel_only, "-ko", "--kernel-only",
"-no-ko", "--no-kernel-only",
"Run only PLBound::GetElementBoundsKernel on a prebuilt "
"element E-vector.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.ParseCheck();
MFEM_VERIFY(dim == 1 || dim == 2, "dim must be 1 or 2.");
MFEM_VERIFY(nx > 0, "nx must be positive.");
MFEM_VERIFY(order >= 0, "order must be non-negative.");
MFEM_VERIFY(num_comp > 0, "num_comp must be positive.");
MFEM_VERIFY(niter > 0, "niter must be positive.");
Device device(device_config);
if (Mpi::Root()) { device.Print(); }
Mesh mesh = (dim == 1) ?
Mesh::MakeCartesian1D(nx, 1.0) :
Mesh::MakeCartesian2D(nx, nx, Element::QUADRILATERAL, true,
1.0, 1.0);
ParMesh pmesh(MPI_COMM_WORLD, mesh);
const int mesh_dim = pmesh.Dimension();
L2_FECollection fec(order, mesh_dim, BasisType::GaussLobatto);
ParFiniteElementSpace fes(&pmesh, &fec, num_comp, Ordering::byNODES);
ParGridFunction input(&fes);
input.Randomize(seed + Mpi::WorldRank());
input.UseDevice(true);
L2_FECollection fec_pc(0, mesh_dim);
ParFiniteElementSpace fes_pc(&pmesh, &fec_pc, num_comp, Ordering::byNODES);
ParGridFunction lowerb(&fes_pc), upperb(&fes_pc);
Vector lower_vec, upper_vec;
PLBound plb(&fes, ref*(fes.GetMaxElementOrder() + 1));
if (kernel_only)
{
const FiniteElement &fe = *fes.GetTypicalFE();
const int rdim = fe.GetDim();
const int nd = fe.GetDof();
const int fes_dim = fes.GetVDim();
Vector e_vec(nd*fes_dim*fes.GetNE(), Device::GetDeviceMemoryType());
e_vec.UseDevice(true);
const ElementRestrictionOperator *elem_restr =
fes.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
MFEM_VERIFY(elem_restr != nullptr,
"Element restriction is required for kernel-only mode.");
elem_restr->Mult(input, e_vec);
for (int i = 0; i < niter; i++)
{
plb.GetElementBoundsKernel(rdim, fes_dim, e_vec, lower_vec, upper_vec);
}
}
else
{
for (int i = 0; i < niter; i++)
{
input.GetElementBounds(plb, lower_vec, upper_vec);
}
}
const real_t *lower_data = lower_vec.HostRead();
const real_t *upper_data = upper_vec.HostRead();
// Build a host reference from the lexicographic E-vector and the scalar
// PLBound::GetNDBounds path to avoid re-entering the device dispatch.
const bool use_dev = input.UseDevice();
PLBound plb_host(&fes, ref*(fes.GetMaxElementOrder() + 1));
Vector lower_ref, upper_ref;
const FiniteElement &fe = *fes.GetTypicalFE();
const int rdim = fe.GetDim();
const int nd = fe.GetDof();
const int nel = fes.GetNE();
const int fes_dim = fes.GetVDim();
Vector e_vec_ref(nd*fes_dim*nel);
lower_ref.SetSize(nel*fes_dim);
upper_ref.SetSize(nel*fes_dim);
const ElementRestrictionOperator *elem_restr =
fes.GetElementRestriction(ElementDofOrdering::LEXICOGRAPHIC);
MFEM_VERIFY(elem_restr != nullptr,
"Element restriction is required for host reference.");
input.UseDevice(false);
input.HostRead();
elem_restr->Mult(input, e_vec_ref);
input.UseDevice(use_dev);
const real_t *e_ref_data = e_vec_ref.HostRead();
for (int d = 0; d < fes_dim; d++)
{
for (int e = 0; e < nel; e++)
{
Vector coeff(nd);
for (int i = 0; i < nd; i++)
{
coeff(i) = e_ref_data[i + nd*(d + fes_dim*e)];
}
Vector lower_c, upper_c;
plb_host.GetNDBounds(rdim, coeff, lower_c, upper_c);
lower_ref(e + d*nel) = lower_c.Min();
upper_ref(e + d*nel) = upper_c.Max();
}
}
const real_t *lower_ref_data = lower_ref.HostRead();
const real_t *upper_ref_data = upper_ref.HostRead();
MFEM_VERIFY(lower_vec.Size() == lower_ref.Size() &&
upper_vec.Size() == upper_ref.Size(),
"Reference element-bound vectors have inconsistent sizes.");
real_t lower_diff = 0.0;
real_t upper_diff = 0.0;
for (int i = 0; i < lower_vec.Size(); i++)
{
lower_diff = std::max(lower_diff,
std::abs(lower_data[i] - lower_ref_data[i]));
}
for (int i = 0; i < upper_vec.Size(); i++)
{
upper_diff = std::max(upper_diff,
std::abs(upper_data[i] - upper_ref_data[i]));
}
MPI_Allreduce(MPI_IN_PLACE, &lower_diff, 1, MPITypeMap<real_t>::mpi_type,
MPI_MAX, pmesh.GetComm());
MPI_Allreduce(MPI_IN_PLACE, &upper_diff, 1, MPITypeMap<real_t>::mpi_type,
MPI_MAX, pmesh.GetComm());
const real_t verify_tol = std::is_same<real_t, float>::value ?
real_t(1.0e-5) : real_t(1.0e-12);
MFEM_VERIFY(lower_diff <= verify_tol && upper_diff <= verify_tol,
"Device element bounds do not match host reference.");
lowerb = lower_vec;
upperb = upper_vec;
real_t lower_min = lowerb.Min();
real_t upper_max = upperb.Max();
MPI_Allreduce(MPI_IN_PLACE, &lower_min, 1, MPITypeMap<real_t>::mpi_type,
MPI_MIN, pmesh.GetComm());
MPI_Allreduce(MPI_IN_PLACE, &upper_max, 1, MPITypeMap<real_t>::mpi_type,
MPI_MAX, pmesh.GetComm());
if (Mpi::Root())
{
cout << "dim: " << mesh_dim << '\n'
<< "nx: " << nx << '\n'
<< "order: " << order << '\n'
<< "num components: " << num_comp << '\n'
<< "PL bound control-point factor: " << ref << '\n'
<< "iterations: " << niter << '\n'
<< "kernel-only mode: " << (kernel_only ? "yes" : "no") << '\n'
<< "host/device lower max diff: " << lower_diff << '\n'
<< "host/device upper max diff: " << upper_diff << '\n'
<< "global lower bound minimum: " << lower_min << '\n'
<< "global upper bound maximum: " << upper_max << endl;
}
if (visualization)
{
char title1[] = "Random input gridfunction";
char title2[] = "Element-wise lower bound";
char title3[] = "Element-wise upper bound";
VisualizeField(pmesh, input, title1, 0, 0);
VisualizeField(pmesh, lowerb, title2, 450, 0);
VisualizeField(pmesh, upperb, title3, 900, 0);
}
return 0;
}
void VisualizeField(ParMesh &pmesh, ParGridFunction &input,
char *title, int pos_x, int pos_y)
{
socketstream sock;
if (pmesh.GetMyRank() == 0)
{
sock.open("localhost", 19916);
sock << "solution\n";
}
pmesh.PrintAsOne(sock);
input.SaveAsOne(sock);
if (pmesh.GetMyRank() == 0)
{
sock << "window_title '" << title << "'\n"
<< "window_geometry "
<< pos_x << " " << pos_y << " " << 400 << " " << 400 << "\n"
<< "keys jRmclApppppppppppp//]]]]]]]]" << endl;
}
}
+2
View File
@@ -71,10 +71,12 @@ set(UNIT_TESTS_SRCS
linalg/test_ode2.cpp
linalg/test_operator.cpp
linalg/test_particlevector.cpp
linalg/test_petsc_nonlinear.cpp
linalg/test_sparsesmoothers.cpp
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++;
}
+18 -2
View File
@@ -271,6 +271,8 @@ TEST_CASE("Variable Order FiniteElementSpace",
const auto space_type = GENERATE(SpaceType::RT, SpaceType::ND);
const int dim = GENERATE(2, 3);
CAPTURE(space_type);
CAPTURE(dim);
Mesh mesh = MakeCartesianMesh(dim == 2 ? 4 : 2, dim);
mesh.EnsureNCMesh();
@@ -698,7 +700,14 @@ static void TestSolveVec(FiniteElementSpace &fespace)
GridFunction x(&fespace);
x = 0.0;
x.ProjectBdrCoefficient(exsol, ess_attr);
if (x.FESpace()->GetTypicalBE()->GetRangeDim() == 0)
{
x.ProjectBdrCoefficientNormal(exsol, ess_attr);
}
else
{
x.ProjectBdrCoefficientTangent(exsol, ess_attr);
}
// Assemble the linear form
LinearForm lf(&fespace);
@@ -1082,7 +1091,14 @@ static void TestSolveParVec(ParFiniteElementSpace &fespace)
ParGridFunction x(&fespace);
x = 0.0;
x.ProjectBdrCoefficient(exsol, ess_attr);
if (x.FESpace()->GetTypicalBE()->GetRangeDim() == 0)
{
x.ProjectBdrCoefficientNormal(exsol, ess_attr);
}
else
{
x.ProjectBdrCoefficientTangent(exsol, ess_attr);
}
// Assemble the linear form
ParLinearForm lf(&fespace);
+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);
}
}
@@ -0,0 +1,74 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "mfem.hpp"
#include "unit_tests.hpp"
using namespace mfem;
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_PETSC)
namespace
{
struct PetscSession
{
PetscSession() { MFEMInitializePetsc(); }
~PetscSession() { MFEMFinalizePetsc(); }
};
class IdentityGradientOperator : public IdentityOperator
{
public:
IdentityGradientOperator() : IdentityOperator(1), _jac(1)
{
_jac.Add(0, 0, 1.0);
_jac.Finalize();
}
Operator &GetGradient(const Vector &) const override
{
return const_cast<SparseMatrix &>(_jac);
}
private:
SparseMatrix _jac;
};
}
TEST_CASE("PetscNonlinearSolver accepts non-empty rhs", "[Parallel][PETSc]")
{
static PetscSession petsc_session;
IdentityGradientOperator oper;
PetscNonlinearSolver solver(MPI_COMM_WORLD, "nl_");
solver.SetRelTol(1.0e-12);
solver.SetAbsTol(1.0e-12);
solver.SetMaxIter(5);
solver.SetPrintLevel(0);
solver.SetJacobianType(Operator::PETSC_MATAIJ);
solver.SetOperator(oper);
Vector x(1);
Vector empty_rhs;
x = 0.0;
solver.Mult(empty_rhs, x);
REQUIRE(x(0) == MFEM_Approx(0.0));
Vector nonempty_rhs(1);
nonempty_rhs(0) = 2.5;
x = 0.0;
solver.Mult(nonempty_rhs, x);
REQUIRE(x.Size() == 1);
REQUIRE(x(0) == MFEM_Approx(nonempty_rhs(0)));
}
#endif
+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);
}
}
+16 -4
View File
@@ -486,8 +486,14 @@ void multidomain_test_3d(FECType fec_type)
{
cylinder_gf.ProjectCoefficient(vcoeff);
outer_gf.ProjectCoefficient(vcoeff);
outer_gf.ProjectBdrCoefficient(vzerocoeff,
outer_cyl_surf_marker);
if (fec_type == FECType::RT)
{
outer_gf.ProjectBdrCoefficientNormal(vzerocoeff, outer_cyl_surf_marker);
}
else
{
outer_gf.ProjectBdrCoefficientTangent(vzerocoeff, outer_cyl_surf_marker);
}
outer_gf_ex.ProjectCoefficient(vcoeff);
}
ParSubMesh::Transfer(cylinder_gf, outer_gf);
@@ -507,8 +513,14 @@ void multidomain_test_3d(FECType fec_type)
{
outer_gf.ProjectCoefficient(vcoeff);
cylinder_gf.ProjectCoefficient(vcoeff);
cylinder_gf.ProjectBdrCoefficient(vzerocoeff,
cylinder_cyl_surf_marker);
if (fec_type == FECType::RT)
{
cylinder_gf.ProjectBdrCoefficientNormal(vzerocoeff, cylinder_cyl_surf_marker);
}
else
{
cylinder_gf.ProjectBdrCoefficientTangent(vzerocoeff, cylinder_cyl_surf_marker);
}
cylinder_gf_ex.ProjectCoefficient(vcoeff);
}
ParSubMesh::Transfer(outer_gf, cylinder_gf);