Compare commits

...
Author SHA1 Message Date
Julian Andrej ea02b5bd36 don't delete integrator 2025-05-30 17:42:47 -07:00
Julian Andrej ae93d82ccd different AV algorithm 2025-05-30 14:12:18 -07:00
Julian Andrej 4eb88fbfa5 add power method 2025-05-29 19:56:47 -07:00
Julian Andrej 5a90acfed6 disable taylor source when not using problem 0 2025-05-29 15:33:51 -07:00
Julian Andrej 69cea35209 parallel updates 2025-04-23 17:54:43 -07:00
Julian Andrej 026493bea7 memory leaks 2025-04-22 17:14:24 -07:00
Julian Andrej fef354dfc5 add warning comment 2025-04-21 08:33:07 -07:00
Julian Andrej 15519823c4 switch to PA in implicit mult 2025-04-17 08:03:39 -07:00
Julian Andrej 127390c6a7 reenable time stepping logic for implicit 2025-04-16 15:34:09 -07:00
Julian Andrej a62661f3cc warnings 2025-04-16 14:39:15 -07:00
Julian Andrej e76ffd82e6 strange bug 2025-04-16 14:39:10 -07:00
Julian Andrej 6288f0b741 enzyme makefile 2025-04-16 14:38:48 -07:00
Julian Andrej 54f8470412 makefile stuff 2025-04-16 11:18:05 -07:00
Julian Andrej 5059d631ba properly delete petsc objects 2025-04-16 08:06:16 -07:00
Julian Andrej a3d652e2c2 unsmart pointers 2025-04-15 11:43:05 -07:00
Julian Andrej 0de221816e lag jacobian assemble 2025-04-15 11:28:42 -07:00
Julian Andrej cbdd94b0ee Merge branch 'master' into dfem-phase1-dev 2025-04-14 09:30:12 -07:00
Julian Andrej b09e705f66 updates with petsc snes 2025-04-14 09:27:17 -07:00
Tzanio Kolev d07af7ce8b Merge pull request #4798 from mfem/new-dev-version-4.8.1
Update version numbers to 4.8.1 -- a new development version
2025-04-11 16:37:57 -07:00
Veselin Dobrev f9c67d7a9e Update version numbers to 4.8.1 -- a new development version 2025-04-11 10:57:07 -07:00
Tzanio Kolev a017191010 Merge pull request #4681 from mfem/mfem-4.8-dev
Final changes for mfem-4.8
2025-04-09 16:16:44 -07:00
Tzanio Kolev b02893686a Final fix before mfem-4.8 2025-04-09 15:40:18 -07:00
Tzanio Kolev 008ac526d1 Final fixes in INSTALL before mfem-4.8 2025-04-09 15:34:54 -07:00
Veselin Dobrev 22fe453fda In CHANGELOG, remove new bullets accidentally added in the section for
version 4.7
2025-04-09 15:26:58 -07:00
Tzanio Kolev 4d39303ad9 Update CHANGELOG 2025-04-09 15:11:38 -07:00
Julian Andrej 0a115bf6a8 reintroduce matrix assembly 2025-04-08 16:46:55 -07:00
Andrew Ho a87dadb8b3 updated HIP install instructions. 2025-04-07 22:08:28 -07:00
Julian Andrej 3835a5e5e8 laghos mpi implicit bugfix 2025-04-07 11:31:03 -07:00
Julian Andrej 1ea4785ab3 doperator size inconsistency 2025-04-07 08:46:26 -07:00
Julian Andrej a109c7e8ad correct parallel tests 2025-04-07 08:45:49 -07:00
Tzanio Kolev cb78abf671 Merge branch 'master' into mfem-4.8-dev 2025-04-06 11:33:39 -07:00
Tzanio Kolev 5e32ae87b4 Merge pull request #4783 from mfem/github-actions-ompi
Use Open MPI instead of MPICH in GitHub Actions
2025-04-06 11:33:05 -07:00
Tzanio Kolev 1963a63075 minor 2025-04-06 11:25:43 -07:00
Tzanio Kolev 6e36cbfb5f minor 2025-04-06 10:35:14 -07:00
Will Pazner 253f578ae4 Use Open MPI instead of MPICH in GitHub Actions
The Ubuntu 24.04 MPICH package is broken, causing CI failures, see
https://bugs.launchpad.net/ubuntu/+source/mpich/+bug/2072338
2025-04-06 09:08:14 -07:00
camierjs 984b269972 Fix parallel dfem diffusion tests w/o prolongation_transpose 2025-04-05 13:21:01 -07:00
Veselin Dobrev 52ff1fdbaf Add GitHub CI test on macOS with single precision 2025-04-05 10:49:20 -07:00
Veselin Dobrev ac53af01ef Fix errors with clang in the single precision build 2025-04-05 10:41:23 -07:00
Veselin Dobrev 5f5b15b5f6 Small tweaks: compilation warnings, PUMI link path, lsf_integral is
serial (not parallel) miniapp.
2025-04-05 08:28:36 -07:00
Andrew Ho d17263e7e5 revert CMake HIP changes
After further testing I suspect the original issue I ran into must
have been some unexpected transient configuration issue that I am no
longer able to reproduce.
2025-04-04 15:32:50 -07:00
Andrew Ho 2f852723f8 Merge remote-tracking branch 'base/mfem-4.8-dev' into mfem-4.8-dev 2025-04-04 12:49:59 -07:00
Andrew Ho e41811ad7f Fixed specifying the HIP_ARCH for CMake >= 3.21.
Added CMake HIP build instructions.
2025-04-04 12:48:22 -07:00
Tzanio Kolev 37034f28a6 Merge pull request #4780 from mfem/fix-pyramid-thread-safe
Fix compilation errors for pyramid elements with MFEM_THREAD_SAFE
2025-04-04 12:21:17 -07:00
Will Pazner 43f8ea13dd Fix compilation errors for pyramid elements with MFEM_THREAD_SAFE 2025-04-04 09:19:12 -07:00
Tzanio Kolev c5363ebaaf Merge branch 'master' into mfem-4.8-dev 2025-04-03 13:00:19 -07:00
Tzanio Kolev c11d9acab8 Merge pull request #4750 from mfem/avoid-uninitialized-buffer-ops
Avoid uninitialized memory use
2025-04-03 12:58:52 -07:00
Tzanio Kolev f0525f62e3 Merge branch 'master' into avoid-uninitialized-buffer-ops 2025-04-03 12:22:31 -07:00
Veselin Dobrev ae478a45cd Fix memory leak 2025-04-03 07:09:12 -07:00
Veselin Dobrev 9e66156622 Fix the case of vdim > 1 in the method EAL2ProjectionH1Space() in
class L2ProjectionGridTransfer::L2ProjectionH1Space.
2025-04-03 06:44:23 -07:00
Tzanio KolevandJan Nikl 09df072897 Update doc/CodeDocumentation.dox
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-04-03 06:38:38 -07:00
Tzanio Kolev 0641035fb9 Merge branch 'master' into mfem-4.8-dev 2025-04-02 14:54:37 -07:00
Tzanio Kolev b5ff969e5f Merge pull request #4774 from mfem/cmake4-fix
Tweaks for CMake 4 and GitHub CI
2025-04-02 14:53:48 -07:00
Chris Vogl 67b5011d50 Update CHANGELOG
minor whitespace change
2025-04-02 12:15:48 -07:00
Veselin Dobrev 29f26a96ea In CHANGELOG, add a bullet with the new CMake version requirements 2025-04-02 11:07:56 -07:00
Tzanio Kolev e5ae0c8b61 Merge branch 'master' into mfem-4.8-dev 2025-04-02 10:27:18 -07:00
Veselin Dobrev bd074035ae Fix typo 2025-04-02 10:02:53 -07:00
Tzanio Kolev aad3a6be39 Merge pull request #4766 from tepperly/mma-with-fewer-raw-pointers
Remove many of the raw pointers from the new MMA solver
2025-04-02 09:50:13 -07:00
Veselin Dobrev 1d3ffaa0bd Fix the CUDA build with older versions of CMake, e.g. v3.20.
Update the CMake version requirement when building with CUDA to 3.17
which is the version where the CUDAToolkit module was added.

Fix CUDA warnings.
2025-04-02 05:00:40 -07:00
Veselin Dobrev 42774a1e21 In CMake, remove logic for CMake < 3.12 and move the logic for the
CUDA architectures before 'enable_language(CUDA)' to allow us to
initialize 'CMAKE_CUDA_ARCHITECTURES' (if not alreasy set by the user)
based on 'CUDA_ARCH'.
2025-04-02 00:29:06 -07:00
Tzanio Kolev ac2beeb4ed Merge branch 'master' into cmake4-fix 2025-04-01 17:41:48 -07:00
Tzanio Kolev b44d75655e Small fixes for mfem-4.8 2025-04-01 17:14:58 -07:00
Tzanio Kolev b8fcc35cbc Merge branch 'master' into mfem-4.8-dev 2025-04-01 16:44:23 -07:00
Veselin Dobrev 5588024a50 Merge pull request #4741 from mfem/tmop-periodic
TMOP for periodic meshes
2025-04-01 16:35:03 -07:00
Vladimir Z Tomov 48a270679c Merge branch 'master' into tmop-periodic 2025-04-01 16:18:06 -07:00
Veselin Dobrev 3d99a9962a In CMakeLists.txt, add a comment about the requirement for CMake 3.12 2025-04-01 16:11:49 -07:00
Veselin Dobrev c6d495ac37 Update cmake4.patch so it can be applied after the other patches 2025-04-01 15:53:15 -07:00
Vladimir Z Tomov 9ad90f4491 Updated the LEXICOGRAPHIC comment in fespace.hpp. 2025-04-01 15:40:30 -07:00
Veselin Dobrev 8c0a137dae Update the CMake minimum version to 3.12.0 and add policy version.
Add a patch for CMake 4 for the "metis-mfem" vcpkg in config/vcpkg.
2025-04-01 15:39:36 -07:00
Tzanio Kolev 8315140852 Merge branch 'master' into mfem-4.8-dev
Conflicts:
	CHANGELOG
	makefile
2025-04-01 12:31:58 -07:00
Veselin Dobrev 3118e38912 Merge pull request #4701 from mfem/dev-inverse-transform
GPU Batch Inverse Element Transform
2025-04-01 12:14:54 -07:00
Tzanio Kolev c34ab2dd69 Renamed hpref_serial/hpref to hpref/phpref in miniapps/meshing 2025-04-01 10:06:44 -07:00
Tzanio Kolev 8a474b7365 Renamed Laplace to Poisson where appropriate 2025-03-31 20:56:40 -07:00
Tzanio Kolev b3e19a2e7a Typos and styling 2025-03-31 18:48:43 -07:00
Tzanio Kolev 54b85f7a1e CHANGELOG changes for mfem-4.8 2025-03-31 16:02:39 -07:00
Arturo Vargas 835b0b72cf Merge branch 'master' into avoid-uninitialized-buffer-ops 2025-03-31 15:31:24 -07:00
Arturo Vargas 25fa866918 fix bugs when having MPI available 2025-03-31 15:31:02 -07:00
Tzanio Kolev ea56560951 CHANGELOG changes for mfem-4.8 2025-03-31 15:23:18 -07:00
Tzanio Kolev e2e497bece CHANGELOG changes for mfem-4.8 2025-03-31 15:06:44 -07:00
Arturo Vargas d77ce29bd6 first pass at fixing bugs 2025-03-31 13:49:57 -07:00
Tzanio Kolev 6e3a32574b Merge branch 'master' into mfem-4.8-dev 2025-03-31 12:46:15 -07:00
Tom Epperly e2b2df0345 Change raw pointers/new/delete in LUSolve to unique_ptr equivalents 2025-03-31 10:04:41 -07:00
Tom Epperly a63be294bd Fix MFEM_ABORT, so MFEM_USE_LAPACK can be true 2025-03-31 08:16:38 -07:00
Tom Epperly e66f6b239f Fix LAPACK calls 2025-03-31 07:35:54 -07:00
Tom Epperly 3ab6b3af2d Merge remote-tracking branch 'origin/master' into mma-with-fewer-raw-pointers 2025-03-31 06:54:11 -07:00
Tzanio Kolev 38c199008f Merge pull request #4571 from mfem/hybridization-gpu
GPU hybridization
2025-03-30 09:44:57 -07:00
Tzanio Kolev fa28a928e4 Merge branch 'master' into mfem-4.8-dev 2025-03-29 13:22:14 -07:00
Tzanio Kolev 24365aa14d Merge pull request #4660 from mfem/reduction
Reduction framework
2025-03-29 13:00:21 -07:00
Tzanio Kolev 2b7360ecab Merge pull request #4666 from mfem/par-sub-transfer-fix
Bug fix and improvements to (Par)TransferMap
2025-03-29 12:57:40 -07:00
Andrew Ho c6159ef184 Merge remote-tracking branch 'base/dev-inverse-transform' into dev-inverse-transform 2025-03-28 16:04:45 -07:00
Andrew Ho 537112ebb8 cleaned up kernel specialization registration
Added assert for unsupported NewtonSegmentProject
2025-03-28 16:03:33 -07:00
Tom Epperly 956da4de04 Fix style 2025-03-28 15:22:35 -07:00
Tom Epperly 6699fc9ab0 Fix parallel methods that I hadn't built yet. 2025-03-28 15:15:12 -07:00
Andrew Ho 1912c74587 Merge branch 'master' into dev-inverse-transform 2025-03-28 15:12:21 -07:00
Tom Epperly 45747103f1 Another missing header 2025-03-28 14:59:04 -07:00
Tom Epperly dbd8d3d7d3 Fix #include 2025-03-28 14:30:25 -07:00
Tom Epperly bbc7658395 Fix one pointer to reference mistake 2025-03-28 14:02:12 -07:00
Tom Epperly a1dedbb916 Remove many of the raw pointers from the new MMA solver 2025-03-28 13:46:37 -07:00
Andrew Ho 06234a875f More cleanup
assert on unsupported ClosestRefNode
fix warning on trying to use | for bools
2025-03-28 13:06:41 -07:00
Andrew Ho 2ce31f6841 one more old code comment 2025-03-28 13:01:10 -07:00
Andrew Ho 6859612af9 cleanup old code 2025-03-28 12:58:47 -07:00
Andrew Ho d9d963cc88 Address review comments
- changed use_dev to use_device
- added a way to directly set initial guess order rather than have
  relative order only
- cleanup newton solves
- updated some documentation
2025-03-28 12:47:49 -07:00
Andrew Ho 669b6e6e49 Cleanup eltrans_basis.hpp
Use segment project for square/cube project, updated doc on what
project returns
2025-03-28 12:44:46 -07:00
Will Pazner 070c9f2787 Update CHANGELOG 2025-03-28 11:49:10 -07:00
Will Pazner 631792da83 Merge remote-tracking branch 'origin/master' into hybridization-gpu 2025-03-28 11:46:53 -07:00
Will Pazner 001f6e6137 Fix H(div) Element Assembly unit test with CUDA 2025-03-28 11:34:28 -07:00
Tzanio Kolev d5a0e90e11 Merge branch 'master' into reduction 2025-03-28 08:41:23 -07:00
Tzanio Kolev 99f88877e6 Merge pull request #4764 from mfem/lor-markers-fix
Fix dereferencing LOR markers
2025-03-28 08:23:00 -07:00
Andrew Ho dd38d5fb80 unused label 2025-03-27 16:10:05 -07:00
Andrew Ho 9434bb5a8e moved goto from reduce 2025-03-27 16:09:26 -07:00
Andrew Ho bcbd52a318 need workspaces and reducers to share common code 2025-03-27 15:42:53 -07:00
Andrew Ho a0fd156f64 cleanup execution paths
cuda/hip/cpu/debug should take very similar execution paths now.

Added missing HostRead to Vector::operator*(const real_t*) and updated
documentation
2025-03-27 15:39:07 -07:00
Andrew Ho 458348a217 revert all mem_manager changes from dynamic shared memory stuff 2025-03-27 14:44:29 -07:00
Andrew Ho 746ff7813b Merge remote-tracking branch 'base/master' into reduction 2025-03-27 14:40:05 -07:00
Andrew Ho 0120b8d50a Fixed doxygen errors 2025-03-27 14:34:24 -07:00
Veselin Dobrev 91b624b9ec Merge pull request #4746 from mfem/umpire-with-external-fmt
Support Umpire with external FMT
2025-03-27 13:56:13 -07:00
Will Pazner 359387131d Merge pull request #4738 from mfem/external-macros
External macros
2025-03-27 13:30:50 -07:00
Will Pazner ad3c02a99c Fix dereferencing LOR markers 2025-03-27 10:18:29 -07:00
Vladimir Z Tomov af8fd2bdb8 minor 2025-03-27 09:50:20 -07:00
Vladimir Z Tomov 640042d6f9 style 2025-03-27 09:39:04 -07:00
Vladimir Z Tomov a954093bad Fixed MinDetJpr() for displacements (it was forgotten). 2025-03-27 09:37:51 -07:00
Vladimir Z Tomov 94d7d2fd0a Merge branch 'master' into tmop-periodic 2025-03-27 09:08:04 -07:00
Will Pazner b91255d889 Allow RAJA kernels with zero size 2025-03-26 17:05:13 -07:00
Will Pazner 4979dc1701 Small style fixes 2025-03-26 15:03:05 -07:00
Tzanio Kolev 98834ea898 Synchronize the list of source direcrtories in the following files:
1. CONTRIBUTING.md
2. doc/CodeDocumentation.conf.in
3. makefile

Adding fem/moonolith in the last one exposed building issues that were fixed
(the MOONOLITH sources were not skipped when building without it).
2025-03-26 14:35:27 -07:00
Tzanio Kolev 6558b84cb2 Merge branch 'master' into hybridization-gpu 2025-03-26 13:44:31 -07:00
Tzanio Kolev 6b23a6bb26 Merge pull request #4739 from mfem/hybridization-gpu-ext-bdr-int
Restored external boundary constraints integrators in [gpu-hybridization]
2025-03-26 13:43:12 -07:00
Tzanio Kolev dc7c8fb6d7 Merge branch 'master' into mfem-4.8-dev 2025-03-26 12:20:40 -07:00
Tzanio Kolev d1b85413f5 Merge pull request #4691 from mfem/issue_4403
Small fixes for (ND,RT)_R(1,2)D visualization [issue_4403]
2025-03-26 10:23:41 -07:00
Tzanio Kolev 25c2975d86 Merge pull request #4038 from mfem/ppr
Parallel p-refinement
2025-03-25 20:13:44 -07:00
Tzanio Kolev 53b047c906 Merge pull request #4460 from mfem/pyramid-dev
Arbitrary order bases on Pyramids [pyramid-dev]
2025-03-25 19:27:57 -07:00
Tzanio Kolev d43343b629 Merge branch 'master' into ppr 2025-03-25 19:03:59 -07:00
Tzanio Kolev 157de7e776 Merge pull request #4486 from mfem/MMA_PR
Method of Moving Asymptotes
2025-03-25 19:02:33 -07:00
Will Pazner 337ad6b26d Merge remote-tracking branch 'origin/master' into hybridization-gpu
# Conflicts:
#	tests/unit/fem/test_bilinearform.cpp
2025-03-25 11:38:02 -07:00
Will Pazner 6dfb871840 Remove unneeded virtual 2025-03-25 11:32:44 -07:00
Will Pazner 5bc8702b6d Fix typo 2025-03-25 11:32:37 -07:00
Stowell, Mark L. 4f1bcca3b1 Updating CHANGELOG 2025-03-25 11:25:22 -07:00
Tzanio Kolev 3afd220361 styling 2025-03-24 18:50:50 -07:00
Andrew Ho bd91fb1317 Merge branch 'master' into external-macros 2025-03-24 13:10:21 -07:00
Will Pazner a5f883bf12 Merge pull request #4755 from mfem/sli-cheby-additions
Small additions in `SLISolver` and `OperatorChebyshevSmoother`
2025-03-24 12:57:45 -07:00
Will Pazner 8524697c43 Merge pull request #4737 from mfem/fix-4732
fix for #4732
2025-03-24 12:57:32 -07:00
Will Pazner d98920592c Merge pull request #4743 from mfem/fix-mpi_integer
Fix MPI data types
2025-03-24 12:57:19 -07:00
Vladimir Z Tomov ae400812cf style 2025-03-24 12:39:38 -07:00
Vladimir Z Tomov 31c50b0df8 Aded periodic-tri.mesh. 2025-03-24 12:35:18 -07:00
Vladimir Z Tomov 6bef88f661 Allow NodalFiniteElements in ElementRestriction. 2025-03-24 11:14:16 -07:00
Tzanio Kolev 565829126e Merge branch 'master' into pyramid-dev 2025-03-24 09:28:24 -07:00
Tzanio Kolev 589403aae2 Merge branch 'master' into ppr 2025-03-24 09:27:52 -07:00
Andrew Ho 8cc350380b Merge branch 'master' into dev-inverse-transform 2025-03-24 06:30:52 -07:00
Andrew Ho 979cf18e6c Merge branch 'master' into external-macros 2025-03-24 06:29:46 -07:00
Andrew Ho fce20d5b0a Merge branch 'master' into reduction 2025-03-24 06:29:18 -07:00
Vladimir Z Tomov beb78bfddd Review comments. 2025-03-23 23:02:13 -07:00
dylan-copeland 1c6e0a6cf8 Increase tolerance for single precision build. 2025-03-20 20:21:56 -07:00
Veselin Dobrev 0a0117d4bc In SLISolver::Mult, support optimized handling of the case b = 0 by
providing empty b (i.e. b.Size() == 0) as the input; this is allowed
only in iterative mode where it makes sense for smoothing operations.

In class OperatorChebyshevSmoother, in the constructor using the
power method to estimate the maximal eigenvalue, add an optional
random seed parameter used to initialize the starting random vector.

Fix a warning in mfem::internal::sqrt for dual numbers.

Fix a doxygen warning in the definition of SparseMatrix::handle.
2025-03-20 19:55:47 -07:00
Andrew Ho f8c68782b6 update changelog 2025-03-19 15:44:19 -07:00
Tom StittandVeselin Dobrev cf821ea682 Update config/defaults.mk
Co-authored-by: Veselin Dobrev <v-dobrev@users.noreply.github.com>
2025-03-19 12:14:27 -07:00
Mittal, Ketan fd451123c9 Merge branch 'master' of https://github.com/mfem/mfem into tmop-periodic 2025-03-19 09:11:04 -07:00
Tzanio Kolev eb39463b86 Merge pull request #4702 from mfem/VectorDiffusionIntegrator
VectorDivergenceIntegrator on surface meshes
2025-03-18 20:06:39 -07:00
Tzanio Kolev b8da77edd5 Merge branch 'master' into mfem-4.8-dev 2025-03-18 19:37:25 -07:00
Tzanio Kolev 5a5be923a3 Merge branch 'master' into VectorDiffusionIntegrator 2025-03-18 18:58:05 -07:00
Tzanio Kolev 3fcceb4aeb Merge pull request #4694 from mfem/tmop-delta
TMOP - solve for displacements
2025-03-18 18:57:27 -07:00
Tom Stitt fc807c84d7 Use fes_lor_scalar instead of fes_lor so that we don't do operations on uninitialzed memory, which may lead to floating point exceptions 2025-03-18 13:48:48 -07:00
Tom Stitt 7abbfe3846 also include FMT_DIR/lib64 2025-03-18 12:19:07 -07:00
linke2 1634aa538e consistent constructors, MMASubSvanberg renaming 2025-03-18 09:51:21 -07:00
Andrew Ho ffee08a1ac Merge branch 'master' into reduction 2025-03-18 09:49:17 -07:00
linke2 8a1640725b changed constructor list 2025-03-17 20:19:42 -07:00
linke2 3b536d765c added parallel constructor (unconstrained) 2025-03-17 17:46:52 -07:00
linke2 d47b17c968 enhanced documentation 2025-03-17 17:26:27 -07:00
linke2 f78553cc86 updated unit test 2025-03-17 17:07:49 -07:00
Tom Stitt f3d0946c5e When Umpire is configured with an external fmt we need to specify FMT_DIR and include extra include and link flags for Umpire 2025-03-17 15:21:56 -07:00
Andrew Ho 46f27fc8bb Merge remote-tracking branch 'base/master' into dev-inverse-transform 2025-03-17 14:46:55 -07:00
Andrew Ho a2949dc8fd revert forall changes
the 1D batching was only used for initial testing, could be in a separate
PR if needed.
2025-03-17 14:45:59 -07:00
Mathias Rainer Schmidt 04e70e9c7c - changed comments 2025-03-17 14:18:48 -07:00
Andrew Ho 8b103e1215 changed tests to pass mesh directly 2025-03-17 13:51:17 -07:00
Andrew Ho e386c1a655 Default to closed uniform points for edgescan if btype is invalid
Update documentation
2025-03-17 13:33:07 -07:00
Andrew Ho 52eb4ff76e Added more tests 2025-03-17 11:14:54 -07:00
linke2 4d648556e6 - added verbosity level
- corrected unit test
- correct error reporting
2025-03-16 20:20:15 -07:00
linke2 29276d0b0c fixed typo 2025-03-14 20:34:26 -07:00
linke2 ade5ef80f4 commit checks 2025-03-14 19:37:32 -07:00
linke2 03d23a3fcf - finalized header file
- updated comments in .cpp, .hpp, unit-test
2025-03-14 18:42:19 -07:00
Vladimir Z Tomov 0ccc9b7346 comment 2025-03-14 17:48:30 -07:00
Andrew Ho 7344659f4b added comment explaining not using MPI_CXX_BOOL directly 2025-03-14 17:28:54 -07:00
Vladimir Z Tomov 26f4d3927c Changes in mesh-optimizer.cpp.
Fixed a bug in serial + PA + CG advector.
2025-03-14 17:28:03 -07:00
Mathias Rainer Schmidt f21d9518ac - updated unit tests
- moved iteration counter into MMA
2025-03-14 16:18:02 -07:00
Andrew Ho f3360ac118 switch to MFEM_MPI_CXX_BOOL
This is an internal macro which switches between MPI_CXX_BOOL and
MPI_C_BOOL if MPI_CXX_BOOL happens to not be defined (ex.: in Windows).
2025-03-14 14:19:17 -07:00
Mathias Rainer Schmidt a2046cc42e - deleted MMAOpt
- added function call for un-constraint optimization
- added unit test for un-contraint optimizaiton
2025-03-14 12:39:40 -07:00
dylan-copeland 7caef9cb33 CUDA build bug fix. 2025-03-14 10:24:42 -07:00
Andrew Ho 940943268b replace MPI_C_BOOL with MPI_CXX_BOOL 2025-03-14 10:06:57 -07:00
Andrew Ho bbdcb2a70f Merge branch 'master' into external-macros 2025-03-14 08:52:52 -07:00
Andrew Ho 0790d37990 Merge branch 'master' into fix-4732 2025-03-14 08:52:42 -07:00
Brendan Keith cfe9f14900 Merge branch 'master' into VectorDiffusionIntegrator 2025-03-14 11:05:34 -04:00
Brendan Keith cd0b3e23ee revert ex40.cpp 2025-03-14 11:04:08 -04:00
adam-sim-dev a2c2292ff2 Merge branch 'master' into ppr 2025-03-14 11:49:55 +08:00
Mark L. Stowell 7c7aed6764 Merge branch 'master' into pyramid-dev 2025-03-13 17:15:10 -07:00
Andrew Ho 1fc980db10 Change MPI_INTEGER to MPI_INT
MPI_INTEGER is intended to represent Fortran integers. MPI_INT is for
C/C++, which may be different.
2025-03-13 17:14:23 -07:00
Stowell, Mark L. 36204b0668 Removing unused variable from integration rule special case 2025-03-13 15:05:10 -07:00
Stowell, Mark L. 9cf6b614d5 Fixing integration point for 1 point quadrature 2025-03-13 15:01:12 -07:00
Vladimir Z Tomov f6e8d72851 minor 2025-03-12 19:25:26 -07:00
Vladimir Z Tomov f71553f6db minor 2025-03-12 18:28:27 -07:00
Vladimir Z Tomov ee0aa9bca7 renamed variables 2025-03-12 18:12:09 -07:00
Vladimir Z Tomov c604c9a098 renamed d -> dx 2025-03-12 18:03:04 -07:00
Andrew Ho 2ced1ceb84 Re-arranged interface to UpdateNodes from a GridFunction
Added tests for 2D embedded in 3D and 3D mesh
2025-03-12 17:42:45 -07:00
Vladimir Z Tomov edf2614651 minor 2025-03-12 17:33:57 -07:00
Vladimir Z Tomov 36a687fcb1 style 2025-03-12 16:59:15 -07:00
Vladimir Z Tomov 3cf8d47182 unused function 2025-03-12 16:39:29 -07:00
Vladimir Z Tomov 6210ea712d changed some L2-H1p transitions. 2025-03-12 16:35:00 -07:00
Dylan Copeland 9fd16ab65d Another bug fix for mixed meshes. 2025-03-12 16:32:12 -07:00
Stowell, Mark L. 66a543a91a Merge branch 'pyramid-dev' of github.com:mfem/mfem into pyramid-dev 2025-03-12 16:29:39 -07:00
Stowell, Mark L. cc7c996425 Adjusting pyramid integration rules and related data 2025-03-12 16:27:58 -07:00
Stowell, Mark L. ac39347119 Reducing the size of the inline-pyramid mesh 2025-03-12 16:26:39 -07:00
Dylan Copeland b56959d59e Merge branch 'ppr' of github.com:mfem/mfem into ppr 2025-03-12 13:50:21 -07:00
Dylan Copeland 62323926b0 Bug fix for mixed meshes. 2025-03-12 13:49:45 -07:00
Andrew Ho 3b1e054c71 revert MFEM_GPU_CHECK
this change is now in a separate PR
2025-03-12 13:32:57 -07:00
Andrew Ho 0ba5eb8422 MFEM_GPU_CHECK fixes
Spelling out ::mfem enables use of this macro (and macros that call
MFEM_GPU_CHECK) in external codes.

Changed the internal variable name to something somewhat obfuscated as
before MFEM_GPU_CHECK(err) would cause compiler errors.
2025-03-12 13:25:42 -07:00
Tzanio Kolev f737d30630 Merge branch 'master' into reduction 2025-03-12 13:14:03 -07:00
Tzanio Kolev b6a0ef770a Merge branch 'master' into issue_4403 2025-03-12 13:13:53 -07:00
Tzanio Kolev 70557e1f14 Merge branch 'master' into VectorDiffusionIntegrator 2025-03-12 13:13:47 -07:00
Andrew Ho e58c14da9c ensure that hypre cmake vars are correctly set 2025-03-12 13:13:27 -07:00
Tzanio Kolev 080ee85544 Merge branch 'master' into dev-inverse-transform 2025-03-12 13:13:17 -07:00
Tzanio Kolev 780d2be2d8 Merge branch 'master' into MMA_PR 2025-03-12 13:13:07 -07:00
Tzanio Kolev b42e537101 Merge branch 'master' into pyramid-dev 2025-03-12 13:13:04 -07:00
Tzanio Kolev f99c7feb53 Merge branch 'master' into ppr 2025-03-12 13:12:51 -07:00
Tzanio Kolev dd43d1fa3c Merge branch 'master' into hybridization-gpu 2025-03-12 13:12:18 -07:00
Andrew Ho 2635018243 explicitly spell out ::mfem namespace in macro
this allows external codes to use MFEM_REGISTER_KERNELS without
injecting into the mfem namespace or having using namespace mfem;
2025-03-12 12:56:34 -07:00
Andrew Ho daa2fe7c98 forall is no longer required in gslib
was only used for test tracking of number of iterations
2025-03-12 11:50:11 -07:00
Vladimir Z Tomov 98ae71ab6d Updated PA.XO in AssemblePA 2025-03-12 11:40:31 -07:00
Andrew Ho cd799dba86 removed ::mfem in batchitrans
isn't needed here
2025-03-12 11:32:21 -07:00
Andrew Ho 00008800b8 fixed typo 2025-03-12 11:11:29 -07:00
Stowell, Mark L. d2cce0a4e5 Merge branch 'pyramid-dev' of github.com:mfem/mfem into pyramid-dev 2025-03-12 10:20:12 -07:00
Stowell, Mark L. 6c1cd2f64b Improvements to comments 2025-03-12 10:19:22 -07:00
Vladimir Z Tomov 52e5d573bf Merge branch 'tmop-delta' into tmop-periodic 2025-03-12 10:06:36 -07:00
Vladimir Z Tomov e7f8bbbd85 Merge branch 'master' into tmop-delta 2025-03-12 09:46:19 -07:00
Vladimir Z Tomov 60eb10c5d7 reviewer comments. 2025-03-12 09:45:50 -07:00
Vladimir Z Tomov 63b141992d minor 2025-03-12 09:28:38 -07:00
Jan Nikl 3086f877c2 Changed extern bdr constr integs to std::vector. 2025-03-11 22:58:13 -07:00
Jan Nikl c35eee7742 Reverted the changes of external bdr constraint integrators. 2025-03-11 22:23:51 -07:00
Tzanio Kolev 56bd1da125 Updated CHANGELOG 2025-03-11 18:15:29 -07:00
Tzanio Kolev dc5e0b0c63 Merge branch 'master' into mfem-4.8-dev 2025-03-11 18:09:34 -07:00
dylan-copeland 64688f369f CHANGELOG 2025-03-11 17:13:02 -07:00
Vladimir Z Tomov e46c3929d1 periodic sample runs and 3D mesh. 2025-03-11 16:01:53 -07:00
Tzanio Kolev fc53b03b5c Merge branch 'master' into mfem-4.8-dev
Conflicts:
	CHANGELOG
2025-03-11 14:59:24 -07:00
Andrew Ho b938d99486 revert change made for tracking step count 2025-03-11 12:19:37 -07:00
Vladimir Z Tomov 859f15088c aborted periodic fitting, fixed the adaptive limiting. 2025-03-11 10:45:10 -07:00
Vladimir Z Tomov 53c1a9de7d Periodic PA. 2025-03-10 10:55:45 -07:00
adam-sim-dev 3dae59b3df Merge branch 'master' into ppr 2025-03-10 16:48:20 +08:00
Dylan Copeland f97b4339d8 Merge branch 'master' of github.com:mfem/mfem into ppr 2025-03-06 20:37:56 -08:00
Dylan Copeland fc63c5e90b Documentation and some simplifications. 2025-03-06 20:37:44 -08:00
Jan Nikl 9212cd91d3 Merge pull request #4731 from mfem/issue_4403-glvis
Fixes for GLVis support of RT/ND_R1/2D elements.
2025-03-06 16:01:09 -08:00
Mittal, Ketan 3aed767737 update findpts miniapp for new interface, and set want_transform to true by default in PRefineAndUpdate 2025-03-06 14:19:37 -08:00
Jan Nikl 0354670dab Cleaned decoding of dofs in GridFunction::ProjectVectorFieldOn. 2025-03-06 02:24:28 -08:00
Jan Nikl dea5c1ef15 Fixed Mesh::SetCurvature() to update vertices when space dimensions change. 2025-03-05 19:21:18 -08:00
Ketan Mittal d7f3fde747 Merge branch 'master' into tmop-delta 2025-03-05 09:43:30 -06:00
Brendan Keith 87e9aacde3 Merge branch 'master' into VectorDiffusionIntegrator 2025-03-03 20:46:28 -05:00
Stowell, Mark L. 97b2561289 Adding comments with paper citations to new basis function classes 2025-03-03 15:55:22 -08:00
Andrew Ho 98b1a68c59 Merge branch 'master' into dev-inverse-transform 2025-03-03 15:11:23 -08:00
Andrew Ho cba0b592ca don't optionally track num steps in gslib
this has extra cost even when not used
2025-03-03 15:09:30 -08:00
adam-sim-dev b5ad6241e2 Merge branch 'master' into pyramid-dev 2025-03-03 03:22:22 +08:00
Jan Nikl 06e6cdc94d Updated GridFunction::ProjectVectorFieldOn() for RT/ND_R1/2D elements. 2025-03-01 12:16:07 -08:00
Jan Nikl c1d0e50948 Revert "Modify examples 31 and 32 to produce surface meshes"
This reverts commit af30acae94.
2025-03-01 11:58:39 -08:00
Dylan Copeland 4153ae8584 Made UpdatePRef a protected function. 2025-02-28 09:51:39 -08:00
Vladimir Z Tomov e024892d8e abort InterpolatorFP for periodic meshes. 2025-02-27 18:21:50 -08:00
Andrew Ho c633d5bf0e fixed name overloading and use of block_id 2025-02-27 15:52:09 -08:00
Andrew Ho 83efa2e336 use integral_constant to sneak a host-only constexpr function call
into device code
2025-02-27 15:45:58 -08:00
Andrew Ho 416ed22555 changed to use a fixed max buffer size, removed dynamic shared memory forall 2025-02-27 15:18:35 -08:00
Vladimir Z Tomov 88be18f03c Periodic advective adaptivity. 2025-02-27 13:22:41 -08:00
Mittal, Ketan b2a50919b9 remove numbering from comments in tmop miniapps 2025-02-27 10:45:34 -08:00
Mittal, Ketan edd8bd98f0 Fix ComputeMinDet for variable order in TMOP 2025-02-27 09:40:18 -08:00
adam-sim-dev add0e99d9d Merge branch 'master' into ppr 2025-02-27 22:27:34 +08:00
Andrew Ho 67850da1f7 inline static internal device functions 2025-02-26 19:23:55 -08:00
adam-sim-dev 57e99ba620 Merge branch 'master' into ppr 2025-02-27 08:38:32 +08:00
Dylan Copeland 8bb331cefa Check for variable-order space in ElementRestriction. 2025-02-26 16:11:16 -08:00
Vladimir Z Tomov 92fa66c5d4 restored HR-adapt and FD for non-periodic. 2025-02-26 16:02:34 -08:00
Vladimir Z Tomov f671f7c453 restored surface fitting for non-periodic meshes 2025-02-26 13:56:09 -08:00
Andrew Ho a9ebb665f7 merged reduction cpu and gpu unit tests 2025-02-26 12:39:41 -08:00
Andrew Ho 051fc969ab change reduction API
reducer.join -> reducer.Join
reducer.init_val -> reducer.SetInitialValue

Changed the default reducers Join and SetInitialValue to static member
functions, but still allow calling these as member functions for LpReducer
2025-02-26 12:39:29 -08:00
Andrew Ho 253ebf4e2f fix compiler error in Device which also defines the symbol err 2025-02-26 12:34:57 -08:00
Andrew Ho 5a65c4ee58 Merge branch 'master' into reduction 2025-02-26 10:50:46 -08:00
Stowell, Mark L. acb01b6e29 Fix for negative Jacobians 2025-02-25 11:44:13 -08:00
Andrew Ho 52c1a3c0b8 fixed element dof indexing 2025-02-24 12:16:38 -08:00
Andrew Ho b360a4e68b Merge branch 'master' into dev-inverse-transform 2025-02-24 07:03:05 -08:00
Andrew Ho 83f553f4a9 fixed non-power of 2 team sizes
these fail during the tree reduction steps
2025-02-24 02:14:44 -08:00
Andrew Ho 5f4d49691b added tests for batch inverse element transform 2025-02-23 23:54:21 -08:00
adam-sim-dev 632c6e34e1 Merge branch 'master' into ppr 2025-02-23 08:34:32 +08:00
Vladimir Z Tomov 012036d007 Periodic limiting. 2025-02-21 17:35:58 -08:00
Andrew Ho a7676c6443 Merge branch 'master' into dev-inverse-transform 2025-02-21 09:12:10 -08:00
Andrew Ho bcde2ec4f1 fixed step counting in findpts for some cases 2025-02-21 06:51:50 -08:00
Rick 19507830f0 Add [RT|ND]_R[2|3]D to Doxygen for FiniteElementCollection::New
As suggested by @paul-hilscher
2025-02-21 10:18:14 +01:00
adam-sim-dev 777efb0582 Merge branch 'master' into ppr 2025-02-21 16:53:50 +08:00
Vladimir Z Tomov 8f42d760fc initial working version for periodic meshes. 2025-02-20 22:38:38 -08:00
Andrew Ho cdbc8c710c implemented iteration count tracking in findpts and batch element transformations 2025-02-20 18:57:47 -08:00
Andrew Ho 76525c8ce3 working towards adding diagnostic for number of steps required in gslib
I think MPI support still needs to be added?
2025-02-20 13:27:46 -08:00
Will Pazner f6d100b119 Merge remote-tracking branch 'origin/master' into hybridization-gpu 2025-02-20 12:18:05 -08:00
Will Pazner f7a5a79d34 Add missing include 2025-02-20 10:06:45 -08:00
Stowell, Mark L. 63022c131a Lowering integration rule order 2025-02-20 09:48:38 -08:00
Will Pazner 53365490b7 Merge remote-tracking branch 'origin/master' into hybridization-gpu
# Conflicts:
#	tests/unit/fem/test_assembly_levels.cpp
2025-02-20 09:12:41 -08:00
Andrew Ho c75c5f2ea8 added optimization for guesses co-located at dofs
this matches findpts, seems like a worthwhile performance upgrade in
most situations over center
2025-02-20 05:15:46 -08:00
Vladimir Z Tomov c97450809d Switched TMOPNewtonSolver::x_0 to a GridFunction. 2025-02-19 22:08:37 -08:00
Andrew Ho 34ea1d5b07 fixed batch edgescan
Adding a native nodal DOFs init guess type would work well for some elements
2025-02-19 20:58:58 -08:00
Andrew Ho d1f560d652 implemented batch edgescan
Could have a smarter traversal order
2025-02-19 17:29:40 -08:00
Dylan Copeland cbe0582fe3 Merge branch 'ppr' of github.com:mfem/mfem into ppr 2025-02-19 17:01:22 -08:00
Dylan Copeland 7fe4c3c199 Bug fixes. 2025-02-19 17:01:09 -08:00
adam-sim-dev 779c17c86d Merge branch 'master' into ppr 2025-02-20 08:27:38 +08:00
Andrew Ho 8048e1d433 return the element result from InvTNewtonSolver
this allows an EdgeScan to re-use the same solver code
2025-02-19 15:10:22 -08:00
Stowell, Mark L. ced77961a5 Removing unnecessary argument assignments 2025-02-19 11:18:39 -08:00
Stowell, Mark L. 68eb708311 Hiding pyramid option in display-basis 2025-02-19 10:59:41 -08:00
Andrew Ho 5144997003 Merge branch 'master' into dev-inverse-transform 2025-02-19 10:32:25 -08:00
Andrew Ho c322d7cbb0 using max_q1d speeds up physnode initial guess finder 2025-02-19 01:59:17 -08:00
Andrew Ho baa2c246f9 changed newtonsolve parallelization strategy
uses a lot less shared memory, hopefully performs better. Need to tune
max_team_x.
2025-02-18 17:57:47 -08:00
adam-sim-dev 1a32b51338 Merge branch 'master' into ppr 2025-02-19 08:36:55 +08:00
Stowell, Mark L. ca1915983d Removing old commented special cases for pyramids 2025-02-18 15:38:12 -08:00
Stowell, Mark L. d1cbdf0766 Removing commented code 2025-02-18 15:35:45 -08:00
Stowell, Mark L. ba63e32ded Reverting makefile changes 2025-02-18 15:30:12 -08:00
Stowell, Mark L. 82e260ab3a Cleaning up argument alignments 2025-02-18 15:15:13 -08:00
Andrew Ho 337dd7c274 changed loop order
try to improve memory access pattern
2025-02-15 08:26:58 -08:00
Andrew Ho 6f3ecfe129 fixed 3d cube linsolve 2025-02-14 20:27:40 -08:00
Andrew Ho 7514594c32 fixed documentation 2025-02-14 12:58:54 -08:00
Andrew Ho ccdaa48f69 batch inverse element transformation should now work for all square and cube element geometries
assumes that evaluating at GLL nodes is equivalent, which I think for
NURBS meshes is not true
2025-02-14 12:32:09 -08:00
Andrew Ho 5b3cbad98c Merge branch 'master' into dev-inverse-transform 2025-02-14 09:01:57 -08:00
Andrew Ho 490b60c272 re-arrangement to make doxygen happy 2025-02-14 00:01:53 -08:00
Andrew Ho 3b5c10c1b3 documentation and formatting 2025-02-13 20:00:27 -08:00
Andrew Ho 9aaa7f13a3 fixed -Wall -Werror for some config options 2025-02-13 16:46:51 -08:00
adam-sim-dev 71f45d285c Merge branch 'master' into ppr 2025-02-14 08:40:57 +08:00
Brendan Keith 8c1dd9faca _r 2025-02-13 19:00:52 -05:00
Brendan Keith 4fea4ee7f6 support for VectorDiffusionIntegrator on surface meshes 2025-02-13 18:05:17 -05:00
Andrew Ho 07605d3097 fixed compile bugs wtih various configurations 2025-02-13 14:58:55 -08:00
Andrew Ho 02aed483e6 fixed newtonsolver 2025-02-13 09:20:16 -08:00
Andrew Ho facb0b5434 implemented newtonproject solves 2025-02-12 22:25:00 -08:00
Andrew Ho cb32b5ee8e implemented square element newton project solve 2025-02-12 21:00:46 -08:00
Vladimir Z Tomov 987203005d minor 2025-02-12 16:06:17 -08:00
Andrew Ho cd50b6b806 setup infrastructure for calling newtonsolve
still need to implement remaining specializations
2025-02-12 13:40:44 -08:00
Andrew Ho 74ef6490a7 implemented elementproject for segments 2025-02-12 13:09:14 -08:00
Andrew Ho f963df773c implemented raw newton solve for segments 2025-02-12 12:52:33 -08:00
Andrew Ho 31786ff0e0 enabled specifying 1D forall thread block size
left as an optional param
2025-02-12 11:15:41 -08:00
Vladimir Z Tomov 7275a2fbab Merge branch 'master' into tmop-delta 2025-02-11 15:59:16 -08:00
Vladimir Z Tomov b13fb27f7e minor 2025-02-11 15:57:49 -08:00
Vladimir Z Tomov e55d0882c3 Reverted testing code. 2025-02-11 15:52:22 -08:00
Dylan Copeland 4d21ab6a4d Type fix. 2025-02-11 15:49:21 -08:00
Vladimir Z Tomov 34fb676fd5 reverted testing code. 2025-02-11 15:42:33 -08:00
Dylan Copeland a55d495f1f Verify that space can be p-refined. 2025-02-11 15:39:51 -08:00
Vladimir Z Tomov f6efa8ea54 Fixed the FD displacements. 2025-02-11 15:24:43 -08:00
Dylan Copeland f0c0994a4e Unused variables. 2025-02-11 13:35:37 -08:00
Dylan Copeland e192397308 Merge branch 'master' of github.com:mfem/mfem into ppr 2025-02-11 13:13:22 -08:00
Dylan Copeland f519d59644 More documentation. Simplified ProlongateToMaxOrder and GridFunction updates. 2025-02-11 13:12:57 -08:00
Andrew Ho 3c17ed88c5 fixed some bugs
switched qaud1d type with basis type
fixed computing coordinate interpolation not summing basis values
2025-02-11 11:56:25 -08:00
Andrew Ho 159f8a1404 changed copyright years 2025-02-11 11:12:26 -08:00
Andrew Ho 0a754a78b9 implemented tensor basis optimization 2025-02-11 11:11:34 -08:00
Vladimir Z Tomov 3cf27e9243 It's better to separated x_0 and x_limiting. 2025-02-11 10:57:09 -08:00
Vladimir Z Tomov a3cf3f4bb2 Full support for solving displacements, including PA, with old interface. 2025-02-11 09:31:51 -08:00
Andrew Ho 35f508ca8c fixed bugs in batch closestphysnode kernel
still could perform better...
2025-02-11 00:31:06 -08:00
adam-sim-dev a9f4652335 Merge branch 'master' into ppr 2025-02-11 14:23:22 +08:00
Tzanio Kolev a2de8287c7 Merge branch 'master' into mfem-4.8-dev 2025-02-10 19:10:35 -08:00
Andrew Ho acea00376b fixed serial reduction save index
should be the thread x id, not just i
2025-02-10 12:36:27 -08:00
Andrew Ho 13fed2e691 implemented PhysNodeFinder for tensor elements 2025-02-10 12:25:19 -08:00
Andrew Ho 1332ba5ee7 stride_sdim needs all element dofs, not just ndof1d 2025-02-10 12:17:14 -08:00
Andrew Ho a252286472 implemented ClosestPhysNodeImpl for segments in SDim 2025-02-10 12:10:31 -08:00
Andrew Ho 8bab223de4 switched init condition kernels to template on geometry instead of dim 2025-02-10 11:52:33 -08:00
Andrew Ho 96392055d6 think I want separate kernels per geometry shape 2025-02-10 10:13:53 -08:00
Andrew Ho 36f1b769c5 specialize on device vs. non-device 2025-02-09 08:36:30 -08:00
Andrew Ho 3e074b3c7e implemented refinement points for batch itransform 2025-02-09 08:31:24 -08:00
Andrew Ho d8773cad73 initialize batch inverse transformation kernels in setup
this is guaranteed to be called before the kernels are used
2025-02-09 07:44:14 -08:00
Andrew Ho 773606139c Merge remote-tracking branch 'base/master' into dev-inverse-transform 2025-02-09 07:35:58 -08:00
Andrew Ho b1f7cb2766 moved batch inverse transform implementation into separate cpp file 2025-02-09 07:34:37 -08:00
Vladimir Z Tomov df47dd65c1 TMOP_Integrator defined w.r.t. displacements. 2025-02-08 09:32:41 -08:00
Rick af30acae94 Modify examples 31 and 32 to produce surface meshes
In the examples where ND_R1D / ND_R2D spaces are used, make sure
that the mesh that is being written for glvis visualization is
embedded in 3D space.
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch issue_4403
# Your branch is up to date with 'origin/issue_4403'.
#
# Changes to be committed:
#	modified:   examples/ex31.cpp
#	modified:   examples/ex31p.cpp
#	modified:   examples/ex32p.cpp
#
# Untracked files:
#	.vscode/
#
2025-02-08 15:56:31 +01:00
Andrew Ho c102d5ecf6 use HUGE_VAL in findpts_local_2
CUDA requires --expt-relaxed-constexpr to call host only constexpr
functions from device code
2025-02-08 01:06:11 -08:00
Will Pazner b1cf2dfe9a Update copyright year 2025-02-07 16:32:57 -08:00
bslazarov c0479e88fc solver interface 2025-02-07 14:47:19 -08:00
Will Pazner e797278779 Merge remote-tracking branch 'origin/master' into hybridization-gpu
# Conflicts:
#	fem/integ/bilininteg_mass_pa.cpp
#	tests/unit/fem/test_assembly_levels.cpp
2025-02-07 14:24:06 -08:00
Andrew Ho 422a752600 Working on GPU-callable code for implementing inverse element transforms 2025-02-07 01:03:27 -08:00
Andrew Ho 2ab1ae4ff6 changed EdgeScan to repeat solve for multiple initial guesses
NewtonElementProject is actually really good, make use of it
2025-02-06 22:01:59 -08:00
Vladimir Z Tomov b5826ff9d5 pass initial positions to integrators. 2025-02-06 16:45:09 -08:00
Stowell, Mark L. a7dcc17d63 Updating copyright dates to 2025 2025-02-06 14:52:52 -08:00
Stowell, Mark L. f5b73dbaeb Merge remote-tracking branch 'origin/master' into pyramid-dev 2025-02-06 14:48:03 -08:00
Andrew Ho df3be0125c working on finding good guesses which can make progress 2025-02-06 12:14:03 -08:00
Vladimir Z Tomov 10c6c8ad22 Simpler computation of x_loc. 2025-02-05 14:35:14 -08:00
Dylan Copeland 4c62788e60 Merge branch 'master' of github.com:mfem/mfem into ppr 2025-02-04 16:18:17 -08:00
Andrew Ho ab36b3caae style 2025-02-04 15:33:23 -08:00
Andrew Ho be659ae4b6 implemented EdgeScan guesser for inverse transforms 2025-02-04 15:32:18 -08:00
Andrew Ho 5ceda33e57 implemented edgescan
fixed hash finalization infinite recursion
2025-02-04 15:13:29 -08:00
Tzanio Kolev 2189509521 Merge branch 'master' into MMA_PR 2025-02-04 14:53:58 -08:00
Tzanio Kolev 2ceee164c5 Merge branch 'master' into pyramid-dev 2025-02-04 14:53:54 -08:00
Tzanio Kolev 1f3790ba2c Merge branch 'master' into reduction 2025-02-04 14:53:21 -08:00
Andrew Ho 38aaa74b52 added API for retrieving underlying array in Poly_1D 2025-02-04 11:49:26 -08:00
Andrew Ho f6e0903d60 changed how bases_container stores the cache 2025-02-04 11:16:52 -08:00
Tzanio Kolev a077bb3a43 Update CHANGELOG 2025-02-03 11:11:03 -08:00
Tzanio Kolev 828dee43f6 Merge branch 'master' into mfem-4.8-dev 2025-02-03 11:04:57 -08:00
Tzanio Kolev 907213b3aa Initial CHANGELOG for mfem-4.8 2025-02-03 09:51:49 -08:00
Tzanio Kolev 3eae5645ae Initial changes for mfem-4.8 2025-02-03 09:24:31 -08:00
Andrew Ho 2f6c1d9b6a added helpers for evauating basis functions on device 2025-01-31 16:29:58 -08:00
Andrew Ho 7717cc5e6c cleanup 2025-01-31 16:17:29 -08:00
Andrew Ho a6da18db40 changed GetPoints so you can request device or host pointers 2025-01-31 15:18:35 -08:00
Andrew Ho 4c491ae4a7 Added a helper for constructing non-cryptographic hashes for use in
hash tables
2025-01-31 15:08:51 -08:00
Stowell, Mark L. 142528c55d Removing outdated comments 2025-01-31 09:34:47 -08:00
Andrew Ho 21590c1e60 testing out increased boundary seed points in 2D
need to add configurable options for seeding, implement in 3D
2025-01-27 14:41:26 -08:00
Andrew Ho c07d124c4c Merge branch 'master' into reduction 2025-01-24 11:17:17 -08:00
Will Pazner cf69527e0a Merge remote-tracking branch 'origin/master' into par-sub-transfer-fix 2025-01-23 09:54:02 -08:00
Will Pazner 945b88c1a8 Restrict (Par)TransferMap::Transfer to grid functions 2025-01-23 09:52:56 -08:00
adam-sim-dev e186ca4e30 Merge branch 'master' into ppr 2025-01-23 21:37:14 +08:00
Dylan Copeland add7281658 Changed R to interpolate minimum order among local neighboring elements. 2025-01-22 18:09:27 -08:00
Andrew Ho 98c6b0e32f all indices should be ints 2025-01-22 17:59:58 -08:00
Andrew Ho 506c34719a should be signed int 2025-01-22 17:03:17 -08:00
Dylan Copeland 2a035e92d2 Merge branch 'master' of github.com:mfem/mfem into ppr 2025-01-22 15:12:49 -08:00
Andrew Ho eccb30bdfe unused variable warning 2025-01-22 14:24:34 -08:00
Andrew Ho 160a52a55e added reduction unit tests, fixed some bugs in reducers 2025-01-22 11:34:59 -08:00
Andrew Ho 83440de8cd also split mesh on non-tensor basis 2025-01-21 15:53:56 -08:00
Andrew Ho 26ba53a52e More clearly ensure all bits are set, especially for signed integral types. 2025-01-21 11:54:54 -08:00
Tzanio Kolev 2cde33b8dd Merge branch 'master' into pyramid-dev 2025-01-21 11:47:30 -08:00
Will Pazner e2815c1864 Bug fix and improvements to (Par)TransferMap
- Fixes a bug in ParSubMesh-to-ParSubMesh ParTransferMap.
   - Implement submesh-to-submesh transfer using composition of transfer maps.
- Allow creation of transfer maps from finite element spaces (rather than grid functions).
- Allow transfer of L-vectors (rather than just grid functions).
2025-01-16 10:54:48 -08:00
Andrew Ho 0f1880a9da added documentation 2025-01-14 10:54:57 -08:00
Andrew Ho 25cfc0991d missing header includes 2025-01-13 14:32:14 -08:00
Andrew Ho cbd035e06d use compiler min/max macros instead of manual values 2025-01-13 14:08:23 -08:00
Andrew Ho 242703e266 Implemented all vector norms to run on GPU
changed to void ptr for dynamic shared memory to reduce templates
2025-01-09 23:26:12 -08:00
Andrew Ho 7ced559b0d GPU accelerate L1 and L-infinity norms 2025-01-09 17:13:08 -08:00
Andrew Ho 06d1cd025f Cleanup CPU code paths for Vector reductions 2025-01-09 16:47:09 -08:00
Andrew Ho 40f8216355 missing headers 2025-01-09 16:30:03 -08:00
Andrew Ho fc8ee486b6 allow user to specify a kernel label (for RAJA) 2025-01-09 15:12:10 -08:00
Andrew Ho 3dae2a01dc added a slightly more descriptive label for the RAJA kernels 2025-01-09 15:02:32 -08:00
Andrew Ho 3b5068ca79 missing header, formatting 2025-01-09 14:49:30 -08:00
Andrew Ho 5b145be17d implemented forall_smem for RAJA GPU backends 2025-01-09 14:17:56 -08:00
Andrew Ho 76f382eba6 make sure forall_smem cpu backup works on cuda and hip 2025-01-09 05:58:17 -08:00
Andrew Ho 6f9cbed618 fixed reduce accidentally always running the CPU backup 2025-01-08 20:26:45 -08:00
Andrew Ho aab40b23d2 implemented reduction framework
still need to add RAJA implementation for dynamic shared memory
2025-01-08 19:02:12 -08:00
adam-sim-dev b211337360 Merge branch 'master' into pyramid-dev 2025-01-09 06:10:56 +08:00
Stowell, Mark L. 6c40c5b643 Updating comment related to pyramid integration rules 2025-01-07 11:19:30 -08:00
Stowell, Mark L. 1aa9e65cfb Cleanup of experimental class 2025-01-07 11:11:04 -08:00
Brendan Keith e05523f36e Merge branch 'master' into pyramid-dev 2024-12-30 19:09:26 -05:00
Arturo Vargas 090cdf3135 Merge branch 'master' into hybridization-gpu 2024-12-18 10:14:59 -08:00
Arturo Vargas dba53a19a2 Merge branch 'master' into hybridization-gpu 2024-12-12 14:58:03 -08:00
Dylan Copeland 7295d1414f Refactor using GetNumBorderDofs. 2024-12-12 13:00:36 -08:00
Dylan Copeland de1d7406ed Merge branch 'master' of github.com:mfem/mfem into ppr 2024-12-12 10:30:03 -08:00
Dylan Copeland 6c7679f7c4 Shadowed variable 2024-12-12 10:29:49 -08:00
Dylan Copeland 9139e09a30 Projection option for hpref_serial. 2024-12-12 09:55:34 -08:00
Dylan Copeland c2bde81c14 CI error 2024-12-11 16:05:51 -08:00
Dylan Copeland c6d30c5c82 Limiting non-boolean R to H1 spaces, and fixing a bug. 2024-12-11 15:50:58 -08:00
Dylan Copeland 47f6ca5693 Fix CI error. 2024-12-10 21:54:41 -08:00
Dylan Copeland 3f30c5b970 Remove unused variables. 2024-12-10 21:45:40 -08:00
Dylan Copeland 80df37426f Merge branch 'master' of github.com:mfem/mfem into ppr 2024-12-10 21:29:51 -08:00
Dylan Copeland 36be48efa9 Minor clean up 2024-12-10 21:11:37 -08:00
Dylan Copeland d2a94aa354 Eliminated non-local DOFs in parallel variable-order spaces. Implemented non-boolean restriction matrix Rvar. Added option to test vector coefficient projection in hpref miniapp. 2024-12-10 21:09:01 -08:00
Mark L. Stowell bb44da48dd Fixing more uninitialized data 2024-12-04 14:58:03 -08:00
Mark L. Stowell 6bec321019 Try that again... 2024-12-04 14:28:57 -08:00
Mark L. Stowell 62def1c528 Fixing compiler warning 2024-12-04 11:39:25 -08:00
Mark L. Stowell faf44555ba Merge branch 'pyramid-dev' of github.com:mfem/mfem into pyramid-dev 2024-12-04 11:22:56 -08:00
Mark L. Stowell 7d74666511 Adjusting error estimates in finite difference unit tests on pyramids 2024-12-04 11:21:25 -08:00
Mark L. Stowell 8046bb2cd6 Adding unit test for symmetry of pyramid finite elements 2024-12-04 11:20:13 -08:00
Mark L. Stowell 030e4b00d1 Fixing direction used for interior Nedelec DoFs 2024-12-04 11:17:30 -08:00
Will Pazner 6c2269d817 Merge remote-tracking branch 'origin/master' into hybridization-gpu
# Conflicts:
#	linalg/batched/native.cpp
2024-11-21 13:16:47 -08:00
adam-sim-dev d0b1f3f816 Merge branch 'master' into ppr 2024-11-13 10:22:48 +08:00
adam-sim-dev be444d4b38 Merge branch 'master' into ppr 2024-11-13 05:28:31 +08:00
Mark L. Stowell e62696f749 Merge branch 'master' into pyramid-dev 2024-11-11 22:16:41 -08:00
Mark L. Stowell 12d1dcb155 Attempting to resolve issue in cmake build on Windows 2024-11-11 21:15:43 -08:00
dylan-copeland 07d59a1843 Prolongate GridFunction for glvis output. 2024-11-11 19:20:53 -08:00
Mark L. Stowell ec5ad279e7 Fixing memory leak in unit test 2024-11-11 16:47:33 -08:00
Mark L. Stowell 522dc28e5a Loosening a unit test tolerance slightly 2024-11-11 16:47:12 -08:00
Mark L. Stowell 1c47f03aba Alternative to PR #4578 2024-11-11 16:01:58 -08:00
Mark L. Stowell 6fd0e8e50a Minor unit test clean-up 2024-11-11 15:49:54 -08:00
Mark L. Stowell 841df1a9bf Making use of new CheckZ function 2024-11-11 15:40:35 -08:00
Mark L. Stowell 0b49ea9594 Removing type ambiguity flagged by Ubuntu 2024-11-11 11:41:56 -08:00
Mark L. Stowell fba17c6579 Testing solution to type ambiguity flagged by Ubuntu runs 2024-11-11 11:02:19 -08:00
Mark L. Stowell 789f5d2e49 double -> real_t 2024-11-11 10:13:02 -08:00
Mark L. Stowell 07b543000f Fixing face dof order in H1 pyramids 2024-11-11 09:57:32 -08:00
adam-sim-dev f89aba61ca Merge branch 'master' into ppr 2024-11-11 08:45:48 +08:00
Mark L. Stowell 8cae2c313e Switching to tighter, more problem specific, tolerances in product interpolator unit tests 2024-11-09 14:40:23 -08:00
Mark L. Stowell 888651e796 Adding unit test of the curl using finite differences 2024-11-08 11:49:06 -08:00
Mark L. Stowell def1a9938d Merge remote-tracking branch 'origin/master' into pyramid-dev 2024-11-08 10:21:16 -08:00
Mark L. Stowell 7b12197e94 Replacing scattered tolerances with a centrally defined one 2024-11-08 10:16:25 -08:00
adam-sim-dev e50c543dde Merge branch 'master' into ppr 2024-11-08 08:48:45 +08:00
Will Pazner 4317659edc Use unique_ptr for MixedBilinearFormExtension 2024-11-07 13:06:26 -08:00
Will Pazner 29b78f70e9 Early return to prevent division by zero 2024-11-07 12:07:04 -08:00
Will Pazner ae9ee68c82 Fix shadowed variable warning 2024-11-07 11:35:56 -08:00
Will Pazner 7c417d7f47 Fix warning 'comparison of integer expressions of different signedness' 2024-11-07 11:30:21 -08:00
Will Pazner f22201a855 Fix shadowed variable warning 2024-11-07 11:17:53 -08:00
Will Pazner 4d5426a314 Merge remote-tracking branch 'origin/master' into hybridization-gpu
# Conflicts:
#	fem/bilinearform_ext.hpp
#	fem/bilininteg.hpp
#	linalg/batched/native.cpp
2024-11-07 10:37:38 -08:00
Dylan Copeland 6baa15afba Merge branch 'master' of github.com:mfem/mfem into ppr 2024-10-16 17:38:47 -07:00
Dylan Copeland b936a27147 offdiagonal -> off-diagonal 2024-10-16 17:38:18 -07:00
Dylan Copeland 05fef0a0d2 Documentation and minor changes. 2024-10-16 16:06:10 -07:00
Mark L. Stowell 2927be94bf Merge branch 'master' into pyramid-dev 2024-10-14 10:38:15 -04:00
Stowell, Mark L f181ceb58d Correcting polynomial space 2024-10-14 10:37:28 -04:00
Stowell, Mark L e160e5cfaf Missed one "double" -> "real_t" 2024-10-09 11:39:32 -04:00
Stowell, Mark L 5b4ae30871 Replacing "double" with "real_t" 2024-10-09 11:29:46 -04:00
Stowell, Mark L 862eb07599 Fixing shadowed variables in RT basis 2024-10-09 11:18:17 -04:00
Stowell, Mark L b4c814efd4 Fixing shadowed variables in H1 basis 2024-10-09 10:47:04 -04:00
Stowell, Mark L 8a15099f67 Fixing display of math formulae in doxygen 2024-10-09 10:37:32 -04:00
Stowell, Mark L 5272fbf7fa Removing VerifyNodal from L2 basis on pyramids 2024-10-09 09:48:39 -04:00
Stowell, Mark L 10de3a3f34 Moving internal methods to protected member functions 2024-10-09 09:48:05 -04:00
Stowell, Mark L bd49a2d320 Fixing unit test issues with scalar pyramid basis functions 2024-10-08 15:31:12 -04:00
Stowell, Mark L a0d509fed6 Adding positive pyramid basis functions to finite element collections 2024-10-08 15:30:38 -04:00
Stowell, Mark L 127867dc00 Adding H1 and L2 Positive basis functions on pyramids 2024-10-08 15:29:54 -04:00
Will Pazner 5e2bac632f Bugfix in HybridizationExtension::MultAhatInv 2024-10-02 18:07:09 -07:00
Will Pazner cba6871889 Exclude essential DOFs in HybridizationExtension::FactorElementMatrices 2024-10-02 16:37:00 -07:00
Will Pazner cf0ad68faa Fix boundary indexing in EABilinearFormExtension::GetElementMatrices 2024-10-01 11:17:48 -07:00
Dylan Copeland ef386590ad Print -> ParPrint for pmesh in hpref and ex6p. 2024-09-27 17:33:26 -07:00
Will Pazner e199da273e Essential BCs in HybridizationExtension::FactorElementMatrices 2024-09-27 14:08:49 -07:00
adam-sim-dev 5b928c919e Merge branch 'master' into ppr 2024-09-26 19:42:20 +08:00
dylan-copeland 8464b3d1e7 cmake fix 2024-09-25 21:12:04 -07:00
dylan-copeland 40f20d1da5 Fixed a memory bug. 2024-09-25 20:45:34 -07:00
dylan-copeland 7e1bccc757 Fix serial build. 2024-09-25 20:22:02 -07:00
Dylan Copeland 89b7c48cf7 Update makefile 2024-09-25 20:10:06 -07:00
Dylan Copeland 351afd3fbb Serial version of UpdatePRef in FiniteElementSpace (now virtual), with a serial miniapp. 2024-09-25 19:31:01 -07:00
Dylan Copeland 68abff7ce2 Merge branch 'master' of github.com:mfem/mfem into ppr 2024-09-25 12:23:32 -07:00
Dylan Copeland 4dc9708669 Address some PR comments. 2024-09-25 12:20:55 -07:00
dylan-copeland a2f32603f8 Simplified interface for p-refinement. 2024-09-20 17:39:21 -07:00
dylan-copeland 8532980ef8 Changed PTh to shared_ptr. 2024-09-20 15:12:08 -07:00
dylan-copeland c7f632e548 Merge branch 'master' of github.com:mfem/mfem into ppr 2024-09-18 10:04:58 -07:00
dylan-copeland c99bd6515e Added a function to p-refine an array of elements. 2024-09-18 10:04:38 -07:00
Stowell, Mark L c659a98754 Implementing Bernstein basis for H1 pyramids 2024-09-18 10:47:13 -04:00
Stowell, Mark L 05ef2ea97d Adding derivatives of binomials without assumptions on the relation between the monomials 2024-09-18 10:46:42 -04:00
Stowell, Mark L 350e235920 Change limiting values on the gradients of pyramid affine parameters 2024-09-18 10:45:31 -04:00
Will Pazner a79aaeac07 Minor Doxygen fix 2024-09-12 13:32:03 -07:00
Stowell, Mark L 8ab27a16cc Removing unnecessary vector size 2024-09-11 16:38:41 -04:00
Stowell, Mark L 04691da19a Further memory leak testing 2024-09-11 12:34:22 -04:00
Stowell, Mark L 89fa1e05b2 Test of memory leak issue 2024-09-11 12:08:02 -04:00
Stowell, Mark L 0685ed9e49 Reverting null test 2024-09-10 19:52:32 -04:00
Stowell, Mark L 00de67dd3a Testing memory leak work-around 2024-09-10 13:22:59 -04:00
linke2 c8d39a3090 Code Style 2024-09-09 14:45:46 -07:00
linke2 2e4bb12589 Updated MMA variable 2024-09-09 13:16:56 -07:00
Will Pazner 0a683fdcfd Add LocalMemory template allowing zero-sized data 2024-09-09 13:00:53 -07:00
Will Pazner 7e1d57f8b1 Add specializations for HybridizationExtension::FactorElementMatrices 2024-09-09 12:46:12 -07:00
linke2 38abb9bee4 Updated to real_t 2024-09-09 10:07:41 -07:00
Will Pazner 455d279c3d Add HybridizationExtension::FactorElementMatrices
This changes the element matrix factorization kernel to use local memory instead
of global memory
2024-09-09 10:07:20 -07:00
linke2 e0569296e7 fix MMA test 2024-09-09 09:47:03 -07:00
linke2 0b6881390f remove unused variable in test 2024-09-09 09:46:25 -07:00
Will Pazner 3655d08ca3 Make HybridizationExtension::DofType public
This is a requirement for host-device lambdas with nvcc
2024-09-07 15:00:22 -07:00
Will Pazner 35041601d4 In hybridization, use i-dof and b-dof Schur complement on device 2024-09-07 13:52:20 -07:00
Will Pazner 4caffa2ebe Move some dense linear algebra functionality to host-device functions 2024-09-07 09:27:31 -07:00
Stowell, Mark L 0bea92acf0 Improving comments 2024-09-06 15:53:53 -07:00
Stowell, Mark L 0ba784d08f Resolving limiting case problems found with fixed order comparison 2024-09-06 15:30:00 -07:00
Stowell, Mark L 8b41801be5 Using new function space where appropriate 2024-09-06 15:28:56 -07:00
Stowell, Mark L c622f40713 Adding new function space for rational polynomials 2024-09-06 15:27:37 -07:00
Stowell, Mark L d79e2af2e3 Adding unit test to compare fixed and arbitrary order pyramid finite elements 2024-09-06 15:26:11 -07:00
Mark L. Stowell 7b5a0a42d1 Merge branch 'master' into pyramid-dev 2024-09-06 09:18:14 -07:00
Stowell, Mark L f89f0eaad4 Fixing quad face dof order in pyramid finite elements 2024-09-05 16:10:49 -07:00
Stowell, Mark L 2758154175 Updating weak operator unit tests 2024-09-05 13:39:03 -07:00
Will Pazner 0aec7595bd AtomicAdd in EABilinearFormExtension::GetElementMatrices 2024-09-05 12:03:34 -07:00
Will Pazner c5064e1884 Handle case of no domain integrators in EABilinearFormExtension::Assemble 2024-09-05 12:03:27 -07:00
Will Pazner ce48acbf30 Temporary: use ib-dof Schur complement LU factorization 2024-09-05 12:02:58 -07:00
Will Pazner 25d80486e0 Hybridization extension fixes and improvements
- Add HybridizationExtension::Reset to clear the element matrices
- Distinguish between interior and boundary free DOFs
2024-09-05 12:02:07 -07:00
linke2 9a1baddd61 fixed shadowed variables 2024-09-04 11:58:25 -07:00
linke2 01eab3864d removed subcommunicator 2024-09-04 11:28:49 -07:00
linke2 455c52f520 MMA Final 2024-09-03 16:42:04 -07:00
Dylan Copeland 95e00dec12 Merge branch 'master' of github.com:mfem/mfem into ppr 2024-08-31 22:34:15 -07:00
Dylan Copeland e27726337c Minor improvements. 2024-08-31 22:33:48 -07:00
Stowell, Mark L 0b82b5b4cb Adding skipped unit tests 2024-08-27 14:33:14 -07:00
Stowell, Mark L a501fd9571 Adding unit test for CurlGrad=0 and DivCurl=0 2024-08-27 11:10:35 -07:00
Stowell, Mark L 6933b983a6 Adding pyramids to Calc*Shape unit tests 2024-08-26 09:59:09 -07:00
Stowell, Mark L ab024fa6b1 Updating copyright statement in new unit test 2024-08-20 09:23:20 -07:00
Stowell, Mark L b0efc18e0f Adding 2 sample pyramid meshes 2024-08-20 09:18:28 -07:00
Stowell, Mark L 4d40cc2152 Removing unnecessary files 2024-08-20 09:13:41 -07:00
Stowell, Mark L 3d9d7a9915 Missed one test code 2024-08-20 08:58:28 -07:00
Stowell, Mark L e19a00f18a Removing test codes 2024-08-20 08:57:45 -07:00
Stowell, Mark L 9548a87a66 Merge remote-tracking branch 'origin/master' into pyramid-dev 2024-08-20 08:37:17 -07:00
dylan-copeland a589ae90b9 Restore communication of all element orders. 2024-08-20 07:54:20 -07:00
dylan-copeland 47bbba2f5c Fix ubuntu build. 2024-08-19 22:56:14 -07:00
dylan-copeland 3553f91f44 Fix mac build. 2024-08-19 22:51:17 -07:00
Dylan Copeland 45f32fb9c6 Eliminated communication of orders greater than base order. 2024-08-19 22:44:06 -07:00
Dylan Copeland 41807f5444 Minor fixes. 2024-08-19 20:41:45 -07:00
dylan-copeland 659559d43b Updating ex6p. 2024-08-19 19:53:51 -07:00
Dylan Copeland c00f044824 Documented and cleaned up various things. Added the new miniapp hpref. 2024-08-19 17:24:52 -07:00
Stowell, Mark L 238cd67d5e Increasing integration order used with pyramids 2024-08-19 17:17:29 -07:00
Stowell, Mark L 96d9068077 Adjusting interpolation vectors for ND and RT pyramids 2024-08-19 17:16:01 -07:00
Stowell, Mark L e91f9b6e0c Augmenting interpolation vectors for ND pyramids 2024-08-19 16:50:28 -07:00
Stowell, Mark L 81f990cc8e Adding unit test for finite element compatibility 2024-08-19 16:28:28 -07:00
Dylan Copeland 70fca5a453 Minor fixes. 2024-08-16 21:26:23 -07:00
dylan-copeland 00a7f10b95 Cleaning up and fixing some minor issues. 2024-08-16 20:28:38 -07:00
Dylan Copeland a4c097f47c Merge branch 'master' of github.com:mfem/mfem into ppr 2024-08-16 18:15:41 -07:00
Dylan Copeland 7198ad8872 Eliminated some discontinuities by fixing a bug in PackDofVar, calling VariableOrderMinimumRule before applying h-refinement constraints, and propagating minimum order from master to slave entities. Added H1 continuity check in unit tests. 2024-08-16 16:40:46 -07:00
Will Pazner 52b00a08a9 Update to new batched linear algebra interface 2024-08-16 12:29:17 -07:00
Will Pazner 9a6048658b Merge remote-tracking branch 'origin/master' into hybridization-gpu
# Conflicts:
#	fem/hybridization.cpp
#	fem/hybridization.hpp
#	linalg/CMakeLists.txt
#	linalg/densemat.cpp
#	linalg/densemat.hpp
2024-08-16 12:29:07 -07:00
Rick Budé d16dae3366 Format source files 2024-07-25 16:22:06 +02:00
Rick Budé ee84d9e365 Load ND_R<1,2>D and RT_R<1,2>D spaces correctly
Resolves a crash when attempting to load a gridfunction from file,
expressed on ND_R1D, ND_R2D, RT_R1D and RT_R2D spaces. See
issue #4403
2024-07-23 17:14:42 +02:00
Will Pazner a0d2d4ab3d Implement HybridizationExtension::AssembleBdrMatrix
Note: this is slow, it is not the recommended way to use the
HybridizationExtension. Batched Element Assembly should be used instead.
2024-06-24 10:53:37 -07:00
Will Pazner 0a08801004 Remove unused variable 2024-06-24 10:33:57 -07:00
Will Pazner 4af565819e Adjustments to matrix assembly interface in HybridizationExtension 2024-06-24 10:33:53 -07:00
Will Pazner bcd5ef92c6 Add batched.{cpp,hpp} to CMakeLists.txt 2024-06-19 16:53:10 -07:00
Will Pazner bb271475ea GPU hybridization optimizations
This commit includes two optimizations:

 - Use CuBLAS for batched linear algebra. Compute Ahat^{-1} Ct using
   batched LU with multiple right-hand sides.
 - After forming face-to-face connectivity, expose more parallelism
   in matrix-matrix kernels.
2024-06-16 22:42:07 -07:00
Stowell, Mark L 97f8b1b16b Adding more bounds checking to pyramid functions 2024-06-14 13:56:25 -07:00
Stowell, Mark L 488e93ad74 Updating meshgen and mesh_geoms flags following pyramid refinement 2024-06-14 13:55:54 -07:00
Will Pazner d448fd0811 Support element and boundary markers in Element Assembly 2024-06-14 10:37:57 -07:00
Stowell, Mark L c0fee4482a Adding pyramid type to DofTrans creation code 2024-06-13 17:47:06 -07:00
Will Pazner 098bb72f34 Fix bug in EABilinearFormExtension 2024-06-12 09:50:37 -04:00
Will Pazner 51961fe66d Add option to add boundary elements to element matrices in EABilinearFormExtension 2024-06-11 17:11:08 -04:00
Will Pazner 14011b604f Add BilinearFormIntegrator::AssembleEABoundary, implement mass
Add unit test for mass EA and legacy boundary integrators.
2024-06-11 17:11:08 -04:00
Will Pazner c9233a2045 Use unique_ptr for BilinearFormExtension 2024-06-11 17:11:08 -04:00
Will Pazner ce9a500498 Fix nvcc 'partially overridden' warnings 2024-06-11 17:11:08 -04:00
Will Pazner 6edf26214c Expose more parallelism in HybridizationExtension kernels 2024-06-11 17:11:08 -04:00
Will Pazner b008b0b8da Minor changes to grad-div timings 2024-06-11 17:11:08 -04:00
Will Pazner 8b8e7b47c8 Host/device memory management 2024-06-11 17:11:08 -04:00
Will Pazner 1ef48cd785 Adjust shared memory H(div) EA kernels 2024-06-11 17:11:08 -04:00
Will Pazner a8c681b67c Enable element assembly with hybridization in grad-div miniapp 2024-06-11 17:11:08 -04:00
Will Pazner a2002aa05b Parallelize HybridizationExtension 2024-06-11 17:11:08 -04:00
Will Pazner 59ef835780 Fix bug in BilinearForm::RecoverFEMSolution with EA hybridization 2024-06-11 17:11:08 -04:00
Will Pazner 857f10f6a4 EA assembly kernels for 3D H(div) 2024-06-11 17:11:08 -04:00
Will Pazner a68de24413 Unify H(div) mass and div-div EA kernels 2024-06-11 17:11:08 -04:00
Will Pazner 177fee04e5 3D element assembly for NormalTraceJumpIntegrator 2024-06-11 17:11:08 -04:00
Will Pazner 5be94b463d real_t instead of double 2024-06-11 17:11:08 -04:00
Will Pazner 045cbad46f CUDA fixes in HybridizationExtension 2024-06-11 17:11:08 -04:00
Will Pazner af482bdbcc HybridizationExtension nvcc limitation 2024-06-11 17:11:08 -04:00
Will Pazner c1e587a504 Fix NormalTraceJumpIntegrator Element Assembly unit test 2024-06-11 17:11:08 -04:00
Will Pazner 2aad85ca57 More device execution in HybridizationExtension::ConstructH 2024-06-11 17:11:08 -04:00
Will Pazner c505933d7f Deep copy when accessing the DOF map on device
Reading reading the DOF map on GPU can cause issues in other parts of the code
when using the debug device. The DOF map is accessed in other places without
explicitly calling HostRead, which fails on non-const access if the device
pointer is valid.
2024-06-11 17:11:08 -04:00
Will Pazner a96222708d Add host device kernel AddMultAtB 2024-06-11 17:11:08 -04:00
Will Pazner 0addaa7342 Check A.Empty() instead of A == NULL in SparseMatrix::PrintMatlab
Otherwise this can fail with the debug device if the host pointer is not valid.
2024-06-11 17:11:08 -04:00
Will Pazner 5218b40945 Call HostRead in ElementRestriction::ElementRestriction
Avoid issues with debug device if device pointer is valid
2024-06-11 17:11:08 -04:00
Will Pazner 5b5af88f32 HybridizationExtension::MultAhatInv on device 2024-06-11 17:11:08 -04:00
Will Pazner 51037a7724 Return lexicographic ordering from element assembly
Convert to native ordering in HybridizationExtension
2024-06-11 17:11:08 -04:00
Will Pazner e325b07545 Add element assembly option to ex4, enable device hybridization 2024-06-11 17:11:07 -04:00
Will Pazner 6083094f16 Eliminate essential DOFs when adding element matrices to hybridized system 2024-06-11 17:11:07 -04:00
Will Pazner bb07ec79f4 Fix batched solve 2024-06-11 17:11:07 -04:00
Will Pazner 33157ddb60 Fix constraint matrices 2024-06-11 17:11:07 -04:00
Will Pazner 2a4e4b7d5a Hybridization device bug fixes and improvements 2024-06-11 17:11:07 -04:00
Will Pazner d160b52f49 Add DenseTensor::NewMemoryAndSize 2024-06-11 17:11:07 -04:00
Will Pazner dd153bcf7a Small refactor 2024-06-11 17:11:07 -04:00
Will Pazner fb4659bc32 Don't need to distinguish between interior and boundary free DOFs 2024-06-11 17:11:07 -04:00
Will Pazner 3344049b13 Eliminate essential DOFs on device 2024-06-11 17:11:07 -04:00
Will Pazner 4a49b90052 Factor out HybridizationExtension::MultR 2024-06-11 17:11:07 -04:00
Will Pazner 18af2042c7 Use element assembly in HybridizationExtension::ConstructC 2024-06-11 17:11:07 -04:00
Will Pazner 6be8650f71 Improve H(div) Element Assembly unit test 2024-06-11 17:11:07 -04:00
Will Pazner 4f9686bd8b Port NormalTraceJumpIntegrator::AssembleEAInteriorFaces to mfem::forall device kernels 2024-06-11 17:11:07 -04:00
Will Pazner 9b54f750c9 Add NormalTraceJumpIntegrator::AssembleEAInteriorFaces
Uses existing EA kernels for mass integrator.

Add unit test to compare with legacy assembly.

Also, fixes bug in EA mass kernel in 1D (r_Bi and r_Bj need to be set inside a
MFEM_FOREACH_THREAD loop).
2024-06-11 17:11:07 -04:00
Will Pazner fa552415d3 Add BilinearFormIntegrator::AssembleEAInteriorFaces for mixed trace integrators 2024-06-11 17:11:07 -04:00
Will Pazner 5ced2e61ff Doxygen fix 2024-06-11 17:11:07 -04:00
Will Pazner e954950e6d Combine AssemblyLevel::ELEMENT with hybridization 2024-06-11 17:11:07 -04:00
Will Pazner 7e589d7ee8 Add Hybridization::AssembleElementMatrices and HybridizationExtension::AssembleElementMatrices 2024-06-11 17:11:07 -04:00
Will Pazner 94ae58fa83 Add BilinearForm::GetElementMatrices
Also add unit test
2024-06-11 17:11:07 -04:00
Will Pazner 69a0b82c89 Support reordering in EABilinearFormExtension::GetElementMatrices 2024-06-11 17:11:07 -04:00
Will Pazner 6d297c8ec0 Add EABilinearFormExtension::GetElementMatrices 2024-06-11 17:11:07 -04:00
Will Pazner d00b172172 Small Doxygen edits for L2InterfaceFaceRestriction 2024-06-11 17:11:07 -04:00
Will Pazner 6b802a0ec0 Element assembly for div-div in 2D 2024-06-11 17:11:07 -04:00
Will Pazner 31f4f5b2bb Element assembly for 2D H(div) mass matrix 2024-06-11 17:11:07 -04:00
Will Pazner 0e135ff1d0 Enable "Serial H1 Full Assembly" unit test without MPI 2024-06-11 17:11:07 -04:00
Will Pazner 2b95924226 Use unique_ptr in BilinearForm for element_matrices, static_cond and hybridization 2024-06-11 17:11:07 -04:00
Will Pazner 9ff6c53ace Don't fall back on legacy Schur complement assembly in HybridizationExtension
Don't need to set up Af_offsets and Af_f_offsets
2024-06-11 17:11:07 -04:00
Will Pazner e3b013c3e8 Device assembly of Schur complement in HybridizationExtension 2024-06-11 17:11:07 -04:00
Will Pazner 2b8ef63bc1 Construct Schur complement in HybridizationExtension 2024-06-11 17:11:07 -04:00
Will Pazner 67e3b289b6 Provide access to gather map in L2InterfaceFaceRestriction 2024-06-11 17:11:07 -04:00
Will Pazner 8d1b7f061a Expose more parallelism in hybridization kernels 2024-06-11 17:11:07 -04:00
Will Pazner 6f9c761bfa Port some hybridization operations to forall kernels 2024-06-11 17:11:07 -04:00
Will Pazner 0ce2e7551c Implement HybridizationExtension::ReduceRHS 2024-06-11 17:11:07 -04:00
Will Pazner c07e081832 Implement L2InterfaceFaceRestriction::AddMultTranspose 2024-06-11 17:11:07 -04:00
Will Pazner 528bd5a5a3 Fix typo in comment in Hybridization 2024-06-11 17:11:07 -04:00
Will Pazner a994abd7d3 Implement A_hat^{-1} in HybridizationExtension 2024-06-11 17:11:07 -04:00
Will Pazner 4566dc299d Minor cleanup in HybridizationExtension 2024-06-11 17:11:07 -04:00
Will Pazner 352403bfc5 Store constraint matrix in EA format in hybridization extension 2024-06-11 17:11:07 -04:00
Will Pazner 0c10a41589 Add L2InterfaceFaceRestriction 2024-06-11 17:11:07 -04:00
Will Pazner ddd54fc459 Reduce transitive includes 2024-06-11 17:11:07 -04:00
Will Pazner 5ef5b333e5 Device execution in HybridizationExtension::ComputeSolution
For now, use ElementDofOrdering::NATIVE. This will need to be changed to
LEXICOGRAPHIC once device assembly of the element matrices is supported.

Also, represent hat DOF types (free interior, free boundary, essential) with
enum instead of raw integer.
2024-06-11 17:11:07 -04:00
Will Pazner cd5feebfe5 Device execution in HybridizationExtension::Init 2024-06-11 17:11:07 -04:00
Will Pazner 4c1e6e91f2 Doxygen for HybridizationExtension 2024-06-11 17:11:07 -04:00
Will Pazner fcba41be04 Initial setup for device execution for hybridization 2024-06-11 17:11:07 -04:00
Will Pazner 82e55c8aea More Doxygen 2024-06-11 17:11:07 -04:00
Will Pazner 9d1f84d81a Add hybridization extension source files 2024-06-11 17:11:07 -04:00
Will Pazner befd4656fc More Doxygen for Hybridization class 2024-06-11 17:11:07 -04:00
Will Pazner 81cd6e8fe6 RAII in Hybridization class 2024-06-11 17:11:07 -04:00
Stowell, Mark L 347d0e6384 Fixing bug in implementation of Jacobi polynomials 2024-06-06 13:51:09 -07:00
Stowell, Mark L eafeb15cd0 Correcting sizes of polynomial arrays 2024-06-05 16:20:07 -07:00
Stowell, Mark L 9178cb687a Small and medium sized bug fixes 2024-06-05 16:19:19 -07:00
Stowell, Mark L 83a34a3e77 Adding access to H1 polynomials 2024-06-05 16:17:15 -07:00
Stowell, Mark L 95705c8121 Nodes implemented but now the real debugging begins 2024-05-29 17:52:08 -07:00
Stowell, Mark L 09222e3313 Adding curls of ND basis functions on pyramids 2024-05-29 13:43:03 -07:00
Dylan Copeland ebbf706a55 Minor fixes. 2024-05-28 13:21:37 -07:00
Dylan Copeland f1f683061d ProlongToMaxOrder is now a member of (Par)GridFunction. 2024-05-28 12:12:47 -07:00
dylan-copeland 26f0dad6fa make clean 2024-05-26 13:22:56 -07:00
dylan-copeland 808b83c282 Ignore (git) examples/order files. 2024-05-26 12:27:59 -07:00
dylan-copeland ef9d32f1a4 Fix the previous fix. 2024-05-26 11:13:52 -07:00
Dylan Copeland 9130cb06e1 Fixed an MPI communication issue. 2024-05-25 21:43:06 -07:00
Dylan Copeland 90bc48532f Changed argument type in MakeDofTable. 2024-05-24 15:09:49 -07:00
dylan-copeland 21360beede Smart pointers 2024-05-24 13:08:43 -07:00
Dylan Copeland e878db0fee Bug fix 2024-05-24 10:23:55 -07:00
Dylan Copeland ce386c1409 Using PR 2854 for GridFunction updates after refinement. Added option for hp-refinement in ex6p. Improved interface for p-refinement. 2024-05-23 21:13:34 -07:00
Stowell, Mark L 06f892e457 Implementation of Nedelec basis on pyramids 2024-05-23 12:55:33 -07:00
Stowell, Mark L 2f894ef8fa Adding functions to support Nedelec basis on pyramids 2024-05-23 12:54:37 -07:00
Dylan Copeland e63ba7dd04 Fixing enum shadow. 2024-05-22 19:58:42 -07:00
Dylan Copeland 232642b5d3 Merge branch 'master' of github.com:mfem/mfem into ppr 2024-05-22 19:32:22 -07:00
Dylan Copeland c668bb6fa2 Enum for VarMessage tags. 2024-05-22 19:31:43 -07:00
Stowell, Mark L ea655797da Generalizing ND_DofTransformation to support pyramids 2024-05-22 13:23:11 -07:00
Dylan Copeland b309b6d789 Refactoring 2024-05-21 21:53:41 -07:00
Dylan Copeland 6956e10323 New struct VarOrderElemInfo. 2024-05-21 20:36:01 -07:00
Dylan Copeland e8dc1eb31c Eliminated inefficient table search. Cleaned up some things. 2024-05-20 21:22:32 -07:00
Stowell, Mark L 2f4f06d549 Switching to more robust rational polynomials for RT basis functions on triangular faces 2024-05-16 14:41:37 -07:00
Stowell, Mark L e9352448f1 Adding alternate rational polynomial which avoids 0/0 on pyramid surface 2024-05-16 14:40:40 -07:00
Stowell, Mark L 6db2519021 Adding finite difference based unit test of CalcDivShape 2024-05-16 14:39:32 -07:00
Stowell, Mark L 230d8830c5 make style 2024-05-14 13:17:08 -07:00
Stowell, Mark L 3373ee5c34 Improving/implementing gradients of H1 and L2 pyramid basis functions 2024-05-14 13:16:06 -07:00
Stowell, Mark L 1ecdd8e7b0 Adding pyramids to unit tests of CalcDShape 2024-05-14 13:14:44 -07:00
Stowell, Mark L 8f0398d330 Adding divergence of HDiv basis functions 2024-05-08 11:55:07 -07:00
Dylan Copeland d208f6a951 Removed some obsolete code. 2024-05-07 18:18:19 -07:00
Stowell, Mark L 29662bf5c6 Adding comment 2024-05-07 15:53:07 -07:00
Stowell, Mark L 59ccdced1a Changing default L2 basis on pyramids to Fuentes version 2024-05-07 15:52:52 -07:00
Stowell, Mark L fb149d7d12 Fixing ordering bug in RT_FuentesPyramid basis 2024-05-07 15:52:13 -07:00
Stowell, Mark L 15ff1a42cd Removing unused code from RT_FunetesPyramid basis 2024-05-07 15:51:46 -07:00
Stowell, Mark L 09fcd3ecca Switching L2_FuentesPyramid basis to use common polynomials with other Fuentes bases 2024-05-07 15:50:32 -07:00
Stowell, Mark L 35857b5379 Fixing bug in L2 basis with closed points (as used in GLVis) 2024-05-07 15:49:37 -07:00
Stowell, Mark L 1450a58374 Removing debugging code 2024-05-07 15:48:28 -07:00
Dylan Copeland d398bac010 Bug fix 2024-05-03 10:41:48 -07:00
Stowell, Mark L c5be56264c Pre-update commit containing much temporary code 2024-05-02 09:40:33 -07:00
Dylan Copeland d3c669d482 Merge branch 'master' of github.com:mfem/mfem into ppr 2024-05-01 19:46:28 -07:00
Dylan Copeland 270f83bd35 Fixed a bug by adding some changed edges to parallel order propagation. 2024-05-01 17:02:27 -07:00
Dylan Copeland e327a173da Remove unused variables. 2024-04-18 21:23:05 -07:00
Dylan Copeland e6c48e3987 Merge branch 'master' of github.com:mfem/mfem into ppr 2024-04-18 17:00:47 -07:00
Dylan Copeland c9ad580d33 Added parallel order propagation with communication of shared entity orders. 2024-04-18 16:37:38 -07:00
Dylan Copeland 040c337ce7 Using local edof and communicating order, instead of encoding order in edof. Fixed an issue with ghost faces having edges without the face order. 2024-04-12 12:15:26 -07:00
Stowell, Mark L 1d46fbba57 Cleaning up deprecation warnings 2024-04-08 13:22:15 -07:00
Stowell, Mark L 4804824873 Merge remote-tracking branch 'origin/master' into pyramid-dev
# Conflicts:
#	fem/fe/fe_base.cpp
#	fem/fe/fe_base.hpp
#	fem/gridfunc.cpp
2024-04-08 12:09:46 -07:00
Stowell, Mark L 1a2b8291f5 Switching to new pyramid polynomial functions 2024-04-08 12:06:45 -07:00
Stowell, Mark L 8f3eafd251 Adding pyramid specific polynomial functions in separate source files 2024-04-08 12:04:15 -07:00
Dylan Copeland 3ec7ae873d Allowing for ghost master faces with inconsistent ghost edge orders. 2024-03-20 15:28:34 -07:00
dylan-copeland 1acaa833b6 -Wunused-but-set-variable 2024-03-16 20:18:34 -07:00
dylan-copeland a07f893b03 Remove unused variables. 2024-03-16 20:03:18 -07:00
Dylan Copeland f0f1885090 Fixed some issues with parallel hp-refinement. Added more unit tests. 2024-03-16 17:46:37 -07:00
dylan-copeland 604cb61b00 Another pedantic error. 2024-02-09 16:42:44 -08:00
dylan-copeland ff6546dd8f Fix shadowed variable. 2024-02-09 16:35:18 -08:00
dylan-copeland 10c9adf8c8 Remove unused variables. 2024-02-09 16:29:22 -08:00
Dylan Copeland 786caf20c3 Extended to support parallel hp-refinement, with unit tests in H1 for 2D and 3D. 2024-02-09 16:21:10 -08:00
Dylan Copeland 4430df415a Remove unused variables. 2024-01-31 20:00:08 -08:00
Dylan Copeland 5acdaf28a5 Generalized for ND and RT face DOFs in parallel. Added parallel ND and RT unit tests on quad and hex meshes. 2024-01-31 19:37:43 -08:00
Stowell, Mark L b482ce2880 Prototype 2nd order Nedelec basis on pyramid shaped elements (incomplete) 2024-01-26 23:11:56 -08:00
Stowell, Mark L 5ab2c0b6e3 Adding temporary test functions for convergence of pyramid bases 2024-01-24 18:41:22 -08:00
Stowell, Mark L ce900ec687 Adding Bergot pyramid bases to finite element collections 2024-01-24 18:40:38 -08:00
Stowell, Mark L 19df0ed57d Adding scalar basis on pyramids from Bergot paper with fewer interior DoFs 2024-01-24 18:38:51 -08:00
Stowell, Mark L a5cd125342 Adding Jacobi polynomials 2024-01-24 18:37:35 -08:00
Dylan Copeland 220e76ec83 Bug fixes. 2024-01-22 12:00:40 -08:00
dylan-copeland c5af057556 Remove unused variables. 2024-01-19 21:58:00 -08:00
dylan-copeland 60f281ddad Fix merge. 2024-01-19 21:45:07 -08:00
dylan-copeland 2408687812 Style 2024-01-19 21:36:38 -08:00
dylan-copeland f054912423 Merge branch 'master' of github.com:mfem/mfem into ppr 2024-01-19 21:35:33 -08:00
Dylan Copeland dca045d48a More general implementation for face DOFs, with more general testing. 2024-01-19 21:01:00 -08:00
Stowell, Mark L c35ca25dfa Adding a mixed tests mesh resembling a butterfly mesh with six pyramids meeting at the origin 2024-01-19 17:27:12 -08:00
Dylan Copeland cea73f8a02 Merge branch 'master' of github.com:mfem/mfem into ppr 2023-12-18 19:07:23 -08:00
Dylan Copeland bf4f411d9c Initial implementation of parallel p-refinement, for 2D and 3D H1 spaces, with unit tests. 2023-12-18 19:04:18 -08:00
Stowell, Mark L a50ef7f774 Resolving post-merge build issues related to pyramid classes 2023-10-06 11:03:40 -07:00
Stowell, Mark L a897af7764 make style 2023-10-06 10:49:28 -07:00
Stowell, Mark L e6ce209f64 Merge remote-tracking branch 'origin/master' into pyramid-dev
# Conflicts:
#	fem/fe.cpp
#	fem/fe.hpp
#	fem/fe_coll.cpp
#	fem/fe_coll.hpp
#	fem/geom.hpp
#	fem/intrules.cpp
#	mesh/mesh.cpp
#	mesh/mesh.hpp
#	mesh/mesh_headers.hpp
#	mesh/pyramid.cpp
#	mesh/pyramid.hpp
#	miniapps/common/mesh_extras.cpp
#	miniapps/tools/display-basis.cpp
#	tests/unit/fem/test_3d_bilininteg.cpp
#	tests/unit/fem/test_get_value.cpp
2023-10-06 10:48:38 -07:00
Stowell, Mark L 3c40606626 Merge branch 'pyramid-dev' of github.com:mfem/mfem into pyramid-dev
# Conflicts:
#	fem/fe.cpp
#	fem/geom.cpp
#	mesh/mesh.cpp
#	tests/unit/fem/test_calcshape.cpp
2023-10-06 10:17:45 -07:00
Stowell, Mark L 23f808fa9d make style 2023-10-06 10:08:05 -07:00
Stowell, Mark L 54cb4f7ce0 Adding tests of H1 bases on additional element types 2023-10-06 10:07:36 -07:00
Stowell, Mark L 576c5d26d6 Merge remote-tracking branch 'origin/no-tet-reorder-dev' into pyramid-dev
# Conflicts:
#	examples/test_eigs.cpp
#	fem/fe_coll.cpp
#	tests/unit/fem/test_domain_int.cpp
2021-07-08 12:42:13 -07:00
Stowell, Mark L c3c983d1e3 Adding H1 and L2 pyramids to domain integration unit test 2021-04-23 22:03:46 -07:00
Stowell, Mark L c97a82b14c Fixing pyramid uniform refinement 2021-04-23 22:02:50 -07:00
Stowell, Mark L 44d2e66858 Fixing interior dof number for pyramid in H1_FECollection 2021-03-10 13:14:32 -08:00
Stowell, Mark L 53728d12d9 Fixing pyramid integration rule 2021-03-10 13:13:39 -08:00
Stowell, Mark L e8a356baf8 Implementing L2_PyramidElement::CalcDShape for DG 2021-03-10 13:13:17 -08:00
Stowell, Mark L 482854800a Expanding unit test comment 2021-03-09 15:20:21 -08:00
Stowell, Mark L d417a515d2 Fixing pyramid cartesian mesh construction 2021-03-09 15:20:02 -08:00
Stowell, Mark L b39cfbfaa3 Implementing L2 on Pyramids 2021-03-09 15:19:26 -08:00
Stowell, Mark L cb5d07181f Implementing H1 on Pyramids 2021-03-09 15:18:59 -08:00
Stowell, Mark L 920099da20 First draft of H1 pyramids from Fuentes et al 2021-03-06 17:03:27 -08:00
Stowell, Mark L 004fb5757d Expanding tests for H1 pyramids 2021-03-06 17:02:58 -08:00
Stowell, Mark L 0783faddd4 Adding Pyramid option to Mesh::Make3D 2021-03-06 17:02:34 -08:00
Stowell, Mark L 9123848ff4 make style 2021-03-06 17:01:53 -08:00
Stowell, Mark L bfffaa3171 Fixing element count when adding pyramids 2021-03-04 16:58:24 -08:00
Stowell, Mark L a3e894ec50 Adding pyramid case to test_eigs 2021-03-04 16:58:04 -08:00
Stowell, Mark L d99c508a68 Updating legalese 2021-03-04 16:57:46 -08:00
Stowell, Mark L 053c71a885 Merge remote-tracking branch 'origin/no-tet-reorder-dev' into pyramid-dev
# Conflicts:
#	fem/fe.hpp
#	fem/intrules.cpp
#	mesh/mesh.cpp
#	mesh/mesh.hpp
#	mesh/mesh_readers.cpp
2021-03-04 16:21:34 -08:00
Stowell, Mark L 59c44959b3 Adding support for wedges and pyramids in display-basis 2020-01-19 23:14:27 -08:00
Stowell, Mark L 1beab25885 Changing reference element in H1_Pyramid 2020-01-19 23:13:53 -08:00
Stowell, Mark L 3835b45427 Tweaks in support of pyramids 2020-01-19 23:13:24 -08:00
Stowell, Mark L 7af8a82cc2 Merge remote-tracking branch 'origin/no-tet-reorder-dev' into pyramid-dev 2020-01-19 20:12:33 -08:00
Stowell, Mark L 3376d6d744 Add polynomial tests and pyramid mesh 2019-12-28 13:32:50 -08:00
Stowell, Mark L 93c87515ab Adding pyramid support to some Mesh methods 2019-12-28 13:28:29 -08:00
Stowell, Mark L 3769f8d788 Merge remote-tracking branch 'origin/no-tet-reorder-dev' into pyramid-dev 2019-12-28 10:02:34 -08:00
Stowell, Mark L e62a12af26 Starting pyramid support in Mesh class 2019-12-27 22:29:08 -08:00
Stowell, Mark L e9e0cd2af3 Adding arbitrary order H1 pyramid elements 2019-12-27 22:28:42 -08:00
Stowell, Mark L c285a77bf1 Fixing typos and adding implementation for new virtual function 2019-12-22 16:16:12 -08:00
Stowell, Mark L de179042a2 Merge remote-tracking branch 'origin/no-tet-reorder-dev' into pyramid-dev
# Conflicts:
#	makefile
#	mesh/mesh.cpp
2019-12-22 16:15:34 -08:00
Stowell, Mark L 6f5396fe11 Adding miniapps/common objects to separate library file 2019-04-17 11:10:16 -07:00
Stowell, Mark L 2b12687eb6 Merge remote-tracking branch 'origin/master' into pyramid-dev 2019-04-12 15:02:33 -07:00
Stowell, Mark L 777ad30e16 Merge remote-tracking branch 'origin/master' into pyramid-dev 2019-04-09 14:14:32 -07:00
Stowell, Mark L e25c40abaa Merge remote-tracking branch 'origin/master' into pyramid-dev
# Conflicts:
#	mesh/mesh.cpp
2019-04-01 10:57:34 -07:00
Stowell, Mark L 97f794057e Merge remote-tracking branch 'origin/master' into pyramid-dev 2019-03-31 14:53:10 -07:00
Stowell, Mark L de171f2751 Merge remote-tracking branch 'origin/master' into pyramid-dev 2019-03-28 21:57:51 -07:00
Stowell, Mark L 183569f3b7 Merge remote-tracking branch 'origin/master' into pyramid-dev 2019-01-20 00:03:24 -08:00
Stowell, Mark L c428ad7ae1 Mixed meshes can no longer assume uniform refinement produces eight times as many elements 2018-12-13 15:46:13 -08:00
Stowell, Mark L 50eef85cd5 Adding initial support for uniform refinement of Pyramid meshes 2018-12-13 11:29:44 -08:00
Stowell, Mark L 7792ced114 Using correct refined element type in Mesh c'tor with refinement option 2018-12-13 00:04:29 -08:00
Stowell, Mark L 4109c06129 Adding RefinedGeometry info for Pyramids 2018-12-13 00:03:12 -08:00
Stowell, Mark L bdb1afeb74 Bugfix: incorrect number of interior dofs 2018-12-11 16:21:12 -08:00
Stowell, Mark L fc556b7c08 Adding integration rule for pyramids 2018-12-11 15:02:24 -08:00
Stowell, Mark L 06bfe53f55 Adding inline pyramid mesh 2018-12-11 11:30:19 -08:00
Stowell, Mark L a38bda120c Adding inline pyramid mesh support 2018-12-11 11:29:03 -08:00
Stowell, Mark L 0a99b6fa99 make style 2018-12-11 11:28:34 -08:00
Stowell, Mark L 84fd3097a7 Bugfix related to precedence of '?' operator 2018-12-11 10:16:00 -08:00
Stowell, Mark L ac6efafd94 Adding pyramid comments to mesh files and vtk cell types for pyramids 2018-12-11 00:58:41 -08:00
Stowell, Mark L 7bf66519a4 Adding a piece-wise constant FE for pyramids 2018-12-11 00:57:34 -08:00
Stowell, Mark L f8c2878969 Supplying Geometry data for Pyramids 2018-12-10 22:12:10 -08:00
Stowell, Mark L 197698dc02 Adding pyramids to various classes 2018-11-25 01:18:05 -08:00
Stowell, Mark L 2e37747182 Forgot to adjust VertToVert::I array for pyramid elements 2018-11-24 21:01:01 -08:00
Stowell, Mark L c57e3b7d50 Adding initial support for pyramid shaped elements 2018-11-24 20:56:05 -08:00
257 changed files with 30047 additions and 3742 deletions
+10 -2
View File
@@ -114,6 +114,13 @@ jobs:
build-system: make
hypre-target: int32
precision: fp32
- os: macos-latest
target: opt
codecov: NO
mpi: par
build-system: make
hypre-target: int32
precision: fp32
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
runs-on: ${{ matrix.os }}
@@ -154,7 +161,8 @@ jobs:
- name: get MPI (Linux)
if: matrix.mpi == 'par' && matrix.os == 'ubuntu-latest'
run: |
sudo apt-get install mpich libmpich-dev
sudo apt-get install openmpi-bin libopenmpi-dev
export OMPI_MCA_rmaps_base_oversubscribe=1
- name: get lcov (Linux)
if: matrix.codecov == 'YES' && matrix.os == 'ubuntu-latest'
@@ -191,7 +199,7 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
- name: get hypre
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
+7
View File
@@ -80,6 +80,7 @@ examples/sol_p.*
examples/sol_r.*
examples/sol_i.*
examples/ex6p-checkpoint.*
examples/order.*
examples/ex9.mesh
examples/ex9-mesh.*
examples/ex9-init.*
@@ -235,6 +236,8 @@ miniapps/meshing/minimal-surface
miniapps/meshing/pminimal-surface
miniapps/meshing/polar-nc
miniapps/meshing/mesh-quality
miniapps/meshing/hpref
miniapps/meshing/phpref
miniapps/meshing/mobius-strip.mesh
miniapps/meshing/klein-bottle.mesh
miniapps/meshing/toroid-*.mesh
@@ -252,6 +255,10 @@ miniapps/meshing/sol.gf
miniapps/meshing/optimized*
miniapps/meshing/perturbed*
miniapps/meshing/polar-nc.mesh
miniapps/meshing/mesh.*
miniapps/meshing/order.*
miniapps/meshing/sol.*
miniapps/meshing/refined.mesh
miniapps/mtop/parheat
miniapps/mtop/ParHeat*
+152 -107
View File
@@ -8,159 +8,218 @@
https://mfem.org
Version 4.7.1 (development)
Version 4.8.1 (development)
===========================
- Refactored ALGOIM cut integration rules. The interface is unified with
the interface for moment based cut integration rules.
- Altered (Par)GridFunction::Compute*Error functions to ensure they return
non-negative values and therefore behave as "norms".
Version 4.8, released on Apr 9, 2025
====================================
Discretization improvements
---------------------------
- Added NURBS-based H(div) and H(curl) elements in 2D and 3D. Only on single
patch meshes. Only implemented for serial computations.
- Added high-order basis functions on pyramid-shaped elements for all spaces in
the de Rham complex based on the paper "Orientation embedded high order shape
functions for the exact sequence elements of all shapes" by Fuentes, Keith,
Demkowicz and Nagaraj (doi.org/10.1016/j.camwa.2015.04.027). Positive basis
functions (Bernstein basis) for H1 and L2 on pyramids were also added.
- Added support for boundary constraints to the hybridization class.
- Added support for parallel p- and hp-refinement on quad/hex meshes. For hp, we
currently support only isotropic refinement with L2 or H1 spaces. See the new
miniapps hpref and phpref in the miniapps/meshing/ directory.
- Added support for external boundary submeshes with nonconformal mesh adaptation.
- Added several improvements for hyperbolic problems:
* Assembly of Jacobians in HyperbolicFormIntegrator
* Component-wise upwinded flux (ComponentwiseUpwindFlux)
* Average fluxes in NumericalFlux (formerly RiemannSolver) and FluxFunction
- Added assembly of Jacobians to `HyperbolicFormIntegrator`.
- Added convenience methods to class FiniteElementSpace to directly identify all
degrees of freedom on the exterior faces of the domain, without referencing
boundary element attributes (GetExteriorVDofs and GetExteriorTrueDofs).
- Added average fluxes to `NumericalFlux` (formerly `RiemannSolver`)
and `FluxFunction`.
- Altered (Par)GridFunction::Compute*Error functions to ensure they return
non-negative values and therefore behave as "norms".
- Added component-wise upwinded flux (`ComponentwiseUpwindFlux`).
- SubMesh and ParSubMesh have been extended to work on nonconforming meshes.
Extracting volume and exterior surface submeshes are both supported.
- Added support for H(div) spaces in class QuadratureInterpolator. For now, only
- LinearFormIntegrator, BilinearFormIntegrator and NonlinearFormIntegrator now
inherit from a base class Integrator that centralizes the logic for selecting
quadrature rules. This includes a virtual method GetDefaultIntegrationRule,
which should be favored over directly defining a default integration rule in
the element-level assembly routines. The latter is still possible, by leaving
the new virtual method as its default base implementation of returning NULL.
- Added NURBS-based H(div) and H(curl) elements in 2D and 3D. Currently only for
single patch meshes in serial.
- Refactored ALGOIM cut integration rules. The interface is unified with the
interface for moment based cut integration rules.
- FiniteElementSpace has new methods to directly set the prolongation and
restriction operators to user-specified sparse matrices.
- Added support for H(div) spaces in class QuadratureInterpolator. Currently only
(vector) VALUES, (vector) PHYSICAL_VALUES, and PHYSICAL_MAGNITUDES evaluations
are implemented. [PR #4669]
are implemented.
- Added support for boundary constraints to class Hybridization.
- Added support for custom interpolation procedure in FindPointsGSLIB.
- Added GSLIB-based gather-scatter operator.
Meshing improvements
--------------------
- Added native AD support for numerous TMOP metrics that didn't have first or
second derivative implementations.
- Added support for nonuniform anisotropic mesh refinement on serial quad/hex
meshes with arbitrary spacing in each direction. This enables in particular
3:1 refinement, as demonstrated in the new meshing miniapp ref321.
- The ExodusII reader now handles pyramid and wedge element types. Mixed meshes
are also supported.
- Added capabilities for optimization and adaptation of periodic meshes with
TMOP. The internals of TMOP were modified so that the optimization problem is
always solved with respect to mesh displacements.
- Added support for nonuniform anisotropic (nonconforming) mesh refinement with
arbitrary spacing in each direction, for quadrilateral (2D) and hexahedral
(3D) meshes. This enables in particular 3:1 refinement, as demonstrated in the
new meshing miniapp ref321.
- New convenience methods for manipulating boundary attribute markers in class
Mesh: MarkExternalBoundaries, MarkNamedBoundaries, UnmarkInternalBoundaries
and UnmarkNamedBoundaries. See Examples 1/1p and 11p for basic usage.
New and updated examples and miniapps
-------------------------------------
- Added miniapps to demonstrate the H(div) and H(curl) NURBS elements.
- Added a new method, GetExteriorFaceMarker, to the serial and parallel mesh
classes for identifying faces on the exterior of the mesh irrespective of
their presence in the list of "boundary elements".
- Added an MFEM example for the eikonal equation. This new solver is based on
the proximal Galerkin method introduced by Keith and Surowiec.
- Added native AD support for computing the derivatives of numerous TMOP metrics
that didn't have first or second derivative implementations.
- Added a new toy miniapp that animates an interesting fidget spiral cone toy.
See miniapps/toys/spiral.cpp.
- Added ExodusII output capability which can handle in particular pyramid and
wedge element types. Mixed meshes are also supported.
- Added a command line option to all miniapps (`-p` or `--send-port`) for
specifying the GLVis server socket port (19916 by default).
- Added InverseElementTransformation::InitGuessType::EdgeScan as an alternative
initial guess type. This guess type tries solving with multiple initial guesses
along the r/s/t=0 edges of the element until a valid solution is found or all
initial guess points are exhausted.
- Added new convenience constructors for NURBS patches and knot vectors.
GPU computing
-------------
- Extended FindPointsGSLIB to support general field interpolation on GPUs. Note
that this requires that switch from gslib v1.0.7 to v1.0.9.
- Added support for GPU-accelerated batched linear algebra (using cuBLAS,
hipBLAS, MAGMA, or native MFEM functionality) through the BatchedLinAlg class.
- A new GPU kernel dispatch mechanism was introduced. Users can instantiate
specialized kernels for specific combinations of (for example) polynomial
degree and number of quadrature points using
`DiffusionIntegrator::AddSpecialization` and
`MassIntegrator::AddSpecialization` (this functionality may be added to more
integrators in the future).
degree and number of quadrature points using method AddSpecialization in
classes DiffusionIntegrator and MassIntegrator (this functionality may be
added to more integrators in the future).
- Calls to slower fallback kernels can be reported to `mfem::err` by setting
the environment variable `MFEM_REPORT_KERNELS` to any value other than `NO`
or by explicitly calling `KernelReporter::Enable`. Users can then add
specializations for these kernels to achieve higher performance.
- Added BatchInverseElementTransformation to batch InverseElementTransformation
searches. Batch searches are currently limited to meshes with a single element
geometry type of SEGMENT, SQUARE, or CUBE. Additional element geometry types
may be added in the future. Mixed element order meshes are supported. Batch
searches can be performed on the CPU (serial per MPI rank) or GPU. Embedded
elements (SEGMENT in 2D/3D space or SQUARE in 3D space) are supported, however
the existing solvers may struggle to find a valid solution.
- Element assembly kernels have been added for low-order refined to
high-order transfer operators. New kernels can be offloaded as device
kernels. Example usage may be found in lor-transfer.cpp under miniapps/tools.
- Calls to slower fallback kernels can be reported to mfem::err by setting the
environment variable MFEM_REPORT_KERNELS to any value other than NO or by
explicitly calling KernelReporter::Enable. For higher performance, users can
then add specializations for these kernels.
- Added support for GPU accelerated FindPointsGSLIB. Note that this will require
the users to switch from gslib v1.0.7 to v1.0.9.
- Element assembly kernels have been added for low-order refined -> high-order
transfer operators. New kernels can be offloaded as device kernels. Example
usage may be found in lor-transfer.cpp in the miniapps/tools/ directory.
- Added GPU acceleration and element assembly for DivDivIntegrator and
NormalTraceJumpIntegrator.
- Allow BlockLowerTriangularPreconditioner to run on GPU.
- Use device vectors in GMRES, FGMRES and other iterative methods.
Miscellaneous
-------------
Linear and nonlinear solvers
----------------------------
- Added GPU acceleration of the algebraic hybridization solver for grad-div
problems in H(div). See Example 4.
- Added a self-contained implementation of the Method of Moving Asymptotes (MMA)
for solving optimization problems.
- Added support for SUNDIALS v7. See the section "API changes" for some small
changes related to this new version.
- Refactored the `ARKStepSolver` class (ARKODE interface) to use
`TimeDependentOperator::Mult` only when the associated ODE operator is
expressed in explicit form (i.e., `TimeDependentOperator::isExplicit()`),
otherwise `TimeDependentOperator::ExplicitMult` is used. A check has been
added to `ARKStepSolver` to verify that the associated ODE operator is not in
explicit form when a mass matrix solver is enabled via a call to either the
`UseMFEMMassLinearSolver` or `UseSundialsMassLinearSolver` methods. This is
because enabling a mass matrix solver assumes that F(u,k,t) = M k in the
associated ODE operator.
- Changed the name of class IterativeSolverMonitor to IterativeSolverController,
which now allows for specifying convergence by a user defined criterion. For
backward compatibility, the old name is still available.
- Added support for custom interpolation procedure in FindPointsGSLIB.
- Refactored the ARKStepSolver class (ARKODE interface) to use the Mult() method
of TimeDependentOperator only when the associated ODE operator is expressed in
explicit form (i.e., TimeDependentOperator::isExplicit()), otherwise the
method ExplicitMult() is used. A check has been added to ARKStepSolver to
verify that the associated ODE operator is not in explicit form when a mass
matrix solver is enabled via a call to either the UseMFEMMassLinearSolver or
UseSundialsMassLinearSolver methods. This is because enabling a mass matrix
solver assumes that F(u,k,t) = M k in the associated ODE operator.
- `FiniteElementSpace` has new methods to directly set prolongation and
restriction operators to arbitrary sparse matrices.
- Added ODE solvers selection routines. This creates a uniformity across
examples, miniapps and other executables in regard to ODE (time-integrator)
selection.
- There are new convenience constructors for NURBS patches and knot vectors.
- Added new mechanism for retrieving and setting state vectors in ODE solvers.
This is relevant for AB/AM and gen-alpha solvers.
- Added convenience methods for manipulating boundary attribute marker arrays;
`(Par)Mesh::MarkExternalBoundaries`, `(Par)Mesh::UnmarkInternalBoundaries`,
`(Par)Mesh::MarkNamedBoundaries`, and `(Par)Mesh::UnmarkNamedBoundaries`.
See examples `ex1.cpp`, `ex1p.cpp`, and `ex11p.cpp` for basic usage.
- Added ODEsolver/ODEsolver2 unit tests to verify order of convergence and
read/write functionality.
- Added `(Par)Mesh::GetExteriorFaceMarker` for identifying faces on the
exterior of the mesh irrespective of their presence in the list of "boundary
elements".
New and updated examples and miniapps
-------------------------------------
- Added an MFEM example for the eikonal equation (examples/ex40) based on the
proximal Galerkin method introduced by Keith and Surowiec.
- Added methods to `(Par)FiniteElementSpace` to identify all degrees of freedom
located on the exterior of the domain without reference to the list of
"boundary elements"; `GetExteriorVDofs` and `GetExteriorTrueDofs`.
- Added miniapps to demonstrate the H(div) and H(curl) NURBS elements.
- `LinearFormIntegrator` and `NonlinearFormIntegrator` (including
`BilinearFormIntegrator`) now all inherit from a base class `Integrator`
that combines some logic related to selecting quadrature rules. This includes
a virtual method `Integrator::GetDefaultIntegrationRule`, which should be
favored over directly defining a default integration rule in the element-level
assembly routines (although the latter is still possible, by leaving the new
virtual method as its default base implementation of returning `NULL`).
- Fixed element visualization in the Mesh explorer miniapp.
- Added a command line option to all miniapps (-p or --send-port) for
specifying the GLVis server socket port (19916 by default).
- Added a new toy miniapp that animates an interesting fidget spiral cone toy.
See miniapps/toys/spiral.cpp.
Miscellaneous
-------------
- Updated the benchmarks (in tests/benchmarks) to work with the latest Google
Benchmarks classes (version 1.9.1). Renamed the MFEM_ENABLE_GOOGLE_BENCHMARKS
CMake option to just MFEM_ENABLE_BENCHMARKS.
- Changed the name of `IterativeSolverMonitor` to `IterativeSolverController`
which now allows for declaring convergence by a user defined criterion. For
backward compatibility, the old name is still available.
- Updated the minimum CMake version requirements:
* CMake >= 3.12 for CPU builds,
* CMake >= 3.17 for CUDA builds, and
* CMake >= 3.14 for HIP builds (CMake >= 3.12 may work as well, not tested).
- Various other simplifications, extensions, and bugfixes in the code.
API changes
-----------
- API change: 'TMOP_Metric_skew2D' has been marked as deprecated.
- In class GridFunction, fec was renamed to fec_owned.
- API change: in class GridFunction, 'fec' was renamed to 'fec_owned'.
- RiemannSolver was renamed to NumericalFlux (the old name has been deprecated
through typedef).
- API change: `RiemannSolver` was renamed to `NumericalFlux` (the old name has
been been deprecated through typedef)
- API change: support for SUNDIALS v7:
* the SUNDIALS types `realtype` and `booleantype` are no longer defined by v7
and therefore MFEM now uses the new type names `sunrealtype` and
`sunbooleantype`, respectively, which MFEM defines when using SUNDIALS < v6
- API changes due to SUNDIALS v7:
* the SUNDIALS types realtype and booleantype are no longer defined by v7
and therefore MFEM now uses the new type names sunrealtype and
sunbooleantype, respectively, which MFEM defines when using SUNDIALS < v6
where these types were not defined.
* The SUNDIALS macro `SUNLS_SUCCESS` and some other `*_SUCCESS` macros were
removed and replaced by `SUN_SUCCESS` in v7, so to avoid tedious checks for
SUNDIALS versions, MFEM now defines and uses the constant `SUN_SUCCESS` when
* The SUNDIALS macro SUNLS_SUCCESS and some other *_SUCCESS macros were
removed and replaced by SUN_SUCCESS in v7, so to avoid tedious checks for
SUNDIALS versions, MFEM now defines and uses the constant SUN_SUCCESS when
using SUNDIALS < v7.
* The constants `SUN_PREC_*`, introduced by SUNDIALS v6 are now introduced by
* The constants SUN_PREC_*, introduced by SUNDIALS v6 are now introduced by
MFEM when using SUNDIALS < v6 to avoid tedious version checks.
- TMOP_Metric_skew2D has been marked as deprecated.
Version 4.7, released on May 7, 2024
====================================
@@ -185,9 +244,6 @@ Meshing improvements
- Added support for internal boundary elements in nonconforming meshes.
- Added ExodusII output capability. The writer can handle first-order (Pyramid5,
Wedge6, Hex8, Tet4) and second-order FE types (Pyramid14, Wedge18, Hex27, Tet10).
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
Discretization improvements
@@ -247,15 +303,6 @@ New and updated examples and miniapps
- Added two new example codes: 38 and 39/39p described above. Substantially
updated Example 18/18p.
- Added ODE solvers selection routines. This creates a uniformity across examples,
miniapps and other executables in regard to ODE(time-integrator) selection.
- Added new mechanism for retrieving and setting state vectors in ODE solvers.
This is relevant for AB/AM and gen-alpha solvers.
- Added ODEsolver/ODEsolver2 unit tests to verify order of convergence and
read/write functionality.
Miscellaneous
-------------
- Updated the Doxygen documentation style, which now requires Doxygen version
@@ -269,8 +316,6 @@ Miscellaneous
- Various other simplifications, extensions, and bugfixes in the code.
- Added GSLIB-based gather-scatter operator.
Version 4.6, released on September 27, 2023
===========================================
+24 -23
View File
@@ -12,7 +12,8 @@
# The variable CMAKE_CXX_STANDARD and related were introduced in CMake v3.1
# Version 3.8 fixes the handling of CMAKE_CXX_STANDARD for try_compile.
# Version 3.8 or newer is required for direct CUDA support.
cmake_minimum_required(VERSION 3.8)
# Version 3.12 or newer is required for setting maximum policy version.
cmake_minimum_required(VERSION 3.12.0...4.0.0)
message(STATUS "CMake version: ${CMAKE_VERSION}")
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
"Path to optional user configuration file.")
@@ -58,7 +59,7 @@ project(mfem NONE)
# Current version of MFEM, see also `makefile`.
# mfem_VERSION = (string)
# MFEM_VERSION = (int) [automatically derived from mfem_VERSION]
set(${PROJECT_NAME}_VERSION 4.7.1)
set(${PROJECT_NAME}_VERSION 4.8.1)
# Prohibit in-source build
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
@@ -104,6 +105,13 @@ endif()
# Include xSDK default CMake file.
include("${CMAKE_CURRENT_SOURCE_DIR}/config/XSDKDefaults.cmake")
# Path to MFEM's CMake modules and utilities.
set(MFEM_CMAKE_PATH ${PROJECT_SOURCE_DIR}/config)
set(CMAKE_MODULE_PATH ${MFEM_CMAKE_PATH}/cmake/modules)
# Load MFEM CMake utilities.
include(MfemCmakeUtilities)
# Enable languages.
enable_language(CXX)
if (MINGW)
@@ -116,16 +124,15 @@ if (MFEM_USE_CUDA)
if (MFEM_USE_HIP)
message(FATAL_ERROR " *** MFEM_USE_HIP cannot be combined with MFEM_USE_CUDA.")
endif()
# CUDAToolkit was added in CMake 3.17, so we require at least CMake 3.17 when
# CUDA is enabled:
if (CMAKE_VERSION VERSION_LESS 3.17.0)
message(FATAL_ERROR "CUDA support requires CMake >= 3.17")
endif()
# Use ${CMAKE_CXX_COMPILER} as the cuda host compiler.
if (NOT CMAKE_CUDA_HOST_COMPILER)
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
endif()
enable_language(CUDA)
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
"CUDA standard to use.")
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL
"Force the use of the chosen CUDA standard.")
set(CMAKE_CUDA_EXTENSIONS OFF CACHE BOOL "Enable CUDA standard extensions.")
set(CUDA_FLAGS "--expt-extended-lambda")
if (CMAKE_VERSION VERSION_LESS 3.18.0)
set(CUDA_FLAGS "-arch=${CUDA_ARCH} ${CUDA_FLAGS}")
@@ -140,18 +147,19 @@ if (MFEM_USE_CUDA)
set(CUDA_ARCH "CMAKE_CUDA_ARCHITECTURES: ${CMAKE_CUDA_ARCHITECTURES}")
endif()
message(STATUS "Using CUDA architecture: ${CUDA_ARCH}")
if (CMAKE_VERSION VERSION_LESS 3.12.0)
# CMake versions 3.8 and 3.9 require this to work; 3.10 and 3.11 are not
# tested and may not actually need this (but should be ok to keep).
set(CUDA_FLAGS "-ccbin=${CMAKE_CXX_COMPILER} ${CUDA_FLAGS}")
set(CMAKE_CUDA_HOST_LINK_LAUNCHER ${CMAKE_CXX_COMPILER})
endif()
enable_language(CUDA)
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
"CUDA standard to use.")
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL
"Force the use of the chosen CUDA standard.")
set(CMAKE_CUDA_EXTENSIONS OFF CACHE BOOL "Enable CUDA standard extensions.")
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${CUDA_FLAGS}")
find_package(CUDAToolkit REQUIRED)
set(CUSPARSE_FOUND TRUE)
set(CUBLAS_FOUND TRUE)
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
# Initialize CUSPARSE_LIBRARIES and CUBLAS_LIBRARIES:
mfem_culib_set_libraries(CUSPARSE cusparse)
mfem_culib_set_libraries(CUBLAS cublas)
endif()
if (XSDK_ENABLE_C)
@@ -164,13 +172,6 @@ endif()
# Suppress warnings about MACOSX_RPATH
set(CMAKE_MACOSX_RPATH OFF CACHE BOOL "")
# CMake needs to know where to find things
set(MFEM_CMAKE_PATH ${PROJECT_SOURCE_DIR}/config)
set(CMAKE_MODULE_PATH ${MFEM_CMAKE_PATH}/cmake/modules)
# Load MFEM CMake utilities.
include(MfemCmakeUtilities)
string(TOUPPER "${PROJECT_NAME}" PROJECT_NAME_UC)
mfem_version_to_int(${${PROJECT_NAME}_VERSION} ${PROJECT_NAME_UC}_VERSION)
set(${PROJECT_NAME_UC}_VERSION_STRING ${${PROJECT_NAME}_VERSION})
+3
View File
@@ -120,7 +120,9 @@ The MFEM source code has the following structure:
| └── superlu
├── fem
│ ├── ceed
│ ├── eltrans
│ ├── fe
│ ├── gslib
│ ├── integ
│ ├── lor
│ ├── moonolith
@@ -128,6 +130,7 @@ The MFEM source code has the following structure:
│ └── tmop
├── general
├── linalg
│ ├── batched
│ └── simd
├── mesh
│ └── submesh
+22 -4
View File
@@ -122,9 +122,14 @@ Parallel build:
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
CUDA build:
(this build requires CMake 3.8 or newer)
(this build requires CMake 3.17 or newer)
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_CUDA=YES
cmake <mfem-source-dir> -DMFEM_USE_CUDA=YES -DCUDA_ARCH=sm_70
make -j 4
HIP build:
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 -DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
make -j 4
Example codes (serial/parallel, depending on the build):
@@ -1002,7 +1007,7 @@ Configuration variables (CMake)
===============================
See the configuration file config/defaults.cmake for the default settings.
Note: the option MFEM_USE_CUDA requires CMake version 3.8 or newer!
Note: the option MFEM_USE_CUDA requires CMake version 3.17 or newer!
Non-standard CMake variables for compilers:
CXX - If set, overwrite the auto-detected C++ compiler, serial build
@@ -1161,6 +1166,19 @@ larger problems, there are two options:
1. Building hypre with '--enable-bigint' defines the local and global indices to
be 64-bit. This is convenient, but requires more memory than necessary.
2. Building hypre with '--enable-mixedint' defines the local indiced to be
2. Building hypre with '--enable-mixedint' defines the local indices to be
32-bit, while using a 64-bit storage for global indices. This option is
currently tested only in ex1p, and may not work in more general settings.
Specific options for HIP
========================
MFEM expects the `ROCM_PATH` environment variable to be set to the path of the
ROCM install, as well as having `$ROCM_PATH/bin` in `PATH`.
Specific options for RAJA+HIP+MPI
=================================
RAJA uses CMake's built-in HIP support (added in CMake 3.21), while MFEM uses
the older HIP C++ library build/linkage. To ensure proper build and linkage
check that `CMAKE_CXX_COMPILER` and `CMAKE_HIP_COMPILER` are set to the same
compiler. This is especially important when using an MPI compiler (for example
crayCC) where some linker flags may get dropped if these two are not identical.
+14 -4
View File
@@ -25,7 +25,12 @@ if (HYPRE_FOUND)
find_package(rocsparse REQUIRED)
find_package(rocrand REQUIRED)
endif()
return()
if (HYPRE_LIBRARIES AND HYPRE_INCLUDE_DIRS AND HYPRE_VERSION)
find_package_handle_standard_args(HYPRE
REQUIRED_VARS HYPRE_LIBRARIES HYPRE_INCLUDE_DIRS HYPRE_VERSION
)
return()
endif()
endif()
include(MfemCmakeUtilities)
@@ -77,9 +82,10 @@ endif()
if (HYPRE_FOUND AND HYPRE_USING_CUDA)
find_package(CUDAToolkit REQUIRED)
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
get_target_property(CURAND_LIBRARIES CUDA::curand LOCATION)
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
# Initialize CUSPARSE_LIBRARIES, CURAND_LIBRARIES, and CUBLAS_LIBRARIES:
mfem_culib_set_libraries(CUSPARSE cusparse)
mfem_culib_set_libraries(CURAND curand)
mfem_culib_set_libraries(CUBLAS cublas)
list(APPEND HYPRE_LIBRARIES ${CUSPARSE_LIBRARIES} ${CURAND_LIBRARIES}
${CUBLAS_LIBRARIES})
set(HYPRE_LIBRARIES ${HYPRE_LIBRARIES} CACHE STRING
@@ -95,3 +101,7 @@ if (HYPRE_FOUND AND HYPRE_USING_HIP)
"HYPRE libraries + dependencies." FORCE)
message(STATUS "Updated HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}")
endif()
find_package_handle_standard_args(HYPRE
REQUIRED_VARS HYPRE_LIBRARIES HYPRE_INCLUDE_DIRS HYPRE_VERSION
)
+4 -2
View File
@@ -19,8 +19,10 @@ mfem_find_package(MAGMA MAGMA MAGMA_DIR "include" "magma.h" "lib" "magma"
"Paths to headers required by MAGMA." "Libraries required by MAGMA.")
if (MAGMA_FOUND AND MFEM_USE_CUDA)
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
find_package(CUDAToolkit REQUIRED)
# Initialize CUSPARSE_LIBRARIES and CUBLAS_LIBRARIES:
mfem_culib_set_libraries(CUSPARSE cusparse)
mfem_culib_set_libraries(CUBLAS cublas)
list(APPEND MAGMA_LIBRARIES ${CUSPARSE_LIBRARIES} ${CUBLAS_LIBRARIES})
set(MAGMA_LIBRARIES ${MAGMA_LIBRARIES} CACHE STRING
"MAGMA libraries + dependencies." FORCE)
+18 -10
View File
@@ -123,16 +123,9 @@ macro(add_mfem_miniapp MFEM_EXE_NAME)
# If CUDA is enabled, tag source files to be compiled with nvcc.
if (MFEM_USE_CUDA)
set_source_files_properties(${MAIN_LIST} ${EXTRA_SOURCES_LIST} PROPERTIES LANGUAGE CUDA)
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
else()
set(LIST_)
foreach(item IN LISTS EXTRA_OPTIONS_LIST)
list(APPEND LIST_ "-Xcompiler=${item}")
endforeach()
set(EXTRA_OPTIONS_LIST ${LIST_})
endif()
set_source_files_properties(${MAIN_LIST} ${EXTRA_SOURCES_LIST}
PROPERTIES LANGUAGE CUDA)
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
endif()
# Actually add the executable
@@ -157,6 +150,21 @@ macro(add_mfem_miniapp MFEM_EXE_NAME)
endif()
endmacro()
# Macro for setting variables like '<culib>_LIBRARIES' where <culib> is a CUDA
# library like cublas. This macro assumes that the CUDAToolkit module was loaded
# successfully. Example usage:
# mfem_culib_set_libraries(CUBLAS cublas)
macro(mfem_culib_set_libraries _CULIB _culib)
# The following command does not work with older CMake versions, e.g. 3.20:
# get_target_property(${_CULIB}_LIBRARIES CUDA::${_culib} LOCATION)
# Therefore, we use the respective internal variable:
set(${_CULIB}_LIBRARIES ${CUDA_${_culib}_LIBRARY})
if (NOT ${_CULIB}_LIBRARIES)
message(FATAL_ERROR
"Error setting ${_CULIB}_LIBRARIES: ${${_CULIB}_LIBRARIES}")
endif()
endmacro()
# Auxiliary function, used in mfem_find_package().
function(mfem_find_component Prefix DirVar IncSuffixes Header LibSuffixes Lib
+13 -16
View File
@@ -484,8 +484,8 @@ SIDRE_LIB = \
# Note that PUMI_DIR is needed -- it is used to check for gmi_sim.h
PUMI_DIR = @MFEM_DIR@/../pumi-2.1.0
PUMI_OPT = -I$(PUMI_DIR)/include
PUMI_LIB = -L$(PUMI_DIR)/lib -lpumi -lcrv -lma -lmds -lapf -lpcu -lgmi -lparma\
-llion -lmth -lapf_zoltan -lspr
PUMI_LIB = -L$(PUMI_DIR)/lib64 -L$(PUMI_DIR)/lib -lpumi -lcrv -lma -lmds -lapf\
-lpcu -lgmi -lparma -llion -lmth -lapf_zoltan -lspr
# HIOP
HIOP_DIR = @MFEM_DIR@/../hiop/install
@@ -568,7 +568,11 @@ RAJA_LIB = $(XLINKER)-rpath,$(RAJA_DIR)/lib -L$(RAJA_DIR)/lib -lRAJA $(CAMP_LIB)
# UMPIRE library configuration
UMPIRE_DIR = @MFEM_DIR@/../umpire
UMPIRE_OPT = -I$(UMPIRE_DIR)/include $(if $(CAMP_DIR), -I$(CAMP_DIR)/include)
UMPIRE_LIB = -L$(UMPIRE_DIR)/lib -lumpire $(CAMP_LIB)
UMPIRE_LIB = -L$(UMPIRE_DIR)/lib -L$(UMPIRE_DIR)/lib64 -lumpire $(CAMP_LIB)
ifdef FMT_DIR
UMPIRE_OPT += -I$(FMT_DIR)/include
UMPIRE_LIB += -L$(FMT_DIR)/lib -L$(FMT_DIR)/lib64 -lfmt
endif
# MKL CPardiso library configuration
MKL_CPARDISO_DIR ?=
@@ -600,20 +604,13 @@ TRIBOL_LIB = -L$(TRIBOL_DIR)/lib -ltribol -lredecomp -L$(AXOM_DIR)/lib -laxom_mi
-laxom_slam -laxom_slic -laxom_core
# Enzyme configuration
# If you want to enable automatic differentiation at compile time, use the
# options below, adapted to your configuration. To be more flexible, we
# recommend using the Enzyme plugin during link time optimization. One option is
# to add your options to the global compiler/linker flags like
#
# BASE_FLAGS += -flto
# CXX_XLINKER += -fuse-ld=lld -Wl,--lto-legacy-pass-manager\
# -Wl,-mllvm=-load=$(ENZYME_DIR)/LLDEnzyme-$(ENZYME_VERSION).so -Wl,
#
ENZYME_DIR ?= @MFEM_DIR@/../enzyme
ENZYME_VERSION ?= 14
ENZYME_OPT = -fno-experimental-new-pass-manager -Xclang -load -Xclang $(ENZYME_DIR)/ClangEnzyme-$(ENZYME_VERSION).so
ENZYME_DIR = @MFEM_DIR@/../enzyme
ENZYME_LLVM_VERSION = 19
ENZYME_OPT = -fplugin=$(ENZYME_DIR)/lib/ClangEnzyme-$(ENZYME_LLVM_VERSION).$(SO_EXT)
ENZYME_LIB = ""
ifeq ($(MFEM_USE_ENZYME),YES)
BASE_FLAGS = -std=c++17
endif
# Google Benchmark, SUNDIALS >= 6.4.0, STRUMPACK, RAJA, UMPIRE, and Tribol require C++14:
ifneq ($(filter YES,$(MFEM_USE_BENCHMARK) $(MFEM_USE_SUNDIALS) $(MFEM_USE_STRUMPACK) $(MFEM_USE_RAJA) $(MFEM_USE_UMPIRE) $(MFEM_USE_TRIBOL)),)
+3 -3
View File
@@ -17,7 +17,7 @@ We provide two containers, which you can either build or use directly from
In the above, "ghcr.io" means "GitHub Container Registry" and
is the [GitHub packages](https://github.com/features/packages) registry that supports
Docker images and other OCI artifacts.
Docker images and other OCI artifacts.
### Ubuntu
@@ -132,7 +132,7 @@ examples.
> Use this build for a development environment with spack and mfem
This container is also [provided on GitHub packages](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu-base),
This container is also [provided on GitHub packages](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu-base),
however you can build it locally too:
```bash
@@ -197,7 +197,7 @@ Average reduction factor = 0.140201
This container is likely ideal for someone that wants to develop mfem itself.
For other use cases, we recommend using the slimmer image. As an example,
if you want to develop with your own code base (and mfem as is in the container)
if you want to develop with your own code base (and mfem as is in the container)
you can bind to somewhere else in the container (e.g., src)
```bash
@@ -0,0 +1,20 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 186a320..39e5356 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.12.0...4.0.0)
project(METIS)
set(GKLIB_PATH "${CMAKE_SOURCE_DIR}/GKlib" CACHE PATH "path to GKlib")
diff --git a/GKlib/CMakeLists.txt b/GKlib/CMakeLists.txt
index 67b600a..44321a6 100644
--- a/GKlib/CMakeLists.txt
+++ b/GKlib/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.12.0...4.0.0)
project(GKlib)
get_filename_component(abs "." ABSOLUTE)
@@ -22,6 +22,7 @@ vcpkg_extract_source_archive_ex(
fix-linux-build-error.patch
install-metisConfig.patch
fix-INT_MIN_define.patch
cmake4.patch
)
vcpkg_configure_cmake(
+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
3
elements
1
1 7 0 1 2 3 4
boundary
5
1 3 3 2 1 0
2 2 0 1 4
3 2 1 2 4
4 2 2 3 4
5 2 3 0 4
vertices
5
3
-0.5 -0.5 0
0.5 -0.5 0
0.5 0.5 0
-0.5 0.5 0
0 0 0.7071067811865475
+108
View File
@@ -0,0 +1,108 @@
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
3
elements
16
1 5 12 13 16 15 21 22 25 24
1 6 9 12 8 18 21 17
1 6 11 8 12 20 17 21
1 6 3 2 12 6 5 15
1 6 11 12 2 14 15 5
1 6 3 12 0 4 13 1
1 6 9 0 12 10 1 13
1 7 12 13 22 21 19
1 7 15 16 13 12 7
1 7 12 21 24 15 23
1 7 9 12 21 18 19
1 7 11 20 21 12 23
1 7 9 10 13 12 19
1 7 11 12 15 14 23
1 7 3 6 15 12 7
1 7 3 12 13 4 7
boundary
39
1 3 5 6 3 2
2 2 6 7 3
2 2 7 4 3
3 3 3 4 1 0
4 2 11 12 8
4 2 9 8 12
5 2 3 12 2
5 2 11 2 12
6 3 0 1 10 9
7 2 10 19 9
7 2 18 9 19
8 3 8 9 18 17
9 2 4 13 1
9 2 10 1 13
10 2 4 7 13
10 2 16 13 7
11 3 13 16 25 22
12 2 10 13 19
12 2 22 19 13
13 2 6 15 7
13 2 16 7 15
14 2 6 5 15
14 2 14 15 5
15 2 14 23 15
15 2 24 15 23
16 3 16 15 24 25
17 3 5 2 11 14
18 2 3 0 12
18 2 9 12 0
19 3 11 8 17 20
20 2 14 11 23
20 2 20 23 11
21 2 18 21 17
21 2 20 17 21
22 2 18 19 21
22 2 22 21 19
23 3 21 22 25 24
24 2 20 21 23
24 2 24 23 21
vertices
26
3
0 -1 -1
1 -1 -1
-1 0 -1
0 0 -1
1 0 -1
-1 1 -1
0 1 -1
1 1 -1
-1 -1 0
0 -1 0
1 -1 0
-1 0 0
0 0 0
1 0 0
-1 1 0
0 1 0
1 1 0
-1 -1 1
0 -1 1
1 -1 1
-1 0 1
0 0 1
1 0 1
-1 1 1
0 1 1
1 1 1
+3 -3
View File
@@ -1,9 +1,9 @@
MFEM INLINE mesh v1.0
type = pyramid
nx = 4
ny = 4
nz = 4
nx = 2
ny = 2
nz = 2
sx = 1.0
sy = 1.0
sz = 1.0
+8 -5
View File
@@ -48,7 +48,7 @@ PROJECT_NAME = MFEM
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = v4.7.1
PROJECT_NUMBER = v4.8.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -951,7 +951,10 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/fem/ceed/integrators/nlconvection \
@MFEM_SOURCE_DIR@/fem/ceed/interface \
@MFEM_SOURCE_DIR@/fem/ceed/solvers \
@MFEM_SOURCE_DIR@/fem/eltrans \
@MFEM_SOURCE_DIR@/fem/fe \
@MFEM_SOURCE_DIR@/fem/gslib \
@MFEM_SOURCE_DIR@/fem/integ \
@MFEM_SOURCE_DIR@/fem/lor \
@MFEM_SOURCE_DIR@/fem/moonolith \
@MFEM_SOURCE_DIR@/fem/qinterp \
@@ -969,6 +972,8 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/adjoint \
@MFEM_SOURCE_DIR@/miniapps/autodiff \
@MFEM_SOURCE_DIR@/miniapps/common \
@MFEM_SOURCE_DIR@/miniapps/dpg \
@MFEM_SOURCE_DIR@/miniapps/dpg/util \
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
@MFEM_SOURCE_DIR@/miniapps/gslib \
@MFEM_SOURCE_DIR@/miniapps/hdiv-linear-solver \
@@ -986,12 +991,10 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/performance \
@MFEM_SOURCE_DIR@/miniapps/shifted \
@MFEM_SOURCE_DIR@/miniapps/solvers \
@MFEM_SOURCE_DIR@/miniapps/spde \
@MFEM_SOURCE_DIR@/miniapps/tools \
@MFEM_SOURCE_DIR@/miniapps/toys \
@MFEM_SOURCE_DIR@/miniapps/tribol \
@MFEM_SOURCE_DIR@/miniapps/spde \
@MFEM_SOURCE_DIR@/miniapps/dpg \
@MFEM_SOURCE_DIR@/miniapps/dpg/util
@MFEM_SOURCE_DIR@/miniapps/tribol
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+16 -16
View File
@@ -42,10 +42,10 @@ namespace mfem {
* - mfem::forall functions in forall.hpp
*
* <H3>Example codes</H3>
* - <a class="el" href="ex0_8cpp_source.html">Example 0</a>: simplest example, nodal H1 FEM for the Laplace problem
* - <a class="el" href="ex0p_8cpp_source.html">Example 0p</a>: simplest parallel example, nodal H1 FEM for the Laplace problem
* - <a class="el" href="examples_2ex1_8cpp_source.html">Example 1</a>: nodal H1 FEM for the Laplace problem (same discretization as ex0 but with more sophisticated options)
* - <a class="el" href="examples_2ex1p_8cpp_source.html">Example 1p</a>: parallel nodal H1 FEM for the Laplace problem (same discretization as ex0p but with more sophisticated options)
* - <a class="el" href="ex0_8cpp_source.html">Example 0</a>: simplest example, nodal H1 FEM for the Poisson problem
* - <a class="el" href="ex0p_8cpp_source.html">Example 0p</a>: simplest parallel example, nodal H1 FEM for the Poisson problem
* - <a class="el" href="examples_2ex1_8cpp_source.html">Example 1</a>: nodal H1 FEM for the Poisson problem (same discretization as ex0 but with more sophisticated options)
* - <a class="el" href="examples_2ex1p_8cpp_source.html">Example 1p</a>: parallel nodal H1 FEM for the Poisson problem (same discretization as ex0p but with more sophisticated options)
* - <a class="el" href="ex2_8cpp_source.html">Example 2</a>: vector FEM for linear elasticity
* - <a class="el" href="ex2p_8cpp_source.html">Example 2p</a>: parallel vector FEM for linear elasticity
* - <a class="el" href="ex3_8cpp_source.html">Example 3</a>: Nedelec H(curl) FEM for the definite Maxwell problem
@@ -54,12 +54,12 @@ namespace mfem {
* - <a class="el" href="ex4p_8cpp_source.html">Example 4p</a>: parallel Raviart-Thomas H(div) FEM for the grad-div problem
* - <a class="el" href="ex5_8cpp_source.html">Example 5</a>: mixed pressure-velocity FEM for the Darcy problem
* - <a class="el" href="ex5p_8cpp_source.html">Example 5p</a>: parallel mixed pressure-velocity FEM for the Darcy problem
* - <a class="el" href="ex6_8cpp_source.html">Example 6</a>: non-conforming adaptive mesh refinement for the Laplace problem
* - <a class="el" href="ex6p_8cpp_source.html">Example 6p</a>: parallel non-conforming adaptive mesh refinement for the Laplace problem
* - <a class="el" href="ex7_8cpp_source.html">Example 7</a>: Laplace problem on a surface (the unit sphere)
* - <a class="el" href="ex7p_8cpp_source.html">Example 7p</a>: parallel Laplace problem on a surface (the unit sphere)
* - <a class="el" href="ex8_8cpp_source.html">Example 8</a>: Discontinuous Petrov-Galerkin (DPG) for the Laplace problem
* - <a class="el" href="ex8p_8cpp_source.html">Example 8p</a>: parallel Discontinuous Petrov-Galerkin (DPG) for the Laplace problem
* - <a class="el" href="ex6_8cpp_source.html">Example 6</a>: non-conforming adaptive mesh refinement for the Poisson problem
* - <a class="el" href="ex6p_8cpp_source.html">Example 6p</a>: parallel non-conforming adaptive mesh refinement for the Poisson problem
* - <a class="el" href="ex7_8cpp_source.html">Example 7</a>: screened Poisson problem on a surface (the unit sphere)
* - <a class="el" href="ex7p_8cpp_source.html">Example 7p</a>: parallel screened Poisson problem on a surface (the unit sphere)
* - <a class="el" href="ex8_8cpp_source.html">Example 8</a>: Discontinuous Petrov-Galerkin (DPG) for the Poisson problem
* - <a class="el" href="ex8p_8cpp_source.html">Example 8p</a>: parallel Discontinuous Petrov-Galerkin (DPG) for the Poisson problem
* - <a class="el" href="ex9_8cpp_source.html">Example 9</a>: Discontinuous Galerkin (DG) time-dependent advection
* - <a class="el" href="ex9p_8cpp_source.html">Example 9p</a>: parallel Discontinuous Galerkin (DG) time-dependent advection
* - <a class="el" href="ex10_8cpp_source.html">Example 10</a>: time-dependent implicit nonlinear elasticity
@@ -67,8 +67,8 @@ namespace mfem {
* - <a class="el" href="ex11p_8cpp_source.html">Example 11p</a>: parallel Laplace eigensolver
* - <a class="el" href="ex12p_8cpp_source.html">Example 12p</a>: parallel linear elasticity eigensolver
* - <a class="el" href="ex13p_8cpp_source.html">Example 13p</a>: parallel Maxwell eigensolver
* - <a class="el" href="ex14_8cpp_source.html">Example 14</a>: Discontinuous Galerkin (DG) for the Laplace problem
* - <a class="el" href="ex14p_8cpp_source.html">Example 14p</a>: parallel Discontinuous Galerkin (DG) for the Laplace problem
* - <a class="el" href="ex14_8cpp_source.html">Example 14</a>: Discontinuous Galerkin (DG) for the Poisson problem
* - <a class="el" href="ex14p_8cpp_source.html">Example 14p</a>: parallel Discontinuous Galerkin (DG) for the Poisson problem
* - <a class="el" href="ex15_8cpp_source.html">Example 15</a>: dynamic AMR for Laplace with prescribed time-dependent source
* - <a class="el" href="ex15p_8cpp_source.html">Example 15p</a>: parallel dynamic AMR for Laplace with prescribed time-dependent source
* - <a class="el" href="ex16_8cpp_source.html">Example 16</a>: time-dependent nonlinear heat equation
@@ -90,8 +90,8 @@ namespace mfem {
* - <a class="el" href="ex24p_8cpp_source.html">Example 24p</a>: parallel mixed finite element spaces and interpolators
* - <a class="el" href="ex25_8cpp_source.html">Example 25</a>: simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
* - <a class="el" href="ex25p_8cpp_source.html">Example 25p</a>: parallel simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
* - <a class="el" href="ex26_8cpp_source.html">Example 26</a>: multigrid preconditioner for the Laplace problem using nodal H1 FEM
* - <a class="el" href="ex26p_8cpp_source.html">Example 26p</a>: parallel multigrid preconditioner for the Laplace problem using nodal H1 FEM
* - <a class="el" href="ex26_8cpp_source.html">Example 26</a>: multigrid preconditioner for the Poisson problem using nodal H1 FEM
* - <a class="el" href="ex26p_8cpp_source.html">Example 26p</a>: parallel multigrid preconditioner for the Poisson problem using nodal H1 FEM
* - <a class="el" href="ex27_8cpp_source.html">Example 27</a>: boundary conditions for the Laplace problem
* - <a class="el" href="ex27p_8cpp_source.html">Example 27p</a>: parallel boundary conditions for the Laplace problem
* - <a class="el" href="ex28_8cpp_source.html">Example 28</a>: sliding contact in elasticity
@@ -230,8 +230,8 @@ namespace mfem {
* - <a class="el" href="parheat_8cpp_source.html">Optimization gradients</a>: Gradients of PDE-constrained function
* - <a class="el" href="par__example_8cpp_source.html">Parallel AD</a>: Parallel p-Laplacian example
* - <a class="el" href="seq__example_8cpp_source.html">Serial AD</a>: Serial p-Laplacian example
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Laplace problem
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Laplace problem
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Poisson problem
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Poisson problem
* - <a class="el" href="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
* - <a class="el" href="contact-patch-test_8cpp_source.html">Contact</a>: mortar contact patch test for elasticity
* - <a class="el" href="multidomain_8cpp_source.html">Multidomain miniapp</a>: Multidomain and Submesh demonstration miniapp
+1 -1
View File
@@ -12,7 +12,7 @@
// ex1 --amgx-file precon.json --amgx-preconditioner -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -10,7 +10,7 @@
// mpirun -np 4 ex1p --amgx-file amg_pcg.json
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+537 -159
View File
File diff suppressed because it is too large Load Diff
+70
View File
@@ -0,0 +1,70 @@
# Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
# at the Lawrence Livermore National Laboratory. All Rights reserved. See files
# LICENSE and NOTICE for details. LLNL-CODE-806117.
#
# This file is part of the MFEM library. For more information and source code
# availability visit https://mfem.org.
#
# MFEM is free software; you can redistribute it and/or modify it under the
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
# Use the MFEM build directory
MFEM_DIR ?= ../..
MFEM_BUILD_DIR ?= ../..
MFEM_INSTALL_DIR ?= ../../mfem
SRC = $(if $(MFEM_DIR:../..=),$(MFEM_DIR)/examples/dfem/,)
CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
$(wildcard $(MFEM_INSTALL_DIR)/share/mfem/config.mk))
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_EXAMPLES =
PAR_EXAMPLES = laghos
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
EXAMPLES = $(PAR_EXAMPLES) $(SEQ_EXAMPLES)
endif
.SUFFIXES:
.SUFFIXES: .o .cpp .mk
.PHONY: all clean clean-build
# Remove built-in rule
%: %.cpp
# Replace the default implicit rule for *.cpp files
%: $(SRC)%.cpp $(MFEM_LIB_FILE) $(CONFIG_MK)
$(MFEM_CXX) $(MFEM_FLAGS) $< -o $@ $(MFEM_LIBS)
all: $(EXAMPLES)
ifeq ($(MFEM_USE_ENZYME),NO)
$(EXAMPLES):
$(error MFEM is not configured with ENZYME)
endif
MFEM_TESTS = EXAMPLES
include $(MFEM_TEST_MK)
# Testing: Parallel vs. serial runs
RUN_MPI_NP = $(MFEM_MPIEXEC) $(MFEM_MPIEXEC_NP)
RUN_MPI = $(RUN_MPI_NP) $(MFEM_MPI_NP)
SERIAL_NAME := Serial dFEM example
PARALLEL_NAME := Parallel dFEM example
%-test-par: %
@$(call mfem-test,$<, $(RUN_MPI), $(PARALLEL_NAME))
%-test-seq: %
@$(call mfem-test,$<,, $(SERIAL_NAME))
# Generate an error message if the MFEM library is not built and exit
$(MFEM_LIB_FILE):
$(error The MFEM library is not built)
clean: clean-build
clean-build:
rm -f *.o *~ $(SEQ_EXAMPLES) $(PAR_EXAMPLES)
rm -rf *.dSYM *.TVD.*breakpoints
+1 -1
View File
@@ -7,7 +7,7 @@
// ex0 -m ../data/square-disc.mesh -o 2
//
// Description: This example code demonstrates the most basic usage of MFEM to
// define a simple finite element discretization of the Laplace
// define a simple finite element discretization of the Poisson
// problem -Delta u = 1 with zero Dirichlet boundary conditions.
// General 2D/3D mesh files and finite element polynomial degrees
// can be specified by command line options.
+1 -1
View File
@@ -8,7 +8,7 @@
//
// Description: This example code demonstrates the most basic parallel usage of
// MFEM to define a simple finite element discretization of the
// Laplace problem -Delta u = 1 with zero Dirichlet boundary
// Poisson problem -Delta u = 1 with zero Dirichlet boundary
// conditions. General 2D/3D serial mesh files and finite element
// polynomial degrees can be specified by command line options.
+1 -1
View File
@@ -50,7 +50,7 @@
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -27,7 +27,7 @@
//
// Description: This example code demonstrates the use of MFEM to define a
// discontinuous Galerkin (DG) finite element discretization of
// the Laplace problem -Delta u = 1 with homogeneous Dirichlet
// the Poisson problem -Delta u = 1 with homogeneous Dirichlet
// boundary conditions. Finite element spaces of any order,
// including zero on regular grids, are supported. The example
// highlights the use of discontinuous spaces and DG-specific face
+1 -1
View File
@@ -26,7 +26,7 @@
//
// Description: This example code demonstrates the use of MFEM to define a
// discontinuous Galerkin (DG) finite element discretization of
// the Laplace problem -Delta u = 1 with homogeneous Dirichlet
// the Poisson problem -Delta u = 1 with homogeneous Dirichlet
// boundary conditions. Finite element spaces of any order,
// including zero on regular grids, are supported. The example
// highlights the use of discontinuous spaces and DG-specific face
+2 -2
View File
@@ -159,7 +159,7 @@ int main(int argc, char *argv[])
FiniteElementSpace fespace(&mesh, &fec);
// 6. As in Example 1p, we set up bilinear and linear forms corresponding to
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the inner loop.
BilinearForm a(&fespace);
LinearForm b(&fespace);
@@ -446,7 +446,7 @@ real_t bdr_func(const Vector &pt, real_t t)
return composite_func(pt, t, front, ball);
}
// Laplace of the exact solution, used for the right hand side.
// Laplacian of the exact solution, used for the right hand side.
real_t rhs_func(const Vector &pt, real_t t)
{
return composite_func(pt, t, front_laplace, ball_laplace);
+2 -2
View File
@@ -181,7 +181,7 @@ int main(int argc, char *argv[])
ParFiniteElementSpace fespace(&pmesh, &fec);
// 7. As in Example 1p, we set up bilinear and linear forms corresponding to
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the inner loop.
ParBilinearForm a(&fespace);
ParLinearForm b(&fespace);
@@ -507,7 +507,7 @@ real_t bdr_func(const Vector &pt, real_t t)
return composite_func(pt, t, front, ball);
}
// Laplace of the exact solution, used for the right hand side.
// Laplacian of the exact solution, used for the right hand side.
real_t rhs_func(const Vector &pt, real_t t)
{
return composite_func(pt, t, front_laplace, ball_laplace);
+1 -1
View File
@@ -45,7 +45,7 @@
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -94,7 +94,7 @@ WaveOperator::WaveOperator(FiniteElementSpace &f,
M->AddDomainIntegrator(new MassIntegrator());
M->Assemble();
// Apply Bcs
// Apply BCs
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
K->FormSystemMatrix(ess_tdof_list, Kmat);
M->FormSystemMatrix(ess_tdof_list, Mmat);
+1 -1
View File
@@ -17,7 +17,7 @@
// ex26 -m ../data/beam-hex.mesh -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions
// as in Example 1.
//
+1 -1
View File
@@ -14,7 +14,7 @@
// mpirun -np 4 ex26p -d ceed-cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions
// as in Example 1.
//
+1 -1
View File
@@ -10,7 +10,7 @@
// Description: This example code demonstrates the use of MFEM to define a
// finite element discretization of a PDE on a 2 dimensional
// surface embedded in a 3 dimensional domain. In this case we
// solve the Laplace problem -Div(sigma Grad u) = 1, with
// solve the Poisson problem -Div(sigma Grad u) = 1, with
// homogeneous Dirichlet boundary conditions, where sigma is an
// anisotropic diffusion constant defined as a 3x3 matrix
// coefficient.
+1 -1
View File
@@ -10,7 +10,7 @@
// Description: This example code demonstrates the use of MFEM to define a
// finite element discretization of a PDE on a 2 dimensional
// surface embedded in a 3 dimensional domain. In this case we
// solve the Laplace problem -Div(sigma Grad u) = 1, with
// solve the Poisson problem -Div(sigma Grad u) = 1, with
// homogeneous Dirichlet boundary conditions, where sigma is an
// anisotropic diffusion constant defined as a 3x3 matrix
// coefficient.
+3 -3
View File
@@ -390,7 +390,7 @@ public:
/**
@brief Class for surface linearform integrator
@brief Class for surface linear form integrator
Integrator to demonstrate the use of the surface integration rule on an
implicit surface defined by a level-set.
@@ -460,7 +460,7 @@ public:
};
/**
@brief Class for subdomain linearform integrator
@brief Class for subdomain linear form integrator
Integrator to demonstrate the use of the subdomain integration rule within
an area defined by an implicit surface defined by a level-set.
@@ -546,7 +546,7 @@ int main(int argc, char *argv[])
args.AddOption(&ref_levels, "-r", "--refine", "Number of meh refinements");
args.AddOption(&method, "-m", "--method",
"Cut integration method: 0 for moments-based, 1 for Algoim.");
args.AddOption(&inttype, "-i", "--integrationtype",
args.AddOption(&inttype, "-i", "--integration-type",
"IntegrationType to demonstrate");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
+1 -1
View File
@@ -16,7 +16,7 @@
//
// The particular problem being solved here is nearly the same
// as that in example 1 i.e. a simple finite element
// discretization of the Laplace problem -Delta u = 1 with
// discretization of the Poisson problem -Delta u = 1 with
// homogeneous Dirichlet boundary conditions and, in this case,
// an inhomogeneous diffusion coefficient. The diffusion
// coefficient is given a small default value throughout the
+1 -1
View File
@@ -16,7 +16,7 @@
//
// The particular problem being solved here is nearly the same
// as that in example 1 i.e. a simple finite element
// discretization of the Laplace problem -Delta u = 1 with
// discretization of the Poisson problem -Delta u = 1 with
// homogeneous Dirichlet boundary conditions and, in this case,
// an inhomogeneous diffusion coefficient. The diffusion
// coefficient is given a small default value throughout the
+5 -7
View File
@@ -65,6 +65,7 @@ int main(int argc, char *argv[])
bool static_cond = false;
bool hybridization = false;
bool pa = false;
bool ea = false;
const char *device_config = "cpu";
bool visualization = 1;
@@ -83,18 +84,14 @@ int main(int argc, char *argv[])
"--no-hybridization", "Enable hybridization.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
"--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
args.ParseCheck();
kappa = freq * M_PI;
// 2. Enable hardware devices such as GPUs, and programming models such as
@@ -166,6 +163,7 @@ int main(int argc, char *argv[])
Coefficient *beta = new ConstantCoefficient(1.0);
BilinearForm *a = new BilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
if (ea) { a->SetAssemblyLevel(AssemblyLevel::ELEMENT); }
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
+5 -13
View File
@@ -71,6 +71,7 @@ int main(int argc, char *argv[])
bool static_cond = false;
bool hybridization = false;
bool pa = false;
bool ea = false;
const char *device_config = "cpu";
bool visualization = 1;
@@ -89,24 +90,14 @@ int main(int argc, char *argv[])
"--no-hybridization", "Enable hybridization.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
"--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
args.ParseCheck();
kappa = freq * M_PI;
// 3. Enable hardware devices such as GPUs, and programming models such as
@@ -194,6 +185,7 @@ int main(int argc, char *argv[])
Coefficient *beta = new ConstantCoefficient(1.0);
ParBilinearForm *a = new ParBilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
if (ea) { a->SetAssemblyLevel(AssemblyLevel::ELEMENT); }
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
+2 -2
View File
@@ -25,7 +25,7 @@
// ex6 -pa -d ceed-cuda:/gpu/cuda/shared
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Laplace
// refinement loop. The problem being solved is again the Poisson
// equation -Delta u = 1 with homogeneous Dirichlet boundary
// conditions. The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
@@ -113,7 +113,7 @@ int main(int argc, char *argv[])
FiniteElementSpace fespace(&mesh, &fec);
// 6. As in Example 1, we set up bilinear and linear forms corresponding to
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the main loop.
BilinearForm a(&fespace);
if (pa)
+104 -7
View File
@@ -3,6 +3,7 @@
// Compile with: make ex6p
//
// Sample runs: mpirun -np 4 ex6p -m ../data/star-hilbert.mesh -o 2
// mpirun -np 4 ex6p -m ../data/star-hilbert.mesh -pref
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -rm 1 -o 1
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -rm 1 -o 2 -h1
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -o 2 -cs
@@ -28,7 +29,7 @@
// mpirun -np 4 ex6p -pa -d ceed-cuda:/gpu/cuda/shared
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Laplace
// refinement loop. The problem being solved is again the Poisson
// equation -Delta u = 1 with homogeneous Dirichlet boundary
// conditions. The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
@@ -41,6 +42,12 @@
// from coarse to fine meshes, restarting from a checkpoint, as
// well as persistent GLVis visualization are also illustrated.
//
// There is also the option to use hp-refinement. Real
// applications should use some problem-dependent criteria for
// selecting between h- and p-refinement, but in this example, we
// simply alternate between refinement types to demonstrate the
// capabilities.
//
// We recommend viewing Example 1 before viewing this example.
#include "mfem.hpp"
@@ -69,6 +76,8 @@ int main(int argc, char *argv[])
bool smooth_rt = true;
bool restart = false;
bool visualization = true;
bool rebalance = true;
bool usePRefinement = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -90,6 +99,10 @@ int main(int argc, char *argv[])
"Stop after reaching this many degrees of freedom.");
args.AddOption(&smooth_rt, "-rt", "--smooth-rt", "-h1", "--smooth-h1",
"Represent the smooth flux in RT or vector H1 space.");
args.AddOption(&usePRefinement, "-pref", "--p-refine", "-no-pref",
"--no-p-refine", "Alternate between h- and p-refinement.");
args.AddOption(&rebalance, "-reb", "--rebalance", "-no-reb",
"--no-rebalance", "Load balance the nonconforming mesh.");
args.AddOption(&restart, "-res", "--restart", "-no-res", "--no-restart",
"Restart computation from the last checkpoint.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -109,6 +122,15 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
if (usePRefinement && rebalance)
{
rebalance = false;
if (myid == 0)
{
cout << "Load balancing is not performed with p-refinements.\n";
}
}
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
@@ -186,7 +208,7 @@ int main(int argc, char *argv[])
ParFiniteElementSpace fespace(pmesh, &fec);
// 11. As in Example 1p, we set up bilinear and linear forms corresponding to
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the main loop.
ParBilinearForm a(&fespace);
if (pa)
@@ -321,7 +343,15 @@ int main(int argc, char *argv[])
if (visualization)
{
sout << "parallel " << num_procs << " " << myid << "\n";
sout << "solution\n" << *pmesh << x << flush;
if (usePRefinement)
{
std::unique_ptr<GridFunction> vis_x = x.ProlongateToMaxOrder();
sout << "solution\n" << *pmesh << *vis_x << flush;
}
else
{
sout << "solution\n" << *pmesh << x << flush;
}
}
if (global_dofs >= max_dofs)
@@ -337,8 +367,31 @@ int main(int argc, char *argv[])
// estimator to obtain element errors, then it selects elements to be
// refined and finally it modifies the mesh. The Stop() method can be
// used to determine if a stopping criterion was met.
refiner.Apply(*pmesh);
if (refiner.Stop())
// Simply alternate between h- and p-refinement.
const bool pRefine = usePRefinement && ((it % 2) == 1);
bool stop = false;
Array<pRefinement> prefinements;
if (pRefine)
{
Array<Refinement> refinements;
refiner.MarkWithoutRefining(*pmesh, refinements);
stop = pmesh->ReduceInt(refinements.Size()) == 0LL;
prefinements.SetSize(refinements.Size());
for (int i=0; i<refinements.Size(); ++i)
{
prefinements[i].index = refinements[i].index;
prefinements[i].delta = 1; // Increase the element order by 1
}
}
else
{
refiner.Apply(*pmesh);
stop = refiner.Stop();
}
if (stop)
{
if (myid == 0)
{
@@ -352,12 +405,20 @@ int main(int argc, char *argv[])
// to any GridFunctions over the space. In this case, the update
// matrix is an interpolation matrix so the updated GridFunction will
// still represent the same function as before refinement.
fespace.Update();
if (pRefine)
{
fespace.PRefineAndUpdate(prefinements);
}
else
{
fespace.Update();
}
x.Update();
// 25. Load balance the mesh, and update the space and solution. Currently
// available only for nonconforming meshes.
if (pmesh->Nonconforming())
if (pmesh->Nonconforming() && rebalance)
{
pmesh->Rebalance();
@@ -389,6 +450,42 @@ int main(int argc, char *argv[])
}
}
// Save result
if (usePRefinement)
{
L2_FECollection fecL2(0, dim);
ParFiniteElementSpace l2fespace(pmesh, &fecL2);
ParGridFunction xo(&l2fespace); // Element order field
xo = 0.0;
for (int e=0; e<pmesh->GetNE(); ++e)
{
const int p_elem = fespace.GetElementOrder(e);
Array<int> dofs;
l2fespace.GetElementDofs(e, dofs);
xo[dofs[0]] = p_elem;
}
ostringstream mesh_name, sol_name, order_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
order_name << "order." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->ParPrint(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
std::unique_ptr<ParGridFunction> vis_x = x.ProlongateToMaxOrder();
vis_x->Save(sol_ofs);
ofstream order_ofs(order_name.str().c_str());
order_ofs.precision(8);
xo.Save(order_ofs);
}
delete smooth_flux_fes;
delete smooth_flux_fec;
delete pmesh;
+2 -2
View File
@@ -9,8 +9,8 @@
//
// Description: This example code demonstrates the use of MFEM to define a
// triangulation of a unit sphere and a simple isoparametric
// finite element discretization of the Laplace problem with mass
// term, -Delta u + u = f.
// finite element discretization of the screened Poisson problem,
// -Delta u + u = f.
//
// The example highlights mesh generation, the use of mesh
// refinement, high-order meshes and finite elements, as well as
+2 -2
View File
@@ -9,8 +9,8 @@
//
// Description: This example code demonstrates the use of MFEM to define a
// triangulation of a unit sphere and a simple isoparametric
// finite element discretization of the Laplace problem with mass
// term, -Delta u + u = f.
// finite element discretization of the screened Poisson problem,
// -Delta u + u = f.
//
// The example highlights mesh generation, the use of mesh
// refinement, high-order meshes and finite elements, as well as
+1 -1
View File
@@ -15,7 +15,7 @@
//
// Description: This example code demonstrates the use of the Discontinuous
// Petrov-Galerkin (DPG) method in its primal 2x2 block form as a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = f with homogeneous Dirichlet boundary conditions. We
// use high-order continuous trial space, a high-order interfacial
// (trace) space, and a high-order discontinuous test space
+1 -1
View File
@@ -14,7 +14,7 @@
//
// Description: This example code demonstrates the use of the Discontinuous
// Petrov-Galerkin (DPG) method in its primal 2x2 block form as a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = f with homogeneous Dirichlet boundary conditions. We
// use high-order continuous trial space, a high-order interfacial
// (trace) space, and a high-order discontinuous test space
+1 -1
View File
@@ -35,7 +35,7 @@
// ex1 -m ../../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -33,7 +33,7 @@
"id": "public-white",
"metadata": {},
"source": [
"This is the simplest MFEM example and a good starting point for new users. The example demonstrates the use of MFEM to define and solve an $H^1$ finite element discretization of the Laplace problem\n",
"This is the simplest MFEM example and a good starting point for new users. The example demonstrates the use of MFEM to define and solve an $H^1$ finite element discretization of the Poisson problem\n",
"\n",
"$$\n",
"-\\Delta u = 1\n",
+4 -1
View File
@@ -68,6 +68,9 @@ endif
ifeq ($(MFEM_USE_CALIPER),YES)
SUBDIRS += caliper
endif
ifeq ($(MFEM_USE_ENZYME),YES)
SUBDIRS += dfem
endif
SUBDIRS_ALL = $(addsuffix /all,$(SUBDIRS))
SUBDIRS_TEST = $(addsuffix /test,$(SUBDIRS))
@@ -189,7 +192,7 @@ clean-build:
clean-exec:
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh ex6p-checkpoint.*
@rm -rf Example5* Example9* Example15* Example16* Example23* ParaView
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.* order.*
@rm -f ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
@rm -f deformed.* velocity.* elastic_energy.* mode_* mode_deriv_* flux.*
@rm -f ex5-p-*.bp ex9-p-*.bp ex12-p-*.bp ex16-p-*.bp
+1 -1
View File
@@ -10,7 +10,7 @@
// mpirun -np 4 ex1p -pa -d cuda --petscopts rc_ex1p_device
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+2 -2
View File
@@ -8,7 +8,7 @@
// mpirun -np 4 ex6p -m ../../data/amr-quad.mesh -nonoverlapping
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Laplace
// refinement loop. The problem being solved is again the Poisson
// equation -Delta u = 1 with homogeneous Dirichlet boundary
// conditions. The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
@@ -131,7 +131,7 @@ int main(int argc, char *argv[])
ParFiniteElementSpace fespace(&pmesh, &fec);
// 7. As in Example 1p, we set up bilinear and linear forms corresponding to
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the main loop.
ParBilinearForm a(&fespace);
ParLinearForm b(&fespace);
+1 -1
View File
@@ -11,7 +11,7 @@
// creating a symbolic link to the above directory in ../../data.
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -12,7 +12,7 @@
// creating a symbolic link to the above directory in ../../data.
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -6,7 +6,7 @@
// Sample runs: mpirun -np 8 ex6p
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Laplace
// refinement loop. The problem being solved is again the Poisson
// equation -Delta u = 1 with homogeneous Dirichlet boundary
// conditions. The problem is solved on a sequence of meshes which
// are adapted in a conforming (tetrahedrons) manner according
+1 -1
View File
@@ -26,7 +26,7 @@
// mpirun -np 4 ex1p -m ../../data/mobius-strip.mesh
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// simple finite element discretization of the Poisson problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+7
View File
@@ -35,6 +35,7 @@ set(SRCS
integ/bilininteg_mass_ea.cpp
integ/bilininteg_mixedcurl_pa.cpp
integ/bilininteg_mixedvecgrad_pa.cpp
integ/bilininteg_trace_jump_ea.cpp
integ/bilininteg_transpose_ea.cpp
integ/bilininteg_vecdiffusion_mf.cpp
integ/bilininteg_vecdiffusion_pa.cpp
@@ -46,6 +47,7 @@ set(SRCS
integ/bilininteg_diffusion_kernels.cpp
integ/bilininteg_elasticity_kernels.cpp
integ/bilininteg_hcurl_kernels.cpp
integ/bilininteg_hdiv_ea.cpp
integ/bilininteg_hdiv_kernels.cpp
integ/bilininteg_hcurlhdiv_kernels.cpp
integ/bilininteg_mass_kernels.cpp
@@ -63,6 +65,7 @@ set(SRCS
dgmassinv.cpp
doftrans.cpp
eltrans.cpp
batchitrans.cpp
estimators.cpp
fe.cpp
fe/face_map_utils.cpp
@@ -73,6 +76,7 @@ set(SRCS
fe/fe_nd.cpp
fe/fe_nurbs.cpp
fe/fe_pos.cpp
fe/fe_pyramid.cpp
fe/fe_rt.cpp
fe/fe_ser.cpp
fe_coll.cpp
@@ -80,6 +84,7 @@ set(SRCS
geom.cpp
gridfunc.cpp
hybridization.cpp
hybridization_ext.cpp
intrules.cpp
intrules_cut.cpp
ceed/interface/basis.cpp
@@ -193,6 +198,7 @@ set(HDRS
fe/fe_nd.hpp
fe/fe_nurbs.hpp
fe/fe_pos.hpp
fe/fe_pyramid.hpp
fe/fe_rt.hpp
fe/fe_ser.hpp
fe_coll.hpp
@@ -201,6 +207,7 @@ set(HDRS
geom.hpp
gridfunc.hpp
hybridization.hpp
hybridization_ext.hpp
intrules.hpp
intrules_cut.hpp
kernel_dispatch.hpp
+2035
View File
File diff suppressed because it is too large Load Diff
+75 -50
View File
@@ -71,15 +71,11 @@ BilinearForm::BilinearForm(FiniteElementSpace * f)
sequence = f->GetSequence();
mat = mat_e = NULL;
extern_bfs = 0;
element_matrices = NULL;
static_cond = NULL;
hybridization = NULL;
precompute_sparsity = 0;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::LEGACY;
batch = 1;
ext = NULL;
}
BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
@@ -89,15 +85,11 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
sequence = f->GetSequence();
mat_e = NULL;
extern_bfs = 1;
element_matrices = NULL;
static_cond = NULL;
hybridization = NULL;
precompute_sparsity = ps;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::LEGACY;
batch = 1;
ext = NULL;
// Copy the pointers to the integrators
domain_integs = bf->domain_integs;
@@ -127,16 +119,16 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
break;
case AssemblyLevel::FULL:
SetDiagonalPolicy( DIAG_ONE ); // Only diagonal policy supported on device
ext = new FABilinearFormExtension(this);
ext.reset(new FABilinearFormExtension(this));
break;
case AssemblyLevel::ELEMENT:
ext = new EABilinearFormExtension(this);
ext.reset(new EABilinearFormExtension(this));
break;
case AssemblyLevel::PARTIAL:
ext = new PABilinearFormExtension(this);
ext.reset(new PABilinearFormExtension(this));
break;
case AssemblyLevel::NONE:
ext = new MFBilinearFormExtension(this);
ext.reset(new MFBilinearFormExtension(this));
break;
default:
MFEM_ABORT("BilinearForm: unknown assembly level");
@@ -145,14 +137,13 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
void BilinearForm::EnableStaticCondensation()
{
delete static_cond;
if (assembly != AssemblyLevel::LEGACY)
{
static_cond = NULL;
static_cond.reset();
MFEM_WARNING("Static condensation not supported for this assembly level");
return;
}
static_cond = new StaticCondensation(fes);
static_cond.reset(new StaticCondensation(fes));
if (static_cond->ReducesTrueVSize())
{
bool symmetric = false; // TODO
@@ -161,8 +152,7 @@ void BilinearForm::EnableStaticCondensation()
}
else
{
delete static_cond;
static_cond = NULL;
static_cond.reset();
}
}
@@ -170,15 +160,18 @@ void BilinearForm::EnableHybridization(FiniteElementSpace *constr_space,
BilinearFormIntegrator *constr_integ,
const Array<int> &ess_tdof_list)
{
delete hybridization;
if (assembly != AssemblyLevel::LEGACY)
if (assembly != AssemblyLevel::LEGACY && assembly != AssemblyLevel::ELEMENT)
{
delete constr_integ;
hybridization = NULL;
hybridization.reset();
MFEM_WARNING("Hybridization not supported for this assembly level");
return;
}
hybridization = new Hybridization(fes, constr_space);
hybridization.reset(new Hybridization(fes, constr_space));
if (assembly == AssemblyLevel::ELEMENT)
{
hybridization->EnableDeviceExecution();
}
hybridization->SetConstraintIntegrator(constr_integ);
hybridization->Init(ess_tdof_list);
}
@@ -231,8 +224,8 @@ void BilinearForm::Finalize (int skip_zeros)
if (!static_cond) { mat->Finalize(skip_zeros); }
if (mat_e) { mat_e->Finalize(skip_zeros); }
if (static_cond) { static_cond->Finalize(); }
if (hybridization) { hybridization->Finalize(); }
}
if (hybridization) { hybridization->Finalize(); }
}
void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi)
@@ -465,6 +458,10 @@ void BilinearForm::Assemble(int skip_zeros)
if (ext)
{
ext->Assemble();
if (hybridization)
{
hybridization->AssembleElementMatrices(GetElementMatrices());
}
return;
}
@@ -835,7 +832,19 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
{
if (ext)
{
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
if (hybridization)
{
FormSystemMatrix(ess_tdof_list, A);
ConstrainedOperator A_constrained(this, ess_tdof_list);
A_constrained.EliminateRHS(x, b);
hybridization->ReduceRHS(b, B);
X.SetSize(B.Size());
X = 0.0;
}
else
{
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
}
return;
}
const SparseMatrix *P = fes->GetConformingProlongation();
@@ -903,7 +912,16 @@ void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
{
if (ext)
{
ext->FormSystemMatrix(ess_tdof_list, A);
if (hybridization)
{
const int remove_zeros = 0;
Finalize(remove_zeros);
A.Reset(&hybridization->GetMatrix(), false);
}
else
{
ext->FormSystemMatrix(ess_tdof_list, A);
}
return;
}
@@ -944,7 +962,7 @@ void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
void BilinearForm::RecoverFEMSolution(const Vector &X,
const Vector &b, Vector &x)
{
if (ext)
if (ext && !hybridization)
{
ext->RecoverFEMSolution(X, b, x);
return;
@@ -1001,16 +1019,26 @@ void BilinearForm::RecoverFEMSolution(const Vector &X,
void BilinearForm::ComputeElementMatrices()
{
if (element_matrices || domain_integs.Size() == 0 || fes->GetNE() == 0)
if (element_matrices) { return; }
if (auto *ea_ext = dynamic_cast<EABilinearFormExtension*>(ext.get()))
{
element_matrices.reset(new DenseTensor);
ea_ext->GetElementMatrices(*element_matrices, ElementDofOrdering::NATIVE, true);
return;
}
if (domain_integs.Size() == 0 || fes->GetNE() == 0)
{
element_matrices.reset(new DenseTensor);
return;
}
int num_elements = fes->GetNE();
int num_dofs_per_el = fes->GetTypicalFE()->GetDof() * fes->GetVDim();
element_matrices = new DenseTensor(num_dofs_per_el, num_dofs_per_el,
num_elements);
element_matrices.reset(new DenseTensor(num_dofs_per_el, num_dofs_per_el,
num_elements));
DenseMatrix tmp;
IsoparametricTransformation eltrans;
@@ -1041,6 +1069,12 @@ void BilinearForm::ComputeElementMatrices()
}
}
const DenseTensor &BilinearForm::GetElementMatrices()
{
ComputeElementMatrices(); // Won't recompute if element_matrices exists
return *element_matrices;
}
void BilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
@@ -1228,15 +1262,13 @@ void BilinearForm::Update(FiniteElementSpace *nfes)
delete mat_e;
mat_e = NULL;
FreeElementMatrices();
delete static_cond;
static_cond = NULL;
static_cond.reset();
if (full_update)
{
delete mat;
mat = NULL;
delete hybridization;
hybridization = NULL;
hybridization.reset();
sequence = fes->GetSequence();
}
else
@@ -1259,9 +1291,6 @@ BilinearForm::~BilinearForm()
{
delete mat_e;
delete mat;
delete element_matrices;
delete static_cond;
delete hybridization;
if (!extern_bfs)
{
@@ -1273,8 +1302,6 @@ BilinearForm::~BilinearForm()
for (k=0; k < boundary_face_integs.Size(); k++)
{ delete boundary_face_integs[k]; }
}
delete ext;
}
@@ -1301,7 +1328,6 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
mat = NULL;
mat_e = NULL;
extern_bfs = 1;
ext = NULL;
// Copy the pointers to the integrators
domain_integs = mbf->domain_integs;
@@ -1331,22 +1357,22 @@ void MixedBilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
case AssemblyLevel::LEGACY:
break;
case AssemblyLevel::FULL:
// ext = new FAMixedBilinearFormExtension(this);
// ext.reset(new FAMixedBilinearFormExtension(this));
// Use the original BilinearForm implementation for now
break;
case AssemblyLevel::ELEMENT:
mfem_error("Element assembly not supported yet... stay tuned!");
// ext = new EAMixedBilinearFormExtension(this);
MFEM_ABORT("Element assembly not supported yet... stay tuned!");
// ext.reset(new EAMixedBilinearFormExtension(this));
break;
case AssemblyLevel::PARTIAL:
ext = new PAMixedBilinearFormExtension(this);
ext.reset(new PAMixedBilinearFormExtension(this));
break;
case AssemblyLevel::NONE:
mfem_error("Matrix-free action not supported yet... stay tuned!");
// ext = new MFMixedBilinearFormExtension(this);
MFEM_ABORT("Matrix-free action not supported yet... stay tuned!");
// ext.reset(new MFMixedBilinearFormExtension(this));
break;
default:
mfem_error("Unknown assembly level");
MFEM_ABORT("Unknown assembly level");
}
}
@@ -2343,7 +2369,6 @@ MixedBilinearForm::~MixedBilinearForm()
for (i = 0; i < boundary_trace_face_integs.Size(); i++)
{ delete boundary_trace_face_integs[i]; }
}
delete ext;
}
void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
@@ -2360,16 +2385,16 @@ void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
// Use the original implementation for now
break;
case AssemblyLevel::ELEMENT:
mfem_error("Element assembly not supported yet... stay tuned!");
MFEM_ABORT("Element assembly not supported yet... stay tuned!");
break;
case AssemblyLevel::PARTIAL:
ext = new PADiscreteLinearOperatorExtension(this);
ext.reset(new PADiscreteLinearOperatorExtension(this));
break;
case AssemblyLevel::NONE:
mfem_error("Matrix-free action not supported yet... stay tuned!");
MFEM_ABORT("Matrix-free action not supported yet... stay tuned!");
break;
default:
mfem_error("Unknown assembly level");
MFEM_ABORT("Unknown assembly level");
}
}
+19 -13
View File
@@ -83,7 +83,7 @@ protected:
/** @brief Extension for supporting Full Assembly (FA),
Element Assembly (EA),Partial Assembly (PA),
or Matrix Free assembly (MF). */
BilinearFormExtension *ext;
std::unique_ptr<BilinearFormExtension> ext;
/** Indicates if the sparse matrix is sorted after assembly when using
Full Assembly (FA). */
@@ -122,10 +122,10 @@ protected:
mutable DenseMatrix elemmat;
mutable Array<int> vdofs;
DenseTensor *element_matrices; ///< Owned.
std::unique_ptr<DenseTensor> element_matrices;
StaticCondensation *static_cond; ///< Owned.
Hybridization *hybridization; ///< Owned.
std::unique_ptr<StaticCondensation> static_cond;
std::unique_ptr<Hybridization> hybridization;
/** @brief This data member allows one to specify what should be done to the
diagonal matrix entries and corresponding RHS values upon elimination of
@@ -148,13 +148,11 @@ protected:
BilinearForm() : Matrix (0)
{
fes = NULL; sequence = -1;
mat = mat_e = NULL; extern_bfs = 0; element_matrices = NULL;
static_cond = NULL; hybridization = NULL;
mat = mat_e = NULL; extern_bfs = 0;
precompute_sparsity = 0;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::LEGACY;
batch = 1;
ext = NULL;
}
private:
@@ -214,7 +212,7 @@ public:
/// Returns the assembly level
AssemblyLevel GetAssemblyLevel() const { return assembly; }
Hybridization *GetHybridization() const { return hybridization; }
Hybridization *GetHybridization() const { return hybridization.get(); }
/** @brief Enable the use of static condensation. For details see the
description for class StaticCondensation in fem/staticcond.hpp This
@@ -224,7 +222,7 @@ public:
/** @brief Check if static condensation was actually enabled by a previous
call to EnableStaticCondensation(). */
bool StaticCondensationIsEnabled() const { return static_cond; }
bool StaticCondensationIsEnabled() const { return static_cond != nullptr; }
/// Return the trace FE space associated with static condensation.
FiniteElementSpace *SCFESpace() const
@@ -569,12 +567,20 @@ public:
void RecoverFEMSolution(const Vector &X, const Vector &b,
Vector &x) override;
/// Compute and store internally all element matrices.
/// @brief Compute and store internally all element matrices.
///
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLeve(), this will
/// use efficient (device-accelerated) assembly of the element matrices.
void ComputeElementMatrices();
/// Free the memory used by the element matrices.
void FreeElementMatrices()
{ delete element_matrices; element_matrices = NULL; }
void FreeElementMatrices() { element_matrices.reset(); }
/// @brief Return a DenseTensor containing the assembled element matrices.
///
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLeve(), this will
/// use efficient (device-accelerated) assembly of the element matrices.
const DenseTensor &GetElementMatrices();
/// Compute the element matrix of the given element
/** The element matrix is computed by calling the domain integrators
@@ -760,7 +766,7 @@ protected:
/** Extension for supporting Full Assembly (FA), Element Assembly (EA),
Partial Assembly (PA), or Matrix Free assembly (MF). */
MixedBilinearFormExtension *ext;
std::unique_ptr<MixedBilinearFormExtension> ext;
/** @brief Indicates the BilinearFormIntegrator%s stored in
MixedBilinearForm#domain_integs, MixedBilinearForm#boundary_integs,
+288 -78
View File
@@ -16,6 +16,7 @@
#include "bilinearform.hpp"
#include "pbilinearform.hpp"
#include "pgridfunc.hpp"
#include "fe/face_map_utils.hpp"
#include "ceed/interface/util.hpp"
namespace mfem
@@ -864,52 +865,137 @@ void EABilinearFormExtension::Assemble()
ne = trial_fes->GetMesh()->GetNE();
elemDofs = trial_fes->GetTypicalFE()->GetDof();
ea_data.SetSize(ne*elemDofs*elemDofs, Device::GetMemoryType());
ea_data.UseDevice(true);
Vector ea_data_tmp;
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
if ( integratorCount == 0 )
auto add_with_markers = [&](const Vector &ea_1, Vector &ea_2, const int ne_,
const Array<int> &markers, const Array<int> &attrs,
const bool add)
{
ea_data = 0.0;
}
for (int i = 0; i < integratorCount; ++i)
if (ne_ == 0) { return; }
const int sz = ea_1.Size() / ne_;
const int *d_m = markers.Read();
const int *d_a = attrs.Read();
const auto d_ea_1 = Reshape(ea_1.Read(), sz, ne_);
auto d_ea_2 = Reshape(add ? ea_2.ReadWrite() : ea_2.Write(), sz, ne_);
mfem::forall(sz*ne_, [=] MFEM_HOST_DEVICE (int idx)
{
const int i = idx % sz;
const int e = idx / sz;
const real_t val = d_m[d_a[e] - 1] ? d_ea_1(i, e) : 0.0;
if (add)
{
d_ea_2(i, e) += val;
}
else
{
d_ea_2(i, e) = val;
}
});
};
{
integrators[i]->AssembleEA(*a->FESpace(), ea_data, i);
ea_data.SetSize(ne*elemDofs*elemDofs);
ea_data.UseDevice(true);
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
Array<Array<int>*> &markers_array = *a->GetDBFI_Marker();
if (integrators.Size() == 0) { ea_data = 0.0; }
for (int i = 0; i < integrators.Size(); ++i)
{
const bool add = (i > 0);
const Array<int> *markers = markers_array[i];
if (markers == nullptr)
{
integrators[i]->AssembleEA(*a->FESpace(), ea_data, add);
}
else
{
ea_data_tmp.SetSize(ea_data.Size());
integrators[i]->AssembleEA(*a->FESpace(), ea_data_tmp, false);
add_with_markers(ea_data_tmp, ea_data, ne, *markers,
elem_attributes, add);
}
}
}
faceDofs = trial_fes->GetTypicalTraceElement()->GetDof();
MFEM_VERIFY(a->GetBBFI()->Size() == 0,
"Element assembly does not support AddBoundaryIntegrator yet.");
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int intFaceIntegratorCount = intFaceIntegrators.Size();
if (intFaceIntegratorCount>0)
{
nf_int = trial_fes->GetNFbyType(FaceType::Interior);
ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
}
for (int i = 0; i < intFaceIntegratorCount; ++i)
{
intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(),
ea_data_int,
ea_data_ext,
i);
Array<BilinearFormIntegrator*> &bdr_integs = *a->GetBBFI();
Array<Array<int>*> &markers_array = *a->GetBBFI_Marker();
const int n_bdr_integs = bdr_integs.Size();
if (n_bdr_integs > 0)
{
nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs);
}
for (int i = 0; i < n_bdr_integs; ++i)
{
const bool add = (i > 0);
const Array<int> *markers = markers_array[i];
if (markers == nullptr)
{
bdr_integs[i]->AssembleEABoundary(*a->FESpace(), ea_data_bdr, add);
}
else
{
ea_data_tmp.SetSize(ea_data_bdr.Size());
bdr_integs[i]->AssembleEABoundary(*a->FESpace(), ea_data_tmp, add);
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
bdr_attributes, add);
}
}
}
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int boundFaceIntegratorCount = bdrFaceIntegrators.Size();
if (boundFaceIntegratorCount>0)
{
nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs, Device::GetMemoryType());
ea_data_bdr = 0.0;
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int intFaceIntegratorCount = intFaceIntegrators.Size();
if (intFaceIntegratorCount>0)
{
nf_int = trial_fes->GetNFbyType(FaceType::Interior);
ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs);
ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs);
}
for (int i = 0; i < intFaceIntegratorCount; ++i)
{
const bool add = (i > 0);
intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(),
ea_data_int,
ea_data_ext,
add);
}
}
for (int i = 0; i < boundFaceIntegratorCount; ++i)
{
bdrFaceIntegrators[i]->AssembleEABoundaryFaces(*a->FESpace(),ea_data_bdr,i);
Array<BilinearFormIntegrator*> &bdr_face_integs = *a->GetBFBFI();
Array<Array<int>*> &markers_array = *a->GetBFBFI_Marker();
const int n_bdr_face_integs = bdr_face_integs.Size();
if (n_bdr_face_integs > 0)
{
nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs);
}
for (int i = 0; i < n_bdr_face_integs; ++i)
{
const bool add = (i > 0);
const Array<int> *markers = markers_array[i];
if (markers == nullptr)
{
bdr_face_integs[i]->AssembleEABoundaryFaces(
*a->FESpace(), ea_data_bdr, add);
}
else
{
ea_data_tmp.SetSize(ea_data_bdr.Size());
bdr_face_integs[i]->AssembleEABoundaryFaces(*a->FESpace(),
ea_data_tmp,
add);
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
bdr_attributes, add);
}
}
}
if (factorize_face_terms && int_face_restrict_lex)
@@ -1021,34 +1107,29 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
}
// Treatment of boundary faces
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int bFISz = bdrFaceIntegrators.Size();
if (!factorize_face_terms && bdr_face_restrict_lex && bFISz>0)
if (!factorize_face_terms && bdr_face_restrict_lex && ea_data_bdr.Size() > 0)
{
// Apply the Boundary Face Restriction
bdr_face_restrict_lex->Mult(x, bdr_face_X);
if (bdr_face_X.Size()>0)
bdr_face_Y = 0.0;
// Apply the boundary face matrices
const int NDOFS = faceDofs;
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
bdr_face_Y = 0.0;
// Apply the boundary face matrices
const int NDOFS = faceDofs;
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(i, j, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
res += A(i, j, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
}
@@ -1149,35 +1230,164 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
}
// Treatment of boundary faces
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int bFISz = bdrFaceIntegrators.Size();
if (!factorize_face_terms && bdr_face_restrict_lex && bFISz>0)
if (!factorize_face_terms && bdr_face_restrict_lex && ea_data_bdr.Size() > 0)
{
// Apply the Boundary Face Restriction
bdr_face_restrict_lex->Mult(x, bdr_face_X);
if (bdr_face_X.Size()>0)
bdr_face_Y = 0.0;
// Apply the boundary face matrices transposed
const int NDOFS = faceDofs;
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
bdr_face_Y = 0.0;
// Apply the boundary face matrices transposed
const int NDOFS = faceDofs;
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(j, i, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
res += A(j, i, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
}
void EABilinearFormExtension::GetElementMatrices(
DenseTensor &element_matrices, ElementDofOrdering ordering, bool add_bdr)
{
// Ensure the EA data is assembled
if (ea_data.Size() == 0) { Assemble(); }
const int ndofs = elemDofs;
element_matrices.SetSize(ndofs, ndofs, ne);
const int N = element_matrices.TotalSize();
const auto d_ea_data = Reshape(ea_data.Read(), ndofs, ndofs, ne);
auto d_element_matrices = Reshape(element_matrices.Write(),
ndofs, ndofs,
ne);
const int *d_dof_map = nullptr;
Array<int> dof_map;
if (ordering == ElementDofOrdering::NATIVE)
{
const TensorBasisElement* tbe =
dynamic_cast<const TensorBasisElement*>(trial_fes->GetFE(0));
if (tbe)
{
// Deep copy to avoid issues with host device (see similar comment in
// HybridizationExtension::ConstructC).
dof_map = tbe->GetDofMap();
d_dof_map = dof_map.Read();
}
}
if (d_dof_map)
{
// Reordering required
mfem::forall(N, [=] MFEM_HOST_DEVICE (int idx)
{
const int e = idx / ndofs / ndofs;
const int i = idx % ndofs;
const int j = (idx / ndofs) % ndofs;
const int ii_s = d_dof_map[i];
const int ii = (ii_s >= 0) ? ii_s : -1 - ii_s;
const int s_i = (ii_s >= 0) ? 1 : -1;
const int jj_s = d_dof_map[j];
const int jj = (jj_s >= 0) ? jj_s : -1 - jj_s;
const int s_j = (jj_s >= 0) ? 1 : -1;
d_element_matrices(ii, jj, e) = s_i*s_j*d_ea_data(j, i, e);
});
}
else
{
// No reordering required
mfem::forall(N, [=] MFEM_HOST_DEVICE (int idx)
{
const int e = idx / ndofs / ndofs;
const int i = idx % ndofs;
const int j = (idx / ndofs) % ndofs;
d_element_matrices(i, j, e) = d_ea_data(j, i, e);
});
}
if (add_bdr && ea_data_bdr.Size() > 0)
{
const int ndof_face = faceDofs;
const auto d_ea_bdr = Reshape(ea_data_bdr.Read(),
ndof_face, ndof_face, nf_bdr);
// Get all the local face maps (mapping from lexicographic face index to
// lexicographic volume index, depending on the local face index).
const Mesh &mesh = *trial_fes->GetMesh();
const int dim = mesh.Dimension();
const int n_faces_per_el = 2*dim; // assuming tensor product
Array<int> face_maps(ndof_face * n_faces_per_el);
for (int lf_i = 0; lf_i < n_faces_per_el; ++lf_i)
{
Array<int> face_map(ndof_face);
trial_fes->GetFE(0)->GetFaceMap(lf_i, face_map);
for (int i = 0; i < ndof_face; ++i)
{
face_maps[i + lf_i*ndof_face] = face_map[i];
}
}
Array<int> face_info(nf_bdr * 2);
{
int fidx = 0;
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
Mesh::FaceInformation finfo = mesh.GetFaceInformation(f);
if (!finfo.IsBoundary()) { continue; }
face_info[0 + fidx*2] = finfo.element[0].local_face_id;
face_info[1 + fidx*2] = finfo.element[0].index;
fidx++;
}
}
const auto d_face_maps = Reshape(face_maps.Read(), ndof_face, n_faces_per_el);
const auto d_face_info = Reshape(face_info.Read(), 2, nf_bdr);
const bool reorder = (ordering == ElementDofOrdering::NATIVE);
mfem::forall_2D(nf_bdr, ndof_face, ndof_face, [=] MFEM_HOST_DEVICE (int f)
{
const int lf_i = d_face_info(0, f);
const int e = d_face_info(1, f);
// Loop over face indices in "native ordering"
MFEM_FOREACH_THREAD(i_lex_face, x, ndof_face)
{
// Convert from lexicographic face DOF to volume DOF
const int i_lex = d_face_maps(i_lex_face, lf_i);
const int ii_s = d_dof_map[i_lex];
const int ii = (ii_s >= 0) ? ii_s : -1 - ii_s;
const int i = reorder ? ii : i_lex;
const int s_i = (ii_s < 0 && reorder) ? -1 : 1;
MFEM_FOREACH_THREAD(j_lex_face, y, ndof_face)
{
// Convert from lexicographic face DOF to volume DOF
const int j_lex = d_face_maps(j_lex_face, lf_i);
const int jj_s = d_dof_map[j_lex];
const int jj = (jj_s >= 0) ? jj_s : -1 - jj_s;
const int j = reorder ? jj : j_lex;
const int s_j = (jj_s < 0 && reorder) ? -1 : 1;
AtomicAdd(d_element_matrices(i, j, e),
s_i*s_j*d_ea_bdr(i_lex_face, j_lex_face, f));
}
}
});
}
}
// Data and methods for fully-assembled bilinear forms
+11
View File
@@ -154,6 +154,17 @@ public:
void Assemble() override;
void Mult(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override;
/// @brief Populates @a element_matrices with the element matrices.
///
/// The element matrices are converted from row-major (how they are stored in
/// @a ea_data) to column-major format.
///
/// If @a ordering is ElementDofOrdering::NATIVE, then the matrices are
/// reordered from the lexicographic ordering used internally.
void GetElementMatrices(DenseTensor &element_matrices,
ElementDofOrdering ordering,
bool add_bdr);
};
/// Data and methods for fully-assembled bilinear forms
+31 -5
View File
@@ -72,6 +72,14 @@ void BilinearFormIntegrator::AssembleEA(const FiniteElementSpace &fes,
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEABoundary(const FiniteElementSpace &fes,
Vector &emat,
const bool add)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleEABoundary(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
&fes,
Vector &ea_data_int,
@@ -82,6 +90,16 @@ void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEAInteriorFaces(
const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes,
Vector &emat,
const bool add)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleEAInteriorFaces(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace
&fes,
Vector &ea_data_bdr,
@@ -2829,17 +2847,18 @@ void VectorDivergenceIntegrator::AssembleElementMatrix2(
DenseMatrix &elmat)
{
dim = trial_fe.GetDim();
sdim = Trans.GetSpaceDim();
int trial_dof = trial_fe.GetDof();
int test_dof = test_fe.GetDof();
real_t c;
dshape.SetSize (trial_dof, dim);
gshape.SetSize (trial_dof, dim);
Jadj.SetSize (dim);
divshape.SetSize (dim*trial_dof);
gshape.SetSize (trial_dof, sdim);
Jadj.SetSize (dim, sdim);
divshape.SetSize (sdim*trial_dof);
shape.SetSize (test_dof);
elmat.SetSize (test_dof, dim*trial_dof);
elmat.SetSize (test_dof, sdim*trial_dof);
const IntegrationRule *ir = GetIntegrationRule(trial_fe, test_fe, Trans);
@@ -2853,13 +2872,15 @@ void VectorDivergenceIntegrator::AssembleElementMatrix2(
trial_fe.CalcDShape (ip, dshape);
test_fe.CalcPhysShape (Trans, shape);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
CalcAdjugate(Trans.Jacobian(), Jadj);
Mult (dshape, Jadj, gshape);
gshape.GradToDiv (divshape);
c = ip.weight;
if (dim != sdim) { c /= Trans.Weight(); }
if (Q)
{
c *= Q -> Eval (Trans, ip);
@@ -2900,6 +2921,11 @@ void DivDivIntegrator::AssembleElementMatrix(
if (ir == NULL)
{
int order = 2 * el.GetOrder() - 2; // <--- OK for RTk
if (el.Space() == FunctionSpace::Uk)
{
order += 2;
}
ir = &IntRules.Get(el.GetGeomType(), order);
}
+39 -7
View File
@@ -124,11 +124,25 @@ public:
/// Assemble diagonal and add it to Vector @a diag.
virtual void AssembleDiagonalMF(Vector &diag);
virtual void AssembleEABoundary(const FiniteElementSpace &fes,
Vector &ea_data_bdr,
const bool add = true);
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext,
const bool add = true);
/// @brief Method defining element assembly for mixed trace integrators.
///
/// This is the element assembly analogue of AssembleFaceMatrix(const
/// FiniteElement&, const FiniteElement&, const FiniteElement&,
/// FaceElementTransformations&, DenseMatrix&).
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes,
Vector &emat,
const bool add = true);
virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
Vector &ea_data_bdr,
const bool add = true);
@@ -383,6 +397,7 @@ public:
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
using BilinearFormIntegrator::AssembleEAInteriorFaces;
void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext,
@@ -494,6 +509,7 @@ public:
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
using BilinearFormIntegrator::AssembleEAInteriorFaces;
void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext,
@@ -2350,6 +2366,8 @@ protected:
const FaceGeometricFactors *face_geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
void AssembleEA_(Vector &ea, const bool add);
public:
using ApplyKernelType = void(*)(const int, const Array<real_t>&,
@@ -2390,7 +2408,10 @@ public:
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
void AssembleDiagonalPA(Vector &diag) override;
virtual void AssembleEABoundary(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
virtual void AssembleDiagonalPA(Vector &diag) override;
void AssembleDiagonalMF(Vector &diag) override;
@@ -2914,6 +2935,8 @@ public:
void AddMultPA(const Vector &x, Vector &y) const override;
void AddMultTransposePA(const Vector &x, Vector &y) const override;
void AssembleDiagonalPA(Vector& diag) override;
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
const Coefficient *GetCoefficient() const { return Q; }
};
@@ -2935,7 +2958,7 @@ private:
Vector pa_data;
const DofToQuad *trial_maps, *test_maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, ne, nq;
int dim, sdim, ne, nq;
int trial_dofs1D, test_dofs1D, quad1D;
public:
@@ -2981,11 +3004,6 @@ class DivDivIntegrator: public BilinearFormIntegrator
protected:
Coefficient *Q;
using BilinearFormIntegrator::AssemblePA;
void AssemblePA(const FiniteElementSpace &fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AssembleDiagonalPA(Vector& diag) override;
private:
#ifndef MFEM_THREAD_SAFE
Vector divshape, te_divshape;
@@ -3012,6 +3030,13 @@ public:
ElementTransformation &Trans,
DenseMatrix &elmat) override;
using BilinearFormIntegrator::AssemblePA;
void AssemblePA(const FiniteElementSpace &fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AssembleDiagonalPA(Vector& diag) override;
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
const Coefficient *GetCoefficient() const { return Q; }
};
@@ -3311,6 +3336,7 @@ public:
void AddMultPA(const Vector&, Vector&) const override;
using BilinearFormIntegrator::AssembleEAInteriorFaces;
void AssembleEAInteriorFaces(const FiniteElementSpace& fes,
Vector &ea_data_int,
Vector &ea_data_ext,
@@ -3624,6 +3650,12 @@ public:
const FiniteElement &test_fe2,
FaceElementTransformations &Trans,
DenseMatrix &elmat) override;
using BilinearFormIntegrator::AssembleEAInteriorFaces;
void AssembleEAInteriorFaces(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes,
Vector &emat,
const bool add = true) override;
};
/** Integrator for the DPG form:$ \langle v, w \rangle $ over a face (the interface) where
+4 -4
View File
@@ -487,7 +487,7 @@ SesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
if (RealInteg() && ImagInteg())
{
// Modify RHS and offdiagonal blocks (imaginary parts of the matrix) to
// Modify RHS and off-diagonal blocks (imaginary parts of the matrix) to
// conform with standard essential BC treatment
if (A_i.Is<ConstrainedOperator>())
{
@@ -576,7 +576,7 @@ SesquilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
if (RealInteg() && ImagInteg())
{
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
// Modify off-diagonal blocks (imaginary parts of the matrix) to conform
// with standard essential BC treatment
if (A_i.Is<ConstrainedOperator>())
{
@@ -1236,7 +1236,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
d_B_r[j] = d_X_r[j];
d_B_i[j] = d_X_i[j];
});
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
// Modify off-diagonal blocks (imaginary parts of the matrix) to conform
// with standard essential BC treatment
if (A_i.Type() == Operator::Hypre_ParCSR)
{
@@ -1324,7 +1324,7 @@ ParSesquilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
if (RealInteg() && ImagInteg())
{
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
// Modify off-diagonal blocks (imaginary parts of the matrix) to conform
// with standard essential BC treatment
if ( A_i.Type() == Operator::Hypre_ParCSR )
{
+459 -7
View File
@@ -27,6 +27,9 @@ using action_t =
using derivative_action_t =
std::function<void(std::vector<Vector> &, const Vector &, Vector &)>;
using assemble_derivative_hypreparmatrix_callback_t =
std::function<void(std::vector<Vector> &, HypreParMatrix &)>;
using restriction_callback_t =
std::function<void(std::vector<Vector> &,
const std::vector<Vector> &,
@@ -47,15 +50,20 @@ public:
const std::vector<Vector *> &solutions_l,
const std::vector<Vector *> &parameters_l,
const restriction_callback_t &restriction_callback,
const std::function<void(Vector &, Vector &)> &prolongation_transpose) :
const std::function<void(Vector &, Vector &)> &prolongation_transpose,
const std::vector<assemble_derivative_hypreparmatrix_callback_t>
&assemble_derivative_hypreparmatrix_callbacks) :
Operator(height, width),
derivative_actions(derivative_actions),
direction(direction),
daction_l(daction_l_size),
daction_l_size(daction_l_size),
derivative_actions_transpose(derivative_actions_transpose),
transpose_direction(transpose_direction),
daction_transpose_l(daction_transpose_l_size),
prolongation_transpose(prolongation_transpose)
prolongation_transpose(prolongation_transpose),
assemble_derivative_hypreparmatrix_callbacks(
assemble_derivative_hypreparmatrix_callbacks)
{
std::vector<Vector> s_l(solutions_l.size());
for (size_t i = 0; i < s_l.size(); i++)
@@ -75,7 +83,7 @@ public:
void Mult(const Vector &direction_t, Vector &y) const override
{
// daction_l.SetSize(height);
daction_l.SetSize(daction_l_size);
daction_l = 0.0;
prolongation(direction, direction_t, direction_l);
@@ -88,7 +96,7 @@ public:
void MultTranspose(const Vector &direction_t, Vector &y) const override
{
// daction_l.SetSize(width);
daction_l.SetSize(width);
daction_l = 0.0;
prolongation(transpose_direction, direction_t, direction_l);
@@ -99,20 +107,36 @@ public:
prolongation_transpose(daction_l, y);
};
void Assemble(HypreParMatrix &A)
{
MFEM_ASSERT(!assemble_derivative_hypreparmatrix_callbacks.empty(),
"derivative can't be assembled into a matrix");
for (int i = 0; i < assemble_derivative_hypreparmatrix_callbacks.size(); i++)
{
assemble_derivative_hypreparmatrix_callbacks[i](fields_e, A);
}
}
private:
std::vector<derivative_action_t> derivative_actions;
FieldDescriptor direction;
mutable Vector daction_l;
const int daction_l_size;
std::vector<derivative_action_t> derivative_actions_transpose;
FieldDescriptor transpose_direction;
mutable Vector daction_transpose_l;
std::function<void(Vector &, Vector &)> prolongation_transpose;
std::vector<assemble_derivative_hypreparmatrix_callback_t>
assemble_derivative_hypreparmatrix_callbacks;
mutable std::vector<Vector> fields_e;
mutable Vector direction_l;
std::function<void(Vector &, Vector &)> prolongation_transpose;
};
class DifferentiableOperator : public Operator
@@ -179,11 +203,12 @@ public:
residual_l.Size(),
daction_transpose_callbacks[derivative_id],
fields[test_space_field_idx],
GetTrueVSize(fields[test_space_field_idx]),
GetVSize(fields[test_space_field_idx]),
solutions_l,
parameters_l,
restriction_callback,
prolongation_transpose);
prolongation_transpose,
assemble_derivative_hypreparmatrix_callbacks[derivative_id]);
}
private:
@@ -194,6 +219,10 @@ private:
std::vector<derivative_action_t>> derivative_action_callbacks;
std::map<size_t,
std::vector<derivative_action_t>> daction_transpose_callbacks;
std::map<size_t,
std::vector<assemble_derivative_hypreparmatrix_callback_t>>
assemble_derivative_hypreparmatrix_callbacks;
std::vector<FieldDescriptor> solutions;
std::vector<FieldDescriptor> parameters;
@@ -325,6 +354,11 @@ void DifferentiableOperator::AddDomainIntegrator(
inputs_vdim[i] = mfem::get<i>(inputs).vdim;
});
if ( mesh.GetNE() == 0)
{
MFEM_ABORT("Mesh with no elements is not yet supported!");
}
Array<int> elem_attributes;
elem_attributes.SetSize(mesh.GetNE());
for (int i = 0; i < mesh.GetNE(); ++i)
@@ -742,6 +776,424 @@ void DifferentiableOperator::AddDomainIntegrator(
});
}, derivative_ids);
}
// Create assembly callbacks for derivatives
// TODO: Host only for now
for_constexpr([&](auto derivative_id)
{
// Field index of the derivative
const size_t d_field_idx = FindIdx(derivative_id, fields);
// First Input index of the derivative
const size_t d_input_idx = [d_field_idx, &input_to_field]
{
for (size_t i = 0; i < input_to_field.size(); i++)
{
if (input_to_field[i] == d_field_idx)
{
return i;
}
}
return size_t(SIZE_MAX);
}();
auto shmem_info =
get_shmem_info<entity_t, num_fields, num_inputs, num_outputs>
(input_dtq_maps, output_dtq_maps, fields, num_entities, inputs, num_qp,
input_size_on_qp, residual_size_on_qp, element_dof_ordering, d_field_idx);
Vector shmem_cache(shmem_info.total_size);
const auto input_is_dependent = dependency_map[derivative_id];
// auto dependent_input_dtq_maps =
// get_marked_entries(input_dtq_maps, input_is_dependent);
const int trial_vdim = GetVDim(fields[d_field_idx]);
const int num_trial_dof_1d =
input_dtq_maps[d_input_idx].B.GetShape()[DofToQuadMap::Index::DOF];
const int num_trial_dof =
get_restriction<entity_t>(fields[d_field_idx], element_dof_ordering)->Height() /
inputs_vdim[d_input_idx] / num_entities;
int total_trial_op_dim = 0;
for_constexpr<num_inputs>([&](auto s)
{
if (input_is_dependent[s] == false)
{
return;
}
total_trial_op_dim += input_size_on_qp[s] / mfem::get<s>(inputs).vdim;
});
const int da_size_on_qp =
GetSizeOnQP<entity_t>(output_fop, fields[test_space_field_idx]);
assemble_derivative_hypreparmatrix_callbacks[derivative_id].push_back(
[=, fields = this->fields]
(std::vector<Vector> &fields_e, HypreParMatrix &A) mutable
{
Vector direction_e(get_restriction<entity_t>(fields[d_field_idx],
element_dof_ordering)->Height());
auto shmem = shmem_cache.ReadWrite();
auto wrapped_fields_e = wrap_fields(fields_e, shmem_info.field_sizes,
num_entities);
auto wrapped_direction_e = Reshape(direction_e.ReadWrite(),
shmem_info.direction_size, num_entities);
Vector a_qp_mem(test_vdim * test_op_dim * trial_vdim * total_trial_op_dim *
num_qp * num_elements);
auto a_qp = Reshape(a_qp_mem.ReadWrite(), test_vdim, test_op_dim,
trial_vdim, total_trial_op_dim, num_qp, num_elements);
Vector Ae_mem(num_test_dof * test_vdim * num_trial_dof * trial_vdim *
num_elements);
Ae_mem = 0.0;
auto A_e = Reshape(Ae_mem.ReadWrite(), num_test_dof, test_vdim, num_trial_dof,
trial_vdim, num_elements);
for (int e = 0; e < num_elements; e++)
{
auto [input_dtq_shmem, output_dtq_shmem, fields_shmem, direction_shmem,
input_shmem, shadow_shmem, residual_shmem, scratch_shmem] =
unpack_shmem(shmem, shmem_info, input_dtq_maps,
output_dtq_maps, wrapped_fields_e, wrapped_direction_e, num_qp, e);
map_fields_to_quadrature_data(
input_shmem, fields_shmem, input_dtq_shmem, input_to_field, inputs, ir_weights,
scratch_shmem, dimension, use_sum_factorization);
set_zero(shadow_shmem);
for (int q = 0; q < num_qp; q++)
{
for (int j = 0; j < trial_vdim; j++)
{
size_t m_offset = 0;
for_constexpr_with_arg([&](auto s, auto&& input_fop)
{
if (input_is_dependent[s] == false)
{
return;
}
auto trial_op_dim = input_size_on_qp[s] / mfem::get<s>(inputs).vdim;
auto d_qp = Reshape(&(shadow_shmem[s])[0], trial_vdim, trial_op_dim, num_qp);
for (int m = 0; m < trial_op_dim; m++)
{
d_qp(j, m, q) = 1.0;
auto r = Reshape(&residual_shmem(0, q), da_size_on_qp);
auto qf_args = decay_tuple<qf_param_ts> {};
#ifdef MFEM_USE_ENZYME
auto qf_shadow_args = decay_tuple<qf_param_ts> {};
apply_kernel_fwddiff_enzyme(r, qfunc, qf_args, qf_shadow_args, input_shmem,
shadow_shmem, q);
#else
apply_kernel_native_dual(r, qfunc, qf_args, input_shmem, shadow_shmem, q);
#endif
d_qp(j, m, q) = 0.0;
auto f = Reshape(&r(0), test_vdim, test_op_dim);
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
a_qp(i, k, j, m + m_offset, q, e) = f(i, k);
}
}
}
m_offset += trial_op_dim;
}, inputs);
}
}
Vector fhat_mem(test_vdim * test_op_dim * num_qp);
auto fhat = Reshape(fhat_mem.ReadWrite(), test_vdim, test_op_dim, num_qp);
if (use_sum_factorization)
{
if (dimension == 2)
{
for (int Jx = 0; Jx < num_trial_dof_1d; Jx++)
{
for (int Jy = 0; Jy < num_trial_dof_1d; Jy++)
{
const int J = Jy + Jx * num_trial_dof_1d;
for (int j = 0; j < trial_vdim; j++)
{
fhat_mem = 0.0;
size_t m_offset = 0;
for_constexpr_with_arg([&](auto s, auto&& input_fop)
{
if (input_is_dependent[s] == false)
{
return;
}
int trial_op_dim = input_size_on_qp[s] / mfem::get<s>(inputs).vdim;
auto &B = input_dtq_maps[s].B;
auto &G = input_dtq_maps[s].G;
if constexpr (is_value_fop<std::decay_t<decltype(input_fop)>>::value)
{
for (int qx = 0; qx < q1d; qx++)
{
for (int qy = 0; qy < q1d; qy++)
{
const int q = qy + qx * q1d;
for (int m = 0; m < trial_op_dim; m++)
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
const real_t f = a_qp(i, k, j, m + m_offset, q, e);
fhat(i, k, q) += f * B(qx, 0, Jx) * B(qy, 0, Jy);
}
}
}
}
}
}
else if constexpr (is_gradient_fop<std::decay_t<decltype(input_fop)>>::value)
{
for (int qx = 0; qx < q1d; qx++)
{
for (int qy = 0; qy < q1d; qy++)
{
const int q = qy + qx * q1d;
for (int m = 0; m < trial_op_dim; m++)
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
const real_t f = a_qp(i, k, j, m + m_offset, q, e);
if (m == 0)
{
fhat(i, k, q) += f * B(qx, 0, Jx) * G(qy, 0, Jy);
}
else
{
fhat(i, k, q) += f * G(qx, 0, Jx) * B(qy, 0, Jy);
}
}
}
}
}
}
}
else
{
MFEM_ABORT("sum factorized sparse matrix assemble routine "
"not implemented for field operator");
}
m_offset += trial_op_dim;
}, inputs);
auto bvtfhat = Reshape(&A_e(0, 0, J, j, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields(
bvtfhat, fhat, output_fop, output_dtq_shmem[0],
scratch_shmem, dimension, use_sum_factorization);
}
}
}
}
else
{
MFEM_ABORT("sum factorized sparse matrix assemble routine "
"not implemented for 3D");
}
}
else
{
for (int J = 0; J < num_trial_dof; J++)
{
for (int j = 0; j < trial_vdim; j++)
{
fhat_mem = 0.0;
size_t m_offset = 0;
for_constexpr_with_arg([&](auto s, auto&& input_fop)
{
if (input_is_dependent[s] == false)
{
return;
}
int trial_op_dim = input_size_on_qp[s] / mfem::get<s>(inputs).vdim;
auto B = is_value_fop<std::decay_t<decltype(input_fop)>>::value ?
input_dtq_maps[s].B : input_dtq_maps[s].G;
for (int q = 0; q < num_qp; q++)
{
for (int m = 0; m < trial_op_dim; m++)
{
for (int i = 0; i < test_vdim; i++)
{
for (int k = 0; k < test_op_dim; k++)
{
const real_t f = a_qp(i, k, j, m + m_offset, q, e);
fhat(i, k, q) += f * B(q, m, J);
}
}
}
}
m_offset += trial_op_dim;
}, inputs);
auto bvtfhat = Reshape(&A_e(0, 0, J, j, e), num_test_dof, test_vdim);
map_quadrature_data_to_fields(
bvtfhat, fhat, output_fop, output_dtq_shmem[0],
scratch_shmem, dimension, use_sum_factorization);
}
}
}
}
bool same_test_and_trial = false;
for (int s = 0; s < num_inputs; s++)
{
if (input_is_dependent[s])
{
if (output_to_field[0] == input_to_field[s])
{
same_test_and_trial = true;
break;
}
}
}
FieldDescriptor *trial_field = nullptr;
for (int s = 0; s < num_inputs; s++)
{
if (input_is_dependent[s])
{
trial_field = &fields[input_to_field[s]];
}
}
auto trial_fes = *std::get_if<const ParFiniteElementSpace *>
(&trial_field->data);
auto test_fes = *std::get_if<const ParFiniteElementSpace *>
(&fields[output_to_field[0]].data);
SparseMatrix mat(test_fes->GetVSize(), trial_fes->GetVSize());
if (test_fes == nullptr)
{
MFEM_ABORT("internal error");
}
// if (same_test_and_trial && use_sum_factorization)
// {
// const ElementRestriction &rest =
// static_cast<const ElementRestriction&>(
// *test_fes->GetElementRestriction(element_dof_ordering));
// rest.FillSparseMatrix(Ae_mem, mat);
// }
// else
{
for (int e = 0; e < num_elements; e++)
{
auto tmp = Reshape(Ae_mem.ReadWrite(), num_test_dof * test_vdim,
num_trial_dof * trial_vdim, num_elements);
DenseMatrix A_e(&tmp(0, 0, e), num_test_dof * test_vdim,
num_trial_dof * trial_vdim);
Array<int> test_vdofs, trial_vdofs;
test_fes->GetElementVDofs(e, test_vdofs);
GetElementVDofs(*trial_field, e, trial_vdofs);
if (use_sum_factorization)
{
Array<int> test_vdofs_mapped(test_vdofs.Size()),
trial_vdofs_mapped(trial_vdofs.Size());
const Array<int> &test_dofmap =
dynamic_cast<const TensorBasisElement&>(*test_fes->GetFE(0)).GetDofMap();
if (test_dofmap.Size() == 0)
{
test_vdofs_mapped = test_vdofs;
}
else
{
MFEM_ASSERT(test_dofmap.Size() == num_test_dof,
"internal error: dof map of the test space does not "
"match previously determined number of test space dofs");
for (int vd = 0; vd < test_vdim; vd++)
{
for (int i = 0; i < num_test_dof; i++)
{
test_vdofs_mapped[i + vd * num_test_dof] =
test_vdofs[test_dofmap[i] + vd * num_test_dof];
}
}
}
const Array<int> &trial_dofmap =
dynamic_cast<const TensorBasisElement&>(*trial_fes->GetFE(0)).GetDofMap();
if (trial_dofmap.Size() == 0)
{
trial_vdofs_mapped = trial_vdofs;
}
else
{
MFEM_ASSERT(trial_dofmap.Size() == num_trial_dof,
"internal error: dof map of the test space does not "
"match previously determined number of test space dofs");
for (int vd = 0; vd < trial_vdim; vd++)
{
for (int i = 0; i < num_trial_dof; i++)
{
trial_vdofs_mapped[i + vd * num_trial_dof] =
trial_vdofs[trial_dofmap[i] + vd * num_trial_dof];
}
}
}
mat.AddSubMatrix(test_vdofs_mapped, trial_vdofs_mapped, A_e, 1);
}
else
{
mat.AddSubMatrix(test_vdofs, trial_vdofs, A_e, 1);
}
}
}
mat.Finalize();
if (same_test_and_trial)
{
HypreParMatrix tmp(test_fes->GetComm(),
test_fes->GlobalVSize(),
test_fes->GetDofOffsets(),
&mat);
A = *RAP(&tmp, test_fes->Dof_TrueDof_Matrix());
}
else
{
HypreParMatrix tmp(test_fes->GetComm(),
test_fes->GlobalVSize(),
trial_fes->GlobalVSize(),
test_fes->GetDofOffsets(),
trial_fes->GetDofOffsets(),
&mat);
A = *RAP(test_fes->Dof_TrueDof_Matrix(), &tmp, trial_fes->Dof_TrueDof_Matrix());
}
});
}, derivative_ids);
}
+98 -3
View File
@@ -290,6 +290,91 @@ void pretty_print(const std::unordered_map<K,std::array<T,N>>& map)
std::cout << "}\n";
}
void print_mpi_root(const std::string& msg)
{
auto myrank = Mpi::WorldRank();
if (myrank == 0)
{
std::cout << msg << std::endl;
std::cout.flush(); // Ensure output is flushed
}
}
/// @brief print with MPI rank synchronization
///
/// @param msg Message to print
void print_mpi_sync(const std::string& msg)
{
auto myrank = Mpi::WorldRank();
auto nranks = Mpi::WorldSize();
if (nranks == 1)
{
// Single process case - just print directly
std::cout << msg << std::endl;
return;
}
// First gather string lengths
int msg_len = msg.length();
std::vector<int> lengths(nranks);
MPI_Gather(&msg_len, 1, MPI_INT,
lengths.data(), 1, MPI_INT,
0, MPI_COMM_WORLD);
if (myrank == 0)
{
// Rank 0: Allocate receive buffer based on gathered lengths
std::vector<std::string> messages(nranks);
messages[0] = msg; // Store rank 0's message
// Receive messages from other ranks
for (int r = 1; r < nranks; r++)
{
std::vector<char> buffer(lengths[r] + 1);
MPI_Recv(buffer.data(), lengths[r], MPI_CHAR,
r, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
messages[r] = std::string(buffer.data(), lengths[r]);
}
// Print all messages in rank order
for (int r = 0; r < nranks; r++)
{
std::cout << "[Rank " << r << "] " << messages[r] << std::endl;
}
std::cout.flush();
}
else
{
// Other ranks: Send message to rank 0
MPI_Send(msg.c_str(), msg_len, MPI_CHAR,
0, 0, MPI_COMM_WORLD);
}
// Final barrier to ensure completion
MPI_Barrier(MPI_COMM_WORLD);
}
/// @brief Pretty print an mfem::Vector with MPI rank
///
/// @param v Vector to print
/// @param myrank MPI rank
/// @param comm MPI communicator
void pretty_print_mpi(const mfem::Vector& v)
{
std::stringstream ss;
ss << "[";
for (int i = 0; i < v.Size(); i++)
{
ss << v(i);
if (i < v.Size() - 1) { ss << ", "; }
}
ss << "]";
print_mpi_sync(ss.str());
}
template <typename ... Ts>
constexpr auto decay_types(mfem::tuple<Ts...> const &)
-> mfem::tuple<std::remove_cv_t<std::remove_reference_t<Ts>>...>;
@@ -485,10 +570,11 @@ void forall(func_t f,
class FDJacobian : public Operator
{
public:
FDJacobian(const Operator &op, const Vector &x) :
FDJacobian(const Operator &op, const Vector &x, real_t fixed_eps = 0.0) :
Operator(op.Height(), op.Width()),
op(op),
x(x)
x(x),
fixed_eps(fixed_eps)
{
f.SetSize(Height());
xpev.SetSize(Width());
@@ -506,7 +592,15 @@ public:
// 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 = lambda * (lambda + xnorm / v.Norml2());
real_t eps;
if (fixed_eps > 0.0)
{
eps = fixed_eps;
}
else
{
eps = lambda * (lambda + xnorm / v.Norml2());
}
for (int i = 0; i < x.Size(); i++)
{
@@ -533,6 +627,7 @@ private:
Vector x, f;
mutable Vector xpev;
real_t lambda = 1.0e-6;
real_t fixed_eps;
real_t xnorm;
};
+59 -19
View File
@@ -201,13 +201,16 @@ const DenseTensor ND_DofTransformation
::TInv(const_cast<real_t *>(TInv_data), 2, 2, 6);
ND_DofTransformation::ND_DofTransformation(int size, int p, int num_edges,
int num_tri_faces)
int num_faces,
int face_types[])
: StatelessDofTransformation(size)
, order(p)
, nedofs(p)
, nfdofs(p*(p-1))
, ntdofs(p*(p-1))
, nqdofs(2*p*(p-1))
, nedges(num_edges)
, nfaces(num_tri_faces)
, nfaces(num_faces)
, ftypes(face_types)
{
}
@@ -221,6 +224,7 @@ void ND_DofTransformation::TransformPrimal(const Array<int> & Fo,
"Face orientation array is shorter than the number of faces in "
"ND_DofTransformation");
int of = 0;
real_t data[2];
Vector v2(data, 2);
DenseMatrix T2;
@@ -228,11 +232,19 @@ void ND_DofTransformation::TransformPrimal(const Array<int> & Fo,
// Transform face DoFs
for (int f=0; f<nfaces; f++)
{
for (int i=0; i<nfdofs/2; i++)
if (ftypes[f] == Geometry::TRIANGLE)
{
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
T2.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
for (int i=0; i<ntdofs/2; i++)
{
v2 = &v[nedges*nedofs + of + 2*i];
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
T2.Mult(v2, &v[nedges*nedofs + of + 2*i]);
}
of += ntdofs;
}
else
{
of += nqdofs;
}
}
}
@@ -247,6 +259,7 @@ void ND_DofTransformation::InvTransformPrimal(const Array<int> & Fo,
"Face orientation array is shorter than the number of faces in "
"ND_DofTransformation");
int of = 0;
real_t data[2];
Vector v2(data, 2);
DenseMatrix T2Inv;
@@ -254,11 +267,19 @@ void ND_DofTransformation::InvTransformPrimal(const Array<int> & Fo,
// Transform face DoFs
for (int f=0; f<nfaces; f++)
{
for (int i=0; i<nfdofs/2; i++)
if (ftypes[f] == Geometry::TRIANGLE)
{
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
for (int i=0; i<ntdofs/2; i++)
{
v2 = &v[nedges*nedofs + of + 2*i];
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.Mult(v2, &v[nedges*nedofs + of + 2*i]);
}
of += ntdofs;
}
else
{
of += nqdofs;
}
}
}
@@ -272,6 +293,7 @@ void ND_DofTransformation::TransformDual(const Array<int> & Fo, real_t *v) const
"Face orientation array is shorter than the number of faces in "
"ND_DofTransformation");
int of = 0;
real_t data[2];
Vector v2(data, 2);
DenseMatrix T2Inv;
@@ -279,12 +301,21 @@ void ND_DofTransformation::TransformDual(const Array<int> & Fo, real_t *v) const
// Transform face DoFs
for (int f=0; f<nfaces; f++)
{
for (int i=0; i<nfdofs/2; i++)
if (ftypes[f] == Geometry::TRIANGLE)
{
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
for (int i=0; i<ntdofs/2; i++)
{
v2 = &v[nedges*nedofs + of + 2*i];
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.MultTranspose(v2, &v[nedges*nedofs + of + 2*i]);
}
of += ntdofs;
}
else
{
of += nqdofs;
}
}
}
@@ -298,6 +329,7 @@ void ND_DofTransformation::InvTransformDual(const Array<int> & Fo,
"Face orientation array is shorter than the number of faces in "
"ND_DofTransformation");
int of = 0;
real_t data[2];
Vector v2(data, 2);
DenseMatrix T2;
@@ -305,11 +337,19 @@ void ND_DofTransformation::InvTransformDual(const Array<int> & Fo,
// Transform face DoFs
for (int f=0; f<nfaces; f++)
{
for (int i=0; i<nfdofs/2; i++)
if (ftypes[f] == Geometry::TRIANGLE)
{
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
T2.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
for (int i=0; i<ntdofs/2; i++)
{
v2 = &v[nedges*nedofs + of + 2*i];
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
T2.MultTranspose(v2, &v[nedges*nedofs + of + 2*i]);
}
of += ntdofs;
}
else
{
of += nqdofs;
}
}
}
+31 -10
View File
@@ -306,13 +306,16 @@ private:
static const DenseTensor T, TInv;
protected:
const int order; // basis function order
const int nedofs; // number of DoFs per edge
const int nfdofs; // number of DoFs per face
const int nedges; // number of edges per element
const int nfaces; // number of triangular faces per element
const int order; // basis function order
const int nedofs; // number of DoFs per edge
const int ntdofs; // number of DoFs per triangular face
const int nqdofs; // number of DoFs per quadrilateral face
const int nedges; // number of edges per element
const int nfaces; // number of faces per element
const int *ftypes; // Pointer to array of Geometry::Type for each face
ND_DofTransformation(int size, int order, int num_edges, int num_tri_faces);
ND_DofTransformation(int size, int order, int num_edges, int num_faces,
int *face_types);
public:
// Return the 2x2 transformation operator for the given face orientation
@@ -322,7 +325,7 @@ public:
static const DenseMatrix & GetFaceInverseTransform(int ori)
{ return TInv(ori); }
bool IsIdentity() const override { return nfdofs < 2; }
bool IsIdentity() const override { return ntdofs < 2; }
void TransformPrimal(const Array<int> & Fo, real_t *v) const override;
void InvTransformPrimal(const Array<int> & Fo, real_t *v) const override;
@@ -334,9 +337,11 @@ public:
/// triangles
class ND_TriDofTransformation : public ND_DofTransformation
{
private:
const int face_type[1] = { Geometry::TRIANGLE };
public:
ND_TriDofTransformation(int order)
: ND_DofTransformation(order*(order + 2), order, 3, 1)
: ND_DofTransformation(order*(order + 2), order, 3, 1, (int *)face_type)
{}
};
@@ -345,7 +350,9 @@ class ND_TetDofTransformation : public ND_DofTransformation
{
public:
ND_TetDofTransformation(int order)
: ND_DofTransformation(order*(order + 2)*(order + 3)/2, order, 6, 4)
: ND_DofTransformation(order*(order + 2)*(order + 3)/2, order, 6, 4,
(int *)Geometry::Constants<Geometry::TETRAHEDRON>::
FaceTypes)
{}
};
@@ -355,7 +362,21 @@ class ND_WedgeDofTransformation : public ND_DofTransformation
public:
ND_WedgeDofTransformation(int order)
: ND_DofTransformation(3 * order * ((order + 1) * (order + 2))/2,
order, 9, 2)
order, 9, 5,
(int *)Geometry::Constants<Geometry::PRISM>::
FaceTypes)
{}
};
/// DoF transformation implementation for the Nedelec basis on pyramid elements
class ND_PyramidDofTransformation : public ND_DofTransformation
{
public:
ND_PyramidDofTransformation(int order)
: ND_DofTransformation(2 * order * (order * (order + 1) + 2),
order, 8, 5,
(int *)Geometry::Constants<Geometry::PYRAMID>::
FaceTypes)
{}
};
+53 -10
View File
@@ -11,6 +11,8 @@
#include "../mesh/mesh_headers.hpp"
#include "fem.hpp"
#include "eltrans/eltrans_basis.hpp"
#include <cmath>
namespace mfem
@@ -66,7 +68,6 @@ const DenseMatrix &ElementTransformation::EvalInverseJ()
return invJ;
}
int InverseElementTransformation::FindClosestPhysPoint(
const Vector& pt, const IntegrationRule &ir)
{
@@ -180,12 +181,13 @@ int InverseElementTransformation::NewtonSolve(const Vector &pt,
const int dim = T->GetDimension();
const int sdim = T->GetSpaceDim();
IntegrationPoint xip, prev_xip;
real_t xd[3], yd[3], dxd[3], dx_norm = -1.0, err_phys, real_dx_norm = -1.0;
Vector x(xd, dim), y(yd, sdim), dx(dxd, dim);
real_t xd[3], yd[3], dxd[3], dxpd[3], dx_norm = -1.0, err_phys,
real_dx_norm = -1.0;
Vector x(xd, dim), y(yd, sdim), dx(dxd, dim), dx_prev(dxpd, dim);
bool hit_bdr = false, prev_hit_bdr = false;
// Use ip0 as initial guess:
xip = *ip0;
xip = ip0;
xip.Get(xd, dim); // xip -> x
if (print_level >= 3)
{
@@ -342,16 +344,18 @@ int InverseElementTransformation::Transform(const Vector &pt,
switch (init_guess_type)
{
case Center:
ip0 = &Geometries.GetCenter(T->GetGeometryType());
ip0 = Geometries.GetCenter(T->GetGeometryType());
break;
case ClosestPhysNode:
case ClosestRefNode:
{
const int order = std::max(T->Order()+rel_qpts_order, 0);
const int order = qpts_order >= 0
? qpts_order
: std::max(T->Order() + rel_qpts_order, 0);
if (order == 0)
{
ip0 = &Geometries.GetCenter(T->GetGeometryType());
ip0 = Geometries.GetCenter(T->GetGeometryType());
}
else
{
@@ -359,11 +363,45 @@ int InverseElementTransformation::Transform(const Vector &pt,
int closest_idx = (init_guess_type == ClosestPhysNode) ?
FindClosestPhysPoint(pt, RefG.RefPts) :
FindClosestRefPoint(pt, RefG.RefPts);
ip0 = &RefG.RefPts.IntPoint(closest_idx);
ip0 = RefG.RefPts.IntPoint(closest_idx);
}
break;
}
case EdgeScan:
{
const int order = qpts_order >= 0
? qpts_order
: std::max(T->Order() + rel_qpts_order, 0);
if (order == 0)
{
ip0 = Geometries.GetCenter(T->GetGeometryType());
}
else
{
auto &ir = *refiner.EdgeScan(T->GetGeometryType(), order + 1);
int res = Outside;
int npts = ir.GetNPoints();
// will return Inside if any test point reports Inside, Outside if
// all points report Outside, else Unknown
for (int i = 0; i < npts; ++i)
{
ip0 = ir.IntPoint(i);
int tmp_res = NewtonSolve(pt, ip);
switch (tmp_res)
{
case Inside:
return Inside;
case Outside:
break;
case Unknown:
res = Unknown;
break;
}
}
return res;
}
break;
}
case GivenPoint:
break;
@@ -444,6 +482,8 @@ int IsoparametricTransformation::OrderJ() const
return (FElem->GetOrder()-1);
case FunctionSpace::Qk:
return (FElem->GetOrder());
case FunctionSpace::Uk:
return (FElem->GetOrder());
default:
MFEM_ABORT("unsupported finite element");
}
@@ -458,6 +498,8 @@ int IsoparametricTransformation::OrderW() const
return (FElem->GetOrder() - 1) * FElem->GetDim();
case FunctionSpace::Qk:
return (FElem->GetOrder() * FElem->GetDim() - 1);
case FunctionSpace::Uk:
return (FElem->GetOrder() * FElem->GetDim() - 1);
default:
MFEM_ABORT("unsupported finite element");
}
@@ -477,6 +519,8 @@ int IsoparametricTransformation::OrderGrad(const FiniteElement *fe) const
return ((k-1)*(d-1)+(l-1));
case FunctionSpace::Qk:
return (k*(d-1)+(l-1));
case FunctionSpace::Uk:
return (k*(d-1)+(l-1));
default:
MFEM_ABORT("unsupported finite element");
}
@@ -712,5 +756,4 @@ real_t FaceElementTransformations::CheckConsistency(int print_level,
return max_dist;
}
}
+281 -30
View File
@@ -17,9 +17,13 @@
#include "intrules.hpp"
#include "fe.hpp"
#include "kernel_dispatch.hpp"
namespace mfem
{
class GridFunction;
class ElementTransformation
{
protected:
@@ -198,40 +202,46 @@ public:
/// Algorithms for selecting an initial guess.
enum InitGuessType
{
Center = 0, ///< Use the center of the reference element.
Center = 0, ///< Use the center of the reference element.
ClosestPhysNode = 1, /**<
Use the point returned by FindClosestPhysPoint() from a reference-space
grid of type and size controlled by SetInitGuessPointsType() and
SetInitGuessRelOrder(), respectively. */
ClosestRefNode = 2, /**<
Use the point returned by FindClosestRefPoint() from a reference-space
grid of type and size controlled by SetInitGuessPointsType() and
SetInitGuessRelOrder(), respectively. */
GivenPoint = 3 ///< Use a specific point, set with SetInitialGuess().
Use the point returned by FindClosestPhysPoint() from a reference-space
grid of type and size controlled by SetInitGuessPointsType() and
SetInitGuessRelOrder(), respectively. */
ClosestRefNode = 2, /**<
Use the point returned by FindClosestRefPoint() from a reference-space
grid of type and size controlled by SetInitGuessPointsType() and
SetInitGuessRelOrder(), respectively. */
GivenPoint = 3, ///< Use a specific point, set with SetInitialGuess().
EdgeScan =
4, /**< Performs full solves on multiple points along the r/s/t=0 edges
of the element. It is recommended that SetInitGuessRelOrder() is
chosen such that max(trans_order+order,0)+1 <= 4 with
SetInitGuessPointsType() as Quadrature1D::ClosedUniform. @see
GeometryRefiner::EdgeScan */
};
/// Solution strategy.
enum SolverType
{
Newton = 0, /**<
Use Newton's algorithm, without restricting the reference-space points
(iterates) to the reference element. */
Newton = 0, /**<
Use Newton's algorithm, without restricting the reference-space points
(iterates) to the reference element. */
NewtonSegmentProject = 1, /**<
Use Newton's algorithm, restricting the reference-space points to the
reference element by scaling back the Newton increments, i.e.
projecting new iterates, x_new, lying outside the element, to the
intersection of the line segment [x_old, x_new] with the boundary. */
NewtonElementProject = 2 /**<
Use Newton's algorithm, restricting the reference-space points to the
reference element by projecting new iterates, x_new, lying outside the
element, to the point on the boundary closest (in reference-space) to
x_new. */
Use Newton's algorithm, restricting the reference-space points to the
reference element by scaling back the Newton increments, i.e.
projecting new iterates, x_new, lying outside the element, to the
intersection of the line segment [x_old, x_new] with the boundary. */
NewtonElementProject = 2, /**<
Use Newton's algorithm, restricting the reference-space points to the
reference element by projecting new iterates, x_new, lying outside the
element, to the point on the boundary closest (in reference-space) to
x_new. */
};
/// Values returned by Transform().
enum TransformResult
{
Inside = 0, ///< The point is inside the element
Inside = 0, ///< The point is inside the element
Outside = 1, ///< The point is _probably_ outside the element
Unknown = 2 ///< The algorithm failed to determine where the point is
};
@@ -241,9 +251,11 @@ protected:
ElementTransformation *T;
// Parameters of the inversion algorithms:
const IntegrationPoint *ip0;
IntegrationPoint ip0;
int init_guess_type; // algorithm to use
GeometryRefiner refiner; // geometry refiner for initial guess
int qpts_order; // num_1D_qpts = rel_qpts_order + 1, or < 0 to use
// rel_qpts_order.
int rel_qpts_order; // num_1D_qpts = max(trans_order+rel_qpts_order,0)+1
int solver_type; // solution strategy to use
int max_iter; // max. number of Newton iterations
@@ -284,19 +296,19 @@ public:
tolerances. */
InverseElementTransformation(ElementTransformation *Trans = NULL)
: T(Trans),
ip0(NULL),
init_guess_type(Center),
refiner(Quadrature1D::OpenHalfUniform),
qpts_order(-1),
rel_qpts_order(-1),
solver_type(NewtonElementProject),
max_iter(16),
#ifdef MFEM_USE_DOUBLE
ref_tol(1e-15),
phys_rtol(1e-15),
phys_rtol(4e-15),
ip_tol(1e-8),
#elif defined(MFEM_USE_SINGLE)
ref_tol(1e-7),
phys_rtol(1e-7),
ref_tol(4e-7),
phys_rtol(1e-6),
ip_tol(1e-4),
#endif
print_level(-1)
@@ -314,16 +326,28 @@ public:
/** @brief Set the initial guess for subsequent calls to Transform(),
switching to the #GivenPoint #InitGuessType at the same time. */
void SetInitialGuess(const IntegrationPoint &init_ip)
{ ip0 = &init_ip; SetInitialGuessType(GivenPoint); }
{ ip0 = init_ip; SetInitialGuessType(GivenPoint); }
/// Set the Quadrature1D type used for the `Closest*` initial guess types.
/// Set the Quadrature1D type used for the `Closest*` and `EdgeScan` initial
/// guess types.
void SetInitGuessPointsType(int q_type) { refiner.SetType(q_type); }
/// Set the relative order used for the `Closest*` initial guess types.
/** The number of points in each spatial direction is given by the formula
max(trans_order+order,0)+1, where trans_order is the order of the current
ElementTransformation. */
void SetInitGuessRelOrder(int order) { rel_qpts_order = order; }
void SetInitGuessRelOrder(int order)
{
qpts_order = -1;
rel_qpts_order = order;
}
/** The number of points in each spatial direction is given by the formula
order+1. */
void SetInitGuessOrder(int order)
{
qpts_order = order;
}
/** @brief Specify which algorithm to use for solving the transformation
equation, i.e. when calling the Transform() method. */
@@ -373,6 +397,233 @@ public:
virtual int Transform(const Vector &pt, IntegrationPoint &ip);
};
/**
* @brief Performs batch inverse element transforms. Currently only supports
* non-mixed meshes with SEGMENT, SQUARE, or CUBE geometries. Mixed
* element order meshes are projected onto an equivalent uniform order mesh.
*/
class BatchInverseElementTransformation
{
// nodes grid function, not owned
const GridFunction *gf_ = nullptr;
// initial guess algorithm to use
InverseElementTransformation::InitGuessType init_guess_type =
InverseElementTransformation::ClosestPhysNode;
int qpts_order = -1; // num_1D_qpts = rel_qpts_order + 1, or < 0 to use
// rel_qpts_order.
// num_1D_qpts = max(trans_order+rel_qpts_order,0)+1
int rel_qpts_order = 0;
// solution strategy to use
InverseElementTransformation::SolverType solver_type =
InverseElementTransformation::NewtonElementProject;
// basis type stored in points1d
int basis_type = BasisType::Invalid;
// initial guess points type. Quadrature1D::Invalid is used for match
// basis_type.
int guess_points_type = Quadrature1D::Invalid;
// max. number of Newton iterations
int max_iter = 16;
// internal element node locations cache
Vector node_pos;
#ifdef MFEM_USE_DOUBLE
// reference space tolerance
real_t ref_tol = 1e-15;
// physical space tolerance (relative)
real_t phys_rtol = 4e-15;
#else
// reference space tolerance
real_t ref_tol = 4e-7;
// physical space tolerance (relative)
real_t phys_rtol = 1e-6;
#endif
// not owned, location of tensor product basis nodes in reference space
const Array<real_t> *points1d = nullptr;
public:
/// Uninitialized BatchInverseElementTransformation. Users must call
/// UpdateNodes before Transform.
BatchInverseElementTransformation();
///
/// Constructs a BatchInverseElementTransformation given @a nodes representing
/// the mesh nodes.
///
BatchInverseElementTransformation(const GridFunction &nodes,
MemoryType d_mt = MemoryType::DEFAULT);
///
/// Constructs a BatchInverseElementTransformation for a given @a mesh.
/// mesh.GetNodes() must not be null.
///
BatchInverseElementTransformation(const Mesh &mesh,
MemoryType d_mt = MemoryType::DEFAULT);
~BatchInverseElementTransformation();
/** @brief Choose how the initial guesses for subsequent calls to Transform()
will be selected. ClosestRefNode is currently not supported. */
void SetInitialGuessType(InverseElementTransformation::InitGuessType itype)
{
MFEM_ASSERT(itype != InverseElementTransformation::ClosestRefNode,
"ClosestRefNode is currently not supported");
init_guess_type = itype;
}
/// Set the Quadrature1D type used for the `Closest*` and `EdgeScan` initial
/// guess types.
void SetInitGuessPointsType(int q_type) { guess_points_type = q_type; }
/// Set the relative order used for the `Closest*` initial guess types.
/** The number of points in each spatial direction is given by the formula
max(trans_order+order,0)+1, where trans_order is the order of the
current ElementTransformation. */
void SetInitGuessRelOrder(int order)
{
qpts_order = -1;
rel_qpts_order = order;
}
/** The number of points in each spatial direction is given by the formula
order+1. */
void SetInitGuessOrder(int order) { qpts_order = order; }
/// @b Gets the basis type nodes are projected onto, or BasisType::Invalid if
/// uninitialized.
int GetBasisType() const { return basis_type; }
/** @brief Specify which algorithm to use for solving the transformation
equation, i.e. when calling the Transform() method. NewtonSegmentProject
is currently not supported. */
void SetSolverType(InverseElementTransformation::SolverType stype)
{
MFEM_ASSERT(stype != InverseElementTransformation::NewtonSegmentProject,
"NewtonSegmentProject is currently not supported");
solver_type = stype;
}
/// Set the maximum number of iterations when solving for a reference point.
void SetMaxIter(int max_it) { max_iter = max_it; }
/// Set the reference-space convergence tolerance.
void SetReferenceTol(real_t ref_sp_tol) { ref_tol = ref_sp_tol; }
/// Set the relative physical-space convergence tolerance.
void SetPhysicalRelTol(real_t phys_rel_tol) { phys_rtol = phys_rel_tol; }
/**
* @brief Updates internal datastructures if @a nodes change. Some version
* of UpdateNodes must be called at least once before calls to Transform if
* nodes have changed.
*/
void UpdateNodes(const GridFunction &nodes,
MemoryType d_mt = MemoryType::DEFAULT);
/**
* @brief Updates internal datastructures if @a mesh nodes change. Some version
* of UpdateNodes must be called at least once before calls to Transform if
* mesh nodes have changed. mesh.GetNodes() must not be null.
*/
void UpdateNodes(const Mesh &mesh, MemoryType d_mt = MemoryType::DEFAULT);
/** @brief Performs a batch request of a set of points belonging to the given
elements.
@a pts list of physical point coordinates ordered by
Ordering::Type::byNODES.
@a elems which element index to search for each corresponding point in
@a pts
@a types output search classification (@see
InverseElementTransformation::TransformResult).
@a refs result reference point coordinates ordered by
Ordering::Type::byNODES. If using InitGuessType::GivenPoint, this should
contain the initial guess for each point.
@a use_device hint for if device acceleration should be used.
Device acceleration is currently only implemented for meshes containing
only a single tensor product basis element type.
@a iters optional array storing how many iterations was spent on each
tested point
*/
void Transform(const Vector &pts, const Array<int> &elems, Array<int> &types,
Vector &refs, bool use_device = true,
Array<int> *iters = nullptr) const;
using ClosestPhysPointKernelType = void (*)(int, int, int, int,
const real_t *, const real_t *,
const int *, const real_t *,
const real_t *, real_t *);
// specialization params: Geom, SDim, use_device
MFEM_REGISTER_KERNELS(FindClosestPhysPoint, ClosestPhysPointKernelType,
(int, int, bool));
using ClosestPhysDofKernelType = void (*)(int, int, int,
const real_t *, const real_t *,
const int *, const real_t *,
real_t *);
// specialization params: Geom, SDim, use_device
MFEM_REGISTER_KERNELS(FindClosestPhysDof, ClosestPhysDofKernelType,
(int, int, bool));
using ClosestRefDofKernelType = void (*)(int, int, int, const real_t *,
const real_t *, const int *,
const real_t *, real_t *);
// specialization params: Geom, SDim, use_device
MFEM_REGISTER_KERNELS(FindClosestRefDof, ClosestRefDofKernelType,
(int, int, bool));
using ClosestRefPointKernelType = void (*)(int, int, int, int, const real_t *,
const real_t *, const int *,
const real_t *, const real_t *,
real_t *);
// specialization params: Geom, SDim, use_device
MFEM_REGISTER_KERNELS(FindClosestRefPoint, ClosestRefPointKernelType,
(int, int, bool));
using NewtonKernelType = void (*)(real_t, real_t, int, int, int, int,
const real_t *, const real_t *,
const int *, const real_t *, int *, int*,
real_t *);
// specialization params: Geom, SDim, SolverType, use_device
MFEM_REGISTER_KERNELS(NewtonSolve, NewtonKernelType,
(int, int, InverseElementTransformation::SolverType,
bool));
using NewtonEdgeScanKernelType = void (*)(real_t, real_t, int, int, int, int,
const real_t *, const real_t *,
const int *, const real_t *,
const real_t *, int, int *, int *,
real_t *);
// specialization params: Geom, SDim, SolverType, use_device
MFEM_REGISTER_KERNELS(NewtonEdgeScan, NewtonEdgeScanKernelType,
(int, int, InverseElementTransformation::SolverType,
bool));
struct Kernels { Kernels(); };
template <int Dim, int SDim>
static void AddFindClosestSpecialization()
{
FindClosestPhysPoint::Specialization<Dim, SDim, true>::Add();
FindClosestRefPoint::Specialization<Dim, SDim, true>::Add();
FindClosestPhysPoint::Specialization<Dim, SDim, false>::Add();
FindClosestRefPoint::Specialization<Dim, SDim, false>::Add();
FindClosestPhysDof::Specialization<Dim, SDim, true>::Add();
FindClosestRefDof::Specialization<Dim, SDim, true>::Add();
FindClosestPhysDof::Specialization<Dim, SDim, false>::Add();
FindClosestRefDof::Specialization<Dim, SDim, false>::Add();
}
template <int Dim, int SDim, InverseElementTransformation::SolverType SType>
static void AddNewtonSolveSpecialization()
{
NewtonSolve::Specialization<Dim, SDim, SType, true>::Add();
NewtonEdgeScan::Specialization<Dim, SDim, SType, true>::Add();
NewtonSolve::Specialization<Dim, SDim, SType, false>::Add();
NewtonEdgeScan::Specialization<Dim, SDim, SType, false>::Add();
}
};
/// A standard isoparametric element transformation
class IsoparametricTransformation : public ElementTransformation
{
+187
View File
@@ -0,0 +1,187 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_ELTRANS_BASIS
#define MFEM_ELTRANS_BASIS
#include "../../general/forall.hpp"
#include "../geom.hpp"
// this file contains utilities for computing nodal basis functions and their
// derivatives in device kernels
namespace mfem
{
namespace eltrans
{
/// Various utilities for working with different element geometries
template <int GeomType> struct GeometryUtils;
template <> struct GeometryUtils<Geometry::SEGMENT>
{
static constexpr MFEM_HOST_DEVICE int Dimension() { return 1; }
/// @b true if the given point x in ref space is inside the element
static bool MFEM_HOST_DEVICE inside(real_t x) { return x >= 0 && x <= 1; }
/// @b Bound the reference coordinate @a x += dx to be inside the segment.
/// @a dx is updated to be dx = project(x+dx) - x
/// @return true if x + dx hit a boundary
static bool MFEM_HOST_DEVICE project(real_t &x, real_t &dx)
{
real_t tmp = x;
x += dx;
if (x < 0)
{
x = 0;
dx = x - tmp;
return true;
}
if (x > 1)
{
x = 1;
dx = x - tmp;
return true;
}
return false;
}
};
template <> struct GeometryUtils<Geometry::SQUARE>
{
static constexpr MFEM_HOST_DEVICE int Dimension() { return 2; }
/// @b true if the given point (x,y) in ref space is inside the element
static bool MFEM_HOST_DEVICE inside(real_t x, real_t y)
{
return (x >= 0) && (x <= 1) && (y >= 0) && (y <= 1);
}
/// @b Bound the reference coordinate @a (x,y) += (dx,dy) to be inside the
/// square.
/// @a dx and @a dy are updated to be (dx,dy) = project(x+dx,y+dy) - (x,y)
/// @return true if (x,y) + (dx,dy) hit a boundary
static bool MFEM_HOST_DEVICE project(real_t &x, real_t &y, real_t &dx,
real_t &dy)
{
bool x_cond = GeometryUtils<Geometry::SEGMENT>::project(x, dx);
bool y_cond = GeometryUtils<Geometry::SEGMENT>::project(y, dy);
return x_cond || y_cond;
}
};
template <> struct GeometryUtils<Geometry::CUBE>
{
static constexpr MFEM_HOST_DEVICE int Dimension() { return 3; }
/// @b true if the given point (x,y,z) in ref space is inside the element
static bool MFEM_HOST_DEVICE inside(real_t x, real_t y, real_t z)
{
return (x >= 0) && (x <= 1) && (y >= 0) && (y <= 1) && (z >= 0) && (z <= 1);
}
/// @b Bound the reference coordinate @a (x,y,z) += (dx,dy,dz) to be inside
/// the cube.
/// @a dx, @a dy, and @ dz are updated to be
/// (dx,dy,dz) = project(x+dx,y+dy,z+dz) - (x,y,z)
/// @return true if (x,y,z) + (dx,dy,dz) hit a boundary
static bool MFEM_HOST_DEVICE project(real_t &x, real_t &y, real_t &z,
real_t &dx, real_t &dy, real_t &dz)
{
bool x_cond = GeometryUtils<Geometry::SEGMENT>::project(x, dx);
bool y_cond = GeometryUtils<Geometry::SEGMENT>::project(y, dy);
bool z_cond = GeometryUtils<Geometry::SEGMENT>::project(z, dz);
return x_cond || y_cond || z_cond;
}
};
/// 1D Lagrange basis from [0, 1]
class Lagrange
{
public:
/// interpolant node locations, in reference space
const real_t *z;
/// number of points
int pN;
/// @b Evaluates the @a i'th Lagrange polynomial at @a x
real_t MFEM_HOST_DEVICE eval(real_t x, int i) const
{
real_t u0 = 1;
real_t den = 1;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
real_t d_j = (x - z[j]);
u0 = d_j * u0;
den *= (z[i] - z[j]);
}
}
den = 1 / den;
return u0 * den;
}
/// @b Evaluates the @a i'th Lagrange polynomial and its first derivative at
/// @a x
void MFEM_HOST_DEVICE eval_d1(real_t &p, real_t &d1, real_t x, int i) const
{
real_t u0 = 1;
real_t u1 = 0;
real_t den = 1;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
real_t d_j = (x - z[j]);
u1 = d_j * u1 + u0;
u0 = d_j * u0;
den *= (z[i] - z[j]);
}
}
den = 1 / den;
p = u0 * den;
d1 = u1 * den;
}
/// @b Evaluates the @a i'th Lagrange polynomial and its first and second
/// derivatives at @a x
void MFEM_HOST_DEVICE eval_d2(real_t &p, real_t &d1, real_t &d2, real_t x,
int i) const
{
real_t u0 = 1;
real_t u1 = 0;
real_t u2 = 0;
real_t den = 1;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
real_t d_j = (x - z[j]);
u2 = d_j * u2 + u1;
u1 = d_j * u1 + u0;
u0 = d_j * u0;
den *= (z[i] - z[j]);
}
}
den = 1 / den;
p = den * u0;
d1 = den * u1;
d2 = 2 * den * u2;
}
};
} // namespace eltrans
} // namespace mfem
#endif
+2 -2
View File
@@ -623,7 +623,7 @@ private:
public:
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
@param di_ The bilinearform to compute the interface flux.
@param di_ The bilinear form to compute the interface flux.
@param sol_ The solution field whose error is to be estimated.
@param flux_fes_ The finite element space for the interface flux.
@param attributes_ The attributes of the subdomain(s) for which the
@@ -635,7 +635,7 @@ public:
const Array<int> &attributes_ = Array<int>());
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
@param di_ The bilinearform to compute the interface flux.
@param di_ The bilinear form to compute the interface flux.
@param sol_ The solution field whose error is to be estimated.
@param flux_fes_ The finite element space for the interface flux.
@param attributes_ The attributes of the subdomain(s) for which the
+41 -17
View File
@@ -69,16 +69,16 @@ inline int ToLexOrdering2D(const int face_id, const int size1d, const int i)
}
/// @brief Given a face DOF index on a shared face, ordered lexicographically
/// relative to element 1, return the corresponding face DOF index ordered
/// lexicographically relative to element 2.
/// relative to element the element (where the local face is face_id), and
/// return the corresponding face DOF index ordered lexicographically relative
/// to the face itself.
MFEM_HOST_DEVICE
inline int PermuteFace2D(const int face_id1, const int face_id2,
const int orientation, const int size1d,
const int index)
inline int PermuteFace2D(const int face_id, const int orientation,
const int size1d, const int index)
{
int new_index;
// Convert from element 1 lex ordering to native ordering
if (face_id1 == 2 || face_id1 == 3)
if (face_id == 2 || face_id == 3)
{
new_index = size1d-1-index;
}
@@ -91,7 +91,18 @@ inline int PermuteFace2D(const int face_id1, const int face_id2,
{
new_index = size1d-1-new_index;
}
// Covert to element 2 lex ordering
return new_index;
}
/// @brief Given a face DOF index on a shared face, ordered lexicographically
/// relative to element 1, return the corresponding face DOF index ordered
/// lexicographically relative to element 2.
MFEM_HOST_DEVICE
inline int PermuteFace2D(const int face_id1, const int face_id2,
const int orientation, const int size1d,
const int index)
{
const int new_index = PermuteFace2D(face_id1, orientation, size1d, index);
return ToLexOrdering2D(face_id2, size1d, new_index);
}
@@ -116,26 +127,22 @@ inline int ToLexOrdering3D(const int face_id, const int size1d, const int i,
}
}
/// @brief Given the index of a face DOF in lexicographic ordering relative
/// element 1, permute the index so that it is lexicographically ordered
/// relative to element 2.
///
/// The given face corresponds to local face index @a face_id1 relative to
/// element 1, and @a face_id2 (with @a orientation) relative to element 2.
/// @brief Given the index of a face DOF in lexicographic ordering relative the
/// element (where the local face id is @a face_id), permute the index so that
/// it is lexicographically ordered relative to the face itself.
MFEM_HOST_DEVICE
inline int PermuteFace3D(const int face_id1, const int face_id2,
const int orientation,
inline int PermuteFace3D(const int face_id, const int orientation,
const int size1d, const int index)
{
int i=0, j=0, new_i=0, new_j=0;
i = index%size1d;
j = index/size1d;
// Convert from lex ordering
if (face_id1==3 || face_id1==4)
if (face_id==3 || face_id==4)
{
i = size1d-1-i;
}
else if (face_id1==0)
else if (face_id==0)
{
j = size1d-1-j;
}
@@ -175,6 +182,23 @@ inline int PermuteFace3D(const int face_id1, const int face_id2,
new_j = (size1d-1-j);
break;
}
return new_i + new_j*size1d;
}
/// @brief Given the index of a face DOF in lexicographic ordering relative
/// element 1, permute the index so that it is lexicographically ordered
/// relative to element 2.
///
/// The given face corresponds to local face index @a face_id1 relative to
/// element 1, and @a face_id2 (with @a orientation) relative to element 2.
MFEM_HOST_DEVICE
inline int PermuteFace3D(const int face_id1, const int face_id2,
const int orientation,
const int size1d, const int index)
{
const int new_index = PermuteFace3D(face_id1, orientation, size1d, index);
const int new_i = new_index%size1d;
const int new_j = new_index/size1d;
return ToLexOrdering3D(face_id2, size1d, new_i, new_j);
}
+95 -62
View File
@@ -973,6 +973,22 @@ void NodalFiniteElement::ProjectDiv(
}
}
void NodalFiniteElement::ReorderLexToNative(int ncomp,
Vector &dofs) const
{
MFEM_ASSERT(lex_ordering.Size() == dof, "Permutation is not defined by FE.");
MFEM_ASSERT(dofs.Size() == ncomp * dof, "Wrong input size.");
Vector dofs_native(ncomp * dof);
for (int i = 0; i < dof; i++)
{
for (int c = 0; c < ncomp; c++)
{
dofs_native(c*dof + lex_ordering[i]) = dofs(c*dof + i);
}
}
dofs = dofs_native;
}
VectorFiniteElement::VectorFiniteElement(int D, Geometry::Type G,
int Do, int O, int M, int F)
@@ -2168,6 +2184,64 @@ void Poly_1D::CalcDBinomTerms(const int p, const real_t x, const real_t y,
}
}
void Poly_1D::CalcDxBinomTerms(const int p, const real_t x, const real_t y,
real_t *u)
{
if (p == 0)
{
u[0] = 0.;
}
else
{
int i;
const int *b = Binom(p);
real_t z = 1.;
for (i = 1; i < p; i++)
{
u[i] = i * b[i]*z;
z *= x;
}
u[p] = i * z;
z = y;
for (i--; i > 0; i--)
{
u[i] *= z;
z *= y;
}
u[0] = 0;
}
}
void Poly_1D::CalcDyBinomTerms(const int p, const real_t x, const real_t y,
real_t *u)
{
if (p == 0)
{
u[0] = 0.;
}
else
{
int i;
const int *b = Binom(p);
real_t z = x;
for (i = 1; i < p; i++)
{
u[i] = b[i]*z;
z *= x;
}
u[p] = 0.;
z = 1.;
for (i--; i > 0; i--)
{
u[i] *= (p - i) * z;
z *= y;
}
u[0] = p * z;
}
}
void Poly_1D::CalcLegendre(const int p, const real_t x, real_t *u)
{
// use the recursive definition for [-1,1]:
@@ -2267,99 +2341,58 @@ void Poly_1D::CalcChebyshev(const int p, const real_t x, real_t *u, real_t *d,
}
}
const real_t *Poly_1D::GetPoints(const int p, const int btype)
const Array<real_t>* Poly_1D::GetPointsArray(const int p, const int btype)
{
Array<real_t*> *pts;
Array<real_t> *val;
BasisType::Check(btype);
const int qtype = BasisType::GetQuadrature1D(btype);
if (qtype == Quadrature1D::Invalid) { return NULL; }
if (qtype == Quadrature1D::Invalid) { return nullptr; }
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (Poly1DGetPoints)
#endif
{
auto it = points_container.find(btype);
if (it != points_container.end())
std::pair<int, int> key(btype, p);
auto it = points_container.find(key);
if (it == points_container.end())
{
pts = it->second;
it = points_container.emplace(key, new Array<real_t>(p + 1, h_mt)).first;
val = it->second.get();
real_t* hptr = val->HostWrite();
quad_func.GivePolyPoints(p + 1, hptr, qtype);
}
else
{
pts = new Array<real_t*>(h_mt);
points_container[btype] = pts;
}
if (pts->Size() <= p)
{
pts->SetSize(p + 1, NULL);
}
if ((*pts)[p] == NULL)
{
(*pts)[p] = new real_t[p + 1];
quad_func.GivePolyPoints(p + 1, (*pts)[p], qtype);
val = it->second.get();
}
}
return (*pts)[p];
return val;
}
Poly_1D::Basis &Poly_1D::GetBasis(const int p, const int btype)
{
Array<Basis*> *bases;
BasisType::Check(btype);
Basis* val;
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (Poly1DGetBasis)
#endif
{
auto it = bases_container.find(btype);
if (it != bases_container.end())
{
bases = it->second;
}
else
{
// we haven't been asked for basis or points of this type yet
bases = new Array<Basis*>(h_mt);
bases_container[btype] = bases;
}
if (bases->Size() <= p)
{
bases->SetSize(p + 1, NULL);
}
if ((*bases)[p] == NULL)
std::pair<int, int> key(btype, p);
auto it = bases_container.find(key);
if (it == bases_container.end())
{
EvalType etype;
if (btype == BasisType::Positive) { etype = Positive; }
else if (btype == BasisType::IntegratedGLL) { etype = Integrated; }
else { etype = Barycentric; }
(*bases)[p] = new Basis(p, GetPoints(p, btype), etype);
it = bases_container
.emplace(key, new Basis(p, GetPoints(p, btype), etype))
.first;
}
val = it->second.get();
}
return *(*bases)[p];
}
Poly_1D::~Poly_1D()
{
for (PointsMap::iterator it = points_container.begin();
it != points_container.end() ; ++it)
{
Array<real_t*>& pts = *it->second;
for (int i = 0; i < pts.Size(); ++i)
{
delete [] pts[i];
}
delete it->second;
}
for (BasisMap::iterator it = bases_container.begin();
it != bases_container.end() ; ++it)
{
Array<Basis*>& bases = *it->second;
for (int i = 0; i < bases.Size(); ++i)
{
delete bases[i];
}
delete it->second;
}
return *val;
}
+61 -11
View File
@@ -15,8 +15,12 @@
#include "../intrules.hpp"
#include "../geom.hpp"
#include "../doftrans.hpp"
#include "../../general/hash.hpp"
#include <map>
#include <memory>
#include <unordered_map>
#include <utility>
namespace mfem
{
@@ -223,9 +227,10 @@ class FunctionSpace
public:
enum
{
Pk, ///< Polynomials of order k
Qk, ///< Tensor products of polynomials of order k
rQk ///< Refined tensor products of polynomials of order k
Pk, ///< Polynomials of order k
Qk, ///< Tensor products of polynomials of order k
rQk, ///< Refined tensor products of polynomials of order k
Uk ///< Rational polynomials of order k
};
};
@@ -794,6 +799,12 @@ public:
TensorBasisElement::GetDofMap, but it is also available for non-tensor
elements. */
const Array<int> &GetLexicographicOrdering() const { return lex_ordering; }
/// Given a lexicographically ordered Vector @a dofs, containing @a ncomp
/// components of the size of the scalar FiniteElement, reorder its entries
/// into native (H1) ordering.
/// The function assumes that GetLexicographicOrdering() is not empty.
void ReorderLexToNative(int ncomp, Vector &dofs) const;
};
/** @brief Intermediate class for finite elements whose basis functions return
@@ -1038,8 +1049,14 @@ public:
};
private:
typedef std::map<int, Array<real_t*>*> PointsMap;
typedef std::map<int, Array<Basis*>*> BasisMap;
/// key: (btype, p), value: underlying storage Array
typedef std::unordered_map<std::pair<int, int>,
std::unique_ptr<Basis>, PairHasher>
BasisMap;
/// key: (btype, p), value: underlying storage Array
typedef std::unordered_map<std::pair<int, int>,
std::unique_ptr<Array<real_t>>, PairHasher>
PointsMap;
MemoryType h_mt;
PointsMap points_container;
@@ -1073,17 +1090,40 @@ public:
@return A pointer to an array containing the `p+1` coordinates of the
points. Returns NULL if the BasisType has no associated set of
points. */
const real_t *GetPoints(const int p, const int btype);
const Array<real_t>* GetPointsArray(const int p, const int btype);
/** @brief Get the coordinates of the points of the given BasisType,
@a btype.
@param[in] p The polynomial degree; the number of points is `p+1`.
@param[in] btype The BasisType.
@param[in] on_device true if the requested pointer should be accessible
from the device.
@return A pointer to an array containing the `p+1` coordinates of the
points. Returns NULL if the BasisType has no associated set of
points. */
const real_t *GetPoints(const int p, const int btype,
bool on_device = false)
{
return GetPointsArray(p, btype)->Read(on_device);
}
/// Get coordinates of an open (GaussLegendre) set of points if degree @a p
const real_t *OpenPoints(const int p,
const int btype = BasisType::GaussLegendre)
{ return GetPoints(p, btype); }
const int btype = BasisType::GaussLegendre,
bool on_device = false)
{
return GetPoints(p, btype, on_device);
}
/// Get coordinates of a closed (GaussLegendre) set of points if degree @a p
const real_t *ClosedPoints(const int p,
const int btype = BasisType::GaussLobatto)
{ return GetPoints(p, btype); }
const int btype = BasisType::GaussLobatto,
bool on_device = false)
{
return GetPoints(p, btype, on_device);
}
/** @brief Get a Poly_1D::Basis object of the given degree and BasisType,
@a btype.
@@ -1158,6 +1198,16 @@ public:
in the already allocated @a d array.*/
static void CalcDBinomTerms(const int p, const real_t x, const real_t y,
real_t *d);
/** @brief Compute the derivatives (w.r.t. x) of the terms in the expansion
of the binomial (x + y)^p. Store the results in the already allocated
@a d array.*/
static void CalcDxBinomTerms(const int p, const real_t x, const real_t y,
real_t *d);
/** @brief Compute the derivatives (w.r.t. y) of the terms in the expansion
of the binomial (x + y)^p. Store the results in the already allocated
@a d array.*/
static void CalcDyBinomTerms(const int p, const real_t x, const real_t y,
real_t *d);
/** @brief Compute the values of the Bernstein basis functions of order
@a p at coordinate @a x and store the results in the already allocated
@@ -1186,7 +1236,7 @@ public:
static void CalcLegendre(const int p, const real_t x, real_t *u);
static void CalcLegendre(const int p, const real_t x, real_t *u, real_t *d);
~Poly_1D();
~Poly_1D() = default;
};
extern MFEM_EXPORT Poly_1D poly1d;
+573 -34
View File
@@ -1341,7 +1341,7 @@ const
LinearPyramidFiniteElement::LinearPyramidFiniteElement()
: NodalFiniteElement(3, Geometry::PYRAMID, 5, 1)
: NodalFiniteElement(3, Geometry::PYRAMID, 5, 1, FunctionSpace::Uk)
{
Nodes.IntPoint(0).x = 0.0;
Nodes.IntPoint(0).y = 0.0;
@@ -3015,7 +3015,7 @@ void P0WdgFiniteElement::CalcDShape(const IntegrationPoint &ip,
P0PyrFiniteElement::P0PyrFiniteElement()
: NodalFiniteElement(3, Geometry::PYRAMID, 1, 0, FunctionSpace::Qk)
: NodalFiniteElement(3, Geometry::PYRAMID, 1, 0, FunctionSpace::Uk)
{
Nodes.IntPoint(0).x = 0.375;
Nodes.IntPoint(0).y = 0.375;
@@ -4818,7 +4818,7 @@ void Nedelec1WdgFiniteElement::ProjectGrad(const FiniteElement &fe,
Nedelec1PyrFiniteElement::Nedelec1PyrFiniteElement()
: VectorFiniteElement(3, Geometry::PYRAMID, 8, 1, H_CURL)
: VectorFiniteElement(3, Geometry::PYRAMID, 8, 1, H_CURL, FunctionSpace::Uk)
{
// not real nodes ...
Nodes.IntPoint(0).x = 0.5;
@@ -4866,7 +4866,11 @@ void Nedelec1PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
{
// We must return the limit of the basis functions as z->1. In order to
// remain inside the pyramid in this limit the x and y coordinates must
// be approaching 0. The resulting limiting basis function values are:
// be approaching 0. Unfortunately we obtain different limits if we
// approach (0,0,1) from different directions. The values provided below
// are the limits as x->(1-z)/2 and y->(1-z)/2 i.e. along the line from
// the center of the base of the pyramid towards the apex. The resulting
// limiting basis function values are:
shape(0,0) = 0.;
shape(0,1) = 0.;
shape(0,2) = 0.;
@@ -4883,21 +4887,21 @@ void Nedelec1PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
shape(3,1) = 0.;
shape(3,2) = 0.;
shape(4,0) = 1.;
shape(4,1) = 1.;
shape(4,2) = 1.;
shape(4,0) = 0.5;
shape(4,1) = 0.5;
shape(4,2) = 0.75;
shape(5,0) = - 1.;
shape(5,1) = 0.;
shape(5,2) = 0.;
shape(5,0) = - 0.5;
shape(5,1) = 0.5;
shape(5,2) = 0.25;
shape(6,0) = 0.;
shape(6,1) = 0.;
shape(6,2) = 0.;
shape(6,0) = - 0.5;
shape(6,1) = - 0.5;
shape(6,2) = - 0.25;
shape(7,0) = 0.;
shape(7,1) = - 1.;
shape(7,2) = 0.;
shape(7,0) = 0.5;
shape(7,1) = - 0.5;
shape(7,2) = 0.25;
return;
}
@@ -4952,36 +4956,36 @@ const
// In order to remain inside the pyramid in this limit the x and y
// coordinates must be approaching 0. The resulting limiting basis
// function values are:
curl_shape(0,0) = 0.;
curl_shape(0,1) = - 2.;
curl_shape(0,0) = - 0.5;
curl_shape(0,1) = - 1.5;
curl_shape(0,2) = 1.;
curl_shape(1,0) = 0.;
curl_shape(1,1) = 0.;
curl_shape(1,0) = 0.5;
curl_shape(1,1) = - 0.5;
curl_shape(1,2) = 1.;
curl_shape(2,0) = 0.;
curl_shape(2,1) = 0.;
curl_shape(2,0) = 0.5;
curl_shape(2,1) = - 0.5;
curl_shape(2,2) = - 1.;
curl_shape(3,0) = 2.;
curl_shape(3,1) = 0.;
curl_shape(3,0) = 1.5;
curl_shape(3,1) = 0.5;
curl_shape(3,2) = - 1.;
curl_shape(4,0) = - 2.;
curl_shape(4,1) = 2.;
curl_shape(4,0) = - 1.;
curl_shape(4,1) = 1.;
curl_shape(4,2) = 0.;
curl_shape(5,0) = 0.;
curl_shape(5,1) = - 2.;
curl_shape(5,0) = - 1.;
curl_shape(5,1) = - 1.;
curl_shape(5,2) = 0.;
curl_shape(6,0) = 0.;
curl_shape(6,1) = 0.;
curl_shape(6,0) = 1.;
curl_shape(6,1) = - 1.;
curl_shape(6,2) = 0.;
curl_shape(7,0) = 2.;
curl_shape(7,1) = 0.;
curl_shape(7,0) = 1.;
curl_shape(7,1) = 1.;
curl_shape(7,2) = 0.;
return;
@@ -5119,6 +5123,540 @@ void Nedelec1PyrFiniteElement::ProjectGrad(const FiniteElement &fe,
}
Nedelec2PyrFiniteElement::Nedelec2PyrFiniteElement()
: VectorFiniteElement(3, Geometry::PYRAMID, 28, 2, H_CURL, FunctionSpace::Uk)
{
const real_t *eop = poly1d.OpenPoints(2 - 1);
const real_t fop = 1. / 3.;
// not real nodes ...
Nodes.IntPoint(0).Set3(eop[0], 0., 0.);
Nodes.IntPoint(1).Set3(eop[1], 0., 0.);
Nodes.IntPoint(2).Set3(1.0, eop[0], 0.);
Nodes.IntPoint(3).Set3(1.0, eop[1], 0.);
Nodes.IntPoint(4).Set3(eop[0], 1.0, 0.);
Nodes.IntPoint(5).Set3(eop[1], 1.0, 0.);
Nodes.IntPoint(6).Set3(0., eop[0], 0.);
Nodes.IntPoint(7).Set3(0., eop[1], 0.);
Nodes.IntPoint(8).Set3(0., 0., eop[0]);
Nodes.IntPoint(9).Set3(0., 0., eop[1]);
Nodes.IntPoint(10).Set3(eop[1], 0., eop[0]);
Nodes.IntPoint(11).Set3(eop[0], 0., eop[1]);
Nodes.IntPoint(12).Set3(eop[1], eop[1], eop[0]);
Nodes.IntPoint(13).Set3(eop[0], eop[0], eop[1]);
Nodes.IntPoint(14).Set3(0., eop[1], eop[0]);
Nodes.IntPoint(15).Set3(0., eop[0], eop[1]);
Nodes.IntPoint(16).Set3(eop[0], 0.5, 0.);
Nodes.IntPoint(17).Set3(eop[1], 0.5, 0.);
Nodes.IntPoint(18).Set3(0.5, eop[0], 0.);
Nodes.IntPoint(19).Set3(0.5, eop[1], 0.);
Nodes.IntPoint(20).Set3(fop, 0., fop);
Nodes.IntPoint(21).Set3(fop, 0., fop);
Nodes.IntPoint(22).Set3(2.*fop, fop, fop);
Nodes.IntPoint(23).Set3(2.*fop, fop, fop);
Nodes.IntPoint(24).Set3(fop, 2.*fop, fop);
Nodes.IntPoint(25).Set3(fop, 2.*fop, fop);
Nodes.IntPoint(26).Set3(0., fop, fop);
Nodes.IntPoint(27).Set3(0., fop, fop);
{
int n = 28;
DenseMatrix I(n,n);
DenseMatrix vecs(n,3);
I = 0.0;
for (int i=0; i<n; i++)
{
CalcVShape(Nodes.IntPoint(i), vecs);
for (int j=0; j<n; j++)
{
I(j,i) = vecs(j,0)*tk[i][0]+vecs(j,1)*tk[i][1]+vecs(j,2)*tk[i][2];
}
}
}
}
void Nedelec2PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const
{
shape = 0.0;
const real_t one = 1.0;
const real_t x = ip.x, y = ip.y, z = ip.z;
const real_t ox = one - x - z, oy = one - y - z, oz = one - z;
const real_t sq3 = sqrt(3.0);
const real_t tol = 1e-6;
if (oz <= tol)
{
// We must return the limit of the basis functions as z->1. In order to
// remain inside the pyramid in this limit the x and y coordinates must
// be approaching 0. The resulting limiting basis function values are:
shape(0,0) = 0.;
shape(0,1) = 0.;
shape(0,2) = 0.;
shape(1,0) = 0.;
shape(1,1) = 0.;
shape(1,2) = 0.;
shape(2,0) = 0.;
shape(2,1) = 0.;
shape(2,2) = 0.;
shape(3,0) = 0.;
shape(3,1) = 0.;
shape(3,2) = 0.;
shape(4,0) = 0.;
shape(4,1) = 0.;
shape(4,2) = 0.;
shape(5,0) = 0.;
shape(5,1) = 0.;
shape(5,2) = 0.;
shape(6,0) = 0.;
shape(6,1) = 0.;
shape(6,2) = 0.;
shape(7,0) = 0.;
shape(7,1) = 0.;
shape(7,2) = 0.;
return;
}
const real_t ozi = one / oz;
const real_t me0120[3] = {oy, 0., x * oy * ozi};
const real_t me1120[3] = {(x - ox) * oy, 0., (x - ox) * x * oy * ozi};
const real_t me0121[3] = {y, 0., x * y * ozi};
const real_t me1121[3] = {(x - ox) * y, 0., (x - ox) * x * y * ozi};
const real_t me0210[3] = {0., ox, ox * y * ozi};
const real_t me1210[3] = {0., ox * (y - oy), ox * y * (y - oy) * ozi};
const real_t me0211[3] = {0., x, x * y * ozi};
const real_t me1211[3] = {0., x * (y - oy), x * y * (y - oy) * ozi};
const real_t te01[3] = {oy * z * ozi, ox * z * ozi,
(ox * oy + (x * oy + ox * y) * z) * ozi * ozi
};
const real_t te11[3] = {oy * z * (z * oz - ox * oy) * ozi * ozi,
ox * z * (z * oz - ox * oy) * ozi * ozi,
(ox * oy + z * (x * oy + ox * y)) *
(z * oz - ox * oy) * ozi * ozi * ozi
};
const real_t te02[3] = {-oy * z * ozi, x * z * ozi,
x * (y * z + oy * oz) * ozi * ozi
};
const real_t te12[3] = {oy * z * (x * oy - z * oz) * ozi * ozi,
-x * z * (x * oy - z * oz) * ozi * ozi,
-x * (y * z + oy * oz) * (x * oy - z * oz)
* ozi * ozi * ozi
};
const real_t te03[3] = {-y * z * ozi, -x * z * ozi,
x * y * (one - 2_r * z) * ozi * ozi
};
const real_t te13[3] = {y * z * (x * y - z * oz) * ozi * ozi,
x * z * (x * y - z * oz) * ozi * ozi,
-x * y * (one - 2_r * z) * (x * y - z * oz)
* ozi * ozi * ozi
};
const real_t te04[3] = {y * z * ozi, -ox * z * ozi,
y * (x * z + ox * oz) * ozi * ozi
};
const real_t te14[3] = {-y * z * (ox * y - z * oz) * ozi * ozi,
ox * z * (ox * y - z * oz) * ozi * ozi,
-y * (x * z + ox * oz) * (ox * y - z * oz)
* ozi * ozi * ozi
};
const real_t qI02[3] = {-y * oy * ozi, 0., -x * y * oy * ozi * ozi};
const real_t qI12[3] = {-(x - ox) * y * oy * ozi * ozi, 0.,
-(x - ox) * x * y * oy * ozi * ozi * ozi
};
const real_t qII02[3] = {0., -x * ox * ozi, -x * y * ox * ozi * ozi};
const real_t qII12[3] = {0., -x * ox * (y - oy) * ozi * ozi,
-x * ox * y * (y - oy) * ozi * ozi * ozi
};
const real_t tI120[3] = {oy * z, 0., x * oy * z * ozi};
const real_t tI121[3] = {y * z, 0., x * y * z * ozi};
const real_t tI210[3] = {0., ox * z, ox * y * z * ozi};
const real_t tI211[3] = {0., x * z, x * y * z * ozi};
const real_t tII120[3] = {-ox * oy * z * ozi, 0., x * ox * oy * ozi};
const real_t tII121[3] = {-ox * y * z * ozi, 0., x * ox * y * ozi};
const real_t tII210[3] = {0., -ox * oy * z * ozi, ox * y * oy * ozi};
const real_t tII211[3] = {0., -x * oy * z * ozi, x * y * oy * ozi};
// Edge 0,1
for (int d=0; d<3; d++)
{
shape(0,d) = 0.5 * me0120[d] + qI02[d]
- sq3 * (0.5 * me1120[d] + qI12[d]) - 1.5 * tI120[d];
}
for (int d=0; d<3; d++)
{
shape(1,d) = 0.5 * me0120[d] + qI02[d]
+ sq3 * (0.5 * me1120[d] + qI12[d]) - 1.5 * tI120[d];
}
// Edge 1,2
for (int d=0; d<3; d++)
{
shape(2,d) = 0.5 * me0211[d] + qII02[d]
- sq3 * (0.5 * me1211[d] + qII12[d]) - 1.5 * tI211[d];
}
for (int d=0; d<3; d++)
{
shape(3,d) = 0.5 * me0211[d] + qII02[d]
+ sq3 * (0.5 * me1211[d] + qII12[d]) - 1.5 * tI211[d];
}
// Edge 3,2
for (int d=0; d<3; d++)
{
shape(4,d) = 0.5 * me0121[d] + qI02[d]
- sq3 * (0.5 * me1121[d] + qI12[d]) - 1.5 * tI121[d];
}
for (int d=0; d<3; d++)
{
shape(5,d) = 0.5 * me0121[d] + qI02[d]
+ sq3 * (0.5 * me1121[d] + qI12[d]) - 1.5 * tI121[d];
}
// Edge 0,3
for (int d=0; d<3; d++)
{
shape(6,d) = 0.5 * me0210[d] + qII02[d]
- sq3 * (0.5 * me1210[d] + qII12[d]) - 1.5 * tI210[d];
}
for (int d=0; d<3; d++)
{
shape(7,d) = 0.5 * me0210[d] + qII02[d]
+ sq3 * (0.5 * me1210[d] + qII12[d]) - 1.5 * tI210[d];
}
// Edge 0,4
for (int d=0; d<3; d++)
{
shape(8,d) = 0.5 * te01[d] - sq3 * 0.5 * te11[d]
- 1.5 * (tI120[d] + tII120[d] + tI210[d] + tII210[d]);
}
for (int d=0; d<3; d++)
{
shape(9,d) = 0.5 * te01[d] + sq3 * 0.5 * te11[d]
- 1.5 * (tI120[d] + tII120[d] + tI210[d] + tII210[d]);
}
// Edge 1,4
for (int d=0; d<3; d++)
{
shape(10,d) = 0.5 * te02[d] - sq3 * 0.5 * te12[d]
- 1.5 * (tII120[d] + tI211[d] + tII211[d]);
}
for (int d=0; d<3; d++)
{
shape(11,d) = 0.5 * te02[d] + sq3 * 0.5 * te12[d]
- 1.5 * (tII120[d] + tI211[d] + tII211[d]);
}
// Edge 2,4
for (int d=0; d<3; d++)
{
shape(12,d) = 0.5 * te03[d] - sq3 * 0.5 * te13[d]
- 1.5 * (tII211[d] + tII121[d]);
}
for (int d=0; d<3; d++)
{
shape(13,d) = 0.5 * te03[d] + sq3 * 0.5 * te13[d]
- 1.5 * (tII211[d] + tII121[d]);
}
// Edge 3,4
for (int d=0; d<3; d++)
{
shape(14,d) = 0.5 * te04[d] - sq3 * 0.5 * te14[d]
- 1.5 * (tI121[d] + tII121[d] + tII210[d]);
}
for (int d=0; d<3; d++)
{
shape(15,d) = 0.5 * te04[d] + sq3 * 0.5 * te14[d]
- 1.5 * (tI121[d] + tII121[d] + tII210[d]);
}
// Quadrilateral face
for (int d=0; d<3; d++)
{
shape(16,d) = -2. * qI02[d] + 2. * sq3 * qI12[d];
}
for (int d=0; d<3; d++)
{
shape(17,d) = -2. * qI02[d] - 2. * sq3 * qI12[d];
}
for (int d=0; d<3; d++)
{
shape(18,d) = 2. * qII02[d] - 2. * sq3 * qII12[d];
}
for (int d=0; d<3; d++)
{
shape(19,d) = 2. * qII02[d] + 2. * sq3 * qII12[d];
}
// Triangular face 0,1,4
for (int d=0; d<3; d++)
{
shape(20,d) = 3. * tI120[d] - 3. * tII120[d];
}
for (int d=0; d<3; d++)
{
shape(21,d) = 3. * tI120[d] + 6. * tII120[d];
}
// Triangular face 1,2,4
for (int d=0; d<3; d++)
{
shape(22,d) = 3. * tI211[d] - 3. * tII211[d];
}
for (int d=0; d<3; d++)
{
shape(23,d) = 3. * tI211[d] + 6. * tII211[d];
}
// Triangular face 2,3,4
for (int d=0; d<3; d++)
{
shape(24,d) = -6. * tI121[d] - 3. * tII121[d];
}
for (int d=0; d<3; d++)
{
shape(25,d) = 3. * tI121[d] + 6. * tII121[d];
}
// Triangular face 3,0,4
for (int d=0; d<3; d++)
{
shape(26,d) = -6. * tI210[d] - 3. * tII210[d];
}
for (int d=0; d<3; d++)
{
shape(27,d) = 3. * tI210[d] + 6. * tII210[d];
}
}
void Nedelec2PyrFiniteElement::CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape)
const
{
const real_t one = 1.0;
const real_t x = ip.x, y = ip.y, z = ip.z, z2 = 2. * z;
const real_t ox = one - x - z, oy = one - y - z, oz = one - z;
const real_t tol = 1e-6;
if (oz <= tol)
{
// We must return the limit of the basis function derivatives as z->1.
// In order to remain inside the pyramid in this limit the x and y
// coordinates must be approaching 0. The resulting limiting basis
// function values are:
curl_shape(0,0) = 0.;
curl_shape(0,1) = - 2.;
curl_shape(0,2) = 1.;
curl_shape(1,0) = 0.;
curl_shape(1,1) = 0.;
curl_shape(1,2) = 1.;
curl_shape(2,0) = 0.;
curl_shape(2,1) = 0.;
curl_shape(2,2) = - 1.;
curl_shape(3,0) = 2.;
curl_shape(3,1) = 0.;
curl_shape(3,2) = - 1.;
curl_shape(4,0) = - 2.;
curl_shape(4,1) = 2.;
curl_shape(4,2) = 0.;
curl_shape(5,0) = 0.;
curl_shape(5,1) = - 2.;
curl_shape(5,2) = 0.;
curl_shape(6,0) = 0.;
curl_shape(6,1) = 0.;
curl_shape(6,2) = 0.;
curl_shape(7,0) = 2.;
curl_shape(7,1) = 0.;
curl_shape(7,2) = 0.;
return;
}
real_t ozi = one / oz;
curl_shape(0,0) = - x * ozi;
curl_shape(0,1) = - 2. + y * ozi;
curl_shape(0,2) = 1.;
curl_shape(1,0) = x * ozi;
curl_shape(1,1) = - y * ozi;
curl_shape(1,2) = 1.;
curl_shape(2,0) = x * ozi;
curl_shape(2,1) = - y * ozi;
curl_shape(2,2) = - 1.;
curl_shape(3,0) = (2. - x - z2) * ozi;
curl_shape(3,1) = y * ozi;
curl_shape(3,2) = - 1.;
curl_shape(4,0) = - 2. * ox * ozi;
curl_shape(4,1) = 2. * oy * ozi;
curl_shape(4,2) = 0.;
curl_shape(5,0) = - 2. * x * ozi;
curl_shape(5,1) = - 2. * oy * ozi;
curl_shape(5,2) = 0.;
curl_shape(6,0) = 2. * x * ozi;
curl_shape(6,1) = - 2. * y * ozi;
curl_shape(6,2) = 0.;
curl_shape(7,0) = 2. * ox * ozi;
curl_shape(7,1) = 2. * y * ozi;
curl_shape(7,2) = 0.;
}
const real_t Nedelec2PyrFiniteElement::tk[28][3] =
{
{1,0,0}, {1,0,0}, {0,1,0}, {0,1,0},
{1,0,0}, {1,0,0}, {0,1,0}, {0,1,0},
{0,0,1}, {0,0,1}, {-1,0,1}, {-1,0,1},
{-1,-1,1}, {-1,-1,1}, {0,-1,1}, {0,-1,1},
{1,0,0}, {1,0,0}, {0,-1,0}, {0,-1,0},
{1,0,0}, {0,0,1}, {0,1,0}, {-1,0,1},
{-1,0,0}, {-1,-1,1}, {0,-1,0}, {0,-1,1}
};
void Nedelec2PyrFiniteElement::GetLocalInterpolation (
ElementTransformation &Trans, DenseMatrix &I) const
{
int k, j;
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
#ifdef MFEM_DEBUG
for (k = 0; k < dof; k++)
{
CalcVShape (Nodes.IntPoint(k), vshape);
for (j = 0; j < dof; j++)
{
real_t d = ( vshape(j,0)*tk[k][0] + vshape(j,1)*tk[k][1] +
vshape(j,2)*tk[k][2] );
if (j == k) { d -= 1.0; }
if (fabs(d) > 1.0e-12)
{
mfem::err << "Nedelec1PyrFiniteElement::GetLocalInterpolation (...)\n"
" k = " << k << ", j = " << j << ", d = " << d << endl;
mfem_error();
}
}
}
#endif
IntegrationPoint ip;
ip.x = ip.y = ip.z = 0.0;
Trans.SetIntPoint (&ip);
// Trans must be linear
const DenseMatrix &J = Trans.Jacobian();
real_t vk[3];
Vector xk (vk, 3);
for (k = 0; k < dof; k++)
{
Trans.Transform (Nodes.IntPoint (k), xk);
ip.x = vk[0]; ip.y = vk[1]; ip.z = vk[2];
CalcVShape (ip, vshape);
// vk = J tk
vk[0] = J(0,0)*tk[k][0]+J(0,1)*tk[k][1]+J(0,2)*tk[k][2];
vk[1] = J(1,0)*tk[k][0]+J(1,1)*tk[k][1]+J(1,2)*tk[k][2];
vk[2] = J(2,0)*tk[k][0]+J(2,1)*tk[k][1]+J(2,2)*tk[k][2];
for (j = 0; j < dof; j++)
if (fabs (I(k,j) = (vshape(j,0)*vk[0]+vshape(j,1)*vk[1]+
vshape(j,2)*vk[2])) < 1.0e-12)
{
I(k,j) = 0.0;
}
}
}
void Nedelec2PyrFiniteElement::Project (
VectorCoefficient &vc, ElementTransformation &Trans,
Vector &dofs) const
{
real_t vk[3];
Vector xk (vk, 3);
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint (&Nodes.IntPoint (k));
const DenseMatrix &J = Trans.Jacobian();
vc.Eval (xk, Trans, Nodes.IntPoint (k));
// xk^t J tk
dofs(k) =
vk[0] * ( J(0,0)*tk[k][0]+J(0,1)*tk[k][1]+J(0,2)*tk[k][2] ) +
vk[1] * ( J(1,0)*tk[k][0]+J(1,1)*tk[k][1]+J(1,2)*tk[k][2] ) +
vk[2] * ( J(2,0)*tk[k][0]+J(2,1)*tk[k][1]+J(2,2)*tk[k][2] );
}
}
void Nedelec2PyrFiniteElement::ProjectGrad(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &grad) const
{
DenseMatrix dshape(fe.GetDof(), 3);
Vector grad_k(fe.GetDof());
grad.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
fe.CalcDShape(Nodes.IntPoint(k), dshape);
dshape.Mult(tk[k], grad_k);
for (int j = 0; j < grad_k.Size(); j++)
{
grad(k,j) = (fabs(grad_k(j)) < 1e-12) ? 0.0 : grad_k(j);
}
}
}
RT0HexFiniteElement::RT0HexFiniteElement()
: VectorFiniteElement(3, Geometry::CUBE, 6, 1, H_DIV, FunctionSpace::Qk)
{
@@ -5950,7 +6488,8 @@ void RT0WdgFiniteElement::ProjectCurl(const FiniteElement &fe,
}
RT0PyrFiniteElement::RT0PyrFiniteElement(bool rt0tets)
: VectorFiniteElement(3, Geometry::PYRAMID, 5, 1, H_DIV), rt0(rt0tets)
: VectorFiniteElement(3, Geometry::PYRAMID, 5, 1, H_DIV, FunctionSpace::Uk),
rt0(rt0tets)
{
// not real nodes ...
Nodes.IntPoint(0).x = 0.5;
@@ -6073,7 +6612,7 @@ void RT0PyrFiniteElement::CalcDivShape(const IntegrationPoint &ip,
}
const real_t RT0PyrFiniteElement::nk[5][3] =
{{0.,0.,-1.}, {0,-.5,0}, {.5,0,.5}, {0,.5,.5}, {-.5,0,0}};
{{0.,0.,-1}, {0,-1,0}, {1,0,1}, {0,1,1}, {-1,0,0}};
void RT0PyrFiniteElement::GetLocalInterpolation (
ElementTransformation &Trans, DenseMatrix &I) const
+28
View File
@@ -1018,6 +1018,34 @@ public:
};
/// A 3D 2nd order Nedelec element on a pyramid
class Nedelec2PyrFiniteElement : public VectorFiniteElement
{
private:
static const real_t tk[28][3];
public:
/// Construct the Nedelec2PyrFiniteElement
Nedelec2PyrFiniteElement();
virtual void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const;
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_ND(Trans, shape); }
virtual void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const;
virtual void GetLocalInterpolation (ElementTransformation &Trans,
DenseMatrix &I) const;
using FiniteElement::Project;
virtual void Project (VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const;
virtual void ProjectGrad(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &grad) const;
};
/// A 3D 0th order Raviert-Thomas element on a cube
class RT0HexFiniteElement : public VectorFiniteElement
{
+825
View File
@@ -1040,4 +1040,829 @@ void H1_WedgeElement::CalcDShape(const IntegrationPoint &ip,
}
}
H1_FuentesPyramidElement::H1_FuentesPyramidElement(const int p, const int btype)
: NodalFiniteElement(3, Geometry::PYRAMID,
p * (p * p + 3) + 1, // Fuentes et al
p, FunctionSpace::Uk)
{
zmax = 0.0;
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
#ifndef MFEM_THREAD_SAFE
tmp_i.SetSize(p + 1);
tmp1_ij.SetSize(p + 1, p + 1);
tmp2_ij.SetSize(p + 1, dim);
tmp_ijk.SetSize(p + 1, p + 1, dim);
tmp_u.SetSize(dof);
tmp_du.SetSize(dof, dim);
#else
Vector tmp_i(p + 1);
DenseMatrix tmp1_ij(p + 1, p + 1);
#endif
// vertices
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
Nodes.IntPoint(2).Set3(cp[p], cp[p], cp[0]);
Nodes.IntPoint(3).Set3(cp[0], cp[p], cp[0]);
Nodes.IntPoint(4).Set3(cp[0], cp[0], cp[p]);
// edges
int o = 5;
for (int i = 1; i < p; i++) // (0,1)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
}
for (int i = 1; i < p; i++) // (1,2)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (3,2)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[0]);
}
for (int i = 1; i < p; i++) // (0,3)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (0,4)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (1,4)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (2,4)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[p-i], cp[i]);
}
for (int i = 1; i < p; i++) // (3,4)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
}
// quadrilateral face
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p-j], cp[0]);
}
}
// triangular faces
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0,1,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (1,2,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3((cp[i] + cp[p-i-j])/w, cp[i]/w, cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (2,3,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[p-i-j]/w, (cp[i] + cp[p-i-j])/w, cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (3,0,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i-j]/w, cp[j]/w);
}
// Points based on Fuentes' interior bubbles
for (int k = 1; k < p; k++)
{
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i] * (1.0 - cp[k]),
cp[j] * (1.0 - cp[k]),
cp[k]);
}
}
}
MFEM_ASSERT(o == dof,
"Number of nodes does not match the "
"number of degrees of freedom");
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
Vector col(T.GetColumn(m), dof);
calcBasis(order, ip, tmp_i, tmp1_ij, col);
}
Ti.Factor(T);
}
void H1_FuentesPyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector tmp_i(p + 1);
Vector tmp_u(dof);
DenseMatrix tmp1_ij(p + 1, p + 1);
#endif
calcBasis(p, ip, tmp_i, tmp1_ij, tmp_u);
Ti.Mult(tmp_u, shape);
}
void H1_FuentesPyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector tmp_i(p + 1);
DenseMatrix tmp1_ij(p + 1, p + 1);
DenseMatrix tmp2_ij(p + 1, dim);
DenseTensor tmp_ijk(p + 1, p + 1, dim);
DenseMatrix tmp_du(dof, dim);
#endif
calcGradBasis(p, ip, tmp_i, tmp2_ij, tmp1_ij, tmp_ijk, tmp_du);
Ti.Mult(tmp_du, dshape);
}
void H1_FuentesPyramidElement::CalcRawShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector tmp_i(p + 1);
DenseMatrix tmp1_ij(p + 1, p + 1);
#endif
calcBasis(p, ip, tmp_i, tmp1_ij, shape);
}
void H1_FuentesPyramidElement::CalcRawDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector tmp_i(p + 1);
DenseMatrix tmp1_ij(p + 1, p + 1);
DenseMatrix tmp2_ij(p + 1, dim);
DenseTensor tmp_ijk(p + 1, p + 1, dim);
#endif
calcGradBasis(p, ip, tmp_i, tmp2_ij, tmp1_ij, tmp_ijk, dshape);
}
void H1_FuentesPyramidElement::calcBasis(const int p,
const IntegrationPoint &ip,
Vector &phi_i, DenseMatrix &phi_ij,
Vector &u) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
zmax = std::max(z, zmax);
real_t mu;
int o = 0;
// Vertices
u[0] = lam1(x, y, z);
u[1] = lam2(x, y, z);
u[2] = lam3(x, y, z);
u[3] = lam4(x, y, z);
u[4] = lam5(x, y, z);
o += 5;
// Mixed edges (base edges)
if (CheckZ(z) && p >= 2)
{
// (a,b) = (1,2), c = 0
phi_E(p, nu01(z, xy, 1), phi_i);
mu = mu0(z, xy, 2);
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_i[i];
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_i[i];
}
// (a,b) = (2,1), c = 0
phi_E(p, nu01(z, xy, 2), phi_i);
mu = mu0(z, xy, 1);
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_i[i];
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_i[i];
}
}
else
{
for (int i = 0; i < 4 * (p - 1); i++, o++)
{
u[o] = 0.0;
}
}
// Triangle edges (upright edges)
if (p >= 2)
{
phi_E(p, lam15(x, y, z), phi_i);
for (int i = 2; i<= p; i++, o++)
{
u[o] = phi_i[i];
}
phi_E(p, lam25(x, y, z), phi_i);
for (int i = 2; i<= p; i++, o++)
{
u[o] = phi_i[i];
}
phi_E(p, lam35(x, y, z), phi_i);
for (int i = 2; i<= p; i++, o++)
{
u[o] = phi_i[i];
}
phi_E(p, lam45(x, y, z), phi_i);
for (int i = 2; i<= p; i++, o++)
{
u[o] = phi_i[i];
}
}
// Quadrilateral face
if (CheckZ(z) && p >= 2)
{
phi_Q(p, mu01(z, xy, 1), mu01(z, xy, 2), phi_ij);
mu = mu0(z);
for (int j = 2; j <= p; j++)
{
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_ij(i,j);
}
}
}
else
{
for (int j = 2; j <= p; j++)
{
for (int i = 2; i <= p; i++, o++)
{
u[o] = 0.0;
}
}
}
// Triangular faces
if (CheckZ(z) && p >= 3)
{
// (a,b) = (1,2), c = 0
phi_T(p, nu012(z, xy, 1), phi_ij);
mu = mu0(z, xy, 2);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
{
u[o] = mu * phi_ij(i,j);
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
{
u[o] = mu * phi_ij(i,j);
}
// (a,b) = (2,1), c = 0
phi_T(p, nu012(z, xy, 2), phi_ij);
mu = mu0(z, xy, 1);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
{
u[o] = mu * phi_ij(i,j);
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
{
u[o] = mu * phi_ij(i,j);
}
}
else
{
for (int i = 0; i < 2 * (p - 1) * (p - 2); i++, o++)
{
u[o] = 0.0;
}
}
// Interior
if (CheckZ(z) && p >= 2)
{
phi_Q(p, mu01(z, xy, 1), mu01(z, xy, 2), phi_ij);
phi_E(p, mu01(z), phi_i);
for (int k = 2; k <= p; k++)
{
for (int j = 2; j <= p; j++)
{
for (int i = 2; i <= p; i++, o++)
{
u[o] = phi_ij(i,j) * phi_i(k);
}
}
}
}
else
{
for (int i = 0; i < (p - 1) * (p - 1) * (p - 1); i++, o++)
{
u[o]= 0.0;
}
}
}
void H1_FuentesPyramidElement::calcGradBasis(const int p,
const IntegrationPoint &ip,
Vector &phi_i,
DenseMatrix &dphi_i,
DenseMatrix &phi_ij,
DenseTensor &dphi_ij,
DenseMatrix &du) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
zmax = std::max(z, zmax);
real_t mu;
Vector dmu(3);
Vector dlam(3);
int o = 0;
// Vertices
dlam = grad_lam1(x, y, z);
for (int d=0; d<3; d++) { du(0, d) = dlam(d); }
dlam = grad_lam2(x, y, z);
for (int d=0; d<3; d++) { du(1, d) = dlam(d); }
dlam = grad_lam3(x, y, z);
for (int d=0; d<3; d++) { du(2, d) = dlam(d); }
dlam = grad_lam4(x, y, z);
for (int d=0; d<3; d++) { du(3, d) = dlam(d); }
dlam = grad_lam5(x, y, z);
for (int d=0; d<3; d++) { du(4, d) = dlam(d); }
o += 5;
// Mixed edges (base edges)
if (CheckZ(z) && p >= 2)
{
// (a,b) = (1,2), c = 0
phi_E(p, nu01(z, xy, 1), grad_nu01(z, xy, 1), phi_i, dphi_i);
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);;
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);;
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
}
// (a,b) = (2,1), c = 0
phi_E(p, nu01(z, xy, 2), grad_nu01(z, xy, 2), phi_i, dphi_i);
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);;
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);;
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
}
}
else
{
for (int i = 0; i < 4 * (p - 1); i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = 0.0;
}
}
// Triangle edges (upright edges)
if (p >= 2)
{
phi_E(p, lam15(x, y, z), grad_lam15(x,y,z), phi_i, dphi_i);
for (int i = 2; i<= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dphi_i(i, d);
}
phi_E(p, lam25(x, y, z), grad_lam25(x, y, z), phi_i, dphi_i);
for (int i = 2; i<= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dphi_i(i, d);
}
phi_E(p, lam35(x, y, z), grad_lam35(x, y, z), phi_i, dphi_i);
for (int i = 2; i<= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dphi_i(i, d);
}
phi_E(p, lam45(x, y, z), grad_lam45(x, y, z), phi_i, dphi_i);
for (int i = 2; i<= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dphi_i(i, d);
}
}
// Quadrilateral face
if (CheckZ(z) && p >= 2)
{
phi_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu01(z, xy, 2), grad_mu01(z, xy, 2), phi_ij, dphi_ij);
mu = mu0(z);
dmu = grad_mu0(z);
for (int j = 2; j <= p; j++)
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
}
else
{
for (int j = 2; j <= p; j++)
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = 0.0;
}
}
// Triangular faces
if (CheckZ(z) && p >= 3)
{
// (a,b) = (1,2), c = 0
phi_T(p, nu012(z, xy, 1), grad_nu012(z, xy, 1), phi_ij, dphi_ij);
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
// (a,b) = (2,1), c = 0
phi_T(p, nu012(z, xy, 2), grad_nu012(z, xy, 2), phi_ij, dphi_ij);
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
}
else
{
for (int i = 0; i < 2 * (p - 1) * (p - 2); i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = 0.0;
}
}
// Interior
if (CheckZ(z) && p >= 2)
{
phi_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu01(z, xy, 2), grad_mu01(z, xy, 2), phi_ij, dphi_ij);
phi_E(p, mu01(z), grad_mu01(z), phi_i, dphi_i);
for (int k = 2; k <= p; k++)
for (int j = 2; j <= p; j++)
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
du(o, d) = dphi_ij(i, j, d) * phi_i(k) +
phi_ij(i, j) * dphi_i(k, d);
}
else
{
for (int i = 0; i < (p - 1) * (p - 1) * (p - 1); i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = 0.0;
}
}
}
H1_BergotPyramidElement::H1_BergotPyramidElement(const int p, const int btype)
: NodalFiniteElement(3, Geometry::PYRAMID,
(p + 1) * (p + 2) * (2 * p + 3) / 6, // Bergot (JSC)
p, FunctionSpace::Uk)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(p + 1);
shape_y.SetSize(p + 1);
shape_z.SetSize(p + 1);
dshape_x.SetSize(p + 1);
dshape_y.SetSize(p + 1);
dshape_z.SetSize(p + 1);
dshape_z_dt.SetSize(p + 1);
ddshape_x.SetSize(p + 1);
ddshape_y.SetSize(p + 1);
ddshape_z.SetSize(p + 1);
u.SetSize(dof);
du.SetSize(dof, dim);
ddu.SetSize(dof, (dim * (dim + 1)) / 2);
#else
Vector shape_x(p + 1), shape_y(p + 1), shape_z(p + 1);
#endif
// vertices
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
Nodes.IntPoint(2).Set3(cp[p], cp[p], cp[0]);
Nodes.IntPoint(3).Set3(cp[0], cp[p], cp[0]);
Nodes.IntPoint(4).Set3(cp[0], cp[0], cp[p]);
// edges
int o = 5;
for (int i = 1; i < p; i++) // (0,1)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
}
for (int i = 1; i < p; i++) // (1,2)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (3,2)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[0]);
}
for (int i = 1; i < p; i++) // (0,3)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (0,4)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (1,4)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (2,4)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[p-i], cp[i]);
}
for (int i = 1; i < p; i++) // (3,4)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
}
// quadrilateral face
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[j], cp[0]);
}
}
// triangular faces
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0,1,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (1,2,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(1.0 - cp[j]/w, cp[i]/w, cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (3,4,2)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[j]/w, 1.0 - cp[i]/w, cp[i]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0,4,3)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[0], cp[j]/w, cp[i]/w);
}
// interior
for (int k = 1; k < p - 1; k++)
{
for (int j = 1; j < p - k; j++)
{
real_t wjk = cp[j] + cp[k] + cp[p-j-k];
for (int i = 1; i < p - k; i++)
{
real_t wik = cp[i] + cp[k] + cp[p-i-k];
real_t w = wik * wjk * cp[p-k];
Nodes.IntPoint(o++).Set3(cp[i] * (cp[j] + cp[p-j-k]) / w,
cp[j] * (cp[i] + cp[p-i-k]) / w,
cp[k] * cp[p-k] / w);
}
}
}
MFEM_ASSERT(o == dof,
"Number of nodes does not match the "
"number of degrees of freedom");
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
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;
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);
}
}
}
}
Ti.Factor(T);
}
void H1_BergotPyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(order+1);
Vector shape_y(order+1);
Vector shape_z(order+1);
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;
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);
}
void H1_BergotPyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix du(dof, dim);
Vector shape_x(order+1);
Vector shape_y(order+1);
Vector shape_z(order+1);
Vector dshape_x(order+1);
Vector dshape_y(order+1);
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;
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) *
pow(1.0 - ip.z, maxij - 1);
}
}
Ti.Mult(du, dshape);
}
}
+68
View File
@@ -13,6 +13,7 @@
#define MFEM_FE_H1
#include "fe_base.hpp"
#include "fe_pyramid.hpp"
namespace mfem
{
@@ -148,6 +149,73 @@ public:
DenseMatrix &dshape) const override;
};
/** Arbitrary order H1 basis functions defined on pyramid-shaped elements
This implementation is closely based on the finite elements
described in section 9.1 of the paper "Orientation embedded high
order shape functions for the exact sequence elements of all shapes"
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
*/
class H1_FuentesPyramidElement
: public NodalFiniteElement, public FuentesPyramid
{
private:
mutable real_t zmax;
#ifndef MFEM_THREAD_SAFE
mutable Vector tmp_i, tmp_u;
mutable DenseMatrix tmp1_ij, tmp2_ij, tmp_du;
mutable DenseTensor tmp_ijk;
#endif
DenseMatrixInverse Ti;
void calcBasis(const int p, const IntegrationPoint &ip,
Vector &phi_i, DenseMatrix &phi_ij, Vector &u) const;
void calcGradBasis(const int p, const IntegrationPoint &ip,
Vector &phi_i, DenseMatrix &dphi_i,
DenseMatrix &phi_ij, DenseTensor &dphi_ij,
DenseMatrix &du) const;
public:
H1_FuentesPyramidElement(const int p,
const int btype = BasisType::GaussLobatto);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
void CalcRawShape(const IntegrationPoint &ip, Vector &shape) const;
void CalcRawDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
real_t GetZetaMax() const { return zmax; }
};
/** Arbitrary order H1 basis functions defined on pyramid-shaped elements
This implementation is based on the finite elements described in the
2010 paper "Higher-Order Finite Elements for Hybrid Meshes Using New
Nodal Pyramidal Elements" by Morgane Bergot, Gary Cohen, and Marc
Durufle, see https://hal.archives-ouvertes.fr/hal-00454261.
*/
class H1_BergotPyramidElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z;
mutable Vector dshape_x, dshape_y, dshape_z, dshape_z_dt, u;
mutable Vector ddshape_x, ddshape_y, ddshape_z;
mutable DenseMatrix du, ddu;
#endif
DenseMatrixInverse Ti;
public:
H1_BergotPyramidElement(const int p,
const int btype = BasisType::GaussLobatto);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
};
} // namespace mfem
#endif
+317
View File
@@ -923,4 +923,321 @@ void L2_WedgeElement::CalcDShape(const IntegrationPoint &ip,
}
}
L2_FuentesPyramidElement::L2_FuentesPyramidElement(const int p, const int btype)
: NodalFiniteElement(3, Geometry::PYRAMID, ((p + 1)*(p + 1)*(p + 1)),
p, FunctionSpace::Uk)
{
const real_t *op = poly1d.OpenPoints(p, VerifyOpen(btype));
// These basis functions are not independent on a closed set of
// interpolation points when p >= 1. For this reason we force the points
// to be open in the z direction whenever closed points are requested.
// This should be regarded as a limitation of this choice of basis function.
// If a truly closed set of points is needed consider using
// L2_BergotPyramidElement instead.
real_t a = 1.0;
if (IsClosedType(btype) && p > 0)
{
a = (poly1d.GetPoints(p, BasisType::GaussLegendre))[p];
}
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(p + 1);
shape_y.SetSize(p + 1);
shape_z.SetSize(p + 1);
dshape_x.SetSize(p + 1);
dshape_y.SetSize(p + 1);
dshape_z.SetSize(p + 1);
u.SetSize(dof);
du.SetSize(dof, dim);
#else
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
#endif
int o = 0;
for (int k = 0; k <= p; k++)
for (int j = 0; j <= p; j++)
for (int i = 0; i <= p; i++)
{
Nodes.IntPoint(o++).Set3(op[i] * (1.0 - a * op[k]),
op[j] * (1.0 - a * op[k]),
a * op[k]);
}
MFEM_ASSERT(o == dof,
"Number of nodes does not match the "
"number of degrees of freedom");
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
CalcHomogenizedScaLegendre(p, mu0(z, xy, 1), mu1(z, xy, 1), shape_x);
CalcHomogenizedScaLegendre(p, mu0(z, xy, 2), mu1(z, xy, 2), shape_y);
CalcHomogenizedScaLegendre(p, mu0(z), mu1(z), shape_z);
o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++, o++)
{
T(o, m) = shape_x[i] * shape_y[j] * shape_z[k];
}
}
}
}
Ti.Factor(T);
}
void L2_FuentesPyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector u(dof);
#endif
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
if (z < 1.0)
{
CalcHomogenizedScaLegendre(p, mu0(z, xy, 1), mu1(z, xy, 1), shape_x);
CalcHomogenizedScaLegendre(p, mu0(z, xy, 2), mu1(z, xy, 2), shape_y);
}
else
{
shape_x = 0.0; shape_x(0) = 1.0;
shape_y = 0.0; shape_y(0) = 1.0;
}
CalcHomogenizedScaLegendre(p, mu0(z), mu1(z), shape_z);
int o = 0;
for (int k = 0; k <= p; k++)
for (int j = 0; j <= p; j++)
for (int i = 0; i <= p; i++, o++)
{
u[o] = shape_x[i] * shape_y[j] * shape_z[k];
}
Ti.Mult(u, shape);
}
void L2_FuentesPyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector dshape_x(p + 1);
Vector dshape_y(p + 1);
Vector dshape_z(p + 1);
DenseMatrix du(dof, dim);
#endif
Poly_1D::CalcLegendre(p, ip.x / (1.0 - ip.z), shape_x.GetData(),
dshape_x.GetData());
Poly_1D::CalcLegendre(p, ip.y / (1.0 - ip.z), shape_y.GetData(),
dshape_y.GetData());
Poly_1D::CalcLegendre(p, ip.z, shape_z.GetData(), dshape_z.GetData());
int o = 0;
for (int k = 0; k <= p; k++)
for (int j = 0; j <= p; j++)
for (int i = 0; i <= p; i++, o++)
{
du(o, 0) = dshape_x[i] * shape_y[j] * shape_z[k] / (1.0 - ip.z);
du(o, 1) = shape_x[i] * dshape_y[j] * shape_z[k] / (1.0 - ip.z);
du(o, 2) = shape_x[i] * shape_y[j] * dshape_z[k] +
(ip.x * dshape_x[i] * shape_y[j] +
ip.y * shape_x[i] * dshape_y[j]) *
shape_z[k] / pow(1.0 - ip.z, 2);
}
Ti.Mult(du, dshape);
}
L2_BergotPyramidElement::L2_BergotPyramidElement(const int p, const int btype)
: NodalFiniteElement(3, Geometry::PYRAMID, (p + 1)*(p + 2)*(2*p + 3)/6,
p, FunctionSpace::Pk)
{
const real_t *op = poly1d.OpenPoints(p, VerifyOpen(btype));
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(p + 1);
shape_y.SetSize(p + 1);
shape_z.SetSize(p + 1);
dshape_x.SetSize(p + 1);
dshape_y.SetSize(p + 1);
dshape_z.SetSize(p + 1);
dshape_z_dt.SetSize(p + 1);
u.SetSize(dof);
du.SetSize(dof, dim);
#else
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector dshape_z_dt(p + 1);
#endif
int o = 0;
for (int k = 0; k <= p; k++)
for (int j = 0; j <= p - k; j++)
{
const real_t wjk = op[j] + op[k] + op[p-j-k];
for (int i = 0; i <= p - k; i++)
{
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);
}
}
MFEM_ASSERT(o == dof,
"Number of nodes does not match the "
"number of degrees of freedom");
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
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());
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);
}
}
}
}
Ti.Factor(T);
}
void L2_BergotPyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector u(dof);
#endif
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);
}
}
}
Ti.Mult(u, shape);
}
void L2_BergotPyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector dshape_x(p + 1);
Vector dshape_y(p + 1);
Vector dshape_z(p + 1);
Vector dshape_z_dt(p + 1);
DenseMatrix du(dof, dim);
#endif
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;
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 > 0) ? (maxij * shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1)) : 0.0);
}
}
}
Ti.Mult(du, dshape);
}
}
+51
View File
@@ -13,6 +13,7 @@
#define MFEM_FE_L2
#include "fe_base.hpp"
#include "fe_pyramid.hpp"
namespace mfem
{
@@ -183,6 +184,56 @@ public:
DenseMatrix &dshape) const override;
};
/** Arbitrary order L2 basis functions defined on pyramid-shaped elements
This implementation is closely based on the finite elements
described in section 9.4 of the paper "Orientation embedded high
order shape functions for the exact sequence elements of all shapes"
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
*/
class L2_FuentesPyramidElement
: public NodalFiniteElement, public FuentesPyramid
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z;
mutable Vector dshape_x, dshape_y, dshape_z;
mutable Vector u;
mutable DenseMatrix du;
#endif
DenseMatrixInverse Ti;
public:
/// Construct the L2_PyramidElement of order @a p and BasisType @a btype
L2_FuentesPyramidElement(const int p,
const int btype = BasisType::GaussLegendre);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
};
/// Arbitrary order L2 elements in 3D on a pyramid
class L2_BergotPyramidElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z;
mutable Vector dshape_x, dshape_y, dshape_z, dshape_z_dt;
mutable Vector u;
mutable DenseMatrix du;
#endif
DenseMatrixInverse Ti;
public:
/// Construct the L2_PyramidElement of order @a p and BasisType @a btype
L2_BergotPyramidElement(const int p,
const int btype = BasisType::GaussLegendre);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
};
} // namespace mfem
#endif
+948
View File
@@ -1581,6 +1581,954 @@ void ND_WedgeElement::CalcCurlShape(const IntegrationPoint &ip,
}
}
const real_t ND_FuentesPyramidElement::tk[27] =
{
1., 0., 0., 0., 1., 0., 0., 0., 1.,
-1., 0., 1., -1.,-1., 1., 0.,-1., 1.,
-1., 0., 0., 0.,-1., 0., -M_SQRT1_2,-M_SQRT1_2,M_SQRT2
};
ND_FuentesPyramidElement::ND_FuentesPyramidElement(const int p,
const int cb_type,
const int ob_type)
: VectorFiniteElement(3, Geometry::PYRAMID, p * (3 * p * p + 5), p,
H_CURL, FunctionSpace::Uk),
dof2tk(dof), doftrans(p)
{
zmax = 0.0;
const real_t *eop = poly1d.OpenPoints(p - 1);
const real_t *top = (p > 1) ? poly1d.OpenPoints(p - 2) : NULL;
const real_t *qop = poly1d.OpenPoints(p - 1, ob_type);
const real_t *qcp = poly1d.ClosedPoints(p, cb_type);
const int pm2 = p - 2;
#ifndef MFEM_THREAD_SAFE
tmp_E_E_ij.SetSize(p, dim);
tmp_dE_E_ij.SetSize(p, dim);
tmp_E_Q1_ijk.SetSize(p, p + 1, dim);
tmp_dE_Q1_ijk.SetSize(p, p + 1, dim);
tmp_E_Q2_ijk.SetSize(p, p + 1, dim);
tmp_dE_Q2_ijk.SetSize(p, p + 1, dim);
tmp_E_T_ijk.SetSize(p - 1, p, dim);
tmp_dE_T_ijk.SetSize(p - 1, p, dim);
tmp_phi_Q1_ij.SetSize(p + 1, p + 1);
tmp_dphi_Q1_ij.SetSize(p + 1, p + 1, dim);
tmp_phi_Q2_ij.SetSize(p + 1, p + 1);
tmp_dphi_Q2_ij.SetSize(p + 1, p + 1, dim);
tmp_phi_E_i.SetSize(p + 1);
tmp_dphi_E_i.SetSize(p + 1, dim);
u.SetSize(dof, dim);
curlu.SetSize(dof, dim);
#else
DenseMatrix tmp_E_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseTensor tmp_dE_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q1_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q1_ij(p + 1, p + 1, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
DenseMatrix u(dof, dim);
#endif
int o = 0;
// edges
for (int i = 0; i < p; i++) // (0, 1)
{
Nodes.IntPoint(o).Set3(eop[i], 0., 0.);
dof2tk[o++] = 0;
}
for (int i = 0; i < p; i++) // (1, 2)
{
Nodes.IntPoint(o).Set3(1., eop[i], 0.);
dof2tk[o++] = 1;
}
for (int i = 0; i < p; i++) // (3, 2)
{
Nodes.IntPoint(o).Set3(eop[i], 1., 0.);
dof2tk[o++] = 0;
}
for (int i = 0; i < p; i++) // (0, 3)
{
Nodes.IntPoint(o).Set3(0., eop[i], 0.);
dof2tk[o++] = 1;
}
for (int i = 0; i < p; i++) // (0, 4)
{
Nodes.IntPoint(o).Set3(0., 0., eop[i]);
dof2tk[o++] = 2;
}
for (int i = 0; i < p; i++) // (1, 4)
{
Nodes.IntPoint(o).Set3(1. - eop[i], 0., eop[i]);
dof2tk[o++] = 3;
}
for (int i = 0; i < p; i++) // (2, 4)
{
Nodes.IntPoint(o).Set3(1. - eop[i], 1. - eop[i], eop[i]);
dof2tk[o++] = 4;
}
for (int i = 0; i < p; i++) // (3, 4)
{
Nodes.IntPoint(o).Set3(0., 1. - eop[i], eop[i]);
dof2tk[o++] = 5;
}
// quadrilateral face (3, 2, 1, 0)
// x-components
for (int j = 1; j < p; j++)
for (int i = 0; i < p; i++)
{
Nodes.IntPoint(o).Set3(qop[i], qcp[p-j], 0.);
dof2tk[o++] = 0; // (1 0 0)
}
// y-components
for (int j = 0; j < p; j++)
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o).Set3(qcp[i], qop[p-1-j], 0.);
dof2tk[o++] = 7; // (0 -1 0)
}
// triangular faces
for (int j = 0; j <= pm2; j++) // (0, 1, 4)
for (int i = 0; i + j <= pm2; i++)
{
real_t w = top[i] + top[j] + top[pm2-i-j];
Nodes.IntPoint(o).Set3(top[i]/w, 0., top[j]/w);
dof2tk[o++] = 0;
Nodes.IntPoint(o).Set3(top[i]/w, 0., top[j]/w);
dof2tk[o++] = 2;
}
for (int j = 0; j <= pm2; j++) // (1, 2, 4)
for (int i = 0; i + j <= pm2; i++)
{
real_t w = top[i] + top[j] + top[pm2-i-j];
Nodes.IntPoint(o).Set3((top[i] + top[pm2-i-j])/w, top[i]/w, top[j]/w);
dof2tk[o++] = 1;
Nodes.IntPoint(o).Set3((top[i] + top[pm2-i-j])/w, top[i]/w, top[j]/w);
dof2tk[o++] = 3;
}
for (int j = 0; j <= pm2; j++) // (2, 3, 4)
for (int i = 0; i + j <= pm2; i++)
{
real_t w = top[i] + top[j] + top[pm2-i-j];
Nodes.IntPoint(o).Set3(top[pm2-i-j]/w, (top[i] + top[pm2-i-j])/w,
top[j]/w);
dof2tk[o++] = 6;
Nodes.IntPoint(o).Set3(top[pm2-i-j]/w, (top[i] + top[pm2-i-j])/w,
top[j]/w);
dof2tk[o++] = 4;
}
for (int j = 0; j <= pm2; j++) // (3, 0, 4)
for (int i = 0; i + j <= pm2; i++)
{
real_t w = top[i] + top[j] + top[pm2-i-j];
Nodes.IntPoint(o).Set3(0., top[pm2-i-j]/w, top[j]/w);
dof2tk[o++] = 7;
Nodes.IntPoint(o).Set3(0., top[pm2-i-j]/w, top[j]/w);
dof2tk[o++] = 5;
}
// interior
// x-components
for (int k = 1; k < p; k++)
for (int j = 1; j < p; j++)
for (int i = 0; i < p; i++)
{
real_t w = 1.0 - qcp[k];
Nodes.IntPoint(o).Set3(qop[i]*w, qcp[j]*w, qcp[k]);
dof2tk[o++] = 0;
}
// y-components
for (int k = 1; k < p; k++)
for (int j = 0; j < p; j++)
for (int i = 1; i < p; i++)
{
real_t w = 1.0 - qcp[k];
Nodes.IntPoint(o).Set3(qcp[i]*w, qop[j]*w, qcp[k]);
dof2tk[o++] = 1;
}
// z-components
for (int k = 0; k < p; k++)
for (int j = 1; j < p; j++)
for (int i = 1; i < p; i++)
{
real_t w = 1.0 - qop[k];
Nodes.IntPoint(o).Set3(qcp[i]*w, qcp[j]*w, qop[k]);
dof2tk[o++] = 8;
}
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
calcBasis(p, ip, tmp_E_E_ij, tmp_E_Q1_ijk, tmp_E_Q2_ijk, tmp_E_T_ijk,
tmp_phi_Q1_ij, tmp_dphi_Q1_ij, tmp_phi_Q2_ij,
tmp_phi_E_i, tmp_dphi_E_i, u);
const Vector tm({tk[3*dof2tk[m]], tk[3*dof2tk[m]+1], tk[3*dof2tk[m]+2]});
u.Mult(tm, T.GetColumn(m));
}
Ti.Factor(T);
}
void ND_FuentesPyramidElement::CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix tmp_E_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q1_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q1_ij(p + 1, p + 1, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
DenseMatrix u(dof, dim);
#endif
calcBasis(p, ip, tmp_E_E_ij, tmp_E_Q1_ijk, tmp_E_Q2_ijk, tmp_E_T_ijk,
tmp_phi_Q1_ij, tmp_dphi_Q1_ij, tmp_phi_Q2_ij,
tmp_phi_E_i, tmp_dphi_E_i, u);
Ti.Mult(u, shape);
}
void ND_FuentesPyramidElement::CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix tmp_E_E_ij(p, dim);
DenseMatrix tmp_dE_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseTensor tmp_dE_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q2_ij(p + 1, p + 1, dim);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
DenseMatrix curlu(dof, dim);
#endif
calcCurlBasis(p, ip, tmp_E_E_ij, tmp_dE_E_ij, tmp_E_Q1_ijk, tmp_dE_Q1_ijk,
tmp_E_Q2_ijk, tmp_dE_Q2_ijk, tmp_E_T_ijk, tmp_dE_T_ijk,
tmp_phi_Q2_ij, tmp_dphi_Q2_ij, tmp_phi_E_i, tmp_dphi_E_i,
curlu);
Ti.Mult(curlu, curl_shape);
}
void ND_FuentesPyramidElement::CalcRawVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix tmp_E_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q1_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q1_ij(p + 1, p + 1, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
#endif
calcBasis(p, ip, tmp_E_E_ij, tmp_E_Q1_ijk, tmp_E_Q2_ijk, tmp_E_T_ijk,
tmp_phi_Q1_ij, tmp_dphi_Q1_ij, tmp_phi_Q2_ij,
tmp_phi_E_i, tmp_dphi_E_i, shape);
}
void ND_FuentesPyramidElement::CalcRawCurlShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix tmp_E_E_ij(p, dim);
DenseMatrix tmp_dE_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseTensor tmp_dE_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q2_ij(p + 1, p + 1, dim);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
#endif
calcCurlBasis(p, ip, tmp_E_E_ij, tmp_dE_E_ij, tmp_E_Q1_ijk, tmp_dE_Q1_ijk,
tmp_E_Q2_ijk, tmp_dE_Q2_ijk, tmp_E_T_ijk, tmp_dE_T_ijk,
tmp_phi_Q2_ij, tmp_dphi_Q2_ij, tmp_phi_E_i, tmp_dphi_E_i,
dshape);
}
void ND_FuentesPyramidElement::calcBasis(const int p,
const IntegrationPoint &ip,
DenseMatrix & E_E_ik,
DenseTensor & E_Q1_ijk,
DenseTensor & E_Q2_ijk,
DenseTensor & E_T_ijk,
DenseMatrix & phi_Q1_ij,
DenseTensor & dphi_Q1_ij,
DenseMatrix & phi_Q2_ij,
Vector & phi_E_k,
DenseMatrix & dphi_E_k,
DenseMatrix &W) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y}), dmu(3);
real_t mu, mu2;
if (std::fabs(1.0 - z) < apex_tol)
{
z = 1.0 - apex_tol;
y = 0.5 * (1.0 - z);
x = 0.5 * (1.0 - z);
xy(0) = x; xy(1) = y;
}
zmax = std::max(z, zmax);
W = 0.0;
int o = 0;
// Mixed Edges
if (z < 1.0)
{
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
E_E(p, nu01(z, xy, 1), nu01_grad_nu01(z, xy, 1), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_E_ik(i, k);
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_E_ik(i, k);
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
E_E(p, nu01(z, xy, 2), nu01_grad_nu01(z, xy, 2), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_E_ik(i, k);
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_E_ik(i, k);
}
}
// Triangle Edges
if (z < 1.0)
{
E_E(p, lam15(x, y, z), lam15_grad_lam15(x, y, z), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = E_E_ik(i, k);
}
E_E(p, lam25(x, y, z), lam25_grad_lam25(x, y, z), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = E_E_ik(i, k);
}
E_E(p, lam35(x, y, z), lam35_grad_lam35(x, y, z), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = E_E_ik(i, k);
}
E_E(p, lam45(x, y, z), lam45_grad_lam45(x, y, z), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = E_E_ik(i, k);
}
}
// Quadrilateral Face
if (z < 1.0 && p >= 2)
{
mu = mu0(z);
mu2 = mu * mu;
// Family I
E_Q(p, mu01(z, xy, 1), mu01_grad_mu01(z, xy, 1), mu01(z, xy, 2),
E_Q1_ijk);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu2 * E_Q1_ijk(i, j, k);
}
// Family II
E_Q(p, mu01(z, xy, 2), mu01_grad_mu01(z, xy, 2), mu01(z, xy, 1),
E_Q2_ijk);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu2 * E_Q2_ijk(i, j, k);
}
}
// Triangular Faces
if (z < 1.0 && p >= 2)
{
// Family I
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
E_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1), E_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
E_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2), E_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// Family II
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
E_T(p, nu120(z, xy, 1), nu12_grad_nu12(z, xy, 1), E_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
E_T(p, nu120(z, xy, 2), nu12_grad_nu12(z, xy, 2), E_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
}
// Interior
if (z < 1.0 && p >= 2)
{
// Family I
phi_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1), mu01(z, xy, 2),
grad_mu01(z, xy, 2), phi_Q1_ij, dphi_Q1_ij);
phi_E(p, mu01(z), grad_mu01(z), phi_E_k, dphi_E_k);
for (int k=2; k<=p; k++)
for (int j=2; j<=p; j++)
for (int i=2; i<=p; i++, o++)
for (int l=0; l<3; l++)
W(o, l) = dphi_Q1_ij(i, j, l) * phi_E_k(k) +
phi_Q1_ij(i, j) * dphi_E_k(k, l);
// Family II
mu = mu0(z);
for (int k=2; k<=p; k++)
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
for (int l=0; l<3; l++)
{
W(o, l) = mu * E_Q1_ijk(i, j, l) * phi_E_k(k);
}
// Family III
for (int k=2; k<=p; k++)
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
for (int l=0; l<3; l++)
{
W(o, l) = mu * E_Q2_ijk(i, j, l) * phi_E_k(k);
}
// Family IV
// Re-using mu from Family II
dmu = grad_mu0(z);
phi_Q(p, mu01(z, xy, 2), mu01(z, xy, 1), phi_Q2_ij);
for (int j=2; j<=p; j++)
for (int i=2; i<=p; i++, o++)
{
const int n = std::max(i,j);
const real_t nmu = n * pow(mu, n-1);
for (int l=0; l<3; l++)
{
W(o, l) = nmu * phi_Q2_ij(i, j) * dmu(l);
}
}
}
}
void ND_FuentesPyramidElement::calcCurlBasis(const int p,
const IntegrationPoint &ip,
DenseMatrix & E_E_ik,
DenseMatrix & dE_E_ik,
DenseTensor & E_Q1_ijk,
DenseTensor & dE_Q1_ijk,
DenseTensor & E_Q2_ijk,
DenseTensor & dE_Q2_ijk,
DenseTensor & E_T_ijk,
DenseTensor & dE_T_ijk,
DenseMatrix & phi_Q2_ij,
DenseTensor & dphi_Q2_ij,
Vector & phi_E_k,
DenseMatrix & dphi_E_k,
DenseMatrix & dW) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y}), dmu(3);
Vector dmuxE(3), E(3), dphi(3), muphi(3);
real_t mu, mu2;
if (std::fabs(1.0 - z) < apex_tol)
{
z = 1.0 - apex_tol;
y = 0.5 * (1.0 - z);
x = 0.5 * (1.0 - z);
xy(0) = x; xy(1) = y;
}
zmax = std::max(z, zmax);
dW = 0.0;
int o = 0;
// Mixed Edges
if (z < 1.0)
{
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);
E_E(p, nu01(z, xy, 1), grad_nu01(z, xy, 1), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
{
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
}
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);
for (int i=0; i<p; i++, o++)
{
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);
E_E(p, nu01(z, xy, 2), grad_nu01(z, xy, 2), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
{
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);
for (int i=0; i<p; i++, o++)
{
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
}
}
}
// Triangle Edges
if (z < 1.0)
{
E_E(p, lam15(x, y, z), grad_lam15(x, y, z), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
dW(o, k) = dE_E_ik(i, k);
}
E_E(p, lam25(x, y, z), grad_lam25(x, y, z), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
dW(o, k) = dE_E_ik(i, k);
}
E_E(p, lam35(x, y, z), grad_lam35(x, y, z), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
dW(o, k) = dE_E_ik(i, k);
}
E_E(p, lam45(x, y, z), grad_lam45(x, y, z), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
dW(o, k) = dE_E_ik(i, k);
}
}
// Quadrilateral Face
if (z < 1.0 && p >= 2)
{
mu = mu0(z);
mu2 = mu * mu;
dmu = grad_mu0(z);
// Family I
E_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu01(z, xy, 2), grad_mu01(z, xy, 2), E_Q1_ijk, dE_Q1_ijk);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_Q1_ijk(i, j, 0);
E(1) = E_Q1_ijk(i, j, 1);
E(2) = E_Q1_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu2 * dE_Q1_ijk(i, j, k) + 2.0 * mu * dmuxE(k);
}
}
// Family II
E_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
mu01(z, xy, 1), grad_mu01(z, xy, 1), E_Q2_ijk, dE_Q2_ijk);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_Q2_ijk(i, j, 0);
E(1) = E_Q2_ijk(i, j, 1);
E(2) = E_Q2_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu2 * dE_Q2_ijk(i, j, k) + 2.0 * mu * dmuxE(k);
}
}
}
// Triangular Faces
if (z < 1.0 && p >= 2)
{
// Family I
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);
E_T(p, nu012(z, xy, 1), grad_nu012(z, xy, 1), E_T_ijk, dE_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);
E_T(p, nu012(z, xy, 2), grad_nu012(z, xy, 2), E_T_ijk, dE_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// Family II
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);
E_T(p, nu120(z, xy, 1), grad_nu120(z, xy, 1), E_T_ijk, dE_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);
E_T(p, nu120(z, xy, 2), grad_nu120(z, xy, 2), E_T_ijk, dE_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
}
// Interior
if (z < 1.0 && p >= 2)
{
// Family I
// Curl is zero so skip these functions
o += (p - 1) * (p - 1) * (p - 1);
// Family II
mu = mu0(z);
dmu = grad_mu0(z);
phi_E(p, mu01(z), grad_mu01(z), phi_E_k, dphi_E_k);
for (int k=2; k<=p; k++)
{
dphi(0) = dphi_E_k(k, 0);
dphi(1) = dphi_E_k(k, 1);
dphi(2) = dphi_E_k(k, 2);
add(mu, dphi, phi_E_k(k), dmu, muphi);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_Q1_ijk(i, j, 0);
E(1) = E_Q1_ijk(i, j, 1);
E(2) = E_Q1_ijk(i, j, 2);
muphi.cross3D(E, dmuxE);
for (int l=0; l<3; l++)
{
dW(o, l) = mu * dE_Q1_ijk(i, j, l) * phi_E_k(k) + dmuxE(l);
}
}
}
// Family III
for (int k=2; k<=p; k++)
{
dphi(0) = dphi_E_k(k, 0);
dphi(1) = dphi_E_k(k, 1);
dphi(2) = dphi_E_k(k, 2);
add(mu, dphi, phi_E_k(k), dmu, muphi);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_Q2_ijk(i, j, 0);
E(1) = E_Q2_ijk(i, j, 1);
E(2) = E_Q2_ijk(i, j, 2);
muphi.cross3D(E, dmuxE);
for (int l=0; l<3; l++)
{
dW(o, l) = mu * dE_Q2_ijk(i, j, l) * phi_E_k(k) + dmuxE(l);
}
}
}
// Family IV
// Re-using mu from Family II
dmu = grad_mu0(z);
phi_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2), mu01(z, xy, 1),
grad_mu01(z, xy, 1), phi_Q2_ij, dphi_Q2_ij);
for (int j=2; j<=p; j++)
for (int i=2; i<=p; i++, o++)
{
const int n = std::max(i,j);
const real_t nmu = n * pow(mu, n-1);
dphi(0) = dphi_Q2_ij(i, j, 0);
dphi(1) = dphi_Q2_ij(i, j, 1);
dphi(2) = dphi_Q2_ij(i, j, 2);
dphi.cross3D(dmu, muphi);
for (int l=0; l<3; l++)
{
dW(o, l) = nmu * muphi(l);
}
}
}
}
ND_R1D_PointElement::ND_R1D_PointElement(int p)
: VectorFiniteElement(1, Geometry::POINT, 2, p,
H_CURL, FunctionSpace::Pk)
+121
View File
@@ -14,6 +14,7 @@
#include "fe_base.hpp"
#include "fe_h1.hpp"
#include "fe_pyramid.hpp"
namespace mfem
{
@@ -415,6 +416,126 @@ public:
};
/** Arbitrary order H(Curl) basis functions defined on pyramid-shaped elements
This implementation is closely based on the finite elements
described in section 9.2 of the paper "Orientation embedded high
order shape functions for the exact sequence elements of all shapes"
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
*/
class ND_FuentesPyramidElement
: public VectorFiniteElement, public FuentesPyramid
{
private:
static const real_t tk[27];
mutable real_t zmax;
#ifndef MFEM_THREAD_SAFE
mutable DenseMatrix tmp_E_E_ij;
mutable DenseMatrix tmp_dE_E_ij;
mutable DenseTensor tmp_E_Q1_ijk;
mutable DenseTensor tmp_dE_Q1_ijk;
mutable DenseTensor tmp_E_Q2_ijk;
mutable DenseTensor tmp_dE_Q2_ijk;
mutable DenseTensor tmp_E_T_ijk;
mutable DenseTensor tmp_dE_T_ijk;
mutable DenseMatrix tmp_phi_Q1_ij;
mutable DenseTensor tmp_dphi_Q1_ij;
mutable DenseMatrix tmp_phi_Q2_ij;
mutable DenseTensor tmp_dphi_Q2_ij;
mutable Vector tmp_phi_E_i;
mutable DenseMatrix tmp_dphi_E_i;
mutable DenseMatrix u;
mutable DenseMatrix curlu;
#endif
Array<int> dof2tk;
DenseMatrixInverse Ti;
ND_PyramidDofTransformation doftrans;
void calcBasis(const int p, const IntegrationPoint & ip,
DenseMatrix & E_E_ik,
DenseTensor & E_Q1_ijk,
DenseTensor & E_Q2_ijk,
DenseTensor & E_T_ijk,
DenseMatrix & phi_Q1_ij,
DenseTensor & dphi_Q1_ij,
DenseMatrix & phi_Q2_ij,
Vector & phi_E_k,
DenseMatrix & dphi_E_k,
DenseMatrix & W) const;
void calcCurlBasis(const int p, const IntegrationPoint & ip,
DenseMatrix & E_E_ik,
DenseMatrix & dE_E_ik,
DenseTensor & E_Q1_ijk,
DenseTensor & dE_Q1_ijk,
DenseTensor & E_Q2_ijk,
DenseTensor & dE_Q2_ijk,
DenseTensor & E_T_ijk,
DenseTensor & dE_T_ijk,
DenseMatrix & phi_Q2_ij,
DenseTensor & dphi_Q2_ij,
Vector & phi_E_k,
DenseMatrix & dphi_E_k,
DenseMatrix & dW) const;
public:
ND_FuentesPyramidElement(const int p,
const int cb_type = BasisType::GaussLobatto,
const int ob_type = BasisType::GaussLegendre);
virtual void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const;
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_ND(Trans, shape); }
virtual void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
DenseMatrix &I) const
{ LocalInterpolation_ND(*this, tk, dof2tk, Trans, I); }
virtual void GetLocalRestriction(ElementTransformation &Trans,
DenseMatrix &R) const
{ LocalRestriction_ND(tk, dof2tk, Trans, R); }
virtual void GetTransferMatrix(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &I) const
{ LocalInterpolation_ND(CheckVectorFE(fe), tk, dof2tk, Trans, I); }
using FiniteElement::Project;
virtual void Project(VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const
{ Project_ND(tk, dof2tk, vc, Trans, dofs); }
virtual void ProjectMatrixCoefficient(
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
{ ProjectMatrixCoefficient_ND(tk, dof2tk, mc, T, dofs); }
virtual void Project(const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const
{ Project_ND(tk, dof2tk, fe, Trans, I); }
virtual void ProjectGrad(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &grad) const
{ ProjectGrad_ND(tk, dof2tk, fe, Trans, grad); }
virtual void ProjectCurl(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const
{ ProjectCurl_ND(tk, dof2tk, fe, Trans, curl); }
void CalcRawVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const;
void CalcRawCurlShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
real_t GetZetaMax() const { return zmax; }
};
/// A 0D Nedelec finite element for the boundary of a 1D domain
/** ND_R1D_PointElement provides a representation of the trace of a three
component Nedelec basis restricted to 1D.
+1 -1
View File
@@ -47,7 +47,7 @@ public:
int GetPatch () const { return patch; }
/// Set which patch should be evaluated
void SetPatch (int p) const { patch = p; }
/// Set which elemenet should be evaluated
/// Set which element should be evaluated
int GetElement () const { return elem; }
/// Get which element is currently considered
void SetElement (int e) const { elem = e; }
+752
View File
@@ -1042,6 +1042,435 @@ void H1Pos_WedgeElement::CalcDShape(const IntegrationPoint &ip,
}
}
H1Pos_PyramidElement::H1Pos_PyramidElement(const int p)
: PositiveFiniteElement(3, Geometry::PYRAMID,
((p + 1)*(p + 2)*(2 * p + 3))/6, p,
FunctionSpace::Uk),
nterms(((p + 1)*(p + 2)*(p + 3)*(p + 4))/24)
{
#ifndef MFEM_THREAD_SAFE
m_shape_1d.SetSize(order + 1);
m_shape.SetSize(nterms);
m_dshape.SetSize(nterms, dim);
#endif
Index idx;
// vertices
dof_map[idx(p,0,0,0,0)] = 0;
Nodes.IntPoint(0).Set3(0., 0., 0.);
dof_map[idx(0,p,0,0,0)] = 1;
Nodes.IntPoint(1).Set3(1., 0., 0.);
dof_map[idx(0,0,p,0,0)] = 2;
Nodes.IntPoint(2).Set3(1., 1., 0.);
dof_map[idx(0,0,0,p,0)] = 3;
Nodes.IntPoint(3).Set3(0., 1., 0.);
dof_map[idx(0,0,0,0,p)] = 4;
Nodes.IntPoint(4).Set3(0., 0., 1.);
// edges (see Geometry::Constants<Geometry::PYRAMID>::Edges
// in fem/geom.cpp)
int o = 5;
for (int i = 1; i < p; i++) // (0,1)
{
dof_map[idx(p-i,i,0,0,0)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, 0., 0.);
}
for (int i = 1; i < p; i++) // (1,2)
{
dof_map[idx(0,p-i,i,0,0)] = o;
Nodes.IntPoint(o++).Set3(1.0, real_t(i)/p, 0.);
}
for (int i = 1; i < p; i++) // (3,2)
{
dof_map[idx(0,0,i,p-i,0)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, 1., 0.);
}
for (int i = 1; i < p; i++) // (0,3)
{
dof_map[idx(p-i,0,0,i,0)] = o;
Nodes.IntPoint(o++).Set3(0., real_t(i)/p, 0.);
}
for (int i = 1; i < p; i++) // (0,4)
{
dof_map[idx(p-i,0,0,0,i)] = o;
Nodes.IntPoint(o++).Set3(0., 0., real_t(i)/p);
}
for (int i = 1; i < p; i++) // (1,4)
{
dof_map[idx(0,p-i,0,0,i)] = o;
Nodes.IntPoint(o++).Set3(real_t(p-i)/p, 0., real_t(i)/p);
}
for (int i = 1; i < p; i++) // (2,4)
{
dof_map[idx(0,0,p-i,0,i)] = o;
Nodes.IntPoint(o++).Set3(real_t(p-i)/p, real_t(p-i)/p, real_t(i)/p);
}
for (int i = 1; i < p; i++) // (3,4)
{
dof_map[idx(0,0,0,p-i,i)] = o;
Nodes.IntPoint(o++).Set3(0., real_t(p-i)/p, real_t(i)/p);
}
// faces (see Geometry::Constants<Geometry::PYRAMID>::FaceVert
// in fem/geom.cpp)
for (int j = 1; j < p; j++)
{
int i1 = j;
int i2 = 0;
int i3 = 0;
int i4 = p - j;
const int i5 = 0;
for (int i = 1; i <= p - j; i++) // (3,2,1,0)
{
i3++;
i4--;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(p-j)/p, 0);
}
for (int i = p - j + 1; i < p; i++) // (3,2,1,0)
{
i1--;
i2++;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(p-j)/p, 0);
}
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0, 1, 4)
{
dof_map[idx(p-i-j,i,0,0,j)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, 0., real_t(j)/p);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (1, 2, 4)
{
dof_map[idx(0,p-i-j,i,0,j)] = o;
Nodes.IntPoint(o++).Set3(real_t(p-j)/p, real_t(i)/p, real_t(j)/p);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (2, 3, 4)
{
dof_map[idx(0,0,p-i-j,i,j)] = o;
Nodes.IntPoint(o++).Set3(real_t(p-i-j)/p, real_t(p-j)/p, real_t(j)/p);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (3, 0, 4)
{
dof_map[idx(i,0,0,p-i-j,j)] = o;
Nodes.IntPoint(o++).Set3(0., real_t(p-i-j)/p, real_t(j)/p);
}
// interior
for (int k = 1; k < p; k++)
for (int j = 1; j + k < p; j++)
{
int i1 = p - j - k;
int i2 = 0;
int i3 = 0;
int i4 = j;
const int i5 = k;
for (int i = 1; i <= j; i++)
{
i3++;
i4--;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
}
for (int i = j + 1; i + k < p; i++)
{
i1--;
i2++;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
}
}
}
// static method
void H1Pos_PyramidElement::CalcShape(const int p, const real_t x,
const real_t y, const real_t z,
real_t *shape_1d,
real_t *shape)
{
const int lshape = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
for (int i=0; i<lshape; i++) { shape[i] = 0.0; }
const real_t l1 = lam1(x, y, z);
const real_t l2 = lam2(x, y, z);
const real_t l3 = lam3(x, y, z);
const real_t l4 = lam4(x, y, z);
const real_t l5 = lam5(x, y, z);
// The basis functions are the terms in the expansion:
// (l1 + l2 + l3 + l4 + l5)^p =
// \sum_{l=0}^p \binom{p}{l} l5^l
// \sum_{k=0}^{p-l} \binom{p-l}{k} l4^k
// \sum_{j=0}^{p-l-k} \binom{p-l-k}{j} l3^j
// \sum_{i=0}^{p-l-k-j} \binom{p-l-k-j}{i} l2^i l1^{p-l-k-j-i}
Index idx;
const int *bp = Poly_1D::Binom(p);
real_t l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, shape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
shape_1d[i2] *= ei345;
shape[o] += shape_1d[i2];
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
}
// static method
void H1Pos_PyramidElement::CalcDShape(const int p, const real_t x,
const real_t y, const real_t z,
real_t *dshape_1d, real_t *dshape)
{
const int nterms = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
for (int i=0; i<3*nterms; i++) { dshape[i] = 0.0; }
const real_t l1 = lam1(x, y, z);
const real_t l2 = lam2(x, y, z);
const real_t l3 = lam3(x, y, z);
const real_t l4 = lam4(x, y, z);
const real_t l5 = lam5(x, y, z);
const Vector dl1 = grad_lam1(x, y, z);
const Vector dl2 = grad_lam2(x, y, z);
const Vector dl3 = grad_lam3(x, y, z);
const Vector dl4 = grad_lam4(x, y, z);
const Vector dl5 = grad_lam5(x, y, z);
// The basis functions are the terms in the expansion:
// (l1 + l2 + l3 + l4 + l5)^p
// We will compute the derivative by first computing the derivatives
// of these terms w.r.t each of the l1, l2, l3, l4, and l5 and summing
// the results together.
Index idx;
// Derivative w.r.t. l1 times grad(l1)
const int *bp = Poly_1D::Binom(p);
real_t l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcDyBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl1 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl1 * dl1[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l2 times grad(l2)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcDxBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl2 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl2*dl2[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l3 times grad(l3)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 1; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = i3*ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl3 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl3*dl3[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l4 times grad(l4)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 1; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = i4*ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl4 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl4*dl4[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l5 times grad(l5)
l5i5 = 1.;
for (int i5 = 1; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = i5*bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl5 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl5*dl5[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
}
void H1Pos_PyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
#ifdef MFEM_THREAD_SAFE
Vector m_shape_1d(order + 1);
Vector m_shape(nterms);
#endif
CalcShape(order, ip.x, ip.y, ip.z, m_shape_1d.GetData(), m_shape.GetData());
for (auto const& it : dof_map)
{
if (it.first < m_shape.Size()) { shape[it.second] = m_shape[it.first]; }
}
}
void H1Pos_PyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
#ifdef MFEM_THREAD_SAFE
Vector m_shape_1d(order + 1);
DenseMatrix m_dshape(nterms, 3);
#endif
CalcDShape(order, ip.x, ip.y, ip.z,
m_shape_1d.GetData(), m_dshape.GetData());
for (auto const& it : dof_map)
for (int d=0; d<3; d++)
{
dshape(it.second, d) = m_dshape(it.first, d);
}
}
L2Pos_SegmentElement::L2Pos_SegmentElement(const int p)
: PositiveTensorFiniteElement(1, p, L2_DOF_MAP)
{
@@ -1448,4 +1877,327 @@ void L2Pos_WedgeElement::CalcDShape(const IntegrationPoint &ip,
}
}
L2Pos_PyramidElement::L2Pos_PyramidElement(const int p)
: PositiveFiniteElement(3, Geometry::PYRAMID,
((p + 1)*(p + 2)*(2 * p + 3))/6, p,
FunctionSpace::Uk),
nterms(((p + 1)*(p + 2)*(p + 3)*(p + 4))/24)
{
#ifndef MFEM_THREAD_SAFE
m_shape_1d.SetSize(order + 1);
m_shape.SetSize(nterms);
m_dshape.SetSize(nterms, dim);
#endif
Index idx;
// interior
for (int o = 0, k = 0; k <= p; k++)
for (int j = 0; j + k <= p; j++)
{
int i1 = p - j - k;
int i2 = 0;
int i3 = -1;
int i4 = j + 1;
const int i5 = k;
for (int i = 0; i <= j; i++)
{
i3++;
i4--;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
}
for (int i = j + 1; i + k <= p; i++)
{
i1--;
i2++;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
}
}
}
// static method
void L2Pos_PyramidElement::CalcShape(const int p, const real_t x,
const real_t y, const real_t z,
real_t *shape_1d,
real_t *shape)
{
const int lshape = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
for (int i=0; i<lshape; i++) { shape[i] = 0.0; }
const real_t l1 = lam1(x, y, z);
const real_t l2 = lam2(x, y, z);
const real_t l3 = lam3(x, y, z);
const real_t l4 = lam4(x, y, z);
const real_t l5 = lam5(x, y, z);
// The basis functions are the terms in the expansion:
// (l1 + l2 + l3 + l4 + l5)^p =
// \sum_{l=0}^p \binom{p}{l} l5^l
// \sum_{k=0}^{p-l} \binom{p-l}{k} l4^k
// \sum_{j=0}^{p-l-k} \binom{p-l-k}{j} l3^j
// \sum_{i=0}^{p-l-k-j} \binom{p-l-k-j}{i} l2^i l1^{p-l-k-j-i}
Index idx;
const int *bp = Poly_1D::Binom(p);
real_t l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, shape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
shape_1d[i2] *= ei345;
shape[o] += shape_1d[i2];
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
}
// static method
void L2Pos_PyramidElement::CalcDShape(const int p, const real_t x,
const real_t y, const real_t z,
real_t *dshape_1d, real_t *dshape)
{
const int nterms = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
for (int i=0; i<3*nterms; i++) { dshape[i] = 0.0; }
const real_t l1 = lam1(x, y, z);
const real_t l2 = lam2(x, y, z);
const real_t l3 = lam3(x, y, z);
const real_t l4 = lam4(x, y, z);
const real_t l5 = lam5(x, y, z);
const Vector dl1 = grad_lam1(x, y, z);
const Vector dl2 = grad_lam2(x, y, z);
const Vector dl3 = grad_lam3(x, y, z);
const Vector dl4 = grad_lam4(x, y, z);
const Vector dl5 = grad_lam5(x, y, z);
// The basis functions are the terms in the expansion:
// (l1 + l2 + l3 + l4 + l5)^p
// We will compute the derivative by first computing the derivatives
// of these terms w.r.t each of the l1, l2, l3, l4, and l5 and summing
// the results together.
Index idx;
// Derivative w.r.t. l1 times grad(l1)
const int *bp = Poly_1D::Binom(p);
real_t l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcDyBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl1 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl1 * dl1[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l2 times grad(l2)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcDxBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl2 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl2*dl2[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l3 times grad(l3)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 1; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = i3*ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl3 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl3*dl3[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l4 times grad(l4)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 1; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = i4*ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl4 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl4*dl4[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l5 times grad(l5)
l5i5 = 1.;
for (int i5 = 1; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = i5*bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl5 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl5*dl5[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
}
void L2Pos_PyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
#ifdef MFEM_THREAD_SAFE
Vector m_shape_1d(order + 1);
Vector m_shape(nterms);
#endif
CalcShape(order, ip.x, ip.y, ip.z, m_shape_1d.GetData(), m_shape.GetData());
for (auto const& it : dof_map)
{
if (it.first < m_shape.Size()) { shape[it.second] = m_shape[it.first]; }
}
}
void L2Pos_PyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
#ifdef MFEM_THREAD_SAFE
Vector m_shape_1d(order + 1);
DenseMatrix m_dshape(nterms, 3);
#endif
CalcDShape(order, ip.x, ip.y, ip.z,
m_shape_1d.GetData(), m_dshape.GetData());
for (auto const& it : dof_map)
for (int d=0; d<3; d++)
{
dshape(it.second, d) = m_dshape(it.first, d);
}
}
}
+119
View File
@@ -13,6 +13,7 @@
#define MFEM_FE_POS
#include "fe_base.hpp"
#include "fe_pyramid.hpp"
namespace mfem
{
@@ -256,6 +257,69 @@ public:
};
/// Arbitrary order H1 elements in 3D utilizing the Bernstein basis on a pyramid
///
/// The pyramid affine-related coordinates $\lambda_i$ for $i=1,\ldots,5$ can
/// be used to define a positive H1 basis by noting that $\lambda_i \ge 0$
/// inside the pyramid for all $i$ and that $\sum_{i=1}^5\lambda_i=1$. This
/// leads to $1 = (\sum_{i=1}^5\lambda_i)^p$. The terms of this product,
/// expanded as a polynomial in the $\lambda_i$, can be used as a Bernstein
/// basis of order $p$ on a pyramid.
class H1Pos_PyramidElement : public PositiveFiniteElement, FuentesPyramid
{
protected:
const int nterms;
#ifndef MFEM_THREAD_SAFE
mutable Vector m_shape_1d;
mutable Vector m_shape;
mutable DenseMatrix m_dshape;
#endif
std::map<int,int> dof_map;
struct Index
{
Index() = default;
int operator()(int i1, int i2, int i3, int i4, int i5)
{
const int p = i1 + i2 + i3 + i4 + i5;
const int min24 = std::min(i2,i4);
i1 += min24;
i2 -= min24;
i3 += min24;
i4 -= min24;
return i2 + i3 * (p - i4 - i5) - i4 * i5 * (p + 2)
- ((i3 - 3) * i3) / 2 + i4 * ((p + 1) * (p + 2)) / 2
+ (i4 * i5 * (i4 + i5)) / 2 + ((i4 - 1) * i4 * (i4 +1)) / 6
- (p + 2) * ((i4 - 1) * i4) / 2
+ (i5 * (5 + 2 * p - i5) * (i5 * i5 - i5 * (5 + 2 * p)
+ 2 * (5 + 5 * p + p * p))) / 24;
}
};
public:
/// Construct the H1Pos_PyramidElement of order @a p
H1Pos_PyramidElement(const int p);
// The size of shape is (p+1)(p+2)(p+3)(p+4)/24.
// The size of shape_1d should be at least p+1.
static void CalcShape(const int p, const real_t x, const real_t y,
const real_t z, real_t *shape_1d, real_t *shape);
// The size of dshape is (p+1)(p+2)(p+3)(p+4)/24 by 3.
// The size of dshape_1d should be at least p+1.
static void CalcDShape(const int p, const real_t x, const real_t y,
const real_t z, real_t *dshape_1d, real_t *dshape);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
// Returns (p+1)(p+2)(p+3)(p+4)/24 which is the size of the temporary arrays
// needed above
int GetNumTerms() const { return nterms; }
};
/// Arbitrary order L2 elements in 1D utilizing the Bernstein basis on a segment
class L2Pos_SegmentElement : public PositiveTensorFiniteElement
{
@@ -369,6 +433,61 @@ public:
DenseMatrix &dshape) const override;
};
/// Arbitrary order L2 elements in 3D utilizing the Bernstein basis on a pyramid
class L2Pos_PyramidElement : public PositiveFiniteElement, FuentesPyramid
{
protected:
const int nterms;
#ifndef MFEM_THREAD_SAFE
mutable Vector m_shape_1d;
mutable Vector m_shape;
mutable DenseMatrix m_dshape;
#endif
std::map<int,int> dof_map;
struct Index
{
Index() = default;
int operator()(int i1, int i2, int i3, int i4, int i5)
{
const int p = i1 + i2 + i3 + i4 + i5;
const int min24 = std::min(i2,i4);
i1 += min24;
i2 -= min24;
i3 += min24;
i4 -= min24;
return i2 + i3 * (p - i4 - i5) - i4 * i5 * (p + 2)
- ((i3 - 3) * i3) / 2 + i4 * ((p + 1) * (p + 2)) / 2
+ (i4 * i5 * (i4 + i5)) / 2 + ((i4 - 1) * i4 * (i4 +1)) / 6
- (p + 2) * ((i4 - 1) * i4) / 2
+ (i5 * (5 + 2 * p - i5) * (i5 * i5 - i5 * (5 + 2 * p)
+ 2 * (5 + 5 * p + p * p))) / 24;
}
};
// Returns (p+1)(p+2)(p+3)(p+4)/24 which is the size of the temporary arrays
// needed below
int GetNumTerms() const { return nterms; }
// The size of shape is (p+1)(p+2)(p+3)(p+4)/24.
// The size of shape_1d should be at least p+1.
static void CalcShape(const int p, const real_t x, const real_t y,
const real_t z, real_t *shape_1d, real_t *shape);
// The size of dshape is (p+1)(p+2)(p+3)(p+4)/24 by 3.
// The size of dshape_1d should be at least p+1.
static void CalcDShape(const int p, const real_t x, const real_t y,
const real_t z, real_t *dshape_1d, real_t *dshape);
public:
/// Construct the L2Pos_PyramidElement of order @a p
L2Pos_PyramidElement(const int p);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
};
} // namespace mfem
#endif
File diff suppressed because it is too large Load Diff
+510
View File
@@ -0,0 +1,510 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_FE_PYRAMID
#define MFEM_FE_PYRAMID
#include "fe_base.hpp"
namespace mfem
{
/** Base class for arbitrary order basis functions on pyramid-shaped elements
This base class provides a common class to store temporary vectors,
matrices, and tensors computed by various functions defined on
pyramid-shaped elements.
The function names defined here are chosen to reflect, as closely as
possible, those used in the paper "Orientation embedded high order
shape functions for the exact sequence elements of all shapes" by
Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
@note Many of the functions below, e.g. lam1, lam2, etc. and related
functions, are singular or multi-valued at the apex of the pyramid. The
values returned near the apex are computed in the limit z->1 using
(x, y, z) = ((1-z)/2, (1-z)/2, z) i.e. along the line from the center
of the base to the apex.
*/
class FuentesPyramid
{
private:
#ifndef MFEM_THREAD_SAFE
mutable DenseMatrix phi_E_mtmp;
mutable Vector phi_Q_vtmp1;
mutable Vector phi_Q_vtmp2;
mutable DenseMatrix phi_Q_mtmp1;
mutable DenseMatrix phi_Q_mtmp2;
mutable Vector phi_T_vtmp1;
mutable Vector phi_T_vtmp2;
mutable Vector phi_T_vtmp3;
mutable Vector phi_T_vtmp4;
mutable DenseMatrix phi_T_mtmp1;
mutable Vector E_E_vtmp;
mutable Vector E_Q_vtmp;
mutable DenseMatrix E_Q_mtmp1;
mutable DenseMatrix E_Q_mtmp2;
mutable DenseMatrix E_Q_mtmp3;
mutable Vector E_T_vtmp1;
mutable Vector E_T_vtmp2;
mutable Vector E_T_vtmp3;
mutable DenseMatrix E_T_mtmp1;
mutable DenseMatrix E_T_mtmp2;
mutable DenseMatrix V_Q_mtmp1;
mutable DenseMatrix V_Q_mtmp2;
mutable Vector V_T_vtmp1;
mutable Vector V_T_vtmp2;
mutable DenseMatrix V_T_mtmp1;
mutable Vector VT_T_vtmp1;
mutable Vector VT_T_vtmp2;
mutable DenseMatrix VT_T_mtmp1;
mutable DenseTensor VT_T_ttmp1;
mutable Vector V_L_vtmp1;
mutable Vector V_L_vtmp2;
mutable DenseMatrix V_L_mtmp1;
mutable DenseMatrix V_L_mtmp2;
mutable Vector V_R_vtmp;
mutable DenseMatrix V_R_mtmp;
#endif
protected:
static constexpr real_t one = 1.0;
static constexpr real_t zero = 0.0;
static constexpr real_t apex_tol = 1e-8;
public:
FuentesPyramid() = default;
static bool CheckZ(real_t z) { return std::abs(z - 1.0) > apex_tol; }
/// Pyramid "Affine" Coordinates
static real_t lam1(real_t x, real_t y, real_t z)
{ return CheckZ(z) ? (1.0 - x - z) * (1.0 - y - z) / (1.0 - z): 0.0; }
static real_t lam2(real_t x, real_t y, real_t z)
{ return CheckZ(z) ? x * (1.0 - y - z) / (1.0 - z): 0.0; }
static real_t lam3(real_t x, real_t y, real_t z)
{ return CheckZ(z) ? x * y / (1.0 - z): 0.0; }
static real_t lam4(real_t x, real_t y, real_t z)
{ return CheckZ(z) ? (1.0 - x - z) * y / (1.0 - z): 0.0; }
static real_t lam5(real_t x, real_t y, real_t z)
{ return CheckZ(z) ? z : 1.0; }
/// Gradients of the "Affine" Coordinates
static Vector grad_lam1(real_t x, real_t y, real_t z);
static Vector grad_lam2(real_t x, real_t y, real_t z);
static Vector grad_lam3(real_t x, real_t y, real_t z);
static Vector grad_lam4(real_t x, real_t y, real_t z);
static Vector grad_lam5(real_t x, real_t y, real_t z);
/// Two component vectors associated with edges touching the apex
static Vector lam15(real_t x, real_t y, real_t z)
{ return Vector({lam1(x, y, z), lam5(x, y, z)}); }
static Vector lam25(real_t x, real_t y, real_t z)
{ return Vector({lam2(x, y, z), lam5(x, y, z)}); }
static Vector lam35(real_t x, real_t y, real_t z)
{ return Vector({lam3(x, y, z), lam5(x, y, z)}); }
static Vector lam45(real_t x, real_t y, real_t z)
{ return Vector({lam4(x, y, z), lam5(x, y, z)}); }
/// Gradients of the above two component vectors
static DenseMatrix grad_lam15(real_t x, real_t y, real_t z);
static DenseMatrix grad_lam25(real_t x, real_t y, real_t z);
static DenseMatrix grad_lam35(real_t x, real_t y, real_t z);
static DenseMatrix grad_lam45(real_t x, real_t y, real_t z);
/// Computes $\lambda_i \nabla \lambda_5 - \lambda_5 \nabla \lambda_i$
static Vector lam15_grad_lam15(real_t x, real_t y, real_t z);
static Vector lam25_grad_lam25(real_t x, real_t y, real_t z);
static Vector lam35_grad_lam35(real_t x, real_t y, real_t z);
static Vector lam45_grad_lam45(real_t x, real_t y, real_t z);
/// Three component vectors associated with triangular faces
static Vector lam125(real_t x, real_t y, real_t z)
{ return Vector({lam1(x, y, z), lam2(x, y, z), lam5(x, y, z)}); }
static Vector lam235(real_t x, real_t y, real_t z)
{ return Vector({lam2(x, y, z), lam3(x, y, z), lam5(x, y, z)}); }
static Vector lam345(real_t x, real_t y, real_t z)
{ return Vector({lam3(x, y, z), lam4(x, y, z), lam5(x, y, z)}); }
static Vector lam435(real_t x, real_t y, real_t z)
{ return Vector({lam4(x, y, z), lam3(x, y, z), lam5(x, y, z)}); }
static Vector lam415(real_t x, real_t y, real_t z)
{ return Vector({lam4(x, y, z), lam1(x, y, z), lam5(x, y, z)}); }
static Vector lam145(real_t x, real_t y, real_t z)
{ return Vector({lam1(x, y, z), lam4(x, y, z), lam5(x, y, z)}); }
/// Vector functions related to the normals to the triangular faces
///
/// Computes
/// $
/// \lambda_i \nabla\lambda_j \times \nabla \lambda_5
/// + \lambda_j \nabla\lambda_5 \times \nabla \lambda_i
/// + \lambda_5 \nabla\lambda_i \times \nabla \lambda_j
/// $
static Vector lam125_grad_lam125(real_t x, real_t y, real_t z);
static Vector lam235_grad_lam235(real_t x, real_t y, real_t z);
static Vector lam345_grad_lam345(real_t x, real_t y, real_t z);
static Vector lam435_grad_lam435(real_t x, real_t y, real_t z);
static Vector lam415_grad_lam415(real_t x, real_t y, real_t z);
static Vector lam145_grad_lam145(real_t x, real_t y, real_t z);
/// Divergences of the above "normal" vector functions divided by 3
static real_t div_lam125_grad_lam125(real_t x, real_t y, real_t z);
static real_t div_lam235_grad_lam235(real_t x, real_t y, real_t z);
static real_t div_lam345_grad_lam345(real_t x, real_t y, real_t z);
static real_t div_lam435_grad_lam435(real_t x, real_t y, real_t z);
static real_t div_lam415_grad_lam415(real_t x, real_t y, real_t z);
static real_t div_lam145_grad_lam145(real_t x, real_t y, real_t z);
static real_t mu0(real_t z)
{ return 1.0 - z; }
static real_t mu1(real_t z)
{ return z; }
static Vector grad_mu0(real_t z)
{ return Vector({0.0, 0.0, -1.0}); }
static Vector grad_mu1(real_t z)
{ return Vector({0.0, 0.0, 1.0}); }
static Vector mu01(real_t z)
{ return Vector({mu0(z), mu1(z)}); }
static DenseMatrix grad_mu01(real_t z);
static real_t mu0(real_t z, const Vector &xy, unsigned int ab)
{ return 1.0 - xy[ab-1] / (1.0 - z); }
static real_t mu1(real_t z, const Vector &xy, unsigned int ab)
{ return xy[ab-1] / (1.0 - z); }
static Vector grad_mu0(real_t z, const Vector xy, unsigned int ab);
static Vector grad_mu1(real_t z, const Vector xy, unsigned int ab);
static Vector mu01(real_t z, Vector xy, unsigned int ab)
{ return Vector({mu0(z, xy, ab), mu1(z, xy, ab)}); }
static DenseMatrix grad_mu01(real_t z, Vector xy, unsigned int ab);
static Vector mu01_grad_mu01(real_t z, Vector xy, unsigned int ab);
static real_t nu0(real_t z, Vector xy, unsigned int ab)
{ return 1.0 - xy[ab-1] - z; }
static real_t nu1(real_t z, Vector xy, unsigned int ab) { return xy[ab-1]; }
static real_t nu2(real_t z, Vector xy, unsigned int ab) { return z; }
static Vector grad_nu0(real_t z, const Vector xy, unsigned int ab);
static Vector grad_nu1(real_t z, const Vector xy, unsigned int ab);
static Vector grad_nu2(real_t z, const Vector xy, unsigned int ab);
static Vector nu01(real_t z, Vector xy, unsigned int ab)
{ return Vector({nu0(z, xy, ab), nu1(z, xy, ab)}); }
static Vector nu12(real_t z, Vector xy, unsigned int ab)
{ return Vector({nu1(z, xy, ab), nu2(z, xy, ab)}); }
static Vector nu012(real_t z, Vector xy, unsigned int ab)
{ return Vector({nu0(z, xy, ab), nu1(z, xy, ab), nu2(z, xy, ab)}); }
static Vector nu120(real_t z, Vector xy, unsigned int ab)
{ return Vector({nu1(z, xy, ab), nu2(z, xy, ab), nu0(z, xy, ab)}); }
static DenseMatrix grad_nu01(real_t z, Vector xy, unsigned int ab);
static DenseMatrix grad_nu012(real_t z, Vector xy, unsigned int ab);
static DenseMatrix grad_nu120(real_t z, Vector xy, unsigned int ab);
static Vector nu01_grad_nu01(real_t z, Vector xy, unsigned int ab);
static Vector nu12_grad_nu12(real_t z, Vector xy, unsigned int ab);
static Vector nu012_grad_nu012(real_t z, Vector xy, unsigned int ab);
/// Shifted and Scaled Legendre Polynomials
/** Implements a scaled and shifted set of Legendre polynomials
$P_i(x;t) = P_i(x / t) * t^i$
where @a t >= 0.0, @a x $\in [0,t]$, and $P_i$ is the shifted Legendre
polynomial defined on $[0,1]$ rather than the usual $[-1,1]$. The
entries stored in @a u correspond to the values of
$P_0$, $P_1$, ... $P_p$.
@a u must be at least @a p + 1 in length
*/
static void CalcScaledLegendre(int p, real_t x, real_t t,
real_t *u);
static void CalcScaledLegendre(int p, real_t x, real_t t,
real_t *u, real_t *dudx, real_t *dudt);
static void CalcScaledLegendre(int p, real_t x, real_t t,
Vector &u);
static void CalcScaledLegendre(int p, real_t x, real_t t,
Vector &u, Vector &dudx, Vector &dudt);
/// Integrated Legendre Polynomials
/** These are the integrals of the shifted and scaled Legendre polynomials
provided above and defined as:
$L_i(x;t) = \int_0^x P_{i-1}(y;t)dy\mbox{ for }i>=1$
These polynomials are computed as:
$L_0(x;t) = 0$, $L_1(x;t) = x$,
$2(2i-1)L_i(x;t) = P_i(x;t) - t^2 P_{i-2}(x;t)\mbox{ for }i>=2$
@a u must be at least @a p + 1 in length
*/
static void CalcIntegratedLegendre(int p, real_t x,
real_t t, real_t *u);
static void CalcIntegratedLegendre(int p, real_t x,
real_t t, real_t *u,
real_t *dudx, real_t *dudt);
static void CalcIntegratedLegendre(int p, real_t x,
real_t t, Vector &u);
static void CalcIntegratedLegendre(int p, real_t x,
real_t t, Vector &u,
Vector &dudx, Vector &dudt);
/** @a u must be at least @a p + 1 in length */
static void CalcHomogenizedScaLegendre(int p, real_t s0, real_t s1,
real_t *u);
static void CalcHomogenizedScaLegendre(int p,
real_t s0, real_t s1,
real_t *u,
real_t *duds0, real_t *duds1);
static void CalcHomogenizedScaLegendre(int p, real_t s0, real_t s1,
Vector &u);
static void CalcHomogenizedScaLegendre(int p,
real_t s0, real_t s1,
Vector &u,
Vector &duds0, Vector &duds1);
/** @a u must be at least @a p + 1 in length */
static void CalcHomogenizedIntLegendre(int p,
real_t t0, real_t t1,
real_t *u);
static void CalcHomogenizedIntLegendre(int p,
real_t t0, real_t t1,
real_t *u,
real_t *dudt0, real_t *dudt1);
static void CalcHomogenizedIntLegendre(int p,
real_t t0, real_t t1,
Vector &u);
static void CalcHomogenizedIntLegendre(int p,
real_t t0, real_t t1,
Vector &u,
Vector &dudt0, Vector &dudt1);
/// Shifted and Scaled Jacobi Polynomials
/** Implements a scaled and shifted set of Jacobi polynomials
$P^\alpha_i(x / t) * t^i$
where @a alpha $= \alpha >-1$, @a t $>= 0.0$, @a x $\in [0,t]$, and
$P^\alpha_i$ is the shifted Jacobi polynomial defined on $[0,1]$ rather
than the usual $[-1,1]$. The entries stored in @a u correspond to the
values of $P^\alpha_0$, $P^\alpha_1$, ... $P^\alpha_p$.
@note Jacobi polynomials typically posses two parameters,
$P^{\alpha, \beta}_i$, but we only consider the special case where
$\beta=0$.
@a u must be at least @a p + 1 in length
*/
static void CalcScaledJacobi(int p, real_t alpha,
real_t x, real_t t,
real_t *u);
static void CalcScaledJacobi(int p, real_t alpha,
real_t x, real_t t,
real_t *u, real_t *dudx, real_t *dudt);
static void CalcScaledJacobi(int p, real_t alpha,
real_t x, real_t t,
Vector &u);
static void CalcScaledJacobi(int p, real_t alpha,
real_t x, real_t t,
Vector &u, Vector &dudx, Vector &dudt);
/// Integrated Jacobi Polynomials
/** These are the integrals of the shifted and scaled Jacobi polynomials
provided above and defined as:
$L^\alpha_i(x;t) = \int_0^x P^\alpha_{i-1}(y;t)dy\mbox{ for }i>=1$
These polynomials are computed as:
$L^\alpha_0(x;t) = 0$, $L^\alpha_1(x;t) = x$,
$L^\alpha_i(x;t) = a_i P^\alpha_i(x;t) + b_i t P^\alpha_{i-1}(x;t)
- c_i t^2 P^\alpha_{i-2}(x;t)\mbox{ for }i>=2$
With
$a_i = (i + \alpha) / (2i + \alpha - 1)(2i + \alpha)$
$b_i = \alpha / (2i + \alpha - 2)(2i + \alpha)$
$c_i = (i - 1) / (2i + \alpha - 2)(2i + \alpha - 1)$
@a u must be at least @a p + 1 in length
*/
static void CalcIntegratedJacobi(int p, real_t alpha,
real_t x, real_t t,
real_t *u);
static void CalcIntegratedJacobi(int p, real_t alpha,
real_t x, real_t t,
real_t *u, real_t *dudx, real_t *dudt);
static void CalcIntegratedJacobi(int p, real_t alpha,
real_t x, real_t t,
Vector &u)
{ CalcIntegratedJacobi(p, alpha, x, t, u.GetData()); }
static void CalcIntegratedJacobi(int p, real_t alpha,
real_t x, real_t t,
Vector &u, Vector &dudx, Vector &dudt)
{
CalcIntegratedJacobi(p, alpha, x, t, u.GetData(),
dudx.GetData(), dudt.GetData());
}
/** @a u must be at least @a p + 1 in length */
static void CalcHomogenizedScaJacobi(int p, real_t alpha,
real_t t0, real_t t1,
real_t *u)
{ CalcScaledJacobi(p, alpha, t1, t0 + t1, u); }
static void CalcHomogenizedScaJacobi(int p, real_t alpha,
real_t t0, real_t t1,
real_t *u,
real_t *dudt0, real_t *dudt1);
static void CalcHomogenizedScaJacobi(int p, real_t alpha,
real_t t0, real_t t1,
Vector &u);
static void CalcHomogenizedScaJacobi(int p, real_t alpha,
real_t t0, real_t t1,
Vector &u,
Vector &dudt0, Vector &dudt1);
/** @a u must be at least @a p + 1 in length */
static void CalcHomogenizedIntJacobi(int p, real_t alpha,
real_t t0, real_t t1,
real_t *u)
{ CalcIntegratedJacobi(p, alpha, t1, t0 + t1, u); }
static void CalcHomogenizedIntJacobi(int p, real_t alpha,
real_t t0, real_t t1,
real_t *u,
real_t *dudt0, real_t *dudt1);
static void CalcHomogenizedIntJacobi(int p, real_t alpha,
real_t t0, real_t t1,
Vector &u);
static void CalcHomogenizedIntJacobi(int p, real_t alpha,
real_t t0, real_t t1,
Vector &u,
Vector &dudt0, Vector &dudt1);
/** @a u must be at least @a p + 1 in length */
static void phi_E(int p, real_t s0, real_t s1, real_t *u);
static void phi_E(int p, real_t s0, real_t s1, real_t *u,
real_t *duds0, real_t *duds1);
static void phi_E(int p, Vector s, Vector &u);
static void phi_E(int p, Vector s, Vector &u, DenseMatrix &duds);
/** @a grad_s must be 2x3 */
void phi_E(int p, Vector s, const DenseMatrix &grad_s,
Vector &u, DenseMatrix &grad_u) const;
/** @a u must be at least (p+1)x(p+1) in size */
void phi_Q(int p, Vector s, Vector t, DenseMatrix &u) const;
void phi_Q(int p, Vector s, const DenseMatrix &grad_s,
Vector t, const DenseMatrix &grad_t,
DenseMatrix &u, DenseTensor &grad_u) const;
void phi_T(int p, Vector nu, DenseMatrix &u) const;
void phi_T(int p, Vector nu, const DenseMatrix &grad_nu,
DenseMatrix &u, DenseTensor &grad_u) const;
/** This is a vector-valued function associated with an edge of a pyramid
The vector @a s contains two coordinate values and @a ds is related to the
gradient of these coordinates with respect to the reference coordinates i.e.
sds = s0 grad s1 - s1 grad s0
*/
void E_E(int p, Vector s, Vector sds, DenseMatrix &u) const;
void E_E(int p, Vector s, const DenseMatrix &grad_s, DenseMatrix &u,
DenseMatrix &curl_u) const;
void E_Q(int p, Vector s, Vector ds, Vector t,
DenseTensor &u) const;
void E_Q(int p, Vector s, const DenseMatrix &grad_s,
Vector t, const DenseMatrix &grad_t,
DenseTensor &u, DenseTensor &curl_u) const;
void E_T(int p, Vector s, Vector sds, DenseTensor &u) const;
void E_T(int p, Vector s, const DenseMatrix &grad_s,
DenseTensor &u, DenseTensor &curl_u) const;
/** This is a vector-valued function associated with the quadrilateral face
of a pyramid
The vectors @a s and @a t contain pairs of coordinate values and @a ds and
@a dt are related to derivatives of these coordinates:
ds = s0 grad s1 - s1 grad s0
dt = t0 grad t1 - t1 grad t0
*/
void V_Q(int p, Vector s, Vector ds, Vector t, Vector dt,
DenseTensor &u) const;
/** This is a vector-valued function associated with the triangular faces of
a pyramid
The vector @a s contains three coordinate values and @a sdsxds is related to
derivatives of these coordinates with respect to the reference coordinates:
sdsxds = s0 grad s1 x grad s2 + s1 grad s2 x grad s0 +
s2 grad s0 x grad s1
*/
void V_T(int p, Vector s, Vector sdsxds, DenseTensor &u) const;
/** This computes V_T as above and its divergence
The vector @a s contains three coordinate values and @a sdsxds is related to
derivatives of these coordinates with respect to the reference coordinates:
sdsxds = s0 grad s1 x grad s2 + s1 grad s2 x grad s0 +
s2 grad s0 x grad s1
The scalar @a dsdsxds is the divergence of sdsxds:
dsdsxds = grad s0 dot (grad s1 x grad s2)
*/
void V_T(int p, Vector s, Vector sdsxds, real_t dsdsxds,
DenseTensor &u, DenseMatrix &du) const;
void VT_T(int p, Vector s, Vector sds, Vector sdsxds,
real_t mu, Vector grad_mu, DenseTensor &u) const;
void VT_T(int p, Vector s, Vector sds, Vector sdsxds,
Vector grad_s2, real_t mu, Vector grad_mu,
DenseTensor &u, DenseMatrix &du) const;
/** This implements $V^\unlhd_{ij}$ from the Fuentes paper
@a u must be at least (p+1)x(p+1)x3
*/
void V_L(int p, Vector sx, const DenseMatrix &grad_sx,
Vector sy, const DenseMatrix &grad_sy,
real_t t, Vector grad_t, DenseTensor &u) const;
/** This implements $V^\unrhd_i$ from the Fuentes paper
@a u must be at least (p+1)x3 */
void V_R(int p, Vector s, const DenseMatrix &grad_s,
real_t mu, Vector grad_mu,
real_t t, Vector grad_t, DenseMatrix &u) const;
};
} // namespace mfem
#endif
+736
View File
@@ -1264,6 +1264,742 @@ void RT_WedgeElement::CalcDivShape(const IntegrationPoint &ip,
}
}
const real_t RT_FuentesPyramidElement::nk[24] =
{
0,0,-1, 0,-1,0, 1,0,1, 0,1,1, -1,0,0,
M_SQRT2,0,M_SQRT1_2, 0,M_SQRT2,M_SQRT1_2, 0,0,1
};
RT_FuentesPyramidElement::RT_FuentesPyramidElement(const int p)
: VectorFiniteElement(3, Geometry::PYRAMID, (p + 1)*(3*p*(p + 2) + 5),
p + 1, H_DIV, FunctionSpace::Uk),
dof2nk(dof)
{
zmax = 0.0;
const real_t *iop = poly1d.OpenPoints(p);
const real_t *icp = poly1d.ClosedPoints(p + 1);
const real_t *bop = poly1d.OpenPoints(p);
#ifndef MFEM_THREAD_SAFE
tmp1_i.SetSize(p + 2);
tmp1_ij.SetSize(p + 2, p + 2);
tmp2_ij.SetSize(p + 2, dim);
tmp3_ij.SetSize(p + 2, dim);
tmp4_ij.SetSize(p + 1, p + 1);
tmp1_ijk.SetSize(p + 1, p + 1, dim);
tmp2_ijk.SetSize(p + 1, p + 1, dim);
tmp3_ijk.SetSize(p + 1, p + 1, dim);
tmp4_ijk.SetSize(p + 1, p + 2, dim);
tmp5_ijk.SetSize(p + 1, p + 2, dim);
tmp6_ijk.SetSize(p + 2, p + 2, dim);
tmp7_ijk.SetSize(p + 2, p + 2, dim);
u.SetSize(dof, dim);
divu.SetSize(dof);
#else
Vector tmp1_i(p + 2);
DenseMatrix tmp1_ij(p + 2, p + 2);
DenseMatrix tmp2_ij(p + 2, dim);
DenseMatrix tmp3_ij(p + 2, dim);
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
DenseMatrix u(dof, dim);
#endif
int o = 0;
// quadrilateral face
for (int j = 0; j <= p; j++)
for (int i = 0; i <= p; i++) // (3,2,1,0)
{
Nodes.IntPoint(o).Set3(bop[i], bop[p-j], 0.);
dof2nk[o++] = 0;
}
// triangular faces
for (int j = 0; j <= p; j++)
for (int i = 0; i + j <= p; i++) // (0,1,4)
{
real_t w = bop[i] + bop[j] + bop[p-i-j];
Nodes.IntPoint(o).Set3(bop[i]/w, 0., bop[j]/w);
dof2nk[o++] = 1;
}
for (int j = 0; j <= p; j++)
for (int i = 0; i + j <= p; i++) // (1,2,4)
{
real_t w = bop[i] + bop[j] + bop[p-i-j];
Nodes.IntPoint(o).Set3(1.-bop[j]/w, bop[i]/w, bop[j]/w);
dof2nk[o++] = 2;
}
for (int j = 0; j <= p; j++)
for (int i = p - j; i >= 0; i--) // (2,3,4)
{
real_t w = bop[i] + bop[j] + bop[p-i-j];
Nodes.IntPoint(o).Set3(bop[i]/w, 1.0-bop[j]/w, bop[j]/w);
dof2nk[o++] = 3;
}
for (int j = 0; j <= p; j++)
for (int i = p - j; i >= 0; i--) // (3,0,4)
{
real_t w = bop[i] + bop[j] + bop[p-i-j];
Nodes.IntPoint(o).Set3(0., bop[i]/w, bop[j]/w);
dof2nk[o++] = 4;
}
// interior
// x-components
for (int k = 0; k <= p; k++)
for (int j = 0; j <= p; j++)
for (int i = 1; i <= p; i++)
{
real_t w = 1.0 - iop[k];
Nodes.IntPoint(o).Set3(icp[i]*w, iop[j]*w, iop[k]);
dof2nk[o++] = 5;
}
// y-components
for (int k = 0; k <= p; k++)
for (int j = 1; j <= p; j++)
for (int i = 0; i <= p; i++)
{
real_t w = 1.0 - iop[k];
Nodes.IntPoint(o).Set3(iop[i]*w, icp[j]*w, iop[k]);
dof2nk[o++] = 6;
}
// z-components
for (int k = 1; k <= p; k++)
for (int j = 0; j <= p; j++)
for (int i = 0; i <= p; i++)
{
real_t w = 1.0 - icp[k];
Nodes.IntPoint(o).Set3(iop[i]*w, iop[j]*w, icp[k]);
dof2nk[o++] = 7;
}
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
const Vector nm({nk[3*dof2nk[m]], nk[3*dof2nk[m]+1], nk[3*dof2nk[m]+2]});
calcBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ijk, tmp5_ijk, tmp6_ijk,
tmp7_ijk,
tmp3_ij, u);
u.Mult(nm, T.GetColumn(m));
}
Ti.Factor(T);
}
void RT_FuentesPyramidElement::CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const
{
#ifdef MFEM_THREAD_SAFE
const int p = order - 1;
Vector tmp1_i(p + 2);
DenseMatrix tmp1_ij(p + 2, p + 2);
DenseMatrix tmp2_ij(p + 2, dim);
DenseMatrix tmp3_ij(p + 2, dim);
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
DenseMatrix u(dof, dim);
#endif
calcBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ijk, tmp5_ijk, tmp6_ijk,
tmp7_ijk, tmp3_ij, u);
Ti.Mult(u, shape);
}
void RT_FuentesPyramidElement::CalcRawVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const
{
#ifdef MFEM_THREAD_SAFE
const int p = order - 1;
Vector tmp1_i(p + 2);
DenseMatrix tmp1_ij(p + 2, p + 2);
DenseMatrix tmp2_ij(p + 2, dim);
DenseMatrix tmp3_ij(p + 2, dim);
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
#endif
calcBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ijk, tmp5_ijk, tmp6_ijk,
tmp7_ijk, tmp3_ij, shape);
}
void RT_FuentesPyramidElement::CalcDivShape(const IntegrationPoint &ip,
Vector &divshape) const
{
#ifdef MFEM_THREAD_SAFE
const int p = order - 1;
Vector tmp1_i(p + 2);
DenseMatrix tmp1_ij(p + 2, p + 2);
DenseMatrix tmp2_ij(p + 2, dim);
DenseMatrix tmp3_ij(p + 2, dim);
DenseMatrix tmp4_ij(p + 1, p + 1);
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
Vector divu(dof);
#endif
divu = 0.0;
calcDivBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ij, tmp4_ijk, tmp5_ijk,
tmp6_ijk, tmp7_ijk, tmp3_ij, divu);
Ti.Mult(divu, divshape);
}
void RT_FuentesPyramidElement::CalcRawDivShape(const IntegrationPoint &ip,
Vector &dshape) const
{
#ifdef MFEM_THREAD_SAFE
const int p = order - 1;
Vector tmp1_i(p + 2);
DenseMatrix tmp1_ij(p + 2, p + 2);
DenseMatrix tmp2_ij(p + 2, dim);
DenseMatrix tmp3_ij(p + 2, dim);
DenseMatrix tmp4_ij(p + 1, p + 1);
DenseTensor tmp1_ijk(p + 1, p + 1, dim);
DenseTensor tmp2_ijk(p + 1, p + 1, dim);
DenseTensor tmp3_ijk(p + 1, p + 1, dim);
DenseTensor tmp4_ijk(p + 1, p + 2, dim);
DenseTensor tmp5_ijk(p + 1, p + 2, dim);
DenseTensor tmp6_ijk(p + 2, p + 2, dim);
DenseTensor tmp7_ijk(p + 2, p + 2, dim);
#endif
calcDivBasis(order, ip, tmp1_i, tmp1_ij, tmp2_ij,
tmp1_ijk, tmp2_ijk, tmp3_ijk, tmp4_ij, tmp4_ijk, tmp5_ijk,
tmp6_ijk, tmp7_ijk, tmp3_ij, dshape);
}
void RT_FuentesPyramidElement::calcBasis(const int p,
const IntegrationPoint &ip,
Vector &phi_k,
DenseMatrix &phi_ij,
DenseMatrix &dphi_k,
DenseTensor &VQ_ijk,
DenseTensor &VT_ijk,
DenseTensor &VTT_ijk,
DenseTensor &E_ijk,
DenseTensor &dE_ijk,
DenseTensor &dphi_ijk,
DenseTensor &VL_ijk,
DenseMatrix &VR_ij,
DenseMatrix &F) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
real_t mu;
if (std::fabs(1.0 - z) < apex_tol)
{
z = 1.0 - apex_tol;
y = 0.5 * (1.0 - z);
x = 0.5 * (1.0 - z);
xy(0) = x; xy(1) = y;
}
zmax = std::max(z, zmax);
F = 0.0;
int o = 0;
// Quadrilateral face
if (z < 1.0)
{
V_Q(p, mu01(z, xy, 1), mu01_grad_mu01(z, xy, 1),
mu01(z, xy, 2), mu01_grad_mu01(z, xy, 2),
VQ_ijk);
const real_t muz3 = pow(mu0(z), 3);
for (int j=0; j<p; j++)
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
F(o, k) = muz3 * VQ_ijk(i, j, k);
}
}
// Triangular faces
if (z < 1.0)
{
Vector dmuz;
// (a,b) = (1,2), c = 0
V_T(p, nu012(z, xy, 1), nu012_grad_nu012(z, xy, 1), VT_ijk);
mu = mu0(z, xy, 2);
dmuz.Destroy(); dmuz = grad_mu0(z, xy, 2);
VT_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1),
nu012_grad_nu012(z, xy, 1), mu, dmuz, VTT_ijk);
for (int j=0; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
F(o, k) = 0.5 * (mu * VT_ijk(i, j, k) + VTT_ijk(i, j, k));
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
dmuz.Destroy(); dmuz = grad_mu1(z, xy, 2);
VT_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1),
nu012_grad_nu012(z, xy, 1), mu, dmuz, VTT_ijk);
for (int j=0; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
F(o, k) = 0.5 * (mu * VT_ijk(i, j, k) + VTT_ijk(i, j, k));
}
// (a,b) = (2,1), c = 0
V_T(p, nu012(z, xy, 2), nu012_grad_nu012(z, xy, 2), VT_ijk);
mu = mu0(z, xy, 1);
dmuz.Destroy(); dmuz = grad_mu0(z, xy, 1);
VT_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2),
nu012_grad_nu012(z, xy, 2), mu, dmuz, VTT_ijk);
for (int j=0; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
F(o, k) = 0.5 * (mu * VT_ijk(i, j, k) + VTT_ijk(i, j, k));
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
dmuz.Destroy(); dmuz = grad_mu1(z, xy, 1);
VT_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2),
nu012_grad_nu012(z, xy, 2), mu, dmuz, VTT_ijk);
for (int j=0; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
F(o, k) = 0.5 * (mu * VT_ijk(i, j, k) + VTT_ijk(i, j, k));
}
}
// Interior
// Family I
if (z < 1.0 && p >= 2)
{
E_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu01(z, xy, 2), grad_mu01(z, xy, 2), E_ijk, dE_ijk);
phi_E(p, mu01(z), grad_mu01(z), phi_k, dphi_k);
const real_t muz = mu0(z);
const Vector dmuz(grad_mu0(z));
Vector dmuphi(3), E(3), v(3);
for (int k=2; k<=p; k++)
{
dmuphi(0) = muz * dphi_k(k,0) + dmuz(0) * phi_k(k);
dmuphi(1) = muz * dphi_k(k,1) + dmuz(1) * phi_k(k);
dmuphi(2) = muz * dphi_k(k,2) + dmuz(2) * phi_k(k);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_ijk(i,j,0); E(1) = E_ijk(i,j,1); E(2) = E_ijk(i,j,2);
dmuphi.cross3D(E, v);
for (int l=0; l<3; l++)
{
F(o, l) = muz * phi_k(k) * dE_ijk(i,j,l) + v(l);
}
}
}
}
// Family II
if (z < 1.0 && p >= 2)
{
E_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
mu01(z, xy, 1), grad_mu01(z, xy, 1), E_ijk, dE_ijk);
// Re-using phi_E from Family I
const real_t muz = mu0(z);
const Vector dmuz(grad_mu0(z));
Vector dmuphi(3), E(3), v(3);
for (int k=2; k<=p; k++)
{
dmuphi(0) = muz * dphi_k(k,0) + dmuz(0) * phi_k(k);
dmuphi(1) = muz * dphi_k(k,1) + dmuz(1) * phi_k(k);
dmuphi(2) = muz * dphi_k(k,2) + dmuz(2) * phi_k(k);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_ijk(i,j,0); E(1) = E_ijk(i,j,1); E(2) = E_ijk(i,j,2);
dmuphi.cross3D(E, v);
for (int l=0; l<3; l++)
{
F(o, l) = muz * phi_k(k) * dE_ijk(i,j,l) + v(l);
}
}
}
}
// Family III
if (z < 1.0 && p >= 2)
{
phi_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
mu01(z, xy, 1), grad_mu01(z, xy, 1), phi_ij, dphi_ijk);
const real_t muz = mu0(z);
const Vector dmuz(grad_mu0(z));
for (int j=2; j<=p; j++)
for (int i=2; i<=p; i++, o++)
{
const int n = std::max(i,j);
const real_t nmu = n * pow(muz, n-1);
F(o, 0) = nmu * (dphi_ijk(i,j,1) * dmuz(2) -
dphi_ijk(i,j,2) * dmuz(1));
F(o, 1) = nmu * (dphi_ijk(i,j,2) * dmuz(0) -
dphi_ijk(i,j,0) * dmuz(2));
F(o, 2) = nmu * (dphi_ijk(i,j,0) * dmuz(1) -
dphi_ijk(i,j,1) * dmuz(0));
}
}
// Family IV
if (z < 1.0 && p >= 2)
{
// Re-using V_Q from Quadrilateral Face
phi_E(p, mu01(z), phi_k);
const real_t muz2 = pow(mu0(z), 2);
for (int k=2; k<=p; k++)
for (int j=0; j<p; j++)
for (int i=0; i<p; i++, o++)
for (int l=0; l<3; l++)
{
F(o, l) = muz2 * VQ_ijk(i, j, l) * phi_k(k);
}
}
// Family V
if (z < 1.0 && p >= 2)
{
V_L(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu01(z, xy, 2), grad_mu01(z, xy, 2), mu0(z), grad_mu0(z), VL_ijk);
const real_t muz = mu1(z);
for (int j=2; j<=p; j++)
for (int i=2; i<=p; i++, o++)
{
const int n = std::max(i, j);
const real_t muzi = pow(muz, n-1);
for (int l=0; l<3; l++)
{
F(o, l) = muzi * VL_ijk(i, j, l);
}
}
}
// Family VI
if (z < 1.0 && p >= 2)
{
V_R(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu1(z, xy, 2), grad_mu1(z, xy, 2), mu0(z), grad_mu0(z), VR_ij);
const real_t muz = mu1(z);
for (int i=2; i<=p; i++, o++)
{
const real_t muzi = pow(muz, i-1);
for (int l=0; l<3; l++)
{
F(o, l) = muzi * VR_ij(i, l);
}
}
}
// Family VII
if (z < 1.0 && p >= 2)
{
V_R(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
mu1(z, xy, 1), grad_mu1(z,xy,1), mu0(z), grad_mu0(z), VR_ij);
const real_t muz = mu1(z);
for (int i=2; i<=p; i++, o++)
{
const real_t muzi = pow(muz, i-1);
for (int l=0; l<3; l++)
{
F(o, l) = muzi * VR_ij(i, l);
}
}
}
}
void RT_FuentesPyramidElement::calcDivBasis(const int p,
const IntegrationPoint &ip,
Vector &phi_k,
DenseMatrix &phi_ij,
DenseMatrix &dphi_k,
DenseTensor &VQ_ijk,
DenseTensor &VT_ijk,
DenseTensor &VTT_ijk,
DenseMatrix &dVTT_ij,
DenseTensor &E_ijk,
DenseTensor &dE_ijk,
DenseTensor &dphi_ijk,
DenseTensor &VL_ijk,
DenseMatrix &VR_ij,
Vector &dF) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
real_t mu;
bool limz1 = false;
if (std::fabs(1.0 - z) < apex_tol)
{
limz1 = true;
z = 1.0 - apex_tol;
y = 0.5 * (1.0 - z);
x = 0.5 * (1.0 - z);
xy(0) = x; xy(1) = y;
}
zmax = std::max(z, zmax);
dF = 0.0;
int o = 0;
// Quadrilateral face
{
V_Q(p, mu01(z, xy, 1), mu01_grad_mu01(z, xy, 1),
mu01(z, xy, 2), mu01_grad_mu01(z, xy, 2),
VQ_ijk);
const real_t muz2 = pow(mu0(z), 2);
const Vector dmuz = grad_mu0(z);
const int o0 = o;
for (int j=0; j<p; j++)
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
dF(o) += 3.0 * muz2 * dmuz(k) * VQ_ijk(i, j, k);
}
// Overwrite lowest order quadrilateral face DoF with known limiting
// value
if (limz1)
{
dF(o0) = -3.0;
}
}
// Triangular faces
{
Vector dmuz;
// (a,b) = (1,2), c = 0
V_T(p, nu012(z, xy, 1), nu012_grad_nu012(z, xy, 1), VT_ijk);
mu = mu0(z, xy, 2);
dmuz.Destroy(); dmuz = grad_mu0(z, xy, 2);
VT_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1),
nu012_grad_nu012(z, xy, 1), grad_nu2(z, xy, 1), mu, dmuz,
VTT_ijk, dVTT_ij);
const int o1 = o;
for (int j=0; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
dF(o) = 0.5 * dVTT_ij(i, j);
for (int k=0; k<3; k++)
{
dF(o) += 0.5 * dmuz(k) * VT_ijk(i, j, k);
}
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
dmuz.Destroy(); dmuz = grad_mu1(z, xy, 2);
VT_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1),
nu012_grad_nu012(z, xy, 1), grad_nu2(z, xy, 1), mu, dmuz,
VTT_ijk, dVTT_ij);
const int o2 = o;
for (int j=0; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
dF(o) = 0.5 * dVTT_ij(i, j);
for (int k=0; k<3; k++)
{
dF(o) += 0.5 * dmuz(k) * VT_ijk(i, j, k);
}
}
// (a,b) = (2,1), c = 0
V_T(p, nu012(z, xy, 2), nu012_grad_nu012(z, xy, 2), VT_ijk);
mu = mu0(z, xy, 1);
dmuz.Destroy(); dmuz = grad_mu0(z, xy, 1);
VT_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2),
nu012_grad_nu012(z, xy, 2), grad_nu2(z, xy, 2), mu, dmuz,
VTT_ijk, dVTT_ij);
const int o3 = o;
for (int j=0; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
dF(o) = 0.5 * dVTT_ij(i, j);
for (int k=0; k<3; k++)
{
dF(o) += 0.5 * dmuz(k) * VT_ijk(i, j, k);
}
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
dmuz.Destroy(); dmuz = grad_mu1(z, xy, 1);
VT_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2),
nu012_grad_nu012(z, xy, 2), grad_nu2(z, xy, 2), mu, dmuz,
VTT_ijk, dVTT_ij);
const int o4 = o;
for (int j=0; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
dF(o) = 0.5 * dVTT_ij(i, j);
for (int k=0; k<3; k++)
{
dF(o) += 0.5 * dmuz(k) * VT_ijk(i, j, k);
}
}
// Overwrite lowest order triangular face DoFs with known limiting values
if (limz1)
{
dF(o1) = 1.5;
dF(o2) = -1.5;
dF(o3) = -1.5;
dF(o4) = 1.5;
}
}
// Interior
// Family I
if (p >= 2)
{
// Divergence is zero so skip ahead
o += (p-1) * (p-1) * p;
}
// Family II
if (p >= 2)
{
// Divergence is zero so skip ahead
o += (p-1) * (p-1) * p;
}
// Family III
if (p >= 2)
{
// Divergence is zero so skip ahead
o += (p-1) * (p-1);
}
// Family IV
if (p >= 2)
{
// Re-using V_Q from Quadrilateral Face
phi_E(p, mu01(z), grad_mu01(z), phi_k, dphi_k);
const real_t muz2 = pow(mu0(z), 2);
const Vector dmuz = grad_mu0(z);
for (int k=2; k<=p; k++)
for (int j=0; j<p; j++)
for (int i=0; i<p; i++, o++)
for (int l=0; l<3; l++)
{
dF(o) += (muz2 * dphi_k(k, l) +
2.0 * mu0(z) * phi_k(k) * dmuz(l)) * VQ_ijk(i, j, l);
}
}
// Family V
if (p >= 2)
{
V_L(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu01(z, xy, 2), grad_mu01(z, xy, 2), mu0(z), grad_mu0(z), VL_ijk);
const real_t muz = mu1(z);
const Vector dmuz = grad_mu1(z);
for (int j=2; j<=p; j++)
for (int i=2; i<=p; i++, o++)
{
const int n = std::max(i, j);
const real_t muzi = pow(muz, n-2);
for (int l=0; l<3; l++)
{
dF(o) += (n-1) * muzi * dmuz(l) * VL_ijk(i, j, l);
}
}
}
// Family VI
if (p >= 2)
{
V_R(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu1(z, xy, 2), grad_mu1(z, xy, 2), mu0(z), grad_mu0(z), VR_ij);
const real_t muz = mu1(z);
const Vector dmuz = grad_mu1(z);
for (int i=2; i<=p; i++, o++)
{
const real_t muzi = pow(muz, i-2);
for (int l=0; l<3; l++)
{
dF(o) += (i-1) * muzi * dmuz(l) * VR_ij(i, l);
}
}
}
// Family VII
if (p >= 2)
{
V_R(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
mu1(z, xy, 1), grad_mu1(z,xy,1), mu0(z), grad_mu0(z), VR_ij);
const real_t muz = mu1(z);
const Vector dmuz = grad_mu1(z);
for (int i=2; i<=p; i++, o++)
{
const real_t muzi = pow(muz, i-2);
for (int l=0; l<3; l++)
{
dF(o) += (i-1) * muzi * dmuz(l) * VR_ij(i, l);
}
}
}
}
const real_t RT_R1D_SegmentElement::nk[9] = { 1.,0.,0., 0.,1.,0., 0.,0.,1. };
RT_R1D_SegmentElement::RT_R1D_SegmentElement(const int p,
+104
View File
@@ -15,6 +15,7 @@
#include "fe_base.hpp"
#include "fe_h1.hpp"
#include "fe_l2.hpp"
#include "fe_pyramid.hpp"
namespace mfem
{
@@ -334,6 +335,109 @@ public:
{ ProjectCurl_RT(nk, dof2nk, fe, Trans, curl); }
};
/** Arbitrary order H(Div) basis functions defined on pyramid-shaped elements
This implementation is closely based on the finite elements
described in section 9.3 of the paper "Orientation embedded high
order shape functions for the exact sequence elements of all shapes"
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
*/
class RT_FuentesPyramidElement
: public VectorFiniteElement, public FuentesPyramid
{
private:
static const real_t nk[24];
mutable real_t zmax;
#ifndef MFEM_THREAD_SAFE
mutable Vector tmp1_i;
mutable DenseMatrix tmp1_ij;
mutable DenseMatrix tmp2_ij;
mutable DenseMatrix tmp3_ij;
mutable DenseMatrix tmp4_ij;
mutable DenseTensor tmp1_ijk;
mutable DenseTensor tmp2_ijk;
mutable DenseTensor tmp3_ijk;
mutable DenseTensor tmp4_ijk;
mutable DenseTensor tmp5_ijk;
mutable DenseTensor tmp6_ijk;
mutable DenseTensor tmp7_ijk;
mutable DenseMatrix u;
mutable Vector divu;
#endif
Array<int> dof2nk;
DenseMatrixInverse Ti;
void calcBasis(const int p, const IntegrationPoint &ip,
Vector &phi_k,
DenseMatrix &phi_ij,
DenseMatrix &dphi_k,
DenseTensor &VQ_ijk,
DenseTensor &VT_ijk,
DenseTensor &VTT_ijk,
DenseTensor &E_ijk, DenseTensor &dE_ijk,
DenseTensor &dphi_ijk,
DenseTensor &VL_ijk,
DenseMatrix &VR_ij,
DenseMatrix &F) const;
void calcDivBasis(const int p, const IntegrationPoint &ip,
Vector &phi_k,
DenseMatrix &phi_ij,
DenseMatrix &dphi_k,
DenseTensor &VQ_ijk,
DenseTensor &VT_ijk,
DenseTensor &VTT_ijk, DenseMatrix &dVTT_ij,
DenseTensor &E_ijk, DenseTensor &dE_ijk,
DenseTensor &dphi_ijk,
DenseTensor &VL_ijk,
DenseMatrix &VR_ij,
Vector &dF) const;
public:
RT_FuentesPyramidElement(const int p);
virtual void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const;
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_RT(Trans, shape); }
virtual void CalcDivShape(const IntegrationPoint &ip,
Vector &divshape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
DenseMatrix &I) const
{ LocalInterpolation_RT(*this, nk, dof2nk, Trans, I); }
virtual void GetLocalRestriction(ElementTransformation &Trans,
DenseMatrix &R) const
{ LocalRestriction_RT(nk, dof2nk, Trans, R); }
virtual void GetTransferMatrix(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &I) const
{ LocalInterpolation_RT(CheckVectorFE(fe), nk, dof2nk, Trans, I); }
using FiniteElement::Project;
virtual void Project(VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const
{ Project_RT(nk, dof2nk, vc, Trans, dofs); }
virtual void ProjectMatrixCoefficient(
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
{ ProjectMatrixCoefficient_RT(nk, dof2nk, mc, T, dofs); }
virtual void Project(const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const
{ Project_RT(nk, dof2nk, fe, Trans, I); }
virtual void ProjectCurl(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const
{ ProjectCurl_RT(nk, dof2nk, fe, Trans, curl); }
void CalcRawVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const;
void CalcRawDivShape(const IntegrationPoint &ip,
Vector &dshape) const;
real_t GetZetaMax() const { return zmax; }
};
/// Arbitrary order, three component, Raviart-Thomas elements in 1D on a segment
/** RT_R1D_SegmentElement provides a representation of a three component
+64 -56
View File
@@ -106,8 +106,6 @@ int FiniteElementCollection::HasFaceDofs(Geometry::Type geom, int p) const
case Geometry::CUBE:
return GetNumDof(Geometry::SQUARE, p);
case Geometry::PRISM:
return max(GetNumDof(Geometry::TRIANGLE, p),
GetNumDof(Geometry::SQUARE, p));
case Geometry::PYRAMID:
return max(GetNumDof(Geometry::TRIANGLE, p),
GetNumDof(Geometry::SQUARE, p));
@@ -310,6 +308,14 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
FiniteElement::INTEGRAL,
BasisType::GetType(name[12]));
}
else if (!strncmp(name, "RT_R1D",6))
{
fec = new RT_R1D_FECollection(atoi(name+11),atoi(name + 7));
}
else if (!strncmp(name, "RT_R2D",6))
{
fec = new RT_R2D_FECollection(atoi(name+11),atoi(name + 7));
}
else if (!strncmp(name, "RT_", 3))
{
fec = new RT_FECollection(atoi(name + 7), atoi(name + 3));
@@ -330,6 +336,14 @@ FiniteElementCollection *FiniteElementCollection::New(const char *name)
BasisType::GetType(name[9]),
BasisType::GetType(name[10]));
}
else if (!strncmp(name, "ND_R1D",6))
{
fec = new ND_R1D_FECollection(atoi(name+11),atoi(name + 7));
}
else if (!strncmp(name, "ND_R2D",6))
{
fec = new ND_R2D_FECollection(atoi(name+11),atoi(name + 7));
}
else if (!strncmp(name, "ND_", 3))
{
fec = new ND_FECollection(atoi(name + 7), atoi(name + 3));
@@ -1673,7 +1687,8 @@ const int *RT1_3DFECollection::DofOrderForOrientation(Geometry::Type GeomType,
}
H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
H1_FECollection::H1_FECollection(const int p, const int dim, const int btype,
const int pyrtype)
: FiniteElementCollection(p)
, dim(dim)
{
@@ -1874,12 +1889,24 @@ 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;
H1_dof[Geometry::PYRAMID] = 0;
if (pyrtype == 0 || b_type == BasisType::Positive)
{
H1_dof[Geometry::PYRAMID] = pm2*pm1*(2*p-3)/6; // Bergot (JSC)
}
else if (pyrtype == 1)
{
H1_dof[Geometry::PYRAMID] = pm1*pm1*pm1; // Fuentes
}
else
{
H1_dof[Geometry::PYRAMID] = (p-1)*(p-2)/2;
}
if (b_type == BasisType::Positive)
{
H1_Elements[Geometry::TETRAHEDRON] = new H1Pos_TetrahedronElement(p);
H1_Elements[Geometry::CUBE] = new H1Pos_HexahedronElement(p);
H1_Elements[Geometry::PRISM] = new H1Pos_WedgeElement(p);
H1_Elements[Geometry::PYRAMID] = new H1Pos_PyramidElement(p);
}
else
{
@@ -1887,8 +1914,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)
{
H1_Elements[Geometry::PYRAMID] = new H1_BergotPyramidElement(p, btype);
}
else
{
H1_Elements[Geometry::PYRAMID] = new H1_FuentesPyramidElement(p, btype);
}
}
H1_Elements[Geometry::PYRAMID] = new LinearPyramidFiniteElement;
const int &TetDof = H1_dof[Geometry::TETRAHEDRON];
TetDofOrd[0] = (TetDof > 0) ? new int[24*TetDof] : nullptr;
@@ -1986,17 +2020,7 @@ H1_FECollection::H1_FECollection(const int p, const int dim, const int btype)
const FiniteElement *
H1_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
{
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 1)
{
return H1_Elements[GeomType];
}
else
{
if (error_mode == RETURN_NULL) { return nullptr; }
MFEM_ABORT("H1 Pyramid basis functions are not yet supported "
"for order > 1.");
return NULL;
}
return H1_Elements[GeomType];
}
const int *H1_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
@@ -2099,7 +2123,7 @@ H1_Trace_FECollection::H1_Trace_FECollection(const int p, const int dim,
L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
const int map_type)
const int map_type, const int pyr_type)
: FiniteElementCollection(p)
, dim(dim)
, m_type(map_type)
@@ -2230,6 +2254,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
L2_Elements[Geometry::TETRAHEDRON] = new L2Pos_TetrahedronElement(p);
L2_Elements[Geometry::CUBE] = new L2Pos_HexahedronElement(p);
L2_Elements[Geometry::PRISM] = new L2Pos_WedgeElement(p);
L2_Elements[Geometry::PYRAMID] = new L2Pos_PyramidElement(p);
}
else
{
@@ -2237,13 +2262,24 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
new L2_TetrahedronElement(p, btype);
L2_Elements[Geometry::CUBE] = new L2_HexahedronElement(p, btype);
L2_Elements[Geometry::PRISM] = new L2_WedgeElement(p, btype);
if (pyr_type == 0)
{
L2_Elements[Geometry::PYRAMID] = new L2_BergotPyramidElement(p, btype);
}
else
{
L2_Elements[Geometry::PYRAMID] = new L2_FuentesPyramidElement(p, btype);
}
}
L2_Elements[Geometry::PYRAMID] = new P0PyrFiniteElement;
L2_Elements[Geometry::TETRAHEDRON]->SetMapType(map_type);
L2_Elements[Geometry::CUBE]->SetMapType(map_type);
L2_Elements[Geometry::PRISM]->SetMapType(map_type);
L2_Elements[Geometry::PYRAMID]->SetMapType(map_type);
if (b_type != BasisType::Positive)
{
L2_Elements[Geometry::PYRAMID]->SetMapType(map_type);
}
// Trace element use the default Gauss-Legendre nodal points for positive basis
if (b_type == BasisType::Positive)
{
@@ -2259,7 +2295,9 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
const int TetDof = L2_Elements[Geometry::TETRAHEDRON]->GetDof();
const int HexDof = L2_Elements[Geometry::CUBE]->GetDof();
const int PriDof = L2_Elements[Geometry::PRISM]->GetDof();
const int MaxDof = std::max(TetDof, std::max(PriDof, HexDof));
const int PyrDof = L2_Elements[Geometry::PYRAMID]->GetDof();
const int MaxDof = std::max(std::max(TetDof, PyrDof),
std::max(PriDof, HexDof));
TetDofOrd[0] = (TetDof > 0) ? new int[24*TetDof] : nullptr;
for (int i = 1; i < 24; i++)
@@ -2367,17 +2405,7 @@ L2_FECollection::L2_FECollection(const int p, const int dim, const int btype,
const FiniteElement *
L2_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
{
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 0)
{
return L2_Elements[GeomType];
}
else
{
if (error_mode == RETURN_NULL) { return nullptr; }
MFEM_ABORT("L2 Pyramid basis functions are not yet supported "
"for order > 0.");
return NULL;
}
return L2_Elements[GeomType];
}
const int *L2_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
@@ -2475,8 +2503,8 @@ RT_FECollection::RT_FECollection(const int order, const int dim,
RT_Elements[Geometry::PRISM] = new RT_WedgeElement(p);
RT_dof[Geometry::PRISM] = p*pp1*(3*p + 4)/2;
RT_Elements[Geometry::PYRAMID] = new RT0PyrFiniteElement(false);
RT_dof[Geometry::PYRAMID] = 0;
RT_Elements[Geometry::PYRAMID] = new RT_FuentesPyramidElement(p);
RT_dof[Geometry::PYRAMID] = 3*p*pp1*pp1;
}
else
{
@@ -2623,17 +2651,7 @@ void RT_FECollection::InitFaces(const int p, const int dim_,
const FiniteElement *
RT_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
{
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 1)
{
return RT_Elements[GeomType];
}
else
{
if (error_mode == RETURN_NULL) { return nullptr; }
MFEM_ABORT("RT Pyramid basis functions are not yet supported "
"for order > 0.");
return NULL;
}
return RT_Elements[GeomType];
}
const int *RT_FECollection::DofOrderForOrientation(Geometry::Type GeomType,
@@ -2901,25 +2919,15 @@ ND_FECollection::ND_FECollection(const int p, const int dim,
ND_Elements[Geometry::PRISM] = new ND_WedgeElement(p);
ND_dof[Geometry::PRISM] = p*pm1*(3*p-4)/2;
ND_Elements[Geometry::PYRAMID] = new Nedelec1PyrFiniteElement;
ND_dof[Geometry::PYRAMID] = 0;
ND_Elements[Geometry::PYRAMID] = new ND_FuentesPyramidElement(p);
ND_dof[Geometry::PYRAMID] = 3*p*pm1*pm1;
}
}
const FiniteElement *
ND_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
{
if (GeomType != Geometry::PYRAMID || this->GetOrder() == 1)
{
return ND_Elements[GeomType];
}
else
{
if (error_mode == RETURN_NULL) { return nullptr; }
MFEM_ABORT("ND Pyramid basis functions are not yet supported "
"for order > 1.");
return NULL;
}
return ND_Elements[GeomType];
}
const StatelessDofTransformation *
+8 -2
View File
@@ -119,12 +119,16 @@ public:
| ND@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | Nedelec vector elements |
| ND_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
| ND_R1D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
| ND_R2D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
| RT_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | Raviart-Thomas vector elements |
| RT@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | Raviart-Thomas vector elements |
| RT_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_ValTrace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_R1D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
| RT_R2D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
| L2_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
| L2Int_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
@@ -278,7 +282,8 @@ protected:
public:
explicit H1_FECollection(const int p, const int dim = 3,
const int btype = BasisType::GaussLobatto);
const int btype = BasisType::GaussLobatto,
const int pyrtype = 1);
const FiniteElement *
FiniteElementForGeometry(Geometry::Type GeomType) const override;
@@ -354,7 +359,8 @@ private:
public:
L2_FECollection(const int p, const int dim,
const int btype = BasisType::GaussLegendre,
const int map_type = FiniteElement::VALUE);
const int map_type = FiniteElement::VALUE,
const int pyrtype = 1);
const FiniteElement *
FiniteElementForGeometry(Geometry::Type GeomType) const override;
+568 -128
View File
File diff suppressed because it is too large Load Diff
+138 -31
View File
@@ -78,7 +78,6 @@ Ordering::Map<Ordering::byVDIM>(int ndofs, int vdim, int dof, int vd)
return (dof >= 0) ? vd+vdim*dof : -1-(vd+vdim*(-1-dof));
}
/// Constants describing the possible orderings of the DOFs in one element.
enum class ElementDofOrdering
{
@@ -87,17 +86,33 @@ enum class ElementDofOrdering
interpolation from the DOFs to quadrature points does not use the
tensor-product structure. */
NATIVE,
/// Lexicographic ordering for tensor-product FiniteElements.
/** This ordering can be used only with tensor-product elements. */
/// Lexicographic: DOFs are listed in order of increasing x-coordinate,
/// followed by increasing y-coordinate, and z-coordinate.
/** This ordering is usually used with tensor-product elements, but it is
also supported by some non-tensor elements. */
LEXICOGRAPHIC
};
/** Represents the index of an element to p-refine, plus a change to the order
of that element. */
struct pRefinement
{
int index; ///< Mesh element number
int delta; ///< Change to element order
pRefinement() = default;
pRefinement(int element, int change)
: index(element), delta(change) {}
};
// Forward declarations
class NURBSExtension;
class BilinearFormIntegrator;
class QuadratureSpace;
class QuadratureInterpolator;
class FaceQuadratureInterpolator;
class PRefinementTransferOperator;
/** @brief Class FiniteElementSpace - responsible for providing FEM view of the
@@ -250,22 +265,44 @@ protected:
/// Number of degrees of freedom. Number of unknowns is #ndofs * #vdim.
int ndofs;
bool variableOrder = false;
/** Polynomial order for each element. If empty, all elements are assumed
to be of the default order (fec->GetOrder()). */
Array<char> elem_order;
int nvdofs, nedofs, nfdofs, nbdofs;
int nvdofs, nedofs, nfdofs, nbdofs, lnedofs, lnfdofs;
int uni_fdof; ///< # of single face DOFs if all faces uniform; -1 otherwise
int *bdofs; ///< internal DOFs of elements if mixed/var-order; NULL otherwise
/** Variable order spaces only: DOF assignments for edges and faces, see
/** Variable-order spaces only: DOF assignments for edges and faces, see
docs in MakeDofTable. For constant order spaces the tables are empty. */
Table var_edge_dofs;
Table var_face_dofs; ///< NOTE: also used for spaces with mixed faces
// Temporary data for condensing all DOFs to local DOFs.
Table loc_var_edge_dofs, loc_var_face_dofs;
/** Map from all DOFs of all orders on each entity to local DOFs of orders
occurring on a local element containing the entity. */
Array<int> all2local;
/// Bit-mask representing a set of orders needed by an edge/face.
typedef std::uint64_t VarOrderBits;
static constexpr int MaxVarOrder = 8*sizeof(VarOrderBits) - 1;
/** Additional data for the var_*_dofs tables: individual variant orders
(these are basically alternate J arrays for var_edge/face_dofs). */
Array<char> var_edge_orders, var_face_orders;
Array<char> loc_var_edge_orders, loc_var_face_orders;
Array<char> ghost_edge_orders, ghost_face_orders;
/// Minimum order among neighboring elements.
mutable Array<int> edge_min_nghb_order, face_min_nghb_order;
/// Marker arrays for ghost master entities to be skipped in conforming
/// interpolation constraints.
Array<bool> skip_edge, skip_face;
// precalculated DOFs for each element, boundary element, and face
mutable Table *elem_dof; // owned (except in NURBS FE space)
@@ -302,9 +339,18 @@ protected:
/// Operator computing the action of the transpose of the restriction.
mutable std::unique_ptr<Operator> R_transpose;
/** Stores the previous FiniteElementSpace, before p-refinement, in the case
that @a PTh is constructed by PRefineAndUpdate(). */
std::unique_ptr<FiniteElementSpace> fesPrev;
/// Transformation to apply to GridFunctions after space Update().
OperatorHandle Th;
std::shared_ptr<PRefinementTransferOperator> PTh;
/// Flag to indicate whether the last update was for p-refinement.
bool lastUpdatePRef = false;
/// The element restriction operators, see GetElementRestriction().
mutable OperatorHandle L2E_nat, L2E_lex;
/// The face restriction operators, see GetFaceRestriction().
@@ -341,7 +387,10 @@ protected:
void UpdateNURBS();
/** Helper function for constructing the data in this class, for initial
construction or updates (e.g. h- or p-refinement). */
void Construct();
void Destroy();
void ConstructDoFTransArray();
@@ -351,6 +400,12 @@ protected:
void BuildBdrElementToDofTable() const;
void BuildFaceToDofTable() const;
/** Get all @a edges and @a faces (in 3D) on boundary elements with attribute
marked as essential in @a bdr_attr_is_ess. */
void GetEssentialBdrEdgesFaces(const Array<int> &bdr_attr_is_ess,
std::set<int> & edges,
std::set<int> & faces) const;
/** @brief Initialize internal data that enables the use of the methods
GetElementForDof() and GetLocalDofForDof(). */
void BuildDofToArrays_() const;
@@ -365,9 +420,8 @@ protected:
boundary. */
void BuildNURBSFaceToDofTable() const;
/// Bit-mask representing a set of orders needed by an edge/face.
typedef std::uint64_t VarOrderBits;
static constexpr int MaxVarOrder = 8*sizeof(VarOrderBits) - 1;
/// Sets @a all2local. See documentation of @a all2local for details.
void SetVarOrderLocalDofs();
/// Return the minimum order (least significant bit set) in the bit mask.
static int MinOrder(VarOrderBits bits);
@@ -375,20 +429,52 @@ protected:
/// Return element order: internal version of GetElementOrder without checks.
int GetElementOrderImpl(int i) const;
/** In a variable order space, calculate a bitmask of polynomial orders that
need to be represented on each edge and face. */
void CalcEdgeFaceVarOrders(Array<VarOrderBits> &edge_orders,
Array<VarOrderBits> &face_orders) const;
/// Returns true if the space is H1 and has variable-order elements.
bool IsVariableOrderH1() const
{
return variableOrder &&
dynamic_cast<const H1_FECollection*>(fec);
}
/** Build the table var_edge_dofs (or var_face_dofs) in a variable order
space; return total edge/face DOFs. */
int MakeDofTable(int ent_dim, const Array<int> &entity_orders,
/** In a variable-order space, calculate a bitmask of polynomial orders that
need to be represented on each edge and face. */
void CalcEdgeFaceVarOrders(
Array<VarOrderBits> &edge_orders, Array<VarOrderBits> &face_orders,
Array<VarOrderBits> &edge_elem_orders,
Array<VarOrderBits> &face_elem_orders,
Array<bool> &skip_edges, Array<bool> &skip_faces) const;
/// Helper function for ParFiniteElementSpace.
virtual void ApplyGhostElementOrdersToEdgesAndFaces(
Array<VarOrderBits> &edge_orders, Array<VarOrderBits> &face_orders) const;
/// Helper function for ParFiniteElementSpace.
virtual void GhostFaceOrderToEdges(
const Array<VarOrderBits> &face_orders,
Array<VarOrderBits> &edge_orders) const { }
/// Returns true if order propagation is done, for variable-order spaces.
virtual bool OrderPropagation(const std::set<int> &edges,
const std::set<int> &faces,
Array<VarOrderBits> &edge_orders,
Array<VarOrderBits> &face_orders) const
{ return edges.size() == 0 && faces.size() == 0; };
/// Returns the number of ghost edges (nonzero in ParFiniteElementSpace).
virtual int NumGhostEdges() const { return 0; }
/// Returns the number of ghost faces (nonzero in ParFiniteElementSpace).
virtual int NumGhostFaces() const { return 0; }
/** Build the table var_edge_dofs (or var_face_dofs) in a variable-order
space; return total edge/face DOFs. */
int MakeDofTable(int ent_dim, const Array<VarOrderBits> &entity_orders,
Table &entity_dofs, Array<char> *var_ent_order);
/// Search row of a DOF table for a DOF set of size 'ndof', return first DOF.
int FindDofs(const Table &var_dof_table, int row, int ndof) const;
/** In a variable order space, return edge DOFs associated with a polynomial
/** In a variable-order space, return edge DOFs associated with a polynomial
order that has 'ndof' degrees of freedom. */
int FindEdgeDof(int edge, int ndof) const
{ return FindDofs(var_edge_dofs, edge, ndof); }
@@ -421,6 +507,11 @@ protected:
/// Calculate the cP and cR matrices for a nonconforming mesh.
void BuildConformingInterpolation() const;
/** In variable-order spaces, enforce the minimum order rule on edges and
faces, by adding constraints to @a deps for high-order DOFs to
interpolate the lowest-order DOFs per mesh entity. */
void VariableOrderMinimumRule(SparseMatrix & deps) const;
static void AddDependencies(SparseMatrix& deps, Array<int>& master_dofs,
Array<int>& slave_dofs, DenseMatrix& I,
int skipfirst = 0);
@@ -571,8 +662,7 @@ public:
FiniteElementSpace(Mesh *mesh,
const FiniteElementCollection *fec,
int vdim = 1, int ordering = Ordering::byNODES)
{ Constructor(mesh, NULL, fec, vdim, ordering); }
int vdim = 1, int ordering = Ordering::byNODES);
/// Construct a NURBS FE space based on the given NURBSExtension, @a ext.
/** @note If the pointer @a ext is NULL, this constructor is equivalent to
@@ -580,8 +670,7 @@ public:
NURBSExtension, @a ext. */
FiniteElementSpace(Mesh *mesh, NURBSExtension *ext,
const FiniteElementCollection *fec,
int vdim = 1, int ordering = Ordering::byNODES)
{ Constructor(mesh, ext, fec, vdim, ordering); }
int vdim = 1, int ordering = Ordering::byNODES);
/// Copy assignment not supported
FiniteElementSpace& operator=(const FiniteElementSpace&) = delete;
@@ -606,18 +695,18 @@ public:
/// Sets the order of the i'th finite element.
/** By default, all elements are assumed to be of fec->GetOrder(). Once
SetElementOrder is called, the space becomes a variable order space. */
SetElementOrder is called, the space becomes a variable-order space. */
void SetElementOrder(int i, int p);
/// Returns the order of the i'th finite element.
int GetElementOrder(int i) const;
/// Return the maximum polynomial order.
int GetMaxElementOrder() const
/// Return the maximum polynomial order over all elements.
virtual int GetMaxElementOrder() const
{ return IsVariableOrder() ? elem_order.Max() : fec->GetOrder(); }
/// Returns true if the space contains elements of varying polynomial orders.
bool IsVariableOrder() const { return elem_order.Size(); }
bool IsVariableOrder() const { return variableOrder; }
/// The returned SparseMatrix is owned by the FiniteElementSpace. The method
/// returns nullptr if the matrix is identity.
@@ -739,7 +828,7 @@ public:
/** NOTE: it is recommended to use GetElementOrder in new code. */
int GetOrder(int i) const { return GetElementOrder(i); }
/** Return the order of an edge. In a variable order space, return the order
/** Return the order of an edge. In a variable-order space, return the order
of a specific variant, or -1 if there are no more variants. */
int GetEdgeOrder(int edge, int variant = 0) const;
@@ -906,7 +995,7 @@ public:
/// @brief Returns the indices of the degrees of freedom for the specified
/// face, including the DOFs for the edges and the vertices of the face.
///
/// In variable order spaces, multiple variants of DOFs can be returned.
/// In variable-order spaces, multiple variants of DOFs can be returned.
/// See GetEdgeDofs() for more details.
/// @return Order of the selected variant, or -1 if there are no more
/// variants.
@@ -918,7 +1007,7 @@ public:
/// @brief Returns the indices of the degrees of freedom for the specified
/// edge, including the DOFs for the vertices of the edge.
///
/// In variable order spaces, multiple sets of DOFs may exist on an edge,
/// In variable-order spaces, multiple sets of DOFs may exist on an edge,
/// corresponding to the different polynomial orders of incident elements.
/// The 'variant' parameter is the zero-based index of the desired DOF set.
/// The variants are ordered from lowest polynomial degree to the highest.
@@ -1366,17 +1455,32 @@ public:
virtual void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes,
OperatorHandle &T) const;
/** @brief Reflect changes in the mesh: update number of DOFs, etc. Also, calculate
GridFunction transformation operator (unless want_transform is false).
Safe to call multiple times, does nothing if space already up to date. */
/** @brief Reflect changes in the mesh: update number of DOFs, etc. Also,
calculate GridFunction transformation operator (unless want_transform is
false). Safe to call multiple times, does nothing if space already up to
date. */
virtual void Update(bool want_transform = true);
/** P-refine and update the space. If @a want_transfer, also maintain the old
space and a transfer operator accessible by GetPrefUpdateOperator(). */
virtual void PRefineAndUpdate(const Array<pRefinement> & refs,
bool want_transfer = true);
/** Return true iff p-refinement is supported in this space. Current support
is only for L2 or H1 spaces on purely quadrilateral or hexahedral
meshes. */
bool PRefinementSupported();
/// Get the GridFunction update operator.
const Operator* GetUpdateOperator() { Update(); return Th.Ptr(); }
/// Return the update operator in the given OperatorHandle, @a T.
void GetUpdateOperator(OperatorHandle &T) { T = Th; }
/** Returns @a PTh, the transfer operator from the previous space to the
current space, after p-refinement. */
std::shared_ptr<const PRefinementTransferOperator> GetPrefUpdateOperator();
/** @brief Set the ownership of the update operator: if set to false, the
Operator returned by GetUpdateOperator() must be deleted outside the
FiniteElementSpace. */
@@ -1399,13 +1503,16 @@ public:
GridFunction to check if it is up to date with the space. */
long GetSequence() const { return sequence; }
/// Return a flag indicating whether the last update was for p-refinement.
bool LastUpdatePRef() const { return lastUpdatePRef; }
/// Return whether or not the space is discontinuous (L2)
bool IsDGSpace() const
{
return dynamic_cast<const L2_FECollection*>(fec) != NULL;
}
/** In variable order spaces on nonconforming (NC) meshes, this function
/** In variable-order spaces on nonconforming (NC) meshes, this function
controls whether strict conformity is enforced in cases where coarse
edges/faces have higher polynomial order than their fine NC neighbors.
In the default (strict) case, the coarse side polynomial order is
+123
View File
@@ -1667,6 +1667,129 @@ RefinedGeometry *GeometryRefiner::Refine(Geometry::Type Geom, int Times,
return RG;
}
const IntegrationRule *GeometryRefiner::EdgeScan(Geometry::Type Geom,
int NPts1d)
{
IntegrationRule *res = nullptr;
NPts1d = std::max(NPts1d, 1);
std::array<int, 3> key = {Type, static_cast<int>(Geom), NPts1d};
auto iter = SGeom.find(key);
if (iter == SGeom.end())
{
// create new scan geometry
switch (Geom)
{
case Geometry::POINT:
case Geometry::SEGMENT:
{
iter = SGeom.emplace(key, new IntegrationRule(1)).first;
res = iter->second.get();
res->IntPoint(0).x = 0;
} break;
case Geometry::TRIANGLE:
case Geometry::SQUARE:
{
const real_t *cp =
poly1d.GetPoints(NPts1d - 1, BasisType::GetNodalBasis(Type));
if (cp[0] == 0)
{
// don't repeat origin
iter = SGeom.emplace(key, new IntegrationRule(2 * NPts1d - 1)).first;
res = iter->second.get();
for (int i = 0; i < NPts1d; ++i)
{
res->IntPoint(i).x = cp[i];
res->IntPoint(i).y = 0;
}
for (int i = 1; i < NPts1d; ++i)
{
res->IntPoint(i - 1 + NPts1d).x = 0;
res->IntPoint(i - 1 + NPts1d).y = cp[i];
}
}
else
{
iter = SGeom.emplace(key, new IntegrationRule(2 * NPts1d)).first;
res = iter->second.get();
for (int i = 0; i < NPts1d; ++i)
{
res->IntPoint(i).x = cp[i];
res->IntPoint(i).y = 0;
}
for (int i = 0; i < NPts1d; ++i)
{
res->IntPoint(i + NPts1d).x = 0;
res->IntPoint(i + NPts1d).y = cp[i];
}
}
} break;
case Geometry::CUBE:
case Geometry::TETRAHEDRON:
case Geometry::PYRAMID:
case Geometry::PRISM:
{
const real_t *cp =
poly1d.GetPoints(NPts1d - 1, BasisType::GetNodalBasis(Type));
if (cp[0] == 0)
{
// don't repeat origin
iter = SGeom.emplace(key, new IntegrationRule(3 * NPts1d - 2)).first;
res = iter->second.get();
for (int i = 0; i < NPts1d; ++i)
{
res->IntPoint(i).x = cp[i];
res->IntPoint(i).y = 0;
res->IntPoint(i).z = 0;
}
for (int i = 1; i < NPts1d; ++i)
{
res->IntPoint(i + NPts1d - 1).x = 0;
res->IntPoint(i + NPts1d - 1).y = cp[i];
res->IntPoint(i + NPts1d - 1).z = 0;
}
for (int i = 1; i < NPts1d; ++i)
{
res->IntPoint(i + 2 * NPts1d - 2).x = 0;
res->IntPoint(i + 2 * NPts1d - 2).y = 0;
res->IntPoint(i + 2 * NPts1d - 2).z = cp[i];
}
}
else
{
iter = SGeom.emplace(key, new IntegrationRule(3 * NPts1d)).first;
res = iter->second.get();
for (int i = 0; i < NPts1d; ++i)
{
res->IntPoint(i).x = cp[i];
res->IntPoint(i).y = 0;
res->IntPoint(i).z = 0;
}
for (int i = 0; i < NPts1d; ++i)
{
res->IntPoint(i + NPts1d).x = 0;
res->IntPoint(i + NPts1d).y = cp[i];
res->IntPoint(i + NPts1d).z = 0;
}
for (int i = 0; i < NPts1d; ++i)
{
res->IntPoint(i + 2 * NPts1d).x = 0;
res->IntPoint(i + 2 * NPts1d).y = 0;
res->IntPoint(i + 2 * NPts1d).z = cp[i];
}
}
} break;
case Geometry::INVALID:
case Geometry::NUM_GEOMETRIES:
MFEM_ABORT("Unknown type of reference element!");
}
}
else
{
res = iter->second.get();
}
return res;
}
const IntegrationRule *GeometryRefiner::RefineInterior(Geometry::Type Geom,
int Times)
{
+13
View File
@@ -15,6 +15,10 @@
#include "../config/config.hpp"
#include "../linalg/densemat.hpp"
#include "intrules.hpp"
#include "../general/hash.hpp"
#include <memory>
#include <unordered_map>
namespace mfem
{
@@ -327,7 +331,13 @@ class GeometryRefiner
{
private:
int Type; // Quadrature1D type (ClosedUniform is default)
/// Cache of RefinedGeometry for Refine
Array<RefinedGeometry *> RGeom[Geometry::NumGeom];
/// Cache of integration rules for EdgeScan
/// key: (type, geom, times)
std::unordered_map<std::array<int, 3>, std::unique_ptr<IntegrationRule>,
ArrayHasher>
SGeom;
Array<IntegrationRule *> IntPts[Geometry::NumGeom];
RefinedGeometry *FindInRGeom(Geometry::Type Geom, int Times,
@@ -344,6 +354,9 @@ public:
RefinedGeometry *Refine(Geometry::Type Geom, int Times, int ETimes = 1);
/// Get an integration rule which scans along the r/s/t=0 edges of the element.
const IntegrationRule *EdgeScan(Geometry::Type Geom, int NPts1d);
/// @note This method always uses Quadrature1D::OpenUniform points.
const IntegrationRule *RefineInterior(Geometry::Type Geom, int Times);
+77 -30
View File
@@ -15,6 +15,7 @@
#include "linearform.hpp"
#include "bilinearform.hpp"
#include "quadinterpolator.hpp"
#include "transfer.hpp"
#include "../mesh/nurbs.hpp"
#include "../general/text.hpp"
@@ -177,23 +178,50 @@ void GridFunction::Update()
}*/
fes_sequence = fes->GetSequence();
const Operator *T = fes->GetUpdateOperator();
if (T)
if (fes->LastUpdatePRef())
{
Vector old_data;
old_data.Swap(*this);
SetSize(T->Height());
UseDevice(true);
T->Mult(old_data, *this);
UpdatePRef();
}
else
{
SetSize(fes->GetVSize());
const Operator *T = fes->GetUpdateOperator();
if (T)
{
Vector old_data;
old_data.Swap(*this);
SetSize(T->Height());
UseDevice(true);
T->Mult(old_data, *this);
}
else
{
SetSize(fes->GetVSize());
}
}
if (t_vec.Size() > 0) { SetTrueVector(); }
}
void GridFunction::UpdatePRef()
{
const std::shared_ptr<const PRefinementTransferOperator> Tp =
fes->GetPrefUpdateOperator();
if (Tp)
{
Vector old_data;
old_data.Swap(*this);
MFEM_VERIFY(Tp->Width() == old_data.Size(),
"Wrong size of PRefinementTransferOperator in UpdatePRef");
SetSize(Tp->Height());
UseDevice(true);
Tp->Mult(old_data, *this);
}
else
{
MFEM_ABORT("Transfer operator undefined in GridFunction::UpdatePRef");
}
}
void GridFunction::SetSpace(FiniteElementSpace *f)
{
if (f != fes) { Destroy(); }
@@ -1161,31 +1189,30 @@ void GridFunction::GetVectorFieldValues(
Array<int> vdofs;
ElementTransformation *transf;
int d, k, n, sdim, dof;
n = ir.GetNPoints();
const int n = ir.GetNPoints();
DofTransformation * doftrans = fes->GetElementVDofs(i, vdofs);
const FiniteElement *fe = fes->GetFE(i);
dof = fe->GetDof();
sdim = fes->GetMesh()->SpaceDimension();
const int dof = fe->GetDof();
const int sdim = fes->GetMesh()->SpaceDimension();
const int vdim = std::max(sdim, fe->GetRangeDim());
// int *dofs = &vdofs[comp*dof];
transf = fes->GetElementTransformation(i);
transf->Transform(ir, tr);
vals.SetSize(n, sdim);
DenseMatrix vshape(dof, sdim);
Vector loc_data, val(sdim);
vals.SetSize(n, vdim);
DenseMatrix vshape(dof, vdim);
Vector loc_data, val(vdim);
GetSubVector(vdofs, loc_data);
if (doftrans)
{
doftrans->InvTransformPrimal(loc_data);
}
for (k = 0; k < n; k++)
for (int k = 0; k < n; k++)
{
const IntegrationPoint &ip = ir.IntPoint(k);
transf->SetIntPoint(&ip);
fe->CalcVShape(*transf, vshape);
vshape.MultTranspose(loc_data, val);
for (d = 0; d < sdim; d++)
for (int d = 0; d < vdim; d++)
{
vals(k,d) = val(d);
}
@@ -1254,37 +1281,33 @@ void GridFunction::ProjectVectorFieldOn(GridFunction &vec_field, int comp)
{
FiniteElementSpace *new_fes = vec_field.FESpace();
int d, i, k, ind, dof, sdim;
Array<int> overlap(new_fes->GetVSize());
Array<int> new_vdofs;
DenseMatrix vals, tr;
sdim = fes->GetMesh()->SpaceDimension();
overlap = 0;
vec_field = 0.0;
for (i = 0; i < new_fes->GetNE(); i++)
for (int i = 0; i < new_fes->GetNE(); i++)
{
const FiniteElement *fe = new_fes->GetFE(i);
const IntegrationRule &ir = fe->GetNodes();
GetVectorFieldValues(i, ir, vals, tr, comp);
new_fes->GetElementVDofs(i, new_vdofs);
dof = fe->GetDof();
for (d = 0; d < sdim; d++)
const int dof = fe->GetDof();
for (int d = 0; d < vals.Width(); d++)
{
for (k = 0; k < dof; k++)
for (int k = 0; k < dof; k++)
{
if ( (ind=new_vdofs[dof*d+k]) < 0 )
{
ind = -1-ind, vals(k, d) = - vals(k, d);
}
vec_field(ind) += vals(k, d);
real_t s;
int ind = FiniteElementSpace::DecodeDof(new_vdofs[dof*d+k], s);
vec_field(ind) += s * vals(k, d);
overlap[ind]++;
}
}
}
for (i = 0; i < overlap.Size(); i++)
for (int i = 0; i < overlap.Size(); i++)
{
vec_field(i) /= overlap[i];
}
@@ -3975,6 +3998,30 @@ void GridFunction::LegacyNCReorder()
Vector::Swap(tmp);
}
std::unique_ptr<GridFunction> GridFunction::ProlongateToMaxOrder() const
{
Mesh *mesh = fes->GetMesh();
const FiniteElementCollection *fesc = fes->FEColl();
const int vdim = fes->GetVDim();
// Find the max order in the space
int maxOrder = fes->GetMaxElementOrder();
// Create a space of maximum order over all elements for output
FiniteElementCollection *fecMax = fesc->Clone(maxOrder);
FiniteElementSpace *fesMax = new FiniteElementSpace(mesh, fecMax, vdim,
fes->GetOrdering());
GridFunction *xMax = new GridFunction(fesMax);
// Interpolate in the maximum-order space
PRefinementTransferOperator P(*fes, *fesMax);
P.Mult(*this, *xMax);
xMax->MakeOwner(fecMax);
return std::unique_ptr<GridFunction>(xMax);
}
real_t ZZErrorEstimator(BilinearFormIntegrator &blfi,
GridFunction &u,
GridFunction &flux, Vector &error_estimates,
+7
View File
@@ -433,6 +433,10 @@ public:
*/
virtual void ProjectDiscCoefficient(VectorCoefficient &coeff, AvgType type);
/** @brief Return a GridFunction with the values of this, prolongated to the
maximum order of all elements in the mesh. */
std::unique_ptr<GridFunction> ProlongateToMaxOrder() const;
protected:
/** @brief Accumulates (depending on @a type) the values of @a coeff at all
shared vdofs and counts in how many zones each vdof appears. */
@@ -463,6 +467,9 @@ protected:
// AccumulateAndCountZones().
void ComputeMeans(AvgType type, Array<int> &zones_per_vdof);
/// P-refinement version of Update().
void UpdatePRef();
public:
/** @brief For each vdof, counts how many elements contain the vdof,
as containment is determined by FiniteElementSpace::GetElementVDofs(). */
+40 -29
View File
@@ -256,7 +256,7 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
(mesh->GetElementType(0)==Element::QUADRILATERAL ||
mesh->GetElementType(0) == Element::HEXAHEDRON));
#ifdef MFEM_USE_MPI
MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MPI_C_BOOL,
MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MFEM_MPI_CXX_BOOL,
MPI_LAND, gsl_comm->c);
#endif
@@ -489,33 +489,34 @@ void FindPointsGSLIB::SetupDevice()
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
int point_pos_ordering)
{
if (!DEV.setup_device) { SetupDevice(); }
if (!DEV.setup_device)
{
SetupDevice();
}
DEV.find_device = true;
const int id = gsl_comm->id,
np = gsl_comm->np;
const int id = gsl_comm->id, np = gsl_comm->np;
gsl_mfem_ref.SetSize(points_cnt*dim);
gsl_mfem_ref.SetSize(points_cnt * dim);
gsl_mfem_elem.SetSize(points_cnt);
gsl_ref.UseDevice(true);
gsl_dist.UseDevice(true);
// Initialize arrays for all points (gsl_code is set to not found on device)
gsl_ref = -1.0;
gsl_mfem_ref = 0.0;
gsl_elem = 0;
gsl_ref = -1.0;
gsl_mfem_ref = 0.0;
gsl_elem = 0;
gsl_mfem_elem = 0;
gsl_proc = id;
gsl_proc = id;
if (dim == 2)
{
FindPointsLocal2(point_pos, point_pos_ordering,
gsl_code, gsl_elem, gsl_ref, gsl_dist, points_cnt);
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
gsl_dist, points_cnt);
}
else
{
FindPointsLocal3(point_pos, point_pos_ordering,
gsl_code, gsl_elem, gsl_ref, gsl_dist, points_cnt);
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
gsl_dist, points_cnt);
}
// Sync from device to host
@@ -535,28 +536,32 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
// and gsl_code using element type, gsl_mfem_ref, and gsl_dist.
for (int index = 0; index < points_cnt; index++)
{
if (gsl_code[index] == CODE_NOT_FOUND) { continue; }
if (gsl_code[index] == CODE_NOT_FOUND)
{
continue;
}
gsl_mfem_elem[index] = gsl_elem[index];
for (int d = 0; d < dim; d++)
{
gsl_mfem_ref(index*dim + d) = 0.5*(gsl_ref(index*dim + d)+1.0);
gsl_mfem_ref(index * dim + d) = 0.5 * (gsl_ref(index * dim + d) + 1.0);
}
IntegrationPoint ip;
if (dim == 2)
{
ip.Set2(gsl_mfem_ref.GetData() + index*dim);
ip.Set2(gsl_mfem_ref.GetData() + index * dim);
}
else if (dim == 3)
{
ip.Set3(gsl_mfem_ref.GetData() + index*dim);
ip.Set3(gsl_mfem_ref.GetData() + index * dim);
}
const int elem = gsl_elem[index];
const FiniteElement *fe = mesh->GetNodalFESpace()->GetFE(elem);
const Geometry::Type gt = fe->GetGeomType(); // assumes quad/hex
int setcode = Geometry::CheckPoint(gt, ip, -rbtol) ?
CODE_INTERNAL : CODE_BORDER;
gsl_code[index] = setcode==CODE_BORDER && gsl_dist(index)>bdr_tol ?
CODE_NOT_FOUND : setcode;
int setcode =
Geometry::CheckPoint(gt, ip, -rbtol) ? CODE_INTERNAL : CODE_BORDER;
gsl_code[index] = setcode == CODE_BORDER && gsl_dist(index) > bdr_tol
? CODE_NOT_FOUND
: setcode;
}
return;
}
@@ -709,13 +714,13 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
if (dim == 2)
{
FindPointsLocal2(point_pos_l, point_pos_ordering,
gsl_code_l, gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
FindPointsLocal2(point_pos_l, point_pos_ordering, gsl_code_l,
gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
}
else
{
FindPointsLocal3(point_pos_l, point_pos_ordering,
gsl_code_l, gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
FindPointsLocal3(point_pos_l, point_pos_ordering, gsl_code_l,
gsl_elem_l, gsl_ref_l, gsl_dist_l, n);
}
gsl_ref_l.HostRead();
@@ -1473,12 +1478,18 @@ void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
else if (gt == Geometry::SQUARE)
{
ir_split_temp = ir_split[1];
el_to_split = gf_in->FESpace()->IsVariableOrder();
// "split" if input mesh is not a tensor basis or has mixed order
el_to_split =
gf_in->FESpace()->IsVariableOrder() ||
dynamic_cast<const TensorBasisElement *>(fes->GetFE(e)) == nullptr;
}
else if (gt == Geometry::CUBE)
{
ir_split_temp = ir_split[0];
el_to_split = gf_in->FESpace()->IsVariableOrder();
// "split" if input mesh is not a tensor basis or has mixed order
el_to_split =
gf_in->FESpace()->IsVariableOrder() ||
dynamic_cast<const TensorBasisElement *>(fes->GetFE(e)) == nullptr;
}
else
{
@@ -1722,7 +1733,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
(mesh->GetElementType(0)==Element::QUADRILATERAL ||
mesh->GetElementType(0) == Element::HEXAHEDRON));
#ifdef MFEM_USE_MPI
MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MPI_C_BOOL,
MPI_Allreduce(MPI_IN_PLACE, &tensor_product_only, 1, MFEM_MPI_CXX_BOOL,
MPI_LAND, gsl_comm->c);
#endif
+10 -16
View File
@@ -94,7 +94,7 @@ protected:
// Tolerance to ignore points found beyond the mesh boundary.
// i.e. if ||x*-x(r)||_2^2 > bdr_tol, we mark point as not found.
double bdr_tol;
// Use CPU functions for mesh/gridfunction on device for gslib1.0.7
// Use CPU functions for Mesh/GridFunction on device for gslib1.0.7
bool gpu_to_cpu_fallback = false;
// Device specific data used for FindPoints
@@ -139,22 +139,16 @@ protected:
// Device functions
// FindPoints locally on device for 3D.
void FindPointsLocal3(const Vector &point_pos,
int point_pos_ordering,
void FindPointsLocal3(const Vector &point_pos, int point_pos_ordering,
Array<unsigned int> &gsl_code_dev_l,
Array<unsigned int> &gsl_elem_dev_l,
Vector &gsl_ref_l,
Vector &gsl_dist_l,
int npt);
Array<unsigned int> &gsl_elem_dev_l, Vector &gsl_ref_l,
Vector &gsl_dist_l, int npt);
// FindPoints locally on device for 2D.
void FindPointsLocal2(const Vector &point_pos,
int point_pos_ordering,
void FindPointsLocal2(const Vector &point_pos, int point_pos_ordering,
Array<unsigned int> &gsl_code_dev_l,
Array<unsigned int> &gsl_elem_dev_l,
Vector &gsl_ref_l,
Vector &gsl_dist_l,
int npt);
Array<unsigned int> &gsl_elem_dev_l, Vector &gsl_ref_l,
Vector &gsl_dist_l, int npt);
// Interpolate on device for 3D.
void InterpolateLocal3(const Vector &field_in,
@@ -181,7 +175,7 @@ protected:
int point_pos_ordering = Ordering::byNODES);
/** Interpolation of field values at prescribed reference space positions.
@param[in] field_in_evec E-vector of gridfunction to be interpolated.
@param[in] field_in_evec E-vector of grid function to be interpolated.
Assumed ordering is NDOFSxVDIMxNEL
@param[in] nel Number of elements in the mesh.
@param[in] ncomp Number of components in the field.
@@ -244,8 +238,8 @@ public:
/// Setup FindPoints and search positions
void FindPoints(Mesh &m, const Vector &point_pos,
int point_pos_ordering = Ordering::byNODES,
const double bb_t = 0.1,
const double newt_tol = 1.0e-12, const int npt_max = 256);
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
const int npt_max = 256);
/** Interpolation of field values at prescribed reference space positions.
@param[in] field_in Function values that will be interpolated on the

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