Compare commits

...
Author SHA1 Message Date
termi-official 0fe62facef Remove problematic inline. 2023-11-17 12:08:37 +01:00
Veselin Dobrev 110bec1507 Merge pull request #3956 from mfem/projectbdr-assert-bug
Wrong assert code in ParGF::ProjectBdrCoefficient
2023-11-15 19:00:47 -08:00
Veselin Dobrev 68635cc1a8 Merge pull request #3081 from mfem/sjg/strumpack-solver-dev
Update STRUMPACK solver interface
2023-11-15 18:58:00 -08:00
Tzanio Kolev 8bb929c2ff Merge pull request #3820 from mfem/sjg/stateless-doftrans-threadsafe
Simplifications to `DofTransformation` and `StatelessDofTransformation`
2023-11-15 17:43:43 -08:00
Tzanio Kolev 4161aac03e Merge pull request #3962 from mfem/tmop-mu14-update
Update to TMOP metric - mu_14
2023-11-14 12:06:34 -08:00
Mark L. Stowell 26613eec97 Merge pull request #3524 from mfem/sjg/bilinearform-integs-marker
Complete domain integrator marker support
2023-11-14 11:46:37 -08:00
Mark L. Stowell 8a5dbbe3fb Merge branch 'master' into sjg/bilinearform-integs-marker 2023-11-14 11:13:58 -08:00
Sebastian Grimberg 4202daa1fa Revert to MPI_Init when the requested threading level does not require MPI_Init_thread 2023-11-13 08:59:04 -08:00
Tzanio Kolev 1c58d6d3d1 Merge pull request #3884 from mfem/lor-surface-meshes
Support surface meshes in batched LOR assembly
2023-11-12 10:30:57 -08:00
Tzanio Kolev b2acd235b2 Merge pull request #3685 from topazus/use-getaddrinfo
Replace deprecated gethostbyname by getaddrinfo
2023-11-12 10:28:31 -08:00
Tzanio Kolev b15d483611 Merge pull request #3968 from mfem/mish2/cmake_generated_headers
Avoid regenerating header files that haven't changed
2023-11-12 10:28:06 -08:00
Tzanio Kolev ecfab8fe9f Merge pull request #3964 from mfem/extra-semicolon-warnings-fix
Fix warnings about extra semicolons from GCC
2023-11-09 07:05:02 -08:00
Tzanio Kolev 935be38e4f Merge pull request #3966 from mfem/bugfix/rwa/reset-lazy-data-parmesh-ncref
Call ResetLazyData for ParMesh undergoing nonconforming refinement
2023-11-09 07:04:13 -08:00
Tzanio Kolev 23a78751d5 Merge pull request #3811 from mfem/bdr-1d
Unify Mesh::be_to_face
2023-11-09 07:02:22 -08:00
Will PaznerandKetan Mittal 21de36604a Fix typo (dim should have been sdim)
Co-authored-by: Ketan Mittal <mittal3@llnl.gov>
2023-11-07 14:24:30 -08:00
Sam Mish 1420fc6b63 avoid regenerating headers that haven't changed 2023-11-07 12:43:59 -08:00
Sebastian Grimberg 723a613236 Clean up unnecessary ;; 2023-11-07 09:08:19 -08:00
Tzanio Kolev 60c8f8adbf Merge pull request #3897 from mfem/hcurl-hdiv-unit-test-speedup
Simplify and speed up "Hcurl/Hdiv PA Coefficient" unit tests
2023-11-07 06:31:08 -08:00
Mark L. Stowell fd4c6892a4 Merge branch 'master' into sjg/bilinearform-integs-marker 2023-11-06 15:31:47 -08:00
Veselin Dobrev 42a506cd53 Merge pull request #3934 from mfem/nbeams/update-ginkgo
Updates for newer Ginkgo versions and improvement to Ginkgo OpenMP use
2023-11-06 15:13:26 -08:00
Robert W. Anderson d6205812cf need a ResetLazyData() when mesh is a pmesh and doing nonconforming refinement 2023-11-06 15:09:08 -08:00
Will Pazner 85484ed1fc Merge pull request #3731 from mfem/bugfix-boundary-integrator
fix a bug that occurs when interpolating values from the boundary
2023-11-06 08:54:55 -08:00
Sebastian Grimberg 0857e8e22f Merge branch 'master' into sjg/stateless-doftrans-threadsafe 2023-11-06 08:30:05 -08:00
Tzanio Kolev 79de70139c Merge pull request #3611 from mfem/hughcars/nc-ho-nedelec-tet-dev
Fix ND elements p>=2 for Parallel Nonconforming meshes with triangular faces
2023-11-06 07:27:41 -08:00
Tzanio Kolev a662e29b50 Merge branch 'master' into hughcars/nc-ho-nedelec-tet-dev 2023-11-06 07:25:25 -08:00
Tzanio Kolev 787df32cb3 Merge pull request #3870 from EdwardPalmer99/EdwardPalmer99/ReadCubit-Refactor
ReadCubit Genesis mesh importer refactoring to improve readability
2023-11-06 07:24:29 -08:00
Vladimir Z Tomov a3c51c8a70 gitignore 2023-11-05 15:47:51 -08:00
Vladimir Z Tomov 05154bad34 minor 2023-11-05 13:08:15 -08:00
Vladimir Z Tomov 8c8b52d572 fix cmake 2023-11-05 12:47:40 -08:00
Vladimir Z Tomov 718bece30c renamed check-tmop-metric to tmop-check-metric 2023-11-05 12:33:30 -08:00
Vladimir Z Tomov 3ce24ea2ba comments and testing function 2023-11-05 12:29:16 -08:00
Veselin Dobrev 62c0444cbf Fix warnings about extra ';' from GCC with '-pedantic' flag 2023-11-04 18:47:52 -07:00
Will Pazner 6594329638 Fix uninitialized be_to_face in 1D ParMesh 2023-11-04 14:07:41 -07:00
Mittal, Ketan 8ab890b7d7 remove fd option for sample run with mu14 2023-11-03 10:10:59 -07:00
Mittal, Ketan 9166d8fdcb add evalP and AssembleH for mu14 2023-11-03 10:05:17 -07:00
Will Pazner 11aa88c5e0 const correctness
Make num_codim_1 const using IIFE lambda.
2023-11-03 08:26:00 -07:00
Tzanio Kolev ef7fd16dca Merge pull request #3899 from mfem/gslib-unit-tests
Unit test for FindPointsGSLIB and a fix for points on element boundaries
2023-11-03 07:53:09 -07:00
Tzanio Kolev 722d64ef3b Merge pull request #3914 from mfem/ex5p-ex9p-minor
Minor: `ex5p`, `ex9p`
2023-11-03 07:52:14 -07:00
Will Pazner 81af422efb Replace usage of deprecated function 2023-11-02 16:14:55 -07:00
Will Pazner de0f5a1da4 Replace usage of deprecated function 2023-11-02 15:41:39 -07:00
Will Pazner ed10bed8c8 Merge remote-tracking branch 'origin/master' into bdr-1d
# Conflicts:
#	fem/qspace.cpp
2023-11-02 14:52:08 -07:00
Will Pazner c794dae53b Move Mesh::GetBdrElementFaceIndex to header file
Also improve the Doxygen comment.

Move deprecated function Mesh::GetBdrElementEdgeIndex closer to its replacement
Mesh::GetBdrElementFaceIndex.
2023-11-02 14:47:56 -07:00
Hugh Carson 164ac6fcff Change nc sample runs to -o 2, and fix potential negative index access. 2023-11-02 11:41:40 -04:00
= 2284f6864b Updated changelog. 2023-11-02 14:57:13 +00:00
Tzanio Kolev 037201e9ad Merge branch 'master' into EdwardPalmer99/ReadCubit-Refactor 2023-11-02 06:49:52 -07:00
stefanhenneking 66f8e1b45c Changing op height to be consistent with true DOF definition. 2023-11-01 20:41:19 -05:00
Hugh Carson cadbb91bd1 Match ex3 nc option to ex3p and ex13p 2023-11-01 17:34:43 -04:00
Mittal, Ketan 78a69c9cc8 remove debugging print 2023-11-01 09:26:29 -07:00
Mittal, Ketan 5ab7e7c638 minor cosmetic changes including refactoring 2023-11-01 09:23:15 -07:00
Veselin Dobrev 1aa9d39127 Some formatting edits and minor tweaks 2023-10-31 22:49:04 -07:00
Will Pazner 9a70f50e22 Update CHANGELOG 2023-10-31 15:56:32 -07:00
Will Pazner c4957f1907 Merge remote-tracking branch 'origin/master' into lor-surface-meshes 2023-10-31 15:55:59 -07:00
nbeams 1ae3aad336 Remove GKO_LEND macro definition from header; address review comments 2023-10-31 17:43:48 -04:00
Mittal, Ketan 88bb203290 Merge branch 'master' of https://github.com/mfem/mfem into gslib-unit-tests 2023-10-31 14:09:29 -07:00
Mittal, Ketan c7bce2f75b Merge branch 'gslib-unit-tests' of https://github.com/mfem/mfem into gslib-unit-tests 2023-10-31 14:09:23 -07:00
Mittal, Ketan 647d8970cc reviewer comments 2023-10-31 14:08:42 -07:00
Will Pazner 14fab74111 Merge pull request #3577 from mfem/fix-nurbs-PrintFunctions
Fix nurbs print functions [fix-nurbs-PrintFunctions]
2023-10-31 13:59:51 -07:00
nbeams adf2951518 Switch from compile-time checking of Ginkgo OpenMP status to runtime check 2023-10-31 16:16:14 -04:00
Sebastian Grimberg aa8e466e42 Address PR feedback: Rename IsEmpty -> IsIdentity and clarify code comments 2023-10-31 12:26:35 -07:00
Tzanio Kolev 4cd1a923b9 Merge pull request #3929 from mfem/hughcars/mixed-mesh-update-bugfix
[Bug] Mixed Mesh invalidation of var_face_dofs
2023-10-31 12:18:51 -07:00
Tzanio Kolev 6eb8c2d270 Merge pull request #3943 from mfem/sjg/be-fbe-ori-fix
Fix integration point transformation from boundary element to face element
2023-10-31 12:18:32 -07:00
Tzanio Kolev 50b14ca232 Merge pull request #3928 from mfem/transformed-coeff
Modify TransformedCoefficient to support std::function
2023-10-31 12:17:45 -07:00
Tzanio Kolev 01c10910bc Merge pull request #3949 from mfem/feature/askinner/hdiv-saddle-point-solver
hdiv-saddle-point-solver
2023-10-31 12:17:28 -07:00
Hugh Carson 683aa2df24 Fix bug where edge-face dofs are mistaken for face dofs 2023-10-30 16:53:48 -04:00
Hugh Carson ce3bb062dd Merge remote-tracking branch 'origin/master' into hughcars/mixed-mesh-update-bugfix 2023-10-30 12:18:51 -04:00
Hugh Carson 1faba2376a Set sample run to use tf 0.5 2023-10-30 12:18:45 -04:00
Hugh Carson cfabc24ac9 Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-10-30 09:58:19 -04:00
Vladimir Z Tomov d9937c5b82 Wrong assert code in ParGF::ProjectBdrCoefficient 2023-10-29 17:26:09 -07:00
Tzanio Kolev a75beafe1d Merge pull request #3926 from mfem/hughcars/edge-face-constraint-ntdof-bug
Fix NC tet edge-face constraint bug
2023-10-28 12:21:53 -07:00
Hugh Carson 1589d13f94 Add some more assert messages and adjust whitespace 2023-10-27 18:12:35 -04:00
Julian Andrej 163a08057c revert typedefs 2023-10-27 12:25:31 -07:00
Hugh Carson 35cd559791 white space fixes 2023-10-26 10:16:16 -04:00
Hugh Carson 8519bdc6be Add mpirun -np 4 ex15p -m ../data/fichera-mixed.mesh to sample runs 2023-10-26 10:07:25 -04:00
Hugh Carson 328d5c088a Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-10-25 13:32:01 -04:00
Hugh Carson fe3033ef0e Address PR Feedback:
- Add sample runs for ex3p and ex13p with nonconforming
- Reconcile nc parameter options for ex3p and ex13p
- Fix MFEM_ASSERT error message
- Realign comments at 80 characters
- Introduce some extra const
- Change doxygen comment to reflect all parameters as @param[in] for AddTriFaces
- Remove comment debris in test_ncmesh
2023-10-25 13:29:28 -04:00
Will Pazner 9863bc5bd6 Change Mesh::GetBdrFace to Mesh::GetBdrElementFaceIndex
Deprecate Mesh::GetBdrFace.
2023-10-24 15:13:45 -07:00
Sebastian Grimberg 026c42fc3c Add unit test 2023-10-24 13:57:12 -07:00
Sebastian Grimberg 2d83759e49 Further clarification of doxygen comments 2023-10-24 13:18:32 -07:00
Sebastian Grimberg f4fdd37735 Unify DofTransformation and VDofTransformation classes for further simplification 2023-10-24 13:11:25 -07:00
Sebastian Grimberg 568b241762 Resolve unit test failures for 1D and 2D meshes (0D and 1D boundaries) 2023-10-24 12:06:32 -07:00
Sebastian Grimberg 16c3f055ff Update docstring for 2D and 3D support 2023-10-24 10:35:25 -07:00
Sebastian Grimberg e428d22e39 Fix an error in the MFEM_ASSERT check from the prior commit 2023-10-24 10:21:32 -07:00
Sebastian Grimberg 5bba155477 Address PR comments: Add 2D support for TransformBdrElementToFace and throw error for unsupported face geometries 2023-10-24 10:18:49 -07:00
Will Pazner 823eef1d9a Deprecate Mesh::GetBdrElementEdgeIndex in favor of Mesh::GetBdrFace 2023-10-24 09:25:59 -07:00
Will Pazner 6118beb9e0 Merge remote-tracking branch 'origin/master' into bdr-1d
# Conflicts:
#	mesh/submesh/psubmesh.cpp
2023-10-24 09:21:28 -07:00
Tzanio Kolev ffc2f31b6f Merge branch 'master' into lor-surface-meshes 2023-10-24 08:56:42 -07:00
Hugh Carson c27bec417c Address PR comments:
- Remove unneeded MPI_Barriers
- Remove some comment debris
- Align some comments at 80 characters
- Fix typo Paralel -> Parallel
2023-10-24 09:49:18 -04:00
Hugh Carson 45e13dec0f Merge remote-tracking branch 'origin/master' into hughcars/edge-face-constraint-ntdof-bug 2023-10-24 09:49:05 -04:00
Aaron Skinner 2ebe366a38 Patches provided by Will Pazner, necessary for saddle point solver to work 2023-10-23 14:06:00 -07:00
Sebastian Grimberg a7f7fc94ea Fix bug in pmesh-fitting miniapp uncovered by NonlinearForm markers 2023-10-22 13:03:34 -07:00
Sebastian Grimberg 46c73c8745 Merge branch 'master' into sjg/stateless-doftrans-threadsafe 2023-10-22 12:00:11 -07:00
Sebastian Grimberg d000d212e5 Revert "ex31p with the -ams option in 2D revealed a bug for DiscreteLinearOperator::Assemble"
This reverts commit ef6074c420.
2023-10-22 11:56:01 -07:00
Sebastian Grimberg 742132da1b Merge branch 'master' into sjg/bilinearform-integs-marker 2023-10-22 11:51:45 -07:00
Sebastian Grimberg 5cec99aaf6 Address PR feedback 2023-10-20 12:07:11 -07:00
Edward Palmer 7a28243aed Merge remote-tracking branch 'origin/master' into EdwardPalmer99/ReadCubit-Refactor 2023-10-20 11:10:15 +01:00
Sebastian Grimberg 1c6430c2f5 Account for all possible boundary element to face orientations in 3D in GridFunction using be_to_bfe 2023-10-19 17:33:28 -07:00
hughcars e5ff79d23b Merge branch 'master' into hughcars/mixed-mesh-update-bugfix 2023-10-18 15:05:00 -04:00
hughcars b4b6962b66 Merge branch 'master' into hughcars/edge-face-constraint-ntdof-bug 2023-10-18 15:04:55 -04:00
Will Pazner 91cb6bee22 Merge remote-tracking branch 'origin/master' into fix-nurbs-PrintFunctions
# Conflicts:
#	CHANGELOG
2023-10-17 16:25:17 -07:00
nbeams 9fdc1462d5 Updates for newer Ginkgo versions;
add check for Ginkgo OpenMP configuration when compiling and adjust default creation of Executors
2023-10-17 18:51:06 -04:00
Julian Andrej b504bfa3ca remove const from parameters in signature 2023-10-17 11:29:50 -07:00
Hugh Carson 3b8fe18288 make style 2023-10-16 10:32:25 -04:00
Hugh Carson 8aa7b97764 Fix some -Winconsistent-missing-override 2023-10-16 10:29:26 -04:00
Hugh Carson d01640402c Fix issue where rebalancing can result in ranks moving from mixed mesh to non-mixed mesh, causing invalidation of var_face_dofs. 2023-10-12 13:59:41 -04:00
Julian Andrej 89fa720b26 modified TransformedCoefficient to support std::function 2023-10-12 10:12:50 -07:00
Hugh Carson b8376455d3 Add edge-face constraints at ghost interfaces
* Refactors NCList to use an unordered map and to avoid bit hacking.
2023-10-12 09:49:04 -04:00
Hugh Carson 778f7a9d8d Failing test 2023-10-11 16:55:24 -04:00
Hugh Carson ea12a7e892 Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-10-11 14:42:23 -04:00
Tzanio Kolev 6885a99955 Merge branch 'master' into lor-surface-meshes 2023-10-10 06:26:07 -07:00
Tzanio Kolev 4a06040b70 Merge branch 'master' into gslib-unit-tests 2023-10-10 06:25:51 -07:00
stefanhenneking 80d78f5167 minor fix 2023-10-05 14:46:49 -05:00
stefanhenneking b9b7df82a1 minor fix 2023-10-05 13:33:27 -05:00
stefanhenneking 336d51bb0a Changing operator height to be semantically consistent with true DOF definitions. 2023-10-05 13:00:57 -05:00
stefanhenneking 38618a58c9 ex5p uses BlockOperator (not BlockMatrix) 2023-10-05 12:56:17 -05:00
Sebastian Grimberg cce556551b Merge branch 'master' into sjg/stateless-doftrans-threadsafe 2023-10-03 08:05:38 -07:00
Sebastian Grimberg a71509b8ce Merge branch 'master' into sjg/strumpack-solver-dev 2023-10-03 08:05:20 -07:00
Mittal, Ketan 0aee632a11 minor comments 2023-10-01 11:20:54 -07:00
Mittal, Ketan a49d3e5217 add 1 more unit test and detect pts on boundary for all element types 2023-09-29 12:16:17 -07:00
Sebastian Grimberg a8502b5b29 Fix bug for GPU builds when using sparse direct solvers with multiple RHS 2023-09-29 07:30:09 -07:00
Sebastian Grimberg a6b8ca5654 Merge branch 'master' into sjg/strumpack-solver-dev 2023-09-29 07:28:18 -07:00
Sebastian Grimberg 771ae1fc3c Merge branch 'master' into sjg/stateless-doftrans-threadsafe 2023-09-29 07:27:38 -07:00
Mittal, Ketan 8b17779a49 make style 2023-09-28 09:47:16 -07:00
Mittal, Ketan 6c0852dda1 minor wordsmithing 2023-09-28 09:45:52 -07:00
Mittal, Ketan a363b97bcb minor 2023-09-28 09:37:51 -07:00
Mittal, Ketan e8b211bd5f add comment and fix copyright 2023-09-27 21:51:39 -07:00
Mittal, Ketan 389af71c69 minor 2023-09-27 21:40:26 -07:00
Mittal, Ketan f927c87804 resolve merge conflict and add unit test for l2 projection at element border 2023-09-27 21:35:47 -07:00
Will Pazner 49459d0fd5 Simplify and speed up "Hcurl/Hdiv PA Coefficient" unit tests 2023-09-27 10:50:06 -07:00
Sebastian Grimberg 81e5489f5e Merge branch 'master' into sjg/stateless-doftrans-threadsafe 2023-09-26 08:44:44 -07:00
Sebastian Grimberg 26841dd3e3 Merge branch 'master' into sjg/strumpack-solver-dev 2023-09-26 08:42:32 -07:00
Will Pazner e1666702d4 Add surface mesh tests to batched LOR unit tests 2023-09-20 09:45:31 -07:00
Will Pazner 6c3c25d439 Correctly handle RT metric factors in batched LOR assembly 2023-09-20 09:45:06 -07:00
Will Pazner 13bd25ea34 Support surface meshes in parallel LOR solvers miniapp 2023-09-20 09:09:11 -07:00
Will Pazner e6f54f8b92 Handle space dimension in BatchedLOR_AMS::FormCoordinateVectors 2023-09-20 09:06:55 -07:00
Will Pazner 465e5e784e Support surface meshes in LOR Solvers miniapp 2023-09-20 09:06:55 -07:00
Will Pazner 369e76bd18 Support surface meshes in batched LOR assembly 2023-09-19 19:56:45 -07:00
EdwardPalmer99 d8cbe8df22 Merge branch 'mfem:master' into EdwardPalmer99/ReadCubit-Refactor 2023-09-13 16:36:04 +01:00
Hugh Carson c069b097ac Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-09-13 11:35:10 -04:00
Edward Palmer 9a65629fe1 Updated changelog. 2023-09-13 15:34:07 +00:00
Edward Palmer c43a0694ed Minor style changes. 2023-09-13 15:06:53 +00:00
Edward Palmer 87af9d2bf9 Added a cubit namespace to mesh_readers.cpp to limit scope. 2023-09-13 14:42:55 +00:00
Edward Palmer 534eebd513 Variable renaming and improved comments for improved readability. 2023-09-13 09:41:39 +00:00
Edward Palmer 1b69afdc8b Variable renaming. 2023-09-12 15:59:37 +00:00
Edward Palmer 5f56e1cc83 Added Cubit helper methods including ReadCubitElementblocks. 2023-09-12 14:22:05 +00:00
Edward Palmer b0df436103 Added ReadCubitDimensions and ReadCubitSideSets. 2023-09-12 13:36:07 +00:00
Edward Palmer 252623ff45 Added ReadCubitNumNodesPerElement 2023-09-12 11:50:14 +00:00
Edward Palmer a4b15cc776 Added ReadCubitNumElementsInBlock method. 2023-09-12 11:36:47 +00:00
Edward Palmer 751d3431d9 Added ReadCubitNodeCoordinates method. 2023-09-12 11:17:17 +00:00
Edward Palmer 23329a8f71 Added HandleNetCDFError method. 2023-09-12 11:03:09 +00:00
Edward Palmer 7d42a94add General refactoring for clarity. 2023-09-12 10:15:38 +00:00
Edward Palmer 991d893615 Variable renaming. 2023-09-11 12:59:59 +00:00
Edward Palmer 839f3190a0 Now using a std::vector for num_side_in_ss. 2023-09-11 12:16:55 +00:00
Edward Palmer 444b8187f6 Replaced if statements with switch statement for cubit_face_type. 2023-09-11 11:02:25 +00:00
Edward Palmer 6842b88608 Replaced num_el_in_blk with a std::vector. 2023-09-11 10:47:18 +00:00
Edward Palmer 16b1a2a9dc Fixed bug with buffer size being 1 byte too short. NC_MAX_NAME does not include the null-terminating final character that is added so buffers should be NC_MAX_NAME + 1 bytes long. 2023-09-11 10:23:19 +00:00
Edward Palmer 6cff8dbcd9 Replaced misuesed constexpr, renamed some local variables. 2023-09-11 09:43:19 +00:00
Hugh Carson c7c650ef9b Remove some unneeded changes to tidy up 2023-09-07 13:10:50 -04:00
Hugh Carson 9508bac66d Fix removal of ExchangeFaceNbrNodes from NC branch 2023-09-07 11:56:48 -04:00
hughcars e818c916f1 Merge branch 'master' into hughcars/nc-ho-nedelec-tet-dev 2023-09-06 10:51:34 -04:00
Hugh Carson d3a1102f33 Make missing on face_nbr_el_ori a hard error to avoid cryptic downstream segfaults 2023-09-01 14:07:34 -04:00
Jacob Lotz 096c35163c Merge branch 'master' into fix-nurbs-PrintFunctions 2023-08-30 17:26:29 +02:00
Hugh Carson 06e2be7cd0 Delete unneeded meshes that snuck in 2023-08-29 14:30:32 -04:00
Hugh Carson b13bc88382 Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-08-29 11:35:33 -04:00
Sebastian Grimberg 56b3d254a0 Merge branch 'master' into sjg/stateless-doftrans-threadsafe 2023-08-24 17:38:51 -07:00
Sebastian Grimberg 3059cf06c7 Merge branch 'master' into sjg/stateless-doftrans-threadsafe 2023-08-17 12:20:13 -07:00
Sebastian Grimberg 61f6c8e115 Merge branch 'master' into sjg/strumpack-solver-dev 2023-08-17 12:17:40 -07:00
Hugh Carson 91561aaef1 Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-08-16 13:20:57 -04:00
Hugh Carson f3a6439117 Fix merge conflict 2023-08-14 16:05:31 -04:00
Hugh Carson fd01b3287b Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-08-14 14:40:41 -04:00
Will Pazner 9159121ff7 Bug fix in Mesh::RemoveInternalBoundaries 2023-08-09 07:48:10 -07:00
Will Pazner 80916f85a4 Set 1D be_to_face in Mesh::FinalizeTopology 2023-08-09 07:48:10 -07:00
Will Pazner e37314ff51 Remove Mesh::be_to_edge
It cannot just be marked MFEM_DEPRECATED: gcc will complain.
2023-08-09 07:48:10 -07:00
Will Pazner 4d47bb42f3 Set up be_to_face in Mesh::Make1D 2023-08-09 07:48:10 -07:00
Will Pazner eba1824ebb Document that GetBdrElementEdgeIndex is the same as GetBdrFace 2023-08-09 07:48:10 -07:00
Will Pazner 9b4c527442 Deprecate Mesh::be_to_edge
Mesh::be_to_face should be used instead for 1D, 2D and 3D.
2023-08-09 07:48:10 -07:00
Will Pazner 66258d17ba Remove 1D as special case in GenerateBoundaryElements 2023-08-09 07:48:10 -07:00
Will Pazner 5359237ffd Add boundary elements in 1D ParSubMesh 2023-08-09 07:48:10 -07:00
Will Pazner 808a8b1a18 Support Mesh::GenerateBoundaryElements for 1D meshes 2023-08-09 07:48:10 -07:00
Sebastian Grimberg db6d9df56b Style/formatting updates for fespace.cpp 2023-08-08 11:59:26 -07:00
Sebastian Grimberg 55a6d1b25a Thread-safe FiniteElementSpace::GetElementDofs and GetBdrElementDofs variants for DofTransformation, similar to Mesh::GetElementTransformation and GetBdrElementTransformation 2023-08-08 11:59:26 -07:00
Sebastian Grimberg e42c849e49 Refactor DofTransformation classes and use in FiniteElementSpace to exploit statefull/stateless objects 2023-08-08 11:59:26 -07:00
Sebastian Grimberg 935f060063 Merge branch 'master' into sjg/strumpack-solver-dev 2023-07-30 18:20:01 -07:00
Hugh Carson add10e3fc4 Updated wording in CHANGELOG 2023-07-21 11:17:13 -04:00
Hugh Carson 56eedbaf06 Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-07-20 16:59:03 -04:00
Hugh Carson a300efe718 Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-07-18 17:24:42 -04:00
Hugh Carson 6b3e60ebda Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-07-11 09:47:47 -04:00
Hugh Carson 1adead75ec Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-07-10 15:21:45 -04:00
Sebastian Grimberg 449df5ceea Merge branch 'master' into sjg/bilinearform-integs-marker 2023-07-07 08:50:28 -07:00
Hugh Carson 73aa96adbf Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-07-07 11:11:13 -04:00
Hugh Carson d193264b40 Update CHANGELOG 2023-07-07 11:11:06 -04:00
Sebastian Grimberg f5d0658223 Address PR comments 2023-07-06 08:15:48 -07:00
Sebastian Grimberg c145a0c687 And another attempt to resolve LNK2019 error 2023-07-03 08:48:10 -07:00
Sebastian Grimberg 947cb04ea4 Another attempt to resolve LNK2019 error 2023-07-03 08:24:54 -07:00
Sebastian Grimberg 075c9caea4 Attempt to resolve LNK2019 linker error on Windows 2023-07-03 07:37:15 -07:00
Sebastian Grimberg 77810e3741 Alternative approach to 15934beb to inititalize MPI with MPI_Init_thread and a level of threading which by default is configured based on MFEM's build configuration, but can be overridden at runtime by modifying Mpi::default_thread_required 2023-06-29 15:31:47 -07:00
Veselin Dobrev 15934bebb9 In the singleton class Mpi: added method Mpi::Init_thread that
performs MPI initialization using MPI_Init_thread; added method
Mpi::Init_auto that selects what MPI init mode to use based on
the configured external packages used by MFEM -- currently it
calls Mpi::Init_thread(MPI_THREAD_MULTIPLE) when using STRUMPACK
built with SLATE or PT-Scotch, otherwise it calls Mpi::Init.

In the examples that can use STRUMPACK, use Mpi::Init_auto instead
of Mpi::Init.

Fix a small unrelated issue noticed during testing: in
miniapps/multidomain/multidomain.cpp, do not open GLVis socket
connections when visualization is disabled.
2023-06-27 17:47:22 -07:00
Hugh Carson 8ba39f2976 Merge remote-tracking branch 'origin/master' into hughcars/nc-ho-nedelec-tet-dev 2023-06-27 12:16:41 -04:00
Sebastian Grimberg a2daad497d Merge branch 'master' into sjg/strumpack-solver-dev 2023-06-26 16:57:20 -07:00
Sebastian Grimberg a357c4fe42 Add warning message to mfem::Mpi::Init when MPI_Init_thread may be required instead of MPI_Init 2023-06-26 10:17:38 -07:00
hughcars a8aca346a2 Merge branch 'master' into hughcars/nc-ho-nedelec-tet-dev 2023-06-26 09:27:47 -04:00
Hugh Carson eec9f7dd86 Fix merge error 2023-06-23 18:06:42 -04:00
Hugh Carson b34d2c0d8d Remove some empty lines 2023-06-23 18:06:02 -04:00
Hugh Carson 55dbbf5dad Fix warning 2023-06-23 18:05:01 -04:00
Hugh Carson a068a86210 Change relative paths to match those from the build directory rather than the source directory 2023-06-23 18:05:01 -04:00
Hugh Carson d3d0f92b83 Remove unneeded refined mesh 2023-06-23 18:05:01 -04:00
Hugh Carson 9a2f460d17 Style fixes 2023-06-23 18:05:01 -04:00
Hugh Carson 00f7b1b9a6 Provisional tests 2023-06-23 18:05:00 -04:00
Hugh Carson 0232930c8c Adding some overrides, range loops, and some comments. Also small refactor of group and owner array building. 2023-06-23 18:02:19 -04:00
Hugh Carson a05009f387 Move the simplest prism meshes to the data folder. 2023-06-23 17:59:59 -04:00
Hugh Carson 1ac3d6e674 Adding pair of quadratic prism meshes causing issues 2023-06-23 17:59:59 -04:00
Hugh Carson 84bfa01f0f Fix for handling dof -> entity map containing entities with zero dofs (P2 tris, no dof, and P2 quads, 1 dof) 2023-06-23 17:59:59 -04:00
Hugh Carson c679e7412f Refactor test, should be easier to change in future for testing on mixed meshes 2023-06-23 17:59:59 -04:00
Hugh Carson 8b954e93ff Fix bug where Swap did not account for the face_nbr_el_ori or face_nbr_el_to_face members 2023-06-23 17:59:59 -04:00
Hugh Carson 11a6b81af2 Fix bug building structures in 2D, accessing empty pointer and add printing to abort message 2023-06-23 17:59:59 -04:00
Hugh Carson 35f1c1a4ed Fix bug where forgot to wait for all send buffers to flush 2023-06-23 17:59:59 -04:00
Hugh Carson 3e4b36d0e1 Upgrades on the test 2023-06-23 17:59:59 -04:00
Hugh Carson e9a11836bb Added in an MPI construction of face_nbr_el_ori from within the ParNCMesh class. Appears to work for Conformal, next stage testing on nonconformal 2023-06-23 17:59:59 -04:00
Hugh Carson c7100ca6e7 Miscellaneous cleanup, a few unique_ptr, some assertions etc. 2023-06-23 17:59:59 -04:00
Hugh Carson 45a1b19426 Provisional work towards computing face_nbr_el_ori and face_nbr_el_to_face for ParNCMesh 2023-06-23 17:59:59 -04:00
Hugh Carson e4c76ed57e Add test checking that ghost elements GetVectorValue evaluates to the same 2023-06-23 17:59:59 -04:00
Hugh Carson b2d011bfb6 Add the nc option to ex13p 2023-06-23 17:59:59 -04:00
Hugh Carson e1c3e701a8 Add a note about not enforcing non-conformal tet refinement 2023-06-23 17:59:59 -04:00
Hugh Carson a32f106d89 Update examples to allow marking non conforming. Can give subtly different results 2023-06-23 17:59:59 -04:00
Hugh Carson 12645a3942 Clean up to use the DofTransformation methods 2023-06-23 17:59:59 -04:00
Hugh Carson 9a3300bc3f Appear to have fixed non conforming P>=2 tets. The key is to apply an additional primal transformation data to information coming down the wire, based on the orientation of the face. Now testing 2023-06-23 17:59:59 -04:00
Hugh Carson 5dfc439f32 Refactor to use final on virtual methods and make dispatching methods inline 2023-06-23 17:59:59 -04:00
Hugh Carson 54f34f9ff1 Planning for how to apply the double face concept on NC processor boundaries 2023-06-23 17:59:59 -04:00
Hugh Carson 0bc0814235 Fix for handling dof sent with negative index 2023-06-23 17:59:59 -04:00
Hugh Carson dccb44ad35 Fixing bug where variable length arrays of length zero are being deaddressed, resulting in heap overflow and/or not failing nullptr checks 2023-06-23 17:59:59 -04:00
Sebastian Grimberg 1726be86b9 Fix unit test hang for older STRUMPACK versions 2023-06-22 11:40:00 -07:00
Sebastian Grimberg a65ee60983 Merge branch 'master' into sjg/strumpack-solver-dev 2023-06-22 11:35:54 -07:00
Sebastian Grimberg ce47a35740 Merge branch 'master' into sjg/strumpack-solver-dev 2023-06-22 10:38:53 -07:00
Sebastian Grimberg e138f366fe Merge branch 'master' into sjg/strumpack-solver-dev 2023-06-21 12:10:10 -07:00
Julian Andrej 9e0970016f fix a bug that occurs when interpolating values from the boundary 2023-06-14 12:52:12 -07:00
Tzanio Kolev c788c4787c Merge branch 'master' into use-getaddrinfo 2023-06-11 15:19:15 -07:00
topazus 1e642147ae Replace deprecated gethostbyname by getaddrinfo 2023-06-06 17:35:36 +08:00
Sebastian Grimberg ef6074c420 ex31p with the -ams option in 2D revealed a bug for DiscreteLinearOperator::Assemble 2023-05-17 16:04:31 -07:00
Jacob Lotz 9f875b317e fix .gitignore 2023-05-17 14:34:47 +02:00
Jacob Lotz eb06c855b5 Merge branch 'master' into fix-nurbs-PrintFunctions 2023-05-16 11:27:38 +02:00
Sebastian Grimberg ec729707e9 Merge branch 'master' into sjg/bilinearform-integs-marker 2023-05-14 17:15:56 -07:00
JacobLotz 31cbe9beed Update gitignore and changelog 2023-05-11 15:58:31 +02:00
JacobLotz 29ecee103b Added documentation and compiling/testing in makefile 2023-05-11 15:43:11 +02:00
Sebastian Grimberg 49ebade007 Minor added simplification missed in 052b5e0c 2023-05-05 11:30:25 -07:00
Sebastian Grimberg 76650a0d1e Merge branch 'master' into sjg/bilinearform-integs-marker 2023-05-05 10:10:48 -07:00
Sebastian Grimberg bdf529b9fe Fix unit test to work with old STRUMPACK versions prior to v7.1.2 2023-05-03 10:33:23 -07:00
Sebastian Grimberg a53acb7ce8 Merge branch 'master' into sjg/strumpack-solver-dev 2023-05-02 17:47:01 -07:00
Sebastian Grimberg 35bd5c84e9 Merge branch 'master' into sjg/bilinearform-integs-marker 2023-05-02 17:46:05 -07:00
Sebastian Grimberg ad21adbf57 Merge branch 'master' into sjg/bilinearform-integs-marker 2023-04-18 11:10:33 -07:00
Sebastian Grimberg ff5238ed5b Merge branch 'master' into sjg/strumpack-solver-dev 2023-04-18 11:09:49 -07:00
JacobLotz 5594a32678 Added MFEM_VERIFY, changed i to cnt, initialized NumOfElements as 0 2023-04-12 15:22:32 +02:00
Sebastian Grimberg 6ae0a5bb89 Fix STRUMPACK version check bug 2023-04-11 21:56:44 -07:00
Sebastian Grimberg 8dedcb1ee3 Fix test and style updates 2023-04-11 10:25:00 -07:00
Sebastian Grimberg 417837e238 Remove code for unsupported STRUMPACK < v3.0
Fix ReturnCode bug for v6.3.1 as well.
2023-04-11 10:23:49 -07:00
Sebastian Grimberg b7a0ae8f6a Merge branch 'master' into sjg/strumpack-solver-dev 2023-04-11 10:01:40 -07:00
Sebastian Grimberg b9bf7a57c1 Merge branch 'master' into sjg/bilinearform-integs-marker 2023-04-03 10:07:41 -07:00
JacobLotz 8d66b1732f Mentioned GetElements() to count elements in doc. 2023-04-03 18:39:42 +02:00
JacobLotz bbd9db5e53 More elegant implementation, some renaming 2023-04-03 17:54:32 +02:00
JacobLotz 6d5a451f09 Printing some headers in examples for clarification 2023-04-03 17:39:34 +02:00
JacobLotz 398c060d4f Add more documentation and spellcheck 2023-04-03 17:39:15 +02:00
JacobLotz 59543bd226 make style 2023-04-03 17:13:59 +02:00
JacobLotz 0d3d2954a0 Add example of fix 2023-04-03 17:13:07 +02:00
JacobLotz 3c7f68e231 Fix bug in KnotVector::PrintFunctions. The function can now also correctly print shapefunctions of knotvectors with repeated knots. 2023-04-03 17:10:17 +02:00
hughcars 3a1b7ebc51 Merge branch 'master' into sjg/strumpack-solver-dev 2023-03-30 12:44:22 -04:00
Sebastian Grimberg 8edc9eaab0 make style 2023-03-15 16:43:55 -07:00
Sebastian Grimberg 052b5e0cf2 Address PR comments 2023-03-15 16:43:02 -07:00
Sebastian Grimberg 6738a32c03 Add domain integrator markers for MixedBilinearForm, DiscreteLinearOperator, and NonlinearFormclasses 2023-03-01 10:48:18 -08:00
Sebastian Grimberg c2138c03e3 Merge branch 'master' into sjg/strumpack-solver-dev 2023-02-28 16:23:53 -08:00
Sebastian Grimberg 0b8b7d748c Merge branch 'master' into sjg/strumpack-solver-dev 2023-01-27 15:25:45 -08:00
Sebastian Grimberg 53bc105b9b Add option to enable or disable STRUMPACK GPU support 2023-01-27 15:25:41 -08:00
Sebastian Grimberg c7f42bd83c Fix deprecated Vector cast 2023-01-12 10:26:11 -08:00
Sebastian Grimberg e7fc38420d Build process fixes for STRUMPACK 2023-01-11 08:48:43 -08:00
Sebastian Grimberg 5295015a31 Add tests and update examples 2023-01-10 10:55:57 -08:00
Sebastian Grimberg 48750e304b Update STRUMPACK solver: 64-bit index support and multiple RHS
For STRUMPACK > 6.3.1, also support mixed precision solver.
2023-01-10 10:54:18 -08:00
Ketan Mittal d234f822b8 minor update for pref 2022-07-28 16:23:16 -07:00
Ketan Mittal 39570992ee Merge branch 'gslib-node-ordering' of https://github.com/mfem/mfem into gslib-unit-tests 2022-07-28 15:36:03 -07:00
Ketan Mittal c60d0934ca initial commit with test 2022-07-27 12:26:39 -07:00
123 changed files with 5775 additions and 3501 deletions
+2 -1
View File
@@ -266,6 +266,7 @@ miniapps/navier/*_output
miniapps/nurbs/nurbs_ex1
miniapps/nurbs/nurbs_ex1p
miniapps/nurbs/nurbs_ex11p
miniapps/nurbs/nurbs_printfunc
miniapps/nurbs/nurbs_patch_ex1
miniapps/nurbs/nurbs_curveint
miniapps/nurbs/refined.mesh
@@ -299,7 +300,7 @@ miniapps/tools/convert-dc
miniapps/tools/lor-transfer
miniapps/tools/plor-transfer
miniapps/tools/get-values
miniapps/tools/check-tmop-metric
miniapps/tools/tmop-check-metric
miniapps/tools/tmop-metric-magnitude
miniapps/tools/nodal-transfer
miniapps/tools/ParaView
+12
View File
@@ -11,6 +11,15 @@
Version 4.6.1 (development)
===========================
Discretization improvements
---------------------------
- Introduced support for higher order non conformal Nedelec elements on
simplices in ParMesh.
Miscellaneous
-------------
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
Version 4.6, released on September 27, 2023
===========================================
@@ -31,6 +40,7 @@ Meshing improvements
* The edge to knot map for NURBS meshes can be determined automatically. It is
no longer needed to specify this in the NURBS mesh.
* Added curve interpolation method for NURBS.
* Added new small miniapp for printing of shape functions of a KnotVector
* See miniapps/nurbs for example meshes and miniapps.
Discretization improvements
@@ -77,6 +87,8 @@ Linear and nonlinear solvers
- Added HIP support to the PETSc and SUNDIALS interfaces.
- Efficient GPU-accelerated LOR assembly now supports surface meshes.
New and updated examples and miniapps
-------------------------------------
- Added a new H(div) solver miniapp demonstrating the use of a matrix-free
+20 -2
View File
@@ -641,16 +641,34 @@ if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
foreach(Header mfem.hpp mfem-performance.hpp)
message(STATUS
"Writing substitute header --> \"${Header}\"")
file(WRITE "${PROJECT_BINARY_DIR}/${Header}"
file(WRITE "${PROJECT_BINARY_DIR}/${Header}.tmp"
"// Auto-generated file.
#define MFEM_CONFIG_FILE \"${PROJECT_BINARY_DIR}/config/_config.hpp\"
#include \"${PROJECT_SOURCE_DIR}/${Header}\"
")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_BINARY_DIR}/${Header}.tmp"
"${PROJECT_BINARY_DIR}/${Header}"
)
execute_process(COMMAND ${CMAKE_COMMAND} -E remove
"${PROJECT_BINARY_DIR}/${Header}.tmp"
)
# This version will be installed in the top include directory:
file(WRITE "${PROJECT_BINARY_DIR}/InstallHeaders/${Header}"
file(WRITE "${PROJECT_BINARY_DIR}/InstallHeaders/${Header}.tmp"
"// Auto-generated file.
#include \"mfem/${Header}\"
")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}.tmp"
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}"
)
execute_process(COMMAND ${CMAKE_COMMAND} -E remove
"${PROJECT_BINARY_DIR}/InstallHeaders/${Header}.tmp"
)
endforeach()
endif()
+1 -2
View File
@@ -659,8 +659,7 @@ The specific libraries and their options are:
requires the PT-Scotch and Scalapack libraries as well as ParMETIS, which
includes METIS 5 in its distribution. Starting with STRUMPACK v2.2.0, ParMETIS
and PT-Scotch are optional dependencies.
The support for STRUMPACK was added in MFEM v3.3.2 and it requires STRUMPACK
2.0.0 or later.
The support for STRUMPACK was added in MFEM v3.3.2.
URL: http://portal.nersc.gov/project/sparse/strumpack
Options: STRUMPACK_OPT, STRUMPACK_LIB.
Versions: STRUMPACK >= 3.0.0.
+2 -1
View File
@@ -157,7 +157,8 @@ set(STRUMPACK_DIR "${MFEM_DIR}/../STRUMPACK-build" CACHE PATH
# STRUMPACK may also depend on "OpenMP", depending on how it was compiled.
# Starting with v2.2.0 of STRUMPACK, ParMETIS and Scotch are optional.
set(STRUMPACK_REQUIRED_PACKAGES "MPI" "MPI_Fortran" "ParMETIS" "METIS"
"ScaLAPACK" "Scotch/ptscotch/ptscotcherr/scotch/scotcherr" CACHE STRING
"Scotch/ptscotch/ptscotcherr/scotch/scotcherr"
"ScaLAPACK" "LAPACK" "BLAS" CACHE STRING
"Additional packages required by STRUMPACK.")
# If the MPI package does not find all required Fortran libraries:
# set(STRUMPACK_REQUIRED_LIBRARIES "gfortran" "mpi_mpifh" CACHE STRING
+20 -5
View File
@@ -331,16 +331,30 @@ STRUMPACK_OPT = -I$(STRUMPACK_DIR)/include $(SCOTCH_OPT)
STRUMPACK_LIB = -L$(STRUMPACK_DIR)/lib -lstrumpack $(MPI_FORTRAN_LIB)\
$(SCOTCH_LIB) $(SCALAPACK_LIB)
# Ginkgo library configuration (currently not needed)
# Ginkgo library configuration
GINKGO_DIR = @MFEM_DIR@/../ginkgo/install
GINKGO_SEARCH_DIR = $(subst @MFEM_DIR@,$(MFEM_DIR),$(GINKGO_DIR))
GINKGO_BUILD_TYPE=Release
ifeq ($(MFEM_USE_GINKGO),YES)
BASE_FLAGS = -std=c++14
endif
GINKGO_OPT = -isystem $(GINKGO_DIR)/include
GINKGO_LIB_DIR = $(sort $(dir $(wildcard $(GINKGO_DIR)/lib*/libginkgo*.a $(GINKGO_DIR)/lib*/libginkgo*.so $(GINKGO_DIR)/lib*/libginkgo*.dylib $(GINKGO_DIR)/lib*/libginkgo*.dll)))
ALL_GINKGO_LIBS_DEBUG = $(notdir $(basename $(wildcard $(GINKGO_DIR)/lib*/libginkgo*d.a $(GINKGO_DIR)/lib*/libginkgo*d.so $(GINKGO_DIR)/lib*/libginkgo*d.dylib $(GINKGO_DIR)/lib*/libginkgo*d.dll)))
ALL_GINKGO_LIBS = $(notdir $(basename $(wildcard $(GINKGO_DIR)/lib*/libginkgo*.a $(GINKGO_DIR)/lib*/libginkgo*.so $(GINKGO_DIR)/lib*/libginkgo*.dylib $(GINKGO_DIR)/lib*/libginkgo*.dll)))
GINKGO_LIB_DIR = $(sort $(dir $(wildcard\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.a\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.so\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.dylib\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.dll)))
GINKGO_LINK_LIB_DIR = $(GINKGO_DIR)$(subst $(GINKGO_SEARCH_DIR),,$(GINKGO_LIB_DIR))
ALL_GINKGO_LIBS_DEBUG = $(notdir $(basename $(wildcard\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*d.a\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*d.so\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*d.dylib\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*d.dll)))
ALL_GINKGO_LIBS = $(notdir $(basename $(wildcard\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.a\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.so\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.dylib\
$(GINKGO_SEARCH_DIR)/lib*/libginkgo*.dll)))
ALL_GINKGO_LIBS_RELEASE = $(filter-out $(ALL_GINKGO_LIBS_DEBUG),$(ALL_GINKGO_LIBS))
GINKGO_LINK = $(subst libginkgo,-lginkgo,$(ALL_GINKGO_LIBS_RELEASE))
ifeq ($(GINKGO_BUILD_TYPE),Debug)
@@ -349,7 +363,8 @@ ifeq ($(GINKGO_BUILD_TYPE),Debug)
endif
else
endif
GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_LIB_DIR) -L$(GINKGO_LIB_DIR) $(GINKGO_LINK)
GINKGO_LIB = $(XLINKER)-rpath,$(GINKGO_LINK_LIB_DIR) -L$(GINKGO_LINK_LIB_DIR)\
$(GINKGO_LINK)
# AmgX library configuration
AMGX_DIR = @MFEM_DIR@/../amgx
+3 -2
View File
@@ -262,12 +262,13 @@ int main(int argc, char *argv[])
#ifdef MFEM_USE_STRUMPACK
if (sp_solver)
{
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(MPI_COMM_WORLD, argc, argv);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->DisableMatching();
strumpack->SetMatching(strumpack::MatchingJob::NONE);
strumpack->SetCompression(strumpack::CompressionType::NONE);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
precond = strumpack;
+9
View File
@@ -5,6 +5,7 @@
// Sample runs: mpirun -np 4 ex13p -m ../data/star.mesh
// mpirun -np 4 ex13p -m ../data/square-disc.mesh -o 2 -n 4
// mpirun -np 4 ex13p -m ../data/beam-tet.mesh
// mpirun -np 4 ex13p -m ../data/beam-tet.mesh -nc -o 2 -rs 1
// mpirun -np 4 ex13p -m ../data/beam-hex.mesh
// mpirun -np 4 ex13p -m ../data/escher.mesh
// mpirun -np 4 ex13p -m ../data/fichera.mesh
@@ -54,6 +55,7 @@ int main(int argc, char *argv[])
int par_ref_levels = 1;
int order = 1;
int nev = 5;
bool nc = false;
bool visualization = 1;
const char *device_config = "cpu";
@@ -69,6 +71,9 @@ int main(int argc, char *argv[])
" isoparametric space.");
args.AddOption(&nev, "-n", "--num-eigs",
"Number of desired eigenmodes.");
args.AddOption(&nc, "-nc", "--non-conforming", "-c",
"--conforming",
"Mark the mesh as nonconforming before partitioning.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -98,6 +103,10 @@ int main(int argc, char *argv[])
// and volume meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
if (nc)
{
mesh->EnsureNCMesh(true);
}
// 5. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement (2 by default, or
+1
View File
@@ -13,6 +13,7 @@
// mpirun -np 4 ex15p -m ../data/square-disc-nurbs.mesh
// mpirun -np 4 ex15p -m ../data/disc-nurbs.mesh
// mpirun -np 4 ex15p -m ../data/fichera.mesh -tf 0.5
// mpirun -np 4 ex15p -m ../data/fichera-mixed.mesh -tf 0.5
// mpirun -np 4 ex15p -m ../data/ball-nurbs.mesh -tf 0.5
// mpirun -np 4 ex15p -m ../data/mobius-strip.mesh
// mpirun -np 4 ex15p -m ../data/amr-quad.mesh
+29 -4
View File
@@ -170,6 +170,7 @@ int main(int argc, char *argv[])
bool herm_conv = true;
bool slu_solver = false;
bool mumps_solver = false;
bool strumpack_solver = false;
bool visualization = 1;
bool pa = false;
const char *device_config = "cpu";
@@ -200,6 +201,11 @@ int main(int argc, char *argv[])
#ifdef MFEM_USE_MUMPS
args.AddOption(&mumps_solver, "-mumps", "--mumps-solver", "-no-mumps",
"--no-mumps-solver", "Use the MUMPS Solver.");
#endif
#ifdef MFEM_USE_STRUMPACK
args.AddOption(&strumpack_solver, "-strumpack", "--strumpack-solver",
"-no-strumpack", "--no-strumpack-solver",
"Use the STRUMPACK Solver.");
#endif
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
@@ -209,13 +215,14 @@ int main(int argc, char *argv[])
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.Parse();
if (slu_solver && mumps_solver)
if (slu_solver + mumps_solver + strumpack_solver > 1)
{
if (myid == 0)
cout << "WARNING: Both SuperLU and MUMPS have been selected,"
<< " please choose either one." << endl
cout << "WARNING: More than one of SuperLU, MUMPS, and STRUMPACK have"
<< " been selected, please choose only one." << endl
<< " Defaulting to SuperLU." << endl;
mumps_solver = false;
strumpack_solver = false;
}
if (iprob > 4) { iprob = 4; }
@@ -474,6 +481,24 @@ int main(int argc, char *argv[])
delete A;
}
#endif
#ifdef MFEM_USE_STRUMPACK
if (!pa && strumpack_solver)
{
HypreParMatrix *A = Ah.As<ComplexHypreParMatrix>()->GetSystemMatrix();
STRUMPACKRowLocMatrix SA(*A);
STRUMPACKSolver strumpack(MPI_COMM_WORLD, argc, argv);
strumpack.SetPrintFactorStatistics(false);
strumpack.SetPrintSolveStatistics(false);
strumpack.SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack.SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack.SetMatching(strumpack::MatchingJob::NONE);
strumpack.SetCompression(strumpack::CompressionType::NONE);
strumpack.SetFromCommandLine();
strumpack.SetOperator(SA);
strumpack.Mult(B, X);
delete A;
}
#endif
#ifdef MFEM_USE_MUMPS
if (!pa && mumps_solver)
{
@@ -493,7 +518,7 @@ int main(int argc, char *argv[])
//
// In PML: 1/mu (abs(1/det(J) J^T J) Curl E, Curl F)
// + omega^2 * epsilon (abs(det(J) * (J^T J)^-1) * E, F)
if (pa || (!slu_solver && !mumps_solver))
if (pa || (!slu_solver && !mumps_solver && !strumpack_solver))
{
ConstantCoefficient absomeg(pow(omega, 2) * epsilon);
RestrictedCoefficient restr_absomeg(absomeg,attr);
+9
View File
@@ -63,6 +63,7 @@ int main(int argc, char *argv[])
int order = 1;
bool static_cond = false;
bool pa = false;
bool nc = false;
const char *device_config = "cpu";
bool visualization = 1;
@@ -77,6 +78,9 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&nc, "-nc", "--non-conforming", "-c",
"--conforming",
"Mark the mesh as nonconforming before partitioning.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -102,6 +106,11 @@ int main(int argc, char *argv[])
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
if (nc)
{
// Can set to false to use conformal refinement for simplices.
mesh->EnsureNCMesh(true);
}
// 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
+10
View File
@@ -5,6 +5,7 @@
// Sample runs: mpirun -np 4 ex3p -m ../data/star.mesh
// mpirun -np 4 ex3p -m ../data/square-disc.mesh -o 2
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh
// mpirun -np 4 ex3p -m ../data/beam-tet.mesh -nc -o 2
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh
// mpirun -np 4 ex3p -m ../data/beam-hex.mesh -o 2 -pa
// mpirun -np 4 ex3p -m ../data/escher.mesh
@@ -70,6 +71,7 @@ int main(int argc, char *argv[])
int order = 1;
bool static_cond = false;
bool pa = false;
bool nc = false;
const char *device_config = "cpu";
bool visualization = true;
#ifdef MFEM_USE_AMGX
@@ -87,6 +89,9 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&nc, "-nc", "--non-conforming", "-c",
"--conforming",
"Mark the mesh as nonconforming before partitioning.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -124,6 +129,11 @@ int main(int argc, char *argv[])
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
if (nc)
{
// Can set to false to use conformal refinement for simplices.
mesh->EnsureNCMesh(true);
}
// 5. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
+2 -1
View File
@@ -450,7 +450,8 @@ int main(int argc, char *argv[])
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_)
: TimeDependentOperator(M_.Height()), M(M_), K(K_), b(b_), z(M_.Height())
: TimeDependentOperator(M_.FESpace()->GetTrueVSize()),
M(M_), K(K_), b(b_), z(height)
{
Array<int> ess_tdof_list;
if (M.GetAssemblyLevel() == AssemblyLevel::LEGACY)
+2 -2
View File
@@ -659,9 +659,9 @@ int main(int argc, char *argv[])
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
const Vector &b_, PrecType prec_type)
: TimeDependentOperator(M_.Height()), b(b_),
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize()), b(b_),
M_solver(M_.ParFESpace()->GetComm()),
z(M_.Height())
z(height)
{
if (M_.GetAssemblyLevel()==AssemblyLevel::LEGACY)
{
+3 -2
View File
@@ -273,12 +273,13 @@ int main(int argc, char *argv[])
#ifdef MFEM_USE_STRUMPACK
if (sp_solver)
{
STRUMPACKSolver * strumpack = new STRUMPACKSolver(argc, argv, MPI_COMM_WORLD);
STRUMPACKSolver * strumpack = new STRUMPACKSolver(MPI_COMM_WORLD, argc, argv);
strumpack->SetPrintFactorStatistics(true);
strumpack->SetPrintSolveStatistics(false);
strumpack->SetKrylovSolver(strumpack::KrylovSolver::DIRECT);
strumpack->SetReorderingStrategy(strumpack::ReorderingStrategy::METIS);
strumpack->DisableMatching();
strumpack->SetMatching(strumpack::MatchingJob::NONE);
strumpack->SetCompression(strumpack::CompressionType::NONE);
strumpack->SetOperator(*Arow);
strumpack->SetFromCommandLine();
precond = strumpack;
+1 -1
View File
@@ -17,7 +17,7 @@
// finite elements (velocity u) and piecewise discontinuous
// polynomials (pressure p).
//
// The example demonstrates the use of the BlockMatrix class, as
// The example demonstrates the use of the BlockOperator class, as
// well as the collective saving of several grid functions in a
// VisIt (visit.llnl.gov) visualization format.
//
+2 -2
View File
@@ -520,10 +520,10 @@ int main(int argc, char *argv[])
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
const Vector &b_,bool M_in_lhs)
: TimeDependentOperator(M_.Height(), 0.0,
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize(), 0.0,
M_in_lhs ? TimeDependentOperator::IMPLICIT
: TimeDependentOperator::EXPLICIT),
b(b_), comm(M_.ParFESpace()->GetComm()), M_solver(comm), z(M_.Height()),
b(b_), comm(M_.ParFESpace()->GetComm()), M_solver(comm), z(height),
iJacobian(NULL), rJacobian(NULL)
{
MAlev = M_.GetAssemblyLevel();
+2 -1
View File
@@ -476,7 +476,8 @@ int main(int argc, char *argv[])
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(BilinearForm &M_, BilinearForm &K_, const Vector &b_)
: TimeDependentOperator(M_.Height()), M(M_), K(K_), b(b_), z(M_.Height())
: TimeDependentOperator(M_.FESpace()->GetTrueVSize()),
M(M_), K(K_), b(b_), z(height)
{
Array<int> ess_tdof_list;
if (M.GetAssemblyLevel() == AssemblyLevel::LEGACY)
+2 -2
View File
@@ -679,10 +679,10 @@ int main(int argc, char *argv[])
// Implementation of class FE_Evolution
FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
const Vector &b_, PrecType prec_type)
: TimeDependentOperator(M_.Height()),
: TimeDependentOperator(M_.ParFESpace()->GetTrueVSize()),
b(b_),
M_solver(M_.ParFESpace()->GetComm()),
z(M_.Height())
z(height)
{
if (M_.GetAssemblyLevel()==AssemblyLevel::LEGACY)
{
-3
View File
@@ -96,9 +96,6 @@ set(SRCS
lor/lor_ads.cpp
lor/lor_ams.cpp
lor/lor_batched.cpp
lor/lor_h1.cpp
lor/lor_nd.cpp
lor/lor_rt.cpp
multigrid.cpp
nonlinearform.cpp
nonlinearform_ext.cpp
+85 -42
View File
@@ -101,6 +101,7 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
// Copy the pointers to the integrators
domain_integs = bf->domain_integs;
domain_integs_marker = bf->domain_integs_marker;
boundary_integs = bf->boundary_integs;
boundary_integs_marker = bf->boundary_integs_marker;
@@ -433,7 +434,6 @@ void BilinearForm::Assemble(int skip_zeros)
// Element-wise integration
for (int i = 0; i < fes -> GetNE(); i++)
{
doftrans = fes->GetElementVDofs(i, vdofs);
if (element_matrices)
{
elmat_p = &(*element_matrices)(i);
@@ -441,6 +441,9 @@ void BilinearForm::Assemble(int skip_zeros)
else
{
const int elem_attr = fes->GetMesh()->GetAttribute(i);
doftrans = fes->GetElementVDofs(i, vdofs);
eltrans = fes->GetElementTransformation(i);
elmat.SetSize(0);
for (int k = 0; k < domain_integs.Size(); k++)
{
@@ -448,9 +451,8 @@ void BilinearForm::Assemble(int skip_zeros)
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
&& !domain_integs[k]->Patchwise())
{
const FiniteElement &fe = *fes->GetFE(i);
eltrans = fes->GetElementTransformation(i);
domain_integs[k]->AssembleElementMatrix(fe, *eltrans, elemmat);
domain_integs[k]->AssembleElementMatrix(*fes->GetFE(i),
*eltrans, elemmat);
if (elmat.Size() == 0)
{
elmat = elemmat;
@@ -1222,11 +1224,14 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
// Copy the pointers to the integrators
domain_integs = mbf->domain_integs;
boundary_integs = mbf->boundary_integs;
trace_face_integs = mbf->trace_face_integs;
boundary_trace_face_integs = mbf->boundary_trace_face_integs;
domain_integs_marker = mbf->domain_integs_marker;
boundary_integs = mbf->boundary_integs;
boundary_integs_marker = mbf->boundary_integs_marker;
trace_face_integs = mbf->trace_face_integs;
boundary_trace_face_integs = mbf->boundary_trace_face_integs;
boundary_trace_face_integs_marker = mbf->boundary_trace_face_integs_marker;
assembly = AssemblyLevel::LEGACY;
@@ -1349,6 +1354,14 @@ void MixedBilinearForm::GetBlocks(Array2D<SparseMatrix *> &blocks) const
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi)
{
domain_integs.Append (bfi);
domain_integs_marker.Append(NULL); // NULL marker means apply everywhere
}
void MixedBilinearForm::AddDomainIntegrator (BilinearFormIntegrator * bfi,
Array<int> &elem_marker)
{
domain_integs.Append (bfi);
domain_integs_marker.Append(&elem_marker);
}
void MixedBilinearForm::AddBoundaryIntegrator (BilinearFormIntegrator * bfi)
@@ -1383,7 +1396,7 @@ void MixedBilinearForm::AddBdrTraceFaceIntegrator(BilinearFormIntegrator *bfi,
boundary_trace_face_integs_marker.Append(&bdr_marker);
}
void MixedBilinearForm::Assemble (int skip_zeros)
void MixedBilinearForm::Assemble(int skip_zeros)
{
if (ext)
{
@@ -1405,8 +1418,20 @@ void MixedBilinearForm::Assemble (int skip_zeros)
if (domain_integs.Size())
{
for (int k = 0; k < domain_integs.Size(); k++)
{
if (domain_integs_marker[k] != NULL)
{
MFEM_VERIFY(domain_integs_marker[k]->Size() ==
(mesh->attributes.Size() ? mesh->attributes.Max() : 0),
"invalid element marker for domain integrator #"
<< k << ", counting from zero");
}
}
for (int i = 0; i < test_fes -> GetNE(); i++)
{
const int elem_attr = mesh->GetAttribute(i);
dom_dof_trans = trial_fes -> GetElementVDofs (i, trial_vdofs);
ran_dof_trans = test_fes -> GetElementVDofs (i, test_vdofs);
eltrans = test_fes -> GetElementTransformation (i);
@@ -1415,10 +1440,14 @@ void MixedBilinearForm::Assemble (int skip_zeros)
elmat = 0.0;
for (int k = 0; k < domain_integs.Size(); k++)
{
domain_integs[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
*test_fes -> GetFE(i),
*eltrans, elemmat);
elmat += elemmat;
if (domain_integs_marker[k] == NULL ||
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
{
domain_integs[k] -> AssembleElementMatrix2 (*trial_fes -> GetFE(i),
*test_fes -> GetFE(i),
*eltrans, elemmat);
elmat += elemmat;
}
}
if (ran_dof_trans || dom_dof_trans)
{
@@ -1941,41 +1970,56 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
return;
}
Array<int> dom_vdofs, ran_vdofs;
ElementTransformation *T;
ElementTransformation *eltrans;
DofTransformation * dom_dof_trans;
DofTransformation * ran_dof_trans;
const FiniteElement *dom_fe, *ran_fe;
DenseMatrix totelmat, elmat;
DenseMatrix elmat;
Mesh *mesh = test_fes->GetMesh();
if (mat == NULL)
{
mat = new SparseMatrix(height, width);
}
if (domain_integs.Size() > 0)
if (domain_integs.Size())
{
for (int k = 0; k < domain_integs.Size(); k++)
{
if (domain_integs_marker[k] != NULL)
{
MFEM_VERIFY(domain_integs_marker[k]->Size() ==
(mesh->attributes.Size() ? mesh->attributes.Max() : 0),
"invalid element marker for domain integrator #"
<< k << ", counting from zero");
}
}
for (int i = 0; i < test_fes->GetNE(); i++)
{
dom_dof_trans = trial_fes->GetElementVDofs(i, dom_vdofs);
ran_dof_trans = test_fes->GetElementVDofs(i, ran_vdofs);
T = test_fes->GetElementTransformation(i);
dom_fe = trial_fes->GetFE(i);
ran_fe = test_fes->GetFE(i);
const int elem_attr = mesh->GetAttribute(i);
dom_dof_trans = trial_fes->GetElementVDofs(i, trial_vdofs);
ran_dof_trans = test_fes->GetElementVDofs(i, test_vdofs);
eltrans = test_fes->GetElementTransformation(i);
domain_integs[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
totelmat);
for (int j = 1; j < domain_integs.Size(); j++)
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
elmat = 0.0;
for (int k = 0; k < domain_integs.Size(); k++)
{
domain_integs[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
elmat);
totelmat += elmat;
if (domain_integs_marker[k] == NULL ||
(*(domain_integs_marker[k]))[elem_attr-1] == 1)
{
domain_integs[k]->AssembleElementMatrix2(*trial_fes->GetFE(i),
*test_fes->GetFE(i),
*eltrans, elemmat);
elmat += elemmat;
}
}
if (ran_dof_trans || dom_dof_trans)
{
TransformPrimal(ran_dof_trans, dom_dof_trans, totelmat);
TransformPrimal(ran_dof_trans, dom_dof_trans, elemmat);
}
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
mat->SetSubMatrix(test_vdofs, trial_vdofs, elemmat, skip_zeros);
}
}
@@ -1984,21 +2028,20 @@ void DiscreteLinearOperator::Assemble(int skip_zeros)
const int nfaces = test_fes->GetMesh()->GetNumFaces();
for (int i = 0; i < nfaces; i++)
{
trial_fes->GetFaceVDofs(i, dom_vdofs);
test_fes->GetFaceVDofs(i, ran_vdofs);
T = test_fes->GetMesh()->GetFaceTransformation(i);
dom_fe = trial_fes->GetFaceElement(i);
ran_fe = test_fes->GetFaceElement(i);
trial_fes->GetFaceVDofs(i, trial_vdofs);
test_fes->GetFaceVDofs(i, test_vdofs);
eltrans = test_fes->GetMesh()->GetFaceTransformation(i);
trace_face_integs[0]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
totelmat);
for (int j = 1; j < trace_face_integs.Size(); j++)
elmat.SetSize(test_vdofs.Size(), trial_vdofs.Size());
elmat = 0.0;
for (int k = 0; k < trace_face_integs.Size(); k++)
{
trace_face_integs[j]->AssembleElementMatrix2(*dom_fe, *ran_fe, *T,
elmat);
totelmat += elmat;
trace_face_integs[k]->AssembleElementMatrix2(*trial_fes->GetFaceElement(i),
*test_fes->GetFaceElement(i),
*eltrans, elemmat);
elmat += elemmat;
}
mat->SetSubMatrix(ran_vdofs, dom_vdofs, totelmat, skip_zeros);
mat->SetSubMatrix(test_vdofs, trial_vdofs, elmat, skip_zeros);
}
}
}
+22 -7
View File
@@ -100,7 +100,7 @@ protected:
/// Includes all by default.
/// 0 - ignore attribute
/// 1 - include attribute
Array<Array<int>*> domain_integs_marker;
Array<Array<int>*> domain_integs_marker; ///< Entries are not owned.
/// Set of Boundary Integrators to be applied.
Array<BilinearFormIntegrator*> boundary_integs;
@@ -722,10 +722,13 @@ protected:
/// Domain integrators.
Array<BilinearFormIntegrator*> domain_integs;
/// Entries are not owned.
Array<Array<int>*> domain_integs_marker;
/// Boundary integrators.
Array<BilinearFormIntegrator*> boundary_integs;
Array<Array<int>*> boundary_integs_marker; ///< Entries are not owned.
/// Entries are not owned.
Array<Array<int>*> boundary_integs_marker;
/// Trace face (skeleton) integrators.
Array<BilinearFormIntegrator*> trace_face_integs;
@@ -805,12 +808,16 @@ public:
/// Adds a domain integrator. Assumes ownership of @a bfi.
void AddDomainIntegrator(BilinearFormIntegrator *bfi);
/// Adds a domain integrator. Assumes ownership of @a bfi.
void AddDomainIntegrator(BilinearFormIntegrator *bfi,
Array<int> &elem_marker);
/// Adds a boundary integrator. Assumes ownership of @a bfi.
void AddBoundaryIntegrator(BilinearFormIntegrator *bfi);
/// Adds a boundary integrator. Assumes ownership of @a bfi.
void AddBoundaryIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker);
void AddBoundaryIntegrator(BilinearFormIntegrator * bfi,
Array<int> &bdr_marker);
/** @brief Add a trace face integrator. Assumes ownership of @a bfi.
@@ -820,14 +827,18 @@ public:
void AddTraceFaceIntegrator(BilinearFormIntegrator *bfi);
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
void AddBdrTraceFaceIntegrator (BilinearFormIntegrator * bfi);
void AddBdrTraceFaceIntegrator(BilinearFormIntegrator * bfi);
/// Adds a boundary trace face integrator. Assumes ownership of @a bfi.
void AddBdrTraceFaceIntegrator (BilinearFormIntegrator * bfi,
Array<int> &bdr_marker);
void AddBdrTraceFaceIntegrator(BilinearFormIntegrator * bfi,
Array<int> &bdr_marker);
/// Access all integrators added with AddDomainIntegrator().
Array<BilinearFormIntegrator*> *GetDBFI() { return &domain_integs; }
/** @brief Access all domain markers added with AddDomainIntegrator().
If no marker was specified when the integrator was added, the
corresponding pointer (to Array<int>) will be NULL. */
Array<Array<int>*> *GetDBFI_Marker() { return &domain_integs_marker; }
/// Access all integrators added with AddBoundaryIntegrator().
Array<BilinearFormIntegrator*> *GetBBFI() { return &boundary_integs; }
@@ -1065,6 +1076,9 @@ public:
/// Adds a domain interpolator. Assumes ownership of @a di.
void AddDomainInterpolator(DiscreteInterpolator *di)
{ AddDomainIntegrator(di); }
void AddDomainInterpolator(DiscreteInterpolator *di,
Array<int> &elem_marker)
{ AddDomainIntegrator(di, elem_marker); }
/// Adds a trace face interpolator. Assumes ownership of @a di.
void AddTraceFaceInterpolator(DiscreteInterpolator *di)
@@ -1072,6 +1086,7 @@ public:
/// Access all interpolators added with AddDomainInterpolator().
Array<BilinearFormIntegrator*> *GetDI() { return &domain_integs; }
Array<Array<int>*> *GetDI_Marker() { return &domain_integs_marker; }
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
/** This method must be called before assembly. */
+1 -1
View File
@@ -303,7 +303,7 @@ void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
std::unordered_map<int,int> f_to_be;
for (int i = 0; i < mesh.GetNBE(); ++i)
{
const int f = mesh.GetBdrElementEdgeIndex(i);
const int f = mesh.GetBdrElementFaceIndex(i);
f_to_be[f] = i;
}
const int nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
+10 -3
View File
@@ -220,12 +220,12 @@ double TransformedCoefficient::Eval(ElementTransformation &T,
{
if (Q2)
{
return (*Transform2)(Q1->Eval(T, ip, GetTime()),
Q2->Eval(T, ip, GetTime()));
return Transform2(Q1->Eval(T, ip, GetTime()),
Q2->Eval(T, ip, GetTime()));
}
else
{
return (*Transform1)(Q1->Eval(T, ip, GetTime()));
return Transform1(Q1->Eval(T, ip, GetTime()));
}
}
@@ -1592,6 +1592,10 @@ void VectorQuadratureFunctionCoefficient::Eval(Vector &V,
QuadF.HostRead();
const int el_idx = QuadF.GetSpace()->GetEntityIndex(T);
// Handle the case of "interior boundary elements" and FaceQuadratureSpace
// with FaceType::Boundary.
if (el_idx < 0) { V = 0.0; return; }
const int ip_idx = QuadF.GetSpace()->GetPermutedIndex(el_idx, ip.index);
if (index == 0 && vdim == QuadF.GetVDim())
@@ -1629,6 +1633,9 @@ double QuadratureFunctionCoefficient::Eval(ElementTransformation &T,
QuadF.HostRead();
Vector temp(1);
const int el_idx = QuadF.GetSpace()->GetEntityIndex(T);
// Handle the case of "interior boundary elements" and FaceQuadratureSpace
// with FaceType::Boundary.
if (el_idx < 0) { return 0.0; }
const int ip_idx = QuadF.GetSpace()->GetPermutedIndex(el_idx, ip.index);
QuadF.GetValues(el_idx, ip_idx, temp);
return temp[0];
+6 -6
View File
@@ -422,15 +422,15 @@ class TransformedCoefficient : public Coefficient
private:
Coefficient * Q1;
Coefficient * Q2;
double (*Transform1)(double);
double (*Transform2)(double,double);
std::function<double(double)> Transform1;
std::function<double(double, double)> Transform2;
public:
TransformedCoefficient (Coefficient * q,double (*F)(double))
: Q1(q), Transform1(F) { Q2 = 0; Transform2 = 0; }
TransformedCoefficient (Coefficient * q, std::function<double(double)> F)
: Q1(q), Transform1(std::move(F)) { Q2 = 0; Transform2 = 0; }
TransformedCoefficient (Coefficient * q1,Coefficient * q2,
double (*F)(double,double))
: Q1(q1), Q2(q2), Transform2(F) { Transform1 = 0; }
std::function<double(double, double)> F)
: Q1(q1), Q2(q2), Transform2(std::move(F)) { Transform1 = 0; }
/// Set the time for internally stored coefficients
void SetTime(double t);
+169 -172
View File
@@ -14,175 +14,166 @@
namespace mfem
{
void DofTransformation::TransformPrimal(double *v) const
{
MFEM_ASSERT(dof_trans_,
"DofTransformation has no local transformation, call "
"SetDofTransformation first!");
int size = dof_trans_->Size();
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
{
for (int i=0; i<vdim_; i++)
{
dof_trans_->TransformPrimal(Fo_, &v[i*size]);
}
}
else
{
Vector vec(size);
for (int i=0; i<vdim_; i++)
{
for (int j=0; j<size; j++)
{
vec(j) = v[j*vdim_+i];
}
dof_trans_->TransformPrimal(Fo_, vec);
for (int j=0; j<size; j++)
{
v[j*vdim_+i] = vec(j);
}
}
}
}
void DofTransformation::InvTransformPrimal(double *v) const
{
MFEM_ASSERT(dof_trans_,
"DofTransformation has no local transformation, call "
"SetDofTransformation first!");
int size = dof_trans_->Height();
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
{
for (int i=0; i<vdim_; i++)
{
dof_trans_->InvTransformPrimal(Fo_, &v[i*size]);
}
}
else
{
Vector vec(size);
for (int i=0; i<vdim_; i++)
{
for (int j=0; j<size; j++)
{
vec(j) = v[j*vdim_+i];
}
dof_trans_->InvTransformPrimal(Fo_, vec);
for (int j=0; j<size; j++)
{
v[j*vdim_+i] = vec(j);
}
}
}
}
void DofTransformation::TransformDual(double *v) const
{
MFEM_ASSERT(dof_trans_,
"DofTransformation has no local transformation, call "
"SetDofTransformation first!");
int size = dof_trans_->Size();
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
{
for (int i=0; i<vdim_; i++)
{
dof_trans_->TransformDual(Fo_, &v[i*size]);
}
}
else
{
Vector vec(size);
for (int i=0; i<vdim_; i++)
{
for (int j=0; j<size; j++)
{
vec(j) = v[j*vdim_+i];
}
dof_trans_->TransformDual(Fo_, vec);
for (int j=0; j<size; j++)
{
v[j*vdim_+i] = vec(j);
}
}
}
}
void DofTransformation::InvTransformDual(double *v) const
{
MFEM_ASSERT(dof_trans_,
"DofTransformation has no local transformation, call "
"SetDofTransformation first!");
int size = dof_trans_->Size();
if (vdim_ == 1 || (Ordering::Type)ordering_ == Ordering::byNODES)
{
for (int i=0; i<vdim_; i++)
{
dof_trans_->InvTransformDual(Fo_, &v[i*size]);
}
}
else
{
Vector vec(size);
for (int i=0; i<vdim_; i++)
{
for (int j=0; j<size; j++)
{
vec(j) = v[j*vdim_+i];
}
dof_trans_->InvTransformDual(Fo_, vec);
for (int j=0; j<size; j++)
{
v[j*vdim_+i] = vec(j);
}
}
}
}
void TransformPrimal(const DofTransformation *ran_dof_trans,
const DofTransformation *dom_dof_trans,
DenseMatrix &elmat)
{
if (ran_dof_trans && dom_dof_trans)
{
ran_dof_trans->TransformPrimalCols(elmat);
dom_dof_trans->TransformDualRows(elmat);
}
else if (ran_dof_trans)
// No action if both transformations are NULL
if (ran_dof_trans)
{
ran_dof_trans->TransformPrimalCols(elmat);
}
else if (dom_dof_trans)
if (dom_dof_trans)
{
dom_dof_trans->TransformDualRows(elmat);
}
else
{
// If both transformations are NULL this function should not be called
}
}
void TransformDual(const DofTransformation *ran_dof_trans,
const DofTransformation *dom_dof_trans,
DenseMatrix &elmat)
{
if (ran_dof_trans && dom_dof_trans)
{
ran_dof_trans->TransformDualCols(elmat);
dom_dof_trans->TransformDualRows(elmat);
}
else if (ran_dof_trans)
// No action if both transformations are NULL
if (ran_dof_trans)
{
ran_dof_trans->TransformDualCols(elmat);
}
else if (dom_dof_trans)
if (dom_dof_trans)
{
dom_dof_trans->TransformDualRows(elmat);
}
else
{
// If both transformations are NULL this function should not be called
}
}
void StatelessVDofTransformation::TransformPrimal(const Array<int> & face_ori,
double *v) const
{
int size = sdoftrans_->Size();
if ((Ordering::Type)ordering_ == Ordering::byNODES || vdim_ == 1)
{
for (int i=0; i<vdim_; i++)
{
sdoftrans_->TransformPrimal(face_ori, &v[i*size]);
}
}
else
{
Vector vec(size);
for (int i=0; i<vdim_; i++)
{
for (int j=0; j<size; j++)
{
vec(j) = v[j*vdim_+i];
}
sdoftrans_->TransformPrimal(face_ori, vec);
for (int j=0; j<size; j++)
{
v[j*vdim_+i] = vec(j);
}
}
}
}
void StatelessVDofTransformation::InvTransformPrimal(
const Array<int> & face_ori,
double *v) const
{
int size = sdoftrans_->Height();
if ((Ordering::Type)ordering_ == Ordering::byNODES)
{
for (int i=0; i<vdim_; i++)
{
sdoftrans_->InvTransformPrimal(face_ori, &v[i*size]);
}
}
else
{
Vector vec(size);
for (int i=0; i<vdim_; i++)
{
for (int j=0; j<size; j++)
{
vec(j) = v[j*vdim_+i];
}
sdoftrans_->InvTransformPrimal(face_ori, vec);
for (int j=0; j<size; j++)
{
v[j*vdim_+i] = vec(j);
}
}
}
}
void StatelessVDofTransformation::TransformDual(const Array<int> & face_ori,
double *v) const
{
int size = sdoftrans_->Size();
if ((Ordering::Type)ordering_ == Ordering::byNODES)
{
for (int i=0; i<vdim_; i++)
{
sdoftrans_->TransformDual(face_ori, &v[i*size]);
}
}
else
{
Vector vec(size);
for (int i=0; i<vdim_; i++)
{
for (int j=0; j<size; j++)
{
vec(j) = v[j*vdim_+i];
}
sdoftrans_->TransformDual(face_ori, vec);
for (int j=0; j<size; j++)
{
v[j*vdim_+i] = vec(j);
}
}
}
}
void StatelessVDofTransformation::InvTransformDual(const Array<int> & face_ori,
double *v) const
{
int size = sdoftrans_->Size();
if ((Ordering::Type)ordering_ == Ordering::byNODES)
{
for (int i=0; i<vdim_; i++)
{
sdoftrans_->InvTransformDual(face_ori, &v[i*size]);
}
}
else
{
Vector vec(size);
for (int i=0; i<vdim_; i++)
{
for (int j=0; j<size; j++)
{
vec(j) = v[j*vdim_+i];
}
sdoftrans_->InvTransformDual(face_ori, vec);
for (int j=0; j<size; j++)
{
v[j*vdim_+i] = vec(j);
}
}
}
}
// ordering (i0j0, i1j0, i0j1, i1j1), each row is a column major matrix
const double ND_StatelessDofTransformation::T_data[24] =
const double ND_DofTransformation::T_data[24] =
{
1.0, 0.0, 0.0, 1.0,
-1.0, -1.0, 0.0, 1.0,
@@ -192,11 +183,11 @@ const double ND_StatelessDofTransformation::T_data[24] =
0.0, 1.0, 1.0, 0.0
};
const DenseTensor ND_StatelessDofTransformation
::T(const_cast<double*>(ND_StatelessDofTransformation::T_data), 2, 2, 6);
const DenseTensor ND_DofTransformation
::T(const_cast<double *>(ND_DofTransformation::T_data), 2, 2, 6);
// ordering (i0j0, i1j0, i0j1, i1j1), each row is a column major matrix
const double ND_StatelessDofTransformation::TInv_data[24] =
const double ND_DofTransformation::TInv_data[24] =
{
1.0, 0.0, 0.0, 1.0,
-1.0, -1.0, 0.0, 1.0,
@@ -206,12 +197,11 @@ const double ND_StatelessDofTransformation::TInv_data[24] =
0.0, 1.0, 1.0, 0.0
};
const DenseTensor ND_StatelessDofTransformation
::TInv(const_cast<double*>(TInv_data), 2, 2, 6);
const DenseTensor ND_DofTransformation
::TInv(const_cast<double *>(TInv_data), 2, 2, 6);
ND_StatelessDofTransformation::ND_StatelessDofTransformation(int size, int p,
int num_edges,
int num_tri_faces)
ND_DofTransformation::ND_DofTransformation(int size, int p, int num_edges,
int num_tri_faces)
: StatelessDofTransformation(size)
, order(p)
, nedofs(p)
@@ -221,18 +211,19 @@ ND_StatelessDofTransformation::ND_StatelessDofTransformation(int size, int p,
{
}
void ND_StatelessDofTransformation::TransformPrimal(const Array<int> & Fo,
double *v) const
void ND_DofTransformation::TransformPrimal(const Array<int> & Fo,
double *v) const
{
// Return immediately when no face DoFs are present
if (nfdofs < 2) { return; }
if (IsIdentity()) { return; }
MFEM_VERIFY(Fo.Size() >= nfaces,
"Face orientation array is shorter than the number of faces in "
"ND_StatelessDofTransformation");
"ND_DofTransformation");
double data[2];
Vector v2(data, 2);
DenseMatrix T2;
// Transform face DoFs
for (int f=0; f<nfaces; f++)
@@ -240,23 +231,25 @@ void ND_StatelessDofTransformation::TransformPrimal(const Array<int> & Fo,
for (int i=0; i<nfdofs/2; i++)
{
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T(Fo[f]).Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
T2.UseExternalData(const_cast<double *>(T.GetData(Fo[f])), 2, 2);
T2.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
}
}
}
void ND_StatelessDofTransformation::InvTransformPrimal(const Array<int> & Fo,
double *v) const
void ND_DofTransformation::InvTransformPrimal(const Array<int> & Fo,
double *v) const
{
// Return immediately when no face DoFs are present
if (nfdofs < 2) { return; }
if (IsIdentity()) { return; }
MFEM_VERIFY(Fo.Size() >= nfaces,
"Face orientation array is shorter than the number of faces in "
"ND_StatelessDofTransformation");
"ND_DofTransformation");
double data[2];
Vector v2(data, 2);
DenseMatrix T2Inv;
// Transform face DoFs
for (int f=0; f<nfaces; f++)
@@ -264,23 +257,24 @@ void ND_StatelessDofTransformation::InvTransformPrimal(const Array<int> & Fo,
for (int i=0; i<nfdofs/2; i++)
{
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
TInv(Fo[f]).Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
T2Inv.UseExternalData(const_cast<double *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
}
}
}
void ND_StatelessDofTransformation::TransformDual(const Array<int> & Fo,
double *v) const
void ND_DofTransformation::TransformDual(const Array<int> & Fo, double *v) const
{
// Return immediately when no face DoFs are present
if (nfdofs < 2) { return; }
if (IsIdentity()) { return; }
MFEM_VERIFY(Fo.Size() >= nfaces,
"Face orientation array is shorter than the number of faces in "
"ND_StatelessDofTransformation");
"ND_DofTransformation");
double data[2];
Vector v2(data, 2);
DenseMatrix T2Inv;
// Transform face DoFs
for (int f=0; f<nfaces; f++)
@@ -288,23 +282,25 @@ void ND_StatelessDofTransformation::TransformDual(const Array<int> & Fo,
for (int i=0; i<nfdofs/2; i++)
{
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
TInv(Fo[f]).MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
T2Inv.UseExternalData(const_cast<double *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
}
}
}
void ND_StatelessDofTransformation::InvTransformDual(const Array<int> & Fo,
double *v) const
void ND_DofTransformation::InvTransformDual(const Array<int> & Fo,
double *v) const
{
// Return immediately when no face DoFs are present
if (nfdofs < 2) { return; }
if (IsIdentity()) { return; }
MFEM_VERIFY(Fo.Size() >= nfaces,
"Face orientation array is shorter than the number of faces in "
"ND_StatelessDofTransformation");
"ND_DofTransformation");
double data[2];
Vector v2(data, 2);
DenseMatrix T2;
// Transform face DoFs
for (int f=0; f<nfaces; f++)
@@ -312,7 +308,8 @@ void ND_StatelessDofTransformation::InvTransformDual(const Array<int> & Fo,
for (int i=0; i<nfdofs/2; i++)
{
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T(Fo[f]).MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
T2.UseExternalData(const_cast<double *>(T.GetData(Fo[f])), 2, 2);
T2.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
}
}
}
+77 -251
View File
@@ -80,6 +80,9 @@ public:
inline int Width() const { return size_; }
inline int NumCols() const { return size_; }
/// If the DofTransformation performs no transformation
virtual bool IsIdentity() const = 0;
/** Transform local DoFs to align with the global DoFs. For example, this
transformation can be used to map the local vector computed by
FiniteElement::Project() to the transformed vector stored within a
@@ -115,6 +118,8 @@ public:
inline void InvTransformDual(const Array<int> & face_orientation,
Vector &v) const
{ InvTransformDual(face_orientation, v.GetData()); }
virtual ~StatelessDofTransformation() = default;
};
/** The DofTransformation class is an extension of the
@@ -133,35 +138,76 @@ public:
transferring finite element degrees of freedom between different meshes.
For examples of its use see the TransferMap used by the SubMesh class.
*/
class DofTransformation : virtual public StatelessDofTransformation
class DofTransformation
{
protected:
Array<int> Fo;
DofTransformation(int size)
: StatelessDofTransformation(size) {}
Array<int> Fo_;
const StatelessDofTransformation * dof_trans_;
int vdim_;
int ordering_;
public:
/** @brief Default constructor which requires that SetDofTransformation be
called before use. */
DofTransformation(int vdim = 1, int ordering = 0)
: dof_trans_(NULL)
, vdim_(vdim)
, ordering_(ordering)
{}
/// Constructor with a known StatelessDofTransformation
DofTransformation(const StatelessDofTransformation & dof_trans,
int vdim = 1, int ordering = 0)
: dof_trans_(&dof_trans)
, vdim_(vdim)
, ordering_(ordering)
{}
/** @brief Configure the transformation using face orientations for the
current element. */
/// The face_orientation array can be obtained from Mesh::GetElementFaces.
inline void SetFaceOrientations(const Array<int> & face_orientation)
{ Fo = face_orientation; }
inline void SetFaceOrientations(const Array<int> & Fo)
{ Fo_ = Fo; }
inline const Array<int> & GetFaceOrientations() const { return Fo; }
/// Return the face orientations for the current element
inline const Array<int> & GetFaceOrientations() const { return Fo_; }
using StatelessDofTransformation::TransformPrimal;
using StatelessDofTransformation::InvTransformPrimal;
using StatelessDofTransformation::TransformDual;
using StatelessDofTransformation::InvTransformDual;
/// Set or change the nested StatelessDofTransformation object
inline void SetDofTransformation(const StatelessDofTransformation & dof_trans)
{
dof_trans_ = &dof_trans;
}
inline void SetDofTransformation(const StatelessDofTransformation * dof_trans)
{
dof_trans_ = dof_trans;
}
/// Return the nested StatelessDofTransformation object
inline const StatelessDofTransformation * GetDofTransformation() const
{ return dof_trans_; }
/// Set or change the vdim and ordering parameter
inline void SetVDim(int vdim = 1, int ordering = 0)
{
vdim_ = vdim;
ordering_ = ordering;
}
/// Return the current vdim value
inline int GetVDim() const { return vdim_; }
inline int Size() const { return dof_trans_->Size(); }
inline int Height() const { return dof_trans_->Height(); }
inline int NumRows() const { return dof_trans_->NumRows(); }
inline int Width() const { return dof_trans_->Width(); }
inline int NumCols() const { return dof_trans_->NumCols(); }
inline bool IsIdentity() const { return dof_trans_->IsIdentity(); }
/** Transform local DoFs to align with the global DoFs. For example, this
transformation can be used to map the local vector computed by
FiniteElement::Project() to the transformed vector stored within a
GridFunction object. */
inline void TransformPrimal(double *v) const
{ TransformPrimal(Fo, v); }
void TransformPrimal(double *v) const;
inline void TransformPrimal(Vector &v) const
{ TransformPrimal(v.GetData()); }
@@ -179,21 +225,18 @@ public:
transform the vector obtained using GridFunction::GetSubVector before it
can be used to compute a local interpolation.
*/
inline void InvTransformPrimal(double *v) const
{ InvTransformPrimal(Fo, v); }
void InvTransformPrimal(double *v) const;
inline void InvTransformPrimal(Vector &v) const
{ InvTransformPrimal(v.GetData()); }
/** Transform dual DoFs as computed by a LinearFormIntegrator before summing
into a LinearForm object. */
inline void TransformDual(double *v) const
{ TransformDual(Fo, v); }
void TransformDual(double *v) const;
inline void TransformDual(Vector &v) const
{ TransformDual(v.GetData()); }
/** Inverse Transform dual DoFs */
inline void InvTransformDual(double *v) const
{ InvTransformDual(Fo, v); }
void InvTransformDual(double *v) const;
inline void InvTransformDual(Vector &v) const
{ InvTransformDual(v.GetData()); }
@@ -225,8 +268,6 @@ public:
TransformDual(V.GetColumn(c));
}
}
virtual ~DofTransformation() = default;
};
/** Transform a matrix of DoFs entries from different finite element spaces as
@@ -245,145 +286,6 @@ void TransformDual(const DofTransformation *ran_dof_trans,
const DofTransformation *dom_dof_trans,
DenseMatrix &elmat);
/** The StatelessVDofTransformation class implements a nested transformation
where an arbitrary StatelessDofTransformation is replicated with a
vdim >= 1.
*/
class StatelessVDofTransformation : virtual public StatelessDofTransformation
{
protected:
int vdim_;
int ordering_;
StatelessDofTransformation * sdoftrans_;
public:
/** @brief Default constructor which requires that SetDofTransformation be
called before use. */
StatelessVDofTransformation(int vdim = 1, int ordering = 0)
: StatelessDofTransformation(0)
, vdim_(vdim)
, ordering_(ordering)
, sdoftrans_(NULL)
{}
/// Constructor with a known StatelessDofTransformation
StatelessVDofTransformation(StatelessDofTransformation & doftrans,
int vdim = 1,
int ordering = 0)
: StatelessDofTransformation(vdim * doftrans.Size())
, vdim_(vdim)
, ordering_(ordering)
, sdoftrans_(&doftrans)
{}
/// Set or change the vdim parameter
inline void SetVDim(int vdim)
{
vdim_ = vdim;
if (sdoftrans_)
{
size_ = vdim_ * sdoftrans_->Size();
}
}
/// Return the current vdim value
inline int GetVDim() const { return vdim_; }
/// Set or change the nested StatelessDofTransformation object
inline void SetDofTransformation(StatelessDofTransformation & doftrans)
{
size_ = vdim_ * doftrans.Size();
sdoftrans_ = &doftrans;
}
/// Return the nested StatelessDofTransformation object
inline StatelessDofTransformation * GetDofTransformation() const
{ return sdoftrans_; }
using StatelessDofTransformation::TransformPrimal;
using StatelessDofTransformation::InvTransformPrimal;
using StatelessDofTransformation::TransformDual;
using StatelessDofTransformation::InvTransformDual;
/** Specializations of these base class methods which account for the vdim
and ordering of the full set of DoFs.
*/
void TransformPrimal(const Array<int> & face_ori, double *v) const;
void InvTransformPrimal(const Array<int> & face_ori, double *v) const;
void TransformDual(const Array<int> & face_ori, double *v) const;
void InvTransformDual(const Array<int> & face_ori, double *v) const;
};
/** The VDofTransformation class implements a nested transformation where an
arbitrary DofTransformation is replicated with a vdim >= 1.
*/
class VDofTransformation : public StatelessVDofTransformation,
public DofTransformation
{
protected:
DofTransformation * doftrans_;
public:
/** @brief Default constructor which requires that SetDofTransformation be
called before use. */
VDofTransformation(int vdim = 1, int ordering = 0)
: StatelessDofTransformation(0)
, StatelessVDofTransformation(vdim, ordering)
, DofTransformation(0)
, doftrans_(NULL)
{}
/// Constructor with a known DofTransformation
/// @note The face orientations in @a doftrans will be copied into the
/// new VDofTransformation object.
VDofTransformation(DofTransformation & doftrans, int vdim = 1,
int ordering = 0)
: StatelessDofTransformation(vdim * doftrans.Size())
, StatelessVDofTransformation(doftrans, vdim, ordering)
, DofTransformation(vdim * doftrans.Size())
, doftrans_(&doftrans)
{
DofTransformation::SetFaceOrientations(doftrans.GetFaceOrientations());
}
using StatelessVDofTransformation::SetDofTransformation;
/// Set or change the nested DofTransformation object
/// @note The face orientations in @a doftrans will be copied into the
/// VDofTransformation object.
void SetDofTransformation(DofTransformation & doftrans)
{
doftrans_ = &doftrans;
StatelessVDofTransformation::SetDofTransformation(doftrans);
DofTransformation::SetFaceOrientations(doftrans.GetFaceOrientations());
}
/// Return the nested DofTransformation object
inline DofTransformation * GetDofTransformation() const { return doftrans_; }
/// Set new face orientations in both the VDofTransformation and the
/// DofTransformation contained within (if there is one).
inline void SetFaceOrientations(const Array<int> & face_orientation)
{
DofTransformation::SetFaceOrientations(face_orientation);
if (doftrans_) { doftrans_->SetFaceOrientations(face_orientation); }
}
using DofTransformation::TransformPrimal;
using DofTransformation::InvTransformPrimal;
using DofTransformation::TransformDual;
using DofTransformation::InvTransformDual;
inline void TransformPrimal(double *v) const
{ TransformPrimal(Fo, v); }
inline void InvTransformPrimal(double *v) const
{ InvTransformPrimal(Fo, v); }
inline void TransformDual(double *v) const
{ TransformDual(Fo, v); }
inline void InvTransformDual(double *v) const
{ InvTransformDual(Fo, v); }
};
/** Abstract base class for high-order Nedelec spaces on elements with
triangular faces.
@@ -396,7 +298,7 @@ public:
be accessed as DenseMatrices using the GetFaceTransform() and
GetFaceInverseTransform() methods.
*/
class ND_StatelessDofTransformation : virtual public StatelessDofTransformation
class ND_DofTransformation : public StatelessDofTransformation
{
private:
static const double T_data[24];
@@ -410,8 +312,7 @@ protected:
const int nedges; // number of edges per element
const int nfaces; // number of triangular faces per element
ND_StatelessDofTransformation(int size, int order,
int num_edges, int num_tri_faces);
ND_DofTransformation(int size, int order, int num_edges, int num_tri_faces);
public:
// Return the 2x2 transformation operator for the given face orientation
@@ -421,116 +322,41 @@ public:
static const DenseMatrix & GetFaceInverseTransform(int ori)
{ return TInv(ori); }
void TransformPrimal(const Array<int> & face_orientation,
double *v) const;
bool IsIdentity() const override { return nfdofs < 2; }
void InvTransformPrimal(const Array<int> & face_orientation,
double *v) const;
void TransformDual(const Array<int> & face_orientation,
double *v) const;
void InvTransformDual(const Array<int> & face_orientation,
double *v) const;
void TransformPrimal(const Array<int> & Fo, double *v) const override;
void InvTransformPrimal(const Array<int> & Fo, double *v) const override;
void TransformDual(const Array<int> & Fo, double *v) const override;
void InvTransformDual(const Array<int> & Fo, double *v) const override;
};
/// Stateless DoF transformation implementation for the Nedelec basis on
/// triangles
class ND_TriStatelessDofTransformation : public ND_StatelessDofTransformation
{
public:
ND_TriStatelessDofTransformation(int order)
: StatelessDofTransformation(order*(order + 2))
, ND_StatelessDofTransformation(order*(order + 2), order, 3, 1)
{}
};
/// DoF transformation implementation for the Nedelec basis on triangles
class ND_TriDofTransformation : public DofTransformation,
public ND_TriStatelessDofTransformation
class ND_TriDofTransformation : public ND_DofTransformation
{
public:
ND_TriDofTransformation(int order)
: StatelessDofTransformation(order*(order + 2))
, DofTransformation(order*(order + 2))
, ND_TriStatelessDofTransformation(order)
{}
using DofTransformation::TransformPrimal;
using DofTransformation::InvTransformPrimal;
using DofTransformation::TransformDual;
using DofTransformation::InvTransformDual;
using ND_TriStatelessDofTransformation::TransformPrimal;
using ND_TriStatelessDofTransformation::InvTransformPrimal;
using ND_TriStatelessDofTransformation::TransformDual;
using ND_TriStatelessDofTransformation::InvTransformDual;
};
/// DoF transformation implementation for the Nedelec basis on tetrahedra
class ND_TetStatelessDofTransformation : public ND_StatelessDofTransformation
{
public:
ND_TetStatelessDofTransformation(int order)
: StatelessDofTransformation(order*(order + 2)*(order + 3)/2)
, ND_StatelessDofTransformation(order*(order + 2)*(order + 3)/2, order,
6, 4)
: ND_DofTransformation(order*(order + 2), order, 3, 1)
{}
};
/// DoF transformation implementation for the Nedelec basis on tetrahedra
class ND_TetDofTransformation : public DofTransformation,
public ND_TetStatelessDofTransformation
class ND_TetDofTransformation : public ND_DofTransformation
{
public:
ND_TetDofTransformation(int order)
: StatelessDofTransformation(order*(order + 2)*(order + 3)/2)
, DofTransformation(order*(order + 2)*(order + 3)/2)
, ND_TetStatelessDofTransformation(order)
{}
using DofTransformation::TransformPrimal;
using DofTransformation::InvTransformPrimal;
using DofTransformation::TransformDual;
using DofTransformation::InvTransformDual;
using ND_TetStatelessDofTransformation::TransformPrimal;
using ND_TetStatelessDofTransformation::InvTransformPrimal;
using ND_TetStatelessDofTransformation::TransformDual;
using ND_TetStatelessDofTransformation::InvTransformDual;
};
/// DoF transformation implementation for the Nedelec basis on wedge elements
class ND_WedgeStatelessDofTransformation : public ND_StatelessDofTransformation
{
public:
ND_WedgeStatelessDofTransformation(int order)
: StatelessDofTransformation(3 * order * ((order + 1) * (order + 2))/2)
, ND_StatelessDofTransformation(3 * order * ((order + 1) * (order + 2))/2,
order, 9, 2)
: ND_DofTransformation(order*(order + 2)*(order + 3)/2, order, 6, 4)
{}
};
/// DoF transformation implementation for the Nedelec basis on wedge elements
class ND_WedgeDofTransformation : public DofTransformation,
public ND_WedgeStatelessDofTransformation
class ND_WedgeDofTransformation : public ND_DofTransformation
{
public:
ND_WedgeDofTransformation(int order)
: StatelessDofTransformation(3 * order * ((order + 1) * (order + 2))/2)
, DofTransformation(3 * order * ((order + 1) * (order + 2))/2)
, ND_WedgeStatelessDofTransformation(order)
: ND_DofTransformation(3 * order * ((order + 1) * (order + 2))/2,
order, 9, 2)
{}
using DofTransformation::TransformPrimal;
using DofTransformation::InvTransformPrimal;
using DofTransformation::TransformDual;
using DofTransformation::InvTransformDual;
using ND_WedgeStatelessDofTransformation::TransformPrimal;
using ND_WedgeStatelessDofTransformation::InvTransformPrimal;
using ND_WedgeStatelessDofTransformation::TransformDual;
using ND_WedgeStatelessDofTransformation::InvTransformDual;
};
} // namespace mfem
+1
View File
@@ -492,6 +492,7 @@ int IsoparametricTransformation::OrderGrad(const FiniteElement *fe) const
void IsoparametricTransformation::Transform (const IntegrationPoint &ip,
Vector &trans)
{
MFEM_ASSERT(FElem != nullptr, "Must provide a valid FiniteElement object!");
shape.SetSize(FElem->GetDof());
trans.SetSize(PointMat.Height());
+1 -1
View File
@@ -596,7 +596,7 @@ public:
/** @brief Return a DoF transformation object for this particular type of
basis.
*/
virtual StatelessDofTransformation * GetDofTransformation() const
virtual const StatelessDofTransformation *GetDofTransformation() const
{ return NULL; }
/// Deconstruct the FiniteElement
+1 -1
View File
@@ -6031,7 +6031,7 @@ void RT0PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
shape(1,2) = z;
shape(2,0) = x * (2.0 - z) * ozi;
shape(2,1) = - y * z * ozi;;
shape(2,1) = - y * z * ozi;
shape(2,2) = z;
shape(3,0) = - x * z * ozi;
+6 -6
View File
@@ -179,7 +179,7 @@ class ND_TetrahedronElement : public VectorFiniteElement
Array<int> dof2tk;
DenseMatrixInverse Ti;
mutable ND_TetStatelessDofTransformation doftrans;
ND_TetDofTransformation doftrans;
public:
/// Construct the ND_TetrahedronElement of order @a p
@@ -201,7 +201,7 @@ public:
ElementTransformation &Trans,
DenseMatrix &I) const
{ LocalInterpolation_ND(CheckVectorFE(fe), tk, dof2tk, Trans, I); }
virtual StatelessDofTransformation * GetDofTransformation() const
virtual const StatelessDofTransformation *GetDofTransformation() const
{ return &doftrans; }
using FiniteElement::Project;
virtual void Project(VectorCoefficient &vc,
@@ -242,7 +242,7 @@ class ND_TriangleElement : public VectorFiniteElement
Array<int> dof2tk;
DenseMatrixInverse Ti;
mutable ND_TriStatelessDofTransformation doftrans;
ND_TriDofTransformation doftrans;
public:
/// Construct the ND_TriangleElement of order @a p
@@ -264,7 +264,7 @@ public:
ElementTransformation &Trans,
DenseMatrix &I) const
{ LocalInterpolation_ND(CheckVectorFE(fe), tk, dof2tk, Trans, I); }
virtual StatelessDofTransformation * GetDofTransformation() const
virtual const StatelessDofTransformation *GetDofTransformation() const
{ return &doftrans; }
using FiniteElement::Project;
virtual void Project(VectorCoefficient &vc,
@@ -346,7 +346,7 @@ private:
#endif
Array<int> dof2tk, t_dof, s_dof;
mutable ND_WedgeStatelessDofTransformation doftrans;
ND_WedgeDofTransformation doftrans;
H1_TriangleElement H1TriangleFE;
ND_TriangleElement NDTriangleFE;
@@ -379,7 +379,7 @@ public:
DenseMatrix &I) const
{ LocalInterpolation_ND(CheckVectorFE(fe), tk, dof2tk, Trans, I); }
virtual StatelessDofTransformation * GetDofTransformation() const
virtual const StatelessDofTransformation *GetDofTransformation() const
{ return &doftrans; }
using FiniteElement::Project;
+1 -1
View File
@@ -2896,7 +2896,7 @@ ND_FECollection::FiniteElementForGeometry(Geometry::Type GeomType) const
}
}
StatelessDofTransformation *
const StatelessDofTransformation *
ND_FECollection::DofTransformationForGeometry(Geometry::Type GeomType) const
{
if (!Geometry::IsTensorProduct(GeomType) && this->GetOrder() > 1)
+2 -2
View File
@@ -63,7 +63,7 @@ public:
/** @brief Returns a DoF transformation object compatible with this basis
and geometry type.
*/
virtual StatelessDofTransformation *
virtual const StatelessDofTransformation *
DofTransformationForGeometry(Geometry::Type GeomType) const
{ return NULL; }
@@ -483,7 +483,7 @@ public:
int DofForGeometry(Geometry::Type GeomType) const override
{ return ND_dof[GeomType]; }
StatelessDofTransformation *
const StatelessDofTransformation *
DofTransformationForGeometry(Geometry::Type GeomType) const override;
const int *DofOrderForOrientation(Geometry::Type GeomType,
+197 -198
View File
@@ -63,7 +63,6 @@ FiniteElementSpace::FiniteElementSpace()
elem_dof(NULL), elem_fos(NULL), bdr_elem_dof(NULL), bdr_elem_fos(NULL),
face_dof(NULL),
NURBSext(NULL), own_ext(false),
DoFTrans(0), VDoFTrans(vdim, ordering),
cP_is_set(false),
Th(Operator::ANY_TYPE),
sequence(0), mesh_sequence(0), orders_changed(false), relaxed_hp(false)
@@ -72,7 +71,6 @@ FiniteElementSpace::FiniteElementSpace()
FiniteElementSpace::FiniteElementSpace(const FiniteElementSpace &orig,
Mesh *mesh_,
const FiniteElementCollection *fec_)
: VDoFTrans(orig.vdim, orig.ordering)
{
mesh_ = mesh_ ? mesh_ : orig.mesh;
fec_ = fec_ ? fec_ : orig.fec;
@@ -212,7 +210,7 @@ void FiniteElementSpace::GetVDofs(int vd, Array<int>& dofs, int ndofs_) const
}
}
void FiniteElementSpace::DofsToVDofs (Array<int> &dofs, int ndofs_) const
void FiniteElementSpace::DofsToVDofs(Array<int> &dofs, int ndofs_) const
{
if (vdim == 1) { return; }
if (ndofs_ < 0) { ndofs_ = this->ndofs; }
@@ -264,7 +262,7 @@ int FiniteElementSpace::DofToVDof(int dof, int vd, int ndofs_) const
}
// static function
void FiniteElementSpace::AdjustVDofs (Array<int> &vdofs)
void FiniteElementSpace::AdjustVDofs(Array<int> &vdofs)
{
int n = vdofs.Size(), *vdof = vdofs;
for (int i = 0; i < n; i++)
@@ -277,36 +275,36 @@ void FiniteElementSpace::AdjustVDofs (Array<int> &vdofs)
}
}
void FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs,
DofTransformation &doftrans) const
{
GetElementDofs(i, vdofs, doftrans);
DofsToVDofs(vdofs);
doftrans.SetVDim(vdim, ordering);
}
DofTransformation *
FiniteElementSpace::GetElementVDofs(int i, Array<int> &vdofs) const
{
DofTransformation * doftrans = GetElementDofs(i, vdofs);
DoFTrans.SetDofTransformation(NULL);
GetElementVDofs(i, vdofs, DoFTrans);
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
}
void FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs,
DofTransformation &doftrans) const
{
GetBdrElementDofs(i, vdofs, doftrans);
DofsToVDofs(vdofs);
if (vdim == 1 || doftrans == NULL)
{
return doftrans;
}
else
{
VDoFTrans.SetDofTransformation(*doftrans);
return &VDoFTrans;
}
doftrans.SetVDim(vdim, ordering);
}
DofTransformation *
FiniteElementSpace::GetBdrElementVDofs(int i, Array<int> &vdofs) const
{
DofTransformation * doftrans = GetBdrElementDofs(i, vdofs);
DofsToVDofs(vdofs);
if (vdim == 1 || doftrans == NULL)
{
return doftrans;
}
else
{
VDoFTrans.SetDofTransformation(*doftrans);
return &VDoFTrans;
}
DoFTrans.SetDofTransformation(NULL);
GetBdrElementVDofs(i, vdofs, DoFTrans);
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
}
void FiniteElementSpace::GetPatchVDofs(int i, Array<int> &vdofs) const
@@ -777,9 +775,9 @@ FiniteElementSpace::H2L_GlobalRestrictionMatrix (FiniteElementSpace *lfes)
return R;
}
void FiniteElementSpace
::AddDependencies(SparseMatrix& deps, Array<int>& master_dofs,
Array<int>& slave_dofs, DenseMatrix& I, int skipfirst)
void FiniteElementSpace::AddDependencies(
SparseMatrix& deps, Array<int>& master_dofs, Array<int>& slave_dofs,
DenseMatrix& I, int skipfirst)
{
for (int i = skipfirst; i < slave_dofs.Size(); i++)
{
@@ -802,11 +800,9 @@ void FiniteElementSpace
}
}
void FiniteElementSpace
::AddEdgeFaceDependencies(SparseMatrix &deps, Array<int> &master_dofs,
const FiniteElement *master_fe,
Array<int> &slave_dofs, int slave_face,
const DenseMatrix *pm) const
void FiniteElementSpace::AddEdgeFaceDependencies(
SparseMatrix &deps, Array<int> &master_dofs, const FiniteElement *master_fe,
Array<int> &slave_dofs, int slave_face, const DenseMatrix *pm) const
{
// In variable-order spaces in 3D, we need to only constrain interior face
// DOFs (this is done one level up), since edge dependencies can be more
@@ -1533,12 +1529,12 @@ SparseMatrix* FiniteElementSpace::RefinementMatrix(int old_ndofs,
localP);
}
FiniteElementSpace::RefinementOperator::RefinementOperator
(const FiniteElementSpace* fespace, Table* old_elem_dof, Table* old_elem_fos,
int old_ndofs)
: fespace(fespace)
, old_elem_dof(old_elem_dof)
, old_elem_fos(old_elem_fos)
FiniteElementSpace::RefinementOperator::RefinementOperator(
const FiniteElementSpace* fespace, Table* old_elem_dof, Table* old_elem_fos,
int old_ndofs)
: fespace(fespace),
old_elem_dof(old_elem_dof),
old_elem_fos(old_elem_fos)
{
MFEM_VERIFY(fespace->GetNE() >= old_elem_dof->Size(),
"Previous mesh is not coarser.");
@@ -1553,7 +1549,7 @@ FiniteElementSpace::RefinementOperator::RefinementOperator
fespace->GetLocalRefinementMatrices(elem_geoms[i], localP[elem_geoms[i]]);
}
ConstructDoFTrans();
ConstructDoFTransArray();
}
FiniteElementSpace::RefinementOperator::RefinementOperator(
@@ -1578,59 +1574,58 @@ FiniteElementSpace::RefinementOperator::RefinementOperator(
old_elem_fos = new Table(*coarse_fes->GetElementToFaceOrientationTable());
}
ConstructDoFTrans();
ConstructDoFTransArray();
}
FiniteElementSpace::RefinementOperator::~RefinementOperator()
{
delete old_elem_dof;
delete old_elem_fos;
for (int i=0; i<old_DoFTrans.Size(); i++)
for (int i=0; i<old_DoFTransArray.Size(); i++)
{
delete old_DoFTrans[i];
delete old_DoFTransArray[i];
}
}
void FiniteElementSpace::RefinementOperator
::ConstructDoFTrans()
void FiniteElementSpace::RefinementOperator::ConstructDoFTransArray()
{
old_DoFTrans.SetSize(Geometry::NUM_GEOMETRIES);
for (int i=0; i<old_DoFTrans.Size(); i++)
old_DoFTransArray.SetSize(Geometry::NUM_GEOMETRIES);
for (int i=0; i<old_DoFTransArray.Size(); i++)
{
old_DoFTrans[i] = NULL;
old_DoFTransArray[i] = NULL;
}
const FiniteElementCollection *fec_ref = fespace->FEColl();
if (dynamic_cast<const ND_FECollection*>(fec_ref))
{
const FiniteElement * nd_tri =
const FiniteElement *nd_tri =
fec_ref->FiniteElementForGeometry(Geometry::TRIANGLE);
if (nd_tri)
{
old_DoFTrans[Geometry::TRIANGLE] =
old_DoFTransArray[Geometry::TRIANGLE] =
new ND_TriDofTransformation(nd_tri->GetOrder());
}
const FiniteElement * nd_tet =
const FiniteElement *nd_tet =
fec_ref->FiniteElementForGeometry(Geometry::TETRAHEDRON);
if (nd_tet)
{
old_DoFTrans[Geometry::TETRAHEDRON] =
old_DoFTransArray[Geometry::TETRAHEDRON] =
new ND_TetDofTransformation(nd_tet->GetOrder());
}
const FiniteElement * nd_pri =
const FiniteElement *nd_pri =
fec_ref->FiniteElementForGeometry(Geometry::PRISM);
if (nd_pri)
{
old_DoFTrans[Geometry::PRISM] =
old_DoFTransArray[Geometry::PRISM] =
new ND_WedgeDofTransformation(nd_pri->GetOrder());
}
}
}
void FiniteElementSpace::RefinementOperator
::Mult(const Vector &x, Vector &y) const
void FiniteElementSpace::RefinementOperator::Mult(const Vector &x,
Vector &y) const
{
Mesh* mesh_ref = fespace->GetMesh();
const CoarseFineTransformations &trans_ref =
@@ -1662,6 +1657,7 @@ void FiniteElementSpace::RefinementOperator
fespace->DofsToVDofs(vd, vdofs);
old_dofs.Copy(old_vdofs);
fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
x.GetSubVector(old_vdofs, subX);
lP.Mult(subX, subY);
y.SetSubVector(vdofs, subY);
@@ -1670,40 +1666,30 @@ void FiniteElementSpace::RefinementOperator
else
{
old_elem_fos->GetRow(emb.parent, old_Fo);
old_DoFTrans[geom]->SetFaceOrientations(old_Fo);
DofTransformation *new_doftrans = NULL;
VDofTransformation *vdoftrans =
dynamic_cast<VDofTransformation*>(doftrans);
if (vdoftrans)
{
new_doftrans = doftrans;
doftrans = vdoftrans->GetDofTransformation();
}
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
old_DoFTrans.SetFaceOrientations(old_Fo);
doftrans->SetVDim();
for (int vd = 0; vd < rvdim; vd++)
{
dofs.Copy(vdofs);
fespace->DofsToVDofs(vd, vdofs);
old_dofs.Copy(old_vdofs);
fespace->DofsToVDofs(vd, old_vdofs, old_ndofs);
x.GetSubVector(old_vdofs, subX);
old_DoFTrans[geom]->InvTransformPrimal(subX);
old_DoFTrans.InvTransformPrimal(subX);
lP.Mult(subX, subY);
doftrans->TransformPrimal(subY);
y.SetSubVector(vdofs, subY);
}
if (vdoftrans)
{
doftrans = new_doftrans;
}
doftrans->SetVDim(rvdim, fespace->GetOrdering());
}
}
}
void FiniteElementSpace::RefinementOperator
::MultTranspose(const Vector &x, Vector &y) const
void FiniteElementSpace::RefinementOperator::MultTranspose(const Vector &x,
Vector &y) const
{
y = 0.0;
@@ -1727,7 +1713,7 @@ void FiniteElementSpace::RefinementOperator
const Geometry::Type geom = mesh_ref->GetElementBaseGeometry(k);
const DenseMatrix &lP = localP[geom](emb.matrix);
DofTransformation * doftrans = fespace->GetElementDofs(k, f_dofs);
DofTransformation *doftrans = fespace->GetElementDofs(k, f_dofs);
old_elem_dof->GetRow(emb.parent, c_dofs);
if (!doftrans)
@@ -1742,7 +1728,6 @@ void FiniteElementSpace::RefinementOperator
fespace->DofsToVDofs(vd, c_vdofs, old_ndofs);
x.GetSubVector(f_vdofs, subX);
for (int p = 0; p < f_dofs.Size(); ++p)
{
if (processed[DecodeDof(f_dofs[p])])
@@ -1750,7 +1735,6 @@ void FiniteElementSpace::RefinementOperator
subX[p] = 0.0;
}
}
lP.MultTranspose(subX, subY);
y.AddElementVector(c_vdofs, subY);
}
@@ -1760,17 +1744,10 @@ void FiniteElementSpace::RefinementOperator
subYt.SetSize(lP.Width());
old_elem_fos->GetRow(emb.parent, old_Fo);
old_DoFTrans[geom]->SetFaceOrientations(old_Fo);
DofTransformation *new_doftrans = NULL;
VDofTransformation *vdoftrans =
dynamic_cast<VDofTransformation*>(doftrans);
if (vdoftrans)
{
new_doftrans = doftrans;
doftrans = vdoftrans->GetDofTransformation();
}
old_DoFTrans.SetDofTransformation(*old_DoFTransArray[geom]);
old_DoFTrans.SetFaceOrientations(old_Fo);
doftrans->SetVDim();
for (int vd = 0; vd < rvdim; vd++)
{
f_dofs.Copy(f_vdofs);
@@ -1787,16 +1764,11 @@ void FiniteElementSpace::RefinementOperator
subX[p] = 0.0;
}
}
lP.MultTranspose(subX, subYt);
old_DoFTrans[geom]->TransformDual(subYt);
old_DoFTrans.TransformDual(subYt);
y.AddElementVector(c_vdofs, subYt);
}
if (vdoftrans)
{
doftrans = new_doftrans;
}
doftrans->SetVDim(rvdim, fespace->GetOrdering());
}
for (int p = 0; p < f_dofs.Size(); ++p)
@@ -2024,8 +1996,8 @@ FiniteElementSpace::DerefinementOperator::~DerefinementOperator()
delete coarse_elem_dof;
}
void FiniteElementSpace::DerefinementOperator
::Mult(const Vector &x, Vector &y) const
void FiniteElementSpace::DerefinementOperator::Mult(const Vector &x,
Vector &y) const
{
Array<int> c_vdofs, f_vdofs;
Vector loc_x, loc_y;
@@ -2227,7 +2199,7 @@ void FiniteElementSpace::Constructor(Mesh *mesh_, NURBSExtension *NURBSext_,
R_transpose.reset();
cP_is_set = false;
ConstructDoFTrans();
ConstructDoFTransArray();
}
else
{
@@ -2239,40 +2211,39 @@ void FiniteElementSpace::Constructor(Mesh *mesh_, NURBSExtension *NURBSext_,
BuildElementToDofTable();
}
void FiniteElementSpace::ConstructDoFTrans()
void FiniteElementSpace::ConstructDoFTransArray()
{
DestroyDoFTrans();
DestroyDoFTransArray();
VDoFTrans.SetVDim(vdim);
DoFTrans.SetSize(Geometry::NUM_GEOMETRIES);
for (int i=0; i<DoFTrans.Size(); i++)
DoFTransArray.SetSize(Geometry::NUM_GEOMETRIES);
for (int i=0; i<DoFTransArray.Size(); i++)
{
DoFTrans[i] = NULL;
DoFTransArray[i] = NULL;
}
if (mesh->Dimension() < 3) { return; }
if (dynamic_cast<const ND_FECollection*>(fec))
{
const FiniteElement * nd_tri =
const FiniteElement *nd_tri =
fec->FiniteElementForGeometry(Geometry::TRIANGLE);
if (nd_tri)
{
DoFTrans[Geometry::TRIANGLE] =
DoFTransArray[Geometry::TRIANGLE] =
new ND_TriDofTransformation(nd_tri->GetOrder());
}
const FiniteElement * nd_tet =
const FiniteElement *nd_tet =
fec->FiniteElementForGeometry(Geometry::TETRAHEDRON);
if (nd_tet)
{
DoFTrans[Geometry::TETRAHEDRON] =
DoFTransArray[Geometry::TETRAHEDRON] =
new ND_TetDofTransformation(nd_tet->GetOrder());
}
const FiniteElement * nd_pri =
const FiniteElement *nd_pri =
fec->FiniteElementForGeometry(Geometry::PRISM);
if (nd_pri)
{
DoFTrans[Geometry::PRISM] =
DoFTransArray[Geometry::PRISM] =
new ND_WedgeDofTransformation(nd_pri->GetOrder());
}
}
@@ -2324,7 +2295,7 @@ void FiniteElementSpace::BuildNURBSFaceToDofTable() const
face_to_be = -1;
for (int b = 0; b < GetNBE(); b++)
{
int f = mesh->GetBdrElementEdgeIndex(b);
int f = mesh->GetBdrElementFaceIndex(b);
face_to_be[f] = b;
}
@@ -2428,6 +2399,7 @@ void FiniteElementSpace::Construct()
{
// the simple case: all edges are of the same order
nedofs = mesh->GetNEdges() * fec->GetNumDof(Geometry::SEGMENT, order);
var_edge_dofs.Clear(); // ensure any old var_edge_dof table is dumped.
}
}
@@ -2446,6 +2418,7 @@ void FiniteElementSpace::Construct()
// the simple case: all faces are of the same geometry and order
uni_fdof = fec->GetNumDof(mesh->GetFaceGeometry(0), order);
nfdofs = mesh->GetNFaces() * uni_fdof;
var_face_dofs.Clear(); // ensure any old var_face_dof table is dumped.
}
}
@@ -2474,7 +2447,7 @@ void FiniteElementSpace::Construct()
ndofs = nvdofs + nedofs + nfdofs + nbdofs;
ConstructDoFTrans();
ConstructDoFTransArray();
// record the current mesh sequence number to detect refinement etc.
mesh_sequence = mesh->GetSequence();
@@ -2499,9 +2472,8 @@ int FiniteElementSpace::MinOrder(VarOrderBits bits)
return 0;
}
void FiniteElementSpace
::CalcEdgeFaceVarOrders(Array<VarOrderBits> &edge_orders,
Array<VarOrderBits> &face_orders) const
void FiniteElementSpace::CalcEdgeFaceVarOrders(
Array<VarOrderBits> &edge_orders, Array<VarOrderBits> &face_orders) const
{
MFEM_ASSERT(IsVariableOrder(), "");
MFEM_ASSERT(Nonconforming(), "");
@@ -2656,7 +2628,6 @@ int FiniteElementSpace::MakeDofTable(int ent_dim,
int dofs = fec->GetNumDof(geom, order);
list.Append(Connection(i, total_dofs));
total_dofs += dofs;
if (var_ent_order) { var_ent_order->Append(order); }
}
}
@@ -2667,7 +2638,6 @@ int FiniteElementSpace::MakeDofTable(int ent_dim,
// build the table
entity_dofs.MakeFromList(num_ent+1, list);
return total_dofs;
}
@@ -2727,8 +2697,8 @@ int FiniteElementSpace::GetNVariants(int entity, int index) const
static const char* msg_orders_changed =
"Element orders changed, you need to Update() the space first.";
DofTransformation *
FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
void FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs,
DofTransformation &doftrans) const
{
MFEM_VERIFY(!orders_changed, msg_orders_changed);
@@ -2736,13 +2706,16 @@ FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
{
elem_dof->GetRow(elem, dofs);
if (DoFTrans[mesh->GetElementBaseGeometry(elem)])
if (DoFTransArray[mesh->GetElementBaseGeometry(elem)])
{
Array<int> Fo;
elem_fos -> GetRow (elem, Fo);
DoFTrans[mesh->GetElementBaseGeometry(elem)]->SetFaceOrientations(Fo);
doftrans.SetDofTransformation(
*DoFTransArray[mesh->GetElementBaseGeometry(elem)]);
doftrans.SetFaceOrientations(Fo);
doftrans.SetVDim();
}
return DoFTrans[mesh->GetElementBaseGeometry(elem)];
return;
}
Array<int> V, E, Eo, F, Fo; // TODO: LocalArray
@@ -2766,10 +2739,12 @@ FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
{
nfd += fec->GetNumDof(mesh->GetFaceGeometry(F[i]), order);
}
if (DoFTrans[mesh->GetElementBaseGeometry(elem)])
if (DoFTransArray[mesh->GetElementBaseGeometry(elem)])
{
DoFTrans[mesh->GetElementBaseGeometry(elem)]
-> SetFaceOrientations(Fo);
doftrans.SetDofTransformation(
*DoFTransArray[mesh->GetElementBaseGeometry(elem)]);
doftrans.SetFaceOrientations(Fo);
doftrans.SetVDim();
}
}
@@ -2828,54 +2803,18 @@ FiniteElementSpace::GetElementDofs(int elem, Array<int> &dofs) const
dofs.Append(bbase + j);
}
}
return DoFTrans[mesh->GetElementBaseGeometry(elem)];
}
void FiniteElementSpace::GetPatchDofs(int patch, Array<int> &dofs) const
DofTransformation *FiniteElementSpace::GetElementDofs(int elem,
Array<int> &dofs) const
{
MFEM_ASSERT(NURBSext,
"FiniteElementSpace::GetPatchDofs needs a NURBSExtension");
NURBSext->GetPatchDofs(patch, dofs);
DoFTrans.SetDofTransformation(NULL);
GetElementDofs(elem, dofs, DoFTrans);
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
}
const FiniteElement *FiniteElementSpace::GetFE(int i) const
{
if (i < 0 || i >= mesh->GetNE())
{
if (mesh->GetNE() == 0)
{
MFEM_ABORT("Empty MPI partitions are not permitted!");
}
MFEM_ABORT("Invalid element id:" << i << "; minimum allowed:" << 0 <<
", maximum allowed:" << mesh->GetNE()-1);
}
const FiniteElement *FE =
fec->GetFE(mesh->GetElementGeometry(i), GetElementOrderImpl(i));
if (NURBSext)
{
NURBSext->LoadFE(i, FE);
}
else
{
#ifdef MFEM_DEBUG
// consistency check: fec->GetOrder() and FE->GetOrder() should return
// the same value (for standard, constant-order spaces)
if (!IsVariableOrder() && FE->GetDim() > 0)
{
MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
"internal error: " <<
FE->GetOrder() << " != " << fec->GetOrder());
}
#endif
}
return FE;
}
DofTransformation *
FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
void FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs,
DofTransformation &doftrans) const
{
MFEM_VERIFY(!orders_changed, msg_orders_changed);
@@ -2883,17 +2822,19 @@ FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
{
bdr_elem_dof->GetRow(bel, dofs);
if (DoFTrans[mesh->GetBdrElementBaseGeometry(bel)])
if (DoFTransArray[mesh->GetBdrElementBaseGeometry(bel)])
{
Array<int> Fo;
bdr_elem_fos -> GetRow (bel, Fo);
DoFTrans[mesh->GetBdrElementBaseGeometry(bel)]->
SetFaceOrientations(Fo);
doftrans.SetDofTransformation(
*DoFTransArray[mesh->GetBdrElementBaseGeometry(bel)]);
doftrans.SetFaceOrientations(Fo);
doftrans.SetVDim();
}
return DoFTrans[mesh->GetBdrElementBaseGeometry(bel)];
return;
}
Array<int> V, E, Eo, Fo; // TODO: LocalArray
Array<int> V, E, Eo; // TODO: LocalArray
int F, oF;
int dim = mesh->Dimension();
@@ -2917,11 +2858,14 @@ FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
{
mesh->GetBdrElementFace(bel, &F, &oF);
if (DoFTrans[mesh->GetBdrElementBaseGeometry(bel)])
if (DoFTransArray[mesh->GetBdrElementBaseGeometry(bel)])
{
Fo.Append(oF);
DoFTrans[mesh->GetBdrElementBaseGeometry(bel)]->
SetFaceOrientations(Fo);
mfem::Array<int> Fo(1);
Fo[0] = oF;
doftrans.SetDofTransformation(
*DoFTransArray[mesh->GetBdrElementBaseGeometry(bel)]);
doftrans.SetFaceOrientations(Fo);
doftrans.SetVDim();
}
}
@@ -2963,8 +2907,14 @@ FiniteElementSpace::GetBdrElementDofs(int bel, Array<int> &dofs) const
dofs.Append(EncodeDof(nvdofs + nedofs + fbase, ind[j]));
}
}
}
return DoFTrans[mesh->GetBdrElementBaseGeometry(bel)];
DofTransformation *FiniteElementSpace::GetBdrElementDofs(int bel,
Array<int> &dofs) const
{
DoFTrans.SetDofTransformation(NULL);
GetBdrElementDofs(bel, dofs, DoFTrans);
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
}
int FiniteElementSpace::GetFaceDofs(int face, Array<int> &dofs,
@@ -2996,7 +2946,14 @@ int FiniteElementSpace::GetFaceDofs(int face, Array<int> &dofs,
order = !IsVariableOrder() ? fec->GetOrder() :
var_face_orders[var_face_dofs.GetI()[face] + variant];
MFEM_ASSERT(fec->GetNumDof(fgeom, order) == nf, "");
MFEM_ASSERT(fec->GetNumDof(fgeom, order) == nf, [&]()
{
std::stringstream msg;
msg << "fec->GetNumDof(" << (fgeom == Geometry::SQUARE ? "square" : "triangle")
<< ", " << order << ") = " << fec->GetNumDof(fgeom, order) << " nf " << nf;
msg << " face " << face << " variant " << variant << std::endl;
return msg.str();
}());
}
else
{
@@ -3127,18 +3084,6 @@ int FiniteElementSpace::GetNumElementInteriorDofs(int i) const
GetElementOrderImpl(i));
}
void FiniteElementSpace::GetEdgeInteriorDofs(int i, Array<int> &dofs) const
{
MFEM_VERIFY(!IsVariableOrder(), "not implemented");
int ne = fec->DofForGeometry(Geometry::SEGMENT);
dofs.SetSize (ne);
for (int j = 0, k = nvdofs+i*ne; j < ne; j++, k++)
{
dofs[j] = k;
}
}
void FiniteElementSpace::GetFaceInteriorDofs(int i, Array<int> &dofs) const
{
MFEM_VERIFY(!IsVariableOrder(), "not implemented");
@@ -3163,6 +3108,61 @@ void FiniteElementSpace::GetFaceInteriorDofs(int i, Array<int> &dofs) const
}
}
void FiniteElementSpace::GetEdgeInteriorDofs(int i, Array<int> &dofs) const
{
MFEM_VERIFY(!IsVariableOrder(), "not implemented");
int ne = fec->DofForGeometry(Geometry::SEGMENT);
dofs.SetSize (ne);
for (int j = 0, k = nvdofs+i*ne; j < ne; j++, k++)
{
dofs[j] = k;
}
}
void FiniteElementSpace::GetPatchDofs(int patch, Array<int> &dofs) const
{
MFEM_ASSERT(NURBSext,
"FiniteElementSpace::GetPatchDofs needs a NURBSExtension");
NURBSext->GetPatchDofs(patch, dofs);
}
const FiniteElement *FiniteElementSpace::GetFE(int i) const
{
if (i < 0 || i >= mesh->GetNE())
{
if (mesh->GetNE() == 0)
{
MFEM_ABORT("Empty MPI partitions are not permitted!");
}
MFEM_ABORT("Invalid element id:" << i << "; minimum allowed:" << 0 <<
", maximum allowed:" << mesh->GetNE()-1);
}
const FiniteElement *FE =
fec->GetFE(mesh->GetElementGeometry(i), GetElementOrderImpl(i));
if (NURBSext)
{
NURBSext->LoadFE(i, FE);
}
else
{
#ifdef MFEM_DEBUG
// consistency check: fec->GetOrder() and FE->GetOrder() should return
// the same value (for standard, constant-order spaces)
if (!IsVariableOrder() && FE->GetDim() > 0)
{
MFEM_ASSERT(FE->GetOrder() == fec->GetOrder(),
"internal error: " <<
FE->GetOrder() << " != " << fec->GetOrder());
}
#endif
}
return FE;
}
const FiniteElement *FiniteElementSpace::GetBE(int i) const
{
int order = fec->GetOrder();
@@ -3235,8 +3235,8 @@ const FiniteElement *FiniteElementSpace::GetEdgeElement(int i,
return fec->GetFE(Geometry::SEGMENT, eo);
}
const FiniteElement *FiniteElementSpace
::GetTraceElement(int i, Geometry::Type geom_type) const
const FiniteElement *FiniteElementSpace::GetTraceElement(
int i, Geometry::Type geom_type) const
{
return fec->TraceFiniteElementForGeometry(geom_type);
}
@@ -3276,7 +3276,7 @@ void FiniteElementSpace::Destroy()
}
E2BFQ_array.SetSize(0);
DestroyDoFTrans();
DestroyDoFTransArray();
dof_elem_array.DeleteAll();
dof_ldof_array.DeleteAll();
@@ -3294,19 +3294,18 @@ void FiniteElementSpace::Destroy()
delete bdr_elem_dof;
delete bdr_elem_fos;
delete face_dof;
delete [] bdofs;
}
ceed::RemoveBasisAndRestriction(this);
}
void FiniteElementSpace::DestroyDoFTrans()
void FiniteElementSpace::DestroyDoFTransArray()
{
for (int i = 0; i < DoFTrans.Size(); i++)
for (int i = 0; i < DoFTransArray.Size(); i++)
{
delete DoFTrans[i];
delete DoFTransArray[i];
}
DoFTrans.SetSize(0);
DoFTransArray.SetSize(0);
}
void FiniteElementSpace::GetTransferOperator(
+56 -21
View File
@@ -271,8 +271,8 @@ protected:
int own_ext;
mutable Array<int> face_to_be; // NURBS FE space only
Array<DofTransformation*> DoFTrans;
mutable VDofTransformation VDoFTrans;
Array<StatelessDofTransformation *> DoFTransArray;
mutable DofTransformation DoFTrans;
/** Matrix representing the prolongation from the global conforming dofs to
a set of intermediate partially conforming dofs, e.g. the dofs associated
@@ -328,8 +328,8 @@ protected:
void Construct();
void Destroy();
void ConstructDoFTrans();
void DestroyDoFTrans();
void ConstructDoFTransArray();
void DestroyDoFTransArray();
void BuildElementToDofTable() const;
void BuildBdrElementToDofTable() const;
@@ -416,10 +416,10 @@ protected:
Table* old_elem_dof; // Owned.
Table* old_elem_fos; // Owned.
Array<DofTransformation*> old_DoFTrans;
mutable VDofTransformation old_VDoFTrans;
Array<StatelessDofTransformation*> old_DoFTransArray;
mutable DofTransformation old_DoFTrans;
void ConstructDoFTrans();
void ConstructDoFTransArray();
public:
/** Construct the operator based on the elem_dof table of the original
@@ -803,7 +803,16 @@ public:
/// with triangular faces.
///
/// @note The returned object should NOT be deleted by the caller.
virtual DofTransformation *GetElementDofs(int elem, Array<int> &dofs) const;
DofTransformation *GetElementDofs(int elem, Array<int> &dofs) const;
/// @brief The same as GetElementDofs(), but with a user-allocated
/// DofTransformation object. @a doftrans must be allocated in advance and
/// will be owned by the caller. The user can use the
/// DofTransformation::GetDofTransformation method on the returned
/// @a doftrans object to detect if the DofTransformation should actually be
/// used.
virtual void GetElementDofs(int elem, Array<int> &dofs,
DofTransformation &doftrans) const;
/// @brief Returns indices of degrees of freedom for boundary element 'bel'.
/// The returned indices are offsets into an @ref ldof vector. See also
@@ -817,13 +826,16 @@ public:
/// with triangular faces.
///
/// @note The returned object should NOT be deleted by the caller.
virtual DofTransformation *GetBdrElementDofs(int bel,
Array<int> &dofs) const;
DofTransformation *GetBdrElementDofs(int bel, Array<int> &dofs) const;
/** @brief Returns indices of degrees of freedom for NURBS patch index
@a patch. Cartesian ordering is used, for the tensor-product degrees of
freedom. */
void GetPatchDofs(int patch, Array<int> &dofs) const;
/// @brief The same as GetBdrElementDofs(), but with a user-allocated
/// DofTransformation object. @a doftrans must be allocated in advance and
/// will be owned by the caller. The user can use the
/// DofTransformation::GetDofTransformation method on the returned
/// @a doftrans object to detect if the DofTransformation should actually be
/// used.
virtual void GetBdrElementDofs(int bel, Array<int> &dofs,
DofTransformation &doftrans) const;
/// @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.
@@ -870,6 +882,13 @@ public:
/// GetElementInteriorVDofs().
void GetElementInteriorDofs(int i, Array<int> &dofs) const;
/// @brief Returns the number of degrees of freedom associated with the
/// interior of the specified element.
///
/// See GetElementInteriorDofs() for more information or to obtain the
/// relevant indices.
int GetNumElementInteriorDofs(int i) const;
/// @brief Returns the indices of the degrees of freedom for the interior
/// of the specified face.
///
@@ -882,13 +901,6 @@ public:
/// GetFaceInteriorVDofs().
void GetFaceInteriorDofs(int i, Array<int> &dofs) const;
/// @brief Returns the number of degrees of freedom associated with the
/// interior of the specified element.
///
/// See GetElementInteriorDofs() for more information or to obtain the
/// relevant indices.
int GetNumElementInteriorDofs(int i) const;
/// @brief Returns the indices of the degrees of freedom for the interior
/// of the specified edge.
///
@@ -897,6 +909,11 @@ public:
void GetEdgeInteriorDofs(int i, Array<int> &dofs) const;
///@}
/** @brief Returns indices of degrees of freedom for NURBS patch index
@a patch. Cartesian ordering is used, for the tensor-product degrees of
freedom. */
void GetPatchDofs(int patch, Array<int> &dofs) const;
/// @anchor dof2vdof @name DoF To VDoF Conversion methods
/// These methods convert between local dof and local vector dof using the
/// appropriate relationship based on the Ordering::Type defined in this
@@ -1023,6 +1040,15 @@ public:
/// @note The returned object should NOT be deleted by the caller.
DofTransformation *GetElementVDofs(int i, Array<int> &vdofs) const;
/// @brief The same as GetElementVDofs(), but with a user-allocated
/// DofTransformation object. @a doftrans must be allocated in advance and
/// will be owned by the caller. The user can use the
/// DofTransformation::GetDofTransformation method on the returned
/// @a doftrans object to detect if the DofTransformation should actually be
/// used.
void GetElementVDofs(int i, Array<int> &vdofs,
DofTransformation &doftrans) const;
/// @brief Returns indices of degrees of freedom for @a i'th boundary
/// element.
/// The returned indices are offsets into an @ref ldof vector with @b vdim
@@ -1038,6 +1064,15 @@ public:
/// @note The returned object should NOT be deleted by the caller.
DofTransformation *GetBdrElementVDofs(int i, Array<int> &vdofs) const;
/// @brief The same as GetBdrElementVDofs(), but with a user-allocated
/// DofTransformation object. @a doftrans must be allocated in advance and
/// will be owned by the caller. The user can use the
/// DofTransformation::GetDofTransformation method on the returned
/// @a doftrans object to detect if the DofTransformation should actually be
/// used.
void GetBdrElementVDofs(int i, Array<int> &vdofs,
DofTransformation &doftrans) const;
/// Returns indices of degrees of freedom in @a vdofs for NURBS patch @a i.
void GetPatchVDofs(int i, Array<int> &vdofs) const;
+9 -9
View File
@@ -31,13 +31,13 @@ FmsBasisTypeToMfemBasis(FmsBasisType b)
switch (b)
{
case FMS_NODAL_GAUSS_OPEN:
retval = mfem::BasisType::GaussLegendre;;
retval = mfem::BasisType::GaussLegendre;
break;
case FMS_NODAL_GAUSS_CLOSED:
retval = mfem::BasisType::GaussLobatto;;
retval = mfem::BasisType::GaussLobatto;
break;
case FMS_POSITIVE:
retval = mfem::BasisType::Positive;;
retval = mfem::BasisType::Positive;
break;
case FMS_NODAL_UNIFORM_OPEN:
retval = mfem::BasisType::OpenUniform;
@@ -1812,22 +1812,22 @@ MeshToFmsMesh(const Mesh *mmesh, FmsMesh *fmesh, FmsComponent *volume)
switch (betype)
{
case Element::POINT:
bdr_eles[FMS_VERTEX].push_back(mmesh->GetBdrElementEdgeIndex(i));
bdr_eles[FMS_VERTEX].push_back(mmesh->GetBdrElementFaceIndex(i));
break;
case Element::SEGMENT:
bdr_eles[FMS_EDGE].push_back(mmesh->GetBdrElementEdgeIndex(i));
bdr_eles[FMS_EDGE].push_back(mmesh->GetBdrElementFaceIndex(i));
break;
case Element::TRIANGLE:
bdr_eles[FMS_TRIANGLE].push_back(mmesh->GetBdrElementEdgeIndex(i));
bdr_eles[FMS_TRIANGLE].push_back(mmesh->GetBdrElementFaceIndex(i));
break;
case Element::QUADRILATERAL:
bdr_eles[FMS_QUADRILATERAL].push_back(mmesh->GetBdrElementEdgeIndex(i));
bdr_eles[FMS_QUADRILATERAL].push_back(mmesh->GetBdrElementFaceIndex(i));
break;
case Element::TETRAHEDRON:
bdr_eles[FMS_TETRAHEDRON].push_back(mmesh->GetBdrElementEdgeIndex(i));
bdr_eles[FMS_TETRAHEDRON].push_back(mmesh->GetBdrElementFaceIndex(i));
break;
case Element::HEXAHEDRON:
bdr_eles[FMS_HEXAHEDRON].push_back(mmesh->GetBdrElementEdgeIndex(i));
bdr_eles[FMS_HEXAHEDRON].push_back(mmesh->GetBdrElementFaceIndex(i));
break;
default:
MFEM_WARNING("Unsupported boundary element " << betype << " at boundary index "
+44 -113
View File
@@ -720,56 +720,6 @@ void GridFunction::GetVectorValues(int i, const IntegrationRule &ir,
GetVectorValues(*Tr, ir, vals);
}
void be_to_bfe(Geometry::Type geom, int o, const IntegrationPoint &ip,
IntegrationPoint &fip)
{
if (geom == Geometry::TRIANGLE)
{
if (o == 2)
{
fip.x = 1.0 - ip.x - ip.y;
fip.y = ip.x;
}
else if (o == 4)
{
fip.x = ip.y;
fip.y = 1.0 - ip.x - ip.y;
}
else
{
fip.x = ip.x;
fip.y = ip.y;
}
fip.z = ip.z;
}
else
{
if (o == 2)
{
fip.x = ip.y;
fip.y = 1.0 - ip.x;
}
else if (o == 4)
{
fip.x = 1.0 - ip.x;
fip.y = 1.0 - ip.y;
}
else if (o == 6)
{
fip.x = 1.0 - ip.y;
fip.y = ip.x;
}
else
{
fip.x = ip.x;
fip.y = ip.y;
}
fip.z = ip.z;
}
fip.weight = ip.weight;
fip.index = ip.index;
}
double GridFunction::GetValue(ElementTransformation &T,
const IntegrationPoint &ip,
int comp, Vector *tr) const
@@ -834,18 +784,15 @@ double GridFunction::GetValue(ElementTransformation &T,
// boundary so we'll evaluate it in the neighboring element.
FaceElementTransformations * FET =
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
MFEM_ASSERT(FET != nullptr,
"FaceElementTransformation must be valid for a boundary element");
// Boundary elements and Boundary Faces may have different
// Boundary elements and boundary faces may have different
// orientations so adjust the integration point if necessary.
int o = 0;
if (fes->GetMesh()->Dimension() == 3)
{
int f;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
}
IntegrationPoint fip;
be_to_bfe(FET->GetGeometryType(), o, ip, fip);
int f, o;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
IntegrationPoint fip =
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o, ip);
// Compute and set the point in element 1 from fip
FET->SetAllIntPoints(&fip);
@@ -973,18 +920,15 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
// the boundary so we'll evaluate it in the neighboring element.
FaceElementTransformations * FET =
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
MFEM_ASSERT(FET != nullptr,
"FaceElementTransformation must be valid for a boundary element");
// Boundary elements and Boundary Faces may have different
// Boundary elements and boundary faces may have different
// orientations so adjust the integration point if necessary.
int o = 0;
if (fes->GetMesh()->Dimension() == 3)
{
int f;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
}
IntegrationPoint fip;
be_to_bfe(FET->GetGeometryType(), o, ip, fip);
int f, o;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
IntegrationPoint fip =
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o, ip);
// Compute and set the point in element 1 from fip
FET->SetAllIntPoints(&fip);
@@ -997,6 +941,8 @@ void GridFunction::GetVectorValue(ElementTransformation &T,
{
FaceElementTransformations * FET =
dynamic_cast<FaceElementTransformations *>(&T);
MFEM_ASSERT(FET != nullptr,
"FaceElementTransformation must be valid for a boundary element");
// Evaluate in neighboring element for both continuous and
// discontinuous fields (the integration point in T1 should have
@@ -1115,11 +1061,10 @@ int GridFunction::GetFaceVectorValues(
int i, int side, const IntegrationRule &ir,
DenseMatrix &vals, DenseMatrix &tr) const
{
int n, di;
int di;
FaceElementTransformations *Transf;
n = ir.GetNPoints();
IntegrationRule eir(n); // ---
IntegrationRule eir(ir.GetNPoints()); // ---
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 0);
if (side == 2)
{
@@ -1141,12 +1086,14 @@ int GridFunction::GetFaceVectorValues(
if (di == 0)
{
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 5);
MFEM_ASSERT(Transf != nullptr, "FaceElementTransformation cannot be null!");
Transf->Loc1.Transform(ir, eir);
GetVectorValues(*Transf->Elem1, eir, vals, &tr);
}
else
{
Transf = fes->GetMesh()->GetFaceElementTransformations(i, 10);
MFEM_ASSERT(Transf != nullptr, "FaceElementTransformation cannot be null!");
Transf->Loc2.Transform(ir, eir);
GetVectorValues(*Transf->Elem2, eir, vals, &tr);
}
@@ -1504,17 +1451,13 @@ double GridFunction::GetDivergence(ElementTransformation &T) const
FaceElementTransformations * FET =
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
// Boundary elements and Boundary Faces may have different
// Boundary elements and boundary faces may have different
// orientations so adjust the integration point if necessary.
int o = 0;
if (fes->GetMesh()->Dimension() == 3)
{
int f;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
}
IntegrationPoint fip;
be_to_bfe(FET->GetGeometryType(), o, T.GetIntPoint(), fip);
int f, o;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
IntegrationPoint fip =
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o,
T.GetIntPoint());
// Compute and set the point in element 1 from fip
FET->SetAllIntPoints(&fip);
@@ -1601,17 +1544,13 @@ void GridFunction::GetCurl(ElementTransformation &T, Vector &curl) const
FaceElementTransformations * FET =
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
// Boundary elements and Boundary Faces may have different
// Boundary elements and boundary faces may have different
// orientations so adjust the integration point if necessary.
int o = 0;
if (fes->GetMesh()->Dimension() == 3)
{
int f;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
}
IntegrationPoint fip;
be_to_bfe(FET->GetGeometryType(), o, T.GetIntPoint(), fip);
int f, o;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
IntegrationPoint fip =
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o,
T.GetIntPoint());
// Compute and set the point in element 1 from fip
FET->SetAllIntPoints(&fip);
@@ -1670,17 +1609,13 @@ void GridFunction::GetGradient(ElementTransformation &T, Vector &grad) const
FaceElementTransformations * FET =
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
// Boundary elements and Boundary Faces may have different
// Boundary elements and boundary faces may have different
// orientations so adjust the integration point if necessary.
int o = 0;
if (fes->GetMesh()->Dimension() == 3)
{
int f;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
}
IntegrationPoint fip;
be_to_bfe(FET->GetGeometryType(), o, T.GetIntPoint(), fip);
int f, o;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
IntegrationPoint fip =
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o,
T.GetIntPoint());
// Compute and set the point in element 1 from fip
FET->SetAllIntPoints(&fip);
@@ -1756,17 +1691,13 @@ void GridFunction::GetVectorGradient(
FaceElementTransformations * FET =
fes->GetMesh()->GetBdrFaceTransformations(T.ElementNo);
// Boundary elements and Boundary Faces may have different
// Boundary elements and boundary faces may have different
// orientations so adjust the integration point if necessary.
int o = 0;
if (fes->GetMesh()->Dimension() == 3)
{
int f;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
}
IntegrationPoint fip;
be_to_bfe(FET->GetGeometryType(), o, T.GetIntPoint(), fip);
int f, o;
fes->GetMesh()->GetBdrElementFace(T.ElementNo, &f, &o);
IntegrationPoint fip =
Mesh::TransformBdrElementToFace(FET->GetGeometryType(), o,
T.GetIntPoint());
// Compute and set the point in element 1 from fip
FET->SetAllIntPoints(&fip);
+35 -8
View File
@@ -10,6 +10,7 @@
// CONTRIBUTING.md for details.
#include "gslib.hpp"
#include "geom.hpp"
#ifdef MFEM_USE_GSLIB
@@ -238,7 +239,8 @@ void FindPointsGSLIB::FindPoints(const Vector &point_pos,
}
// Map element number for simplices, and ref_pos from [-1,1] to [0,1] for
// both simplices and quads.
// both simplices and quads. Also sets code to 1 for points found on element
// faces/edges.
MapRefPosAndElemIndices();
}
@@ -681,6 +683,9 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
int nptorig = points_cnt,
npt = points_cnt;
// tolerance for point to be marked as on element edge/face
double btol = 1e-12;
GridFunction *gf_rst_map_temp = NULL;
int nptsend = 0;
@@ -694,7 +699,7 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
// Pack data to send via crystal router
struct gslib::array *outpt = new gslib::array;
struct out_pt { double r[3]; uint index, el, proc; };
struct out_pt { double r[3]; uint index, el, proc, code; };
struct out_pt *pt;
array_init(struct out_pt, outpt, nptsend);
outpt->n=nptsend;
@@ -712,12 +717,12 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
pt->index = index;
pt->proc = gsl_proc[index];
pt->el = gsl_elem[index];
pt->code = gsl_code[index];
++pt;
}
// Transfer data to target MPI ranks
sarray_transfer(struct out_pt, outpt, proc, 1, cr);
// Map received points
npt = outpt->n;
pt = (struct out_pt *)outpt->ptr;
@@ -731,7 +736,13 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
const Geometry::Type gt = fe->GetGeomType();
pt->el = mesh_elem;
if (gt == Geometry::SQUARE || gt == Geometry::CUBE) { ++pt; continue; }
if (gt == Geometry::SQUARE || gt == Geometry::CUBE)
{
// check if it is on element boundary
pt->code = Geometry::CheckPoint(gt, ip, -btol) ? 0 : 1;
++pt;
continue;
}
else if (gt == Geometry::TRIANGLE)
{
gf_rst_map_temp = gf_rst_map[0];
@@ -758,6 +769,10 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
{
pt->r[d] = mfem_ref(d);
}
// check if point is on element boundary
ip.Set3(&pt->r[0]);
pt->code = Geometry::CheckPoint(gt, ip, -btol) ? 0 : 1;
++pt;
}
@@ -774,6 +789,7 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
{
gsl_mfem_ref(d + pt->index*dim) = pt->r[d];
}
gsl_code[pt->index] = pt->code;
++pt;
}
array_free(outpt);
@@ -784,12 +800,22 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
{
if (gsl_code[index] != 2 && gsl_proc[index] == gsl_comm->id)
{
IntegrationPoint ip;
Vector mfem_ref(gsl_mfem_ref.GetData()+index*dim, dim);
ip.Set2(mfem_ref.GetData());
if (dim == 3) { ip.z = mfem_ref(2); }
const int elem = gsl_elem[index];
const int mesh_elem = split_element_map[elem];
const FiniteElement *fe = mesh->GetNodalFESpace()->GetFE(mesh_elem);
const Geometry::Type gt = fe->GetGeomType();
gsl_mfem_elem[index] = mesh_elem;
if (gt == Geometry::SQUARE || gt == Geometry::CUBE) { continue; }
if (gt == Geometry::SQUARE || gt == Geometry::CUBE)
{
gsl_code[index] = Geometry::CheckPoint(gt, ip, -btol) ? 0 : 1;
continue;
}
else if (gt == Geometry::TRIANGLE)
{
gf_rst_map_temp = gf_rst_map[0];
@@ -808,11 +834,12 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
}
int local_elem = split_element_index[elem];
IntegrationPoint ip;
Vector mfem_ref(gsl_mfem_ref.GetData()+index*dim, dim);
gf_rst_map_temp->GetVectorValue(local_elem, ip, mfem_ref);
// Check if the point is on element boundary
ip.Set2(mfem_ref.GetData());
if (dim == 3) { ip.z = mfem_ref(2); }
gf_rst_map_temp->GetVectorValue(local_elem, ip, mfem_ref);
gsl_code[index] = Geometry::CheckPoint(gt, ip, -btol) ? 0 : 1;
}
}
}
+1 -1
View File
@@ -128,7 +128,7 @@ void MassIntegrator::AssemblePABoundary(const FiniteElementSpace &fes)
int map_type = el.GetMapType();
dim = el.GetDim(); // Dimension of the boundary element, *not* the mesh
ne = fes.GetMesh()->GetNBE();
ne = fes.GetMesh()->GetNFbyType(FaceType::Boundary);
nq = ir->GetNPoints();
face_geom = mesh->GetFaceGeometricFactors(*ir, GeometricFactors::DETERMINANTS,
FaceType::Boundary, mt);
+1 -1
View File
@@ -137,7 +137,7 @@ bool LinearForm::SupportsDevice() const
// Make sure every boundary element corresponds to a boundary face
for (int be = 0; be < fes->GetNBE(); ++be)
{
const int f = mesh.GetBdrElementEdgeIndex(be);
const int f = mesh.GetBdrElementFaceIndex(be);
const auto face_info = mesh.GetFaceInformation(f);
if (!face_info.IsBoundary())
{
+1 -1
View File
@@ -148,7 +148,7 @@ void LinearFormExtension::Update()
std::unordered_map<int,int> f_to_be;
for (int i = 0; i < mesh.GetNBE(); ++i)
{
const int f = mesh.GetBdrElementEdgeIndex(i);
const int f = mesh.GetBdrElementFaceIndex(i);
f_to_be[f] = i;
}
MFEM_VERIFY(size_t(nf_bdr) == f_to_be.size(), "Incompatible sizes");
+1 -1
View File
@@ -395,8 +395,8 @@ void VectorBoundaryLFIntegrator::AssembleRHSElementVect(
{
const IntegrationPoint &ip = ir->IntPoint(i);
Q.Eval(vec, Tr, ip);
Tr.SetIntPoint (&ip);
Q.Eval(vec, Tr, ip);
vec *= Tr.Weight() * ip.weight;
el.CalcShape(ip, shape);
for (int k = 0; k < vdim; k++)
+8 -8
View File
@@ -257,13 +257,13 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
// vertices of the LOR mesh. The vertex coordinates are already computed in
// E-vector format and passed in in X_vert.
//
// In this function, we need to convert X_vert (which has the shape (dim,
// In this function, we need to convert X_vert (which has the shape (sdim,
// ndof_per_el, nel_ho)) to T-DOF format.
//
// We place the results in the vector xyz_tvec, which has shape (ntdofs, dim)
// We place the results in the vector xyz_tvec, which has shape (ntdofs, sdim)
// and then make the hypre vectors x, y, and z point to subvectors.
//
// In 2D, z is NULL.
// When the space dimension is 2, z is NULL.
// Create the H1 vertex space and get the element restriction
ElementDofOrdering ordering = ElementDofOrdering::LEXICOGRAPHIC;
@@ -275,17 +275,17 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
const int nel_ho = vert_fes.GetNE();
const int ndp1 = order + 1;
const int ndof_per_el = static_cast<int>(pow(ndp1, dim));
const int sdim = dim;
const int sdim = vert_fes.GetMesh()->SpaceDimension();
const int ntdofs = R->Height();
const MemoryClass mc = GetHypreMemoryClass();
bool dev = (mc == MemoryClass::DEVICE);
xyz_tvec = new Vector(ntdofs*dim);
xyz_tvec = new Vector(ntdofs*sdim);
auto xyz_tv = Reshape(HypreWrite(xyz_tvec->GetMemory()), ntdofs, dim);
auto xyz_tv = Reshape(HypreWrite(xyz_tvec->GetMemory()), ntdofs, sdim);
const auto xyz_e =
Reshape(HypreRead(X_vert.GetMemory()), dim, ndof_per_el, nel_ho);
Reshape(HypreRead(X_vert.GetMemory()), sdim, ndof_per_el, nel_ho);
const auto d_offsets = HypreRead(el_restr->Offsets().GetMemory());
const auto d_indices = HypreRead(el_restr->Indices().GetMemory());
const auto ltdof_ldof = HypreRead(R->GetMemoryJ());
@@ -309,7 +309,7 @@ void BatchedLOR_AMS::FormCoordinateVectors(const Vector &X_vert)
x = new HypreParVector(vert_fes.GetComm(), glob_size, d_x_ptr, cols, dev);
double *d_y_ptr = xyz_tv + 1*ntdofs;
y = new HypreParVector(vert_fes.GetComm(), glob_size, d_y_ptr, cols, dev);
if (dim == 3)
if (sdim == 3)
{
double *d_z_ptr = xyz_tv + 2*ntdofs;
z = new HypreParVector(vert_fes.GetComm(), glob_size, d_z_ptr, cols, dev);
+37 -31
View File
@@ -77,6 +77,7 @@ void BatchedLORAssembly::FormLORVertexCoordinates(FiniteElementSpace &fes_ho,
// Get nodal points at the LOR vertices
const int dim = mesh_ho.Dimension();
const int sdim = mesh_ho.SpaceDimension();
const int nel_ho = mesh_ho.GetNE();
const int order = fes_ho.GetMaxElementOrder();
const int nd1d = order + 1;
@@ -94,7 +95,7 @@ void BatchedLORAssembly::FormLORVertexCoordinates(FiniteElementSpace &fes_ho,
IntegrationRule ir = GetCollocatedIntRule(fes_ho);
// Map from nodal E-vector to Q-vector at the LOR vertex points
X_vert.SetSize(dim*ndof_per_el*nel_ho);
X_vert.SetSize(sdim*ndof_per_el*nel_ho);
const QuadratureInterpolator *quad_interp =
nodal_fes->GetQuadratureInterpolator(ir);
quad_interp->SetOutputLayout(QVectorLayout::byVDIM);
@@ -380,44 +381,49 @@ void BatchedLORAssembly::SparseIJToCSR(OperatorHandle &A) const
FillJAndData(*A_mat);
}
template <int ORDER, int SDIM, typename LOR_KERNEL>
static void Assemble_(LOR_KERNEL &kernel, int dim)
{
if (dim == 2) { kernel.template Assemble2D<ORDER,SDIM>(); }
else if (dim == 3) { kernel.template Assemble3D<ORDER>(); }
else { MFEM_ABORT("Unsupported dimension"); }
}
template <int ORDER, typename LOR_KERNEL>
static void Assemble_(LOR_KERNEL &kernel, int dim, int sdim)
{
if (sdim == 2) { Assemble_<ORDER,2>(kernel, dim); }
else if (sdim == 3) { Assemble_<ORDER,3>(kernel, dim); }
else { MFEM_ABORT("Unsupported space dimension."); }
}
template <typename LOR_KERNEL>
static void Assemble_(LOR_KERNEL &kernel, int dim, int sdim, int order)
{
switch (order)
{
case 1: Assemble_<1>(kernel, dim, sdim); break;
case 2: Assemble_<2>(kernel, dim, sdim); break;
case 3: Assemble_<3>(kernel, dim, sdim); break;
case 4: Assemble_<4>(kernel, dim, sdim); break;
case 5: Assemble_<5>(kernel, dim, sdim); break;
case 6: Assemble_<6>(kernel, dim, sdim); break;
case 7: Assemble_<7>(kernel, dim, sdim); break;
case 8: Assemble_<8>(kernel, dim, sdim); break;
default: MFEM_ABORT("No kernel order " << order << "!");
}
}
template <typename LOR_KERNEL>
void BatchedLORAssembly::AssemblyKernel(BilinearForm &a)
{
LOR_KERNEL kernel(a, fes_ho, X_vert, sparse_ij, sparse_mapping);
const int dim = fes_ho.GetMesh()->Dimension();
const int sdim = fes_ho.GetMesh()->SpaceDimension();
const int order = fes_ho.GetMaxElementOrder();
if (dim == 2)
{
switch (order)
{
case 1: kernel.template Assemble2D<1>(); break;
case 2: kernel.template Assemble2D<2>(); break;
case 3: kernel.template Assemble2D<3>(); break;
case 4: kernel.template Assemble2D<4>(); break;
case 5: kernel.template Assemble2D<5>(); break;
case 6: kernel.template Assemble2D<6>(); break;
case 7: kernel.template Assemble2D<7>(); break;
case 8: kernel.template Assemble2D<8>(); break;
default: MFEM_ABORT("No kernel order " << order << "!");
}
}
else if (dim == 3)
{
switch (order)
{
case 1: kernel.template Assemble3D<1>(); break;
case 2: kernel.template Assemble3D<2>(); break;
case 3: kernel.template Assemble3D<3>(); break;
case 4: kernel.template Assemble3D<4>(); break;
case 5: kernel.template Assemble3D<5>(); break;
case 6: kernel.template Assemble3D<6>(); break;
case 7: kernel.template Assemble3D<7>(); break;
case 8: kernel.template Assemble3D<8>(); break;
default: MFEM_ABORT("No kernel order " << order << "!");
}
}
Assemble_(kernel, dim, sdim, order);
}
void BatchedLORAssembly::AssembleWithoutBC(BilinearForm &a, OperatorHandle &A)
+9 -2
View File
@@ -22,15 +22,22 @@ namespace mfem
class BatchedLOR_H1 : BatchedLORKernel
{
public:
template <int ORDER> void Assemble2D();
template <int ORDER, int SDIM> void Assemble2D();
template <int ORDER> void Assemble3D();
BatchedLOR_H1(BilinearForm &a,
FiniteElementSpace &fes_ho_,
Vector &X_vert_,
Vector &sparse_ij_,
Array<int> &sparse_mapping_);
Array<int> &sparse_mapping_)
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
{
ProjectLORCoefficient<MassIntegrator>(a, c1);
ProjectLORCoefficient<DiffusionIntegrator>(a, c2);
}
};
}
#include "lor_h1_impl.hpp"
#endif
+2 -56
View File
@@ -9,7 +9,6 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "lor_h1.hpp"
#include "lor_util.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../general/forall.hpp"
@@ -17,7 +16,7 @@
namespace mfem
{
template <int ORDER>
template <int ORDER, int SDIM>
void BatchedLOR_H1::Assemble2D()
{
const int nel_ho = fes_ho.GetNE();
@@ -74,31 +73,8 @@ void BatchedLOR_H1::Assemble2D()
for (int i=0; i<sz_local_mat; ++i) { local_mat[i] = 0.0; }
double vx[4], vy[4];
LORVertexCoordinates2D<ORDER>(X, iel_ho, kx, ky, vx, vy);
SetupLORQuadData2D<ORDER,SDIM,false,false>(X, iel_ho, kx, ky, Q, false);
for (int iqy=0; iqy<2; ++iqy)
{
for (int iqx=0; iqx<2; ++iqx)
{
const double x = iqx;
const double y = iqy;
const double w = 1.0/4.0;
double J_[2*2];
DeviceTensor<2> J(J_, 2, 2);
Jacobian2D(x, y, vx, vy, J);
const double detJ = Det2D(J);
const double w_detJ = w/detJ;
Q(0,iqy,iqx) = w_detJ * (J(0,1)*J(0,1) + J(1,1)*J(1,1)); // 1,1
Q(1,iqy,iqx) = -w_detJ * (J(0,1)*J(0,0) + J(1,1)*J(1,0)); // 1,2
Q(2,iqy,iqx) = w_detJ * (J(0,0)*J(0,0) + J(1,0)*J(1,0)); // 2,2
Q(3,iqy,iqx) = w*detJ;
}
}
for (int iqx=0; iqx<2; ++iqx)
{
for (int iqy=0; iqy<2; ++iqy)
@@ -519,34 +495,4 @@ void BatchedLOR_H1::Assemble3D()
}
}
// Explicit template instantiations
template void BatchedLOR_H1::Assemble2D<1>();
template void BatchedLOR_H1::Assemble2D<2>();
template void BatchedLOR_H1::Assemble2D<3>();
template void BatchedLOR_H1::Assemble2D<4>();
template void BatchedLOR_H1::Assemble2D<5>();
template void BatchedLOR_H1::Assemble2D<6>();
template void BatchedLOR_H1::Assemble2D<7>();
template void BatchedLOR_H1::Assemble2D<8>();
template void BatchedLOR_H1::Assemble3D<1>();
template void BatchedLOR_H1::Assemble3D<2>();
template void BatchedLOR_H1::Assemble3D<3>();
template void BatchedLOR_H1::Assemble3D<4>();
template void BatchedLOR_H1::Assemble3D<5>();
template void BatchedLOR_H1::Assemble3D<6>();
template void BatchedLOR_H1::Assemble3D<7>();
template void BatchedLOR_H1::Assemble3D<8>();
BatchedLOR_H1::BatchedLOR_H1(BilinearForm &a,
FiniteElementSpace &fes_ho_,
Vector &X_vert_,
Vector &sparse_ij_,
Array<int> &sparse_mapping_)
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
{
ProjectLORCoefficient<MassIntegrator>(a, c1);
ProjectLORCoefficient<DiffusionIntegrator>(a, c2);
}
} // namespace mfem
+9 -2
View File
@@ -22,15 +22,22 @@ namespace mfem
class BatchedLOR_ND : BatchedLORKernel
{
public:
template <int ORDER> void Assemble2D();
template <int ORDER, int SDIM> void Assemble2D();
template <int ORDER> void Assemble3D();
BatchedLOR_ND(BilinearForm &a,
FiniteElementSpace &fes_ho_,
Vector &X_vert_,
Vector &sparse_ij_,
Array<int> &sparse_mapping_);
Array<int> &sparse_mapping_)
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
{
ProjectLORCoefficient<VectorFEMassIntegrator>(a, c1);
ProjectLORCoefficient<CurlCurlIntegrator>(a, c2);
}
};
}
#include "lor_nd_impl.hpp"
#endif
+2 -56
View File
@@ -9,7 +9,6 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "lor_nd.hpp"
#include "lor_util.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../general/forall.hpp"
@@ -17,7 +16,7 @@
namespace mfem
{
template <int ORDER>
template <int ORDER, int SDIM>
void BatchedLOR_ND::Assemble2D()
{
const int nel_ho = fes_ho.GetNE();
@@ -83,31 +82,8 @@ void BatchedLOR_ND::Assemble2D()
// local_mat is the local (dense) stiffness matrix
for (int i=0; i<sz_local_mat; ++i) { local_mat[i] = 0.0; }
double vx[4], vy[4];
LORVertexCoordinates2D<ORDER>(X, iel_ho, kx, ky, vx, vy);
SetupLORQuadData2D<ORDER,SDIM,false,true>(X, iel_ho, kx, ky, Q, true);
for (int iqx=0; iqx<2; ++iqx)
{
for (int iqy=0; iqy<2; ++iqy)
{
const double x = iqx;
const double y = iqy;
const double w = 1.0/4.0;
double J_[2*2];
DeviceTensor<2> J(J_, 2, 2);
Jacobian2D(x, y, vx, vy, J);
const double detJ = Det2D(J);
const double w_detJ = w/detJ;
Q(0,iqy,iqx) = w_detJ * (J(0,1)*J(0,1) + J(1,1)*J(1,1)); // 1,1
Q(1,iqy,iqx) = -w_detJ * (J(0,1)*J(0,0) + J(1,1)*J(1,0)); // 1,2
Q(2,iqy,iqx) = w_detJ * (J(0,0)*J(0,0) + J(1,0)*J(1,0)); // 2,2
Q(3,iqy,iqx) = w_detJ;
}
}
for (int iqx=0; iqx<2; ++iqx)
{
for (int iqy=0; iqy<2; ++iqy)
@@ -563,34 +539,4 @@ void BatchedLOR_ND::Assemble3D()
}
}
// Explicit template instantiations
template void BatchedLOR_ND::Assemble2D<1>();
template void BatchedLOR_ND::Assemble2D<2>();
template void BatchedLOR_ND::Assemble2D<3>();
template void BatchedLOR_ND::Assemble2D<4>();
template void BatchedLOR_ND::Assemble2D<5>();
template void BatchedLOR_ND::Assemble2D<6>();
template void BatchedLOR_ND::Assemble2D<7>();
template void BatchedLOR_ND::Assemble2D<8>();
template void BatchedLOR_ND::Assemble3D<1>();
template void BatchedLOR_ND::Assemble3D<2>();
template void BatchedLOR_ND::Assemble3D<3>();
template void BatchedLOR_ND::Assemble3D<4>();
template void BatchedLOR_ND::Assemble3D<5>();
template void BatchedLOR_ND::Assemble3D<6>();
template void BatchedLOR_ND::Assemble3D<7>();
template void BatchedLOR_ND::Assemble3D<8>();
BatchedLOR_ND::BatchedLOR_ND(BilinearForm &a,
FiniteElementSpace &fes_ho_,
Vector &X_vert_,
Vector &sparse_ij_,
Array<int> &sparse_mapping_)
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
{
ProjectLORCoefficient<VectorFEMassIntegrator>(a, c1);
ProjectLORCoefficient<CurlCurlIntegrator>(a, c2);
}
} // namespace mfem
+9 -2
View File
@@ -22,15 +22,22 @@ namespace mfem
class BatchedLOR_RT : BatchedLORKernel
{
public:
template <int ORDER> void Assemble2D();
template <int ORDER, int SDIM> void Assemble2D();
template <int ORDER> void Assemble3D();
BatchedLOR_RT(BilinearForm &a,
FiniteElementSpace &fes_ho_,
Vector &X_vert_,
Vector &sparse_ij_,
Array<int> &sparse_mapping_);
Array<int> &sparse_mapping_)
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
{
ProjectLORCoefficient<VectorFEMassIntegrator>(a, c1);
ProjectLORCoefficient<DivDivIntegrator>(a, c2);
}
};
}
#include "lor_rt_impl.hpp"
#endif
+2 -56
View File
@@ -9,7 +9,6 @@
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "lor_rt.hpp"
#include "lor_util.hpp"
#include "../../linalg/dtensor.hpp"
#include "../../general/forall.hpp"
@@ -17,7 +16,7 @@
namespace mfem
{
template <int ORDER>
template <int ORDER, int SDIM>
void BatchedLOR_RT::Assemble2D()
{
const int nel_ho = fes_ho.GetNE();
@@ -79,31 +78,8 @@ void BatchedLOR_RT::Assemble2D()
// local_mat is the local (dense) stiffness matrix
for (int i=0; i<sz_local_mat; ++i) { local_mat[i] = 0.0; }
double vx[4], vy[4];
LORVertexCoordinates2D<ORDER>(X, iel_ho, kx, ky, vx, vy);
SetupLORQuadData2D<ORDER,SDIM,true,false>(X, iel_ho, kx, ky, Q, true);
for (int iqx=0; iqx<2; ++iqx)
{
for (int iqy=0; iqy<2; ++iqy)
{
const double x = iqx;
const double y = iqy;
const double w = 1.0/4.0;
double J_[2*2];
DeviceTensor<2> J(J_, 2, 2);
Jacobian2D(x, y, vx, vy, J);
const double detJ = Det2D(J);
const double w_detJ = w/detJ;
Q(0,iqy,iqx) = w_detJ * (J(0,0)*J(0,0) + J(1,0)*J(1,0)); // 1,1
Q(1,iqy,iqx) = w_detJ * (J(0,0)*J(0,1) + J(1,0)*J(1,1)); // 1,2
Q(2,iqy,iqx) = w_detJ * (J(0,1)*J(0,1) + J(1,1)*J(1,1)); // 2,2
Q(3,iqy,iqx) = w_detJ;
}
}
for (int iqx=0; iqx<2; ++iqx)
{
for (int iqy=0; iqy<2; ++iqy)
@@ -547,34 +523,4 @@ void BatchedLOR_RT::Assemble3D()
}
}
// Explicit template instantiations
template void BatchedLOR_RT::Assemble2D<1>();
template void BatchedLOR_RT::Assemble2D<2>();
template void BatchedLOR_RT::Assemble2D<3>();
template void BatchedLOR_RT::Assemble2D<4>();
template void BatchedLOR_RT::Assemble2D<5>();
template void BatchedLOR_RT::Assemble2D<6>();
template void BatchedLOR_RT::Assemble2D<7>();
template void BatchedLOR_RT::Assemble2D<8>();
template void BatchedLOR_RT::Assemble3D<1>();
template void BatchedLOR_RT::Assemble3D<2>();
template void BatchedLOR_RT::Assemble3D<3>();
template void BatchedLOR_RT::Assemble3D<4>();
template void BatchedLOR_RT::Assemble3D<5>();
template void BatchedLOR_RT::Assemble3D<6>();
template void BatchedLOR_RT::Assemble3D<7>();
template void BatchedLOR_RT::Assemble3D<8>();
BatchedLOR_RT::BatchedLOR_RT(BilinearForm &a,
FiniteElementSpace &fes_ho_,
Vector &X_vert_,
Vector &sparse_ij_,
Array<int> &sparse_mapping_)
: BatchedLORKernel(fes_ho_, X_vert_, sparse_ij_, sparse_mapping_)
{
ProjectLORCoefficient<VectorFEMassIntegrator>(a, c1);
ProjectLORCoefficient<DivDivIntegrator>(a, c2);
}
} // namespace mfem
+107 -35
View File
@@ -20,11 +20,22 @@
namespace mfem
{
template <int ORDER>
MFEM_HOST_DEVICE inline void LORVertexCoordinates2D(
const double *X, int iel_ho, int kx, int ky, double vx[4], double vy[4])
MFEM_HOST_DEVICE inline double Det2D(DeviceMatrix &J)
{
return J(0,0)*J(1,1) - J(1,0)*J(0,1);
}
MFEM_HOST_DEVICE inline double Det3D(DeviceMatrix &J)
{
return J(0,0) * (J(1,1) * J(2,2) - J(2,1) * J(1,2)) -
J(1,0) * (J(0,1) * J(2,2) - J(2,1) * J(0,2)) +
J(2,0) * (J(0,1) * J(1,2) - J(1,1) * J(0,2));
}
template <int ORDER, int SDIM=2>
MFEM_HOST_DEVICE inline void LORVertexCoordinates2D(
const double *X, int iel_ho, int kx, int ky, double **v)
{
const int dim = 2;
const int nd1d = ORDER + 1;
const int nvert_per_el = nd1d*nd1d;
@@ -33,23 +44,31 @@ MFEM_HOST_DEVICE inline void LORVertexCoordinates2D(
const int v2 = kx + 1 + nd1d*(ky + 1);
const int v3 = kx + nd1d*(ky + 1);
const int e0 = dim*(v0 + nvert_per_el*iel_ho);
const int e1 = dim*(v1 + nvert_per_el*iel_ho);
const int e2 = dim*(v2 + nvert_per_el*iel_ho);
const int e3 = dim*(v3 + nvert_per_el*iel_ho);
const int e0 = SDIM*(v0 + nvert_per_el*iel_ho);
const int e1 = SDIM*(v1 + nvert_per_el*iel_ho);
const int e2 = SDIM*(v2 + nvert_per_el*iel_ho);
const int e3 = SDIM*(v3 + nvert_per_el*iel_ho);
// Vertex coordinates
vx[0] = X[e0 + 0];
vy[0] = X[e0 + 1];
v[0][0] = X[e0 + 0];
v[1][0] = X[e0 + 1];
vx[1] = X[e1 + 0];
vy[1] = X[e1 + 1];
v[0][1] = X[e1 + 0];
v[1][1] = X[e1 + 1];
vx[2] = X[e2 + 0];
vy[2] = X[e2 + 1];
v[0][2] = X[e2 + 0];
v[1][2] = X[e2 + 1];
vx[3] = X[e3 + 0];
vy[3] = X[e3 + 1];
v[0][3] = X[e3 + 0];
v[1][3] = X[e3 + 1];
if (SDIM == 3)
{
v[2][0] = X[e0 + 2];
v[2][1] = X[e1 + 2];
v[2][2] = X[e2 + 2];
v[2][3] = X[e3 + 2];
}
}
template <int ORDER>
@@ -112,15 +131,80 @@ MFEM_HOST_DEVICE inline void LORVertexCoordinates3D(
vz[7] = X[e7 + 2];
}
template <int SDIM=2>
MFEM_HOST_DEVICE inline void Jacobian2D(
const double x, const double y, const double vx[4], const double vy[4],
DeviceMatrix &J)
{
J(0,0) = -(1-y)*vx[0] + (1-y)*vx[1] + y*vx[2] - y*vx[3];
J(0,1) = -(1-x)*vx[0] - x*vx[1] + x*vx[2] + (1-x)*vx[3];
const double x, const double y, double **v, DeviceMatrix &J);
J(1,0) = -(1-y)*vy[0] + (1-y)*vy[1] + y*vy[2] - y*vy[3];
J(1,1) = -(1-x)*vy[0] - x*vy[1] + x*vy[2] + (1-x)*vy[3];
template <> MFEM_HOST_DEVICE inline void Jacobian2D<2>(
const double x, const double y, double **v, DeviceMatrix &J)
{
J(0,0) = -(1-y)*v[0][0] + (1-y)*v[0][1] + y*v[0][2] - y*v[0][3];
J(0,1) = -(1-x)*v[0][0] - x*v[0][1] + x*v[0][2] + (1-x)*v[0][3];
J(1,0) = -(1-y)*v[1][0] + (1-y)*v[1][1] + y*v[1][2] - y*v[1][3];
J(1,1) = -(1-x)*v[1][0] - x*v[1][1] + x*v[1][2] + (1-x)*v[1][3];
}
template <> MFEM_HOST_DEVICE inline void Jacobian2D<3>(
const double x, const double y, double **v, DeviceMatrix &J)
{
J(0,0) = -(1-y)*v[0][0] + (1-y)*v[0][1] + y*v[0][2] - y*v[0][3];
J(0,1) = -(1-x)*v[0][0] - x*v[0][1] + x*v[0][2] + (1-x)*v[0][3];
J(1,0) = -(1-y)*v[1][0] + (1-y)*v[1][1] + y*v[1][2] - y*v[1][3];
J(1,1) = -(1-x)*v[1][0] - x*v[1][1] + x*v[1][2] + (1-x)*v[1][3];
J(2,0) = -(1-y)*v[2][0] + (1-y)*v[2][1] + y*v[2][2] - y*v[2][3];
J(2,1) = -(1-x)*v[2][0] - x*v[2][1] + x*v[2][2] + (1-x)*v[2][3];
}
template <int ORDER, int SDIM, bool RT, bool ND>
MFEM_HOST_DEVICE inline void SetupLORQuadData2D(
const double *X, int iel_ho, int kx, int ky, DeviceTensor<3> &Q, bool piola)
{
double vx[4], vy[4], vz[4];
double *v[] = {vx, vy, vz};
LORVertexCoordinates2D<ORDER,SDIM>(X, iel_ho, kx, ky, v);
for (int iqy=0; iqy<2; ++iqy)
{
for (int iqx=0; iqx<2; ++iqx)
{
const double x = iqx;
const double y = iqy;
const double w = 1.0/4.0;
double J_[SDIM*2];
DeviceTensor<2> J(J_, SDIM, 2);
Jacobian2D<SDIM>(x, y, v, J);
if (SDIM == 2)
{
const double detJ = Det2D(J);
const double w_detJ = w/detJ;
const double E = J(0,0)*J(0,0) + J(1,0)*J(1,0);
const double F = J(0,0)*J(0,1) + J(1,0)*J(1,1);
const double G = J(0,1)*J(0,1) + J(1,1)*J(1,1);
Q(0,iqy,iqx) = w_detJ * (RT ? E : G); // 1,1
Q(1,iqy,iqx) = w_detJ * (RT ? F : -F); // 1,2
Q(2,iqy,iqx) = w_detJ * (RT ? G : E); // 2,2
Q(3,iqy,iqx) = (ND || RT) ? w_detJ : w*detJ;
}
else
{
const double E = J(0,0)*J(0,0) + J(1,0)*J(1,0) + J(2,0)*J(2,0);
const double F = J(0,0)*J(0,1) + J(1,0)*J(1,1) + J(2,0)*J(2,1);
const double G = J(0,1)*J(0,1) + J(1,1)*J(1,1) + J(2,1)*J(2,1);
const double detJ = sqrt(E*G - F*F);
const double w_detJ = w/detJ;
Q(0,iqy,iqx) = w_detJ * (RT ? E : G); // 1,1
Q(1,iqy,iqx) = w_detJ * (RT ? F : -F); // 1,2
Q(2,iqy,iqx) = w_detJ * (RT ? G : E); // 2,2
Q(3,iqy,iqx) = (ND || RT) ? w_detJ : w*detJ;
}
}
}
}
MFEM_HOST_DEVICE inline void Jacobian3D(
@@ -180,18 +264,6 @@ MFEM_HOST_DEVICE inline void Adjugate3D(const DeviceMatrix &J, DeviceMatrix &A)
A(2,2) = (J(0,0) * J(1,1)) - (J(0,1) * J(1,0));
}
MFEM_HOST_DEVICE inline double Det2D(DeviceMatrix &J)
{
return J(0,0)*J(1,1) - J(1,0)*J(0,1);
}
MFEM_HOST_DEVICE inline double Det3D(DeviceMatrix &J)
{
return J(0,0) * (J(1,1) * J(2,2) - J(2,1) * J(1,2)) -
J(1,0) * (J(0,1) * J(2,2) - J(2,1) * J(0,2)) +
J(2,0) * (J(0,1) * J(1,2) - J(1,1) * J(0,2));
}
}
#endif
+169 -11
View File
@@ -97,12 +97,37 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
const FiniteElement *fe;
ElementTransformation *T;
DofTransformation *doftrans;
Mesh *mesh = fes->GetMesh();
double energy = 0.0;
if (dnfi.Size())
{
// Which attributes need to be processed?
Array<int> attr_marker(mesh->attributes.Size() ?
mesh->attributes.Max() : 0);
attr_marker = 0;
for (int k = 0; k < dnfi.Size(); k++)
{
if (dnfi_marker[k] == NULL)
{
attr_marker = 1;
break;
}
Array<int> &marker = *dnfi_marker[k];
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
"invalid marker for domain integrator #"
<< k << ", counting from zero");
for (int i = 0; i < attr_marker.Size(); i++)
{
attr_marker[i] |= marker[i];
}
}
for (int i = 0; i < fes->GetNE(); i++)
{
const int attr = mesh->GetAttribute(i);
if (attr_marker[attr-1] == 0) { continue; }
fe = fes->GetFE(i);
doftrans = fes->GetElementVDofs(i, vdofs);
T = fes->GetElementTransformation(i);
@@ -110,6 +135,9 @@ double NonlinearForm::GetGridFunctionEnergy(const Vector &x) const
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
for (int k = 0; k < dnfi.Size(); k++)
{
if (dnfi_marker[k] &&
(*dnfi_marker[k])[attr-1] == 0) { continue; }
energy += dnfi[k]->GetElementEnergy(*fe, *T, el_x);
}
}
@@ -175,8 +203,32 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
if (dnfi.Size())
{
// Which attributes need to be processed?
Array<int> attr_marker(mesh->attributes.Size() ?
mesh->attributes.Max() : 0);
attr_marker = 0;
for (int k = 0; k < dnfi.Size(); k++)
{
if (dnfi_marker[k] == NULL)
{
attr_marker = 1;
break;
}
Array<int> &marker = *dnfi_marker[k];
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
"invalid marker for domain integrator #"
<< k << ", counting from zero");
for (int i = 0; i < attr_marker.Size(); i++)
{
attr_marker[i] |= marker[i];
}
}
for (int i = 0; i < fes->GetNE(); i++)
{
const int attr = mesh->GetAttribute(i);
if (attr_marker[attr-1] == 0) { continue; }
fe = fes->GetFE(i);
doftrans = fes->GetElementVDofs(i, vdofs);
T = fes->GetElementTransformation(i);
@@ -184,6 +236,9 @@ void NonlinearForm::Mult(const Vector &x, Vector &y) const
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
for (int k = 0; k < dnfi.Size(); k++)
{
if (dnfi_marker[k] &&
(*dnfi_marker[k])[attr-1] == 0) { continue; }
dnfi[k]->AssembleElementVector(*fe, *T, el_x, el_y);
if (doftrans) {doftrans->TransformDual(el_y); }
py.AddElementVector(vdofs, el_y);
@@ -322,8 +377,32 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
if (dnfi.Size())
{
// Which attributes need to be processed?
Array<int> attr_marker(mesh->attributes.Size() ?
mesh->attributes.Max() : 0);
attr_marker = 0;
for (int k = 0; k < dnfi.Size(); k++)
{
if (dnfi_marker[k] == NULL)
{
attr_marker = 1;
break;
}
Array<int> &marker = *dnfi_marker[k];
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
"invalid marker for domain integrator #"
<< k << ", counting from zero");
for (int i = 0; i < attr_marker.Size(); i++)
{
attr_marker[i] |= marker[i];
}
}
for (int i = 0; i < fes->GetNE(); i++)
{
const int attr = mesh->GetAttribute(i);
if (attr_marker[attr-1] == 0) { continue; }
fe = fes->GetFE(i);
doftrans = fes->GetElementVDofs(i, vdofs);
T = fes->GetElementTransformation(i);
@@ -331,6 +410,9 @@ Operator &NonlinearForm::GetGradient(const Vector &x) const
if (doftrans) {doftrans->InvTransformPrimal(el_x); }
for (int k = 0; k < dnfi.Size(); k++)
{
if (dnfi_marker[k] &&
(*dnfi_marker[k])[attr-1] == 0) { continue; }
dnfi[k]->AssembleElementGrad(*fe, *T, el_x, elmat);
if (doftrans) { doftrans->TransformDual(elmat); }
Grad->AddSubMatrix(vdofs, vdofs, elmat, skip_zeros);
@@ -561,13 +643,6 @@ BlockNonlinearForm::BlockNonlinearForm(Array<FiniteElementSpace *> &f) :
SetSpaces(f);
}
void BlockNonlinearForm::AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nfi,
Array<int> &bdr_attr_marker)
{
bfnfi.Append(nfi);
bfnfi_marker.Append(&bdr_attr_marker);
}
void BlockNonlinearForm::SetEssentialBC(
const Array<Array<int> *> &bdr_attr_is_ess, Array<Vector *> &rhs)
{
@@ -592,6 +667,7 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
Array<const FiniteElement *> fe(fes.Size());
ElementTransformation *T;
DofTransformation *doftrans;
Mesh *mesh = fes[0]->GetMesh();
double energy = 0.0;
for (int i=0; i<fes.Size(); ++i)
@@ -601,8 +677,33 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
}
if (dnfi.Size())
{
// Which attributes need to be processed?
Array<int> attr_marker(mesh->attributes.Size() ?
mesh->attributes.Max() : 0);
attr_marker = 0;
for (int k = 0; k < dnfi.Size(); k++)
{
if (dnfi_marker[k] == NULL)
{
attr_marker = 1;
break;
}
Array<int> &marker = *dnfi_marker[k];
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
"invalid marker for domain integrator #"
<< k << ", counting from zero");
for (int i = 0; i < attr_marker.Size(); i++)
{
attr_marker[i] |= marker[i];
}
}
for (int i = 0; i < fes[0]->GetNE(); ++i)
{
const int attr = mesh->GetAttribute(i);
if (attr_marker[attr-1] == 0) { continue; }
T = fes[0]->GetElementTransformation(i);
for (int s=0; s<fes.Size(); ++s)
{
@@ -614,9 +715,13 @@ double BlockNonlinearForm::GetEnergyBlocked(const BlockVector &bx) const
for (int k = 0; k < dnfi.Size(); ++k)
{
if (dnfi_marker[k] &&
(*dnfi_marker[k])[attr-1] == 0) { continue; }
energy += dnfi[k]->GetElementEnergy(fe, *T, el_x_const);
}
}
}
// free the allocated memory
for (int i = 0; i < fes.Size(); ++i)
@@ -656,6 +761,7 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
Array<const FiniteElement *> fe2(fes.Size());
ElementTransformation *T;
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
Mesh *mesh = fes[0]->GetMesh();
by.UseDevice(true);
by = 0.0;
@@ -670,8 +776,32 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
if (dnfi.Size())
{
// Which attributes need to be processed?
Array<int> attr_marker(mesh->attributes.Size() ?
mesh->attributes.Max() : 0);
attr_marker = 0;
for (int k = 0; k < dnfi.Size(); k++)
{
if (dnfi_marker[k] == NULL)
{
attr_marker = 1;
break;
}
Array<int> &marker = *dnfi_marker[k];
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
"invalid marker for domain integrator #"
<< k << ", counting from zero");
for (int i = 0; i < attr_marker.Size(); i++)
{
attr_marker[i] |= marker[i];
}
}
for (int i = 0; i < fes[0]->GetNE(); ++i)
{
const int attr = mesh->GetAttribute(i);
if (attr_marker[attr-1] == 0) { continue; }
T = fes[0]->GetElementTransformation(i);
for (int s = 0; s < fes.Size(); ++s)
{
@@ -683,6 +813,9 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
for (int k = 0; k < dnfi.Size(); ++k)
{
if (dnfi_marker[k] &&
(*dnfi_marker[k])[attr-1] == 0) { continue; }
dnfi[k]->AssembleElementVector(fe, *T,
el_x_const, el_y);
@@ -698,7 +831,6 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
if (fnfi.Size())
{
Mesh *mesh = fes[0]->GetMesh();
FaceElementTransformations *tr;
for (int i = 0; i < mesh->GetNumFaces(); ++i)
@@ -736,8 +868,8 @@ void BlockNonlinearForm::MultBlocked(const BlockVector &bx,
if (bfnfi.Size())
{
Mesh *mesh = fes[0]->GetMesh();
FaceElementTransformations *tr;
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
mesh->bdr_attributes.Max() : 0);
@@ -858,6 +990,7 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
Array<const FiniteElement *>fe2(fes.Size());
ElementTransformation * T;
Array<DofTransformation *> doftrans(fes.Size()); doftrans = nullptr;
Mesh *mesh = fes[0]->GetMesh();
for (int i=0; i<fes.Size(); ++i)
{
@@ -888,8 +1021,32 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
if (dnfi.Size())
{
// Which attributes need to be processed?
Array<int> attr_marker(mesh->attributes.Size() ?
mesh->attributes.Max() : 0);
attr_marker = 0;
for (int k = 0; k < dnfi.Size(); k++)
{
if (dnfi_marker[k] == NULL)
{
attr_marker = 1;
break;
}
Array<int> &marker = *dnfi_marker[k];
MFEM_ASSERT(marker.Size() == attr_marker.Size(),
"invalid marker for domain integrator #"
<< k << ", counting from zero");
for (int i = 0; i < attr_marker.Size(); i++)
{
attr_marker[i] |= marker[i];
}
}
for (int i = 0; i < fes[0]->GetNE(); ++i)
{
const int attr = mesh->GetAttribute(i);
if (attr_marker[attr-1] == 0) { continue; }
T = fes[0]->GetElementTransformation(i);
for (int s = 0; s < fes.Size(); ++s)
{
@@ -901,6 +1058,9 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
for (int k = 0; k < dnfi.Size(); ++k)
{
if (dnfi_marker[k] &&
(*dnfi_marker[k])[attr-1] == 0) { continue; }
dnfi[k]->AssembleElementGrad(fe, *T, el_x_const, elmats);
for (int j=0; j<fes.Size(); ++j)
@@ -923,7 +1083,6 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
if (fnfi.Size())
{
FaceElementTransformations *tr;
Mesh *mesh = fes[0]->GetMesh();
for (int i = 0; i < mesh->GetNumFaces(); ++i)
{
@@ -960,7 +1119,6 @@ void BlockNonlinearForm::ComputeGradientBlocked(const BlockVector &bx) const
if (bfnfi.Size())
{
FaceElementTransformations *tr;
Mesh *mesh = fes[0]->GetMesh();
// Which boundary attributes need to be processed?
Array<int> bdr_attr_marker(mesh->bdr_attributes.Size() ?
+17 -4
View File
@@ -37,6 +37,7 @@ protected:
/// Set of Domain Integrators to be assembled (added).
Array<NonlinearFormIntegrator*> dnfi; // owned
Array<Array<int>*> dnfi_marker; // not owned
/// Set of interior face Integrators to be assembled (added).
Array<NonlinearFormIntegrator*> fnfi; // owned
@@ -108,7 +109,12 @@ public:
/// Adds new Domain Integrator.
void AddDomainIntegrator(NonlinearFormIntegrator *nlfi)
{ dnfi.Append(nlfi); }
{ dnfi.Append(nlfi); dnfi_marker.Append(NULL); }
/// Adds new Domain Integrator, restricted to specific attributes.
void AddDomainIntegrator(NonlinearFormIntegrator *nlfi,
Array<int> &elem_marker)
{ dnfi.Append(nlfi); dnfi_marker.Append(&elem_marker); }
/// Access all integrators added with AddDomainIntegrator().
Array<NonlinearFormIntegrator*> *GetDNFI() { return &dnfi; }
@@ -227,13 +233,14 @@ protected:
/// Set of Domain Integrators to be assembled (added).
Array<BlockNonlinearFormIntegrator*> dnfi;
Array<Array<int>*> dnfi_marker;
/// Set of interior face Integrators to be assembled (added).
Array<BlockNonlinearFormIntegrator*> fnfi;
/// Set of Boundary Face Integrators to be assembled (added).
Array<BlockNonlinearFormIntegrator*> bfnfi;
Array<Array<int>*> bfnfi_marker;
Array<Array<int>*> bfnfi_marker;
/** Auxiliary block-vectors for wrapping input and output vectors or holding
GridFunction-like block-vector data (e.g. in parallel). */
@@ -298,7 +305,12 @@ public:
/// Adds new Domain Integrator.
void AddDomainIntegrator(BlockNonlinearFormIntegrator *nlfi)
{ dnfi.Append(nlfi); }
{ dnfi.Append(nlfi); dnfi_marker.Append(NULL); }
/// Adds new Domain Integrator, restricted to specific attributes.
void AddDomainIntegrator(BlockNonlinearFormIntegrator *nlfi,
Array<int> &elem_marker)
{ dnfi.Append(nlfi); dnfi_marker.Append(&elem_marker); }
/// Adds new Interior Face Integrator.
void AddInteriorFaceIntegrator(BlockNonlinearFormIntegrator *nlfi)
@@ -311,7 +323,8 @@ public:
/** @brief Adds new Boundary Face Integrator, restricted to specific boundary
attributes. */
void AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nlfi,
Array<int> &bdr_marker);
Array<int> &bdr_marker)
{ bfnfi.Append(nlfi); bfnfi_marker.Append(&bdr_marker); }
virtual void SetEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
Array<Vector *> &rhs);
+109 -50
View File
@@ -466,53 +466,54 @@ void ParFiniteElementSpace::ApplyLDofSigns(Table &el_dof) const
ApplyLDofSigns(all_dofs);
}
DofTransformation *
ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs) const
void ParFiniteElementSpace::GetElementDofs(int i, Array<int> &dofs,
DofTransformation &doftrans) const
{
if (elem_dof)
{
elem_dof->GetRow(i, dofs);
if (DoFTrans[mesh->GetElementBaseGeometry(i)])
if (DoFTransArray[mesh->GetElementBaseGeometry(i)])
{
Array<int> Fo;
elem_fos->GetRow(i, Fo);
DoFTrans[mesh->GetElementBaseGeometry(i)]->SetFaceOrientations(Fo);
return DoFTrans[mesh->GetElementBaseGeometry(i)];
doftrans.SetDofTransformation(
*DoFTransArray[mesh->GetElementBaseGeometry(i)]);
doftrans.SetFaceOrientations(Fo);
doftrans.SetVDim();
}
return NULL;
return;
}
DofTransformation * doftrans = FiniteElementSpace::GetElementDofs(i, dofs);
FiniteElementSpace::GetElementDofs(i, dofs, doftrans);
if (Conforming())
{
ApplyLDofSigns(dofs);
}
return doftrans;
}
DofTransformation *
ParFiniteElementSpace::GetBdrElementDofs(int i, Array<int> &dofs) const
void ParFiniteElementSpace::GetBdrElementDofs(int i, Array<int> &dofs,
DofTransformation &doftrans) const
{
if (bdr_elem_dof)
{
bdr_elem_dof->GetRow(i, dofs);
if (DoFTrans[mesh->GetBdrElementBaseGeometry(i)])
if (DoFTransArray[mesh->GetBdrElementBaseGeometry(i)])
{
Array<int> Fo;
bdr_elem_fos -> GetRow (i, Fo);
DoFTrans[mesh->GetBdrElementBaseGeometry(i)]->SetFaceOrientations(Fo);
return DoFTrans[mesh->GetBdrElementBaseGeometry(i)];
bdr_elem_fos->GetRow(i, Fo);
doftrans.SetDofTransformation(
*DoFTransArray[mesh->GetBdrElementBaseGeometry(i)]);
doftrans.SetFaceOrientations(Fo);
doftrans.SetVDim();
}
return NULL;
return;
}
DofTransformation * doftrans =
FiniteElementSpace::GetBdrElementDofs(i, dofs);
FiniteElementSpace::GetBdrElementDofs(i, dofs, doftrans);
if (Conforming())
{
ApplyLDofSigns(dofs);
}
return doftrans;
}
int ParFiniteElementSpace::GetFaceDofs(int i, Array<int> &dofs,
@@ -939,8 +940,8 @@ void ParFiniteElementSpace::Build_Dof_TrueDof_Matrix() const // matrix P
}
else if (i_offd[i+1] == i_offd[i] + 2)
{
const double * T = ND_StatelessDofTransformation
::GetFaceTransform(ltori[i]).GetData();
const double *T =
ND_DofTransformation::GetFaceTransform(ltori[i]).GetData();
j_offd[i_offd[i] + 1] = j_offd[i_offd[i]] + 1;
d_offd[i_offd[i]] = T[0]; d_offd[i_offd[i] + 1] = T[2];
i++;
@@ -1454,31 +1455,30 @@ void ParFiniteElementSpace::ExchangeFaceNbrData()
delete [] requests;
}
DofTransformation *ParFiniteElementSpace::GetFaceNbrElementVDofs(
int i, Array<int> &vdofs) const
void ParFiniteElementSpace::GetFaceNbrElementVDofs(
int i, Array<int> &vdofs, DofTransformation &doftrans) const
{
face_nbr_element_dof.GetRow(i, vdofs);
DofTransformation *doftrans = NULL;
Geometry::Type geom = GetFaceNbrFE(i)->GetGeomType();
if (DoFTrans[geom])
if (DoFTransArray[GetFaceNbrFE(i)->GetGeomType()])
{
Array<int> F, Fo;
pmesh->GetFaceNbrElementFaces(pmesh->GetNE() + i, F, Fo);
doftrans = DoFTrans[geom];
doftrans->SetFaceOrientations(Fo);
}
if (vdim == 1 || doftrans == NULL)
{
return doftrans;
}
else
{
VDoFTrans.SetDofTransformation(*doftrans);
return &VDoFTrans;
doftrans.SetDofTransformation(
*DoFTransArray[GetFaceNbrFE(i)->GetGeomType()]);
doftrans.SetFaceOrientations(Fo);
doftrans.SetVDim(vdim, ordering);
}
}
DofTransformation *ParFiniteElementSpace::GetFaceNbrElementVDofs(
int i, Array<int> &vdofs) const
{
DoFTrans.SetDofTransformation(NULL);
GetFaceNbrElementVDofs(i, vdofs, DoFTrans);
return DoFTrans.GetDofTransformation() ? &DoFTrans : NULL;
}
void ParFiniteElementSpace::GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const
{
// Works for NC mesh where 'i' is an index returned by
@@ -1946,9 +1946,8 @@ struct PMatrixRow
void AddRow(const PMatrixRow &other, double coef)
{
elems.reserve(elems.size() + other.elems.size());
for (unsigned i = 0; i < other.elems.size(); i++)
for (const PMatrixElement &oei : other.elems)
{
const PMatrixElement &oei = other.elems[i];
elems.push_back(
PMatrixElement(oei.column, oei.stride, coef * oei.value));
}
@@ -2058,7 +2057,7 @@ void NeighborRowMessage::Encode(int rank)
for (unsigned i = 0; i < rows.size(); i++)
{
const RowInfo &ri = rows[i];
const MeshId &id = pncmesh->GetNCList(ri.entity).LookUp(ri.index);
const MeshId &id = *pncmesh->GetNCList(ri.entity).GetMeshIdAndType(ri.index).id;
ent_ids[ri.entity].Append(id);
row_idx[ri.entity].Append(i);
group_ids[ri.entity].Append(ri.group);
@@ -2132,25 +2131,33 @@ void NeighborRowMessage::Decode(int rank)
rows.clear();
rows.reserve(nrows);
// read rows
// read rows ent = {0,1,2} means vertex, edge and face entity
for (int ent = 0, gi = 0; ent < 3; ent++)
{
// extract the vertex list, edge list or face list.
const Array<MeshId> &ids = ent_ids[ent];
for (int i = 0; i < ids.Size(); i++)
{
const MeshId &id = ids[i];
// read the particular element dof value off the stream.
int edof = bin_io::read<int>(stream);
// handle orientation and sign change
const int *ind = NULL;
// Handle orientation and sign change. This flips the sign on dofs
// where necessary, and for edges and faces also reorders if flipped,
// i.e. an edge with 1 -> 2 -> 3 -> 4 might become -4 -> -3 -> -2 -> -1
// This cannot treat all face dofs, as they can have rotations and
// reflections.
const int *ind = nullptr;
Geometry::Type geom = Geometry::Type::INVALID;
if (ent == 1)
{
// edge NC orientation is element defined.
int eo = pncmesh->GetEdgeNCOrientation(id);
ind = fec->DofOrderForOrientation(Geometry::SEGMENT, eo);
}
else if (ent == 2)
{
Geometry::Type geom = pncmesh->GetFaceGeometry(id.index);
geom = pncmesh->GetFaceGeometry(id.index);
int fo = pncmesh->GetFaceOrientation(id.index);
ind = fec->DofOrderForOrientation(geom, fo);
}
@@ -2165,13 +2172,14 @@ void NeighborRowMessage::Decode(int rank)
// If edof arrived with a negative index, flip it, and the scaling.
double s = (edof < 0) ? -1.0 : 1.0;
edof = (edof < 0) ? -1 - edof : edof;
if (ind && (edof = ind[edof]) < 0)
{
edof = -1 - edof;
s *= -1.0;
}
// Create a row for this entity, recording the index of the mesh
// element
rows.push_back(RowInfo(ent, id.index, edof, group_ids[gi++]));
rows.back().row.read(stream, s);
@@ -2181,6 +2189,63 @@ void NeighborRowMessage::Decode(int rank)
<< rows.back().index << ", edof " << rows.back().edof
<< std::endl;
#endif
if (ent == 2 && fec->GetContType() == FiniteElementCollection::TANGENTIAL
&& !Geometry::IsTensorProduct(geom))
{
// ND face dofs need to be processed together, as the transformation
// is given by a 2x2 matrix, so we manually apply an extra increment
// to the loop counter and add in a new row. Once these rows are
// placed, they represent the Identity transformation. To map across
// the processor boundary, we also need to apply a Primal
// Transformation (see doftrans.hpp) to a notional "global dof"
// orientation. For simplicity we perform the action of these 2x2
// matrices manually using the AddRow capability, followed by a
// Collapse.
// To perform the operations, we add and subtract initial versions
// of the rows, that represent [1 0; 0 1] in row major notation. The
// first row represents the 1 at (0,0) in [1 0; 0 1] The second row
// represents the 1 at (1,1) in [1 0; 0 1]
// We can safely bind this reference as rows was reserved above so
// there is no hidden copying that could result in a dangling
// reference.
auto &first_row = rows.back().row;
// This is the first "fundamental unit" used in the transformation.
const auto initial_first_row = first_row;
// Extract the next dof too, and apply any dof order transformation
// expected.
const MeshId &next_id = ids[++i];
const int fo = pncmesh->GetFaceOrientation(next_id.index);
ind = fec->DofOrderForOrientation(geom, fo);
edof = bin_io::read<int>(stream);
// If edof arrived with a negative index, flip it, and the scaling.
s = (edof < 0) ? -1.0 : 1.0;
edof = (edof < 0) ? -1 - edof : edof;
if (ind && (edof = ind[edof]) < 0)
{
edof = -1 - edof;
s *= -1.0;
}
rows.push_back(RowInfo(ent, next_id.index, edof, group_ids[gi++]));
rows.back().row.read(stream, s);
auto &second_row = rows.back().row;
// This is the second "fundamental unit" used in the transformation.
const auto initial_second_row = second_row;
const double *T =
ND_DofTransformation::GetFaceTransform(fo).GetData();
first_row.AddRow(initial_first_row, T[0] - 1.0);
first_row.AddRow(initial_second_row, T[2]);
second_row.AddRow(initial_first_row, T[1]);
second_row.AddRow(initial_second_row, T[3] - 1.0);
first_row.Collapse();
second_row.Collapse();
}
}
}
}
@@ -2308,12 +2373,6 @@ int ParFiniteElementSpace
Array<int> *dof_tdof,
bool partial) const
{
// TODO: general face DOF transformations in NeighborRowMessage::Decode()
MFEM_VERIFY(!(fec->GetOrder() >= 2
&& pmesh->HasGeometry(Geometry::TETRAHEDRON)
&& fec->GetContType() == FiniteElementCollection::TANGENTIAL),
"Nedelec NC tets of order >= 2 are not supported yet.");
const bool dg = (nvdofs == 0 && nedofs == 0 && nfdofs == 0);
#ifdef MFEM_PMATRIX_STATS
+37 -29
View File
@@ -190,15 +190,15 @@ private:
/// Updates the internal mesh pointer. @warning @a new_mesh must be
/// <b>topologically identical</b> to the existing mesh. Used if the address
/// of the Mesh object has changed, e.g. in @a Mesh::Swap.
virtual void UpdateMeshPointer(Mesh *new_mesh);
void UpdateMeshPointer(Mesh *new_mesh) override;
/// Copies the prolongation and restriction matrices from @a fes.
///
/// Used for low order preconditioning on non-conforming meshes. If the DOFs
/// require a permutation, it will be supplied by non-NULL @a perm. NULL @a
/// perm indicates that no permutation is required.
virtual void CopyProlongationAndRestriction(const FiniteElementSpace &fes,
const Array<int> *perm);
void CopyProlongationAndRestriction(const FiniteElementSpace &fes,
const Array<int> *perm) override;
public:
// Face-neighbor data
@@ -282,32 +282,38 @@ public:
{ return Dof_TrueDof_Matrix()->GetGlobalNumCols(); }
/// Return the number of local vector true dofs.
virtual int GetTrueVSize() const { return ltdof_size; }
int GetTrueVSize() const override { return ltdof_size; }
/// Returns indexes of degrees of freedom in array dofs for i'th element.
virtual DofTransformation *GetElementDofs(int i, Array<int> &dofs) const;
/// Returns indexes of degrees of freedom in array dofs for i'th element and
/// returns the DofTransformation data in a user-provided object.
using FiniteElementSpace::GetElementDofs;
void GetElementDofs(int i, Array<int> &dofs,
DofTransformation &doftrans) const override;
/// Returns indexes of degrees of freedom for i'th boundary element.
virtual DofTransformation *GetBdrElementDofs(int i, Array<int> &dofs) const;
/// Returns indexes of degrees of freedom for i'th boundary element and
/// returns the DofTransformation data in a user-provided object.
using FiniteElementSpace::GetBdrElementDofs;
void GetBdrElementDofs(int i, Array<int> &dofs,
DofTransformation &doftrans) const override;
/** Returns the indexes of the degrees of freedom for i'th face
including the dofs for the edges and the vertices of the face. */
virtual int GetFaceDofs(int i, Array<int> &dofs, int variant = 0) const;
int GetFaceDofs(int i, Array<int> &dofs, int variant = 0) const override;
/** Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th element in the mesh object. If @a i is greater than
or equal to the number of local mesh elements, @a i will be interpreted
as a shifted index of a face neighbor element. */
virtual const FiniteElement *GetFE(int i) const;
const FiniteElement *GetFE(int i) const override;
/** Returns an Operator that converts L-vectors to E-vectors on each face.
The parallel version is different from the serial one because of the
presence of shared faces. Shared faces are treated as interior faces,
the returned operator handles the communication needed to get the
shared face values from other MPI ranks */
virtual const FaceRestriction *GetFaceRestriction(
const FaceRestriction *GetFaceRestriction(
ElementDofOrdering f_ordering, FaceType type,
L2FaceValues mul = L2FaceValues::DoubleValued) const;
L2FaceValues mul = L2FaceValues::DoubleValued) const override;
void GetSharedEdgeDofs(int group, int ei, Array<int> &dofs) const;
void GetSharedTriangleDofs(int group, int fi, Array<int> &dofs) const;
@@ -347,15 +353,15 @@ public:
void Synchronize(Array<int> &ldof_marker) const;
/// Determine the boundary degrees of freedom
virtual void GetEssentialVDofs(const Array<int> &bdr_attr_is_ess,
Array<int> &ess_dofs,
int component = -1) const;
void GetEssentialVDofs(const Array<int> &bdr_attr_is_ess,
Array<int> &ess_dofs,
int component = -1) const override;
/** Get a list of essential true dofs, ess_tdof_list, corresponding to the
boundary attributes marked in the array bdr_attr_is_ess. */
virtual void GetEssentialTrueDofs(const Array<int> &bdr_attr_is_ess,
Array<int> &ess_tdof_list,
int component = -1);
void GetEssentialTrueDofs(const Array<int> &bdr_attr_is_ess,
Array<int> &ess_tdof_list,
int component = -1) override;
/** If the given ldof is owned by the current processor, return its local
tdof number, otherwise return -1 */
@@ -370,18 +376,20 @@ public:
HYPRE_BigInt GetMyDofOffset() const;
HYPRE_BigInt GetMyTDofOffset() const;
virtual const Operator *GetProlongationMatrix() const;
const Operator *GetProlongationMatrix() const override;
/** Get an Operator that performs the action of GetRestrictionMatrix(),
but potentially with a non-assembled optimized matrix-free
implementation. */
virtual const Operator *GetRestrictionOperator() const;
const Operator *GetRestrictionOperator() const override;
/// Get the R matrix which restricts a local dof vector to true dof vector.
virtual const SparseMatrix *GetRestrictionMatrix() const
const SparseMatrix *GetRestrictionMatrix() const override
{ Dof_TrueDof_Matrix(); return R; }
// Face-neighbor functions
void ExchangeFaceNbrData();
int GetFaceNbrVSize() const { return num_face_nbr_dofs; }
void GetFaceNbrElementVDofs(int i, Array<int> &vdofs,
DofTransformation &doftrans) const;
DofTransformation *GetFaceNbrElementVDofs(int i, Array<int> &vdofs) const;
void GetFaceNbrFaceVDofs(int i, Array<int> &vdofs) const;
const FiniteElement *GetFaceNbrFE(int i) const;
@@ -402,15 +410,15 @@ public:
// Transfer parallel true-dof data from coarse_fes, defined on a coarse mesh,
// to this FE space, defined on a refined mesh. See full documentation in the
// base class, FiniteElementSpace::GetTrueTransferOperator.
virtual void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes,
OperatorHandle &T) const;
void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes,
OperatorHandle &T) const override;
/** Reflect changes in the mesh. Calculate one of the refinement/derefinement
/rebalance matrices, unless want_transform is false. */
virtual void Update(bool want_transform = true);
void Update(bool want_transform = true) override;
/// Free ParGridFunction transformation matrix (if any), to save memory.
virtual void UpdatesFinished()
void UpdatesFinished() override
{
FiniteElementSpace::UpdatesFinished();
old_dof_offsets.DeleteAll();
@@ -442,9 +450,9 @@ public:
const GroupCommunicator &GetGroupCommunicator() const;
virtual void Mult(const Vector &x, Vector &y) const;
void Mult(const Vector &x, Vector &y) const override;
virtual void MultTranspose(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const override;
};
/// Auxiliary device class used by ParFiniteElementSpace.
@@ -493,9 +501,9 @@ public:
virtual ~DeviceConformingProlongationOperator();
virtual void Mult(const Vector &x, Vector &y) const;
void Mult(const Vector &x, Vector &y) const override;
virtual void MultTranspose(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const override;
};
}
+21 -6
View File
@@ -469,17 +469,16 @@ void ParGridFunction::GetVectorValue(ElementTransformation &T,
}
Array<int> vdofs;
DofTransformation * doftrans = pfes->GetFaceNbrElementVDofs(nbr_el_no,
vdofs);
const FiniteElement *fe = pfes->GetFaceNbrFE(nbr_el_no);
int dof = fe->GetDof();
DofTransformation * doftrans = pfes->GetFaceNbrElementVDofs(nbr_el_no, vdofs);
Vector loc_data;
face_nbr_data.GetSubVector(vdofs, loc_data);
if (doftrans)
{
doftrans->InvTransformPrimal(loc_data);
}
const FiniteElement *fe = pfes->GetFaceNbrFE(nbr_el_no);
const int dof = fe->GetDof();
if (fe->GetRangeType() == FiniteElement::SCALAR)
{
Vector shape(dof);
@@ -694,7 +693,23 @@ void ParGridFunction::ProjectBdrCoefficient(
#ifdef MFEM_DEBUG
Array<int> ess_vdofs_marker;
pfes->GetEssentialVDofs(attr, ess_vdofs_marker);
if (vcoeff) { pfes->GetEssentialVDofs(attr, ess_vdofs_marker); }
else
{
ess_vdofs_marker.SetSize(Size());
ess_vdofs_marker = 0;
for (int i = 0; i < fes->GetVDim(); i++)
{
if (!coeff[i]) { continue; }
Array<int> component_dof_marker;
pfes->GetEssentialVDofs(attr, component_dof_marker,i);
for (int j = 0; j<Size(); j++)
{
ess_vdofs_marker[j] = bool(ess_vdofs_marker[j]) ||
bool(component_dof_marker[j]);
}
}
}
for (int i = 0; i < values_counter.Size(); i++)
{
MFEM_ASSERT(pfes->GetLocalTDofNumber(i) == -1 ||
+1 -1
View File
@@ -212,7 +212,7 @@ public:
virtual double GetValue(int i, const IntegrationPoint &ip,
int vdim = 1) const;
double GetValue(ElementTransformation &T)
{ return GetValue(T.ElementNo, T.GetIntPoint()); }
{ return GetValue(T, T.GetIntPoint()); }
// Redefine to handle the case when T describes a face-neighbor element
virtual double GetValue(ElementTransformation &T, const IntegrationPoint &ip,
+9 -2
View File
@@ -164,13 +164,20 @@ int FaceQuadratureSpace::GetPermutedIndex(int idx, int iq) const
int FaceQuadratureSpace::GetEntityIndex(const ElementTransformation &T) const
{
auto get_face_index = [this](const int idx)
{
const auto it = face_indices_inv.find(idx);
if (it == face_indices_inv.end()) { return -1; }
else { return it->second; }
};
switch (T.ElementType)
{
case ElementTransformation::FACE:
return face_indices_inv.at(T.ElementNo);
return get_face_index(T.ElementNo);
case ElementTransformation::BDR_ELEMENT:
case ElementTransformation::BDR_FACE:
return face_indices_inv.at(mesh.GetBdrElementEdgeIndex(T.ElementNo));
return get_face_index(mesh.GetBdrElementFaceIndex(T.ElementNo));
default:
MFEM_ABORT("Invalid element type.");
return -1;
+2 -1
View File
@@ -87,7 +87,8 @@ public:
///
/// For a QuadratureSpace defined on elements, this just returns the element
/// index. For FaceQuadratureSpace, the returned index depends on the chosen
/// FaceType.
/// FaceType. If the entity is not found (for example, if @a T represents an
/// interior face, and the space has FaceType::Boundary) then -1 is returned.
virtual int GetEntityIndex(const ElementTransformation &T) const = 0;
/// Write the QuadratureSpace to the stream @a out.
+41 -10
View File
@@ -462,21 +462,52 @@ void TMOP_Metric_009::AssembleH(const DenseMatrix &Jpt,
ie.Assemble_ddI1b(weight, A.GetData());
}
// mu_14 = |T-I|^2
double TMOP_Metric_014::EvalWMatrixForm(const DenseMatrix &Jpt) const
{
// mu_14 = |J - I|^2.
DenseMatrix Mat(Jpt);
Mat(0,0) -= 1.0;
Mat(1,1) -= 1.0;
return Mat.FNorm2();
}
double TMOP_Metric_014::EvalW(const DenseMatrix &Jpt) const
{
MFEM_VERIFY(Jtr != NULL,
"Requires a target Jacobian, use SetTargetJacobian().");
// mu_14 = |J - I|^2 = I1[J-I].
DenseMatrix Mat(Jpt);
Mat(0,0) -= 1.0;
Mat(1,1) -= 1.0;
DenseMatrix Id(2,2);
ie.SetJacobian(Mat.GetData());
return ie.Get_I1();
}
Id(0,0) = 1; Id(0,1) = 0;
Id(1,0) = 0; Id(1,1) = 1;
void TMOP_Metric_014::EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{
// P = dI1[J-I] d/dJ[J-I] = dI1[J-I].
DenseMatrix JptMinusId = Jpt;
for (int i = 0; i < Jpt.Size(); i++)
{
JptMinusId(i, i) -= 1.0;
}
ie.SetJacobian(JptMinusId.GetData());
P = ie.Get_dI1();
}
DenseMatrix Mat(2,2);
Mat = Jpt;
Mat.Add(-1,Id);
return Mat.FNorm2();
void TMOP_Metric_014::AssembleH(const DenseMatrix &Jpt,
const DenseMatrix &DS,
const double weight,
DenseMatrix &A) const
{
// dP = ddI1[J-I].
DenseMatrix JptMinusId = Jpt;
for (int i = 0; i < Jpt.Size(); i++)
{
JptMinusId(i, i) -= 1.0;
}
ie.SetJacobian(JptMinusId.GetData());
ie.SetDerivativeMatrix(DS.Height(), DS.GetData());
ie.Assemble_ddI1(weight, A.GetData());
}
double TMOP_Metric_022::EvalW(const DenseMatrix &Jpt) const
+9 -5
View File
@@ -373,16 +373,20 @@ public:
/// 2D non-barrier Shape+Size+Orientation (VOS) metric (polyconvex).
class TMOP_Metric_014 : public TMOP_QualityMetric
{
protected:
mutable InvariantsEvaluator2D<double> ie;
public:
// W = |T-I|^2.
// W = |J - I|^2.
virtual double EvalWMatrixForm(const DenseMatrix &Jpt) const;
// W = I1[J-I].
virtual double EvalW(const DenseMatrix &Jpt) const;
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const
{ MFEM_ABORT("Not implemented"); }
virtual void EvalP(const DenseMatrix &Jpt, DenseMatrix &P) const;
virtual void AssembleH(const DenseMatrix &Jpt, const DenseMatrix &DS,
const double weight, DenseMatrix &A) const
{ MFEM_ABORT("Not implemented"); }
const double weight, DenseMatrix &A) const;
};
/// 2D Shifted barrier form of shape metric (mu_2).
+12
View File
@@ -26,6 +26,10 @@
#include "sort_pairs.hpp"
#include "globals.hpp"
#ifdef MFEM_USE_STRUMPACK
#include <StrumpackConfig.hpp> // STRUMPACK_USE_PTSCOTCH, etc.
#endif
#include <iostream>
#include <map>
@@ -34,6 +38,14 @@ using namespace std;
namespace mfem
{
#if defined(MFEM_USE_STRUMPACK) && \
(defined(STRUMPACK_USE_PTSCOTCH) || defined(STRUMPACK_USE_SLATE_SCALAPACK))
int Mpi::default_thread_required = MPI_THREAD_MULTIPLE;
#else
int Mpi::default_thread_required = MPI_THREAD_SINGLE;
#endif
GroupTopology::GroupTopology(const GroupTopology &gt)
: MyComm(gt.MyComm),
group_lproc(gt.group_lproc)
+36 -14
View File
@@ -22,7 +22,6 @@
#include "globals.hpp"
#include <mpi.h>
namespace mfem
{
@@ -32,10 +31,34 @@ namespace mfem
class Mpi
{
public:
/// Singleton creation with Mpi::Init();
static void Init() { Init_(NULL, NULL); }
/// Singleton creation with Mpi::Init(argc,argv);
static void Init(int &argc, char **&argv) { Init_(&argc, &argv); }
/// Singleton creation with Mpi::Init(argc, argv).
static void Init(int &argc, char **&argv,
int required = default_thread_required,
int *provided = nullptr)
{ Init(&argc, &argv, required, provided); }
/// Singleton creation with Mpi::Init().
static void Init(int *argc = nullptr, char ***argv = nullptr,
int required = default_thread_required,
int *provided = nullptr)
{
MFEM_VERIFY(!IsInitialized(), "MPI already initialized!");
if (required == MPI_THREAD_SINGLE)
{
int mpi_err = MPI_Init(argc, argv);
MFEM_VERIFY(!mpi_err, "error in MPI_Init()!");
if (provided) { *provided = MPI_THREAD_SINGLE; }
}
else
{
int mpi_provided;
int mpi_err = MPI_Init_thread(argc, argv, required, &mpi_provided);
MFEM_VERIFY(!mpi_err, "error in MPI_Init()!");
if (provided) { *provided = mpi_provided; }
}
// The Mpi singleton object below needs to be created after MPI_Init() for
// some MPI implementations.
Singleton();
}
/// Finalize MPI (if it has been initialized and not yet already finalized).
static void Finalize()
{
@@ -71,20 +94,19 @@ public:
}
/// Return true if the rank in MPI_COMM_WORLD is zero.
static bool Root() { return WorldRank() == 0; }
/// Default level of thread support for MPI_Init_thread.
static MFEM_EXPORT int default_thread_required;
private:
/// Initialize MPI
static void Init_(int *argc, char ***argv)
/// Initialize the Mpi singleton.
static Mpi &Singleton()
{
MFEM_VERIFY(!IsInitialized(), "MPI already initialized!")
MPI_Init(argc, argv);
// The "mpi" object below needs to be created after MPI_Init() for some
// MPI implementations
static Mpi mpi;
return mpi;
}
/// Finalize MPI
/// Finalize MPI.
~Mpi() { Finalize(); }
/// Prevent direct construction of objects of this class
Mpi() { }
/// Prevent direct construction of objects of this class.
Mpi() {}
};
/** @brief A simple convenience class based on the Mpi singleton class above.
+50 -31
View File
@@ -16,13 +16,13 @@
#include <cstdlib>
#include <errno.h>
#ifndef _WIN32
#include <netinet/in.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#else
#include <winsock.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#ifdef _MSC_VER
typedef int ssize_t;
// Link with ws2_32.lib
@@ -51,47 +51,66 @@ int isockstream::establish()
{
// char myname[129];
char myname[] = "localhost";
int port;
struct sockaddr_in sa;
struct hostent *hp;
int sfd;
struct addrinfo hints, *res, *rp;
memset(&sa, 0, sizeof(struct sockaddr_in));
// gethostname(myname, 128);
hp= gethostbyname(myname);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
if (hp == NULL)
int s = getaddrinfo(myname, NULL, &hints, &res);
if (s != 0)
{
mfem::err << "isockstream::establish(): gethostbyname() failed!\n"
<< "isockstream::establish(): gethostname() returned: '"
mfem::err << "isockstream::establish(): getaddrinfo() failed!\n"
<< "isockstream::establish(): getaddrinfo() returned: '"
<< myname << "'" << endl;
error = 1;
return (-1);
}
sa.sin_family= hp->h_addrtype;
sa.sin_port= htons(portnum);
if ((port = socket(AF_INET, SOCK_STREAM, 0)) < 0)
// loop the list of address structures returned by getaddrinfo()
for (rp = res; rp != NULL; rp = rp->ai_next)
{
mfem::err << "isockstream::establish(): socket() failed!" << endl;
error = 2;
if ((sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
{
mfem::err << "isockstream::establish(): socket() failed!" << endl;
error = 2;
return (-1);
}
int on = 1;
if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
{
mfem::err << "isockstream::establish(): setsockopt() failed!" << endl;
return (-1);
}
#if defined(__APPLE__)
if (bind(sfd, (const struct sockaddr *)rp->ai_addr, rp->ai_addrlen) < 0)
#else
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) < 0)
#endif
{
mfem::err << "isockstream::establish(): bind() failed!" << endl;
close(sfd);
error = 3;
continue;
}
break;
}
// No address succeeded
if (rp == NULL)
{
mfem::err << "Could not bind\n";
return (-1);
}
int on=1;
setsockopt(port, SOL_SOCKET, SO_REUSEADDR, (char *)(&on), sizeof(on));
if (bind(port,(const sockaddr*)&sa,(socklen_t)sizeof(struct sockaddr_in)) < 0)
{
mfem::err << "isockstream::establish(): bind() failed!" << endl;
close(port);
error = 3;
return (-1);
}
listen(port, 4);
error = 0;
return (port);
freeaddrinfo(res);
listen(sfd, 4);
return (sfd);
}
int isockstream::read_data(int s, char *buf, int n)
+35 -31
View File
@@ -19,15 +19,15 @@
#include <cstring> // memset, memcpy, strerror
#include <cerrno> // errno
#ifndef _WIN32
#include <netdb.h> // gethostbyname
#include <netdb.h> // getaddrinfo
#include <arpa/inet.h> // htons
#include <sys/types.h> // socket, setsockopt, connect, recv, send
#include <sys/socket.h> // socket, setsockopt, connect, recv, send
#include <unistd.h> // close
#include <netinet/in.h> // sockaddr_in
#define closesocket (::close)
#else
#include <winsock.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#ifdef _MSC_VER
typedef int ssize_t;
// Link with ws2_32.lib
@@ -93,8 +93,7 @@ int socketbuf::attach(int sd)
int socketbuf::open(const char hostname[], int port)
{
struct sockaddr_in sa;
struct hostent *hp;
struct addrinfo hints, *res, *rp;
if (!wsInit_.Initialized())
{
@@ -105,42 +104,47 @@ int socketbuf::open(const char hostname[], int port)
setg(NULL, NULL, NULL);
setp(obuf, obuf + buflen);
hp = gethostbyname(hostname);
if (hp == NULL)
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
int s = getaddrinfo(hostname, NULL, &hints, &res);
if (s != 0)
{
socket_descriptor = -3;
return -1;
}
memset(&sa, 0, sizeof(sa));
memcpy((char *)&sa.sin_addr, hp->h_addr, hp->h_length);
sa.sin_family = hp->h_addrtype;
sa.sin_port = htons(port);
socket_descriptor = socket(hp->h_addrtype, SOCK_STREAM, 0);
if (socket_descriptor < 0)
for (rp = res; rp != NULL; rp = rp->ai_next)
{
return -1;
}
socket_descriptor = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (socket_descriptor < 0)
{
continue;
}
#if defined __APPLE__
// OS X does not support the MSG_NOSIGNAL option of send().
// Instead we can use the SO_NOSIGPIPE socket option.
int on = 1;
if (setsockopt(socket_descriptor, SOL_SOCKET, SO_NOSIGPIPE,
(char *)(&on), sizeof(on)) < 0)
{
closesocket(socket_descriptor);
socket_descriptor = -2;
return -1;
}
// OS X does not support the MSG_NOSIGNAL option of send().
// Instead we can use the SO_NOSIGPIPE socket option.
int on = 1;
if (setsockopt(socket_descriptor, SOL_SOCKET, SO_NOSIGPIPE,
&on, sizeof(on)) < 0)
{
closesocket(socket_descriptor);
socket_descriptor = -2;
return -1;
}
#endif
if (connect(socket_descriptor,
(const struct sockaddr *)&sa, sizeof(sa)) < 0)
{
closesocket(socket_descriptor);
socket_descriptor = -2;
return -1;
if (connect(socket_descriptor, rp->ai_addr, rp->ai_addrlen) < 0)
{
closesocket(socket_descriptor);
socket_descriptor = -2;
continue;
}
}
freeaddrinfo(res);
return 0;
}
+2 -1
View File
@@ -218,7 +218,8 @@ void Table::SetIJ(int *newI, int *newJ, int newsize)
int Table::Push(int i, int j)
{
MFEM_ASSERT( i >=0 && i<size, "Index out of bounds. i = "<<i);
MFEM_ASSERT(i >=0 &&
i<size, "Index out of bounds. i = " << i << " size " << size);
for (int k = I[i], end = I[i+1]; k < end; k++)
{
+252 -15
View File
@@ -21,6 +21,7 @@
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <cstring>
namespace mfem
{
@@ -28,8 +29,14 @@ namespace mfem
namespace Ginkgo
{
// Create a GinkgoExecutor of type exec_type.
GinkgoExecutor::GinkgoExecutor(ExecType exec_type)
{
#if defined(MFEM_USE_CUDA) || defined(MFEM_USE_HIP)
gko::version_info gko_version = gko::version_info::get();
bool gko_with_omp_support = (strcmp(gko_version.omp_version.tag,
"not compiled") != 0);
#endif
switch (exec_type)
{
case GinkgoExecutor::REFERENCE:
@@ -49,13 +56,23 @@ GinkgoExecutor::GinkgoExecutor(ExecType exec_type)
#ifdef MFEM_USE_CUDA
int current_device = 0;
MFEM_GPU_CHECK(cudaGetDevice(&current_device));
executor = gko::CudaExecutor::create(current_device,
gko::OmpExecutor::create());
if (gko_with_omp_support)
{
executor = gko::CudaExecutor::create(current_device,
gko::OmpExecutor::create());
}
else
{
executor = gko::CudaExecutor::create(current_device,
gko::ReferenceExecutor::create());
}
#endif
}
else
{
MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
"any valid devices.");
}
break;
}
case GinkgoExecutor::HIP:
@@ -65,24 +82,111 @@ GinkgoExecutor::GinkgoExecutor(ExecType exec_type)
#ifdef MFEM_USE_HIP
int current_device = 0;
MFEM_GPU_CHECK(hipGetDevice(&current_device));
executor = gko::HipExecutor::create(current_device,
gko::OmpExecutor::create());
if (gko_with_omp_support)
{
executor = gko::HipExecutor::create(current_device,
gko::OmpExecutor::create());
}
else
{
executor = gko::HipExecutor::create(current_device,
gko::ReferenceExecutor::create());
}
#endif
}
else
mfem::err << "gko::HipExecutor::get_num_devices() did not report "
<< "any valid devices" << std::endl;
{
MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
"any valid devices.");
}
break;
}
default:
mfem::err << "Invalid ExecType specified" << std::endl;
MFEM_ABORT("Invalid ExecType specified");
}
}
// Create a GinkgoExecutor of type exec_type, with host_exec_type for the
// related CPU Executor (only applicable to GPU backends).
GinkgoExecutor::GinkgoExecutor(ExecType exec_type, ExecType host_exec_type)
{
switch (exec_type)
{
case GinkgoExecutor::REFERENCE:
{
MFEM_WARNING("Parameter host_exec_type ignored for CPU GinkgoExecutor.");
executor = gko::ReferenceExecutor::create();
break;
}
case GinkgoExecutor::OMP:
{
MFEM_WARNING("Parameter host_exec_type ignored for CPU GinkgoExecutor.");
executor = gko::OmpExecutor::create();
break;
}
case GinkgoExecutor::CUDA:
{
if (gko::CudaExecutor::get_num_devices() > 0)
{
#ifdef MFEM_USE_CUDA
int current_device = 0;
MFEM_GPU_CHECK(cudaGetDevice(&current_device));
if (host_exec_type == GinkgoExecutor::OMP)
{
executor = gko::CudaExecutor::create(current_device,
gko::OmpExecutor::create());
}
else
{
executor = gko::CudaExecutor::create(current_device,
gko::ReferenceExecutor::create());
}
#endif
}
else
{
MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
"any valid devices.");
}
break;
}
case GinkgoExecutor::HIP:
{
if (gko::HipExecutor::get_num_devices() > 0)
{
#ifdef MFEM_USE_HIP
int current_device = 0;
MFEM_GPU_CHECK(hipGetDevice(&current_device));
if (host_exec_type == GinkgoExecutor::OMP)
{
executor = gko::HipExecutor::create(current_device,
gko::OmpExecutor::create());
}
else
{
executor = gko::HipExecutor::create(current_device,
gko::ReferenceExecutor::create());
}
#endif
}
else
{
MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
"any valid devices.");
}
break;
}
default:
MFEM_ABORT("Invalid ExecType specified");
}
}
// Create a GinkgoExecutor to match MFEM's device configuration.
GinkgoExecutor::GinkgoExecutor(Device &mfem_device)
{
// Pick "best match" Executor based on MFEM device configuration.
gko::version_info gko_version = gko::version_info::get();
bool gko_with_omp_support = (strcmp(gko_version.omp_version.tag,
"not compiled") != 0);
if (mfem_device.Allows(Backend::CUDA_MASK))
{
if (gko::CudaExecutor::get_num_devices() > 0)
@@ -90,13 +194,23 @@ GinkgoExecutor::GinkgoExecutor(Device &mfem_device)
#ifdef MFEM_USE_CUDA
int current_device = 0;
MFEM_GPU_CHECK(cudaGetDevice(&current_device));
executor = gko::CudaExecutor::create(current_device,
gko::OmpExecutor::create());
if (gko_with_omp_support)
{
executor = gko::CudaExecutor::create(current_device,
gko::OmpExecutor::create());
}
else
{
executor = gko::CudaExecutor::create(current_device,
gko::ReferenceExecutor::create());
}
#endif
}
else
{
MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
"any valid devices.");
}
}
else if (mfem_device.Allows(Backend::HIP_MASK))
{
@@ -105,16 +219,123 @@ GinkgoExecutor::GinkgoExecutor(Device &mfem_device)
#ifdef MFEM_USE_HIP
int current_device = 0;
MFEM_GPU_CHECK(hipGetDevice(&current_device));
executor = gko::HipExecutor::create(current_device, gko::OmpExecutor::create());
if (gko_with_omp_support)
{
executor = gko::HipExecutor::create(current_device,
gko::OmpExecutor::create());
}
else
{
executor = gko::HipExecutor::create(current_device,
gko::ReferenceExecutor::create());
}
#endif
}
else
{
MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
"any valid devices.");
}
}
else
{
executor = gko::OmpExecutor::create();
if (mfem_device.Allows(Backend::OMP_MASK))
{
// Also use OpenMP for Ginkgo, if Ginkgo supports it
if (gko_with_omp_support)
{
executor = gko::OmpExecutor::create();
}
else
{
executor = gko::ReferenceExecutor::create();
}
}
else
{
executor = gko::ReferenceExecutor::create();
}
}
}
// Create a GinkgoExecutor to match MFEM's device configuration, with
// a specific host_exec_type for the associated CPU Executor (only
// applicable to GPU backends).
GinkgoExecutor::GinkgoExecutor(Device &mfem_device, ExecType host_exec_type)
{
if (mfem_device.Allows(Backend::CUDA_MASK))
{
if (gko::CudaExecutor::get_num_devices() > 0)
{
#ifdef MFEM_USE_CUDA
int current_device = 0;
MFEM_GPU_CHECK(cudaGetDevice(&current_device));
if (host_exec_type == GinkgoExecutor::OMP)
{
executor = gko::CudaExecutor::create(current_device,
gko::OmpExecutor::create());
}
else
{
executor = gko::CudaExecutor::create(current_device,
gko::ReferenceExecutor::create());
}
#endif
}
else
{
MFEM_ABORT("gko::CudaExecutor::get_num_devices() did not report "
"any valid devices.");
}
}
else if (mfem_device.Allows(Backend::HIP_MASK))
{
if (gko::HipExecutor::get_num_devices() > 0)
{
#ifdef MFEM_USE_HIP
int current_device = 0;
MFEM_GPU_CHECK(hipGetDevice(&current_device));
if (host_exec_type == GinkgoExecutor::OMP)
{
executor = gko::HipExecutor::create(current_device,
gko::OmpExecutor::create());
}
else
{
executor = gko::HipExecutor::create(current_device,
gko::ReferenceExecutor::create());
}
#endif
}
else
{
MFEM_ABORT("gko::HipExecutor::get_num_devices() did not report "
"any valid devices.");
}
}
else
{
MFEM_WARNING("Parameter host_exec_type ignored for CPU GinkgoExecutor.");
if (mfem_device.Allows(Backend::OMP_MASK))
{
// Also use OpenMP for Ginkgo, if Ginkgo supports it
gko::version_info gko_version = gko::version_info::get();
bool gko_with_omp_support = (strcmp(gko_version.omp_version.tag,
"not compiled") != 0);
if (gko_with_omp_support)
{
executor = gko::OmpExecutor::create();
}
else
{
executor = gko::ReferenceExecutor::create();
}
}
else
{
executor = gko::ReferenceExecutor::create();
}
}
}
@@ -195,7 +416,7 @@ const
gko::log::Logger::criterion_check_completed_mask);
#endif
residual_logger = std::make_shared<ResidualLogger<>>(executor,
gko::lend(system_oper),b);
system_oper.get(),b);
}
@@ -334,7 +555,7 @@ GinkgoIterativeSolver::Mult(const Vector &x, Vector &y) const
// Create the logger object to log some data from the solvers to confirm
// convergence.
initialize_ginkgo_log(gko::lend(gko_x));
initialize_ginkgo_log(gko_x.get());
MFEM_VERIFY(convergence_logger, "convergence logger not initialized" );
if (print_level==1)
@@ -350,7 +571,11 @@ GinkgoIterativeSolver::Mult(const Vector &x, Vector &y) const
combined_factory->add_logger(convergence_logger);
// Finally, apply the solver to x and get the solution in y.
#if MFEM_GINKGO_VERSION < 10600
solver->apply(gko::lend(gko_x), gko::lend(gko_y));
#else
solver->apply(gko_x, gko_y);
#endif
// Get the number of iterations taken to converge to the solution.
final_iter = convergence_logger->get_num_iterations();
@@ -467,6 +692,10 @@ void GinkgoIterativeSolver::SetOperator(const Operator &op)
new OperatorWrapper(executor, op.Height(), &op));
}
// Set MFEM Solver size values
height = op.Height();
width = op.Width();
// Generate the solver from the solver using the system matrix or operator.
solver = solver_gen->generate(system_oper);
}
@@ -878,7 +1107,11 @@ GinkgoPreconditioner::Mult(const Vector &x, Vector &y) const
gko_array<double>::view(executor,
y.Size(),
y.ReadWrite(on_device)), 1);
#if MFEM_GINKGO_VERSION < 10600
generated_precond.get()->apply(gko::lend(gko_x), gko::lend(gko_y));
#else
generated_precond.get()->apply(gko_x, gko_y);
#endif
}
void GinkgoPreconditioner::SetOperator(const Operator &op)
@@ -917,6 +1150,10 @@ void GinkgoPreconditioner::SetOperator(const Operator &op)
generated_precond = precond_gen->generate(gko::give(gko_matrix));
has_generated_precond = true;
// Set MFEM Solver size values
height = op.Height();
width = op.Width();
}
+143 -75
View File
@@ -265,9 +265,13 @@ double compute_norm(const gko::matrix::Dense<ValueType> *b)
// Initialize a result scalar containing the value 0.0.
auto b_norm = gko::initialize<gko::matrix::Dense<ValueType>>({0.0}, exec);
// Use the dense `compute_norm2` function to compute the norm.
b->compute_norm2(lend(b_norm));
#if MFEM_GINKGO_VERSION < 10600
b->compute_norm2(gko::lend(b_norm));
#else
b->compute_norm2(b_norm);
#endif
// Use the other utility function to return the norm contained in `b_norm``
return std::pow(get_norm(lend(b_norm)),2);
return std::pow(get_norm(b_norm.get()),2);
}
/**
@@ -319,84 +323,43 @@ struct ResidualLogger : gko::log::Logger
using gko_dense = gko::matrix::Dense<ValueType>;
// Customize the logging hook which is called every time an iteration is
// completed
void on_iteration_complete(const gko::LinOp *,
const gko::size_type &iteration,
const gko::LinOp *residual,
const gko::LinOp *solution,
const gko::LinOp *residual_norm,
const gko::LinOp *implicit_sq_residual_norm) const override
{
// If the solver shares the current solution vector and we want to
// compute the residual from that
if (solution && compute_real_residual)
{
// Store the matrix's executor
auto exec = matrix->get_executor();
// Compute the real residual vector by calling apply on the system
// First, compute res = A * x
matrix->apply(gko::lend(solution), gko::lend(res));
// Now do res = res - b, depending on which vector/oper type
// Check if b is a Ginkgo vector or wrapped MFEM Vector
if (dynamic_cast<const VectorWrapper*>(b))
{
const VectorWrapper *b_cast = gko::as<const VectorWrapper>(b);
// Copy the MFEM Vector stored in b
VectorWrapper *res_cast = gko::as<VectorWrapper>(res);
res_cast->get_mfem_vec_ref() -= b_cast->get_mfem_vec_const_ref();
}
else
{
// Create a scalar containing the value -1.0
auto neg_one = gko::initialize<gko_dense>({-1.0}, exec);
res->add_scaled(gko::lend(neg_one), gko::lend(b));
}
// Compute the norm of the residual vector and add it to the
// `residual_norms` vector
residual_norms.push_back(compute_norm(gko::lend(res)));
}
else
{
// If the solver shares an implicit or recurrent residual norm, log its value
if (implicit_sq_residual_norm)
{
auto dense_norm = gko::as<gko_dense>(implicit_sq_residual_norm);
// Add the norm to the `residual_norms` vector
residual_norms.push_back(get_norm(dense_norm));
// Otherwise, use the recurrent residual vector
}
else if (residual_norm)
{
auto dense_norm = gko::as<gko_dense>(residual_norm);
// Add the norm to the `residual_norms` vector
residual_norms.push_back(get_norm(dense_norm));
// Otherwise, use the recurrent residual vector
}
else
{
auto dense_residual = gko::as<gko_dense>(residual);
// Compute the residual vector's norm
auto norm = compute_norm(gko::lend(dense_residual));
// Add the computed norm to the `residual_norms` vector
residual_norms.push_back(norm);
}
}
// Add the current iteration number to the `iterations` vector
iterations.push_back(iteration);
}
// Version for solver that doesn't log implicit res norm
// Ginkgo 1.5 and older: version for solver that doesn't log implicit res norm
void on_iteration_complete(const gko::LinOp *op,
const gko::size_type &iteration,
const gko::LinOp *residual,
const gko::LinOp *solution,
const gko::LinOp *residual_norm) const override
{
on_iteration_complete(op, iteration, residual, solution, residual_norm,
nullptr);
iteration_complete_core(iteration, residual, solution, residual_norm,
nullptr);
}
// Ginkgo 1.5 and older: version with implicit residual norm
void on_iteration_complete(const gko::LinOp *op,
const gko::size_type &iteration,
const gko::LinOp *residual,
const gko::LinOp *solution,
const gko::LinOp *residual_norm,
const gko::LinOp *implicit_sq_residual_norm) const override
{
iteration_complete_core(iteration, residual, solution, residual_norm,
implicit_sq_residual_norm);
}
#if MFEM_GINKGO_VERSION > 10500
// Ginkgo 1.6 and newer
void on_iteration_complete(const gko::LinOp *op,
const gko::LinOp *rhs,
const gko::LinOp *solution,
const gko::size_type &iteration,
const gko::LinOp *residual,
const gko::LinOp *residual_norm,
const gko::LinOp *implicit_sq_residual_norm,
const gko::array<gko::stopping_status>* status,
bool stopped) const override
{
iteration_complete_core(iteration, residual, solution, residual_norm,
implicit_sq_residual_norm);
}
#endif
// Construct the logger and store the system matrix and b vectors
ResidualLogger(std::shared_ptr<const gko::Executor> exec,
@@ -428,6 +391,89 @@ struct ResidualLogger : gko::log::Logger
}
private:
// Customize the logging hook which is called every time an iteration is
// completed.
void iteration_complete_core(const gko::size_type &iteration,
const gko::LinOp *residual,
const gko::LinOp *solution,
const gko::LinOp *residual_norm,
const gko::LinOp *implicit_sq_residual_norm) const
{
// If the solver shares the current solution vector and we want to
// compute the residual from that
if (solution && compute_real_residual)
{
// Store the matrix's executor
auto exec = matrix->get_executor();
// Compute the real residual vector by calling apply on the system
// First, compute res = A * x
#if MFEM_GINKGO_VERSION < 10600
matrix->apply(gko::lend(solution), gko::lend(res));
#else
matrix->apply(solution, res);
#endif
// Now do res = res - b, depending on which vector/oper type
// Check if b is a Ginkgo vector or wrapped MFEM Vector
if (dynamic_cast<const VectorWrapper*>(b))
{
const VectorWrapper *b_cast = gko::as<const VectorWrapper>(b);
// Copy the MFEM Vector stored in b
VectorWrapper *res_cast = gko::as<VectorWrapper>(res);
res_cast->get_mfem_vec_ref() -= b_cast->get_mfem_vec_const_ref();
}
else
{
// Create a scalar containing the value -1.0
auto neg_one = gko::initialize<gko_dense>({-1.0}, exec);
#if MFEM_GINKGO_VERSION < 10600
res->add_scaled(gko::lend(neg_one), gko::lend(b));
#else
res->add_scaled(neg_one, b);
#endif
}
// Compute the norm of the residual vector and add it to the
// `residual_norms` vector
#if MFEM_GINKGO_VERSION < 10600
residual_norms.push_back(compute_norm(gko::lend(res)));
#else
residual_norms.push_back(compute_norm(res));
#endif
}
else
{
// If the solver shares an implicit or recurrent residual norm, log its value
if (implicit_sq_residual_norm)
{
auto dense_norm = gko::as<gko_dense>(implicit_sq_residual_norm);
// Add the norm to the `residual_norms` vector
residual_norms.push_back(get_norm(dense_norm));
// Otherwise, use the recurrent residual vector
}
else if (residual_norm)
{
auto dense_norm = gko::as<gko_dense>(residual_norm);
// Add the norm to the `residual_norms` vector
residual_norms.push_back(get_norm(dense_norm));
// Otherwise, use the recurrent residual vector
}
else
{
auto dense_residual = gko::as<gko_dense>(residual);
// Compute the residual vector's norm
#if MFEM_GINKGO_VERSION < 10600
auto norm = compute_norm(gko::lend(dense_residual));
#else
auto norm = compute_norm(dense_residual);
#endif
// Add the computed norm to the `residual_norms` vector
residual_norms.push_back(norm);
}
}
// Add the current iteration number to the `iterations` vector
iterations.push_back(iteration);
}
// Pointer to the system matrix
const gko::LinOp *matrix;
// Pointer to the right hand sides
@@ -469,18 +515,40 @@ public:
/**
* Constructor.
* Takes an @p GinkgoExecType argument and creates an Executor.
* In Ginkgo, GPU Executors must have an associated host Executor.
* This routine will select a CPU Executor based on the OpenMP support
* for Ginkgo.
*/
GinkgoExecutor(ExecType exec_type);
/**
* Constructor.
* Takes an @p GinkgoExecType argument and creates an Executor.
* In Ginkgo, GPU Executors must have an associated host Executor.
* This routine allows for explicite setting of the CPU Executor
* for GPU backends.
*/
GinkgoExecutor(ExecType exec_type, ExecType host_exec_type);
/**
* Constructor.
* Takes an MFEM @p Device object and creates an Executor
* that "matches" (e.g., if MFEM is using the CPU, Ginkgo
* will choose the OmpExecutor; if MFEM is using CUDA,
* Ginkgo will choose the CudaExecutor).
* will choose the Reference or OmpExecutor based on MFEM's
* configuration and Ginkgo's capabilities; if MFEM is using
* CUDA, Ginkgo will choose the CudaExecutor with a default
* CPU Executor based on Ginkgo's OpenMP support).
*/
GinkgoExecutor(Device &mfem_device);
/**
* Constructor.
* Takes an MFEM @p Device object and creates an Executor
* that "matches", but allows the user to specify the host
* Executor for GPU backends.
*/
GinkgoExecutor(Device &mfem_device, ExecType host_exec_type);
/**
* Destructor.
*/
-1
View File
@@ -264,7 +264,6 @@ void HypreParVector::WrapHypreParVector(hypre_ParVector *y, bool owner)
Vector * HypreParVector::GlobalVector() const
{
hypre_Vector *hv = hypre_ParVectorToVectorAll(*this);
if (hv == nullptr) { return nullptr; }
Vector *v = new Vector(hv->data, internal::to_int(hv->size));
v->MakeDataOwner();
hypre_SeqVectorSetDataOwner(hv,0);
+442 -209
View File
@@ -16,238 +16,471 @@
#include "strumpack.hpp"
using namespace std;
using namespace strumpack;
namespace mfem
{
STRUMPACKRowLocMatrix::STRUMPACKRowLocMatrix(MPI_Comm comm,
int num_loc_rows, int first_loc_row,
int glob_nrows, int glob_ncols,
int *I, int *J, double *data)
: comm_(comm), A_(NULL)
int num_loc_rows,
HYPRE_BigInt first_loc_row,
HYPRE_BigInt glob_nrows,
HYPRE_BigInt glob_ncols,
int *I, HYPRE_BigInt *J,
double *data, bool sym_sparse)
{
// Set mfem::Operator member data
height = num_loc_rows;
width = num_loc_rows;
// Allocate STRUMPACK's CSRMatrixMPI
int nprocs, rank;
MPI_Comm_rank(comm_, &rank);
MPI_Comm_size(comm_, &nprocs);
int * dist = new int[nprocs + 1];
dist[rank + 1] = first_loc_row + num_loc_rows;
// Allocate STRUMPACK's CSRMatrixMPI (copies all inputs)
int rank, nprocs;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &nprocs);
Array<HYPRE_BigInt> dist(nprocs + 1);
dist[0] = 0;
MPI_Allgather(MPI_IN_PLACE, 0, MPI_INT, dist + 1, 1, MPI_INT, comm_);
A_ = new CSRMatrixMPI<double,int>(num_loc_rows, I, J, data, dist, comm_, false);
delete[] dist;
}
dist[rank + 1] = first_loc_row + (HYPRE_BigInt)num_loc_rows;
MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL,
dist.GetData() + 1, 1, HYPRE_MPI_BIG_INT, comm);
STRUMPACKRowLocMatrix::STRUMPACKRowLocMatrix(const HypreParMatrix & hypParMat)
: comm_(hypParMat.GetComm()),
A_(NULL)
{
// First cast the parameter to a hypre_ParCSRMatrix
hypre_ParCSRMatrix * parcsr_op =
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix&>(hypParMat);
MFEM_ASSERT(parcsr_op != NULL,"STRUMPACK: const_cast failed in SetOperator");
// Create the CSRMatrixMPI A_ by borrowing the internal data from a
// hypre_CSRMatrix.
hypParMat.HostRead();
hypre_CSRMatrix * csr_op = hypre_MergeDiagAndOffd(parcsr_op);
hypParMat.HypreRead();
hypre_CSRMatrixSetDataOwner(csr_op,0);
#if MFEM_HYPRE_VERSION >= 21600
// For now, this method assumes that HYPRE_Int is int. Also, csr_op->num_cols
// is of type HYPRE_Int, so if we want to check for big indices in
// csr_op->big_j, we'll have to check all entries and that check will only be
// necessary in HYPRE_MIXEDINT mode which is not supported at the moment.
hypre_CSRMatrixBigJtoJ(csr_op);
#endif
height = csr_op->num_rows;
width = csr_op->num_rows;
int nprocs, rank;
MPI_Comm_rank(comm_, &rank);
MPI_Comm_size(comm_, &nprocs);
int * dist = new int[nprocs + 1];
dist[rank + 1] = parcsr_op->first_row_index + csr_op->num_rows;
dist[0] = 0;
MPI_Allgather(MPI_IN_PLACE, 0, MPI_INT, dist + 1, 1, MPI_INT, comm_);
A_ = new CSRMatrixMPI<double,int>(csr_op->num_rows, csr_op->i, csr_op->j,
csr_op->data, dist, comm_, false);
delete[] dist;
// Everything has been copied or abducted so delete the structure
hypre_CSRMatrixDestroy(csr_op);
}
STRUMPACKRowLocMatrix::~STRUMPACKRowLocMatrix()
{
// Delete the struct
if ( A_ != NULL ) { delete A_; }
}
STRUMPACKSolver::STRUMPACKSolver( int argc, char* argv[], MPI_Comm comm )
: comm_(comm),
APtr_(NULL),
solver_(NULL)
{
this->Init(argc, argv);
}
STRUMPACKSolver::STRUMPACKSolver( STRUMPACKRowLocMatrix & A )
: comm_(A.GetComm()),
APtr_(&A),
solver_(NULL)
{
height = A.Height();
width = A.Width();
this->Init(0, NULL);
}
STRUMPACKSolver::~STRUMPACKSolver()
{
if ( solver_ != NULL ) { delete solver_; }
}
void STRUMPACKSolver::Init( int argc, char* argv[] )
{
MPI_Comm_size(comm_, &numProcs_);
MPI_Comm_rank(comm_, &myid_);
factor_verbose_ = false;
solve_verbose_ = false;
solver_ = new StrumpackSparseSolverMPIDist<double,int>(comm_, argc, argv,
false);
}
void STRUMPACKSolver::SetFromCommandLine( )
{
solver_->options().set_from_command_line( );
}
void STRUMPACKSolver::SetPrintFactorStatistics( bool print_stat )
{
factor_verbose_ = print_stat;
}
void STRUMPACKSolver::SetPrintSolveStatistics( bool print_stat )
{
solve_verbose_ = print_stat;
}
void STRUMPACKSolver::SetKrylovSolver( strumpack::KrylovSolver method )
{
solver_->options().set_Krylov_solver( method );
}
void STRUMPACKSolver::SetReorderingStrategy( strumpack::ReorderingStrategy
method )
{
solver_->options().set_reordering_method( method );
}
void STRUMPACKSolver::DisableMatching( )
{
#if STRUMPACK_VERSION_MAJOR >= 3
solver_->options().set_matching( strumpack::MatchingJob::NONE );
#if !(defined(HYPRE_BIGINT) || defined(HYPRE_MIXEDINT))
A_ = new strumpack::CSRMatrixMPI<double, HYPRE_BigInt>(
(HYPRE_BigInt)num_loc_rows, I, J, data, dist.GetData(),
comm, sym_sparse);
#else
solver_->options().set_mc64job( strumpack::MC64Job::NONE );
Array<HYPRE_BigInt> II(num_loc_rows+1);
for (int i = 0; i <= num_loc_rows; i++) { II[i] = (HYPRE_BigInt)I[i]; }
A_ = new strumpack::CSRMatrixMPI<double, HYPRE_BigInt>(
(HYPRE_BigInt)num_loc_rows, II.GetData(), J, data, dist.GetData(),
comm, sym_sparse);
#endif
}
void STRUMPACKSolver::EnableMatching( )
STRUMPACKRowLocMatrix::STRUMPACKRowLocMatrix(const Operator &op,
bool sym_sparse)
{
#if STRUMPACK_VERSION_MAJOR >= 3
solver_->options().set_matching
( strumpack::MatchingJob::MAX_DIAGONAL_PRODUCT_SCALING );
#else
solver_->options().set_mc64job
( strumpack::MC64Job::MAX_DIAGONAL_PRODUCT_SCALING );
#endif
}
#if STRUMPACK_VERSION_MAJOR >= 3
void STRUMPACKSolver::EnableParallelMatching( )
{
solver_->options().set_matching
( strumpack::MatchingJob::COMBBLAS );
}
#endif
void STRUMPACKSolver::SetRelTol( double rtol )
{
solver_->options().set_rel_tol( rtol );
}
void STRUMPACKSolver::SetAbsTol( double atol )
{
solver_->options().set_abs_tol( atol );
}
void STRUMPACKSolver::Mult( const Vector & x, Vector & y ) const
{
MFEM_ASSERT(APtr_ != NULL,
"STRUMPACK Error: The operator must be set before"
" the system can be solved.");
MFEM_ASSERT(x.Size() == Width(), "invalid x.Size() = " << x.Size()
<< ", expected size = " << Width());
MFEM_ASSERT(y.Size() == Height(), "invalid y.Size() = " << y.Size()
<< ", expected size = " << Height());
double* yPtr = y.HostWrite();
const double* xPtr = x.HostRead();
solver_->options().set_verbose( factor_verbose_ );
ReturnCode ret = solver_->factor();
switch (ret)
{
case ReturnCode::SUCCESS: break;
case ReturnCode::MATRIX_NOT_SET:
{
MFEM_ABORT("STRUMPACK: Matrix was not set!");
}
break;
case ReturnCode::REORDERING_ERROR:
{
MFEM_ABORT("STRUMPACK: Matrix reordering failed!");
}
break;
default:
{
MFEM_ABORT("STRUMPACK: 'factor()' error code = " << ret);
}
}
solver_->options().set_verbose( solve_verbose_ );
solver_->solve(xPtr, yPtr);
}
void STRUMPACKSolver::SetOperator( const Operator & op )
{
// Verify that we have a compatible operator
APtr_ = dynamic_cast<const STRUMPACKRowLocMatrix*>(&op);
if ( APtr_ == NULL )
{
mfem_error("STRUMPACKSolver::SetOperator : not STRUMPACKRowLocMatrix!");
}
solver_->set_matrix( *(APtr_->getA()) );
const HypreParMatrix *APtr = dynamic_cast<const HypreParMatrix *>(&op);
MFEM_VERIFY(APtr, "Not a compatible matrix type");
MPI_Comm comm = APtr->GetComm();
// Set mfem::Operator member data
height = op.Height();
width = op.Width();
// First cast the parameter to a hypre_ParCSRMatrix
hypre_ParCSRMatrix *parcsr_op =
(hypre_ParCSRMatrix *)const_cast<HypreParMatrix &>(*APtr);
// Create the CSRMatrixMPI A by taking the internal data from a
// hypre_CSRMatrix
APtr->HostRead();
hypre_CSRMatrix *csr_op = hypre_MergeDiagAndOffd(parcsr_op);
APtr->HypreRead();
HYPRE_Int *Iptr = csr_op->i;
#if MFEM_HYPRE_VERSION >= 21600
HYPRE_BigInt *Jptr = csr_op->big_j;
#else
HYPRE_Int *Jptr = csr_op->j;
#endif
double *data = csr_op->data;
HYPRE_BigInt fst_row = parcsr_op->first_row_index;
HYPRE_Int m_loc = csr_op->num_rows;
// Allocate STRUMPACK's CSRMatrixMPI
int rank, nprocs;
MPI_Comm_rank(comm, &rank);
MPI_Comm_size(comm, &nprocs);
Array<HYPRE_BigInt> dist(nprocs + 1);
dist[0] = 0;
dist[rank + 1] = fst_row + (HYPRE_BigInt)m_loc;
MPI_Allgather(MPI_IN_PLACE, 0, MPI_DATATYPE_NULL,
dist.GetData() + 1, 1, HYPRE_MPI_BIG_INT, comm);
#if !defined(HYPRE_MIXEDINT)
A_ = new strumpack::CSRMatrixMPI<double, HYPRE_BigInt>(
(HYPRE_BigInt)m_loc, Iptr, Jptr, data, dist.GetData(),
comm, sym_sparse);
#else
Array<HYPRE_BigInt> II(m_loc+1);
for (int i = 0; i <= m_loc; i++) { II[i] = (HYPRE_BigInt)Iptr[i]; }
A_ = new strumpack::CSRMatrixMPI<double, HYPRE_BigInt>(
(HYPRE_BigInt)m_loc, II.GetData(), Jptr, data, dist.GetData(),
comm, sym_sparse);
#endif
// Everything has been copied so delete the structure
hypre_CSRMatrixDestroy(csr_op);
}
STRUMPACKRowLocMatrix::~STRUMPACKRowLocMatrix()
{
delete A_;
}
template <typename STRUMPACKSolverType>
STRUMPACKSolverBase<STRUMPACKSolverType>::
STRUMPACKSolverBase(MPI_Comm comm, int argc, char *argv[])
: APtr_(NULL),
factor_verbose_(false),
solve_verbose_(false),
reorder_reuse_(false),
nrhs_(-1)
{
solver_ = new STRUMPACKSolverType(comm, argc, argv, false);
}
template <typename STRUMPACKSolverType>
STRUMPACKSolverBase<STRUMPACKSolverType>::
STRUMPACKSolverBase(STRUMPACKRowLocMatrix &A, int argc, char *argv[])
: APtr_(&A),
factor_verbose_(false),
solve_verbose_(false),
reorder_reuse_(false),
nrhs_(-1)
{
solver_ = new STRUMPACKSolverType(A.GetComm(), argc, argv, false);
SetOperator(A);
}
template <typename STRUMPACKSolverType>
STRUMPACKSolverBase<STRUMPACKSolverType>::
~STRUMPACKSolverBase()
{
delete solver_;
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetFromCommandLine()
{
solver_->options().set_from_command_line();
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetPrintFactorStatistics(bool print_stat)
{
factor_verbose_ = print_stat;
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetPrintSolveStatistics(bool print_stat)
{
solve_verbose_ = print_stat;
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>
::SetRelTol(double rtol)
{
solver_->options().set_rel_tol(rtol);
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>
::SetAbsTol(double atol)
{
solver_->options().set_abs_tol(atol);
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>
::SetMaxIter(int max_it)
{
solver_->options().set_maxit(max_it);
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>
::SetReorderingReuse(bool reuse)
{
reorder_reuse_ = reuse;
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>
::EnableGPU()
{
solver_->options().enable_gpu();
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>
::DisableGPU()
{
solver_->options().disable_gpu();
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetKrylovSolver(strumpack::KrylovSolver method)
{
solver_->options().set_Krylov_solver(method);
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetReorderingStrategy(strumpack::ReorderingStrategy method)
{
solver_->options().set_reordering_method(method);
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetMatching(strumpack::MatchingJob job)
{
solver_->options().set_matching(job);
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetCompression(strumpack::CompressionType type)
{
#if STRUMPACK_VERSION_MAJOR >= 5
solver_->options().set_compression(type);
#else
switch (type)
{
case strumpack::NONE:
solver_->options().disable_BLR();
solver_->options().disable_HSS();
break;
case strumpack::BLR:
solver_->options().enable_BLR();
break;
case strumpack::HSS:
solver_->options().enable_HSS();
break;
default:
MFEM_ABORT("Invalid compression type for STRUMPACK version " <<
STRUMPACK_VERSION_MAJOR << "!");
break;
}
#endif
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetCompressionRelTol(double rtol)
{
#if STRUMPACK_VERSION_MAJOR >= 5
solver_->options().set_compression_rel_tol(rtol);
#else
solver_->options().BLR_options().set_rel_tol(rtol);
solver_->options().HSS_options().set_rel_tol(rtol);
#endif
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetCompressionAbsTol(double atol)
{
#if STRUMPACK_VERSION_MAJOR >= 5
solver_->options().set_compression_abs_tol(atol);
#else
solver_->options().BLR_options().set_abs_tol(atol);
solver_->options().HSS_options().set_abs_tol(atol);
#endif
}
#if STRUMPACK_VERSION_MAJOR >= 5
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetCompressionLossyPrecision(int precision)
{
solver_->options().set_lossy_precision(precision);
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetCompressionButterflyLevels(int levels)
{
solver_->options().HODLR_options().set_butterfly_levels(levels);
}
#endif
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
SetOperator(const Operator &op)
{
// Verify that we have a compatible operator
bool first_mat = !APtr_;
APtr_ = dynamic_cast<const STRUMPACKRowLocMatrix *>(&op);
MFEM_VERIFY(APtr_,
"STRUMPACK: Operator is not a STRUMPACKRowLocMatrix!");
// Set mfem::Operator member data
height = op.Height();
width = op.Width();
if (first_mat || !reorder_reuse_)
{
solver_->set_matrix(*(APtr_->GetA()));
}
else
{
solver_->update_matrix_values(*(APtr_->GetA()));
}
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
FactorInternal() const
{
MFEM_ASSERT(APtr_,
"STRUMPACK: Operator must be set before the system can be "
"solved!");
solver_->options().set_verbose(factor_verbose_);
strumpack::ReturnCode ret = solver_->factor();
if (ret != strumpack::ReturnCode::SUCCESS)
{
#if STRUMPACK_VERSION_MAJOR >= 7
MFEM_ABORT("STRUMPACK: Factor failed with return code " << ret << "!");
#else
MFEM_ABORT("STRUMPACK: Factor failed!");
#endif
}
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
Mult(const Vector &x, Vector &y) const
{
MFEM_ASSERT(x.Size() == Width(),
"STRUMPACK: Invalid x.Size() = " << x.Size() <<
", expected size = " << Width() << "!");
MFEM_ASSERT(y.Size() == Height(),
"STRUMPACK: Invalid y.Size() = " << y.Size() <<
", expected size = " << Height() << "!");
const double *xPtr = x.HostRead();
double *yPtr = y.HostReadWrite();
FactorInternal();
solver_->options().set_verbose(solve_verbose_);
strumpack::ReturnCode ret = solver_->solve(xPtr, yPtr, false);
if (ret != strumpack::ReturnCode::SUCCESS)
{
#if STRUMPACK_VERSION_MAJOR >= 7
MFEM_ABORT("STRUMPACK: Solve failed with return code " << ret << "!");
#else
MFEM_ABORT("STRUMPACK: Solve failed!");
#endif
}
}
template <typename STRUMPACKSolverType>
void STRUMPACKSolverBase<STRUMPACKSolverType>::
ArrayMult(const Array<const Vector *> &X, Array<Vector *> &Y) const
{
MFEM_ASSERT(X.Size() == Y.Size(),
"Number of columns mismatch in STRUMPACK solve!");
if (X.Size() == 1)
{
nrhs_ = 1;
MFEM_ASSERT(X[0] && Y[0], "Missing Vector in STRUMPACK solve!");
Mult(*X[0], *Y[0]);
return;
}
// Multiple RHS case
int ldx = Height();
if (nrhs_ != X.Size())
{
rhs_.SetSize(X.Size() * ldx);
sol_.SetSize(X.Size() * ldx);
nrhs_ = X.Size();
}
for (int i = 0; i < nrhs_; i++)
{
MFEM_ASSERT(X[i] && X[i]->Size() == Width(),
"STRUMPACK: Missing or invalid sized RHS Vector in solve!");
Vector s(rhs_, i * ldx, ldx);
s = *X[i];
rhs_.SyncMemory(s); // Update flags for rhs_ if updated on device
}
const double *xPtr = rhs_.HostRead();
double *yPtr = sol_.HostReadWrite();
FactorInternal();
solver_->options().set_verbose(solve_verbose_);
strumpack::ReturnCode ret = solver_->solve(nrhs_, xPtr, ldx, yPtr, ldx,
false);
if (ret != strumpack::ReturnCode::SUCCESS)
{
#if STRUMPACK_VERSION_MAJOR >= 7
MFEM_ABORT("STRUMPACK: Solve failed with return code " << ret << "!");
#else
MFEM_ABORT("STRUMPACK: Solve failed!");
#endif
}
for (int i = 0; i < nrhs_; i++)
{
MFEM_ASSERT(Y[i] && Y[i]->Size() == Width(),
"STRUMPACK: Missing or invalid sized solution Vector in solve!");
Vector s(sol_, i * ldx, ldx);
*Y[i] = s;
}
}
STRUMPACKSolver::
STRUMPACKSolver(MPI_Comm comm)
: STRUMPACKSolverBase<strumpack::
SparseSolverMPIDist<double, HYPRE_BigInt>>
(comm, 0, NULL) {}
STRUMPACKSolver::
STRUMPACKSolver(STRUMPACKRowLocMatrix &A)
: STRUMPACKSolverBase<strumpack::
SparseSolverMPIDist<double, HYPRE_BigInt>>
(A, 0, NULL) {}
STRUMPACKSolver::
STRUMPACKSolver(MPI_Comm comm, int argc, char *argv[])
: STRUMPACKSolverBase<strumpack::
SparseSolverMPIDist<double, HYPRE_BigInt>>
(comm, argc, argv) {}
STRUMPACKSolver::
STRUMPACKSolver(STRUMPACKRowLocMatrix &A, int argc, char *argv[])
: STRUMPACKSolverBase<strumpack::
SparseSolverMPIDist<double, HYPRE_BigInt>>
(A, argc, argv) {}
#if STRUMPACK_VERSION_MAJOR >= 7
STRUMPACKMixedPrecisionSolver::
STRUMPACKMixedPrecisionSolver(MPI_Comm comm)
: STRUMPACKSolverBase<strumpack::
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
(comm, 0, NULL) {}
STRUMPACKMixedPrecisionSolver::
STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A)
: STRUMPACKSolverBase<strumpack::
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
(A, 0, NULL) {}
STRUMPACKMixedPrecisionSolver::
STRUMPACKMixedPrecisionSolver(MPI_Comm comm, int argc, char *argv[])
: STRUMPACKSolverBase<strumpack::
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
(comm, argc, argv) {}
STRUMPACKMixedPrecisionSolver::
STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A, int argc, char *argv[])
: STRUMPACKSolverBase<strumpack::
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
(A, argc, argv) {}
#endif
template class STRUMPACKSolverBase<strumpack::
SparseSolverMPIDist<double, HYPRE_BigInt>>;
#if STRUMPACK_VERSION_MAJOR >= 7
template class STRUMPACKSolverBase<strumpack::
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>;
#endif
} // mfem namespace
#endif // MFEM_USE_MPI
+169 -67
View File
@@ -16,12 +16,14 @@
#ifdef MFEM_USE_STRUMPACK
#ifdef MFEM_USE_MPI
#include "operator.hpp"
#include "hypre.hpp"
#include <mpi.h>
// STRUMPACK headers
#include "StrumpackSparseSolverMPIDist.hpp"
#include "StrumpackSparseSolverMixedPrecisionMPIDist.hpp"
namespace mfem
{
@@ -34,63 +36,80 @@ public:
be of size (local) nrows by (global) glob_ncols. The new parallel matrix
contains copies of all input arrays (so they can be deleted). */
STRUMPACKRowLocMatrix(MPI_Comm comm,
int num_loc_rows, int first_loc_row,
int glob_nrows, int glob_ncols,
int *I, int *J, double *data);
int num_loc_rows, HYPRE_BigInt first_loc_row,
HYPRE_BigInt glob_nrows, HYPRE_BigInt glob_ncols,
int *I, HYPRE_BigInt *J, double *data,
bool sym_sparse = false);
/** Creates a copy of the parallel matrix hypParMat in STRUMPACK's RowLoc
format. All data is copied so the original matrix may be deleted. */
STRUMPACKRowLocMatrix(const HypreParMatrix & hypParMat);
STRUMPACKRowLocMatrix(const Operator &op, bool sym_sparse = false);
~STRUMPACKRowLocMatrix();
void Mult(const Vector &x, Vector &y) const
{
mfem_error("STRUMPACKRowLocMatrix::Mult(...)\n"
" matrix vector products are not supported.");
MFEM_ABORT("STRUMPACKRowLocMatrix::Mult: Matrix vector products are not "
"supported!");
}
MPI_Comm GetComm() const { return comm_; }
MPI_Comm GetComm() const { return A_->comm(); }
strumpack::CSRMatrixMPI<double,int>* getA() const { return A_; }
strumpack::CSRMatrixMPI<double, HYPRE_BigInt> *GetA() const { return A_; }
private:
MPI_Comm comm_;
strumpack::CSRMatrixMPI<double,int>* A_;
}; // mfem::STRUMPACKRowLocMatrix
strumpack::CSRMatrixMPI<double, HYPRE_BigInt> *A_;
};
/** The MFEM STRUMPACK Direct Solver class.
The mfem::STRUMPACKSolver class uses the STRUMPACK library to perform LU
factorization of a parallel sparse matrix. The solver is capable of handling
double precision types. See http://portal.nersc.gov/project/sparse/strumpack
double precision types. See
http://portal.nersc.gov/project/sparse/strumpack/.
*/
class STRUMPACKSolver : public mfem::Solver
template <typename STRUMPACKSolverType>
class STRUMPACKSolverBase : public Solver
{
protected:
// Constructor with MPI_Comm parameter and command line arguments.
STRUMPACKSolverBase(MPI_Comm comm, int argc, char *argv[]);
// Constructor with STRUMPACK matrix object and command line arguments.
STRUMPACKSolverBase(STRUMPACKRowLocMatrix &A, int argc, char *argv[]);
public:
// Constructor with MPI_Comm parameter.
STRUMPACKSolver( int argc, char* argv[], MPI_Comm comm );
// Constructor with STRUMPACK Matrix Object.
STRUMPACKSolver( STRUMPACKRowLocMatrix & A);
// Default destructor.
~STRUMPACKSolver( void );
virtual ~STRUMPACKSolverBase();
// Factor and solve the linear system y = Op^{-1} x.
void Mult( const Vector & x, Vector & y ) const;
void Mult(const Vector &x, Vector &y) const;
void ArrayMult(const Array<const Vector *> &X, Array<Vector *> &Y) const;
// Set the operator.
void SetOperator( const Operator & op );
void SetOperator(const Operator &op);
// Set various solver options. Refer to STRUMPACK documentation for
// details.
void SetFromCommandLine( );
void SetPrintFactorStatistics( bool print_stat );
void SetPrintSolveStatistics( bool print_stat );
void SetRelTol( double rtol );
void SetAbsTol( double atol );
void SetFromCommandLine();
void SetPrintFactorStatistics(bool print_stat);
void SetPrintSolveStatistics(bool print_stat);
// Set tolerances and iterations for iterative solvers. Compression
// tolerance is handled below.
void SetRelTol(double rtol);
void SetAbsTol(double atol);
void SetMaxIter(int max_it);
// Set the flag controlling reuse of the symbolic factorization for multiple
// operators. This method has to be called before repeated calls to
// SetOperator.
void SetReorderingReuse(bool reuse);
// Enable or not GPU off-loading available if STRUMPACK was compiled with CUDA. Note
// that input/output from MFEM to STRUMPACK is all still through host memory.
void EnableGPU();
void DisableGPU();
/**
* STRUMPACK is an (approximate) direct solver. It can be used as a direct
@@ -100,70 +119,153 @@ public:
* used without preconditioner.
*
* Supported values are:
* AUTO: Use iterative refinement if no HSS compression is used,
* otherwise use GMRes.
* DIRECT: No outer iterative solver, just a single application of
* the multifrontal solver.
* REFINE: Iterative refinement.
* PREC_GMRES: Preconditioned GMRes.
* The preconditioner is the (approx) multifrontal solver.
* GMRES: UN-preconditioned GMRes. (for testing mainly)
* PREC_BICGSTAB: Preconditioned BiCGStab.
* The preconditioner is the (approx) multifrontal solver.
* AUTO: Use iterative refinement if no HSS compression is
* used, otherwise use GMRes
* DIRECT: No outer iterative solver, just a single application
* of the multifrontal solver
* REFINE: Iterative refinement
* PREC_GMRES: Preconditioned GMRes
* The preconditioner is the (approx) multifrontal solver
* GMRES: UN-preconditioned GMRes (for testing mainly)
* PREC_BICGSTAB: Preconditioned BiCGStab
* The preconditioner is the (approx) multifrontal solver
* BICGSTAB: UN-preconditioned BiCGStab. (for testing mainly)
*/
void SetKrylovSolver( strumpack::KrylovSolver method );
void SetKrylovSolver(strumpack::KrylovSolver method);
/**
* Supported reorderings are:
* METIS, PARMETIS, SCOTCH, PTSCOTCH, RCM
* NATURAL: Do not reorder the system
* METIS: Use Metis nested-dissection reordering (default)
* PARMETIS: Use ParMetis nested-dissection reordering
* SCOTCH: Use Scotch nested-dissection reordering
* PTSCOTCH: Use PT-Scotch nested-dissection reordering
* RCM: Use RCM reordering
* GEOMETRIC: A simple geometric nested dissection code that
* only works for regular meshes
* AMD: Approximate minimum degree
* MMD: Multiple minimum degree
* AND: Nested dissection
* MLF: Minimum local fill
* SPECTRAL: Spectral nested dissection
*/
void SetReorderingStrategy( strumpack::ReorderingStrategy method );
void SetReorderingStrategy(strumpack::ReorderingStrategy method);
/**
* Disable static pivoting for stability. The static pivoting in strumpack
* Configure static pivoting for stability. The static pivoting in STRUMPACK
* permutes the sparse input matrix in order to get large (nonzero) elements
* on the diagonal. If the input matrix is already diagonally dominant, this
* reordering can be disabled.
*
* Supported matching algorithms are:
* NONE: Don't do anything
* MAX_CARDINALITY: Maximum cardinality
* MAX_SMALLEST_DIAGONAL: Maximum smallest diagonal value
* MAX_SMALLEST_DIAGONAL_2: Same as MAX_SMALLEST_DIAGONAL
* but different algorithm
* MAX_DIAGONAL_SUM: Maximum sum of diagonal values
* MAX_DIAGONAL_PRODUCT_SCALING: Maximum product of diagonal values
* and row and column scaling (default)
* COMBBLAS: Use AWPM from CombBLAS (only with
* version >= 3)
*/
void DisableMatching();
void SetMatching(strumpack::MatchingJob job);
/**
* Enable static pivoting for stability using the MC64 algorithm with
* job=5. Using a matching algorithm, this will permute the sparse input
* matrix in order to get nonzero elements (as large as possible) on the
* diagonal. And will also scale the rows and columns of the matrix.
* Enable support for rank-structured data formats, which can be used
* for compression within the sparse solver.
*
* Supported compression types are:
* NONE: No compression, purely direct solver (default)
* HSS: HSS compression of frontal matrices
* BLR: Block low-rank compression of fronts
* HODLR: Hierarchically Off-diagonal Low-Rank
* compression of frontal matrices
* BLR_HODLR: Block low-rank compression of medium
* fronts and Hierarchically Off-diagonal
* Low-Rank compression of large fronts
* ZFP_BLR_HODLR: ZFP compression for small fronts,
* Block low-rank compression of medium
* fronts and Hierarchically Off-diagonal
* Low-Rank compression of large fronts
* LOSSLESS: Lossless compression
* LOSSY: Lossy compression
*
* For versions of STRUMPACK < 5, we support only NONE, HSS, and BLR.
* BLR_HODLR and ZPR_BLR_HODLR are supported in STRUMPACK >= 6.
*/
void EnableMatching();
#if STRUMPACK_VERSION_MAJOR >= 3
/**
* Use the AWPM (approximate weight perfect matching) algorithm from the
* Combinatorial BLAS library for static pivoting, i.e. getting large
* nonzeros on the diagonal. This requires that strumpack was compiled with
* support for Combinatorial BLAS.
*/
void EnableParallelMatching();
void SetCompression(strumpack::CompressionType type);
void SetCompressionRelTol(double rtol);
void SetCompressionAbsTol(double atol);
#if STRUMPACK_VERSION_MAJOR >= 5
void SetCompressionLossyPrecision(int precision);
void SetCompressionButterflyLevels(int levels);
#endif
private:
void Init( int argc, char* argv[] );
// Helper method for calling the STRUMPACK factoriation routine.
void FactorInternal() const;
protected:
MPI_Comm comm_;
int numProcs_;
int myid_;
const STRUMPACKRowLocMatrix *APtr_;
STRUMPACKSolverType *solver_;
bool factor_verbose_;
bool solve_verbose_;
bool reorder_reuse_;
const STRUMPACKRowLocMatrix * APtr_;
strumpack::StrumpackSparseSolverMPIDist<double,int> * solver_;
mutable Vector rhs_, sol_;
mutable int nrhs_;
};
}; // mfem::STRUMPACKSolver class
class STRUMPACKSolver :
public STRUMPACKSolverBase<strumpack::
SparseSolverMPIDist<double, HYPRE_BigInt>>
{
public:
// Constructor with MPI_Comm parameter.
STRUMPACKSolver(MPI_Comm comm);
} // mfem namespace
// Constructor with STRUMPACK matrix object.
STRUMPACKSolver(STRUMPACKRowLocMatrix &A);
// Constructor with MPI_Comm parameter and command line arguments.
STRUMPACKSolver(MPI_Comm comm, int argc, char *argv[]);
MFEM_DEPRECATED STRUMPACKSolver(int argc, char *argv[], MPI_Comm comm)
: STRUMPACKSolver(comm, argc, argv) {}
// Constructor with STRUMPACK matrix object and command line arguments.
STRUMPACKSolver(STRUMPACKRowLocMatrix &A, int argc, char *argv[]);
// Destructor.
~STRUMPACKSolver() {}
};
#if STRUMPACK_VERSION_MAJOR >= 7
class STRUMPACKMixedPrecisionSolver :
public STRUMPACKSolverBase<strumpack::
SparseSolverMixedPrecisionMPIDist<float, double, HYPRE_BigInt>>
{
public:
// Constructor with MPI_Comm parameter.
STRUMPACKMixedPrecisionSolver(MPI_Comm comm);
// Constructor with STRUMPACK matrix object.
STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A);
// Constructor with MPI_Comm parameter and command line arguments.
STRUMPACKMixedPrecisionSolver(MPI_Comm comm, int argc, char *argv[]);
// Constructor with STRUMPACK matrix object and command line arguments.
STRUMPACKMixedPrecisionSolver(STRUMPACKRowLocMatrix &A,
int argc, char *argv[]);
// Destructor.
~STRUMPACKMixedPrecisionSolver() {}
};
#endif
} // namespace mfem
#endif // MFEM_USE_MPI
#endif // MFEM_USE_STRUMPACK
+1
View File
@@ -650,6 +650,7 @@ void SuperLUSolver::ArrayMult(const Array<const Vector *> &X,
MFEM_ASSERT(X[i], "Missing Vector in SuperLUSolver::Mult!");
Vector s(sol_, i * ldx, ldx);
s = *X[i];
sol_.SyncMemory(s); // Update flags for sol_ if updated on device
}
}
+8 -5
View File
@@ -35,10 +35,6 @@
namespace mfem
{
/** Count the number of entries in an array of doubles for which isfinite
is false, i.e. the entry is a NaN or +/-Inf. */
inline int CheckFinite(const double *v, const int n);
/// Define a shortcut for std::numeric_limits<double>::infinity()
#ifndef __CYGWIN__
inline double infinity()
@@ -444,7 +440,7 @@ public:
/** @brief Count the number of entries in the Vector for which isfinite
is false, i.e. the entry is a NaN or +/-Inf. */
int CheckFinite() const { return mfem::CheckFinite(HostRead(), size); }
int CheckFinite() const;
/// Destroys vector.
virtual ~Vector();
@@ -494,6 +490,8 @@ inline bool IsFinite(const double &val)
#endif
}
/** Count the number of entries in an array of doubles for which isfinite
is false, i.e. the entry is a NaN or +/-Inf. */
inline int CheckFinite(const double *v, const int n)
{
int bad = 0;
@@ -504,6 +502,11 @@ inline int CheckFinite(const double *v, const int n)
return bad;
}
inline int Vector::CheckFinite() const
{
return mfem::CheckFinite(HostRead(), size);
}
inline Vector::Vector(int s)
{
MFEM_ASSERT(s>=0,"Unexpected negative size.");
+196 -132
View File
@@ -384,6 +384,12 @@ void Mesh::GetElementTransformation(int i, IsoparametricTransformation *ElTr)
}
}
ElementTransformation *Mesh::GetElementTransformation(int i)
{
GetElementTransformation(i, &Transformation);
return &Transformation;
}
void Mesh::GetElementTransformation(int i, const Vector &nodes,
IsoparametricTransformation *ElTr)
{
@@ -428,19 +434,6 @@ void Mesh::GetElementTransformation(int i, const Vector &nodes,
}
}
ElementTransformation *Mesh::GetElementTransformation(int i)
{
GetElementTransformation(i, &Transformation);
return &Transformation;
}
ElementTransformation *Mesh::GetBdrElementTransformation(int i)
{
GetBdrElementTransformation(i, &BdrTransformation);
return &BdrTransformation;
}
void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
{
ElTr->Attribute = GetBdrAttribute(i);
@@ -501,6 +494,12 @@ void Mesh::GetBdrElementTransformation(int i, IsoparametricTransformation* ElTr)
}
}
ElementTransformation *Mesh::GetBdrElementTransformation(int i)
{
GetBdrElementTransformation(i, &BdrTransformation);
return &BdrTransformation;
}
void Mesh::GetFaceTransformation(int FaceNo, IsoparametricTransformation *FTr)
{
FTr->Attribute = (Dim == 1) ? 1 : faces[FaceNo]->GetAttribute();
@@ -1102,7 +1101,7 @@ void Mesh::ApplyLocalSlaveTransformation(FaceElementTransformations &FT,
FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
{
FaceElementTransformations *tr;
int fn = GetBdrFace(BdrElemNo);
int fn = GetBdrElementFaceIndex(BdrElemNo);
// Check if the face is interior, shared, or nonconforming.
if (FaceIsTrueInterior(fn) || faces_info[fn].NCFace >= 0)
@@ -1117,24 +1116,6 @@ FaceElementTransformations *Mesh::GetBdrFaceTransformations(int BdrElemNo)
return tr;
}
int Mesh::GetBdrFace(int BdrElemNo) const
{
int fn;
if (Dim == 3)
{
fn = be_to_face[BdrElemNo];
}
else if (Dim == 2)
{
fn = be_to_edge[BdrElemNo];
}
else
{
fn = boundary[BdrElemNo]->GetVertices()[0];
}
return fn;
}
Mesh::FaceInformation Mesh::GetFaceInformation(int f) const
{
FaceInformation face;
@@ -1448,7 +1429,7 @@ Array<int> Mesh::GetFaceToBdrElMap() const
face_to_be = -1;
for (int i = 0; i < NumOfBdrElements; i++)
{
face_to_be[GetBdrElementEdgeIndex(i)] = i;
face_to_be[GetBdrElementFaceIndex(i)] = i;
}
return face_to_be;
}
@@ -1539,7 +1520,6 @@ void Mesh::Destroy()
faces.DeleteAll();
faces_info.DeleteAll();
nc_faces_info.DeleteAll();
be_to_edge.DeleteAll();
be_to_face.DeleteAll();
// TODO:
@@ -1906,12 +1886,7 @@ int Mesh::AddBdrPoint(int v, int attr)
void Mesh::GenerateBoundaryElements()
{
int i, j;
Array<int> &be2face = (Dim == 2) ? be_to_edge : be_to_face;
// GenerateFaces();
for (i = 0; i < boundary.Size(); i++)
for (int i = 0; i < boundary.Size(); i++)
{
FreeElement(boundary[i]);
}
@@ -1924,22 +1899,24 @@ void Mesh::GenerateBoundaryElements()
// count the 'NumOfBdrElements'
NumOfBdrElements = 0;
for (i = 0; i < faces_info.Size(); i++)
for (int i = 0; i < faces_info.Size(); i++)
{
if (faces_info[i].Elem2No < 0) { NumOfBdrElements++; }
}
// Add the boundary elements
boundary.SetSize(NumOfBdrElements);
be2face.SetSize(NumOfBdrElements);
for (j = i = 0; i < faces_info.Size(); i++)
be_to_face.SetSize(NumOfBdrElements);
for (int i = 0, j = 0; i < faces_info.Size(); i++)
{
if (faces_info[i].Elem2No < 0)
{
boundary[j] = faces[i]->Duplicate(this);
be2face[j++] = i;
be_to_face[j++] = i;
}
}
// In 3D, 'bel_to_edge' is destroyed but it's not updated.
// Note: in 3D, 'bel_to_edge' is destroyed but it's not updated.
}
void Mesh::FinalizeCheck()
@@ -1969,7 +1946,7 @@ void Mesh::FinalizeTriMesh(int generate_edges, int refine, bool fix_orientation)
if (generate_edges)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
GenerateFaces();
CheckBdrElementOrientation();
}
@@ -1997,7 +1974,7 @@ void Mesh::FinalizeQuadMesh(int generate_edges, int refine,
if (generate_edges)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
GenerateFaces();
CheckBdrElementOrientation();
}
@@ -2299,8 +2276,7 @@ void Mesh::ReorderElements(const Array<int> &ordering, bool reorder_vertices)
// - edge_vertex - no need to rebuild
// - geom_factors - no need to rebuild
// - be_to_edge - 2D only
// - be_to_face - 3D only
// - be_to_face
// - Nodes
@@ -2386,9 +2362,9 @@ void Mesh::ReorderElements(const Array<int> &ordering, bool reorder_vertices)
if (Dim > 1)
{
// generate el_to_edge, be_to_edge (2D), bel_to_edge (3D)
// generate el_to_edge, be_to_face (2D), bel_to_edge (3D)
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
if (Dim > 2)
{
@@ -2778,8 +2754,8 @@ void Mesh::DoNodeReorder(DSTable *old_v_to_v, Table *old_elem_vert)
}
if (el_to_edge)
{
// update 'el_to_edge', 'be_to_edge' (2D), 'bel_to_edge' (3D)
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
// update 'el_to_edge', 'be_to_face' (2D), 'bel_to_edge' (3D)
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
if (Dim == 2)
{
// update 'faces' and 'faces_info'
@@ -2856,7 +2832,7 @@ void Mesh::FinalizeTetMesh(int generate_edges, int refine, bool fix_orientation)
if (generate_edges == 1)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
else
{
@@ -2891,7 +2867,7 @@ void Mesh::FinalizeWedgeMesh(int generate_edges, int refine,
if (generate_edges == 1)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
else
{
@@ -2923,7 +2899,7 @@ void Mesh::FinalizeHexMesh(int generate_edges, int refine, bool fix_orientation)
if (generate_edges)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
else
{
@@ -3002,7 +2978,7 @@ void Mesh::FinalizeTopology(bool generate_bdr)
{
// el_to_edge may already be allocated (P2 VTK meshes)
if (!el_to_edge) { el_to_edge = new Table; }
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
if (Dim == 2)
{
GenerateFaces(); // 'Faces' in 2D refers to the edges
@@ -3022,8 +2998,17 @@ void Mesh::FinalizeTopology(bool generate_bdr)
GenerateFaces();
if (NumOfBdrElements == 0 && generate_bdr)
{
// be_to_face will be set inside GenerateBoundaryElements
GenerateBoundaryElements();
}
else
{
be_to_face.SetSize(NumOfBdrElements);
for (int i = 0; i < NumOfBdrElements; ++i)
{
be_to_face[i] = boundary[i]->GetVertices()[0];
}
}
}
if (ncmesh)
@@ -3555,7 +3540,7 @@ void Mesh::Make2D(int nx, int ny, Element::Type type,
if (generate_edges == 1)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
GenerateFaces();
CheckBdrElementOrientation();
}
@@ -3613,6 +3598,11 @@ void Mesh::Make1D(int n, double sx)
SetMeshGen();
GenerateFaces();
// Set be_to_face
be_to_face.SetSize(2);
be_to_face[0] = 0;
be_to_face[1] = n;
attributes.Append(1);
bdr_attributes.Append(1); bdr_attributes.Append(2);
}
@@ -3666,9 +3656,6 @@ Mesh::Mesh(const Mesh &mesh, bool copy_nodes)
// Copy the boundary-to-edge Table, bel_to_edge (3D)
bel_to_edge = (mesh.bel_to_edge) ? new Table(*mesh.bel_to_edge) : NULL;
// Copy the boundary-to-edge Array, be_to_edge (2D)
mesh.be_to_edge.Copy(be_to_edge);
// Duplicate the faces and faces_info.
faces.SetSize(mesh.faces.Size());
for (int i = 0; i < faces.Size(); i++)
@@ -5269,7 +5256,7 @@ void Mesh::UpdateNURBS()
if (el_to_edge)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
if (el_to_face)
@@ -6169,15 +6156,15 @@ int Mesh::CheckBdrElementOrientation(bool fix_it)
if (el_to_edge == NULL) // edges were not generated
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
GenerateFaces(); // 'Faces' in 2D refers to the edges
}
for (int i = 0; i < NumOfBdrElements; i++)
{
if (faces_info[be_to_edge[i]].Elem2No < 0) // boundary face
if (faces_info[be_to_face[i]].Elem2No < 0) // boundary face
{
int *bv = boundary[i]->GetVertices();
int *fv = faces[be_to_edge[i]]->GetVertices();
int *fv = faces[be_to_face[i]]->GetVertices();
if (bv[0] != fv[0])
{
if (fix_it)
@@ -6271,6 +6258,111 @@ int Mesh::CheckBdrElementOrientation(bool fix_it)
return wo;
}
IntegrationPoint Mesh::TransformBdrElementToFace(Geometry::Type geom, int o,
const IntegrationPoint &ip)
{
IntegrationPoint fip = ip;
if (geom == Geometry::POINT)
{
return fip;
}
else if (geom == Geometry::SEGMENT)
{
MFEM_ASSERT(o >= 0 && o < 2, "Invalid orientation for Geometry::SEGMENT!");
if (o == 0)
{
fip.x = ip.x;
}
else if (o == 1)
{
fip.x = 1.0 - ip.x;
}
}
else if (geom == Geometry::TRIANGLE)
{
MFEM_ASSERT(o >= 0 && o < 6, "Invalid orientation for Geometry::TRIANGLE!");
if (o == 0) // 0, 1, 2
{
fip.x = ip.x;
fip.y = ip.y;
}
else if (o == 5) // 0, 2, 1
{
fip.x = ip.y;
fip.y = ip.x;
}
else if (o == 2) // 1, 2, 0
{
fip.x = 1.0 - ip.x - ip.y;
fip.y = ip.x;
}
else if (o == 1) // 1, 0, 2
{
fip.x = 1.0 - ip.x - ip.y;
fip.y = ip.y;
}
else if (o == 4) // 2, 0, 1
{
fip.x = ip.y;
fip.y = 1.0 - ip.x - ip.y;
}
else if (o == 3) // 2, 1, 0
{
fip.x = ip.x;
fip.y = 1.0 - ip.x - ip.y;
}
}
else if (geom == Geometry::SQUARE)
{
MFEM_ASSERT(o >= 0 && o < 8, "Invalid orientation for Geometry::SQUARE!");
if (o == 0) // 0, 1, 2, 3
{
fip.x = ip.x;
fip.y = ip.y;
}
else if (o == 1) // 0, 3, 2, 1
{
fip.x = ip.y;
fip.y = ip.x;
}
else if (o == 2) // 1, 2, 3, 0
{
fip.x = ip.y;
fip.y = 1.0 - ip.x;
}
else if (o == 3) // 1, 0, 3, 2
{
fip.x = 1.0 - ip.x;
fip.y = ip.y;
}
else if (o == 4) // 2, 3, 0, 1
{
fip.x = 1.0 - ip.x;
fip.y = 1.0 - ip.y;
}
else if (o == 5) // 2, 1, 0, 3
{
fip.x = 1.0 - ip.y;
fip.y = 1.0 - ip.x;
}
else if (o == 6) // 3, 0, 1, 2
{
fip.x = 1.0 - ip.y;
fip.y = ip.x;
}
else if (o == 7) // 3, 2, 1, 0
{
fip.x = ip.x;
fip.y = 1.0 - ip.y;
}
}
else
{
MFEM_ABORT("Unsupported face geometry for TransformBdrElementToFace!");
}
return fip;
}
int Mesh::GetNumGeometries(int dim) const
{
MFEM_ASSERT(0 <= dim && dim <= Dim, "invalid dim: " << dim);
@@ -6323,7 +6415,7 @@ void Mesh::GetBdrElementEdges(int i, Array<int> &edges, Array<int> &cor) const
{
edges.SetSize(1);
cor.SetSize(1);
edges[0] = be_to_edge[i];
edges[0] = be_to_face[i];
const int *v = boundary[i]->GetVertices();
cor[0] = (v[0] < v[1]) ? (1) : (-1);
}
@@ -6564,42 +6656,26 @@ Array<int> Mesh::FindFaceNeighbors(const int elem) const
void Mesh::GetBdrElementFace(int i, int *f, int *o) const
{
const int *bv, *fv;
*f = GetBdrElementFaceIndex(i);
*f = be_to_face[i];
bv = boundary[i]->GetVertices();
fv = faces[be_to_face[i]]->GetVertices();
const int *fv = (Dim > 1) ? faces[*f]->GetVertices() : NULL;
const int *bv = boundary[i]->GetVertices();
// find the orientation of the bdr. elem. w.r.t.
// the corresponding face element (that's the base)
switch (GetBdrElementType(i))
switch (GetBdrElementGeometry(i))
{
case Element::TRIANGLE:
*o = GetTriOrientation(fv, bv);
break;
case Element::QUADRILATERAL:
*o = GetQuadOrientation(fv, bv);
break;
default:
MFEM_ABORT("invalid geometry");
case Geometry::POINT: *o = 0; break;
case Geometry::SEGMENT: *o = (fv[0] == bv[0]) ? 0 : 1; break;
case Geometry::TRIANGLE: *o = GetTriOrientation(fv, bv); break;
case Geometry::SQUARE: *o = GetQuadOrientation(fv, bv); break;
default: MFEM_ABORT("invalid geometry");
}
}
int Mesh::GetBdrElementEdgeIndex(int i) const
{
switch (Dim)
{
case 1: return boundary[i]->GetVertices()[0];
case 2: return be_to_edge[i];
case 3: return be_to_face[i];
default: MFEM_ABORT("invalid dimension!");
}
return -1;
}
void Mesh::GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
{
int fid = GetBdrElementEdgeIndex(bdr_el);
int fid = GetBdrElementFaceIndex(bdr_el);
const FaceInfo &fi = faces_info[fid];
MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
@@ -6621,7 +6697,7 @@ void Mesh::GetBdrElementAdjacentElement(int bdr_el, int &el, int &info) const
void Mesh::GetBdrElementAdjacentElement2(int bdr_el, int &el, int &info) const
{
int fid = GetBdrElementEdgeIndex(bdr_el);
int fid = GetBdrElementFaceIndex(bdr_el);
const FaceInfo &fi = faces_info[fid];
MFEM_ASSERT(fi.Elem1Inf % 64 == 0, "internal error"); // orientation == 0
@@ -6747,7 +6823,7 @@ void Mesh::GetVertexToVertexTable(DSTable &v_to_v) const
}
}
int Mesh::GetElementToEdgeTable(Table & e_to_f, Array<int> &be_to_f)
int Mesh::GetElementToEdgeTable(Table &e_to_f)
{
int i, NumberOfEdges;
@@ -6762,11 +6838,11 @@ int Mesh::GetElementToEdgeTable(Table & e_to_f, Array<int> &be_to_f)
if (Dim == 2)
{
// Initialize the indices for the boundary elements.
be_to_f.SetSize(NumOfBdrElements);
be_to_face.SetSize(NumOfBdrElements);
for (i = 0; i < NumOfBdrElements; i++)
{
const int *v = boundary[i]->GetVertices();
be_to_f[i] = v_to_v(v[0], v[1]);
be_to_face[i] = v_to_v(v[0], v[1]);
}
}
else if (Dim == 3)
@@ -7183,7 +7259,7 @@ STable3D *Mesh::GetFacesTable()
break;
}
default:
MFEM_ABORT("Unexpected type of Element.");
MFEM_ABORT("Unexpected type of Element: " << GetElementType(i));
}
}
return faces_tbl;
@@ -7369,7 +7445,7 @@ void Mesh::ReorientTetMesh()
GenerateFaces();
if (el_to_edge)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
}
else
@@ -8404,7 +8480,7 @@ void Mesh::UniformRefinement2D_base(bool update_nodes)
if (el_to_edge == NULL)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
int quad_counter = 0;
@@ -8493,8 +8569,8 @@ void Mesh::UniformRefinement2D_base(bool update_nodes)
const int attr = boundary[i]->GetAttribute();
int *v = boundary[i]->GetVertices();
new_boundary[j++] = new Segment(v[0], oedge+be_to_edge[i], attr);
new_boundary[j++] = new Segment(oedge+be_to_edge[i], v[1], attr);
new_boundary[j++] = new Segment(v[0], oedge+be_to_face[i], attr);
new_boundary[j++] = new Segment(oedge+be_to_face[i], v[1], attr);
FreeElement(boundary[i]);
}
@@ -8534,7 +8610,7 @@ void Mesh::UniformRefinement2D_base(bool update_nodes)
NumOfBdrElements = 2 * NumOfBdrElements;
NumOfFaces = 0;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
GenerateFaces();
last_operation = Mesh::REFINE;
@@ -8564,7 +8640,7 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
if (el_to_edge == NULL)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
if (el_to_face == NULL)
@@ -9255,7 +9331,7 @@ void Mesh::UniformRefinement3D_base(Array<int> *f2qf_ptr, DSTable *v_to_v_p,
CheckBdrElementOrientation(false);
#endif
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
last_operation = Mesh::REFINE;
sequence++;
@@ -9389,7 +9465,7 @@ void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
if (el_to_edge != NULL)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
GenerateFaces();
}
@@ -9485,7 +9561,7 @@ void Mesh::LocalRefinement(const Array<int> &marked_el, int type)
// 5. Update element-to-edge and element-to-face relations.
if (el_to_edge != NULL)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
if (el_to_face != NULL)
{
@@ -9676,7 +9752,7 @@ void Mesh::InitFromNCMesh(const NCMesh &ncmesh_)
if (Dim > 1)
{
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
if (Dim > 2)
{
@@ -9723,7 +9799,6 @@ void Mesh::Swap(Mesh& other, bool non_geometry)
mfem::Swap(el_to_edge, other.el_to_edge);
mfem::Swap(el_to_face, other.el_to_face);
mfem::Swap(el_to_el, other.el_to_el);
mfem::Swap(be_to_edge, other.be_to_edge);
mfem::Swap(bel_to_edge, other.bel_to_edge);
mfem::Swap(be_to_face, other.be_to_face);
mfem::Swap(face_edge, other.face_edge);
@@ -12178,9 +12253,9 @@ void Mesh::RemoveUnusedVertices()
DeleteTables();
if (Dim > 1)
{
// generate el_to_edge, be_to_edge (2D), bel_to_edge (3D)
// generate el_to_edge, be_to_face (2D), bel_to_edge (3D)
el_to_edge = new Table;
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
if (Dim > 2)
{
@@ -12211,7 +12286,7 @@ void Mesh::RemoveInternalBoundaries()
int new_bel_to_edge_nnz = 0;
for (int i = 0; i < GetNBE(); i++)
{
if (FaceIsInterior(GetBdrElementEdgeIndex(i)))
if (FaceIsInterior(GetBdrElementFaceIndex(i)))
{
FreeElement(boundary[i]);
}
@@ -12228,32 +12303,24 @@ void Mesh::RemoveInternalBoundaries()
if (num_bdr_elem == GetNBE()) { return; }
Array<Element *> new_boundary(num_bdr_elem);
Array<int> new_be_to_edge, new_be_to_face;
Array<int> new_be_to_face;
Table *new_bel_to_edge = NULL;
new_boundary.SetSize(0);
if (Dim == 2)
new_be_to_face.Reserve(num_bdr_elem);
if (Dim == 3)
{
new_be_to_edge.Reserve(num_bdr_elem);
}
else if (Dim == 3)
{
new_be_to_face.Reserve(num_bdr_elem);
new_bel_to_edge = new Table;
new_bel_to_edge->SetDims(num_bdr_elem, new_bel_to_edge_nnz);
}
for (int i = 0; i < GetNBE(); i++)
{
if (!FaceIsInterior(GetBdrElementEdgeIndex(i)))
if (!FaceIsInterior(GetBdrElementFaceIndex(i)))
{
new_boundary.Append(boundary[i]);
if (Dim == 2)
int row = new_be_to_face.Size();
new_be_to_face.Append(be_to_face[i]);
if (Dim == 3)
{
new_be_to_edge.Append(be_to_edge[i]);
}
else if (Dim == 3)
{
int row = new_be_to_face.Size();
new_be_to_face.Append(be_to_face[i]);
int *e = bel_to_edge->GetRow(i);
int ne = bel_to_edge->RowSize(i);
int *new_e = new_bel_to_edge->GetRow(row);
@@ -12269,13 +12336,10 @@ void Mesh::RemoveInternalBoundaries()
NumOfBdrElements = new_boundary.Size();
mfem::Swap(boundary, new_boundary);
if (Dim == 2)
mfem::Swap(be_to_face, new_be_to_face);
if (Dim == 3)
{
mfem::Swap(be_to_edge, new_be_to_edge);
}
else if (Dim == 3)
{
mfem::Swap(be_to_face, new_be_to_face);
delete bel_to_edge;
bel_to_edge = new_bel_to_edge;
}
+60 -12
View File
@@ -220,9 +220,9 @@ protected:
Table *el_to_edge;
Table *el_to_face;
Table *el_to_el;
Array<int> be_to_edge; // for 2D
Table *bel_to_edge; // for 3D
Array<int> be_to_face;
Array<int> be_to_face; // faces = vertices (1D), edges (2D), faces (3D)
Table *bel_to_edge; // for 3D only
// Note that the following tables are owned by this class and should not be
// deleted by the caller. Of these three tables, only face_edge and
@@ -322,9 +322,38 @@ protected:
void ReadNURBSMesh(std::istream &input, int &curved, int &read_gf);
void ReadInlineMesh(std::istream &input, bool generate_edges = false);
void ReadGmshMesh(std::istream &input, int &curved, int &read_gf);
/* Note NetCDF (optional library) is used for reading cubit files */
#ifdef MFEM_USE_NETCDF
/// @brief Load a mesh from a Genesis file.
void ReadCubit(const char *filename, int &curved, int &read_gf);
/// @brief The final step in constructing the mesh from a Genesis file. This
/// is only called if the mesh order == 2 (determined internally from the
/// cubit element type).
void FinalizeCubitSecondOrderMesh(const int cubit_element_type,
const int num_element_blocks,
const int num_nodes_per_element,
const int *start_of_block,
const double *coordx,
const double *coordy,
const double *coordz,
const int **element_blocks);
/// @brief Returns a pointer to a new mfem::Element based on the provided
/// cubit element type. This is used internally to create the mesh elements
/// from a Genesis file.
Element *CreateCubitElement(const int cubit_element_type,
const int *vertex_ids,
const int block_id);
/// @brief Returns a pointer to a new mfem::Element based on the provided
/// cubit face type. This is used internally to create the boundary elements
/// from a Genesis file.
Element *CreateCubitBoundaryElement(const int cubit_face_type,
const int *vertex_ids,
const int sideset_id) const;
#endif
/// Determine the mesh generator bitmask #meshgen, see MeshGenerator().
@@ -504,7 +533,7 @@ protected:
nodes in the elements. For example, if T is the element to edge table
T(i, 0) gives the index of edge in element i that connects vertex 0
to vertex 1, etc. Returns the number of the edges. */
int GetElementToEdgeTable(Table &, Array<int> &);
int GetElementToEdgeTable(Table &);
/// Used in GenerateFaces()
void AddPointFaceElement(int lf, int gf, int el);
@@ -1330,13 +1359,13 @@ public:
element @a elem, including @a elem. */
Array<int> FindFaceNeighbors(const int elem) const;
/// Return the index and the orientation of the face of bdr element i. (3D)
void GetBdrElementFace(int i, int *f, int *o) const;
/** Return the index and the orientation of the vertex of bdr element i. (1D)
Return the index and the orientation of the edge of bdr element i. (2D)
Return the index and the orientation of the face of bdr element i. (3D)
/** Return the vertex index of boundary element i. (1D)
Return the edge index of boundary element i. (2D)
Return the face index of boundary element i. (3D) */
int GetBdrElementEdgeIndex(int i) const;
In 2D, the returned edge orientation is 0 or 1, not +/-1 as returned by
GetElementEdges/GetBdrElementEdges. */
void GetBdrElementFace(int i, int *f, int *o) const;
/** @brief For the given boundary element, bdr_el, return its adjacent
element and its info, i.e. 64*local_bdr_index+bdr_orientation.
@@ -1358,8 +1387,19 @@ public:
@sa GetBdrElementAdjacentElement() */
void GetBdrElementAdjacentElement2(int bdr_el, int &el, int &info) const;
/// Return the local face index for the given boundary face.
int GetBdrFace(int BdrElemNo) const;
/// @brief Return the local face (codimension-1) index for the given boundary
/// element index.
int GetBdrElementFaceIndex(int be_idx) const { return be_to_face[be_idx]; }
/// Deprecated in favor of GetBdrElementFaceIndex().
MFEM_DEPRECATED int GetBdrFace(int i) const { return GetBdrElementFaceIndex(i); }
/** Return the vertex index of boundary element i. (1D)
Return the edge index of boundary element i. (2D)
Return the face index of boundary element i. (3D)
Deprecated in favor of GetBdrElementFaceIndex(). */
MFEM_DEPRECATED int GetBdrElementEdgeIndex(int i) const { return GetBdrElementFaceIndex(i); }
/// @}
@@ -1409,6 +1449,14 @@ public:
/// should not be deleted by the caller.
static FiniteElement *GetTransformationFEforElementType(Element::Type);
/** @brief For the vertex (1D), edge (2D), or face (3D) of a boundary element
with the orientation @a o, return the transformation of the boundary
element integration point @ ip to the face element. In 2D, the
the orientation is 0 or 1 as returned by GetBdrElementFace, not +/-1.
Supports both internal and external boundaries. */
static IntegrationPoint TransformBdrElementToFace(Geometry::Type geom, int o,
const IntegrationPoint &ip);
/// @anchor mfem_Mesh_elem_trans
/// @name Access the coordinate transformation for individual elements
///
+968 -584
View File
File diff suppressed because it is too large Load Diff
+80 -71
View File
@@ -3021,13 +3021,13 @@ void NCMesh::TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
if (nd.HasEdge())
{
// check if the edge is already a master in 'edge_list'
int type;
const MeshId &eid = edge_list.LookUp(nd.edge_index, &type);
if (type == 1)
const auto eid_and_type = edge_list.GetMeshIdAndType(nd.edge_index);
if (eid_and_type.type == NCList::MeshIdType::MASTER
|| eid_and_type.type == NCList::MeshIdType::CONFORMING)
{
// in this case we need to add an edge-face constraint, because the
// master edge is really a (face-)slave itself
// non-slave edge is really a (face-)slave itself.
const MeshId &eid = *eid_and_type.id;
face_list.slaves.Append(
Slave(-1 - eid.index, eid.element, eid.local, Geometry::TRIANGLE));
@@ -3048,9 +3048,10 @@ void NCMesh::TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
TraverseTetEdge(mid, vn1, pmid, p1, matrix_map);
}
bool NCMesh::TraverseTriFace(int vn0, int vn1, int vn2,
const PointMatrix& pm, int level,
MatrixMap &matrix_map)
NCMesh::TriFaceTraverseResults NCMesh::TraverseTriFace(int vn0, int vn1,
int vn2,
const PointMatrix& pm, int level,
MatrixMap &matrix_map)
{
if (level > 0)
{
@@ -3069,7 +3070,7 @@ bool NCMesh::TraverseTriFace(int vn0, int vn1, int vn2,
sl.local = ReorderFacePointMat(vn0, vn1, vn2, -1, elem, pm, pm_r);
sl.matrix = matrix_map.GetIndex(pm_r);
return true;
return {true, elements[elem].rank != MyRank};
}
}
@@ -3077,7 +3078,7 @@ bool NCMesh::TraverseTriFace(int vn0, int vn1, int vn2,
if (TriFaceSplit(vn0, vn1, vn2, mid))
{
Point pmid0(pm(0), pm(1)), pmid1(pm(1), pm(2)), pmid2(pm(2), pm(0));
bool b[4];
TriFaceTraverseResults b[4];
b[0] = TraverseTriFace(vn0, mid[0], mid[2],
PointMatrix(pm(0), pmid0, pmid2),
@@ -3095,16 +3096,21 @@ bool NCMesh::TraverseTriFace(int vn0, int vn1, int vn2,
PointMatrix(pmid1, pmid2, pmid0),
level+1, matrix_map);
// traverse possible tet edges constrained by the master face
if (HaveTets() && !b[3])
// Traverse possible tet edges constrained by the master face. This needs to occur if
// none of these first NC level faces are split further, OR if they are on different
// processors. The different processor constraint is needed in the case of local
// elements constrained by this face via the edge alone. Cannot know this a priori, so
// just constrain any edge attached to two neighbors.
if (HaveTets() && (!b[3].unsplit || b[3].ghost_neighbor))
{
if (!b[1]) { TraverseTetEdge(mid[0],mid[1], pmid0,pmid1, matrix_map); }
if (!b[2]) { TraverseTetEdge(mid[1],mid[2], pmid1,pmid2, matrix_map); }
if (!b[0]) { TraverseTetEdge(mid[2],mid[0], pmid2,pmid0, matrix_map); }
// If the faces have no further splits, so would not be captured by normal face
// relations, add possible edge constraints.
if (!b[1].unsplit || b[1].ghost_neighbor) { TraverseTetEdge(mid[0],mid[1], pmid0,pmid1, matrix_map); }
if (!b[2].unsplit || b[2].ghost_neighbor) { TraverseTetEdge(mid[1],mid[2], pmid1,pmid2, matrix_map); }
if (!b[0].unsplit || b[0].ghost_neighbor) { TraverseTetEdge(mid[2],mid[0], pmid2,pmid0, matrix_map); }
}
}
return false;
return {false, false};
}
void NCMesh::BuildFaceList()
@@ -3402,77 +3408,80 @@ void NCMesh::NCList::Clear()
point_matrices[i].DeleteAll();
}
inv_index.DeleteAll();
inv_index.clear();
}
long NCMesh::NCList::TotalSize() const
NCMesh::NCList::MeshIdAndType
NCMesh::NCList::GetMeshIdAndType(int index) const
{
return conforming.Size() + masters.Size() + slaves.Size();
}
const NCMesh::MeshId& NCMesh::NCList::LookUp(int index, int *type) const
{
if (!inv_index.Size())
BuildIndex();
const auto it = inv_index.find(index);
auto ft = it != inv_index.end() ? it->second.first : MeshIdType::UNRECOGNIZED;
switch (ft)
{
int max_index = -1;
case MeshIdType::CONFORMING:
return {&conforming[it->second.second], it->second.first};
case MeshIdType::MASTER:
return {&masters[it->second.second], it->second.first};
case MeshIdType::SLAVE:
return {&slaves[it->second.second], it->second.first};
case MeshIdType::UNRECOGNIZED:
default:
return {nullptr, MeshIdType::UNRECOGNIZED};
}
}
NCMesh::NCList::MeshIdType
NCMesh::NCList::GetMeshIdType(int index) const
{
BuildIndex();
auto it = inv_index.find(index);
return (it != inv_index.end()) ? it->second.first : MeshIdType::UNRECOGNIZED;
}
bool
NCMesh::NCList::CheckMeshIdType(int index, MeshIdType ft) const
{
return GetMeshIdType(index) == ft;
}
void
NCMesh::NCList::BuildIndex() const
{
if (inv_index.size() == 0)
{
auto index_compare = [](const MeshId &a, const MeshId &b) { return a.index < b.index; };
auto max_conforming = std::max_element(conforming.begin(), conforming.end(),
index_compare);
auto max_master = std::max_element(masters.begin(), masters.end(),
index_compare);
auto max_slave = std::max_element(slaves.begin(), slaves.end(), index_compare);
int max_conforming_index = max_conforming != nullptr ? max_conforming->index :
-1;
int max_master_index = max_master != nullptr ? max_master->index : -1;
int max_slave_index = max_slave != nullptr ? max_slave->index : -1;
inv_index.reserve(std::max({max_conforming_index, max_master_index, max_slave_index}));
for (int i = 0; i < conforming.Size(); i++)
{
max_index = std::max(conforming[i].index, max_index);
inv_index.emplace(conforming[i].index, std::make_pair(MeshIdType::CONFORMING,
i));
}
for (int i = 0; i < masters.Size(); i++)
{
max_index = std::max(masters[i].index, max_index);
inv_index.emplace(masters[i].index, std::make_pair(MeshIdType::MASTER, i));
}
for (int i = 0; i < slaves.Size(); i++)
{
if (slaves[i].index < 0) { continue; }
max_index = std::max(slaves[i].index, max_index);
}
inv_index.SetSize(max_index + 1);
inv_index = -1;
for (int i = 0; i < conforming.Size(); i++)
{
inv_index[conforming[i].index] = (i << 2);
}
for (int i = 0; i < masters.Size(); i++)
{
inv_index[masters[i].index] = (i << 2) + 1;
}
for (int i = 0; i < slaves.Size(); i++)
{
if (slaves[i].index < 0) { continue; }
inv_index[slaves[i].index] = (i << 2) + 2;
inv_index.emplace(slaves[i].index, std::make_pair(MeshIdType::SLAVE, i));
}
}
MFEM_ASSERT(index >= 0 && index < inv_index.Size(), "");
int key = inv_index[index];
if (!type)
{
MFEM_VERIFY(key >= 0, "index " << index << " not found.");
}
else // return entity type if requested, don't abort when not found
{
*type = (key >= 0) ? (key & 0x3) : -1;
static MeshId invalid;
if (*type < 0) { return invalid; } // not found
}
// return found entity MeshId
switch (key & 0x3)
{
case 0: return conforming[key >> 2];
case 1: return masters[key >> 2];
case 2: return slaves[key >> 2];
default: MFEM_ABORT("internal error"); return conforming[0];
}
MFEM_ASSERT(inv_index.size() > 0,
"Empty inverse index, member lists must be populated before BuildIndex is called!");
}
//// Neighbors /////////////////////////////////////////////////////////////////
void NCMesh::CollectEdgeVertices(int v0, int v1, Array<int> &indices)
+62 -13
View File
@@ -24,6 +24,7 @@
#include <vector>
#include <map>
#include <iostream>
#include <unordered_map>
namespace mfem
{
@@ -223,30 +224,73 @@ public:
, master(-1), matrix(0), edge_flags(0) {}
};
/// Lists all edges/faces in the nonconforming mesh.
struct NCList
{
Array<MeshId> conforming;
Array<Master> masters;
Array<Slave> slaves;
Array<MeshId> conforming; ///< All MeshIds corresponding to conformal faces
Array<Master> masters; ///< All MeshIds corresponding to master faces
Array<Slave> slaves; ///< All MeshIds corresponding to slave faces
/// List of unique point matrices for each slave geometry.
Array<DenseMatrix*> point_matrices[Geometry::NumGeom];
/// Return the point matrix oriented according to the master and slave edges
void OrientedPointMatrix(const Slave &slave,
DenseMatrix &oriented_matrix) const;
/// Particular MeshId type, used for allowing static casting to the
/// appropriate child type after searching the NCList. UNRECOGNIZED
/// denotes that an instance is not known within the NCList, meaning that
/// it does not play a part in NC mechanics. This can be because the index
/// did not exist in the original Mesh, or because the entry is a boundary
/// face, whose NC status is always conforming.
enum class MeshIdType : char {CONFORMING, MASTER, SLAVE, UNRECOGNIZED};
/// Helper storing a reference to a MeshId type, and the face type it can
/// be cast to
struct MeshIdAndType
{
const MeshId * const id; ///< Pointer to a possible MeshId, nullptr if not found
/// MeshIdType corresponding to the MeshId. UNRECOGNIZED if unfound.
const MeshIdType type;
};
/// Return a mesh id and type for a given nc index.
MeshIdAndType GetMeshIdAndType(int index) const;
/// Return a face type for a given nc index.
MeshIdType GetMeshIdType(int index) const;
/// Given an index, check if this is a certain face type.
bool CheckMeshIdType(int index, MeshIdType type) const;
/// Erase the contents of the conforming, master and slave arrays.
void Clear();
bool Empty() const { return !conforming.Size() && !masters.Size(); }
long TotalSize() const;
/// Whether the NCList is empty.
bool Empty() const
{
return conforming.Size() == 0
&& masters.Size() == 0
&& slaves.Size() == 0;
}
/// The total size of the component arrays in the NCList.
long TotalSize() const
{
return conforming.Size() + masters.Size() + slaves.Size();
}
/// The memory usage of the three public arrays. Does not account for the
/// inverse index.
long MemoryUsage() const;
const MeshId& LookUp(int index, int *type = NULL) const;
~NCList() { Clear(); }
private:
mutable Array<int> inv_index;
// Check for existence or construct the inv_index list map if necessary.
// const because only modifies the mutable member inv_index.
void BuildIndex() const;
/// A lazily constructed map from index to MeshId. Built whenever
/// GetMeshIdAndType, GetMeshIdType or CheckMeshIdType is called for the
/// first time. The MeshIdType is stored with, to enable casting to Slave
/// or Master elements appropriately.
mutable std::unordered_map<int, std::pair<MeshIdType, int>> inv_index;
};
/// Return the current list of conforming and nonconforming faces.
@@ -727,9 +771,14 @@ protected: // implementation
void TraverseQuadFace(int vn0, int vn1, int vn2, int vn3,
const PointMatrix& pm, int level, Face* eface[4],
MatrixMap &matrix_map);
bool TraverseTriFace(int vn0, int vn1, int vn2,
const PointMatrix& pm, int level,
MatrixMap &matrix_map);
struct TriFaceTraverseResults
{
bool unsplit; ///< Whether this face has no further splits.
bool ghost_neighbor; ///< Whether the face neighbor is a ghost.
};
TriFaceTraverseResults TraverseTriFace(int vn0, int vn1, int vn2,
const PointMatrix& pm, int level,
MatrixMap &matrix_map);
void TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
MatrixMap &matrix_map);
void TraverseEdge(int vn0, int vn1, double t0, double t1, int flags,
+15 -6
View File
@@ -39,6 +39,7 @@ KnotVector::KnotVector(int Order_, int NCP)
Order = Order_;
NumOfControlPoints = NCP;
knot.SetSize(NumOfControlPoints + Order + 1);
NumOfElements = 0;
knot = -1.;
}
@@ -129,27 +130,35 @@ void KnotVector::Print(std::ostream &os) const
knot.Print(os, knot.Size());
}
void KnotVector::PrintFunctions(std::ostream &os, int samples) const
{
MFEM_VERIFY(GetNE(), "Elements not counted. Use GetElements().");
Vector shape(Order+1);
double x, dx = 1.0/double (samples - 1);
for (int i = 0; i <GetNE() ; i++)
/* @a cnt is a counter including elements between repeated knots if
present. This is required for usage of CalcShape. */
int cnt = 0;
for (int e = 0; e < GetNE(); e++, cnt++)
{
// Avoid printing shapes between repeated knots
if (!isElement(cnt)) { e--; continue; }
for (int j = 0; j <samples; j++)
{
x =j*dx;
os<< x + i;
os<< x + e;
CalcShape ( shape, i, x);
CalcShape ( shape, cnt, x);
for (int d = 0; d < Order+1; d++) { os<<"\t"<<shape[d]; }
CalcDShape ( shape, i, x);
CalcDShape ( shape, cnt, x);
for (int d = 0; d < Order+1; d++) { os<<"\t"<<shape[d]; }
CalcD2Shape ( shape, i, x);
CalcD2Shape ( shape, cnt, x);
for (int d = 0; d < Order+1; d++) { os<<"\t"<<shape[d]; }
os<<endl;
}
+4
View File
@@ -88,6 +88,10 @@ public:
void Print(std::ostream &out) const;
/** Prints the non-zero shape functions and their first and second
derivatives associated with the KnotVector per element. Use GetElements()
to count the elements before using this function. @a samples is the
number of samples of the shape functions per element.*/
void PrintFunctions(std::ostream &out, int samples=11) const;
/// Destroys KnotVector
+137 -214
View File
@@ -34,7 +34,6 @@ ParMesh::ParMesh(const ParMesh &pmesh, bool copy_nodes)
group_sedge(pmesh.group_sedge),
group_stria(pmesh.group_stria),
group_squad(pmesh.group_squad),
face_nbr_el_to_face(NULL),
glob_elem_offset(-1),
glob_offset_sequence(-1),
gtopo(pmesh.gtopo)
@@ -106,8 +105,7 @@ ParMesh& ParMesh::operator=(ParMesh &&mesh)
ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
int part_method)
: face_nbr_el_to_face(NULL)
, glob_elem_offset(-1)
: glob_elem_offset(-1)
, glob_offset_sequence(-1)
, gtopo(comm)
{
@@ -188,7 +186,7 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
if (Dim > 1)
{
el_to_edge = new Table;
NumOfEdges = Mesh::GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = Mesh::GetElementToEdgeTable(*el_to_edge);
}
STable3D *faces_tbl = NULL;
@@ -199,6 +197,19 @@ ParMesh::ParMesh(MPI_Comm comm, Mesh &mesh, int *partitioning_,
GenerateFaces();
// Make sure the be_to_face array is initialized.
// In 2D, it will be set in the above call to Mesh::GetElementToEdgeTable.
// In 3D, it will be set in GetElementToFaceTable.
// In 1D, we need to set it manually.
if (Dim == 1)
{
be_to_face.SetSize(NumOfBdrElements);
for (int i = 0; i < NumOfBdrElements; ++i)
{
be_to_face[i] = boundary[i]->GetVertices()[0];
}
}
ListOfIntegerSets groups;
{
// the first group is the local one
@@ -437,7 +448,7 @@ int ParMesh::BuildLocalBoundary(const Mesh& mesh, const int* partitioning,
for (int i = 0; i < mesh.GetNBE(); i++)
{
int edge = mesh.GetBdrElementEdgeIndex(i);
int edge = mesh.GetBdrElementFaceIndex(i);
int el1 = edge_element->GetRow(edge)[0];
if (partitioning[el1] == MyRank)
{
@@ -453,7 +464,7 @@ int ParMesh::BuildLocalBoundary(const Mesh& mesh, const int* partitioning,
boundary.SetSize(nbdry);
for (int i = 0; i < mesh.GetNBE(); i++)
{
int edge = mesh.GetBdrElementEdgeIndex(i);
int edge = mesh.GetBdrElementFaceIndex(i);
int el1 = edge_element->GetRow(edge)[0];
if (partitioning[el1] == MyRank)
{
@@ -854,7 +865,6 @@ ParMesh::ParMesh(const ParNCMesh &pncmesh)
: MyComm(pncmesh.MyComm)
, NRanks(pncmesh.NRanks)
, MyRank(pncmesh.MyRank)
, face_nbr_el_to_face(NULL)
, glob_elem_offset(-1)
, glob_offset_sequence(-1)
, gtopo(MyComm)
@@ -906,7 +916,7 @@ void ParMesh::FinalizeParTopo()
sface_lface.SetSize(nst + shared_quads.Size());
if (sface_lface.Size())
{
STable3D *faces_tbl = GetFacesTable();
auto faces_tbl = std::unique_ptr<STable3D>(GetFacesTable());
for (int st = 0; st < nst; st++)
{
const int *v = shared_trias[st].v;
@@ -917,13 +927,11 @@ void ParMesh::FinalizeParTopo()
const int *v = shared_quads[sq].v;
sface_lface[nst+sq] = (*faces_tbl)(v[0], v[1], v[2], v[3]);
}
delete faces_tbl;
}
}
ParMesh::ParMesh(MPI_Comm comm, istream &input, bool refine)
: face_nbr_el_to_face(NULL)
, glob_elem_offset(-1)
: glob_elem_offset(-1)
, glob_offset_sequence(-1)
, gtopo(comm)
{
@@ -1134,7 +1142,7 @@ void ParMesh::MakeRefined_(ParMesh &orig_mesh, int ref_factor, int ref_type)
MyComm = orig_mesh.GetComm();
NRanks = orig_mesh.GetNRanks();
MyRank = orig_mesh.GetMyRank();
face_nbr_el_to_face = NULL;
face_nbr_el_to_face = nullptr;
glob_elem_offset = -1;
glob_offset_sequence = -1;
gtopo = orig_mesh.gtopo;
@@ -2123,7 +2131,7 @@ void ParMesh::ExchangeFaceNbrData()
if (Nonconforming())
{
// with ParNCMesh we can set up face neighbors without communication
// with ParNCMesh we can set up face neighbors mostly without communication
pncmesh->GetFaceNeighbors(*this);
have_face_nbr_data = true;
@@ -2192,7 +2200,7 @@ void ParMesh::ExchangeFaceNbrData()
if (Dim == 3)
{
GetFaceNbrElementToFaceTable();
BuildFaceNbrElementToFaceTable();
}
if (del_tables) { delete gr_sface; }
@@ -2457,8 +2465,7 @@ void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
// convert the element data into face_nbr_elements
face_nbr_elements.SetSize(face_nbr_elements_offset[num_face_nbrs]);
face_nbr_el_ori.Clear();
face_nbr_el_ori.SetSize(face_nbr_elements_offset[num_face_nbrs], 6);
face_nbr_el_ori.reset(new Table(face_nbr_elements_offset[num_face_nbrs], 6));
while (true)
{
int fn;
@@ -2489,7 +2496,7 @@ void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
if (Dim == 3)
{
int nf = el->GetNFaces();
int * fn_ori = face_nbr_el_ori.GetRow(elem_off);
int * fn_ori = face_nbr_el_ori->GetRow(elem_off);
for (int j = 0; j < nf; j++)
{
fn_ori[j] = recv_elemdata[j];
@@ -2499,7 +2506,7 @@ void ParMesh::ExchangeFaceNbrData(Table *gr_sface, int *s2l_face)
face_nbr_elements[elem_off++] = el;
}
}
face_nbr_el_ori.Finalize();
face_nbr_el_ori->Finalize();
MPI_Waitall(num_face_nbrs, send_requests, statuses);
@@ -2721,191 +2728,118 @@ STable3D *ParMesh::GetSharedFacesTable()
return sfaces_tbl;
}
STable3D *ParMesh::GetFaceNbrElementToFaceTable(int ret_ftbl)
template <int N>
void
ParMesh::AddTriFaces(const Array<int> &elem_vertices,
const std::unique_ptr<STable3D> &faces,
const std::unique_ptr<STable3D> &shared_faces,
int elem, int start, int end, const int fverts[][N])
{
int i, *v;
STable3D * faces_tbl = GetFacesTable();
STable3D * sfaces_tbl = GetSharedFacesTable();
for (int i = start; i < end; ++i)
{
// Reference face vertices.
const auto fv = fverts[i];
// Element specific face vertices.
const Vert3 elem_fv(elem_vertices[fv[0]], elem_vertices[fv[1]],
elem_vertices[fv[2]]);
if (face_nbr_el_to_face != NULL)
{
delete face_nbr_el_to_face;
// Check amongst the faces of elements local to this rank for this set of vertices
const int lf = faces->Index(elem_fv.v[0], elem_fv.v[1], elem_fv.v[2]);
// If the face wasn't found amonst processor local elements, search the
// ghosts for this set of vertices.
const int sf = lf < 0 ? shared_faces->Index(elem_fv.v[0], elem_fv.v[1],
elem_fv.v[2]) : -1;
// If find local face -> use that
// else if find shared face -> shift and use that
// else no face found -> set to -1
const int face_to_add = lf < 0 ? (sf >= 0 ? sf + NumOfFaces : -1) : lf;
MFEM_ASSERT(sf >= 0 ||
lf >= 0, "Face must be from a local or a face neighbor element");
// Add this discovered face to the list of faces of this face neighbor element
face_nbr_el_to_face->Push(elem, face_to_add);
}
face_nbr_el_to_face = new Table(face_nbr_elements.Size(), 6);
for (i = 0; i < face_nbr_elements.Size(); i++)
}
void ParMesh::BuildFaceNbrElementToFaceTable()
{
const auto faces = std::unique_ptr<STable3D>(GetFacesTable());
const auto shared_faces = std::unique_ptr<STable3D>(GetSharedFacesTable());
face_nbr_el_to_face.reset(new Table(face_nbr_elements.Size(), 6));
Array<int> v;
// Helper for adding quadrilateral faces.
auto add_quad_faces = [&faces, &shared_faces, &v, this]
(int elem, int start, int end, const int fverts[][4])
{
v = face_nbr_elements[i]->GetVertices();
for (int i = start; i < end; ++i)
{
const int * const fv = fverts[i];
int k = 0;
int max = v[fv[0]];
if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
if (max < v[fv[3]]) { k = 3; }
int v0 = -1, v1 = -1, v2 = -1;
switch (k)
{
case 0:
v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
break;
case 1:
v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
break;
case 2:
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
break;
case 3:
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
break;
}
int lf = faces->Index(v0, v1, v2);
if (lf < 0)
{
lf = shared_faces->Index(v0, v1, v2);
if (lf >= 0)
{
lf += NumOfFaces;
}
}
face_nbr_el_to_face->Push(elem, lf);
}
};
for (int i = 0; i < face_nbr_elements.Size(); i++)
{
face_nbr_elements[i]->GetVertices(v);
switch (face_nbr_elements[i]->GetType())
{
case Element::TETRAHEDRON:
{
for (int j = 0; j < 4; j++)
{
const int *fv = tet_t::FaceVert[j];
int lf = faces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
if (lf < 0)
{
lf = sfaces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
if (lf >= 0)
{
lf += NumOfFaces;
}
}
face_nbr_el_to_face->Push(i, lf);
}
AddTriFaces(v, faces, shared_faces, i, 0, 4, tet_t::FaceVert);
break;
}
case Element::WEDGE:
{
for (int j = 0; j < 2; j++)
{
const int *fv = pri_t::FaceVert[j];
int lf = faces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
if (lf < 0)
{
lf = sfaces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
if (lf >= 0)
{
lf += NumOfFaces;
}
}
face_nbr_el_to_face->Push(i, lf);
}
for (int j = 2; j < 5; j++)
{
const int *fv = pri_t::FaceVert[j];
int k = 0;
int max = v[fv[0]];
if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
if (max < v[fv[3]]) { k = 3; }
int v0 = -1, v1 = -1, v2 = -1;
switch (k)
{
case 0:
v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
break;
case 1:
v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
break;
case 2:
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
break;
case 3:
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
break;
}
int lf = faces_tbl->Index(v0, v1, v2);
if (lf < 0)
{
lf = sfaces_tbl->Index(v0, v1, v2);
if (lf >= 0)
{
lf += NumOfFaces;
}
}
face_nbr_el_to_face->Push(i, lf);
}
AddTriFaces(v, faces, shared_faces, i, 0, 2, pri_t::FaceVert);
add_quad_faces(i, 2, 5, pri_t::FaceVert);
break;
}
case Element::PYRAMID:
{
for (int j = 0; j < 1; j++)
{
const int *fv = pyr_t::FaceVert[j];
int k = 0;
int max = v[fv[0]];
if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
if (max < v[fv[3]]) { k = 3; }
int v0 = -1, v1 = -1, v2 = -1;
switch (k)
{
case 0:
v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
break;
case 1:
v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
break;
case 2:
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
break;
case 3:
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
break;
}
int lf = faces_tbl->Index(v0, v1, v2);
if (lf < 0)
{
lf = sfaces_tbl->Index(v0, v1, v2);
if (lf >= 0)
{
lf += NumOfFaces;
}
}
face_nbr_el_to_face->Push(i, lf);
}
for (int j = 1; j < 5; j++)
{
const int *fv = pyr_t::FaceVert[j];
int lf = faces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
if (lf < 0)
{
lf = sfaces_tbl->Index(v[fv[0]], v[fv[1]], v[fv[2]]);
if (lf >= 0)
{
lf += NumOfFaces;
}
}
face_nbr_el_to_face->Push(i, lf);
}
add_quad_faces(i, 0, 1, pyr_t::FaceVert);
AddTriFaces(v, faces, shared_faces, i, 1, 5, pyr_t::FaceVert);
break;
}
case Element::HEXAHEDRON:
{
// find the face by the vertices with the smallest 3 numbers
// z = 0, y = 0, x = 1, y = 1, x = 0, z = 1
for (int j = 0; j < 6; j++)
{
const int *fv = hex_t::FaceVert[j];
int k = 0;
int max = v[fv[0]];
if (max < v[fv[1]]) { max = v[fv[1]], k = 1; }
if (max < v[fv[2]]) { max = v[fv[2]], k = 2; }
if (max < v[fv[3]]) { k = 3; }
int v0 = -1, v1 = -1, v2 = -1;
switch (k)
{
case 0:
v0 = v[fv[1]]; v1 = v[fv[2]]; v2 = v[fv[3]];
break;
case 1:
v0 = v[fv[0]]; v1 = v[fv[2]]; v2 = v[fv[3]];
break;
case 2:
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[3]];
break;
case 3:
v0 = v[fv[0]]; v1 = v[fv[1]]; v2 = v[fv[2]];
break;
}
int lf = faces_tbl->Index(v0, v1, v2);
if (lf < 0)
{
lf = sfaces_tbl->Index(v0, v1, v2);
if (lf >= 0)
{
lf += NumOfFaces;
}
}
face_nbr_el_to_face->Push(i, lf);
}
add_quad_faces(i, 0, 6, hex_t::FaceVert);
break;
}
default:
@@ -2913,14 +2847,6 @@ STable3D *ParMesh::GetFaceNbrElementToFaceTable(int ret_ftbl)
}
}
face_nbr_el_to_face->Finalize();
delete sfaces_tbl;
if (ret_ftbl)
{
return faces_tbl;
}
delete faces_tbl;
return NULL;
}
int ParMesh::GetFaceNbrRank(int fn) const
@@ -2941,33 +2867,28 @@ int ParMesh::GetFaceNbrRank(int fn) const
}
void
ParMesh::GetFaceNbrElementFaces(int i, Array<int> &fcs, Array<int> &cor) const
ParMesh::GetFaceNbrElementFaces(int i, Array<int> &faces,
Array<int> &orientations) const
{
int n, j;
int el_nbr = i - GetNE();
if (face_nbr_el_to_face)
if (face_nbr_el_to_face != nullptr && el_nbr < face_nbr_el_to_face->Size())
{
face_nbr_el_to_face->GetRow(el_nbr, fcs);
face_nbr_el_to_face->GetRow(el_nbr, faces);
}
else
{
MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
"face_nbr_el_to_face not generated.");
"face_nbr_el_to_face not generated correctly.");
}
if (el_nbr < face_nbr_el_ori.Size())
if (face_nbr_el_ori != nullptr && el_nbr < face_nbr_el_ori->Size())
{
const int * row = face_nbr_el_ori.GetRow(el_nbr);
n = fcs.Size();
cor.SetSize(n);
for (j=0; j<n; j++)
{
cor[j] = row[j];
}
face_nbr_el_ori->GetRow(el_nbr, orientations);
}
else
{
MFEM_ABORT("ParMesh::GetFaceNbrElementFaces(...) : "
"face_nbr_el_to_face not generated.");
"face_nbr_el_ori not generated correctly.");
}
}
@@ -3227,7 +3148,7 @@ int ParMesh::GetSharedFace(int sface) const
{
MFEM_ASSERT(Dim > 1, "");
const NCMesh::NCList &shared = pncmesh->GetSharedList(Dim-1);
int csize = (int) shared.conforming.Size();
int csize = shared.conforming.Size();
return sface < csize
? shared.conforming[sface].index
: shared.slaves[sface - csize].index;
@@ -3412,7 +3333,7 @@ void ParMesh::ReorientTetMesh()
GenerateFaces();
if (el_to_edge)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
}
else
@@ -3647,7 +3568,7 @@ void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
// 6. Update element-to-edge relations.
if (el_to_edge != NULL)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
} // 'if (Dim == 3)'
@@ -3885,7 +3806,7 @@ void ParMesh::LocalRefinement(const Array<int> &marked_el, int type)
if (el_to_edge != NULL)
{
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
GenerateFaces();
}
} // 'if (Dim == 2)'
@@ -3947,6 +3868,8 @@ void ParMesh::NonconformingRefinement(const Array<Refinement> &refinements,
"serial Mesh)");
}
ResetLazyData();
DeleteFaceNbrData();
// NOTE: no check of !refinements.Size(), in parallel we would have to reduce
@@ -4579,7 +4502,7 @@ void ParMesh::UniformRefinement3D()
DSTable v_to_v(NumOfVertices);
GetVertexToVertexTable(v_to_v);
STable3D *faces_tbl = GetFacesTable();
auto faces_tbl = std::unique_ptr<STable3D>(GetFacesTable());
// call Mesh::UniformRefinement3D_base so that it won't update the nodes
Array<int> f2qf;
@@ -4594,7 +4517,6 @@ void ParMesh::UniformRefinement3D()
// update the groups
UniformRefineGroups3D(old_nv, old_nedges, v_to_v, *faces_tbl,
f2qf.Size() ? &f2qf : NULL);
delete faces_tbl;
UpdateNodes();
}
@@ -5379,7 +5301,7 @@ Mesh ParMesh::GetSerialMesh(int save_rank) const
for (int e = 0; e < NumOfElements; e++)
{
const int attr = elements[e]->GetAttribute();
const int geom_type = elements[e]->GetGeometryType();;
const int geom_type = elements[e]->GetGeometryType();
ints.Append(attr);
ints.Append(geom_type);
pfespace_linear.GetElementDofs(e, dofs);
@@ -6693,6 +6615,8 @@ void ParMesh::Swap(ParMesh &other)
mfem::Swap(face_nbr_vertices, other.face_nbr_vertices);
mfem::Swap(send_face_nbr_elements, other.send_face_nbr_elements);
mfem::Swap(send_face_nbr_vertices, other.send_face_nbr_vertices);
std::swap(face_nbr_el_ori, other.face_nbr_el_ori);
std::swap(face_nbr_el_to_face, other.face_nbr_el_to_face);
// Nodes, NCMesh, and NURBSExtension are taken care of by Mesh::Swap
mfem::Swap(pncmesh, other.pncmesh);
@@ -6713,8 +6637,7 @@ void ParMesh::Destroy()
}
shared_edges.DeleteAll();
delete face_nbr_el_to_face;
face_nbr_el_to_face = NULL;
face_nbr_el_to_face = nullptr;
}
ParMesh::~ParMesh()
+34 -5
View File
@@ -77,8 +77,11 @@ protected:
// sface ids: all triangles first, then all quads
Array<int> sface_lface;
Table *face_nbr_el_to_face;
Table face_nbr_el_ori; // orientations for each face (from nbr processor)
/// Table that maps from face neighbor element number, to the face numbers of
/// that element.
std::unique_ptr<Table> face_nbr_el_to_face;
/// orientations for each face (from nbr processor)
std::unique_ptr<Table> face_nbr_el_ori;
IsoparametricTransformation FaceNbrTransformation;
@@ -113,7 +116,32 @@ protected:
bool DecodeFaceSplittings(HashTable<Hashed2> &v_to_v, const int *v,
const Array<unsigned> &codes, int &pos);
STable3D *GetFaceNbrElementToFaceTable(int ret_ftbl = 0);
// Given a completed FacesTable and SharedFacesTable, construct a table that
// maps from face neighbor element number, to the set of faces of that
// element. Store the resulting data in the member variable
// face_nbr_el_to_face. If the mesh is nonconforming, this also builds the
// the face_nbr_el_ori variable from the faces_info.
void BuildFaceNbrElementToFaceTable();
/**
* @brief Helper function for adding triangle face neighbor element to face
* table entries. Have to use a template here rather than lambda capture
* because the FaceVert entries in Geometry have inner size of 3 for tets and
* 4 for everything else.
*
* @tparam N Inner dimension on the fvert variable, 3 for tet, 4 otherwise
* @param[in] v Set of vertices for this element
* @param[in] faces Table of faces interior to this rank
* @param[in] shared_faces Table of faces shared by this rank and another
* @param[in] elem The face neighbor element
* @param[in] start Starting index into fverts
* @param[in] end End index into fverts
* @param[in] fverts Array of face vertices for this particular geometry.
*/
template <int N>
void AddTriFaces(const Array<int> &v, const std::unique_ptr<STable3D> &faces,
const std::unique_ptr<STable3D> &shared_faces,
int elem, int start, int end, const int fverts[][N]);
void GetFaceNbrElementTransformation(
int i, IsoparametricTransformation *ElTr);
@@ -287,7 +315,7 @@ protected:
public:
/// Default constructor. Create an empty @a ParMesh.
ParMesh() : MyComm(0), NRanks(0), MyRank(-1), face_nbr_el_to_face(NULL),
ParMesh() : MyComm(0), NRanks(0), MyRank(-1),
glob_elem_offset(-1), glob_offset_sequence(-1),
have_face_nbr_data(false), pncmesh(NULL) { }
@@ -465,7 +493,8 @@ public:
int GetFaceNbrRank(int fn) const;
/** Similar to Mesh::GetElementFaces */
void GetFaceNbrElementFaces(int i, Array<int> &fcs, Array<int> &cor) const;
void GetFaceNbrElementFaces(int i, Array<int> &faces,
Array<int> &orientation) const;
/** Similar to Mesh::GetFaceToElementTable with added face-neighbor elements
with indices offset by the local number of elements. */
+135 -1
View File
@@ -16,10 +16,12 @@
#include "mesh_headers.hpp"
#include "pncmesh.hpp"
#include "../general/binaryio.hpp"
#include "../general/communication.hpp"
#include <numeric> // std::accumulate
#include <map>
#include <climits> // INT_MIN, INT_MAX
#include <array>
namespace mfem
{
@@ -887,6 +889,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
Array<Element*> fnbr;
Array<Connection> send_elems;
std::map<int, std::vector<int>> recv_elems;
// Counts the number of slave faces of a master. This may be larger than the
// number of shared slaves if there exist degenerate slave-faces from face-edge constraints.
@@ -901,6 +904,11 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
fnbr.Reserve(bound);
send_elems.Reserve(bound);
// If there are face neighbor elements with triangular faces, the
// `face_nbr_el_ori` structure will need to be built. This requires
// communication so we attempt to avoid it by checking first.
bool face_nbr_w_tri_faces = false;
// go over all shared faces and collect face neighbor elements
for (int i = 0; i < shared.conforming.Size(); i++)
{
@@ -914,8 +922,12 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
if (e[0]->rank == MyRank) { std::swap(e[0], e[1]); }
MFEM_ASSERT(e[0]->rank != MyRank && e[1]->rank == MyRank, "");
face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[0]->geom));
face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[1]->geom));
fnbr.Append(e[0]);
send_elems.Append(Connection(e[0]->rank, e[1]->index));
recv_elems[e[0]->rank].push_back(e[0]->index);
}
for (int i = 0; i < shared.masters.Size(); i++)
@@ -924,7 +936,7 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
for (int j = mf.slaves_begin; j < mf.slaves_end; j++)
{
const Slave &sf = full_list.slaves[j];
if (sf.element < 0) { continue; }
if (sf.element < 0 || sf.index < 0) { continue; }
MFEM_ASSERT(mf.element >= 0, "");
Element* e[2] = { &elements[mf.element], &elements[sf.element] };
@@ -938,8 +950,12 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
}
if (loc0) { std::swap(e[0], e[1]); }
face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[0]->geom));
face_nbr_w_tri_faces |= !Geometry::IsTensorProduct(Geometry::Type(e[1]->geom));
fnbr.Append(e[0]);
send_elems.Append(Connection(e[0]->rank, e[1]->index));
recv_elems[e[0]->rank].push_back(e[0]->index);
}
}
@@ -1022,6 +1038,13 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
send_elems.Sort();
send_elems.Unique();
for (auto &kv : recv_elems)
{
std::sort(kv.second.begin(), kv.second.end());
kv.second.erase(std::unique(kv.second.begin(), kv.second.end()),
kv.second.end());
}
for (int i = 0, last_rank = -1; i < send_elems.Size(); i++)
{
Connection &c = send_elems[i];
@@ -1175,6 +1198,117 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
}
}
// In 3D some extra orientation data structures can be needed.
if (Dim == 3)
{
// Populates face_nbr_el_to_face, always needed.
pmesh.BuildFaceNbrElementToFaceTable();
if (face_nbr_w_tri_faces)
{
// There are face neighbor elements with triangular faces, need to
// perform communication to ensure the orientation is valid.
using RankToOrientation = std::map<int, std::vector<std::array<int, 6>>>;
constexpr std::array<int, 6> unset_ori{{-1,-1,-1,-1,-1,-1}};
const int rank = pmesh.GetMyRank();
// Loop over send elems, compute the orientation and place in the
// buffer to send to each processor. Note elements are
// lexicographically sorted with rank and element number, and this
// ordering holds across processors.
RankToOrientation send_rank_to_face_neighbor_orientations;
Array<int> orientations, faces;
// send_elems goes from rank of the receiving processor, to the index
// of the face neighbor element on this processor.
for (const auto &se : send_elems)
{
const auto &true_rank = pmesh.face_nbr_group[se.from];
pmesh.GetElementFaces(se.to, faces, orientations);
// Place a new entry of unset orientations
send_rank_to_face_neighbor_orientations[true_rank].emplace_back(unset_ori);
// Copy the entries, any unset faces will remain -1.
std::copy(orientations.begin(), orientations.end(),
send_rank_to_face_neighbor_orientations[true_rank].back().begin());
}
// Initialize the receive buffers and resize to match the expected
// number of elements coming in. The copy ensures the appropriate rank
// pairings are in place, and for a purely conformal interface, the
// resize is a no-op.
auto recv_rank_to_face_neighbor_orientations =
send_rank_to_face_neighbor_orientations;
for (auto &kv : recv_rank_to_face_neighbor_orientations)
{
kv.second.resize(recv_elems[kv.first].size());
}
// For asynchronous send/recv, will use arrays of requests to monitor the
// status of the connections.
std::vector<MPI_Request> send_requests, recv_requests;
std::vector<MPI_Status> status(nranks);
// NOTE: This is CRITICAL, to ensure the addresses of these requests
// do not change between the send/recv and the wait.
send_requests.reserve(nranks);
recv_requests.reserve(nranks);
// Shared face communication is bidirectional -> any rank to whom
// orientations must be sent, will need to send orientations back. The
// orientation data is contiguous because std::array<int,6> is an
// aggregate. Loop over each communication pairing, and dispatch the
// buffer loaded with all the orientation data.
for (const auto &kv : send_rank_to_face_neighbor_orientations)
{
send_requests.emplace_back(); // instantiate a request for tracking.
// low rank sends on low, high rank sends on high.
const int send_tag = (rank < kv.first)
? std::min(rank, kv.first)
: std::max(rank, kv.first);
MPI_Isend(&kv.second[0][0], int(kv.second.size() * 6),
MPI_INT, kv.first, send_tag, pmesh.MyComm, &send_requests.back());
}
// Loop over the communication pairing again, and receive the
// symmetric buffer from the other processor.
for (auto &kv : recv_rank_to_face_neighbor_orientations)
{
recv_requests.emplace_back(); // instantiate a request for tracking
// low rank receives on high, high rank receives on low.
const int recv_tag = (rank < kv.first)
? std::max(rank, kv.first)
: std::min(rank, kv.first);
MPI_Irecv(&kv.second[0][0], int(kv.second.size() * 6),
MPI_INT, kv.first, recv_tag, pmesh.MyComm, &recv_requests.back());
}
// Wait until all receive buffers are full before beginning to process.
MPI_Waitall(int(recv_requests.size()), recv_requests.data(), status.data());
pmesh.face_nbr_el_ori.reset(new Table(pmesh.face_nbr_elements.Size(), 6));
int elem = 0;
for (const auto &kv : recv_rank_to_face_neighbor_orientations)
{
// All elements associated to this face-neighbor rank
for (const auto &eo : kv.second)
{
std::copy(eo.begin(), eo.end(), pmesh.face_nbr_el_ori->GetRow(elem));
++elem;
}
}
pmesh.face_nbr_el_ori->Finalize();
// Must wait for all send buffers to be released before the scope closes.
MPI_Waitall(int(send_requests.size()), send_requests.data(), status.data());
}
}
// NOTE: this function skips ParMesh::send_face_nbr_vertices and
// ParMesh::face_nbr_vertices_offset, these are not used outside of ParMesh
}
+2 -4
View File
@@ -108,9 +108,7 @@ public:
passed. */
void Rebalance(const Array<int> *custom_partition = NULL);
// interface for ParFiniteElementSpace
int GetNElements() const { return NElements; }
int GetNGhostVertices() const { return NGhostVertices; }
@@ -141,8 +139,8 @@ public:
return (index < NFaces) ? face_orient[index] : 0;
}
typedef short GroupId;
typedef std::vector<int> CommGroup;
using GroupId = short;
using CommGroup = std::vector<int>;
/// Return vertex/edge/face ('entity' == 0/1/2, resp.) owner.
GroupId GetEntityOwnerId(int entity, int index)
-3
View File
@@ -1028,9 +1028,6 @@ void ParPumiMesh::UpdateMesh(const ParMesh* AdaptedpMesh)
bel_to_edge = (AdaptedpMesh->bel_to_edge) ?
new Table(*(AdaptedpMesh->bel_to_edge)) : NULL;
// Copy the boudary-to-edge Array, be_to_edge (2D)
AdaptedpMesh->be_to_edge.Copy(be_to_edge);
// Duplicate the faces and faces_info.
faces.SetSize(AdaptedpMesh->faces.Size());
for (int i = 0; i < faces.Size(); i++)
+15 -9
View File
@@ -235,10 +235,13 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
// Add boundaries
{
int num_of_faces_or_edges =
(Dim == 3) ? NumOfFaces :
((Dim == 2) ? NumOfEdges : NumOfVertices);
Array<int> &be2face = (Dim == 2) ? be_to_edge : be_to_face;
const int num_codim_1 = [this]()
{
if (Dim == 1) { return NumOfVertices; }
else if (Dim == 2) { return NumOfEdges; }
else if (Dim == 3) { return NumOfFaces; }
else { MFEM_ABORT("Invalid dimension."); return -1; }
}();
if (Dim == 3)
{
@@ -249,7 +252,7 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
}
NumOfBdrElements = 0;
for (int i = 0; i < num_of_faces_or_edges; i++)
for (int i = 0; i < num_codim_1; i++)
{
if (GetFaceInformation(i).IsBoundary())
{
@@ -258,14 +261,17 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
}
boundary.SetSize(NumOfBdrElements);
be2face.SetSize(NumOfBdrElements);
be_to_face.SetSize(NumOfBdrElements);
Array<int> parent_face_to_be = parent.GetFaceToBdrElMap();
int max_bdr_attr = parent.bdr_attributes.Max();
for (int i = 0, j = 0; i < num_of_faces_or_edges; i++)
for (int i = 0, j = 0; i < num_codim_1; i++)
{
if (GetFaceInformation(i).IsBoundary())
{
boundary[j] = faces[i]->Duplicate(this);
be_to_face[j] = i;
if (from == SubMesh::From::Domain && Dim >= 2)
{
int pbeid = Dim == 3 ? parent_face_to_be[parent_face_ids_[i]] :
@@ -283,7 +289,7 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
{
boundary[j]->SetAttribute(SubMesh::GENERATED_ATTRIBUTE);
}
be2face[j++] = i;
++j;
}
}
}
@@ -323,7 +329,7 @@ ParSubMesh::ParSubMesh(const ParMesh &parent, SubMesh::From from,
if (Dim > 1)
{
if (!el_to_edge) { el_to_edge = new Table; }
NumOfEdges = GetElementToEdgeTable(*el_to_edge, be_to_edge);
NumOfEdges = GetElementToEdgeTable(*el_to_edge);
}
SetAttributes();
+7 -12
View File
@@ -317,8 +317,7 @@ ParTransferMap::CorrectFaceOrientations(const ParFiniteElementSpace &fes,
if (parent_face_ori.Size() == 0) { return; }
VDofTransformation vdoftrans(fes.GetVDim(),
fes.GetOrdering());
DofTransformation doftrans(fes.GetVDim(), fes.GetOrdering());
int dim = mesh->Dimension();
bool face = (dim == 3);
@@ -332,17 +331,13 @@ ParTransferMap::CorrectFaceOrientations(const ParFiniteElementSpace &fes,
if (parent_face_ori[i] == 0) { continue; }
Geometry::Type geom = face ? mesh->GetFaceGeometry(i) :
mesh->GetElementGeometry(i);;
mesh->GetElementGeometry(i);
StatelessDofTransformation * doftrans =
fec->DofTransformationForGeometry(geom);
if (doftrans == NULL) { continue; }
vdoftrans.SetDofTransformation(*doftrans);
if (!fec->DofTransformationForGeometry(geom)) { continue; }
doftrans.SetDofTransformation(*fec->DofTransformationForGeometry(geom));
Fo[0] = parent_face_ori[i];
vdoftrans.SetFaceOrientations(Fo);
doftrans.SetFaceOrientations(Fo);
if (face)
{
@@ -356,12 +351,12 @@ ParTransferMap::CorrectFaceOrientations(const ParFiniteElementSpace &fes,
if (sub_to_parent_map)
{
src.GetSubVector(vdofs, face_vector);
vdoftrans.TransformPrimal(face_vector);
doftrans.TransformPrimal(face_vector);
}
else
{
dst.GetSubVector(vdofs, face_vector);
vdoftrans.InvTransformPrimal(face_vector);
doftrans.InvTransformPrimal(face_vector);
}
for (int j = 0; j < vdofs.Size(); j++)
+2 -2
View File
@@ -65,7 +65,7 @@ SubMesh::SubMesh(const Mesh &parent, From from,
for (int i = 0; i < NumOfBdrElements; i++)
{
int pbeid = parent_face_to_be[parent_face_ids_[GetBdrFace(i)]];
int pbeid = parent_face_to_be[parent_face_ids_[GetBdrElementFaceIndex(i)]];
if (pbeid != -1)
{
int attr = parent.GetBdrElement(pbeid)->GetAttribute();
@@ -117,7 +117,7 @@ SubMesh::SubMesh(const Mesh &parent, From from,
for (int i = 0; i < NumOfBdrElements; i++)
{
int pbeid = parent_face_to_be[parent_edge_ids_[GetBdrFace(i)]];
int pbeid = parent_face_to_be[parent_edge_ids_[GetBdrElementFaceIndex(i)]];
if (pbeid != -1)
{
int attr = parent.GetBdrElement(pbeid)->GetAttribute();
+7 -12
View File
@@ -241,8 +241,7 @@ void TransferMap::CorrectFaceOrientations(const FiniteElementSpace &fes,
if (parent_face_ori.Size() == 0) { return; }
VDofTransformation vdoftrans(fes.GetVDim(),
fes.GetOrdering());
DofTransformation doftrans(fes.GetVDim(), fes.GetOrdering());
int dim = mesh->Dimension();
bool face = (dim == 3);
@@ -256,17 +255,13 @@ void TransferMap::CorrectFaceOrientations(const FiniteElementSpace &fes,
if (parent_face_ori[i] == 0) { continue; }
Geometry::Type geom = face ? mesh->GetFaceGeometry(i) :
mesh->GetElementGeometry(i);;
mesh->GetElementGeometry(i);
StatelessDofTransformation * doftrans =
fec->DofTransformationForGeometry(geom);
if (doftrans == NULL) { continue; }
vdoftrans.SetDofTransformation(*doftrans);
if (!fec->DofTransformationForGeometry(geom)) { continue; }
doftrans.SetDofTransformation(*fec->DofTransformationForGeometry(geom));
Fo[0] = parent_face_ori[i];
vdoftrans.SetFaceOrientations(Fo);
doftrans.SetFaceOrientations(Fo);
if (face)
{
@@ -280,12 +275,12 @@ void TransferMap::CorrectFaceOrientations(const FiniteElementSpace &fes,
if (sub_to_parent_map)
{
src.GetSubVector(vdofs, face_vector);
vdoftrans.TransformPrimal(face_vector);
doftrans.TransformPrimal(face_vector);
}
else
{
dst.GetSubVector(vdofs, face_vector);
vdoftrans.InvTransformPrimal(face_vector);
doftrans.InvTransformPrimal(face_vector);
}
for (int j = 0; j < vdofs.Size(); j++)
+1 -1
View File
@@ -1403,7 +1403,7 @@ void maxwell_solution_curlcurl(const Vector & X,
curlcurlE.resize(dim);
for (int i = 0; i < dim; ++i)
{
curlcurlE[i] = 0.0;;
curlcurlE[i] = 0.0;
}
switch (prob)
{
+1 -2
View File
@@ -347,8 +347,7 @@ int main (int argc, char *argv[])
{
if ((double) rand() / RAND_MAX < 0.5)
{
int element_order = sc_fes.GetElementOrder(e);
sc_fes.SetElementOrder(e, element_order + 1);
sc_fes.SetElementOrder(e, order + 1);
}
}
sc_fes.Update(false);
+1 -1
View File
@@ -278,4 +278,4 @@ void SolveCG(Operator &A, Solver &P, const Vector &B, Vector &X)
cout << "Done.\nIterations: " << cg.GetNumIterations()
<< "\nElapsed: " << tic_toc.RealTime() << endl;
}
};
}
+1 -1
View File
@@ -35,7 +35,7 @@
// Adapted analytic shape:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -bnd -qt 1 -qo 8
// Adapted analytic size+orientation:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 100 -bnd -qt 1 -qo 8
// Adapted analytic shape+orientation:
// mesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
//
+1 -1
View File
@@ -1012,7 +1012,7 @@ struct QuarterPeach: public Surface
for (int i = 0; i < GetNBE(); i++)
{
Element *el = GetBdrElement(i);
const int fn = GetBdrElementEdgeIndex(i);
const int fn = GetBdrElementFaceIndex(i);
MFEM_VERIFY(!FaceIsTrueInterior(fn),"");
Array<int> vertices;
GetFaceVertices(fn, vertices);
+1 -2
View File
@@ -359,7 +359,6 @@ int main (int argc, char *argv[])
}
pmesh->ExchangeFaceNbrData();
// Surface fitting.
L2_FECollection mat_coll(0, dim);
H1_FECollection surf_fit_fec(mesh_poly_deg, dim);
@@ -432,7 +431,6 @@ int main (int argc, char *argv[])
}
else { surf_fit_bg_gf0->ProjectCoefficient(*ls_coeff); }
surf_fit_bg_grad_fes =
new ParFiniteElementSpace(pmesh_surf_fit_bg, surf_fit_bg_fec, dim);
surf_fit_bg_grad = new ParGridFunction(surf_fit_bg_grad_fes);
@@ -630,6 +628,7 @@ int main (int argc, char *argv[])
}
}
}
pmesh->SetAttributes();
// 13. Setup the final NonlinearForm (which defines the integral of interest,
// its first and second derivatives). Here we can use a combination of
+1 -1
View File
@@ -35,7 +35,7 @@
// Adapted analytic shape:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 2 -tid 4 -ni 200 -bnd -qt 1 -qo 8
// Adapted analytic size+orientation:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 200 -bnd -qt 1 -qo 8 -fd
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 14 -tid 4 -ni 200 -bnd -qt 1 -qo 8
// Adapted analytic shape+orientation:
// mpirun -np 4 pmesh-optimizer -m square01.mesh -o 2 -rs 2 -mid 85 -tid 4 -ni 100 -bnd -qt 1 -qo 8 -fd
//
+1 -1
View File
@@ -1019,7 +1019,7 @@ struct QuarterPeach: public Surface
for (int i = 0; i < GetNBE(); i++)
{
Element *el = GetBdrElement(i);
const int fn = GetBdrElementEdgeIndex(i);
const int fn = GetBdrElementFaceIndex(i);
MFEM_VERIFY(!FaceIsTrueInterior(fn),"");
Array<int> vertices;
GetFaceVertices(fn, vertices);
+4 -2
View File
@@ -322,17 +322,19 @@ int main(int argc, char *argv[])
char vishost[] = "localhost";
int visport = 19916;
socketstream cyl_sol_sock(vishost, visport);
socketstream cyl_sol_sock;
if (visualization)
{
cyl_sol_sock.open(vishost, visport);
cyl_sol_sock << "parallel " << num_procs << " " << myid << "\n";
cyl_sol_sock.precision(8);
cyl_sol_sock << "solution\n" << cylinder_submesh << temperature_cylinder_gf <<
"pause\n" << std::flush;
}
socketstream block_sol_sock(vishost, visport);
socketstream block_sol_sock;
if (visualization)
{
block_sol_sock.open(vishost, visport);
block_sol_sock << "parallel " << num_procs << " " << myid << "\n";
block_sol_sock.precision(8);
block_sol_sock << "solution\n" << block_submesh << temperature_block_gf <<
+7
View File
@@ -17,6 +17,10 @@ add_mfem_miniapp(nurbs_curveint
MAIN nurbs_curveint.cpp
LIBRARIES mfem)
add_mfem_miniapp(nurbs_printfunc
MAIN nurbs_printfunc.cpp
LIBRARIES mfem)
add_mfem_miniapp(nurbs_patch_ex1
MAIN nurbs_patch_ex1.cpp
LIBRARIES mfem)
@@ -67,6 +71,9 @@ if (MFEM_ENABLE_TESTING)
add_test(NAME nurbs_ex1_weak_patch_format_r1_ser
COMMAND $<TARGET_FILE:nurbs_ex1> -no-vis
-m ${PROJECT_SOURCE_DIR}/data/square-disc-nurbs-patch.mesh -o 2 --weak-bc -r 1)
add_test(NAME nurbs_printfunc
COMMAND $<TARGET_FILE:nurbs_printfunc>)
if (MFEM_USE_LAPACK)
add_test(NAME nurbs_patch_ex1_o4_r2_iro8_patcha_ser
+4 -1
View File
@@ -21,7 +21,7 @@ CONFIG_MK = $(MFEM_BUILD_DIR)/config/config.mk
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_curveint
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_curveint nurbs_printfunc
PAR_MINIAPPS = nurbs_ex1p nurbs_ex11p
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
@@ -109,6 +109,9 @@ nurbs_curveint-test-seq: nurbs_curveint
@$(call mfem-test,$<,, NURBS miniapp,$(CI_ARGS_1))
@$(call mfem-test,$<,, NURBS miniapp,$(CI_ARGS_2))
nurbs_printfunc-test-seq: nurbs_printfunc
@$(call mfem-test,$<,, NURBS miniapp)
EX1P_ARGS_1 :=
EX1P_ARGS_2 := -m ../../data/pipe-nurbs-2d.mesh -o 2 -no-ibp
EX1P_ARGS_3 := -m ../../data/ball-nurbs.mesh -o 2 --weak-bc -r 0

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