Compare commits

...
Author SHA1 Message Date
Brandon Talamini c645943fc4 Fix indexing error in building of derivative operator 2025-12-23 15:43:48 -08:00
Brandon Talamini 0970705c48 Fix some parameter space sizing that is needed to use it in a diff operator 2025-12-19 16:14:05 -08:00
Eric B. Chin b6e9914ef8 Merge branch 'master' into dfem-sum-fixes 2025-12-16 16:08:44 -08:00
Tzanio Kolev 8452c8cc7f Merge pull request #5153 from mfem/doc-fixes
ex41 and contact doc fixes
2025-12-15 08:39:46 -08:00
Socratis PetridesandJan Nikl 3d25fe2e3d Update doc/CodeDocumentation.dox
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-12-12 12:48:59 -08:00
Socratis Petrides 9093d35699 ex41 and contact doc fixes 2025-12-12 11:42:00 -08:00
Tzanio Kolev fd9c5fbfc1 Merge pull request #5152 from mfem/new-dev-version-4.9.1
Update version numbers to 4.9.1 -- a new development version
2025-12-12 11:13:49 -08:00
Veselin Dobrev b5c712dc80 Update version numbers to 4.9.1 -- a new development version 2025-12-12 10:03:11 -08:00
Tzanio Kolev d9d6526cc1 Merge pull request #5057 from mfem/mfem-4.9-dev
Final changes for mfem-4.9
2025-12-11 10:59:53 -08:00
Tzanio Kolev c7515017c8 minor 2025-12-11 10:43:08 -08:00
Tzanio Kolev d0e4d9a242 minor 2025-12-11 10:40:28 -08:00
Tzanio Kolev eb7a9abc46 Update doxygen for mfem-4.9 2025-12-11 10:39:02 -08:00
Veselin Dobrev 3f7e5a3aa4 Remove a FIXME that was addressed 2025-12-11 10:21:41 -08:00
camierjs c328a0f635 CHANGELOG minor typo 2025-12-11 09:57:25 -08:00
Tzanio Kolev f5632ae914 minor 2025-12-11 09:54:03 -08:00
Tzanio Kolev c46fff561b Final CHANGELOG update before the mfem-4.9 release 2025-12-11 09:36:07 -08:00
Will Pazner 9cc9765e15 Add missing Doxygen comment 2025-12-10 21:23:05 -08:00
camierjs ae72ee8016 ParticleSet Fallback auto return type 2025-12-10 10:55:54 -08:00
Veselin Dobrev 091789d078 In ex41/ex41p, update the default time step to be the same as in ex9/ex9p
and remove a sample run that is now the same as the default.

Fix a -Wextra warning in mesh_readers.cpp.
2025-12-09 18:48:15 -08:00
camierjs b534c77ce3 - Adding -tf 1.0 to ex41p* test command
- Fix std::pow to int cast warning
2025-12-09 17:44:01 -08:00
camierjs 87ca8b3560 Reduce ex41 t_final downto 1.0 2025-12-09 17:10:04 -08:00
camierjs 387005e37a with style 2025-12-09 15:25:57 -08:00
camierjs 1d81f19c37 minor argument type safety casts 2025-12-09 15:16:11 -08:00
Jan Nikl bb26cc73d8 Minor fix of signedness. 2025-12-09 14:30:06 -08:00
camierjs dd11be5d6b Rename UTF8 MTOP variables to ASCII ones 2025-12-09 14:14:23 -08:00
Veselin Dobrev ecc2490ef9 Fix in DenseTensor::MemoryUsage() 2025-12-09 11:12:03 -08:00
camierjs b02842265b Silence some 'declared but never referenced' TMOP specialization warnings 2025-12-09 10:51:56 -08:00
Tzanio Kolev 9ad4e1258f Merge branch 'master' into mfem-4.9-dev 2025-12-08 15:02:14 -08:00
Tzanio Kolev 733f8f231b Merge pull request #4977 from mfem/imex-conv-diff-dg
IMEX Implementation with DG Convection-Diffusion example [imex-conv-diff-dg]
2025-12-08 14:59:41 -08:00
Jan Nikl 7f71efd74a Update CHANGELOG for #5097 2025-12-08 10:32:14 -08:00
blaz 822d6dcf01 remove Bcast to avoid confusions 2025-12-07 13:59:08 -08:00
blaz 0d70d354a5 style 2025-12-06 19:41:07 -08:00
blaz 9e35901815 MPI_BOOL 2025-12-06 19:39:13 -08:00
blaz 14b4c7b4d7 Bcast 2025-12-05 23:54:46 -08:00
blaz 8be02504cb adding message for changing the time step 2025-12-05 22:41:27 -08:00
blaz 9025fdec27 avoids different behaviour between processes 2025-12-05 22:36:05 -08:00
blaz 21d1bc4a8b modified limits 2025-12-05 20:23:28 -08:00
blaz 26a832b81f style 2025-12-05 20:18:03 -08:00
Tzanio Kolev f9f83882ce Merge pull request #4986 from mfem/particleset-particle-dev
Particle Tracking (`ParticleSet`)
2025-12-05 20:17:08 -08:00
blaz e1efc3b437 replaced direct comparison of real numbers 2025-12-05 20:16:02 -08:00
Veselin Dobrev 238523b52b Some additional edits mostly related to moving the 'navier' directory
to the 'fluids' sub-directory.
2025-12-05 15:06:54 -08:00
Veselin Dobrev a70350df55 Merge branch 'master' into particleset-particle-dev 2025-12-05 14:36:34 -08:00
Tzanio Kolev 903e11aee4 Merge pull request #5097 from mfem/task/2025_10_dc_quad_points
Add quadrature func support to VisIt and Conduit data collections
2025-12-05 13:58:12 -08:00
Tzanio Kolev 1c952e3c1f Merge pull request #5059 from mfem/isf
Incompressible Schrödinger Flow
2025-12-05 12:05:35 -08:00
John Camier b2d8ef9e2a Merge branch 'master' into isf 2025-12-05 09:58:29 -08:00
Veselin Dobrev 536a3c50be In miniapps/navier/makefile, fix the shared build 2025-12-05 09:31:55 -08:00
Will Pazner b3eaff280f Merge pull request #5138 from mfem/amgf-mixedint
fix mixed-int bug
2025-12-05 09:27:11 -08:00
John Camier 9ead7ddcc7 Merge branch 'master' into isf 2025-12-05 07:53:17 -08:00
Veselin Dobrev fca40e0662 Merge pull request #5083 from mfem/dg-diffusion-pa-matrix-coeff
Add support for PA DG diffusion with matrix coefficients
2025-12-04 23:21:23 -08:00
camierjs c3e6837706 Merge branch 'master' into isf 2025-12-04 21:39:04 -08:00
blaz b20015c70d fix for problem 3 2025-12-04 21:03:27 -08:00
Ketan Mittal 6a376e1cfd Merge branch 'master' into particleset-particle-dev 2025-12-04 19:36:51 -08:00
Joseph Signorelli 4691c4d751 Merge branch 'particleset-particle-dev' of github.com:mfem/mfem into particleset-particle-dev 2025-12-04 21:36:32 -06:00
Joseph Signorelli 01a5f979a0 fix mistake 2025-12-04 21:36:26 -06:00
Mittal, Ketan 39226e2520 remove some leftover comment 2025-12-04 19:36:22 -08:00
Joseph Signorelli d514d0578c Update CHANGELOG 2025-12-04 21:31:15 -06:00
Socratis Petrides ebe07e7ddb fix mixed-int bug 2025-12-04 18:30:21 -08:00
Tzanio Kolev b6cd380106 Merge pull request #5115 from mfem/dfem-pa-caching
dFEM PA caching
2025-12-04 17:26:39 -08:00
camierjs 3803c3305e - Remove unnecessary general/forall.hpp include in miniapps and tests
- Fix cast warnings in mesh (nc)nurbs files
2025-12-04 16:49:09 -08:00
Veselin Dobrev 88f9dc96db Changes to support single precision MFEM build in ConduitDataCollection 2025-12-04 16:36:01 -08:00
Joseph Signorelli 08b3639eca fix typo 2025-12-04 18:17:19 -06:00
Joseph Signorelli 306702b4ab characteristic --> property 2025-12-04 18:15:55 -06:00
Mittal, Ketan 93ee0e31b3 add * to the sample run 2025-12-04 15:51:59 -08:00
Mittal, Ketan 9037765299 minor changes to miniapp description 2025-12-04 15:50:25 -08:00
Joseph Signorelli d0a53c205c Add note stating GSLIB requirement for particle inclusion 2025-12-04 17:38:54 -06:00
Joseph Signorelli 1eb69ebe21 Add description for Navier Bifurcation 2025-12-04 17:29:39 -06:00
Tzanio KolevandChris Vogl c96f0d3fe6 Update CHANGELOG
Co-authored-by: Chris Vogl <vogl2@llnl.gov>
2025-12-04 15:21:51 -08:00
Veselin Dobrev 0a4cce7bd5 Fix some compiler warnings from -Wshadow and -Wextra 2025-12-04 14:53:50 -08:00
Joseph Signorelli dc6e90914f minor 2025-12-04 15:34:21 -06:00
Joseph Signorelli abd828804e minor doc fix 2025-12-04 15:34:15 -06:00
Joseph Signorelli 89d5b3a95d address reviewer comments 2025-12-04 11:52:07 -06:00
Eric B. Chin 32bdd86c7a Merge branch 'master' into dfem-sum-fixes 2025-12-04 08:51:48 -08:00
Joseph SignorelliandJan Nikl 6f7f169b7e Update fem/particleset.hpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-12-04 07:45:59 -06:00
Veselin Dobrev e2f5504b6c Remove ex41p from the lists of examples with device support 2025-12-03 17:44:14 -08:00
camierjs 48de40c399 Added general/forall header in top MFEM one 2025-12-03 17:18:36 -08:00
Julian Andrej 93856df95f index bug 2025-12-03 14:35:11 -08:00
Joseph Signorelli 0bf6682aa0 minor 2025-12-03 15:52:22 -06:00
Joseph Signorelli 4dd85be8e3 add warning for invalidation of Particle ref 2025-12-03 15:49:25 -06:00
Julian Andrej c299adb3ff fix derivative indexing 2025-12-03 13:41:08 -08:00
Julian Andrej 2008738c1f more docs 2025-12-03 13:21:26 -08:00
Julian Andrej 798141da18 add more docs 2025-12-03 13:13:33 -08:00
Julian Andrej aa03f968ef switch to _THREAD_DIRECT 2025-12-03 12:59:38 -08:00
Julian Andrej a7de138602 Merge branch 'master' into dfem-pa-caching 2025-12-03 12:58:51 -08:00
Joseph Signorelli edd6f10eb4 style 2025-12-03 14:46:50 -06:00
Joseph Signorelli e484a01593 Address reviewer comments
- IDs --> global IDs
- AddNamedField docs update
- Verify particle in SetParticle
- Space in Particle_2 fields/tags added
2025-12-03 14:45:16 -06:00
Mittal, Ketan 7b7cda633d minor doc fix 2025-12-03 11:02:39 -08:00
Mittal, Ketan f0c9e15cf3 update doxygen documentation for sample particle data 2025-12-03 10:59:28 -08:00
Mittal, Ketan 111569288f move example particle documentation inside doxygen 2025-12-02 18:47:26 -08:00
Mittal, Ketan bbe243ad54 minor 2025-12-02 18:25:17 -08:00
camierjs 87b701bfd1 Merge branch 'master' into isf 2025-12-02 17:46:20 -08:00
camierjs 674c982be6 Serial out of source builds still needs relative general/forall.hpp 2025-12-02 17:46:08 -08:00
Ketan Mittal 66d2bbe977 Merge branch 'master' into particleset-particle-dev 2025-12-02 17:06:47 -08:00
Ketan MittalandJan Nikl 0274f2b6fa Apply suggestions from code review
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-12-02 17:06:30 -08:00
Mittal, Ketan c31f303ed2 documentation 2025-12-02 17:05:29 -08:00
Tzanio Kolev 9708cc522e Merge pull request #5135 from mfem/gitlab-ci-extra-time
Gitlab CI: allocate more time for the dane-baseline pipeline
2025-12-02 12:49:54 -08:00
bslazarov ebdd44d5bd comment modification 2025-12-02 11:46:00 -08:00
bslazarov 48976d3856 changelog 2025-12-02 11:42:16 -08:00
camierjs 8ec27e5adf Fix cmake list ordering 2025-12-02 11:39:21 -08:00
camierjs e5dea3f531 Resolve conflicts 2025-12-02 11:38:12 -08:00
Tzanio Kolev a546e320fd Merge pull request #4996 from mfem/contact-miniapp
Contact miniapp
2025-12-02 11:28:18 -08:00
camierjs 02d3a24007 Remove uneeded forall header file 2025-12-02 11:11:57 -08:00
camierjs 95f530ef55 Merge branch 'master' into isf 2025-12-02 10:48:07 -08:00
camierjs 27c54f58fa - Added the 'miniapps/fluids' in CHANGELOG
- Fix NVCC error (enclosing parent function for an extended __host__ __device__ lambda must allow its address to be taken)
- Fix relative path to 'general/forall.hpp'
2025-12-02 10:47:53 -08:00
Jan Nikl 60dc887101 Updated CHANGELOG for #4659 2025-12-02 10:07:21 -08:00
Veselin Dobrev fbcd9cf4d3 In Gitlab CI, change the allocation time for 'dane-baseline' to 1 hour
The previous commit had the change in the wrong place.
2025-12-02 09:56:59 -08:00
Tzanio Kolev 939f8f5522 Updated CHANGELOG 2025-12-02 09:39:28 -08:00
Mittal, Ketan 35b8ae1ce4 documentation and move UpdateId 2025-12-02 09:38:12 -08:00
Tzanio Kolev f284bdf21a Merge branch 'master' into mfem-4.9-dev 2025-12-02 08:31:12 -08:00
Veselin Dobrev f60799af0c In Gitlab CI, increase the time allocation for dane-baseline to 1 hour 2025-12-02 08:04:47 -08:00
Joseph Signorelli 2a9202cf1a Add comment clarifying tag storage in Particle 2025-12-02 08:09:35 -06:00
Will Pazner bd6b580c23 Add symmetric matrix coefficient case to PA DG Diffusion unit test 2025-12-01 23:06:42 -08:00
Will Pazner 65813867e4 Merge remote-tracking branch 'origin/master' into dg-diffusion-pa-matrix-coeff 2025-12-01 23:03:27 -08:00
Tzanio Kolev 74ff518840 Merge pull request #4840 from mfem/qf-project-gf-fallback
Add fallback for QuadratureFunction::ProjectGridFunction
2025-12-01 20:03:00 -08:00
Tzanio Kolev 4492be9ebb Merge pull request #4028 from mfem/table-array
Use Array<int> instead of Memory<int> in Table
2025-12-01 20:02:00 -08:00
Tzanio Kolev b62e215620 Merge pull request #5121 from tomstitt/claude/review-integ-kernels-01LSxNDE62DLqsm9LRtXfbcJ
Fix array shape in `SmemPADiffusionDiagonal2D`
2025-12-01 19:59:41 -08:00
Tzanio Kolev 8497c5129b Merge pull request #5127 from mfem/fix-clang-cuda
Fix CLANG + CUDA builds
2025-12-01 19:59:19 -08:00
Tzanio Kolev 12cda5ec86 Merge pull request #5113 from mfem/mfem-bounds-const
Fix const-ness for bounding related methods
2025-12-01 19:58:52 -08:00
Cyrus Harrison 41fa7cc6ae Remove comment 2025-12-01 08:38:58 -08:00
Cyrus HarrisonandJan Nikl 3787d95b62 Update fem/datacollection.hpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-12-01 08:34:20 -08:00
blaz b6ef7a9d5d modifications 2025-11-30 23:34:45 -08:00
Boyan Lazarov 487c665968 Merge branch 'master' into imex-conv-diff-dg 2025-11-29 01:11:46 -08:00
blaz af2439c80b style 2025-11-28 21:21:02 -08:00
blaz a81fd590ac modification of the time integrators 2025-11-28 21:20:14 -08:00
Boyan LazarovandJan Nikl 10ef282ee6 Update fem/lor/lor.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-28 20:18:16 -08:00
John Camier 25e1562c41 Merge branch 'master' into dfem-pa-caching 2025-11-28 10:04:21 -08:00
camierjs a5595f0887 Fix makefile miniapps/fluids/schrodinger-flow dir 2025-11-28 09:52:56 -08:00
camierjs 0fce2c9fb4 Rename to miniapps/fluids/schrodinger-flow 2025-11-28 09:26:25 -08:00
Veselin Dobrev d90a23f195 Fix the build with PUMI: use 'std::swap' instead of just 'swap'.
In class DenseSymmetricMatrix:
* Fix a warning from -Wextra about implicit copy-ctor.
* Explicitly use defaulted copy/move ctor/assignment.
* A small fix in the SetSize method.
* Rewrap doxygen comments to 80 chars
2025-11-26 15:32:23 -08:00
EB Chin 8bfe86c4c6 get l vector sizes 2025-11-26 14:28:19 -08:00
Eric B. Chin 7d392d2bc8 add assemble for derivative of scalar qfn 2025-11-26 09:11:54 -08:00
Tzanio Kolev 0c5fa5c148 Merge branch 'master' into contact-miniapp 2025-11-25 19:50:00 -08:00
Mittal, Ketan c907c4403f force all default constructors and destructor 2025-11-25 15:40:23 -08:00
Mittal, Ketan 38634ea24a force copy constructor 2025-11-25 15:10:11 -08:00
Will Pazner 838dd47259 TMOP W caching in new files 2025-11-25 14:00:31 -08:00
Will Pazner c78863a437 Merge remote-tracking branch 'origin/master' into table-array
# Conflicts:
#	fem/tmop/tmop_pa_da3.cpp
#	fem/tmop/tmop_pa_tc2.cpp
#	fem/tmop/tmop_pa_tc3.cpp
#	general/array.hpp
#	linalg/densemat.hpp
2025-11-25 14:00:20 -08:00
Mittal, Ketan 7a4d71d40b merge and resolve conflicts 2025-11-25 13:48:09 -08:00
Ketan Mittal 2985487736 Merge branch 'master' into mfem-bounds-const 2025-11-25 13:25:11 -08:00
Mittal, Ketan e3377b35d7 force particle move constructor 2025-11-25 13:20:06 -08:00
Tzanio Kolev 811cc65e5c Merge branch 'master' into isf 2025-11-25 12:18:57 -08:00
Tzanio Kolev 2caa75e35a Merge pull request #5072 from mfem/mtop-gpu
[MTOP] solver with GPU support
2025-11-25 12:16:20 -08:00
Tzanio Kolev adf110b3a7 Merge pull request #4981 from mfem/multivector-dev
`ParticleVector`
2025-11-25 12:15:53 -08:00
Tzanio Kolev 46fd4bdb93 Merge pull request #5117 from mfem/mexp-nurbs-m
Fix mesh-explorer material view for 3D NURBS meshes
2025-11-25 12:10:55 -08:00
Tzanio Kolev 97adb8d71e Merge branch 'master' into task/2025_10_dc_quad_points 2025-11-25 11:42:28 -08:00
Tzanio KolevandJan Nikl b6caf24a13 Update fem/datacollection.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-25 11:42:11 -08:00
Mittal, Ketan b199703b61 changes to address signed/unsigned casts 2025-11-25 11:18:32 -08:00
John Camier ab8080f413 Merge branch 'master' into claude/review-integ-kernels-01LSxNDE62DLqsm9LRtXfbcJ 2025-11-25 10:38:47 -06:00
camierjs 7f27e4605e Use MFEM_ABORT_KERNEL instead 2025-11-25 08:21:08 -08:00
Mittal, Ketan a2eb5daca4 navier bifurcation - particle vis optional 2025-11-24 20:24:06 -08:00
Mittal, Ketan 58cf6a430d minor 2025-11-24 17:40:47 -08:00
camierjs dbab49b725 Avoid taking address of pure virtual function 2025-11-24 17:22:51 -08:00
Mittal, Ketan 137a91cd29 rename AddField to AddNamedField and move data for rotating time history 2025-11-24 16:13:02 -08:00
Mittal, Ketan 8229290c6b initialization warning 2025-11-24 14:36:29 -08:00
Mittal, Ketan 15f82dd8a0 minor 2025-11-24 11:30:53 -08:00
Mittal, Ketan a860c5d18a merge and resolve conflicts 2025-11-24 10:21:40 -08:00
Mittal, Ketan 275c4762a9 fix vis functions 2025-11-24 10:18:54 -08:00
Mittal, Ketan 916e23ddc4 formatting 2025-11-24 09:23:33 -08:00
Mittal, Ketan d45bdbcea2 update CHANGELOG 2025-11-24 09:16:57 -08:00
Mittal, Ketan 9d9c7387a7 Merge branch 'master' of https://github.com/mfem/mfem into multivector-dev 2025-11-24 09:10:14 -08:00
Julian AndrejandJohn Camier fea85d855b Update fem/dfem/doperator.hpp
Co-authored-by: John Camier <camierjs@gmail.com>
2025-11-24 08:28:24 -08:00
Tzanio Kolev 266ff0cdf6 Merge branch 'master' into contact-miniapp 2025-11-23 17:52:07 -08:00
Mittal, Ketan 4817b4df6e Merge branch 'particleset-particle-dev' of https://github.com/mfem/mfem into particleset-particle-dev 2025-11-22 17:43:19 -08:00
Mittal, Ketan 13ad1094ba reviewer comments 2025-11-22 17:43:06 -08:00
John Camier 8c88e76283 Merge branch 'master' into isf 2025-11-21 21:38:19 -06:00
Boyan LazarovandJan Nikl 1a863dd5e1 Update examples/ex41.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 18:33:41 -08:00
blaz 800ae10652 style 2025-11-21 18:32:18 -08:00
blaz ebdbae1a32 changes addressing review comments 2025-11-21 18:31:38 -08:00
Boyan LazarovandJan Nikl 02570c2034 Update examples/ex41.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 18:16:43 -08:00
Boyan LazarovandJan Nikl 7f1bf4c48c Update examples/ex41.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 18:16:02 -08:00
blaz bd65769bac modifications 2025-11-21 18:15:30 -08:00
blaz 70bc8f51e7 modifications 2025-11-21 12:41:29 -08:00
Boyan LazarovandJan Nikl 98bc7fde72 Update examples/ex41.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:23:57 -08:00
Boyan LazarovandJan Nikl 08a1454bad Update examples/ex41.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:20:53 -08:00
Boyan LazarovandJan Nikl 4a8e18de90 Update examples/ex41.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:20:31 -08:00
Boyan LazarovandJan Nikl de71d2ec28 Update linalg/ode.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:20:12 -08:00
Boyan LazarovandJan Nikl c968008b62 Update linalg/ode.cpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:19:48 -08:00
Boyan LazarovandJan Nikl a188730d9c Update linalg/ode.hpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:18:39 -08:00
Boyan LazarovandJan Nikl 4f11387929 Update linalg/ode.hpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:18:17 -08:00
Boyan LazarovandJan Nikl e84092a4a1 Update linalg/ode.hpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:17:54 -08:00
Boyan LazarovandJan Nikl 712e806b77 Update linalg/ode.hpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:17:19 -08:00
Boyan LazarovandJan Nikl 3f76fe3282 Update linalg/ode.hpp
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-21 12:15:16 -08:00
blaz ecd6becbf9 removing WIP 2025-11-21 12:14:26 -08:00
Tzanio Kolev e44df538a5 Merge branch 'master' into dfem-pa-caching 2025-11-21 10:39:56 -08:00
Tzanio Kolev fa8c861b75 Merge pull request #5022 from mfem/dfem-assemble-matrix
dFEM assemble implementations
2025-11-21 10:39:17 -08:00
Socratis Petrides 5453c63c91 cmakelist fix 2025-11-21 09:52:16 -08:00
camierjs 81497ae1ed Sample runs file to fluids/schrodinger_flow 2025-11-20 18:12:42 -06:00
Julian Andrej cc41b690d2 Merge branch 'dfem-assemble-matrix' into dfem-pa-caching
# Conflicts:
#	fem/dfem/util.hpp
2025-11-20 09:03:12 -08:00
Joseph Signorelli b476b769c9 minor 2025-11-20 06:20:36 -06:00
Claude 5db9a79022 Fix smem array dimension bug in SmemPADiffusionDiagonal2D
Fixed array dimension mismatch similar to the bugs fixed in PRs #5108
and #4931. The QD array was declared as [MD1][MQ1] but accessed as
[Q][D], causing incorrect memory layout when MQ1 > MD1.

Changed:
  MFEM_SHARED real_t QD[3][NBZ][MD1][MQ1];
To:
  MFEM_SHARED real_t QD[3][NBZ][MQ1][MD1];

This matches the access pattern QD0[qx][dy] where qx < Q1D and dy < D1D,
and the pointer cast real_t (*)[MD1] which expects the second dimension
to be MD1.
2025-11-19 23:00:05 +00:00
Julian Andrej b9f1a89470 adapt mesh paths 2025-11-19 14:53:57 -08:00
Mittal, Ketan f844193a23 modify particle injection scheme to randomize initial location in navier example 2025-11-19 14:20:27 -08:00
Julian Andrej 36fc1b9705 move miniapps 2025-11-19 14:10:58 -08:00
Mittal, Ketan e3eb2516fa minor 2025-11-19 13:13:15 -08:00
Cyrus 86b200f138 style 2025-11-19 12:55:22 -08:00
Cyrus Harrison aa0dbb384f apply review suggestions 2025-11-19 12:50:06 -08:00
Mittal, Ketan 11ff698c77 update removeparticles logic 2025-11-19 11:32:05 -08:00
Mittal, Ketan 5291bd59fc change particle id type to unsigned long long 2025-11-19 10:52:11 -08:00
camierjs e1edcb321d Fix nvcc access errors and VectorCoefficient partially overridden warnings 2025-11-19 12:14:42 -06:00
John Camier c0d9732d1b Merge branch 'master' into mtop-gpu 2025-11-19 09:45:55 -08:00
Mittal, Ketan 237f566334 merge remote changes and resolve conflicts 2025-11-18 14:01:48 -08:00
Mittal, Ketan f9cb7e0a06 more reviewer comments 2025-11-18 13:18:16 -08:00
Julian Andrej 354859c91d size_t -> int for map key 2025-11-18 08:29:48 -08:00
Socratis Petrides 6bc2b2b5aa another doc fix 2025-11-17 19:53:55 -08:00
Ketan MittalandJan Nikl f970b2cf8f Apply suggestions from code review
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-17 19:36:14 -08:00
Mittal, Ketan 96b1ba0e4f address some reviewer comments 2025-11-17 19:28:59 -08:00
Socratis Petrides dd2e531218 fix doxygen comment 2025-11-17 19:12:31 -08:00
Socratis Petrides 769c396613 fixing comments in ip 2025-11-17 18:59:11 -08:00
Socratis Petrides b7a5940f00 forgotten makefile changes 2025-11-17 18:55:03 -08:00
Socratis Petrides cab5e5f99c added the new direct solver interface to contact driver 2025-11-17 18:53:13 -08:00
Socratis Petrides 0226a21a76 added generic direct solver interf 2025-11-17 18:52:32 -08:00
John Camier 86867e2378 Merge branch 'master' into isf 2025-11-17 18:22:30 -08:00
Socratis Petrides 9302c4229a remove leftover comment 2025-11-17 15:12:16 -08:00
Dylan Copeland 096605f532 Revert empty line. 2025-11-17 15:03:29 -08:00
Dylan Copeland 69b4689048 Fix a bug so that "m" option works for 3D NURBS meshes. 2025-11-17 14:58:21 -08:00
Julian Andrej c3ee799e24 use fold expression 2025-11-17 13:57:57 -08:00
Ketan MittalandJan Nikl 2a504d30a7 Apply suggestions from code review
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-11-17 13:56:18 -08:00
Julian Andrej 8056ba5610 try again 2025-11-17 13:49:22 -08:00
Julian Andrej 7dd05fe015 guard against MSVC N=0 case 2025-11-17 12:58:08 -08:00
Socratis Petrides 861cb9cb6b Merge branch 'master' into contact-miniapp 2025-11-17 11:59:29 -08:00
Socratis Petrides 458254a1f6 set relax_type depending on Hypre version 2025-11-17 11:59:00 -08:00
Socratis Petrides 4defb5c401 removing no longer needed c++ standard spec 2025-11-17 11:58:09 -08:00
Mittal, Ketan aecd9648af merge with master and resolve conflicts 2025-11-17 11:37:43 -08:00
Mittal, Ketan 1e592a7442 change reordering logic 2025-11-17 11:35:47 -08:00
Julian Andrej 78608e4fcd remove prints 2025-11-17 11:09:15 -08:00
Julian Andrej 78a90bbee8 remove prints 2025-11-17 11:08:57 -08:00
Julian Andrej d0f4f9baaf deduction simplification 2025-11-17 11:07:04 -08:00
Julian Andrej 060e253132 simplification for deduction 2025-11-17 11:06:15 -08:00
Joseph Signorelli bc7ea43317 Move ordering to linalg 2025-11-17 12:17:53 -06:00
Tzanio Kolev 9cc28d8772 Merge branch 'master' into mfem-bounds-const 2025-11-17 05:25:06 -08:00
Tzanio Kolev 1877a40a85 Merge pull request #4995 from mfem/amgf
AMGF Solver
2025-11-17 04:45:34 -08:00
Tzanio Kolev 99785763af Merge pull request #5078 from mfem/user-inner-prod
User-Defined Inner Products for IterativeSolvers
2025-11-17 04:41:45 -08:00
Tzanio Kolev e3b4edcae1 Merge pull request #5114 from mfem/dof-table-mem-leak-fix
Fix memory leak in `TensorBasisElement::GetTensorDofToQuad`
2025-11-15 10:02:33 -08:00
Tzanio Kolev 34307512d5 Merge pull request #5108 from mfem/fix-smem-pa-diffusion-apply-2d-indexing
Fix indexing in `SmemPADiffusionApply2D`
2025-11-15 10:01:53 -08:00
Vladimir Z Tomov cef3517d60 updated contact/README with more details for the build. 2025-11-14 18:00:22 -08:00
Julian Andrej 72b7390aca pa caching 2025-11-14 15:04:06 -08:00
camierjs e6c9f73fc6 Use more informative variable names
Update README with equations and solver steps
2025-11-14 11:36:34 -08:00
Socratis Petrides 555d5771a6 Merge branch 'amgf' into contact-miniapp 2025-11-14 09:55:00 -08:00
Socratis Petrides e1d4c6cd40 fix petsc include 2025-11-14 09:54:34 -08:00
Socratis Petrides 147204ee70 readme edits 2025-11-13 17:28:15 -08:00
Socratis Petrides 4b8832190d cmake fixes 2025-11-13 17:23:55 -08:00
Socratis Petrides 69159b13a6 Merge branch 'amgf' into contact-miniapp 2025-11-13 16:54:05 -08:00
Ketan Mittal edbdf0628d Merge branch 'master' into mfem-bounds-const 2025-11-13 14:16:53 -08:00
Tzanio Kolev 7a9253d13c Merge branch 'master' into dfem-assemble-matrix 2025-11-13 13:18:20 -08:00
Victor DeCaria 1f8f2f1826 slight optimization 2025-11-13 12:55:05 -07:00
Victor DeCaria 1c9b9beb1e change logic for storing dof2quad tables 2025-11-13 12:44:57 -07:00
John Camier 20dcc1f991 Merge branch 'master' into mtop-gpu 2025-11-13 11:03:48 -08:00
Socratis Petrides 13252e4734 Fix formatting in CHANGELOG 2025-11-13 11:00:14 -08:00
camierjs 806ed1da96 Merge branch 'master' into isf 2025-11-13 10:58:38 -08:00
camierjs 1c486dbb39 Add sample runs, propagate new miniapps/fluids changes 2025-11-13 10:54:45 -08:00
camierjs 6f121c8abf Move ISF miniapp to fluids subdirectory 2025-11-13 10:53:52 -08:00
Julian Andrej 53a5c2f687 remove unused variables 2025-11-13 10:40:27 -08:00
Julian Andrej 6b279d6e32 initialize qp only once each thread 2025-11-13 10:22:21 -08:00
John Camier 032bf0f3dd Merge branch 'master' into fix-smem-pa-diffusion-apply-2d-indexing 2025-11-13 09:36:46 -08:00
John CamiercamierjsVladimir Z TomovMittal, Ketan <mittal3@llnl.gov>
50368046bc [TMOP] Simplify kernels (#3658)
* Simplify TMOP kernels, fix unit tests to run --all tests with adjusted tolerance

* make style

* Split TMOP h3s file with metrics

* TMOP kernel MFEM_HOST_DEVICE fix

* Cleanup TMOP CUDA kernels from base class

* Added TMOP PA metrics directory

* meld toward master

* [tmop] struct to class friends

* Simplify tmop file names

* make style

* Cleanup

* Style and vscode gitignore

* WIP resolve conflicts

* 2024 headers

* Tmop pass

* All tmop tests

* make style

* Add astyle to clang format

* make style

* Fix class visibility

* Include cleanup

* real_t pass

* style

* MFEM_REGISTER_KERNELS for TMOPAssembleGradPA_001

* make style

* add config files

* Update config

* metric_t

* wip with T

* wip

* wip T Specialization

* c++20, fmt make_format_args

* print types and values

* wip Kernel<decltype(M)>

* wip

* Working with metric_t, int, int

* C++20 ok

* C++17 cleaned

* Rename tmop files

* Sync TMOP kernels with dispatch

* make style

* Cleanup metrics

* Use TMOPKernel

* 3D metrics standalone

* Chdir assemble

* tmop 2d/3d directories

* TMOP assemble using specializations

* All TMOP kernel specializations

* MFEM_REPORT_KERNELS

* make style

* Sync with master

* Sync with master

* make style

* make style

* Removed 2d/3d TMOP sub-directories

* CMake TMOP file list update

* makefile directories order

* With style

* Re-enable vscode gitignore

* Fix merge conflicts

* make style

* Sync

* Meld toward master

* Changes toward master

* make style

* Meld back fem tmop files

* Fix TMOP_Integrator friends

* PA tests fix & history bump

* Cleanup test tmop and fix energy2 metric data

* Update copyright 2010-2025

* 2D energy metrics

* 3D energy metrics

* make style

* Simplify metric registration

* TMOP fem kernels with double buffering

* grad3, grad3_coef

* grad3_coef, grad3, mult3_coefs, mult3

* TMOP sm kernels tools

* Rename kernels smem and use regs

* Grad3 w/ vector reg grad

* Kernel register cleanup

* Add MAX_TMOP_1D and HIP tmop ctests

* Add kernels_foreach

* Add kernels foreach

* Prefix foreach_thread

* Kernels regs w/ foreach threads

* Swap Y and X in forward only

* Backward kernels_regs

* Use simplified grad3d

* Wip D1D Q1D

* Runtime D1D Q1D

* Remove T1D

* Cleanup

* AddKernelSpecializations

* Sync with SetMaxOf

* Rename to LoadDofs and use deduced templated parameters

* Grad2d & factorization

* Eval3d for grad3 coef

* Eval2d for grad2 coef

* Cleanup TMOP_SetupGradPA_C0_2D

* Use Bld and B

* Use other accessors

* TMOPAddMultPA3D

* TMOP_AddMultPA_C0_2D

* TMOP_AddMultGradPA_3D

* TMOP_AddMultGradPA_2D

* TMOP_AddMultGradPA_C0_3D

* AddMultGradPA_C0_2D

* TMOP_AssembleDiagonalPA_2D

* Wip TMOP_AssembleDiagonalPA_C0_3D

* TMOP_AssembleDiagonalPA_3D

* TMOP_MinDetJpr_3D

* TMOP_EnergyPA_C0_2D

* TMOPEnergyPA3D

* TMOP_TcIdealShapeGivenSize_3D

* TMOP_DatcSize_3D

* Remove MAX_TMOP_1D

* Remove smem kernels

* TMOP cleanup

* TMOP - solve for displacements #4694 changes

* Cleanup and move verifications

* Rename TMOP Assemble kernels

* Move kernel regs to TMOP pa

* make style

* Meld back toward master

* Meld back to master

* Use static constexpr

* Temporary branch-history

* Help msvc with namespaces

* MSVC inner static constexpr

* Move regs to mfem namespace

* MSVC all static constexpr

* TMOP_AssembleDiagPA_C0_3D w/o regs

* Avoid set but unused variable

* MSVC TMOP_AssembleDiagPA_C0_3D ternary test try

* MSVC MFEM_TMOP_REGISTER_MDQ_KERNEL

* Switch to MFEM_TMOP_MDQ_REGISTER

* MSVC help with static constexpr

* MSVC conversions try

* MSVC as_regs2d_ref

* MSVC Explicitly bind as reference

* MSCV with reinterpret_cast

* MSVC avoiding required l-values

* MSVC avoid explicit ref bindings

* MSVC avoid explicit ref bindings 2D

* Cleanup

* Enable MFEM_TMOP_PA_DEVICE with makefile

* TMOP tests w/o Kernel Specializations

* TMOP re-enable kernels specializations

* TMOP PA tests tolerances

* TMOP tests adjustments

* Fix transposed eval regs access

* MSVC remove not allowed dllimport definitions

* MSVC linalg vector warning fix

* MSVC avoiding definition of dllimport function not allowed

* Re-enable DetKernels specializations

* Sync latest TMOP changes

* TMOP PA tests normalization wip

* Sync TMOP tests

* Remove debug file

* Meld back toward master

* Add missing tmop make source dir

* tmop shadowing, CMake & make mpi tests

* TMOP periodic tests, shadowing fix

* TMOP pa mpi tests, fix shadowing

* TMOP tighten Square01 + Combo tests

* TMOP MSVC include ordering

* Revert TMOP MPI debug device tests

* Add TMOP_DatcSize_2D

* Use mfem::future for tensor

* Move TMOP PA specific kernels to sync'ed fem kernels

* makefile source dirs fix

* use explicit namespace to avoid clash (swap)

* Revert to MFEM_FOREACH_THREAD
Use scalar/vector regs types

* Sync kernels

* Sync kernels

* Avoid applying non-zero offset to null pointer runtime error

* Remove debug include

* TMOP rename coef to limit

* Comments.

* minor

* changelog

* Replace TMOP's MFEM_FOREACH_THREAD with MFEM_FOREACH_THREAD_DIRECT

* add some missing metric IDs

* Revert branch-history

* Add missing MFEM_SYNC_THREAD in kernels
Verify TMOP isfinite energy

* UseDevice for local vectors

* make style

* No grids in TMOP_DatcSize kernels

* Remove isfinite assertions
Cleanup unused header files
Add 3D energy finite verifications

* Filter out TMOP PA tests

---------

Co-authored-by: camierjs <camierjs@Io>
Co-authored-by: Vladimir Z Tomov <tomov2@llnl.gov>
Co-authored-by: Mittal, Ketan <mittal3@llnl.gov>
2025-11-13 08:47:32 -08:00
Mittal, Ketan 05d5294709 fix const-ness for bounding related methods 2025-11-12 23:09:44 -08:00
Julian Andrej 5ed263e9b4 try threadblocks 2025-11-12 16:56:36 -08:00
Ketan Mittal 7b85c04c72 Merge branch 'master' into particleset-particle-dev 2025-11-12 14:51:39 -08:00
camierjs 5450915b0b CHANGELOG mtop miniapp entry 2025-11-12 10:31:08 -08:00
camierjs 06d6bdbad0 Merge branch 'mtop-gpu' of github.com:mfem/mfem into mtop-gpu 2025-11-12 09:49:38 -08:00
camierjs c2955a8d52 Rename to GetSolutionVector & GetAdjointSolutionVector 2025-11-12 09:49:13 -08:00
John Camier 5b1de08790 Merge branch 'master' into mtop-gpu 2025-11-11 20:44:47 -08:00
John Camier 6d4492bad1 Merge branch 'master' into isf 2025-11-11 20:44:23 -08:00
Tzanio Kolev 19733980de Merge pull request #4692 from mfem/fix-issue-4455
Fix Mesh::MakeSimplicial for surface in 3D
2025-11-11 17:36:02 -08:00
John Camier 081124511d Merge branch 'master' into isf 2025-11-11 12:18:22 -08:00
Will Pazner 7e98c14f9b Fix copy and move semantics in DenseSymmetricMatrix 2025-11-11 11:28:20 -08:00
Julian Andrej 4528a608ea Merge branch 'master' into dfem-assemble-matrix 2025-11-11 11:24:58 -08:00
Socratis PetridesandWill Pazner 6f8d55d35f MFEM_VERIFY
Co-authored-by: Will Pazner <11493037+pazner@users.noreply.github.com>
2025-11-11 09:59:35 -08:00
Tom Stitt 459f93d71c flip indexing for DQ0/DQ1 so we don't overrun xy-block smem when MQ1>MD1 2025-11-11 09:53:13 -08:00
Joseph Signorelli 3be205bd4a minor doc fixes 2025-11-11 07:58:05 -06:00
blaz 1f20f8a232 removed device setup as it is not supported 2025-11-10 21:07:41 -08:00
Ketan Mittal 71699fe790 Merge branch 'master' into multivector-dev 2025-11-10 18:09:47 -08:00
Mittal, Ketan 269db5255b Merge branch 'master' of https://github.com/mfem/mfem into particleset-particle-dev 2025-11-10 18:09:17 -08:00
Socratis Petrides 25bf3e0235 style 2025-11-10 16:32:08 -08:00
Socratis Petrides 763ea10fd1 remove deprecated warning 2025-11-10 16:31:45 -08:00
Socratis Petrides 4439841f79 adjusting the contact driverr to the amgf changes 2025-11-10 16:28:53 -08:00
Socratis Petrides d834fd8cb3 Merge branch 'amgf' into contact-miniapp 2025-11-10 16:11:15 -08:00
Socratis Petrides 7f79fc8adc reviewers comments 2025-11-10 16:11:03 -08:00
Socratis Petrides 1b2ad14923 Merge branch 'amgf' into contact-miniapp 2025-11-10 14:45:40 -08:00
Socratis Petrides a6c6d63aa6 fix doxygen complaint 2025-11-10 14:45:24 -08:00
Socratis Petrides 95fc0b414f fix readme line length 2025-11-10 14:44:24 -08:00
Socratis Petrides 4f0b12acd6 Merge branch 'amgf' into contact-miniapp 2025-11-10 14:32:02 -08:00
Socratis Petrides 56689d3c0e fixing changelog line length 2025-11-10 14:31:44 -08:00
Socratis Petrides 04453a12c7 merge with amgf 2025-11-10 13:27:51 -08:00
Socratis Petrides 4a5f5f8528 Merge branch 'master' into amgf 2025-11-10 13:18:18 -08:00
Socratis Petrides e30c8b53d8 fixing comments 2025-11-10 13:17:55 -08:00
camierjs b497159d50 Remove used private field 2025-11-10 13:00:02 -08:00
camierjs 60e0bef7d3 Merge branch 'master' into mtop-gpu 2025-11-10 12:05:06 -08:00
camierjs f6d6148108 Fix comments 2025-11-10 12:04:58 -08:00
camierjs 7cda588460 Remove unused declarations 2025-11-10 12:03:52 -08:00
Tzanio Kolev 73afff37cc Merge pull request #5099 from farscape-project/restricted
Add {ND,RT}_R{1,2}D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] to FEC factory
2025-11-10 12:01:54 -08:00
Mittal, Ketan 5272af9702 skip documentation for kernel specialization 2025-11-10 11:49:29 -08:00
Mittal, Ketan 2898b4e900 fix greek symbols 2025-11-10 11:08:11 -08:00
camierjs c8d2c38b57 Merge branch 'master' into mtop-gpu 2025-11-10 08:31:17 -08:00
camierjs 2a24edf180 Use '-1' for 'all' BCs, add verifications
Add sample runs in mtop example
Remove unused code in mtop example
2025-11-10 08:31:01 -08:00
Julian Andrej 555ce60a73 style 2025-11-10 08:00:57 -08:00
Julian Andrej 76de06bf32 remove vectordivergence from gpu tests 2025-11-10 07:57:57 -08:00
Joseph Signorelli 5955c67c0e fix type check in MFEM_ASSERT 2025-11-09 18:33:43 -06:00
Joseph Signorelli c6d43016b1 style 2025-11-09 18:27:16 -06:00
Joseph Signorelli 9d9ce86383 Remove AddSpecialization since it is not functional 2025-11-09 18:25:18 -06:00
Joseph Signorelli 32cb62627a Cleanup + enhancements to new transfer capability 2025-11-09 18:25:01 -06:00
Tzanio Kolev 087a29249a Merge pull request #5096 from mfem/guard-raja-gpu-in-forall
raja+gpu forall guard
2025-11-09 14:09:11 -08:00
John Camier a68111620a Merge branch 'master' into isf 2025-11-09 07:37:04 -08:00
John Camier a5fd83b1fb Merge branch 'master' into mtop-gpu 2025-11-09 07:36:56 -08:00
blaz 8a02f2ab4a style 2025-11-09 00:15:52 -08:00
blaz 6ab2ede252 implementation of requested changes 2025-11-09 00:13:50 -08:00
Tzanio Kolev f2e841d7f9 Merge pull request #5084 from mfem/complex-gf-save-dev
Adding [Par]ComplexGridFuction::Save
2025-11-08 16:54:05 -08:00
Tzanio Kolev dcff876e94 Merge pull request #4979 from mfem/array-vector-improvements-dev
Improvements to Vector + Array
2025-11-08 16:53:41 -08:00
Will Pazner 4d042959e8 Add convenience function Mesh::IsMixedMesh 2025-11-08 14:03:37 -08:00
Joseph Signorelli 7ae91efec2 Only cast to size_t for int type comparison 2025-11-08 09:55:06 -06:00
Mittal, Ketan 48da9bad4f fix int comparison with size_t 2025-11-07 16:55:44 -08:00
Mittal, Ketan 4f04240372 cmake fix for navier-bifurcation 2025-11-07 16:51:12 -08:00
Mittal, Ketan caf484d910 modify navier bifurcation so that it can also be compiled without particles 2025-11-07 16:28:36 -08:00
Tzanio Kolev 19e4c53acd Merge branch 'master' into qf-project-gf-fallback 2025-11-07 13:34:56 -08:00
Cyrus 34b41058f9 style 2025-11-07 13:27:37 -08:00
Cyrus Harrison 9e48d1b618 Merge branch 'master' into task/2025_10_dc_quad_points 2025-11-07 13:08:08 -08:00
Joseph Signorelli a1c1e48404 Merge branch 'particleset-particle-dev' of github.com:mfem/mfem into particleset-particle-dev 2025-11-07 15:04:09 -06:00
Joseph Signorelli b94d258523 minor doc updates 2025-11-07 15:04:00 -06:00
Cyrus Harrison a4ffea2f8a add more entries to the mfem_root file for gf and qf 2025-11-07 13:03:00 -08:00
Mittal, Ketan bb8ff078fb restore transfer function 2025-11-07 12:59:51 -08:00
Mittal, Ketan d5098c8381 clean up and redistribute only particles not on same rank 2025-11-07 12:38:03 -08:00
Mittal, Ketan c007fb4ad4 cosmetic 2025-11-07 10:33:29 -08:00
Mittal, Ketan 5526a18358 cleanup and new transfer function 2025-11-07 09:16:31 -08:00
Tzanio Kolev 0555b6cc97 Merge pull request #4333 from mfem/fix-caliper-scope
Fix `MFEM_PERF_SCOPE` with latest Caliper
2025-11-06 06:25:45 -08:00
Mittal, Ketan 618c0233ea make style and some formatting changes 2025-11-05 13:47:42 -08:00
Mittal, Ketan 66b30e5492 merge particlevector branch 2025-11-05 13:17:29 -08:00
Mittal, Ketan 9f07a13589 fix makefile and add some ifdef guards 2025-11-05 12:41:53 -08:00
Tzanio Kolev ea329bca35 Merge pull request #5088 from mfem/fix-boris-dev
Fix Boris rotation term using pre-rotation momentum [fix-boris-dev]
2025-11-05 09:54:33 -08:00
Tzanio Kolev 78cd3d3838 Merge branch 'master' into multivector-dev 2025-11-05 09:53:34 -08:00
John Camier 2726959a2d Merge branch 'master' into fix-caliper-scope 2025-11-05 09:06:37 -08:00
Nuno Nobre 2fef8ca1f0 Add {ND,RT}_R{1,2}D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] to FEC factory 2025-11-05 15:27:18 +00:00
Joseph Signorelli 7da0f4c226 TestResize --> TestSetNumParticles for clarity 2025-11-05 08:24:08 -06:00
Joseph Signorelli 3681b6f6a2 Merge branch 'multivector-dev' of github.com:mfem/mfem into multivector-dev 2025-11-05 08:19:08 -06:00
Joseph Signorelli 021bf157a7 Documentation updates 2025-11-05 08:16:16 -06:00
Joseph Signorelli 0463749eb9 update_data --> keep_data 2025-11-05 08:16:07 -06:00
Mittal, Ketan b8443bf867 formatting fixes 2025-11-04 17:09:54 -08:00
Mittal, Ketan 793fd42653 minor 2025-11-04 16:59:11 -08:00
Joseph Signorelli 7e6034c4e2 potential fix to mem leak? 2025-11-04 18:10:00 -06:00
Mark L. Stowell 0f5b6cc0f5 Merge pull request #3525 from mfem/sjg/mesh-vis-dev
Mesh and DataCollection upgrades
2025-11-04 14:58:15 -08:00
Mittal, Ketan db4f95d252 Merge branch 'multivector-dev' of https://github.com/mfem/mfem into multivector-dev 2025-11-04 14:45:21 -08:00
Mittal, Ketan 40bed3db8f make style 2025-11-04 14:45:06 -08:00
Ketan Mittal 2f6c8b720c Merge branch 'master' into multivector-dev 2025-11-04 14:43:25 -08:00
Mittal, Ketan 44b8778af1 add update data to SetNumParticles and GrowSize 2025-11-04 14:42:43 -08:00
Mittal, Ketan 4311f1ab04 make style 2025-11-04 14:07:01 -08:00
Mittal, Ketan cc1a5774e1 add class documentation 2025-11-04 14:01:50 -08:00
Mittal, Ketan c1b7a529d6 resolve some documentation mix-up due to earlier merge 2025-11-04 13:43:11 -08:00
Mittal, Ketan 6dca8a1c54 merge upstream changes 2025-11-04 13:38:34 -08:00
Mittal, Ketan b0b21633b7 fix line wraps and add some checks 2025-11-04 13:36:54 -08:00
Joseph Signorelli 71aa4a366d Add update_data flag for SetOrdering + SetVDim 2025-11-04 15:21:29 -06:00
Joseph Signorelli 218880f7e1 add wrong_comp_count check to unit test 2025-11-04 15:14:23 -06:00
Joseph Signorelli 0c61b567a5 Merge branch 'array-vector-improvements-dev' into multivector-dev 2025-11-04 14:41:08 -06:00
Joseph Signorelli 55fb86f566 Merge branch 'master' into multivector-dev 2025-11-04 14:39:22 -06:00
Joseph Signorelli 94f2670dc0 style 2025-11-04 14:36:29 -06:00
Joseph Signorelli 749a04e7b1 MultiVector --> ParticleVector 2025-11-04 14:35:46 -06:00
Joseph Signorelli c6744e3f39 multivector --> particlevector file names 2025-11-04 14:21:22 -06:00
Joseph Signorelli 8a35a7843a style 2025-11-04 14:11:53 -06:00
Joseph Signorelli 967e811442 Keep existing data for SetVDim, with unit test 2025-11-04 14:11:24 -06:00
Joseph Signorelli 260f7c345b Add Get/SetComponents w/ unit test 2025-11-04 13:55:02 -06:00
Tzanio Kolev 058e3414b0 Merge branch 'master' into fix-caliper-scope 2025-11-04 11:51:58 -08:00
Joseph Signorelli 1bfe259d2a add test_ordering to cmakelists 2025-11-04 13:47:56 -06:00
Joseph Signorelli a4b5eb8bda Add unit test for getting + setting vector values 2025-11-04 13:47:27 -06:00
Joseph Signorelli 0fce771a08 rm file header duplication 2025-11-04 13:46:48 -06:00
Joseph Signorelli 331151d4f6 Create new file for test ordering 2025-11-04 13:46:22 -06:00
Julian Andrej 78b4b00359 remove unnecessary header 2025-11-04 11:31:09 -08:00
Mark L. Stowell e756067456 Merge branch 'master' into sjg/mesh-vis-dev 2025-11-04 11:23:49 -08:00
Veselin Dobrev aaf2d99de8 Merge pull request #5095 from mfem/product-coeff-gpu
Fix GPU test failure
2025-11-04 10:17:23 -08:00
Julian Andrej 9c110c6acb Merge branch 'master' into dfem-assemble-matrix
# Conflicts:
#	tests/unit/dfem/test_mass.cpp
2025-11-04 08:36:38 -08:00
Tzanio Kolev bcf5153192 Merge pull request #5001 from mfem/feature-gslib-fetch
Enable CMake fetching of GSLIB (and renaming of FETCH_TPLS, HYPRE_FETCH, and METIS_FETCH)
2025-11-04 07:53:01 -08:00
Tzanio Kolev 93066142ab Merge pull request #4993 from mfem/dfem-boundary-integrator
dFEM Boundary Integrator
2025-11-04 07:46:12 -08:00
Tzanio Kolev c523e620d9 make style 2025-11-04 07:32:43 -08:00
Joseph Signorelli eabce59758 Default vdim to 1 2025-11-04 08:09:02 -06:00
Joseph Signorelli 779a4e6c0b Move ordering to general in its own file 2025-11-04 08:07:26 -06:00
Joseph Signorelli acfa01c46c Add warning to Vector::DeleteAt for unique indices 2025-11-04 07:41:42 -06:00
Cyrus Harrison af36461372 add quad func support to visit and conduit data collections 2025-11-03 15:18:02 -08:00
Tom Stitt 47c39d5102 followup to #4923 so mfem built with raja gpu support can be used in a library that isn't using the device compiler 2025-11-03 14:33:43 -08:00
Tzanio Kolev 4c803a8214 Merge pull request #5073 from mfem/sundials-add-finalize
Add Finalize routine to Sundials class for precise control of cleanup
2025-11-03 09:40:06 -08:00
Joseph Signorelli 43231cb143 Revert "Move Ordering to multivector.hpp/cpp" and move Ordering::Reorder to fespace.cpp/hpp
This reverts commit 769d2914c8.
2025-11-03 11:19:34 -06:00
John Camier 89a4775928 Merge branch 'master' into isf 2025-11-02 08:50:03 -08:00
John Camier d235e23b6a Merge branch 'master' into mtop-gpu 2025-11-02 08:49:58 -08:00
John Camier 5f088873f3 Merge branch 'master' into dg-diffusion-pa-matrix-coeff 2025-11-02 08:49:46 -08:00
Will Pazner 7757c47fcf Add symmetric matrix test case to "PA DG Diffusion" 2025-11-01 14:06:21 -07:00
Will Pazner cd2e84fa1c Fix copy and move semantics in DenseSymmetricMatrix 2025-11-01 14:06:01 -07:00
Tzanio Kolev f460b547ea Merge pull request #4861 from mfem/vector-pa-kernels
Vector pa kernels
2025-11-01 12:00:56 -07:00
Tzanio Kolev fa13480fcf Merge pull request #5027 from mfem/batch-linalg-pivot
Change batched linalg to always use 1-based pivot indexing.
2025-11-01 11:59:38 -07:00
Tzanio Kolev 48683a7f02 Merge branch 'master' into dfem-boundary-integrator 2025-11-01 11:51:05 -07:00
Tom Stitt 97cb5abb35 switch to CALI_CXX_MARK_SCOPE 2025-10-31 14:48:45 -07:00
Tom Stitt f09ca5df3c Merge remote-tracking branch 'origin/master' into fix-caliper-scope 2025-10-31 13:47:11 -07:00
Joseph Signorelli 0db3e561a3 style 2025-10-31 14:43:34 -05:00
Will Pazner 9fb62e141c Add non-symmetric test to "PA DG Diffusion" 2025-10-31 12:32:45 -07:00
Joseph SignorelliandAndrew Ho 229c9d9fbe Use new scan wrappers
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-10-31 13:40:30 -05:00
Julian Andrej 58c6ac70c2 another fallback case 2025-10-31 09:20:53 -07:00
Will Pazner 7c38e8ca72 Add HostRead to unit test 2025-10-30 12:26:21 -07:00
Julian Andrej fdd909b132 typos and missing params 2025-10-30 08:34:43 -07:00
Julian Andrej 9222808f7c documentation 2025-10-30 08:23:20 -07:00
Julian Andrej 792c5b7617 revert to random input vector 2025-10-30 07:42:18 -07:00
Mark L. Stowell c18a0e470b Merge branch 'master' into complex-gf-save-dev 2025-10-30 00:06:41 -07:00
John Camier b9dc31be2b Merge branch 'master' into vector-pa-kernels 2025-10-29 19:36:56 -07:00
John Camier 2a23163240 Merge branch 'master' into isf 2025-10-29 19:36:46 -07:00
John Camier 09e4b7296e Merge branch 'master' into mtop-gpu 2025-10-29 19:36:36 -07:00
Stowell, Mark L. 6f848e00f8 More file closures 2025-10-29 15:13:30 -07:00
Stowell, Mark L. ce3c13781b Improving documentation 2025-10-29 14:41:06 -07:00
Stowell, Mark L. 41378e2fa3 Closing files in unit test 2025-10-29 14:40:36 -07:00
Veselin Dobrev f19768cc70 Merge pull request #4952 from mfem/hughcars/race-condition-fix
Fix OpenMP race conditions
2025-10-29 14:03:50 -07:00
Julian Andrej 6e67b690d9 change fallback mechanism 2025-10-29 11:29:48 -07:00
Tzanio Kolev 5e40324e5d Merge branch 'master' into vector-pa-kernels 2025-10-29 11:24:34 -07:00
Tzanio Kolev a39d48748d Merge pull request #5063 from mfem/product-coeff-gpu
Implement Coefficient::Project for sum, product, and ratio coefficients
2025-10-29 11:21:41 -07:00
Tzanio Kolev 56a122e86a Merge pull request #5086 from mfem/artv3/qfunction-temp-memory
QuadratureFunction: add constructor that takes device memory type
2025-10-29 11:21:25 -07:00
Stowell, Mark L. 49d57f4f6f Correcting for loop range 2025-10-29 11:10:15 -07:00
Julian Andrej 09bff27a2c remove clang format 2025-10-29 08:30:57 -07:00
Julian Andrej 8001d0b75a make style 2025-10-28 16:49:45 -07:00
Julian Andrej 2bebd0e4a4 relax norms properly 2025-10-28 16:47:16 -07:00
Stowell, Mark L. 525ef99753 Merge branch 'complex-gf-save-dev' of github.com:mfem/mfem into complex-gf-save-dev 2025-10-28 16:20:43 -07:00
Stowell, Mark L. e6180f56ee Documentation updates and improved error reporting 2025-10-28 16:20:24 -07:00
Mark L. Stowell 132de1fa57 Merge branch 'master' into complex-gf-save-dev 2025-10-28 14:55:21 -07:00
Stowell, Mark L. e1a35d929c Adding unit test (and bug fixes that it found) 2025-10-28 14:27:33 -07:00
Julian Andrej 3154767cc5 relax tolerance to 2*eps 2025-10-28 13:10:35 -07:00
Chris Vogl ac84807be1 merged in master with conflict resolution in FindHypre 2025-10-28 12:48:35 -07:00
Chris Vogl 9601a02547 added deprecation warning for FETCH_TPLS as requested by @kmittal2 2025-10-28 12:36:10 -07:00
Chris Vogl b9bebd0921 add changes suggested by @nmnombre and @k-collie to support default and enivornment C flags for GSLIB 2025-10-28 12:24:27 -07:00
Andrew Ho d276ad6aff Merge branch 'master' into array-vector-improvements-dev 2025-10-28 11:48:12 -07:00
Julian Andrej f4f4889a4c fix memory leak 2025-10-28 11:42:28 -07:00
Mark L. Stowell f3aeae7b04 Merge branch 'master' into sjg/mesh-vis-dev 2025-10-28 11:23:14 -07:00
Julian Andrej 7850d9d0a9 integration rule 2025-10-28 10:57:20 -07:00
John Camier 2944bc5ea3 Merge branch 'master' into product-coeff-gpu 2025-10-28 10:19:40 -07:00
John Camier 2eb244fb21 Merge branch 'master' into vector-pa-kernels 2025-10-27 18:19:18 -07:00
John Camier 1d4029bcd8 Merge branch 'master' into isf 2025-10-27 18:19:05 -07:00
John Camier decc2f695d Merge branch 'master' into mtop-gpu 2025-10-27 18:18:56 -07:00
Julian Andrej 83330e2639 revert debug device 2025-10-27 16:17:01 -07:00
Julian Andrej 5889c2155e use existing matrix comparison test - gpu still failing 2025-10-27 15:38:10 -07:00
Andrew Ho c817c3cdbe Merge branch 'master' into batch-linalg-pivot 2025-10-27 13:17:02 -07:00
Julian Andrej a24b5edcf0 move AddIntegrator to public for nvcc 2025-10-27 08:24:03 -07:00
Julian Andrej b47f3e52a3 add fallback return for dispatch 2025-10-27 08:09:16 -07:00
Julian Andrej 16694dd1c7 Merge branch 'dfem-assemble-matrix' of github.com:mfem/mfem into dfem-assemble-matrix 2025-10-27 08:04:01 -07:00
Julian Andrej e8c3dc1e8f explicit casts 2025-10-27 08:03:51 -07:00
Julian Andrej 8a6ac41d69 slight norm comparison discrepancy remaining on rt-2d-q3 2025-10-27 08:03:43 -07:00
Rushan Zhang 93f3b73790 fix mistake with Boris 2025-10-26 18:55:34 -04:00
Tzanio Kolev 4870ecd351 Merge branch 'master' into dfem-boundary-integrator 2025-10-26 11:41:08 -07:00
Tzanio KolevandJohn Camier 5ed9f34dd8 Update tests/unit/dfem/test_mass.cpp
Co-authored-by: John Camier <camierjs@gmail.com>
2025-10-26 11:40:47 -07:00
Tzanio Kolev 41c8a8e65e Merge pull request #5051 from mfem/cmake-gpu
Change CMake CUDA_ARCH behavior
2025-10-26 11:39:33 -07:00
Tzanio Kolev a2025d7693 Merge pull request #5018 from mfem/parallel-scan
Additional parallel scan wrappers
2025-10-26 11:39:08 -07:00
camierjs ae629dda70 Fix typos 2025-10-25 11:53:33 -07:00
John Camier 70439dcb84 Merge branch 'master' into dfem-boundary-integrator 2025-10-25 11:27:22 -07:00
John Camier 1485fedec9 Merge branch 'master' into product-coeff-gpu 2025-10-25 11:26:27 -07:00
camierjs 2bad979cac Add GPU tags for dFEM diffusion, divergence and lvector parallel unit tests 2025-10-24 18:05:58 -07:00
Sohail Reddy 1ead635557 checked variable name 2025-10-24 17:13:29 -07:00
Sohail Reddy c903edd931 Merge branch 'master' into user-inner-prod 2025-10-24 17:01:51 -07:00
Sohail Reddy d8cd8f4f37 added doc for inner product operators 2025-10-24 17:01:11 -07:00
Arturo Vargas 4c9769a927 add contructor that takes device memory type 2025-10-24 11:13:33 -07:00
Julian Andrej b83a4184b5 guard weight instantiation 2025-10-24 10:00:51 -07:00
Julian Andrej 042762701b reorganize dimensional dispatch 2025-10-24 10:00:38 -07:00
Julian Andrej 3b6532e7bb style 2025-10-24 10:00:26 -07:00
Andrew Ho 8731dd171e old comment 2025-10-24 09:23:34 -07:00
Andrew Ho 45b1b3c535 Merge remote-tracking branch 'base/parallel-scan' into parallel-scan 2025-10-24 09:18:59 -07:00
Andrew Ho d450cc563c missing header 2025-10-24 09:17:45 -07:00
Andrew Ho 4083fcc4b4 Merge branch 'master' into parallel-scan 2025-10-24 09:10:12 -07:00
John Camier f27a7c5612 Merge branch 'master' into vector-pa-kernels 2025-10-24 08:57:15 -07:00
John Camier e9f0e86d61 Merge branch 'master' into isf 2025-10-24 08:57:04 -07:00
John Camier 9e058cbfe9 Merge branch 'master' into mtop-gpu 2025-10-24 08:56:55 -07:00
Mark L. Stowell 1027c6a9a8 Merge branch 'master' into complex-gf-save-dev 2025-10-24 08:06:34 -07:00
Stowell, Mark L. 7843bfe46a Fixing CI errors 2025-10-24 08:05:44 -07:00
Chris VoglandNuno Nobre 71aa2f0985 Update config/cmake/modules/FindGSLIB.cmake
adding a make clean command to GSLIB fetching to ensure it is rebuilt when CMake configuration is changed... also added -O2 -fPIC flags when building a shared library

Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2025-10-23 23:45:33 -07:00
Boyan Lazarov 8afc72d0f5 Merge branch 'master' into imex-conv-diff-dg 2025-10-23 22:49:20 -07:00
blaz b598b81496 using only the interface of TimeDependentOperator 2025-10-23 22:46:57 -07:00
Andrew Ho 35f0d5bfe1 complex densemat pivot fix 2025-10-23 18:59:24 -07:00
Tzanio Kolev 70a3355e90 Merge pull request #4431 from mfem/woptim/leverage-radiuss-ci
Leverage radiuss CI + add tioga
2025-10-23 18:26:33 -07:00
Stowell, Mark L. a6b1eff541 Ignoring new output files 2025-10-23 17:51:51 -07:00
Stowell, Mark L. 893c28bff3 Testing Save in parallel 2025-10-23 17:45:35 -07:00
Stowell, Mark L. 207fcd2fb6 Adding Save to ParComplexGridFunction 2025-10-23 17:45:19 -07:00
Stowell, Mark L. d312108f14 Testing serial Save function 2025-10-23 17:44:57 -07:00
Stowell, Mark L. 409aa1cba7 Adding Save to ComplexGridFunction 2025-10-23 17:44:39 -07:00
Veselin Dobrev 454a215175 More tweaks in Gitlab CI 2025-10-23 16:21:29 -07:00
Tom Stitt 8af606b848 forgot static 2025-10-23 13:30:16 -07:00
Tom Stitt 9a2bbec272 one more change 2025-10-23 13:29:48 -07:00
Sohail Reddy 57f4be30c5 Made InnerProductOperator::Dot virtual 2025-10-23 10:13:12 -07:00
Tom Stitt 9b9a6cfe34 missing one 2025-10-22 21:37:36 -07:00
Will Pazner 579f88a06d Add unit test for PA DG diffusion with matrix coefficients 2025-10-21 15:52:42 -07:00
Will Pazner 69650e350b Add support for PA DG diffusion with matrix coefficients 2025-10-21 15:52:42 -07:00
Will Pazner 9604a64178 Add unit test for PA with MatrixConstantCoefficient 2025-10-21 15:52:42 -07:00
Will Pazner 4733235d42 Fix bug in CoefficientVector::Project(MatrixCoefficient&, bool) when tranpose is true 2025-10-21 15:52:42 -07:00
Will Pazner c6d455699d Add support for constant matrix coefficients in PA diffusion integrator 2025-10-21 15:52:42 -07:00
camierjs 2658a312dd Fix documentation typo 2025-10-21 12:12:12 -07:00
Andrew Ho dbea0c3a0b Merge branch 'master' into batch-linalg-pivot 2025-10-21 11:40:47 -07:00
camierjs 52dffe5f8a Add visualization keys 2025-10-21 11:15:02 -07:00
Sohail Reddy 96a1cf8c93 updated vectors in WeightedInnerProduct to use different weighting operators 2025-10-21 10:18:11 -07:00
John Camier 8fbd815ad9 Merge branch 'master' into dfem-assemble-matrix 2025-10-21 10:09:46 -07:00
John Camier 86c103ad23 Merge branch 'master' into mfem-4.9-dev 2025-10-21 09:03:15 -07:00
Andrew HoandJohn Camier a447a33528 Update tests/unit/general/test_scan.cpp
Co-authored-by: John Camier <camierjs@gmail.com>
2025-10-21 08:34:02 -07:00
Andrew HoandJohn Camier 79f2ce2540 Update tests/unit/general/test_scan.cpp
Co-authored-by: John Camier <camierjs@gmail.com>
2025-10-21 08:33:54 -07:00
Sohail Reddy a109493821 fixed MPI variable initialization 2025-10-20 12:57:57 -07:00
Veselin Dobrev 32ce005ca0 Try to fix Gitlab CI 2025-10-20 09:42:21 -07:00
John Camier 3f0e146fb6 Merge branch 'master' into hughcars/race-condition-fix 2025-10-20 09:36:06 -07:00
John Camier 87c5e59228 Merge branch 'master' into parallel-scan 2025-10-20 09:35:18 -07:00
John Camier 9e91bfb376 Merge branch 'master' into cmake-gpu 2025-10-20 09:09:33 -07:00
Sohail Reddy 253a5b4b79 Added operator weighted inner product. Moved inner product defintions to solvers.hpp/cpp 2025-10-19 13:37:39 -07:00
John Camier b9105ca3e8 Merge branch 'master' into product-coeff-gpu 2025-10-19 10:31:42 -07:00
camierjs 90379a4792 mtop_test_iso_elasticity mesh file directory and output numbers 2025-10-19 10:24:33 -07:00
camierjs 5f33d92ca8 Move mtop data meshes to miniapp folder 2025-10-19 09:30:40 -07:00
John Camier fdb4368212 Merge branch 'master' into vector-pa-kernels 2025-10-19 09:27:54 -07:00
John Camier d911f7986a Merge branch 'master' into isf 2025-10-19 09:27:44 -07:00
John Camier c04e52bc6a Merge branch 'master' into mtop-gpu 2025-10-19 09:27:35 -07:00
Sohail Reddy 1808ca1f3a Added class for user-defined inner products. Updated IterativeSolver to use custom inner products. 2025-10-19 01:14:50 -07:00
Socratis Petrides f492c5c70d add README 2025-10-18 19:15:39 -07:00
Socratis Petrides 474d9811dd minor edits in the driver 2025-10-17 21:19:39 -07:00
Socratis Petrides 9a2ef1263f contact-miniapp: squash all changes since branching from master 2025-10-17 18:39:55 -07:00
Veselin Dobrev c34de48041 In Gitlab CI, use a longer timeout for the Matrix pipeline 2025-10-17 16:34:20 -07:00
Will Pazner 6602df4fd2 In QuadratureInterpolator::SupportsFESpace, return false for mixed meshes or variable orders
In QuadratureFunction::ProjectGridFunction unit test, test (element) QuadratureSpace also.
2025-10-17 14:58:46 -07:00
Will Pazner e9ca0960f7 Add fix for Mesh::MakeHigherOrderSimplicial_ on surface meshes 2025-10-17 09:15:45 -07:00
Will Pazner 8c236973c7 Merge remote-tracking branch 'origin/master' into fix-issue-4455
# Conflicts:
#	tests/unit/mesh/test_mesh.cpp
2025-10-17 09:15:35 -07:00
Will Pazner 8d0a2d4336 Use star-surf in 'MakeSimplicial Surface Mesh' unit test 2025-10-17 08:57:35 -07:00
Veselin Dobrev 0694668c24 In Gitlab CI, update the radiuss/radiuss-shared-ci ref to the latest
release tag, v2025.09.1.
2025-10-16 20:20:33 -07:00
Veselin Dobrev 3492b20c70 Try to fix reporting in Gitlab CI 2025-10-16 19:47:47 -07:00
Socratis Petrides 033b29edfc unit test fix
adjusting tol
loosen the iteration requirement
fix int64
mem leak fix
one more fix for BigInt
another fix for int64
2025-10-16 19:27:25 -07:00
Veselin Dobrev 5fec899806 Try to fix reporting in Gitlab CI 2025-10-16 16:13:26 -07:00
Veselin Dobrev fc010f0423 In Gitlab CI, test why reporting does not work 2025-10-16 13:44:02 -07:00
Socratis Petrides 6bffbc1d2e Merge branch 'master' into amgf 2025-10-16 12:45:47 -07:00
Socratis Petrides ec6937c286 Merge branch 'amgf' of github.com:mfem/mfem into amgf 2025-10-16 12:45:38 -07:00
Socratis Petrides acc1b3a28c adding a small test for amgf 2025-10-16 12:44:52 -07:00
Adrien Bernede a9427e2663 Merge branch 'master' into woptim/leverage-radiuss-ci 2025-10-15 22:07:09 -07:00
Tzanio Kolev 72deb3b12c Merge branch 'master' into woptim/leverage-radiuss-ci 2025-10-15 16:09:08 -07:00
Will Pazner 63d983ed6a Add unit test for QuadratureFunction::ProjectGridFunction 2025-10-15 14:54:25 -07:00
Will Pazner 59a95cc8e6 In QuadratureFunction::ProjectGridFunction, fall back earlier
GetElementRestriction or GetFaceRestriction may fail in unsupported cases.

In the case of FaceQuadratureSpace, fall back on non-tensor meshes since
ElementDofOrdering::NATIVE is not supported in the restriction operator.
2025-10-15 14:52:36 -07:00
Jan Nikl 440d552e85 Merge branch 'master' into qf-project-gf-fallback 2025-10-15 10:04:34 -07:00
Julian Andrej 67d31a13fd Merge branch 'dfem-boundary-integrator' of github.com:mfem/mfem into dfem-boundary-integrator 2025-10-15 08:29:49 -07:00
Julian Andrej 0320e5a476 fix for ordering in tensor transform 2025-10-15 08:29:17 -07:00
John Camier 91e56806bd Merge branch 'master' into mtop-gpu 2025-10-15 07:46:42 -07:00
Tzanio Kolev 041b4ab958 Merge branch 'master' into dfem-boundary-integrator 2025-10-15 06:53:16 -07:00
Cody Balos 3374851918 whitespace 2025-10-14 16:48:32 -07:00
Cody Balos 46a44c5241 add Sundials::Finalize routine to allow for precise control of cleanup 2025-10-14 16:43:44 -07:00
John Camier b61771199b Merge branch 'master' into isf 2025-10-14 16:29:07 -07:00
camierjs 641692ac4a Merge branch 'master' into vector-pa-kernels 2025-10-14 16:28:30 -07:00
camierjs f97bfdea7e Cleanup and remove unused changes 2025-10-14 16:26:10 -07:00
Andrew Ho 159c89542b formatting 2025-10-14 15:40:08 -07:00
camierjs 3bfa5f0e37 [MTOP] solver with GPU support 2025-10-14 15:37:14 -07:00
Andrew Ho 37fc5a3d5c Merge branch 'master' into cmake-gpu 2025-10-14 11:42:48 -07:00
Andrew Ho 6fad311e11 Merge branch 'master' into parallel-scan 2025-10-14 11:41:16 -07:00
Dylan Copeland fcdc9c43d5 Fix uninitialized variables. 2025-10-14 11:24:34 -07:00
Julian Andrej d32a63171f trying to figure out attribute arrays, 3d boundary jacobians failing 2025-10-14 09:53:43 -07:00
Veselin Dobrev 4fdf2cdda1 In Gitlab CI, remove the explicit suppression of CUSPARSE warnings 2025-10-13 19:58:42 -07:00
Veselin Dobrev 0af0b9b420 Merge branch 'master' into woptim/leverage-radiuss-ci 2025-10-13 19:57:33 -07:00
Julian Andrej 3c0f1d783e fix 1d case 2025-10-13 15:04:56 -07:00
Julian Andrej cd55bcc67b fix attempt 2025-10-13 13:44:46 -07:00
Julian Andrej 689288e18d fix unit tests, failing 2d derivatives 2025-10-13 09:29:07 -07:00
John Camier 01afc1685a Merge branch 'master' into vector-pa-kernels 2025-10-13 08:12:57 -07:00
John Camier 2747db62a0 Merge branch 'master' into isf 2025-10-13 08:12:49 -07:00
Tzanio Kolev 1dd816d11f Merge branch 'master' into mfem-4.9-dev 2025-10-12 16:57:16 -07:00
Veselin Dobrev 4501988a3b Try to fix Gitlab CI 2025-10-10 21:54:49 -07:00
Veselin Dobrev cd82063118 Try to fix Gitlab CI 2025-10-10 20:57:43 -07:00
Veselin Dobrev cac3f72e9e Try to fix Gitlab CI 2025-10-10 19:56:54 -07:00
Veselin Dobrev d93a030431 Updates in Gitlab CI 2025-10-10 19:04:07 -07:00
Veselin Dobrev 8cfd5e0a07 Update the mfem-uberenv commit hash 2025-10-10 17:29:47 -07:00
Veselin Dobrev 63110738d4 In Gitlab CI replace Lassen with Matrix 2025-10-10 17:25:53 -07:00
John Camier c05d58a9c2 Merge branch 'master' into vector-pa-kernels 2025-10-10 08:01:57 -07:00
Will Pazner 2647e9ef3d Implement Coefficient::Project for sum, product, and ratio coefficients
Add unit test to compare with Coefficient::Eval
2025-10-09 23:03:58 -07:00
Andrew Ho 0efc76834a Merge branch 'master' into batch-linalg-pivot 2025-10-08 16:09:48 -07:00
Andrew Ho 1311729f79 needs to be uncommented to actually work 2025-10-08 16:08:26 -07:00
camierjs 6cb1f3a11d Restrict identifiers characters 2025-10-08 15:21:49 -07:00
camierjs 8e8f7a5933 Avoid visualization in ISF tests 2025-10-08 14:40:40 -07:00
camierjs 94e5520fc4 Fix CMake pschrodinger_flow tests 2025-10-08 14:09:49 -07:00
camierjs 9e2abb4907 Add Incompressible Schrödinger Flow miniapp make tests 2025-10-08 13:43:45 -07:00
camierjs b011eea5ac Single precision device sincos fix 2025-10-08 13:23:16 -07:00
camierjs 82a69bb15b Allow single build 2025-10-08 13:10:38 -07:00
Boyan Lazarov 78034273ea Merge branch 'master' into imex-conv-diff-dg 2025-10-08 12:31:14 -07:00
blaz fd6fb0c7be style 2025-10-08 12:30:34 -07:00
camierjs 718fe7a898 Update miniapps/solvers README 2025-10-08 12:29:56 -07:00
camierjs 0a4930e714 Merge branch 'master' into isf 2025-10-08 12:02:15 -07:00
camierjs 05eb28b889 Simplify complex functions 2025-10-08 12:01:53 -07:00
Andrew Ho 526f73688e Merge branch 'master' into dfem-boundary-integrator 2025-10-08 11:34:42 -07:00
Andrew Ho aa0a33f51b workaround for umpire and BLT using the deprecated CMake CUDA integration 2025-10-08 10:22:08 -07:00
blaz e1b547ef97 small modification 2025-10-06 23:00:38 -07:00
Andrew Ho 34abf22be5 Merge branch 'master' into batch-linalg-pivot 2025-10-06 11:38:43 -07:00
Andrew Ho aa35d62e28 fix compatibility with CMAKE_HIP_ARCHITECTURES 2025-10-06 10:31:50 -07:00
Tzanio Kolev 0a55df91f1 Initial changes for mfem-4.9 2025-10-06 07:51:32 -07:00
Tzanio Kolev bfa80426bb Merge branch 'master' into amgf 2025-10-03 09:28:03 -07:00
John Camier 0d6eced899 Merge branch 'master' into vector-pa-kernels 2025-10-03 08:18:05 -07:00
John Camier e336658f8c Merge branch 'master' into hughcars/race-condition-fix 2025-10-02 15:31:59 -07:00
Andrew Ho b276939d69 Merge branch 'master' into parallel-scan 2025-10-02 13:23:25 -07:00
Veselin Dobrev 324ab0684e Merge branch 'master' into woptim/leverage-radiuss-ci 2025-10-02 12:33:03 -07:00
Veselin Dobrev c5efd6a06a Gitlab CI: replace Ruby with Dane 2025-10-02 11:57:54 -07:00
Anthony 435e1dfe3c Merge branch 'master' into imex-conv-diff-dg 2025-10-02 18:51:19 +00:00
Andrew Ho 626cb41c1b Merge branch 'master' into cmake-gpu 2025-10-02 11:47:59 -07:00
Veselin Dobrev 0c167ab5e7 Merge pull request #4684 from mfem/leverage-radiuss-ci--updates
Updates for the branch `woptim/leverage-radiuss-ci`
2025-10-02 11:33:47 -07:00
Hugh Carson 944217e7f3 Merge branch 'master' into hughcars/race-condition-fix 2025-10-02 12:20:20 -04:00
Hugh Carson bd31eba056 Revert "Introduce DenseMatrix::NewMemoryAndSize and use to reallocate buffer"
This reverts commit 94b80fcd3c.
2025-10-02 12:18:59 -04:00
Andrew Ho eea5d48c4e Change CUDA_ARCH so it behaves like CMAKE_CUDA_ARCHITECTURES while still supporting the previous usage. 2025-10-01 11:08:02 -07:00
John Camier 5637c98f50 Merge branch 'master' into vector-pa-kernels 2025-09-30 11:35:05 -07:00
John Camier fe7cce14b7 Merge branch 'master' into vector-pa-kernels 2025-09-29 07:45:27 -07:00
Hugh Carson 94b80fcd3c Introduce DenseMatrix::NewMemoryAndSize and use to reallocate buffer 2025-09-26 10:12:51 -04:00
John Camier 3d6c5ebd71 Merge branch 'master' into vector-pa-kernels 2025-09-25 15:58:31 -07:00
John Camier 6e2148a698 Merge branch 'master' into hughcars/race-condition-fix 2025-09-25 15:58:14 -07:00
Andrew Ho 20caca8333 Merge branch 'master' into parallel-scan 2025-09-25 15:24:03 -07:00
Andrew Ho 37ebeed499 Merge branch 'master' into batch-linalg-pivot 2025-09-25 15:23:38 -07:00
Joseph Signorelli 1f6afa0f87 Use up-to-date scan 2025-09-25 09:34:41 -05:00
Joseph Signorelli a18d36e841 Merge branch 'master' into array-vector-improvements-dev 2025-09-25 09:24:32 -05:00
Socratis Petrides a1b9ad1403 update comment 2025-09-24 17:09:28 -07:00
Socratis Petrides 47c85401cd adding comments 2025-09-24 16:58:35 -07:00
Socratis Petrides 2aa40b53e4 Changelog 2025-09-24 16:57:42 -07:00
Tzanio Kolev 792af80eac Merge branch 'master' into dfem-assemble-matrix 2025-09-24 08:12:52 -07:00
Andrew Ho dd21f5469c Merge branch 'master' into batch-linalg-pivot 2025-09-23 19:46:10 -07:00
Andrew Ho 1ee9cbcc43 Merge branch 'master' into parallel-scan 2025-09-23 16:30:40 -07:00
Andrew Ho 9c56bc4d9e Merge remote-tracking branch 'base/batch-linalg-pivot' into batch-linalg-pivot 2025-09-23 14:40:16 -07:00
Andrew Ho 229e1b1bc6 also need to change the base LUFactors class to 1-based 2025-09-23 14:39:32 -07:00
Andrew Ho c2b508c5fc Merge branch 'master' into batch-linalg-pivot 2025-09-23 14:05:13 -07:00
Andrew Ho 7df869974d Change batched linalg to always use 1-based pivot indexing.
This makes the native backend consistent with vendors and MAGMA
2025-09-23 14:02:03 -07:00
camierjs 737d3d6f2b Options & sample runs 2025-09-23 11:38:11 -07:00
camierjs c5f0b5dee1 Merge branch 'master' into isf 2025-09-23 09:56:30 -07:00
Julian Andrej 593622ae88 host/device bug 2025-09-23 09:48:57 -07:00
Julian Andrej 256821fb6c maybe unused annotations 2025-09-23 09:48:47 -07:00
Julian Andrej cffcd4ea89 hypre gpu fix 2025-09-23 09:48:39 -07:00
John Camier 30859f614d Merge branch 'master' into sjg/mesh-vis-dev 2025-09-23 08:00:27 -07:00
Julian Andrej 66dfc9352f nvcc fixes 2025-09-22 11:58:55 -07:00
Julian Andrej 4cd6c1fbfb add matrix assemble and amg option to minsurface miniapp 2025-09-22 10:06:04 -07:00
camierjs b73ae540e1 Fix DO_NOT_DOCUMENT cond 2025-09-22 07:50:13 -07:00
camierjs 56615c8fc3 Fix VectorDiffusionIntegrator AddMultPA definitions 2025-09-21 20:42:39 -07:00
camierjs 5e99a705c2 Merge branch 'master' into vector-pa-kernels 2025-09-21 20:42:38 -07:00
John Camier 8d4819b143 Merge branch 'master' into hughcars/race-condition-fix 2025-09-21 20:13:30 -07:00
camierjs 9d1fe4ba6b Fix FormLinearSystem unit test merge duplicate 2025-09-21 10:58:22 -07:00
camierjs 8147aeba7d Merge branch 'master' into vector-pa-kernels 2025-09-21 10:54:17 -07:00
John Camier 78afa5d313 Merge branch 'master' into table-array 2025-09-21 10:31:08 -07:00
John Camier ce3f215d75 Merge branch 'master' into hughcars/race-condition-fix 2025-09-21 10:15:00 -07:00
Julian Andrej 47ab067ead Merge branch 'master' into dfem-assemble-matrix 2025-09-19 10:17:29 -07:00
Toni-ko 5872250edc Trying to fix some failing checks 2025-09-19 09:39:04 -07:00
Julian Andrej 72682e3f46 assemble methods and unit tests 2025-09-19 09:16:26 -07:00
Toni-ko b41d2681ac fix for some failing checks 2025-09-18 17:26:33 -07:00
Toni-ko 5840edb3f5 Added option to use continuous elements 2025-09-18 17:09:03 -07:00
Toni-ko 668156f7c4 style update 2025-09-18 16:37:54 -07:00
Toni-ko fad7b3c0fd set problem type using template, along with other small modifications 2025-09-18 16:37:35 -07:00
blaz f2918eda9e small mod 2 2025-09-18 11:09:23 -07:00
blaz 6989bc3899 small modifications 2025-09-18 11:01:23 -07:00
Andrew Ho 8edf90f72a Merge branch 'master' into parallel-scan 2025-09-17 10:30:30 -07:00
Andrew Ho 28590b2026 Added conditional copy/compaction wrappers
Speed up CMake unit test building
2025-09-15 15:43:25 -07:00
Andrew Ho 5429e98bb3 Merge branch 'master' into parallel-scan 2025-09-15 11:48:03 -07:00
Julian Andrej 0e1e7ac5c9 guard more logical expression 2025-09-14 11:54:15 -07:00
Julian Andrej 1c03e51342 doc 2025-09-14 11:52:11 -07:00
Julian Andrej dea236d9ff more warnings 2025-09-14 11:50:46 -07:00
Julian Andrej 90e9ec2d50 Merge branch 'dfem-boundary-integrator' of github.com:mfem/mfem into dfem-boundary-integrator 2025-09-14 11:18:01 -07:00
Julian Andrej b9da92c5a1 logical expression warnings 2025-09-14 11:17:50 -07:00
camierjs 4849db33bf Update gitignore 2025-09-09 11:29:50 -07:00
EB Chin fb7466c819 Merge branch 'master' into dfem-sum-fixes 2025-09-08 10:46:59 -07:00
Eric B. Chin b9f1d5151d change mfem_abort to mfem_abort_kernel 2025-09-03 17:34:18 -07:00
Eric B. Chin 35bdeaaf30 fixes for sum fieldoperator 2025-09-03 17:29:59 -07:00
Chris VoglandNuno Nobre d84095af57 specify MPI flag for fetched GSLIB build
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2025-09-03 14:57:05 -07:00
Chris Vogl 8dba8024a1 added MFEM_ prefix to fetch variables; added GSLIB default; changed naming 2025-09-02 16:05:18 -07:00
Chris Vogl 41151d5fe9 added functionality to fetch GSLIB 2025-09-02 16:05:18 -07:00
Hugh Carson 3dfe67a219 Merge branch 'master' into sjg/mesh-vis-dev 2025-09-02 12:11:44 -04:00
Hugh Carson 36e2c896e4 Fix race condition in DofToQuad 2025-09-02 12:10:27 -04:00
Hugh Carson 1fa6c1ded9 Add thread safe buffer version of operator() for DenseTensor 2025-09-02 12:10:27 -04:00
Tzanio Kolev 419de5c398 Merge branch 'master' into dfem-boundary-integrator 2025-08-31 15:33:20 -07:00
Toni-ko 9bb4bbb87a update gitignore 2025-08-25 17:02:51 -07:00
Toni-ko b2be14cfe4 fixed option parser issue 2025-08-25 15:41:33 -07:00
Socratis Petrides 1e62733c26 include memory 2025-08-25 15:15:33 -07:00
Toni-ko 6505a97745 added an override mark to mult1 2025-08-25 15:14:51 -07:00
Socratis Petrides 7ab83365aa another fix for serial builts 2025-08-25 14:57:40 -07:00
Toni-ko 1ef3beec81 fixes for tests 2025-08-25 14:54:22 -07:00
Toni-ko d66004b3b3 code style 2025-08-25 14:42:15 -07:00
Toni-ko 41985bcbce fixes for tests 2025-08-25 14:42:01 -07:00
Socratis Petrides 60100a336e fix serial built 2025-08-25 13:48:47 -07:00
Toni-ko c9b6d0c9d2 Fixed some unused variable issues 2025-08-25 12:36:19 -07:00
Toni-ko e016ca926a code style 2025-08-25 11:44:54 -07:00
Toni-ko 6a1ef643d3 Fixed warning with hidden function 2025-08-25 11:40:24 -07:00
Will Pazner d9d4b5bb72 Rename member variable to fix shadow warning 2025-08-23 15:13:05 -07:00
Will Pazner 5c04ba55f6 Fix failing TMOP tests
Keep a cached copy of GeomToPerfGeomJac for use on device.

Be sure to call HostRead before DenseMatrix::Det.
2025-08-23 15:06:03 -07:00
Socratis Petrides e09479465b derived amgf class 2025-08-22 15:16:45 -07:00
Socratis Petrides c4da5827a2 removing op handle. Doing the RAP by type cast 2025-08-21 22:19:05 -07:00
Socratis Petrides 8df19f39ef first iteration on FilteredSolver 2025-08-21 18:37:04 -07:00
Joseph Signorelli 405c674d22 Update unit tests to check host + device 2025-08-21 15:24:19 -07:00
Joseph Signorelli 83362c7b4d Add HostReadWrite to beginnning of Array 2025-08-21 15:13:50 -07:00
Will Pazner 240443abfb Vector::DeleteAt on device using InclusiveScan 2025-08-21 14:42:17 -07:00
Andrew Ho c189f50ab4 Added GPU-accelerated parallel scan 2025-08-20 11:15:53 -07:00
Socratis Petrides 7d7d4cf6f4 class sketch 2025-08-19 17:45:35 -07:00
Joseph Signorelli 820e14875b Merge branch 'master' into particleset-particle-dev 2025-08-19 16:42:49 -07:00
Joseph Signorelli f43c6771c6 Merge branch 'master' into multivector-dev 2025-08-19 16:42:31 -07:00
Will Pazner 061a92067f Merge branch 'master' into array-vector-improvements-dev 2025-08-19 16:33:02 -07:00
Joseph SignorelliandWill Pazner 0ddb02c7e7 fix type
Co-authored-by: Will Pazner <11493037+pazner@users.noreply.github.com>
2025-08-19 15:57:45 -07:00
Joseph SignorelliandWill Pazner 427406d1b8 Update Vector::Reserve
Co-authored-by: Will Pazner <11493037+pazner@users.noreply.github.com>
2025-08-19 13:36:42 -07:00
Joseph Signorelli 2ffbdf1a18 add newlines to end of all files 2025-08-19 13:30:09 -07:00
Joseph Signorelli 065bbcb9a5 Use Array<int> of size 1 for tags in Particle, add set tag ref, and include unit test for GetParticleRef 2025-08-19 13:24:16 -07:00
Joseph Signorelli 09327924ee Merge branch 'master' into multivector-dev 2025-08-19 12:15:01 -07:00
Joseph Signorelli 5026d6ca8a Merge branch 'master' into array-vector-improvements-dev 2025-08-19 12:14:21 -07:00
Joseph Signorelli e659e823e9 Add newlines to end of files 2025-08-19 12:06:43 -07:00
Joseph Signorelli 04d7e8a62f add newlines to bottom of test files 2025-08-19 12:03:34 -07:00
Joseph Signorelli a660b5fc07 Potential fix to std::iota not found for windows build 2025-08-18 16:14:12 -07:00
Joseph Signorelli 502e422d4b Potential fix to Particle::tags memory leak 2025-08-18 15:47:32 -07:00
Joseph Signorelli a97a13a342 Minor documentation improvements 2025-08-18 15:28:26 -07:00
Joseph Signorelli 65a95551ea Fix another int comparison w/ std::size_t 2025-08-18 14:56:04 -07:00
Joseph Signorelli 9fb85d2d0b Fix remaining -Wall 2025-08-18 14:42:53 -07:00
Joseph Signorelli 99cdb577fb Fix unused const variable (for when MFEM_USE_GSLIB not defined) 2025-08-18 14:23:35 -07:00
Joseph Signorelli 4c36ae0f47 Fix initialize of std::string w/ nullptr 2025-08-18 13:42:53 -07:00
Joseph Signorelli ad839667c0 Single-precision 2025-08-18 12:46:47 -07:00
Joseph Signorelli 8e058595b4 fix reorder-ctor error 2025-08-18 11:28:43 -07:00
Joseph Signorelli e06f1a4267 use std::size_t for loops over std .size() types 2025-08-18 11:21:28 -07:00
Joseph Signorelli eef84c7a10 Do not build navier_particles + navier_bifurcation if not MFEM_USE_GSLIB, in makefile 2025-08-18 11:07:12 -07:00
Joseph Signorelli 916d14d2b7 Fix use of string after lifetime ends 2025-08-18 11:06:56 -07:00
Joseph Signorelli 00dc6d2780 fix test errors 2025-08-15 15:56:04 -07:00
Joseph Signorelli dfc786fbbf Fix docs 2025-08-15 15:31:48 -07:00
Joseph Signorelli 1f8dbc7bfe Fix doc 2025-08-15 15:20:23 -07:00
Joseph Signorelli 194f3005bb Add channel2.mesh 2025-08-15 15:16:46 -07:00
Joseph Signorelli 05103d26a9 Add clean to makefile for bifurcation 2025-08-15 14:42:27 -07:00
Joseph Signorelli 6a6f5e4d23 Add navier bifurcation (+ output) to gitignore 2025-08-15 14:40:59 -07:00
Joseph Signorelli 5dd665f37b minor 2025-08-15 14:38:00 -07:00
Joseph Signorelli f5d33d661a style NavierParticles 2025-08-15 14:37:18 -07:00
Joseph Signorelli 217b3d2d2d Add Navier_Bifurcation 2025-08-15 14:36:58 -07:00
Joseph Signorelli 5974bfbafb Add GetCurrentVorticity to Navier 2025-08-15 14:36:27 -07:00
Joseph Signorelli 05da808856 Add NavierParticles class 2025-08-15 14:13:22 -07:00
Joseph Signorelli 7d159da97c Add particles_redist miniapp 2025-08-15 14:00:55 -07:00
Joseph Signorelli b6d6473d36 Formatting + style 2025-08-15 14:00:43 -07:00
Joseph Signorelli 9ff5d24102 serial compile bug fixes 2025-08-15 13:57:47 -07:00
Joseph Signorelli 3dd9427c7e Fix bug when compiling w/o GSLIB 2025-08-15 13:57:47 -07:00
Joseph Signorelli 0d0c02b715 Add miniapp common particle functions + ParticleTrajectories class 2025-08-15 13:57:47 -07:00
Joseph Signorelli 355e434575 Add particles_extras.cpp/hpp to miniapps/common 2025-08-15 13:57:47 -07:00
Veselin Dobrev 1bf4c4f7fe Merge branch 'master' into table-array 2025-08-15 12:23:56 -07:00
Joseph Signorelli f8fa4854bf Add particle/particleset unit test. 2025-08-15 11:01:00 -07:00
Joseph Signorelli bbed72b3c6 Add ParticleSet class 2025-08-15 10:47:42 -07:00
Joseph Signorelli f377c63ea7 Add Particle class 2025-08-15 10:40:56 -07:00
Joseph Signorelli ed80737a9a Create particleset.cpp/hpp 2025-08-15 10:32:22 -07:00
Joseph Signorelli 6aba0652f1 Merge branch 'multivector-dev' into particleset-particle-dev 2025-08-13 16:48:37 -07:00
Joseph Signorelli f85ee8391d Merge branch 'fdpts-improve-dev' into particleset-particle-dev 2025-08-13 16:48:09 -07:00
Joseph Signorelli 715ab0a328 Fix typo causing doc fail 2025-08-13 16:35:47 -07:00
Joseph Signorelli 160100e0b3 style 2025-08-13 16:27:19 -07:00
Joseph Signorelli 997942b44e Add MultiVector w/ unit tests 2025-08-13 16:22:23 -07:00
Joseph Signorelli a1bb9cfe9f style 2025-08-13 16:22:23 -07:00
Joseph Signorelli 2489dce8a0 Add Vector::Reserve 2025-08-13 16:22:23 -07:00
Joseph Signorelli 4ea338fe53 Add Vector::DeleteAt w/ unit test 2025-08-13 16:22:23 -07:00
Joseph Signorelli eb5a0eb132 Add Array::DeleteAt w/ unit test. 2025-08-13 16:22:23 -07:00
Joseph Signorelli 236ba45fb9 Add Ordering::Reorder w/ unit test 2025-08-13 16:11:52 -07:00
Joseph Signorelli e23975a2d8 Add test_multivector.cpp 2025-08-13 16:10:41 -07:00
Joseph Signorelli 769d2914c8 Move Ordering to multivector.hpp/cpp 2025-08-13 16:02:37 -07:00
Joseph Signorelli 5c87a6c665 Create new files multivector.cpp/hpp 2025-08-13 15:47:17 -07:00
Joseph Signorelli 915853cee0 style 2025-08-13 14:51:59 -07:00
Joseph Signorelli a52599d4cc Add Vector::Reserve 2025-08-13 14:49:39 -07:00
Joseph Signorelli 0d5b13c4aa Add Vector::DeleteAt w/ unit test 2025-08-13 14:44:55 -07:00
Joseph Signorelli dda6b0dbe1 Add Array::DeleteAt w/ unit test. 2025-08-13 14:39:29 -07:00
Veselin Dobrev 32efc9be3c Merge pull request #4978 from mfem/table-array-additions
Proposed addtions and changes to PR #4028 (branch table-array)
2025-08-13 12:16:20 -07:00
Veselin Dobrev 2675f36788 Update formatting 2025-08-13 10:57:22 -07:00
Veselin Dobrev 3372b2d1a8 Proposed addtions and changes to PR #4028 (branch table-array)
* Add mfem::swap for the classes Memory, Array, Array2D, and Vector.
* These mfem::swap functions are for use by the standard library.
* Re-define mfem::Swap to use mfem::swap, or, if is not defined, std::swap
* Remove some calls to Memory::Reset after Memory::Delete since the latter
  calls the former
* Update/improve the definitions of the Vector move-constructor and move-
  assignment; in the copy-assignment, skip the virtual calls to
  v.UseDevice(bool) when they are not needed.
* In DenseMatrix::SetSize, update the size of the data Array to match
  height x width.
* Propagate the parameter use_dev in Table::{ReadI,ReadJ} to the respective
  Array::Read calls.
* In Table::Size_of_connections, return J.Size() instead of I[size].
* Some small Doxygen tweaks.
2025-08-12 14:51:58 -07:00
Veselin Dobrev 8a278c70d6 Merge branch 'master' into table-array 2025-08-12 14:44:38 -07:00
blaz e5d4917c65 fix memory leaks 2025-08-11 20:23:32 -07:00
blaz 258d8c7dec Merge branch 'master' into imex-conv-diff-dg 2025-08-11 19:13:04 -07:00
Toni-ko e3ce7f2de4 resolving an error 2025-08-11 18:06:20 -07:00
Toni-ko a579e7f6c1 attempting to resolve failing check 2025-08-11 18:02:07 -07:00
Toni-ko e65eae9a59 typo 2025-08-11 17:52:12 -07:00
Toni-ko 93c45b205b code style update 2025-08-11 17:11:35 -07:00
Toni-ko 8805e6cfcf Added Sample Runs. Updated comments. Code cleanup. 2025-08-11 16:22:38 -07:00
Will Pazner 7fea470192 Modify Array::Copy to match semantics of Vector
Also revert Array copy assignment to use Array::Copy
2025-08-11 15:24:25 -07:00
Will Pazner 6bab6a283f Use same copy assignment semantics in Array as in Vector 2025-08-09 10:25:57 -07:00
Will PaznerandVeselin Dobrev 5a7edc9548 Fix Array move constructor and assignment operator
Co-authored-by: Veselin Dobrev <dobrev@llnl.gov>
2025-08-09 08:28:40 -07:00
Toni-ko 305dd2e17c example 41 using lor preconditioner 2025-08-08 13:42:17 -07:00
Toni-ko 4bc378f84f Merge remote-tracking branch 'origin/lor_dg_preconditioner' into imex-conv-diff-dg 2025-08-07 17:55:21 -07:00
Toni-ko 2ccd455706 Included preconditioner 2025-08-07 15:41:11 -07:00
Veselin Dobrev bdb627a5ae Merge branch 'master' into table-array
Resolved conflicts:
   general/array.hpp
2025-08-07 11:24:27 -07:00
Toni-ko f319c14ecc Parallel Version of example 41 works 2025-08-06 09:41:41 -07:00
Will Pazner 30d975fb9b Don't need specializations of mfem::Swap for Array or Array2D 2025-08-05 15:39:29 -07:00
Will Pazner dd68bbe9fd Use explicitly defaulted copy and move operations in Array2D 2025-08-05 15:38:43 -07:00
Will Pazner d1efdf93be Use default move constructor and assignment in Array<T> 2025-08-05 15:38:14 -07:00
Will Pazner e95d59cdf1 Implement mfem::Swap in terms of std::swap 2025-08-05 15:37:49 -07:00
Will Pazner b8c1b5c2fa Use explicitly defaulted move and copy member functions in DenseMatrix 2025-08-05 15:37:13 -07:00
Will Pazner a30af2ed43 Variable name consistency in Memory
Documentation was 'other', code was 'orig'. Changed to 'other'.
2025-08-05 15:36:39 -07:00
Toni-ko bb467f97da First draft of the parallel version of ex41. Still some debugging to do. 2025-08-01 16:24:44 -07:00
Toni-ko 479433c871 Added a higher order IMEX Scheme - fixed some typos 2025-07-31 14:25:40 -07:00
Toni-ko 4269b06149 Changed SplitODESolver so that it inherits from ODESolver. Added an RK2 IMEX Method. 2025-07-30 15:27:47 -07:00
Julian Andrej c579f28b3f boundary integrator 2025-07-30 12:02:59 -07:00
Julian Andrej 947694ae63 add tensor::weight 2025-07-30 11:57:50 -07:00
Julian Andrej e97704d91c transformation bugfix for n x m tensor from memory 2025-07-30 11:57:21 -07:00
Will Pazner f5da02ce45 Fix bug in DenseTensor::NewMemoryAndSize 2025-07-29 13:11:13 -07:00
Toni-ko b8660af425 Implements SplitTimeDependentOperator class along with supporting methods. Implements an IMEX scheme for ex41. Solutions to ex41 look reasonable. 2025-07-28 16:55:23 -07:00
Julian Andrej 1f8f7e44bc remove dead code 2025-07-28 11:45:31 -07:00
Will Pazner dc13e67e87 Add placeholder for example 41, DG convection-diffusion
Currently just a cleaned-up version of ex9. Will add diffusion and
IMEX time integration.
2025-07-25 11:24:16 -07:00
camierjs 39262f0376 Incompressible Schrödinger Flow Miniapp 2025-07-21 16:34:19 -07:00
John Camier 1aba4e0bb8 Merge branch 'master' into vector-pa-kernels 2025-07-04 08:02:46 -07:00
camierjs 03cf0edb5b Sync kernels 2025-07-02 11:04:40 -07:00
camierjs 0838384b78 Merge branch 'master' into vector-pa-kernels 2025-07-02 07:40:25 -07:00
Veselin Dobrev 3e321e6c9e Merge branch 'master' into woptim/leverage-radiuss-ci 2025-07-01 16:35:45 -07:00
John Camier 9c20900150 Merge branch 'master' into vector-pa-kernels 2025-07-01 09:51:22 -07:00
camierjs 52014e215e Sync kernels 2025-06-24 10:35:10 -07:00
camierjs 2daa072b55 Rename to GradTranspose3d kernel 2025-06-23 17:39:54 -07:00
John Camier ed31cb7bbd Merge branch 'master' into vector-pa-kernels 2025-06-23 16:11:19 -07:00
John Camier 7811775ab1 Merge branch 'master' into vector-pa-kernels 2025-06-23 07:46:38 -07:00
John Camier 5edabb64b7 Merge branch 'master' into vector-pa-kernels 2025-06-18 10:22:30 -07:00
camierjs 319b870ae1 Use future namespace for tensor
Fix couple of warnings
2025-06-16 08:47:39 -07:00
camierjs ba70885008 Merge branch 'master' into vector-pa-kernels 2025-06-16 08:46:29 -07:00
John Camier 2e706883d0 Merge branch 'master' into vector-pa-kernels 2025-06-13 08:49:04 -07:00
camierjs 614530599f Merge branch 'master' into vector-pa-kernels 2025-05-28 11:37:33 -07:00
camierjs acbe258939 CMake remove header 2025-05-19 15:25:09 -07:00
camierjs 7dba46021e Move kernel methods to fem/kernels.hpp 2025-05-19 15:13:20 -07:00
camierjs 189c201792 Fix shadowing 2025-05-19 11:17:18 -07:00
camierjs 0742475bc7 test_pa_kernels tags 2025-05-19 11:00:11 -07:00
camierjs 716c201fd3 GPU vector kernels fix 2025-05-19 10:35:40 -07:00
camierjs f9972a57c0 Simplify back SmemPAVectorMassApply2D 2025-05-19 08:27:44 -07:00
camierjs b3e9ddd846 WIN32 cmath 2025-05-18 15:56:41 -07:00
camierjs f3443f92c3 MSVC fix 2025-05-18 15:19:15 -07:00
camierjs c50905e492 MAX_T1D for WIN32 2025-05-18 14:52:38 -07:00
camierjs 7a37cc2448 MSVC header guards 2025-05-18 13:03:19 -07:00
camierjs 82c2e01678 Cleanup 2025-05-18 12:45:11 -07:00
camierjs 2ead488818 Pre SmemPAVectorMassApply2D cleanup 2025-05-18 12:33:12 -07:00
camierjs 190c4a091d WIP SmemPAVectorMassApply2D layouts 2025-05-18 11:13:18 -07:00
camierjs 6bb5f8ae0b Change pa data layout 2025-05-18 09:45:29 -07:00
camierjs 3b88332c98 Merge branch 'master' into vector-pa-kernels 2025-05-17 15:13:20 -07:00
camierjs ea25a9bb7b Remove PAVectorDiffusionApply kernels, fuse SDIM != DIM diffusion vector into same kernel 2025-05-17 15:12:57 -07:00
camierjs 62dd1aee3b Cleanup 2025-05-15 17:31:52 -07:00
camierjs e41c782974 Use explicit type names for registers 2025-05-15 15:02:12 -07:00
camierjs eb6843443f Simplify regs_t kernels types on CPU 2025-05-15 12:53:45 -07:00
camierjs 51c28ade7b Fix grad ii vs. jj matrix coeff
VectorDiagonalPA tests
2025-05-13 18:31:21 -07:00
camierjs 686feffc1b VectorDiffusionAddMultPA registered 2025-05-13 15:54:42 -07:00
camierjs 795a121f4e SmemPAVectorDiffusionApply3D 2025-05-13 15:33:01 -07:00
camierjs 6a04555556 SmemPAVectorDiffusionApply2D 2025-05-13 15:19:18 -07:00
camierjs 580dc6f50c Simplify VectorDiffusionIntegrator::AssemblePA 2025-05-13 12:26:17 -07:00
camierjs d27fa842b1 Pre AssemblePA 2D vector diffusion
simplify
2025-05-13 12:11:26 -07:00
camierjs 3fb13a3614 WIP 3D vector diffusion with sym mcoeff 2025-05-13 11:52:45 -07:00
camierjs 3c2f98bf64 test_vector_pa_integrator all tests passed 2025-05-13 10:59:12 -07:00
Hugh Carson f4bbc69ae6 Merge remote-tracking branch 'origin/master' into sjg/mesh-vis-dev 2025-05-13 12:18:24 -04:00
Hugh Carson 484249d94c Move coefficient fields to ParaViewDataCollection 2025-05-13 12:16:26 -04:00
camierjs 2a3c1c5bde VectorDiffusionIntegrator 2D cleanup 2025-05-13 08:55:04 -07:00
camierjs 031cd67c76 VectorDiffusionIntegrator 2D mcoeff 2025-05-12 18:13:00 -07:00
camierjs 7f7e5f61e2 All c, d 2025-05-12 17:49:24 -07:00
camierjs 582773213e WIP 0 1 2025-05-12 17:45:20 -07:00
camierjs 3086daecee WIP PAVectorDiffusionApply2D mcoeff 2025-05-12 17:16:27 -07:00
camierjs c707a4d3f6 jj=ii 2025-05-12 16:00:57 -07:00
camierjs ba4befcfb9 VectorDiffusionIntegrator wip Matrix coefficient 2025-05-12 13:59:05 -07:00
camierjs 72d28e0cdb VectorDiffusionIntegrator AssemblePA vector coefficients 2025-05-12 10:06:39 -07:00
camierjs 1c04593e79 VectorMassIntegrator cleanup 2025-05-12 07:19:29 -07:00
camierjs 0892f190cf VectorMassIntegrator 3D MatrixFunctionCoefficient 2025-05-12 07:15:40 -07:00
camierjs b08d12b5f1 VectorMassIntegrator 2D with MatrixFunctionCoefficients 2025-05-11 18:07:51 -07:00
camierjs d2e283419a VectorMassIntegrator PA specializations 2025-05-09 18:09:21 -07:00
camierjs 3949decb69 PAVectorMassApply2D with Eval2d 2025-05-09 17:09:18 -07:00
camierjs 93dbe1404a PAVectorMassApply3D with Eval3d 2025-05-09 16:32:21 -07:00
camierjs 68ae66c3ed vecmass PA setup 2025-05-09 15:51:24 -07:00
Tzanio Kolev 1206e9c575 Merge branch 'master' into qf-project-gf-fallback 2025-05-03 13:27:56 -07:00
Will Pazner 86b16c8989 Add fallback for QuadratureFunction::ProjectGridFunction
The (slower) fallback will be used when QuadratureInterpolator is not supported
for the finite element space.
2025-04-29 10:35:44 -07:00
Hugh Carson feda9f0125 make style 2025-04-22 14:36:58 -04:00
Hugh Carson 003b4175df Merge remote-tracking branch 'origin/master' into sjg/mesh-vis-dev 2025-04-22 14:20:21 -04:00
Hugh Carson fe83a192e9 Merge branch 'master' into sjg/mesh-vis-dev 2025-04-15 14:01:33 -04:00
Adrien M. BERNEDE 746fbca164 Merge branch 'woptim/leverage-radiuss-ci' into leverage-radiuss-ci--updates 2025-03-12 21:50:32 +01:00
Tzanio Kolev e7f71da564 Merge branch 'master' into woptim/leverage-radiuss-ci 2025-03-12 13:13:56 -07:00
Adrien M. BERNEDE 7d9048f9f0 Merge branch 'woptim/leverage-radiuss-ci' into leverage-radiuss-ci--updates 2025-03-07 17:49:19 +01:00
Adrien M. BERNEDE 3b2fe7ce7f Merge branch 'master' into woptim/leverage-radiuss-ci 2025-03-07 17:49:03 +01:00
Adrien M. BERNEDE dd64dcc10b Merge branch 'woptim/leverage-radiuss-ci' into leverage-radiuss-ci--updates 2025-02-19 14:23:21 +01:00
Adrien M. BERNEDE f89e89e9fa Merge branch 'master' into woptim/leverage-radiuss-ci 2025-02-19 14:21:35 +01:00
Adrien M. BERNEDE 2e27af385c Proposal to avoid .tioga_job_command override 2025-02-17 16:41:37 +01:00
Adrien M. BERNEDE f1025ecdeb Explain REGISTRY_TOKEN 2025-02-14 16:10:47 +01:00
Adrien M. BERNEDE 09b5dcab6d Merge branch 'woptim/leverage-radiuss-ci' into leverage-radiuss-ci--updates 2025-02-14 16:00:41 +01:00
Adrien M. BERNEDE 22e1f000b9 Update tests/gitlab readme and reproducibility files 2025-02-14 15:58:55 +01:00
Adrien M. BERNEDE 8884c31461 Update .gitlab/README.md 2025-02-14 15:26:51 +01:00
Adrien M. BERNEDE 04b868a47b Merge branch 'woptim/leverage-radiuss-ci' into leverage-radiuss-ci--updates 2025-02-13 17:39:01 +01:00
Adrien M. BERNEDE 8b99a6a847 Point at merge commit in mfem-uberenv 2025-02-11 11:35:33 +01:00
Adrien M. BERNEDE 4868222660 Update mfem-uberenv 2025-02-10 20:41:04 +01:00
Francis Giraldeau 82aa3a135c Fix Mesh::MakeSimplicial for surface in 3D
The Z dimension of a surface mesh was lost when converting it to simplex.

Add a test checking all vertices after the conversion. We create a special mesh for this specific case.

Close #4455
2025-02-08 23:01:09 -05:00
Veselin Dobrev f4ef788e40 Address some feedback 2025-02-06 14:42:15 -08:00
Veselin Dobrev 8f333155cc Updates for the branch 'woptim/leverage-radiuss-ci' 2025-02-05 13:08:25 -08:00
Hugh Carson 06d9613f59 Merge remote-tracking branch 'origin/master' into sjg/mesh-vis-dev 2025-01-21 14:59:56 -05:00
Adrien M. BERNEDE 5499eb8939 Merge branch 'master' into woptim/leverage-radiuss-ci 2025-01-16 15:34:19 +01:00
Adrien M. BERNEDE 5ccdd4b53d Merge branch 'master' into woptim/leverage-radiuss-ci 2025-01-13 12:21:54 +01:00
Will Pazner d0a994fa02 Add Array<T>::NewMemoryAndSize
Analogous to Vector::NewMemoryAndSize.

Implement DenseTensor::NewMemoryAndSize in terms of Array<T>::NewMemoryAndSize.
2024-11-22 10:50:12 -08:00
Will Pazner 6e4f6d11be Merge remote-tracking branch 'origin/master' into table-array 2024-11-22 10:41:03 -08:00
Adrien Bernede c50a55f05c Merge branch 'master' into woptim/leverage-radiuss-ci 2024-11-04 15:29:28 +01:00
Adrien Bernede 1c146ff523 Merge branch 'master' into woptim/leverage-radiuss-ci 2024-10-21 14:52:48 +02:00
Adrien M. BERNEDE 00d3b1ca49 Merge branch 'master' into woptim/leverage-radiuss-ci 2024-10-08 10:44:33 +02:00
Adrien M. BERNEDE 0f13bc76f5 Merge branch 'master' into woptim/leverage-radiuss-ci 2024-09-26 11:41:29 +02:00
Hugh Carson 0c2f087bb9 Merge branch 'master' into sjg/mesh-vis-dev 2024-09-13 13:08:42 -04:00
Adrien Bernede b377eac8cd Merge branch 'master' into woptim/leverage-radiuss-ci 2024-09-12 11:13:57 +02:00
Adrien M. BERNEDE 175a4302c3 Run baseline sub-pipeline as soon as machine is verified 2024-08-08 23:36:29 +02:00
Adrien M. BERNEDE e31f662f8b Fix typo 2024-08-08 18:10:43 +02:00
Adrien M. BERNEDE 903ddbf97d Update build_and_test script to populate Gitlab registry as build cache 2024-08-08 18:01:31 +02:00
Adrien M. BERNEDE 9c44c45a7e Deactivate rocm 5.7 job on tioga 2024-08-06 18:09:27 +02:00
Adrien M. BERNEDE 4da44357d0 Reduce allocation duration on ruby 2024-08-05 15:50:30 +02:00
Adrien M. BERNEDE bf55c1cc46 Only one node for CI jobs, so that /dev/shm exists for any sub job 2024-08-05 12:56:56 +02:00
Adrien M. BERNEDE 96d188ac12 Merge branch 'master' into woptim/leverage-radiuss-ci 2024-08-05 11:30:11 +02:00
Adrien M. BERNEDE c0a1e91237 Revert "TEMP: pci queue not working on tioga"
This reverts commit 788c1a15da.
2024-08-05 10:43:26 +02:00
Adrien M. BERNEDE 788c1a15da TEMP: pci queue not working on tioga 2024-08-02 14:49:26 +02:00
Adrien M. BERNEDE bb72f2c0c6 mfem-uberenv: Attempt at enforcing coherent rocm compiler in rocm stack 2024-07-30 16:53:26 +02:00
Adrien M. BERNEDE edeb7ab0f9 Attempt to use cray-libsci through modules in Spack 2024-07-30 16:19:14 +02:00
Adrien M. BERNEDE 55f954e6c3 mfem-uberenv: fix 2024-07-30 15:03:35 +02:00
Adrien M. BERNEDE 587ef0ef47 mfem-uberenv: Adding cray-libsci as sole blas / lapack provider 2024-07-30 14:57:00 +02:00
Adrien M. BERNEDE 23a18a46b3 Fix logic to prevent pushing to autotest repo 2024-07-29 12:36:53 +02:00
Adrien M. BERNEDE 8e00d7c191 Fix logic to prevent pushing to autotest repo 2024-07-29 12:20:30 +02:00
Adrien M. BERNEDE b242fde7ec Fix reproducer 2024-07-29 12:03:08 +02:00
Adrien M. BERNEDE a09c9ef266 Fix reproducer 2024-07-29 11:37:44 +02:00
Adrien Bernede a2e5ffc9db Fix reproducer 2024-07-26 18:15:27 +02:00
Adrien Bernede 6bebe4caf9 Update mfem uberenv with missing MFEM package patches 2024-07-26 14:55:05 +02:00
Adrien M. BERNEDE 97e554fb13 Update mfem-uberenv: do not use spack compiler 2024-07-25 17:25:20 +02:00
Adrien M. BERNEDE 3a30286567 Revert unecessary changes, fix alloc command using xargs in shared ci 2024-07-23 16:53:14 +02:00
Adrien M. BERNEDE bdc919f084 fix hope 2024-07-23 12:36:38 +02:00
Adrien M. BERNEDE 8cc6821cad hope 2024-07-23 12:33:52 +02:00
Adrien M. BERNEDE f2851bbb61 ... 2024-07-23 12:24:08 +02:00
Adrien M. BERNEDE 49e19c2414 ??? 2024-07-23 12:15:36 +02:00
Adrien M. BERNEDE 73cbdc75e1 New attempt 2024-07-23 12:08:07 +02:00
Adrien M. BERNEDE 81b91619ae Apply improved variable handling 2024-07-23 11:55:25 +02:00
Adrien M. BERNEDE 070ab14951 Looking for a fix (4) 2024-07-22 18:01:43 +02:00
Adrien M. BERNEDE bbbb50ff5b Looking for a fix (ter) 2024-07-22 17:32:04 +02:00
Adrien M. BERNEDE 204c9c60d1 Looking for a fix (bis) 2024-07-22 17:25:19 +02:00
Adrien M. BERNEDE e7f8898546 Looking for a fix 2024-07-22 17:16:46 +02:00
Adrien M. BERNEDE 9c64303464 Remove extra quotes 2024-07-22 16:02:51 +02:00
Adrien M. BERNEDE 28bf478b68 Prevent early variable expansion 2024-07-22 15:55:29 +02:00
Adrien M. BERNEDE 9b0edad337 Fix script name 2024-07-22 15:40:22 +02:00
Adrien M. BERNEDE d6cbd76c46 Fix no jobs imported from radiuss-spack-configs 2024-07-22 14:06:06 +02:00
Adrien M. BERNEDE 2c8a7548cb Complete moving to Shared CI 2024-07-22 13:02:54 +02:00
Adrien M. BERNEDE 33ce5f1765 WIP: moving to RADIUSS Shared CI 2024-07-19 09:18:21 +02:00
Adrien M. BERNEDE 84ce97a114 Merge branch 'master' into woptim/ci-tioga 2024-07-18 09:34:39 +02:00
Adrien M. BERNEDE c508781257 Remove Corona from CI machines, replaced by Tioga 2024-07-18 09:33:35 +02:00
Adrien M. BERNEDE df994a4890 Update mfem-uberenv: add external installs for rocsparse and hipsparse 2024-07-17 16:01:56 +02:00
Adrien M. BERNEDE db63f71f40 Increase number of nodes for ruby 2024-07-17 16:01:03 +02:00
Adrien M. BERNEDE c4b05bd83e Update mfem-uberenv: fix spack configs path 2024-07-17 11:55:16 +02:00
Adrien M. BERNEDE 6155e148a7 Do not use upstream spack 2024-07-17 11:30:02 +02:00
Adrien M. BERNEDE b8e0bd0692 Update Uberenv with Tioga configs 2024-07-17 10:49:16 +02:00
Adrien M. BERNEDE 675ecb994f Use Python3 to run uberenv 2024-07-17 10:42:08 +02:00
Adrien M. BERNEDE e3d5c35614 Update Uberenv, spack and spack configs 2024-07-17 10:36:38 +02:00
Adrien M. BERNEDE 953a063626 Merge branch 'master' into woptim/ci-tioga 2024-07-17 10:17:36 +02:00
Adrien M. BERNEDE 535e39bbb1 Update build_and_test script with required changes to work well with new mfem-uberenv 2024-07-16 17:02:37 +02:00
Adrien M. BERNEDE cfa41e0d9c Enforce quotes is corona job command 2024-07-15 12:11:23 +02:00
Adrien M. BERNEDE 0ee13cc107 Fix SPECS in corona and tioga CI 2024-07-15 12:07:26 +02:00
Adrien M. BERNEDE 4635107f2b Attempt at forcing quotes 2024-07-15 11:34:46 +02:00
Adrien M. BERNEDE e5c1b4412c Unify allocation process between corona and tioga 2024-07-12 16:47:12 +02:00
Adrien M. BERNEDE a3cd66cf84 Fix: remove nonexistent partition 2024-07-12 16:13:02 +02:00
Adrien M. BERNEDE 7abddc527a Get corona and tioga jobs to run by default (ON_<MACHINE> var) 2024-07-12 15:59:06 +02:00
Adrien Bernede 6afec618be Merge branch 'master' into woptim/ci-tioga 2024-07-12 12:24:39 +02:00
Adrien Bernede fff5a50f21 Merge branch 'master' into woptim/ci-tioga 2024-06-25 15:04:49 +02:00
Will Pazner 8c8b79fdd9 Merge remote-tracking branch 'origin/master' into table-array
# Conflicts:
#	general/table.cpp
#	general/table.hpp
2024-06-24 11:08:08 -07:00
Will Pazner 326acce08c Replace double with real_t in BatchLUFactor 2024-06-02 14:03:46 -07:00
Will Pazner 3d78f073ae Merge remote-tracking branch 'origin/master' into table-array
# Conflicts:
#	general/table.hpp
#	linalg/densemat.cpp
#	linalg/densemat.hpp
2024-06-02 13:25:08 -07:00
Tom Stitt 257ab6837b fix MFEM_PERF_SCOPE with latest caliper 2024-05-31 13:09:42 -07:00
Adrien M. BERNEDE e8b229bb54 Merge branch 'woptim/gitlab-updates' into woptim/ci-tioga 2024-05-23 11:26:12 +02:00
Adrien M. BERNEDE f7db5cbc24 Merge branch 'woptim/gitlab-updates' into woptim/ci-tioga 2024-05-23 11:19:07 +02:00
Adrien M. BERNEDE 1a1df8de48 Update relationship between jobs for correct ordering 2024-05-21 11:32:43 +02:00
Adrien M. BERNEDE 3f7fddb188 A first attempt at adding CI on tioga 2024-05-21 11:24:02 +02:00
Sebastian Grimberg 94ccc4f364 Fix for single precision builds 2024-05-06 12:20:30 -07:00
Sebastian Grimberg d86da2d2c5 Merge branch 'master' into sjg/mesh-vis-dev 2024-05-06 12:17:30 -07:00
Sebastian Grimberg 46d81f4742 Merge branch 'master' into sjg/mesh-vis-dev 2024-04-04 11:31:21 -07:00
Will Pazner 9f1c1b811a Merge branch 'densemat-array' into table-array 2024-01-30 12:24:41 -08:00
Will Pazner 843365752f Fix Array copy/move bug in DeviceConformingProlongationOperator 2024-01-02 13:29:39 -08:00
Will Pazner cc21c6b859 Add move assignment operator to Array 2024-01-02 11:40:37 -08:00
Will Pazner d788f190ea Use Array<double> in DenseMatrix and DenseTensor 2023-12-13 21:39:36 -08:00
Will Pazner d0bb1a86ed Delete specialization of mfem::Swap for Table 2023-12-13 21:11:08 -08:00
Will Pazner fafec06958 Use std::move in mfem::Swap 2023-12-13 21:10:49 -08:00
Will Pazner e52a2b4dcd Use Array<int> instead of Memory<int> in Table
Also make some small Doxygen edits
2023-12-13 14:13:38 -08:00
Sebastian Grimberg 81786e6671 Merge branch 'master' into sjg/mesh-vis-dev 2023-12-12 18:17:37 -08:00
Sebastian Grimberg 70a1fd7679 Revert addition of length scale for mesh coordinates in mesh output 2023-11-06 07:58:57 -08:00
Sebastian Grimberg feefc7d62a Merge branch 'master' into sjg/mesh-vis-dev 2023-11-06 07:52:01 -08:00
Sebastian Grimberg 3061b78689 Merge branch 'master' into sjg/mesh-vis-dev 2023-09-29 07:27:12 -07:00
Sebastian Grimberg a4f4e00ba0 Merge branch 'master' into sjg/mesh-vis-dev 2023-09-06 11:30:19 -07:00
Sebastian Grimberg ba938e8a97 Fix from merge 2023-09-06 11:30:17 -07:00
Sebastian Grimberg 1cc174cd7f Merge branch 'master' into sjg/mesh-vis-dev 2023-08-17 12:17:56 -07:00
Sebastian Grimberg db840eb015 Merge branch 'master' into sjg/mesh-vis-dev 2023-07-30 18:19:40 -07:00
Hugh Carson 77d32658fa Remove changes to test_operator.cpp 2023-07-21 13:58:34 -04:00
Hugh Carson d55cacbd6e make style 2023-07-21 10:53:15 -04:00
Hugh Carson c42e6e7687 Merge remote-tracking branch 'origin/master' into sjg/mesh-vis-dev 2023-07-21 10:34:59 -04:00
Sebastian Grimberg a96d4cdf33 Merge branch 'master' into sjg/mesh-vis-dev 2023-06-26 15:37:39 -07:00
Sebastian Grimberg ec402f6b24 Merge branch 'master' into sjg/mesh-vis-dev 2023-06-15 14:37:50 -07:00
Sebastian Grimberg 2a6f96bb90 Merge branch 'master' into sjg/mesh-vis-dev 2023-05-16 18:40:05 -07:00
Sebastian Grimberg 6398fa198b Merge branch 'master' into sjg/mesh-vis-dev 2023-05-02 17:45:44 -07:00
Sebastian Grimberg f1f1b420b7 Merge branch 'master' into sjg/mesh-vis-dev 2023-04-18 11:10:18 -07:00
hughcars ad989ab425 Merge branch 'master' into sjg/mesh-vis-dev 2023-03-30 15:04:25 -04:00
Sebastian Grimberg 7caa15acde Address PR comments 2023-03-15 16:23:40 -07:00
Sebastian Grimberg 04009fedb0 make style 2023-03-06 10:19:59 -08:00
Sebastian Grimberg 098db1b70c Mesh and DataCollection upgrades 2023-03-03 17:28:25 -08:00
334 changed files with 36121 additions and 11833 deletions
+33 -14
View File
@@ -19,9 +19,15 @@ CMakeFiles/
# Clangd server cache
*.cache*
#vscode settings
/.vscode/
# Backup files
*~
# clangd index
/.cache/
# Default install location
/mfem/
@@ -79,12 +85,17 @@ examples/sol_u.*
examples/sol_p.*
examples/sol_r.*
examples/sol_i.*
examples/sol_z.*
examples/ex6p-checkpoint.*
examples/order.*
examples/ex9.mesh
examples/ex9-mesh.*
examples/ex9-init.*
examples/ex9-final.*
examples/ex41.mesh
examples/ex41-mesh.*
examples/ex41-init.*
examples/ex41-final.*
examples/deformed.*
examples/velocity.*
examples/elastic_energy.*
@@ -216,6 +227,9 @@ miniapps/electromagnetics/Joule_[0-9]*
miniapps/electromagnetics/Lorentz_[0-9]*
miniapps/electromagnetics/Lorentz.dat
miniapps/fluids/schrodinger-flow/schrodinger_flow
miniapps/fluids/schrodinger-flow/pschrodinger_flow
miniapps/gslib/field-diff
miniapps/gslib/field-interp
miniapps/gslib/findpts
@@ -223,6 +237,7 @@ miniapps/gslib/pfindpts
miniapps/gslib/schwarz_ex1
miniapps/gslib/schwarz_ex1p
miniapps/gslib/interpolated.gf
miniapps/gslib/particles_redist
miniapps/meshing/mobius-strip
miniapps/meshing/klein-bottle
@@ -269,10 +284,8 @@ miniapps/meshing/refined.mesh
miniapps/meshing/bounding-box*
miniapps/meshing/jacobian-determinant*
miniapps/mtop/parheat
miniapps/mtop/ParHeat/*
miniapps/mtop/seqheat
miniapps/mtop/SeqHeat/*
miniapps/mtop/ParaView/
miniapps/mtop/mtop_test_iso_elasticity
miniapps/autodiff/paradiff
miniapps/autodiff/seqadiff
@@ -282,16 +295,19 @@ miniapps/autodiff/seq_example
miniapps/autodiff/seq_test
miniapps/autodiff/Example/*
miniapps/navier/navier_mms
miniapps/navier/navier_kovasznay
miniapps/navier/navier_kovasznay_vs
miniapps/navier/navier_tgv
miniapps/navier/navier_shear
miniapps/navier/navier_3dfoc
miniapps/navier/navier_turbchan
miniapps/navier/navier_cht
miniapps/navier/tgv_out*.txt
miniapps/navier/*_output
miniapps/fluids/navier/navier_mms
miniapps/fluids/navier/navier_kovasznay
miniapps/fluids/navier/navier_kovasznay_vs
miniapps/fluids/navier/navier_tgv
miniapps/fluids/navier/navier_shear
miniapps/fluids/navier/navier_3dfoc
miniapps/fluids/navier/navier_turbchan
miniapps/fluids/navier/navier_cht
miniapps/fluids/navier/navier_bifurcation
miniapps/fluids/navier/Navier_Bifurcation_[0-9]*
miniapps/fluids/navier/ParaView
miniapps/fluids/navier/tgv_out*.txt
miniapps/fluids/navier/*_output
miniapps/nurbs/nurbs_ex1
miniapps/nurbs/nurbs_ex1p
@@ -414,6 +430,9 @@ miniapps/tribol/contact-patch-test
miniapps/diag-smoothers/abs-l1-jacobi
miniapps/diag-smoothers/mg-abs-l1-jacobi
miniapps/contact/contact
miniapps/contact/ParaView
# Unit test binary and outputs
tests/unit/output_meshes
tests/unit/unit_tests
+529 -70
View File
@@ -9,91 +9,550 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# DESCRIPTION:
###############################################################################
# General GitLab pipelines configurations for supercomputers and Linux clusters
# at Lawrence Livermore National Laboratory (LLNL). This entire pipeline is
# LLNL-specific!
include:
- project: 'lc-templates/id_tokens'
file: 'id_tokens.yml'
# The pipeline is divided into stages. Usually, jobs in a given stage wait for
# the preceding stages to complete before to start. However, we sometimes use
# the "needs" keyword and express the DAG of jobs for more efficiency.
# - We use setup and setup_baseline phases to download content outside of mfem
# directory.
# - Allocate/Release is where Dane resource are allocated/released once for all.
# - Build and Test is where we build and MFEM for multiple toolchains.
# - Baseline_checks gathers baseline-type test suites execution
# - Baseline_publish, only available on master, allows to update baseline
# results
stages:
- sub-pipelines
# at Lawrence Livermore National Laboratory (LLNL).
# This entire pipeline is LLNL-specific
#
# Important note: This file is a template provided by llnl/radiuss-shared-ci.
# Remains to set variable values, change the reference to the radiuss-shared-ci
# repo, opt-in and out optional features. The project can then extend it with
# additional stages.
#
# In addition, each project should copy over and complete:
# - .gitlab/custom-jobs-and-variables.yml
# - .gitlab/subscribed-pipelines.yml
#
# The jobs should be specified in a file local to the project,
# - .gitlab/jobs/${CI_MACHINE}.yml
# or generated (see LLNL/Umpire for an example).
###############################################################################
# MAP OF GITLAB CI
#######################
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# File dependencies: direct, through jobs, through variables
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# .gitlab-ci.yml
# ├── .build-and-test [job]
# │ ├── .gitlab/custom-jobs-and-variables.yml
# │ │ ├── .custom_job [job]
# │ │ ├── .reproducer_vars [job]
# │ │ ├── .report_job_success [job]
# │ │ │ └── .gitlab/scripts/report_build_and_test [script]
# │ │ │ ├── .gitlab/scripts/safe_create_rundir [script]
# │ │ │ └── .gitlab/scripts/git_try_to_push [script]
# │ │ ├── .report_job_failure [job]
# │ │ │ └── .gitlab/scripts/report_build_and_test [script]
# │ │ │ ├── .gitlab/scripts/safe_create_rundir [script]
# │ │ │ └── .gitlab/scripts/git_try_to_push [script]
# │ │ └── JOB_CMD [var]
# │ │ └── tests/gitlab/build_and_test [script]
# │ │ └── tests/gitlab/get_mfem_uberenv [script]
# │ ├── <radiuss-shared-ci>/pipelines/matrix.yml [conditional]
# │ │ ├── .on_matrix [job]
# │ │ ├── .matrix_reproducer_init [job]
# │ │ ├── .matrix_reproducer_vars [job]
# │ │ ├── .matrix_reproducer_job [job]
# │ │ ├── .matrix_job_command [job]
# │ │ └── .job_on_matrix [job]
# │ ├── <radiuss-shared-ci>/pipelines/dane.yml [conditional]
# │ │ ├── .on_dane [job]
# │ │ ├── .dane_reproducer_init [job]
# │ │ ├── .dane_reproducer_vars [job]
# │ │ ├── .dane_reproducer_job [job]
# │ │ ├── .dane_job_command [job]
# │ │ ├── .job_on_dane [job]
# │ │ ├── allocate_resources [job]
# │ │ └── release_resources [job]
# │ ├── <radiuss-shared-ci>/pipelines/tioga.yml [conditional]
# │ │ ├── .on_tioga [job]
# │ │ ├── .tioga_reproducer_init [job]
# │ │ ├── .tioga_reproducer_vars [job]
# │ │ ├── .tioga_reproducer_job [job]
# │ │ ├── .tioga_job_command [job]
# │ │ ├── .job_on_tioga [job]
# │ │ ├── allocate_resources [job]
# │ │ └── release_resources [job]
# │ ├── <artifact>/matrix-jobs.yml [conditional, from 'generate-job-lists']
# │ │ ├── .gitlab/jobs/matrix.yml
# │ │ │ ├── .matrix_reproducer_vars [job]
# │ │ │ ├── setup [job]
# │ │ │ │ └── ./tests/gitlab/build_and_test_setup [script]
# │ │ │ ├── opt_mpi_cuda_gcc [job]
# │ │ │ └── opt_mpi_cuda_hypre_cuda_gcc [job]
# │ │ └── .gitlab/jobs/matrix-reports.yml [used conditionally]
# │ │ ├── report_job_success
# │ │ └── report_job_failure
# │ ├── <artifact>/dane-jobs.yml [conditional, from 'generate-job-lists']
# │ │ ├── .gitlab/jobs/dane.yml
# │ │ │ ├── .dane_reproducer_vars [job]
# │ │ │ ├── setup [job]
# │ │ │ │ └── ./tests/gitlab/build_and_test_setup [script]
# │ │ │ ├── debug_ser_gcc_10 [job]
# │ │ │ ├── debug_par_gcc_10 [job]
# │ │ │ ├── opt_ser_gcc_10 [job]
# │ │ │ ├── opt_par_gcc_10 [job]
# │ │ │ ├── opt_par_gcc_10_sundials [job]
# │ │ │ ├── opt_par_gcc_10_petsc [job]
# │ │ │ └── opt_par_gcc_10_pumi [job]
# │ │ └── .gitlab/jobs/dane-reports.yml [used conditionally]
# │ │ ├── report_job_success
# │ │ └── report_job_failure
# │ └── <artifact>/tioga-jobs.yml [conditional, from 'generate-job-lists']
# │ ├── .gitlab/jobs/tioga.yml
# │ │ ├── .tioga_reproducer_vars [job]
# │ │ ├── setup [job]
# │ │ │ └── ./tests/gitlab/build_and_test_setup [script]
# │ │ └── cce_16_0_1 [job]
# │ └── .gitlab/jobs/tioga-reports.yml [used conditionally]
# │ ├── report_job_success
# │ └── report_job_failure
# └── .gitlab/subscribed-pipelines.yml
# ├── .machine-check [job]
# ├── generate-job-lists [job]
# ├── dane-up-check [job]
# ├── dane-build-and-test [job]
# ├── dane-baseline [job]
# │ └── .gitlab/dane-baseline.yml
# │ ├── .on_dane [job]
# │ ├── baselinecheck_mfem_intel_dane [job]
# │ │ └── .gitlab/scripts/baseline [script]
# │ ├── cleanup [job]
# │ ├── report_baseline [job]
# │ │ ├── .gitlab/scripts/safe_create_rundir [script]
# │ │ └── .gitlab/scripts/git_try_to_push [script]
# │ ├── baselinepublish_mfem_dane [job]
# │ │ └── .gitlab/scripts/rebaseline [script]
# │ ├── .gitlab/custom-jobs-and-variables.yml
# │ │ └── <same as above: see .gitlab-ci.yml/.build-and-test>
# │ └── .gitlab/configs/setup-baseline.yml
# │ └── setup_baseline [job]
# ├── tioga-up-check [job]
# ├── tioga-build-and-test [job]
# ├── matrix-up-check [job]
# └── matrix-build-and-test [job]
#
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# File tree hierarchy with file contents highlights
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# In addition to the files in the MFEM repo, the Gitlab CI uses files from the
# radiuss/radiuss-shared-ci project, see below, after the <mfem-root> tree.
#
# <mfem root>
# ├── .gitlab-ci.yml [this file]
# │ ├── <jobs>
# │ │ └── .build-and-test
# │ ├── <included files>
# │ │ ├── .gitlab/subscribed-pipelines.yml
# │ │ ├── .gitlab/custom-jobs-and-variables.yml [by ".build-and-test"]
# │ │ ├── <artifact> [by ".build-and-test"]
# │ │ │ ├── artifact: '${CI_MACHINE}-jobs.yml'
# │ │ │ └── job: 'generate-job-lists'
# │ │ └── <external> [by ".build-and-test"]
# │ │ ├── project: 'radiuss/radiuss-shared-ci'
# │ │ ├── ref: 'v2025.09.1'
# │ │ └── file: 'pipelines/${CI_MACHINE}.yml'
# │ └── <defined variables>
# │ ├── CUSTOM_CI_BUILDS_DIR
# │ ├── USER_CI_TOP_DIR
# │ ├── SHARED_REPOS_DIR
# │ ├── AUTOTEST_ROOT
# │ ├── MFEM_DATA_DIR
# │ ├── AUTOTEST
# │ ├── AUTOTEST_COMMIT
# │ ├── REBASELINE
# │ ├── GITHUB_PROJECT_NAME
# │ └── GITHUB_PROJECT_ORG
# ├── .gitlab
# │ ├── configs
# │ │ └── setup-baseline.yml
# │ │ ├── <jobs>
# │ │ │ └── setup_baseline
# │ │ └── <used variables>
# │ │ ├── MACHINE_NAME
# │ │ ├── REBASELINE
# │ │ ├── AUTOTEST
# │ │ ├── AUTOTEST_COMMIT
# │ │ ├── BUILD_ROOT
# │ │ ├── TPLS_REPO
# │ │ ├── TESTS_REPO
# │ │ ├── AUTOTEST_ROOT
# │ │ └── AUTOTEST_REPO
# │ ├── jobs
# │ │ ├── matrix-reports.yml
# │ │ │ ├── <jobs>
# │ │ │ │ ├── report_job_success
# │ │ │ │ └── report_job_failure
# │ │ │ └── <used jobs>
# │ │ │ ├── .on_matrix
# │ │ │ ├── .report_job_success
# │ │ │ └── .report_job_failure
# │ │ ├── matrix.yml
# │ │ │ ├── <jobs>
# │ │ │ │ ├── .matrix_reproducer_vars
# │ │ │ │ ├── setup
# │ │ │ │ ├── opt_mpi_cuda_gcc
# │ │ │ │ └── opt_mpi_cuda_hypre_cuda_gcc
# │ │ │ ├── <used jobs>
# │ │ │ │ ├── .reproducer_vars
# │ │ │ │ ├── .on_matrix
# │ │ │ │ └── .job_on_matrix
# │ │ │ ├── <included and used files>
# │ │ │ │ └── tests/gitlab/build_and_test_setup [by "setup"]
# │ │ │ └── <defined variables>
# │ │ │ └── SPEC
# │ │ ├── dane-reports.yml
# │ │ │ ├── <jobs>
# │ │ │ │ ├── report_job_success
# │ │ │ │ └── report_job_failure
# │ │ │ └── <used jobs>
# │ │ │ ├── .on_dane
# │ │ │ ├── .report_job_success
# │ │ │ └── .report_job_failure
# │ │ ├── dane.yml
# │ │ │ ├── <jobs>
# │ │ │ │ ├── .dane_reproducer_vars
# │ │ │ │ ├── setup
# │ │ │ │ ├── debug_ser_gcc_10
# │ │ │ │ ├── debug_par_gcc_10
# │ │ │ │ ├── opt_ser_gcc_10
# │ │ │ │ ├── opt_par_gcc_10
# │ │ │ │ ├── opt_par_gcc_10_sundials
# │ │ │ │ ├── opt_par_gcc_10_petsc
# │ │ │ │ └── opt_par_gcc_10_pumi
# │ │ │ ├── <used jobs>
# │ │ │ │ ├── .reproducer_vars
# │ │ │ │ ├── .on_dane
# │ │ │ │ └── .job_on_dane
# │ │ │ ├── <included and used files>
# │ │ │ │ └── tests/gitlab/build_and_test_setup [by "setup"]
# │ │ │ └── <defined variables>
# │ │ │ ├── SPEC
# │ │ │ └── THREADS
# │ │ ├── tioga-reports.yml
# │ │ │ ├── <jobs>
# │ │ │ │ ├── report_job_success
# │ │ │ │ └── report_job_failure
# │ │ │ └── <used jobs>
# │ │ │ ├── .on_tioga
# │ │ │ ├── .report_job_success
# │ │ │ └── .report_job_failure
# │ │ └── tioga.yml
# │ │ ├── <jobs>
# │ │ │ ├── .tioga_reproducer_vars
# │ │ │ ├── setup
# │ │ │ └── opt_mpi_rocm_hypre_rocm
# │ │ ├── <used jobs>
# │ │ │ ├── .reproducer_vars
# │ │ │ ├── .on_tioga
# │ │ │ └── .job_on_tioga
# │ │ ├── <included and used files>
# │ │ │ └── tests/gitlab/build_and_test_setup [by "setup"]
# │ │ └── <defined variables>
# │ │ ├── SPEC
# │ │ └── THREADS
# │ ├── scripts
# │ │ ├── baseline
# │ │ │ └── <used variables>
# │ │ │ ├── BASELINE_TEST
# │ │ │ ├── SYS_TYPE
# │ │ │ ├── MACHINE_NAME
# │ │ │ ├── CI_PROJECT_DIR
# │ │ │ ├── ARTIFACTS_DIR
# │ │ │ ├── BUILD_ROOT
# │ │ │ └── TPLS_DIR
# │ │ ├── git_try_to_push
# │ │ ├── rebaseline
# │ │ │ └── <used variables>
# │ │ │ ├── CI_PROJECT_DIR
# │ │ │ ├── ARTIFACTS_DIR
# │ │ │ ├── SYS_TYPE
# │ │ │ ├── BUILD_ROOT
# │ │ │ ├── MACHINE_NAME
# │ │ │ └── CI_PIPELINE_ID
# │ │ ├── report_build_and_test
# │ │ │ ├── <used files>
# │ │ │ │ ├── .gitlab/scripts/safe_create_rundir
# │ │ │ │ └── .gitlab/scripts/git_try_to_push
# │ │ │ └── <used variables>
# │ │ │ ├── AUTOTEST_ROOT
# │ │ │ ├── CI_COMMIT_REF_SLUG
# │ │ │ ├── CI_PROJECT_DIR
# │ │ │ ├── CI_PIPELINE_URL
# │ │ │ ├── AUTOTEST_COMMIT
# │ │ │ └── CI_MACHINE
# │ │ └── safe_create_rundir
# │ ├── custom-jobs-and-variables.yml
# │ │ ├── <jobs>
# │ │ │ ├── .custom_job
# │ │ │ ├── .reproducer_vars
# │ │ │ ├── .report_job_success
# │ │ │ └── .report_job_failure
# │ │ ├── <used files>
# │ │ │ ├── tests/gitlab/build_and_test [in JOB_CMD]
# │ │ │ └── .gitlab/scripts/report_build_and_test [by .report_job_*]
# │ │ ├── <defined variables>
# │ │ │ ├── JOB_CMD
# │ │ │ ├── BUILD_ROOT
# │ │ │ ├── ALLOC_NAME
# │ │ │ ├── TPLS_REPO
# │ │ │ ├── TESTS_REPO
# │ │ │ ├── AUTOTEST_REPO
# │ │ │ ├── MFEM_DATA_REPO
# │ │ │ ├── ARTIFACTS_DIR: artifacts
# │ │ │ ├── SLURM_OVERLAP: 1
# │ │ │ ├── DANE_SHARED_ALLOC
# │ │ │ ├── DANE_JOB_ALLOC
# │ │ │ ├── TIOGA_SHARED_ALLOC
# │ │ │ ├── TIOGA_JOB_ALLOC
# │ │ │ └── MATRIX_JOB_ALLOC
# │ │ └── <used variables>
# │ │ ├── SPEC
# │ │ ├── BUILD_ROOT
# │ │ └── ...
# │ ├── dane-baseline.yml
# │ │ ├── <jobs>
# │ │ │ ├── .on_dane
# │ │ │ ├── baselinecheck_mfem_intel_dane
# │ │ │ ├── cleanup
# │ │ │ ├── report_baseline
# │ │ │ └── baselinepublish_mfem_dane
# │ │ ├── <included and used files>
# │ │ │ ├── .gitlab/custom-jobs-and-variables.yml
# │ │ │ ├── .gitlab/configs/setup-baseline.yml
# │ │ │ ├── .gitlab/scripts/rebaseline
# │ │ │ ├── .gitlab/scripts/baseline
# │ │ │ └── .gitlab/scripts/git_try_to_push
# │ │ ├── <defined variables>
# │ │ │ ├── BASELINE_TEST: baseline
# │ │ │ ├── MACHINE_NAME: dane
# │ │ │ ├── TPLS_DIR
# │ │ │ └── export MFEM_TEST_NP
# │ │ └── <used variables>
# │ │ ├── ON_DANE
# │ │ ├── AUTOTEST [defined by .gitlab-ci.yml]
# │ │ ├── BUILD_ROOT [defined by custom-jobs-and-variables.yml]
# │ │ ├── TPLS_DIR [defined by this file]
# │ │ ├── ARTIFACTS_DIR [defined by custom-jobs-and-variables.yml]
# │ │ ├── MACHINE_NAME [defined by this file]
# │ │ ├── AUTOTEST_COMMIT [defined by .gitlab-ci.yml]
# │ │ ├── AUTOTEST_ROOT [defined by .gitlab-ci.yml]
# │ │ ├── BASELINE_TEST [defined by this file]
# │ │ └── REBASELINE [defined by .gitlab-ci.yml]
# │ └── subscribed-pipelines.yml
# │ ├── <jobs>
# │ │ ├── .machine-check
# │ │ ├── generate-job-lists
# │ │ ├── dane-up-check
# │ │ ├── dane-build-and-test
# │ │ ├── dane-baseline
# │ │ ├── tioga-up-check
# │ │ ├── tioga-build-and-test
# │ │ ├── matrix-up-check
# │ │ └── matrix-build-and-test
# │ ├── <used jobs>
# │ │ └── .build-and-test [from ".gitlab-ci.yml"]
# │ ├── <included files>
# │ │ └── .gitlab/dane-baseline.yml [by "dane-baseline"]
# │ └── <used variables>
# │ ├── GITHUB_PROJECT_ORG
# │ ├── GITHUB_PROJECT_NAME
# │ ├── AUTOTEST
# │ ├── AUTOTEST_COMMIT
# │ └── REBASELINE
# └── tests
# ├── gitlab
# │ ├── build_and_test
# │ │ ├── <builds and tests a given MFEM spec with uberenv>
# │ │ ├── <used files>
# │ │ │ ├── tests/uberenv/uberenv.py [deps mode, cloned]
# │ │ │ └── tests/gitlab/get_mfem_uberenv [deps mode]
# │ │ └── <used variables>
# │ │ ├── SYS_TYPE
# │ │ ├── THREADS [num. parallel jobs to build MFEM]
# │ │ ├── MODULE_LIST [modules to load]
# │ │ ├── CI_JOB_ID
# │ │ ├── USE_DEV_SHM
# │ │ ├── SPACK_DEBUG
# │ │ ├── DEBUG_MODE
# │ │ ├── REGISTRY_TOKEN
# │ │ ├── CI_REGISTRY_USER (defined by Gitlab)
# │ │ ├── USER
# │ │ ├── CI_REGISTRY_IMAGE (defined by Gitlab)
# │ │ └── CI_JOB_TOKEN (defined by Gitlab)
# │ ├── build_and_test_setup
# │ │ ├── <updates MFEM_DATA_REPO and AUTOTEST_REPO using locks>
# │ │ └── <used variables>
# │ │ ├── MFEM_DATA_REPO
# │ │ ├── SHARED_REPOS_DIR
# │ │ ├── AUTOTEST_REPO
# │ │ └── AUTOTEST_ROOT
# │ └── get_mfem_uberenv
# │ ├── <github.com/mfem/mfem-uberenv.git -> tests/uberenv>
# │ └── <defines the uberenv hash to use>
# └── uberenv [cloned by tests/gitlab/get_mfem_uberenv]
# └── uberenv.py
#
# <root of radiuss/radiuss-shared-ci, ref: 'v2025.09.1'>
# └── pipelines
# ├── matrix.yml
# │ ├── <jobs>
# │ │ ├── .on_matrix
# │ │ ├── .matrix_reproducer_init
# │ │ ├── .matrix_reproducer_vars
# │ │ ├── .matrix_reproducer_job
# │ │ ├── .matrix_job_command
# │ │ └── .job_on_matrix
# │ ├── <used jobs>
# │ │ └── .custom_job [from .gitlab/custom-jobs-and-variables.yml]
# │ └── <used variables>
# │ ├── ON_MATRIX
# │ ├── ADVANCED_JOB
# │ ├── ALL_TARGETS
# │ ├── SYS_TYPE
# │ ├── LLNL_SERVICE_USER
# │ ├── USER
# │ ├── GITHUB_PROJECT_NAME
# │ ├── GITHUB_PROJECT_ORG
# │ ├── MATRIX_JOB_ALLOC
# │ └── JOB_CMD
# ├── dane.yml
# │ ├── <jobs>
# │ │ ├── .on_dane
# │ │ ├── .dane_reproducer_init
# │ │ ├── .dane_reproducer_vars
# │ │ ├── .dane_reproducer_job
# │ │ ├── .dane_job_command
# │ │ ├── .job_on_dane
# │ │ ├── allocate_resources
# │ │ └── release_resources
# │ ├── <used jobs>
# │ │ └── .custom_job [from .gitlab/custom-jobs-and-variables.yml]
# │ ├── <defined variables>
# │ │ └── export JOBID
# │ └── <used variables>
# │ ├── ON_DANE
# │ ├── ADVANCED_JOB
# │ ├── ALL_TARGETS
# │ ├── SYS_TYPE
# │ ├── LLNL_SERVICE_USER
# │ ├── USER
# │ ├── GITHUB_PROJECT_NAME
# │ ├── GITHUB_PROJECT_ORG
# │ ├── DANE_JOB_ALLOC
# │ ├── JOB_CMD
# │ ├── JOBID
# │ ├── ALLOC_NAME
# │ └── DANE_SHARED_ALLOC
# └── tioga.yml
# ├── <jobs>
# │ ├── .on_tioga
# │ ├── .tioga_reproducer_init
# │ ├── .tioga_reproducer_vars
# │ ├── .tioga_reproducer_job
# │ ├── .tioga_job_command
# │ ├── .job_on_tioga
# │ ├── allocate_resources
# │ └── release_resources
# ├── <used jobs>
# │ └── .custom_job [from .gitlab/custom-jobs-and-variables.yml]
# ├── <defined variables>
# │ └── PROXY
# └── <used variables>
# ├── ON_TIOGA
# ├── ADVANCED_JOB
# ├── ALL_TARGETS
# ├── SYS_TYPE
# ├── LLNL_SERVICE_USER
# ├── USER
# ├── GITHUB_PROJECT_NAME
# ├── GITHUB_PROJECT_ORG
# ├── TIOGA_JOB_ALLOC
# ├── JOB_CMD
# ├── PROXY
# ├── ALLOC_NAME
# └── TIOGA_SHARED_ALLOC
###############################################################################
# We define the following GitLab pipeline variables:
variables:
##### LC GITLAB CONFIGURATION
CUSTOM_CI_BUILDS_DIR: "/usr/workspace/mfem/gitlab-runner"
##### PROJECT VARIABLES
USER_CI_TOP_DIR: "${CUSTOM_CI_BUILDS_DIR}/${GITLAB_USER_LOGIN}"
SHARED_REPOS_DIR: "${USER_CI_TOP_DIR}/repos"
AUTOTEST_ROOT: "${SHARED_REPOS_DIR}"
# MFEM_DATA_DIR is setup in '.gitlab/configs/setup-build-and-test.yml' and
# used in '.gitlab/configs/<machine>-config.yml':
MFEM_DATA_DIR: "${SHARED_REPOS_DIR}/mfem-data"
# AUTOTEST: enable (ON/YES) or disable (any other value) test reporting. See
# also AUTOTEST_COMMIT.
AUTOTEST: "OFF"
# AUTOTEST_COMMIT: used only when AUTOTEST is set to ON/YES.
# * If AUTOTEST_COMMIT is set to ON/YES, reporting jobs will commit their
# files to the MFEM/autotest repo.
# * If AUTOTEST_COMMIT is NOT set to ON/YES, reporting jobs will NOT commit
# their files to the MFEM/autotest repo. Instead they will just show the
# contents of the report files and remove them.
AUTOTEST_COMMIT: "ON"
# REBASELINE:
# Defines the default choice for updating the saved baseline results. By default
# the baseline can only be updated from the master branch. This variable offers
# the option to manually ask for rebaselining from another branch if necessary.
REBASELINE: "NO"
AUTOTEST: "NO"
# AUTOTEST_COMMIT: used only when AUTOTEST is set to YES.
# * If AUTOTEST_COMMIT is NOT set to NO, reporting jobs will commit their
# files to the MFEM/autotest repo.
# * If AUTOTEST_COMMIT is set to NO, reporting jobs will NOT commit their
# files to the MFEM/autotest repo. Instead they will just show the contents
# of the report files and remove them.
AUTOTEST_COMMIT: "YES"
REBASELINE: "OFF"
# Trigger subpipelines:
dane-build-and-test:
stage: sub-pipelines
##### SHARED_CI CONFIGURATION
# Required information about GitHub repository
GITHUB_PROJECT_NAME: "mfem"
GITHUB_PROJECT_ORG: "MFEM"
# Override the pattern describing branches that will skip the "draft PR filter
# test". Add protected branches here. See default value in
# preliminary-ignore-draft-pr.yml.
# ALWAYS_RUN_PATTERN: ""
###############################################################################
##### High level stages
# We organize the test-pipelines stage with sub-pipelines. Each sub-pipeline
# corresponds to a test batch on a given machine.
stages:
- prerequisites
- test-pipelines
###############################################################################
# Template for jobs triggering a build-and-test sub-pipeline:
.build-and-test:
stage: test-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
# pipelines manually or using scheduling
# Explicitly pass down values that are not always propagated to child
# pipelines, e.g. when a variable is set in the "Settings -> CI" web
# interface (project variables).
# Note: in some cases, this does not work as expected, e.g. when the
# variable is not re-defined in the web interface; in such cases, the child
# pipeline gets a definition like '${AUTOTEST}', i.e. it behaves as if
# AUTOTEST is undefined, even though there is a default value in
# .gitlab-ci.yml.
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/dane-build-and-test.yml
include:
- local: '.gitlab/custom-jobs-and-variables.yml'
- project: 'radiuss/radiuss-shared-ci'
ref: 'v2025.09.1'
file: 'pipelines/${CI_MACHINE}.yml'
- artifact: '${CI_MACHINE}-jobs.yml'
job: 'generate-job-lists'
strategy: depend
forward:
pipeline_variables: true
dane-baseline:
stage: sub-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
# pipelines manually or using scheduling
REBASELINE: "${REBASELINE}"
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/dane-baseline.yml
strategy: depend
lassen-build-and-test:
stage: sub-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
# pipelines manually or using scheduling
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/lassen-build-and-test.yml
strategy: depend
corona-build-and-test:
stage: sub-pipelines
variables:
# Explicitly pass down values that we want to be able to set when triggering
# pipelines manually or using scheduling
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/corona-build-and-test.yml
strategy: depend
###############################################################################
include:
# Sets ID tokens for every job using `default:`
- project: 'lc-templates/id_tokens'
file: 'id_tokens.yml'
# [Optional] checks preliminary to running the actual CI test
#- project: 'radiuss/radiuss-shared-ci'
# ref: 'v2025.09.1'
# file: 'preliminary-ignore-draft-pr.yml'
# pipelines subscribed by the project
- local: '.gitlab/subscribed-pipelines.yml'
+34 -12
View File
@@ -15,9 +15,9 @@ and nightly testing on GitLab.
## Top level
The root configuration file is `.gitlab-ci.yml` at the root of MFEM repo.
This file only defines one stage, in which we trigger several
sub-pipelines.
The root configuration file is `.gitlab-ci.yml` at the root of MFEM repo. This
file only defines three stages, a prerequisites one, and two main stages in
which we trigger several sub-pipelines.
We use sub-pipelines to isolate the test for one combination of `machine`
and `test type`.
@@ -25,8 +25,8 @@ and `test type`.
Machines typically include:
* Dane: Intel Sapphire Rapids
* Lassen: Power9 + Nvidia GPU
* Corona: AMD GPU
* Matrix: Intel Sapphire Rapids + Nvidia H100 GPU
* Tioga: AMD MI250X GPU
Test types include:
@@ -39,9 +39,31 @@ altering the scheduling, execution and displaying of the others.
## Sub-pipelines
Each file is this directory is the root configuration file for one
sub-pipeline. The naming reflects the corresponding couple (`machine`,
`test_type`).
### build-and-test
The build-and-test sub-pipelines leverage RADIUSS Shared CI to share most of
the CI implementation. RADIUSS Shared CI provides a shared CI infrastructure
vetted on most LC systems of interest and efficiently leveraging each machine
scheduler to increase CI throughput. The maintenance of RADIUSS Shared CI is
shared among several RADIUSS projects.
Jobs for the build-and-test sub-pipelines are defined in the jobs directory.
Because build-and-test jobs leverage Uberenv and Spack to build the
dependencies automatically, the jobs essentially consists in a `spack spec`
defined in the jobs files, and some scheduling parameters defined in the
`.gitlab/custom-jobs-and-variables.yml` file.
Build-and-test jobs all run the `tests/gitlab/build_and_test` script.
The build-and-test pipelines are controlled by the
`.gitlab/subscribed-pipelines.yml` which defines which machines to run on and
implements additional features like machine availability check, and job list
generation.
### baseline
Baseline sub-pipelines are described by files with names reflecting the
machine it runs on, e.g. `dane-baseline`.
Those files define the *stages* and the *jobs* for the sub-pipeline. They
also contain any configuration that cannot be shared. For the most part
@@ -63,11 +85,11 @@ usage function. This should be improved.
# More testing
## Adding a new target to a build_and_test pipeline
## Adding a new target to a build-and-test pipeline
`build_and_test` pipelines rely on Spack to install dependencies. Spack is
`build-and-test` pipelines rely on Spack to install dependencies. Spack is
driven by Uberenv which helps freezing Spack configuration: the goal being to
point to specific commit in Spack and isolate its configuration so that it is
point to a specific commit in Spack and isolate its configuration so that it is
not influenced by the user environment. More documentation about this can be
found in `tests/gitlab`.
@@ -82,7 +104,7 @@ spack spec to use. Adding a job on Dane for example resumes to:
<job_name>:
variables:
SPEC: "<spack_spec>"
extends: .build_and_test_on_dane
extends: .job_on_dane
```
The remaining and non trivial work is to make sure this spec is working. To
-40
View File
@@ -1,40 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
include:
- project: 'lc-templates/id_tokens'
file: 'id_tokens.yml'
# We define the following GitLab pipeline variables:
variables:
# The path to the shared resource between all jobs. For example, external
# repositories like 'tests' and 'tpls' are cloned here. Also, 'tpls' is built
# once for all targets, so that build happen here. The BUILD_ROOT is unique to
# the pipeline, preventing any form of concurrency with other pipelines. This
# also means that the BUILD_ROOT directory will never be cleaned.
# TODO: add a clean-up mechanism
BUILD_ROOT: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${MACHINE_NAME}-pipeline-${CI_PIPELINE_ID}
# On LLNL's Dane, there is only one allocation shared among jobs in order to
# save time and resource. This allocation has to be uniquely named so that we
# are sure to retrieve it.
ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID}
# Git repositories used in the pipeline
TPLS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tpls.git
TESTS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tests.git
AUTOTEST_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/autotest.git
MFEM_DATA_REPO: https://github.com/mfem/data.git
# Directory used to place artifacts.
ARTIFACTS_DIR: artifacts
SLURM_OVERLAP: 1
-59
View File
@@ -1,59 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# GitLab pipeline configuration for the Corona machine at LLNL
variables:
MACHINE_NAME: corona
.on_corona:
tags:
- shell
- corona
rules:
# Don't run corona jobs if...
# Note: This makes corona an "opt-in" machine. To activate builds on corona
# for a given GitLab clone of MFEM, go to Setting/CI-CD/variables, and set
# "ON_CORONA" to "ON". An LC account on for corona is required to trigger a
# pipeline there.
- if: '$CI_COMMIT_BRANCH =~ /_cnone/ || $ON_CORONA != "ON"'
when: never
# Don't run autotest update if...
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
when: never
# Report success on success status
- if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"'
when: on_success
# Report failure on failure status
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"'
when: on_failure
# Always release resource
- if: '$CI_JOB_NAME =~ /release_resource/'
when: always
# Always cleanup
- if: '$CI_JOB_NAME =~ /cleanup/'
when: always
# Default is to run if previous stage succeeded
- when: on_success
# Spack helped builds
# Generic corona build job, extending build script
.build_and_test_on_corona:
extends: [.on_corona]
stage: build_and_test
script:
# THREADS is used by 'tests/gitlab/build_and_test', run below
- export THREADS=12
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- echo ${MFEM_DATA_DIR}
- echo ${SPEC}
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) -t 15 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
-56
View File
@@ -1,56 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# GitLab pipelines configurations for the Dane machine at LLNL
variables:
MACHINE_NAME: dane
.on_dane:
tags:
- shell
- dane
rules:
# Don't run dane jobs if...
- if: '$CI_COMMIT_BRANCH =~ /_qnone/ || $ON_DANE == "OFF"'
when: never
# Don't run autotest update if...
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
when: never
# Report success on success status
- if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"'
when: on_success
# Report failure on failure status
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"'
when: on_failure
# Always release resource
- if: '$CI_JOB_NAME =~ /release_resource/'
when: always
# Always cleanup
- if: '$CI_JOB_NAME =~ /cleanup/'
when: always
# Default is to run if previous stage succeeded
- when: on_success
# Spack helped builds
# Generic dane build job, extending build script
.build_and_test_on_dane:
extends: [.on_dane]
stage: build_and_test
script:
# THREADS is used by 'tests/gitlab/build_and_test', run below
# Dane has 224 threads/node and we run 7 separate jobs: 224=7*32
- export THREADS=28
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- echo ${MFEM_DATA_DIR}
- echo ${SPEC}
- srun $( [[ -n "${JOBID}" ]] && echo "--jobid=${JOBID}" ) --reservation=ci -t 60 -N 1 tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
-48
View File
@@ -1,48 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# GitLab pipelines configurations for the Lassen machine at LLNL
variables:
MACHINE_NAME: lassen
.on_lassen:
tags:
- shell
- lassen
rules:
- if: '$CI_COMMIT_BRANCH =~ /_lnone/ || $ON_LASSEN == "OFF"' #run except if ...
when: never
# Don't run autotest update if...
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "YES"'
when: never
# Report success on success status
- if: '$CI_JOB_NAME =~ /report_job_success/ && $AUTOTEST == "YES"'
when: on_success
# Report failure on failure status
- if: '$CI_JOB_NAME =~ /report_job_failure/ && $AUTOTEST == "YES"'
when: on_failure
# Always cleanup
- if: '$CI_JOB_NAME =~ /cleanup/'
when: always
- when: on_success
# Lassen uses a different job scheduler (spectrum lsf) that does not allow
# pre-allocation the same way slurm does. We use the pci queue on lassen
# to speed-up the allocation.
.build_and_test_on_lassen:
extends: [.on_lassen]
stage: build_and_test
script:
- echo ${MFEM_DATA_DIR}
- echo ${SPEC}
# Next script uses 'THREADS': leaving it empty --> it uses 'make all -j'
- lalloc 1 -W 45 -q pci --atsdisable tests/gitlab/build_and_test --spec "${SPEC}" --data-dir "${MFEM_DATA_DIR}" --data
needs: [setup]
-77
View File
@@ -1,77 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Jobs report
.report_job_success:
script:
- echo ${MACHINE_NAME}
- echo ${AUTOTEST}
- echo ${AUTOTEST_COMMIT}
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
- cd ${AUTOTEST_ROOT}
- |
(
date
echo "Waiting to acquire lock on '$PWD/autotest.lock' ..."
# try to get an exclusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
done
echo "Acquired lock on '$PWD/autotest.lock'"
date
# Report SUCCESS while holding the file lock on 'autotest.lock'.
# The next script uses the following environment variables:
# - MACHINE_NAME, AUTOTEST_ROOT, AUTOTEST_COMMIT
# - CI_COMMIT_REF_SLUG, CI_PROJECT_DIR, CI_PIPELINE_URL
# It also calls the script '.gitlab/scripts/safe_create_rundir'.
${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test_success
err=$?
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> autotest.lock
.report_job_failure:
script:
- echo ${MACHINE_NAME}
- echo ${AUTOTEST}
- echo ${AUTOTEST_COMMIT}
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
- cd ${AUTOTEST_ROOT}
- |
(
date
echo "Waiting to acquire lock on '$PWD/autotest.lock' ..."
# try to get an exclusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
done
echo "Acquired lock on '$PWD/autotest.lock'"
date
# Report FAILURE while holding the file lock on 'autotest.lock'.
# The next script uses the following environment variables:
# - MACHINE_NAME, AUTOTEST_ROOT, AUTOTEST_COMMIT
# - CI_COMMIT_REF_SLUG, CI_PROJECT_DIR, CI_PIPELINE_URL
# It also calls the script '.gitlab/scripts/safe_create_rundir'.
${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test_failure
err=$?
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> autotest.lock
-90
View File
@@ -1,90 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Setup clones the mfem/data repo in ${SHARED_REPOS_DIR}. The build_and_test
# script then symlinks the repo to the parent directory of the MFEM source
# directory. Unit tests that depend on the mfem/data repo will then detect that
# this directory is present and be enabled.
setup:
tags:
- shell
- dane
stage: setup
variables:
GIT_STRATEGY: none
script:
#
# Setup MFEM_DATA_DIR=${SHARED_REPOS_DIR}/mfem-data, see '.gitlab-ci.yml'
# and '.gitlab/configs/<machine>-config.yml'
#
- echo "MACHINE_NAME = ${MACHINE_NAME}"
- echo "AUTOTEST = ${AUTOTEST}"
- echo "AUTOTEST_COMMIT = ${AUTOTEST_COMMIT}"
- echo "SHARED_REPOS_DIR ${SHARED_REPOS_DIR}"
- mkdir -p ${SHARED_REPOS_DIR} && cd ${SHARED_REPOS_DIR}
- command -v flock || echo "Required command 'flock' not found"
- |
(
date
echo "Waiting to acquire lock on '$PWD/mfem-data.lock' ..."
# try to get an exclusive lock on fd 9 (mfem-data.lock) repeating the
# try every 5 seconds; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
done
echo "Acquired lock on '$PWD/mfem-data.lock'"
date
# clone/update the mfem/data repo while holding the file lock on
# 'mfem-data.lock'
err=0
if [[ ! -d "mfem-data" ]]; then
git clone ${MFEM_DATA_REPO} "mfem-data"
else
cd "mfem-data" && git pull && cd ..
fi || err=1
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> mfem-data.lock
#
# Setup ${AUTOTEST_ROOT}/autotest:
#
- echo "AUTOTEST_ROOT ${AUTOTEST_ROOT}"
- mkdir -p ${AUTOTEST_ROOT} && cd ${AUTOTEST_ROOT}
- |
(
date
echo "Waiting to acquire lock on '$PWD/autotest.lock' ..."
# try to get an exclusive lock on fd 9 (autotest.lock) repeating the try
# every 5 seconds; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do
sleep 5
done
echo "Acquired lock on '$PWD/autotest.lock'"
date
# clone/update the autotest repo while holding the file lock on
# 'autotest.lock'
err=0
if [[ ! -d "autotest" ]]; then
git clone ${AUTOTEST_REPO}
else
cd autotest && git pull && cd ..
fi || err=1
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> autotest.lock
-67
View File
@@ -1,67 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
stages:
- setup
- allocate_resource
- build_and_test
- release_resource_and_report
# Slurm shared allocation
allocate_resource:
variables:
GIT_STRATEGY: none
extends: .on_corona
stage: allocate_resource
script:
- echo ${ALLOC_NAME}
- salloc --exclusive --nodes=1 --partition=mi60 --time=45 --no-shell --job-name=${ALLOC_NAME}
timeout: 6h
needs: [setup]
# Build and test jobs, simply provide a spec
rocm_gcc_8.3.1:
variables:
SPEC: "@develop%gcc@8.3.1+rocm amdgpu_target=gfx906"
extends: .build_and_test_on_corona
needs: [allocate_resource]
# Release slurm allocation
release_resource:
variables:
GIT_STRATEGY: none
extends: .on_corona
stage: release_resource_and_report
script:
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- ([[ -n "${JOBID}" ]] && scancel ${JOBID})
needs: [rocm_gcc_8.3.1]
# Jobs report
report_job_success:
stage: release_resource_and_report
extends:
- .on_corona
- .report_job_success
report_job_failure:
stage: release_resource_and_report
extends:
- .on_corona
- .report_job_failure
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/corona-config.yml
- local: .gitlab/configs/setup-build-and-test.yml
- local: .gitlab/configs/report-build-and-test.yml
+132
View File
@@ -0,0 +1,132 @@
# 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:
- project: 'lc-templates/id_tokens'
file: 'id_tokens.yml'
# We define the following GitLab pipeline variables:
variables:
# Set the build-and-test command.
# Nested variables are allowed and useful to customize the job command. We
# protect variables with quotes so that their value may remain a string even if
# they contain whitespaces.
JOB_CMD:
value: tests/gitlab/build_and_test --spec \"${SPEC}\" --data-dir ${MFEM_DATA_DIR} --data
# The path to the shared resource between all jobs in the 'dane-baseline'
# pipeline. For example, external repositories like 'tests' and 'tpls' are
# cloned here. Also, 'tpls' is built once for all targets, so that build happens
# here. The BUILD_ROOT is unique to the pipeline, preventing any form of
# concurrency with other pipelines. This directory is removed by the 'cleanup'
# stage in the 'dane-baseline' pipeline.
BUILD_ROOT: ${USER_CI_TOP_DIR}/${CI_PROJECT_NAME}-${CI_MACHINE}-pipeline-${CI_PIPELINE_ID}
# On LLNL's dane and tioga, the 'build-and-test' pipelines creates only one
# allocation shared among jobs in the pipeline in order to save time and
# resources. This allocation has to be uniquely named so that we are sure to
# retrieve it and avoid collisions.
ALLOC_NAME: ${CI_PROJECT_NAME}_ci_${CI_PIPELINE_ID}
# Git repositories used in the pipelines:
# - TPLS_REPO and TESTS_REPO are used only by the 'dane-baseline' pipeline
# - AUTOTEST_REPO is used by all pipelines
# - MFEM_DATA_REPO is used only by the 'build-and-test' pipelines
TPLS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tpls.git
TESTS_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/tests.git
AUTOTEST_REPO: ssh://git@mybitbucket.llnl.gov:7999/mfem/autotest.git
MFEM_DATA_REPO: https://github.com/mfem/data.git
# Directory used to place artifacts:
# - ARTIFACTS_DIR is only used by the 'dane-baseline' pipeline
ARTIFACTS_DIR: artifacts
SLURM_OVERLAP: 1
# Dane
# Arguments for top level allocation
DANE_SHARED_ALLOC: "--exclusive --reservation=ci --time=60 --nodes=1"
# Arguments for job level allocation
# Note: We repeat the reservation, necessary when jobs are manually re-triggered.
DANE_JOB_ALLOC: "--reservation=ci --overlap --nodes=1"
# Tioga
# Arguments for top level allocation
TIOGA_SHARED_ALLOC: "--queue=pci --exclusive --time-limit=45m --nodes=1"
# Arguments for job level allocation
TIOGA_JOB_ALLOC: "--nodes=1 --begin-time=+5s"
# Matrix
# Arguments for top level allocation
MATRIX_SHARED_ALLOC: "-p pdebug --exclusive --time=45 --nodes=1 -G 4"
# Arguments for job level allocation
# Note: We repeat the reservation, necessary when jobs are manually re-triggered.
MATRIX_JOB_ALLOC: "--overlap --nodes=1"
# Configuration shared by build and test jobs specific to this project.
# Not all configuration can be shared. Here projects can fine tune the
# CI behavior.
# See Umpire for an example (export junit test reports).
.custom_job:
artifacts:
reports:
# Note: this part is not used by the 'dane-baseline' pipeline.
# FIXME: BUILD_ROOT, TPLS_REPO, TESTS_REPO are not needed here.
# Also, the definition of SHARED_REPOS_DIR is wrong.
.reproducer_vars:
script:
- |
echo -e "
# Variables \n
export SPEC=\"${SPEC//\"/\\\"}\" \n
# Directories \n
export BUILD_ROOT=\"\${working_dir}\" \n
export SHARED_REPOS_DIR=\"\${BUILD_ROOT}/..\" \n
export MFEM_DATA_DIR=\"\${SHARED_REPOS_DIR}/mfem-data\" \n
# Repositories \n
export TPLS_REPO=\"${TPLS_REPO//\"/\\\"}\" \n
export TESTS_REPO=\"${TESTS_REPO//\"/\\\"}\" \n
export AUTOTEST_REPO=\"${AUTOTEST_REPO//\"/\\\"}\" \n
export MFEM_DATA_REPO=\"${MFEM_DATA_REPO//\"/\\\"}\" \n
# Setup directories \n
./tests/gitlab/build_and_test_setup \n
# Using the CI build cache is optional and requires a token. Set it like so: \n
# export REGISTRY_TOKEN=\"<your token here>\" \n"
#
# Jobs report
.report_job_success:
script:
- ${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test SUCCESS
rules:
- when: on_success
.report_job_failure:
script:
- ${CI_PROJECT_DIR}/.gitlab/scripts/report_build_and_test FAILURE
rules:
- when: on_failure
# Keep the following for debugging purposes: renaming this job from
# '.show_variables' to 'show_variables' will insert this debug job at the
# beginning of all child pipelines.
.show_variables:
tags: [shell, oslic]
variables:
GIT_STRATEGY: none
stage: .pre
script:
- |
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
echo "AUTOTEST=${AUTOTEST}"
echo "AUTOTEST_COMMIT=${AUTOTEST_COMMIT}"
echo "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
# Fail the job on purpose to prevent the rest of the pipeline from running
false
+33 -5
View File
@@ -11,6 +11,7 @@
variables:
BASELINE_TEST: baseline
MACHINE_NAME: dane
stages:
- setup
@@ -19,6 +20,25 @@ stages:
- cleanup
- baseline_publish
.on_dane:
tags:
- shell
- dane
rules:
# Don't run dane jobs if...
- if: '$ON_DANE == "OFF"'
when: never
# Don't run autotest update if...
# Note: in some cases, the content of AUTOTEST can be '${AUTOTEST}', so we
# need to treat that value as the default value of 'OFF'.
- if: '$CI_JOB_NAME =~ /report/ && $AUTOTEST != "ON" && $AUTOTEST != "YES"'
when: never
# Always cleanup
- if: '$CI_JOB_NAME =~ /cleanup/'
when: always
# Default is to run if previous stage succeeded
- when: on_success
baselinecheck_mfem_intel_dane:
extends: [.on_dane]
stage: baseline_check
@@ -29,6 +49,9 @@ baselinecheck_mfem_intel_dane:
# .gitlab/configs/setup-baseline.yml.
TPLS_DIR: ${BUILD_ROOT}/tpls
script:
- echo "AUTOTEST=$AUTOTEST"
- echo "AUTOTEST_COMMIT=$AUTOTEST_COMMIT"
- echo "AUTOTEST_ROOT=$AUTOTEST_ROOT"
- echo ${BUILD_ROOT}
- echo ${TPLS_DIR}
# Used by the tests in MFEM/tests, dane has 224 threads/node:
@@ -89,7 +112,13 @@ report_baseline:
cp ${rundir}/pipeline.txt ${rundir}/autotest-email.html
fi
msg="GitLab CI log for ${BASELINE_TEST} on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
# Note: in some cases, the content of AUTOTEST_COMMIT can be
# '${AUTOTEST_COMMIT}', so we need to treat that value as the default
# value of 'ON'.
if [[ "$AUTOTEST_COMMIT" == '${AUTOTEST_COMMIT}' ]]; then
AUTOTEST_COMMIT="ON"
fi
if [[ "$AUTOTEST_COMMIT" == "ON" || "$AUTOTEST_COMMIT" == "YES" ]]; then
git pull && \
git add ${rundir} && \
git commit -m "${msg}" && \
@@ -117,8 +146,8 @@ baselinepublish_mfem_dane:
extends: [.on_dane]
stage: baseline_publish
rules:
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "YES"'
- if: '$REBASELINE == "YES"'
# - if: '$CI_COMMIT_BRANCH == "master" || $REBASELINE == "ON"'
- if: '$REBASELINE == "ON"'
when: manual
script:
- echo ${BUILD_ROOT}
@@ -128,6 +157,5 @@ baselinepublish_mfem_dane:
- .gitlab/scripts/rebaseline
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/dane-config.yml
- local: .gitlab/custom-jobs-and-variables.yml
- local: .gitlab/configs/setup-baseline.yml
-94
View File
@@ -1,94 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
stages:
- setup
- allocate_resource
- build_and_test
- release_resource_and_report
# Allocate
allocate_resource:
variables:
GIT_STRATEGY: none
extends: .on_dane
stage: allocate_resource
script:
- echo ${ALLOC_NAME}
- salloc --exclusive --nodes=1 --reservation=ci --time=60 --no-shell --job-name=${ALLOC_NAME}
timeout: 6h
# GitLab jobs for the Dane machine at LLNL
debug_ser_gcc_10:
variables:
SPEC: "%gcc@10.3.1 +debug~mpi"
extends: .build_and_test_on_dane
debug_par_gcc_10:
variables:
SPEC: "%gcc@10.3.1 +debug+mpi"
extends: .build_and_test_on_dane
opt_ser_gcc_10:
variables:
SPEC: "%gcc@10.3.1 ~mpi"
extends: .build_and_test_on_dane
opt_par_gcc_10:
variables:
SPEC: "%gcc@10.3.1"
extends: .build_and_test_on_dane
opt_par_gcc_10_sundials:
variables:
SPEC: "%gcc@10.3.1 +sundials"
extends: .build_and_test_on_dane
opt_par_gcc_10_petsc:
variables:
SPEC: "%gcc@10.3.1 +petsc ^petsc+mumps~superlu-dist"
extends: .build_and_test_on_dane
opt_par_gcc_10_pumi:
variables:
SPEC: "%gcc@10.3.1 +pumi"
extends: .build_and_test_on_dane
# Release
release_resource:
variables:
GIT_STRATEGY: none
extends: .on_dane
stage: release_resource_and_report
script:
- echo ${ALLOC_NAME}
- export JOBID=$(squeue -h --name=${ALLOC_NAME} --format=%A)
- echo ${JOBID}
- ([[ -n "${JOBID}" ]] && scancel ${JOBID})
# Jobs report
report_job_success:
stage: release_resource_and_report
extends:
- .on_dane
- .report_job_success
report_job_failure:
stage: release_resource_and_report
extends:
- .on_dane
- .report_job_failure
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/dane-config.yml
- local: .gitlab/configs/setup-build-and-test.yml
- local: .gitlab/configs/report-build-and-test.yml
+19
View File
@@ -0,0 +1,19 @@
# 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.
# Jobs report
report_job_success:
extends: [.on_dane, .report_job_success]
stage: jobs-stage-3
report_job_failure:
extends: [.on_dane, .report_job_failure]
stage: jobs-stage-3
+87
View File
@@ -0,0 +1,87 @@
# 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.
# Override reproducer section to define MFEM specific variables.
.dane_reproducer_vars:
script:
- !reference [.reproducer_vars, script]
# TODO: Setup script should be defined as a bash script (but then GIT_STRATEGY
# cannot be "none" anymore).
# Setup clones the mfem/data repo in ${SHARED_REPOS_DIR}. The build_and_test
# script then symlinks the repo to the parent directory of the MFEM source
# directory. Unit tests that depend on the mfem/data repo will then detect that
# this directory is present and be enabled.
setup:
extends: .on_dane
stage: jobs-stage-1
script:
- ./tests/gitlab/build_and_test_setup
########################
# Overridden shared jobs
########################
# When using shared jobs, we can duplicate them here to override description and
# add necessary changes.
# We keep ${PROJECT_<MACHINE>_VARIANTS} and ${PROJECT_<MACHINE>_DEPS} So that
# the comparison with the original job is easier.
############
# Extra jobs
############
# We do not recommend using ${PROJECT_<MACHINE>_VARIANTS} and
# ${PROJECT_<MACHINE>_DEPS} in the extra jobs. There is not reason not to fully
# describe the spec here.
.mfem_job_on_dane:
extends: .job_on_dane
stage: jobs-stage-2
variables:
# Dane has 224 threads/node and we run 7 separate jobs: 224=7*32
THREADS: 28
debug_ser_gcc_10:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 +debug~mpi"
debug_par_gcc_10:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 +debug+mpi"
opt_ser_gcc_10:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 ~mpi"
opt_par_gcc_10:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1"
opt_par_gcc_10_sundials:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 +sundials"
opt_par_gcc_10_petsc:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 +petsc ^petsc+mumps~superlu-dist"
opt_par_gcc_10_pumi:
extends: .mfem_job_on_dane
variables:
SPEC: "%gcc@10.3.1 +pumi"
+19
View File
@@ -0,0 +1,19 @@
# 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.
# Jobs report
report_job_success:
extends: [.on_matrix, .report_job_success]
stage: jobs-stage-3
report_job_failure:
extends: [.on_matrix, .report_job_failure]
stage: jobs-stage-3
+65
View File
@@ -0,0 +1,65 @@
# 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.
# Override reproducer section to define UMPIRE specific variables.
.matrix_reproducer_vars:
script:
- !reference [.reproducer_vars, script]
#TODO: Setup script should be defined as a bash script (but then GIT_STRATEGY cannot be "none" anymore).
# Setup clones the mfem/data repo in ${SHARED_REPOS_DIR}. The build_and_test
# script then symlinks the repo to the parent directory of the MFEM source
# directory. Unit tests that depend on the mfem/data repo will then detect that
# this directory is present and be enabled.
setup:
extends: .on_matrix
stage: jobs-stage-1
script:
- ./tests/gitlab/build_and_test_setup
########################
# Overridden shared jobs
########################
# When using shared jobs , we can duplicate them here to override description and add necessary changes.
# We keep ${PROJECT_<MACHINE>_VARIANTS} and ${PROJECT_<MACHINE>_DEPS} So that
# the comparison with the original job is easier.
############
# Extra jobs
############
# We do not recommend using ${PROJECT_<MACHINE>_VARIANTS} and
# ${PROJECT_<MACHINE>_DEPS} in the extra jobs. There is not reason not to fully
# describe the spec here.
.mfem_job_on_matrix:
extends: .job_on_matrix
stage: jobs-stage-2
variables:
# We run 2 jobs on 1 node that has 112 threads
THREADS: 48
# These modules need to be consistent with the uberenv configurations:
MODULE_LIST: "gcc/10.3.1-magic cuda/12.9.1"
allocate_resources:
timeout: 4h
opt_mpi_cuda_gcc:
extends: .mfem_job_on_matrix
variables:
SPEC: "%gcc@10.3.1 +mpi +cuda cuda_arch=90"
opt_mpi_cuda_hypre_cuda_gcc:
extends: .mfem_job_on_matrix
variables:
SPEC: "%gcc@10.3.1 +mpi +cuda cuda_arch=90 ^hypre+cuda"
+20
View File
@@ -0,0 +1,20 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Jobs report
report_job_success:
extends: [.on_tioga, .report_job_success]
stage: jobs-stage-3
report_job_failure:
extends: [.on_tioga, .report_job_failure]
stage: jobs-stage-3
+70
View File
@@ -0,0 +1,70 @@
# 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.
# Override reproducer section to define UMPIRE specific variables.
.tioga_reproducer_vars:
script:
- !reference [.reproducer_vars, script]
#TODO: Setup script should be defined as a bash script (but then GIT_STRATEGY cannot be "none" anymore).
# Setup clones the mfem/data repo in ${SHARED_REPOS_DIR}. The build_and_test
# script then symlinks the repo to the parent directory of the MFEM source
# directory. Unit tests that depend on the mfem/data repo will then detect that
# this directory is present and be enabled.
setup:
extends: .on_tioga
stage: jobs-stage-1
script:
- ./tests/gitlab/build_and_test_setup
########################
# Overridden shared jobs
########################
# When using shared jobs , we can duplicate them here to override description and add necessary changes.
# We keep ${PROJECT_<MACHINE>_VARIANTS} and ${PROJECT_<MACHINE>_DEPS} So that
# the comparison with the original job is easier.
############
# Extra jobs
############
# We do not recommend using ${PROJECT_<MACHINE>_VARIANTS} and
# ${PROJECT_<MACHINE>_DEPS} in the extra jobs. There is not reason not to fully
# describe the spec here.
# Build and test jobs, simply provide a spec
#.tioga_job_command:
# script:
# - echo PROXY="${PROXY}"
# - echo TIOGA_JOB_ALLOC="${TIOGA_JOB_ALLOC}"
# - "printf '#!/bin/bash\n%s\n' \"${JOB_CMD}\" > flux_script.sh"
# - cat flux_script.sh
# - ${PROXY} flux watch $( ${PROXY} flux batch -o output.stdout.type=kvs ${TIOGA_JOB_ALLOC} flux_script.sh )
# - rm -f flux_script.sh
.mfem_job_on_tioga:
extends: .job_on_tioga
stage: jobs-stage-2
variables:
# We run 1 job on 1 node that has 64 threads
THREADS: 64
opt_mpi_rocm_hypre_rocm:
extends: .mfem_job_on_tioga
variables:
SPEC: "%rocmcc@=6.3.1 +rocm amdgpu_target=gfx90a ^hypre+rocm"
# cce_16_0_1:
# extends: .mfem_job_on_tioga
# variables:
# SPEC: "%cce@=16.0.1"
-44
View File
@@ -1,44 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
stages:
- setup
- build_and_test
- report
opt_mpi_cuda_gcc:
variables:
SPEC: "%gcc@8.3.1 +mpi +cuda cuda_arch=70"
extends: .build_and_test_on_lassen
opt_mpi_cuda_hypre_cuda_gcc:
variables:
SPEC: "%gcc@8.3.1 +mpi +cuda cuda_arch=70 ^hypre+cuda~shared cuda_arch=70"
extends: .build_and_test_on_lassen
# Jobs report
report_job_success:
stage: report
extends:
- .on_lassen
- .report_job_success
report_job_failure:
stage: report
extends:
- .on_lassen
- .report_job_failure
include:
- local: .gitlab/configs/common.yml
- local: .gitlab/configs/lassen-config.yml
- local: .gitlab/configs/setup-build-and-test.yml
- local: .gitlab/configs/report-build-and-test.yml
+1 -3
View File
@@ -32,11 +32,9 @@ mkdir _${BASELINE_TEST} && cd _${BASELINE_TEST}
# run
if [[ "${MACHINE_NAME}" == "dane" ]]; then
salloc --nodes=1 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
salloc --nodes=1 -t 60 --exclusive --reservation=ci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
elif [[ ${MACHINE_NAME} == "corona" ]]; then
salloc --nodes=1 -t 60 -p pbatch ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
elif [[ ${MACHINE_NAME} == "lassen" ]]; then
lalloc 1 -q pci ../runtest ../../mfem "${BASELINE_TEST} ${TPLS_DIR}"
else
echo "Unknown machine: MACHINE_NAME=$MACHINE_NAME"
exit 1
+118
View File
@@ -0,0 +1,118 @@
#!/bin/bash
# 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.
function info_msg ()
{
echo "[Information:] ${1}"
}
function error_msg ()
{
echo "[Error:] ${1}"
}
# Perform a report while holding a lock file to prevent concurrency on
# the destination.
# Usage:
# locked_clone <report_function> <lock_name>
function locked_report ()
{
if ! command -v flock
then
error_msg "Required command 'flock' not found"
exit 1
fi
info_msg "Will report ${1} while holding a lock in ${2}"
( date; info_msg "Waiting to acquire lock on '${PWD}/${2}.lock' ..."
# try to get an exclusive lock on fd 9 (mfem-data.lock) repeating the
# try every 5 seconds; we may want to add a counter for the number of
# retries to interrupt a potential infinite loop
while ! flock -n 9; do sleep 5; done
date; info_msg "Acquired lock on '${PWD}/${2}.lock'"
report ${1}
err=$?
# sleep for a period to allow NFS to propagate the above changes;
# clearly, there is no guarantee that other NFS clients will see the
# changes even after the timeout
sleep 10
exit $err
) 9> ${2}.lock
}
function report ()
{
if [[ "${1}" == "SUCCESS" ]]
then
info_msg "All the ${MACHINE_NAME} jobs passed"
status_msg="The 'build-and-test' jobs on ${MACHINE_NAME} were SUCCESSFUL."
elif [[ "${1}" == "FAILURE" ]]
then
info_msg "At least one failure on ${MACHINE_NAME}"
status_msg="Some 'build-and-test' jobs on ${MACHINE_NAME} FAILED."
else
error_msg "Unknown status: ${1} ... aborting"
exit 1
fi
cd ${AUTOTEST_ROOT}/autotest || \
{ error_msg "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
mkdir -p ${MACHINE_NAME}
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}"
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir)
printf "%s\n" "${status_msg}" \
"Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.err
msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
if [[ "${1}" == "FAILURE" ]]
then
# Create 'autotest-email.html' to indicate failure:
cp ${rundir}/gitlab.err ${rundir}/autotest-email.html
fi
# Note: in some cases, the content of AUTOTEST_COMMIT can be
# '${AUTOTEST_COMMIT}', so we need to treat that value as the default
# value of 'ON'.
if [[ "$AUTOTEST_COMMIT" == '${AUTOTEST_COMMIT}' ]]; then
AUTOTEST_COMMIT="ON"
fi
if [[ "$AUTOTEST_COMMIT" == "ON" || "$AUTOTEST_COMMIT" == "YES" ]]; then
git pull && \
git add ${rundir} && \
git commit -m "${msg}" && \
${CI_PROJECT_DIR}/.gitlab/scripts/git_try_to_push
else
for file in ${rundir}/*; do
echo "------------------------------"
echo "Content of '$file'"
echo "******************************"
cat $file
echo "******************************"
done
rm -rf ${rundir} || true
fi
}
export MACHINE_NAME=${CI_MACHINE}
info_msg "MACHINE_NAME is ${MACHINE_NAME}"
info_msg "AUTOTEST_ROOT is ${AUTOTEST_ROOT}"
info_msg "AUTOTEST=$AUTOTEST"
info_msg "AUTOTEST_COMMIT=$AUTOTEST_COMMIT"
cd ${AUTOTEST_ROOT} && locked_report ${1} autotest
@@ -1,45 +0,0 @@
#!/bin/bash
# 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.
echo "Runs if there was at least one failure on ${MACHINE_NAME}"
cd ${AUTOTEST_ROOT}/autotest || \
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
mkdir -p ${MACHINE_NAME}
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}"
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir)
printf "%s\n" "Some 'build-and-test' jobs on ${MACHINE_NAME} FAILED." \
"Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.err
msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
# Create 'autotest-email.html' to indicate failure:
cp ${rundir}/gitlab.err ${rundir}/autotest-email.html
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
git pull && \
git add ${rundir} && \
git commit -m "${msg}" && \
${CI_PROJECT_DIR}/.gitlab/scripts/git_try_to_push
else
for file in ${rundir}/*; do
echo "------------------------------"
echo "Content of '$file'"
echo "******************************"
cat $file
echo "******************************"
done
rm -rf ${rundir} || true
fi
@@ -1,42 +0,0 @@
#!/bin/bash
# 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.
echo "Can only run if all the ${MACHINE_NAME} jobs passed"
cd ${AUTOTEST_ROOT}/autotest || \
{ echo "Invalid 'autotest' dir: ${AUTOTEST_ROOT}/autotest"; exit 1; }
mkdir -p ${MACHINE_NAME}
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-ci-${CI_COMMIT_REF_SLUG}"
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir $rundir)
printf "%s\n" "The 'build-and-test' jobs on ${MACHINE_NAME} were SUCCESSFUL." \
"Pipeline URL:" "$CI_PIPELINE_URL" > ${rundir}/gitlab.out
msg="GitLab CI log for build-and-test on ${MACHINE_NAME} ($(date +%Y-%m-%d))"
if [[ "$AUTOTEST_COMMIT" != "NO" ]]; then
git pull && \
git add ${rundir} && \
git commit -m "${msg}" && \
${CI_PROJECT_DIR}/.gitlab/scripts/git_try_to_push
else
for file in ${rundir}/*; do
echo "------------------------------"
echo "Content of '$file'"
echo "******************************"
cat $file
echo "******************************"
done
rm -rf ${rundir} || true
fi
+130
View File
@@ -0,0 +1,130 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# The template job to test whether a machine is up.
# Expects CI_MACHINE defined to machine name.
.machine-check:
stage: prerequisites
tags: [shell, oslic]
variables:
GIT_STRATEGY: none
script:
- |
if [[ $(jq '.[env.CI_MACHINE].total_nodes_up' /usr/global/tools/lorenz/data/loginnodeStatus) == 0 ]]
then
echo -e "\e[31mNo node available on ${CI_MACHINE}\e[0m"
false && \
curl --url "https://api.github.com/repos/${GITHUB_PROJECT_ORG}/${GITHUB_PROJECT_NAME}/statuses/${CI_COMMIT_SHA}" \
--header 'Content-Type: application/json' \
--header "authorization: Bearer ${GITHUB_TOKEN}" \
--data "{ \"state\": \"failure\", \"target_url\": \"${CI_PIPELINE_URL}\", \"description\": \"GitLab ${CI_MACHINE} down\", \"context\": \"ci/gitlab/${CI_MACHINE}\" }"
exit 1
fi
###
# Trigger a build-and-test pipeline for a machine.
# Comment the jobs for machines you dont need.
###
# One job to generate the job list for all the subpipelines
generate-job-lists:
stage: prerequisites
tags: [shell, oslic]
variables:
LOCAL_JOBS_PATH: ".gitlab/jobs"
script:
- |
echo "AUTOTEST=$AUTOTEST"
echo "AUTOTEST_COMMIT=$AUTOTEST_COMMIT"
echo "AUTOTEST_ROOT=$AUTOTEST_ROOT"
- |
cat ${LOCAL_JOBS_PATH}/dane.yml > dane-jobs.yml
if [[ ${AUTOTEST} == "ON" || ${AUTOTEST} == "YES" ]]
then
cat ${LOCAL_JOBS_PATH}/dane-reports.yml >> dane-jobs.yml
fi
- |
cat ${LOCAL_JOBS_PATH}/matrix.yml > matrix-jobs.yml
if [[ ${AUTOTEST} == "ON" || ${AUTOTEST} == "YES" ]]
then
cat ${LOCAL_JOBS_PATH}/matrix-reports.yml >> matrix-jobs.yml
fi
- |
cat ${LOCAL_JOBS_PATH}/tioga.yml > tioga-jobs.yml
if [[ ${AUTOTEST} == "ON" || ${AUTOTEST} == "YES" ]]
then
cat ${LOCAL_JOBS_PATH}/tioga-reports.yml >> tioga-jobs.yml
fi
artifacts:
paths:
- dane-jobs.yml
- matrix-jobs.yml
- tioga-jobs.yml
# DANE
dane-up-check:
variables:
CI_MACHINE: "dane"
extends: [.machine-check]
dane-build-and-test:
variables:
CI_MACHINE: "dane"
needs: [dane-up-check, generate-job-lists]
extends: [.build-and-test]
# DANE, MFEM Specific
dane-baseline:
stage: test-pipelines
variables:
# Explicitly pass down values that are not always propagated to child
# pipelines, e.g. when a variable is set in the "Settings -> CI" web
# interface (project variables).
# Note: in some cases, this does not work as expected, e.g. when the
# variable is not re-defined in the web interface; in such cases, the child
# pipeline gets a definition like '${AUTOTEST}', i.e. it behaves as if
# AUTOTEST is undefined, even though there is a default value in
# .gitlab-ci.yml.
AUTOTEST: "${AUTOTEST}"
AUTOTEST_COMMIT: "${AUTOTEST_COMMIT}"
trigger:
include: .gitlab/dane-baseline.yml
strategy: depend
forward:
pipeline_variables: true
needs: [dane-up-check]
# TIOGA
tioga-up-check:
variables:
CI_MACHINE: "tioga"
extends: [.machine-check]
tioga-build-and-test:
variables:
CI_MACHINE: "tioga"
needs: [tioga-up-check, generate-job-lists]
extends: [.build-and-test]
# Matrix
matrix-up-check:
variables:
CI_MACHINE: "matrix"
extends: [.machine-check]
matrix-build-and-test:
variables:
CI_MACHINE: "matrix"
needs: [matrix-up-check, generate-job-lists]
extends: [.build-and-test]
+130 -56
View File
@@ -8,9 +8,13 @@
https://mfem.org
Version 4.8.1 (development)
Version 4.9.1 (development)
===========================
Version 4.9, released on Dec 11, 2025
=====================================
Starting with this version, MFEM requires a C++17 compiler.
Discretization improvements
@@ -19,70 +23,132 @@ Discretization improvements
nonlinear finite element operators, based on Enzyme or dual numbers AD at
quadrature points. These features are part of the new mfem::future namespace
and some of the API can change in the future. See the new dFEM minimal surface
miniapp in the miniapps/dfem/ directory for illustration of dFEM's use.
miniapp in the miniapps/dfem/ directory for illustration of dFEM's use. Using
Enzyme for AD in MFEM is tested with clang v19 and requires clang/LLVM built
with plugin support. See INSTALL for more details.
- Using Enzyme for AD in MFEM is tested with clang v19 and requires clang/LLVM
built with plugin support. See INSTALL for more details.
- Introduced initial support for particle methods in MFEM with new classes
Particle, ParticleSet and ParticleVector.
* Particle is a convenient interface for individual particle data.
* ParticleSet manages and stores particle data in a struct-of-arrays form,
carrying particle coordinates and IDs along with an arbitrary number of
Vector and integer data for each particle.
* ParticleVector is a Vector-derived container that stores vector data for an
arbitrary number of particles contiguously based on specified vdim/ordering.
See the new particle miniapps in miniapps/gslib/ and miniapps/fluids/navier/.
- Added a new miniapp and specialized AMG solver (AMGF) for optimization-based
contact mechanics. The miniapp solves large-scale frictionless contact using a
self-contained Interior Point (IP) solver, mortar-based contact constraints
provided by Tribol. The resulting linear systems are solved with the new AMGF
solver (see below). Benchmark examples include the two-block, ironing, and
beam-sphere problems. See the miniapps/contact/ directory.
- Added support for boundary integration to the hyperbolic framework. Two new
classes BdrHyperbolicDirichletIntegrator and BoundaryHyperbolicFlowIntegrator
have been introduced for implementation of weak Dirichlet boundary conditions
with a general flux or for the linear case respectively.
- Added a method to compute piecewise linear bounds on high-order functions on
tensor-product elements.
- Added support for interior face integration enabling DG methods in
ParMixedBilinearForm, ParNonlinearForm and ParBlockNonlinearForm.
- In the ParMoonolith integration, added support for variational resampling of
H1 vector fields.
- Added support for boundary integration to the hyperbolic framework. In this
regard, new classes `BdrHyperbolicDirichletIntegrator` and
`BoundaryHyperbolicFlowIntegrator` have been introduced for implementation
of weak Dirichlet boundary conditions with a general flux or for the linear
case respectively.
- Added method to compute piecewise linear bounds on high-order functions on
tensor-product elements.
- Parallel anisotropic refinement of hexahedral meshes is now supported,
provided that neighboring hexahedra are not refined in conflicting directions.
A new ParMesh method is added to check for such conflicts, before refinement.
- Introduced IMEX ODE solvers based on a split-operator framework. Added
examples ex41 and ex41p demonstrating IMEX DG/CG discretizations of the
convectiondiffusion equation, with ex41p using DG LOR preconditioning.
Meshing improvements
--------------------
- The TMOP kernel hierarchy has been restructured to reduce compilation time.
Most large kernels have been split into smaller specific kernels for each
metric. The directory structure has been updated with assemble, metrics, mult
and tools subdirectories. New kernel dispatch and specialization system has
also been integrated. Unit tests have been revised to ensure --all tests pass.
- Introduced NC-patch NURBS meshes, which are conforming element-wise but allow
for nonconforming patch topology. This new mesh format supports element
spacing formulas for refinement, as well as local refinement factors for a
subset of knot vectors.
- Added support for higher order meshes in Mesh::MakeSimplicial and
ParMesh::MakeSimplicial.
- Added a new miniapp for interpolating a surface grid of points in 3D using a
smooth NURBS surface, that can then be sampled at arbitrary resolution while
staying close to the original geometry. See miniapps/nurbs/nurbs_surface.
- Parallel anisotropic refinement of hexahedral meshes is now supported,
provided that neighboring hexahedra are not refined in conflicting directions.
A new ParMesh method is added to check for such conflicts, before refinement.
- Added support for higher order meshes in (Par)Mesh::MakeSimplicial.
Linear and nonlinear solvers
----------------------------
- Added FilteredSolver: a base class for solvers with filtering. It handles
cases where a solver performs well except in small subspaces, by adding a
filtering step formulated as a subspace correction.
- Added AMGFSolver: a derived class of FilteredSolver, specialized for AMG with
Filtering (AMGF), providing robust preconditioning for linear systems arising
in constrained optimization problems such as frictionless contact.
GPU computing
-------------
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
executes on device if either the vector or the array have the device flag
set. This is most often used for setting constant essential boundary
conditions. A new function Vector::SetSubVectorHost has been added in cases
where host execution is always needed (e.g. when the DOFs array is small).
- Added the 'gpu', 'raja-gpu', and 'ceed-gpu' backend aliases/shortcuts which
automatically select between CUDA or HIP.
- Added the option to enable GPU-aware MPI in MFEM using the environment
variable 'MFEM_GPU_AWARE_MPI' set to any value. Setting this environment
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.
- Implemented a GPU-accelerated matrix-free AMR derefinement GridFunction update
operator. This supports mixed geometry meshes and variable order spaces, and
is the default derefinement operator constructed by FiniteElementSpace::Update
and ParFiniteElementSpace::Update. The operator requires the finite element
space to be nonconforming.
- Introduced MFEM_FOREACH_THREAD_DIRECT, which directly maps loop tasks to GPU
threads, assigning one task per thread.
- Implemented a GPU-accelerated matrix-free AMR derefinement `GridFunction`
update operator. This supports mixed geometry meshes and variable order
spaces, and is the default derefinement operator constructed by
`FiniteElementSpace::Update` and `ParFiniteElementSpace::Update`.
The operator requires `FiniteElementSpace::Nonconforming() == true`.
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
executes on device if either the vector or the array have the device flag
set. This is most often used for setting constant essential BCs. A new method,
SetSubVectorHost, has been added for cases where host execution is always
needed (e.g. when the DOFs array is small).
- Added GPU support in GradientGridFunction and InnerProduct Coefficient classes
by implementing their Project methods.
- Added new method: GridFunction::GetGradients, with GPU support, for computing
the gradients of a GridFunction on all elements.
- Added GPU support in GradientGridFunctionCoefficient and
InnerProductCoefficient by implementing their Project methods.
- The CUDA-specific names used by some of the unit tests like 'cunit_tests' and
'pcunit_tests' were replaced by names using 'gpu' instead of 'c' (short for
CUDA) or 'cuda'. These tests automatically run the CUDA/HIP tests based on the
MFEM build configuration.
New and updated examples and miniapps
-------------------------------------
- Added miniapps to demonstrate an implementation of the absolute-value
L(1)-Jacobi preconditioners in partially assembled operators. This includes
Multigrid wrapper to demonstrate the effectiveness of these Jacobi-type
operators as smoothers.
These miniapps can be found in `miniapps/diag-smoothers`.
- Added the miniapps/fluids directory and moved the previous Navier and the new
incompressible Schrödinger flow miniapps into it.
- Introduced the new Incompressible Schrödinger Flow (ISF) miniapp, which models
inviscid fluid dynamics by solving the linear Schrödinger equation, leveraging
the hydrodynamical analogy to quantum mechanics.
- New particle-related miniapps:
* New transient Navier-Stokes fluid-particles solver NavierParticles in
miniapps/fluids/navier/navier_particles, for modeling tracer particles in
fluid flow, demonstrating use of the new ParticleSet class.
* New Navier miniapp, miniapps/fluids/navier/navier_bifurcation, showing the
use of NavierParticles in a 2D bifurcating channel flow.
* New FindPointsGSLIB miniapp, miniapps/gslib/particles_redist, showing
parallel-redistribution of particle data between MPI ranks.
* Particle visualization features in common/particles_extras for viewing
particle locations and trajectories (ParticleTrajectories) using GLVis.
- Added a new miniapp (meshing/mesh-bounding-boxes) that computes the bounding
boxes for each element of a given mesh, and the bounds on the determinant of
@@ -95,36 +161,44 @@ New and updated examples and miniapps
of a charged particle, subject to Lorentz forces, in electrostatic and/or
magnetostatic fields as computed by the volta or tesla miniapps.
API changes:
-----------
- mfem::internal::tensor and mfem::internal::dual have been moved to
mfem::future::tensor and mfem::future::dual.
- Added miniapps to demonstrate an implementation of the absolute-value
l1-Jacobi preconditioners in partially assembled operators. This includes
Multigrid wrapper to demonstrate the effectiveness of these Jacobi-type
operators as smoothers. See the miniapps/diag-smoothers/ directory.
- API addition: in class `Operator`, added virtual functions: `AbsMult`, and
`AbsMultTranspose`; in class `Vector`, added `Abs` and `Pow`.
- Updated the mtop miniapp with a GPU enabled forward and adjoint solver for
isotropic linear elasticity.
Miscellaneous
-------------
- Added the "gpu", "raja-gpu", and "ceed-gpu" backend aliases/shortcuts which
automatically select between CUDA or HIP.
- Introduced MFEM_FETCH_TPLS CMake option to enable downloading, configuring,
and building of TPLs alongside MFEM (currently supported TPLs are hypre,
METIS, and GSLIB).
- The CUDA-specific names used by some of the unit tests like 'cunit_tests' and
'pcunit_tests' were replaced by names using 'gpu' instead of 'c' (short for
CUDA) or 'cuda'. These tests automatically run the CUDA/HIP tests based on the
MFEM build configuration.
- Added quadrature function support to the VisIt and Conduit data collections.
- Added the option to enable GPU-aware MPI in MFEM using the environment
variable 'MFEM_GPU_AWARE_MPI' set to any value. Setting this environment
variable is an alternative to calling 'Device::SetGPUAwareMPI(true)'.
- Added access to the internal parallel matrix in Par(Mixed)BilinearForm and
related utility methods for elimination of BCs.
- FindPointsGSLIB has a new constructor that accepts the mesh object and
internally calls the Setup() method so users do not have to. The FreeData()
method has also been moved to the destructor so users do not need to manually
free-up the memory if the destructor is called before MPI_Finalize().
- Added parallel Address Sanitizer, serial and parallel Undefined Behavior
Sanitizer and serial Memory Sanitizer GitHub actions tests on Ubuntu.
- FindPointsGSLIB has a new constructor that accepts the mesh object and
internally calls the Setup() method so that the user does not have to.
The FreeData() method has also been moved to the destructor so the user does
not need to manually free-up the memory if the destructor is called before
MPI_Finalize().
API changes
-----------
- mfem::internal::tensor and mfem::internal::dual have been moved to
mfem::future::tensor and mfem::future::dual.
- API addition: in class Operator, added virtual functions: AbsMult, and
AbsMultTranspose; in class Vector, added Abs and Pow.
- ParBilinearForm::EliminateEssentialVDofsInRhs() has been deprecated in favor
of ParallelEliminateEssentialTDofsInRhs().
Version 4.8, released on Apr 9, 2025
====================================
+58 -25
View File
@@ -59,7 +59,7 @@ project(mfem NONE)
# Current version of MFEM, see also `makefile`.
# mfem_VERSION = (string)
# MFEM_VERSION = (int) [automatically derived from mfem_VERSION]
set(${PROJECT_NAME}_VERSION 4.8.1)
set(${PROJECT_NAME}_VERSION 4.9.1)
# Prohibit in-source build
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
@@ -133,33 +133,49 @@ if (MFEM_USE_CUDA)
if (NOT CMAKE_CUDA_HOST_COMPILER)
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
endif()
if (CMAKE_VERSION VERSION_LESS 3.18.0)
set(CUDA_FLAGS "-arch=${CUDA_ARCH} ${CUDA_FLAGS}")
elseif (NOT CMAKE_CUDA_ARCHITECTURES)
string(REGEX REPLACE "^sm_" "" ARCH_NUMBER "${CUDA_ARCH}")
if ("${CUDA_ARCH}" STREQUAL "sm_${ARCH_NUMBER}")
set(CMAKE_CUDA_ARCHITECTURES "${ARCH_NUMBER}")
else()
message(FATAL_ERROR "Unknown CUDA_ARCH: ${CUDA_ARCH}")
endif()
if (NOT CMAKE_CUDA_ARCHITECTURES)
# make CUDA_ARCH resemble the same form as CMAKE_CUDA_ARCHITECTURES
string(REPLACE "sm_" "" CUDA_ARCH_TMP "${CUDA_ARCH}")
string(REPLACE "," ";" CUDA_ARCH "${CUDA_ARCH_TMP}")
set(CMAKE_CUDA_ARCHITECTURES "${CUDA_ARCH}")
else()
set(CUDA_ARCH "CMAKE_CUDA_ARCHITECTURES: ${CMAKE_CUDA_ARCHITECTURES}")
endif()
message(STATUS "Using CUDA architecture: ${CUDA_ARCH}")
enable_language(CUDA)
if (CMAKE_VERSION VERSION_LESS 3.18.0)
# backup try to detect if this is clang or nvcc
if(CMAKE_CUDA_COMPILER MATCHES "nvcc$")
# nvcc
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
endif()
# backup try to detect if this is clang or nvcc
if(CMAKE_CUDA_COMPILER MATCHES "nvcc$")
# nvcc
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
if ("all" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "native" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}"
OR "all-major" STREQUAL "${CMAKE_CUDA_ARCHITECTURES}")
set(CUDA_FLAGS "-arch=${CMAKE_CUDA_ARCHITECTURES} ${CUDA_FLAGS}")
else()
# build -gencode sequence for multiple architectures
foreach(ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
set(CUDA_FLAGS
"-gencode arch=compute_${ENTRY},code=sm_${ENTRY} ${CUDA_FLAGS}")
endforeach()
endif()
else()
# build cuda-gpu-arch sequence for multiple architectures
# does not support all/all-major/native
foreach(ENTRY IN LISTS CMAKE_CUDA_ARCHITECTURES)
set(CUDA_FLAGS "-cuda-gpu-arch=sm_${ENTRY} ${CUDA_FLAGS}")
endforeach()
endif()
else()
if (CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
# nvcc
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
set(CUDA_FLAGS "${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
endif()
# TODO: all, native, all-major require CMake 3.24+
# backport support for CMake 3.18 to 3.24
if (CMAKE_CUDA_COMPILER_ID STREQUAL "NVIDIA")
# nvcc
set(MFEM_CUDA_COMPILER_IS_NVCC ON)
set(CUDA_FLAGS
"${CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr")
endif()
endif()
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
"CUDA standard to use.")
@@ -242,10 +258,16 @@ endif()
# AMD HIP
if (MFEM_USE_HIP)
if (HIP_ARCH)
message(STATUS "Using HIP architecture: ${HIP_ARCH}")
set(GPU_TARGETS "${HIP_ARCH}" CACHE STRING "HIP targets to compile for")
if (NOT CMAKE_HIP_ARCHITECTURES)
if (HIP_ARCH)
set(CMAKE_HIP_ARCHITECTURES CACHE STRING "HIP targets to compile for" "${HIP_ARCH}")
set(GPU_TARGETS "${HIP_ARCH}" CACHE STRING "HIP targets to compile for" FORCE)
endif()
else()
set(HIP_ARCH CACHE STRING "HIP targets to compile for" "${CMAKE_HIP_ARCHITECTURES}")
set(GPU_TARGETS "${CMAKE_HIP_ARCHITECTURES}" CACHE STRING "HIP targets to compile for" FORCE)
endif()
message(STATUS "Using HIP architecture: ${CMAKE_HIP_ARCHITECTURES}")
if (ROCM_PATH)
list(INSERT CMAKE_PREFIX_PATH 0 ${ROCM_PATH})
endif()
@@ -278,8 +300,19 @@ if (MFEM_USE_OPENMP OR MFEM_USE_LEGACY_OPENMP)
endif()
endif()
# Umpire (must be included before hypre, so hypre can use it if needed)
# Warn user if deprecated FETCH_TPLS is provided
if (DEFINED FETCH_TPLS)
message(STATUS "Setting MFEM_FETCH_TPLS to user-provided value of FETCH_TPLS (i.e., MFEM_FETCH_TPLS=${FETCH_TPLS})")
set (MFEM_FETCH_TPLS FETCH_TPLS)
message(DEPRECATION "The use of FETCH_TPLS is deprecated and will be removed in future verison. Please use MFEM_FETCH_TPLS instead.")
endif()
# Umpire (must be included before hypre, so hypre can use it if needed)
if (MFEM_USE_UMPIRE)
# umpire uses FindCUDA, which needs CMP0146=OLD in CMake >= 3.27
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.27.0)
cmake_policy(SET CMP0146 OLD)
endif()
find_package(UMPIRE REQUIRED)
endif()
+8 -1
View File
@@ -129,6 +129,10 @@ The MFEM source code has the following structure:
│ ├── moonolith
│ ├── qinterp
│ └── tmop
│ | ├── assemble
│ | ├── metrics
│ | ├── mult
│ | └── tools
├── general
├── linalg
│ ├── batched
@@ -139,16 +143,19 @@ The MFEM source code has the following structure:
│ ├── adjoint
│ ├── autodiff
│ ├── common
│ ├── contact
│ ├── dfem
│ ├── dpg
│ ├── electromagnetics
│ ├── fluids
│ │ ├── navier
│ │ └── schrodinger-flow
│ ├── gslib
│ ├── hdiv-linear-solver
│ ├── hooke
│ ├── meshing
│ ├── mtop
│ ├── multidomain
│ ├── navier
│ ├── nurbs
│ ├── parelag
│ ├── performance
+6 -4
View File
@@ -123,7 +123,7 @@ Parallel build:
Parallel build with fetching of hypre and METIS:
mkdir <mfem-buil-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DFETCH_TPLS=YES
cmake <mfem-source-dir> -DMFEM_USE_MPI=YES -DMFEM_FETCH_TPLS=YES
make -j 4
CUDA build:
@@ -1081,9 +1081,10 @@ The following options are CMake specific:
MFEM_ENABLE_TESTING - Enable the ctest framework for testing.
MFEM_ENABLE_EXAMPLES - Build all of the examples by default.
MFEM_ENABLE_MINIAPPS - Build all of the miniapps by default.
FETCH_TPLS - Enable fetching of all supported third-party libraries.
HYPRE_FETCH - Enable fetching of hypre.
METIS_FETCH - Enable fetching of metis.
MFEM_FETCH_TPLS - Enable fetching of all supported third-party libraries.
MFEM_FETCH_GSLIB - Enable fetching of gslib.
MFEM_FETCH_HYPRE - Enable fetching of hypre.
MFEM_FETCH_METIS - Enable fetching of metis.
External libraries (CMake):
---------------------------
@@ -1149,6 +1150,7 @@ The MFEM CMake build system also provides fetching (automated building) for the
packages/libraries listed below. Note that when fetching is enabled, any related
auto-detection functionality is disabled.
- GSLIB
- HYPRE
- METIS
+38 -1
View File
@@ -9,10 +9,47 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Defines the following variables:
# Defines the following variables if fetching of TPLs is disabled (default):
# - GSLIB_FOUND
# - GSLIB_LIBRARIES
# - GSLIB_INCLUDE_DIRS
# otherwise, the following are defined:
# - GSLIB (imported library target)
if (MFEM_FETCH_GSLIB OR MFEM_FETCH_TPLS)
enable_language(C)
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
set(GSLIB_FETCH_VERSION 1.0.9)
set(GSLIB_C_FLAGS ${CMAKE_C_FLAGS_${BUILD_TYPE}})
if (CMAKE_C_FLAGS)
set(GSLIB_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
endif()
if (BUILD_SHARED_LIBS)
set(GSLIB_C_FLAGS "${GSLIB_C_FLAGS} -fPIC")
endif()
add_library(GSLIB STATIC IMPORTED)
# define external project and create future include directory so it is present
# to pass CMake checks at end of MFEM configuration step
message(STATUS "Will fetch GSLIB ${GSLIB_FETCH_VERSION} to be built with ${GSLIB_C_FLAGS}")
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/gslib)
include(ExternalProject)
ExternalProject_Add(gslib
GIT_REPOSITORY https://github.com/Nek5000/gslib
GIT_TAG v${GSLIB_FETCH_VERSION}
GIT_SHALLOW TRUE
UPDATE_DISCONNECTED TRUE
PREFIX ${PREFIX}
CONFIGURE_COMMAND ""
BUILD_COMMAND cd ${PREFIX}/src/gslib && $(MAKE) clean && $(MAKE) DESTDIR=${PREFIX} MPI=$<BOOL:${MFEM_USE_MPI}> "CFLAGS= ${GSLIB_C_FLAGS}"
INSTALL_COMMAND "")
file(MAKE_DIRECTORY ${PREFIX}/include)
# set imported library target properties
add_dependencies(GSLIB gslib)
set_target_properties(GSLIB PROPERTIES
IMPORTED_LOCATION ${PREFIX}/lib/libgs.a
INTERFACE_INCLUDE_DIRECTORIES ${PREFIX}/include)
return()
endif()
include(MfemCmakeUtilities)
mfem_find_package(GSLIB GSLIB GSLIB_DIR "include" gslib.h "lib" gs
+8 -8
View File
@@ -37,21 +37,21 @@ if (HYPRE_FOUND OR TARGET HYPRE)
endif()
endif()
if (HYPRE_FETCH OR FETCH_TPLS)
# Collect all HYPRE_ENABLE variables and pass them to hypre, assuming they are BOOL.
if (MFEM_FETCH_HYPRE OR MFEM_FETCH_TPLS)
set(HYPRE_FETCH_VERSION 2.33.0)
set(HYPRE_FETCH_TAG "v${HYPRE_FETCH_VERSION}" CACHE STRING "Tag, branch, or commit for HYPRE")
add_library(HYPRE STATIC IMPORTED)
# set options and associated dependencies
set(HYPRE_CMAKE_OPTIONS "")
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
# collect all HYPRE_ENABLE variables and pass them to hypre, assuming they are BOOL.
get_cmake_property(all_vars VARIABLES)
foreach(var ${all_vars})
if(var MATCHES "^HYPRE_ENABLE")
list(APPEND HYPRE_CMAKE_OPTIONS "-D${var}:BOOL=${${var}}")
endif()
endforeach()
set(HYPRE_FETCH_VERSION 2.33.0)
set(HYPRE_FETCH_TAG "v${HYPRE_FETCH_VERSION}" CACHE STRING "Tag, branch, or commit for HYPRE")
add_library(HYPRE STATIC IMPORTED)
# set options and associated dependencies
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
# process all MFEM_USE variables that impact hypre
if (MFEM_USE_CUDA)
list(APPEND HYPRE_CMAKE_OPTIONS -DHYPRE_ENABLE_CUDA:BOOL=ON -DCMAKE_CUDA_ARCHITECTURES:STRING=${CMAKE_CUDA_ARCHITECTURES})
find_package(CUDAToolkit REQUIRED)
+1 -1
View File
@@ -18,7 +18,7 @@
# - METIS (imported library target)
# - METIS_VERSION_5 (cache variable)
if (METIS_FETCH OR FETCH_TPLS)
if (MFEM_FETCH_METIS OR MFEM_FETCH_TPLS)
set(METIS_FETCH_VERSION 4.0.3)
add_library(METIS STATIC IMPORTED)
# define external project
+4 -3
View File
@@ -91,9 +91,10 @@ option(MFEM_ENABLE_BENCHMARKS "Build all of the benchmarks" OFF)
# Allow a user to specify fetching of certain third-party libraries instead of
# searching for existing installations.
option(FETCH_TPLS "Enable fetching of all supported third-party libraries" OFF)
option(HYPRE_FETCH "Enable fetching of hypre" OFF)
option(METIS_FETCH "Enable fetching of METIS" OFF)
option(MFEM_FETCH_TPLS "Enable fetching of all supported third-party libraries" OFF)
option(MFEM_FETCH_GSLIB "Enable fetching of GSLIB" OFF)
option(MFEM_FETCH_HYPRE "Enable fetching of hypre" OFF)
option(MFEM_FETCH_METIS "Enable fetching of METIS" OFF)
# Setting CXX/MPICXX on the command line or in user.cmake will overwrite the
# autodetected C++ compiler.
+1 -1
View File
@@ -101,7 +101,7 @@ $ cd ../miniapps
$ ls
CMakeLists.txt common meshing nurbs shifted toys
adjoint electromagnetics mtop parelag solvers
autodiff gslib navier performance tools
autodiff gslib fluids performance tools
```
And an example in "toys"
+14 -2
View File
@@ -85,6 +85,10 @@ groups_serial=(
"DPG miniapps:"
"miniapps/dpg"
"{acoustics,convection-diffusion,diffusion,maxwell}.cpp"'
'"isf"
"Schrodinger flow miniapps:"
"miniapps/fluids/schrodinger-flow"
"schrodinger_flow.cpp"'
'"gslib"
"GSLIB miniapps:"
"miniapps/gslib"
@@ -166,6 +170,10 @@ groups_parallel=(
"miniapps/electromagnetics"
"joule.cpp"'
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
'"isf"
"Schrodinger flow miniapps:"
"miniapps/fluids/schrodinger-flow"
"pschrodinger_flow.cpp"'
'"adjoint"
"Adjoint miniapps:"
"miniapps/adjoint"
@@ -191,7 +199,7 @@ groups_parallel=(
# todo: miniapps/multidomain
'"navier"
"Navier miniapps:"
"miniapps/navier"
"miniapps/fluids/navier"
"navier_cht.cpp"'
# todo: add other navier miniapps
'"nurbs"
@@ -281,6 +289,10 @@ groups_all=(
"miniapps/electromagnetics"
"joule.cpp"'
# "{joule,maxwell,tesla,volta}.cpp"' # todo: multiline sample runs
'"isf"
"Schrodinger flow miniapps:"
"miniapps/fluids/schrodinger-flow"
"{,p}schrodinger_flow.cpp"'
'"adjoint"
"Adjoint miniapps:"
"miniapps/adjoint"
@@ -308,7 +320,7 @@ groups_all=(
# todo: miniapps/multidomain
'"navier"
"Navier miniapps:"
"miniapps/navier"
"miniapps/fluids/navier"
"navier_cht.cpp"'
# todo: add other navier miniapps
'"nurbs"
+156
View File
@@ -0,0 +1,156 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see fem/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
# PYRAMID = 7
#
dimension
2
elements
25
3 3 0 1 2 3
3 3 1 4 5 2
3 3 4 6 7 5
3 3 6 8 9 7
3 3 8 10 11 9
3 3 10 12 13 11
3 3 12 14 15 13
3 3 14 16 17 15
3 3 16 18 19 17
3 3 18 20 21 19
3 3 20 22 23 21
3 3 22 24 25 23
3 3 24 26 27 25
3 3 26 28 29 27
3 3 28 30 31 29
3 3 30 32 33 31
3 3 32 34 35 33
3 3 17 19 36 37
3 3 37 36 38 39
3 3 39 38 40 41
3 3 41 40 42 43
3 3 43 42 44 45
3 3 45 44 46 47
3 3 47 46 48 49
3 3 49 48 50 51
boundary
52
2 1 0 1
2 1 2 3
1 1 3 0
2 1 1 4
2 1 5 2
2 1 4 6
2 1 7 5
2 1 6 8
2 1 9 7
2 1 8 10
2 1 11 9
2 1 10 12
2 1 13 11
2 1 12 14
2 1 15 13
2 1 14 16
2 1 17 15
2 1 16 18
2 1 18 20
2 1 21 19
2 1 20 22
2 1 23 21
2 1 22 24
2 1 25 23
2 1 24 26
2 1 27 25
2 1 26 28
2 1 29 27
2 1 28 30
2 1 31 29
2 1 30 32
2 1 33 31
2 1 32 34
3 1 34 35
2 1 35 33
2 1 19 36
2 1 37 17
2 1 36 38
2 1 39 37
2 1 38 40
2 1 41 39
2 1 40 42
2 1 43 41
2 1 42 44
2 1 45 43
2 1 44 46
2 1 47 45
2 1 46 48
2 1 49 47
2 1 48 50
4 1 50 51
2 1 51 49
vertices
52
2
0 0
1 0
1 1
0 1
2 0
2 1
3 0
3 1
4 0
4 1
5 0
5 1
6 0
6 1
7 0
7 1
8 0
8 1
9 0
9 1
10 0
10 1
11 0
11 1
12 0
12 1
13 0
13 1
14 0
14 1
15 0
15 1
16 0
16 1
17 0
17 1
9 2
8 2
9 3
8 3
9 4
8 4
9 5
8 5
9 6
8 6
9 7
8 7
9 8
8 8
9 9
8 9
+4 -2
View File
@@ -48,7 +48,7 @@ PROJECT_NAME = MFEM
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = v4.8.1
PROJECT_NUMBER = v4.9.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -973,10 +973,13 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/adjoint \
@MFEM_SOURCE_DIR@/miniapps/autodiff \
@MFEM_SOURCE_DIR@/miniapps/common \
@MFEM_SOURCE_DIR@/miniapps/contact \
@MFEM_SOURCE_DIR@/miniapps/dfem \
@MFEM_SOURCE_DIR@/miniapps/dpg \
@MFEM_SOURCE_DIR@/miniapps/dpg/util \
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
@MFEM_SOURCE_DIR@/miniapps/fluids/navier \
@MFEM_SOURCE_DIR@/miniapps/fluids/schrodinger-flow \
@MFEM_SOURCE_DIR@/miniapps/gslib \
@MFEM_SOURCE_DIR@/miniapps/hdiv-linear-solver \
@MFEM_SOURCE_DIR@/miniapps/hooke \
@@ -987,7 +990,6 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/meshing \
@MFEM_SOURCE_DIR@/miniapps/mtop \
@MFEM_SOURCE_DIR@/miniapps/multidomain \
@MFEM_SOURCE_DIR@/miniapps/navier \
@MFEM_SOURCE_DIR@/miniapps/nurbs \
@MFEM_SOURCE_DIR@/miniapps/parelag \
@MFEM_SOURCE_DIR@/miniapps/performance \
+4 -1
View File
@@ -117,6 +117,8 @@ namespace mfem {
* - <a class="el" href="ex39p_8cpp_source.html">Example 39p</a>: parallel named mesh attributes
* - <a class="el" href="ex40_8cpp_source.html">Example 40</a>: eikonal equation
* - <a class="el" href="ex40p_8cpp_source.html">Example 40p</a>: parallel eikonal equation
* - <a class="el" href="ex41_8cpp_source.html">Example 41</a>: DG/CG IMEX time-dependent advection-diffusion
* - <a class="el" href="ex41p_8cpp_source.html">Example 41p</a>: parallel DG/CG IMEX time-dependent advection-diffusion
*
* <H4>AmgX Examples</H4>
* - Variants of Examples
@@ -234,7 +236,8 @@ namespace mfem {
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Poisson problem
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Poisson problem
* - <a class="el" href="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
* - <a class="el" href="contact-patch-test_8cpp_source.html">Contact</a>: mortar contact patch test for elasticity
* - <a class="el" href="contact-patch-test_8cpp_source.html">Tribol</a>: mortar contact patch test for elasticity
* - <a class="el" href="contact_8cpp_source.html">Contact</a>: Frictionless contact examples using <a class="el" href="classmfem_1_1IPSolver.html#details">IP optimization</a> and the <a class="el" href="classmfem_1_1AMGFSolver.html#details">AMGF solver</a>
* - <a class="el" href="multidomain_8cpp_source.html">Multidomain miniapp</a>: Multidomain and Submesh demonstration miniapp
* - <a class="el" href="pdiffusion_8cpp_source.html">DPG Diffusion example</a>: DPG formulation for the diffusion problem
* - <a class="el" href="pmaxwell_8cpp_source.html">DPG Maxwell example</a>: DPG formulation for the indefinite Maxwell problem
+4
View File
@@ -46,6 +46,7 @@ list(APPEND ALL_EXE_SRCS
ex38.cpp
ex39.cpp
ex40.cpp
ex41.cpp
)
if (MFEM_USE_MPI)
@@ -89,6 +90,7 @@ if (MFEM_USE_MPI)
ex37p.cpp
ex39p.cpp
ex40p.cpp
ex41p.cpp
)
endif()
@@ -131,6 +133,8 @@ if (MFEM_ENABLE_TESTING)
list(APPEND THIS_TEST_OPTIONS "-dg")
elseif(${TEST_NAME} MATCHES "ex37p*")
list(APPEND THIS_TEST_OPTIONS "-mi" "3")
elseif(${TEST_NAME} MATCHES "ex41p*")
list(APPEND THIS_TEST_OPTIONS "-tf" "1.0")
endif()
if (NOT (${TEST_NAME} MATCHES ".*p$"))
+3
View File
@@ -471,10 +471,13 @@ int main(int argc, char *argv[])
ofstream sol_r_ofs("sol_r.gf");
ofstream sol_i_ofs("sol_i.gf");
ofstream sol_z_ofs("sol_z.gf");
sol_r_ofs.precision(8);
sol_i_ofs.precision(8);
sol_z_ofs.precision(8);
u.real().Save(sol_r_ofs);
u.imag().Save(sol_i_ofs);
u.Save(sol_z_ofs);
}
// 14. Send the solution by socket to a GLVis server.
+5 -1
View File
@@ -507,10 +507,11 @@ int main(int argc, char *argv[])
// 15. Save the refined mesh and the solution in parallel. This output can be
// viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_r_name, sol_i_name;
ostringstream mesh_name, sol_r_name, sol_i_name, sol_z_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_r_name << "sol_r." << setfill('0') << setw(6) << myid;
sol_i_name << "sol_i." << setfill('0') << setw(6) << myid;
sol_z_name << "sol_z." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
@@ -518,10 +519,13 @@ int main(int argc, char *argv[])
ofstream sol_r_ofs(sol_r_name.str().c_str());
ofstream sol_i_ofs(sol_i_name.str().c_str());
ofstream sol_z_ofs(sol_z_name.str().c_str());
sol_r_ofs.precision(8);
sol_i_ofs.precision(8);
sol_z_ofs.precision(8);
u.real().Save(sol_r_ofs);
u.imag().Save(sol_i_ofs);
u.Save(sol_z_ofs);
}
// 16. Send the solution by socket to a GLVis server.
+589
View File
@@ -0,0 +1,589 @@
// MFEM Example 41
//
// Compile with: make ex41
//
// Sample runs:
// ex41
// ex41 -cg
// ex41 -m ../data/periodic-hexagon.mesh -p 0 -r 2 -dt 0.005 -tf 10
// ex41 -m ../data/periodic-square.mesh -p 1 -r 2 -dt 0.005 -tf 9
// ex41 -m ../data/periodic-hexagon.mesh -p 1 -r 2 -dt 0.005 -tf 9
// ex41 -m ../data/amr-quad.mesh -p 1 -r 2 -dt 0.002 -tf 9
// ex41 -m ../data/star-q3.mesh -p 1 -r 2 -dt 0.001 -tf 9
// ex41 -m ../data/star-mixed.mesh -p 1 -r 2 -dt 0.005 -tf 9
// ex41 -m ../data/disc-nurbs.mesh -p 1 -r 3 -dt 0.005 -tf 9
// ex41 -m ../data/disc-nurbs.mesh -p 2 -r 3 -dt 0.005 -tf 9
// ex41 -m ../data/periodic-square.mesh -p 3 -r 4 -dt 0.0025 -tf 9 -vs 20
// ex41 -m ../data/periodic-cube.mesh -p 0 -r 2 -o 2 -dt 0.01 -tf 8
//
// Device sample runs:
//
// Description: This example code solves the time-dependent advection-diffusion
// equation du/dt + v.grad(u) - a div(grad(u)) = 0, where v is a
// given fluid velocity, a is the diffusion coefficient, and
// u0(x)=u(0,x) is a given initial condition.
//
// The example demonstrates the use of Discontinuous Galerkin (DG)
// bilinear forms in MFEM (face integrators), and the use of IMEX
// ODE time integrators.
//
// The option to use continuous finite elements is available too.
#include "mfem.hpp"
using namespace std;
using namespace mfem;
// Mesh bounding box
Vector bb_min, bb_max;
// Velocity coefficient
template<int problem=0>
void velocity_function(const Vector &x, Vector &v)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
{
// Translations in 1D, 2D, and 3D
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
break;
}
break;
}
case 1:
case 2:
{
// Clockwise rotation in 2D around the origin
const real_t w = M_PI/2;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
}
break;
}
case 3:
{
// Clockwise twisting rotation in 2D around the origin
const real_t w = M_PI/2;
real_t d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
d = d*d;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
}
break;
}
}
}
// Initial condition
template<int problem=0>
real_t u0_function(const Vector &x)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
case 1:
{
switch (dim)
{
case 1:
return exp(-40.*pow(X(0)-0.5,2));
case 2:
case 3:
{
real_t rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
if (dim == 3)
{
const real_t s = (1. + 0.25*cos(2*M_PI*X(2)));
rx *= s;
ry *= s;
}
return ( std::erfc(w*(X(0)-cx-rx))*std::erfc(-w*(X(0)-cx+rx)) *
std::erfc(w*(X(1)-cy-ry))*std::erfc(-w*(X(1)-cy+ry)) )/16;
}
}
}
case 2:
{
real_t x_ = X(0), y_ = X(1), rho, phi;
rho = std::hypot(x_, y_);
phi = atan2(y_, x_);
return pow(sin(M_PI*rho),2)*sin(3*phi);
}
case 3:
{
const real_t f = M_PI;
return sin(f*X(0))*sin(f*X(1));
}
}
return 0.0;
}
/// Solver for the implicit part of the ODE (the diffusion term).
/// Solves systems of the form: (M + dt*S) k = rhs.
class Implicit_Solver : public Solver
{
private:
SparseMatrix &M, &S, A;
CGSolver linear_solver;
BlockILU prec;
real_t dt;
public:
Implicit_Solver(SparseMatrix &M_, SparseMatrix &S_,
const FiniteElementSpace &fes)
: M(M_),
S(S_),
prec(fes.GetTypicalFE()->GetDof(),
BlockILU::Reordering::MINIMUM_DISCARDED_FILL),
dt(1.0)
{
linear_solver.iterative_mode = false;
linear_solver.SetRelTol(1e-9);
linear_solver.SetAbsTol(0.0);
linear_solver.SetMaxIter(100);
linear_solver.SetPrintLevel(0);
linear_solver.SetPreconditioner(prec);
}
void SetTimeStep(real_t dt_)
{
real_t ddt = dt-dt_;
real_t epsilon;
epsilon = std::numeric_limits<real_t>::epsilon();
epsilon*=10;
if (std::abs(ddt) > epsilon)
{
dt = dt_;
// Form operator A = M + dt*S
A = S;
A *= dt;
A += M;
// this will also call SetOperator on the preconditioner
linear_solver.SetOperator(A);
}
}
void SetOperator(const Operator &op) override
{
linear_solver.SetOperator(op);
}
void Mult(const Vector &x, Vector &y) const override
{
linear_solver.Mult(x, y);
}
};
/** A time-dependent operator for the right-hand side of the ODE. The weak
form of the advection-diffusion equation is M du/dt = K u - S u + b,
where M is the mass matrix, K and S are the advection and diffusion
matrices, and b describes the flow on the boundary. In the case of IMEX
evolution, the diffusion term is treated implicitly, and the advection
term is treated explicitly. */
class IMEX_Evolution : public TimeDependentOperator
{
private:
BilinearForm &M, &K, &S;
const Vector &b;
unique_ptr<Solver> M_prec;
CGSolver M_solver;
unique_ptr<Implicit_Solver> implicit_solver;
mutable Vector z;
public:
IMEX_Evolution(BilinearForm &M_, BilinearForm &K_, BilinearForm &S_,
const Vector &b_);
/// Evaluate k1=M^{-1}*G1(u,t); -> k1 = M^{-1}*(K*u + b)
void Mult1(const Vector &x, Vector &y) const;
/// Evaluate k2: M*k2 = G2(u+k2*dt,t); -> (M+S*dt)*k2=-S*u
void ImplicitSolve2(const real_t dt, const Vector &x, Vector &k);
void Mult(const Vector &x, Vector &y) const override
{
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_1 == GetEvalMode())
{
Mult1(x,y);
}
else
{
mfem_error("TimeDependentOperator::Mult() is not overridden!");
}
}
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override
{
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_2 == GetEvalMode())
{
ImplicitSolve2(dt,x,k);
}
else
{
mfem_error("TimeDependentOperator::ImplicitSolve() is not overridden!");
}
}
};
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
int problem = 0;
const char *mesh_file = "../data/periodic-square.mesh";
int ref_levels = 2;
int order = 3;
int ode_solver_type = 64; //IMEXRK3(3,4,3)
real_t t_final = 10.0;
real_t dt = 0.01;
bool paraview = false;
bool cg = false;
int vis_steps = 50;
real_t diffusion_term = 0.01;
real_t kappa = -1.0;
real_t sigma = -1.0;
bool visualization = true;
bool visit = false;
bool binary = false;
int precision = 8;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh", "Mesh file to use.");
args.AddOption(&problem, "-p", "--problem",
"Problem setup to use. See options in velocity_function().");
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order", "Order of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
ODESolver::IMEXTypes.c_str());
args.AddOption(&t_final, "-tf", "--t-final", "Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step", "Time step.");
args.AddOption(&diffusion_term, "-dc", "--diffusion-coeff",
"Diffusion coefficient in the PDE.");
args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
"--no-paraview-datafiles",
"Save data files for ParaView (paraview.org) visualization.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
"--ascii-datafiles",
"Use binary (Sidre) or ascii format for VisIt data files.");
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&cg, "-cg", "--continuous-galerkin", "-dg",
"--discontinuous-galerkin",
"Use Continuous-Galerkin Finite elements (Default is DG)");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
if (kappa < 0)
{
kappa = (order+1)*(order+1);
}
args.PrintOptions(cout);
// 2. Read the mesh from the given mesh file. We can handle geometrically
// periodic meshes in this code.
Mesh mesh(mesh_file);
const int dim = mesh.Dimension();
// 3. Define the IMEX (Split) ODE solver used for time integration. The IMEX
// solvers currently available are: 61 - Forward Backward Euler,
// 62 - IMEXRK2(2,2,2), 63 - IMEXRK2(2,3,2), and 64 - IMEX_DIRK_RK3.
unique_ptr<ODESolver> ode_solver = ODESolver::SelectIMEX(ode_solver_type);
// 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
// command-line parameter.
for (int lev = 0; lev < ref_levels; lev++) {mesh.UniformRefinement();}
if (mesh.NURBSext) {mesh.SetCurvature(max(order, 1));}
mesh.GetBoundingBox(bb_min, bb_max, max(order, 1));
// 5. Define the discontinuous DG finite element space of the given
// polynomial order on the refined mesh.
FiniteElementCollection *fec = NULL;
if (cg)
{
fec = new H1_FECollection(order, dim);
}
else
{
fec = new DG_FECollection(order, dim, BasisType::GaussLobatto);
}
FiniteElementSpace fes(&mesh, fec);
cout << "Number of unknowns: " << fes.GetVSize() << endl;
// 6. Set up and assemble the bilinear and linear forms corresponding to the
// DG discretization. The DGTraceIntegrator involves integrals over mesh
// interior faces.
std::unique_ptr<VectorFunctionCoefficient> velocity;
if (0==problem)
{
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<0>));
}
else if (1==problem)
{
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<1>));
}
else if (2==problem)
{
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<2>));
}
else if (3==problem)
{
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<3>));
}
ConstantCoefficient diff_coeff(diffusion_term);
BilinearForm m(&fes);
BilinearForm k(&fes);
BilinearForm s(&fes);
Vector b(fes.GetTrueVSize());
b = 0.0; //The inflow on the boundaries is set to zero.
m.AddDomainIntegrator(new MassIntegrator);
constexpr real_t alpha = -1.0;
k.AddDomainIntegrator(new ConvectionIntegrator(*velocity, alpha));
s.AddDomainIntegrator(new DiffusionIntegrator(diff_coeff));
if (!cg)
{
k.AddInteriorFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity,
alpha));
k.AddBdrFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity, alpha));
s.AddInteriorFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma,
kappa));
s.AddBdrFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma, kappa));
}
int skip_zeros = 0;
m.Assemble(skip_zeros);
k.Assemble(skip_zeros);
s.Assemble(skip_zeros);
m.Finalize(skip_zeros);
k.Finalize(skip_zeros);
s.Finalize(skip_zeros);
// 7. Define the initial conditions.
std::unique_ptr<FunctionCoefficient> u0;
if (0==problem)
{
u0.reset(new FunctionCoefficient(u0_function<0>));
}
else if (1==problem)
{
u0.reset(new FunctionCoefficient(u0_function<1>));
}
else if (2==problem)
{
u0.reset(new FunctionCoefficient(u0_function<2>));
}
else if (3==problem)
{
u0.reset(new FunctionCoefficient(u0_function<3>));
}
GridFunction u(&fes);
u.ProjectCoefficient(*u0);
// Create data collection for solution output: either VisItDataCollection for
// ascii data files, or SidreDataCollection for binary data files.
DataCollection *dc = NULL;
if (visit)
{
if (binary)
{
#ifdef MFEM_USE_SIDRE
dc = new SidreDataCollection("Example41", &mesh);
#else
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
#endif
}
else
{
dc = new VisItDataCollection("Example41", &mesh);
dc->SetPrecision(precision);
}
dc->RegisterField("solution", &u);
dc->SetCycle(0);
dc->SetTime(0.0);
dc->Save();
}
// 8. Set up paraview visualization, if desired.
unique_ptr<ParaViewDataCollection> pv;
if (paraview)
{
pv = make_unique<ParaViewDataCollection>("Example41", &mesh);
pv->SetPrefixPath("ParaView");
pv->RegisterField("solution", &u);
pv->SetLevelsOfDetail(order);
pv->SetDataFormat(VTKFormat::BINARY);
pv->SetHighOrderOutput(true);
pv->SetCycle(0);
pv->SetTime(0.0);
pv->Save();
}
socketstream sout;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
sout.open(vishost, visport);
if (!sout)
{
cout << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << endl;
visualization = false;
cout << "GLVis visualization disabled.\n";
}
else
{
sout.precision(precision);
sout << "solution\n" << mesh << u;
sout << "pause\n";
sout << flush;
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
}
// 9. Define the time-dependent evolution operator describing the ODE
// right-hand side, and perform time-integration (looping over the time
// iterations, ti, with a time-step dt).
IMEX_Evolution adv(m, k, s, b);
real_t t = 0.0;
adv.SetTime(t);
ode_solver->Init(adv);
bool done = false;
for (int ti = 0; !done; )
{
real_t dt_real = min(dt, t_final - t);
ode_solver->Step(u, t, dt_real);
ti++;
done = (t >= t_final - 1e-8*dt);
if (done || ti % vis_steps == 0)
{
cout << "time step: " << ti << ", time: " << t << endl;
if (paraview)
{
pv->SetCycle(ti);
pv->SetTime(t);
pv->Save();
}
if (visualization)
{
sout << "solution\n" << mesh << u << flush;
}
if (visit)
{
dc->SetCycle(ti);
dc->SetTime(t);
dc->Save();
}
}
}
delete fec;
return 0;
}
// Implementation of class IMEX_Evolution
IMEX_Evolution::IMEX_Evolution(BilinearForm &M_, BilinearForm &K_,
BilinearForm &S_, const Vector &b_)
: TimeDependentOperator(M_.FESpace()->GetTrueVSize()),
M(M_), K(K_), S(S_), b(b_), z(height)
{
Array<int> ess_tdof_list;
if (M.GetAssemblyLevel() == AssemblyLevel::LEGACY)
{
M_prec = make_unique<DSmoother>(M.SpMat());
M_solver.SetOperator(M.SpMat());
implicit_solver = make_unique<Implicit_Solver>(M.SpMat(), S.SpMat(),
*M.FESpace());
}
else
{
MFEM_ABORT("Implicit time integration is not supported with partial assembly");
}
M_solver.SetPreconditioner(*M_prec);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(100);
M_solver.SetPrintLevel(0);
}
void IMEX_Evolution::Mult1(const Vector &x, Vector &y) const
{
// Perform the explicit step
// y = M^{-1} (K x + b)
K.Mult(x, z);
z += b;
M_solver.Mult(z, y);
}
void IMEX_Evolution::ImplicitSolve2(const real_t dt, const Vector &x, Vector &k)
{
// Perform the implicit step
// solve for k, k = -(M+dt S)^{-1} S x
MFEM_VERIFY(implicit_solver != NULL,
"Implicit time integration is not supported with partial assembly");
S.Mult(x, z);
z.Neg();
implicit_solver->SetTimeStep(dt);
implicit_solver->Mult(z, k);
}
+737
View File
@@ -0,0 +1,737 @@
// MFEM Example 41 - Parallel Version
//
// Compile with: make ex41p
//
// Sample runs:
// mpirun -np 4 ex41p
// mpirun -np 4 ex41p -cg
// mpirun -np 4 ex41p -m ../data/periodic-hexagon.mesh -p 0 -dt 0.005 -tf 10
// mpirun -np 4 ex41p -m ../data/periodic-square.mesh -p 1 -dt 0.005 -tf 9
// mpirun -np 4 ex41p -m ../data/periodic-hexagon.mesh -p 1 -dt 0.005 -tf 9
// mpirun -np 4 ex41p -m ../data/star-q3.mesh -p 1 -rp 1 -dt 0.001 -tf 9
// mpirun -np 4 ex41p -m ../data/disc-nurbs.mesh -p 1 -rp 1 -dt 0.005 -tf 9
// mpirun -np 4 ex41p -m ../data/disc-nurbs.mesh -p 2 -rp 1 -dt 0.005 -tf 9
// mpirun -np 4 ex41p -m ../data/periodic-square.mesh -rp 2 -dt 0.0025 -tf 9 -vs 20
// mpirun -np 4 ex41p -m ../data/periodic-cube.mesh -p 0 -rs 2 -o 2 -dt 0.01 -tf 8
//
// Device sample runs:
//
// Description: This example code solves the time-dependent advection-diffusion
// equation du/dt + v.grad(u) - a div(grad(u)) = 0, where v is a
// given fluid velocity, a is the diffusion coefficient, and
// u0(x)=u(0,x) is a given initial condition.
//
// The example demonstrates the use of Discontinuous Galerkin (DG)
// bilinear forms in MFEM (face integrators), DG-LOR Preconditioning
// and the use of IMEX ODE time integrators.
//
// The Option to use Continuous Finite Elements is available too.
#include "mfem.hpp"
using namespace std;
using namespace mfem;
// Mesh bounding box
Vector bb_min, bb_max;
// Velocity coefficient
template<int problem=0>
void velocity_function(const Vector &x, Vector &v)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
{
// Translations in 1D, 2D, and 3D
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = sqrt(2./3.); v(1) = sqrt(1./3.); break;
case 3: v(0) = sqrt(3./6.); v(1) = sqrt(2./6.); v(2) = sqrt(1./6.);
break;
}
break;
}
case 1:
case 2:
{
// Clockwise rotation in 2D around the origin
const real_t w = M_PI/2;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = w*X(1); v(1) = -w*X(0); break;
case 3: v(0) = w*X(1); v(1) = -w*X(0); v(2) = 0.0; break;
}
break;
}
case 3:
{
// Clockwise twisting rotation in 2D around the origin
const real_t w = M_PI/2;
real_t d = max((X(0)+1.)*(1.-X(0)),0.) * max((X(1)+1.)*(1.-X(1)),0.);
d = d*d;
switch (dim)
{
case 1: v(0) = 1.0; break;
case 2: v(0) = d*w*X(1); v(1) = -d*w*X(0); break;
case 3: v(0) = d*w*X(1); v(1) = -d*w*X(0); v(2) = 0.0; break;
}
break;
}
}
}
// Initial condition
template<int problem=0>
real_t u0_function(const Vector &x)
{
int dim = x.Size();
// map to the reference [-1,1] domain
Vector X(dim);
for (int i = 0; i < dim; i++)
{
real_t center = (bb_min[i] + bb_max[i]) * 0.5;
X(i) = 2 * (x(i) - center) / (bb_max[i] - bb_min[i]);
}
switch (problem)
{
case 0:
case 1:
{
switch (dim)
{
case 1:
return exp(-40.*pow(X(0)-0.5,2));
case 2:
case 3:
{
real_t rx = 0.45, ry = 0.25, cx = 0., cy = -0.2, w = 10.;
if (dim == 3)
{
const real_t s = (1. + 0.25*cos(2*M_PI*X(2)));
rx *= s;
ry *= s;
}
return ( std::erfc(w*(X(0)-cx-rx))*std::erfc(-w*(X(0)-cx+rx)) *
std::erfc(w*(X(1)-cy-ry))*std::erfc(-w*(X(1)-cy+ry)) )/16;
}
}
}
case 2:
{
real_t x_ = X(0), y_ = X(1), rho, phi;
rho = std::hypot(x_, y_);
phi = atan2(y_, x_);
return pow(sin(M_PI*rho),2)*sin(3*phi);
}
case 3:
{
const real_t f = M_PI;
return sin(f*X(0))*sin(f*X(1));
}
}
return 0.0;
}
class Implicit_Solver : public Solver
{
private:
HypreParMatrix &M, &S;
HypreParMatrix *A;
CGSolver linear_solver;
real_t dt;
SparseMatrix M_diag;
public:
Implicit_Solver(HypreParMatrix &M_, HypreParMatrix &S_,
const FiniteElementSpace &fes)
: M(M_),
S(S_),
A(nullptr),
linear_solver(M.GetComm()),
dt(1.0)
{
linear_solver.iterative_mode = false;
linear_solver.SetRelTol(1e-9);
linear_solver.SetAbsTol(0.0);
linear_solver.SetMaxIter(100);
linear_solver.SetPrintLevel(0);
M.GetDiag(M_diag);
}
void SetTimeStep(real_t dt_)
{
real_t ddt = dt-dt_;
// syncronize ddt across all processes
MPI_Comm comm = M.GetComm();
int myrank;
MPI_Comm_rank(comm, &myrank);
MPI_Bcast(&ddt, 1, MPI_DOUBLE, 0, comm);
real_t epsilon;
epsilon = std::numeric_limits<real_t>::epsilon();
// allow for some tolerance in the time stepping process
epsilon*=10;
if (fabs(ddt) > epsilon)
{
if (0==myrank)
{
cout << "Updating Implicit_Solver time step from " << dt
<< " to " << dt_ << endl;
}
delete A;
dt = dt_;
// Form operator A = M + dt*S
A = Add(dt, S, 1.0, M);
linear_solver.SetOperator(*A);
}
}
void SetOperator(const Operator &op) override
{
linear_solver.SetOperator(op);
}
void Mult(const Vector &x, Vector &y) const override
{
linear_solver.Mult(x, y);
}
void SetPreconditioner(Solver &precond)
{
linear_solver.SetPreconditioner(precond);
}
~Implicit_Solver() override
{
delete A;
}
};
/** A time-dependent operator for the right-hand side of the ODE. The DG weak
form of the advection-diffusion equation is (M + dt S) du/dt = Su - K u + b
, where M and K are the mass and advection matrices, and b describes the
flow on the boundary. In the case of IMEX evolution, the diffusion term is
treated implicitly, and the advection term is treated explicitly. */
class IMEX_Evolution : public TimeDependentOperator
{
private:
OperatorHandle M, K, S, A;
const Vector &b;
Solver *M_prec;
CGSolver M_solver;
Implicit_Solver *implicit_solver;
LORSolver<HypreBoomerAMG>* lor_solver;
mutable Vector z;
mutable Vector w;
public:
IMEX_Evolution(ParBilinearForm &M_, ParBilinearForm &K_, ParBilinearForm &S_,
const Vector &b_, ParBilinearForm &A_);
virtual
~IMEX_Evolution()
{
delete implicit_solver;
delete lor_solver;
delete M_prec;
}
void Mult1(const Vector &x, Vector &y) const;
void ImplicitSolve2(const real_t dt, const Vector &x, Vector &k);
void Mult(const Vector &x, Vector &y) const override
{
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_1 == GetEvalMode())
{
Mult1(x,y);
}
else
{
mfem_error("TimeDependentOperator::Mult() is not overridden!");
}
}
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override
{
if (TimeDependentOperator::EvalMode::ADDITIVE_TERM_2 == GetEvalMode())
{
ImplicitSolve2(dt,x,k);
}
else
{
mfem_error("TimeDependentOperator::ImplicitSolve() is not overridden!");
}
}
};
int main(int argc, char *argv[])
{
// 1. Initialize MPI and HYPRE.
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
// 2. Parse command-line options.
int problem = 0;
const char *mesh_file = "../data/periodic-square.mesh";
int ser_ref_levels = 2;
int par_ref_levels = 0;
int order = 3;
int ode_solver_type = 64; // 61 - Forward Backward Euler
// 62 - IMEXRK2(2,2,2)
// 63 - IMEXRK2(2,3,2)
// 64 - IMEXRK3(3,4,3)
real_t t_final = 10.0;
real_t dt = 0.01;
bool paraview = false;
bool cg = false;
int vis_steps = 50;
bool adios2 = false;
bool binary = false;
real_t diffusion_term = 0.01;
real_t kappa = -1.0;
real_t sigma = -1.0;
bool visualization = true;
bool visit = false;
int precision = 16;
cout.precision(precision);
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&problem, "-p", "--problem",
"Problem setup to use. See options in velocity_function().");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
ODESolver::IMEXTypes.c_str());
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&diffusion_term, "-dc", "--diffusion-coeff",
"Diffusion coefficient in the PDE.");
args.AddOption(&paraview, "-paraview", "--paraview-datafiles", "-no-paraview",
"--no-paraview-datafiles",
"Save data files for ParaView (paraview.org) visualization.");
args.AddOption(&visit, "-visit", "--visit-datafiles", "-no-visit",
"--no-visit-datafiles",
"Save data files for VisIt (visit.llnl.gov) visualization.");
args.AddOption(&adios2, "-adios2", "--adios2-streams", "-no-adios2",
"--no-adios2-streams",
"Save data using adios2 streams.");
args.AddOption(&binary, "-binary", "--binary-datafiles", "-ascii",
"--ascii-datafiles",
"Use binary (Sidre) or ascii format for VisIt data files.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&cg, "-cg", "--continuous-galerkin", "-dg",
"--discontinuous-galerkin",
"Use Continuous-Galerkin Finite elements (Default is DG)");
args.Parse();
if (!args.Good())
{
if (Mpi::Root())
{
args.PrintUsage(cout);
}
return 1;
}
if (Mpi::Root())
{
args.PrintOptions(cout);
}
if (kappa < 0)
{
kappa = (order+1)*(order+1);
}
// 3. Read the mesh from the given mesh file. We can handle geometrically
// periodic meshes in this code.
Mesh *mesh = new Mesh(mesh_file);
const int dim = mesh->Dimension();
// 4. Define the IMEX (Split) ODE solver used for time integration. The IMEX
// solvers currently available are: 55 - Forward Backward Euler,
// 56 - IMEXRK2(2,2,2), 57 - IMEXRK2(2,3,2), and
unique_ptr<ODESolver> ode_solver = ODESolver::SelectIMEX(ode_solver_type);
// 5. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
// command-line parameter.
for (int lev = 0; lev < ser_ref_levels; lev++) { mesh->UniformRefinement(); }
if (mesh->NURBSext)
{
mesh->SetCurvature(max(order, 1));
}
mesh->GetBoundingBox(bb_min, bb_max, max(order, 1));
// 6. Define the parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int lev = 0; lev < par_ref_levels; lev++)
{
pmesh->UniformRefinement();
}
// 7. Define the discontinuous DG finite element space of the given
// polynomial order on the refined mesh.
FiniteElementCollection *fec = NULL;
if (cg)
{
fec = new H1_FECollection(order, dim);
}
else
{
fec = new DG_FECollection(order, dim, BasisType::GaussLobatto);
}
ParFiniteElementSpace *fes = new ParFiniteElementSpace(pmesh, fec);
HYPRE_BigInt global_vSize = fes->GlobalTrueVSize();
if (Mpi::Root())
{
cout << "Number of unknowns: " << global_vSize << endl;
}
// 8. Set up and assemble the bilinear and linear forms corresponding to the
// DG discretization. The DGTraceIntegrator involves integrals over mesh
// interior faces.
std::unique_ptr<VectorFunctionCoefficient> velocity;
if (0==problem)
{
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<0>));
}
else if (1==problem)
{
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<1>));
}
else if (2==problem)
{
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<2>));
}
else if (3==problem)
{
velocity.reset(new VectorFunctionCoefficient(dim, velocity_function<3>));
}
ConstantCoefficient diff_coeff(diffusion_term);
ConstantCoefficient dt_diff_coeff(dt*diffusion_term);
ParBilinearForm *m = new ParBilinearForm(fes);
ParBilinearForm *k = new ParBilinearForm(fes);
ParBilinearForm *s = new ParBilinearForm(fes);
m->AddDomainIntegrator(new MassIntegrator());
constexpr real_t alpha = -1.0;
k->AddDomainIntegrator(new ConvectionIntegrator(*velocity, alpha));
s->AddDomainIntegrator(new DiffusionIntegrator(diff_coeff));
// For the preconditioner - create billinear form corresponding to
// operator (M + dt S)
ParBilinearForm *a = new ParBilinearForm(fes);
a->AddDomainIntegrator(new MassIntegrator);
a->AddDomainIntegrator(new DiffusionIntegrator(dt_diff_coeff));
if (!cg)
{
k->AddInteriorFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity,
alpha));
k->AddBdrFaceIntegrator(new NonconservativeDGTraceIntegrator(*velocity, alpha));
s->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma,
kappa));
s->AddBdrFaceIntegrator(new DGDiffusionIntegrator(diff_coeff, sigma, kappa));
a->AddInteriorFaceIntegrator(new DGDiffusionIntegrator(dt_diff_coeff, sigma,
kappa));
a->AddBdrFaceIntegrator(new DGDiffusionIntegrator(dt_diff_coeff, sigma, kappa));
}
int skip_zeros = 0;
m->Assemble(skip_zeros);
k->Assemble(skip_zeros);
s->Assemble(skip_zeros);
a->Assemble();
m->Finalize(skip_zeros);
k->Finalize(skip_zeros);
s->Finalize(skip_zeros);
a->Finalize(skip_zeros);
HypreParVector b(fes);
b = 0.0;
// 9. Define the initial conditions. Set up visualization (if desired).
std::unique_ptr<FunctionCoefficient> u0;
if (0==problem)
{
u0.reset(new FunctionCoefficient(u0_function<0>));
}
else if (1==problem)
{
u0.reset(new FunctionCoefficient(u0_function<1>));
}
else if (2==problem)
{
u0.reset(new FunctionCoefficient(u0_function<2>));
}
else if (3==problem)
{
u0.reset(new FunctionCoefficient(u0_function<3>));
}
ParGridFunction *u = new ParGridFunction(fes);
u->ProjectCoefficient(*u0);
HypreParVector *U = u->GetTrueDofs();
DataCollection *dc = NULL;
if (visit)
{
if (binary)
{
#ifdef MFEM_USE_SIDRE
dc = new SidreDataCollection("Example41-Parallel", pmesh);
#else
MFEM_ABORT("Must build with MFEM_USE_SIDRE=YES for binary output.");
#endif
}
else
{
dc = new VisItDataCollection("Example41-Parallel", pmesh);
dc->SetPrecision(precision);
// To save the mesh using MFEM's parallel mesh format:
// dc->SetFormat(DataCollection::PARALLEL_FORMAT);
}
dc->RegisterField("solution", u);
dc->SetCycle(0);
dc->SetTime(0.0);
dc->Save();
}
ParaViewDataCollection *pd = NULL;
if (paraview)
{
pd = new ParaViewDataCollection("Example41P", pmesh);
pd->SetPrefixPath("ParaView");
pd->RegisterField("solution", u);
pd->SetLevelsOfDetail(order);
pd->SetDataFormat(VTKFormat::BINARY);
pd->SetHighOrderOutput(true);
pd->SetCycle(0);
pd->SetTime(0.0);
pd->Save();
}
socketstream sout;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
sout.open(vishost, visport);
if (!sout)
{
if (Mpi::Root())
{
cout << "Unable to connect to GLVis server at "
<< vishost << ':' << visport << endl;
}
visualization = false;
if (Mpi::Root())
{
cout << "GLVis visualization disabled.\n";
}
}
else
{
sout << "parallel " << num_procs << " " << myid << "\n";
sout.precision(precision);
sout << "solution\n" << *pmesh << *u;
sout << "pause\n";
sout << flush;
if (Mpi::Root())
{
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
}
}
#ifdef MFEM_USE_ADIOS2
ADIOS2DataCollection *adios2_dc = NULL;
if (adios2)
{
std::string postfix(mesh_file);
postfix.erase(0, std::string("../data/").size() );
postfix += "_o" + std::to_string(order);
const std::string collection_name = "ex41-p-" + postfix + ".bp";
adios2_dc = new ADIOS2DataCollection(MPI_COMM_WORLD, collection_name, pmesh);
// output data substreams are half the number of mpi processes
adios2_dc->SetParameter("SubStreams", std::to_string(num_procs/2) );
// adios2_dc->SetLevelsOfDetail(2);
adios2_dc->RegisterField("solution", u);
adios2_dc->SetCycle(0);
adios2_dc->SetTime(0.0);
adios2_dc->Save();
}
#endif
// 10. Define the time-dependent evolution operator describing the
// ODE right-hand side, and perform time-integration (looping
// over the time iterations, ti, with a time-step dt).
IMEX_Evolution adv(*m, *k, *s, b, *a);
real_t t = 0.0;
adv.SetTime(t);
ode_solver->Init(adv);
bool done = false;
for (int ti = 0; !done; )
{
real_t dt_real = min(dt, t_final - t);
ode_solver->Step(*U, t, dt_real);
ti++;
done = (t >= t_final - 1e-8*dt);
if (done || ti % vis_steps == 0)
{
if (Mpi::Root())
{
cout << "time step: " << ti << ", time: " << t << endl;
}
*u = *U;
if (visualization)
{
sout << "parallel " << num_procs << " " << myid << "\n";
sout << "solution\n" << *pmesh << *u << flush;
}
if (paraview)
{
pd->SetCycle(ti);
pd->SetTime(t);
pd->Save();
}
#ifdef MFEM_USE_ADIOS2
// transient solutions can be visualized with ParaView
if (adios2)
{
adios2_dc->SetCycle(ti);
adios2_dc->SetTime(t);
adios2_dc->Save();
}
#endif
}
}
// 11. Free the used memory.
delete pd;
delete U;
delete u;
delete a;
delete s;
delete k;
delete m;
delete fes;
delete pmesh;
delete dc;
delete fec;
return 0;
}
// Implementation of class IMEX_Evolution
IMEX_Evolution::IMEX_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
ParBilinearForm &S_, const Vector &b_, ParBilinearForm &A_)
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize()), b(b_),
M_solver(M_.ParFESpace()->GetComm()), z(height), w(height)
{
if (M_.GetAssemblyLevel()==AssemblyLevel::LEGACY)
{
M.Reset(M_.ParallelAssemble(), true);
K.Reset(K_.ParallelAssemble(), true);
S.Reset(S_.ParallelAssemble(), true);
}
else
{
M.Reset(&M_, false);
K.Reset(&K_, false);
S.Reset(&S_, false);
}
M_solver.SetOperator(*M);
Array<int> ess_tdof_list;
if (M_.GetAssemblyLevel() == AssemblyLevel::LEGACY)
{
A.Reset(A_.ParallelAssemble(), true);
HypreParMatrix &M_mat = *M.As<HypreParMatrix>();
HypreParMatrix &S_mat = *S.As<HypreParMatrix>();
HypreSmoother *hypre_prec = new HypreSmoother(M_mat, HypreSmoother::Jacobi);
M_prec = hypre_prec;
implicit_solver = new Implicit_Solver(M_mat, S_mat, *M_.FESpace());
lor_solver = new LORSolver<HypreBoomerAMG>(A_, ess_tdof_list);
lor_solver->GetSolver().SetSystemsOptions(A_.ParFESpace()->GetVDim(), true);
implicit_solver -> SetPreconditioner(*lor_solver);
}
else
{
MFEM_ABORT("Implicit time integration is not supported with partial assembly");
}
M_solver.SetPreconditioner(*M_prec);
M_solver.iterative_mode = false;
M_solver.SetRelTol(1e-9);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(100);
M_solver.SetPrintLevel(0);
}
void IMEX_Evolution::Mult1(const Vector &x, Vector &y) const
{
// Perform the explicit step
// y = M^{-1} (K x + b)
K->Mult(x, z);
z += b;
M_solver.Mult(z, y);
}
void IMEX_Evolution::ImplicitSolve2(const real_t dt, const Vector &x, Vector &k)
{
// Perform the implicit step
// solve for k, k = -(M+dt S)^{-1} S x
MFEM_VERIFY(implicit_solver != NULL,
"Implicit time integration is not supported with partial assembly");
S->Mult(x, z);
z*= -1.0;
implicit_solver->SetTimeStep(dt);
implicit_solver->Mult(z, k);
}
+8 -4
View File
@@ -22,11 +22,11 @@ MFEM_LIB_FILE = mfem_is_not_built
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30 \
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40 ex41
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
ex25p ex26p ex27p ex28p ex29p ex30p ex31p ex32p ex33p ex34p ex35p ex36p \
ex37p ex39p ex40p
ex37p ex39p ex40p ex41p
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p \
ex22p ex24p ex25p ex26p ex34p ex35p
@@ -157,6 +157,10 @@ ex37-test-seq: ex37
@$(call mfem-test,$<,, Serial example,-mi 3)
ex37p-test-par: ex37p
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-mi 3)
ex41-test-seq: ex41
@$(call mfem-test,$<,, Serial example,-tf 1.0)
ex41p-test-par: ex41p
@$(call mfem-test,$<, $(RUN_MPI), Parallel example,-tf 1.0)
# Testing: optional tests
ifeq ($(MFEM_USE_STRUMPACK),YES)
ex11p-test-strumpack: ex11p
@@ -195,8 +199,8 @@ clean-build:
clean-exec:
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh ex6p-checkpoint.*
@rm -rf Example5* Example9* Example15* Example16* Example23* ParaView
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.* order.*
@rm -f ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.* sol_z.*
@rm -f order.* ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
@rm -f deformed.* velocity.* elastic_energy.* mode_* mode_deriv_* flux.*
@rm -f ex5-p-*.bp ex9-p-*.bp ex12-p-*.bp ex16-p-*.bp
@rm -f ex16.mesh ex16-mesh.* ex16-init.* ex16-final.*
+50 -27
View File
@@ -128,32 +128,46 @@ set(SRCS
normal_deriv_restriction.cpp
staticcond.cpp
tmop.cpp
tmop/tmop_pa.cpp
tmop/tmop_pa_da3.cpp
tmop/tmop_pa_h2d.cpp
tmop/tmop_pa_h2d_c0.cpp
tmop/tmop_pa_h2m.cpp
tmop/tmop_pa_h2m_c0.cpp
tmop/tmop_pa_h2s.cpp
tmop/tmop_pa_h2s_c0.cpp
tmop/tmop_pa_h3d.cpp
tmop/tmop_pa_h3d_c0.cpp
tmop/tmop_pa_h3m.cpp
tmop/tmop_pa_h3m_c0.cpp
tmop/tmop_pa_h3s.cpp
tmop/tmop_pa_h3s_c0.cpp
tmop/tmop_pa_jp2.cpp
tmop/tmop_pa_jp3.cpp
tmop/tmop_pa_p2.cpp
tmop/tmop_pa_p2_c0.cpp
tmop/tmop_pa_p3.cpp
tmop/tmop_pa_p3_c0.cpp
tmop/tmop_pa_tc2.cpp
tmop/tmop_pa_tc3.cpp
tmop/tmop_pa_w2.cpp
tmop/tmop_pa_w2_c0.cpp
tmop/tmop_pa_w3.cpp
tmop/tmop_pa_w3_c0.cpp
tmop/pa.cpp
tmop/assemble/diag2_limit.cpp
tmop/assemble/diag2.cpp
tmop/assemble/grad2_limit.cpp
tmop/assemble/grad2.cpp
tmop/assemble/diag3_limit.cpp
tmop/assemble/diag3.cpp
tmop/assemble/grad3_limit.cpp
tmop/assemble/grad3.cpp
tmop/metrics/001.cpp
tmop/metrics/002.cpp
tmop/metrics/007.cpp
tmop/metrics/056.cpp
tmop/metrics/077.cpp
tmop/metrics/080.cpp
tmop/metrics/094.cpp
tmop/metrics/302.cpp
tmop/metrics/303.cpp
tmop/metrics/315.cpp
tmop/metrics/318.cpp
tmop/metrics/321.cpp
tmop/metrics/332.cpp
tmop/metrics/338.cpp
tmop/mult/grad2_limit.cpp
tmop/mult/grad2.cpp
tmop/mult/mult2_limit.cpp
tmop/mult/mult2.cpp
tmop/mult/grad3_limit.cpp
tmop/mult/grad3.cpp
tmop/mult/mult3_limit.cpp
tmop/mult/mult3.cpp
tmop/tools/det2_jpr.cpp
tmop/tools/det3_jpr.cpp
tmop/tools/discrete.cpp
tmop/tools/energy2_limit.cpp
tmop/tools/energy2.cpp
tmop/tools/energy3_limit.cpp
tmop/tools/energy3.cpp
tmop/tools/target2.cpp
tmop/tools/target3.cpp
tmop_tools.cpp
tmop_amr.cpp
gslib.cpp
@@ -165,6 +179,7 @@ set(SRCS
hyperbolic.cpp
integrator.cpp
bounds.cpp
particleset.cpp
)
set(HDRS
@@ -182,6 +197,8 @@ set(HDRS
integ/bilininteg_hdiv_kernels.hpp
integ/bilininteg_hcurlhdiv_kernels.hpp
integ/bilininteg_mass_kernels.hpp
integ/bilininteg_vecdiffusion_pa.hpp
integ/bilininteg_vecmass_pa.hpp
coefficient.hpp
complex_fem.hpp
convergence.hpp
@@ -279,7 +296,12 @@ set(HDRS
tfespace.hpp
tintrules.hpp
tmop.hpp
tmop/tmop_pa.hpp
tmop/pa.hpp
tmop/assemble/grad2.hpp
tmop/assemble/grad2.hpp
tmop/mult/mult2.hpp
tmop/mult/mult3.hpp
tmop/tools/energy2.hpp
tmop_tools.hpp
tmop_amr.hpp
gslib.hpp
@@ -287,6 +309,7 @@ set(HDRS
hyperbolic.hpp
integrator.hpp
bounds.hpp
particleset.hpp
)
if (MFEM_USE_SIDRE)
-1
View File
@@ -3066,7 +3066,6 @@ void VectorDiffusionIntegrator::AssembleElementMatrix(
for (int i = 0; i < ir -> GetNPoints(); i++)
{
const IntegrationPoint &ip = ir->IntPoint(i);
el.CalcDShape(ip, dshape);
+44 -41
View File
@@ -2596,41 +2596,40 @@ public:
by scalar FE through standard transformation. */
class VectorMassIntegrator: public BilinearFormIntegrator
{
private:
int vdim;
int vdim = -1, Q_order = 0;
Vector shape, te_shape, vec;
DenseMatrix partelmat;
DenseMatrix mcoeff;
int Q_order;
protected:
Coefficient *Q;
VectorCoefficient *VQ;
MatrixCoefficient *MQ;
Coefficient *Q = nullptr;
VectorCoefficient *VQ = nullptr;
MatrixCoefficient *MQ = nullptr;
// PA extension
Vector pa_data;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
int ne, dim, dofs1D, quad1D, coeff_vdim;
Vector pa_data;
public:
/// Construct an integrator with coefficient 1.0
VectorMassIntegrator()
: vdim(-1), Q_order(0), Q(NULL), VQ(NULL), MQ(NULL) { }
VectorMassIntegrator() = default;
/** Construct an integrator with scalar coefficient q. If possible, save
memory by using a scalar integrator since the resulting matrix is block
diagonal with the same diagonal block repeated. */
VectorMassIntegrator(Coefficient &q, int qo = 0)
: vdim(-1), Q_order(qo), Q(&q), VQ(NULL), MQ(NULL) { }
VectorMassIntegrator(Coefficient &q, const IntegrationRule *ir)
: BilinearFormIntegrator(ir), vdim(-1), Q_order(0), Q(&q), VQ(NULL),
MQ(NULL) { }
VectorMassIntegrator(Coefficient &q, int qo = 0): Q_order(qo), Q(&q) { }
VectorMassIntegrator(Coefficient &q, const IntegrationRule *ir):
BilinearFormIntegrator(ir), Q(&q) { }
/// Construct an integrator with diagonal coefficient q
VectorMassIntegrator(VectorCoefficient &q, int qo = 0)
: vdim(q.GetVDim()), Q_order(qo), Q(NULL), VQ(&q), MQ(NULL) { }
VectorMassIntegrator(VectorCoefficient &q, int qo = 0):
vdim(q.GetVDim()), Q_order(qo), VQ(&q) { }
/// Construct an integrator with matrix coefficient q
VectorMassIntegrator(MatrixCoefficient &q, int qo = 0)
: vdim(q.GetVDim()), Q_order(qo), Q(NULL), VQ(NULL), MQ(&q) { }
VectorMassIntegrator(MatrixCoefficient &q, int qo = 0):
vdim(q.GetVDim()), Q_order(qo), MQ(&q) { }
int GetVDim() const { return vdim; }
void SetVDim(int vdim_) { vdim = vdim_; }
@@ -2642,6 +2641,7 @@ public:
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat) override;
using BilinearFormIntegrator::AssemblePA;
void AssemblePA(const FiniteElementSpace &fes) override;
void AssembleMF(const FiniteElementSpace &fes) override;
@@ -2650,6 +2650,15 @@ public:
void AddMultPA(const Vector &x, Vector &y) const override;
void AddMultMF(const Vector &x, Vector &y) const override;
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
using VectorMassAddMultPAType =
void(*)(const int, const int,
const Array<real_t>&, const Vector&,
const Vector&, Vector&, const int, const int);
MFEM_REGISTER_KERNELS(VectorMassAddMultPA,
VectorMassAddMultPAType,
(int, int, int));
};
@@ -3120,23 +3129,21 @@ public:
to be the spatial dimension (i.e. 2-dimension or 3-dimension). */
class VectorDiffusionIntegrator : public BilinearFormIntegrator
{
protected:
Coefficient *Q = NULL;
VectorCoefficient *VQ = NULL;
MatrixCoefficient *MQ = NULL;
int vdim = -1;
DenseMatrix dshape, dshapedxt, pelmat;
DenseMatrix mcoeff;
Vector vcoeff;
protected:
Coefficient *Q = nullptr;
VectorCoefficient *VQ = nullptr;
MatrixCoefficient *MQ = nullptr;
// PA extension
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, sdim, ne, dofs1D, quad1D;
int ne, dim, sdim, dofs1D, quad1D, coeff_vdim;
Vector pa_data;
private:
DenseMatrix dshape, dshapedxt, pelmat;
int vdim = -1;
DenseMatrix mcoeff;
Vector vcoeff;
public:
VectorDiffusionIntegrator(const IntegrationRule *ir = nullptr);
@@ -3189,6 +3196,7 @@ public:
void AssembleElementVector(const FiniteElement &el,
ElementTransformation &Tr,
const Vector &elfun, Vector &elvect) override;
using BilinearFormIntegrator::AssemblePA;
void AssemblePA(const FiniteElementSpace &fes) override;
void AssembleMF(const FiniteElementSpace &fes) override;
@@ -3198,13 +3206,11 @@ public:
void AddMultMF(const Vector &x, Vector &y) const override;
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
/// arguments: ne, B, G, Bt, Gt, pa_data, x, y, d1d, q1d, vdim
using ApplyKernelType = void (*)(const int, 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, const int);
/// arguments: ne, coeff_vdim, B, G, pa_data, x, y, d1d, q1d, vdim
using ApplyKernelType = void (*)(const int, const int,
const Array<real_t> &, const Array<real_t> &,
const Vector &, const Vector &, Vector &,
const int, const int, const int);
/// arguments: dim, vdim, d1d, q1d
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int, int));
@@ -3215,10 +3221,7 @@ public:
ApplyPAKernels::Specialization<DIM, VDIM, D1D, Q1D>::Add();
}
struct Kernels
{
Kernels();
};
// struct Kernels { Kernels(); };
};
/** Integrator for the linear elasticity form:
+9 -5
View File
@@ -207,7 +207,8 @@ void PLBound::Setup(const int nb_i, const int ncp_i,
}
}
PLBound::PLBound(FiniteElementSpace *fes, int ncp_i, int cp_type_i)
PLBound::PLBound(const FiniteElementSpace *fes, const int ncp_i,
const int cp_type_i)
{
MFEM_VERIFY(!fes->IsVariableOrder(),
"Variable order meshes not yet supported.");
@@ -264,7 +265,8 @@ PLBound::PLBound(FiniteElementSpace *fes, int ncp_i, int cp_type_i)
Setup(nb, ncp, b_type, cp_type, tol);
}
void PLBound::Get1DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
void PLBound::Get1DBounds(const Vector &coeff, Vector &intmin,
Vector &intmax) const
{
real_t x,w;
intmin.SetSize(ncp);
@@ -346,7 +348,8 @@ void PLBound::Get1DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
}
}
void PLBound::Get2DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
void PLBound::Get2DBounds(const Vector &coeff, Vector &intmin,
Vector &intmax) const
{
intmin.SetSize(ncp*ncp);
intmax.SetSize(ncp*ncp);
@@ -482,7 +485,8 @@ void PLBound::Get2DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
}
}
void PLBound::Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
void PLBound::Get3DBounds(const Vector &coeff, Vector &intmin,
Vector &intmax) const
{
int nb2 = nb*nb,
ncp2 = ncp*ncp,
@@ -624,7 +628,7 @@ void PLBound::Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const
}
}
void PLBound::GetNDBounds(int rdim, Vector &coeff,
void PLBound::GetNDBounds(const int rdim, const Vector &coeff,
Vector &intmin, Vector &intmax) const
{
if (rdim == 1)
+9 -8
View File
@@ -9,8 +9,8 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_BOUND
#define MFEM_BOUND
#ifndef MFEM_BOUNDS
#define MFEM_BOUNDS
#include "../config/config.hpp"
#include "fespace.hpp"
@@ -89,7 +89,8 @@ public:
}
// Constructor
PLBound(FiniteElementSpace *fes, int ncp_i = -1, int cp_type_i = 0);
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
int GetMinimumPointsForGivenBases(int nb_i, int b_type_i,
@@ -105,7 +106,7 @@ public:
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 1D/2D/3D.
void GetNDBounds(int rdim, Vector &coeff,
void GetNDBounds(const int rdim, const Vector &coeff,
Vector &intmin, Vector &intmax) const;
/// Get number of control points used to compute the bounds.
@@ -113,15 +114,15 @@ public:
private:
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 1D.
void Get1DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
void Get1DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 2D.
void Get2DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
void Get2DBounds(const Vector &coeff, Vector &intmin, Vector &intmax) const;
/// Compute piecewise linear bounds for the lexicographically-ordered
/// coefficients in @a coeff in 3D.
void Get3DBounds(Vector &coeff, Vector &intmin, Vector &intmax) const;
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.
@@ -133,4 +134,4 @@ private:
} // namespace mfem
#endif // MFEM_BOUND
#endif // MFEM_BOUNDS
+76 -3
View File
@@ -1085,6 +1085,29 @@ void SumCoefficient::SetTime(real_t t)
this->Coefficient::SetTime(t);
}
void SumCoefficient::Project(QuadratureFunction &qf)
{
if (a == nullptr)
{
// qf = alpha*aConst + beta * b
const real_t d_alpha_a = aConst*alpha;
const real_t d_beta = beta;
b->Project(qf);
auto d_qf = qf.ReadWrite();
mfem::forall(qf.Size(), [=] MFEM_HOST_DEVICE (int i)
{
d_qf[i] = d_alpha_a + d_beta*d_qf[i];
});
}
else
{
a->Project(qf);
QuadratureFunction qf_b(*qf.GetSpace());
b->Project(qf_b);
add(alpha, qf, beta, qf_b, qf);
}
}
void ProductCoefficient::SetTime(real_t t)
{
if (a) { a->SetTime(t); }
@@ -1092,6 +1115,23 @@ void ProductCoefficient::SetTime(real_t t)
this->Coefficient::SetTime(t);
}
void ProductCoefficient::Project(QuadratureFunction &qf)
{
if (a == nullptr)
{
// qf = aConst * b
b->Project(qf);
qf *= aConst;
}
else
{
a->Project(qf);
QuadratureFunction qf_b(qf.GetSpace());
b->Project(qf_b);
qf *= qf_b;
}
}
void RatioCoefficient::SetTime(real_t t)
{
if (a) { a->SetTime(t); }
@@ -1099,6 +1139,38 @@ void RatioCoefficient::SetTime(real_t t)
this->Coefficient::SetTime(t);
}
void RatioCoefficient::Project(QuadratureFunction &qf)
{
if (b == nullptr)
{
if (a == nullptr)
{
qf = aConst / bConst;
}
else
{
a->Project(qf);
qf *= 1.0/bConst;
}
}
else
{
if (a == nullptr)
{
b->Project(qf);
qf.Reciprocal();
qf *= aConst;
}
else
{
a->Project(qf);
QuadratureFunction qf_b(qf.GetSpace());
b->Project(qf_b);
qf /= qf_b;
}
}
}
void PowerCoefficient::SetTime(real_t t)
{
if (a) { a->SetTime(t); }
@@ -1955,7 +2027,7 @@ void CoefficientVector::Project(MatrixCoefficient &coeff, bool transpose)
{
if (auto *const_coeff = dynamic_cast<MatrixConstantCoefficient*>(&coeff))
{
SetConstant(const_coeff->GetMatrix());
SetConstant(const_coeff->GetMatrix(), transpose);
}
else if (auto *const_sym_coeff =
dynamic_cast<SymmetricMatrixConstantCoefficient*>(&coeff))
@@ -2016,7 +2088,7 @@ void CoefficientVector::SetConstant(const Vector &constant)
}
}
void CoefficientVector::SetConstant(const DenseMatrix &constant)
void CoefficientVector::SetConstant(const DenseMatrix &constant, bool transpose)
{
const int nq = (storage & CoefficientStorage::CONSTANTS) ? 1 : qs.GetSize();
const int width = constant.Width();
@@ -2029,7 +2101,8 @@ void CoefficientVector::SetConstant(const DenseMatrix &constant)
{
for (int i = 0; i < height; ++i)
{
(*this)[i + j*height + iq*vdim] = constant(i, j);
const real_t val = transpose ? constant(j,i) : constant(i,j);
(*this)[i + j*height + iq*vdim] = val;
}
}
}
+10 -1
View File
@@ -1456,6 +1456,9 @@ public:
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
/// @copydoc Coefficient::Project(QuadratureFunction &)
void Project(QuadratureFunction &qf) override;
/// Reset the first term in the linear combination as a constant
void SetAConst(real_t A) { a = NULL; aConst = A; }
/// Return the first term in the linear combination
@@ -1637,6 +1640,9 @@ public:
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
/// @copydoc Coefficient::Project(QuadratureFunction &)
void Project(QuadratureFunction &qf) override;
/// Reset the first term in the product as a constant
void SetAConst(real_t A) { a = NULL; aConst = A; }
/// Return the first term in the product
@@ -1685,6 +1691,9 @@ public:
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
/// @copydoc Coefficient::Project(QuadratureFunction &)
void Project(QuadratureFunction &qf) override;
/// Reset the numerator in the ratio as a constant
void SetAConst(real_t A) { a = NULL; aConst = A; }
/// Return the numerator of the ratio
@@ -2511,7 +2520,7 @@ public:
void SetConstant(const Vector &constant);
/// Set this vector to the given constant matrix.
void SetConstant(const DenseMatrix &constant);
void SetConstant(const DenseMatrix &constant, bool transpose=false);
/// Set this vector to the given constant symmetric matrix.
void SetConstant(const DenseSymmetricMatrix &constant);
+281 -8
View File
@@ -11,14 +11,15 @@
#include "complex_fem.hpp"
#include "../general/forall.hpp"
#include "../general/text.hpp"
using namespace std;
namespace mfem
{
ComplexGridFunction::ComplexGridFunction(FiniteElementSpace *fes)
: Vector(2*(fes->GetVSize()))
ComplexGridFunction::ComplexGridFunction(FiniteElementSpace *f)
: Vector(2*(f->GetVSize())), fes(f), fec_owned(NULL)
{
UseDevice(true);
this->Vector::operator=(0.0);
@@ -28,12 +29,88 @@ ComplexGridFunction::ComplexGridFunction(FiniteElementSpace *fes)
gfi = new GridFunction();
gfi->MakeRef(fes, *this, fes->GetVSize());
fes_sequence = fes->GetSequence();
}
ComplexGridFunction::ComplexGridFunction(Mesh *m, std::istream &input)
: Vector(), fes(NULL), fec_owned(NULL)
{
string buff;
// Grid functions are stored on the device
UseDevice(true);
input >> std::ws;
getline(input, buff); // 'ComplexGridFunction'
filter_dos(buff);
if (buff != "ComplexGridFunction")
{
MFEM_ABORT("unrecognized file header: " << buff);
}
fes = new FiniteElementSpace;
fec_owned = fes->Load(m, input);
skip_comment_lines(input, '#');
istream::int_type next_char = input.peek();
if (next_char == 'N') // First letter of "NURBS_patches"
{
getline(input, buff);
filter_dos(buff);
if (buff == "NURBS_patches")
{
MFEM_ABORT("NURBS not yet supported with ComplexGridFunction objects");
}
else
{
MFEM_ABORT("unknown section: " << buff);
}
}
else
{
Vector::Load(input, 2*fes->GetVSize());
// if the mesh is a legacy (v1.1) NC mesh, it has old vertex ordering
if (fes->Nonconforming() &&
fes->GetMesh()->ncmesh->IsLegacyLoaded())
{
// LegacyNCReorder();
MFEM_ABORT("LegacyNCReorder not supported for "
"ComplexGridFunction objects");
}
}
gfr = new GridFunction();
gfr->MakeRef(fes, *this, 0);
gfi = new GridFunction();
gfi->MakeRef(fes, *this, fes->GetVSize());
fes_sequence = fes->GetSequence();
}
void ComplexGridFunction::Destroy()
{
delete gfr; delete gfi;
if (fec_owned)
{
delete fes;
delete fec_owned;
fec_owned = NULL;
}
}
void
ComplexGridFunction::Update()
{
FiniteElementSpace *fes = gfr->FESpace();
if (fes->GetSequence() == fes_sequence)
{
return; // space and grid function are in sync, no-op
}
fes_sequence = fes->GetSequence();
const int vsize = fes->GetVSize();
const Operator *T = fes->GetUpdateOperator();
@@ -84,6 +161,17 @@ ComplexGridFunction::Update()
}
}
int ComplexGridFunction::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());
}
void
ComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff)
@@ -149,6 +237,35 @@ ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
gfi->SyncAliasMemory(*this);
}
void ComplexGridFunction::Save(std::ostream &os) const
{
os << "ComplexGridFunction\n";
fes->Save(os);
os << '\n';
if (fes->GetOrdering() == Ordering::byNODES)
{
Vector::Print(os, 1);
}
else
{
Vector::Print(os, fes->GetVDim());
}
os.flush();
}
void ComplexGridFunction::Save(const char *fname, int precision) const
{
ofstream ofs(fname);
ofs.precision(precision);
Save(ofs);
}
std::ostream &operator<<(std::ostream &os, const ComplexGridFunction &sol)
{
sol.Save(os);
return os;
}
ComplexLinearForm::ComplexLinearForm(FiniteElementSpace *fes,
ComplexOperator::Convention convention)
@@ -654,8 +771,8 @@ SesquilinearForm::Update(FiniteElementSpace *nfes)
#ifdef MFEM_USE_MPI
ParComplexGridFunction::ParComplexGridFunction(ParFiniteElementSpace *pfes)
: Vector(2*(pfes->GetVSize()))
ParComplexGridFunction::ParComplexGridFunction(ParFiniteElementSpace *pf)
: Vector(2*(pf->GetVSize())), pfes(pf), fec_owned(NULL)
{
UseDevice(true);
this->Vector::operator=(0.0);
@@ -665,12 +782,105 @@ ParComplexGridFunction::ParComplexGridFunction(ParFiniteElementSpace *pfes)
pgfi = new ParGridFunction();
pgfi->MakeRef(pfes, *this, pfes->GetVSize());
fes_sequence = pfes->GetSequence();
}
ParComplexGridFunction::ParComplexGridFunction(ParMesh *m, std::istream &input)
: Vector(), pfes(NULL), fec_owned(NULL)
{
string buff;
// Grid functions are stored on the device
UseDevice(true);
input >> std::ws;
getline(input, buff); // 'ParComplexGridFunction'
filter_dos(buff);
if (buff != "ParComplexGridFunction")
{
MFEM_ABORT("unrecognized file header: " << buff);
}
FiniteElementSpace *fes = new FiniteElementSpace;
fec_owned = fes->Load(m, input);
pfes = new ParFiniteElementSpace(m, fec_owned, fes->GetVDim(),
fes->GetOrdering());
delete fes;
skip_comment_lines(input, '#');
istream::int_type next_char = input.peek();
if (next_char == 'N') // First letter of "NURBS_patches"
{
getline(input, buff);
filter_dos(buff);
if (buff == "NURBS_patches")
{
MFEM_ABORT("NURBS not yet supported with ComplexGridFunction objects");
}
else
{
MFEM_ABORT("unknown section: " << buff);
}
}
else
{
int vsize = pfes->GetVSize();
Vector::Load(input, 2*vsize);
real_t *data_ = const_cast<real_t*>(HostRead());
for (int i = 0; i < vsize; i++)
{
if (pfes->GetDofSign(i) < 0)
{
data_[i] = -data_[i];
data_[i+vsize] = -data_[i+vsize];
}
}
// if the mesh is a legacy (v1.1) NC mesh, it has old vertex ordering
if (pfes->Nonconforming() &&
pfes->GetMesh()->ncmesh->IsLegacyLoaded())
{
// LegacyNCReorder();
MFEM_ABORT("LegacyNCReorder not supported for "
"ComplexGridFunction objects");
}
}
pgfr = new ParGridFunction();
pgfr->MakeRef(pfes, *this, 0);
pgfi = new ParGridFunction();
pgfi->MakeRef(pfes, *this, pfes->GetVSize());
fes_sequence = pfes->GetSequence();
}
void ParComplexGridFunction::Destroy()
{
delete pgfr; delete pgfi;
if (fec_owned)
{
delete pfes;
delete fec_owned;
fec_owned = NULL;
}
}
void
ParComplexGridFunction::Update()
{
ParFiniteElementSpace *pfes = pgfr->ParFESpace();
if (pfes->GetSequence() == fes_sequence)
{
return; // space and grid function are in sync, no-op
}
fes_sequence = pfes->GetSequence();
const int vsize = pfes->GetVSize();
const Operator *T = pfes->GetUpdateOperator();
@@ -719,6 +929,17 @@ ParComplexGridFunction::Update()
}
}
int ParComplexGridFunction::VectorDim() const
{
const FiniteElement *fe = pfes->GetTypicalFE();
if (!fe || fe->GetRangeType() == FiniteElement::SCALAR)
{
return pfes->GetVDim();
}
return pfes->GetVDim()*std::max(pfes->GetMesh()->SpaceDimension(),
fe->GetRangeDim());
}
void
ParComplexGridFunction::ProjectCoefficient(Coefficient &real_coeff,
Coefficient &imag_coeff)
@@ -789,7 +1010,6 @@ ParComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
void
ParComplexGridFunction::Distribute(const Vector *tv)
{
ParFiniteElementSpace *pfes = pgfr->ParFESpace();
const int tvsize = pfes->GetTrueVSize();
tv->Read();
@@ -807,7 +1027,6 @@ ParComplexGridFunction::Distribute(const Vector *tv)
void
ParComplexGridFunction::ParallelProject(Vector &tv) const
{
ParFiniteElementSpace *pfes = pgfr->ParFESpace();
const int tvsize = pfes->GetTrueVSize();
tv.Write();
@@ -825,6 +1044,60 @@ ParComplexGridFunction::ParallelProject(Vector &tv) const
tvi.SyncAliasMemory(tv);
}
void ParComplexGridFunction::Save(std::ostream &os) const
{
os << "ParComplexGridFunction\n";
pfes->Save(os);
os << '\n';
int vsize = pfes->GetVSize();
real_t *data_ = const_cast<real_t*>(HostRead());
for (int i = 0; i < vsize; i++)
{
if (pfes->GetDofSign(i) < 0)
{
data_[i] = -data_[i];
data_[i+vsize] = -data_[i+vsize];
}
}
if (pfes->GetOrdering() == Ordering::byNODES)
{
Vector::Print(os, 1);
}
else
{
Vector::Print(os, pfes->GetVDim());
}
for (int i = 0; i < vsize; i++)
{
if (pfes->GetDofSign(i) < 0)
{
data_[i] = -data_[i];
data_[i+vsize] = -data_[i+vsize];
}
}
os.flush();
}
void ParComplexGridFunction::Save(const char *fname, int precision) const
{
int rank = pfes->GetMyRank();
ostringstream fname_with_suffix;
fname_with_suffix << fname << "." << setfill('0') << setw(6) << rank;
ofstream ofs(fname_with_suffix.str().c_str());
ofs.precision(precision);
Save(ofs);
}
std::ostream &operator<<(std::ostream &os, const ParComplexGridFunction &sol)
{
sol.Save(os);
return os;
}
ParComplexLinearForm::ParComplexLinearForm(ParFiniteElementSpace *pfes,
ComplexOperator::Convention
+159 -16
View File
@@ -35,15 +35,53 @@ private:
GridFunction * gfi;
protected:
void Destroy() { delete gfr; delete gfi; }
/// FE space on which the grid function lives. Owned if #fec_owned
/// is not NULL.
FiniteElementSpace *fes;
/** @brief Used when the grid function is read from a file. It can also be
set explicitly, see MakeOwner().
If not NULL, this pointer is owned by the ComplexGridFunction. */
FiniteElementCollection *fec_owned;
long fes_sequence; // see FiniteElementSpace::sequence, Mesh::sequence
void Destroy();
public:
/** @brief Construct a ComplexGridFunction associated with the
FiniteElementSpace @a *f. */
ComplexGridFunction(FiniteElementSpace *f);
/** @brief Construct a ComplexGridFunction on the given Mesh, using the data
from @a input.
The content of @a input should be in the format created by the method
Save(). The reconstructed FiniteElementSpace and FiniteElementCollection
are owned by the ComplexGridFunction. */
ComplexGridFunction(Mesh *m, std::istream &input);
void Update();
/** Return update counter, similar to Mesh::GetSequence(). Used to
check if it is up to date with the space. */
long GetSequence() const { return fes_sequence; }
/// Make the ComplexGridFunction the owner of #fec_owned and #fes.
/** If the new FiniteElementCollection, @a fec_, is NULL, ownership
of #fec_owned and #fes is taken away. */
void MakeOwner(FiniteElementCollection *fec_) { fec_owned = fec_; }
/// Returns a pointer to the FiniteElementCollection used to
/// construct this ComplexGridFunction if this class owns that
/// object. Otherwise this function will return NULL.
FiniteElementCollection *OwnFEC() { return fec_owned; }
/// Shortcut for calling FiniteElementSpace::GetVectorDim() on the
/// underlying #fes
int VectorDim() const;
/// Assign constant values to the ComplexGridFunction data.
ComplexGridFunction &operator=(const std::complex<real_t> & value)
{ *gfr = value.real(); *gfi = value.imag(); return *this; }
@@ -63,8 +101,8 @@ public:
VectorCoefficient &imag_coeff,
Array<int> &attr);
FiniteElementSpace *FESpace() { return gfr->FESpace(); }
const FiniteElementSpace *FESpace() const { return gfr->FESpace(); }
FiniteElementSpace *FESpace() { return fes; }
const FiniteElementSpace *FESpace() const { return fes; }
GridFunction & real() { return *gfr; }
GridFunction & imag() { return *gfi; }
@@ -79,11 +117,52 @@ public:
/// @a gfr and @a gfi to match the ComplexGridFunction.
void SyncAlias() { gfr->SyncAliasMemory(*this); gfi->SyncAliasMemory(*this); }
/// @brief Returns ||u_ex - u_h||_L2 for complex-valued scalar fields
///
/// @see GridFunction::ComputeL2Error(Coefficient &exsol,
/// const IntegrationRule *irs[],
/// const Array<int> *elems) const
/// for more detailed documentation.
virtual real_t ComputeL2Error(Coefficient &exsolr, Coefficient &exsoli,
const IntegrationRule *irs[] = NULL) const
{
real_t err_r = gfr->ComputeL2Error(exsolr, irs);
real_t err_i = gfi->ComputeL2Error(exsoli, irs);
return sqrt(err_r * err_r + err_i * err_i);
}
/// @brief Returns ||u_ex - u_h||_L2 for complex-valued vector fields
///
/// @see GridFunction::ComputeL2Error(VectorCoefficient &exsol,
/// const IntegrationRule *irs[],
/// const Array<int> *elems) const
/// for more detailed documentation.
virtual real_t ComputeL2Error(VectorCoefficient &exsolr,
VectorCoefficient &exsoli,
const IntegrationRule *irs[] = NULL,
Array<int> *elems = NULL) const
{
real_t err_r = gfr->ComputeL2Error(exsolr, irs, elems);
real_t err_i = gfi->ComputeL2Error(exsoli, irs, elems);
return sqrt(err_r * err_r + err_i * err_i);
}
/// Save the ComplexGridFunction to an output stream.
virtual void Save(std::ostream &out) const;
/// Save the ComplexGridFunction to a file
/** The given @a precision will be used for ASCII output. */
virtual void Save(const char *fname, int precision=16) const;
/// Destroys the grid function.
virtual ~ComplexGridFunction() { Destroy(); }
};
/** Overload operator<< for std::ostream and ComplexGridFunction; not valid
for the class ParComplexGridFunction */
std::ostream &operator<<(std::ostream &out, const ComplexGridFunction &sol);
/** Class for a complex-valued linear form
The @a convention argument in the class's constructor is documented in the
@@ -345,12 +424,23 @@ public:
class ParComplexGridFunction : public Vector
{
private:
ParGridFunction * pgfr;
ParGridFunction * pgfi;
protected:
void Destroy() { delete pgfr; delete pgfi; }
/// FE space on which the grid function lives. Owned if #fec_owned
/// is not NULL.
ParFiniteElementSpace *pfes;
/** @brief Used when the grid function is read from a file. It can also be
set explicitly, see MakeOwner().
If not NULL, this pointer is owned by the ParComplexGridFunction. */
FiniteElementCollection *fec_owned;
long fes_sequence; // see FiniteElementSpace::sequence, Mesh::sequence
void Destroy();
public:
@@ -358,8 +448,33 @@ public:
ParFiniteElementSpace @a *pf. */
ParComplexGridFunction(ParFiniteElementSpace *pf);
/** @brief Construct a ParComplexGridFunction on a given ParMesh,
@a pmesh, reading from an std::istream.
In the process, a ParFiniteElementSpace and a FiniteElementCollection are
constructed. The new ParComplexGridFunction assumes ownership of both. */
ParComplexGridFunction(ParMesh *pmesh, std::istream &input);
void Update();
/** Return update counter, similar to Mesh::GetSequence(). Used to
check if it is up to date with the space. */
long GetSequence() const { return fes_sequence; }
/// Make the ParComplexGridFunction the owner of #fec_owned and #pfes.
/** If the new FiniteElementCollection, @a fec_, is NULL, ownership
of #fec_owned and #pfes is taken away. */
void MakeOwner(FiniteElementCollection *fec_) { fec_owned = fec_; }
/// Returns a pointer to the FiniteElementCollection used to
/// construct this ParComplexGridFunction if this class owns that
/// object. Otherwise this function will return NULL.
FiniteElementCollection *OwnFEC() { return fec_owned; }
/// Shortcut for calling FiniteElementSpace::GetVectorDim() on the
/// underlying #pfes
int VectorDim() const;
/// Assign constant values to the ParComplexGridFunction data.
ParComplexGridFunction &operator=(const std::complex<real_t> & value)
{ *pgfr = value.real(); *pgfi = value.imag(); return *this; }
@@ -385,11 +500,11 @@ public:
/// Returns the vector restricted to the true dofs.
void ParallelProject(Vector &tv) const;
FiniteElementSpace *FESpace() { return pgfr->FESpace(); }
const FiniteElementSpace *FESpace() const { return pgfr->FESpace(); }
FiniteElementSpace *FESpace() { return pfes; }
const FiniteElementSpace *FESpace() const { return pfes; }
ParFiniteElementSpace *ParFESpace() { return pgfr->ParFESpace(); }
const ParFiniteElementSpace *ParFESpace() const { return pgfr->ParFESpace(); }
ParFiniteElementSpace *ParFESpace() { return pfes; }
const ParFiniteElementSpace *ParFESpace() const { return pfes; }
ParGridFunction & real() { return *pgfr; }
ParGridFunction & imag() { return *pgfi; }
@@ -402,17 +517,32 @@ public:
/// Update the alias memory location of the real and imaginary
/// ParGridFunction @a pgfr and @a pgfi to match the ParComplexGridFunction.
void SyncAlias() { pgfr->SyncAliasMemory(*this); pgfi->SyncAliasMemory(*this); }
void SyncAlias()
{ pgfr->SyncAliasMemory(*this); pgfi->SyncAliasMemory(*this); }
/// @brief Returns ||u_ex - u_h||_L2 in parallel for complex-valued
/// scalar fields
///
/// @see GridFunction::ComputeL2Error(Coefficient &exsol,
/// const IntegrationRule *irs[],
/// const Array<int> *elems) const
/// for more detailed documentation.
virtual real_t ComputeL2Error(Coefficient &exsolr, Coefficient &exsoli,
const IntegrationRule *irs[] = NULL) const
const IntegrationRule *irs[] = NULL,
Array<int> *elems = NULL) const
{
real_t err_r = pgfr->ComputeL2Error(exsolr, irs);
real_t err_i = pgfi->ComputeL2Error(exsoli, irs);
return sqrt(err_r * err_r + err_i * err_i);
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
real_t err_i = pgfi->ComputeL2Error(exsoli, irs, elems);
return hypot(err_r, err_i);
}
/// @brief Returns ||u_ex - u_h||_L2 in parallel for complex-valued
/// vector fields
///
/// @see GridFunction::ComputeL2Error(VectorCoefficient &exsol,
/// const IntegrationRule *irs[],
/// const Array<int> *elems) const
/// for more detailed documentation.
virtual real_t ComputeL2Error(VectorCoefficient &exsolr,
VectorCoefficient &exsoli,
const IntegrationRule *irs[] = NULL,
@@ -420,15 +550,28 @@ public:
{
real_t err_r = pgfr->ComputeL2Error(exsolr, irs, elems);
real_t err_i = pgfi->ComputeL2Error(exsoli, irs, elems);
return sqrt(err_r * err_r + err_i * err_i);
return hypot(err_r, err_i);
}
/// Save the local portion of the ParComplexGridFunction
/** This differs from the serial ComplexGridFunction::Save in that it
takes into account the signs of the local dofs. */
void Save(std::ostream &out) const;
/// Save the ParComplexGridFunction to files
/** Saves one file for each MPI rank. The files will be given suffixes
according to the MPI rank. The given @a precision will be used for ASCII
output. */
void Save(const char *fname, int precision=16) const;
/// Destroys grid function.
virtual ~ParComplexGridFunction() { Destroy(); }
};
/** Overload operator<< for std::ostream and ParComplexGridFunction */
std::ostream &operator<<(std::ostream &out, const ParComplexGridFunction &sol);
/** Class for a complex-valued, parallel linear form
The @a convention argument in the class's constructor is documented in the
+269 -47
View File
@@ -70,8 +70,8 @@ ConduitDataCollection::~ConduitDataCollection()
void ConduitDataCollection::Save()
{
std::string dir_name = MeshDirectoryName();
int err = create_directory(dir_name, mesh, myid);
if (err)
int err_ = create_directory(dir_name, mesh, myid);
if (err_)
{
MFEM_ABORT("Error creating directory: " << dir_name);
}
@@ -88,6 +88,7 @@ void ConduitDataCollection::Save()
<< verify_info.to_json());
}
// wrap all grid functions
FieldMapConstIterator itr;
for ( itr = field_map.begin(); itr != field_map.end(); itr++)
{
@@ -103,6 +104,16 @@ void ConduitDataCollection::Save()
}
}
// wrap all quadrature functions
QFieldMapConstIterator qf_itr;
for ( qf_itr = q_field_map.begin(); qf_itr != q_field_map.end(); qf_itr++)
{
std::string name = qf_itr->first;
QuadratureFunction *qf = qf_itr->second;
QuadratureFunctionToBlueprintField(qf,
n_mesh["fields"][name]);
}
// save mesh data
SaveMeshAndFields(myid,
n_mesh,
@@ -157,6 +168,16 @@ ConduitDataCollection::SetProtocol(const std::string &protocol)
relay_protocol = protocol;
}
// Conduit data type id for the MFEM precision
constexpr conduit::index_t mfem_precision_conduit_id =
#if defined(MFEM_USE_DOUBLE)
CONDUIT_NATIVE_DOUBLE_ID;
#elif defined(MFEM_USE_SINGLE)
CONDUIT_NATIVE_FLOAT_ID;
#else
#error Unknown MFEM precision
#endif
//------------------------------
// begin static public methods
//------------------------------
@@ -206,42 +227,41 @@ ConduitDataCollection::BlueprintMeshToMesh(const Node &n_mesh,
// get the number of points
int num_verts = n_coordset_vals[0].dtype().number_of_elements();
// get vals for points
const double *verts_ptr = NULL;
const real_t *verts_ptr = NULL;
// the mfem mesh constructor needs coords with interleaved (aos) type
// ordering, even for 1d + 2d we always need 3 doubles b/c it uses
// Array<Vertex> and Vertex is a pod of 3 doubles. we check for this
// ordering, even for 1d + 2d we always need 3 real_t (double/float) b/c it
// uses Array<Vertex> and Vertex is a pod of 3 real_t. we check for this
// case, if we don't have it we convert the data
if (ndims == 3 &&
n_coordset_vals[0].dtype().is_double() &&
n_coordset_vals[0].dtype().id() == mfem_precision_conduit_id &&
blueprint::mcarray::is_interleaved(n_coordset_vals) )
{
// already interleaved mcarray of 3 doubles,
// already interleaved mcarray of 3 real_t (double/float),
// return ptr to beginning
verts_ptr = n_coordset_vals[0].value();
}
else
{
Node n_tmp;
// check all vals, if we don't have doubles convert
// to doubles
// check all vals, if we don't have real_t (double/float) convert
// to real_t
NodeConstIterator itr = n_coordset_vals.children();
while (itr.has_next())
{
const Node &c_vals = itr.next();
std::string c_name = itr.name();
if ( c_vals.dtype().is_double() )
if ( c_vals.dtype().id() == mfem_precision_conduit_id )
{
// zero copy current coords
n_tmp[c_name].set_external(c_vals);
}
else
{
// convert
c_vals.to_double_array(n_tmp[c_name]);
c_vals.to_data_type(mfem_precision_conduit_id, n_tmp[c_name]);
}
}
@@ -250,13 +270,13 @@ ConduitDataCollection::BlueprintMeshToMesh(const Node &n_mesh,
if (ndims < 3)
{
// add dummy z
n_tmp["z"].set(DataType::c_double(num_verts));
n_tmp["z"].set(DataType(mfem_precision_conduit_id, num_verts));
}
if (ndims < 2)
{
// add dummy y
n_tmp["y"].set(DataType::c_double(num_verts));
n_tmp["y"].set(DataType(mfem_precision_conduit_id, num_verts));
}
Node &n_conv_coords_vals = n_conv["coordsets"][coords_name]["values"];
@@ -452,7 +472,7 @@ ConduitDataCollection::BlueprintMeshToMesh(const Node &n_mesh,
// if nodes gf is attached later, it resets the space dim based
// on the gf's fes.
Mesh *mesh = new Mesh(// from coordset
const_cast<double*>(verts_ptr),
const_cast<real_t*>(verts_ptr),
num_verts,
// from topology
const_cast<int*>(elem_indices),
@@ -519,7 +539,7 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
// can't return a gf that zero copies the conduit data
Node n_conv;
const double *vals_ptr = NULL;
const real_t *vals_ptr = NULL;
int vdim = 1;
@@ -529,10 +549,10 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
{
vdim = n_field["values"].number_of_children();
// need to check that we have doubles and
// need to check that we have real_t (double/float) and
// cover supported layouts
if ( n_field["values"][0].dtype().is_double() )
if ( n_field["values"][0].dtype().id() == mfem_precision_conduit_id )
{
// check for contig
if (n_field["values"].is_contiguous())
@@ -556,27 +576,26 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
vals_ptr = n_conv["values"].child(0).value();
}
}
else // convert to doubles and use contig
else // convert to real_t (double/float) and use contig
{
Node n_tmp;
// check all vals, if we don't have doubles convert
// to doubles
// check all vals, if we don't have real_t (double/float) convert
// to real_t
NodeConstIterator itr = n_field["values"].children();
while (itr.has_next())
{
const Node &c_vals = itr.next();
std::string c_name = itr.name();
if ( c_vals.dtype().is_double() )
if ( c_vals.dtype().id() == mfem_precision_conduit_id )
{
// zero copy current coords
n_tmp[c_name].set_external(c_vals);
}
else
{
// convert
c_vals.to_double_array(n_tmp[c_name]);
c_vals.to_data_type(mfem_precision_conduit_id, n_tmp[c_name]);
}
}
@@ -589,14 +608,15 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
}
else
{
if (n_field["values"].dtype().is_double() &&
if (n_field["values"].dtype().id() == mfem_precision_conduit_id &&
n_field["values"].is_compact())
{
vals_ptr = n_field["values"].value();
}
else
{
n_field["values"].to_double_array(n_conv["values"]);
n_field["values"].to_data_type(mfem_precision_conduit_id,
n_conv["values"]);
vals_ptr = n_conv["values"].value();
}
}
@@ -620,14 +640,14 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
if (zero_copy)
{
res = new GridFunction(fes,const_cast<double*>(vals_ptr));
res = new GridFunction(fes,const_cast<real_t*>(vals_ptr));
}
else
{
// copy case, this constructor will alloc the space for the GF data
res = new GridFunction(fes);
// create an mfem vector that wraps the conduit data
Vector vals_vec(const_cast<double*>(vals_ptr),fes->GetVSize());
Vector vals_vec(const_cast<real_t*>(vals_ptr),fes->GetVSize());
// copy values into the result
(*res) = vals_vec;
}
@@ -639,6 +659,155 @@ ConduitDataCollection::BlueprintFieldToGridFunction(Mesh *mesh,
return res;
}
//---------------------------------------------------------------------------//
mfem::QuadratureFunction *
ConduitDataCollection::BlueprintFieldToQuadratureFunction(Mesh *mesh,
const Node &n_field,
bool zero_copy)
{
// n_conv holds converted data (when necessary for mfem api)
// if n_conv is used ( !n_conv.dtype().empty() ) we
// know that some data allocation was necessary, so we
// can't return a qf that zero copies the conduit data
Node n_conv;
const real_t *vals_ptr = NULL;
int vdim = 1;
if (n_field["values"].dtype().is_object())
{
vdim = n_field["values"].number_of_children();
// need to check that we have real_t (double/float) and
// cover supported layouts
if ( n_field["values"][0].dtype().id() == mfem_precision_conduit_id )
{
// quad funcs use what mfem calls byVDIM
// and what conduit calls interleaved
// check for interleaved
if (blueprint::mcarray::is_interleaved(n_field["values"]))
{
// conduit mcarray interleaved == mfem byVDIM
vals_ptr = n_field["values"].child(0).value();
}
else
{
// for mcarray generic case -- default to byVDIM
// aka interleaved
blueprint::mcarray::to_interleaved(n_field["values"],
n_conv["values"]);
vals_ptr = n_conv["values"].child(0).value();
}
}
else // convert to real_t (double/float) and use interleaved
{
Node n_tmp;
// check all vals, if we don't have real_t (double/float) convert
// to real_t
NodeConstIterator itr = n_field["values"].children();
while (itr.has_next())
{
const Node &c_vals = itr.next();
std::string c_name = itr.name();
if ( c_vals.dtype().id() == mfem_precision_conduit_id )
{
// zero copy current coords
n_tmp[c_name].set_external(c_vals);
}
else
{
// convert
c_vals.to_data_type(mfem_precision_conduit_id, n_tmp[c_name]);
}
}
// for mcarray generic case -- default to byVDIM
// aka interleaved
blueprint::mcarray::to_interleaved(n_tmp,
n_conv["values"]);
vals_ptr = n_conv["values"].child(0).value();
}
}
else // scalar case
{
if (n_field["values"].dtype().id() == mfem_precision_conduit_id &&
n_field["values"].is_compact())
{
vals_ptr = n_field["values"].value();
}
else
{
n_field["values"].to_data_type(mfem_precision_conduit_id,
n_conv["values"]);
vals_ptr = n_conv["values"].value();
}
}
if (zero_copy && !n_conv.dtype().is_empty())
{
//Info: "Cannot zero-copy since data conversions were necessary"
zero_copy = false;
}
// we need basis name to create the proper mfem quad space and quad func
// the pattern used to encode the quad space params is:
// QF_{ORDER}_{VDIM}
// ORDER is the degree of the polynomials for the quad rule
// VDIM is the number of components at each quad point (scalar, vector, etc)
int qf_order = 0;
int qf_vdim = 0;
std::string qf_name = n_field["basis"].as_string();
const char *qf_name_cstr = qf_name.c_str();
if (!strncmp(qf_name_cstr, "QF_", 3))
{
// parse {ORDER}
qf_order = atoi(qf_name_cstr + 3);
// find second `_`
const char *qf_vdim_cstr = strstr(qf_name_cstr+3,"_");
if (qf_vdim_cstr == NULL)
{
MFEM_ABORT("Error parsing quadrature function description string: "
<< qf_name << std::endl
<< "Expected: QF_{ORDER}_{VDIM}");
}
// parse {VDIM}
qf_vdim = atoi(qf_vdim_cstr+1);
}
else
{
MFEM_ABORT("Error parsing quadrature function description string: "
<< qf_name << std::endl
<< "Expected: QF_{ORDER}_{VDIM}");
}
MFEM_VERIFY(qf_vdim == vdim, "vector dimension mismatch: vdim = " << vdim
<< ", qf_vdim = " << qf_vdim);
mfem::QuadratureSpace *quad_space = new mfem::QuadratureSpace(mesh, qf_order);
mfem::QuadratureFunction *res = new mfem::QuadratureFunction();
if (zero_copy)
{
res->SetSpace(quad_space, const_cast<real_t*>(vals_ptr), vdim);
res->SetOwnsSpace(true);
}
else
{
res->SetSpace(quad_space, vdim);
res->SetOwnsSpace(true);
// copy case, this constructor will alloc the space for the quad data
// create an mfem vector that wraps the conduit data
Vector vals_vec(const_cast<real_t*>(vals_ptr),res->Size());
// copy values into the result
(*res) = vals_vec;
}
return res;
}
//---------------------------------------------------------------------------//
void
ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
@@ -656,20 +825,20 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
// Setup main coordset
////////////////////////////////////////////
// Assumes mfem::Vertex has the layout of a double array.
// Assumes mfem::Vertex has the layout of a real_t (double/float) array.
// this logic assumes an mfem vertex is always 3 doubles wide
// this logic assumes an mfem vertex is always 3 real_t (double/float) wide
int stride = sizeof(mfem::Vertex);
int num_vertices = mesh->GetNV();
MFEM_ASSERT( ( stride == 3 * sizeof(double) ),
MFEM_ASSERT( ( stride == 3 * sizeof(real_t) ),
"Unexpected stride for Vertex");
Node &n_mesh_coords = n_mesh["coordsets"][coordset_name];
n_mesh_coords["type"] = "explicit";
double *coords_ptr = mesh->GetVertex(0);
real_t *coords_ptr = mesh->GetVertex(0);
n_mesh_coords["values/x"].set_external(coords_ptr,
num_vertices,
@@ -680,14 +849,14 @@ ConduitDataCollection::MeshToBlueprintMesh(Mesh *mesh,
{
n_mesh_coords["values/y"].set_external(coords_ptr,
num_vertices,
sizeof(double),
sizeof(real_t),
stride);
}
if (dim >= 3)
{
n_mesh_coords["values/z"].set_external(coords_ptr,
num_vertices,
sizeof(double) * 2,
sizeof(real_t) * 2,
stride);
}
@@ -942,6 +1111,59 @@ ConduitDataCollection::GridFunctionToBlueprintField(mfem::GridFunction *gf,
}
//---------------------------------------------------------------------------//
void
ConduitDataCollection::QuadratureFunctionToBlueprintField(
mfem::QuadratureFunction *qf,
Node &n_field,
const std::string &main_topology_name)
{
// For quadrature functions, use basis pattern:
// QF_{ORDER}_{VDIM}
int qf_vdim = qf->GetVDim();
int qf_order = qf->GetSpace()->GetOrder();
int qf_size = qf->GetSpace()->GetSize();
{
std::ostringstream oss;
oss << "QF_" << qf_order << "_" << qf_vdim;
n_field["basis"] = oss.str();
n_field["topology"] = main_topology_name;
}
if (qf_vdim == 1) // scalar case
{
n_field["values"].set_external(const_cast<real_t *>(qf->HostRead()),
qf_size);
}
else // vector case
{
// deal with striding of all components
// quadrature functions are always byVDIM
// or what conduit calls interleaved
index_t offset = 0;
index_t stride = sizeof(real_t) * qf_vdim;
for (int d = 0; d < qf_vdim; d++)
{
std::ostringstream oss;
oss << "v" << d;
std::string comp_name = oss.str();
n_field["values"][comp_name].set_external(const_cast<real_t *>(qf->HostRead()),
qf_size,
offset,
stride);
offset += sizeof(real_t);
}
}
}
//------------------------------
// end static public methods
//------------------------------
@@ -967,7 +1189,7 @@ ConduitDataCollection::RootFileName()
//---------------------------------------------------------------------------//
std::string
ConduitDataCollection::MeshFileName(int domain_id,
const std::string &relay_protocol)
const std::string &relay_protocol_)
{
std::string res = prefix_path +
name +
@@ -976,7 +1198,7 @@ ConduitDataCollection::MeshFileName(int domain_id,
"/domain_" +
to_padded_string(domain_id, pad_digits_rank) +
"." +
relay_protocol;
relay_protocol_;
return res;
}
@@ -994,7 +1216,7 @@ ConduitDataCollection::MeshDirectoryName()
//---------------------------------------------------------------------------//
std::string
ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol)
ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol_)
{
std::ostringstream oss;
oss << name
@@ -1003,7 +1225,7 @@ ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol)
<< "/domain_%0"
<< pad_digits_rank
<< "d."
<< relay_protocol;
<< relay_protocol_;
return oss.str();
}
@@ -1013,14 +1235,14 @@ ConduitDataCollection::MeshFilePattern(const std::string &relay_protocol)
void
ConduitDataCollection::SaveRootFile(int num_domains,
const Node &n_mesh,
const std::string &relay_protocol)
const std::string &relay_protocol_)
{
// default to json root file, except for hdf5 case
std::string root_proto = "json";
if (relay_protocol == "hdf5")
if (relay_protocol_ == "hdf5")
{
root_proto = relay_protocol;
root_proto = relay_protocol_;
}
Node n_root;
@@ -1051,14 +1273,14 @@ ConduitDataCollection::SaveRootFile(int num_domains,
}
}
// add extra header info
n_root["protocol/name"] = relay_protocol;
n_root["protocol/name"] = relay_protocol_;
n_root["protocol/version"] = "0.3.1";
// we will save one file per domain, so trees == files
n_root["number_of_files"] = num_domains;
n_root["number_of_trees"] = num_domains;
n_root["file_pattern"] = MeshFilePattern(relay_protocol);
n_root["file_pattern"] = MeshFilePattern(relay_protocol_);
n_root["tree_pattern"] = "";
// Add the time, time step, and cycle
@@ -1073,9 +1295,9 @@ ConduitDataCollection::SaveRootFile(int num_domains,
void
ConduitDataCollection::SaveMeshAndFields(int domain_id,
const Node &n_mesh,
const std::string &relay_protocol)
const std::string &relay_protocol_)
{
relay::io::save(n_mesh, MeshFileName(domain_id, relay_protocol));
relay::io::save(n_mesh, MeshFileName(domain_id, relay_protocol_));
}
//---------------------------------------------------------------------------//
@@ -1172,13 +1394,13 @@ ConduitDataCollection::LoadRootFile(Node &root_out)
//---------------------------------------------------------------------------//
void
ConduitDataCollection::LoadMeshAndFields(int domain_id,
const std::string &relay_protocol)
const std::string &relay_protocol_)
{
// Note: This path doesn't use any info from the root file
// it uses the implicit mfem ConduitDataCollection layout
Node n_mesh;
relay::io::load( MeshFileName(domain_id, relay_protocol), n_mesh);
relay::io::load( MeshFileName(domain_id, relay_protocol_), n_mesh);
Node verify_info;
+33 -7
View File
@@ -50,11 +50,11 @@ namespace mfem
Those that construct MFEM objects from Conduit Nodes (Conduit Blueprint to
MFEM) provide a zero-copy option. Zero-copy is only possible if the
blueprint data matches the data types provided by the MFEM API, for example:
ints for connectivity arrays, doubles for field value arrays, allocations
that match MFEM's striding options, etc. If these constraints are not met,
MFEM objects that own the data are created and returned. In either case
pointers to new MFEM object instances are returned, the zero-copy only
applies to data backing the MFEM object instances.
ints for connectivity arrays, real_t (double/float) for field value arrays,
allocations that match MFEM's striding options, etc. If these constraints
are not met, MFEM objects that own the data are created and returned. In
either case pointers to new MFEM object instances are returned, the
zero-copy only applies to data backing the MFEM object instances.
@note QuadratureFunction%s (q-fields) are not supported.
@@ -183,6 +183,21 @@ public:
conduit::Node &out,
const std::string &main_topology_name = "main");
/// Describes a MFEM quadrature function using the mesh blueprint
/** Sets up passed conduit::Node out to describe the given quadrature function
using the mesh field blueprint.
Zero-copies as much data as possible.
@a main_toplogy_name is used to set the associated topology name.
With the default setting, the resulting field is associated with the
topology `main`.
*/
static void QuadratureFunctionToBlueprintField(QuadratureFunction *qf,
conduit::Node &out,
const std::string &main_topology_name = "main");
/// Constructs and MFEM mesh from a Conduit Blueprint Description
/** @a main_topology_name is used to select which topology to use, when
empty ("") the first topology entry will be used.
@@ -190,7 +205,7 @@ public:
If zero_copy == true, tries to construct a mesh that points to the data
described by the conduit node. This is only possible if the data in the
node matches the data types needed for the MFEM API (ints for
connectivity, doubles for field values, etc). If these constraints are
connectivity, real_t for field values, etc). If these constraints are
not met, a mesh that owns the data is created and returned.
*/
static Mesh *BlueprintMeshToMesh(const conduit::Node &n_mesh,
@@ -200,7 +215,7 @@ public:
/// Constructs and MFEM Grid Function from a Conduit Blueprint Description
/** If zero_copy == true, tries to construct a grid function that points to
the data described by the conduit node. This is only possible if the data
in the node matches the data types needed for the MFEM API (doubles for
in the node matches the data types needed for the MFEM API (real_t for
field values, allocated in soa or aos ordering, etc). If these
constraints are not met, a grid function that owns the data is created
and returned.
@@ -208,6 +223,17 @@ public:
static GridFunction *BlueprintFieldToGridFunction(Mesh *mesh,
const conduit::Node &n_field,
bool zero_copy = false);
/// Constructs and MFEM Quadrature Function from a Conduit Blueprint Description
/** If zero_copy == true, tries to construct a quadrature function that points to
the data described by the conduit node. This is only possible if the data
in the node matches the data types needed for the MFEM API (real_t for
field values, allocated in an interleavred/byVDIM order, etc). If these
constraints are not met, a grid function that owns the data is created
and returned.
*/
static QuadratureFunction *BlueprintFieldToQuadratureFunction(Mesh *mesh,
const conduit::Node &n_field,
bool zero_copy = false);
private:
/// Converts from MFEM element type enum to mesh bp shape name
+209 -16
View File
@@ -310,9 +310,9 @@ void DataCollection::SaveField(const std::string &field_name)
}
}
void DataCollection::SaveQField(const std::string &q_field_name)
void DataCollection::SaveQField(const std::string &field_name)
{
QFieldMapIterator it = q_field_map.find(q_field_name);
QFieldMapIterator it = q_field_map.find(field_name);
if (it != q_field_map.end())
{
SaveOneQField(it);
@@ -430,7 +430,9 @@ void VisItDataCollection::RegisterField(const std::string& name,
}
DataCollection::RegisterField(name, gf);
field_info_map[name] = VisItFieldInfo("nodes", gf->VectorDim(), LOD);
field_info_map[name] = VisItFieldInfo("nodes", gf->VectorDim(), LOD,
gf->FESpace()->FEColl()->Name(),
gf->FESpace()->FEColl()->GetOrder());
visit_levels_of_detail = std::max(visit_levels_of_detail, LOD);
}
@@ -449,7 +451,14 @@ void VisItDataCollection::RegisterQField(const std::string& name,
}
DataCollection::RegisterQField(name, qf);
field_info_map[name] = VisItFieldInfo("elements", 1, LOD);
// For quadrature functions, use basis pattern:
// QF_{ORDER}_{VDIM}
int qf_vdim = qf->GetVDim();
int qf_order = qf->GetSpace()->GetOrder();
std::ostringstream oss;
oss << "QF_" << qf_order << "_" << qf_vdim;
field_info_map[name] = VisItFieldInfo("quadrature", qf->GetVDim(), LOD,
oss.str(), qf_order);
visit_levels_of_detail = std::max(visit_levels_of_detail, LOD);
}
@@ -623,7 +632,8 @@ void VisItDataCollection::LoadFields()
{
field_map.Register(it->first, new GridFunction(mesh, file), own_data);
}
else if ((it->second).association == "elements")
else if ((it->second).association == "elements" || // old style
(it->second).association == "quadrature") // new style
{
q_field_map.Register(it->first, new QuadratureFunction(mesh, file), own_data);
}
@@ -637,7 +647,8 @@ void VisItDataCollection::LoadFields()
it->first,
new ParGridFunction(dynamic_cast<ParMesh*>(mesh), file), own_data);
}
else if ((it->second).association == "elements")
else if ((it->second).association == "elements" || // old style
(it->second).association == "quadrature") // new style
{
q_field_map.Register(it->first, new QuadratureFunction(mesh, file), own_data);
}
@@ -676,6 +687,8 @@ std::string VisItDataCollection::GetVisItRootString()
ftags["assoc"] = picojson::value((it->second).association);
ftags["comps"] = picojson::value(to_string((it->second).num_components));
ftags["lod"] = picojson::value(to_string((it->second).lod));
ftags["basis"] = picojson::value((it->second).basis);
ftags["order"] = picojson::value(to_string((it->second).order));
field["path"] = picojson::value(path_str + it->first + file_ext_format);
field["tags"] = picojson::value(ftags);
fields[it->first] = picojson::value(field);
@@ -752,9 +765,31 @@ void VisItDataCollection::ParseVisItRootString(const std::string& json)
it != fields_obj.end(); ++it)
{
picojson::value tags = it->second.get("tags");
// defaults that allow us to parse older mfem_root files
int lod = 1;
std::string basis = "";
int order = -1;
if (tags.contains("lod"))
{
lod = to_int(tags.get("lod").get<std::string>());
}
if (tags.contains("basis"))
{
basis = tags.get("comps").get<std::string>();
}
if (tags.contains("order"))
{
order = to_int(tags.get("comps").get<std::string>());
}
field_info_map[it->first] =
VisItFieldInfo(tags.get("assoc").get<std::string>(),
to_int(tags.get("comps").get<std::string>()));
to_int(tags.get("comps").get<std::string>()),
lod, basis, order);
}
}
}
@@ -780,6 +815,11 @@ void ParaViewDataCollectionBase::SetHighOrderOutput(bool high_order_output_)
high_order_output = high_order_output_;
}
void ParaViewDataCollectionBase::SetBoundaryOutput(bool bdr_output_)
{
bdr_output = bdr_output_;
}
void ParaViewDataCollectionBase::SetCompressionLevel(int compression_level_)
{
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
@@ -935,16 +975,19 @@ void ParaViewDataCollection::Save()
std::string vtu_prefix = col_path + "/" + GenerateVTUPath() + "/";
// Save the local part of the mesh and grid functions fields to the local
// VTU file
// VTU file. Also save coefficient fields.
{
std::ofstream os(vtu_prefix + GenerateVTUFileName("proc", myid));
os.precision(precision);
SaveDataVTU(os, levels_of_detail);
}
// Save the local part of the quadrature function fields
// Save the local part of the quadrature function fields.
for (const auto &qfield : q_field_map)
{
MFEM_VERIFY(!bdr_output,
"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));
qfield.second->SaveVTU(os, pv_data_format, GetCompressionLevel(), field_name);
@@ -960,7 +1003,7 @@ void ParaViewDataCollection::Save()
std::ofstream pvtu_out(vtu_prefix + GeneratePVTUFileName("data"));
WritePVTUHeader(pvtu_out);
// Grid function fields
// Grid function fields and coefficient fields
pvtu_out << "<PPointData>\n";
for (auto &field_it : field_map)
{
@@ -971,7 +1014,24 @@ void ParaViewDataCollection::Save()
<< VTKComponentLabels(vec_dim) << " "
<< "format=\"" << GetDataFormatString() << "\" />\n";
}
for (auto &field_it : coeff_field_map)
{
int vec_dim = 1;
pvtu_out << "<PDataArray type=\"" << GetDataTypeString()
<< "\" Name=\"" << field_it.first
<< "\" NumberOfComponents=\"" << vec_dim << "\" "
<< "format=\"" << GetDataFormatString() << "\" />\n";
}
for (auto &field_it : vcoeff_field_map)
{
int vec_dim = field_it.second->GetVDim();
pvtu_out << "<PDataArray type=\"" << GetDataTypeString()
<< "\" Name=\"" << field_it.first
<< "\" NumberOfComponents=\"" << vec_dim << "\" "
<< "format=\"" << GetDataFormatString() << "\" />\n";
}
pvtu_out << "</PPointData>\n";
// Element attributes
pvtu_out << "<PCellData>\n";
pvtu_out << "\t<PDataArray type=\"Int32\" Name=\"" << "attribute"
@@ -1069,7 +1129,8 @@ void ParaViewDataCollection::SaveDataVTU(std::ostream &os, int ref)
}
os << " version=\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
os << "<UnstructuredGrid>\n";
mesh->PrintVTU(os,ref,pv_data_format,high_order_output,GetCompressionLevel());
mesh->PrintVTU(os,ref,pv_data_format,high_order_output,GetCompressionLevel(),
bdr_output);
// dump out the grid functions as point data
os << "<PointData >\n";
@@ -1077,8 +1138,21 @@ void ParaViewDataCollection::SaveDataVTU(std::ostream &os, int ref)
// iterate over all grid functions
for (FieldMapIterator it=field_map.begin(); it!=field_map.end(); ++it)
{
MFEM_VERIFY(!bdr_output,
"GridFunction output is not supported for "
"ParaViewDataCollection on domain boundary!");
SaveGFieldVTU(os,ref,it);
}
// save the coefficient functions
// iterate over all Coefficient and VectorCoefficient functions
for (const auto &kv : coeff_field_map)
{
SaveCoeffFieldVTU(os, ref, kv.first, *kv.second);
}
for (const auto &kv : vcoeff_field_map)
{
SaveVCoeffFieldVTU(os, ref, kv.first, *kv.second);
}
os << "</PointData>\n";
// close the mesh
os << "</Piece>\n"; // close the piece open in the PrintVTU method
@@ -1101,7 +1175,6 @@ void ParaViewDataCollection::SaveGFieldVTU(std::ostream &os, int ref_,
<< "format=\"" << GetDataFormatString() << "\" >" << '\n';
if (vec_dim == 1)
{
// scalar data
for (int i = 0; i < mesh->GetNE(); i++)
{
RefG = GlobGeometryRefiner.Refine(
@@ -1131,11 +1204,131 @@ void ParaViewDataCollection::SaveGFieldVTU(std::ostream &os, int ref_,
}
}
}
if (IsBinaryFormat())
if (pv_data_format != VTKFormat::ASCII)
{
WriteVTKEncodedCompressed(os,buf.data(),buf.size(),GetCompressionLevel());
os << '\n';
WriteBase64WithSizeAndClear(os, buf, GetCompressionLevel());
}
os << "</DataArray>" << std::endl;
}
void ParaViewDataCollection::SaveCoeffFieldVTU(std::ostream &os, int ref_,
const std::string &name, Coefficient &coeff)
{
RefinedGeometry *RefG;
real_t val;
std::vector<char> buf;
int vec_dim = 1;
os << "<DataArray type=\"" << GetDataTypeString()
<< "\" Name=\"" << name
<< "\" NumberOfComponents=\"" << vec_dim << "\""
<< " format=\"" << GetDataFormatString() << "\" >" << '\n';
{
// scalar data
if (!bdr_output)
{
for (int i = 0; i < mesh->GetNE(); i++)
{
RefG = GlobGeometryRefiner.Refine(
mesh->GetElementBaseGeometry(i), ref_, 1);
ElementTransformation *eltrans = mesh->GetElementTransformation(i);
const IntegrationRule *ir = &RefG->RefPts;
for (int j = 0; j < ir->GetNPoints(); j++)
{
const IntegrationPoint &ip = ir->IntPoint(j);
eltrans->SetIntPoint(&ip);
val = coeff.Eval(*eltrans, ip);
WriteBinaryOrASCII(os, buf, val, "\n", pv_data_format);
}
}
}
else
{
for (int i = 0; i < mesh->GetNBE(); i++)
{
RefG = GlobGeometryRefiner.Refine(
mesh->GetBdrElementBaseGeometry(i), ref_, 1);
ElementTransformation *eltrans = mesh->GetBdrElementTransformation(i);
const IntegrationRule *ir = &RefG->RefPts;
for (int j = 0; j < ir->GetNPoints(); j++)
{
const IntegrationPoint &ip = ir->IntPoint(j);
eltrans->SetIntPoint(&ip);
val = coeff.Eval(*eltrans, ip);
WriteBinaryOrASCII(os, buf, val, "\n", pv_data_format);
}
}
}
}
if (pv_data_format != VTKFormat::ASCII)
{
WriteBase64WithSizeAndClear(os, buf, GetCompressionLevel());
}
os << "</DataArray>" << std::endl;
}
void ParaViewDataCollection::SaveVCoeffFieldVTU(std::ostream &os, int ref_,
const std::string &name, VectorCoefficient &coeff)
{
RefinedGeometry *RefG;
Vector val;
std::vector<char> buf;
int vec_dim = coeff.GetVDim();
os << "<DataArray type=\"" << GetDataTypeString()
<< "\" Name=\"" << name
<< "\" NumberOfComponents=\"" << vec_dim << "\""
<< " format=\"" << GetDataFormatString() << "\" >" << '\n';
{
// vector data
if (!bdr_output)
{
for (int i = 0; i < mesh->GetNE(); i++)
{
RefG = GlobGeometryRefiner.Refine(
mesh->GetElementBaseGeometry(i), ref_, 1);
ElementTransformation *eltrans = mesh->GetElementTransformation(i);
const IntegrationRule *ir = &RefG->RefPts;
for (int j = 0; j < ir->GetNPoints(); j++)
{
const IntegrationPoint &ip = ir->IntPoint(j);
eltrans->SetIntPoint(&ip);
coeff.Eval(val, *eltrans, ip);
for (int jj = 0; jj < val.Size(); jj++)
{
WriteBinaryOrASCII(os, buf, val(jj), " ", pv_data_format);
}
if (pv_data_format == VTKFormat::ASCII) { os << '\n'; }
}
}
}
else
{
for (int i = 0; i < mesh->GetNBE(); i++)
{
RefG = GlobGeometryRefiner.Refine(
mesh->GetBdrElementBaseGeometry(i), ref_, 1);
ElementTransformation *eltrans = mesh->GetBdrElementTransformation(i);
const IntegrationRule *ir = &RefG->RefPts;
for (int j = 0; j < ir->GetNPoints(); j++)
{
const IntegrationPoint &ip = ir->IntPoint(j);
eltrans->SetIntPoint(&ip);
coeff.Eval(val, *eltrans, ip);
for (int jj = 0; jj < val.Size(); jj++)
{
WriteBinaryOrASCII(os, buf, val(jj), " ", pv_data_format);
}
if (pv_data_format == VTKFormat::ASCII) { os << '\n'; }
}
}
}
}
if (pv_data_format != VTKFormat::ASCII)
{
WriteBase64WithSizeAndClear(os, buf, GetCompressionLevel());
}
os << "</DataArray>" << std::endl;
}
+59 -17
View File
@@ -133,6 +133,7 @@ private:
/// A collection of named QuadratureFunctions
typedef NamedFieldsMap<QuadratureFunction> QFieldMap;
public:
typedef GFieldMap::MapType FieldMapType;
typedef GFieldMap::iterator FieldMapIterator;
@@ -249,10 +250,9 @@ public:
{ field_map.Deregister(field_name, own_data); }
/// Add a QuadratureFunction to the collection.
virtual void RegisterQField(const std::string& q_field_name,
virtual void RegisterQField(const std::string& field_name,
QuadratureFunction *qf)
{ q_field_map.Register(q_field_name, qf, own_data); }
{ q_field_map.Register(field_name, qf, own_data); }
/// Remove a QuadratureFunction from the collection
virtual void DeregisterQField(const std::string& field_name)
@@ -280,13 +280,13 @@ public:
#endif
/// Check if a QuadratureFunction with the given name is in the collection.
bool HasQField(const std::string& q_field_name) const
{ return q_field_map.Has(q_field_name); }
bool HasQField(const std::string& field_name) const
{ return q_field_map.Has(field_name); }
/// Get a pointer to a QuadratureFunction in the collection.
/** Returns NULL if @a field_name is not in the collection. */
QuadratureFunction *GetQField(const std::string& q_field_name)
{ return q_field_map.Get(q_field_name); }
QuadratureFunction *GetQField(const std::string& field_name)
{ return q_field_map.Get(field_name); }
/// Get a const reference to the internal field map.
/** The keys in the map are the field names and the values are pointers to
@@ -302,11 +302,13 @@ public:
/// Get a pointer to the mesh in the collection
Mesh *GetMesh() { return mesh; }
/// Set/change the mesh associated with the collection
/** When passed a Mesh, assumes the serial case: MPI rank id is set to 0 and
MPI num_procs is set to 1. When passed a ParMesh, MPI info from the
ParMesh is used to set the DataCollection's MPI rank and num_procs. */
virtual void SetMesh(Mesh *new_mesh);
#ifdef MFEM_USE_MPI
/// Set/change the mesh associated with the collection.
/** For this case, @a comm is used to set the DataCollection's MPI rank id
@@ -369,8 +371,7 @@ public:
/// Save one field, assuming the collection directory already exists.
virtual void SaveField(const std::string &field_name);
/// Save one q-field, assuming the collection directory already exists.
virtual void SaveQField(const std::string &q_field_name);
virtual void SaveQField(const std::string &field_name);
/// Load the collection. Not implemented in the base class DataCollection.
virtual void Load(int cycle_ = 0);
@@ -407,12 +408,18 @@ public:
class VisItFieldInfo
{
public:
std::string association;
int num_components;
int lod;
VisItFieldInfo() { association = ""; num_components = 0; lod = 1;}
VisItFieldInfo(std::string association_, int num_components_, int lod_ = 1)
{ association = association_; num_components = num_components_; lod =lod_;}
std::string association = "";
int num_components = 0;
int lod = 1;
std::string basis = "";
int order = -1;
VisItFieldInfo() = default;
VisItFieldInfo(std::string association_, int num_components_, int lod_ = 1,
std::string basis_ = "", int order_ = -1)
{
association = association_; num_components = num_components_; lod =lod_;
basis = basis_; order = order_;
}
};
/// Data collection with VisIt I/O routines
@@ -510,7 +517,9 @@ protected:
int compression_level = -1;
bool high_order_output = false;
bool restart_mode = false;
bool bdr_output = false;
VTKFormat pv_data_format = VTKFormat::BINARY;
public:
ParaViewDataCollectionBase(const std::string &name, Mesh *mesh);
@@ -543,6 +552,10 @@ public:
/// Reading high-order data requires ParaView 5.5 or later.
void SetHighOrderOutput(bool high_order_output_);
/// @brief Configures collection to save only fields evaluated on boundaries of
/// the mesh.
void SetBoundaryOutput(bool bdr_output_);
/// If compression is enabled, return the compression level, else return 0.
int GetCompressionLevel() const;
@@ -564,8 +577,6 @@ public:
///
/// If restart is enabled, new writes will preserve timestep metadata for any
/// solutions prior to the currently defined time.
///
/// Initially, restart mode is disabled.
void UseRestartMode(bool restart_mode_);
};
@@ -575,11 +586,23 @@ class ParaViewDataCollection : public ParaViewDataCollectionBase
private:
std::fstream pvd_stream;
/// A collection of named Coefficients and VectorCoefficients
using CoeffFieldMap = NamedFieldsMap<Coefficient>;
using VCoeffFieldMap = NamedFieldsMap<VectorCoefficient>;
/** A FieldMap mapping registered names to Coefficient and VectorCoefficient
pointers. */
CoeffFieldMap coeff_field_map;
VCoeffFieldMap vcoeff_field_map;
protected:
void WritePVTUHeader(std::ostream &out);
void WritePVTUFooter(std::ostream &out, const std::string &vtu_prefix);
void SaveDataVTU(std::ostream &out, int ref);
void SaveGFieldVTU(std::ostream& out, int ref_, const FieldMapIterator& it);
void SaveCoeffFieldVTU(std::ostream& out, int ref_, const std::string &name,
Coefficient &coeff);
void SaveVCoeffFieldVTU(std::ostream& out, int ref_, const std::string &name,
VectorCoefficient& coeff);
const char *GetDataFormatString() const;
const char *GetDataTypeString() const;
@@ -598,6 +621,25 @@ public:
ParaViewDataCollection(const std::string& collection_name,
Mesh *mesh_ = nullptr);
/// Get a const reference to the internal coefficient-field map.
const typename CoeffFieldMap::MapType &GetCoeffFieldMap() const
{ return coeff_field_map.GetMap(); }
const typename VCoeffFieldMap::MapType &GetVCoeffFieldMap() const
{ return vcoeff_field_map.GetMap(); }
/// Add a Coefficient or VectorCoefficient to the collection.
void RegisterCoeffField(const std::string& field_name, Coefficient *coeff)
{ coeff_field_map.Register(field_name, coeff, own_data); }
void RegisterVCoeffField(const std::string& field_name,
VectorCoefficient *vcoeff)
{ vcoeff_field_map.Register(field_name, vcoeff, own_data); }
/// Remove a Coefficient or VectorCoefficient from the collection
void DeregisterCoeffField(const std::string& field_name)
{ coeff_field_map.Deregister(field_name, own_data); }
void DeregisterVCoeffField(const std::string& field_name)
{ vcoeff_field_map.Deregister(field_name, own_data); }
/// Save the collection - the directory name is constructed based on the
/// cycle value
void Save() override;
+403
View File
@@ -0,0 +1,403 @@
// 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 "util.hpp"
namespace mfem::future
{
/// @brief Assemble element matrix for three dimensional data.
///
/// Note: In the below layouts, total_trial_op_dim is > 1 if
/// there are more than one inputs dependent on the derivative variable.
///
/// @param A Memory for one element matrix with layout
/// [test_ndof, test_vdim, trial_ndof, trial_vdim].
/// @param fhat Memory to hold the residual computation with layout
/// [test_vdim, test_op_dim, nqp].
/// @param qpdc The quadrature point data cache with data layout
/// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, nqp].
/// @param itod Input Trial Operator Dimension array. If the trial
/// operator is not dependent, the dimension is 0 to indicate that.
/// @param inputs The input field operator types.
/// @param output The output field operator types.
/// @param input_dtqmaps The input DofToQuad maps.
/// @param output_dtqmap The output DofToQuad maps.
/// @param scratch_shmem Scratch shared memory for computations.
/// @param q1d The number of quadrature points in one dimension.
/// @param td1d The number of trial dofs in one dimension.
template <typename input_fop_ts, size_t num_inputs, typename output_fop_t>
MFEM_HOST_DEVICE void assemble_element_mat_t3d(
const DeviceTensor<4, real_t>& A,
const DeviceTensor<3, real_t>& fhat,
const DeviceTensor<5, const real_t>& qpdc,
const DeviceTensor<1, const real_t>& itod,
const input_fop_ts& inputs,
const output_fop_t& output,
const std::array<DofToQuadMap, num_inputs>& input_dtqmaps,
const DofToQuadMap& output_dtqmap,
std::array<DeviceTensor<1>, 6>& scratch_shmem,
const int& q1d,
const int& td1d)
{
constexpr int dimension = 3;
// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, num_qp]
const int test_vdim = qpdc.GetShape()[0];
const int test_op_dim = qpdc.GetShape()[1];
const int trial_vdim = qpdc.GetShape()[2];
// [num_test_dof, ...]
const auto num_test_dof = A.GetShape()[0];
for (int Jx = 0; Jx < td1d; Jx++)
{
for (int Jy = 0; Jy < td1d; Jy++)
{
for (int Jz = 0; Jz < td1d; Jz++)
{
const int J = Jx + td1d * (Jy + td1d * Jz);
for (int j = 0; j < trial_vdim; j++)
{
for (int tv = 0; tv < test_vdim; tv++)
{
for (int tod = 0; tod < test_op_dim; tod++)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
fhat(tv, tod, q) = 0.0;
}
}
}
}
}
// MSVC lambda capture workaround
[[maybe_unused]] const auto& inputs_ref = inputs;
int m_offset = 0;
for_constexpr<num_inputs>([&](auto s)
{
using fop_t = std::decay_t<decltype(get<s>(inputs_ref))>;
const int trial_op_dim = static_cast<int>(itod(static_cast<int>(s)));
if (trial_op_dim == 0)
{
// This is inside a lambda so we have to return
// instead of idiomatic 'continue'.
return;
}
auto& B = input_dtqmaps[s].B;
auto& G = input_dtqmaps[s].G;
if constexpr (is_value_fop<fop_t>::value)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
for (int m = 0; m < trial_op_dim; m++)
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
const real_t f = qpdc(i, k, j, m + m_offset, q);
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy) * B(qz, 0, Jz);
}
}
}
}
}
}
}
else if constexpr (is_gradient_fop<fop_t>::value)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
for (int m = 0; m < trial_op_dim; m++)
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
const real_t f = qpdc(i, k, j, m + m_offset, q);
if (m == 0)
{
fhat(i, k, q) += f * G(qx, 0, Jx) * B(qy, 0, Jy) * B(qz, 0, Jz);
}
else if (m == 1)
{
fhat(i, k, q) += f * B(qx, 0, Jx) * G(qy, 0, Jy) * B(qz, 0, Jz);
}
else if (m == 2)
{
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy) * G(qz, 0, Jz);
}
}
}
}
}
}
}
}
else
{
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
MFEM_ABORT("sum factorized sparse matrix assemble routine "
"not implemented for field operator");
#endif
}
MFEM_SYNC_THREAD;
m_offset += trial_op_dim;
});
auto bvtfhat = Reshape(&A(0, 0, J, j), num_test_dof, test_vdim);
map_quadrature_data_to_fields(bvtfhat, fhat, output, output_dtqmap,
scratch_shmem, dimension, true);
}
}
}
}
}
/// @brief Assemble element matrix for two dimensional data.
///
/// Note: In the below layouts, total_trial_op_dim is > 1 if
/// there are more than one inputs dependent on the derivative variable.
///
/// @param A Memory for one element matrix with layout
/// [test_ndof, test_vdim, trial_ndof, trial_vdim].
/// @param fhat Memory to hold the residual computation with layout
/// [test_vdim, test_op_dim, nqp].
/// @param qpdc The quadrature point data cache with data layout
/// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, nqp].
/// @param itod Input Trial Operator Dimension array. If the trial
/// operator is not dependent, the dimension is 0 to indicate that.
/// @param inputs The input field operator types.
/// @param output The output field operator types.
/// @param input_dtqmaps The input DofToQuad maps.
/// @param output_dtqmap The output DofToQuad maps.
/// @param scratch_shmem Scratch shared memory for computations.
/// @param q1d The number of quadrature points in one dimension.
/// @param td1d The number of trial dofs in one dimension.
template <typename input_fop_ts, size_t num_inputs, typename output_fop_t>
MFEM_HOST_DEVICE void assemble_element_mat_t2d(
const DeviceTensor<4, real_t>& A,
const DeviceTensor<3, real_t>& fhat,
const DeviceTensor<5, const real_t>& qpdc,
const DeviceTensor<1, const real_t>& itod,
const input_fop_ts& inputs,
const output_fop_t& output,
const std::array<DofToQuadMap, num_inputs>& input_dtqmaps,
const DofToQuadMap& output_dtqmap,
std::array<DeviceTensor<1>, 6>& scratch_shmem,
const int& q1d,
const int& td1d)
{
constexpr int dimension = 2;
// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, num_qp]
const int test_vdim = qpdc.GetShape()[0];
const int test_op_dim = qpdc.GetShape()[1];
const int trial_vdim = qpdc.GetShape()[2];
// [num_test_dof, ...]
const auto num_test_dof = A.GetShape()[0];
for (int Jx = 0; Jx < td1d; Jx++)
{
for (int Jy = 0; Jy < td1d; Jy++)
{
const int J = Jy + Jx * td1d;
for (int j = 0; j < trial_vdim; j++)
{
for (int tv = 0; tv < test_vdim; tv++)
{
for (int tod = 0; tod < test_op_dim; tod++)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
const int q = qy + qx * q1d;
fhat(tv, tod, q) = 0.0;
}
}
}
}
// MSVC lambda capture workaround
[[maybe_unused]] const auto& inputs_ref = inputs;
int m_offset = 0;
for_constexpr<num_inputs>([&](auto s)
{
using fop_t = std::decay_t<decltype(get<s>(inputs_ref))>;
const int trial_op_dim = static_cast<int>(itod(static_cast<int>(s)));
if (trial_op_dim == 0)
{
// This is inside a lambda so we have to return
// instead of idiomatic 'continue'.
return;
}
auto& B = input_dtqmaps[s].B;
auto& G = input_dtqmaps[s].G;
if constexpr (is_value_fop<fop_t>::value)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
const int q = qy + qx * q1d;
for (int m = 0; m < trial_op_dim; m++)
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
const real_t f = qpdc(i, k, j, m + m_offset, q);
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy);
}
}
}
}
}
}
else if constexpr (is_gradient_fop<fop_t>::value)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
const int q = qy + qx * q1d;
for (int m = 0; m < trial_op_dim; m++)
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
const real_t f = qpdc(i, k, j, m + m_offset, q);
if (m == 0)
{
fhat(i, k, q) += f * B(qx, 0, Jx) * G(qy, 0, Jy);
}
else
{
fhat(i, k, q) += f * G(qx, 0, Jx) * B(qy, 0, Jy);
}
}
}
}
}
}
}
else
{
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
MFEM_ABORT("sum factorized sparse matrix assemble routine "
"not implemented for field operator");
#endif
}
MFEM_SYNC_THREAD;
m_offset += trial_op_dim;
});
auto bvtfhat = Reshape(&A(0, 0, J, j), num_test_dof, test_vdim);
map_quadrature_data_to_fields(bvtfhat, fhat, output, output_dtqmap,
scratch_shmem, dimension, true);
}
}
}
}
/// @brief Assemble element matrix for two or three dimensional data.
///
/// Note: In the below layouts, total_trial_op_dim is > 1 if
/// there are more than one inputs dependent on the derivative variable.
///
/// @param A Memory for one element matrix with layout
/// [test_ndof, test_vdim, trial_ndof, trial_vdim].
/// @param fhat Memory to hold the residual computation with layout
/// [test_vdim, test_op_dim, nqp].
/// @param qpdc The quadrature point data cache with data layout
/// [test_vdim, test_op_dim, trial_vdim, total_trial_op_dim, nqp].
/// @param itod Input Trial Operator Dimension array. If the trial
/// operator is not dependent, the dimension is 0 to indicate that.
/// @param inputs The input field operator types.
/// @param output The output field operator types.
/// @param input_dtqmaps The input DofToQuad maps.
/// @param output_dtqmap The output DofToQuad maps.
/// @param scratch_shmem Scratch shared memory for computations.
/// @param dimension The spatial dimension.
/// @param q1d The number of quadrature points in one dimension.
/// @param td1d The number of trial dofs in one dimension.
/// @param use_sum_factorization Indicator if sum factorization is used.
template <typename input_fop_ts, size_t num_inputs, typename output_fop_t>
MFEM_HOST_DEVICE void assemble_element_mat_naive(
const DeviceTensor<4, real_t>& A,
const DeviceTensor<3, real_t>& fhat,
const DeviceTensor<5, const real_t>& qpdc,
const DeviceTensor<1, const real_t>& itod,
const input_fop_ts& inputs,
const output_fop_t& output,
const std::array<DofToQuadMap, num_inputs>& input_dtqmaps,
const DofToQuadMap& output_dtqmap,
std::array<DeviceTensor<1>, 6>& scratch_shmem,
const int& dimension,
const int& q1d,
const int& td1d,
const bool& use_sum_factorization)
{
if (use_sum_factorization)
{
if (dimension == 2)
{
assemble_element_mat_t2d(A, fhat, qpdc, itod, inputs, output,
input_dtqmaps, output_dtqmap, scratch_shmem, q1d, td1d);
}
else if (dimension == 3)
{
assemble_element_mat_t3d(A, fhat, qpdc, itod, inputs, output,
input_dtqmaps, output_dtqmap, scratch_shmem, q1d, td1d);
}
}
else
{
#if !(defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP))
MFEM_ABORT("element matrix assemble not implemented for non tensor "
"product basis");
#endif
}
}
} // namespace mfem::future
+667 -53
View File
@@ -22,6 +22,7 @@
#include "interpolate.hpp"
#include "integrate.hpp"
#include "qfunction_apply.hpp"
#include "assemble.hpp"
namespace mfem::future
{
@@ -30,14 +31,23 @@ namespace mfem::future
using action_t =
std::function<void(std::vector<Vector> &, const std::vector<Vector> &, Vector &)>;
/// @brief Type alias for a function that computes the cache for the action of a derivative
using derivative_setup_t =
std::function<void(std::vector<Vector> &, const Vector &)>;
/// @brief Type alias for a function that computes the action of a derivative
using derivative_action_t =
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
/// @brief Type alias for a function that assembles the sparse matrix of a
/// @brief Type alias for a function that assembles the SparseMatrix of a
/// derivative operator
using assemble_derivative_sparsematrix_callback_t =
std::function<void(std::vector<Vector> &, SparseMatrix *&)>;
/// @brief Type alias for a function that assembles the HypreParMatrix of a
/// derivative operator
using assemble_derivative_hypreparmatrix_callback_t =
std::function<void(std::vector<Vector> &, HypreParMatrix &)>;
std::function<void(std::vector<Vector> &, HypreParMatrix *&)>;
/// @brief Type alias for a function that applies the appropriate restriction to
/// the solution and parameters
@@ -81,6 +91,8 @@ public:
const std::vector<Vector *> &parameters_l,
const restriction_callback_t &restriction_callback,
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
const std::vector<assemble_derivative_sparsematrix_callback_t>
&assemble_derivative_sparsematrix_callbacks,
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
&assemble_derivative_hypreparmatrix_callbacks) :
Operator(height, width),
@@ -91,6 +103,8 @@ public:
derivative_actions_transpose(derivative_actions_transpose),
transpose_direction(transpose_direction),
prolongation_transpose(prolongation_transpose),
assemble_derivative_sparsematrix_callbacks(
assemble_derivative_sparsematrix_callbacks),
assemble_derivative_hypreparmatrix_callbacks(
assemble_derivative_hypreparmatrix_callbacks)
{
@@ -129,6 +143,30 @@ public:
prolongation_transpose(daction_l, result_t);
};
void Assemble(Vector& result_t) const
{
auto l_width = GetVSize(direction);
daction_l.SetSize(l_width);
daction_l = 0.0;
direction_l.SetSize(l_width);
direction_l = 0.0;
for (const auto &f : derivative_actions)
{
for (int i{0}; i < direction_l.Size(); ++i)
{
direction_l[i] = 1.0;
mfem::Vector local_daction_l(daction_l_size);
local_daction_l = 0.0;
f(fields_e, direction_l, local_daction_l);
daction_l[i] += local_daction_l[0];
direction_l[i] = 0.0;
}
}
prolongation_transpose(daction_l, result_t);
}
/// @brief Compute the transpose of the derivative operator on a given
/// vector.
///
@@ -156,14 +194,29 @@ public:
prolongation_transpose(daction_l, result_t);
};
/// @brief Assemble the derivative operator into a SparseMatrix.
///
/// @param A The SparseMatrix to assemble the derivative operator into. Can
/// be an uninitialized object.
void Assemble(SparseMatrix *&A)
{
MFEM_ASSERT(!assemble_derivative_sparsematrix_callbacks.empty(),
"derivative can't be assembled into a SparseMatrix");
for (const auto &f : assemble_derivative_sparsematrix_callbacks)
{
f(fields_e, A);
}
}
/// @brief Assemble the derivative operator into a HypreParMatrix.
///
/// @param A The HypreParMatrix to assemble the derivative operator into. Can
/// be an uninitialized object.
void Assemble(HypreParMatrix &A)
void Assemble(HypreParMatrix *&A)
{
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
"derivative can't be assembled into a matrix");
"derivative can't be assembled into a HypreParMatrix");
for (const auto &f : assemble_derivative_hypreparmatrix_callbacks)
{
@@ -196,6 +249,10 @@ private:
std::function<void(Vector &, Vector &)> prolongation_transpose;
/// Callbacks that assemble derivatives into a SparseMatrix.
std::vector<assemble_derivative_sparsematrix_callback_t>
assemble_derivative_sparsematrix_callbacks;
/// Callbacks that assemble derivatives into a HypreParMatrix.
std::vector<assemble_derivative_hypreparmatrix_callback_t>
assemble_derivative_hypreparmatrix_callbacks;
@@ -211,8 +268,8 @@ private:
///
/// The operator is constructed with solution fields that it will act on and
/// parameter fields that define coefficients. Quadrature functions are added by
/// e.g. using AddDomainIntegrator() which specify how the operator evaluates f
/// those functionas and parameters at quadrature points.
/// e.g. using AddDomainIntegrator() which specify how the operator evaluates
/// those functions and parameters at quadrature points.
///
/// Derivatives can be computed by obtaining a DerivativeOperator using
/// GetDerivative().
@@ -280,6 +337,22 @@ public:
}
}
/// @brief Add an integrator to the operator.
/// Called only from AddDomainIntegrator() and AddBoundaryIntegrator().
template <
typename entity_t,
typename qfunc_t,
typename input_t,
typename output_t,
typename derivative_ids_t>
void AddIntegrator(
qfunc_t &qfunc,
input_t inputs,
output_t outputs,
const IntegrationRule &integration_rule,
const Array<int> &attributes,
derivative_ids_t derivative_ids);
/// @brief Add a domain integrator to the operator.
///
/// @param qfunc The quadrature function to be added.
@@ -305,6 +378,31 @@ public:
const Array<int> &domain_attributes,
derivative_ids_t derivative_ids = std::make_index_sequence<0> {});
/// @brief Add a boundary integrator to the operator.
///
/// @param qfunc The quadrature function to be added.
/// @param inputs Tuple of FieldOperators for the inputs of the quadrature
/// function.
/// @param outputs Tuple of FieldOperators for the outputs of the quadrature
/// function.
/// @param integration_rule IntegrationRule to use with this integrator.
/// @param boundary_attributes Boundary attributes marker array indicating over
/// which attributes this integrator will integrate over.
/// @param derivative_ids Derivatives to be made available for this
/// integrator.
template <
typename qfunc_t,
typename input_t,
typename output_t,
typename derivative_ids_t = decltype(std::make_index_sequence<0> {})>
void AddBoundaryIntegrator(
qfunc_t &qfunc,
input_t inputs,
output_t outputs,
const IntegrationRule &integration_rule,
const Array<int> &boundary_attributes,
derivative_ids_t derivative_ids = std::make_index_sequence<0> {});
/// @brief Set the parameters for the operator.
///
/// This has to be called before using Mult() or MultTranspose().
@@ -357,6 +455,34 @@ public:
const size_t derivative_idx = FindIdx(derivative_id, fields);
std::vector<Vector> s_l(solutions_l.size());
for (size_t i = 0; i < s_l.size(); i++)
{
s_l[i] = *sol_l[i];
}
std::vector<Vector> p_l(parameters_l.size());
for (size_t i = 0; i < p_l.size(); i++)
{
p_l[i] = *par_l[i];
}
fields_e.resize(solutions_l.size() + parameters_l.size());
restriction_callback(s_l, p_l, fields_e);
// Dummy
Vector dir_l;
if (derivative_idx >= s_l.size())
{
dir_l = p_l[derivative_idx - s_l.size()];
}
else
{
dir_l = s_l[derivative_idx];
}
derivative_setup_callbacks[derivative_id][0](fields_e, dir_l);
return std::make_shared<DerivativeOperator>(
height,
GetTrueVSize(fields[derivative_idx]),
@@ -369,7 +495,8 @@ public:
sol_l,
par_l,
restriction_callback,
prolongation_transpose,
derivative_prolongation_transpose,
assemble_derivative_sparsematrix_callbacks[derivative_id],
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
}
@@ -379,10 +506,14 @@ private:
MultLevel mult_level = TVECTOR;
std::vector<action_t> action_callbacks;
std::map<size_t, std::vector<derivative_setup_t>> derivative_setup_callbacks;
std::map<size_t,
std::vector<derivative_action_t>> derivative_action_callbacks;
std::map<size_t,
std::vector<derivative_action_t>> daction_transpose_callbacks;
std::map<size_t,
std::vector<assemble_derivative_sparsematrix_callback_t>>
assemble_derivative_sparsematrix_callbacks;
std::map<size_t,
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
assemble_derivative_hypreparmatrix_callbacks;
@@ -400,9 +531,12 @@ private:
mutable Vector residual_e;
std::function<void(Vector &, Vector &)> prolongation_transpose;
std::function<void(Vector &, Vector &)> derivative_prolongation_transpose;
std::function<void(Vector &, Vector &)> output_restriction_transpose;
restriction_callback_t restriction_callback;
std::map<size_t, Vector> derivative_qp_caches;
std::map<size_t, size_t> assembled_vector_sizes;
bool use_tensor_product_structure = true;
@@ -423,7 +557,52 @@ void DifferentiableOperator::AddDomainIntegrator(
const Array<int> &domain_attributes,
derivative_ids_t derivative_ids)
{
using entity_t = Entity::Element;
AddIntegrator<Entity::Element>(
qfunc, inputs, outputs, integration_rule, domain_attributes, derivative_ids);
}
template <
typename qfunc_t,
typename input_t,
typename output_t,
typename derivative_ids_t>
void DifferentiableOperator::AddBoundaryIntegrator(
qfunc_t &qfunc,
input_t inputs,
output_t outputs,
const IntegrationRule &integration_rule,
const Array<int> &boundary_attributes,
derivative_ids_t derivative_ids)
{
if (mesh.GetNFbyType(FaceType::Boundary) != mesh.GetNBE())
{
MFEM_ABORT("AddBoundaryIntegrator on meshes with interior boundaries is not supported.");
}
AddIntegrator<Entity::BoundaryElement>(
qfunc, inputs, outputs, integration_rule, boundary_attributes, derivative_ids);
}
template <
typename entity_t,
typename qfunc_t,
typename input_t,
typename output_t,
typename derivative_ids_t>
void DifferentiableOperator::AddIntegrator(
qfunc_t &qfunc,
input_t inputs,
output_t outputs,
const IntegrationRule &integration_rule,
const Array<int> &attributes,
derivative_ids_t derivative_ids)
{
if constexpr (!(std::is_same_v<entity_t, Entity::Element> ||
std::is_same_v<entity_t, Entity::BoundaryElement>))
{
static_assert(dfem::always_false<entity_t>,
"entity type not supported in AddIntegrator");
}
static constexpr size_t num_inputs =
tuple_size<decltype(inputs)>::value;
@@ -484,25 +663,44 @@ void DifferentiableOperator::AddDomainIntegrator(
inputs_vdim[i] = get<i>(inputs).vdim;
});
Array<int> elem_attributes;
elem_attributes.SetSize(mesh.GetNE());
for (int i = 0; i < mesh.GetNE(); ++i)
const Array<int> *elem_attributes = nullptr;
if constexpr (std::is_same_v<entity_t, Entity::Element>)
{
elem_attributes[i] = mesh.GetAttribute(i);
elem_attributes = &mesh.GetElementAttributes();
}
else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
{
elem_attributes = &mesh.GetBdrFaceAttributes();
}
const auto output_fop = get<0>(outputs);
test_space_field_idx = FindIdx(output_fop.GetFieldId(), fields);
bool use_sum_factorization = false;
auto entity_element_type =
Element::TypeFromGeometry(mesh.GetTypicalElementGeometry());
if ((entity_element_type == Element::QUADRILATERAL ||
entity_element_type == Element::HEXAHEDRON) &&
use_tensor_product_structure == true)
Element::Type entity_element_type;
if constexpr (std::is_same_v<entity_t, Entity::Element>)
{
use_sum_factorization = true;
entity_element_type =
Element::TypeFromGeometry(mesh.GetTypicalElementGeometry());
if ((entity_element_type == Element::QUADRILATERAL ||
entity_element_type == Element::HEXAHEDRON) &&
use_tensor_product_structure == true)
{
use_sum_factorization = true;
}
}
else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
{
entity_element_type =
Element::TypeFromGeometry(mesh.GetTypicalFaceGeometry());
if ((entity_element_type == Element::SEGMENT ||
entity_element_type == Element::QUADRILATERAL) &&
use_tensor_product_structure == true)
{
use_sum_factorization = true;
}
}
ElementDofOrdering element_dof_ordering = ElementDofOrdering::NATIVE;
@@ -540,12 +738,21 @@ void DifferentiableOperator::AddDomainIntegrator(
prolongation_transpose = get_prolongation_transpose(
fields[test_space_field_idx], output_fop, mesh.GetComm());
const int dimension = mesh.Dimension();
[[maybe_unused]] const int num_elements = GetNumEntities<Entity::Element>(mesh);
int dimension;
if constexpr (std::is_same_v<entity_t, Entity::Element>)
{
dimension = mesh.Dimension();
}
else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
{
dimension = mesh.Dimension() - 1;
}
[[maybe_unused]] const int num_elements = GetNumEntities<entity_t>(mesh);
const int num_entities = GetNumEntities<entity_t>(mesh);
const int num_qp = integration_rule.GetNPoints();
if constexpr (is_sum_fop<decltype(output_fop)>::value)
if constexpr (is_sum_fop<std::remove_const_t<decltype(output_fop)>>::value)
{
residual_l.SetSize(1);
height = 1;
@@ -577,10 +784,13 @@ void DifferentiableOperator::AddDomainIntegrator(
auto input_dtq_maps = create_dtq_maps<entity_t>(inputs, dtq, input_to_field);
auto output_dtq_maps = create_dtq_maps<entity_t>(outputs, dtq, output_to_field);
const int test_vdim = output_fop.vdim;
const int test_op_dim = output_fop.size_on_qp / output_fop.vdim;
int test_vdim = output_fop.vdim;
if constexpr (is_sum_fop<std::remove_const_t<decltype(output_fop)>>::value) {
test_vdim = 1;
}
const int test_op_dim = output_fop.size_on_qp / test_vdim;
const int num_test_dof =
num_entities ? (output_e_size / output_fop.vdim / num_entities) : 0;
num_entities ? std::max(1, output_e_size / test_vdim / num_entities) : 0;
auto ir_weights = Reshape(integration_rule.GetWeights().Read(), num_qp);
@@ -615,6 +825,12 @@ void DifferentiableOperator::AddDomainIntegrator(
thread_blocks.z = 1;
}
}
else if (dimension == 1)
{
thread_blocks.x = q1d;
thread_blocks.y = 1;
thread_blocks.z = 1;
}
action_callbacks.push_back(
// Explicitly capture everything we need, so we can make explicit choice
@@ -630,7 +846,7 @@ void DifferentiableOperator::AddDomainIntegrator(
test_vdim, // int (= output_fop.vdim)
test_op_dim, // int (derived from output_fop)
inputs, // mfem::future::tuple
domain_attributes, // Array<int>
attributes, // Array<int>
ir_weights, // DeviceTensor
use_sum_factorization, // bool
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
@@ -663,13 +879,13 @@ void DifferentiableOperator::AddDomainIntegrator(
action_shmem_info.field_sizes,
num_entities);
const bool has_attr = domain_attributes.Size() > 0;
const auto d_domain_attr = domain_attributes.Read();
const auto d_elem_attr = elem_attributes.Read();
const bool has_attr = attributes.Size() > 0;
const auto d_attr = attributes.Read();
const auto d_elem_attr = elem_attributes->Read();
forall([=] MFEM_HOST_DEVICE (int e, void *shmem)
{
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
if (has_attr && !d_attr[d_elem_attr[e] - 1]) { return; }
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, input_shmem,
residual_shmem, scratch_shmem] =
@@ -697,6 +913,17 @@ void DifferentiableOperator::AddDomainIntegrator(
// will fail.
if constexpr (derivative_ids_t::size() != 0)
{
if constexpr (is_sum_fop<std::remove_const_t<decltype(output_fop)>>::value)
{
// NOTE: the field ID in Value isn't used, so set it to anything
derivative_prolongation_transpose = get_prolongation_transpose(
fields[test_space_field_idx], Value<0>(), mesh.GetComm());
}
else
{
derivative_prolongation_transpose = prolongation_transpose;
}
// Create the action of the derivatives
for_constexpr([&, &or_transpose =
this->output_restriction_transpose](const std::size_t derivative_id)
@@ -716,7 +943,8 @@ void DifferentiableOperator::AddDomainIntegrator(
// print_shared_memory_info(shmem_info);
Vector direction_e;
Vector direction_e(get_restriction<entity_t>(fields[d_field_idx],
element_dof_ordering)->Height());
Vector derivative_action_e(output_e_size);
derivative_action_e = 0.0;
@@ -728,24 +956,84 @@ void DifferentiableOperator::AddDomainIntegrator(
}
const auto input_is_dependent = it->second;
derivative_action_callbacks[derivative_id].push_back(
// Trial operator dimension for each input.
// The trial operator dimension is set for each input that is
// dependent and if it is independent the dimension is 0.
Vector inputs_trial_op_dim(num_inputs);
int total_trial_op_dim = 0;
{
auto itod = Reshape(inputs_trial_op_dim.HostReadWrite(), num_inputs);
int idx = 0;
for_constexpr<num_inputs>([&](auto s)
{
if (!input_is_dependent[s])
{
itod(idx) = 0;
}
else
{
// TODO: BUG! Make this a general function that works for all kinds of inputs.
itod(idx) = input_size_on_qp[s] / get<s>(inputs).vdim;
}
total_trial_op_dim += static_cast<int>(itod(idx));
idx++;
});
}
// First Input index of the derivative
const size_t d_input_idx = [d_field_idx, &input_to_field]
{
for (size_t i = 0; i < input_to_field.size(); i++)
{
if (input_to_field[i] == d_field_idx)
{
return i;
}
}
return size_t(SIZE_MAX);
}();
const int trial_vdim = GetVDim(fields[d_field_idx]);
const int num_trial_dof =
get_restriction<entity_t>(fields[d_field_idx], element_dof_ordering)->Height() /
inputs_vdim[d_input_idx] / num_entities;
const int num_trial_dof_1d =
input_dtq_maps[d_input_idx].B.GetShape()[DofToQuadMap::Index::DOF];
Vector Ae_mem(num_test_dof * test_vdim * num_trial_dof * trial_vdim *
num_entities);
Ae_mem = 0.0;
// Quadrature point local derivative cache for each element, with data
// layout:
// [test_vdim, test_op_dim, trial_vdim, trial_op_dim, qp, num_entities].
derivative_qp_caches[derivative_id] = Vector(test_vdim * test_op_dim *
trial_vdim *
total_trial_op_dim * num_qp * num_entities);
// Create local references for MSVC lambda capture compatibility
auto& fields_ref = this->fields;
auto& derivative_qp_caches_ref = this->derivative_qp_caches[derivative_id];
// In each of the callbacks we're saving the derivatives in the quadrature point
// caches. This trades memory with computational effort but also minimizes
// data movement on each multiplication of the gradient with a directional
// vector.
derivative_setup_callbacks[derivative_id].push_back(
[
// capture by copy:
dimension, // int
num_entities, // int
num_test_dof, // int
num_qp, // int
q1d, // int
test_vdim, // int (= output_fop.vdim)
test_op_dim, // int (derived from output_fop)
inputs, // mfem::future::tuple
domain_attributes, // Array<int>
attributes, // Array<int>
ir_weights, // DeviceTensor
use_sum_factorization, // bool
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
input_to_field, // std::array<int, s>
output_fop, // class derived from FieldOperator
qfunc, // qfunc_t
thread_blocks, // ThreadBlocks
shmem_cache, // Vector (local)
@@ -753,35 +1041,37 @@ void DifferentiableOperator::AddDomainIntegrator(
// TODO: make this Array<int> a member of the DifferentiableOperator
// and capture it by ref.
elem_attributes, // Array<int>
element_dof_ordering, // ElementDofOrdering
input_is_dependent, // std::array<bool, num_inputs>
direction, // FieldDescriptor
direction_e, // Vector
derivative_action_e, // Vector
element_dof_ordering, // ElementDofOrdering
da_size_on_qp, // int
total_trial_op_dim,
trial_vdim,
inputs_trial_op_dim,
// capture by ref:
&or_transpose
](
std::vector<Vector> &f_e, const Vector &dir_l,
Vector &der_action_l) mutable
&qpdc_mem = derivative_qp_caches_ref
](std::vector<Vector> &f_e, const Vector &dir_l) mutable
{
restriction<entity_t>(direction, dir_l, direction_e,
element_dof_ordering);
auto ye = Reshape(derivative_action_e.ReadWrite(), num_test_dof,
test_vdim, num_entities);
auto wrapped_fields_e = wrap_fields(f_e, shmem_info.field_sizes,
num_entities);
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
shmem_info.direction_size,
num_entities);
const auto d_elem_attr = elem_attributes.Read();
const bool has_attr = domain_attributes.Size() > 0;
const auto d_domain_attr = domain_attributes.Read();
auto qpdc = Reshape(qpdc_mem.ReadWrite(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp, num_entities);
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
const auto d_elem_attr = elem_attributes->Read();
const bool has_attr = attributes.Size() > 0;
const auto d_domain_attr = attributes.Read();
derivative_action_e = 0.0;
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
{
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
@@ -799,20 +1089,104 @@ void DifferentiableOperator::AddDomainIntegrator(
inputs, ir_weights, scratch_shmem, dimension,
use_sum_factorization);
// TODO: Probably redundant
set_zero(shadow_shmem);
auto qpdc_e = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp);
call_qfunction_derivative<qf_param_ts>(
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc_e, itod, da_size_on_qp,
q1d, dimension, use_sum_factorization);
}, num_entities, thread_blocks, shmem_info.total_size,
shmem_cache.ReadWrite());
});
// The derivative action only uses the quadrature point caches and applies
// them to an input vector before integrating with the desired trial operator.
derivative_action_callbacks[derivative_id].push_back(
[
// capture by copy:
dimension, // int
num_entities, // int
num_test_dof, // int
num_qp, // int
q1d, // int
test_vdim, // int (= output_fop.vdim)
test_op_dim, // int (derived from output_fop)
inputs, // mfem::future::tuple
attributes, // Array<int>
ir_weights, // DeviceTensor
use_sum_factorization, // bool
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
output_fop, // class derived from FieldOperator
thread_blocks, // ThreadBlocks
shmem_cache, // Vector (local)
shmem_info, // SharedMemoryInfo
// TODO: make this Array<int> a member of the DifferentiableOperator
// and capture it by ref.
elem_attributes, // Array<int>
input_is_dependent, // std::array<bool, num_inputs>
direction, // FieldDescriptor
direction_e, // Vector
derivative_action_e, // Vector
element_dof_ordering, // ElementDofOrdering
inputs_trial_op_dim,
total_trial_op_dim,
trial_vdim,
// capture by ref:
&qpdc_mem = derivative_qp_caches_ref,
&or_transpose
](
std::vector<Vector> &f_e, const Vector &dir_l,
Vector &der_action_l) mutable
{
restriction<entity_t>(direction, dir_l, direction_e,
element_dof_ordering);
auto ye = Reshape(derivative_action_e.ReadWrite(), num_test_dof,
test_vdim, num_entities);
auto wrapped_fields_e = wrap_fields(f_e, shmem_info.field_sizes,
num_entities);
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
shmem_info.direction_size,
num_entities);
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp, num_entities);
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
const bool has_attr = attributes.Size() > 0;
const auto d_attr = attributes.Read();
const auto d_elem_attr = elem_attributes->Read();
derivative_action_e = 0.0;
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
{
if (has_attr && !d_attr[d_elem_attr[e] - 1]) { return; }
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem,
direction_shmem, input_shmem,
shadow_shmem_, residual_shmem,
scratch_shmem] =
unpack_shmem(shmem, shmem_info, input_dtq_maps, output_dtq_maps,
wrapped_fields_e, wrapped_direction_e, num_qp, e);
auto &shadow_shmem = shadow_shmem_;
map_direction_to_quadrature_data_conditional(
shadow_shmem, direction_shmem, input_dtq_shmem, inputs,
ir_weights, scratch_shmem, input_is_dependent, dimension,
use_sum_factorization);
call_qfunction_derivative_action<qf_param_ts>(
qfunc, input_shmem, shadow_shmem, residual_shmem,
da_size_on_qp, num_qp, q1d, dimension, use_sum_factorization);
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim,
test_op_dim, num_qp);
auto qpdce = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp);
apply_qpdc(fhat, shadow_shmem, qpdce, itod, q1d, dimension,
use_sum_factorization);
auto y = Reshape(&ye(0, 0, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields(
y, fhat, output_fop, output_dtq_shmem[0],
@@ -821,6 +1195,246 @@ void DifferentiableOperator::AddDomainIntegrator(
shmem_cache.ReadWrite());
or_transpose(derivative_action_e, der_action_l);
});
assemble_derivative_sparsematrix_callbacks[derivative_id].push_back(
[
// capture by copy:
dimension, // int
num_entities, // int
num_test_dof, // int
num_qp, // int
q1d, // int
test_vdim, // int (= output_fop.vdim)
test_op_dim, // int (derived from output_fop)
inputs, // mfem::future::tuple
attributes, // Array<int>
use_sum_factorization, // bool
input_dtq_maps, // std::array<DofToQuadMap, num_fields>
output_dtq_maps, // std::array<DofToQuadMap, num_fields>
input_to_field, // std::array<int, s>
output_fop, // class derived from FieldOperator
thread_blocks, // ThreadBlocks
shmem_cache, // Vector (local)
shmem_info, // SharedMemoryInfo
// TODO: make this Array<int> a member of the DifferentiableOperator
// and capture it by ref.
elem_attributes, // Array<int>
input_is_dependent, // std::array<bool, num_inputs>
direction_e, // Vector
total_trial_op_dim,
trial_vdim,
num_trial_dof,
num_trial_dof_1d,
inputs_trial_op_dim,
Ae_mem,
output_to_field,
// capture by ref:
&qpdc_mem = derivative_qp_caches_ref,
&fields = fields_ref
](std::vector<Vector> &f_e, SparseMatrix *&A) mutable
{
auto wrapped_fields_e = wrap_fields(f_e, shmem_info.field_sizes,
num_entities);
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
shmem_info.direction_size,
num_entities);
auto qpdc = Reshape(qpdc_mem.Read(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp, num_entities);
auto itod = Reshape(inputs_trial_op_dim.Read(), num_inputs);
auto Ae = Reshape(Ae_mem.ReadWrite(), num_test_dof, test_vdim, num_trial_dof,
trial_vdim, num_entities);
const auto d_elem_attr = elem_attributes->Read();
const bool has_attr = attributes.Size() > 0;
const auto d_domain_attr = attributes.Read();
forall([=] MFEM_HOST_DEVICE (int e, real_t *shmem)
{
if (has_attr && !d_domain_attr[d_elem_attr[e] - 1]) { return; }
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem,
direction_shmem, input_shmem,
shadow_shmem_, residual_shmem,
scratch_shmem] =
unpack_shmem(shmem, shmem_info, input_dtq_maps, output_dtq_maps,
wrapped_fields_e, wrapped_direction_e, num_qp, e);
auto fhat = Reshape(&residual_shmem(0, 0), test_vdim, test_op_dim, num_qp);
auto Aee = Reshape(&Ae(0, 0, 0, 0, e), num_test_dof, test_vdim, num_trial_dof,
trial_vdim);
auto qpdce = Reshape(&qpdc(0, 0, 0, 0, 0, e), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp);
assemble_element_mat_naive(Aee, fhat, qpdce, itod, inputs, output_fop,
input_dtq_shmem, output_dtq_shmem[0], scratch_shmem, dimension, q1d,
num_trial_dof_1d, use_sum_factorization);
}, num_entities, thread_blocks, shmem_info.total_size,
shmem_cache.ReadWrite());
FieldDescriptor *trial_field = nullptr;
for (size_t s = 0; s < num_inputs; s++)
{
if (input_is_dependent[s])
{
trial_field = &fields[input_to_field[s]];
}
}
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
(&trial_field->data);
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
(&fields[output_to_field[0]].data);
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
auto tmp = Reshape(Ae_mem.HostReadWrite(), num_test_dof * test_vdim,
num_trial_dof * trial_vdim, num_entities);
for (int e = 0; e < num_entities; e++)
{
DenseMatrix Aee(&tmp(0, 0, e), num_test_dof * test_vdim,
num_trial_dof * trial_vdim);
Array<int> test_vdofs, trial_vdofs;
test_fes->GetElementVDofs(e, test_vdofs);
trial_fes->GetElementVDofs(e, trial_vdofs);
if (use_sum_factorization)
{
Array<int> test_vdofs_mapped(test_vdofs.Size());
const Array<int> &test_dofmap =
dynamic_cast<const TensorBasisElement&>(*test_fes->GetFE(0)).GetDofMap();
if (test_dofmap.Size() == 0)
{
test_vdofs_mapped = test_vdofs;
}
else
{
MFEM_ASSERT(test_dofmap.Size() == num_test_dof,
"internal error: dof map of the test space does not "
"match previously determined number of test space dofs");
for (int vd = 0; vd < test_vdim; vd++)
{
for (int i = 0; i < num_test_dof; i++)
{
test_vdofs_mapped[i + vd * num_test_dof] =
test_vdofs[test_dofmap[i] + vd * num_test_dof];
}
}
}
Array<int> trial_vdofs_mapped(trial_vdofs.Size());
const Array<int> &trial_dofmap =
dynamic_cast<const TensorBasisElement&>(*trial_fes->GetFE(0)).GetDofMap();
if (trial_dofmap.Size() == 0)
{
trial_vdofs_mapped = trial_vdofs;
}
else
{
MFEM_ASSERT(trial_dofmap.Size() == num_trial_dof,
"internal error: dof map of the test space does not "
"match previously determined number of test space dofs");
for (int vd = 0; vd < trial_vdim; vd++)
{
for (int i = 0; i < num_trial_dof; i++)
{
trial_vdofs_mapped[i + vd * num_trial_dof] =
trial_vdofs[trial_dofmap[i] + vd * num_trial_dof];
}
}
}
A->AddSubMatrix(test_vdofs_mapped, trial_vdofs_mapped, Aee, 1);
}
else
{
A->AddSubMatrix(test_vdofs, trial_vdofs, Aee, 1);
}
}
A->Finalize();
});
// Create local references for MSVC lambda capture compatibility
auto& assemble_derivative_sparsematrix_callbacks_ref =
this->assemble_derivative_sparsematrix_callbacks[derivative_id];
assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
[
input_is_dependent,
input_to_field,
output_to_field,
&spmatcb = assemble_derivative_sparsematrix_callbacks_ref,
&fields = fields_ref
](std::vector<Vector> &f_e, HypreParMatrix *&A) mutable
{
SparseMatrix *spmat = nullptr;
for (const auto &f : spmatcb)
{
f(f_e, spmat);
}
if (spmat == nullptr)
{
MFEM_ABORT("internal error");
}
bool same_test_and_trial = false;
for (size_t s = 0; s < num_inputs; s++)
{
if (input_is_dependent[s])
{
if (output_to_field[0] == input_to_field[s])
{
same_test_and_trial = true;
break;
}
}
}
FieldDescriptor *trial_field = nullptr;
for (size_t s = 0; s < num_inputs; s++)
{
if (input_is_dependent[s])
{
trial_field = &fields[input_to_field[s]];
}
}
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
(&trial_field->data);
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
(&fields[output_to_field[0]].data);
if (same_test_and_trial)
{
HypreParMatrix tmp(test_fes->GetComm(),
test_fes->GlobalVSize(),
test_fes->GetDofOffsets(),
spmat);
A = RAP(&tmp, test_fes->Dof_TrueDof_Matrix());
}
else
{
HypreParMatrix tmp(test_fes->GetComm(),
test_fes->GlobalVSize(),
trial_fes->GlobalVSize(),
test_fes->GetDofOffsets(),
trial_fes->GetDofOffsets(),
spmat);
A = RAP(test_fes->Dof_TrueDof_Matrix(), &tmp,
trial_fes->Dof_TrueDof_Matrix());
}
delete spmat;
});
}, derivative_ids);
}
}
+89 -6
View File
@@ -90,11 +90,90 @@ void map_quadrature_data_to_fields_impl(
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
MFEM_ABORT_KERNEL("quadrature data mapping to field is not implemented for"
" this field descriptor");
}
}
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_tensor_impl_1d(
DeviceTensor<2, real_t> &y,
const DeviceTensor<3, real_t> &f,
const output_t &output,
const DofToQuadMap &dtq,
std::array<DeviceTensor<1>, 6> &scratch_mem)
{
[[maybe_unused]] auto B = dtq.B;
[[maybe_unused]] auto G = dtq.G;
if constexpr (is_value_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
const int vdim = output.vdim;
const int test_dim = output.size_on_qp / vdim;
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d);
auto yd = Reshape(&y(0, 0), d1d, vdim);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t acc = 0.0;
for (int qx = 0; qx < q1d; qx++)
{
acc += fqp(vd, 0, qx) * B(qx, 0, dx);
}
yd(dx, vd) = acc;
}
}
MFEM_SYNC_THREAD;
}
else if constexpr (is_gradient_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = G.GetShape();
const int vdim = output.vdim;
const int test_dim = output.size_on_qp / vdim;
auto fqp = Reshape(&f(0, 0, 0), vdim, test_dim, q1d);
auto yd = Reshape(&y(0, 0), d1d, vdim);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(dx, x, d1d)
{
real_t acc = 0.0;
for (int qx = 0; qx < q1d; qx++)
{
acc += fqp(vd, 0, qx) * G(qx, 0, dx);
}
yd(dx, vd) = acc;
}
}
MFEM_SYNC_THREAD;
}
else if constexpr (is_identity_fop<std::decay_t<output_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
auto fqp = Reshape(&f(0, 0, 0), output.size_on_qp, q1d);
auto yqp = Reshape(&y(0, 0), output.size_on_qp, q1d);
for (int sq = 0; sq < output.size_on_qp; sq++)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
yqp(sq, qx) = fqp(sq, qx);
}
MFEM_SYNC_THREAD;
}
}
else
{
MFEM_ABORT("quadrature data mapping to field is not implemented for"
" this field descriptor with sum factorization on tensor product elements");
}
}
template <typename output_t>
MFEM_HOST_DEVICE
void map_quadrature_data_to_fields_tensor_impl_2d(
@@ -227,8 +306,8 @@ 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");
}
}
@@ -413,8 +492,8 @@ 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");
}
}
@@ -431,7 +510,11 @@ void map_quadrature_data_to_fields(
{
if (use_sum_factorization)
{
if (dimension == 2)
if (dimension == 1)
{
map_quadrature_data_to_fields_tensor_impl_1d(y, f, output, dtq, scratch_mem);
}
else if (dimension == 2)
{
map_quadrature_data_to_fields_tensor_impl_2d(y, f, output, dtq, scratch_mem);
}
+113 -8
View File
@@ -338,6 +338,92 @@ void map_field_to_quadrature_data_tensor_product_2d(
}
}
template <typename field_operator_t>
MFEM_HOST_DEVICE inline
void map_field_to_quadrature_data_tensor_product_1d(
DeviceTensor<2> &field_qp,
const DofToQuadMap &dtq,
const DeviceTensor<1> &field_e,
const field_operator_t &input,
const DeviceTensor<1, const real_t> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem)
{
[[maybe_unused]] auto B = dtq.B;
[[maybe_unused]] auto G = dtq.G;
if constexpr (is_value_fop<std::decay_t<field_operator_t>>::value)
{
auto [q1d, unused, d1d] = B.GetShape();
const int vdim = input.vdim;
const auto field = Reshape(&field_e[0], d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t acc = 0.0;
for (int dx = 0; dx < d1d; dx++)
{
acc += B(qx, 0, dx) * field(dx, vd);
}
fqp(vd, qx) = acc;
}
}
MFEM_SYNC_THREAD;
}
else if constexpr (
is_gradient_fop<std::decay_t<field_operator_t>>::value)
{
const auto [q1d, unused, d1d] = B.GetShape();
const int vdim = input.vdim;
const int dim = input.dim;
const auto field = Reshape(&field_e[0], d1d, vdim);
auto fqp = Reshape(&field_qp[0], vdim, dim, q1d);
for (int vd = 0; vd < vdim; vd++)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
real_t acc = 0.0;
for (int dx = 0; dx < d1d; dx++)
{
acc += G(qx, 0, dx) * field(dx, vd);
}
fqp(vd, 0, qx) = acc;
}
MFEM_SYNC_THREAD;
}
}
// TODO: Create separate function for clarity
else if constexpr (
std::is_same_v<std::decay_t<field_operator_t>, Weight>)
{
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);
auto w = Reshape(&integration_weights[0], q1d);
auto f = Reshape(&field_qp[0], q1d);
MFEM_FOREACH_THREAD(qx, x, q1d)
{
f(qx) = w(qx);
}
MFEM_SYNC_THREAD;
}
else if constexpr (is_identity_fop<std::decay_t<field_operator_t>>::value)
{
const int q1d = B.GetShape()[0];
auto field = Reshape(&field_e[0], input.size_on_qp, q1d);
field_qp = field;
}
else
{
static_assert(dfem::always_false<std::decay_t<field_operator_t>>,
"can't map field to quadrature data");
}
}
template <typename field_operator_t>
MFEM_HOST_DEVICE
void map_field_to_quadrature_data(
@@ -425,7 +511,7 @@ void map_fields_to_quadrature_data(
std::array<DeviceTensor<2>, num_inputs> &fields_qp,
const std::array<DeviceTensor<1>, num_fields> &fields_e,
const std::array<DofToQuadMap, num_inputs> &dtqmaps,
const std::array<int, num_inputs> &input_to_field,
const std::array<size_t, num_inputs> &input_to_field,
const field_operator_ts &fops,
const DeviceTensor<1, const real_t> &integration_weights,
const std::array<DeviceTensor<1>, 6> &scratch_mem,
@@ -440,11 +526,18 @@ void map_fields_to_quadrature_data(
for_constexpr<num_inputs>([&](auto i)
{
const DeviceTensor<1> &field_e =
(input_to_field[i] == -1) ? dummy_field_weight : fields_e[input_to_field[i]];
(input_to_field[i] == SIZE_MAX) ? dummy_field_weight :
fields_e[input_to_field[i]];
if (use_sum_factorization)
{
if (dimension == 2)
if (dimension == 1)
{
map_field_to_quadrature_data_tensor_product_1d(
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
integration_weights, scratch_mem);
}
else if (dimension == 2)
{
map_field_to_quadrature_data_tensor_product_2d(
fields_qp[i], dtqmaps[i], field_e, get<i>(fops),
@@ -489,14 +582,20 @@ void map_field_to_quadrature_data_conditional(
{
if (use_sum_factorization)
{
if (dimension == 2)
if (dimension == 1)
{
map_field_to_quadrature_data_tensor_product_3d(
map_field_to_quadrature_data_tensor_product_1d(
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
}
else if (dimension == 2)
{
map_field_to_quadrature_data_tensor_product_2d(
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
}
else if (dimension == 3)
{
map_field_to_quadrature_data_tensor_product_2d(
map_field_to_quadrature_data_tensor_product_3d(
field_qp, dtqmap, field_e, fop, integration_weights, scratch_mem);
}
}
@@ -539,7 +638,7 @@ void map_direction_to_quadrature_data_conditional(
const std::array<DeviceTensor<1>, 6> &scratch_mem,
const std::array<bool, num_inputs> &conditions,
const int &dimension,
const bool &use_sum_factorization = false)
const bool &use_sum_factorization)
{
for_constexpr<num_inputs>([&](auto i)
{
@@ -547,7 +646,13 @@ void map_direction_to_quadrature_data_conditional(
{
if (use_sum_factorization)
{
if (dimension == 2)
if (dimension == 1)
{
map_field_to_quadrature_data_tensor_product_1d(
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
integration_weights, scratch_mem);
}
else if (dimension == 2)
{
map_field_to_quadrature_data_tensor_product_2d(
directions_qp[i], dtqmaps[i], direction_e, get<i>(fops),
+13 -1
View File
@@ -40,6 +40,9 @@ public:
/// returns the local size of the space
virtual int GetVSize() const = 0;
/// Number of elements
virtual int GetNE() const = 0;
/// Get spatial dimension
///
/// returns always 1.
@@ -74,6 +77,7 @@ public:
return elem_restr.get();
}
protected:
int vdim;
DofToQuad dtq;
@@ -96,7 +100,7 @@ public:
/// taken directly from the integration rule.
UniformParameterSpace(Mesh &mesh, const IntegrationRule &ir, int vdim,
bool used_in_tensor_product = true) :
ParameterSpace(vdim)
ParameterSpace(vdim), ne(mesh.GetNE())
{
// Setup DofToQuad information
dtq.nqpt = (int)floor(std::pow(ir.GetNPoints(), 1.0 / mesh.Dimension()) + 0.5);
@@ -122,12 +126,20 @@ public:
return lsize;
}
int GetNE() const override
{
return ne;
}
private:
/// T-vector size
int tsize;
/// L-vector size
int lsize;
/// Number of elements
int ne;
};
class ParameterFunction : public Vector
+337 -16
View File
@@ -44,11 +44,20 @@ void call_qfunction(
{
if (use_sum_factorization)
{
if (dimension == 2)
if (dimension == 1)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
auto qf_args = decay_tuple<qf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), rs_qp);
apply_kernel(r, qfunc, qf_args, input_shmem, q);
}
}
else if (dimension == 2)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
{
const int q = qx + q1d * qy;
auto qf_args = decay_tuple<qf_param_ts> {};
@@ -59,11 +68,11 @@ void call_qfunction(
}
else if (dimension == 3)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
auto qf_args = decay_tuple<qf_param_ts> {};
@@ -83,7 +92,7 @@ void call_qfunction(
}
else
{
MFEM_FOREACH_THREAD(q, x, num_qp)
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
{
auto qf_args = decay_tuple<qf_param_ts> {};
auto r = Reshape(&residual_shmem(0, q), rs_qp);
@@ -123,11 +132,26 @@ void call_qfunction_derivative_action(
{
if (use_sum_factorization)
{
if (dimension == 2)
if (dimension == 1)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
auto r = Reshape(&residual_shmem(0, q), das_qp);
auto qf_args = decay_tuple<qf_param_ts> {};
#ifdef MFEM_USE_ENZYME
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
shadow_shmem, q);
#else
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
#endif
}
}
else if (dimension == 2)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
{
const int q = qx + q1d * qy;
auto r = Reshape(&residual_shmem(0, q), das_qp);
@@ -144,11 +168,11 @@ void call_qfunction_derivative_action(
}
else if (dimension == 3)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
auto r = Reshape(&residual_shmem(0, q), das_qp);
@@ -164,11 +188,14 @@ void call_qfunction_derivative_action(
}
}
}
MFEM_SYNC_THREAD;
else
{
MFEM_ABORT_KERNEL("unsupported dimension");
}
}
else
{
MFEM_FOREACH_THREAD(q, x, num_qp)
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
{
auto r = Reshape(&residual_shmem(0, q), das_qp);
auto qf_args = decay_tuple<qf_param_ts> {};
@@ -180,7 +207,301 @@ void call_qfunction_derivative_action(
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
#endif
}
MFEM_SYNC_THREAD;
}
MFEM_SYNC_THREAD;
}
namespace detail
{
template <
typename qf_param_ts,
typename qfunc_t,
std::size_t num_fields>
MFEM_HOST_DEVICE inline
void call_qfunction_derivative(
qfunc_t &qfunc,
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
DeviceTensor<2> &residual_shmem,
DeviceTensor<5> &qpdc,
const DeviceTensor<1, const real_t> &itod,
const int &das_qp,
const int &q)
{
const int test_vdim = qpdc.GetShape()[0];
const int test_op_dim = qpdc.GetShape()[1];
const int trial_vdim = qpdc.GetShape()[2];
const int num_qp = qpdc.GetShape()[4];
const size_t num_inputs = itod.GetShape()[0];
for (int j = 0; j < trial_vdim; j++)
{
int m_offset = 0;
for (size_t s = 0; s < num_inputs; s++)
{
const int trial_op_dim = static_cast<int>(itod(s));
if (trial_op_dim == 0)
{
continue;
}
auto d_qp = Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
for (int m = 0; m < trial_op_dim; m++)
{
d_qp(j, m, q) = 1.0;
auto r = Reshape(&residual_shmem(0, q), das_qp);
auto qf_args = decay_tuple<qf_param_ts> {};
#ifdef MFEM_USE_ENZYME
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
shadow_shmem, q);
#else
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
#endif
d_qp(j, m, q) = 0.0;
auto f = Reshape(&r(0), test_vdim, test_op_dim);
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
qpdc(i, k, j, m + m_offset, q) = f(i, k);
}
}
}
m_offset += trial_op_dim;
}
}
}
}
/// @brief Call a qfunction with the given parameters and
/// compute it's derivative represented by the Jacobian on
/// each quadrature point.
///
/// @param qfunc the qfunction to call.
/// @param input_shmem the input shared memory.
/// @param shadow_shmem the shadow shared memory.
/// @param residual_shmem the residual shared memory.
/// @param qpdc the quadrature point data cache holding the resulting
/// Jacobians on each quadrature point.
/// @param itod inputs trial operator dimension.
/// If input is dependent the value corresponds to the spatial dimension, otherwise
/// a zero indicates non-dependence on the variable.
/// @param das_qp the size of the derivative action.
/// @param q1d the number of quadrature points in 1D.
/// @param dimension the spatial dimension.
/// @param use_sum_factorization whether to use sum factorization.
/// @tparam qf_param_ts the tuple type of the qfunction parameters.
template <
typename qf_param_ts,
typename qfunc_t,
std::size_t num_fields>
MFEM_HOST_DEVICE inline
void call_qfunction_derivative(
qfunc_t &qfunc,
const std::array<DeviceTensor<2>, num_fields> &input_shmem,
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
DeviceTensor<2> &residual_shmem,
DeviceTensor<5> &qpdc,
const DeviceTensor<1, const real_t> &itod,
const int &das_qp,
const int &q1d,
const int &dimension,
const bool &use_sum_factorization)
{
if (use_sum_factorization)
{
if (dimension == 1)
{
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
{
detail::call_qfunction_derivative<qf_param_ts>(
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
}
}
else if (dimension == 2)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
{
const int q = qx + q1d * qy;
detail::call_qfunction_derivative<qf_param_ts>(
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
}
}
}
else if (dimension == 3)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
detail::call_qfunction_derivative<qf_param_ts>(
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
}
}
}
}
else
{
MFEM_ABORT_KERNEL("unsupported dimension");
}
}
else
{
const int num_qp = qpdc.GetShape()[4];
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
{
detail::call_qfunction_derivative<qf_param_ts>(
qfunc, input_shmem, shadow_shmem, residual_shmem, qpdc, itod, das_qp, q);
}
}
MFEM_SYNC_THREAD;
}
namespace detail
{
/// @brief Apply the quadrature point data cache (qpdc) to a vector
/// (usually a direction) on quadrature point q.
///
/// The qpdc consists of compatible data to be used for integration with a test
/// operator, e.g. Jacobians of a linearization from a FE operation with a trial
/// function including integration weights and necessesary transformations.
///
/// @param fhat the qpdc applied to a vector in shadow_memory.
/// @param shadow_shmem the shadow shared memory.
/// @param qpdc the quadrature point data cache holding the resulting
/// Jacobians on each quadrature point.
/// @param itod inputs trial operator dimension.
/// If input is dependent the value corresponds to the spatial dimension, otherwise
/// a zero indicates non-dependence on the variable.
/// @param q the current quadrature point index.
template <size_t num_fields>
MFEM_HOST_DEVICE inline
void apply_qpdc(
DeviceTensor<3> &fhat,
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
const DeviceTensor<5, const real_t> &qpdc,
const DeviceTensor<1, const real_t> &itod,
const int &q)
{
const int test_vdim = qpdc.GetShape()[0];
const int test_op_dim = qpdc.GetShape()[1];
const int trial_vdim = qpdc.GetShape()[2];
const int num_qp = qpdc.GetShape()[4];
const size_t num_inputs = itod.GetShape()[0];
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
real_t sum = 0.0;
int m_offset = 0;
for (size_t s = 0; s < num_inputs; s++)
{
const int trial_op_dim = static_cast<int>(itod(s));
if (trial_op_dim == 0)
{
continue;
}
const auto d_qp =
Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
for (int j = 0; j < trial_vdim; j++)
{
for (int m = 0; m < trial_op_dim; m++)
{
sum += qpdc(i, k, j, m + m_offset, q) * d_qp(j, m, q);
}
}
m_offset += trial_op_dim;
}
fhat(i, k, q) = sum;
}
}
}
}
/// @brief Apply the quadrature point data cache (qpdc) to a vector
/// (usually a direction).
///
/// The qpdc consists of compatible data to be used for integration with a test
/// operator, e.g. Jacobians of a linearization from a FE operation with a trial
/// function including integration weights and necessesary transformations.
///
/// @param fhat the qpdc applied to a vector in shadow_memory.
/// @param shadow_shmem the shadow shared memory.
/// @param qpdc the quadrature point data cache holding the resulting
/// Jacobians on each quadrature point.
/// @param itod inputs trial operator dimension.
/// If input is dependent the value corresponds to the spatial dimension, otherwise
/// a zero indicates non-dependence on the variable.
/// @param q1d number of quadrature points in 1D.
/// @param dimension spatial dimension.
/// @param use_sum_factorization whether to use sum factorization.
template <size_t num_fields>
MFEM_HOST_DEVICE inline
void apply_qpdc(
DeviceTensor<3> &fhat,
const std::array<DeviceTensor<2>, num_fields> &shadow_shmem,
const DeviceTensor<5, const real_t> &qpdc,
const DeviceTensor<1, const real_t> &itod,
const int &q1d,
const int &dimension,
const bool &use_sum_factorization)
{
if (use_sum_factorization)
{
if (dimension == 1)
{
MFEM_FOREACH_THREAD_DIRECT(q, x, q1d)
{
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
else if (dimension == 2)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
{
const int q = qx + q1d * qy;
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
}
else if (dimension == 3)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(qz, z, q1d)
{
const int q = qx + q1d * (qy + q1d * qz);
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
}
}
else
{
MFEM_ABORT_KERNEL("unsupported dimension");
}
}
else
{
const int num_qp = qpdc.GetShape()[4];
MFEM_FOREACH_THREAD_DIRECT(q, x, num_qp)
{
detail::apply_qpdc(fhat, shadow_shmem, qpdc, itod, q);
}
}
}
+13 -5
View File
@@ -44,7 +44,7 @@ void process_qf_arg(
{
for (int j = 0; j < n; j++)
{
arg(j, i).value = u((i * m) + j);
arg(j, i).value = u((i * n) + j);
}
}
}
@@ -94,8 +94,8 @@ void process_qf_arg(
{
for (int j = 0; j < n; j++)
{
arg(j, i).value = u((i * m) + j);
arg(j, i).gradient = v((i * m) + j);
arg(j, i).value = u((i * n) + j);
arg(j, i).gradient = v((i * n) + j);
}
}
}
@@ -181,6 +181,14 @@ void process_derivative_from_native_dual(
}
}
template <typename T>
MFEM_HOST_DEVICE inline
void process_derivative_from_native_dual(
DeviceTensor<1, T> &r,
const dual<T, T> &x)
{
r(0) = x.gradient;
}
template <typename T0, typename T1>
MFEM_HOST_DEVICE inline
@@ -230,7 +238,7 @@ void process_qf_arg(
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * m) + j);
arg(j, i) = u((i * n) + j);
}
}
}
@@ -330,7 +338,7 @@ void process_qf_arg(
{
for (int j = 0; j < n; j++)
{
arg(j, i) = u((i * m) + j);
arg(j, i) = u((i * n) + j);
}
}
}
+3 -3
View File
@@ -454,7 +454,7 @@ MFEM_HOST_DEVICE constexpr auto operator+=(tuple<T...>& x,
*
* @tparam T the types stored in the tuples x and y
* @tparam i integer sequence used to index the tuples
* @param x tuple of values to be subracted from
* @param x tuple of values to be subtracted from
* @param y tuple of values to subtract from x
*/
template <typename... T, int... i>
@@ -596,7 +596,7 @@ MFEM_HOST_DEVICE constexpr auto div_helper(const real_t a,
* @tparam T the types stored in the tuple y
* @tparam i The integer sequence to i
* @param x tuple of values
* @param a the constant denomenator
* @param a the constant denominator
* @return the returned tuple ratio
*/
template <typename... T, int... i>
@@ -726,7 +726,7 @@ MFEM_HOST_DEVICE constexpr auto operator*(const tuple<T...>& x, const real_t a)
/**
* @tparam T the types stored in the tuple
* @tparam i a list of indices used to acces each element of the tuple
* @tparam i a list of indices used to access each element of the tuple
* @param out the ostream to write the output to
* @param A the tuple of values
* @brief helper used to implement printing a tuple of values
+136 -39
View File
@@ -20,6 +20,7 @@
#include <vector>
#include <type_traits>
#include <numeric>
#include <iomanip>
#include "../../general/communication.hpp"
#include "../../general/forall.hpp"
@@ -107,25 +108,32 @@ constexpr void for_constexpr_with_arg(lambda&& f, arg_t&& arg)
indices{});
}
template <typename... input_ts, std::size_t... Is>
auto make_dependency_map_impl(
tuple<input_ts...> inputs,
std::index_sequence<Is...>)
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...>)
{
auto make_dependency_array = [&](auto i)
{
return std::array<bool, sizeof...(input_ts)>
{
(get<i>(inputs).GetFieldId() == get<Is>(inputs).GetFieldId())...
};
};
return { (get<I>(inputs).GetFieldId() == get<Is>(inputs).GetFieldId())... };
}
std::unordered_map<int, std::array<bool, sizeof...(input_ts)>> map;
for_constexpr<sizeof...(input_ts)>([&](auto i)
template <typename... input_ts, std::size_t... Is>
auto make_dependency_map_impl(tuple<input_ts...> inputs,
std::index_sequence<Is...>)
{
constexpr std::size_t N = sizeof...(input_ts);
if constexpr (N == 0)
return std::unordered_map<int, std::array<bool, 0>> {};
std::unordered_map<int, std::array<bool, N>> map;
(void)std::initializer_list<int>
{
map[get<i>(inputs).GetFieldId()] =
make_dependency_array(std::integral_constant<std::size_t, i> {});
});
(
map[get<Is>(inputs).GetFieldId()] =
make_dependency_array<Is>(inputs, std::make_index_sequence<N>{}),
0
)...
};
return map;
}
@@ -200,24 +208,45 @@ void print_tuple(const std::tuple<Args...>& t)
/// ..., vmn]]
/// which is compatible with numpy syntax.
///
/// @param m mfem::DenseMatrix to print
/// @param out ostream to print to
/// @param A mfem::DenseMatrix to print
inline
void pretty_print(const mfem::DenseMatrix& m)
void pretty_print(std::ostream &out, const mfem::DenseMatrix &A)
{
out << "[";
for (int i = 0; i < m.NumRows(); i++)
// Determine the max width of any entry in scientific notation
int max_width = 0;
for (int i = 0; i < A.NumRows(); ++i)
{
for (int j = 0; j < m.NumCols(); j++)
for (int j = 0; j < A.NumCols(); ++j)
{
out << m(i, j);
if (j < m.NumCols() - 1)
std::ostringstream oss;
oss << std::scientific << std::setprecision(2) << A(i, j);
max_width = std::max(max_width, static_cast<int>(oss.str().length()));
}
}
out << "[\n";
for (int i = 0; i < A.NumRows(); ++i)
{
out << " [";
for (int j = 0; j < A.NumCols(); ++j)
{
out << std::setw(max_width) << std::scientific << std::setprecision(2) <<
A(i, j);
if (j < A.NumCols() - 1)
{
out << ", ";
}
}
if (i < m.NumRows() - 1)
out << "]";
if (i < A.NumRows() - 1)
{
out << ", ";
out << ",\n";
}
else
{
out << "\n";
}
}
out << "]\n";
@@ -356,7 +385,7 @@ void print_mpi_sync(const std::string& msg)
else
{
// Other ranks: Send message to rank 0
MPI_Send(msg.c_str(), static_cast<int>(msg_len), MPI_CHAR,
MPI_Send(const_cast<char*>(msg.c_str()), static_cast<int>(msg_len), MPI_CHAR,
0, 0, MPI_COMM_WORLD);
}
@@ -944,7 +973,44 @@ const Operator *get_element_restriction(const FieldDescriptor &f,
}
else
{
static_assert(dfem::always_false<T>, "can't use GetElementRestriction on type");
static_assert(dfem::always_false<T>,
"can't use get_element_restriction on type");
}
return nullptr; // Unreachable, but avoids compiler warning
}, f.data);
}
/// @brief Get the face restriction operator for a field descriptor.
///
/// @param f the field descriptor.
/// @param o the face dof ordering.
/// @param ft the face type
/// @param m indicator if single or double valued
/// @returns the face restriction operator for the field descriptor in
/// specified ordering.
inline
const Operator *get_face_restriction(const FieldDescriptor &f,
ElementDofOrdering o,
FaceType ft,
L2FaceValues m)
{
return std::visit([&o, &ft, &m](auto&& arg) -> const Operator*
{
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->GetFaceRestriction(o, ft, m);
}
else if constexpr (std::is_same_v<T, const ParameterSpace *>)
{
// ParameterSpace does not support face restrictions
MFEM_ABORT("internal error");
}
else
{
static_assert(dfem::always_false<T>,
"can't use get_face_restriction on type");
}
return nullptr; // Unreachable, but avoids compiler warning
}, f.data);
@@ -965,16 +1031,44 @@ const Operator *get_restriction(const FieldDescriptor &f,
{
return get_element_restriction(f, o);
}
else if constexpr (std::is_same_v<entity_t, Entity::BoundaryElement>)
{
return get_face_restriction(f, o, FaceType::Boundary,
L2FaceValues::SingleValued);
}
MFEM_ABORT("restriction not implemented for Entity");
return nullptr;
}
template <typename entity_t>
inline int get_num_entities(const FieldDescriptor& f)
{
if constexpr (std::is_same_v<entity_t, Entity::Element>) {
return std::visit(
[](auto&& arg) {
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->GetNE();
} else if constexpr (std::is_same_v<T, const ParameterSpace*>) {
// TODO: implement GetNE() on ParameterSpace
return arg->GetNE();
} else {
static_assert(dfem::always_false<T>, "can't use get_num_entities on type");
}
return 0; // Unreachable, but avoids compiler warning
},
f.data);
}
MFEM_ABORT("get_num_entities not implemented for Entity");
}
/// @brief Get a transpose restriction callback for a field descriptor.
///
/// @param f the field descriptor.
/// @param o the element dof ordering.
/// @param fop the field operator.
/// @returns a tuple containting a std::function with the transpose
/// @returns a tuple containing a std::function with the transpose
/// restriction callback and it's height.
template <typename entity_t, typename fop_t>
inline std::tuple<std::function<void(const Vector&, Vector&)>, int>
@@ -987,9 +1081,10 @@ get_restriction_transpose(
{
auto RT = [=](const Vector &v_e, Vector &v_l)
{
v_l += v_e;
v_l += v_e.Sum();
};
return std::make_tuple(RT, 1);
auto n_el = get_num_entities<entity_t>(f);
return std::make_tuple(RT, n_el);
}
else
{
@@ -1116,7 +1211,9 @@ std::function<void(const Vector&, Vector&)> get_prolongation_transpose(
{
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);
// NOTE: This doesn't work for a DerivativeOperator. You need to apply prolongation transpose on the field,
// since the DOF IDs are different across ranks.
// MPI_Allreduce(&local_sum, y.GetData(), 1, MPI_DOUBLE, MPI_SUM, mpi_comm);
};
return PT;
}
@@ -1362,12 +1459,12 @@ int GetSizeOnQP(const field_operator_t &, const FieldDescriptor &f)
/// @tparam entity_t the entity type (see Entity).
/// @returns an array mapping field operator types to field descriptor indices.
template <typename entity_t, typename field_operator_ts>
std::array<int, tuple_size<field_operator_ts>::value>
std::array<size_t, tuple_size<field_operator_ts>::value>
create_descriptors_to_fields_map(
const std::vector<FieldDescriptor> &fields,
field_operator_ts &fops)
{
std::array<int, tuple_size<field_operator_ts>::value> map;
std::array<size_t, tuple_size<field_operator_ts>::value> map;
auto find_id = [](const std::vector<FieldDescriptor> &fields, std::size_t i)
{
@@ -1379,9 +1476,9 @@ create_descriptors_to_fields_map(
if (it == fields.end())
{
return -1;
return SIZE_MAX;
}
return static_cast<int>(it - fields.begin());
return static_cast<size_t>(it - fields.begin());
};
auto f = [&](auto &fop, auto &map)
@@ -1389,10 +1486,10 @@ create_descriptors_to_fields_map(
if constexpr (std::is_same_v<std::decay_t<decltype(fop)>, Weight>)
{
// TODO-bug: stealing dimension from the first field
fop.dim = GetDimension<Entity::Element>(fields[0]);
fop.dim = GetDimension<entity_t>(fields[0]);
fop.vdim = 1;
fop.size_on_qp = 1;
map = -1;
map = SIZE_MAX;
}
else
{
@@ -2178,7 +2275,7 @@ template <
std::array<DofToQuadMap, N> create_dtq_maps_impl(
field_operator_ts &fops,
std::vector<const DofToQuad*> &dtqs,
const std::array<int, N> &field_map,
const std::array<size_t, N> &field_map,
std::index_sequence<Is...>)
{
auto f = [&](auto fop, std::size_t idx)
@@ -2263,7 +2360,7 @@ template <
std::array<DofToQuadMap, num_fields> create_dtq_maps(
field_operator_ts &fops,
std::vector<const DofToQuad*> &dtqmaps,
const std::array<int, num_fields> &to_field_map)
const std::array<size_t, num_fields> &to_field_map)
{
return create_dtq_maps_impl<entity_t>(
fops, dtqmaps,
+59 -42
View File
@@ -661,65 +661,78 @@ void ScalarFiniteElement::ScalarLocalL2Restriction(
void NodalFiniteElement::CreateLexicographicFullMap(const IntegrationRule &ir)
const
{
// Get the FULL version of the map.
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
//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++)
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (DofToQuad)
#endif
{
for (int d = 0; d < b_dim; d++)
// Get the FULL version of the map.
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
//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);
}
}
const DofToQuad &NodalFiniteElement::GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const
{
//Should make this loop a function of FiniteElement
for (int i = 0; i < dof2quad_array.Size(); i++)
DofToQuad *d2q = nullptr;
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (DofToQuad)
#endif
{
const DofToQuad &d2q = *dof2quad_array[i];
if (d2q.IntRule == &ir && d2q.mode == mode) { return d2q; }
//Should make this loop a function of FiniteElement
for (int i = 0; i < dof2quad_array.Size(); i++)
{
d2q = dof2quad_array[i];
if (d2q->IntRule == &ir && d2q->mode == mode) { break; }
d2q = nullptr;
}
}
if (d2q) { return *d2q; }
if (mode != DofToQuad::LEXICOGRAPHIC_FULL)
{
return FiniteElement::GetDofToQuad(ir, mode);
@@ -2620,8 +2633,12 @@ const DofToQuad &TensorBasisElement::GetTensorDofToQuad(
{
for (int i = 0; i < dof2quad_array.Size(); i++)
{
d2q = dof2quad_array[i];
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
auto* d2q_ = dof2quad_array[i];
if (d2q_->IntRule == &ir && d2q_->mode == mode)
{
d2q = d2q_;
break;
}
}
if (!d2q)
{
+33 -9
View File
@@ -308,13 +308,25 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
FiniteElement::INTEGRAL,
BasisType::GetType(name[12]));
}
else if (!strncmp(name, "RT_R1D",6))
else if (!strncmp(name, "RT_R1D_", 7))
{
fec = new RT_R1D_FECollection(atoi(name+11),atoi(name + 7));
fec = new RT_R1D_FECollection(atoi(name + 11), atoi(name + 7));
}
else if (!strncmp(name, "RT_R2D",6))
else if (!strncmp(name, "RT_R1D@", 7))
{
fec = new RT_R2D_FECollection(atoi(name+11),atoi(name + 7));
fec = new RT_R1D_FECollection(atoi(name + 14), atoi(name + 10),
BasisType::GetType(name[7]),
BasisType::GetType(name[8]));
}
else if (!strncmp(name, "RT_R2D_", 7))
{
fec = new RT_R2D_FECollection(atoi(name + 11), atoi(name + 7));
}
else if (!strncmp(name, "RT_R2D@", 7))
{
fec = new RT_R2D_FECollection(atoi(name + 14), atoi(name + 10),
BasisType::GetType(name[7]),
BasisType::GetType(name[8]));
}
else if (!strncmp(name, "RT_", 3))
{
@@ -336,13 +348,25 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
BasisType::GetType(name[9]),
BasisType::GetType(name[10]));
}
else if (!strncmp(name, "ND_R1D",6))
else if (!strncmp(name, "ND_R1D_", 7))
{
fec = new ND_R1D_FECollection(atoi(name+11),atoi(name + 7));
fec = new ND_R1D_FECollection(atoi(name + 11), atoi(name + 7));
}
else if (!strncmp(name, "ND_R2D",6))
else if (!strncmp(name, "ND_R1D@", 7))
{
fec = new ND_R2D_FECollection(atoi(name+11),atoi(name + 7));
fec = new ND_R1D_FECollection(atoi(name + 14), atoi(name + 10),
BasisType::GetType(name[7]),
BasisType::GetType(name[8]));
}
else if (!strncmp(name, "ND_R2D_", 7))
{
fec = new ND_R2D_FECollection(atoi(name + 11), atoi(name + 7));
}
else if (!strncmp(name, "ND_R2D@", 7))
{
fec = new ND_R2D_FECollection(atoi(name + 14), atoi(name + 10),
BasisType::GetType(name[7]),
BasisType::GetType(name[8]));
}
else if (!strncmp(name, "ND_", 3))
{
@@ -485,7 +509,7 @@ GetFace(int &nv, v_t &v, int &ne, e_t &e, eo_t &eo,
int v0 = v[f_consts::Edges[i][0]];
int v1 = v[f_consts::Edges[i][1]];
int eor = 0;
if (v0 > v1) { swap(v0, v1); eor = 1; }
if (v0 > v1) { std::swap(v0, v1); eor = 1; }
for (int j = g_consts::VertToVert::I[v0]; true; j++)
{
MFEM_ASSERT(j < g_consts::VertToVert::I[v0+1],
+4
View File
@@ -120,7 +120,9 @@ public:
| ND_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
| ND_R1D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
| ND_R1D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
| ND_R2D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
| ND_R2D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
| RT_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | Raviart-Thomas vector elements |
| RT@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | Raviart-Thomas vector elements |
| RT_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
@@ -128,7 +130,9 @@ public:
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_R1D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
| RT_R1D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
| RT_R2D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
| RT_R2D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
| L2_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
| L2Int_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
+1
View File
@@ -50,6 +50,7 @@
#include "dgmassinv.hpp"
#include "hyperbolic.hpp"
#include "bounds.hpp"
#include "particleset.hpp"
#include "dfem/doperator.hpp"
+15 -31
View File
@@ -27,37 +27,6 @@ using namespace std;
namespace mfem
{
template <>
void Ordering::DofsToVDofs<Ordering::byNODES>(int ndofs, int vdim,
Array<int> &dofs)
{
// static method
int size = dofs.Size();
dofs.SetSize(size*vdim);
for (int vd = 1; vd < vdim; vd++)
{
for (int i = 0; i < size; i++)
{
dofs[i+size*vd] = Map<byNODES>(ndofs, vdim, dofs[i], vd);
}
}
}
template <>
void Ordering::DofsToVDofs<Ordering::byVDIM>(int ndofs, int vdim,
Array<int> &dofs)
{
// static method
int size = dofs.Size();
dofs.SetSize(size*vdim);
for (int vd = vdim-1; vd >= 0; vd--)
{
for (int i = 0; i < size; i++)
{
dofs[i+size*vd] = Map<byVDIM>(ndofs, vdim, dofs[i], vd);
}
}
}
FiniteElementSpace::FiniteElementSpace()
: mesh(NULL), fec(NULL), vdim(0), ordering(Ordering::byNODES),
@@ -1583,6 +1552,11 @@ const FaceRestriction *FiniteElementSpace::GetFaceRestriction(
const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
const IntegrationRule &ir) const
{
if (!QuadratureInterpolator::SupportsFESpace(*this))
{
return nullptr;
}
for (int i = 0; i < E2Q_array.Size(); i++)
{
const QuadratureInterpolator *qi = E2Q_array[i];
@@ -1597,6 +1571,11 @@ const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
const QuadratureInterpolator *FiniteElementSpace::GetQuadratureInterpolator(
const QuadratureSpace &qs) const
{
if (!QuadratureInterpolator::SupportsFESpace(*this))
{
return nullptr;
}
for (int i = 0; i < E2Q_array.Size(); i++)
{
const QuadratureInterpolator *qi = E2Q_array[i];
@@ -1612,6 +1591,11 @@ const FaceQuadratureInterpolator
*FiniteElementSpace::GetFaceQuadratureInterpolator(
const IntegrationRule &ir, FaceType type) const
{
if (!FaceQuadratureInterpolator::SupportsFESpace(*this))
{
return nullptr;
}
if (type==FaceType::Interior)
{
for (int i = 0; i < E2IFQ_array.Size(); i++)
+13 -40
View File
@@ -13,6 +13,7 @@
#define MFEM_FESPACE
#include "../config/config.hpp"
#include "../linalg/ordering.hpp"
#include "../linalg/sparsemat.hpp"
#include "../mesh/mesh.hpp"
#include "fe_coll.hpp"
@@ -24,29 +25,6 @@
namespace mfem
{
/** @brief The ordering method used when the number of unknowns per mesh node
(vector dimension) is bigger than 1. */
class Ordering
{
public:
/// %Ordering methods:
enum Type
{
byNODES, /**< loop first over the nodes (inner loop) then over the vector
dimension (outer loop); symbolically it can be represented
as: XXX...,YYY...,ZZZ... */
byVDIM /**< loop first over the vector dimension (inner loop) then over
the nodes (outer loop); symbolically it can be represented
as: XYZ,XYZ,XYZ,... */
};
template <Type Ord>
static inline int Map(int ndofs, int vdim, int dof, int vd);
template <Type Ord>
static void DofsToVDofs(int ndofs, int vdim, Array<int> &dofs);
};
/// @brief Type describing possible layouts for Q-vectors.
/// @sa QuadratureInterpolator and FaceQuadratureInterpolator.
enum class QVectorLayout
@@ -64,20 +42,6 @@ enum class QVectorLayout
byVDIM
};
template <> inline int
Ordering::Map<Ordering::byNODES>(int ndofs, int vdim, int dof, int vd)
{
MFEM_ASSERT(dof < ndofs && -1-dof < ndofs && 0 <= vd && vd < vdim, "");
return (dof >= 0) ? dof+ndofs*vd : dof-ndofs*vd;
}
template <> inline int
Ordering::Map<Ordering::byVDIM>(int ndofs, int vdim, int dof, int vd)
{
MFEM_ASSERT(dof < ndofs && -1-dof < ndofs && 0 <= vd && vd < vdim, "");
return (dof >= 0) ? vd+vdim*dof : -1-(vd+vdim*(-1-dof));
}
/// Constants describing the possible orderings of the DOFs in one element.
enum class ElementDofOrdering
{
@@ -799,7 +763,10 @@ public:
@note The returned pointer is shared. A good practice, before using it,
is to set all its properties to their expected values, as other parts of
the code may also change them. That is, it's good to call
SetOutputLayout() and DisableTensorProducts() before interpolating. */
SetOutputLayout() and DisableTensorProducts() before interpolating.
@note If the space is not supported by QuadratureInterpolator, nullptr is
returned. */
const QuadratureInterpolator *GetQuadratureInterpolator(
const IntegrationRule &ir) const;
@@ -815,7 +782,10 @@ public:
@note The returned pointer is shared. A good practice, before using it,
is to set all its properties to their expected values, as other parts of
the code may also change them. That is, it's good to call
SetOutputLayout() and DisableTensorProducts() before interpolating. */
SetOutputLayout() and DisableTensorProducts() before interpolating.
@note If the space is not supported by QuadratureInterpolator, nullptr is
returned. */
const QuadratureInterpolator *GetQuadratureInterpolator(
const QuadratureSpace &qs) const;
@@ -825,7 +795,10 @@ public:
@note The returned pointer is shared. A good practice, before using it,
is to set all its properties to their expected values, as other parts of
the code may also change them. That is, it's good to call
SetOutputLayout() and DisableTensorProducts() before interpolating. */
SetOutputLayout() and DisableTensorProducts() before interpolating.
@note If the space is not supported by FaceQuadratureInterpolator,
nullptr is returned. */
const FaceQuadratureInterpolator *GetFaceQuadratureInterpolator(
const IntegrationRule &ir, FaceType type) const;
+7 -7
View File
@@ -4610,7 +4610,7 @@ GridFunction *Extrude1DGridFunction(Mesh *mesh, Mesh *mesh2d,
void GridFunction::GetElementBoundsAtControlPoints(const int elem,
const PLBound &plb,
Vector &lower, Vector &upper,
const int vdim)
const int vdim) const
{
const FiniteElement *fe = fes->GetFE(elem);
int fes_dim = fes->GetVDim();
@@ -4626,7 +4626,7 @@ void GridFunction::GetElementBoundsAtControlPoints(const int elem,
fes->GetElementDofs(elem, dof_idx);
int ndofs = dof_idx.Size();
int n_c_pts = std::pow(plb.GetNControlPoints(), rdim);
int n_c_pts = static_cast<int>(std::pow(plb.GetNControlPoints(), rdim));
lower.SetSize(n_c_pts*(vdim > 0 ? 1 : fes_dim));
upper.SetSize(n_c_pts*(vdim > 0 ? 1 : fes_dim));
@@ -4658,13 +4658,13 @@ void GridFunction::GetElementBoundsAtControlPoints(const int elem,
void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
Vector &lower, Vector &upper,
const int vdim)
const int vdim) const
{
Vector lowerC, upperC;
GetElementBoundsAtControlPoints(elem, plb, lowerC, upperC, vdim);
const FiniteElement *fe = fes->GetFE(elem);
int rdim = fe->GetDim();
int n_c_pts = std::pow(plb.GetNControlPoints(), rdim);
int n_c_pts = static_cast<int>(std::pow(plb.GetNControlPoints(), rdim));
int fes_dim = fes->GetVDim();
lower.SetSize((vdim > 0 ? 1 :fes_dim));
upper.SetSize((vdim > 0 ? 1 :fes_dim));
@@ -4681,7 +4681,7 @@ void GridFunction::GetElementBounds(const int elem, const PLBound &plb,
void GridFunction::GetElementBounds(const PLBound &plb,
Vector &lower, Vector &upper,
const int vdim)
const int vdim) const
{
int nel = fes->GetNE();
int fes_dim = fes->GetVDim();
@@ -4704,7 +4704,7 @@ void GridFunction::GetElementBounds(const PLBound &plb,
PLBound GridFunction::GetElementBounds(Vector &lower,
Vector &upper,
const int ref_factor,
const int vdim)
const int vdim) const
{
int max_order = fes->GetMaxElementOrder();
PLBound plb(fes, ref_factor*(max_order+1));
@@ -4713,7 +4713,7 @@ PLBound GridFunction::GetElementBounds(Vector &lower,
}
PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
const int ref_factor, const int vdim)
const int ref_factor, const int vdim) const
{
int max_order = fes->GetMaxElementOrder();
PLBound plb(fes, ref_factor*(max_order+1));
+5 -5
View File
@@ -1604,7 +1604,7 @@ public:
/// We compute the bounds for each vdim if @a 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 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
@@ -1614,27 +1614,27 @@ public:
/// PLBound object used to compute the bounds.
/// We compute the bounds for each vdim if @a vdim < 1.
PLBound GetElementBounds(Vector &lower, Vector &upper,
const int ref_factor=1, const int vdim=-1);
const int ref_factor=1, const int vdim=-1) const;
/// Compute piecewise linear bounds on the given element at the grid of
/// [plb.ncp x plb.ncp x plb.ncp] control points for each of the vdim
/// components of the gridfunction.
void GetElementBoundsAtControlPoints(const int elem, const PLBound &plb,
Vector &lower, Vector &upper,
const int vdim = -1);
const int vdim = -1) 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,
Vector &lower, Vector &upper,
const int vdim = -1);
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:
/// 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 int vdim=-1) const;
///@}
/// Destroys grid function.
+1
View File
@@ -324,6 +324,7 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
gsl_elem[i] = 0;
for (int d = 0; d < dim; d++) { gsl_ref(i*dim + d) = -1.; }
gsl_code[i] = 2;
gsl_proc[i] = gsl_comm->id;
}
}
+95 -64
View File
@@ -25,9 +25,9 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
const GeometricFactors &el_geom,
const FaceGeometricFactors &face_geom,
const FaceNeighborGeometricFactors *nbr_geom,
const Vector &q, const real_t sigma,
const real_t kappa, Vector &pa_data,
const Array<int> &face_info_)
const Vector &q, const int coeff_dim,
const real_t sigma, const real_t kappa,
Vector &pa_data, const Array<int> &face_info_)
{
const auto J_loc = Reshape(el_geom.J.Read(), Q1D, Q1D, 2, 2, NE);
const auto detJe_loc = Reshape(el_geom.detJ.Read(), Q1D, Q1D, NE);
@@ -41,9 +41,9 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
const auto detJf = Reshape(face_geom.detJ.Read(), Q1D, NF);
const auto n = Reshape(face_geom.normal.Read(), Q1D, 2, NF);
const bool const_q = (q.Size() == 1);
const auto Q =
const_q ? Reshape(q.Read(), 1, 1) : Reshape(q.Read(), Q1D, NF);
const bool const_q = (q.Size() == coeff_dim);
const auto Q = const_q ? Reshape(q.Read(), coeff_dim, 1, 1)
: Reshape(q.Read(), coeff_dim, Q1D, NF);
const auto W = w.Read();
@@ -53,6 +53,12 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
// (q, 1/h, J0_0, J0_1, J1_0, J1_1)
auto pa = Reshape(pa_data.Write(), 6, Q1D, NF);
auto get_coeff = [const_q] MFEM_HOST_DEVICE (const decltype(Q) &Q, int i,
int qx, int e)
{
return const_q ? Q(i,0,0) : Q(i,qx,e);
};
mfem::forall(NF, [=] MFEM_HOST_DEVICE(int f) -> void
{
const int normal_dir[] = {face_info(0, f), face_info(1, f)};
@@ -71,10 +77,26 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
for (int p = 0; p < Q1D; ++p)
{
const real_t Qp = const_q ? Q(0, 0) : Q(p, f);
pa(0, p, f) = kappa * Qp * W[p] * detJf(p, f);
real_t qh = 0.0;
real_t hi = 0.0;
real_t Qtn[2];
if (coeff_dim > 1)
{
// matrix coefficient
Qtn[0] = get_coeff(Q,0,p,f)*n(p,0,f) + get_coeff(Q,1,p,f)*n(p,1,f);
Qtn[1] = get_coeff(Q,2,p,f)*n(p,0,f) + get_coeff(Q,3,p,f)*n(p,1,f);
qh = Qtn[0]*n(p,0,f) + Qtn[1]*n(p,1,f);
}
else
{
qh = get_coeff(Q, 0, p, f);
Qtn[0] = qh*n(p,0,f);
Qtn[1] = qh*n(p,1,f);
}
pa(0, p, f) = kappa * qh * W[p] * detJf(p, f);
for (int side = 0; side < nsides; ++side)
{
int i, j;
@@ -89,15 +111,13 @@ static void PADGDiffusionSetup2D(const int Q1D, const int NE, const int NF,
const auto &detJ = (side == 1 && shared) ? detJ_shared : detJe_loc;
real_t nJi[2];
nJi[0] =
n(p, 0, f) * J(i, j, 1, 1, e) - n(p, 1, f) * J(i, j, 0, 1, e);
nJi[1] =
-n(p, 0, f) * J(i, j, 1, 0, e) + n(p, 1, f) * J(i, j, 0, 0, e);
nJi[0] = Qtn[0]*J(i, j, 1, 1, e) - Qtn[1]*J(i, j, 0, 1, e);
nJi[1] = -Qtn[0]*J(i, j, 1, 0, e) + Qtn[1]*J(i, j, 0, 0, e);
const real_t dJe = detJ(i, j, e);
const real_t dJf = detJf(p, f);
const real_t w = factor * Qp * W[p] * dJf / dJe;
const real_t w = factor * W[p] * dJf / dJe;
const int ni = normal_dir[side];
const int ti = 1 - ni;
@@ -126,9 +146,9 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
const GeometricFactors &el_geom,
const FaceGeometricFactors &face_geom,
const FaceNeighborGeometricFactors *nbr_geom,
const Vector &q, const real_t sigma,
const real_t kappa, Vector &pa_data,
const Array<int> &face_info_)
const Vector &q, const int coeff_dim,
const real_t sigma, const real_t kappa,
Vector &pa_data, const Array<int> &face_info_)
{
const auto J_loc = Reshape(el_geom.J.Read(), Q1D, Q1D, Q1D, 3, 3, NE);
const auto detJe_loc = Reshape(el_geom.detJ.Read(), Q1D, Q1D, Q1D, NE);
@@ -142,9 +162,9 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
const auto detJf = Reshape(face_geom.detJ.Read(), Q1D, Q1D, NF);
const auto n = Reshape(face_geom.normal.Read(), Q1D, Q1D, 3, NF);
const bool const_q = (q.Size() == 1);
const auto Q =
const_q ? Reshape(q.Read(), 1, 1, 1) : Reshape(q.Read(), Q1D, Q1D, NF);
const bool const_q = (q.Size() == coeff_dim);
const auto Q = const_q ? Reshape(q.Read(), coeff_dim, 1, 1, 1)
: Reshape(q.Read(), coeff_dim, Q1D, Q1D, NF);
const auto W = Reshape(w.Read(), Q1D, Q1D);
@@ -157,6 +177,12 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
// (J00, J01, J02, J10, J11, J12, q/h)
const auto pa = Reshape(pa_data.Write(), 7, Q1D, Q1D, NF);
auto get_coeff = [const_q] MFEM_HOST_DEVICE (const decltype(Q) &Q, int i,
int qx, int qy, int e)
{
return const_q ? Q(i,0,0,0) : Q(i,qx,qy,e);
};
mfem::forall_2D(NF, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int f) -> void
{
MFEM_SHARED int perm[2][3];
@@ -192,11 +218,32 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
{
MFEM_FOREACH_THREAD(p2, y, Q1D)
{
const real_t Qp = const_q ? Q(0, 0, 0) : Q(p1, p2, f);
const real_t dJf = detJf(p1, p2, f);
real_t hi = 0.0;
real_t Qtn[3];
real_t qh = 0.0;
if (coeff_dim > 1)
{
// matrix coefficient
for (int d = 0; d < 3; ++d)
{
Qtn[d] = get_coeff(Q,0+3*d,p1,p2,f)*n(p1,p2,0,f)
+ get_coeff(Q,1+3*d,p1,p2,f)*n(p1,p2,1,f)
+ get_coeff(Q,2+3*d,p1,p2,f)*n(p1,p2,2,f);
qh += Qtn[d] * n(p1,p2,d,f);
}
}
else
{
qh = get_coeff(Q,0,p1,p2,f);
Qtn[0] = qh * n(p1,p2,0,f);
Qtn[1] = qh * n(p1,p2,1,f);
Qtn[2] = qh * n(p1,p2,2,f);
}
for (int side = 0; side < nsides; ++side)
{
int i, j, k;
@@ -210,38 +257,29 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
// *INDENT-OFF*
real_t nJi[3];
nJi[0] = (-J(i, j, k, 1, 2, e) * J(i, j, k, 2, 1, e) +
J(i, j, k, 1, 1, e) * J(i, j, k, 2, 2, e)) *
n(p1, p2, 0, f) +
(J(i, j, k, 0, 2, e) * J(i, j, k, 2, 1, e) -
J(i, j, k, 0, 1, e) * J(i, j, k, 2, 2, e)) *
n(p1, p2, 1, f) +
(-J(i, j, k, 0, 2, e) * J(i, j, k, 1, 1, e) +
J(i, j, k, 0, 1, e) * J(i, j, k, 1, 2, e)) *
n(p1, p2, 2, f);
J(i, j, k, 1, 1, e) * J(i, j, k, 2, 2, e)) * Qtn[0] +
(J(i, j, k, 0, 2, e) * J(i, j, k, 2, 1, e) -
J(i, j, k, 0, 1, e) * J(i, j, k, 2, 2, e)) * Qtn[1] +
(-J(i, j, k, 0, 2, e) * J(i, j, k, 1, 1, e) +
J(i, j, k, 0, 1, e) * J(i, j, k, 1, 2, e)) * Qtn[2];
nJi[1] = (J(i, j, k, 1, 2, e) * J(i, j, k, 2, 0, e) -
J(i, j, k, 1, 0, e) * J(i, j, k, 2, 2, e)) *
n(p1, p2, 0, f) +
(-J(i, j, k, 0, 2, e) * J(i, j, k, 2, 0, e) +
J(i, j, k, 0, 0, e) * J(i, j, k, 2, 2, e)) *
n(p1, p2, 1, f) +
(J(i, j, k, 0, 2, e) * J(i, j, k, 1, 0, e) -
J(i, j, k, 0, 0, e) * J(i, j, k, 1, 2, e)) *
n(p1, p2, 2, f);
J(i, j, k, 1, 0, e) * J(i, j, k, 2, 2, e)) * Qtn[0] +
(-J(i, j, k, 0, 2, e) * J(i, j, k, 2, 0, e) +
J(i, j, k, 0, 0, e) * J(i, j, k, 2, 2, e)) * Qtn[1] +
(J(i, j, k, 0, 2, e) * J(i, j, k, 1, 0, e) -
J(i, j, k, 0, 0, e) * J(i, j, k, 1, 2, e)) * Qtn[2];
nJi[2] = (-J(i, j, k, 1, 1, e) * J(i, j, k, 2, 0, e) +
J(i, j, k, 1, 0, e) * J(i, j, k, 2, 1, e)) *
n(p1, p2, 0, f) +
(J(i, j, k, 0, 1, e) * J(i, j, k, 2, 0, e) -
J(i, j, k, 0, 0, e) * J(i, j, k, 2, 1, e)) *
n(p1, p2, 1, f) +
(-J(i, j, k, 0, 1, e) * J(i, j, k, 1, 0, e) +
J(i, j, k, 0, 0, e) * J(i, j, k, 1, 1, e)) *
n(p1, p2, 2, f);
J(i, j, k, 1, 0, e) * J(i, j, k, 2, 1, e)) * Qtn[0] +
(J(i, j, k, 0, 1, e) * J(i, j, k, 2, 0, e) -
J(i, j, k, 0, 0, e) * J(i, j, k, 2, 1, e)) * Qtn[1] +
(-J(i, j, k, 0, 1, e) * J(i, j, k, 1, 0, e) +
J(i, j, k, 0, 0, e) * J(i, j, k, 1, 1, e)) * Qtn[2];
// *INDENT-ON*
const real_t dJe = detJe(i, j, k, e);
const real_t val = factor * Qp * W(p1, p2) * dJf / dJe;
const real_t val = factor * W(p1, p2) * dJf / dJe;
for (int d = 0; d < 3; ++d)
{
@@ -260,7 +298,7 @@ static void PADGDiffusionSetup3D(const int Q1D, const int NE, const int NF,
pa(5, p1, p2, f) = 0.0;
}
pa(6, p1, p2, f) = kappa * hi * Qp * W(p1, p2) * dJf;
pa(6, p1, p2, f) = kappa * hi * qh * W(p1, p2) * dJf;
}
}
});
@@ -502,19 +540,12 @@ void DGDiffusionIntegrator::SetupPA(const FiniteElementSpace &fes,
// Evaluate the coefficient at the face quadrature points.
FaceQuadratureSpace fqs(mesh, ir, type);
CoefficientVector q(fqs, CoefficientStorage::COMPRESSED);
if (Q)
{
q.Project(*Q);
}
else if (MQ)
{
MFEM_ABORT("Not yet implemented"); /* q.Project(*MQ); */
}
else
{
q.SetConstant(1.0);
}
CoefficientVector q(fqs, CoefficientStorage::CONSTANTS);
if (Q) { q.Project(*Q); }
else if (MQ) { q.Project(*MQ); }
else { q.SetConstant(1.0); }
const int coeff_dim = q.GetVDim();
Array<int> face_info;
if (dim == 1)
@@ -525,15 +556,15 @@ void DGDiffusionIntegrator::SetupPA(const FiniteElementSpace &fes,
{
PADGDiffusionSetupFaceInfo2D(nf, mesh, type, face_info);
PADGDiffusionSetup2D(quad1D, ne, nf, ir.GetWeights(), *el_geom,
*face_geom, nbr_geom.get(), q, sigma, kappa, pa_data,
face_info);
*face_geom, nbr_geom.get(), q, coeff_dim, sigma,
kappa, pa_data, face_info);
}
else if (dim == 3)
{
PADGDiffusionSetupFaceInfo3D(nf, mesh, type, face_info);
PADGDiffusionSetup3D(quad1D, ne, nf, ir.GetWeights(), *el_geom,
*face_geom, nbr_geom.get(), q, sigma, kappa, pa_data,
face_info);
*face_geom, nbr_geom.get(), q, coeff_dim, sigma,
kappa, pa_data, face_info);
}
}
+40 -29
View File
@@ -134,14 +134,19 @@ void PADiffusionSetup2D<2>(const int Q1D,
Vector &d)
{
const bool symmetric = (coeffDim != 4);
const bool const_c = c.Size() == 1;
MFEM_VERIFY(coeffDim < 3 ||
!const_c, "Constant matrix coefficient not supported");
const bool const_c = c.Size() == coeffDim;
const auto W = Reshape(w.Read(), Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,2,2,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1) :
const auto C = const_c ? Reshape(c.Read(), coeffDim,1,1,1) :
Reshape(c.Read(), coeffDim,Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D, symmetric ? 3 : 4, NE);
auto get_coeff = [const_c] MFEM_HOST_DEVICE
(const decltype(C) &C, int i, int qx, int qy, int e)
{
return const_c ? C(i,0,0,0) : C(i,qx,qy,e);
};
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
@@ -156,10 +161,11 @@ void PADiffusionSetup2D<2>(const int Q1D,
if (coeffDim == 3 || coeffDim == 4) // Matrix coefficient
{
// First compute entries of R = MJ^{-T}, without det J factor.
const real_t M11 = C(0,qx,qy,e);
const real_t M12 = C(1,qx,qy,e);
const real_t M21 = symmetric ? M12 : C(2,qx,qy,e);
const real_t M22 = symmetric ? C(2,qx,qy,e) : C(3,qx,qy,e);
const real_t M11 = get_coeff(C,0,qx,qy,e);
const real_t M12 = get_coeff(C,1,qx,qy,e);
const real_t M21 = symmetric ? M12 : get_coeff(C,2,qx,qy,e);
const real_t M22 = symmetric ? get_coeff(C,2,qx,qy,e)
: get_coeff(C,3,qx,qy,e);
const real_t R11 = M11*J22 - M12*J12;
const real_t R21 = M21*J22 - M22*J12;
const real_t R12 = -M11*J21 + M12*J11;
@@ -177,9 +183,8 @@ void PADiffusionSetup2D<2>(const int Q1D,
}
else // Vector or scalar coefficient
{
const real_t C1 = const_c ? C(0,0,0,0) : C(0,qx,qy,e);
const real_t C2 = const_c ? C(0,0,0,0) :
(coeffDim == 2 ? C(1,qx,qy,e) : C(0,qx,qy,e));
const real_t C1 = get_coeff(C,0,qx,qy,e);
const real_t C2 = get_coeff(C,coeffDim==2?1:0,qx,qy,e);
D(qx,qy,0,e) = w_detJ * (C2*J12*J12 + C1*J22*J22); // 1,1
D(qx,qy,1,e) = -w_detJ * (C2*J12*J11 + C1*J22*J21); // 1,2
@@ -244,14 +249,19 @@ void PADiffusionSetup3D(const int Q1D,
Vector &d)
{
const bool symmetric = (coeffDim != 9);
const bool const_c = c.Size() == 1;
MFEM_VERIFY(coeffDim < 6 ||
!const_c, "Constant matrix coefficient not supported");
const bool const_c = c.Size() == coeffDim;
const auto W = Reshape(w.Read(), Q1D,Q1D,Q1D);
const auto J = Reshape(j.Read(), Q1D,Q1D,Q1D,3,3,NE);
const auto C = const_c ? Reshape(c.Read(), 1,1,1,1,1) :
const auto C = const_c ? Reshape(c.Read(), coeffDim,1,1,1,1) :
Reshape(c.Read(), coeffDim,Q1D,Q1D,Q1D,NE);
auto D = Reshape(d.Write(), Q1D,Q1D,Q1D, symmetric ? 6 : 9, NE);
auto get_coeff = [const_c] MFEM_HOST_DEVICE
(const decltype(C) &C, int i, int qx, int qy, int qz, int e)
{
return const_c ? C(i,0,0,0,0) : C(i,qx,qy,qz,e);
};
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(qx,x,Q1D)
@@ -287,15 +297,18 @@ void PADiffusionSetup3D(const int Q1D,
if (coeffDim == 6 || coeffDim == 9) // Matrix coefficient version
{
// Compute entries of R = MJ^{-T} = M adj(J)^T, without det J.
const real_t M11 = C(0, qx,qy,qz, e);
const real_t M12 = C(1, qx,qy,qz, e);
const real_t M13 = C(2, qx,qy,qz, e);
const real_t M21 = (!symmetric) ? C(3, qx,qy,qz, e) : M12;
const real_t M22 = (!symmetric) ? C(4, qx,qy,qz, e) : C(3, qx,qy,qz, e);
const real_t M23 = (!symmetric) ? C(5, qx,qy,qz, e) : C(4, qx,qy,qz, e);
const real_t M31 = (!symmetric) ? C(6, qx,qy,qz, e) : M13;
const real_t M32 = (!symmetric) ? C(7, qx,qy,qz, e) : M23;
const real_t M33 = (!symmetric) ? C(8, qx,qy,qz, e) : C(5, qx,qy,qz, e);
const real_t M11 = get_coeff(C, 0, qx,qy,qz, e);
const real_t M12 = get_coeff(C, 1, qx,qy,qz, e);
const real_t M13 = get_coeff(C, 2, qx,qy,qz, e);
const real_t M21 = (!symmetric) ? get_coeff(C, 3, qx,qy,qz, e) : M12;
const real_t M22 = (!symmetric) ? get_coeff(C, 4, qx,qy,qz, e)
: get_coeff(C, 3, qx,qy,qz, e);
const real_t M23 = (!symmetric) ? get_coeff(C, 5, qx,qy,qz, e)
: get_coeff(C, 4, qx,qy,qz, e);
const real_t M31 = (!symmetric) ? get_coeff(C, 6, qx,qy,qz, e) : M13;
const real_t M32 = (!symmetric) ? get_coeff(C, 7, qx,qy,qz, e) : M23;
const real_t M33 = (!symmetric) ? get_coeff(C, 8, qx,qy,qz, e)
: get_coeff(C, 5, qx,qy,qz, e);
const real_t R11 = M11*A11 + M12*A12 + M13*A13;
const real_t R12 = M11*A21 + M12*A22 + M13*A23;
@@ -335,11 +348,9 @@ void PADiffusionSetup3D(const int Q1D,
}
else // Vector or scalar coefficient version
{
const real_t C1 = const_c ? C(0,0,0,0,0) : C(0,qx,qy,qz,e);
const real_t C2 = const_c ? C(0,0,0,0,0) :
(coeffDim == 3 ? C(1,qx,qy,qz,e) : C(0,qx,qy,qz,e));
const real_t C3 = const_c ? C(0,0,0,0,0) :
(coeffDim == 3 ? C(2,qx,qy,qz,e) : C(0,qx,qy,qz,e));
const real_t C1 = get_coeff(C,0,qx,qy,qz,e);
const real_t C2 = get_coeff(C,coeffDim==3?1:0,qx,qy,qz,e);
const real_t C3 = get_coeff(C,coeffDim==3?2:0,qx,qy,qz,e);
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
D(qx,qy,qz,0,e) = w_detJ * (C1*A11*A11 + C2*A12*A12 + C3*A13*A13); // 1,1
+5 -5
View File
@@ -201,7 +201,7 @@ inline void SmemPADiffusionDiagonal2D(const int NE,
MFEM_SHARED real_t BG[2][MQ1*MD1];
real_t (*B)[MD1] = (real_t (*)[MD1]) (BG+0);
real_t (*G)[MD1] = (real_t (*)[MD1]) (BG+1);
MFEM_SHARED real_t QD[3][NBZ][MD1][MQ1];
MFEM_SHARED real_t QD[3][NBZ][MQ1][MD1];
real_t (*QD0)[MD1] = (real_t (*)[MD1])(QD[0] + tidz);
real_t (*QD1)[MD1] = (real_t (*)[MD1])(QD[1] + tidz);
real_t (*QD2)[MD1] = (real_t (*)[MD1])(QD[2] + tidz);
@@ -769,8 +769,8 @@ inline void SmemPADiffusionApply2D(const int NE,
u += Gt[dx][qx] * QQ0[qy][qx];
v += Bt[dx][qx] * QQ1[qy][qx];
}
DQ0[qy][dx] = u;
DQ1[qy][dx] = v;
DQ0[dx][qy] = u;
DQ1[dx][qy] = v;
}
}
MFEM_SYNC_THREAD;
@@ -782,8 +782,8 @@ inline void SmemPADiffusionApply2D(const int NE,
real_t v = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += DQ0[qy][dx] * Bt[dy][qy];
v += DQ1[qy][dx] * Gt[dy][qy];
u += DQ0[dx][qy] * Bt[dy][qy];
v += DQ1[dx][qy] * Gt[dy][qy];
}
Y(dx,dy,e) += (u + v);
}
+5 -22
View File
@@ -17,11 +17,9 @@
#include "../ceed/integrators/diffusion/diffusion.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
/// \cond DO_NOT_DOCUMENT
namespace mfem
{
namespace internal
/// \cond DO_NOT_DOCUMENT
namespace mfem::internal
{
// PA Diffusion Apply 2D kernel
@@ -333,23 +331,8 @@ PAVectorDiffusionApply3D(const int NE, const Array<real_t> &b,
}
});
}
} // namespace internal
} // namespace mfem::internal
template <int DIM, int VDIM, int T_D1D, int T_Q1D>
VectorDiffusionIntegrator::ApplyKernelType
VectorDiffusionIntegrator::ApplyPAKernels::Kernel()
{
if constexpr (DIM == 2)
{
return internal::PAVectorDiffusionApply2D<T_D1D, T_Q1D, VDIM>;
}
else if constexpr (DIM == 3)
{
return internal::PAVectorDiffusionApply3D;
}
MFEM_ABORT("");
}
} // namespace mfem
/// \endcond DO_NOT_DOCUMENT
#endif
#endif // MFEM_BILININTEG_VECDIFFUSION_KERNELS_HPP
+248 -186
View File
@@ -9,13 +9,14 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "../../general/forall.hpp"
#include "../ceed/integrators/diffusion/diffusion.hpp"
#include "bilininteg_vecdiffusion_kernels.hpp"
#include "./bilininteg_vecdiffusion_pa.hpp" // IWYU pragma: keep
// #include "bilininteg_vecdiffusion_kernels.hpp"
// #include "bilininteg_vecdiffusion_pa.hpp"
namespace mfem
{
@@ -23,7 +24,7 @@ namespace mfem
VectorDiffusionIntegrator::VectorDiffusionIntegrator(const IntegrationRule *ir)
: BilinearFormIntegrator(ir)
{
static Kernels kernels;
// static Kernels kernels;
}
VectorDiffusionIntegrator::VectorDiffusionIntegrator(Coefficient &q)
@@ -67,210 +68,263 @@ VectorDiffusionIntegrator::VectorDiffusionIntegrator(MatrixCoefficient &mq)
vdim = mq.GetVDim();
}
// PA Diffusion Assemble 2D kernel
static void PAVectorDiffusionSetup2D(const int Q1D,
const int NE,
const Array<real_t> &w,
const Vector &j,
const Vector &c,
Vector &op)
{
const int NQ = Q1D*Q1D;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 2, 2, NE);
auto y = Reshape(op.Write(), NQ, 3, NE);
const bool const_c = c.Size() == 1;
const auto C = const_c ? Reshape(c.Read(), 1,1) :
Reshape(c.Read(), NQ, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int q = 0; q < NQ; ++q)
{
const real_t J11 = J(q,0,0,e);
const real_t J21 = J(q,1,0,e);
const real_t J12 = J(q,0,1,e);
const real_t J22 = J(q,1,1,e);
const real_t C1 = const_c ? C(0,0) : C(q,e);
const real_t c_detJ = W[q] * C1 / ((J11*J22)-(J21*J12));
y(q,0,e) = c_detJ * (J12*J12 + J22*J22); // 1,1
y(q,1,e) = -c_detJ * (J12*J11 + J22*J21); // 1,2
y(q,2,e) = c_detJ * (J11*J11 + J21*J21); // 2,2
}
});
}
// PA Diffusion Assemble 3D kernel
static void PAVectorDiffusionSetup3D(const int Q1D,
const int NE,
const Array<real_t> &w,
const Vector &j,
const Vector &c,
Vector &op)
{
const int NQ = Q1D*Q1D*Q1D;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, 3, 3, NE);
auto y = Reshape(op.Write(), NQ, 6, NE);
const bool const_c = c.Size() == 1;
const auto C = const_c ? Reshape(c.Read(), 1,1) :
Reshape(c.Read(), NQ,NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int q = 0; q < NQ; ++q)
{
const real_t J11 = J(q,0,0,e);
const real_t J21 = J(q,1,0,e);
const real_t J31 = J(q,2,0,e);
const real_t J12 = J(q,0,1,e);
const real_t J22 = J(q,1,1,e);
const real_t J32 = J(q,2,1,e);
const real_t J13 = J(q,0,2,e);
const real_t J23 = J(q,1,2,e);
const real_t J33 = J(q,2,2,e);
const real_t detJ = J11 * (J22 * J33 - J32 * J23) -
J21 * (J12 * J33 - J32 * J13) +
J31 * (J12 * J23 - J22 * J13);
const real_t C1 = const_c ? C(0,0) : C(q,e);
const real_t c_detJ = W[q] * C1 / detJ;
// adj(J)
const real_t A11 = (J22 * J33) - (J23 * J32);
const real_t A12 = (J32 * J13) - (J12 * J33);
const real_t A13 = (J12 * J23) - (J22 * J13);
const real_t A21 = (J31 * J23) - (J21 * J33);
const real_t A22 = (J11 * J33) - (J13 * J31);
const real_t A23 = (J21 * J13) - (J11 * J23);
const real_t A31 = (J21 * J32) - (J31 * J22);
const real_t A32 = (J31 * J12) - (J11 * J32);
const real_t A33 = (J11 * J22) - (J12 * J21);
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
y(q,0,e) = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
y(q,1,e) = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
y(q,2,e) = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
y(q,3,e) = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
y(q,4,e) = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
y(q,5,e) = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
}
});
}
static void PAVectorDiffusionSetup(const int dim,
const int Q1D,
const int NE,
const Array<real_t> &W,
const Vector &J,
const Vector &C,
Vector &op)
{
if (!(dim == 2 || dim == 3))
{
MFEM_ABORT("Dimension not supported.");
}
if (dim == 2)
{
PAVectorDiffusionSetup2D(Q1D, NE, W, J, C, op);
}
if (dim == 3)
{
PAVectorDiffusionSetup3D(Q1D, NE, W, J, C, op);
}
}
void VectorDiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetTypicalFE();
const IntegrationRule *ir
= IntRule ? IntRule : &DiffusionIntegrator::GetRule(el, el);
const auto *ir = IntRule ? IntRule : &DiffusionIntegrator::GetRule(el, el);
if (DeviceCanUseCeed())
{
delete ceedOp;
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
fes.IsVariableOrder();
if (mixed)
{
ceedOp = new ceed::MixedPADiffusionIntegrator(*this, fes, Q);
}
else
{
ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q);
}
const bool mixed =
mesh->GetNumGeometries(mesh->Dimension()) > 1 || fes.IsVariableOrder();
if (mixed) { ceedOp = new ceed::MixedPADiffusionIntegrator(*this, fes, Q); }
else { ceedOp = new ceed::PADiffusionIntegrator(fes, *ir, Q); }
return;
}
const int dims = el.GetDim();
const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
const int nq = ir->GetNPoints();
// If vdim is not set, set it to the space dimension
vdim = (vdim == -1) ? fes.GetVDim() : vdim;
MFEM_VERIFY(vdim == fes.GetVDim(), "vdim != fes.GetVDim()");
const MemoryType mt = pa_mt == MemoryType::DEFAULT
? Device::GetDeviceMemoryType()
: pa_mt;
ne = fes.GetNE();
dim = mesh->Dimension();
sdim = mesh->SpaceDimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
const int nq = ir->GetNPoints();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(symmDims * nq * ne, Device::GetDeviceMemoryType());
const int q1d = quad1D;
MFEM_VERIFY(!VQ && !MQ,
"Only scalar coefficient supported for partial assembly for VectorDiffusionIntegrator");
if (!(dim == 2 || dim == 3)) { MFEM_ABORT("Dimension not supported."); }
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
CoefficientVector coeff(qs, CoefficientStorage::FULL);
if (Q)
{
coeff.Project(*Q);
}
else if (VQ)
{
coeff.Project(*VQ);
MFEM_VERIFY(VQ->GetVDim() == vdim, "VQ vdim vs. vdim error");
}
else if (MQ)
{
coeff.ProjectTranspose(*MQ);
MFEM_VERIFY(MQ->GetVDim() == vdim, "MQ dimension vs. vdim error");
MFEM_VERIFY(coeff.Size() == (vdim*vdim) * ne * nq, "MQ size error");
}
else { coeff.SetConstant(1.0); }
coeff_vdim = coeff.GetVDim();
const bool scalar_coeff = coeff_vdim == 1;
const bool vector_coeff = coeff_vdim == vdim;
const bool matrix_coeff = coeff_vdim == vdim * vdim;
MFEM_VERIFY(scalar_coeff + vector_coeff + matrix_coeff == 1, "");
const int pa_size = dim * dim;
pa_data.SetSize(nq * pa_size * vdim * (matrix_coeff ? dim : 1) * ne, mt);
const Array<real_t> &w = ir->GetWeights();
const Vector &j = geom->J;
Vector &d = pa_data;
if (dim == 1) { MFEM_ABORT("dim==1 not supported in PAVectorDiffusionSetup"); }
if (dim == 2 && sdim == 3)
{
constexpr int DIM = 2;
constexpr int SDIM = 3;
const int NQ = quad1D*quad1D;
auto W = w.Read();
auto J = Reshape(j.Read(), NQ, SDIM, DIM, ne);
auto D = Reshape(d.Write(), NQ, SDIM, ne);
MFEM_VERIFY(scalar_coeff, "");
const int nc = vdim;
const auto W = Reshape(ir->GetWeights().Read(), q1d, q1d);
const auto J = Reshape(geom->J.Read(), q1d, q1d, sdim, dim, ne);
const auto C = Reshape(coeff.Read(), coeff_vdim, q1d, q1d, ne);
auto D = Reshape(pa_data.Write(), q1d, q1d, pa_size,
vdim * (matrix_coeff ? dim : 1), ne);
const bool const_c = coeff.Size() == 1;
const auto C = const_c ? Reshape(coeff.Read(), 1,1) :
Reshape(coeff.Read(), NQ,ne);
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
mfem::forall_2D(ne, q1d, q1d, [=] MFEM_HOST_DEVICE(int e)
{
for (int q = 0; q < NQ; ++q)
MFEM_FOREACH_THREAD(qy, y, q1d)
{
const real_t wq = W[q];
const real_t J11 = J(q,0,0,e);
const real_t J21 = J(q,1,0,e);
const real_t J31 = J(q,2,0,e);
const real_t J12 = J(q,0,1,e);
const real_t J22 = J(q,1,1,e);
const real_t J32 = J(q,2,1,e);
const real_t E = J11*J11 + J21*J21 + J31*J31;
const real_t G = J12*J12 + J22*J22 + J32*J32;
const real_t F = J11*J12 + J21*J22 + J31*J32;
const real_t iw = 1.0 / sqrt(E*G - F*F);
const real_t C1 = const_c ? C(0,0) : C(q,e);
const real_t alpha = wq * C1 * iw;
D(q,0,e) = alpha * G; // 1,1
D(q,1,e) = -alpha * F; // 1,2
D(q,2,e) = alpha * E; // 2,2
MFEM_FOREACH_THREAD(qx, x, q1d)
{
for (int i = 0; i < nc; ++i)
{
const real_t wq = W(qx, qy);
const real_t J11 = J(qx, qy, 0, 0, e);
const real_t J21 = J(qx, qy, 1, 0, e);
const real_t J31 = J(qx, qy, 2, 0, e);
const real_t J12 = J(qx, qy, 0, 1, e);
const real_t J22 = J(qx, qy, 1, 1, e);
const real_t J32 = J(qx, qy, 2, 1, e);
const real_t E = J11*J11 + J21*J21 + J31*J31;
const real_t G = J12*J12 + J22*J22 + J32*J32;
const real_t F = J11*J12 + J21*J22 + J31*J32;
const real_t iw = 1.0 / sqrt(E*G - F*F);
const auto C0 = C(0, qx, qy, e);
const real_t alpha = wq * C0 * iw;
D(qx, qy, 0, i, e) = alpha * G; // 1,1
D(qx, qy, 1, i, e) = -alpha * F; // 1,2
D(qx, qy, 2, i, e) = -alpha * F; // 2,1 == 1,2
D(qx, qy, 3, i, e) = alpha * E; // 2,2
}
}
}
});
}
else if (dim == 2 && sdim == 2)
{
const int nc = vdim, cvdim = coeff_vdim;
const auto W = Reshape(ir->GetWeights().Read(), q1d, q1d);
const auto J = Reshape(geom->J.Read(), q1d, q1d, sdim, dim, ne);
const auto C = Reshape(coeff.Read(), coeff_vdim, q1d, q1d, ne);
auto DE = Reshape(pa_data.Write(), q1d, q1d, pa_size,
vdim * (matrix_coeff ? dim : 1), ne);
mfem::forall_2D(ne, q1d, q1d, [=] MFEM_HOST_DEVICE(int e)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
const real_t J11 = J(qx, qy, 0, 0, e);
const real_t J21 = J(qx, qy, 1, 0, e);
const real_t J12 = J(qx, qy, 0, 1, e);
const real_t J22 = J(qx, qy, 1, 1, e);
const real_t w_detJ = W(qx, qy) / ((J11*J22)-(J21*J12));
const real_t D0 = w_detJ * (J12*J12 + J22*J22);
const real_t D1 = -w_detJ * (J12*J11 + J22*J21);
const real_t D2 = w_detJ * (J11*J11 + J21*J21);
const int map[4] = {0, 2, 1, 3};
for (int i = 0; i < (matrix_coeff ? cvdim : nc); ++i)
{
const auto k = matrix_coeff ? map[i] : (vector_coeff ? i : 0);
const auto Cc = C(k, qx, qy, e);
DE(qx, qy, 0, i, e) = D0 * Cc;
DE(qx, qy, 1, i, e) = D1 * Cc;
DE(qx, qy, 2, i, e) = D1 * Cc;
DE(qx, qy, 3, i, e) = D2 * Cc;
}
}
}
});
}
else if (dim == 3 && sdim == 3)
{
const int nc = vdim, cvdim = coeff_vdim;
const auto W = Reshape(ir->GetWeights().Read(), q1d, q1d, q1d);
const auto J = Reshape(geom->J.Read(), q1d, q1d, q1d, sdim, dim, ne);
const auto C = Reshape(coeff.Read(), coeff_vdim, q1d, q1d, q1d, ne);
auto DE = Reshape(pa_data.Write(), q1d, q1d, q1d, pa_size,
vdim * (matrix_coeff ? dim : 1), ne);
mfem::forall_3D(ne, q1d, q1d, q1d, [=] MFEM_HOST_DEVICE(int e)
{
MFEM_FOREACH_THREAD(qz, z, q1d)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
const real_t J11 = J(qx, qy, qz, 0, 0, e);
const real_t J21 = J(qx, qy, qz, 1, 0, e);
const real_t J31 = J(qx, qy, qz, 2, 0, e);
const real_t J12 = J(qx, qy, qz, 0, 1, e);
const real_t J22 = J(qx, qy, qz, 1, 1, e);
const real_t J32 = J(qx, qy, qz, 2, 1, e);
const real_t J13 = J(qx, qy, qz, 0, 2, e);
const real_t J23 = J(qx, qy, qz, 1, 2, e);
const real_t J33 = J(qx, qy, qz, 2, 2, e);
const real_t detJ = J11 * (J22 * J33 - J32 * J23) -
J21 * (J12 * J33 - J32 * J13) +
J31 * (J12 * J23 - J22 * J13);
const real_t c_detJ = W(qx, qy, qz) / detJ;
// adj(J)
const real_t A11 = (J22 * J33) - (J23 * J32);
const real_t A12 = (J32 * J13) - (J12 * J33);
const real_t A13 = (J12 * J23) - (J22 * J13);
const real_t A21 = (J31 * J23) - (J21 * J33);
const real_t A22 = (J11 * J33) - (J13 * J31);
const real_t A23 = (J21 * J13) - (J11 * J23);
const real_t A31 = (J21 * J32) - (J31 * J22);
const real_t A32 = (J31 * J12) - (J11 * J32);
const real_t A33 = (J11 * J22) - (J12 * J21);
// detJ J^{-1} J^{-T} = (1/detJ) adj(J) adj(J)^T
const real_t D11 = c_detJ * (A11*A11 + A12*A12 + A13*A13); // 1,1
const real_t D21 = c_detJ * (A11*A21 + A12*A22 + A13*A23); // 2,1
const real_t D31 = c_detJ * (A11*A31 + A12*A32 + A13*A33); // 3,1
const real_t D22 = c_detJ * (A21*A21 + A22*A22 + A23*A23); // 2,2
const real_t D32 = c_detJ * (A21*A31 + A22*A32 + A23*A33); // 3,2
const real_t D33 = c_detJ * (A31*A31 + A32*A32 + A33*A33); // 3,3
const int map[9] = {0, 3, 6, 1, 4, 7, 2, 5, 8};
for (int i = 0; i < (matrix_coeff ? cvdim : nc); ++i)
{
const auto k = matrix_coeff ? map[i] : vector_coeff ? i : 0;
const auto Ck = C(k, qx, qy, qz, e);
DE(qx, qy, qz, 0, i, e) = D11 * Ck;
DE(qx, qy, qz, 1, i, e) = D21 * Ck;
DE(qx, qy, qz, 2, i, e) = D31 * Ck;
DE(qx, qy, qz, 3, i, e) = D22 * Ck;
DE(qx, qy, qz, 4, i, e) = D32 * Ck;
DE(qx, qy, qz, 5, i, e) = D33 * Ck;
}
}
}
}
});
}
else
{
PAVectorDiffusionSetup(dim, quad1D, ne, w, j, coeff, d);
MFEM_ABORT("Unknown VectorDiffusionIntegrator::AssemblePA kernel for"
<< " dim:" << dim << ", vdim:" << vdim << ", sdim:" << sdim);
}
}
// PA Diffusion Apply kernel
void VectorDiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
// Use CEED backend if available
if (DeviceCanUseCeed()) { return ceedOp->AddMult(x, y); }
// Add the VectorDiffusionAddMultPA specializations
static const auto vector_diffusion_kernel_specializations =
(
// 2D, SDIM = 2
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 2,2>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 3,3>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 4,4>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 5,5>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 6,6>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 7,7>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 8,8>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,2, 9,9>::Add(),
// 2D, SDIM = 3
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,3, 2,2>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,3, 3,3>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,3, 4,4>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<2,3, 5,5>::Add(),
// 3D, SDIM = 3
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 2,2>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 2,3>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 3,4>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 4,5>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 4,6>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 5,6>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 5,8>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 6,7>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 7,8>::Add(),
VectorDiffusionIntegrator::ApplyPAKernels::Specialization<3,3, 8,9>::Add(),
true);
MFEM_CONTRACT_VAR(vector_diffusion_kernel_specializations);
ApplyPAKernels::Run(dim, sdim, dofs1D, quad1D,
ne, coeff_vdim, maps->B, maps->G, pa_data, x, y,
sdim, dofs1D, quad1D);
}
template<int T_D1D = 0, int T_Q1D = 0>
static void PAVectorDiffusionDiagonal2D(const int NE,
const Array<real_t> &b,
@@ -284,12 +338,15 @@ static void PAVectorDiffusionDiagonal2D(const int NE,
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
const auto B = Reshape(b.Read(), Q1D, D1D);
const auto G = Reshape(g.Read(), Q1D, D1D);
// note the different shape for D, this is a (symmetric) matrix so we only
// store necessary entries
auto D = Reshape(d.Read(), Q1D*Q1D, 3, NE);
MFEM_VERIFY(d.Size() == Q1D*Q1D*4*2*NE, "");
const auto D = Reshape(d.Read(), Q1D*Q1D, /*3*/4, 2, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, 2, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
@@ -310,9 +367,9 @@ static void PAVectorDiffusionDiagonal2D(const int NE,
for (int qy = 0; qy < Q1D; ++qy)
{
const int q = qx + qy * Q1D;
const real_t D0 = D(q,0,e);
const real_t D1 = D(q,1,e);
const real_t D2 = D(q,2,e);
const real_t D0 = D(q,0,0,e);
const real_t D1 = D(q,1,0,e);
const real_t D2 = D(q,3/*2*/,0,e); // size from 3 (symmetric) to 4 (dims x dims)
QD0[qx][dy] += B(qy, dy) * B(qy, dy) * D0;
QD1[qx][dy] += B(qy, dy) * G(qy, dy) * D1;
QD2[qx][dy] += G(qy, dy) * G(qy, dy) * D2;
@@ -356,7 +413,8 @@ static void PAVectorDiffusionDiagonal3D(const int NE,
MFEM_VERIFY(Q1D <= max_q1d, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto Q = Reshape(d.Read(), Q1D*Q1D*Q1D, 6, NE);
MFEM_VERIFY(d.Size() == Q1D*Q1D*Q1D*9*3*NE, "");
auto Q = Reshape(d.Read(), Q1D*Q1D*Q1D, 9/*PA_SIZE:dims*dims*/, 3/*VDIM*/, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, 3, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
@@ -384,7 +442,8 @@ static void PAVectorDiffusionDiagonal3D(const int NE,
const int k = j >= i ?
3 - (3-i)*(2-i)/2 + j:
3 - (3-j)*(2-j)/2 + i;
const real_t O = Q(q,k,e);
// using 6 symmetric values
const real_t O = Q(q,k,0,e);
const real_t Bz = B(qz,dz);
const real_t Gz = G(qz,dz);
const real_t L = i==2 ? Gz : Bz;
@@ -468,12 +527,14 @@ void VectorDiffusionIntegrator::AssembleDiagonalPA(Vector &diag)
}
else
{
MFEM_VERIFY(!VQ && !MQ, "VQ and MQ not supported.");
PAVectorDiffusionAssembleDiagonal(dim, dofs1D, quad1D, ne,
maps->B, maps->G,
pa_data, diag);
}
}
/*
// PA Diffusion Apply kernel
void VectorDiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
@@ -514,5 +575,6 @@ VectorDiffusionIntegrator::Kernels::Kernels()
}
/// \endcond DO_NOT_DOCUMENT
*/
} // namespace mfem
+202
View File
@@ -0,0 +1,202 @@
// 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/array.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../linalg/vector.hpp"
#include "../bilininteg.hpp"
#include "../kernels.hpp"
using mfem::kernels::internal::SetMaxOf;
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
namespace internal
{
template<int T_SDIM = 0, int T_D1D = 0, int T_Q1D = 0>
void SmemPAVectorDiffusionApply2D(const int NE,
const int coeff_vdim,
const Array<real_t> &b,
const Array<real_t> &g,
const Vector &d,
const Vector &x,
Vector &y,
const int sdim = 0,
const int d1d = 0,
const int q1d = 0)
{
static constexpr int DIM = 2;
const int SDIM = T_SDIM ? T_SDIM : sdim;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int PA_SIZE = DIM*DIM;
const bool matrix_coeff = coeff_vdim == DIM*DIM;
const auto B = b.Read(), G = g.Read();
const auto DE = Reshape(d.Read(), Q1D, Q1D, PA_SIZE,
SDIM * (matrix_coeff ? SDIM : 1), NE);
const auto XE = Reshape(x.Read(), D1D, D1D, SDIM, NE);
auto YE = Reshape(y.ReadWrite(), D1D, D1D, SDIM, NE);
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1], smem[MQ1][MQ1];
kernels::internal::vd_regs2d_t<3, DIM, MQ1> r0, r1;
kernels::internal::LoadMatrix(D1D, Q1D, B, sB);
kernels::internal::LoadMatrix(D1D, Q1D, G, sG);
for (int i = 0; i < SDIM; i++)
{
for (int j = 0; j < (matrix_coeff ? SDIM : 1); j++)
{
kernels::internal::LoadDofs2d(e, D1D, i, XE, r0);
kernels::internal::Grad2d(D1D, Q1D, smem, sB, sG, r0, r1, i);
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const real_t gradX = r1[i][0][qy][qx];
const real_t gradY = r1[i][1][qy][qx];
const int k = matrix_coeff ? (j + i * SDIM) : i;
const real_t O11 = DE(qx,qy,0,k,e), O12 = DE(qx,qy,1,k,e);
const real_t O21 = DE(qx,qy,2,k,e), O22 = DE(qx,qy,3,k,e);
r0[i][0][qy][qx] = (O11 * gradX) + (O12 * gradY);
r0[i][1][qy][qx] = (O21 * gradX) + (O22 * gradY);
} // qx
} // qy
MFEM_SYNC_THREAD;
kernels::internal::GradTranspose2d(D1D, Q1D, smem, sB, sG, r0, r1, i);
const int ij = matrix_coeff ? j : i;
kernels::internal::WriteDofs2d(e, D1D, i, ij, r1, YE);
} // j
} // i
});
}
template<int T_SDIM = 0, int T_D1D = 0, int T_Q1D = 0>
void SmemPAVectorDiffusionApply3D(const int NE,
const int coeff_vdim,
const Array<real_t> &b,
const Array<real_t> &g,
const Vector &d,
const Vector &x,
Vector &y,
const int sdim = 0,
const int d1d = 0,
const int q1d = 0)
{
static constexpr int DIM = 3;
const int SDIM = T_SDIM ? T_SDIM : sdim;
MFEM_VERIFY(SDIM == 3, "SDIM must be 3");
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const int PA_SIZE = DIM*DIM;
const bool matrix_coeff = coeff_vdim == DIM*DIM;
const auto B = b.Read(), G = g.Read();
const auto DE = Reshape(d.Read(), Q1D, Q1D, Q1D, PA_SIZE,
SDIM * (matrix_coeff ? SDIM : 1), NE);
const auto XE = Reshape(x.Read(), D1D, D1D, D1D, SDIM, NE);
auto YE = Reshape(y.ReadWrite(), D1D, D1D, D1D, SDIM, NE);
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1], smem[MQ1][MQ1];
kernels::internal::vd_regs3d_t<3, DIM, MQ1> r0, r1;
kernels::internal::LoadMatrix(D1D, Q1D, B, sB);
kernels::internal::LoadMatrix(D1D, Q1D, G, sG);
for (int i = 0; i < SDIM; i++)
{
for (int j = 0; j < (matrix_coeff ? SDIM : 1); j++)
{
kernels::internal::LoadDofs3d(e, D1D, i, XE, r0);
kernels::internal::Grad3d(D1D, Q1D, smem, sB, sG, r0, r1, i);
for (int qz = 0; qz < Q1D; qz++)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const real_t gradX = r1[i][0][qz][qy][qx];
const real_t gradY = r1[i][1][qz][qy][qx];
const real_t gradZ = r1[i][2][qz][qy][qx];
const int k = matrix_coeff ? (j + i * SDIM) : i;
const real_t O11 = DE(qx,qy,qz,0,k,e), O12 = DE(qx,qy,qz,1,k,e),
O13 = DE(qx,qy,qz,2,k,e);
const real_t O22 = DE(qx,qy,qz,3,k,e), O23 = DE(qx,qy,qz,4,k,e);
const real_t O33 = DE(qx,qy,qz,5,k,e);
r0[i][0][qz][qy][qx] = (O11*gradX)+(O12*gradY)+(O13*gradZ);
r0[i][1][qz][qy][qx] = (O12*gradX)+(O22*gradY)+(O23*gradZ);
r0[i][2][qz][qy][qx] = (O13*gradX)+(O23*gradY)+(O33*gradZ);
} // qx
} // qy
} // qz
MFEM_SYNC_THREAD;
kernels::internal::GradTranspose3d(D1D, Q1D, smem, sB, sG, r0, r1, i);
const int ij = matrix_coeff ? j : i;
kernels::internal::WriteDofs3d(e, D1D, i, ij, r1, YE);
} // j
} // i
});
}
} // namespace internal
template<int DIM, int T_SDIM, int T_D1D, int T_Q1D>
VectorDiffusionIntegrator::ApplyKernelType
VectorDiffusionIntegrator::ApplyPAKernels::Kernel()
{
if (DIM == 2)
{
return internal::SmemPAVectorDiffusionApply2D<T_SDIM, T_D1D, T_Q1D>;
}
else if (DIM == 3)
{
return internal::SmemPAVectorDiffusionApply3D<T_SDIM, T_D1D, T_Q1D>;
}
else { MFEM_ABORT("Unsupported kernel"); }
}
inline VectorDiffusionIntegrator::ApplyKernelType
VectorDiffusionIntegrator::ApplyPAKernels::Fallback(int dim, int sdim,
int d1d, int q1d)
{
if (dim == 2)
{
return internal::SmemPAVectorDiffusionApply2D;
}
else if (dim == 3)
{
return internal::SmemPAVectorDiffusionApply3D;
}
else { MFEM_ABORT("Unsupported kernel"); }
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
+199 -382
View File
@@ -9,120 +9,218 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../../general/forall.hpp"
#include "../ceed/integrators/mass/mass.hpp"
#include "./bilininteg_vecmass_pa.hpp" // IWYU pragma: keep
namespace mfem
{
void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
// Assuming the same element type
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetTypicalFE();
ElementTransformation *T = mesh->GetTypicalElementTransformation();
const IntegrationRule *ir
= IntRule ? IntRule : &MassIntegrator::GetRule(el, el, *T);
ElementTransformation &Trans = *mesh->GetTypicalElementTransformation();
const auto *ir = IntRule ? IntRule : &MassIntegrator::GetRule(el, el, Trans);
if (DeviceCanUseCeed())
{
delete ceedOp;
const bool mixed = mesh->GetNumGeometries(mesh->Dimension()) > 1 ||
fes.IsVariableOrder();
if (mixed)
{
ceedOp = new ceed::MixedPAMassIntegrator(*this, fes, Q);
}
else
{
ceedOp = new ceed::PAMassIntegrator(fes, *ir, Q);
}
const bool mixed =
mesh->GetNumGeometries(mesh->Dimension()) > 1 || fes.IsVariableOrder();
if (mixed) { ceedOp = new ceed::MixedPAMassIntegrator(*this, fes, Q); }
else { ceedOp = new ceed::PAMassIntegrator(fes, *ir, Q); }
return;
}
// If vdim is not set, set it to the space dimension
vdim = (vdim == -1) ? Trans.GetSpaceDim() : vdim;
MFEM_VERIFY(vdim == fes.GetVDim(), "vdim != fes.GetVDim()");
MFEM_VERIFY(vdim == mesh->Dimension(), "vdim != dim");
const MemoryType mt = pa_mt == MemoryType::DEFAULT
? Device::GetDeviceMemoryType()
: pa_mt;
ne = mesh->GetNE();
dim = mesh->Dimension();
ne = fes.GetMesh()->GetNE();
nq = ir->GetNPoints();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::COORDINATES |
GeometricFactors::JACOBIANS);
const int nq = ir->GetNPoints();
const int sdim = mesh->SpaceDimension();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(ne*nq, Device::GetDeviceMemoryType());
real_t coeff = 1.0;
const int q1d = quad1D;
if (!(dim == 2 || dim == 3)) { MFEM_ABORT("Dimension not supported."); }
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(qs);
if (Q)
{
ConstantCoefficient *cQ = dynamic_cast<ConstantCoefficient*>(Q);
MFEM_VERIFY(cQ != NULL, "Only ConstantCoefficient is supported.");
coeff = cQ->constant;
coeff.Project(*Q);
}
if (!(dim == 2 || dim == 3))
else if (VQ)
{
MFEM_ABORT("Dimension not supported.");
coeff.Project(*VQ);
MFEM_VERIFY(VQ->GetVDim() == vdim, "VQ vdim vs. vdim error");
}
else if (MQ)
{
coeff.ProjectTranspose(*MQ);
MFEM_VERIFY(MQ->GetVDim() == vdim, "MQ dimension vs. vdim error");
MFEM_VERIFY(coeff.Size() == (vdim*vdim) * ne * nq, "MQ size error");
}
else { coeff.SetConstant(1.0); }
coeff_vdim = coeff.GetVDim();
const bool const_coeff = coeff_vdim == 1;
const bool vector_coeff = coeff_vdim == vdim;
const bool matrix_coeff = coeff_vdim == vdim * vdim;
MFEM_VERIFY(const_coeff + vector_coeff + matrix_coeff == 1, "");
pa_data.SetSize(coeff_vdim * nq * ne, mt);
const auto w_r = ir->GetWeights().Read();
if (dim == 2)
{
const real_t constant = coeff;
const int NE = ne;
const int NQ = nq;
auto w = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,2,2,NE);
auto v = Reshape(pa_data.Write(), NQ, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
const auto W = Reshape(w_r, q1d, q1d);
const auto C = Reshape(coeff.Read(), coeff_vdim, q1d, q1d, ne);
const auto J = Reshape(geom->J.Read(), q1d, q1d, sdim, dim, ne);
auto D = Reshape(pa_data.Write(), q1d, q1d, coeff_vdim, ne);
mfem::forall_2D(ne, q1d, q1d, [=] MFEM_HOST_DEVICE(int e)
{
for (int q = 0; q < NQ; ++q)
MFEM_FOREACH_THREAD(qy, y, q1d)
{
const real_t J11 = J(q,0,0,e);
const real_t J12 = J(q,1,0,e);
const real_t J21 = J(q,0,1,e);
const real_t J22 = J(q,1,1,e);
const real_t detJ = (J11*J22)-(J21*J12);
v(q,e) = w[q] * constant * detJ;
MFEM_FOREACH_THREAD(qx, x, q1d)
{
const real_t J11 = J(qx, qy, 0, 0, e), J12 = J(qx, qy, 1, 0, e);
const real_t J21 = J(qx, qy, 0, 1, e), J22 = J(qx, qy, 1, 1, e);
const real_t detJ = (J11 * J22) - (J21 * J12);
const real_t w_det = W(qx, qy) * detJ;
D(qx, qy, 0, e) = C(0, qx, qy, e) * w_det;
if (const_coeff) { continue; }
D(qx, qy, 1, e) = C(1, qx, qy, e) * w_det;
if (vector_coeff) { continue; }
assert(matrix_coeff);
D(qx, qy, 2, e) = C(2, qx, qy, e) * w_det;
D(qx, qy, 3, e) = C(3, qx, qy, e) * w_det;
}
}
});
}
if (dim == 3)
else if (dim == 3)
{
const real_t constant = coeff;
const int NE = ne;
const int NQ = nq;
auto W = ir->GetWeights().Read();
auto J = Reshape(geom->J.Read(), NQ,3,3,NE);
auto v = Reshape(pa_data.Write(), NQ,NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
const auto W = Reshape(w_r, q1d, q1d, q1d);
const auto C = Reshape(coeff.Read(), coeff_vdim, q1d, q1d, q1d, ne);
const auto J = Reshape(geom->J.Read(), q1d, q1d, q1d, sdim, dim, ne);
auto D = Reshape(pa_data.Write(), q1d, q1d, q1d, coeff_vdim, ne);
mfem::forall_3D(ne, q1d, q1d, q1d, [=] MFEM_HOST_DEVICE(int e)
{
for (int q = 0; q < NQ; ++q)
MFEM_FOREACH_THREAD(qz, z, q1d)
{
const real_t J11 = J(q,0,0,e), J12 = J(q,0,1,e), J13 = J(q,0,2,e);
const real_t J21 = J(q,1,0,e), J22 = J(q,1,1,e), J23 = J(q,1,2,e);
const real_t J31 = J(q,2,0,e), J32 = J(q,2,1,e), J33 = J(q,2,2,e);
const real_t detJ = J11 * (J22 * J33 - J32 * J23) -
J21 * (J12 * J33 - J32 * J13) +
J31 * (J12 * J23 - J22 * J13);
v(q,e) = W[q] * constant * detJ;
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
const real_t J11 = J(qx, qy, qz, 0, 0, e),
J12 = J(qx, qy, qz, 0, 1, e),
J13 = J(qx, qy, qz, 0, 2, e);
const real_t J21 = J(qx, qy, qz, 1, 0, e),
J22 = J(qx, qy, qz, 1, 1, e),
J23 = J(qx, qy, qz, 1, 2, e);
const real_t J31 = J(qx, qy, qz, 2, 0, e),
J32 = J(qx, qy, qz, 2, 1, e),
J33 = J(qx, qy, qz, 2, 2, e);
const real_t detJ = J11 * (J22 * J33 - J32 * J23) -
J21 * (J12 * J33 - J32 * J13) +
J31 * (J12 * J23 - J22 * J13);
const real_t w_det = W(qx, qy, qz) * detJ;
D(qx, qy, qz, 0, e) = C(0, qx, qy, qz, e) * w_det;
if (const_coeff) { continue; }
D(qx, qy, qz, 1, e) = C(1, qx, qy, qz, e) * w_det;
D(qx, qy, qz, 2, e) = C(2, qx, qy, qz, e) * w_det;
if (vector_coeff) { continue; }
D(qx, qy, qz, 3, e) = C(3, qx, qy, qz, e) * w_det;
D(qx, qy, qz, 4, e) = C(4, qx, qy, qz, e) * w_det;
D(qx, qy, qz, 5, e) = C(5, qx, qy, qz, e) * w_det;
D(qx, qy, qz, 6, e) = C(6, qx, qy, qz, e) * w_det;
D(qx, qy, qz, 7, e) = C(7, qx, qy, qz, e) * w_det;
D(qx, qy, qz, 8, e) = C(8, qx, qy, qz, e) * w_det;
}
}
}
});
}
else
{
MFEM_ABORT("Unknown VectorMassIntegrator::AssemblePA kernel for"
<< " dim:" << dim << ", vdim:" << vdim << ", sdim:" << sdim);
}
}
template<const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal2D(const int NE,
const Array<real_t> &B_,
const Array<real_t> &Bt_,
const Vector &op_,
Vector &diag_,
const int d1d = 0,
const int q1d = 0)
void VectorMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
// Use CEED backend if available
if (DeviceCanUseCeed()) { return ceedOp->AddMult(x, y); }
// Add the VectorMassAddMultPA specializations
static const auto vector_mass_kernel_specializations =
( // 2D
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 2,2>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 3,3>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 3,4>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 4,4>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 4,6>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 5,5>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 6,6>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 7,7>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 8,8>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<2, 9,9>::Add(),
// 3D
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 2,2>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 2,3>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 3,4>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 3,5>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 4,5>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 4,6>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 4,8>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 5,6>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 5,8>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 6,7>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 7,8>::Add(),
VectorMassIntegrator::VectorMassAddMultPA::Specialization<3, 8,9>::Add(),
true);
MFEM_CONTRACT_VAR(vector_mass_kernel_specializations);
VectorMassAddMultPA::Run(dim, dofs1D, quad1D,
ne, coeff_vdim, maps->B, pa_data, x, y,
dofs1D, quad1D);
}
template <const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal2D(const int NE,
const Array<real_t> &b,
const Vector &pa_data, Vector &diag,
const int d1d = 0, const int q1d = 0)
{
constexpr int VDIM = 2;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 2;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
auto B = Reshape(B_.Read(), Q1D, D1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, NE);
auto y = Reshape(diag_.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
const auto B = Reshape(b.Read(), Q1D, D1D);
const auto D = Reshape(pa_data.Read(), Q1D, Q1D, NE);
auto Y = Reshape(diag.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE(int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -137,7 +235,7 @@ static void PAVectorMassAssembleDiagonal2D(const int NE,
temp[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp[qx][dy] += B(qy, dy) * B(qy, dy) * op(qx, qy, e);
temp[qx][dy] += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
}
}
}
@@ -150,33 +248,31 @@ static void PAVectorMassAssembleDiagonal2D(const int NE,
{
temp1 += B(qx, dx) * B(qx, dx) * temp[qx][dy];
}
y(dx, dy, 0, e) = temp1;
y(dx, dy, 1, e) = temp1;
Y(dx, dy, 0, e) = temp1;
Y(dx, dy, 1, e) = temp1;
}
}
});
}
template<const int T_D1D = 0, const int T_Q1D = 0>
template <const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal3D(const int NE,
const Array<real_t> &B_,
const Array<real_t> &Bt_,
const Vector &op_,
Vector &diag_,
const int d1d = 0,
const int q1d = 0)
const Vector &pa_data, Vector &diag,
const int d1d = 0, const int q1d = 0)
{
constexpr int VDIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 3;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
auto B = Reshape(B_.Read(), Q1D, D1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, NE);
auto y = Reshape(diag_.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
const auto B = Reshape(B_.Read(), Q1D, D1D);
MFEM_VERIFY(pa_data.Size() == Q1D * Q1D * Q1D * NE, "pa_data size error");
const auto D = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, NE);
auto Y = Reshape(diag.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE(int e)
{
const int D1D = T_D1D ? T_D1D : d1d; // nvcc workaround
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
// the following variables are evaluated at compile time
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
@@ -192,7 +288,8 @@ static void PAVectorMassAssembleDiagonal3D(const int NE,
temp[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
temp[qx][qy][dz] += B(qz, dz) * B(qz, dz) * op(qx, qy, qz, e);
temp[qx][qy][dz] +=
B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
}
}
}
@@ -207,7 +304,8 @@ static void PAVectorMassAssembleDiagonal3D(const int NE,
temp2[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp2[qx][dy][dz] += B(qy, dy) * B(qy, dy) * temp[qx][qy][dz];
temp2[qx][dy][dz] +=
B(qy, dy) * B(qy, dy) * temp[qx][qy][dz];
}
}
}
@@ -221,323 +319,42 @@ static void PAVectorMassAssembleDiagonal3D(const int NE,
real_t temp3 = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp3 += B(qx, dx) * B(qx, dx)
* temp2[qx][dy][dz];
temp3 += B(qx, dx) * B(qx, dx) * temp2[qx][dy][dz];
}
y(dx, dy, dz, 0, e) = temp3;
y(dx, dy, dz, 1, e) = temp3;
y(dx, dy, dz, 2, e) = temp3;
Y(dx, dy, dz, 0, e) = temp3;
Y(dx, dy, dz, 1, e) = temp3;
Y(dx, dy, dz, 2, e) = temp3;
}
}
}
});
}
static void PAVectorMassAssembleDiagonal(const int dim,
const int D1D,
const int Q1D,
const int NE,
static void PAVectorMassAssembleDiagonal(const int dim, const int D1D,
const int Q1D, const int NE,
const Array<real_t> &B,
const Array<real_t> &Bt,
const Vector &op,
Vector &y)
const Vector &pa_data,
Vector &diag)
{
if (dim == 2)
{
return PAVectorMassAssembleDiagonal2D(NE, B, Bt, op, y, D1D, Q1D);
return PAVectorMassAssembleDiagonal2D(NE, B, pa_data, diag, D1D, Q1D);
}
else if (dim == 3)
{
return PAVectorMassAssembleDiagonal3D(NE, B, Bt, op, y, D1D, Q1D);
return PAVectorMassAssembleDiagonal3D(NE, B, pa_data, diag, D1D, Q1D);
}
MFEM_ABORT("Dimension not implemented.");
}
void VectorMassIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed())
{
ceedOp->GetDiagonal(diag);
}
if (DeviceCanUseCeed()) { ceedOp->GetDiagonal(diag); }
else
{
PAVectorMassAssembleDiagonal(dim, dofs1D, quad1D, ne,
maps->B, maps->Bt,
pa_data, diag);
}
}
template<const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassApply2D(const int NE,
const Array<real_t> &B_,
const Array<real_t> &Bt_,
const Vector &op_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 2;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
auto B = Reshape(B_.Read(), Q1D, D1D);
auto Bt = Reshape(Bt_.Read(), D1D, Q1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, NE);
auto x = Reshape(x_.Read(), D1D, D1D, VDIM, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d; // nvcc workaround
const int Q1D = T_Q1D ? T_Q1D : q1d;
// the following variables are evaluated at compile time
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
real_t sol_xy[max_Q1D][max_Q1D];
for (int c = 0; c < VDIM; ++c)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
real_t sol_x[max_Q1D];
for (int qy = 0; qy < Q1D; ++qy)
{
sol_x[qy] = 0.0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const real_t s = x(dx,dy,c,e);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] += B(qx,dx)* s;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t d2q = B(qy,dy);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] += d2q * sol_x[qx];
}
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] *= op(qx,qy,e);
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
real_t sol_x[max_D1D];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] = 0.0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t s = sol_xy[qy][qx];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] += Bt(dx,qx) * s;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const real_t q2d = Bt(dy,qy);
for (int dx = 0; dx < D1D; ++dx)
{
y(dx,dy,c,e) += q2d * sol_x[dx];
}
}
}
}
});
}
template<const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassApply3D(const int NE,
const Array<real_t> &B_,
const Array<real_t> &Bt_,
const Vector &op_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int VDIM = 3;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
auto B = Reshape(B_.Read(), Q1D, D1D);
auto Bt = Reshape(Bt_.Read(), D1D, Q1D);
auto op = Reshape(op_.Read(), Q1D, Q1D, Q1D, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, VDIM, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
real_t sol_xyz[max_Q1D][max_Q1D][max_Q1D];
for (int c = 0; c < VDIM; ++ c)
{
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xyz[qz][qy][qx] = 0.0;
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
real_t sol_xy[max_Q1D][max_Q1D];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
real_t sol_x[max_Q1D];
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] = 0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const real_t s = x(dx,dy,dz,c,e);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_x[qx] += B(qx,dx) * s;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t wy = B(qy,dy);
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xy[qy][qx] += wy * sol_x[qx];
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
const real_t wz = B(qz,dz);
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xyz[qz][qy][qx] += wz * sol_xy[qy][qx];
}
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
sol_xyz[qz][qy][qx] *= op(qx,qy,qz,e);
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
real_t sol_xy[max_D1D][max_D1D];
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
sol_xy[dy][dx] = 0;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
real_t sol_x[max_D1D];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] = 0;
}
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t s = sol_xyz[qz][qy][qx];
for (int dx = 0; dx < D1D; ++dx)
{
sol_x[dx] += Bt(dx,qx) * s;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
const real_t wy = Bt(dy,qy);
for (int dx = 0; dx < D1D; ++dx)
{
sol_xy[dy][dx] += wy * sol_x[dx];
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
const real_t wz = Bt(dz,qz);
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
y(dx,dy,dz,c,e) += wz * sol_xy[dy][dx];
}
}
}
}
}
});
}
static void PAVectorMassApply(const int dim,
const int D1D,
const int Q1D,
const int NE,
const Array<real_t> &B,
const Array<real_t> &Bt,
const Vector &op,
const Vector &x,
Vector &y)
{
if (dim == 2)
{
return PAVectorMassApply2D(NE, B, Bt, op, x, y, D1D, Q1D);
}
if (dim == 3)
{
return PAVectorMassApply3D(NE, B, Bt, op, x, y, D1D, Q1D);
}
MFEM_ABORT("Unknown kernel.");
}
void VectorMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
if (DeviceCanUseCeed())
{
ceedOp->AddMult(x, y);
}
else
{
PAVectorMassApply(dim, dofs1D, quad1D, ne, maps->B, maps->Bt, pa_data, x, y);
MFEM_VERIFY(coeff_vdim == 1, "coeff_vdim != 1");
MFEM_VERIFY(!VQ && !MQ, "VQ and MQ not supported");
PAVectorMassAssembleDiagonal(dim, dofs1D, quad1D, ne, maps->B, pa_data, diag);
}
}
+212
View File
@@ -0,0 +1,212 @@
// 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/array.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../linalg/vector.hpp"
#include "../bilininteg.hpp"
#include "../kernels.hpp"
using mfem::kernels::internal::SetMaxOf;
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
namespace internal
{
template <int T_D1D = 0, int T_Q1D = 0>
void SmemPAVectorMassApply2D(const int NE,
const int coeff_vdim,
const Array<real_t> &b,
const Vector &d,
const Vector &x,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
static constexpr int DIM = 2, VDIM = 2;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const bool const_coeff = coeff_vdim == 1;
const bool vector_coeff = coeff_vdim == DIM;
const bool matrix_coeff = coeff_vdim == DIM*DIM;
const auto B = b.Read();
const auto D = Reshape(d.Read(), Q1D, Q1D, coeff_vdim, NE);
const auto X = Reshape(x.Read(), D1D, D1D, VDIM, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
MFEM_SHARED real_t sB[MD1][MQ1], smem[MQ1][MQ1];
kernels::internal::v_regs2d_t<VDIM, MQ1> r0, r1;
kernels::internal::LoadMatrix(D1D, Q1D, B, sB);
kernels::internal::LoadDofs2d(e, D1D, X, r0);
kernels::internal::Eval2d(D1D, Q1D, smem, sB, r0, r1);
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const real_t Qx = r1[0][qy][qx];
const real_t Qy = r1[1][qy][qx];
const real_t D0 = D(qx, qy, 0, e);
if (const_coeff)
{
r0[0][qy][qx] = D0 * Qx;
r0[1][qy][qx] = D0 * Qy;
}
if (vector_coeff)
{
const real_t D1 = D(qx, qy, 1, e);
r0[0][qy][qx] = D0 * Qx;
r0[1][qy][qx] = D1 * Qy;
}
if (matrix_coeff)
{
const real_t D1 = D(qx, qy, 1, e);
const real_t D2 = D(qx, qy, 2, e);
const real_t D3 = D(qx, qy, 3, e);
r0[0][qy][qx] = D0 * Qx + D1 * Qy;
r0[1][qy][qx] = D2 * Qx + D3 * Qy;
}
}
}
kernels::internal::EvalTranspose2d(D1D, Q1D, smem, sB, r0, r1);
kernels::internal::WriteDofs2d(e, D1D, r1, Y);
});
}
template <int T_D1D = 0, int T_Q1D = 0>
void SmemPAVectorMassApply3D(const int NE,
const int coeff_vdim,
const Array<real_t> &b,
const Vector &d,
const Vector &x,
Vector &y,
const int d1d = 0,
const int q1d = 0)
{
static constexpr int VDIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const bool const_coeff = coeff_vdim == 1;
const bool vector_coeff = coeff_vdim == VDIM;
const bool matrix_coeff = coeff_vdim == VDIM*VDIM;
const auto B = b.Read();
const auto D = Reshape(d.Read(), Q1D, Q1D, Q1D, coeff_vdim, NE);
const auto X = Reshape(x.Read(), D1D, D1D, D1D, VDIM, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
MFEM_SHARED real_t sB[MD1][MQ1], smem[MQ1][MQ1];
kernels::internal::v_regs3d_t<VDIM, MQ1> r0, r1;
kernels::internal::LoadMatrix(D1D, Q1D, B, sB);
kernels::internal::LoadDofs3d(e, D1D, X, r0);
kernels::internal::Eval3d(D1D, Q1D, smem, sB, r0, r1);
for (int qz = 0; qz < Q1D; qz++)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const real_t Qx = r1[0][qz][qy][qx];
const real_t Qy = r1[1][qz][qy][qx];
const real_t Qz = r1[2][qz][qy][qx];
const real_t D0 = D(qx, qy, qz, 0, e);
if (const_coeff)
{
r0[0][qz][qy][qx] = D0 * Qx;
r0[1][qz][qy][qx] = D0 * Qy;
r0[2][qz][qy][qx] = D0 * Qz;
}
if (vector_coeff)
{
const real_t D1 = D(qx, qy, qz, 1, e);
const real_t D2 = D(qx, qy, qz, 2, e);
r0[0][qz][qy][qx] = D0 * Qx;
r0[1][qz][qy][qx] = D1 * Qy;
r0[2][qz][qy][qx] = D2 * Qz;
}
if (matrix_coeff)
{
const real_t D1 = D(qx, qy, qz, 1, e);
const real_t D2 = D(qx, qy, qz, 2, e);
const real_t D3 = D(qx, qy, qz, 3, e);
const real_t D4 = D(qx, qy, qz, 4, e);
const real_t D5 = D(qx, qy, qz, 5, e);
const real_t D6 = D(qx, qy, qz, 6, e);
const real_t D7 = D(qx, qy, qz, 7, e);
const real_t D8 = D(qx, qy, qz, 8, e);
r0[0][qz][qy][qx] = D0 * Qx + D1 * Qy + D2 * Qz;
r0[1][qz][qy][qx] = D3 * Qx + D4 * Qy + D5 * Qz;
r0[2][qz][qy][qx] = D6 * Qx + D7 * Qy + D8 * Qz;
}
}
}
}
kernels::internal::EvalTranspose3d(D1D, Q1D, smem, sB, r0, r1);
kernels::internal::WriteDofs3d(e, D1D, r1, Y);
});
}
} // namespace internal
template<int DIM, int T_D1D, int T_Q1D>
VectorMassIntegrator::VectorMassAddMultPAType
VectorMassIntegrator::VectorMassAddMultPA::Kernel()
{
if (DIM == 2)
{
return internal::SmemPAVectorMassApply2D<T_D1D,T_Q1D>;
}
else if (DIM == 3)
{
return internal::SmemPAVectorMassApply3D<T_D1D, T_Q1D>;
}
else { MFEM_ABORT("Unsupported kernel"); }
}
inline VectorMassIntegrator::VectorMassAddMultPAType
VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int d1d, int q1d)
{
if (dim == 2)
{
return internal::SmemPAVectorMassApply2D;
}
else if (dim == 3)
{
return internal::SmemPAVectorMassApply3D;
}
else { MFEM_ABORT("Unsupported kernel"); }
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
+710 -3
View File
@@ -14,6 +14,7 @@
#include "../config/config.hpp"
#include "../linalg/dtensor.hpp"
#include "../linalg/tensor.hpp"
namespace mfem
{
@@ -26,7 +27,713 @@ namespace kernels
namespace internal
{
/// Load B1d matrice into shared memory
// Types for tensors mapped to registers
// - N is the number of threads in each of the x and y dimensions
// - N should not be greater than 32, to have a maximum of 1024 threads
// On GPU, the last two dimensions are set to 0 to match a 2D tile of threads
#if ((defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)) || \
(defined(MFEM_USE_HIP) && defined(__HIP_DEVICE_COMPILE__)))
template <int N = 0>
using s_regs2d_t = mfem::future::tensor<real_t, 0, 0>;
template <int VDIM, int N>
using v_regs2d_t = mfem::future::tensor<real_t, VDIM, 0, 0>;
template <int VDIM, int DIM, int N = 0>
using vd_regs2d_t = mfem::future::tensor<real_t, VDIM, DIM, 0, 0>;
template <int N>
using s_regs3d_t = mfem::future::tensor<real_t, N, 0, 0>;
template <int VDIM, int N>
using v_regs3d_t = mfem::future::tensor<real_t, VDIM, N, 0, 0>;
template <int VDIM, int DIM, int N>
using vd_regs3d_t = mfem::future::tensor<real_t, VDIM, DIM, N, 0, 0>;
// on GPU, SetMaxOf is a no-op, for minimal register usage
constexpr int SetMaxOf(int n) { return n; }
#else
template <int N>
using s_regs2d_t = mfem::future::tensor<real_t, N, N>;
template <int VDIM, int N>
using v_regs2d_t = mfem::future::tensor<real_t, VDIM, N, N>;
template <int VDIM, int DIM, int N>
using vd_regs2d_t = mfem::future::tensor<real_t, VDIM, DIM, N, N>;
template <int N>
using s_regs3d_t = mfem::future::tensor<real_t, N, N, N>;
template <int VDIM, int N>
using v_regs3d_t = mfem::future::tensor<real_t, VDIM, N, N, N>;
template <int VDIM, int DIM, int N>
using vd_regs3d_t = mfem::future::tensor<real_t, VDIM, DIM, N, N, N>;
// on CPU, get next multiple of 4, allowing better alignments
template <int N>
constexpr int NextMultipleOf(int n)
{
static_assert(N > 0 && (N & (N - 1)) == 0, "N must be a power of 2");
return (n + (N - 1)) & ~(N - 1);
}
constexpr int SetMaxOf(int n) { return NextMultipleOf<4>(n); }
#endif // CUDA/HIP && DEVICE_COMPILE
/// 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])
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
N[dy][qx] = M[dy * q1d + qx];
}
}
MFEM_SYNC_THREAD;
}
/// Load 2D input VDIM*DIM vector into given register tensor, specific component
template <int VDIM, int DIM, int MQ1 = 0>
inline MFEM_HOST_DEVICE void LoadDofs2d(const int e, const int d1d, const int c,
const DeviceTensor<4, const real_t> &X,
vd_regs2d_t<VDIM, DIM, MQ1> &Y)
{
for (int d = 0; d < DIM; d++)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
Y[c][d][dy][dx] = X(dx, dy, c, e);
}
}
}
MFEM_SYNC_THREAD;
}
/// Load 2D input VDIM*DIM vector into given register tensor
template <int VDIM, int DIM, int MQ1 = 0>
inline MFEM_HOST_DEVICE void LoadDofs2d(const int e, const int d1d,
const DeviceTensor<4, const real_t> &X,
vd_regs2d_t<VDIM, DIM, MQ1> &Y)
{
for (int c = 0; c < VDIM; ++c) { LoadDofs2d(e, d1d, c, X, Y); }
}
/// Load 2D input VDIM vector into given register tensor
template <int VDIM, int MQ1 = 0>
inline MFEM_HOST_DEVICE void LoadDofs2d(const int e, const int d1d,
const DeviceTensor<4, const real_t> &X,
v_regs2d_t<VDIM, MQ1> &Y)
{
for (int c = 0; c < VDIM; ++c)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
Y[c][dy][dx] = X(dx, dy, c, e);
}
}
}
MFEM_SYNC_THREAD;
}
/// Load 2D input scalar into given register tensor
template <int MQ1 = 0>
inline MFEM_HOST_DEVICE void LoadDofs2d(const int e, const int d1d,
const DeviceTensor<3, const real_t> &X,
s_regs2d_t<MQ1> &Y)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
Y[dy][dx] = X(dx, dy, e);
}
}
MFEM_SYNC_THREAD;
}
/// Write 2D vector into given device tensor, with read (i) write (j) indices
template <int VDIM, int DIM, int MQ1 = 0>
inline MFEM_HOST_DEVICE void WriteDofs2d(const int e, const int d1d,
const int i, const int j,
vd_regs2d_t<VDIM, DIM, MQ1> &X,
const DeviceTensor<4, real_t> &Y)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
real_t y = 0.0;
for (int d = 0; d < DIM; d++) { y += X(i, d, dy, dx); }
Y(dx, dy, j, e) += y;
}
}
MFEM_SYNC_THREAD;
}
/// Write 2D VDIM*DIM vector into given device tensor
template <int VDIM, int DIM, int MQ1 = 0>
inline MFEM_HOST_DEVICE void WriteDofs2d(const int e, const int d1d,
vd_regs2d_t<VDIM, DIM, MQ1> &X,
const DeviceTensor<4, real_t> &Y)
{
for (int c = 0; c < VDIM; ++c) { WriteDofs2d(e, d1d, c, c, X, Y); }
}
/// Write 2D VDIM vector into given device tensor
template <int VDIM, int MQ1 = 0>
inline MFEM_HOST_DEVICE void WriteDofs2d(const int e, const int d1d,
v_regs2d_t<VDIM, MQ1> &X,
const DeviceTensor<4, real_t> &Y)
{
for (int c = 0; c < VDIM; ++c)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
Y(dx, dy, c, e) += X(c, dy, dx);
}
}
}
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,
const DeviceTensor<5, 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, e);
}
}
}
}
MFEM_SYNC_THREAD;
}
/// Load 3D input VDIM*DIM vector into given register tensor
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void LoadDofs3d(const int e, const int d1d,
const DeviceTensor<5, const real_t> &X,
vd_regs3d_t<VDIM, DIM, MQ1> &Y)
{
for (int c = 0; c < VDIM; ++c) { LoadDofs3d(e, d1d, c, X, Y); }
}
/// Load 3D input VDIM vector into given register tensor
template <int VDIM, int MQ1>
inline MFEM_HOST_DEVICE void LoadDofs3d(const int e, const int d1d,
const DeviceTensor<5, const real_t> &X,
v_regs3d_t<VDIM, MQ1> &Y)
{
for (int c = 0; c < VDIM; ++c)
{
for (int dz = 0; dz < d1d; ++dz)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
Y[c][dz][dy][dx] = X(dx,dy,dz,c,e);
}
}
}
}
MFEM_SYNC_THREAD;
}
/// Load 3D input scalar into given register tensor
template <int MQ1>
inline MFEM_HOST_DEVICE void LoadDofs3d(const int e, const int d1d,
const DeviceTensor<4, const real_t> &X,
s_regs3d_t<MQ1> &Y)
{
for (int dz = 0; dz < d1d; ++dz)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
Y[dz][dy][dx] = X(dx,dy,dz,e);
}
}
}
MFEM_SYNC_THREAD;
}
/// Write 3D scalar into given device tensor, with read (i) write (j) indices
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void WriteDofs3d(const int e, const int d1d,
const int i, const int j,
vd_regs3d_t<VDIM, DIM, MQ1> &X,
const DeviceTensor<5, real_t> &Y)
{
for (int dz = 0; dz < d1d; ++dz)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
real_t value = 0.0;
for (int d = 0; d < DIM; d++) { value += X(i, d, dz, dy, dx); }
Y(dx, dy, dz, j, e) += value;
}
}
}
MFEM_SYNC_THREAD;
}
/// Write 3D VDIM*DIM vector into given device tensor
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void WriteDofs3d(const int e, const int d1d,
vd_regs3d_t<VDIM, DIM, MQ1> &X,
const DeviceTensor<5, real_t> &Y)
{
for (int c = 0; c < VDIM; ++c) { WriteDofs3d(e, d1d, c, c, X, Y); }
}
/// Write 3D VDIM vector into given device tensor
template <int VDIM, int MQ1>
inline MFEM_HOST_DEVICE void WriteDofs3d(const int e, const int d1d,
v_regs3d_t<VDIM, MQ1> &X,
const DeviceTensor<5, real_t> &Y)
{
for (int c = 0; c < VDIM; ++c)
{
for (int dz = 0; dz < d1d; ++dz)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, d1d)
{
Y(dx, dy, dz, c, e) += X(c, 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,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const s_regs2d_t<MQ1> &X,
s_regs2d_t<MQ1> &Y)
{
MFEM_FOREACH_THREAD_DIRECT(y, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(x, x, (Transpose ? q1d : d1d))
{
smem[y][x] = X[y][x];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(y, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(x, x, (Transpose ? d1d : q1d))
{
real_t u = 0.0;
for (int k = 0; k < (Transpose ? q1d : d1d); ++k)
{
u += (Transpose ? B[x][k] : B[k][x]) * smem[y][k];
}
Y[y][x] = u;
}
}
MFEM_SYNC_THREAD;
}
/// 2D scalar contraction, Y direction
template <bool Transpose, int MQ1>
inline MFEM_HOST_DEVICE void ContractY2d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const s_regs2d_t<MQ1> &X,
s_regs2d_t<MQ1> &Y)
{
MFEM_FOREACH_THREAD_DIRECT(y, y, (Transpose ? q1d : d1d))
{
MFEM_FOREACH_THREAD_DIRECT(x, x, q1d) { smem[y][x] = X[y][x]; }
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(y, y, (Transpose ? d1d : q1d))
{
MFEM_FOREACH_THREAD_DIRECT(x, x, q1d)
{
real_t u = 0.0;
for (int k = 0; k < (Transpose ? q1d : d1d); ++k)
{
u += (Transpose ? B[y][k] : B[k][y]) * smem[k][x];
}
Y[y][x] = u;
}
}
MFEM_SYNC_THREAD;
}
/// 2D scalar copy
template <int MQ1 = 0>
inline MFEM_HOST_DEVICE void Copy2d(const int q1d,
s_regs2d_t<MQ1> &X,
s_regs2d_t<MQ1> &Y)
{
MFEM_FOREACH_THREAD_DIRECT(y, y, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(x, x, q1d) { Y[y][x] = X[y][x]; }
}
MFEM_SYNC_THREAD;
}
/// 2D scalar contraction: X & Y directions, with additional copy
template <bool Transpose, int MQ1>
inline MFEM_HOST_DEVICE void Contract2d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*Bx)[MQ1],
const real_t (*By)[MQ1],
s_regs2d_t<MQ1> &X,
s_regs2d_t<MQ1> &Y)
{
if (!Transpose)
{
ContractX2d<false>(d1d, q1d, smem, Bx, X, Y);
ContractY2d<false>(d1d, q1d, smem, By, Y, X);
Copy2d(q1d, X, Y);
}
else
{
Copy2d(q1d, X, Y);
ContractY2d<true>(d1d, q1d, smem, By, Y, X);
ContractX2d<true>(d1d, q1d, smem, Bx, X, Y);
}
}
/// 2D scalar evaluation
template <int MQ1, bool Transpose = false>
inline MFEM_HOST_DEVICE void Eval2d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
s_regs2d_t<MQ1> &X,
s_regs2d_t<MQ1> &Y)
{
Contract2d<Transpose, MQ1>(d1d, q1d, smem, B, B, X, Y);
}
/// 2D vector evaluation
template <int VDIM, int MQ1, bool Transpose = false>
inline MFEM_HOST_DEVICE void Eval2d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
v_regs2d_t<VDIM, MQ1> &X,
v_regs2d_t<VDIM, MQ1> &Y)
{
for (int c = 0; c < VDIM; c++)
{
Eval2d<MQ1, Transpose>(d1d, q1d, smem, B, X[c], Y[c]);
}
}
/// 2D vector transposed evaluation
template <int VDIM, int MQ1>
inline MFEM_HOST_DEVICE void EvalTranspose2d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
v_regs2d_t<VDIM, MQ1> &X,
v_regs2d_t<VDIM, MQ1> &Y)
{
Eval2d<VDIM, MQ1, true>(d1d, q1d, smem, B, X, Y);
}
/// 2D vector gradient, with component
template <int VDIM, int DIM, int MQ1, bool Transpose = false>
inline MFEM_HOST_DEVICE void Grad2d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
vd_regs2d_t<VDIM, DIM, MQ1> &X,
vd_regs2d_t<VDIM, DIM, MQ1> &Y,
const int c)
{
for (int d = 0; d < DIM; d++)
{
const real_t (*Bx)[MQ1] = (d == 0) ? G : B;
const real_t (*By)[MQ1] = (d == 1) ? G : B;
Contract2d<Transpose>(d1d, q1d, smem, Bx, By, X[c][d], Y[c][d]);
}
}
/// 2D vector gradient
template <int VDIM, int DIM, int MQ1, bool Transpose = false>
inline MFEM_HOST_DEVICE void Grad2d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
vd_regs2d_t<VDIM, DIM, MQ1> &X,
vd_regs2d_t<VDIM, DIM, MQ1> &Y)
{
for (int c = 0; c < VDIM; ++c)
{
Grad2d<VDIM, DIM, MQ1, Transpose>(d1d, q1d, smem, B, G, X, Y, c);
}
}
/// 2D vector transposed gradient
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradTranspose2d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
vd_regs2d_t<VDIM, DIM, MQ1> &X,
vd_regs2d_t<VDIM, DIM, MQ1> &Y)
{
constexpr bool Transpose = true;
Grad2d<VDIM, DIM, MQ1, Transpose>(d1d, q1d, smem, B, G, X, Y);
}
/// 2D scalar contraction, with component
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradTranspose2d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
vd_regs2d_t<VDIM, DIM, MQ1> &X,
vd_regs2d_t<VDIM, DIM, MQ1> &Y,
const int c)
{
constexpr bool Transpose = true;
Grad2d<VDIM, DIM, MQ1, Transpose>(d1d, q1d, smem, B, G, X, Y, c);
}
/// 3D scalar contraction, X direction
template <bool Transpose, int MQ1>
inline MFEM_HOST_DEVICE void ContractX3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const s_regs3d_t<MQ1> &X,
s_regs3d_t<MQ1> &Y)
{
for (int z = 0; z < d1d; ++z)
{
MFEM_FOREACH_THREAD_DIRECT(y, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(x, x, (Transpose ? q1d : d1d))
{
smem[y][x] = X[z][y][x];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(y, y, d1d)
{
MFEM_FOREACH_THREAD_DIRECT(x, x, (Transpose ? d1d : q1d))
{
real_t u = 0.0;
for (int k = 0; k < (Transpose ? q1d : d1d); ++k)
{
u += (Transpose ? B[x][k] : B[k][x]) * smem[y][k];
}
Y[z][y][x] = u;
}
}
MFEM_SYNC_THREAD;
}
}
/// 3D scalar contraction, Y direction
template <bool Transpose, int MQ1>
inline MFEM_HOST_DEVICE void ContractY3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const s_regs3d_t<MQ1> &X,
s_regs3d_t<MQ1> &Y)
{
for (int z = 0; z < d1d; ++z)
{
MFEM_FOREACH_THREAD_DIRECT(y, y, (Transpose ? q1d : d1d))
{
MFEM_FOREACH_THREAD_DIRECT(x, x, q1d) { smem[y][x] = X[z][y][x]; }
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(y, y, (Transpose ? d1d : q1d))
{
MFEM_FOREACH_THREAD_DIRECT(x, x, q1d)
{
real_t u = 0.0;
for (int k = 0; k < (Transpose ? q1d : d1d); ++k)
{
u += (Transpose ? B[y][k] : B[k][y]) * smem[k][x];
}
Y[z][y][x] = u;
}
}
MFEM_SYNC_THREAD;
}
}
/// 3D scalar contraction, Z direction
template <bool Transpose, int MQ1>
inline MFEM_HOST_DEVICE void ContractZ3d(const int d1d, const int q1d,
const real_t (*B)[MQ1],
const s_regs3d_t<MQ1> &X,
s_regs3d_t<MQ1> &Y)
{
for (int z = 0; z < (Transpose ? d1d : q1d); ++z)
{
MFEM_FOREACH_THREAD_DIRECT(y, y, q1d)
{
MFEM_FOREACH_THREAD_DIRECT(x, x, q1d)
{
real_t u = 0.0;
for (int k = 0; k < (Transpose ? q1d : d1d); ++k)
{
u += (Transpose ? B[z][k] : B[k][z]) * X[k][y][x];
}
Y[z][y][x] = u;
}
}
}
}
/// 3D scalar contraction: X, Y & Z directions
template <bool Transpose, int MQ1>
inline MFEM_HOST_DEVICE void Contract3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*Bx)[MQ1],
const real_t (*By)[MQ1],
const real_t (*Bz)[MQ1],
s_regs3d_t<MQ1> &X,
s_regs3d_t<MQ1> &Y)
{
if (!Transpose)
{
ContractX3d<false>(d1d, q1d, smem, Bx, X, Y);
ContractY3d<false>(d1d, q1d, smem, By, Y, X);
ContractZ3d<false>(d1d, q1d, Bz, X, Y);
}
else
{
ContractZ3d<true>(d1d, q1d, Bz, X, Y);
ContractY3d<true>(d1d, q1d, smem, By, Y, X);
ContractX3d<true>(d1d, q1d, smem, Bx, X, Y);
}
}
/// 3D scalar evaluation
template <int MQ1, bool Transpose = false>
inline MFEM_HOST_DEVICE void Eval3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
s_regs3d_t<MQ1> &X,
s_regs3d_t<MQ1> &Y)
{
Contract3d<Transpose>(d1d, q1d, smem, B, B, B, X, Y);
}
/// 3D vector evaluation
template <int VDIM, int MQ1, bool Transpose = false>
inline MFEM_HOST_DEVICE void Eval3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
v_regs3d_t<VDIM, MQ1> &X,
v_regs3d_t<VDIM, MQ1> &Y)
{
for (int c = 0; c < VDIM; c++)
{
Eval3d<MQ1, Transpose>(d1d, q1d, smem, B, X[c], Y[c]);
}
}
/// 3D vector transposed evaluation
template <int VDIM, int MQ1>
inline MFEM_HOST_DEVICE void EvalTranspose3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
v_regs3d_t<VDIM, MQ1> &X,
v_regs3d_t<VDIM, MQ1> &Y)
{
Eval3d<VDIM, MQ1, true>(d1d, q1d, smem, B, X, Y);
}
/// 3D vector gradient, with component
template <int VDIM, int DIM, int MQ1, bool Transpose = false>
inline MFEM_HOST_DEVICE void Grad3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
vd_regs3d_t<VDIM, DIM, MQ1> &X,
vd_regs3d_t<VDIM, DIM, MQ1> &Y,
const int c)
{
for (int d = 0; d < DIM; d++)
{
const real_t (*Bx)[MQ1] = (d == 0) ? G : B;
const real_t (*By)[MQ1] = (d == 1) ? G : B;
const real_t (*Bz)[MQ1] = (d == 2) ? G : B;
Contract3d<Transpose>(d1d, q1d, smem, Bx, By, Bz, X[c][d], Y[c][d]);
}
}
/// 3D vector gradient
template <int VDIM, int DIM, int MQ1, bool Transpose = false>
inline MFEM_HOST_DEVICE void Grad3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
vd_regs3d_t<VDIM, DIM, MQ1> &X,
vd_regs3d_t<VDIM, DIM, MQ1> &Y)
{
for (int c = 0; c < VDIM; c++)
{
Grad3d<VDIM, DIM, MQ1, Transpose>(d1d, q1d, smem, B, G, X, Y, c);
}
}
/// 3D vector transposed gradient
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradTranspose3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
vd_regs3d_t<VDIM, DIM, MQ1> &X,
vd_regs3d_t<VDIM, DIM, MQ1> &Y)
{
Grad3d<VDIM, DIM, MQ1, true>(d1d, q1d, smem, B, G, X, Y);
}
/// 3D vector transposed gradient, with component
template <int VDIM, int DIM, int MQ1>
inline MFEM_HOST_DEVICE void GradTranspose3d(const int d1d, const int q1d,
real_t (&smem)[MQ1][MQ1],
const real_t (*B)[MQ1],
const real_t (*G)[MQ1],
vd_regs3d_t<VDIM, DIM, MQ1> &X,
vd_regs3d_t<VDIM, DIM, MQ1> &Y,
const int c)
{
Grad3d<VDIM, DIM, MQ1, true>(d1d, q1d, smem, B, G, X, Y, c);
}
/// Load B1d matrix into shared memory
template<int MD1, int MQ1>
MFEM_HOST_DEVICE inline void LoadB(const int D1D, const int Q1D,
const ConstDeviceMatrix &b,
@@ -48,7 +755,7 @@ MFEM_HOST_DEVICE inline void LoadB(const int D1D, const int Q1D,
MFEM_SYNC_THREAD;
}
/// Load Bt1d matrices into shared memory
/// Load Bt1d matrix into shared memory
template<int MD1, int MQ1>
MFEM_HOST_DEVICE inline void LoadBt(const int D1D, const int Q1D,
const ConstDeviceMatrix &b,
@@ -1551,7 +2258,7 @@ MFEM_HOST_DEVICE inline void GradXt(const int D1D, const int Q1D,
}
}
} // namespace kernels::internal
} // namespace internal
} // namespace kernels
+950
View File
@@ -0,0 +1,950 @@
// 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 "particleset.hpp"
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
// Ignore warnings from the gslib header (GCC version)
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
namespace gslib
{
extern "C"
{
#include <gslib.h>
} // extern C
} // namespace gslib
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
namespace mfem
{
Particle::Particle(int dim, const Array<int> &field_vdims, int num_tags)
: coords(dim), fields(), tags()
{
coords = 0.0;
fields.reserve(field_vdims.Size());
for (int f = 0; f < field_vdims.Size(); f++)
{
fields.emplace_back(field_vdims[f]);
fields.back() = 0.0;
}
tags.reserve(num_tags);
for (int t = 0; t < num_tags; t++)
{
tags.emplace_back(1);
tags.back()[0] = 0;
}
}
void Particle::SetTagRef(int t, int *tag_data)
{
MFEM_ASSERT(t >= 0 &&
static_cast<size_t>(t) < tags.size(), "Invalid tag index");
tags[t].MakeRef(tag_data, 1);
}
void Particle::SetFieldRef(int f, real_t *field_data)
{
MFEM_ASSERT(f >= 0 &&
static_cast<size_t>(f) < fields.size(), "Invalid field "
"index");
Vector temp(field_data, fields[f].Size());
fields[f].MakeRef(temp, 0, fields[f].Size());
}
bool Particle::operator==(const Particle &rhs) const
{
// Compare coordinate size and values
if (coords.Size() != rhs.coords.Size())
{
return false;
}
for (int d = 0; d < coords.Size(); d++)
{
if (coords[d] != rhs.coords[d])
{
return false;
}
}
// Compare fields vdim and values
if (fields.size() != rhs.fields.size())
{
return false;
}
for (size_t f = 0; f < fields.size(); f++)
{
if (fields[f].Size() != rhs.fields[f].Size())
{
return false;
}
for (int c = 0; c < fields[f].Size(); c++)
{
if (fields[f][c] != rhs.fields[f][c])
{
return false;
}
}
}
// Compare tags size and values
if (tags.size() != rhs.tags.size())
{
return false;
}
for (size_t t = 0; t < tags.size(); t++)
{
if (tags[t][0] != rhs.tags[t][0])
{
return false;
}
}
return true;
}
void Particle::Print(std::ostream &os) const
{
os << "Coords: (";
for (int d = 0; d < coords.Size(); d++)
{
os << coords[d] << ( (d+1 < coords.Size()) ? "," : ")\n");
}
for (size_t f = 0; f < fields.size(); f++)
{
os << "Field " << f << ": (";
for (int c = 0; c < fields[f].Size(); c++)
{
os << fields[f][c] << ( (c+1 < fields[f].Size()) ? "," : ")\n");
}
}
for (size_t t = 0; t < tags.size(); t++)
{
os << "Tag " << t << ": " << tags[t][0] << "\n";
}
}
Array<Ordering::Type> ParticleSet::GetOrderingArray(Ordering::Type o, int N)
{
Array<Ordering::Type> ordering_arr(N);
ordering_arr = o;
return ordering_arr;
}
std::string ParticleSet::GetDefaultFieldName(int i)
{
return "Field_" + std::to_string(i);
}
std::string ParticleSet::GetDefaultTagName(int i)
{
return "Tag_" + std::to_string(i);
}
Array<const char*> ParticleSet::GetEmptyNameArray(int N)
{
Array<const char*> names(N);
names = nullptr;
return names;
}
#ifdef MFEM_USE_MPI
int ParticleSet::GetRank(MPI_Comm comm_)
{
int r; MPI_Comm_rank(comm_, &r);
return r;
}
int ParticleSet::GetSize(MPI_Comm comm_)
{
int s; MPI_Comm_size(comm_, &s);
return s;
}
#endif // MFEM_USE_MPI
void ParticleSet::Reserve(int res)
{
ids.Reserve(res);
// Reserve fields
for (int f = -1; f < GetNFields(); f++)
{
ParticleVector &pv = (f == -1 ? coords : *fields[f]);
pv.Reserve(res*pv.GetVDim());
}
// Reserve tags
for (int t = 0; t < GetNTags(); t++)
{
tags[t]->Reserve(res);
}
}
const Array<int> ParticleSet::GetFieldVDims() const
{
Array<int> field_vdims(GetNFields());
for (int f = 0; f < GetNFields(); f++)
{
field_vdims[f] = Field(f).GetVDim();
}
return field_vdims;
}
void ParticleSet::AddParticles(const Array<IDType> &new_ids,
Array<int> *new_indices)
{
int num_add = new_ids.Size();
int old_np = GetNParticles();
int new_np = old_np + num_add;
// Set indices of new particles
if (new_indices)
{
new_indices->SetSize(num_add);
for (int i = 0; i < num_add; i++)
{
(*new_indices)[i] = ids.Size() + i;
}
}
// Add new ids
ids.Append(new_ids);
// Update data
for (int f = -1; f < GetNFields(); f++)
{
ParticleVector &pv = (f == -1 ? coords : *fields[f]);
pv.SetNumParticles(new_np); // does not delete existing data
}
// Update tags
for (int t = 0; t < GetNTags(); t++)
{
tags[t]->SetSize(new_np);
}
}
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
/// \cond DO_NOT_DOCUMENT
template<size_t NBytes>
void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
const Array<int> &send_idxs,
const Array<unsigned int> &send_ranks)
{
struct pdata_t
{
alignas(real_t) std::array<std::byte, NBytes> data;
IDType id;
};
int nreals = pset.GetFieldVDims().Sum() + pset.Coords().GetVDim();
int ntags = pset.GetNTags();
size_t nbytes = nreals*sizeof(real_t) + ntags*sizeof(int);
MFEM_VERIFY(nbytes <= NBytes, "More data than can be packed.");
using parr_t = pdata_t;
gslib::array gsl_arr;
parr_t *pdata_arr;
array_init(parr_t, &gsl_arr, send_idxs.Size());
pdata_arr = (parr_t*) gsl_arr.ptr;
gsl_arr.n = send_idxs.Size();
for (int i = 0; i < send_idxs.Size(); i++)
{
parr_t &pdata = pdata_arr[i];
pdata.id = pset.GetIDs()[send_idxs[i]];
// Copy particle data directly into pdata
size_t counter = 0;
for (int f = -1; f < pset.GetNFields(); f++)
{
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
for (int c = 0; c < pv.GetVDim(); c++)
{
std::memcpy(pdata.data.data() + counter, &pv(send_idxs[i], c),
sizeof(real_t));
counter += sizeof(real_t);
}
}
// Copy tags
for (int t = 0; t < pset.GetNTags(); t++)
{
Array<int> &tag_arr = pset.Tag(t);
std::memcpy(pdata.data.data() + counter, &tag_arr[send_idxs[i]],
sizeof(int));
counter += sizeof(int);
}
}
int nparticles = pset.GetNParticles();
int nsend = send_idxs.Size();
// Transfer particles
sarray_transfer_ext(parr_t, &gsl_arr, send_ranks.GetData(),
sizeof(unsigned int), pset.cr);
// Make sure we have enough space for received particles
int nrecv = (int) gsl_arr.n;
int ndelete = nsend - nrecv;
if (ndelete > 0)
{
// Remove unneeded particles
auto datap = const_cast<int*>(send_idxs.GetData());
Array<int> delete_idxs(datap + nrecv, ndelete);
pset.RemoveParticles(delete_idxs);
}
else
{
pset.Reserve(nparticles-ndelete);
}
pdata_arr = (parr_t*) gsl_arr.ptr;
// Add newly-recvd data directly to active state
for (int i = 0; i < nrecv; i++)
{
parr_t &pdata = pdata_arr[i];
IDType id = pdata.id;
int new_loc_idx;
if (i < nsend) // update existing particle
{
new_loc_idx = send_idxs[i];
pset.UpdateID(new_loc_idx, id);
}
else
{
// add new particle
Array<int> idx_temp;
pset.AddParticles(Array<IDType>({id}), &idx_temp);
new_loc_idx = idx_temp[0]; // Get index of newly-added particle
}
size_t counter = 0;
for (int f = -1; f < pset.GetNFields(); f++)
{
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
for (int c = 0; c < pv.GetVDim(); c++)
{
real_t& val = pv(new_loc_idx, c);
std::memcpy(&val, pdata.data.data() + counter, sizeof(real_t));
counter += sizeof(real_t);
}
}
for (int t = 0; t < pset.GetNTags(); t++)
{
Array<int> &tag_arr = pset.Tag(t);
std::memcpy(&tag_arr[new_loc_idx],
pdata.data.data() + counter, sizeof(int));
counter += sizeof(int);
}
}
array_free(&gsl_arr);
}
template<size_t NBytes>
ParticleSet::TransferParticlesType ParticleSet::TransferParticles::Kernel()
{
return &ParticleSet::TransferParticlesImpl<NBytes>;
}
ParticleSet::Kernels::Kernels()
{
constexpr size_t sizd = sizeof(real_t);
TransferParticles::Specialization<2*sizd>::Add();
TransferParticles::Specialization<3*sizd>::Add();
TransferParticles::Specialization<4*sizd>::Add();
TransferParticles::Specialization<8*sizd>::Add();
TransferParticles::Specialization<12*sizd>::Add();
TransferParticles::Specialization<16*sizd>::Add();
TransferParticles::Specialization<20*sizd>::Add();
TransferParticles::Specialization<24*sizd>::Add();
TransferParticles::Specialization<28*sizd>::Add();
TransferParticles::Specialization<32*sizd>::Add();
TransferParticles::Specialization<36*sizd>::Add();
TransferParticles::Specialization<40*sizd>::Add();
}
auto ParticleSet::TransferParticles::Fallback(size_t bufsize)
-> ParticleSet::TransferParticlesType
{
constexpr size_t sizd = sizeof(real_t);
if (bufsize < 4*sizd)
{
return &ParticleSet::TransferParticlesImpl<4*sizd>;
}
else if (bufsize < 8*sizd)
{
return &ParticleSet::TransferParticlesImpl<8*sizd>;
}
else if (bufsize < 12*sizd)
{
return &ParticleSet::TransferParticlesImpl<12*sizd>;
}
else if (bufsize < 16*sizd)
{
return &ParticleSet::TransferParticlesImpl<16*sizd>;
}
else if (bufsize < 20*sizd)
{
return &ParticleSet::TransferParticlesImpl<20*sizd>;
}
else if (bufsize < 24*sizd)
{
return &ParticleSet::TransferParticlesImpl<24*sizd>;
}
else if (bufsize < 28*sizd)
{
return &ParticleSet::TransferParticlesImpl<28*sizd>;
}
else if (bufsize < 32*sizd)
{
return &ParticleSet::TransferParticlesImpl<32*sizd>;
}
else if (bufsize < 36*sizd)
{
return &ParticleSet::TransferParticlesImpl<36*sizd>;
}
else if (bufsize < 40*sizd)
{
return &ParticleSet::TransferParticlesImpl<40*sizd>;
}
return &ParticleSet::TransferParticlesImpl<60*sizd>;
}
/// \endcond DO_NOT_DOCUMENT
void ParticleSet::Redistribute(const Array<unsigned int> &rank_list)
{
MFEM_ASSERT(rank_list.Size() == GetNParticles(),
"rank_list must be of size GetNParticles().");
int rank = GetRank(comm);
// Get particles to be transferred
// (Avoid unnecessary copies of particle data into and out of buffers)
Array<int> send_idxs;
Array<unsigned int> send_ranks;
send_idxs.Reserve(rank_list.Size());
send_ranks.Reserve(rank_list.Size());
for (int i = 0; i < rank_list.Size(); i++)
{
if (rank != static_cast<int>(rank_list[i]))
{
send_idxs.Append(i);
send_ranks.Append(rank_list[i]);
}
}
// Compute number of bytes of a single particle
int nreals = GetFieldVDims().Sum() + coords.GetVDim();
int ntags = GetNTags();
size_t nbytes = nreals*sizeof(real_t) + ntags*sizeof(int);
// Dispatch to appropriate redistribution function for this size
TransferParticles::Run(nbytes, *this, send_idxs, send_ranks);
}
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
Particle ParticleSet::CreateParticle() const
{
return Particle(GetDim(), GetFieldVDims(), GetNTags());
}
void ParticleSet::WriteToFile(const char *fname,
const std::stringstream &ss_header, const std::stringstream &ss_data)
{
#ifdef MFEM_USE_MPI
// Parallel:
int rank = GetRank(comm);
MPI_File_delete(fname, MPI_INFO_NULL); // delete old file if it exists
MPI_File file;
int mpi_err = MPI_File_open(comm, fname, MPI_MODE_CREATE | MPI_MODE_WRONLY,
MPI_INFO_NULL, &file);
MFEM_VERIFY(mpi_err == MPI_SUCCESS, "MPI_File_open failed.");
// Print header
if (rank == 0)
{
MPI_File_write_at(file, 0, ss_header.str().data(), ss_header.str().size(),
MPI_CHAR, MPI_STATUS_IGNORE);
}
// Compute the data size in bytes
MPI_Offset data_size = ss_data.str().size();
MPI_Offset offset;
// Compute the offsets using an exclusive scan
MPI_Exscan(&data_size, &offset, 1, MPI_OFFSET, MPI_SUM, comm);
if (rank == 0)
{
offset = 0;
}
// Add offset from the header
offset += ss_header.str().size();
// Write data collectively
MPI_File_write_at_all(file, offset, ss_data.str().data(),
data_size, MPI_BYTE, MPI_STATUS_IGNORE);
// Close file
MPI_File_close(&file);
#else
// Serial:
std::ofstream ofs(fname);
MFEM_VERIFY(ofs.is_open() && !ofs.fail(),
"Error: Could not open file " << fname << " for writing.");
ofs << ss_header.str() << ss_data.str();
ofs.close();
#endif // MFEM_USE_MPI
}
ParticleSet::ParticleSet(int id_stride_, IDType id_counter_, int num_particles,
int dim, Ordering::Type coords_ordering, const Array<int> &field_vdims,
const Array<Ordering::Type> &field_orderings,
const Array<const char*> &field_names_, int num_tags,
const Array<const char*> &tag_names_)
: id_stride(id_stride_),
id_counter(id_counter_),
coords(dim, coords_ordering)
{
// Initialize fields
for (int f = 0; f < field_vdims.Size(); f++)
{
AddField(field_vdims[f], field_orderings[f], field_names_[f]);
}
// Initialize tags
for (int t = 0; t < num_tags; t++)
{
AddTag(tag_names_[t]);
}
// Add num_particles
Array<IDType> init_ids(num_particles);
for (int i = 0; i < num_particles; i++)
{
init_ids[i] = id_counter;
id_counter += id_stride;
}
AddParticles(init_ids);
}
bool ParticleSet::IsValidParticle(const Particle &p) const
{
if (p.GetDim() != GetDim())
{
return false;
}
if (p.GetNFields() != GetNFields())
{
return false;
}
for (int f = 0; f < GetNFields(); f++)
{
if (p.GetFieldVDim(f) != Field(f).GetVDim())
{
return false;
}
}
if (p.GetNTags() != GetNTags())
{
return false;
}
return true;
}
ParticleSet::ParticleSet(int num_particles, int dim,
Ordering::Type coords_ordering)
: ParticleSet(1, 0, num_particles, dim, coords_ordering, Array<int>(),
Array<Ordering::Type>(), Array<const char*>(), 0,
Array<const char*>())
{
}
ParticleSet::ParticleSet(int num_particles, int dim,
const Array<int> &field_vdims, int num_tags,
Ordering::Type all_ordering)
: ParticleSet(1, 0, num_particles, dim, all_ordering, field_vdims,
GetOrderingArray(all_ordering, field_vdims.Size()),
GetEmptyNameArray(field_vdims.Size()), num_tags,
GetEmptyNameArray(num_tags))
{
}
ParticleSet::ParticleSet(int num_particles, int dim,
const Array<int> &field_vdims, const Array<const
char*> &field_names_, int num_tags,
const Array<const char*> &tag_names_,
Ordering::Type all_ordering)
: ParticleSet(1, 0, num_particles, dim, all_ordering, field_vdims,
GetOrderingArray(all_ordering, field_vdims.Size()),
field_names_, num_tags,
tag_names_)
{
}
ParticleSet::ParticleSet(int num_particles, int dim,
Ordering::Type coords_ordering,
const Array<int> &field_vdims,
const Array<Ordering::Type> &field_orderings,
const Array<const char*> &field_names_, int num_tags,
const Array<const char*> &tag_names_)
: ParticleSet(1, 0, num_particles, dim, coords_ordering, field_vdims,
field_orderings, field_names_, num_tags, tag_names_)
{
}
#ifdef MFEM_USE_MPI
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
Ordering::Type coords_ordering)
: ParticleSet(comm_, rank_num_particles, dim, coords_ordering, Array<int>(),
Array<Ordering::Type>(), Array<const char*>(), 0,
Array<const char*>())
{
};
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
const Array<int> &field_vdims, int num_tags,
Ordering::Type all_ordering)
: ParticleSet(comm_, rank_num_particles, dim, all_ordering, field_vdims,
GetOrderingArray(all_ordering, field_vdims.Size()),
GetEmptyNameArray(field_vdims.Size()), num_tags,
GetEmptyNameArray(num_tags))
{
}
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
const Array<int> &field_vdims, const Array<const
char*> &field_names_,
int num_tags, const Array<const char*> &tag_names_,
Ordering::Type all_ordering)
: ParticleSet(comm_, rank_num_particles, dim, all_ordering, field_vdims,
GetOrderingArray(all_ordering, field_vdims.Size()),
field_names_, num_tags,
tag_names_)
{
}
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
Ordering::Type coords_ordering,
const Array<int> &field_vdims,
const Array<Ordering::Type> &field_orderings,
const Array<const char*> &field_names_, int num_tags,
const Array<const char*> &tag_names_)
: ParticleSet(GetSize(comm_), (IDType)GetRank(comm_),
rank_num_particles,
dim,
coords_ordering,
field_vdims,
field_orderings,
field_names_,
num_tags,
tag_names_)
{
comm = comm_;
#ifdef MFEM_USE_GSLIB
gsl_comm = new gslib::comm;
cr = new gslib::crystal;
comm_init(gsl_comm, comm);
crystal_init(cr, gsl_comm);
#endif // MFEM_USE_GSLIB
}
#endif // MFEM_USE_MPI
ParticleSet::IDType ParticleSet::GetGlobalNParticles() const
{
IDType total = (IDType)GetNParticles();
#ifdef MFEM_USE_MPI
MPI_Allreduce(MPI_IN_PLACE, &total, 1, MPI_UNSIGNED_LONG_LONG,
MPI_SUM, comm);
#endif // MFEM_USE_MPI
return total;
}
int ParticleSet::AddField(int vdim, Ordering::Type field_ordering,
const char* field_name)
{
std::string field_name_str(field_name ? field_name : "");
if (!field_name)
{
field_name_str = GetDefaultFieldName(field_names.size());
}
fields.emplace_back(std::make_unique<ParticleVector>(vdim, field_ordering,
GetNParticles()));
field_names.emplace_back(field_name_str);
return GetNFields() - 1;
}
int ParticleSet::AddTag(const char* tag_name)
{
std::string tag_name_str(tag_name ? tag_name : "");
if (!tag_name)
{
tag_name_str = GetDefaultTagName(tag_names.size());
}
tags.emplace_back(std::make_unique<Array<int>>(GetNParticles()));
tag_names.emplace_back(tag_name_str);
return GetNTags() - 1;
}
void ParticleSet::AddParticle(const Particle &p)
{
MFEM_ASSERT(IsValidParticle(p),
"Particle is incompatible with ParticleSet.");
// Add the particle
Array<int> idxs;
AddParticles(Array<IDType>({id_counter}), &idxs);
id_counter += id_stride;
// Set the new particle data
int idx = idxs[0];
SetParticle(idx, p);
}
void ParticleSet::AddParticles(int num_particles, Array<int> *new_indices)
{
Array<IDType> add_ids(num_particles);
for (int i = 0; i < num_particles; i++)
{
add_ids[i] = id_counter;
id_counter += id_stride;
}
AddParticles(add_ids, new_indices);
}
void ParticleSet::RemoveParticles(const Array<int> &list)
{
// Delete IDs
ids.DeleteAt(list);
// Delete data
for (int f = -1; f < GetNFields(); f++)
{
ParticleVector &pv = (f == -1 ? coords : *fields[f]);
pv.DeleteParticles(list);
}
// Delete tags
for (int t = 0; t < GetNTags(); t++)
{
tags[t]->DeleteAt(list);
}
}
Particle ParticleSet::GetParticle(int i) const
{
Particle p = CreateParticle();
Coords().GetValues(i, p.Coords());
for (int f = 0; f < GetNFields(); f++)
{
Field(f).GetValues(i, p.Field(f));
}
for (int t = 0; t < GetNTags(); t++)
{
p.Tag(t) = Tag(t)[i];
}
return p;
}
bool ParticleSet::IsParticleRefValid() const
{
if (coords.GetOrdering() == Ordering::byNODES)
{
return false;
}
for (int f = 0; f < GetNFields(); f++)
{
if (fields[f]->GetOrdering() == Ordering::byNODES)
{
return false;
}
}
return true;
}
Particle ParticleSet::GetParticleRef(int i)
{
Particle p = CreateParticle();
Coords().GetValuesRef(i, p.Coords());
for (int f = 0; f < GetNFields(); f++)
{
MFEM_ASSERT(Field(f).GetOrdering() == Ordering::byVDIM,
"GetParticleRef only valid when all fields ordered byVDIM.");
p.SetFieldRef(f, Field(f).GetData() + i*Field(f).GetVDim());
}
for (int t = 0; t < GetNTags(); t++)
{
p.SetTagRef(t, &(*tags[t])[i]);
}
return p;
}
void ParticleSet::SetParticle(int i, const Particle &p)
{
MFEM_ASSERT(IsValidParticle(p),
"Particle is incompatible with ParticleSet.");
Coords().SetValues(i, p.Coords());
for (int f = 0; f < GetNFields(); f++)
{
Field(f).SetValues(i, p.Field(f));
}
for (int t = 0; t < GetNTags(); t++)
{
Tag(t)[i] = p.Tag(t);
}
}
void ParticleSet::PrintCSV(const char *fname, int precision)
{
Array<int> all_field_idxs(GetNFields()), all_tag_idxs(GetNTags());
for (int f = 0; f < GetNFields(); f++)
{
all_field_idxs[f] = f;
}
for (int t = 0; t < GetNTags(); t++)
{
all_tag_idxs[t] = t;
}
PrintCSV(fname, all_field_idxs, all_tag_idxs, precision);
}
void ParticleSet::PrintCSV(const char *fname, const Array<int> &field_idxs,
const Array<int> &tag_idxs, int precision)
{
std::stringstream ss_header;
// Configure header:
ss_header << "id";
#ifdef MFEM_USE_MPI
ss_header << ",rank";
#endif // MFEM_USE_MPI
std::array<char, 3> ax = {'X', 'Y', 'Z'};
for (int c = 0; c < coords.GetVDim(); c++)
{
ss_header << "," << ax[c];
}
for (int f = 0; f < field_idxs.Size(); f++)
{
ParticleVector &pv = *fields[field_idxs[f]];
for (int c = 0; c < pv.GetVDim(); c++)
{
ss_header << "," << field_names[field_idxs[f]] <<
(pv.GetVDim() > 1 ? "_" + std::to_string(c) : "");
}
}
for (int t = 0; t < tag_idxs.Size(); t++)
{
ss_header << "," << tag_names[tag_idxs[t]];
}
ss_header << "\n";
// Configure data
std::stringstream ss_data;
ss_data.precision(precision);
#ifdef MFEM_USE_MPI
int rank = GetRank(comm);
#endif // MFEM_USE_MPI
for (int i = 0; i < GetNParticles(); i++)
{
ss_data << ids[i];
#ifdef MFEM_USE_MPI
ss_data << "," << rank;
#endif // MFEM_USE_MPI
for (int c = 0; c < coords.GetVDim(); c++)
{
ss_data << "," << coords(i, c);
}
for (int f = 0; f < field_idxs.Size(); f++)
{
ParticleVector &pv = *fields[field_idxs[f]];
for (int c = 0; c < pv.GetVDim(); c++)
{
ss_data << "," << pv(i, c);
}
}
for (int t = 0; t < tag_idxs.Size(); t++)
{
ss_data << "," << (*tags[tag_idxs[t]])[i];
}
ss_data << "\n";
}
// Write
WriteToFile(fname, ss_header, ss_data);
}
ParticleSet::~ParticleSet()
{
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
if (gsl_comm)
{
if (!Mpi::IsFinalized()) // currently segfaults inside gslib otherwise
{
crystal_free(cr);
comm_free(gsl_comm);
delete gsl_comm;
delete cr;
}
}
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
}
} // namespace mfem
+685
View File
@@ -0,0 +1,685 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_PARTICLESET
#define MFEM_PARTICLESET
#include "../config/config.hpp"
#include "../linalg/linalg.hpp"
#include "gslib.hpp"
#include "kernel_dispatch.hpp"
namespace mfem
{
/** @brief Container for data associated with a single particle.
*
* @note This class mainly serves as a convenience interface to individual
* particle data from ParticleSet. We recommend seeing ParticleSet first.
*
* @details As described in ParticleSet documentation, each particle has a
* position (\ref coords), arbitrary number of scalar or vector \ref real_t
* data (\ref fields), and arbitrary number of integers (\ref tags)
* associated with it.
*
* \ref fields can thus hold data such as mass, momentum, and velocity, while
* \ref tags can hold integer data such as particle type, color, etc.
*
* Each particle also has a unique global ID, but that is managed by the
* ParticleSet class and not stored in this Particle class. Simiarly, the names
* of the fields and tags, typically useful for output purposes, are managed by
* the ParticleSet class.
*
*
* For clarity, we will use the particles below to illustrate the data layout
* for \ref coords, \ref fields, and \ref tags
*
* @anchor sample_particle_data
* @code
* Particle_0: coords = (x0, y0),
* fields = {'mass'=m0, 'vel' = (vx0, vy0)},
* tags = {'type'=t0, 'color'=color0}
* Particle_1: coords = (x1, y1),
* fields = {'mass'=m1, 'vel' = (vx1, vy1)},
* tags = {'type'=t1, 'color'=color1}
* Particle_2: coords = (x2, y2),
* fields = {'mass'=m2, 'vel' = (vx2, vy2)},
* tags = {'type'=t2, 'color'=color2}
* @endcode
*
*/
class Particle
{
protected:
/** @brief Spatial coordinates
*
* @details For the \ref sample_particle_data, \ref coords would hold
* (x_i, y_i) for each particle i.
*/
Vector coords;
/** @brief A std::vector of Vector where each Vector holds data for a given
* field (e.g., mass, momentum or velocity) associated with the particle.
*
* @details For the \ref sample_particle_data, \ref fields would be
* fields[0]=(m_i), fields[1]=(vx_i,vy_i) for each particle i.
*/
std::vector<Vector> fields;
/** @brief A std::vector of Array<int> where each Array<int> holds data
* for a given tag.
*
* @details For the \ref sample_particle_data, \ref tags would be
* tags[0]=(type_i), tags[1]=(color_i) for each particle i. \n
*
* @note An Array of length 1 is used for EACH tag, strictly for
* its owning/non-owning semantics (see Array<T>::MakeRef).
*/
std::vector<Array<int>> tags;
public:
/** @brief Construct a Particle instance.
* @param[in] dim Spatial dimension (size of #coords).
* @param[in] field_vdims Vector dimensions of particle fields.
* @param[in] num_tags Number of integer tags.
*/
Particle(int dim, const Array<int> &field_vdims, int num_tags);
// Force default constructors and destructor
Particle(const Particle&) = default;
Particle& operator=(const Particle&) = default;
Particle(Particle&&) = default;
Particle& operator=(Particle&&) = default;
~Particle() = default;
/// Get the spatial dimension of this particle.
int GetDim() const { return coords.Size(); }
/// Get the number of fields associated with this particle.
int GetNFields() const { return fields.size(); }
/// Get the vector dimension of field \p f .
int GetFieldVDim(int f) const { return fields[f].Size(); }
/// Get the number of tags associated with this particle.
int GetNTags() const { return tags.size(); }
/// Get reference to particle coordinates Vector.
Vector& Coords() { return coords; }
/// Get const reference to particle coordinates Vector.
const Vector& Coords() const { return coords; }
/// Get reference to field \p f , component \p c value.
real_t& FieldValue(int f, int c=0)
{
MFEM_ASSERT(f >= 0 && static_cast<std::size_t>(f) < fields.size(),
"Invalid field index");
MFEM_ASSERT(c >= 0 && c < fields[f].Size(),
"Invalid component index");
return fields[f][c];
}
/// Get const reference to field \p f , component \p c value.
const real_t& FieldValue(int f, int c=0) const
{
MFEM_ASSERT(f >= 0 && static_cast<std::size_t>(f) < fields.size(),
"invalid field index");
MFEM_ASSERT(c >= 0 && c < fields[f].Size(),
"invalid component index");
return fields[f][c];
}
/// Get reference to field \p f Vector.
Vector& Field(int f)
{
MFEM_ASSERT(f >= 0 && static_cast<std::size_t>(f) < fields.size(),
"invalid field index");
return fields[f];
}
/// Get const reference to field \p f Vector.
const Vector& Field(int f) const
{
MFEM_ASSERT(f >= 0 && static_cast<std::size_t>(f) < fields.size(),
"invalid field index");
return fields[f];
}
/// Get reference to tag \p t .
int& Tag(int t)
{
MFEM_ASSERT(t >= 0 && static_cast<std::size_t>(t) < tags.size(),
"invalid tag index");
return tags[t][0];
}
/// Get const reference to tag \p t .
const int& Tag(int t) const
{
MFEM_ASSERT(t >= 0 && static_cast<std::size_t>(t) < tags.size(),
"invalid tag index");
return tags[t][0];
}
/// Set tag \p t to reference external data.
void SetTagRef(int t, int *tag_data);
/// Set field \p f to reference external data.
void SetFieldRef(int f, real_t *field_data);
/// Particle equality operator.
bool operator==(const Particle &rhs) const;
/// Particle inequality operator.
bool operator!=(const Particle &rhs) const { return !operator==(rhs); }
/// Print all particle data to \p os.
void Print(std::ostream &os=mfem::out) const;
};
/** @brief ParticleSet initializes and manages data associated with particles.
*
* @details Particles are inherently initialized to have a position and an ID,
* and optionally can have any number of Vector (of arbitrary vdim) and scalar
* integer data in the form of @b fields and @b tags respectively. All particle
* data are internally stored in a Struct-of-Arrays fashion, as elaborated on
* below.
*
* @par Coordinates:
* All particle coordinates are stored in a ParticleVector with vector
* dimension equal to the spatial dimension, ordered either byNODES or byVDIM.
* The ParticleVector \ref coords contains the coordinates of all particles.
*
* @par IDs:
* Each particle is assigned a unique global ID of type IDType. In parallel,
* IDs are initialized starting with @b rank and striding by @b size. The IDs
* of all particles owned by this rank are stored in \ref ids.
*
* @par Fields:
* Fields represent scalar or vector \ref real_t data to be associated with
* each particles, such as mass, momentum, or moment. For a given field, all
* particle data is stored in a single ParticleVector with a given
* vector dimension (1 for scalar data) and Ordering::Type (byNODES or
* byVDIM). The unique_ptrs to all the ParticleVectors are stored in the
* std::vector \ref fields.
*
* @par Tags:
* Tags represent integers associated with each particle. For a given tag,
* all particle data are stored in a single Array<int>. The unique_ptrs to all
* the Array<int> is stored in the std::vector \ref tags.
*
* @par Names:
* Each field and tag can optionally be given a name (string) to be used when
* printing particle data in CSV format using PrintCSV(). The names of all
* fields and tags are stored in the std::vectors \ref field_names and
* \ref tag_names, respectively.
*
* @note We assume that all particles in a ParticleSet have the same number
* of fields and tags.
*
* Following the example in the Particle class, we will use the
* particles below to illustrate the data layout for \ref coords, \ref ids,
* \ref fields, \ref tags, \ref field_names, and \ref tag_names.
* In each case, the name of the field and tag is enclosed in '...' for
* clarity. Additionally, we assume for this example that the particle
* coordinates and the 'vel' field are ordered byVDIM in their respective
* ParticleVector.
* @anchor sample_particleset_data
* @code
* Particle_0: id = id0, coords = (x0, y0),
* fields = {'mass'=m0, 'vel' = (vx0, vy0)},
* tags = {'type'=t0, 'color'=c0}
* Particle_1: id = id1, coords = (x1, y1),
* fields = {'mass'=m1, 'vel' = (vx1, vy1)},
* tags = {'type'=t1, 'color'=c1}
* Particle_2: id = id2, coords = (x2, y2),
* fields = {'mass'=m2, 'vel' = (vx2, vy2)},
* tags = {'type'=t2, 'color'=c2}
* @endcode
*/
class ParticleSet
{
public:
using IDType = unsigned long long;
private:
/// Constructs an Array of size N filled with Ordering::Type o.
static Array<Ordering::Type> GetOrderingArray(Ordering::Type o, int N);
/// Returns default field name for field index i. "Field_{i}"
static std::string GetDefaultFieldName(int i);
/// Returns default tag name for tag index i. "Tag_{i}"
static std::string GetDefaultTagName(int i);
/// Constructs an Array of size N filled with nullptr.
static Array<const char*> GetEmptyNameArray(int N);
#ifdef MFEM_USE_MPI
static int GetRank(MPI_Comm comm_);
static int GetSize(MPI_Comm comm_);
#endif // MFEM_USE_MPI
protected:
/// Stride for IDs (used internally when new particles are added).
/** In parallel, this defaults to the number of MPI ranks. */
const int id_stride;
/// Current globally unique ID to be assigned to the next particle added.
/** In parallel, this starts locally as the rank and increments with
* id_stride, ensuring a global unique identifier whenever a particle is
* added.
*/
IDType id_counter;
/** @brief Global unique IDs of particles owned by this rank.
*
* @details For the \ref sample_particleset_data, \ref ids would be
* ids[0]=id0, ids[1]=id1, ids[2]=id2.
*/
Array<IDType> ids;
/** @brief Spatial coordinates of particles owned by this rank.
*
* @details For the \ref sample_particleset_data, \ref coords would be
* coords=(x0,y0,x1,y1,x2,y2) assuming coords ordering is byVDIM.
*/
ParticleVector coords;
/** @brief All particle fields for particles owned by this rank.
*
* @details For the \ref sample_particleset_data, \ref fields would be
* *fields[0]=(m0,m1,m2), *fields[1]=(vx0,vy0,vx1,vy1,vx2,vy2)
* assuming fields[1] ordering is byVDIM.
*/
std::vector<std::unique_ptr<ParticleVector>> fields;
/** @brief All particle tags for particles owned by this rank.
*
* @details For the \ref sample_particleset_data, \ref tags would be
* *tags[0]=(t0,t1,t2), *tags[1]=(c0,c1,c2).
*/
std::vector<std::unique_ptr<Array<int>>> tags;
/** @brief Field names, to be written when PrintCSV() is called.
*
* @details For the \ref sample_particleset_data, \ref field_names would be
* field_names[0]='mass', field_names[1]='vel'.
*/
std::vector<std::string> field_names;
/** @brief Tag names, to be written when PrintCSV() is called.
*
* @details For the \ref sample_particleset_data, \ref tag_names would be
* tag_names[0]='type', tag_names[1]='color'.
*/
std::vector<std::string> tag_names;
/** @brief Add particles with global identifiers \p new_ids and
* optionally get the local indices of new particles in \p new_indices .
*
* @details Note the data of new particles is uninitialized and must be
* set.
*/
void AddParticles(const Array<IDType> &new_ids,
Array<int> *new_indices=nullptr);
#ifdef MFEM_USE_MPI
MPI_Comm comm;
#endif // MFEM_USE_MPI
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
struct gslib::crystal *cr = nullptr; // gslib's internal data
struct gslib::comm *gsl_comm = nullptr; // gslib's internal data
/// \cond DO_NOT_DOCUMENT
template<std::size_t NBytes>
static void TransferParticlesImpl(ParticleSet &pset,
const Array<int> &send_idxs,
const Array<unsigned int> &send_ranks);
using TransferParticlesType = void (*)(ParticleSet &pset,
const Array<int> &send_idxs,
const Array<unsigned int> &send_ranks);
// Specialization parameter: NBytes
MFEM_REGISTER_KERNELS(TransferParticles, TransferParticlesType, (size_t));
friend TransferParticles;
struct Kernels
{
Kernels();
};
/// \endcond
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
/** @brief Update global ID of a particle.
*
* @details This method updates the global ID of the particle at given
* local index after Redistribute().
*
* @note This method must be used very carefully as it updates global
* ID of a particle.
*/
void UpdateID(int local_idx, IDType new_global_id)
{ ids[local_idx] = new_global_id; }
/** @brief Create a Particle object with the same spatial dimension,
* number of fields and field vdims, and number of tags as this ParticleSet.
*/
Particle CreateParticle() const;
/** @brief Write string in \p ss_header , followed by \p ss_data , to a
* single file; compatible in parallel.
*/
void WriteToFile(const char *fname, const std::stringstream &ss_header,
const std::stringstream &ss_data);
/** @brief Check if a particle could belong in this ParticleSet by
* comparing field and tag dimension.
*/
bool IsValidParticle(const Particle &p) const;
/** @brief Hidden main constructor of ParticleSet
*
* @param[in] id_stride_ ID stride.
* @param[in] id_counter_ Starting ID counter.
* @param[in] num_particles Number of particles to initialize.
* @param[in] dim Particle spatial dimension.
* @param[in] coords_ordering Ordering of coordinates
* @param[in] field_vdims Array of field vector dimensions
* @param[in] field_orderings Array of field ordering types.
* @param[in] field_names_ Array of field names.
* @param[in] num_tags Number of tags to register.
* @param[in] tag_names_ Array of tag names.
*/
ParticleSet(int id_stride_, IDType id_counter_, int num_particles, int dim,
Ordering::Type coords_ordering, const Array<int> &field_vdims,
const Array<Ordering::Type> &field_orderings,
const Array<const char*> &field_names_, int num_tags,
const Array<const char*> &tag_names_);
public:
/** @brief Construct a serial ParticleSet.
*
* @param[in] num_particles Number of particles to initialize.
* @param[in] dim Particle spatial dimension.
* @param[in] coords_ordering Ordering of coordinates.
*/
ParticleSet(int num_particles, int dim,
Ordering::Type coords_ordering=Ordering::byVDIM);
/** @brief Construct a serial ParticleSet with specified fields and tags at
* construction.
*
* @param[in] num_particles Number of particles to initialize.
* @param[in] dim Particle spatial dimension.
* @param[in] field_vdims Array of field vector dimensions.
* @param[in] num_tags Number of tags to register.
* @param[in] all_ordering (Optional) Ordering of coordinates and
* field ParticleVector.
*/
ParticleSet(int num_particles, int dim, const Array<int> &field_vdims,
int num_tags, Ordering::Type all_ordering=Ordering::byVDIM);
/** @brief Construct a serial ParticleSet with specified fields and tags at
* construction, with names.
*
* @param[in] num_particles Number of particles to initialize.
* @param[in] dim Particle spatial dimension.
* @param[in] field_vdims Array of field vector dimensions.
* @param[in] field_names_ Array of field names.
* @param[in] num_tags Number of tags to register.
* @param[in] tag_names_ Array of tag names.
* @param[in] all_ordering (Optional) Ordering of coordinates and
* field ParticleVector.
*/
ParticleSet(int num_particles, int dim, const Array<int> &field_vdims,
const Array<const char*> &field_names_, int num_tags,
const Array<const char*> &tag_names_,
Ordering::Type all_ordering=Ordering::byVDIM);
/** @brief Comprehensive serial constructor of ParticleSet.
*
* @param[in] num_particles Number of particles to initialize.
* @param[in] dim Particle spatial dimension.
* @param[in] coords_ordering Ordering of coordinates.
* @param[in] field_vdims Array of field vector dimensions.
* @param[in] field_orderings Array of field ordering types.
* @param[in] field_names_ Array of field names.
* @param[in] num_tags Number of tags to register.
* @param[in] tag_names_ Array of tag names.
*/
ParticleSet(int num_particles, int dim, Ordering::Type coords_ordering,
const Array<int> &field_vdims,
const Array<Ordering::Type> &field_orderings,
const Array<const char*> &field_names_, int num_tags,
const Array<const char*> &tag_names_);
#ifdef MFEM_USE_MPI
/** @brief Construct a parallel ParticleSet.
*
* @param[in] comm_ MPI communicator.
* @param[in] rank_num_particles Number of particles to initialize.
* @param[in] dim Particle spatial dimension.
* @param[in] coords_ordering (Optional) Ordering of coordinates.
*/
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
Ordering::Type coords_ordering=Ordering::byVDIM);
/** @brief Construct a parallel ParticleSet with specified fields and tags
* at construction.
*
* @param[in] comm_ MPI communicator.
* @param[in] rank_num_particles # of particles to initialize on this rank.
* @param[in] dim Particle spatial dimension.
* @param[in] field_vdims Array of field vector dimensions.
* @param[in] num_tags Number of tags to register.
* @param[in] all_ordering (Optional) Ordering of coordinates and
* field ParticleVector.
*/
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
const Array<int> &field_vdims, int num_tags,
Ordering::Type all_ordering=Ordering::byVDIM);
/** @brief Construct a parallel ParticleSet with specified fields and tags
* at construction, with names (for PrintCSV()).
*
* @param[in] comm_ MPI communicator.
* @param[in] rank_num_particles # of particles to initialize on this rank.
* @param[in] dim Particle spatial dimension.
* @param[in] field_vdims Array of field vector dimension.
* @param[in] field_names_ Array of field names.
* @param[in] num_tags Number of tags to register.
* @param[in] tag_names_ Array of tag names.
* @param[in] all_ordering (Optional) Ordering of coordinates and
* field ParticleVector.
*/
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
const Array<int> &field_vdims,
const Array<const char*> &field_names_,
int num_tags, const Array<const char*> &tag_names_,
Ordering::Type all_ordering=Ordering::byVDIM);
/** @brief Comprehensive parallel constructor of ParticleSet.
*
* @param[in] comm_ MPI communicator.
* @param[in] rank_num_particles # of particles to initialize on this rank.
* @param[in] dim Particle spatial dimension.
* @param[in] coords_ordering Ordering of coordinates.
* @param[in] field_vdims Array of field vector dimensions.
* @param[in] field_orderings Array of field ordering types.
* @param[in] field_names_ Array of field names.
* @param[in] num_tags Number of tags to register.
* @param[in] tag_names_ Array of tag names.
*/
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
Ordering::Type coords_ordering, const Array<int> &field_vdims,
const Array<Ordering::Type> &field_orderings,
const Array<const char*> &field_names_, int num_tags,
const Array<const char*> &tag_names_);
/// Get the MPI communicator for this ParticleSet.
MPI_Comm GetComm() const { return comm; };
#endif // MFEM_USE_MPI
/// Get the global number of active particles across all ranks.
IDType GetGlobalNParticles() const;
/// Get the spatial dimension.
int GetDim() const { return coords.GetVDim(); }
/// Get the global IDs of the active particles owned by this ParticleSet.
const Array<IDType>& GetIDs() const { return ids; }
/** @brief Add a field to the ParticleSet.
*
* @param[in] vdim Vector dimension of the field.
* @param[in] field_ordering (Optional) Ordering::Type of the field.
* @param[in] field_name (Optional) Name of the field.
*
* @return Index of the newly-added field.
*/
int AddField(int vdim, Ordering::Type field_ordering=Ordering::byVDIM,
const char* field_name=nullptr);
/** @brief Add a field to the ParticleSet.
*
* @details Same as AddField() but with different parameter order
* for convenience
*/
int AddNamedField(int vdim, const char* field_name,
Ordering::Type field_ordering=Ordering::byVDIM)
{
return AddField(vdim, field_ordering, field_name);
}
/** @brief Add a tag to the ParticleSet.
*
* @param[in] tag_name (Optional) Name of the tag.
*
* @return Index of the newly-added tag.
*/
int AddTag(const char* tag_name=nullptr);
/// Reserve memory for \p res particles.
/** Can help to avoid re-allocation for adding + removing particles. */
void Reserve(int res);
/// Get the number of active particles currently held by this ParticleSet.
int GetNParticles() const { return ids.Size(); }
/// Get the number of fields registered to particles.
int GetNFields() const { return fields.size(); }
/// Get an Array<int> of the field vector-dimensions registered to particles.
const Array<int> GetFieldVDims() const;
/// Get Field vector-dimension
int FieldVDim(int f) const { return fields[f]->GetVDim(); }
/// Get the number of tags registered to particles.
int GetNTags() const { return tags.size(); }
/// Add a particle using Particle .
void AddParticle(const Particle &p);
/** @brief Add \p num_particles particles, and optionally get the local
* indices of new particles in \p new_indices .
*
* @details The data of new particles is uninitialized and must be
* set.
*/
void AddParticles(int num_particles, Array<int> *new_indices=nullptr);
/// Remove particle data specified by \p list of particle indices.
void RemoveParticles(const Array<int> &list);
/// Get a reference to the coordinates ParticleVector.
ParticleVector& Coords() { return coords; }
/// Get a const reference to the coordinates ParticleVector.
const ParticleVector& Coords() const { return coords; }
/// Get a reference to field \p f 's ParticleVector.
ParticleVector& Field(int f) { return *fields[f]; }
/// Get a const reference to field \p f 's ParticleVector.
const ParticleVector& Field(int f) const { return *fields[f]; }
/// Get a reference to tag \p t 's Array<int>.
Array<int>& Tag(int t) { return *tags[t]; }
/// Get a const reference to tag \p t 's Array<int>.
const Array<int>& Tag(int t) const { return *tags[t]; }
/** @brief Get new Particle object with copy of data associated with
particle \p i . */
Particle GetParticle(int i) const;
/** @brief Get Particle object whose members reference the actual data
* associated with particle \p i in this ParticleSet.
*
* @see IsParticleRefValid for when this method can be used.
*
* @warning If particles are added, removed, or redistributed after
* invoking this, the returned Particle member references may be
* invalidated.
*/
Particle GetParticleRef(int i);
/** @brief Determine if GetParticleRef is valid.
*
* If coordinates and all fields are ordered byVDIM, then returns true.
* Otherwise, false.
*/
bool IsParticleRefValid() const;
/// Set data for particle at index \p i with data from provided particle \p p
void SetParticle(int i, const Particle &p);
/** @brief Print all particle data to a comma-delimited CSV file.
*
* The first row contains the header. We include the particle ID,
* owning rank (in parallel), coordinates, followed by all fields and
* tags.
*
* The output can be visualized in Paraview by loading the csv files, and
* applying the "Table To Points" filter.
*/
void PrintCSV(const char *fname, int precision=16);
/** @brief Print only particle field and tags given by \p field_idxs and
\p tag_idxs respectively to a CSV file. */
void PrintCSV(const char *fname, const Array<int> &field_idxs,
const Array<int> &tag_idxs, int precision=16);
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
/** @brief Redistribute particle data to \p rank_list
@param[in] rank_list Array of size GetNParticles() denoting ultimate
destination of particle data. Index = this rank
means no data is moved.
*/
void Redistribute(const Array<unsigned int> &rank_list);
#endif // MFEM_USE_MPI && MFEM_USE_GSLIB
/// Destructor
~ParticleSet();
ParticleSet(const ParticleSet&) = delete;
ParticleSet& operator=(const ParticleSet&) = delete;
};
} // namespace mfem
#endif // MFEM_PARTICLESET
+13 -8
View File
@@ -5220,10 +5220,10 @@ void ConformingProlongationOperator::Mult(const Vector &x, Vector &y) const
for (int i = 0; i < m; i++)
{
const int end = external_ldofs[i];
std::copy(xdata+j-i, xdata+end-i, ydata+j);
if (end > j) { std::copy(xdata+j-i, xdata+end-i, ydata+j); }
j = end+1;
}
std::copy(xdata+j-m, xdata+Width(), ydata+j);
if (Width() > (j-m)) { std::copy(xdata+j-m, xdata+Width(), ydata+j); }
const int out_layout = 0; // 0 - output is ldofs array
if (!local)
@@ -5251,10 +5251,10 @@ void ConformingProlongationOperator::MultTranspose(
for (int i = 0; i < m; i++)
{
const int end = external_ldofs[i];
std::copy(xdata+j, xdata+end, ydata+j-i);
if (end > j) { std::copy(xdata+j, xdata+end, ydata+j-i); }
j = end+1;
}
std::copy(xdata+j, xdata+Height(), ydata+j-m);
if (Height() > j) { std::copy(xdata+j, xdata+Height(), ydata+j-m); }
const int out_layout = 2; // 2 - output is an array on all ltdofs
if (!local)
@@ -5271,7 +5271,8 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
MFEM_ASSERT(R->Finalized(), "");
const int tdofs = R->Height();
MFEM_ASSERT(tdofs == R->HostReadI()[tdofs], "");
ltdof_ldof = Array<int>(const_cast<int*>(R->HostReadJ()), tdofs);
ltdof_ldof.SetSize(tdofs);
ltdof_ldof.CopyFrom(R->HostReadJ());
{
Table nbr_ltdof;
gc.GetNeighborLTDofTable(nbr_ltdof);
@@ -5294,9 +5295,13 @@ DeviceConformingProlongationOperator::DeviceConformingProlongationOperator(
}
Table unique_shr;
Transpose(shared_ltdof, unique_shr, unique_ltdof.Size());
unq_ltdof = Array<int>(unique_ltdof, unique_ltdof.Size());
unq_shr_i = Array<int>(unique_shr.GetI(), unique_shr.Size()+1);
unq_shr_j = Array<int>(unique_shr.GetJ(), unique_shr.Size_of_connections());
unq_ltdof = unique_ltdof;
// Steal I and J arrays from the unique_shr table.
unq_shr_i.GetMemory() = unique_shr.GetIMemory();
unq_shr_i.SetSize(unique_shr.Size()+1);
unq_shr_j.GetMemory() = unique_shr.GetJMemory();
unq_shr_j.SetSize(unique_shr.Size_of_connections());
unique_shr.LoseData();
}
nbr_ltdof.GetJMemory().Delete();
nbr_ltdof.LoseData();
+1 -1
View File
@@ -1407,7 +1407,7 @@ real_t L2ZZErrorEstimator(BilinearFormIntegrator &flux_integrator,
}
PLBound ParGridFunction::GetBounds(Vector &lower, Vector &upper,
const int ref_factor, const int vdim)
const int ref_factor, const int vdim) const
{
PLBound plb = GridFunction::GetBounds(lower, upper, ref_factor, vdim);
int siz = vdim > 0 ? 1 : fes->GetVDim();
+1 -1
View File
@@ -587,7 +587,7 @@ public:
/// PLBound object used to compute the bounds. Note: if vdim < 1, we compute
/// the bounds for each vector dimension.
PLBound GetBounds(Vector &lower, Vector &upper,
const int ref_factor=1, const int vdim=-1) override;
const int ref_factor=1, const int vdim=-1) 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
+40 -7
View File
@@ -56,6 +56,20 @@ void QuadratureFunction::Save(std::ostream &os) const
os.flush();
}
void QuadratureFunction::ProjectGridFunctionFallback(const GridFunction &gf)
{
if (gf.VectorDim() == 1)
{
GridFunctionCoefficient coeff(&gf);
coeff.Coefficient::Project(*this);
}
else
{
VectorGridFunctionCoefficient coeff(&gf);
coeff.VectorCoefficient::Project(*this);
}
}
void QuadratureFunction::ProjectGridFunction(const GridFunction &gf)
{
SetVDim(gf.VectorDim());
@@ -68,14 +82,23 @@ void QuadratureFunction::ProjectGridFunction(const GridFunction &gf)
ElementDofOrdering::LEXICOGRAPHIC :
ElementDofOrdering::NATIVE;
// Use quadrature interpolator to go from E-vector to Q-vector
const QuadratureInterpolator *qi =
gf_fes.GetQuadratureInterpolator(*qs_elem);
// If quadrature interpolator doesn't support this space, then fallback
// on slower (non-device) version, and return early.
if (!qi)
{
ProjectGridFunctionFallback(gf);
return;
}
// Use element restriction to go from L-vector to E-vector
const Operator *R = gf_fes.GetElementRestriction(ordering);
Vector e_vec(R->Height());
R->Mult(gf, e_vec);
// Use quadrature interpolator to go from E-vector to Q-vector
const QuadratureInterpolator *qi =
gf_fes.GetQuadratureInterpolator(*qs_elem);
qi->SetOutputLayout(QVectorLayout::byVDIM);
qi->DisableTensorProducts(!use_tensor_products);
qi->PhysValues(e_vec, *this);
@@ -83,12 +106,25 @@ void QuadratureFunction::ProjectGridFunction(const GridFunction &gf)
else if (auto *qs_face = dynamic_cast<FaceQuadratureSpace*>(qspace))
{
const FiniteElementSpace &gf_fes = *gf.FESpace();
const FaceType face_type = qs_face->GetFaceType();
const bool use_tensor_products = UsesTensorBasis(gf_fes);
const ElementDofOrdering ordering = use_tensor_products ?
ElementDofOrdering::LEXICOGRAPHIC :
ElementDofOrdering::NATIVE;
const FaceType face_type = qs_face->GetFaceType();
// Use quadrature interpolator to go from E-vector to Q-vector
const FaceQuadratureInterpolator *qi =
gf_fes.GetFaceQuadratureInterpolator(qspace->GetIntRule(0), face_type);
// If quadrature interpolator doesn't support this space, then fallback
// on slower (non-device) version, and return early. Also, currently,
// ElementDofOrdering::NATIVE in FaceRestriction, so fall back in that
// case too.
if (qi == nullptr || ordering == ElementDofOrdering::NATIVE)
{
ProjectGridFunctionFallback(gf);
return;
}
// Use element restriction to go from L-vector to E-vector
const Operator *R = gf_fes.GetFaceRestriction(
@@ -96,9 +132,6 @@ void QuadratureFunction::ProjectGridFunction(const GridFunction &gf)
Vector e_vec(R->Height());
R->Mult(gf, e_vec);
// Use quadrature interpolator to go from E-vector to Q-vector
const FaceQuadratureInterpolator *qi =
gf_fes.GetFaceQuadratureInterpolator(qspace->GetIntRule(0), face_type);
qi->SetOutputLayout(QVectorLayout::byVDIM);
qi->DisableTensorProducts(!use_tensor_products);
qi->Values(e_vec, *this);
+12
View File
@@ -27,6 +27,8 @@ protected:
bool own_qspace; ///< Does this own the associated QuadratureSpaceBase?
int vdim; ///< Vector dimension.
void ProjectGridFunctionFallback(const GridFunction &gf);
public:
/// Default constructor, results in an empty vector.
QuadratureFunction() : qspace(nullptr), own_qspace(false), vdim(0)
@@ -41,6 +43,12 @@ public:
qspace(&qspace_), own_qspace(false), vdim(vdim_)
{ UseDevice(true); }
/// Same as above but specify the device memory type
QuadratureFunction(QuadratureSpaceBase &qspace_, MemoryType mt, int vdim_ = 1)
: Vector(vdim_*qspace_.GetSize(), mt),
qspace(&qspace_), own_qspace(false), vdim(vdim_)
{ UseDevice(true); }
/// Create a QuadratureFunction based on the given QuadratureSpaceBase.
/** The QuadratureFunction does not assume ownership of the
QuadratureSpaceBase.
@@ -48,6 +56,10 @@ public:
QuadratureFunction(QuadratureSpaceBase *qspace_, int vdim_ = 1)
: QuadratureFunction(*qspace_, vdim_) { }
/// Same as above but specify the device memory type
QuadratureFunction(QuadratureSpaceBase *qspace_, MemoryType mt, int vdim_ = 1)
: QuadratureFunction(*qspace_, mt, vdim_) { }
/** @brief Create a QuadratureFunction based on the given QuadratureSpaceBase,
using the external (host) data, @a qf_data. */
/** The QuadratureFunction does not assume ownership of the
+12 -6
View File
@@ -69,9 +69,7 @@ QuadratureInterpolator::QuadratureInterpolator(const FiniteElementSpace &fes,
d_buffer.UseDevice(true);
if (fespace->GetNE() == 0) { return; }
const FiniteElement *fe = fespace->GetTypicalFE();
MFEM_VERIFY(fe->GetMapType() == FiniteElement::MapType::VALUE ||
fe->GetMapType() == FiniteElement::MapType::H_DIV,
MFEM_VERIFY(SupportsFESpace(fes),
"Only elements with MapType VALUE and H_DIV are supported!");
}
@@ -86,12 +84,20 @@ QuadratureInterpolator::QuadratureInterpolator(const FiniteElementSpace &fes,
{
d_buffer.UseDevice(true);
if (fespace->GetNE() == 0) { return; }
const FiniteElement *fe = fespace->GetTypicalFE();
MFEM_VERIFY(fe->GetMapType() == FiniteElement::MapType::VALUE ||
fe->GetMapType() == FiniteElement::MapType::H_DIV,
MFEM_VERIFY(SupportsFESpace(fes),
"Only elements with MapType VALUE and H_DIV are supported!");
}
bool QuadratureInterpolator::SupportsFESpace(const FiniteElementSpace &fespace)
{
const FiniteElement *fe = fespace.GetTypicalFE();
const Mesh &mesh = *fespace.GetMesh();
return (fe->GetMapType() == FiniteElement::MapType::VALUE ||
fe->GetMapType() == FiniteElement::MapType::H_DIV)
&& (!fespace.IsVariableOrder())
&& (!mesh.IsMixedMesh());
}
namespace internal
{
+3
View File
@@ -155,6 +155,9 @@ public:
void MultTranspose(unsigned eval_flags, const Vector &q_val,
const Vector &q_der, Vector &e_vec) const;
/// @brief Returns true if the given finite element space is supported by
/// QuadratureInterpolator.
static bool SupportsFESpace(const FiniteElementSpace &fespace);
using TensorEvalKernelType = void(*)(const int, const real_t *, const real_t *,
real_t *, const int, const int, const int);
+11 -11
View File
@@ -77,17 +77,17 @@ FaceQuadratureInterpolator::FaceQuadratureInterpolator(
if (fespace->GetNE() == 0) { return; }
GetSigns(*fespace, type, signs);
const FiniteElement *fe = fespace->GetTypicalFE();
const ScalarFiniteElement *sfe =
dynamic_cast<const ScalarFiniteElement*>(fe);
const TensorBasisElement *tfe =
dynamic_cast<const TensorBasisElement*>(fe);
MFEM_VERIFY(sfe != NULL, "Only scalar finite elements are supported");
MFEM_VERIFY(tfe != NULL &&
(tfe->GetBasisType()==BasisType::GaussLobatto ||
tfe->GetBasisType()==BasisType::Positive),
"Only Gauss-Lobatto and Bernstein basis are supported in "
"FaceQuadratureInterpolator.");
MFEM_VERIFY(SupportsFESpace(fes), "Unsupported finite element space");
}
bool FaceQuadratureInterpolator::SupportsFESpace(const FiniteElementSpace &fes)
{
const FiniteElement *fe = fes.GetTypicalFE();
const auto *sfe = dynamic_cast<const ScalarFiniteElement*>(fe);
const auto *tfe = dynamic_cast<const TensorBasisElement*>(fe);
return sfe != nullptr && tfe != nullptr && (
tfe->GetBasisType() == BasisType::GaussLobatto ||
tfe->GetBasisType() == BasisType::Positive);
}
template<const int T_VDIM, const int T_ND1D, const int T_NQ1D>
+4
View File
@@ -64,6 +64,10 @@ public:
FaceQuadratureInterpolator(const FiniteElementSpace &fes,
const IntegrationRule &ir, FaceType type);
/// @brief Returns true if the given finite element space is supported by
/// FaceQuadratureInterpolator.
static bool SupportsFESpace(const FiniteElementSpace &fes);
/** @brief Disable the use of tensor product evaluations, for tensor-product
elements, e.g. quads and hexes. */
/** Currently, tensor product evaluations are not implemented and this method
+9
View File
@@ -5368,6 +5368,15 @@ void TMOP_Integrator::ParEnableNormalization(const ParGridFunction &x)
}
#endif
void TMOP_Integrator::GetNormalizationFactors(real_t &m_normal,
real_t &l_normal,
real_t &s_normal)
{
m_normal = this->metric_normal;
l_normal = this->lim_normal;
s_normal = this->surf_fit_normal;
}
void TMOP_Integrator::ComputeNormalizationEnergies(const GridFunction &x,
real_t &metric_energy,
real_t &lim_energy)
+43
View File
@@ -452,6 +452,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 9; }
};
/// 2D non-barrier Shape+Size+Orientation (VOS) metric (polyconvex).
@@ -502,6 +504,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 22; }
};
/// 2D barrier shape metric (polyconvex).
@@ -522,6 +526,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 50; }
};
/// 2D non-barrier size (V) metric (not polyconvex).
@@ -593,6 +599,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 58; }
};
/// 2D non-barrier Shape+Size (VS) metric.
@@ -675,6 +683,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 85; }
};
/// 2D compound barrier Shape+Size (VS) metric (balanced).
@@ -732,6 +742,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 98; }
};
/// 2D untangling metric.
@@ -751,6 +763,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 211; }
};
/// Shifted barrier form of metric 56 (area, ideal barrier metric), 2D
@@ -771,6 +785,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 252; }
};
/// 3D barrier Shape (S) metric, well-posed (polyconvex & invex).
@@ -790,6 +806,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 301; }
};
/// 3D barrier Shape (S) metric, well-posed (polyconvex & invex).
@@ -872,6 +890,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 311; }
};
/// 3D Shape (S) metric, untangling version of 303.
@@ -930,6 +950,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 316; }
};
/// 3D Size (V) metric.
@@ -1074,6 +1096,7 @@ public:
AddQualityMetric(sz_metric, gamma);
}
int Id() const override { return 333; }
virtual ~TMOP_Metric_333() { delete sh_metric; delete sz_metric; }
};
@@ -1136,6 +1159,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 342; }
};
/// 3D barrier Shape+Size (VS) metric, well-posed (polyconvex).
@@ -1177,6 +1202,8 @@ public:
void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const real_t weight, DenseMatrix &A) const override;
int Id() const override { return 352; }
};
/// 3D non-barrier Shape (S) metric.
@@ -1584,6 +1611,11 @@ protected:
const TargetType target_type;
bool uses_phys_coords; // see UsesPhysicalCoordinates()
/// Cached copy of GeomToPerfGeomJac used on device.
mutable DenseMatrix current_W;
/// Geometry type of current W matrix (used for cache invalidation).
mutable Geometry::Type current_W_type = Geometry::INVALID;
#ifdef MFEM_USE_MPI
MPI_Comm comm;
#endif
@@ -1963,6 +1995,12 @@ class TMOP_Integrator : public NonlinearFormIntegrator
protected:
friend class TMOPNewtonSolver;
friend class TMOPComboIntegrator;
friend class TMOPEnergyPA2D;
friend class TMOPEnergyPA3D;
friend class TMOPAssembleGradPA2D;
friend class TMOPAssembleGradPA3D;
friend class TMOPAddMultPA2D;
friend class TMOPAddMultPA3D;
// Initial positions of the mesh nodes. Not owned. The pointer is set at the
// start of the solve by TMOPNewtonSolver::Mult(), and unset at the end.
@@ -2483,6 +2521,11 @@ public:
void ParEnableNormalization(const ParGridFunction &x);
#endif
/** @brief Get the normalization factors of the metric */
void GetNormalizationFactors(real_t &metric_normal,
real_t &lim_normal,
real_t &surf_fit_normal);
/** @brief Enables FD-based approximation and computes dx. */
void EnableFiniteDifferences(const GridFunction &x);
#ifdef MFEM_USE_MPI
+180
View File
@@ -0,0 +1,180 @@
// 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 "../pa.hpp"
#include "../../tmop.hpp"
#include "../../../general/forall.hpp"
#include "../../../linalg/kernels.hpp"
namespace mfem
{
/* // Original i-j assembly (old invariants code).
for (int e = 0; e < NE; e++)
{
for (int q = 0; q < nqp; q++)
{
el.CalcDShape(ip, DSh);
Mult(DSh, Jrt, DS);
for (int i = 0; i < dof; i++)
{
for (int j = 0; j < dof; j++)
{
for (int r = 0; r < dim; r++)
{
for (int c = 0; c < dim; c++)
{
for (int rr = 0; rr < dim; rr++)
{
for (int cc = 0; cc < dim; cc++)
{
const real_t H = h(r, c, rr, cc);
A(e, i + r*dof, j + rr*dof) +=
weight_q * DS(i, c) * DS(j, cc) * H;
}
}
}
}
}
}
}
}*/
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
void TMOP_AssembleDiagPA_2D(const int NE,
const ConstDeviceMatrix &B,
const ConstDeviceMatrix &G,
const DeviceTensor<5, const real_t> &J,
const DeviceTensor<7, const real_t> &H,
DeviceTensor<4> &D,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
// Takes into account Jtr by replacing H with Href at all quad points.
MFEM_SHARED real_t Href_data[2 * 2 * 2 * MQ1 * MQ1];
DeviceTensor<5, real_t> Href(Href_data, 2, 2, 2, MQ1, MQ1);
for (int v = 0; v < 2; v++)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
const real_t *Jtr = &J(0, 0, qx, qy, e);
real_t Jrt_data[4];
ConstDeviceMatrix Jrt(Jrt_data, 2, 2);
kernels::CalcInverse<2>(Jtr, Jrt_data);
for (int m = 0; m < 2; m++)
{
for (int n = 0; n < 2; n++)
{
// Hr_{v,m,n,q} = \sum_{s,t=1}^d
// Jrt_{m,s,q} H_{v,s,v,t,q} Jrt_{n,t,q}
Href(v, m, n, qx, qy) = 0.0;
for (int s = 0; s < 2; s++)
{
for (int t = 0; t < 2; t++)
{
Href(v, m, n, qx, qy) +=
Jrt(m, s) * H(v, s, v, t, qx, qy, e) * Jrt(n, t);
}
}
}
}
}
}
}
MFEM_SHARED real_t qd[2 * 2 * MQ1 * MD1];
DeviceTensor<4, real_t> QD(qd, 2, 2, MQ1, MD1);
for (int v = 0; v < 2; v++)
{
// Contract in y.
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
for (int m = 0; m < 2; m++)
{
for (int n = 0; n < 2; n++) { QD(m, n, qx, dy) = 0.0; }
}
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t By = B(qy, dy);
const real_t Gy = G(qy, dy);
for (int m = 0; m < 2; m++)
{
for (int n = 0; n < 2; n++)
{
const real_t L = (m == 1 ? Gy : By);
const real_t R = (n == 1 ? Gy : By);
QD(m, n, qx, dy) += L * Href(v, m, n, qx, qy) * R;
}
}
}
}
}
MFEM_SYNC_THREAD;
// Contract in x.
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t d = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Bx = B(qx, dx);
const real_t Gx = G(qx, dx);
for (int m = 0; m < 2; m++)
{
for (int n = 0; n < 2; n++)
{
const real_t L = (m == 0 ? Gx : Bx);
const real_t R = (n == 0 ? Gx : Bx);
d += L * QD(m, n, qx, dy) * R;
}
}
}
D(dx, dy, v, e) += d;
}
}
MFEM_SYNC_THREAD;
}
});
}
MFEM_TMOP_MDQ_REGISTER(TMOPAssembleDiag2D, TMOP_AssembleDiagPA_2D);
MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiag2D);
void TMOP_Integrator::AssembleDiagonalPA_2D(Vector &diagonal) const
{
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = Reshape(PA.maps->B.Read(), q, d);
const auto G = Reshape(PA.maps->G.Read(), q, d);
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
const auto H = Reshape(PA.H.Read(), 2, 2, 2, 2, q, q, NE);
auto D = Reshape(diagonal.ReadWrite(), d, d, 2, NE);
TMOPAssembleDiag2D::Run(d, q, NE, B, G, J, H, D, d, q);
}
} // namespace mfem
+83
View File
@@ -0,0 +1,83 @@
// 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 "../pa.hpp"
#include "../../tmop.hpp"
#include "../../../general/forall.hpp"
namespace mfem
{
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
void TMOP_AssembleDiagPA_C0_2D(const int NE,
const ConstDeviceMatrix &B,
const DeviceTensor<5, const real_t> &H0,
DeviceTensor<4> &D,
const int d1d, const int q1d)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
MFEM_SHARED real_t qd[MQ1 * MD1];
DeviceTensor<2, real_t> QD(qd, MQ1, MD1);
for (int v = 0; v < 2; v++)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
QD(qx, dy) = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t bb = B(qy, dy) * B(qy, dy);
QD(qx, dy) += bb * H0(v, v, qx, qy, e);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t d = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t bb = B(qx, dx) * B(qx, dx);
d += bb * QD(qx, dy);
}
D(dx, dy, v, e) += d;
}
}
MFEM_SYNC_THREAD;
}
});
}
MFEM_TMOP_MDQ_REGISTER(TMOPAssembleDiagCoef2D, TMOP_AssembleDiagPA_C0_2D);
MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiagCoef2D);
void TMOP_Integrator::AssembleDiagonalPA_C0_2D(Vector &diagonal) const
{
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = Reshape(PA.maps->B.Read(), q, d);
const auto H0 = Reshape(PA.H0.Read(), 2, 2, q, q, NE);
auto D = Reshape(diagonal.ReadWrite(), d, d, 2, NE);
TMOPAssembleDiagCoef2D::Run(d, q, NE, B, H0, D, d, q);
}
} // namespace mfem
+229
View File
@@ -0,0 +1,229 @@
// 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 "../pa.hpp"
#include "../../tmop.hpp"
#include "../../kernels.hpp"
#include "../../../general/forall.hpp"
#include "../../../linalg/kernels.hpp"
namespace mfem
{
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
void TMOP_AssembleDiagPA_3D(const int NE,
const ConstDeviceMatrix &B,
const ConstDeviceMatrix &G,
const DeviceTensor<6, const real_t> &J,
const DeviceTensor<8, const real_t> &H,
DeviceTensor<5> &D,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
MFEM_SHARED real_t smem[3][3][MQ1][MQ1];
kernels::internal::vd_regs3d_t<3, 3, MQ1> rH, r0, r1;
for (int v = 0; v < 3; ++v)
{
// Takes into account Jtr by replacing H with Href at all quad points.
for (int qz = 0; qz < Q1D; ++qz)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const real_t *Jtr = &J(0, 0, qx, qy, qz, e);
real_t Jrt_data[9];
ConstDeviceMatrix Jrt(Jrt_data, 3, 3);
kernels::CalcInverse<3>(Jtr, Jrt_data);
real_t h[3][3];
for (int s = 0; s < 3; s++)
{
for (int t = 0; t < 3; t++)
{
h[s][t] = H(v, s, v, t, qx, qy, qz, e);
}
}
for (int m = 0; m < 3; m++)
{
for (int n = 0; n < 3; n++)
{
// Hr_{v,m,n,q} = \sum_{s,t=1}^d
// Jrt_{m,s,q} H_{v,s,v,t,q} Jrt_{n,t,q}
rH(m, n, qz, qy, qx) = 0.0;
for (int s = 0; s < 3; s++)
{
for (int t = 0; t < 3; t++)
{
rH(m, n, qz, qy, qx) += Jrt(m, s) * h[s][t] * Jrt(n, t);
}
}
}
}
}
}
MFEM_SYNC_THREAD;
}
// Contract in z.
for (int dz = 0; dz < D1D; ++dz)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
for (int m = 0; m < 3; m++)
{
for (int n = 0; n < 3; n++)
{
r0(m, n, dz, qy, qx) = 0.0;
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
const real_t Bz = B(qz, dz), Gz = G(qz, dz);
for (int m = 0; m < 3; m++)
{
for (int n = 0; n < 3; n++)
{
const real_t L = (m == 2 ? Gz : Bz);
const real_t R = (n == 2 ? Gz : Bz);
r0(m, n, dz, qy, qx) += L * rH(m, n, qz, qy, qx) * R;
}
}
}
}
}
MFEM_SYNC_THREAD;
}
// Contract in y.
for (int dz = 0; dz < D1D; ++dz)
{
for (int m = 0; m < 3; m++)
{
for (int n = 0; n < 3; n++)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
smem[m][n][qy][qx] = r0(m, n, dz, qy, qx);
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
for (int m = 0; m < 3; m++)
{
for (int n = 0; n < 3; n++)
{
r1(m, n, dz, dy, qx) = 0.0;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t By = B(qy, dy);
const real_t Gy = G(qy, dy);
for (int m = 0; m < 3; m++)
{
for (int n = 0; n < 3; n++)
{
const real_t L = (m == 1 ? Gy : By);
const real_t R = (n == 1 ? Gy : By);
r1(m, n, dz, dy, qx) += L * smem[m][n][qy][qx] * R;
}
}
}
}
}
MFEM_SYNC_THREAD;
}
// Contract in x.
for (int dz = 0; dz < D1D; ++dz)
{
for (int m = 0; m < 3; m++)
{
for (int n = 0; n < 3; n++)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
smem[m][n][dy][qx] = r1(m, n, dz, dy, qx);
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t d = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Bx = B(qx, dx);
const real_t Gx = G(qx, dx);
for (int m = 0; m < 3; m++)
{
for (int n = 0; n < 3; n++)
{
const real_t L = (m == 0 ? Gx : Bx);
const real_t R = (n == 0 ? Gx : Bx);
d += L * smem[m][n][dy][qx] * R;
}
}
}
D(dx, dy, dz, v, e) += d;
}
}
MFEM_SYNC_THREAD;
}
}
});
}
MFEM_TMOP_MDQ_REGISTER(TMOPAssembleDiag3D, TMOP_AssembleDiagPA_3D);
MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiag3D);
void TMOP_Integrator::AssembleDiagonalPA_3D(Vector &diagonal) const
{
const int NE = PA.ne, d = PA.maps->ndof, q = PA.maps->nqpt;
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = Reshape(PA.maps->B.Read(), q, d);
const auto G = Reshape(PA.maps->G.Read(), q, d);
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
const auto H = Reshape(PA.H.Read(), 3, 3, 3, 3, q, q, q, NE);
auto D = Reshape(diagonal.ReadWrite(), d, d, d, 3, NE);
TMOPAssembleDiag3D::Run(d, q, NE, B, G, J, H, D, d, q);
}
} // namespace mfem

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