Compare commits

...
Author SHA1 Message Date
Sohail Reddy c1ea8aefb7 Added support for MultiVector and cleaned up. 2026-08-11 13:40:38 -07:00
Sohail Reddy 04444dc37e Merge branch 'multi-vector-dev' into multiapp-io 2026-08-03 15:39:46 -07:00
Sohail Reddy 6f204dde67 Store intermediate fields for jacobian ccompuations 2026-08-03 11:26:36 -07:00
Sohail Reddy f04f98752b Merge branch 'stdmap-generic' into multiapp-io 2026-07-27 18:48:58 -07:00
Sohail Reddy 9b31004ce5 Changed GenericMap to GenericFieldMap 2026-07-27 18:24:51 -07:00
Sohail Reddy 04571c5ddd Using new GenericMap inplace of NamedFieldMap 2026-07-27 16:50:31 -07:00
Sohail Reddy e448b71831 Merge branch 'stdmap-generic' into multiapp-io 2026-07-27 15:56:49 -07:00
Sohail Reddy 48e415e17e Added a generic version of the NamedFieldsMap 2026-07-27 15:56:35 -07:00
Sohail Reddy 8b249e8004 Added Mult(Vector,Vector) support for app call 2026-07-27 14:49:30 -07:00
Veselin Dobrev 9c17c781e1 Merge pull request #5274 from AnnevanGils/complexgridfunction_Lp_error
ComplexGridFunction Lp error implementation
2026-07-22 11:12:17 -07:00
Veselin Dobrev f72b0151e2 Merge pull request #5406 from mfem/gslib-uint-fix
Bug fix for GSLIB: undefined type uint in  header file
2026-07-22 11:07:26 -07:00
Sohail Reddy 1585e7d9f1 Updated GraphGradient 2026-07-21 20:16:35 -07:00
Sohail Reddy ae4a2ee9a6 Added easier method to define input and output size 2026-07-21 19:43:58 -07:00
Sohail Reddy 14e8c6ce45 Removed DataNodes for input and output fields to the graph 2026-07-21 19:36:59 -07:00
Sohail Reddy 05f5967267 fixed typo 2026-07-21 15:11:03 -07:00
Sohail Reddy d9e0018e98 Changed function naming JVP/VJP to GradientMult/GradientMultTranspose and added topological sorting of the graph nodes 2026-07-21 14:19:15 -07:00
Will Pazner c8ef9676e8 Merge pull request #5402 from nmnobre/output
Fix output of H(curl) vars in 1d
2026-07-21 11:22:03 -07:00
Sohail Reddy b1def1e3fa Added check to ensure gslib's integer-type 'uint' is 'unsigned int' 2026-07-15 20:50:59 -07:00
Sohail Reddy f08dba7b84 Clean up and added operator() to execute nodes 2026-07-15 20:18:53 -07:00
Sohail Reddy 7e75c7f6fb Changed Field type from Source/Target to Output/Input 2026-07-15 19:31:26 -07:00
Sohail Reddy a1962fa492 Changed field collection name 2026-07-15 19:00:42 -07:00
Sohail Reddy ce7fefb6eb Bug fix for indexing into input and output Field vectors 2026-07-15 18:50:48 -07:00
Sohail Reddy 503b286f98 Removed support for FieldEdge and improved input/output field indexing 2026-07-15 18:48:15 -07:00
Sohail Reddy cd0616fae0 Improved support for input and output fields andcleaned up old infrastructure 2026-07-15 16:38:28 -07:00
Veselin Dobrev 891b675c7f Use 'hypot' instead of 'sqrt' + squares. 2026-07-15 15:50:01 -07:00
Will Pazner 951cf8886b Merge pull request #5411 from Pennycook/cudss-0.8.0
Add support for cuDSS 0.8.0
2026-07-14 18:18:46 -07:00
Sohail Reddy 1ff144a358 Cleaned up field and edge fetching and adding inputs/outputs 2026-07-14 16:23:54 -07:00
Ketan Mittal f0f1052c99 Merge branch 'master' into gslib-uint-fix 2026-07-14 13:45:26 -07:00
Sohail Reddy 2c02d1b3cb Added initial support for Input and Output Fields to Nodes 2026-07-14 13:17:24 -07:00
Veselin Dobrev 9a87b34c47 Merge pull request #5397 from mfem/spde_small_fix
Fix in the SPDE documentation - a factor of 2
2026-07-14 12:19:15 -07:00
Veselin Dobrev 80e40ace14 Merge pull request #5372 from mfem/bugfix-project
bugfix for coefficient project to quadrature function
2026-07-14 12:18:12 -07:00
Veselin Dobrev f5d71a2798 Merge pull request #5394 from mfem/fix_find_SuiteSparse
fix for finding SuiteSparse with the latest PETSc
2026-07-14 12:16:33 -07:00
Veselin Dobrev 9981355ba2 Merge pull request #5230 from mfem/lorentz-device
Lorentz with particles on device
2026-07-14 12:14:48 -07:00
Sohail Reddy f77dc8d7e9 Removed support for Vectors in FieldEdge and removed unused features 2026-07-13 11:28:28 -07:00
John Pennycook 1416665dc3 Add support for cuDSS 0.8.0
cuDSS 0.8.0 includes some API breaking changes, as described here:
https://docs.nvidia.com/cuda/cudss/migration_guide.html

This commit adds support for the new 0.8.0 API, guarded by #ifdef
macros to ensure compatibility for previous 0.7.x builds.
2026-07-13 02:44:16 -07:00
Sohail Reddy c23b850b77 Added FDJacobian::GetGradient to update finite difference Jacobian 2026-07-10 01:14:02 -07:00
Sohail Reddy ebda52d76c Merge branch 'gslib-uint-fix' into multiapp-mult 2026-07-09 20:05:30 -07:00
Sohail Reddy f0d4d84a91 Bug fix for type uint = unsigned in, defined in gslib's internal header 2026-07-09 16:08:44 -07:00
Sohail Reddy c8ab3cbf69 Branched off master 2026-07-09 15:34:24 -07:00
Nuno Nobre d7c781a6f9 Fix output of H(curl) vars in 1d 2026-07-09 01:49:26 +01:00
Ketan Mittal c9b2ed7a65 Merge branch 'lorentz-device' of https://github.com/mfem/mfem into lorentz-device 2026-07-07 14:14:41 -07:00
Ketan Mittal 9eaa3cdf0a merge with master and resolve conflicts 2026-07-07 14:14:25 -07:00
Ketan MittalSeth Wattscopilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>Tzanio Kolev
17d1afc3b7 Option to specify increase in axis-aligned bounding box size for surface meshes with FindPointsGSLIB (#5259)
* initial commit - working alg

* Add new source files to CMakeLists.txt

* fix bdr tol when min bb size is specified

* support for triangles in surface mesh capability. tested for mixed meshes as well

* empty partition fix and uninitialized values for surface mesh

* fix variable naming and getboundingboxmesh on device

* change semantics of bounding box input for surface meshes

* make style

* documentation and clean up

* fix device access

* update serial miniapp and rename some variables

* minor

* add unit tests for surface meshes

* consolidate shared machinery in a helper file

* rename bb_t

* rename some functions and clean up

* minor

* simplify includes

* remove unused parameter and improve documentation

* restore whitespace

* Make gslib local helpers static

* reviewer comments

* fix edge initialization

* add the new kernel helper in CMakeLists.txt

* manage life of crystal router object in FindPointsGSLIB

* get rid of unnecessary MFEM_DEVICE_SYNC

---------

Co-authored-by: Seth Watts <watts24@llnl.gov>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Tzanio Kolev <tzanio@llnl.gov>
2026-07-07 13:35:30 -07:00
Tzanio Kolev 0eaa3b521a Merge branch 'master' into spde_small_fix 2026-07-07 13:31:32 -07:00
Tzanio Kolev 46d05563d9 Merge pull request #5379 from mfem/speed-up-gitignore-job
Optimize gitignore job
2026-07-07 13:05:55 -07:00
Tzanio Kolev 27c1ea66a4 Merge pull request #5380 from mfem/add-workflow-path-filters
Add path filters to non-required workflows
2026-07-07 13:04:03 -07:00
Tzanio Kolev 7b69944246 Merge branch 'master' into lorentz-device 2026-07-07 11:56:03 -07:00
blaz fa13c848c9 fix in the documentation - a factor of 2 2026-07-07 00:13:18 -07:00
Veselin Dobrev 8d09be7080 Merge pull request #5330 from mfem/hcurl_domain_lf
Add device assembly support for H(curl) VectorFEDomainLFIntegrator
2026-07-06 14:53:56 -07:00
Veselin Dobrev 7f71dacae6 Merge pull request #5388 from mfem/nbeams/fix-gko-hypre-type
Fix gko_hypre_* types creation
2026-07-06 14:52:14 -07:00
bslazarov a7b74155f7 fix for finding SuiteSparse with the latest PETSc 2026-07-04 18:40:59 -07:00
John Camier 0c20eef8fe Merge branch 'master' into lorentz-device 2026-07-03 19:04:50 +02:00
John Camier 842514bde2 Merge branch 'master' into bugfix-project 2026-07-03 19:03:33 +02:00
John Camier ee498a19f7 Merge branch 'master' into add-workflow-path-filters 2026-07-03 19:03:11 +02:00
John Camier bd5f9d80b4 Merge branch 'master' into speed-up-gitignore-job 2026-07-02 18:06:03 +02:00
Veselin Dobrev 6ee3bbde89 Merge pull request #5346 from nmnobre/hypremat
Preemptively delete rownnz if ownership flags set to -1
2026-07-01 12:18:03 -07:00
Veselin Dobrev 92f4fe3bd0 Merge pull request #5383 from mfem/raja-stream-fix
Raja stream fix
2026-07-01 10:02:00 -07:00
nbeams 0171b4b02d Only set gko_hypre_* types when building with MPI 2026-06-30 21:20:25 +00:00
John Camier 68e3a929c2 Merge branch 'master' into lorentz-device 2026-06-30 17:51:21 +02:00
John Camier 06d18956f8 Merge branch 'master' into bugfix-project 2026-06-27 20:14:54 +02:00
John Camier 60c2ac77d1 Merge branch 'master' into raja-stream-fix 2026-06-27 20:04:17 +02:00
John Camier ee23534091 Merge branch 'master' into speed-up-gitignore-job 2026-06-27 20:03:58 +02:00
John Camier c0da3d6aa9 Merge branch 'master' into add-workflow-path-filters 2026-06-27 20:01:48 +02:00
Andrew Ho fef38a9fd2 RAJA resources appear to be relatively lightweight, just construct it when needed 2026-06-26 14:45:43 -07:00
Tzanio Kolev 01aa047be1 Merge pull request #5373 from mfem/fix-project-bdr-types
[BUG] Fixed type narrowing in ProjectBdrCoefficientNormal unit test
2026-06-26 12:11:27 -07:00
Tzanio Kolev 8c68e8402f Merge branch 'master' into speed-up-gitignore-job 2026-06-26 11:44:38 -07:00
Tzanio Kolev a24e3f6dfb Merge pull request #5365 from mfem/fix-cmake-parallel
Fix CMake parallel build issue
2026-06-26 11:34:26 -07:00
Tzanio Kolev 950f406803 Merge pull request #5377 from mfem/fix-specializations
Fix AddSpecialization header includes
2026-06-26 11:33:24 -07:00
Tzanio Kolev ade7aedfc6 Merge pull request #5126 from mfem/support-shared-build-with-fetching
Support shared MFEM build when fetching TPLs
2026-06-26 11:32:12 -07:00
Andrew Ho 79bca13634 Don't insist on RAJA/CAMP always using default stream and create an internal resource with the default stream 2026-06-25 21:59:07 -07:00
John Camier 5048b1a219 Merge branch 'master' into lorentz-device 2026-06-25 07:47:47 +02:00
John Camier b84988d5f1 Merge branch 'master' into bugfix-project 2026-06-25 07:44:37 +02:00
John Camier 55aa12823c Merge branch 'master' into fix-specializations 2026-06-25 07:44:01 +02:00
Andrew Ho 80555fa132 added documentation 2026-06-24 20:45:13 -07:00
Tara Drwenski 9a98c2be01 Revert "Test: Comment out something from gitignore to test gitignore job"
This reverts commit 16f9cb63a1.
2026-06-24 14:12:34 -07:00
Tzanio Kolev 268231dd09 Merge branch 'master' into hcurl_domain_lf 2026-06-24 12:47:14 -07:00
Tara Drwenski 10880e5ad2 Add path filters to non-required workflows 2026-06-24 11:10:07 -07:00
Tara Drwenski 3df2f14eb9 Merge branch 'master' into speed-up-gitignore-job 2026-06-24 11:06:31 -07:00
Tzanio Kolev 21f1404580 Merge branch 'master' into support-shared-build-with-fetching 2026-06-24 11:03:58 -07:00
Tzanio Kolev 63a38bed84 Merge branch 'master' into hypremat 2026-06-24 11:03:55 -07:00
Tzanio Kolev 867c0a0e7e Merge pull request #5174 from mfem/tmop-mesh-validity
Ensuring mesh validity during mesh optimization using bounds on Jacobian determinant
2026-06-24 11:02:33 -07:00
Tzanio Kolev 270c4d5175 Merge pull request #5374 from mfem/fix-macos-runner
Fix MacOS runner issue
2026-06-24 10:59:51 -07:00
Veselin Dobrev 0779fbbc72 Merge pull request #5302 from lindsayad/bel-sorting
ReorderElements: sort boundary elements by face index after reordering
2026-06-24 10:31:39 -07:00
Tara Drwenski 16f9cb63a1 Test: Comment out something from gitignore to test gitignore job 2026-06-24 10:23:58 -07:00
Tara Drwenski 58da879f06 Remove mfem-analysis from badges in the contributing guide 2026-06-24 10:23:58 -07:00
Tara Drwenski 09b9e1c775 Style fix: use YES instead of true to match current style 2026-06-24 10:23:58 -07:00
Tara Drwenski 1ac86956d1 Move gitignore check from own workflow to builds-and-tests to avoid rebuilding 2026-06-24 08:32:37 -07:00
Andrew Ho 48622d3f1e need to include simplices headers in order for AddSpecialization to work 2026-06-23 16:12:38 -07:00
Tara Drwenski 5cd7f88da0 Remove XCode version setup which breaks on runner update 2026-06-23 11:46:48 -07:00
Tzanio Kolev cc6a34b575 Merge pull request #5358 from mfem/ci-num-tasks
In CI, auto-detect the number of build tasks
2026-06-23 11:45:03 -07:00
Jan Nikl 9cf0b8cb08 Fixed type narrowing in ProjectBdrCoefficientNormal unit test. 2026-06-23 10:22:52 -07:00
Andrew Ho 4843835f98 bugfix where coefficient projection assumes quadrature function is valid on host 2026-06-23 09:59:19 -07:00
Ketan Mittal 161630bf12 fix edge initialization for surface kernels 2026-06-22 16:11:56 -07:00
Veselin Dobrev fa9fdc6576 Address reviewer comments 2026-06-22 10:24:28 -07:00
Nuno NobreandJan Nikl 03c99b8dfe Apply minor rephrasing suggestions
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-06-22 18:08:23 +01:00
Ketan Mittal b65e7ee791 Merge branch 'lorentz-device' of https://github.com/mfem/mfem into lorentz-device 2026-06-22 09:29:16 -07:00
Ketan Mittal 473084c8c5 nvcc fix for host/device lambdas 2026-06-22 09:28:55 -07:00
Tzanio Kolev e25f778f7b Reorganized CHANGELOG 2026-06-22 08:56:58 -07:00
Ketan Mittal 2f0bbc6fc4 Merge branch 'master' into lorentz-device 2026-06-22 08:55:49 -07:00
Tzanio Kolev b5baebd0b3 Merge branch 'master' into tmop-mesh-validity 2026-06-22 08:40:47 -07:00
Tzanio Kolev c9ecc5b466 Merge pull request #5205 from mfem/trace-p-ref
PRefinementTransferOperator for trace spaces
2026-06-22 08:40:35 -07:00
Tzanio Kolev f38de5059e Merge branch 'master' into tmop-mesh-validity 2026-06-22 08:39:13 -07:00
Tzanio Kolev 0ff0e1c76b Merge pull request #5223 from mfem/jdongg/pa-simplices
Partial assembly algorithms for Bernstein basis on simplicial meshes [pa-simplices-dev]
2026-06-22 08:37:01 -07:00
Ketan Mittal c7cba857af fix tag access in Get/SetParticle 2026-06-19 11:00:15 -07:00
Ketan Mittal 85a16c2e43 Merge branch 'lorentz-device' of https://github.com/mfem/mfem into lorentz-device 2026-06-19 09:51:48 -07:00
Ketan Mittal 3c4d103982 do compact transfer between host-device during redistribute 2026-06-19 09:51:24 -07:00
Veselin Dobrev fcca0eaa71 Replace the 'mfem/github-actions' branch name 'update-num-tasks' with the
tag 'v2.7'.
2026-06-18 14:58:00 -07:00
Ketan Mittal 0bfa06f87d merge with master and resolve conflicts 2026-06-18 12:40:44 -07:00
Ketan Mittal 3aed584bf7 Merge branch 'tmop-mesh-validity' of https://github.com/mfem/mfem into tmop-mesh-validity 2026-06-18 08:59:48 -07:00
Ketan Mittal c4d944cfad restore tolerance for barrier 2026-06-18 08:59:33 -07:00
Socratis Petrides c05114555f Merge branch 'trace-p-ref' of github.com:mfem/mfem into trace-p-ref 2026-06-17 18:41:27 -07:00
Socratis Petrides dfc15d77e5 Tzanio/codex review 2026-06-17 18:40:16 -07:00
Tzanio Kolev 25e5ac7db0 Merge branch 'master' into trace-p-ref 2026-06-17 09:14:57 -07:00
John Camier d0533903f3 Merge branch 'master' into bel-sorting 2026-06-17 07:25:22 -07:00
John Camier 38ed1e049b Merge branch 'master' into lorentz-device 2026-06-17 05:58:47 -07:00
Socratis Petrides 48e258a60c add ownership documentation 2026-06-16 17:37:39 -07:00
Socratis Petrides e1b9c355e6 fix local prolonation issue in case of vdim > 1 2026-06-16 17:36:49 -07:00
Socratis Petrides c3db6ed873 fix headers 2026-06-16 17:35:50 -07:00
Socratis PetridesandTzanio Kolev 56b3e1ecc6 Update CHANGELOG
Co-authored-by: Tzanio Kolev <tzanio@llnl.gov>
2026-06-16 11:58:22 -07:00
Socratis PetridesandTzanio Kolev 5385e090f7 Update CHANGELOG
Co-authored-by: Tzanio Kolev <tzanio@llnl.gov>
2026-06-16 11:57:54 -07:00
Socratis PetridesandTzanio Kolev ecb7767b70 Update CHANGELOG
Co-authored-by: Tzanio Kolev <tzanio@llnl.gov>
2026-06-16 11:57:21 -07:00
Socratis PetridesandTzanio Kolev 42bf39cd8c Update miniapps/dpg/makefile
Co-authored-by: Tzanio Kolev <tzanio@llnl.gov>
2026-06-16 11:57:01 -07:00
Tzanio Kolev 716f92d636 Merge pull request #5309 from mfem/fix-rocm7-hipsparse
Fix hipsparse in ROCm 7
2026-06-16 11:28:45 -07:00
Tzanio Kolev efdc17ce07 Merge pull request #5339 from mfem/tmop-AL-multiZ
TMOP - allow multiple fields in adaptive limiting.
2026-06-16 11:24:22 -07:00
John Camier ffab9d82fa Merge branch 'master' into bel-sorting 2026-06-16 07:23:55 -07:00
camierjs 891eaf2829 Fix missing B/Bt for OCCA backend 2026-06-16 07:07:34 -07:00
Ketan Mittal 053804f759 Merge branch 'master' into tmop-mesh-validity 2026-06-15 13:03:44 -07:00
Andrew Ho a8af93c4ec Merge branch 'master' into trace-p-ref 2026-06-15 12:19:38 -07:00
Andrew Ho ac7fa02927 Merge branch 'master' into support-shared-build-with-fetching 2026-06-15 11:59:04 -07:00
Ketan Mittal eeca0b4cd5 reviewer comments 2026-06-14 19:52:40 -07:00
Victor A. P. Magri ad18d18be1 Fix CMake parallel build issue with auto-fetched hypre 2026-06-13 00:10:33 -04:00
jdongg 00a1f5ed64 Add explicit return outside of if statement for mass and diffusion kernels 2026-06-12 08:05:30 -07:00
Veselin Dobrev a76b0dbc0e In GitHub CI, in the sanitizer builds of hypre and metis, do not set the
environment variables CXXFLAGS and LDFLAGS -- there are not needed and were
causing the hypre build to fail.
2026-06-12 02:43:18 -07:00
Veselin Dobrev 59ce107b33 In GitHub CI, fix caching key 2026-06-12 02:21:59 -07:00
Veselin Dobrev ce1036ccc8 Another try to define ACTIONS_VERSION in
.github/actions/sanitize/config/action.yml
in a way that it will be picked up by other sanitizer actions.
2026-06-12 01:06:06 -07:00
Veselin Dobrev b6861ed26f In GitHub CI, define ACTIONS_VERSION in
.github/actions/sanitize/config/action.yml
instead of
   .github/workflows/sanitizers.yml
where it was not picked up by some called actions.
2026-06-12 00:53:39 -07:00
Veselin Dobrev 8522cceb89 In CI, variables like ${{env.VAR}} cannot be used in "uses:" fields. 2026-06-11 23:46:43 -07:00
Veselin Dobrev dec9509a5d In GitHub CI, use and environment variable to set the version (branch or tag)
of the 'mfem/github-actions' to use.
2026-06-11 18:56:46 -07:00
Vladimir Z Tomov 5f02c6f64b fixed nvcc error for protected functions 2026-06-11 14:50:52 -07:00
jdongg 8a69b52f48 Add explicit return in diffusion and mass kernels in else branch. 2026-06-10 20:52:49 -07:00
Tzanio Kolev 51a0f8accb Merge branch 'master' into jdongg/pa-simplices 2026-06-10 15:54:26 -07:00
Ketan Mittal eb4fa33a7b Merge branch 'master' of https://github.com/mfem/mfem into lorentz-device 2026-06-10 15:50:13 -07:00
Andrew Ho 42d7d20e43 Merge branch 'master' into hcurl_domain_lf 2026-06-10 15:27:57 -07:00
Nuno Nobre 75f8ca8cd4 Switch to GetHypreMemoryLocation() 2026-06-10 18:33:14 +01:00
Nuno Nobre 36e915f226 Guard hypre_CSRMatrixMemoryLocation w/ hypre version check 2026-06-10 18:11:40 +01:00
Nuno Nobre f236d70a19 Avoid calling HypreParMatrix::Write() again and clearing ptrs 2026-06-10 13:58:58 +01:00
Nuno Nobre 85e33c6645 Use hypre_CSRMatrix{I,J,Data,OwnsData} and update explainer comment 2026-06-10 13:22:46 +01:00
Veselin Dobrev 7b62f035a5 Add a fix for the issue -- alternative to the solution in PR #5346. 2026-06-10 13:14:28 +01:00
Nuno Nobre 81fb02389f Revert "Preemptively delete rownnz if ownership flags set to -1"
This reverts commit 7a6313d725.
2026-06-10 11:35:16 +01:00
Nuno Nobre 23666fd4d8 Fix missing #ifdef MFEM_USE_MPI in new unit test 2026-06-10 11:25:58 +01:00
Nuno Nobre f3a60e2f08 Merge branch 'master' into hypremat 2026-06-10 11:25:00 +01:00
Veselin Dobrev 8777bc0810 In CI, set the number of build tasks for the 'build-mfem' action automatically 2026-06-09 18:20:58 -07:00
Ketan Mittal de97775bd0 Merge branch 'master' into tmop-mesh-validity 2026-06-09 16:11:59 -07:00
John Camier ad453255a5 Merge branch 'master' into bel-sorting 2026-06-09 06:46:23 -07:00
John Camier 2143ed5ca8 Merge branch 'master' into fix-rocm7-hipsparse 2026-06-09 06:46:06 -07:00
John Camier 2f21794999 Merge branch 'master' into hypremat 2026-06-09 06:45:29 -07:00
John Camier 2aa8372cdc Merge branch 'master' into jdongg/pa-simplices 2026-06-09 06:43:22 -07:00
Ketan Mittal 0176c7664c merge and resolve conflicts 2026-06-08 19:27:48 -07:00
Ketan Mittal e1c0705785 reviewer comments 2026-06-08 19:24:21 -07:00
Andrew Ho 50b8f67bd1 Merge branch 'master' into hcurl_domain_lf 2026-06-08 12:41:22 -07:00
Vladimir Z Tomov a4d114ad6e renamed a function, improved some comments 2026-06-08 10:04:43 -07:00
Ketan Mittal 1e5609f6c9 Merge branch 'tmop-mesh-validity' of https://github.com/mfem/mfem into tmop-mesh-validity 2026-06-08 09:16:47 -07:00
Ketan Mittal c21d90589b Merge branch 'master' of https://github.com/mfem/mfem into tmop-mesh-validity 2026-06-08 09:16:28 -07:00
Ketan Mittal bef7183ce7 documentation and CHANGELOG 2026-06-08 09:16:23 -07:00
Veselin Dobrev db7da59b03 Add a test that reproduces the issue described in PR #5200 2026-06-07 19:30:38 +01:00
John Camier 28194a6736 Merge branch 'master' into lorentz-device 2026-06-06 16:37:35 -07:00
Tzanio Kolev f6eda1fce6 Merge branch 'master' into tmop-mesh-validity 2026-06-05 16:40:10 -07:00
Tzanio Kolev a1bcd7443d Merge branch 'master' into jdongg/pa-simplices 2026-06-05 16:39:29 -07:00
Ketan Mittal 9c4d742db8 update gridfunction after h-adaptivity 2026-06-05 09:41:42 -07:00
Ketan Mittal 16ac6c40a3 refactor to have TMOPNewtonSolver manage bounding detJ 2026-06-05 09:33:21 -07:00
jdongg ee2484383c Remove own_rules from intrules.cpp 2026-06-04 15:43:36 -07:00
jdongg 1863dafeca Remove own rules flag and function. 2026-06-04 15:18:35 -07:00
Vladimir Z Tomov a4c7828ac2 pr review comments 2026-06-04 15:07:12 -07:00
Ketan Mittal 0a02f5737a address reviewer comments 2026-06-04 14:00:41 -07:00
Ketan Mittal 154a1840d0 Merge branch 'tmop-mesh-validity' of https://github.com/mfem/mfem into tmop-mesh-validity 2026-06-04 12:41:56 -07:00
Ketan Mittal 13e83cbd14 minor 2026-06-04 12:41:53 -07:00
Vladimir Z Tomov f64405ebb6 minor 2026-06-03 17:56:37 -07:00
Ketan Mittal 92f2057650 Merge branch 'master' into tmop-mesh-validity 2026-06-03 17:48:17 -07:00
Ketan Mittal f3558cb78c Merge branch 'master' into tmop-AL-multiZ 2026-06-03 10:15:35 -07:00
John Camier d5ddb83c9d Merge branch 'master' into jdongg/pa-simplices 2026-06-02 06:08:39 -07:00
camierjs 869b0e48a6 Add simplices tests with D1D > Q1D 2026-06-01 09:31:24 -07:00
John Camier 75180606f8 Merge branch 'master' into bel-sorting 2026-06-01 08:07:45 -07:00
John Camier 3cac4326e4 Merge branch 'master' into fix-rocm7-hipsparse 2026-06-01 08:07:06 -07:00
Ketan Mittal da5e8b5844 Merge branch 'master' into tmop-mesh-validity 2026-05-31 22:52:14 -07:00
Vladimir Z Tomov 081a90d1fa style 2026-05-31 14:45:56 -07:00
Vladimir Z Tomov d06a3eec7f use Vectors instead of allocating hypre vectors in AdvectorCG 2026-05-29 11:14:50 -07:00
jdongg 2766e56f2d Remove GaussJacobi out-of-range error check on alpha and beta before hard-coded cases. Add error check for unsupported geometries in StroudIntegrationRules class. 2026-05-28 12:58:02 -07:00
jdongg 0c781120a2 Remove references to InverseDuffyTrans in comments. Add description of on-the-fly inverse Duffy transform in GetRaggedTensorDofToQuad. Fix typos in GaussJacobi MFEM_ABORT. Remove unnecessary if statement in GaussJacobi routine when floating point type is undefined. 2026-05-28 11:51:50 -07:00
camierjs 449c9f5903 Avoid duplicate meshes in test_pa_simplices 2026-05-28 06:27:50 -07:00
Tzanio Kolev 6f793edfb7 Merge branch 'master' into tmop-AL-multiZ 2026-05-27 09:34:31 -07:00
John Camier 6455a0c1fc Merge branch 'master' into jdongg/pa-simplices 2026-05-27 06:31:47 -07:00
Vladimir Z Tomov b19c3c7e01 avoid Makeref 2026-05-26 22:23:20 -07:00
Vladimir Z Tomov fb7c8af59e more Newton iterations in the tmop unit test 2026-05-26 20:32:55 -07:00
Mittal, Ketan f6ea1ea0da minor 2026-05-26 10:01:57 -07:00
Nuno Nobre 7a6313d725 Preemptively delete rownnz if ownership flags set to -1 2026-05-26 17:24:58 +01:00
Mittal, Ketan 84bbead832 move lambdas to static functions for nvcc 2026-05-26 09:09:20 -07:00
Vladimir TomovandCopilot Autofix powered by AI ef6510b42e typo
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-25 19:09:36 -07:00
Vladimir TomovandCopilot Autofix powered by AI 27ce32de07 typo
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-25 19:08:24 -07:00
Vladimir Z Tomov 341f6ec683 minor 2026-05-25 19:06:57 -07:00
Vladimir Z Tomov 8c88e1e26a style 2026-05-25 19:01:17 -07:00
Vladimir Z Tomov cabdb42bb4 Remap the adaptive limiting fields as a multi-component vector. 2026-05-25 18:52:04 -07:00
Vladimir Z Tomov e4a69c6245 vis in the mesh-optimizer, shorter 3d runs 2026-05-25 17:38:27 -07:00
Mittal, Ketan dbb751d7fb Merge branch 'master' of https://github.com/mfem/mfem into lorentz-device 2026-05-25 15:33:29 -07:00
John Camier e447f090cc Merge branch 'master' into jdongg/pa-simplices 2026-05-24 20:14:10 -07:00
Vladimir Z Tomov c3096d08bf Better example with 2 fields in the miniapps. 2026-05-24 18:07:53 -07:00
Vladimir Z Tomov 67b906ea3b Restored the optimization when the coefficients are ConstantCoefficients. 2026-05-24 17:46:37 -07:00
Vladimir Z Tomov 9167fc0c58 style 2026-05-22 18:03:28 -07:00
Vladimir Z Tomov e48ffe3579 cleanup 2026-05-22 18:00:32 -07:00
Vladimir Z Tomov a7697c1db1 cleanup 2026-05-22 17:34:05 -07:00
Vladimir Z Tomov 36c1cce132 delta_max per specified function. 2026-05-22 17:09:04 -07:00
Vladimir Z Tomov 38f257cf87 Multiple fields for adaptive limiting - initial pass. 2026-05-22 16:32:34 -07:00
camierjs a0022e0330 Fix number of threads in simplices kernels to allow D1D > Q1D, revert Stroud int rules in ex1[p], fix thread race in SmemPADiffusionApplyTetrahedron 2026-05-22 06:45:02 -07:00
camierjs 3a2f286e2d Select Stroud integration rule ine ex1[p] when needed, add verification in simplicial kernels that D1D <= Q1D 2026-05-21 08:09:33 -07:00
Mittal, Ketan 58e6c4fc6d merge with master and resolve conflicts 2026-05-20 11:21:29 -07:00
jdongg 7031f7cb80 Remove pullback flag from simplex unit tests 2026-05-19 22:24:45 -07:00
jdongg afaced7cd6 Rebase InverseDuffyTrans removal onto master. 2026-05-19 21:58:22 -07:00
jdongg d671ca4712 Remove InverseDuffyTrans and perform inverse mapping on the fly in GetRaggedTensorDofToQuad. Remove Pullback option from StroudIntRules object. Remove unused T array from RaggedDofToQuad object and mass+diffusion integrators. 2026-05-19 21:57:06 -07:00
John Camier 1095aee346 Merge branch 'master' into jdongg/pa-simplices 2026-05-19 20:28:53 -07:00
camierjs ba8764300c Revert the nvcc warning fix that will be addressed in another PR 2026-05-19 11:43:11 -07:00
camierjs 510477a204 Fix nvcc with gcc use of literal operator warnings 2026-05-19 09:23:17 -07:00
camierjs 0b9ae2202c Fix nvcc identifier warnings: preceded by whitespace in a literal operator declaration 2026-05-19 08:38:55 -07:00
camierjs 60641259b2 Revert ex1[p] refinements 2026-05-19 08:27:20 -07:00
camierjs 99e2b5023d Simplify ex1[p] with IsSimplexMesh 2026-05-19 08:26:01 -07:00
John Camier 4c59f6cfe6 Merge branch 'master' into jdongg/pa-simplices 2026-05-18 06:04:46 -07:00
Andrew Ho 670dfb2d98 fix compiler warning about missing enum cases 2026-05-15 12:23:24 -07:00
Andrew Ho 4167f0027c combine H(div) and H(curl) linear form extension tests 2026-05-15 12:14:13 -07:00
Andrew Ho b59ccd206d specify only for 3D H(curl) 2026-05-15 11:45:54 -07:00
Ketan Mittal cee913cc04 Merge branch 'master' into tmop-mesh-validity 2026-05-15 10:34:26 -07:00
Andrew Ho 76b033ad36 accidentally duplicated file 2026-05-15 01:19:54 -07:00
Andrew Ho 0cd0a98198 Add device assembly support for H(curl) VectorFEDomainLFIntegrator. 2026-05-15 01:08:22 -07:00
Alex Lindsay bd5d928084 Pare down doxygen for GetFace group 2026-05-14 14:48:09 -07:00
John Camier 5e62d62c3d Merge branch 'master' into jdongg/pa-simplices 2026-05-14 13:21:04 -07:00
Ketan Mittal 5faba43544 Merge branch 'master' into bel-sorting 2026-05-14 10:34:41 -07:00
Alex Lindsay 20805d87b4 Prevent comment lines from going past column 80 2026-05-13 11:33:48 -07:00
Alex Lindsay 0119d25dfc Factor out common testing code 2026-05-13 11:32:12 -07:00
John Camier 22c15267f9 Merge branch 'master' into bel-sorting 2026-05-12 06:58:24 -07:00
John Camier 6c83fec2da Merge branch 'master' into fix-rocm7-hipsparse 2026-05-12 06:58:03 -07:00
Ketan Mittal 411a361ebf Merge branch 'master' into lorentz-device 2026-05-09 20:22:29 -07:00
John Camier bc845844ea Merge branch 'master' into jdongg/pa-simplices 2026-05-09 11:31:25 -07:00
camierjs 35ceed5193 Cleanup 2026-05-07 11:10:58 -07:00
camierjs ce52e1f51f Use MFEM_FOREACH_THREAD_DIRECT for simplices kernels 2026-05-07 11:01:25 -07:00
camierjs 16a21b366e Fix missing sync thread in smem mass simplices 2026-05-07 10:41:00 -07:00
John Camier d540fa5a12 Merge branch 'master' into bel-sorting 2026-05-07 08:06:44 -07:00
Tzanio Kolev 1221dea58e Merge branch 'master' into fix-rocm7-hipsparse 2026-05-07 07:29:53 -07:00
camierjs a5b9a7948f Add ex1p device simplices sample runs 2026-05-06 17:22:44 -07:00
camierjs 386e7e8c6a Add ex1 device simplices sample runs 2026-05-06 17:15:27 -07:00
camierjs 26077ab9aa Remove the ex_pa_simplices miniapps 2026-05-06 14:22:44 -07:00
Ketan Mittal 0ee57ac12d Merge branch 'master' into lorentz-device 2026-05-05 22:53:21 -07:00
camierjs 87bdb90bcc Merge branch 'master' into jdongg/pa-simplices 2026-05-05 11:20:27 -07:00
camierjs 848b54d13a Add pa-simplices to the selected files for formatting 2026-05-05 11:20:15 -07:00
camierjs 62214f61ae Merge branch 'master' into jdongg/pa-simplices 2026-05-05 06:29:17 -07:00
John Camier dee971c308 Merge branch 'master' into lorentz-device 2026-05-05 06:21:51 -07:00
John Camier 9b93f1c1e1 Merge branch 'master' into bel-sorting 2026-05-05 06:19:44 -07:00
John Camier be03c9703f Merge branch 'master' into fix-rocm7-hipsparse 2026-05-05 06:14:52 -07:00
Tom Stitt b885fdc50f fix 2026-05-04 17:18:39 -07:00
Tom Stitt e4c0069ad9 better documentation 2026-05-04 14:41:49 -07:00
Tom StittandAndrew Ho 9fe53c2403 Apply suggestion from @helloworld922
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2026-05-04 14:14:01 -07:00
Mittal, Ketan bb12710561 Merge branch 'lorentz-device' of https://github.com/mfem/mfem into lorentz-device 2026-05-04 14:04:25 -07:00
Mittal, Ketan f3276a0d5d address co-pilot comments 2026-05-04 14:04:11 -07:00
Ketan Mittal 00b9678b1d Merge branch 'master' into tmop-mesh-validity 2026-05-04 10:31:41 -07:00
Tzanio Kolev 426b5dc6cd Merge branch 'master' into lorentz-device 2026-05-02 13:00:07 -07:00
camierjs 5e15a2e29b Cleanup PA simplices tests 2026-04-30 20:56:34 -07:00
camierjs d168f5b489 Revert intrules in PA simplices unit tests 2026-04-30 20:06:24 -07:00
camierjs 7a5d13e86a Fix & simplify ceed benchmark tests 2026-04-30 18:58:43 -07:00
camierjs 2e0d1697b9 Use specific simplices diffusion specializations when Q=P
Use integrations rules from integrators for unit testing
2026-04-30 18:25:13 -07:00
camierjs 87168f51ab Meld back toward master, removing un-warnings 2026-04-30 14:43:49 -07:00
jdongg 2558e32ee0 Switch std::beta to std::tgamma for compliances with C++11 standard 2026-04-30 13:28:32 -07:00
jdongg 7521efeff9 Add unit tests for Stroud quadrature rules as well as general Gauss-Jacobi rules 2026-04-30 13:11:57 -07:00
camierjs 3b23fd4941 Add extra meshs for PA simplices tests 2026-04-30 10:54:44 -07:00
camierjs d6a084322b Use new StroudIntRules for PA simplices tests 2026-04-30 09:34:01 -07:00
camierjs d2a38dd3d4 Merge branch 'jdongg/pa-simplices' of github.com:mfem/mfem into jdongg/pa-simplices 2026-04-30 09:27:34 -07:00
camierjs 17aab43082 Fix PA simplices tests with ir, fix PADiffusionApplyTriangle accumulate 2026-04-30 09:27:11 -07:00
jdongg e5c3cf4ee3 Move Duffy transform from IntegrationRule class to free-standing functions. Create StroudIntegrationRules class for caching Stroud rules. 2026-04-30 07:10:38 -07:00
camierjs b09cc88bab Just avoid the funct_coeff 2026-04-29 19:06:33 -07:00
camierjs 620904124e Init also y in test_pa_simplices 2026-04-29 18:50:03 -07:00
camierjs 6e55cfdc39 Shuffle back bilininteg mass pa simplices 2026-04-29 18:46:16 -07:00
camierjs bef23c5770 Split bilininteg mass pa simplices 2026-04-29 18:38:24 -07:00
camierjs a6fcf162a6 Split bilininteg diffusion pa simplices
and add initial unit tests
2026-04-29 18:20:12 -07:00
John Camier ba9af41877 Merge branch 'master' into jdongg/pa-simplices 2026-04-29 17:04:48 -07:00
John Camier 2762b9dbfc Merge branch 'master' into bel-sorting 2026-04-29 17:04:40 -07:00
John Camier d75a153df8 Merge branch 'master' into fix-rocm7-hipsparse 2026-04-29 17:04:16 -07:00
Mittal, Ketan 0dd81462c0 use forall_switch instead of MFEM_FORALL 2026-04-29 15:29:46 -07:00
Mittal, Ketan 25514d6e8e merge with master and resolve conflicts 2026-04-29 15:02:32 -07:00
Mittal, Ketan 9f01e61a57 add unit test for redistribution of particle data when it is on device 2026-04-29 14:58:30 -07:00
Mittal, Ketan 90fdd7e762 Merge branch 'master' of https://github.com/mfem/mfem into lorentz-device 2026-04-27 14:00:49 -07:00
Mittal, Ketan cfa6594977 minor 2026-04-27 14:00:47 -07:00
John Camier b170c6ae54 Merge branch 'master' into jdongg/pa-simplices 2026-04-27 08:12:29 -07:00
John Camier c4b4ad3224 Merge branch 'master' into bel-sorting 2026-04-27 08:11:59 -07:00
John Camier cecd75aff6 Merge branch 'master' into fix-rocm7-hipsparse 2026-04-27 08:11:53 -07:00
jdongg 50b197e754 Remove unused variables 2026-04-20 13:05:50 -07:00
jdongg 69dc2b5142 Make style 2026-04-20 12:56:55 -07:00
jdongg fc26f0a773 Switch temporary 1d GJ rules for Stroud construction from heap to stack memory. Clean up diffusion and mass kernels so they all use transposed basis arrays in quad-to-dof operation. Enforce uniform loop index notation across all kernels. 2026-04-20 12:53:00 -07:00
jdongg d160b88706 Guard against 0-size shared memory arrays in simplex diffusion kernels 2026-04-17 19:09:46 -07:00
jdongg 007d5e3e43 Re-add specializations for D1D=1 and D1D=9 since they no longer cause issues 2026-04-17 18:55:58 -07:00
jdongg fd78d48dfb Fix memory leaks (missing virtual destructor for DofToQuad after new derived class); fix race condition in diffusion integrator 2026-04-17 14:38:53 -07:00
camierjs c1d1df3d80 Merge branch 'master' into jdongg/pa-simplices 2026-04-17 07:04:10 -07:00
Tom Stitt c0d32918b0 add explicit call to hipsparseSpMV_preprocess to avoid runtime errors in rocsparse with ROCm 7
add checks to all cu/hipsparse calls, which i've confirmed would have cought this

add a static toggle for vendor libsparse usage
2026-04-16 12:36:23 -07:00
Socratis Petrides 03937d0dc2 Fix typo 2026-04-16 12:01:56 -07:00
jdongg 63e24c2a25 Fix documentation of RAGGED_TENSOR mode in DofToQuad. Move Bernstein-specific fields to derived class RaggedDofToQuad. Guard against overwriting Stroud rules in IntegrationRules::Set. 2026-04-15 20:05:06 -07:00
Socratis Petrides c202f9244a more fixes from review comments 2026-04-15 15:07:55 -07:00
Alex LindsayandClaude Sonnet 4.6 76cf9a2d00 Add Doxygen for Mesh::GetFaceElements()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 15:50:11 -07:00
Alex LindsayandClaude Sonnet 4.6 4f796b3708 ReorderElements: sort boundary elements by face index after reordering
After calling ReorderElements the elements[] array is reordered but
boundary[] was left in its original arbitrary order.  be_to_face is
correctly rebuilt by vertex lookup, so individual face lookups remained
correct, but the boundary element ordering was inconsistent with the new
volume element ordering.

Sort boundary[] and be_to_face[] by face index (be_to_face[i]) after
rebuilding the face tables.  Face indices are assigned by
GetElementToFaceTable in the new element order, so sorting by face index
produces the same result as GenerateBoundaryElements would on a mesh
originally stored in the reordered element order.  Because each boundary
face has exactly one boundary element the sort is a strict total order
with no ties, making the output deterministic: two meshes with the same
geometry but different initial numberings produce identical mesh files
after Hilbert reordering.

For 3D meshes bel_to_edge (boundary-element-to-edge table) is also
permuted to stay consistent with the new boundary element ordering.

Add unit tests covering 3D hex, 2D quad, and 3D tet meshes that verify
the adjacent-element indices are non-decreasing across the sorted
boundary element list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 15:32:32 -07:00
Socratis Petrides 2399e5e294 refactored trace tests to avoid code dublication 2026-04-06 18:53:36 -07:00
Socratis Petrides 205b694162 Merge branch 'master' into trace-p-ref 2026-04-06 18:30:43 -07:00
Socratis Petrides f2b661ca05 more fixes 2026-04-06 18:30:15 -07:00
Socratis Petrides c8ef971f03 review fixes 2026-04-06 18:11:06 -07:00
Socratis Petrides 10aa1133b2 review fixes 2026-04-06 13:22:59 -07:00
John Camier 05c3a2c83f Merge branch 'master' into jdongg/pa-simplices 2026-04-04 07:11:19 -07:00
jdongg d194a26542 Fix memory leak 2026-04-02 06:12:50 -07:00
Justin Dong a3a368dfd9 Make style 2026-04-02 05:26:08 -07:00
jdongg e8f39487b9 Fix doxygen formatting 2026-04-02 05:08:15 -07:00
jdongg ec6e2a7c74 Address PR reviews, pt. 1: removed unused lines, improve documentation, remove individual IntegrationRule objects for Stroud components, remove IsBernsteinSimplexSpace function from fespace and combine with UsesRaggedTensorBasis, ensuring checks for entire -mesh geoemtries are done, introduce MAX_Q1D_SIMPLEX and
MAX_D1D_SIMPLEX.
2026-04-02 04:51:31 -07:00
John Camier cb1f54ed2b Merge branch 'master' into jdongg/pa-simplices 2026-03-29 17:31:29 -07:00
John Camier 9a18da5aaa Merge branch 'master' into jdongg/pa-simplices 2026-03-24 07:58:41 -07:00
AnnevanGils 02822e620a Merge branch 'master' into complexgridfunction_Lp_error 2026-03-24 10:49:26 +01:00
camierjs 4e31111827 Add BP'7' for simplices 2026-03-20 17:50:05 -07:00
camierjs 0cbf5b5562 CEED bench GLL simplices BP5 fix 2026-03-20 16:39:12 -07:00
camierjs d81bbc1214 BP5 hex/tet 2026-03-20 16:34:25 -07:00
camierjs 41255f308e Fix Benchmark internal namespace 2026-03-20 14:44:22 -07:00
camierjs 96be0cafdf Add benchmark tests with simplices 2026-03-20 08:30:23 -07:00
camierjs 55fc2f806c Merge branch 'master' into jdongg/pa-simplices 2026-03-20 05:59:14 -07:00
Anne 25a26b5e6f fix variable names shadowing members 2026-03-19 12:29:06 +01:00
Anne d6aacb95cf fix typo in documentation 2026-03-19 12:07:53 +01:00
Anne 0466624b21 apply astyle format 2026-03-19 12:03:54 +01:00
Anne 42e2d0b9a4 Add Lp error implementation to ComplexGridFunction using the modulus of complex values. Add interface ComputeMaxError for special case p = infinity. 2026-03-18 11:17:08 +01:00
Ketan Mittal 08bf7f991b Merge branch 'master' into lorentz-device 2026-03-17 19:01:33 -07:00
Ketan Mittal 0503cbd41c Merge branch 'master' into lorentz-device 2026-03-11 10:57:58 -07:00
Ketan Mittal 74d9671ec8 Merge branch 'master' into lorentz-device 2026-03-07 16:14:45 -08:00
Ketan Mittal 9917fa8306 Merge branch 'master' into tmop-mesh-validity 2026-03-06 10:12:51 -08:00
Mittal, Ketan 74f64934e8 Merge branch 'master' of https://github.com/mfem/mfem into lorentz-device 2026-03-05 11:10:05 -08:00
Mittal, Ketan 8ff5affe45 Merge branch 'lorentz-device' of https://github.com/mfem/mfem into lorentz-device 2026-03-05 11:09:59 -08:00
Mittal, Ketan 2e7a6d745c cosmetic 2026-03-05 11:09:50 -08:00
camierjs eb95b46fad Missing Stroud flags 2026-03-05 09:42:14 -08:00
camierjs a0778c759e StroudFlag to bool 2026-03-05 09:04:05 -08:00
camierjs a922c1f2c3 Merge remote-tracking branch 'origin/catch-tests' into jdongg/pa-simplices 2026-03-05 08:31:35 -08:00
camierjs 17600f59f2 make style 2026-03-05 07:02:26 -08:00
camierjs 6cfec88f47 Merge branch 'master' into jdongg/pa-simplices 2026-03-05 07:01:30 -08:00
jdongg f11009d674 Switch 'or' to || for windows tests 2026-03-05 02:02:00 -08:00
jdongg 5df384f6ef Remove unused variables 2026-03-05 01:33:54 -08:00
jdongg daa555b68d Change pullback of Stroud rule to reference cube in AssemblePA routines to avoid assigning pointer to integration rule to a const integration rule 2026-03-05 00:59:36 -08:00
jdongg 76cfafe700 Switch 2D mass and diffusion kernels on triangles to stack memory 2026-03-04 18:51:54 -08:00
Socratis Petrides 58976c6f41 add relaxation in the smoother to fix non-PD issue of the preconditioner 2026-03-03 22:18:50 -08:00
Chris Vogl aa4f1bc8e4 Removed conditional and combined setting of initial GSLIB flags (from @nmnobre) 2026-02-27 12:03:06 -08:00
Chris VoglandNuno Nobre fcc4b2dade Switch to using OPTFLAGS for METIS fetching (from @nmnobre)
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2026-02-26 15:31:16 -08:00
Chris VoglandNuno Nobre 3c137f36cb Improved consistency across TPL fetching (from @nmnobre)
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2026-02-26 15:27:10 -08:00
Chris VoglandNuno Nobre 9211b97eb0 Fixed typo in GSLIB_FLAGS name (from @nmnobre)
Co-authored-by: Nuno Nobre <nuno.nobre@stfc.ac.uk>
2026-02-26 15:21:57 -08:00
Ketan Mittal 9cb3c1e1a6 Merge branch 'master' into lorentz-device 2026-02-26 13:02:01 -08:00
Veselin Dobrev 1ed3b48c2e In class MultiVector, remove the need for Memory flag synchronizations
in some cases. This required changes in the internals of the class.

Added some new methods in class MultiVector.
2026-02-26 09:57:21 -08:00
Veselin Dobrev fbd9189e7b Restrist with 'enable_if' the variadic template MultiVector ctor and
MakeRef method to be considered only when the arg types are convertible
to (Vector &).
2026-02-25 19:17:07 -08:00
Veselin Dobrev 1dd889cb16 Add support for constructing and re-constructing MultiVectors to reference
multiple Vectors given as arguments.
2026-02-25 17:44:31 -08:00
Veselin Dobrev 2e8fbd661a Fix a warning in a miniapp. 2026-02-25 14:56:28 -08:00
Veselin Dobrev 6e424dba6e Draft implementation of an array-of-Vectors class where each Vector generally
has a different size and is allocated independently.

The tentative name for the new class is MultiVector.

In class Operator, added new virtual methods Mult() and GetGradient() that
use MultiVectors.
2026-02-25 13:51:43 -08:00
Mittal, Ketan 0f449eb906 Merge branch 'lorentz-device' of https://github.com/mfem/mfem into lorentz-device 2026-02-23 15:53:08 -08:00
Mittal, Ketan cdca79060b print when data is on device 2026-02-23 15:52:56 -08:00
camierjs 0114739e5e Fix more warnings 2026-02-21 18:14:23 -08:00
camierjs 708b642be2 Merge branch 'master' into jdongg/pa-simplices 2026-02-21 13:00:45 -08:00
camierjs d78357494d Fix some other warnings 2026-02-21 11:59:33 -08:00
camierjs 9398b1a6e0 Fix warnings 2026-02-21 11:52:23 -08:00
camierjs b23a087f42 Test for fec in IsBernsteinSimplexSpace 2026-02-21 11:27:58 -08:00
Ketan Mittal 3505a5a354 Merge branch 'master' into lorentz-device 2026-02-20 09:31:38 -08:00
Ketan Mittal 72203ebb5c Merge branch 'master' into tmop-mesh-validity 2026-02-17 14:24:27 -08:00
Mittal, Ketan d62ffe149a style 2026-02-17 08:49:04 -08:00
Tzanio Kolev 86b2ad3a61 Merge branch 'master' into jdongg/pa-simplices 2026-02-17 08:29:10 -08:00
Socratis Petrides 35ba876ecf CI fix 2026-02-14 15:15:31 -08:00
Socratis Petrides 2886c4b211 pass ess_bdr_marker in pmg 2026-02-13 17:11:10 -08:00
Socratis Petrides 9455ed83b2 add ess_tdof treatement in pmg 2026-02-13 17:10:44 -08:00
Mittal, Ketan cc46bfdfcc Merge branch 'tmop-mesh-validity' of https://github.com/mfem/mfem into tmop-mesh-validity 2026-02-13 13:03:04 -08:00
Mittal, Ketan 010e96b6f0 remove shadow variable 2026-02-13 13:02:52 -08:00
Ketan Mittal edf551c40a Merge branch 'master' into tmop-mesh-validity 2026-02-13 09:47:32 -08:00
Mittal, Ketan fd36e1b177 Merge branch 'findpts-device-data-movement' of https://github.com/mfem/mfem into lorentz-device 2026-02-11 09:41:08 -08:00
Mittal, Ketan d390f9a7d1 remove timers 2026-02-11 09:41:02 -08:00
Mittal, Ketan 128f650dd3 documentation 2026-02-11 08:52:31 -08:00
Mittal, Ketan 2bbb317831 minor 2026-02-10 16:35:22 -08:00
Mittal, Ketan 36835e62e0 remove unused variable 2026-02-10 16:25:01 -08:00
Mittal, Ketan 70867ab87b minor 2026-02-10 16:04:18 -08:00
Mittal, Ketan 4b59c90b08 merge and resolve conflicts 2026-02-10 15:56:43 -08:00
Ketan Mittal b538e9d34c Merge branch 'master' into tmop-mesh-validity 2026-02-10 15:48:42 -08:00
Mittal, Ketan 79733572d7 initial commit 2026-02-10 09:23:02 -08:00
Mittal, Ketan 6b5d1e55de Merge branch 'findpts-device-data-movement' of https://github.com/mfem/mfem into lorentz-device 2026-02-10 09:19:07 -08:00
Mittal, Ketan 6b5eb2f92f minor 2026-02-10 09:17:06 -08:00
Mittal, Ketan 49c346233e minor clean up 2026-02-10 09:13:57 -08:00
Socratis Petrides 505661eb5f minor 2026-02-09 19:25:36 -08:00
Mittal, Ketan e81352715e Merge branch 'findpts-device-data-movement' of https://github.com/mfem/mfem 2026-02-09 13:32:17 -08:00
Ketan Mittal 159f1873d6 Merge branch 'master' into particle-device 2026-02-09 13:26:35 -08:00
Mittal, Ketan fa07a503dd minor 2026-02-09 13:26:08 -08:00
Mittal, Ketan d65fcc5d8c minor comment and split line 2026-02-09 09:56:14 -08:00
Mittal, Ketan 1696197f54 merge with master 2026-02-09 09:41:52 -08:00
Mittal, Ketan 19089ac132 merge on host instead of device 2026-02-09 09:37:31 -08:00
camierjs 5e9d32a0f7 Fix CMake PA simplices test, add visualization 2026-02-07 14:11:09 -08:00
camierjs 724866141a make style, remove unused-variable & shadows 2026-02-07 13:54:12 -08:00
John Camier f17c9eef12 Merge branch 'master' into jdongg/pa-simplices 2026-02-07 13:29:43 -08:00
Socratis Petrides 69c1d0d15b changelog 2026-02-07 12:52:47 -08:00
Socratis Petrides 5f7d138aef style 2026-02-06 18:18:19 -08:00
Tzanio Kolev 4659602e63 Merge branch 'master' into jdongg/pa-simplices 2026-02-06 07:11:45 -08:00
jdongg 1771a26426 Updated new function documentation and CHANGELOG 2026-02-05 15:04:39 -08:00
jdongg b42a383201 Modified Stroud rules to output nodes in the simplex, with InverseDuffyTrans routine added to pull back to reference cube when necessary 2026-02-05 13:23:18 -08:00
Socratis Petrides deae7e4997 minor 2026-02-05 10:33:10 -08:00
psocratis 6142015168 refactoring 2026-02-04 22:46:27 -08:00
psocratis 305a7f1e02 added pmg and cpmg options to dpg miniapps 2026-02-04 18:40:51 -08:00
psocratis 0bff07026c additional pmg and complex pmg options added including coarse solve option 2026-02-04 18:40:17 -08:00
psocratis 32c3e5dfd8 add complexoperator-->complexhypre 2026-02-04 18:39:05 -08:00
psocratis ee78679277 add blockoperator-->monolythic 2026-02-04 18:38:08 -08:00
jdongg 185f8b099d Small bug fixes to 2D mass and diffusion simplex kernels 2026-02-04 16:48:07 -08:00
psocratis 89a0f18b30 small leak 2026-02-04 11:14:18 -08:00
Socratis Petrides 4cbc345ba6 fix makefile and cmakelists.txt 2026-02-03 21:58:34 -08:00
Socratis Petrides abe3843712 typo 2026-02-03 21:47:04 -08:00
Socratis Petrides 3c2d1e4814 shadow var fix 2026-02-03 21:11:13 -08:00
Socratis Petrides a8c4ed3c79 expand p-MG to the complex case 2026-02-03 18:51:04 -08:00
Socratis Petrides 0aa73fa285 simplify precond construction in pmax 2026-02-03 15:12:01 -08:00
Socratis Petrides f1652b5ba0 add convenient functions for solver construction 2026-02-03 12:35:58 -08:00
Socratis Petrides fed0baf6b6 fix leak in complex case 2026-02-03 12:34:23 -08:00
Socratis Petrides 49a8ee5b54 fix possible leak in case of static_cond and add method to retrun trace_fes 2026-02-03 12:32:40 -08:00
Socratis Petrides c9569a0629 shadow var fix 2026-02-02 23:50:36 -08:00
Socratis Petrides a4e83b5836 p-mg cleanup 2026-02-02 22:30:02 -08:00
Socratis Petrides 8b285047bd some helper function for constructor order of fecols + Clone impl 2026-02-02 22:29:34 -08:00
psocratis f999be0372 valgrind fixes for p-mg 2026-02-01 18:12:32 -08:00
psocratis e639cfd75b style 2026-02-01 15:49:29 -08:00
psocratis 2753692303 Merge branch 'master' into trace-p-ref 2026-02-01 15:42:07 -08:00
psocratis 2722e979fc fix mg-transpose issue 2026-02-01 15:41:26 -08:00
psocratis 144c0ce106 CI 2026-02-01 14:49:58 -08:00
psocratis b64172e12e remove no longer needed code 2026-02-01 13:27:06 -08:00
psocratis c0072fcb84 fix windows CI issue 2026-02-01 13:02:18 -08:00
psocratis 37cd34a0e1 fix shadow var 2026-01-30 22:50:44 -08:00
psocratis 005be84af4 valgrind fixes 2026-01-30 22:44:48 -08:00
Socratis Petrides 0ef682c156 fixing dim for AMSvsADS in smoother 2026-01-30 17:03:26 -08:00
Socratis Petrides 3faa838774 expanding unit tests to serial/parallel true transfer 2026-01-30 17:02:53 -08:00
Socratis Petrides f10c2793c9 adding NC case in serial 2026-01-30 17:02:18 -08:00
Socratis Petrides 9f4b83362a p-ref MG for dpg diffusion 2026-01-28 00:02:13 -08:00
Socratis Petrides 8ef423ca65 traces p-ref example for p-diffusion 2026-01-28 00:01:00 -08:00
Socratis Petrides aff5173656 fix CI 2026-01-26 22:45:47 -08:00
Socratis Petrides 2164f07f01 starte p-mg for dpg-diffusion 2026-01-26 22:08:38 -08:00
Mittal, Ketan 2b3657c76b Merge branch 'master' of https://github.com/mfem/mfem into particle-device 2026-01-26 09:32:18 -08:00
Mittal, Ketan 63d45eb194 initial commit 2026-01-26 09:32:05 -08:00
Socratis Petrides fc0037da31 minor 2026-01-25 22:06:19 -08:00
Socratis Petrides 795a1a29a3 minor print fix 2026-01-25 00:11:44 -08:00
Socratis Petrides 476d9c5111 fix serial case 2026-01-25 00:06:18 -08:00
Socratis Petrides 64625f7333 tests for parallel trace/assemble prefoperator 2026-01-24 18:12:49 -08:00
Socratis Petrides 4ae8c1bc2b started on p-multigrid in serial 2026-01-24 18:12:11 -08:00
Socratis Petrides 4ae97f1834 hpp file changes for pref assembly 2026-01-24 18:11:37 -08:00
Socratis Petrides 816e8e4bea add option to get the preftransfer operator in parallel as a hypreparmatrix 2026-01-24 18:11:12 -08:00
Socratis Petrides c14d630f14 tet ND case fixed for assembled Transfer P 2026-01-24 00:03:09 -08:00
Socratis Petrides 899433f79f added PrerinementTransfer SparseMatrix. Still need to address the ND tet case wrt to doftrans 2026-01-23 18:54:42 -08:00
Ketan Mittal ff9294e5b0 Merge branch 'master' into tmop-mesh-validity 2026-01-23 15:41:31 -08:00
Socratis Petrides 16db883427 style 2026-01-22 22:11:46 -08:00
Socratis Petrides 1834268091 complete pref-tests 2026-01-22 22:11:25 -08:00
Socratis Petrides 947e25f769 clean up of ProjectTrace methods 2026-01-22 22:10:59 -08:00
Socratis Petrides 9fbe90527a fix trace pref prolongation 2026-01-22 22:09:35 -08:00
jdongg 076f1450e5 Cleanup junk files 2026-01-22 00:46:41 -08:00
Socratis Petrides bfa672c644 error computation 2026-01-22 00:24:33 -08:00
Justin Dong 30ac5d8d38 Fixed style issues 2026-01-21 20:35:36 -08:00
jdongg 5a2b05cc35 Remove unused variables, cleanup after rebasing. 2026-01-21 20:00:49 -08:00
Socratis Petrides afe388a573 style 2026-01-21 19:35:22 -08:00
Socratis Petrides 50a9cce9f4 first tests on project 2026-01-21 19:35:03 -08:00
Socratis Petrides 8ee2fdbfd9 Project coeff for trace/skeleton 2026-01-21 19:34:40 -08:00
Socratis Petrides d6b4594150 Pref mat-free for trace space 2026-01-21 19:34:06 -08:00
jdongg 771e263db8 Rebase onto main 2026-01-21 18:10:45 -08:00
jdongg ee13fec158 Added shared memory implementation of diffusion and mass integrators with partial assembly 2026-01-21 17:43:41 -08:00
Justin Dong 0162a6727d Optimized ragged tensor nested for loops by collapsing to 1d loops with appropriate forward and inverse maps to recover nested indices 2026-01-21 17:36:37 -08:00
Justin Dong 31fae9d195 Adding support for partial assembly of tetrahedrons for mass and diffusion integrators with Bernstein basis 2026-01-21 17:34:25 -08:00
Justin Dong 6ce5fe8fac Initial implementation of partial assembly on triangles for Bernstein basis
Rebase onto master
2026-01-21 17:33:07 -08:00
jdongg 1739cebfdb Added shared memory implementation of diffusion and mass integrators with partial assembly 2026-01-21 17:03:17 -08:00
Mittal, Ketan 9e5b93a532 initial commit with working prototype 2026-01-15 18:30:23 -08:00
Mittal, Ketan a7ca72c59f update get jacobian function 2025-12-22 12:24:15 -08:00
Mittal, Ketan 8267c7ea64 Merge branch 'master' of https://github.com/mfem/mfem into tmop-mesh-validity 2025-12-22 12:18:20 -08:00
Mittal, Ketan d2cf8fef73 remove unneeded function 2025-12-22 12:18:17 -08:00
Mittal, Ketan 2df3fc8ddb remove unneeded function 2025-12-22 12:16:05 -08:00
Mittal, Ketan 59ee0d93c6 incorporate bounds from other branch 2025-12-14 16:57:34 -08:00
Mittal, Ketan d8164fd158 wip: check if function positive 2025-12-14 16:40:44 -08:00
Mittal, Ketan 23ce5f08e7 Merge branch 'plbound-extremum' of https://github.com/mfem/mfem into tmop-mesh-validity 2025-12-13 17:37:42 -08:00
Mittal, Ketan e059253549 Merge branch 'master' of https://github.com/mfem/mfem into tmop-mesh-validity 2025-12-13 17:35:13 -08:00
kmittal2 c89807da1e use in TMOP solver 2025-12-10 20:20:17 -08:00
kmittal2 53de3bde2c Merge branch 'master' of https://github.com/mfem/mfem into tmop-mesh-validity 2025-12-06 18:08:33 -08:00
kmittal2 56414350e2 initial commit - function to extract Jacobian determinant gridfunction in mesh/pmesh 2025-12-06 18:07:57 -08:00
Chris Vogl e5111df7c6 rename fetched GSLIB option variable to be consistent with fetched METIS 2025-11-24 16:20:53 -08:00
Chris Vogl 4fe80816d2 added fPIC flag option for fetched METIS, also includes optimization flags as done with fetched GSLIB 2025-11-24 16:20:16 -08:00
Chris Vogl 0839f915b4 added position independent code option for fetched hypre 2025-11-24 16:19:10 -08:00
Chris Vogl 23b4a5a5b8 style cleanup of GSLIB fetching to match other fetching code 2025-11-24 16:17:42 -08:00
Justin Dong 41d15a3a0a Optimized ragged tensor nested for loops by collapsing to 1d loops with appropriate forward and inverse maps to recover nested indices 2025-07-22 12:13:15 -07:00
Justin Dong daca70bf80 Adding support for partial assembly of tetrahedrons for mass and diffusion integrators with Bernstein basis 2025-07-02 11:28:39 -07:00
Justin Dong 0e02aa947a Initial implementation of partial assembly on triangles for Bernstein basis 2025-06-30 21:22:25 -07:00
154 changed files with 14416 additions and 3319 deletions
@@ -94,6 +94,16 @@ inputs:
description: If true, do not set any CXXFLAGS or LDFLAGS.
default: false
# Unfortunately, "uses:" fields cannot have references to variables like
# ${{env.MFEM_ACTIONS_VERSION}}, so the branch/tag name has to be hard coded.
# Therefore, in the future, when updating the version of the
# mfem/github-actions to use, we'll have to replace:
# - all definitions of MFEM_ACTIONS_VERSION and
# - all "uses:" fields that refer to mfem/github-actions.
MFEM_ACTIONS_VERSION:
description: Version (branch or tag) of the mfem/github-actions to use.
default: v2.7
runs:
using: 'composite'
steps:
@@ -118,6 +128,7 @@ runs:
echo UBSAN_LDFLAGS=${{inputs.UBSAN_LDFLAGS}} >> $GITHUB_ENV
echo MSAN_CXXFLAGS=${{inputs.MSAN_CXXFLAGS}} >> $GITHUB_ENV
echo MSAN_LDFLAGS=${{inputs.MSAN_LDFLAGS}} >> $GITHUB_ENV
echo MFEM_ACTIONS_VERSION=${{inputs.MFEM_ACTIONS_VERSION}} >> $GITHUB_ENV
shell: bash
- name: Env (dir)
+1 -1
View File
@@ -53,7 +53,7 @@ runs:
run: echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.UBSAN_CXXFLAGS}} >> $GITHUB_ENV
shell: bash
- uses: mfem/github-actions/build-mfem@v2.6
- uses: mfem/github-actions/build-mfem@v2.7
if: ${{steps.debug.outputs.cache-hit != 'true'}}
env:
CXXFLAGS: ${{env.CXXFLAGS}}
+6
View File
@@ -12,6 +12,11 @@
name: 'Install MPI'
description: 'Installs MPI and set up its environment variables'
inputs:
NO_FLAGS:
description: If true, do not set any CXXFLAGS or LDFLAGS.
default: false
runs:
using: 'composite'
steps:
@@ -27,6 +32,7 @@ runs:
shell: bash
- name: Env (bis)
if: ${{ inputs.NO_FLAGS != 'true' }}
run: |
echo CXXFLAGS=${{env.CXXFLAGS}} ${{env.MPI_INC}} >> $GITHUB_ENV
echo LDFLAGS=${{env.LDFLAGS}} ${{env.MPI_LIB}} >> $GITHUB_ENV
+2 -2
View File
@@ -37,14 +37,14 @@ runs:
with:
path: ${{env.HYPRE_DIR}}
fail-on-cache-miss: true
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-${{env.MFEM_ACTIONS_VERSION}}
- uses: actions/cache/restore@v5 # Cache for Metis
if: ${{inputs.par == 'true'}}
with:
path: ${{env.METIS_DIR}}
fail-on-cache-miss: true
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
key: ${{runner.os}}-build-${{env.METIS_DIR}}-${{env.MFEM_ACTIONS_VERSION}}
- name: Hypre/Metis links
if: ${{inputs.par == 'true'}}
+2 -6
View File
@@ -29,16 +29,12 @@ Runs a number of static repository-level sanity checks.
- `branch-history` guards against accidental commits of large files using the `--history` option of the `config/githooks/pre-push` script.
## `mfem-analysis.yml` (`build-analysis`)
Checks if the code builds and satisfies minimal requirements.
- `gitignore` builds hypre, METIS, and MFEM using `mfem/github-actions/build-hypre`, `mfem/github-actions/build-metis`, and `mfem/github-actions/build-mfem` and checks for correct `.gitignore` settings by running the `tests/scripts/gitignore` script.
## `builds-and-tests.yml`
Runs a matrix of builds and tests runs with different compilers, OS, mfem/hypre settings, etc. Also processes and upload Codecov reports.
One matrix job runs `tests/scripts/gitignore` after `make test-noclean` to check generated artifacts against `.gitignore`.
Uses the following GitHub Actions from <https://github.com/mfem/github-actions>:
- `mfem/github-actions/build-hypre`
+22 -22
View File
@@ -40,6 +40,7 @@ env:
METIS_ARCHIVE_MAC: metis-4.0.3-mac.tgz
METIS_TOP_DIR: metis-4.0.3
MFEM_TOP_DIR: mfem
MFEM_ACTIONS_VERSION: v2.7
# Note for future improvements:
#
@@ -110,6 +111,7 @@ jobs:
build-system: make
hypre-target: int64
precision: fp64
gitignore-check: YES
- os: ubuntu-latest
target: opt
codecov: NO
@@ -170,20 +172,6 @@ jobs:
env
shell: bash
# For info on Xcode see:
# - https://github.com/actions/runner-images/issues/12541
# - https://github.com/actions/runner-images/blob/releases/macos-15-arm64/20250811/images/macos/macos-15-arm64-Readme.md#xcode
- name: Xcode version setup (MacOS)
if: matrix.os == 'macos-latest'
run: |
XCODE_PATH="/Applications/Xcode_16.4.app"
echo "> sudo xcode-select -s ${XCODE_PATH}"
sudo xcode-select -s ${XCODE_PATH}
echo "> g++ -v"
g++ -v
echo "> clang++ -v"
clang++ -v
# Only get MPI if defined for the job.
# TODO: It would be nice to have only one step, e.g. with a dedicated
# action, but I (@adrienbernede) don't see how at the moment.
@@ -228,11 +216,11 @@ jobs:
uses: actions/cache@v5
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-${{ env.MFEM_ACTIONS_VERSION }}
- name: get hypre
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
uses: mfem/github-actions/build-hypre@v2.6
uses: mfem/github-actions/build-hypre@v2.7
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
@@ -242,7 +230,7 @@ jobs:
- name: get hypre (Windows)
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os == 'windows-latest'
uses: mfem/github-actions/build-hypre@v2.6
uses: mfem/github-actions/build-hypre@v2.7
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
@@ -258,11 +246,11 @@ jobs:
uses: actions/cache@v5
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-${{ env.MFEM_ACTIONS_VERSION }}
- name: install metis
if: matrix.mpi == 'par' && matrix.os != 'windows-latest' && steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.6
uses: mfem/github-actions/build-metis@v2.7
with:
archive: ${{ matrix.os != 'macos-latest' && env.METIS_ARCHIVE || env.METIS_ARCHIVE_MAC }}
dir: ${{ env.METIS_TOP_DIR }}
@@ -304,7 +292,7 @@ jobs:
# MFEM build and test
- name: build
uses: mfem/github-actions/build-mfem@v2.6
uses: mfem/github-actions/build-mfem@v2.7
env:
VCPKG_DEFAULT_BINARY_CACHE: ${{ github.workspace }}/vcpkg_cache
with:
@@ -330,7 +318,13 @@ jobs:
- name: tests
if: matrix.build-system == 'make' && (matrix.target == 'opt' || matrix.os == 'ubuntu-latest')
run: |
cd ${{ env.MFEM_TOP_DIR }} && make test
cd ${{ env.MFEM_TOP_DIR }}
if [[ "${{ matrix.gitignore-check }}" == "YES" ]]; then
make test-noclean
else
make test
fi
shell: bash
- name: cmake checks
if: matrix.build-system == 'cmake' && matrix.target == 'dbg'
@@ -375,10 +369,16 @@ jobs:
# Code coverage (process and upload reports)
- name: codecov
if: matrix.codecov == 'YES'
uses: mfem/github-actions/upload-coverage@v2.6
uses: mfem/github-actions/upload-coverage@v2.7
with:
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
project_dir: ${{ env.MFEM_TOP_DIR }}
directories: "fem general linalg mesh"
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
- name: gitignore
if: matrix.gitignore-check == 'YES'
run: |
cd ${{ env.MFEM_TOP_DIR }}/tests/scripts
./runtest gitignore
+10
View File
@@ -14,9 +14,19 @@ name: "Static Analysis"
on:
push:
branches: ["master", "next"]
paths-ignore: &docs-only-paths
- "**/*.md"
- "doc/**"
- ".binder/**"
- "CITATION.cff"
- "LICENSE"
- "NOTICE"
- "CHANGELOG"
- "INSTALL"
pull_request:
# The branches below must be a subset of the branches above
branches: ["master"]
paths-ignore: *docs-only-paths
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
-100
View File
@@ -1,100 +0,0 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
name: "Build Analysis"
permissions:
actions: write
on:
push:
branches:
- master
- next
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
HYPRE_ARCHIVE: v2.19.0.tar.gz
HYPRE_TOP_DIR: hypre-2.19.0
METIS_ARCHIVE: metis-4.0.3.tar.gz
METIS_TOP_DIR: metis-4.0.3
COVERAGE_ENV: mfem-coverage
jobs:
gitignore:
runs-on: ubuntu-latest
steps:
- name: checkout MFEM
uses: actions/checkout@v6
with:
path: mfem
- name: Get MPI (Linux)
run: |
sudo apt-get install openmpi-bin libopenmpi-dev
export OMPI_MCA_rmaps_base_oversubscribe=1
- name: Cache Hypre Install
id: hypre-cache
uses: actions/cache@v5
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
- name: Get Hypre
if: steps.hypre-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.6
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
target: int32
- name: Cache Metis Install
id: metis-cache
uses: actions/cache@v5
with:
path: ${{ env.METIS_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.METIS_TOP_DIR }}-v2.5
- name: Install Metis
if: steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.6
with:
archive: ${{ env.METIS_ARCHIVE }}
dir: ${{ env.METIS_TOP_DIR }}
# MFEM build and test
- name: build-mfem
uses: mfem/github-actions/build-mfem@v2.6
with:
os: ${{ runner.os }}
target: opt
codecov: NO
mpi: par
build-system: make
hypre-dir: ${{ env.HYPRE_TOP_DIR }}
metis-dir: ${{ env.METIS_TOP_DIR }}
mfem-dir: mfem
- name: test (no clean)
run: |
cd mfem && make test-noclean
- name: gitignore
run: |
cd mfem/tests/scripts
./runtest gitignore
+6 -2
View File
@@ -19,18 +19,22 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
with:
NO_FLAGS: true
- name: Cache
id: cache
uses: actions/cache@v5
with:
path: ${{env.HYPRE_DIR}}
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-v2.5
key: ${{runner.os}}-ompi-build-${{env.HYPRE_DIR}}-int32-fp64-${{ env.MFEM_ACTIONS_VERSION }}
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
uses: ./.github/actions/sanitize/mpi
with:
NO_FLAGS: true
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.6
uses: mfem/github-actions/build-hypre@v2.7
with:
archive: ${{env.HYPRE_TGZ}}
dir: ${{env.HYPRE_DIR}}
+6 -2
View File
@@ -19,18 +19,22 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: ./.github/actions/sanitize/config
with:
NO_FLAGS: true
- name: Cache
id: cache
uses: actions/cache@v5
with:
path: ${{env.METIS_DIR}}
key: ${{runner.os}}-build-${{env.METIS_DIR}}-v2.5
key: ${{runner.os}}-build-${{env.METIS_DIR}}-${{env.MFEM_ACTIONS_VERSION}}
- name: Setup
if: steps.cache.outputs.cache-hit != 'true'
uses: ./.github/actions/sanitize/mpi
with:
NO_FLAGS: true
- name: Build
if: steps.cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.6
uses: mfem/github-actions/build-metis@v2.7
with:
archive: ${{env.METIS_TGZ}}
dir: ${{env.METIS_DIR}}
+10
View File
@@ -17,7 +17,17 @@ permissions:
on:
push:
branches: ["master", "next"]
paths-ignore: &docs-only-paths
- "**/*.md"
- "doc/**"
- ".binder/**"
- "CITATION.cff"
- "LICENSE"
- "NOTICE"
- "CHANGELOG"
- "INSTALL"
pull_request:
paths-ignore: *docs-only-paths
workflow_dispatch:
concurrency:
+57 -30
View File
@@ -11,35 +11,49 @@
Version 4.9.1 (development)
===========================
- Policy for AI-assisted contribution added to CONTRIBUTING.md
- Added policy for AI-assisted contribution to CONTRIBUTING.md.
Discretization improvements
---------------------------
- Added NVIDIA cuDSS library interface. Implementation examples have been
added to ex1 and ex1p. See https://developer.nvidia.com/cudss for more
details. Supported versions >= 0.6.0.
- Improved FindPointsGSLIB surface mesh capability with support for simplices
and an option to specify axis-aligned bounding box padding for near-surface
point queries.
- Added GPU-enabled partial assembly for simplicial Bernstein H1 basis based on
ragged tensor algorithms (see DOI: 10.1137/11082539X) for mass and diffusion
integrators.
- Replaced legacy simplex quadrature rules with symmetric positive weight rules
for triangles (orders 0-25) and tetrahedra (orders 0-20). These rules
guarantee all-positive weights and interior quadrature points, improving
numerical stability. Higher orders fall back to Grundmann-Moller.
* Triangle rules: Witherden and Vincent, DOI: 10.1016/j.camwa.2015.03.017
* Tet rules (d=1-13): Witherden and Vincent (same as above)
* Tet rules (d=14-20): Chuluunbaatar et al., DOI: 10.1016/j.camwa.2022.08.016
- Added support for general 1D Gauss-Jacobi quadrature rules and Stroud conical
quadrature rules on triangles and tetrahedra.
- Improved the GridFunction projection routines. Projections work for Scalar,
Vector and VectorFE, also NURBS versions. Optionally different types of
projections can be selected, default behavior has not changed.
- Added GridFunction projection methods for trace spaces, i.e., project
coefficients on the mesh skeleton.
- Added methods to estimate function extremum using piecewise linear bounds plus
recursive subdivision.
- Extend FindPointsGSLIB to support surface meshes.
- Replaced legacy simplex quadrature rules with symmetric positive-weight
rules for triangles (orders 0-25) and tetrahedra (orders 0-20). These
rules guarantee all-positive weights and interior quadrature points,
improving numerical stability. Higher orders fall back to Grundmann-Moller.
Triangle rules: Witherden & Vincent, Comput. Math. Appl. 69(10):1232-1241,
2015.
Tet rules (d=1-13): Witherden & Vincent (ibid).
Tet rules (d=14-20): Chuluunbaatar et al., Comput. Math. Appl. 124:89-97,
2022.
- Improved the gridfunction projection routines. Projections work for Scalar,
Vector and VectorFE, also NURBS versions. Optionally different types of
projections can be selected, default behaviour has not changed.
- Added methods to estimate function extremum using piecewise linear bounds +
recursive subdivision.
Meshing improvements
--------------------
- Added option to guarantee mesh validity during TMOP-based r-adaptivity, using
bounds on the determinant of the mesh transformation Jacobian.
- Added PA support for TMOP's adaptive limiting functionality. Multiple
GridFunctions and Coefficients can be combined to form a composite term.
- Improved support for 1D NURBS meshes with variable order, including using
the patches construct for 1D NURBS meshes.
@@ -48,22 +62,35 @@ Meshing improvements
parallel visualization, e.g. with GLVis. This is supported by both the Print
and PrintAsOne methods of ParMesh. See ParMesh::SetPrintInterfaces().
New and updated examples and miniapps
-------------------------------------
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
capability.
Linear and nonlinear solvers
----------------------------
- Added support for trace spaces in PRefinementTransferOperator. This is used in
PRefinement multigrid methods for problems posed on trace spaces (see e.g. the
DPG miniapps).
GPU computing
-------------
- Added device assembly support for 3D H(curl) VectorFEDomainLFIntegrator.
- Added NVIDIA cuDSS library interface. Implementation examples have been
added to ex1 and ex1p. See https://developer.nvidia.com/cudss for more
details. Supported versions >= 0.6.0.
- Allow specifying GPU kernel launch bounds for native and RAJA GPU backends.
New and updated examples and miniapps
-------------------------------------
- The Lorentz miniapp (in miniapps/electromagnetics) has been updated to
leverage the ParticleSet capability.
- Added (Complex)PRefinementMultigrid solver option in the DPG miniapps.
Miscellaneous
-------------
- Fixed signed DOF handling in parallel grid-function reading (read constructor)
and saving via ParGridFunction::SaveAsOne(). Simplified the process of
applying the DOF signs by using the new method ApplyDofSigns() in class
ParFiniteElementSpace -- the method will return immediately if no sign flips
are needed.
- Fixed signed DOF handling in ParGridFunction reading (read constructor) and
saving via SaveAsOne(). Simplified the process of applying the DOF signs by
using the new method ApplyDofSigns() in class ParFiniteElementSpace: the
method will return immediately if no sign flips are needed.
Version 4.9, released on Dec 11, 2025
-1
View File
@@ -6,7 +6,6 @@
<a href="https://github.com/mfem/mfem/blob/master/LICENSE"><img alt="License" src="https://img.shields.io/badge/License-BSD-blue.svg"></a>
<a href="https://github.com/mfem/mfem/releases/latest"><img alt="GitHub release" src="https://img.shields.io/github/v/release/mfem/mfem"></a>
<a href="https://github.com/mfem/mfem/actions/workflows/repo-check.yml?query=branch%3Amaster"><img alt="Repo check" src="https://github.com/mfem/mfem/actions/workflows/repo-check.yml/badge.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml?query=branch%3Amaster"><img alt="Build Analysis" src="https://github.com/mfem/mfem/actions/workflows/mfem-analysis.yml/badge.svg?branch=master"></a>
<a href="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml?query=branch%3Amaster"><img alt="Builds and Tests" src="https://github.com/mfem/mfem/actions/workflows/builds-and-tests.yml/badge.svg?branch=master"></a>
<a href="https://ci.appveyor.com/project/mfem/mfem"><img alt="Build Status" src="https://ci.appveyor.com/api/projects/status/19non9sqm6msi2wy?svg=true"></a>
<a href="https://docs.mfem.org/html/index.html"><img alt="Documentation" src="https://img.shields.io/badge/code-documented-brightgreen.svg"></a>
+8 -10
View File
@@ -18,19 +18,17 @@
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)
# set options (technically flags because GSLIB does not use cmake)
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
set(GSLIB_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
if (BUILD_SHARED_LIBS)
set(GSLIB_FLAGS "${GSLIB_FLAGS} -fPIC")
endif()
# 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}")
message(STATUS "Will fetch GSLIB ${GSLIB_FETCH_VERSION} to be built with ${GSLIB_FLAGS}")
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/gslib)
include(ExternalProject)
ExternalProject_Add(gslib
@@ -40,7 +38,7 @@ if (MFEM_FETCH_GSLIB OR MFEM_FETCH_TPLS)
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}"
BUILD_COMMAND cd ${PREFIX}/src/gslib && $(MAKE) clean && $(MAKE) DESTDIR=${PREFIX} MPI=$<BOOL:${MFEM_USE_MPI}> "CFLAGS=${GSLIB_FLAGS}"
INSTALL_COMMAND "")
file(MAKE_DIRECTORY ${PREFIX}/include)
# set imported library target properties
+3 -1
View File
@@ -44,6 +44,9 @@ if (MFEM_FETCH_HYPRE OR MFEM_FETCH_TPLS)
# set options and associated dependencies
set(HYPRE_CMAKE_OPTIONS "")
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
if (BUILD_SHARED_LIBS)
list(APPEND HYPRE_CMAKE_OPTIONS -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=ON)
endif()
# collect all HYPRE_ENABLE variables and pass them to hypre, assuming they are BOOL.
get_cmake_property(all_vars VARIABLES)
foreach(var ${all_vars})
@@ -95,7 +98,6 @@ if (MFEM_FETCH_HYPRE OR MFEM_FETCH_TPLS)
UPDATE_DISCONNECTED TRUE
SOURCE_SUBDIR src
PREFIX ${HYPRE_INSTALL}
BUILD_COMMAND ${CMAKE_COMMAND} --build . -- -j${CMAKE_BUILD_PARALLEL_LEVEL}
CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=${HYPRE_INSTALL} -DCMAKE_INSTALL_LIBDIR:PATH=lib ${HYPRE_CMAKE_OPTIONS})
file(MAKE_DIRECTORY ${HYPRE_INSTALL}/include)
# set imported library target properties
+10 -2
View File
@@ -19,10 +19,18 @@
# - METIS_VERSION_5 (cache variable)
if (MFEM_FETCH_METIS OR MFEM_FETCH_TPLS)
enable_language(C)
set(METIS_FETCH_VERSION 4.0.3)
add_library(METIS STATIC IMPORTED)
# set options (technically flags because METIS does not use cmake)
set(METIS_FLAGS "-Wno-implicit-int -Wno-incompatible-pointer-types")
string(TOUPPER "${CMAKE_BUILD_TYPE}" BUILD_TYPE)
set(METIS_FLAGS "${METIS_FLAGS} ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BUILD_TYPE}}")
if (BUILD_SHARED_LIBS)
set(METIS_FLAGS "${METIS_FLAGS} -fPIC")
endif()
# define external project
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with default options")
message(STATUS "Will fetch METIS ${METIS_FETCH_VERSION} to be built with ${METIS_FLAGS}")
set(PREFIX ${CMAKE_BINARY_DIR}/fetch/metis)
include(ExternalProject)
ExternalProject_Add(metis
@@ -32,7 +40,7 @@ if (MFEM_FETCH_METIS OR MFEM_FETCH_TPLS)
UPDATE_DISCONNECTED TRUE
PREFIX ${PREFIX}
CONFIGURE_COMMAND tar -xzf ../metis/metis-${METIS_FETCH_VERSION}-mac.tgz --strip=1
BUILD_COMMAND $(MAKE) COPTIONS=-Wno-incompatible-pointer-types
BUILD_COMMAND $(MAKE) clean && $(MAKE) "OPTFLAGS=${METIS_FLAGS}"
INSTALL_COMMAND mkdir -p ${PREFIX}/lib && cp libmetis.a ${PREFIX}/lib/)
# set imported library target properties
add_dependencies(METIS metis)
+9 -9
View File
@@ -22,15 +22,15 @@ include(MfemCmakeUtilities)
mfem_find_package(SuiteSparse SuiteSparse SuiteSparse_DIR "" "" "" ""
"Paths to headers required by SuiteSparse."
"Libraries required by SuiteSparse."
ADD_COMPONENT "UMFPACK" "include;suitesparse" umfpack.h "lib" umfpack
ADD_COMPONENT "KLU" "include;suitesparse" klu.h "lib" klu
ADD_COMPONENT "AMD" "include;suitesparse" amd.h "lib" amd
ADD_COMPONENT "BTF" "include;suitesparse" btf.h "lib" btf
ADD_COMPONENT "CHOLMOD" "include;suitesparse" cholmod.h "lib" cholmod
ADD_COMPONENT "COLAMD" "include;suitesparse" colamd.h "lib" colamd
ADD_COMPONENT "CAMD" "include;suitesparse" camd.h "lib" camd
ADD_COMPONENT "CCOLAMD" "include;suitesparse" ccolamd.h "lib" ccolamd
ADD_COMPONENT "config" "include;suitesparse" SuiteSparse_config.h "lib"
ADD_COMPONENT "UMFPACK" "include;include/suitesparse;suitesparse" umfpack.h "lib" umfpack
ADD_COMPONENT "KLU" "include;include/suitesparse;suitesparse" klu.h "lib" klu
ADD_COMPONENT "AMD" "include;include/suitesparse;suitesparse" amd.h "lib" amd
ADD_COMPONENT "BTF" "include;include/suitesparse;suitesparse" btf.h "lib" btf
ADD_COMPONENT "CHOLMOD" "include;include/suitesparse;suitesparse" cholmod.h "lib" cholmod
ADD_COMPONENT "COLAMD" "include;include/suitesparse;suitesparse" colamd.h "lib" colamd
ADD_COMPONENT "CAMD" "include;include/suitesparse;suitesparse" camd.h "lib" camd
ADD_COMPONENT "CCOLAMD" "include;include/suitesparse;suitesparse" ccolamd.h "lib" ccolamd
ADD_COMPONENT "config" "include;include/suitesparse;suitesparse" SuiteSparse_config.h "lib"
suitesparseconfig)
if (SuiteSparse_FOUND AND METIS_VERSION_5)
+13 -12
View File
@@ -50,6 +50,10 @@
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cpu
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
//
// Device simplices sample runs:
// ex1 -pa -d gpu -m ../data/inline-tet.mesh
// ex1 -pa -d gpu -m ../data/inline-tri.mesh
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
@@ -138,25 +142,25 @@ int main(int argc, char *argv[])
}
// 5. Define a finite element space on the mesh. Here we use continuous
// Lagrange finite elements of the specified order. If order < 1, we
// instead use an isoparametric/isogeometric space.
// Lagrange finite elements of the specified order.
// - If order < 1, we instead use an isoparametric/isogeometric space.
// - If the mesh is simplicial and partial assembly is requested,
// we use the positive basis, which supports device execution.
FiniteElementCollection *fec;
bool delete_fec;
auto basis_type = (pa && mesh.IsSimplexMesh()) ?
BasisType::Positive : BasisType::GaussLobatto;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
delete_fec = true;
fec = new H1_FECollection(order, dim, basis_type);
}
else if (mesh.GetNodes())
{
fec = mesh.GetNodes()->OwnFEC();
delete_fec = false;
cout << "Using isoparametric FEs: " << fec->Name() << endl;
}
else
{
fec = new H1_FECollection(order = 1, dim);
delete_fec = true;
fec = new H1_FECollection(order = 1, dim, basis_type);
}
FiniteElementSpace fespace(&mesh, fec);
cout << "Number of finite element unknowns: "
@@ -292,10 +296,7 @@ int main(int argc, char *argv[])
}
// 15. Free the used memory.
if (delete_fec)
{
delete fec;
}
if (order > 0) { delete fec; }
return 0;
}
+14 -13
View File
@@ -42,7 +42,11 @@
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/square-mixed.mesh
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/fichera-mixed.mesh
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/beam-tet.mesh
//
// Device simplices sample runs:
// mpirun -np 4 ex1p -pa -d gpu -m ../data/inline-tet.mesh
// mpirun -np 4 ex1p -pa -d gpu -m ../data/inline-tri.mesh
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
@@ -165,19 +169,20 @@ int main(int argc, char *argv[])
}
// 7. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
// use continuous Lagrange finite elements of the specified order.
// - If order < 1, we instead use an isoparametric/isogeometric space.
// - If the mesh is simplicial and partial assembly is requested,
// we use the positive basis, which supports device execution.
FiniteElementCollection *fec;
bool delete_fec;
auto basis_type = (pa && pmesh.IsSimplexMesh()) ?
BasisType::Positive : BasisType::GaussLobatto;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
delete_fec = true;
fec = new H1_FECollection(order, dim, basis_type);
}
else if (pmesh.GetNodes())
{
fec = pmesh.GetNodes()->OwnFEC();
delete_fec = false;
if (myid == 0)
{
cout << "Using isoparametric FEs: " << fec->Name() << endl;
@@ -185,8 +190,7 @@ int main(int argc, char *argv[])
}
else
{
fec = new H1_FECollection(order = 1, dim);
delete_fec = true;
fec = new H1_FECollection(order = 1, dim, basis_type);
}
ParFiniteElementSpace fespace(&pmesh, fec);
HYPRE_BigInt size = fespace.GlobalTrueVSize();
@@ -333,10 +337,7 @@ int main(int argc, char *argv[])
}
// 17. Free the used memory.
if (delete_fec)
{
delete fec;
}
if (order > 0) { delete fec; }
return 0;
}
+4 -1
View File
@@ -133,7 +133,7 @@ set(SRCS
tmop/assemble/diag2.cpp
tmop/assemble/grad2_limit.cpp
tmop/assemble/grad2.cpp
tmop/assemble/diag3_limit.cpp
tmop/assemble/diag3_limit.cpp
tmop/assemble/diag3.cpp
tmop/assemble/grad3_limit.cpp
tmop/assemble/grad3.cpp
@@ -195,12 +195,14 @@ set(HDRS
integ/bilininteg_dgtrace_kernels.hpp
integ/bilininteg_vecdiffusion_kernels.hpp
integ/bilininteg_convection_kernels.hpp
integ/bilininteg_diffusion_pa_simplices.hpp
integ/bilininteg_diffusion_kernels.hpp
integ/bilininteg_elasticity_kernels.hpp
integ/bilininteg_hcurl_kernels.hpp
integ/bilininteg_hdiv_kernels.hpp
integ/bilininteg_hcurlhdiv_kernels.hpp
integ/bilininteg_mass_kernels.hpp
integ/bilininteg_mass_pa_simplices.hpp
integ/bilininteg_vecdiffusion_pa.hpp
integ/bilininteg_vecmass_pa.hpp
coefficient.hpp
@@ -309,6 +311,7 @@ set(HDRS
tmop_tools.hpp
tmop_amr.hpp
gslib.hpp
gslib/gslib_kernel_helpers.hpp
transfer.hpp
hyperbolic.hpp
integrator.hpp
+22 -4
View File
@@ -1345,7 +1345,8 @@ real_t DiffusionIntegrator::ComputeFluxEnergy
}
const IntegrationRule &DiffusionIntegrator::GetRule(
const FiniteElement &trial_fe, const FiniteElement &test_fe)
const FiniteElement &trial_fe, const FiniteElement &test_fe,
const bool stroud)
{
int order;
if (trial_fe.Space() == FunctionSpace::Pk)
@@ -1362,7 +1363,15 @@ const IntegrationRule &DiffusionIntegrator::GetRule(
{
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
}
return IntRules.Get(trial_fe.GetGeomType(), order);
if (stroud)
{
return StroudIntRules.Get(trial_fe.GetGeomType(), order);
}
else
{
return IntRules.Get(trial_fe.GetGeomType(), order);
}
}
MassIntegrator::MassIntegrator(const IntegrationRule *ir)
@@ -1449,7 +1458,8 @@ void MassIntegrator::AssembleElementMatrix2(
const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
const ElementTransformation &Trans)
const ElementTransformation &Trans,
const bool stroud)
{
// int order = trial_fe.GetOrder() + test_fe.GetOrder();
const int order = trial_fe.GetOrder() + test_fe.GetOrder() + Trans.OrderW();
@@ -1458,7 +1468,15 @@ const IntegrationRule &MassIntegrator::GetRule(const FiniteElement &trial_fe,
{
return RefinedIntRules.Get(trial_fe.GetGeomType(), order);
}
return IntRules.Get(trial_fe.GetGeomType(), order);
if (stroud)
{
return StroudIntRules.Get(trial_fe.GetGeomType(), order);
}
else
{
return IntRules.Get(trial_fe.GetGeomType(), order);
}
}
+40 -2
View File
@@ -2184,11 +2184,22 @@ public:
const Vector&, const Vector&,
Vector&, const int, const int);
using ApplySimplexKernelType = void(*)(const int, const bool, const Array<int>&,
const Array<int>&,
const Array<int>&, const Array<int>&, const Array<int>&,
const Array<real_t>&, const Array<real_t>&,
const Array<real_t>&, const Array<real_t>&,
const Array<real_t>&, const Array<real_t>&,
const Vector&, const Vector&,
Vector&, const int, const int);
using DiagonalKernelType = void(*)(const int, const bool, const Array<real_t>&,
const Array<real_t>&, const Vector&, Vector&,
const int, const int);
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
MFEM_REGISTER_KERNELS(ApplySimplexPAKernels, ApplySimplexKernelType, (int, int,
int));
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
struct Kernels { Kernels(); };
@@ -2341,7 +2352,8 @@ public:
void AddMultPatchPA(const int patch, const Vector &x, Vector &y) const;
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe);
const FiniteElement &test_fe,
const bool stroud = false);
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
@@ -2352,6 +2364,13 @@ public:
{
ApplyPAKernels::Specialization<DIM,D1D,Q1D>::Add();
DiagonalPAKernels::Specialization<DIM,D1D,Q1D>::Add();
AddSimplexSpecialization<DIM,D1D,Q1D>();
}
template <int DIM, int D1D, int Q1D>
static void AddSimplexSpecialization()
{
ApplySimplexPAKernels::Specialization<DIM,D1D,Q1D>::Add();
}
protected:
const IntegrationRule* GetDefaultIntegrationRule(
@@ -2388,11 +2407,22 @@ public:
const Array<real_t>&, const Vector&,
const Vector&, Vector&, const int, const int);
using ApplySimplexKernelType = void(*)(const int, const Array<int>&,
const Array<int>&,
const Array<int>&, const Array<int>&, const Array<int>&,
const Array<real_t>&, const Array<real_t>&,
const Array<real_t>&, const Array<real_t>&,
const Array<real_t>&, const Array<real_t>&,
const Vector&, const Vector&, Vector&,
const int, const int);
using DiagonalKernelType = void(*)(const int, const Array<real_t>&,
const Vector&, Vector&, const int,
const int);
MFEM_REGISTER_KERNELS(ApplyPAKernels, ApplyKernelType, (int, int, int));
MFEM_REGISTER_KERNELS(ApplySimplexPAKernels, ApplySimplexKernelType, (int, int,
int));
MFEM_REGISTER_KERNELS(DiagonalPAKernels, DiagonalKernelType, (int, int, int));
struct Kernels { Kernels(); };
@@ -2441,7 +2471,8 @@ public:
static const IntegrationRule &GetRule(const FiniteElement &trial_fe,
const FiniteElement &test_fe,
const ElementTransformation &Trans);
const ElementTransformation &Trans,
const bool stroud = false);
bool SupportsCeed() const override { return DeviceCanUseCeed(); }
@@ -2452,6 +2483,13 @@ public:
{
ApplyPAKernels::Specialization<DIM,D1D,Q1D>::Add();
DiagonalPAKernels::Specialization<DIM,D1D,Q1D>::Add();
AddSimplexSpecialization<DIM,D1D,Q1D>();
}
template <int DIM, int D1D, int Q1D>
static void AddSimplexSpecialization()
{
ApplySimplexPAKernels::Specialization<DIM,D1D,Q1D>::Add();
}
protected:
+6
View File
@@ -54,6 +54,8 @@ void Coefficient::Project(QuadratureFunction &qf)
QuadratureSpaceBase &qspace = *qf.GetSpace();
const int ne = qspace.GetNE();
Vector values;
// GetValues makes a reference, but we need it to be valid on Host
qf.HostWrite();
for (int iel = 0; iel < ne; ++iel)
{
qf.GetValues(iel, values);
@@ -327,6 +329,8 @@ void VectorCoefficient::Project(QuadratureFunction &qf)
const int ne = qspace.GetNE();
DenseMatrix values;
Vector col;
// GetValues makes a reference, but we need it to be valid on Host
qf.HostWrite();
for (int iel = 0; iel < ne; ++iel)
{
qf.GetValues(iel, values);
@@ -695,6 +699,8 @@ void MatrixCoefficient::Project(QuadratureFunction &qf, bool transpose)
QuadratureSpaceBase &qspace = *qf.GetSpace();
const int ne = qspace.GetNE();
DenseMatrix values, matrix;
// GetValues makes a reference, but we need it to be valid on Host
qf.HostWrite();
for (int iel = 0; iel < ne; ++iel)
{
qf.GetValues(iel, values);
+75
View File
@@ -237,6 +237,81 @@ ComplexGridFunction::ProjectBdrCoefficientTangent(VectorCoefficient
gfi->SyncAliasMemory(*this);
}
real_t
ComplexGridFunction::ComputeLpError(const real_t p,
Coefficient &exsolr,
Coefficient &exsoli,
Coefficient *weight,
const IntegrationRule *irs[],
const Array<int> *elems) const
{
real_t error = 0.0;
const FiniteElement *fe;
ElementTransformation *T;
Vector valsr;
Vector valsi;
const GridFunction& gf_r = real();
const GridFunction& gf_i = imag();
for (int i = 0; i < fes->GetNE(); i++)
{
if (elems != NULL && (*elems)[i] == 0) { continue; }
fe = fes->GetFE(i);
const IntegrationRule *ir;
if (irs)
{
ir = irs[fe->GetGeomType()];
}
else
{
int intorder = 2*fe->GetOrder() + 3;
ir = &(IntRules.Get(fe->GetGeomType(), intorder));
}
real_t elem_error = 0.0;
gf_r.GetValues(i, *ir, valsr);
gf_i.GetValues(i, *ir, valsi);
T = fes->GetElementTransformation(i);
for (int j = 0; j < ir->GetNPoints(); j++)
{
const IntegrationPoint &ip = ir->IntPoint(j);
T->SetIntPoint(&ip);
real_t diffr = valsr(j) - exsolr.Eval(*T, ip);
real_t diffi = valsi(j) - exsoli.Eval(*T, ip);
real_t diff = hypot(diffr, diffi);
if (p < infinity())
{
diff = pow(diff, p);
if (weight)
{
diff *= weight->Eval(*T, ip);
}
elem_error += ip.weight * T->Weight() * diff;
}
else
{
if (weight)
{
diff *= weight->Eval(*T, ip);
}
error = std::max(error, diff);
}
}
if (p < infinity())
{
// negative quadrature weights may cause the error to be negative
error += fabs(elem_error);
}
}
if (p < infinity())
{
error = pow(error, 1./p);
}
return error;
}
void ComplexGridFunction::Save(std::ostream &os) const
{
os << "ComplexGridFunction\n";
+69
View File
@@ -166,6 +166,75 @@ public:
return sqrt(err_r * err_r + err_i * err_i);
}
/// @brief Returns Max|u_ex - u_h| error for complex-valued H1 or L2 elements
///
/// Compute the $L_\infty$ error across the entire domain.
///
/// @param[in] exsolr Coefficient object reproducing the real part of the
/// anticipated values of the scalar field, Re(u_ex).
/// @param[in] exsoli Coefficient object reproducing the imaginary part of
/// the anticipated values of the scalar field, Im(u_ex).
/// @param[in] irs Optional pointer to an array of custom integration
/// rules e.g. higher order than the default rules. If
/// present the array will be indexed by
/// Geometry::Type.
///
/// @note Uses ComputeLpError internally. See the ComputeLpError
/// documentation for generalizations of this error computation.
///
/// @note If an array of integration rules is provided through @a irs, be
/// sure to include valid rules for each element type that may occur
/// in the list of elements.
///
virtual real_t ComputeMaxError(Coefficient &exsolr,
Coefficient &exsoli,
const IntegrationRule *irs[] = NULL) const
{
return ComputeLpError(infinity(), exsolr, exsoli, NULL, irs);
}
/// @brief Returns ||u_ex - u_h||_Lp for complex-valued H1 or L2 elements
///
/// Computes:
/// $$(\sum_{elems} \int_{elem} w \, |u_{ex} - u_h|^p)^{1/p}$$
/// Where:
/// $$|u_{ex} - u_h| = \sqrt{Re(u_{ex} - u_h)^2 + Im(u_{ex} - u_h)^2}$$
///
/// @param[in] p Real value indicating the exponent of the $L^p$ norm.
/// To avoid domain errors p should have a positive value,
/// either finite or infinite.
/// @param[in] exsolr Coefficient object reproducing the real part of the
/// anticipated values of the scalar field, Re(u_ex).
/// @param[in] exsoli Coefficient object reproducing the imaginary part of
/// the anticipated values of the scalar field, Im(u_ex).
/// @param[in] weight Optional pointer to a Coefficient object reproducing
/// a weighting function, w.
/// @param[in] irs Optional pointer to an array of custom integration
/// rules e.g. higher order than the default rules. If
/// present the array will be indexed by Geometry::Type.
/// @param[in] elems Optional pointer to a marker array, with a length
/// equal to the number of local elements, indicating
/// which elements to integrate over. Only those elements
/// corresponding to non-zero entries in @a elems will
/// contribute to the computed L2 error.
///
/// @note If an array of integration rules is provided through @a irs, be
/// sure to include valid rules for each element type that may occur
/// in the list of elements.
///
/// @note Quadratures with negative weights (as in some simplex integration
/// rules in MFEM) can produce negative integrals even with
/// non-negative integrands. To avoid returning negative errors this
/// function uses the absolute values of the element-wise integrals.
/// This may lead to results which are not entirely consistent with
/// such integration rules.
virtual real_t ComputeLpError(const real_t p,
Coefficient &exsolr,
Coefficient &exsoli,
Coefficient *weight = NULL,
const IntegrationRule *irs[] = NULL,
const Array<int> *elems = NULL) const;
/// Save the ComplexGridFunction to an output stream.
virtual void Save(std::ostream &out) const;
+3 -1
View File
@@ -1181,12 +1181,14 @@ void ParaViewDataCollection::SaveGFieldVTU(std::ostream &os, int ref_,
DenseMatrix vval, pmat;
std::vector<char> buf;
int vec_dim = it->second->VectorDim();
int map_type = it->second->FESpace()->GetTypicalFE()->GetMapType();
os << "<DataArray type=\"" << GetDataTypeString()
<< "\" Name=\"" << it->first
<< "\" NumberOfComponents=\"" << vec_dim << "\" "
<< VTKComponentLabels(vec_dim) << " "
<< "format=\"" << GetDataFormatString() << "\" >" << '\n';
if (vec_dim == 1)
if (vec_dim == 1 && (map_type == FiniteElement::VALUE ||
map_type == FiniteElement::INTEGRAL))
{
for (int i = 0; i < mesh->GetNE(); i++)
{
+79 -30
View File
@@ -25,21 +25,35 @@
namespace mfem
{
/// Lightweight adaptor over an std::map from strings to pointer to T
template<typename T>
class NamedFieldsMap
/// Lightweight adaptor over an std::map from type K to type to V
template<typename K, typename V,
typename = typename std::enable_if<std::is_default_constructible<V>::value>::type>
class GenericFieldMap
{
private:
static constexpr bool ValueIsPointer = std::is_pointer<V>::value;
public:
typedef std::map<std::string, T*> MapType;
typedef std::map<K, V> MapType;
typedef typename MapType::iterator iterator;
typedef typename MapType::const_iterator const_iterator;
/// Register field @a field with name @a fname
/** Replace existing field associated with @a fname (and optionally
delete associated pointer if @a own_data is true) */
void Register(const std::string& fname, T* field, bool own_data)
/// Register field @a field with name @a key
/// Only enabled if the template parameter V is not a pointer
template<typename = std::enable_if<!ValueIsPointer, bool>>
void Register(const K& key, V field)
{
T*& ref = field_map[fname];
field_map[key] = field;
}
/// Register field @a field with name @a key
/** Replace existing field associated with @a key (and optionally
delete associated pointer if @a own_data is true).
Only enabled if the template parameter V is a pointer*/
template<typename = std::enable_if<ValueIsPointer, bool>>
void Register(const K& key, V field, bool own_data)
{
V& ref = field_map[key];
if (own_data)
{
delete ref; // if newly allocated -> ref is null -> OK
@@ -47,23 +61,40 @@ public:
ref = field;
}
/// Unregister association between field @a field and name @a fname
/** Optionally delete associated pointer if @a own_data is true */
void Deregister(const std::string& fname, bool own_data)
/// Unregister association between field @a field and name @a key
/// Only enabled if the template parameter V is not a pointer
template<typename = std::enable_if<!ValueIsPointer, bool>>
void Deregister(const K& key)
{
iterator it = field_map.find(fname);
iterator it = field_map.find(key);
if ( it != field_map.end() )
{
field_map.erase(it);
}
}
/// Unregister association between field @a field and name @a key
/** Optionally delete associated pointer if @a own_data is true.
Only enabled if the template parameter V is a pointer */
template<typename = std::enable_if<ValueIsPointer, bool>>
void Deregister(const K& key, bool own_data)
{
iterator it = field_map.find(key);
if ( it != field_map.end() )
{
if (own_data)
{
delete it->second;
it->second = nullptr;
}
field_map.erase(it);
}
}
/// Clear all associations between names and fields
/** Delete associated pointers when @a own_data is true */
/** Delete associated pointers when @a own_data is true.
Only enabled if the template parameter V is a pointer */
template<typename = std::enable_if<ValueIsPointer, bool>>
void DeleteData(bool own_data)
{
for (iterator it = field_map.begin(); it != field_map.end(); ++it)
@@ -76,22 +107,37 @@ public:
}
}
/// Predicate to check if a field is associated with name @a fname
bool Has(const std::string& fname) const
/// Predicate to check if a field is associated with name @a key
bool Has(const K& key) const
{
return field_map.find(fname) != field_map.end();
return field_map.find(key) != field_map.end();
}
/// Get a pointer to the field associated with name @a fname
/** @return Pointer to field associated with @a fname or NULL */
T* Get(const std::string& fname) const
/// Get a pointer to the field associated with name @a key
/** @return Field associated with @a key or NULL,
if value is pointer and key not found */
V Get(const K& key) const
{
const_iterator it = field_map.find(fname);
return it != field_map.end() ? it->second : NULL;
const_iterator it = field_map.find(key);
if (it != field_map.end())
{
return it->second;
}
else
{
if constexpr (ValueIsPointer)
{
return nullptr;
}
else
{
return V(); // Return default-constructed value for non-pointer types
}
}
}
/// Returns a const reference to the underlying map
const MapType& GetMap() const { return field_map; }
const MapType &GetMap() const { return field_map; }
/// Returns the number of registered fields
int NumFields() const { return field_map.size(); }
@@ -106,21 +152,24 @@ public:
/// Returns an end const iterator to the registered fields
const_iterator end() const { return field_map.end(); }
/// Returns an iterator to the field @a fname
iterator find(const std::string& fname)
{ return field_map.find(fname); }
/// Returns an iterator to the field @a key
iterator find(const K& key)
{ return field_map.find(key); }
/// Returns a const iterator to the field @a fname
const_iterator find(const std::string& fname) const
{ return field_map.find(fname); }
/// Returns a const iterator to the field @a key
const_iterator find(const K& key) const
{ return field_map.find(key); }
/// Clears the map of registered fields without reclaiming memory
/// Clears the map of registered fields
void clear() { field_map.clear(); }
protected:
MapType field_map;
};
/// Lightweight adaptor over an std::map from strings to pointer to T
template<typename T>
using NamedFieldsMap = GenericFieldMap<std::string, T*>;
/** A class for collecting finite element data that is part of the same
simulation. Currently, this class groups together grid functions (fields),
+16 -2
View File
@@ -671,6 +671,20 @@ public:
MPI_COMM_WORLD);
}
Operator& GetGradient(const Vector &x0) const override
{
x = x0;
f.UseDevice(x.UseDevice());
xpev.UseDevice(x.UseDevice());
op.Mult(x, f);
const real_t xnorm_local = x.Norml2();
MPI_Allreduce(&xnorm_local, &xnorm, 1, MPITypeMap<real_t>::mpi_type, MPI_SUM,
MPI_COMM_WORLD);
return const_cast<FDJacobian&>(*this);
}
void Mult(const Vector &v, Vector &y) const override
{
// See [1] for choice of eps.
@@ -725,11 +739,11 @@ public:
private:
const Operator &op;
Vector x, f;
mutable Vector x, f;
mutable Vector xpev;
real_t lambda = 1.0e-6;
real_t fixed_eps;
real_t xnorm;
mutable real_t xnorm;
};
/// @brief Find the index of a field descriptor in a vector of field descriptors.
+42 -1
View File
@@ -167,7 +167,15 @@ public:
/** @brief Full multidimensional representation which does not use tensor
product structure. The ordering of the degrees of freedom is the
same as TENSOR, but the sizes of B and G are the same as FULL.*/
LEXICOGRAPHIC_FULL
LEXICOGRAPHIC_FULL,
/** @brief Ragged tensor product representation using 1D matrices/tensors
with dimensions using 1D number of quadrature points and ragged tensor degrees of
freedom. */
/** Used only for partial assembly of the H1 positive basis. The
size of B is d1d x qnpt x dim. Since different Gauss-Jacobi quadrature rules
are employed in each dimension, we need to store dim arrays. */
RAGGED_TENSOR
};
/// Describes the contents of the #B, #Bt, #G, and #Gt arrays, see #Mode.
@@ -228,6 +236,39 @@ public:
const Array<DofToQuad*> &dof2quad_array,
const IntegrationRule &ir,
DofToQuad::Mode mode);
virtual ~DofToQuad() = default;
};
/** @brief Structure representing the matrices/tensors needed to evaluate (in
reference space) the values, gradients, divergences, or curls of a positive
FiniteElement on simplices at the quadrature points of Stroud conical quadrature. */
class RaggedDofToQuad : public DofToQuad
{
public:
/** @brief Special basis function structures for positive (Bernstein) basis with
partial assembly. The storage layout of Ba1 is ndof x nqpt for scalar elements.
The storage layout of Ba2 is ndof x ndof x nqpt. In particular, we have
Ba2(iqpt, a1, a2) = B^{p-a1}_{a2}(x_{iqpt}). */
Array<real_t> Ba1, Ba2, Ba3;
Array<real_t> Ba1t, Ba2t, Ba3t;
/** @brief Special structures for gradients of positive basis with partial assembly.
The gradient arrays exploit properties of the Bernstein basis which allow grad(B^p_alpha)
to be expressed as the sum of products of B^{p-1}_alpha and the barycentric coordinates.
Thus, Ga1 and Ga2 simply contain the ragged tensor product components of B^{p-1}_alpha */
Array<real_t> Ga1, Ga2, Ga3;
Array<real_t> Ga1t, Ga2t, Ga3t;
/** @brief Mapping from the Bernstein multi-index (a_1, ..., a_d) to the lexicographic
dof index. */
Array<int> lex_map;
Array<int> forward_map2d_diff, forward_map3d_diff;
Array<int> inverse_map2d_diff, inverse_map3d_diff;
Array<int> forward_map2d_mass, forward_map3d_mass;
Array<int> inverse_map2d_mass, inverse_map3d_mass;
};
/// Describes the function space on each element
+302
View File
@@ -557,6 +557,101 @@ H1Pos_TriangleElement::H1Pos_TriangleElement(const int p)
}
}
const DofToQuad &H1Pos_TriangleElement::GetRaggedTensorDofToQuad(
const FiniteElement &fe, const IntegrationRule &ir,
DofToQuad::Mode mode,
Array<DofToQuad*> &dof2quad_array)
{
DofToQuad *d2q = nullptr;
MFEM_VERIFY(mode == DofToQuad::RAGGED_TENSOR, "invalid mode requested");
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (DofToQuad)
#endif
{
for (int i = 0; i < dof2quad_array.Size(); i++)
{
d2q = dof2quad_array[i];
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
}
if (!d2q)
{
d2q = new RaggedDofToQuad;
const int ndof = fe.GetOrder() + 1; // verify
const int nqpt = (int)floor(pow(ir.GetNPoints(), 1.0/fe.GetDim()) + 0.5);
d2q->FE = &fe;
d2q->IntRule = &ir;
d2q->mode = mode;
d2q->ndof = ndof;
d2q->nqpt = nqpt;
RaggedDofToQuad *rd2q = static_cast<RaggedDofToQuad*>(d2q);
rd2q->Ba1.SetSize(nqpt*ndof);
// second component of ragged tensor basis, technically dof*(dof-1)/2 entries
rd2q->Ba2.SetSize((int)nqpt*ndof*ndof);
rd2q->Ba1t.SetSize(nqpt*ndof);
rd2q->Ba2t.SetSize((int)nqpt*ndof*ndof);
// stores first component of ragged tensor basis with order p-1, for gradients only
rd2q->Ga1.SetSize(nqpt*(ndof -1));
// stores second component of ragged tensor basis with order p-1
rd2q->Ga2.SetSize(nqpt*(ndof-1)*(ndof -1));
rd2q->Ga1t.SetSize(nqpt*(ndof -1));
rd2q->Ga2t.SetSize(nqpt*(ndof-1)*(ndof -1));
rd2q->lex_map.SetSize(ndof * ndof);
Vector shape_a1(ndof), shape_a2(ndof * ndof);
Vector shape_Ga1(ndof-1), shape_Ga2((ndof-1) * (ndof-1));
for (int i = 0; i < nqpt; i++)
{
// The first 'nqpt' points in the first dimension 'ir' have the same x-coordinates as those
// of the 1D rule (ie. (2,0) Gauss-Jacobi rule). The first 'nqpt' points in the second dimension
// 'ir' have the same y-coordinates as those of the 1D rule for second dimension (i.e. (1,0)
// Gauss-Jacobi rule). Additionally, the Bernstein PA algorithms expect evaluation of the
// component 1D bases at the Stroud nodes pulled back to the unit square, so perform the pullback
// on the fly.
const real_t x = ir.IntPoint(i).x;
const real_t y = ir.IntPoint(nqpt*i).y / (1.0 - ir.IntPoint(nqpt*i).x);
Poly_1D::CalcBernstein(ndof-1, x, shape_a1);
Poly_1D::CalcBernstein(ndof-2, x, shape_Ga1);
for (int j = 0; j < ndof; j++)
{
rd2q->Ba1t[i+nqpt*j] = rd2q->Ba1[j+ndof*i] = shape_a1(j);
if (j < ndof-1)
{
rd2q->Ga1t[i+nqpt*j] = rd2q->Ga1[j+(ndof-1)*i] = shape_Ga1(j);
Poly_1D::CalcBernstein(ndof-2-j, y, shape_Ga2);
}
Poly_1D::CalcBernstein(ndof-1-j, y, shape_a2);
for (int k = 0; k < ndof-j; k++)
{
rd2q->Ba2t[i + nqpt*(j + ndof*k)] = rd2q->Ba2[k + ndof*(j + ndof*i)] = shape_a2(
k);
if (j < ndof-1 && k < ndof-j-1)
{
rd2q->Ga2t[i + nqpt*(j + (ndof-1)*k)] = rd2q->Ga2[k + (ndof-1)*(j +
(ndof-1)*i)] = shape_Ga2(k);
}
}
}
}
// stores the mapping from 2D Bernstein multi-index (i,j,p-i-j) to the
// lexicographic DOF ordering
for (int i = 0; i < ndof; i++)
{
for (int j = 0; j < ndof-i; j++)
{
int idx = ((2 * (ndof-1) + 3) - j) * j / 2 + i;
rd2q->lex_map[j + ndof*i] = idx;
}
}
dof2quad_array.Append(d2q);
}
}
return *d2q;
}
// static method
void H1Pos_TriangleElement::CalcShape(
const int p, const real_t l1, const real_t l2, real_t *shape)
@@ -749,6 +844,213 @@ H1Pos_TetrahedronElement::H1Pos_TetrahedronElement(const int p)
}
}
const DofToQuad &H1Pos_TetrahedronElement::GetRaggedTensorDofToQuad(
const FiniteElement &fe, const IntegrationRule &ir,
DofToQuad::Mode mode,
Array<DofToQuad*> &dof2quad_array)
{
DofToQuad *d2q = nullptr;
MFEM_VERIFY(mode == DofToQuad::RAGGED_TENSOR, "invalid mode requested");
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (DofToQuad)
#endif
{
for (int i = 0; i < dof2quad_array.Size(); i++)
{
d2q = dof2quad_array[i];
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
}
if (!d2q)
{
d2q = new RaggedDofToQuad;
const int ndof = fe.GetOrder() + 1; // verify
const int nqpt = (int)floor(pow(ir.GetNPoints(), 1.0/fe.GetDim()) + 0.5);
const int basis_dim2d = ndof*(ndof+1) / 2;
const int basis_dim3d = ndof*(ndof+1)*(ndof+2) / 6;
const int basis_dim2d_diff = (ndof-1)*(ndof) / 2;
const int basis_dim3d_diff = (ndof-1)*(ndof)*(ndof+1) / 6;
d2q->FE = &fe;
d2q->IntRule = &ir;
d2q->mode = mode;
d2q->ndof = ndof;
d2q->nqpt = nqpt;
RaggedDofToQuad *rd2q = static_cast<RaggedDofToQuad*>(d2q);
rd2q->Ba1.SetSize(nqpt * ndof);
// second component of ragged tensor basis, technically dof*(dof-1)/2 entries
rd2q->Ba2.SetSize(nqpt * basis_dim2d);
// third component of ragged tensor basis, technically dof*(dof-1)/2 entries
rd2q->Ba3.SetSize(nqpt * basis_dim3d);
rd2q->Ba1t.SetSize(nqpt * ndof);
rd2q->Ba2t.SetSize(nqpt * basis_dim2d);
rd2q->Ba3t.SetSize(nqpt * basis_dim3d);
// stores first component of ragged tensor basis with order p-1, for gradients only
rd2q->Ga1.SetSize(nqpt * (ndof-1));
// stores second component of ragged tensor basis with order p-1
rd2q->Ga2.SetSize(nqpt * basis_dim2d_diff);
// stores third component of ragged tensor basis with order p-1
rd2q->Ga3.SetSize(nqpt * basis_dim3d_diff);
rd2q->Ga1t.SetSize(nqpt * (ndof-1));
rd2q->Ga2t.SetSize(nqpt * basis_dim2d_diff);
rd2q->Ga3t.SetSize(nqpt * basis_dim3d_diff);
rd2q->lex_map.SetSize(ndof * ndof * ndof);
rd2q->forward_map2d_diff.SetSize((ndof-1) * (ndof-1));
rd2q->forward_map3d_diff.SetSize((ndof-1) * (ndof-1) * (ndof-1));
rd2q->inverse_map2d_diff.SetSize(2 * basis_dim2d_diff);
rd2q->inverse_map3d_diff.SetSize(3 * basis_dim3d_diff);
rd2q->forward_map2d_mass.SetSize(ndof * ndof);
rd2q->forward_map3d_mass.SetSize(ndof * ndof * ndof);
rd2q->inverse_map2d_mass.SetSize(2 * basis_dim2d);
rd2q->inverse_map3d_mass.SetSize(2 * basis_dim3d);
// forward and inverse maps for multi-index to collpased 1d index for diffusion, can combine
// these four loops, but need four idx's and clause for shorter diff loops
int idx = 0;
for (int i = 0; i < ndof-1; i++)
{
for (int j = 0; j < ndof-i-1; j++)
{
rd2q->forward_map2d_diff[j + (ndof-1)*i] = idx;
rd2q->inverse_map2d_diff[2*idx] = i;
rd2q->inverse_map2d_diff[1 + 2*idx] = j;
idx++;
}
}
idx = 0;
for (int k = 0; k < ndof-1; k++)
{
for (int j = 0; j < ndof-k-1; j++)
{
for (int i = 0; i < ndof-k-j-1; i++)
{
rd2q->forward_map3d_diff[k + (ndof-1)*(j + (ndof-1)*i)] = idx;
rd2q->inverse_map3d_diff[3*idx] = i;
rd2q->inverse_map3d_diff[1 + 3*idx] = j;
rd2q->inverse_map3d_diff[2 + 3*idx] = k;
idx++;
}
}
}
// forward and inverse maps for multi-index to collpased 1d index for mass
idx = 0;
for (int j = 0; j < ndof; j++)
{
for (int i = 0; i < ndof-j; i++)
{
rd2q->forward_map2d_mass[j + ndof*i] = idx;
rd2q->inverse_map2d_mass[2*idx] = i;
rd2q->inverse_map2d_mass[1 + 2*idx] = j;
idx++;
}
}
idx = 0;
for (int k = 0; k < ndof; k++)
{
for (int j = 0; j < ndof-k; j++)
{
for (int i = 0; i < ndof-k-j; i++)
{
rd2q->forward_map3d_mass[k + ndof*(j + ndof*i)] = idx;
rd2q->inverse_map3d_mass[2*idx] = i;
rd2q->inverse_map3d_mass[1 + 2*idx] = j;
// d2q->inverse_map3d_mass[2 + 3*idx] = k;
idx++;
}
}
}
Vector shape_a1(ndof), shape_a2(ndof * ndof), shape_a3(ndof * ndof * ndof);
Vector shape_Ga1(ndof-1), shape_Ga2(ndof-1), shape_Ga3(ndof-1);
for (int i = 0; i < nqpt; i++)
{
// The first 'nqpt' points in the first dimension 'ir' have the same x-coordinates as those
// of the 1D rule (ie. (2,0) Gauss-Jacobi rule). The first 'nqpt' points in the second dimension
// 'ir' have the same y-coordinates as those of the 1D rule for second dimension (i.e. (1,0)
// Gauss-Jacobi rule). The first 'nqpt' points in the third dimension have the same z-coordinates
// as those of the 1D rule for the third dimension (i.e. Gauss-Legendre rule). Additionally,
// the Bernstein PA algorithms expect evaluation of the component 1D bases at the Stroud nodes
// pulled back to the unit cube, so perform the pullback on the fly.
const real_t x = ir.IntPoint(i).x;
const real_t y = ir.IntPoint(nqpt*i).y / (1.0 - ir.IntPoint(nqpt*i).x);
const real_t z = ir.IntPoint(nqpt*nqpt*i).z / (1.0 - ir.IntPoint(
nqpt*nqpt*i).x - ir.IntPoint(nqpt*nqpt*i).y);
Poly_1D::CalcBernstein(ndof-1, x, shape_a1);
Poly_1D::CalcBernstein(ndof-2, x, shape_Ga1);
for (int j = 0; j < ndof; j++)
{
rd2q->Ba1t[i+nqpt*j] = rd2q->Ba1[j+ndof*i] = shape_a1(j);
if (j < ndof-1)
{
rd2q->Ga1t[i+nqpt*j] = rd2q->Ga1[j+(ndof-1)*i] = shape_Ga1(j);
Poly_1D::CalcBernstein(ndof-2-j, y, shape_Ga2);
}
Poly_1D::CalcBernstein(ndof-1-j, y, shape_a2);
for (int k = 0; k < ndof-j; k++)
{
const int a_2d_mass = rd2q->forward_map2d_mass[k + ndof*j];
rd2q->Ba2t[i + nqpt*a_2d_mass] = rd2q->Ba2[a_2d_mass + basis_dim2d*i] =
shape_a2(
k);
if (j < ndof-1 && k < ndof-j-1)
{
const int a_2d_diff = rd2q->forward_map2d_diff[k + (ndof-1)*j];
rd2q->Ga2t[i + nqpt*a_2d_diff] = rd2q->Ga2[a_2d_diff + basis_dim2d_diff*i] =
shape_Ga2(k);
Poly_1D::CalcBernstein(ndof-2-j-k, z, shape_Ga3);
}
Poly_1D::CalcBernstein(ndof-1-j-k, z, shape_a3);
for (int m = 0; m < ndof-j-k; m++)
{
const int a_3d_mass = rd2q->forward_map3d_mass[m + ndof*(k + ndof*j)];
rd2q->Ba3t[i + nqpt*a_3d_mass] = rd2q->Ba3[a_3d_mass + basis_dim3d*i] =
shape_a3(
m);
if (j < ndof-1 && k < ndof-j-1 && m < ndof-j-k-1)
{
// // collapsed 1D access
// d2q->Ga3[i + nqpt*(m + d2q->offset3d[k + (ndof-1)*j])] = shape_Ga3(m);
// collapsed 1D access with forward mapping
const int a_3d_diff = rd2q->forward_map3d_diff[m + (ndof-1)*(k + (ndof-1)*j)];
rd2q->Ga3t[i + nqpt*a_3d_diff] = rd2q->Ga3[a_3d_diff + basis_dim3d_diff*i] =
shape_Ga3(m);
}
}
}
}
}
// stores the mapping from 3D Bernstein multi-index (i,j,k,p-i-j-k) to the
// lexicographic DOF ordering
int p = ndof - 1;
for (int i = 0; i < ndof; i++)
{
for (int j = 0; j < ndof-i; j++)
{
for (int k = 0; k < ndof-i-j; k++)
{
int dof = (p+1)*(p+2)*(p+3) / 6;
int tet = (p-k)*(p-k+1)*(p-k+2) / 6;
int tri = (p+1-k-j)*(p+2-k-j)/2;
int multi_idx = dof - tet - tri + i;
rd2q->lex_map[k + ndof*(j + ndof*i)] = multi_idx;
}
}
}
dof2quad_array.Append(d2q);
}
}
return *d2q;
}
// static method
void H1Pos_TetrahedronElement::CalcShape(
const int p, const real_t l1, const real_t l2, const real_t l3,
+30
View File
@@ -191,6 +191,21 @@ public:
/// Construct the H1Pos_TriangleElement of order @a p
H1Pos_TriangleElement(const int p);
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const override
{
return (mode == DofToQuad::RAGGED_TENSOR) ?
GetRaggedTensorDofToQuad(*this, ir, mode, dof2quad_array) :
FiniteElement::GetDofToQuad(ir, mode);
}
static const DofToQuad &GetRaggedTensorDofToQuad(
const FiniteElement &fe, const IntegrationRule &ir,
DofToQuad::Mode mode,
Array<DofToQuad*> &dof2quad_array);
const Array<int> &GetDofMap() const { return dof_map; }
// The size of shape is (p+1)(p+2)/2 (dof).
static void CalcShape(const int p, const real_t x, const real_t y,
real_t *shape);
@@ -220,6 +235,21 @@ public:
/// Construct the H1Pos_TetrahedronElement of order @a p
H1Pos_TetrahedronElement(const int p);
const DofToQuad &GetDofToQuad(const IntegrationRule &ir,
DofToQuad::Mode mode) const override
{
return (mode == DofToQuad::RAGGED_TENSOR) ?
GetRaggedTensorDofToQuad(*this, ir, mode, dof2quad_array) :
FiniteElement::GetDofToQuad(ir, mode);
}
static const DofToQuad &GetRaggedTensorDofToQuad(
const FiniteElement &fe, const IntegrationRule &ir,
DofToQuad::Mode mode,
Array<DofToQuad*> &dof2quad_array);
const Array<int> &GetDofMap() const { return dof_map; }
// The size of shape is (p+1)(p+2)(p+3)/6 (dof).
static void CalcShape(const int p, const real_t x, const real_t y,
const real_t z, real_t *shape);
+27
View File
@@ -250,6 +250,14 @@ public:
its GetOrder() method. */
virtual FiniteElementCollection *Clone(int p) const;
/** @brief Return the order parameter used to construct this collection.
* This differs from GetOrder() depending on the collection type. */
virtual int GetConstructorOrder() const
{
MFEM_ABORT("Collection " << Name() << " does not support GetConstructorOrder");
return -1;
}
protected:
const int base_p; ///< Order as returned by GetOrder().
@@ -314,6 +322,9 @@ public:
FiniteElementCollection *Clone(int p) const override
{ return new H1_FECollection(p, dim, b_type); }
int GetConstructorOrder() const override
{ return base_p; }
virtual ~H1_FECollection();
};
@@ -343,6 +354,10 @@ class H1_Trace_FECollection : public H1_FECollection
public:
H1_Trace_FECollection(const int p, const int dim,
const int btype = BasisType::GaussLobatto);
FiniteElementCollection *Clone(int p) const override
{ return new H1_Trace_FECollection(p, dim+1, b_type); }
};
/// Arbitrary order "L2-conforming" discontinuous finite elements.
@@ -396,6 +411,9 @@ public:
FiniteElementCollection *Clone(int p) const override
{ return new L2_FECollection(p, dim, b_type, m_type); }
int GetConstructorOrder() const override
{ return base_p; }
virtual ~L2_FECollection();
};
@@ -456,6 +474,9 @@ public:
FiniteElementCollection *Clone(int p) const override
{ return new RT_FECollection(p, dim, cb_type, ob_type); }
int GetConstructorOrder() const override
{ return base_p-1; }
virtual ~RT_FECollection();
};
@@ -536,6 +557,9 @@ public:
FiniteElementCollection *Clone(int p) const override
{ return new ND_FECollection(p, dim, cb_type, ob_type); }
int GetConstructorOrder() const override
{ return dim>1 ? base_p : base_p+1; }
virtual ~ND_FECollection();
};
@@ -548,6 +572,9 @@ public:
ND_Trace_FECollection(const int p, const int dim,
const int cb_type = BasisType::GaussLobatto,
const int ob_type = BasisType::GaussLegendre);
FiniteElementCollection *Clone(int p) const override
{ return new ND_Trace_FECollection(p, dim+1, cb_type, ob_type); }
};
/// Arbitrary order 3D H(curl)-conforming Nedelec finite elements in 1D.
+1 -2
View File
@@ -4631,9 +4631,8 @@ FiniteElementCollection *FiniteElementSpace::Load(Mesh *m, std::istream &input)
ElementDofOrdering GetEVectorOrdering(const FiniteElementSpace& fes)
{
return UsesTensorBasis(fes)?
return (UsesTensorBasis(fes) || fes.UsesRaggedTensorBasis()) ?
ElementDofOrdering::LEXICOGRAPHIC:
ElementDofOrdering::NATIVE;
}
} // namespace mfem
+12
View File
@@ -1514,6 +1514,18 @@ public:
return dynamic_cast<const L2_FECollection*>(fec) != NULL;
}
/// @brief Return true if the mesh contains only one topology, the elements are
/// all triangles or tetrahedrons, and the elements are ragged tensor elements
/// i.e. Bernstein/positive basis.
bool UsesRaggedTensorBasis() const
{
bool simplex = this->GetMesh()->IsSimplexMesh();
bool positive =
dynamic_cast<const mfem::H1Pos_TriangleElement *>(this->GetTypicalFE()) ||
dynamic_cast<const mfem::H1Pos_TetrahedronElement *>(this->GetTypicalFE());
return simplex && positive;
}
/** In variable-order spaces on nonconforming (NC) meshes, this function
controls whether strict conformity is enforced in cases where coarse
edges/faces have higher polynomial order than their fine NC neighbors.
+167
View File
@@ -2256,6 +2256,104 @@ void GridFunction::AccumulateAndCountBdrTangentValues(
}
}
void GridFunction::AccumulateAndCountTraceValues(
Coefficient *coeff[], VectorCoefficient *vcoeff,
Array<int> &values_counter)
{
if (vcoeff)
{
MFEM_VERIFY(fes->GetVDim() == vcoeff->GetVDim(),
"vcoeff vdim != fes VDim");
MFEM_VERIFY(fes->GetTypicalTraceElement()->GetMapType() ==
FiniteElement::VALUE &&
fes->GetTypicalTraceElement()->GetRangeType() ==
FiniteElement::SCALAR,
"Can only call ProjectTraceCoefficient on scalar value-type "
"trace elements. "
"Use ProjectTraceCoefficientNormal for RT and "
"ProjectTraceCoefficientTangent for ND finite elements.");
}
Array<int> vdofs;
Vector vc;
values_counter.SetSize(Size());
values_counter = 0;
const int vdim = fes->GetVDim();
HostReadWrite();
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
{
const FiniteElement *fe = fes->GetFaceElement(i);
const int fdof = fe->GetDof();
ElementTransformation *transf = fes->GetMesh()->GetFaceTransformation(i);
const IntegrationRule &ir = fe->GetNodes();
fes->GetFaceVDofs(i, vdofs);
for (int j = 0; j < fdof; j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
transf->SetIntPoint(&ip);
if (vcoeff) { vcoeff->Eval(vc, *transf, ip); }
for (int d = 0; d < vdim; d++)
{
if (!vcoeff && !coeff[d]) { continue; }
real_t val = vcoeff ? vc(d) : coeff[d]->Eval(*transf, ip);
int ind = vdofs[fdof*d+j];
if ( ind < 0 )
{
val = -val, ind = -1-ind;
}
if (++values_counter[ind] == 1)
{
(*this)(ind) = val;
}
else
{
(*this)(ind) += val;
}
}
}
}
}
void GridFunction::AccumulateAndCountTraceTangentValues(
VectorCoefficient &vcoeff, Array<int> &values_counter)
{
MFEM_VERIFY(fes->GetVDim() == 1, "fespace VDim != 1");
MFEM_VERIFY(fes->GetTypicalTraceElement()
->GetRangeType() == FiniteElement::VECTOR &&
fes->GetTypicalTraceElement()
->GetMapType() == FiniteElement::H_CURL,
"Not an ND FE space!");
MFEM_VERIFY(fes->GetTypicalTraceElement()->GetPhysRangeDim(
fes->GetMesh()->SpaceDimension()) == vcoeff.GetVDim(),
"vcoeff vdim != PhysRangeDim");
const FiniteElement *fe;
ElementTransformation *T;
Array<int> dofs;
Vector lvec;
values_counter.SetSize(Size());
values_counter = 0;
HostReadWrite();
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
{
fe = fes->GetFaceElement(i);
T = fes->GetMesh()->GetFaceTransformation(i);
fes->GetFaceVDofs(i, dofs);
lvec.SetSize(fe->GetDof());
fe->Project(vcoeff, *T, lvec);
accumulate_dofs(dofs, lvec, *this, values_counter);
}
}
void GridFunction::ComputeMeans(AvgType type, Array<int> &zones_per_vdof)
{
switch (type)
@@ -2698,6 +2796,74 @@ void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
}
}
void GridFunction::ProjectTraceCoefficient(Coefficient *coeff[])
{
Array<int> values_counter;
AccumulateAndCountTraceValues(coeff, NULL, values_counter);
ComputeMeans(ARITHMETIC, values_counter);
}
void GridFunction::ProjectTraceCoefficient(Coefficient &coeff)
{
MFEM_VERIFY(FESpace()->GetVDim() == 1, "ProjectTraceCoefficient(Coefficient&)"
"is only valid for scalar GridFunction");
Coefficient *coeff_p = &coeff;
ProjectTraceCoefficient(&coeff_p);
}
void GridFunction::ProjectTraceCoefficient(VectorCoefficient &vcoeff)
{
MFEM_VERIFY(FESpace()->GetVDim() == vcoeff.GetVDim(),
"Incompatible vcoeff vdim and fes vdim");
Array<int> values_counter;
AccumulateAndCountTraceValues(NULL, &vcoeff, values_counter);
ComputeMeans(ARITHMETIC, values_counter);
}
void GridFunction::ProjectTraceCoefficientNormal(VectorCoefficient &vcoeff)
{
MFEM_VERIFY(fes->GetVDim() == 1, "fespace VDim != 1");
MFEM_VERIFY(fes->GetTypicalTraceElement()->GetRangeType() ==
FiniteElement::SCALAR &&
fes->GetTypicalTraceElement()->GetMapType() ==
FiniteElement::INTEGRAL, "Not an RT FE space!");
MFEM_VERIFY(vcoeff.GetVDim() == fes->GetMesh()->SpaceDimension(),
"vcoeff vdim (" << vcoeff.GetVDim()
<< ") != SpaceDimension ("
<< fes->GetMesh()->SpaceDimension() << ")");
const FiniteElement *fe;
ElementTransformation *T;
Array<int> dofs;
int dim = vcoeff.GetVDim();
Vector vc(dim), nor(dim), lvec;
for (int i = 0; i < fes->GetMesh()->GetNumFaces(); i++)
{
fe = fes->GetFaceElement(i);
T = fes->GetMesh()->GetFaceTransformation(i);
const IntegrationRule &ir = fe->GetNodes();
lvec.SetSize(fe->GetDof());
for (int j = 0; j < ir.GetNPoints(); j++)
{
const IntegrationPoint &ip = ir.IntPoint(j);
T->SetIntPoint(&ip);
vcoeff.Eval(vc, *T, ip);
CalcOrtho(T->Jacobian(), nor);
lvec(j) = (vc * nor);
}
fes->GetFaceVDofs(i, dofs);
SetSubVector(dofs, lvec);
}
}
void GridFunction::ProjectTraceCoefficientTangent(VectorCoefficient &vcoeff)
{
Array<int> values_counter;
AccumulateAndCountTraceTangentValues(vcoeff, values_counter);
ComputeMeans(ARITHMETIC, values_counter);
}
void GridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
real_t rtol, int iter)
{
@@ -5286,6 +5452,7 @@ PLBound GridFunction::GetBounds(Vector &lower, Vector &upper,
{
int max_order = fes->GetMaxElementOrder();
PLBound plb(fes, ref_factor*(max_order+1));
Vector lel, uel;
GetElementBounds(plb, lel, uel, vdim);
+24
View File
@@ -578,6 +578,13 @@ protected:
const Array<int> &bdr_attr,
Array<int> &values_counter);
void AccumulateAndCountTraceValues(Coefficient *coeff[],
VectorCoefficient *vcoeff,
Array<int> &values_counter);
void AccumulateAndCountTraceTangentValues(VectorCoefficient &vcoeff,
Array<int> &values_counter);
// Complete the computation of averages; called e.g. after
// AccumulateAndCountZones().
void ComputeMeans(AvgType type, Array<int> &zones_per_vdof);
@@ -663,6 +670,23 @@ public:
ProjectBdrCoefficient(&coeff_p, attr);
}
/// Project a Coefficient on a GridFunction defined on H1 trace space
void ProjectTraceCoefficient(Coefficient *coeff[]);
void ProjectTraceCoefficient(Coefficient &coeff);
/** @brief Project a VectorCoefficient @a vcoeff on a GridFunction
defined on a Vector H1 trace space. Note that this also works
for a scalar H1 trace space, where only the first component of
@a vcoeff is used. */
void ProjectTraceCoefficient(VectorCoefficient &vcoeff);
/** @brief Project a VectorCoefficient on a GridFunction
defined on an RT trace space */
void ProjectTraceCoefficientNormal(VectorCoefficient &vcoeff);
/** @brief Project a VectorCoefficient on a GridFunction
defined on an ND trace space */
void ProjectTraceCoefficientTangent(VectorCoefficient &vcoeff);
/** @brief Project a VectorCoefficient on the GridFunction, modifying only
DOFs on the boundary associated with the boundary attributes marked in
the @a attr array. */
+1234 -726
View File
File diff suppressed because it is too large Load Diff
+166 -51
View File
@@ -12,6 +12,9 @@
#ifndef MFEM_GSLIB
#define MFEM_GSLIB
#include <map>
#include <vector>
#include "../config/config.hpp"
#ifdef MFEM_USE_MPI
#include "pgridfunc.hpp"
@@ -119,6 +122,11 @@ protected:
// IntegrationRules for simplex->Quad/Hex and to project to p_max in-case of
// p-refinement.
Array<IntegrationRule *> ir_split;
/// Integration rules built at the field polynomial order (only for surface
/// meshes when mesh order is not the same as gridfunction order).
Array<IntegrationRule *> ir_split_sol;
/// Order at which #ir_split_sol was built; -1 means not built.
int ir_split_sol_order = -1;
Array<FiniteElementSpace *> fes_rst_map; //FESpaces to map Quad/Hex->Simplex
Array<GridFunction *> gf_rst_map; // GridFunctions to map Quad/Hex->Simplex
FiniteElementCollection *fec_map_lin;
@@ -134,6 +142,8 @@ protected:
AvgType avgtype; // average type used for L2 functions
Array<int> split_element_map;
Array<int> split_element_index;
// Geometry::Type (as int) of the original element for each split quad.
Array<int> split_element_geom;
int NE_split_total; // total number of elements after mesh splitting
int mesh_points_cnt; // number of mesh nodes
// Tolerance to ignore points found beyond the mesh boundary.
@@ -141,6 +151,12 @@ protected:
double bdr_tol;
// Use CPU functions for Mesh/GridFunction on device for gslib1.0.7
bool gpu_to_cpu_fallback = false;
// Check if a point is inside the oriented bounding box of an
// element before the Newton iteration.
// Note: only used in MFEM implementation (not in gslib) which currently
// supports GPU kernels for area meshes in 2D, volume meshes in 3D,
// and surface meshes in 1D/2D/3D.
bool obb_check = true;
// Device specific data used for FindPoints
struct DEV_STRUCT
@@ -162,11 +178,16 @@ protected:
mutable double surf_dist_tol;
} DEV;
/// Use GSLIB for communication and interpolation
// Helper function to setup and free gslib's crystal router.
void SetupCrystal(); // Called inside Setup and SetupSurf_base
void FreeCrystal(); // Called inside FreeData
/// Use GSLIB for communication and interpolation. Updates field_out on
/// host.
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out,
const int field_out_ordering);
/// Uses GSLIB Crystal Router for communication followed by MFEM's
/// interpolation functions
/// interpolation functions. Updates field_out on host.
virtual void InterpolateGeneral(const GridFunction &field_in,
Vector &field_out,
const int field_out_ordering);
@@ -181,12 +202,26 @@ protected:
IntegrationRule *irule,
int order);
/** @brief Build integration rules at the given @a order for each split mesh
* and store them in @a ir_out. Requires that \ref SetupSplitMeshes has
* already been called. */
virtual void SetupIntegrationRules(const int order,
Array<IntegrationRule *> &ir_out);
/** @brief Helper function that calls \ref SetupSplitMeshes and
* \ref SetupIntegrationRuleForSplitMesh. */
* \ref SetupIntegrationRules. */
virtual void SetupSplitMeshesAndIntegrationRules(const int order);
/// Get GridFunction value at the points expected by GSLIB.
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals) const;
/** @brief Get GridFunction value at the points expected by GSLIB.
* @param[in] gf_in Grid function to evaluate.
* @param[out] node_vals Output values.
* @param[in] ir_in If non-null, use these rules instead of #ir_split.
* @param[in] by_element If true, output has element-major layout
* [nel][vdim][ndofs]; otherwise component-major
* layout [vdim][total_pts]. */
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals,
const Array<IntegrationRule *> *ir_in = nullptr,
bool by_element = false) const;
/** @brief Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For
* simplices, find the original element number (that was split into
@@ -291,29 +326,60 @@ protected:
void findptsedge_setup_2(DEV_STRUCT &devs,
const double *const elx[2],
const unsigned n,
const uint nel,
const unsigned int nel,
const unsigned m,
const double bbox_tol,
const uint local_hash_size,
const uint global_hash_size);
const double bbox_rel_size_inc,
const unsigned int local_hash_size,
const unsigned int global_hash_size,
const Vector *aabb_sz_inc);
/// Preprocess 3D surface mesh needed for FindPoints.
void findptssurf_setup_3(DEV_STRUCT &devs,
const double *const elx[3],
const unsigned n,
const uint nel,
const unsigned int nel,
const unsigned m,
const double bbox_tol,
const uint local_hash_size,
const uint global_hash_size,
const int rD);
const double bbox_rel_size_inc,
const unsigned int local_hash_size,
const unsigned int global_hash_size,
const int rD,
const Vector *aabb_sz_inc);
/** @brief Shared implementation for the public surface-setup methods.
*
* @details Initializes the surface-search data structures, builds the
* split-element representation expected by gslib, and constructs the
* element bounding boxes used by the MFEM surface kernels.
*
* If @a aabb_sz_inc is null, the setup stores the default oriented
* bounding boxes and uses @a bbox_rel_size_inc as their relative size
* increase factor.
*
* If @a aabb_sz_inc is non-null, the setup stores axis-aligned bounding
* boxes only, applies the requested absolute AABB expansion in each
* physical direction, and adjusts the tolerance @a bdr_tol so points
* found in the expanded region are classified as border points.
*
* @param[in] m Input surface mesh.
* @param[in] bbox_rel_size_inc Relative size increase applied when
* expanding each element bounding box during
* setup.
* @param[in] aabb_sz_inc Optional total absolute AABB expansion
* applied to the stored axis-aligned
* bounding boxes after construction.
* @param[in] newt_tol Newton tolerance for the point-search
* kernels.
*/
void SetupSurf_Base(Mesh &m,
const double bbox_rel_size_inc,
const Vector *aabb_sz_inc,
const double newt_tol);
public:
/// Serial constructor
FindPointsGSLIB();
/// Serial constructor + setup with given Mesh (see \ref Setup)
FindPointsGSLIB(Mesh &mesh_in, const double bb_t = 0.1,
FindPointsGSLIB(Mesh &mesh_in, const double bbox_rel_size_inc = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
@@ -322,7 +388,7 @@ public:
FindPointsGSLIB(MPI_Comm comm_);
/// Constructor + setup with given ParMesh (see \ref Setup)
FindPointsGSLIB(ParMesh &mesh_in, const double bb_t = 0.1,
FindPointsGSLIB(ParMesh &mesh_in, const double bbox_rel_size_inc = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
#endif
@@ -338,23 +404,59 @@ public:
Note: not tested with periodic (L2).
Note: the input mesh \p m must have Nodes set.
@param[in] m Input mesh.
@param[in] bb_t (Optional) Relative size of bounding box around
each element.
@param[in] newt_tol (Optional) Newton tolerance for the gslib
search methods.
@param[in] npt_max (Optional) Number of points for simultaneous
iteration. This alters performance and
memory footprint.
@param[in] m Input mesh.
@param[in] bbox_rel_size_inc (Optional) Relative size increase applied
when expanding each element bounding box.
@param[in] newt_tol (Optional) Newton tolerance for the gslib
search methods.
@param[in] npt_max (Optional) Number of points for
simultaneous iteration. This alters
performance and memory footprint.
*/
void Setup(Mesh &m, const double bb_t = 0.1, const double newt_tol = 1.0e-12,
void Setup(Mesh &m, const double bbox_rel_size_inc = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
/// Preprocess the surface mesh to compute data for FindPoints.
void SetupSurf(Mesh &m,
const double bb_t = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
const double bbox_rel_size_inc = 0.1,
const double newt_tol = 1.0e-12);
/** @brief Preprocess the surface mesh to compute data for FindPoints using
* absolute AABB expansion.
*
* @details This method computes only axis-aligned bounding boxes and
* increases their total length by a user-specified amount in each
* physical direction. The absolute AABB expansion is applied
* symmetrically to the lower and upper bounds.
*
* The size of @a aabb_sz_inc determines how the expansion values are
* interpreted:
* - `1`: one expansion value used in every direction for every element
* - `NElements`: one expansion value per element, reused in x/y/z
* directions
* - `SpaceDim`: one expansion value per physical direction, reused for
* every element
* - `NElements*SpaceDim`: one expansion value per element and direction,
* ordered as `(dx1,dy1,dz1, ... dxN,dyN,dzN)`
*
* This method disables the oriented bounding-box precheck because the
* stored boxes are modified only in their axis-aligned representation.
*
* @param[in] m Input surface mesh.
* @param[in] aabb_sz_inc Total absolute AABB expansion applied in
* each physical direction to the stored
* axis-aligned bounding boxes.
* @param[in] newt_tol Newton tolerance for the point-search
* kernels.
*
* @note We disable the oriented bounding box check with this setup.
* @a bdr_tol is also adjusted so that all points in the AABBs can
* be found.
*/
void SetupSurfWithAABBExpansion(Mesh &m, const Vector &aabb_sz_inc,
const double newt_tol = 1.0e-12);
/** @brief Searches positions given in physical space by \p point_pos.
@@ -401,7 +503,8 @@ public:
/// Setup FindPoints and search positions
void FindPoints(Mesh &m, const Vector &point_pos,
const int point_pos_ordering = Ordering::byNODES,
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
const double bbox_rel_size_inc = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
/** @brief Interpolation of field values at prescribed reference space
@@ -413,7 +516,11 @@ public:
mesh that was given to Setup().
@param[out] field_out Interpolated values. For points that are not found
the value is set to #default_interp_value.
The output ordering is determined from field_in.*/
The output ordering is determined from field_in.
@note: field_out is moved to device if field_in is on device. Otherwise,
field_out memory allocation is not changed.
*/
virtual void Interpolate(const GridFunction &field_in, Vector &field_out);
/// Interpolation of field values, with output ordering specification.
@@ -468,7 +575,12 @@ public:
* @details When using FindPoints, gslib may return points as found on the
* boundary even when they are slightly outside the domain. This tolerance
* is used to filter such points based on the distance^2 value and mark them
* as not found.*/
* as not found.
*
* @note When the SetupSurfWithAABBExpansion method is used for surface
* meshes, this tolerance is automatically computed based on the size of
* expanded AABBs. Using this method will override that computed tolerance.
* */
virtual void SetDistanceToleranceForPointsFoundOnBoundary(double bdr_tol_)
{
bdr_tol = bdr_tol_;
@@ -603,25 +715,28 @@ public:
Note: not tested with periodic meshes (L2).
Note: the input mesh \p m must have Nodes set.
@param[in] m Input mesh.
@param[in] meshid A unique # for each overlapping mesh. This id is
used to make sure that points being searched are not
looked for in the mesh that they belong to.
@param[in] gfmax (Optional) GridFunction in H1 that is used as a
discriminator when one point is located in multiple
meshes. The mesh that maximizes gfmax is chosen.
For example, using the distance field based on the
overlapping boundaries is helpful for convergence
during Schwarz iterations.
@param[in] bb_t (Optional) Relative size of bounding box around
each element.
@param[in] newt_tol (Optional) Newton tolerance for the gslib
search methods.
@param[in] npt_max (Optional) Number of points for simultaneous
iteration. This alters performance and
memory footprint.*/
void Setup(Mesh &m, const int meshid, GridFunction *gfmax = NULL,
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
@param[in] m Input mesh.
@param[in] meshid A unique # for each overlapping mesh.
This id is used to make sure that points
being searched are not looked for in the
mesh that they belong to.
@param[in] gfmax (Optional) GridFunction in H1 that is used
as a discriminator when one point is
located in multiple meshes. The mesh that
maximizes gfmax is chosen. For example,
using the distance field based on the
overlapping boundaries is helpful for
convergence during Schwarz iterations.
@param[in] bbox_rel_size_inc (Optional) Relative size increase applied
when expanding each element bounding box.
@param[in] newt_tol (Optional) Newton tolerance for the gslib
search methods.
@param[in] npt_max (Optional) Number of points for
simultaneous iteration. This alters
performance and memory footprint.*/
void Setup(Mesh &m, const int meshid, GridFunction *gfmax = nullptr,
const double bbox_rel_size_inc = 0.1,
const double newt_tol = 1.0e-12,
const int npt_max = 256);
/** Searches positions given in physical space by \p point_pos. All output
@@ -677,7 +792,7 @@ class GSOPGSLIB
protected:
struct gslib::crystal *cr; // gslib's internal data
struct gslib::comm *gsl_comm; // gslib's internal data
struct gslib::gs_data *gsl_data = NULL;
struct gslib::gs_data *gsl_data = nullptr;
int num_ids;
public:
+64 -170
View File
@@ -11,7 +11,7 @@
#include "../gslib.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/kernels.hpp"
#include "gslib_kernel_helpers.hpp"
#ifdef MFEM_USE_GSLIB
@@ -27,8 +27,6 @@
#pragma GCC diagnostic pop
#endif
#include <climits>
namespace mfem
{
#if GSLIB_RELEASE_VERSION >= 10009
@@ -54,127 +52,14 @@ struct findptsElementGPT_t
double x[DIM], jac[DIM * DIM], hes[4];
};
struct dbl_range_t
{
double min, max;
};
struct obbox_t
{
double c0[DIM], A[DIM * DIM];
dbl_range_t x[DIM];
};
struct findptsLocalHashData_t
{
int hash_n;
dbl_range_t bnd[DIM];
double fac[DIM];
unsigned int *offset;
int max;
};
// Eval the ith Lagrange interpolant and its first derivative at x.
// Note: lCoeff stores pre-computed coefficients for fast evaluation.
static MFEM_HOST_DEVICE inline void lag_eval_first_der(double *p0, double x,
int i, const double *z,
const double *lCoeff,
int pN)
{
double u0 = 1, u1 = 0;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
double d_j = 2 * (x - z[j]);
u1 = d_j * u1 + u0;
u0 = d_j * u0;
}
}
p0[i] = lCoeff[i] * u0;
p0[pN+i] = 2.0 * lCoeff[i] * u1;
}
// Eval the ith Lagrange interpolant and its first and second derivative at x.
// Note: lCoeff stores pre-computed coefficients for fast evaluation.
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
int i, const double *z,
const double *lCoeff,
int pN)
{
double u0 = 1, u1 = 0, u2 = 0;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
double d_j = 2 * (x - z[j]);
u2 = d_j * u2 + u1;
u1 = d_j * u1 + u0;
u0 = d_j * u0;
}
}
p0[i] = lCoeff[i] * u0;
p0[pN+i] = 2.0 * lCoeff[i] * u1;
p0[2*pN+i] = 8.0 * lCoeff[i] * u2;
}
// Axis-aligned bounding box test.
static MFEM_HOST_DEVICE inline double AABB_test(const obbox_t *const b,
const double x[2])
{
double test = 1;
for (int d = 0; d < 2; ++d)
{
double b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
test = test < 0 ? test : b_d;
}
return test;
}
// Axis-aligned bounding box test followed by oriented bounding-box test.
static MFEM_HOST_DEVICE inline double bbox_test(const obbox_t *const b,
const double x[2])
{
const double bxyz = AABB_test(b, x);
if (bxyz < 0)
{
return bxyz;
}
else
{
double dxyz[2];
for (int d = 0; d < 2; ++d)
{
dxyz[d] = x[d] - b->c0[d];
}
double test = 1;
for (int d = 0; d < 2; ++d)
{
double rst = 0;
for (int e = 0; e < 2; ++e)
{
rst += b->A[d * 2 + e] * dxyz[e];
}
double brst = (rst + 1) * (1 - rst);
test = test < 0 ? test : brst;
}
return test;
}
}
// Element index corresponding to hash mesh that the point is located in.
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
const double x[2])
{
const int n = p->hash_n;
int sum = 0;
for (int d = 2 - 1; d >= 0; --d)
{
sum *= n;
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
sum += i < 0 ? 0 : (n - 1 < i ? n - 1 : i);
}
return sum;
}
using dbl_range_t = gslib::dbl_range_t;
using obbox_t = gslib::obbox_t<DIM>;
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<DIM>;
using gslib::bbox_test;
using gslib::hash_index;
using gslib::l2norm2;
using gslib::lag_eval_first_der;
using gslib::lag_eval_second_der;
/*Solve Ax=y. A is row-major */
static MFEM_HOST_DEVICE inline void lin_solve_2(double x[2], const double A[4],
@@ -185,12 +70,6 @@ static MFEM_HOST_DEVICE inline void lin_solve_2(double x[2], const double A[4],
x[1] = idet*(A[0]*y[1] - A[2]*y[0]);
}
/* L2 norm squared. */
static MFEM_HOST_DEVICE inline double l2norm2(const double x[2])
{
return x[0] * x[0] + x[1] * x[1];
}
/* the bit structure of flags is CSSRR
the C bit --- 1<<4 --- is set when the point is converged
RR is 0 = 00b if r is unconstrained,
@@ -352,7 +231,7 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *res,
const findptsElementPoint_t *p,
const double tol)
{
const double dist2 = l2norm2(resid);
const double dist2 = l2norm2<2>(resid);
const double decr = p->dist2 - dist2;
const double pred = p->dist2p;
for (int d = 0; d < 2; ++d)
@@ -695,25 +574,25 @@ static MFEM_HOST_DEVICE double tensor_ig2_j(double *g_partials,
}
template<int T_D1D = 0>
static void FindPointsLocal2D_Kernel(const int npt,
const double tol,
const double *x,
const int point_pos_ordering,
const double *xElemCoord,
const int nel,
const double *wtend,
const double *boxinfo,
const int hash_n,
const double *hashMin,
const double *hashFac,
unsigned int *hashOffset,
unsigned int *const code_base,
unsigned int *const el_base,
double *const r_base,
double *const dist2_base,
const double *gll1D,
const double *lagcoeff,
const int pN = 0)
static void FindPointsLocal2DKernel(const int npt,
const double tol,
const double *x,
const int point_pos_ordering,
const double *xElemCoord,
const int nel,
const double *wtend,
const double *boxinfo,
const int hash_n,
const double *hashMin,
const double *hashFac,
unsigned int *hashOffset,
unsigned int *const code_base,
unsigned int *const el_base,
double *const r_base,
double *const dist2_base,
const double *gll1D,
const double *lagcoeff,
const int pN = 0)
{
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
const int D1D = T_D1D ? T_D1D : pN;
@@ -1175,30 +1054,45 @@ void FindPointsGSLIB::FindPointsLocal2(const Vector &point_pos,
switch (DEV.dof1d)
{
case 2:
return FindPointsLocal2D_Kernel<2>(
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
pgll1d, plc);
FindPointsLocal2DKernel<2>(npt, DEV.newt_tol, pp,
point_pos_ordering, pgslm,
NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
case 3:
return FindPointsLocal2D_Kernel<3>(
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
pgll1d, plc);
FindPointsLocal2DKernel<3>(npt, DEV.newt_tol, pp,
point_pos_ordering, pgslm,
NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
case 4:
return FindPointsLocal2D_Kernel<4>(
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
pgll1d, plc);
FindPointsLocal2DKernel<4>(npt, DEV.newt_tol, pp,
point_pos_ordering, pgslm,
NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
case 5:
return FindPointsLocal2D_Kernel<5>(
npt, DEV.newt_tol, pp, point_pos_ordering, pgslm, NE_split_total, pwt,
pbb, DEV.lh_nx, plhm, plhf, plho, pcode, pelem, pref, pdist,
pgll1d, plc);
FindPointsLocal2DKernel<5>(npt, DEV.newt_tol, pp,
point_pos_ordering, pgslm,
NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
default:
return FindPointsLocal2D_Kernel(npt, DEV.newt_tol, pp, point_pos_ordering,
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx,
plhm, plhf, plho, pcode, pelem,
pref, pdist, pgll1d, plc, DEV.dof1d);
FindPointsLocal2DKernel(npt, DEV.newt_tol, pp,
point_pos_ordering, pgslm,
NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc, DEV.dof1d);
break;
}
}
#undef DIM2
+29 -157
View File
@@ -11,9 +11,7 @@
#include "../gslib.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/kernels.hpp"
#include <climits>
#include "gslib_kernel_helpers.hpp"
#ifdef MFEM_USE_GSLIB
@@ -59,128 +57,15 @@ struct findptsElemPt
double x[DIM], jac[DIM * DIM], hes[18];
};
struct dbl_range_t
{
double min, max;
};
struct obbox_t
{
double c0[DIM], A[DIM * DIM];
dbl_range_t x[DIM];
};
struct findptsLocalHashData_t
{
int hash_n;
dbl_range_t bnd[DIM];
double fac[DIM];
unsigned int *offset;
// int max;
};
// Eval the ith Lagrange interpolant and its first derivative at x.
// Note: lCoeff stores pre-computed coefficients for fast evaluation.
static MFEM_HOST_DEVICE inline void lag_eval_first_der(double *p0, double x,
int i, const double *z,
const double *lCoeff,
int pN)
{
double u0 = 1, u1 = 0;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
double d_j = 2*(x-z[j]);
u1 = d_j*u1+u0;
u0 = d_j*u0;
}
}
p0[i] = lCoeff[i]*u0;
p0[pN+i] = 2.0*lCoeff[i]*u1;
}
// Eval the ith Lagrange interpolant and its first and second derivative at x.
// Note: lCoeff stores pre-computed coefficients for fast evaluation.
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
int i, const double *z,
const double *lCoeff,
int pN)
{
double u0 = 1, u1 = 0, u2 = 0;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
double d_j = 2*(x-z[j]);
u2 = d_j*u2+u1;
u1 = d_j*u1+u0;
u0 = d_j*u0;
}
}
p0[i] = lCoeff[i]*u0;
p0[pN+i] = 2.0*lCoeff[i]*u1;
p0[2*pN+i] = 8.0*lCoeff[i]*u2;
}
// Axis-aligned bounding box test.
static MFEM_HOST_DEVICE inline double AABB_test(const obbox_t *const b,
const double x[3])
{
double b_d;
for (int d = 0; d < 3; ++d)
{
b_d = (x[d]-b->x[d].min)*(b->x[d].max-x[d]);
if (b_d < 0) { return b_d; }
}
return b_d;
}
// Axis-aligned bounding box test followed by oriented bounding-box test.
static MFEM_HOST_DEVICE inline double bbox_test(const obbox_t *const b,
const double x[3])
{
const double bxyz = AABB_test(b, x);
if (bxyz < 0)
{
return bxyz;
}
else
{
double dxyz[3];
for (int d = 0; d < 3; ++d)
{
dxyz[d] = x[d]-b->c0[d];
}
double test = 1;
for (int d = 0; d < 3; ++d)
{
double rst = 0;
for (int e = 0; e < 3; ++e)
{
rst += b->A[d*3+e]*dxyz[e];
}
double brst = (rst+1)*(1-rst);
test = test < 0 ? test : brst;
}
return test;
}
}
// Element index corresponding to hash mesh that the point is located in.
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
const double x[3])
{
const int n = p->hash_n;
int sum = 0;
for (int d = 3-1; d >= 0; --d)
{
sum *= n;
int i = (int)floor((x[d]-p->bnd[d].min)*p->fac[d]);
sum += i < 0 ? 0 : (n-1 < i ? n-1 : i);
}
return sum;
}
using dbl_range_t = gslib::dbl_range_t;
using obbox_t = gslib::obbox_t<DIM>;
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<DIM>;
using gslib::bbox_test;
using gslib::hash_index;
using gslib::l2norm2;
using gslib::lag_eval_first_der;
using gslib::lag_eval_second_der;
using gslib::lin_solve_sym_2;
// Solve Ax=y. A is row-major.
static MFEM_HOST_DEVICE inline void lin_solve_3(double x[3], const double A[9],
@@ -199,22 +84,6 @@ static MFEM_HOST_DEVICE inline void lin_solve_3(double x[3], const double A[9],
x[2] = idet*(inv6*y[0]+inv7*y[1]+inv8*y[2]);
}
// Solve Ax=y. A is a symmetric 2x2 matrix.
static MFEM_HOST_DEVICE inline void lin_solve_sym_2(double x[2],
const double A[3],
const double y[2])
{
const double idet = 1 / (A[0]*A[2]-A[1]*A[1]);
x[0] = idet*(A[2]*y[0]-A[1]*y[1]);
x[1] = idet*(A[0]*y[1]-A[1]*y[0]);
}
// L2 norm.
static MFEM_HOST_DEVICE inline double l2norm2(const double x[3])
{
return x[0]*x[0]+x[1]*x[1]+x[2]*x[2];
}
/* the bit structure of flags is CTTSSRR
the C bit --- 1<<6 --- is set when the point is converged
RR is 0 = 00b if r is unconstrained,
@@ -459,7 +328,7 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsPt *res,
const findptsPt *p,
const double tol)
{
const double dist2 = l2norm2(resid);
const double dist2 = l2norm2<3>(resid);
const double decr = p->dist2-dist2;
const double pred = p->dist2p;
for (int d = 0; d < 3; ++d)
@@ -1809,33 +1678,36 @@ void FindPointsGSLIB::FindPointsLocal3(const Vector &point_pos,
{
case 2:
FindPointsLocal3DKernel<2>(npt, DEV.newt_tol, pp, point_pos_ordering,
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
plc);
pgslm, NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist, pgll1d, plc);
break;
case 3:
FindPointsLocal3DKernel<3>(npt, DEV.newt_tol, pp, point_pos_ordering,
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
plc);
pgslm, NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist, pgll1d, plc);
break;
case 4:
FindPointsLocal3DKernel<4>(npt, DEV.newt_tol, pp, point_pos_ordering,
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
plc);
pgslm, NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist, pgll1d, plc);
break;
case 5:
FindPointsLocal3DKernel<5>(npt, DEV.newt_tol, pp, point_pos_ordering,
pgslm, NE_split_total, pwt, pbb, DEV.lh_nx, plhm,
plhf, plho, pcode, pelem, pref, pdist, pgll1d,
plc);
pgslm, NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist, pgll1d, plc);
break;
default:
FindPointsLocal3DKernel(npt, DEV.newt_tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc,
FindPointsLocal3DKernel(npt, DEV.newt_tol, pp,
point_pos_ordering, pgslm,
NE_split_total, pwt, pbb,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist, pgll1d, plc,
DEV.dof1d);
break;
}
}
#undef pMax
+107 -176
View File
@@ -11,6 +11,7 @@
#include "../gslib.hpp"
#include "../../general/forall.hpp"
#include "gslib_kernel_helpers.hpp"
#ifdef MFEM_USE_GSLIB
@@ -52,113 +53,14 @@ struct findptsElementGPT_t
double x[sDIM], jac[sDIM*rDIM], hes[sDIM*rDIM];
};
struct dbl_range_t
{
double min, max;
};
struct obbox_t
{
double c0[sDIM], A[sDIM*sDIM];
dbl_range_t x[sDIM];
};
struct findptsLocalHashData_t
{
int hash_n;
dbl_range_t bnd[sDIM];
double fac[sDIM];
unsigned int *offset;
};
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
int i, const double *z,
const double *lCoeff,
int pN)
{
double u0 = 1, u1 = 0, u2 = 0;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
double d_j = 2 * (x-z[j]);
u2 = d_j * u2 + u1;
u1 = d_j * u1 + u0;
u0 = d_j * u0;
}
}
double *p1 = p0 + pN, *p2 = p0 + 2 * pN;
p0[i] = lCoeff[i] * u0;
p1[i] = 2.0 * lCoeff[i] * u1;
p2[i] = 8.0 * lCoeff[i] * u2;
}
/* positive when possibly inside */
static MFEM_HOST_DEVICE inline double obbox_axis_test(const obbox_t *const b,
const double x[sDIM])
{
double b_d;
for (int d=0; d<sDIM; ++d)
{
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
if (b_d < 0) // if outside in any dimension
{
return b_d;
}
}
return b_d; // only positive if inside
}
/* positive when given point is possibly inside given obbox b */
static MFEM_HOST_DEVICE inline double obbox_test(const obbox_t *const b,
const double x[sDIM])
{
const double bxyz = obbox_axis_test(b,x);
if (bxyz<0) // test if point is in AABB
{
return bxyz;
}
else // test OBB only if inside AABB
{
double dxyz[sDIM];
for (int d=0; d<sDIM; ++d)
{
dxyz[d] = x[d] - b->c0[d];
}
double test = 1;
for (int d=0; d<sDIM; ++d)
{
double rst = 0;
for (int e=0; e<sDIM; ++e)
{
rst += b->A[d*2 + e] * dxyz[e];
}
double brst = (rst+1)*(1-rst);
test = test<0 ? test : brst;
}
return test;
}
}
/* Hash index in the hash table to the elements that possibly contain the point x */
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
const double x[2])
{
const int n = p->hash_n;
int sum = 0;
for (int d=sDIM-1; d>=0; --d)
{
sum *= n;
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
}
return sum;
}
static MFEM_HOST_DEVICE inline double l2norm2(const double x[2])
{
return x[0] * x[0] + x[1] * x[1];
}
using dbl_range_t = gslib::dbl_range_t;
using obbox_t = gslib::obbox_t<sDIM>;
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<sDIM>;
using gslib::AABB_test;
using gslib::bbox_test;
using gslib::hash_index;
using gslib::l2norm2;
using gslib::lag_eval_second_der;
/* the bit structure of flags is CRR
the C bit --- 1<<2 --- is set when the point is converged
@@ -187,29 +89,29 @@ static MFEM_HOST_DEVICE inline int point_index(const int x)
/* check reduction in objective against prediction, and adjust
trust region radius (p->tr) accordingly;
may reject the prior step, returning 1; otherwise returns 0
sets out->dist2, out->index, out->x, out->oldr in any event,
leaving out->r, out->dr, out->flags to be set when returning 0 */
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
sets out_pt->dist2, out_pt->index, out_pt->x, out_pt->oldr in any event,
leaving out_pt->r, out_pt->dr, out_pt->flags to be set when returning 0 */
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out_pt,
const double resid[2],
const findptsElementPoint_t *p,
const double tol)
{
const double dist2 = l2norm2(resid);
const double dist2 = l2norm2<2>(resid);
const double decr = p->dist2 - dist2;
const double pred = p->dist2p;
out->x[0] = p->x[0];
out->x[1] = p->x[1];
out->oldr = p->r;
out->dist2 = dist2;
out_pt->x[0] = p->x[0];
out_pt->x[1] = p->x[1];
out_pt->oldr = p->r;
out_pt->dist2 = dist2;
if (decr >= 0.01*pred)
{
if (decr >= 0.9*pred) // very good iteration
{
out->tr = p->tr*2;
out_pt->tr = p->tr*2;
}
else // somewhat good iteration
{
out->tr = p->tr;
out_pt->tr = p->tr;
}
return false;
}
@@ -220,21 +122,21 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
"very good iteration" --- this doubles the trust radius,
which is why we divide by 4 below */
double v0 = fabs(p->r - p->oldr);
out->tr = v0/4.0;
out->dist2 = p->dist2;
out->r = p->oldr;
out->flags = p->flags>>3;
out->dist2p = -HUGE_VAL;
out_pt->tr = v0/4.0;
out_pt->dist2 = p->dist2;
out_pt->r = p->oldr;
out_pt->flags = p->flags>>3;
out_pt->dist2p = -HUGE_VAL;
if (pred < dist2*tol)
{
out->flags |= CONVERGED_FLAG;
out_pt->flags |= CONVERGED_FLAG;
}
return true;
}
}
static MFEM_HOST_DEVICE inline void newton_edge( findptsElementPoint_t *const
out,
out_pt,
const double jac[2],
const double rhess,
const double resid[2],
@@ -304,9 +206,9 @@ newton_edge_fin:
{
new_flags |= CONVERGED_FLAG;
}
out->r = newr;
out->dist2p = -v;
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
out_pt->r = newr;
out_pt->dist2p = -v;
out_pt->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
}
static MFEM_HOST_DEVICE void seed_j( const double *elx[sDIM],
@@ -332,26 +234,27 @@ static MFEM_HOST_DEVICE void seed_j( const double *elx[sDIM],
}
template<int T_D1D = 0>
static void FindPointsEdgeLocal2D_Kernel( const int npt,
const double tol,
const double dist2tol,
const double *x,
const int point_pos_ordering,
const double *xElemCoord,
const int nel,
const double *wtend,
const double *boxinfo,
const int hash_n,
const double *hashMin,
const double *hashFac,
unsigned int *hashOffset,
unsigned int *const code_base,
unsigned int *const el_base,
double *const r_base,
double *const dist2_base,
const double *gll1D,
const double *lagcoeff,
const int pN = 0 )
static void FindPointsEdgeLocal2DKernel( const int npt,
const double tol,
const double dist2tol,
const double *x,
const int point_pos_ordering,
const double *xElemCoord,
const int nel,
const double *wtend,
const double *boxinfo,
const bool obb_check,
const int hash_n,
const double *hashMin,
const double *hashFac,
unsigned int *hashOffset,
unsigned int *const code_base,
unsigned int *const el_base,
double *const r_base,
double *const dist2_base,
const double *gll1D,
const double *lagcoeff,
const int pN = 0 )
{
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
const int D1D = T_D1D ? T_D1D : pN;
@@ -412,22 +315,34 @@ static void FindPointsEdgeLocal2D_Kernel( const int npt,
{
const unsigned int el = *elp;
const int n_box_ents = obb_check ? (3*sDIM + sDIM2) : (2*sDIM);
bool pass_bb = true;
obbox_t box;
int n_box_ents = 3*sDIM + sDIM2;
for (int idx = 0; idx < sDIM; ++idx)
if (obb_check)
{
box.c0[idx] = boxinfo[n_box_ents*el + idx];
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
for (int idx = 0; idx < sDIM; ++idx)
{
box.c0[idx] = boxinfo[n_box_ents*el + idx];
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
}
for (int idx = 0; idx < sDIM2; ++idx)
{
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
}
pass_bb = (bbox_test(&box, x_i) >= 0);
}
else
{
for (int d = 0; d < sDIM; ++d)
{
box.x[d].min = boxinfo[n_box_ents*el + d];
box.x[d].max = boxinfo[n_box_ents*el + sDIM + d];
}
pass_bb = (AABB_test(&box, x_i) >= 0);
}
for (int idx = 0; idx < sDIM2; ++idx)
{
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
}
if (obbox_test(&box,x_i)>=0)
if (pass_bb)
{
//------------ findpts_local ------------------
{
@@ -516,11 +431,14 @@ static void FindPointsEdgeLocal2D_Kernel( const int npt,
double *hess = jac + sDIM*rDIM;
findptsElementGEdge_t edge;
for (int d=0; d<sDIM; ++d)
{
edge.x[d] = constraint_workspace + d*D1D;
}
MFEM_FOREACH_THREAD(j,x,D1D)
{
for (int d=0; d<sDIM; ++d)
{
edge.x[d] = constraint_workspace + d*D1D;
edge.x[d][j] = elx[d][j];
}
}
@@ -681,28 +599,41 @@ void FindPointsGSLIB::FindPointsEdgeLocal2( const Vector &point_pos,
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
auto plc = DEV.lagcoeff.Read(use_dev);
double dist2tol = DEV.surf_dist_tol;
const bool obb_chk = obb_check;
switch (DEV.dof1d)
{
case 2:
return FindPointsEdgeLocal2D_Kernel<2>(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc);
FindPointsEdgeLocal2DKernel<2>(npt, DEV.newt_tol, dist2tol,
pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
case 3:
return FindPointsEdgeLocal2D_Kernel<3>(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc);
FindPointsEdgeLocal2DKernel<3>(npt, DEV.newt_tol, dist2tol,
pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
case 4:
return FindPointsEdgeLocal2D_Kernel<4>(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc);
FindPointsEdgeLocal2DKernel<4>(npt, DEV.newt_tol, dist2tol,
pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
default:
return FindPointsEdgeLocal2D_Kernel(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
FindPointsEdgeLocal2DKernel(npt, DEV.newt_tol, dist2tol, pp,
point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc, DEV.dof1d);
break;
}
}
#undef sDIM
+109 -181
View File
@@ -11,6 +11,7 @@
#include "../gslib.hpp"
#include "../../general/forall.hpp"
#include "gslib_kernel_helpers.hpp"
#ifdef MFEM_USE_GSLIB
@@ -54,117 +55,14 @@ struct findptsElementGPT_t
double x[sDIM], jac[sDIM], hes[sDIM*(1+1)];
};
struct dbl_range_t
{
double min, max;
};
struct obbox_t
{
double c0[sDIM], A[sDIM*sDIM];
dbl_range_t x[sDIM];
};
struct findptsLocalHashData_t
{
int hash_n;
dbl_range_t bnd[sDIM];
double fac[sDIM];
unsigned int *offset;
};
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
int i, const double *z,
const double *lCoeff,
int pN)
{
double u0 = 1, u1 = 0, u2 = 0;
for (int j=0; j<pN; ++j)
{
if (i!=j)
{
double d_j = 2 * (x-z[j]);
u2 = d_j * u2 + u1;
u1 = d_j * u1 + u0;
u0 = d_j * u0;
}
}
double *p1 = p0 + pN, *p2 = p0 + 2 * pN;
p0[i] = lCoeff[i] * u0;
p1[i] = 2.0 * lCoeff[i] * u1;
p2[i] = 8.0 * lCoeff[i] * u2;
}
/* positive when possibly inside */
static MFEM_HOST_DEVICE inline double obbox_axis_test(const obbox_t *const b,
const double x[sDIM])
{
double b_d;
for (int d=0; d<sDIM; ++d)
{
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
if (b_d < 0) // if outside in any dimension
{
return b_d;
}
}
return b_d; // only positive if inside in all dimensions
}
/* positive when possibly inside */
static MFEM_HOST_DEVICE inline double obbox_test(const obbox_t *const b,
const double x[sDIM])
{
const double bxyz = obbox_axis_test(b, x);
if (bxyz<0)
{
return bxyz;
}
else
{
double dxyz[3];
// dxyz: distance of the point from the center of the OBB
for (int d=0; d<sDIM; ++d)
{
dxyz[d] = x[d] - b->c0[d];
}
// transform dxyz to the local coordinate system of the OBB,
// and check if the point is inside the OBB [-1,1]^sDIM
double test = 1;
for (int d=0; d<sDIM; ++d)
{
double rst = 0;
for (int e=0; e<sDIM; ++e)
{
rst += b->A[d*sDIM + e] * dxyz[e];
}
double brst = (rst+1)*(1-rst);
test = test<0 ? test : brst;
}
return test;
}
}
/* Hash index in the hash table to the elements that possibly contain the point x */
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
const double x[sDIM])
{
const int n = p->hash_n;
int sum = 0;
for (int d=sDIM-1; d>=0; --d)
{
sum *= n;
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
}
return sum;
}
static MFEM_HOST_DEVICE inline double norm2(const double x[sDIM])
{
return ( x[0]*x[0] + x[1]*x[1] + x[2]*x[2] );
}
using dbl_range_t = gslib::dbl_range_t;
using obbox_t = gslib::obbox_t<sDIM>;
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<sDIM>;
using gslib::AABB_test;
using gslib::bbox_test;
using gslib::hash_index;
using gslib::l2norm2;
using gslib::lag_eval_second_der;
/* the bit structure of flags is CRR
the C bit --- 1<<2 --- is set when the point is converged
@@ -175,47 +73,46 @@ static MFEM_HOST_DEVICE inline double norm2(const double x[sDIM])
#define CONVERGED_FLAG (1u<<2)
#define FLAG_MASK 0x07u
/* returns the number of constrained reference coordinates, max 2
/* returns the number of constrained reference coordinates, max 1
*/
static MFEM_HOST_DEVICE inline int num_constrained(const int flags)
{
const int y = (flags | flags>>1);
return (y & 1u) + (y>>2 & 1u);
return ((flags | flags>>1) & 1u);
}
static MFEM_HOST_DEVICE inline int point_index(const int x)
{
return ((x>>1)&1u) | ((x>>2)&2u);
return ((x>>1)&1u);
}
/* check reduction in objective against prediction, and adjust
trust region radius (p->tr) accordingly;
may reject the prior step, returning 1; otherwise returns 0
sets out->dist2, out->index, out->x, out->oldr in any event,
leaving out->r, out->dr, out->flags to be set when returning 0 */
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
sets out_pt->dist2, out_pt->index, out_pt->x, out_pt->oldr in any event,
leaving out_pt->r, out_pt->dr, out_pt->flags to be set when returning 0 */
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out_pt,
const double resid[3],
const findptsElementPoint_t *p,
const double tol)
{
const double dist2 = norm2(resid);
const double dist2 = l2norm2<sDIM>(resid);
const double decr = p->dist2 - dist2;
const double pred = p->dist2p;
for (int d=0; d<sDIM; ++d)
{
out->x[d] = p->x[d];
out_pt->x[d] = p->x[d];
}
out->oldr = p->r;
out->dist2 = dist2;
out_pt->oldr = p->r;
out_pt->dist2 = dist2;
if (decr>=0.01*pred)
{
if (decr>=0.9*pred) // very good iteration
{
out->tr = 2*p->tr;
out_pt->tr = 2*p->tr;
}
else // good iteration
{
out->tr = p->tr;
out_pt->tr = p->tr;
}
return false;
}
@@ -226,21 +123,21 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
"very good iteration" --- this doubles the trust radius,
which is why we divide by 4 below */
double v0 = fabs(p->r - p->oldr);
out->tr = v0/4.0;
out->dist2 = p->dist2;
out->r = p->oldr;
out->flags = p->flags>>3;
out->dist2p = -HUGE_VAL;
out_pt->tr = v0/4.0;
out_pt->dist2 = p->dist2;
out_pt->r = p->oldr;
out_pt->flags = p->flags>>3;
out_pt->dist2p = -HUGE_VAL;
if (pred<dist2*tol)
{
out->flags |= CONVERGED_FLAG;
out_pt->flags |= CONVERGED_FLAG;
}
return true;
}
}
static MFEM_HOST_DEVICE inline void newton_edge(findptsElementPoint_t *const
out,
out_pt,
const double jac[sDIM*rDIM],
const double rhes,
const double resid[sDIM],
@@ -314,9 +211,9 @@ newton_edge_fin:
{
new_flags |= CONVERGED_FLAG;
}
out->r = nr;
out->dist2p = -v;
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
out_pt->r = nr;
out_pt->dist2p = -v;
out_pt->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<3);
#undef EVAL
}
@@ -338,31 +235,32 @@ static MFEM_HOST_DEVICE void seed_j(const double *elx[sDIM],
{
dx[d] = x[d] - elx[d][ir];
}
dist2[ir] = norm2(dx);;
dist2[ir] = l2norm2(dx);
r[ir] = z[ir];
}
template<int T_D1D = 0>
static void FindPointsEdgeLocal3D_Kernel(const int npt,
const double tol,
const double dist2tol,
const double *x,
const int point_pos_ordering,
const double *xElemCoord,
const int nel,
const double *wtend,
const double *boxinfo,
const int hash_n,
const double *hashMin,
const double *hashFac,
unsigned int *hashOffset,
unsigned int *const code_base,
unsigned int *const el_base,
double *const r_base,
double *const dist2_base,
const double *gll1D,
const double *lagcoeff,
const int pN = 0)
static void FindPointsEdgeLocal3DKernel(const int npt,
const double tol,
const double dist2tol,
const double *x,
const int point_pos_ordering,
const double *xElemCoord,
const int nel,
const double *wtend,
const double *boxinfo,
const bool obb_check,
const int hash_n,
const double *hashMin,
const double *hashFac,
unsigned int *hashOffset,
unsigned int *const code_base,
unsigned int *const el_base,
double *const r_base,
double *const dist2_base,
const double *gll1D,
const double *lagcoeff,
const int pN = 0)
{
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
const int D1D = T_D1D ? T_D1D : pN;
@@ -419,21 +317,35 @@ static void FindPointsEdgeLocal3D_Kernel(const int npt,
for (; elp!=ele; ++elp)
{
const unsigned int el = *elp;
const int n_box_ents = obb_check ? (3*sDIM + sDIM2) : (2*sDIM);
bool pass_bb = true;
obbox_t box;
int n_box_ents = 3*sDIM + sDIM2;
for (int idx = 0; idx < sDIM; ++idx)
if (obb_check)
{
box.c0[idx] = boxinfo[n_box_ents*el + idx];
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
for (int idx = 0; idx < sDIM; ++idx)
{
box.c0[idx] = boxinfo[n_box_ents*el + idx];
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
}
for (int idx = 0; idx < sDIM2; ++idx)
{
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
}
pass_bb = (bbox_test(&box, x_i) >= 0);
}
for (int idx = 0; idx < sDIM2; ++idx)
else
{
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
for (int d = 0; d < sDIM; ++d)
{
box.x[d].min = boxinfo[n_box_ents*el + d];
box.x[d].max = boxinfo[n_box_ents*el + sDIM + d];
}
pass_bb = (AABB_test(&box, x_i) >= 0);
}
if (obbox_test(&box, x_i)>=0)
if (pass_bb)
{
//// findpts_local ////
{
@@ -521,11 +433,14 @@ static void FindPointsEdgeLocal3D_Kernel(const int npt,
double *hess = jac + sDIM*rDIM;
findptsElementGEdge_t edge;
for (int d=0; d<sDIM; ++d)
{
edge.x[d] = constraint_workspace + d*D1D;
}
MFEM_FOREACH_THREAD(j,x,D1D)
{
for (int d=0; d<sDIM; ++d)
{
edge.x[d] = constraint_workspace + d*D1D;
edge.x[d][j] = elx[d][j];
}
}
@@ -688,28 +603,41 @@ void FindPointsGSLIB::FindPointsEdgeLocal3(const Vector &point_pos,
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
auto plc = DEV.lagcoeff.Read(use_dev);
double dist2tol = DEV.surf_dist_tol;
const bool obb_chk = obb_check;
switch (DEV.dof1d)
{
case 2:
return FindPointsEdgeLocal3D_Kernel<2>(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc);
FindPointsEdgeLocal3DKernel<2>(npt, DEV.newt_tol, dist2tol,
pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
case 3:
return FindPointsEdgeLocal3D_Kernel<3>(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc);
FindPointsEdgeLocal3DKernel<3>(npt, DEV.newt_tol, dist2tol,
pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
case 4:
return FindPointsEdgeLocal3D_Kernel<4>(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc);
FindPointsEdgeLocal3DKernel<4>(npt, DEV.newt_tol, dist2tol,
pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
default:
return FindPointsEdgeLocal3D_Kernel(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
FindPointsEdgeLocal3DKernel(npt, DEV.newt_tol, dist2tol, pp,
point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc, DEV.dof1d);
break;
}
}
#undef rDIM2
+131 -206
View File
@@ -11,6 +11,7 @@
#include "../gslib.hpp"
#include "../../general/forall.hpp"
#include "gslib_kernel_helpers.hpp"
#ifdef MFEM_USE_GSLIB
#ifdef MFEM_HAVE_GCC_PRAGMA_DIAGNOSTIC
@@ -51,124 +52,15 @@ struct findptsElementGPT_t
double x[sDIM], jac[sDIM*rDIM], hes[sDIM*(rDIM+1)];
};
struct dbl_range_t
{
double min, max;
};
struct obbox_t
{
double c0[sDIM], A[sDIM*sDIM];
dbl_range_t x[sDIM];
};
struct findptsLocalHashData_t
{
int hash_n;
dbl_range_t bnd[sDIM];
double fac[sDIM];
unsigned int *offset;
};
static MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
int i, const double *z,
const double *lCoeff,
int pN)
{
double u0 = 1, u1 = 0, u2 = 0;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
double d_j = 2 * (x - z[j]);
u2 = d_j * u2 + u1;
u1 = d_j * u1 + u0;
u0 = d_j * u0;
}
}
p0[i] = lCoeff[i] * u0;
p0[pN+i] = 2.0 * lCoeff[i] * u1;
p0[2*pN+i] = 8.0 * lCoeff[i] * u2;
}
/* positive when possibly inside */
static MFEM_HOST_DEVICE inline double AABB_test(const obbox_t *const b,
const double x[sDIM])
{
double b_d;
for (int d=0; d<sDIM; ++d)
{
b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
if (b_d < 0) // if outside in any dimension
{
return b_d;
}
}
return b_d; // only positive if inside in all dimensions
}
/* positive when possibly inside */
static MFEM_HOST_DEVICE inline double bbox_test(const obbox_t *const b,
const double x[sDIM])
{
const double bxyz = AABB_test(b, x);
if (bxyz<0)
{
return bxyz;
}
else
{
double dxyz[3];
// dxyz: distance of the point from the center of the OBB
for (int d=0; d<sDIM; ++d)
{
dxyz[d] = x[d] - b->c0[d];
}
// tranform dxyz to the local coordinate system of the OBB,
// and check if the point is inside the OBB [-1,1]^sDIM
double test = 1;
for (int d=0; d<sDIM; ++d)
{
double rst = 0;
for (int e=0; e<sDIM; ++e)
{
rst += b->A[d*sDIM + e] * dxyz[e];
}
double brst = (rst+1)*(1-rst);
test = test<0 ? test : brst;
}
return test;
}
}
/* Hash index in the hash table to the elements that possibly contain the point x */
static MFEM_HOST_DEVICE inline int hash_index(const findptsLocalHashData_t *p,
const double x[sDIM])
{
const int n = p->hash_n;
int sum = 0;
for (int d=sDIM-1; d>=0; --d)
{
sum *= n;
int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
sum += i<0 ? 0 : (n-1 < i ? n-1 : i);
}
return sum;
}
static MFEM_HOST_DEVICE inline void lin_solve_sym_2(double x[2],
const double A[3],
const double y[2])
{
const double idet = 1 / (A[0] * A[2] - A[1] * A[1]);
x[0] = idet * (A[2] * y[0] - A[1] * y[1]);
x[1] = idet * (A[0] * y[1] - A[1] * y[0]);
}
static MFEM_HOST_DEVICE inline double l2norm2(const double x[sDIM])
{
return ( x[0]*x[0] + x[1]*x[1] + x[2]*x[2]);
}
using dbl_range_t = gslib::dbl_range_t;
using obbox_t = gslib::obbox_t<sDIM>;
using findptsLocalHashData_t = gslib::findptsLocalHashData_t<sDIM>;
using gslib::AABB_test;
using gslib::bbox_test;
using gslib::hash_index;
using gslib::l2norm2;
using gslib::lag_eval_second_der;
using gslib::lin_solve_sym_2;
/* the bit structure of flags is CSSRR
the C bit --- 1<<4 --- is set when the point is converged
@@ -219,18 +111,10 @@ static MFEM_HOST_DEVICE inline int point_index(const int x)
return ((x>>1)&1u) | ((x>>2)&2u);
}
static MFEM_HOST_DEVICE inline findptsElementGEdge_t
static MFEM_HOST_DEVICE inline void
get_edge(const double *elx[3], const double *wtend, int ei,
double *workspace, int &side_init, int jidx, int pN)
int &side_init, int jidx, int pN, findptsElementGEdge_t &edge)
{
findptsElementGEdge_t edge;
for (int d=0; d<sDIM; ++d)
{
edge.x[d] = workspace + d*pN;
edge.dxdn[d] = workspace + sDIM*pN + d*pN;
edge.d2xdn[d] = workspace + 2*sDIM*pN + d*pN;
}
// given edge index, compute normal and tangential directions
const int dn = ei>>1, //0 for rmin/rmax, 1 for smin/smax
de = plus_1_mod_2(dn); // 1 for rmin/rmax, 0 for smin/smax
@@ -256,7 +140,6 @@ get_edge(const double *elx[3], const double *wtend, int ei,
edge.d2xdn[dd][jj] = sums_k[1];
#undef ELX
}
return edge;
}
static MFEM_HOST_DEVICE inline findptsElementGPT_t get_pt(const double *elx[3],
@@ -312,34 +195,34 @@ static MFEM_HOST_DEVICE inline findptsElementGPT_t get_pt(const double *elx[3],
/* check reduction in objective against prediction, and adjust
trust region radius (p->tr) accordingly;
may reject the prior step, returning 1; otherwise returns 0
sets out->dist2, out->index, out->x, out->oldr in any event,
leaving out->r, out->dr, out->flags to be set when returning 0 */
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
sets out_pt->dist2, out_pt->index, out_pt->x, out_pt->oldr in any event,
leaving out_pt->r, out_pt->dr, out_pt->flags to be set when returning 0 */
static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out_pt,
const double resid[3],
const findptsElementPoint_t *p,
const double tol)
{
const double dist2 = l2norm2(resid);
const double dist2 = l2norm2<sDIM>(resid);
const double decr = p->dist2 - dist2;
const double pred = p->dist2p;
for (int d=0; d<sDIM; ++d)
{
out->x[d] = p->x[d];
out_pt->x[d] = p->x[d];
}
for (int d=0; d<rDIM; ++d)
{
out->oldr[d] = p->r[d];
out_pt->oldr[d] = p->r[d];
}
out->dist2 = dist2;
out_pt->dist2 = dist2;
if (decr>=0.01*pred)
{
if (decr>=0.9*pred) // very good iteration
{
out->tr = 2*p->tr;
out_pt->tr = 2*p->tr;
}
else // good iteration
{
out->tr = p->tr;
out_pt->tr = p->tr;
}
return false;
}
@@ -351,17 +234,17 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
which is why we divide by 4 below */
double v0 = fabs(p->r[0] - p->oldr[0]),
v1 = fabs(p->r[1] - p->oldr[1]);
out->tr = ( v0>v1 ? v0 : v1 )/4;
out->dist2 = p->dist2;
out->flags = p->flags >> 5;
out->dist2p = -HUGE_VAL;
out_pt->tr = ( v0>v1 ? v0 : v1 )/4;
out_pt->dist2 = p->dist2;
out_pt->flags = p->flags >> 5;
out_pt->dist2p = -HUGE_VAL;
for (int d=0; d<rDIM; ++d)
{
out->r[d] = p->oldr[d];
out_pt->r[d] = p->oldr[d];
}
if (pred<dist2*tol)
{
out->flags |= CONVERGED_FLAG;
out_pt->flags |= CONVERGED_FLAG;
}
return true;
}
@@ -369,7 +252,7 @@ static MFEM_HOST_DEVICE bool reject_prior_step_q(findptsElementPoint_t *out,
/* minimize ||resid - jac * dr||_2, with |dr| <= tr, |r0+dr|<=1
(exact solution of trust region problem) */
static MFEM_HOST_DEVICE void newton_face( findptsElementPoint_t *const out,
static MFEM_HOST_DEVICE void newton_face( findptsElementPoint_t *const out_pt,
const double jac[sDIM*rDIM],
const double rhes[3],
const double resid[sDIM],
@@ -540,19 +423,19 @@ newton_face_constrained:
}
newton_face_fin:
out->dist2p = -2*v;
out_pt->dist2p = -2*v;
dr[0] = r[0] - p->r[0];
dr[1] = r[1] - p->r[1];
if ( fabs(dr[0])+fabs(dr[1]) < tol)
{
new_flags |= CONVERGED_FLAG;
}
out->r[0] = r[0], out->r[1] = r[1];
out->flags = new_flags | ((p->flags & FLAG_MASK)<<5);
out_pt->r[0] = r[0], out_pt->r[1] = r[1];
out_pt->flags = new_flags | ((p->flags & FLAG_MASK)<<5);
}
static MFEM_HOST_DEVICE inline void newton_edge(findptsElementPoint_t *const
out,
out_pt,
const double jac[sDIM*rDIM],
const double rhes,
const double resid[sDIM],
@@ -637,10 +520,10 @@ newton_edge_fin:
{
new_flags |= CONVERGED_FLAG;
}
out->r[de] = nr;
out->r[dn] = p->r[dn];
out->dist2p = -v;
out->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<5);
out_pt->r[de] = nr;
out_pt->r[dn] = p->r[dn];
out_pt->dist2p = -v;
out_pt->flags = flags | new_flags | ((p->flags & FLAG_MASK)<<5);
#undef EVAL
}
@@ -676,26 +559,27 @@ static MFEM_HOST_DEVICE void seed_j(const double *elx[sDIM],
// global memory access of element coordinates.
// Are the structs being stored in "local memory" or registers?
template<int T_D1D = 0>
static void FindPointsSurfLocal3D_Kernel(const int npt,
const double tol,
const double dist2tol,
const double *x,
const int point_pos_ordering,
const double *xElemCoord,
const int nel,
const double *wtend,
const double *boxinfo,
const int hash_n,
const double *hashMin,
const double *hashFac,
unsigned int *hashOffset,
unsigned int *const code_base,
unsigned int *const el_base,
double *const r_base,
double *const dist2_base,
const double *gll1D,
const double *lagcoeff,
const int pN = 0)
static void FindPointsSurfLocal3DKernel(const int npt,
const double tol,
const double dist2tol,
const double *x,
const int point_pos_ordering,
const double *xElemCoord,
const int nel,
const double *wtend,
const double *boxinfo,
const bool obb_check,
const int hash_n,
const double *hashMin,
const double *hashFac,
unsigned int *hashOffset,
unsigned int *const code_base,
unsigned int *const el_base,
double *const r_base,
double *const dist2_base,
const double *gll1D,
const double *lagcoeff,
const int pN = 0)
{
const int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
const int D1D = T_D1D ? T_D1D : pN;
@@ -753,22 +637,36 @@ static void FindPointsSurfLocal3D_Kernel(const int npt,
{
const unsigned int el = *elp;
// construct obbox on the fly
const int n_box_ents = obb_check ? (3*sDIM + sDIM2) : (2*sDIM);
bool pass_bb = true;
obbox_t box;
int n_box_ents = 3*sDIM + sDIM2;
for (int idx = 0; idx < sDIM; ++idx)
if (obb_check)
{
box.c0[idx] = boxinfo[n_box_ents*el + idx];
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
// construct obbox on the fly
for (int idx = 0; idx < sDIM; ++idx)
{
box.c0[idx] = boxinfo[n_box_ents*el + idx];
box.x[idx].min = boxinfo[n_box_ents*el + sDIM + idx];
box.x[idx].max = boxinfo[n_box_ents*el + 2*sDIM + idx];
}
for (int idx = 0; idx < sDIM2; ++idx)
{
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
}
pass_bb = (bbox_test(&box, x_i) >= 0);
}
else
{
for (int d = 0; d < sDIM; ++d)
{
box.x[d].min = boxinfo[n_box_ents*el + d];
box.x[d].max = boxinfo[n_box_ents*el + sDIM + d];
}
pass_bb = (AABB_test(&box, x_i) >= 0);
}
for (int idx = 0; idx < sDIM2; ++idx)
{
box.A[idx] = boxinfo[n_box_ents*el + 3*sDIM + idx];
}
if (bbox_test(&box, x_i) < 0) { continue; }
if (!pass_bb) { continue; }
//// findpts_local ////
{
@@ -968,13 +866,19 @@ static void FindPointsSurfLocal3D_Kernel(const int npt,
double *hes_T = jac + sDIM*rDIM;
double *hes = hes_T + hes_count*sDIM;
findptsElementGEdge_t edge;
for (int d=0; d<sDIM; ++d)
{
edge.x[d] = constraint_workspace + d*D1D;
edge.dxdn[d] = constraint_workspace + d*D1D
+ sDIM*D1D;
edge.d2xdn[d] = constraint_workspace + d*D1D
+ 2*sDIM*D1D;
}
MFEM_FOREACH_THREAD(j,x,D1D*sDIM)
{
// utilized first D1D threads
edge = get_edge(elx, wtend, ei,
constraint_workspace, edge_init, j,
D1D);
// One thread per physical component and edge DOF.
get_edge(elx, wtend, ei, edge_init, j, D1D, edge);
}
MFEM_SYNC_THREAD;
@@ -1045,7 +949,15 @@ static void FindPointsSurfLocal3D_Kernel(const int npt,
steep *= tmp->r[dn];
if (steep<0)
{
newton_face( fpt,jac,hes,resid,tmp->flags&CONVERGED_FLAG,tmp,tol);
double face_hes[3] =
{
dn == 0 ? hes[2] : hes[0],
hes[1],
dn == 0 ? hes[0] : hes[2]
};
newton_face(fpt, jac, face_hes, resid,
tmp->flags & CONVERGED_FLAG,
tmp, tol);
}
else
{
@@ -1211,29 +1123,42 @@ void FindPointsGSLIB::FindPointsSurfLocal3(const Vector &point_pos,
auto pgll1d = DEV.gll1d.ReadWrite(use_dev);
auto plc = DEV.lagcoeff.Read(use_dev);
double dist2tol = DEV.surf_dist_tol;
const bool obb_chk = obb_check;
switch (DEV.dof1d)
{
case 2:
return FindPointsSurfLocal3D_Kernel<2>(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc);
FindPointsSurfLocal3DKernel<2>(npt, DEV.newt_tol, dist2tol,
pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
case 3:
return FindPointsSurfLocal3D_Kernel<3>(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc);
FindPointsSurfLocal3DKernel<3>(npt, DEV.newt_tol, dist2tol,
pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
case 4:
return FindPointsSurfLocal3D_Kernel<4>(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc);
FindPointsSurfLocal3DKernel<4>(npt, DEV.newt_tol, dist2tol,
pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc);
break;
default:
return FindPointsSurfLocal3D_Kernel(
npt, DEV.newt_tol, dist2tol, pp, point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, DEV.lh_nx, plhm, plhf,
plho, pcode, pelem, pref, pdist, pgll1d, plc, DEV.dof1d);
FindPointsSurfLocal3DKernel(npt, DEV.newt_tol, dist2tol, pp,
point_pos_ordering, pgslm,
NE_split_total, pwt, pbb, obb_chk,
DEV.lh_nx, plhm, plhf, plho,
pcode, pelem, pref, pdist,
pgll1d, plc, DEV.dof1d);
break;
}
}
+190
View File
@@ -0,0 +1,190 @@
#ifndef MFEM_GSLIB_KERNEL_HELPERS_HPP
#define MFEM_GSLIB_KERNEL_HELPERS_HPP
#include "../../config/config.hpp"
#include <cmath>
namespace mfem
{
namespace gslib
{
struct dbl_range_t
{
double min, max;
};
template <int SDIM>
struct obbox_t
{
double c0[SDIM], A[SDIM * SDIM];
dbl_range_t x[SDIM];
};
template <int SDIM>
struct findptsLocalHashData_t
{
int hash_n;
dbl_range_t bnd[SDIM];
double fac[SDIM];
unsigned int *offset;
};
// Eval the ith Lagrange interpolant at x.
MFEM_HOST_DEVICE inline void lagrange_eval(double *p0, double x,
int i, int p_Nq,
double *z, double *lagrangeCoeff)
{
double p_i = (1 << (p_Nq - 1));
for (int j = 0; j < p_Nq; ++j)
{
const double d_j = x - z[j];
p_i *= j == i ? 1 : d_j;
}
p0[i] = lagrangeCoeff[i] * p_i;
}
// Eval the ith Lagrange interpolant and its first derivative at x.
MFEM_HOST_DEVICE inline void lag_eval_first_der(double *p0, double x,
int i, const double *z,
const double *lCoeff,
int pN)
{
double u0 = 1, u1 = 0;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
const double d_j = 2 * (x - z[j]);
u1 = d_j * u1 + u0;
u0 = d_j * u0;
}
}
p0[i] = lCoeff[i] * u0;
p0[pN + i] = 2.0 * lCoeff[i] * u1;
}
// Eval the ith Lagrange interpolant and its first and second derivative at x.
MFEM_HOST_DEVICE inline void lag_eval_second_der(double *p0, double x,
int i, const double *z,
const double *lCoeff,
int pN)
{
double u0 = 1, u1 = 0, u2 = 0;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
const double d_j = 2 * (x - z[j]);
u2 = d_j * u2 + u1;
u1 = d_j * u1 + u0;
u0 = d_j * u0;
}
}
p0[i] = lCoeff[i] * u0;
p0[pN + i] = 2.0 * lCoeff[i] * u1;
p0[2 * pN + i] = 8.0 * lCoeff[i] * u2;
}
// Solve Ax=y where A is a symmetric 2x2 matrix packed as {a00, a01, a11}.
MFEM_HOST_DEVICE inline void lin_solve_sym_2(double x[2],
const double A[3],
const double y[2])
{
const double idet = 1 / (A[0] * A[2] - A[1] * A[1]);
x[0] = idet * (A[2] * y[0] - A[1] * y[1]);
x[1] = idet * (A[0] * y[1] - A[1] * y[0]);
}
// Positive when the point is inside the axis-aligned bounding box.
template <int SDIM>
MFEM_HOST_DEVICE inline double AABB_test(const obbox_t<SDIM> *const b,
const double (&x)[SDIM])
{
double test = 1.0;
for (int d = 0; d < SDIM; ++d)
{
const double b_d = (x[d] - b->x[d].min) * (b->x[d].max - x[d]);
test = test < 0.0 ? test : b_d;
}
return test;
}
// Positive when the point is inside the oriented bounding box.
template <int SDIM>
MFEM_HOST_DEVICE inline double bbox_test(const obbox_t<SDIM> *const b,
const double (&x)[SDIM])
{
const double bxyz = AABB_test(b, x);
if (bxyz < 0.0)
{
return bxyz;
}
double dxyz[SDIM];
for (int d = 0; d < SDIM; ++d)
{
dxyz[d] = x[d] - b->c0[d];
}
double test = 1.0;
for (int d = 0; d < SDIM; ++d)
{
double rst = 0.0;
for (int e = 0; e < SDIM; ++e)
{
rst += b->A[d * SDIM + e] * dxyz[e];
}
const double brst = (rst + 1.0) * (1.0 - rst);
test = test < 0.0 ? test : brst;
}
return test;
}
// Hash index in the hash table for the point x.
template <int SDIM>
MFEM_HOST_DEVICE inline int hash_index(
const findptsLocalHashData_t<SDIM> *const p,
const double (&x)[SDIM])
{
const int n = p->hash_n;
int sum = 0;
for (int d = SDIM - 1; d >= 0; --d)
{
sum *= n;
const int i = (int)floor((x[d] - p->bnd[d].min) * p->fac[d]);
sum += i < 0 ? 0 : (n - 1 < i ? n - 1 : i);
}
return sum;
}
// Squared Euclidean norm.
template <int SDIM>
MFEM_HOST_DEVICE inline double l2norm2(const double (&x)[SDIM])
{
double sum = 0.0;
for (int d = 0; d < SDIM; ++d)
{
sum += x[d] * x[d];
}
return sum;
}
template <int SDIM>
MFEM_HOST_DEVICE inline double l2norm2(const double *x)
{
double sum = 0.0;
for (int d = 0; d < SDIM; ++d)
{
sum += x[d] * x[d];
}
return sum;
}
} // namespace gslib
} // namespace mfem
#endif
+22 -27
View File
@@ -11,7 +11,7 @@
#include "../gslib.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/kernels.hpp"
#include "gslib_kernel_helpers.hpp"
#ifdef MFEM_USE_GSLIB
@@ -33,17 +33,7 @@ namespace mfem
#define CODE_BORDER 1
#define CODE_NOT_FOUND 2
static MFEM_HOST_DEVICE void lagrange_eval(double *p0, double x,
int i, int p_Nq,
double *z, double *lagrangeCoeff)
{
double p_i = (1 << (p_Nq - 1));
for (int j=0; j<p_Nq; ++j)
{
p_i *= j==i ? 1 : x-z[j];
}
p0[i] = lagrangeCoeff[i] * p_i;
}
using gslib::lagrange_eval;
template<int T_D1D = 0>
static void InterpolateLocal1DKernel(const double *const gf_in,
@@ -123,21 +113,26 @@ void FindPointsGSLIB::InterpolateLocal1( const Vector &field_in,
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
switch (dof1Dsol)
{
case 2: return InterpolateLocal1DKernel<2>(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf);
case 3: return InterpolateLocal1DKernel<3>(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf);
case 4: return InterpolateLocal1DKernel<4>(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf);
case 5: return InterpolateLocal1DKernel<5>(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf);
default: return InterpolateLocal1DKernel(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf, dof1Dsol);
case 2:
InterpolateLocal1DKernel<2>(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
case 3:
InterpolateLocal1DKernel<3>(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
case 4:
InterpolateLocal1DKernel<4>(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
case 5:
InterpolateLocal1DKernel<5>(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
default:
InterpolateLocal1DKernel(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf, dof1Dsol);
break;
}
}
#undef CODE_INTERNAL
+22 -27
View File
@@ -11,6 +11,7 @@
#include "../gslib.hpp"
#include "../../general/forall.hpp"
#include "gslib_kernel_helpers.hpp"
#ifdef MFEM_USE_GSLIB
@@ -32,18 +33,7 @@ namespace mfem
#define CODE_BORDER 1
#define CODE_NOT_FOUND 2
static MFEM_HOST_DEVICE void lagrange_eval(double *p0, double x,
int i, int p_Nq,
double *z, double *lagrangeCoeff)
{
double p_i = (1 << (p_Nq - 1));
for (int j = 0; j < p_Nq; ++j)
{
double d_j = x - z[j];
p_i *= j == i ? 1 : d_j;
}
p0[i] = lagrangeCoeff[i] * p_i;
}
using gslib::lagrange_eval;
template<int T_D1D = 0>
static void InterpolateLocal2DKernel(const double *const gf_in,
@@ -132,21 +122,26 @@ void FindPointsGSLIB::InterpolateLocal2(const Vector &field_in,
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
switch (dof1Dsol)
{
case 2: return InterpolateLocal2DKernel<2>(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf);
case 3: return InterpolateLocal2DKernel<3>(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf);
case 4: return InterpolateLocal2DKernel<4>(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf);
case 5: return InterpolateLocal2DKernel<5>(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf);
default: return InterpolateLocal2DKernel(pfin, pgsl, pgslr, pfout,
npt, ncomp,
pgll, plcf, dof1Dsol);
case 2:
InterpolateLocal2DKernel<2>(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
case 3:
InterpolateLocal2DKernel<3>(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
case 4:
InterpolateLocal2DKernel<4>(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
case 5:
InterpolateLocal2DKernel<5>(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
default:
InterpolateLocal2DKernel(pfin, pgsl, pgslr, pfout,
npt, ncomp, pgll, plcf, dof1Dsol);
break;
}
}
+22 -27
View File
@@ -11,6 +11,7 @@
#include "../gslib.hpp"
#include "../../general/forall.hpp"
#include "gslib_kernel_helpers.hpp"
#ifdef MFEM_USE_GSLIB
@@ -32,18 +33,7 @@ namespace mfem
#define CODE_BORDER 1
#define CODE_NOT_FOUND 2
static MFEM_HOST_DEVICE void lagrange_eval(double *p0, double x,
int i, int p_Nq,
double *z, double *lagrangeCoeff)
{
double p_i = (1 << (p_Nq - 1));
for (int j = 0; j < p_Nq; ++j)
{
double d_j = x - z[j];
p_i *= j == i ? 1 : d_j;
}
p0[i] = lagrangeCoeff[i] * p_i;
}
using gslib::lagrange_eval;
template<int T_D1D = 0>
static void InterpolateLocal3DKernel(const double *const gf_in,
@@ -135,21 +125,26 @@ void FindPointsGSLIB::InterpolateLocal3(const Vector &field_in,
auto plcf = DEV.lagcoeff_sol.ReadWrite(use_dev);
switch (dof1Dsol)
{
case 2: return InterpolateLocal3DKernel<2>(pfin, pgsle, pgslr, pfout,
npt, ncomp,
pgll, plcf);
case 3: return InterpolateLocal3DKernel<3>(pfin, pgsle, pgslr, pfout,
npt, ncomp,
pgll, plcf);
case 4: return InterpolateLocal3DKernel<4>(pfin, pgsle, pgslr, pfout,
npt, ncomp,
pgll, plcf);
case 5: return InterpolateLocal3DKernel<5>(pfin, pgsle, pgslr, pfout,
npt, ncomp,
pgll, plcf);
default: return InterpolateLocal3DKernel(pfin, pgsle, pgslr, pfout,
npt, ncomp,
pgll, plcf, dof1Dsol);
case 2:
InterpolateLocal3DKernel<2>(pfin, pgsle, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
case 3:
InterpolateLocal3DKernel<3>(pfin, pgsle, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
case 4:
InterpolateLocal3DKernel<4>(pfin, pgsle, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
case 5:
InterpolateLocal3DKernel<5>(pfin, pgsle, pgslr, pfout,
npt, ncomp, pgll, plcf);
break;
default:
InterpolateLocal3DKernel(pfin, pgsle, pgslr, pfout,
npt, ncomp, pgll, plcf, dof1Dsol);
break;
}
}
@@ -10,6 +10,7 @@
// CONTRIBUTING.md for details.
#include "bilininteg_diffusion_kernels.hpp"
#include "bilininteg_diffusion_pa_simplices.hpp" // IWYU pragma: keep
namespace mfem
{
@@ -19,6 +20,13 @@ namespace mfem
DiffusionIntegrator::Kernels::Kernels()
{
// 2D
// Q = P, only for simplex
DiffusionIntegrator::AddSimplexSpecialization<2,2,1>();
DiffusionIntegrator::AddSimplexSpecialization<2,3,2>();
DiffusionIntegrator::AddSimplexSpecialization<2,4,3>();
DiffusionIntegrator::AddSimplexSpecialization<2,5,4>();
DiffusionIntegrator::AddSimplexSpecialization<2,6,5>();
DiffusionIntegrator::AddSimplexSpecialization<2,7,6>();
// Q = P+1
DiffusionIntegrator::AddSpecialization<2,1,1>();
DiffusionIntegrator::AddSpecialization<2,2,2>();
@@ -40,7 +48,18 @@ DiffusionIntegrator::Kernels::Kernels()
DiffusionIntegrator::AddSpecialization<2,8,9>();
DiffusionIntegrator::AddSpecialization<2,9,10>();
// others
DiffusionIntegrator::AddSimplexSpecialization<2,2,5>();
DiffusionIntegrator::AddSimplexSpecialization<2,3,6>();
// 3D
// Q = P, only for simplex
DiffusionIntegrator::AddSimplexSpecialization<3,2,1>();
DiffusionIntegrator::AddSimplexSpecialization<3,3,2>();
DiffusionIntegrator::AddSimplexSpecialization<3,4,3>();
DiffusionIntegrator::AddSimplexSpecialization<3,5,4>();
DiffusionIntegrator::AddSimplexSpecialization<3,6,5>();
DiffusionIntegrator::AddSimplexSpecialization<3,7,6>();
DiffusionIntegrator::AddSimplexSpecialization<3,8,7>();
// Q = P+1
DiffusionIntegrator::AddSpecialization<3,1,1>();
DiffusionIntegrator::AddSpecialization<3,2,2>();
+21 -16
View File
@@ -12,7 +12,6 @@
#ifndef MFEM_BILININTEG_DIFFUSION_KERNELS_HPP
#define MFEM_BILININTEG_DIFFUSION_KERNELS_HPP
#include "../kernel_dispatch.hpp"
#include "../../config/config.hpp"
#include "../../general/array.hpp"
#include "../../general/forall.hpp"
@@ -20,6 +19,8 @@
#include "../../linalg/vector.hpp"
#include "../bilininteg.hpp"
#include "bilininteg_diffusion_pa_simplices.hpp"
namespace mfem
{
@@ -637,8 +638,8 @@ inline void SmemPADiffusionApply2D(const int NE,
const bool symmetric,
const Array<real_t> &b_,
const Array<real_t> &g_,
const Array<real_t> &bt_,
const Array<real_t> &gt_,
const Array<real_t> &,
const Array<real_t> &,
const Vector &d_,
const Vector &x_,
Vector &y_,
@@ -1218,43 +1219,47 @@ inline void SmemPADiffusionApply3D(const int NE,
namespace
{
using ApplyKernelType = DiffusionIntegrator::ApplyKernelType;
using ApplySimplexKernelType = DiffusionIntegrator::ApplySimplexKernelType;
using DiagonalKernelType = DiffusionIntegrator::DiagonalKernelType;
}
template<int DIM, int T_D1D, int T_Q1D>
template<int DIM, int D1D, int Q1D>
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Kernel()
{
if constexpr (DIM == 2) { return internal::SmemPADiffusionApply2D<T_D1D,T_Q1D>; }
else if constexpr (DIM == 3) { return internal::SmemPADiffusionApply3D<T_D1D, T_Q1D>; }
MFEM_ABORT("");
if constexpr (DIM == 2) { return internal::SmemPADiffusionApply2D<D1D, Q1D>; }
else if constexpr (DIM == 3) { return internal::SmemPADiffusionApply3D<D1D, Q1D>; }
else { MFEM_ABORT(""); }
return nullptr;
}
inline
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Fallback(int DIM, int, int)
ApplyKernelType DiffusionIntegrator::ApplyPAKernels::Fallback(int dim, int, int)
{
if (DIM == 2) { return internal::PADiffusionApply2D; }
else if (DIM == 3) { return internal::PADiffusionApply3D; }
if (dim == 2) { return internal::PADiffusionApply2D; }
else if (dim == 3) { return internal::PADiffusionApply3D; }
else { MFEM_ABORT(""); }
}
template<int DIM, int D1D, int Q1D>
DiagonalKernelType DiffusionIntegrator::DiagonalPAKernels::Kernel()
{
if constexpr (DIM == 2) { return internal::SmemPADiffusionDiagonal2D<D1D,Q1D>; }
if constexpr (DIM == 2) { return internal::SmemPADiffusionDiagonal2D<D1D, Q1D>; }
else if constexpr (DIM == 3) { return internal::SmemPADiffusionDiagonal3D<D1D, Q1D>; }
MFEM_ABORT("");
else { MFEM_ABORT(""); }
return nullptr;
}
inline DiagonalKernelType
DiffusionIntegrator::DiagonalPAKernels::Fallback(int DIM, int, int)
DiffusionIntegrator::DiagonalPAKernels::Fallback(int dim, int, int)
{
if (DIM == 2) { return internal::PADiffusionDiagonal2D; }
else if (DIM == 3) { return internal::PADiffusionDiagonal3D; }
if (dim == 2) { return internal::PADiffusionDiagonal2D; }
else if (dim == 3) { return internal::PADiffusionDiagonal3D; }
else { MFEM_ABORT(""); }
return nullptr;
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
#endif
+31 -2
View File
@@ -15,6 +15,7 @@
#include "../../mesh/nurbs.hpp"
#include "../ceed/integrators/diffusion/diffusion.hpp"
#include "bilininteg_diffusion_kernels.hpp"
#include "bilininteg_diffusion_pa_simplices.hpp"
namespace mfem
{
@@ -68,6 +69,24 @@ void DiffusionIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
#endif // MFEM_USE_OCCA
if (fespace->UsesRaggedTensorBasis())
{
const auto *rmaps = static_cast<const RaggedDofToQuad*>(maps);
return ApplySimplexPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric,
rmaps->lex_map,
rmaps->forward_map2d_diff,
rmaps->inverse_map2d_diff,
rmaps->forward_map3d_diff,
rmaps->inverse_map3d_diff,
rmaps->Ga1,
rmaps->Ga2,
rmaps->Ga3,
rmaps->Ga1t,
rmaps->Ga2t,
rmaps->Ga3t,
Dv, x, y, dofs1D, quad1D);
}
ApplyPAKernels::Run(dim, dofs1D, quad1D, ne, symmetric, B, G, Bt,
Gt, Dv, x, y, dofs1D, quad1D);
}
@@ -94,7 +113,8 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
fespace = &fes;
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetTypicalFE();
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el);
const bool stroud = fes.UsesRaggedTensorBasis();
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, stroud);
if (DeviceCanUseCeed())
{
delete ceedOp;
@@ -119,13 +139,22 @@ void DiffusionIntegrator::AssemblePA(const FiniteElementSpace &fes)
dim = mesh->Dimension();
ne = fes.GetNE();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
if (stroud)
{
maps = &el.GetDofToQuad(*ir, DofToQuad::RAGGED_TENSOR);
}
else
{
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
}
const int sdim = mesh->SpaceDimension();
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = maps->ndof;
quad1D = maps->nqpt;
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(qs, CoefficientStorage::COMPRESSED);
// QuadratureSpace expects ir defined in reference simplex for Bernstein
// elements with partial assembly
if (MQ) { coeff.ProjectTranspose(*MQ); }
else if (VQ) { coeff.Project(*VQ); }
File diff suppressed because it is too large Load Diff
+3
View File
@@ -10,6 +10,7 @@
// CONTRIBUTING.md for details.
#include "bilininteg_mass_kernels.hpp"
#include "bilininteg_mass_pa_simplices.hpp" // IWYU pragma: keep
namespace mfem
{
@@ -39,8 +40,10 @@ MassIntegrator::Kernels::Kernels()
MassIntegrator::AddSpecialization<2,9,10>();
// others
MassIntegrator::AddSpecialization<2,2,4>();
MassIntegrator::AddSpecialization<2,2,5>();
MassIntegrator::AddSpecialization<2,3,6>();
MassIntegrator::AddSpecialization<2,4,6>();
// 3D
// Q=P+1
MassIntegrator::AddSpecialization<3,1,1>();
+25 -17
View File
@@ -19,6 +19,8 @@
#include "../../linalg/vector.hpp"
#include "../bilininteg.hpp"
#include "bilininteg_mass_pa_simplices.hpp"
namespace mfem
{
@@ -1408,51 +1410,57 @@ using ApplyKernelType = MassIntegrator::ApplyKernelType;
using DiagonalKernelType = MassIntegrator::DiagonalKernelType;
}
template<int DIM, int T_D1D, int T_Q1D>
template<int DIM, int D1D, int Q1D>
ApplyKernelType MassIntegrator::ApplyPAKernels::Kernel()
{
if constexpr (DIM == 1) { return internal::PAMassApply1D; }
else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<T_D1D,T_Q1D>; }
else if constexpr (DIM == 2) { return internal::SmemPAMassApply2D<D1D, Q1D>; }
else if constexpr (DIM == 3)
{
constexpr int MDQ = T_D1D >= T_Q1D ? T_D1D : T_Q1D;
constexpr int MDQ = D1D >= Q1D ? D1D : Q1D;
// max 64 threads in z limit in cuda and hip
if constexpr (MDQ > 0)
{
return internal::SmemPAMassApply3D<T_D1D, T_Q1D,
return internal::SmemPAMassApply3D<D1D, Q1D,
internal::mass::NBZ3D(MDQ)>;
}
}
MFEM_ABORT("");
else { MFEM_ABORT(""); }
return nullptr;
}
inline ApplyKernelType MassIntegrator::ApplyPAKernels::Fallback(
int DIM, int, int)
int dim, int, int)
{
if (DIM == 1) { return internal::PAMassApply1D; }
else if (DIM == 2) { return internal::PAMassApply2D; }
else if (DIM == 3) { return internal::PAMassApply3D; }
if (dim == 1) { return internal::PAMassApply1D; }
else if (dim == 2) { return internal::PAMassApply2D; }
else if (dim == 3) { return internal::PAMassApply3D; }
else { MFEM_ABORT(""); }
return nullptr;
}
template<int DIM, int T_D1D, int T_Q1D>
template<int DIM, int D1D, int Q1D>
DiagonalKernelType MassIntegrator::DiagonalPAKernels::Kernel()
{
if constexpr (DIM == 1) { return internal::PAMassAssembleDiagonal1D; }
else if constexpr (DIM == 2) { return internal::SmemPAMassAssembleDiagonal2D<T_D1D,T_Q1D>; }
else if constexpr (DIM == 3) { return internal::SmemPAMassAssembleDiagonal3D<T_D1D, T_Q1D>; }
MFEM_ABORT("");
else if constexpr (DIM == 2) { return internal::SmemPAMassAssembleDiagonal2D<D1D, Q1D>; }
else if constexpr (DIM == 3) { return internal::SmemPAMassAssembleDiagonal3D<D1D, Q1D>; }
else { MFEM_ABORT(""); }
return nullptr;
}
inline DiagonalKernelType MassIntegrator::DiagonalPAKernels::Fallback(
int DIM, int, int)
int dim, int, int)
{
if (DIM == 1) { return internal::PAMassAssembleDiagonal1D; }
else if (DIM == 2) { return internal::PAMassAssembleDiagonal2D; }
else if (DIM == 3) { return internal::PAMassAssembleDiagonal3D; }
if (dim == 1) { return internal::PAMassAssembleDiagonal1D; }
else if (dim == 2) { return internal::PAMassAssembleDiagonal2D; }
else if (dim == 3) { return internal::PAMassAssembleDiagonal3D; }
else { MFEM_ABORT(""); }
return nullptr;
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
#endif
+43 -5
View File
@@ -15,6 +15,7 @@
#include "../qfunction.hpp"
#include "../ceed/integrators/mass/mass.hpp"
#include "bilininteg_mass_kernels.hpp"
#include "bilininteg_mass_pa_simplices.hpp"
namespace mfem
{
@@ -29,9 +30,11 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
// Assuming the same element type
fespace = &fes;
Mesh *mesh = fes.GetMesh();
dim = mesh->Dimension();
const FiniteElement &el = *fes.GetTypicalFE();
ElementTransformation *T0 = mesh->GetTypicalElementTransformation();
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T0);
const bool stroud = fes.UsesRaggedTensorBasis();
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, el, *T0, stroud);
if (DeviceCanUseCeed())
{
delete ceedOp;
@@ -48,17 +51,25 @@ void MassIntegrator::AssemblePA(const FiniteElementSpace &fes)
return;
}
int map_type = el.GetMapType();
dim = mesh->Dimension();
ne = fes.GetMesh()->GetNE();
nq = ir->GetNPoints();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS, mt);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
if (stroud)
{
maps = &el.GetDofToQuad(*ir, DofToQuad::RAGGED_TENSOR);
}
else
{
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
}
dofs1D = maps->ndof;
quad1D = maps->nqpt;
pa_data.SetSize(ne*nq, mt);
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
// QuadratureSpace expects ir defined in reference simplex for Bernstein
// elements with partial assembly
{
const int NE = ne;
const int NQ = nq;
@@ -147,9 +158,10 @@ void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
{
const int D1D = dofs1D;
const int Q1D = quad1D;
const Vector &D = pa_data;
const Array<real_t> &B = maps->B;
const Array<real_t> &Bt = maps->Bt;
const Vector &D = pa_data;
#ifdef MFEM_USE_OCCA
if (DeviceCanUseOcca())
{
@@ -164,7 +176,31 @@ void MassIntegrator::AddMultPA(const Vector &x, Vector &y) const
MFEM_ABORT("OCCA PA Mass Apply unknown kernel!");
}
#endif // MFEM_USE_OCCA
ApplyPAKernels::Run(dim, D1D, Q1D, ne, B, Bt, D, x, y, D1D, Q1D);
if (fespace->UsesRaggedTensorBasis())
{
const auto *rmaps = static_cast<const RaggedDofToQuad*>(maps);
const Array<real_t> &Ba1 = rmaps->Ba1;
const Array<real_t> &Ba2 = rmaps->Ba2;
const Array<real_t> &Ba3 = rmaps->Ba3;
const Array<real_t> &Ba1t = rmaps->Ba1t;
const Array<real_t> &Ba2t = rmaps->Ba2t;
const Array<real_t> &Ba3t = rmaps->Ba3t;
const Array<int> &lex_map = rmaps->lex_map;
const Array<int> &forward_map2d = rmaps->forward_map2d_mass;
const Array<int> &inverse_map2d = rmaps->inverse_map2d_mass;
const Array<int> &forward_map3d = rmaps->forward_map3d_mass;
const Array<int> &inverse_map3d = rmaps->inverse_map3d_mass;
ApplySimplexPAKernels::Run(dim, D1D, Q1D, ne, lex_map, forward_map2d,
inverse_map2d,
forward_map3d, inverse_map3d, Ba1, Ba2, Ba3, Ba1t, Ba2t, Ba3t,
D, x, y, D1D, Q1D);
}
else
{
ApplyPAKernels::Run(dim, D1D, Q1D, ne, B, Bt, D, x, y, D1D, Q1D);
}
}
}
@@ -177,6 +213,8 @@ void MassIntegrator::AddAbsMultPA(const Vector &x, Vector &y) const
}
else
{
MFEM_VERIFY(!fespace->UsesRaggedTensorBasis(),
"AbsMultPA not implemented for ragged tensor basis");
Vector abs_pa_data(pa_data);
abs_pa_data.Abs();
Array<real_t> absB(maps->B);
File diff suppressed because it is too large Load Diff
+500
View File
@@ -307,6 +307,506 @@ DomainLFIntegrator::AssembleKernels::Kernel()
MFEM_ABORT("");
}
template <int T_D1D = 0, int T_Q1D = 0>
static void HdivDLFAssemble2D(const int ne, const Array<int> &markers,
const Vector &jac, const Array<real_t> &weights,
const Array<real_t> &testBO,
const Array<real_t> &testBC, const Vector &coeff,
Vector &y, const int d, const int q)
{
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D,
"Problem size too large.");
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D,
"Problem size too large.");
MFEM_VERIFY(y.Size() == 2 * (d - 1) * d * ne, "");
constexpr int vdim = 2;
const auto F = coeff.Read();
const auto M = markers.Read();
const auto BO = Reshape(testBO.Read(), q, d-1);
const auto BC = Reshape(testBC.Read(), q, d);
const auto J = Reshape(jac.Read(), q, q, vdim, vdim, ne);
const auto W = Reshape(weights.Read(), q, q);
const bool cst = coeff.Size() == vdim;
const auto C = cst ? Reshape(F,vdim,1,1,1) : Reshape(F,vdim,q,q,ne);
auto Y = y.ReadWrite();
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int vdim = 2;
if (M[e] == 0) { return; } // ignore
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HDIV_MAX_Q1D;
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HDIV_MAX_D1D;
MFEM_SHARED real_t sBot[Q*D];
MFEM_SHARED real_t sBct[Q*D];
MFEM_SHARED real_t sQQ[vdim*Q*Q];
MFEM_SHARED real_t sQD[vdim*Q*D];
// Bo and Bc into shared memory
const DeviceMatrix Bot(sBot, d-1, q);
kernels::internal::LoadB<D,Q>(d-1, q, BO, sBot);
const DeviceMatrix Bct(sBct, d, q);
kernels::internal::LoadB<D,Q>(d, q, BC, sBct);
const DeviceCube QQ(sQQ, q, q, vdim);
const DeviceCube QD(sQD, q, d, vdim);
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const real_t cst_val_0 = C(0,0,0,0);
const real_t cst_val_1 = C(1,0,0,0);
MFEM_FOREACH_THREAD(y,y,q)
{
MFEM_FOREACH_THREAD(x,x,q)
{
const real_t J0 = J(x,y,0,vd,e);
const real_t J1 = J(x,y,1,vd,e);
const real_t C0 = cst ? cst_val_0 : C(0,x,y,e);
const real_t C1 = cst ? cst_val_1 : C(1,x,y,e);
QQ(x,y,vd) = W(x,y)*(J0*C0 + J1*C1);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const int nx = (vd == 0) ? d : d-1;
DeviceMatrix Btx = (vd == 0) ? Bct : Bot;
MFEM_FOREACH_THREAD(qy,y,q)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t qd = 0.0;
for (int qx = 0; qx < q; ++qx)
{
qd += QQ(qx,qy,vd) * Btx(dx,qx);
}
QD(dx,qy,vd) = qd;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const int nx = (vd == 0) ? d : d-1;
const int ny = (vd == 1) ? d : d-1;
DeviceMatrix Bty = (vd == 1) ? Bct : Bot;
DeviceTensor<4> Yxy(Y, nx, ny, vdim, ne);
MFEM_FOREACH_THREAD(dy,y,ny)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t dd = 0.0;
for (int qy = 0; qy < q; ++qy)
{
dd += QD(dx,qy,vd) * Bty(dy,qy);
}
Yxy(dx,dy,vd,e) += dd;
}
}
}
MFEM_SYNC_THREAD;
});
}
template <int T_D1D = 0, int T_Q1D = 0>
static void HdivDLFAssemble3D(const int ne, const Array<int> &markers,
const Vector &jac, const Array<real_t> &weights,
const Array<real_t> &testBO,
const Array<real_t> &testBC, const Vector &coeff,
Vector &y, const int d, const int q)
{
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D,
"Problem size too large.");
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D,
"Problem size too large.");
MFEM_VERIFY(y.Size() == 3 * (d - 1) * (d - 1) * d * ne, "y wrong length");
constexpr int vdim = 3;
const auto F = coeff.Read();
const auto M = markers.Read();
const auto BO = Reshape(testBO.Read(), q, d-1);
const auto BC = Reshape(testBC.Read(), q, d);
const auto J = Reshape(jac.Read(), q, q, q, vdim, vdim, ne);
const auto W = Reshape(weights.Read(), q, q, q);
const bool cst = coeff.Size() == vdim;
const auto C = cst ? Reshape(F,vdim,1,1,1,1) : Reshape(F,vdim,q,q,q,ne);
auto Y = y.ReadWrite();
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int vdim = 3;
if (M[e] == 0) { return; } // ignore
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HDIV_MAX_Q1D;
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HDIV_MAX_D1D;
MFEM_SHARED real_t sBot[Q*D];
MFEM_SHARED real_t sBct[Q*D];
// Bo and Bc into shared memory
const DeviceMatrix Bot(sBot, d-1, q);
kernels::internal::LoadB<D,Q>(d-1, q, BO, sBot);
const DeviceMatrix Bct(sBct, d, q);
kernels::internal::LoadB<D,Q>(d, q, BC, sBct);
MFEM_SHARED real_t sm0[vdim*Q*Q*Q];
MFEM_SHARED real_t sm1[vdim*Q*Q*Q];
DeviceTensor<4> QQQ(sm1, q, q, q, vdim);
DeviceTensor<4> DQQ(sm0, d, q, q, vdim);
DeviceTensor<4> DDQ(sm1, d, d, q, vdim);
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const real_t cst_val_0 = C(0,0,0,0,0);
const real_t cst_val_1 = C(1,0,0,0,0);
const real_t cst_val_2 = C(2,0,0,0,0);
MFEM_FOREACH_THREAD(y,y,q)
{
MFEM_FOREACH_THREAD(x,x,q)
{
for (int z = 0; z < q; ++z)
{
const real_t J0 = J(x,y,z,0,vd,e);
const real_t J1 = J(x,y,z,1,vd,e);
const real_t J2 = J(x,y,z,2,vd,e);
const real_t C0 = cst ? cst_val_0 : C(0,x,y,z,e);
const real_t C1 = cst ? cst_val_1 : C(1,x,y,z,e);
const real_t C2 = cst ? cst_val_2 : C(2,x,y,z,e);
QQQ(x,y,z,vd) = W(x,y,z)*(J0*C0 + J1*C1 + J2*C2);
}
}
}
}
MFEM_SYNC_THREAD;
// Apply Bt operator
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const int nx = (vd == 0) ? d : d-1;
DeviceMatrix Btx = (vd == 0) ? Bct : Bot;
MFEM_FOREACH_THREAD(qy,y,q)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t u[Q];
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz) { u[qz] = 0.0; }
MFEM_UNROLL(Q)
for (int qx = 0; qx < q; ++qx)
{
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
u[qz] += QQQ(qx,qy,qz,vd) * Btx(dx,qx);
}
}
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz) { DQQ(dx,qy,qz,vd) = u[qz]; }
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const int nx = (vd == 0) ? d : d-1;
const int ny = (vd == 1) ? d : d-1;
DeviceMatrix Bty = (vd == 1) ? Bct : Bot;
MFEM_FOREACH_THREAD(dy,y,ny)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t u[Q];
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz) { u[qz] = 0.0; }
MFEM_UNROLL(Q)
for (int qy = 0; qy < q; ++qy)
{
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
u[qz] += DQQ(dx,qy,qz,vd) * Bty(dy,qy);
}
}
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz) { DDQ(dx,dy,qz,vd) = u[qz]; }
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const int nx = (vd == 0) ? d : d-1;
const int ny = (vd == 1) ? d : d-1;
const int nz = (vd == 2) ? d : d-1;
DeviceTensor<5> Yxyz(Y, nx, ny, nz, vdim, ne);
DeviceMatrix Btz = (vd == 2) ? Bct : Bot;
MFEM_FOREACH_THREAD(dy,y,ny)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t u[D];
MFEM_UNROLL(D)
for (int dz = 0; dz < nz; ++dz) { u[dz] = 0.0; }
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
MFEM_UNROLL(D)
for (int dz = 0; dz < nz; ++dz)
{
u[dz] += DDQ(dx,dy,qz,vd) * Btz(dz,qz);
}
}
MFEM_UNROLL(D)
for (int dz = 0; dz < nz; ++dz) { Yxyz(dx,dy,dz,vd,e) += u[dz]; }
}
}
}
MFEM_SYNC_THREAD;
});
}
/// @param ne number of elements
/// @param markers array where entry markers[e] == 0 to skip assembly over
/// element e element
/// @param jac Spatial Jacobians evaluated at all quadrature points
/// @param weights 1D quadrature weights
/// @param testBO 1D open basis test functions
/// @param testBC 1D closed basis test functions
/// @param coeff coefficient values evaluated at quadrature points, possibly
/// compressed.
/// @param d number of 1D closed dofs
/// @param q number of 1D quadrature points
/// @tparam T_D1D maximum number of dofs along any direction, or 0
/// @tparam T_Q1D maximum number of quadrature points along any direction, or 0
template <int T_D1D = 0, int T_Q1D = 0>
static void HcurlDLFAssemble3D(const int ne, const Array<int> &markers,
const Vector &jac, const Array<real_t> &weights,
const Array<real_t> &testBO,
const Array<real_t> &testBC, const Vector &coeff,
Vector &y, const int d, const int q)
{
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D,
"Problem size too large.");
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D,
"Problem size too large.");
MFEM_VERIFY(y.Size() == 3 * (d - 1) * d * d * ne, "y wrong length");
constexpr int vdim = 3;
const auto F = coeff.Read();
const auto M = markers.Read();
const auto BO = Reshape(testBO.Read(), q, d-1);
const auto BC = Reshape(testBC.Read(), q, d);
const auto J = Reshape(jac.Read(), q, q, q, vdim, vdim, ne);
const auto W = Reshape(weights.Read(), q, q, q);
const bool cst = coeff.Size() == vdim;
const auto C = cst ? Reshape(F,vdim,1,1,1,1) : Reshape(F,vdim,q,q,q,ne);
auto Y = y.ReadWrite();
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE(int e)
{
if (M[e] == 0)
{
// ignore
return;
}
constexpr int vdim = 3;
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HCURL_MAX_Q1D;
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HCURL_MAX_D1D;
MFEM_SHARED real_t sBot[Q * D];
MFEM_SHARED real_t sBct[Q * D];
// Bo and Bc into shared memory
const DeviceMatrix Bot(sBot, d - 1, q);
kernels::internal::LoadB<D, Q>(d - 1, q, BO, sBot);
const DeviceMatrix Bct(sBct, d, q);
kernels::internal::LoadB<D, Q>(d, q, BC, sBct);
MFEM_SHARED real_t sm0[vdim * Q * Q * Q];
MFEM_SHARED real_t sm1[vdim * Q * Q * Q];
DeviceTensor<4> QQQ(sm1, q, q, q, vdim);
DeviceTensor<4> DQQ(sm0, d, q, q, vdim);
DeviceTensor<4> DDQ(sm1, d, d, q, vdim);
const real_t cst_val_0 = C(0, 0, 0, 0, 0);
const real_t cst_val_1 = C(1, 0, 0, 0, 0);
const real_t cst_val_2 = C(2, 0, 0, 0, 0);
MFEM_FOREACH_THREAD(vd, z, vdim)
{
MFEM_FOREACH_THREAD(y, y, q)
{
MFEM_FOREACH_THREAD(x, x, q)
{
for (int z = 0; z < q; ++z)
{
real_t curr[3];
curr[0] = cst ? cst_val_0 : C(0, x, y, z, e);
curr[1] = cst ? cst_val_1 : C(1, x, y, z, e);
curr[2] = cst ? cst_val_2 : C(2, x, y, z, e);
const real_t J11 = J(x, y, z, 0, 0, e);
const real_t J21 = J(x, y, z, 1, 0, e);
const real_t J31 = J(x, y, z, 2, 0, e);
const real_t J12 = J(x, y, z, 0, 1, e);
const real_t J22 = J(x, y, z, 1, 1, e);
const real_t J32 = J(x, y, z, 2, 1, e);
const real_t J13 = J(x, y, z, 0, 2, e);
const real_t J23 = J(x, y, z, 1, 2, e);
const real_t J33 = J(x, y, z, 2, 2, e);
// 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);
const real_t A[9] = {A11, A12, A13, A21, A22,
A23, A31, A32, A33
};
QQQ(x, y, z, vd) = W(x, y, z) * (A[vd * vdim] * curr[0] +
A[vd * vdim + 1] * curr[1] +
A[vd * vdim + 2] * curr[2]);
}
}
}
}
MFEM_SYNC_THREAD;
// Apply Bt operator
MFEM_FOREACH_THREAD(vd, z, vdim)
{
const int nx = (vd == 0) ? d - 1 : d;
DeviceMatrix Btx = (vd == 0) ? Bot : Bct;
MFEM_FOREACH_THREAD(qy, y, q)
{
MFEM_FOREACH_THREAD(dx, x, nx)
{
real_t u[Q];
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
u[qz] = 0.0;
}
MFEM_UNROLL(Q)
for (int qx = 0; qx < q; ++qx)
{
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
u[qz] += QQQ(qx, qy, qz, vd) * Btx(dx, qx);
}
}
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
DQQ(dx, qy, qz, vd) = u[qz];
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd, z, vdim)
{
const int nx = (vd == 0) ? d - 1 : d;
const int ny = (vd == 1) ? d - 1 : d;
DeviceMatrix Bty = (vd == 1) ? Bot : Bct;
MFEM_FOREACH_THREAD(dy, y, ny)
{
MFEM_FOREACH_THREAD(dx, x, nx)
{
real_t u[Q];
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
u[qz] = 0.0;
}
MFEM_UNROLL(Q)
for (int qy = 0; qy < q; ++qy)
{
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
u[qz] += DQQ(dx, qy, qz, vd) * Bty(dy, qy);
}
}
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
DDQ(dx, dy, qz, vd) = u[qz];
}
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd, z, vdim)
{
const int nx = (vd == 0) ? d - 1 : d;
const int ny = (vd == 1) ? d - 1 : d;
const int nz = (vd == 2) ? d - 1 : d;
DeviceTensor<5> Yxyz(Y, nx, ny, nz, vdim, ne);
DeviceMatrix Btz = (vd == 2) ? Bot : Bct;
MFEM_FOREACH_THREAD(dy, y, ny)
{
MFEM_FOREACH_THREAD(dx, x, nx)
{
real_t u[D];
MFEM_UNROLL(D)
for (int dz = 0; dz < nz; ++dz)
{
u[dz] = 0.0;
}
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
MFEM_UNROLL(D)
for (int dz = 0; dz < nz; ++dz)
{
u[dz] += DDQ(dx, dy, qz, vd) * Btz(dz, qz);
}
}
MFEM_UNROLL(D)
for (int dz = 0; dz < nz; ++dz)
{
Yxyz(dx, dy, dz, vd, e) += u[dz];
}
}
}
}
MFEM_SYNC_THREAD;
});
}
template <FiniteElement::DerivType TestType, int DIM, int TEST_D1D, int Q1D>
VectorFEDomainLFIntegrator::AssembleKernelType
VectorFEDomainLFIntegrator::AssembleKernels::Kernel()
{
if constexpr (TestType == FiniteElement::DIV)
{
if constexpr (DIM == 2)
{
return HdivDLFAssemble2D<TEST_D1D, Q1D>;
}
if constexpr (DIM == 3)
{
return HdivDLFAssemble3D<TEST_D1D, Q1D>;
}
}
if constexpr (TestType == FiniteElement::CURL)
{
if constexpr (DIM == 3)
{
return HcurlDLFAssemble3D<TEST_D1D, Q1D>;
}
}
MFEM_ABORT("");
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
+68 -301
View File
@@ -13,317 +13,76 @@
#include "../../fem/kernels.hpp"
#include "../fem.hpp"
#include "lininteg_domain_kernels.hpp"
namespace mfem
{
template<int T_D1D = 0, int T_Q1D = 0>
static void HdivDLFAssemble2D(
const int ne, const int d, const int q, const int *markers, const real_t *bo,
const real_t *bc, const real_t *j, const real_t *weights,
const Vector &coeff, real_t *y)
VectorFEDomainLFIntegrator::Kernels::Kernels()
{
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D,
"Problem size too large.");
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D,
"Problem size too large.");
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 1, 1>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 2, 2>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 3, 3>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 4, 4>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 5, 5>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 6, 6>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 7, 7>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 2, 8, 8>();
static constexpr int vdim = 2;
const auto F = coeff.Read();
const auto M = Reshape(markers, ne);
const auto BO = Reshape(bo, q, d-1);
const auto BC = Reshape(bc, q, d);
const auto J = Reshape(j, q, q, vdim, vdim, ne);
const auto W = Reshape(weights, q, q);
const bool cst = coeff.Size() == vdim;
const auto C = cst ? Reshape(F,vdim,1,1,1) : Reshape(F,vdim,q,q,ne);
auto Y = Reshape(y, 2*(d-1)*d, ne);
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 1, 1>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 2, 2>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 3, 3>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 4, 4>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 5, 5>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 6, 6>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 7, 7>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::DIV, 3, 8, 8>();
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE (int e)
{
if (M(e) == 0) { return; } // ignore
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 1, 1>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 2, 2>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 3, 3>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 4, 4>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 5, 5>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 6, 6>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 7, 7>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 8, 8>();
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HDIV_MAX_Q1D;
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HDIV_MAX_D1D;
MFEM_SHARED real_t sBot[Q*D];
MFEM_SHARED real_t sBct[Q*D];
MFEM_SHARED real_t sQQ[vdim*Q*Q];
MFEM_SHARED real_t sQD[vdim*Q*D];
// Bo and Bc into shared memory
const DeviceMatrix Bot(sBot, d-1, q);
kernels::internal::LoadB<D,Q>(d-1, q, BO, sBot);
const DeviceMatrix Bct(sBct, d, q);
kernels::internal::LoadB<D,Q>(d, q, BC, sBct);
const DeviceCube QQ(sQQ, q, q, vdim);
const DeviceCube QD(sQD, q, d, vdim);
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const real_t cst_val_0 = C(0,0,0,0);
const real_t cst_val_1 = C(1,0,0,0);
MFEM_FOREACH_THREAD(y,y,q)
{
MFEM_FOREACH_THREAD(x,x,q)
{
const real_t J0 = J(x,y,0,vd,e);
const real_t J1 = J(x,y,1,vd,e);
const real_t C0 = cst ? cst_val_0 : C(0,x,y,e);
const real_t C1 = cst ? cst_val_1 : C(1,x,y,e);
QQ(x,y,vd) = W(x,y)*(J0*C0 + J1*C1);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const int nx = (vd == 0) ? d : d-1;
DeviceMatrix Btx = (vd == 0) ? Bct : Bot;
MFEM_FOREACH_THREAD(qy,y,q)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t qd = 0.0;
for (int qx = 0; qx < q; ++qx)
{
qd += QQ(qx,qy,vd) * Btx(dx,qx);
}
QD(dx,qy,vd) = qd;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const int nx = (vd == 0) ? d : d-1;
const int ny = (vd == 1) ? d : d-1;
DeviceMatrix Bty = (vd == 1) ? Bct : Bot;
DeviceTensor<4> Yxy(Y, nx, ny, vdim, ne);
MFEM_FOREACH_THREAD(dy,y,ny)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t dd = 0.0;
for (int qy = 0; qy < q; ++qy)
{
dd += QD(dx,qy,vd) * Bty(dy,qy);
}
Yxy(dx,dy,vd,e) += dd;
}
}
}
MFEM_SYNC_THREAD;
});
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 1, 2>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 2, 3>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 3, 4>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 4, 5>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 5, 6>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 6, 7>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 7, 8>();
VectorFEDomainLFIntegrator::AddSpecialization<FiniteElement::CURL, 3, 8, 9>();
}
template<int T_D1D = 0, int T_Q1D = 0>
static void HdivDLFAssemble3D(
const int ne, const int d, const int q, const int *markers, const real_t *bo,
const real_t *bc, const real_t *j, const real_t *weights,
const Vector &coeff, real_t *y)
/// \cond DO_NOT_DOCUMENT
VectorFEDomainLFIntegrator::AssembleKernelType
VectorFEDomainLFIntegrator::AssembleKernels::Fallback(
FiniteElement::DerivType TestType, int DIM, int, int)
{
MFEM_VERIFY(T_D1D || d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D,
"Problem size too large.");
MFEM_VERIFY(T_Q1D || q <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D,
"Problem size too large.");
static constexpr int vdim = 3;
const auto F = coeff.Read();
const auto M = Reshape(markers, ne);
const auto BO = Reshape(bo, q, d-1);
const auto BC = Reshape(bc, q, d);
const auto J = Reshape(j, q, q, q, vdim, vdim, ne);
const auto W = Reshape(weights, q, q, q);
const bool cst = coeff.Size() == vdim;
const auto C = cst ? Reshape(F,vdim,1,1,1,1) : Reshape(F,vdim,q,q,q,ne);
auto Y = Reshape(y, 2*(d-1)*(d-1)*d, ne);
mfem::forall_3D(ne, q, q, vdim, [=] MFEM_HOST_DEVICE (int e)
if (TestType == FiniteElement::DIV)
{
if (M(e) == 0) { return; } // ignore
constexpr int Q = T_Q1D ? T_Q1D : DofQuadLimits::HDIV_MAX_Q1D;
constexpr int D = T_D1D ? T_D1D : DofQuadLimits::HDIV_MAX_D1D;
MFEM_SHARED real_t sBot[Q*D];
MFEM_SHARED real_t sBct[Q*D];
// Bo and Bc into shared memory
const DeviceMatrix Bot(sBot, d-1, q);
kernels::internal::LoadB<D,Q>(d-1, q, BO, sBot);
const DeviceMatrix Bct(sBct, d, q);
kernels::internal::LoadB<D,Q>(d, q, BC, sBct);
MFEM_SHARED real_t sm0[vdim*Q*Q*Q];
MFEM_SHARED real_t sm1[vdim*Q*Q*Q];
DeviceTensor<4> QQQ(sm1, q, q, q, vdim);
DeviceTensor<4> DQQ(sm0, d, q, q, vdim);
DeviceTensor<4> DDQ(sm1, d, d, q, vdim);
MFEM_FOREACH_THREAD(vd,z,vdim)
if (DIM == 2)
{
const real_t cst_val_0 = C(0,0,0,0,0);
const real_t cst_val_1 = C(1,0,0,0,0);
const real_t cst_val_2 = C(2,0,0,0,0);
MFEM_FOREACH_THREAD(y,y,q)
{
MFEM_FOREACH_THREAD(x,x,q)
{
for (int z = 0; z < q; ++z)
{
const real_t J0 = J(x,y,z,0,vd,e);
const real_t J1 = J(x,y,z,1,vd,e);
const real_t J2 = J(x,y,z,2,vd,e);
const real_t C0 = cst ? cst_val_0 : C(0,x,y,z,e);
const real_t C1 = cst ? cst_val_1 : C(1,x,y,z,e);
const real_t C2 = cst ? cst_val_2 : C(2,x,y,z,e);
QQQ(x,y,z,vd) = W(x,y,z)*(J0*C0 + J1*C1 + J2*C2);
}
}
}
return HdivDLFAssemble2D<0, 0>;
}
MFEM_SYNC_THREAD;
// Apply Bt operator
MFEM_FOREACH_THREAD(vd,z,vdim)
if (DIM == 3)
{
const int nx = (vd == 0) ? d : d-1;
DeviceMatrix Btx = (vd == 0) ? Bct : Bot;
MFEM_FOREACH_THREAD(qy,y,q)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t u[Q];
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz) { u[qz] = 0.0; }
MFEM_UNROLL(Q)
for (int qx = 0; qx < q; ++qx)
{
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
u[qz] += QQQ(qx,qy,qz,vd) * Btx(dx,qx);
}
}
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz) { DQQ(dx,qy,qz,vd) = u[qz]; }
}
}
return HdivDLFAssemble3D<0, 0>;
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const int nx = (vd == 0) ? d : d-1;
const int ny = (vd == 1) ? d : d-1;
DeviceMatrix Bty = (vd == 1) ? Bct : Bot;
MFEM_FOREACH_THREAD(dy,y,ny)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t u[Q];
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz) { u[qz] = 0.0; }
MFEM_UNROLL(Q)
for (int qy = 0; qy < q; ++qy)
{
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
u[qz] += DQQ(dx,qy,qz,vd) * Bty(dy,qy);
}
}
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz) { DDQ(dx,dy,qz,vd) = u[qz]; }
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(vd,z,vdim)
{
const int nx = (vd == 0) ? d : d-1;
const int ny = (vd == 1) ? d : d-1;
const int nz = (vd == 2) ? d : d-1;
DeviceTensor<5> Yxyz(Y, nx, ny, nz, vdim, ne);
DeviceMatrix Btz = (vd == 2) ? Bct : Bot;
MFEM_FOREACH_THREAD(dy,y,ny)
{
MFEM_FOREACH_THREAD(dx,x,nx)
{
real_t u[D];
MFEM_UNROLL(D)
for (int dz = 0; dz < nz; ++dz) { u[dz] = 0.0; }
MFEM_UNROLL(Q)
for (int qz = 0; qz < q; ++qz)
{
MFEM_UNROLL(D)
for (int dz = 0; dz < nz; ++dz)
{
u[dz] += DDQ(dx,dy,qz,vd) * Btz(dz,qz);
}
}
MFEM_UNROLL(D)
for (int dz = 0; dz < nz; ++dz) { Yxyz(dx,dy,dz,vd,e) += u[dz]; }
}
}
}
MFEM_SYNC_THREAD;
});
}
static void HdivDLFAssemble(const FiniteElementSpace &fes,
const IntegrationRule *ir,
const Array<int> &markers,
const Vector &coeff,
Vector &y)
{
Mesh &mesh = *fes.GetMesh();
const int dim = mesh.Dimension();
const FiniteElement *el = fes.GetTypicalFE();
const auto *vel = dynamic_cast<const VectorTensorFiniteElement *>(el);
MFEM_VERIFY(vel != nullptr, "Must be VectorTensorFiniteElement");
const MemoryType mt = Device::GetDeviceMemoryType();
const DofToQuad &maps_o = vel->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
const DofToQuad &maps_c = vel->GetDofToQuad(*ir, DofToQuad::TENSOR);
const int d = maps_c.ndof, q = maps_c.nqpt;
constexpr int flags = GeometricFactors::JACOBIANS;
const GeometricFactors *geom = mesh.GetGeometricFactors(*ir, flags, mt);
decltype(&HdivDLFAssemble2D<>) ker =
dim == 2 ? HdivDLFAssemble2D<> : HdivDLFAssemble3D<>;
if (dim==2)
{
if (d==1 && q==1) { ker=HdivDLFAssemble2D<1,1>; }
if (d==2 && q==2) { ker=HdivDLFAssemble2D<2,2>; }
if (d==3 && q==3) { ker=HdivDLFAssemble2D<3,3>; }
if (d==4 && q==4) { ker=HdivDLFAssemble2D<4,4>; }
if (d==5 && q==5) { ker=HdivDLFAssemble2D<5,5>; }
if (d==6 && q==6) { ker=HdivDLFAssemble2D<6,6>; }
if (d==7 && q==7) { ker=HdivDLFAssemble2D<7,7>; }
if (d==8 && q==8) { ker=HdivDLFAssemble2D<8,8>; }
}
if (dim==3)
else if (TestType == FiniteElement::CURL)
{
if (d==2 && q==2) { ker=HdivDLFAssemble3D<2,2>; }
if (d==3 && q==3) { ker=HdivDLFAssemble3D<3,3>; }
if (d==4 && q==4) { ker=HdivDLFAssemble3D<4,4>; }
if (d==5 && q==5) { ker=HdivDLFAssemble3D<5,5>; }
if (d==6 && q==6) { ker=HdivDLFAssemble3D<6,6>; }
if (d==7 && q==7) { ker=HdivDLFAssemble3D<7,7>; }
if (d==8 && q==8) { ker=HdivDLFAssemble3D<8,8>; }
if (DIM == 3)
{
return HcurlDLFAssemble3D<0, 0>;
}
}
MFEM_VERIFY(ker, "No kernel ndof " << d << " nqpt " << q);
const int ne = mesh.GetNE();
const int *M = markers.Read();
const real_t *Bo = maps_o.B.Read();
const real_t *Bc = maps_c.B.Read();
const real_t *J = geom->J.Read();
const real_t *W = ir->GetWeights().Read();
real_t *Y = y.ReadWrite();
ker(ne, d, q, M, Bo, Bc, J, W, coeff, Y);
MFEM_ABORT("");
}
/// \endcond DO_NOT_DOCUMENT
void VectorFEDomainLFIntegrator::AssembleDevice(const FiniteElementSpace &fes,
const Array<int> &markers,
@@ -337,15 +96,23 @@ void VectorFEDomainLFIntegrator::AssembleDevice(const FiniteElementSpace &fes,
QuadratureSpace qs(*fes.GetMesh(), *ir);
CoefficientVector coeff(QF, qs, CoefficientStorage::COMPRESSED);
const int fe_type = fe.GetDerivType();
if (fe_type == FiniteElement::DIV)
{
HdivDLFAssemble(fes, ir, markers, coeff, b);
}
else
{
MFEM_ABORT("Not implemented.");
}
const FiniteElement::DerivType fe_type =
static_cast<FiniteElement::DerivType>(fe.GetDerivType());
Mesh &mesh = *fes.GetMesh();
const int dim = mesh.Dimension();
const FiniteElement *el = fes.GetTypicalFE();
const auto *vel = dynamic_cast<const VectorTensorFiniteElement *>(el);
MFEM_VERIFY(vel != nullptr, "Must be VectorTensorFiniteElement");
const MemoryType mt = Device::GetDeviceMemoryType();
const DofToQuad &maps_o = vel->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
const DofToQuad &maps_c = vel->GetDofToQuad(*ir, DofToQuad::TENSOR);
const int d = maps_c.ndof, q = maps_c.nqpt;
constexpr int flags = GeometricFactors::JACOBIANS;
const GeometricFactors *geom = mesh.GetGeometricFactors(*ir, flags, mt);
AssembleKernels::Run(fe_type, dim, d, q, mesh.GetNE(), markers, geom->J,
ir->GetWeights(), maps_o.B, maps_c.B, coeff, b, d, q);
}
} // namespace mfem
+376
View File
@@ -236,6 +236,58 @@ IntegrationRule::ApplyToKnotIntervals(KnotVector const& kv) const
return kvir;
}
IntegrationRule IntegrationRule::Reorder(const Array<int> &ordering) const
{
const int np = GetNPoints();
MFEM_VERIFY(np == ordering.Size(), "Invalid permutation size");
IntegrationRule ir(np);
ir.SetOrder(GetOrder());
for (int i = 0; i < np; i++)
{
IntegrationPoint &ip_new = ir.IntPoint(i);
const IntegrationPoint &ip_old = IntPoint(ordering[i]);
ip_new.Set(ip_old.x, ip_old.y, ip_old.z, ip_old.weight);
}
return ir;
}
IntegrationRule DuffyTrans(const IntegrationRule &ir, int dim)
{
IntegrationRule ir_mapped(ir.GetNPoints());
ir_mapped.SetOrder(ir.GetOrder());
if (dim == 2)
{
for (int i = 0; i < ir.GetNPoints(); i++)
{
IntegrationPoint &ip_mapped = ir_mapped.IntPoint(i);
ip_mapped.y = ir.IntPoint(i).y * (1 - ir.IntPoint(i).x);
ip_mapped.x = ir.IntPoint(i).x;
ip_mapped.weight = ir.IntPoint(i).weight;
}
return ir_mapped;
}
else if (dim == 3)
{
for (int i = 0; i < ir.GetNPoints(); i++)
{
IntegrationPoint &ip_mapped = ir_mapped.IntPoint(i);
ip_mapped.z = ir.IntPoint(i).z * (1 - ir.IntPoint(i).x) * (1 - ir.IntPoint(
i).y);
ip_mapped.y = ir.IntPoint(i).y * (1 - ir.IntPoint(i).x);
ip_mapped.x = ir.IntPoint(i).x;
ip_mapped.weight = ir.IntPoint(i).weight;
}
return ir_mapped;
}
else
{
MFEM_ABORT("Duffy transformation not implemented for this dimension!");
}
}
#ifdef MFEM_USE_MPFR
// Class for computing hi-precision (HP) quadrature in 1D
@@ -433,6 +485,142 @@ public:
#endif // MFEM_USE_MPFR
void QuadratureFunctions1D::GaussJacobi(const int np, const real_t alpha,
const real_t beta, IntegrationRule* ir)
{
/* The np-point Gauss-Jacobi quadrature rule is exact for polynomials of
degree 2np - 1 with weight function w(x) = (1-x)^alpha * x^beta. The
nodes are the zeros of the Jacobi polynomial P_{np}^{alpha,beta} and
the weights are
w_i = C / [(1 - x_i^2) * P'_{np}^{alpha,beta}(x_i)^2]
C = 2^{alpha + beta + 1} * Gamma(np + alpha + 1) * Gamma(np + beta + 1)
/ [Gamma(np + alpha + beta + 1) * Gamma(np + 1)].
The nodes are computed via nonlinear solve (Newton's method) with an
initial guess corresponding to Gatteschi's asymptotic expansions of the
Jacobi polynomial roots [1].
The current initial guess has been tested and performs well for
np <= 200 and -1 <= alpha, beta <= 4. For larger np, it may be necessary
utilize different initial guesses in the vicinity of x = -1,+1 [2].
[1] Gautschi, W., & Giordano, C. (2008). Luigi Gatteschis work on
asymptotics of special functions and their zeros. Numerical Algorithms,
49, 11-31.
[2] Hale, N., & Townsend, A. (2013). Fast and accurate computation of
Gauss--Legendre and Gauss--Jacobi quadrature nodes and weights.
SIAM Journal on Scientific Computing, 35(2), A652-A674.
*/
ir->SetSize(np);
ir->SetPointIndices();
ir->SetOrder(2*np - 1);
if (alpha <= -1.0 || beta <= -1.0)
{
MFEM_ABORT("Gauss-Jacobi quadrature only defined for alpha > -1 and beta > -1");
}
// Jacobi weight function is undefined whenever alpha <= -1 or beta <= -1
if (alpha > 4.0 || beta > 4.0)
{
MFEM_ABORT("Current Gauss-Jacobi quadrature implementation only tested for alpha <= 4 and beta <= 4");
}
// current asymptotic expansions for initial guess may perform poorly for large alpha, beta
switch (np)
{
case 1:
real_t x = (beta - alpha) / (alpha + beta + 2);
real_t w = pow(2, alpha + beta + 1) * tgamma(alpha + 2) * tgamma(
beta + 2) / (tgamma(alpha + beta + 2));
w = 0.5 * w / pow(2, alpha + beta);
// map weight to to [0,1], with additional 1/(2^(alpha + beta)) factor coming from mapping
// the weight (1-x)^alpha * (1+x)^beta to [0,1] as well.
ir->IntPoint(0).Set1w(0.5 * x + 0.5,
4.0 * w / ((1.0 - x*x) * (alpha + beta + 2) * (alpha + beta + 2)));
return;
}
#ifndef MFEM_USE_MPFR
const int n = np;
// common constants for Jacobi polynomials
real_t ab = alpha + beta;
real_t a2_minus_b2 = (alpha - beta) * (alpha + beta);
// roots of P^(alpha,beta)_n in the interval [-1,1]
for (int i = 1; i <= n; i++)
{
// rather than using Chebyshev points for initial guess, use Gatteschi's asymptotic expansion for roots of Jacobi
// polynomials
real_t n_ab_plus_1 = 2 * n + alpha + beta + 1;
real_t v = (2 * i + alpha - 0.5) * M_PI / n_ab_plus_1;
real_t theta = v + 1.0 / (n_ab_plus_1*n_ab_plus_1) * ((0.25 - alpha*alpha) *
1.0/tan(0.5*v) - (0.25 - beta*beta) * tan(0.5*v));
real_t z = cos(theta);
real_t pp, p1, dz, xi = 0.;
bool done = false;
while (1)
{
real_t p2 = 1;
p1 = ((alpha-beta) + (alpha + beta + 2) * z) / 2;
for (int j = 1; j <= n-1; j++)
{
real_t p3 = p2;
p2 = p1;
real_t jx2_ab = 2 * j + ab;
real_t an = (jx2_ab) * (jx2_ab + 2);
real_t bn = a2_minus_b2;
real_t cn = 2 * (j + alpha) * (j + beta) * (jx2_ab + 2) / (jx2_ab + 1);
real_t D = (jx2_ab + 1) / (2 * (j + 1) * (j + ab + 1) * (jx2_ab));
p1 = ((an * z + bn) * p2 - cn * p3) * D;
}
// p1 is Jacobi polynomial
pp = n * (alpha - beta - (2 * n + ab) * z) * p1 + 2 * (n + alpha) *
(n + beta) * p2;
pp = pp / ((2 * n + ab) * (1 - z*z));
// derivative of the Jacobi polynomial
if (done) { break; }
dz = p1/pp;
#ifdef MFEM_USE_SINGLE
if (std::abs(dz) < 1e-7)
#elif defined MFEM_USE_DOUBLE
if (std::abs(dz) < std::numeric_limits<real_t>::epsilon())
// this seems to cause trouble if we try std::abs(dz) < 1e-16
#else
MFEM_ABORT("Floating point type undefined");
// if (std::abs(dz) < 1e-16)
#endif
{
done = true;
xi = z - dz;
}
z -= dz;
}
real_t c0 = exp(lgamma(n + alpha + 1) - lgamma(n + ab + 1)) * exp(lgamma(
n + beta + 1) - lgamma(n + 1));
// ratio of gamma functions prone to overflow for large n, so compute logarithms
// of Gamma function instead, i.e. Gamma(a)/Gamma(b) = exp(lgamma(a) - lgamma(b))
ir->IntPoint(n-i).x = 0.5 * xi + 0.5;
ir->IntPoint(n-i).weight = 0.5 * c0 * pow(2.0,
ab + 1) / ((1.0 - xi*xi)*pp*pp) / pow(2, ab);
// map nodes and weights to the interval [0,1]
}
#else // MFEM_USE_MPFR is defined
MFEM_ABORT("MPFR implementation of Gauss-Jacobi quadrature not defined yet");
#endif // MFEM_USE_MPFR
}
void QuadratureFunctions1D::GaussLegendre(const int np, IntegrationRule* ir)
{
ir->SetSize(np);
@@ -2362,6 +2550,194 @@ IntegrationRule *IntegrationRules::CubeIntegrationRule(int Order)
return CubeIntRules[Order];
}
StroudIntegrationRules StroudIntRules;
StroudIntegrationRules::StroudIntegrationRules()
{
const MemoryType h_mt = MemoryType::HOST;
SquareStroudIntRules.SetSize(32, h_mt);
SquareStroudIntRules = NULL;
TriangleStroudIntRules.SetSize(32, h_mt);
TriangleStroudIntRules = NULL;
CubeStroudIntRules.SetSize(32, h_mt);
CubeStroudIntRules = NULL;
TetrahedronStroudIntRules.SetSize(32, h_mt);
TetrahedronStroudIntRules = NULL;
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
IntRuleLocks.SetSize(Geometry::NUM_GEOMETRIES, h_mt);
for (int i = 0; i < Geometry::NUM_GEOMETRIES; i++)
{
omp_init_lock(&IntRuleLocks[i]);
}
#endif
}
const IntegrationRule &StroudIntegrationRules::Get(int GeomType, int Order)
{
Array<IntegrationRule *> *ir_array = NULL;
switch (GeomType)
{
case Geometry::TRIANGLE: ir_array = &TriangleStroudIntRules; break;
case Geometry::TETRAHEDRON: ir_array = &TetrahedronStroudIntRules; break;
case Geometry::INVALID:
case Geometry::NUM_GEOMETRIES:
MFEM_ABORT("Unknown type of reference element!");
default:
MFEM_ABORT("Stroud rules only valid for triangular and tetrahedral elements!");
}
if (Order < 0)
{
Order = 0;
}
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
omp_set_lock(&IntRuleLocks[GeomType]);
#endif
if (!HaveIntRule(*ir_array, Order))
{
IntegrationRule *ir = GenerateIntegrationRule(GeomType, Order);
#ifdef MFEM_DEBUG
int RealOrder = Order;
while (RealOrder+1 < ir_array->Size() && (*ir_array)[RealOrder+1] == ir)
{
RealOrder++;
}
MFEM_VERIFY(RealOrder == ir->GetOrder(), "internal error");
#else
MFEM_CONTRACT_VAR(ir);
#endif
}
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
omp_unset_lock(&IntRuleLocks[GeomType]);
#endif
return *(*ir_array)[Order];
}
void StroudIntegrationRules::DeleteIntRuleArray(
Array<IntegrationRule *> &ir_array) const
{
// Many of the intrules have multiple contiguous copies in the ir_array
// so we have to be careful to not delete them twice.
IntegrationRule *ir = NULL;
for (int i = 0; i < ir_array.Size(); i++)
{
if (ir_array[i] != NULL && ir_array[i] != ir)
{
ir = ir_array[i];
delete ir;
}
}
}
StroudIntegrationRules::~StroudIntegrationRules()
{
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
for (int i = 0; i < Geometry::NUM_GEOMETRIES; i++)
{
omp_destroy_lock(&IntRuleLocks[i]);
}
#endif
DeleteIntRuleArray(SquareStroudIntRules);
DeleteIntRuleArray(TriangleStroudIntRules);
DeleteIntRuleArray(CubeStroudIntRules);
DeleteIntRuleArray(TetrahedronStroudIntRules);
}
IntegrationRule *StroudIntegrationRules::GenerateIntegrationRule(int GeomType,
int Order)
{
switch (GeomType)
{
case Geometry::TRIANGLE:
return TriangleStroudIntegrationRule(Order);
case Geometry::TETRAHEDRON:
return TetrahedronStroudIntegrationRule(Order);
case Geometry::INVALID:
case Geometry::NUM_GEOMETRIES:
MFEM_ABORT("Unknown type of reference element!");
default:
MFEM_ABORT("Stroud rules only valid for triangular and tetrahedral elements!");
}
return NULL;
}
/* Integration rule in reference triangle according to tensor product Gauss-Jacobi rule.
The nodes and weights are used in the original form defined on the reference
square to evaluate the component 1D basis functions. Mapping to the reference
triangle via IntegrationRule::DuffyTrans() occurs only in evaluation of coefficient
vectors, see e.g. MassIntegrator::AssemblePASimplex. */
IntegrationRule *StroudIntegrationRules::TriangleStroudIntegrationRule(
int Order)
{
int RealOrder = GetSegmentRealOrder(Order);
// Order is one of {RealOrder-1,RealOrder}
// if (!HaveIntRule(SegmentIntRules, RealOrder))
// {
// SegmentIntegrationRule(RealOrder);
// }
IntegrationRule ir_0_0;
// Gauss-Jacobi is exact for 2*n-1
int n = RealOrder/2 + 1;
QuadratureFunctions1D::GaussJacobi(n, 0.0, 0.0, &ir_0_0);
IntegrationRule ir_1_0;
QuadratureFunctions1D::GaussJacobi(n, 1.0, 0.0, &ir_1_0);
AllocIntRule(TriangleStroudIntRules, RealOrder); // RealOrder >= Order
// create rule in unit square
TriangleStroudIntRules[RealOrder-1] =
TriangleStroudIntRules[RealOrder] =
new IntegrationRule(ir_1_0, ir_0_0);
// map rule to reference triangle
// TriangleStroudIntRules[RealOrder-1]->DuffyTrans(2);
*TriangleStroudIntRules[RealOrder-1] =
DuffyTrans(*TriangleStroudIntRules[RealOrder-1], 2);
return TriangleStroudIntRules[Order];
}
/* Integration rule in reference tetrahedron according to tensor product Gauss-Jacobi rule.
The nodes and weights are used in the original form defined on the reference
square to evaluate the component 1D basis functions. Mapping to the reference
triangle via IntegrationRule::DuffyTrans() occurs only in evaluation of coefficient
vectors, see e.g. MassIntegrator::AssemblePASimplex. */
IntegrationRule *StroudIntegrationRules::TetrahedronStroudIntegrationRule(
int Order)
{
int RealOrder = GetSegmentRealOrder(Order);
// Order is one of {RealOrder-1,RealOrder}
IntegrationRule ir_0_0;
int n = RealOrder/2 + 1;
QuadratureFunctions1D::GaussJacobi(n, 0.0, 0.0, &ir_0_0);
IntegrationRule ir_1_0;
QuadratureFunctions1D::GaussJacobi(n, 1.0, 0.0, &ir_1_0);
IntegrationRule ir_2_0;
QuadratureFunctions1D::GaussJacobi(n, 2.0, 0.0, &ir_2_0);
AllocIntRule(TetrahedronStroudIntRules, RealOrder); // RealOrder >= Order
// create rule in unit cube
TetrahedronStroudIntRules[RealOrder-1] =
TetrahedronStroudIntRules[RealOrder] =
new IntegrationRule(ir_2_0, ir_1_0, ir_0_0);
// map rule to reference tetrahedron
// TetrahedronStroudIntRules[RealOrder-1]->DuffyTrans(3);
*TetrahedronStroudIntRules[RealOrder-1] =
DuffyTrans(*TetrahedronStroudIntRules[RealOrder-1], 3);
return TetrahedronStroudIntRules[Order];
}
IntegrationRule& NURBSMeshRules::GetElementRule(const int elem,
const int patch, const int *ijk,
Array<const KnotVector*> const& kv) const
+68
View File
@@ -269,6 +269,13 @@ public:
/// applying this rule on each knot interval.
IntegrationRule* ApplyToKnotIntervals(KnotVector const& kv) const;
/** @brief Returns an integration rule such that the new IntegrationPoints
* are re-ordered based on @a ordering.
*
* @details In the new integration rule, ip_new[i] = ip_old[ordering[i]]
*/
IntegrationRule Reorder(const Array<int> &ordering) const;
/// Destroys an IntegrationRule object
~IntegrationRule() { }
};
@@ -378,6 +385,8 @@ public:
These methods calculate the actual points and weights for the different
types of quadrature rules. */
///@{
static void GaussJacobi(const int np, const real_t alpha, const real_t beta,
IntegrationRule* ir);
static void GaussLegendre(const int np, IntegrationRule* ir);
static void GaussLobatto(const int np, IntegrationRule *ir);
static void OpenUniform(const int np, IntegrationRule *ir);
@@ -487,12 +496,71 @@ public:
~IntegrationRules();
};
/// Container class for integration rules
class StroudIntegrationRules
{
private:
Array<IntegrationRule *> SquareStroudIntRules;
Array<IntegrationRule *> TriangleStroudIntRules;
Array<IntegrationRule *> CubeStroudIntRules;
Array<IntegrationRule *> TetrahedronStroudIntRules;
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
Array<omp_lock_t> IntRuleLocks;
#endif
void AllocIntRule(Array<IntegrationRule *> &ir_array, int Order) const
{
if (ir_array.Size() <= Order)
{
ir_array.SetSize(Order + 1, NULL);
}
}
bool HaveIntRule(Array<IntegrationRule *> &ir_array, int Order) const
{
return (ir_array.Size() > Order && ir_array[Order] != NULL);
}
int GetSegmentRealOrder(int Order) const
{
return Order | 1; // valid for all quad_type's
}
void DeleteIntRuleArray(Array<IntegrationRule *> &ir_array) const;
/// The following methods allocate new IntegrationRule objects without
/// checking if they already exist. To avoid memory leaks use
/// IntegrationRules::Get(int GeomType, int Order) instead.
IntegrationRule *GenerateIntegrationRule(int GeomType, int Order);
IntegrationRule *TriangleStroudIntegrationRule(int Order);
IntegrationRule *TetrahedronStroudIntegrationRule(int Order);
public:
/// Sets initial sizes for the integration rule arrays, but rules
/// are defined the first time they are requested with the Get method.
explicit StroudIntegrationRules();
/// Returns a Stroud integration rule for given GeomType and Order.
const IntegrationRule &Get(int GeomType, int Order);
/// Destroys an StroudIntegrationRules object
~StroudIntegrationRules();
};
/// A global object with all integration rules (defined in intrules.cpp)
extern MFEM_EXPORT IntegrationRules IntRules;
/// A global object with all refined integration rules
extern MFEM_EXPORT IntegrationRules RefinedIntRules;
/// A global object with all Stroud integration rules (defined in intrules.cpp)
extern MFEM_EXPORT StroudIntegrationRules StroudIntRules;
/// Duffy Transformation of 2D and 3D tensor product rules of the form
/// $X(t) = \sum_{i=1}^{d+1} \lambda_i(t) * x_i$, where $x_i$ are the vertices
/// of the simplex and $\lambda_i = t_i * (1-\lambda_1-...-\lambda_{i-1})$, with
/// $t$ being the coordinates in the unit square/cube. This function is used only
/// in the partial assembly of Bernstein elements on simplices and does NOT
/// modify the quadrature weights.
IntegrationRule DuffyTrans(const IntegrationRule &ir, int dim);
}
#endif
+7
View File
@@ -471,6 +471,13 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
}
}
VectorFEDomainLFIntegrator::VectorFEDomainLFIntegrator(
VectorCoefficient &F, const IntegrationRule *ir)
: DeltaLFIntegrator(F, ir), QF(F)
{
static Kernels kernels{};
}
void VectorFEDomainLFIntegrator::AssembleRHSElementVect(
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
{
+36 -2
View File
@@ -369,8 +369,8 @@ private:
Vector vec;
public:
VectorFEDomainLFIntegrator(VectorCoefficient &F)
: DeltaLFIntegrator(F), QF(F) { }
VectorFEDomainLFIntegrator(VectorCoefficient &F,
const IntegrationRule *ir = nullptr);
void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
@@ -387,6 +387,40 @@ public:
Vector &b) override;
using LinearFormIntegrator::AssembleRHSElementVect;
/// @param ne number of elements
/// @param markers array where entry markers[e] == 0 to skip assembly over
/// element e element
/// @param jac Spatial Jacobians evaluated at all quadrature points
/// @param weights 1D quadrature weights
/// @param testBO 1D open basis test functions
/// @param testBC 1D closed basis test functions
/// @param coeff coefficient values evaluated at quadrature points, possibly
/// compressed.
/// @param d number of 1D closed dofs
/// @param q number of 1D quadrature points
using AssembleKernelType = void (*)(const int NE, const Array<int> &markers,
const Vector &jac,
const Array<real_t> &weights,
const Array<real_t> &testBO,
const Array<real_t> &testBC,
const Vector &coeff, Vector &y,
const int testd1d, const int q1d);
/// parameters: test_fetype, ndims, test_d1d, q1d
MFEM_REGISTER_KERNELS(AssembleKernels, AssembleKernelType,
(FiniteElement::DerivType, int, int, int));
struct Kernels
{
Kernels();
};
template <FiniteElement::DerivType TestType, int DIM, int TEST_D1D, int Q1D>
static void AddSpecialization()
{
AssembleKernels::Specialization<TestType, DIM, TEST_D1D, Q1D>::Add();
}
};
/// $ (Q, \mathrm{curl}(v))_{\Omega} $ for Nedelec Elements
+3
View File
@@ -224,6 +224,9 @@ public:
/** @see GetGradient(const Vector &) */
Operator &GetGradient(const Vector &x, bool finalize) const;
/// Suppress a warning about hiding overloaded virtual function.
using Operator::GetGradient;
/// Update the NonlinearForm to propagate updates of the associated FE space.
/** After calling this method, the essential boundary conditions need to be
set again. */
+354 -63
View File
@@ -10,6 +10,7 @@
// CONTRIBUTING.md for details.
#include "particleset.hpp"
#include "../general/forall.hpp"
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
@@ -225,6 +226,7 @@ void ParticleSet::AddParticles(const Array<IDType> &new_ids,
}
}
// Add new ids
ids.HostReadWrite();
ids.Append(new_ids);
// Update data
@@ -244,6 +246,102 @@ void ParticleSet::AddParticles(const Array<IDType> &new_ids,
#if defined(MFEM_USE_MPI) && defined(MFEM_USE_GSLIB)
/// \cond DO_NOT_DOCUMENT
// Static helper: gather selected particle-vector entries into a compact buffer.
// nvcc does not allow extended host/device lambdas in non-public members.
static void GatherParticleVectorDevice(const ParticleVector &pv,
const Array<int> &send_idxs,
Vector &send_data,
int nsend)
{
const int vdim = pv.GetVDim();
const int ordering = pv.GetOrdering();
const int num_particles = pv.GetNumParticles();
const MemoryClass device_mc = Device::GetDeviceMemoryClass();
send_data.SetSize(nsend*vdim);
real_t *d_send_data =
send_data.GetMemory().Write(device_mc, send_data.Size());
const real_t *d_src = pv.GetMemory().Read(device_mc, pv.Size());
const int *d_send_idxs = send_idxs.GetMemory().Read(device_mc, nsend);
mfem::forall(nsend, [=] MFEM_HOST_DEVICE (int i)
{
const int p = d_send_idxs[i];
const int offset = (ordering == Ordering::byVDIM) ? p * vdim : p;
const int stride = (ordering == Ordering::byVDIM) ? 1 : num_particles;
for (int c = 0; c < vdim; c++)
{
d_send_data[i*vdim + c] = d_src[offset + c*stride];
}
});
}
// Static helper: gather selected tag values into a compact buffer.
// nvcc does not allow extended host/device lambdas in non-public members.
static void GatherParticleTagsDevice(const Array<int> &tag,
const Array<int> &send_idxs,
Array<int> &send_tag,
int nsend)
{
const MemoryClass device_mc = Device::GetDeviceMemoryClass();
send_tag.SetSize(nsend);
int *d_send_tag = send_tag.GetMemory().Write(device_mc, nsend);
const int *d_tag = tag.GetMemory().Read(device_mc, tag.Size());
const int *d_send_idxs = send_idxs.GetMemory().Read(device_mc, nsend);
mfem::forall(nsend, [=] MFEM_HOST_DEVICE (int i)
{
d_send_tag[i] = d_tag[d_send_idxs[i]];
});
}
// Static helper: scatter compact particle-vector entries to particle storage.
// nvcc does not allow extended host/device lambdas in non-public members.
static void ScatterParticleVectorDevice(ParticleVector &pv,
const Vector &recv_data,
const Array<int> &recv_locs,
int nrecv)
{
const int vdim = pv.GetVDim();
const int ordering = pv.GetOrdering();
const int num_particles = pv.GetNumParticles();
const MemoryClass device_mc = Device::GetDeviceMemoryClass();
const real_t *d_recv_data =
recv_data.GetMemory().Read(device_mc, recv_data.Size());
const int *d_recv_locs = recv_locs.GetMemory().Read(device_mc, nrecv);
real_t *d_dst = pv.GetMemory().ReadWrite(device_mc, pv.Size());
mfem::forall(nrecv, [=] MFEM_HOST_DEVICE (int i)
{
const int p = d_recv_locs[i];
const int offset = (ordering == Ordering::byVDIM) ? p * vdim : p;
const int stride = (ordering == Ordering::byVDIM) ? 1 : num_particles;
for (int c = 0; c < vdim; c++)
{
d_dst[offset + c*stride] = d_recv_data[i*vdim + c];
}
});
}
// Static helper: scatter compact tag values to particle storage.
// nvcc does not allow extended host/device lambdas in non-public members.
static void ScatterParticleTagsDevice(Array<int> &tag,
const Array<int> &recv_tag,
const Array<int> &recv_locs,
int nrecv)
{
const MemoryClass device_mc = Device::GetDeviceMemoryClass();
const int *d_recv_tag = recv_tag.GetMemory().Read(device_mc, nrecv);
const int *d_recv_locs = recv_locs.GetMemory().Read(device_mc, nrecv);
int *d_tag = tag.GetMemory().ReadWrite(device_mc, tag.Size());
mfem::forall(nrecv, [=] MFEM_HOST_DEVICE (int i)
{
d_tag[d_recv_locs[i]] = d_recv_tag[i];
});
}
template<size_t NBytes>
void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
const Array<int> &send_idxs,
@@ -266,37 +364,108 @@ void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
array_init(parr_t, &gsl_arr, send_idxs.Size());
pdata_arr = (parr_t*) gsl_arr.ptr;
int nparticles = pset.GetNParticles();
int nsend = send_idxs.Size();
gsl_arr.n = send_idxs.Size();
const int *h_send_idxs_initial = send_idxs.HostRead();
const IDType *h_ids = pset.GetIDs().HostRead();
for (int i = 0; i < send_idxs.Size(); i++)
{
parr_t &pdata = pdata_arr[i];
pdata.id = pset.GetIDs()[send_idxs[i]];
pdata.id = h_ids[h_send_idxs_initial[i]];
}
// Copy particle data directly into pdata
size_t counter = 0;
for (int f = -1; f < pset.GetNFields(); f++)
// Pack coords and fields into the GSLIB send buffer. Device-resident data
// is first gathered into a compact device buffer so that only selected
// particles are copied back to host. Host-resident data is packed directly.
int max_vdim = pset.Coords().GetVDim();
for (int f = 0; f < pset.GetNFields(); f++)
{
int f_vdim = pset.Field(f).GetVDim();
if (f_vdim > max_vdim) { max_vdim = f_vdim; }
}
Vector send_data;
Array<int> send_tag;
if (Device::IsEnabled())
{
send_data.SetSize(nsend * max_vdim); // allocate max size over all fields
send_tag.SetSize(nsend);
}
size_t counter = 0;
for (int f = -1; f < pset.GetNFields(); f++)
{
const ParticleVector &pv = f == -1 ? pset.Coords() : pset.Field(f);
const int vdim = pv.GetVDim();
const int ordering = pv.GetOrdering();
const int num_particles = pv.GetNumParticles();
const bool use_dev = Device::IsEnabled() && pv.UseDevice();
if (use_dev)
{
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
for (int c = 0; c < pv.GetVDim(); c++)
GatherParticleVectorDevice(pv, send_idxs, send_data, nsend);
const real_t *h_send_data = send_data.HostRead();
for (int i = 0; i < nsend; i++)
{
std::memcpy(pdata.data.data() + counter, &pv(send_idxs[i], c),
sizeof(real_t));
counter += sizeof(real_t);
std::memcpy(pdata_arr[i].data.data() + counter,
h_send_data + i*vdim, vdim * sizeof(real_t));
}
}
else
{
const real_t *h_src = pv.HostRead();
const int *h_send_idxs = send_idxs.HostRead();
for (int i = 0; i < nsend; i++)
{
parr_t &pdata = pdata_arr[i];
const int p = h_send_idxs[i];
const int offset = (ordering == Ordering::byVDIM) ? p * vdim : p;
const int stride = (ordering == Ordering::byVDIM) ? 1 :
num_particles;
for (int c = 0; c < vdim; c++)
{
std::memcpy(pdata.data.data() + counter + c*sizeof(real_t),
h_src + offset + c*stride, 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);
}
counter += vdim*sizeof(real_t);
}
int nparticles = pset.GetNParticles();
int nsend = send_idxs.Size();
// Pack tags after all real_t data. Each tag uses the same selective
// device gather path when its Array is device-resident.
for (int t = 0; t < pset.GetNTags(); t++)
{
const Array<int> &tag = pset.Tag(t);
const size_t tag_counter = counter + t*sizeof(int);
const bool use_dev = Device::IsEnabled() && tag.UseDevice();
if (use_dev)
{
GatherParticleTagsDevice(tag, send_idxs, send_tag, nsend);
const int *h_send_tag = send_tag.HostRead();
for (int i = 0; i < nsend; i++)
{
std::memcpy(pdata_arr[i].data.data() + tag_counter,
h_send_tag + i, sizeof(int));
}
}
else
{
const int *h_tag = tag.HostRead();
const int *h_send_idxs = send_idxs.HostRead();
for (int i = 0; i < nsend; i++)
{
std::memcpy(pdata_arr[i].data.data() + tag_counter,
h_tag + h_send_idxs[i], sizeof(int));
}
}
}
// Transfer particles
sarray_transfer_ext(parr_t, &gsl_arr, send_ranks.GetData(),
@@ -304,11 +473,20 @@ void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
// Make sure we have enough space for received particles
int nrecv = (int) gsl_arr.n;
Vector recv_data;
Array<int> recv_tag;
if (Device::IsEnabled())
{
recv_data.SetSize(nrecv * max_vdim);
recv_tag.SetSize(nrecv);
}
int ndelete = nsend - nrecv;
if (ndelete > 0)
{
// Remove unneeded particles
auto datap = const_cast<int*>(send_idxs.GetData());
auto datap = const_cast<int*>(send_idxs.HostRead());
Array<int> delete_idxs(datap + nrecv, ndelete);
pset.RemoveParticles(delete_idxs);
}
@@ -319,47 +497,133 @@ void ParticleSet::TransferParticlesImpl(ParticleSet &pset,
pdata_arr = (parr_t*) gsl_arr.ptr;
// Add newly-recvd data directly to active state
// Make a list of new IDs to add
int num_new = nrecv > nsend ? nrecv - nsend : 0;
Array<IDType> new_ids(num_new);
for (int i = 0; i < num_new; i++)
{
new_ids[i] = pdata_arr[nsend + i].id;
}
// Add particles in batch
Array<int> new_indices;
if (num_new > 0)
{
pset.AddParticles(new_ids, &new_indices);
}
// Map each received packet to the local particle slot it updates.
Array<int> recv_locs(nrecv);
int *h_recv_locs = recv_locs.HostWrite();
const int *h_send_idxs_recv = send_idxs.HostRead();
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);
h_recv_locs[i] = h_send_idxs_recv[i];
pset.UpdateID(h_recv_locs[i], pdata.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
h_recv_locs[i] = new_indices[i - nsend];
}
}
size_t counter = 0;
for (int f = -1; f < pset.GetNFields(); f++)
// Unpack coords and fields from GSLIB host packets. Device-resident
// destinations use a compact host buffer followed by a device scatter.
size_t recv_counter = 0;
for (int f = -1; f < pset.GetNFields(); f++)
{
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
const int vdim = pv.GetVDim();
const int ordering = pv.GetOrdering();
const int num_particles = pv.GetNumParticles();
const bool use_dev = Device::IsEnabled() && pv.UseDevice();
if (use_dev)
{
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
for (int c = 0; c < pv.GetVDim(); c++)
recv_data.SetSize(nrecv*vdim);
real_t *h_recv_data = recv_data.HostWrite();
for (int i = 0; i < nrecv; i++)
{
real_t& val = pv(new_loc_idx, c);
std::memcpy(&val, pdata.data.data() + counter, sizeof(real_t));
counter += sizeof(real_t);
std::memcpy(h_recv_data + i*vdim,
pdata_arr[i].data.data() + recv_counter,
vdim*sizeof(real_t));
}
ScatterParticleVectorDevice(pv, recv_data, recv_locs, nrecv);
}
else
{
real_t *h_dst = pv.HostReadWrite();
const int *h_recv_locs_read = recv_locs.HostRead();
for (int i = 0; i < nrecv; i++)
{
parr_t &pdata = pdata_arr[i];
const int p = h_recv_locs_read[i];
const int offset = (ordering == Ordering::byVDIM) ? p * vdim : p;
const int stride = (ordering == Ordering::byVDIM) ? 1 :
num_particles;
for (int c = 0; c < vdim; c++)
{
std::memcpy(h_dst + offset + c*stride,
pdata.data.data() + recv_counter + c*sizeof(real_t),
sizeof(real_t));
}
}
}
for (int t = 0; t < pset.GetNTags(); t++)
recv_counter += vdim*sizeof(real_t);
}
// Unpack tags after all real_t data, using the same compact scatter path
// for device-resident tag arrays.
for (int t = 0; t < pset.GetNTags(); t++)
{
Array<int> &tag = pset.Tag(t);
const size_t tag_counter = recv_counter + t*sizeof(int);
const bool use_dev = Device::IsEnabled() && tag.UseDevice();
if (use_dev)
{
Array<int> &tag_arr = pset.Tag(t);
std::memcpy(&tag_arr[new_loc_idx],
pdata.data.data() + counter, sizeof(int));
counter += sizeof(int);
recv_tag.SetSize(nrecv);
int *h_recv_tag = recv_tag.HostWrite();
for (int i = 0; i < nrecv; i++)
{
std::memcpy(h_recv_tag + i,
pdata_arr[i].data.data() + tag_counter, sizeof(int));
}
ScatterParticleTagsDevice(tag, recv_tag, recv_locs, nrecv);
}
else
{
int *h_tag = tag.HostReadWrite();
const int *h_recv_locs_read = recv_locs.HostRead();
for (int i = 0; i < nrecv; i++)
{
std::memcpy(h_tag + h_recv_locs_read[i],
pdata_arr[i].data.data() + tag_counter, sizeof(int));
}
}
}
array_free(&gsl_arr);
// Restore Device validity if needed
for (int f = -1; f < pset.GetNFields(); f++)
{
ParticleVector &pv = (f == -1 ? pset.Coords() : pset.Field(f));
pv.ReadWrite(pv.UseDevice());
}
for (int t = 0; t < pset.GetNTags(); t++)
{
Array<int> &tag_arr = pset.Tag(t);
if (tag_arr.UseDevice()) { tag_arr.ReadWrite(true); }
}
}
template<size_t NBytes>
@@ -526,11 +790,14 @@ 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_)
const Array<const char*> &tag_names_,
bool use_device)
: id_stride(id_stride_),
id_counter(id_counter_),
coords(dim, coords_ordering)
{
if (use_device) { coords.UseDevice(true); }
// Initialize fields
for (int f = 0; f < field_vdims.Size(); f++)
{
@@ -580,21 +847,22 @@ bool ParticleSet::IsValidParticle(const Particle &p) const
}
ParticleSet::ParticleSet(int num_particles, int dim,
Ordering::Type coords_ordering)
Ordering::Type coords_ordering,
bool use_device)
: ParticleSet(1, 0, num_particles, dim, coords_ordering, Array<int>(),
Array<Ordering::Type>(), Array<const char*>(), 0,
Array<const char*>())
Array<const char*>(), use_device)
{
}
ParticleSet::ParticleSet(int num_particles, int dim,
const Array<int> &field_vdims, int num_tags,
Ordering::Type all_ordering)
Ordering::Type all_ordering, bool use_device)
: 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))
GetEmptyNameArray(num_tags), use_device)
{
}
@@ -602,11 +870,11 @@ 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)
Ordering::Type all_ordering, bool use_device)
: ParticleSet(1, 0, num_particles, dim, all_ordering, field_vdims,
GetOrderingArray(all_ordering, field_vdims.Size()),
field_names_, num_tags,
tag_names_)
tag_names_, use_device)
{
}
@@ -616,9 +884,9 @@ ParticleSet::ParticleSet(int num_particles, int dim,
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_)
const Array<const char*> &tag_names_, bool use_device)
: ParticleSet(1, 0, num_particles, dim, coords_ordering, field_vdims,
field_orderings, field_names_, num_tags, tag_names_)
field_orderings, field_names_, num_tags, tag_names_, use_device)
{
}
@@ -627,21 +895,21 @@ ParticleSet::ParticleSet(int num_particles, int dim,
#ifdef MFEM_USE_MPI
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
Ordering::Type coords_ordering)
Ordering::Type coords_ordering, bool use_device)
: ParticleSet(comm_, rank_num_particles, dim, coords_ordering, Array<int>(),
Array<Ordering::Type>(), Array<const char*>(), 0,
Array<const char*>())
Array<const char*>(), use_device)
{
};
ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
const Array<int> &field_vdims, int num_tags,
Ordering::Type all_ordering)
Ordering::Type all_ordering, bool use_device)
: 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))
GetEmptyNameArray(num_tags), use_device)
{
}
@@ -650,11 +918,11 @@ 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)
Ordering::Type all_ordering, bool use_device)
: ParticleSet(comm_, rank_num_particles, dim, all_ordering, field_vdims,
GetOrderingArray(all_ordering, field_vdims.Size()),
field_names_, num_tags,
tag_names_)
tag_names_, use_device)
{
}
@@ -664,7 +932,7 @@ ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
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_)
const Array<const char*> &tag_names_, bool use_device)
: ParticleSet(GetSize(comm_), (IDType)GetRank(comm_),
rank_num_particles,
dim,
@@ -673,7 +941,7 @@ ParticleSet::ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
field_orderings,
field_names_,
num_tags,
tag_names_)
tag_names_, use_device)
{
comm = comm_;
#ifdef MFEM_USE_GSLIB
@@ -705,6 +973,7 @@ int ParticleSet::AddField(int vdim, Ordering::Type field_ordering,
}
fields.emplace_back(std::make_unique<ParticleVector>(vdim, field_ordering,
GetNParticles()));
if (coords.UseDevice()) { fields.back()->UseDevice(true); }
field_names.emplace_back(field_name_str);
return GetNFields() - 1;
@@ -718,6 +987,7 @@ int ParticleSet::AddTag(const char* tag_name)
tag_name_str = GetDefaultTagName(tag_names.size());
}
tags.emplace_back(std::make_unique<Array<int>>(GetNParticles()));
if (coords.UseDevice()) { tags.back()->GetMemory().UseDevice(true); }
tag_names.emplace_back(tag_name_str);
return GetNTags() - 1;
@@ -782,7 +1052,7 @@ Particle ParticleSet::GetParticle(int i) const
for (int t = 0; t < GetNTags(); t++)
{
p.Tag(t) = Tag(t)[i];
p.Tag(t) = Tag(t).HostRead()[i];
}
return p;
@@ -790,13 +1060,21 @@ Particle ParticleSet::GetParticle(int i) const
bool ParticleSet::IsParticleRefValid() const
{
if (coords.GetOrdering() == Ordering::byNODES)
if (coords.GetOrdering() == Ordering::byNODES || coords.UseDevice())
{
return false;
}
for (int f = 0; f < GetNFields(); f++)
{
if (fields[f]->GetOrdering() == Ordering::byNODES)
if (fields[f]->GetOrdering() == Ordering::byNODES ||
fields[f]->UseDevice())
{
return false;
}
}
for (int t = 0; t < GetNTags(); t++)
{
if (tags[t]->UseDevice())
{
return false;
}
@@ -806,6 +1084,10 @@ bool ParticleSet::IsParticleRefValid() const
Particle ParticleSet::GetParticleRef(int i)
{
MFEM_ASSERT(IsParticleRefValid(),
"GetParticleRef is only valid when coordinates and fields are "
"ordered byVDIM and particle data is host-resident.");
Particle p = CreateParticle();
Coords().GetValuesRef(i, p.Coords());
@@ -839,7 +1121,7 @@ void ParticleSet::SetParticle(int i, const Particle &p)
for (int t = 0; t < GetNTags(); t++)
{
Tag(t)[i] = p.Tag(t);
Tag(t).HostReadWrite()[i] = p.Tag(t);
}
}
@@ -900,6 +1182,15 @@ void ParticleSet::PrintCSV(const char *fname, const Array<int> &field_idxs,
#ifdef MFEM_USE_MPI
int rank = GetRank(comm);
#endif // MFEM_USE_MPI
// make sure we can read tag data on host. fields and coords will be read as
// needed in the loop below, so we don't need to pre-read them here.
for (int i = 0; i < GetNTags(); i++)
{
tags[i]->HostRead();
}
ids.HostRead();
// Write particle data
for (int i = 0; i < GetNParticles(); i++)
{
ss_data << ids[i];
+49 -12
View File
@@ -211,6 +211,12 @@ public:
* byVDIM). The unique_ptrs to all the ParticleVectors are stored in the
* std::vector \ref fields.
*
* @par Device Behavior:
* When a ParticleSet is constructed with \p use_device=true, \ref coords and
* all ParticleVector fields are marked to use device memory. Fields added
* later through \ref AddField inherit the current device mode (through
* \ref coords).
*
* @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
@@ -369,7 +375,10 @@ protected:
* ID of a particle.
*/
void UpdateID(int local_idx, IDType new_global_id)
{ ids[local_idx] = new_global_id; }
{
ids.HostReadWrite();
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.
@@ -399,12 +408,14 @@ protected:
* @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] use_device Use device memory for particle fields.
*/
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_);
const Array<const char*> &tag_names_,
bool use_device);
public:
@@ -413,9 +424,12 @@ public:
* @param[in] num_particles Number of particles to initialize.
* @param[in] dim Particle spatial dimension.
* @param[in] coords_ordering Ordering of coordinates.
* @param[in] use_device (Optional) Use device memory for particle
* fields.
*/
ParticleSet(int num_particles, int dim,
Ordering::Type coords_ordering=Ordering::byVDIM);
Ordering::Type coords_ordering=Ordering::byVDIM,
bool use_device=false);
/** @brief Construct a serial ParticleSet with specified fields and tags at
* construction.
@@ -426,9 +440,12 @@ public:
* @param[in] num_tags Number of tags to register.
* @param[in] all_ordering (Optional) Ordering of coordinates and
* field ParticleVector.
* @param[in] use_device (Optional) Use device memory for particle
* fields.
*/
ParticleSet(int num_particles, int dim, const Array<int> &field_vdims,
int num_tags, Ordering::Type all_ordering=Ordering::byVDIM);
int num_tags, Ordering::Type all_ordering=Ordering::byVDIM,
bool use_device=false);
/** @brief Construct a serial ParticleSet with specified fields and tags at
* construction, with names.
@@ -441,11 +458,14 @@ public:
* @param[in] tag_names_ Array of tag names.
* @param[in] all_ordering (Optional) Ordering of coordinates and
* field ParticleVector.
* @param[in] use_device (Optional) Use device memory for particle
* fields.
*/
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);
Ordering::Type all_ordering=Ordering::byVDIM,
bool use_device=false);
/** @brief Comprehensive serial constructor of ParticleSet.
*
@@ -457,12 +477,15 @@ public:
* @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] use_device (Optional) Use device memory for particle
* fields.
*/
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_);
const Array<const char*> &tag_names_,
bool use_device=false);
#ifdef MFEM_USE_MPI
/** @brief Construct a parallel ParticleSet.
@@ -471,9 +494,12 @@ public:
* @param[in] rank_num_particles Number of particles to initialize.
* @param[in] dim Particle spatial dimension.
* @param[in] coords_ordering (Optional) Ordering of coordinates.
* @param[in] use_device (Optional) Use device memory for particle
* fields.
*/
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
Ordering::Type coords_ordering=Ordering::byVDIM);
Ordering::Type coords_ordering=Ordering::byVDIM,
bool use_device=false);
/** @brief Construct a parallel ParticleSet with specified fields and tags
* at construction.
@@ -485,10 +511,13 @@ public:
* @param[in] num_tags Number of tags to register.
* @param[in] all_ordering (Optional) Ordering of coordinates and
* field ParticleVector.
* @param[in] use_device (Optional) Use device memory for particle
* fields.
*/
ParticleSet(MPI_Comm comm_, int rank_num_particles, int dim,
const Array<int> &field_vdims, int num_tags,
Ordering::Type all_ordering=Ordering::byVDIM);
Ordering::Type all_ordering=Ordering::byVDIM,
bool use_device=false);
/** @brief Construct a parallel ParticleSet with specified fields and tags
* at construction, with names (for PrintCSV()).
@@ -502,12 +531,15 @@ public:
* @param[in] tag_names_ Array of tag names.
* @param[in] all_ordering (Optional) Ordering of coordinates and
* field ParticleVector.
* @param[in] use_device (Optional) Use device memory for particle
* fields.
*/
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);
Ordering::Type all_ordering=Ordering::byVDIM,
bool use_device=false);
/** @brief Comprehensive parallel constructor of ParticleSet.
*
@@ -520,12 +552,15 @@ public:
* @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] use_device (Optional) Use device memory for particle
* fields.
*/
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_);
const Array<const char*> &tag_names_,
bool use_device=false);
/// Get the MPI communicator for this ParticleSet.
MPI_Comm GetComm() const { return comm; };
@@ -545,6 +580,8 @@ public:
* @param[in] field_ordering (Optional) Ordering::Type of the field.
* @param[in] field_name (Optional) Name of the field.
*
* @note New fields inherit the current device mode of \ref coords.
*
* @return Index of the newly-added field.
*/
int AddField(int vdim, Ordering::Type field_ordering=Ordering::byVDIM,
@@ -637,8 +674,8 @@ public:
/** @brief Determine if GetParticleRef is valid.
*
* If coordinates and all fields are ordered byVDIM, then returns true.
* Otherwise, false.
* Returns true when coordinates and all fields are ordered byVDIM and
* particle data is host-resident. Otherwise, false.
*/
bool IsParticleRefValid() const;
+9 -2
View File
@@ -50,14 +50,21 @@ ElementRestriction::ElementRestriction(const FiniteElementSpace &f,
const FiniteElement *fe = fes.GetFE(e);
auto el_t = dynamic_cast<const TensorBasisElement*>(fe);
auto el_n = dynamic_cast<const NodalFiniteElement*>(fe);
if (el_t || el_n) { continue; }
auto el_p = dynamic_cast<const H1Pos_TriangleElement*>(fe) ||
dynamic_cast<const H1Pos_TetrahedronElement*>(fe);
if (el_t || el_n || el_p) { continue; }
MFEM_ABORT("Finite element not suitable for lexicographic ordering");
}
const FiniteElement *fe = fes.GetTypicalFE();
auto el_t = dynamic_cast<const TensorBasisElement*>(fe);
auto el_n = dynamic_cast<const NodalFiniteElement*>(fe);
auto el_p_tri = dynamic_cast<const H1Pos_TriangleElement*>(fe);
auto el_p_tet = dynamic_cast<const H1Pos_TetrahedronElement*>(fe);
const Array<int> &fe_dof_map =
(el_t) ? el_t->GetDofMap() : el_n->GetLexicographicOrdering();
el_n ? el_n->GetLexicographicOrdering() :
el_t ? el_t->GetDofMap() :
el_p_tri ? el_p_tri->GetDofMap() :
el_p_tet->GetDofMap();
MFEM_VERIFY(fe_dof_map.Size() > 0, "invalid dof map");
dof_map = fe_dof_map.HostRead();
}
+303 -113
View File
@@ -3758,7 +3758,8 @@ void TMOP_Integrator::SetInitialMeshPos(const GridFunction *x0)
TMOP_Integrator::~TMOP_Integrator()
{
delete lim_func;
delete adapt_lim_gf;
for (int i = 0; i < adapt_lim_gf.Size(); i++) { delete adapt_lim_gf[i]; }
for (int i = 0; i < adapt_lim_gf0.Size(); i++) { delete adapt_lim_gf0[i]; }
delete surf_fit_gf;
delete surf_fit_limiter;
delete surf_fit_grad;
@@ -3800,20 +3801,13 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const GridFunction &z0,
AdaptivityEvaluator &ae,
real_t delta_max)
{
MFEM_VERIFY(delta_max > 0.0,
"EnableAdaptiveLimiting requires delta_max > 0.0.");
adapt_lim_gf0 = &z0;
delete adapt_lim_gf;
adapt_lim_gf = new GridFunction(z0);
adapt_lim_coeff = &coeff;
adapt_lim_eval = &ae;
adapt_lim_delta_max = delta_max;
adapt_lim_eval->SetSerialMetaInfo(*z0.FESpace()->GetMesh(),
*z0.FESpace());
adapt_lim_eval->SetInitialField
(*adapt_lim_gf->FESpace()->GetMesh()->GetNodes(), *adapt_lim_gf);
Array<const GridFunction *> z0_arr(1);
Array<Coefficient *> c_arr(1);
Array<real_t> d_arr(1);
z0_arr[0] = &z0;
c_arr[0] = &coeff;
d_arr[0] = delta_max;
EnableAdaptiveLimiting(z0_arr, c_arr, ae, d_arr);
}
#ifdef MFEM_USE_MPI
@@ -3822,21 +3816,111 @@ void TMOP_Integrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
AdaptivityEvaluator &ae,
real_t delta_max)
{
MFEM_VERIFY(delta_max > 0.0,
"EnableAdaptiveLimiting requires delta_max > 0.0.");
Array<const ParGridFunction *> z0_arr(1);
Array<Coefficient *> c_arr(1);
Array<real_t> d_arr(1);
z0_arr[0] = &z0;
c_arr[0] = &coeff;
d_arr[0] = delta_max;
EnableAdaptiveLimiting(z0_arr, c_arr, ae, d_arr);
}
#endif
adapt_lim_gf0 = &z0;
adapt_lim_pgf0 = &z0;
delete adapt_lim_gf;
adapt_lim_gf = new GridFunction(z0);
adapt_lim_coeff = &coeff;
void TMOP_Integrator::
EnableAdaptiveLimiting(const Array<const GridFunction *> &z0,
const Array<Coefficient *> &coeff,
AdaptivityEvaluator &ae, const Array<real_t> &delta_max)
{
MFEM_VERIFY(z0.Size() > 0, "Requires at least one field.");
MFEM_VERIFY(z0.Size() == coeff.Size(), "Requires one Coefficient per field.");
MFEM_VERIFY(z0.Size() == delta_max.Size(), "Requires one delta_max per field.");
for (int i = 0; i < delta_max.Size(); i++)
{
MFEM_VERIFY(delta_max[i] > 0.0, "Requires delta_max > 0.0.");
}
// Verify compatibility of input fields.
const FiniteElementSpace *sfes = z0[0]->FESpace();
MFEM_VERIFY(sfes->GetVDim() == 1, "Expects scalar input GridFunctions.");
const int ndofs = sfes->GetVSize();
Mesh *mesh = sfes->GetMesh();
MFEM_VERIFY(mesh->GetNodes(), "EnableAdaptiveLimiting requires mesh Nodes.");
for (int i = 0; i < z0.Size(); i++)
{
MFEM_VERIFY(z0[i], "NULL GridFunction pointer.");
const FiniteElementSpace *fes_i = z0[i]->FESpace();
MFEM_VERIFY(fes_i->GetVDim() == 1, "Expects scalar input GridFunctions.");
MFEM_VERIFY(fes_i->GetVSize() == ndofs,
"All fields must be on the same FE space.");
MFEM_VERIFY(fes_i->GetMesh() == mesh,
"All fields must be on the same Mesh.");
MFEM_VERIFY(coeff[i], "NULL Coefficient pointer.");
}
// Delete previous adaptive limiting data.
for (int i = 0; i < adapt_lim_gf.Size(); i++) { delete adapt_lim_gf[i]; }
for (int i = 0; i < adapt_lim_gf0.Size(); i++) { delete adapt_lim_gf0[i]; }
adapt_lim_coeff.SetSize(coeff.Size());
for (int i = 0; i < coeff.Size(); i++) { adapt_lim_coeff[i] = coeff[i]; }
adapt_lim_eval = &ae;
adapt_lim_delta_max = delta_max;
adapt_lim_init_nodes = *mesh->GetNodes();
adapt_lim_eval->SetParMetaInfo(*z0.ParFESpace()->GetParMesh(),
*z0.ParFESpace());
adapt_lim_eval->SetInitialField
(*adapt_lim_gf->FESpace()->GetMesh()->GetNodes(), *adapt_lim_gf);
// Use one internal vector field (vdim = #fields) so remapping can be done in
// one call and incremental remap state (when provided by the evaluator) is
// preserved across TMOP iterations.
//
// Use Ordering::byNODES for the packed vector field so packing / unpacking
// can be done with contiguous sub-vector copies (device-friendly).
const int nal = z0.Size();
const Ordering::Type packed_ord = Ordering::byNODES;
// Setup the evaluator.
#ifdef MFEM_USE_MPI
if (auto pfes = dynamic_cast<const ParFiniteElementSpace *>(sfes))
{
auto *pm = pfes->GetParMesh();
MFEM_VERIFY(pm, "Invalid ParMesh.");
ParFiniteElementSpace vfes(pm, pfes->FEColl(), nal, packed_ord);
adapt_lim_eval->SetParMetaInfo(*pm, vfes);
}
else
#endif
{
FiniteElementSpace vfes(mesh, sfes->FEColl(), nal, packed_ord);
adapt_lim_eval->SetSerialMetaInfo(*mesh, vfes);
}
// Copy the initial fields; remapped fields are initialized to the same data.
adapt_lim_gf0.SetSize(z0.Size());
adapt_lim_gf.SetSize(z0.Size());
for (int i = 0; i < z0.Size(); i++)
{
adapt_lim_gf0[i] = new GridFunction(*z0[i]);
adapt_lim_gf[i] = new GridFunction(*z0[i]);
}
// Initialize the evaluator with the packed vector field.
Vector init_field_vec;
init_field_vec.SetSize(nal * ndofs, *adapt_lim_gf0[0]);
init_field_vec.UseDevice(adapt_lim_gf0[0]->UseDevice());
for (int c = 0; c < nal; c++)
{
init_field_vec.SetVector(*adapt_lim_gf0[c], c * ndofs);
}
adapt_lim_eval->SetInitialField(adapt_lim_init_nodes, init_field_vec);
}
#ifdef MFEM_USE_MPI
void TMOP_Integrator::
EnableAdaptiveLimiting(const Array<const ParGridFunction *> &z0,
const Array<Coefficient *> &coeff,
AdaptivityEvaluator &ae, const Array<real_t> &delta_max)
{
Array<const GridFunction *> z0_base(z0.Size());
for (int i = 0; i < z0.Size(); i++) { z0_base[i] = z0[i]; }
EnableAdaptiveLimiting(z0_base, coeff, ae, delta_max);
}
#endif
@@ -4157,26 +4241,61 @@ void TMOP_Integrator::GetSurfaceFittingErrors(const Vector &d_loc,
void TMOP_Integrator::UpdateAfterMeshTopologyChange()
{
if (adapt_lim_gf)
if (adapt_lim_gf.Size() > 0)
{
adapt_lim_gf->Update();
adapt_lim_eval->SetSerialMetaInfo(*adapt_lim_gf->FESpace()->GetMesh(),
*adapt_lim_gf->FESpace());
adapt_lim_eval->SetInitialField
(*adapt_lim_gf->FESpace()->GetMesh()->GetNodes(), *adapt_lim_gf);
for (int i = 0; i < adapt_lim_gf0.Size(); i++) { adapt_lim_gf0[i]->Update(); }
for (int i = 0; i < adapt_lim_gf.Size(); i++) { adapt_lim_gf[i]->Update(); }
Mesh *mesh = adapt_lim_gf[0]->FESpace()->GetMesh();
// Same setup as in EnableAdaptiveLimiting().
const int nal = adapt_lim_coeff.Size();
const Ordering::Type packed_ord = Ordering::byNODES;
FiniteElementSpace vfes(mesh, adapt_lim_gf[0]->FESpace()->FEColl(), nal,
packed_ord);
adapt_lim_eval->SetSerialMetaInfo(*mesh, vfes);
adapt_lim_init_nodes = *mesh->GetNodes();
const int ndofs = adapt_lim_gf0[0]->Size();
Vector init_field_vec;
init_field_vec.SetSize(nal * ndofs, *adapt_lim_gf0[0]);
init_field_vec.UseDevice(adapt_lim_gf0[0]->UseDevice());
for (int c = 0; c < nal; c++)
{
init_field_vec.SetVector(*adapt_lim_gf0[c], c * ndofs);
}
adapt_lim_eval->SetInitialField(adapt_lim_init_nodes, init_field_vec);
}
}
#ifdef MFEM_USE_MPI
void TMOP_Integrator::ParUpdateAfterMeshTopologyChange()
{
if (adapt_lim_gf)
if (adapt_lim_gf.Size() > 0)
{
adapt_lim_gf->Update();
adapt_lim_eval->SetParMetaInfo(*adapt_lim_pgf0->ParFESpace()->GetParMesh(),
*adapt_lim_pgf0->ParFESpace());
adapt_lim_eval->SetInitialField
(*adapt_lim_gf->FESpace()->GetMesh()->GetNodes(), *adapt_lim_gf);
for (int i = 0; i < adapt_lim_gf0.Size(); i++) { adapt_lim_gf0[i]->Update(); }
for (int i = 0; i < adapt_lim_gf.Size(); i++) { adapt_lim_gf[i]->Update(); }
// Same setup as in EnableAdaptiveLimiting().
auto *pfes = dynamic_cast<ParFiniteElementSpace *>(adapt_lim_gf[0]->FESpace());
MFEM_VERIFY(pfes, "internal error");
ParMesh *pmesh = pfes->GetParMesh();
const int nal = adapt_lim_coeff.Size();
const Ordering::Type packed_ord = Ordering::byNODES;
ParFiniteElementSpace vfes(pmesh, pfes->FEColl(), nal, packed_ord);
adapt_lim_eval->SetParMetaInfo(*pmesh, vfes);
adapt_lim_init_nodes = *pmesh->GetNodes();
const int ndofs = adapt_lim_gf0[0]->Size();
Vector init_field_vec;
init_field_vec.SetSize(nal * ndofs, *adapt_lim_gf0[0]);
init_field_vec.UseDevice(adapt_lim_gf0[0]->UseDevice());
for (int c = 0; c < nal; c++)
{
init_field_vec.SetVector(*adapt_lim_gf0[c], c * ndofs);
}
adapt_lim_eval->SetInitialField(adapt_lim_init_nodes, init_field_vec);
}
}
#endif
@@ -4208,7 +4327,8 @@ real_t TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
// No adaptive limiting / surface fitting terms if the function is called
// as part of a FD derivative computation (because we include the exact
// derivatives of these terms in FD computations).
const bool adaptive_limiting = (adapt_lim_gf && fd_call_flag == false);
const bool adaptive_limiting = (adapt_lim_gf.Size() > 0 &&
fd_call_flag == false);
const bool surface_fit = (surf_fit_marker && fd_call_flag == false);
DSh.SetSize(dof, dim);
@@ -4271,11 +4391,21 @@ real_t TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
// the physical coordinates (i.e. changes in 'elfun'), e.g. when the
// coefficient is a ConstantCoefficient or a GridFunctionCoefficient.
const int nal = adapt_lim_coeff.Size();
const int nqp = ir.GetNPoints();
Vector adapt_lim_gf_q, adapt_lim_gf0_q;
if (adaptive_limiting)
{
adapt_lim_gf->GetValues(el_id, ir, adapt_lim_gf_q);
adapt_lim_gf0->GetValues(el_id, ir, adapt_lim_gf0_q);
adapt_lim_gf_q.SetSize(nal * nqp);
adapt_lim_gf0_q.SetSize(nal * nqp);
Vector zc, z0c;
for (int c = 0; c < nal; c++)
{
zc.MakeRef(adapt_lim_gf_q, c * nqp, nqp);
z0c.MakeRef(adapt_lim_gf0_q, c * nqp, nqp);
adapt_lim_gf[c]->GetValues(el_id, ir, zc);
adapt_lim_gf0[c]->GetValues(el_id, ir, z0c);
}
}
for (int i = 0; i < ir.GetNPoints(); i++)
@@ -4307,9 +4437,13 @@ real_t TMOP_Integrator::GetElementEnergy(const FiniteElement &el,
// Contribution from the adaptive limiting term.
if (adaptive_limiting)
{
const real_t diff = (adapt_lim_gf_q(i) - adapt_lim_gf0_q(i)) /
adapt_lim_delta_max;
val += adapt_lim_coeff->Eval(*Tpr, ip) * lim_normal * diff * diff;
for (int c = 0; c < nal; c++)
{
const int idx = c * nqp + i;
const real_t diff = (adapt_lim_gf_q(idx) - adapt_lim_gf0_q(idx)) /
adapt_lim_delta_max[c];
val += adapt_lim_coeff[c]->Eval(*Tpr, ip) * lim_normal * diff * diff;
}
}
energy += weight * val;
@@ -4602,7 +4736,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
// Define ref->physical transformation, when a Coefficient is specified.
IsoparametricTransformation *Tpr = NULL;
if (metric_coeff || lim_coeff || adapt_lim_gf ||
if (metric_coeff || lim_coeff || adapt_lim_gf.Size() > 0 ||
surf_fit_gf || surf_fit_pos || exact_action)
{
Tpr = new IsoparametricTransformation;
@@ -4700,7 +4834,7 @@ void TMOP_Integrator::AssembleElementVectorExact(const FiniteElement &el,
}
}
if (adapt_lim_gf) { AssembleElemVecAdaptLim(el, *Tpr, ir, weights, PMatO); }
if (adapt_lim_gf.Size() > 0) { AssembleElemVecAdaptLim(el, *Tpr, ir, weights, PMatO); }
if (surf_fit_gf || surf_fit_pos) { AssembleElemVecSurfFit(el, *Tpr, PMatO); }
delete Tpr;
@@ -4774,7 +4908,8 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
// Define ref->physical transformation, when a Coefficient is specified.
IsoparametricTransformation *Tpr = NULL;
if (metric_coeff || lim_coeff || adapt_lim_gf || surf_fit_gf || surf_fit_pos)
if (metric_coeff || lim_coeff || adapt_lim_gf.Size() > 0 ||
surf_fit_gf || surf_fit_pos)
{
Tpr = new IsoparametricTransformation;
Tpr->SetFE(&el);
@@ -4829,7 +4964,7 @@ void TMOP_Integrator::AssembleElementGradExact(const FiniteElement &el,
}
}
if (adapt_lim_gf) { AssembleElemGradAdaptLim(el, *Tpr, ir, weights, elmat); }
if (adapt_lim_gf.Size() > 0) { AssembleElemGradAdaptLim(el, *Tpr, ir, weights, elmat); }
if (surf_fit_gf || surf_fit_pos) { AssembleElemGradSurfFit(el, *Tpr, elmat);}
delete Tpr;
@@ -4842,34 +4977,42 @@ void TMOP_Integrator::AssembleElemVecAdaptLim(const FiniteElement &el,
DenseMatrix &mat)
{
const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size();
Vector shape(dof), adapt_lim_gf_e, adapt_lim_gf_q, adapt_lim_gf0_q(nqp);
const int nal = adapt_lim_coeff.Size();
Vector shape(dof), adapt_lim_gf_e, adapt_lim_gf_q(nqp), adapt_lim_gf0_q(nqp);
Array<int> dofs;
adapt_lim_gf->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
adapt_lim_gf->GetSubVector(dofs, adapt_lim_gf_e);
adapt_lim_gf->GetValues(Tpr.ElementNo, ir, adapt_lim_gf_q);
adapt_lim_gf0->GetValues(Tpr.ElementNo, ir, adapt_lim_gf0_q);
adapt_lim_gf[0]->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
// Project the gradient of adapt_lim_gf in the same space.
// The FE coefficients of the gradient go in adapt_lim_gf_grad_e.
DenseMatrix adapt_lim_gf_grad_e(dof, dim);
DenseMatrix grad_phys; // This will be (dof x dim, dof).
el.ProjectGrad(el, Tpr, grad_phys);
Vector grad_ptr(adapt_lim_gf_grad_e.GetData(), dof*dim);
grad_phys.Mult(adapt_lim_gf_e, grad_ptr);
Vector adapt_lim_gf_grad_q(dim);
for (int q = 0; q < nqp; q++)
for (int c = 0; c < nal; c++)
{
const IntegrationPoint &ip = ir.IntPoint(q);
el.CalcShape(ip, shape);
const real_t delta2 = adapt_lim_delta_max[c] * adapt_lim_delta_max[c];
adapt_lim_gf[c]->GetSubVector(dofs, adapt_lim_gf_e);
adapt_lim_gf[c]->GetValues(Tpr.ElementNo, ir, adapt_lim_gf_q);
adapt_lim_gf0[c]->GetValues(Tpr.ElementNo, ir, adapt_lim_gf0_q);
adapt_lim_gf_grad_e.MultTranspose(shape, adapt_lim_gf_grad_q);
adapt_lim_gf_grad_q *= 2.0 * (adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) /
adapt_lim_delta_max / adapt_lim_delta_max;
adapt_lim_gf_grad_q *= weights(q) * lim_normal * adapt_lim_coeff->Eval(Tpr, ip);
DenseMatrix adapt_lim_gf_grad_e(dof, dim);
Vector grad_ptr(adapt_lim_gf_grad_e.GetData(), dof*dim);
grad_phys.Mult(adapt_lim_gf_e, grad_ptr);
AddMultVWt(shape, adapt_lim_gf_grad_q, mat);
for (int q = 0; q < nqp; q++)
{
const IntegrationPoint &ip = ir.IntPoint(q);
el.CalcShape(ip, shape);
adapt_lim_gf_grad_e.MultTranspose(shape, adapt_lim_gf_grad_q);
adapt_lim_gf_grad_q *= 2.0 * (adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) /
delta2;
adapt_lim_gf_grad_q *=
weights(q) * lim_normal * adapt_lim_coeff[c]->Eval(Tpr, ip);
AddMultVWt(shape, adapt_lim_gf_grad_q, mat);
}
}
}
@@ -4880,60 +5023,66 @@ void TMOP_Integrator::AssembleElemGradAdaptLim(const FiniteElement &el,
DenseMatrix &mat)
{
const int dof = el.GetDof(), dim = el.GetDim(), nqp = weights.Size();
Vector shape(dof), adapt_lim_gf_e, adapt_lim_gf_q, adapt_lim_gf0_q(nqp);
const int nal = adapt_lim_coeff.Size();
Vector shape(dof), adapt_lim_gf_e, adapt_lim_gf_q(nqp), adapt_lim_gf0_q(nqp);
Array<int> dofs;
adapt_lim_gf->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
adapt_lim_gf->GetSubVector(dofs, adapt_lim_gf_e);
adapt_lim_gf->GetValues(Tpr.ElementNo, ir, adapt_lim_gf_q);
adapt_lim_gf0->GetValues(Tpr.ElementNo, ir, adapt_lim_gf0_q);
adapt_lim_gf[0]->FESpace()->GetElementDofs(Tpr.ElementNo, dofs);
// Project the gradient of adapt_lim_gf in the same space.
// The FE coefficients of the gradient go in adapt_lim_gf_grad_e.
DenseMatrix adapt_lim_gf_grad_e(dof, dim);
DenseMatrix grad_phys; // This will be (dof x dim, dof).
el.ProjectGrad(el, Tpr, grad_phys);
Vector grad_ptr(adapt_lim_gf_grad_e.GetData(), dof*dim);
grad_phys.Mult(adapt_lim_gf_e, grad_ptr);
// Project the gradient of each gradient of adapt_lim_gf in the same space.
// The FE coefficients of the second derivatives go in adapt_lim_gf_hess_e.
DenseMatrix adapt_lim_gf_hess_e(dof*dim, dim);
Mult(grad_phys, adapt_lim_gf_grad_e, adapt_lim_gf_hess_e);
// Reshape to be more convenient later (no change in the data).
adapt_lim_gf_hess_e.SetSize(dof, dim*dim);
Vector adapt_lim_gf_grad_q(dim);
DenseMatrix adapt_lim_gf_hess_q(dim, dim);
for (int q = 0; q < nqp; q++)
for (int c = 0; c < nal; c++)
{
const IntegrationPoint &ip = ir.IntPoint(q);
el.CalcShape(ip, shape);
const real_t delta2 = adapt_lim_delta_max[c] * adapt_lim_delta_max[c];
adapt_lim_gf[c]->GetSubVector(dofs, adapt_lim_gf_e);
adapt_lim_gf[c]->GetValues(Tpr.ElementNo, ir, adapt_lim_gf_q);
adapt_lim_gf0[c]->GetValues(Tpr.ElementNo, ir, adapt_lim_gf0_q);
adapt_lim_gf_grad_e.MultTranspose(shape, adapt_lim_gf_grad_q);
Vector gg_ptr(adapt_lim_gf_hess_q.GetData(), dim*dim);
adapt_lim_gf_hess_e.MultTranspose(shape, gg_ptr);
DenseMatrix adapt_lim_gf_grad_e(dof, dim);
Vector grad_ptr(adapt_lim_gf_grad_e.GetData(), dof*dim);
grad_phys.Mult(adapt_lim_gf_e, grad_ptr);
const real_t coeff = adapt_lim_coeff->Eval(Tpr, ip);
const real_t factor =
weights(q) * lim_normal * coeff * 2.0 /
(adapt_lim_delta_max * adapt_lim_delta_max);
// Project the gradient of each gradient of adapt_lim_gf in the same space.
// The FE coefficients of the second derivatives go in adapt_lim_gf_hess_e.
DenseMatrix adapt_lim_gf_hess_e(dof*dim, dim);
Mult(grad_phys, adapt_lim_gf_grad_e, adapt_lim_gf_hess_e);
// Reshape to be more convenient later (no change in the data).
adapt_lim_gf_hess_e.SetSize(dof, dim*dim);
for (int i = 0; i < dof * dim; i++)
for (int q = 0; q < nqp; q++)
{
const int idof = i % dof, idim = i / dof;
for (int j = 0; j <= i; j++)
const IntegrationPoint &ip = ir.IntPoint(q);
el.CalcShape(ip, shape);
adapt_lim_gf_grad_e.MultTranspose(shape, adapt_lim_gf_grad_q);
Vector gg_ptr(adapt_lim_gf_hess_q.GetData(), dim*dim);
adapt_lim_gf_hess_e.MultTranspose(shape, gg_ptr);
const real_t coeff_q = adapt_lim_coeff[c]->Eval(Tpr, ip);
const real_t factor =
weights(q) * lim_normal * coeff_q * 2.0 /
delta2;
for (int i = 0; i < dof * dim; i++)
{
const int jdof = j % dof, jdim = j / dof;
const real_t entry =
factor *
(adapt_lim_gf_grad_q(idim) * shape(idof) *
adapt_lim_gf_grad_q(jdim) * shape(jdof) +
(adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) *
adapt_lim_gf_hess_q(idim, jdim) * shape(idof) * shape(jdof));
mat(i, j) += entry;
if (i != j) { mat(j, i) += entry; }
const int idof = i % dof, idim = i / dof;
for (int j = 0; j <= i; j++)
{
const int jdof = j % dof, jdim = j / dof;
const real_t entry =
factor *
(adapt_lim_gf_grad_q(idim) * shape(idof) *
adapt_lim_gf_grad_q(jdim) * shape(jdof) +
(adapt_lim_gf_q(q) - adapt_lim_gf0_q(q)) *
adapt_lim_gf_hess_q(idim, jdim) * shape(idof) * shape(jdof));
mat(i, j) += entry;
if (i != j) { mat(j, i) += entry; }
}
}
}
}
@@ -5206,7 +5355,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
fd_call_flag = false;
// Contributions from adaptive limiting, surface fitting (exact derivatives).
if (adapt_lim_gf || surf_fit_gf || surf_fit_pos)
if (adapt_lim_gf.Size() > 0 || surf_fit_gf || surf_fit_pos)
{
const IntegrationRule &ir = ActionIntegrationRule(el);
const int nqp = ir.GetNPoints();
@@ -5230,7 +5379,7 @@ void TMOP_Integrator::AssembleElementVectorFD(const FiniteElement &el,
}
PMatO.UseExternalData(elvect.GetData(), dof, dim);
if (adapt_lim_gf) { AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); }
if (adapt_lim_gf.Size() > 0) { AssembleElemVecAdaptLim(el, Tpr, ir, weights, PMatO); }
if (surf_fit_gf || surf_fit_pos) { AssembleElemVecSurfFit(el, Tpr, PMatO); }
}
}
@@ -5316,7 +5465,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
fd_call_flag = false;
// Contributions from adaptive limiting.
if (adapt_lim_gf || surf_fit_gf || surf_fit_pos)
if (adapt_lim_gf.Size() > 0 || surf_fit_gf || surf_fit_pos)
{
const IntegrationRule &ir = GradientIntegrationRule(el);
const int nqp = ir.GetNPoints();
@@ -5339,7 +5488,7 @@ void TMOP_Integrator::AssembleElementGradFD(const FiniteElement &el,
ir.IntPoint(q).weight;
}
if (adapt_lim_gf) { AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); }
if (adapt_lim_gf.Size() > 0) { AssembleElemGradAdaptLim(el, Tpr, ir, weights, elmat); }
if (surf_fit_gf || surf_fit_pos) { AssembleElemGradSurfFit(el, Tpr, elmat); }
}
}
@@ -5686,9 +5835,22 @@ UpdateAfterMeshPositionChange(const Vector &d, const FiniteElementSpace &d_fes)
}
// Update adapt_lim_gf if adaptive limiting is enabled.
if (adapt_lim_gf)
if (adapt_lim_gf.Size() > 0)
{
adapt_lim_eval->ComputeAtNewPosition(x_loc, *adapt_lim_gf, ordering);
// All adapt_lim_gf are remapped as a multi-component vector.
const int nal = adapt_lim_coeff.Size();
const int ndofs = adapt_lim_gf0[0]->Size();
Vector new_field_vec;
new_field_vec.SetSize(nal * ndofs, *adapt_lim_gf[0]);
new_field_vec.UseDevice(adapt_lim_gf[0]->UseDevice());
adapt_lim_eval->ComputeAtNewPosition(x_loc, new_field_vec, ordering);
for (int c = 0; c < nal; c++)
{
const real_t *src = new_field_vec.Read() + c * ndofs;
real_t *dst = adapt_lim_gf[c]->Write();
internal::device_copy(dst, src, ndofs);
}
if (PA.enabled)
{
PA.AL_grads_assembled = false;
@@ -5698,9 +5860,17 @@ UpdateAfterMeshPositionChange(const Vector &d, const FiniteElementSpace &d_fes)
// Refresh PA.ALF from the updated adapt_lim_gf.
const ElementDofOrdering ord = ElementDofOrdering::LEXICOGRAPHIC;
const Operator *alf_R =
adapt_lim_gf->FESpace()->GetElementRestriction(ord);
alf_R->Mult(*adapt_lim_gf, PA.ALF);
const FiniteElementSpace *alfes = adapt_lim_gf[0]->FESpace();
const Operator *alf_R = alfes->GetElementRestriction(ord);
const int Esize = alf_R->Height();
Vector ALFc;
for (int c = 0; c < nal; c++)
{
MFEM_VERIFY(adapt_lim_gf[c]->Size() == ndofs, "internal error");
ALFc.MakeRef(PA.ALF, c * Esize, Esize);
alf_R->Mult(*adapt_lim_gf[c], ALFc);
}
// Step 2 of PA.ALFmF0 update: add the new ALF.
PA.ALFmF0 += PA.ALF;
@@ -5917,7 +6087,7 @@ ComputeUntangleMetricQuantiles(const Vector &d, const FiniteElementSpace &fes)
dynamic_cast<const ParFiniteElementSpace *>(&fes);
#endif
if (wcuo && wcuo->GetBarrierType() ==
if (wcuo->GetBarrierType() ==
TMOP_WorstCaseUntangleOptimizer_Metric::BarrierType::Shifted)
{
real_t min_detT = ComputeMinDetT(x_loc, fes);
@@ -5929,7 +6099,7 @@ ComputeUntangleMetricQuantiles(const Vector &d, const FiniteElementSpace &fes)
MPITypeMap<real_t>::mpi_type, MPI_MIN, pfes->GetComm());
}
#endif
if (wcuo) { wcuo->SetMinDetT(min_detT_all); }
wcuo->SetMinDetT(min_detT_all);
}
real_t max_muT = ComputeUntanglerMaxMuBarrier(x_loc, fes);
@@ -5975,6 +6145,16 @@ void TMOPComboIntegrator::EnableAdaptiveLimiting(const GridFunction &z0,
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
}
void TMOPComboIntegrator::
EnableAdaptiveLimiting(const Array<const GridFunction *> &z0,
const Array<Coefficient *> &coeff,
AdaptivityEvaluator &ae, const Array<real_t> &delta_max)
{
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
}
#ifdef MFEM_USE_MPI
void TMOPComboIntegrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
Coefficient &coeff,
@@ -5985,6 +6165,16 @@ void TMOPComboIntegrator::EnableAdaptiveLimiting(const ParGridFunction &z0,
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
}
void TMOPComboIntegrator::
EnableAdaptiveLimiting(const Array<const ParGridFunction *> &z0,
const Array<Coefficient *> &coeff,
AdaptivityEvaluator &ae, const Array<real_t> &delta_max)
{
MFEM_VERIFY(tmopi.Size() > 0, "No TMOP_Integrators were added.");
tmopi[0]->EnableAdaptiveLimiting(z0, coeff, ae, delta_max);
}
#endif
void TMOPComboIntegrator::SetLimitingNodes(const GridFunction &n0)
+34 -11
View File
@@ -2038,14 +2038,17 @@ protected:
real_t lim_normal;
// Adaptive limiting.
const GridFunction *adapt_lim_gf0; // Not owned.
#ifdef MFEM_USE_MPI
const ParGridFunction *adapt_lim_pgf0;
#endif
GridFunction *adapt_lim_gf; // Owned. Updated by adapt_lim_eval.
Coefficient *adapt_lim_coeff; // Not owned.
AdaptivityEvaluator *adapt_lim_eval; // Not owned.
real_t adapt_lim_delta_max = 1.0;
// Adaptive limiting fields. Each field adds a term to the integral:
// int [ c_k (z_k(x) - z_k0(x0))^2 / delta_max_k^2 ] dx
// with one Coefficient per field. The fields z_k(x) are remapped from their
// initial values z_k0(x0) through a single AdaptivityEvaluator instance.
// All GridFunctions must use the same FE space.
Array<GridFunction *> adapt_lim_gf0; // Owned. Initial fields z_k0(x0).
Array<GridFunction *> adapt_lim_gf; // Owned. Remapped fields z_k(x).
Vector adapt_lim_init_nodes; // Owned. Initial mesh nodes (ldofs).
Array<Coefficient *> adapt_lim_coeff; // Not owned, one per field.
AdaptivityEvaluator *adapt_lim_eval; // Not owned. Used for all fields.
Array<real_t> adapt_lim_delta_max; // Per-field delta_max_k (>0).
// Surface fitting.
const Array<bool> *surf_fit_marker; // Not owned. Nodes to fit.
@@ -2141,13 +2144,13 @@ protected:
{
bool enabled;
int dim, ne, nq;
int nal = 0; // number of adaptive limiting fields
mutable DenseTensor Jtr;
mutable bool Jtr_needs_update;
mutable bool Jtr_debug_grad;
mutable Vector E, O, X0, XL, H, C0, LD, H0, MC, ALC,
ALF, ALFmF0, ALFG, ALFH;
ALF, ALFmF0, ALFG, ALFH, ALD;
mutable bool AL_grads_assembled;
real_t al_delta;
const DofToQuad *maps;
const DofToQuad *maps_lim = nullptr;
const DofToQuad *maps_nodes = nullptr;
@@ -2314,7 +2317,6 @@ public:
integ_order(-1), metric_coeff(NULL), metric_normal(1.0),
lim_nodes0(NULL), lim_coeff(NULL),
lim_dist(NULL), lim_func(NULL), lim_normal(1.0),
adapt_lim_gf0(NULL), adapt_lim_gf(NULL), adapt_lim_coeff(NULL),
adapt_lim_eval(NULL),
surf_fit_marker(NULL), surf_fit_coeff(NULL),
surf_fit_gf(NULL), surf_fit_eval(NULL),
@@ -2403,10 +2405,21 @@ public:
Smaller values activate the term faster. */
void EnableAdaptiveLimiting(const GridFunction &z0, Coefficient &coeff,
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
/// Multi-field adaptive limiting with per-field delta_max values. All
/// GridFunctions must be on the same FiniteElementSpace.
void EnableAdaptiveLimiting(const Array<const GridFunction *> &z0,
const Array<Coefficient *> &coeff,
AdaptivityEvaluator &ae,
const Array<real_t> &delta_max);
#ifdef MFEM_USE_MPI
/// Parallel support for adaptive limiting.
void EnableAdaptiveLimiting(const ParGridFunction &z0, Coefficient &coeff,
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
/// Multi-field parallel adaptive limiting with per-field delta_max values.
void EnableAdaptiveLimiting(const Array<const ParGridFunction *> &z0,
const Array<Coefficient *> &coeff,
AdaptivityEvaluator &ae,
const Array<real_t> &delta_max);
#endif
/** @brief Fitting of certain DOFs to the zero level set of a function.
@@ -2632,10 +2645,20 @@ public:
/// Adds the adaptive limiting term to the first integrator.
void EnableAdaptiveLimiting(const GridFunction &z0, Coefficient &coeff,
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
/// Multi-field adaptive limiting with per-field delta_max values.
void EnableAdaptiveLimiting(const Array<const GridFunction *> &z0,
const Array<Coefficient *> &coeff,
AdaptivityEvaluator &ae,
const Array<real_t> &delta_max);
#ifdef MFEM_USE_MPI
/// Parallel support for adaptive limiting.
void EnableAdaptiveLimiting(const ParGridFunction &z0, Coefficient &coeff,
AdaptivityEvaluator &ae, real_t delta_max = 1.0);
/// Multi-field parallel adaptive limiting with per-field delta_max values.
void EnableAdaptiveLimiting(const Array<const ParGridFunction *> &z0,
const Array<Coefficient *> &coeff,
AdaptivityEvaluator &ae,
const Array<real_t> &delta_max);
#endif
+31 -14
View File
@@ -119,15 +119,14 @@ void TMOP_AssembleDiagPA_AdaptLim_2D(const real_t lim_normal,
const real_t *Jtr = &J(0, 0, qx, qy, e);
const real_t detJtr = kernels::Det<2>(Jtr);
const real_t weight = W(qx, qy) * detJtr;
const real_t coeff = const_coeff ? ALC(0, 0, 0) : ALC(qx, qy, e);
const real_t coeff = const_coeff ? ALC(0,0,0) : ALC(qx, qy, e);
const real_t factor = weight * coeff * normal_inv_delta_sq;
const real_t diff = alf_quad(qy, qx);
const real_t grad_v = ALF_grad(v, qx, qy, e);
const real_t hess_vv = ALF_hess(v, v, qx, qy, e);
const real_t hdiag = factor * (grad_v * grad_v + diff * hess_vv);
QD(qx, dy) += bb * hdiag;
QD(qx, dy) += bb * factor * (grad_v*grad_v + diff * hess_vv);
}
}
}
@@ -176,27 +175,45 @@ MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiagAdaptLim2D);
void TMOP_Integrator::AssembleDiagonalPA_AdaptLim_2D(Vector &diagonal) const
{
const real_t ln = lim_normal;
const real_t delta_max = PA.al_delta;
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 bool const_coeff = PA.ALC.Size() == 1;
const auto ALC = const_coeff
? Reshape(PA.ALC.Read(), 1, 1, 1)
: Reshape(PA.ALC.Read(), q, q, NE);
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
const auto *B = PA.maps->B.Read();
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
const auto ALF_grad = Reshape(PA.ALFG.Read(), 2, q, q, NE);
const auto ALF_hess = Reshape(PA.ALFH.Read(), 2, 2, q, q, NE);
auto D = Reshape(diagonal.ReadWrite(), d, d, 2, NE);
TMOPAssembleDiagAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
const real_t *ALD = PA.ALD.HostRead();
const int ndof_el = d * d;
const int nqp_el = q * q;
const int ALF_stride = ndof_el * NE;
const int ALFG_stride = 2 * nqp_el * NE;
const int ALFH_stride = 2 * 2 * nqp_el * NE;
const bool const_coeff = (PA.ALC.Size() == nal);
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
const real_t *ALC_all = PA.ALC.Read();
const real_t *ALFmF0_all = PA.ALFmF0.Read();
const real_t *ALFG_all = PA.ALFG.Read();
const real_t *ALFH_all = PA.ALFH.Read();
for (int c = 0; c < nal; c++)
{
const real_t delta_max = ALD[c];
const auto ALC = const_coeff
? Reshape(ALC_all + c, 1, 1, 1)
: Reshape(ALC_all + c * ALC_stride, q, q, NE);
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, NE);
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 2, q, q, NE);
const auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 2, 2, q, q, NE);
TMOPAssembleDiagAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
}
}
} // namespace mfem
+32 -13
View File
@@ -183,15 +183,15 @@ void TMOP_AssembleDiagPA_AdaptLim_3D(const real_t lim_normal,
const real_t *Jtr = &J(0, 0, qx, qy, qz, e);
const real_t detJtr = kernels::Det<3>(Jtr);
const real_t weight = W(qx, qy, qz) * detJtr;
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
const real_t coeff =
const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
const real_t factor = weight * coeff * normal_inv_delta_sq;
const real_t diff = alf_quad(qz, qy, qx);
const real_t grad_v = ALF_grad(v, qx, qy, qz, e);
const real_t hess_vv = ALF_hess(v, v, qx, qy, qz, e);
const real_t hdiag = factor * (grad_v * grad_v + diff * hess_vv);
u += bb * hdiag;
u += bb * factor * (grad_v * grad_v + diff * hess_vv);
}
r0[dz][qy][qx] = u;
}
@@ -265,26 +265,45 @@ MFEM_TMOP_MDQ_SPECIALIZE(TMOPAssembleDiagAdaptLim3D);
void TMOP_Integrator::AssembleDiagonalPA_AdaptLim_3D(Vector &diagonal) const
{
const real_t ln = lim_normal;
const real_t delta_max = PA.al_delta;
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 bool const_coeff = PA.ALC.Size() == 1;
const auto ALC = const_coeff
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
: Reshape(PA.ALC.Read(), q, q, q, NE);
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
const auto *B = PA.maps->B.Read();
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
const auto ALF_grad = Reshape(PA.ALFG.Read(), 3, q, q, q, NE);
const auto ALF_hess = Reshape(PA.ALFH.Read(), 3, 3, q, q, q, NE);
auto D = Reshape(diagonal.ReadWrite(), d, d, d, 3, NE);
TMOPAssembleDiagAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
const real_t *ALD = PA.ALD.HostRead();
const int ndof_el = d * d * d;
const int nqp_el = q * q * q;
const int ALF_stride = ndof_el * NE;
const int ALFG_stride = 3 * nqp_el * NE;
const int ALFH_stride = 3 * 3 * nqp_el * NE;
const bool const_coeff = (PA.ALC.Size() == nal);
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
const real_t *ALC_all = PA.ALC.Read();
const real_t *ALFmF0_all = PA.ALFmF0.Read();
const real_t *ALFG_all = PA.ALFG.Read();
const real_t *ALFH_all = PA.ALFH.Read();
for (int c = 0; c < nal; c++)
{
const real_t delta_max = ALD[c];
const auto ALC = const_coeff
? Reshape(ALC_all + c, 1, 1, 1, 1)
: Reshape(ALC_all + c * ALC_stride, q, q, q, NE);
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, d, NE);
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 3, q, q, q, NE);
const auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 3, 3, q, q, q, NE);
TMOPAssembleDiagAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE,
J, W, B, ALF_grad, ALF_hess, ALFmF0, D, d, q);
}
}
} // namespace mfem
+22 -7
View File
@@ -113,7 +113,7 @@ void TMOP_AssembleGradPA_C0_2D(const real_t lim_normal,
});
}
// Assemble gradient and Hessian of ALF field at quadrature points for AdaptLim (2D)
// Assemble gradient and Hessian of ALF field at quad points for AdaptLim (2D).
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
void TMOP_AssembleGradPA_AdaptLim_2D(const int NE,
const real_t *B_nodes,
@@ -185,7 +185,7 @@ void TMOP_AssembleGradPA_AdaptLim_2D(const int NE,
}
MFEM_SYNC_THREAD;
// Compute/interpolate gradient and Hessian one vector component at a time.
// Compute/interpolate gradient and Hessian, one component at a time.
for (int c = 0; c < 2; c++)
{
kernels::internal::s_regs2d_t<MD1> rgrad_nodes, ddalf_dx_n, ddalf_dy_n;
@@ -326,16 +326,31 @@ void TMOP_Integrator::AssembleGradPA_AdaptLim_2D(const Vector &x) const
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
const auto *B_nodes = PA.maps_nodes->B.Read(),
*G_nodes = PA.maps_nodes->G.Read();
const auto *B = PA.maps->B.Read();
const auto X = Reshape(x.Read(), d, d, 2, NE);
const auto ALF = Reshape(PA.ALF.Read(), d, d, NE);
auto ALF_grad = Reshape(PA.ALFG.Write(), 2, q, q, NE);
auto ALF_hess = Reshape(PA.ALFH.Write(), 2, 2, q, q, NE);
const int ndof_el = d * d;
const int nqp_el = q * q;
const int ALF_stride = ndof_el * NE;
const int ALFG_stride = 2 * nqp_el * NE;
const int ALFH_stride = 2 * 2 * nqp_el * NE;
TMOPAssembleGradAdaptLim2D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
ALF_grad, ALF_hess, d, q);
const real_t *ALF_all = PA.ALF.Read();
real_t *ALFG_all = PA.ALFG.Write();
real_t *ALFH_all = PA.ALFH.Write();
for (int c = 0; c < nal; c++)
{
const auto ALF = Reshape(ALF_all + c * ALF_stride, d, d, NE);
auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 2, q, q, NE);
auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 2, 2, q, q, NE);
TMOPAssembleGradAdaptLim2D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
ALF_grad, ALF_hess, d, q);
}
PA.AL_grads_assembled = true;
}
+25 -8
View File
@@ -164,7 +164,7 @@ void TMOP_Integrator::AssembleGradPA_C0_3D(const Vector &x) const
J, W, b, bld, XL, X, H0, exp_lim, d, q);
}
// Assemble gradient and Hessian of ALF field at quadrature points for AdaptLim (3D)
// Assemble gradient and Hessian of ALF field at quadr points for AdaptLim (3D)
template <int MD1, int MQ1, int T_D1D = 0, int T_Q1D = 0>
void TMOP_AssembleGradPA_AdaptLim_3D(const int NE,
const real_t *B_nodes,
@@ -202,7 +202,8 @@ void TMOP_AssembleGradPA_AdaptLim_3D(const int NE,
kernels::internal::Grad3d(D1D, D1D, smem.d, sB_nodes, sG_nodes, r_X, r_J);
// Compute the reference derivatives of ALF at DOF nodes.
kernels::internal::s_regs3d_t<MD1> alf_n, dalf_dxi_n, dalf_deta_n, dalf_dzeta_n;
kernels::internal::s_regs3d_t<MD1> alf_n;
kernels::internal::s_regs3d_t<MD1> dalf_dxi_n, dalf_deta_n, dalf_dzeta_n;
kernels::internal::LoadDofs3d(e, D1D, ALF, alf_n);
kernels::internal::Contract3d<false, MD1>(D1D, D1D, smem.d,
sG_nodes, sB_nodes, sB_nodes,
@@ -222,7 +223,8 @@ void TMOP_AssembleGradPA_AdaptLim_3D(const int NE,
// Compute/interpolate gradient and Hessian one vector component at a time.
for (int c = 0; c < 3; c++)
{
kernels::internal::s_regs3d_t<MD1> rgrad_nodes, dd_dxi_n, dd_deta_n, dd_dzeta_n;
kernels::internal::s_regs3d_t<MD1> rgrad_nodes;
kernels::internal::s_regs3d_t<MD1> dd_dxi_n, dd_deta_n, dd_dzeta_n;
// Precompute the inverse of the physical Jacobian.
kernels::internal::vd_regs3d_t<3, 3, MD1> Jpr_inv;
@@ -399,16 +401,31 @@ void TMOP_Integrator::AssembleGradPA_AdaptLim_3D(const Vector &x) const
MFEM_VERIFY(d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
const auto *B_nodes = PA.maps_nodes->B.Read(),
*G_nodes = PA.maps_nodes->G.Read();
const auto *B = PA.maps->B.Read();
const auto X = Reshape(x.Read(), d, d, d, 3, NE);
const auto ALF = Reshape(PA.ALF.Read(), d, d, d, NE);
auto ALF_grad = Reshape(PA.ALFG.Write(), 3, q, q, q, NE);
auto ALF_hess = Reshape(PA.ALFH.Write(), 3, 3, q, q, q, NE);
const int ndof_el = d * d * d;
const int nqp_el = q * q * q;
const int ALF_stride = ndof_el * NE;
const int ALFG_stride = 3 * nqp_el * NE;
const int ALFH_stride = 3 * 3 * nqp_el * NE;
TMOPAssembleGradAdaptLim3D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
ALF_grad, ALF_hess, d, q);
const real_t *ALF_all = PA.ALF.Read();
real_t *ALFG_all = PA.ALFG.Write();
real_t *ALFH_all = PA.ALFH.Write();
for (int c = 0; c < nal; c++)
{
const auto ALF = Reshape(ALF_all + c * ALF_stride, d, d, d, NE);
auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 3, q, q, q, NE);
auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 3, 3, q, q, q, NE);
TMOPAssembleGradAdaptLim3D::Run(d, q, NE, B_nodes, G_nodes, B, X, ALF,
ALF_grad, ALF_hess, d, q);
}
PA.AL_grads_assembled = true;
}
+29 -10
View File
@@ -182,27 +182,46 @@ void TMOP_Integrator::AddMultGradPA_AdaptLim_2D(const Vector &R,
Vector &C) const
{
const real_t ln = lim_normal;
const real_t delta_max = PA.al_delta;
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 bool const_coeff = PA.ALC.Size() == 1;
const auto ALC = const_coeff
? Reshape(PA.ALC.Read(), 1, 1, 1)
: Reshape(PA.ALC.Read(), q, q, NE);
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
const auto *B = PA.maps->B.Read();
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
const auto RR = Reshape(R.Read(), d, d, 2, NE);
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
const auto ALF_grad = Reshape(PA.ALFG.Read(), 2, q, q, NE);
const auto ALF_hess = Reshape(PA.ALFH.Read(), 2, 2, q, q, NE);
auto Y = Reshape(C.ReadWrite(), d, d, 2, NE);
TMOPMultGradAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, B,
RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
const real_t *ALD = PA.ALD.HostRead();
const int ndof_el = d * d;
const int nqp_el = q * q;
const int ALF_stride = ndof_el * NE;
const int ALFG_stride = 2 * nqp_el * NE;
const int ALFH_stride = 2 * 2 * nqp_el * NE;
const bool const_coeff = (PA.ALC.Size() == nal);
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
const real_t *ALC_all = PA.ALC.Read();
const real_t *ALFmF0_all = PA.ALFmF0.Read();
const real_t *ALFG_all = PA.ALFG.Read();
const real_t *ALFH_all = PA.ALFH.Read();
for (int c = 0; c < nal; c++)
{
const real_t delta_max = ALD[c];
const auto ALC = const_coeff
? Reshape(ALC_all + c, 1, 1, 1)
: Reshape(ALC_all + c * ALC_stride, q, q, NE);
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, NE);
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 2, q, q, NE);
const auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 2, 2, q, q, NE);
TMOPMultGradAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, B,
RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
}
}
} // namespace mfem
+40 -16
View File
@@ -169,10 +169,12 @@ void TMOP_AddMultGradPA_AdaptLim_3D(const real_t lim_normal,
// Hessian action:
// H = factor * (grad x grad + (gf - gf0) * hess)
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
const real_t coeff =
const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
const real_t factor = weight * coeff * normal_inv_delta_sq;
const real_t grad_dot_R =
grad_alf[0] * R_q[0] + grad_alf[1] * R_q[1] + grad_alf[2] * R_q[2];
const real_t grad_dot_R = grad_alf[0] * R_q[0] +
grad_alf[1] * R_q[1] +
grad_alf[2] * R_q[2];
real_t hess_R[3];
hess_R[0] =
ALF_hess(0, 0, qx, qy, qz, e) * R_q[0] +
@@ -187,9 +189,12 @@ void TMOP_AddMultGradPA_AdaptLim_3D(const real_t lim_normal,
ALF_hess(2, 1, qx, qy, qz, e) * R_q[1] +
ALF_hess(2, 2, qx, qy, qz, e) * R_q[2];
r00(0, qz, qy, qx) = factor * (grad_alf[0] * grad_dot_R + diff * hess_R[0]);
r00(1, qz, qy, qx) = factor * (grad_alf[1] * grad_dot_R + diff * hess_R[1]);
r00(2, qz, qy, qx) = factor * (grad_alf[2] * grad_dot_R + diff * hess_R[2]);
r00(0, qz, qy, qx) = factor * (grad_alf[0] * grad_dot_R +
diff * hess_R[0]);
r00(1, qz, qy, qx) = factor * (grad_alf[1] * grad_dot_R +
diff * hess_R[1]);
r00(2, qz, qy, qx) = factor * (grad_alf[2] * grad_dot_R +
diff * hess_R[2]);
}
}
}
@@ -206,27 +211,46 @@ void TMOP_Integrator::AddMultGradPA_AdaptLim_3D(const Vector &R,
Vector &C) const
{
const real_t ln = lim_normal;
const real_t delta_max = PA.al_delta;
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 bool const_coeff = PA.ALC.Size() == 1;
const auto ALC = const_coeff
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
: Reshape(PA.ALC.Read(), q, q, q, NE);
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
const auto *B = PA.maps->B.Read();
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
const auto RR = Reshape(R.Read(), d, d, d, 3, NE);
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
const auto ALF_grad = Reshape(PA.ALFG.Read(), 3, q, q, q, NE);
const auto ALF_hess = Reshape(PA.ALFH.Read(), 3, 3, q, q, q, NE);
auto Y = Reshape(C.ReadWrite(), d, d, d, 3, NE);
TMOPMultGradAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, B,
RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
const real_t *ALD = PA.ALD.HostRead();
const int ndof_el = d * d * d;
const int nqp_el = q * q * q;
const int ALF_stride = ndof_el * NE;
const int ALFG_stride = 3 * nqp_el * NE;
const int ALFH_stride = 3 * 3 * nqp_el * NE;
const bool const_coeff = (PA.ALC.Size() == nal);
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
const real_t *ALC_all = PA.ALC.Read();
const real_t *ALFmF0_all = PA.ALFmF0.Read();
const real_t *ALFG_all = PA.ALFG.Read();
const real_t *ALFH_all = PA.ALFH.Read();
for (int c = 0; c < nal; c++)
{
const real_t delta_max = ALD[c];
const auto ALC = const_coeff
? Reshape(ALC_all + c, 1, 1, 1, 1)
: Reshape(ALC_all + c * ALC_stride, q, q, q, NE);
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, d, NE);
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 3, q, q, q, NE);
const auto ALF_hess = Reshape(ALFH_all + c * ALFH_stride, 3, 3, q, q, q, NE);
TMOPMultGradAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J,
W, B, RR, ALF_grad, ALF_hess, ALFmF0, Y, d, q);
}
}
} // namespace mfem
+25 -9
View File
@@ -205,25 +205,41 @@ void TMOP_Integrator::AddMultPA_AdaptLim_2D([[maybe_unused]] const Vector &x,
Vector &y) const
{
const real_t ln = lim_normal;
const real_t delta_max = PA.al_delta;
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 bool const_coeff = PA.ALC.Size() == 1;
const auto ALC = const_coeff
? Reshape(PA.ALC.Read(), 1, 1, 1)
: Reshape(PA.ALC.Read(), q, q, NE);
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
const auto *B = PA.maps->B.Read();
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
const auto ALF_grad = Reshape(PA.ALFG.Read(), 2, q, q, NE);
auto Y = Reshape(y.ReadWrite(), d, d, 2, NE);
TMOPMultAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
B, ALF_grad, ALFmF0, Y, d, q);
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
const real_t *ALD = PA.ALD.HostRead();
const int ndof_el = d * d;
const int nqp_el = q * q;
const int ALF_stride = ndof_el * NE;
const int ALFG_stride = 2 * nqp_el * NE;
const bool const_coeff = (PA.ALC.Size() == nal);
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
const real_t *ALC_all = PA.ALC.Read();
const real_t *ALFmF0_all = PA.ALFmF0.Read();
const real_t *ALFG_all = PA.ALFG.Read();
for (int c = 0; c < nal; c++)
{
const real_t delta_max = ALD[c];
const auto ALC = const_coeff
? Reshape(ALC_all + c, 1, 1, 1)
: Reshape(ALC_all + c * ALC_stride, q, q, NE);
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, NE);
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 2, q, q, NE);
TMOPMultAdaptLim::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
B, ALF_grad, ALFmF0, Y, d, q);
}
}
} // namespace mfem
+28 -12
View File
@@ -76,7 +76,7 @@ void TMOP_AddMultPA_C0_3D(const real_t lim_normal,
r11(1, qz, qy, qx),
r11(2, qz, qy, qx)
};
const real_t coeff0 = const_c0 ? C0(0, 0, 0, 0) : C0(qx, qy, qz, e);
const real_t coeff0 = const_c0 ? C0(0,0,0,0) : C0(qx, qy, qz, e);
real_t d1[3];
// Eval_d1 (Quadratic Limiter)
@@ -193,7 +193,8 @@ void TMOP_AddMultPA_AdaptLim_3D(const real_t lim_normal,
const real_t detJtr = kernels::Det<3>(Jtr);
const real_t weight = W(qx, qy, qz) * detJtr;
const real_t coeff = const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
const real_t coeff =
const_coeff ? ALC(0, 0, 0, 0) : ALC(qx, qy, qz, e);
const real_t factor = weight * coeff * normal_inv_delta_sq *
alf_quad(qz, qy, qx);
@@ -217,26 +218,41 @@ void TMOP_Integrator::AddMultPA_AdaptLim_3D([[maybe_unused]] const Vector &x,
Vector &y) const
{
const real_t ln = lim_normal;
const real_t delta_max = PA.al_delta;
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 bool const_coeff = PA.ALC.Size() == 1;
const auto ALC = const_coeff
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
: Reshape(PA.ALC.Read(), q, q, q, NE);
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
const auto *B = PA.maps->B.Read();
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
const auto ALF_grad = Reshape(PA.ALFG.Read(), 3, q, q, q, NE);
auto Y = Reshape(y.ReadWrite(), d, d, d, 3, NE);
TMOPMultAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
B, ALF_grad, ALFmF0, Y, d, q);
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
const real_t *ALD = PA.ALD.HostRead();
const int ndof_el = d * d * d;
const int nqp_el = q * q * q;
const int ALF_stride = ndof_el * NE;
const int ALFG_stride = 3 * nqp_el * NE;
const bool const_coeff = (PA.ALC.Size() == nal);
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
const real_t *ALC_all = PA.ALC.Read();
const real_t *ALFmF0_all = PA.ALFmF0.Read();
const real_t *ALFG_all = PA.ALFG.Read();
for (int c = 0; c < nal; c++)
{
const real_t delta_max = ALD[c];
const auto ALC = const_coeff
? Reshape(ALC_all + c, 1, 1, 1, 1)
: Reshape(ALC_all + c * ALC_stride, q, q, q, NE);
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, d, NE);
const auto ALF_grad = Reshape(ALFG_all + c * ALFG_stride, 3, q, q, q, NE);
TMOPMultAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W,
B, ALF_grad, ALFmF0, Y, d, q);
}
}
} // namespace mfem
+99 -41
View File
@@ -46,14 +46,14 @@ void TMOP_Integrator::AssembleGradPA(const Vector &de,
{
AssembleGradPA_2D(xe);
if (lim_coeff) { AssembleGradPA_C0_2D(xe); }
if (adapt_lim_gf) { AssembleGradPA_AdaptLim_2D(xe); }
if (adapt_lim_gf.Size() > 0) { AssembleGradPA_AdaptLim_2D(xe); }
}
if (PA.dim == 3)
{
AssembleGradPA_3D(xe);
if (lim_coeff) { AssembleGradPA_C0_3D(xe); }
if (adapt_lim_gf) { AssembleGradPA_AdaptLim_3D(xe); }
if (adapt_lim_gf.Size() > 0) { AssembleGradPA_AdaptLim_3D(xe); }
}
}
@@ -201,12 +201,15 @@ void TMOP_Integrator::UpdateCoefficientsPA(const Vector &d_loc)
// All are constant or not specified.
if (PA.MC.Size() == 1 && PA.C0.Size() <= 1 && PA.ALC.Size() <= 1) { return; }
const int nal = PA.nal;
const bool alc_is_qvec =
(nal > 0) ? (PA.ALC.Size() == nal * PA.nq * PA.ne) : false;
if (PA.MC.Size() == 1 && PA.C0.Size() <= 1 && !alc_is_qvec) { return; }
// Coefficients are always evaluated on the CPU for now.
PA.MC.HostWrite();
PA.C0.HostWrite();
PA.ALC.HostWrite();
if (alc_is_qvec) { PA.ALC.HostWrite(); }
const IntegrationRule &ir = *PA.ir;
auto T = new IsoparametricTransformation;
@@ -231,11 +234,17 @@ void TMOP_Integrator::UpdateCoefficientsPA(const Vector &d_loc)
}
}
if (PA.ALC.Size() > 1)
if (alc_is_qvec)
{
for (int q = 0; q < PA.nq; ++q)
MFEM_VERIFY(nal == adapt_lim_coeff.Size(), "internal error");
for (int c = 0; c < nal; c++)
{
PA.ALC(q + e * PA.nq) = adapt_lim_coeff->Eval(*T, ir.IntPoint(q));
real_t *ALC_c = PA.ALC.HostWrite() + c * PA.nq * PA.ne;
for (int q = 0; q < PA.nq; ++q)
{
ALC_c[q + e * PA.nq] =
adapt_lim_coeff[c]->Eval(*T, ir.IntPoint(q));
}
}
}
}
@@ -336,37 +345,67 @@ void TMOP_Integrator::AssemblePA(const FiniteElementSpace &fes)
if (lim_coeff) { AssemblePA_Limiting(); }
// Adaptive limiting: adapt_lim_coeff -> PA.ALC, adapt_lim_gf -> PA.ALF,
// adapt_lim_gf0 -> PA.ALF0, adapt_lim_delta_max -> PA.ALD
if (adapt_lim_gf) { AssemblePA_AdaptLim(); }
if (adapt_lim_gf.Size() > 0) { AssemblePA_AdaptLim(); }
}
void TMOP_Integrator::AssemblePA_AdaptLim()
{
const FiniteElementSpace *alfes = adapt_lim_gf->FESpace();
const int nal = adapt_lim_coeff.Size();
MFEM_VERIFY(nal > 0, "internal error");
MFEM_VERIFY(adapt_lim_gf.Size() == nal && adapt_lim_gf0.Size() == nal,
"internal error");
const FiniteElementSpace *alfes = adapt_lim_gf[0]->FESpace();
MFEM_VERIFY(alfes && alfes->GetVDim() == 1, "internal error");
MFEM_VERIFY(strcmp(alfes->FEColl()->Name(), PA.fes->FEColl()->Name()) == 0 &&
alfes->FEColl()->GetOrder() == PA.fes->FEColl()->GetOrder(),
"The PA code assumes the same FE spaces for mesh and limiting.");
PA.AL_grads_assembled = false;
PA.nal = nal;
// adapt_lim_coeff -> PA.ALC (Q-vector).
PA.ALC.UseDevice(true);
if (auto *cQ = dynamic_cast<ConstantCoefficient *>(adapt_lim_coeff))
// adapt_lim_coeff -> PA.ALC
// Keep the ConstantCoefficient fast-path: when all coefficients are
// constant, store one scalar per adaptive-limiting term.
bool all_const = true;
for (int c = 0; c < nal; c++)
{
PA.ALC.SetSize(1, Device::GetMemoryType());
PA.ALC.HostWrite();
PA.ALC(0) = cQ->constant;
if (!dynamic_cast<ConstantCoefficient *>(adapt_lim_coeff[c]))
{
all_const = false;
break;
}
}
PA.ALC.UseDevice(true);
if (all_const)
{
PA.ALC.SetSize(nal, Device::GetMemoryType());
real_t *ALC_all = PA.ALC.HostWrite();
for (int c = 0; c < nal; c++)
{
auto *cc = dynamic_cast<ConstantCoefficient *>(adapt_lim_coeff[c]);
MFEM_VERIFY(cc, "internal error");
ALC_all[c] = cc->constant;
}
}
else
{
PA.ALC.SetSize(PA.nq * PA.ne, Device::GetMemoryType());
auto ALC = Reshape(PA.ALC.HostWrite(), PA.nq, PA.ne);
for (int e = 0; e < PA.ne; ++e)
// If one Coefficient is not constant, we allocate the full size for
// all Coefficients. Could be optimized in the future.
PA.ALC.SetSize(nal * PA.nq * PA.ne, Device::GetMemoryType());
real_t *ALC_all = PA.ALC.HostWrite();
for (int c = 0; c < nal; c++)
{
ElementTransformation &T = *PA.fes->GetElementTransformation(e);
for (int q = 0; q < PA.ir->GetNPoints(); ++q)
real_t *ALC_c = ALC_all + c * PA.nq * PA.ne;
for (int e = 0; e < PA.ne; ++e)
{
ALC(q, e) = adapt_lim_coeff->Eval(T, PA.ir->IntPoint(q));
ElementTransformation &T = *PA.fes->GetElementTransformation(e);
for (int q = 0; q < PA.ir->GetNPoints(); ++q)
{
ALC_c[q + e * PA.nq] =
adapt_lim_coeff[c]->Eval(T, PA.ir->IntPoint(q));
}
}
}
}
@@ -396,29 +435,46 @@ void TMOP_Integrator::AssemblePA_AdaptLim()
PA.maps_nodes = &fe_n->GetDofToQuad(lex_nodes, DofToQuad::TENSOR);
}
// adapt_lim_gf -> PA.ALF (E-vector, same pattern as LD).
const FiniteElement &fe = *alfes->GetTypicalFE();
PA.ALF.SetSize(PA.ne * fe.GetDof(), Device::GetMemoryType());
PA.ALF.UseDevice(true);
// Restrict each adaptive limiting field into separate contiguous E-vectors
// (one block per adaptive limiting term).
const Operator *alf_R = alfes->GetElementRestriction(ordering);
alf_R->Mult(*adapt_lim_gf, PA.ALF);
// adapt_lim_gf - adapt_lim_gf0 -> PA.ALFmF0
PA.ALFmF0.SetSize(PA.ne * fe.GetDof(), Device::GetMemoryType());
const int ndofs = alfes->GetVSize();
const int Esize = alf_R->Height();
PA.ALF.SetSize(nal * Esize, Device::GetMemoryType());
PA.ALF.UseDevice(true);
PA.ALFmF0.SetSize(nal * Esize, Device::GetMemoryType());
PA.ALFmF0.UseDevice(true);
alf_R->Mult(*adapt_lim_gf0, PA.ALFmF0);
Vector ALFc, ALF0c;
for (int c = 0; c < nal; c++)
{
ALFc.MakeRef(PA.ALF, c * Esize, Esize);
ALF0c.MakeRef(PA.ALFmF0, c * Esize, Esize);
MFEM_VERIFY(adapt_lim_gf[c]->Size() == ndofs, "internal error");
MFEM_VERIFY(adapt_lim_gf0[c]->Size() == ndofs, "internal error");
alf_R->Mult(*adapt_lim_gf[c], ALFc);
alf_R->Mult(*adapt_lim_gf0[c], ALF0c);
}
// Build differences in-place: ALFmF0 = ALF - ALF0.
PA.ALFmF0 *= -1.0;
PA.ALFmF0 += PA.ALF;
// adapt_lim_delta_max -> PA.al_delta.
PA.al_delta = adapt_lim_delta_max;
// Per-field delta_max values.
MFEM_VERIFY(adapt_lim_delta_max.Size() == nal, "internal error");
PA.ALD.SetSize(nal);
PA.ALD.HostWrite();
for (int c = 0; c < nal; c++) { PA.ALD(c) = adapt_lim_delta_max[c]; }
// Allocate storage for gradient and Hessian of ALF at quadrature points
// These will be filled during AssembleGradPA
const int dim = PA.dim;
PA.ALFG.UseDevice(true);
PA.ALFG.SetSize(dim * PA.nq * PA.ne, Device::GetMemoryType());
PA.ALFG.SetSize(nal * dim * PA.nq * PA.ne, Device::GetMemoryType());
PA.ALFH.UseDevice(true);
PA.ALFH.SetSize(dim * dim * PA.nq * PA.ne, Device::GetMemoryType());
PA.ALFH.SetSize(nal * dim * dim * PA.nq * PA.ne, Device::GetMemoryType());
}
@@ -439,14 +495,14 @@ void TMOP_Integrator::AssembleGradDiagonalPA(Vector &de) const
{
AssembleDiagonalPA_2D(de);
if (lim_coeff) { AssembleDiagonalPA_C0_2D(de); }
if (adapt_lim_gf) { AssembleDiagonalPA_AdaptLim_2D(de); }
if (adapt_lim_gf.Size() > 0) { AssembleDiagonalPA_AdaptLim_2D(de); }
}
if (PA.dim == 3)
{
AssembleDiagonalPA_3D(de);
if (lim_coeff) { AssembleDiagonalPA_C0_3D(de); }
if (adapt_lim_gf) { AssembleDiagonalPA_AdaptLim_3D(de); }
if (adapt_lim_gf.Size() > 0) { AssembleDiagonalPA_AdaptLim_3D(de); }
}
}
@@ -473,7 +529,7 @@ void TMOP_Integrator::AddMultPA(const Vector &de, Vector &ye) const
{
AddMultPA_2D(xe, ye);
if (lim_coeff) { AddMultPA_C0_2D(xe, ye); }
if (adapt_lim_gf)
if (adapt_lim_gf.Size() > 0)
{
// AddMultPA_AdaptLim_2D uses the precomputed AdaptLim field gradient
// at quadrature points (PA.ALFG). Ensure it is up-to-date for the
@@ -488,7 +544,7 @@ void TMOP_Integrator::AddMultPA(const Vector &de, Vector &ye) const
{
AddMultPA_3D(xe, ye);
if (lim_coeff) { AddMultPA_C0_3D(xe, ye); }
if (adapt_lim_gf)
if (adapt_lim_gf.Size() > 0)
{
AssembleGradPA_AdaptLim_3D(xe);
AddMultPA_AdaptLim_3D(xe, ye);
@@ -513,14 +569,14 @@ void TMOP_Integrator::AddMultGradPA(const Vector &re, Vector &ce) const
{
AddMultGradPA_2D(re, ce);
if (lim_coeff) { AddMultGradPA_C0_2D(re, ce); }
if (adapt_lim_gf) { AddMultGradPA_AdaptLim_2D(re, ce); }
if (adapt_lim_gf.Size() > 0) { AddMultGradPA_AdaptLim_2D(re, ce); }
}
if (PA.dim == 3)
{
AddMultGradPA_3D(re, ce);
if (lim_coeff) { AddMultGradPA_C0_3D(re, ce); }
if (adapt_lim_gf) { AddMultGradPA_AdaptLim_3D(re, ce); }
if (adapt_lim_gf.Size() > 0) { AddMultGradPA_AdaptLim_3D(re, ce); }
}
}
@@ -549,14 +605,16 @@ real_t TMOP_Integrator::GetLocalStateEnergyPA(const Vector &de) const
{
GetLocalStateEnergyPA_2D(xe, energy);
if (lim_coeff) { energy += GetLocalStateEnergyPA_C0_2D(xe); }
if (adapt_lim_gf) { energy += GetLocalStateEnergyPA_AdaptLim_2D(); }
if (adapt_lim_gf.Size() > 0)
{ energy += GetLocalStateEnergyPA_AdaptLim_2D(); }
}
if (PA.dim == 3)
{
GetLocalStateEnergyPA_3D(xe, energy);
if (lim_coeff) { energy += GetLocalStateEnergyPA_C0_3D(xe); }
if (adapt_lim_gf) { energy += GetLocalStateEnergyPA_AdaptLim_3D(); }
if (adapt_lim_gf.Size() > 0)
{ energy += GetLocalStateEnergyPA_AdaptLim_3D(); }
}
return energy;
+26 -9
View File
@@ -182,26 +182,43 @@ MFEM_TMOP_MDQ_SPECIALIZE(TMOPEnergyAdaptLim2D);
real_t TMOP_Integrator::GetLocalStateEnergyPA_AdaptLim_2D() const
{
const real_t ln = lim_normal;
const real_t delta_max = PA.al_delta;
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 bool const_coeff = PA.ALC.Size() == 1;
const auto ALC = const_coeff
? Reshape(PA.ALC.Read(), 1, 1, 1)
: Reshape(PA.ALC.Read(), q, q, NE);
const auto J = Reshape(PA.Jtr.Read(), 2, 2, q, q, NE);
const auto *b = PA.maps->B.Read();
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q);
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, NE);
auto E = Reshape(PA.E.Write(), q, q, NE);
TMOPEnergyAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
ALFmF0, E, d, q);
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
MFEM_VERIFY(PA.ALD.Size() == nal, "internal error");
PA.ALD.HostRead();
return PA.E * PA.O;
const int ndof_el = d * d;
const int nqp_el = q * q;
const int ALF_stride = ndof_el * NE;
const bool const_coeff = (PA.ALC.Size() == nal);
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
const real_t *ALC_all = PA.ALC.Read();
const real_t *ALFmF0_all = PA.ALFmF0.Read();
real_t energy = 0.0;
for (int c = 0; c < nal; c++)
{
const real_t delta_max = PA.ALD(c);
const auto ALC = const_coeff
? Reshape(ALC_all + c, 1, 1, 1)
: Reshape(ALC_all + c * ALC_stride, q, q, NE);
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, NE);
TMOPEnergyAdaptLim2D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
ALFmF0, E, d, q);
energy += PA.E * PA.O;
}
return energy;
}
} // namespace mfem
+26 -9
View File
@@ -198,26 +198,43 @@ MFEM_TMOP_MDQ_SPECIALIZE(TMOPEnergyAdaptLim3D);
real_t TMOP_Integrator::GetLocalStateEnergyPA_AdaptLim_3D() const
{
const real_t ln = lim_normal;
const real_t delta_max = PA.al_delta;
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 bool const_coeff = PA.ALC.Size() == 1;
const auto ALC = const_coeff
? Reshape(PA.ALC.Read(), 1, 1, 1, 1)
: Reshape(PA.ALC.Read(), q, q, q, NE);
const auto J = Reshape(PA.Jtr.Read(), 3, 3, q, q, q, NE);
const auto *b = PA.maps->B.Read();
const auto W = Reshape(PA.ir->GetWeights().Read(), q, q, q);
const auto ALFmF0 = Reshape(PA.ALFmF0.Read(), d, d, d, NE);
auto E = Reshape(PA.E.Write(), q, q, q, NE);
TMOPEnergyAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
ALFmF0, E, d, q);
const int nal = PA.nal;
MFEM_VERIFY(nal > 0, "internal error");
MFEM_VERIFY(PA.ALD.Size() == nal, "internal error");
PA.ALD.HostRead();
return PA.E * PA.O;
const int ndof_el = d * d * d;
const int nqp_el = q * q * q;
const int ALF_stride = ndof_el * NE;
const bool const_coeff = (PA.ALC.Size() == nal);
const int ALC_stride = const_coeff ? 1 : (nqp_el * NE);
const real_t *ALC_all = PA.ALC.Read();
const real_t *ALFmF0_all = PA.ALFmF0.Read();
real_t energy = 0.0;
for (int c = 0; c < nal; c++)
{
const real_t delta_max = PA.ALD(c);
const auto ALC = const_coeff
? Reshape(ALC_all + c, 1, 1, 1, 1)
: Reshape(ALC_all + c * ALC_stride, q, q, q, NE);
const auto ALFmF0 = Reshape(ALFmF0_all + c * ALF_stride, d, d, d, NE);
TMOPEnergyAdaptLim3D::Run(d, q, ln, delta_max, const_coeff, ALC, NE, J, W, b,
ALFmF0, E, d, q);
energy += PA.E * PA.O;
}
return energy;
}
} // namespace mfem
+2
View File
@@ -759,6 +759,7 @@ void TMOPHRSolver::Update()
gridfuncarr[i]->SetTrueVector();
gridfuncarr[i]->SetFromTrueVector();
}
tmopns->UpdateDeterminantBoundGridFunction();
// Update Discrete Indicator for all the TMOP_Integrators in NonLinearForm
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
@@ -806,6 +807,7 @@ void TMOPHRSolver::ParUpdate()
pgridfuncarr[i]->SetTrueVector();
pgridfuncarr[i]->SetFromTrueVector();
}
tmopns->UpdateDeterminantBoundGridFunction();
// Update Discrete Indicator
Array<NonlinearFormIntegrator*> &integs = *(nlf->GetDNFI());
+105 -7
View File
@@ -68,6 +68,9 @@ void AdvectorCG::ComputeAtNewPosition(const Vector &new_mesh_nodes,
}
}
// Without this, the next remap would start from the initial mesh, i.e.,
// every consecutive remap would be more expensive, as it would have to
// transport the solution through bigger displacements.
field0 = new_field;
nodes0 = new_mesh_nodes;
}
@@ -305,8 +308,14 @@ void ParAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
M.BilinearForm::operator=(0.0);
M.Assemble();
HypreParVector *RHS = rhs.ParallelAssemble();
HypreParVector X(K.ParFESpace());
Vector RHS;
RHS.SetSize(M.ParFESpace()->GetTrueVSize(), ind);
RHS.UseDevice(ind.UseDevice());
rhs.ParallelAssemble(RHS);
Vector X;
X.SetSize(M.ParFESpace()->GetTrueVSize(), ind);
X.UseDevice(ind.UseDevice());
X = 0.0;
OperatorHandle Mop;
@@ -335,10 +344,8 @@ void ParAdvectorCGOper::Mult(const Vector &ind, Vector &di_dt) const
lin_solver.SetRelTol(rtol); lin_solver.SetAbsTol(0.0);
lin_solver.SetMaxIter(100);
lin_solver.SetPrintLevel(0);
lin_solver.Mult(*RHS, X);
lin_solver.Mult(RHS, X);
K.ParFESpace()->GetProlongationMatrix()->Mult(X, di_dt);
delete RHS;
delete prec;
}
#endif
@@ -493,7 +500,10 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &d_in,
// Check if the starting mesh (given by x) is inverted. Note that x hasn't
// been modified by the Newton update yet.
const real_t min_detT_in = ComputeMinDet(d_loc, *fes);
const real_t min_detT_in =
detJpr_pos_bound ? ComputeDetJptLowerBound(d_loc, *fes)
/* */ : ComputeMinDet(d_loc, *fes);
const bool untangling = (min_detT_in <= 0.0) ? true : false;
const real_t untangle_factor = 1.5;
if (untangling)
@@ -544,7 +554,10 @@ real_t TMOPNewtonSolver::ComputeScalingFactor(const Vector &d_in,
#endif
// Check the changes in detJ.
min_detT_out = ComputeMinDet(d_loc, *fes);
min_detT_out =
detJpr_pos_bound ? ComputeDetJptLowerBound(d_loc, *fes)
/* */ : ComputeMinDet(d_loc, *fes);
if (untangling == false && min_detT_out <= min_detJ_limit)
{
// No untangling, and detJ got negative (or small) -- no good.
@@ -969,6 +982,37 @@ void TMOPNewtonSolver::ProcessNewState(const Vector &dx) const
}
}
void TMOPNewtonSolver::EnsurePositiveDeterminantBound(
Mesh &mesh, int ref_factor, int max_recursion_depth)
{
#ifdef MFEM_USE_MPI
if (ParMesh *pmesh = dynamic_cast<ParMesh *>(&mesh))
{
det_gf = pmesh->GetJacobianDeterminantGF();
}
else
#endif
{
det_gf = mesh.GetJacobianDeterminantGF();
}
// setup the PLBound object for estimating the minima.
// note: this must be updated if the mesh is p-refined.
int max_order = det_gf->FESpace()->GetMaxElementOrder();
det_plb = std::make_unique<PLBound>(det_gf->FESpace(),
ref_factor*(max_order+1));
plb_rec_depth = max_recursion_depth;
detJpr_pos_bound = true;
}
void TMOPNewtonSolver::UpdateDeterminantBoundGridFunction()
{
if (!det_gf) { return; }
det_gf->FESpace()->Update();
det_gf->Update();
}
real_t TMOPNewtonSolver::ComputeMinDet(const Vector &d_loc,
const FiniteElementSpace &fes) const
{
@@ -1028,6 +1072,60 @@ real_t TMOPNewtonSolver::ComputeMinDet(const Vector &d_loc,
return min_detJ;
}
real_t TMOPNewtonSolver::ComputeDetJptLowerBound(const Vector &d_loc,
const FiniteElementSpace &fes) const
{
MFEM_VERIFY(det_gf != nullptr && det_plb != nullptr,
"Determinant bounding has not been setup.");
FiniteElementSpace *det_fes = det_gf->FESpace();
MFEM_VERIFY(!det_fes->IsVariableOrder() && UsesTensorBasis(*det_fes),
"Determinant lower bounds require a fixed-order tensor-product "
"determinant space.");
Array<int> dofs, xdofs;
DenseMatrix dshape, Jpr, pos;
Vector d_loc_el, detvals;
for (int e = 0; e < fes.GetNE(); e++)
{
const FiniteElement *fe = fes.GetFE(e);
const int dof = fe->GetDof(), dim = fe->GetDim();
dshape.SetSize(dof, dim);
Jpr.SetSize(dim);
pos.SetSize(dof, dim);
Vector posV(pos.Data(), dof * dim);
x_0.GetElementDofValues(e, posV);
if (periodic)
{
auto n_el = dynamic_cast<const NodalFiniteElement *>(fe);
n_el->ReorderLexToNative(dim, posV);
}
fes.GetElementVDofs(e, xdofs);
d_loc.GetSubVector(xdofs, d_loc_el);
posV += d_loc_el;
const IntegrationRule &irule = det_fes->GetFE(e)->GetNodes();
const int nsp = irule.GetNPoints();
detvals.SetSize(nsp);
det_fes->GetElementDofs(e, dofs);
for (int q = 0; q < nsp; q++)
{
fe->CalcDShape(irule.IntPoint(q), dshape);
MultAtB(pos, dshape, Jpr);
detvals(q) = Jpr.Det();
}
det_gf->SetSubVector(dofs, detvals);
}
auto minbounds = det_gf->EstimateFunctionMinimum(0, *det_plb,
plb_rec_depth, 1e-5);
const DenseMatrix &Wideal =
Geometries.GetGeomToPerfGeomJac(fes.GetMesh()->GetTypicalElementGeometry());
return minbounds.first/Wideal.Det();
}
#ifdef MFEM_USE_MPI
// Metric values are visualized by creating an L2 finite element functions and
// computing the metric values at the nodes.
+32
View File
@@ -204,6 +204,11 @@ protected:
// These fields are relevant for mixed meshes.
IntegrationRules *IntegRules;
int integ_order;
// Determinant lower-bound data used by the line search.
bool detJpr_pos_bound = false;
std::unique_ptr<GridFunction> det_gf;
std::unique_ptr<PLBound> det_plb;
int plb_rec_depth = 0;
MemoryType temp_mt = MemoryType::DEFAULT;
@@ -216,9 +221,16 @@ protected:
return ir;
}
/// Compute the minimum det(Jpt) of the trial mesh at quadrature points
/// (computes det(Jpr) and scales by the det of ideal target element).
real_t ComputeMinDet(const Vector &d_loc,
const FiniteElementSpace &fes) const;
/// Compute a lower bound for det(Jpt) of the trial mesh,
/// (computes det(Jpr) and scales by the det of ideal target element).
real_t ComputeDetJptLowerBound(const Vector &d_loc,
const FiniteElementSpace &fes) const;
real_t MinDetJpr_2D(const FiniteElementSpace *, const Vector &) const;
real_t MinDetJpr_3D(const FiniteElementSpace *, const Vector &) const;
@@ -261,6 +273,26 @@ public:
void SetMinDetPtr(real_t *md_ptr) { min_det_ptr = md_ptr; }
/** @brief Ensure a positive lower bound for the Jacobian determinant in
tensor-product elements during line-search.
@note The solver creates and updates its own determinant GridFunction
from @a mesh while testing trial mesh positions. When @a mesh is a
ParMesh, the internal determinant field is a ParGridFunction. The
@a ref_factor controls the number of control points used by the PLBound
object, and @a max_recursion_depth controls the depth used by the
minimum-value estimator.
The determinant is represented by a high-order GridFunction computed
at the mesh nodes. The order is chosen s.t. interpolating the det at
some quad point would be equivalent to computing the det directly at the
same quad point using the mesh positions.
*/
void EnsurePositiveDeterminantBound(Mesh &mesh, int ref_factor,
int max_recursion_depth = 0);
/// Update internal determinant GridFunction after a mesh topology change.
void UpdateDeterminantBoundGridFunction();
/// Set the memory type for temporary memory allocations.
void SetTempMemoryType(MemoryType mt) { temp_mt = mt; }
+282 -16
View File
@@ -2057,6 +2057,10 @@ TransferOperator::TransferOperator(const FiniteElementSpace& lFESpace_,
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize())
{
bool isvar_order = lFESpace_.IsVariableOrder() || hFESpace_.IsVariableOrder();
bool is_trace_space =
(dynamic_cast<const H1_Trace_FECollection*>(lFESpace_.FEColl()) ||
dynamic_cast<const ND_Trace_FECollection*>(lFESpace_.FEColl()) ||
dynamic_cast<const RT_Trace_FECollection*>(lFESpace_.FEColl()));
if (lFESpace_.FEColl() == hFESpace_.FEColl() && !isvar_order)
{
OperatorPtr P(Operator::ANY_TYPE);
@@ -2066,6 +2070,7 @@ TransferOperator::TransferOperator(const FiniteElementSpace& lFESpace_,
}
else if (lFESpace_.GetVDim() == 1
&& hFESpace_.GetVDim() == 1
&& !is_trace_space
&& dynamic_cast<const TensorBasisElement*>(lFESpace_.GetTypicalFE())
&& dynamic_cast<const TensorBasisElement*>(hFESpace_.GetTypicalFE())
&& !isvar_order
@@ -2096,15 +2101,245 @@ void TransferOperator::MultTranspose(const Vector& x, Vector& y) const
PRefinementTransferOperator::PRefinementTransferOperator(
const FiniteElementSpace& lFESpace_, const FiniteElementSpace& hFESpace_)
const FiniteElementSpace& lFESpace_, const FiniteElementSpace& hFESpace_,
bool assemble_matrix)
: Operator(hFESpace_.GetVSize(), lFESpace_.GetVSize()), lFESpace(lFESpace_),
hFESpace(hFESpace_)
{
isvar_order = lFESpace_.IsVariableOrder() || hFESpace_.IsVariableOrder();
MFEM_VERIFY(lFESpace.FEColl()->GetContType() ==
hFESpace.FEColl()->GetContType(),
"Incompatible finite element space continuity types.");
is_trace_space =
(dynamic_cast<const H1_Trace_FECollection*>(lFESpace.FEColl()) ||
dynamic_cast<const ND_Trace_FECollection*>(lFESpace.FEColl()) ||
dynamic_cast<const RT_Trace_FECollection*>(lFESpace.FEColl()));
if (assemble_matrix) { AssembleMatrix(); }
}
void PRefinementTransferOperator::AssembleMatrix()
{
Mesh* mesh = hFESpace.GetMesh();
const int nL = lFESpace.GetVSize();
const int nH = hFESpace.GetVSize();
P.reset(new SparseMatrix(nH, nL));
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
Geometry::Type cached_geom = Geometry::INVALID;
const FiniteElement* h_fe = nullptr;
const FiniteElement* l_fe = nullptr;
IsoparametricTransformation T;
int vdim = lFESpace.GetVDim();
const int iend = (is_trace_space) ? mesh->GetNumFaces() : mesh->GetNE();
DofTransformation doftrans_h, doftrans_l;
Vector w(nH); w = 0.0;
for (int i = 0; i < iend; i++)
{
if (is_trace_space)
{
hFESpace.GetFaceDofs(i, h_dofs);
lFESpace.GetFaceDofs(i, l_dofs);
}
else
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
}
const Geometry::Type geom = (is_trace_space) ? mesh->GetFaceGeometry(i)
: mesh->GetElementBaseGeometry(i);
if (geom != cached_geom || isvar_order)
{
h_fe = (is_trace_space) ? hFESpace.GetFaceElement(i) : hFESpace.GetFE(i);
l_fe = (is_trace_space) ? lFESpace.GetFaceElement(i) : lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
cached_geom = geom;
}
DenseMatrix Aeff(loc_prol);
TransformPrimal(doftrans_h, doftrans_l, Aeff);
for (int vd = 0; vd < vdim; vd++)
{
DenseMatrix temp_Aeff(Aeff);
l_dofs.Copy(l_vdofs);
lFESpace.DofsToVDofs(vd, l_vdofs);
h_dofs.Copy(h_vdofs);
hFESpace.DofsToVDofs(vd, h_vdofs);
temp_Aeff.AdjustDofDirection(h_vdofs, l_vdofs);
P->AddSubMatrix(h_vdofs, l_vdofs, temp_Aeff);
for (int rr = 0; rr < h_vdofs.Size(); rr++)
{
w(h_vdofs[rr]) += 1.0;
}
}
}
P->Finalize();
Vector inv_w(nH);
for (int i = 0; i < nH; i++)
{
inv_w(i) = (w(i) > 0.0) ? (1.0 / w(i)) : 1.0;
}
P->ScaleRows(inv_w);
assembled = true;
}
std::unique_ptr<SparseMatrix>
PRefinementTransferOperator::BuildConformingTransferMatrix() const
{
MFEM_VERIFY(assembled && P, "Matrix path requires assembled P.");
const SparseMatrix *Pl = lFESpace.GetConformingProlongation();
const SparseMatrix *Rh = hFESpace.GetRestrictionMatrix();
if (Pl && Rh)
{
SparseMatrix *RhP = mfem::Mult(*Rh, *P);
SparseMatrix *RhPPl = mfem::Mult(*RhP, *Pl);
delete RhP;
return std::unique_ptr<SparseMatrix>(RhPPl);
}
else if (Pl)
{
return std::unique_ptr<SparseMatrix>(mfem::Mult(*P, *Pl));
}
else if (Rh)
{
return std::unique_ptr<SparseMatrix>(mfem::Mult(*Rh, *P));
}
else
{
return std::make_unique<SparseMatrix>(*P);
}
}
std::unique_ptr<Operator>
PRefinementTransferOperator::BuildConformingTransferOperator() const
{
const Operator *Pl = lFESpace.GetProlongationMatrix();
const Operator *Rh = hFESpace.GetRestrictionOperator();
if (Pl && Rh)
{
return std::make_unique<TripleProductOperator>(Rh,
const_cast<PRefinementTransferOperator*>(this), Pl,
false, false, false);
}
else if (Pl)
{
return std::make_unique<ProductOperator>
(const_cast<PRefinementTransferOperator*>(this), Pl,
false, false);
}
else if (Rh)
{
return std::make_unique<ProductOperator>(Rh,
const_cast<PRefinementTransferOperator*>(this),
false, false);
}
else
{
// return nullptr to mean "identity/no-op wrapper", i.e. use `this`
return nullptr;
}
}
Operator *
PRefinementTransferOperator::GetTrueTransferOperator()
{
if (tP) { return tP.get(); }
#ifdef MFEM_USE_MPI
const ParFiniteElementSpace* lpfes = dynamic_cast<const ParFiniteElementSpace*>
(&lFESpace);
const ParFiniteElementSpace* hpfes = dynamic_cast<const ParFiniteElementSpace*>
(&hFESpace);
bool parallel = (lpfes) && (hpfes);
if (parallel)
{
if (assembled)
{
HypreParMatrix * Pl = lpfes->Dof_TrueDof_Matrix();
const SparseMatrix * Rh = hpfes->GetRestrictionMatrix();
// Rh * P
SparseMatrix * RhP = mfem::Mult(*Rh, *P);
HypreParMatrix * RhPh = new HypreParMatrix(hpfes->GetComm(),
hpfes->GlobalTrueVSize(), lpfes->GlobalVSize(),
hpfes->GetTrueDofOffsets(), lpfes->GetDofOffsets(), RhP);
HypreStealOwnership(*RhPh, *RhP);
delete RhP;
HypreParMatrix * tmp = ParMult(RhPh, Pl, true);
delete RhPh;
tP.reset(tmp);
return tP.get();
}
else
{
auto Pl = lpfes->GetProlongationMatrix();
auto Rh = hpfes->GetRestrictionOperator();
tP = std::make_unique<TripleProductOperator>(Rh, this, Pl, false, false, false);
return tP.get();
}
}
else
{
if (assembled)
{
auto M = BuildConformingTransferMatrix();
tP.reset(M.release());
return tP.get();
}
else
{
tP = BuildConformingTransferOperator();
return tP ? tP.get() : this;
}
}
#else
{
if (assembled)
{
auto M = BuildConformingTransferMatrix();
tP.reset(M.release());
return tP.get();
}
else
{
tP = BuildConformingTransferOperator();
return tP ? tP.get() : this;
}
}
#endif
}
void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
{
y = 0.0;
if (assembled) { P->Mult(x, y); return; }
Mesh* mesh = hFESpace.GetMesh();
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
@@ -2117,19 +2352,31 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
int vdim = lFESpace.GetVDim();
y = 0.0;
DofTransformation doftrans_h, doftrans_l;
for (int i = 0; i < mesh->GetNE(); i++)
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
const int iend = (is_trace_space) ? mesh->GetNumFaces() : mesh->GetNE();
for (int i = 0; i < iend; i++)
{
if (is_trace_space)
{
hFESpace.GetFaceDofs(i, h_dofs);
lFESpace.GetFaceDofs(i, l_dofs);
}
else
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
}
const Geometry::Type geom = (is_trace_space) ? mesh->GetFaceGeometry(i)
: mesh->GetElementBaseGeometry(i);
if (geom != cached_geom || isvar_order)
{
h_fe = hFESpace.GetFE(i);
l_fe = lFESpace.GetFE(i);
h_fe = (is_trace_space) ? hFESpace.GetFaceElement(i) : hFESpace.GetFE(i);
l_fe = (is_trace_space) ? lFESpace.GetFaceElement(i) : lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
subY.SetSize(loc_prol.Height());
@@ -2144,6 +2391,7 @@ void PRefinementTransferOperator::Mult(const Vector& x, Vector& y) const
hFESpace.DofsToVDofs(vd, h_vdofs);
x.GetSubVector(l_vdofs, subX);
doftrans_l.InvTransformPrimal(subX);
loc_prol.Mult(subX, subY);
doftrans_h.TransformPrimal(subY);
y.SetSubVector(h_vdofs, subY);
@@ -2156,6 +2404,12 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
{
y = 0.0;
if (assembled)
{
P->MultTranspose(x, y);
return;
}
Mesh* mesh = hFESpace.GetMesh();
Array<int> l_dofs, h_dofs, l_vdofs, h_vdofs;
DenseMatrix loc_prol;
@@ -2173,16 +2427,28 @@ void PRefinementTransferOperator::MultTranspose(const Vector& x,
DofTransformation doftrans_h, doftrans_l;
for (int i = 0; i < mesh->GetNE(); i++)
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
int iend = (is_trace_space) ? mesh->GetNumFaces() : mesh->GetNE();
for (int i = 0; i < iend; i++)
{
if (is_trace_space)
{
hFESpace.GetFaceDofs(i, h_dofs);
lFESpace.GetFaceDofs(i, l_dofs);
}
else
{
hFESpace.GetElementDofs(i, h_dofs, doftrans_h);
lFESpace.GetElementDofs(i, l_dofs, doftrans_l);
}
const Geometry::Type geom = (is_trace_space) ? mesh->GetFaceGeometry(i)
: mesh->GetElementBaseGeometry(i);
const Geometry::Type geom = mesh->GetElementBaseGeometry(i);
if (geom != cached_geom || isvar_order)
{
h_fe = hFESpace.GetFE(i);
l_fe = lFESpace.GetFE(i);
h_fe = (is_trace_space) ? hFESpace.GetFaceElement(i) : hFESpace.GetFE(i);
l_fe = (is_trace_space) ? lFESpace.GetFaceElement(i) : lFESpace.GetFE(i);
T.SetIdentityTransformation(h_fe->GetGeomType());
h_fe->GetTransferMatrix(*l_fe, T, loc_prol);
loc_prol.Transpose();
+27 -4
View File
@@ -569,15 +569,38 @@ private:
const FiniteElementSpace& lFESpace;
const FiniteElementSpace& hFESpace;
bool isvar_order;
bool is_trace_space;
bool assembled = false;
std::unique_ptr<SparseMatrix> P;
std::unique_ptr<Operator> tP;
std::unique_ptr<SparseMatrix> BuildConformingTransferMatrix() const;
std::unique_ptr<Operator> BuildConformingTransferOperator() const;
void AssembleMatrix();
public:
/// @brief Constructs a transfer operator from \p lFESpace to \p hFESpace
/// which have different FE collections.
/** No matrices are assembled, only the action to a vector is being computed.
The underlying finite elements need to implement the GetTransferMatrix
methods. */
/** By default no matrices are assembled, only the action to a vector is
being computed. The underlying finite elements need to implement
the GetTransferMatrix methods. */
PRefinementTransferOperator(const FiniteElementSpace& lFESpace_,
const FiniteElementSpace& hFESpace_);
const FiniteElementSpace& hFESpace_,
bool assemble_matrix = false);
/** @brief Return the true-dof transfer operator.
The returned pointer is non-owning; the operator is either this object
or a cached operator owned by this PRefinementTransferOperator. The
pointer remains valid until this PRefinementTransferOperator is
destroyed and must not be deleted by the caller. */
Operator * GetTrueTransferOperator();
const Operator * GetTrueTransferOperator() const
{
return const_cast<PRefinementTransferOperator*>(this)
->GetTrueTransferOperator();
}
/// Destructor
virtual ~PRefinementTransferOperator() { }
-9
View File
@@ -38,15 +38,6 @@
#define CUB_IGNORE_DEPRECATED_CPP_DIALECT
#define THRUST_IGNORE_DEPRECATED_CPP_DIALECT
// MFEM only supports using RAJA/CAMP backends in default stream mode because
// memory calls are performed outside of the RAJA ecosystem
#ifndef CAMP_USE_PLATFORM_DEFAULT_STREAM
#define CAMP_USE_PLATFORM_DEFAULT_STREAM 1
#else
#if !CAMP_USE_PLATFORM_DEFAULT_STREAM
#error "MFEM only supports RAJA/CAMP with the default platform stream."
#endif
#endif
#include "RAJA/RAJA.hpp"
#if defined(RAJA_ENABLE_CUDA) && !defined(MFEM_USE_CUDA)
#error When RAJA is built with CUDA, MFEM_USE_CUDA=YES is required
+3 -1
View File
@@ -581,7 +581,9 @@ void Device::Setup(const std::string &device_option, const int device_id)
if (Allows(Backend::CUDA)) { CudaDeviceSetup(dev, ngpu); }
if (Allows(Backend::HIP)) { HipDeviceSetup(dev, ngpu); }
if (Allows(Backend::RAJA_CUDA) || Allows(Backend::RAJA_HIP))
{ RajaDeviceSetup(dev, ngpu); }
{
RajaDeviceSetup(dev, ngpu);
}
// The check for MFEM_USE_OCCA is in the function OccaDeviceSetup().
if (Allows(Backend::OCCA_MASK)) { OccaDeviceSetup(dev); }
if (Allows(Backend::CEED_MASK))
+17
View File
@@ -16,6 +16,11 @@
#include "globals.hpp"
#include "mem_manager.hpp"
#ifdef MFEM_USE_RAJA
#include "RAJA/RAJA.hpp"
#endif
#include <memory>
#include <string>
namespace mfem
@@ -266,6 +271,18 @@ public:
static inline bool Allows(unsigned long b_mask)
{ return Get().backends & b_mask; }
#if defined(MFEM_USE_RAJA) && \
(defined(RAJA_ENABLE_CUDA) || defined(RAJA_ENABLE_HIP))
static inline auto GetRajaResource()
{
#if defined(RAJA_ENABLE_CUDA)
return RAJA::resources::Cuda::CudaFromStream(0, Get().GetId());
#elif defined(RAJA_ENABLE_HIP)
return RAJA::resources::Hip::HipFromStream(0, Get().GetId());
#endif
}
#endif
/** @brief Get the current Host MemoryType. This is the MemoryType used by
most MFEM classes when allocating memory used on the host.
*/
+42 -38
View File
@@ -46,6 +46,8 @@ struct DofQuadLimits_CUDA
{
static constexpr int MAX_D1D = 14;
static constexpr int MAX_Q1D = 14;
static constexpr int MAX_D1D_SIMPLEX = 14;
static constexpr int MAX_Q1D_SIMPLEX = 14;
static constexpr int MAX_T1D = 32;
static constexpr int HCURL_MAX_D1D = 5;
static constexpr int HCURL_MAX_Q1D = 6;
@@ -59,6 +61,8 @@ struct DofQuadLimits_HIP
{
static constexpr int MAX_D1D = 10;
static constexpr int MAX_Q1D = 10;
static constexpr int MAX_D1D_SIMPLEX = 9;
static constexpr int MAX_Q1D_SIMPLEX = 9;
static constexpr int MAX_T1D = 32;
static constexpr int HCURL_MAX_D1D = 5;
static constexpr int HCURL_MAX_Q1D = 5;
@@ -73,9 +77,13 @@ struct DofQuadLimits_CPU
#ifndef _WIN32
static constexpr int MAX_D1D = 24;
static constexpr int MAX_Q1D = 24;
static constexpr int MAX_D1D_SIMPLEX = 24;
static constexpr int MAX_Q1D_SIMPLEX = 24;
#else
static constexpr int MAX_D1D = 14;
static constexpr int MAX_Q1D = 14;
static constexpr int MAX_D1D_SIMPLEX = 14;
static constexpr int MAX_Q1D_SIMPLEX = 14;
#endif
static constexpr int MAX_T1D = 32;
static constexpr int HCURL_MAX_D1D = 10;
@@ -117,6 +125,8 @@ struct DeviceDofQuadLimits
{
int MAX_D1D; ///< Maximum number of 1D nodal points.
int MAX_Q1D; ///< Maximum number of 1D quadrature points.
int MAX_D1D_SIMPLEX; ///< Maximum number of 1D nodal points for simplices.
int MAX_Q1D_SIMPLEX; ///< Maximum number of 1D quadrature points for simplices.
int HCURL_MAX_D1D; ///< Maximum number of 1D nodal points for H(curl).
int HCURL_MAX_Q1D; ///< Maximum number of 1D quadrature points for H(curl).
int HDIV_MAX_D1D; ///< Maximum number of 1D nodal points for H(div).
@@ -148,6 +158,8 @@ private:
{
MAX_D1D = T::MAX_D1D;
MAX_Q1D = T::MAX_Q1D;
MAX_D1D_SIMPLEX = T::MAX_D1D_SIMPLEX;
MAX_Q1D_SIMPLEX = T::MAX_Q1D_SIMPLEX;
HCURL_MAX_D1D = T::HCURL_MAX_D1D;
HCURL_MAX_Q1D = T::HCURL_MAX_Q1D;
HDIV_MAX_D1D = T::HDIV_MAX_D1D;
@@ -305,8 +317,8 @@ template <typename DBODY>
void RajaCuWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
RAJA::forall<RAJA::cuda_exec<MFEM_CUDA_BLOCKS,true>>(RAJA::RangeSegment(0,N),
d_body);
RAJA::forall<RAJA::cuda_exec<MFEM_CUDA_BLOCKS, true> >(
Device::GetRajaResource(), RAJA::RangeSegment(0, N), d_body);
}
template <typename DBODY>
@@ -319,9 +331,9 @@ void RajaCuWrap2D(const int N, DBODY &&d_body,
using namespace RAJA;
using RAJA::RangeSegment;
launch<cuda_launch_policy>
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE (LaunchContext ctx)
launch<cuda_launch_policy>(Device::GetRajaResource(),
LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<cuda_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
@@ -337,7 +349,6 @@ void RajaCuWrap2D(const int N, DBODY &&d_body,
});
});
});
MFEM_GPU_CHECK(cudaGetLastError());
@@ -353,9 +364,9 @@ void RajaCuWrap2DLaunchBounds(const int N, DBODY &&d_body, const int X,
using namespace RAJA;
using RAJA::RangeSegment;
launch<cuda_launch_bounds_policy<LB> >
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE(LaunchContext ctx)
launch<cuda_launch_bounds_policy<LB> >(
Device::GetRajaResource(), LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<cuda_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
{
@@ -378,13 +389,12 @@ void RajaCuWrap3D(const int N, DBODY &&d_body,
using namespace RAJA;
using RAJA::RangeSegment;
launch<cuda_launch_policy>
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE (LaunchContext ctx)
launch<cuda_launch_policy>(Device::GetRajaResource(),
LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body);
});
MFEM_GPU_CHECK(cudaGetLastError());
@@ -398,12 +408,10 @@ void RajaCuWrap3DLaunchBounds(const int N, DBODY &&d_body,
using namespace RAJA;
using RAJA::RangeSegment;
launch<cuda_launch_bounds_policy<LB> >
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body);
});
launch<cuda_launch_bounds_policy<LB> >(
Device::GetRajaResource(), LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE(LaunchContext ctx)
{ loop<cuda_teams_x>(ctx, RangeSegment(0, N), d_body); });
MFEM_GPU_CHECK(cudaGetLastError());
}
@@ -472,8 +480,8 @@ template <typename DBODY>
void RajaHipWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
RAJA::forall<RAJA::hip_exec<MFEM_HIP_BLOCKS,true>>(RAJA::RangeSegment(0,N),
d_body);
RAJA::forall<RAJA::hip_exec<MFEM_HIP_BLOCKS,true> >(RAJA::RangeSegment(0,N),
d_body);
}
template <typename DBODY>
@@ -486,9 +494,9 @@ void RajaHipWrap2D(const int N, DBODY &&d_body,
using namespace RAJA;
using RAJA::RangeSegment;
launch<hip_launch_policy>
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE (LaunchContext ctx)
launch<hip_launch_policy>(Device::GetRajaResource(),
LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
@@ -504,7 +512,6 @@ void RajaHipWrap2D(const int N, DBODY &&d_body,
});
});
});
MFEM_GPU_CHECK(hipGetLastError());
@@ -520,9 +527,9 @@ void RajaHipWrap2DLaunchBounds(const int N, DBODY &&d_body, const int X,
using namespace RAJA;
using RAJA::RangeSegment;
launch<hip_launch_bounds_policy<LB> >
(LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE(LaunchContext ctx)
launch<hip_launch_bounds_policy<LB> >(
Device::GetRajaResource(), LaunchParams(Teams(G), Threads(X, Y, BZ)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, G), [&] (const int n)
{
@@ -545,13 +552,12 @@ void RajaHipWrap3D(const int N, DBODY &&d_body,
using namespace RAJA;
using RAJA::RangeSegment;
launch<hip_launch_policy>
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE (LaunchContext ctx)
launch<hip_launch_policy>(Device::GetRajaResource(),
LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body);
});
MFEM_GPU_CHECK(hipGetLastError());
@@ -565,12 +571,10 @@ void RajaHipWrap3DLaunchBounds(const int N, DBODY &&d_body, const int X,
using namespace RAJA;
using RAJA::RangeSegment;
launch<hip_launch_bounds_policy<LB> >
(LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE(LaunchContext ctx)
{
loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body);
});
launch<hip_launch_bounds_policy<LB> >(
Device::GetRajaResource(), LaunchParams(Teams(GRID), Threads(X, Y, Z)),
[=] RAJA_DEVICE(LaunchContext ctx)
{ loop<hip_teams_x>(ctx, RangeSegment(0, N), d_body); });
MFEM_GPU_CHECK(hipGetLastError());
}
+2
View File
@@ -27,6 +27,7 @@ list(APPEND SRCS
handle.cpp
matrix.cpp
mma.cpp
multivector.cpp
ode.cpp
operator.cpp
ordering.cpp
@@ -63,6 +64,7 @@ list(APPEND HDRS
linalg.hpp
matrix.hpp
mma.hpp
multivector.hpp
ode.hpp
operator.hpp
ordering.hpp
+28
View File
@@ -14,6 +14,7 @@
#include "blockvector.hpp"
#include "blockoperator.hpp"
namespace mfem
{
@@ -129,6 +130,33 @@ void BlockOperator::MultTranspose(const Vector &x, Vector &y) const
}
}
#ifdef MFEM_USE_MPI
HypreParMatrix * BlockOperator::GetMonolithicHypreParMatrix() const
{
Array2D<const HypreParMatrix*> blocks(nRowBlocks, nColBlocks);
for (int i = 0; i < nRowBlocks; ++i)
{
for (int j = 0; j < nColBlocks; ++j)
{
if (IsZeroBlock(i, j))
{
blocks(i, j) = nullptr;
}
else
{
auto mat = dynamic_cast<const HypreParMatrix*>(&GetBlock(i, j));
MFEM_VERIFY(mat,"BlockOperator block (" << i << "," << j
<< ") is not a HypreParMatrix.");
blocks(i, j) = mat;
}
}
}
Array2D<real_t> coef_mut = coef; // make a non-const copy
return HypreParMatrixFromBlocks(blocks, &coef_mut);
}
#endif
BlockOperator::~BlockOperator()
{
if (owns_blocks)
+12
View File
@@ -16,6 +16,9 @@
#include "../general/array.hpp"
#include "operator.hpp"
#include "blockvector.hpp"
#ifdef MFEM_USE_MPI
#include "hypre.hpp"
#endif
namespace mfem
{
@@ -105,6 +108,15 @@ public:
/// Action of the transpose operator
void MultTranspose (const Vector & x, Vector & y) const override;
#ifdef MFEM_USE_MPI
/** @brief Returns a monolithic HypreParMatrix formed by merging the blocks of
this BlockOperator, assuming every block is a HypreParMatrix.
The returned matrix is newly allocated and owned by the caller, who is
responsible for deleting it. */
HypreParMatrix * GetMonolithicHypreParMatrix() const;
#endif
~BlockOperator();
//! Controls the ownership of the blocks: if nonzero, BlockOperator will
+48
View File
@@ -10,6 +10,9 @@
// CONTRIBUTING.md for details.
#include "complex_operator.hpp"
#ifdef MFEM_USE_MPI
#include "blockoperator.hpp"
#endif
#include <set>
#include <map>
@@ -164,6 +167,51 @@ void ComplexOperator::MultTranspose(const Vector &x_r, const Vector &x_i,
}
}
#ifdef MFEM_USE_MPI
ComplexHypreParMatrix * ComplexOperator::AsComplexHypreParMatrix() const
{
HypreParMatrix *Ar = nullptr;
HypreParMatrix *Ai = nullptr;
bool own_r = false;
bool own_i = false;
if (auto *Ahr = dynamic_cast<const HypreParMatrix*>(&real()))
{
Ar = const_cast<HypreParMatrix*>(Ahr);
}
else if (auto *Br = dynamic_cast<const BlockOperator*>(&real()))
{
Ar = Br->GetMonolithicHypreParMatrix();
own_r = true;
}
else
{
MFEM_ABORT("Real part is neither HypreParMatrix nor BlockOperator.");
}
if (auto *Ahi = dynamic_cast<const HypreParMatrix*>(&imag()))
{
Ai = const_cast<HypreParMatrix*>(Ahi);
}
else if (auto *Bi = dynamic_cast<const BlockOperator*>(&imag()))
{
Ai = Bi->GetMonolithicHypreParMatrix();
own_i = true;
}
else
{
MFEM_ABORT("Imag part is neither HypreParMatrix nor BlockOperator.");
}
return new ComplexHypreParMatrix(Ar, Ai, own_r, own_i, GetConvention());
}
#endif
SparseMatrix & ComplexSparseMatrix::real()
{
+17
View File
@@ -24,6 +24,9 @@
namespace mfem
{
#ifdef MFEM_USE_MPI
class ComplexHypreParMatrix; // forward declaration
#endif
/** @brief Mimic the action of a complex operator using two real operators.
@@ -118,6 +121,20 @@ public:
Convention GetConvention() const { return convention_; }
#ifdef MFEM_USE_MPI
/** @brief Return a newly allocated ComplexHypreParMatrix representation.
If the real and imaginary parts are HypreParMatrix objects, the returned
object borrows them and they must outlive the returned
ComplexHypreParMatrix. If they are BlockOperator objects with
HypreParMatrix blocks, they are first merged into monolithic matrices
owned by the returned ComplexHypreParMatrix.
The returned ComplexHypreParMatrix is owned by the caller, who is
responsible for deleting it. */
ComplexHypreParMatrix *AsComplexHypreParMatrix() const;
#endif
protected:
// Let this be hidden from the public interface since the implementation
// depends on internal members
+40 -9
View File
@@ -15,10 +15,20 @@
#ifdef MFEM_USE_CUDSS
#if CUDSS_VERSION >= 800
#ifdef MFEM_USE_SINGLE
#define CUDA_REAL_T CUDA_R_32F
#define CUDSS_REAL_T CUDSS_R_32F
#else
#define CUDA_REAL_T CUDA_R_64F
#define CUDSS_REAL_T CUDSS_R_64F
#endif
#define CUDSS_INT_T CUDSS_R_32I
#else
#ifdef MFEM_USE_SINGLE
#define CUDSS_REAL_T CUDA_R_32F
#else
#define CUDSS_REAL_T CUDA_R_64F
#endif
#define CUDSS_INT_T CUDA_R_32I
#endif
// Define a cuDSS error check macro, MFEM_CUDSS_CHECK(x), where x returns/is of
@@ -65,8 +75,13 @@ CuDSSSolver::CuDSSSolver(MPI_Comm comm_) : mpi_comm(comm_)
#endif
MFEM_CUDSS_CHECK(cudssSetCommLayer(handle, comm_lib));
#if CUDSS_VERSION >= 800
MFEM_CUDSS_CHECK(cudssDataSet(handle, solverData, CUDSS_DATA_COMM_HOST,
&mpi_comm, sizeof(MPI_Comm *)));
#else
MFEM_CUDSS_CHECK(cudssDataSet(handle, solverData, CUDSS_DATA_COMM,
&mpi_comm, sizeof(MPI_Comm *)));
#endif
}
#endif // MFEM_USE_MPI
@@ -257,11 +272,19 @@ void CuDSSSolver::SetMatrixCuDSS(int *csr_offsets, int *csr_columns,
CuMemcpyDtoD(csr_offsets_d, csr_offsets, (n_loc + 1) * sizeof(int));
CuMemcpyDtoD(csr_columns_d, csr_columns, nnz * sizeof(int));
#if CUDSS_VERSION >= 800
MFEM_CUDSS_CHECK(
cudssMatrixCreateCsr(
Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
csr_columns_d, csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
CUDSS_BASE_ZERO));
csr_columns_d, csr_values_d, CUDSS_INT_T, CUDSS_INT_T, CUDSS_REAL_T,
mat_type, mview, CUDSS_BASE_ZERO));
#else
MFEM_CUDSS_CHECK(
cudssMatrixCreateCsr(
Ac.get(), n_global, n_global, nnz, csr_offsets_d, NULL,
csr_columns_d, csr_values_d, CUDSS_INT_T, CUDSS_REAL_T,
mat_type, mview, CUDSS_BASE_ZERO));
#endif
}
else // !reorder_reuse
{
@@ -269,11 +292,19 @@ void CuDSSSolver::SetMatrixCuDSS(int *csr_offsets, int *csr_columns,
{
MFEM_CUDSS_CHECK(cudssMatrixDestroy(*Ac));
}
#if CUDSS_VERSION >= 800
MFEM_CUDSS_CHECK(
cudssMatrixCreateCsr(
Ac.get(), n_global, n_global, nnz, csr_offsets, NULL, csr_columns,
csr_values_d, CUDA_R_32I, CUDA_REAL_T, mat_type, mview,
CUDSS_BASE_ZERO));
Ac.get(), n_global, n_global, nnz, csr_offsets, NULL,
csr_columns, csr_values_d, CUDSS_INT_T, CUDSS_INT_T, CUDSS_REAL_T,
mat_type, mview, CUDSS_BASE_ZERO));
#else
MFEM_CUDSS_CHECK(
cudssMatrixCreateCsr(
Ac.get(), n_global, n_global, nnz, csr_offsets, NULL,
csr_columns, csr_values_d, CUDSS_INT_T, CUDSS_REAL_T,
mat_type, mview, CUDSS_BASE_ZERO));
#endif
}
#ifdef MFEM_USE_MPI
if (Mpi::IsInitialized())
@@ -334,10 +365,10 @@ void CuDSSSolver::SetNumRHS(int nrhs_) const
}
// Create empty RHS and solution vectors
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&xc, n_global, nrhs_, n_global, NULL,
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
CUDSS_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
MFEM_CUDSS_CHECK(cudssMatrixCreateDn(&yc, n_global, nrhs_, n_global, NULL,
CUDA_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
CUDSS_REAL_T, CUDSS_LAYOUT_COL_MAJOR));
#ifdef MFEM_USE_MPI
MFEM_CUDSS_CHECK(cudssMatrixSetDistributionRow1d(xc, row_start, row_end));
+41 -2
View File
@@ -2158,7 +2158,7 @@ void DenseMatrix::GetFromVector(int offset, const Vector &v)
}
}
void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
void DenseMatrix::AdjustDofDirection(const Array<int> &dofs)
{
const int n = Height();
@@ -2169,7 +2169,7 @@ void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
}
#endif
int *dof = dofs;
const int *dof = dofs;
for (int i = 0; i < n-1; i++)
{
const int s = (dof[i] < 0) ? (-1) : (1);
@@ -2185,6 +2185,45 @@ void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
}
}
void DenseMatrix::AdjustDofDirection(Array<int> &row_dofs,
Array<int> &col_dofs)
{
const int nr = row_dofs.Size();
const int nc = col_dofs.Size();
MFEM_VERIFY(Height() == nr && Width() == nc,
"DenseMatrix::AdjustDofDirection: size mismatch.");
// Extract signs and convert to unsigned indices
Vector rsign(nr), csign(nc);
for (int i = 0; i < nr; i++)
{
const int d = row_dofs[i];
if (d >= 0) { rsign(i) = 1.0; }
else { rsign(i) = -1.0; row_dofs[i] = -d - 1; continue; }
row_dofs[i] = d;
}
for (int j = 0; j < nc; j++)
{
const int d = col_dofs[j];
if (d >= 0) { csign(j) = 1.0; }
else { csign(j) = -1.0; col_dofs[j] = -d - 1; continue; }
col_dofs[j] = d;
}
// Apply row/column signs
for (int i = 0; i < nr; i++)
{
const real_t rs = rsign(i);
for (int j = 0; j < nc; j++)
{
(*this)(i,j) *= rs * csign(j);
}
}
}
void DenseMatrix::SetRow(int row, real_t value)
{
for (int j = 0; j < Width(); j++)
+7 -1
View File
@@ -474,7 +474,13 @@ public:
void GetFromVector(int offset, const Vector &v);
/** If (dofs[i] < 0 and dofs[j] >= 0) or (dofs[i] >= 0 and dofs[j] < 0)
then (*this)(i,j) = -(*this)(i,j). */
void AdjustDofDirection(Array<int> &dofs);
void AdjustDofDirection(const Array<int> &dofs);
/** If (row_dofs[i] < 0) xor (col_dofs[j] < 0) then
(*this)(i,j) = -(*this)(i,j). This method also converts
row_dofs/col_dofs to unsigned indices (d -> -d-1). */
void AdjustDofDirection(Array<int> &row_dofs,
Array<int> &col_dofs);
/// Replace small entries, abs(a_ij) <= eps, with zero.
void Threshold(real_t eps);
+2
View File
@@ -39,6 +39,7 @@ namespace Ginkgo
{
template <typename T> using gko_array = gko::array<T>;
#if defined(MFEM_USE_MPI) && GINKGO_BUILD_MPI
// for inter-operability with hypre integer types
using gko_hypre_int =
std::conditional_t<sizeof(HYPRE_Int) == sizeof(std::int32_t), std::int32_t,
@@ -50,6 +51,7 @@ static_assert(!std::is_void_v<gko_hypre_int>,
"HYPRE_Int type is incompatible with Ginkgo");
static_assert(!std::is_void_v<gko_hypre_bigint>,
"HYPRE_BigInt type is incompatible with Ginkgo");
#endif
/**
* Helper class for a case where a wrapped MFEM Vector
+29 -3
View File
@@ -2872,8 +2872,8 @@ void HypreParMatrix::Destroy()
if (HypreUsingGPU() && ParCSROwner && (diagOwner < 0 || offdOwner < 0))
{
// Put the "host" or "hypre" pointers in {i,j,data} of A->{diag,offd}, so
// that they can be destroyed by hypre when hypre_ParCSRMatrixDestroy(A)
// is called below.
// that they can be destroyed by mfem_hypre_TFree_host() or hypre when
// hypre_ParCSRMatrixDestroy(A) is called below, respectively.
// Check that if both diagOwner and offdOwner are negative then they have
// the same value.
@@ -2882,7 +2882,33 @@ void HypreParMatrix::Destroy()
MemoryClass mc = (diagOwner == -1 || offdOwner == -1) ?
Device::GetHostMemoryClass() : GetHypreMemoryClass();
Write(mc, diagOwner < 0, offdOwner <0);
Write(mc, diagOwner < 0, offdOwner < 0);
if (diagOwner == -1)
{
// Note: mfem_hypre_TFree_host() sets the pointer to NULL.
mfem_hypre_TFree_host(hypre_CSRMatrixI(A->diag));
if (hypre_CSRMatrixOwnsData(A->diag))
{
mfem_hypre_TFree_host(hypre_CSRMatrixJ(A->diag));
mfem_hypre_TFree_host(hypre_CSRMatrixData(A->diag));
}
#if MFEM_HYPRE_VERSION >= 21800
hypre_CSRMatrixMemoryLocation(A->diag) = GetHypreMemoryLocation();
#endif
}
if (offdOwner == -1)
{
// Note: mfem_hypre_TFree_host() sets the pointer to NULL.
mfem_hypre_TFree_host(hypre_CSRMatrixI(A->offd));
if (hypre_CSRMatrixOwnsData(A->offd))
{
mfem_hypre_TFree_host(hypre_CSRMatrixJ(A->offd));
mfem_hypre_TFree_host(hypre_CSRMatrixData(A->offd));
}
#if MFEM_HYPRE_VERSION >= 21800
hypre_CSRMatrixMemoryLocation(A->offd) = GetHypreMemoryLocation();
#endif
}
}
#endif
+5 -4
View File
@@ -432,10 +432,11 @@ private:
// and A->col_map_offd.
// The possible values for diagOwner are:
// -1: no special treatment of A->diag (default)
// when hypre is built with CUDA support, A->diag owns the "host"
// pointers (according to A->diag->owns_data)
// -2: used when hypre is built with CUDA support, A->diag owns the "hypre"
// pointers (according to A->diag->owns_data)
// when hypre is using GPU, A->diag owns the "host" pointers (according
// to A->diag->owns_data); these host pointers are freed by MFEM using
// hypre's host deallocation macros
// -2: used when hypre is using GPU, A->diag owns the "hypre" pointers
// (according to A->diag->owns_data)
// 0: prevent hypre from destroying A->diag->{i,j,data}
// 1: same as 0, plus own the "host" A->diag->{i,j}
// 2: same as 0, plus own the "host" A->diag->data
+1
View File
@@ -15,6 +15,7 @@
// Linear algebra header file
#include "vector.hpp"
#include "multivector.hpp"
#include "operator.hpp"
#include "matrix.hpp"
#include "sparsemat.hpp"
+60
View File
@@ -0,0 +1,60 @@
// 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 "multivector.hpp"
namespace mfem
{
MultiVector::MultiVector(const Array<int> &vector_sizes)
{
SetSizes(vector_sizes);
}
MultiVector::MultiVector(const Array<int> &vector_sizes, MemoryType mt)
{
SetSizes(vector_sizes, mt);
}
MultiVector::MultiVector(Vector &base, const Array<int> &vector_sizes)
{
MakeRef(base, vector_sizes);
}
void MultiVector::SetSizes(const Array<int> &vector_sizes)
{
blocks.resize(vector_sizes.Size());
for (int i = 0; i < vector_sizes.Size(); i++)
{
operator[](i).SetSize(vector_sizes[i]);
}
}
void MultiVector::SetSizes(const Array<int> &vector_sizes, MemoryType mt)
{
blocks.resize(vector_sizes.Size());
for (int i = 0; i < vector_sizes.Size(); i++)
{
operator[](i).SetSize(vector_sizes[i], mt);
}
}
void MultiVector::MakeRef(Vector &base, const Array<int> &vector_sizes)
{
blocks.resize(vector_sizes.Size());
for (int offset = 0, i = 0; i < vector_sizes.Size(); i++)
{
blocks[i].emplace<0>(base, offset, vector_sizes[i]);
offset += vector_sizes[i];
}
}
} // namespace mfem

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