Compare commits

...
622 Commits
Author SHA1 Message Date
Tzanio Kolev 5581b0c079 Merge pull request #4983 from dnpham23/master
Methods for extracting boundary edge DOFs from a 3D ParMesh and for synchronizing boundary conditions across processor boundaries
2026-08-20 15:23:36 -07:00
Tzanio Kolev e0ef9a423c Merge pull request #5439 from mfem/weighted-lor-transfer-v2
Add weighted LOR transfer
2026-08-20 11:38:06 -07:00
Dzung Pham fcbbfd49b9 Address review feedback: Fix H1 DOF dedup and single-precision reductions 2026-08-19 17:16:02 -07:00
Tzanio Kolev 5b1cc58146 Merge branch 'master' into master 2026-08-19 16:57:41 -07:00
Dzung Pham 240b2d5461 Make GroupCommunicator::MaxAbs tie-breaking deterministic
On an equal-magnitude tie, MaxAbs now keeps the more positive value, so
opposite-sign ties resolve to the positive one regardless of accumulation
order; a strictly larger magnitude still wins and keeps its sign. Update
the doc comment, add a GroupCommunicatorMaxAbs unit test, and add a
CHANGELOG entry.
2026-08-19 15:55:05 -07:00
Dzung Pham 962943a743 Address najlkin review: tidy API and includes
- GetBoundaryElementsByAttribute: return std::vector<Array<int>>
  indexed to match bdr_attrs instead of an unordered_map keyed by
  attribute.
- ParFiniteElementSpace::GetBoundaryLoopEdgeDofs: make ldof_marker an
  optional trailing output (Array<int>*, default nullptr). It is still
  computed internally and swapped into the caller's array only when
  requested; the same information is derivable from boundary_edge_dofs
  via ListToMarker().
- Remove unused <unordered_set>/<unordered_map> includes from the
  fespace/pfespace headers; include them directly in pfespace.cpp where
  they are actually used.
- Update the boundary-edge-DOF unit tests for the new signatures.
2026-08-19 13:30:32 -07:00
Tzanio Kolev 463cb07baf Merge pull request #5456 from mfem/extra_gpu_em
MixedVector gradient partial assembly
2026-08-19 13:19:13 -07:00
Andrew Ho 7e05f29325 changelog 2026-08-19 13:13:33 -07:00
Dzung Pham 67e329b0e0 Add missing newline at end of test_bdr_edgedof.cpp 2026-08-19 12:15:43 -07:00
Will Pazner 721d80b314 Fix LOR transfer miniapp integration on mixed meshes 2026-08-19 10:29:04 -07:00
Will Pazner 1bc33816f0 Merge remote-tracking branch 'origin/master' into weighted-lor-transfer-v2
# Conflicts:
#	CHANGELOG
2026-08-19 10:11:37 -07:00
Tzanio Kolev 51a60d6460 Merge branch 'master' into master 2026-08-19 09:27:22 -07:00
Tzanio Kolev c661137756 Merge pull request #5415 from Sbozzolo/node-local-output-dirs
Create node-local DataCollection output folders
2026-08-19 09:24:35 -07:00
Dzung Pham aa0a0f1cc7 Fix -Wshadow error in boundary edge DOF test helper
The inner loop index in ComputeBoundaryLoopLength shadowed the outer loop
variable introduced when the helper was switched to iterate over the
Array<int> dof_edges; rename it to fix the -Werror,-Wshadow CI build.
2026-08-18 23:02:42 -07:00
Dzung Pham 60d4ee9bf5 Address review feedback: Array<int> interface, doc/style fixes
- Redesign GetBoundaryLoopEdgeDofs/ComputeLoopEdgeOrientations to expose
  single-indexed Array<int> outputs instead of maps/sets (maps kept only as
  internal scratch); drop the vestigial dof_to_orientation output.
- Move the integral-type check into the BitOR template via static_assert and
  remove the unused ReduceOp enum Reduce overload.
- Remove redundant ParFiniteElementSpace overrides that duplicated the base
  implementations (mesh == pmesh for all Par constructors).
- Document the boundary loop definition and MaxAbs tie-breaking semantics.
- Add CHANGELOG entry; hardcode the expected loop-edge DOF count in the
  partition-invariance test.
2026-08-18 22:48:33 -07:00
Tzanio Kolev 5b3b486379 small fix 2026-08-18 18:30:11 -07:00
Tzanio Kolev 907a629f82 Merge pull request #5232 from mfem/tuple-refactor
refactor tuple for generic size
2026-08-18 17:56:53 -07:00
Andrew Ho 8789221a6b review comments 2026-08-18 16:07:22 -07:00
Andrew Ho 69a7a605c0 Merge remote-tracking branch 'origin/extra_gpu_em' into extra_gpu_em 2026-08-18 15:42:24 -07:00
Andrew Ho 610ce458f6 Revert documentation comments 2026-08-18 15:41:41 -07:00
Tzanio Kolev f7b6e0c0f0 Merge branch 'master' into weighted-lor-transfer-v2 2026-08-18 11:40:23 -07:00
Andrew Ho 7dea939ff8 Merge branch 'master' into extra_gpu_em 2026-08-18 10:57:46 -07:00
Andrew Ho 1ccb7bc613 Merge branch 'master' into extra_gpu_em 2026-08-18 10:57:24 -07:00
Tzanio Kolev e032c15aef Merge pull request #5249 from mfem/multi-vector-dev
Add new array-of-Vectors class that supports separate memory allocations for the individual Vectors
2026-08-18 10:57:15 -07:00
Veselin Dobrev 10ceb3e66b Added CHANGELOG entry for class MultiVector 2026-08-18 10:47:01 -07:00
Tzanio Kolev efa30a4a62 Merge pull request #5400 from mfem/gpu_em
GPU improvements for electromagnetics
2026-08-18 10:41:44 -07:00
Andrew Ho fbd217e8a4 Fixed bug for H1->RT 2026-08-18 10:35:32 -07:00
Andrew Ho c11172b842 Added MultTranspose test
It appears the bug for 3D H1->RT is tied to having NE > 1
2026-08-18 10:23:16 -07:00
Andrew Ho 0efbbd938a Merge remote-tracking branch 'origin/extra_gpu_em' into extra_gpu_em 2026-08-18 08:27:01 -07:00
Andrew Ho e5fae218af Added PA tests for all coefficient types for MixedVectorGradientIntegrator
Test seems to be failing for 3D RT
2026-08-18 00:32:39 -07:00
Tzanio Kolev 3ef9a5c668 Merge branch 'master' into gpu_em 2026-08-17 19:00:24 -07:00
Andrew Ho 968dc0bfce extra documentation from kris 2026-08-17 14:16:20 -07:00
Andrew Ho 8a88975532 Merge remote-tracking branch 'origin/master' into extra_gpu_em 2026-08-17 14:11:44 -07:00
Andrew Ho b279e7f318 style 2026-08-17 14:11:30 -07:00
Andrew Ho 4b9d8b9247 Additional changes from Kris Beckwith 2026-08-17 13:48:19 -07:00
Tzanio Kolev 7b85e1e9c1 Merge pull request #5440 from Sbozzolo/cuda-multi-arch-makefile
Makefile: support multiple CUDA architectures
2026-08-17 12:16:43 -07:00
Tzanio Kolev 775195b887 Merge pull request #5454 from mfem/umpire-cmake
Update Umpire CMake
2026-08-17 12:15:50 -07:00
Andrew Ho 89adf27a44 reduce max order since higher orders exceed the max dof/quad limits for HIP 2026-08-16 13:51:17 -07:00
Tzanio Kolev a7dbea190f Merge pull request #5435 from adamqc/fix-pncmesh-rebalance-attributes
Preserve element attributes during ParNCMesh rebalance
2026-08-15 13:35:42 -07:00
Tzanio Kolev 12e9b66eae Merge pull request #5412 from mfem/cuda-or-hip-in-c++-mode
Better support for using `mfem.hpp` in pure C++ sources when MFEM is built with CUDA or HIP
2026-08-15 13:27:42 -07:00
Tzanio Kolev 6f3ed5508a Merge branch 'master' into node-local-output-dirs 2026-08-14 18:06:25 -07:00
Tzanio Kolev 713edd670d Merge pull request #5399 from mfem/lor-mesh-connectivity
Support batched LOR assembly on highly connected meshes
2026-08-14 16:58:34 -07:00
Tzanio Kolev e2d6f5fb3b Merge pull request #5451 from mfem/shadow-warnings-take-2
Adjust default warnings
2026-08-14 16:58:16 -07:00
Tzanio Kolev 45b0e6e02c Merge pull request #5386 from mfem/curl_interp_pa
Curl Interpolator PA
2026-08-14 16:57:43 -07:00
Veselin Dobrev d37b7867ec In 'tuple.hpp':
* moved helper functions inside the namespace mfem::future::detail
* generalized functions using 'real_t' to any "scalar" type
* some formatting edits
2026-08-14 16:55:46 -07:00
Veselin Dobrev 73779b1de6 In the unit test 'test_tuple.cpp':
* fix for the case of debug + cuda/hip build
* add a gpu test for operator+ for tuples
2026-08-14 15:04:03 -07:00
Veselin DobrevandHugh Carson 8307a751db Apply suggestion from @hughcars
Co-authored-by: Hugh Carson <114775781+hughcars@users.noreply.github.com>
2026-08-13 17:15:12 -07:00
Andrew Ho 9f12aee475 review comments 2026-08-13 14:13:21 -07:00
Veselin Dobrev 366157036e Fix the test_tuple unit test for single-precision builds. 2026-08-13 14:10:19 -07:00
Andrew Ho 8afc1d1e36 Umpire also has moved to C++20 2026-08-13 14:03:03 -07:00
Veselin Dobrev 2bc734468d Fix the tuple unit test for serial build.
A few formatting edits.

Exclude the namespace mfem::future::detail from docs.
2026-08-13 13:34:03 -07:00
Tzanio Kolev c07c534f42 Merge pull request #5423 from adamqc/par-sesquilinear-device-diagonal-dev
Make complex system assembly device-safe
2026-08-13 07:27:54 -07:00
Will Pazner ccade73917 Move WARNING_FLAGS to the end of the file 2026-08-12 11:33:08 -07:00
Andrew Ho d169312edd Merge branch 'curl_interp_pa' into gpu_em 2026-08-12 10:15:28 -07:00
Andrew Ho 8812081cfc review comments 2026-08-12 10:14:16 -07:00
Andrew Ho b20051c06b Merge branch 'curl_interp_pa' into gpu_em 2026-08-12 08:45:25 -07:00
Andrew Ho d66068b754 fixed comment 2026-08-12 08:45:13 -07:00
Andrew Ho 362ca5b66d Merge branch 'curl_interp_pa' into gpu_em 2026-08-12 08:43:31 -07:00
Andrew Ho f9282b38f6 Make lor_ams produce a consistent gradient sign for RT as Curl
The sign shouldn't matter, but just for consistency
2026-08-12 08:41:42 -07:00
Tzanio Kolev aab2e1ebf8 Merge pull request #5337 from mfem/densetensor-move-fix
Add explicit move and copy operators to DenseTensor
2026-08-12 08:08:27 -07:00
Tzanio Kolev 8c2a8580b6 Merge pull request #5445 from mfem/macos-make-fix
Add a workaround for an issue with MacOS's default `make`
2026-08-12 08:08:04 -07:00
Veselin Dobrev 9141e85e15 Renamed an internal variable and an internal function. 2026-08-12 01:00:01 -07:00
Andrew Ho e57b63c660 Merge branch 'curl_interp_pa' into gpu_em 2026-08-11 21:30:06 -07:00
Andrew Ho 5d1958cfdf Remove rotated gradient 2026-08-11 21:23:53 -07:00
Veselin Dobrev 8183755dbf Reviewer feedback. 2026-08-11 16:37:42 -07:00
Andrew Ho 614a355c04 Merge branch 'curl_interp_pa' into gpu_em 2026-08-11 15:46:20 -07:00
Andrew Ho 79a88dfef5 undid change of removing ProjectGrad from 2D RT space quad and triangle elements
This is used by HypreAMS, unclear if it's ok to change HypreAMS to use
the CurlInterpolator instead of GradInterpolator for all possible edge
spaces.
2026-08-11 15:44:59 -07:00
Andrew Ho bd13f53db1 Merge branch 'curl_interp_pa' into gpu_em 2026-08-11 15:08:55 -07:00
Will Pazner 36be39433f Add L2 projection transfer ctors without coefficients 2026-08-11 14:57:42 -07:00
Andrew Ho eb738baebe Also test that curl interpolator produces the right rotation 2026-08-11 14:44:41 -07:00
Will Pazner 46c5aed37b Use only explicit capture in DifferentiableOperator lambda 2026-08-11 14:37:29 -07:00
Will Pazner 6b8f53308f Make PEDANTIC_FLAG logic more robust 2026-08-11 14:32:12 -07:00
Will Pazner 1369d61457 Rename captured variable 2026-08-11 14:32:01 -07:00
Will Pazner e7d6b370dc Silence -Wshadow false positives on clang version < 17 2026-08-11 12:48:21 -07:00
Will Pazner ba07e91128 Enable -pedantic only for gcc and clang 2026-08-11 12:48:02 -07:00
Will Pazner ebdf68a1c3 Whitespace in defaults.mk 2026-08-11 12:47:44 -07:00
Andrew Ho 5f31928c2b Merge branch 'curl_interp_pa' into gpu_em 2026-08-11 12:33:50 -07:00
Andrew Ho 0cf5aca53e updated comment 2026-08-11 12:32:16 -07:00
Andrew Ho 2357771384 Merge branch 'curl_interp_pa' into gpu_em 2026-08-11 12:27:23 -07:00
Andrew Ho 7efeb617b1 changelog 2026-08-11 12:25:31 -07:00
Andrew Ho fe025de316 Fixed bug in FA ProjectCurl for 2D RT->H1
Added unit tests for 2D CurlInterpolator
2026-08-11 12:14:43 -07:00
Tzanio Kolev 56edc22b3a Merge branch 'master' into weighted-lor-transfer-v2 2026-08-11 12:13:10 -07:00
Will Pazner 35b32b6a02 Make sure backwards operator is supported in plor-transfer 2026-08-11 11:36:18 -07:00
Will Pazner 76b5f341cc Merge pull request #5443 from mfem/raja-cpp
Bump RAJA required C++ version in CMake
2026-08-11 11:06:25 -07:00
Ce Qin ea8468ea95 Merge remote-tracking branch 'origin/master' into par-sesquilinear-device-diagonal-dev
# Conflicts:
#	fem/complex_fem.cpp
2026-08-11 22:23:22 +08:00
Tzanio Kolev 2ea59935d8 Merge branch 'master' into fix-pncmesh-rebalance-attributes 2026-08-10 11:02:57 -07:00
Andrew Ho 4e5ebe6451 Merge branch 'master' into gpu_em 2026-08-10 09:58:41 -07:00
Andrew Ho 04f23f353c Merge branch 'master' into curl_interp_pa 2026-08-10 09:57:49 -07:00
Tzanio Kolev 0a76b8bfb2 Merge pull request #5434 from adamqc/fix-integrated-gll-bdr-projection
Fix IntegratedGLL projection for Nedelec segment elements
2026-08-10 09:11:24 -07:00
Tzanio Kolev 6116b49933 Merge branch 'master' into fix-integrated-gll-bdr-projection
Conflicts:
	tests/unit/fem/test_project_bdr.cpp
2026-08-10 09:09:05 -07:00
Tzanio Kolev 09f6023468 Merge pull request #5426 from mfem/najlkin/fix-getedgetrans
[BUG] Fixed projection on periodic NC meshes
2026-08-10 09:03:48 -07:00
Veselin Dobrev 610a8f9c0b Merge branch 'master' into gpu_em 2026-08-09 23:06:44 -07:00
Veselin Dobrev 8f01292a45 Merge branch 'master' into curl_interp_pa 2026-08-09 23:00:52 -07:00
Veselin Dobrev f7056be951 Merge pull request #5334 from mfem/hcurl_mass_pa
VectorFEMassIntegrator ApplyPA improvements
2026-08-09 22:54:07 -07:00
Veselin Dobrev 790848019e Add a workaround for an issue with MacOS's default 'make': when
running 'make all -j 12' two times in a row, the second run hangs.
2026-08-08 22:22:26 -07:00
Tzanio Kolev 01b146ab01 Merge pull request #5385 from mfem/specialization-tests
Extra Specialization Tests
2026-08-08 11:01:03 -07:00
Tzanio Kolev ae87b89f16 Merge branch 'master' into specialization-tests 2026-08-07 11:54:45 -07:00
Will Pazner 28e0f3569a Merge pull request #5351 from mfem/batched-lu-fix-5342
Batched LU failure handling consistency
2026-08-07 09:32:12 -07:00
Will Pazner 3c9ee8ff42 Change StaticAssertCudaOrHipLanguage to RequireCudaOrHipLanguage
Add constexpr default template parameter to simplify usage.
2026-08-07 09:01:00 -07:00
Andrew Ho f49b9a58e8 missed the 2d case 2026-08-07 08:41:28 -07:00
Ce Qin 8bfac662f4 Fix code-style 2026-08-07 19:58:50 +08:00
Andrew HoandJohn Camier 905696021a Update tests/unit/fem/specializations/test_qinterp_det.cpp
Co-authored-by: John Camier <camierjs@gmail.com>
2026-08-06 10:01:56 -07:00
Andrew HoandJohn Camier dc6e1ff4ea Update tests/unit/fem/specializations/test_qinterp_grad.cpp
Co-authored-by: John Camier <camierjs@gmail.com>
2026-08-06 10:01:47 -07:00
Ce Qin ed563f3090 Expand ParNCMesh rebalance attribute coverage 2026-08-06 21:47:11 +08:00
Ce Qin 51f2f5dd78 Simplify integrated ND shape evaluation and tests 2026-08-06 21:33:14 +08:00
Tzanio Kolev 4e828b9240 Merge pull request #5437 from mfem/grad-kernel-sm0-size
Grad kernel bug
2026-08-06 03:46:47 -07:00
Dzung Pham fb2ee4d248 Merge remote-tracking branch 'upstream/master' 2026-08-05 22:53:12 -07:00
Dzung Pham 88b8ead3b9 Address review feedback
Document that GroupCommunicator::MaxAbs returns the signed value with the
largest absolute value, not the non-negative absolute value.
2026-08-05 18:19:17 -07:00
Dzung Pham 81b6d40fd1 Address review feedback
- Mention float in the Sum/Min/Max/MaxAbs instantiation comments.
- Wrap a long GetBdrElementFace call to stay within 80 columns.
- Explain in GetBoundaryLoopEdgeDofs why occurrences of GetEdgeDofs are
  counted rather than collecting GetEdgeInteriorDofs: the count also resolves
  vertex DOFs (kept at loop endpoints, dropped when shared), which
  GetEdgeInteriorDofs omits entirely.
2026-08-05 16:06:39 -07:00
Dzung Pham 2256251e56 Address review feedback
- Serial GetBoundaryLoopEdgeDofs (3D and 2D): keep an edge/vertex DOF only
  when it appears in exactly one selected boundary element, matching the
  parallel version and handling non-manifold junctions correctly.
- Inline the GetBoundaryElementsByAttributeImpl and
  ComputeLoopEdgeOrientationsImpl free helpers into their member functions.
- Make GroupCommunicator::ReduceOp an enum class and note that the reduction
  result is signed.
- Rename DoF -> DOF throughout the PR's additions and reflow doc comments to
  80 columns.
- Remove the unused GeneratePartitionings helper; build test partitions in
  place; add explanatory comments to the partition-invariant and
  shared-DOF-ownership tests.
2026-08-05 14:58:00 -07:00
Veselin Dobrev d627b19f06 Merge pull request #5297 from mfem/pncmesh-spacing
General NC mesh spacing in parallel
2026-08-05 14:35:52 -07:00
Jan Nikl 9b35464986 Added a tet unit test. 2026-08-05 13:27:48 -07:00
Andrew Ho f14a9bb53f Bump RAJA required C++ version in CMake 2026-08-05 13:09:02 -07:00
Jan Nikl 4eafaaa628 Added doxygen to GetTraceCollection(). 2026-08-05 11:35:44 -07:00
Jan Nikl bcab63b41c Added an assert for local edge index. 2026-08-05 11:25:48 -07:00
Jan Nikl 28c1f905b6 Removed non-L2 case from GetEdgeTransformation(). 2026-08-05 11:20:30 -07:00
Gabriele Bozzola 66dbe60cb1 Makefile: defer CUDA architecture flag selection 2026-08-05 07:05:36 -07:00
Andrew Ho f898d0bcde Merge branch 'hcurl_mass_pa' into curl_interp_pa 2026-08-05 07:01:19 -07:00
Gabriele Bozzola b8fcd640e5 Makefile: support multiple CUDA architectures
This PR changes the Makefile so that CUDA_ARCH can accept a
comma-separated list of compute capabilities (e.g.
CUDA_ARCH=sm_70,sm_80), mirroring the multi-architecture support the
CMake build already provides.
2026-08-05 01:26:56 -07:00
Veselin Dobrev 86dc01be73 In the ParELAG miniapp, MultilevelHcurlHdivSolver.cpp, use the
namespace qualified class name parelag::MultiVector to avoid
conflics with the new mfem::MultiVector class.
2026-08-04 16:31:07 -07:00
Veselin Dobrev 6e05112e5c Restored the MultiVector versions of the methods Operator::Mult
and Operator::GetGradient with new names: Operator::MultMV and
Operator::GetGradientMV.

In the non-const version of MultiVector::operator[], always generate
an error if the accessed block is read-only, i.e. it is a pointer to
a const Vector.

Update the doxygen documentation for the addition of read-only blocks,
i.e. block that use a pointer to a const Vector.

Reorder some method declarations in class MultiVector.
2026-08-04 16:12:51 -07:00
Jan Nikl c566a165b2 Added a check for 3D. 2026-08-04 14:44:57 -07:00
Andrew Ho 733d0bd177 Merge branch 'master' into hcurl_mass_pa 2026-08-04 14:05:34 -07:00
Andrew Ho 6e2bd88274 fallback version appears to still be faster for CPU for ND to ND 2026-08-04 14:00:38 -07:00
Andrew Ho 7a0a7bd1da style 2026-08-04 13:58:10 -07:00
Andrew Ho e59487bf14 move weak curl PA test to test_pa_coeff 2026-08-04 12:42:15 -07:00
Andrew Ho 647750ffa9 Merge remote-tracking branch 'origin/gpu_em' into gpu_em 2026-08-04 12:30:50 -07:00
Jan Nikl db42eb3255 Added edge to face table and used it for GetEdgeTransformation(). 2026-08-04 12:20:38 -07:00
Veselin Dobrev 1c19aba72a Merge pull request #5405 from mfem/jdongg/fix-gauss-jacobi-mpfr
Remove Gauss-Jacobi warning for missing MPFR implementation
2026-08-04 12:11:34 -07:00
Veselin Dobrev 1631ec67fa Merge pull request #5251 from Heinrich-BR/mixed-sesquilinear-dev
Mixed Sesquilinear Forms
2026-08-04 12:09:49 -07:00
Andrew Ho eceb502df3 Merge branch 'curl_interp_pa' into gpu_em 2026-08-04 11:54:55 -07:00
Andrew Ho bfdaf07a19 Merge branch 'hcurl_mass_pa' into curl_interp_pa 2026-08-04 11:50:49 -07:00
Andrew Ho d4b59fe357 appears to always be better to call the smem version even for CPU paths 2026-08-04 11:47:27 -07:00
Andrew Ho cdf077b560 Merge branch 'master' into hcurl_mass_pa 2026-08-04 11:06:42 -07:00
Andrew Ho 50ce940dee changelog 2026-08-04 11:04:54 -07:00
Jan Nikl d3307a6957 Added constexpr in the unit test. 2026-08-04 10:58:02 -07:00
Jan Nikl bde4cbbccd Changed parameters of the unit test. 2026-08-04 10:55:51 -07:00
camierjs 31ec16fa8a Support const MultiVector refs and remove Operator MultiVector Mult/GetGradient 2026-08-04 10:43:49 -07:00
Julian Andrej c8b64fef23 add tests and remove possible copy 2026-08-04 10:36:07 -07:00
camierjs 195ebe8812 Merge branch 'master' into multi-vector-dev 2026-08-04 09:53:19 -07:00
Will Pazner ebbdd4bbb4 Merge remote-tracking branch 'origin/master' into weighted-lor-transfer-v2 2026-08-04 09:46:09 -07:00
Will Pazner 63c2be4ed6 Update CHANGELOG 2026-08-04 09:45:58 -07:00
Will Pazner f324dd58d0 Add weighted LOR transfer sample runs 2026-08-04 09:42:21 -07:00
Will Pazner 626e4cc9c9 Check if backwards operator is supported in weighted LOR transfer 2026-08-04 09:42:12 -07:00
Will Pazner f19dfabb75 Fix member variable shadowing 2026-08-04 09:37:10 -07:00
Will Pazner b1b49cd3e9 Delete old comment 2026-08-04 09:35:33 -07:00
Will Pazner 2d33afe729 Remove unneeded ElementTransformation from ElemMixedEvaluation 2026-08-04 09:34:01 -07:00
Will Pazner 98b6f7c1cf Remove unneeded gitignore 2026-08-04 09:31:36 -07:00
Will Pazner 7483034f7c Add momentum-conserving weighted LOR transfer to miniapp 2026-08-04 09:30:04 -07:00
Dylan Copeland 4c16395398 CHANGELOG 2026-08-04 09:05:44 -07:00
Jan Nikl cfb05a4a60 Added a unit test for boundary projection on nodal NC mesh. 2026-08-03 17:50:12 -07:00
Will Pazner 48d16f7993 Remove standalone weighted LOR transfer miniapp 2026-08-03 16:09:27 -07:00
Will Pazner 1bfdf5bf31 Add option for weighted transfer in {lor,plor}_transfer miniapp 2026-08-03 16:09:06 -07:00
Will Pazner 5a28c20815 Add default constructor to CoefficientWithOrder 2026-08-03 16:09:06 -07:00
Will Pazner 3f38fc53f1 Add weighted LOR transfer example 2026-08-03 16:09:04 -07:00
Vlado Tomov LOFT e9cce62beb bug then d1d > q1d (happens in 3d for q1 remhos test) 2026-08-03 12:03:34 -07:00
Andrew Ho 7bb2d30100 Merge branch 'master' into batched-lu-fix-5342 2026-08-03 08:00:30 -07:00
Tzanio Kolev 7f061c7531 Merge pull request #5396 from mfem/pyramid-type-dev
Unit Test for High-Order Mixed Meshes [pyramid-type-dev]
2026-08-02 09:18:52 -07:00
Tzanio Kolev da7aaf8044 Merge pull request #5404 from mfem/gitlab-reporting-fix
In Gitlab CI, report a failure to run the `baseline` test as an error
2026-08-01 11:27:50 -07:00
Tzanio Kolev ca71605cf2 Merge branch 'master' into pyramid-type-dev 2026-08-01 11:26:19 -07:00
Tzanio Kolev ddea622f2c Merge pull request #5317 from mfem/nlconvpa
VectorConvectionNLFIntegrator PA support
2026-08-01 11:24:57 -07:00
Ce Qin 51a0058f65 Preserve element attributes during ParNCMesh rebalance 2026-08-01 23:09:30 +08:00
Ce Qin fd223f68b5 Fix integrated ND segment projection 2026-08-01 12:27:12 +08:00
camierjs c08f6a6262 Revert whitespace style in tests/unit/CMakeLists.txt 2026-07-31 12:34:06 -07:00
camierjs acf62776a0 Merge branch 'master' into nlconvpa 2026-07-31 12:24:45 -07:00
Mark L. Stowell 28dff525f2 Merge branch 'master' into pyramid-type-dev 2026-07-31 10:39:03 -07:00
Stowell, Mark L. e0b326633e Using consistent variable names 2026-07-31 10:38:10 -07:00
Stowell, Mark L. 7e5f9ed0db Merge branch 'pyramid-type-dev' of github.com:mfem/mfem into pyramid-type-dev 2026-07-31 10:35:33 -07:00
Stowell, Mark L. 29eb764386 Fixing pyramid type in FEC Clone methods 2026-07-31 10:34:45 -07:00
Stowell, Mark L. d01ed701be Fixing symmetry of L2 interpolation points 2026-07-31 10:20:56 -07:00
Stowell, Mark L. fabaf8a71c Updating offset when skipping certain element types 2026-07-31 10:17:44 -07:00
Stowell, Mark L. 2e746a45a2 Adding default pyramid basis type as a user-defined static variable 2026-07-31 10:14:22 -07:00
Stowell, Mark L. add500e73d Adding new unit test to CMake file 2026-07-31 10:13:18 -07:00
Stowell, Mark L. 8355f2b506 Adding apex limit test to H1 Pyramid c'tor 2026-07-31 10:11:52 -07:00
Tzanio Kolev d3cb97afb9 Merge pull request #5343 from mfem/vecdivpa
Improved PA VectorDivergenceIntegrator
2026-07-31 10:07:49 -07:00
Tzanio Kolev 7ad772ba05 Merge branch 'master' into vecdivpa 2026-07-31 10:06:34 -07:00
Tzanio Kolev 5c916241dc Merge pull request #5344 from mfem/vecmasspa
Improved VectorMassAssembleDiagonalPA
2026-07-31 10:05:53 -07:00
Tzanio Kolev e527b4aaac Merge pull request #5427 from mfem/tdrwenski/shadow-warnings
Align debug build warnings with CI
2026-07-31 10:04:07 -07:00
Tzanio Kolev 4e0479ef61 Merge pull request #5429 from mfem/bugfix/kweiss/mfem-1d-nurbs-patch
Skips comments when parsing nurbs patch controlpoints sections
2026-07-31 10:03:03 -07:00
Ce Qin eac57686c5 Rename the Hypre diagonal kernel 2026-07-31 09:29:34 +08:00
Andrew Ho ad962de425 fix merge 2026-07-30 10:18:23 -07:00
Andrew Ho c44c2f0cdf Merge branch 'master' into specialization-tests 2026-07-30 10:09:56 -07:00
Tzanio Kolev e3bae63ad3 Merge pull request #5381 from mfem/gpu-qinterp-integ
Integral space QuadratureInterpolator
2026-07-30 09:58:46 -07:00
Tzanio Kolev 1133560e56 Merge pull request #5094 from mfem/pgf-save-dev
Adding [Par]GridFunction::Save unit test (and bug fix)
2026-07-30 09:58:06 -07:00
Tzanio Kolev b08082488a Merge pull request #5424 from mfem/raja-hip-fix
Fixed a missing GetRajaResource() for hip
2026-07-30 09:56:37 -07:00
Tzanio Kolev a1532a029f Merge pull request #5052 from mfem/paraview-min-order-1
Ensure ParaViewDataCollection has minimum order 1
2026-07-30 09:55:34 -07:00
John Camier 4b4b18f76b Merge branch 'master' into pyramid-type-dev 2026-07-30 11:56:39 -04:00
John Camier 25a1c8f4a4 Merge branch 'master' into tuple-refactor 2026-07-30 10:02:33 -04:00
Ce Qin a60ba38833 Share complex operator construction 2026-07-30 14:13:03 +08:00
Dzung Pham dae8a18c32 Address review feedback
- communication.hpp: discard the BitOR_Op branch at compile time for
  non-integral types, which otherwise failed to link.
- communication.cpp: fix the neighbor-major buffer stride in
  ReduceMarked() byGroup mode, which mixed values from different DoFs.
- pfespace.cpp: reset ess_edge_list alongside ess_tdof_list so reuse
  does not leave stale entries.
- pfespace.cpp: reconcile 2D boundary vertex DoF occurrence parity
  across sharing groups, so a loop split between ranks matches serial.
- pfespace.cpp: synchronize the local marker before converting to true
  DoFs, so a selected shared DoF is not skipped by every rank.

Adds three regression tests, each confirmed to fail without its fix.
2026-07-29 12:09:39 -07:00
camierjs 34ff2399d0 Merge branch 'master' into nlconvpa 2026-07-29 09:24:51 -07:00
camierjs e95e86ce89 Fix warning missing return statement 2026-07-29 09:24:43 -07:00
camierjs ecd7134521 Fix warning missing return statement 2026-07-29 09:20:26 -07:00
camierjs 500936a816 Merge branch 'master' into vecdivpa 2026-07-29 09:18:40 -07:00
camierjs 48bcf417af Fix warning missing return statement at end of non-void function 2026-07-29 09:18:01 -07:00
camierjs e9826ac778 Merge branch 'master' into vecmasspa 2026-07-29 09:16:38 -07:00
Ce Qin 2fa81463ae Share imaginary essential diagonal handling 2026-07-29 13:41:09 +08:00
Kenneth Weiss c057a95552 unit test for skipping comments in nurbs patch sections 2026-07-28 18:46:23 -07:00
Kenneth Weiss a58cfe3b79 skips comments while parsing nurbs patch sections 2026-07-28 18:42:02 -07:00
Tara Drwenski 52e3b40633 Simplify default debug flags 2026-07-28 15:31:47 -07:00
Veselin Dobrev 5c7073d4ea Merge pull request #5407 from mfem/skip-branch-history
Add option to skip branch-history check
2026-07-28 12:17:52 -07:00
Gabriele Bozzola a2a14e8ad8 Add changelog entry 2026-07-28 12:12:23 -07:00
Gabriele Bozzola 98bbd8ad94 Merge branch 'master' into node-local-output-dirs 2026-07-28 12:10:45 -07:00
Andrew Ho 0909dc634a Merge branch 'master' into specialization-tests 2026-07-28 11:23:20 -07:00
Tzanio Kolev dc995c4aa0 Merge branch 'master' into cuda-or-hip-in-c++-mode 2026-07-28 10:35:26 -07:00
Tzanio Kolev dc68ebcf57 Merge pull request #5387 from mfem/tdrwenski/add-ccache-ci
Add ccache to GitHub CI builds
2026-07-28 10:17:25 -07:00
Stowell, Mark L. 6c0caaf161 Removing an overlooked output file 2026-07-27 10:26:28 -07:00
Will Pazner 2903d0f666 Support coefficient-weighted LOR transfer 2026-07-24 16:22:33 -07:00
Will Pazner 006e82f199 Remove unnecessary scope 2026-07-24 16:22:33 -07:00
Stowell, Mark L. 5f3ee49bf4 Tweaking the implementation and documentation 2026-07-24 13:54:22 -07:00
Stowell, Mark L. 3bcca9155b Adding unit test for ParGridFunction c'tor from multiple GridFunction objects 2026-07-24 12:01:04 -07:00
Stowell, Mark L. 90a6b33249 Merge branch 'pgf-save-dev' of github.com:mfem/mfem into pgf-save-dev 2026-07-24 11:21:35 -07:00
Stowell, Mark L. eda259eb3e Adding preserve option to ParGridFunction c'tor 2026-07-24 11:19:48 -07:00
Tara Drwenski 719f53828c Add -pedantic to debug builds (make and CMake) 2026-07-24 10:46:07 -07:00
Tara Drwenski bfbbee112c Enable -Wshadow in debug builds with clang (make and CMake) 2026-07-24 10:46:07 -07:00
Jan Nikl b9c960cc0d Removed separate edge transformation basis type. 2026-07-24 10:29:48 -07:00
Jan Nikl 0aa392a4ea Implemented GetEdgeTransformation for L2 elements. 2026-07-23 23:42:02 -07:00
Andrew Ho 0c4b6b76f6 style 2026-07-23 18:55:21 -07:00
Andrew Ho a3dc2f8850 Missed a missing GetRajaResource() 2026-07-23 14:48:56 -07:00
John Camier ab6d0d9777 Merge branch 'master' into tuple-refactor 2026-07-23 13:28:51 -04:00
John Camier f1440636d1 Merge branch 'master' into nlconvpa 2026-07-23 13:28:32 -04:00
John Camier 3ce8b9e250 Merge branch 'master' into hcurl_mass_pa 2026-07-23 13:28:17 -04:00
John Camier 073891f99e Merge branch 'master' into vecdivpa 2026-07-23 13:28:02 -04:00
John Camier e52671cf77 Merge branch 'master' into vecmasspa 2026-07-23 13:27:52 -04:00
John Camier 43dc6cb530 Merge branch 'master' into gpu-qinterp-integ 2026-07-23 13:27:35 -04:00
John Camier bbecf57ea0 Merge branch 'master' into tdrwenski/add-ccache-ci 2026-07-23 13:27:08 -04:00
John Camier 278e7f1fe6 Merge branch 'master' into skip-branch-history 2026-07-23 13:26:46 -04:00
Ce Qin ffa3d0789b Make complex system assembly device-safe 2026-07-23 22:57:05 +08:00
jdongg eaf91c9c08 Rebase onto master 2026-07-22 16:13:24 -07:00
jdongg 8330565463 Fix horizontal overflow of warning message 2026-07-22 16:00:15 -07:00
Mark L. Stowell b3c1651ddf Merge branch 'master' into pgf-save-dev 2026-07-22 14:53:07 -07:00
Justin Dong 6a6e9b5d6b Merge branch 'master' into jdongg/fix-gauss-jacobi-mpfr 2026-07-22 13:11:36 -07:00
Gabriele Bozzola 354af888c4 Create node-local DataCollection output folders
Often times, compute nodes have local storage that is faster than the
shared filesystem. Using node-local storage compared to the shared
filesystem can also be advantageous to reduce the stress on such
filesystem (which impacts all the users on a cluster).

At the moment, `DataCollection::create_directory` creates the collection
directory only on the global root rank (`myid == 0`) so that non-root
nodes cannot write their per-rank ParaView and VisIt outputs when the
path is not on the shared filesystem (e.g., on `/tmp` or `/scartch`).

In this PR, I have the lowest rank on each shared-memory node (found via
`MPI_COMM_TYPE_SHARED`) create the directory. When the filesystem is not
shared, each node will have the folder where to write their outptu
files. When the filesystem is shared, the extra mkdir() hits EEXIST,
which is already tolerated, so behavior there is unchanged.
2026-07-22 16:05:59 -04: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
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
Andrew Ho a8edefc027 review comments 2026-07-21 10:28:05 -07:00
Andrew Ho 609a9c0e3b Merge branch 'hcurl_mass_pa' into gpu_em 2026-07-21 10:23:34 -07:00
Andrew Ho 340fe85001 review comments 2026-07-20 20:46:47 -07:00
Will Pazner 10e95b9246 Add note about branch-history to README 2026-07-20 10:02:33 -07:00
Will Pazner 57dd7a5c9a Change branch-history check logic 2026-07-20 10:00:41 -07:00
John Camier 4c6291f018 Merge branch 'master' into hcurl_mass_pa 2026-07-17 13:18:09 -07:00
Hugh Carson 753e02c1c8 Merge branch 'master' into master 2026-07-17 15:16:05 -04:00
“Henrique c6378788af Add guard fix to (Par)SesquilinearForm 2026-07-17 18:22:08 +01:00
John Camier 41b4008f93 Merge branch 'master' into gpu-qinterp-integ 2026-07-17 09:31:50 -07:00
Tara Drwenski 1f551b5aac Add ccache for enzyme to CI 2026-07-17 09:02:59 -07:00
Julian Andrej d6fffff08c remove unreachable macro 2026-07-17 08:24:10 -07:00
“Henrique d65409fdc0 Fix FormRectangularLinearSystem guard 2026-07-17 15:16:40 +01:00
Tara Drwenski 5d909dbcf1 Inject ccache via OMPI_CXX for MPI builds 2026-07-16 11:00:48 -07:00
Tara Drwenski 896c3bbe30 Improve cleanup cache workflow to ensure all caches get removed 2026-07-16 11:00:48 -07:00
“Henrique 8b14357249 Added MixedSesquilinearForm changes to CHANGELOG 2026-07-16 15:42:55 +01:00
“Henrique cc1c6daed3 Fix ParMixedSesquilinearForm::FormRectangularLinearSystem bug 2026-07-16 15:39:06 +01:00
Sohail Reddy b1def1e3fa Added check to ensure gslib's integer-type 'uint' is 'unsigned int' 2026-07-15 20:50:59 -07:00
Dylan Copeland 28b6c85b44 Using mt19937. 2026-07-15 16:01:46 -07:00
Veselin Dobrev 891b675c7f Use 'hypot' instead of 'sqrt' + squares. 2026-07-15 15:50:01 -07:00
“Henrique 0a43f3ca1f Formatting fix 2026-07-15 21:07:46 +01:00
“Henrique 212edacfd1 Linting 2026-07-15 21:07:46 +01:00
“Henrique 45cd0db146 More review suggestions 2026-07-15 21:07:46 +01:00
Henrique BRandJan Nikl f0505ec6eb Apply suggestions from code review
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2026-07-15 21:07:46 +01:00
“Henrique d3fda1ed30 Comment fixes 2026-07-15 21:07:46 +01:00
“Henrique f63e95a7a1 Expand tests to cover more cases 2026-07-15 21:07:46 +01:00
“Henrique 24e63e6802 Fix serial indexing bug 2026-07-15 21:07:46 +01:00
“Henrique e8961b32ff Fix indexing issue 2026-07-15 21:07:46 +01:00
“Henrique 9c4fa75530 Remove redundant code 2026-07-15 21:07:46 +01:00
“Henrique b904dd0131 More review fixes 2026-07-15 21:07:45 +01:00
“Henrique 24652e2a36 Linting 2026-07-15 21:07:45 +01:00
“Henrique 529209bcf2 Review suggestions 2026-07-15 21:07:45 +01:00
“Henrique 501e37d105 Small fixes 2026-07-15 21:07:45 +01:00
“Henrique d48384f9f4 Linting 2026-07-15 21:07:45 +01:00
“Henrique 94a815d9c9 Added unit test 2026-07-15 21:07:45 +01:00
“Henrique 1a03792398 Add Update method 2026-07-15 21:07:45 +01:00
“Henrique 118e97772c Change Hypre_ParCSR to MFEM_SPARSEMAT 2026-07-15 21:07:45 +01:00
Henrique BRandSocratis Petrides f1138eae7a Apply suggestions from code review
Co-authored-by: Socratis Petrides <petrides1@llnl.gov>
2026-07-15 21:07:45 +01:00
“Henrique 449199525b Added (Par)MixedSesquilinearForms 2026-07-15 21:07:45 +01:00
John Camier 17ecabf915 Merge branch 'master' into tuple-refactor 2026-07-15 09:01:56 -07:00
John Camier 6e26615ef9 Merge branch 'master' into tdrwenski/add-ccache-ci 2026-07-15 08:31:36 -07:00
John Camier 861f629b58 Merge branch 'master' into skip-branch-history 2026-07-15 07:40:30 -07:00
John Camier de1a876e39 Merge branch 'master' into jdongg/fix-gauss-jacobi-mpfr 2026-07-15 07:38:48 -07:00
John Camier 866b6922b3 Merge branch 'master' into vecmasspa 2026-07-15 06:01:29 -07:00
John Camier ee57be0c4e Merge branch 'master' into vecdivpa 2026-07-15 06:01:22 -07:00
John Camier d90a1c6662 Merge branch 'master' into nlconvpa 2026-07-15 06:01:17 -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
Dylan Copeland 538711c13f Merge branch 'master' of github.com:mfem/mfem into pncmesh-spacing 2026-07-14 15:36:04 -07:00
Dylan Copeland 412cc42685 Added new miniapps to doxygen html documentation. 2026-07-14 15:35:42 -07:00
John Camier 472da91016 Merge branch 'master' into vecmasspa 2026-07-14 14:03:32 -07:00
John Camier 1871a7122e Merge branch 'master' into vecdivpa 2026-07-14 14:03:30 -07:00
John Camier cff5d989f7 Merge branch 'master' into nlconvpa 2026-07-14 14:03:26 -07:00
Ketan Mittal f0f1052c99 Merge branch 'master' into gslib-uint-fix 2026-07-14 13:45:26 -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
camierjs 86aebd39dc Add AddSpecialization to register all VectorConvectionNLF kernels: AddMultPA, AddMultGrad & GradDiag 2026-07-14 11:20:44 -07:00
Veselin Dobrev c8b1dcad70 Fix the non-GPU build 2026-07-14 05:35:20 -07:00
Veselin Dobrev fa006da71e Modifications allowing the use of 'mfem.hpp' in pure c++ source files when
the library is built with CUDA or HIP support.
2026-07-14 04:38:48 -07:00
Andrew Ho 1e5f9e4d6b Merge branch 'hcurl_mass_pa' into gpu_em 2026-07-13 18:50:46 -07:00
Andrew Ho b0cc0a9b8c fix specializations
this works for the default for linear, quadratic, and cubic meshes
2026-07-13 18:42:33 -07:00
camierjs f2fa9f1295 Use CoefficientVector constructor directly, use COMPRESSED storage 2026-07-13 16:34:27 -07:00
camierjs 572cda7deb Remove duplicate 'VectorConvectionNLF' prefix for nonlininteg registered kernels 2026-07-13 16:23:52 -07:00
camierjs 3f5dfc8bfd Avoid the std::exchange in test_pa_nlvc unit tests 2026-07-13 16:02:23 -07:00
camierjs 6086293e35 Avoid long lines in test_pa_diagonal unit tests 2026-07-13 15:44:20 -07:00
camierjs 9d729f0c11 Add integ_pa and integ_fa ownership comments in test_pa_kernels unit tests 2026-07-13 15:38:53 -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
Andrew Ho 4b1852e924 revert whitespace changes 2026-07-10 11:00:44 -07:00
Andrew Ho 1471e1dcae review comments 2026-07-10 10:59:05 -07:00
Andrew Ho e839a5e8ab Merge remote-tracking branch 'origin/hcurl_mass_pa' into hcurl_mass_pa 2026-07-10 10:29:00 -07:00
Andrew Ho 9d40c8b40c Merge branch 'master' into hcurl_mass_pa 2026-07-10 10:28:35 -07:00
Andrew Ho 069c618def review comments 2026-07-10 10:27:05 -07:00
Will Pazner 10868379f8 Add skip check output 2026-07-09 17:55:11 -07:00
Will Pazner 8c25b2f8c3 Add skip-branch-history option 2026-07-09 17:50:25 -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
jdongg e6d5e98a06 Remove error message for missing Gauss-Jacobi MPFR implementation and use double precision. Add warning. 2026-07-09 13:42:07 -07:00
Dylan Copeland cfa3440178 Merge branch 'master' of github.com:mfem/mfem into pncmesh-spacing 2026-07-09 13:19:50 -07:00
Veselin Dobrev dc00e63579 In Gitlab CI, report a failure to run the 'baseline' test as an error. 2026-07-09 11:57:22 -07:00
Andrew Ho ed9a29130f changelog 2026-07-09 11:31:59 -07:00
Andrew Ho 9a80c8cd14 Merge branch 'curl_interp_pa' into gpu_em 2026-07-09 11:26:33 -07:00
Andrew Ho 143d7bf31b changelog 2026-07-09 11:26:19 -07:00
Andrew Ho 8358ee93fa Extracted changes from gpu_em to for lower dimension CurlInterpolator 2026-07-09 11:24:11 -07:00
Andrew Ho eb38d6ecd8 Merge branch 'master' into curl_interp_pa 2026-07-09 11:16:22 -07:00
Andrew Ho 5f80fb1eb7 change to use override 2026-07-09 11:12:55 -07:00
Andrew Ho 52efc31130 style 2026-07-09 10:44:29 -07:00
Andrew Ho b7dc53af15 added patches from Kris to support out of plane 2D EM 2026-07-09 10:40:24 -07:00
Andrew Ho 2b5c0c6fe4 formatting 2026-07-08 19:02:49 -07:00
Nuno Nobre d7c781a6f9 Fix output of H(curl) vars in 1d 2026-07-09 01:49:26 +01:00
Andrew Ho 7b8af2b05f Merge branch 'master' into gpu_em 2026-07-08 16:20:32 -07:00
Andrew Ho 1433d4aec4 added checks for map type 2026-07-08 16:07:52 -07:00
camierjs 7d1f4ab1ec CHANGELOG, add diagonal test, fix use of += for AssembleDiagonalPA 2026-07-08 15:55:41 -07:00
camierjs 1066ef593f CHANGELOG, 2D mixed-order specializations, 3D MFEM_VERIFY & cleanup 2026-07-08 15:30:22 -07:00
camierjs a5ece9c0ca Add abort for ConvectiveVectorConvectionNLFIntegrator and SkewSymmetricVectorConvectionNLFIntegrator 2026-07-08 15:15:19 -07:00
Andrew Ho 74d1579371 changelog 2026-07-08 15:14:23 -07:00
Andrew Ho ea83267885 Added support for L2 Integral spaces to MixedScalarCurlIntegrator 2026-07-08 15:10:35 -07:00
camierjs 3718cb8248 CHANGELOG, nlvc bench in makefile, align instantiations 2026-07-08 15:10:26 -07:00
camierjs 321961cfc9 Add tests for user specializations 2026-07-08 15:04:30 -07:00
camierjs f0fe1796bf Fix header file kernels so users can instantiate their own specializations 2026-07-08 14:40:44 -07:00
Stowell, Mark L. e7f4b88a09 Merge branch 'pyramid-type-dev' of github.com:mfem/mfem into pyramid-type-dev 2026-07-08 14:30:13 -07:00
Stowell, Mark L. 7e620ad313 Fixing more apex issues 2026-07-08 14:29:59 -07:00
camierjs 89460c70ca Add tests with different orders for the vector and scalar space
Fix header file kernels so users can instantiate their own specializations
2026-07-08 14:17:39 -07:00
Will Pazner 49201d41c3 Support batched LOR assembly on highly connected meshes
The same change was made for full assembly in PR #4646.
2026-07-08 12:24:53 -07:00
John Camier b8671ed8a1 Merge branch 'master' into vecmasspa 2026-07-08 06:55:11 -07:00
John Camier de5ccf68ad Merge branch 'master' into vecdivpa 2026-07-08 06:55:01 -07:00
John Camier d3238fe235 Merge branch 'master' into nlconvpa 2026-07-08 06:54:48 -07: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 9b8f799ac5 Merge branch 'master' into pyramid-type-dev 2026-07-07 13:31:23 -07:00
Tzanio Kolev a53c446dd7 Merge branch 'master' into hcurl_mass_pa 2026-07-07 13:31:12 -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
Andrew Ho 3c8c8c21a9 Merge branch 'master' into hcurl_mass_pa 2026-07-07 11:19:23 -07:00
Mark L. Stowell 8374b84bff Merge branch 'master' into pyramid-type-dev 2026-07-07 09:23:25 -07:00
John Camier 0e4d208f06 Merge branch 'master' into vecmasspa 2026-07-07 17:05:58 +02:00
John Camier eb04f3c1ea Merge branch 'master' into vecdivpa 2026-07-07 17:05:56 +02:00
John Camier 7c50e9f807 Merge branch 'master' into nlconvpa 2026-07-07 17:05:48 +02:00
blaz fa13c848c9 fix in the documentation - a factor of 2 2026-07-07 00:13:18 -07:00
Stowell, Mark L. db727de78b Adding 1D mesh to the new unit test 2026-07-06 15:09:52 -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
Stowell, Mark L. e5106b52f1 Merge remote-tracking branch 'origin/master' into pyramid-type-dev
# Conflicts:
#	mesh/pmesh.cpp
2026-07-06 11:31:11 -07:00
Stowell, Mark L. 64cc5a98bd Documenting a new parameter 2026-07-06 11:18:03 -07:00
Stowell, Mark L. 485a0f2ce8 Changing assumptions in Mesh::DoNodeReorder for high order mixed meshes 2026-07-06 10:55:32 -07:00
Stowell, Mark L. 4c95e84e53 Handling pyramid apex more robustly in Bergot H1 and L2 bases 2026-07-06 10:53:54 -07:00
Stowell, Mark L. bd2f74f25b Correcting comments 2026-07-06 10:53:07 -07:00
Stowell, Mark L. 14826adf5d Adding support for selecting the Bergot pyramids in meshes and FE collections 2026-07-06 10:52:48 -07:00
Stowell, Mark L. 48c7debfce Adding unit test for read/write high order and mixed meshes 2026-07-06 10:50:06 -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 50d58159bd Merge branch 'master' into tuple-refactor 2026-07-03 19:04:42 +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 6302cd37b7 Merge branch 'master' into gpu-qinterp-integ 2026-07-03 19:02:40 +02:00
camierjs d93bca38aa Cleanup 2026-07-02 11:16:39 -07:00
camierjs 37c20ff70e Rework ElasticityAssembleDiagonalPA to avoid using scratch memory 2026-07-02 10:48:35 -07:00
camierjs ad804074f9 Fix ElasticityIntegrator AssembleDiagonalPA/AddMultPA QVec size 2026-07-02 10:48:35 -07:00
camierjs 2197dd8b06 Adjust SmemPAVectorMassAssembleDiagonal3D 2026-07-02 10:48:35 -07:00
camierjs b40de0e4e4 2D/3D VectorMassAssembleDiagonalPA specialized on T_Q1D 2026-07-02 10:48:35 -07:00
camierjs 9f4c3f8cbf Use max D1D/Q1D instead of T1D 2026-07-02 10:01:36 -07:00
camierjs 28eb5906f2 Use DofQuadLimits instead of local T_MDQ 2026-07-02 09:49:47 -07:00
camierjs 8c9987e63a With style 2026-07-02 09:34:49 -07:00
camierjs 3495617be6 Cleanup nlvc tests and add libCEED verifications 2026-07-02 09:17:02 -07:00
John Camier bd5f9d80b4 Merge branch 'master' into speed-up-gitignore-job 2026-07-02 18:06:03 +02:00
camierjs 07ebe7889e Cleanup & use MFEM_GENERATE_RANGES instead of overload functions 2026-07-02 07:53:46 -07:00
camierjs 18668ddca6 Direct threads and cmath for win32 2026-07-01 15:55:18 -07:00
camierjs 5e4b69f3d8 Cleanup bilininteg_vecdiv_pa 2026-07-01 14:32:50 -07:00
camierjs bfe77c97f2 Simplify tests unit test_pa_vecdiv 2026-07-01 14:12:11 -07:00
Dzung Pham 8f5c7a0eca Merge remote-tracking branch 'upstream/master' 2026-07-01 13:43:07 -07:00
camierjs 7091d4ceb1 Improved PA VectorDivergenceIntegrator
Add shared-memory PA kernels with kernel registration, transpose support,
and unit tests.
2026-07-01 13:34:44 -07:00
camierjs e0ecd9b8ff Bring stacked changes from vecdivpa 2026-07-01 13:32:29 -07:00
John Camier af0f8520d6 Merge branch 'master' into nlconvpa 2026-07-01 21:46:46 +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
Dylan Copeland fbb0e44dce Merge branch 'master' of github.com:mfem/mfem into pncmesh-spacing 2026-06-30 17:43:43 -07:00
nbeams 0171b4b02d Only set gko_hypre_* types when building with MPI 2026-06-30 21:20:25 +00:00
Jan Nikl a5fca573a9 Merge branch 'master' into pgf-save-dev 2026-06-30 13:28:19 -07:00
Tara Drwenski 43de095a2d Decrease ccache max size to 1G 2026-06-30 13:18:51 -07:00
LwhJesse cc585df285 Apply batched LU code style 2026-07-01 03:23:06 +08:00
LwhJesse c7f2950458 Address batched LU review feedback 2026-07-01 03:02:31 +08:00
Tara Drwenski b25f438922 Delete a PR's caches when it is closed 2026-06-30 10:42:48 -07:00
Tara Drwenski 49310d0260 Increase ccache max size to 2G 2026-06-30 09:39:18 -07:00
Tara Drwenski a9bc59fdd5 Add ccache sloppiness and basedir for direct-mode hits 2026-06-30 09:38:28 -07:00
Tara Drwenski 17fa9b844c Shorten comment 2026-06-30 08:51:51 -07:00
John Camier 68e3a929c2 Merge branch 'master' into lorentz-device 2026-06-30 17:51:21 +02:00
Jesse Li 068b61eb3f Merge branch 'master' into batched-lu-fix-5342 2026-06-30 17:41:40 +08:00
LwhJesse 3419a50655 Make batched LU failure test robust on GPU 2026-06-30 17:24:25 +08:00
Tara Drwenski 821a899d50 Add ccache to GitHub CI builds 2026-06-29 17:11:06 -07:00
John Camier 06d18956f8 Merge branch 'master' into bugfix-project 2026-06-27 20:14:54 +02:00
John Camier f0d9a81fd4 Merge branch 'master' into nlconvpa 2026-06-27 20:13:25 +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
Andrew Ho f6b333681f duplicate test names 2026-06-26 14:00:23 -07:00
Andrew Ho 644b4ef141 fix formatting 2026-06-26 13:22:42 -07:00
Andrew Ho 08d6dd777a add tests which mimic users adding their own specializations 2026-06-26 13:16:06 -07:00
Andrew Ho 879413e774 Merge branch 'gpu-qinterp-integ' into specialization-tests 2026-06-26 11:57:24 -07:00
Tzanio Kolev 8c68e8402f Merge branch 'master' into speed-up-gitignore-job 2026-06-26 11:44:38 -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
camierjs e1f7df8d44 Merge branch 'master' into nlconvpa 2026-06-25 10:48:31 +02:00
John Camier 5048b1a219 Merge branch 'master' into lorentz-device 2026-06-25 07:47:47 +02:00
John Camier 63627acf30 Merge branch 'master' into tuple-refactor 2026-06-25 07:46:45 +02:00
John Camier b84988d5f1 Merge branch 'master' into bugfix-project 2026-06-25 07:44:37 +02: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
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 63a38bed84 Merge branch 'master' into hypremat 2026-06-24 11:03:55 -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
Stowell, Mark L. db0c173012 Merge remote-tracking branch 'origin/master' into pgf-save-dev
# Conflicts:
#	fem/pgridfunc.cpp
2026-06-23 11:57:23 -07:00
Ketan Mittal 161630bf12 fix edge initialization for surface kernels 2026-06-22 16:11:56 -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
Ketan Mittal 2f0bbc6fc4 Merge branch 'master' into lorentz-device 2026-06-22 08:55:49 -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
Jesse Li ce80de49d0 Merge branch 'master' into batched-lu-fix-5342 2026-06-18 20:09:31 +08:00
John Camier 86af0f883c Merge branch 'master' into tuple-refactor 2026-06-17 08:59:28 -07:00
John Camier 38ed1e049b Merge branch 'master' into lorentz-device 2026-06-17 05:58:47 -07:00
LwhJesse c529d34eea Fix GPU BLAS helper build guard 2026-06-17 20:09:21 +08:00
LwhJesse 742d043ead Improve batched LU failure checks 2026-06-17 20:06:14 +08:00
Dylan Copeland a67c93d0b8 Merge branch 'master' of github.com:mfem/mfem into pncmesh-spacing 2026-06-15 13:50:23 -07:00
Ketan Mittal eeca0b4cd5 reviewer comments 2026-06-14 19:52:40 -07:00
John Camier f51b8c2047 Merge branch 'master' into nlconvpa 2026-06-10 17:01:51 -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
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
John Camier dbb5fe2f0e Merge branch 'master' into tuple-refactor 2026-06-09 06:54:17 -07:00
John Camier 2f21794999 Merge branch 'master' into hypremat 2026-06-09 06:45:29 -07:00
John Camier 8d512c82f4 Merge branch 'master' into nlconvpa 2026-06-09 06:42: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
John Camier 8feb690d6d Merge branch 'master' into nlconvpa 2026-06-06 06:27:04 -07:00
Tzanio Kolev 94da954917 Merge branch 'master' into tuple-refactor 2026-06-05 16:01:05 -07:00
LwhJesse 006855bec2 Handle batched LU failures in GPU backends 2026-06-03 13:09:17 +08:00
John Camier fd55dc64d0 Merge branch 'master' into nlconvpa 2026-06-02 06:08:29 -07:00
Dylan Copeland 96f9456a7d Reformatting. 2026-05-31 17:38:46 -07:00
Tzanio Kolev 6ce18b2005 Merge branch 'master' into tuple-refactor 2026-05-27 09:24:12 -07:00
John Camier 16af7365a2 Merge branch 'master' into nlconvpa 2026-05-27 06:30:56 -07:00
Dylan Copeland c26f1937a9 Minor fixes suggested by copilot. 2026-05-26 22:06:49 -07:00
Dylan Copeland 8431604228 Merge branch 'master' of github.com:mfem/mfem into pncmesh-spacing 2026-05-26 21:28:49 -07:00
Julian Andrej c09b6d8a1d make style 2026-05-26 19:49:07 -07:00
Julian AndrejandCopilot Autofix powered by AI 19d9175833 replace tuple implementation with generic sized
Apply suggestions from code review

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

Add tuple include

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

use move instead of copy

properly do forwards
2026-05-26 17:41:13 -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
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 b67b1af8f8 Merge branch 'master' into nlconvpa 2026-05-24 20:13:45 -07:00
John Camier 0d3b658dc4 Merge branch 'master' into nlconvpa 2026-05-21 06:05:13 -07:00
Mittal, Ketan 58e6c4fc6d merge with master and resolve conflicts 2026-05-20 11:21:29 -07:00
Will Pazner e01d5afadb Add move and copy operators to DenseTensor
The default-provided move and copy could cause a crash because the
internal Mk DenseMatrix may be dangling, and so it cannot be moved
or copied into.
2026-05-19 14:04:11 -07:00
camierjs bd4504d7ae Simplify MDQ for NLVC PA kernels 2026-05-19 11:12:50 -07:00
camierjs f622b53731 Simplify nlvc unit tests 2026-05-19 10:13:31 -07:00
John Camier 60eb714229 Merge branch 'master' into nlconvpa 2026-05-18 06:04:17 -07:00
John Camier 1d3a723af9 Merge branch 'master' into nlconvpa 2026-05-14 13:20:27 -07:00
Ketan Mittal 411a361ebf Merge branch 'master' into lorentz-device 2026-05-09 20:22:29 -07:00
John Camier 2e8e4a5377 Merge branch 'master' into nlconvpa 2026-05-09 11:31:10 -07:00
Dylan Copeland 27deb9cdd2 Merge branch 'master' of github.com:mfem/mfem into pncmesh-spacing 2026-05-06 12:02:19 -07:00
Ketan Mittal 0ee57ac12d Merge branch 'master' into lorentz-device 2026-05-05 22:53:21 -07:00
John Camier 139c3ddaa6 Merge branch 'master' into nlconvpa 2026-05-05 15:32:54 -07:00
John Camier dee971c308 Merge branch 'master' into lorentz-device 2026-05-05 06:21:51 -07:00
John Camier 24f1022f7d Merge branch 'master' into nlconvpa 2026-05-05 06:19: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
Tzanio Kolev 426b5dc6cd Merge branch 'master' into lorentz-device 2026-05-02 13:00:07 -07:00
John Camier edc4d9a187 Merge branch 'master' into nlconvpa 2026-04-29 17:04:08 -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
Stowell, Mark L. 6a169432fa Adding [Pyraqmid] tag to unit tests 2026-04-27 12:03:26 -07:00
Tzanio Kolev 03c576d1ea Merge branch 'master' into pgf-save-dev 2026-04-26 14:35:44 -06:00
camierjs 2277decd8c With style 2026-04-25 14:24:05 -07:00
camierjs 36f6ff983a VectorConvectionNLFAddMultGradPA3D fallback checks, fix copilot reviews and Win32 math defines 2026-04-25 14:23:22 -07:00
camierjs 6fc6cf9186 Fix Windows compile-time constant expressions 2026-04-25 13:39:08 -07:00
camierjs bb06604dac Avoid narrowing non-constant-expression in initializer list 2026-04-25 13:24:06 -07:00
camierjs 058c6b2dee Avoid documenting NLVC registered kernels 2026-04-25 13:13:51 -07:00
camierjs 94135f3ed2 Merge branch 'camierjs-NLConvPA' into mfem-NLConvPA 2026-04-25 12:56:25 -07:00
camierjs 47c1d6230a Simplify NLVC diagonal kernels 2026-04-25 12:52:57 -07:00
camierjs a22c2c8d72 Cleanup instantiated NLVC registered kernels 2026-04-25 12:21:08 -07:00
camierjs 65f6ade43d Remove low order 3D VectorConvection kernels 2026-04-25 11:29:10 -07:00
camierjs 64cf121310 Meld back MFEM header 2026-04-25 11:07:13 -07:00
camierjs 92e1eace88 Revert test_nl_convection_nd 2026-04-25 11:06:24 -07:00
camierjs e6a3835983 Meld back toward master, rename nlvc unit tests 2026-04-25 11:01:45 -07:00
camierjs d97c8ec672 Cleanup debug traces 2026-04-25 10:33:20 -07:00
camierjs 6d9f34a3d7 Cleanup debug traces, nlvc benchmarks & use transposed adjugate 2026-04-25 08:50:32 -07:00
camierjs 5b73d20291 Cleanup NLF VConv diagonal 2026-04-25 07:15:07 -07:00
camierjs 4febbb7721 Merge branch 'master' into mfem-NLConvPA 2026-04-25 06:08:49 -07:00
camierjs ab81de5bf5 Merge branch 'master' into camierjs-NLConvPA 2026-04-25 05:49:57 -07:00
camierjs 23814cc1fa nlvc diagonal tests & benchmarks 2026-04-24 20:44:59 -07:00
camierjs 6307cef7cb Merge branch 'NLConvPA' of github.com:camierjs/mfem-NLConvPA into camierjs-NLConvPA 2026-04-24 17:51:37 -07:00
camierjs 156f7f930d NVTX marks 2026-04-24 17:51:35 -07:00
camierjs 0d5f21188d Add missing low order specializations 2026-04-24 17:48:28 -07:00
camierjs a786d4f293 SmemPAConvectionNLGradDiagonal 2026-04-24 17:46:27 -07:00
camierjs 018ab7b974 wip SmemPAConvectionNLGradDiagonalPA2D 2026-04-24 14:21:49 -07:00
Dylan Copeland a7b30bed56 Merge branch 'master' of github.com:mfem/mfem into pncmesh-spacing 2026-04-21 19:20:03 -07:00
camierjs f66aaa46bd Merge branch 'camierjs-NLConvPA' into nlconvpa 2026-04-20 14:42:09 -07:00
camierjs eff6bc5abc Probe for ConstantCoefficient first 2026-04-16 10:52:14 -07:00
camierjs baf29bff27 Add NVTX marks and fmt::fmt 2026-04-15 16:12:35 -07:00
camierjs 9969e42270 Split LOVectorConvectionNLFAddMultGradPA3DType 2026-04-15 16:02:13 -07:00
camierjs 4936834c5e Split LO/HO VectorConvectionNLF Grad kernels 2026-04-15 15:46:30 -07:00
camierjs da51f42c90 H100x runs 2026-04-15 15:33:31 -07:00
camierjs a7dd90466e Register VectorConvectionNLFAddMultPA, benchmarks 2026-04-15 14:35:32 -07:00
camierjs d231431ca7 LOSmemPAConvectionNLGradApply3D 2026-04-15 09:50:20 -07:00
camierjs e55b49b932 Ini kernels LO 2026-04-15 06:52:21 -07:00
camierjs e480c5f37b Register VectorConvectionNLFAddMultGradPA 2026-04-14 13:07:16 -07:00
camierjs 3911f44906 SmemPAConvectionNLGradApply3D and FunctionCoefficient 2026-04-14 11:59:05 -07:00
camierjs 6fa3bc57eb SmemPAConvectionNLGradApply2D running 2026-04-13 22:45:16 -07:00
camierjs 157a1f04f9 wip SmemPAConvectionNLGradApply2D 2nd part 2026-04-13 22:20:31 -07:00
camierjs 7bc531ba39 wip SmemPAConvectionNLGradApply2D 2026-04-13 21:47:37 -07:00
Dylan Copeland fd63847904 Merge branch 'master' of github.com:mfem/mfem into pncmesh-spacing 2026-04-13 20:09:16 -07:00
camierjs 9343b54c89 Added SmemPAConvectionNLApply2D 2026-04-13 18:28:46 -07:00
camierjs 0ec3e1d21a wip SmemPAConvectionNLApply3D new kernels 2026-04-13 17:12:57 -07:00
camierjs 4aa44a9b39 Merge remote-tracking branch 'refs/remotes/origin/NLConvPA' into NLConvPA 2026-04-13 13:20:34 -07:00
camierjs d191d332f8 Merge remote-tracking branch 'refs/remotes/origin/NLConvPA' into NLConvPA 2026-04-13 13:19:06 -07:00
camierjs 9dd104c211 fix fmt_FOUND 2026-04-13 13:18:33 -07:00
camierjs e62d26a450 wip Q_adj 2026-04-13 13:18:10 -07:00
camierjs 7ee86d6e75 wip SmemPAConvectionNLGradApply2D 2026-04-13 12:48:49 -07:00
camierjs 3d0878ded5 Setup PA NLConv tests 2026-04-12 15:24:11 -07:00
Dylan Copeland 5269fc2bf2 gitignore 2026-04-09 16:08:14 -07:00
Dylan Copeland 4036a7d0c2 Remove unused variable. 2026-04-09 15:22:56 -07:00
Dylan Copeland 8099ca947e General spacing for refinement of parallel NC meshes. Added a parallel miniapp, demonstrating 3:1 refinement. 2026-04-09 14:44:53 -07:00
AnnevanGils 02822e620a Merge branch 'master' into complexgridfunction_Lp_error 2026-03-24 10:49:26 +01: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
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
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
Ketan Mittal 3505a5a354 Merge branch 'master' into lorentz-device 2026-02-20 09:31:38 -08:00
Mittal, Ketan d62ffe149a style 2026-02-17 08:49:04 -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 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
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
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
Mittal, Ketan 9e5b93a532 initial commit with working prototype 2026-01-15 18:30:23 -08:00
Dzung Pham b33de09ae7 clean up unused variables 2025-12-04 11:43:58 -05:00
Dzung Pham cfdf4321e2 Apply code style formatting to boundary edge DoF files and tests 2025-12-04 01:25:33 -05:00
Dzung Pham 84055907cb Merge branch 'master' into master 2025-12-03 22:56:34 -05:00
Mark L. Stowell 3d8a58fc90 Merge branch 'master' into pgf-save-dev 2025-10-30 00:06:32 -07:00
Stowell, Mark L. ed472cba47 More file closures 2025-10-29 15:13:50 -07:00
Stowell, Mark L. 1449e5f9d3 Closing files in unit test 2025-10-29 14:41:39 -07:00
Stowell, Mark L. 89d94457fe Adding [Par]GridFunction::Save unit test (and bug fix) 2025-10-29 12:33:02 -07:00
Will Pazner c4e4c21951 Ensure ParaViewDataCollection has minimum order 1
This is the minimum order that is supported by ParaView
2025-10-01 12:55:51 -07:00
Dzung Pham e7523c2f87 refactoring functions in fespace and pfespace 2025-09-15 22:36:21 -04:00
Dzung Pham 97bd40f2f0 extend GetBoundaryEdgeDofs to cover both 2D and 3D cases, rename it to GetBoundaryLoopEdgeDofs, remove SynchronizeBC, add unit test for 2D case 2025-09-13 13:25:17 -04:00
Dzung Pham 8f090cf9dc update unit test to reduce # of cases, replace test mesh with a leaner one, add documentation and some minor changes 2025-09-11 01:20:49 -04:00
Dzung Pham ad83ec9d69 Refactoring 2025-09-04 03:02:40 -04:00
Dzung Pham 1049100166 WIP - refactoring 2025-08-29 11:38:32 -07:00
Dzung Pham 157a2360b4 fix format, add missing documentation, fix variable shadow 2025-08-21 18:41:42 -07:00
Dzung Pham adbbeaccb3 remove temporary test folder 2025-08-20 17:10:28 -04:00
Dzung Pham 65257818c5 add unit tests for edge dof extractions and synchronization 2025-08-20 17:10:28 -04:00
Dzung Pham bedb6f4050 clean up and reorganize the tests for boundary edge dof methods 2025-08-20 17:10:28 -04:00
Dzung Pham 51ed5a1359 remove out-of-date manual patch files 2025-08-20 17:10:28 -04:00
Dzung Pham 2fcb7bd083 Update tests for boundary edge DoFs 2025-08-20 17:10:28 -04:00
Dzung Pham 7b336c9ea2 update cubetest 2025-08-20 17:10:28 -04:00
Dzung Pham b7784c087e generalize the edge dof extraction method to multiple boundary loops 2025-08-20 17:10:28 -04:00
Dzung Pham 92fe6ae2bc WIP 2025-08-20 17:10:28 -04:00
Dzung Pham 8793fe8536 Add alternative method to synchronize only marked ldofs across processors. 2025-08-20 17:10:28 -04:00
Dzung Pham 9a20a3b1ec Update unit tests and cubetest to use new GetBoundaryEdgeDoFs and ReduceMarked (if applicable) 2025-08-20 17:10:28 -04:00
Dzung Pham 472241ccbf Modify GetBoundaryEdgeDoFs to return ldof markers, add new reduction method ReduceMarked 2025-08-20 17:10:28 -04:00
Dzung Pham 90c8967758 Clean up the cube test 2025-08-20 17:10:28 -04:00
Dzung Pham 4e7aaac06a Add new synchronize method for applying loop BC on tdofs in parallel. Add new MaxAbs reduce operation 2025-08-20 17:10:28 -04:00
Dzung Pham b3f9e66822 Update Makefile for new test, modify cube test to use edge dof extraction from mfem 2025-08-20 17:10:28 -04:00
Dzung Pham ab36f48dee Add method to compute perimeters of bounary loops 2025-08-20 17:10:28 -04:00
Dzung Pham ad47e2ec05 Add new test for loop edge boundary condition 2025-08-20 17:10:28 -04:00
Dzung Pham 03c24ccddc Add new mesh for testing 2025-08-20 17:10:28 -04:00
Dzung Pham 7754a02670 Add patch files to include edge dof methods into pfespace 2025-08-20 17:10:28 -04:00
Dzung Pham 7778f2840a Add brute force unit tests 2025-08-20 17:10:28 -04:00
Dzung Pham dbae2da272 Add methods to extract boundary edge dofs and loop orientations 2025-08-20 17:10:28 -04:00
196 changed files with 19587 additions and 6472 deletions
+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`
+70 -2
View File
@@ -111,6 +111,7 @@ jobs:
build-system: make
hypre-target: int64
precision: fp64
gitignore-check: YES
- os: ubuntu-latest
target: opt
codecov: NO
@@ -141,6 +142,10 @@ jobs:
continue-on-error: ${{ matrix.enzyme && true || false }}
# Enable ccache for all jobs except Windows (would need sccache).
env:
USE_CCACHE: ${{ matrix.os != 'windows-latest' }}
steps:
# Fix 'No space left on device' errors for Ubuntu builds.
- name: Run Actions Cleaner
@@ -289,6 +294,52 @@ jobs:
echo "OMPI_CC=$LLVM_PREFIX/bin/clang" >> $GITHUB_ENV
echo "OMPI_CXX=$LLVM_PREFIX/bin/clang++" >> $GITHUB_ENV
# Restore the compiler cache (ccache). The key embeds the run id, so new
# runs save a fresh snapshot; the restore-keys prefix warm-starts from the
# most recent prior run (incl. the base branch for PRs).
- name: cache ccache
if: ${{ env.USE_CCACHE == 'true' }}
uses: actions/cache@v5
with:
path: .ccache
key: ccache-${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}${{ matrix.enzyme && '-enzyme' || '' }}-${{ github.run_id }}
restore-keys: |
ccache-${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}${{ matrix.enzyme && '-enzyme' || '' }}-
# Configure ccache and select how it is injected into the MFEM build:
# - make: set CXX="ccache g++"; for MPI, OMPI_CXX="ccache g++" so mpicxx
# runs ccache around g++ (not ccache around the mpicxx wrapper).
# - cmake: set CMAKE_<LANG>_COMPILER_LAUNCHER=ccache.
# - enzyme: wrap the brew clang++ via OMPI_CXX.
# The chosen options are passed through build-mfem's 'config-options'
# input (see the build step below).
- name: configure ccache
if: ${{ env.USE_CCACHE == 'true' }}
run: |
command -v ccache >/dev/null 2>&1 || {
if [[ "${{ runner.os }}" == "Linux" ]]; then
sudo apt-get update && sudo apt-get install -y ccache
else
brew install ccache
fi
}
echo "CCACHE_DIR=${{ github.workspace }}/.ccache" >> $GITHUB_ENV
echo "CCACHE_MAXSIZE=1G" >> $GITHUB_ENV
echo "CCACHE_COMPILERCHECK=content" >> $GITHUB_ENV
# Ignore header timestamps (restamped by each checkout) so direct mode hits.
echo "CCACHE_SLOPPINESS=include_file_mtime,include_file_ctime,time_macros" >> $GITHUB_ENV
# Hash absolute paths relative to the workspace.
echo "CCACHE_BASEDIR=${{ github.workspace }}" >> $GITHUB_ENV
if [[ "${{ matrix.enzyme }}" == "true" ]]; then
echo "OMPI_CXX=ccache $LLVM_PREFIX/bin/clang++" >> $GITHUB_ENV
elif [[ "${{ matrix.build-system }}" == "cmake" ]]; then
echo 'CCACHE_CONFIG_OPTS=-DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache' >> $GITHUB_ENV
else
echo "OMPI_CXX=ccache g++" >> $GITHUB_ENV
echo 'CCACHE_CONFIG_OPTS=CXX="ccache g++" MPICXX="mpicxx"' >> $GITHUB_ENV
fi
shell: bash
# MFEM build and test
- name: build
uses: mfem/github-actions/build-mfem@v2.7
@@ -304,9 +355,14 @@ jobs:
metis-dir: ${{ env.METIS_TOP_DIR }}
mfem-dir: ${{ env.MFEM_TOP_DIR }}
precision: ${{ matrix.precision }}
config-options: ${{ matrix.config-opts }}
config-options: ${{ matrix.config-opts }} ${{ env.CCACHE_CONFIG_OPTS }}
library-only: ${{ matrix.target == 'dbg' && matrix.os != 'ubuntu-latest' }}
- name: ccache stats
if: ${{ env.USE_CCACHE == 'true' }}
run: ccache -s
shell: bash
# Run checks (and only checks) on debug targets
- name: checks
if: matrix.build-system == 'make' && matrix.target == 'dbg'
@@ -317,7 +373,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'
@@ -369,3 +431,9 @@ jobs:
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
+42
View File
@@ -0,0 +1,42 @@
# 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.
---
# A closed PR's caches can never be restored again, so delete them to free
# space against the 10 GB per-repo cache limit.
name: Cleanup PR caches
on:
pull_request:
types: [closed]
permissions:
actions: write
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Delete caches for the closed PR
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_REF: refs/pull/${{ github.event.pull_request.number }}/merge
run: |
echo "Deleting caches for $PR_REF"
while :; do
ids=$(gh cache list --ref "$PR_REF" --limit 100 --json id --jq '.[].id')
[ -n "$ids" ] || break
echo "$ids" | while read -r id; do
[ -n "$id" ] || continue
echo "Deleting cache $id"
gh cache delete "$id" || echo " (already gone)"
done
done
+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 }}
-102
View File
@@ -1,102 +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
MFEM_ACTIONS_VERSION: v2.7
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 }}-int32-fp64-${{ env.MFEM_ACTIONS_VERSION }}
- name: Get Hypre
if: steps.hypre-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-hypre@v2.7
with:
archive: ${{ env.HYPRE_ARCHIVE }}
dir: ${{ env.HYPRE_TOP_DIR }}
target: int32
precision: fp64
- 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 }}-${{ env.MFEM_ACTIONS_VERSION }}
- name: Install Metis
if: steps.metis-cache.outputs.cache-hit != 'true'
uses: mfem/github-actions/build-metis@v2.7
with:
archive: ${{ env.METIS_ARCHIVE }}
dir: ${{ env.METIS_TOP_DIR }}
# MFEM build and test
- name: build-mfem
uses: mfem/github-actions/build-mfem@v2.7
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
+33 -4
View File
@@ -13,6 +13,7 @@ name: "Checks"
permissions:
actions: write
pull-requests: read
on:
push:
@@ -29,6 +30,11 @@ concurrency:
# by checking if the workflow trigger is 'push' ("github.event_name == 'push'")
# and if we are in a fork ("github.event.pull_request.head.repo.full_name !=
# github.repository").
#
# The logic for the branch-history check is slightly different, since that check
# also inspects the PR's labels to allow for overriding failures. In this case,
# we run on all 'pull_request' triggers, but only run for 'push' triggers that
# do not correspond to any open PRs.
jobs:
file-headers-check:
@@ -128,10 +134,7 @@ jobs:
branch-history:
if: |
github.ref != 'refs/heads/next' &&
github.ref != 'refs/heads/master' &&
(github.event_name == 'push' ||
github.event.pull_request.head.repo.full_name != github.repository)
github.ref != 'refs/heads/next' && github.ref != 'refs/heads/master'
runs-on: ubuntu-latest
steps:
- name: checkout mfem
@@ -139,7 +142,27 @@ jobs:
with:
fetch-depth: 0
- name: check for pull request
id: check_pr
if: github.event_name == 'push'
env:
GH_TOKEN: ${{ github.token }}
run: |
pr_exists=$(gh pr list --repo "$GITHUB_REPOSITORY" \
--head "$GITHUB_REF_NAME" \
--state open \
--json number \
--jq 'length > 0')
echo "pr_exists=$pr_exists" >> "$GITHUB_OUTPUT"
- name: branch-history
id: branch_history
if: |
(github.event_name == 'pull_request' ||
github.event_name == 'workflow_dispatch' ||
steps.check_pr.outputs.pr_exists == 'false')
continue-on-error: ${{ contains(github.event.pull_request.labels.*.name,
'branch-history-override') }}
run: |
# We override origin to make sure we point to the main repo.
# This is to have consistent test results on PRs from forks.
@@ -147,3 +170,9 @@ jobs:
git remote add origin https://github.com/mfem/mfem.git
git checkout -b gh-actions-branch-history
./config/githooks/pre-push --history
- name: report branch-history override
if: steps.branch_history.outcome == 'failure'
run: |
echo "::warning::branch-history check failed, but the" \
"'branch-history-override' label is set."
+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:
+1
View File
@@ -260,6 +260,7 @@ miniapps/meshing/polar-nc
miniapps/meshing/mesh-quality
miniapps/meshing/hpref
miniapps/meshing/phpref
miniapps/meshing/pref321
miniapps/meshing/mobius-strip.mesh
miniapps/meshing/klein-bottle.mesh
miniapps/meshing/toroid-*.mesh
+4 -2
View File
@@ -102,12 +102,14 @@ report_baseline:
mkdir -p ${MACHINE_NAME}
rundir="${MACHINE_NAME}/$(date +%Y-%m-%d)-gitlab-${BASELINE_TEST}-${CI_COMMIT_REF_SLUG}"
rundir=$(${CI_PROJECT_DIR}/.gitlab/scripts/safe_create_rundir ${rundir})
cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir}
status=0
cp ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/* ${rundir} || { status=1; }
printf "%s\n" "" "Pipeline URL:" "$CI_PIPELINE_URL" \
>> ${rundir}/pipeline.txt
# We create an autotest-email.html file, because that's how we signal
# that there was an error / diff (temporary).
if [[ -f ${rundir}/${BASELINE_TEST}.err ]] || \
if [[ $status -ne 0 ]] || \
[[ -f ${rundir}/${BASELINE_TEST}.err ]] || \
[[ -f ${rundir}/${BASELINE_TEST}-${MACHINE_NAME}.diff ]]; then
cp ${rundir}/pipeline.txt ${rundir}/autotest-email.html
fi
+76
View File
@@ -15,6 +15,10 @@ Version 4.9.1 (development)
Discretization improvements
---------------------------
- 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.
@@ -42,8 +46,29 @@ Discretization improvements
- Extend FindPointsGSLIB to support surface meshes.
- Added support for complex-valued mixed bilinear forms via the new classes
MixedSesquilinearForm and ParMixedSesquilinearForm, mirroring the existing
SesquilinearForm classes. Rectangular complex operators are now also
handled correctly by ComplexSparseMatrix::GetSystemMatrix and
ComplexHypreParMatrix::GetSystemMatrix, which previously assumed equal
trial and test spaces.
- Added FiniteElementSpace::GetBoundaryLoopEdgeDofs to extract the edge DOFs on
the perimeter loop of a set of boundary elements, with a ParFiniteElementSpace
overload that reconciles the selection across processor boundaries so the
result is partition invariant. This is useful for imposing boundary conditions
on boundary edge DOFs.
- Added a MaxAbs reduction to GroupCommunicator that selects the signed value of
largest magnitude across a group, keeping its sign. Equal-magnitude ties
resolve deterministically to the positive value.
Meshing improvements
--------------------
- Added support for nonuniform anisotropic mesh refinement on parallel quad/hex
meshes with arbitrary spacing in each direction. This enables in particular
3:1 refinement in parallel, as demonstrated in the new meshing miniapp pref321.
- Added option to guarantee mesh validity during TMOP-based r-adaptivity, using
bounds on the determinant of the mesh transformation Jacobian.
@@ -64,18 +89,55 @@ Linear and nonlinear solvers
PRefinement multigrid methods for problems posed on trace spaces (see e.g. the
DPG miniapps).
- Added new class MultiVector: an array of Vectors of different sizes where each
Vector can be allocated independently. Also, added associated methods in class
Operator: MultMV, MultTransposeMV, and GetGradientMV, that use MultiVector
objects for input and/or output parameters. [PR #5249]
GPU computing
-------------
- Improved partial assembly for VectorDivergenceIntegrator with shared-memory
kernels, kernel registration, and transpose support.
- Improved partial-assembly diagonal kernels for VectorMassIntegrator (shared-
memory specializations) and ElasticityIntegrator (no scratch Q-vector).
- Added PA gradient and diagonal support for VectorConvectionNLFIntegrator
(AssembleGradPA, AddMultGradPA, AssembleGradDiagonalPA).
- Added device assembly support for 3D H(curl) VectorFEDomainLFIntegrator.
- Added partial assembly support for MixedScalarWeakGradientIntegrator.
- Added partial assembly support for MixedDotProductIntegrator.
- Added partial assembly support for MixedScalarCrossProductIntegrator.
- Added partial assembly support for MixedScalarWeakCrossProductIntegrator.
- Added partial assembly support for MixedVectorGradientIntegrator for H1->RT.
- Added support for device partial assembly CurlInterpolator.
This supports 2D and 3D variants:
2D H1 (out-of-plane) to RT (in-plane)
2D ND (in-plane) to Integral L2 (out-of-plane)
3D ND to RT
- 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.
- Changed VectorFEMassIntegrator to use kernel specialization dispatch for
partial assembly.
- Added support for FiniteElement::MapType::INTEGRAL spaces to
QuadratureInterpolator.
- Added support for FiniteElement::MapType::INTEGRAL spaces to
MixedScalarCurlIntegrator.
New and updated examples and miniapps
-------------------------------------
- The Lorentz miniapp (in miniapps/electromagnetics) has been updated to
@@ -90,6 +152,20 @@ Miscellaneous
using the new method ApplyDofSigns() in class ParFiniteElementSpace: the
method will return immediately if no sign flips are needed.
- Added support for coefficient-weighted LOR transfer in
L2ProjectionGridTransfer. The transfer conserves the weighted mass, for
example when transferring velocity while conserving density-weighted momentum.
This is illustrated in the lor-transfer and plor-transfer miniapps.
- Added support for saving DataCollection output on the node-local storage,
instead of requiring that the filesystem is shared among all the ranks.
API changes
-----------
- Removed ProjectGrad from 2D RT elements. Users should use ProjectCurl instead.
This also fixes a bug where ProjectCurl was returning the negative curl,
identical to ProjectGrad.
Version 4.9, released on Dec 11, 2025
=====================================
+10 -12
View File
@@ -88,18 +88,9 @@ if (MFEM_USE_STRUMPACK OR MFEM_USE_MUMPS)
# Just needed to find the MPI_Fortran libraries to link with
set(XSDK_ENABLE_Fortran ON)
endif()
# Ginkgo requires C++17:
if ((MFEM_USE_GINKGO) AND ("${CMAKE_CXX_STANDARD}" LESS "17"))
set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard to use." FORCE)
# Google Benchmark, SUNDIALS, STRUMPACK, Tribol, RAJA and Umpire require C++14:
elseif ((MFEM_USE_BENCHMARK OR
MFEM_USE_SUNDIALS OR
MFEM_USE_STRUMPACK OR
MFEM_USE_TRIBOL OR
MFEM_USE_RAJA OR
MFEM_USE_UMPIRE) AND
("${CMAKE_CXX_STANDARD}" LESS "14"))
set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ standard to use." FORCE)
# RAJA requires C++20:
if ((MFEM_USE_UMPIRE OR MFEM_USE_RAJA) AND ("${CMAKE_CXX_STANDARD}" LESS "20"))
set(CMAKE_CXX_STANDARD 20 CACHE STRING "C++ standard to use." FORCE)
endif()
# Include xSDK default CMake file.
@@ -239,6 +230,13 @@ else()
set(MFEM_DEBUG OFF)
endif()
# Shadow warnings for clang only; GCC's -Wshadow flags more.
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pedantic -Wall -Wshadow")
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pedantic -Wall")
endif()
# Shared build on Windows
if (WIN32 AND BUILD_SHARED_LIBS)
# CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS works only with MSVC?
-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>
+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)
+35 -4
View File
@@ -27,7 +27,10 @@ MPICXX = mpicxx
BASE_FLAGS = -std=c++17
OPTIM_FLAGS = -O3 $(BASE_FLAGS)
DEBUG_FLAGS = -g $(XCOMPILER)-Wall $(BASE_FLAGS)
# The variable WARNING_FLAGS depends on which compiler is used, and is defined
# later in this file.
DEBUG_FLAGS = $(strip -g $(addprefix $(XCOMPILER),$(WARNING_FLAGS)) $(BASE_FLAGS))
# Prefixes for passing flags to the compiler and linker when using CXX or MPICXX
CXX_XCOMPILER =
@@ -46,6 +49,10 @@ SHARED = NO
#
# If you set MFEM_USE_ENZYME=YES, must use CUDA_CXX=clang++
CUDA_CXX = nvcc
# CUDA compute capability used during compilation, e.g. sm_60. Multiple
# architectures can be requested as a comma-separated list, e.g. sm_70,sm_80.
# A single value may also be one of the nvcc special values "all",
# "all-major", or "native".
CUDA_ARCH = sm_60
# Base CUDA install directory, only needed if building with clang+cuda:
# The default setting is:
@@ -54,11 +61,23 @@ CUDA_ARCH = sm_60
# 3. Use /usr/local/cuda
CUDA_DIR = $(or $(CUDA_HOME),$(patsubst %/,%,$(dir \
$(patsubst %/,%,$(dir $(shell command -v nvcc))))),/usr/local/cuda)
# Derive nvcc/clang architecture flags from CUDA_ARCH. A comma-separated list
# expands into one -gencode / --cuda-gpu-arch flag per architecture; otherwise
# use the -arch / --cuda-gpu-arch shorthand.
MFEM_COMMA := ,
CUDA_ARCH_NUMS = $(patsubst sm_%,%,$(subst $(MFEM_COMMA), ,$(CUDA_ARCH)))
NVCC_ARCH_FLAGS = $(strip $(if $(findstring $(MFEM_COMMA),$(CUDA_ARCH)),\
$(foreach arch,$(CUDA_ARCH_NUMS),\
-gencode arch=compute_$(arch)$(MFEM_COMMA)code=sm_$(arch)),\
-arch=$(CUDA_ARCH)))
CLANG_ARCH_FLAGS = $(strip $(if $(findstring $(MFEM_COMMA),$(CUDA_ARCH)),\
$(foreach arch,$(CUDA_ARCH_NUMS),--cuda-gpu-arch=sm_$(arch)),\
--cuda-gpu-arch=$(CUDA_ARCH)))
# flags for clang+cuda
CLANG_CUDA_FLAGS = -xcuda --cuda-path=$(CUDA_DIR) --cuda-gpu-arch=$(CUDA_ARCH)
CLANG_CUDA_FLAGS = -xcuda --cuda-path=$(CUDA_DIR) $(CLANG_ARCH_FLAGS)
# flags for nvcc
NVCC_FLAGS = -x=cu --expt-extended-lambda --expt-relaxed-constexpr \
-arch=$(CUDA_ARCH) -isystem "$(CUDA_DIR)/include"
$(NVCC_ARCH_FLAGS) -isystem "$(CUDA_DIR)/include"
# Prefixes for passing flags to the host compiler and linker when using
# CUDA_CXX=nvcc
CUDA_XCOMPILER = -Xcompiler=
@@ -376,7 +395,7 @@ CUDSS_LIBRARY_DIR = $(CUDSS_DIR)/lib
CUDSS_OPT = -I$(CUDSS_INCLUDE_DIR)
CUDSS_LIB = \
$(XLINKER)-rpath,$(CUDSS_LIBRARY_DIR) -L$(CUDSS_LIBRARY_DIR) -lcudss
# The cuDSS communication and threading libraries.
# The cuDSS communication and threading libraries.
MFEM_CUDSS_COMM_LIB = $(abspath $(wildcard $(or $(CUDSS_COMM_LIB),\
$(subst @MFEM_DIR@,$(MFEM_DIR), $(CUDSS_LIBRARY_DIR)/libcudss_commlayer_openmpi.so))))
MFEM_CUDSS_THREADING_LIB = $(abspath $(wildcard $(or $(CUDSS_THREADING_LIB),\
@@ -659,3 +678,15 @@ VERBOSE = NO
# Optional build tag
MFEM_BUILD_TAG = $(shell uname -snm)
# Enable -pedantic flag only for gcc or clang. nvcc complains with -pedantic
# because of line directives.
PEDANTIC_FLAG = $(if \
$(findstring NVIDIA,$(shell $(MFEM_CXX) --version 2>&1)),, \
$(if $(or \
$(findstring gcc version,$(shell $(MFEM_CXX) -v 2>&1)), \
$(findstring clang version,$(shell $(MFEM_CXX) -v 2>&1))),-pedantic,))
# Enable shadow warnings for clang only; GCC's -Wshadow flags more.
SHADOW_WARNING_FLAG = $(if $(findstring clang,\
$(shell $(MFEM_HOST_CXX) --version 2>/dev/null)),-Wshadow,)
WARNING_FLAGS = $(PEDANTIC_FLAG) -Wall $(SHADOW_WARNING_FLAG)
+5
View File
@@ -39,3 +39,8 @@ when a picture was added for documentation.
If that is the case, make sure the failure is indeed justified, and rerun the
push command with the `--no-verify` option. This will skip the hooks, allowing
you to push those changes.
The `branch-history` check is run automatically through GitHub Actions. If a
branch is known to have a large number of changes that are legitimate, the
check can be overridden by setting the label 'branch-history-override' on the
pull request.
+131
View File
@@ -0,0 +1,131 @@
// Define the cube sizes
L_outer = 1.0;
L_inner = 0.5;
// Set mesh size and algorithm
mesh_size = 0.4;
Mesh.Algorithm3D = 1; // Delaunay algorithm for 3D mesh
Mesh.CharacteristicLengthFactor = 1.0;
Mesh.MshFileVersion = 2.2;
// Define center point for concentric cubes
cx = 0.5;
cy = 0.5;
cz = 0.5;
// Define the points (vertices of the outer cube)
Point(1) = {cx-L_outer/2, cy-L_outer/2, cz-L_outer/2, mesh_size};
Point(2) = {cx+L_outer/2, cy-L_outer/2, cz-L_outer/2, mesh_size};
Point(3) = {cx+L_outer/2, cy+L_outer/2, cz-L_outer/2, mesh_size};
Point(4) = {cx-L_outer/2, cy+L_outer/2, cz-L_outer/2, mesh_size};
Point(5) = {cx-L_outer/2, cy-L_outer/2, cz+L_outer/2, mesh_size};
Point(6) = {cx+L_outer/2, cy-L_outer/2, cz+L_outer/2, mesh_size};
Point(7) = {cx+L_outer/2, cy+L_outer/2, cz+L_outer/2, mesh_size};
Point(8) = {cx-L_outer/2, cy+L_outer/2, cz+L_outer/2, mesh_size};
// Define the points (vertices of the inner cube)
Point(9) = {cx-L_inner/2, cy-L_inner/2, cz-L_inner/2, mesh_size};
Point(10) = {cx+L_inner/2, cy-L_inner/2, cz-L_inner/2, mesh_size};
Point(11) = {cx+L_inner/2, cy+L_inner/2, cz-L_inner/2, mesh_size};
Point(12) = {cx-L_inner/2, cy+L_inner/2, cz-L_inner/2, mesh_size};
Point(13) = {cx-L_inner/2, cy-L_inner/2, cz+L_inner/2, mesh_size};
Point(14) = {cx+L_inner/2, cy-L_inner/2, cz+L_inner/2, mesh_size};
Point(15) = {cx+L_inner/2, cy+L_inner/2, cz+L_inner/2, mesh_size};
Point(16) = {cx-L_inner/2, cy+L_inner/2, cz+L_inner/2, mesh_size};
// Define the lines (edges of the outer cube)
Line(1) = {1, 2};
Line(2) = {2, 3};
Line(3) = {3, 4};
Line(4) = {4, 1};
Line(5) = {5, 6};
Line(6) = {6, 7};
Line(7) = {7, 8};
Line(8) = {8, 5};
Line(9) = {1, 5};
Line(10) = {2, 6};
Line(11) = {3, 7};
Line(12) = {4, 8};
// Define the lines (edges of the inner cube)
Line(13) = {9, 10};
Line(14) = {10, 11};
Line(15) = {11, 12};
Line(16) = {12, 9};
Line(17) = {13, 14};
Line(18) = {14, 15};
Line(19) = {15, 16};
Line(20) = {16, 13};
Line(21) = {9, 13};
Line(22) = {10, 14};
Line(23) = {11, 15};
Line(24) = {12, 16};
// Define the surfaces (faces of the outer cube)
Line Loop(1) = {1, 2, 3, 4};
Plane Surface(1) = {1};
Line Loop(2) = {5, 6, 7, 8};
Plane Surface(2) = {2};
Line Loop(3) = {9, 5, -10, -1};
Plane Surface(3) = {3};
Line Loop(4) = {10, 6, -11, -2};
Plane Surface(4) = {4};
Line Loop(5) = {11, 7, -12, -3};
Plane Surface(5) = {5};
Line Loop(6) = {12, 8, -9, -4};
Plane Surface(6) = {6};
// Define the surfaces (faces of the inner cube)
Line Loop(7) = {13, 14, 15, 16};
Plane Surface(7) = {7};
Line Loop(8) = {17, 18, 19, 20};
Plane Surface(8) = {8};
Line Loop(9) = {21, 17, -22, -13};
Plane Surface(9) = {9};
Line Loop(10) = {22, 18, -23, -14};
Plane Surface(10) = {10};
Line Loop(11) = {23, 19, -24, -15};
Plane Surface(11) = {11};
Line Loop(12) = {24, 20, -21, -16};
Plane Surface(12) = {12};
// Define the volumes
Surface Loop(1) = {1, 2, 3, 4, 5, 6};
Surface Loop(2) = {7, 8, 9, 10, 11, 12};
Volume(1) = {1, 2}; // Outer volume with inner hole
Volume(2) = {2}; // Inner volume
// Assign physical groups
Physical Volume(1) = {1}; // Outer volume
Physical Volume(2) = {2}; // Inner volume
// Outer cube surfaces
Physical Surface(1) = {1}; // Outer bottom
Physical Surface(2) = {2}; // Outer top
Physical Surface(3) = {3}; // Outer front
Physical Surface(4) = {4}; // Outer right
Physical Surface(5) = {5}; // Outer back
Physical Surface(6) = {6}; // Outer left
// Inner cube surfaces
Physical Surface(7) = {7}; // Inner bottom (-xy)
Physical Surface(8) = {8}; // Inner top (+xy)
Physical Surface(9) = {9}; // Inner front (-xz)
Physical Surface(10) = {10}; // Inner right (+yz)
Physical Surface(11) = {11}; // Inner back (+xz)
Physical Surface(12) = {12}; // Inner left (-yz)
// Mesh control
Mesh.OptimizeNetgen = 1;
Mesh.Optimize = 1;
Mesh.ElementOrder = 1;
+907
View File
@@ -0,0 +1,907 @@
$MeshFormat
2.2 0 8
$EndMeshFormat
$Nodes
138
1 0 0 0
2 1 0 0
3 1 1 0
4 0 1 0
5 0 0 1
6 1 0 1
7 1 1 1
8 0 1 1
9 0.25 0.25 0.25
10 0.75 0.25 0.25
11 0.75 0.75 0.25
12 0.25 0.75 0.25
13 0.25 0.25 0.75
14 0.75 0.25 0.75
15 0.75 0.75 0.75
16 0.25 0.75 0.75
17 0.3333333333325025 0 0
18 0.6666666666657889 0 0
19 1 0.3333333333325025 0
20 1 0.6666666666657889 0
21 0.6666666666675911 1 0
22 0.3333333333347203 1 0
23 0 0.6666666666675911 0
24 0 0.3333333333347203 0
25 0.3333333333325025 0 1
26 0.6666666666657889 0 1
27 1 0.3333333333325025 1
28 1 0.6666666666657889 1
29 0.6666666666675911 1 1
30 0.3333333333347203 1 1
31 0 0.6666666666675911 1
32 0 0.3333333333347203 1
33 0 0 0.3333333333325025
34 0 0 0.6666666666657889
35 1 0 0.3333333333325025
36 1 0 0.6666666666657889
37 1 1 0.3333333333325025
38 1 1 0.6666666666657889
39 0 1 0.3333333333325025
40 0 1 0.6666666666657889
41 0.5000000000003468 0.25 0.25
42 0.75 0.5000000000003468 0.25
43 0.5000000000013763 0.75 0.25
44 0.25 0.5000000000013763 0.25
45 0.5000000000003468 0.25 0.75
46 0.75 0.5000000000003468 0.75
47 0.5000000000013763 0.75 0.75
48 0.25 0.5000000000013763 0.75
49 0.25 0.25 0.5000000000003468
50 0.75 0.25 0.5000000000003468
51 0.75 0.75 0.5000000000003468
52 0.25 0.75 0.5000000000003468
53 0.7113248654055673 0.4999999999991457 0
54 0.2886751345942123 0.5000000000011557 0
55 0.5000000000006117 0.7525600817161773 0
56 0.4999999999993867 0.2474399182839603 0
57 0.2423197548524782 0.7576802451481532 0
58 0.757680245147464 0.2423197548520695 0
59 0.2423197548507857 0.2423197548513912 0
60 0.7576802451491019 0.7576802451486099 0
61 0.7113248654055673 0.4999999999991457 1
62 0.2886751345942123 0.5000000000011557 1
63 0.5000000000006117 0.7525600817161773 1
64 0.4999999999993867 0.2474399182839603 1
65 0.2423197548524782 0.7576802451481532 1
66 0.757680245147464 0.2423197548520695 1
67 0.2423197548507857 0.2423197548513912 1
68 0.7576802451491019 0.7576802451486099 1
69 0.4999999999993203 0 0.301447615129799
70 0.4999999999992795 0 0.7028666213189801
71 0.7525600817158393 0 0.5007190394076877
72 0.2474399182836191 0 0.5007190394076877
73 0.7576802451479793 0 0.7576802451479793
74 0.2423197548517962 0 0.7576802451477375
75 0.7576802451484569 0 0.2423197548510767
76 0.2423197548513188 0 0.2423197548513187
77 1 0.4999999999993203 0.301447615129799
78 1 0.4999999999992795 0.7028666213189801
79 1 0.7525600817158394 0.5007190394076877
80 1 0.2474399182836191 0.5007190394076877
81 1 0.7576802451479794 0.7576802451479794
82 1 0.2423197548517962 0.7576802451477376
83 1 0.7576802451484569 0.2423197548510768
84 1 0.2423197548513188 0.2423197548513188
85 0.5000000000008327 1 0.3014476151298047
86 0.500000000000961 1 0.7028666213191928
87 0.2474399182842484 1 0.5007190394077241
88 0.7525600817164384 1 0.5007190394078933
89 0.2423197548520873 1 0.7576802451480517
90 0.7576802451481496 1 0.2423197548518761
91 0.2423197548516099 1 0.2423197548510044
92 0.7576802451486874 1 0.7576802451481952
93 0 0.5000000000008327 0.3014476151298047
94 0 0.500000000000961 0.7028666213191928
95 0 0.2474399182842484 0.5007190394077241
96 0 0.7525600817164384 0.5007190394078933
97 0 0.2423197548520873 0.7576802451480517
98 0 0.7576802451481496 0.2423197548518761
99 0 0.2423197548516099 0.2423197548510044
100 0 0.7576802451486874 0.7576802451481952
101 0.3968750000003409 0.603125000000244 0.25
102 0.4374999999998713 0.4375000000001287 0.25
103 0.5739583333335919 0.5718750000001767 0.25
104 0.6093749999999631 0.3906250000003402 0.25
105 0.3968750000003409 0.603125000000244 0.75
106 0.4374999999998713 0.4375000000001287 0.75
107 0.5739583333335919 0.5718750000001767 0.75
108 0.6093749999999631 0.3906250000003402 0.75
109 0.3806942419826734 0.25 0.3806942419826734
110 0.5625000000001735 0.25 0.4375000000000001
111 0.4254282069971791 0.25 0.5712615403304835
112 0.6093749999998808 0.25 0.6093749999998808
113 0.75 0.3806942419826734 0.3806942419826734
114 0.75 0.5625000000001735 0.4375000000000001
115 0.75 0.4254282069971791 0.5712615403304835
116 0.75 0.6093749999998808 0.6093749999998808
117 0.3968750000004991 0.75 0.3968750000002804
118 0.4375000000000001 0.75 0.5625000000004308
119 0.5739583333336153 0.75 0.4281250000000707
120 0.6093749999998166 0.75 0.6093749999993661
121 0.25 0.3968750000004991 0.3968750000002804
122 0.25 0.4375000000000001 0.5625000000004308
123 0.25 0.5739583333336153 0.4281250000000707
124 0.25 0.6093749999998166 0.6093749999993661
125 0.4962939304035875 0.5214350017087855 0.4925553109323813
126 0.3432581985549767 0.6471275530923523 0.3554206606168006
127 0.6442168181713744 0.5929232373214773 0.3593785632300704
128 0.625174517421737 0.3491579444372839 0.3604636129462503
129 0.6130544111091688 0.6576364353993367 0.5046720648819553
130 0.4281518698369243 0.3632662294430484 0.3548726263205102
131 0.3639383531355198 0.3520949250221278 0.4978389662613994
132 0.629585530087249 0.3489878162230438 0.5124654846339122
133 0.3710853378652663 0.6517586292698121 0.6382643241302075
134 0.5917018263727056 0.6522525456211955 0.6390764961119443
135 0.3530810314228338 0.4582477062424107 0.6430239639637545
136 0.6571010904289998 0.5300774811423468 0.6603970500567977
137 0.6484596018596915 0.361399676127967 0.6360267588157177
138 0.4782020887035478 0.3534611476388013 0.6141275027013793
$EndNodes
$Elements
760
1 2 2 1 1 1 17 59
2 2 2 1 1 24 1 59
3 2 2 1 1 18 2 58
4 2 2 1 1 2 19 58
5 2 2 1 1 20 3 60
6 2 2 1 1 3 21 60
7 2 2 1 1 22 4 57
8 2 2 1 1 4 23 57
9 2 2 1 1 17 18 56
10 2 2 1 1 17 56 59
11 2 2 1 1 56 18 58
12 2 2 1 1 19 20 53
13 2 2 1 1 19 53 58
14 2 2 1 1 53 20 60
15 2 2 1 1 21 22 55
16 2 2 1 1 21 55 60
17 2 2 1 1 55 22 57
18 2 2 1 1 23 24 54
19 2 2 1 1 23 54 57
20 2 2 1 1 54 24 59
21 2 2 1 1 54 53 55
22 2 2 1 1 53 54 56
23 2 2 1 1 55 53 60
24 2 2 1 1 53 56 58
25 2 2 1 1 54 55 57
26 2 2 1 1 56 54 59
27 2 2 2 2 5 25 67
28 2 2 2 2 32 5 67
29 2 2 2 2 26 6 66
30 2 2 2 2 6 27 66
31 2 2 2 2 28 7 68
32 2 2 2 2 7 29 68
33 2 2 2 2 30 8 65
34 2 2 2 2 8 31 65
35 2 2 2 2 25 26 64
36 2 2 2 2 25 64 67
37 2 2 2 2 64 26 66
38 2 2 2 2 27 28 61
39 2 2 2 2 27 61 66
40 2 2 2 2 61 28 68
41 2 2 2 2 29 30 63
42 2 2 2 2 29 63 68
43 2 2 2 2 63 30 65
44 2 2 2 2 31 32 62
45 2 2 2 2 31 62 65
46 2 2 2 2 62 32 67
47 2 2 2 2 62 61 63
48 2 2 2 2 61 62 64
49 2 2 2 2 63 61 68
50 2 2 2 2 61 64 66
51 2 2 2 2 62 63 65
52 2 2 2 2 64 62 67
53 2 2 3 3 17 1 76
54 2 2 3 3 1 33 76
55 2 2 3 3 2 18 75
56 2 2 3 3 35 2 75
57 2 2 3 3 5 25 74
58 2 2 3 3 34 5 74
59 2 2 3 3 26 6 73
60 2 2 3 3 6 36 73
61 2 2 3 3 18 17 69
62 2 2 3 3 69 17 76
63 2 2 3 3 18 69 75
64 2 2 3 3 25 26 70
65 2 2 3 3 25 70 74
66 2 2 3 3 70 26 73
67 2 2 3 3 33 34 72
68 2 2 3 3 33 72 76
69 2 2 3 3 72 34 74
70 2 2 3 3 36 35 71
71 2 2 3 3 71 35 75
72 2 2 3 3 36 71 73
73 2 2 3 3 69 70 71
74 2 2 3 3 70 69 72
75 2 2 3 3 69 71 75
76 2 2 3 3 72 69 76
77 2 2 3 3 71 70 73
78 2 2 3 3 70 72 74
79 2 2 4 4 19 2 84
80 2 2 4 4 2 35 84
81 2 2 4 4 3 20 83
82 2 2 4 4 37 3 83
83 2 2 4 4 6 27 82
84 2 2 4 4 36 6 82
85 2 2 4 4 28 7 81
86 2 2 4 4 7 38 81
87 2 2 4 4 20 19 77
88 2 2 4 4 77 19 84
89 2 2 4 4 20 77 83
90 2 2 4 4 27 28 78
91 2 2 4 4 27 78 82
92 2 2 4 4 78 28 81
93 2 2 4 4 35 36 80
94 2 2 4 4 35 80 84
95 2 2 4 4 80 36 82
96 2 2 4 4 38 37 79
97 2 2 4 4 79 37 83
98 2 2 4 4 38 79 81
99 2 2 4 4 77 78 79
100 2 2 4 4 78 77 80
101 2 2 4 4 77 79 83
102 2 2 4 4 80 77 84
103 2 2 4 4 79 78 81
104 2 2 4 4 78 80 82
105 2 2 5 5 21 3 90
106 2 2 5 5 3 37 90
107 2 2 5 5 4 22 91
108 2 2 5 5 39 4 91
109 2 2 5 5 7 29 92
110 2 2 5 5 38 7 92
111 2 2 5 5 30 8 89
112 2 2 5 5 8 40 89
113 2 2 5 5 22 21 85
114 2 2 5 5 85 21 90
115 2 2 5 5 22 85 91
116 2 2 5 5 29 30 86
117 2 2 5 5 29 86 92
118 2 2 5 5 86 30 89
119 2 2 5 5 37 38 88
120 2 2 5 5 37 88 90
121 2 2 5 5 88 38 92
122 2 2 5 5 40 39 87
123 2 2 5 5 87 39 91
124 2 2 5 5 40 87 89
125 2 2 5 5 85 86 87
126 2 2 5 5 86 85 88
127 2 2 5 5 85 87 91
128 2 2 5 5 88 85 90
129 2 2 5 5 87 86 89
130 2 2 5 5 86 88 92
131 2 2 6 6 1 24 99
132 2 2 6 6 33 1 99
133 2 2 6 6 23 4 98
134 2 2 6 6 4 39 98
135 2 2 6 6 32 5 97
136 2 2 6 6 5 34 97
137 2 2 6 6 8 31 100
138 2 2 6 6 40 8 100
139 2 2 6 6 24 23 93
140 2 2 6 6 93 23 98
141 2 2 6 6 24 93 99
142 2 2 6 6 31 32 94
143 2 2 6 6 31 94 100
144 2 2 6 6 94 32 97
145 2 2 6 6 34 33 95
146 2 2 6 6 95 33 99
147 2 2 6 6 34 95 97
148 2 2 6 6 39 40 96
149 2 2 6 6 39 96 98
150 2 2 6 6 96 40 100
151 2 2 6 6 93 94 95
152 2 2 6 6 94 93 96
153 2 2 6 6 93 95 99
154 2 2 6 6 96 93 98
155 2 2 6 6 95 94 97
156 2 2 6 6 94 96 100
157 2 2 7 7 9 41 102
158 2 2 7 7 44 9 102
159 2 2 7 7 41 10 104
160 2 2 7 7 10 42 104
161 2 2 7 7 42 11 103
162 2 2 7 7 11 43 103
163 2 2 7 7 43 12 101
164 2 2 7 7 12 44 101
165 2 2 7 7 102 41 104
166 2 2 7 7 42 103 104
167 2 2 7 7 43 101 103
168 2 2 7 7 101 44 102
169 2 2 7 7 101 102 103
170 2 2 7 7 103 102 104
171 2 2 8 8 13 45 106
172 2 2 8 8 48 13 106
173 2 2 8 8 45 14 108
174 2 2 8 8 14 46 108
175 2 2 8 8 46 15 107
176 2 2 8 8 15 47 107
177 2 2 8 8 47 16 105
178 2 2 8 8 16 48 105
179 2 2 8 8 106 45 108
180 2 2 8 8 46 107 108
181 2 2 8 8 47 105 107
182 2 2 8 8 105 48 106
183 2 2 8 8 105 106 107
184 2 2 8 8 107 106 108
185 2 2 9 9 41 9 109
186 2 2 9 9 9 49 109
187 2 2 9 9 10 41 110
188 2 2 9 9 50 10 110
189 2 2 9 9 13 45 111
190 2 2 9 9 49 13 111
191 2 2 9 9 45 14 112
192 2 2 9 9 14 50 112
193 2 2 9 9 41 109 110
194 2 2 9 9 111 45 112
195 2 2 9 9 109 49 111
196 2 2 9 9 50 110 112
197 2 2 9 9 110 109 111
198 2 2 9 9 110 111 112
199 2 2 10 10 42 10 113
200 2 2 10 10 10 50 113
201 2 2 10 10 11 42 114
202 2 2 10 10 51 11 114
203 2 2 10 10 14 46 115
204 2 2 10 10 50 14 115
205 2 2 10 10 46 15 116
206 2 2 10 10 15 51 116
207 2 2 10 10 42 113 114
208 2 2 10 10 115 46 116
209 2 2 10 10 113 50 115
210 2 2 10 10 51 114 116
211 2 2 10 10 114 113 115
212 2 2 10 10 114 115 116
213 2 2 11 11 43 11 119
214 2 2 11 11 11 51 119
215 2 2 11 11 12 43 117
216 2 2 11 11 52 12 117
217 2 2 11 11 15 47 120
218 2 2 11 11 51 15 120
219 2 2 11 11 47 16 118
220 2 2 11 11 16 52 118
221 2 2 11 11 117 43 119
222 2 2 11 11 47 118 120
223 2 2 11 11 119 51 120
224 2 2 11 11 52 117 118
225 2 2 11 11 118 117 119
226 2 2 11 11 118 119 120
227 2 2 12 12 9 44 121
228 2 2 12 12 49 9 121
229 2 2 12 12 44 12 123
230 2 2 12 12 12 52 123
231 2 2 12 12 48 13 122
232 2 2 12 12 13 49 122
233 2 2 12 12 16 48 124
234 2 2 12 12 52 16 124
235 2 2 12 12 121 44 123
236 2 2 12 12 48 122 124
237 2 2 12 12 49 121 122
238 2 2 12 12 123 52 124
239 2 2 12 12 122 121 123
240 2 2 12 12 122 123 124
241 4 2 1 1 105 62 106 107
242 4 2 1 1 102 54 101 103
243 4 2 1 1 118 86 120 119
244 4 2 1 1 124 94 123 122
245 4 2 1 1 52 39 12 96
246 4 2 1 1 52 12 39 87
247 4 2 1 1 88 38 15 51
248 4 2 1 1 79 15 38 51
249 4 2 1 1 80 14 50 36
250 4 2 1 1 71 50 14 36
251 4 2 1 1 120 88 15 51
252 4 2 1 1 116 15 79 51
253 4 2 1 1 50 14 112 71
254 4 2 1 1 52 117 12 87
255 4 2 1 1 111 69 109 110
256 4 2 1 1 114 77 115 113
257 4 2 1 1 124 123 94 96
258 4 2 1 1 120 86 88 119
259 4 2 1 1 14 45 64 108
260 4 2 1 1 103 54 101 55
261 4 2 1 1 62 105 63 107
262 4 2 1 1 63 29 15 47
263 4 2 1 1 14 45 26 64
264 4 2 1 1 51 11 88 37
265 4 2 1 1 80 50 10 35
266 4 2 1 1 49 33 9 72
267 4 2 1 1 71 10 50 35
268 4 2 1 1 79 11 51 37
269 4 2 1 1 49 9 33 95
270 4 2 1 1 12 123 52 96
271 4 2 1 1 96 16 52 40
272 4 2 1 1 49 13 34 72
273 4 2 1 1 87 52 16 40
274 4 2 1 1 13 49 34 95
275 4 2 1 1 43 101 12 55
276 4 2 1 1 43 12 22 55
277 4 2 1 1 106 61 108 107
278 4 2 1 1 102 103 104 53
279 4 2 1 1 50 115 14 80
280 4 2 1 1 80 10 50 113
281 4 2 1 1 9 109 49 72
282 4 2 1 1 47 15 63 107
283 4 2 1 1 124 16 52 96
284 4 2 1 1 49 121 9 95
285 4 2 1 1 15 28 68 46
286 4 2 1 1 81 28 15 46
287 4 2 1 1 92 15 29 47
288 4 2 1 1 27 82 14 46
289 4 2 1 1 14 66 27 46
290 4 2 1 1 26 45 14 73
291 4 2 1 1 78 115 116 114
292 4 2 1 1 112 70 111 110
293 4 2 1 1 79 11 114 51
294 4 2 1 1 71 50 10 110
295 4 2 1 1 10 41 104 56
296 4 2 1 1 77 80 115 113
297 4 2 1 1 69 109 72 111
298 4 2 1 1 63 16 30 47
299 4 2 1 1 25 45 13 64
300 4 2 1 1 52 16 118 87
301 4 2 1 1 95 13 49 122
302 4 2 1 1 57 12 23 44
303 4 2 1 1 119 11 88 51
304 4 2 1 1 41 17 9 56
305 4 2 1 1 18 41 10 56
306 4 2 1 1 21 11 43 55
307 4 2 1 1 63 105 16 47
308 4 2 1 1 13 45 106 64
309 4 2 1 1 9 102 41 56
310 4 2 1 1 72 49 13 111
311 4 2 1 1 11 103 43 55
312 4 2 1 1 70 112 71 110
313 4 2 1 1 116 79 78 114
314 4 2 1 1 32 13 48 67
315 4 2 1 1 16 89 30 47
316 4 2 1 1 32 48 13 97
317 4 2 1 1 48 16 100 31
318 4 2 1 1 121 123 93 122
319 4 2 1 1 119 117 118 85
320 4 2 1 1 10 19 42 84
321 4 2 1 1 17 9 76 41
322 4 2 1 1 24 9 59 44
323 4 2 1 1 42 19 10 58
324 4 2 1 1 10 41 18 75
325 4 2 1 1 20 83 11 42
326 4 2 1 1 90 11 43 21
327 4 2 1 1 53 104 102 56
328 4 2 1 1 106 61 64 108
329 4 2 1 1 87 118 117 85
330 4 2 1 1 95 121 93 122
331 4 2 1 1 80 50 115 113
332 4 2 1 1 109 49 72 111
333 4 2 1 1 78 27 28 46
334 4 2 1 1 31 94 48 32
335 4 2 1 1 19 77 20 42
336 4 2 1 1 23 24 44 93
337 4 2 1 1 123 124 52 96
338 4 2 1 1 88 120 119 51
339 4 2 1 1 91 43 85 117
340 4 2 1 1 39 98 12 96
341 4 2 1 1 12 91 39 87
342 4 2 1 1 15 92 38 88
343 4 2 1 1 38 81 15 79
344 4 2 1 1 36 80 14 82
345 4 2 1 1 14 71 36 73
346 4 2 1 1 103 102 54 53
347 4 2 1 1 61 106 62 107
348 4 2 1 1 86 29 30 47
349 4 2 1 1 26 45 70 25
350 4 2 1 1 21 85 22 43
351 4 2 1 1 94 93 123 122
352 4 2 1 1 119 118 86 85
353 4 2 1 1 12 96 93 123
354 4 2 1 1 115 78 77 114
355 4 2 1 1 69 111 70 110
356 4 2 1 1 28 27 61 46
357 4 2 1 1 48 62 31 32
358 4 2 1 1 88 15 92 120
359 4 2 1 1 79 81 15 116
360 4 2 1 1 71 14 112 73
361 4 2 1 1 114 116 79 51
362 4 2 1 1 112 50 71 110
363 4 2 1 1 91 12 117 87
364 4 2 1 1 80 115 14 82
365 4 2 1 1 63 15 29 68
366 4 2 1 1 64 26 14 66
367 4 2 1 1 47 63 105 107
368 4 2 1 1 43 103 101 55
369 4 2 1 1 35 10 80 84
370 4 2 1 1 33 76 9 72
371 4 2 1 1 37 11 88 90
372 4 2 1 1 35 71 10 75
373 4 2 1 1 37 79 11 83
374 4 2 1 1 9 99 33 95
375 4 2 1 1 68 63 15 107
376 4 2 1 1 99 44 93 121
377 4 2 1 1 18 69 41 17
378 4 2 1 1 10 35 2 84
379 4 2 1 1 15 7 28 81
380 4 2 1 1 3 37 90 11
381 4 2 1 1 108 64 14 66
382 4 2 1 1 51 38 79 37
383 4 2 1 1 12 4 23 98
384 4 2 1 1 12 39 91 4
385 4 2 1 1 49 33 72 34
386 4 2 1 1 10 71 110 75
387 4 2 1 1 114 11 79 83
388 4 2 1 1 40 96 16 100
389 4 2 1 1 34 13 74 72
390 4 2 1 1 16 87 40 89
391 4 2 1 1 34 97 13 95
392 4 2 1 1 20 19 42 53
393 4 2 1 1 54 23 24 44
394 4 2 1 1 87 16 118 89
395 4 2 1 1 95 97 13 122
396 4 2 1 1 57 22 12 55
397 4 2 1 1 41 102 104 56
398 4 2 1 1 64 45 106 108
399 4 2 1 1 101 57 12 55
400 4 2 1 1 74 13 5 25
401 4 2 1 1 13 34 74 5
402 4 2 1 1 8 40 16 100
403 4 2 1 1 16 31 65 8
404 4 2 1 1 82 36 6 14
405 4 2 1 1 102 9 59 56
406 4 2 1 1 106 67 13 64
407 4 2 1 1 119 88 11 90
408 4 2 1 1 95 49 121 122
409 4 2 1 1 52 118 117 87
410 4 2 1 1 124 16 96 100
411 4 2 1 1 80 10 113 84
412 4 2 1 1 109 9 76 72
413 4 2 1 1 72 13 70 111
414 4 2 1 1 99 9 121 95
415 4 2 1 1 30 65 16 63
416 4 2 1 1 13 67 25 64
417 4 2 1 1 11 55 53 103
418 4 2 1 1 123 12 44 93
419 4 2 1 1 56 104 10 58
420 4 2 1 1 10 18 56 58
421 4 2 1 1 21 60 11 55
422 4 2 1 1 17 59 9 56
423 4 2 1 1 16 63 62 105
424 4 2 1 1 48 105 16 62
425 4 2 1 1 111 45 13 70
426 4 2 1 1 119 86 88 85
427 4 2 1 1 123 93 94 96
428 4 2 1 1 115 78 80 77
429 4 2 1 1 111 72 69 70
430 4 2 1 1 55 103 54 53
431 4 2 1 1 63 61 62 107
432 4 2 1 1 14 27 66 6
433 4 2 1 1 99 9 1 24
434 4 2 1 1 49 33 34 95
435 4 2 1 1 96 52 39 40
436 4 2 1 1 22 12 91 4
437 4 2 1 1 20 11 83 3
438 4 2 1 1 29 15 92 7
439 4 2 1 1 18 10 75 2
440 4 2 1 1 36 50 80 35
441 4 2 1 1 18 17 41 56
442 4 2 1 1 51 88 38 37
443 4 2 1 1 62 31 16 48
444 4 2 1 1 62 16 31 65
445 4 2 1 1 26 45 25 64
446 4 2 1 1 104 58 42 10
447 4 2 1 1 29 63 30 47
448 4 2 1 1 77 78 79 114
449 4 2 1 1 71 69 70 110
450 4 2 1 1 42 103 11 53
451 4 2 1 1 93 94 95 122
452 4 2 1 1 118 87 86 85
453 4 2 1 1 61 62 106 64
454 4 2 1 1 53 102 54 56
455 4 2 1 1 78 82 27 46
456 4 2 1 1 94 48 32 97
457 4 2 1 1 86 30 89 47
458 4 2 1 1 78 28 81 46
459 4 2 1 1 26 70 45 73
460 4 2 1 1 100 94 48 31
461 4 2 1 1 86 92 29 47
462 4 2 1 1 76 69 17 41
463 4 2 1 1 42 19 77 84
464 4 2 1 1 77 83 20 42
465 4 2 1 1 18 41 69 75
466 4 2 1 1 43 85 90 21
467 4 2 1 1 91 43 117 12
468 4 2 1 1 48 62 32 67
469 4 2 1 1 27 66 61 46
470 4 2 1 1 68 28 61 46
471 4 2 1 1 54 24 59 44
472 4 2 1 1 19 42 53 58
473 4 2 1 1 23 54 57 44
474 4 2 1 1 12 93 96 98
475 4 2 1 1 99 44 121 9
476 4 2 1 1 42 58 104 53
477 4 2 1 1 21 43 22 55
478 4 2 1 1 53 20 11 42
479 4 2 1 1 53 11 20 60
480 4 2 1 1 106 67 48 13
481 4 2 1 1 36 71 50 35
482 4 2 1 1 52 87 39 40
483 4 2 1 1 48 67 106 62
484 4 2 1 1 72 70 13 74
485 4 2 1 1 9 33 99 1
486 4 2 1 1 3 37 11 83
487 4 2 1 1 13 34 5 97
488 4 2 1 1 12 39 4 98
489 4 2 1 1 14 36 6 73
490 4 2 1 1 15 38 92 7
491 4 2 1 1 8 40 89 16
492 4 2 1 1 75 35 2 10
493 4 2 1 1 30 16 65 8
494 4 2 1 1 14 27 6 82
495 4 2 1 1 26 14 66 6
496 4 2 1 1 16 31 8 100
497 4 2 1 1 32 13 67 5
498 4 2 1 1 13 67 5 25
499 4 2 1 1 68 7 28 15
500 4 2 1 1 29 15 7 68
501 4 2 1 1 57 4 23 12
502 4 2 1 1 22 12 4 57
503 4 2 1 1 10 19 84 2
504 4 2 1 1 9 59 1 24
505 4 2 1 1 17 9 59 1
506 4 2 1 1 18 10 2 58
507 4 2 1 1 11 21 90 3
508 4 2 1 1 20 11 3 60
509 4 2 1 1 16 62 63 65
510 4 2 1 1 11 53 55 60
511 4 2 1 1 78 81 79 116
512 4 2 1 1 71 112 70 73
513 4 2 1 1 86 88 92 120
514 4 2 1 1 96 94 124 100
515 4 2 1 1 94 97 95 122
516 4 2 1 1 89 118 87 86
517 4 2 1 1 15 38 7 81
518 4 2 1 1 102 44 59 9
519 4 2 1 1 59 44 102 54
520 4 2 1 1 110 71 69 75
521 4 2 1 1 79 77 114 83
522 4 2 1 1 76 69 109 72
523 4 2 1 1 113 77 80 84
524 4 2 1 1 93 99 121 95
525 4 2 1 1 87 117 91 85
526 4 2 1 1 90 119 43 11
527 4 2 1 1 80 78 115 82
528 4 2 1 1 88 119 85 90
529 4 2 1 1 90 43 119 85
530 4 2 1 1 46 108 66 61
531 4 2 1 1 46 66 108 14
532 4 2 1 1 26 14 6 73
533 4 2 1 1 44 101 57 12
534 4 2 1 1 44 57 101 54
535 4 2 1 1 46 107 68 15
536 4 2 1 1 46 68 107 61
537 4 2 1 1 9 33 1 76
538 4 2 1 1 100 124 48 94
539 4 2 1 1 100 48 124 16
540 4 2 1 1 25 70 13 45
541 4 2 1 1 56 53 104 58
542 4 2 1 1 61 64 108 66
543 4 2 1 1 106 62 67 64
544 4 2 1 1 102 59 54 56
545 4 2 1 1 32 13 5 97
546 4 2 1 1 30 16 8 89
547 4 2 1 1 13 70 25 74
548 4 2 1 1 97 122 48 13
549 4 2 1 1 75 110 41 69
550 4 2 1 1 54 57 101 55
551 4 2 1 1 97 48 122 94
552 4 2 1 1 47 118 89 86
553 4 2 1 1 47 89 118 16
554 4 2 1 1 17 9 1 76
555 4 2 1 1 11 21 3 60
556 4 2 1 1 10 19 2 58
557 4 2 1 1 75 41 110 10
558 4 2 1 1 61 63 68 107
559 4 2 1 1 70 111 45 112
560 4 2 1 1 78 115 46 116
561 4 2 1 1 77 114 42 113
562 4 2 1 1 69 41 109 110
563 4 2 1 1 107 61 108 46
564 4 2 1 1 103 42 104 53
565 4 2 1 1 118 86 47 120
566 4 2 1 1 94 124 48 122
567 4 2 1 1 117 119 43 85
568 4 2 1 1 123 44 121 93
569 4 2 1 1 101 54 102 44
570 4 2 1 1 48 105 62 106
571 4 2 1 1 91 43 12 22
572 4 2 1 1 91 43 22 85
573 4 2 1 1 93 12 23 98
574 4 2 1 1 93 23 12 44
575 4 2 1 1 46 81 116 15
576 4 2 1 1 46 116 81 78
577 4 2 1 1 92 47 120 15
578 4 2 1 1 120 47 92 86
579 4 2 1 1 46 82 115 78
580 4 2 1 1 46 115 82 14
581 4 2 1 1 73 45 112 70
582 4 2 1 1 73 112 45 14
583 4 2 1 1 99 44 9 24
584 4 2 1 1 99 44 24 93
585 4 2 1 1 84 42 113 77
586 4 2 1 1 84 113 42 10
587 4 2 1 1 76 41 109 69
588 4 2 1 1 109 41 76 9
589 4 2 1 1 42 83 114 77
590 4 2 1 1 42 114 83 11
591 4 2 2 2 135 13 122 131
592 4 2 2 2 132 138 125 110
593 4 2 2 2 138 108 125 106
594 4 2 2 2 136 108 125 137
595 4 2 2 2 110 138 125 131
596 4 2 2 2 136 107 134 125
597 4 2 2 2 122 135 131 125
598 4 2 2 2 13 49 122 131
599 4 2 2 2 136 108 137 46
600 4 2 2 2 110 138 131 111
601 4 2 2 2 132 138 110 112
602 4 2 2 2 106 107 108 125
603 4 2 2 2 138 112 137 45
604 4 2 2 2 104 127 125 128
605 4 2 2 2 113 115 114 125
606 4 2 2 2 131 122 125 121
607 4 2 2 2 112 45 14 137
608 4 2 2 2 110 131 125 130
609 4 2 2 2 115 116 114 125
610 4 2 2 2 132 138 112 137
611 4 2 2 2 50 137 115 132
612 4 2 2 2 104 127 128 42
613 4 2 2 2 105 134 125 133
614 4 2 2 2 129 120 51 119
615 4 2 2 2 104 130 128 125
616 4 2 2 2 105 134 133 47
617 4 2 2 2 135 106 138 125
618 4 2 2 2 106 105 107 125
619 4 2 2 2 138 108 106 45
620 4 2 2 2 104 130 125 102
621 4 2 2 2 104 127 42 103
622 4 2 2 2 109 131 49 111
623 4 2 2 2 106 135 48 105
624 4 2 2 2 126 101 43 125
625 4 2 2 2 138 137 125 108
626 4 2 2 2 104 127 103 125
627 4 2 2 2 116 114 129 51
628 4 2 2 2 136 108 46 107
629 4 2 2 2 136 108 107 125
630 4 2 2 2 52 124 123 125
631 4 2 2 2 132 112 110 50
632 4 2 2 2 44 121 125 123
633 4 2 2 2 131 122 121 49
634 4 2 2 2 105 134 47 107
635 4 2 2 2 106 13 135 138
636 4 2 2 2 52 133 125 118
637 4 2 2 2 103 43 101 125
638 4 2 2 2 117 43 119 125
639 4 2 2 2 105 134 107 125
640 4 2 2 2 137 115 132 125
641 4 2 2 2 50 137 132 112
642 4 2 2 2 106 135 105 125
643 4 2 2 2 102 101 44 125
644 4 2 2 2 104 130 102 41
645 4 2 2 2 138 45 13 111
646 4 2 2 2 131 138 135 13
647 4 2 2 2 138 112 45 111
648 4 2 2 2 131 138 13 111
649 4 2 2 2 107 134 15 136
650 4 2 2 2 118 52 117 125
651 4 2 2 2 101 126 12 44
652 4 2 2 2 104 130 41 128
653 4 2 2 2 138 137 108 45
654 4 2 2 2 133 118 16 47
655 4 2 2 2 44 101 126 125
656 4 2 2 2 133 134 118 47
657 4 2 2 2 133 134 125 118
658 4 2 2 2 124 122 123 125
659 4 2 2 2 129 118 119 125
660 4 2 2 2 102 104 103 125
661 4 2 2 2 135 48 13 106
662 4 2 2 2 123 122 121 125
663 4 2 2 2 103 127 43 125
664 4 2 2 2 106 45 13 138
665 4 2 2 2 103 11 127 42
666 4 2 2 2 127 51 129 114
667 4 2 2 2 13 49 131 111
668 4 2 2 2 137 108 14 46
669 4 2 2 2 15 47 134 107
670 4 2 2 2 10 41 128 104
671 4 2 2 2 16 118 133 52
672 4 2 2 2 46 107 15 136
673 4 2 2 2 41 102 9 130
674 4 2 2 2 128 50 132 110
675 4 2 2 2 10 41 110 128
676 4 2 2 2 50 137 112 14
677 4 2 2 2 130 102 9 44
678 4 2 2 2 105 133 16 47
679 4 2 2 2 127 11 103 43
680 4 2 2 2 128 130 41 110
681 4 2 2 2 116 134 15 51
682 4 2 2 2 137 45 14 108
683 4 2 2 2 12 126 101 43
684 4 2 2 2 133 48 135 105
685 4 2 2 2 128 42 10 104
686 4 2 2 2 131 9 109 49
687 4 2 2 2 118 117 119 125
688 4 2 2 2 102 103 101 125
689 4 2 2 2 129 118 125 134
690 4 2 2 2 125 129 116 114
691 4 2 2 2 117 126 43 125
692 4 2 2 2 126 117 12 52
693 4 2 2 2 52 126 117 125
694 4 2 2 2 12 117 126 43
695 4 2 2 2 127 51 114 11
696 4 2 2 2 114 129 127 125
697 4 2 2 2 127 119 43 125
698 4 2 2 2 119 11 127 43
699 4 2 2 2 127 113 114 125
700 4 2 2 2 127 113 42 114
701 4 2 2 2 128 130 110 125
702 4 2 2 2 15 47 120 134
703 4 2 2 2 127 11 114 42
704 4 2 2 2 120 47 118 134
705 4 2 2 2 44 130 102 125
706 4 2 2 2 44 126 12 123
707 4 2 2 2 123 44 126 125
708 4 2 2 2 129 118 134 120
709 4 2 2 2 110 132 128 125
710 4 2 2 2 128 127 125 113
711 4 2 2 2 128 127 113 42
712 4 2 2 2 128 50 110 10
713 4 2 2 2 113 42 10 128
714 4 2 2 2 129 134 125 116
715 4 2 2 2 127 51 11 119
716 4 2 2 2 129 134 116 51
717 4 2 2 2 127 51 119 129
718 4 2 2 2 129 119 127 125
719 4 2 2 2 110 131 130 109
720 4 2 2 2 109 130 9 131
721 4 2 2 2 121 130 9 44
722 4 2 2 2 122 48 13 135
723 4 2 2 2 44 121 130 125
724 4 2 2 2 110 138 111 112
725 4 2 2 2 131 138 125 135
726 4 2 2 2 115 137 14 46
727 4 2 2 2 126 52 12 123
728 4 2 2 2 123 126 52 125
729 4 2 2 2 50 137 14 115
730 4 2 2 2 136 137 115 46
731 4 2 2 2 121 9 131 49
732 4 2 2 2 121 131 130 125
733 4 2 2 2 131 130 9 121
734 4 2 2 2 113 132 115 125
735 4 2 2 2 113 50 115 132
736 4 2 2 2 128 50 10 113
737 4 2 2 2 132 113 128 125
738 4 2 2 2 128 50 113 132
739 4 2 2 2 133 105 135 125
740 4 2 2 2 52 124 125 133
741 4 2 2 2 133 48 105 16
742 4 2 2 2 16 133 124 52
743 4 2 2 2 136 137 125 115
744 4 2 2 2 132 138 137 125
745 4 2 2 2 41 130 9 109
746 4 2 2 2 134 120 15 51
747 4 2 2 2 129 118 120 119
748 4 2 2 2 129 120 134 51
749 4 2 2 2 135 122 124 125
750 4 2 2 2 135 48 124 122
751 4 2 2 2 133 48 16 124
752 4 2 2 2 133 135 124 125
753 4 2 2 2 133 48 124 135
754 4 2 2 2 116 136 134 125
755 4 2 2 2 115 136 116 125
756 4 2 2 2 46 115 136 116
757 4 2 2 2 136 134 15 116
758 4 2 2 2 46 136 15 116
759 4 2 2 2 109 41 130 110
760 4 2 2 2 110 131 109 111
$EndElements
+77
View File
@@ -0,0 +1,77 @@
// Square-in-square 2D geometry for MFEM
// Creates concentric squares with different material attributes
// Define the square sizes
L_outer = 2.0;
L_inner = 0.5;
// Set mesh size and algorithm
mesh_size = 1.0;
Mesh.Algorithm = 6; // Frontal-Delaunay for 2D triangular mesh
Mesh.CharacteristicLengthFactor = 1.0;
Mesh.MshFileVersion = 2.2;
// Define center point for concentric squares
cx = 0.0;
cy = 0.0;
// Define the points (vertices of the outer square)
Point(1) = {cx-L_outer/2, cy-L_outer/2, 0, mesh_size}; // bottom-left outer
Point(2) = {cx+L_outer/2, cy-L_outer/2, 0, mesh_size}; // bottom-right outer
Point(3) = {cx+L_outer/2, cy+L_outer/2, 0, mesh_size}; // top-right outer
Point(4) = {cx-L_outer/2, cy+L_outer/2, 0, mesh_size}; // top-left outer
// Define the points (vertices of the inner square)
Point(5) = {cx-L_inner/2, cy-L_inner/2, 0, mesh_size}; // bottom-left inner
Point(6) = {cx+L_inner/2, cy-L_inner/2, 0, mesh_size}; // bottom-right inner
Point(7) = {cx+L_inner/2, cy+L_inner/2, 0, mesh_size}; // top-right inner
Point(8) = {cx-L_inner/2, cy+L_inner/2, 0, mesh_size}; // top-left inner
// Define the lines (edges of the outer square)
Line(1) = {1, 2}; // bottom edge
Line(2) = {2, 3}; // right edge
Line(3) = {3, 4}; // top edge
Line(4) = {4, 1}; // left edge
// Define the lines (edges of the inner square)
Line(5) = {5, 6}; // bottom edge
Line(6) = {6, 7}; // right edge
Line(7) = {7, 8}; // top edge
Line(8) = {8, 5}; // left edge
// Define the surfaces
// Outer square boundary
Line Loop(1) = {1, 2, 3, 4};
// Inner square boundary (hole in the outer region)
Line Loop(2) = {5, 6, 7, 8};
// Define the surface areas
// Outer region (annular region between squares)
Plane Surface(1) = {1, 2}; // Outer loop minus inner loop (creates hole)
// Inner region (solid inner square)
Plane Surface(2) = {2}; // Inner loop only
// Assign physical groups for materials
Physical Surface(1) = {1}; // Outer material (annular region)
Physical Surface(2) = {2}; // Inner material (solid square)
// Physical lines for boundary conditions
// Outer square boundary edges
Physical Line(1) = {1}; // outer bottom
Physical Line(2) = {2}; // outer right
Physical Line(3) = {3}; // outer top
Physical Line(4) = {4}; // outer left
// Inner square boundary edges
Physical Line(5) = {5}; // inner bottom
Physical Line(6) = {6}; // inner right
Physical Line(7) = {7}; // inner top
Physical Line(8) = {8}; // inner left
// Mesh control for quality
Mesh.OptimizeNetgen = 1;
Mesh.Optimize = 1;
Mesh.ElementOrder = 1;
Mesh.RecombineAll = 0; // Keep triangular elements (don't recombine to quads)
+50
View File
@@ -0,0 +1,50 @@
$MeshFormat
2.2 0 8
$EndMeshFormat
$Nodes
13
1 -1 -1 0
2 1 -1 0
3 1 1 0
4 -1 1 0
5 -0.25 -0.25 0
6 0.25 -0.25 0
7 0.25 0.25 0
8 -0.25 0.25 0
9 -2.752797989558076e-12 -1 0
10 1 -2.752797989558076e-12 0
11 2.752797989558076e-12 1 0
12 -1 2.752797989558076e-12 0
13 0 0 0
$EndNodes
$Elements
28
1 1 2 1 1 1 9
2 1 2 1 1 9 2
3 1 2 2 2 2 10
4 1 2 2 2 10 3
5 1 2 3 3 3 11
6 1 2 3 3 11 4
7 1 2 4 4 4 12
8 1 2 4 4 12 1
9 1 2 5 5 5 6
10 1 2 6 6 6 7
11 1 2 7 7 7 8
12 1 2 8 8 8 5
13 2 2 1 1 6 5 9
14 2 2 1 1 5 8 12
15 2 2 1 1 7 6 10
16 2 2 1 1 8 7 11
17 2 2 1 1 9 5 1
18 2 2 1 1 5 12 1
19 2 2 1 1 6 9 2
20 2 2 1 1 10 6 2
21 2 2 1 1 7 10 3
22 2 2 1 1 11 7 3
23 2 2 1 1 8 11 4
24 2 2 1 1 8 4 12
25 2 2 2 2 5 6 13
26 2 2 2 2 8 5 13
27 2 2 2 2 6 7 13
28 2 2 2 2 7 8 13
$EndElements
+38
View File
@@ -0,0 +1,38 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see fem/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
# PYRAMID = 7
dimension
2
elements
2
1 3 0 1 4 3
1 2 1 2 4
boundary
5
1 1 0 1
1 1 1 2
1 1 2 4
1 1 4 3
1 1 3 0
vertices
5
2
0 0
1 0
2 0
0 1
1 1
+2 -1
View File
@@ -1083,7 +1083,8 @@ EXCLUDE_PATTERNS =
# ANamespace::AClass, ANamespace::*Test
EXCLUDE_SYMBOLS = mfem::internal \
mfem::kernels::internal
mfem::kernels::internal \
mfem::future::detail
# The EXAMPLE_PATH tag can be used to specify one or more files or directories
# that contain example code fragments that are included (see the \include
+4
View File
@@ -201,6 +201,7 @@ namespace mfem {
* - <a class="el" href="nurbs__naca__cmesh_8cpp_source.html">NURBS NACA Mesher</a>: generate NURBS based mesh around a NACA foil
* - <a class="el" href="nurbs__printfunc_8cpp_source.html">NURBS Printer</a>: print the NURBS-basis
* - <a class="el" href="nurbs__mesh_info_8cpp_source.html">NURBS Mesh info</a>: print the info of a NURBS mesh
* - <a class="el" href="nurbs__surface_8cpp_source.html">NURBS Surface</a>: interpolate a 3D Surface in a NURBS Patch
*
* <H3>Miniapps</H3>
* - <a class="el" href="volta_8cpp_source.html">Volta</a>: simple electrostatics simulation code
@@ -245,6 +246,9 @@ namespace mfem {
* - <a class="el" href="pdiffusion_8cpp_source.html">DPG Diffusion example</a>: DPG formulation for the diffusion problem
* - <a class="el" href="pmaxwell_8cpp_source.html">DPG Maxwell example</a>: DPG formulation for the indefinite Maxwell problem
* - <a class="el" href="lor__elast_8cpp_source.html">LOR Elasticity</a>: solve linear elasticity with LOR preconditioning on GPUs
* - <a class="el" href="reflector_8cpp_source.html">Reflector Miniapp</a>: reflect a mesh about a plane
* - <a class="el" href="ref321_8cpp_source.html">3:1 Refinement Miniapp</a>: perform 3:1 anisotropic mesh refinements
* - <a class="el" href="pref321_8cpp_source.html">3:1 Refinement Miniapp</a>: parallel 3:1 anisotropic mesh refinements
*
* See also the <a class="el" href="https://mfem.org/examples/">examples documentation</a> online.
*/
+8 -1
View File
@@ -57,6 +57,8 @@ set(SRCS
integ/lininteg_domain_grad.cpp
integ/lininteg_domain_vectorfe.cpp
integ/nonlininteg_vecconvection_pa.cpp
integ/nonlininteg_vecconvection_pa_diag.cpp
integ/nonlininteg_vecconvection_pa_grad.cpp
integ/nonlininteg_vecconvection_mf.cpp
coefficient.cpp
complex_fem.cpp
@@ -133,7 +135,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
@@ -204,7 +206,11 @@ set(HDRS
integ/bilininteg_mass_kernels.hpp
integ/bilininteg_mass_pa_simplices.hpp
integ/bilininteg_vecdiffusion_pa.hpp
integ/bilininteg_vecdiv_pa.hpp
integ/bilininteg_vecmass_pa.hpp
integ/nonlininteg_vecconvection_pa.hpp
integ/nonlininteg_vecconvection_pa_diag.hpp
integ/nonlininteg_vecconvection_pa_grad.hpp
coefficient.hpp
complex_fem.hpp
convergence.hpp
@@ -311,6 +317,7 @@ set(HDRS
tmop_tools.hpp
tmop_amr.hpp
gslib.hpp
gslib/gslib_kernel_helpers.hpp
transfer.hpp
hyperbolic.hpp
integrator.hpp
+12 -2
View File
@@ -1997,7 +1997,11 @@ void PADiscreteLinearOperatorExtension::Assemble()
}
else
{
mfem_error("A real ElementRestriction is required in this setting!");
const L2ElementRestriction* l2_elem_restrict =
dynamic_cast<const L2ElementRestriction*>(elem_restrict_test);
MFEM_VERIFY(l2_elem_restrict,
"A real ElementRestriction is required in this setting!");
test_multiplicity = 1.0;
}
auto tm = test_multiplicity.ReadWrite();
@@ -2036,7 +2040,13 @@ void PADiscreteLinearOperatorExtension::AddMult(
}
else
{
mfem_error("In this setting you need a real ElementRestriction!");
const L2ElementRestriction* l2_elem_restrict =
dynamic_cast<const L2ElementRestriction*>(elem_restrict_test);
MFEM_VERIFY(l2_elem_restrict,
"In this setting you need a real ElementRestriction!");
tempY.SetSize(y.Size());
l2_elem_restrict->MultTranspose(localTest, tempY);
y += tempY;
}
}
+413 -326
View File
File diff suppressed because it is too large Load Diff
+5 -1
View File
@@ -1055,7 +1055,8 @@ public:
typedef VectorCoefficient DiagonalMatrixCoefficient;
/// Base class for Matrix Coefficients that optionally depend on time and space.
/** Base class for matrix-valued coefficients that optionally depend on time
and space. */
class MatrixCoefficient
{
protected:
@@ -1102,6 +1103,9 @@ public:
/// the quadrature points. The matrix will be transposed or not according to
/// the boolean argument @a transpose.
///
/// The stored entries use the same row/column convention as `Eval()`,
/// unless `transpose == true`, in which case `K^T` is stored instead.
///
/// The @a vdim of the QuadratureFunction should be equal to the height times
/// the width of the matrix.
virtual void Project(QuadratureFunction &qf, bool transpose=false);
+1036 -138
View File
File diff suppressed because it is too large Load Diff
+421
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;
@@ -323,6 +392,9 @@ private:
bool RealInteg();
bool ImagInteg();
void BuildComplexOperator(OperatorHandle &A_r, OperatorHandle &A_i,
OperatorHandle &A) const;
public:
SesquilinearForm(FiniteElementSpace *fes,
ComplexOperator::Convention
@@ -436,6 +508,186 @@ public:
virtual ~SesquilinearForm();
};
/** Class for a mixed sesquilinear form
A mixed sesquilinear form is a generalization of a mixed bilinear form to
complex-valued fields. Mixed sesquilinear forms are linear in the second
argument but the first argument involves a complex conjugate in the sense
that:
a(alpha u, beta v) = conj(alpha) beta a(u, v)
The @a convention argument in the class's constructor is documented in the
mfem::ComplexOperator class found in linalg/complex_operator.hpp.
When supplying integrators to the MixedSesquilinearForm either the real or
imaginary integrator can be NULL. This indicates that the corresponding
portion of the complex-valued material coefficient is equal to zero.
*/
class MixedSesquilinearForm
{
private:
ComplexOperator::Convention conv;
MixedBilinearForm * mblfr;
MixedBilinearForm * mblfi;
/* These methods check if the real/imag parts of the sesqulinear form are not
empty */
bool RealInteg();
bool ImagInteg();
public:
MixedSesquilinearForm(
FiniteElementSpace * trial_fes,
FiniteElementSpace * test_fes,
ComplexOperator::Convention convention = ComplexOperator::HERMITIAN);
/** @brief Create a MixedSesquilinearForm on the given trial and test
FiniteElementSpaces, using the same integrators as the
MixedBilinearForms @a bfr and @a bfi.
The FiniteElementSpace pointers are not owned by the newly constructed
object.
The integrators are copied as pointers and they are not owned by the
newly constructed MixedSesquilinearForm. */
MixedSesquilinearForm(
FiniteElementSpace * trial_fes,
FiniteElementSpace * test_fes,
MixedBilinearForm * bfr,
MixedBilinearForm * bfi,
ComplexOperator::Convention convention = ComplexOperator::HERMITIAN);
ComplexOperator::Convention GetConvention() const { return conv; }
void SetConvention(const ComplexOperator::Convention & convention) { conv = convention; }
/// Set the desired assembly level.
/** Valid choices are:
- AssemblyLevel::LEGACY (default)
- AssemblyLevel::FULL
- AssemblyLevel::PARTIAL
- AssemblyLevel::ELEMENT
- AssemblyLevel::NONE
This method must be called before assembly. */
void SetAssemblyLevel(AssemblyLevel assembly_level)
{
mblfr->SetAssemblyLevel(assembly_level);
mblfi->SetAssemblyLevel(assembly_level);
}
MixedBilinearForm & real() { return *mblfr; }
MixedBilinearForm & imag() { return *mblfi; }
const MixedBilinearForm & real() const { return *mblfr; }
const MixedBilinearForm & imag() const { return *mblfi; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/// Adds new Domain Integrator, restricted to specific attributes.
void AddDomainIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag,
Array<int> & elem_marker);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/** @brief Adds new boundary Integrator, restricted to specific boundary
attributes.
Assumes ownership of @a bfi.
The mfem::array @a bdr_marker is stored internally as a pointer to the given
mfem::Array<int> object. */
void AddBoundaryIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag,
Array<int> & bdr_marker);
/// Adds new interior Face Integrator. Assumes ownership of @a bfi.
void AddInteriorFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/// Adds new boundary Face Integrator. Assumes ownership of @a bfi.
void AddBdrFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/** @brief Adds new boundary Face Integrator, restricted to specific boundary
attributes.
Assumes ownership of @a bfi.
The mfem::array @a bdr_marker is stored internally as a pointer to the given
mfem::Array<int> object. */
void AddBdrFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag,
Array<int> & bdr_marker);
/** @brief Add a trace face integrator. Assumes ownership of @a bfi.
This type of integrator assembles terms over all faces of the mesh using
the face FE from the trial space and the two adjacent volume FEs from
the test space. */
void AddTraceFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
void AddBdrTraceFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
void AddBdrTraceFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag,
Array<int> &bdr_marker);
/// Assemble the local matrix
void Assemble(int skip_zeros = 1);
/// Finalizes the matrix initialization.
void Finalize(int skip_zeros = 1);
/// Updates the internal mixed forms with the new finite element space.
virtual void Update();
/** @brief Return a ComplexSparseMatrix wrapping the local (L-dof) real
and imaginary matrices of the form.
The returned wrapper has to be deleted by the caller, but it does not
own the wrapped real and imaginary matrices, which remain owned by
this form. */
ComplexSparseMatrix *AssembleComplexSparseMatrix();
/// Return the trial FE space associated with the MixedSesquilinearForm.
FiniteElementSpace *TrialFESpace() { return mblfr->TrialFESpace(); }
/// Read-only access to the associated trial FiniteElementSpace.
const FiniteElementSpace *TrialFESpace() const { return mblfr->TrialFESpace(); }
/// Return the test FE space associated with the MixedSesquilinearForm.
FiniteElementSpace *TestFESpace() { return mblfr->TestFESpace(); }
/// Read-only access to the associated test FiniteElementSpace.
const FiniteElementSpace *TestFESpace() const { return mblfr->TestFESpace(); }
void FormRectangularLinearSystem(const Array<int> & ess_trial_tdof_list,
const Array<int> & ess_test_tdof_list,
Vector & x,
Vector & b,
OperatorHandle & A,
Vector & X,
Vector & B);
void FormRectangularSystemMatrix(const Array<int> & ess_trial_tdof_list,
const Array<int> & ess_test_tdof_list,
OperatorHandle & A);
virtual ~MixedSesquilinearForm();
};
#ifdef MFEM_USE_MPI
/// Class for parallel complex-valued grid function - real + imaginary part
@@ -737,6 +989,12 @@ private:
bool RealInteg();
bool ImagInteg();
void SetImaginaryEssentialDiagonalToZero(
const Array<int> &ess_tdof_list, OperatorHandle &A);
void BuildComplexOperator(OperatorHandle &A_r, OperatorHandle &A_i,
OperatorHandle &A) const;
public:
ParSesquilinearForm(ParFiniteElementSpace *pf,
ComplexOperator::Convention
@@ -852,6 +1110,169 @@ public:
virtual ~ParSesquilinearForm();
};
/** Class for a parallel mixed sesquilinear form
A mixed sesquilinear form is a generalization of a mixed bilinear form to
complex-valued fields. Mixed sesquilinear forms are linear in the second
argument but the first argument involves a complex conjugate in the sense
that:
a(alpha u, beta v) = conj(alpha) beta a(u, v)
The @a convention argument in the class's constructor is documented in the
mfem::ComplexOperator class found in linalg/complex_operator.hpp.
When supplying integrators to the ParMixedSesquilinearForm either the real
or imaginary integrator can be NULL. This indicates that the corresponding
portion of the complex-valued material coefficient is equal to zero.
*/
class ParMixedSesquilinearForm
{
private:
ComplexOperator::Convention conv;
ParMixedBilinearForm * pmblfr;
ParMixedBilinearForm * pmblfi;
/* These methods check if the real/imag parts of the sesqulinear form are
not empty */
bool RealInteg();
bool ImagInteg();
public:
ParMixedSesquilinearForm(
ParFiniteElementSpace * trial_fes,
ParFiniteElementSpace * test_fes,
ComplexOperator::Convention convention = ComplexOperator::HERMITIAN);
/** @brief Create a ParMixedSesquilinearForm on the given trial and test
ParFiniteElementSpaces, using the same integrators as the
ParMixedBilinearForms @a pbfr and @a pbfi.
The ParFiniteElementSpace pointers are not owned by the newly
constructed object.
The integrators are copied as pointers and they are not owned by the
newly constructed ParMixedSesquilinearForm. */
ParMixedSesquilinearForm(
ParFiniteElementSpace * trial_fes,
ParFiniteElementSpace * test_fes,
ParMixedBilinearForm * pbfr,
ParMixedBilinearForm * pbfi,
ComplexOperator::Convention convention = ComplexOperator::HERMITIAN);
ComplexOperator::Convention GetConvention() const { return conv; }
void SetConvention(const ComplexOperator::Convention & convention) { conv = convention; }
/// Set the desired assembly level.
/** Valid choices are:
- AssemblyLevel::LEGACY (default)
- AssemblyLevel::FULL
- AssemblyLevel::PARTIAL
- AssemblyLevel::ELEMENT
- AssemblyLevel::NONE
This method must be called before assembly. */
void SetAssemblyLevel(AssemblyLevel assembly_level)
{
pmblfr->SetAssemblyLevel(assembly_level);
pmblfi->SetAssemblyLevel(assembly_level);
}
ParMixedBilinearForm & real() { return *pmblfr; }
ParMixedBilinearForm & imag() { return *pmblfi; }
const ParMixedBilinearForm & real() const { return *pmblfr; }
const ParMixedBilinearForm & imag() const { return *pmblfi; }
/// Adds new Domain Integrator.
void AddDomainIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/// Adds new Domain Integrator, restricted to specific attributes.
void AddDomainIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag,
Array<int> & elem_marker);
/// Adds new Boundary Integrator.
void AddBoundaryIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/** @brief Adds new boundary Integrator, restricted to specific boundary
attributes.
Assumes ownership of @a bfi.
The mfem::array @a bdr_marker is stored internally as a pointer to the given
mfem::Array<int> object. */
void AddBoundaryIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag,
Array<int> & bdr_marker);
/// Adds new interior Face Integrator. Assumes ownership of @a bfi.
void AddInteriorFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/// Adds new boundary Face Integrator. Assumes ownership of @a bfi.
void AddBdrFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/** @brief Adds new boundary Face Integrator, restricted to specific boundary
attributes.
Assumes ownership of @a bfi.
The mfem::array @a bdr_marker is stored internally as a pointer to the given
mfem::Array<int> object. */
void AddBdrFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag,
Array<int> & bdr_marker);
/** @brief Add a trace face integrator. Assumes ownership of @a bfi.
This type of integrator assembles terms over all faces of the mesh using
the face FE from the trial space and the two adjacent volume FEs from
the test space. */
void AddTraceFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
void AddBdrTraceFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag);
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
void AddBdrTraceFaceIntegrator(BilinearFormIntegrator * bfi_real,
BilinearFormIntegrator * bfi_imag,
Array<int> &bdr_marker);
/// Assemble the local matrix
void Assemble(int skip_zeros = 1);
/// Finalizes the matrix initialization.
void Finalize(int skip_zeros = 1);
/// Updates the internal mixed forms with the new finite element space.
virtual void Update();
/// Returns the matrix assembled on the true dofs, i.e. P^t A P.
/** The returned matrix has to be deleted by the caller. */
ComplexHypreParMatrix * ParallelAssemble();
void FormRectangularLinearSystem(const Array<int> & ess_trial_tdof_list,
const Array<int> & ess_test_tdof_list,
Vector & x,
Vector & b,
OperatorHandle & A,
Vector & X,
Vector & B);
void FormRectangularSystemMatrix(const Array<int> & ess_trial_tdof_list,
const Array<int> & ess_test_tdof_list,
OperatorHandle & A);
virtual ~ParMixedSesquilinearForm();
};
#endif // MFEM_USE_MPI
}
+23 -5
View File
@@ -38,9 +38,24 @@ int DataCollection::create_directory(const std::string &dir_name,
// create directories recursively
const char path_delim = '/';
std::string::size_type pos = 0;
int err_flag;
int err_flag = 0;
#ifdef MFEM_USE_MPI
const ParMesh *pmesh = dynamic_cast<const ParMesh*>(mesh);
// In addition to the global root, let the lowest rank on each shared-memory
// node create the directory too, so that node-local (non-shared) filesystems
// get it on every node rather than only where the global root lives. On a
// shared filesystem the extra mkdir() hits EEXIST and is tolerated below.
bool node_root = true;
if (pmesh)
{
MPI_Comm node_comm;
MPI_Comm_split_type(pmesh->GetComm(), MPI_COMM_TYPE_SHARED, myid,
MPI_INFO_NULL, &node_comm);
int node_rank;
MPI_Comm_rank(node_comm, &node_rank);
node_root = (node_rank == 0);
MPI_Comm_free(&node_comm);
}
#endif
do
@@ -52,7 +67,7 @@ int DataCollection::create_directory(const std::string &dir_name,
err_flag = mkdir(subdir.c_str(), 0777);
err_flag = (err_flag && (errno != EEXIST)) ? 1 : 0;
#else
if (myid == 0 || pmesh == NULL)
if (node_root || pmesh == NULL)
{
err_flag = mkdir(subdir.c_str(), 0777);
err_flag = (err_flag && (errno != EEXIST)) ? 1 : 0;
@@ -64,7 +79,8 @@ int DataCollection::create_directory(const std::string &dir_name,
#ifdef MFEM_USE_MPI
if (pmesh)
{
MPI_Bcast(&err_flag, 1, MPI_INT, 0, pmesh->GetComm());
MPI_Allreduce(MPI_IN_PLACE, &err_flag, 1, MPI_INT, MPI_MAX,
pmesh->GetComm());
}
#endif
@@ -809,7 +825,7 @@ ParaViewDataCollectionBase::ParaViewDataCollectionBase(
void ParaViewDataCollectionBase::SetLevelsOfDetail(int levels_of_detail_)
{
levels_of_detail = levels_of_detail_;
levels_of_detail = std::max(levels_of_detail_, 1);
}
void ParaViewDataCollectionBase::SetHighOrderOutput(bool high_order_output_)
@@ -1181,12 +1197,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++)
{
+48
View File
@@ -51,4 +51,52 @@ DifferentiableOperator::DifferentiableOperator(
}
}
void FDJacobian::Mult(const Vector &v, Vector &y) const
{
// See [1] for choice of eps.
//
// [1] Woodward, C.S., Gardner, D.J. and Evans, K.J., 2015. On the use of
// finite difference matrix-vector products in Newton-Krylov solvers for
// implicit climate dynamics with spectral elements. Procedia Computer
// Science, 51, pp.2036-2045.
real_t eps;
if (fixed_eps > 0.0)
{
eps = fixed_eps;
}
else
{
const real_t vnorm_local = v.Norml2();
real_t vnorm;
MPI_Allreduce(&vnorm_local, &vnorm, 1, MPITypeMap<real_t>::mpi_type, MPI_SUM,
MPI_COMM_WORLD);
eps = lambda * (lambda + xnorm / vnorm);
}
// x + eps * v
{
const auto d_v = v.Read();
const auto d_x = x.Read();
auto d_xpev = xpev.Write();
mfem::forall(x.Size(), [=] MFEM_HOST_DEVICE (int i)
{
d_xpev[i] = d_x[i] + eps * d_v[i];
});
}
// y = f(x + eps * v)
op.Mult(xpev, y);
// y = (f(x + eps * v) - f(x)) / eps
{
const auto d_f = f.Read();
auto d_y = y.ReadWrite();
mfem::forall(f.Size(), [=] MFEM_HOST_DEVICE (int i)
{
d_y[i] = (d_y[i] - d_f[i]) / eps;
});
}
}
#endif // MFEM_USE_MPI
+23 -22
View File
@@ -697,17 +697,18 @@ void DifferentiableOperator::AddIntegrator(
// The explicit captures are necessary to avoid dependency on
// the specific instance of this class (this pointer).
restriction_callback =
[=, solutions = this->solutions, parameters = this->parameters]
(std::vector<Vector> &sol,
const std::vector<Vector> &par,
std::vector<Vector> &f)
restriction_callback = [element_dof_ordering,
solutions_ = this->solutions,
parameters_ = this->parameters]
(std::vector<Vector> &sol,
const std::vector<Vector> &par,
std::vector<Vector> &f)
{
restriction<entity_t>(solutions, sol, f,
restriction<entity_t>(solutions_, sol, f,
element_dof_ordering);
restriction<entity_t>(parameters, par, f,
restriction<entity_t>(parameters_, par, f,
element_dof_ordering,
solutions.size());
solutions_.size());
};
prolongation_transpose = get_prolongation_transpose(
@@ -835,19 +836,19 @@ void DifferentiableOperator::AddIntegrator(
// capture by ref:
&restriction_cb = this->restriction_callback,
&fields_e = this->fields_e,
&residual_e = this->residual_e,
&output_restriction_transpose = this->output_restriction_transpose
&fields_e_ = this->fields_e,
&residual_e_ = this->residual_e,
&output_restriction_transpose_ = this->output_restriction_transpose
]
(std::vector<Vector> &sol, const std::vector<Vector> &par, Vector &res)
mutable // mutable: needed to modify 'shmem_cache'
{
restriction_cb(sol, par, fields_e);
restriction_cb(sol, par, fields_e_);
residual_e = 0.0;
auto ye = Reshape(residual_e.ReadWrite(), test_vdim, num_test_dof, num_entities);
residual_e_ = 0.0;
auto ye = Reshape(residual_e_.ReadWrite(), test_vdim, num_test_dof, num_entities);
auto wrapped_fields_e = wrap_fields(fields_e,
auto wrapped_fields_e = wrap_fields(fields_e_,
action_shmem_info.field_sizes,
num_entities);
@@ -878,7 +879,7 @@ void DifferentiableOperator::AddIntegrator(
y, fhat, output_fop, output_dtq_shmem[0],
scratch_shmem, dimension, use_sum_factorization);
}, num_entities, thread_blocks, action_shmem_info.total_size, shmem_cache.ReadWrite());
output_restriction_transpose(residual_e, res);
output_restriction_transpose_(residual_e_, res);
});
// Without this compile-time check, some valid instantiations of this method
@@ -1193,7 +1194,7 @@ void DifferentiableOperator::AddIntegrator(
// capture by ref:
&qpdc_mem = derivative_qp_caches_ref,
&fields = fields_ref
&fields_ = fields_ref
](std::vector<Vector> &f_e, SparseMatrix *&A) mutable
{
auto wrapped_fields_e = wrap_fields(f_e, shmem_info.field_sizes,
@@ -1241,14 +1242,14 @@ void DifferentiableOperator::AddIntegrator(
{
if (input_is_dependent[s])
{
trial_field = &fields[input_to_field[s]];
trial_field = &fields_[input_to_field[s]];
}
}
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
(&trial_field->data);
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
(&fields[output_to_field[0]].data);
(&fields_[output_to_field[0]].data);
A = new SparseMatrix(test_fes->GetVSize(), trial_fes->GetVSize());
@@ -1334,7 +1335,7 @@ void DifferentiableOperator::AddIntegrator(
input_to_field,
output_to_field,
&spmatcb = assemble_derivative_sparsematrix_callbacks_ref,
&fields = fields_ref
&fields_ = fields_ref
](std::vector<Vector> &f_e, HypreParMatrix *&A) mutable
{
SparseMatrix *spmat = nullptr;
@@ -1366,14 +1367,14 @@ void DifferentiableOperator::AddIntegrator(
{
if (input_is_dependent[s])
{
trial_field = &fields[input_to_field[s]];
trial_field = &fields_[input_to_field[s]];
}
}
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
(&trial_field->data);
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
(&fields[output_to_field[0]].data);
(&fields_[output_to_field[0]].data);
if (same_test_and_trial)
{
+742 -768
View File
File diff suppressed because it is too large Load Diff
+9 -52
View File
@@ -597,7 +597,7 @@ struct ThreadBlocks
int z = 1;
};
#if defined(MFEM_USE_CUDA_OR_HIP)
#if defined(MFEM_USE_CUDA_OR_HIP_LANG)
template <typename func_t>
__global__ void forall_kernel_shmem(func_t f, int n)
{
@@ -617,10 +617,11 @@ void forall(func_t f,
int num_shmem = 0,
real_t *shmem = nullptr)
{
if (Device::Allows(Backend::CUDA_MASK) ||
Device::Allows(Backend::HIP_MASK))
internal::RequireKernelCompilation();
#if defined(MFEM_USE_CUDA_OR_HIP_LANG)
if (Device::Allows(Backend::CUDA_MASK | Backend::HIP_MASK))
{
#if defined(MFEM_USE_CUDA_OR_HIP)
// int gridsize = (N + Z - 1) / Z;
int num_bytes = num_shmem * sizeof(decltype(shmem));
dim3 block_size(blocks.x, blocks.y, blocks.z);
@@ -631,9 +632,10 @@ void forall(func_t f,
MFEM_GPU_CHECK(hipGetLastError());
#endif
MFEM_DEVICE_SYNC;
#endif
return;
}
else if (Device::Allows(Backend::CPU_MASK))
#endif
if (Device::Allows(Backend::CPU_MASK))
{
MFEM_ASSERT(!((bool)num_shmem != (bool)shmem),
"Backend::CPU needs a pre-allocated shared memory block");
@@ -671,52 +673,7 @@ public:
MPI_COMM_WORLD);
}
void Mult(const Vector &v, Vector &y) const override
{
// See [1] for choice of eps.
//
// [1] Woodward, C.S., Gardner, D.J. and Evans, K.J., 2015. On the use of
// finite difference matrix-vector products in Newton-Krylov solvers for
// implicit climate dynamics with spectral elements. Procedia Computer
// Science, 51, pp.2036-2045.
real_t eps;
if (fixed_eps > 0.0)
{
eps = fixed_eps;
}
else
{
const real_t vnorm_local = v.Norml2();
real_t vnorm;
MPI_Allreduce(&vnorm_local, &vnorm, 1, MPITypeMap<real_t>::mpi_type, MPI_SUM,
MPI_COMM_WORLD);
eps = lambda * (lambda + xnorm / vnorm);
}
// x + eps * v
{
const auto d_v = v.Read();
const auto d_x = x.Read();
auto d_xpev = xpev.Write();
mfem::forall(x.Size(), [=] MFEM_HOST_DEVICE (int i)
{
d_xpev[i] = d_x[i] + eps * d_v[i];
});
}
// y = f(x + eps * v)
op.Mult(xpev, y);
// y = (f(x + eps * v) - f(x)) / eps
{
const auto d_f = f.Read();
auto d_y = y.ReadWrite();
mfem::forall(f.Size(), [=] MFEM_HOST_DEVICE (int i)
{
d_y[i] = (d_y[i] - d_f[i]) / eps;
});
}
}
void Mult(const Vector &v, Vector &y) const override;
virtual MemoryClass GetMemoryClass() const override
{
+6 -5
View File
@@ -1316,13 +1316,14 @@ void VectorFiniteElement::Project_RT(
}
}
void VectorFiniteElement::ProjectGrad_RT(
void VectorFiniteElement::ProjectCurl2D_RT(
const real_t *nk, const Array<int> &d2n, const FiniteElement &fe,
ElementTransformation &Trans, DenseMatrix &grad) const
{
// 2D "ProjectCurl_RT"
if (dim != 2)
{
mfem_error("VectorFiniteElement::ProjectGrad_RT works only in 2D!");
mfem_error("VectorFiniteElement::ProjectCurl2D_RT works only in 2D!");
}
DenseMatrix dshape(fe.GetDof(), fe.GetDim());
@@ -1333,8 +1334,8 @@ void VectorFiniteElement::ProjectGrad_RT(
for (int k = 0; k < dof; k++)
{
fe.CalcDShape(Nodes.IntPoint(k), dshape);
tk[0] = nk[d2n[k]*dim+1];
tk[1] = -nk[d2n[k]*dim];
tk[0] = -nk[d2n[k]*dim+1];
tk[1] = nk[d2n[k]*dim];
dshape.Mult(tk, grad_k);
for (int j = 0; j < grad_k.Size(); j++)
{
@@ -1381,7 +1382,7 @@ void VectorFiniteElement::ProjectCurl_ND(
}
}
void VectorFiniteElement::ProjectCurl_RT(
void VectorFiniteElement::ProjectCurl3D_RT(
const real_t *nk, const Array<int> &d2n, const FiniteElement &fe,
ElementTransformation &Trans, DenseMatrix &curl) const
{
+8 -7
View File
@@ -957,10 +957,11 @@ protected:
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const;
// rotated gradient in 2D
void ProjectGrad_RT(const real_t *nk, const Array<int> &d2n,
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &grad) const;
// Input is a scalar representing the Z (out of plane) component, Output is
// the X-Y (in-plane) RT curl
void ProjectCurl2D_RT(const real_t *nk, const Array<int> &d2n,
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &grad) const;
// Compute the curl as a discrete operator from ND FE (fe) to ND FE (this).
// The natural FE for the range is RT, so this is an approximation.
@@ -968,9 +969,9 @@ protected:
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &curl) const;
void ProjectCurl_RT(const real_t *nk, const Array<int> &d2n,
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &curl) const;
void ProjectCurl3D_RT(const real_t *nk, const Array<int> &d2n,
const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &curl) const;
/** @brief Project a vector coefficient onto the ND basis functions
@param tk Edge tangent vectors for this element type
+4 -4
View File
@@ -307,12 +307,12 @@ public:
/** @brief virtual function which evaluates the values of all
shape functions at a given point ip and stores
them in the vector shape of dimension Dof (4) */
them in the vector shape of dimension Dof (6) */
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
/** @brief virtual function which evaluates the values of all
partial derivatives of all shape functions at a given
point ip and stores them in the matrix dshape (Dof x Dim) (4 x 3)
point ip and stores them in the matrix dshape (Dof x Dim) (6 x 3)
so that each row contains the derivatives of one shape function */
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
@@ -336,12 +336,12 @@ public:
/** @brief virtual function which evaluates the values of all
shape functions at a given point ip and stores
them in the vector shape of dimension Dof (4) */
them in the vector shape of dimension Dof (5) */
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
/** @brief virtual function which evaluates the values of all
partial derivatives of all shape functions at a given
point ip and stores them in the matrix dshape (Dof x Dim) (4 x 3)
point ip and stores them in the matrix dshape (Dof x Dim) (5 x 3)
so that each row contains the derivatives of one shape function */
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
+131 -58
View File
@@ -1757,22 +1757,45 @@ H1_BergotPyramidElement::H1_BergotPyramidElement(const int p, const int btype)
real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
o = 0;
for (int i = 0; i <= p; i++)
if (std::abs(z - 1.0) < apex_tol)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0, shape_z);
for (int k = 0; k <= p - maxij; k++)
// Compute the limit of the basis functions as z->1 with x and y on the
// line between the center of the base and the apex
o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
T(o++, m) = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
int maxij = std::max(i, j);
for (int k = 0; k <= p - maxij; k++)
if (i == 0 && j == 0)
{
T(o++, m) = ((k + 3.) * k + 2.) / 2.;
}
else
{
T(o++, m) = 0.;
}
}
}
else
{
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
o = 0;
for (int i = 0; i <= p; i++)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0, shape_z);
for (int k = 0; k <= p - maxij; k++)
{
T(o++, m) = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
}
}
}
}
@@ -1793,25 +1816,44 @@ void H1_BergotPyramidElement::CalcShape(const IntegrationPoint &ip,
Vector u(dof);
#endif
real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
real_t z = ip.z;
const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
const real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
shape_z);
for (int k = 0; k <= p - maxij; k++)
u[o++] = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
}
if (std::abs(z - 1.0) < apex_tol)
{
// Compute the limit of the basis functions as z->1 with x and y on the
// line between the center of the base and the apex
u = 0.;
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
for (int k = 0; k <= p - maxij; k++, o++)
if (i == 0 && j == 0)
{
u(o) = ((k + 3.) * k + 2.) / 2.;
}
}
}
else
{
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0, shape_z);
for (int k = 0; k <= p - maxij; k++)
u[o++] = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
}
}
Ti.Mult(u, shape);
}
@@ -1830,37 +1872,68 @@ void H1_BergotPyramidElement::CalcDShape(const IntegrationPoint &ip,
Vector dshape_z(order+1);
Vector dshape_z_dt(order+1);
#endif
real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
real_t z = ip.z;
const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
const real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData(), dshape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData(), dshape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
shape_z, dshape_z, dshape_z_dt);
for (int k = 0; k <= p - maxij; k++, o++)
if (std::abs(z - 1.0) < apex_tol)
{
// Compute the limit of the gradients of the basis functions as
// z->1 with x and y on the line between the center of the base and the
// apex
du = 0.;
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
du(o,0) = dshape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,1) = shape_x(i) * dshape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,2) = shape_x(i) * shape_y(j) * dshape_z(k) *
pow(1.0 - ip.z, maxij) +
(ip.x * dshape_x(i) * shape_y(j) +
ip.y * shape_x(i) * dshape_y(j)) *
shape_z(k) * pow(1.0 - ip.z, maxij - 2) -
maxij * shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
int maxij = std::max(i, j);
for (int k = 0; k <= p - maxij; k++, o++)
{
if (i == 0 && j == 0)
{
du(o,2) = (((k + 6.) * k + 11.) * k + 6.) * k / 6.;
}
else if (i == 1 && j == 0)
{
du(o,0) = ((((k + 10.) * k + 35.) * k + 50.) * k + 24.) / 24.;
}
else if (i == 0 && j == 1)
{
du(o,1) = ((((k + 10.) * k + 35.) * k + 50.) * k + 24.) / 24.;
}
}
}
}
}
else
{
poly1d.CalcLegendre(p, x, shape_x.GetData(), dshape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData(), dshape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0,
shape_z, dshape_z, dshape_z_dt);
for (int k = 0; k <= p - maxij; k++, o++)
{
du(o,0) = dshape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,1) = shape_x(i) * dshape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,2) = shape_x(i) * shape_y(j) * dshape_z(k) *
pow(1.0 - ip.z, maxij) +
(ip.x * dshape_x(i) * shape_y(j) +
ip.y * shape_x(i) * dshape_y(j)) *
shape_z(k) * pow(1.0 - ip.z, maxij - 2) -
maxij * shape_x(i) * shape_y(j) * shape_z(k) *
(maxij > 0 ? pow(1.0 - ip.z, maxij - 1) : 0.0);
}
}
}
Ti.Mult(du, dshape);
}
+2
View File
@@ -208,6 +208,8 @@ private:
#endif
DenseMatrixInverse Ti;
static constexpr real_t apex_tol = 1e-8;
public:
H1_BergotPyramidElement(const int p,
const int btype = BasisType::GaussLobatto);
+130 -56
View File
@@ -1106,9 +1106,16 @@ L2_BergotPyramidElement::L2_BergotPyramidElement(const int p, const int btype)
{
const real_t wik = op[i] + op[k] + op[p-i-k];
const real_t w = wik * wjk * op[p-k];
Nodes.IntPoint(o++).Set3(op[i] * (op[j] + op[p-j-k]) / w,
op[j] * (op[j] + op[p-j-k]) / w,
op[k] * op[p-k] / w);
if (std::abs(w) < apex_tol)
{
Nodes.IntPoint(o++).Set3(0.,0.,1.);
}
else
{
Nodes.IntPoint(o++).Set3(op[i] * (op[j] + op[p-j-k]) / w,
op[j] * (op[i] + op[p-i-k]) / w,
op[k] * op[p-k] / w);
}
}
}
@@ -1125,22 +1132,45 @@ L2_BergotPyramidElement::L2_BergotPyramidElement(const int p, const int btype)
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
const real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
o = 0;
for (int i = 0; i <= p; i++)
if (std::abs(z - 1.0) < apex_tol)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0, shape_z);
for (int k = 0; k <= p - maxij; k++)
// Compute the limit of the basis functions as z->1 with x and y on the
// line between the center of the base and the apex
o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
T(o++, m) = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
int maxij = std::max(i, j);
for (int k = 0; k <= p - maxij; k++)
if (i == 0 && j == 0)
{
T(o++, m) = ((k + 3.) * k + 2.) / 2.;
}
else
{
T(o++, m) = 0.;
}
}
}
else
{
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
o = 0;
for (int i = 0; i <= p; i++)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0, shape_z);
for (int k = 0; k <= p - maxij; k++)
{
T(o++, m) = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
}
}
}
}
@@ -1165,26 +1195,41 @@ void L2_BergotPyramidElement::CalcShape(const IntegrationPoint &ip,
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
const real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
if (std::abs(z - 1.0) < apex_tol)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
shape_z);
for (int k = 0; k <= p - maxij; k++)
// Compute the limit of the basis functions as z->1 with x and y on the
// line between the center of the base and the apex
u = 0.;
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
u[o++] = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
int maxij = std::max(i, j);
for (int k = 0; k <= p - maxij; k++, o++)
if (i == 0 && j == 0)
{
u(o) = ((k + 3.) * k + 2.) / 2.;
}
}
}
}
else
{
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0, shape_z);
for (int k = 0; k <= p - maxij; k++)
u[o++] = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
}
}
Ti.Mult(u, shape);
}
@@ -1208,35 +1253,64 @@ void L2_BergotPyramidElement::CalcDShape(const IntegrationPoint &ip,
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
const real_t z = ip.z;
Poly_1D::CalcLegendre(p, x, shape_x.GetData(), dshape_x.GetData());
Poly_1D::CalcLegendre(p, y, shape_y.GetData(), dshape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
if (std::abs(z - 1.0) < apex_tol)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
shape_z, dshape_z, dshape_z_dt);
for (int k = 0; k <= p - maxij; k++, o++)
// Compute the limit of the gradients of the basis functions as
// z->1 with x and y on the line between the center of the base and the
// apex
du = 0.;
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
du(o,0) = dshape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,1) = shape_x(i) * dshape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,2) = shape_x(i) * shape_y(j) * dshape_z(k) *
pow(1.0 - ip.z, maxij) +
(ip.x * dshape_x(i) * shape_y(j) +
ip.y * shape_x(i) * dshape_y(j)) *
shape_z(k) * pow(1.0 - ip.z, maxij - 2) -
((maxij > 0) ? (maxij * shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1)) : 0.0);
int maxij = std::max(i, j);
for (int k = 0; k <= p - maxij; k++, o++)
{
if (i == 0 && j == 0)
{
du(o,2) = (((k + 6.) * k + 11.) * k + 6.) * k / 6.;
}
else if (i == 1 && j == 0)
{
du(o,0) = ((((k + 10.) * k + 35.) * k + 50.) * k + 24.) / 24.;
}
else if (i == 0 && j == 1)
{
du(o,1) = ((((k + 10.) * k + 35.) * k + 50.) * k + 24.) / 24.;
}
}
}
}
}
else
{
Poly_1D::CalcLegendre(p, x, shape_x.GetData(), dshape_x.GetData());
Poly_1D::CalcLegendre(p, y, shape_y.GetData(), dshape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0,
shape_z, dshape_z, dshape_z_dt);
for (int k = 0; k <= p - maxij; k++, o++)
{
du(o,0) = dshape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,1) = shape_x(i) * dshape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,2) = shape_x(i) * shape_y(j) * dshape_z(k) *
pow(1.0 - ip.z, maxij) +
(ip.x * dshape_x(i) * shape_y(j) +
ip.y * shape_x(i) * dshape_y(j)) *
shape_z(k) * pow(1.0 - ip.z, maxij - 2) -
maxij * shape_x(i) * shape_y(j) * shape_z(k) *
(maxij > 0 ? pow(1.0 - ip.z, maxij - 1) : 0.0);
}
}
}
Ti.Mult(du, dshape);
}
+2
View File
@@ -225,6 +225,8 @@ private:
#endif
DenseMatrixInverse Ti;
static constexpr real_t apex_tol = 1e-8;
public:
/// Construct the L2_PyramidElement of order @a p and BasisType @a btype
L2_BergotPyramidElement(const int p,
+38 -1
View File
@@ -1282,12 +1282,49 @@ ND_SegmentElement::ND_SegmentElement(const int p, const int ob_type)
}
}
void ND_SegmentElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
if (obasis1d.IsIntegratedType()) { obasis1d.ScaleIntegrated(false); }
obasis1d.Eval(ip.x, shape);
}
void ND_SegmentElement::CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const
{
Vector vshape(shape.Data(), dof);
obasis1d.Eval(ip.x, vshape);
CalcShape(ip, vshape);
}
void ND_SegmentElement::ProjectIntegrated(VectorCoefficient &vc,
ElementTransformation &Trans,
Vector &dofs) const
{
MFEM_ASSERT(obasis1d.IsIntegratedType(), "Not integrated type");
real_t vk[Geometry::MaxDim];
Vector xk(vk, vc.GetVDim());
const real_t *cp = poly1d.ClosedPoints(dof, BasisType::GaussLobatto);
const IntegrationRule &ir = IntRules.Get(Geometry::SEGMENT, dof);
IntegrationPoint ip;
for (int i = 0; i < dof; i++)
{
const real_t h = cp[i+1] - cp[i];
real_t val = 0.0;
for (int q = 0; q < ir.GetNPoints(); q++)
{
const IntegrationPoint &ip1d = ir.IntPoint(q);
ip.x = cp[i] + h*ip1d.x;
Trans.SetIntPoint(&ip);
vc.Eval(xk, Trans, ip);
val += ip1d.weight*Trans.Jacobian().InnerProduct(tk, vk);
}
dofs(i) = val*h;
}
}
const real_t ND_WedgeElement::tk[15] =
+10 -3
View File
@@ -303,8 +303,7 @@ public:
/** @brief Construct the ND_SegmentElement of order @a p and open
BasisType @a ob_type */
ND_SegmentElement(const int p, const int ob_type = BasisType::GaussLegendre);
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override
{ obasis1d.Eval(ip.x, shape); }
void CalcShape(const IntegrationPoint &ip, Vector &shape) const override;
void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const override;
void CalcVShape(ElementTransformation &Trans,
@@ -325,7 +324,10 @@ public:
using FiniteElement::Project;
void Project(VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const override
{ Project_ND(tk, dof2tk, vc, Trans, dofs); }
{
if (obasis1d.IsIntegratedType()) { ProjectIntegrated(vc, Trans, dofs); }
else { Project_ND(tk, dof2tk, vc, Trans, dofs); }
}
void ProjectMatrixCoefficient(MatrixCoefficient &mc,
ElementTransformation &T,
Vector &dofs) const override
@@ -338,6 +340,11 @@ public:
ElementTransformation &Trans,
DenseMatrix &grad) const override
{ ProjectGrad_ND(tk, dof2tk, fe, Trans, grad); }
protected:
void ProjectIntegrated(VectorCoefficient &vc,
ElementTransformation &Trans,
Vector &dofs) const;
};
class ND_WedgeElement : public VectorFiniteElement
+6
View File
@@ -17,6 +17,12 @@
namespace mfem
{
struct ScalarPyramid
{
// Default basis type for H1 and L2 pyramids
static inline int DefaultType = 1; // Bergot(0) or Fuentes(1)
};
/** Base class for arbitrary order basis functions on pyramid-shaped elements
This base class provides a common class to store temporary vectors,
+6 -16
View File
@@ -73,16 +73,11 @@ public:
void Project(const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const override
{ Project_RT(nk, dof2nk, fe, Trans, I); }
// Gradient + rotation = Curl: H1 -> H(div)
void ProjectGrad(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &grad) const override
{ ProjectGrad_RT(nk, dof2nk, fe, Trans, grad); }
// Curl = Gradient + rotation: H1 -> H(div)
void ProjectCurl(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const override
{ ProjectGrad_RT(nk, dof2nk, fe, Trans, curl); }
{ ProjectCurl2D_RT(nk, dof2nk, fe, Trans, curl); }
void GetFaceMap(const int face_id, Array<int> &face_map) const override;
@@ -148,7 +143,7 @@ public:
void ProjectCurl(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const override
{ ProjectCurl_RT(nk, dof2nk, fe, Trans, curl); }
{ ProjectCurl3D_RT(nk, dof2nk, fe, Trans, curl); }
/// @brief Return the mapping from lexicographically ordered face DOFs to
/// lexicographically ordered element DOFs corresponding to local face
@@ -210,16 +205,11 @@ public:
void Project(const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const override
{ Project_RT(nk, dof2nk, fe, Trans, I); }
// Gradient + rotation = Curl: H1 -> H(div)
void ProjectGrad(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &grad) const override
{ ProjectGrad_RT(nk, dof2nk, fe, Trans, grad); }
// Curl = Gradient + rotation: H1 -> H(div)
void ProjectCurl(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const override
{ ProjectGrad_RT(nk, dof2nk, fe, Trans, curl); }
{ ProjectCurl2D_RT(nk, dof2nk, fe, Trans, curl); }
};
@@ -274,7 +264,7 @@ public:
void ProjectCurl(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const override
{ ProjectCurl_RT(nk, dof2nk, fe, Trans, curl); }
{ ProjectCurl3D_RT(nk, dof2nk, fe, Trans, curl); }
};
class RT_WedgeElement : public VectorFiniteElement
@@ -332,7 +322,7 @@ public:
void ProjectCurl(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const override
{ ProjectCurl_RT(nk, dof2nk, fe, Trans, curl); }
{ ProjectCurl3D_RT(nk, dof2nk, fe, Trans, curl); }
};
/** Arbitrary order H(Div) basis functions defined on pyramid-shaped elements
@@ -428,7 +418,7 @@ public:
virtual void ProjectCurl(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const
{ ProjectCurl_RT(nk, dof2nk, fe, Trans, curl); }
{ ProjectCurl3D_RT(nk, dof2nk, fe, Trans, curl); }
void CalcRawVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const;
+88 -30
View File
@@ -228,7 +228,19 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
}
else if (!strncmp(name, "H1_", 3))
{
fec = new H1_FECollection(atoi(name + 7), atoi(name + 3));
// Parse pyramid basis type if included in the name
const char *pyr = strstr(name, "Pyr");
if (pyr == NULL)
{
// Use default pyramid type elements
fec = new H1_FECollection(atoi(name + 7), atoi(name + 3));
}
else
{
// Use specific pyramid type elements
fec = new H1_FECollection(atoi(name + 7), atoi(name + 3),
BasisType::GaussLobatto, atoi(pyr + 3));
}
}
else if (!strncmp(name, "H1Pos_Trace_", 12))
{
@@ -245,26 +257,44 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
}
else if (!strncmp(name, "H1@", 3))
{
fec = new H1_FECollection(atoi(name + 9), atoi(name + 5),
BasisType::GetType(name[3]));
// Parse pyramid basis type if included in the name
const char *pyr = strstr(name, "Pyr");
if (pyr == NULL)
{
// Use default pyramid type elements
fec = new H1_FECollection(atoi(name + 9), atoi(name + 5),
BasisType::GetType(name[3]));
}
else
{
// Use specific pyramid type elements
fec = new H1_FECollection(atoi(name + 9), atoi(name + 5),
BasisType::GetType(name[3]),
atoi(pyr + 3));
}
}
else if (!strncmp(name, "L2_T", 4))
fec = new L2_FECollection(atoi(name + 10), atoi(name + 6),
atoi(name + 4));
else if (!strncmp(name, "L2_", 3))
else if (!strncmp(name, "L2", 2))
{
fec = new L2_FECollection(atoi(name + 7), atoi(name + 3));
}
else if (!strncmp(name, "L2Int_T", 7))
{
fec = new L2_FECollection(atoi(name + 13), atoi(name + 9),
atoi(name + 7), FiniteElement::INTEGRAL);
}
else if (!strncmp(name, "L2Int_", 6))
{
fec = new L2_FECollection(atoi(name + 10), atoi(name + 6),
BasisType::GaussLegendre,
FiniteElement::INTEGRAL);
// Parse Map Type
const int mtype = strstr(name, "Int") == NULL ?
FiniteElement::VALUE : FiniteElement::INTEGRAL;
// Parse the base order
const int p = atoi(strstr(name, "_P") + 2);
// Parse the mesh dimension
const int dim = atoi(strstr(name, "D") - 1);
// Parse basis type if specified
const char *t = strstr(name, "_T");
const int btype = t == NULL ? BasisType::GaussLegendre : atoi(t + 2);
// Parse the pyramid type if specified
const char *pyr = strstr(name, "Pyr");
const int ptype = pyr == NULL ? 1 : atoi(pyr + 3);
// Create collection
fec = new L2_FECollection(p, dim, btype, mtype, ptype);
}
else if (!strncmp(name, "RT_Trace_", 9))
{
@@ -1709,9 +1739,10 @@ const int *RT1_3DFECollection::DofOrderForOrientation(Geometry::Type GeomType,
H1_FECollection::H1_FECollection(const int p, const int dim, const int btype,
const int pyrtype)
const int pyr_type)
: FiniteElementCollection(p)
, dim(dim)
, p_type(pyr_type)
{
MFEM_VERIFY(p >= 1, "H1_FECollection requires order >= 1.");
MFEM_VERIFY(dim >= 0 && dim <= 3, "H1_FECollection requires 0 <= dim <= 3.");
@@ -1724,7 +1755,14 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype,
{
case BasisType::GaussLobatto:
{
snprintf(h1_name, 32, "H1_%dD_P%d", dim, p);
if (pyr_type == ScalarPyramid::DefaultType)
{
snprintf(h1_name, 32, "H1_%dD_P%d", dim, p);
}
else
{
snprintf(h1_name, 32, "H1_%dD_P%d_Pyr%d", dim, p, pyr_type);
}
break;
}
case BasisType::Positive:
@@ -1910,11 +1948,11 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype,
H1_dof[Geometry::TETRAHEDRON] = (TriDof*pm3)/3;
H1_dof[Geometry::CUBE] = QuadDof*pm1;
H1_dof[Geometry::PRISM] = TriDof*pm1;
if (pyrtype == 0 || b_type == BasisType::Positive)
if (pyr_type == 0 || b_type == BasisType::Positive)
{
H1_dof[Geometry::PYRAMID] = pm2*pm1*(2*p-3)/6; // Bergot (JSC)
}
else if (pyrtype == 1)
else if (pyr_type == 1)
{
H1_dof[Geometry::PYRAMID] = pm1*pm1*pm1; // Fuentes
}
@@ -1935,13 +1973,15 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype,
new H1_TetrahedronElement(p, btype);
H1_Elements[Geometry::CUBE] = new H1_HexahedronElement(p, btype);
H1_Elements[Geometry::PRISM] = new H1_WedgeElement(p, btype);
if (pyrtype == 0)
if (pyr_type == 0)
{
H1_Elements[Geometry::PYRAMID] = new H1_BergotPyramidElement(p, btype);
H1_Elements[Geometry::PYRAMID] =
new H1_BergotPyramidElement(p, btype);
}
else
{
H1_Elements[Geometry::PYRAMID] = new H1_FuentesPyramidElement(p, btype);
H1_Elements[Geometry::PYRAMID] =
new H1_FuentesPyramidElement(p, btype);
}
}
@@ -2148,6 +2188,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
: FiniteElementCollection(p)
, dim(dim)
, m_type(map_type)
, p_type(pyr_type)
{
MFEM_VERIFY(p >= 0, "L2_FECollection requires order >= 0.");
@@ -2163,10 +2204,25 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
switch (btype)
{
case BasisType::GaussLegendre:
snprintf(d_name, 32, "%s_%dD_P%d", prefix, dim, p);
if (pyr_type == ScalarPyramid::DefaultType)
{
snprintf(d_name, 32, "%s_%dD_P%d", prefix, dim, p);
}
else
{
snprintf(d_name, 32, "%s_%dD_P%d_Pyr%d", prefix, dim, p, pyr_type);
}
break;
default:
snprintf(d_name, 32, "%s_T%d_%dD_P%d", prefix, btype, dim, p);
if (pyr_type == ScalarPyramid::DefaultType)
{
snprintf(d_name, 32, "%s_T%d_%dD_P%d", prefix, btype, dim, p);
}
else
{
snprintf(d_name, 32, "%s_T%d_%dD_P%d_Pyr%d",
prefix, btype, dim, p, pyr_type);
}
}
for (int g = 0; g < Geometry::NumGeom; g++)
@@ -2285,11 +2341,13 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
L2_Elements[Geometry::PRISM] = new L2_WedgeElement(p, btype);
if (pyr_type == 0)
{
L2_Elements[Geometry::PYRAMID] = new L2_BergotPyramidElement(p, btype);
L2_Elements[Geometry::PYRAMID] =
new L2_BergotPyramidElement(p, btype);
}
else
{
L2_Elements[Geometry::PYRAMID] = new L2_FuentesPyramidElement(p, btype);
L2_Elements[Geometry::PYRAMID] =
new L2_FuentesPyramidElement(p, btype);
}
}
+10 -5
View File
@@ -100,6 +100,10 @@ public:
return FiniteElementForGeometry(GeomType);
}
/** @brief Returns a collection of the trace elements.
@note The collection is owned by the caller and is NOT deleted in the
destructor. */
virtual FiniteElementCollection *GetTraceCollection() const;
virtual ~FiniteElementCollection();
@@ -286,7 +290,7 @@ protected:
class H1_FECollection : public FiniteElementCollection
{
protected:
int dim, b_type;
int dim, b_type, p_type;
char h1_name[32];
FiniteElement *H1_Elements[Geometry::NumGeom];
int H1_dof[Geometry::NumGeom];
@@ -295,7 +299,7 @@ protected:
public:
explicit H1_FECollection(const int p, const int dim = 3,
const int btype = BasisType::GaussLobatto,
const int pyrtype = 1);
const int pyr_type = ScalarPyramid::DefaultType);
const FiniteElement *
FiniteElementForGeometry(Geometry::Type GeomType) const override;
@@ -320,7 +324,7 @@ public:
const int *GetDofMap(Geometry::Type GeomType, int p) const;
FiniteElementCollection *Clone(int p) const override
{ return new H1_FECollection(p, dim, b_type); }
{ return new H1_FECollection(p, dim, b_type, p_type); }
int GetConstructorOrder() const override
{ return base_p; }
@@ -367,6 +371,7 @@ private:
int dim;
int b_type; // BasisType
int m_type; // map type
int p_type; // Pyramid type (0 -> Bergot, 1 -> Fuentes)
char d_name[32];
ScalarFiniteElement *L2_Elements[Geometry::NumGeom];
ScalarFiniteElement *Tr_Elements[Geometry::NumGeom];
@@ -379,7 +384,7 @@ public:
L2_FECollection(const int p, const int dim,
const int btype = BasisType::GaussLegendre,
const int map_type = FiniteElement::VALUE,
const int pyrtype = 1);
const int pyr_type = ScalarPyramid::DefaultType);
const FiniteElement *
FiniteElementForGeometry(Geometry::Type GeomType) const override;
@@ -409,7 +414,7 @@ public:
int GetBasisType() const { return b_type; }
FiniteElementCollection *Clone(int p) const override
{ return new L2_FECollection(p, dim, b_type, m_type); }
{ return new L2_FECollection(p, dim, b_type, m_type, p_type); }
int GetConstructorOrder() const override
{ return base_p; }
+206
View File
@@ -22,6 +22,8 @@
#include <algorithm>
#include <cmath>
#include <cstdarg>
#include <unordered_map>
#include <unordered_set>
using namespace std;
@@ -4527,6 +4529,210 @@ void FiniteElementSpace
}
}
void FiniteElementSpace::GetBoundaryLoopEdgeDofs(
const Array<int> &boundary_element_indices,
Array<int> &boundary_edge_dofs,
Array<int> *dof_edges,
Array<int> *dof_boundary_elements) const
{
MFEM_VERIFY(mesh->Dimension() >= 2,
"GetBoundaryLoopEdgeDofs requires 2D or 3D meshes to find edge objects");
boundary_edge_dofs.SetSize(0);
if (dof_edges) { dof_edges->SetSize(0); }
if (dof_boundary_elements) { dof_boundary_elements->SetSize(0); }
// A DOF that appears in exactly one selected boundary element lies on the
// bounding loop; one appearing in two or more is interior to the boundary
// region and is dropped. Count occurrences of each DOF (using scratch maps,
// exposed only as parallel-indexed Array<int> below) and record, on first
// sight, the local edge and boundary element carrying it.
//
// The count is over GetEdgeDofs, which returns endpoint vertex DOFs as well
// as edge-interior DOFs (relevant for collections such as ND_R2D that carry
// vertex DOFs). Edge-interior DOFs occur once per edge, so the count mainly
// resolves vertex DOFs: a vertex shared by several elements is interior and
// dropped, while a genuine loop-corner (open-curve endpoint) vertex is kept.
// This is why we count GetEdgeDofs rather than collecting GetEdgeInteriorDofs,
// which would omit the endpoint vertex DOFs the method is documented to keep.
// The 3D removal criterion (any edge in two or more faces) matches the
// parallel version rather than a parity toggle.
std::unordered_map<int, int> dof_count, dof_edge, dof_belem;
Array<int> edge_dofs, edges, edge_orientations;
const int dim = mesh->Dimension();
for (int i = 0; i < boundary_element_indices.Size(); ++i)
{
const int boundary_element_idx = boundary_element_indices[i];
std::unordered_set<int> boundary_element_dofs;
if (dim == 3)
{
// Boundary elements are 2D faces; extract their 1D edges.
int face_index, face_orientation;
mesh->GetBdrElementFace(boundary_element_idx, &face_index,
&face_orientation);
mesh->GetFaceEdges(face_index, edges, edge_orientations);
}
else
{
// Boundary elements are 1D segments, each being a single edge.
mesh->GetBdrElementEdges(boundary_element_idx, edges, edge_orientations);
MFEM_VERIFY(edges.Size() == 1,
"2D boundary element should have exactly one edge");
}
for (int j = 0; j < edges.Size(); ++j)
{
GetEdgeDofs(edges[j], edge_dofs);
for (int k = 0; k < edge_dofs.Size(); ++k)
{
const int dof = edge_dofs[k];
// Count each DOF once per boundary element and record metadata the
// first time it is seen, so H1 DOFs shared by multiple edges of the
// same element are not double counted.
if (boundary_element_dofs.insert(dof).second &&
dof_count[dof]++ == 0)
{
dof_edge[dof] = edges[j];
dof_belem[dof] = boundary_element_idx;
}
}
}
}
// Emit the DOFs seen in exactly one selected boundary element, in a
// deterministic (increasing DOF index) order shared by all output arrays.
std::vector<int> kept;
kept.reserve(dof_count.size());
for (const auto &[dof, count] : dof_count)
{
if (count == 1) { kept.push_back(dof); }
}
std::sort(kept.begin(), kept.end());
boundary_edge_dofs.Reserve(static_cast<int>(kept.size()));
if (dof_edges) { dof_edges->Reserve(static_cast<int>(kept.size())); }
if (dof_boundary_elements)
{
dof_boundary_elements->Reserve(static_cast<int>(kept.size()));
}
for (int dof : kept)
{
boundary_edge_dofs.Append(dof);
if (dof_edges) { dof_edges->Append(dof_edge[dof]); }
if (dof_boundary_elements) { dof_boundary_elements->Append(dof_belem[dof]); }
}
}
void FiniteElementSpace::GetBoundaryElementsByAttribute(
const Array<int> &bdr_attrs,
std::vector<Array<int>> &attr_to_elements)
{
// One (initially empty) list of boundary elements per requested attribute,
// indexed to match bdr_attrs.
attr_to_elements.assign(bdr_attrs.Size(), Array<int>());
// Map attribute value -> position in bdr_attrs for quick lookup.
std::unordered_map<int, int> attr_to_index;
for (int i = 0; i < bdr_attrs.Size(); ++i)
{
attr_to_index[bdr_attrs[i]] = i;
}
// Bucket boundary elements by their attribute.
for (int i = 0; i < mesh->GetNBE(); ++i)
{
int attr = mesh->GetBdrElement(i)->GetAttribute();
auto it = attr_to_index.find(attr);
if (it != attr_to_index.end())
{
attr_to_elements[it->second].Append(i);
}
}
}
void FiniteElementSpace::GetBoundaryElementsByAttribute(int bdr_attr,
Array<int> &boundary_elements)
{
boundary_elements.SetSize(0);
for (int i = 0; i < mesh->GetNBE(); ++i)
{
if (mesh->GetBdrElement(i)->GetAttribute() == bdr_attr)
{
boundary_elements.Append(i);
}
}
}
void FiniteElementSpace::ComputeLoopEdgeOrientations(
const Array<int> &dof_edges,
const Array<int> &dof_boundary_elements,
const Vector &loop_normal,
Array<int> &dof_orientations) const
{
MFEM_VERIFY(dof_edges.Size() == dof_boundary_elements.Size(),
"dof_edges and dof_boundary_elements must be parallel-indexed");
const int ndof = dof_edges.Size();
dof_orientations.SetSize(ndof);
Array<int> edge_verts, bdr_elem_verts;
Vector edge_vec(3), to_edge_vec(3), cross_product(3);
for (int i = 0; i < ndof; i++)
{
const int edge_id = dof_edges[i];
const int bdr_elem_idx = dof_boundary_elements[i];
// Get edge vertices
mesh->GetEdgeVertices(edge_id, edge_verts);
const real_t *v0 = mesh->GetVertex(edge_verts[0]);
const real_t *v1 = mesh->GetVertex(edge_verts[1]);
// Get boundary element vertices
mesh->GetBdrElement(bdr_elem_idx)->GetVertices(bdr_elem_verts);
// Find the third vertex (not part of the edge)
int third_vertex = -1;
for (int j = 0; j < bdr_elem_verts.Size(); j++)
{
int v = bdr_elem_verts[j];
if (v != edge_verts[0] && v != edge_verts[1])
{
third_vertex = v;
break;
}
}
if (third_vertex == -1)
{
MFEM_ABORT("Boundary element " << bdr_elem_idx << " has only 2 vertices, "
"but 3D boundary elements must have at least 3 vertices");
}
const real_t *v2 = mesh->GetVertex(third_vertex);
// Edge vector
for (int j = 0; j < 3; j++) { edge_vec[j] = v1[j] - v0[j]; }
// Vector from third vertex to edge (use edge midpoint)
for (int j = 0; j < 3; j++)
{
real_t edge_midpoint = (v0[j] + v1[j]) * 0.5;
to_edge_vec[j] = edge_midpoint - v2[j];
}
// Cross product: to_edge × edge
to_edge_vec.cross3D(edge_vec, cross_product);
// Check alignment with loop normal
real_t dot_product = cross_product * loop_normal;
dof_orientations[i] = (dot_product > 0) ? 1 : -1;
}
}
FiniteElementCollection *FiniteElementSpace::Load(Mesh *m, std::istream &input)
{
string buff;
+75
View File
@@ -22,6 +22,7 @@
#include "restriction.hpp"
#include <iostream>
#include <unordered_map>
#include <vector>
namespace mfem
{
@@ -1389,6 +1390,80 @@ public:
virtual void GetExteriorTrueDofs(Array<int> &exterior_dofs,
int component = -1) const;
/** @brief Extract the edge degrees of freedom of a boundary "loop".
Here a "loop" is the set of boundary edges bounding the region covered by
@a boundary_element_indices: in 3D the outer edges of a patch of boundary
faces, in 2D the boundary segments themselves. An edge that is shared by
two (or more) of the selected boundary elements is interior to that region
rather than on its bounding loop, so its DOFs are excluded from the result.
This exclusion of interior DOFs is the defining feature of the method.
The three output arrays share a single indexing: for each valid index @a i,
@a dof_edges[i] and @a dof_boundary_elements[i] describe the DOF
@a boundary_edge_dofs[i].
@param[in] boundary_element_indices Boundary element indices spanning a
boundary surface (3D) or curve (2D).
@param[out] boundary_edge_dofs Local DOF indices on the boundary loop.
@param[out] dof_edges Optional; local edge index carrying each DOF.
@param[out] dof_boundary_elements Optional; a boundary element containing
each DOF.
@note In 3D the edge DOFs are extracted from the 1D edges of the 2D
boundary faces; in 2D they come directly from the 1D boundary segments, so
@a dof_edges then holds the boundary element (segment) edge indices.
@note This method uses GetEdgeDofs internally, which returns both vertex and
edge DOFs. Standard Nédélec elements (ND_FECollection) have no vertex DOFs,
so only genuine edge DOFs appear. Collections that carry vertex DOFs (e.g.
ND_R2D_FECollection) additionally contribute the vertex DOFs at loop
endpoints.
@note This is the serial version. For parallel meshes, use the parallel
version in ParFiniteElementSpace which handles processor boundaries
correctly.
@note Requires a 2D or 3D mesh to identify edge objects. The method will
assert if called on 1D meshes.
@note Only supports conforming meshes; non-conforming meshes are not
supported. */
void GetBoundaryLoopEdgeDofs(const Array<int> &boundary_element_indices,
Array<int> &boundary_edge_dofs,
Array<int> *dof_edges = nullptr,
Array<int> *dof_boundary_elements = nullptr) const;
/** @brief Get boundary elements grouped by attribute.
For each attribute in @a bdr_attrs, collect the indices of all boundary
elements carrying that attribute. The result is indexed to match
@a bdr_attrs: @a attr_to_elements[i] holds the boundary elements with
attribute @a bdr_attrs[i]. */
void GetBoundaryElementsByAttribute(
const Array<int> &bdr_attrs,
std::vector<Array<int>> &attr_to_elements);
/** @brief Get all boundary elements with a specific attribute. */
void GetBoundaryElementsByAttribute(int bdr_attr,
Array<int> &boundary_elements);
/** @brief Compute edge orientations relative to a boundary loop direction.
For each boundary-loop DOF described by @a dof_edges and
@a dof_boundary_elements (see GetBoundaryLoopEdgeDofs), determine whether
the carrying edge is
traversed in the direction consistent with @a loop_normal, following the
right-hand rule. Intended for 3D meshes.
@param[in] dof_edges Local edge index of each DOF (parallel-indexed with
the boundary_edge_dofs output of GetBoundaryLoopEdgeDofs).
@param[in] dof_boundary_elements A boundary element containing each DOF,
using the same indexing as @a dof_edges.
@param[in] loop_normal Normal vector defining the loop orientation.
@param[out] dof_orientations Orientation (+1 or -1) for each DOF, using the
same indexing as @a dof_edges. */
void ComputeLoopEdgeOrientations(const Array<int> &dof_edges,
const Array<int> &dof_boundary_elements,
const Vector &loop_normal,
Array<int> &dof_orientations) const;
/// Convert a Boolean marker array to a list containing all marked indices.
static void MarkerToList(const Array<int> &marker, Array<int> &list);
+1236 -728
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;
}
}
+2
View File
@@ -178,6 +178,8 @@ void ConvectionIntegrator::AssemblePA(const FiniteElementSpace &fes)
// Assumes tensor-product elements
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetTypicalFE();
MFEM_VERIFY(el.GetMapType() == FiniteElement::VALUE,
"Only value map type currently supported");
ElementTransformation &Trans = *mesh->GetTypicalElementTransformation();
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, Trans);
if (DeviceCanUseCeed())
+3 -3
View File
@@ -91,15 +91,15 @@ void ElasticityAddMultPA(const int dim, const int nDofs,
void ElasticityAssembleDiagonalPA(const int dim, const int nDofs,
const CoefficientVector &lambda,
const CoefficientVector &mu, const GeometricFactors &geom,
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
const DofToQuad &maps, const IntegrationRule &ir, Vector &diag)
{
switch (dim)
{
case 2:
ElasticityAssembleDiagonalPA_<2>(nDofs, lambda, mu, geom, maps, QVec, diag);
ElasticityAssembleDiagonalPA_<2>(nDofs, lambda, mu, geom, maps, ir, diag);
break;
case 3:
ElasticityAssembleDiagonalPA_<3>(nDofs, lambda, mu, geom, maps, QVec, diag);
ElasticityAssembleDiagonalPA_<3>(nDofs, lambda, mu, geom, maps, ir, diag);
break;
default:
MFEM_ABORT("Only dimensions 2 and 3 supported.");
+44 -55
View File
@@ -38,7 +38,6 @@
#include "../../linalg/vector.hpp"
#include "../../linalg/tensor.hpp"
#include "../quadinterpolator.hpp"
#include "../bilininteg.hpp"
#include "../coefficient.hpp"
#include "../qfunction.hpp"
@@ -133,12 +132,12 @@ void ElasticityAssembleEA(const int dim, const int i_block, const int j_block,
/// @param[in] mu Quadrature function for second Lame param.
/// @param[in] geom Geometric factors corresponding to fespace.
/// @param[in] maps DofToQuad maps for one element (assume elements all same).
/// @param QVec Scratch Q-Vector. nQuad x dim x dim x dim x dim x numEls.
/// @param[in] ir Integration rule.
/// @param[out] diag diagonal of A. nDofs x dim x numEls.
void ElasticityAssembleDiagonalPA(const int dim, const int nDofs,
const CoefficientVector &lambda,
const CoefficientVector &mu, const GeometricFactors &geom,
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag);
const DofToQuad &maps, const IntegrationRule &ir, Vector &diag);
/// Templated implementation of ElasticityAddMultPA.
template<int dim, int i_block = -1, int j_block = -1>
@@ -280,77 +279,67 @@ void ElasticityAddMultPA_(const int nDofs, const FiniteElementSpace &fespace,
template<int dim>
void ElasticityAssembleDiagonalPA_(const int nDofs,
const CoefficientVector &lambda,
const CoefficientVector &mu, const GeometricFactors &geom,
const DofToQuad &maps, QuadratureFunction &QVec, Vector &diag)
const CoefficientVector &mu,
const GeometricFactors &geom,
const DofToQuad &maps,
const IntegrationRule &ir,
Vector &diag)
{
using future::tensor;
using future::make_tensor;
using future::det;
using future::inv;
using future::make_tensor;
using future::tensor;
// Assuming all elements are the same
const auto &ir = QVec.GetIntRule(0);
static constexpr int d = dim;
const int numPoints = ir.GetNPoints();
const int numEls = lambda.Size()/numPoints;
const int numEls = lambda.Size() / numPoints;
const auto lamDev = Reshape(lambda.Read(), numPoints, numEls);
const auto muDev = Reshape(mu.Read(), numPoints, numEls);
const auto J = Reshape(geom.J.Read(), numPoints, d, d, numEls);
auto Q = Reshape(QVec.ReadWrite(), numPoints, d,d, d, numEls);
const real_t *ipWeights = ir.GetWeights().Read();
mfem::forall_2D(numEls, numPoints,1, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(p, x,numPoints)
{
auto invJ = inv(make_tensor<d, d>(
[&](int i, int j) { return J(p, i, j, e); }));
const real_t w = ipWeights[p] /det(invJ);
for (int n = 0; n < d; n++)
{
for (int m = 0; m < d; m++)
{
for (int q = 0; q < d; q++)
{
// compute contraction of 4*sym(grad(u))sym(grad(v)) term.
// this contraction could be made slightly cheaper using Voigt
// notation, but repeated entries are summed for simplicity.
real_t contraction = 0.;
for (int a = 0; a < d; a++)
{
for (int b = 0; b < d; b++)
{
contraction += ((a == q)*invJ(m,b) + (b==q)*invJ(m,a))*((a == q)
*invJ(n, b) + (b==q)*invJ(n,a));
}
}
// lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
// contraction = 4*sym(grad(u))sym(grad(v))
Q(p,m,n,q,e) = w*(lamDev(p, e)*invJ(m,q)*invJ(n,q)
+ 0.5*muDev(p, e)*contraction);
}
}
}
}
});
// Reduce quadrature function to an E-Vector
const auto QRead = Reshape(QVec.Read(), numPoints, d, d, d, numEls);
auto diagDev = Reshape(diag.Write(), nDofs, d, numEls);
const auto G = Reshape(maps.G.Read(), numPoints, d, nDofs);
auto diagDev = Reshape(diag.Write(), nDofs, d, numEls);
mfem::forall_2D(numEls, d, nDofs, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(i, y, nDofs)
MFEM_FOREACH_THREAD_DIRECT(i, y, nDofs)
{
MFEM_FOREACH_THREAD(q, x, d)
MFEM_FOREACH_THREAD_DIRECT(q, x, d)
{
real_t sum = 0.;
for (int n = 0; n < d; n++)
real_t sum = 0.0;
for (int p = 0; p < numPoints; p++)
{
for (int m = 0; m < d; m++)
const auto invJ = inv(make_tensor<d, d>([&](int r, int c)
{
for (int p = 0; p < numPoints; p++ )
return J(p, r, c, e);
}));
const real_t w = ipWeights[p] / det(invJ);
for (int n = 0; n < d; n++)
{
for (int m = 0; m < d; m++)
{
sum += QRead(p,m,n,q,e)*G(p,m,i)*G(p,n,i);
// compute contraction of 4*sym(grad(u))sym(grad(v)) term.
// this contraction could be made slightly cheaper using Voigt
// notation, but repeated entries are summed for simplicity.
real_t contraction = 0.0;
for (int a = 0; a < d; a++)
{
for (int b = 0; b < d; b++)
{
contraction +=
((a == q) * invJ(m, b) + (b == q) * invJ(m, a)) *
((a == q) * invJ(n, b) + (b == q) * invJ(n, a));
}
}
// lambda*div(u)*div(v) + 2*mu*sym(grad(u))*sym(grad(v))
// contraction = 4*sym(grad(u))sym(grad(v))
const real_t Q =
w * (lamDev(p, e) * invJ(m, q) * invJ(n, q)
+ 0.5 * muDev(p, e) * contraction);
sum += Q * G(p, m, i) * G(p, n, i);
}
}
}
+1 -3
View File
@@ -10,7 +10,6 @@
// CONTRIBUTING.md for details.
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "bilininteg_elasticity_kernels.hpp"
@@ -59,9 +58,8 @@ void ElasticityIntegrator::AssemblePA(const FiniteElementSpace &fes)
void ElasticityIntegrator::AssembleDiagonalPA(Vector &diag)
{
q_vec->SetVDim(vdim*vdim*vdim*vdim);
internal::ElasticityAssembleDiagonalPA(vdim, ndofs, *lambda_quad, *mu_quad,
*geom, *maps, *q_vec, diag);
*geom, *maps, *IntRule, diag);
}
void ElasticityIntegrator::AddMultPA(const Vector &x, Vector &y) const
+29 -10
View File
@@ -148,11 +148,12 @@ void PAHcurlMassAssembleDiagonal3D(const int D1D,
}
void PAHcurlMassApply2D(const int NE, const bool symmetric,
const bool scalar_coeff, const Array<real_t> &bo,
const Array<real_t> &bc, const Array<real_t> &bot,
const Array<real_t> &bct, const Vector &pa_data,
const Vector &x, Vector &y, const int D1D,
const int TestD1D, const int Q1D)
[[maybe_unused]] const bool scalar_coeff,
const Array<real_t> &bo, const Array<real_t> &bc,
const Array<real_t> &bot, const Array<real_t> &bct,
const Vector &pa_data, const Vector &x, Vector &y,
const int D1D, [[maybe_unused]] const int TestD1D,
const int Q1D)
{
MFEM_ASSERT(D1D == TestD1D,
"Trial and Test space must have the same number of dofs");
@@ -275,11 +276,12 @@ void PAHcurlMassApply2D(const int NE, const bool symmetric,
}
void PAHcurlMassApply3D(const int NE, const bool symmetric,
const bool scalar_coeff, const Array<real_t> &bo,
const Array<real_t> &bc, const Array<real_t> &bot,
const Array<real_t> &bct, const Vector &pa_data,
const Vector &x, Vector &y, const int D1D,
const int TestD1D, const int Q1D)
[[maybe_unused]] const bool scalar_coeff,
const Array<real_t> &bo, const Array<real_t> &bc,
const Array<real_t> &bot, const Array<real_t> &bct,
const Vector &pa_data, const Vector &x, Vector &y,
const int D1D, [[maybe_unused]] const int TestD1D,
const int Q1D)
{
MFEM_VERIFY(D1D == TestD1D,
"Trial and test spaces must have same number of dofs");
@@ -783,6 +785,23 @@ void PAHcurlL2Setup2D(const int Q1D,
});
}
void PAHcurlL2IntSetup2D(const int Q1D, const int NE, const Array<real_t> &w,
Vector &coeff, const Vector &detJ, Vector &op)
{
const int NQ = Q1D*Q1D;
auto W = w.Read();
auto C = Reshape(coeff.Read(), NQ, NE);
auto J = Reshape(detJ.Read(), NQ, NE);
auto y = Reshape(op.Write(), NQ, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int q = 0; q < NQ; ++q)
{
y(q,e) = W[q] * C(q,e) / J(q,e);
}
});
}
void PAHcurlL2Setup3D(const int NQ,
const int coeffDim,
const int NE,
+17 -10
View File
@@ -190,19 +190,22 @@ void PAHcurlMassApply2D(const int NE, const bool symmetric,
// PA H(curl) Mass Apply 3D kernel
void PAHcurlMassApply3D(const int NE, const bool symmetric,
const bool scalar_coeff, const Array<real_t> &bo,
const Array<real_t> &bc, const Array<real_t> &bot,
const Array<real_t> &bct, const Vector &pa_data,
const Vector &x, Vector &y, const int TrialD1D,
const int TestD1D, const int Q1D);
[[maybe_unused]] const bool scalar_coeff,
const Array<real_t> &bo, const Array<real_t> &bc,
const Array<real_t> &bot, const Array<real_t> &bct,
const Vector &pa_data, const Vector &x, Vector &y,
const int TrialD1D, [[maybe_unused]] const int TestD1D,
const int Q1D);
// Shared memory PA H(curl) Mass Apply 3D kernel
template <int T_D1D = 0, int T_Q1D = 0, int TBATCH = 0, bool ACCUMULATE = true>
inline void SmemPAHcurlMassApply3D(
const int NE, const bool symmetric, const bool scalar_coeff,
const Array<real_t> &bo, const Array<real_t> &bc, const Array<real_t> &bot,
const Array<real_t> &bct, const Vector &pa_data, const Vector &x, Vector &y,
const int d1d = 0, const int = 0, const int q1d = 0)
const int NE, const bool symmetric, [[maybe_unused]] const bool scalar_coeff,
const Array<real_t> &bo, const Array<real_t> &bc,
[[maybe_unused]] const Array<real_t> &bot,
[[maybe_unused]] const Array<real_t> &bct, const Vector &pa_data,
const Vector &x, Vector &y, const int d1d = 0,
[[maybe_unused]] const int test_d1d = 0, const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -1886,13 +1889,17 @@ inline void SmemPACurlCurlApply3D(const int d1d,
ForallWrap<3>(true, NE, device_kernel, host_kernel, Q1D, Q1D, Q1D);
}
// PA H(curl)-L2 Assemble 2D kernel
// PA H(curl)-L2 value Assemble 2D kernel
void PAHcurlL2Setup2D(const int Q1D,
const int NE,
const Array<real_t> &w,
Vector &coeff,
Vector &op);
// PA H(curl)-L2 integral Assemble 2D kernel
void PAHcurlL2IntSetup2D(const int Q1D, const int NE, const Array<real_t> &w,
Vector &coeff, const Vector &detJ, Vector &op);
// PA H(curl)-L2 Assemble 3D kernel
void PAHcurlL2Setup3D(const int NQ,
const int coeffDim,
+343 -8
View File
@@ -19,6 +19,213 @@
namespace mfem
{
namespace
{
void PAHcurlApplyCurl2D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &Bo_,
const Array<real_t> &Gc_,
const Vector &x_,
Vector &y_)
{
auto Bo = Reshape(Bo_.Read(), o_dofs1D, o_dofs1D);
auto Gc = Reshape(Gc_.Read(), o_dofs1D, c_dofs1D);
auto X = Reshape(x_.Read(), 2 * c_dofs1D * o_dofs1D, NE);
auto Y = Reshape(y_.ReadWrite(), o_dofs1D, o_dofs1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int iy = 0; iy < c_dofs1D; ++iy)
{
for (int ix = 0; ix < o_dofs1D; ++ix)
{
const real_t xv = X(ix + iy * o_dofs1D, e);
for (int oy = 0; oy < o_dofs1D; ++oy)
{
const real_t gy = Gc(oy, iy);
for (int ox = 0; ox < o_dofs1D; ++ox)
{
Y(ox, oy, e) -= Bo(ox, ix) * gy * xv;
}
}
}
}
const int y_nd = c_dofs1D * o_dofs1D;
for (int iy = 0; iy < o_dofs1D; ++iy)
{
for (int ix = 0; ix < c_dofs1D; ++ix)
{
const real_t xv = X(y_nd + ix + iy * c_dofs1D, e);
for (int oy = 0; oy < o_dofs1D; ++oy)
{
const real_t by = Bo(oy, iy);
for (int ox = 0; ox < o_dofs1D; ++ox)
{
Y(ox, oy, e) += Gc(ox, ix) * by * xv;
}
}
}
}
});
}
void PAHcurlApplyCurlTranspose2D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &Bo_,
const Array<real_t> &Gc_,
const Vector &x_,
Vector &y_)
{
auto Bo = Reshape(Bo_.Read(), o_dofs1D, o_dofs1D);
auto Gc = Reshape(Gc_.Read(), o_dofs1D, c_dofs1D);
auto X = Reshape(x_.Read(), o_dofs1D, o_dofs1D, NE);
auto Y = Reshape(y_.ReadWrite(), 2 * c_dofs1D * o_dofs1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < o_dofs1D; ++dx)
{
real_t sum = 0.0;
for (int oy = 0; oy < o_dofs1D; ++oy)
{
const real_t gy = Gc(oy, dy);
for (int ox = 0; ox < o_dofs1D; ++ox)
{
sum -= Bo(ox, dx) * gy * X(ox, oy, e);
}
}
Y(dx + dy * o_dofs1D, e) += sum;
}
}
const int y_nd = c_dofs1D * o_dofs1D;
for (int dy = 0; dy < o_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
real_t sum = 0.0;
for (int oy = 0; oy < o_dofs1D; ++oy)
{
const real_t by = Bo(oy, dy);
for (int ox = 0; ox < o_dofs1D; ++ox)
{
sum += Gc(ox, dx) * by * X(ox, oy, e);
}
}
Y(y_nd + dx + dy * c_dofs1D, e) += sum;
}
}
});
}
void PAHdivApplyCurl2D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &Bc_,
const Array<real_t> &Gc_,
const Vector &x_,
Vector &y_)
{
auto Bc = Reshape(Bc_.Read(), c_dofs1D, c_dofs1D);
auto Gc = Reshape(Gc_.Read(), o_dofs1D, c_dofs1D);
auto X = Reshape(x_.Read(), c_dofs1D, c_dofs1D, NE);
auto Y = Reshape(y_.ReadWrite(), 2 * c_dofs1D * o_dofs1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int iy = 0; iy < c_dofs1D; ++iy)
{
for (int ix = 0; ix < c_dofs1D; ++ix)
{
const real_t xv = X(ix, iy, e);
for (int oy = 0; oy < o_dofs1D; ++oy)
{
const real_t gy = Gc(oy, iy);
for (int ox = 0; ox < c_dofs1D; ++ox)
{
Y(ox + oy * c_dofs1D, e) += Bc(ox, ix) * gy * xv;
}
}
}
}
const int y_nd = c_dofs1D * o_dofs1D;
for (int iy = 0; iy < c_dofs1D; ++iy)
{
for (int ix = 0; ix < c_dofs1D; ++ix)
{
const real_t xv = X(ix, iy, e);
for (int oy = 0; oy < c_dofs1D; ++oy)
{
const real_t by = Bc(oy, iy);
for (int ox = 0; ox < o_dofs1D; ++ox)
{
Y(y_nd + ox + oy * o_dofs1D, e) -= Gc(ox, ix) * by * xv;
}
}
}
}
});
}
void PAHdivApplyCurlTranspose2D(const int c_dofs1D,
const int o_dofs1D,
const int NE,
const Array<real_t> &Bc_,
const Array<real_t> &Gc_,
const Vector &x_,
Vector &y_)
{
auto Bc = Reshape(Bc_.Read(), c_dofs1D, c_dofs1D);
auto Gc = Reshape(Gc_.Read(), o_dofs1D, c_dofs1D);
auto X = Reshape(x_.Read(), 2 * c_dofs1D * o_dofs1D, NE);
auto Y = Reshape(y_.ReadWrite(), c_dofs1D, c_dofs1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int dy = 0; dy < o_dofs1D; ++dy)
{
for (int dx = 0; dx < c_dofs1D; ++dx)
{
const real_t xv = X(dx + dy * c_dofs1D, e);
for (int iy = 0; iy < c_dofs1D; ++iy)
{
const real_t gy = Gc(dy, iy);
for (int ix = 0; ix < c_dofs1D; ++ix)
{
Y(ix, iy, e) += Bc(dx, ix) * gy * xv;
}
}
}
}
const int y_nd = c_dofs1D * o_dofs1D;
for (int dy = 0; dy < c_dofs1D; ++dy)
{
for (int dx = 0; dx < o_dofs1D; ++dx)
{
const real_t xv = X(y_nd + dx + dy * o_dofs1D, e);
for (int iy = 0; iy < c_dofs1D; ++iy)
{
const real_t by = Bc(dy, iy);
for (int ix = 0; ix < c_dofs1D; ++ix)
{
Y(ix, iy, e) -= Gc(dx, ix) * by * xv;
}
}
}
}
});
}
}
// Apply to x corresponding to DOFs in H^1 (domain) the (topological) gradient
// to get a dof in H(curl) (range). You can think of the range as the "test" space
// and the domain as the "trial" space, but there's no integration.
@@ -1955,15 +2162,101 @@ void IdentityInterpolator::AddMultTransposePA(const Vector &x, Vector &y) const
void CurlInterpolator::AssemblePA(const FiniteElementSpace &dom_fes,
const FiniteElementSpace &ran_fes)
{
// TODO: 1D and 2D meshes
Mesh *mesh = dom_fes.GetMesh();
dim = mesh->Dimension();
ne = dom_fes.GetNE();
pa_mode_2d = 0;
MFEM_VERIFY(ne == ran_fes.GetNE(),
"Different meshes for domain and range spaces");
if (dim == 2)
{
pa_data.SetSize(0);
const FiniteElement *dom_fel = dom_fes.GetTypicalFE();
const FiniteElement *ran_fel = ran_fes.GetTypicalFE();
const bool hcurl_to_scalar =
dynamic_cast<const VectorTensorFiniteElement*>(dom_fel) != NULL &&
dom_fel->GetDerivType() == FiniteElement::CURL &&
dynamic_cast<const TensorBasisElement*>(ran_fel) != NULL &&
ran_fel->GetRangeType() == FiniteElement::SCALAR;
const bool scalar_to_hdiv =
dynamic_cast<const TensorBasisElement*>(dom_fel) != NULL &&
dom_fel->GetRangeType() == FiniteElement::SCALAR &&
dynamic_cast<const VectorTensorFiniteElement*>(ran_fel) != NULL &&
ran_fel->GetDerivType() == FiniteElement::DIV;
MFEM_VERIFY(hcurl_to_scalar || scalar_to_hdiv,
"2D CurlInterpolator PA supports H(curl)->scalar and scalar->H(div) only.");
int closed_basis_type = -1;
int open_basis_type = -1;
if (hcurl_to_scalar)
{
const auto *trial_fec = dynamic_cast<const ND_FECollection*>(dom_fes.FEColl());
const auto *range_fec = dynamic_cast<const L2_FECollection*>(ran_fes.FEColl());
MFEM_VERIFY(trial_fec != NULL, "H(curl) domain must use ND_FECollection.");
MFEM_VERIFY(range_fec != NULL, "Scalar range must use L2_FECollection.");
MFEM_VERIFY(ran_fel->GetMapType() == FiniteElement::INTEGRAL,
"2D H(curl)->scalar CurlInterpolator PA supports integral-map scalar range spaces only.");
closed_basis_type = trial_fec->GetClosedBasisType();
open_basis_type = trial_fec->GetOpenBasisType();
MFEM_VERIFY(range_fec->GetBasisType() == open_basis_type,
"Domain/range open basis types do not match.");
pa_mode_2d = 1;
}
else
{
const auto *trial_fec = dynamic_cast<const H1_FECollection*>(dom_fes.FEColl());
const auto *range_fec = dynamic_cast<const RT_FECollection*>(ran_fes.FEColl());
MFEM_VERIFY(trial_fec != NULL, "Scalar domain must use H1_FECollection.");
MFEM_VERIFY(range_fec != NULL, "H(div) range must use RT_FECollection.");
closed_basis_type = trial_fec->GetBasisType();
open_basis_type = range_fec->GetOpenBasisType();
MFEM_VERIFY(range_fec->GetClosedBasisType() == closed_basis_type,
"Domain/range closed basis types do not match.");
pa_mode_2d = 2;
}
const int order = hcurl_to_scalar
? dynamic_cast<const VectorTensorFiniteElement*>(dom_fel)->GetOrder()
: dynamic_cast<const NodalTensorFiniteElement*>(dom_fel)->GetOrder();
c_dofs1D = order + 1;
o_dofs1D = order;
closed_dofquad_fe.reset(new H1_SegmentElement(order, closed_basis_type));
open_dofquad_fe.reset(new L2_SegmentElement(order - 1, open_basis_type));
mfem::QuadratureFunctions1D qf1d;
mfem::IntegrationRule closed_ir;
closed_ir.SetSize(c_dofs1D);
qf1d.GaussLobatto(c_dofs1D, &closed_ir);
mfem::IntegrationRule open_ir;
open_ir.SetSize(o_dofs1D);
qf1d.GaussLegendre(o_dofs1D, &open_ir);
maps_C_C = &closed_dofquad_fe->GetDofToQuad(closed_ir, DofToQuad::TENSOR);
maps_O_C = &closed_dofquad_fe->GetDofToQuad(open_ir, DofToQuad::TENSOR);
maps_O_O = &open_dofquad_fe->GetDofToQuad(open_ir, DofToQuad::TENSOR);
MFEM_VERIFY(maps_C_C->ndof == c_dofs1D && maps_C_C->nqpt == c_dofs1D, "");
MFEM_VERIFY(maps_O_C->ndof == c_dofs1D && maps_O_C->nqpt == o_dofs1D, "");
MFEM_VERIFY(maps_O_O->ndof == o_dofs1D && maps_O_O->nqpt == o_dofs1D, "");
return;
}
closed_dofquad_fe.reset();
open_dofquad_fe.reset();
maps_C_C = nullptr;
maps_O_C = nullptr;
maps_O_O = nullptr;
const VectorTensorFiniteElement *dom_el =
dynamic_cast<const VectorTensorFiniteElement *>(dom_fes.GetTypicalFE());
const VectorTensorFiniteElement *ran_el =
dynamic_cast<const VectorTensorFiniteElement *>(ran_fes.GetTypicalFE());
MFEM_VERIFY(dom_el != NULL, "Only VectorTensorFiniteElement is supported!");
MFEM_VERIFY(ran_el != NULL, "Only VectorTensorFiniteElement is supported!");
// only supports H(curl) -> H(div) because of discontinuity requirements
MFEM_VERIFY(dom_el->GetDerivType() == FiniteElement::CURL,
"Domain space must be H(curl)");
MFEM_VERIFY(ran_el->GetDerivType() == FiniteElement::DIV,
@@ -1971,9 +2264,7 @@ void CurlInterpolator::AssemblePA(const FiniteElementSpace &dom_fes,
const int dims = dom_el->GetDim();
MFEM_VERIFY(dims == 3, "");
dim = mesh->Dimension();
ne = dom_fes.GetNE();
ndof_o = dom_el->GetOrder();
int ndof_c = ndof_o + 1;
nquad_o = ran_el->GetOrder();
@@ -2052,14 +2343,58 @@ CurlInterpolator::CurlInterpolator() { static Kernels kernels{}; }
void CurlInterpolator::AddMultPA(const Vector &x, Vector &y) const
{
ApplyPAKernels::Run(dim, ndof_o, nquad_o, ne, ndof_o, nquad_o, pa_data, x,
y);
if (dim == 2)
{
MFEM_VERIFY(maps_C_C != nullptr && maps_O_C != nullptr,
"2D CurlInterpolator PA data is not assembled.");
if (pa_mode_2d == 1)
{
MFEM_VERIFY(maps_O_O != nullptr,
"2D CurlInterpolator scalar curl map is not assembled.");
PAHcurlApplyCurl2D(c_dofs1D, o_dofs1D, ne, maps_O_O->B, maps_O_C->G,
x, y);
}
else if (pa_mode_2d == 2)
{
PAHdivApplyCurl2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B, maps_O_C->G,
x, y);
}
else
{
MFEM_ABORT("Unsupported 2D CurlInterpolator mode.");
}
return;
}
ApplyPAKernels::Run(dim, ndof_o, nquad_o, ne, ndof_o, nquad_o, pa_data, x, y);
}
void CurlInterpolator::AddMultTransposePA(const Vector &x, Vector &y) const
{
ApplyTPAKernels::Run(dim, ndof_o, nquad_o, ne, ndof_o, nquad_o, pa_data, x,
y);
if (dim == 2)
{
MFEM_VERIFY(maps_C_C != nullptr && maps_O_C != nullptr,
"2D CurlInterpolator PA data is not assembled.");
if (pa_mode_2d == 1)
{
MFEM_VERIFY(maps_O_O != nullptr,
"2D CurlInterpolator scalar curl map is not assembled.");
PAHcurlApplyCurlTranspose2D(c_dofs1D, o_dofs1D, ne, maps_O_O->B,
maps_O_C->G, x, y);
}
else if (pa_mode_2d == 2)
{
PAHdivApplyCurlTranspose2D(c_dofs1D, o_dofs1D, ne, maps_C_C->B,
maps_O_C->G, x, y);
}
else
{
MFEM_ABORT("Unsupported 2D CurlInterpolator mode.");
}
return;
}
ApplyTPAKernels::Run(dim, ndof_o, nquad_o, ne, ndof_o, nquad_o, pa_data, x, y);
}
/// \cond DO_NOT_DOCUMENT
+538 -1
View File
@@ -12,6 +12,7 @@
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "bilininteg_hdiv_kernels.hpp"
#include "bilininteg_hcurl_kernels.hpp"
#include "bilininteg_hcurlhdiv_kernels.hpp"
@@ -21,6 +22,29 @@ namespace mfem
namespace
{
class Rotated2DVectorCoefficient : public VectorCoefficient
{
public:
explicit Rotated2DVectorCoefficient(VectorCoefficient &coeff)
: VectorCoefficient(2), coeff_(&coeff), value_(2) { }
void SetTime(real_t t) override { coeff_->SetTime(t); }
using VectorCoefficient::Eval;
void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) override
{
coeff_->Eval(value_, T, ip);
V.SetSize(2);
V(0) = -value_(1);
V(1) = value_(0);
}
private:
VectorCoefficient *coeff_;
mutable Vector value_;
};
void PAHcurlDotSetup2D(const int q1d,
const int ne,
const bool test_map_integral,
@@ -305,6 +329,232 @@ void PAHcurlDotApplyTranspose2D(const int d1d,
});
}
void PAHdivDotSetup2D(const int q1d,
const int ne,
const bool test_map_integral,
const Array<real_t> &w,
const Vector &jacobians,
const Vector &coeff,
Vector &op)
{
auto W = Reshape(w.Read(), q1d, q1d);
auto J = Reshape(jacobians.Read(), q1d, q1d, 2, 2, ne);
auto C = Reshape(coeff.Read(), 2, q1d, q1d, ne);
auto O = Reshape(op.Write(), 2, q1d, q1d, ne);
mfem::forall_2D(ne, q1d, q1d, [=] MFEM_HOST_DEVICE (int e)
{
MFEM_FOREACH_THREAD(qy, y, q1d)
{
MFEM_FOREACH_THREAD(qx, x, q1d)
{
const real_t J11 = J(qx, qy, 0, 0, e);
const real_t J12 = J(qx, qy, 1, 0, e);
const real_t J21 = J(qx, qy, 0, 1, e);
const real_t J22 = J(qx, qy, 1, 1, e);
const real_t detJ = (J11 * J22) - (J21 * J12);
const real_t scale = W(qx, qy) * (test_map_integral ? 1.0 / detJ : 1.0);
const real_t Vx = C(0, qx, qy, e);
const real_t Vy = C(1, qx, qy, e);
O(0, qx, qy, e) = scale * (J11 * Vx + J21 * Vy);
O(1, qx, qy, e) = scale * (J12 * Vx + J22 * Vy);
}
}
});
}
void PAHdivDotApply2D(const int d1d,
const int d1d_test,
const int q1d,
const int ne,
const Array<real_t> &bo,
const Array<real_t> &bc,
const Array<real_t> &bt,
const Vector &pa_data,
const Vector &x,
Vector &y)
{
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D, "");
MFEM_VERIFY(d1d_test <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D, "");
auto Bo = Reshape(bo.Read(), q1d, d1d - 1);
auto Bc = Reshape(bc.Read(), q1d, d1d);
auto Bt = Reshape(bt.Read(), d1d_test, q1d);
auto O = Reshape(pa_data.Read(), 2, q1d, q1d, ne);
auto X = Reshape(x.Read(), 2 * (d1d - 1) * d1d, ne);
auto Y = Reshape(y.ReadWrite(), d1d_test, d1d_test, ne);
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int MAX_D1D = DofQuadLimits::MAX_D1D;
constexpr int MAX_Q1D = DofQuadLimits::HDIV_MAX_Q1D;
real_t mass[MAX_Q1D][MAX_Q1D][2];
for (int qy = 0; qy < q1d; ++qy)
{
for (int qx = 0; qx < q1d; ++qx)
{
mass[qy][qx][0] = 0.0;
mass[qy][qx][1] = 0.0;
}
}
int osc = 0;
for (int dy = 0; dy < d1d - 1; ++dy)
{
real_t mass_x[MAX_Q1D];
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
for (int dx = 0; dx < d1d; ++dx)
{
const real_t t = X(dx + (dy * d1d) + osc, e);
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] += t * Bc(qx, dx); }
}
for (int qy = 0; qy < q1d; ++qy)
{
const real_t wy = Bo(qy, dy);
for (int qx = 0; qx < q1d; ++qx) { mass[qy][qx][0] += mass_x[qx] * wy; }
}
}
osc += d1d * (d1d - 1);
for (int dy = 0; dy < d1d; ++dy)
{
real_t mass_x[MAX_Q1D];
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
for (int dx = 0; dx < d1d - 1; ++dx)
{
const real_t t = X(dx + (dy * (d1d - 1)) + osc, e);
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] += t * Bo(qx, dx); }
}
for (int qy = 0; qy < q1d; ++qy)
{
const real_t wy = Bc(qy, dy);
for (int qx = 0; qx < q1d; ++qx) { mass[qy][qx][1] += mass_x[qx] * wy; }
}
}
for (int qy = 0; qy < q1d; ++qy)
{
real_t sol_x[MAX_D1D];
for (int dx = 0; dx < d1d_test; ++dx) { sol_x[dx] = 0.0; }
for (int qx = 0; qx < q1d; ++qx)
{
const real_t s = O(0, qx, qy, e) * mass[qy][qx][0]
+ O(1, qx, qy, e) * mass[qy][qx][1];
for (int dx = 0; dx < d1d_test; ++dx)
{
sol_x[dx] += s * Bt(dx, qx);
}
}
for (int dy = 0; dy < d1d_test; ++dy)
{
const real_t wy = Bt(dy, qy);
for (int dx = 0; dx < d1d_test; ++dx)
{
Y(dx, dy, e) += sol_x[dx] * wy;
}
}
}
});
}
void PAHdivDotApplyTranspose2D(const int d1d,
const int d1d_test,
const int q1d,
const int ne,
const Array<real_t> &bo,
const Array<real_t> &bc,
const Array<real_t> &b,
const Vector &pa_data,
const Vector &x,
Vector &y)
{
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D, "");
MFEM_VERIFY(d1d_test <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D, "");
auto Bo = Reshape(bo.Read(), q1d, d1d - 1);
auto Bc = Reshape(bc.Read(), q1d, d1d);
auto B = Reshape(b.Read(), q1d, d1d_test);
auto O = Reshape(pa_data.Read(), 2, q1d, q1d, ne);
auto X = Reshape(x.Read(), d1d_test, d1d_test, ne);
auto Y = Reshape(y.ReadWrite(), 2 * (d1d - 1) * d1d, ne);
mfem::forall(ne, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int MAX_Q1D = DofQuadLimits::HDIV_MAX_Q1D;
real_t mass[MAX_Q1D][MAX_Q1D];
for (int qy = 0; qy < q1d; ++qy)
{
for (int qx = 0; qx < q1d; ++qx)
{
mass[qy][qx] = 0.0;
}
}
for (int dy = 0; dy < d1d_test; ++dy)
{
real_t sol_x[MAX_Q1D];
for (int qx = 0; qx < q1d; ++qx) { sol_x[qx] = 0.0; }
for (int dx = 0; dx < d1d_test; ++dx)
{
const real_t t = X(dx, dy, e);
for (int qx = 0; qx < q1d; ++qx) { sol_x[qx] += t * B(qx, dx); }
}
for (int qy = 0; qy < q1d; ++qy)
{
const real_t wy = B(qy, dy);
for (int qx = 0; qx < q1d; ++qx) { mass[qy][qx] += sol_x[qx] * wy; }
}
}
int osc = 0;
for (int dy = 0; dy < d1d - 1; ++dy)
{
real_t mass_x[MAX_Q1D];
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
for (int qy = 0; qy < q1d; ++qy)
{
const real_t wy = Bo(qy, dy);
for (int qx = 0; qx < q1d; ++qx)
{
mass_x[qx] += (O(0, qx, qy, e) * mass[qy][qx]) * wy;
}
}
for (int dx = 0; dx < d1d; ++dx)
{
real_t sum = 0.0;
for (int qx = 0; qx < q1d; ++qx) { sum += mass_x[qx] * Bc(qx, dx); }
Y(dx + (dy * d1d) + osc, e) += sum;
}
}
osc += d1d * (d1d - 1);
for (int dy = 0; dy < d1d; ++dy)
{
real_t mass_x[MAX_Q1D];
for (int qx = 0; qx < q1d; ++qx) { mass_x[qx] = 0.0; }
for (int qy = 0; qy < q1d; ++qy)
{
const real_t wy = Bc(qy, dy);
for (int qx = 0; qx < q1d; ++qx)
{
mass_x[qx] += (O(1, qx, qy, e) * mass[qy][qx]) * wy;
}
}
for (int dx = 0; dx < d1d - 1; ++dx)
{
real_t sum = 0.0;
for (int qx = 0; qx < q1d; ++qx) { sum += mass_x[qx] * Bo(qx, dx); }
Y(dx + (dy * (d1d - 1)) + osc, e) += sum;
}
}
});
}
void PAHcurlDotApply3D(const int d1d,
const int d1d_test,
const int q1d,
@@ -812,6 +1062,8 @@ void MixedScalarCurlIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
= IntRule ? IntRule : &MassIntegrator::GetRule(*eltest, *eltest,
*mesh->GetTypicalElementTransformation());
auto map_type = eltest->GetMapType();
const int dims = el->GetDim();
MFEM_VERIFY(dims == 2, "");
@@ -843,7 +1095,23 @@ void MixedScalarCurlIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
if (dim == 2)
{
internal::PAHcurlL2Setup2D(quad1D, ne, ir->GetWeights(), coeff, pa_data);
switch (map_type)
{
case FiniteElement::VALUE:
internal::PAHcurlL2Setup2D(quad1D, ne, ir->GetWeights(), coeff,
pa_data);
break;
case FiniteElement::INTEGRAL:
{
const MemoryType mt = (pa_mt == MemoryType::DEFAULT) ?
Device::GetDeviceMemoryType() : pa_mt;
auto geom = mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS, mt);
internal::PAHcurlL2IntSetup2D(quad1D, ne, ir->GetWeights(), coeff,
geom->detJ, pa_data);
} break;
default:
MFEM_ABORT("Unsupported map type");
}
}
else
{
@@ -1194,4 +1462,273 @@ void MixedVectorWeakCurlIntegrator::AddMultTransposePA(const Vector &x,
}
}
void MixedScalarWeakGradientIntegrator::AssemblePA(const FiniteElementSpace
&trial_fes,
const FiniteElementSpace &test_fes)
{
Mesh *mesh = trial_fes.GetMesh();
const FiniteElement *trial_fel = trial_fes.GetTypicalFE();
const FiniteElement *test_fel = test_fes.GetTypicalFE();
const TensorBasisElement *trial_tensor_el =
dynamic_cast<const TensorBasisElement*>(trial_fel);
MFEM_VERIFY(trial_tensor_el != NULL,
"Only tensor-product scalar trial elements are supported!");
const VectorTensorFiniteElement *test_el =
dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
MFEM_VERIFY(test_el->GetDerivType() == mfem::FiniteElement::DIV,
"Only H(div) test spaces are supported!");
const IntegrationRule *ir = IntRule ? IntRule : &MassIntegrator::GetRule(
*test_el, *test_el,
*mesh->GetTypicalElementTransformation());
const int dims = test_el->GetDim();
MFEM_VERIFY(dims == 2 || dims == 3, "");
const int nq = ir->GetNPoints();
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "");
ne = trial_fes.GetNE();
MFEM_VERIFY(ne == test_fes.GetNE(),
"Different meshes for test and trial spaces");
mapsC = &test_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
mapsO = &test_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
dofs1D = mapsC->ndof;
quad1D = mapsC->nqpt;
L2mapsO = &trial_fel->GetDofToQuad(*ir, DofToQuad::TENSOR);
L2dofs1D = L2mapsO->ndof;
MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
if (dim == 2) { MFEM_VERIFY(nq == quad1D * quad1D, ""); }
else { MFEM_VERIFY(nq == quad1D * quad1D * quad1D, ""); }
pa_data.SetSize(nq * ne, Device::GetMemoryType());
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(Q, qs, CoefficientStorage::FULL);
if (trial_fel->GetMapType() == FiniteElement::INTEGRAL)
{
const GeometricFactors *geom =
mesh->GetGeometricFactors(*ir, GeometricFactors::DETERMINANTS);
coeff /= geom->detJ;
}
if (dim == 2)
{
internal::PAHdivL2Setup2D(quad1D, ne, ir->GetWeights(), coeff, pa_data);
}
else
{
internal::PAHdivL2Setup3D(quad1D, ne, ir->GetWeights(), coeff, pa_data);
}
}
void MixedScalarWeakGradientIntegrator::AddMultPA(const Vector &x,
Vector &y) const
{
if (dim == 2)
{
internal::PAHdivL2ApplyTranspose2D(dofs1D, quad1D, L2dofs1D, ne, L2mapsO->B,
mapsC->Gt, mapsO->Bt, pa_data, x, y);
}
else if (dim == 3)
{
internal::PAHdivL2ApplyTranspose3D(dofs1D, quad1D, L2dofs1D, ne, L2mapsO->B,
mapsC->Gt, mapsO->Bt, pa_data, x, y);
}
else
{
MFEM_ABORT("Unsupported dimension!");
}
}
void MixedScalarWeakGradientIntegrator::AddMultTransposePA(const Vector &x,
Vector &y) const
{
if (dim == 2)
{
internal::PAHdivL2Apply2D(dofs1D, quad1D, L2dofs1D, ne, mapsO->B, mapsC->G,
L2mapsO->Bt, pa_data, x, y);
}
else if (dim == 3)
{
internal::PAHdivL2Apply3D(dofs1D, quad1D, L2dofs1D, ne, mapsO->B, mapsC->G,
L2mapsO->Bt, pa_data, x, y);
}
else
{
MFEM_ABORT("Unsupported dimension!");
}
}
void MixedScalarCrossProductIntegrator::AssemblePA(const FiniteElementSpace
&trial_fes,
const FiniteElementSpace &test_fes)
{
Mesh *mesh = trial_fes.GetMesh();
const FiniteElement *trial_fel = trial_fes.GetTypicalFE();
const FiniteElement *test_fel = test_fes.GetTypicalFE();
const VectorTensorFiniteElement *trial_el =
dynamic_cast<const VectorTensorFiniteElement *>(trial_fel);
MFEM_VERIFY(trial_el != NULL, "Only VectorTensorFiniteElement is supported!");
MFEM_VERIFY(trial_el->GetDerivType() == mfem::FiniteElement::DIV,
"Only H(div) trial spaces are supported!");
const TensorBasisElement *test_tensor_el =
dynamic_cast<const TensorBasisElement*>(test_fel);
MFEM_VERIFY(test_tensor_el != NULL,
"Only tensor-product scalar test elements are supported!");
const IntegrationRule *ir = IntRule;
if (ir == nullptr)
{
const int order = trial_fel->GetOrder() + test_fel->GetOrder()
+ mesh->GetTypicalElementTransformation()->OrderW();
ir = &IntRules.Get(trial_fel->GetGeomType(), order);
}
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2, "Only 2D is supported.");
MFEM_VERIFY(trial_el->GetDim() == dim && test_fel->GetDim() == dim,
"Trial/test dimension mismatch.");
ne = trial_fes.GetNE();
MFEM_VERIFY(ne == test_fes.GetNE(),
"Different meshes for test and trial spaces");
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
mapsC = &trial_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
mapsO = &trial_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
mapsTest = &test_fel->GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = mapsC->ndof;
dofs1Dtest = mapsTest->ndof;
quad1D = mapsC->nqpt;
test_map_integral = (test_fel->GetMapType() == FiniteElement::INTEGRAL);
MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
MFEM_VERIFY(quad1D == mapsTest->nqpt, "Trial/test quadrature mismatch");
MFEM_VERIFY(dofs1D <= DeviceDofQuadLimits::Get().HDIV_MAX_D1D, "");
MFEM_VERIFY(dofs1Dtest <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(quad1D <= DeviceDofQuadLimits::Get().HDIV_MAX_Q1D, "");
const int nq = ir->GetNPoints();
MFEM_VERIFY(nq == quad1D * quad1D, "");
Rotated2DVectorCoefficient rotated(*VQ);
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(rotated, qs, CoefficientStorage::FULL);
pa_data.SetSize(dim * nq * ne, Device::GetMemoryType());
PAHdivDotSetup2D(quad1D, ne, test_map_integral, ir->GetWeights(),
geom->J, coeff, pa_data);
}
void MixedScalarCrossProductIntegrator::AddMultPA(const Vector &x,
Vector &y) const
{
PAHdivDotApply2D(dofs1D, dofs1Dtest, quad1D, ne,
mapsO->B, mapsC->B, mapsTest->Bt,
pa_data, x, y);
}
void MixedScalarCrossProductIntegrator::AddMultTransposePA(const Vector &x,
Vector &y) const
{
PAHdivDotApplyTranspose2D(dofs1D, dofs1Dtest, quad1D, ne,
mapsO->B, mapsC->B, mapsTest->B,
pa_data, x, y);
}
void MixedScalarWeakCrossProductIntegrator::AssemblePA(
const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes)
{
Mesh *mesh = trial_fes.GetMesh();
const FiniteElement *trial_fel = trial_fes.GetTypicalFE();
const FiniteElement *test_fel = test_fes.GetTypicalFE();
const TensorBasisElement *trial_tensor_el =
dynamic_cast<const TensorBasisElement*>(trial_fel);
MFEM_VERIFY(trial_tensor_el != NULL,
"Only tensor-product scalar trial elements are supported!");
const VectorTensorFiniteElement *test_el =
dynamic_cast<const VectorTensorFiniteElement*>(test_fel);
MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
MFEM_VERIFY(test_el->GetDerivType() == mfem::FiniteElement::CURL,
"Only H(curl) test spaces are supported!");
const IntegrationRule *ir = IntRule;
if (ir == nullptr)
{
const int order = trial_fel->GetOrder() + test_fel->GetOrder()
+ mesh->GetTypicalElementTransformation()->OrderW();
ir = &IntRules.Get(trial_fel->GetGeomType(), order);
}
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2, "Only 2D is supported.");
MFEM_VERIFY(test_el->GetDim() == dim && trial_fel->GetDim() == dim,
"Trial/test dimension mismatch.");
ne = trial_fes.GetNE();
MFEM_VERIFY(ne == test_fes.GetNE(),
"Different meshes for test and trial spaces");
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
mapsC = &test_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
mapsO = &test_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
mapsTrial = &trial_fel->GetDofToQuad(*ir, DofToQuad::TENSOR);
dofs1D = mapsC->ndof;
dofs1Dtrial = mapsTrial->ndof;
quad1D = mapsC->nqpt;
trial_map_integral = (trial_fel->GetMapType() == FiniteElement::INTEGRAL);
MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
MFEM_VERIFY(quad1D == mapsTrial->nqpt, "Trial/test quadrature mismatch");
MFEM_VERIFY(dofs1D <= DeviceDofQuadLimits::Get().HCURL_MAX_D1D, "");
MFEM_VERIFY(dofs1Dtrial <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(quad1D <= DeviceDofQuadLimits::Get().HCURL_MAX_Q1D, "");
const int nq = ir->GetNPoints();
MFEM_VERIFY(nq == quad1D * quad1D, "");
Rotated2DVectorCoefficient rotated(*VQ);
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(rotated, qs, CoefficientStorage::FULL);
pa_data.SetSize(dim * nq * ne, Device::GetMemoryType());
PAHcurlDotSetup2D(quad1D, ne, trial_map_integral, ir->GetWeights(),
geom->J, coeff, pa_data);
// Match the extra sign introduced by the legacy assembled path's
// MixedScalarWeakCrossProductIntegrator::CalcShape().
pa_data *= -1.0;
}
void MixedScalarWeakCrossProductIntegrator::AddMultPA(const Vector &x,
Vector &y) const
{
PAHcurlDotApplyTranspose2D(dofs1D, dofs1Dtrial, quad1D, ne,
mapsO->B, mapsC->B, mapsTrial->B,
pa_data, x, y);
}
void MixedScalarWeakCrossProductIntegrator::AddMultTransposePA(const Vector &x,
Vector &y) const
{
PAHcurlDotApply2D(dofs1D, dofs1Dtrial, quad1D, ne,
mapsO->B, mapsC->B, mapsTrial->Bt,
pa_data, x, y);
}
} // namespace mfem
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+365
View File
@@ -0,0 +1,365 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#pragma once
#include "../../config/config.hpp"
#include "../../general/array.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../linalg/vector.hpp"
#include "../bilininteg.hpp"
#include "../kernels.hpp"
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
namespace internal
{
// Shared memory PA Divergence Apply 2D kernel
template<int T_TR_D1D = 0, int T_TE_D1D = 0, int T_Q1D = 0>
inline void SmemPADivergenceApply2D(const int NE,
const Array<real_t> &b_,
const Array<real_t> &g_,
const Array<real_t> &bt_,
const Vector &q_,
const Vector &x_,
Vector &y_,
const int tr_d1d = 0,
const int te_d1d = 0,
const int q1d = 0)
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(TR_D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(TE_D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = b_.Read(), G = g_.Read(), Bt = bt_.Read();
const auto Q = Reshape(q_.Read(), Q1D, Q1D, 2, 2, NE);
const auto X = Reshape(x_.Read(), TR_D1D, TR_D1D, 2, NE);
auto Y = Reshape(y_.ReadWrite(), TE_D1D, TE_D1D, 1, NE);
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t smem[MQ1][MQ1];
MFEM_SHARED real_t sB[MQ1][MQ1], sG[MQ1][MQ1];
kernels::internal::vd_regs2d_t<2, 2, MQ1> g0, g1;
kernels::internal::v_regs2d_t<1, MQ1> r0, r1;
kernels::internal::LoadMatrix(TR_D1D, Q1D, B, sB);
kernels::internal::LoadMatrix(TR_D1D, Q1D, G, sG);
kernels::internal::LoadDofs2d(e, TR_D1D, X, g0);
kernels::internal::Grad2d(TR_D1D, Q1D, smem, sB, sG, g0, g1);
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
r0[0][qy][qx] =
g1[0][0][qy][qx] * Q(qx, qy, 0, 0, e) +
g1[0][1][qy][qx] * Q(qx, qy, 1, 0, e) +
g1[1][0][qy][qx] * Q(qx, qy, 0, 1, e) +
g1[1][1][qy][qx] * Q(qx, qy, 1, 1, e);
}
}
MFEM_SYNC_THREAD;
kernels::internal::LoadMatrix<MQ1,true>(TE_D1D, Q1D, Bt, sB);
kernels::internal::EvalTranspose2d(TE_D1D, Q1D, smem, sB, r0, r1);
kernels::internal::WriteDofs2d(e, TE_D1D, r1, Y);
});
}
// Shared memory PA Divergence Apply 2D kernel transpose
template<int T_TR_D1D = 0, int T_TE_D1D = 0, int T_Q1D = 0>
inline void SmemPADivergenceApplyTranspose2D(const int NE,
const Array<real_t> &bt,
const Array<real_t> &gt,
const Array<real_t> &b,
const Vector &q_,
const Vector &x_,
Vector &y_,
const int tr_d1d = 0,
const int te_d1d = 0,
const int q1d = 0)
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(TR_D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(TE_D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto Bt = bt.Read(), Gt = gt.Read(), B = b.Read();
const auto Q = Reshape(q_.Read(), Q1D, Q1D, 2, 2, NE);
const auto X = Reshape(x_.Read(), TE_D1D, TE_D1D, 1, NE);
auto Y = Reshape(y_.ReadWrite(), TR_D1D, TR_D1D, 2, NE);
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t smem[MQ1][MQ1];
MFEM_SHARED real_t sB[MQ1][MQ1], sG[MQ1][MQ1];
kernels::internal::v_regs2d_t<1, MQ1> r0, r1;
kernels::internal::vd_regs2d_t<2, 2, MQ1> g0, g1;
kernels::internal::LoadMatrix(TE_D1D, Q1D, B, sB);
kernels::internal::LoadDofs2d(e, TE_D1D, X, r0);
kernels::internal::Eval2d(TE_D1D, Q1D, smem, sB, r0, r1);
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
g0[0][0][qy][qx] = r1[0][qy][qx] * Q(qx, qy, 0, 0, e);
g0[0][1][qy][qx] = r1[0][qy][qx] * Q(qx, qy, 1, 0, e);
g0[1][0][qy][qx] = r1[0][qy][qx] * Q(qx, qy, 0, 1, e);
g0[1][1][qy][qx] = r1[0][qy][qx] * Q(qx, qy, 1, 1, e);
}
}
MFEM_SYNC_THREAD;
kernels::internal::LoadMatrix<MQ1,true>(TR_D1D, Q1D, Bt, sB);
kernels::internal::LoadMatrix<MQ1,true>(TR_D1D, Q1D, Gt, sG);
kernels::internal::GradTranspose2d(TR_D1D, Q1D, smem, sB, sG, g0, g1);
kernels::internal::WriteDofs2d(e, TR_D1D, g1, Y);
});
}
// Shared memory PA Divergence Apply 3D kernel transpose
template<int T_TR_D1D = 0, int T_TE_D1D = 0, int T_Q1D = 0>
inline void SmemPADivergenceApplyTranspose3D(const int NE,
const Array<real_t> &bt,
const Array<real_t> &gt,
const Array<real_t> &b,
const Vector &q_,
const Vector &x_,
Vector &y_,
int tr_d1d = 0,
int te_d1d = 0,
int q1d = 0)
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(TR_D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(TE_D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto Bt = bt.Read(), Gt = gt.Read(), B = b.Read();
const auto Q = Reshape(q_.Read(), Q1D, Q1D, Q1D, 3, 3, NE);
const auto X = Reshape(x_.Read(), TE_D1D, TE_D1D, TE_D1D, 1, NE);
auto Y = Reshape(y_.ReadWrite(), TR_D1D, TR_D1D, TR_D1D, 3, NE);
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t smem[MQ1][MQ1];
MFEM_SHARED real_t sB[MQ1][MQ1], sG[MQ1][MQ1];
kernels::internal::v_regs3d_t<1, MQ1> r0, r1;
kernels::internal::vd_regs3d_t<3, 3, MQ1> g0, g1;
kernels::internal::LoadMatrix(TE_D1D, Q1D, B, sB);
kernels::internal::LoadDofs3d(e, TE_D1D, X, r0);
kernels::internal::Eval3d(TE_D1D, Q1D, smem, sB, r0, r1);
for (int qz = 0; qz < Q1D; qz++)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const auto r = r1[0][qz][qy][qx];
g0[0][0][qz][qy][qx] = r * Q(qx, qy, qz, 0, 0, e);
g0[0][1][qz][qy][qx] = r * Q(qx, qy, qz, 1, 0, e);
g0[0][2][qz][qy][qx] = r * Q(qx, qy, qz, 2, 0, e);
g0[1][0][qz][qy][qx] = r * Q(qx, qy, qz, 0, 1, e);
g0[1][1][qz][qy][qx] = r * Q(qx, qy, qz, 1, 1, e);
g0[1][2][qz][qy][qx] = r * Q(qx, qy, qz, 2, 1, e);
g0[2][0][qz][qy][qx] = r * Q(qx, qy, qz, 0, 2, e);
g0[2][1][qz][qy][qx] = r * Q(qx, qy, qz, 1, 2, e);
g0[2][2][qz][qy][qx] = r * Q(qx, qy, qz, 2, 2, e);
}
}
}
MFEM_SYNC_THREAD;
kernels::internal::LoadMatrix<MQ1,true>(TR_D1D, Q1D, Bt, sB);
kernels::internal::LoadMatrix<MQ1,true>(TR_D1D, Q1D, Gt, sG);
kernels::internal::GradTranspose3d(TR_D1D, Q1D, smem, sB, sG, g0, g1);
kernels::internal::WriteDofs3d(e, TR_D1D, g1, Y);
});
}
// Shared memory PA Divergence Apply 3D kernel
template<int T_TR_D1D = 0, int T_TE_D1D = 0, int T_Q1D = 0>
inline void SmemPADivergenceApply3D(const int NE,
const Array<real_t> &b_,
const Array<real_t> &g_,
const Array<real_t> &bt_,
const Vector &q_,
const Vector &x_,
Vector &y_,
const int tr_d1d = 0,
const int te_d1d = 0,
const int q1d = 0)
{
const int TR_D1D = T_TR_D1D ? T_TR_D1D : tr_d1d;
const int TE_D1D = T_TE_D1D ? T_TE_D1D : te_d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(TR_D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(TE_D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = b_.Read(), G = g_.Read(), Bt = bt_.Read();
const auto Q = Reshape(q_.Read(), Q1D, Q1D, Q1D, 3,3, NE);
const auto X = Reshape(x_.Read(), TR_D1D, TR_D1D, TR_D1D, 3, NE);
auto Y = Reshape(y_.ReadWrite(), TE_D1D, TE_D1D, TE_D1D, 1, NE);
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t smem[MQ1][MQ1];
MFEM_SHARED real_t sB[MQ1][MQ1], sG[MQ1][MQ1];
kernels::internal::vd_regs3d_t<3, 3, MQ1> g0, g1;
kernels::internal::v_regs3d_t<1, MQ1> r0, r1;
kernels::internal::LoadMatrix(TR_D1D, Q1D, B, sB);
kernels::internal::LoadMatrix(TR_D1D, Q1D, G, sG);
kernels::internal::LoadDofs3d(e, TR_D1D, X, g0);
kernels::internal::Grad3d(TR_D1D, Q1D, smem, sB, sG, g0, g1);
for (int qz = 0; qz < Q1D; qz++)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
r0[0][qz][qy][qx] =
// c = 0
g1[0][0][qz][qy][qx] * Q(qx, qy, qz, 0, 0, e) +
g1[0][1][qz][qy][qx] * Q(qx, qy, qz, 1, 0, e) +
g1[0][2][qz][qy][qx] * Q(qx, qy, qz, 2, 0, e) +
// c = 1
g1[1][0][qz][qy][qx] * Q(qx, qy, qz, 0, 1, e) +
g1[1][1][qz][qy][qx] * Q(qx, qy, qz, 1, 1, e) +
g1[1][2][qz][qy][qx] * Q(qx, qy, qz, 2, 1, e) +
// c = 2
g1[2][0][qz][qy][qx] * Q(qx, qy, qz, 0, 2, e) +
g1[2][1][qz][qy][qx] * Q(qx, qy, qz, 1, 2, e) +
g1[2][2][qz][qy][qx] * Q(qx, qy, qz, 2, 2, e);
}
}
}
MFEM_SYNC_THREAD;
kernels::internal::LoadMatrix<MQ1, true>(TE_D1D, Q1D, Bt, sB);
kernels::internal::EvalTranspose3d(TE_D1D, Q1D, smem, sB, r0, r1);
kernels::internal::WriteDofs3d(e, TE_D1D, r1, Y);
});
}
} // namespace internal
template<int DIM, int T_TR_D1D, int T_TE_D1D, int T_Q1D>
VectorDivergenceIntegrator::VectorDivergenceAddMultPAType
VectorDivergenceIntegrator::VectorDivergenceAddMultPA::Kernel()
{
static_assert(T_TR_D1D <= T_Q1D && T_TE_D1D <= T_Q1D);
if constexpr (DIM == 2)
{
return internal::SmemPADivergenceApply2D<T_TR_D1D, T_TE_D1D, T_Q1D>;
}
else if constexpr (DIM == 3)
{
return internal::SmemPADivergenceApply3D<T_TR_D1D, T_TE_D1D, T_Q1D>;
}
MFEM_ABORT("Unsupported kernel");
}
inline VectorDivergenceIntegrator::VectorDivergenceAddMultPAType
VectorDivergenceIntegrator::VectorDivergenceAddMultPA::Fallback
(int dim, int tr_d1d, int te_d1d, int q1d)
{
MFEM_VERIFY(tr_d1d <= q1d && te_d1d <= q1d, "");
MFEM_VERIFY(tr_d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(te_d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
if (dim == 2)
{
return internal::SmemPADivergenceApply2D;
}
else if (dim == 3)
{
return internal::SmemPADivergenceApply3D;
}
MFEM_ABORT("Unsupported kernel");
}
template<int DIM, int T_TR_D1D, int T_TE_D1D, int T_Q1D>
VectorDivergenceIntegrator::VectorDivergenceAddMultTransposePAType
VectorDivergenceIntegrator::VectorDivergenceAddMultTransposePA::Kernel()
{
static_assert(T_TR_D1D <= T_Q1D && T_TE_D1D <= T_Q1D);
if constexpr (DIM == 2)
{
return internal::SmemPADivergenceApplyTranspose2D<T_TR_D1D, T_TE_D1D, T_Q1D>;
}
else if constexpr (DIM == 3)
{
return internal::SmemPADivergenceApplyTranspose3D<T_TR_D1D, T_TE_D1D, T_Q1D>;
}
MFEM_ABORT("Unsupported kernel");
}
inline VectorDivergenceIntegrator::VectorDivergenceAddMultTransposePAType
VectorDivergenceIntegrator::VectorDivergenceAddMultTransposePA::Fallback
(int dim, int tr_d1d, int te_d1d, int q1d)
{
MFEM_VERIFY(tr_d1d <= q1d && te_d1d <= q1d, "");
MFEM_VERIFY(tr_d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(te_d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
if (dim == 2)
{
return internal::SmemPADivergenceApplyTranspose2D;
}
else if (dim == 3)
{
return internal::SmemPADivergenceApplyTranspose3D;
}
MFEM_ABORT("Unsupported kernel");
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
+34 -149
View File
@@ -22,6 +22,8 @@ void VectorMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetTypicalFE();
MFEM_VERIFY(el.GetMapType() == FiniteElement::VALUE,
"Only value map type supported");
ElementTransformation &Trans = *mesh->GetTypicalElementTransformation();
const auto *ir = IntRule ? IntRule : &MassIntegrator::GetRule(el, el, Trans);
@@ -205,157 +207,40 @@ void VectorMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
template <const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal2D(const int NE,
const Array<real_t> &b,
const Vector &pa_data, Vector &diag,
const int d1d = 0, const int q1d = 0)
{
constexpr int VDIM = 2;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = Reshape(b.Read(), Q1D, D1D);
const auto D = Reshape(pa_data.Read(), Q1D, Q1D, NE);
auto Y = Reshape(diag.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE(int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
real_t temp[max_Q1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dy = 0; dy < D1D; ++dy)
{
temp[qx][dy] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp[qx][dy] += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
}
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
real_t temp1 = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp1 += B(qx, dx) * B(qx, dx) * temp[qx][dy];
}
Y(dx, dy, 0, e) = temp1;
Y(dx, dy, 1, e) = temp1;
}
}
});
}
template <const int T_D1D = 0, const int T_Q1D = 0>
static void PAVectorMassAssembleDiagonal3D(const int NE,
const Array<real_t> &B_,
const Vector &pa_data, Vector &diag,
const int d1d = 0, const int q1d = 0)
{
constexpr int VDIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = Reshape(B_.Read(), Q1D, D1D);
MFEM_VERIFY(pa_data.Size() == Q1D * Q1D * Q1D * NE, "pa_data size error");
const auto D = Reshape(pa_data.Read(), Q1D, Q1D, Q1D, NE);
auto Y = Reshape(diag.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE(int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
// the following variables are evaluated at compile time
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
real_t temp[max_Q1D][max_Q1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int dz = 0; dz < D1D; ++dz)
{
temp[qx][qy][dz] = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
temp[qx][qy][dz] +=
B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
}
}
}
}
real_t temp2[max_Q1D][max_D1D][max_D1D];
for (int qx = 0; qx < Q1D; ++qx)
{
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
temp2[qx][dy][dz] = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
temp2[qx][dy][dz] +=
B(qy, dy) * B(qy, dy) * temp[qx][qy][dz];
}
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
real_t temp3 = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
temp3 += B(qx, dx) * B(qx, dx) * temp2[qx][dy][dz];
}
Y(dx, dy, dz, 0, e) = temp3;
Y(dx, dy, dz, 1, e) = temp3;
Y(dx, dy, dz, 2, e) = temp3;
}
}
}
});
}
static void PAVectorMassAssembleDiagonal(const int dim, const int D1D,
const int Q1D, const int NE,
const Array<real_t> &B,
const Vector &pa_data,
Vector &diag)
{
if (dim == 2)
{
return PAVectorMassAssembleDiagonal2D(NE, B, pa_data, diag, D1D, Q1D);
}
else if (dim == 3)
{
return PAVectorMassAssembleDiagonal3D(NE, B, pa_data, diag, D1D, Q1D);
}
MFEM_ABORT("Dimension not implemented.");
}
void VectorMassIntegrator::AssembleDiagonalPA(Vector &diag)
{
if (DeviceCanUseCeed()) { ceedOp->GetDiagonal(diag); }
else
{
MFEM_VERIFY(coeff_vdim == 1, "coeff_vdim != 1");
MFEM_VERIFY(!VQ && !MQ, "VQ and MQ not supported");
PAVectorMassAssembleDiagonal(dim, dofs1D, quad1D, ne, maps->B, pa_data, diag);
}
if (DeviceCanUseCeed()) { return ceedOp->GetDiagonal(diag); }
MFEM_VERIFY(coeff_vdim == 1, "coeff_vdim != 1");
MFEM_VERIFY(!VQ && !MQ, "VQ and MQ not supported");
// Add the VectorMassAssembleDiagonalPA specializations
static const auto vector_mass_assemble_diagonal_kernel_specializations =
( // 2D
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 2>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 3>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 4>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 5>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 6>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 7>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<2, 8>::Add(),
// 3D
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 2>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 3>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 4>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 5>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 6>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 7>::Add(),
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Specialization<3, 8>::Add(),
true);
MFEM_CONTRACT_VAR(vector_mass_assemble_diagonal_kernel_specializations);
VectorMassAssembleDiagonalPA::Run(dim, quad1D, // templated arguments
ne, dofs1D, quad1D,
maps->B.Read(),
pa_data.Read(),
diag.ReadWrite());
}
} // namespace mfem
+170 -2
View File
@@ -176,8 +176,146 @@ void SmemPAVectorMassApply3D(const int NE,
});
}
template <int T_Q1D = 0, int T_MDQ = 16>
static void SmemPAVectorMassAssembleDiagonal2D(const int ne,
const int d1d,
const int q1d,
const real_t *b_r,
const real_t *d_r,
real_t *y_rw)
{
constexpr int VDIM = 2;
const int D1D = d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(Q1D <= T_MDQ && D1D <= Q1D, "");
const auto B = Reshape(b_r, Q1D, D1D);
const auto D = Reshape(d_r, Q1D, Q1D, ne);
auto Y = Reshape(y_rw, D1D, D1D, VDIM, ne);
mfem::forall_2D<T_Q1D*T_Q1D>(
ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
MFEM_SHARED real_t sm[MQ1][MQ1];
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += B(qy, dy) * B(qy, dy) * D(qx, qy, e);
}
sm[qx][dy] = u;
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += B(qx, dx) * B(qx, dx) * sm[qx][dy];
}
Y(dx, dy, 0, e) += u;
Y(dx, dy, 1, e) += u;
}
}
});
}
// T_MDQ <= 10 so the Q1D^3 thread block stays within the 1024/block GPU limit
template <int T_Q1D = 0, int T_MDQ = 10>
static void SmemPAVectorMassAssembleDiagonal3D(const int ne,
const int d1d,
const int q1d,
const real_t *b_r,
const real_t *d_r,
real_t *y_rw)
{
constexpr int VDIM = 3;
const int D1D = d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(Q1D <= T_MDQ && D1D <= Q1D, "");
const auto B = Reshape(b_r, Q1D, D1D);
const auto D = Reshape(d_r, Q1D, Q1D, Q1D, ne);
auto Y = Reshape(y_rw, D1D, D1D, D1D, VDIM, ne);
mfem::forall_3D<T_Q1D*T_Q1D*T_Q1D>(
ne, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MDQ;
MFEM_SHARED real_t sm[2][MQ1][MQ1][MQ1];
MFEM_FOREACH_THREAD_DIRECT(dz, z, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
real_t u = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u += B(qz, dz) * B(qz, dz) * D(qx, qy, qz, e);
}
sm[0][dz][qy][qx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dz, z, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += B(qy, dy) * B(qy, dy) * sm[0][dz][qy][qx];
}
sm[1][dz][dy][qx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dz, z, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += B(qx, dx) * B(qx, dx) * sm[1][dz][dy][qx];
}
Y(dx, dy, dz, 0, e) += u;
Y(dx, dy, dz, 1, e) += u;
Y(dx, dy, dz, 2, e) += u;
}
}
}
});
}
} // namespace internal
// AddMultPA kernels
template<int DIM, int T_D1D, int T_Q1D>
VectorMassIntegrator::VectorMassAddMultPAType
VectorMassIntegrator::VectorMassAddMultPA::Kernel()
@@ -194,7 +332,7 @@ VectorMassIntegrator::VectorMassAddMultPA::Kernel()
}
inline VectorMassIntegrator::VectorMassAddMultPAType
VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int d1d, int q1d)
VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int, int)
{
if (dim == 2)
{
@@ -204,7 +342,37 @@ VectorMassIntegrator::VectorMassAddMultPA::Fallback(int dim, int d1d, int q1d)
{
return internal::SmemPAVectorMassApply3D;
}
else { MFEM_ABORT("Unsupported kernel"); }
MFEM_ABORT("Unsupported kernel");
}
// DiagonalPA kernels
template<int DIM, int T_Q1D>
VectorMassIntegrator::VectorMassAssembleDiagonalPAType
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Kernel()
{
if constexpr (DIM == 2)
{
return internal::SmemPAVectorMassAssembleDiagonal2D<T_Q1D>;
}
else if constexpr (DIM == 3)
{
return internal::SmemPAVectorMassAssembleDiagonal3D<T_Q1D>;
}
MFEM_ABORT("Unsupported kernel");
}
inline VectorMassIntegrator::VectorMassAssembleDiagonalPAType
VectorMassIntegrator::VectorMassAssembleDiagonalPA::Fallback(int dim, int)
{
if (dim == 2)
{
return internal::SmemPAVectorMassAssembleDiagonal2D;
}
else if (dim == 3)
{
return internal::SmemPAVectorMassAssembleDiagonal3D;
}
MFEM_ABORT("Unsupported kernel");
}
/// \endcond DO_NOT_DOCUMENT
+4 -22
View File
@@ -13,10 +13,6 @@
#define MFEM_BILININTEG_VECTORFEMASS_KERNELS_HPP
#include "../../config/config.hpp"
#include "../../general/array.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../linalg/vector.hpp"
#include "../bilininteg.hpp"
#include "bilininteg_diffusion_kernels.hpp"
@@ -77,15 +73,8 @@ VectorFEMassIntegrator::ApplyPAKernels::Kernel()
}
else if constexpr (trial_div && test_div)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
// assumes TrialD1D == TestD1D
return internal::SmemPAHdivMassApply3D<TrialD1D, Q1D>;
}
else
{
return internal::PAHdivMassApply3D;
}
// assumes TrialD1D == TestD1D
return internal::SmemPAHdivMassApply3D<TrialD1D, Q1D>;
}
else if constexpr (trial_curl && test_div)
{
@@ -104,15 +93,8 @@ VectorFEMassIntegrator::ApplyPAKernels::Kernel()
}
else if constexpr (trial_div && test_div)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
// assumes TrialD1D == TestD1D
return internal::SmemPAHdivMassApply2D<TrialD1D, Q1D>;
}
else
{
return internal::PAHdivMassApply2D;
}
// assumes TrialD1D == TestD1D
return internal::SmemPAHdivMassApply2D<TrialD1D, Q1D>;
}
else if constexpr (trial_curl && test_div)
{
+21 -17
View File
@@ -10,8 +10,6 @@
// CONTRIBUTING.md for details.
#include "../bilininteg.hpp"
#include "../gridfunc.hpp"
#include "../qfunction.hpp"
#include "bilininteg_vectorfemass_kernels.hpp"
namespace mfem
@@ -72,17 +70,7 @@ VectorFEMassIntegrator::ApplyPAKernels::Fallback(
VectorFEMassIntegrator::Kernels::Kernels()
{
// h(curl), h(curl)
// P = Q (3D)
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 2, 2, 2>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 3, 3, 3>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 4, 4, 4>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 5, 5, 5>();
// P = Q + 1 (3D)
// Q = P + 1 (3D)
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 2, 2, 3>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
@@ -91,8 +79,26 @@ VectorFEMassIntegrator::Kernels::Kernels()
FiniteElement::CURL, 3, 4, 4, 5>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 5, 5, 6>();
// Q = P + 2 (3D)
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 2, 2, 4>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 3, 3, 5>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 4, 4, 6>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 5, 5, 7>();
// Q = P + 4 (3D)
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 2, 2, 6>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 3, 3, 7>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 4, 4, 8>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::CURL,
FiniteElement::CURL, 3, 5, 5, 9>();
// h(div), h(div)
// P = Q (2D)
// Q = P (2D)
VectorFEMassIntegrator::AddSpecialization<FiniteElement::DIV,
FiniteElement::DIV, 2, 2, 2, 2>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::DIV,
@@ -102,7 +108,7 @@ VectorFEMassIntegrator::Kernels::Kernels()
VectorFEMassIntegrator::AddSpecialization<FiniteElement::DIV,
FiniteElement::DIV, 2, 5, 5, 5>();
// P = Q + 1 (3D)
// Q = P + 1 (3D)
VectorFEMassIntegrator::AddSpecialization<FiniteElement::DIV,
FiniteElement::DIV, 3, 2, 2, 3>();
VectorFEMassIntegrator::AddSpecialization<FiniteElement::DIV,
@@ -333,8 +339,6 @@ void VectorFEMassIntegrator::AddAbsMultPA(const Vector &x, Vector &y) const
Array<real_t> absBo(mapsO->B);
Array<real_t> absBc(mapsC->B);
// Array<real_t> absBto(mapsO->Bt);
// Array<real_t> absBtc(mapsC->Bt);
Array<real_t> absBto_t(mapsOtest->Bt);
Array<real_t> absBtc_t(mapsCtest->Bt);
+143 -771
View File
@@ -9,21 +9,51 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "../../general/forall.hpp"
#include "../nonlininteg.hpp"
#include "../ceed/integrators/nlconvection/nlconvection.hpp"
#include "./nonlininteg_vecconvection_pa.hpp" // IWYU pragma: keep
#include "./nonlininteg_vecconvection_pa_grad.hpp" // IWYU pragma: keep
#include "./nonlininteg_vecconvection_pa_diag.hpp" // IWYU pragma: keep
namespace mfem
{
VectorConvectionNLFIntegrator::Kernels::Kernels()
{
// 2D
VectorConvectionNLFIntegrator::AddSpecialization<2, 2, 2>();
VectorConvectionNLFIntegrator::AddSpecialization<2, 2, 3>();
VectorConvectionNLFIntegrator::AddSpecialization<2, 3, 4>();
VectorConvectionNLFIntegrator::AddSpecialization<2, 3, 5>();
VectorConvectionNLFIntegrator::AddSpecialization<2, 4, 5>();
VectorConvectionNLFIntegrator::AddSpecialization<2, 4, 6>();
VectorConvectionNLFIntegrator::AddSpecialization<2, 5, 7>();
VectorConvectionNLFIntegrator::AddSpecialization<2, 5, 8>();
VectorConvectionNLFIntegrator::AddSpecialization<2, 6, 8>();
// 3D
VectorConvectionNLFIntegrator::AddSpecialization<3, 2, 3>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 2, 4>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 2, 5>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 3, 4>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 3, 5>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 3, 6>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 4, 5>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 4, 6>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 4, 7>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 4, 8>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 5, 6>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 5, 7>();
VectorConvectionNLFIntegrator::AddSpecialization<3, 5, 8>();
}
void VectorConvectionNLFIntegrator::AssemblePA(const FiniteElementSpace &fes)
{
MFEM_ASSERT(fes.GetOrdering() == Ordering::byNODES,
"PA Only supports Ordering::byNODES!");
Mesh *mesh = fes.GetMesh();
const FiniteElement &el = *fes.GetTypicalFE();
ElementTransformation &T = *mesh->GetTypicalElementTransformation();
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, T);
ElementTransformation &Tr = *mesh->GetTypicalElementTransformation();
const IntegrationRule *ir = IntRule ? IntRule : &GetRule(el, Tr);
if (DeviceCanUseCeed())
{
delete ceedOp;
@@ -39,769 +69,124 @@ void VectorConvectionNLFIntegrator::AssemblePA(const FiniteElementSpace &fes)
}
return;
}
dim = mesh->Dimension();
ne = fes.GetMesh()->GetNE();
ne = mesh->GetNE();
nq = ir->GetNPoints();
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
dim = mesh->Dimension();
MFEM_VERIFY(dim == 2 || dim == 3, "Dimension not supported");
const MemoryType mt = pa_mt == MemoryType::DEFAULT
? Device::GetDeviceMemoryType()
: pa_mt;
pa_adj.SetSize(ne * nq * dim * dim, mt);
geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS, mt);
maps = &el.GetDofToQuad(*ir, DofToQuad::TENSOR);
pa_data.SetSize(ne * nq * dim * dim, Device::GetMemoryType());
real_t COEFF = 1.0;
if (Q)
{
ConstantCoefficient *cQ = dynamic_cast<ConstantCoefficient *>(Q);
MFEM_VERIFY(cQ != NULL, "only ConstantCoefficient is supported!");
COEFF = cQ->constant;
}
const int NE = ne;
const int NQ = nq;
auto W = ir->GetWeights().Read();
if (dim == 1)
{
MFEM_ABORT("dim==1 not supported!");
}
d1d = maps->ndof;
q1d = maps->nqpt;
QuadratureSpace qs(*mesh, *ir);
CoefficientVector coeff(Q, qs, CoefficientStorage::COMPRESSED);
const int nq1d = q1d * q1d * (dim==3 ? q1d : 1);
MFEM_VERIFY(coeff.Size() == 1 || coeff.Size() == nq1d*ne, "Invalid coeff");
MFEM_VERIFY(ir->GetWeights().Size() == nq1d, "Invalid weights size");
const auto w_r = ir->GetWeights().Read();
const bool const_coeff = coeff.Size() == 1;
if (dim == 2)
{
auto J = Reshape(geom->J.Read(), NQ, 2, 2, NE);
auto G = Reshape(pa_data.Write(), NQ, 2, 2, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
const int Q1D = q1d;
constexpr int VDIM = 2, DIM = 2;
const auto W = Reshape(w_r, Q1D, Q1D);
const auto C = const_coeff ?
Reshape(coeff.Read(), 1, 1, 1) :
Reshape(coeff.Read(), Q1D, Q1D, ne);
const auto J = Reshape(geom->J.Read(), Q1D, Q1D, VDIM, DIM, ne);
auto A = Reshape(pa_adj.Write(), VDIM, DIM, Q1D, Q1D, ne);
mfem::forall_2D(ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
for (int q = 0; q < NQ; ++q)
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
const real_t J11 = J(q, 0, 0, e);
const real_t J12 = J(q, 0, 1, e);
const real_t J21 = J(q, 1, 0, e);
const real_t J22 = J(q, 1, 1, e);
// Store wq * Q * adj(J)
G(q, 0, 0, e) = W[q] * COEFF * J22; // 1,1
G(q, 0, 1, e) = W[q] * COEFF * -J12; // 1,2
G(q, 1, 0, e) = W[q] * COEFF * -J21; // 2,1
G(q, 1, 1, e) = W[q] * COEFF * J11; // 2,2
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const real_t J11 = J(qx, qy, 0, 0, e), J12 = J(qx, qy, 0, 1, e);
const real_t J21 = J(qx, qy, 1, 0, e), J22 = J(qx, qy, 1, 1, e);
// adj(J)
const real_t A11 = +J22, A12 = -J12;
const real_t A21 = -J21, A22 = +J11;
// Store w * coeff * adj(J)
const real_t w = W(qx, qy);
const real_t c = const_coeff ? C(0, 0, 0) : C(qx, qy, e);
A(0, 0, qx, qy, e) = w * c * A11;
A(1, 0, qx, qy, e) = w * c * A12;
A(0, 1, qx, qy, e) = w * c * A21;
A(1, 1, qx, qy, e) = w * c * A22;
}
}
});
}
if (dim == 3)
else if (dim == 3)
{
auto J = Reshape(geom->J.Read(), NQ, 3, 3, NE);
auto G = Reshape(pa_data.Write(), NQ, 3, 3, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
const int Q1D = q1d;
constexpr int VDIM = 3, DIM = 3;
const auto W = Reshape(w_r, Q1D, Q1D, Q1D);
const auto C = const_coeff ?
Reshape(coeff.Read(), 1, 1, 1, 1) :
Reshape(coeff.Read(), Q1D, Q1D, Q1D, ne);
const auto J = Reshape(geom->J.Read(), Q1D, Q1D, Q1D, VDIM, DIM, ne);
auto A = Reshape(pa_adj.Write(), VDIM, DIM, Q1D, Q1D, Q1D, ne);
mfem::forall_3D(ne, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
for (int q = 0; q < NQ; ++q)
MFEM_FOREACH_THREAD_DIRECT(qz, z, Q1D)
{
const real_t J11 = J(q, 0, 0, e);
const real_t J21 = J(q, 1, 0, e);
const real_t J31 = J(q, 2, 0, e);
const real_t J12 = J(q, 0, 1, e);
const real_t J22 = J(q, 1, 1, e);
const real_t J32 = J(q, 2, 1, e);
const real_t J13 = J(q, 0, 2, e);
const real_t J23 = J(q, 1, 2, e);
const real_t J33 = J(q, 2, 2, e);
const real_t cw = W[q] * COEFF;
// 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);
// Store wq * Q * adj(J)
G(q, 0, 0, e) = cw * A11; // 1,1
G(q, 0, 1, e) = cw * A12; // 1,2
G(q, 0, 2, e) = cw * A13; // 1,3
G(q, 1, 0, e) = cw * A21; // 2,1
G(q, 1, 1, e) = cw * A22; // 2,2
G(q, 1, 2, e) = cw * A23; // 2,3
G(q, 2, 0, e) = cw * A31; // 3,1
G(q, 2, 1, e) = cw * A32; // 3,2
G(q, 2, 2, e) = cw * A33; // 3,3
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const real_t J11 = J(qx, qy, qz, 0, 0, e),
J12 = J(qx, qy, qz, 0, 1, e),
J13 = J(qx, qy, qz, 0, 2, e);
const real_t J21 = J(qx, qy, qz, 1, 0, e),
J22 = J(qx, qy, qz, 1, 1, e),
J23 = J(qx, qy, qz, 1, 2, e);
const real_t J31 = J(qx, qy, qz, 2, 0, e),
J32 = J(qx, qy, qz, 2, 1, e),
J33 = J(qx, qy, qz, 2, 2, e);
const real_t c =
const_coeff ? C(0, 0, 0, 0) : C(qx, qy, qz, e);
const real_t cw = W(qx, qy, qz) * c;
// 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);
// Store wq * coeff * adj(J)
A(0, 0, qx, qy, qz, e) = cw * A11;
A(1, 0, qx, qy, qz, e) = cw * A12;
A(2, 0, qx, qy, qz, e) = cw * A13;
A(0, 1, qx, qy, qz, e) = cw * A21;
A(1, 1, qx, qy, qz, e) = cw * A22;
A(2, 1, qx, qy, qz, e) = cw * A23;
A(0, 2, qx, qy, qz, e) = cw * A31;
A(1, 2, qx, qy, qz, e) = cw * A32;
A(2, 2, qx, qy, qz, e) = cw * A33;
}
}
}
});
}
}
// PA Convection NL 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
static void PAConvectionNLApply2D(const int NE,
const Array<real_t> &b,
const Array<real_t> &g,
const Array<real_t> &bt,
const Vector &q_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto Bt = Reshape(bt.Read(), D1D, Q1D);
auto Q = Reshape(q_.Read(), Q1D * Q1D, 2, 2, NE);
auto x = Reshape(x_.Read(), D1D, D1D, 2, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, 2, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
else
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
real_t data[max_Q1D][max_Q1D][2];
real_t grad0[max_Q1D][max_Q1D][2];
real_t grad1[max_Q1D][max_Q1D][2];
real_t Z[max_Q1D][max_Q1D][2];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
data[qy][qx][0] = 0.0;
data[qy][qx][1] = 0.0;
grad0[qy][qx][0] = 0.0;
grad0[qy][qx][1] = 0.0;
grad1[qy][qx][0] = 0.0;
grad1[qy][qx][1] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
real_t dataX[max_Q1D][2];
real_t gradX0[max_Q1D][2];
real_t gradX1[max_Q1D][2];
for (int qx = 0; qx < Q1D; ++qx)
{
dataX[qx][0] = 0.0;
dataX[qx][1] = 0.0;
gradX0[qx][0] = 0.0;
gradX0[qx][1] = 0.0;
gradX1[qx][0] = 0.0;
gradX1[qx][1] = 0.0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const real_t s0 = x(dx, dy, 0, e);
const real_t s1 = x(dx, dy, 1, e);
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Bx = B(qx, dx);
const real_t Gx = G(qx, dx);
dataX[qx][0] += s0 * Bx;
dataX[qx][1] += s1 * Bx;
gradX0[qx][0] += s0 * Gx;
gradX0[qx][1] += s0 * Bx;
gradX1[qx][0] += s1 * Gx;
gradX1[qx][1] += s1 * Bx;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t By = B(qy, dy);
const real_t Gy = G(qy, dy);
for (int qx = 0; qx < Q1D; ++qx)
{
data[qy][qx][0] += dataX[qx][0] * By;
data[qy][qx][1] += dataX[qx][1] * By;
grad0[qy][qx][0] += gradX0[qx][0] * By;
grad0[qy][qx][1] += gradX0[qx][1] * Gy;
grad1[qy][qx][0] += gradX1[qx][0] * By;
grad1[qy][qx][1] += gradX1[qx][1] * Gy;
}
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
const int q = qx + qy * Q1D;
const real_t u1 = data[qy][qx][0];
const real_t u2 = data[qy][qx][1];
const real_t grad00 = grad0[qy][qx][0];
const real_t grad01 = grad0[qy][qx][1];
const real_t grad10 = grad1[qy][qx][0];
const real_t grad11 = grad1[qy][qx][1];
const real_t Dxu1 = grad00 * Q(q, 0, 0, e) + grad01 * Q(q, 1, 0, e);
const real_t Dyu1 = grad00 * Q(q, 0, 1, e) + grad01 * Q(q, 1, 1, e);
const real_t Dxu2 = grad10 * Q(q, 0, 0, e) + grad11 * Q(q, 1, 0, e);
const real_t Dyu2 = grad10 * Q(q, 0, 1, e) + grad11 * Q(q, 1, 1, e);
Z[qy][qx][0] = u1 * Dxu1 + u2 * Dyu1;
Z[qy][qx][1] = u1 * Dxu2 + u2 * Dyu2;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
real_t Y[max_D1D][2];
for (int dx = 0; dx < D1D; ++dx)
{
Y[dx][0] = 0.0;
Y[dx][1] = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Btx = Bt(dx, qx);
Y[dx][0] += Btx * Z[qy][qx][0];
Y[dx][1] += Btx * Z[qy][qx][1];
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
const real_t Bty = Bt(dy, qy);
y(dx, dy, 0, e) += Bty * Y[dx][0];
y(dx, dy, 1, e) += Bty * Y[dx][1];
}
}
}
});
}
// PA Convection NL 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
static void PAConvectionNLApply3D(const int NE,
const Array<real_t> &b,
const Array<real_t> &g,
const Array<real_t> &bt,
const Vector &q_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
constexpr int VDIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
auto B = Reshape(b.Read(), Q1D, D1D);
auto G = Reshape(g.Read(), Q1D, D1D);
auto Bt = Reshape(bt.Read(), D1D, Q1D);
auto Q = Reshape(q_.Read(), Q1D * Q1D * Q1D, VDIM, VDIM, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, VDIM, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
constexpr int VDIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int max_D1D = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int max_Q1D = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
real_t data[max_Q1D][max_Q1D][max_Q1D][VDIM];
real_t grad0[max_Q1D][max_Q1D][max_Q1D][VDIM];
real_t grad1[max_Q1D][max_Q1D][max_Q1D][VDIM];
real_t grad2[max_Q1D][max_Q1D][max_Q1D][VDIM];
real_t Z[max_Q1D][max_Q1D][max_Q1D][VDIM];
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
data[qz][qy][qx][0] = 0.0;
data[qz][qy][qx][1] = 0.0;
data[qz][qy][qx][2] = 0.0;
grad0[qz][qy][qx][0] = 0.0;
grad0[qz][qy][qx][1] = 0.0;
grad0[qz][qy][qx][2] = 0.0;
grad1[qz][qy][qx][0] = 0.0;
grad1[qz][qy][qx][1] = 0.0;
grad1[qz][qy][qx][2] = 0.0;
grad2[qz][qy][qx][0] = 0.0;
grad2[qz][qy][qx][1] = 0.0;
grad2[qz][qy][qx][2] = 0.0;
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
real_t dataXY[max_Q1D][max_Q1D][VDIM];
real_t gradXY0[max_Q1D][max_Q1D][VDIM];
real_t gradXY1[max_Q1D][max_Q1D][VDIM];
real_t gradXY2[max_Q1D][max_Q1D][VDIM];
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
dataXY[qy][qx][0] = 0.0;
dataXY[qy][qx][1] = 0.0;
dataXY[qy][qx][2] = 0.0;
gradXY0[qy][qx][0] = 0.0;
gradXY0[qy][qx][1] = 0.0;
gradXY0[qy][qx][2] = 0.0;
gradXY1[qy][qx][0] = 0.0;
gradXY1[qy][qx][1] = 0.0;
gradXY1[qy][qx][2] = 0.0;
gradXY2[qy][qx][0] = 0.0;
gradXY2[qy][qx][1] = 0.0;
gradXY2[qy][qx][2] = 0.0;
}
}
for (int dy = 0; dy < D1D; ++dy)
{
real_t dataX[max_Q1D][VDIM];
real_t gradX0[max_Q1D][VDIM];
real_t gradX1[max_Q1D][VDIM];
real_t gradX2[max_Q1D][VDIM];
for (int qx = 0; qx < Q1D; ++qx)
{
dataX[qx][0] = 0.0;
dataX[qx][1] = 0.0;
dataX[qx][2] = 0.0;
gradX0[qx][0] = 0.0;
gradX0[qx][1] = 0.0;
gradX0[qx][2] = 0.0;
gradX1[qx][0] = 0.0;
gradX1[qx][1] = 0.0;
gradX1[qx][2] = 0.0;
gradX2[qx][0] = 0.0;
gradX2[qx][1] = 0.0;
gradX2[qx][2] = 0.0;
}
for (int dx = 0; dx < D1D; ++dx)
{
const real_t s0 = x(dx, dy, dz, 0, e);
const real_t s1 = x(dx, dy, dz, 1, e);
const real_t s2 = x(dx, dy, dz, 2, e);
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Bx = B(qx, dx);
const real_t Gx = G(qx, dx);
dataX[qx][0] += s0 * Bx;
dataX[qx][1] += s1 * Bx;
dataX[qx][2] += s2 * Bx;
gradX0[qx][0] += s0 * Gx;
gradX0[qx][1] += s0 * Bx;
gradX0[qx][2] += s0 * Bx;
gradX1[qx][0] += s1 * Gx;
gradX1[qx][1] += s1 * Bx;
gradX1[qx][2] += s1 * Bx;
gradX2[qx][0] += s2 * Gx;
gradX2[qx][1] += s2 * Bx;
gradX2[qx][2] += s2 * Bx;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t By = B(qy, dy);
const real_t Gy = G(qy, dy);
for (int qx = 0; qx < Q1D; ++qx)
{
dataXY[qy][qx][0] += dataX[qx][0] * By;
dataXY[qy][qx][1] += dataX[qx][1] * By;
dataXY[qy][qx][2] += dataX[qx][2] * By;
gradXY0[qy][qx][0] += gradX0[qx][0] * By;
gradXY0[qy][qx][1] += gradX0[qx][1] * Gy;
gradXY0[qy][qx][2] += gradX0[qx][2] * By;
gradXY1[qy][qx][0] += gradX1[qx][0] * By;
gradXY1[qy][qx][1] += gradX1[qx][1] * Gy;
gradXY1[qy][qx][2] += gradX1[qx][2] * By;
gradXY2[qy][qx][0] += gradX2[qx][0] * By;
gradXY2[qy][qx][1] += gradX2[qx][1] * Gy;
gradXY2[qy][qx][2] += gradX2[qx][2] * By;
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
const real_t Bz = B(qz, dz);
const real_t Gz = G(qz, dz);
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
data[qz][qy][qx][0] += dataXY[qy][qx][0] * Bz;
data[qz][qy][qx][1] += dataXY[qy][qx][1] * Bz;
data[qz][qy][qx][2] += dataXY[qy][qx][2] * Bz;
grad0[qz][qy][qx][0] += gradXY0[qy][qx][0] * Bz;
grad0[qz][qy][qx][1] += gradXY0[qy][qx][1] * Bz;
grad0[qz][qy][qx][2] += gradXY0[qy][qx][2] * Gz;
grad1[qz][qy][qx][0] += gradXY1[qy][qx][0] * Bz;
grad1[qz][qy][qx][1] += gradXY1[qy][qx][1] * Bz;
grad1[qz][qy][qx][2] += gradXY1[qy][qx][2] * Gz;
grad2[qz][qy][qx][0] += gradXY2[qy][qx][0] * Bz;
grad2[qz][qy][qx][1] += gradXY2[qy][qx][1] * Bz;
grad2[qz][qy][qx][2] += gradXY2[qy][qx][2] * Gz;
}
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
for (int qy = 0; qy < Q1D; ++qy)
{
for (int qx = 0; qx < Q1D; ++qx)
{
const int q = qx + Q1D * (qy + qz * Q1D);
const real_t u1 = data[qz][qy][qx][0];
const real_t u2 = data[qz][qy][qx][1];
const real_t u3 = data[qz][qy][qx][2];
const real_t grad00 = grad0[qz][qy][qx][0];
const real_t grad01 = grad0[qz][qy][qx][1];
const real_t grad02 = grad0[qz][qy][qx][2];
const real_t grad10 = grad1[qz][qy][qx][0];
const real_t grad11 = grad1[qz][qy][qx][1];
const real_t grad12 = grad1[qz][qy][qx][2];
const real_t grad20 = grad2[qz][qy][qx][0];
const real_t grad21 = grad2[qz][qy][qx][1];
const real_t grad22 = grad2[qz][qy][qx][2];
const real_t Dxu1 = grad00 * Q(q, 0, 0, e)
+ grad01 * Q(q, 1, 0, e)
+ grad02 * Q(q, 2, 0, e);
const real_t Dyu1 = grad00 * Q(q, 0, 1, e)
+ grad01 * Q(q, 1, 1, e)
+ grad02 * Q(q, 2, 1, e);
const real_t Dzu1 = grad00 * Q(q, 0, 2, e)
+ grad01 * Q(q, 1, 2, e)
+ grad02 * Q(q, 2, 2, e);
const real_t Dxu2 = grad10 * Q(q, 0, 0, e)
+ grad11 * Q(q, 1, 0, e)
+ grad12 * Q(q, 2, 0, e);
const real_t Dyu2 = grad10 * Q(q, 0, 1, e)
+ grad11 * Q(q, 1, 1, e)
+ grad12 * Q(q, 2, 1, e);
const real_t Dzu2 = grad10 * Q(q, 0, 2, e)
+ grad11 * Q(q, 1, 2, e)
+ grad12 * Q(q, 2, 2, e);
const real_t Dxu3 = grad20 * Q(q, 0, 0, e)
+ grad21 * Q(q, 1, 0, e)
+ grad22 * Q(q, 2, 0, e);
const real_t Dyu3 = grad20 * Q(q, 0, 1, e)
+ grad21 * Q(q, 1, 1, e)
+ grad22 * Q(q, 2, 1, e);
const real_t Dzu3 = grad20 * Q(q, 0, 2, e)
+ grad21 * Q(q, 1, 2, e)
+ grad22 * Q(q, 2, 2, e);
Z[qz][qy][qx][0] = u1 * Dxu1 + u2 * Dyu1 + u3 * Dzu1;
Z[qz][qy][qx][1] = u1 * Dxu2 + u2 * Dyu2 + u3 * Dzu2;
Z[qz][qy][qx][2] = u1 * Dxu3 + u2 * Dyu3 + u3 * Dzu3;
}
}
}
for (int qz = 0; qz < Q1D; ++qz)
{
real_t opXY[max_D1D][max_D1D][VDIM];
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
opXY[dy][dx][0] = 0.0;
opXY[dy][dx][1] = 0.0;
opXY[dy][dx][2] = 0.0;
}
}
for (int qy = 0; qy < Q1D; ++qy)
{
real_t opX[max_D1D][VDIM];
for (int dx = 0; dx < D1D; ++dx)
{
opX[dx][0] = 0.0;
opX[dx][1] = 0.0;
opX[dx][2] = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Btx = Bt(dx, qx);
opX[dx][0] += Btx * Z[qz][qy][qx][0];
opX[dx][1] += Btx * Z[qz][qy][qx][1];
opX[dx][2] += Btx * Z[qz][qy][qx][2];
}
}
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
const real_t Bty = Bt(dy, qy);
opXY[dy][dx][0] += Bty * opX[dx][0];
opXY[dy][dx][1] += Bty * opX[dx][1];
opXY[dy][dx][2] += Bty * opX[dx][2];
}
}
}
for (int dz = 0; dz < D1D; ++dz)
{
for (int dy = 0; dy < D1D; ++dy)
{
for (int dx = 0; dx < D1D; ++dx)
{
const real_t Btz = Bt(dz, qz);
y(dx, dy, dz, 0, e) += Btz * opXY[dy][dx][0];
y(dx, dy, dz, 1, e) += Btz * opXY[dy][dx][1];
y(dx, dy, dz, 2, e) += Btz * opXY[dy][dx][2];
}
}
}
}
});
}
template<int T_D1D = 0, int T_Q1D = 0, int T_MAX_D1D = 0, int T_MAX_Q1D = 0>
static void SmemPAConvectionNLApply3D(const int NE,
const Array<real_t> &b_,
const Array<real_t> &g_,
const Vector &d_,
const Vector &x_,
Vector &y_,
const int d1d = 0,
const int q1d = 0)
{
constexpr int VDIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : T_MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MAX_Q1D;
MFEM_VERIFY(D1D <= MD1, "");
MFEM_VERIFY(Q1D <= MQ1, "");
auto b = Reshape(b_.Read(), Q1D, D1D);
auto g = Reshape(g_.Read(), Q1D, D1D);
auto D = Reshape(d_.Read(), Q1D * Q1D * Q1D, VDIM, VDIM, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, VDIM, NE);
auto Y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
const int tidz = MFEM_THREAD_ID(z);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
constexpr int MD1 = T_D1D ? T_D1D : T_MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : T_MAX_Q1D;
MFEM_SHARED real_t BG[2][MQ1 * MD1];
real_t(*B)[MD1] = (real_t(*)[MD1])(BG + 0);
real_t(*G)[MD1] = (real_t(*)[MD1])(BG + 1);
real_t(*Bt)[MQ1] = (real_t(*)[MQ1])(BG + 0);
MFEM_SHARED real_t U[2][MQ1][MQ1][MQ1];
MFEM_SHARED real_t sm0[3][MQ1 * MQ1 * MQ1];
MFEM_SHARED real_t sm1[3][MQ1 * MQ1 * MQ1];
real_t(*DDQ0)[MD1][MQ1] = (real_t(*)[MD1][MQ1])(sm0 + 0);
real_t(*DDQ1)[MD1][MQ1] = (real_t(*)[MD1][MQ1])(sm0 + 1);
real_t(*X)[MD1][MD1] = (real_t(*)[MD1][MD1])(sm0 + 2);
real_t(*DQQ0)[MQ1][MQ1] = (real_t(*)[MQ1][MQ1])(sm1 + 0);
real_t(*DQQ1)[MQ1][MQ1] = (real_t(*)[MQ1][MQ1])(sm1 + 1);
real_t(*DQQ2)[MQ1][MQ1] = (real_t(*)[MQ1][MQ1])(sm1 + 2);
real_t(*QQQ0)[MQ1][MQ1] = (real_t(*)[MQ1][MQ1])(sm0 + 0);
real_t(*QQQ1)[MQ1][MQ1] = (real_t(*)[MQ1][MQ1])(sm0 + 1);
real_t(*QQQ2)[MQ1][MQ1] = (real_t(*)[MQ1][MQ1])(sm0 + 2);
real_t(*QQD0)[MQ1][MD1] = (real_t(*)[MQ1][MD1])(sm1 + 0);
real_t(*QDD0)[MD1][MD1] = (real_t(*)[MD1][MD1])(sm0 + 0);
MFEM_SHARED real_t Z[MQ1][MQ1][MQ1];
for (int cy = 0; cy < VDIM; ++cy)
{
if (tidz == 0)
{
MFEM_FOREACH_THREAD(q, x, Q1D)
{
MFEM_FOREACH_THREAD(d, y, D1D)
{
B[q][d] = b(q, d);
G[q][d] = g(q, d);
}
}
}
MFEM_FOREACH_THREAD(qz, z, Q1D)
{
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(qx, x, Q1D) { Z[qz][qy][qx] = 0.0; }
}
}
MFEM_SYNC_THREAD;
for (int c = 0; c < VDIM; ++c)
{
MFEM_FOREACH_THREAD(dz, z, D1D)
{
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(dx, x, D1D)
{
X[dz][dy][dx] = x(dx, dy, dz, cy, e);
U[0][dz][dy][dx] = x(dx, dy, dz, c, e);
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, D1D)
{
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
real_t u = 0.0;
real_t v = 0.0;
real_t z = 0.0;
for (int dx = 0; dx < D1D; ++dx)
{
const real_t coord = X[dz][dy][dx];
const real_t value = U[0][dz][dy][dx];
u += coord * B[qx][dx];
v += coord * G[qx][dx];
z += value * B[qx][dx];
}
DDQ0[dz][dy][qx] = u;
DDQ1[dz][dy][qx] = v;
U[1][dz][dy][qx] = z;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, D1D)
{
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
real_t u = 0.0;
real_t v = 0.0;
real_t w = 0.0;
real_t z = 0.0;
for (int dy = 0; dy < D1D; ++dy)
{
u += DDQ1[dz][dy][qx] * B[qy][dy];
v += DDQ0[dz][dy][qx] * G[qy][dy];
w += DDQ0[dz][dy][qx] * B[qy][dy];
z += U[1][dz][dy][qx] * B[qy][dy];
}
DQQ0[dz][qy][qx] = u;
DQQ1[dz][qy][qx] = v;
DQQ2[dz][qy][qx] = w;
U[0][dz][qy][qx] = z;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, Q1D)
{
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
real_t u = 0.0;
real_t v = 0.0;
real_t w = 0.0;
real_t z = 0.0;
for (int dz = 0; dz < D1D; ++dz)
{
u += DQQ0[dz][qy][qx] * B[qz][dz];
v += DQQ1[dz][qy][qx] * B[qz][dz];
w += DQQ2[dz][qy][qx] * G[qz][dz];
z += U[0][dz][qy][qx] * B[qz][dz];
}
QQQ0[qz][qy][qx] = u;
QQQ1[qz][qy][qx] = v;
QQQ2[qz][qy][qx] = w;
U[1][qz][qy][qx] = z;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, Q1D)
{
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(qx, x, Q1D)
{
const int q = qx + (qy + qz * Q1D) * Q1D;
const real_t z = U[1][qz][qy][qx];
const real_t gX = QQQ0[qz][qy][qx];
const real_t gY = QQQ1[qz][qy][qx];
const real_t gZ = QQQ2[qz][qy][qx];
const real_t d = gX * D(q, 0, c, e) + gY * D(q, 1, c, e)
+ gZ * D(q, 2, c, e);
Z[qz][qy][qx] += z * d;
}
}
}
MFEM_SYNC_THREAD;
} // for each conv component
if (tidz == 0)
{
MFEM_FOREACH_THREAD(d, y, D1D)
{
MFEM_FOREACH_THREAD(q, x, Q1D) { Bt[d][q] = b(q, d); }
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, Q1D)
{
MFEM_FOREACH_THREAD(qy, y, Q1D)
{
MFEM_FOREACH_THREAD(dx, x, D1D)
{
real_t u = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
u += Z[qz][qy][qx] * Bt[dx][qx];
}
QQD0[qz][qy][dx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(qz, z, Q1D)
{
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(dx, x, D1D)
{
real_t u = 0.0;
for (int qy = 0; qy < Q1D; ++qy)
{
u += QQD0[qz][qy][dx] * Bt[dy][qy];
}
QDD0[qz][dy][dx] = u;
}
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD(dz, z, D1D)
{
MFEM_FOREACH_THREAD(dy, y, D1D)
{
MFEM_FOREACH_THREAD(dx, x, D1D)
{
real_t u = 0.0;
for (int qz = 0; qz < Q1D; ++qz)
{
u += QDD0[qz][dy][dx] * Bt[dz][qz];
}
Y(dx, dy, dz, cy, e) += u;
}
}
}
MFEM_SYNC_THREAD;
}
});
MFEM_ABORT("dim " << dim << " not supported!");
}
}
void VectorConvectionNLFIntegrator::AddMultPA(const Vector &x, Vector &y) const
@@ -812,26 +197,13 @@ void VectorConvectionNLFIntegrator::AddMultPA(const Vector &x, Vector &y) const
}
else
{
const int NE = ne;
const int D1D = maps->ndof;
const int Q1D = maps->nqpt;
const Vector &QV = pa_data;
const Array<real_t> &B = maps->B;
const Array<real_t> &G = maps->G;
const Array<real_t> &Bt = maps->Bt;
if (dim == 2)
{
return PAConvectionNLApply2D(NE, B, G, Bt, QV, x, y, D1D, Q1D);
}
if (dim == 3)
{
constexpr int T_MAX_D1D = 8;
constexpr int T_MAX_Q1D = 8;
MFEM_VERIFY(D1D <= T_MAX_D1D && Q1D <= T_MAX_Q1D, "Not yet implemented!");
return SmemPAConvectionNLApply3D<0, 0, T_MAX_D1D, T_MAX_Q1D>
(NE, B, G, QV, x, y, D1D, Q1D);
}
MFEM_ABORT("Not yet implemented!");
AddMultPAKernels::Run(dim, d1d, q1d, ne,
maps->B.Read(),
maps->G.Read(),
pa_adj.Read(),
x.Read(),
y.ReadWrite(),
d1d, q1d);
}
}
+209
View File
@@ -0,0 +1,209 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#pragma once
#include "../../config/config.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../kernels.hpp"
#include "../nonlininteg.hpp"
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
namespace internal
{
// PA Convection NL 2D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPAConvectionNLApply2D(const int NE,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *x,
real_t *y,
const int d1d = 0,
const int q1d = 0)
{
static constexpr int VDIM = 2, DIM = 2;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto B = Reshape(b, Q1D, D1D);
const auto G = Reshape(g, Q1D, D1D);
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, NE);
const auto X = Reshape(x, D1D, D1D, VDIM, NE);
auto Y = Reshape(y, D1D, D1D, VDIM, NE);
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t smem[MQ1][MQ1], sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::vd_regs2d_t<VDIM, DIM, MQ1> g0, g1;
kernels::internal::v_regs2d_t<VDIM, MQ1> r0, r1;
kernels::internal::v_regs2d_t<VDIM, MQ1> s0, s1;
kernels::internal::LoadMatrix(D1D, Q1D, B, sB);
kernels::internal::LoadMatrix(D1D, Q1D, G, sG);
kernels::internal::LoadDofs2d(e, D1D, X, r0);
kernels::internal::Eval2d(D1D, Q1D, smem, sB, r0, r1); // u vector-value
kernels::internal::LoadDofs2d(e, D1D, X, g0);
kernels::internal::Grad2d(D1D, Q1D, smem, sB, sG, g0, g1); // u vector-gradient
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const future::tensor<real_t, 2> U =
{
r1[0][qy][qx], r1[1][qy][qx]
};
const future::tensor<real_t, 2,2> gradU = {{
{g1[0][0][qy][qx], g1[1][0][qy][qx]},
{g1[0][1][qy][qx], g1[1][1][qy][qx]},
}
};
const future::tensor<real_t, 2,2> Q = {{
{A(0,0,qx,qy,e), A(1,0,qx,qy,e)},
{A(0,1,qx,qy,e), A(1,1,qx,qy,e)},
}
};
const future::tensor<real_t, 2> conv = transpose(gradU) * (Q * U);
s0[0][qy][qx] = conv[0];
s0[1][qy][qx] = conv[1];
}
}
MFEM_SYNC_THREAD;
kernels::internal::EvalTranspose2d(D1D, Q1D, smem, sB, s0, s1);
kernels::internal::WriteDofs2d(e, D1D, s1, Y);
});
}
// PA Convection NL 3D kernel
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPAConvectionNLApply3D(const int NE,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *x,
real_t *y,
const int d1d = 0,
const int q1d = 0)
{
static constexpr int VDIM = 3, DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto B = Reshape(b, Q1D, D1D);
const auto G = Reshape(g, Q1D, D1D);
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, Q1D, NE);
const auto X = Reshape(x, D1D, D1D, D1D, VDIM, NE);
auto Y = Reshape(y, D1D, D1D, D1D, VDIM, NE);
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t smem[MQ1][MQ1], sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::vd_regs3d_t<VDIM, DIM, MQ1> g0, g1;
kernels::internal::v_regs3d_t<VDIM, MQ1> r0, r1;
kernels::internal::v_regs3d_t<VDIM, MQ1> s0, s1;
kernels::internal::LoadMatrix(D1D, Q1D, B, sB);
kernels::internal::LoadMatrix(D1D, Q1D, G, sG);
kernels::internal::LoadDofs3d(e, D1D, X, r0);
kernels::internal::Eval3d(D1D, Q1D, smem, sB, r0, r1); // u vector-value
kernels::internal::LoadDofs3d(e, D1D, X, g0);
kernels::internal::Grad3d(D1D, Q1D, smem, sB, sG, g0, g1); // u vector-gradient
for (int qz = 0; qz < Q1D; qz++)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
const future::tensor<real_t, 3> U =
{
r1[0][qz][qy][qx], r1[1][qz][qy][qx], r1[2][qz][qy][qx]
};
const future::tensor<real_t, 3,3> gradU = {{
{g1[0][0][qz][qy][qx], g1[1][0][qz][qy][qx], g1[2][0][qz][qy][qx]},
{g1[0][1][qz][qy][qx], g1[1][1][qz][qy][qx], g1[2][1][qz][qy][qx]},
{g1[0][2][qz][qy][qx], g1[1][2][qz][qy][qx], g1[2][2][qz][qy][qx]}
}
};
const future::tensor<real_t, 3,3> Q = {{
{A(0,0,qx,qy,qz,e), A(1,0,qx,qy,qz,e), A(2,0,qx,qy,qz,e)},
{A(0,1,qx,qy,qz,e), A(1,1,qx,qy,qz,e), A(2,1,qx,qy,qz,e)},
{A(0,2,qx,qy,qz,e), A(1,2,qx,qy,qz,e), A(2,2,qx,qy,qz,e)}
}
};
const future::tensor<real_t, 3> conv = transpose(gradU) * (Q * U);
s0[0][qz][qy][qx] = conv[0];
s0[1][qz][qy][qx] = conv[1];
s0[2][qz][qy][qx] = conv[2];
}
}
}
MFEM_SYNC_THREAD;
kernels::internal::EvalTranspose3d(D1D, Q1D, smem, sB, s0, s1);
kernels::internal::WriteDofs3d(e, D1D, s1, Y);
});
}
} // namespace internal
template<int DIM, int T_D1D, int T_Q1D>
VectorConvectionNLFIntegrator::AddMultPAType
VectorConvectionNLFIntegrator::AddMultPAKernels::Kernel()
{
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
if constexpr (DIM == 2)
{
return internal::SmemPAConvectionNLApply2D<T_D1D, T_Q1D>;
}
else if constexpr (DIM == 3)
{
return internal::SmemPAConvectionNLApply3D<T_D1D, T_Q1D>;
}
MFEM_ABORT("Unsupported kernel");
}
inline VectorConvectionNLFIntegrator::AddMultPAType
VectorConvectionNLFIntegrator::AddMultPAKernels::Fallback
(int dim, int d1d, int q1d)
{
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
if (dim == 2)
{
return internal::SmemPAConvectionNLApply2D<>;
}
else if (dim == 3)
{
return internal::SmemPAConvectionNLApply3D<>;
}
MFEM_ABORT("Unsupported kernel");
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
@@ -0,0 +1,50 @@
// 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 "../ceed/interface/util.hpp"
#include "./nonlininteg_vecconvection_pa_diag.hpp" // IWYU pragma: keep
namespace mfem
{
void VectorConvectionNLFIntegrator::AssembleGradDiagonalPA(Vector &de) const
{
MFEM_VERIFY(!DeviceCanUseCeed(),
"VectorConvectionNLFIntegrator PA gradients are not supported "
"with the libCEED backend");
if (dim == 2)
{
GradDiagPA2D::Run(d1d, q1d, ne,
maps->B.Read(),
maps->G.Read(),
pa_adj.Read(),
pa_u.Read(),
de.ReadWrite(),
d1d, q1d);
}
else if (dim == 3)
{
GradDiagPA3D::Run(d1d, q1d, ne,
maps->B.Read(),
maps->G.Read(),
pa_adj.Read(),
pa_u.Read(),
de.ReadWrite(),
d1d, q1d);
}
else
{
MFEM_ABORT("Unsupported dimension");
}
}
} // namespace mfem
@@ -0,0 +1,302 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#pragma once
#include "../../config/config.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../kernels.hpp"
#include "../nonlininteg.hpp"
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
namespace internal
{
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPAConvectionNLGradDiagonal2D(const int NE,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *u,
real_t *de,
const int d1d,
const int q1d)
{
static constexpr int VDIM = 2, DIM = 2;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, NE);
const auto U = Reshape(u, D1D, D1D, VDIM, NE);
auto D = Reshape(de, D1D, D1D, VDIM, NE);
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t sM[3][MQ1][MQ1], sQ[3][MQ1][MQ1];
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::v_regs2d_t<VDIM, MQ1> r0, r1;
kernels::internal::vd_regs2d_t<VDIM, DIM, MQ1> g0, g1;
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
kernels::internal::LoadDofs2d(e, D1D, U, r0);
kernels::internal::Eval2d(D1D, Q1D, sM[0], sB, r0, r1);
kernels::internal::LoadDofs2d(e, D1D, U, g0);
kernels::internal::Grad2d(D1D, Q1D, sM[0], sB, sG, g0, g1);
for (int v = 0; v < VDIM; ++v)
{
future::tensor<real_t, VDIM> e_v = {};
e_v[v] = real_t(1);
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
const future::tensor<real_t, VDIM> u_val =
{
r1[0][qy][qx], r1[1][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> Q_adj =
{
{ { A(0, 0, qx, qy, e), A(1, 0, qx, qy, e) },
{ A(0, 1, qx, qy, e), A(1, 1, qx, qy, e) }
}
};
const future::tensor<real_t, VDIM, DIM> grad_U =
{
{ { g1[0][0][qy][qx], g1[1][0][qy][qx] },
{ g1[0][1][qy][qx], g1[1][1][qy][qx] }
}
};
const auto one = Q_adj * u_val;
const auto two = transpose(grad_U) * (Q_adj * e_v);
sQ[0][qx][qy] = one[0];
sQ[1][qx][qy] = one[1];
sQ[2][qx][qy] = two[v];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
real_t s[3] = {};
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t By = sB[dy][qy], Gy = sG[dy][qy];
s[0] += By * By * sQ[0][qx][qy];
s[1] += Gy * By * sQ[1][qx][qy];
s[2] += By * By * sQ[2][qx][qy];
}
sM[0][qx][dy] = s[0];
sM[1][qx][dy] = s[1];
sM[2][qx][dy] = s[2];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t d = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Bx = sB[dx][qx], Gx = sG[dx][qx];
d += Gx * Bx * sM[0][qx][dy] +
Bx * Bx * sM[1][qx][dy] +
Bx * Bx * sM[2][qx][dy];
}
D(dx, dy, v, e) += d;
}
}
MFEM_SYNC_THREAD;
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPAConvectionNLGradDiagonal3D(const int NE,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *u,
real_t *de,
const int d1d,
const int q1d)
{
static constexpr int VDIM = 3, DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, Q1D, NE);
const auto U = Reshape(u, D1D, D1D, D1D, VDIM, NE);
auto D = Reshape(de, D1D, D1D, D1D, VDIM, NE);
mfem::forall_2D<T_Q1D * T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t sM[4][MQ1][MQ1], sQ[4][MQ1][MQ1];
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::v_regs3d_t<VDIM, MQ1> r0, r1;
kernels::internal::vd_regs3d_t<VDIM, DIM, MQ1> g0, g1;
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
kernels::internal::LoadDofs3d(e, D1D, U, r0);
kernels::internal::Eval3d(D1D, Q1D, sM[0], sB, r0, r1);
kernels::internal::LoadDofs3d(e, D1D, U, g0);
kernels::internal::Grad3d(D1D, Q1D, sM[0], sB, sG, g0, g1);
for (int v = 0; v < VDIM; ++v)
{
future::tensor<real_t, VDIM> e_v = {};
e_v[v] = real_t(1);
for (int dz = 0; dz < D1D; ++dz)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
real_t s[4] = {};
for (int qz = 0; qz < Q1D; ++qz)
{
const future::tensor<real_t, VDIM> u_val =
{
r1[0][qz][qy][qx], r1[1][qz][qy][qx], r1[2][qz][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> Q_adj = {{
{A(0,0,qx,qy,qz,e), A(1,0,qx,qy,qz,e), A(2,0,qx,qy,qz,e)},
{A(0,1,qx,qy,qz,e), A(1,1,qx,qy,qz,e), A(2,1,qx,qy,qz,e)},
{A(0,2,qx,qy,qz,e), A(1,2,qx,qy,qz,e), A(2,2,qx,qy,qz,e)}
}
};
const future::tensor<real_t, VDIM, DIM> grad_U = {{
{g1[0][0][qz][qy][qx], g1[1][0][qz][qy][qx], g1[2][0][qz][qy][qx]},
{g1[0][1][qz][qy][qx], g1[1][1][qz][qy][qx], g1[2][1][qz][qy][qx]},
{g1[0][2][qz][qy][qx], g1[1][2][qz][qy][qx], g1[2][2][qz][qy][qx]}
}
};
const auto one = Q_adj * u_val;
const auto two = transpose(grad_U) * (Q_adj * e_v);
const real_t Bz = sB[dz][qz], Gz = sG[dz][qz];
s[0] += one[0] * Bz * Bz;
s[1] += one[1] * Bz * Bz;
s[2] += one[2] * Bz * Gz;
s[3] += two[v] * Bz * Bz;
}
sQ[0][qx][qy] = s[0];
sQ[1][qx][qy] = s[1];
sQ[2][qx][qy] = s[2];
sQ[3][qx][qy] = s[3];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
real_t s[4] = {};
for (int qy = 0; qy < Q1D; ++qy)
{
const real_t By = sB[dy][qy], Gy = sG[dy][qy];
s[0] += By * By * sQ[0][qx][qy];
s[1] += Gy * By * sQ[1][qx][qy];
s[2] += By * By * sQ[2][qx][qy];
s[3] += By * By * sQ[3][qx][qy];
}
sM[0][dy][qx] = s[0];
sM[1][dy][qx] = s[1];
sM[2][dy][qx] = s[2];
sM[3][dy][qx] = s[3];
}
}
MFEM_SYNC_THREAD;
MFEM_FOREACH_THREAD_DIRECT(dy, y, D1D)
{
MFEM_FOREACH_THREAD_DIRECT(dx, x, D1D)
{
real_t d = 0.0;
for (int qx = 0; qx < Q1D; ++qx)
{
const real_t Bx = sB[dx][qx], Gx = sG[dx][qx];
d += Gx * Bx * sM[0][dy][qx];
d += Bx * Bx * sM[1][dy][qx];
d += Bx * Bx * sM[2][dy][qx];
d += Bx * Bx * sM[3][dy][qx];
}
D(dx, dy, dz, v, e) += d;
}
}
MFEM_SYNC_THREAD;
}
}
});
}
} // namespace internal
template<int T_D1D, int T_Q1D>
VectorConvectionNLFIntegrator::GradDiagPAType
VectorConvectionNLFIntegrator::GradDiagPA2D::Kernel()
{
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
return internal::SmemPAConvectionNLGradDiagonal2D<T_D1D, T_Q1D>;
}
inline VectorConvectionNLFIntegrator::GradDiagPAType
VectorConvectionNLFIntegrator::GradDiagPA2D::Fallback(int d1d, int q1d)
{
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
return internal::SmemPAConvectionNLGradDiagonal2D<>;
}
template<int T_D1D, int T_Q1D>
VectorConvectionNLFIntegrator::GradDiagPAType
VectorConvectionNLFIntegrator::GradDiagPA3D::Kernel()
{
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
return internal::SmemPAConvectionNLGradDiagonal3D<T_D1D, T_Q1D>;
}
inline VectorConvectionNLFIntegrator::GradDiagPAType
VectorConvectionNLFIntegrator::GradDiagPA3D::Fallback(int d1d, int q1d)
{
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
return internal::SmemPAConvectionNLGradDiagonal3D<>;
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
@@ -0,0 +1,64 @@
// 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 "../ceed/interface/util.hpp"
#include "./nonlininteg_vecconvection_pa_grad.hpp" // IWYU pragma: keep
namespace mfem
{
void VectorConvectionNLFIntegrator::AssembleGradPA(
const Vector &u, const FiniteElementSpace &fes)
{
MFEM_VERIFY(!DeviceCanUseCeed(),
"VectorConvectionNLFIntegrator PA gradients are not supported "
"with the libCEED backend");
this->pa_u = u;
AssemblePA(fes);
}
void VectorConvectionNLFIntegrator::AddMultGradPA(const Vector &x,
Vector &y) const
{
MFEM_VERIFY(!DeviceCanUseCeed(),
"VectorConvectionNLFIntegrator PA gradients are not supported "
"with the libCEED backend");
if (dim == 2)
{
AddMultGradPA2D::Run(d1d, q1d, ne,
maps->B.Read(),
maps->G.Read(),
pa_adj.Read(),
pa_u.Read(),
x.Read(),
y.ReadWrite(),
d1d, q1d);
}
else if (dim == 3)
{
AddMultGradPA3D::Run(d1d, q1d, ne,
maps->B.Read(),
maps->G.Read(),
pa_adj.Read(),
pa_u.Read(),
x.Read(),
y.ReadWrite(),
d1d, q1d);
}
else
{
MFEM_ABORT("Unsupported dimension");
}
}
} // namespace mfem
@@ -0,0 +1,257 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#pragma once
#include "../../config/config.hpp"
#include "../../general/forall.hpp"
#include "../../linalg/dtensor.hpp"
#include "../kernels.hpp"
#include "../nonlininteg.hpp"
namespace mfem
{
/// \cond DO_NOT_DOCUMENT
namespace internal
{
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPAConvectionNLGradApply2D(const int ne,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *u,
const real_t *du,
real_t *y,
const int d1d,
const int q1d)
{
static constexpr int VDIM = 2, DIM = 2;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, ne);
const auto U = Reshape(u, D1D, D1D, VDIM, ne);
const auto dU = Reshape(du, D1D, D1D, VDIM, ne);
auto Y = Reshape(y, D1D, D1D, VDIM, ne);
mfem::forall_2D<T_Q1D * T_Q1D>(ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t smem[MQ1][MQ1];
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::vd_regs2d_t<VDIM, DIM, MQ1> g0, g1, g2;
kernels::internal::v_regs2d_t<DIM, MQ1> r0, r1, r2;
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
kernels::internal::LoadDofs2d(e, D1D, dU, g0);
kernels::internal::Grad2d(D1D, Q1D, smem, sB, sG, g0, g1); // δu gradient
kernels::internal::LoadDofs2d(e, D1D, U, r0);
kernels::internal::Eval2d(D1D, Q1D, smem, sB, r0, r2); // u value
kernels::internal::LoadDofs2d(e, D1D, dU, r0);
kernels::internal::Eval2d(D1D, Q1D, smem, sB, r0, r1); // δu value
kernels::internal::LoadDofs2d(e, D1D, U, g0);
kernels::internal::Grad2d(D1D, Q1D, smem, sB, sG, g0, g2); // u gradient
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
// First part of the Jacobian: u·∇δu
const future::tensor<real_t, DIM> u_val =
{
r2[0][qy][qx], r2[1][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> Q_adj =
{
{ { A(0, 0, qx, qy, e), A(1, 0, qx, qy, e) },
{ A(0, 1, qx, qy, e), A(1, 1, qx, qy, e) }
}
};
const future::tensor<real_t, VDIM, DIM> grad_dU =
{
{ { g1[0][0][qy][qx], g1[1][0][qy][qx] },
{ g1[0][1][qy][qx], g1[1][1][qy][qx] }
}
};
const auto one = transpose(grad_dU) * (Q_adj * u_val);
// Second part of the Jacobian: δu·∇u
const future::tensor<real_t, DIM> du_val =
{
r1[0][qy][qx], r1[1][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> grad_U =
{
{ { g2[0][0][qy][qx], g2[1][0][qy][qx] },
{ g2[0][1][qy][qx], g2[1][1][qy][qx] }
}
};
const auto two = transpose(grad_U) * (Q_adj * du_val);
// u⋅∇δu + δu⋅∇u
r0[0][qy][qx] = one[0] + two[0];
r0[1][qy][qx] = one[1] + two[1];
}
}
MFEM_SYNC_THREAD;
kernels::internal::EvalTranspose2d(D1D, Q1D, smem, sB, r0, r1);
kernels::internal::WriteDofs2d(e, D1D, r1, Y);
});
}
template<int T_D1D = 0, int T_Q1D = 0>
inline void SmemPAConvectionNLGradApply3D(const int ne,
const real_t *b,
const real_t *g,
const real_t *a,
const real_t *u,
const real_t *du,
real_t *y,
const int d1d,
const int q1d)
{
static constexpr int VDIM = 3, DIM = 3;
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto A = Reshape(a, VDIM, DIM, Q1D, Q1D, Q1D, ne);
const auto U = Reshape(u, D1D, D1D, D1D, VDIM, ne);
const auto dU = Reshape(du, D1D, D1D, D1D, VDIM, ne);
auto Y = Reshape(y, D1D, D1D, D1D, VDIM, ne);
mfem::forall_2D<T_Q1D * T_Q1D>(ne, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
constexpr int MQ1 = T_Q1D ? T_Q1D : DofQuadLimits::MAX_Q1D;
MFEM_SHARED real_t smem[MQ1][MQ1];
MFEM_SHARED real_t sB[MD1][MQ1], sG[MD1][MQ1];
kernels::internal::v_regs3d_t<VDIM, MQ1> r0, r1, r2;
kernels::internal::vd_regs3d_t<VDIM, DIM, MQ1> g0, g1, g2;
kernels::internal::LoadMatrix(D1D, Q1D, b, sB);
kernels::internal::LoadMatrix(D1D, Q1D, g, sG);
kernels::internal::LoadDofs3d(e, D1D, dU, g0);
kernels::internal::Grad3d(D1D, Q1D, smem, sB, sG, g0, g1); // δu gradient
kernels::internal::LoadDofs3d(e, D1D, U, r0);
kernels::internal::Eval3d(D1D, Q1D, smem, sB, r0, r2); // u value
kernels::internal::LoadDofs3d(e, D1D, dU, r0);
kernels::internal::Eval3d(D1D, Q1D, smem, sB, r0, r1); // δu value
kernels::internal::LoadDofs3d(e, D1D, U, g0);
kernels::internal::Grad3d(D1D, Q1D, smem, sB, sG, g0, g2); // u gradient
for (int qz = 0; qz < Q1D; qz++)
{
MFEM_FOREACH_THREAD_DIRECT(qy, y, Q1D)
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, Q1D)
{
// First part of the Jacobian: u·∇δu
const future::tensor<real_t, DIM> u_val =
{
r2[0][qz][qy][qx],
r2[1][qz][qy][qx],
r2[2][qz][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> Q_adj = {{
{A(0,0,qx,qy,qz,e), A(1,0,qx,qy,qz,e), A(2,0,qx,qy,qz,e)},
{A(0,1,qx,qy,qz,e), A(1,1,qx,qy,qz,e), A(2,1,qx,qy,qz,e)},
{A(0,2,qx,qy,qz,e), A(1,2,qx,qy,qz,e), A(2,2,qx,qy,qz,e)}
}
};
const future::tensor<real_t, DIM, DIM> grad_dU = {{
{g1[0][0][qz][qy][qx], g1[1][0][qz][qy][qx], g1[2][0][qz][qy][qx]},
{g1[0][1][qz][qy][qx], g1[1][1][qz][qy][qx], g1[2][1][qz][qy][qx]},
{g1[0][2][qz][qy][qx], g1[1][2][qz][qy][qx], g1[2][2][qz][qy][qx]}
}
};
const auto one = transpose(grad_dU) * (Q_adj * u_val);
// Second part of the Jacobian: δu·∇u
const future::tensor<real_t, DIM> du_val =
{
r1[0][qz][qy][qx], r1[1][qz][qy][qx], r1[2][qz][qy][qx]
};
const future::tensor<real_t, VDIM, DIM> grad_U = {{
{g2[0][0][qz][qy][qx], g2[1][0][qz][qy][qx], g2[2][0][qz][qy][qx]},
{g2[0][1][qz][qy][qx], g2[1][1][qz][qy][qx], g2[2][1][qz][qy][qx]},
{g2[0][2][qz][qy][qx], g2[1][2][qz][qy][qx], g2[2][2][qz][qy][qx]}
}
};
const auto two = transpose(grad_U) * (Q_adj * du_val);
// u⋅∇δu + δu⋅∇u
r0[0][qz][qy][qx] = one[0] + two[0];
r0[1][qz][qy][qx] = one[1] + two[1];
r0[2][qz][qy][qx] = one[2] + two[2];
}
}
}
MFEM_SYNC_THREAD;
kernels::internal::EvalTranspose3d(D1D, Q1D, smem, sB, r0, r1);
kernels::internal::WriteDofs3d(e, D1D, r1, Y);
});
}
} // namespace internal
template<int T_D1D, int T_Q1D>
VectorConvectionNLFIntegrator::AddMultGradPAType
VectorConvectionNLFIntegrator::AddMultGradPA2D::Kernel()
{
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
return internal::SmemPAConvectionNLGradApply2D<T_D1D, T_Q1D>;
}
inline VectorConvectionNLFIntegrator::AddMultGradPAType
VectorConvectionNLFIntegrator::AddMultGradPA2D::Fallback(int d1d, int q1d)
{
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
return internal::SmemPAConvectionNLGradApply2D<>;
}
template<int T_D1D, int T_Q1D>
VectorConvectionNLFIntegrator::AddMultGradPAType
VectorConvectionNLFIntegrator::AddMultGradPA3D::Kernel()
{
static_assert(T_D1D <= T_Q1D, "d1d > q1d is not supported");
return internal::SmemPAConvectionNLGradApply3D<T_D1D, T_Q1D>;
}
inline VectorConvectionNLFIntegrator::AddMultGradPAType
VectorConvectionNLFIntegrator::AddMultGradPA3D::Fallback(int d1d, int q1d)
{
MFEM_VERIFY(d1d <= q1d, "d1d > q1d is not supported");
MFEM_VERIFY(d1d <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(q1d <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
return internal::SmemPAConvectionNLGradApply3D<>;
}
/// \endcond DO_NOT_DOCUMENT
} // namespace mfem
+4 -8
View File
@@ -542,7 +542,10 @@ void QuadratureFunctions1D::GaussJacobi(const int np, const real_t alpha,
return;
}
#ifndef MFEM_USE_MPFR
#ifdef MFEM_USE_MPFR
MFEM_WARNING("MPFR implementation of Gauss-Jacobi quadrature not implemented yet. Falling "
"back to double precision implementation...");
#endif
const int n = np;
// common constants for Jacobi polynomials
@@ -611,13 +614,6 @@ void QuadratureFunctions1D::GaussJacobi(const int np, const real_t alpha,
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
}
+9 -2
View File
@@ -83,7 +83,7 @@ constexpr int SetMaxOf(int n) { return NextMultipleOf<4>(n); }
#endif // CUDA/HIP && DEVICE_COMPILE
/// Load 2D matrix into shared memory
template <int MQ1>
template <int MQ1, bool TRANSPOSE = false>
inline MFEM_HOST_DEVICE void LoadMatrix(const int d1d, const int q1d,
const real_t *M, real_t (*N)[MQ1])
{
@@ -91,7 +91,14 @@ inline MFEM_HOST_DEVICE void LoadMatrix(const int d1d, const int q1d,
{
MFEM_FOREACH_THREAD_DIRECT(qx, x, q1d)
{
N[dy][qx] = M[dy * q1d + qx];
if constexpr (TRANSPOSE)
{
N[dy][qx] = M[qx * d1d + dy];
}
else
{
N[dy][qx] = M[dy * q1d + qx];
}
}
}
MFEM_SYNC_THREAD;
+4 -4
View File
@@ -94,10 +94,10 @@ void BatchedLOR_AMS::Form2DEdgeToVertex_RT(Array<int> &edge2vert)
const int iv0 = ix + iy*op1;
const int iv1 = ix1 + iy1*op1;
// Rotated gradient in 2D (-dy, dx), so flip the sign for the first
// component (c == 0).
e2v(0, iedge) = (c == 1) ? iv0 : iv1;
e2v(1, iedge) = (c == 1) ? iv1 : iv0;
// 2D curl (dy, -dx), so flip the sign for the second
// component (c == 1).
e2v(0, iedge) = (c == 0) ? iv0 : iv1;
e2v(1, iedge) = (c == 0) ? iv1 : iv0;
}
}
}
+12 -9
View File
@@ -142,8 +142,6 @@ static MFEM_HOST_DEVICE int GetAndIncrementNnzIndex(const int i_L, int* I)
int BatchedLORAssembly::FillI(SparseMatrix &A) const
{
static constexpr int Max = 16;
const int nvdof = fes_ho.GetVSize();
const int ndof_per_el = fes_ho.GetTypicalFE()->GetDof();
@@ -165,6 +163,8 @@ int BatchedLORAssembly::FillI(SparseMatrix &A) const
const auto K = dof_glob2loc_offsets_.Read();
const auto map = Reshape(sparse_mapping.Read(), nnz_per_row, ndof_per_el);
Array<int> ij_elts(dof_glob2loc_.Size() * 2);
auto d_ij_elts = Reshape(ij_elts.Write(), dof_glob2loc_.Size(), 2);
auto I = A.WriteI();
@@ -176,10 +176,10 @@ int BatchedLORAssembly::FillI(SparseMatrix &A) const
const int sii = el_dof_lex(ii_el, iel_ho);
const int ii = (sii >= 0) ? sii : -1 -sii;
// Get number and list of elements containing this DOF
int i_elts[Max];
const int i_offset = K[ii];
const int i_next_offset = K[ii+1];
const int i_ne = i_next_offset - i_offset;
int *i_elts = &d_ij_elts(i_offset, 0);
for (int e_i = 0; e_i < i_ne; ++e_i)
{
const int si_E = dof_glob2loc[i_offset+e_i]; // signed
@@ -202,7 +202,7 @@ int BatchedLORAssembly::FillI(SparseMatrix &A) const
}
else // assembly required
{
int j_elts[Max];
int *j_elts = &d_ij_elts(j_offset, 1);
for (int e_j = 0; e_j < j_ne; ++e_j)
{
const int sj_E = dof_glob2loc[j_offset+e_j]; // signed
@@ -269,7 +269,8 @@ void BatchedLORAssembly::FillJAndData(SparseMatrix &A) const
mfem::forall(nvdof + 1, [=] MFEM_HOST_DEVICE (int i) { I[i] = I2[i]; });
}
static constexpr int Max = 16;
Array<int> ij_B_el(dof_glob2loc_.Size() * 4);
auto d_ij_B_el = Reshape(ij_B_el.Write(), dof_glob2loc_.Size(), 4);
mfem::forall(ndof_per_el*nel_ho, [=] MFEM_HOST_DEVICE (int i)
{
@@ -279,11 +280,13 @@ void BatchedLORAssembly::FillJAndData(SparseMatrix &A) const
const int sii = el_dof_lex(ii_el, iel_ho); // signed
const int ii = (sii >= 0) ? sii : -1 - sii;
// Get number and list of elements containing this DOF
int i_elts[Max];
int i_B[Max];
const int i_offset = K[ii];
const int i_next_offset = K[ii+1];
const int i_ne = i_next_offset - i_offset;
int *i_elts = &d_ij_B_el(i_offset, 0);
int *i_B = &d_ij_B_el(i_offset, 1);
for (int e_i = 0; e_i < i_ne; ++e_i)
{
const int si_E = dof_glob2loc[i_offset+e_i]; // signed
@@ -312,8 +315,8 @@ void BatchedLORAssembly::FillJAndData(SparseMatrix &A) const
}
else // assembly required
{
int j_elts[Max];
int j_B[Max];
int *j_elts = &d_ij_B_el(j_offset, 2);
int *j_B = &d_ij_B_el(j_offset, 3);
for (int e_j = 0; e_j < j_ne; ++e_j)
{
const int sj_E = dof_glob2loc[j_offset+e_j]; // signed
+11
View File
@@ -100,6 +100,17 @@ PANonlinearFormExtension::Gradient::Gradient(const PANonlinearFormExtension &e):
void PANonlinearFormExtension::Gradient::AssembleGrad(const Vector &g)
{
if (DeviceCanUseCeed())
{
for (int i = 0; i < ext.dnfi.Size(); ++i)
{
MFEM_VERIFY(dynamic_cast<VectorConvectionNLFIntegrator *>
(ext.dnfi[i]) == nullptr,
"VectorConvectionNLFIntegrator PA gradients are not supported "
"with the libCEED backend");
}
}
ext.elemR->Mult(g, ext.xe);
for (int i = 0; i < ext.dnfi.Size(); ++i)
{
+70
View File
@@ -954,4 +954,74 @@ void SkewSymmetricVectorConvectionNLFIntegrator::AssembleElementGrad(
}
}
void ConvectiveVectorConvectionNLFIntegrator::AssemblePA(
const FiniteElementSpace &)
{
MFEM_ABORT("ConvectiveVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
void ConvectiveVectorConvectionNLFIntegrator::AssembleGradPA(
const Vector &, const FiniteElementSpace &)
{
MFEM_ABORT("ConvectiveVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
void ConvectiveVectorConvectionNLFIntegrator::AddMultPA(
const Vector &, Vector &) const
{
MFEM_ABORT("ConvectiveVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
void ConvectiveVectorConvectionNLFIntegrator::AddMultGradPA(
const Vector &, Vector &) const
{
MFEM_ABORT("ConvectiveVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
void ConvectiveVectorConvectionNLFIntegrator::AssembleGradDiagonalPA(
Vector &) const
{
MFEM_ABORT("ConvectiveVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
void SkewSymmetricVectorConvectionNLFIntegrator::AssemblePA(
const FiniteElementSpace &)
{
MFEM_ABORT("SkewSymmetricVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
void SkewSymmetricVectorConvectionNLFIntegrator::AssembleGradPA(
const Vector &, const FiniteElementSpace &)
{
MFEM_ABORT("SkewSymmetricVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
void SkewSymmetricVectorConvectionNLFIntegrator::AddMultPA(
const Vector &, Vector &) const
{
MFEM_ABORT("SkewSymmetricVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
void SkewSymmetricVectorConvectionNLFIntegrator::AddMultGradPA(
const Vector &, Vector &) const
{
MFEM_ABORT("SkewSymmetricVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
void SkewSymmetricVectorConvectionNLFIntegrator::AssembleGradDiagonalPA(
Vector &) const
{
MFEM_ABORT("SkewSymmetricVectorConvectionNLFIntegrator does not support "
"partial assembly; use VectorConvectionNLFIntegrator");
}
}
+70 -8
View File
@@ -18,6 +18,7 @@
#include "fespace.hpp"
#include "ceed/interface/operator.hpp"
#include "integrator.hpp"
#include "kernel_dispatch.hpp"
namespace mfem
{
@@ -384,15 +385,17 @@ private:
DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
Vector shape;
// PA extension
Vector pa_data;
int dim, ne, nq, d1d, q1d;
Vector pa_adj, pa_u;
const DofToQuad *maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq;
public:
VectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }
struct Kernels { Kernels(); };
VectorConvectionNLFIntegrator() = default;
VectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { static Kernels kernels; }
VectorConvectionNLFIntegrator() { static Kernels kernels; }
static const IntegrationRule &GetRule(const FiniteElement &fe,
const ElementTransformation &T);
@@ -411,12 +414,55 @@ public:
void AssemblePA(const FiniteElementSpace &fes) override;
void AssembleMF(const FiniteElementSpace &fes) override;
void AssembleGradPA(const Vector &x, const FiniteElementSpace &fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AddMultMF(const Vector &x, Vector &y) const override;
using AddMultPAType =
void(*)(const int ne, const real_t *B, const real_t *G, const real_t *A,
const real_t *x, real_t *y,
const int d1d, const int q1d);
MFEM_REGISTER_KERNELS(AddMultPAKernels, AddMultPAType, (int, int, int));
void AddMultGradPA(const Vector &x, Vector &y) const override;
using AddMultGradPAType =
void(*)(const int ne, const real_t *B, const real_t *G, const real_t *A,
const real_t *u, const real_t *x, real_t *y,
const int d1d, const int q1d);
MFEM_REGISTER_KERNELS(AddMultGradPA2D, AddMultGradPAType, (int, int));
MFEM_REGISTER_KERNELS(AddMultGradPA3D, AddMultGradPAType, (int, int));
void AssembleGradDiagonalPA(Vector &) const override;
using GradDiagPAType =
void (*)(const int ne, const real_t *B, const real_t *G, const real_t *A,
const real_t *u, real_t *y,
const int d1d, const int q1d);
MFEM_REGISTER_KERNELS(GradDiagPA2D, GradDiagPAType, (int, int));
MFEM_REGISTER_KERNELS(GradDiagPA3D, GradDiagPAType, (int, int));
template <int DIM, int D1D, int Q1D>
static void AddSpecialization()
{
AddMultPAKernels::Specialization<DIM, D1D, Q1D>::Add();
if constexpr (DIM == 2)
{
AddMultGradPA2D::Specialization<D1D, Q1D>::Add();
GradDiagPA2D::Specialization<D1D, Q1D>::Add();
}
else if constexpr (DIM == 3)
{
AddMultGradPA3D::Specialization<D1D, Q1D>::Add();
GradDiagPA3D::Specialization<D1D, Q1D>::Add();
}
}
void AssembleMF(const FiniteElementSpace &fes) override;
void AddMultMF(const Vector &x, Vector &y) const override;
protected:
const IntegrationRule* GetDefaultIntegrationRule(
@@ -430,7 +476,8 @@ protected:
/** This class is used to assemble the convective form of the nonlinear term
arising in the Navier-Stokes equations $(u \cdot \nabla v, w )$ */
arising in the Navier-Stokes equations $(u \cdot \nabla v, w )$.
Partial assembly is not supported; use VectorConvectionNLFIntegrator. */
class ConvectiveVectorConvectionNLFIntegrator :
public VectorConvectionNLFIntegrator
{
@@ -448,12 +495,20 @@ public:
ElementTransformation &trans,
const Vector &elfun,
DenseMatrix &elmat) override;
using NonlinearFormIntegrator::AssemblePA;
void AssemblePA(const FiniteElementSpace &fes) override;
void AssembleGradPA(const Vector &x, const FiniteElementSpace &fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AddMultGradPA(const Vector &x, Vector &y) const override;
void AssembleGradDiagonalPA(Vector &diag) const override;
};
/** This class is used to assemble the skew-symmetric form of the nonlinear term
arising in the Navier-Stokes equations
$.5*(u \cdot \nabla v, w ) - .5*(u \cdot \nabla w, v )$ */
$.5*(u \cdot \nabla v, w ) - .5*(u \cdot \nabla w, v )$.
Partial assembly is not supported; use VectorConvectionNLFIntegrator. */
class SkewSymmetricVectorConvectionNLFIntegrator :
public VectorConvectionNLFIntegrator
{
@@ -471,6 +526,13 @@ public:
ElementTransformation &trans,
const Vector &elfun,
DenseMatrix &elmat) override;
using NonlinearFormIntegrator::AssemblePA;
void AssemblePA(const FiniteElementSpace &fes) override;
void AssembleGradPA(const Vector &x, const FiniteElementSpace &fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AddMultGradPA(const Vector &x, Vector &y) const override;
void AssembleGradDiagonalPA(Vector &diag) const override;
};
}
+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;
+338
View File
@@ -26,6 +26,8 @@
#include <limits>
#include <list>
#include <unordered_map>
#include <unordered_set>
namespace mfem
{
@@ -1285,6 +1287,342 @@ void ParFiniteElementSpace::GetExteriorVDofs(Array<int> &ext_dofs,
Synchronize(ext_dofs);
}
void ParFiniteElementSpace::GetBoundaryLoopEdgeDofs(
const Array<int> &boundary_element_indices,
Array<int> &ess_tdof_list,
Array<int> &boundary_edge_dofs_out,
Array<int> *ldof_marker,
Array<int> *dof_edges,
Array<int> *dof_boundary_elements,
Array<int> *ess_edge_list)
{
MFEM_VERIFY(!pmesh->Nonconforming(),
"GetBoundaryLoopEdgeDofs does not support nonconforming meshes");
MFEM_VERIFY(pmesh->Dimension() >= 2,
"GetBoundaryLoopEdgeDofs requires 2D or 3D meshes to find 1D edge objects");
// Call the serial version, then rebuild scratch maps/set from the returned
// arrays for the O(1) lookups the parallel reconciliation below needs.
Array<int> loc_dofs, loc_edges, loc_belems;
FiniteElementSpace::GetBoundaryLoopEdgeDofs(boundary_element_indices, loc_dofs,
&loc_edges, &loc_belems);
std::unordered_set<int> boundary_edge_dofs;
std::unordered_map<int, int> dof_to_edge_map;
std::unordered_map<int, int> dof_to_boundary_element;
boundary_edge_dofs.reserve(loc_dofs.Size());
dof_to_edge_map.reserve(loc_dofs.Size());
dof_to_boundary_element.reserve(loc_dofs.Size());
for (int i = 0; i < loc_dofs.Size(); i++)
{
const int dof = loc_dofs[i];
boundary_edge_dofs.insert(dof);
dof_to_edge_map[dof] = loc_edges[i];
dof_to_boundary_element[dof] = loc_belems[i];
}
// Parallel processing: Build edge sharing lookup table
std::unordered_map<int, int> edge_to_group_size;
int num_groups = pmesh->GetNGroups();
int total_shared_edges = 0;
for (int group = 1; group < num_groups; group++)
{
total_shared_edges += pmesh->GroupNEdges(group);
}
edge_to_group_size.reserve(total_shared_edges);
for (int group = 1; group < num_groups; group++)
{
int group_size = pmesh->gtopo.GetGroupSize(group);
int num_edges_in_group = pmesh->GroupNEdges(group);
for (int i = 0; i < num_edges_in_group; i++)
{
edge_to_group_size.emplace(pmesh->GroupEdge(group, i), group_size);
}
}
// Get global indices
Array<HYPRE_BigInt> global_edge_indices;
pmesh->GetGlobalEdgeIndices(global_edge_indices);
// Handle dimension-specific boundary element relationships
Array<HYPRE_BigInt> global_face_indices;
std::unordered_map<int, int> boundary_element_to_companion;
std::unordered_set<int> dofs_to_remove;
const int dim = pmesh->Dimension();
if (dim == 3)
{
// In 3D: boundary elements are faces, we track which face each boundary element is
pmesh->GetGlobalFaceIndices(global_face_indices);
for (int boundary_element_idx : boundary_element_indices)
{
int face_index, face_orientation;
pmesh->GetBdrElementFace(boundary_element_idx, &face_index, &face_orientation);
boundary_element_to_companion[boundary_element_idx] = face_index;
}
std::vector<HYPRE_BigInt> local_data;
local_data.reserve(boundary_edge_dofs.size() * 2);
std::unordered_set<int> processed_edges;
processed_edges.reserve(boundary_edge_dofs.size());
for (const auto& [dof, local_edge] : dof_to_edge_map)
{
// Skip if already processed this edge
if (!processed_edges.insert(local_edge).second) { continue; }
// Check if edge is shared (fast lookup)
auto it = edge_to_group_size.find(local_edge);
if (it != edge_to_group_size.end() && it->second > 1)
{
// Get boundary element and companion index directly from pre-computed map
int boundary_element_idx = dof_to_boundary_element[dof];
int companion_index = boundary_element_to_companion[boundary_element_idx];
// Store edge-face pair for 3D artificial boundary detection
local_data.push_back(global_edge_indices[local_edge]);
local_data.push_back(global_face_indices[companion_index]);
}
}
// MPI communication for 3D artificial boundary detection
int num_procs = pmesh->GetNRanks();
int local_size = local_data.size();
std::vector<int> mpi_arrays(num_procs * 4);
int* all_sizes = mpi_arrays.data();
int* displs = all_sizes + num_procs;
int* byte_sizes = displs + num_procs;
int* byte_displs = byte_sizes + num_procs;
MPI_Allgather(&local_size, 1, MPI_INT, all_sizes, 1, MPI_INT, pmesh->GetComm());
int total_size = 0;
constexpr int hypre_size = sizeof(HYPRE_BigInt);
for (int i = 0; i < num_procs; i++)
{
displs[i] = total_size;
byte_displs[i] = total_size * hypre_size;
total_size += all_sizes[i];
byte_sizes[i] = all_sizes[i] * hypre_size;
}
if (total_size > 0)
{
std::vector<HYPRE_BigInt> all_data(total_size);
MPI_Allgatherv(local_data.data(), local_size * hypre_size, MPI_BYTE,
all_data.data(), byte_sizes, byte_displs, MPI_BYTE, pmesh->GetComm());
// Build global-to-local edge mapping
std::unordered_map<HYPRE_BigInt, int> global_to_local_edge;
global_to_local_edge.reserve(global_edge_indices.Size());
for (int i = 0; i < global_edge_indices.Size(); ++i)
{
global_to_local_edge[global_edge_indices[i]] = i;
}
// Process collected data to find edges in multiple faces (artificial boundaries)
std::unordered_map<HYPRE_BigInt, std::unordered_set<HYPRE_BigInt>>edge_to_faces;
edge_to_faces.reserve(total_size / 2);
for (size_t i = 0; i < all_data.size(); i += 2)
{
edge_to_faces[all_data[i]].insert(all_data[i + 1]);
}
// Mark DOFs from artificial edges for removal
dofs_to_remove.reserve(local_data.size() / 4);
for (size_t i = 0; i < local_data.size(); i += 2)
{
HYPRE_BigInt global_edge_id = local_data[i];
// If this edge appears in 2+ distinct faces, it's artificial
if (edge_to_faces[global_edge_id].size() >= 2)
{
int local_edge = global_to_local_edge[global_edge_id];
Array<int> local_edge_dofs;
GetEdgeDofs(local_edge, local_edge_dofs);
// Mark boundary DOFs of this edge for removal
for (int k = 0; k < local_edge_dofs.Size(); ++k)
{
int dof = local_edge_dofs[k];
if (boundary_edge_dofs.count(dof))
{
dofs_to_remove.insert(dof);
}
}
}
}
}
}
else if (dim == 2)
{
// In 2D the boundary elements are themselves the edges, so there are no
// artificial boundary edges to detect. However, for collections with
// vertex DOFs (e.g. ND_R2D), a vertex shared by two boundary segments is
// interior to the boundary curve and must be dropped. The serial code
// does this by erasing a DOF on its second occurrence, which only sees
// the occurrences local to this rank. When the two segments meeting at a
// vertex live on different ranks, each rank sees a single occurrence and
// wrongly keeps the DOF. Reconcile the occurrence parity across each
// sharing group: membership in boundary_edge_dofs is the local parity,
// and the parities sum (mod 2) to the global occurrence parity.
Array<int> boundary_dof_count(GetVSize());
boundary_dof_count = 0;
for (const int dof : boundary_edge_dofs)
{
boundary_dof_count[dof] = 1;
}
// implement allreduce(+) as reduce(+) + broadcast
gcomm->Reduce<int>(boundary_dof_count, GroupCommunicator::Sum);
gcomm->Bcast(boundary_dof_count);
for (const int dof : boundary_edge_dofs)
{
if (boundary_dof_count[dof] % 2 == 0)
{
dofs_to_remove.insert(dof);
}
}
}
// Remove artificial DOFs
for (int dof : dofs_to_remove)
{
boundary_edge_dofs.erase(dof);
dof_to_edge_map.erase(dof);
dof_to_boundary_element.erase(dof);
}
// Convert to true DOFs and output
ess_tdof_list.SetSize(0);
ess_tdof_list.Reserve(boundary_edge_dofs.size());
if (ess_edge_list)
{
// Reset as well, so that it stays in correspondence with ess_tdof_list
// when the same output array is reused across calls.
ess_edge_list->SetSize(0);
ess_edge_list->Reserve(boundary_edge_dofs.size());
}
// Marker of the boundary edge DOFs. Always computed locally because the
// parallel reconciliation below needs it; only copied to the caller's output
// if requested (see the ldof_marker parameter).
Array<int> local_ldof_marker(GetVSize());
local_ldof_marker = 0;
for (int dof : boundary_edge_dofs)
{
local_ldof_marker[dof] = 1; // Mark all boundary edge dofs
}
// Make sure that a selected shared DOF is marked on every rank of its
// sharing group, including ranks holding none of the selected boundary
// elements. Only the group master owns the corresponding true DOF, so
// without this the true DOF would be emitted by no rank at all: the
// non-master ranks get -1 from GetLocalTDofNumber(), while the master may
// not have selected the DOF locally.
Synchronize(local_ldof_marker);
// A DOF marked only through the synchronization above has no local
// dof_to_edge_map entry, but the shared edge carrying it is still present in
// the local mesh. Build the missing DOF -> edge entries from the shared
// edges of the groups, so that ess_edge_list stays in correspondence with
// ess_tdof_list. Note that a vertex DOF is not associated with a unique
// edge, so it is only resolved when it is an interior DOF of an edge.
std::unordered_map<int, int> shared_dof_to_edge;
Array<int> shared_edge_dofs;
for (int group = 1; group < num_groups; group++)
{
const int num_edges_in_group = pmesh->GroupNEdges(group);
for (int i = 0; i < num_edges_in_group; i++)
{
const int edge = pmesh->GroupEdge(group, i);
GetEdgeInteriorDofs(edge, shared_edge_dofs);
for (int k = 0; k < shared_edge_dofs.Size(); k++)
{
shared_dof_to_edge.emplace(shared_edge_dofs[k], edge);
}
}
}
// Build parallel arrays for DOFs and corresponding edges
std::vector<std::pair<int, int>> tdof_edge_pairs;
tdof_edge_pairs.reserve(boundary_edge_dofs.size());
for (int dof = 0; dof < local_ldof_marker.Size(); dof++)
{
if (!local_ldof_marker[dof]) { continue; }
const int tdof = GetLocalTDofNumber(dof);
if (tdof < 0) { continue; } // tdof == -1 means not owned by this rank
int edge = -1;
auto it = dof_to_edge_map.find(dof);
if (it != dof_to_edge_map.end())
{
edge = it->second;
}
else
{
auto shared_it = shared_dof_to_edge.find(dof);
if (shared_it != shared_dof_to_edge.end())
{
edge = shared_it->second;
}
}
tdof_edge_pairs.push_back({tdof, edge});
}
// Sort by true DOF index to maintain consistent ordering
std::sort(tdof_edge_pairs.begin(), tdof_edge_pairs.end());
// Extract sorted true DOFs and edges
for (const auto& pair : tdof_edge_pairs)
{
ess_tdof_list.Append(pair.first);
if (ess_edge_list)
{
ess_edge_list->Append(pair.second);
}
}
// Emit the local boundary-loop DOFs in a deterministic (increasing DOF
// index) order shared by all output arrays.
std::vector<int> kept(boundary_edge_dofs.begin(), boundary_edge_dofs.end());
std::sort(kept.begin(), kept.end());
boundary_edge_dofs_out.SetSize(0);
boundary_edge_dofs_out.Reserve(static_cast<int>(kept.size()));
if (dof_edges)
{
dof_edges->SetSize(0);
dof_edges->Reserve(static_cast<int>(kept.size()));
}
if (dof_boundary_elements)
{
dof_boundary_elements->SetSize(0);
dof_boundary_elements->Reserve(static_cast<int>(kept.size()));
}
for (int dof : kept)
{
boundary_edge_dofs_out.Append(dof);
if (dof_edges) { dof_edges->Append(dof_to_edge_map[dof]); }
if (dof_boundary_elements)
{
dof_boundary_elements->Append(dof_to_boundary_element[dof]);
}
}
if (ldof_marker) { ldof_marker->Swap(local_ldof_marker); }
}
void ParFiniteElementSpace::GetExteriorTrueDofs(Array<int> &ext_tdof_list,
int component) const
{
+35
View File
@@ -460,6 +460,41 @@ public:
void GetExteriorTrueDofs(Array<int> &ext_tdof_list,
int component = -1) const override;
/** @brief Extract the edge degrees of freedom of a boundary "loop" on a
parallel mesh (see the serial FiniteElementSpace::GetBoundaryLoopEdgeDofs
for the definition of a loop). This version removes the artificial
boundary edges that appear at processor boundaries, so the selected DOFs
are independent of the mesh partitioning.
As in the serial version, the @a boundary_edge_dofs_out, @a dof_edges and
@a dof_boundary_elements outputs share a single indexing describing the
same local DOF at each position.
Requirements:
- Mesh must be conforming (no hanging nodes)
- Mesh dimension must be >= 2
@param[in] boundary_element_indices Array of boundary element indices.
@param[out] ess_tdof_list Essential true DOF indices, sorted ascending.
@param[out] boundary_edge_dofs_out Local boundary-loop DOF indices.
@param[out] ldof_marker Optional; marker of the boundary edge DOFs,
derivable from @a boundary_edge_dofs_out via ListToMarker().
@param[out] dof_edges Optional; local edge index of each DOF.
@param[out] dof_boundary_elements Optional; a boundary element containing
each DOF.
@param[out] ess_edge_list Optional array of edge indices, in one-to-one
correspondence with @a ess_tdof_list. An entry
is -1 when the true DOF is owned by this rank
but no local edge can be associated with it,
which can happen for a shared vertex DOF whose
boundary elements are all on other ranks. */
void GetBoundaryLoopEdgeDofs(const Array<int> &boundary_element_indices,
Array<int> &ess_tdof_list,
Array<int> &boundary_edge_dofs_out,
Array<int> *ldof_marker = nullptr,
Array<int> *dof_edges = nullptr,
Array<int> *dof_boundary_elements = nullptr,
Array<int> *ess_edge_list = nullptr);
/** If the given ldof is owned by the current processor, return its local
tdof number, otherwise return -1 */
int GetLocalTDofNumber(int ldof) const;
+11 -1
View File
@@ -22,10 +22,20 @@ using namespace std;
namespace mfem
{
ParGridFunction::ParGridFunction(ParFiniteElementSpace *pf, GridFunction *gf)
ParGridFunction::ParGridFunction(ParFiniteElementSpace *pf, GridFunction *gf,
bool preserve)
{
fes = pfes = pf;
SetDataAndSize(gf->GetData(), gf->Size());
if (pfes->HaveDofSigns())
{
MFEM_VERIFY(!preserve, "Differing sign conventions for the serial and "
"parallel grid functions will prevent preserving the serial "
"GridFunctions in this context.");
pfes->ApplyDofSigns(HostReadWrite());
}
}
ParGridFunction::ParGridFunction(ParFiniteElementSpace *pf, HypreParVector *tv)
+6 -2
View File
@@ -100,8 +100,12 @@ public:
/// Construct a ParGridFunction using a GridFunction as external data.
/** The parallel space @a *pf and the space used by @a *gf should match. The
data from @a *gf is used as the local data of the ParGridFunction on each
processor. The ParGridFunction does not assume ownership of the data. */
ParGridFunction(ParFiniteElementSpace *pf, GridFunction *gf);
processor. The ParGridFunction does not assume ownership of the data.
The boolean, @a preserve, indicates that the data stored in @a *gf should
remain unchanged. An error will occur if @a preserve is true and
construction of a valid ParGridFunction requires the data to change. */
ParGridFunction(ParFiniteElementSpace *pf, GridFunction *gf,
bool preserve = true);
/** @brief Creates grid function on (all) dofs from a given vector on the
true dofs, i.e. P tv. */
+9 -36
View File
@@ -51,48 +51,21 @@ QuadratureInterpolator::DetKernels::Fallback(int DIM, int SDIM, int D1D,
{
if (DIM == 1)
{
if (SDIM == 1)
{
return internal::quadrature_interpolator::Det1D;
}
else if (SDIM == 2)
{
return internal::quadrature_interpolator::Det1DSurface<0, 0, 2>;
}
else if (SDIM == 3)
{
return internal::quadrature_interpolator::Det1DSurface<0, 0, 3>;
}
else
{
MFEM_ABORT("");
}
}
else if (DIM == 2 && SDIM == 2)
{
return internal::quadrature_interpolator::Det2D<>;
}
else if (DIM == 2 && SDIM == 3)
{
return internal::quadrature_interpolator::Det2DSurface<>;
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<0,0,2>; }
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<0,0,3>; }
else { MFEM_ABORT(""); }
}
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D; }
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface; }
else if (DIM == 3)
{
const int MD = DeviceDofQuadLimits::Get().MAX_DET_1D;
const int MQ = DeviceDofQuadLimits::Get().MAX_DET_1D;
if (D1D <= MD && Q1D <= MQ)
{
return internal::quadrature_interpolator::Det3D<0, 0, true>;
}
else
{
return internal::quadrature_interpolator::Det3D<0, 0, false>;
}
}
else
{
MFEM_ABORT("");
if (D1D <= MD && Q1D <= MQ) { return internal::quadrature_interpolator::Det3D<0,0,true>; }
else { return internal::quadrature_interpolator::Det3D<0,0,false>; }
}
else { MFEM_ABORT(""); }
}
/// @endcond
+52 -38
View File
@@ -31,9 +31,9 @@ namespace quadrature_interpolator
{
template <QVectorLayout Q_LAYOUT, bool Integral>
static void IntValues1D(const int NE, const real_t *b_, const real_t *detJ_,
const real_t *x_, real_t *y_, const int vdim,
const int d1d, const int q1d)
static void ImplValues1D(const int NE, const real_t *b_, const real_t *detJ_,
const real_t *x_, real_t *y_, const int vdim,
const int d1d, const int q1d)
{
mfem::forall(NE, [=] MFEM_HOST_DEVICE(int e)
{
@@ -72,15 +72,15 @@ template <QVectorLayout Q_LAYOUT>
static void Values1D(const int NE, const real_t *b_, const real_t *x_,
real_t *y_, const int vdim, const int d1d, const int q1d)
{
IntValues1D<Q_LAYOUT, false>(NE, b_, nullptr, x_, y_, vdim, d1d, q1d);
ImplValues1D<Q_LAYOUT, false>(NE, b_, nullptr, x_, y_, vdim, d1d, q1d);
}
// Template compute kernel for Values in 2D: tensor product version.
template <QVectorLayout Q_LAYOUT, bool Integral, int T_VDIM = 0, int T_D1D = 0,
int T_Q1D = 0, int T_NBZ = 1>
static void IntValues2D(const int NE, const real_t *b_, const real_t *detJ_,
const real_t *x_, real_t *y_, const int vdim = 0,
const int d1d = 0, const int q1d = 0)
static void ImplValues2D(const int NE, const real_t *b_, const real_t *detJ_,
const real_t *x_, real_t *y_, const int vdim = 0,
const int d1d = 0, const int q1d = 0)
{
static constexpr int NBZ = T_NBZ ? T_NBZ : 1;
@@ -159,16 +159,16 @@ static void Values2D(const int NE, const real_t *b_, const real_t *x_,
real_t *y_, const int vdim = 0, const int d1d = 0,
const int q1d = 0)
{
return IntValues2D<Q_LAYOUT, false, T_VDIM, T_D1D, T_Q1D, T_NBZ>(
return ImplValues2D<Q_LAYOUT, false, T_VDIM, T_D1D, T_Q1D, T_NBZ>(
NE, b_, nullptr, x_, y_, vdim, d1d, q1d);
}
// Template compute kernel for Values in 3D: tensor product version.
template <QVectorLayout Q_LAYOUT, bool Integral, int T_VDIM = 0, int T_D1D = 0,
int T_Q1D = 0>
static void IntValues3D(const int NE, const real_t *b_, const real_t *detJ_,
const real_t *x_, real_t *y_, const int vdim = 0,
const int d1d = 0, const int q1d = 0)
static void ImplValues3D(const int NE, const real_t *b_, const real_t *detJ_,
const real_t *x_, real_t *y_, const int vdim = 0,
const int d1d = 0, const int q1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
@@ -251,23 +251,23 @@ static void Values3D(const int NE, const real_t *b_, const real_t *x_,
real_t *y_, const int vdim = 0, const int d1d = 0,
const int q1d = 0)
{
return IntValues3D<Q_LAYOUT, false, T_VDIM, T_D1D, T_Q1D>(
return ImplValues3D<Q_LAYOUT, false, T_VDIM, T_D1D, T_Q1D>(
NE, b_, nullptr, x_, y_, vdim, d1d, q1d);
}
template <bool Integral>
void IntEval1D(const int NE, const int vdim, const QVectorLayout q_layout,
const real_t *detJ, const GeometricFactors *geom,
const DofToQuad &maps, const Vector &e_vec, Vector &q_val,
Vector &q_der, Vector &q_det, const int eval_flags);
void ImplEval1D(const int NE, const int vdim, const QVectorLayout q_layout,
const real_t *detJ, const GeometricFactors *geom,
const DofToQuad &maps, const Vector &e_vec, Vector &q_val,
Vector &q_der, Vector &q_det, const int eval_flags);
inline void Eval1D(const int NE, const int vdim, const QVectorLayout q_layout,
const GeometricFactors *geom, const DofToQuad &maps,
const Vector &e_vec, Vector &q_val, Vector &q_der, Vector &q_det,
const int eval_flags)
{
IntEval1D<false>(NE, vdim, q_layout, nullptr, geom, maps, e_vec, q_val,
q_der, q_det, eval_flags);
ImplEval1D<false>(NE, vdim, q_layout, nullptr, geom, maps, e_vec, q_val,
q_der, q_det, eval_flags);
}
// Template compute kernel for 2D quadrature interpolation:
@@ -275,11 +275,11 @@ inline void Eval1D(const int NE, const int vdim, const QVectorLayout q_layout,
// * assumes 'e_vec' is using ElementDofOrdering::NATIVE,
// * assumes 'maps.mode == FULL'.
template <bool Integral, const int T_VDIM, const int T_ND, const int T_NQ>
static void
IntEval2D(const int NE, const int vdim, const QVectorLayout q_layout,
const real_t *detJ_, const GeometricFactors *geom,
const DofToQuad &maps, const Vector &e_vec, Vector &q_val,
Vector &q_der, Vector &q_det, const int eval_flags)
static void ImplEval2D(const int NE, const int vdim,
const QVectorLayout q_layout, const real_t *detJ_,
const GeometricFactors *geom, const DofToQuad &maps,
const Vector &e_vec, Vector &q_val, Vector &q_der,
Vector &q_det, const int eval_flags)
{
using QI = QuadratureInterpolator;
@@ -293,6 +293,12 @@ IntEval2D(const int NE, const int vdim, const QVectorLayout q_layout,
MFEM_ASSERT(!geom || geom->mesh->SpaceDimension() == 2, "");
MFEM_VERIFY(ND <= QI::MAX_ND2D, "");
MFEM_VERIFY(NQ <= QI::MAX_NQ2D, "");
if constexpr(Integral)
{
MFEM_VERIFY(!(eval_flags & (QI::DERIVATIVES | QI::PHYSICAL_DERIVATIVES |
QI::DETERMINANTS)),
"Integral FE does not support computing derivatives");
}
const auto B = Reshape(maps.B.Read(), NQ, ND);
const auto G = Reshape(maps.G.Read(), NQ, 2, ND);
const auto J = Reshape(geom ? geom->J.Read() : nullptr, NQ, 2, 2, NE);
@@ -449,8 +455,9 @@ static void Eval2D(const int NE, const int vdim, const QVectorLayout q_layout,
const Vector &e_vec, Vector &q_val, Vector &q_der,
Vector &q_det, const int eval_flags)
{
IntEval2D<false, T_VDIM, T_ND, T_NQ>(NE, vdim, q_layout, nullptr, geom, maps,
e_vec, q_val, q_der, q_det, eval_flags);
ImplEval2D<false, T_VDIM, T_ND, T_NQ>(NE, vdim, q_layout, nullptr, geom,
maps, e_vec, q_val, q_der, q_det,
eval_flags);
}
// Template compute kernel for 3D quadrature interpolation:
@@ -458,11 +465,11 @@ static void Eval2D(const int NE, const int vdim, const QVectorLayout q_layout,
// * assumes 'e_vec' is using ElementDofOrdering::NATIVE,
// * assumes 'maps.mode == FULL'.
template <bool Integral, const int T_VDIM, const int T_ND, const int T_NQ>
static void
IntEval3D(const int NE, const int vdim, const QVectorLayout q_layout,
const real_t *detJ_, const GeometricFactors *geom,
const DofToQuad &maps, const Vector &e_vec, Vector &q_val,
Vector &q_der, Vector &q_det, const int eval_flags)
static void ImplEval3D(const int NE, const int vdim,
const QVectorLayout q_layout, const real_t *detJ_,
const GeometricFactors *geom, const DofToQuad &maps,
const Vector &e_vec, Vector &q_val, Vector &q_der,
Vector &q_det, const int eval_flags)
{
using QI = QuadratureInterpolator;
@@ -477,6 +484,12 @@ IntEval3D(const int NE, const int vdim, const QVectorLayout q_layout,
MFEM_VERIFY(ND <= QI::MAX_ND3D, "");
MFEM_VERIFY(NQ <= QI::MAX_NQ3D, "");
MFEM_VERIFY(VDIM == 3 || !(eval_flags & QI::DETERMINANTS), "");
if constexpr(Integral)
{
MFEM_VERIFY(!(eval_flags & (QI::DERIVATIVES | QI::PHYSICAL_DERIVATIVES |
QI::DETERMINANTS)),
"Integral FE does not support computing derivatives");
}
const auto B = Reshape(maps.B.Read(), NQ, ND);
const auto G = Reshape(maps.G.Read(), NQ, 3, ND);
const auto J = Reshape(geom ? geom->J.Read() : nullptr, NQ, 3, 3, NE);
@@ -635,8 +648,9 @@ static void Eval3D(const int NE, const int vdim, const QVectorLayout q_layout,
const Vector &e_vec, Vector &q_val, Vector &q_der,
Vector &q_det, const int eval_flags)
{
IntEval3D<false, T_VDIM, T_ND, T_NQ>(NE, vdim, q_layout, nullptr, geom, maps,
e_vec, q_val, q_der, q_det, eval_flags);
ImplEval3D<false, T_VDIM, T_ND, T_NQ>(NE, vdim, q_layout, nullptr, geom,
maps, e_vec, q_val, q_der, q_det,
eval_flags);
}
} // namespace quadrature_interpolator
@@ -649,9 +663,9 @@ template <int DIM, QVectorLayout Q_LAYOUT, int VDIM, int D1D, int Q1D, int NBZ>
QuadratureInterpolator::IntTensorEvalKernelType
QuadratureInterpolator::IntTensorEvalKernels::Kernel()
{
if constexpr (DIM == 1) { return internal::quadrature_interpolator::IntValues1D<Q_LAYOUT, true>; }
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::IntValues2D<Q_LAYOUT, true, VDIM, D1D, Q1D, NBZ>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::IntValues3D<Q_LAYOUT, true, VDIM, D1D, Q1D>; }
if constexpr (DIM == 1) { return internal::quadrature_interpolator::ImplValues1D<Q_LAYOUT, true>; }
else if constexpr (DIM == 2) { return internal::quadrature_interpolator::ImplValues2D<Q_LAYOUT, true, VDIM, D1D, Q1D, NBZ>; }
else if constexpr (DIM == 3) { return internal::quadrature_interpolator::ImplValues3D<Q_LAYOUT, true, VDIM, D1D, Q1D>; }
MFEM_ABORT("");
}
@@ -670,9 +684,9 @@ QuadratureInterpolator::IntEvalKernelType
QuadratureInterpolator::IntEvalKernels::Kernel()
{
using namespace internal::quadrature_interpolator;
if constexpr (DIM == 1) { return IntEval1D<true>; }
else if constexpr (DIM == 2) { return IntEval2D<true,VDIM,ND,NQ>; }
else if constexpr (DIM == 3) { return IntEval3D<true,VDIM,ND,NQ>; }
if constexpr (DIM == 1) { return ImplEval1D<true>; }
else if constexpr (DIM == 2) { return ImplEval2D<true,VDIM,ND,NQ>; }
else if constexpr (DIM == 3) { return ImplEval3D<true,VDIM,ND,NQ>; }
MFEM_ABORT("");
}
+3 -2
View File
@@ -268,8 +268,9 @@ static void Derivatives3D(const int NE,
DeviceMatrix B(BG[0], D1D, Q1D);
DeviceMatrix G(BG[1], D1D, Q1D);
MFEM_SHARED real_t sm0[3][MQ1*MQ1*MQ1];
MFEM_SHARED real_t sm1[3][MQ1*MQ1*MQ1];
constexpr int MDQ = MD1 > MQ1 ? MD1 : MQ1;
MFEM_SHARED real_t sm0[3][MD1*MD1*MDQ];
MFEM_SHARED real_t sm1[3][MD1*MQ1*MQ1];
DeviceTensor<3> X(sm0[2], D1D, D1D, D1D);
DeviceTensor<3> DDQ0(sm0[0], D1D, D1D, Q1D);
DeviceTensor<3> DDQ1(sm0[1], D1D, D1D, Q1D);
+20 -14
View File
@@ -111,10 +111,10 @@ namespace quadrature_interpolator
// * assumes 'e_vec' is using ElementDofOrdering::NATIVE,
// * assumes 'maps.mode == FULL'.
template <bool Integral>
void IntEval1D(const int NE, const int vdim, const QVectorLayout q_layout,
const real_t *detJ_, const GeometricFactors *geom,
const DofToQuad &maps, const Vector &e_vec, Vector &q_val,
Vector &q_der, Vector &q_det, const int eval_flags)
void ImplEval1D(const int NE, const int vdim, const QVectorLayout q_layout,
const real_t *detJ_, const GeometricFactors *geom,
const DofToQuad &maps, const Vector &e_vec, Vector &q_val,
Vector &q_der, Vector &q_det, const int eval_flags)
{
using QI = QuadratureInterpolator;
@@ -123,6 +123,12 @@ void IntEval1D(const int NE, const int vdim, const QVectorLayout q_layout,
MFEM_ASSERT(maps.mode == DofToQuad::FULL, "internal error");
MFEM_ASSERT(!geom || geom->mesh->SpaceDimension() == 1, "");
MFEM_VERIFY(vdim == 1 || !(eval_flags & QI::DETERMINANTS), "");
if constexpr(Integral)
{
MFEM_VERIFY(!(eval_flags & (QI::DERIVATIVES | QI::PHYSICAL_DERIVATIVES |
QI::DETERMINANTS)),
"Integral FE does not support computing derivatives");
}
const auto B_ = maps.B.Read();
const auto G_ = maps.G.Read();
const auto J = Reshape(geom ? geom->J.Read() : nullptr, nq, NE);
@@ -202,17 +208,17 @@ void IntEval1D(const int NE, const int vdim, const QVectorLayout q_layout,
}
template void
IntEval1D<true>(const int NE, const int vdim, const QVectorLayout q_layout,
const real_t *detJ, const GeometricFactors *geom,
const DofToQuad &maps, const Vector &e_vec, Vector &q_val,
Vector &q_der, Vector &q_det, const int eval_flags);
template void
IntEval1D<false>(const int NE, const int vdim, const QVectorLayout q_layout,
ImplEval1D<true>(const int NE, const int vdim, const QVectorLayout q_layout,
const real_t *detJ, const GeometricFactors *geom,
const DofToQuad &maps, const Vector &e_vec, Vector &q_val,
Vector &q_der, Vector &q_det, const int eval_flags);
template void
ImplEval1D<false>(const int NE, const int vdim, const QVectorLayout q_layout,
const real_t *detJ, const GeometricFactors *geom,
const DofToQuad &maps, const Vector &e_vec, Vector &q_val,
Vector &q_der, Vector &q_det, const int eval_flags);
} // namespace quadrature_interpolator
} // namespace internal
@@ -460,15 +466,15 @@ template <QVectorLayout Q_LAYOUT> auto IntFallbackTensorEvalKernel(int DIM)
{
if (DIM == 1)
{
return IntValues1D<Q_LAYOUT, true>;
return ImplValues1D<Q_LAYOUT, true>;
}
else if (DIM == 2)
{
return IntValues2D<Q_LAYOUT, true>;
return ImplValues2D<Q_LAYOUT, true>;
}
else if (DIM == 3)
{
return IntValues3D<Q_LAYOUT, true>;
return ImplValues3D<Q_LAYOUT, true>;
}
MFEM_ABORT("");
}
+4
View File
@@ -117,6 +117,10 @@ public:
FiniteElementSpace is a vector space) and their determinants are computed
and stored in @a q_det.
For Integral spaces, the flags VALUES requests the computation of the
scalar field values. The result is stored in @a q_val. Derivative types
are not supported.
For H(div)-conforming spaces, the flags VALUES / PHYSICAL_VALUES request
the computation of the vector field values in reference or physical
space, respectively. The flag PHYSICAL_MAGNITUDES requests the
+305 -174
View File
@@ -231,9 +231,11 @@ const Operator &InterpolationGridTransfer::BackwardOperator()
L2ProjectionGridTransfer::L2Projection::L2Projection(
const FiniteElementSpace &fes_ho_, const FiniteElementSpace &fes_lor_,
CoefficientWithOrder coeff_ho_, CoefficientWithOrder coeff_lor_,
MemoryType d_mt_)
: Operator(fes_lor_.GetVSize(), fes_ho_.GetVSize()),
fes_ho(fes_ho_), fes_lor(fes_lor_), d_mt(d_mt_)
fes_ho(fes_ho_), fes_lor(fes_lor_), coeff_ho(coeff_ho_),
coeff_lor(coeff_lor_), d_mt(d_mt_)
{ }
void L2ProjectionGridTransfer::L2Projection::BuildHo2Lor(
@@ -263,12 +265,13 @@ void L2ProjectionGridTransfer::L2Projection::ElemMixedMass(
IntegrationPointTransformation& ip_tr,
DenseMatrix& M_mixed_el) const
{
int order = fe_lor.GetOrder() + fe_ho.GetOrder() + tr_lor->OrderW();
const IntegrationRule* ir = &IntRules.Get(geom, order);
int order = fe_lor.GetOrder() + fe_ho.GetOrder() + tr_lor->OrderW() +
coeff_ho.order;
const IntegrationRule &ir = IntRules.Get(geom, order);
M_mixed_el = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint& ip_lor = ir->IntPoint(i);
const IntegrationPoint& ip_lor = ir.IntPoint(i);
IntegrationPoint ip_ho;
ip_tr.Transform(ip_lor, ip_ho);
Vector shape_lor(fe_lor.GetDof());
@@ -284,23 +287,23 @@ void L2ProjectionGridTransfer::L2Projection::ElemMixedMass(
{
w *= tr_lor->Weight();
}
if (coeff_ho)
{
w *= coeff_ho.coeff->Eval(*tr_ho, ip_ho);
}
shape_lor *= w;
AddMultVWt(shape_lor, shape_ho, M_mixed_el);
}
}
void L2ProjectionGridTransfer::L2Projection::ElemMixedMass(
Geometry::Type geom, const FiniteElement& fe_ho,
const FiniteElement& fe_lor, ElementTransformation* el_tr,
IntegrationPointTransformation& ip_tr,
void L2ProjectionGridTransfer::L2Projection::ElemMixedEvaluation(
Geometry::Type geom, const FiniteElement& fe_ho, const FiniteElement& fe_lor,
IntegrationPointTransformation& ip_tr, const IntegrationRule& ir,
DenseMatrix& B_L, DenseMatrix& B_H) const
{
int order = fe_lor.GetOrder() + fe_ho.GetOrder() + el_tr->OrderW();
const IntegrationRule* ir = &IntRules.Get(geom, order);
for (int i = 0; i < ir->GetNPoints(); i++)
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint& ip_lor = ir->IntPoint(i);
const IntegrationPoint& ip_lor = ir.IntPoint(i);
IntegrationPoint ip_ho;
// maps integration point ip_lor -> ip_ho
@@ -320,7 +323,6 @@ void L2ProjectionGridTransfer::L2Projection::ElemMixedMass(
B_H(i, j) = shape_ho(j);
}
}
}
void L2ProjectionGridTransfer::L2Projection::MixedMassEA(
@@ -328,10 +330,11 @@ void L2ProjectionGridTransfer::L2Projection::MixedMassEA(
const FiniteElementSpace& fes_lor_ea,
Vector &M_LH, MemoryType d_mt_)
{
Mesh* mesh_ho = fes_ho_ea.GetMesh();
Mesh* mesh_lor = fes_lor_ea.GetMesh();
int nel_ho = mesh_ho->GetNE();
int nel_lor = mesh_lor->GetNE();
Mesh &mesh_ho = *fes_ho_ea.GetMesh();
Mesh &mesh_lor = *fes_lor_ea.GetMesh();
const int nel_ho = mesh_ho.GetNE();
const int nel_lor = mesh_lor.GetNE();
if (nel_ho == 0)
{
@@ -339,11 +342,11 @@ void L2ProjectionGridTransfer::L2Projection::MixedMassEA(
return;
}
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
const CoarseFineTransformations& cf_tr = mesh_lor.GetRefinementTransforms();
int nref_max = 0;
Array<Geometry::Type> geoms;
mesh_ho->GetGeometries(mesh_ho->Dimension(), geoms);
mesh_ho.GetGeometries(mesh_ho.Dimension(), geoms);
for (int ig = 0; ig < geoms.Size(); ++ig)
{
Geometry::Type geom = geoms[ig];
@@ -360,130 +363,226 @@ void L2ProjectionGridTransfer::L2Projection::MixedMassEA(
{
// Assume all HO elements are LOR in the same way
const int iho = 0;
{
Array<int> lor_els;
ho2lor.GetRow(iho, lor_els);
int nref = ho2lor.RowSize(iho);
Geometry::Type geom = mesh_ho->GetElementBaseGeometry(iho);
const FiniteElement &fe_ho = *fes_ho_ea.GetFE(iho);
const FiniteElement &fe_lor = *fes_lor_ea.GetFE(lor_els[0]);
// Allocate space for DenseTensors
ElementTransformation *el_tr = fes_lor_ea.GetElementTransformation(0);
int order = fe_lor.GetOrder() + fe_ho.GetOrder() + el_tr->OrderW();
const IntegrationRule* ir_ea = &IntRules.Get(geom, order);
int qPts = ir_ea->GetNPoints();
// Containers for the basis functions sampled at quadrature points
B_L.SetSize(qPts, fe_lor.GetDof(), nref, d_mt);
B_H.SetSize(qPts, fe_ho.GetDof(), nref, d_mt);
D.SetSize(qPts, nref, nel_ho, d_mt);
const GeometricFactors *geo_facts =
mesh_lor->GetGeometricFactors(*ir_ea, GeometricFactors::DETERMINANTS);
MFEM_ASSERT(nel_ho*nref == nel_lor, "we expect nel_ho*nref == nel_lor");
// Setup data at quadrature points
// TODO add support for user coefficient
const auto W = Reshape(ir_ea->GetWeights().Read(), qPts);
const auto J = Reshape(geo_facts->detJ.Read(), qPts, nel_lor);
const auto d_D = Reshape(D.Write(), qPts, nref, nel_ho);
mfem::forall(qPts * nref * nel_ho, [=] MFEM_HOST_DEVICE (int tid)
{
const int q = tid % qPts;
const int iref = (tid / qPts) % nref;
const int iho = (tid / (qPts * nref)) % nel_ho;
const int lo_el_id = iref + nref*iho;
const real_t detJ = J(q, lo_el_id);
d_D(q, iref, iho) = W(q) * detJ;
});
emb_tr.SetIdentityTransformation(geom);
const DenseTensor &pmats = cf_tr.point_matrices[geom];
// Collect the basis functions
for (int iref = 0; iref < nref; ++iref)
{
int ilor = lor_els[iref];
// Now assemble the block-row of the mixed mass matrix associated
// with integrating HO functions against LOR functions on the LOR
// sub-element.
// Create the transformation that embeds the fine low-order element
// within the coarse high-order element in reference space
emb_tr.SetPointMat(pmats(cf_tr.embeddings[ilor].matrix));
DenseMatrix &b_lo = B_L(ilor);
DenseMatrix &b_ho = B_H(ilor);
ElemMixedMass(geom, fe_ho, fe_lor, el_tr, ip_tr, b_lo, b_ho);
} // loop over subcells of ho element
// end of quadrature point setup
}
} // completed setup of basis function and quadrature point
// Assemble mixed mass matrix
{
int iho = 0;
Array<int> lor_els;
ho2lor.GetRow(iho, lor_els);
int nref = ho2lor.RowSize(iho);
const int nref = ho2lor.RowSize(iho);
MFEM_VERIFY(nel_ho*nref == nel_lor, "we expect nel_ho*nref == nel_lor");
Geometry::Type geom = mesh_ho.GetElementBaseGeometry(iho);
emb_tr.SetIdentityTransformation(geom);
const DenseTensor &pmats = cf_tr.point_matrices[geom];
const FiniteElement &fe_ho = *fes_ho_ea.GetFE(iho);
const FiniteElement &fe_lor = *fes_lor_ea.GetFE(lor_els[0]);
const int ndof_ho = fe_ho.GetDof();
const int ndof_lor = fe_lor.GetDof();
const int qPts = D.SizeI();
// Allocate space for DenseTensors
ElementTransformation &el_tr = *mesh_lor.GetTypicalElementTransformation();
const int order = fe_lor.GetOrder() + fe_ho.GetOrder() + el_tr.OrderW()
+ coeff_ho.order;
const IntegrationRule &ir_ea = IntRules.Get(geom, order);
const int qPts = ir_ea.GetNPoints();
M_LH.SetSize(ndof_lor*ndof_ho*nref*nel_ho, d_mt);
// Containers for the basis functions sampled at quadrature points
B_L.SetSize(qPts, fe_lor.GetDof(), nref, d_mt);
B_H.SetSize(qPts, fe_ho.GetDof(), nref, d_mt);
D.SetSize(qPts, nref, nel_ho, d_mt);
// Rows x columns
// Recall MFEM is column major
// rows x columns is inverted - matrix is ndof_lor x ndof_ho
auto v_M_LH = Reshape(M_LH.Write(), ndof_lor, ndof_ho, nref,
nel_ho);
const GeometricFactors *geo_facts =
mesh_lor.GetGeometricFactors(ir_ea, GeometricFactors::DETERMINANTS);
const int fe_ho_ndof = fe_ho.GetDof();
const int fe_lor_ndof = fe_lor.GetDof();
Vector coeff_vec(qPts*nel_lor);
coeff_vec.UseDevice(true);
auto d_B_L = Reshape(B_L.Read(), qPts, fe_lor_ndof, nref);
auto d_B_H = Reshape(B_H.Read(), qPts, fe_ho_ndof, nref);
auto d_D = Reshape(D.Read(), qPts, nref, nel_ho);
const int dim = mesh_ho.Dimension();
const int nq1d = (int)floor(pow(ir_ea.Size(), 1.0/dim) + 0.5);
const int nref_1d = (int)floor(pow(nref, 1.0/dim) + 0.5);
mfem::forall(fe_ho_ndof*nref*nel_ho, [=] MFEM_HOST_DEVICE (int idx)
if (!coeff_ho)
{
const int bh = idx % fe_ho_ndof;
const int iref = (idx / fe_ho_ndof) % nref;
const int iho = idx / fe_ho_ndof / nref;
// (B_lo_dofs x Q) x (Q x B_ho_dofs)
for (int bl = 0; bl < fe_lor_ndof; ++bl)
coeff_vec = 1.0;
}
else if (UsesTensorBasis(fes_ho) &&
nq1d*nref_1d <= DeviceDofQuadLimits::Get().MAX_Q1D)
{
// Fast coefficient evaluation for tensor-product case. We create a
// "composite" quadrature rule in the high-order element that is the
// union of the quadrature rules within each of the low-order-refined
// subelements.
//
// NOTE: if the integration rule order is high and there are many LOR
// subelements, this can create a very big quadrature rule. That is
// why we need to check that we do not exceed MAX_Q1D. If we do, then
// we fall back on the slower "legacy" evaluation.
// Construct the composite rule as a tensor-product of the 1D LOR rule.
IntegrationRule ir_ho = [&]()
{
real_t dot = 0.0;
for (int qi=0; qi<qPts; ++qi)
IntegrationRule ir_ho_1d(nq1d * nref_1d);
for (int iref = 0; iref < nref_1d; ++iref)
{
dot += d_B_L(qi, bl, iref) * d_D(qi, iref, iho) * d_B_H(qi, bh, iref);
const real_t a = pmats(cf_tr.embeddings[iref].matrix)(0,0);
const real_t b = pmats(cf_tr.embeddings[iref].matrix)(0,1);
for (int iq = 0; iq < nq1d; ++iq)
{
ir_ho_1d[iq + iref*nq1d].x = a + ir_ea[iq].x*(b - a);
}
}
if (dim == 1) { return ir_ho_1d; }
else if (dim == 2) { return IntegrationRule(ir_ho_1d, ir_ho_1d); }
else { return IntegrationRule(ir_ho_1d, ir_ho_1d, ir_ho_1d); }
}();
// Project the high-order coefficient on the high-order composite rule.
QuadratureSpace qs(mesh_ho, ir_ho);
CoefficientVector coeff_vec_ho(*coeff_ho.coeff, qs);
// Permute the coefficient values to the expected LOR ordering.
const int nq_ho = ir_ho.Size();
const auto d_Q_ho = Reshape(coeff_vec_ho.Read(), nq_ho, nel_ho);
const auto d_Q = Reshape(coeff_vec.Write(), qPts, nel_lor);
mfem::forall(nq_ho * nel_ho, [=] MFEM_HOST_DEVICE (int ii)
{
const int e_ho = ii / nq_ho;
const int iq_ho = ii % nq_ho;
int iq_tensor = iq_ho;
int iq_lor = 0;
int iref = 0;
int iq_stride = 1;
int iref_stride = 1;
const int nq_ho_1d = nq1d*nref_1d;
for (int d = 0; d < dim; ++d)
{
const int iq_ho_1d = iq_tensor % nq_ho_1d;
iq_tensor /= nq_ho_1d;
iq_lor += (iq_ho_1d % nq1d)*iq_stride;
iref += (iq_ho_1d / nq1d)*iref_stride;
iq_stride *= nq1d;
iref_stride *= nref_1d;
}
const int e_lor = iref + e_ho*nref;
d_Q(iq_lor, e_lor) = d_Q_ho(iq_ho, e_ho);
});
}
else
{
// Legacy/fallback coefficient evaluation for non-tensor-product cases
// or when the number of quadrature points is too large for the device
// kernels.
IntegrationPoint ip_ho;
for (int e_ho = 0; e_ho < nel_ho; ++e_ho)
{
ElementTransformation &ho_tr = *mesh_ho.GetElementTransformation(e_ho);
for (int iref = 0; iref < nref; ++iref)
{
const int e_lor = iref + e_ho*nref;
emb_tr.SetPointMat(pmats(cf_tr.embeddings[e_lor].matrix));
for (int iq = 0; iq < qPts; ++iq)
{
const IntegrationPoint &ip_lor = ir_ea[iq];
ip_tr.Transform(ip_lor, ip_ho);
ho_tr.SetIntPoint(&ip_ho);
coeff_vec[iq + e_lor*qPts] = coeff_ho.coeff->Eval(ho_tr, ip_ho);
}
}
// column major storage
v_M_LH(bl, bh, iref, iho) = dot;
}
}
// Setup data at quadrature points
const auto W = Reshape(ir_ea.GetWeights().Read(), qPts);
const auto J = Reshape(geo_facts->detJ.Read(), qPts, nel_lor);
const auto d_D = Reshape(D.Write(), qPts, nref, nel_ho);
const auto d_Q = Reshape(coeff_vec.Read(), qPts, nel_lor);
mfem::forall(qPts * nref * nel_ho, [=] MFEM_HOST_DEVICE (int tid)
{
const int q = tid % qPts;
const int iref = (tid / qPts) % nref;
const int iho = (tid / (qPts * nref)) % nel_ho;
const int lo_el_id = iref + nref*iho;
const real_t detJ = J(q, lo_el_id);
d_D(q, iref, iho) = W(q) * d_Q(q, lo_el_id) * detJ;
});
} // end of mixed assembly mass matrix
// Collect the basis functions
for (int iref = 0; iref < nref; ++iref)
{
int ilor = lor_els[iref];
// Now assemble the block-row of the mixed mass matrix associated
// with integrating HO functions against LOR functions on the LOR
// sub-element.
// Create the transformation that embeds the fine low-order element
// within the coarse high-order element in reference space
emb_tr.SetPointMat(pmats(cf_tr.embeddings[ilor].matrix));
DenseMatrix &b_lo = B_L(ilor);
DenseMatrix &b_ho = B_H(ilor);
ElemMixedEvaluation(geom, fe_ho, fe_lor, ip_tr, ir_ea, b_lo, b_ho);
} // loop over subcells of ho element
// end of quadrature point setup
} // completed setup of basis function and quadrature point
// Assemble mixed mass matrix
int iho = 0;
Array<int> lor_els;
ho2lor.GetRow(iho, lor_els);
int nref = ho2lor.RowSize(iho);
const FiniteElement &fe_ho = *fes_ho_ea.GetFE(iho);
const FiniteElement &fe_lor = *fes_lor_ea.GetFE(lor_els[0]);
const int ndof_ho = fe_ho.GetDof();
const int ndof_lor = fe_lor.GetDof();
const int qPts = D.SizeI();
M_LH.SetSize(ndof_lor*ndof_ho*nref*nel_ho, d_mt);
// Rows x columns
// Recall MFEM is column major
// rows x columns is inverted - matrix is ndof_lor x ndof_ho
auto v_M_LH = Reshape(M_LH.Write(), ndof_lor, ndof_ho, nref,
nel_ho);
const int fe_ho_ndof = fe_ho.GetDof();
const int fe_lor_ndof = fe_lor.GetDof();
auto d_B_L = Reshape(B_L.Read(), qPts, fe_lor_ndof, nref);
auto d_B_H = Reshape(B_H.Read(), qPts, fe_ho_ndof, nref);
auto d_D = Reshape(D.Read(), qPts, nref, nel_ho);
mfem::forall(fe_ho_ndof*nref*nel_ho, [=] MFEM_HOST_DEVICE (int idx)
{
const int bh = idx % fe_ho_ndof;
const int iref = (idx / fe_ho_ndof) % nref;
const int iho = idx / fe_ho_ndof / nref;
// (B_lo_dofs x Q) x (Q x B_ho_dofs)
for (int bl = 0; bl < fe_lor_ndof; ++bl)
{
real_t dot = 0.0;
for (int qi=0; qi<qPts; ++qi)
{
dot += d_B_L(qi, bl, iref) * d_D(qi, iref, iho) * d_B_H(qi, bh, iref);
}
// column major storage
v_M_LH(bl, bh, iref, iho) = dot;
}
});
}
L2ProjectionGridTransfer::L2ProjectionL2Space::L2ProjectionL2Space
(const FiniteElementSpace &fes_ho_, const FiniteElementSpace &fes_lor_,
CoefficientWithOrder coeff_ho_, CoefficientWithOrder coeff_lor_,
const bool use_ea_, MemoryType d_mt_)
: L2Projection(fes_ho_, fes_lor_, d_mt_),
use_ea(use_ea_)
: L2Projection(fes_ho_, fes_lor_, coeff_ho_, coeff_lor_, d_mt_), use_ea(use_ea_)
{
if (use_ea)
{
@@ -559,7 +658,11 @@ L2ProjectionGridTransfer::L2ProjectionL2Space::L2ProjectionL2Space
DenseMatrix Minv_lor(ndof_lor*nref, ndof_lor*nref);
DenseMatrix M_mixed(ndof_lor*nref, ndof_ho);
MassIntegrator mi;
MassIntegrator mi = [&]()
{
return coeff_lor ? MassIntegrator(*coeff_lor.coeff) : MassIntegrator();
}();
DenseMatrix M_lor_el(ndof_lor, ndof_lor);
DenseMatrixInverse Minv_lor_el(&M_lor_el);
DenseMatrix M_lor(ndof_lor*nref, ndof_lor*nref);
@@ -577,6 +680,10 @@ L2ProjectionGridTransfer::L2ProjectionL2Space::L2ProjectionL2Space
// Assemble the low-order refined mass matrix and invert locally
int ilor = lor_els[iref];
ElementTransformation *tr_lor = fes_lor.GetElementTransformation(ilor);
const int order = 2*fe_lor.GetOrder() + tr_lor->OrderW() + coeff_lor.order;
mi.SetIntegrationRule(IntRules.Get(geom, order));
mi.AssembleElementMatrix(fe_lor, *tr_lor, M_lor_el);
M_lor.CopyMN(M_lor_el, iref*ndof_lor, iref*ndof_lor);
Minv_lor_el.Factor();
@@ -668,25 +775,22 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAL2ProjectionL2Space()
// Need to compute M_L
// Note: Using user-inputted M_LH IntegrationRule ir
// (higher order than needed) in order to re-use coeff
MassIntegrator mi;
MassIntegrator mi = [&]()
{
return coeff_lor ? MassIntegrator(*coeff_lor.coeff) : MassIntegrator();
}();
const int order = 2*fes_lor.GetMaxElementOrder()
+ mesh_lor->GetTypicalElementTransformation()->OrderW()
+ coeff_lor.order;
mi.SetIntegrationRule(
IntRules.Get(mesh_lor->GetTypicalElementGeometry(), order));
Vector M_ea_lor;
int ndof_lor;
int ndof_ho;
int nref;
{
int iho = 0;
Array<int> lor_els;
ho2lor.GetRow(iho, lor_els);
nref = ho2lor.RowSize(iho);
const FiniteElement &fe_ho = *fes_ho.GetFE(iho);
const FiniteElement &fe_lor = *fes_lor.GetFE(lor_els[0]);
ndof_ho = fe_ho.GetDof();
ndof_lor = fe_lor.GetDof();
M_ea_lor.SetSize(ndof_lor*ndof_lor*nel_lor, d_mt);
}
const int ndof_lor = fes_lor.GetTypicalFE()->GetDof();
const int ndof_ho = fes_ho.GetTypicalFE()->GetDof();
const int nref = ho2lor.RowSize(0);
M_ea_lor.SetSize(ndof_lor*ndof_lor*nel_lor, d_mt);
const bool add = false;
mi.AssembleEA(fes_lor, M_ea_lor, add);
@@ -1032,8 +1136,9 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAProlongateTranspose(
L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
const FiniteElementSpace& fes_ho_, const FiniteElementSpace& fes_lor_,
CoefficientWithOrder coeff_ho_, CoefficientWithOrder coeff_lor_,
const bool use_ea_, MemoryType d_mt_)
: L2Projection(fes_ho_, fes_lor_, d_mt_),
: L2Projection(fes_ho_, fes_lor_, coeff_ho_, coeff_lor_, d_mt_),
use_ea(use_ea_)
{
@@ -1092,8 +1197,9 @@ L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
L2ProjectionGridTransfer::L2ProjectionH1Space::L2ProjectionH1Space(
const ParFiniteElementSpace& pfes_ho, const ParFiniteElementSpace& pfes_lor,
CoefficientWithOrder coeff_ho_, CoefficientWithOrder coeff_lor_,
const bool use_ea_, MemoryType d_mt_)
: L2Projection(pfes_ho, pfes_lor, d_mt_),
: L2Projection(pfes_ho, pfes_lor, coeff_ho_, coeff_lor_, d_mt_),
use_ea(use_ea_), pcg(pfes_ho.GetComm())
{
@@ -1165,12 +1271,12 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::SetupPCG()
void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space()
{
Mesh* mesh_ho = fes_ho.GetMesh();
Mesh* mesh_lor = fes_lor.GetMesh();
int nel_ho = mesh_ho->GetNE();
int nel_lor = mesh_lor->GetNE();
int ndof_ho = fes_ho.GetNDofs();
int ndof_lor = fes_lor.GetNDofs();
Mesh &mesh_ho = *fes_ho.GetMesh();
Mesh &mesh_lor = *fes_lor.GetMesh();
const int nel_ho = mesh_ho.GetNE();
const int nel_lor = mesh_lor.GetNE();
const int ndof_ho = fes_ho.GetNDofs();
const int ndof_lor = fes_lor.GetNDofs();
// If the local mesh is empty, skip all computations
if (nel_ho == 0)
@@ -1178,11 +1284,11 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space()
return;
}
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
const CoarseFineTransformations& cf_tr = mesh_lor.GetRefinementTransforms();
int nref_max = 0;
Array<Geometry::Type> geoms;
mesh_ho->GetGeometries(mesh_ho->Dimension(), geoms);
mesh_ho.GetGeometries(mesh_ho.Dimension(), geoms);
for (int ig = 0; ig < geoms.Size(); ++ig)
{
Geometry::Type geom = geoms[ig];
@@ -1205,7 +1311,8 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space()
BilinearForm Mho(fes_ho_scalar.get());
Mho.SetAssemblyLevel(AssemblyLevel::PARTIAL);
Mho.AddDomainIntegrator(new MassIntegrator);
Mho.AddDomainIntegrator(coeff_ho ? new MassIntegrator(*coeff_ho.coeff)
: new MassIntegrator);
Mho.Assemble();
// Processor local lumped Mass
@@ -1215,7 +1322,16 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space()
BilinearForm Mlor(fes_lor_scalar.get());
Mlor.SetAssemblyLevel(AssemblyLevel::PARTIAL);
Mlor.AddDomainIntegrator(new MassIntegrator);
{
MassIntegrator *mi = coeff_lor ? new MassIntegrator(*coeff_lor.coeff)
: new MassIntegrator;
const int order = 2*fes_lor.GetMaxElementOrder()
+ mesh_lor.GetTypicalElementTransformation()->OrderW()
+ coeff_lor.order;
mi->SetIntegrationRule(
IntRules.Get(mesh_lor.GetTypicalElementGeometry(), order));
Mlor.AddDomainIntegrator(mi);
}
Mlor.Assemble();
Vector ones_lor(Mlor.Width()); ones_lor = 1.0;
@@ -1228,15 +1344,14 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space()
MixedMassEA(fes_ho, fes_lor, M_LH_ea, d_mt);
// Set ownership
M_LH_local_op = new H1SpaceMixedMassOperator(fes_ho_scalar.get(),
fes_lor_scalar.get(),
&ho2lor,
&M_LH_ea);
M_LH.reset(new H1SpaceMixedMassOperator(fes_ho_scalar.get(),
fes_lor_scalar.get(),
&ho2lor,
&M_LH_ea));
ML_inv_vea.reset(new H1SpaceLumpedMassOperator(fes_ho_scalar.get(),
fes_lor_scalar.get(),
ML_inv_ea));
M_LH.reset(M_LH_local_op);
R.reset(new ProductOperator(ML_inv_vea.get(), M_LH.get(), false,
false));
@@ -1253,18 +1368,18 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space()
void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space
(const ParFiniteElementSpace& pfes_ho, const ParFiniteElementSpace& pfes_lor)
{
Mesh* mesh_ho = pfes_ho.GetParMesh();
Mesh* mesh_lor = pfes_lor.GetParMesh();
int nel_ho = mesh_ho->GetNE();
int nel_lor = mesh_lor->GetNE();
Mesh &mesh_ho = *pfes_ho.GetParMesh();
Mesh &mesh_lor = *pfes_lor.GetParMesh();
int nel_ho = mesh_ho.GetNE();
int nel_lor = mesh_lor.GetNE();
int ndof_ho = pfes_ho.GetNDofs();
int ndof_lor = pfes_lor.GetNDofs();
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
const CoarseFineTransformations& cf_tr = mesh_lor.GetRefinementTransforms();
int nref_max = 0;
Array<Geometry::Type> geoms;
mesh_ho->GetGeometries(mesh_ho->Dimension(), geoms);
mesh_ho.GetGeometries(mesh_ho.Dimension(), geoms);
for (int ig = 0; ig < geoms.Size(); ++ig)
{
Geometry::Type geom = geoms[ig];
@@ -1287,7 +1402,8 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space
ParBilinearForm pMho(pfes_ho_scalar.get());
pMho.SetAssemblyLevel(AssemblyLevel::PARTIAL);
pMho.AddDomainIntegrator(new MassIntegrator);
pMho.AddDomainIntegrator(coeff_ho ? new MassIntegrator(*coeff_ho.coeff)
: new MassIntegrator);
pMho.Assemble();
// Processor local lumped Mass
@@ -1297,7 +1413,16 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space
ParBilinearForm pMlor(pfes_lor_scalar.get());
pMlor.SetAssemblyLevel(AssemblyLevel::PARTIAL);
pMlor.AddDomainIntegrator(new MassIntegrator);
{
MassIntegrator *mi = coeff_lor ? new MassIntegrator(*coeff_lor.coeff)
: new MassIntegrator;
const int order = 2*fes_lor.GetMaxElementOrder()
+ mesh_lor.GetTypicalElementTransformation()->OrderW()
+ coeff_lor.order;
mi->SetIntegrationRule(
IntRules.Get(mesh_lor.GetTypicalElementGeometry(), order));
pMlor.AddDomainIntegrator(mi);
}
pMlor.Assemble();
Vector ones_lor(pMlor.Width()); ones_lor = 1.0;
@@ -1570,7 +1695,7 @@ std::unique_ptr<SparseMatrix>>
int ilor = lor_els[iref];
ElementTransformation* el_tr = fes_lor.GetElementTransformation(ilor);
int order = 2 * fe_lor.GetOrder() + el_tr->OrderW();
int order = 2 * fe_lor.GetOrder() + el_tr->OrderW() + coeff_lor.order;
const IntegrationRule* ir = &IntRules.Get(geom, order);
ML_el = 0.0;
for (int i = 0; i < ir->GetNPoints(); ++i)
@@ -1578,7 +1703,13 @@ std::unique_ptr<SparseMatrix>>
const IntegrationPoint& ip_lor = ir->IntPoint(i);
fe_lor.CalcShape(ip_lor, shape_lor);
el_tr->SetIntPoint(&ip_lor);
ML_el += (shape_lor *= (el_tr->Weight() * ip_lor.weight));
real_t w = ip_lor.weight;
if (coeff_lor)
{
w *= coeff_lor.coeff->Eval(*el_tr, ip_lor);
}
shape_lor *= el_tr->Weight() * w;
ML_el += shape_lor;
}
fes_lor.GetElementDofs(ilor, dofs_lor);
ML_inv.AddElementVector(dofs_lor, ML_el);
@@ -2024,8 +2155,8 @@ void L2ProjectionGridTransfer::BuildF()
{
if (!Parallel())
{
F = new L2ProjectionH1Space(dom_fes, ran_fes,
use_ea, d_mt);
F = new L2ProjectionH1Space(
dom_fes, ran_fes, coeff_ho, coeff_lor, use_ea, d_mt);
}
else
{
@@ -2034,15 +2165,15 @@ void L2ProjectionGridTransfer::BuildF()
static_cast<mfem::ParFiniteElementSpace&>(dom_fes);
const mfem::ParFiniteElementSpace& ran_pfes =
static_cast<mfem::ParFiniteElementSpace&>(ran_fes);
F = new L2ProjectionH1Space(dom_pfes, ran_pfes,
use_ea, d_mt);
F = new L2ProjectionH1Space(
dom_pfes, ran_pfes, coeff_ho, coeff_lor, use_ea, d_mt);
#endif
}
}
else
{
F = new L2ProjectionL2Space(dom_fes, ran_fes,
use_ea, d_mt);
F = new L2ProjectionL2Space(
dom_fes, ran_fes, coeff_ho, coeff_lor, use_ea, d_mt);
}
}
+76 -7
View File
@@ -19,6 +19,8 @@
#include "pfespace.hpp"
#endif
#include <cstddef>
namespace mfem
{
@@ -162,6 +164,18 @@ public:
};
struct CoefficientWithOrder
{
Coefficient *coeff;
int order;
CoefficientWithOrder() : coeff(nullptr), order(0) { }
CoefficientWithOrder(std::nullptr_t) : coeff(nullptr), order(0) { }
CoefficientWithOrder(Coefficient &coeff_) : coeff(&coeff_), order(1) { }
CoefficientWithOrder(Coefficient &coeff_, int order_)
: coeff(&coeff_), order(order_) { }
operator bool() const { return coeff != nullptr; }
};
/** @brief Transfer data in L2 and H1 finite element spaces between a coarse
mesh and an embedded refined mesh using L2 projection. */
/** The forward, coarse-to-fine, transfer uses L2 projection. The backward,
@@ -207,6 +221,8 @@ public:
protected:
const FiniteElementSpace& fes_ho;
const FiniteElementSpace& fes_lor;
CoefficientWithOrder coeff_ho;
CoefficientWithOrder coeff_lor;
MemoryType d_mt;
Array<int> offsets;
@@ -214,8 +230,15 @@ public:
L2Projection(const FiniteElementSpace& fes_ho_,
const FiniteElementSpace& fes_lor_,
CoefficientWithOrder coeff_ho_,
CoefficientWithOrder coeff_lor_,
MemoryType d_mt_ = Device::GetHostMemoryType());
L2Projection(const FiniteElementSpace& fes_ho_,
const FiniteElementSpace& fes_lor_,
MemoryType d_mt_ = Device::GetHostMemoryType())
: L2Projection(fes_ho_, fes_lor_, nullptr, nullptr, d_mt_) { }
void BuildHo2Lor(int nel_ho, int nel_lor,
const CoarseFineTransformations& cf_tr);
@@ -225,11 +248,11 @@ public:
IntegrationPointTransformation& ip_tr,
DenseMatrix& M_mixed_el) const;
void ElemMixedMass(Geometry::Type geom, const FiniteElement& fe_ho,
const FiniteElement& fe_lor,
ElementTransformation* el_tr,
IntegrationPointTransformation& ip_tr,
DenseMatrix& B_L, DenseMatrix& B_H) const;
void ElemMixedEvaluation(Geometry::Type geom, const FiniteElement& fe_ho,
const FiniteElement& fe_lor,
IntegrationPointTransformation& ip_tr,
const IntegrationRule& ir,
DenseMatrix& B_L, DenseMatrix& B_H) const;
public:
/* Returns the Mixed Mass M_LH via device element assembly by building the
basis functions and data at the quadrature points. */
@@ -287,9 +310,17 @@ public:
public:
L2ProjectionL2Space(const FiniteElementSpace& fes_ho_,
const FiniteElementSpace& fes_lor_,
CoefficientWithOrder coeff_ho_,
CoefficientWithOrder coeff_lor_,
const bool use_ea_,
MemoryType d_mt_ = Device::GetHostMemoryType());
L2ProjectionL2Space(const FiniteElementSpace& fes_ho_,
const FiniteElementSpace& fes_lor_,
const bool use_ea_,
MemoryType d_mt_ = Device::GetHostMemoryType())
: L2ProjectionL2Space(fes_ho_, fes_lor_, nullptr, nullptr, use_ea_, d_mt_) { }
/*Same as above but assembles and stores R_ea, P_ea */
void EAL2ProjectionL2Space();
@@ -356,13 +387,30 @@ public:
public:
L2ProjectionH1Space(const FiniteElementSpace &fes_ho_,
const FiniteElementSpace &fes_lor_,
CoefficientWithOrder coeff_ho_,
CoefficientWithOrder coeff_lor_,
const bool use_ea_,
MemoryType d_mt_ = Device::GetHostMemoryType());
L2ProjectionH1Space(const FiniteElementSpace& fes_ho_,
const FiniteElementSpace& fes_lor_,
const bool use_ea_,
MemoryType d_mt_ = Device::GetHostMemoryType())
: L2ProjectionH1Space(fes_ho_, fes_lor_, nullptr, nullptr, use_ea_, d_mt_) { }
#ifdef MFEM_USE_MPI
L2ProjectionH1Space(const ParFiniteElementSpace &pfes_ho_,
const ParFiniteElementSpace &pfes_lor_,
CoefficientWithOrder coeff_ho_,
CoefficientWithOrder coeff_lor_,
const bool use_ea_,
MemoryType d_mt_ = Device::GetHostMemoryType());
L2ProjectionH1Space(const ParFiniteElementSpace& fes_ho_,
const ParFiniteElementSpace& fes_lor_,
const bool use_ea_,
MemoryType d_mt_ = Device::GetHostMemoryType())
: L2ProjectionH1Space(fes_ho_, fes_lor_, nullptr, nullptr, use_ea_, d_mt_) { }
#endif
/// Same as above but assembles action of R through 4 parts:
/// ( ) inv( lumped(M_L) ), which is a diagonal matrix (essentially a vector)
@@ -508,18 +556,38 @@ public:
virtual ~L2Prolongation() { }
};
/// Coefficient for the mixed L2 inner product.
CoefficientWithOrder coeff_ho;
/// Coefficient for the low-order L2 inner product.
CoefficientWithOrder coeff_lor;
L2Projection *F; ///< Forward, coarse-to-fine, operator
L2Prolongation *B; ///< Backward, fine-to-coarse, operator
bool force_l2_space;
public:
/// Construct the unweighted L2 projection grid transfer.
L2ProjectionGridTransfer(FiniteElementSpace &coarse_fes_,
FiniteElementSpace &fine_fes_,
bool force_l2_space_ = false,
MemoryType d_mt_ = Device::GetHostMemoryType()) // move to method
: GridTransfer(coarse_fes_, fine_fes_),
F(NULL), B(NULL), force_l2_space(force_l2_space_)
{ }
coeff_ho(nullptr), coeff_lor(nullptr), F(nullptr), B(nullptr),
force_l2_space(force_l2_space_) { }
/// @brief Construct the weighted L2 projection grid transfer.
///
/// The low-order inner product is weighted by @a coeff_lor, and the mixed
/// inner product is weighted by @a coeff_ho.
L2ProjectionGridTransfer(FiniteElementSpace &coarse_fes_,
FiniteElementSpace &fine_fes_,
CoefficientWithOrder coeff_ho_,
CoefficientWithOrder coeff_lor_,
bool force_l2_space_ = false,
MemoryType d_mt_ = Device::GetHostMemoryType()) // move to method
: GridTransfer(coarse_fes_, fine_fes_),
coeff_ho(coeff_ho_), coeff_lor(coeff_lor_), F(nullptr), B(nullptr),
force_l2_space(force_l2_space_) { }
virtual ~L2ProjectionGridTransfer();
const Operator &ForwardOperator() override;
@@ -527,6 +595,7 @@ public:
const Operator &BackwardOperator() override;
bool SupportsBackwardsOperator() const override;
private:
void BuildF();
};

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