Compare commits

...
Author SHA1 Message Date
camierjs 5457706a29 Fix merge with global API 2026-05-01 10:28:29 -07:00
camierjs fd195d53a5 Merge branch 'dfem-multiple-outputs' into dfem-kernels 2026-05-01 10:03:48 -07:00
camierjs ced299b0b3 Remove vscode files 2026-05-01 09:49:26 -07:00
camierjs 667022093d Merge branch 'master' into origin-dfem-kernels 2026-05-01 09:47:16 -07:00
camierjs 5a3e9a93c2 Merge branch 'master' into dfem-multiple-outputs 2026-05-01 09:30:41 -07:00
Julian Andrej b2bf589c87 jit playground updates 2026-04-29 13:09:32 -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
Julian Andrej ac48dfcfa5 update 2026-04-27 16:02:33 -07:00
Tzanio Kolev 7ff0bd3bb0 Merge branch 'master' into dc-ofstream-fix-minor 2026-04-26 14:30:17 -06: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
Julian Andrej 5e98b82b26 skeleton 2026-04-20 14:52:41 -07:00
Julian Andrej dfc582149d move files 2026-04-20 10:27:29 -07:00
Julian Andrej 79680d9bc9 reorganizing dfem backends 2026-04-20 10:21:39 -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
Tzanio Kolev dbbd425a22 Merge pull request #5186 from mfem/particles-pic-dev-pr
Electrostatic PIC
2026-04-14 13:54:29 -06:00
Tzanio Kolev 156f338e49 Merge pull request #4626 from mfem/mfem-v13-mesh-reader-fix-issue-4625
[BUG] Fix for mfem v13 mesh format reader
2026-04-14 13:38:27 -06:00
Will Pazner 53581cb5b7 Merge pull request #5300 from mfem/fix-nvcc-static_cast-warnings
Fix nvcc warnings
2026-04-14 11:04:36 -07:00
camierjs f261928799 Merge branch 'master' into camierjs-dfem-kernels 2026-04-14 08:10:38 -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
camierjs f4baeb41ad Added MMA(magic 4|5) 2026-04-12 11:08:24 -07:00
camierjs fd73f3895f Ini PADiffMmaIntegrator 2026-04-12 09:24:21 -07:00
camierjs 5820d70a50 remove unused variable 2026-04-11 14:33:58 -07:00
camierjs d923432284 Use T_Q1D for offset computation 2026-04-11 14:29:20 -07:00
camierjs cffd9c618f Reorder kernels & add fallbacks 2026-04-11 14:20:43 -07:00
camierjs 6ccc490e18 Specialization only on Q1D for dFEM 2026-04-11 13:51:34 -07:00
camierjs 80f973d708 Swap DIM as fastest indice in shared mem 2026-04-11 13:35:14 -07:00
camierjs ef13f6e9d8 Specialization only on Q1D 2026-04-11 13:24:16 -07:00
camierjs 48cb7bcc2e Cleanup 2026-04-11 13:18:21 -07:00
camierjs 621a9ed1d6 Low @ 4.43378k/s 2026-04-11 13:01:34 -07:00
camierjs 5ca23fd56f Fix PADiffLowMult 2026-04-11 11:31:35 -07:00
camierjs 1b9d97ec28 Fix CPU regs3d_t layout 2026-04-11 11:14:17 -07:00
camierjs 4add547b04 cleanup 2026-04-11 08:32:50 -07:00
camierjs 9c9b5cee2f dFEM new kernels with tensors from raw pointers @ 4.2k/s 2026-04-11 08:21:15 -07:00
camierjs 80b5ecfb7b Add back PA ∂fem std kernels 2026-04-10 17:57:43 -07:00
camierjs 074a716634 Re-enable specialized kernels and add #7 w/o specilization 2026-04-10 17:14:12 -07:00
camierjs ad9682118d wip QF apply LOW kernels 2026-04-10 17:06:19 -07:00
camierjs 25cba3afa5 Cleanup, inc kernels3d header file 2026-04-10 14:58:55 -07:00
camierjs 76f061dd9c Low running on GPU 2026-04-10 14:16:04 -07:00
camierjs 22f25269a5 wip low kernels 2026-04-10 13:19:47 -07:00
camierjs 684d8fc64b WIP LOW reg Grad 3D 2026-04-10 11:34:59 -07:00
camierjs ded0173a92 wip MFEM_SHARED full d0_regs3d_t 2026-04-09 13:40:57 -07:00
camierjs 7aca674961 Added #6 PADiffLowIntegrator 2026-04-09 11:58:10 -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
camierjs be6f6299aa Bring smem version 2026-04-08 16:57:34 -07:00
Andrew Ho 6c5f513eaa Merge branch 'master' into fix-umpire-dep 2026-04-08 16:37:59 -07:00
camierjs c81fabcc54 BP3/1/6/160 @ 4k/s 2026-04-08 15:50:23 -07:00
camierjs f963bd897b wip forall_2D_batch, constant memory 2026-04-08 15:13:04 -07:00
camierjs dcd5bee0f6 wip forall_2D_batch tries 2026-04-08 13:02:14 -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
camierjs 1dd20334e2 Add forall_kernel_static_smem_launch_bounds 2026-04-07 16:35:07 -07: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
camierjs 8889988956 Tuos runs w/ new kernels 2026-04-07 10:21:12 -07:00
camierjs 06b4a68c7d dFEM new kernels hc vdim, same B/G outputs 2026-04-07 09:52:07 -07:00
camierjs 17e15d08cd Merge branch 'main' of github.com:camierjs/mfem-dfem-kernels into main 2026-04-07 08:13:53 -07:00
camierjs c05ce7dacd R Identity 2026-04-07 08:13:51 -07:00
camierjs c80f7fb1f1 json update 2026-04-07 08:12:32 -07:00
camierjs 0eac62aa3b wip restriction D2D 2026-04-07 07:07:58 -07:00
camierjs fbe07d97ea All but MF new kernels 2026-04-07 05:58:47 -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
camierjs e4ce8375f3 Reuse qdata for PA ∂fem new kernels 2026-04-06 18:06:09 -07: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
camierjs da959ef7e9 Add back mi300a constant B & G 2026-04-03 20:19:32 -07:00
camierjs aba87d9e14 Add user cmake tuo 2026-04-03 19:27:40 -07:00
camierjs f0efcf4253 nvcc compilations 2026-04-03 19:18:04 -07:00
camierjs 7aee5f56ba Rename user cmake file 2026-04-03 18:22:16 -07:00
camierjs 9bd3409458 Add darwin/matrix user cmake files 2026-04-03 18:21:46 -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
camierjs cca1678ccb Add vscode files 2026-04-03 09:32:34 -07:00
camierjs 97ca2f9ecc Fix dFEM new action for exact CG iterations 2026-04-03 09:16:37 -07: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
camierjs a00f222761 dFEM kernels CPU runs 2026-04-02 17:48:30 -07:00
camierjs 99ebc58be4 wip merge fixes 2026-04-02 11:47:40 -07:00
camierjs cf213ea6b6 Merge branch 'master' into camierjs-dfem-kernels 2026-04-02 10:21:43 -07:00
camierjs 031be712a1 Warnings fix 2026-04-02 10:13:53 -07:00
camierjs 134bc32d93 Merge branch 'origin-dfem-kernels' into camierjs-dfem-kernels 2026-04-02 10:05:30 -07:00
camierjs e640a3e3fb Cleanup and run with new traces 2026-04-02 09:49:26 -07:00
Julian Andrej faba224c26 jit playground 2026-04-02 08:34:33 -07: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
Giorgis Georgakoudis 485121d3ad Use proteus::jit_arg instrumentation 2026-03-30 16:55:34 -07:00
Giorgis Georgakoudis af527e27d1 Update top-level CMakeLists.txt for proteus
- Add target-based path for libProteusPass
- Link with libproteus
2026-03-30 16:51:52 -07:00
Julian Andrej 3e680af733 disable derivatives temporarily 2026-03-30 12:41:16 -07:00
Julian Andrej 4212310405 add proteus 2026-03-30 12:20:44 -07: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
Julian Andrej 41aed0e916 cosmetic changes 2026-03-12 08:42:35 -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
Julian Andrej 9bf156adf2 bugfix 2026-03-10 09:18:45 -07:00
Julian Andrej 9f0fcd6b10 custom layouts 2026-03-10 08:10:38 -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
Julian Andrej 2350a5e9eb typo 2026-03-05 10:52:39 -08:00
Julian Andrej 001c686a19 make rank 0 tensor compatible with real_t 2026-03-05 10:50:33 -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
Julian Andrej da9fc85862 support MultiVector 2026-03-04 13:32:19 -08:00
Julian Andrej 996553be3d simplify assert 2026-03-04 12:53:26 -08:00
Julian Andrej ff6715b8b1 Merge branch 'multi-vector-dev' into dfem-multiple-outputs 2026-03-04 12:46:47 -08: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
Julian Andrej 54acbdd395 consistency checks 2026-03-04 10:35:30 -08: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
Julian Andrej 76d4f1942b refactor how bases are created 2026-03-04 07:36:19 -08:00
Julian Andrej 939310203d updates 2026-03-03 15:02:16 -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
Julian Andrej 979f08b3eb allow Q-function arguments to be non-const references 2026-03-02 09:14:15 -08:00
Tzanio Kolev 5ce6e90ceb Merge pull request #5253 from mfem/bugfix-nurbs-orientation
NURBS orientation bug fix
2026-03-01 11:46:34 -08:00
Tzanio Kolev 03ba184adb Merge pull request #4882 from mfem/debug-mem-silent
Add option to run debug memory backend without issuing errors
2026-03-01 11:46:15 -08:00
Tzanio Kolev cfa87477da Merge pull request #5248 from lindsayad/dont-do-math-with-enums
Don't do arithmetic with enums
2026-03-01 11:45:47 -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
dylan-copeland 939932bc68 Merge branch 'master' of github.com:mfem/mfem into bugfix-nurbs-orientation 2026-02-27 13:59:37 -08:00
Tzanio Kolev d3ae34710c Merge pull request #5195 from mfem/flip-index
Index sign functions
2026-02-27 13:32:22 -08:00
Dylan Copeland 9b652996b2 Remove an unnecessary assertion. 2026-02-27 11:48:01 -08:00
Dylan Copeland 5cfbbe5fad Bug fix for NURBS orientation, when the same knotvector index is used on all edges of some patches. 2026-02-27 11:24:36 -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
Veselin Dobrev 1ed3b48c2e In class MultiVector, remove the need for Memory flag synchronizations
in some cases. This required changes in the internals of the class.

Added some new methods in class MultiVector.
2026-02-26 09:57:21 -08: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
Tzanio Kolev 71909cd5e3 Merge pull request #5203 from mfem/bugfix/unit-test-cout
Changing std::cout to mfem::out in unit test
2026-02-26 05:50:05 -08:00
Veselin Dobrev fbd9189e7b Restrist with 'enable_if' the variadic template MultiVector ctor and
MakeRef method to be considered only when the arg types are convertible
to (Vector &).
2026-02-25 19:17:07 -08:00
Veselin Dobrev 1dd889cb16 Add support for constructing and re-constructing MultiVectors to reference
multiple Vectors given as arguments.
2026-02-25 17:44:31 -08:00
Veselin Dobrev 2e8fbd661a Fix a warning in a miniapp. 2026-02-25 14:56:28 -08:00
Veselin Dobrev 6e424dba6e Draft implementation of an array-of-Vectors class where each Vector generally
has a different size and is allocated independently.

The tentative name for the new class is MultiVector.

In class Operator, added new virtual methods Mult() and GetGradient() that
use MultiVectors.
2026-02-25 13:51:43 -08:00
Alex Lindsay a5a3169064 Don't do arithmetic with enums
Else with gcc 13.3 with `-std=c++20` I get warnings
2026-02-25 13:35:10 -07: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
Dylan Copeland afded067a7 Replace absdof. 2026-02-24 11:17:54 -08: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
John Camier 629e93afd9 Merge branch 'master' into debug-mem-silent 2026-02-24 09:20:59 -08:00
Tzanio Kolev 3e277808a9 Merge pull request #5210 from mfem/hughcars/array-move-assignment-bugfix
Fix Array non-owning move assignment
2026-02-24 08:41:48 -08:00
Tzanio Kolev 864fb1ce9e Merge pull request #5217 from mfem/quadrature-function-fix
Fix a bug in `QuadratureFunction::GetValues` that returns a `DenseMatrix` view
2026-02-24 08:40:53 -08:00
Tzanio Kolev ac7415cc69 Merge branch 'master' into bugfix/unit-test-cout 2026-02-24 08:38:11 -08:00
Veselin Dobrev 38030d4395 Merge pull request #5222 from mfem/bugfix/chapman39/rm-return-from-omp
Remove return from openmp section
2026-02-24 08:18:53 -08:00
Julian Andrej 60a04e4e4f qdata L to Q 2026-02-23 17:22:52 -08:00
Julian Andrej 8d95a6e5ca qdata 2026-02-23 16:51:58 -08:00
Julian Andrej 10cb466fb2 more stuff 2026-02-23 14:14:41 -08:00
Alex Tyler Chapman 2c96dc6a1f Merge branch 'master' into bugfix/chapman39/rm-return-from-omp 2026-02-23 11:53:58 -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
Julian Andrej 5be9de7e95 bugfixes 2026-02-23 09:15:17 -08:00
John Camier ecbc7bf8c2 Merge branch 'master' into bugfix/unit-test-cout 2026-02-23 08:00:52 -08:00
John Camier 5b5a21edac Merge branch 'master' into debug-mem-silent 2026-02-22 12:28:39 -08:00
Tzanio Kolev 1f84ba036e Merge pull request #5179 from izaid/pfes
Fixed NULL pointer segfault in GetSurfaceFittingErrors
2026-02-21 12:52:33 -08:00
Tzanio Kolev 76d0312309 Merge branch 'master' into hughcars/array-move-assignment-bugfix 2026-02-21 12:52:11 -08:00
Tzanio Kolev c7ed339260 Merge pull request #5228 from mfem/mesh-3d-part-fix
Improve 3d mesh partitions
2026-02-21 12:43:49 -08:00
Tzanio Kolev f1b3a33fb2 Merge pull request #5234 from mfem/few-small-fixes
A few small fixes
2026-02-21 12:43:02 -08:00
rzhangbq 9e261aeb36 format 2026-02-20 18:06:33 -05:00
rzhangbq 3fe3c00c72 format 2026-02-20 18:05:51 -05:00
Julian Andrej a13a4f4d8b more 2026-02-20 14:14:10 -08: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
Julian Andrej bf9b6f4d83 multiple outputs with derivatives 2026-02-19 12:51:30 -08: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
Veselin Dobrev e3dedbbd5b Increase a relative tolerance due to failures on some machines 2026-02-18 10:27:44 -08: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
John Camier 261f3805b8 Merge branch 'master' into debug-mem-silent 2026-02-17 10:59:40 -08:00
John Camier b386b2d6b6 Merge branch 'master' into bugfix/chapman39/rm-return-from-omp 2026-02-17 08:28:34 -08:00
Tzanio Kolev 6e98055eb7 Merge branch 'master' into quadrature-function-fix 2026-02-17 08:28:12 -08:00
Tzanio Kolev 2b14134496 Merge branch 'master' into najlkin/fix-cmplx-assign 2026-02-17 08:27:01 -08:00
Tzanio Kolev 5abd44f212 Merge pull request #5227 from mfem/add-constexpr
Add if constexpr to Kernel Specializations
2026-02-17 08:25:11 -08:00
Veselin Dobrev 75012728db Formatting: re-wrap comment to 80 chars/line. 2026-02-14 13:10:30 -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
Dylan Copeland 1f094244f8 Typo 2026-02-13 10:37:58 -08:00
Dylan Copeland cc16ddadbf Merge branch 'master' of github.com:mfem/mfem into flip-index 2026-02-13 10:35:32 -08:00
Veselin Dobrev 8acd5cd3a2 Fix a size bug in thread-safe mode in H1_TriangleElement::CalcHessian.
Fix use-after-delete bug in nurbs_ex10p.cpp.

Use relative tolerance in the "Collocated Derivative Kernels" unit test
to resolve failures in some setups with the original absolute tolerance.
2026-02-13 10:29:22 -08:00
chapman39 1f5bc1c3d8 wording 2026-02-13 10:03:00 -08:00
chapman39 3588d47ec1 Merge remote-tracking branch 'origin/master' into bugfix/chapman39/rm-return-from-omp 2026-02-13 09:59:31 -08:00
Andrew Ho e7f5996bdf fix warnings with some compilers 2026-02-13 09:28:16 -08:00
Tzanio Kolev 560ad1b5a3 Merge pull request #5157 from mfem/plbound-extremum
Estimate function minimum/maximum using recursion + piecewise linear bounds
2026-02-13 07:08:57 -08:00
Alex Tyler Chapman f0fe5b0ec0 Merge branch 'master' into bugfix/chapman39/rm-return-from-omp 2026-02-11 15:14:48 -08:00
chapman39 a92983051a style 2026-02-11 15:14:23 -08: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
Will Pazner 37843b050c Add one more 'if constexpr' 2026-02-11 11:33:51 -08: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
camierjs 436714f5ef Revert unused included header 2026-02-11 11:18:02 -08: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
camierjs 1ceef4f786 Add few missing Kernel() constexpr 2026-02-11 11:11:35 -08: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
Vladimir Z Tomov 6bb6745c0e improve 3d partitions 2026-02-10 16:23:38 -08:00
Mittal, Ketan 60f47c287d CHANGELOG 2026-02-10 14:28:17 -08: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
Mittal, Ketan 164ee942c8 Merge branch 'plbound-extremum' of https://github.com/mfem/mfem into plbound-extremum 2026-02-10 10:59:44 -08:00
Mittal, Ketan 16c4fbdd29 reviewer comments 2026-02-10 10:59:33 -08:00
Ketan Mittal e28093274b Merge branch 'master' into plbound-extremum 2026-02-09 19:06:18 -08:00
Andrew Ho 87dd19e6c0 more constexprs 2026-02-09 17:35:17 -08:00
Andrew Ho 678f53c306 use constexpr to prevent unintended kernel Pinstantiations 2026-02-09 17:17:26 -08:00
Julian Andrej 52b8703b78 bugs 2026-02-06 16:26:43 -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
Julian Andrej 69e7820d01 phew 2026-02-06 15:05:03 -08:00
chapman39 32fb4bf244 remove return from openmp section 2026-02-05 14:56:40 -08:00
Julian Andrej dbedeecece more refactor 2026-02-05 09:58:24 -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
Julian Andrej e8847b80a2 refactor 2026-02-04 13:25:00 -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
Veselin Dobrev 2d021685de Fix a bug in QuadratureFunction::GetValues when getting a reference to
the data for one mesh element as a DenseMatrix.

Added new methods:
- Array<T>::MakeRef(Memory<T> &base, int offset, int size_)
- DenseMatrix::MakeRef(Memory<real_t> &base, int offset, int h, int w)
2026-02-02 08:42:58 -08: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
Hugh Carson d6ea262498 Address PR feedback
- Hoist src deletion
- Remove unneeded explicit cast given src is named
2026-01-30 14:01:59 -05:00
Andrew Ho 26e9057f02 revert change, updated comment to why libdl gets special treatment 2026-01-30 07:27:04 -08:00
Dylan Copeland 6b147fd9ff Merge branch 'master' of github.com:mfem/mfem into flip-index 2026-01-29 15:45:52 -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
Hugh Carson a0981cb363 Refactor test for showing equivalency of copy and move assignment 2026-01-27 14:15:08 -05:00
Mittal, Ketan 1fd8301d38 Merge branch 'particles-pic-dev-pr' of https://github.com/mfem/mfem into particles-pic-dev-pr 2026-01-27 11:03:35 -08:00
Mittal, Ketan a013a150c1 include ordering argument in Interpolate 2026-01-27 11:03:27 -08:00
Hugh Carson 894de992da Move assignment of non-owned arrays must fallback to copy assignment 2026-01-27 14:01:23 -05: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
Julian Andrej 0fe2aece0b enable multiple outputs 2026-01-26 14:58:58 -08: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
Tzanio Kolev 942249395b Merge branch 'master' into debug-mem-silent 2026-01-24 11:40:50 -08:00
rzhangbq 7f5b68dfbd remove redundant findpoints 2026-01-23 17:28:53 -05:00
Stowell, Mark L. a3f6d5b971 Changing std::cout to mfem::out 2026-01-22 15:37:51 -08: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
Ketan Mittal 5546250963 Merge branch 'master' into plbound-extremum 2026-01-21 11:00:02 -08: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
John Camier 5b065ad7f2 Merge branch 'master' into debug-mem-silent 2026-01-20 17:59:07 -08:00
Ketan Mittal 7f4d7b8f4e Merge branch 'master' into plbound-extremum 2026-01-16 12:54:56 -08: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
Mittal, Ketan 6529372830 reviewer comments 2026-01-15 13:10:47 -08: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 d66d799387 minor 2026-01-14 15:25:11 -08:00
Mittal, Ketan 51f205b273 reviewer comments 2026-01-14 14:14:31 -08:00
Dylan Copeland 0161ad9d92 Minor simplifications. 2026-01-14 12:22:47 -08:00
Dylan Copeland 3589479481 Merge branch 'master' of github.com:mfem/mfem into flip-index 2026-01-14 12:10:30 -08:00
Dylan Copeland e9acfeccda Using new sign functions. 2026-01-14 12:10:07 -08: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 ad7cf12cd5 Merge branch 'master' into plbound-extremum 2026-01-13 12:24:55 -08: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
Ketan Mittal abdb023ae3 Merge branch 'master' into plbound-extremum 2026-01-12 10:12:14 -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
izaid 5cd3ec521b applied astyle 2026-01-08 23:19:16 +00: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
Mittal, Ketan d1a9c6e62d format miniapp output 2026-01-08 10:53:49 -08:00
Mittal, Ketan 8f0b57138b Merge branch 'master' of https://github.com/mfem/mfem into plbound-extremum 2026-01-08 10:37:40 -08:00
Mittal, Ketan 3167a1c98b remove default value from tol in pgridfunc.hpp 2026-01-08 10:31:19 -08:00
Tzanio Kolev 9488637956 Merge branch 'master' into pfes 2026-01-07 14:20:49 -08:00
Mittal, Ketan f429737c12 change 0.0 to 0_r 2026-01-06 12:22:27 -08:00
Andrew Ho ad40704e20 Merge branch 'master' into cmake-config 2026-01-06 11:55:40 -08:00
Mittal, Ketan 04fd683e9c doxygen fix 2026-01-06 11:38:42 -08:00
Mittal, Ketan 4b9f46a6b0 Merge branch 'plbound-extremum' of https://github.com/mfem/mfem into plbound-extremum 2026-01-06 11:30:04 -08:00
Mittal, Ketan 793a5b6d60 minor 2026-01-06 11:29:47 -08:00
Mittal, Ketan 4e6e9a13b6 Merge branch 'master' of https://github.com/mfem/mfem into plbound-extremum 2026-01-06 11:15:53 -08:00
Dylan Copeland 6f280d81b5 Introducing new functions for flipping index signs. 2026-01-05 18:46:25 -08:00
izaid 90353c437e Fixed bug in GetSurfaceFittingErrors 2026-01-01 23:09:09 +01:00
John Camier fbb50af208 Merge branch 'master' into debug-mem-silent 2025-12-30 12:50:33 -08:00
Andrew Ho d3470c07c9 Merge branch 'master' into cmake-config 2025-12-16 12:05:27 -08:00
Ketan Mittal 5b917af59b Merge branch 'master' into plbound-extremum 2025-12-15 12:59:44 -08:00
Mittal, Ketan f956c6b2de function for pargridfunction 2025-12-14 16:33:38 -08:00
Mittal, Ketan 62dbc570b2 add functions to compute min/max over all elements 2025-12-14 15:57:05 -08:00
Mittal, Ketan c221f5a29d initial commit 2025-12-13 15:20:43 -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
John Camier ce8cd01cfd Merge branch 'master' into debug-mem-silent 2025-10-28 10:20:51 -07:00
John Camier a60baf8ce6 Merge branch 'master' into debug-mem-silent 2025-10-25 11:28:07 -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
John Camier 96bba18449 Merge branch 'master' into debug-mem-silent 2025-10-20 09:36:28 -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
John Camier f9cce3ab62 Merge branch 'master' into debug-mem-silent 2025-09-29 09:25:14 -07:00
John Camier 292700bb52 Merge branch 'master' into debug-mem-silent 2025-09-25 15:58:22 -07:00
John Camier fd2f0df34f Merge branch 'master' into debug-mem-silent 2025-09-21 20:13:41 -07:00
John Camier 4ac41a6427 Merge branch 'master' into debug-mem-silent 2025-09-21 10:27:05 -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
Tzanio Kolev 58826d64c9 Merge branch 'master' into debug-mem-silent 2025-08-31 15:32:36 -07:00
Victor DeCaria a126203ccd implement suggested change 2025-08-21 15:08:57 -06:00
victor-decaria-nnl 6656a7ef72 Merge branch 'master' into debug-mem-silent 2025-08-20 15:50:34 -04: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
camierjs b639394d56 Cleanup 2025-07-10 08:26:00 -07:00
camierjs 12c2d71bd2 Merge branch 'master' into dfem-kernels 2025-07-10 08:25:18 -07:00
camierjs c6f7e9f635 Switched VDIM/DIM dimensions runs 2025-07-07 13:56:11 -07:00
camierjs 7151713d9c Try StiffnessMult with VDIM/DIM layout 2025-07-07 10:10:06 -07:00
camierjs a5379de077 Use constants, simplify & cleanup 2025-07-06 16:29:53 -07:00
camierjs 85d24f1354 Merge branch 'master' into dfem-kernels 2025-07-06 14:45:43 -07:00
camierjs b60a76f0db Cleanup 2025-07-06 14:45:06 -07:00
camierjs e0dc7659fb With specializations 2025-07-06 11:56:22 -07:00
camierjs b7b4268138 use_kernels_specialization 2025-07-05 21:11:17 -07:00
camierjs e89a61399c GPU runs 2025-07-05 15:57:31 -07:00
camierjs 6c90686880 Forced inlines w/o changes 2025-07-05 15:12:09 -07:00
camierjs 331a66c027 Cleanup 2025-07-05 14:51:59 -07:00
camierjs 6d509fa5c3 Cleanup 2025-07-05 14:40:23 -07:00
camierjs b62cf39359 Simplify 2025-07-05 14:34:58 -07:00
camierjs 49f44e65c0 Cleanup & Simplify 2025-07-05 12:04:54 -07:00
camierjs 32697fea42 cleanup 2025-07-05 11:37:11 -07:00
camierjs c80a091f56 w/o unpack_shmem 2025-07-05 10:56:43 -07:00
camierjs bbc6708976 with r2 2025-07-05 10:46:52 -07:00
camierjs 6a01f6551a Action 2025-07-05 10:17:05 -07:00
camierjs 0c663a8aa2 with process_qf_result 2025-07-05 08:28:27 -07:00
camierjs be224ed94a wip apply_kernel 2025-07-05 08:17:17 -07:00
camierjs c09da71078 wip back action_callback_new 2025-07-04 18:07:15 -07:00
camierjs cf5f0126ce Avoid double mdofs in first iteration 2025-07-04 17:15:46 -07:00
camierjs a23e8907d7 removed fqp and use directly r0 2025-07-04 17:03:04 -07:00
camierjs 4ca2805303 map_quadrature_data_to_fields 2025-07-04 14:14:08 -07:00
camierjs 1b775faa43 is_gradient_fop 2025-07-04 13:38:00 -07:00
camierjs 0edefaeae5 action_callback_new cleanup 2025-07-04 12:50:59 -07:00
camierjs a12132ccb6 MFEM_NEW_KERNELS & action_callback_new 2025-07-04 11:30:30 -07:00
camierjs 9f044d89b5 bench_dfem run with assert Grad diff 2025-07-04 09:51:07 -07:00
camierjs 191e3df84b LoadDofs3d, Grad3d 2025-07-04 09:45:34 -07:00
camierjs 6b6f8afdac wip sync 2025-07-04 09:11:58 -07:00
camierjs 25e333dbf4 Merge branch 'dfem-bench' 2025-07-04 08:12:03 -07:00
camierjs 856d13e9ff Roctx init 2025-07-04 08:00:50 -07:00
camierjs eb8f7f433c Run tests/unit/dfem/test_diffusion_q1d 2025-07-02 17:00:41 -07:00
camierjs 6a693a818f wip merge fix 2025-07-02 15:05:47 -07:00
camierjs fa7d81095a Merge branch 'master' into dfem-kernels 2025-07-02 15:05:30 -07:00
camierjs 16260082f6 wip interpolate 2025-07-02 14:52:18 -07:00
camierjs 7763785ed7 Use MFEM_FOREACH_THREAD_DIRECT 2025-07-02 10:29:40 -07:00
camierjs 2baa889917 Merge branch 'master' into dfem-bench 2025-07-02 08:38:17 -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
John Camier 8804df317d Merge branch 'master' into debug-mem-silent 2025-07-01 09:51:06 -07:00
camierjs 2ed1a9eaad BP3/1/6/25 @ 40 MDof/s 2025-06-30 18:02:56 -07:00
camierjs 1545f03a94 Merge branch 'master' into dfem-bench 2025-06-30 16:19:53 -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
camierjs 59a5c9fc79 Sync with fem/kernels.hpp, still performance wip 2025-06-24 11:43:05 -07:00
camierjs c389a3c434 Use latest dFEM for benchmark 2025-06-24 11:27:34 -07:00
camierjs ec96a85f86 Merge branch 'master' into dfem-bench 2025-06-24 11:27:15 -07:00
John Camier 95408b0fae Merge branch 'master' into debug-mem-silent 2025-06-22 20:25:51 -07:00
John Camier 79819a5563 Merge branch 'master' into debug-mem-silent 2025-06-18 10:22:04 -07: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
John Camier d57fc7c0d9 Merge branch 'master' into debug-mem-silent 2025-06-16 08:33:43 -07:00
John Camier 9fb590d79d Merge branch 'master' into debug-mem-silent 2025-06-13 08:48:05 -07:00
Victor DeCaria cb4ca9228f add option to disable protection with debug backend 2025-06-05 09:34:25 -06: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
camierjs dd99371cda dFEM diffusion test Identity vs. None fix 2025-05-19 16:48:56 -07:00
camierjs 90aa6fc544 Merge branch 'dfem-phase1-dev' into dfem-kernels 2025-05-19 16:44:02 -07:00
camierjs 7b4fcc3e52 Add qp wip header/test 2025-05-19 16:42:15 -07:00
camierjs 81b6b7eeb2 Merge branch 'master'/'dfem-phase-1' into dfem-bench 2025-05-19 16:02:46 -07:00
camierjs 4b5974f600 Fix CMake and dFEM bench 2025-05-19 16:01:16 -07:00
camierjs a6926f4ce6 Merge branch 'dfem-phase1-dev' 2025-05-19 15:54:58 -07:00
camierjs b6e972af79 Merge branch 'master' 2025-05-19 15:49:54 -07:00
Julian Andrej e76ec19775 restructure 2025-05-19 14:46:20 -07:00
Julian Andrej d797322fea path 2025-05-19 08:22:36 -07:00
Julian AndrejandJohn Camier 5a5e34a744 Update fem/dfem/doperator.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2025-05-19 08:11:13 -07:00
Julian AndrejandJohn Camier 93db7052ff Update fem/dfem/util.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2025-05-19 08:10:44 -07:00
Julian AndrejandJohn Camier f7170af7bd Update fem/dfem/tuple.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2025-05-19 08:10:12 -07:00
Julian AndrejandJohn Camier b78eef3eaa Update fem/dfem/tuple.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2025-05-19 08:10:00 -07:00
Julian Andrej d5decea85c Revert "change default location for enzyme and add instructions"
This reverts commit dea3ae3317.
2025-05-16 12:59:14 -07:00
Julian Andrej dea3ae3317 change default location for enzyme and add instructions 2025-05-16 12:55:04 -07:00
Julian Andrej 33c1e50235 astyle 2025-05-16 12:39:47 -07:00
Julian Andrej 5718ad1b53 cuda compat 2025-05-16 12:38:08 -07:00
Julian AndrejandAndrew Ho 4f3671e253 Update fem/dfem/util.hpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-05-16 11:58:50 -07:00
Julian AndrejandAndrew Ho 4e08bb1b66 Update fem/dfem/util.hpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-05-16 11:58:42 -07:00
Julian AndrejandAndrew Ho 69c5016b63 Update fem/dfem/util.hpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-05-16 11:58:20 -07:00
Julian AndrejandAndrew Ho ce1bf58dc0 Update fem/dfem/doperator.hpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-05-16 11:58:13 -07:00
Julian AndrejandAndrew Ho 5eb00c9ee6 Update fem/dfem/doperator.hpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-05-16 11:58:05 -07:00
Julian AndrejandAndrew Ho 1f3b6b95aa Update fem/dfem/util.hpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-05-16 11:57:58 -07:00
Julian AndrejandAndrew Ho 118db41049 Update fem/dfem/util.hpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-05-16 11:57:49 -07:00
Julian AndrejandAndrew Ho 8390c3e50b Update fem/dfem/doperator.hpp
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-05-16 11:57:40 -07:00
Julian Andrej 168b5179e6 remove findenzyme module 2025-05-16 11:57:11 -07:00
Julian AndrejandAndrew Ho 7697f6d400 Update CMakeLists.txt
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-05-16 11:56:11 -07:00
Julian AndrejandJan Nikl 235ebce5d5 Update examples/dfem/minimal_surface.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-05-16 07:58:47 -07:00
Julian AndrejandJan Nikl e8a09d6499 Update examples/dfem/minimal_surface.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-05-16 07:57:47 -07:00
Julian AndrejandJan Nikl edc67827d8 Update examples/dfem/minimal_surface.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-05-16 07:57:34 -07:00
Julian Andrej 9e5cdef2ef add host device 2025-05-14 17:39:40 -07:00
Andrew Ho c2f4a5e248 Updated makefile to work with clang as the cuda compiler 2025-05-14 11:25:59 -07:00
Julian Andrej 72d811b289 device support for fdjacobian 2025-05-14 09:57:18 -07:00
Julian Andrej 43731aa990 memory type for temporary 2025-05-14 09:46:39 -07:00
Julian Andrej 45f59fff3a device memory locations 2025-05-14 09:23:58 -07:00
Julian Andrej 58a4cfa132 cuda compat 2025-05-14 07:43:13 -07:00
Julian Andrej 333dd3f2fd rename ParametricSpace -> ParameterSpace 2025-05-13 13:29:38 -07:00
Julian Andrej c4f7dd77b1 bugs 2025-05-13 13:24:18 -07:00
Julian Andrej f442b83573 whitespace 2025-05-13 11:33:54 -07:00
Julian Andrej 768aaae25d docs 2025-05-13 11:27:36 -07:00
Julian Andrej eab997c557 typo 2025-05-13 11:26:09 -07:00
Julian Andrej 9d73dc487d docs 2025-05-13 11:24:36 -07:00
Julian Andrej 2575ac61ba more comments 2025-05-13 09:08:54 -07:00
Julian Andrej 6130144da1 comments 2025-05-13 08:46:15 -07:00
camierjs 68db31da44 SetMaxOf comments 2025-05-12 18:15:09 -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
Julian Andrej 1acbce733c cmake 2025-05-09 11:46:10 -07:00
Julian Andrej b44316049b cmake 2025-05-09 10:41:11 -07:00
Julian Andrej 2e133e8ecb remove serial tests from cmake 2025-05-09 10:36:37 -07:00
Julian Andrej dfb2f4d7f2 typos 2025-05-09 08:41:17 -07:00
Julian Andrej 10e9e4215f cmake 2025-05-09 08:38:46 -07:00
Julian Andrej 8125a211d3 Merge branch 'master' into dfem-phase1-dev 2025-05-08 09:40:48 -07:00
Julian Andrej 818b8db433 switch example to CG 2025-05-07 17:19:36 -07:00
Jan Nikl 8a522f5e7d Fixed submesh unit test. 2025-05-07 16:26:34 -07:00
Julian Andrej ad4626edfc leftover comment 2025-05-07 15:51:29 -07:00
Julian Andrej 8d7e8933cf mesh 2025-05-07 15:50:43 -07:00
Julian Andrej 3ad21a409f precision 2025-05-07 15:31:39 -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
Julian Andrej b16b550150 corrections 2025-05-07 15:08:11 -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
Julian Andrej 102dc8bd02 ifdef 2025-05-07 14:20:41 -07:00
Julian Andrej e306ba0c85 ifdef 2025-05-07 13:44:30 -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
Julian Andrej 4b88ad2b0a more minsurface 2025-05-07 13:19:28 -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
Julian Andrej d0fb4e342e example draft 2025-05-06 21:06:56 -07:00
Julian Andrej b53d0529db bug 2025-05-06 17:41:48 -07:00
Julian Andrej 8c7988b525 changes 2025-05-06 17:41:22 -07:00
Julian Andrej dfffe4b5e8 rename fops 2025-05-06 09:12:33 -07:00
Julian Andrej 538aa11904 rename fops 2025-05-06 08:56:20 -07:00
Julian Andrej 6fa978af9a rename fops 2025-05-06 08:53:31 -07:00
Julian Andrej 3f81af72f6 rename fops 2025-05-06 08:51:06 -07:00
Julian Andrej 97f1cf08fb docs 2025-05-05 13:45:03 -07:00
camierjs e047cec18a All 3 MQ1Settings working 2025-05-03 14:30:34 -07:00
camierjs bdcf59d109 make_qf_map 2025-05-03 13:48:03 -07:00
Tzanio Kolev d3f1379dc8 Merge branch 'master' into dfem-phase1-dev 2025-05-03 13:46:43 -07:00
camierjs b876d32452 Pre cleanup MQ1 on qfunction 2025-05-03 13:06:34 -07:00
camierjs 50a6be3d58 wip runtime_get 2025-05-03 10:49:25 -07:00
camierjs 5251db2278 All interpolate gradient tests 2025-05-02 17:31:40 -07:00
camierjs 92fca7cf01 Interpolate all Gradient but toroid mesh 2025-05-02 17:27:54 -07:00
camierjs e22f5bc048 Interpolate Gradient AlmostEq 2025-05-02 17:17:22 -07:00
camierjs f971d1e0bb Merge branch 'dfem-phase1-dev' 2025-05-02 15:29:05 -07:00
camierjs 752917acaa Rename Diffusion PA kernels
dFEM DOperator debug traces
2025-05-02 15:27:59 -07:00
Julian Andrej ea6fb52698 bug 2025-05-02 13:09:47 -07:00
Julian Andrej 07a87e369c style 2025-05-02 12:01:08 -07:00
camierjs 56c46e6da5 Remove MFEM_FOREACH_THREAD1 2025-05-02 11:23:27 -07:00
camierjs 2db4ca1300 Avoid MFEM recompilation with dFEM changes 2025-05-02 11:19:48 -07:00
camierjs 53bc415268 Squashed commit of the following:
commit be537728df
Merge: 8cc9eec53 4e5b98b10
Author: camierjs <camierjs@gmail.com>
Date:   Fri May 2 10:37:34 2025 -0700

    Merge branch 'dfem-phase1-dev' into dfem-bench

commit 4e5b98b10f
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Fri May 2 10:05:20 2025 -0700

    doc

commit d4acd906bf
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Fri May 2 09:01:51 2025 -0700

    update brew before enzyme install

commit d751ce66a3
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Fri May 2 08:43:46 2025 -0700

    ci

commit 3f0abd4dfd
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Fri May 2 08:40:45 2025 -0700

    ci

commit 44a423d804
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Fri May 2 08:39:58 2025 -0700

    ci

commit 3e61e0490e
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Fri May 2 08:38:47 2025 -0700

    ci

commit def4919592
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Fri May 2 08:33:33 2025 -0700

    ci config

commit 2d147d70e0
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Fri May 2 08:33:29 2025 -0700

    reintroduce tests

commit e29e64dffe
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Fri May 2 08:04:44 2025 -0700

    reintroduce macos fp64 ci target

commit a7ec259bd5
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Thu May 1 16:46:22 2025 -0700

    reintroduce macos fp64 ci target

commit 3e93e19767
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Thu May 1 14:41:25 2025 -0700

    enzyme bug notes

commit 532b065596
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Thu May 1 14:41:17 2025 -0700

    consistency

commit 82c1e2315b
Author: Julian Andrej <andrej1@llnl.gov>
Date:   Thu May 1 13:19:25 2025 -0700

    modernize

commit 8cc9eec535
Author: camierjs <camierjs@gmail.com>
Date:   Thu May 1 11:06:03 2025 -0700

    Remove unused code

commit dece65be31
Author: camierjs <camierjs@gmail.com>
Date:   Thu May 1 10:59:50 2025 -0700

    Header and style

commit 3e6d29b3dd
Author: camierjs <camierjs@gmail.com>
Date:   Thu May 1 10:53:12 2025 -0700

    Meld toward dfem

commit 487135b497
Author: camierjs <camierjs@gmail.com>
Date:   Thu May 1 10:48:02 2025 -0700

    Meld back toward dfem dev

commit 91f648aa95
Author: camierjs <camierjs@gmail.com>
Date:   Thu May 1 10:36:43 2025 -0700

    Remove examples leftovers

commit 999931ded2
Author: camierjs <camierjs@gmail.com>
Date:   Thu May 1 10:36:18 2025 -0700

    Sync dfem bench
2025-05-02 10:40:23 -07:00
camierjs be537728df Merge branch 'dfem-phase1-dev' into dfem-bench 2025-05-02 10:37:34 -07:00
Julian Andrej 4e5b98b10f doc 2025-05-02 10:05:20 -07:00
Julian Andrej d4acd906bf update brew before enzyme install 2025-05-02 09:01:51 -07:00
Julian Andrej d751ce66a3 ci 2025-05-02 08:43:46 -07:00
Julian Andrej 3f0abd4dfd ci 2025-05-02 08:40:45 -07:00
Julian Andrej 44a423d804 ci 2025-05-02 08:39:58 -07:00
Julian Andrej 3e61e0490e ci 2025-05-02 08:38:47 -07:00
Julian Andrej def4919592 ci config 2025-05-02 08:33:33 -07:00
Julian Andrej 2d147d70e0 reintroduce tests 2025-05-02 08:33:29 -07:00
Julian Andrej e29e64dffe reintroduce macos fp64 ci target 2025-05-02 08:04:44 -07:00
Julian Andrej a7ec259bd5 reintroduce macos fp64 ci target 2025-05-01 16:46:22 -07:00
Julian Andrej 3e93e19767 enzyme bug notes 2025-05-01 14:41:25 -07:00
Julian Andrej 532b065596 consistency 2025-05-01 14:41:17 -07:00
Julian Andrej 82c1e2315b modernize 2025-05-01 13:19:25 -07:00
camierjs 8cc9eec535 Remove unused code 2025-05-01 11:06:03 -07:00
camierjs dece65be31 Header and style 2025-05-01 10:59:50 -07:00
camierjs 3e6d29b3dd Meld toward dfem 2025-05-01 10:53:12 -07:00
camierjs 487135b497 Meld back toward dfem dev 2025-05-01 10:48:02 -07:00
camierjs 91f648aa95 Remove examples leftovers 2025-05-01 10:36:43 -07:00
camierjs 999931ded2 Sync dfem bench 2025-05-01 10:36:18 -07:00
camierjs 15dbcae725 Add version info 2025-05-01 10:26:49 -07:00
camierjs 01efb623da Update kernels pa to regs use 2025-05-01 10:08:01 -07:00
camierjs f854c5262d Move kernels pa to dfem regs 2025-05-01 10:07:46 -07:00
camierjs a91b754aaa Update dfem examples 2025-05-01 10:06:06 -07:00
camierjs b1623ff3d4 Sync dfem examples with latest changes 2025-05-01 10:05:54 -07:00
camierjs c2426ca45a Merge branch 'dfem-phase1-dev' 2025-05-01 09:35:22 -07:00
camierjs 276f419a3d Merge branch 'dfem-phase1-dev' of github.com:mfem/mfem into dfem-phase1-dev 2025-05-01 09:31:53 -07:00
Julian Andrej c91b8bea01 prevent possible indexing error 2025-05-01 08:42:56 -07:00
Veselin Dobrev 7bdceca6ce Windows CI debug 2025-05-01 04:48:28 -07:00
Veselin Dobrev 6f9a263435 Disable Ninja on windows -- it does not detect MSVC.
Add a debug action step to print the environment under windows.
2025-05-01 03:49:52 -07:00
Veselin Dobrev 28a7865ed1 Fix MSVC build issue.
Use the Ninja CMake generator on Windows to try to speedup the build.
2025-05-01 00:44:37 -07:00
Julian Andrej 6e7335ac52 Revert "test more captures"
This reverts commit 8115383dec.
2025-04-30 16:49:35 -07:00
Julian Andrej 8115383dec test more captures 2025-04-30 16:34:17 -07:00
Julian Andrej 3d1b017a60 Revert "test capture"
This reverts commit bf14e5b018.
2025-04-30 16:29:16 -07:00
Julian Andrej bf14e5b018 test capture 2025-04-30 16:12:23 -07:00
Julian Andrej fb3517453f correctness 2025-04-30 15:48:35 -07:00
Julian Andrej bfca6beb28 Revert "hints for mscv"
This reverts commit 78a60cc1d9.
2025-04-30 14:30:02 -07:00
Julian Andrej f51e46d3d8 changelog 2025-04-30 14:02:28 -07:00
Julian Andrej 78a60cc1d9 hints for mscv 2025-04-30 14:02:24 -07:00
Julian Andrej 935d3a9e42 namespaces 2025-04-30 10:30:44 -07:00
Julian Andrej 35866f8485 namespaces 2025-04-30 09:40:45 -07:00
Julian Andrej 6b4b644355 namespaces 2025-04-30 09:38:52 -07:00
Julian Andrej b9ec58e7a1 guards 2025-04-30 09:19:17 -07:00
Julian Andrej 4644aed322 native ad test 2025-04-30 09:18:22 -07:00
Julian Andrej 80da896859 namespaces 2025-04-30 09:18:16 -07:00
Julian Andrej b96dcb4401 namespaces 2025-04-30 08:54:57 -07:00
Julian Andrej 5054f1784d again 2025-04-29 14:13:51 -07:00
Julian Andrej 788c0efda0 sync input values 2025-04-29 14:10:42 -07:00
Julian Andrej 4d49d42702 typo 2025-04-29 13:33:16 -07:00
Julian Andrej f5192230e0 more msvc handholding 2025-04-29 11:22:24 -07:00
Tzanio Kolev 400e3eca7d Merge branch 'master' into dfem-phase1-dev 2025-04-29 09:55:18 -07:00
Julian Andrej b90c8d80fe remove problematic constexpr 2025-04-29 08:50:20 -07:00
Veselin Dobrev a0491f6bfc Fix some msvc warnings which also fixed some compilation errors 2025-04-29 01:43:41 -07:00
Julian Andrej a8df54cf5d please msvc 2025-04-28 20:39:29 -07:00
Julian Andrej 9c4e43ee12 revert 2025-04-28 19:29:23 -07:00
Julian Andrej 7a1887c525 oops 2025-04-28 17:58:40 -07:00
Julian Andrej 907783f9ca testing 2025-04-28 17:56:23 -07:00
Julian Andrej f4f68fa021 size 2025-04-28 17:08:04 -07:00
Julian Andrej b76e9e80a7 real annoying real_t 2025-04-28 17:03:59 -07:00
Julian Andrej b8f677b2fe shadows 2025-04-28 16:59:44 -07:00
Julian Andrej 6e42fbae4d guards 2025-04-28 16:51:34 -07:00
Julian Andrej b8c0008061 include orders etc 2025-04-28 16:36:44 -07:00
Julian Andrej cdce090c2a cmake 2025-04-28 15:48:25 -07:00
Paul Hilscher 7994a3df8b fix unsigned signed comparison warning 2025-04-29 07:42:24 +09:00
Julian Andrej e246c0852b c++17 2025-04-28 15:40:50 -07: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
Julian Andrej 4db86286ee unguard test 2025-04-28 14:05:55 -07:00
Julian Andrej 9308946715 guards 2025-04-28 14:05:44 -07:00
Julian Andrej d28eca6b7f renaming 2025-04-28 14:05:34 -07:00
Julian Andrej 8e26105232 temporary disable offended unit tests 2025-04-28 11:53:12 -07:00
Tzanio Kolev ab52f334e2 Merge branch 'master' into mfem-v13-mesh-reader-fix-issue-4625 2025-04-26 12:25:30 -07:00
Julian Andrej c674f9f7ad defuse test 2025-04-24 15:29:09 -07:00
Julian Andrej 537d30120a Merge branch 'master' into dfem-phase1-dev 2025-04-24 14:35:23 -07:00
Julian Andrej 519267e1cb paths 2025-04-24 14:02:09 -07:00
Julian Andrej 2c495fb70d shadow warnings 2025-04-24 13:36:05 -07:00
Julian Andrej 401d1aec7b ci 2025-04-24 13:02:10 -07:00
Julian Andrej 8299b1c036 ci 2025-04-24 12:52:18 -07:00
Julian Andrej 9dd1e4dbdb ci 2025-04-24 11:48:17 -07:00
Julian Andrej 47a3534eff ci 2025-04-24 11:44:08 -07:00
Julian Andrej 2f39ff66f3 ci 2025-04-24 11:34:36 -07:00
Julian Andrej ddca183704 ci 2025-04-24 11:20:31 -07:00
Julian Andrej 078ce6130c ci 2025-04-24 11:17:54 -07:00
Julian Andrej 6d15c2a156 ci 2025-04-24 11:11:54 -07:00
Julian Andrej 7d705c0677 ci 2025-04-24 11:09:42 -07:00
Julian Andrej c027328b91 ci 2025-04-24 11:07:27 -07:00
Julian Andrej 65cb67e1c1 ci 2025-04-24 11:04:54 -07:00
Julian Andrej 494f27c14c ci 2025-04-24 11:00:37 -07:00
Julian Andrej 3c02b72084 ci 2025-04-24 10:51:20 -07:00
Julian Andrej 75e2be35ba ci 2025-04-24 10:46:12 -07:00
Julian Andrej b0f9cbfd26 ci 2025-04-24 10:42:24 -07:00
Julian Andrej 6afea18cde ci 2025-04-24 10:39:14 -07:00
Julian Andrej 2e69ff4b97 ci 2025-04-24 10:33:08 -07:00
Julian Andrej 6c70fe9334 ci 2025-04-24 10:27:46 -07:00
Julian Andrej 4ccbd4581e ci 2025-04-24 10:19:07 -07:00
Julian Andrej 6e262f6c3f ci 2025-04-24 10:13:37 -07:00
Julian Andrej 52e10475a5 ci 2025-04-24 10:10:20 -07:00
Julian Andrej c0299a5a4b ci 2025-04-24 10:05:24 -07:00
Julian Andrej 06eecb0dce yaml lint and first enzyme ci entries 2025-04-24 10:01:07 -07:00
Julian Andrej 96261a7742 c++17 and experimental namespace 2025-04-23 18:09:12 -07:00
Julian Andrej d7c479fa1e documentation 2025-04-21 09:21:07 -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
Julian Andrej 8b01d8f13b std::cout -> mfem::out 2025-04-16 10:53:23 -07:00
Julian Andrej 710da275c8 add dfem folder to makefile 2025-04-16 09:02:57 -07:00
Julian Andrej fd481eb725 correct include orders 2025-04-16 09:02:43 -07:00
Julian Andrej b5bbdbbed5 vectorfe leftover 2025-04-16 09:02:31 -07:00
Tzanio Kolev 0f78d8aa5c Merge branch 'master' into najlkin/extrd-1d-vec 2025-04-15 13:46:39 -07:00
Julian Andrej 6a26200314 remove vectorfe crumbs 2025-04-15 11:31:14 -07:00
Andrew Ho 3f98aa1cfb Merge branch 'master' into warn-gridfunc 2025-04-14 16:15:59 -07:00
Julian Andrej a485121526 msvc ambiguity enable_if 2025-04-14 14:40:52 -07:00
Julian Andrej 1f9e1cf175 brackets 2025-04-14 14:03:28 -07:00
Julian Andrej ec402882da move guard 2025-04-14 13:58:10 -07:00
Julian Andrej e7633e0e2c guard tests 2025-04-14 13:49:46 -07:00
Julian Andrej 30aeb465b7 includes 2025-04-14 13:27:15 -07:00
Julian Andrej ff4993fc51 array include 2025-04-14 13:13:44 -07:00
Julian Andrej 0a42ea8021 copyright dates 2025-04-14 13:13:33 -07:00
Julian Andrej b8d024b59b remove example subdirectory 2025-04-14 10:51:57 -07:00
Julian Andrejandcamierjs 9e1ccf4543 phase 1 skeleton
Co-authored-by: camierjs <camierjs@gmail.com>
2025-04-14 09:43:43 -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
camierjs 075ebb255d Do one first benchmark 2025-04-09 11:35:40 -07:00
camierjs 3eb6a5b3b2 Merge branch 'dfem-phase1-dev' 2025-04-03 14:01:28 -07:00
Julian Andrej 8ba1f17f72 add nonlinear solver options to command line arguments 2025-04-03 11:01:31 -07:00
Julian Andrej e5f5a79e66 attempt to fix parametric function transfers 2025-04-03 08:19:53 -07:00
Julian Andrej 43f1b19767 switch to 2d by default 2025-04-03 08:19:32 -07:00
Julian Andrej 7bebe4528f stop printing dependency maps 2025-04-03 08:19:16 -07:00
camierjs da63657cdd GCC warning fixes 2025-04-02 18:40:57 -07:00
camierjs 2b1d271888 Merge branch 'dfem-phase1-dev' 2025-04-02 18:34:46 -07:00
camierjs 47fb8a4fda No auto for gcc 2025-04-02 18:34:30 -07:00
camierjs ee7d9726df Warnings & fixes 2025-04-02 18:34:08 -07:00
camierjs 44b560a916 Merge branch 'dfem-phase1-dev' 2025-04-02 17:56:37 -07:00
Julian Andrej 5657f6ebe8 Merge branch 'dfem-phase1-dev' of github.com:mfem/mfem into dfem-phase1-dev 2025-04-02 17:44:22 -07:00
Julian Andrej 19543b6b16 more device stuff 2025-04-02 17:41:57 -07:00
camierjs 94a832a0c6 Merge branch 'dfem-phase1-dev' 2025-04-02 17:21:09 -07:00
camierjs b56e994ecd Copyright header, includes trim & warning fixes 2025-04-02 17:20:29 -07:00
camierjs 8be11cdfdb Remove duplicate inline 2025-04-02 16:49:20 -07:00
camierjs d71a9602b5 Merge branch 'dfem-phase1-dev' 2025-04-02 16:41:06 -07:00
camierjs 1108bb7e85 Use SetMaxOf inside kernel 2025-04-02 16:40:38 -07:00
Julian Andrej ae8e5aa88d some device stuff 2025-04-02 16:21:18 -07:00
camierjs 17f4acf6b1 Merge branch 'main' of github.com:camierjs/mfem-dfem-bench into main 2025-04-02 16:03:32 -07:00
camierjs 2ce3f3037c Cleanup 2025-04-02 16:03:30 -07:00
camierjs 29189a6d4a Merge branch 'dfem-phase1-dev' 2025-04-02 16:02:10 -07:00
Julian Andrej 08f3c86b8a make attributes device compatible 2025-04-02 15:46:19 -07:00
camierjs c6eb171b5b Back to foreach treads 2025-04-02 14:33:42 -07:00
camierjs d26695cd2a Use latest AddDomainIntegrator API 2025-04-02 12:18:21 -07:00
camierjs 01ab390b06 Merge branch 'dfem-phase1-dev' 2025-04-02 12:09:57 -07:00
camierjs 43c42295d3 Few changes with clang 20.1 2025-04-02 12:09:36 -07:00
camierjs 52bc915120 Few fixes to run on device and removed warnings 2025-04-02 12:07:57 -07:00
camierjs cd9cabb955 Cleanup all hipGetLastError 2025-04-02 09:32:36 -07:00
Jan Nikl b7253275fc Added extrusion of vector 1D grid functions. 2025-04-01 17:49:16 -07:00
Julian Andrej e66a61c198 add build instructions 2025-03-31 17:23:33 -07:00
Julian Andrej f8b3c78b19 tensor additions 2025-03-31 14:28:29 -07:00
Julian Andrej 4749746171 add laghos 2025-03-31 14:28:10 -07:00
camierjs 1ddd01c2a0 All dfem BP3 versions 2025-03-31 13:47:06 -07:00
camierjs 87ec3850b5 Update Diffusion class 2025-03-30 13:08:31 -07:00
camierjs 1b25a61c9e Re-order kpc benchmarks 2025-03-30 11:52:41 -07:00
camierjs 7bee8e8161 tests/benchmarks/bench_dfem 2025-03-30 11:40:41 -07:00
camierjs a545ff8264 Bring StiffnessIntegrator in bench dfem 2025-03-30 10:29:17 -07:00
camierjs 5352234aef Use SetMaxOf 2025-03-30 10:06:54 -07:00
camierjs c3732f9d86 dfem diffusion3d D1D Q1D tests 2025-03-30 09:46:08 -07:00
camierjs b95f3809fe ParametricSpace d1d/q1d 2025-03-28 17:24:29 -07:00
camierjs e6a28b7753 Merge branch 'dfem-phase1-dev' 2025-03-28 15:09:46 -07:00
camierjs 62adea8b46 WIP dfem diffusion 2025-03-28 15:09:22 -07:00
camierjs 7d11db33c0 Add dfem diffusion multi-version example and bench dfem setup 2025-03-28 12:09:52 -07:00
Julian Andrej 11fce4235b revert width determination 2025-03-28 08:16:26 -07:00
camierjs f500b4875f dfem bench check 2025-03-27 10:48:46 -07:00
camierjs ba212c583e bench dfem init with nvtx 2025-03-27 10:34:12 -07:00
Julian Andrej fd341e07da example 2025-03-21 15:54:58 -07:00
Julian Andrej d59e2a229c phase 1 skeleton 2025-03-21 15:54:18 -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
185 changed files with 13694 additions and 2354 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ runs:
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
- uses: actions/cache@v5
if: ${{env.DEBUG == 'true'}}
id: debug
with:
+1 -1
View File
@@ -36,7 +36,7 @@ runs:
steps:
- uses: ./.github/actions/sanitize/config
- uses: actions/cache@v4
- uses: actions/cache@v5
if: ${{env.DEBUG == 'true' && inputs.cache-skip != 'true'}}
id: debug
with:
+5 -5
View File
@@ -23,7 +23,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/cache/restore@v4 # Cache for LLVM libcxx
- uses: actions/cache/restore@v5 # Cache for LLVM libcxx
with:
path: ${{env.LLVM_DIR}}
fail-on-cache-miss: true
@@ -32,14 +32,14 @@ runs:
- uses: ./.github/actions/sanitize/mpi
if: ${{inputs.par == 'true'}}
- uses: actions/cache/restore@v4 # Cache for Hypre
- uses: actions/cache/restore@v5 # Cache for Hypre
if: ${{inputs.par == 'true'}}
with:
path: ${{env.HYPRE_DIR}}
fail-on-cache-miss: true
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
- uses: actions/cache/restore@v4 # Cache for Metis
- uses: actions/cache/restore@v5 # Cache for Metis
if: ${{inputs.par == 'true'}}
with:
path: ${{env.METIS_DIR}}
@@ -51,13 +51,13 @@ runs:
run: ln -s -f ${{env.HYPRE_DIR}} hypre && ln -s -f ${{env.METIS_DIR}} metis-4.0
shell: bash
- uses: actions/cache/restore@v4 # Cache for LSAN suppression file
- uses: actions/cache/restore@v5 # Cache for LSAN suppression file
with:
path: ${{env.LSAN_DIR}}
fail-on-cache-miss: true
key: build-lsan-suppression-file
- uses: actions/checkout@v4 # Checkout the repository
- uses: actions/checkout@v6 # Checkout the repository
with:
path: mfem
# ref: ${{env.BRANCH}}
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
remove-docker-images: 'true'
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v6
# It's easier to reference named variables than indexes of the matrix
- name: Set Environment
+6 -5
View File
@@ -153,7 +153,7 @@ jobs:
# /home/runner/work/mfem/mfem/mfem
# Note: Done now to access "install-hypre" and "install-metis" actions.
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
path: ${{ env.MFEM_TOP_DIR }}
# Fetch the complete history for codecov to access commits ID
@@ -225,7 +225,7 @@ jobs:
- name: cache hypre
id: hypre-cache
if: matrix.mpi == 'par'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
@@ -255,7 +255,7 @@ jobs:
- name: cache metis
id: metis-cache
if: matrix.mpi == 'par' && matrix.os != 'windows-latest'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
@@ -270,7 +270,7 @@ jobs:
- name: cache vcpkg (Windows)
id: vcpkg-cache
if: matrix.os == 'windows-latest'
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: vcpkg_cache
key: ${{ runner.os }}-${{ matrix.mpi }}-vcpkg-v1
@@ -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 -4
View File
@@ -40,11 +40,11 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -57,7 +57,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@v4
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
@@ -70,4 +70,4 @@ jobs:
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v4
+3 -3
View File
@@ -39,7 +39,7 @@ jobs:
steps:
- name: checkout MFEM
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
path: mfem
@@ -50,7 +50,7 @@ jobs:
- name: Cache Hypre Install
id: hypre-cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
@@ -65,7 +65,7 @@ jobs:
- name: Cache Metis Install
id: metis-cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
+4 -4
View File
@@ -38,7 +38,7 @@ jobs:
github.event.pull_request.head.repo.full_name != github.repository)
steps:
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: copyright check
id: copyright
@@ -93,7 +93,7 @@ jobs:
github.event.pull_request.head.repo.full_name != github.repository)
steps:
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: get astyle
run: |
@@ -110,7 +110,7 @@ jobs:
github.event.pull_request.head.repo.full_name != github.repository)
steps:
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: get doxygen and graphviz
run: |
@@ -135,7 +135,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: checkout mfem
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
+2 -2
View File
@@ -17,11 +17,11 @@ jobs:
runs-on: ubuntu-latest
name: 2.19.0
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{env.HYPRE_DIR}}
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
+2 -2
View File
@@ -27,13 +27,13 @@ jobs:
llvm_use_sanitizer: "Undefined"
name: ${{matrix.sanitizer}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
with:
NO_FLAGS: true
- name: Cache
id: cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{env.LLVM_DIR}}
key: build-libcxx-${{env.LLVM_VER}}-${{matrix.sanitizer}}
+2 -2
View File
@@ -17,11 +17,11 @@ jobs:
runs-on: ubuntu-latest
name: lsan.supp
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{env.LSAN_DIR}}
key: build-lsan-suppression-file
+2 -2
View File
@@ -17,11 +17,11 @@ jobs:
runs-on: ubuntu-latest
name: 4.0.3
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
- name: Cache
id: cache
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{env.METIS_DIR}}
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
+7 -7
View File
@@ -28,7 +28,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/mfem
with:
par: ${{inputs.par}}
@@ -40,7 +40,7 @@ jobs:
env:
ex: ${{inputs.par && 'ex1p' || 'ex1'}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -58,7 +58,7 @@ jobs:
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -82,7 +82,7 @@ jobs:
env:
exclude: ${{inputs.par && '-E "_ser"' || ''}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -107,7 +107,7 @@ jobs:
run: ${{inputs.par && '-R "_cpu_np"' || ''}}
exclude: ${{inputs.par && '"unit_tests|debug"' || '"^unit_tests$|debug"'}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -131,7 +131,7 @@ jobs:
env:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
@@ -165,7 +165,7 @@ jobs:
unit_tests: ${{inputs.par && 'punit_tests' || 'unit_tests'}}
np: ${{inputs.par && '_np=2' || ''}}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/restore
id: restore
with:
+6 -2
View File
@@ -443,12 +443,16 @@ 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
tests/unit/punit_tests
tests/unit/gpu_unit_tests
tests/unit/pgpu_unit_tests
tests/unit/cunit_tests
tests/unit/pcunit_tests
tests/unit/sedov_tests_*
tests/unit/psedov_tests_*
tests/unit/tmop_pa_tests_*
+19
View File
@@ -8,6 +8,22 @@
https://mfem.org
Version 4.10 (development)
==========================
Discretization improvements
---------------------------
- Replaced legacy simplex quadrature rules with symmetric positive-weight
rules for triangles (orders 0-25) and tetrahedra (orders 0-20). These
rules guarantee all-positive weights and interior quadrature points,
improving numerical stability. Higher orders fall back to Grundmann-Moller.
Triangle rules: Witherden & Vincent, Comput. Math. Appl. 69(10):1232-1241,
2015.
Tet rules (d=1-13): Witherden & Vincent (ibid).
Tet rules (d=14-20): Chuluunbaatar et al., Comput. Math. Appl. 124:89-97,
2022.
Version 4.9.1 (development)
===========================
@@ -17,6 +33,9 @@ Discretization improvements
Vector and VectorFE, also NURBS versions. Optionally different types of
projections can be selected, default behaviour has not changed.
- Added methods to estimate function extremum using piecewise linear bounds +
recursive subdivision.
Meshing improvements
--------------------
- Improved support for 1D NURBS meshes with variable order, including using
+22 -1
View File
@@ -592,6 +592,13 @@ if (MFEM_USE_ENZYME)
set(ENZYME_INCLUDE_DIRS ${ENZYME_DIR}/include)
endif()
if (MFEM_USE_PROTEUS)
enable_language(C)
find_package(proteus REQUIRED PATHS "${PROTEUS_DIR}")
message(STATUS "${PROTEUS_DIR}/include")
include_directories("${PROTEUS_DIR}/include")
endif()
# MFEM_TIMER_TYPE
if (NOT DEFINED MFEM_TIMER_TYPE)
if (APPLE)
@@ -652,6 +659,8 @@ foreach(TPL IN LISTS MFEM_TPLS)
endif()
endforeach(TPL)
# reverse to remove the first instance of entries in TPL_LIBRARIES
# so later duplicates are kept (for dependency ordering)
list(REVERSE TPL_LIBRARIES)
list(REMOVE_DUPLICATES TPL_LIBRARIES)
list(REVERSE TPL_LIBRARIES)
@@ -726,6 +735,16 @@ mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
target_compile_features(mfem PUBLIC cxx_std_${CMAKE_CXX_STANDARD})
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES} ${TPL_TARGETS})
if (MFEM_USE_PROTEUS)
add_library(ClangProteusFlags INTERFACE IMPORTED)
set_target_properties(ClangProteusFlags PROPERTIES
INTERFACE_COMPILE_OPTIONS "-fpass-plugin=$<TARGET_FILE:ProteusPass>"
)
target_link_libraries(mfem PUBLIC ClangProteusFlags)
target_link_libraries(mfem PUBLIC proteus)
endif()
if (TPL_TARGETS)
add_dependencies(mfem ${TPL_TARGETS})
endif()
@@ -1015,5 +1034,7 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
# Create 'config.mk' from 'config.mk.in' for the build and install locations and
# define install rules for 'config.mk' and 'test.mk'
#-------------------------------------------------------------------------------
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
option(MFEM_EXPORT_GPU_CONFIG "Export config.mk for GPU-enabled downstream packages" ON)
endif()
mfem_export_mk_files()
+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")
+89 -17
View File
@@ -701,7 +701,6 @@ endfunction(mfem_find_library)
# Extract compile and link options needed by the given target.
#
function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
if (NOT TARGET ${Target})
return()
endif()
@@ -799,7 +798,12 @@ function(mfem_get_target_options Target CompileOptsVar LinkOptsVar)
# message(STATUS "Lib = ${Lib}")
# Filter-out generator expressions
if (NOT ("${Lib}" MATCHES "^\\$"))
list(APPEND LinkOpts "${Lib}")
if(NOT ("${Lib}" STREQUAL "dl"))
list(APPEND LinkOpts "${Lib}")
else()
# for some reason libdl doesn't include the "-l"
list(APPEND LinkOpts "-ldl")
endif()
endif()
else()
mfem_get_target_options(${Lib} COpts LOpts)
@@ -888,9 +892,18 @@ function(mfem_export_mk_files)
set(${var} NO)
endif()
endforeach()
# TODO: Add support for MFEM_USE_CUDA=YES
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
set(MFEM_HOST_CXX ${MFEM_CXX})
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
set(MFEM_CXX ${CMAKE_CUDA_COMPILER})
if(MFEM_CUDA_COMPILER_IS_NVCC)
set(MFEM_HOST_CXX ${CMAKE_CUDA_HOST_COMPILER})
else()
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
endif()
else()
# mfem doesn't use enable_language(HIP)
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
set(MFEM_HOST_CXX ${CMAKE_CXX_COMPILER})
endif()
set(MFEM_CPPFLAGS "")
get_target_property(cxx_std mfem CXX_STANDARD)
# For now, we ignore the setting of the CXX_EXTENSIONS property. If this
@@ -900,6 +913,50 @@ function(mfem_export_mk_files)
string(STRIP
"${cxx_std_flag} ${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
MFEM_CXXFLAGS)
if(MFEM_EXPORT_GPU_CONFIG)
if (MFEM_USE_CUDA)
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} ${CMAKE_CUDA_FLAGS}")
if (MFEM_CUDA_COMPILER_IS_NVCC)
set(MFEM_CXXFLAGS "-x=cu ${MFEM_CXXFLAGS} -ccbin ${CMAKE_CXX_COMPILER} --forward-unknown-to-host-compiler")
# The following intentionally hides CUDA deprecation warnings
foreach(ENTRY IN LISTS CUDAToolkit_INCLUDE_DIRS)
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -isystem ${ENTRY}")
endforeach()
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
# architecture flags not part of CMAKE_CUDA_FLAGS
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -arch=${CMAKE_CUDA_ARCHITECTURES}")
else()
foreach (ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
set(MFEM_CXXFLAGS
"${MFEM_CXXFLAGS} -gencode arch=compute_${ENTRY},code=sm_${ENTRY}")
endforeach()
endif()
endif()
else()
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xcuda --cuda-path=${CUDAToolkit_LIBRARY_ROOT}")
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.18.0)
# architecture flags not part of CMAKE_CUDA_FLAGS
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
# TODO: not supported
else()
foreach(ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
set(MFEM_CXXFLAGS "-cuda-gpu-arch=sm_${ENTRY} ${MFEM_CXXFLAGS}")
endforeach()
endif()
endif()
endif()
elseif (MFEM_USE_HIP)
set(MFEM_CXXFLAGS "${MFEM_CXXFLAGS} -xhip")
foreach(ENTRY IN LISTS CMAKE_HIP_ARCHITECTURES)
set(MFEM_CXXFLAGS "--offload-arch=${ENTRY} ${MFEM_CXXFLAGS}")
endforeach()
endif()
endif()
set(MFEM_TPLFLAGS "")
foreach(dir ${TPL_INCLUDE_DIRS})
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} -I${dir}")
@@ -930,6 +987,9 @@ function(mfem_export_mk_files)
set(MFEM_SHARED NO)
set(MFEM_STATIC YES)
endif()
if (MFEM_USE_CUDA)
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} -lcudart")
endif()
set(MFEM_BUILD_TAG "${CMAKE_SYSTEM}")
set(MFEM_PREFIX "${CMAKE_INSTALL_PREFIX}")
# For the next 4 variables, these are the values for the build-tree version of
@@ -938,8 +998,15 @@ function(mfem_export_mk_files)
set(MFEM_LIB_DIR "${PROJECT_BINARY_DIR}")
set(MFEM_TEST_MK "${PROJECT_SOURCE_DIR}/config/test.mk")
set(MFEM_CONFIG_EXTRA "MFEM_BUILD_DIR ?= ${PROJECT_BINARY_DIR}")
# TODO: CUDA/HIP support:
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
if (MFEM_USE_CUDA AND MFEM_EXPORT_GPU_CONFIG)
if (MFEM_CUDA_COMPILER_IS_NVCC)
set(MFEM_XLINKER "-Xlinker=")
else()
set(MFEM_XLINKER "${CMAKE_CUDA_LINKER_WRAPPER_FLAG}")
endif()
else()
set(MFEM_XLINKER "${CMAKE_CXX_LINKER_WRAPPER_FLAG}")
endif()
set(MFEM_MPIEXEC ${MPIEXEC})
if (NOT MFEM_MPIEXEC)
set(MFEM_MPIEXEC "mpirun")
@@ -987,16 +1054,21 @@ function(mfem_export_mk_files)
# handle interfaces (e.g., SCOREC::apf)
if ("${lib}" MATCHES "SCOREC::.*" OR "${lib}" MATCHES "Ginkgo::.*" OR "${lib}" MATCHES "ParMoonolith::.*")
elseif (TARGET "${lib}")
mfem_get_target_options(${lib} CompileOpts LinkOpts)
mfem_get_target_options(${lib} CompileOpts2 LinkOpts2)
# remove generator expressions
string(GENEX_STRIP "${CompileOpts2}" CompileOpts)
string(GENEX_STRIP "${LinkOpts2}" LinkOpts)
# Removing duplicates may lead to issues:
# list(REMOVE_DUPLICATES CompileOpts)
# list(REMOVE_DUPLICATES LinkOpts)
string(REPLACE ";" " " COpts "${CompileOpts}")
string(REPLACE ";" " " LOpts "${LinkOpts}")
# message(STATUS "${lib}[COpts]: '${COpts}'")
# message(STATUS "${lib}[LOpts]: '${LOpts}'")
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpts}")
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpts}")
# message(WARNING "${lib}[LinkOpts]: ${LinkOpts}")
# message(WARNING "${lib}[CompileOpts]: ${CompileOpts}")
foreach(LOpt IN LISTS LinkOpts)
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${LOpt}")
endforeach()
foreach(COpt IN LISTS CompileOpts)
set(MFEM_TPLFLAGS "${MFEM_TPLFLAGS} ${COpt}")
endforeach()
# message(FATAL_ERROR "***** interface lib found ... exiting *****")
# handle static and shared libs
elseif ("${suffix}" STREQUAL "${CMAKE_SHARED_LIBRARY_SUFFIX}")
@@ -1004,7 +1076,7 @@ function(mfem_export_mk_files)
get_filename_component(fullLibName ${lib} NAME_WE)
string(REGEX REPLACE "^lib" "" libname ${fullLibName})
set(MFEM_EXT_LIBS
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
"${MFEM_EXT_LIBS} ${shared_link_flag}${dir} -L${dir} -l${libname}")
else()
set(MFEM_EXT_LIBS "${MFEM_EXT_LIBS} ${lib}")
endif()
@@ -1013,7 +1085,7 @@ function(mfem_export_mk_files)
# Create the build-tree version of 'config.mk'
configure_file(
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
"${PROJECT_BINARY_DIR}/config/config.mk")
"${PROJECT_BINARY_DIR}/config/config.mk" @ONLY)
# Copy 'test.mk' from the source-tree to the build-tree
configure_file(
"${PROJECT_SOURCE_DIR}/config/test.mk"
@@ -1031,7 +1103,7 @@ function(mfem_export_mk_files)
# Create the install-tree version of 'config.mk'
configure_file(
"${PROJECT_SOURCE_DIR}/config/config.mk.in"
"${PROJECT_BINARY_DIR}/config/config-install.mk")
"${PROJECT_BINARY_DIR}/config/config-install.mk" @ONLY)
# Install rules for 'config.mk' and 'test.mk'
install(FILES ${PROJECT_SOURCE_DIR}/config/test.mk
+18
View File
@@ -157,4 +157,22 @@ constexpr real_t operator""_r(unsigned long long v)
#endif
#endif // MFEM_USE_MPI not defined
#ifdef NVTX_DBG_HPP
#include NVTX_DBG_HPP
#else
#define db1(...)
#define dbg(...)
#define dbl(...)
#define dba(...)
#define dbc(...)
#define NVTX_MARK_FUNCTION
#define NVTX_MARK_BEGIN(...)
#define NVTX_INI(...)
#define NVTX_END(...)
#define NVTX_MARK_INI(...)
#define NVTX_MARK_END(...)
#define NVTX_MARK(...)
#define NVTX(...)
#endif
#endif // MFEM_CONFIG_HPP
+1
View File
@@ -47,6 +47,7 @@ list(APPEND ALL_EXE_SRCS
ex39.cpp
ex40.cpp
ex41.cpp
jitplayground.cpp
)
if (MFEM_USE_MPI)
+2 -2
View File
@@ -5,9 +5,9 @@
// Sample runs:
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 462 -n 10 -o 2 -elast
// mpirun -np 4 ex12p -m ../data/beam-tet.mesh -s 464 -n 10 -o 2 -elast
// mpirun -np 4 ex12p -m ../data/beam-hex.mesh -s 3878
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 81
// mpirun -np 4 ex12p -m ../data/beam-wedge.mesh -s 82
// mpirun -np 4 ex12p -m ../data/beam-tri.mesh -s 3877 -o 2 -sys
// mpirun -np 4 ex12p -m ../data/beam-quad.mesh -s 4544 -n 6 -o 3 -elast
// mpirun -np 4 ex12p -m ../data/beam-quad-nurbs.mesh
+27 -9
View File
@@ -302,15 +302,21 @@ int main(int argc, char *argv[])
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Exact: Real Part'" << flush;
// Make sure all ranks have sent their real solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
socketstream sol_sock_i(vishost, visport);
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i.precision(8);
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Exact: Imaginary Part'" << flush;
// Make sure all ranks have sent their imaginary solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
}
// 11. Set up the parallel sesquilinear form a(.,.) on the finite element
@@ -534,15 +540,21 @@ int main(int argc, char *argv[])
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u.real()
<< "window_title 'Solution: Real Part'" << flush;
// Make sure all ranks have sent their real solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
socketstream sol_sock_i(vishost, visport);
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i.precision(8);
sol_sock_i << "solution\n" << *pmesh << u.imag()
<< "window_title 'Solution: Imaginary Part'" << flush;
// Make sure all ranks have sent their imaginary solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
}
if (visualization && exact_sol)
{
@@ -551,15 +563,21 @@ int main(int argc, char *argv[])
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock_r(vishost, visport);
socketstream sol_sock_i(vishost, visport);
sol_sock_r << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_r.precision(8);
sol_sock_i.precision(8);
sol_sock_r << "solution\n" << *pmesh << u_exact->real()
<< "window_title 'Error: Real Part'" << flush;
// Make sure all ranks have sent their real solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
socketstream sol_sock_i(vishost, visport);
sol_sock_i << "parallel " << num_procs << " " << myid << "\n";
sol_sock_i.precision(8);
sol_sock_i << "solution\n" << *pmesh << u_exact->imag()
<< "window_title 'Error: Imaginary Part'" << flush;
// Make sure all ranks have sent their imaginary solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
}
if (visualization)
{
+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);
+536
View File
@@ -0,0 +1,536 @@
#include <mfem.hpp>
#include "../fem/dfem/util.hpp"
#include <proteus/CppJitModule.h>
#include "jitplayground.hpp"
#include <algorithm>
#include <array>
#include <cctype>
#include <cmath>
#include <fstream>
#include <initializer_list>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
namespace util
{
constexpr std::string_view Dirname(std::string_view path)
{
const size_t last_sep = path.find_last_of("/\\");
if (last_sep == std::string_view::npos) { return {}; }
return path.substr(0, last_sep);
}
constexpr std::string_view thisFileDir = Dirname(__FILE__);
}
template <typename T>
static std::string TypeNameString()
{
return std::string(mfem::future::get_type_name<T>());
}
template <typename Tuple, size_t... Is>
static auto ParamTypeStringsImpl(std::index_sequence<Is...>)
{
return std::array<std::string, sizeof...(Is)>
{
TypeNameString<std::remove_reference_t<decltype(mfem::future::get<Is>(std::declval<Tuple&>()))>>()...
};
}
template <typename Tuple>
static auto ParamTypeStrings()
{
return ParamTypeStringsImpl<Tuple>(
std::make_index_sequence<mfem::future::tuple_size<Tuple>::value> {});
}
static std::string_view Trim(std::string_view s)
{
size_t begin = 0;
while (begin < s.size() && std::isspace(static_cast<unsigned char>(s[begin])))
{
++begin;
}
size_t end = s.size();
while (end > begin &&
std::isspace(static_cast<unsigned char>(s[end - 1])))
{
--end;
}
return s.substr(begin, end - begin);
}
static bool IsValidIdentifier(std::string_view s)
{
if (s.empty()) { return false; }
const unsigned char c0 = static_cast<unsigned char>(s[0]);
if (!(std::isalpha(c0) || c0 == '_')) { return false; }
for (size_t i = 1; i < s.size(); ++i)
{
const unsigned char c = static_cast<unsigned char>(s[i]);
if (!(std::isalnum(c) || c == '_')) { return false; }
}
return true;
}
static bool ParseJitDirective(std::string_view line,
std::string &type,
std::string &var,
std::string &kind)
{
const size_t jit_pos = line.find("$JIT");
if (jit_pos == std::string_view::npos) { return false; }
const size_t open = line.find('[', jit_pos);
const size_t close = line.find(']', jit_pos);
MFEM_VERIFY(open != std::string_view::npos &&
close != std::string_view::npos &&
close > open,
"malformed $JIT directive (expected brackets): " << line);
const std::string_view payload = line.substr(open + 1, close - open - 1);
const size_t comma1 = payload.find(',');
const size_t comma2 = (comma1 == std::string_view::npos)
? std::string_view::npos
: payload.find(',', comma1 + 1);
MFEM_VERIFY(comma1 != std::string_view::npos &&
comma2 != std::string_view::npos,
"malformed $JIT directive (expected 3 comma-separated fields): "
<< line);
const std::string_view f0 = Trim(payload.substr(0, comma1));
const std::string_view f1 = Trim(payload.substr(comma1 + 1,
comma2 - comma1 - 1));
const std::string_view f2 = Trim(payload.substr(comma2 + 1));
MFEM_VERIFY(!f0.empty() && !f1.empty() && !f2.empty(),
"malformed $JIT directive (empty field): " << line);
type.assign(f0);
var.assign(f1);
kind.assign(f2);
return true;
}
static std::string ReadFileOrEmpty(const std::string &fn)
{
std::ifstream file(fn);
if (!file.is_open())
{
std::cerr << "could not open file " << fn << "\n";
return {};
}
std::stringstream buffer;
buffer << file.rdbuf();
return buffer.str();
}
static std::vector<std::string> ExtractJitVarNames(const std::string
&kernel_code)
{
std::stringstream ss(kernel_code);
std::string line;
std::vector<std::string> var_names;
std::unordered_set<std::string> seen_vars;
while (std::getline(ss, line))
{
std::string type, var, kind;
if (ParseJitDirective(line, type, var, kind))
{
MFEM_VERIFY(IsValidIdentifier(var),
"$JIT variable must be a valid identifier: " << var);
MFEM_VERIFY(seen_vars.insert(var).second,
"duplicate $JIT variable name: " << var);
var_names.push_back(var);
}
}
return var_names;
}
static std::string RewriteKernelForJit(std::string kernel_code,
const std::vector<std::string> &jit_values)
{
std::stringstream ss(kernel_code);
std::string line;
std::string out;
out.reserve(kernel_code.size() + 128);
bool have_pending = false;
size_t pending_index = 0;
std::string pending_type;
std::string pending_var;
std::unordered_set<std::string> seen_vars;
while (std::getline(ss, line))
{
line.push_back('\n');
if (have_pending)
{
MFEM_VERIFY(pending_index < jit_values.size(),
"not enough JIT values provided");
const size_t indent_end = line.find_first_not_of(" \t");
const std::string indent =
(indent_end == std::string::npos) ? std::string() :
line.substr(0, indent_end);
out += indent + "const " + pending_type + " " + pending_var + " = " +
jit_values[pending_index] + ";\n";
have_pending = false;
++pending_index;
continue;
}
std::string type, var, kind;
if (ParseJitDirective(line, type, var, kind))
{
MFEM_VERIFY(IsValidIdentifier(var),
"$JIT variable must be a valid identifier: " << var);
MFEM_VERIFY(kind == "generic",
"unsupported $JIT kind: " << kind);
MFEM_VERIFY(seen_vars.insert(var).second,
"duplicate $JIT variable name: " << var);
pending_type = std::move(type);
pending_var = std::move(var);
have_pending = true;
continue; // drop directive line
}
out += line;
}
MFEM_VERIFY(!have_pending,
"$JIT directive must annotate a following line");
MFEM_VERIFY(jit_values.size() == pending_index,
"JIT value count must match number of $JIT directives");
return out;
}
static std::string GeneratedOutputPath(std::string_view original_path)
{
const size_t last_sep = original_path.find_last_of("/\\");
const size_t dot = original_path.find_last_of('.');
const bool dot_in_filename =
(dot != std::string_view::npos) &&
(last_sep == std::string_view::npos || dot > last_sep);
const std::string_view base =
dot_in_filename ? original_path.substr(0, dot) : original_path;
return std::string(base) + "_generated.hpp";
}
static void WriteFileOrWarn(const std::string &path,
const std::string &contents)
{
std::ofstream out(path);
if (!out.is_open())
{
std::cerr << "could not write generated file " << path << "\n";
return;
}
out << contents;
}
class JitQFunction
{
public:
template <typename ImplT, size_t N>
JitQFunction(ImplT, const std::string &fn,
const std::array<bool, N> &activity_map)
{
using qf_signature = typename
mfem::future::get_function_signature<
decltype(&ImplT::operator())>::type;
using qf_param_ts = typename qf_signature::parameter_ts;
constexpr size_t nparams = mfem::future::tuple_size<qf_param_ts>::value;
static_assert(N == nparams, "activity_map size must match qfunc arity");
this->fn = fn;
this->nparams = nparams;
this->activity_map.reserve(N);
for (size_t i = 0; i < N; ++i)
{
this->activity_map.push_back(activity_map[i]);
}
{
const auto param_types_arr = ParamTypeStrings<qf_param_ts>();
this->param_types.assign(param_types_arr.begin(), param_types_arr.end());
}
this->return_type = TypeNameString<typename qf_signature::return_t>();
this->return_is_void = std::is_same_v<typename qf_signature::return_t, void>;
this->impl_type_name = TypeNameString<ImplT>();
this->jit_var_names = ExtractJitVarNames(ReadFileOrEmpty(fn));
}
template <typename ReturnT, typename... Args>
ReturnT run(std::string_view name,
std::initializer_list<std::pair<std::string_view, std::string_view>> jit_values,
Args&&... args)
{
auto ordered_values = MatchJitValues(jit_values);
auto &mod = GetOrCreateModule(ordered_values);
auto &instance = mod.instantiate(std::string(name), std::string());
return instance.template run<ReturnT>(std::forward<Args>(args)...);
}
template <typename ReturnT, typename... Args>
ReturnT run_primal(
std::initializer_list<std::pair<std::string_view, std::string_view>> jit_values,
Args&&... args)
{
return run<ReturnT>(qfunc_name, jit_values,
std::forward<Args>(args)...);
}
template <typename ReturnT, typename... Args>
ReturnT run_derivative(
std::initializer_list<std::pair<std::string_view, std::string_view>> jit_values,
Args&&... args)
{
return run<ReturnT>(qfunc_name + "_fwddiff", jit_values,
std::forward<Args>(args)...);
}
private:
std::vector<std::string_view> MatchJitValues(
std::initializer_list<std::pair<std::string_view, std::string_view>>
named_values) const
{
std::unordered_map<std::string_view, std::string_view> value_map;
for (const auto &[name, value] : named_values)
{
value_map[name] = value;
}
std::vector<std::string_view> ordered_values;
ordered_values.reserve(jit_var_names.size());
for (const auto &var_name : jit_var_names)
{
auto it = value_map.find(var_name);
MFEM_VERIFY(it != value_map.end(),
"missing JIT value for variable: " << var_name);
ordered_values.push_back(it->second);
}
MFEM_VERIFY(ordered_values.size() == named_values.size(),
"provided " << named_values.size() << " JIT values but expected "
<< jit_var_names.size());
return ordered_values;
}
std::string BuildModuleCode(const std::vector<std::string> &jit_values) const
{
std::string module_code =
RewriteKernelForJit(ReadFileOrEmpty(fn), jit_values);
module_code += "\n\n";
module_code += "// --- generated ---\n";
module_code +=
"template <typename return_type, typename... Args>\n"
"return_type __enzyme_fwddiff(Args...);\n"
"\n"
"extern int enzyme_const;\n"
"extern int enzyme_dup;\n"
"\n";
// Generate a primal wrapper with the requested symbol name, so the kernel
// header can just define the qfunc as a functor.
//
// Note: Proteus instantiates entrypoints via `qfunc_wrapper<>(...)` even
// when there are no user template args, so keep the wrapper itself a
// template (with a default parameter) while still doing literal `$JIT`
// replacements in the kernel code.
module_code += "template <typename = void>\n";
module_code += return_type + " " +
std::string(qfunc_name) + "(";
bool first = true;
for (size_t i = 0; i < nparams; ++i)
{
if (!first) { module_code += ", "; }
first = false;
module_code += param_types[i] + " Arg" + std::to_string(i);
}
module_code += ")\n";
module_code += "{\n";
module_code += " " + impl_type_name + " qf;\n";
if (return_is_void)
{
module_code += " ";
}
else
{
module_code += " return ";
}
module_code += "qf(";
for (size_t i = 0; i < nparams; ++i)
{
if (i) { module_code += ", "; }
module_code += "Arg" + std::to_string(i);
}
module_code += ");\n";
module_code += "}\n\n";
module_code += "template <typename = void>\n";
module_code += return_type + " " +
std::string(qfunc_name) + "_fwddiff(";
first = true;
for (size_t i = 0; i < nparams; ++i)
{
if (!first) { module_code += ", "; }
first = false;
module_code += param_types[i] + " Arg" + std::to_string(i);
if (activity_map[i])
{
module_code += ", " + param_types[i] + " dArg" + std::to_string(i);
}
}
module_code += ")\n";
module_code += "{\n";
if (return_is_void)
{
module_code += " __enzyme_fwddiff<void>(\n";
}
else
{
module_code += " return __enzyme_fwddiff<" +
return_type + ">(\n";
}
module_code += " (void*)" + std::string(qfunc_name) + "<>";
module_code += ",\n";
for (size_t i = 0; i < nparams; ++i)
{
if (activity_map[i])
{
module_code += " enzyme_dup, Arg" + std::to_string(i) +
", dArg" + std::to_string(i);
}
else
{
module_code += " enzyme_const, Arg" + std::to_string(i);
}
module_code += (i + 1 == nparams) ? ");\n" : ",\n";
}
module_code += "}\n";
WriteFileOrWarn(GeneratedOutputPath(fn), module_code);
return module_code;
}
proteus::CppJitModule &GetOrCreateModule(
const std::vector<std::string_view> &jit_values)
{
std::string key;
for (const auto &val : jit_values)
{
if (!key.empty()) { key += ","; }
key += val;
}
auto it = modules.find(key);
if (it != modules.end())
{
return *it->second;
}
std::vector<std::string> values(jit_values.begin(), jit_values.end());
std::string code = BuildModuleCode(values);
auto mod = std::make_unique<proteus::CppJitModule>("host", code,
DefaultExtraArgs());
auto [inserted, ok] = modules.emplace(key, std::move(mod));
MFEM_VERIFY(ok, "failed to cache JIT module");
return *inserted->second;
}
static std::vector<std::string> DefaultExtraArgs()
{
return {"-fplugin=/Users/andrej1/local/enzyme/lib/ClangEnzyme-20.dylib"};
}
std::string qfunc_name = "qfunc_wrapper";
std::string fn;
size_t nparams = 0;
std::vector<bool> activity_map;
std::vector<std::string> param_types;
std::string return_type;
bool return_is_void = false;
std::string impl_type_name;
std::vector<std::string> jit_var_names;
std::unordered_map<std::string, std::unique_ptr<proteus::CppJitModule>> modules;
};
int main()
{
const size_t N = 4;
const size_t M = 5;
const double A = 123.4;
std::vector<double> X(N);
std::vector<double> Y(N);
for (size_t i = 0; i < N; ++i)
{
X[i] = static_cast<double>(i + 1);
Y[i] = static_cast<double>(N - i);
}
// // >>> user interface calls
// const std::string kernel_path = std::string(util::thisFileDir) +
// "/jitplayground.hpp";
// JitQFunction qf(daxpy_op{}, kernel_path, std::array{false, true, false});
// // <<< user interface calls
// // this will happen internally in dFEM
daxpy_op op;
printf("\n\nfunction call\n");
op(&A, X.data(), Y.data(), &N);
// reset X for the derivative test
for (size_t i = 0; i < N; ++i)
{
X[i] = static_cast<double>(i + 1);
Y[i] = static_cast<double>(N - i);
}
std::vector<double> dX(N, 1.0);
printf("\n\nforward diff call\n");
daxpy_op_fwddiff(&A, X.data(), dX.data(), Y.data(), &N);
std::vector<double> dX_manual(N, A);
printf("\n\nderivative checks\n");
std::cout << "dX: ";
for (size_t i = 0; i < N; ++i)
{
std::cout << dX[i] << (i + 1 == N ? '\n' : ' ');
}
std::cout << "dX_manual: ";
for (size_t i = 0; i < N; ++i)
{
std::cout << dX_manual[i] << (i + 1 == N ? '\n' : ' ');
}
double max_abs_err = 0.0;
for (size_t i = 0; i < N; ++i)
{
max_abs_err = std::max(max_abs_err, std::abs(dX[i] - dX_manual[i]));
}
std::cout << "max |dX - dX_manual| = " << max_abs_err << "\n";
return 0;
}
+58
View File
@@ -0,0 +1,58 @@
#pragma once
#include <cstddef>
#include <vector>
#include <type_traits>
#include "proteus/JitInterface.h"
struct daxpy_op
{
void operator()(
const double *a,
double *x,
const double *y,
const size_t *N) const
{
const size_t n = *N;
auto lam = [=, n = proteus::jit_variable(n)]
() __attribute__((annotate("jit")))
{
printf("N = %zu\n", n);
for (size_t i = 0; i < n; ++i)
{
printf("x[%zu] = %f, y[%zu] = %f\n", i, x[i], i, y[i]);
x[i] = *a * x[i] + y[i];
printf("updated x[%zu] = %f\n", i, x[i]);
}
};
proteus::register_lambda(lam);
lam();
}
};
template <typename return_type, typename... Args>
return_type __enzyme_fwddiff(Args...);
extern int enzyme_const;
extern int enzyme_dup;
void daxpy_op_wrapper(const double * Arg0, double * Arg1,
const double * Arg2, const size_t *Arg3)
{
daxpy_op qf;
qf(Arg0, Arg1, Arg2, Arg3);
}
void daxpy_op_fwddiff(const double * Arg0, double * Arg1,
double * dArg1, const double * Arg2, const size_t *Arg3)
{
__enzyme_fwddiff<void>(
(void*)daxpy_op_wrapper,
enzyme_const, Arg0,
enzyme_dup, Arg1, dArg1,
enzyme_const, Arg2,
enzyme_const, Arg3);
}
+2
View File
@@ -157,6 +157,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
+20 -13
View File
@@ -121,6 +121,11 @@ set(SRCS
qinterp/eval_hdiv.cpp
qinterp/grad_by_nodes.cpp
qinterp/grad_by_vdim.cpp
qinterp/grad_transpose.cpp
qinterp/grad_transpose_by_nodes.cpp
qinterp/grad_transpose_by_vdim.cpp
qinterp/eval_transpose.cpp
qinterp/eval_transpose_by_vdim.cpp
qspace.cpp
quadinterpolator.cpp
quadinterpolator_face.cpp
@@ -133,7 +138,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
@@ -278,8 +283,10 @@ set(HDRS
qfunction.hpp
qinterp/det.hpp
qinterp/eval.hpp
qinterp/eval_transpose.hpp
qinterp/eval_hdiv.hpp
qinterp/grad.hpp
qinterp/grad_transpose.hpp
qspace.hpp
quadinterpolator.hpp
quadinterpolator_face.hpp
@@ -313,36 +320,36 @@ set(HDRS
)
if (MFEM_USE_SIDRE)
list(APPEND SRCS sidredatacollection.cpp)
list(APPEND HDRS sidredatacollection.hpp)
list(APPEND SRCS sidredatacollection.cpp)
list(APPEND HDRS sidredatacollection.hpp)
endif()
if (MFEM_USE_CONDUIT)
list(APPEND SRCS conduitdatacollection.cpp)
list(APPEND HDRS conduitdatacollection.hpp)
list(APPEND SRCS conduitdatacollection.cpp)
list(APPEND HDRS conduitdatacollection.hpp)
endif()
if (MFEM_USE_ADIOS2)
list(APPEND SRCS adios2datacollection.cpp)
list(APPEND HDRS adios2datacollection.hpp)
list(APPEND SRCS adios2datacollection.cpp)
list(APPEND HDRS adios2datacollection.hpp)
endif()
if (MFEM_USE_FMS)
list(APPEND SRCS fmsdatacollection.cpp fmsconvert.cpp)
list(APPEND HDRS fmsdatacollection.hpp fmsconvert.hpp)
list(APPEND SRCS fmsdatacollection.cpp fmsconvert.cpp)
list(APPEND HDRS fmsdatacollection.hpp fmsconvert.hpp)
endif()
if (MFEM_USE_MPI)
list(APPEND SRCS
list(APPEND SRCS
pbilinearform.cpp
pfespace.cpp
pgridfunc.cpp
plinearform.cpp
pnonlinearform.cpp
prestriction.cpp)
# If this list (HDRS -> HEADERS) is used for install, we probably want the
# headers added all the time.
list(APPEND HDRS
# If this list (HDRS -> HEADERS) is used for install, we probably want the
# headers added all the time.
list(APPEND HDRS
pbilinearform.hpp
pfespace.hpp
pgridfunc.hpp
+7 -3
View File
@@ -729,7 +729,8 @@ void BilinearForm::Assemble(int skip_zeros)
tr = mesh -> GetBdrFaceTransformations (i);
if (tr != NULL)
{
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
mfem::DofTransformation doftrans;
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
fe1 = fes -> GetFE (tr -> Elem1No);
// The fe2 object is really a dummy and not used on the boundaries,
// but we can't dereference a NULL pointer, and we don't want to
@@ -743,6 +744,7 @@ void BilinearForm::Assemble(int skip_zeros)
boundary_face_integs[k] -> AssembleFaceMatrix (*fe1, *fe2, *tr,
elemmat);
doftrans.TransformDual(elemmat);
mat -> AddSubMatrix (vdofs, vdofs, elemmat, skip_zeros);
}
}
@@ -1723,6 +1725,7 @@ void MixedBilinearForm::Assemble(int skip_zeros)
}
}
DofTransformation dom_dof_trans, ran_dof_trans;
for (int i = 0; i < trial_fes -> GetNBE(); i++)
{
const int bdr_attr = mesh->GetBdrAttribute(i);
@@ -1731,8 +1734,8 @@ void MixedBilinearForm::Assemble(int skip_zeros)
ftr = mesh -> GetBdrFaceTransformations (i);
if (ftr != NULL)
{
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs);
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs);
trial_fes->GetElementVDofs(ftr->Elem1No, trial_vdofs, dom_dof_trans);
test_fes->GetElementVDofs(ftr->Elem1No, test_vdofs, ran_dof_trans);
trial_fe1 = trial_fes->GetFE(ftr->Elem1No);
test_fe1 = test_fes->GetFE(ftr->Elem1No);
// The test_fe2 object is really a dummy and not used on the
@@ -1748,6 +1751,7 @@ void MixedBilinearForm::Assemble(int skip_zeros)
boundary_face_integs[k]->AssembleFaceMatrix(*trial_fe1, *test_fe1, *trial_fe2,
*test_fe2,
*ftr, elemmat);
TransformDual(ran_dof_trans, dom_dof_trans, elemmat);
mat->AddSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
}
}
+18 -13
View File
@@ -2178,18 +2178,22 @@ class DiffusionIntegrator: public BilinearFormIntegrator
{
public:
using ApplyKernelType = void(*)(const int, const bool, const Array<real_t>&,
const Array<real_t>&, const Array<real_t>&,
const Array<real_t>&,
const Vector&, const Vector&,
Vector&, const int, const int);
using DiffusionApplyKernelType = void(*)(const int, const bool,
const Array<real_t>&,
const Array<real_t>&, const Array<real_t>&,
const Array<real_t>&,
const Vector&, const Vector&,
Vector&, const int, const int);
using DiagonalKernelType = void(*)(const int, const bool, const Array<real_t>&,
const Array<real_t>&, const Vector&, Vector&,
const int, const int);
using DiffusionDiagonalKernelType = void(*)(const int, const bool,
const Array<real_t>&,
const Array<real_t>&, const Vector&, Vector&,
const int, const int);
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
MFEM_REGISTER_KERNELS(DiffusionApplyPAKernel, DiffusionApplyKernelType,
(int, int, int));
MFEM_REGISTER_KERNELS(DiffusionDiagonalPAKernel, DiffusionDiagonalKernelType,
(int, int, int));
struct Kernels { Kernels(); };
protected:
@@ -2209,6 +2213,7 @@ private:
const FiniteElementSpace *fespace;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
public:
int dim, ne, dofs1D, quad1D;
Vector pa_data;
bool symmetric = true; ///< False if using a nonsymmetric matrix coefficient
@@ -2350,8 +2355,8 @@ public:
template <int DIM, int D1D, int Q1D>
static void AddSpecialization()
{
ApplyPAKernels::Specialization<DIM,D1D,Q1D>::Add();
DiagonalPAKernels::Specialization<DIM,D1D,Q1D>::Add();
DiffusionApplyPAKernel::Specialization<DIM,D1D,Q1D>::Add();
DiffusionDiagonalPAKernel::Specialization<DIM,D1D,Q1D>::Add();
}
protected:
const IntegrationRule* GetDefaultIntegrationRule(
@@ -2710,7 +2715,7 @@ public:
/** Integrator for $(-Q u, \nabla v)$ for Nedelec ($u$) and $H^1$ ($v$) elements.
This is equivalent to a weak divergence of the $H(curl$ basis functions. */
This is equivalent to a weak divergence of the $H(curl)$ basis functions. */
class VectorFEWeakDivergenceIntegrator: public BilinearFormIntegrator
{
protected:
+67 -28
View File
@@ -39,8 +39,8 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
b_type = b_type_i;
cp_type = cp_type_i;
tol = tol_i;
lbound.SetSize(nb, ncp);
ubound.SetSize(nb, ncp);
lbound.SetSize(ncp, nb);
ubound.SetSize(ncp, nb);
nodes.SetSize(nb);
weights.SetSize(nb);
control_points.SetSize(ncp);
@@ -125,21 +125,25 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
{
if (j == 0)
{
lbound(i, j) = bv(i);
ubound(i, j) = bv(i);
lbound(j,i) = bv(i);
ubound(j,i) = bv(i);
}
else if (j == ncp-1)
{
lbound(i, j) = bv(i);
ubound(i, j) = bv(i);
lbound(j,i) = bv(i);
ubound(j,i) = bv(i);
}
else
{
vals(0) = bv(i);
vals(1) = bmv(i) + dm*bdmv(i);
vals(2) = bpv(i) + dp*bdpv(i);
lbound(i, j) = vals.Min()-tol; // tolerance for good measure
ubound(i, j) = vals.Max()+tol; // tolerance for good measure
lbound(j,i) = vals.Min()-tol; // tolerance for good measure
ubound(j,i) = vals.Max()+tol; // tolerance for good measure
if (b_type == 2)
{
lbound(j,i) = std::max(lbound(j,i),0_r);
}
}
}
}
@@ -273,8 +277,7 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
intmax.SetSize(ncp);
intmin = 0.0;
intmax = 0.0;
Vector coeffm(nb);
coeffm = 0.0;
Vector coeffm;
real_t a0 = 0.0;
real_t a1 = 0.0;
@@ -302,6 +305,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
// compute L2 projection for linear bases: a0 + a1*x
if (proj)
{
coeffm.SetSize(nb);
coeffm = 0.0;
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes_int(i)-1;
@@ -342,8 +347,8 @@ void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
real_t c = coeffm(i);
for (int j = 0; j < ncp; j++)
{
intmin(j) += min(lbound(i,j)*c, ubound(i,j)*c);
intmax(j) += max(lbound(i,j)*c, ubound(i,j)*c);
intmin(j) += min(lbound(j,i)*c, ubound(j,i)*c);
intmax(j) += max(lbound(j,i)*c, ubound(j,i)*c);
}
}
}
@@ -474,10 +479,10 @@ void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
real_t w1 = intmaxT(id2++);
for (int k = 0; k < ncp; k++) // kth row
{
vals(0) = w0*lbound(j,k);
vals(1) = w0*ubound(j,k);
vals(2) = w1*lbound(j,k);
vals(3) = w1*ubound(j,k);
vals(0) = w0*lbound(k,j);
vals(1) = w0*ubound(k,j);
vals(2) = w1*lbound(k,j);
vals(3) = w1*ubound(k,j);
intmin(k*ncp+i) += vals.Min();
intmax(k*ncp+i) += vals.Max();
}
@@ -553,17 +558,17 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
for (int i = 0; i < nb; i++)
{
x = 2.0*nodes(i)-1; // x-coordinate
minBounds(i) -= a0V(j) + a1V(j)*x;
maxBounds(i) -= a0V(j) + a1V(j)*x;
minNodalVals(i) -= a0V(j) + a1V(j)*x;
maxNodalVals(i) -= a0V(j) + a1V(j)*x;
}
// Compute Bernstein coefficients
LUFactors lu(basisMatLU.GetData(), lu_ip.GetData());
lu.Solve(nb, 1, minBounds.GetData());
lu.Solve(nb, 1, maxBounds.GetData());
lu.Solve(nb, 1, minNodalVals.GetData());
lu.Solve(nb, 1, maxNodalVals.GetData());
for (int i = 0; i < nb; i++)
{
intminT(i*ncp2+j) = minBounds(i);
intmaxT(i*ncp2+j) = maxBounds(i);
intminT(i*ncp2+j) = minNodalVals(i);
intmaxT(i*ncp2+j) = maxNodalVals(i);
}
}
}
@@ -617,10 +622,10 @@ void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
real_t w1 = intmaxT(id2++);
for (int k = 0; k < ncp; k++) // kth slice
{
vals(0) = w0*lbound(j,k);
vals(1) = w0*ubound(j,k);
vals(2) = w1*lbound(j,k);
vals(3) = w1*ubound(j,k);
vals(0) = w0*lbound(k,j);
vals(1) = w0*ubound(k,j);
vals(2) = w1*lbound(k,j);
vals(3) = w1*ubound(k,j);
intmin(k*ncp2+i) += vals.Min();
intmax(k*ncp2+i) += vals.Max();
}
@@ -653,7 +658,8 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
Vector &nodesBern) const
{
const int nbern = nodesBern.Size();
L2_SegmentElement el(nbern-1, 2); // we use L2 to leverage lexicographic order
L2_SegmentElement el(nbern-1, 2);
// we use L2 to leverage lexicographic order
Array<int> ordering = el.GetLexicographicOrdering();
basisMat.SetSize(nbern, nbern);
Vector shape(nbern);
@@ -666,6 +672,39 @@ void PLBound::SetupBernsteinBasisMat(DenseMatrix &basisMat,
}
}
DenseMatrix PLBound::GetBoundingMatrix(int dim, bool is_lower) const
{
if (dim > 1)
{
const int ncpd = static_cast<int>(std::pow(ncp, dim));
const int nbd = static_cast<int>(std::pow(nb, dim));
DenseMatrix boundND(ncpd, nbd);
Vector phimin, phimax, col;
Vector coeffs(nbd);
coeffs = 0.0;
for (int j = 0; j < nbd; j++)
{
coeffs(j) = 1.0;
boundND.GetColumnReference(j, col);
GetNDBounds(dim, coeffs, phimin, phimax);
col = is_lower ? phimin : phimax;
coeffs(j) = 0.0;
}
return boundND;
}
return is_lower ? lbound : ubound;
}
DenseMatrix PLBound::GetLowerBoundMatrix(int dim) const
{
return GetBoundingMatrix(dim, true);
}
DenseMatrix PLBound::GetUpperBoundMatrix(int dim) const
{
return GetBoundingMatrix(dim, false);
}
constexpr int PLBound::min_ncp_gl_x[2][11];
constexpr int PLBound::min_ncp_gll_x[2][11];
constexpr int PLBound::min_ncp_pos_x[2][11];
@@ -716,4 +755,4 @@ void PLBound::Print(std::ostream &outp) const
ubound.Print(outp);
}
}
}
+71 -20
View File
@@ -19,14 +19,18 @@ namespace mfem
{
/** @name Piecewise linear bounds of bases
\brief Piecewise linear bounds of bases can be used to compute bounds on the grid function in each element. The bounds for the bases are constructed based on the following parameters:
\brief Piecewise linear bounds of bases can be used to compute bounds on
the grid function in each element. The bounds for the bases are constructed
based on the following parameters:
(i) @b nb: number of bases/nodes in 1D (i.e. polynomial order+1),
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
(ii) @b b_type: bases type, 0 - Lagrange interpolants on Gauss-Legendre
nodes, 1 - Lagrange interpolants on Gauss-Lobatto-Legendre nodes, and
2 - Positive/Bernstein bases on uniformly distributed nodes,
(iii) @b ncp: number of control points used to construct the piecewise linear bounds
(iii) @b ncp: number of control points used to construct the piecewise
linear bounds
(iv) @b cp_type: control point distribution. 0 - GL + end-points,
1 - Chebyshev.
@@ -35,7 +39,9 @@ namespace mfem
If the user does not specify @b ncp and @b cp_type, the minimum value of
@b ncp is used that would bound the bases for the @b cp_type. We default
to @b cp_type = 0 as it requires fewer number of points to bound the bases. Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and increasing @b ncp results in tighter bounds.
to @b cp_type = 0 as it requires fewer number of points to bound the bases.
Typically, @b ncp = 2 @b nb is sufficient to get fairly compact bounds, and
increasing @b ncp results in tighter bounds.
Finally, only tensor-product elements are currently supported.
@@ -54,7 +60,7 @@ 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;
DenseMatrix lbound, ubound; // nb x ncp matrices with bounds of all bases
DenseMatrix lbound, ubound; // ncp x nb matrices with bounds of all bases
// Some auxillary storage for computing the bounds with Bernstein
DenseMatrix basisMatNodes; // Bernstein bases at equispaced nodes
DenseMatrix basisMatInt; // Bernstein bases at GLL nodes
@@ -80,6 +86,9 @@ private:
{3,5,8,9,11,12,13,13,14,15,16}
};
/// Helper function to extract lower or upper bounding matrix
DenseMatrix GetBoundingMatrix(int dim, bool is_lower) const;
public:
// Constructor
PLBound(const int nb_i, const int ncp_i, const int b_type_i,
@@ -92,40 +101,82 @@ public:
PLBound(const FiniteElementSpace *fes,
const int ncp_i = -1, const int cp_type_i = 0);
// Get minimum number of control points needed to bound the given bases
/// Get minimum number of control points needed to bound the given bases
int GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
int cp_type_i) const;
// Print information about the bounds
/// Print information about the bounds
void Print(std::ostream &outp = mfem::out) const;
// Enable (default) or disable linear projection before bounding.
// This projection increases the computational cost but results in tighter
// bounds.
/** @brief Enable (default) or disable linear projection before bounding.
*
* @details This projection increases the computational cost but results in
* tighter bounds.
*/
void SetProjectionFlagForBounding(bool proj_) { proj = proj_; }
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 1D/2D/3D.
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
* nodal coefficients in @a coeff in 1D/2D/3D.
*
* @param[in] rdim The spatial dimension of the element (1, 2, or 3).
* @param[in] coeff The vector of lexicographically-ordered coefficients.
* Should be of size nb^rdim, where nb is the number of
* bases/nodes in 1D. These coefficients must correspond
* to the bases type and number of bases, used in the
* constructor of PLBound.
*
* @param[out] intmin The vector of minimum bound for all control points.
* @param[out] intmax The vector of maximum bound for all control points.
* Both intmin and intmax are of size ncp^rdim, where
* ncp is the number of control points in 1D, and are
* ordered lexicographically.
*/
void GetNDBounds(const int rdim, const Vector &coeff,
Vector &intmin, Vector &intmax) const;
/// Get number of control points used to compute the bounds.
int GetNControlPoints() const { return ncp; }
/// Get 1D control point locations (lexicographic order) in [0,1].
const Vector &GetControlPoints() const { return control_points; }
/** @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
* by a simple matrix-vector product with the
* lexicographically-ordered nodal coefficients.
* The resulting output is also lexicographically-ordered.
*
* @note These matrices do not account for the linear projection step that
* is optionally done in GetNDBounds before bounding the function.
*/
///@{
DenseMatrix GetLowerBoundMatrix(int dim = 1) const;
DenseMatrix GetUpperBoundMatrix(int dim = 1) const;
///@}
private:
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 1D.
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
* nodal coefficients in @a coeff in 1D.
* See GetNDBounds for details of the input and output parameters.
*/
void Get1DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 2D.
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
* nodal coefficients in @a coeff in 2D.
* See GetNDBounds for details of the input and output parameters.
*/
void Get2DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 3D.
/** @brief Compute piecewise linear bounds for the lexicographically-ordered
* nodal coefficients in @a coeff in 3D.
* See GetNDBounds for details of the input and output parameters.
*/
void Get3DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/// Setup matrix used to compute values at given 1D locations in [0,1]
/// for Bernstein bases.
/** @brief Setup matrix used to compute values at given 1D locations in [0,1]
* for Bernstein bases.
*/
void SetupBernsteinBasisMat(DenseMatrix &basisMat, Vector &nodesBern) const;
void Setup(const int nb_i, const int ncp_i, const int b_type_i,
+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
+19
View File
@@ -82,6 +82,25 @@ public:
/// underlying #fes
int VectorDim() const;
/// Copy assignment. Only the data of the base class Vector is copied.
/** It is assumed that this object and @a rhs use FiniteElementSpace%s that
have the same size.
@note Defining this method overwrites the implicitly defined copy
assignment operator. */
ComplexGridFunction &operator=(const ComplexGridFunction &rhs)
{ return operator=((const Vector &)rhs); }
/// Copy the data from @a v.
/** The size of @a v must be equal to double of the size of the associated
FiniteElementSpace #fes. */
ComplexGridFunction &operator=(const Vector &v)
{
MFEM_ASSERT(fes && v.Size() == 2*fes->GetVSize(), "");
Vector::operator=(v);
return *this;
}
/// Assign constant values to the ComplexGridFunction data.
ComplexGridFunction &operator=(const std::complex<real_t> & value)
{ *gfr = value.real(); *gfi = value.imag(); return *this; }
+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";
+587
View File
@@ -0,0 +1,587 @@
// 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.
#pragma once
#include <cassert>
#include <cstddef>
// #include "fem/kernels.hpp"
#include "fem/kernels3d.hpp"
namespace ker = mfem::kernels::internal;
namespace low = mfem::kernels::internal::low;
#include "fem/kernel_dispatch.hpp"
// #include "linalg/kernels.hpp"
#include "util.hpp"
#undef NVTX_COLOR
#define NVTX_COLOR ::nvtx::kOrchid
namespace mfem::future
{
/** @brief Zero-copy view of a contiguous block as a `tensor<T, n1>` */
template<typename T, int n1>
MFEM_HOST_DEVICE
const tensor<T, n1>& as_tensor(const T* ptr)
{
// std::launder makes this defined behavior under strict aliasing rules
return *std::launder(reinterpret_cast<const tensor<T, n1>*>(ptr));
}
// convenience overload if you prefer a mutable view
template<typename T, int n1>
MFEM_HOST_DEVICE
tensor<T, n1>& as_tensor(T* ptr)
{
return *std::launder(reinterpret_cast<tensor<T, n1>*>(ptr));
}
/** @brief Zero-copy view of a contiguous block as a `tensor<T, n1, n2>` */
template<typename T, int n1, int n2>
MFEM_HOST_DEVICE
const tensor<T, n1, n2>& as_tensor(const T* ptr)
{
// std::launder makes this defined behavior under strict aliasing rules
return *std::launder(reinterpret_cast<const tensor<T, n1, n2>*>(ptr));
}
// convenience overload if you prefer a mutable view
template<typename T, int n1, int n2>
MFEM_HOST_DEVICE
tensor<T, n1, n2>& as_tensor(T* ptr)
{
return *std::launder(reinterpret_cast<tensor<T, n1, n2>*>(ptr));
}
/** @brief Zero-copy view of a contiguous block as a `tensor<T, n1, n2, n3>` */
template<typename T, int n1, int n2, int n3>
MFEM_HOST_DEVICE
const tensor<T, n1, n2, n3>& as_tensor(const T* ptr)
{
// std::launder makes this defined behavior under strict aliasing rules
return *std::launder(reinterpret_cast<const tensor<T, n1, n2, n3>*>(ptr));
}
// convenience overload if you prefer a mutable view
template<typename T, int n1, int n2, int n3>
MFEM_HOST_DEVICE
tensor<T, n1, n2, n3>& as_tensor(T* ptr)
{
return *std::launder(reinterpret_cast<tensor<T, n1, n2, n3>*>(ptr));
}
/** @brief Zero-copy view of a contiguous block as a `tensor<T, n1, n2, n3, n4>` */
template<typename T, int n1, int n2, int n3, int n4>
MFEM_HOST_DEVICE
const tensor<T, n1, n2, n3, n4>& as_tensor(const T* ptr)
{
// std::launder makes this defined behavior under strict aliasing rules
return *std::launder(reinterpret_cast<const tensor<T, n1, n2, n3, n4>*>(ptr));
}
// convenience overload if you prefer a mutable view
template<typename T, int n1, int n2, int n3, int n4>
MFEM_HOST_DEVICE
tensor<T, n1, n2, n3, n4>& as_tensor(T* ptr)
{
return *std::launder(reinterpret_cast<tensor<T, n1, n2, n3, n4>*>(ptr));
}
template <std::size_t N>
MFEM_HOST_DEVICE inline
std::array<real_t*, N>
load_field_e_ptr(const std::array<DeviceTensor<2>, N> &fields_e,
const int e)
{
std::array<real_t*, N> f;
for_constexpr<N>([&](auto i) { f[i] = &fields_e[i](0, e); });
return f;
}
namespace qf
{
template <int T_Q1D,
size_t num_args,
typename reg_t,
typename qfunc_t,
typename args_ts>
MFEM_HOST_DEVICE inline
void apply_kernel(reg_t &res /*output*/,
reg_t &reg,
const real_t *rd,
const int qx, const int qy, const int qz,
const qfunc_t &qfunc, args_ts &args)
{
if constexpr (num_args == 2) // PAApply
{
// ∇u
tensor<real_t, 3> &arg_0 = get<0>(args);
arg_0[0] = reg[qz][qy][qx][0];
arg_0[1] = reg[qz][qy][qx][1];
arg_0[2] = reg[qz][qy][qx][2];
// D (PA data)
tensor<real_t, 3, 3> &arg_1 = get<1>(args);
if constexpr (T_Q1D > 0)
{
const auto *D = (const real_t (*)[T_Q1D][T_Q1D][3][3]) rd;
for (int k = 0; k < 3; k++)
{
for (int j = 0; j < 3; j++)
{
arg_1[k][j] = D[qx][qy][qz][k][j];
}
}
}
else
{
static_assert(false);
// const auto D = Reshape(r2, 3, 3, Q1D, Q1D, Q1D);
// for (int j = 0; j < 3; j++)
// {
// for (int k = 0; k < 3; k++)
// {
// arg_1[k][j] = D(j, k, qz, qy, qx);
// }
// }
}
}
else
{
// MFApply comes here
assert(false);
// MFEM_ABORT("Only two arguments (∇u and D) are supported in apply_kernel for now");
}
const auto r = get<0>(apply(qfunc, args));
if constexpr (decltype(r)::ndim == 1)
{
// process_qf_result_from_reg(r0, qx, qy, qz, r);
as_tensor<real_t, 3>(&res[qz][qy][qx][0]) = r;
}
else
{
static_assert(false);
}
}
} // namespace qf
#define MFEM_D2Q_MAX_SIZE 4
static MFEM_CONSTANT real_t Bi[MFEM_D2Q_MAX_SIZE][8*8], Bo[8*8];
static MFEM_CONSTANT real_t Gi[MFEM_D2Q_MAX_SIZE][8*8], Go[8*8];
template<size_t num_fields,
size_t num_inputs,
size_t num_outputs,
typename restriction_cb_t,
typename qfunc_t,
typename input_t,
typename output_fop_t>
class NewActionCallback
{
restriction_cb_t &restriction_cb;
qfunc_t &qfunc;
input_t &inputs;
const std::array<size_t, num_inputs> &input_to_field;
const std::array<DofToQuadMap, num_inputs> &input_dtq_maps;
const std::array<DofToQuadMap, num_outputs> &output_dtq_maps;
const int num_entities;
const int test_vdim;
const int num_test_dof;
const int dimension;
const ThreadBlocks &thread_blocks;
SharedMemoryInfo<num_fields, num_inputs, num_outputs> &shmem_info;
const Array<int> &attributes;
const output_fop_t &output_fop;
const Array<int> *elem_attributes;
// refs
std::vector<Vector> &fields_e;
Vector &residual_e;
std::function<void(Vector &, Vector &)> &output_restriction_transpose;
// args
std::vector<Vector> &solutions_l;
const std::vector<Vector> &parameters_l;
Vector &residual_l;
public:
NewActionCallback() = delete;
NewActionCallback(const bool use_kernels_specialization,
restriction_cb_t &restriction_cb,
qfunc_t &qfunc,
input_t &inputs,
const std::array<size_t, num_inputs> &input_to_field,
const std::array<DofToQuadMap, num_inputs> &input_dtq_maps,
const std::array<DofToQuadMap, num_outputs> &output_dtq_maps,
const int num_entities,
const int test_vdim,
const int num_test_dof,
const int dimension,
const ThreadBlocks &thread_blocks,
SharedMemoryInfo<num_fields, num_inputs, num_outputs> &shmem_info,
const Array<int> &attributes,
const output_fop_t &output_fop,
const Array<int> *elem_attributes,
// refs
std::vector<Vector> &fields_e,
Vector &residual_e,
std::function<void(Vector &, Vector &)> &output_restriction_transpose,
// args
std::vector<Vector> &solutions_l,
const std::vector<Vector> &parameters_l,
Vector &residual_l):
restriction_cb(restriction_cb),
qfunc(qfunc),
inputs(inputs),
input_to_field(input_to_field),
input_dtq_maps(input_dtq_maps),
output_dtq_maps(output_dtq_maps),
num_entities(num_entities),
test_vdim(test_vdim),
num_test_dof(num_test_dof),
dimension(dimension),
thread_blocks(thread_blocks),
shmem_info(shmem_info),
attributes(attributes),
output_fop(output_fop),
elem_attributes(elem_attributes),
fields_e(fields_e),
residual_e(residual_e),
output_restriction_transpose(output_restriction_transpose),
solutions_l(solutions_l),
parameters_l(parameters_l),
residual_l(residual_l)
{
if (!use_kernels_specialization) { return; }
NewActionCallbackKernels::template Specialization<3>::Add(); // 1
NewActionCallbackKernels::template Specialization<4>::Add(); // 2
NewActionCallbackKernels::template Specialization<5>::Add(); // 3
NewActionCallbackKernels::template Specialization<6>::Add(); // 4
NewActionCallbackKernels::template Specialization<7>::Add(); // 5
NewActionCallbackKernels::template Specialization<8>::Add(); // 6
}
template<int T_Q1D = 0>
static void action_callback_new(const int d1d,
restriction_cb_t &restriction_cb,
qfunc_t &qfunc,
[[maybe_unused]] input_t &inputs,
[[maybe_unused]] const std::array<size_t, num_inputs> &input_to_field,
const std::array<DofToQuadMap, num_inputs> &input_dtq_maps,
const std::array<DofToQuadMap, num_outputs> &output_dtq_maps,
[[maybe_unused]] const int dimension,
const int num_entities,
[[maybe_unused]] const int test_vdim,
[[maybe_unused]] const int num_test_dof,
const ThreadBlocks &thread_blocks,
[[maybe_unused]] SharedMemoryInfo<num_fields, num_inputs, num_outputs>
&shmem_info,
[[maybe_unused]] const Array<int> &attributes,
[[maybe_unused]] const output_fop_t &output_fop,
[[maybe_unused]] const Array<int> *elem_attributes,
// refs
std::vector<Vector> &fields_e,
Vector &residual_e,
std::function<void(Vector &, Vector &)> &output_restriction_transpose,
// args
std::vector<Vector> &solutions_l,
const std::vector<Vector> &parameters_l,
Vector &residual_l,
// fallback arguments
const int q1d)
{
NVTX_MARK_FUNCTION;
assert(dimension == 3);
static_assert(MFEM_D2Q_MAX_SIZE >= num_inputs, "MFEM_D2Q_MAX_SIZE error");
constexpr int DIM = 3;
[[maybe_unused]] static bool ini = (for_constexpr<num_inputs>([&](auto i)
{
const auto dtq = input_dtq_maps[i];
{
const auto [q, _, p] = dtq.B.GetShape();
const auto B = (const real_t*)input_dtq_maps[i].B;
dbg("Loading Bi[{}]: q={} p={}", i.value, q, p);
if (B) { Gpu(MemcpyToSymbol)(Bi[i], B, (p*q)*sizeof(real_t)); }
}
{
const auto [q, _, p] = dtq.G.GetShape();
const auto G = (const real_t*)input_dtq_maps[i].G;
if (G) { Gpu(MemcpyToSymbol)(Gi[i], G, (p*q)*sizeof(real_t)); }
}
if constexpr (i == 0) // output B
{
const auto dtq_o = output_dtq_maps[0];
const auto [q, _, p] = dtq_o.B.GetShape();
const auto B = (const real_t*)dtq_o.B;
if (B) { Gpu(MemcpyToSymbol)(Bo, B, (p*q)*sizeof(real_t)); }
}
if constexpr (i == 0) // output G
{
const auto dtq_o = output_dtq_maps[0];
const auto [q, _, p] = dtq_o.G.GetShape();
const auto G = (const real_t*)dtq_o.G;
if (G) { Gpu(MemcpyToSymbol)(Go, G, (p*q)*sizeof(real_t)); }
dbg("Loaded B and G to constant memory");
}
}), true);
// types
using qf_signature =
typename create_function_signature<decltype(&qfunc_t::operator())>::type;
using qf_param_ts = typename qf_signature::parameter_ts;
restriction_cb(solutions_l, parameters_l, fields_e);
NVTX_INI("res=0");
residual_e = 0.0;
NVTX_END("res=0");
// auto wrapped_fields_e =
// wrap_fields(fields_e, shmem_info.field_sizes, num_entities);
const bool has_attr = attributes.Size() > 0;
const auto d_attr = attributes.Read();
const auto d_elem_attr = elem_attributes->Read();
// const int vdim = input.vdim;
// const auto fields_e_ptr = load_field_e_ptr(wrapped_fields_e, e);
// const real_t *field_e_r = fields_e_ptr[input_to_field[i]];
// const auto fields_e_ptr = load_field_e_ptr(wrapped_fields_e, e);
const int NE = num_entities;
constexpr int VDIM = 1;
const auto XE = Reshape(fields_e[0].Read(), d1d, d1d, d1d, VDIM, NE);
const real_t *dx_ptr = fields_e[1].Read();
auto YE = Reshape(residual_e.ReadWrite(), d1d, d1d, d1d, VDIM, NE);
const auto B = (const real_t*)input_dtq_maps[0/*i*/].B;
const auto G = (const real_t*)input_dtq_maps[0/*i*/].G;
NVTX_INI("forall");
dfem::forall<T_Q1D*T_Q1D*T_Q1D>([=] MFEM_HOST_DEVICE (int e, void *)
{
if (has_attr && !d_attr[d_elem_attr[e] - 1]) { return; }
constexpr int MQ1 = T_Q1D > 0 ? T_Q1D : 8;
MFEM_SHARED real_t sm0[MQ1][MQ1][MQ1][3];
MFEM_SHARED real_t sm1[MQ1][MQ1][MQ1][3];
// real_t (&sm0_ptr)[MQ1][MQ1][MQ1][3] = sm0;
// real_t (&sm1_ptr)[MQ1][MQ1][MQ1][3] = sm1;
low::regs3d_t<DIM, MQ1> reg;
const real_t *rd = dx_ptr;
// const auto fields_e_ptr = load_field_e_ptr(wrapped_fields_e, e);
MFEM_SHARED real_t sB[MQ1][MQ1], sG[MQ1][MQ1];
// real_t (&sB_ptr)[MD1][MQ1] = sB;
// real_t (&sG_ptr)[MD1][MQ1] = sG;
// Interpolate
// for_constexpr<num_inputs>(
// [ D1D, Q1D, MQ1, e,
// &input_dtq_maps,
// &sm0_ptr, &sm1_ptr,
// &sB = sB_ptr, &sG = sG_ptr,
// &inputs,
// // &fields_e_ptr,
// &reg, &rd,
// &input_to_field ] (auto i)
{
// const auto input = get<0/*i*/>(inputs);
// using field_operator_t = std::decay_t<decltype(input)>;
// if constexpr (is_gradient_fop<field_operator_t>::value) // Grad
{
// const int vdim = input.vdim;
// const real_t *field_e_r = fields_e_ptr[input_to_field[i]];
// const auto XE = Reshape(field_e_r, D1D, D1D, D1D, vdim);
// const auto sB = reinterpret_cast<const real_t (*)[MQ1]>(Bi[i]);
// const auto sG = reinterpret_cast<const real_t (*)[MQ1]>(Gi[i]);
low::LoadMatrix(d1d, q1d, B, sB);
low::LoadMatrix(d1d, q1d, G, sG);
// for (int c = 0; c < vdim; c++)
// constexpr int c = 0;
{
low::LoadDofs3d(e, d1d, XE, sm0);
low::Grad3d(d1d, q1d, sB, sG, sm0, sm1, reg);
}
}
// else if constexpr (is_identity_fop<field_operator_t>::value) // Identity
{
// db1("Identity");
// rd = fields_e_ptr[input_to_field[i]];
// rd = dx_ptr;
}
// else if constexpr (is_weight_fop<field_operator_t>::value) // Weight
// {
// dbg("Weight");
// rw = fields_e_ptr[input_to_field[i]]; // 🔥
// }
// else
{
// MFApply comes here
// assert(false);
// MFEM_ABORT("Only Grad and Identity field operators are supported");
}
}//); // for_constexpr<num_inputs>
MFEM_FOREACH_THREAD_DIRECT(qz,z,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,q1d)
{
#if 0
auto qf_args = decay_tuple<qf_param_ts> {};
qf::apply_kernel<T_Q1D, num_inputs>
(reg, reg, rd, qx, qy, qz, qfunc, qf_args);
#elif 0
real_t v[3], u[3] = { reg[qz][qy][qx][0],
reg[qz][qy][qx][1],
reg[qz][qy][qx][2]
};
const auto *D = (real_t (*)[T_Q1D][T_Q1D][3][3]) rd;
kernels::Mult(3, 3, &D[qx][qy][qz][0][0], u, v);
reg[qz][qy][qx][0] = v[0];
reg[qz][qy][qx][1] = v[1];
reg[qz][qy][qx][2] = v[2];
#elif 0
const auto *D = (real_t (*)[T_Q1D][T_Q1D][3][3]) rd;
const auto args = decay_tuple<qf_param_ts>
{
{{ reg[qz][qy][qx][0], reg[qz][qy][qx][1], reg[qz][qy][qx][2] }},
{{
{{ D[qx][qy][qz][0][0], D[qx][qy][qz][0][1], D[qx][qy][qz][0][2] }},
{{ D[qx][qy][qz][1][0], D[qx][qy][qz][1][1], D[qx][qy][qz][1][2] }},
{{ D[qx][qy][qz][2][0], D[qx][qy][qz][2][1], D[qx][qy][qz][2][2] }}
}
}
};
const auto r = get<0>(apply(qfunc, args));
reg[qz][qy][qx][0] = r[0];
reg[qz][qy][qx][1] = r[1];
reg[qz][qy][qx][2] = r[2];
#elif 0
auto u = as_tensor<real_t, 3>(&reg[qz][qy][qx][0]);
const auto *d = (real_t (*)[T_Q1D][T_Q1D][3][3]) rd;
auto D = as_tensor<real_t, 3, 3>(&d[qx][qy][qz][0][0]);
auto r = D * u;
reg[qz][qy][qx][0] = r[0];
reg[qz][qy][qx][1] = r[1];
reg[qz][qy][qx][2] = r[2];
#else
auto args = decay_tuple<qf_param_ts> {};
get<0>(args) = as_tensor<real_t, 3>(&reg[qz][qy][qx][0]);
if constexpr (T_Q1D > 0)
{
get<1>(args) = as_tensor<real_t, 3, 3>(rd + 9*(qx*T_Q1D*T_Q1D + qy*T_Q1D + qz));
}
else
{
get<1>(args) = as_tensor<real_t, 3, 3>(rd + 9*(qx*q1d*q1d + qy*q1d + qz));
}
auto r = get<0>(apply(qfunc, args));
if constexpr (decltype(r)::ndim == 1)
{
as_tensor<real_t, 3>(&reg[qz][qy][qx][0]) = r;
}
else { static_assert(false); }
#endif
}
}
}
MFEM_SYNC_THREAD;
// Integrate
// if constexpr (is_gradient_fop<std::decay_t<output_fop_t>>::value) // Gradient
{
// const auto sB = reinterpret_cast<const real_t (*)[MQ1]>(Bo);
// const auto sG = reinterpret_cast<const real_t (*)[MQ1]>(Go);
low::GradTranspose3d(d1d, q1d, sB, sG, reg, sm1, sm0);
low::WriteDofs3d(d1d, 0, e, reg, YE);
}
},
num_entities, thread_blocks, 0, nullptr);
NVTX_END("forall");
NVTX_INI("out^T");
output_restriction_transpose(residual_e, residual_l);
NVTX_END("out^T");
}
using NewActionKernelType = decltype(&NewActionCallback::action_callback_new<>);
MFEM_REGISTER_KERNELS(NewActionCallbackKernels, NewActionKernelType, (int));
void Apply(const int d1d, const int q1d)
{
db1();
NewActionCallbackKernels::Run(q1d,
// args
d1d,
restriction_cb,
qfunc,
inputs,
input_to_field,
input_dtq_maps,
output_dtq_maps,
dimension,
num_entities,
test_vdim,
num_test_dof,
thread_blocks,
shmem_info,
attributes,
output_fop,
elem_attributes,
fields_e,
residual_e,
output_restriction_transpose,
solutions_l,
parameters_l,
residual_l,
// fallback arguments
q1d);
}
};
template<size_t num_fields, size_t num_inputs, size_t num_outputs,
typename restriction_cb_t, typename qfunc_t, typename input_t, typename output_fop_t>
template<int T_Q1D>
typename NewActionCallback<num_fields, num_inputs, num_outputs, restriction_cb_t, qfunc_t, input_t, output_fop_t>::NewActionKernelType
NewActionCallback<num_fields, num_inputs, num_outputs, restriction_cb_t, qfunc_t, input_t, output_fop_t>::NewActionCallbackKernels::Kernel()
{
return action_callback_new<T_Q1D>;
}
template<size_t num_fields, size_t num_inputs, size_t num_outputs,
typename restriction_cb_t, typename qfunc_t, typename input_t, typename output_fop_t>
typename NewActionCallback<num_fields, num_inputs, num_outputs, restriction_cb_t, qfunc_t, input_t, output_fop_t>::NewActionKernelType
NewActionCallback<num_fields, num_inputs, num_outputs, restriction_cb_t, qfunc_t, input_t, output_fop_t>::NewActionCallbackKernels::Fallback
(int q1d)
{
dbg("\x1b[33mFallback q1d:{}", q1d);
// MFEM_ABORT("No kernel for q1d=" << q1d);
// return nullptr;
return action_callback_new<>;
}
} // namespace mfem::future
+111
View File
@@ -0,0 +1,111 @@
#pragma once
#include "../util.hpp"
#include "../../integrator_ctx.hpp"
#include <utility>
namespace mfem::future
{
namespace GlobalQFImpl
{
template<
typename qfunc_t,
typename inputs_t,
typename outputs_t,
size_t ninputs = tuple_size<inputs_t>::value,
size_t noutputs = tuple_size<outputs_t>::value>
struct Action
{
Action(
IntegratorContext ctx,
qfunc_t qfunc,
inputs_t inputs,
outputs_t outputs) :
ctx(ctx),
qfunc(std::move(qfunc)),
inputs(inputs),
outputs(outputs)
{
create_fop_to_fd(inputs, ctx.infds, input_to_infd);
create_fop_to_fd(outputs, ctx.outfds, output_to_outfd);
check_consistency(inputs, input_to_infd, ctx.infds);
check_consistency(outputs, output_to_outfd, ctx.outfds);
create_fieldbases(inputs, input_to_infd, ctx.infds, ctx.ir, input_bases);
create_fieldbases(outputs, output_to_outfd, ctx.outfds, ctx.ir, output_bases);
create_qlayouts(inputs, ctx.in_qlayouts, input_qlayouts);
create_qlayouts(outputs, ctx.out_qlayouts, output_qlayouts);
const int nqp = ctx.ir.GetNPoints();
gnqp = nqp * ctx.nentities;
xq_offsets.SetSize(ninputs + 1);
xq_offsets[0] = 0;
constexpr_for<0, ninputs>([&](auto i)
{
const auto input = get<i>(inputs);
xq_offsets[i + 1] = nqp * input.size_on_qp * ctx.nentities;
});
xq_offsets.PartialSum();
xq.Update(xq_offsets);
yq_offsets.SetSize(noutputs + 1);
yq_offsets[0] = 0;
constexpr_for<0, noutputs>([&](auto i)
{
const auto output = get<i>(outputs);
yq_offsets[i + 1] = nqp * output.size_on_qp * ctx.nentities;
});
yq_offsets.PartialSum();
yq.Update(yq_offsets);
}
void operator()(
const std::vector<Vector *> &xe,
std::vector<Vector *> &ye) const
{
if (ctx.attr.Size() == 0) { return; }
// E -> Q
interpolate(input_to_infd, input_bases, xe, xq);
// Q -> Q
static_assert(
detail::supports_tensor_array_qfunc<qfunc_t, inputs_t, outputs_t>::value,
"qfunc signature not supported by default backend Action");
detail::call_qfunc(
qfunc, xq, yq, gnqp, input_qlayouts, output_qlayouts,
std::make_index_sequence<ninputs> {},
std::make_index_sequence<noutputs> {});
// Q -> E
integrate(output_to_outfd, output_bases, yq, ye);
}
IntegratorContext ctx;
qfunc_t qfunc;
inputs_t inputs;
outputs_t outputs;
std::array<size_t, ninputs> input_to_infd;
std::array<size_t, noutputs> output_to_outfd;
std::array<FieldBasis, ninputs> input_bases;
std::array<FieldBasis, noutputs> output_bases;
std::array<std::vector<int>, ninputs> input_qlayouts;
std::array<std::vector<int>, noutputs> output_qlayouts;
int gnqp = 0;
Array<int> xq_offsets, yq_offsets;
mutable BlockVector xq, yq;
};
}
}
@@ -0,0 +1,131 @@
#pragma once
#include "../fem/quadinterpolator.hpp"
#include "../../integrator_ctx.hpp"
#include "../util.hpp"
#include <utility>
namespace mfem::future
{
namespace GlobalQFImpl
{
template<
int derivative_id,
typename qfunc_t,
typename inputs_t,
typename outputs_t,
size_t ninputs = tuple_size<inputs_t>::value,
size_t noutputs = tuple_size<outputs_t>::value>
struct DerivativeActionEnzyme
{
DerivativeActionEnzyme(
IntegratorContext ctx,
qfunc_t &qfunc,
inputs_t inputs,
outputs_t outputs) :
ctx(ctx),
qfunc(qfunc),
inputs(inputs),
outputs(outputs)
{
create_fop_to_fd(inputs, ctx.infds, input_to_infd);
create_fop_to_fd(outputs, ctx.outfds, output_to_outfd);
check_consistency(inputs, input_to_infd, ctx.infds);
check_consistency(outputs, output_to_outfd, ctx.outfds);
create_fieldbases(inputs, input_to_infd, ctx.infds, ctx.ir, input_bases);
create_fieldbases(outputs, output_to_outfd, ctx.outfds, ctx.ir, output_bases);
create_qlayouts(inputs, ctx.in_qlayouts, input_qlayouts);
create_qlayouts(outputs, ctx.out_qlayouts, output_qlayouts);
const int nqp = ctx.ir.GetNPoints();
gnqp = nqp * ctx.nentities;
xq_offsets.SetSize(ninputs + 1);
xq_offsets[0] = 0;
constexpr_for<0, ninputs>([&](auto i)
{
const auto input = get<i>(inputs);
xq_offsets[i + 1] = nqp * input.size_on_qp * ctx.nentities;
});
xq_offsets.PartialSum();
xq.Update(xq_offsets);
yq_offsets.SetSize(noutputs + 1);
yq_offsets[0] = 0;
constexpr_for<0, noutputs>([&](auto i)
{
const auto output = get<i>(outputs);
yq_offsets[i + 1] = nqp * output.size_on_qp * ctx.nentities;
});
yq_offsets.PartialSum();
yq.Update(yq_offsets);
// For each dependent input in the dependency map we create a shadow
// memory variable at the quadrature point level.
const auto activity_map = detail::make_activity_map<derivative_id>(inputs);
shadow_xq_offsets.SetSize(ninputs + 1);
shadow_xq_offsets = 0;
constexpr_for<0, ninputs>([&](auto i)
{
if (activity_map[i])
{
shadow_xq_offsets[i + 1] =
xq_offsets[i + 1] - xq_offsets[i];;
}
});
shadow_xq_offsets.PartialSum();
shadow_xq.Update(shadow_xq_offsets);
}
void operator()(
const std::vector<Vector *> &xe,
const Vector *de,
std::vector<Vector *> &ye) const
{
if (ctx.attr.Size() == 0) { return; }
// E -> Q
interpolate(input_to_infd, input_bases, xe, xq);
const auto activity_map = detail::make_activity_map<derivative_id>(inputs);
interpolate(input_to_infd, input_bases, xe, shadow_xq, activity_map);
// Q -> Q
static_assert(
detail::supports_tensor_array_qfunc<qfunc_t, inputs_t, outputs_t>::value,
"qfunc signature not supported by default backend Action");
detail::enzyme_fwddiff<derivative_id, qfunc_t, inputs_t, outputs_t>(
qfunc, xq, shadow_xq, yq, gnqp, input_qlayouts, output_qlayouts,
std::make_index_sequence<ninputs> {},
std::make_index_sequence<noutputs> {});
// Q -> E
integrate(output_to_outfd, output_bases, yq, ye);
}
IntegratorContext ctx;
qfunc_t &qfunc;
inputs_t inputs;
outputs_t outputs;
std::array<size_t, ninputs> input_to_infd;
std::array<size_t, noutputs> output_to_outfd;
std::array<FieldBasis, ninputs> input_bases;
std::array<FieldBasis, noutputs> output_bases;
std::array<std::vector<int>, ninputs> input_qlayouts;
std::array<std::vector<int>, noutputs> output_qlayouts;
int gnqp = 0;
Array<int> xq_offsets, shadow_xq_offsets, yq_offsets;
mutable BlockVector xq, shadow_xq, yq;
};
}
}
+42
View File
@@ -0,0 +1,42 @@
#pragma once
#include "action.hpp"
#include "derivative_action_enzyme.hpp"
namespace mfem::future
{
struct GlobalQFBackend
{
template<
typename qfunc_t,
typename inputs_t,
typename outputs_t>
auto static MakeAction(
const IntegratorContext &ctx,
qfunc_t qfunc,
inputs_t inputs,
outputs_t outputs)
{
return GlobalQFImpl::Action(ctx, qfunc, inputs, outputs);
}
template<
int derivative_id,
typename qfunc_t,
typename inputs_t,
typename outputs_t>
auto static MakeDerivativeAction(
const IntegratorContext &ctx,
qfunc_t qfunc,
inputs_t inputs,
outputs_t outputs)
{
return GlobalQFImpl::DerivativeActionEnzyme<
derivative_id, qfunc_t, inputs_t, outputs_t>(
ctx, qfunc, inputs, outputs);
}
};
}
+166
View File
@@ -0,0 +1,166 @@
#pragma once
#include "../util.hpp"
#include "../../integrator_ctx.hpp"
#include <utility>
namespace mfem::future
{
namespace LocalQFImpl
{
template<
typename qfunc_t,
typename inputs_t,
typename outputs_t,
size_t ninputs = tuple_size<inputs_t>::value,
size_t noutputs = tuple_size<outputs_t>::value>
struct Action
{
Action(
IntegratorContext ctx,
qfunc_t qfunc,
inputs_t inputs,
outputs_t outputs) :
ctx(ctx),
qfunc(std::move(qfunc)),
inputs(inputs),
outputs(outputs)
{
create_fop_to_fd(inputs, ctx.infds, input_to_infd);
create_fop_to_fd(outputs, ctx.outfds, output_to_outfd);
check_consistency(inputs, input_to_infd, ctx.infds);
check_consistency(outputs, output_to_outfd, ctx.outfds);
const int nqp = ctx.ir.GetNPoints();
// Initialize DofToQuad maps for inputs
for_constexpr<ninputs>([&](auto i)
{
const auto &fd = ctx.infds[input_to_infd[i]];
std::visit([&](auto* space_ptr)
{
using T = std::decay_t<decltype(*space_ptr)>;
if constexpr (std::is_same_v<T, FiniteElementSpace> ||
std::is_same_v<T, ParFiniteElementSpace>)
{
const auto *fe = space_ptr->GetTypicalFE();
input_dtq_maps[i] = &fe->GetDofToQuad(ctx.ir, DofToQuad::TENSOR);
}
}, fd.data);
});
// Initialize DofToQuad maps for outputs
for_constexpr<noutputs>([&](auto i)
{
const auto &fd = ctx.outfds[output_to_outfd[i]];
std::visit([&](auto* space_ptr)
{
using T = std::decay_t<decltype(*space_ptr)>;
if constexpr (std::is_same_v<T, FiniteElementSpace> ||
std::is_same_v<T, ParFiniteElementSpace>)
{
const auto *fe = space_ptr->GetTypicalFE();
output_dtq_maps[i] = &fe->GetDofToQuad(ctx.ir, DofToQuad::TENSOR);
}
}, fd.data);
});
}
void operator()(
const std::vector<Vector *> &xe,
std::vector<Vector *> &ye) const
{
if (ctx.attr.Size() == 0) { return; }
// input_dtq_maps
// const auto B = (const real_t*)input_dtq_maps[0/*i*/].B;
// const auto G = (const real_t*)input_dtq_maps[0/*i*/].G;
// dfem::forall<T_Q1D*T_Q1D*T_Q1D>([=] MFEM_HOST_DEVICE (int e, void *)
// {
// if (has_attr && !d_attr[d_elem_attr[e] - 1]) { return; }
// constexpr int MQ1 = T_Q1D > 0 ? T_Q1D : 8;
// MFEM_SHARED real_t sm0[MQ1][MQ1][MQ1][3];
// MFEM_SHARED real_t sm1[MQ1][MQ1][MQ1][3];
// low::regs3d_t<DIM, MQ1> reg;
// const real_t *rd = dx_ptr;
// MFEM_SHARED real_t sB[MQ1][MQ1], sG[MQ1][MQ1];
// {
// low::LoadMatrix(d1d, q1d, B, sB);
// low::LoadMatrix(d1d, q1d, G, sG);
// {
// low::LoadDofs3d(e, d1d, XE, sm0);
// low::Grad3d(d1d, q1d, sB, sG, sm0, sm1, reg);
// }
// }
// // else if constexpr (is_identity_fop<field_operator_t>::value) // Identity
// {
// // db1("Identity");
// // rd = fields_e_ptr[input_to_field[i]];
// // rd = dx_ptr;
// }
// }
// MFEM_FOREACH_THREAD_DIRECT(qz,z,q1d)
// {
// MFEM_FOREACH_THREAD_DIRECT(qy,y,q1d)
// {
// MFEM_FOREACH_THREAD_DIRECT(qx,x,q1d)
// {
// auto args = decay_tuple<qf_param_ts> {};
// get<0>(args) = as_tensor<real_t, 3>(&reg[qz][qy][qx][0]);
// if constexpr (T_Q1D > 0)
// {
// get<1>(args) = as_tensor<real_t, 3, 3>(rd + 9*(qx*T_Q1D*T_Q1D + qy*T_Q1D + qz));
// }
// else
// {
// get<1>(args) = as_tensor<real_t, 3, 3>(rd + 9*(qx*q1d*q1d + qy*q1d + qz));
// }
// auto r = get<0>(apply(qfunc, args));
// if constexpr (decltype(r)::ndim == 1)
// {
// as_tensor<real_t, 3>(&reg[qz][qy][qx][0]) = r;
// }
// else { static_assert(false); }
// }
// }
// }
// MFEM_SYNC_THREAD;
// // Integrate
// // if constexpr (is_gradient_fop<std::decay_t<output_fop_t>>::value) // Gradient
// {
// // const auto sB = reinterpret_cast<const real_t (*)[MQ1]>(Bo);
// // const auto sG = reinterpret_cast<const real_t (*)[MQ1]>(Go);
// low::GradTranspose3d(d1d, q1d, sB, sG, reg, sm1, sm0);
// low::WriteDofs3d(d1d, 0, e, reg, YE);
// }
// },
// num_entities, thread_blocks, 0, nullptr);
}
IntegratorContext ctx;
qfunc_t qfunc;
inputs_t inputs;
outputs_t outputs;
std::array<size_t, ninputs> input_to_infd;
std::array<size_t, noutputs> output_to_outfd;
std::array<const DofToQuad*, ninputs> input_dtq_maps;
std::array<const DofToQuad*, noutputs> output_dtq_maps;
};
}
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include "../../integrator_ctx.hpp"
#include "action.hpp"
namespace mfem::future
{
struct LocalQFBackend
{
template<
typename qfunc_t,
typename inputs_t,
typename outputs_t>
auto static MakeAction(
const IntegratorContext &ctx,
qfunc_t qfunc,
inputs_t inputs,
outputs_t outputs)
{
return LocalQFImpl::Action(ctx, qfunc, inputs, outputs);
}
template<
int derivative_id,
typename qfunc_t,
typename inputs_t,
typename outputs_t>
auto static MakeDerivativeAction(
const IntegratorContext &ctx,
qfunc_t qfunc,
inputs_t inputs,
outputs_t outputs)
{
MFEM_ABORT("LocalQFBackend does not support derivative actions.");
}
};
}
+659
View File
@@ -0,0 +1,659 @@
#pragma once
#include "../fem/quadinterpolator.hpp"
#include "../util.hpp"
#include "general/enzyme.hpp"
namespace mfem::future
{
template <size_t N, size_t... Is>
constexpr std::array<bool, N> all_true_impl(std::index_sequence<Is...>)
{
return {{((void)Is, true)...}};
}
template <size_t N>
constexpr std::array<bool, N> all_true()
{
return all_true_impl<N>(std::make_index_sequence<N> {});
}
struct FieldBasis
{
// E-vector -> Q-vector
std::function<void(const Vector &, Vector &)> forward;
// Q-vector -> E-vector
std::function<void(const Vector &, Vector &)> transpose;
};
inline FieldBasis FromQI(const QuadratureInterpolator *qi,
QuadratureInterpolator::EvalFlags mode)
{
return
{
[qi, mode](const Vector &xe, Vector &xq)
{
qi->SetOutputLayout(QVectorLayout::byVDIM);
if (mode == QuadratureInterpolator::VALUES)
{
qi->Values(xe, xq);
}
else
{
qi->Derivatives(xe, xq);
}
},
[qi, mode](const Vector &yq, Vector &ye)
{
Vector empty;
qi->SetOutputLayout(QVectorLayout::byVDIM);
if (mode == QuadratureInterpolator::VALUES)
{
qi->AddMultTranspose(QuadratureInterpolator::VALUES, yq, empty, ye);
}
else
{
qi->AddMultTranspose(QuadratureInterpolator::DERIVATIVES, empty, yq, ye);
}
}
};
}
// QuadratureFunction identity copy
inline FieldBasis FromQF()
{
return
{
[](const Vector &xe, Vector &xq) { xq = xe; },
[](const Vector &yq, Vector &ye) { ye = yq; }
};
}
// User-defined parameter space B
inline FieldBasis FromPS(const Operator *B, const Operator *Bt)
{
return
{
[B](const Vector &xe, Vector &xq) { B->Mult(xe, xq); },
[Bt](const Vector &yq, Vector &ye) { Bt->Mult(yq, ye); }
};
}
inline FieldBasis FieldBasisFromWeight(const IntegrationRule &ir)
{
return
{
[&ir](const Vector &, Vector &xq)
{
const int nqp = ir.GetNPoints();
MFEM_ASSERT(xq.Size() % nqp == 0, "weight block has unexpected size");
const int ne = xq.Size() / nqp;
const real_t *wref = ir.GetWeights().Read();
for (int e = 0; e < ne; e++)
{
std::memcpy(xq.GetData() + e*nqp, wref, nqp*sizeof(real_t));
}
},
[](const Vector &, Vector &) {}
};
}
inline const FieldBasis GetFieldBasis(const FieldDescriptor &f,
const IntegrationRule &ir,
QuadratureInterpolator::EvalFlags mode)
{
return std::visit([&ir, &mode](auto && arg) -> FieldBasis
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, const FiniteElementSpace *>)
{
return FromQI(arg->GetQuadratureInterpolator(ir), mode);
}
else if constexpr (std::is_same_v<T, const ParFiniteElementSpace *>)
{
return FromQI(arg->GetQuadratureInterpolator(ir), mode);
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
return FromQF();
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
return FromPS(arg->GetB(), arg->GetBt());
}
else if constexpr (std::is_same_v<T, const IntegrationRule *>)
{
return FieldBasis{};
}
else
{
static_assert(dfem::always_false<T>, "internal error");
}
}, f.data);
}
template <typename fops_t, size_t nfops>
void create_fieldbases(
fops_t &fops,
const std::array<size_t, nfops> &fop_to_fd,
const std::vector<FieldDescriptor> &fds,
const IntegrationRule &ir,
std::array<FieldBasis, nfops> &bases)
{
constexpr_for<0, nfops>([&](auto i)
{
const auto fop = get<i>(fops);
using fop_t = std::decay_t<decltype(fop)>;
const auto fd = fds[fop_to_fd[i]];
constexpr QuadratureInterpolator::EvalFlags dummy_mode =
QuadratureInterpolator::VALUES;
if constexpr (is_identity_fop<fop_t>::value)
{
bases[i] = GetFieldBasis(fd, ir, dummy_mode);
}
else if constexpr (is_weight_fop<fop_t>::value)
{
bases[i] = FieldBasisFromWeight(ir);
}
else if constexpr (is_value_fop<fop_t>::value)
{
bases[i] = GetFieldBasis(fd, ir, QuadratureInterpolator::VALUES);
}
else if constexpr (is_gradient_fop<fop_t>::value)
{
bases[i] = GetFieldBasis(fd, ir, QuadratureInterpolator::DERIVATIVES);
}
});
}
template <typename fops_t, size_t nfops>
void check_consistency(
fops_t &fops,
const std::array<size_t, nfops> &fop_to_fd,
const std::vector<FieldDescriptor> &fields)
{
constexpr_for<0, nfops>([&](auto i)
{
const auto input = get<i>(fops);
using input_t = std::decay_t<decltype(input)>;
const auto fd = fields[fop_to_fd[i]];
if constexpr (is_identity_fop<input_t>::value)
{
MFEM_ASSERT(std::holds_alternative<const QuadratureFunction *>(fd.data),
"Identity FieldOperator requested on non "
"QuadratureFunction");
}
else if constexpr (is_weight_fop<input_t>::value)
{
}
else if constexpr (is_value_fop<input_t>::value)
{
MFEM_ASSERT(std::holds_alternative<const FiniteElementSpace *>(fd.data) ||
std::holds_alternative<const ParFiniteElementSpace *>(fd.data) ||
std::holds_alternative<const ParameterSpace *>(fd.data),
"Value FieldOperator requested on non "
"QuadratureFunction");
}
else if constexpr (is_gradient_fop<input_t>::value)
{
MFEM_ASSERT(std::holds_alternative<const FiniteElementSpace *>(fd.data) ||
std::holds_alternative<const ParFiniteElementSpace *>(fd.data),
"Value FieldOperator requested on non "
"QuadratureFunction");
}
});
}
template <size_t ninputs>
void interpolate(
const std::array<size_t, ninputs> &input_to_infd,
const std::array<FieldBasis, ninputs> &input_bases,
const std::vector<Vector *> &xe,
BlockVector &xq,
const std::array<bool, ninputs> &conditional = all_true<ninputs>())
{
constexpr_for<0, ninputs>([&](auto i)
{
if (!conditional.empty() && !conditional[i]) { return; }
input_bases[i].forward(*xe[input_to_infd[i]], xq.GetBlock(i));
});
}
template <size_t noutputs>
void integrate(
const std::array<size_t, noutputs> &output_to_outfd,
const std::array<FieldBasis, noutputs> &output_bases,
const BlockVector &yq,
std::vector<Vector *> &ye)
{
for (auto v : ye) { *v = 0.0; }
constexpr_for<0, noutputs>([&](auto i)
{
output_bases[i].transpose(yq.GetBlock(i), *ye[output_to_outfd[i]]);
});
}
namespace detail
{
template <typename T>
struct is_tensor_array : std::false_type {};
template <typename scalar_t, int... Dims>
struct is_tensor_array<tensor_array<scalar_t, Dims...>> : std::true_type {};
template <typename T>
struct is_tensor_array_mut : std::false_type {};
template <typename scalar_t, int... Dims>
struct is_tensor_array_mut<tensor_array<scalar_t, Dims...>> :
std::bool_constant<!std::is_const_v<scalar_t>> {};
template <typename ndarray_t>
inline void set_layout_default(ndarray_t &a)
{
if constexpr (ndarray_t::tensor_rank() == 0) { return; }
constexpr std::size_t nd = ndarray_t::rank();
constexpr std::size_t td = ndarray_t::tensor_rank();
std::array<std::size_t, nd + td> perm{};
for (std::size_t i = 0; i < td; i++) { perm[i] = nd + i; }
for (std::size_t i = 0; i < nd; i++) { perm[td + i] = i; }
a.set_layout(perm);
}
template <typename ndarray_t>
inline void set_layout(ndarray_t& a, const std::vector<int>& layout)
{
if constexpr (ndarray_t::tensor_rank() == 0) { return; }
constexpr std::size_t nd = ndarray_t::rank();
constexpr std::size_t td = ndarray_t::tensor_rank();
constexpr std::size_t N = nd + td;
// missing means default
if (layout.empty()) { set_layout_default(a); return; }
MFEM_VERIFY(layout.size() == N,
"layout size mismatch: expected " << N << " got " << layout.size());
// TODO: make a version of set_layout that takes `std::vector<int>`
std::array<std::size_t, N> perm{};
for (std::size_t i = 0; i < N; i++)
{
MFEM_VERIFY(layout[i] >= 0, "layout index must be >=0");
perm[i] = static_cast<std::size_t>(layout[i]);
}
a.set_layout(perm);
}
/// Primary template: intentionally undefined — gives a clear error for unsupported types.
template <typename T>
struct tensor_array_traits;
/// Matches tensor<scalar_t, sizes...>
template <typename scalar_t, int... sizes>
struct tensor_array_traits<tensor<scalar_t, sizes...>>
{
using scalar_type = scalar_t;
template <std::size_t ndims>
using array_type = tensor_ndarray<scalar_t, ndims, sizes...>;
};
/// Matches tensor_ndarray<scalar_t, ndims, tensor_sizes...>
template <typename scalar_t, int ndims, int... tensor_sizes>
struct tensor_array_traits<tensor_ndarray<scalar_t, ndims, tensor_sizes...>>
{
using scalar_type = scalar_t;
template <std::size_t N>
using array_type = tensor_ndarray<scalar_t, N, tensor_sizes...>;
};
/// Entry point: explicit tensor type T as template argument.
template <typename T, typename ptr_scalar_t, typename... dyn_sizes_t>
decltype(auto) make_tensor_array(ptr_scalar_t *ptr,
const std::vector<int>* layout,
dyn_sizes_t... dynamic_sizes)
{
using traits = tensor_array_traits<T>;
using array_t = typename traits::template array_type<sizeof...(dynamic_sizes)>;
auto a = array_t(ptr, {std::size_t(dynamic_sizes)...});
if (layout) { set_layout(a, *layout); }
else { set_layout_default(a); }
return a;
}
template <typename qfunc_t, typename inputs_t, typename outputs_t>
struct supports_tensor_array_qfunc
{
using qf_signature = typename get_function_signature<qfunc_t>::type;
using qf_param_ts = typename qf_signature::parameter_ts;
static constexpr int ninputs = tuple_size<inputs_t>::value;
static constexpr int noutputs = tuple_size<outputs_t>::value;
static constexpr int nparams = tuple_size<qf_param_ts>::value;
template <std::size_t... Is>
static constexpr bool InputsOk(std::index_sequence<Is...>)
{
return (is_tensor_array<std::remove_cv_t<std::remove_reference_t<
typename tuple_element<Is, qf_param_ts>::type>>>::value && ...);
}
template <std::size_t... Is>
static constexpr bool OutputsOk(std::index_sequence<Is...>)
{
return (is_tensor_array_mut<std::remove_cv_t<std::remove_reference_t<
typename tuple_element<ninputs + Is, qf_param_ts>::type>>>::value && ...);
}
static constexpr bool value =
(nparams == ninputs + noutputs) &&
InputsOk(std::make_index_sequence<ninputs> {}) &&
OutputsOk(std::make_index_sequence<noutputs> {});
};
template <typename qfunc_t, std::size_t... Is, std::size_t... Os>
inline void call_qfunc(
const qfunc_t &qfunc,
const BlockVector &xq,
BlockVector &yq,
int gnqp,
const std::array<std::vector<int>, sizeof...(Is)>& in_layouts,
const std::array<std::vector<int>, sizeof...(Os)>& out_layouts,
std::index_sequence<Is...>,
std::index_sequence<Os...>)
{
constexpr std::size_t ninputs = sizeof...(Is);
using qf_signature = typename get_function_signature<qfunc_t>::type;
using qf_param_ts = typename qf_signature::parameter_ts;
auto inputs = std::make_tuple(
make_tensor_array<std::remove_cv_t<std::remove_reference_t<
typename tuple_element<Is, qf_param_ts>::type>>>(
xq.GetBlock(Is).Read(), &in_layouts[Is], gnqp)...);
auto outputs = std::make_tuple(
make_tensor_array<std::remove_cv_t<std::remove_reference_t<
typename tuple_element<ninputs + Os, qf_param_ts>::type>>>(
yq.GetBlock(Os).ReadWrite(), &out_layouts[Os], gnqp)...);
std::apply([&](auto&&... args)
{
qfunc(args...);
}, std::tuple_cat(inputs, outputs));
}
template <typename func_t, typename... arg_ts>
MFEM_HOST_DEVICE inline
auto qfunction_wrapper(const func_t &f, arg_ts...args)
{
return f(args...);
}
template <std::size_t derivative_id, std::size_t I, typename Tuple, std::size_t... Is>
constexpr std::array<bool, sizeof...(Is)>
make_activity_array(std::index_sequence<Is...>)
{
return { (std::decay_t<typename tuple_element<Is, Tuple>::type>::GetFieldId() == derivative_id)... };
}
template <std::size_t derivative_id, typename inputs_t, std::size_t... Is>
constexpr auto make_activity_map_impl(std::index_sequence<Is...>)
{
constexpr std::size_t N = sizeof...(Is);
if constexpr (N == 0)
return std::array<bool, 0> {};
return make_activity_array<derivative_id, 0, inputs_t>
(std::make_index_sequence<N> {});
}
template <std::size_t derivative_id, typename inputs_t>
constexpr auto make_activity_map(inputs_t)
{
return make_activity_map_impl<derivative_id, inputs_t>(
std::make_index_sequence<tuple_size<inputs_t>::value> {});
}
namespace enzyme_detail
{
template <auto wrapper_fn, typename qf_return_t, typename... AccArgs>
__attribute__((always_inline)) inline void
do_enzyme_call(AccArgs... acc)
{
__enzyme_fwddiff<qf_return_t>(wrapper_fn, acc...);
}
template <auto wrapper_fn, typename qf_return_t,
size_t CurO, size_t NO,
typename primals_t, typename derivs_t,
typename... AccArgs>
__attribute__((always_inline)) inline void
process_outputs(primals_t &primals, derivs_t &derivs, AccArgs... acc)
{
if constexpr (CurO == NO)
{
do_enzyme_call<wrapper_fn, qf_return_t>(acc...);
}
else
{
process_outputs<wrapper_fn, qf_return_t, CurO + 1, NO>(
primals, derivs,
acc...,
enzyme_dupnoneed,
&std::get<CurO>(primals),
&std::get<CurO>(derivs));
}
}
template <auto wrapper_fn, typename qf_return_t,
size_t CurI, size_t NI, bool... ActivityMap,
typename inputs_t, typename shadows_t,
typename primals_t, typename derivs_t,
typename... AccArgs>
__attribute__((always_inline)) inline void
process_inputs(inputs_t &inputs, shadows_t &shadows,
primals_t &primals, derivs_t &derivs,
AccArgs... acc)
{
if constexpr (CurI == NI)
{
constexpr size_t NO = std::tuple_size_v<primals_t>;
process_outputs<wrapper_fn, qf_return_t, 0, NO>(
primals, derivs, acc...);
}
else
{
constexpr bool active =
std::array<bool, sizeof...(ActivityMap)> {ActivityMap...} [CurI];
if constexpr (active)
{
std::cout << "Input[" << CurI << "]: ACTIVE (enzyme_dup)\n"
<< " primal ptr type: "
<< get_type_name<decltype(&std::get<CurI>(inputs))>() << "\n"
<< " shadow ptr type: "
<< get_type_name<decltype(&std::get<CurI>(shadows))>() << "\n";
}
else
{
std::cout << "Input[" << CurI << "]: INACTIVE (enzyme_const)\n"
<< " primal ptr type: "
<< get_type_name<decltype(&std::get<CurI>(inputs))>() << "\n";
}
if constexpr (active)
{
process_inputs<wrapper_fn, qf_return_t, CurI + 1, NI, ActivityMap...>(
inputs, shadows, primals, derivs,
acc...,
enzyme_dup,
&std::get<CurI>(inputs),
&std::get<CurI>(shadows));
}
else
{
process_inputs<wrapper_fn, qf_return_t, CurI + 1, NI, ActivityMap...>(
inputs, shadows, primals, derivs,
acc...,
enzyme_const,
&std::get<CurI>(inputs));
}
}
}
} // namespace enzyme_detail
template <size_t derivative_id, typename qfunc_t, typename inputs_t, typename outputs_t,
std::size_t... Is, std::size_t... Os>
inline void enzyme_fwddiff(
qfunc_t &qfunc,
const BlockVector &xq,
const BlockVector &shadow_xq,
BlockVector &yq,
const int &gnqp,
const std::array<std::vector<int>, sizeof...(Is)>& in_layouts,
const std::array<std::vector<int>, sizeof...(Os)>& out_layouts,
std::index_sequence<Is...>,
std::index_sequence<Os...>)
{
#ifdef MFEM_USE_ENZYME
constexpr std::size_t ninputs = sizeof...(Is);
constexpr std::size_t noutputs = sizeof...(Os);
using qf_signature = typename get_function_signature<qfunc_t>::type;
using qf_param_ts = typename qf_signature::parameter_ts;
using qf_return_t = typename qf_signature::return_t;
constexpr auto activity_map = make_activity_map<derivative_id>(inputs_t{});
static_assert(activity_map.size() == ninputs, "activity map size mismatch");
std::cout << "activity_map: ";
for (const auto &v : activity_map)
{
std::cout << v << " ";
}
std::cout << "\n";
auto inputs = std::make_tuple(
make_tensor_array<std::remove_cv_t<std::remove_reference_t<
typename tuple_element<Is, qf_param_ts>::type>>>(
xq.GetBlock(Is).Read(), &in_layouts[Is], gnqp)...);
auto shadows = std::make_tuple(
make_tensor_array<std::remove_cv_t<std::remove_reference_t<
typename tuple_element<Is, qf_param_ts>::type>>>(
shadow_xq.GetBlock(Is).Read(), &in_layouts[Is], gnqp)...);
std::array<Vector, noutputs> primal_storage;
((primal_storage[Os].SetSize(yq.GetBlock(Os).Size())), ...);
auto primals_out = std::make_tuple(
make_tensor_array<std::remove_cv_t<std::remove_reference_t<
typename tuple_element<ninputs + Os, qf_param_ts>::type>>>(
primal_storage[Os].ReadWrite(), &out_layouts[Os], gnqp)...);
auto derivs_out = std::make_tuple(
make_tensor_array<std::remove_cv_t<std::remove_reference_t<
typename tuple_element<ninputs + Os, qf_param_ts>::type>>>(
yq.GetBlock(Os).ReadWrite(), &out_layouts[Os], gnqp)...);
using wrapper_fn_t = qf_return_t (*)(
const qfunc_t &,
std::remove_reference_t<decltype(std::get<Is>(inputs))>...,
std::remove_reference_t<decltype(std::get<Os>(primals_out))>...);
constexpr wrapper_fn_t wrapper_fn =
qfunction_wrapper<qfunc_t,
std::remove_reference_t<decltype(std::get<Is>(inputs))>...,
std::remove_reference_t<decltype(std::get<Os>(primals_out))>...>;
// wrapper_fn travels as a non-type template parameter throughout without
// being stored.
enzyme_detail::process_inputs<
wrapper_fn,
qf_return_t,
0,
ninputs,
activity_map[Is]...
>(inputs, shadows,
primals_out, derivs_out,
enzyme_const, &qfunc // seed: qfunc is always inactive
);
#else
MFEM_ABORT("enzyme_fwddiff requires MFEM_USE_ENZYME");
#endif
}
} // namespace detail
// Create quadrature function fop to fields map
template <typename fops_t, size_t N = tuple_size<fops_t>::value, size_t M>
void create_fop_to_fd(const fops_t &fops,
const std::vector<FieldDescriptor> &fields,
std::array<size_t, M> &fop_to_fd)
{
static_assert(N == M, "sizes must match");
constexpr_for<0, N>([&](auto i)
{
const auto fop = get<i>(fops);
fop_to_fd[i] = std::numeric_limits<size_t>::max();
for (size_t j = 0; j < fields.size(); j++)
{
// TODO: output.GetFieldId() should probably store/return size_t
if (static_cast<int>(fields[j].id) == fop.GetFieldId())
{
fop_to_fd[i] = j;
}
}
// Handle Weight type. There is no FieldDescriptor for the weight.
// TODO: Create weight descriptor for the weight for internal use?
// TODO: this is a hack...
if (is_weight_fop<std::remove_cv_t<decltype(fop)>>::value)
{
fop_to_fd[i] = 0;
}
else if (fop_to_fd[i] == std::numeric_limits<size_t>::max())
{
MFEM_ABORT("not found");
}
});
}
template <typename fops_t, size_t nfops>
void create_qlayouts(const fops_t &fops,
const std::unordered_map<std::type_index, std::vector<int>> &a,
std::array<std::vector<int>, nfops> &b)
{
constexpr_for<0, nfops>([&](auto i)
{
using fop_t =
std::remove_cv_t<std::remove_reference_t<decltype(get<i>(fops))>>;
auto it = a.find(std::type_index(typeid(fop_t)));
if (it != a.end()) { b[i] = it->second; }
else { b[i].clear(); }
});
}
}
+96 -21
View File
@@ -11,44 +11,119 @@
#include "doperator.hpp"
#include <algorithm>
#ifdef MFEM_USE_MPI
using namespace mfem;
using namespace mfem::future;
void DifferentiableOperator::SetParameters(std::vector<Vector *> p) const
DifferentiableOperator::DifferentiableOperator(
const std::vector<FieldDescriptor> &infds,
const std::vector<FieldDescriptor> &outfds,
const ParMesh &mesh) :
Operator(),
mesh(mesh),
infds(infds),
outfds(outfds)
{
MFEM_ASSERT(parameters.size() == p.size(),
"number of parameters doesn't match descriptors");
for (size_t i = 0; i < parameters.size(); i++)
unionfds.clear();
unionfds.insert(unionfds.end(), infds.begin(), infds.end());
unionfds.insert(unionfds.end(), outfds.begin(), outfds.end());
std::sort(unionfds.begin(), unionfds.end());
auto last = std::unique(unionfds.begin(), unionfds.end());
unionfds.erase(last, unionfds.end());
infields_l.resize(infds.size());
for (size_t i = 0; i < infds.size(); i++)
{
p[i]->Read();
parameters_l[i] = *p[i];
infields_l[i] = new Vector(GetVSize(infds[i]));
}
infields_e.resize(infds.size());
}
DifferentiableOperator::DifferentiableOperator(
const std::vector<FieldDescriptor> &solutions,
const std::vector<FieldDescriptor> &parameters,
const ParMesh &mesh) :
mesh(mesh),
solutions(solutions),
parameters(parameters)
void DifferentiableOperator::SetMultLevel(MultLevel level)
{
fields.resize(solutions.size() + parameters.size());
fields_e.resize(fields.size());
solutions_l.resize(solutions.size());
parameters_l.resize(parameters.size());
mult_level = level;
}
for (size_t i = 0; i < solutions.size(); i++)
void DifferentiableOperator::Mult(const Vector &x, Vector &y) const
{
MFEM_ASSERT(!action_callbacks.empty(),
"no integrators have been set");
MFEM_ASSERT(dynamic_cast<const BlockVector*>(&x),
"x needs to be a BlockVector");
MFEM_ASSERT(dynamic_cast<const BlockVector*>(&y),
"y needs to be a BlockVector");
const auto &bx = static_cast<const BlockVector &>(x);
auto &by = static_cast<BlockVector &>(y);
Mult(bx, by);
}
void DifferentiableOperator::DisableTensorProductStructure(bool disable)
{
use_tensor_product_structure = !disable;
}
std::shared_ptr<DerivativeOperator> DifferentiableOperator::GetDerivative(
size_t derivative_id, const Vector &x)
{
MFEM_ASSERT(derivative_action_callbacks.find(derivative_id) !=
derivative_action_callbacks.end(),
"no derivative action has been found for ID " << derivative_id);
const size_t dfidx = FindIdx(derivative_id, infds);
// Get transpose callbacks if available, otherwise pass empty vector
std::vector<derivative_action_t> transpose_callbacks;
auto it = daction_transpose_callbacks.find(derivative_id);
if (it != daction_transpose_callbacks.end())
{
fields[i] = solutions[i];
transpose_callbacks = it->second;
}
for (size_t i = 0; i < parameters.size(); i++)
return std::make_shared<DerivativeOperator>(
height,
GetTrueVSize(infds[dfidx]),
derivative_action_callbacks[derivative_id],
transpose_callbacks,
infds[dfidx],
x,
infds,
outfds);
}
std::shared_ptr<DerivativeOperator> DifferentiableOperator::GetDerivative(
size_t derivative_id, const MultiVector &x)
{
MFEM_ASSERT(derivative_action_callbacks.find(derivative_id) !=
derivative_action_callbacks.end(),
"no derivative action has been found for ID " << derivative_id);
const size_t dfidx = FindIdx(derivative_id, infds);
// Get transpose callbacks if available, otherwise pass empty vector
std::vector<derivative_action_t> transpose_callbacks;
auto it = daction_transpose_callbacks.find(derivative_id);
if (it != daction_transpose_callbacks.end())
{
fields[i + solutions.size()] = parameters[i];
transpose_callbacks = it->second;
}
return std::make_shared<DerivativeOperator>(
height,
GetTrueVSize(infds[dfidx]),
derivative_action_callbacks[derivative_id],
transpose_callbacks,
infds[dfidx],
x,
infds,
outfds);
}
#endif // MFEM_USE_MPI
+236 -896
View File
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
// 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.
#pragma once
#include "../fespace.hpp"
#include "parameterspace.hpp"
namespace mfem::future
{
/// @brief FieldDescriptor struct
///
/// This struct is used to store information about a field.
struct FieldDescriptor
{
using data_variant_t =
std::variant<const FiniteElementSpace *,
const ParFiniteElementSpace *,
const QuadratureFunction *,
const ParameterSpace *>;
/// Field ID
std::size_t id;
/// Field variant
data_variant_t data;
/// Default constructor
FieldDescriptor() :
id(SIZE_MAX), data(data_variant_t{}) {}
/// Constructor
template <typename T>
FieldDescriptor(std::size_t field_id, const T* v) :
id(field_id), data(v) {}
bool operator==(const FieldDescriptor& other) const
{
return id == other.id;
}
bool operator<(const FieldDescriptor& other) const
{
return id < other.id;
}
friend void swap(FieldDescriptor& a, FieldDescriptor& b)
{
using std::swap;
swap(a.id, b.id);
swap(a.data, b.data);
}
};
}
+11 -8
View File
@@ -90,8 +90,8 @@ void map_quadrature_data_to_fields_impl(
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor");
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
" for this field descriptor");
}
}
@@ -169,8 +169,9 @@ void map_quadrature_data_to_fields_tensor_impl_1d(
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
"for this field descriptor with sum factorization on"
" tensor product elements");
}
}
@@ -306,8 +307,9 @@ void map_quadrature_data_to_fields_tensor_impl_2d(
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
" for this field descriptor with sum factorization on"
" tensor product elements");
}
}
@@ -492,8 +494,9 @@ void map_quadrature_data_to_fields_tensor_impl_3d(
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented"
" for this field descriptor with sum factorization on"
" tensor product elements");
}
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "util.hpp"
namespace mfem::future
{
struct IntegratorContext
{
const ParMesh &mesh;
const Array<int> *elem_attr;
Array<int> attr;
int nentities;
const std::vector<FieldDescriptor> &infds;
const std::vector<FieldDescriptor> &outfds;
const std::vector<FieldDescriptor> &unionfds;
const IntegrationRule &ir;
std::unordered_map<std::type_index, std::vector<int>> &in_qlayouts;
std::unordered_map<std::type_index, std::vector<int>> &out_qlayouts;
};
}
+94 -9
View File
@@ -9,8 +9,23 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#pragma once
// #define NVTX_COLOR nvtx::kPeru
#include "util.hpp"
#include "fem/kernels.hpp"
///////////////////////////////////////////////////////////////////////////////
template <class T>
inline std::enable_if_t<!std::numeric_limits<T>::is_integer, bool>
AlmostEq(T x, T y, T tolerance = 15.0 * std::numeric_limits<T>::epsilon())
{
const T neg = std::abs(x - y);
constexpr T min = std::numeric_limits<T>::min();
constexpr T eps = std::numeric_limits<T>::epsilon();
const T min_abs = std::min(std::abs(x), std::abs(y));
if (std::abs(min_abs) == 0.0) { return neg < eps; }
return (neg / (1.0 + std::max(min, min_abs))) < tolerance;
}
namespace mfem::future
{
@@ -30,6 +45,7 @@ void map_field_to_quadrature_data_tensor_product_3d(
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
{
dbg("Value");
auto [q1d, unused, d1d] = B.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
@@ -94,10 +110,11 @@ void map_field_to_quadrature_data_tensor_product_3d(
else if constexpr (
is_gradient_fop<std::decay_t<field_operator_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
// dbg("Gradient");
const auto [q1d, B_dim, d1d] = B.GetShape();
const int vdim = input.vdim;
const int dim = input.dim;
const auto field = Reshape(&field_e[0], d1d, d1d, d1d, vdim);
const auto field = Reshape(&std::as_const(field_e[0]), d1d, d1d, d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d, q1d, q1d);
auto s0 = Reshape(&scratch_mem[0](0), d1d, d1d, q1d);
@@ -106,7 +123,30 @@ void map_field_to_quadrature_data_tensor_product_3d(
auto s3 = Reshape(&scratch_mem[3](0), d1d, q1d, q1d);
auto s4 = Reshape(&scratch_mem[4](0), d1d, q1d, q1d);
for (int vd = 0; vd < vdim; vd++)
// constexpr int MQ1 = T_Q1D > 0 ? T_Q1D : 8;
// static constexpr int DIM = 3;
// MFEM_VERIFY(q1d <= MQ1, "q1d > MQ1");
// MFEM_SHARED real_t smem[MQ1][MQ1];
// kernels::internal::d_regs3d_t<DIM, MQ1> r0, r1;
// real_t sB[MQ1][MQ1], sG[MQ1][MQ1];
/*
{
assert(B_dim == 1 && "1D B required!");
kernels::internal::LoadMatrix(d1d, q1d, B, sB);
kernels::internal::LoadMatrix(d1d, q1d, G, sG);
for (int qx = 0; qx < q1d; qx++)
{
for (int dx = 0; dx < d1d; dx++)
{
assert(AlmostEq(B(qx, 0, dx), sB[dx][qx]));
assert(AlmostEq(G(qx, 0, dx), sG[dx][qx]));
}
}
}*/
for (int c = 0; c < vdim; c++)
{
MFEM_FOREACH_THREAD(dz, z, d1d)
{
@@ -117,7 +157,7 @@ void map_field_to_quadrature_data_tensor_product_3d(
real_t uv[2] = {0.0, 0.0};
for (int dx = 0; dx < d1d; dx++)
{
const real_t f = field(dx, dy, dz, vd);
const real_t f = field(dx, dy, dz, c);
uv[0] += f * B(qx, 0, dx);
uv[1] += f * G(qx, 0, dx);
}
@@ -163,19 +203,59 @@ void map_field_to_quadrature_data_tensor_product_3d(
uvw[1] += s3(dz, qy, qx) * B(qz, 0, dz);
uvw[2] += s4(dz, qy, qx) * G(qz, 0, dz);
}
fqp(vd, 0, qx, qy, qz) = uvw[0];
fqp(vd, 1, qx, qy, qz) = uvw[1];
fqp(vd, 2, qx, qy, qz) = uvw[2];
fqp(c, 0, qx, qy, qz) = uvw[0];
fqp(c, 1, qx, qy, qz) = uvw[1];
fqp(c, 2, qx, qy, qz) = uvw[2];
}
}
}
MFEM_SYNC_THREAD;
}
/*
{
for (int c = 0; c < vdim; c++)
{
kernels::internal::LoadDofs3d(d1d, c, field, r0);
for (int d = 0; d < DIM; d++)
{
for (int dz = 0; dz < d1d; dz++)
{
for (int dy = 0; dy < d1d; dy++)
{
for (int dx = 0; dx < d1d; dx++)
{
const real_t f = field(dx, dy, dz, c);
assert(AlmostEq(f, r0[d][dz][dy][dx]));
}
}
}
}
kernels::internal::Grad3d(d1d, q1d, smem, sB, sG, r0, r1, c);
for (int qz = 0; qz < q1d; qz++)
{
for (int qy = 0; qy < q1d; qy++)
{
for (int qx = 0; qx < q1d; qx++)
{
if (!AlmostEq(fqp(c, d, qx, qy, qz), r1[d][qz][qy][qx]))
{
dbg("\x1b[31m[{}:d] {} {}", c, fqp(c, d, qx, qy, qz), r1[d][qz][qy][qx]);
dbg("❌❌❌"), std::exit(EXIT_FAILURE);
}
}
}
}
}
// dbg("✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅");//, std::exit(EXIT_SUCCESS);
}*/
}
// TODO: Create separate function for clarity
else if constexpr (
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
{
// dbg("None");
const int num_qp = integration_weights.GetShape()[0];
// TODO: eeek
const int q1d = (int)floor(std::pow(num_qp, 1.0/input.dim) + 0.5);
@@ -518,6 +598,9 @@ void map_fields_to_quadrature_data(
const int &dimension,
const bool &use_sum_factorization = false)
{
// dbg();
assert(use_sum_factorization && "❌ use_sum_factorization required");
// When the input_to_field map returns -1, this means the requested input
// is the integration weight. Weights don't have a user defined field
// attached to them and we create a dummy field which is not accessed
@@ -578,6 +661,7 @@ void map_field_to_quadrature_data_conditional(
const int &dimension,
const bool &use_sum_factorization = false)
{
assert(false && "❌ condition not implemented");
if (condition)
{
if (use_sum_factorization)
@@ -619,6 +703,7 @@ void map_fields_to_quadrature_data_conditional(
const std::array<bool, num_inputs> &conditions,
const bool &use_sum_factorization = false)
{
assert(false && "❌ condition not implemented");
for_constexpr<num_inputs>([&](auto i)
{
map_field_to_quadrature_data_conditional(
@@ -627,7 +712,7 @@ void map_fields_to_quadrature_data_conditional(
});
}
template <size_t num_inputs, typename field_operator_ts>
template <int T_Q1D, size_t num_inputs, typename field_operator_ts>
MFEM_HOST_DEVICE
void map_direction_to_quadrature_data_conditional(
std::array<DeviceTensor<2>, num_inputs> &directions_qp,
@@ -660,7 +745,7 @@ void map_direction_to_quadrature_data_conditional(
}
else if (dimension == 3)
{
map_field_to_quadrature_data_tensor_product_3d(
map_field_to_quadrature_data_tensor_product_3d<T_Q1D>(
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
integration_weights, scratch_mem);
}
+18 -3
View File
@@ -43,7 +43,7 @@ public:
/// Get spatial dimension
///
/// returns always 1.
int Dimension() const
constexpr int Dimension() const
{
return 1;
}
@@ -74,11 +74,14 @@ public:
return elem_restr.get();
}
virtual const Operator* GetB() const = 0;
virtual const Operator* GetBt() const = 0;
protected:
int vdim;
DofToQuad dtq;
mutable std::unique_ptr<Operator> prolongation;
mutable std::unique_ptr<Operator> elem_restr;
mutable std::unique_ptr<Operator> prolongation, elem_restr, B, Bt;
};
/// @brief Uniform parameter space
@@ -122,6 +125,18 @@ public:
return lsize;
}
const Operator* GetB() const override
{
MFEM_ABORT("UniformParameterSpace does not support GetB");
return nullptr;
}
const Operator* GetBt() const override
{
MFEM_ABORT("UniformParameterSpace does not support GetBt");
return nullptr;
}
private:
/// T-vector size
int tsize;
+2
View File
@@ -243,6 +243,8 @@ void process_qf_arg(
}
}
// const tensor<real_t, DIM> ∇u
// const tensor<real_t, DIM, DIM> D (PA_DATA)
template <typename arg_type>
MFEM_HOST_DEVICE inline
void process_qf_arg(const DeviceTensor<2> &u, arg_type &arg, int qp)
+76
View File
@@ -0,0 +1,76 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#pragma once
#include "tuple.hpp"
#include "../linalg/tensor.hpp"
using namespace mfem::future;
using mfem::future::tensor;
// Helper to add dimension to tensor type
template<typename T, int qp>
struct AddQPDimension;
// Specialization for tensor<real_t, dim>
template<typename real_t, int dim, int qp>
struct AddQPDimension<tensor<real_t, dim>, qp>
{
using type = tensor<real_t, dim, qp>;
};
// Specialization for tensor<real_t, dim, dim>
template<typename real_t, int dim, int qp>
struct AddQPDimension<tensor<real_t, dim, dim>, qp>
{
using type = tensor<real_t, dim, dim, qp>;
};
// Specialization for real_t (transforms to tensor<real_t, qp>)
template<typename real_t, int qp>
struct AddQPDimension
{
using type = tensor<real_t, qp>;
};
// Helper to transform tuple
template<typename Tuple, int qp>
struct TransformTupleQP {};
// Specialization for mfem::future::tuple
template<int qp, typename... Types>
struct TransformTupleQP<mfem::future::tuple<Types...>, qp>
{
using type = mfem::future::tuple<typename AddQPDimension<Types, qp>::type...>;
};
template<int qp, typename... Types>
struct TransformTupleQP<std::tuple<Types...>, qp>
{
using type = std::tuple<typename AddQPDimension<Types, qp>::type...>;
};
// Function to transform tuple type with qp dimension
template<int qp, typename qf_param_ts>
struct add_qp_dimension
{
using type = typename TransformTupleQP<qf_param_ts, qp>::type;
};
// Helper alias template for cleaner usage
template<int qp, typename qf_param_ts>
using add_qp_dimension_t = typename add_qp_dimension<qp, qf_param_ts>::type;
// ...AddDomainIntegrator...
// {
// constexpr int Q1D = 4;
// using qf_param_augmentd_ts = add_qp_dimension_t<Q1D, decay_tuple<qf_param_ts>>;
// }
+533 -50
View File
@@ -21,6 +21,7 @@
#include <type_traits>
#include <numeric>
#include <iomanip>
#include <typeindex>
#include "../../general/communication.hpp"
#include "../../general/forall.hpp"
@@ -28,13 +29,19 @@
#include "../fe/fe_base.hpp"
#include "../fespace.hpp"
#include "../pfespace.hpp"
#include "../qfunction.hpp"
#include "../../mesh/mesh.hpp"
#include "../../linalg/dtensor.hpp"
#include "../quadinterpolator.hpp"
#include "fielddescriptor.hpp"
#include "fieldoperator.hpp"
#include "parameterspace.hpp"
#include "tuple.hpp"
#undef NVTX_COLOR
#define NVTX_COLOR ::nvtx::kLightBlue
namespace mfem::future
{
@@ -75,7 +82,7 @@ constexpr void for_constexpr(lambda&& f,
}
template <typename lambda>
constexpr void for_constexpr(lambda&& f, std::integer_sequence<std::size_t>) {}
constexpr void for_constexpr(lambda&&, std::integer_sequence<std::size_t>) {}
template <int... n, typename lambda>
constexpr void for_constexpr(lambda&& f)
@@ -84,7 +91,7 @@ constexpr void for_constexpr(lambda&& f)
}
template <typename lambda, typename arg_t>
constexpr void for_constexpr_with_arg(lambda&& f, arg_t&& arg,
constexpr void for_constexpr_with_arg(lambda&&, arg_t&&,
std::integer_sequence<std::size_t>)
{
// Base case - do nothing for empty sequence
@@ -108,6 +115,16 @@ constexpr void for_constexpr_with_arg(lambda&& f, arg_t&& arg)
indices{});
}
template <auto start, auto end, auto inc = 1, typename F>
constexpr void constexpr_for(F&& f)
{
if constexpr (start < end)
{
f(std::integral_constant<decltype(start), start>());
constexpr_for<start + inc, end, inc>(f);
}
}
template <std::size_t I, typename Tuple, std::size_t... Is>
std::array<bool, sizeof...(Is)>
make_dependency_array(const Tuple& inputs, std::index_sequence<Is...>)
@@ -444,6 +461,21 @@ struct create_function_signature<output_t (*)(input_ts...)>
using type = FunctionSignature<output_t(input_ts...)>;
};
template <typename...>
using void_t = void;
template <typename T, typename = void>
struct get_function_signature
{
using type = typename create_function_signature<T>::type;
};
template <typename T>
struct get_function_signature<T, void_t<decltype(&T::operator())>>
{
using type = typename create_function_signature<decltype(&T::operator())>::type;
};
template <typename T>
constexpr int GetFieldId()
{
@@ -538,38 +570,12 @@ auto get_marked_entries(
/// @param t the tuple to filter fields from.
/// @returns a tuple containing only the fields with field IDs not equal to -1.
template <typename... Ts>
constexpr auto filter_fields(const std::tuple<Ts...>& t)
constexpr auto filter_fields(const std::tuple<Ts...>&)
{
return std::tuple_cat(
std::conditional_t<Ts::GetFieldId() != -1, std::tuple<Ts>, std::tuple<>> {}...);
}
/// @brief FieldDescriptor struct
///
/// This struct is used to store information about a field.
struct FieldDescriptor
{
using data_variant_t =
std::variant<const FiniteElementSpace *,
const ParFiniteElementSpace *,
const ParameterSpace *>;
/// Field ID
std::size_t id;
/// Field variant
data_variant_t data;
/// Default constructor
FieldDescriptor() :
id(SIZE_MAX), data(data_variant_t{}) {}
/// Constructor
template <typename T>
FieldDescriptor(std::size_t field_id, const T* v) :
id(field_id), data(v) {}
};
namespace dfem
{
template <class... T> constexpr bool always_false = false;
@@ -599,7 +605,7 @@ struct ThreadBlocks
#if defined(MFEM_USE_CUDA_OR_HIP)
template <typename func_t>
__global__ void forall_kernel_shmem(func_t f, int n)
__global__ void forall_kernel_extern_shmem(func_t f, int n)
{
int i = blockIdx.x;
extern __shared__ real_t shmem[];
@@ -608,23 +614,48 @@ __global__ void forall_kernel_shmem(func_t f, int n)
f(i, shmem);
}
}
template <typename func_t>
__global__ void forall_kernel_static_smem(func_t f, int n)
{
int i = blockIdx.x;
if (i >= n) { return; }
f(i, nullptr);
}
template <int MAX_THREADS_PER_BLOCK, typename func_t>
__global__
MFEM_LAUNCH_BOUNDS(MAX_THREADS_PER_BLOCK)
static void forall_kernel_static_smem_launch_bounds(func_t f, int n)
{
for (int k = blockIdx.x; k < n; k += gridDim.x) { f(k, nullptr); }
}
#endif
template <typename func_t>
template </*typename kernel_tag,*/ typename func_t>
void forall(func_t f,
const int &N,
const ThreadBlocks &blocks,
int num_shmem = 0,
[[maybe_unused]] const ThreadBlocks &blocks,
[[maybe_unused]] int num_shmem = 0,
real_t *shmem = nullptr)
{
db1();
if (Device::Allows(Backend::CUDA_MASK) ||
Device::Allows(Backend::HIP_MASK))
{
#if defined(MFEM_USE_CUDA_OR_HIP)
// int gridsize = (N + Z - 1) / Z;
int num_bytes = num_shmem * sizeof(decltype(shmem));
db1("num_bytes:{}", num_bytes);
db1("block: {}x{}x{}", blocks.x, blocks.y, blocks.z);
dim3 block_size(blocks.x, blocks.y, blocks.z);
forall_kernel_shmem<<<N, block_size, num_bytes>>>(f, N);
// ForallKernel<kernel_tag>::run<<<N, block_size, num_bytes>>>(f, N);
if (num_bytes > 0)
{
forall_kernel_extern_shmem<<<N, block_size, num_bytes>>>(f, N);
}
else
{
forall_kernel_static_smem<<<N, block_size>>>(f, N);
}
#if defined(MFEM_USE_CUDA)
MFEM_GPU_CHECK(cudaGetLastError());
#elif defined(MFEM_USE_HIP)
@@ -635,6 +666,7 @@ void forall(func_t f,
}
else if (Device::Allows(Backend::CPU_MASK))
{
db1("CPU_MASK");
MFEM_ASSERT(!((bool)num_shmem != (bool)shmem),
"Backend::CPU needs a pre-allocated shared memory block");
for (int i = 0; i < N; i++)
@@ -648,6 +680,69 @@ void forall(func_t f,
}
}
namespace dfem
{
template <int MAX_THREADS_PER_BLOCK = 0, typename func_t>
void forall(func_t f,
const int &N,
[[maybe_unused]] const ThreadBlocks &blocks,
[[maybe_unused]] int num_shmem = 0,
real_t *shmem = nullptr)
{
db1();
if (Device::Allows(Backend::CUDA_MASK) ||
Device::Allows(Backend::HIP_MASK))
{
#if defined(MFEM_USE_CUDA_OR_HIP)
int num_bytes = num_shmem * sizeof(decltype(shmem));
db1("num_bytes:{}", num_bytes);
db1("block: {}x{}x{}", blocks.x, blocks.y, blocks.z);
db1("MAX_THREADS_PER_BLOCK:{}", MAX_THREADS_PER_BLOCK);
dim3 block_size(blocks.x, blocks.y, blocks.z);
if constexpr (MAX_THREADS_PER_BLOCK > 0)
{
assert(num_bytes == 0);
forall_kernel_static_smem_launch_bounds
<MAX_THREADS_PER_BLOCK><<<N, block_size>>> (f, N);
}
else
{
static_assert(MAX_THREADS_PER_BLOCK == 0);
if (num_bytes == 0)
{
forall_kernel_static_smem<<<N, block_size>>>(f, N);
}
else
{
forall_kernel_extern_shmem<<<N, block_size, num_bytes>>>(f, N);
}
}
#if defined(MFEM_USE_CUDA)
MFEM_GPU_CHECK(cudaGetLastError());
#elif defined(MFEM_USE_HIP)
MFEM_GPU_CHECK(hipGetLastError());
#endif
// MFEM_DEVICE_SYNC; // ⚠️
#endif
}
else if (Device::Allows(Backend::CPU_MASK))
{
db1("CPU_MASK");
MFEM_ASSERT(!((bool)num_shmem != (bool)shmem),
"Backend::CPU needs a pre-allocated shared memory block");
for (int i = 0; i < N; i++)
{
f(i, shmem);
}
}
else
{
MFEM_ABORT("no compute backend available");
}
}
}
/// @todo To be removed.
class FDJacobian : public Operator
{
@@ -772,6 +867,10 @@ int GetVSize(const FieldDescriptor &f)
{
return arg->GetVSize();
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
return arg->Size();
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
return arg->GetVSize();
@@ -810,6 +909,10 @@ void GetElementVDofs(const FieldDescriptor &f, int el, Array<int> &vdofs)
{
arg->GetElementVDofs(el, vdofs);
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
MFEM_ABORT("internal error");
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
MFEM_ABORT("internal error");
@@ -844,6 +947,10 @@ int GetTrueVSize(const FieldDescriptor &f)
{
return arg->GetTrueVSize();
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
return arg->Size();
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
return arg->GetTrueVSize();
@@ -874,6 +981,10 @@ int GetVDim(const FieldDescriptor &f)
{
return arg->GetVDim();
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
return arg->GetVDim();
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
return arg->GetVDim();
@@ -909,6 +1020,10 @@ int GetDimension(const FieldDescriptor &f)
return arg->GetMesh()->Dimension() - 1;
}
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
return arg->GetSpace()->GetMesh()->Dimension();
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
return arg->Dimension();
@@ -921,6 +1036,36 @@ int GetDimension(const FieldDescriptor &f)
}, f.data);
}
inline
std::variant<const QuadratureInterpolator *, const Operator *>get_qinterp(
const FieldDescriptor &f,
const IntegrationRule &ir)
{
return std::visit([&ir](auto && arg) -> const QuadratureInterpolator*
{
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, const FiniteElementSpace *> ||
std::is_same_v<T, const ParFiniteElementSpace *>)
{
return arg->GetQuadratureInterpolator(ir);
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
// QuadratureFunction doesn't need a QuadratureInterpolator
return nullptr;
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
return nullptr;
}
else
{
static_assert(dfem::always_false<T>, "internal error");
}
return nullptr; // Unreachable, but avoids compiler warning
}, f.data);
}
/// @brief Get the prolongation operator for a field descriptor.
///
@@ -929,6 +1074,7 @@ int GetDimension(const FieldDescriptor &f)
inline
const Operator *get_prolongation(const FieldDescriptor &f)
{
NVTX("get P");
return std::visit([](auto&& arg) -> const Operator*
{
using T = std::decay_t<decltype(arg)>;
@@ -937,6 +1083,10 @@ const Operator *get_prolongation(const FieldDescriptor &f)
{
return arg->GetProlongationMatrix();
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
return nullptr;
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
return arg->GetProlongationMatrix();
@@ -959,6 +1109,7 @@ inline
const Operator *get_element_restriction(const FieldDescriptor &f,
ElementDofOrdering o)
{
NVTX("get ER");
return std::visit([&o](auto&& arg) -> const Operator*
{
using T = std::decay_t<decltype(arg)>;
@@ -967,6 +1118,10 @@ const Operator *get_element_restriction(const FieldDescriptor &f,
{
return arg->GetElementRestriction(o);
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
return nullptr;
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
return arg->GetElementRestriction(o);
@@ -994,6 +1149,7 @@ const Operator *get_face_restriction(const FieldDescriptor &f,
FaceType ft,
L2FaceValues m)
{
NVTX("get FR");
return std::visit([&o, &ft, &m](auto&& arg) -> const Operator*
{
using T = std::decay_t<decltype(arg)>;
@@ -1002,6 +1158,11 @@ const Operator *get_face_restriction(const FieldDescriptor &f,
{
return arg->GetFaceRestriction(o, ft, m);
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
// QuadratureFunction does not support face restrictions
MFEM_ABORT("internal error");
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
// ParameterSpace does not support face restrictions
@@ -1027,6 +1188,7 @@ inline
const Operator *get_restriction(const FieldDescriptor &f,
const ElementDofOrdering &o)
{
NVTX("get R");
if constexpr (std::is_same_v<entity_t, Entity::Element>)
{
return get_element_restriction(f, o);
@@ -1052,12 +1214,14 @@ inline std::tuple<std::function<void(const Vector&, Vector&)>, int>
get_restriction_transpose(
const FieldDescriptor &f,
const ElementDofOrdering &o,
const fop_t &fop)
[[maybe_unused]] const fop_t &fop)
{
NVTX("get R^T");
if constexpr (is_sum_fop<fop_t>::value)
{
auto RT = [=](const Vector &v_e, Vector &v_l)
{
NVTX("R^T sum");
v_l += v_e;
};
return std::make_tuple(RT, 1);
@@ -1067,6 +1231,7 @@ get_restriction_transpose(
const Operator *R = get_restriction<entity_t>(f, o);
std::function<void(const Vector&, Vector&)> RT = [=](const Vector &x, Vector &y)
{
NVTX("R^T+");
R->AddMultTranspose(x, y);
};
return std::make_tuple(RT, R->Height());
@@ -1086,11 +1251,26 @@ get_restriction_transpose(
inline
void prolongation(const FieldDescriptor field, const Vector &x, Vector &field_l)
{
NVTX("P");
const auto P = get_prolongation(field);
NVTX_INI("SetSize");
field_l.SetSize(P->Height());
NVTX_END("SetSize");
NVTX_INI("P->Mult");
P->Mult(x, field_l);
}
inline
void prolongation_transpose(
const FieldDescriptor &field, const Vector &field_l, Vector &x)
{
const auto P = get_prolongation(field);
x.SetSize(P->Width());
P->MultTranspose(field_l, x);
}
/// @brief Apply the prolongation operator to a vector of fields.
///
/// x is a long vector containing the data for all fields on tdofs and
@@ -1107,6 +1287,7 @@ void prolongation(const std::array<FieldDescriptor, N> fields,
const Vector &x,
std::array<Vector, M> &fields_l)
{
NVTX("P");
int data_offset = 0;
for (int i = 0; i < N; i++)
{
@@ -1114,9 +1295,14 @@ void prolongation(const std::array<FieldDescriptor, N> fields,
const int width = P->Width();
// const Vector x_i(x.GetData() + data_offset, width);
const Vector x_i(const_cast<Vector&>(x), data_offset, width);
fields_l[i].SetSize(P->Height());
NVTX_INI("SetSize");
fields_l[i].SetSize(P->Height());
NVTX_END("SetSize");
NVTX_INI("P->Mult");
P->Mult(x_i, fields_l[i]);
NVTX_END("P->Mult");
data_offset += width;
}
}
@@ -1130,20 +1316,259 @@ void prolongation(const std::array<FieldDescriptor, N> fields,
/// @param fields the array of field descriptors.
/// @param x the input vector in tdofs.
/// @param fields_l the array of output vectors in vdofs.
// inline
// void prolongation(const std::vector<FieldDescriptor> fields,
// const Vector &x,
// std::vector<Vector> &fields_l)
// {
// int data_offset = 0;
// for (std::size_t i = 0; i < fields.size(); i++)
// {
// const auto P = get_prolongation(fields[i]);
// const int width = P->Width();
// const Vector x_i(const_cast<Vector&>(x), data_offset, width);
// fields_l[i].SetSize(P->Height());
// P->Mult(x_i, fields_l[i]);
// data_offset += width;
// }
// }
inline
void prolongation(const std::vector<FieldDescriptor> fields,
const Vector &x,
std::vector<Vector> &fields_l)
void prolongation(
const std::vector<FieldDescriptor> fields,
const BlockVector &x,
std::vector<Vector *> &x_l)
{
int data_offset = 0;
for (std::size_t i = 0; i < fields.size(); i++)
MFEM_ASSERT(x.NumBlocks() == static_cast<int>(x_l.size()),
"error " << x.NumBlocks() << " vs " << x_l.size());
for (int i = 0; i < x.NumBlocks(); i++)
{
const auto P = get_prolongation(fields[i]);
const int width = P->Width();
const Vector x_i(const_cast<Vector&>(x), data_offset, width);
fields_l[i].SetSize(P->Height());
P->Mult(x_i, fields_l[i]);
data_offset += width;
// If nullptr, assume Identity.
if (P == nullptr)
{
*x_l[i] = x.GetBlock(i);
}
else
{
const auto P = get_prolongation(fields[i]);
MFEM_ASSERT(P->Width() == x.GetBlock(i).Size(),
"prolongation not applicable to given input data size " <<
P->Width() << " vs " << x.GetBlock(i).Size());
MFEM_ASSERT(P->Height() == x_l[i]->Size(),
"prolongation not applicable to given output data size " <<
P->Height() << " vs " << x_l[i]->Size());
P->Mult(x.GetBlock(i), *x_l[i]);
}
}
}
inline
void prolongation(
const std::vector<FieldDescriptor> fields,
const MultiVector &x,
std::vector<Vector *> &x_l)
{
MFEM_ASSERT(x.NumBlocks() == static_cast<int>(x_l.size()),
"error " << x.NumBlocks() << " vs " << x_l.size());
for (int i = 0; i < x.NumBlocks(); i++)
{
const auto P = get_prolongation(fields[i]);
// If nullptr, assume Identity.
if (P == nullptr)
{
*x_l[i] = x[i];
}
else
{
const auto P = get_prolongation(fields[i]);
MFEM_ASSERT(P->Width() == x[i].Size(),
"prolongation not applicable to given input data size " <<
P->Width() << " vs " << x[i].Size());
MFEM_ASSERT(P->Height() == x_l[i]->Size(),
"prolongation not applicable to given output data size " <<
P->Height() << " vs " << x_l[i]->Size());
P->Mult(x[i], *x_l[i]);
}
}
}
inline
void prolongation_transpose(
const std::vector<FieldDescriptor> fields,
const std::vector<Vector *> &x_l,
BlockVector &x)
{
MFEM_ASSERT(static_cast<int>(x_l.size()) == x.NumBlocks(),
"error " << x_l.size() << " vs " << x.NumBlocks());
for (size_t i = 0; i < x_l.size(); i++)
{
const auto P = get_prolongation(fields[i]);
// If nullptr, assume Identity.
if (P == nullptr)
{
x.GetBlock(i) = *x_l[i];
}
else
{
MFEM_ASSERT(P->Height() == x_l[i]->Size(),
"prolongation not applicable to given input data size " <<
P->Height() << " vs " << x_l[i]->Size());
MFEM_ASSERT(P->Width() == x.GetBlock(i).Size(),
"prolongation not applicable to given output data size " <<
P->Width() << " vs " << x.GetBlock(i).Size());
P->MultTranspose(*x_l[i], x.GetBlock(i));
}
}
}
inline
void prolongation_transpose(
const std::vector<FieldDescriptor> fields,
const std::vector<Vector *> &x_l,
MultiVector &x)
{
MFEM_ASSERT(static_cast<int>(x_l.size()) == x.NumBlocks(),
"error " << x_l.size() << " vs " << x.NumBlocks());
for (size_t i = 0; i < x_l.size(); i++)
{
const auto P = get_prolongation(fields[i]);
// If nullptr, assume Identity.
if (P == nullptr)
{
x[i] = *x_l[i];
}
else
{
MFEM_ASSERT(P->Height() == x_l[i]->Size(),
"prolongation not applicable to given input data size " <<
P->Height() << " vs " << x_l[i]->Size());
MFEM_ASSERT(P->Width() == x[i].Size(),
"prolongation not applicable to given output data size " <<
P->Width() << " vs " << x[i].Size());
P->MultTranspose(*x_l[i], x[i]);
}
}
}
template <typename entity_t>
void restriction(
const std::vector<FieldDescriptor> fields,
const std::vector<Vector *> &x_l,
std::vector<Vector *> &x_e)
{
MFEM_ASSERT(x_l.size() == x_e.size(),
"internal error " << x_l.size() << " vs " << x_e.size());
for (size_t i = 0; i < fields.size(); i++)
{
int s = 0;
const auto R = get_restriction<entity_t>(
fields[i], ElementDofOrdering::LEXICOGRAPHIC);
// If nullptr, assume Identity.
if (R == nullptr)
{
s = x_l[i]->Size();
}
else
{
s = R->Height();
}
// TODO
if (x_e[i] == nullptr)
{
x_e[i] = new Vector(s);
}
x_e[i]->SetSize(s);
if (R == nullptr)
{
x_e[i] = x_l[i];
}
else
{
MFEM_ASSERT(R->Width() == x_l[i]->Size(),
"restriction not applicable to given input data size " <<
R->Width() << " vs " << x_l[i]->Size());
R->Mult(*x_l[i], *x_e[i]);
}
}
}
template <typename entity_t>
void prepare_residual(
const std::vector<FieldDescriptor> &fields,
std::vector<Vector *> &r_e)
{
for (size_t i = 0; i < fields.size(); i++)
{
int s = 0;
if (std::holds_alternative<const QuadratureFunction *>(fields[i].data))
{
const auto fd = std::get<const QuadratureFunction *>(fields[i].data);
s = fd->Size();
}
else
{
const auto R = get_restriction<entity_t>(
fields[i], ElementDofOrdering::LEXICOGRAPHIC);
s = R->Height();
}
// TODO
if (r_e[i] == nullptr)
{
r_e[i] = new Vector(s);
}
else
{
r_e[i]->SetSize(s);
}
}
}
template <typename entity_t>
void restriction_transpose(
const std::vector<FieldDescriptor> &fields,
const std::vector<Vector *> &x_e,
std::vector<Vector *> &x_l)
{
for (size_t i = 0; i < fields.size(); i++)
{
int s = 0;
const auto R = get_restriction<entity_t>(
fields[i], ElementDofOrdering::LEXICOGRAPHIC);
// TODO: if nullptr, assume Identity
if (R == nullptr)
{
s = x_e[i]->Size();
}
else
{
s = R->Width();
}
// TODO
if (x_l[i] == nullptr)
{
x_l[i] = new Vector(s);
}
x_l[i]->SetSize(s);
// TODO: if nullptr, assume Identity
if (R == nullptr)
{
x_l[i] = x_e[i];
}
else
{
R->MultTranspose(*x_e[i], *x_l[i]);
}
}
}
@@ -1152,6 +1577,7 @@ void get_lvectors(const std::vector<FieldDescriptor> fields,
const Vector &x,
std::vector<Vector> &fields_l)
{
NVTX("get_lvectors");
int data_offset = 0;
for (std::size_t i = 0; i < fields.size(); i++)
{
@@ -1178,13 +1604,15 @@ template <typename fop_t>
inline
std::function<void(const Vector&, Vector&)> get_prolongation_transpose(
const FieldDescriptor &f,
const fop_t &fop,
[[maybe_unused]] const fop_t &fop,
MPI_Comm mpi_comm)
{
NVTX("get P^T");
if constexpr (is_sum_fop<fop_t>::value)
{
auto PT = [=](const Vector &r_local, Vector &y)
{
NVTX("P^T sum");
MFEM_ASSERT(y.Size() == 1, "output size doesn't match kernel description");
real_t local_sum = r_local.Sum();
MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM, mpi_comm);
@@ -1195,6 +1623,7 @@ std::function<void(const Vector&, Vector&)> get_prolongation_transpose(
{
auto PT = [=](const Vector &r_local, Vector &y)
{
NVTX("P^T Identity");
y = r_local;
};
return PT;
@@ -1202,6 +1631,7 @@ std::function<void(const Vector&, Vector&)> get_prolongation_transpose(
const Operator *P = get_prolongation(f);
auto PT = [=](const Vector &r_local, Vector &y)
{
NVTX("P^T");
P->MultTranspose(r_local, y);
};
return PT;
@@ -1220,12 +1650,19 @@ void restriction(const FieldDescriptor u,
Vector &field_e,
ElementDofOrdering ordering)
{
NVTX("R");
const auto R = get_restriction<entity_t>(u, ordering);
MFEM_ASSERT(R->Width() == u_l.Size(),
"restriction not applicable to given data size");
const int height = R->Height();
NVTX_INI("SetSize");
field_e.SetSize(height);
NVTX_END("SetSize");
NVTX_INI("R->Mult");
R->Mult(u_l, field_e);
NVTX_END("R->Mult");
}
/// @brief Apply the restriction operator to a vector of fields.
@@ -1243,14 +1680,29 @@ void restriction(const std::vector<FieldDescriptor> u,
ElementDofOrdering ordering,
const int offset = 0)
{
NVTX("R");
for (std::size_t i = 0; i < u.size(); i++)
{
const auto R = get_restriction<entity_t>(u[i], ordering);
MFEM_ASSERT(R->Width() == u_l[i].Size(),
"restriction not applicable to given data size");
const int height = R->Height();
// NVTX_INI("SetSize");
fields_e[i + offset].SetSize(height);
R->Mult(u_l[i], fields_e[i + offset]);
// NVTX_END("SetSize");
// NVTX_INI("R->Mult");
if (dynamic_cast<const IdentityOperator*>(R))
{
NVTX("Identity");
fields_e[i + offset].NewMemoryAndSize(u_l[i].GetMemory(), u_l[i].Size(), false);
}
else
{
R->Mult(u_l[i], fields_e[i + offset]);
}
// NVTX_END("R->Mult");
}
}
@@ -1262,14 +1714,21 @@ void element_restriction(const std::array<FieldDescriptor, N> u,
ElementDofOrdering ordering,
const int offset = 0)
{
NVTX("ER");
for (int i = 0; i < N; i++)
{
const auto R = get_element_restriction(u[i], ordering);
MFEM_ASSERT(R->Width() == u_l[i].Size(),
"element restriction not applicable to given data size");
const int height = R->Height();
NVTX_INI("SetSize");
fields_e[i + offset].SetSize(height);
NVTX_END("SetSize");
NVTX_INI("R->Mult");
R->Mult(u_l[i], fields_e[i + offset]);
NVTX_END("R->Mult");
}
}
@@ -1326,6 +1785,10 @@ const DofToQuad *GetDofToQuad(const FieldDescriptor &f,
return &arg->GetTypicalTraceElement()->GetDofToQuad(ir, mode);
}
}
else if constexpr (std::is_same_v<T, const QuadratureFunction *>)
{
return nullptr;
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
return &arg->GetDofToQuad();
@@ -1457,7 +1920,7 @@ create_descriptors_to_fields_map(
auto f = [&](auto &fop, auto &map)
{
if constexpr (std::is_same_v<std::decay_t<decltype(fop)>, Weight>)
if constexpr (is_weight_fop<std::decay_t<decltype(fop)>>::value)
{
// TODO-bug: stealing dimension from the first field
fop.dim = GetDimension<entity_t>(fields[0]);
@@ -1587,7 +2050,7 @@ get_shmem_info(
const std::array<DofToQuadMap, num_outputs> &output_dtq_maps,
const std::vector<FieldDescriptor> &fields,
const int &num_entities,
const input_t &inputs,
[[maybe_unused]] const input_t &inputs,
const int &num_qp,
const std::vector<int> &input_size_on_qp,
const int &residual_size_on_qp,
@@ -2342,5 +2805,25 @@ std::array<DofToQuadMap, num_fields> create_dtq_maps(
std::make_index_sequence<num_fields> {});
}
struct QLayoutEntry
{
std::type_index type;
std::vector<int> layout;
template <class Fop>
QLayoutEntry(Fop, std::initializer_list<int> idx) :
type(typeid(Fop)), layout(idx) {}
};
static void ExtractQLayouts(
const std::initializer_list<QLayoutEntry> entries,
std::unordered_map<std::type_index, std::vector<int>>& out)
{
for (const auto& e : entries)
{
out[e.type] = e.layout;
}
}
} // namespace mfem::future
#endif
+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
+122 -44
View File
@@ -663,58 +663,59 @@ const
#pragma omp critical (DofToQuad)
#endif
{
// If the new Dof2Quad is already present, e.g. added in a previous call
// or added by another omp thread, return.
// Do not run if the new Dof2Quad is already present, e.g. added in a
// previous call or added by another omp thread.
if (DofToQuad::SearchArray(dof2quad_array, ir,
DofToQuad::LEXICOGRAPHIC_FULL))
{ return; }
// Undo the native ordering which is what FiniteElement::GetDofToQuad
// returns.
auto *d2q_new = new DofToQuad(d2q);
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
const int nqpt = ir.GetNPoints();
const int b_dim = (range_type == VECTOR) ? dim : 1;
for (int i = 0; i < nqpt; i++)
DofToQuad::LEXICOGRAPHIC_FULL) == nullptr)
{
for (int d = 0; d < b_dim; d++)
// Undo the native ordering which is what FiniteElement::GetDofToQuad
// returns.
auto *d2q_new = new DofToQuad(d2q);
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
const int nqpt = ir.GetNPoints();
const int b_dim = (range_type == VECTOR) ? dim : 1;
for (int i = 0; i < nqpt; i++)
{
for (int j = 0; j < dof; j++)
for (int d = 0; d < b_dim; d++)
{
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
for (int j = 0; j < dof; j++)
{
const double val = d2q.B[i + nqpt*(d+b_dim*lex_ordering[j])];
d2q_new->B[i+nqpt*(d+b_dim*j)] = val;
d2q_new->Bt[j+dof*(i+nqpt*d)] = val;
}
}
}
}
const int g_dim = [this]()
{
switch (deriv_type)
const int g_dim = [this]()
{
case GRAD: return dim;
case DIV: return 1;
case CURL: return cdim;
default: return 0;
}
}();
for (int i = 0; i < nqpt; i++)
{
for (int d = 0; d < g_dim; d++)
{
for (int j = 0; j < dof; j++)
switch (deriv_type)
{
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
case GRAD: return dim;
case DIV: return 1;
case CURL: return cdim;
default: return 0;
}
}();
for (int i = 0; i < nqpt; i++)
{
for (int d = 0; d < g_dim; d++)
{
for (int j = 0; j < dof; j++)
{
const double val = d2q.G[i + nqpt*(d+g_dim*lex_ordering[j])];
d2q_new->G[i+nqpt*(d+g_dim*j)] = val;
d2q_new->Gt[j+dof*(i+nqpt*d)] = val;
}
}
}
dof2quad_array.Append(d2q_new);
}
dof2quad_array.Append(d2q_new);
}
}
@@ -1043,9 +1044,50 @@ void VectorFiniteElement::SetDerivMembers()
switch (map_type)
{
case H_DIV:
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
switch (dim)
{
case 3: // div: 3D H_DIV -> 3D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
case 2: // div: 2D H_DIV -> 2D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_DIV_R2D:
switch (dim)
{
case 2: // div: 2D H_DIV_R2D -> 2D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
case 1: // div: 1D H_DIV_R2D -> 1D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_DIV_R1D:
switch (dim)
{
case 1: // div: 1D H_DIV_R1D -> 1D INTEGRAL
deriv_type = DIV;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_CURL:
switch (dim)
@@ -1063,13 +1105,49 @@ void VectorFiniteElement::SetDerivMembers()
break;
case 1:
deriv_type = NONE;
deriv_range_type = SCALAR;
deriv_map_type = INTEGRAL;
deriv_range_type = UNKNOWN_RANGE_TYPE;
deriv_map_type = UNKNOWN_MAP_TYPE;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_CURL_R2D:
switch (dim)
{
case 2:
// curl: 2D H_CURL_R2D -> H_DIV_R2D
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV_R2D;
break;
case 1:
// curl: 1D H_CURL_R2D -> H_DIV_R2D
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV_R2D;
break;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
case H_CURL_R1D:
switch (dim)
{
case 1:
// curl: 1D H_CURL_R1D -> H_DIV_R1D
deriv_type = CURL;
deriv_range_type = VECTOR;
deriv_map_type = H_DIV_R1D;
break;
case 0:
deriv_type = NONE;
deriv_range_type = UNKNOWN_RANGE_TYPE;
deriv_map_type = UNKNOWN_MAP_TYPE;
default:
MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
break;
default:
MFEM_ABORT("Invalid MapType = " << map_type);
}
+31 -3
View File
@@ -295,10 +295,20 @@ public:
$ u(x) = (1/w) \hat u(\hat x) $ */
H_DIV, /**< For vector fields; preserves surface integrals of the
normal component $ u(x) = (J/w) \hat u(\hat x) $ */
H_CURL /**< For vector fields; preserves line integrals of the
H_CURL, /**< For vector fields; preserves line integrals of the
tangential component
$ u(x) = J^{-t} \hat u(\hat x) $ (square J),
$ u(x) = J(J^t J)^{-1} \hat u(\hat x) $ (general J) */
H_DIV_R2D, /**< For 3-component vector fields in 2D; equivalent to a
direct sum of an H_DIV basis and an INTEGRAL basis */
H_CURL_R2D,/**< For 3-component vector fields in 2D; equivalent to a
direct sum of an H_CURL basis and a VALUE basis */
H_DIV_R1D, /**< For 3-component vector fields in 1D; equivalent to a
direct sum of a VALUE basis and a pair of INTEGRAL
bases */
H_CURL_R1D /**< For 3-component vector fields in 1D; equivalent to a
direct sum of an INTEGRAL basis and a pair of VALUE
bases */
};
/** @brief Enumeration for DerivType: defines which derivative method
@@ -330,12 +340,28 @@ public:
int GetDim() const { return dim; }
/** @brief Returns the vector dimension for vector-valued finite elements,
which is also the dimension of the interpolation operation. */
which is also the dimension of the interpolation operation and the
width of the DenseMatrix argument in
CalcVShape(const IntegrationPoint &ip, DenseMatrix &shape). */
int GetRangeDim() const { return vdim; }
/// Returns the dimension of the curl for vector-valued finite elements.
/** @brief Returns the vector dimension, in physical space, for
vector-valued finite elements, which is also the width of the
DenseMatrix argument in
CalcPhysVShape(ElementTransformation &Trans, DenseMatrix &shape). */
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
CalcCurlShape(const IntegrationPoint &ip, DenseMatrix &curl_shape). */
int GetCurlDim() const { return cdim; }
/** Returns the dimension, in physical space, of the curl for vector-valued
finite elements, which is also the width of the DenseMatrix argument in
CalcPhysCurlShape(ElementTransformation &Trans, DenseMatrix &curl_shape).
*/
virtual int GetPhysCurlDim(int /* space_dim */) const { return cdim; }
/// Returns the Geometry::Type of the reference element.
Geometry::Type GetGeomType() const { return geom_type; }
@@ -990,6 +1016,8 @@ protected:
public:
VectorFiniteElement(int D, Geometry::Type G, int Do, int O, int M,
int F = FunctionSpace::Pk);
int GetPhysRangeDim(int space_dim) const override { return space_dim; }
};
/// @brief Class for computing 1D special polynomials and their associated basis
+1 -1
View File
@@ -589,7 +589,7 @@ void H1_TriangleElement::CalcHessian(const IntegrationPoint &ip,
Vector shape_x(p + 1), shape_y(p + 1), shape_l(p + 1);
Vector dshape_x(p + 1), dshape_y(p + 1), dshape_l(p + 1);
Vector ddshape_x(p + 1), ddshape_y(p + 1), ddshape_l(p + 1);
DenseMatrix ddu(dof, dim);
DenseMatrix ddu(dof, (dim*(dim+1))/2);
#endif
poly1d.CalcBasis(p, ip.x, shape_x, dshape_x, ddshape_x);
+4 -4
View File
@@ -2531,7 +2531,7 @@ void ND_FuentesPyramidElement::calcCurlBasis(const int p,
ND_R1D_PointElement::ND_R1D_PointElement(int p)
: VectorFiniteElement(1, Geometry::POINT, 2, p,
H_CURL, FunctionSpace::Pk)
H_CURL_R1D, FunctionSpace::Pk)
{
// VectorFiniteElement::SetDerivMembers doesn't support 0D H_CURL elements
// so we mimic a 1D element and then correct the dimension here.
@@ -2562,7 +2562,7 @@ ND_R1D_SegmentElement::ND_R1D_SegmentElement(const int p,
const int cb_type,
const int ob_type)
: VectorFiniteElement(1, Geometry::SEGMENT, 3 * p + 2, p,
H_CURL, FunctionSpace::Pk),
H_CURL_R1D, FunctionSpace::Pk),
dof2tk(dof),
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cb_type))),
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type)))
@@ -2839,7 +2839,7 @@ ND_R2D_SegmentElement::ND_R2D_SegmentElement(const int p,
const int cb_type,
const int ob_type)
: VectorFiniteElement(1, Geometry::SEGMENT, 2 * p + 1, p,
H_CURL, FunctionSpace::Pk),
H_CURL_R2D, FunctionSpace::Pk),
dof2tk(dof),
cbasis1d(poly1d.GetBasis(p, VerifyClosed(cb_type))),
obasis1d(poly1d.GetBasis(p - 1, VerifyOpen(ob_type)))
@@ -3023,7 +3023,7 @@ void ND_R2D_SegmentElement::Project(VectorCoefficient &vc,
ND_R2D_FiniteElement::ND_R2D_FiniteElement(int p, Geometry::Type G, int Do,
const real_t *tk_fe)
: VectorFiniteElement(2, G, Do, p,
H_CURL, FunctionSpace::Pk),
H_CURL_R2D, FunctionSpace::Pk),
tk(tk_fe),
dof_map(dof),
dof2tk(dof)
+6
View File
@@ -663,6 +663,9 @@ public:
const int cb_type = BasisType::GaussLobatto,
const int ob_type = BasisType::GaussLegendre);
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;
@@ -705,6 +708,9 @@ private:
DenseMatrix &I) const;
public:
int GetPhysRangeDim(int space_dim) const override { return 3; }
int GetPhysCurlDim(int space_dim) const override { return 3; }
using FiniteElement::CalcVShape;
using FiniteElement::CalcPhysCurlShape;
+3 -3
View File
@@ -2006,7 +2006,7 @@ RT_R1D_SegmentElement::RT_R1D_SegmentElement(const int p,
const int cb_type,
const int ob_type)
: VectorFiniteElement(1, Geometry::SEGMENT, 3 * p + 4, p + 1,
H_DIV, FunctionSpace::Pk),
H_DIV_R1D, FunctionSpace::Pk),
dof2nk(dof),
cbasis1d(poly1d.GetBasis(p + 1, VerifyClosed(cb_type))),
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type)))
@@ -2281,7 +2281,7 @@ const real_t RT_R2D_SegmentElement::nk[2] = { 0.,1.};
RT_R2D_SegmentElement::RT_R2D_SegmentElement(const int p,
const int ob_type)
: VectorFiniteElement(1, Geometry::SEGMENT, p + 1, p + 1,
H_DIV, FunctionSpace::Pk),
H_DIV_R2D, FunctionSpace::Pk),
dof2nk(dof),
obasis1d(poly1d.GetBasis(p, VerifyOpen(ob_type)))
{
@@ -2392,7 +2392,7 @@ void RT_R2D_SegmentElement::LocalInterpolation(const VectorFiniteElement &cfe,
RT_R2D_FiniteElement::RT_R2D_FiniteElement(int p, Geometry::Type G, int Do,
const real_t *nk_fe)
: VectorFiniteElement(2, G, Do, p + 1,
H_DIV, FunctionSpace::Pk),
H_DIV_R2D, FunctionSpace::Pk),
nk(nk_fe),
dof_map(dof),
dof2nk(dof)
+6
View File
@@ -510,6 +510,9 @@ public:
RT_R2D_SegmentElement(const int p,
const int ob_type = BasisType::GaussLegendre);
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;
@@ -547,6 +550,9 @@ private:
DenseMatrix &I) const;
public:
int GetPhysRangeDim(int space_dim) const override { return 3; }
int GetPhysCurlDim(int space_dim) const override { return 0; }
using FiniteElement::CalcVShape;
void CalcVShape(ElementTransformation &Trans,
+1 -1
View File
@@ -52,7 +52,7 @@
#include "bounds.hpp"
#include "particleset.hpp"
#include "dfem/doperator.hpp"
// #include "dfem/doperator.hpp"
#ifdef MFEM_USE_MPI
#include "pfespace.hpp"
+28 -19
View File
@@ -282,14 +282,7 @@ int FiniteElementSpace::DofToVDof(int dof, int vd, int ndofs_) const
void FiniteElementSpace::AdjustVDofs(Array<int> &vdofs)
{
int n = vdofs.Size(), *vdof = vdofs;
for (int i = 0; i < n; i++)
{
int j;
if ((j = vdof[i]) < 0)
{
vdof[i] = -1-j;
}
}
for (int i = 0; i < n; i++) { vdof[i] = UnsignIndex(vdof[i]); }
}
void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
@@ -483,13 +476,14 @@ void FiniteElementSpace::ReorderElementToDofTable()
for (int k = 0, dof_counter = 0; k < nnz; k++)
{
const int sdof = J[k]; // signed dof
const int dof = (sdof < 0) ? -1-sdof : sdof;
const int dof = UnsignIndex(sdof);
int new_dof = dof_marker[dof];
if (new_dof < 0)
{
dof_marker[dof] = new_dof = dof_counter++;
}
J[k] = (sdof < 0) ? -1-new_dof : new_dof; // preserve the sign of sdof
// Preserve the sign of sdof
J[k] = (sdof < 0) ? FlipIndexSign(new_dof) : new_dof;
}
}
@@ -547,7 +541,7 @@ void MarkDofs(const Array<int> &dofs, Array<int> &mark_array)
{
for (auto d : dofs)
{
mark_array[d >= 0 ? d : -1 - d] = -1;
mark_array[UnsignIndex(d)] = -1;
}
}
@@ -931,7 +925,7 @@ void FiniteElementSpace::AddDependencies(
if (std::abs(coef) > 1e-12)
{
const int mdof = master_dofs[j];
if (mdof != sdof && mdof != (-1-sdof))
if (mdof != sdof && mdof != FlipIndexSign(sdof))
{
deps.Add(sdof, mdof, coef);
}
@@ -1024,7 +1018,7 @@ int FiniteElementSpace::GetDegenerateFaceDofs(int index, Array<int> &dofs,
// FiniteElementSpace::AddDependencies.
Array<int> edof;
int order = GetEdgeDofs(-1 - index, edof, variant);
int order = GetEdgeDofs(FlipIndexSign(index), edof, variant);
int nv = fec->DofForGeometry(Geometry::POINT);
int ne = fec->DofForGeometry(Geometry::SEGMENT);
@@ -1710,8 +1704,8 @@ SparseMatrix *FiniteElementSpace::RefinementMatrix_main(
for (int i = 0; i < fine_ldof; i++)
{
int r = DofToVDof(dofs[i], vd);
int m = (r >= 0) ? r : (-1 - r);
const int r = DofToVDof(dofs[i], vd);
const int m = UnsignIndex(r);
if (!mark[m])
{
@@ -1772,7 +1766,7 @@ SparseMatrix *FiniteElementSpace::VariableOrderRefinementMatrix(
for (int i = 0; i < fine_ldof; i++)
{
const int r = DofToVDof(dofs[i], vd);
int m = (r >= 0) ? r : (-1 - r);
const int m = UnsignIndex(r);
if (!mark[m])
{
@@ -2482,8 +2476,8 @@ SparseMatrix* FiniteElementSpace::DerefinementMatrix(int old_ndofs,
{
if (!std::isfinite(lR(i, 0))) { continue; }
int r = DofToVDof(dofs[i], vd);
int m = (r >= 0) ? r : (-1 - r);
const int r = DofToVDof(dofs[i], vd);
const int m = UnsignIndex(r);
if (is_dg || !mark[m])
{
@@ -3201,7 +3195,7 @@ void FiniteElementSpace::CalcEdgeFaceVarOrders(
else
{
// degenerate face (i.e., edge-face constraint)
slave_orders |= edge_orders[-1 - slave.index];
slave_orders |= edge_orders[FlipIndexSign(slave.index)];
}
}
@@ -3940,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");
@@ -3970,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
{
+14 -2
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;
@@ -1150,7 +1150,7 @@ public:
/// Helper to return the DOF associated with a sign encoded DOF
static inline int DecodeDof(int dof)
{ return (dof >= 0) ? dof : (-1 - dof); }
{ return UnsignIndex(dof); }
/// Helper to determine the DOF and sign of a sign encoded DOF
static inline int DecodeDof(int dof, real_t& sign)
@@ -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;
+642 -73
View File
@@ -30,6 +30,7 @@
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>
namespace mfem
{
@@ -344,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();
@@ -2049,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;
@@ -2201,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;
@@ -2354,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;
@@ -2629,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;
@@ -2944,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;
@@ -2973,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;
@@ -3029,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;
@@ -3045,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
@@ -3061,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);
@@ -3081,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);
@@ -3136,52 +3144,33 @@ void GridFunction::ProjectBdrCoefficient(Coefficient *coeff[],
}
void GridFunction::ProjectBdrCoefficientNormal(
VectorCoefficient &vcoeff, const Array<int> &bdr_attr)
Coefficient *coeff, VectorCoefficient *vcoeff, const Array<int> &bdr_attr)
{
#if 0
// implementation for the case when the face dofs are integrals of the
// normal component.
const FiniteElement *fe;
ElementTransformation *T;
Array<int> dofs;
int dim = vcoeff.GetVDim();
Vector vc(dim), nor(dim), lvec, shape;
for (int i = 0; i < fes->GetNBE(); i++)
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)
{
if (bdr_attr[fes->GetBdrAttribute(i)-1] == 0)
{
continue;
}
fe = fes->GetBE(i);
T = fes->GetBdrElementTransformation(i);
int intorder = 2*fe->GetOrder(); // !!!
const IntegrationRule &ir = IntRules.Get(fe->GetGeomType(), intorder);
int nd = fe->GetDof();
lvec.SetSize(nd);
shape.SetSize(nd);
lvec = 0.0;
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
T->SetIntPoint(&ip);
vcoeff.Eval(vc, *T, ip);
CalcOrtho(T->Jacobian(), nor);
fe->CalcShape(ip, shape);
lvec.Add(ip.weight * (vc * nor), shape);
}
fes->GetBdrElementDofs(i, dofs);
SetSubVector(dofs, lvec);
MFEM_VERIFY(vcoeff->GetVDim() == fes->GetMesh()->SpaceDimension(),
"vcoeff vdim (" << vcoeff->GetVDim()
<< ") != SpaceDimension ("
<< fes->GetMesh()->SpaceDimension() << ")");
}
#else
// implementation for the case when the face dofs are scaled point
// values of the normal component.
const FiniteElement *fe;
ElementTransformation *T;
Array<int> dofs;
int dim = vcoeff.GetVDim();
Vector vc(dim), nor(dim), lvec;
Vector vc, nor, lvec;
DofTransformation doftrans;
if (vcoeff)
{
const int dim = vcoeff->GetVDim();
vc.SetSize(dim);
nor.SetSize(dim);
}
for (int i = 0; i < fes->GetNBE(); i++)
{
@@ -3197,15 +3186,22 @@ void GridFunction::ProjectBdrCoefficientNormal(
{
const IntegrationPoint &ip = ir.IntPoint(j);
T->SetIntPoint(&ip);
vcoeff.Eval(vc, *T, ip);
CalcOrtho(T->Jacobian(), nor);
lvec(j) = (vc * nor);
if (coeff)
{
const real_t c = coeff->Eval(*T, ip);
lvec(j) = c * T->Weight();
}
else if (vcoeff)
{
vcoeff->Eval(vc, *T, ip);
CalcOrtho(T->Jacobian(), nor);
lvec(j) = (vc * nor);
}
}
fes->GetBdrElementDofs(i, dofs, doftrans);
doftrans.TransformPrimal(lvec);
SetSubVector(dofs, lvec);
}
#endif
}
void GridFunction::ProjectBdrCoefficientTangent(
@@ -5006,6 +5002,14 @@ real_t ExtrudeCoefficient::Eval(ElementTransformation &T,
return sol_in.Eval(*T_in, ip);
}
void VectorExtrudeCoefficient::Eval(Vector &v, ElementTransformation &T,
const IntegrationPoint &ip)
{
ElementTransformation *T_in =
mesh_in->GetElementTransformation(T.ElementNo / n);
T_in->SetIntPoint(&ip);
sol_in.Eval(v, *T_in, ip);
}
GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
GridFunction *sol, const int ny)
@@ -5056,10 +5060,17 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
return NULL;
}
FiniteElementSpace *solfes2d;
// assuming sol is scalar
solfes2d = new FiniteElementSpace(mesh2d, solfec2d);
const int vdim = sol->FESpace()->GetVDim();
solfes2d = new FiniteElementSpace(mesh2d, solfec2d, vdim);
sol2d = new GridFunction(solfes2d);
sol2d->MakeOwner(solfec2d);
if (vdim > 1)
{
VectorGridFunctionCoefficient vcsol(sol);
VectorExtrudeCoefficient vc2d(mesh, vcsol, ny);
sol2d->ProjectCoefficient(vc2d);
}
else
{
GridFunctionCoefficient csol(sol);
ExtrudeCoefficient c2d(mesh, csol, ny);
@@ -5117,6 +5128,103 @@ void GridFunction::GetElementBoundsAtControlPoints(const int elem,
}
}
void GridFunction::GetElementBoundsAtControlPoints(const int elem,
const PLBound &plb,
const Vector &ref_range,
const int vdim,
Vector &lower, Vector &upper,
Vector &control_pos) const
{
const FiniteElement *fe = fes->GetFE(elem);
const IntegrationRule ir_in = fe->GetNodes();
IntegrationRule ir_new(ir_in.GetNPoints());
const int dim = fes->GetMesh()->Dimension();
const L2_FECollection *l2fec = dynamic_cast<const L2_FECollection *>
(fes->FEColl());
const TensorBasisElement *tbe =
dynamic_cast<const TensorBasisElement *>(fe);
MFEM_VERIFY(tbe != NULL, "TensorBasis FiniteElement expected.");
const Array<int> &dof_map = tbe->GetDofMap();
bool lexico = (dof_map.Size() == 0);
bool bern = (tbe->GetBasisType() == BasisType::Positive);
bool h1 = (l2fec == nullptr);
Vector loc_data; // gridfunction values
// Construct an integration rule to evaluate the gridfunction in
// subinterval.
for (int i = 0; i < ir_in.GetNPoints(); i++)
{
IntegrationPoint &ip_new = ir_new.IntPoint(i);
const IntegrationPoint &ip_old =
ir_in.IntPoint((lexico || bern) ? i : dof_map[i]);
Vector ip_coord(dim);
ip_old.Get(ip_coord.GetData(), dim);
for (int d = 0; d < dim; d++)
{
ip_coord(d) = ref_range(d) +
(ref_range(dim+d) - ref_range(d)) * ip_coord(d);
}
ip_new.Set(ip_coord.GetData(), dim);
}
GetValues(elem, ir_new, loc_data, vdim);
// At this point, the loc_data contains function values ordered
// lexicographically, unless we are using Bernstein bases.
// For Bernstein, we need to project and get coefficients first.
// For bernstein, we get coefficients corresponding to these function values
if (bern)
{
int bt = 4; // BasisType::ClosedUniform
int o = fe->GetOrder();
DenseMatrix projmat;
NodalTensorFiniteElement *ntfe = nullptr;
if (dim == 1)
{
if (h1) { ntfe = new H1_SegmentElement(o, bt); }
else { ntfe = new L2_SegmentElement(o, bt); }
}
else if (dim == 2)
{
if (h1) { ntfe = new H1_QuadrilateralElement(o, bt); }
else { ntfe = new L2_QuadrilateralElement(o, bt); }
}
else if (dim == 3)
{
if (h1) { ntfe = new H1_HexahedronElement(o, bt); }
else { ntfe = new L2_HexahedronElement(o, bt); }
}
// projection matrix from H1 to Positive
ElementTransformation *eltran = fes->GetElementTransformation(elem);
fe->Project(*ntfe, *eltran, projmat);
Vector loc_data_temp(loc_data.Size());
projmat.Mult(loc_data, loc_data_temp);
for (int i = 0; i < dof_map.Size(); i++)
{
loc_data(i) = loc_data_temp(dof_map[i]);
}
if (dof_map.Size() == 0) { loc_data = loc_data_temp; }
delete ntfe;
}
// Get bounds at control points
plb.GetNDBounds(dim, loc_data, lower, upper);
// Save control point positions
int ncp = plb.GetNControlPoints();
control_pos.SetSize(dim * ncp);
const Vector control_pos_1D = plb.GetControlPoints();
for (int i = 0; i < ncp; i++)
{
for (int d = 0; d < dim; d++)
{
control_pos(i + d*ncp) =
ref_range(d) + (ref_range(dim+d)-ref_range(d))*control_pos_1D(i);
}
}
}
void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
Vector &lower, Vector &upper,
const int vdim) const
@@ -5197,6 +5305,467 @@ PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
return plb;
}
struct IntervalNode
{
real_t val_min;
real_t val_max;
Array<IntervalNode *> child;
IntervalNode(real_t vmin, real_t vmax)
: val_min(vmin), val_max(vmax)
{
child.SetSize(0);
}
void AddChild(IntervalNode *ch) { child.Append(ch); }
real_t GetChildMinLower()
{
if (child.Size() == 0)
{
return val_min;
}
real_t valmin = numeric_limits<real_t>::max();
for (int i = 0; i < child.Size(); i++)
{
real_t candidate = child[i]->GetChildMinLower();
valmin = std::min(valmin, candidate);
}
return valmin;
}
real_t GetChildMinUpper()
{
if (child.Size() == 0)
{
return val_max;
}
real_t valmax = numeric_limits<real_t>::max();
for (int i = 0; i < child.Size(); i++)
{
real_t candidate = child[i]->GetChildMinUpper();
valmax = std::min(valmax, candidate);
}
return valmax;
}
real_t GetChildMaxLower()
{
if (child.Size() == 0)
{
return val_min;
}
real_t valmin = numeric_limits<real_t>::lowest();
for (int i = 0; i < child.Size(); i++)
{
real_t candidate = child[i]->GetChildMaxLower();
valmin = std::max(valmin, candidate);
}
return valmin;
}
real_t GetChildMaxUpper()
{
if (child.Size() == 0)
{
return val_max;
}
real_t valmax = numeric_limits<real_t>::lowest();
for (int i = 0; i < child.Size(); i++)
{
real_t candidate = child[i]->GetChildMaxUpper();
valmax = std::max(valmax, candidate);
}
return valmax;
}
void DeleteChildren()
{
for (int i = 0; i < child.Size(); i++)
{
child[i]->DeleteChildren();
delete child[i];
}
child.SetSize(0);
}
};
struct SearchInterval
{
Vector ref_range;
int depth;
IntervalNode *node;
SearchInterval(const Vector &ref_range_in, int d, IntervalNode *n)
: ref_range(ref_range_in), depth(d), node(n)
{ }
};
struct IntervalCompareMin
{
bool operator()(const SearchInterval *a, const SearchInterval *b) const
{
return a->node->val_min > b->node->val_min;
}
};
struct IntervalCompareMax
{
bool operator()(const SearchInterval *a, const SearchInterval *b) const
{
return a->node->val_max < b->node->val_max;
}
};
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
const int elem, const PLBound &plb, const int vdim,
const int max_depth, const real_t tol) const
{
real_t min_threshold = std::numeric_limits<real_t>::max();
return EstimateFunctionMinimum(elem, plb, vdim, max_depth, tol,
min_threshold);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
const int elem, const PLBound &plb, const int vdim,
const int max_depth, const real_t tol, real_t &min_threshold) const
{
const int dim = this->FESpace()->GetMesh()->Dimension();
const int ncp = plb.GetNControlPoints();
Vector pos_range(2*dim); pos_range = 0.0;
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
Vector lower, upper, cp_ref_loc;
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
real_t val_min = lower.Min();
real_t val_max = upper.Min();
min_threshold = std::min(min_threshold, val_max);
// Pruning: if the element's lower bound is greater than the current global
// upper bound, this element cannot contain the global minimum.
if (val_min >= min_threshold)
{
return std::make_pair(val_min, val_max);
}
if (val_min == val_max || max_depth == 0)
{
min_threshold = std::min(min_threshold, val_min);
return std::make_pair(val_min, val_max);
}
real_t abs_tol = tol*(val_max-val_min);
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
initial_node);
std::priority_queue<SearchInterval*,
std::vector<SearchInterval*>, IntervalCompareMin> pq;
pq.push(initial_interval);
real_t min_upper_bound = upper.Min();
real_t min_lower_bound = lower.Min();
while (!pq.empty())
{
SearchInterval *current = pq.top();
pq.pop();
int curr_depth = current->depth;
// Reached max depth or this interval cannot contain the global minimum
if (current->node->val_min >= min_threshold || curr_depth >= max_depth)
{
delete current;
continue;
}
min_lower_bound = initial_node->GetChildMinLower();
if (min_upper_bound - min_lower_bound < abs_tol)
{
delete current;
break;
}
// Subdivide the interval and get bounds on it
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
vdim, lower, upper, cp_ref_loc);
// process the bounds and create sub-intervals
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
{
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
{
for (int i = 0; i < ncp-1; i++)
{
real_t lv = 0.0, uv = 0.0;
if (dim == 1)
{
lv = std::min(lower(i), lower(i+1));
uv = std::min(upper(i), upper(i+1));
}
else if (dim == 2)
{
lv = std::min({lower(i + j*ncp), lower((i+1) + j*ncp),
lower(i + (j+1)*ncp),
lower((i+1) + (j+1)*ncp)});
uv = std::min({upper(i + j*ncp), upper((i+1) + j*ncp),
upper(i + (j+1)*ncp),
upper((i+1) + (j+1)*ncp)});
}
else if (dim == 3)
{
lv = std::min({lower(i + j*ncp + k*ncp*ncp),
lower((i+1) + j*ncp + k*ncp*ncp),
lower(i + (j+1)*ncp + k*ncp*ncp),
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
lower(i + j*ncp + (k+1)*ncp*ncp),
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
uv = std::min({upper(i + j*ncp + k*ncp*ncp),
upper((i+1) + j*ncp + k*ncp*ncp),
upper(i + (j+1)*ncp + k*ncp*ncp),
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
upper(i + j*ncp + (k+1)*ncp*ncp),
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
}
IntervalNode *child_node = new IntervalNode(lv, uv);
current->node->AddChild(child_node);
if (lv < min_threshold)
{
min_upper_bound = std::min(min_upper_bound, uv);
min_threshold = std::min(min_threshold, uv);
if (curr_depth < max_depth)
{
pos_range(0) = cp_ref_loc(i);
pos_range(0+dim) = cp_ref_loc(i+1);
if (dim >= 2)
{
pos_range(1) = cp_ref_loc(ncp + j);
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
}
if (dim == 3)
{
pos_range(2) = cp_ref_loc(2*ncp + k);
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
}
SearchInterval *child_interval =
new SearchInterval(pos_range, curr_depth + 1,
child_node);
pq.push(child_interval);
}
}
}
}
}
delete current;
}
// clean up remaining intervals in queue
while (!pq.empty())
{
delete pq.top();
pq.pop();
}
min_lower_bound = initial_node->GetChildMinLower();
initial_node->DeleteChildren();
delete initial_node;
min_threshold = std::min(min_threshold, min_lower_bound);
return std::make_pair(min_lower_bound, min_upper_bound);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
const int elem, const PLBound &plb, const int vdim,
const int max_depth, const real_t tol) const
{
real_t max_threshold = std::numeric_limits<real_t>::lowest();
return EstimateFunctionMaximum(elem, plb, vdim, max_depth, tol,
max_threshold);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
const int elem, const PLBound &plb, const int vdim,
const int max_depth, const real_t tol, real_t &max_threshold) const
{
const int dim = this->FESpace()->GetMesh()->Dimension();
const int ncp = plb.GetNControlPoints();
Vector pos_range(2*dim); pos_range = 0.0;
for (int d = 0; d < dim; d++) { pos_range(d+dim) = 1.0; }
Vector lower, upper, cp_ref_loc;
GetElementBoundsAtControlPoints(elem, plb, lower, upper, vdim);
real_t val_min = lower.Max();
real_t val_max = upper.Max();
max_threshold = std::max(max_threshold, val_min);
// Pruning: if the element's upper bound is less than the current global
// lower bound, this element cannot contain the global maximum.
if (val_max <= max_threshold)
{
return std::make_pair(val_min, val_max);
}
if (val_min == val_max || max_depth == 0)
{
max_threshold = std::max(max_threshold, val_max);
return std::make_pair(val_min, val_max);
}
real_t abs_tol = tol*(val_max-val_min);
IntervalNode *initial_node = new IntervalNode(val_min, val_max);
SearchInterval *initial_interval = new SearchInterval(pos_range, 0,
initial_node);
std::priority_queue<SearchInterval*,
std::vector<SearchInterval*>, IntervalCompareMax> pq;
pq.push(initial_interval);
real_t max_lower_bound = val_min;
real_t max_upper_bound = val_max;
while (!pq.empty())
{
SearchInterval *current = pq.top();
pq.pop();
int curr_depth = current->depth;
// Reached max depth or this interval cannot contain the global maximum.
if (current->node->val_max <= max_threshold || curr_depth >= max_depth)
{
delete current;
continue;
}
max_upper_bound = initial_node->GetChildMaxUpper();
if (max_upper_bound - max_lower_bound < abs_tol)
{
delete current;
break;
}
// Subdivide the interval and get bounds on it
GetElementBoundsAtControlPoints(elem, plb, current->ref_range,
vdim, lower, upper, cp_ref_loc);
// process the bounds and create sub-intervals
for (int k = 0; k < (dim == 3 ? ncp-1 : 1); k++)
{
for (int j = 0; j < (dim >= 2 ? ncp-1 : 1); j++)
{
for (int i = 0; i < ncp-1; i++)
{
real_t lv = 0.0, uv = 0.0;
if (dim == 1)
{
lv = std::max(lower(i), lower(i+1));
uv = std::max(upper(i), upper(i+1));
}
else if (dim == 2)
{
lv = std::max({lower(i + j*ncp), lower((i+1) + j*ncp),
lower(i + (j+1)*ncp),
lower((i+1) + (j+1)*ncp)});
uv = std::max({upper(i + j*ncp), upper((i+1) + j*ncp),
upper(i + (j+1)*ncp),
upper((i+1) + (j+1)*ncp)});
}
else if (dim == 3)
{
lv = std::max({lower(i + j*ncp + k*ncp*ncp),
lower((i+1) + j*ncp + k*ncp*ncp),
lower(i + (j+1)*ncp + k*ncp*ncp),
lower((i+1) + (j+1)*ncp + k*ncp*ncp),
lower(i + j*ncp + (k+1)*ncp*ncp),
lower((i+1) + j*ncp + (k+1)*ncp*ncp),
lower(i + (j+1)*ncp + (k+1)*ncp*ncp),
lower((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
uv = std::max({upper(i + j*ncp + k*ncp*ncp),
upper((i+1) + j*ncp + k*ncp*ncp),
upper(i + (j+1)*ncp + k*ncp*ncp),
upper((i+1) + (j+1)*ncp + k*ncp*ncp),
upper(i + j*ncp + (k+1)*ncp*ncp),
upper((i+1) + j*ncp + (k+1)*ncp*ncp),
upper(i + (j+1)*ncp + (k+1)*ncp*ncp),
upper((i+1) + (j+1)*ncp + (k+1)*ncp*ncp)});
}
IntervalNode *child_node = new IntervalNode(lv, uv);
current->node->AddChild(child_node);
if (uv > max_threshold)
{
max_lower_bound = std::max(max_lower_bound, lv);
max_threshold = std::max(max_threshold, lv);
if (curr_depth < max_depth)
{
pos_range(0) = cp_ref_loc(i);
pos_range(0+dim) = cp_ref_loc(i+1);
if (dim >= 2)
{
pos_range(1) = cp_ref_loc(ncp + j);
pos_range(1+dim) = cp_ref_loc(ncp + j+1);
}
if (dim == 3)
{
pos_range(2) = cp_ref_loc(2*ncp + k);
pos_range(2+dim) = cp_ref_loc(2*ncp + k+1);
}
SearchInterval *child_interval =
new SearchInterval(pos_range, curr_depth + 1,
child_node);
pq.push(child_interval);
}
}
}
}
}
delete current;
}
// clean up remaining intervals in queue
while (!pq.empty())
{
delete pq.top();
pq.pop();
}
max_upper_bound = initial_node->GetChildMaxUpper();
initial_node->DeleteChildren();
delete initial_node;
max_threshold = std::max(max_threshold, max_upper_bound);
return std::make_pair(max_lower_bound, max_upper_bound);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMinimum(
const int vdim, const PLBound &plb, const int max_depth,
const real_t tol) const
{
real_t global_min_lower = std::numeric_limits<real_t>::max();
real_t global_min_upper = std::numeric_limits<real_t>::max();
for (int i = 0; i < fes->GetNE(); i++)
{
std::pair<real_t, real_t> min_pair =
EstimateFunctionMinimum(i, plb, vdim, max_depth, tol,
global_min_lower);
global_min_upper = std::min(global_min_upper, min_pair.second);
}
return std::make_pair(global_min_lower, global_min_upper);
}
std::pair<real_t, real_t> GridFunction::EstimateFunctionMaximum(
const int vdim, const PLBound &plb, const int max_depth,
const real_t tol) const
{
real_t global_max_lower = std::numeric_limits<real_t>::lowest();
real_t global_max_upper = std::numeric_limits<real_t>::lowest();
for (int i = 0; i < fes->GetNE(); i++)
{
std::pair<real_t, real_t> max_pair =
EstimateFunctionMaximum(i, plb, vdim, max_depth, tol,
global_max_upper);
global_max_lower = std::max(global_max_lower, max_pair.first);
}
return std::make_pair(global_max_lower, global_max_upper);
}
}
+207 -25
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
@@ -532,6 +550,9 @@ public:
std::unique_ptr<GridFunction> ProlongateToMaxOrder() const;
protected:
void ProjectBdrCoefficientNormal(Coefficient *coeff, VectorCoefficient *vcoeff,
const Array<int> &attr);
/** @brief Accumulates (depending on @a type) the values of @a coeff at all
shared vdofs and counts in how many zones each vdof appears. */
void AccumulateAndCountZones(Coefficient &coeff, AvgType type,
@@ -564,6 +585,70 @@ protected:
/// P-refinement version of Update().
void UpdatePRef();
/** @brief Estimate the minimum value of the GridFunction in element @a elem
* if it is below a certain @a min_threshold.
*
* @details For a given element \p elem and grid function component \p vdim
* an estimate of the function minimum is the minimum of the piecewise
* linear lower bound obtained using the given PLBound object. The actual
* minimum is between [minimum lower bound, minimum upper bound]. We
* improve the estimate of the function minimum by recursively
* subdividing the interval with the lowest lower bound, and computing
* bounds on the sub-intervals.
* This process continues until (i) the maximum recursion depth is reached
* or (ii) the difference between the minimum upper bound and minimum lower
* bound is less than a certain tolerance (\p tol * [initial maximum
* upper bound - initial minimum lower bound]).
* The function also terminates if the lowest minima estimate is found
* to be above the given threshold \p min_threshold. This is useful when
* we are interested in computing the global minimum of the function
* over all elements. In this case we can reject elements where the lowest
* bound is above the current global minimum. In case the function
* minimum on the element is below the global minimum, we update
* \p min_threshold.
*
* We return a pair of values that bracket the actual minimum, i.e.
* [min_lower_bound, min_upper_bound].
*/
std::pair<real_t,real_t> EstimateFunctionMinimum(const int elem,
const PLBound &plb,
const int vdim,
const int max_depth,
const real_t tol,
real_t &min_threshold)const;
/** @brief Estimate the maximum value of the GridFunction in element @a elem
* if it is below a certain @a max_threshold.
*
* @details For a given element \p elem and grid function component \p vdim
* an estimate of the function maximum is the maximum of the piecewise
* linear upper bound obtained using the given PLBound object. The actual
* maximum is between [maximum lower bound, maximum upper bound]. We
* improve the estimate of the function maximum by recursively
* subdividing the interval with the highest upper bound, and computing
* bounds on the sub-intervals.
* This process continues until (i) the maximum recursion depth is reached
* or (ii) the difference between the maximum upper bound and maximum lower
* bound is less than a certain tolerance (\p tol * [initial maximum
* upper bound - initial maximum lower bound]).
* The function also terminates if the highest maxima estimate is found
* to be below the given threshold \p max_threshold. This is useful when
* we are interested in computing the global maximum of the function
* over all elements. In this case we can reject elements where the upper
* bound is below the current global maximum. In case the function
* maximum on the element is above the global maximum, we update
* \p max_threshold.
*
* We return a pair of values that bracket the actual maximum, i.e.
* [max_lower_bound, max_upper_bound].
*/
std::pair<real_t,real_t> EstimateFunctionMaximum(const int elem,
const PLBound &plb,
const int vdim,
const int max_depth,
const real_t tol,
real_t &max_threshold)const;
public:
/** @brief For each vdof, counts how many elements contain the vdof,
as containment is determined by FiniteElementSpace::GetElementVDofs(). */
@@ -592,15 +677,26 @@ public:
virtual void ProjectBdrCoefficient(Coefficient *coeff[],
const Array<int> &attr);
/** Project the normal component of the given VectorCoefficient on
the boundary. Only boundary attributes that are marked in
'bdr_attr' are projected. Assumes RT-type VectorFE GridFunction. */
/** @brief Project the normal component of the given VectorCoefficient on
the boundary. */
/** Only boundary attributes that are marked in @a bdr_attr are
projected. Assumes RT-type vector finite element GridFunction. */
void ProjectBdrCoefficientNormal(VectorCoefficient &vcoeff,
const Array<int> &bdr_attr);
const Array<int> &bdr_attr)
{ ProjectBdrCoefficientNormal(NULL, &vcoeff, bdr_attr); }
/** @brief Project the given Coefficient in the normal direction on the
boundary. */
/** Only boundary attributes that are marked in @a bdr_attr are projected.
Assumes RT-type vector finite element GridFunction. */
void ProjectBdrCoefficientNormal(Coefficient &coeff,
const Array<int> &bdr_attr)
{ ProjectBdrCoefficientNormal(&coeff, NULL, bdr_attr); }
/** @brief Project the tangential components of the given VectorCoefficient
on the boundary. Only boundary attributes that are marked in @a bdr_attr
are projected. Assumes ND-type VectorFE GridFunction. */
on the boundary. */
/** Only boundary attributes that are marked in @a bdr_attr
are projected. Assumes ND-type vector finite element GridFunction. */
virtual void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
const Array<int> &bdr_attr);
@@ -1662,21 +1758,21 @@ public:
*/
///@{
/// Computes the \ref PLBound for the gridfunction with number of control
/// points based on @a ref_factor, and returns the overall bounds for each
/// vdim (across all elements) in @b lower and @b upper. We also return the
/// points based on \p ref_factor, and returns the overall bounds for each
/// vdim (across all elements) in \p lower and \p upper. We also return the
/// PLBound object used to compute the bounds.
/// We compute the bounds for each vdim if @a vdim < 1.
/// We compute the bounds for each vdim if \p vdim < 1.
/// Note: For most cases, this method/interface will be sufficient.
virtual PLBound GetBounds(Vector &lower, Vector &upper,
const int ref_factor=1, const int vdim=-1) const;
/// Computes the \ref PLBound for the gridfunction with number of control
/// points based on @a ref_factor, and returns the bounds for each element
/// ordered byVDim:
/// points based on \p ref_factor, and returns the bounds for each element
/// ordered byNodes:
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}. We also return the
/// PLBound object used to compute the bounds.
/// We compute the bounds for each vdim if @a vdim < 1.
/// We compute the bounds for each vdim if \p vdim < 1.
PLBound GetElementBounds(Vector &lower, Vector &upper,
const int ref_factor=1, const int vdim=-1) const;
@@ -1687,6 +1783,18 @@ public:
Vector &lower, Vector &upper,
const int vdim = -1) const;
/** @brief Gets the bounds on given reference range inside an element.
*
* @details @a ref_range is a vector of size 2*dim that specifies the
* lower and upper limits in each dimension of the reference element.
* For example, in 2D, ref_range = [rmin, smin, rmax, smax].
*/
void GetElementBoundsAtControlPoints(const int elem, const PLBound &plb,
const Vector &ref_range,
const int vdim,
Vector &lower, Vector &upper,
Vector &control_pos) const;
/// Compute bounds on the grid function for the given element.
/// The bounds are stored in @b lower and @b upper.
void GetElementBounds(const int elem, const PLBound &plb,
@@ -1694,11 +1802,45 @@ public:
const int vdim = -1) const;
/// Compute bounds on the grid function for all the elements. The bounds
/// are returned in @b lower and @b upper, ordered byVDim:
/// are returned in @b lower and @b upper, ordered byNodes:
/// lower_{0,0}, lower_{1,0}, ..., lower_{ne-1,0},
/// lower_{0,1}, ..., lower_{ne-1,vdim-1}
void GetElementBounds(const PLBound &plb, Vector &lower, Vector &upper,
const int vdim=-1) const;
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
*
* @details See the protected version of EstimateFunctionMinimum for
* details.
*/
std::pair<real_t, real_t> EstimateFunctionMinimum(const int elem,
const PLBound &plb,
const int vdim,
const int max_depth,
const real_t tol) const;
/** @brief Estimate the minimum value of the GridFunction in element @a elem.
*
* @details See the protected version of EstimateFunctionMaximum for
* details.
*/
std::pair<real_t, real_t> EstimateFunctionMaximum(const int elem,
const PLBound &plb,
const int vdim,
const int max_depth,
const real_t tol) const;
/** @brief Estimate the GridFunction minimum across all elements. */
virtual std::pair<real_t,real_t> EstimateFunctionMinimum(const int vdim,
const PLBound &plb,
const int max_depth,
const real_t tol) const;
/** @brief Estimate the GridFunction maximum across all elements. */
virtual std::pair<real_t,real_t> EstimateFunctionMaximum(const int vdim,
const PLBound &plb,
const int max_depth,
const real_t tol) const;
///@}
/// Destroys grid function.
@@ -1804,7 +1946,7 @@ real_t ComputeElementLpDistance(real_t p, int i,
GridFunction& gf1, GridFunction& gf2);
/// Class used for extruding scalar GridFunctions
/// Class used for extruding a scalar coefficient
class ExtrudeCoefficient : public Coefficient
{
private:
@@ -1812,13 +1954,53 @@ private:
Mesh *mesh_in;
Coefficient &sol_in;
public:
/// Constructs an instance of VectorExtrudeCoefficient
/**
* @param m 1D mesh
* @param s 1D vector coefficient
* @param n_ number of transverse elements of the extruded mesh
*/
ExtrudeCoefficient(Mesh *m, Coefficient &s, int n_)
: n(n_), mesh_in(m), sol_in(s) { }
: n(n_), mesh_in(m), sol_in(s)
{ MFEM_VERIFY(n > 0, "Number of transverse elements must be positive!"); }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
virtual ~ExtrudeCoefficient() { }
};
/// Extrude a scalar 1D GridFunction, after extruding the mesh with Extrude1D.
/// Class used for extruding a vector coefficient
class VectorExtrudeCoefficient : public VectorCoefficient
{
private:
int n;
Mesh *mesh_in;
VectorCoefficient &sol_in;
public:
/// Constructs an instance of VectorExtrudeCoefficient
/**
* @param m 1D mesh
* @param s 1D vector coefficient
* @param n_ number of transverse elements of the extruded mesh
*/
VectorExtrudeCoefficient(Mesh *m, VectorCoefficient &s, int n_)
: VectorCoefficient(s.GetVDim()), n(n_), mesh_in(m), sol_in(s)
{ MFEM_VERIFY(n > 0, "Number of transverse elements must be positive!"); }
void Eval(Vector &v, ElementTransformation &T,
const IntegrationPoint &ip) override;
using VectorCoefficient::Eval;
virtual ~VectorExtrudeCoefficient() { }
};
/// Extrude a 1D GridFunction, after extruding the mesh with Extrude1D()
/**
* @param mesh 1D mesh
* @param mesh2d extruded mesh
* @param sol grid function
* @param ny number of transverse elements of the extruded mesh
*/
GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
GridFunction *sol, const int ny);
+6 -5
View File
@@ -490,7 +490,7 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
}
DEV.find_device = true;
const int id = gsl_comm->id, np = gsl_comm->np;
const unsigned int id = gsl_comm->id, np = gsl_comm->np;
gsl_mfem_ref.SetSize(points_cnt * dim);
gsl_mfem_elem.SetSize(points_cnt);
@@ -652,7 +652,7 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
{
const int pp = hash_offset[i];
/* don't send back to where it just came from */
if (pp == p->proc)
if (static_cast<unsigned>(pp) == p->proc)
{
continue;
}
@@ -1068,7 +1068,7 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
sarray_transfer(struct evalOutPt_t, &outpt, proc, 1, cr);
opt = (evalOutPt_t *)outpt.ptr;
for (int index = 0; index < outpt.n; index++)
for (size_t index = 0; index < outpt.n; index++)
{
int idx = ordering == Ordering::byNODES ?
opt->index + i*points_cnt :
@@ -1413,7 +1413,7 @@ void FindPointsGSLIB::SetupSplitMeshesAndIntegrationRules(const int order)
{
MFEM_VERIFY(mesh, "Setup FindPointsGSLIB with mesh first.");
const int dof1D = order+1;
const int dim = mesh->Dimension();
dim = mesh->Dimension();
SetupSplitMeshes();
if (dim == 2)
@@ -2254,7 +2254,8 @@ void FindPointsGSLIB::DistributeInterpolatedValues(const Vector &int_vals,
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
// Store received data
MFEM_VERIFY(outpt->n == points_cnt, "Incompatible size. Number of points "
MFEM_VERIFY(outpt->n == static_cast<size_t>(points_cnt),
"Incompatible size. Number of points "
"received does not match the number of points originally "
"found using FindPoints.");
+6
View File
@@ -202,13 +202,19 @@ protected:
const int dof1dsol, const int ordering);
public:
/// Serial constructor
FindPointsGSLIB();
/// Serial constructor + setup with given Mesh (see \ref Setup)
FindPointsGSLIB(Mesh &mesh_in, const double bb_t = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
#ifdef MFEM_USE_MPI
/// Constructor for ParMesh
FindPointsGSLIB(MPI_Comm comm_);
/// Constructor + setup with given ParMesh (see \ref Setup)
FindPointsGSLIB(ParMesh &mesh_in, const double bb_t = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
+1 -1
View File
@@ -254,7 +254,7 @@ get_edge(const double *elx[2], const double *wtend, int ei,
edge.dxdn[d] = workspace + (2 + d) * pN; //dxdn and dydn at DOFs along edge
}
if (side_init != (1u << ei))
if (static_cast<unsigned>(side_init) != (1u << ei))
{
#define ELX(d, j, k) elx[d][j + k * pN] // assumes lexicographic ordering
for (int d = 0; d < 2; ++d)
+2 -2
View File
@@ -294,7 +294,7 @@ get_face(const double *elx[3], const double *wtend, int fi, double *workspace,
face.dxdn[d] = workspace+(3+d)*p_Nfr;
}
if (side_init != (1u << fi))
if (static_cast<unsigned>(side_init) != (1u << fi))
{
const int e_stride[3] = {1, pN, pN*pN};
#define ELX(d, j, k, l) elx[d][j*e_stride[d1]+k*e_stride[d2]+l*e_stride[dn]]
@@ -342,7 +342,7 @@ get_edge(const double *elx[3], const double *wtend, int ei, double *workspace,
if (jidx >= 3*pN) { return edge; }
if (side_init != (64u << ei))
if (static_cast<unsigned>(side_init) != (64u << ei))
{
const int e_stride[3] = {1, pN, pN*pN};
#define ELX(d, j, k, l) elx[d][j*e_stride[de]+k*e_stride[dn1]+l*e_stride[dn2]]
+18 -8
View File
@@ -1064,6 +1064,8 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
// Grad X
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,D1D)
@@ -1084,6 +1086,8 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
// Grad Y
MFEM_FOREACH_THREAD_DIRECT(dz,z,D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
@@ -1105,6 +1109,8 @@ inline void SmemPADiffusionApply3D(const int NE,
}
}
MFEM_SYNC_THREAD;
// Grad Z + Q-function
MFEM_FOREACH_THREAD_DIRECT(qz,z,Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,Q1D)
@@ -1217,20 +1223,23 @@ inline void SmemPADiffusionApply3D(const int NE,
namespace
{
using ApplyKernelType = DiffusionIntegrator::ApplyKernelType;
using DiagonalKernelType = DiffusionIntegrator::DiagonalKernelType;
using DiffusionApplyKernelType =
DiffusionIntegrator::DiffusionApplyKernelType;
using DiffusionDiagonalKernelType =
DiffusionIntegrator::DiffusionDiagonalKernelType;
}
template<int DIM, int T_D1D, int T_Q1D>
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Kernel()
DiffusionApplyKernelType DiffusionIntegrator::DiffusionApplyPAKernel::Kernel()
{
if constexpr (DIM == 2) { return internal::SmemPADiffusionApply2D<T_D1D,T_Q1D>; }
else if constexpr (DIM == 3) { return internal::SmemPADiffusionApply3D<T_D1D, T_Q1D>; }
MFEM_ABORT("");
}
inline
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Fallback(int DIM, int, int)
inline DiffusionApplyKernelType
DiffusionIntegrator::DiffusionApplyPAKernel::Fallback(int DIM, int, int)
{
if (DIM == 2) { return internal::PADiffusionApply2D; }
else if (DIM == 3) { return internal::PADiffusionApply3D; }
@@ -1238,15 +1247,16 @@ ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Fallback(int DIM, int, int)
}
template<int DIM, int D1D, int Q1D>
DiagonalKernelType DiffusionIntegrator::DiagonalPAKernels::Kernel()
DiffusionDiagonalKernelType
DiffusionIntegrator::DiffusionDiagonalPAKernel::Kernel()
{
if constexpr (DIM == 2) { return internal::SmemPADiffusionDiagonal2D<D1D,Q1D>; }
else if constexpr (DIM == 3) { return internal::SmemPADiffusionDiagonal3D<D1D, Q1D>; }
MFEM_ABORT("");
}
inline DiagonalKernelType
DiffusionIntegrator::DiagonalPAKernels::Fallback(int DIM, int, int)
inline DiffusionDiagonalKernelType
DiffusionIntegrator::DiffusionDiagonalPAKernel::Fallback(int DIM, int, int)
{
if (DIM == 2) { return internal::PADiffusionDiagonal2D; }
else if (DIM == 3) { return internal::PADiffusionDiagonal3D; }
+7 -7
View File
@@ -31,8 +31,8 @@ void DiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
const Array<real_t> &B = maps->B;
const Array<real_t> &G = maps->G;
const Vector &Dv = pa_data;
DiagonalPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Dv,
diag, dofs1D, quad1D);
DiffusionDiagonalPAKernel::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Dv,
diag, dofs1D, quad1D);
}
}
@@ -68,8 +68,8 @@ void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
#endif // MFEM_USE_OCCA
ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Bt,
Gt, Dv, x, y, dofs1D, quad1D);
DiffusionApplyPAKernel::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Bt,
Gt, Dv, x, y, dofs1D, quad1D);
}
}
@@ -174,9 +174,9 @@ void DiffusionIntegrator::AddAbsMultPA(const Vector &x, Vector &y) const
abs_pa_data.Abs();
auto abs_maps = maps->Abs();
ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric,
abs_maps.B, abs_maps.G, abs_maps.Bt, abs_maps.Gt,
abs_pa_data, x, y, dofs1D, quad1D);
DiffusionApplyPAKernel::Run(dim, dofs1D, quad1D, ne, symmetric,
abs_maps.B, abs_maps.G, abs_maps.Bt, abs_maps.Gt,
abs_pa_data, x, y, dofs1D, quad1D);
}
void DiffusionIntegrator::AddAbsMultTransposePA(const Vector &x,
+18 -8
View File
@@ -197,15 +197,21 @@ static void EAHdivAssemble3D(const int NE,
// Assemble (one row per thread)
MFEM_FOREACH_THREAD(idx_i, x, NDOF)
{
// NOTE: due to an llvm backend bug, usage of the modulus operator
// has been removed from this foreach section.
const int ic = idx_i / NDOF_C;
const int idx_ii = idx_i % NDOF_C;
const int idx_ii = idx_i - ic * NDOF_C; // idx_i % NDOF_C
const int nx_i = (ic == 0) ? D1D : D1D-1;
const int ny_i = (ic == 1) ? D1D : D1D-1;
const int ix = idx_ii % nx_i;
const int iy = (idx_ii / nx_i) % ny_i;
const int iz = (idx_ii / nx_i) / ny_i;
const int qx_i = idx_ii / nx_i;
const int ix = idx_ii - qx_i * nx_i; // idx_ii % nx_i
const int qy_i = qx_i / ny_i;
const int iy = qx_i - qy_i * ny_i; // (idx_ii / nx_i) % ny_i
const int iz = qy_i; // (idx_ii / nx_i) / ny_i
const real_t (&Bi1)[MQ1][MD1] = (ic == 0) ? r_Bc : r_Bo;
const real_t (&Bi2)[MQ1][MD1] = (ic == 1) ? r_Bc : r_Bo;
@@ -214,14 +220,18 @@ static void EAHdivAssemble3D(const int NE,
for (int idx_j = 0; idx_j < NDOF; ++idx_j)
{
const int jc = idx_j / NDOF_C;
const int idx_jj = idx_j % NDOF_C;
const int idx_jj = idx_j - jc * NDOF_C; // idx_j % NDOF_C
const int nx_j = (jc == 0) ? D1D : D1D-1;
const int ny_j = (jc == 1) ? D1D : D1D-1;
const int jx = idx_jj % nx_j;
const int jy = (idx_jj / nx_j) % ny_j;
const int jz = (idx_jj / nx_j) / ny_j;
const int qx_j = idx_jj / nx_j;
const int jx = idx_jj - qx_j * nx_j; // idx_jj % nx_j
const int qy_j = qx_j / ny_j;
const int jy = qx_j - qy_j * ny_j; // (idx_jj / nx_j) % ny_j
const int jz = qy_j; // (idx_jj / nx_j) / ny_j
const real_t (&Bj1)[MQ1][MD1] = (jc == 0) ? r_Bc : r_Bo;
const real_t (&Bj2)[MQ1][MD1] = (jc == 1) ? r_Bc : r_Bo;
+3 -3
View File
@@ -171,15 +171,15 @@ template<int DIM, int T_SDIM, int T_D1D, int T_Q1D>
VectorDiffusionIntegrator::ApplyKernelType
VectorDiffusionIntegrator::ApplyPAKernels::Kernel()
{
if (DIM == 2)
if constexpr (DIM == 2)
{
return internal::SmemPAVectorDiffusionApply2D<T_SDIM, T_D1D, T_Q1D>;
}
else if (DIM == 3)
else if constexpr (DIM == 3)
{
return internal::SmemPAVectorDiffusionApply3D<T_SDIM, T_D1D, T_Q1D>;
}
else { MFEM_ABORT("Unsupported kernel"); }
MFEM_ABORT("Unsupported kernel");
}
inline VectorDiffusionIntegrator::ApplyKernelType
+3 -3
View File
@@ -182,15 +182,15 @@ template<int DIM, int T_D1D, int T_Q1D>
VectorMassIntegrator::VectorMassAddMultPAType
VectorMassIntegrator::VectorMassAddMultPA::Kernel()
{
if (DIM == 2)
if constexpr (DIM == 2)
{
return internal::SmemPAVectorMassApply2D<T_D1D,T_Q1D>;
}
else if (DIM == 3)
else if constexpr (DIM == 3)
{
return internal::SmemPAVectorMassApply3D<T_D1D, T_Q1D>;
}
else { MFEM_ABORT("Unsupported kernel"); }
MFEM_ABORT("Unsupported kernel");
}
inline VectorMassIntegrator::VectorMassAddMultPAType
+6 -10
View File
@@ -301,18 +301,14 @@ template <int DIM, int T_D1D, int T_Q1D>
DomainLFIntegrator::AssembleKernelType
DomainLFIntegrator::AssembleKernels::Kernel()
{
switch (DIM)
{
case 1:
return DLFEvalAssemble1D<T_D1D, T_Q1D>;
case 2:
return DLFEvalAssemble2D<T_D1D, T_Q1D>;
case 3:
return DLFEvalAssemble3D<T_D1D, T_Q1D>;
}
if constexpr (DIM == 1) { return DLFEvalAssemble1D<T_D1D, T_Q1D>; }
if constexpr (DIM == 2) { return DLFEvalAssemble2D<T_D1D, T_Q1D>; }
if constexpr (DIM == 3) { return DLFEvalAssemble3D<T_D1D, T_Q1D>; }
MFEM_ABORT("");
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
#endif
#endif // MFEM_LININTEG_DOMAIN_KERNELS_HPP
+811 -327
View File
File diff suppressed because it is too large Load Diff
+63 -64
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.
@@ -125,18 +121,6 @@ private:
void AddTriPoints3b(const int off, const real_t b, const real_t weight)
{ AddTriPoints3(off, (1. - b)/2., b, weight); }
void AddTriPoints3R(const int off, const real_t a, const real_t b,
const real_t c, const real_t weight)
{
IntPoint(off + 0).Set2w(a, b, weight);
IntPoint(off + 1).Set2w(c, a, weight);
IntPoint(off + 2).Set2w(b, c, weight);
}
void AddTriPoints3R(const int off, const real_t a, const real_t b,
const real_t weight)
{ AddTriPoints3R(off, a, b, 1. - a - b, weight); }
void AddTriPoints6(const int off, const real_t a, const real_t b,
const real_t c, const real_t weight)
{
@@ -183,14 +167,6 @@ private:
AddTetPoints3(off + 1, a, 1. - 3.*a, weight);
}
// given b, add the permutations of (a,a,a,b), where 3*a + b = 1
void AddTetPoints4b(const int off, const real_t b, const real_t weight)
{
const real_t a = (1. - b)/3.;
IntPoint(off).Set(a, a, a, weight);
AddTetPoints3(off + 1, a, b, weight);
}
// add the permutations of (a,a,b,b), 2*(a + b) = 1
void AddTetPoints6(const int off, const real_t a, const real_t weight)
{
@@ -209,14 +185,37 @@ private:
AddTetPoints6(off + 6, a, bc, cb, weight);
}
// given (b,c), add the permutations of (a,a,b,c), 2*a + b + c = 1
void AddTetPoints12bc(const int off, const real_t b, const real_t c,
const real_t weight)
// add all 24 permutations of (a,b,c,d) where a+b+c+d = 1, all distinct
void AddTetPoints24(const int off, const real_t a, const real_t b,
const real_t c, const real_t weight)
{
const real_t a = (1. - b - c)/2.;
AddTetPoints3(off, a, b, weight);
AddTetPoints3(off + 3, a, c, weight);
AddTetPoints6(off + 6, a, b, c, weight);
const real_t d = 1. - a - b - c;
// all 24 permutations of 4 distinct barycentric coordinates
// permuting which coordinate goes to x, y, z (4th is 1-x-y-z)
IntPoint(off + 0).Set(a, b, c, weight);
IntPoint(off + 1).Set(a, b, d, weight);
IntPoint(off + 2).Set(a, c, b, weight);
IntPoint(off + 3).Set(a, c, d, weight);
IntPoint(off + 4).Set(a, d, b, weight);
IntPoint(off + 5).Set(a, d, c, weight);
IntPoint(off + 6).Set(b, a, c, weight);
IntPoint(off + 7).Set(b, a, d, weight);
IntPoint(off + 8).Set(b, c, a, weight);
IntPoint(off + 9).Set(b, c, d, weight);
IntPoint(off + 10).Set(b, d, a, weight);
IntPoint(off + 11).Set(b, d, c, weight);
IntPoint(off + 12).Set(c, a, b, weight);
IntPoint(off + 13).Set(c, a, d, weight);
IntPoint(off + 14).Set(c, b, a, weight);
IntPoint(off + 15).Set(c, b, d, weight);
IntPoint(off + 16).Set(c, d, a, weight);
IntPoint(off + 17).Set(c, d, b, weight);
IntPoint(off + 18).Set(d, a, b, weight);
IntPoint(off + 19).Set(d, a, c, weight);
IntPoint(off + 20).Set(d, b, a, weight);
IntPoint(off + 21).Set(d, b, c, weight);
IntPoint(off + 22).Set(d, c, a, weight);
IntPoint(off + 23).Set(d, c, b, weight);
}
public:
+44
View File
@@ -207,6 +207,28 @@ inline MFEM_HOST_DEVICE void WriteDofs2d(const int e, const int d1d,
MFEM_SYNC_THREAD;
}
/// Load 3D input DIM vector at element offset into given register tensor
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void LoadDofs3d(const int d1d, const int c,
const DeviceTensor<4, const real_t> &X,
vd_regs3d_t<VDIM, DIM, MQ1> &Y)
{
for (int d = 0; d < DIM; d++)
{
for (int dz = 0; dz < d1d; ++dz)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
Y[c][d][dz][dy][dx] = X(dx, dy, dz, c);
}
}
}
}
MFEM_SYNC_THREAD;
}
/// Load 3D input VDIM*DIM vector into given register tensor, specific component
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void LoadDofs3d(const int e, const int d1d, const int c,
@@ -332,6 +354,28 @@ inline MFEM_HOST_DEVICE void WriteDofs3d(const int e, const int d1d,
MFEM_SYNC_THREAD;
}
/// Write 3D DIM vector into given device tensor for specific component
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void WriteDofs3d(const int d1d, const int c,
vd_regs3d_t<VDIM, DIM, MQ1> &X,
DeviceTensor<4, real_t> &Y)
{
for (int dz = 0; dz < d1d; ++dz)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
for (int d = 0; d < DIM; ++d)
{
Y(dx, dy, dz, c) += X(c, d, dz, dy, dx);
}
}
}
}
MFEM_SYNC_THREAD;
}
/// 2D scalar contraction, X direction
template <bool Transpose, int MQ1>
inline MFEM_HOST_DEVICE void ContractX2d(const int d1d, const int q1d,
+332
View File
@@ -0,0 +1,332 @@
// 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.
#pragma once
#include "../config/config.hpp"
#include "../general/forall.hpp"
#include "../linalg/dtensor.hpp"
#include "kernels.hpp" // IWYU pragma: keep
namespace mfem::kernels::internal::low
{
#if ((defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)) || \
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
template <int DIM, int N>
// struct regs3d_device_wrapper: mfem::future::tensor<real_t, DIM, 0, 0, 0> {};
struct regs3d_device_wrapper: mfem::future::tensor<real_t, 0, 0, 0, DIM> {};
template <int DIM, int N>
using regs3d_t = regs3d_device_wrapper<DIM, N>;
#else
template <int DIM, int N>
using regs3d_t = mfem::future::tensor<real_t, N, N, N, DIM>;
// using regs3d_t = mfem::future::tensor<real_t, DIM, N, N, N>;
#endif
///////////////////////////////////////////////////////////////////////////////
/// Load 2D matrix into shared memory
template <int MQ1>
inline MFEM_HOST_DEVICE void LoadMatrix(const int d1d, const int q1d,
const real_t *M, real_t (*N)[MQ1])
{
if (MFEM_THREAD_ID(z) == 0)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
N[dy][qx] = M[dy * q1d + qx];
}
}
}
}
///////////////////////////////////////////////////////////////////////////////
template <int DIM, int MQ1>
inline MFEM_HOST_DEVICE void LoadDofs3d(const int e, const int d1d,
const DeviceTensor<5, const real_t> &XE,
real_t (&sm0)[MQ1][MQ1][MQ1][DIM])
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx,x,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dz,z,d1d)
{
sm0[dz][dy][dx][0] = XE(dx, dy, dz, 0, e);
}
}
}
MFEM_SYNC_THREAD;
}
///////////////////////////////////////////////////////////////////////////////
/// 3D Scalar Gradient, 1/3
template<int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradX(const int d1d, const int q1d,
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
const real_t (&sm0)[MQ1][MQ1][MQ1][DIM],
real_t (&sm1)[MQ1][MQ1][MQ1][DIM])
{
MFEM_FOREACH_THREAD_DIRECT(dz,z,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,q1d)
{
real_t u = 0.0, v = 0.0;
MFEM_UNROLL(MQ1)
for (int dx = 0; dx < d1d; ++dx)
{
const auto x = sm0[dz][dy][dx][0];
u = std::fma(B[dx][qx], x, u);
v = std::fma(G[dx][qx], x, v);
}
sm1[dz][dy][qx][0] = u;
sm1[dz][dy][qx][1] = v;
}
}
}
MFEM_SYNC_THREAD;
}
///////////////////////////////////////////////////////////////////////////////
/// 3D Scalar Gradient, 2/3
template<int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradY(const int d1d, const int q1d,
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
const real_t (&sm1)[MQ1][MQ1][MQ1][DIM],
real_t (&sm0)[MQ1][MQ1][MQ1][DIM])
{
MFEM_FOREACH_THREAD_DIRECT(dz,z,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,q1d)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
for (int dy = 0; dy < d1d; ++dy)
{
u = std::fma(sm1[dz][dy][qx][1], B[dy][qy], u);
v = std::fma(sm1[dz][dy][qx][0], G[dy][qy], v);
w = std::fma(sm1[dz][dy][qx][0], B[dy][qy], w);
}
sm0[dz][qy][qx][0] = u;
sm0[dz][qy][qx][1] = v;
sm0[dz][qy][qx][2] = w;
}
}
}
MFEM_SYNC_THREAD;
}
///////////////////////////////////////////////////////////////////////////////
/// 3D Scalar Gradient, 3/3
template<int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradZ(const int d1d, const int q1d,
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
const real_t (&sm0)[MQ1][MQ1][MQ1][DIM],
regs3d_t<DIM,MQ1> &reg)
{
MFEM_FOREACH_THREAD_DIRECT(qz,z,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,q1d)
{
real_t u[3] = {0.0, 0.0, 0.0};
MFEM_UNROLL(MQ1)
for (int dz = 0; dz < d1d; ++dz)
{
u[0] = std::fma(B[dz][qz], sm0[dz][qy][qx][0], u[0]);
u[1] = std::fma(B[dz][qz], sm0[dz][qy][qx][1], u[1]);
u[2] = std::fma(G[dz][qz], sm0[dz][qy][qx][2], u[2]);
}
reg[qz][qy][qx][0] = u[0];
reg[qz][qy][qx][1] = u[1];
reg[qz][qy][qx][2] = u[2];
}
}
}
MFEM_SYNC_THREAD;
}
///////////////////////////////////////////////////////////////////////////////
/// 3D scalar gradient
template <int DIM, int MQ1>
inline MFEM_HOST_DEVICE void Grad3d(const int d1d, const int q1d,
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
real_t (&sm0)[MQ1][MQ1][MQ1][DIM],
real_t (&sm1)[MQ1][MQ1][MQ1][DIM],
regs3d_t<DIM,MQ1> &reg)
{
GradX(d1d, q1d, B, G, sm0, sm1); // Grad X
GradY(d1d, q1d, B, G, sm1, sm0); // Grad Y
GradZ(d1d, q1d, B, G, sm0, reg); // Grad Z
}
///////////////////////////////////////////////////////////////////////////////
/// 3D Scalar Gradient Transposed, 1/3
template<int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradTranspose3dX(const int d1d, const int q1d,
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
regs3d_t<DIM,MQ1> &reg,
real_t (&sm1)[MQ1][MQ1][MQ1][DIM],
real_t (&sm0)[MQ1][MQ1][MQ1][DIM])
{
MFEM_FOREACH_THREAD_DIRECT(qz,z,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qx,x,q1d)
{
sm1[qz][qy][qx][0] = reg[qz][qy][qx][0];
sm1[qz][qy][qx][1] = reg[qz][qy][qx][1];
sm1[qz][qy][qx][2] = reg[qz][qy][qx][2];
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(qz,z,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy,y,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx,x,d1d)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
for (int qx = 0; qx < q1d; ++qx)
{
u = std::fma(sm1[qz][qy][qx][0], G[dx][qx], u);
v = std::fma(sm1[qz][qy][qx][1], B[dx][qx], v);
w = std::fma(sm1[qz][qy][qx][2], B[dx][qx], w);
}
sm0[qz][qy][dx][0] = u;
sm0[qz][qy][dx][1] = v;
sm0[qz][qy][dx][2] = w;
}
}
}
MFEM_SYNC_THREAD;
}
///////////////////////////////////////////////////////////////////////////////
/// 3D Scalar Gradient Transposed, 2/3
template<int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradTranspose3dY(const int d1d, const int q1d,
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
real_t (&sm0)[MQ1][MQ1][MQ1][DIM],
real_t (&sm1)[MQ1][MQ1][MQ1][DIM])
{
MFEM_FOREACH_THREAD_DIRECT(qz,z,q1d)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx,x,d1d)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
for (int qy = 0; qy < q1d; ++qy)
{
u = std::fma(sm0[qz][qy][dx][0], B[dy][qy], u);
v = std::fma(sm0[qz][qy][dx][1], G[dy][qy], v);
w = std::fma(sm0[qz][qy][dx][2], B[dy][qy], w);
}
sm1[qz][dy][dx][0] = u;
sm1[qz][dy][dx][1] = v;
sm1[qz][dy][dx][2] = w;
}
}
}
MFEM_SYNC_THREAD;
}
///////////////////////////////////////////////////////////////////////////////
/// 3D Scalar Gradient Transposed, 3/3
template<int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradTranspose3dZ(const int d1d, const int q1d,
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
real_t (&sm1)[MQ1][MQ1][MQ1][DIM],
regs3d_t<DIM,MQ1> &reg)
{
MFEM_FOREACH_THREAD_DIRECT(dz,z,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx,x,d1d)
{
real_t u = 0.0, v = 0.0, w = 0.0;
MFEM_UNROLL(MQ1)
for (int qz = 0; qz < q1d; ++qz)
{
u = std::fma(sm1[qz][dy][dx][0], B[dz][qz], u);
v = std::fma(sm1[qz][dy][dx][1], B[dz][qz], v);
w = std::fma(sm1[qz][dy][dx][2], G[dz][qz], w);
}
reg[dz][dy][dx][0] = u;
reg[dz][dy][dx][1] = v;
reg[dz][dy][dx][2] = w;
}
}
}
MFEM_SYNC_THREAD;
}
///////////////////////////////////////////////////////////////////////////////
/// 3D scalar gradient transposed
template <int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradTranspose3d(const int d1d, const int q1d,
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
regs3d_t<DIM,MQ1> &reg,
real_t (&sm1)[MQ1][MQ1][MQ1][DIM],
real_t (&sm0)[MQ1][MQ1][MQ1][DIM])
{
GradTranspose3dX(d1d, q1d, B, G, reg, sm1, sm0); // Grad^T X
GradTranspose3dY(d1d, q1d, B, G, sm0, sm1); // Grad^T Y
GradTranspose3dZ(d1d, q1d, B, G, sm1, reg); // Grad^T Z
}
///////////////////////////////////////////////////////////////////////////////
/// 3D Scalar Gradient Transposed, 3/3
template<int DIM, int MQ1>
inline MFEM_HOST_DEVICE void WriteDofs3d(const int d1d,
const int c, const int e,
regs3d_t<DIM,MQ1> &reg,
const DeviceTensor<5, real_t> &YE)
{
MFEM_FOREACH_THREAD_DIRECT(dz,z,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dy,y,d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx,x,d1d)
{
const real_t u = reg[dz][dy][dx][0];
const real_t v = reg[dz][dy][dx][1];
const real_t w = reg[dz][dy][dx][2];
YE(dx, dy, dz, c, e) += (u + v + w);
}
}
}
}
} // namespace mfem::kernels::internal
+3 -1
View File
@@ -297,7 +297,8 @@ void LinearForm::Assemble()
tr = mesh->GetBdrFaceTransformations(i);
if (tr != NULL)
{
fes -> GetElementVDofs (tr -> Elem1No, vdofs);
mfem::DofTransformation doftrans;
fes -> GetElementVDofs (tr -> Elem1No, vdofs, doftrans);
for (int k = 0; k < boundary_face_integs.Size(); k++)
{
if (boundary_face_integs_marker[k] &&
@@ -307,6 +308,7 @@ void LinearForm::Assemble()
boundary_face_integs[k]->
AssembleRHSElementVect(*fes->GetFE(tr->Elem1No),
*tr, elemvect);
doftrans.TransformDual(elemvect);
AddElementVector (vdofs, elemvect);
}
}
+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; }
+6 -5
View File
@@ -158,15 +158,16 @@ void LORBase::ConstructLocalDofPermutation(Array<int> &perm_) const
int i;
i = dofmap_lor[off_lor + i1 + i2*2];
int s1 = i < 0 ? -1 : 1;
int idof_lor = vdof_lor[absdof(i)];
int idof_lor = vdof_lor[UnsignIndex(i)];
i = dofmap_ho[off_ho + i1*n1 + i2*n2];
int s2 = i < 0 ? -1 : 1;
int idof_ho = vdof_ho[absdof(i)];
int idof_ho = vdof_ho[UnsignIndex(i)];
int s3 = idof_lor < 0 ? -1 : 1;
int s4 = idof_ho < 0 ? -1 : 1;
int s = s1*s2*s3*s4;
i = absdof(idof_ho);
perm_[absdof(idof_lor)] = s < 0 ? -1-absdof(i) : absdof(i);
i = UnsignIndex(idof_ho);
perm_[UnsignIndex(idof_lor)] = s < 0 ? -1-UnsignIndex(i) :
UnsignIndex(i);
}
}
};
@@ -232,7 +233,7 @@ void LORBase::ConstructDofPermutation() const
int j = l_perm[i];
int s = j < 0 ? -1 : 1;
int t_i = pfes_lor->GetLocalTDofNumber(i);
int t_j = pfes_ho->GetLocalTDofNumber(absdof(j));
int t_j = pfes_ho->GetLocalTDofNumber(UnsignIndex(j));
// Either t_i and t_j both -1, or both non-negative
if ((t_i < 0 && t_j >=0) || (t_j < 0 && t_i >= 0))
{
-2
View File
@@ -57,8 +57,6 @@ private:
/// values (after temporarily changing them for LOR assembly).
void ResetIntegrationRules(GetIntegratorsFn get_integrators);
static inline int absdof(int i) { return i < 0 ? -1-i : i; }
protected:
enum FESpaceType { H1, ND, RT, L2, INVALID };
+3
View File
@@ -224,6 +224,9 @@ public:
/** @see GetGradient(const Vector &) */
Operator &GetGradient(const Vector &x, bool finalize) const;
/// Suppress a warning about hiding overloaded virtual function.
using Operator::GetGradient;
/// Update the NonlinearForm to propagate updates of the associated FE space.
/** After calling this method, the essential boundary conditions need to be
set again. */
+41 -47
View File
@@ -424,7 +424,7 @@ void ParFiniteElementSpace::GetGroupComm(
{
if (ind[l] < 0)
{
dofs[l] = m + (-1-ind[l]);
dofs[l] = m + FlipIndexSign(ind[l]);
if (g_ldof_sign)
{
(*g_ldof_sign)[dofs[l]] = -1;
@@ -462,7 +462,7 @@ void ParFiniteElementSpace::GetGroupComm(
{
if (ind[l] < 0)
{
dofs[l] = m + (-1-ind[l]);
dofs[l] = m + FlipIndexSign(ind[l]);
if (g_ldof_sign)
{
(*g_ldof_sign)[dofs[l]] = -1;
@@ -500,7 +500,7 @@ void ParFiniteElementSpace::GetGroupComm(
{
if (ind[l] < 0)
{
dofs[l] = m + (-1-ind[l]);
dofs[l] = m + FlipIndexSign(ind[l]);
if (g_ldof_sign)
{
(*g_ldof_sign)[dofs[l]] = -1;
@@ -538,16 +538,16 @@ void ParFiniteElementSpace::ApplyLDofSigns(Array<int> &dofs) const
{
if (dofs[i] < 0)
{
if (ldof_sign[-1-dofs[i]] < 0)
if (ldof_sign[FlipIndexSign(dofs[i])] < 0)
{
dofs[i] = -1-dofs[i];
dofs[i] = FlipIndexSign(dofs[i]);
}
}
else
{
if (ldof_sign[dofs[i]] < 0)
{
dofs[i] = -1-dofs[i];
dofs[i] = FlipIndexSign(dofs[i]);
}
}
}
@@ -699,7 +699,8 @@ void ParFiniteElementSpace::GetSharedEdgeDofs(
for (int i = 0; i < dofs.Size(); i++)
{
const int di = dofs[i];
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
dofs[i] = di >= 0 ? rdofs[di] :
FlipIndexSign(rdofs[FlipIndexSign(di)]);
}
}
}
@@ -723,7 +724,8 @@ void ParFiniteElementSpace::GetSharedTriangleDofs(
for (int i = 0; i < dofs.Size(); i++)
{
const int di = dofs[i];
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
dofs[i] = di >= 0 ? rdofs[di] :
FlipIndexSign(rdofs[FlipIndexSign(di)]);
}
}
}
@@ -747,7 +749,8 @@ void ParFiniteElementSpace::GetSharedQuadrilateralDofs(
for (int i = 0; i < dofs.Size(); i++)
{
const int di = dofs[i];
dofs[i] = (di >= 0) ? rdofs[di] : -1-rdofs[-1-di];
dofs[i] = (di >= 0) ? rdofs[di] :
FlipIndexSign(rdofs[FlipIndexSign(di)]);
}
}
}
@@ -1487,7 +1490,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
GetElementVDofs(my_elems[i], ldofs);
for (int j = 0; j < ldofs.Size(); j++)
{
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
int ldof = UnsignIndex(ldofs[j]);
if (ldof_marker[ldof] != fn)
{
@@ -1548,7 +1551,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
GetElementVDofs(my_elems[i], ldofs);
for (int j = 0; j < ldofs.Size(); j++)
{
int ldof = (ldofs[j] >= 0 ? ldofs[j] : -1-ldofs[j]);
int ldof = UnsignIndex(ldofs[j]);
if (ldof_marker[ldof] != fn)
{
@@ -1573,14 +1576,15 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
for (int i = 0; i < num_ldofs; i++)
{
int ldof = (ldofs_fn[i] >= 0 ? ldofs_fn[i] : -1-ldofs_fn[i]);
int ldof = UnsignIndex(ldofs_fn[i]);
ldof_marker[ldof] = i;
}
for ( ; j < j_end; j++)
{
int ldof = (send_J[j] >= 0 ? send_J[j] : -1-send_J[j]);
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] : -1-ldof_marker[ldof]);
const int ldof = UnsignIndex(send_J[j]);
send_J[j] = (send_J[j] >= 0 ? ldof_marker[ldof] :
FlipIndexSign(ldof_marker[ldof]));
}
}
@@ -1672,12 +1676,7 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
{
for (int j_end = face_nbr_ldof.GetI()[fn+1]; j < j_end; j++)
{
int ldof = face_nbr_ldof.GetJ()[j];
if (ldof < 0)
{
ldof = -1-ldof;
}
const int ldof = UnsignIndex(face_nbr_ldof.GetJ()[j]);
face_nbr_glob_dof_map[j] = dof_face_nbr_offsets[fn] + ldof;
}
}
@@ -1721,7 +1720,7 @@ void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
MFEM_ASSERT(Nonconforming() && i >= pmesh->GetNumFaces(), "");
int el1, el2, inf1, inf2;
pmesh->GetFaceElements(i, &el1, &el2);
el2 = -1 - el2;
el2 = FlipIndexSign(el2);
pmesh->GetFaceInfos(i, &inf1, &inf2);
MFEM_ASSERT(0 <= el2 && el2 < face_nbr_element_dof.Size(), "");
const int nd = face_nbr_element_dof.RowSize(el2);
@@ -1737,7 +1736,8 @@ void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
for (int j = 0; j < vdofs.Size(); j++)
{
const int ldof = vdofs[j];
vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] : -1-vol_vdofs[-1-ldof];
vdofs[j] = (ldof >= 0) ? vol_vdofs[ldof] :
FlipIndexSign(vol_vdofs[FlipIndexSign(ldof)]);
}
}
@@ -2061,8 +2061,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
for (int j = 0; j < ne; j++)
{
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
/* */ : (-1 - (first + (-1 - ind[j])));
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
FlipIndexSign(first + FlipIndexSign(ind[j]));
}
}
else
@@ -2072,8 +2072,8 @@ void ParFiniteElementSpace::GetGhostFaceDofs(const MeshId &face_id,
const int *ind = fec->DofOrderForOrientation(Geometry::SEGMENT, Eo[i]);
for (int j = 0; j < ne; j++)
{
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j])
/* */ : (-1 - (first + (-1 - ind[j])));
dofs[offset++] = (ind[j] >= 0) ? (first + ind[j]) :
FlipIndexSign(first + FlipIndexSign(ind[j]));
}
}
}
@@ -2866,7 +2866,7 @@ void NeighborRowMessage::Encode(int rank)
if (ind && (edof = ind[edof]) < 0)
{
edof = -1 - edof;
edof = FlipIndexSign(edof);
s = -1;
}
@@ -3067,10 +3067,10 @@ void NeighborRowMessage::Decode(int rank)
// If edof arrived with a negative index, flip it, and the scaling.
real_t s = (edof < 0) ? -1.0 : 1.0;
edof = (edof < 0) ? -1 - edof : edof;
edof = UnsignIndex(edof);
if (ind && (edof = ind[edof]) < 0)
{
edof = -1 - edof;
edof = FlipIndexSign(edof);
s *= -1.0;
}
@@ -3121,10 +3121,10 @@ void NeighborRowMessage::Decode(int rank)
// If edof arrived with a negative index, flip it, and the scaling.
s = (edof < 0) ? -1.0 : 1.0;
edof = (edof < 0) ? -1 - edof : edof;
edof = UnsignIndex(edof);
if (ind && (edof = ind[edof]) < 0)
{
edof = -1 - edof;
edof = FlipIndexSign(edof);
s *= -1.0;
}
@@ -4405,12 +4405,9 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
{
for (int j = 0; j < dofs.Size(); j++)
{
int row = DofToVDof(dofs[j], vd);
if (row < 0) { row = -1 - row; }
int col = DofToVDof(old_dofs[j], vd, old_ndofs);
if (col < 0) { col = -1 - col; }
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
const int col = UnsignIndex(DofToVDof(old_dofs[j], vd,
old_ndofs));
i_diag[row] = col;
}
}
@@ -4435,9 +4432,7 @@ ParFiniteElementSpace::RebalanceMatrix(int old_ndofs,
{
for (int j = 0; j < dofs.Size(); j++)
{
int row = DofToVDof(dofs[j], vd);
if (row < 0) { row = -1 - row; }
const int row = UnsignIndex(DofToVDof(dofs[j], vd));
if (i_diag[row] == i_diag[row+1]) // diag row empty?
{
i_offd[row] = old_dofs[j + vd * dofs.Size()];
@@ -4546,9 +4541,9 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
{
const Embedding &emb = dtrans.embeddings[k];
int fine_rank = old_ranks[k];
int coarse_rank = (emb.parent < 0) ? (-1 - emb.parent)
: old_pncmesh->ElementRank(emb.parent);
const int fine_rank = old_ranks[k];
const int coarse_rank = (emb.parent < 0) ? FlipIndexSign(emb.parent)
: old_pncmesh->ElementRank(emb.parent);
if (coarse_rank != MyRank && fine_rank == MyRank)
{
@@ -4636,8 +4631,8 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
{
if (!std::isfinite(lR(i, 0))) { continue; }
int r = DofToVDof(dofs[i], vd);
int m = (r >= 0) ? r : (-1 - r);
const int r = DofToVDof(dofs[i], vd);
const int m = UnsignIndex(r);
if (is_dg || !mark[m])
{
@@ -4686,8 +4681,7 @@ ParFiniteElementSpace::ParallelDerefinementMatrix(int old_ndofs,
{
if (!std::isfinite(lR(i, 0))) { continue; }
int r = DofToVDof(dofs[i], vd);
int m = (r >= 0) ? r : (-1 - r);
const int m = UnsignIndex(DofToVDof(dofs[i], vd));
if (is_dg || !mark[m])
{
+48 -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)
{
@@ -1568,6 +1582,39 @@ PLBound ParGridFunction::GetBounds(Vector &lower, Vector &upper,
return plb;
}
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMinimum(
const int vdim, const PLBound &plb, const int max_depth,
const real_t tol) const
{
std::pair<real_t, real_t> minmax =
GridFunction::EstimateFunctionMinimum(vdim, plb, max_depth, tol);
real_t glob_min_lower = minmax.first;
real_t glob_min_upper = minmax.second;
MPI_Allreduce(MPI_IN_PLACE, &glob_min_lower, 1,
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
MPI_Allreduce(MPI_IN_PLACE, &glob_min_upper, 1,
MFEM_MPI_REAL_T, MPI_MIN, pfes->GetComm());
return std::make_pair(glob_min_lower, glob_min_upper);
}
std::pair<real_t, real_t> ParGridFunction::EstimateFunctionMaximum(
const int vdim, const PLBound &plb, const int max_depth,
const real_t tol) const
{
std::pair<real_t, real_t> minmax =
GridFunction::EstimateFunctionMaximum(vdim, plb, max_depth, tol);
real_t glob_max_lower = minmax.first;
real_t glob_max_upper = minmax.second;
MPI_Allreduce(MPI_IN_PLACE, &glob_max_lower, 1,
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
MPI_Allreduce(MPI_IN_PLACE, &glob_max_upper, 1,
MFEM_MPI_REAL_T, MPI_MAX, pfes->GetComm());
return std::make_pair(glob_max_lower, glob_max_upper);
}
} // namespace mfem
#endif // MFEM_USE_MPI
+19 -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
@@ -609,6 +609,18 @@ public:
PLBound GetBounds(Vector &lower, Vector &upper,
const int ref_factor=1, const int vdim=-1) const override;
/** @brief Estimate the GridFunction minimum across all elements. */
std::pair<real_t, real_t> EstimateFunctionMinimum(const int vdim,
const PLBound &plb,
const int max_depth,
const real_t tol) const override;
/** @brief Estimate the GridFunction maximum across all elements. */
std::pair<real_t, real_t> EstimateFunctionMaximum(const int vdim,
const PLBound &plb,
const int max_depth,
const real_t tol) const override;
/** Save the local portion of the ParGridFunction. This differs from the
serial GridFunction::Save in that it takes into account the signs of
the local dofs. */
+11 -5
View File
@@ -321,12 +321,17 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
const int vd = vdim;
const bool t = byvdim;
const int threshold = ndofs;
const int nsdofs = pfes.GetFaceNbrVSize();
const int nsdofs = pfes.GetFaceNbrVSize() / vd;
auto d_indices1 = scatter_indices1.Read();
auto d_indices2 = scatter_indices2.Read();
auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
auto d_x_shared = Reshape(face_nbr_data.Read(),
t?vd:nsdofs, t?nsdofs:vd);
const int ne_shared = nsdofs / elem_dofs;
const int nedof = elem_dofs;
// Note: the shape of face_nbr_data, as determined by
// ParFiniteElementSpace::ExchangeFaceNbrData, is (elem_dofs, vdim,
// ne_shared), independent of the ordering (byNODES or byVDIM) of the finite
// element space.
auto d_x_shared = Reshape(face_nbr_data.Read(), elem_dofs, vd, ne_shared);
auto d_y = Reshape(y.Write(), nface_dofs, vd, 2, nf);
mfem::forall(nfdofs, [=] MFEM_HOST_DEVICE (int i)
{
@@ -346,8 +351,9 @@ void ParL2FaceRestriction::DoubleValuedConformingMult(
}
else if (idx2>=threshold) // shared boundary
{
d_y(dof, c, 1, face) = d_x_shared(t?c:(idx2-threshold),
t?(idx2-threshold):c);
const int e_shared = (idx2 - threshold) / nedof;
const int i_shared = (idx2 - threshold) % nedof;
d_y(dof, c, 1, face) = d_x_shared(i_shared,c,e_shared);
}
else // true boundary
{
+1 -4
View File
@@ -271,10 +271,7 @@ inline void QuadratureFunction::GetValues(
const int s_offset = qspace->Offset(idx);
const int sl_size = qspace->Offset(idx + 1) - s_offset;
// Make the values matrix memory an alias of the quadrature function memory
Memory<real_t> &values_mem = values.GetMemory();
values_mem.Delete();
values_mem.MakeAlias(GetMemory(), vdim*s_offset, vdim*sl_size);
values.SetSize(vdim, sl_size);
values.MakeRef(GetMemory(), vdim*s_offset, vdim, sl_size);
}
inline void QuadratureFunction::GetValues(
+8 -9
View File
@@ -334,17 +334,16 @@ template<int DIM, int SDIM, int D1D, int Q1D>
QuadratureInterpolator::DetKernelType
QuadratureInterpolator::DetKernels::Kernel()
{
if (DIM == 1)
if constexpr (DIM == 1)
{
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
else { MFEM_ABORT(""); }
if constexpr (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if constexpr (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
else if constexpr (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
}
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
else { MFEM_ABORT(""); }
else if constexpr (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
else if constexpr (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
MFEM_ABORT("");
}
/// @endcond
+4 -4
View File
@@ -203,10 +203,10 @@ template<int DIM, QVectorLayout Q_LAYOUT,
QuadratureInterpolator::TensorEvalKernelType
QuadratureInterpolator::TensorEvalKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
else if (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
else if (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
else { MFEM_ABORT(""); }
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Values1D<Q_LAYOUT>; }
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Values2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Values3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
MFEM_ABORT("");
}
/// @endcond
+9 -2
View File
@@ -453,8 +453,15 @@ QuadratureInterpolator::TensorEvalHDivKernels::Kernel()
{
using namespace internal::quadrature_interpolator;
static_assert(DIM == 2 || DIM == 3, "only DIM=2 and DIM=3 are implemented!");
if (DIM == 2) { return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>; }
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
if constexpr (DIM == 2)
{
return EvalHDiv2D<Q_LAYOUT, FLAGS, D1D, Q1D>;
}
else if constexpr (DIM == 3)
{
return EvalHDiv3D<Q_LAYOUT, FLAGS, D1D, Q1D>;
}
MFEM_ABORT("only DIM=2 and DIM=3 are implemented!");
}
/// @endcond
+44
View File
@@ -0,0 +1,44 @@
// 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 "../quadinterpolator.hpp"
#include "eval_transpose.hpp"
namespace mfem
{
/// @cond Suppress_Doxygen_warnings
QuadratureInterpolator::TensorEvalTransposeKernelType
QuadratureInterpolator::TensorEvalTransposeKernels::Fallback(
int DIM, QVectorLayout Q_LAYOUT, int, int, int)
{
using namespace internal::quadrature_interpolator;
if (Q_LAYOUT == QVectorLayout::byNODES)
{
if (DIM == 1) { return ValuesTranspose1D<QVectorLayout::byNODES>; }
else if (DIM == 2) { return ValuesTranspose2D<QVectorLayout::byNODES>; }
else if (DIM == 3) { return ValuesTranspose3D<QVectorLayout::byNODES>; }
}
else
{
if (DIM == 1) { return ValuesTranspose1D<QVectorLayout::byVDIM>; }
else if (DIM == 2) { return ValuesTranspose2D<QVectorLayout::byVDIM>; }
else if (DIM == 3) { return ValuesTranspose3D<QVectorLayout::byVDIM>; }
}
MFEM_ABORT("Invalid dimension");
return nullptr;
}
/// @endcond
} // namespace mfem
+300
View File
@@ -0,0 +1,300 @@
// 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.
#pragma once
#include "../quadinterpolator.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../linalg/kernels.hpp"
#include "../kernels.hpp"
namespace mfem
{
namespace internal
{
namespace quadrature_interpolator
{
template<QVectorLayout Q_LAYOUT>
static void ValuesTranspose1D(const int NE,
const real_t *b_,
const real_t *q_,
real_t *e_,
const int vdim,
const int d1d,
const int q1d)
{
const auto b = Reshape(b_, q1d, d1d);
const auto qd = Q_LAYOUT == QVectorLayout::byNODES ?
Reshape(q_, q1d, vdim, NE) :
Reshape(q_, vdim, q1d, NE);
auto e = Reshape(e_, d1d, vdim, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int el)
{
for (int c = 0; c < vdim; c++)
{
for (int d = 0; d < d1d; d++)
{
real_t u = 0.0;
for (int q = 0; q < q1d; q++)
{
const real_t qval = Q_LAYOUT == QVectorLayout::byVDIM ?
qd(c, q, el) : qd(q, c, el);
u += b(q, d) * qval;
}
e(d, c, el) += u;
}
}
});
}
template<QVectorLayout Q_LAYOUT,
int T_VDIM = 0, int T_D1D = 0, int T_Q1D = 0,
int T_NBZ = 1>
static void ValuesTranspose2D(const int NE,
const real_t *b_,
const real_t *q_,
real_t *e_,
const int vdim = 0,
const int d1d = 0,
const int q1d = 0)
{
static constexpr int NBZ = T_NBZ ? T_NBZ : 1;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = T_VDIM ? T_VDIM : vdim;
const auto b = Reshape(b_, Q1D, D1D);
const auto q = Q_LAYOUT == QVectorLayout::byNODES ?
Reshape(q_, Q1D, Q1D, VDIM, NE) :
Reshape(q_, VDIM, Q1D, Q1D, NE);
auto e = Reshape(e_, D1D, D1D, VDIM, NE);
mfem::forall_2D_batch(NE, D1D, D1D, NBZ, [=] MFEM_HOST_DEVICE (int el)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = T_VDIM ? T_VDIM : vdim;
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;
const int tidz = MFEM_THREAD_ID(z);
MFEM_SHARED real_t sB[MQ1*MD1];
MFEM_SHARED real_t sm0[NBZ][MDQ*MDQ];
MFEM_SHARED real_t sm1[NBZ][MDQ*MDQ];
kernels::internal::LoadB<MD1,MQ1>(D1D,Q1D,b,sB);
ConstDeviceMatrix B(sB, D1D, Q1D);
DeviceMatrix QQ(sm0[tidz], MQ1, MQ1);
DeviceMatrix DQ(sm1[tidz], MD1, MQ1);
DeviceMatrix DD(sm0[tidz], MD1, MD1);
for (int c = 0; c < VDIM; c++)
{
// Load Q data
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
QQ(qx,qy) = Q_LAYOUT == QVectorLayout::byVDIM ?
q(c,qx,qy,el) : q(qx,qy,c,el);
}
}
MFEM_SYNC_THREAD;
// Transpose in y: QQ -> DQ (apply B^T in y-direction)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += B(dy,qy) * QQ(qx,qy);
}
DQ(dy,qx) = u;
}
}
MFEM_SYNC_THREAD;
// Transpose in x: DQ -> DD (apply B^T in x-direction)
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += B(dx,qx) * DQ(dy,qx);
}
DD(dx,dy) = u;
}
}
MFEM_SYNC_THREAD;
// Store result
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
e(dx,dy,c,el) += DD(dx,dy);
}
}
MFEM_SYNC_THREAD;
}
});
}
template<QVectorLayout Q_LAYOUT,
int T_VDIM = 0, int T_D1D = 0, int T_Q1D = 0>
static void ValuesTranspose3D(const int NE,
const real_t *b_,
const real_t *q_,
real_t *e_,
const int vdim = 0,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = T_VDIM ? T_VDIM : vdim;
const auto b = Reshape(b_, Q1D, D1D);
const auto q = Q_LAYOUT == QVectorLayout::byNODES ?
Reshape(q_, Q1D, Q1D, Q1D, VDIM, NE) :
Reshape(q_, VDIM, Q1D, Q1D, Q1D, NE);
auto e = Reshape(e_, D1D, D1D, D1D, VDIM, NE);
mfem::forall_3D(NE, D1D, D1D, D1D, [=] MFEM_HOST_DEVICE (int el)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = T_VDIM ? T_VDIM : vdim;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_INTERP_1D;
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_INTERP_1D;
constexpr int MDQ = (MQ1 > MD1) ? MQ1 : MD1;
MFEM_SHARED real_t sB[MQ1*MD1];
MFEM_SHARED real_t sm0[MDQ*MDQ*MDQ];
MFEM_SHARED real_t sm1[MDQ*MDQ*MDQ];
kernels::internal::LoadB<MD1,MQ1>(D1D,Q1D,b,sB);
ConstDeviceMatrix B(sB, D1D, Q1D);
DeviceCube QQQ(sm0, MQ1, MQ1, MQ1);
DeviceCube DQQ(sm1, MD1, MQ1, MQ1);
DeviceCube DDQ(sm0, MD1, MD1, MQ1);
DeviceCube DDD(sm1, MD1, MD1, MD1);
for (int c = 0; c < VDIM; c++)
{
// Load Q data
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
QQQ(qx,qy,qz) = Q_LAYOUT == QVectorLayout::byVDIM ?
q(c,qx,qy,qz,el) : q(qx,qy,qz,c,el);
}
}
}
MFEM_SYNC_THREAD;
// Transpose in z
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u += B(dz,qz) * QQQ(qx,qy,qz);
}
DQQ(dz,qx,qy) = u;
}
}
}
MFEM_SYNC_THREAD;
// Transpose in y
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += B(dy,qy) * DQQ(dz,qx,qy);
}
DDQ(dz,dy,qx) = u;
}
}
}
MFEM_SYNC_THREAD;
// Transpose in x
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += B(dx,qx) * DDQ(dz,dy,qx);
}
DDD(dx,dy,dz) = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
e(dx,dy,dz,c,el) += DDD(dx,dy,dz);
}
}
}
MFEM_SYNC_THREAD;
}
});
}
} // namespace quadrature_interpolator
} // namespace internal
template<int DIM, QVectorLayout Q_LAYOUT,
int VDIM, int D1D, int Q1D, int NBZ>
QuadratureInterpolator::TensorEvalTransposeKernelType
QuadratureInterpolator::TensorEvalTransposeKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::ValuesTranspose1D<Q_LAYOUT>; }
else if (DIM == 2) { return internal::quadrature_interpolator::ValuesTranspose2D<Q_LAYOUT, VDIM, D1D, Q1D, NBZ>; }
else if (DIM == 3) { return internal::quadrature_interpolator::ValuesTranspose3D<Q_LAYOUT, VDIM, D1D, Q1D>; }
else { MFEM_ABORT(""); }
}
} // namespace mfem
+61
View File
@@ -0,0 +1,61 @@
// 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 "../quadinterpolator.hpp"
#include "eval_transpose.hpp"
namespace mfem
{
namespace internal
{
namespace quadrature_interpolator
{
void InitEvalTransposeByVDimKernels()
{
using k = QuadratureInterpolator::TensorEvalTransposeKernels;
constexpr auto L = QVectorLayout::byVDIM;
// 2D
k::Specialization<2,L,1,2,4>::Opt<8>::Add();
k::Specialization<2,L,1,3,6>::Opt<4>::Add();
k::Specialization<2,L,1,4,8>::Opt<2>::Add();
k::Specialization<2,L,2,2,4>::Opt<8>::Add();
k::Specialization<2,L,2,3,4>::Opt<8>::Add();
k::Specialization<2,L,2,3,6>::Opt<4>::Add();
k::Specialization<2,L,2,4,6>::Opt<2>::Add();
k::Specialization<2,L,2,4,8>::Opt<2>::Add();
// 3D
k::Specialization<3,L,1,2,4>::Opt<1>::Add();
k::Specialization<3,L,1,3,6>::Opt<1>::Add();
k::Specialization<3,L,1,4,8>::Opt<1>::Add();
k::Specialization<3,L,3,2,4>::Opt<1>::Add();
k::Specialization<3,L,3,3,6>::Opt<1>::Add();
k::Specialization<3,L,3,4,8>::Opt<1>::Add();
k::Specialization<3,L,3,2,2>::Opt<1>::Add();
k::Specialization<3,L,3,3,3>::Opt<1>::Add();
k::Specialization<3,L,3,4,4>::Opt<1>::Add();
k::Specialization<3,L,3,5,5>::Opt<1>::Add();
k::Specialization<3,L,3,6,6>::Opt<1>::Add();
k::Specialization<3,L,3,7,7>::Opt<1>::Add();
k::Specialization<3,L,3,8,8>::Opt<1>::Add();
k::Specialization<3,L,3,9,9>::Opt<1>::Add();
k::Specialization<3,L,3,4,6>::Opt<1>::Add();
k::Specialization<3,L,3,3,4>::Opt<1>::Add();
}
} // namespace quadrature_interpolator
} // namespace internal
} // namespace mfem
+8 -8
View File
@@ -592,10 +592,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
QuadratureInterpolator::GradKernelType
QuadratureInterpolator::GradKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
else if (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
else if (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
else { MFEM_ABORT(""); }
if constexpr (DIM == 1) { return internal::quadrature_interpolator::Derivatives1D<Q_LAYOUT, GRAD_PHYS>; }
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::Derivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::Derivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
MFEM_ABORT("");
}
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
@@ -603,10 +603,10 @@ template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
QuadratureInterpolator::CollocatedGradKernelType
QuadratureInterpolator::CollocatedGradKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
else if (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
else if (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
else { MFEM_ABORT(""); }
if constexpr (DIM == 1) { return internal::quadrature_interpolator::CollocatedDerivatives1D<Q_LAYOUT, GRAD_PHYS>; }
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::CollocatedDerivatives2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, NBZ>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::CollocatedDerivatives3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D>; }
MFEM_ABORT("");
}
/// @endcond
+62
View File
@@ -0,0 +1,62 @@
// 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 "../quadinterpolator.hpp"
#include "grad_transpose.hpp"
namespace mfem
{
/// @cond Suppress_Doxygen_warnings
QuadratureInterpolator::GradTransposeKernelType
QuadratureInterpolator::GradTransposeKernels::Fallback(
int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int, int, int)
{
using namespace internal::quadrature_interpolator;
if (Q_LAYOUT == QVectorLayout::byNODES)
{
if (GRAD_PHYS)
{
if (DIM == 1) { return DerivativesTranspose1D<QVectorLayout::byNODES, true>; }
else if (DIM == 2) { return DerivativesTranspose2D<QVectorLayout::byNODES, true>; }
else if (DIM == 3) { return DerivativesTranspose3D<QVectorLayout::byNODES, true>; }
}
else
{
if (DIM == 1) { return DerivativesTranspose1D<QVectorLayout::byNODES, false>; }
else if (DIM == 2) { return DerivativesTranspose2D<QVectorLayout::byNODES, false>; }
else if (DIM == 3) { return DerivativesTranspose3D<QVectorLayout::byNODES, false>; }
}
}
else
{
if (GRAD_PHYS)
{
if (DIM == 1) { return DerivativesTranspose1D<QVectorLayout::byVDIM, true>; }
else if (DIM == 2) { return DerivativesTranspose2D<QVectorLayout::byVDIM, true>; }
else if (DIM == 3) { return DerivativesTranspose3D<QVectorLayout::byVDIM, true>; }
}
else
{
if (DIM == 1) { return DerivativesTranspose1D<QVectorLayout::byVDIM, false>; }
else if (DIM == 2) { return DerivativesTranspose2D<QVectorLayout::byVDIM, false>; }
else if (DIM == 3) { return DerivativesTranspose3D<QVectorLayout::byVDIM, false>; }
}
}
MFEM_ABORT("Invalid dimension");
return nullptr;
}
/// @endcond
} // namespace mfem
+737
View File
@@ -0,0 +1,737 @@
// 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.
#pragma once
#include "../quadinterpolator.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../linalg/kernels.hpp"
#include "../kernels.hpp"
namespace mfem
{
namespace internal
{
namespace quadrature_interpolator
{
// Transpose gradient operation: integrate against shape function derivatives
// This is the adjoint of the Derivatives operation
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS>
static void DerivativesTranspose1D(const int NE,
const real_t *b_,
const real_t *g_,
const real_t *j_,
const real_t *q_,
real_t *e_,
const int sdim,
const int vdim,
const int d1d,
const int q1d)
{
MFEM_CONTRACT_VAR(b_);
const int SDIM = GRAD_PHYS ? sdim : 1;
const auto g = Reshape(g_, q1d, d1d);
const auto j = Reshape(j_, q1d, SDIM, NE);
const auto q = Q_LAYOUT == QVectorLayout::byNODES ?
Reshape(q_, q1d, vdim, SDIM, NE):
Reshape(q_, vdim, SDIM, q1d, NE);
auto e = Reshape(e_, d1d, vdim, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int el)
{
for (int c = 0; c < vdim; c++)
{
for (int d = 0; d < d1d; d++)
{
real_t u = 0.0;
for (int qx = 0; qx < q1d; qx++)
{
// Load gradient from q-vector
real_t dq[3] = {0.0, 0.0, 0.0};
for (int s = 0; s < SDIM; ++s)
{
if (Q_LAYOUT == QVectorLayout::byVDIM) { dq[s] = q(c, s, qx, el); }
if (Q_LAYOUT == QVectorLayout::byNODES) { dq[s] = q(qx, c, s, el); }
}
// Apply inverse Jacobian transpose (adjoint of physical gradient)
real_t du = dq[0];
if (GRAD_PHYS)
{
if (SDIM == 1) { du = dq[0] / j(qx, 0, el); }
else if (SDIM == 2)
{
const real_t Jloc[2] = {j(qx,0,el), j(qx,1,el)};
real_t Jinv[3];
kernels::CalcLeftInverse<2,1>(Jloc, Jinv);
du = Jinv[0]*dq[0] + Jinv[1]*dq[1];
}
else // SDIM == 3
{
const real_t Jloc[3] = {j(qx,0,el), j(qx,1,el), j(qx,2,el)};
real_t Jinv[3];
kernels::CalcLeftInverse<3,1>(Jloc, Jinv);
du = Jinv[0]*dq[0] + Jinv[1]*dq[1] + Jinv[2]*dq[2];
}
}
// Accumulate contribution (transpose of G matrix)
u += g(qx, d) * du;
}
e(d, c, el) += u;
}
}
});
}
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS,
int T_VDIM = 0, int T_D1D = 0, int T_Q1D = 0,
int T_NBZ = 1>
static void DerivativesTranspose2D(const int NE,
const real_t *b_,
const real_t *g_,
const real_t *j_,
const real_t *q_,
real_t *e_,
const int sdim = 2,
const int vdim = 0,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = T_VDIM ? T_VDIM : vdim;
const int SDIM = GRAD_PHYS ? sdim : 2;
static constexpr int NBZ = T_NBZ ? T_NBZ : 1;
const auto b = Reshape(b_, Q1D, D1D);
const auto g = Reshape(g_, Q1D, D1D);
const auto j = Reshape(j_, Q1D, Q1D, SDIM, 2, NE);
const auto q = Q_LAYOUT == QVectorLayout::byNODES ?
Reshape(q_, Q1D, Q1D, VDIM, SDIM, NE):
Reshape(q_, VDIM, SDIM, Q1D, Q1D, NE);
auto e = Reshape(e_, D1D, D1D, VDIM, NE);
mfem::forall_2D_batch(NE, D1D, D1D, NBZ, [=] MFEM_HOST_DEVICE (int el)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = T_VDIM ? T_VDIM : vdim;
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;
const int tidz = MFEM_THREAD_ID(z);
MFEM_SHARED real_t BG[2][MQ1*MD1];
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,b,g,BG);
DeviceMatrix B(BG[0], D1D, Q1D);
DeviceMatrix G(BG[1], D1D, Q1D);
MFEM_SHARED real_t sm0[NBZ][MDQ*MDQ];
MFEM_SHARED real_t sm1[NBZ][MDQ*MDQ];
DeviceMatrix QQ(sm0[tidz], MQ1, MQ1);
DeviceMatrix DQ0(sm1[tidz], MD1, MQ1);
DeviceMatrix DQ1(sm1[tidz], MD1, MQ1); // Reuse sm1 after DQ0 is done
DeviceMatrix DD(sm0[tidz], MD1, MD1); // Reuse sm0 after QQ is done
for (int c = 0; c < VDIM; c++)
{
// Load Q data and apply inverse Jacobian
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
// Load gradient components
real_t dq[3] = {0.0, 0.0, 0.0};
for (int d = 0; d < SDIM; ++d)
{
if (Q_LAYOUT == QVectorLayout::byVDIM) { dq[d] = q(c, d, qx, qy, el); }
else { dq[d] = q(qx, qy, c, d, el); }
}
// Apply inverse Jacobian transpose (adjoint of physical gradient)
real_t du[2] = {dq[0], dq[1]};
if (GRAD_PHYS)
{
if (SDIM == 2)
{
real_t Jloc[4], Jinv[4];
Jloc[0] = j(qx,qy,0,0,el);
Jloc[1] = j(qx,qy,1,0,el);
Jloc[2] = j(qx,qy,0,1,el);
Jloc[3] = j(qx,qy,1,1,el);
kernels::CalcInverse<2>(Jloc, Jinv);
const real_t U = Jinv[0]*dq[0] + Jinv[2]*dq[1];
const real_t V = Jinv[1]*dq[0] + Jinv[3]*dq[1];
du[0] = U;
du[1] = V;
}
else // SDIM == 3
{
real_t Jloc[6], Jinv[6];
Jloc[0] = j(qx,qy,0,0,el);
Jloc[1] = j(qx,qy,1,0,el);
Jloc[2] = j(qx,qy,2,0,el);
Jloc[3] = j(qx,qy,0,1,el);
Jloc[4] = j(qx,qy,1,1,el);
Jloc[5] = j(qx,qy,2,1,el);
kernels::CalcLeftInverse<3,2>(Jloc, Jinv);
const real_t U = Jinv[0]*dq[0] + Jinv[2]*dq[1] + Jinv[4]*dq[2];
const real_t V = Jinv[1]*dq[0] + Jinv[3]*dq[1] + Jinv[5]*dq[2];
du[0] = U;
du[1] = V;
}
}
QQ(qx, qy) = du[0]; // Store du/dx component
}
}
MFEM_SYNC_THREAD;
// Apply B^T in y-direction: QQ -> DQ0
// (Transpose of d/dx which uses DQ1(dy,qx)*B(dy,qy))
// Must produce DQ0(dy,qx) to match forward's DQ1 indexing
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += B(dy,qy) * QQ(qx,qy);
}
DQ0(dy,qx) = u;
}
}
MFEM_SYNC_THREAD;
// Apply G^T in x-direction: DQ0 -> DD
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += G(dx,qx) * DQ0(dy,qx);
}
DD(dx,dy) = u;
}
}
MFEM_SYNC_THREAD;
// Accumulate to output
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
e(dx,dy,c,el) += DD(dx,dy);
}
}
MFEM_SYNC_THREAD;
// Now process du/dy component
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
// Load gradient components
real_t dq[3] = {0.0, 0.0, 0.0};
for (int d = 0; d < SDIM; ++d)
{
if (Q_LAYOUT == QVectorLayout::byVDIM) { dq[d] = q(c, d, qx, qy, el); }
else { dq[d] = q(qx, qy, c, d, el); }
}
// Apply inverse Jacobian transpose
real_t du[2] = {dq[0], dq[1]};
if (GRAD_PHYS)
{
if (SDIM == 2)
{
real_t Jloc[4], Jinv[4];
Jloc[0] = j(qx,qy,0,0,el);
Jloc[1] = j(qx,qy,1,0,el);
Jloc[2] = j(qx,qy,0,1,el);
Jloc[3] = j(qx,qy,1,1,el);
kernels::CalcInverse<2>(Jloc, Jinv);
const real_t U = Jinv[0]*dq[0] + Jinv[2]*dq[1];
const real_t V = Jinv[1]*dq[0] + Jinv[3]*dq[1];
du[0] = U;
du[1] = V;
}
else // SDIM == 3
{
real_t Jloc[6], Jinv[6];
Jloc[0] = j(qx,qy,0,0,el);
Jloc[1] = j(qx,qy,1,0,el);
Jloc[2] = j(qx,qy,2,0,el);
Jloc[3] = j(qx,qy,0,1,el);
Jloc[4] = j(qx,qy,1,1,el);
Jloc[5] = j(qx,qy,2,1,el);
kernels::CalcLeftInverse<3,2>(Jloc, Jinv);
const real_t U = Jinv[0]*dq[0] + Jinv[2]*dq[1] + Jinv[4]*dq[2];
const real_t V = Jinv[1]*dq[0] + Jinv[3]*dq[1] + Jinv[5]*dq[2];
du[0] = U;
du[1] = V;
}
}
QQ(qx, qy) = du[1]; // Store du/dy component
}
}
MFEM_SYNC_THREAD;
// Apply G^T in y-direction: QQ -> DQ1
// (Transpose of d/dy which uses DQ0(dy,qx)*G(dy,qy))
// Must produce DQ1(dy,qx) to match forward's DQ0 indexing
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += G(dy,qy) * QQ(qx,qy);
}
DQ1(dy,qx) = u;
}
}
MFEM_SYNC_THREAD;
// Apply B^T in x-direction: DQ1 -> DD
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += B(dx,qx) * DQ1(dy,qx);
}
DD(dx,dy) = u;
}
}
MFEM_SYNC_THREAD;
// Accumulate to output
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
e(dx,dy,c,el) += DD(dx,dy);
}
}
MFEM_SYNC_THREAD;
}
});
}
template<QVectorLayout Q_LAYOUT, bool GRAD_PHYS,
int T_VDIM = 0, int T_D1D = 0, int T_Q1D = 0>
static void DerivativesTranspose3D(const int NE,
const real_t *b_,
const real_t *g_,
const real_t *j_,
const real_t *q_,
real_t *e_,
const int sdim = 3,
const int vdim = 0,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = T_VDIM ? T_VDIM : vdim;
const auto b = Reshape(b_, Q1D, D1D);
const auto g = Reshape(g_, Q1D, D1D);
const auto j = Reshape(j_, Q1D, Q1D, Q1D, 3, 3, NE);
const auto q = Q_LAYOUT == QVectorLayout::byNODES ?
Reshape(q_, Q1D, Q1D, Q1D, VDIM, 3, NE):
Reshape(q_, VDIM, 3, Q1D, Q1D, Q1D, NE);
auto e = Reshape(e_, D1D, D1D, D1D, VDIM, NE);
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int el)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int VDIM = T_VDIM ? T_VDIM : vdim;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_INTERP_1D;
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_INTERP_1D;
MFEM_SHARED real_t BG[2][MQ1*MD1];
kernels::internal::LoadBG<MD1,MQ1>(D1D,Q1D,b,g,BG);
DeviceMatrix B(BG[0], D1D, Q1D);
DeviceMatrix G(BG[1], D1D, Q1D);
MFEM_SHARED real_t sm0[3][MQ1*MQ1*MQ1];
MFEM_SHARED real_t sm1[3][MQ1*MQ1*MQ1];
DeviceCube QQQ(sm0[0], MQ1, MQ1, MQ1);
DeviceCube DQQ(sm1[0], MD1, MQ1, MQ1);
DeviceCube DDQ(sm0[0], MD1, MD1, MQ1);
DeviceCube DDD(sm1[0], MD1, MD1, MD1);
for (int c = 0; c < VDIM; c++)
{
// Process du/dx component
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t dq[3];
if (Q_LAYOUT == QVectorLayout::byVDIM)
{
dq[0] = q(c,0,qx,qy,qz,el);
dq[1] = q(c,1,qx,qy,qz,el);
dq[2] = q(c,2,qx,qy,qz,el);
}
else
{
dq[0] = q(qx,qy,qz,c,0,el);
dq[1] = q(qx,qy,qz,c,1,el);
dq[2] = q(qx,qy,qz,c,2,el);
}
real_t du[3] = {dq[0], dq[1], dq[2]};
if (GRAD_PHYS)
{
real_t Jloc[9], Jinv[9];
for (int col = 0; col < 3; col++)
{
for (int row = 0; row < 3; row++)
{
Jloc[row+3*col] = j(qx,qy,qz,row,col,el);
}
}
kernels::CalcInverse<3>(Jloc, Jinv);
const real_t U = Jinv[0]*dq[0] + Jinv[3]*dq[1] + Jinv[6]*dq[2];
const real_t V = Jinv[1]*dq[0] + Jinv[4]*dq[1] + Jinv[7]*dq[2];
const real_t W = Jinv[2]*dq[0] + Jinv[5]*dq[1] + Jinv[8]*dq[2];
du[0] = U; du[1] = V; du[2] = W;
}
QQQ(qx,qy,qz) = du[0];
}
}
}
MFEM_SYNC_THREAD;
// Apply G^T in x: QQQ -> DQQ (transpose of G⊗B⊗B)
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += G(dx,qx) * QQQ(qx,qy,qz);
}
DQQ(dx,qy,qz) = u;
}
}
}
MFEM_SYNC_THREAD;
// Apply B^T in y: DQQ -> DDQ
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += B(dy,qy) * DQQ(dx,qy,qz);
}
DDQ(dx,dy,qz) = u;
}
}
}
MFEM_SYNC_THREAD;
// Apply B^T in z: DDQ -> DDD
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u += B(dz,qz) * DDQ(dx,dy,qz);
}
DDD(dx,dy,dz) = u;
}
}
}
MFEM_SYNC_THREAD;
// Accumulate result
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
e(dx,dy,dz,c,el) += DDD(dx,dy,dz);
}
}
}
MFEM_SYNC_THREAD;
// Process du/dy component
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t dq[3];
if (Q_LAYOUT == QVectorLayout::byVDIM)
{
dq[0] = q(c,0,qx,qy,qz,el);
dq[1] = q(c,1,qx,qy,qz,el);
dq[2] = q(c,2,qx,qy,qz,el);
}
else
{
dq[0] = q(qx,qy,qz,c,0,el);
dq[1] = q(qx,qy,qz,c,1,el);
dq[2] = q(qx,qy,qz,c,2,el);
}
real_t du[3] = {dq[0], dq[1], dq[2]};
if (GRAD_PHYS)
{
real_t Jloc[9], Jinv[9];
for (int col = 0; col < 3; col++)
{
for (int row = 0; row < 3; row++)
{
Jloc[row+3*col] = j(qx,qy,qz,row,col,el);
}
}
kernels::CalcInverse<3>(Jloc, Jinv);
const real_t U = Jinv[0]*dq[0] + Jinv[3]*dq[1] + Jinv[6]*dq[2];
const real_t V = Jinv[1]*dq[0] + Jinv[4]*dq[1] + Jinv[7]*dq[2];
const real_t W = Jinv[2]*dq[0] + Jinv[5]*dq[1] + Jinv[8]*dq[2];
du[0] = U; du[1] = V; du[2] = W;
}
QQQ(qx,qy,qz) = du[1];
}
}
}
MFEM_SYNC_THREAD;
// Apply B^T in x: QQQ -> DQQ (transpose of B⊗G⊗B)
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += B(dx,qx) * QQQ(qx,qy,qz);
}
DQQ(dx,qy,qz) = u;
}
}
}
MFEM_SYNC_THREAD;
// Apply G^T in y: DQQ -> DDQ
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += G(dy,qy) * DQQ(dx,qy,qz);
}
DDQ(dx,dy,qz) = u;
}
}
}
MFEM_SYNC_THREAD;
// Apply B^T in z: DDQ -> DDD
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u += B(dz,qz) * DDQ(dx,dy,qz);
}
DDD(dx,dy,dz) = u;
}
}
}
MFEM_SYNC_THREAD;
// Accumulate result
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
e(dx,dy,dz,c,el) += DDD(dx,dy,dz);
}
}
}
MFEM_SYNC_THREAD;
// Process du/dz component
MFEM_FOREACH_THREAD(qz,z,Q1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t dq[3];
if (Q_LAYOUT == QVectorLayout::byVDIM)
{
dq[0] = q(c,0,qx,qy,qz,el);
dq[1] = q(c,1,qx,qy,qz,el);
dq[2] = q(c,2,qx,qy,qz,el);
}
else
{
dq[0] = q(qx,qy,qz,c,0,el);
dq[1] = q(qx,qy,qz,c,1,el);
dq[2] = q(qx,qy,qz,c,2,el);
}
real_t du[3] = {dq[0], dq[1], dq[2]};
if (GRAD_PHYS)
{
real_t Jloc[9], Jinv[9];
for (int col = 0; col < 3; col++)
{
for (int row = 0; row < 3; row++)
{
Jloc[row+3*col] = j(qx,qy,qz,row,col,el);
}
}
kernels::CalcInverse<3>(Jloc, Jinv);
const real_t U = Jinv[0]*dq[0] + Jinv[3]*dq[1] + Jinv[6]*dq[2];
const real_t V = Jinv[1]*dq[0] + Jinv[4]*dq[1] + Jinv[7]*dq[2];
const real_t W = Jinv[2]*dq[0] + Jinv[5]*dq[1] + Jinv[8]*dq[2];
du[0] = U; du[1] = V; du[2] = W;
}
QQQ(qx,qy,qz) = du[2];
}
}
}
MFEM_SYNC_THREAD;
// Apply G^T in z: QQQ -> DQQ (transpose of B⊗B⊗G)
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(qy,y,Q1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u += G(dz,qz) * QQQ(qx,qy,qz);
}
DQQ(qx,qy,dz) = u;
}
}
}
MFEM_SYNC_THREAD;
// Apply B^T in y: DQQ -> DDQ
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += B(dy,qy) * DQQ(qx,qy,dz);
}
DDQ(qx,dy,dz) = u;
}
}
}
MFEM_SYNC_THREAD;
// Apply B^T in x: DDQ -> DDD
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += B(dx,qx) * DDQ(qx,dy,dz);
}
DDD(dx,dy,dz) = u;
}
}
}
MFEM_SYNC_THREAD;
// Accumulate result
MFEM_FOREACH_THREAD(dz,z,D1D)
{
MFEM_FOREACH_THREAD(dy,y,D1D)
{
MFEM_FOREACH_THREAD(dx,x,D1D)
{
e(dx,dy,dz,c,el) += DDD(dx,dy,dz);
}
}
}
MFEM_SYNC_THREAD;
}
});
}
} // namespace quadrature_interpolator
} // namespace internal
template<int DIM, QVectorLayout Q_LAYOUT, bool GRAD_PHYS, int VDIM, int D1D,
int Q1D, int NBZ>
QuadratureInterpolator::GradTransposeKernelType
QuadratureInterpolator::GradTransposeKernels::Kernel()
{
if (DIM == 1) { return internal::quadrature_interpolator::DerivativesTranspose1D<Q_LAYOUT, GRAD_PHYS>; }
else if (DIM == 2) { return internal::quadrature_interpolator::DerivativesTranspose2D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D, NBZ>; }
else if (DIM == 3) { return internal::quadrature_interpolator::DerivativesTranspose3D<Q_LAYOUT, GRAD_PHYS, VDIM, D1D, Q1D>; }
else { MFEM_ABORT(""); }
}
} // namespace mfem

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