Compare commits

...
331 Commits
Author SHA1 Message Date
thatguynoe 2a8898a89e correct essential boundary comment 2026-07-07 15:07:41 -04:00
thatguynoe 07c92a7e00 Merge branch 'ex43' of https://github.com/mfem/mfem into ex43 2026-07-07 14:23:25 -04:00
Noe ReyesandSocratis Petrides 66e5d3782b set ess_bdr correctly in parallel
Co-authored-by: Socratis Petrides <petrides1@llnl.gov>
2026-07-07 14:23:13 -04:00
thatguynoe 4d3928d9b9 decrease solver tolerance in serial 2026-07-07 14:21:12 -04:00
thatguynoe 784148ca84 use doxygen compatible comments 2026-07-07 14:19:00 -04:00
thatguynoe aed9fb51c2 remove useless variable 2026-07-07 13:40:08 -04:00
thatguynoe 1bb3b662c6 add boundary-only check 2026-07-07 13:38:08 -04:00
thatguynoe e92710ae06 correct variable name 2026-07-07 13:35:38 -04:00
thatguynoe c95bbd8c56 add sample run for star.mesh 2026-01-22 00:18:20 -05:00
thatguynoe f9d9479d4d update examples/CMakeLists.txt 2026-01-21 20:24:39 -05:00
thatguynoe c6b5e80ebc update doc/CodeDocumentation.dox 2026-01-21 20:24:22 -05:00
Noe Reyes 8944ee325b Merge branch 'master' into ex43 2026-01-21 20:12:35 -05:00
Tzanio Kolev f2a42123f7 Merge pull request #4326 from mfem/nurbs_nodal_interp
Nodal interpolation for NURBS
2026-01-21 08:53:42 -08:00
Tzanio Kolev 85c95269ad Merge branch 'master' into nurbs_nodal_interp 2026-01-21 08:47:56 -08:00
Tzanio Kolev dfd55716bd Merge pull request #5196 from mfem/pncmesh-update
ParNCMesh update bug fix
2026-01-19 11:28:49 -08:00
Tzanio Kolev 49a50027dd Merge pull request #5167 from mfem/nurbs-variable-1d-dev
Nurbs variable 1d dev
2026-01-19 11:28:21 -08:00
Tzanio Kolev b5af0e9f81 Merge pull request #5184 from mfem/najlkin/fix-ex21-rhs
[BUG] Fixed rhs in example 21
2026-01-16 10:50:21 -08:00
Tzanio Kolev 33452bd3e8 Merge pull request #5189 from mfem/najlkin/fix-ghost-face
[BUG] Fixed initialization of ghost face transformations
2026-01-16 10:49:52 -08:00
Tzanio Kolev 27ed1f64f9 Merge pull request #5068 from farscape-project/typos
Documentation fixes and minor refactoring
2026-01-16 10:49:34 -08:00
Dylan Copeland 230dc805f3 Unit test. 2026-01-15 11:40:57 -08:00
Kenneth Weiss f6bb6982e8 Updates CHANGELOG 2026-01-14 14:35:15 -08:00
Dylan Copeland 8df194e6b0 Check whether update has already been done, to avoid an incorrect additional update. 2026-01-14 13:52:29 -08:00
Jan Nikl a0f2250a76 Minor decoration. 2026-01-14 10:14:10 -08:00
Kenneth Weiss 882dedf5f3 Changes based on PR review suggestions 2026-01-13 21:14:21 -08:00
Kenneth Weiss 5f051efc49 Merge branch 'master' into nurbs-variable-1d-dev 2026-01-13 21:04:44 -08:00
Jan Nikl 20bf4d5a98 Added a unit test for shared faces. 2026-01-13 13:24:31 -08:00
Tzanio Kolev 8921101d14 Merge pull request #5176 from mfem/remhos-kernel-prep
Remhos benchmarking prep
2026-01-13 11:18:08 -08:00
Jan Nikl f74b394c8a Changed the ghost face transformation face number to the ghost. 2026-01-13 11:01:18 -08:00
Ido Akkerman e282d4518e Add changes to CHANGELOG 2026-01-13 13:20:32 +01:00
Jan Nikl 5e43a958b7 Minor correction of GetGhostFaceTransformation(). 2026-01-12 09:53:59 -08:00
Jan Nikl 14fc04e9ee Fixed initialization of ghost face transformations. 2026-01-12 09:47:43 -08:00
Tzanio Kolev 8102cb8141 Merge pull request #5175 from mfem/artv3/grifun-mem-type-const
Specify device memory type in grid function constructor
2026-01-11 16:05:03 -08:00
Jan Nikl 99c858c389 Fixed ex21 rhs integrator. 2026-01-09 20:43:53 -08:00
Kenneth Weiss 23986c5c64 Generalizes a check in NURBSExtension::CheckPatches
Per PR review suggestion.
2026-01-08 10:13:21 -08:00
Tzanio Kolev af23695747 Merge branch 'master' into artv3/grifun-mem-type-const 2026-01-07 14:19:37 -08:00
Kenneth Weiss 1f97289d2a Merge branch 'master' into nurbs-variable-1d-dev 2026-01-07 13:07:48 -08:00
Kenneth Weiss 19db07b30d Simplifies 1D checks in NURBSExtension::CheckPatches()
Per PR review discussion. The checks were unnecessarily overconstraining.
2026-01-07 13:05:40 -08:00
Kenneth Weiss 419f890e5c Adds datafiles for 1D NURBS tests instead of inlining the meshes as strings
Per PR review suggestion.
2026-01-07 12:22:11 -08:00
Tzanio Kolev 3216d86e6c Merge pull request #4496 from mfem/refactor-cmake-data-directory
Added CMake dependencies on individual data files
2026-01-07 10:59:04 -08:00
Kenneth Weiss 9c85a6365f Renames NURBSExtension function to GetPatchSpaceDimension()
Per PR review discussion.
2026-01-07 10:42:45 -08:00
Kenneth Weiss 2a10ab2f48 Merge branch 'master' into nurbs-variable-1d-dev 2026-01-07 10:26:23 -08:00
Vladimir Z Tomov 19e60a38cc style 2026-01-06 17:28:43 -08:00
Vladimir Z Tomov e2a382dca4 Moved DGMassInverse::DGMassCGIteration to hpp. Removed specializations (done in remhos). 2026-01-06 17:26:29 -08:00
Arturo VargasandTom Stitt ffaae2ea82 Update fem/gridfunc.hpp
Co-authored-by: Tom Stitt <stitt4@llnl.gov>
2026-01-06 15:59:01 -08:00
Chris Vogl 7f17f33763 merged master and resolved conflicts in miniapps/nurbs/CMakeLists.txt 2026-01-06 13:20:23 -08:00
Ido Akkerman d33fd770cd Merge branch 'master' into nurbs_nodal_interp 2026-01-06 10:44:15 +01:00
Vladimir Z Tomov b2947bbaca comments 2026-01-05 13:11:10 -08:00
Vladimir Z Tomov 6b7b6e8966 Return Mesh and use Array. 2026-01-05 09:57:18 -08:00
Tzanio Kolev 4a17243415 Merge pull request #5079 from mfem/ode-stage-solve
Update ODESolver to work with ImplicitSolve for stage-state, u, and stage-slope, du/dt
2026-01-02 11:41:38 -08:00
John Camier b1edfc3497 Merge branch 'master' into artv3/grifun-mem-type-const 2025-12-30 12:50:16 -08:00
John Camier 654fd1b611 Merge branch 'master' into remhos-kernel-prep 2025-12-30 12:49:42 -08:00
Tzanio Kolev 5731d2f22b Merge pull request #5123 from mfem/fix-nurbs-orientation
Fix nurbs orientations
2025-12-29 14:23:39 -08:00
Tzanio Kolev 150cbcf8c5 Merge pull request #5111 from mfem/sparse-smoothers-transpose
Add MultTranspose for DSmoother and GSSmoother
2025-12-29 13:36:17 -08:00
Will Pazner 1e35d9e49f Merge pull request #5092 from mfem/gpu-uuid
Added a way to get the GPU uuid
2025-12-29 12:04:11 -08:00
Tzanio Kolev ba2ebd318b Merge pull request #5061 from mfem/dg-lor-neumann
Add support for Neumann boundary conditions in the LOR DG preconditioner
2025-12-27 14:25:25 -08:00
Tzanio Kolev a9fd723a4e Merge pull request #5159 from mfem/table-readwrite-fix
Add missing HostReadWriteJ in Table
2025-12-27 14:24:21 -08:00
Sohail Reddy 0fa8a5c90d moved ComputeSlopeFromState function to public 2025-12-26 16:15:25 -08:00
Kenneth Weiss cdc6893087 Cleanup -- removes some unnecessary changes 2025-12-26 12:57:30 -08:00
Kenneth Weiss 059bbd7f2e Merge branch 'master' into nurbs-variable-1d-dev 2025-12-26 12:16:58 -08:00
Vladimir Z Tomov 53d7207786 updated comment 2025-12-25 21:18:13 -08:00
Kenneth Weiss 6a99a7a63e Consolidates 1D/2D/3D logic for NURBSExtention::Load() 2025-12-25 20:50:49 -08:00
Kenneth Weiss 1d7e1e9bb4 Adds 1D support to NURBSExtension::CheckPatches() 2025-12-25 18:20:02 -08:00
Kenneth Weiss ea0bec7d13 In NURBSExtension header, moves GetPatchDim() near Dimension()
Per PR suggestion
2025-12-25 17:46:33 -08:00
Kenneth Weiss ad6baa0cff Adds 1D support to NURBSExtension::CheckKVDirection
Refactors several functions to use a new utility function
NURBSExtension::GetPatchKnotVectorEdges() which handles 1D, 2D and 3D.
2025-12-25 15:13:20 -08:00
Kenneth Weiss a049988e16 Consolidated 1D with 2D/3D handling in NURBSExtension utility functions
NURBSExtension::CreateComprehensiveKV(), NURBSExtension::UpdateUniqueKV(), and NURBSExtension::ConsistentKVSets()
2025-12-25 14:36:34 -08:00
Kenneth Weiss db75e98609 Adds 1D support to NURBSExtension::ConsistentKVSets() 2025-12-25 14:30:13 -08:00
Kenneth Weiss 88d2d8bdb1 Adds unit test for shared knot vectors in 1D 2025-12-25 13:43:28 -08:00
Kenneth Weiss 902f2fdb08 Simplify code in NURBSExtension::CreateComprehensiveKV() 2025-12-25 13:41:26 -08:00
Kenneth Weiss 46a22b93eb Renames NURBSExtension::GetPhysicalDim() to GetPatchDim()
Per PR suggestion, this is better than falling back to the reference space dimension. We now MFEM_VERIFY that HavePatches() is true.
2025-12-25 12:47:10 -08:00
Kenneth Weiss 58c8905261 Removes dof2patch from Set1DSolutionVector
Per PR comments, dof2patch is related to NC patches, and is not relevant in 1D
2025-12-25 12:13:04 -08:00
John Camier d191906f7a Merge branch 'master' into remhos-kernel-prep 2025-12-25 08:40:37 -08:00
Tzanio Kolev 8e3a7b90fc Merge pull request #5168 from mfem/remove-culib-from-magma
Fix HIP+MAGMA MFEM Link
2025-12-25 04:46:29 -08:00
Kenneth Weiss 25fc5c18dd Adds edges section for 1D NURBS in Mesh::PrintTopoEdges()
This allows the meshes to be viewed in VisIt.
2025-12-24 18:40:17 -08:00
Kenneth Weiss dd3414a3a6 Removes asssumption that number of patches == number of KnotVectors in 1D 2025-12-24 17:02:48 -08:00
Vladimir Z Tomov e6a990c2a5 minor 2025-12-24 11:55:36 -08:00
John Camier 935c5e4967 Merge branch 'master' into remhos-kernel-prep 2025-12-24 11:24:01 -08:00
Tzanio Kolev 2a6f07a2fb Merge pull request #5164 from mfem/particles-navier-dir-fix
Fix directory path in navier-bifurcation
2025-12-24 07:50:14 -08:00
Tzanio Kolev bab0f55127 Merge pull request #5166 from mfem/fix-1d-nurbs
Fix patch offset error in for 1D nurbs
2025-12-24 07:49:00 -08:00
Tzanio Kolev 821b7b31cf Merge pull request #5160 from mfem/task/chapman39/rename-serac-to-smith
Rename Serac to Smith
2025-12-24 07:48:25 -08:00
Tzanio Kolev 8bdb7f9a0d Merge pull request #5163 from mfem/fix-5151
mfem CMake C++ standard
2025-12-24 07:48:02 -08:00
Tzanio Kolev 7f314e25ce Merge pull request #5014 from mfem/nbeams/gko-updates-distrib
Add distributed Ginkgo support
2025-12-24 07:47:31 -08:00
Vladimir Z Tomov 1b5f71f1fb doxygen 2025-12-23 15:51:39 -08:00
Vladimir Z Tomov 65092ce621 minor 2025-12-23 15:45:35 -08:00
Vladimir Z Tomov 2f38aa9f73 doxygen fix 2025-12-23 15:39:41 -08:00
Vladimir Z Tomov e30bf35f60 minor 2025-12-23 15:31:58 -08:00
Vladimir Z Tomov c2623f407b minor 2025-12-23 15:30:50 -08:00
Vladimir Z Tomov d26be8cbca Kernels needed for the Remhos gpu tests.
Function to setup mesh partitioning.
2025-12-23 15:27:09 -08:00
Arturo Vargas e3f710518e specify memory type in grid function constructor 2025-12-23 10:43:52 -08:00
Brendan Keith 31551a3b03 Merge branch 'master' into ex43 2025-12-22 17:34:07 -05:00
Andrew Ho dee699eede switch to use the cuda runtime API for compatibility with MFEM_GPU_CHECK 2025-12-22 06:41:53 -08:00
Tzanio Kolev dc74fa2aeb Merge branch 'master' into nurbs_nodal_interp 2025-12-21 11:58:30 -08:00
Kenneth Weiss 9f8b7ed137 Fixes spaceDim after calling UpdateNURBS on the mesh
This is called after mesh.DegreeElevate()
2025-12-18 14:45:46 -08:00
Tom Stitt 6eb6a632c9 remove cublas and cusparse from MAGMA_LIB because it breaks hip 2025-12-18 12:58:49 -08:00
Kenneth Weiss 371e7389ec Properly handle 1D curves in 2D/3D space when we don't have patches defined 2025-12-18 12:51:17 -08:00
Kenneth Weiss a49bbf288e Bugfix -- using the wrong vector dimension
Caught by sanitizer tests.
2025-12-18 11:14:38 -08:00
Kenneth Weiss f003563fcd make style 2025-12-17 19:17:07 -08:00
Kenneth Weiss b0eb063b35 Adds a unit test to procedurally generate a 1D NURBS mesh in 2D 2025-12-17 18:34:22 -08:00
Kenneth Weiss 51c140f4ff Adds a multispan nurbs-segment test mesh and updates unit tests 2025-12-17 17:51:51 -08:00
Kenneth Weiss b0ed0c81e4 Adds 3D NURBS curves meshes and tests them 2025-12-17 17:19:37 -08:00
Kenneth Weiss d7e7ac746f Renames nurbs segment mesh files to include physical dimension 2025-12-17 16:42:38 -08:00
Kenneth Weiss 1535b5a932 Adds support for loading 1D NURBS patches
Adds a 1D NURBS patch mesh along with a unit test.
2025-12-17 15:40:18 -08:00
Justin Laughlin 5db0bffc2f Address feedback in review - merge my changes with Dylans 2025-12-17 14:55:01 -08:00
Dylan Copeland 3446841376 Name change. 2025-12-17 14:32:16 -08:00
Dylan Copeland c0d5cd54c5 Documentation. 2025-12-17 14:26:04 -08:00
Kenneth Weiss 10b38b4d2b Bugfix for loading 1D NURBS mesh with varying curve orders
Also updates unit test and associated data file to contain
a linear, quadratic and cubic curve.
2025-12-17 14:22:44 -08:00
Kenneth Weiss 09acac006e Adds unit test for loading uniform degree 1D NURBS mesh 2025-12-17 14:10:01 -08:00
Ido Akkerman eb606dd1b7 Fix patch offset error in for 1D nurbs 2025-12-17 13:27:21 +01:00
Mittal, Ketan 1282722ecf fix directory path due to change in navier location 2025-12-16 12:11:22 -08:00
Andrew Ho d1ca02310d Merge branch 'master' into nbeams/gko-updates-distrib 2025-12-16 12:05:14 -08:00
Andrew Ho 4fa3f6a15d Merge branch 'master' into refactor-cmake-data-directory 2025-12-16 12:05:03 -08:00
Andrew Ho b6285fb7eb ensure downstream libraries build with at least the same C++ standard as mfem 2025-12-16 09:31:07 -08:00
Ido Akkerman 4ab7f66cc8 Fix Banded Lapack solver 2025-12-16 12:09:11 +01:00
Ketan Mittal 81272cb468 Merge branch 'master' into gpu-uuid 2025-12-15 13:03:13 -08:00
Justin Laughlin 9ed202928e Remove repeated unit tests in CMakeLists.txt 2025-12-15 12:27:16 -08:00
Justin Laughlin 809eb996ca Merge branch 'master' into fix-nurbs-orientation 2025-12-15 12:21:40 -08:00
Justin Laughlin 62b27d3989 Add unit test for CorrectPatchTopoOrientations 2025-12-15 12:21:27 -08:00
Justin Laughlin b444c89ab0 Add test nurbs mesh - flipped edges 2025-12-15 11:39:39 -08:00
Alex Tyler Chapman eafb82943b Merge branch 'master' into task/chapman39/rename-serac-to-smith 2025-12-15 10:37:21 -08:00
chapman39 a2243413e6 rename serac to smith 2025-12-15 10:32:16 -08:00
Tzanio Kolev 8452c8cc7f Merge pull request #5153 from mfem/doc-fixes
ex41 and contact doc fixes
2025-12-15 08:39:46 -08:00
Will Pazner bd4df5fad0 Add missing HostReadWriteJ in Table 2025-12-14 11:31:57 -08:00
Socratis PetridesandJan Nikl 3d25fe2e3d Update doc/CodeDocumentation.dox
Co-authored-by: Jan Nikl <nikl1@llnl.gov>
2025-12-12 12:48:59 -08:00
Socratis Petrides 9093d35699 ex41 and contact doc fixes 2025-12-12 11:42:00 -08:00
Will Pazner b718da4997 Merge remote-tracking branch 'origin/master' into sparse-smoothers-transpose
# Conflicts:
#	tests/unit/CMakeLists.txt
2025-12-09 09:17:13 -08:00
Dylan Copeland 3c1f6d96f6 Fixed memory leaks. 2025-12-03 14:27:41 -08:00
Dylan Copeland 6e46adeae2 Unused variable. 2025-12-03 12:40:26 -08:00
Dylan Copeland a25c71d57e Merge branch 'master' of github.com:mfem/mfem into fix-nurbs-orientation 2025-12-03 11:34:59 -08:00
Dylan Copeland de99fd0caa Generalized algorithm for correcting knotvector orientations. Changed the reflector miniapp to output a NURBS mesh for NURBS input. 2025-12-03 11:34:19 -08:00
Andrew Ho fe6e84553f Merge branch 'master' into refactor-cmake-data-directory 2025-12-02 11:23:48 -08:00
thatguynoe 411a35656e apply style 2025-12-02 13:22:06 -05:00
Noe Reyes 125e883264 Merge branch 'master' into ex43 2025-12-02 12:53:36 -05:00
thatguynoe cff888bbaa shorten name of linear form integrator 2025-12-02 12:51:14 -05:00
thatguynoe ca8aa8aef2 include connection with ex28 2025-12-02 12:49:44 -05:00
thatguynoe 240dcdc693 correct comment about nt 2025-12-01 20:06:04 -05:00
thatguynoe b35a103a9d fixed -> displaced in description 2025-12-01 20:05:39 -05:00
thatguynoe 40edc5b23c add ex43 2025-12-01 17:26:27 -05:00
Andrew Ho 30e470ec12 Merge branch 'master' into gpu-uuid 2025-12-01 12:51:44 -08:00
Andrew Ho 15060b8da4 expanded documentation 2025-12-01 12:39:43 -08:00
Ido Akkerman 62bbb917ca Remove non-nurbs examples 2025-12-01 13:26:52 +01:00
Ido Akkerman 28d95ecf0c Remove non-nurbs examples 2025-12-01 13:26:36 +01:00
Dylan Copeland 117fb91d34 Shadowed variable. 2025-11-28 11:23:44 -08:00
Dylan Copeland 6227904dbd Remove main.cpp. 2025-11-28 11:13:08 -08:00
Dylan Copeland 1b07ccd1b8 Remove testing drivers. 2025-11-28 11:12:04 -08:00
Dylan Copeland d8576eeb3b Set knotvector orientations by sweeping through patches. 2025-11-28 10:38:46 -08:00
Justin Laughlin 7579eb32c9 Cleanup 2025-11-23 12:05:42 -08:00
Justin Laughlin edc818f036 Cleanup 2025-11-23 11:58:11 -08:00
Justin Laughlin 3f4d577042 Style 2025-11-23 11:43:05 -08:00
Justin Laughlin df23247bc8 Generalize CheckPatchTopoOrientation to 3D 2025-11-23 11:42:23 -08:00
Justin Laughlin c9246e28f4 Add CheckPatchTopoOrientation 2025-11-23 11:31:07 -08:00
Justin Laughlin 4b2cc48687 Add 3patch mesh to miniapps/nurbs/meshes 2025-11-20 20:39:28 -08:00
Justin Laughlin 3c910d2651 Cleanup + WIP orientation flipper 2025-11-20 20:38:28 -08:00
Justin Laughlin 2f4f364a27 small fixes to sbox.cpp 2025-11-20 20:37:29 -08:00
Justin Laughlin 46714ce661 Make CheckPatches optional 2025-11-20 12:36:25 -08:00
Sohail Reddy f768ba1c75 Changed ImplicitVariable to ImplicitVariableType 2025-11-20 11:04:10 -08:00
Justin Laughlin d5969dfb94 Flip orientations - minimally working 2025-11-19 22:03:30 -08:00
Will Pazner 5bf0ff54d0 Use relative tolerance in SparseSmoother::EnsureTranspose 2025-11-17 14:21:37 -08:00
Will Pazner 2def15c3f0 constexpr 2025-11-17 10:10:18 -08:00
Will Pazner ac257b0ee9 Add old constructors for backwards compatibility
There is no implicit conversion from int to enum types
2025-11-17 10:10:11 -08:00
Will Pazner 3bdcae8928 Improve Doxygen for DSmoother and GSSmoother 2025-11-17 09:36:52 -08:00
Will Pazner c95ab91de1 Special case for DSmoother::MultTranspose
Don't need to form the transpose when only the action of D (and not A) is
needed.
2025-11-17 09:36:34 -08:00
Ido Akkerman b51338e308 Correct DEPRECATE statement 2025-11-17 08:15:31 +01:00
Andrew Ho 29cfcec850 Merge branch 'master' into gpu-uuid 2025-11-14 00:46:33 -08:00
Sohail Reddy 7c89e72c98 fixed style 2025-11-13 15:11:09 -08:00
Sohail Reddy 5c938034c8 Removed html tables, cleaned documentation 2025-11-13 14:13:30 -08:00
Will Pazner a5f68ad7a6 Add MultTranspose for DSmoother and GSSmoother
For non-symmetric matrices, this requires forming the transpose matrix.

For l1-Jacobi and lumped Jacobi, we don't implement the transpose action for
non-symmetric matrices.
2025-11-12 17:05:53 -08:00
Andrew Ho b993e42c24 Merge branch 'master' into gpu-uuid 2025-11-10 13:16:26 -08:00
Andrew Ho 79d02d1f3b missing a few dependencies for copying data 2025-11-03 14:09:59 -08:00
Andrew Ho 709fe826dd update other data copy directories to use new method 2025-11-03 12:41:38 -08:00
f3a53ab812 Refactor copy_data target to track individual data files
Co-authored-by: Chris Vogl <vogl2@llnl.gov>
Co-authored-by: Justin Laughlin <justinglaughlin@gmail.com>
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-11-03 17:53:25 +00:00
Andrew HoandWill Pazner fc4f4fb5ed Update general/device.cpp
Co-authored-by: Will Pazner <11493037+pazner@users.noreply.github.com>
2025-10-27 14:39:39 -07:00
Andrew Ho b74f76bdc4 Added a way to get the GPU uuid 2025-10-27 13:32:49 -07:00
Sohail Reddy 0d7b7d0766 fixed typo in doc 2025-10-24 16:00:04 -07:00
Sohail Reddy a5835794e5 fixed examples to use new variable names 2025-10-24 14:31:21 -07:00
Sohail Reddy db8c62eaf5 Added documentation on switching between solving for stage state and slope, updated examples and variable names. 2025-10-24 14:22:12 -07:00
Nuno Nobre faaaa4b62a Remove only (explicit) occurence of NDEBUG 2025-10-21 22:40:23 +01:00
Sohail Reddy 83232d67d1 disabled Implicit stage-stage solve for SecondOrderODESolvers 2025-10-20 13:46:45 -07:00
Sohail Reddy 5fd5b76e47 Updated examples ex9 and ex16 to demonstrate use of implicit stage-state vs stage-slope solve. 2025-10-19 16:51:07 -07:00
Sohail Reddy b57c18b664 Added capability to ImplicitSolve for either stage slope, k=du/dt, or the stage state. Defaults to stage-slope for compatibility with existing TimeDependentOperators 2025-10-19 16:42:33 -07:00
Nuno Nobre 888c2bc308 Keep attribute-to-marker special case for miniapps only 2025-10-18 15:13:54 +01:00
Tzanio Kolev b2ab00eec7 Merge branch 'master' into nurbs_nodal_interp 2025-10-16 06:49:45 -07:00
Ido Akkerman 5e6e2fdd83 Add examples to documentation and add capability to changelog 2025-10-16 10:05:26 +02:00
Nuno Nobre 1b10de0da7 Refactor redundant implementation of AttrToMarker 2025-10-13 20:45:53 +01:00
Nuno Nobre 61587437e5 Allow constructing/updating PWConstCoefficient with a temp Vector 2025-10-13 20:45:53 +01:00
Nuno Nobre bf21910fe4 Fix submesh attributes documentation 2025-10-13 20:45:53 +01:00
Nuno Nobre 99aa716c89 Fix fec documentation table 2025-10-13 20:45:49 +01:00
Nuno Nobre 5484856fc0 Fix typos in code documentation comments 2025-10-13 20:41:45 +01:00
Will Pazner 9338a6ea61 Add support for Neumann boundary conditions in the LOR DG preconditioner
Don't add boundary penalty terms where markers are unset or if no boundary face
integrator has been added to the form.
2025-10-09 18:17:38 -07:00
nbeamsandAndrew Ho 65b2525ad7 Improve HYPRE/Ginkgo integer type interop
Co-authored-by: Andrew Ho <ho37@llnl.gov>
2025-10-09 02:26:00 +00:00
Ido Akkerman 13727dace4 Change projection name in parallel 2025-10-08 13:35:14 +02:00
Ido Akkerman b47c3f4b94 Make style 2025-10-08 13:23:01 +02:00
Ido Akkerman 02dfb6319f Rename projection types3 + change timestep 2025-10-08 13:10:48 +02:00
Ido Akkerman 9e28f2da81 Rename projection types2 2025-10-08 13:10:32 +02:00
Ido Akkerman 749d2c0b66 Rename projection types 2025-10-08 13:10:06 +02:00
Andrew Ho 67311104a5 Merge branch 'master' into nbeams/gko-updates-distrib 2025-10-06 16:40:50 -07:00
Ido Akkerman bdd476b87e remove files 2025-10-03 11:40:48 +02:00
Ido Akkerman 29bbdd4694 update gitignore 2025-10-02 13:28:09 +02:00
Ido Akkerman cd6bcb0890 Add files to ignore list 2025-10-02 12:29:03 +02:00
Ido Akkerman 4b6f25a743 Change default values for nurbs_ex10 2025-10-02 12:25:19 +02:00
Ido Akkerman 26937bdd2b Small cosmetic changes 2025-10-02 11:50:59 +02:00
Andrew Ho addb6db73e Merge branch 'master' into nbeams/gko-updates-distrib 2025-09-25 15:24:25 -07:00
Ido Akkerman 9c8d50656f Merge branch 'master' into nurbs_nodal_interp 2025-09-23 12:43:27 +02:00
Tzanio Kolev d1333c2732 Merge branch 'master' into nbeams/gko-updates-distrib 2025-09-17 03:30:12 -07:00
Ido Akkerman 26b618aef7 Improved demko routine, also added demko tests for each order 2025-09-12 10:59:44 +02:00
nbeams 05b60c9c7b Remove sqrt from setting iterative solver tolerances in Ginkgo solvers 2025-09-10 22:21:32 +00:00
nbeams d630bf5250 Add distributed support for Ginkgo solvers and preconditioners 2025-09-10 22:21:27 +00:00
Ido Akkerman d3bcc68e2a Improve demko compute routine 2025-09-08 15:48:53 +02:00
Ido Akkerman 694e78eb39 Remove debud stuff from nurbs example 2025-08-14 12:14:55 +02:00
Ido Akkerman 883acbc190 Add array of spaces test case to nursb miniapps 2025-08-13 16:30:07 +02:00
Ido Akkerman 70b021c228 Add projection type flag 2025-08-13 16:29:27 +02:00
Ido Akkerman 178acf2113 Make style 2025-07-18 15:40:00 +02:00
Ido Akkerman bcc475d13c Add local L2 projection for NURBS VectorFE 2025-07-18 15:38:36 +02:00
Ido Akkerman c2e9390a36 Add small Row L2 utility to densematrix 2025-07-18 15:38:09 +02:00
Ido Akkerman c390f35b95 Add L2 local projection for the VectorFE case -- also in parallel 2025-07-18 09:34:03 +02:00
Ido Akkerman ccb013a4ab Fix mem leak 2025-07-17 15:56:18 +02:00
Ido Akkerman 6f83f57737 remove space in name 2025-07-17 15:56:05 +02:00
Ido Akkerman c01fefe638 Remove nurbs_ex24p, and clean nurbs_ex24 2025-07-17 15:43:58 +02:00
Ido Akkerman a96f2e5227 Adding local L2 projection for vectorcoeff on array of FE. Also add parallel LocalL2 Projection 2025-07-17 14:36:34 +02:00
Ido Akkerman 75c7e1e350 Clean nurbs_ex10p 2025-07-17 14:34:48 +02:00
Ido Akkerman cfd21dd69e Merge remote-tracking branch 'origin/vector_coeff_comp' into nurbs_nodal_interp 2025-07-17 13:31:35 +02:00
Ido Akkerman 0d1dfae3d7 Parallel version of nurbs_ex10 2025-07-17 12:50:05 +02:00
Ido Akkerman e7157ad835 Convert ex10 for NURBS -- to check projection of vectorcoeff on fespace of an array of fe 2025-07-17 12:29:17 +02:00
Ido Akkerman 8fd826dfbf Fix shadow 2025-07-17 09:41:14 +02:00
Ido Akkerman 080361c668 Fix documenation 2 2025-07-17 09:39:33 +02:00
Ido Akkerman cc8ba26bbb Fix documenation 2025-07-17 09:39:10 +02:00
Ido Akkerman 70dcb6902d Merge fixes and inspired improvements 2025-07-17 09:24:26 +02:00
Ido Akkerman 71050660b8 Fix merge error 2025-07-16 17:54:12 +02:00
Ido Akkerman 817e3b340d Make style 2025-07-16 17:47:49 +02:00
Ido Akkerman a5e64f7c80 Merge branch 'master' into nurbs_nodal_interp 2025-07-16 17:44:48 +02:00
Ido Akkerman ec69300bc1 Merge branch 'master' into nurbs_nodal_interp 2025-07-16 17:37:21 +02:00
Ido Akkerman 946a396b33 Fix nurbs_ex24 2025-07-16 17:32:19 +02:00
Ido Akkerman 095cc69a45 Adding preliminary implementation of LocalL2 projection 2025-07-16 17:11:55 +02:00
Ido Akkerman 479c2f7cc0 Revert back to master II 2025-07-16 15:56:46 +02:00
Ido Akkerman 4c5181bddb Revert back to master 2025-07-16 15:49:52 +02:00
Ido Akkerman 704c150d70 Change all examples back to master version 2025-07-16 15:38:41 +02:00
Ido Akkerman 08d5a2a44f Change back mesh and fespace 2025-07-16 15:29:31 +02:00
Ido Akkerman 4f575e6708 Remove NURBS fecollection selection routines 2025-07-16 14:43:41 +02:00
Ido Akkerman 1d7c4785fa Add vector and matrix component coefficient 2025-07-16 13:56:44 +02:00
Tzanio Kolev 771f947918 Merge branch 'master' into nurbs_nodal_interp 2025-07-01 12:46:46 -07:00
Veselin Dobrev 7023ecd83b Merge branch 'master' into nurbs_nodal_interp 2025-06-24 15:26:54 -07:00
Veselin Dobrev 54300f99ab Small formatting changes in nurbs_ex24.cpp 2025-06-24 12:07:40 -07:00
Justin Laughlin efe2804020 Add global projection option to nurbs_ex24 (default true) 2025-06-23 10:46:44 -07:00
Justin LaughlinandVeselin Dobrev 7b2c295670 Update fem/gridfunc.cpp
Co-authored-by: Veselin Dobrev <v-dobrev@users.noreply.github.com>
2025-06-23 09:31:50 -07:00
Justin Laughlin b872e07a4a Add GetRangeType(2) to ProjectCoefficientGlobalL2 check 2025-06-19 13:04:00 -07:00
Justin Laughlin 8f152bd7ad Change nurbs_ex24 ProjectCoefficient -> ProjectCoefficientGlobalL2 2025-06-19 12:19:57 -07:00
Veselin Dobrev 1ce8a82200 Updates related to DofTransformation modifications in 'master' 2025-06-17 21:34:31 -07:00
Veselin Dobrev 05c5bae7bf Merge branch 'master' into nurbs_nodal_interp
Resolved conflicts:
   fem/gridfunc.cpp
2025-06-17 21:27:19 -07:00
Veselin Dobrev 5c0587765f Updates to address issues when mering this PR in 'next' 2025-06-14 23:12:52 -07:00
John Camier 9e140eecfa Merge branch 'master' into nurbs_nodal_interp 2025-06-14 10:54:54 -07:00
camierjs 4a46614e33 Guard doftrans usage 2025-06-14 09:27:34 -07:00
camierjs 2bef31bee0 Avoid using internal DofTransformation object in gridfunc 2025-06-14 09:12:16 -07:00
Justin Laughlin 7feb9341b3 Fix warning from overloaded FiniteElement::Project in NURBS FE classes 2025-06-10 15:56:01 -07:00
Justin Laughlin 33e88aa7ad Allow Demko points to be recomputed 2025-06-10 15:36:23 -07:00
Justin Laughlin 47335190e0 Add some documentation 2025-06-10 15:30:09 -07:00
Justin Laughlin b02c99b9ef Revert FiniteElementCollection changes in examples 2025-06-10 15:18:28 -07:00
Justin Laughlin 96f6292915 Add NURBSext check to GroupComm in GridFunction::ProjectCoefficient 2025-06-10 14:21:37 -07:00
Justin Laughlin 59142c46bd Merge branch 'master' into nurbs_nodal_interp 2025-05-29 10:21:19 -07:00
Justin Laughlin d06558db12 Merge branch 'master' into nurbs_nodal_interp 2025-05-05 15:59:41 -07:00
Ido Akkerman 1eb05fe75c Fix bug 2024-10-03 11:06:27 +02:00
Ido Akkerman 07ed6bfb6e Merge branch 'master' into nurbs_nodal_interp 2024-10-03 11:06:10 +02:00
Ido Akkerman bb52bc02ac Merge branch 'master' into nurbs_nodal_interp 2024-09-17 12:19:29 +02:00
Ido Akkerman e4766b309a Need to make function virtual 2024-09-12 14:46:31 +02:00
Ido Akkerman 6a3cfb85c3 Add parallel vector projection case 2024-09-12 14:24:22 +02:00
IdoAkkerman e5fbfcf0c8 Add comment 2024-07-12 12:13:20 +02:00
IdoAkkerman 5026449f49 Correct typo 2024-07-12 12:13:07 +02:00
IdoAkkerman daf5b446a0 Remove app 2024-07-12 12:12:28 +02:00
IdoAkkerman 70ce649d6c Add commandline option 2024-07-12 12:11:52 +02:00
IdoAkkerman 4f2da87ca7 Merge remote-tracking branch 'origin/master' into nurbs_nodal_interp 2024-07-11 10:25:54 +02:00
IdoAkkerman 67e13d9c40 Fix parallel solve + comment + remove pointer + make style 2024-07-11 10:25:40 +02:00
IdoAkkerman 0f85bd80c3 Typos + L2 global projection in parallel 2024-07-10 10:31:07 +02:00
IdoAkkerman b9a60afd77 gitignore pt2 2024-07-09 16:16:35 +02:00
IdoAkkerman f9957edc77 Add file to ignore list 2024-07-09 14:54:36 +02:00
IdoAkkerman c828c72ec6 Add comments on definition mechanism 2024-07-08 15:46:18 +02:00
IdoAkkerman 4976d446a2 Add global L2 projection to Gridfunction 2024-07-08 15:22:30 +02:00
IdoAkkerman 1594ac92c5 Fix mem error 2024-07-08 15:06:17 +02:00
IdoAkkerman 3787362e97 Remove file 2024-07-08 15:02:50 +02:00
IdoAkkerman 97a9330f71 Second round of comments 2024-07-08 10:58:15 +02:00
IdoAkkerman a52d97b44d Rename GetParam to GetKnotLocation 2024-07-05 12:21:24 +02:00
IdoAkkerman b6d7817bd6 Remaining comments addressed 2024-07-05 12:02:44 +02:00
IdoAkkerman 06c2bcbac1 Merge remote-tracking branch 'origin/master' into nurbs_nodal_interp 2024-07-05 10:25:23 +02:00
IdoAkkerman df786b2dfc Addressing most points from dylan copeland 2024-07-05 10:25:01 +02:00
IdoAkkerman 5ba2a73a37 Correct mem leak 2024-07-01 16:18:41 +02:00
IdoAkkerman 6118eb20cf Cosmetic changes 2024-07-01 13:15:24 +02:00
IdoAkkerman 2f0bb591ca Add changes to log 2024-06-27 10:01:12 +02:00
IdoAkkerman a9a687a780 Change to Botella points 2024-06-27 10:00:51 +02:00
IdoAkkerman bf27c77c86 Merge remote-tracking branch 'origin/master' into nurbs_nodal_interp 2024-06-27 09:47:44 +02:00
IdoAkkerman 217f5f70e3 Merge remote-tracking branch 'origin/master' into nurbs_nodal_interp 2024-06-21 13:46:11 +02:00
IdoAkkerman 43025ad7cb Remove cout 2024-06-17 11:50:54 +02:00
IdoAkkerman 16d03b0cdc Merge remote-tracking branch 'origin/master' into nurbs_nodal_interp 2024-06-17 10:13:41 +02:00
IdoAkkerman 6ba83329a3 make style 2024-06-17 10:13:23 +02:00
IdoAkkerman 247119adc3 SMALL important fix -- checked routines 2024-06-17 10:13:07 +02:00
IdoAkkerman e72bd42913 Add projection to NURBS Vector Fe basis 2024-06-17 09:11:22 +02:00
IdoAkkerman 3e5c4abeef Clean gridfun projection functions 2024-06-17 09:06:33 +02:00
IdoAkkerman 057732b2df Add Nodal injection test miniapp 2024-06-17 09:06:00 +02:00
IdoAkkerman 69bf849af8 Merge branch 'master' into nurbs_nodal_interp 2024-06-12 17:24:33 +02:00
IdoAkkerman 0515f4695a Fix pendantic error 2024-06-06 15:14:00 +02:00
IdoAkkerman 9463b1b68e Fix pedantic and output files - \2 2024-06-04 11:26:57 +02:00
IdoAkkerman 50bb49fd0c Fix pedantic and output files 2024-06-04 11:01:07 +02:00
IdoAkkerman d2cb937744 Merge remote-tracking branch 'origin/master' into nurbs_nodal_interp 2024-06-04 10:28:08 +02:00
IdoAkkerman d212192b27 Merge branch 'nurbs_nodal_interp' of github.com:mfem/mfem into nurbs_nodal_interp 2024-06-04 10:26:07 +02:00
IdoAkkerman 775a6e38de Bug fix 2024-06-04 10:25:59 +02:00
IdoAkkerman 2e66d2bc6d Fix deprecated 2024-06-04 10:05:43 +02:00
Ido Akkerman 8fb1804c8a Update gridfunc.cpp 2024-05-31 23:51:02 +02:00
Ido Akkerman 47dde022a3 Update gridfunc.cpp 2024-05-31 23:42:09 +02:00
Ido Akkerman d77488e91d Update nurbs.cpp 2024-05-31 23:31:49 +02:00
IdoAkkerman eee3fa1285 Remove debug statement 2024-05-31 13:12:20 +02:00
IdoAkkerman 0ce54fea42 Small bugfix 2024-05-31 13:11:59 +02:00
IdoAkkerman 04bc1180af Remove debug output 2024-05-31 12:50:36 +02:00
IdoAkkerman 710e747f05 Missing example 2024-05-31 12:48:02 +02:00
IdoAkkerman c72436d718 Allow selection of NURBSFE in some examples 2024-05-31 12:47:43 +02:00
IdoAkkerman 3ae5df47b8 Communicate in parallel 2024-05-31 12:46:35 +02:00
IdoAkkerman 9a80d45519 Switch to better signal value 2024-05-31 12:46:10 +02:00
IdoAkkerman 7764148c77 Nicer shorthand 2024-05-31 11:48:14 +02:00
IdoAkkerman c5866395f4 Add vector support 2024-05-31 11:47:33 +02:00
IdoAkkerman b01f679eb2 Simplify selection of correct fe collection 2024-05-31 10:51:24 +02:00
IdoAkkerman 9fb31e3339 Init NURBS fespace a lot easier 2024-05-31 10:50:26 +02:00
IdoAkkerman 335592d8b3 Important index fix 2024-05-31 10:49:39 +02:00
IdoAkkerman fd4e49dac7 Beautify 2024-05-30 17:34:14 +02:00
IdoAkkerman a4376f597e Small tweak to nurbs_ex1 2024-05-30 17:29:25 +02:00
IdoAkkerman 45596d0efd Non zero BCs in example -- use Coefficient Proejctor 2024-05-30 17:25:49 +02:00
IdoAkkerman 54c4bfea26 make style 2024-05-30 17:24:29 +02:00
IdoAkkerman 4bf54a7633 Further improvements to nurbs mesh info printer 2024-05-30 17:24:06 +02:00
IdoAkkerman f86e08b979 Add NURBS specific projection routine to GF 2024-05-30 17:23:29 +02:00
IdoAkkerman e3b3688c26 Add NURBS specific projection to NURBS FE 2024-05-30 17:22:53 +02:00
IdoAkkerman d109c7eb05 Add check for knot spans 2024-05-30 17:22:11 +02:00
IdoAkkerman 85d89a8dcd make style 2024-05-30 17:21:18 +02:00
IdoAkkerman deabbdd18a Add nurbs mesh infor printing miniapp 2024-05-30 14:54:46 +02:00
IdoAkkerman 477e642582 Remove debug comment 2024-05-30 14:54:15 +02:00
IdoAkkerman 99e8701696 Make functions deprecated 2024-05-30 14:53:50 +02:00
IdoAkkerman f2745a149c Switch to new, and fixed, location routines 2024-05-30 13:39:57 +02:00
IdoAkkerman 1c84b89c47 Add botella,demko,greville poins and unit test 2024-05-30 13:11:05 +02:00
IdoAkkerman bea1969e5c NURBS as nodalFE 2024-05-23 14:03:32 +02:00
100 changed files with 9910 additions and 1117 deletions
+9
View File
@@ -313,6 +313,8 @@ miniapps/nurbs/nurbs_ex1
miniapps/nurbs/nurbs_ex1p
miniapps/nurbs/nurbs_ex3
miniapps/nurbs/nurbs_ex5
miniapps/nurbs/nurbs_ex10
miniapps/nurbs/nurbs_ex10p
miniapps/nurbs/nurbs_ex11p
miniapps/nurbs/nurbs_ex24
miniapps/nurbs/nurbs_solenoidal
@@ -338,7 +340,14 @@ miniapps/nurbs/nurbs_naca_cmesh
miniapps/nurbs/naca-cmesh.mesh
miniapps/nurbs/glvis_naca-cmesh.mesh
miniapps/nurbs/Naca_cmesh
miniapps/nurbs/nurbs_mesh_info
miniapps/nurbs/k*_*.dat
miniapps/nurbs/*-Surface.mesh
miniapps/nurbs/*.mesh
miniapps/nurbs/*.sol
miniapps/nurbs/deformed.*
miniapps/nurbs/elastic_energy.*
miniapps/nurbs/velocity.*
miniapps/performance/ex1
miniapps/performance/ex1p
+11
View File
@@ -11,6 +11,17 @@
Version 4.9.1 (development)
===========================
Discretization improvements
---------------------------
- Improved the gridfunction projection routines. Projections work for Scalar,
Vector and VectorFE, also NURBS versions. Optionally different types of
projections can be selected, default behaviour has not changed.
Meshing improvements
--------------------
- Improved support for 1D NURBS meshes with variable order, including using
the patches construct for 1D NURBS meshes.
Version 4.9, released on Dec 11, 2025
=====================================
+11 -5
View File
@@ -723,6 +723,7 @@ set(MFEM_INSTALL_DIR ${CMAKE_INSTALL_PREFIX})
# Declaring the library
mfem_add_library(mfem ${SOURCES} ${HEADERS} ${MASTER_HEADERS})
target_compile_features(mfem PUBLIC cxx_std_${CMAKE_CXX_STANDARD})
# message(STATUS "TPL_LIBRARIES = ${TPL_LIBRARIES}")
target_link_libraries(mfem PUBLIC ${TPL_LIBRARIES} ${TPL_TARGETS})
if (TPL_TARGETS)
@@ -869,11 +870,12 @@ add_dependencies(exec
# - https://cmake.org/Bug/view.php?id=8438
# Add a target to copy the mfem data directory to the build directory
add_custom_command(OUTPUT data_is_copied
COMMAND ${CMAKE_COMMAND} -E copy_directory ${PROJECT_SOURCE_DIR}/data data
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
COMMENT "Copying the data directory ...")
add_custom_target(copy_data DEPENDS data_is_copied)
# Implementable as a single copy_directory_if_different command w/ CMake >= 3.26
file(GLOB DATA_FILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/data/*)
add_custom_target(copy_data
COMMAND ${CMAKE_COMMAND} -E make_directory data
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_FILES} data
COMMENT "Syncing the data directory ...")
# Add 'copy_data' as a prerequisite for all executables, if the source and the
# build directories are not the same.
if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
@@ -1005,6 +1007,10 @@ install(FILES
install(EXPORT ${PROJECT_NAME_UC}Targets
DESTINATION ${INSTALL_CMAKE_DIR})
# Install the data directory if present, i.e. if the copy_data target is built
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/data
DESTINATION ${MFEM_INSTALL_DIR} OPTIONAL)
#-------------------------------------------------------------------------------
# Create 'config.mk' from 'config.mk.in' for the build and install locations and
# define install rules for 'config.mk' and 'test.mk'
+3 -1
View File
@@ -725,7 +725,9 @@ The specific libraries and their options are:
URL: https://ginkgo-project.github.io
Options: GINKGO_OPT, GINKGO_LIB, GINKGO_DIR, GINKGO_BUILD_TYPE (Release or
Debug).
Versions: Ginkgo >= 1.9.0.
Versions: Ginkgo >= 1.9.0. When building Ginkgo with distributed support, a
recent version of the "develop" branch is required (1.11 as defined
in include/ginkgo/config.hpp).
- AmgX (optional), used when MFEM_USE_AMGX = YES.
URL: https://github.com/NVIDIA/AMGX
+1 -1
View File
@@ -407,7 +407,7 @@ AMGX_LIB = -L$(AMGX_DIR)/lib -lamgx -lcusparse -lcusolver -lcublas -lnvToolsExt
# MAGMA library configuration
MAGMA_DIR = @MFEM_DIR@/../magma
MAGMA_OPT = -I$(MAGMA_DIR)/include
MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a -lcublas -lcusparse $(LAPACK_LIB)
MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a $(LAPACK_LIB)
# GnuTLS library configuration
GNUTLS_OPT =
@@ -0,0 +1,86 @@
MFEM NURBS mesh v1.0
dimension
1
# Four segments with different NURBS orders, described via patches.
elements
4
1 1 0 1
2 1 2 3
3 1 4 5
4 1 6 7
boundary
0
edges
4
0 0 1
1 2 3
2 4 5
3 6 7
vertices
8
patches
# Patch 0: linear (order 1, 3 spans)
knotvectors
1
1 4 0 0 .4 .6 1 1
dimension
2
controlpoints
0.0 0.0 1.0
0.6 0.4 1.0
0.4 0.6 1.0
1.0 1.0 1.0
# Patch 1: quadratic (order 2, 2 spans)
knotvectors
1
2 4 0 0 0 .5 1 1 1
dimension
2
controlpoints
1.0 0.0 1.0
1.9 0.0 1.21
2.0 0.9 1.22
2.0 1.0 1.0
# Patch 2: cubic (order 3, 3 spans)
knotvectors
1
3 6 0 0 0 0 .33 .66 1 1 1 1
dimension
2
controlpoints
2.0 0.0 1.0
2.1 0.2 1.31
3.5 0.4 1.32
2.5 0.6 1.33
2.9 1.0 1.34
3.0 1.0 1.0
# Patch 3: quartic (order 4, 1 span)
knotvectors
1
4 5 0 0 0 0 0 1 1 1 1 1
dimension
2
controlpoints
3.0 0.0 1.0
3.45 0.5 1.41
3.50 1.0 1.42
3.75 0.8 1.43
4.0 0.0 1.0
+79
View File
@@ -0,0 +1,79 @@
MFEM NURBS mesh v1.0
#
# MFEM Geometry Types (see fem/geom.hpp):
#
# SEGMENT = 1
# SQUARE = 3
# CUBE = 5
#
dimension
1
# Three segments with different NURBS orders, described via patches.
elements
3
1 1 0 1
2 1 2 3
3 1 4 5
boundary
6
1 0 0
1 0 1
1 0 2
1 0 3
1 0 4
1 0 5
edges
3
0 0 1
1 2 3
2 4 5
vertices
6
patches
# Patch 0: linear (order 1, 2 control points)
knotvectors
1
1 2 0 0 1 1
dimension
2
controlpoints
0.0 0.0 1.0
1.0 1.0 1.0
# Patch 1: quadratic (order 2, 3 control points)
knotvectors
1
2 3 0 0 0 1 1 1
dimension
2
controlpoints
1.0 0.0 1.0
1.02 1.02 1.2
2.0 1.0 1.0
# Patch 2: cubic (order 3, 4 control points)
knotvectors
1
3 4 0 0 0 0 1 1 1 1
dimension
2
controlpoints
2.0 0.0 1.0
2.03 0.83 1.31
2.33 1.03 1.32
3.0 1.0 1.0
+72
View File
@@ -0,0 +1,72 @@
MFEM NURBS mesh v1.0
#
# MFEM Geometry Types (see fem/geom.hpp):
#
# SEGMENT = 1
# SQUARE = 3
# CUBE = 5
#
dimension
1
elements
3
1 1 0 1
2 1 2 3
3 1 4 5
boundary
6
1 0 0
1 0 1
1 0 2
1 0 3
1 0 4
1 0 5
edges
3
0 0 1
1 2 3
2 4 5
vertices
6
# Edge 0: linear (order 1, 2 control points)
# Edge 1: quadratic (order 2, 3 control points)
# Edge 2: cubic (order 3, 4 control points)
knotvectors
3
1 2 0 0 1 1
2 3 0 0 0 1 1 1
3 4 0 0 0 0 1 1 1 1
# One weight per control point, in the same order as the control points; (2 + 3 + 4) = 9 weights total
weights
1
1
1
1
1
1
1.2
1.31
1.32
FiniteElementSpace
FiniteElementCollection: NURBS
VDim: 2
Ordering: 1
0.0 0.0
1.0 1.0
1.0 0.0
2.0 1.0
2.0 0.0
3.0 1.0
1.02 1.02
2.03 0.83
2.33 1.03
+79
View File
@@ -0,0 +1,79 @@
MFEM NURBS mesh v1.0
#
# MFEM Geometry Types (see fem/geom.hpp):
#
# SEGMENT = 1
# SQUARE = 3
# CUBE = 5
#
dimension
1
# Three segments with different NURBS orders, described via patches.
elements
3
1 1 0 1
2 1 2 3
3 1 4 5
boundary
6
1 0 0
1 0 1
1 0 2
1 0 3
1 0 4
1 0 5
edges
3
0 0 1
1 2 3
2 4 5
vertices
6
patches
# Patch 0: linear (order 1, 2 control points)
knotvectors
1
1 2 0 0 1 1
dimension
3
controlpoints
0.0 0.0 0.01 1.0
1.0 1.0 1.01 1.0
# Patch 1: quadratic (order 2, 3 control points)
knotvectors
1
2 3 0 0 0 1 1 1
dimension
3
controlpoints
1.0 0.0 0.02 1.0
1.02 1.02 0.52 1.2
2.0 1.0 1.02 1.0
# Patch 2: cubic (order 3, 4 control points)
knotvectors
1
3 4 0 0 0 0 1 1 1 1
dimension
3
controlpoints
2.0 0.0 0.03 1.0
2.03 0.83 0.33 1.31
2.33 1.03 0.63 1.32
3.0 1.0 1.03 1.0
+72
View File
@@ -0,0 +1,72 @@
MFEM NURBS mesh v1.0
#
# MFEM Geometry Types (see fem/geom.hpp):
#
# SEGMENT = 1
# SQUARE = 3
# CUBE = 5
#
dimension
1
elements
3
1 1 0 1
2 1 2 3
3 1 4 5
boundary
6
1 0 0
1 0 1
1 0 2
1 0 3
1 0 4
1 0 5
edges
3
0 0 1
1 2 3
2 4 5
vertices
6
# Edge 0: linear (order 1, 2 control points)
# Edge 1: quadratic (order 2, 3 control points)
# Edge 2: cubic (order 3, 4 control points)
knotvectors
3
1 2 0 0 1 1
2 3 0 0 0 1 1 1
3 4 0 0 0 0 1 1 1 1
# One weight per control point, in the same order as the control points; (2 + 3 + 4) = 9 weights total
weights
1
1
1
1
1
1
1.2
1.31
1.32
FiniteElementSpace
FiniteElementCollection: NURBS
VDim: 3
Ordering: 1
0.0 0.0 0.01
1.0 1.0 1.01
1.0 0.0 0.02
2.0 1.0 1.02
2.0 0.0 0.03
3.0 1.0 1.03
1.02 1.02 0.52
2.03 0.83 0.33
2.33 1.03 0.63
+9 -1
View File
@@ -117,6 +117,10 @@ namespace mfem {
* - <a class="el" href="ex39p_8cpp_source.html">Example 39p</a>: parallel named mesh attributes
* - <a class="el" href="ex40_8cpp_source.html">Example 40</a>: eikonal equation
* - <a class="el" href="ex40p_8cpp_source.html">Example 40p</a>: parallel eikonal equation
* - <a class="el" href="ex41_8cpp_source.html">Example 41</a>: DG/CG IMEX time-dependent advection-diffusion
* - <a class="el" href="ex41p_8cpp_source.html">Example 41p</a>: parallel DG/CG IMEX time-dependent advection-diffusion
* - <a class="el" href="ex43_8cpp_source.html">Example 43</a>: sliding boundary conditions in linear elasticity
* - <a class="el" href="ex43p_8cpp_source.html">Example 43p</a>: parallel sliding boundary conditions in linear elasticity
*
* <H4>AmgX Examples</H4>
* - Variants of Examples
@@ -188,6 +192,8 @@ namespace mfem {
* <a class="el" href="nurbs__ex1p_8cpp_source.html">1p</a>,
* <a class="el" href="nurbs__ex3_8cpp_source.html">3</a>,
* <a class="el" href="nurbs__ex5_8cpp_source.html">5</a>,
* <a class="el" href="nurbs__ex10_8cpp_source.html">10</a>,
* <a class="el" href="nurbs__ex10p_8cpp_source.html">10p</a>,
* <a class="el" href="nurbs__ex11p_8cpp_source.html">11p</a>, and
* <a class="el" href="nurbs__ex24_8cpp_source.html">24</a>,
* demonstrating howto perform NURBS-based Isogeometric Analysis.
@@ -196,6 +202,7 @@ namespace mfem {
* - <a class="el" href="nurbs__curveint_8cpp_source.html">NURBS Interpolation</a>: NURBS interpolation of given geometry
* - <a class="el" href="nurbs__naca__cmesh_8cpp_source.html">NURBS NACA Mesher</a>: generate NURBS based mesh around a NACA foil
* - <a class="el" href="nurbs__printfunc_8cpp_source.html">NURBS Printer</a>: print the NURBS-basis
* - <a class="el" href="nurbs__mesh_info_8cpp_source.html">NURBS Mesh info</a>: print the info of a NURBS mesh
*
* <H3>Miniapps</H3>
* - <a class="el" href="volta_8cpp_source.html">Volta</a>: simple electrostatics simulation code
@@ -234,7 +241,8 @@ namespace mfem {
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Poisson problem
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Poisson problem
* - <a class="el" href="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
* - <a class="el" href="contact-patch-test_8cpp_source.html">Contact</a>: mortar contact patch test for elasticity
* - <a class="el" href="contact-patch-test_8cpp_source.html">Tribol</a>: mortar contact patch test for elasticity
* - <a class="el" href="contact_8cpp_source.html">Contact</a>: Frictionless contact examples using <a class="el" href="classmfem_1_1IPSolver.html#details">IP optimization</a> and the <a class="el" href="classmfem_1_1AMGFSolver.html#details">AMGF solver</a>
* - <a class="el" href="multidomain_8cpp_source.html">Multidomain miniapp</a>: Multidomain and Submesh demonstration miniapp
* - <a class="el" href="pdiffusion_8cpp_source.html">DPG Diffusion example</a>: DPG formulation for the diffusion problem
* - <a class="el" href="pmaxwell_8cpp_source.html">DPG Maxwell example</a>: DPG formulation for the indefinite Maxwell problem
+2
View File
@@ -47,6 +47,7 @@ list(APPEND ALL_EXE_SRCS
ex39.cpp
ex40.cpp
ex41.cpp
ex43.cpp
)
if (MFEM_USE_MPI)
@@ -91,6 +92,7 @@ if (MFEM_USE_MPI)
ex39p.cpp
ex40p.cpp
ex41p.cpp
ex43p.cpp
)
endif()
+29 -6
View File
@@ -105,6 +105,7 @@ int main(int argc, char *argv[])
bool visualization = true;
bool visit = false;
int vis_steps = 5;
bool solve_implicit_state = false;
int precision = 8;
cout.precision(precision);
@@ -126,6 +127,9 @@ int main(int argc, char *argv[])
"Alpha coefficient.");
args.AddOption(&kappa, "-k", "--kappa",
"Kappa coefficient offset.");
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
"-imp-slope", "--implicit-slope",
"Implicitly solve for stage state or slope.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -179,6 +183,11 @@ int main(int argc, char *argv[])
// 7. Initialize the conduction operator and the visualization.
ConductionOperator oper(fespace, alpha, kappa, u);
using ImplicitVariableType = ConductionOperator::ImplicitVariableType;
ImplicitVariableType imp_var = solve_implicit_state ?
ImplicitVariableType::STATE
: ImplicitVariableType::SLOPE;
oper.SetImplicitVariableType(imp_var);
u_gf.SetFromTrueDofs(u);
{
@@ -316,11 +325,14 @@ void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
}
void ConductionOperator::ImplicitSolve(const real_t dt,
const Vector &u, Vector &du_dt)
const Vector &u, Vector &k)
{
// Solve the equation:
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
// for du_dt, where K is linearized by using u from the previous timestep
// M*k = -K(u + dt*k) for k = du/dt, if solving for stage-slope
// or
// M*k = -dt*K(k) + M*u for k = u_s, if solving for stage-state
// where K is linearized by using u from the previous timestep, and
// the stage-state and slope relation: du/dt = (u_s - u)/dt.
if (!T)
{
T = Add(1.0, Mmat, dt, Kmat);
@@ -328,9 +340,20 @@ void ConductionOperator::ImplicitSolve(const real_t dt,
T_solver.SetOperator(*T);
}
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
Kmat.Mult(u, z);
z.Neg();
T_solver.Mult(z, du_dt);
// Construct current right-hand side for stage state vs. slope solve
if (ImplicitVarTypeIsState())
{
// k, on return, is the stage value u_s
Mmat.Mult(u, z);
}
else
{
// k, on return, is the stage slope du/dt
Kmat.Mult(u, z);
z.Neg();
}
T_solver.Mult(z, k);
}
void ConductionOperator::SetParameters(const Vector &u)
+29 -6
View File
@@ -115,6 +115,7 @@ int main(int argc, char *argv[])
bool visit = false;
int vis_steps = 5;
bool adios2 = false;
bool solve_implicit_state = false;
int precision = 8;
cout.precision(precision);
@@ -138,6 +139,9 @@ int main(int argc, char *argv[])
"Alpha coefficient.");
args.AddOption(&kappa, "-k", "--kappa",
"Kappa coefficient offset.");
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
"-imp-slope", "--implicit-slope",
"Implicitly solve for stage state or slope.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -212,6 +216,11 @@ int main(int argc, char *argv[])
// 9. Initialize the conduction operator and the VisIt visualization.
ConductionOperator oper(fespace, alpha, kappa, u);
using ImplicitVariableType = ConductionOperator::ImplicitVariableType;
ImplicitVariableType imp_var = solve_implicit_state ?
ImplicitVariableType::STATE
: ImplicitVariableType::SLOPE;
oper.SetImplicitVariableType(imp_var);
u_gf.SetFromTrueDofs(u);
{
@@ -407,11 +416,14 @@ void ConductionOperator::Mult(const Vector &u, Vector &du_dt) const
}
void ConductionOperator::ImplicitSolve(const real_t dt,
const Vector &u, Vector &du_dt)
const Vector &u, Vector &k)
{
// Solve the equation:
// du_dt = M^{-1}*[-K(u + dt*du_dt)]
// for du_dt, where K is linearized by using u from the previous timestep
// M*k = -K(u + dt*k) for k = du/dt, if solving for stage-slope
// or
// M*k = -dt*K(k) + M*u for k = u_s, if solving for stage-state
// where K is linearized by using u from the previous timestep, and
// the stage-state and slope relation: du/dt = (u_s - u)/dt.
if (!T)
{
T = Add(1.0, Mmat, dt, Kmat);
@@ -419,9 +431,20 @@ void ConductionOperator::ImplicitSolve(const real_t dt,
T_solver.SetOperator(*T);
}
MFEM_VERIFY(dt == current_dt, ""); // SDIRK methods use the same dt
Kmat.Mult(u, z);
z.Neg();
T_solver.Mult(z, du_dt);
// Construct current right-hand side for stage state vs. slope solve
if (ImplicitVarTypeIsState())
{
// k, on return, is the stage value u
Mmat.Mult(u, z);
}
else
{
// k, on return, is the stage slope du/dt
Kmat.Mult(u, z);
z.Neg();
}
T_solver.Mult(z, k);
}
void ConductionOperator::SetParameters(const Vector &u)
+1 -1
View File
@@ -119,7 +119,7 @@ int main(int argc, char *argv[])
}
LinearForm b(&fespace);
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
// 6. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the linear elasticity integrator with piece-wise
+1 -1
View File
@@ -140,7 +140,7 @@ int main(int argc, char *argv[])
}
ParLinearForm b(&fespace);
b.AddDomainIntegrator(new VectorBoundaryLFIntegrator(f));
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
// 6. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the linear elasticity integrator with piece-wise
+278
View File
@@ -0,0 +1,278 @@
// MFEM Example 43
//
// Compile with: make ex43
//
// Sample runs: ex43 -m ../data/ball-nurbs.mesh -r 2
// ex43 -m ../data/ref-cube.mesh -r 2
// ex43 -m ../data/fichera.mesh
// ex43 -m ../data/star.mesh
//
// Description: This example code solves a linear elasticity problem using
// Nitsche's method to enforce sliding boundary conditions. In
// particular, we consider a linear elastic body that is displaced
// in the normal direction on the entire boundary, but is free to
// slide in the tangential direction. This is achieved by imposing
// homogeneous Dirichlet boundary conditions on the normal
// component of the displacement, while applying homogeneous
// Neumann boundary conditions on the tangential components of the
// displacement. By enforcing a uniform, constant normal
// displacement on the boundary, we can simulate the effect of
// compressing or expanding the elastic body uniformly. These
// boundary conditions are applied weakly using Nitsche's method,
// allowing for more flexibility in handling complex geometries in
// either 2D or 3D.
//
// The strong form is given by:
//
// Div(σ(u)) = 0 in Ω
// u ⋅ n = g on Γ
// σ(u) ⊥ n on Γ
//
// where σ(u) = λ tr(ε(u)) I + 2μ ε(u) is the stress tensor, ε(u)
// is the strain tensor, λ and μ are the Lamé parameters, and g is
// the prescribed displacement on the boundary. Here, n is the
// outward normal on the boundary Γ = ∂Ω.
//
// The weak form using Nitsche's method is:
//
// Find u ∈ V such that a(u,v) = b(v) for all v ∈ V
//
// where
//
// a(u,v) := ∫_Ω σ(u) : ε(v) dx
// - ∫_Γ (σ(u) n ⋅ n) (v ⋅ n) dS
// - ∫_Γ (σ(v) n ⋅ n) (u ⋅ n) dS
// + κ ∫_Γ h⁻¹ (λ + 2μ) (u ⋅ n) (v ⋅ n) dS,
//
// b(v) := - ∫_Γ σ(v) n ⋅ n g dS
// + κ ∫_Γ h⁻¹ (λ + 2μ) (v ⋅ n) g dS,
//
// with κ > 0 being a penalty parameter. Here, h is a
// characteristic element size on the boundary. The function
// space V is a vector H1-conforming finite element space.
//
// This example can be viewed as an alternative to Example 28.
// Whereas Example 28 imposes sliding boundary conditions using
// the general-purpose constrained system solvers found in
// mfem/linalg/constraints.hpp, this example employs Nitsche's
// method to weakly enforce the same condition by modifying the
// underlying variational formulation. Unlike Example 28, the
// approach here is specialized to isotropic linear elasticity,
// but it has the advantage of producing a well-conditioned SPD
// stiffness matrix that can be readily preconditioned with
// standard AMG. We recommend reviewing Example 2 before working
// through this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
real_t displ_mag = 0.1;
int order = 1;
int ref_levels = 0;
real_t lambda = 1.0;
real_t mu = 1.0;
real_t kappa = -1.0;
bool static_cond = false;
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&displ_mag, "-g", "--displ",
"Magnitude of the normal displacement.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&ref_levels, "-r", "--ref_levels",
"Number of uniform mesh refinements.");
args.AddOption(&lambda, "-l", "--lambda", "First Lamé parameter.");
args.AddOption(&mu, "-mu", "--mu", "Second Lamé parameter.");
args.AddOption(&kappa, "-k", "--kappa",
"The penalty parameter, should be positive."
" Negative values are replaced with (order+1)^2.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
if (kappa < 0)
{
kappa = (order+1)*(order+1);
}
args.PrintOptions(cout);
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral or hexahedral elements with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. Select the order of the finite element discretization space. For NURBS
// meshes, we increase the order by degree elevation.
if (mesh->NURBSext)
{
mesh->DegreeElevate(order, order);
}
// 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement.
for (int i = 0; i < ref_levels; i++)
{
mesh->UniformRefinement();
}
// 5. Interpolate the geometry after refinement to control geometry error.
int curvature_order = max(order, 2);
mesh->SetCurvature(curvature_order);
// 6. Define a finite element space on the mesh. Here we use vector finite
// elements, i.e. dim copies of a scalar finite element space. The vector
// dimension is specified by the last argument of the FiniteElementSpace
// constructor. For NURBS meshes, we use the (degree elevated) NURBS space
// associated with the mesh nodes.
FiniteElementCollection *fec;
FiniteElementSpace *fespace;
if (mesh->NURBSext)
{
fec = NULL;
fespace = mesh->GetNodes()->FESpace();
}
else
{
fec = new H1_FECollection(order, dim);
fespace = new FiniteElementSpace(mesh, fec, dim);
}
cout << "Number of finite element unknowns: " << fespace->GetTrueVSize()
<< endl << "Assembling: " << flush;
// 7. Mark the boundary attributes where the sliding (Nitsche) boundary
// conditions are to be applied. These b.c. are imposed weakly, by adding
// the appropriate boundary integrators over the marked 'ess_bdr' to the
// bilinear and linear forms. Thus, no dofs are eliminated; there are no
// essential boundary conditions.
Array<int> ess_tdof_list, ess_bdr(mesh->bdr_attributes.Max());
ess_bdr = 1;
// 8. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
GridFunction x(fespace);
x = 0.0;
// 9. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the linear elasticity integrator with constant
// coefficients lambda and mu.
ConstantCoefficient lambda_c(lambda);
ConstantCoefficient mu_c(mu);
BilinearForm *a = new BilinearForm(fespace);
a->AddDomainIntegrator(new ElasticityIntegrator(lambda_c,mu_c));
a->AddBdrFaceIntegrator(
new SlidingElasticityIntegrator(lambda_c, mu_c, kappa),
ess_bdr);
// 10. Set up the linear form b(.) corresponding to the Nitsche method
// to impose the Dirichlet boundary conditions. Here, we set the
// prescribed displacement on the Dirichlet boundary to be a constant
// normal displacement of magnitude 'displ_mag'.
ConstantCoefficient g(displ_mag);
LinearForm *b = new LinearForm(fespace);
b->AddBdrFaceIntegrator(
new SlidingElasticityLFIntegrator(
g, lambda_c, mu_c, kappa), ess_bdr);
b->Assemble();
// 11. Assemble the bilinear form and the corresponding linear system,
// applying any necessary transformations such as: eliminating boundary
// conditions, applying conforming constraints for non-conforming AMR,
// static condensation, etc.
cout << "matrix ... " << flush;
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
SparseMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
cout << "done." << endl;
cout << "Size of linear system: " << A.Height() << endl;
#ifndef MFEM_USE_SUITESPARSE
// 12. Define a simple symmetric Gauss-Seidel preconditioner and use it to
// solve the system Ax=b with PCG.
GSSmoother M(A);
PCG(A, M, B, X, 1, 500, 1e-12, 0.0);
#else
// 12. If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
UMFPackSolver umf_solver;
umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
umf_solver.SetOperator(A);
umf_solver.Mult(B, X);
#endif
// 13. Recover the solution as a finite element grid function.
a->RecoverFEMSolution(X, *b, x);
// 14. For non-NURBS meshes, make the mesh curved based on the finite element
// space. This means that we define the mesh elements through a fespace
// based transformation of the reference element. This allows us to save
// the displaced mesh as a curved mesh when using high-order finite
// element displacement field. We assume that the initial mesh (read from
// the file) is not higher order curved mesh compared to the chosen FE
// space.
if (!mesh->NURBSext)
{
mesh->SetNodalFESpace(fespace);
}
// 15. Save the displaced mesh and the inverted solution (which gives the
// backward displacements to the original grid). This output can be
// viewed later using GLVis: "glvis -m displaced.mesh -g sol.gf".
{
GridFunction *nodes = mesh->GetNodes();
*nodes += x;
x *= -1;
ofstream mesh_ofs("displaced.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
ofstream sol_ofs("sol.gf");
sol_ofs.precision(8);
x.Save(sol_ofs);
}
// 16. Send the above data by socket to a GLVis server. Use the "n" and "b"
// keys in GLVis to visualize the displacements.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock.precision(8);
sol_sock << "solution\n" << *mesh << x << flush;
}
// 17. Free the used memory.
delete a;
delete b;
if (fec)
{
delete fespace;
delete fec;
}
delete mesh;
return 0;
}
+332
View File
@@ -0,0 +1,332 @@
// MFEM Example 43 - Parallel Version
//
// Compile with: make ex43p
//
// Sample runs: mpirun -np 4 ex43p -m ../data/ball-nurbs.mesh -r 2
// mpirun -np 4 ex43p -m ../data/ref-cube.mesh -r 2
// mpirun -np 4 ex43p -m ../data/fichera.mesh
// mpirun -np 4 ex43p -m ../data/star.mesh
//
// Description: This example code solves a linear elasticity problem using
// Nitsche's method to enforce sliding boundary conditions. In
// particular, we consider a linear elastic body that is displaced
// in the normal direction on the entire boundary, but is free to
// slide in the tangential direction. This is achieved by imposing
// homogeneous Dirichlet boundary conditions on the normal
// component of the displacement, while applying homogeneous
// Neumann boundary conditions on the tangential components of the
// displacement. By enforcing a uniform, constant normal
// displacement on the boundary, we can simulate the effect of
// compressing or expanding the elastic body uniformly. These
// boundary conditions are applied weakly using Nitsche's method,
// allowing for more flexibility in handling complex geometries in
// either 2D or 3D.
//
// The strong form is given by:
//
// Div(σ(u)) = 0 in Ω
// u ⋅ n = g on Γ
// σ(u) ⊥ n on Γ
//
// where σ(u) = λ tr(ε(u)) I + 2μ ε(u) is the stress tensor, ε(u)
// is the strain tensor, λ and μ are the Lamé parameters, and g is
// the prescribed displacement on the boundary. Here, n is the
// outward normal on the boundary Γ = ∂Ω.
//
// The weak form using Nitsche's method is:
//
// Find u ∈ V such that a(u,v) = b(v) for all v ∈ V
//
// where
//
// a(u,v) := ∫_Ω σ(u) : ε(v) dx
// - ∫_Γ (σ(u) n ⋅ n) (v ⋅ n) dS
// - ∫_Γ (σ(v) n ⋅ n) (u ⋅ n) dS
// + κ ∫_Γ h⁻¹ (λ + 2μ) (u ⋅ n) (v ⋅ n) dS,
//
// b(v) := - ∫_Γ σ(v) n ⋅ n g dS
// + κ ∫_Γ h⁻¹ (λ + 2μ) (v ⋅ n) g dS,
//
// with κ > 0 being a penalty parameter. Here, h is a
// characteristic element size on the boundary. The function
// space V is a vector H1-conforming finite element space.
//
// This example can be viewed as an alternative to Example 28.
// Whereas Example 28 imposes sliding boundary conditions using
// the general-purpose constrained system solvers found in
// mfem/linalg/constraints.hpp, this example employs Nitsche's
// method to weakly enforce the same condition by modifying the
// underlying variational formulation. Unlike Example 28, the
// approach here is specialized to isotropic linear elasticity,
// but it has the advantage of producing a well-conditioned SPD
// stiffness matrix that can be readily preconditioned with
// standard AMG. We recommend reviewing Example 2 before working
// through this example.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Initialize MPI and HYPRE.
Mpi::Init(argc, argv);
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
// 2. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
real_t displ_mag = 0.1;
int order = 1;
int ref_levels = 0;
real_t lambda = 1.0;
real_t mu = 1.0;
real_t kappa = -1.0;
bool static_cond = false;
bool reorder_space = false;
bool visualization = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&displ_mag, "-g", "--displ",
"Magnitude of the normal displacement.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&ref_levels, "-r", "--ref_levels",
"Number of uniform mesh refinements.");
args.AddOption(&lambda, "-l", "--lambda", "First Lamé parameter.");
args.AddOption(&mu, "-mu", "--mu", "Second Lamé parameter.");
args.AddOption(&kappa, "-k", "--kappa",
"The penalty parameter, should be positive."
" Negative values are replaced with (order+1)^2.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&reorder_space, "-nodes", "--by-nodes", "-vdim", "--by-vdim",
"Use byNODES ordering of vector space instead of byVDIM");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (kappa < 0)
{
kappa = (order+1)*(order+1);
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 3. Read the (serial) mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral or hexahedral elements with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Select the order of the finite element discretization space. For NURBS
// meshes, we increase the order by degree elevation.
if (mesh->NURBSext)
{
mesh->DegreeElevate(order, order);
}
// 5. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement.
for (int i = 0; i < ref_levels; i++)
{
mesh->UniformRefinement();
}
// 6. Interpolate the geometry after refinement to control geometry error.
int curvature_order = max(order, 2);
mesh->SetCurvature(curvature_order);
// 7. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
// 8. Define a finite element space on the mesh. Here we use vector finite
// elements, i.e. dim copies of a scalar finite element space. The vector
// dimension is specified by the last argument of the FiniteElementSpace
// constructor. For NURBS meshes, we use the (degree elevated) NURBS space
// associated with the mesh nodes.
FiniteElementCollection *fec;
ParFiniteElementSpace *fespace;
const bool use_nodal_fespace = pmesh->NURBSext;
if (use_nodal_fespace)
{
fec = NULL;
fespace = (ParFiniteElementSpace *)pmesh->GetNodes()->FESpace();
}
else
{
fec = new H1_FECollection(order, dim);
if (reorder_space)
{
fespace = new ParFiniteElementSpace(pmesh, fec, dim, Ordering::byNODES);
}
else
{
fespace = new ParFiniteElementSpace(pmesh, fec, dim, Ordering::byVDIM);
}
}
HYPRE_BigInt size = fespace->GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl
<< "Assembling: " << flush;
}
// 9. Mark the boundary attributes where the sliding (Nitsche) boundary
// conditions are to be applied. These b.c. are imposed weakly, by adding
// the appropriate boundary integrators over the marked 'ess_bdr' to the
// bilinear and linear forms. Thus, no dofs are eliminated; there are no
// essential boundary conditions.
Array<int> ess_tdof_list, ess_bdr;
if (pmesh->bdr_attributes.Size())
{
ess_bdr.SetSize(pmesh->bdr_attributes.Max());
ess_bdr = 1;
}
// 10. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
ParGridFunction x(fespace);
x = 0.0;
// 11. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the linear elasticity integrator with constant
// coefficients lambda and mu.
ConstantCoefficient lambda_c(lambda);
ConstantCoefficient mu_c(mu);
ParBilinearForm *a = new ParBilinearForm(fespace);
a->AddDomainIntegrator(new ElasticityIntegrator(lambda_c,mu_c));
a->AddBdrFaceIntegrator(
new SlidingElasticityIntegrator(lambda_c, mu_c, kappa),
ess_bdr);
// 12. Set up the linear form b(.) corresponding to the Nitsche method
// to impose the Dirichlet boundary conditions. Here, we set the
// prescribed displacement on the Dirichlet boundary to be a constant
// normal displacement of magnitude 'displ_mag'.
ConstantCoefficient g(displ_mag);
ParLinearForm *b = new ParLinearForm(fespace);
b->AddBdrFaceIntegrator(
new SlidingElasticityLFIntegrator(
g, lambda_c, mu_c, kappa), ess_bdr);
b->Assemble();
// 13. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
if (myid == 0) { cout << "matrix ... " << flush; }
if (static_cond) { a->EnableStaticCondensation(); }
a->Assemble();
HypreParMatrix A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
if (myid == 0)
{
cout << "done." << endl;
cout << "Size of linear system: " << A.GetGlobalNumRows() << endl;
}
// 14. Define and apply a parallel PCG solver for A X = B with the BoomerAMG
// preconditioner from hypre.
HypreBoomerAMG *amg = new HypreBoomerAMG(A);
if (!a->StaticCondensationIsEnabled())
{
amg->SetElasticityOptions(fespace);
}
else
{
amg->SetSystemsOptions(dim, reorder_space);
}
HyprePCG *pcg = new HyprePCG(A);
pcg->SetTol(1e-8);
pcg->SetMaxIter(500);
pcg->SetPrintLevel(2);
pcg->SetPreconditioner(*amg);
pcg->Mult(B, X);
// 15. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
// 16. For non-NURBS meshes, make the mesh curved based on the finite element
// space. This means that we define the mesh elements through a fespace
// based transformation of the reference element. This allows us to save
// the displaced mesh as a curved mesh when using high-order finite
// element displacement field. We assume that the initial mesh (read from
// the file) is not higher order curved mesh compared to the chosen FE
// space.
if (!use_nodal_fespace)
{
pmesh->SetNodalFESpace(fespace);
}
// 17. Save in parallel the displaced mesh and the inverted solution (which
// gives the backward displacements to the original grid). This output
// can be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
GridFunction *nodes = pmesh->GetNodes();
*nodes += x;
x *= -1;
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
}
// 18. Send the above data by socket to a GLVis server. Use the "n" and "b"
// keys in GLVis to visualize the displacements.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << *pmesh << x << flush;
}
// 19. Free the used memory.
delete pcg;
delete amg;
delete a;
delete b;
if (fec)
{
delete fespace;
delete fec;
}
delete pmesh;
return 0;
}
+20 -1
View File
@@ -160,6 +160,7 @@ int main(int argc, char *argv[])
bool paraview = false;
bool binary = false;
int vis_steps = 5;
bool solve_implicit_state = false;
int precision = 8;
cout.precision(precision);
@@ -187,6 +188,9 @@ int main(int argc, char *argv[])
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
"-imp-slope", "--implicit-slope",
"Implicitly solve for stage state or slope.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -366,6 +370,11 @@ int main(int argc, char *argv[])
// right-hand side, and perform time-integration (looping over the time
// iterations, ti, with a time-step dt).
FE_Evolution adv(m, k, b);
using ImplicitVariableType = FE_Evolution::ImplicitVariableType;
ImplicitVariableType imp_var = solve_implicit_state ?
ImplicitVariableType::STATE
: ImplicitVariableType::SLOPE;
adv.SetImplicitVariableType(imp_var);
real_t t = 0.0;
adv.SetTime(t);
@@ -459,7 +468,17 @@ void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
{
MFEM_VERIFY(dg_solver != NULL,
"Implicit time integration is not supported with partial assembly");
K.Mult(x, z);
// Construct current right-hand side for stage state vs. slope solve
if (ImplicitVarTypeIsState())
{
// k, on return, is the stage value u
M.Mult(x, z);
}
else
{
// k, on return, is the stage slope du/dt
K.Mult(x, z);
}
z += b;
dg_solver->SetTimeStep(dt);
dg_solver->Mult(z, k);
+20 -1
View File
@@ -257,6 +257,7 @@ int main(int argc, char *argv[])
bool adios2 = false;
bool binary = false;
int vis_steps = 5;
bool solve_implicit_state = false;
#if MFEM_HYPRE_VERSION >= 21800
PrecType prec_type = PrecType::AIR;
#else
@@ -290,6 +291,9 @@ int main(int argc, char *argv[])
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&solve_implicit_state, "-imp-state", "--implicit-state",
"-imp-slope", "--implicit-slope",
"Implicitly solve for stage state or slope.");
args.AddOption((int *)&prec_type, "-pt", "--prec-type", "Preconditioner for "
"implicit solves. 0 for ILU, 1 for pAIR-AMG.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -536,6 +540,11 @@ int main(int argc, char *argv[])
// right-hand side, and perform time-integration (looping over the time
// iterations, ti, with a time-step dt).
FE_Evolution adv(*m, *k, *B, prec_type);
using ImplicitVariableType = FE_Evolution::ImplicitVariableType;
ImplicitVariableType imp_var = solve_implicit_state ?
ImplicitVariableType::STATE
: ImplicitVariableType::SLOPE;
adv.SetImplicitVariableType(imp_var);
real_t t = 0.0;
adv.SetTime(t);
@@ -676,7 +685,17 @@ FE_Evolution::FE_Evolution(ParBilinearForm &M_, ParBilinearForm &K_,
// (M - dt*K) d = K*u + b
void FE_Evolution::ImplicitSolve(const real_t dt, const Vector &x, Vector &k)
{
K->Mult(x, z);
// Construct current right-hand side for stage state vs. slope solve
if (ImplicitVarTypeIsState())
{
// k, on return, is the stage value u
M->Mult(x, z);
}
else
{
// k, on return, is the stage slope du/dt
K->Mult(x, z);
}
z += b;
dg_solver->SetTimeStep(dt);
dg_solver->Mult(z, k);
+6
View File
@@ -14,6 +14,12 @@ list(APPEND GINKGO_EXAMPLES_SRCS
ex1.cpp
)
if (MFEM_USE_MPI AND GINKGO_BUILD_MPI)
list(APPEND GINKGO_EXAMPLES_SRCS
ex1p.cpp
)
endif()
# Include the source directory where mfem.hpp and mfem-performance.hpp are.
include_directories(BEFORE ${PROJECT_BINARY_DIR})
+3 -3
View File
@@ -207,7 +207,7 @@ int main(int argc, char *argv[])
Ginkgo::IcPreconditioner ginkgo_precond(exec, "paric", 30);
Ginkgo::CGSolver ginkgo_solver(exec, ginkgo_precond);
ginkgo_solver.SetPrintLevel(print_lvl);
ginkgo_solver.SetRelTol(1e-12);
ginkgo_solver.SetRelTol(sqrt(1e-12));
ginkgo_solver.SetAbsTol(0.0);
ginkgo_solver.SetMaxIter(400);
ginkgo_solver.SetOperator(*(A.Ptr()));
@@ -225,7 +225,7 @@ int main(int argc, char *argv[])
Ginkgo::MFEMPreconditioner gko_M(exec, M);
Ginkgo::CGSolver ginkgo_solver(exec, gko_M);
ginkgo_solver.SetPrintLevel(print_lvl);
ginkgo_solver.SetRelTol(1e-12);
ginkgo_solver.SetRelTol(sqrt(1e-12));
ginkgo_solver.SetAbsTol(0.0);
ginkgo_solver.SetMaxIter(400);
ginkgo_solver.SetOperator(*(A.Ptr()));
@@ -283,7 +283,7 @@ int main(int argc, char *argv[])
Ginkgo::MFEMPreconditioner gko_M(exec, M);
Ginkgo::CGSolver ginkgo_solver(exec, gko_M);
ginkgo_solver.SetPrintLevel(print_lvl);
ginkgo_solver.SetRelTol(1e-12);
ginkgo_solver.SetRelTol(sqrt(1e-12));
ginkgo_solver.SetAbsTol(0.0);
ginkgo_solver.SetMaxIter(400);
ginkgo_solver.SetOperator(*(A.Ptr()));
+436
View File
@@ -0,0 +1,436 @@
// MFEM Example 1 - Parallel Version
// GINKGO Modification
//
// Compile with: make ex1p
//
// Sample runs: mpirun -np 4 ex1p -m ../data/square-disc.mesh
// mpirun -np 4 ex1p -m ../data/star.mesh
// mpirun -np 4 ex1p -m ../data/star-mixed.mesh
// mpirun -np 4 ex1p -m ../data/escher.mesh
// mpirun -np 4 ex1p -m ../data/fichera.mesh
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
// mpirun -np 4 ex1p -m ../data/octahedron.mesh -o 1
// mpirun -np 4 ex1p -m ../data/periodic-annulus-sector.msh
// mpirun -np 4 ex1p -m ../data/periodic-torus-sector.msh
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/star-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../data/disc-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/pipe-nurbs.mesh -o -1
// mpirun -np 4 ex1p -m ../data/ball-nurbs.mesh -o 2
// mpirun -np 4 ex1p -m ../data/fichera-mixed-p2.mesh -o 2
// mpirun -np 4 ex1p -m ../data/star-surf.mesh
// mpirun -np 4 ex1p -m ../data/square-disc-surf.mesh
// mpirun -np 4 ex1p -m ../data/inline-segment.mesh
// mpirun -np 4 ex1p -m ../data/amr-quad.mesh
// mpirun -np 4 ex1p -m ../data/amr-hex.mesh
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh
// mpirun -np 4 ex1p -m ../data/mobius-strip.mesh -o -1 -sc
//
// Device sample runs:
// mpirun -np 4 ex1p -pa -d cuda
// mpirun -np 4 ex1p -fa -d cuda
// mpirun -np 4 ex1p -pa -d occa-cuda
// mpirun -np 4 ex1p -pa -d raja-omp
// mpirun -np 4 ex1p -pa -d ceed-cpu
// mpirun -np 4 ex1p -pa -d ceed-cpu -o 4 -a
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/square-mixed.mesh
// mpirun -np 4 ex1p -pa -d ceed-cpu -m ../data/fichera-mixed.mesh
// * mpirun -np 4 ex1p -pa -d ceed-cuda
// * mpirun -np 4 ex1p -pa -d ceed-hip
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/square-mixed.mesh
// mpirun -np 4 ex1p -pa -d ceed-cuda:/gpu/cuda/shared -m ../data/fichera-mixed.mesh
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
// space (i.e. quadratic for quadratic curvilinear mesh, NURBS for
// NURBS mesh, etc.)
//
// The example highlights the use of mesh refinement, finite
// element grid functions, as well as linear and bilinear forms
// corresponding to the left-hand side and right-hand side of the
// discrete linear system. We also cover the explicit elimination
// of essential boundary conditions, static condensation, and the
// optional connection to the GLVis tool for visualization.
#include "mfem.hpp"
#include <fstream>
#include <iostream>
#ifndef MFEM_USE_GINKGO
#error This example requires that MFEM is built with MFEM_USE_GINKGO=YES
#endif
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// 1. Initialize MPI and HYPRE.
Mpi::Init();
int num_procs = Mpi::WorldSize();
int myid = Mpi::WorldRank();
Hypre::Init();
// 2. Parse command-line options.
const char *mesh_file = "../../data/star.mesh";
int order = 1;
bool static_cond = false;
bool pa = false;
bool fa = false;
const char *device_config = "cpu";
bool visualization = true;
int solver_config = 0;
int print_lvl = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&fa, "-fa", "--full-assembly", "-no-fa",
"--no-full-assembly", "Enable Full Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&solver_config, "-s", "--solver-config",
"Solver and preconditioner combination: \n\t"
" 0 - Ginkgo solver and Ginkgo preconditioner, \n\t"
" 1 - Ginkgo solver and MFEM preconditioner, \n\t"
" 2 - MFEM solver and Ginkgo preconditioner, \n\t"
" 3 - MFEM solver and MFEM preconditioner.");
args.AddOption(&print_lvl, "-pl", "--print-level",
"Print level for iterative solver (1 prints every iteration).");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
device.SetGPUAwareMPI(true);
if (myid == 0) { device.Print(); }
// 4. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume meshes with the same code.
Mesh mesh(mesh_file, 1, 1);
int dim = mesh.Dimension();
// 5. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 10,000 elements.
{
int ref_levels =
(int)floor(log(10000./mesh.GetNE())/log(2.)/dim);
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
}
}
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh pmesh(MPI_COMM_WORLD, mesh);
mesh.Clear();
{
int par_ref_levels = 2;
for (int l = 0; l < par_ref_levels; l++)
{
pmesh.UniformRefinement();
}
}
// 7. Define a parallel finite element space on the parallel mesh. Here we
// use continuous Lagrange finite elements of the specified order. If
// order < 1, we instead use an isoparametric/isogeometric space.
FiniteElementCollection *fec;
bool delete_fec;
if (order > 0)
{
fec = new H1_FECollection(order, dim);
delete_fec = true;
}
else if (pmesh.GetNodes())
{
fec = pmesh.GetNodes()->OwnFEC();
delete_fec = false;
if (myid == 0)
{
cout << "Using isoparametric FEs: " << fec->Name() << endl;
}
}
else
{
fec = new H1_FECollection(order = 1, dim);
delete_fec = true;
}
ParFiniteElementSpace fespace(&pmesh, fec);
HYPRE_BigInt size = fespace.GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of finite element unknowns: " << size << endl;
}
// 8. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
Array<int> ess_tdof_list;
if (pmesh.bdr_attributes.Size())
{
Array<int> ess_bdr(pmesh.bdr_attributes.Max());
ess_bdr = 1;
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
// 9. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (1,phi_i) where phi_i are the basis functions in fespace.
ParLinearForm b(&fespace);
ConstantCoefficient one(1.0);
b.AddDomainIntegrator(new DomainLFIntegrator(one));
b.Assemble();
// 10. Define the solution vector x as a parallel finite element grid
// function corresponding to fespace. Initialize x with initial guess of
// zero, which satisfies the boundary conditions.
ParGridFunction x(&fespace);
x = 0.0;
// 11. Set up the parallel bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the
// Diffusion domain integrator.
ParBilinearForm a(&fespace);
if (pa) { a.SetAssemblyLevel(AssemblyLevel::PARTIAL); }
if (fa)
{
a.SetAssemblyLevel(AssemblyLevel::FULL);
// Sort the matrix column indices when running on GPU or with OpenMP (i.e.
// when Device::IsEnabled() returns true). This makes the results
// bit-for-bit deterministic at the cost of somewhat longer run time.
a.EnableSparseMatrixSorting(Device::IsEnabled());
}
a.AddDomainIntegrator(new DiffusionIntegrator(one));
// 12. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
if (static_cond) { a.EnableStaticCondensation(); }
a.Assemble();
OperatorPtr A;
Vector B, X;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
// 13. Solve the linear system A X = B.
if (!pa)
{
switch (solver_config)
{
// Solve the linear system with CG + Schwarz (with IC) from Ginkgo
case 0:
{
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
Ginkgo::GinkgoExecutor exec(device);
Ginkgo::IcPreconditioner local_solver(exec, "exact");
Ginkgo::SchwarzPreconditioner gko_M(exec, MPI_COMM_WORLD, local_solver);
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
ginkgo_solver.SetPrintLevel(print_lvl);
ginkgo_solver.SetRelTol(sqrt(1e-12));
ginkgo_solver.SetAbsTol(0.0);
ginkgo_solver.SetMaxIter(400);
ginkgo_solver.SetOperator(*(A.Ptr()));
ginkgo_solver.Mult(B, X);
break;
}
// Solve the linear system with CG from Ginkgo + MFEM preconditioner
case 1:
{
if (myid == 0) { cout << "Using Ginkgo solver + MFEM preconditioner...\n"; }
Ginkgo::GinkgoExecutor exec(device);
//Create MFEM preconditioner and wrap it for Ginkgo's use.
HypreBoomerAMG M((HypreParMatrix&)(*A));
Ginkgo::MFEMPreconditioner gko_M(exec, M, MPI_COMM_WORLD);
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
ginkgo_solver.SetPrintLevel(print_lvl);
ginkgo_solver.SetRelTol(sqrt(1e-12));
ginkgo_solver.SetAbsTol(0.0);
ginkgo_solver.SetMaxIter(400);
ginkgo_solver.SetOperator(*(A.Ptr()));
ginkgo_solver.Mult(B, X);
break;
}
// Ginkgo Schwarz preconditioner (local ParIC) + MFEM CG solver
case 2:
{
if (myid == 0) { cout << "Using MFEM solver + Ginkgo preconditioner...\n"; }
Ginkgo::GinkgoExecutor exec(device);
Ginkgo::IcPreconditioner local_M(exec, "exact");
Ginkgo::SchwarzPreconditioner M(exec, MPI_COMM_WORLD, local_M);
M.SetOperator(*(A.Ptr())); // Generate the preconditioner for the matrix A.
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(sqrt(1e-12));
cg.SetMaxIter(400);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.Mult(B, X);
break;
}
// MFEM solver + MFEM preconditioner
case 3:
{
if (myid == 0) { cout << "Using MFEM solver + MFEM preconditioner...\n"; }
HypreBoomerAMG M((HypreParMatrix&)(*A));
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(sqrt(1e-12));
cg.SetMaxIter(400);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.Mult(B, X);
break;
}
} // End switch on solver_config
}
// Partial assembly mode. Cannot use Ginkgo preconditioners, but can use Ginkgo
// solvers.
else
{
if (UsesTensorBasis(fespace))
{
// Use Jacobi preconditioning in partial assembly mode.
OperatorJacobiSmoother M(a, ess_tdof_list);
switch (solver_config)
{
case 0:
{
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
MFEM_ABORT("Cannot use Ginkgo preconditioner in partial assembly mode.\n"
" Try -s 1 to test Ginkgo solver with an MFEM preconditioner.");
break;
}
// Use Ginkgo solver with MFEM preconditioner
case 1:
{
if (myid == 0) { cout << "Using Ginkgo solver + MFEM preconditioner...\n"; }
Ginkgo::GinkgoExecutor exec(device);
// Wrap MFEM preconditioner for Ginkgo's use.
Ginkgo::MFEMPreconditioner gko_M(exec, M, MPI_COMM_WORLD);
Ginkgo::CGSolver ginkgo_solver(exec, MPI_COMM_WORLD, gko_M);
ginkgo_solver.SetPrintLevel(print_lvl);
ginkgo_solver.SetRelTol(sqrt(1e-12));
ginkgo_solver.SetAbsTol(0.0);
ginkgo_solver.SetMaxIter(400);
ginkgo_solver.SetOperator(*(A.Ptr()));
ginkgo_solver.Mult(B, X);
break;
}
// No Ginkgo preconditioners work with matrix-free; error
case 2:
{
if (myid == 0) { cout << "Using Ginkgo solver + preconditioner...\n"; }
MFEM_ABORT("Cannot use Ginkgo preconditioner in partial assembly mode.\n"
" Try -s 1 to test Ginkgo solver with an MFEM preconditioner.");
break;
}
// Use MFEM solver and preconditioner
case 3:
{
if (myid == 0) { cout << "Using MFEM solver + MFEM preconditioner...\n"; }
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(sqrt(1e-12));
cg.SetMaxIter(400);
cg.SetPrintLevel(1);
cg.SetPreconditioner(M);
cg.SetOperator(*A);
cg.Mult(B, X);
break;
}
} // End switch on solver_config
}
else // CG with no preconditioning
{
if (myid == 0) { cout << "Using MFEM solver + no preconditioner...\n"; }
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(sqrt(1e-12));
cg.SetMaxIter(400);
cg.SetPrintLevel(1);
cg.SetOperator(*A);
cg.Mult(B, X);
}
}
// 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a.RecoverFEMSolution(X, b, x);
// 15. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np <np> -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh.Print(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
x.Save(sol_ofs);
}
// 16. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
socketstream sol_sock(vishost, visport);
sol_sock << "parallel " << num_procs << " " << myid << "\n";
sol_sock.precision(8);
sol_sock << "solution\n" << pmesh << x << flush;
}
// 17. Free the used memory.
if (delete_fec)
{
delete fec;
}
return 0;
}
+1 -2
View File
@@ -20,9 +20,8 @@ CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
# Currently there are only serial Ginkgo examples
SEQ_EXAMPLES = ex1
PAR_EXAMPLES =
PAR_EXAMPLES = ex1p
ifeq ($(MFEM_USE_MPI),NO)
EXAMPLES = $(SEQ_EXAMPLES)
else
+2 -2
View File
@@ -22,11 +22,11 @@ MFEM_LIB_FILE = mfem_is_not_built
SEQ_EXAMPLES = ex0 ex1 ex2 ex3 ex4 ex5 ex6 ex7 ex8 ex9 ex10 ex14 ex15 ex16 \
ex17 ex18 ex19 ex20 ex21 ex22 ex23 ex24 ex25 ex26 ex27 ex28 ex29 ex30 \
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40 ex41
ex31 ex33 ex34 ex36 ex37 ex38 ex39 ex40 ex41 ex43
PAR_EXAMPLES = ex0p ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex8p ex9p ex10p ex11p \
ex12p ex13p ex14p ex15p ex16p ex17p ex18p ex19p ex20p ex21p ex22p ex24p \
ex25p ex26p ex27p ex28p ex29p ex30p ex31p ex32p ex33p ex34p ex35p ex36p \
ex37p ex39p ex40p ex41p
ex37p ex39p ex40p ex41p ex43p
SEQ_DEVICE_EXAMPLES = ex1 ex3 ex4 ex5 ex6 ex9 ex14 ex22 ex24 ex25 ex26 ex34
PAR_DEVICE_EXAMPLES = ex1p ex2p ex3p ex4p ex5p ex6p ex7p ex9p ex13p ex14p \
ex22p ex24p ex25p ex26p ex34p ex35p
+175
View File
@@ -4213,6 +4213,181 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
}
}
void SlidingElasticityIntegrator::AssembleFaceMatrix(
const FiniteElement &el1, const FiniteElement &el2,
FaceElementTransformations &Trans, DenseMatrix &elmat)
{
MFEM_ASSERT(Trans.Elem2No < 0,
"support for interior faces is not implemented");
#ifdef MFEM_THREAD_SAFE
// For descriptions of these variables, see the class declaration.
Vector shape1;
DenseMatrix dshape1;
DenseMatrix adjJ;
DenseMatrix dshape1_ps;
Vector nor;
Vector nL1;
Vector nM1;
Vector nt1;
Vector dshape1_dnM;
Vector dshape1_dnt;
DenseMatrix jmat;
#endif
const int dim = el1.GetDim();
const int ndofs1 = el1.GetDof();
const int nvdofs = dim * ndofs1;
// Initially 'elmat' corresponds to the term:
// < { sigma(u) n . ñ }, v . ñ > =
// < { (lambda div(u) I + mu (grad(u) + grad(u)^T)) n . ñ }, v . ñ >
// But eventually, it's going to be replaced by:
// elmat := -elmat + alpha*elmat^T + jmat
elmat.SetSize(nvdofs);
elmat = 0.;
const bool kappa_is_nonzero = (kappa != 0.0);
if (kappa_is_nonzero)
{
jmat.SetSize(nvdofs);
jmat = 0.;
}
adjJ.SetSize(dim);
shape1.SetSize(ndofs1);
dshape1.SetSize(ndofs1, dim);
dshape1_ps.SetSize(ndofs1, dim);
nor.SetSize(dim);
nL1.SetSize(dim);
nM1.SetSize(dim);
nt1.SetSize(dim);
dshape1_dnM.SetSize(ndofs1);
dshape1_dnt.SetSize(ndofs1);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
// a simple choice for the integration order; is this OK?
const int order = 2 * el1.GetOrder();
ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
for (int pind = 0; pind < ir->GetNPoints(); ++pind)
{
const IntegrationPoint &ip = ir->IntPoint(pind);
// Set the integration point in the face and the neighboring elements
Trans.SetAllIntPoints(&ip);
// Access the neighboring element's integration point
const IntegrationPoint &eip1 = Trans.GetElement1IntPoint();
el1.CalcShape(eip1, shape1);
el1.CalcDShape(eip1, dshape1);
CalcAdjugate(Trans.Elem1->Jacobian(), adjJ);
Mult(dshape1, adjJ, dshape1_ps);
if (dim == 1)
{
nor(0) = 2*eip1.x - 1.0;
}
else
{
CalcOrtho(Trans.Jacobian(), nor);
}
if (!nt)
{
// Set ñ to the unit normal vector if not provided
nt1 = nor;
nt1 /= nt1.Norml2();
}
else
{
// Evaluate vector function ñ at integration point
nt->Eval(nt1, *Trans.Elem1, eip1);
}
const real_t W = ip.weight;
const real_t W1 = W / Trans.Elem1->Weight();
const real_t WL1 = W1 * lambda->Eval(*Trans.Elem1, eip1);
const real_t WM1 = W1 * mu->Eval(*Trans.Elem1, eip1);
nL1.Set(WL1, nor);
nM1.Set(WM1, nor);
const real_t WLM = WL1 + 2.0*WM1;
dshape1_ps.Mult(nM1, dshape1_dnM);
dshape1_ps.Mult(nt1, dshape1_dnt);
const real_t jmatcoef = kappa * (nor*nor) * WLM;
const real_t nL_dot_nt1 = nL1 * nt1;
for (int jm = 0, j = 0; jm < dim; ++jm)
{
for (int jdof = 0; jdof < ndofs1; ++jdof, ++j)
{
const real_t t1 = dshape1_ps(jdof, jm) * nL_dot_nt1;
const real_t t2 = dshape1_dnM(jdof) * nt1(jm);
const real_t t3 = dshape1_dnt(jdof) * nM1(jm);
const real_t tt = t1 + t2 + t3;
for (int im = 0, i = 0; im < dim; ++im)
{
for (int idof = 0; idof < ndofs1; ++idof, ++i)
{
elmat(i, j) += tt * shape1(idof) * nt1(im);
}
}
}
}
if (kappa_is_nonzero)
{
for (int jm = 0, j = 0; jm < dim; ++jm)
{
for (int jdof = 0; jdof < ndofs1; ++jdof, ++j)
{
const real_t sj = jmatcoef * shape1(jdof) * nt1(jm);
for (int im = 0, i = 0; im < dim; ++im)
{
for (int idof = 0; idof < ndofs1; ++idof, ++i)
{
jmat(i, j) += shape1(idof) * sj * nt1(im);
}
}
}
}
}
}
// elmat := -elmat + alpha*elmat^t + jmat
if (kappa_is_nonzero)
{
for (int i = 0; i < nvdofs; ++i)
{
for (int j = 0; j < i; ++j)
{
real_t aij = elmat(i,j), aji = elmat(j,i), mij = jmat(i,j);
elmat(i,j) = alpha*aji - aij + mij;
elmat(j,i) = alpha*aij - aji + mij;
}
elmat(i,i) = (alpha - 1.)*elmat(i,i) + jmat(i,i);
}
}
else
{
for (int i = 0; i < nvdofs; ++i)
{
for (int j = 0; j < i; ++j)
{
real_t aij = elmat(i,j), aji = elmat(j,i);
elmat(i,j) = alpha*aji - aij;
elmat(j,i) = alpha*aij - aji;
}
elmat(i,i) *= (alpha - 1.);
}
}
}
void TraceJumpIntegrator::AssembleFaceMatrix(
const FiniteElement &trial_face_fe, const FiniteElement &test_fe1,
+78
View File
@@ -3738,6 +3738,84 @@ protected:
DenseMatrix &elmat, DenseMatrix &jmat);
};
/** Integrator for the Nitsche elasticity form:
$$
\begin{split}
a(u,v)
&:= -\langle \sigma(u)\, \vec{n} \cdot \tilde{n},\ v \cdot \tilde{n}
\rangle + \alpha \langle \sigma(v)\, \vec{n} \cdot \tilde{n},\ u \cdot
\tilde{n} \rangle + \kappa \langle h^{-1} (\lambda + 2\mu)\, u \cdot
\tilde{n},\ v \cdot \tilde{n} \rangle \\
&= -\int_\Gamma (\sigma(u)\, n \cdot \tilde{n})(v \cdot \tilde{n})\, dS +
\alpha \int_\Gamma (\sigma(v)\, n \cdot \tilde{n})(u \cdot \tilde{n})\,
dS + \kappa \int_\Gamma h^{-1} (\lambda + 2\mu)(u \cdot \tilde{n})(v
\cdot \tilde{n})\, dS.
\end{split}
$$
For isotropic media,
$$
\begin{split}
\sigma(u) &= \lambda \nabla \cdot u I + 2 \mu \varepsilon(u) \\
&= \lambda \nabla \cdot u I + 2 \mu \frac{1}{2} (\nabla u + \nabla
u^{\mathrm{T}}) \\
&= \lambda \nabla \cdot u I + \mu (\nabla u + \nabla u^{\mathrm{T}})
\end{split}
$$
where $I$ is the identity matrix, $\lambda$ and $\mu$ are the Lamé
coefficients (see ElasticityIntegrator), $\tilde{n}$ is a unit vector
field, $\alpha = \pm 1$ and $\kappa > 0$ are the Nitsche parameters, and
$u$, $v$ are the trial and test functions, respectively.
This is a '%Vector' integrator, i.e. defined for FE spaces using multiple
copies of a scalar FE space.
*/
class SlidingElasticityIntegrator : public BilinearFormIntegrator
{
public:
SlidingElasticityIntegrator(Coefficient &lambda_, Coefficient &mu_,
real_t kappa_)
: nt(NULL), lambda(&lambda_), mu(&mu_), alpha(-1.0), kappa(kappa_) { }
SlidingElasticityIntegrator(VectorCoefficient &nt_, Coefficient &lambda_,
Coefficient &mu_, real_t alpha_, real_t kappa_)
: nt(&nt_), lambda(&lambda_), mu(&mu_), alpha(alpha_), kappa(kappa_) { }
using BilinearFormIntegrator::AssembleFaceMatrix;
void AssembleFaceMatrix(const FiniteElement &el1,
const FiniteElement &el2,
FaceElementTransformations &Trans,
DenseMatrix &elmat) override;
protected:
VectorCoefficient *nt;
Coefficient *lambda, *mu;
real_t alpha, kappa;
#ifndef MFEM_THREAD_SAFE
// values of all scalar basis functions for one component of u (which is a
// vector) at the integration point in the reference space
Vector shape1;
// values of derivatives of all scalar basis functions for one component
// of u (which is a vector) at the integration point in the reference space
DenseMatrix dshape1;
// Adjugate of the Jacobian of the transformation: adjJ = det(J) J^{-1}
DenseMatrix adjJ;
// gradient of shape functions in the real (physical, not reference)
// coordinates, scaled by det(J):
// dshape_ps(jdof,jm) = sum_{t} adjJ(t,jm)*dshape(jdof,t)
DenseMatrix dshape1_ps;
Vector nor; // nor = |weight(J_face)| n
Vector nL1; // nL1 = (lambda1 * ip.weight / detJ1) nor
Vector nM1; // nM1 = (mu1 * ip.weight / detJ1) nor
Vector nt1; // nt1 = vector function ñ evaluated at ip1
Vector dshape1_dnM; // dshape1_dnM = dshape1_ps . nM1
Vector dshape1_dnt; // dshape1_dnt = dshape1_ps . nt1
// 'jmat' corresponds to the term: kappa <h⁻¹ u ⋅ ñ, v ⋅ ñ>
DenseMatrix jmat;
#endif
};
/** Integrator for the DPG form:$ \langle v, [w] \rangle $ over all faces (the interface) where
the trial variable $v$ is defined on the interface and the test variable $w$ is
defined inside the elements, generally in a DG space. */
+67
View File
@@ -1302,6 +1302,73 @@ real_t TraceCoefficient::Eval(ElementTransformation &T,
return ma.Trace();
}
VectorComponentCoefficient::VectorComponentCoefficient(VectorCoefficient &A,
int c)
: a(&A), va(A.GetVDim())
{
SetComponent(c);
}
void VectorComponentCoefficient::SetComponent(int c)
{
MFEM_ASSERT(c < a->GetVDim() && c >= 0,
"VectorComponentCoefficient: "
"Index not in range.");
component = c;
}
void VectorComponentCoefficient::SetTime(real_t t)
{
if (a) { a->SetTime(t); }
this->Coefficient::SetTime(t);
}
real_t VectorComponentCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
a->Eval(va, T, ip);
return va[component];
}
MatrixComponentCoefficient::MatrixComponentCoefficient(MatrixCoefficient &A,
int ri, int ci)
: a(&A), ma(A.GetHeight(), A.GetWidth())
{
SetRowIndex(ri);
SetColumnIndex(ci);
}
void MatrixComponentCoefficient::SetRowIndex(int ri)
{
MFEM_ASSERT(ri < a->GetHeight() && ri >= 0,
"MatrixComponentCoefficient: "
"Row index not in range.");
row_idx = ri;
}
void MatrixComponentCoefficient::SetColumnIndex(int ci)
{
MFEM_ASSERT(ci < a->GetWidth() && ci >= 0,
"MatrixComponentCoefficient: "
"Column index not in range.");
col_idx = ci;
}
void MatrixComponentCoefficient::SetTime(real_t t)
{
if (a) { a->SetTime(t); }
this->Coefficient::SetTime(t);
}
real_t MatrixComponentCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
a->Eval(ma, T, ip);
return ma(row_idx,col_idx);
}
VectorSumCoefficient::VectorSumCoefficient(int dim)
: VectorCoefficient(dim),
ACoef(NULL), BCoef(NULL),
+83 -5
View File
@@ -114,11 +114,10 @@ public:
/// Construct the constant coefficient using a vector of constants.
/** @a c should be a vector defined by attributes, so for region with
attribute @a i @a c[i-1] is the coefficient in that region */
PWConstCoefficient(Vector &c)
{ constants.SetSize(c.Size()); constants=c; }
PWConstCoefficient(const Vector &c) { UpdateConstants(c); }
/// Update the constants with vector @a c.
void UpdateConstants(Vector &c) { constants.SetSize(c.Size()); constants=c; }
void UpdateConstants(const Vector &c) { constants = c; }
/// Return a reference to the i-th constant
real_t &operator()(int i) { return constants(i-1); }
@@ -1332,8 +1331,8 @@ public:
/// Get the coefficient located at (i,j) in the matrix.
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
/** @brief Set the coefficient located at (i,j) in the matrix. By default by
default this will take ownership of the Coefficient passed in, but this
/** @brief Set the coefficient located at (i,j) in the matrix. By default
this will take ownership of the Coefficient passed in, but this
can be overridden with the @a own parameter. */
void Set(int i, int j, Coefficient * c, bool own=true);
@@ -1873,6 +1872,85 @@ public:
const IntegrationPoint &ip) override;
};
/// Scalar coefficient defined as component of a vector coefficient
class VectorComponentCoefficient : public Coefficient
{
private:
VectorCoefficient *a = nullptr;
mutable Vector va;
int component;
public:
/// Construct with a vector coefficient.
VectorComponentCoefficient(VectorCoefficient &A)
: a(&A), va(A.GetVDim()), component(0) {};
VectorComponentCoefficient(VectorCoefficient &A, int c);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
/// Reset the vector coefficient
void SetACoef(VectorCoefficient &A) { a = &A; }
/// Return the vector coefficient
VectorCoefficient * GetACoef() const { return a; }
/// Set the component
void SetComponent(int c);
/// Return the component
int GetComponent() const { return component; }
/// Evaluate the trace coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
};
/// Scalar coefficient defined as component of a matrix coefficient
class MatrixComponentCoefficient : public Coefficient
{
private:
MatrixCoefficient *a = nullptr;
mutable DenseMatrix ma;
int row_idx,col_idx;
public:
MatrixComponentCoefficient(MatrixCoefficient &A)
: a(&A), ma(A.GetHeight(), A.GetWidth()), row_idx(0), col_idx(0) {};
/// Construct with the matrix coefficient.
MatrixComponentCoefficient(MatrixCoefficient &A, int ri, int ci);
/// Set the time for internally stored coefficients
void SetTime(real_t t) override;
/// Reset the matrix coefficient
void SetACoef(MatrixCoefficient &A) { a = &A; }
/// Return the matrix coefficient
MatrixCoefficient * GetACoef() const { return a; }
/// Reset the index
void SetRowIndex(int ri);
/// Return the index
int GetRowIndex() const { return row_idx; }
/// Reset the index
void SetColumnIndex(int ci);
/// Return the index
int GetColumnIndex() const { return col_idx; }
/// Evaluate the trace coefficient at @a ip.
real_t Eval(ElementTransformation &T,
const IntegrationPoint &ip) override;
};
/// Vector coefficient defined as the linear combination of two vectors
class VectorSumCoefficient : public VectorCoefficient
{
+1 -1
View File
@@ -10,7 +10,7 @@
// CONTRIBUTING.md for details.
#pragma once
// This is serac's tuple implementation
// This is smith's tuple implementation
#include <ostream>
#include "../../config/config.hpp"
-165
View File
@@ -12,7 +12,6 @@
#include "dgmassinv.hpp"
#include "bilinearform.hpp"
#include "dgmassinv_kernels.hpp"
#include "../general/forall.hpp"
namespace mfem
{
@@ -119,151 +118,6 @@ void DGMassInverse::Update()
DGMassInverse::~DGMassInverse() = default;
template<int DIM, int D1D, int Q1D>
void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
{
using namespace internal; // host/device kernel functions
const int NE = fes.GetNE();
const int d1d = m->dofs1D;
const int q1d = m->quad1D;
const int ND = static_cast<int>(pow(d1d, DIM));
const auto B = m->maps->B.Read();
const auto Bt = m->maps->Bt.Read();
const auto pa_data = m->pa_data.Read();
const auto dinv = diag_inv.Read();
auto r = r_.Write();
auto d = d_.Write();
auto z = z_.Write();
auto u = u_.ReadWrite();
const real_t RELTOL = rel_tol;
const real_t ABSTOL = abs_tol;
const int MAXIT = max_iter;
const bool IT_MODE = iterative_mode;
const bool CHANGE_BASIS = (d2q != nullptr);
// b is the right-hand side (if no change of basis, this just points to the
// incoming RHS vector, if we have to change basis, this points to the
// internal b2 vector where we put the transformed RHS)
const real_t *b;
// the following are non-null if we have to change basis
real_t *b2 = nullptr; // non-const access to b2
const real_t *b_orig = nullptr; // RHS vector in "original" basis
const real_t *d2q_B = nullptr; // matrix to transform initial guess
const real_t *q2d_B = nullptr; // matrix to transform solution
const real_t *q2d_Bt = nullptr; // matrix to transform RHS
if (CHANGE_BASIS)
{
d2q_B = d2q->B.Read();
q2d_B = B_.Read();
q2d_Bt = Bt_.Read();
b2 = b2_.Write();
b_orig = b_.Read();
b = b2;
}
else
{
b = b_.Read();
}
static constexpr int NB = Q1D ? Q1D : 1; // block size
mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
{
// Perform change of basis if needed
if (CHANGE_BASIS)
{
// Transform RHS
DGMassBasis<DIM,D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
if (IT_MODE)
{
// Transform initial guess
DGMassBasis<DIM,D1D>(e, NE, d2q_B, u, u, d1d);
}
}
const int tid = MFEM_THREAD_ID(x) + NB*MFEM_THREAD_ID(y);
// Compute first residual
if (IT_MODE)
{
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, u, r, d1d, q1d);
DGMassAxpy(e, NE, ND, 1.0, b, -1.0, r, r); // r = b - r
}
else
{
// if not in iterative mode, use zero initial guess
const int BX = MFEM_THREAD_SIZE(x);
const int BY = MFEM_THREAD_SIZE(y);
const int bxy = BX*BY;
const auto B = ConstDeviceMatrix(b, ND, NE);
auto U = DeviceMatrix(u, ND, NE);
auto R = DeviceMatrix(r, ND, NE);
for (int i = tid; i < ND; i += bxy)
{
U(i, e) = 0.0;
R(i, e) = B(i, e);
}
MFEM_SYNC_THREAD;
}
DGMassPreconditioner(e, NE, ND, dinv, r, z);
DGMassAxpy(e, NE, ND, 1.0, z, 0.0, z, d); // d = z
real_t nom = DGMassDot<NB>(e, NE, ND, d, r);
if (nom < 0.0) { return; /* Not positive definite */ }
real_t r0 = fmax(nom*RELTOL*RELTOL, ABSTOL*ABSTOL);
if (nom <= r0) { return; /* Converged */ }
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d);
real_t den = DGMassDot<NB>(e, NE, ND, z, d);
if (den <= 0.0)
{
DGMassDot<NB>(e, NE, ND, d, d);
// d2 > 0 => not positive definite
if (den == 0.0) { return; }
}
// start iteration
int i = 1;
while (true)
{
const real_t alpha = nom/den;
DGMassAxpy(e, NE, ND, 1.0, u, alpha, d, u); // u = u + alpha*d
DGMassAxpy(e, NE, ND, 1.0, r, -alpha, z, r); // r = r - alpha*A*d
DGMassPreconditioner(e, NE, ND, dinv, r, z);
real_t betanom = DGMassDot<NB>(e, NE, ND, r, z);
if (betanom < 0.0) { return; /* Not positive definite */ }
if (betanom <= r0) { break; /* Converged */ }
if (++i > MAXIT) { break; }
const real_t beta = betanom/nom;
DGMassAxpy(e, NE, ND, 1.0, z, beta, d, d); // d = z + beta*d
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d); // z = A d
den = DGMassDot<NB>(e, NE, ND, d, z);
if (den <= 0.0)
{
DGMassDot<NB>(e, NE, ND, d, d);
// d2 > 0 => not positive definite
if (den == 0.0) { break; }
}
nom = betanom;
}
if (CHANGE_BASIS)
{
DGMassBasis<DIM,D1D>(e, NE, q2d_B, u, u, d1d);
}
});
}
void DGMassInverse::Mult(const Vector &Mu, Vector &u) const
{
// Dispatch to templated version based on dim, d1d, and q1d.
@@ -306,23 +160,4 @@ DGMassInvKernels::DGMassInvKernels()
k::Specialization<3,6,7>::Add();
}
/// @cond Suppress_Doxygen_warnings
template <int DIM, int D1D, int Q1D>
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
{
return &DGMassInverse::DGMassCGIteration<DIM,D1D,Q1D>;
}
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
int dim, int, int)
{
if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
else { MFEM_ABORT("Unsupported dimension."); }
}
/// @endcond
} // namespace mfem
+165
View File
@@ -15,6 +15,7 @@
#include "../linalg/kernels.hpp"
#include "kernels.hpp"
#include "integ/bilininteg_mass_kernels.hpp"
#include "dgmassinv.hpp"
namespace mfem
{
@@ -333,6 +334,170 @@ void DGMassBasis(const int e,
} // namespace internal
template<int DIM, int D1D, int Q1D>
void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
{
using namespace internal; // host/device kernel functions
const int NE = fes.GetNE();
const int d1d = m->dofs1D;
const int q1d = m->quad1D;
const int ND = static_cast<int>(pow(d1d, DIM));
const auto B = m->maps->B.Read();
const auto Bt = m->maps->Bt.Read();
const auto pa_data = m->pa_data.Read();
const auto dinv = diag_inv.Read();
auto r = r_.Write();
auto d = d_.Write();
auto z = z_.Write();
auto u = u_.ReadWrite();
const real_t RELTOL = rel_tol;
const real_t ABSTOL = abs_tol;
const int MAXIT = max_iter;
const bool IT_MODE = iterative_mode;
const bool CHANGE_BASIS = (d2q != nullptr);
// b is the right-hand side (if no change of basis, this just points to the
// incoming RHS vector, if we have to change basis, this points to the
// internal b2 vector where we put the transformed RHS)
const real_t *b;
// the following are non-null if we have to change basis
real_t *b2 = nullptr; // non-const access to b2
const real_t *b_orig = nullptr; // RHS vector in "original" basis
const real_t *d2q_B = nullptr; // matrix to transform initial guess
const real_t *q2d_B = nullptr; // matrix to transform solution
const real_t *q2d_Bt = nullptr; // matrix to transform RHS
if (CHANGE_BASIS)
{
d2q_B = d2q->B.Read();
q2d_B = B_.Read();
q2d_Bt = Bt_.Read();
b2 = b2_.Write();
b_orig = b_.Read();
b = b2;
}
else
{
b = b_.Read();
}
static constexpr int NB = Q1D ? Q1D : 1; // block size
mfem::forall_2D(NE, NB, NB, [=] MFEM_HOST_DEVICE (int e)
{
// Perform change of basis if needed
if (CHANGE_BASIS)
{
// Transform RHS
DGMassBasis<DIM,D1D>(e, NE, q2d_Bt, b_orig, b2, d1d);
if (IT_MODE)
{
// Transform initial guess
DGMassBasis<DIM,D1D>(e, NE, d2q_B, u, u, d1d);
}
}
const int tid = MFEM_THREAD_ID(x) + NB*MFEM_THREAD_ID(y);
// Compute first residual
if (IT_MODE)
{
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, u, r, d1d, q1d);
DGMassAxpy(e, NE, ND, 1.0, b, -1.0, r, r); // r = b - r
}
else
{
// if not in iterative mode, use zero initial guess
const int BX = MFEM_THREAD_SIZE(x);
const int BY = MFEM_THREAD_SIZE(y);
const int bxy = BX*BY;
const auto B = ConstDeviceMatrix(b, ND, NE);
auto U = DeviceMatrix(u, ND, NE);
auto R = DeviceMatrix(r, ND, NE);
for (int i = tid; i < ND; i += bxy)
{
U(i, e) = 0.0;
R(i, e) = B(i, e);
}
MFEM_SYNC_THREAD;
}
DGMassPreconditioner(e, NE, ND, dinv, r, z);
DGMassAxpy(e, NE, ND, 1.0, z, 0.0, z, d); // d = z
real_t nom = DGMassDot<NB>(e, NE, ND, d, r);
if (nom < 0.0) { return; /* Not positive definite */ }
real_t r0 = fmax(nom*RELTOL*RELTOL, ABSTOL*ABSTOL);
if (nom <= r0) { return; /* Converged */ }
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d);
real_t den = DGMassDot<NB>(e, NE, ND, z, d);
if (den <= 0.0)
{
DGMassDot<NB>(e, NE, ND, d, d);
// d2 > 0 => not positive definite
if (den == 0.0) { return; }
}
// start iteration
int i = 1;
while (true)
{
const real_t alpha = nom/den;
DGMassAxpy(e, NE, ND, 1.0, u, alpha, d, u); // u = u + alpha*d
DGMassAxpy(e, NE, ND, 1.0, r, -alpha, z, r); // r = r - alpha*A*d
DGMassPreconditioner(e, NE, ND, dinv, r, z);
real_t betanom = DGMassDot<NB>(e, NE, ND, r, z);
if (betanom < 0.0) { return; /* Not positive definite */ }
if (betanom <= r0) { break; /* Converged */ }
if (++i > MAXIT) { break; }
const real_t beta = betanom/nom;
DGMassAxpy(e, NE, ND, 1.0, z, beta, d, d); // d = z + beta*d
DGMassApply<DIM,D1D,Q1D>(e, NE, B, Bt, pa_data, d, z, d1d, q1d); // z = A d
den = DGMassDot<NB>(e, NE, ND, d, z);
if (den <= 0.0)
{
DGMassDot<NB>(e, NE, ND, d, d);
// d2 > 0 => not positive definite
if (den == 0.0) { break; }
}
nom = betanom;
}
if (CHANGE_BASIS)
{
DGMassBasis<DIM,D1D>(e, NE, q2d_B, u, u, d1d);
}
});
}
/// @cond Suppress_Doxygen_warnings
template <int DIM, int D1D, int Q1D>
inline DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
{
return &DGMassInverse::DGMassCGIteration<DIM,D1D,Q1D>;
}
inline DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
int dim, int, int)
{
if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
else { MFEM_ABORT("Unsupported dimension."); }
}
/// @endcond
} // namespace mfem
#endif
+3 -3
View File
@@ -44,7 +44,7 @@ public:
NumBasisTypes = 9 /**< Keep track of maximum types to prevent
hard-coding */
};
/** @brief If the input does not represents a valid BasisType, abort with an
/** @brief If the input does not represent a valid BasisType, abort with an
error; otherwise return the input. */
static int Check(int b_type)
{
@@ -52,7 +52,7 @@ public:
"unknown BasisType: " << b_type);
return b_type;
}
/** @brief If the input does not represents a valid nodal BasisType, abort
/** @brief If the input does not represent a valid nodal BasisType, abort
with an error; otherwise return the input. */
static int CheckNodal(int b_type)
{
@@ -1120,7 +1120,7 @@ public:
return GetPoints(p, btype, on_device);
}
/// Get coordinates of a closed (GaussLegendre) set of points if degree @a p
/// Get coordinates of a closed (GaussLobatto) set of points if degree @a p
const real_t *ClosedPoints(const int p,
const int btype = BasisType::GaussLobatto,
bool on_device = false)
+516 -1
View File
@@ -84,6 +84,46 @@ void NURBS1DFiniteElement::CalcHessian (const IntegrationPoint &ip,
add(1.0, hess, (-d2sum + 2*dsum*dsum*sum)*sum*sum, shape_x, hess);
}
void NURBS1DFiniteElement::Project(Coefficient &coeff,
ElementTransformation &Trans,
Vector &dofs) const
{
IntegrationPoint ip;
for (int i = 0; i <= order; i++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+order)) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+order);
Trans.SetIntPoint(&ip);
dofs(i) = coeff.Eval(Trans, ip);
}
}
void NURBS1DFiniteElement::Project(VectorCoefficient &vc,
ElementTransformation &Trans,
Vector &dofs) const
{
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
Vector x(vc.GetVDim());
IntegrationPoint ip;
for (int i = 0; i <= order; i++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+order)) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+order);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
for (int j = 0; j < x.Size(); j++)
{
dofs(dof*j+i) = x(j);
}
}
}
void NURBS2DFiniteElement::SetOrder() const
{
@@ -215,6 +255,63 @@ void NURBS2DFiniteElement::CalcHessian (const IntegrationPoint &ip,
}
}
void NURBS2DFiniteElement::Project(Coefficient &coeff,
ElementTransformation &Trans,
Vector &dofs) const
{
IntegrationPoint ip;
for (int o = 0, j = 0; j <= orders[1]; j++)
{
real_t ky = kv[1]->GetBotella(ijk[1] + j);
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
{
o += orders[0] + 1;
continue;
}
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
for (int i = 0; i <= orders[0]; i++, o++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
Trans.SetIntPoint(&ip);
dofs(o) = coeff.Eval(Trans, ip);
}
}
}
void NURBS2DFiniteElement::Project(VectorCoefficient &vc,
ElementTransformation &Trans,
Vector &dofs) const
{
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
Vector x(vc.GetVDim());
IntegrationPoint ip;
for (int o = 0, j = 0; j <= orders[1]; j++)
{
real_t ky = kv[1]->GetBotella(ijk[1] + j);
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
{
o += orders[0] + 1;
continue;
}
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
for (int i = 0; i <= orders[0]; i++, o++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
for (int v = 0; v < x.Size(); v++)
{
dofs(dof*v+o) = x(v);
}
}
}
}
void NURBS3DFiniteElement::SetOrder() const
{
@@ -401,6 +498,85 @@ void NURBS3DFiniteElement::CalcHessian (const IntegrationPoint &ip,
}
}
void NURBS3DFiniteElement::Project(Coefficient &coeff,
ElementTransformation &Trans,
Vector &dofs) const
{
IntegrationPoint ip;
for (int o = 0, k = 0; k <= orders[2]; k++)
{
real_t kz = kv[2]->GetBotella(ijk[2] + k);
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
{
o += (orders[0] + 1)*(orders[1] + 1);
continue;
}
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
for (int j = 0; j <= orders[1]; j++)
{
real_t ky = kv[1]->GetBotella(ijk[1] + j);
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
{
o += orders[0] + 1;
continue;
}
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
for (int i = 0; i <= orders[0]; i++, o++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
Trans.SetIntPoint(&ip);
dofs(o) = coeff.Eval(Trans, ip);
}
}
}
}
void NURBS3DFiniteElement::Project(VectorCoefficient &vc,
ElementTransformation &Trans,
Vector &dofs) const
{
MFEM_ASSERT(dofs.Size() == vc.GetVDim()*dof, "");
Vector x(vc.GetVDim());
IntegrationPoint ip;
for (int o = 0, k = 0; k <= orders[2]; k++)
{
real_t kz = kv[2]->GetBotella(ijk[2] + k);
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
{
o += (orders[0] + 1)*(orders[1] + 1);
continue;
}
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
for (int j = 0; j <= orders[1]; j++)
{
real_t ky = kv[1]->GetBotella(ijk[1] + j);
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
{
o += orders[0] + 1;
continue;
}
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
for (int i = 0; i <= orders[0]; i++, o++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
for (int v = 0; v < x.Size(); v++)
{
dofs(dof*v+o) = x(v);
}
}
}
}
}
void NURBS_HDiv2DFiniteElement::SetOrder() const
{
@@ -517,6 +693,63 @@ void NURBS_HDiv2DFiniteElement::CalcDivShape(const IntegrationPoint &ip,
}
}
void NURBS_HDiv2DFiniteElement::Project(VectorCoefficient &vc,
ElementTransformation &Trans,
Vector &dofs) const
{
MFEM_ASSERT(dofs.Size() == dof, "");
MFEM_ASSERT(vc.GetVDim() == 2, "");
Vector x(2), mx(2);
IntegrationPoint ip;
int o = 0;
for (int j = 0; j <= orders[1]; j++)
{
real_t ky = kv[1]->GetBotella(ijk[1] + j);
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
{
o += orders[0] + 2;
continue;
}
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
for (int i = 0; i <= orders[0]+1; i++, o++)
{
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.AdjugateJacobian().Mult(x,mx);
dofs(o) = mx(0);
}
}
for (int j = 0; j <= orders[1]+1; j++)
{
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
{
o += orders[0] + 1;
continue;
}
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
for (int i = 0; i <= orders[0]; i++, o++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.AdjugateJacobian().Mult(x,mx);
dofs(o) = mx(1);
}
}
}
NURBS_HDiv2DFiniteElement::~NURBS_HDiv2DFiniteElement()
{
if (kv1[0]) { delete kv1[0]; }
@@ -696,6 +929,120 @@ void NURBS_HDiv3DFiniteElement::CalcDivShape(const IntegrationPoint &ip,
}
}
void NURBS_HDiv3DFiniteElement::Project(VectorCoefficient &vc,
ElementTransformation &Trans,
Vector &dofs) const
{
MFEM_ASSERT(dofs.Size() == dof, "");
MFEM_ASSERT(vc.GetVDim() == 3, "");
Vector x(2), mx(3);
IntegrationPoint ip;
int o = 0;
for (int k = 0; k <= orders[2]; k++)
{
real_t kz = kv[2]->GetBotella(ijk[2] + k);
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
{
o += (orders[0] + 2)*(orders[1] + 1);
continue;
}
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
for (int j = 0; j <= orders[1]; j++)
{
real_t ky = kv[1]->GetBotella(ijk[1] + j);
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
{
o += orders[0] + 2;
continue;
}
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
for (int i = 0; i <= orders[0]+1; i++, o++)
{
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.AdjugateJacobian().Mult(x,mx);
dofs(o) = mx(0);
}
}
}
for (int k = 0; k <= orders[2]; k++)
{
real_t kz = kv[2]->GetBotella(ijk[2] + k);
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
{
o += (orders[0] + 1)*(orders[1] + 2);
continue;
}
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
for (int j = 0; j <= orders[1]+1; j++)
{
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
{
o += orders[0] + 1;
continue;
}
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
for (int i = 0; i <= orders[0]; i++, o++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.AdjugateJacobian().Mult(x,mx);
dofs(o) = mx(1);
}
}
}
for (int k = 0; k <= orders[2]+1; k++)
{
real_t kz = kv1[2]->GetBotella(ijk[2] + k);
if (!kv1[2]->inSpan(kz, ijk[2]+orders[2]+1))
{
o += (orders[0] + 1)*(orders[1] + 1);
continue;
}
ip.z = kv1[2]->GetRefPoint(kz, ijk[2]+orders[2]+1);
for (int j = 0; j <= orders[1]; j++)
{
real_t ky = kv[1]->GetBotella(ijk[1] + j);
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
{
o += orders[0] + 1;
continue;
}
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
for (int i = 0; i <= orders[0]; i++, o++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.AdjugateJacobian().Mult(x,mx);
dofs(o) = mx(2);
}
}
}
}
NURBS_HDiv3DFiniteElement::~NURBS_HDiv3DFiniteElement()
{
if (kv1[0]) { delete kv1[0]; }
@@ -817,13 +1164,68 @@ void NURBS_HCurl2DFiniteElement::CalcCurlShape(const IntegrationPoint &ip,
}
}
void NURBS_HCurl2DFiniteElement::Project(VectorCoefficient &vc,
ElementTransformation &Trans,
Vector &dofs) const
{
MFEM_ASSERT(dofs.Size() == dof, "");
MFEM_ASSERT(vc.GetVDim() == 2, "");
Vector x(2), xm(2);
IntegrationPoint ip;
int i, j, o;
for (o = 0, j = 0; j <= orders[1]+1; j++)
{
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
{
o += orders[0] + 1;
continue;
}
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
for (i = 0; i <= orders[0]; i++, o++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.Jacobian().MultTranspose(x,xm);
dofs(o) = xm(0);
}
}
for (j = 0; j <= orders[1]; j++)
{
real_t ky = kv[1]->GetBotella(ijk[1] + j);
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
{
o += orders[0] + 2;
continue;
}
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
for (i = 0; i <= orders[0]+1; i++, o++)
{
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.Jacobian().MultTranspose(x,xm);
dofs(o) = xm(1);
}
}
}
NURBS_HCurl2DFiniteElement::~NURBS_HCurl2DFiniteElement()
{
if (kv1[0]) { delete kv1[0]; }
if (kv1[1]) { delete kv1[1]; }
}
void NURBS_HCurl3DFiniteElement::SetOrder() const
{
orders[0] = kv[0]->GetOrder();
@@ -1003,11 +1405,124 @@ void NURBS_HCurl3DFiniteElement::CalcCurlShape(const IntegrationPoint &ip,
curl_shape(o,0) = shape1_x(i)*dsy1_sz;
curl_shape(o,1) = -dshape1_x(i)*sy1_sz;
curl_shape(o,2) = 0.0;
}
}
}
}
void NURBS_HCurl3DFiniteElement::Project(VectorCoefficient &vc,
ElementTransformation &Trans,
Vector &dofs) const
{
MFEM_ASSERT(dofs.Size() == dof, "");
MFEM_ASSERT(vc.GetVDim() == 3, "");
Vector x(3), xm(3);
IntegrationPoint ip;
int o = 0;
for (int k = 0; k <= orders[2]+1; k++)
{
real_t kz = kv1[2]->GetBotella(ijk[2] + k);
if (!kv1[2]->inSpan(kz, ijk[2]+orders[2]+1))
{
o += (orders[0] + 1)*(orders[1] + 2);
continue;
}
ip.z = kv1[2]->GetRefPoint(kz, ijk[2]+orders[2]+1);
for (int j = 0; j <= orders[1]+1; j++)
{
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
{
o += orders[0] + 1;
continue;
}
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
for (int i = 0; i <= orders[0]; i++, o++)
{
real_t kx = kv[0]->GetBotella(ijk[0] + i);
if (!kv[0]->inSpan(kx, ijk[0]+orders[0])) { continue; }
ip.x = kv[0]->GetRefPoint(kx, ijk[0]+orders[0]);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.Jacobian().MultTranspose(x,xm);
dofs(o) = xm(0);
}
}
}
for (int k = 0; k <= orders[2]+1; k++)
{
real_t kz = kv1[2]->GetBotella(ijk[2] + k);
if (!kv1[2]->inSpan(kz, ijk[2]+orders[2]+1))
{
o += (orders[0] + 2)*(orders[1] + 1);
continue;
}
ip.z = kv1[2]->GetRefPoint(kz, ijk[2]+orders[2]+1);
for (int j = 0; j <= orders[1]; j++)
{
real_t ky = kv[1]->GetBotella(ijk[1] + j);
if (!kv[1]->inSpan(ky, ijk[1]+orders[1]))
{
o += orders[0] + 2;
continue;
}
ip.y = kv[1]->GetRefPoint(ky, ijk[1]+orders[1]);
for (int i = 0; i <= orders[0]+1; i++, o++)
{
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.Jacobian().MultTranspose(x,xm);
dofs(o) = xm(1);
}
}
}
for (int k = 0; k <= orders[2]; k++)
{
real_t kz = kv[2]->GetBotella(ijk[2] + k);
if (!kv[2]->inSpan(kz, ijk[2]+orders[2]))
{
o += (orders[0] + 2)*(orders[1] + 2);
continue;
}
ip.z = kv[2]->GetRefPoint(kz, ijk[2]+orders[2]);
for (int j = 0; j <= orders[1]+1; j++)
{
real_t ky = kv1[1]->GetBotella(ijk[1] + j);
if (!kv1[1]->inSpan(ky, ijk[1]+orders[1]+1))
{
o += orders[0] + 2;
continue;
}
ip.y = kv1[1]->GetRefPoint(ky, ijk[1]+orders[1]+1);
for (int i = 0; i <= orders[0]+1; i++, o++)
{
real_t kx = kv1[0]->GetBotella(ijk[0] + i);
if (!kv1[0]->inSpan(kx, ijk[0]+orders[0]+1)) { continue; }
ip.x = kv1[0]->GetRefPoint(kx, ijk[0]+orders[0]+1);
Trans.SetIntPoint(&ip);
vc.Eval(x, Trans, ip);
Trans.Jacobian().MultTranspose(x,xm);
dofs(o) = xm(2);
}
}
}
}
NURBS_HCurl3DFiniteElement::~NURBS_HCurl3DFiniteElement()
{
if (kv1[0]) { delete kv1[0]; }
+64
View File
@@ -86,6 +86,18 @@ public:
DenseMatrix &dshape) const override;
void CalcHessian (const IntegrationPoint &ip,
DenseMatrix &hessian) const override;
using FiniteElement::Project;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(Coefficient &coeff,
ElementTransformation &Trans, Vector &dofs) const override;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(VectorCoefficient &vcoeff,
ElementTransformation &Trans, Vector &dofs) const override;
};
/// An arbitrary order 2D NURBS element on a square
@@ -121,6 +133,18 @@ public:
DenseMatrix &dshape) const override;
void CalcHessian (const IntegrationPoint &ip,
DenseMatrix &hessian) const override;
using FiniteElement::Project;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(Coefficient &coeff,
ElementTransformation &Trans, Vector &dofs) const override;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(VectorCoefficient &vcoeff,
ElementTransformation &Trans, Vector &dofs) const override;
};
/// An arbitrary order 3D NURBS element on a cube
@@ -161,6 +185,18 @@ public:
DenseMatrix &dshape) const override;
void CalcHessian (const IntegrationPoint &ip,
DenseMatrix &hessian) const override;
using FiniteElement::Project;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(Coefficient &coeff,
ElementTransformation &Trans, Vector &dofs) const override;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(VectorCoefficient &vcoeff,
ElementTransformation &Trans, Vector &dofs) const override;
};
@@ -242,6 +278,13 @@ public:
void CalcDivShape(const IntegrationPoint &ip,
Vector &divshape) const override;
using FiniteElement::Project;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(VectorCoefficient &vcoeff,
ElementTransformation &Trans, Vector &dofs) const override;
~NURBS_HDiv2DFiniteElement();
};
@@ -336,6 +379,13 @@ public:
void CalcDivShape(const IntegrationPoint &ip,
Vector &divshape) const override;
using FiniteElement::Project;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(VectorCoefficient &vcoeff,
ElementTransformation &Trans, Vector &dofs) const override;
~NURBS_HDiv3DFiniteElement();
};
@@ -415,6 +465,13 @@ public:
void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const override;
using FiniteElement::Project;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(VectorCoefficient &vcoeff,
ElementTransformation &Trans, Vector &dofs) const override;
~NURBS_HCurl2DFiniteElement();
};
@@ -506,6 +563,13 @@ public:
void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const override;
using FiniteElement::Project;
/** Evaluate the dofs that are defined on this element.
Dofs that can not be evaluated will remain unmodified. */
void Project(VectorCoefficient &vcoeff,
ElementTransformation &Trans, Vector &dofs) const override;
~NURBS_HCurl3DFiniteElement();
};
+13 -13
View File
@@ -111,36 +111,36 @@ public:
| :------: | :---: | :---: | :-------: | :-----: | :---: |
| H1_[DIM]_[ORDER] | H1 | * | 1 | VALUE | H1 nodal elements |
| H1@[BTYPE]_[DIM]_[ORDER] | H1 | * | * | VALUE | H1 nodal elements |
| H1Pos_[DIM]_[ORDER] | H1 | * | 1 | VALUE | H1 nodal elements |
| H1Pos_[DIM]_[ORDER] | H1 | * | 2 | VALUE | H1 nodal elements |
| H1Pos_Trace_[DIM]_[ORDER] | H^{1/2} | * | 2 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
| H1_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
| H1_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
| H1_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * | VALUE | H^{1/2}-conforming trace elements for H1 defined on the interface between mesh elements (faces,edges,vertices) |
| ND_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | Nedelec vector elements |
| ND@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | Nedelec vector elements |
| ND_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
| ND_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces,edges) |
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * / * | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces,edges) |
| ND_R1D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
| ND_R1D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
| ND_R2D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
| ND_R2D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(curl) | * | * / * | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 2D. |
| RT_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | Raviart-Thomas vector elements |
| RT@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | Raviart-Thomas vector elements |
| RT_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_ValTrace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_Trace_[DIM]_[ORDER] | H^{1/2} | * | 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_ValTrace_[DIM]_[ORDER] | H^{1/2} | * | 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | * | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_R1D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
| RT_R1D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
| RT_R2D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
| RT_R2D@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H(div) | * | * / * | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 2D. |
| L2_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | * | VALUE | Discontinuous L2 elements |
| L2Int_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
| L2Int_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
| L2Int_T[BTYPE]_[DIM]_[ORDER] | L2 | * | * | INTEGRAL | Discontinuous L2 elements |
| DG_Iface_[DIM]_[ORDER] | - | * | 0 | VALUE | Discontinuous elements on the interface between mesh elements (faces) |
| DG_Iface@[BTYPE]_[DIM]_[ORDER] | - | * | 0 | VALUE | Discontinuous elements on the interface between mesh elements (faces) |
| DG_Iface@[BTYPE]_[DIM]_[ORDER] | - | * | * | VALUE | Discontinuous elements on the interface between mesh elements (faces) |
| DG_IntIface_[DIM]_[ORDER] | - | * | 0 | INTEGRAL | Discontinuous elements on the interface between mesh elements (faces) |
| DG_IntIface@[BTYPE]_[DIM]_[ORDER] | - | * | 0 | INTEGRAL | Discontinuous elements on the interface between mesh elements (faces) |
| DG_IntIface@[BTYPE]_[DIM]_[ORDER] | - | * | * | INTEGRAL | Discontinuous elements on the interface between mesh elements (faces) |
| NURBS[ORDER] | - | * | - | VALUE | Non-Uniform Rational B-Splines (NURBS) elements |
| LinearNonConf3D | - | 1 | 1 | VALUE | Piecewise-linear nonconforming finite elements in 3D |
| CrouzeixRaviart | - | - | - | - | Crouzeix-Raviart nonconforming elements in 2D |
@@ -172,7 +172,7 @@ public:
| :------: | :--------: |
| [DIM] | Dimension of the elements (1D, 2D, 3D) |
| [ORDER] | Approximation order of the elements (P0, P1, P2, ...) |
| [BTYPE] | BasisType of the element (0-GaussLegendre, 1 - GaussLobatto, 2-Bernstein, 3-OpenUniform, 4-CloseUniform, 5-OpenHalfUniform) |
| [BTYPE] | BasisType of the element (0-GaussLegendre, 1-GaussLobatto, 2-Bernstein, 3-OpenUniform, 4-CloseUniform, 5-OpenHalfUniform 6-Serendipity 7-ClosedGL 8-IntegratedGLL) |
| [OBTYPE] | Open BasisType of the element for elements which have both types |
| [CBTYPE] | Closed BasisType of the element for elements which have both types |
+521 -60
View File
@@ -2352,52 +2352,83 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
}
}
void GridFunction::ProjectCoefficient(Coefficient &coeff)
void GridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
{
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
DofTransformation doftrans;
Array<int> vdofs;
Vector vals;
if (delta_c == NULL)
{
if (fes->GetNURBSext() == NULL)
{
Array<int> vdofs;
Vector vals;
for (int i = 0; i < fes->GetNE(); i++)
switch (type)
{
fes->GetElementVDofs(i, vdofs, doftrans);
vals.SetSize(vdofs.Size());
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
doftrans.TransformPrimal(vals);
SetSubVector(vdofs, vals);
case ProjectType::ELEMENT_L2:
ProjectCoefficientElementL2(coeff);
return;
case ProjectType::GLOBAL_L2:
ProjectCoefficientGlobalL2(coeff);
return;
default:
for (int i = 0; i < fes->GetNE(); i++)
{
fes->GetElementVDofs(i, vdofs, doftrans);
vals.SetSize(vdofs.Size());
fes->GetFE(i)->Project(coeff, *fes->GetElementTransformation(i), vals);
doftrans.TransformPrimal(vals);
SetSubVector(vdofs, vals);
}
}
}
else
{
// Define and assemble linear form
LinearForm b(fes);
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
b.Assemble();
switch (type)
{
case ProjectType::DEFAULT:
case ProjectType::ELEMENT_L2:
ProjectCoefficientElementL2(coeff);
return;
case ProjectType::GLOBAL_L2:
ProjectCoefficientGlobalL2(coeff);
return;
case ProjectType::ELEMENT:
constexpr real_t signal = std::numeric_limits<real_t>::min();
// Define and assemble bilinear form
BilinearForm a(fes);
a.AddDomainIntegrator(new MassIntegrator());
a.Assemble();
for (int i = 0; i < fes->GetNE(); i++)
{
fes->GetElementVDofs(i, vdofs, doftrans);
vals.SetSize(vdofs.Size());
vals = signal;
// Set solver and preconditioner
SparseMatrix A(a.SpMat());
GSSmoother prec(A);
CGSolver cg;
cg.SetOperator(A);
cg.SetPreconditioner(prec);
cg.SetRelTol(1e-12);
cg.SetMaxIter(1000);
cg.SetPrintLevel(0);
fes->GetFE(i)->Project(coeff,
*fes->GetElementTransformation(i),
vals);
doftrans.TransformPrimal(vals);
// Solve and get solution
*this = 0.0;
cg.Mult(b,*this);
// Remove undefined dofs
// The knot location (either Botella, Demko or Greville point)
// where the NURBS dof are evaluated might fall outside of the
// domain of the element. In that case the value is not set, and
// the value remains the signal value.
int s = 0;
for (int ii = 0; ii < vals.Size(); ii++)
{
if (vals[ii] != signal)
{
vdofs[s] = vdofs[ii];
vals(s) = vals(ii);
s++;
}
}
vdofs.SetSize(s);
vals.SetSize(s);
// Add reduced dofs to global vector
SetSubVector(vdofs, vals);
}
}
}
}
else
@@ -2410,6 +2441,167 @@ void GridFunction::ProjectCoefficient(Coefficient &coeff)
}
}
void GridFunction::ProjectCoefficientGlobalL2(Coefficient &coeff, real_t rtol,
int iter)
{
// Define and assemble linear form
LinearForm b(fes);
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
b.Assemble();
// Define and assemble bilinear form
BilinearForm a(fes);
a.AddDomainIntegrator(new MassIntegrator());
a.Assemble();
// Set solver and preconditioner
SparseMatrix A(a.SpMat());
GSSmoother prec(A);
CGSolver cg;
cg.SetOperator(A);
cg.SetPreconditioner(prec);
cg.SetRelTol(rtol);
cg.SetMaxIter(iter);
cg.SetPrintLevel(0);
// Solve and get solution
*this = 0.0;
cg.Mult(b,*this);
}
void GridFunction::ProjectCoefficientElementL2(Coefficient &coeff)
{
Vector Va;
ProjectCoefficientElementL2_(coeff, *this, Va);
(*this) /= Va;
}
void GridFunction::ProjectCoefficientElementL2_(Coefficient &coeff,
Vector &x, Vector &Va)
{
DofTransformation doftrans;
Array<int> vdofs;
Vector shape,shape2, elvect, elwght;
DenseMatrix elmat;
Va.SetSize(fes->GetNDofs() );
x.SetSize(fes->GetNDofs() );
Va = 0.0;
x = 0.0;
if (fes->GetNURBSext() == NULL)
{
for (int e = 0; e < fes->GetNE(); e++)
{
fes->GetElementDofs (e, vdofs, doftrans);
ElementTransformation &tr = *fes -> GetElementTransformation (e);
const FiniteElement &el = *fes->GetFE(e);
int dof = el.GetDof();
shape.SetSize(dof);
elvect.SetSize(dof);
elwght.SetSize(dof);
elmat.SetSize(dof,dof);
elvect = 0.0;
elwght = 0.0;
elmat = 0.0;
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
2 * el.GetOrder() + 1);
// Element vector & weight
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
tr.SetIntPoint (&ip);
real_t wght = ip.weight*tr.Weight();
real_t val = coeff.Eval(tr, ip);
el.CalcPhysShape(tr, shape);
elvect.Add(wght * val, shape);
elwght.Add(wght, shape);
AddMult_a_VVt(wght, shape, elmat);
}
// Solve
if (!LinearSolve(elmat, elvect.GetData(),1e-12))
{
MFEM_WARNING("Error in inverting element local matrix");
}
// Scale
elvect *= elwght;
// Add reduced dofs to global vector
x.AddElementVector(vdofs, elvect);
Va.AddElementVector(vdofs, elwght);
}
}
else
{
for (int e = 0; e < fes->GetNE(); e++)
{
fes->GetElementDofs (e, vdofs, doftrans);
ElementTransformation &tr = *fes -> GetElementTransformation (e);
const FiniteElement &el = *fes->GetFE(e);
int dof = el.GetDof();
int dim = el.GetDim();
int p = el.GetOrder();
L2_FECollection fe_coll(p, dim);
//H1_FECollection fe_coll(p, dim, BasisType::Positive);
const FiniteElement &el2 = *fe_coll.FiniteElementForGeometry(el.GetGeomType());
MFEM_ASSERT(el2.GetDof() == dof, "Element dofs do not match.");
shape.SetSize(dof);
shape2.SetSize(dof);
elvect.SetSize(dof);
elwght.SetSize(dof);
elmat.SetSize(dof,dof);
elvect = 0.0;
elwght = 0.0;
elmat = 0.0;
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
2 * el.GetOrder() + 1);
// Element vector & weight
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
tr.SetIntPoint (&ip);
real_t wght = ip.weight*tr.Weight();
real_t val = coeff.Eval(tr, ip);
el.CalcPhysShape(tr, shape);
el2.CalcPhysShape(tr, shape2);
elvect.Add(wght * val, shape2);
elwght.Add(wght, shape);
AddMult_a_VVt(wght, shape2, elmat);
}
// Solve
if (!LinearSolve(elmat, elvect.GetData(),1e-12))
{
MFEM_WARNING("Error in inverting element local matrix 2");
}
// Map to NURBS
DenseMatrix I;
el2.Project(el,tr,I);
if (!LinearSolve(I, elvect.GetData(),1e-32))
{
MFEM_WARNING("Error in inverting element local matrix 3");
}
// Scale
elvect *= elwght;
// Add reduced dofs to global vector
x.AddElementVector(vdofs, elvect);
Va.AddElementVector(vdofs, elwght);
}
}
}
void GridFunction::ProjectCoefficient(
Coefficient &coeff, Array<int> &dofs, int vd)
{
@@ -2434,49 +2626,318 @@ void GridFunction::ProjectCoefficient(
}
}
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff)
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
ProjectType type)
{
Array<int> vdofs;
Vector vals;
DofTransformation doftrans;
if (fes->GetNURBSext() == NULL)
{
int i;
Array<int> vdofs;
Vector vals;
for (i = 0; i < fes->GetNE(); i++)
switch (type)
{
fes->GetElementVDofs(i, vdofs, doftrans);
vals.SetSize(vdofs.Size());
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
doftrans.TransformPrimal(vals);
SetSubVector(vdofs, vals);
case ProjectType::ELEMENT_L2:
ProjectCoefficientElementL2(vcoeff);
return;
case ProjectType::GLOBAL_L2:
ProjectCoefficientGlobalL2(vcoeff);
return;
default:
for (int i = 0; i < fes->GetNE(); i++)
{
fes->GetElementVDofs(i, vdofs, doftrans);
vals.SetSize(vdofs.Size());
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
doftrans.TransformPrimal(vals);
SetSubVector(vdofs, vals);
}
}
}
else
{
// Define and assemble linear form
LinearForm b(fes);
switch (type)
{
case ProjectType::DEFAULT:
case ProjectType::ELEMENT_L2:
ProjectCoefficientElementL2(vcoeff);
return;
case ProjectType::GLOBAL_L2:
ProjectCoefficientGlobalL2(vcoeff);
return;
case ProjectType::ELEMENT:
constexpr real_t signal = std::numeric_limits<real_t>::min();
for (int i = 0; i < fes->GetNE(); i++)
{
fes->GetElementVDofs(i, vdofs, doftrans);
vals.SetSize(vdofs.Size());
vals = signal;
fes->GetFE(i)->Project(vcoeff, *fes->GetElementTransformation(i), vals);
doftrans.TransformPrimal(vals);
// Remove undefined dofs
// The knot location (either Botella, Demko or Greville point)
// where the NURBS dof are evaluated might fall outside of the
// domain of the element. In that case the value is not set, and
// the value remains the signal value.
int s = 0;
for (int ii = 0; ii < vals.Size(); ii++)
{
if (vals[ii] != signal)
{
vdofs[s] = vdofs[ii];
vals(s) = vals(ii);
s++;
}
}
vdofs.SetSize(s);
vals.SetSize(s);
// Add reduced dofs to global vector
SetSubVector(vdofs, vals);
}
}
}
}
void GridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
real_t rtol, int iter)
{
// Define and assemble linear form
LinearForm b(fes);
BilinearForm a(fes);
if (fes->GetTypicalFE()->GetRangeType() == mfem::FiniteElement::VECTOR)
{
b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(vcoeff));
b.Assemble();
// Define and assemble bilinear form
BilinearForm a(fes);
a.AddDomainIntegrator(new VectorFEMassIntegrator());
a.Assemble();
}
else
{
b.AddDomainIntegrator(new VectorDomainLFIntegrator(vcoeff));
a.AddDomainIntegrator(new VectorMassIntegrator());
}
a.Assemble();
b.Assemble();
// Set solver and preconditioner
SparseMatrix A(a.SpMat());
GSSmoother prec(A);
CGSolver cg;
cg.SetOperator(A);
cg.SetPreconditioner(prec);
cg.SetRelTol(1e-12);
cg.SetMaxIter(1000);
cg.SetPrintLevel(0);
// Set solver and preconditioner
SparseMatrix A(a.SpMat());
GSSmoother prec(A);
CGSolver cg;
cg.SetOperator(A);
cg.SetPreconditioner(prec);
cg.SetRelTol(rtol);
cg.SetMaxIter(iter);
cg.SetPrintLevel(0);
// Solve and get solution
*this = 0.0;
cg.Mult(b,*this);
// Solve and get solution
*this = 0.0;
cg.Mult(b,*this);
}
void GridFunction::ProjectCoefficientElementL2_(VectorCoefficient &vcoeff,
Vector &x, Vector &Va)
{
DofTransformation doftrans;
Array<int> vdofs;
Vector shapel2, elvect, elwght, val;
DenseMatrix shape, elmat;
Va.SetSize(Size());
x.SetSize(Size());
Va = 0.0;
x = 0.0;
if (fes->GetNURBSext() == NULL)
{
for (int e = 0; e < fes->GetNE(); e++)
{
fes->GetElementVDofs (e, vdofs, doftrans);
ElementTransformation &tr = *fes -> GetElementTransformation (e);
const FiniteElement &el = *fes->GetFE(e);
int dof = el.GetDof();
int dim = el.GetRangeDim();
shape.SetSize(dof,dim);
shapel2.SetSize(dof);
elvect.SetSize(dof);
elwght.SetSize(dof);
elmat.SetSize(dof,dof);
elvect = 0.0;
elwght = 0.0;
elmat = 0.0;
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
2 * el.GetOrder() + 1);
// Element vector & weight
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
tr.SetIntPoint (&ip);
real_t wght = ip.weight*tr.Weight();
vcoeff.Eval(val, tr, ip);
val *= wght;
el.CalcPhysVShape(tr, shape);
shape.AddMult (val, elvect);
AddMult_a_AAt(wght, shape, elmat);
shape.GetRowl2(shapel2);
elwght.Add(wght, shapel2);
}
// Solve
if (!LinearSolve(elmat, elvect.GetData(),1e-12))
{
MFEM_WARNING("Error in inverting element local matrix");
}
// Scale
elvect *= elwght;
// Add to global vector
x.AddElementVector(vdofs, elvect);
// Add to weight vector -- no need for an orientation
for (int i = 0; i < vdofs.Size(); i++)
{
vdofs[i] = FiniteElementSpace::DecodeDof(vdofs[i]);
}
Va.AddElementVector(vdofs, elwght);
}
}
else
{
DenseMatrix partelmat;
Vector shape2;
if (fes->GetTypicalFE()->GetOrder() >= 6 )
{
MFEM_WARNING("This project is not stable for"
"NURBS VectorFE with order >= 5");
}
for (int e = 0; e < fes->GetNE(); e++)
{
fes->GetElementVDofs (e, vdofs, doftrans);
ElementTransformation &tr = *fes -> GetElementTransformation (e);
const FiniteElement &el = *fes->GetFE(e);
int dof = el.GetDof();
int dim = el.GetRangeDim();
int p = el.GetOrder();
L2_FECollection fe_coll(p, dim);
const FiniteElement &el2 = *fe_coll.FiniteElementForGeometry(el.GetGeomType());
int dof2 = el2.GetDof();
MFEM_ASSERT(dof2*dim >= dof, "Element dofs do not match.");
shape2.SetSize(dof2);
shape.SetSize(dof,dim);
shapel2.SetSize(dof);
elvect.SetSize(dof2*dim);
elwght.SetSize(dof);
elmat.SetSize(dof2*dim,dof2*dim);
partelmat.SetSize(dof2,dof2);
elvect = 0.0;
elwght = 0.0;
elmat = 0.0;
const IntegrationRule &ir = IntRules.Get(el.GetGeomType(),
2 * el.GetOrder() + 1);
// Element vector & weight
for (int i = 0; i < ir.GetNPoints(); i++)
{
const IntegrationPoint &ip = ir.IntPoint(i);
tr.SetIntPoint (&ip);
real_t wght = ip.weight*tr.Weight();
vcoeff.Eval(val, tr, ip);
val *= wght;
el2.CalcPhysShape(tr, shape2);
el.CalcPhysVShape(tr, shape);
for (int k = 0; k < dim; k++)
{
for (int s = 0; s < dof2; s++)
{
elvect(dof2*k+s) += val(k) * shape2(s);
}
}
MultVVt(shape2, partelmat);
partelmat *= wght;
for (int k = 0; k < dim; k++)
{
elmat.AddMatrix(partelmat, dof2*k, dof2*k);
}
shape.GetRowl2(shapel2);
elwght.Add(wght, shapel2);
}
// Solve
if (!LinearSolve(elmat, elvect.GetData()))
{
MFEM_WARNING("Error in inverting element local matrix");
}
// Map to NURBS
DenseMatrix I;
el2.Project(el,tr,I);
// LSQ solve
// For higher order NURBS solving this non-square matrix causes issues.
// For Order <=4 the routine seems to work fine.
Vector vec(dof);
DenseMatrix mat(dof, dof);
I.Transpose();
I.Mult(elvect, vec);
MultAAt(I, mat);
if (!LinearSolve(mat, vec.GetData(), 1e-24))
{
mat.TestInversion();
MFEM_WARNING("Error in inverting element local matrix");
}
elvect = vec;
// Scale
elvect *= elwght;
// Add to global vector
x.AddElementVector(vdofs, elvect);
// Add to weight vector -- no need for an orientation
for (int i = 0; i < vdofs.Size(); i++)
{
vdofs[i] = FiniteElementSpace::DecodeDof(vdofs[i]);
}
Va.AddElementVector(vdofs, elwght);
}
}
}
void GridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
{
if (fes->GetTypicalFE()->GetRangeType() == mfem::FiniteElement::VECTOR)
{
Vector Va;
ProjectCoefficientElementL2_(vcoeff, *this, Va);
(*this) /= Va;
}
else
{
Array<int> vdofs(fes->GetNDofs());
Vector x, Va;
VectorComponentCoefficient coeff(vcoeff,
0); // 0 to ensure we have a valid object
for (int v = 0; v < VectorDim(); v++)
{
coeff.SetComponent(v);
ProjectCoefficientElementL2_(coeff, x, Va);
x /= Va;
fes->GetVDofs(v, vdofs);
SetSubVector(vdofs, x);
}
}
}
+71 -7
View File
@@ -27,6 +27,24 @@
namespace mfem
{
/** This enumerated type describes the three main projection types:
- ELEMENT, assigns the degree of freedom per element, as specified in the
specific element
- GLOBAL_L2, solves a global L2 projection
- ELEMENT_L2, solves a element level L2 projection. Inter element
connectivity is dealt with similar as in:
Bezier-Projection : A unified approach for local projection and
quadrature-free refinement and coarsening of NURBS and T-splines with
particular application to isogeometric design and analysis
[CMAME (284) 2015 pg 55-105]
- DEFAULT, for NURBS spaces this is ELEMENT_L2, while for all other spaces
this ELEMENT.
Note 1: ELEMENT_L2 also works for non NURBS elements
Note 2: For NURBS elements the ELEMENT projection gives results without
over and undershoots. However, the gradient near the boundary does not
converge.*/
enum class ProjectType { DEFAULT, ELEMENT, GLOBAL_L2, ELEMENT_L2 };
/// Class for grid function - Vector with associated FE space.
class GridFunction : public Vector
{
@@ -66,13 +84,17 @@ protected:
degree of freedom. */
void ProjectDiscCoefficient(VectorCoefficient &coeff, Array<int> &dof_attr);
/** Helper function for ProjectCoefficientElementL2 */
void ProjectCoefficientElementL2_(Coefficient &coeff, Vector &sol, Vector &Va);
void ProjectCoefficientElementL2_(VectorCoefficient &vcoeff, Vector &sol,
Vector &Va);
/// Loading helper.
void LegacyNCReorder();
void Destroy();
public:
GridFunction() { fes = NULL; fec_owned = NULL; fes_sequence = 0; UseDevice(true); }
/// Copy constructor. The internal true-dof vector #t_vec is not copied.
@@ -84,6 +106,10 @@ public:
GridFunction(FiniteElementSpace *f) : Vector(f->GetVSize())
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
/// Same as above but specify the memory type
GridFunction(FiniteElementSpace *f, MemoryType mt) : Vector(f->GetVSize(), mt)
{ fes = f; fec_owned = NULL; fes_sequence = f->GetSequence(); UseDevice(true); }
/// Construct a GridFunction using previously allocated array @a data.
/** The GridFunction does not assume ownership of @a data which is assumed to
be of size at least `f->GetVSize()`. Similar to the Vector constructor
@@ -420,9 +446,30 @@ public:
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
projection computation depends on the choice of the FiniteElementSpace
#fes. Note that this is usually interpolation at the degrees of freedom
in each element (not L2 projection). For NURBS spaces these degrees of
freedom are not available and L2 projection is resorted to as fallback. */
virtual void ProjectCoefficient(Coefficient &coeff);
in each element (not L2 projection). For elements without a projection
member function one could use ProjectCoefficientGlobalL2 instead.
NOTE: For parallel simulations with NURBS elements some dofs might
not be defined, if the evaluation point does not reside on this rank.
If that is the case it is defined on another rank, and the issue is
rectified with the appropriate communication, see in ParGridFunction.
*/
virtual void ProjectCoefficient(Coefficient &coeff,
ProjectType type = ProjectType::DEFAULT);
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
projection is a global L2 projection. This routine can be used a
fallback for elements without a projection member function.*/
virtual void ProjectCoefficientGlobalL2(Coefficient &coeff,
real_t rtol = 1e-12,
int iter = 1000);
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
projection is an element local L2 projection, with an appropriate
weighting for Dofs that are shared between elements. Inspired on
Bezier-Projection [CMAME (284) 2015 pg 55-105]
This routine can be used a fallback for elements without a projection
member function.*/
virtual void ProjectCoefficientElementL2(Coefficient &coeff);
/** @brief Project @a coeff Coefficient to @a this GridFunction, using one
element for each degree of freedom in @a dofs and nodal interpolation on
@@ -432,9 +479,26 @@ public:
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction. The
projection computation depends on the choice of the FiniteElementSpace
#fes. Note that this is usually interpolation at the degrees of freedom
in each element (not L2 projection). For NURBS spaces these degrees of
freedom are not available and L2 projection is resorted to as fallback. */
void ProjectCoefficient(VectorCoefficient &vcoeff);
in each element (not L2 projection). For elements without a projection
member function one could use ProjectCoefficientGlobalL2 instead.
NOTE: For parallel simulations with NURBS elements some dofs might
not be defined, if the evaluation point does not reside on this rank.
If that is the case it is defined on another rank, and the issue is
rectified with the appropriate communication, see in ParGridFunction.*/
virtual void ProjectCoefficient(VectorCoefficient &vcoeff,
ProjectType type = ProjectType::DEFAULT);
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
projection is a global L2 projection. This routine can be used a
fallback for elements without a projection member function.*/
virtual void ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
real_t rtol = 1e-12,
int iter = 1000);
/** @brief Project @a coeff Coefficient to @a this GridFunction. The
projection is a global L2 projection. This routine can be used a
fallback for elements without a projection member function.*/
virtual void ProjectCoefficientElementL2(VectorCoefficient &vcoeff);
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction, using
one element for each degree of freedom in @a dofs and nodal interpolation
+147
View File
@@ -1054,7 +1054,154 @@ void DGElasticityDirichletLFIntegrator::AssembleRHSElementVect(
}
}
void SlidingElasticityLFIntegrator::AssembleRHSElementVect(
const FiniteElement &el, ElementTransformation &Tr, Vector &elvect)
{
mfem_error("SlidingElasticityLFIntegrator::AssembleRHSElementVect");
}
void SlidingElasticityLFIntegrator::AssembleRHSElementVect(
const FiniteElement &el, FaceElementTransformations &Tr, Vector &elvect)
{
MFEM_ASSERT(Tr.Elem2No < 0, "interior boundary is not supported");
#ifdef MFEM_THREAD_SAFE
Vector shape;
DenseMatrix dshape;
DenseMatrix adjJ;
DenseMatrix dshape_ps;
Vector nor;
Vector dshape_dn;
Vector dshape_du;
real_t g_val;
Vector nt_val;
#endif
const int dim = el.GetDim();
const int ndofs = el.GetDof();
const int nvdofs = dim*ndofs;
elvect.SetSize(nvdofs);
elvect = 0.0;
adjJ.SetSize(dim);
shape.SetSize(ndofs);
dshape.SetSize(ndofs, dim);
dshape_ps.SetSize(ndofs, dim);
nor.SetSize(dim);
dshape_dn.SetSize(ndofs);
dshape_du.SetSize(ndofs);
nt_val.SetSize(dim);
const IntegrationRule *ir = IntRule;
if (ir == NULL)
{
const int order = 2*el.GetOrder(); // <-----
ir = &IntRules.Get(Tr.GetGeometryType(), order);
}
for (int pi = 0; pi < ir->GetNPoints(); ++pi)
{
const IntegrationPoint &ip = ir->IntPoint(pi);
// Set the integration point in the face and the neighboring element
Tr.SetAllIntPoints(&ip);
// Access the neighboring element's integration point
const IntegrationPoint &eip = Tr.GetElement1IntPoint();
el.CalcShape(eip, shape);
el.CalcDShape(eip, dshape);
CalcAdjugate(Tr.Elem1->Jacobian(), adjJ);
Mult(dshape, adjJ, dshape_ps);
if (dim == 1)
{
nor(0) = 2*eip.x - 1.0;
}
else
{
CalcOrtho(Tr.Jacobian(), nor);
}
if (!nt)
{
// Set nt to the unit normal vector if not provided
nt_val = nor;
nt_val /= nt_val.Norml2();
}
else
{
// Evaluate the vector field using the face transformation.
nt->Eval(nt_val, Tr, ip);
}
// Evaluate the Dirichlet b.c. using the face transformation.
g_val = g->Eval(Tr, ip);
real_t WL, WM, jcoef;
{
const real_t W = ip.weight / Tr.Elem1->Weight();
WL = W * lambda->Eval(*Tr.Elem1, eip);
WM = W * mu->Eval(*Tr.Elem1, eip);
jcoef = kappa * (WL + 2.0*WM) * (nor*nor);
dshape_ps.Mult(nor, dshape_dn);
dshape_ps.Mult(nt_val, dshape_du);
}
// alpha < g, (lambda div(v) I + mu (grad(v) + grad(v)^T)) n . ñ > +
// + kappa < h^{-1} (lambda + 2 mu) g, v . ñ >
// i = idof + ndofs * im
// v_phi(i,d) = delta(im,d) phi(idof)
// div(v_phi(i)) = dphi(idof,im)
// (grad(v_phi(i)))(k,l) = delta(im,k) dphi(idof,l)
//
// term 1:
// alpha < g, lambda div(v_phi(i)) n . ñ > =
// alpha lambda g div(v_phi(i)) (n.ñ) =
// alpha lambda g dphi(idof,im) (n.ñ) --> quadrature -->
// ip.weight/det(J1) alpha lambda g (nor.ñ) dshape_ps(idof,im) =
// alpha * WL * g_val * (nor*nt_val) * dshape_ps(idof,im)
// term 2:
// alpha < g, mu grad(v_phi(i)) n . ñ > =
// alpha mu g ñ^T grad(v_phi(i)) n =
// alpha mu g ñ(k) delta(im,k) dphi(idof,l) n(l) =
// alpha mu g ñ(im) dphi(idof,l) n(l) --> quadrature -->
// ip.weight/det(J1) alpha mu ñ(im) g dshape_ps(idof,l) nor(l) =
// alpha * WM * g_val * nt_val(im) * dshape_dn(idof)
// term 3:
// alpha < g, mu (grad(v_phi(i)))^T n . ñ > =
// alpha mu g n^T grad(v_phi(i)) ñ =
// alpha mu g n(k) delta(im,k) dphi(idof,l) ñ(l) =
// alpha mu g n(im) dphi(idof,l) ñ(l) --> quadrature -->
// ip.weight/det(J1) alpha mu g nor(im) dshape_ps(idof,l) ñ(l) =
// alpha * WM * g_val * nor(im) * dshape_du(idof)
// term j:
// < kappa h^{-1} (lambda + 2 mu) g, ñ . v_phi(i) > =
// kappa/h (lambda + 2 mu) g ñ(k) v_phi(i,k) =
// kappa/h (lambda + 2 mu) g ñ(k) delta(im,k) phi(idof) =
// kappa/h (lambda + 2 mu) g ñ(im) phi(idof) --> quadrature -->
// [ 1/h = |nor|/det(J1) ]
// ip.weight/det(J1) |nor|^2 (lambda + 2 mu) kappa g ñ(im) phi(idof) =
// jcoef * g_val * nt_val(im) * shape(idof)
WM *= alpha;
const real_t t1 = alpha * WL * g_val * (nor*nt_val);
for (int im = 0, i = 0; im < dim; ++im)
{
const real_t t2 = WM * g_val * nt_val(im);
const real_t t3 = WM * g_val * nor(im);
const real_t tj = jcoef * g_val * nt_val(im);
for (int idof = 0; idof < ndofs; ++idof, ++i)
{
elvect(i) += (t1*dshape_ps(idof,im) + t2*dshape_dn(idof) +
t3*dshape_du(idof) + tj*shape(idof));
}
}
}
}
void WhiteGaussianNoiseDomainLFIntegrator::AssembleRHSElementVect
(const FiniteElement &el,
+56
View File
@@ -646,6 +646,62 @@ public:
using LinearFormIntegrator::AssembleRHSElementVect;
};
/** Boundary linear form integrator for imposing non-zero Dirichlet boundary
conditions, in a Nitsche elasticity formulation. Specifically, the linear
form is given by
$$
\begin{split}
b(v) &:= \alpha \int_\Gamma (\lambda\, \mathrm{div}(v)\, I + \mu (\nabla v
+ \nabla v^{\mathrm{T}}))\, n \cdot \tilde{n}\, g\, dS + \kappa \int_\Gamma
h^{-1} (\lambda + 2\mu) (v \cdot \tilde{n})\, g\, dS
\end{split}
$$
where $g$ is the given Dirichlet data, $n$ is the unit normal, $\tilde{n}$ is
a unit vector field, and $\alpha = \pm 1$, $\kappa > 0$ are the Nitsche
parameters. The parameters $\lambda$ and $\mu$ should match the parameters
with the same names used in the bilinear form integrator,
SlidingElasticityIntegrator.
*/
class SlidingElasticityLFIntegrator : public LinearFormIntegrator
{
protected:
Coefficient *g;
VectorCoefficient *nt;
Coefficient *lambda, *mu;
real_t alpha, kappa;
#ifndef MFEM_THREAD_SAFE
Vector shape;
DenseMatrix dshape;
DenseMatrix adjJ;
DenseMatrix dshape_ps;
Vector nor;
Vector dshape_dn;
Vector dshape_du;
real_t g_val;
Vector nt_val;
#endif
public:
SlidingElasticityLFIntegrator(Coefficient &g_,
Coefficient &lambda_, Coefficient &mu_,
real_t kappa_)
: g(&g_), nt(NULL), lambda(&lambda_), mu(&mu_), alpha(-1.0), kappa(kappa_) {}
SlidingElasticityLFIntegrator(Coefficient &g_, VectorCoefficient &nt_,
Coefficient &lambda_, Coefficient &mu_,
real_t alpha_, real_t kappa_)
: g(&g_), nt(&nt_), lambda(&lambda_), mu(&mu_), alpha(alpha_), kappa(kappa_) {}
void AssembleRHSElementVect(const FiniteElement &el,
ElementTransformation &Tr,
Vector &elvect) override;
void AssembleRHSElementVect(const FiniteElement &el,
FaceElementTransformations &Tr,
Vector &elvect) override;
using LinearFormIntegrator::AssembleRHSElementVect;
};
/** Class for spatial white Gaussian noise integration.
+16 -2
View File
@@ -23,6 +23,8 @@ class BatchedLOR_DG : BatchedLORKernel
{
IntegrationRule ir_face; ///< Collocated Gauss-Lobatto face quadrature rule.
real_t kappa; ///< DG penalty parameter.
bool has_bdr_integ; ///< Is there a boundary integrator?
const Array<int> *bdr_markers; ///< Boundary integrator markers.
public:
template <int ORDER, int SDIM> void Assemble2D();
template <int ORDER> void Assemble3D();
@@ -38,8 +40,7 @@ public:
ProjectLORCoefficient<MassIntegrator>(a, c1);
ProjectLORCoefficient<DiffusionIntegrator>(a, c2);
auto *integ = GetInteriorFaceIntegrator<DGDiffusionIntegrator>(a);
if (integ)
if (auto *integ = GetInteriorFaceIntegrator<DGDiffusionIntegrator>(a))
{
kappa = integ->GetPenaltyParameter();
}
@@ -47,6 +48,19 @@ public:
{
kappa = 0.0;
}
has_bdr_integ = false;
auto *bdr_face_integs = a.GetBFBFI();
for (int i = 0; i < bdr_face_integs->Size(); ++i)
{
if (auto *integ = dynamic_cast<DGDiffusionIntegrator*>((*bdr_face_integs)[i]))
{
kappa = integ->GetPenaltyParameter();
bdr_markers = (*a.GetBFBFI_Marker())[i];
has_bdr_integ = true;
break;
}
}
}
/// @brief Compute and return the face info array.
+18
View File
@@ -22,9 +22,13 @@ namespace mfem
Array<int> BatchedLOR_DG::GetFaceInfo() const
{
Mesh &mesh = *fes_ho.GetMesh();
const Array<int> &bdr_face_attrs = mesh.GetBdrFaceAttributes();
const int nf = mesh.GetNumFaces();
Array<int> face_info(nf * 6); // (e0, f0, o0, e1, f1, o1)
auto h_face_info = Reshape(face_info.HostWrite(), 6, nf);
int bdr_face_counter = 0;
for (int f = 0; f < nf; ++f)
{
auto finfo = mesh.GetFaceInformation(f);
@@ -43,6 +47,19 @@ Array<int> BatchedLOR_DG::GetFaceInfo() const
h_face_info(4, f) = -1;
h_face_info(5, f) = -1;
}
if (finfo.IsBoundary())
{
// Check if Neumann boundary; skip these when adding boundary penalties
const int bdr_attr = bdr_face_attrs[bdr_face_counter];
if (!has_bdr_integ || (bdr_markers && !(*bdr_markers)[bdr_attr - 1]))
{
h_face_info(0, f) = -1;
h_face_info(1, f) = -1;
h_face_info(2, f) = -1;
}
bdr_face_counter += 1;
}
}
return face_info;
}
@@ -144,6 +161,7 @@ void BatchedLOR_DG::AssembleFaceTerms()
{
const int f_0 = d_face_info(1, f);
const int f_1 = d_face_info(4, f);
if (f_0 < 0) { return; } // Skip Neumann boundary faces
const int nsides = (f_1 >= 0) ? 2 : 1;
for (int el_i = 0; el_i < nsides; ++el_i)
{
+1 -4
View File
@@ -78,10 +78,7 @@ template <int Dim>
void BuildBoxes(const Mesh &mesh,
std::vector<::moonolith::AABB<Dim, double>> &element_boxes)
{
#ifndef NDEBUG
const int dim = mesh.Dimension();
assert(dim == Dim);
#endif
MFEM_ASSERT(mesh.Dimension() == Dim, "Mesh and box dimensions mismatched");
element_boxes.resize(mesh.GetNE());
DenseMatrix pts;
+152 -2
View File
@@ -543,13 +543,22 @@ void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
}
}
void ParGridFunction::ProjectCoefficient(Coefficient &coeff)
void ParGridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
{
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
if (delta_c == NULL)
{
GridFunction::ProjectCoefficient(coeff);
(*this) = std::numeric_limits<real_t>::min();
GridFunction::ProjectCoefficient(coeff,type);
// Accumulate for all vdofs.
if (pfes->GetNURBSext())
{
GroupCommunicator &gcomm = pfes->GroupComm();
gcomm.Reduce<real_t>(data, GroupCommunicator::Max);
gcomm.Bcast<real_t>(data);
}
}
else
{
@@ -565,6 +574,147 @@ void ParGridFunction::ProjectCoefficient(Coefficient &coeff)
}
}
void ParGridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
ProjectType type)
{
GridFunction::ProjectCoefficient(vcoeff, type);
// Accumulate for all vdofs.
if (pfes->GetNURBSext())
{
GroupCommunicator &gcomm = pfes->GroupComm();
gcomm.Reduce<real_t>(data, GroupCommunicator::Max);
gcomm.Bcast<real_t>(data);
}
}
void ParGridFunction::ProjectCoefficientGlobalL2(Coefficient &coeff,
real_t rtol,
int iter)
{
// Define and assemble linear form
ParLinearForm b(pfes);
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
b.Assemble();
// Define and assemble bilinear form
ParBilinearForm a(pfes);
a.AddDomainIntegrator(new MassIntegrator());
a.Assemble();
// Configure solver
OperatorPtr A;
Vector B, X, x(*this);
Array<int> ess_tdof_list;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
Solver *prec = new HypreBoomerAMG;
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(rtol);
cg.SetMaxIter(iter);
cg.SetPrintLevel(0);
cg.SetPreconditioner(*prec);
cg.SetOperator(*A);
cg.Mult(B, X);
a.RecoverFEMSolution(X, b, x);
delete prec;
}
void ParGridFunction::ProjectCoefficientElementL2(Coefficient &coeff)
{
Vector Va;
ProjectCoefficientElementL2_(coeff, *this, Va);
GroupCommunicator &gcomm = pfes->GroupComm();
gcomm.Reduce<real_t>(GetData(), GroupCommunicator::Sum);
gcomm.Bcast<real_t>(GetData());
gcomm.Reduce<real_t>(Va.GetData(), GroupCommunicator::Sum);
gcomm.Bcast<real_t>(Va.GetData());
(*this)/=Va;
}
void ParGridFunction::ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
real_t rtol, int iter)
{
// Define and assemble linear form
ParLinearForm b(pfes);
ParBilinearForm a(pfes);
// Dimension argument to GetRangeType is arbitrary to be 3, could also be 2.
if (fes->FEColl()->GetRangeType(3) == mfem::FiniteElement::VECTOR)
{
b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(vcoeff));
a.AddDomainIntegrator(new VectorFEMassIntegrator());
}
else
{
b.AddDomainIntegrator(new VectorDomainLFIntegrator(vcoeff));
a.AddDomainIntegrator(new VectorMassIntegrator());
}
b.Assemble();
a.Assemble();
// Configure solver
OperatorPtr A;
Vector B, X, x(*this);
x = 0.0;
Array<int> ess_tdof_list;
a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);
Solver *prec = new HypreBoomerAMG;
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(rtol);
cg.SetMaxIter(iter);
cg.SetPrintLevel(0);
cg.SetPreconditioner(*prec);
cg.SetOperator(*A);
cg.Mult(B, X);
a.RecoverFEMSolution(X, b, x);
x.Print();
delete prec;
}
void ParGridFunction::ProjectCoefficientElementL2(VectorCoefficient &vcoeff)
{
if (fes->GetTypicalFE()->GetRangeType() == mfem::FiniteElement::VECTOR)
{
Vector Va;
ProjectCoefficientElementL2_(vcoeff, *this, Va);
GroupCommunicator &gcomm = pfes->GroupComm();
gcomm.Reduce<real_t>(GetData(), GroupCommunicator::Sum);
gcomm.Bcast<real_t>(GetData());
gcomm.Reduce<real_t>(Va.GetData(), GroupCommunicator::Sum);
gcomm.Bcast<real_t>(Va.GetData());
(*this)/=Va;
}
else
{
Array<int> vdofs(fes->GetNDofs());
Vector x, Va, gVa(Size());
VectorComponentCoefficient coeff(vcoeff,0);
*this = 0.0;
gVa = 0.0;
for (int v = 0; v < VectorDim(); v++)
{
coeff.SetComponent(v);
ProjectCoefficientElementL2_(coeff, x, Va);
fes->GetVDofs(v, vdofs);
SetSubVector(vdofs, x);
gVa.SetSubVector(vdofs, Va);
}
GroupCommunicator &gcomm = pfes->GroupComm();
gcomm.Reduce<real_t>(GetData(), GroupCommunicator::Sum);
gcomm.Bcast<real_t>(GetData());
gcomm.Reduce<real_t>(gVa.GetData(), GroupCommunicator::Sum);
gcomm.Bcast<real_t>(gVa.GetData());
*this /= gVa;
}
}
void ParGridFunction::ProjectDiscCoefficient(VectorCoefficient &coeff)
{
// local maximal element attribute for each dof
+21 -1
View File
@@ -72,6 +72,10 @@ public:
ParGridFunction(ParFiniteElementSpace *pf) : GridFunction(pf), pfes(pf) { }
/// Same as above but specify the device memory type
ParGridFunction(ParFiniteElementSpace *pf, MemoryType mt) :
GridFunction(pf, mt), pfes(pf) { }
/// Construct a ParGridFunction using previously allocated array @a data.
/** The ParGridFunction does not assume ownership of @a data which is assumed
to be of size at least `pf->GetVSize()`. Similar to the GridFunction and
@@ -257,7 +261,11 @@ public:
void GetElementDofValues(int el, Vector &dof_vals) const override;
using GridFunction::ProjectCoefficient;
void ProjectCoefficient(Coefficient &coeff) override;
void ProjectCoefficient(Coefficient &coeff,
ProjectType type = ProjectType::DEFAULT) override;
void ProjectCoefficient(VectorCoefficient &vcoeff,
ProjectType type = ProjectType::DEFAULT) override;
using GridFunction::ProjectDiscCoefficient;
/** @brief Project a discontinuous vector coefficient as a grid function on
@@ -282,6 +290,18 @@ public:
void ProjectBdrCoefficientTangent(VectorCoefficient &vcoeff,
const Array<int> &bdr_attr) override;
void ProjectCoefficientGlobalL2(Coefficient &coeff,
real_t rtol = 1e-12,
int iter = 1000) override;
void ProjectCoefficientElementL2(Coefficient &coeff) override;
void ProjectCoefficientGlobalL2(VectorCoefficient &vcoeff,
real_t rtol = 1e-12,
int iter = 1000) override;
void ProjectCoefficientElementL2(VectorCoefficient &vcoeff) override;
/// @brief Returns ||u_ex - u_h||_L1 in parallel for H1 or L2 elements
///
/// @see GridFunction::ComputeL1Error(Coefficient *exsol[],
+25
View File
@@ -22,6 +22,8 @@
#include <unordered_map>
#include <map>
#include <sstream>
#include <iomanip>
namespace mfem
{
@@ -716,6 +718,29 @@ void Device::DeviceMem(size_t *free, size_t *total)
#endif
}
std::string Device::GetUUID(const int device_id)
{
std::stringstream res;
#if defined(MFEM_USE_CUDA)
cudaDeviceProp prop;
MFEM_GPU_CHECK(cudaGetDeviceProperties(&prop, device_id));
for (int i = 0; i < 16; ++i)
{
res << std::setfill('0') << std::setw(2) << std::hex
<< static_cast<unsigned>(prop.uuid.bytes[i]);
}
#elif defined(MFEM_USE_HIP)
hipUUID uuid;
MFEM_GPU_CHECK(hipDeviceGetUuid(&uuid, device_id));
for (int i = 0; i < 16; ++i)
{
res << std::setfill('0') << std::setw(2) << std::hex
<< static_cast<unsigned>(uuid.bytes[i]);
}
#endif
return res.str();
}
int Device::NumMultiprocessors(int dev)
{
#if defined(MFEM_USE_CUDA)
+4
View File
@@ -255,6 +255,10 @@ public:
/// Get the number of available devices (may be called before configuration).
static int GetDeviceCount();
/// Gets a string representation of the GPU UUID.
/// 0 <= @a device_id < GetDeviceCount()
static std::string GetUUID(const int device_id = 0);
/** @brief Return true if any of the backends in the backend mask, @a b_mask,
are allowed. */
/** This method can be used with any of the Backend::Id constants, the
+1 -1
View File
@@ -146,7 +146,7 @@ public:
int *ReadWriteJ(bool on_dev = true) { return J.ReadWrite(on_dev); }
const int *HostReadJ() const { return J.HostRead(); }
int *HostWriteJ() { return J.HostWrite(); }
int *ReadWriteJ() { return J.HostReadWrite(); }
int *HostReadWriteJ() { return J.HostReadWrite(); }
/// Sort the column (TYPE II) indices in each row.
void SortRows();
+29
View File
@@ -1370,6 +1370,35 @@ void DenseMatrix::Getl1Diag(Vector &l) const
}
}
void DenseMatrix::GetRowl1(Vector &l) const
{
l.SetSize(height);
l = 0.0;
for (int j = 0; j < width; ++j)
for (int i = 0; i < height; ++i)
{
l(i) += fabs((*this)(i,j));
}
}
void DenseMatrix::GetRowl2(Vector &l) const
{
l.SetSize(height);
l = 0.0;
for (int j = 0; j < width; ++j)
for (int i = 0; i < height; ++i)
{
l[i] += operator()(i,j)*operator()(i,j);
}
for (int i = 0; i < height; ++i)
{
l[i] = sqrt(l[i]);
}
}
void DenseMatrix::GetRowSums(Vector &l) const
{
l.SetSize(height);
+6 -2
View File
@@ -346,8 +346,12 @@ public:
/// Returns the diagonal of the matrix
void GetDiag(Vector &d) const;
/// Returns the l1 norm of the rows of the matrix v_i = sum_j |a_ij|
void Getl1Diag(Vector &l) const;
/// Compute the row sums of the DenseMatrix
MFEM_DEPRECATED void Getl1Diag(Vector &l) const;
/// Returns the l1 norm of the rows of the matrix v_i = sum_j |a_ij|
void GetRowl1(Vector &l) const;
/// Returns the l2norm of the rows of the DenseMatrix
void GetRowl2(Vector &l) const;
/// Returns the row sums of the DenseMatrix
void GetRowSums(Vector &l) const;
/// Creates n x n diagonal matrix with diagonal elements c
+823 -146
View File
File diff suppressed because it is too large Load Diff
+755 -80
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -1681,6 +1681,13 @@ void HypreParMatrix::GetOffd(SparseMatrix &offd, HYPRE_BigInt* &cmap) const
cmap = A->col_map_offd;
}
void HypreParMatrix::GetOffdColMap(HYPRE_BigInt* &cmap,
HYPRE_Int &num_cols) const
{
cmap = A->col_map_offd;
num_cols = hypre_CSRMatrixNumCols(A->offd);
}
void HypreParMatrix::MergeDiagAndOffd(SparseMatrix &merged)
{
HostRead();
+10
View File
@@ -665,6 +665,8 @@ public:
void GetDiag(SparseMatrix &diag) const;
/// Get the local off-diagonal block. NOTE: 'offd' will not own any data.
void GetOffd(SparseMatrix &offd, HYPRE_BigInt* &cmap) const;
/// Get the global column mapping for the local off-diagonal block.
void GetOffdColMap(HYPRE_BigInt* &cmap, HYPRE_Int &num_cols) const;
/** @brief Get a single SparseMatrix containing all rows from this processor,
merged from the diagonal and off-diagonal blocks stored by the
HypreParMatrix. */
@@ -959,6 +961,14 @@ public:
const Memory<HYPRE_Int> &GetDiagMemoryJ() const { return mem_diag.J; }
const Memory<real_t> &GetDiagMemoryData() const { return mem_diag.data; }
Memory<HYPRE_Int> &GetOffdMemoryI() { return mem_offd.I; }
Memory<HYPRE_Int> &GetOffdMemoryJ() { return mem_offd.J; }
Memory<real_t> &GetOffdMemoryData() { return mem_offd.data; }
const Memory<HYPRE_Int> &GetOffdMemoryI() const { return mem_offd.I; }
const Memory<HYPRE_Int> &GetOffdMemoryJ() const { return mem_offd.J; }
const Memory<real_t> &GetOffdMemoryData() const { return mem_offd.data; }
/// @brief Prints the locally owned rows in parallel. The resulting files can
/// be read with Read_IJMatrix().
void Print(const std::string &fname, HYPRE_Int offi = 0,
+101 -6
View File
@@ -10,6 +10,7 @@
// CONTRIBUTING.md for details.
#include "../general/communication.hpp"
#include "../general/forall.hpp"
#include "operator.hpp"
#include "ode.hpp"
@@ -184,6 +185,23 @@ void ODESolver::Init(TimeDependentOperator &f_)
mem_type = GetMemoryType(f_.GetMemoryClass());
}
void ODESolver::ComputeSlopeFromState(const real_t dt, const Vector &u,
Vector &k)
{
// k currently holds state u(t+dt),
// convert to slope k = du/dt ~= (u(t+dt)-u(t))/dt
const int usz = u.Size();
real_t fac = 1.0/dt;
auto d_u = u.Read();
auto d_k = k.ReadWrite();
mfem::forall(usz, [=] MFEM_HOST_DEVICE (int i)
{
d_k[i] -= d_u[i];
d_k[i] *= fac;
});
}
void ForwardEulerSolver::Init(TimeDependentOperator &f_)
{
ODESolver::Init(f_);
@@ -629,6 +647,10 @@ void AdamsMoultonSolver::Step(Vector &x, real_t &t, real_t &dt)
}
state.ShiftStages();
f->ImplicitSolve(a[0]*dt, x, state[0]);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a[0]*dt, x, state[0]);
}
x.Add(a[0]*dt, state[0]);
t += dt;
}
@@ -661,7 +683,15 @@ void BackwardEulerSolver::Step(Vector &x, real_t &t, real_t &dt)
{
f->SetTime(t + dt);
f->ImplicitSolve(dt, x, k); // solve for k: k = f(x + dt*k, t + dt)
x.Add(dt, k);
if (f->ImplicitVarTypeIsState())
{
x = k; // x = u_{i+1}
}
else
{
x.Add(dt, k);
}
t += dt;
}
@@ -676,7 +706,16 @@ void ImplicitMidpointSolver::Step(Vector &x, real_t &t, real_t &dt)
{
f->SetTime(t + dt/2);
f->ImplicitSolve(dt/2, x, k);
x.Add(dt, k);
if (f->ImplicitVarTypeIsState())
{
x.Neg();
x.Add(2.0, k);
}
else
{
x.Add(dt, k);
}
t += dt;
}
@@ -718,11 +757,19 @@ void SDIRK23Solver::Step(Vector &x, real_t &t, real_t &dt)
// note: with gamma_opt=3, both solve are outside [t,t+dt] since a>1
f->SetTime(t + gamma*dt);
f->ImplicitSolve(gamma*dt, x, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(gamma*dt, x, k);
}
add(x, (1.-2.*gamma)*dt, k, y); // y = x + (1-2*gamma)*dt*k
x.Add(dt/2, k);
f->SetTime(t + (1.-gamma)*dt);
f->ImplicitSolve(gamma*dt, y, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(gamma*dt, y, k);
}
x.Add(dt/2, k);
t += dt;
}
@@ -749,17 +796,29 @@ void SDIRK34Solver::Step(Vector &x, real_t &t, real_t &dt)
f->SetTime(t + a*dt);
f->ImplicitSolve(a*dt, x, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, x, k);
}
add(x, (0.5-a)*dt, k, y);
add(x, (2.*a)*dt, k, z);
x.Add(b*dt, k);
f->SetTime(t + dt/2);
f->ImplicitSolve(a*dt, y, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, y, k);
}
z.Add((1.-4.*a)*dt, k);
x.Add((1.-2.*b)*dt, k);
f->SetTime(t + (1.-a)*dt);
f->ImplicitSolve(a*dt, z, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, z, k);
}
x.Add(b*dt, k);
t += dt;
}
@@ -785,15 +844,27 @@ void SDIRK33Solver::Step(Vector &x, real_t &t, real_t &dt)
f->SetTime(t + a*dt);
f->ImplicitSolve(a*dt, x, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, x, k);
}
add(x, (c-a)*dt, k, y);
x.Add(b*dt, k);
f->SetTime(t + c*dt);
f->ImplicitSolve(a*dt, y, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, y, k);
}
x.Add((1.0-a-b)*dt, k);
f->SetTime(t + dt);
f->ImplicitSolve(a*dt, x, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, x, k);
}
x.Add(a*dt, k);
t += dt;
}
@@ -818,6 +889,10 @@ void TrapezoidalRuleSolver::Step(Vector &x, real_t &t, real_t &dt)
f->SetTime(t + dt);
f->ImplicitSolve(dt/2.0, y, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(0.5*dt, y, k);
}
x.Add(dt/2.0, k);
t += dt;
}
@@ -848,11 +923,19 @@ void ESDIRK32Solver::Step(Vector &x, real_t &t, real_t &dt)
f->SetTime(t + (2.0*a)*dt);
f->ImplicitSolve(a*dt, y, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, y, k);
}
z.Add(b*dt, k);
x.Add(b*dt, k);
f->SetTime(t + dt);
f->ImplicitSolve(a*dt, z, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, z, k);
}
x.Add(a*dt, k);
t += dt;
}
@@ -885,11 +968,19 @@ void ESDIRK33Solver::Step(Vector &x, real_t &t, real_t &dt)
f->SetTime(t + (2.0*a)*dt);
f->ImplicitSolve(a*dt, y, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, y, k);
}
z.Add(b*dt, k);
x.Add(b_2*dt, k);
f->SetTime(t + dt);
f->ImplicitSolve(a*dt, z, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(a*dt, z, k);
}
x.Add(b_3*dt, k);
t += dt;
}
@@ -955,6 +1046,10 @@ void GeneralizedAlphaSolver::Step(Vector &x, real_t &t, real_t &dt)
real_t dt_eff = (gamma*alpha_f/alpha_m)*dt;
f->SetTime(t + alpha_f*dt);
f->ImplicitSolve(dt_eff, y, k);
if (f->ImplicitVarTypeIsState())
{
ComputeSlopeFromState(dt_eff, y, k);
}
// Update x and xdot
x.Add((1.0 - (gamma/alpha_m))*dt, state[0]);
@@ -1116,8 +1211,8 @@ void SecondOrderODESolver::EulerStep(Vector &x, Vector &dxdt, real_t &t,
f->SetTime(t + dt);
f->ImplicitSolve(0.5*dt*dt, dt, x, dxdt, state[0]);
x .Add(0.5*dt*dt, state[0]);
dxdt.Add(dt, state[0]);
x.Add(0.5*dt*dt, state[0]);
dxdt.Add(dt, state[0]);
t += dt;
}
@@ -1203,8 +1298,8 @@ void NewmarkSolver::Step(Vector &x, Vector &dxdt, real_t &t, real_t &dt)
f->SetTime(t + dt);
f->ImplicitSolve(fac3*dt*dt, fac4*dt, x, dxdt, state[0]);
x .Add(fac3*dt*dt, state[0]);
dxdt.Add(fac4*dt, state[0]);
x.Add(fac3*dt*dt, state[0]);
dxdt.Add(fac4*dt, state[0]);
t += dt;
}
+76
View File
@@ -120,6 +120,7 @@ public:
class ODESolver
{
protected:
using ImplicitVariableType = TimeDependentOperator::ImplicitVariableType;
/// Pointer to the associated TimeDependentOperator.
TimeDependentOperator *f; // f(.,t) : R^n --> R^n
MemoryType mem_type;
@@ -192,6 +193,22 @@ public:
/// Returns how many State vectors the ODE requires
virtual int GetStateSize() { return 0; };
///@brief Returns @a true if the ODESolver supports the given
/// #ImplicitVariableType, @a var, and returns @a false otherwise.
///@note Should be overriden in ODESolver that calls TimeDependentOperator::ImplicitSolve().
virtual bool SupportsImplicitVariableType(ImplicitVariableType var) const
{ return false; };
/** @brief Compute the finite-difference slope, @a $\frac{du}{dt} \approx \frac{u(t+dt)-u(t)}{dt}$,
* and store it in @a k.
* @param [in] dt Finite difference step size.
* @param [in] u state vector, @a u(t).
* @param [in,out] k On input, @a k contains the state vector, @a u( @a t+ @a dt).
* On output, @a k contains the computed slope, @a du/dt.
* */
virtual void ComputeSlopeFromState(const real_t dt, const Vector &u,
Vector &k);
// Help info for ODESolver options
static MFEM_EXPORT std::string ExplicitTypes;
static MFEM_EXPORT std::string ImplicitTypes;
@@ -361,6 +378,12 @@ public:
void Init(TimeDependentOperator &f_) override;
void Step(Vector &x, real_t &t, real_t &dt) override;
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
@@ -374,6 +397,12 @@ public:
void Init(TimeDependentOperator &f_) override;
void Step(Vector &x, real_t &t, real_t &dt) override;
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
@@ -395,6 +424,12 @@ public:
void Init(TimeDependentOperator &f_) override;
void Step(Vector &x, real_t &t, real_t &dt) override;
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
@@ -409,6 +444,12 @@ public:
void Init(TimeDependentOperator &f_) override;
void Step(Vector &x, real_t &t, real_t &dt) override;
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
@@ -423,6 +464,12 @@ public:
void Init(TimeDependentOperator &f_) override;
void Step(Vector &x, real_t &t, real_t &dt) override;
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
@@ -437,6 +484,12 @@ public:
void Init(TimeDependentOperator &f_) override;
void Step(Vector &x, real_t &t, real_t &dt) override;
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
@@ -451,6 +504,12 @@ public:
void Init(TimeDependentOperator &f_) override;
void Step(Vector &x, real_t &t, real_t &dt) override;
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
@@ -465,6 +524,12 @@ public:
void Init(TimeDependentOperator &f_) override;
void Step(Vector &x, real_t &t, real_t &dt) override;
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
@@ -490,6 +555,12 @@ public:
ODEStateData& GetState() override { return state; }
const ODEStateData& GetState() const override { return state; }
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
@@ -606,6 +677,11 @@ public:
ODEStateData& GetState() override { return state; }
const ODEStateData& GetState() const override { return state; }
bool SupportsImplicitVariableType(ImplicitVariableType var) const override
{
return (var == ImplicitVariableType::STATE ||
var == ImplicitVariableType::SLOPE);
}
};
/** A 1-stage, 2nd order AM method. */
+37 -3
View File
@@ -381,11 +381,24 @@ public:
ADDITIVE_TERM_2
};
/** Used to specify the variable being returned by ImplicitSolve(). This can
* be queried by ODESolver to identify the variable being solved for.
* @warning Not all ODESolver may support all options. See ODESolver::SupportsImplicitVariableType() */
enum ImplicitVariableType
{
SLOPE, ///< stage slope, $k = \frac{du}{dt}$.
STATE ///< stage state, $k = u$.
};
protected:
real_t t; ///< Current time.
Type type; /**< @brief Describes the form of the TimeDependentOperator, see
the documentation of #Type. */
EvalMode eval_mode; ///< Current evaluation mode.
ImplicitVariableType implicit_variable_type =
ImplicitVariableType::SLOPE; /**< @brief
Return variable for
ImplicitSolve()*/
public:
/** @brief Construct a "square" TimeDependentOperator (u,t) -> k(u,t), where
@@ -429,6 +442,24 @@ public:
virtual void SetEvalMode(const EvalMode new_eval_mode)
{ eval_mode = new_eval_mode; }
/** @brief Sets the #ImplicitVariableType for ImplicitSolve()*/
virtual void SetImplicitVariableType(const ImplicitVariableType variable_type)
{ implicit_variable_type = variable_type; }
/** @brief Returns the #ImplicitVariableType for ImplicitSolve(). */
virtual ImplicitVariableType GetImplicitVariableType() const
{ return implicit_variable_type; }
/** @brief Returns @a true if implicit variable is #STATE and @a false otherwise.
* Used by ODESolver to identify the stage variable returned by ImplicitSolve() */
virtual bool ImplicitVarTypeIsState() const
{ return (implicit_variable_type == ImplicitVariableType::STATE); }
/** @brief Returns @a true if implicit variable is #SLOPE and @a false otherwise.
* Used by ODESolver to identify the stage variable returned by ImplicitSolve() */
virtual bool ImplicitVarTypeIsSlope() const
{ return (implicit_variable_type == ImplicitVariableType::SLOPE); }
/** @brief Perform the action of the explicit part of the operator, G:
@a v = G(@a u, t) where t is the current time.
@@ -462,7 +493,8 @@ public:
/** @brief Solve for the unknown @a k, at the current time t, the following
equation:
F(@a u + @a gamma @a k, @a k, t) = G(@a u + @a gamma @a k, t).
1. $F( u + \gamma k, k, t) = G( u + \gamma k, t)$, if solving for stage-slope (default)
2. $F( u , \frac{k-u}{\gamma}, t) = G(k, t)$, if solving for stage-state
For solving an ordinary differential equation of the form
$ M \frac{dy}{dt} = g(y,t) $, recall that F and G can be defined in
@@ -472,8 +504,9 @@ public:
2. F(u,k,t) = M k and G(u,t) = g(u,t)
3. F(u,k,t) = M k - g(u,t) and G(u,t) = 0
Regardless of the choice of F and G, this function should solve for @a k
in M @a k = g(@a u + @a gamma @a k, t).
Regardless of the choice of F and G, this function should solve for @a k:
- $~Mk = g( u + \gamma k, t)~$, if solving for stage-slope.
- $~Mk = \gamma g(k, t) + Mu~$, if solving for stage-state
To see how @a k can be useful, consider the backward Euler method defined
by $ y(t + \Delta t) = y(t) + \Delta t k_0 $ where
@@ -491,6 +524,7 @@ public:
$ y(t) + \Delta t \sum_{j=1}^{i-1} a_{ij} k_j $ and @a gamma set to
$ a_{ii} \Delta t $, for $ k_i $. For example, see class SDIRK33Solver.
See SetImplicitVariableType() to switch between different variable modes.
If not re-implemented, this method simply generates an error. */
virtual void ImplicitSolve(const real_t gamma, const Vector &u, Vector &k);
+65 -18
View File
@@ -23,15 +23,31 @@ namespace mfem
void SparseSmoother::SetOperator(const Operator &a)
{
oper = dynamic_cast<const SparseMatrix*>(&a);
if (oper == NULL)
{
mfem_error("SparseSmoother::SetOperator : not a SparseMatrix!");
}
MFEM_VERIFY(oper != nullptr, "Operator must be a SparseMatrix");
height = oper->Height();
width = oper->Width();
At.reset();
oper_T = nullptr;
}
void SparseSmoother::EnsureTranspose() const
{
if (oper_T) { return; }
const real_t tol = 1e-14;
if (oper->IsSymmetric() > tol * oper->MaxNorm())
{
At.reset(Transpose(*oper));
oper_T = At.get();
}
else
{
At.reset();
oper_T = oper;
}
}
/// Matrix vector multiplication with GS Smoother.
void GSSmoother::Mult(const Vector &x, Vector &y) const
{
if (!iterative_mode)
@@ -51,21 +67,33 @@ void GSSmoother::Mult(const Vector &x, Vector &y) const
}
}
/// Create the Jacobi smoother.
DSmoother::DSmoother(const SparseMatrix &a, int t, real_t s, int it)
: SparseSmoother(a)
void GSSmoother::MultTranspose(const Vector &x, Vector &y) const
{
type = t;
scale = s;
iterations = it;
EnsureTranspose();
if (!iterative_mode)
{
y = 0.0;
}
for (int i = 0; i < iterations; i++)
{
if (type != 1)
{
oper_T->Gauss_Seidel_forw(x, y);
}
if (type != 2)
{
oper_T->Gauss_Seidel_back(x, y);
}
}
}
/// Matrix vector multiplication with Jacobi smoother.
void DSmoother::Mult(const Vector &x, Vector &y) const
void DSmoother::Mult_(const SparseMatrix &A, const Vector &x, Vector &y) const
{
if (!iterative_mode && type == 0 && iterations == 1)
{
oper->DiagScale(x, y, scale, use_abs_diag);
A.DiagScale(x, y, scale, use_abs_diag);
return;
}
@@ -90,22 +118,41 @@ void DSmoother::Mult(const Vector &x, Vector &y) const
{
if (type == 0)
{
oper->Jacobi(x, *p, *r, scale, use_abs_diag);
A.Jacobi(x, *p, *r, scale, use_abs_diag);
}
else if (type == 1)
{
oper->Jacobi2(x, *p, *r, scale);
A.Jacobi2(x, *p, *r, scale);
}
else if (type == 2)
{
oper->Jacobi3(x, *p, *r, scale);
A.Jacobi3(x, *p, *r, scale);
}
else
{
mfem_error("DSmoother::Mult wrong type");
MFEM_ABORT("Invalid type.");
}
Swap<Vector*>(r, p);
}
}
void DSmoother::Mult(const Vector &x, Vector &y) const
{
Mult_(*oper, x, y);
}
void DSmoother::MultTranspose(const Vector &x, Vector &y) const
{
if (iterations == 1 && !iterative_mode)
{
Mult_(*oper, x, y);
return;
}
EnsureTranspose();
MFEM_VERIFY(type == 0 || !At, "l1 or lumped Jacobi transpose not implemented"
" for non-symmetric matrices");
Mult_(*oper_T, x, y);
}
}
+117 -25
View File
@@ -15,67 +15,159 @@
#include "../config/config.hpp"
#include "sparsemat.hpp"
#include <memory>
namespace mfem
{
/// Abstract base class for smoothers created from a SparseMatrix.
class SparseSmoother : public MatrixInverse
{
protected:
const SparseMatrix *oper;
const SparseMatrix *oper = nullptr; ///< The underlying matrix.
/// Pointer to the transpose of the underlying matrix. If the matrix is
/// symmetric, this will be the same as @a oper. If the matrix is not
/// symmetric, the transpose will be formed and stored in @a At. The
/// transpose will only be formed if MultTranspose() is called.
mutable const SparseMatrix *oper_T = nullptr;
mutable std::unique_ptr<SparseMatrix> At; ///< Transpose of A, if needed.
void EnsureTranspose() const; ///< Ensure that the transpose is set.
public:
SparseSmoother() { oper = NULL; }
SparseSmoother() = default;
SparseSmoother(const SparseMatrix &a)
: MatrixInverse(a) { oper = &a; }
SparseSmoother(const SparseMatrix &a) { SetOperator(a); }
/// Sets the underlying matrix. @a a must be a SparseMatrix.
void SetOperator(const Operator &a) override;
};
/// Data type for Gauss-Seidel smoother of sparse matrix
/// Gauss-Seidel smoother of a sparse matrix.
class GSSmoother : public SparseSmoother
{
public:
enum GSType
{
SYMMETRIC, ///< Forward Gauss-Seidel, then backward.
FORWARD, ///< Forward Gauss-Seidel ($L^{-1}$).
BACKWARD ///< Backward Gauss-Seidel ($U^{-1}$).
};
protected:
int type; // 0, 1, 2 - symmetric, forward, backward
int iterations;
GSType type; ///< Type of Gauss-Seidel, see GSSmoother::GSType.
int iterations; ///< Number of stationary iterations.
public:
/// Create GSSmoother.
GSSmoother(int t = 0, int it = 1) { type = t; iterations = it; }
/// @brief Create a Gauss-Seidel smoother. SetOperator() will need to be
/// called with a SparseMatrix before first use.
///
/// @param[in] t Type of GS smoother (see GSSmoother::GSType)
/// @param[in] it Number of stationary iterations to perform
GSSmoother(GSType t = SYMMETRIC, int it = 1) { type = t; iterations = it; }
/// Create GSSmoother.
GSSmoother(const SparseMatrix &a, int t = 0, int it = 1)
: SparseSmoother(a) { type = t; iterations = it; }
/// @brief Create a Jacobi smoother using the SparseMatrix @a a.
///
/// @param[in] a The underlying SparseMatrix
/// @param[in] t Type of GS smoother (see GSSmoother::GSType)
/// @param[in] it Number of stationary iterations to perform
GSSmoother(const SparseMatrix &a, GSType t = SYMMETRIC, int it = 1)
: GSSmoother(t, it) { SetOperator(a); }
/// Matrix vector multiplication with GS Smoother.
/// Same as GSSmoother(GSType,int), for backwards compatibility.
GSSmoother(int t, int it = 1) : GSSmoother(GSType(t), it) { }
/// @brief Same as GSSmoother(const SparseMatrix&,GSType,int), for
/// backwards compatibility.
GSSmoother(const SparseMatrix &a, int t, int it = 1)
: GSSmoother(a, GSType(t), it) { }
/// @brief Application of the Gauss-Seidel smoother.
///
/// Applies a stationary Gauss-Seidel iteration. If Solver::iterative_mode is
/// true, then @a y is used as the initial guess, and Gauss-Seidel is applied
/// to the residual $x - Ay$.
void Mult(const Vector &x, Vector &y) const override;
/// Application of the transpose of the Gauss-Seidel smoother.
void MultTranspose(const Vector &x, Vector &y) const override;
};
/// Data type for scaled Jacobi-type smoother of sparse matrix
/// Jacobi-type diagonal smoother of a sparse matrix.
class DSmoother : public SparseSmoother
{
public:
enum JacobiType
{
JACOBI, ///< Scale by the diagonal of the matrix.
L1_JACOBI, ///< Scale by the l1-norm of the rows.
LUMPED_JACOBI ///< Scale by the sum of the rows.
};
protected:
int type; // 0, 1, 2 - scaled Jacobi, scaled l1-Jacobi, scaled lumped-Jacobi
real_t scale;
int iterations;
/// Uses abs values of the diagonal entries. Relevant only when type = 0.
JacobiType type; ///< Type of diagonal scaling, see DSmoother::JacobiType.
real_t scale; ///< Scaling (damping) factor.
int iterations; ///< Number of stationary iterations to perform.
/// @brief Uses abs values of the diagonal entries. Relevant only with type
/// JacobiType::JACOBI.
bool use_abs_diag = false;
mutable Vector z;
mutable Vector z; ///< Temporary work vector.
/// Apply the Jacobi smoother (used internally by Mult() and MultTranspose())
void Mult_(const SparseMatrix &A, const Vector &x, Vector &y) const;
public:
/// Create Jacobi smoother.
DSmoother(int t = 0, real_t s = 1., int it = 1)
/// @brief Create a Jacobi smoother. SetOperator() will need to be called
/// with a SparseMatrix before first use.
///
/// @param[in] t Type of Jacobi smoother (see DSmoother::JacobiType)
/// @param[in] s Scaling factor
/// @param[in] it Number of stationary iterations to perform
DSmoother(JacobiType t = JACOBI, real_t s = 1., int it = 1)
{ type = t; scale = s; iterations = it; }
/// Create Jacobi smoother.
DSmoother(const SparseMatrix &a, int t = 0, real_t s = 1., int it = 1);
/// @brief Create a Jacobi smoother using the SparseMatrix @a a.
///
/// @param[in] a The underlying SparseMatrix
/// @param[in] t Type of Jacobi smoother (see DSmoother::JacobiType)
/// @param[in] s Scaling factor
/// @param[in] it Number of stationary iterations to perform
DSmoother(const SparseMatrix &a, JacobiType t = JACOBI, real_t s = 1.,
int it = 1) : DSmoother(t, s, it) { SetOperator(a); }
/// Replace diag entries with their abs values. Relevant only when type = 0.
/// @brief Same as DSmoother(JacobiType,real_t,int), for backwards compatbility.
DSmoother(int t, real_t s = 1., int it = 1)
: DSmoother(JacobiType(t), s, it) { }
/// @brief Same as DSmoother(const SparseMatrix&,JacobiType,real_t,int), for
/// backwards compatbility.
DSmoother(const SparseMatrix &a, int t, real_t s = 1., int it = 1)
: DSmoother(a, JacobiType(t), s, it) { }
/// @brief Replace diagonal entries with their absolute values. Relevant only
/// with JacobiType::JACOBI.
void SetPositiveDiagonal(bool pos_diag = true) { use_abs_diag = pos_diag; }
/// Matrix vector multiplication with Jacobi smoother.
/// @brief Apply the Jacobi smoother.
///
/// Applies a stationary iteration with diagonal scaling. If
/// Solver::iterative_mode is true, then @a y is used as the initial guess
/// (and the diagonal scaling is applied to the residual $x - Ay$, giving
/// $D^{-1}(x - Ay)$).
///
/// By default, Solver::iterative_mode is false and only one iteration is
/// performed, corresponding to $y = D^{-1}x$.
void Mult(const Vector &x, Vector &y) const override;
/// @brief Apply the transpose of the Jacobi smoother.
///
/// If the underlying matrix is symmetric, or if only one iteration is
/// performed with zero initial guess (Solver::iterative_mode is false), then
/// this is the same as Mult(). For non-symmetric matrices with iteration
/// count greater than one, only JacobiType::JACOBI is supported.
void MultTranspose(const Vector &x, Vector &y) const override;
};
}
+2 -2
View File
@@ -113,13 +113,13 @@ AttributeSets::GetAttributeSetMarker(const std::string & set_name) const
Array<int> AttributeSets::AttrToMarker(int max_attr, const Array<int> &attrs)
{
MFEM_ASSERT(attrs.Max() <= max_attr, "Invalid attribute number present.");
MFEM_VERIFY(attrs.Min() >= 1, "Found attribute less than one")
MFEM_ASSERT(attrs.Max() <= max_attr, "Found attribute greater than max_attr")
Array<int> marker(max_attr);
marker = 0;
for (auto const &attr : attrs)
{
MFEM_VERIFY(attr > 0, "Attribute number less than one!");
marker[attr-1] = 1;
}
return marker;
+430 -14
View File
@@ -36,6 +36,7 @@
#include <numeric>
#include <unordered_map>
#include <unordered_set>
#include <list>
// Include the METIS header, if using version 5. If using METIS 4, the needed
// declarations are inlined below, i.e. no header is needed.
@@ -4772,12 +4773,12 @@ Mesh::Mesh(real_t *vertices_, int num_vertices,
FinalizeTopology();
}
Mesh::Mesh( const NURBSExtension& ext )
Mesh::Mesh(const NURBSExtension& ext)
: attribute_sets(attributes), bdr_attribute_sets(bdr_attributes)
{
SetEmpty();
/// make an internal copy of the NURBSExtension
NURBSext = new NURBSExtension( ext );
NURBSext = new NURBSExtension(ext);
Dim = NURBSext->Dimension();
NumOfVertices = NURBSext->GetNV();
@@ -4791,11 +4792,12 @@ Mesh::Mesh( const NURBSExtension& ext )
if (NURBSext->HavePatches())
{
NURBSFECollection *fec = new NURBSFECollection(NURBSext->GetOrder());
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
const int vdim = NURBSext->GetPatchSpaceDimension();
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
Ordering::byVDIM);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec);
NURBSext->SetCoordsFromPatches(*Nodes);
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
own_nodes = 1;
spaceDim = Nodes->VectorDim();
for (int i = 0; i < spaceDim; i++)
@@ -6409,7 +6411,7 @@ void Mesh::UpdateNURBS()
NURBSext->SetKnotsFromPatches();
Dim = NURBSext->Dimension();
spaceDim = Dim;
spaceDim = Nodes->FESpace()->GetVDim();
if (NumOfElements != NURBSext->GetNE())
{
@@ -6434,7 +6436,8 @@ void Mesh::UpdateNURBS()
Nodes->FESpace()->Update();
Nodes->Update();
NodesUpdated();
NURBSext->SetCoordsFromPatches(*Nodes);
const int vdim = Nodes->FESpace()->GetVDim();
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
if (NumOfVertices != NURBSext->GetNV())
{
@@ -6537,6 +6540,8 @@ void Mesh::LoadPatchTopo(std::istream &input, Array<int> &edge_to_ukv)
Array<int> ukv_to_rpkv;
GetEdgeToUniqueKnotvector(edge_to_ukv, ukv_to_rpkv);
}
CorrectPatchTopoOrientations(edge_to_ukv);
}
void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
@@ -6547,9 +6552,9 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
const int NPKV = NP * dim; // number of patch knotvectors
constexpr int notset = -9999999;
// Sign convention
auto sign = [](int i) { return -1 - i; };
auto unsign = [](int i) { return (i < 0) ? -1 - i : i; };
// Edge index -> dimension convention
auto flipSign = [](int i) { return -1 - i; };
auto unSign = [](int i) { return (i < 0) ? -1 - i : i; };
// Local edge index -> dimension convention
auto edge_to_dim = [](int i) { return (i < 8) ? ((i & 1) ? 1 : 0) : 2; };
Array<int> v(2); // vertices of an edge
@@ -6564,7 +6569,7 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
{
GetElementVertices(i, v);
// Sign is based on the edge's vertex indices
edge_to_ukv[i] = (v[1] > v[0]) ? i : sign(i);
edge_to_ukv[i] = (v[1] > v[0]) ? i : flipSign(i);
ukv_to_rpkv[i] = i;
}
return;
@@ -6614,14 +6619,14 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
// We've set this edge already - link this index to it
if (edge_to_pkv[edge] != notset)
{
const int pkv_other = unsign(edge_to_pkv[edge]);
const int pkv_other = unSign(edge_to_pkv[edge]);
unite(pkv, pkv_other);
}
else
{
GetEdgeVertices(edge, v);
// Sign is based on the edge's vertex indices
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : sign(pkv);
edge_to_pkv[edge] = (v[1] > v[0]) ? pkv : flipSign(pkv);
}
}
}
@@ -6648,11 +6653,255 @@ void Mesh::GetEdgeToUniqueKnotvector(Array<int> &edge_to_ukv,
edge_to_ukv.SetSize(NumOfEdges);
for (int i = 0; i < NumOfEdges; i++)
{
const int pkv = unsign(edge_to_pkv[i]);
const int pkv = unSign(edge_to_pkv[i]);
const int rpkv = pkv_to_rpkv[pkv];
const int ukv = rpkv_to_ukv[rpkv];
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? sign(ukv) : ukv;
edge_to_ukv[i] = (edge_to_pkv[i] < 0) ? flipSign(ukv) : ukv;
}
CorrectPatchTopoOrientations(edge_to_ukv);
}
void Mesh::CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const
{
const int dim = Dimension(); // Topological (not physical) dimension
if (dim == 1) { return; }
// Sign convention
auto flipSign = [](int i) { return -1 - i; };
const Table *face2elem = GetFaceToElementTable();
Array<int> pfaces, orient;
Array<int> fe, feo;
// Finds elements sharing a face containing knotvector kv.
auto faceNeighbors = [&](int p, int kv, std::unordered_set<int> &nghb)
{
if (dim == 2) { GetElementEdges(p, pfaces, orient); }
else { GetElementFaces(p, pfaces, orient); }
for (auto face : pfaces)
{
// Check whether this face contains kv.
GetFaceEdges(face, fe, feo);
bool hasKV = false;
for (auto e : fe)
{
const int skv = edge_to_ukv[e];
if (skv == kv || flipSign(skv) == kv) { hasKV = true; }
}
if (hasKV)
{
Array<int> row;
face2elem->GetRow(face, row);
for (auto elem : row) { nghb.insert(elem); }
}
}
};
std::vector<std::vector<int>> dir_edges;
if (dim == 2)
{
dir_edges =
{
{0,2},
{1,3}
};
}
else
{
dir_edges =
{
{0,2,4,6},
{1,3,5,7},
{8,9,10,11}
};
}
Array<int> ukvs((dim==2) ? 4 : 12);
Array<int> pe, oe;
bool initKV = false;
auto setPatchDirections = [&](int p, int kv, Array<bool> &edgeSet,
std::unordered_set<int> &visited)
{
// Edges and orientations for this patch
GetElementEdges(p, pe, oe);
// Get the signed unique knot vector indices
for (int i = 0; i < pe.Size(); i++)
{
ukvs[i] = edge_to_ukv[pe[i]];
ukvs[i] = (oe[i] < 0) ? flipSign(ukvs[i]) : ukvs[i];
}
// Find the direction with this kv.
int thisDir = -1;
for (int d=0; d<dim; ++d) // Loop over directions.
{
const int skv = edge_to_ukv[pe[dir_edges[d][0]]];
if (skv == kv || flipSign(skv) == kv)
{
thisDir = d;
}
}
MFEM_VERIFY(thisDir >= 0, "");
// For this direction, find any edge already set. If no edge is set, we
// arbitrarily take the first.
int ref_edge0 = dir_edges[thisDir][0];
for (auto ref_edge : dir_edges[thisDir])
{
const int edge = pe[ref_edge];
if (edgeSet[edge])
{
ref_edge0 = ref_edge;
}
}
if (initKV && !edgeSet[pe[ref_edge0]])
{
visited.erase(p);
return false; // There is no set edge in this direction on this patch.
}
initKV = true;
// Use ref_edge0 to set other edges in this direction.
edgeSet[pe[ref_edge0]] = true;
for (auto i : dir_edges[thisDir])
{
if (i == ref_edge0)
{
continue;
}
const int edge = pe[i];
if ((dim == 2 && ukvs[i] != flipSign(ukvs[ref_edge0])) ||
(dim == 3 && ukvs[i] == flipSign(ukvs[ref_edge0])))
{
// Flip the sign of this edge
MFEM_VERIFY(!edgeSet[edge], "");
edge_to_ukv[edge] = flipSign(edge_to_ukv[edge]);
}
edgeSet[edge] = true;
}
return true;
};
Array<bool> edgeSet(NumOfEdges); // Whether edge has orientation set
edgeSet = false;
std::unordered_set<int> unset; // Patches with an unset edge
for (int i=0; i<NumOfElements; ++i) { unset.insert(i); }
const int max_iter = 3 * NumOfElements;
for (int iter=0; iter<max_iter; ++iter)
{
// Iteratively choose an unset patch (meaning not all edges have
// orientation set), choose a knotvector index for which the corresponding
// edges on this patch are not set, and sweep over all patches containing
// this knotvector. The patch sweep is ordered, by maintaining an ordered
// list `nextPatches` set by finding face-neighbor patches of visited
// patches, where the common face contains the knotvector. When each patch
// is visited, the edge orientations are set consistently. This iteration
// terminates when all edges have been set on all patches.
std::list<int> nextPatches; // Next patches to visit, ordered
std::unordered_set<int> nextSet; // nextPatches as a set
std::unordered_set<int> visited; // Visit each patch only once
if (unset.size() == 0)
{
break;
}
const int p0 = *unset.begin();
nextPatches.push_back(p0); // Start from arbitrary unset patch
nextSet.insert(p0);
// Choose an arbitrary unset direction for the first patch.
GetElementEdges(p0, pe, oe);
int unsetDim = -1;
for (int d=0; d<dim; ++d) // Loop over dimensions.
{
if (!edgeSet[pe[dir_edges[d][0]]])
{
unsetDim = d;
}
}
if (unsetDim == -1)
{
unset.erase(p0);
continue;
}
const int kv_signed = edge_to_ukv[pe[dir_edges[unsetDim][0]]];
const int kv = kv_signed < 0 ? flipSign(kv_signed) : kv_signed;
MFEM_VERIFY(!edgeSet[pe[dir_edges[unsetDim][0]]], "");
initKV = false;
while (nextPatches.size() > 0)
{
const int p = nextPatches.front();
nextPatches.pop_front();
nextSet.erase(p);
visited.insert(p);
const bool somethingSet = setPatchDirections(p, kv, edgeSet, visited);
if (!somethingSet)
{
continue;
}
// Find neighbors of patch p sharing a conforming face, via face2elem.
std::unordered_set<int> neighbors;
faceNeighbors(p, kv, neighbors);
bool allSet = true;
GetElementEdges(p, pe, oe);
for (auto edge : pe)
{
if (!edgeSet[edge])
{
allSet = false;
}
}
if (allSet)
{
unset.erase(p);
}
// Add neighbors not done to nextPatches.
for (auto n : neighbors)
{
if (n != p && visited.count(n) == 0 && unset.count(n) > 0)
{
if (nextSet.count(n) == 0)
{
nextPatches.push_back(n);
nextSet.insert(n);
}
}
}
}
}
bool allSet = true;
for (auto eset : edgeSet)
{
if (!eset)
{
allSet = false;
}
}
MFEM_VERIFY(allSet && unset.size() == 0, "Some edge is not set");
delete face2elem;
}
void Mesh::LoadNonconformingPatchTopo(std::istream &input,
@@ -7587,6 +7836,17 @@ bool Mesh::IsMixedMesh() const
void Mesh::GetElementEdges(int i, Array<int> &edges, Array<int> &cor) const
{
if (Dim == 1)
{
// In 1D, elements are segments and can be treated as edges.
edges.SetSize(1);
cor.SetSize(1);
edges[0] = i;
const int *v = elements[i]->GetVertices();
cor[0] = (v[0] < v[1]) ? (1) : (-1);
return;
}
if (el_to_edge)
{
el_to_edge->GetRow(i, edges);
@@ -9563,6 +9823,8 @@ void Mesh::GetVertices(Vector &vert_coord) const
void Mesh::SetVertices(const Vector &vert_coord)
{
MFEM_VERIFY(vert_coord.Size() == spaceDim * NumOfVertices, "");
vertices.SetSize(NumOfVertices);
for (int i = 0, nv = vertices.Size(); i < nv; i++)
for (int j = 0; j < spaceDim; j++)
{
@@ -12140,6 +12402,38 @@ void Mesh::PrintTopoEdges(std::ostream &os, const Array<int> &e_to_k,
{
Array<int> vert;
// In 1D patch-topology NURBS meshes, knotvector orientation is stored in the
// file's `edges` section, but the topological 1D mesh has NumOfEdges == 0
// (its "faces" are vertices). When a valid edge->knotvector map is provided,
// print a pseudo-edge list derived from the 1D elements so external tools
// (e.g. VisIt) can consume the mapping.
if (Dim == 1 && NumOfEdges == 0 && e_to_k.Size() == NumOfElements)
{
const int ne = NumOfElements;
os << "\nedges\n" << ne << '\n';
for (int i = 0; i < ne; i++)
{
const int *v = elements[i]->GetVertices();
int v0 = v[0], v1 = v[1];
int ki = e_to_k[i];
const bool flip = (ki < 0); // desired output vertex order: descending
if (flip) { ki = -1 - ki; } // print the unsigned knotvector index
// Encode the sign of e_to_k in the vertex ordering, consistent with
// Mesh::LoadPatchTopo(): v0 > v1 => negative sign.
if ((v0 > v1) != flip) { std::swap(v0, v1); }
os << ki << ' ' << v0 << ' ' << v1 << '\n';
}
if (!vmap)
{
os << "\nvertices\n" << NumOfVertices << '\n';
}
return;
}
os << "\nedges\n" << NumOfEdges << '\n';
for (int i = 0; i < NumOfEdges; i++)
{
@@ -15452,6 +15746,128 @@ Mesh *Extrude2D(Mesh *mesh, const int nz, const real_t sz)
return mesh3d;
}
Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
int &par_ref, Array<int> &partitioning)
{
MFEM_VERIFY(dim > 1, "Not implemented for 1D meshes.");
auto factor = [&](int N)
{
for (int i = static_cast<int>(sqrt(N)); i > 0; i--)
{ if (N % i == 0) { return i; } }
return 1;
};
par_ref = 0;
const int ref_factor = (dim == 2) ? 4 : 8;
// Elements per task before performing parallel refinements.
// This will be used to form the serial mesh.
int el0 = elem_per_mpi;
while (el0 % ref_factor == 0)
{
el0 /= ref_factor;
par_ref++;
}
// In the serial mesh we have:
// The number of MPI blocks is mpi_cnt = mp_x.mpy_y.mpy_z.
// The size of each MPI block is el0 = el0_x.el0_y.el0_z.
int mpi_x, mpi_y, mpi_z;
int el0_x, el0_y, el0_z;
if (dim == 2)
{
mpi_x = factor(mpi_cnt);
mpi_y = mpi_cnt / mpi_x;
// Switch order for better balance.
el0_y = factor(el0);
el0_x = el0 / el0_y;
}
else
{
mpi_x = factor(mpi_cnt);
mpi_y = factor(mpi_cnt / mpi_x);
mpi_z = mpi_cnt / mpi_x / mpi_y;
// Switch order for better balance.
el0_z = factor(el0);
el0_y = factor(el0 / el0_z);
el0_x = el0 / el0_y / el0_z;
}
if (print && dim == 2)
{
int elem_par_x = mpi_x * el0_x * pow(2, par_ref),
elem_par_y = mpi_y * el0_y * pow(2, par_ref);
mfem::out << "--- Mesh generation: \n";
mfem::out << "Par mesh: " << elem_par_x << " x " << elem_par_y
<< " (" << elem_par_x * elem_par_y << " elements)\n"
<< "Elem / task: "
<< el0_x * pow(2, par_ref) << " x "
<< el0_y * pow(2, par_ref)
<< " (" << el0_x * pow(2, 2*par_ref) * el0_y << " elements)\n"
<< "MPI blocks: " << mpi_x << " x " << mpi_y
<< " (" << mpi_x * mpi_y << " mpi tasks)\n" << "-\n"
<< "Serial mesh: "
<< mpi_x * el0_x << " x " << mpi_y * el0_y
<< " (" << mpi_x * el0_x * mpi_y * el0_y << " elements)\n"
<< "Elem / task: " << el0_x << " x " << el0_y << std::endl
<< "Par refine: " << par_ref << std::endl;
mfem::out << "--- \n";
}
if (print && dim == 3)
{
int elem_par_x = mpi_x * el0_x * pow(2, par_ref),
elem_par_y = mpi_y * el0_y * pow(2, par_ref),
elem_par_z = mpi_z * el0_z * pow(2, par_ref);
mfem::out << "--- Mesh generation: \n";
mfem::out << "Par mesh: "
<< elem_par_x << " x " << elem_par_y << " x " << elem_par_z
<< " (" << elem_par_x*elem_par_y*elem_par_z << " elements)\n"
<< "Elem / task: "
<< el0_x * pow(2, par_ref) << " x "
<< el0_y * pow(2, par_ref) << " x "
<< el0_z * pow(2, par_ref)
<< " (" << el0_x*pow(2, 3*par_ref)*el0_y*el0_z << " elements)\n"
<< "MPI blocks: " << mpi_x << " x " << mpi_y << " x " << mpi_z
<< " (" << mpi_x * mpi_y * mpi_z << " mpi tasks)\n" << "-\n"
<< "Serial mesh: "
<< mpi_x*el0_x << " x " << mpi_y*el0_y << " x " << mpi_z*el0_z
<< " (" << mpi_x*el0_x*mpi_y*el0_y*mpi_z*el0_z << " elements)\n"
<< "Elem / task: "
<< el0_x << " x " << el0_y << " x " << el0_z << std::endl
<< "Par refine: " << par_ref << std::endl;
mfem::out << "--- \n";
}
Mesh mesh;
int nxyz[3];
if (dim == 2)
{
mesh = Mesh::MakeCartesian2D(mpi_x * el0_x,
mpi_y * el0_y, Element::QUADRILATERAL, true);
nxyz[0] = mpi_x; nxyz[1] = mpi_y;
}
else
{
mesh = Mesh::MakeCartesian3D(mpi_x * el0_x,
mpi_y * el0_y,
mpi_z * el0_z, Element::HEXAHEDRON, true);
nxyz[0] = mpi_x; nxyz[1] = mpi_y; nxyz[2] = mpi_z;
}
const int NE = mesh.GetNE();
partitioning.SetSize(NE);
std::unique_ptr<int[]> p_raw(mesh.CartesianPartitioning(nxyz));
std::copy(p_raw.get(), p_raw.get() + NE, partitioning.GetData());
return mesh;
}
bool Mesh::Conforming() const
{
if (NURBSext)
+33 -5
View File
@@ -527,6 +527,9 @@ protected:
void PrintTopoEdges(std::ostream &out, const Array<int> &e_to_k,
bool vmap = false) const;
/// Set signs to ensure knotvectors are pointed in the same direction.
void CorrectPatchTopoOrientations(Array<int> &edge_to_ukv) const;
/// Used in GetFaceElementTransformations (...)
void GetLocalPtToSegTransformation(IsoparametricTransformation &,
int i) const;
@@ -984,8 +987,8 @@ public:
///@}
/// Construct a Mesh from a NURBSExtension
explicit Mesh( const NURBSExtension& ext );
/// Construct a Mesh from a NURBSExtension, which is deep-copied.
explicit Mesh(const NURBSExtension& ext);
/** @anchor mfem_Mesh_construction
@name Methods for piecewise Mesh construction.
@@ -2538,13 +2541,16 @@ public:
changing the mesh file itself. Examples in miniapps/nurbs/meshes. */
void RefineNURBSFromFile(std::string ref_file);
/// For NURBS meshes, insert the new knots in @a kv, for each direction.
/// For NURBS meshes, insert the new knots in @a kv, for each KnotVector.
/// The size of @a kv should be the number of KnotVectors in NURBSExtension.
void KnotInsert(Array<KnotVector*> &kv);
/// For NURBS meshes, insert the knots in @a kv, for each direction.
/// For NURBS meshes, insert the knots in @a kv, for each KnotVector.
/// The size of @a kv should be the number of KnotVectors in NURBSExtension.
void KnotInsert(Array<Vector*> &kv);
/// For NURBS meshes, remove the knots in @a kv, for each direction.
/// For NURBS meshes, remove the knots in @a kv, for each KnotVector.
/// The size of @a kv should be the number of KnotVectors in NURBSExtension.
void KnotRemove(Array<Vector*> &kv);
/* For each knot vector:
@@ -3201,6 +3207,28 @@ Mesh *Extrude1D(Mesh *mesh, const int ny, const real_t sy,
/// Extrude a 2D mesh
Mesh *Extrude2D(Mesh *mesh, const int nz, const real_t sz);
/** @brief Constructs the smallest possible [0,1]^dim serial mesh that can be
used later to obtain a ParMesh with @a elem_per_mpi elements, with the same
topology, for each of the @a mpi_cnt MPI tasks. For quads and hexes.
The serial mesh has the smallest possible number of elements. The parallel
mesh will be obtained by parallel refinements. Each MPI task will have
elements with the same topology (same number, same connectivity).
@param[in] dim dimension (2 or 3).
@param[in] mpi_cnt number of MPI tasks.
@param[in] elem_per_mpi number of elements per MPI task.
@param[in] print shows meshing info in the terminal.
@param[out] par_ref number of parallel refinement needed afterwards.
@param[out] partitioning partitioning to create the desired ParMesh.
Usual use case:
Mesh mesh = PartitionMPI(dim, mpi_cnt, elem_per_mpi, print, par_ref, par);
ParMesh pmesh(MPI_COMM_WORLD, mesh, par.GetData());
for (int lev = 0; lev < par_ref; lev++) { pmesh.UniformRefinement(); } */
Mesh PartitionMPI(int dim, int mpi_cnt, int elem_per_mpi, bool print,
int &par_ref, Array<int> &partitioning);
// shift cyclically 3 integers left-to-right
inline void ShiftRight(int &a, int &b, int &c)
{
+3 -2
View File
@@ -1328,11 +1328,12 @@ void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf,
if (NURBSext->HavePatches())
{
NURBSFECollection *fec = new NURBSFECollection(NURBSext->GetOrder());
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
const int vdim = NURBSext->GetPatchSpaceDimension();
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
Ordering::byVDIM);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec);
NURBSext->SetCoordsFromPatches(*Nodes);
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
own_nodes = 1;
read_gf = 0;
spaceDim = Nodes->VectorDim();
+2
View File
@@ -2827,6 +2827,8 @@ void NCNURBSExtension::PropagateFactorsForKV(int rf_default)
}
}
delete face2elem;
// For any unset entries of kvf, set to default refinement factor rf_default.
for (size_t i=0; i<kvf.size(); ++i)
{
+634 -241
View File
File diff suppressed because it is too large Load Diff
+139 -32
View File
@@ -51,6 +51,21 @@ protected:
/// Number of elements, defined by distinct knots.
int NumOfElements;
// Stores the demko points
mutable Vector demko;
/// Compute all the Demko points
void ComputeDemko() const;
#ifdef MFEM_USE_LAPACK
// Data for reusing banded matrix factorization in FindInterpolant().
mutable DenseMatrix fact_AB; /// Banded matrix factorization
mutable Array<int> fact_ipiv; /// Row pivot indices
#else
mutable DenseMatrix A_coll_inv; /// Collocation matrix inverse
#endif
public:
/// Create an empty KnotVector.
KnotVector() = default;
@@ -63,6 +78,14 @@ public:
order @a order and number of control points @a NCP. */
KnotVector(int order, int NCP);
/** @brief Create a KnotVector with order @a order and knots @a knot.
If @a k has the correct number of repeated knots at the begin and end,
then this constructor will copy the knots as provided.
Otherwise, the knot vector will be extended by repeating the end knots
(order + 1) times. Internal knots will retain the multiplicity as given
in the input. */
KnotVector(int order, const Vector &k);
/** @brief Create a KnotVector by passing in a degree, a Vector of interval
lengths of length n, and a list of continuity of length n + 1.
@@ -103,13 +126,69 @@ public:
with @a isElement for non-empty knot spans (elements). */
int GetNKS() const { return NumOfControlPoints - Order; }
/** @brief Return the parameter for element reference coordinate @a xi
in [0,1], for the element beginning at knot @a ni. */
real_t getKnotLocation(real_t xi, int ni) const
{ return (xi*knot(ni+1) + (1. - xi)*knot(ni)); }
/// Return whether knot location @a u is in a given span @a ni.
bool inSpan(real_t u, int ni) const
{
if ((u < knot(ni)) || (u > knot(ni+1))) { return false; }
return true;
}
/// Return the index of the knot span containing parameter @a u.
int findKnotSpan(real_t u) const;
int GetSpan(real_t u) const;
/** @brief Return the reference coordinate in [0,1] for parameter @a u
in the element beginning at knot @a ni. */
real_t GetRefPoint(real_t u, int ni) const
{ return (u-knot(ni))/(knot(ni+1)-knot(ni)); };
/** @brief Return the knot location for element reference coordinate @a xi
in [0,1], for the element beginning at knot @a ni. */
real_t GetKnotLocation(real_t xi, int ni) const
{ return (xi*knot(ni+1) + (1. - xi)*knot(ni)); }
/** @brief Return the parameter for element reference coordinate @a xi
in [0,1], for the element beginning at knot @a ni. */
MFEM_DEPRECATED real_t getKnotLocation(real_t xi, int ni) const
{ return (xi*knot(ni+1) + (1. - xi)*knot(ni)); } // Use GetKnotLocation instead
/// Return the index of the knot span containing parameter @a u.
MFEM_DEPRECATED int findKnotSpan(real_t u) const; // Use GetSpan instead
/** Gives the @a i average knot location. Average is taken over @a Order
number of knots.*/
real_t GetGreville(int i) const;
void GetGreville(Vector &xi) const;
/** Gives the knot location where the @a i shape function is maximum.
Reverts to the Greville point if knot is repeated @a Order +1 times.
For background see:
Olivier Botella and Karim Shariff.
"B-spline methods in fluid dynamics."
International Journal of Computational Fluid Dynamics 17.2 (2003): 133-149.
Points are found using Newton iteration, with the Greville point as the
starting value. */
real_t GetBotella(int i) const;
void GetBotella(Vector &xi) const;
/** Gives the knot location of the @a i extremum of the Chebyshev spline.
For background see:
Stephen Demko
"On the existence of interpolating projections onto spline spaces."
Journal of approximation theory 43.2 (1985): 151-156.
Points are found using Remez iteration:
- Find interpolant, given by a, through given points, given by Demko
- Find extrema of this polynomial and update Demko points
- Repeat until converged
- Use the Greville point as starting point */
real_t GetDemko(int i) const;
void GetDemko(Vector &xi) const;
// The following functions evaluate shape functions, which are B-spline basis
// functions.
@@ -136,19 +215,32 @@ public:
/** @brief Gives the locations of the maxima of the KnotVector in reference
space. The function gives the knot span @a ks, the coordinate in the
knot span @a xi, and the coordinate of the maximum in parameter space
@a u. */
void FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const;
@a u.
The main purpose of this function is its use in FindInterpolant.
Use GetBotella instead for each shape function separately, perhaps in
conjuction with GetSpan and GetRefPoint.*/
MFEM_DEPRECATED void FindMaxima(Array<int> &ks, Vector &xi, Vector &u) const;
/** @brief Global curve interpolation through the points @a x (overwritten).
@a x is an array with the length of the spatial dimension containing
vectors with spatial coordinates. The control points of the interpolated
curve are returned in @a x in the same form.
Use GetInterpolant instead. For the knot location one can use either
GetBotella, GetDemko or GetGreville. FindInterpolant uses the Botella
points, however, the Demko points might be more appropriate. */
MFEM_DEPRECATED void FindInterpolant(Array<Vector*> &x, bool reuse_inverse);
The inverse of the collocation matrix, used in the interpolation, is
stored for repeated calls and used if @a reuse_inverse is true. Reuse is
valid only if this KnotVector has not changed since the initial call with
@a reuse_inverse false. */
void FindInterpolant(Array<Vector*> &x, bool reuse_inverse = false);
/** @brief Global curve interpolation through the points @a x (overwritten)
at the knot location @a u. The control points of the
interpolated curve are returned in @a x in the same form.
For the knot location one can use for instance GetBotella, GetDemko or
GetGreville. The Demko points might be most appropriate.*/
void GetInterpolant(Array<Vector*> &x, const Vector &u,
bool reuse_inverse = false) const;
/// Different interface to same routine
void GetInterpolant(const Vector &x, const Vector &u,
Vector &a, bool reuse_inverse = false) const;
/** Set @a diff, comprised of knots in @a kv not contained in this KnotVector.
@a kv must be of the same order as this KnotVector. The current
@@ -191,6 +283,18 @@ public:
number of samples of the shape functions per element.*/
void PrintFunctions(std::ostream &os, int samples=11) const;
/** Prints the function with basis function coefficient @a a, and its 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 PrintFunction(std::ostream &os, const Vector &a, int samples=11) const;
/** Prints the @a i-th function and its 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 PrintFunction(std::ostream &os, int i, int samples=11) const;
/// Destroys KnotVector
~KnotVector() { }
@@ -209,14 +313,6 @@ public:
/** @brief Flag to indicate whether the KnotVector has been coarsened, which
means it is ready for non-nested refinement. */
bool coarse;
#ifdef MFEM_USE_LAPACK
// Data for reusing banded matrix factorization in FindInterpolant().
DenseMatrix fact_AB; /// Banded matrix factorization
Array<int> fact_ipiv; /// Row pivot indices
#else
DenseMatrix A_coll_inv; /// Collocation matrix inverse
#endif
};
@@ -596,28 +692,29 @@ protected:
if the KnotVector index associated with edge @a edge is negative. */
inline const KnotVector *KnotVec(int edge, int oedge, int *okv) const;
/// Throw an error if any patch has an inconsistent edge_to_ukv mapping.
void CheckPatches();
/// Throw an error if any boundary patch has invalid KnotVector orientation.
void CheckBdrPatches();
MFEM_DEPRECATED void CheckBdrPatches();
/// Return the patch-topology edge indices that define the KnotVectors for
/// patch @a p in each parametric direction.
void GetPatchDirectionEdges(int p, Array<int> &edges);
/** @brief Return the directions in @a kvdir of the KnotVectors in patch @a p
based on the patch edge orientations. Each entry of @a kvdir is -1 if the
KnotVector direction is flipped, +1 otherwise. */
void CheckKVDirection(int p, Array <int> &kvdir);
/** @brief Create the comprehensive set of KnotVectors. In 1D, this set is
identical to the unique set of KnotVectors. */
/** @brief Create the comprehensive set of KnotVectors, one per patch and
parametric direction, accounting for the edge orientations. */
void CreateComprehensiveKV();
/** Update the unique set of KnotVectors. In 1D, this set is identical to
the comprehensive set of KnotVectors. */
/** @brief Update the unique set of KnotVectors from the comprehensive set
of KnotVectors. */
void UpdateUniqueKV();
/** @brief Check if the comprehensive array of KnotVectors agrees with the
unique set of KnotVectors, on each patch. Return false if there is a
difference, true otherwise. This function throws an error in 1D. */
difference, true otherwise. */
bool ConsistentKVSets();
/// Return KnotVectors in @a kv in each dimension for patch @a p.
@@ -794,6 +891,9 @@ public:
void MergeGridFunctions(GridFunction *gf_array[], int num_pieces,
GridFunction &merged);
/// Returns false if any patch has an inconsistent edge_to_ukv mapping.
bool CheckPatches();
/// Destroy a NURBSExtension.
virtual ~NURBSExtension();
@@ -820,6 +920,13 @@ public:
/// Return the dimension of the reference space (not physical space).
int Dimension() const { return patchTopo->Dimension(); }
/** @brief Return the physical dimension of the NURBS geometry
The physical dimension is inferred from the first patch,
i.e. number of coordinates per control point minus one (for the weight).
This method requires patch data to be present, i.e. HavePatches() == true */
int GetPatchSpaceDimension() const;
/// Return the number of patches.
int GetNP() const { return patchTopo->GetNE(); }
@@ -933,9 +1040,9 @@ public:
void ConvertToPatches(const Vector &Nodes);
/// Set KnotVectors from @a patches and construct mesh and space data.
void SetKnotsFromPatches();
/** @brief Set FE coordinates in @a Nodes, using data from @a patches, and
erase @a patches. */
void SetCoordsFromPatches(Vector &Nodes);
/** @brief Set FE coordinates in @a Nodes, using data from @a patches,
with physical vector dimension @a vdim, and erase @a patches. */
void SetCoordsFromPatches(Vector &Nodes, int vdim);
/** @brief Read a GridFunction @a sol from stream @a input, written
patch-by-patch, e.g. with PrintSolution(). */
+13 -3
View File
@@ -3041,7 +3041,7 @@ void ParMesh::GetSharedFaceTransformationsByLocalIndex(
// for ghost faces we need a special version of GetFaceTransformation
if (is_ghost)
{
GetGhostFaceTransformation(FElTr, face_type, face_geom);
GetGhostFaceTransformation(FaceNo, FElTr);
mask |= FaceElementTransformations::HAVE_FACE;
}
@@ -3064,19 +3064,29 @@ void ParMesh::GetSharedFaceTransformationsByLocalIndex(
}
void ParMesh::GetGhostFaceTransformation(
FaceElementTransformations &FElTr, Element::Type face_type,
Geometry::Type face_geom) const
int FaceNo, FaceElementTransformations &FElTr) const
{
MFEM_ASSERT(FaceNo >= GetNumFaces(), "Not a ghost face.");
// use the local face data
const int LocFaceNo = nc_faces_info[faces_info[FaceNo].NCFace].MasterFace;
FElTr.Attribute = (Dim == 1) ? 1 : faces[LocFaceNo]->GetAttribute();
FElTr.ElementNo = FaceNo;
FElTr.ElementType = ElementTransformation::FACE;
FElTr.mesh = this;
// calculate composition of FElTr.Loc1 and FElTr.Elem1
DenseMatrix &face_pm = FElTr.GetPointMat();
FElTr.Reset();
if (Nodes == NULL)
{
const Element::Type face_type = GetFaceElementType(LocFaceNo);
FElTr.Elem1->Transform(FElTr.Loc1.Transf.GetPointMat(), face_pm);
FElTr.SetFE(GetTransformationFEforElementType(face_type));
}
else
{
const Geometry::Type face_geom = GetFaceGeometry(LocFaceNo);
const FiniteElement* face_el =
Nodes->FESpace()->GetTraceElement(FElTr.Elem1No, face_geom);
MFEM_VERIFY(dynamic_cast<const NodalFiniteElement*>(face_el),
+1 -9
View File
@@ -150,15 +150,7 @@ protected:
int elem, int start, int end, const int fverts[][N]);
void GetGhostFaceTransformation(
FaceElementTransformations &FElTr, Element::Type face_type,
Geometry::Type face_geom) const;
void GetGhostFaceTransformation(
FaceElementTransformations *FElTr, Element::Type face_type,
Geometry::Type face_geom) const
{
MFEM_ASSERT(FElTr, "Missing FaceElementTransformations object!");
GetGhostFaceTransformation(*FElTr, face_type, face_geom);
}
int FaceNo, FaceElementTransformations &FElTr) const;
/// Update the groups after triangle refinement
void RefineGroups(const DSTable &v_to_v, int *middle);
+10 -2
View File
@@ -1195,8 +1195,17 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
}
}
// If there are shared slaves, they will also need to be updated.
// If there are shared slaves, they will also need to be updated. First,
// check whether the update has already been done.
bool sharedUpdated = false;
if (shared.slaves.Size())
{
int nfaces = NFaces, nghosts = NGhostFaces;
if (Dim <= 2) { nfaces = NEdges, nghosts = NGhostEdges; }
sharedUpdated = (pmesh.faces_info.Size() == nfaces + nghosts);
}
if (shared.slaves.Size() && !sharedUpdated)
{
int nfaces = NFaces, nghosts = NGhostFaces;
if (Dim <= 2) { nfaces = NEdges, nghosts = NGhostEdges; }
@@ -1310,7 +1319,6 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
}
}
// In 3D some extra orientation data structures can be needed.
if (Dim == 3)
{
+8 -6
View File
@@ -34,14 +34,16 @@ class ParNCSubMesh;
* subset of the parent Mesh and reuses the parallel distribution.
*
* The attributes are taken from the parent. That means if a volume is extracted
* from a volume, it has the same domain attribute as the parent. Its boundary
* attributes are generated (there will be one boundary attribute 1 for all of
* the boundaries).
* from a volume, it has the same domain attribute as the parent. Its new
* boundary attributes are, for any boundary common to the parent and the new
* submesh, the boundary attribute of the parent; and, for all new boundaries,
* a single, generated, common attribute equal to one plus the largest boundary
* attribute of the parent.
*
* If a surface is extracted from a volume, the boundary attribute from the
* parent is assigned to be the new domain attribute. Its boundary attributes
* are generated (there will be one boundary attribute 1 for all of the
* boundaries).
* parent is assigned to be the new domain attribute. Its new boundary attribute
* is a single, generated, common attribute equal to one plus the largest
* boundary attribute of the parent.
*
* For more customized boundary attributes, the resulting ParSubMesh has to be
* postprocessed.
+8 -6
View File
@@ -28,14 +28,16 @@ class NCSubMesh;
* subset of the parents Mesh and reuses the parallel distribution.
*
* The attributes are taken from the parent. That means if a volume is extracted
* from a volume, it has the same domain attribute as the parent. Its boundary
* attributes are generated (there will be one boundary attribute 1 for all of
* the boundaries).
* from a volume, it has the same domain attribute as the parent. Its new
* boundary attributes are, for any boundary common to the parent and the new
* submesh, the boundary attribute of the parent; and, for all new boundaries,
* a single, generated, common attribute equal to one plus the largest boundary
* attribute of the parent.
*
* If a surface is extracted from a volume, the boundary attribute from the
* parent is assigned to be the new domain attribute. Its boundary attributes
* are generated (there will be one boundary attribute 1 for all of the
* boundaries).
* parent is assigned to be the new domain attribute. Its new boundary attribute
* is a single, generated, common attribute equal to one plus the largest
* boundary attribute of the parent.
*
* For more customized boundary attributes, the resulting SubMesh has to be
* postprocessed.
-21
View File
@@ -232,27 +232,6 @@ MergeMeshNodes(Mesh * mesh, int logging)
}
}
void AttrToMarker(int max_attr, const Array<int> &attrs, Array<int> &marker)
{
MFEM_ASSERT(attrs.Max() <= max_attr, "Invalid attribute number present.");
marker.SetSize(max_attr);
if (attrs.Size() == 1 && attrs[0] == -1)
{
marker = 1;
}
else
{
marker = 0;
for (int j=0; j<attrs.Size(); j++)
{
int attr = attrs[j];
MFEM_VERIFY(attr > 0, "Attribute number less than one!");
marker[attr-1] = 1;
}
}
}
void AffineTransformation::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
+8 -3
View File
@@ -33,9 +33,14 @@ void MergeMeshNodes(Mesh * mesh, int logging);
/// Convert a set of attribute numbers to a marker array
/** The marker array will be of size max_attr and it will contain only zeroes
and ones. Ones indicate which attribute numbers are present in the attrs
array. In the special case when attrs has a single entry equal to -1 the
marker array will contain all ones. */
void AttrToMarker(int max_attr, const Array<int> &attrs, Array<int> &marker);
array. In the special case when attrs has an entry equal to -1 the marker
array will contain all ones. */
inline
void AttrToMarker(int max_attr, const Array<int> &attrs, Array<int> &marker)
{
if (attrs.Find(-1) != -1) { (marker = Array<int>(max_attr)) = 1; }
else { marker = AttributeSets::AttrToMarker(max_attr, attrs); }
}
/// Transform a mesh according to an arbitrary affine transformation
/// y = A x + b
@@ -127,7 +127,7 @@ int main(int argc, char *argv[])
}
// Load mesh + complete any serial refinements
Mesh mesh("../../data/channel-bifurcation-2d.mesh");
Mesh mesh("../../../data/channel-bifurcation-2d.mesh");
for (int lev = 0; lev < ctx.rs_levels; lev++)
{
mesh.UniformRefinement();
+3 -5
View File
@@ -15,16 +15,14 @@ set(MESH_GF_FILES
triple-pt-1.gf
triple-pt-2.gf
)
# add target which keeps required mesh files in sync
set(SRC_MESH_GF_FILES)
foreach(MESH_GF_FILE ${MESH_GF_FILES})
list(APPEND SRC_MESH_GF_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${MESH_GF_FILE})
endforeach()
add_custom_command(OUTPUT data_is_copied
add_custom_target(copy_miniapps_gslib_data
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_GF_FILES} .
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
COMMENT "Copying gslib miniapps data files ...")
add_custom_target(copy_miniapps_gslib_data DEPENDS data_is_copied)
COMMENT "Syncing gslib miniapps data files ...")
if (MFEM_USE_GSLIB)
add_mfem_miniapp(schwarz_ex1
+2 -4
View File
@@ -27,11 +27,9 @@ set(SRC_MESH_FILES)
foreach(MESH_FILE ${MESH_FILES})
list(APPEND SRC_MESH_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${MESH_FILE})
endforeach()
add_custom_command(OUTPUT data_is_copied
add_custom_target(copy_miniapps_meshing_data
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_FILES} .
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
COMMENT "Copying meshing miniapps data files ...")
add_custom_target(copy_miniapps_meshing_data DEPENDS data_is_copied)
COMMENT "Syncing meshing miniapps data files ...")
add_mfem_miniapp(klein-bottle
MAIN klein-bottle.cpp
+234 -12
View File
@@ -33,8 +33,7 @@
using namespace std;
using namespace mfem;
void ReflectPoint(Vector & p, Vector const& origin, Vector const& normal)
void ReflectPoint(Vector &p, const Vector &origin, const Vector &normal)
{
Vector diff(3);
Vector proj(3);
@@ -57,12 +56,12 @@ private:
// Map from reflected to original mesh elements
std::vector<int> *r2o;
std::vector<std::vector<int>> *perm;
std::vector<std::array<int, 8>> *perm;
public:
ReflectedCoefficient(VectorCoefficient &A, Vector const& origin_,
Vector const& normal_, std::vector<int> *r2o_,
Mesh *mesh, std::vector<std::vector<int>> *refPerm) :
Mesh *mesh, std::vector<std::array<int, 8>> *refPerm) :
VectorCoefficient(3), a(&A), origin(origin_), normal(normal_),
meshOrig(mesh), r2o(r2o_), perm(refPerm)
{ }
@@ -109,7 +108,7 @@ void ReflectedCoefficient::Eval(Vector &V, ElementTransformation &T,
// give the columns of A.
// Permutation p is such that hex_reflected[i] = hex_init[p[i]]
const std::vector<int>& p = (*perm)[elem];
const std::array<int, 8>& p = (*perm)[elem];
// ip is on reflected hex. We map from the reflected hex to the initial
// hex, in reference space. Thus we use y = Ax + b, where x is in the
@@ -164,7 +163,7 @@ void ReflectedCoefficient::Eval(Vector &V, ElementTransformation &T,
// Find perm such that h1[i] = h2[perm[i]]
void GetHexPermutation(Array<int> const& h1, Array<int> const& h2,
std::vector<int> & perm)
std::array<int, 8> &perm)
{
std::map<int, int> h2inv;
const int n = perm.size();
@@ -236,7 +235,7 @@ public:
int AddElement(Array<int> const& vertices, const bool reorder);
Mesh *mesh;
std::vector<std::vector<int>> refPerm;
std::vector<std::array<int, 8>> refPerm;
private:
std::vector<std::vector<int>> faces;
@@ -279,13 +278,13 @@ int HexMeshBuilder::AddElement(Array<int> const& vertices, const bool reorder)
}
while (reordered);
std::vector<int> perm_e(8);
std::array<int, 8> perm_e;
GetHexPermutation(rvert, vertices, perm_e);
refPerm.push_back(perm_e);
}
else
{
refPerm.push_back(std::vector<int> {0, 1, 2, 3, 4, 5, 6, 7});
refPerm.push_back(std::array<int, 8> {0, 1, 2, 3, 4, 5, 6, 7});
}
SaveHexFaces(mesh->GetNE(), rvert);
@@ -762,7 +761,10 @@ bool GetMeshElementOrder(Mesh const& mesh, Vector const& origin,
return true;
}
Mesh* ReflectHighOrderMesh(Mesh & mesh, Vector origin, Vector normal)
Mesh* ReflectHighOrderMesh(Mesh &mesh,
const Vector &origin, const Vector &normal,
std::vector<std::array<int, 8>> &hexPerm,
std::vector<int> &elOrder)
{
MFEM_VERIFY(mesh.Dimension() == 3, "Only 3D meshes can be reflected");
@@ -837,7 +839,6 @@ Mesh* ReflectHighOrderMesh(Mesh & mesh, Vector origin, Vector normal)
}
}
std::vector<int> elOrder;
const bool onPlane = GetMeshElementOrder(mesh, origin, normal, elOrder);
for (int eidx=0; eidx<mesh.GetNE(); eidx++)
@@ -1006,6 +1007,133 @@ Mesh* ReflectHighOrderMesh(Mesh & mesh, Vector origin, Vector normal)
*reflected_nodes = newReflectedNodes;
}
hexPerm = builder.refPerm;
return reflected;
}
void ReorderHexArray(const std::array<int, 3> &dim,
const array<int, 8> &hexperm,
std::array<int, 3> &dir, std::array<int, 3> &dims,
Array3D<int> &permArray);
NURBSPatch* ReflectPatch(NURBSPatch *patch, int nx, int ny, int nz,
const Vector &origin, const Vector &normal,
const std::array<int, 8> &hexPerm)
{
// The hexahedral element for this patch in the reflected patch topology mesh
// is the reflection of an original patch topology mesh element, with
// reference vertices permuted according to hexPerm. The original grid of
// (nx + 1) x (ny + 1) x (nz + 1)
// control points has a new size and ordering, depending on hexPerm. Now,
// ReorderHexArray finds the new dimensions of this grid in `dims`, maps the
// directions in `dir`, and sets the permutation of grid indices as triples
// in `permArray`.
std::array<int, 3> dims, dir;
Array3D<int> permArray;
ReorderHexArray({nx+1, ny+1, nz+1}, hexPerm, dir, dims, permArray);
const KnotVector *kv0 = patch->GetKV(dir[0]);
const KnotVector *kv1 = patch->GetKV(dir[1]);
const KnotVector *kv2 = patch->GetKV(dir[2]);
NURBSPatch *rpatch = new NURBSPatch(kv0, kv1, kv2, 4);
// Reflect the control points in this reflected patch `rpatch`.
Vector vr(3);
for (int i=0; i<dims[0]; ++i)
{
for (int j=0; j<dims[1]; ++j)
{
for (int k=0; k<dims[2]; ++k)
{
const int old = permArray(i,j,k);
const int i0 = old / ((ny + 1) * (nz + 1));
const int j0 = (old - (i0 * (ny + 1) * (nz + 1))) / (nz + 1);
const int k0 = old - (i0 * (ny + 1) * (nz + 1)) - (j0 * (nz+1));
const real_t w = (*patch)(i0,j0,k0,3); // Weight
for (int l=0; l<3; ++l) { vr[l] = (*patch)(i0,j0,k0,l) / w; }
ReflectPoint(vr, origin, normal);
for (int l=0; l<3; ++l) { (*rpatch)(i,j,k,l) = vr[l] * w; }
(*rpatch)(i,j,k,3) = w;
}
}
}
return rpatch;
}
Mesh* ReflectNURBSMesh(Mesh &mesh, const Vector &origin, const Vector &normal)
{
MFEM_VERIFY(mesh.NURBSext && mesh.Dimension() == 3,
"Only 3D NURBS meshes can be reflected");
Mesh patchTopo = mesh.NURBSext->GetPatchTopology(); // Deep copy
Array<NURBSPatch*> patchesOriginal, patches;
mesh.GetNURBSPatches(patchesOriginal); // Deep copy
NURBSPatchMap p2g(mesh.NURBSext);
const KnotVector *kv[3];
const int pnv = patchTopo.GetNV();
Vector vert_coord(3 * patchTopo.GetNV());
for (int p=0; p<patchesOriginal.Size(); ++p)
{
p2g.SetPatchDofMap(p, kv);
const int nx = p2g.nx();
const int ny = p2g.ny();
const int nz = p2g.nz();
Array<int> vert;
patchTopo.GetElementVertices(p, vert);
for (int l=0; l<3; ++l)
{
const int os = l * pnv;
vert_coord[vert[0] + os] = (*patchesOriginal[p])(0,0,0,l);
vert_coord[vert[1] + os] = (*patchesOriginal[p])(nx,0,0,l);
vert_coord[vert[2] + os] = (*patchesOriginal[p])(nx,ny,0,l);
vert_coord[vert[3] + os] = (*patchesOriginal[p])(0,ny,0,l);
vert_coord[vert[4] + os] = (*patchesOriginal[p])(0,0,nz,l);
vert_coord[vert[5] + os] = (*patchesOriginal[p])(nx,0,nz,l);
vert_coord[vert[6] + os] = (*patchesOriginal[p])(nx,ny,nz,l);
vert_coord[vert[7] + os] = (*patchesOriginal[p])(0,ny,nz,l);
}
}
patchTopo.SetVertices(vert_coord);
std::vector<std::array<int, 8>> hexPerm;
std::vector<int> elOrder;
Mesh *reflectedPatchTopo = ReflectHighOrderMesh(patchTopo, origin, normal,
hexPerm, elOrder);
// Construct reflected patches. Note that reflectedPatchTopo has patch
// ordering depending on patchTopo.
for (int p=0; p<patchesOriginal.Size(); ++p)
{
const int p_orig = elOrder[p]; // TODO: use r2o instead?
p2g.SetPatchDofMap(p_orig, kv);
const int nx = p2g.nx();
const int ny = p2g.ny();
const int nz = p2g.nz();
patches.Append(patchesOriginal[p_orig]);
patches.Append(ReflectPatch(patchesOriginal[p_orig], nx, ny, nz,
origin, normal, hexPerm[(2 * p) + 1]));
}
NURBSExtension *ne = new NURBSExtension(reflectedPatchTopo, patches);
delete reflectedPatchTopo;
for (auto patch : patches) { delete patch; }
Mesh *reflected = new Mesh(*ne);
delete ne;
return reflected;
}
@@ -1045,7 +1173,19 @@ int main(int argc, char *argv[])
Mesh mesh(mesh_file, 0, 0);
Mesh *reflected = ReflectHighOrderMesh(mesh, origin, normal);
Mesh *reflected{nullptr};
//if (mesh.IsNURBS()) // TODO: available in PR 4936
if (mesh.NURBSext)
{
reflected = ReflectNURBSMesh(mesh, origin, normal);
}
else
{
std::vector<std::array<int, 8>> hexPerm;
std::vector<int> elOrder;
reflected = ReflectHighOrderMesh(mesh, origin, normal, hexPerm, elOrder);
}
// Save the final mesh
ofstream mesh_ofs("reflected.mesh");
@@ -1065,3 +1205,85 @@ int main(int argc, char *argv[])
return 0;
}
void HexVertexIJK(const int idx, std::array<int, 3>& ijk)
{
ijk[2] = idx / 4;
const int id2d = idx - (4 * ijk[2]);
ijk[1] = id2d / 2;
ijk[0] = (ijk[1] == 0) ? id2d : 3 - id2d;
}
void ReorderHexArray(const std::array<int, 3> &dim,
const array<int, 8> &hexperm,
std::array<int, 3> &dir, std::array<int, 3> &dims,
Array3D<int> &permArray)
{
int prinV[4] = {0, 1, 3, 4}; // Vertices in principal directions (after 0)
int newPrinV[4];
// newVertices[i] = oldVertices[hexperm[i]]
// Hence newPrinV[0] = hexperm[0] is the index
// of new vertex 0 in the old hex.
std::array<int, 3> newIJK[4];
for (int i = 0; i < 4; ++i)
{
newPrinV[i] = hexperm[prinV[i]];
HexVertexIJK(newPrinV[i], newIJK[i]);
}
// For direction i in the new hex, dir[i] is the direction in the old hex.
Array<bool> rev(3);
for (int i = 0; i < 3; ++i)
{
bool iset = false;
for (int j = 0; j < 3; ++j)
{
const int d = newIJK[i + 1][j] - newIJK[0][j];
if (d != 0)
{
MFEM_VERIFY(!iset, "");
MFEM_VERIFY(d == 1 || d == -1, "");
dir[i] = j;
rev[i] = (d == -1);
iset = true;
}
}
MFEM_VERIFY(iset, "");
dims[i] = dim[dir[i]];
}
MFEM_VERIFY(dir[0] + dir[1] + dir[2] == 3, "");
permArray.SetSize(dims[0], dims[1], dims[2]);
Array<int> old_ijk(3);
Array<int> new_ijk(3);
for (int i = 0; i < dims[0]; ++i)
for (int j = 0; j < dims[1]; ++j)
for (int k = 0; k < dims[2]; ++k)
{
new_ijk[0] = i;
new_ijk[1] = j;
new_ijk[2] = k;
for (int m = 0; m < 3; ++m)
{
const int d = dir[m]; // Old hex direction
if (rev[m])
{
old_ijk[d] = dim[d] - 1 - new_ijk[m];
}
else
{
old_ijk[d] = new_ijk[m];
}
}
permArray(i, j, k) =
old_ijk[2] + (old_ijk[1] * dim[2]) + (old_ijk[0] * dim[1] * dim[2]);
}
}
+2 -4
View File
@@ -19,11 +19,9 @@ set(SRC_MESH_FILES)
foreach(MESH_FILE ${MESH_FILES})
list(APPEND SRC_MESH_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${MESH_FILE})
endforeach()
add_custom_command(OUTPUT data_is_copied
add_custom_target(copy_miniapps_multidomain_data
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_FILES} .
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
COMMENT "Copying multidomain miniapps data files ...")
add_custom_target(copy_miniapps_multidomain_data DEPENDS data_is_copied)
COMMENT "Syncing multidomain miniapps data files ...")
# Parallel apps.
if (MFEM_USE_MPI)
+19 -24
View File
@@ -9,31 +9,12 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
set(MESH_FILES
cube-nurbs.mesh
ijk-hex-nurbs.mesh
plus-nurbs-permuted.mesh
plus-nurbs.mesh
square-nurbs.mesh
two-cubes-nurbs-autoedge.mesh
two-cubes-nurbs-rot.mesh
two-cubes-nurbs.mesh
two-squares-nurbs-autoedge.mesh
two-squares-nurbs-rot.mesh
two-squares-nurbs.mesh
)
# Add a target to copy the mesh files from the source directory; used by sample
# runs.
set(SRC_MESH_FILES)
foreach(MESH_FILE ${MESH_FILES})
list(APPEND SRC_MESH_FILES ${CMAKE_CURRENT_SOURCE_DIR}/meshes/${MESH_FILE})
endforeach()
add_custom_command(OUTPUT data_is_copied
# add target which keeps required mesh files in sync
file(GLOB SRC_MESH_FILES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/meshes/*)
add_custom_target(copy_miniapps_nurbs_data
COMMAND ${CMAKE_COMMAND} -E make_directory meshes
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_FILES} meshes/
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
COMMENT "Copying nurbs miniapps data files ...")
add_custom_target(copy_miniapps_nurbs_data DEPENDS data_is_copied)
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_FILES} meshes
COMMENT "Syncing nurbs miniapps data directory ...")
add_mfem_miniapp(nurbs_ex1
MAIN nurbs_ex1.cpp
@@ -50,6 +31,11 @@ add_mfem_miniapp(nurbs_ex5
LIBRARIES mfem)
add_dependencies(nurbs_ex5 copy_miniapps_nurbs_data)
add_mfem_miniapp(nurbs_ex10
MAIN nurbs_ex10.cpp
LIBRARIES mfem)
add_dependencies(nurbs_ex10 copy_miniapps_nurbs_data)
add_mfem_miniapp(nurbs_ex24
MAIN nurbs_ex24.cpp
LIBRARIES mfem)
@@ -70,6 +56,10 @@ add_mfem_miniapp(nurbs_printfunc
LIBRARIES mfem)
add_dependencies(nurbs_printfunc copy_miniapps_nurbs_data)
add_mfem_miniapp(nurbs_mesh_info
MAIN nurbs_mesh_info.cpp
LIBRARIES mfem)
add_mfem_miniapp(nurbs_patch_ex1
MAIN nurbs_patch_ex1.cpp
LIBRARIES mfem)
@@ -267,6 +257,11 @@ if (MFEM_USE_MPI)
LIBRARIES mfem)
add_dependencies(nurbs_ex1p copy_miniapps_nurbs_data)
add_mfem_miniapp(nurbs_ex10p
MAIN nurbs_ex10p.cpp
LIBRARIES mfem)
add_dependencies(nurbs_ex10p copy_miniapps_nurbs_data)
add_mfem_miniapp(nurbs_ex11p
MAIN nurbs_ex11p.cpp
LIBRARIES mfem)
+7 -4
View File
@@ -20,9 +20,11 @@ CONFIG_MK = $(or $(wildcard $(MFEM_BUILD_DIR)/config/config.mk),\
MFEM_LIB_FILE = mfem_is_not_built
-include $(CONFIG_MK)
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_ex3 nurbs_ex5 nurbs_ex24 \
nurbs_curveint nurbs_printfunc nurbs_solenoidal nurbs_naca_cmesh nurbs_surface
PAR_MINIAPPS = nurbs_ex1p nurbs_ex11p
SEQ_MINIAPPS = nurbs_ex1 nurbs_patch_ex1 nurbs_ex3 nurbs_ex5 nurbs_ex10 \
nurbs_ex24 nurbs_curveint nurbs_printfunc nurbs_solenoidal nurbs_naca_cmesh \
nurbs_mesh_info
PAR_MINIAPPS = nurbs_ex1p nurbs_ex10p nurbs_ex11p
ifeq ($(MFEM_USE_MPI),NO)
MINIAPPS = $(SEQ_MINIAPPS)
else
@@ -199,6 +201,7 @@ clean-build:
clean-exec:
@rm -f refined.mesh sin-fit.mesh ex5.mesh exsol.mesh mesh.* sol.* mode_*
@rm -f naca-cmesh.mesh sol_?.gf *-Surface.mesh
@rm -f naca-cmesh.mesh sol_?.gf k?_*.dat *-Surface.mesh
@rm -rf Example1* Example3* Example5* Solenoidal_* ParaView
@rm -rf CurveInt Naca_cmesh glvis_naca-cmesh.mesh solution.dat
@rm -rf velocity.* elastic_energy.* deformed.*
@@ -0,0 +1,76 @@
MFEM NURBS mesh v1.0
# Same as 3patch-nurbs.mesh but with some flipped edges
# This will fail to load without CorrectPatchTopoOrientations
dimension
2
elements
3
1 3 0 1 4 3
1 3 1 2 5 4
1 3 5 6 3 4
boundary
0
edges
9
2 1 0
1 1 4
2 4 3
1 0 3
0 1 2
1 5 2
0 5 4
2 5 6
0 3 6
vertices
7
patches
knotvectors
2
1 2 0 0 1 1
1 2 0 0 1 1
dimension
2
controlpoints
0 0 1
1 0 1
0 1 1
1 1 1
knotvectors
2
1 2 0 0 1 1
1 2 0 0 1 1
dimension
2
controlpoints
1 0 1
2 0 1
1 1 1
2 2 1
knotvectors
2
1 2 0 0 1 1
1 2 0 0 1 1
dimension
2
controlpoints
2 2 1
1 2 1
1 1 1
0 1 1
+72
View File
@@ -0,0 +1,72 @@
MFEM NURBS mesh v1.0
dimension
2
elements
3
1 3 0 1 4 3
1 3 1 2 5 4
1 3 5 6 3 4
boundary
0
edges
9
2 0 1
1 1 4
2 3 4
1 0 3
0 1 2
1 2 5
0 4 5
2 6 5
0 3 6
vertices
7
patches
knotvectors
2
1 2 0 0 1 1
1 2 0 0 1 1
dimension
2
controlpoints
0 0 1
1 0 1
0 1 1
1 1 1
knotvectors
2
1 2 0 0 1 1
1 2 0 0 1 1
dimension
2
controlpoints
1 0 1
2 0 1
1 1 1
2 2 1
knotvectors
2
1 2 0 0 1 1
1 2 0 0 1 1
dimension
2
controlpoints
2 2 1
1 2 1
1 1 1
0 1 1
+15 -21
View File
@@ -127,14 +127,6 @@ int main(int argc, char *argv[])
patch(1,1,0) = 0.5*l;
patch(1,1,1) = 0.5*l;
// 2. Interpolation process
Array<Vector*> xy(2);
xy[0] = new Vector();
xy[1] = new Vector();
Vector xi_args, u_args;
Array<int> i_args;
xy[0]->SetSize(ncp); xy[1]->SetSize(ncp);
// Refine direction which has fitting
if (!ifbspline)
{
@@ -150,24 +142,28 @@ int main(int argc, char *argv[])
}
patch.KnotInsert(0, *kv);
// We locate the control points at the location of the maxima of the
// knot vectors. This works very well for patches with unit weights.
kv->FindMaxima(i_args,xi_args, u_args);
// We locate the control points at the demko points.
Vector u(ncp),x(ncp),interp(ncp);
kv->GetDemko(u);
for (int i = 0; i < ncp; i++)
{
(*xy[0])[i] = u_args[i]*l;
(*xy[1])[i] = a * sin((*xy[0])[i]/l*2*M_PI)-0.5*l;
(*xy[0])[i] -= 0.5*l;
x[i] = (u[i] - 0.5)*l;
}
kv->GetInterpolant(x,u,interp);
for (int i = 0; i < ncp; i++)
{
patch(i,0,0) = interp[i];
}
kv->FindInterpolant(xy);
// Apply interpolation to patch
for (int i = 0; i < ncp; i++)
{
patch(i,0,0) = (*xy[0])[i];
patch(i,0,1) = (*xy[1])[i];
x[i] = a * sin(u[i]*2*M_PI)-0.5*l;
}
kv->GetInterpolant(x,u,interp);
for (int i = 0; i < ncp; i++)
{
patch(i,0,1) = interp[i];
}
if (!ifbspline)
@@ -243,8 +239,6 @@ int main(int argc, char *argv[])
delete mesh;
delete kv_o1;
delete kv;
delete xy[0];
delete xy[1];
return 0;
}
+34 -6
View File
@@ -139,10 +139,24 @@ public:
};
real_t sol(const Vector & x)
{
if (x.Size() >= 2)
{
if ((x[1] - x[0] - 0.5 < 0.0) &&
(x[0] + x[1] -0.99 < 0.0))
{
return 1.0;
}
}
return 0.0;
}
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../data/star.mesh";
const char *mesh_file = "../../data/square-nurbs.mesh";
const char *per_file = "none";
const char *ref_file = "";
int ref_levels = -1;
@@ -158,6 +172,7 @@ int main(int argc, char *argv[])
Array<int> order(1);
int visport = 19916;
order[0] = 1;
bool homogenousBC = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -174,11 +189,14 @@ int main(int argc, char *argv[])
"Slave boundaries for periodic BCs");
args.AddOption(&neu, "-n", "--neu",
"Boundaries with Neumann BCs");
args.AddOption(&homogenousBC, "-h", "--hom",
"-nh", "--no-hom",
"Selection for using homogenous Dirichelet boundary conditions.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree) or -1 for"
" isoparametric space.");
args.AddOption(&ibp, "-ibp", "--ibp", "-no-ibp",
"--no-ibp",
args.AddOption(&ibp, "-ibp", "--ibp",
"-no-ibp", "--no-ibp",
"Selects the standard weak form (IBP) or the nonstandard (NO-IBP).");
args.AddOption(&strongBC, "-sbc", "--strong-bc", "-wbc",
"--weak-bc",
@@ -408,10 +426,20 @@ int main(int argc, char *argv[])
b->Assemble();
// 7. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x with initial guess of zero,
// which satisfies the boundary conditions.
// corresponding to fespace. Initialize x with initial guess that
// satisfies the boundary conditions. Force the use of the ELEMENT
// projection type, also in the case of a NURBS spaces. For a NURBS space
// this will give a projection without any over and undershoots.
GridFunction x(fespace);
x = 0.0;
if (homogenousBC)
{
x = 0.0;
}
else
{
FunctionCoefficient sol_cf(sol);
x.ProjectCoefficient(sol_cf, ProjectType::ELEMENT);
}
// 8. Set up the bilinear form a(.,.) on the finite element space
// corresponding to the Laplacian operator -Delta, by adding the Diffusion
+613
View File
@@ -0,0 +1,613 @@
// MFEM Example 10 -- modified for NURBS FE
//
// Compile with: make nurbs_ex10
//
// Sample runs:
// nurbs_ex10 -m ../../data/beam-quad-nurbs.mesh -s 23 -r 2 -o 2 -dt 0.1
//
// Description: This examples solves a time dependent nonlinear elasticity
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
// hyperelastic model and S is a viscosity operator of Laplacian
// type. The geometry of the domain is assumed to be as follows:
//
// +---------------------+
// boundary --->| |
// attribute 1 | |
// (fixed) +---------------------+
//
// The example demonstrates the use of nonlinear operators (the
// class HyperelasticOperator defining H(x)), as well as their
// implicit time integration using a Newton method for solving an
// associated reduced backward-Euler type nonlinear equation
// (class ReducedSystemOperator). Each Newton step requires the
// inversion of a Jacobian matrix, which is done through a
// (preconditioned) inner solver. Note that implementing the
// method HyperelasticOperator::ImplicitSolve is the only
// requirement for high-order implicit (SDIRK) time integration.
//
// We recommend viewing examples 2 and 9 before viewing this
// example.
#include "mfem.hpp"
#include <memory>
#include <iostream>
#include <fstream>
using namespace std;
using namespace mfem;
class ReducedSystemOperator;
/** After spatial discretization, the hyperelastic model can be written as a
* system of ODEs:
* dv/dt = -M^{-1}*(H(x) + S*v)
* dx/dt = v,
* where x is the vector representing the deformation, v is the velocity field,
* M is the mass matrix, S is the viscosity matrix, and H(x) is the nonlinear
* hyperelastic operator.
*
* Class HyperelasticOperator represents the right-hand side of the above
* system of ODEs. */
class HyperelasticOperator : public TimeDependentOperator
{
protected:
FiniteElementSpace &fespace;
BilinearForm M, S;
NonlinearForm H;
real_t viscosity;
HyperelasticModel *model;
CGSolver M_solver; // Krylov solver for inverting the mass matrix M
DSmoother M_prec; // Preconditioner for the mass matrix M
/** Nonlinear operator defining the reduced backward Euler equation for the
velocity. Used in the implementation of method ImplicitSolve. */
ReducedSystemOperator *reduced_oper;
/// Newton solver for the reduced backward Euler equation
NewtonSolver newton_solver;
/// Solver for the Jacobian solve in the Newton method
Solver *J_solver;
/// Preconditioner for the Jacobian solve in the Newton method
Solver *J_prec;
mutable Vector z; // auxiliary vector
public:
HyperelasticOperator(FiniteElementSpace &f, Array<int> &ess_bdr,
real_t visc, real_t mu, real_t K);
/// Compute the right-hand side of the ODE system.
void Mult(const Vector &vx, Vector &dvx_dt) const override;
/** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
real_t ElasticEnergy(const Vector &x) const;
real_t KineticEnergy(const Vector &v) const;
void GetElasticEnergyDensity(const GridFunction &x,
GridFunction &w,
ProjectType proj_type) const;
~HyperelasticOperator() override;
};
/** Nonlinear operator of the form:
k --> (M + dt*S)*k + H(x + dt*v + dt^2*k) + S*v,
where M and S are given BilinearForms, H is a given NonlinearForm, v and x
are given vectors, and dt is a scalar. */
class ReducedSystemOperator : public Operator
{
private:
BilinearForm *M, *S;
NonlinearForm *H;
mutable SparseMatrix *Jacobian;
real_t dt;
const Vector *v, *x;
mutable Vector w, z;
public:
ReducedSystemOperator(BilinearForm *M_, BilinearForm *S_, NonlinearForm *H_);
/// Set current dt, v, x values - needed to compute action and Jacobian.
void SetParameters(real_t dt_, const Vector *v_, const Vector *x_);
/// Compute y = H(x + dt (v + dt k)) + M k + S (v + dt k).
void Mult(const Vector &k, Vector &y) const override;
/// Compute J = M + dt S + dt^2 grad_H(x + dt (v + dt k)).
Operator &GetGradient(const Vector &k) const override;
~ReducedSystemOperator() override;
};
/** Function representing the elastic energy density for the given hyperelastic
model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
class ElasticEnergyCoefficient : public Coefficient
{
private:
HyperelasticModel &model;
const GridFunction &x;
DenseMatrix J;
public:
ElasticEnergyCoefficient(HyperelasticModel &m, const GridFunction &x_)
: model(m), x(x_) { }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
~ElasticEnergyCoefficient() override { }
};
void InitialDeformation(const Vector &x, Vector &y);
void InitialVelocity(const Vector &x, Vector &v);
void visualize(ostream &os, Mesh *mesh, GridFunction *deformed_nodes,
GridFunction *field, const char *field_name = NULL,
bool init_vis = false);
int main(int argc, char *argv[])
{
// 1. Parse command-line options.
const char *mesh_file = "../../data/beam-quad-nurbs.mesh";
int ref_levels = 1;
int order = 2;
int ode_solver_type = 23;
real_t t_final = 0.5;
real_t dt = 0.1;
real_t visc = 1e-2;
real_t mu = 0.25;
real_t K = 5.0;
int proj_type_int = 0;
bool visualization = true;
int vis_steps = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
ODESolver::Types.c_str());
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&visc, "-v", "--viscosity",
"Viscosity coefficient.");
args.AddOption(&mu, "-mu", "--shear-modulus",
"Shear modulus in the Neo-Hookean hyperelastic model.");
args.AddOption(&K, "-K", "--bulk-modulus",
"Bulk modulus in the Neo-Hookean hyperelastic model.");
args.AddOption(&proj_type_int, "-proj", "--projection",
"Projection type:\n."
" 0 = DEFAULT: ELEMENTL2 for NURBS elements, ELEMENT else.\n"
" 1 = ELEMENT: As defined in the respective element.\n"
" 2 = GLOBALL2: Global L2 projection.\n"
" 3 = ELEMENTL2: Element L2 projection.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
ProjectType proj_type = static_cast<ProjectType>(proj_type_int);
// 2. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral and hexahedral meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 3. Define the ODE solver used for time integration. Several implicit
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
// explicit Runge-Kutta methods are available.
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
// 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement, where 'ref_levels' is a
// command-line parameter.
for (int lev = 0; lev < ref_levels; lev++)
{
mesh->UniformRefinement();
}
// 5. Define the vector finite element spaces representing the mesh
// deformation x, the velocity v, and the initial configuration, x_ref.
// Define also the elastic energy density, w, which is in a discontinuous
// higher-order space. Since x and v are integrated in time as a system,
// we group them together in block vector vx, with offsets given by the
// fe_offset array.
FiniteElementCollection *fec = nullptr;
NURBSExtension *NURBSext = nullptr;
if (mesh->NURBSext)
{
NURBSext = new NURBSExtension(mesh->NURBSext, order);
fec = new NURBSFECollection(order);
cout << "Using NURBS FEs: " << fec->Name() << endl;
}
else
{
fec = new H1_FECollection(order, dim);
cout << "Using H1 FEs: " << fec->Name() << endl;
}
FiniteElementSpace fespace(mesh, NURBSext, fec, dim);
int fe_size = fespace.GetTrueVSize();
cout << "Number of velocity/deformation unknowns: " << fe_size << endl;
Array<int> fe_offset(3);
fe_offset[0] = 0;
fe_offset[1] = fe_size;
fe_offset[2] = 2*fe_size;
BlockVector vx(fe_offset);
GridFunction v, x;
v.MakeTRef(&fespace, vx.GetBlock(0), 0);
x.MakeTRef(&fespace, vx.GetBlock(1), 0);
GridFunction x_ref(&fespace);
mesh->GetNodes(x_ref);
L2_FECollection w_fec(order + 1, dim);
FiniteElementSpace w_fespace(mesh, &w_fec);
GridFunction w(&w_fespace);
// 6. Set the initial conditions for v and x, and the boundary conditions on
// a beam-like mesh (see description above).
VectorFunctionCoefficient velo(dim, InitialVelocity);
v.ProjectCoefficient(velo, proj_type);
v.SetTrueVector();
VectorFunctionCoefficient deform(dim, InitialDeformation);
x.ProjectCoefficient(deform, proj_type);
x.SetTrueVector();
Array<int> ess_bdr(fespace.GetMesh()->bdr_attributes.Max());
ess_bdr = 0;
ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed
// 7. Initialize the hyperelastic operator, the GLVis visualization and print
// the initial energies.
HyperelasticOperator oper(fespace, ess_bdr, visc, mu, K);
socketstream vis_v, vis_w;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
vis_v.open(vishost, visport);
vis_v.precision(8);
v.SetFromTrueVector(); x.SetFromTrueVector();
visualize(vis_v, mesh, &x, &v, "Velocity", true);
vis_w.open(vishost, visport);
if (vis_w)
{
oper.GetElasticEnergyDensity(x, w, proj_type);
vis_w.precision(8);
visualize(vis_w, mesh, &x, &w, "Elastic energy density", true);
}
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
real_t ee0 = oper.ElasticEnergy(x.GetTrueVector());
real_t ke0 = oper.KineticEnergy(v.GetTrueVector());
cout << "initial elastic energy (EE) = " << ee0 << endl;
cout << "initial kinetic energy (KE) = " << ke0 << endl;
cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
real_t t = 0.0;
oper.SetTime(t);
ode_solver->Init(oper);
// 8. Perform time-integration (looping over the time iterations, ti, with a
// time-step dt).
bool last_step = false;
for (int ti = 1; !last_step; ti++)
{
real_t dt_real = min(dt, t_final - t);
ode_solver->Step(vx, t, dt_real);
last_step = (t >= t_final - 1e-8*dt);
if (last_step || (ti % vis_steps) == 0)
{
real_t ee = oper.ElasticEnergy(x.GetTrueVector());
real_t ke = oper.KineticEnergy(v.GetTrueVector());
cout << "step " << ti << ", t = " << t << ", EE = " << ee << ", KE = "
<< ke << ", ΔTE = " << (ee+ke)-(ee0+ke0) << endl;
if (visualization)
{
v.SetFromTrueVector(); x.SetFromTrueVector();
visualize(vis_v, mesh, &x, &v);
if (vis_w)
{
oper.GetElasticEnergyDensity(x, w, proj_type);
visualize(vis_w, mesh, &x, &w);
}
}
}
}
// 9. Save the displaced mesh, the velocity and elastic energy.
{
v.SetFromTrueVector(); x.SetFromTrueVector();
GridFunction *nodes = &x;
int owns_nodes = 0;
mesh->SwapNodes(nodes, owns_nodes);
ofstream mesh_ofs("deformed.mesh");
mesh_ofs.precision(8);
mesh->Print(mesh_ofs);
mesh->SwapNodes(nodes, owns_nodes);
ofstream velo_ofs("velocity.sol");
velo_ofs.precision(8);
v.Save(velo_ofs);
ofstream ee_ofs("elastic_energy.sol");
ee_ofs.precision(8);
oper.GetElasticEnergyDensity(x, w, proj_type);
w.Save(ee_ofs);
}
// 10. Free the used memory.
delete fec;
delete mesh;
return 0;
}
void visualize(ostream &os, Mesh *mesh, GridFunction *deformed_nodes,
GridFunction *field, const char *field_name, bool init_vis)
{
if (!os)
{
return;
}
GridFunction *nodes = deformed_nodes;
int owns_nodes = 0;
mesh->SwapNodes(nodes, owns_nodes);
os << "solution\n" << *mesh << *field;
mesh->SwapNodes(nodes, owns_nodes);
if (init_vis)
{
os << "window_size 800 800\n";
os << "window_title '" << field_name << "'\n";
if (mesh->SpaceDimension() == 2)
{
os << "view 0 0\n"; // view from top
os << "keys jl\n"; // turn off perspective and light
}
os << "keys cm\n"; // show colorbar and mesh
// update value-range; keep mesh-extents fixed
os << "autoscale value\n";
os << "pause\n";
}
os << flush;
}
ReducedSystemOperator::ReducedSystemOperator(
BilinearForm *M_, BilinearForm *S_, NonlinearForm *H_)
: Operator(M_->Height()), M(M_), S(S_), H(H_), Jacobian(NULL),
dt(0.0), v(NULL), x(NULL), w(height), z(height)
{ }
void ReducedSystemOperator::SetParameters(real_t dt_, const Vector *v_,
const Vector *x_)
{
dt = dt_; v = v_; x = x_;
}
void ReducedSystemOperator::Mult(const Vector &k, Vector &y) const
{
// compute: y = H(x + dt*(v + dt*k)) + M*k + S*(v + dt*k)
add(*v, dt, k, w);
add(*x, dt, w, z);
H->Mult(z, y);
M->AddMult(k, y);
S->AddMult(w, y);
}
Operator &ReducedSystemOperator::GetGradient(const Vector &k) const
{
delete Jacobian;
Jacobian = Add(1.0, M->SpMat(), dt, S->SpMat());
add(*v, dt, k, w);
add(*x, dt, w, z);
SparseMatrix *grad_H = dynamic_cast<SparseMatrix *>(&H->GetGradient(z));
Jacobian->Add(dt*dt, *grad_H);
return *Jacobian;
}
ReducedSystemOperator::~ReducedSystemOperator()
{
delete Jacobian;
}
HyperelasticOperator::HyperelasticOperator(FiniteElementSpace &f,
Array<int> &ess_bdr, real_t visc,
real_t mu, real_t K)
: TimeDependentOperator(2*f.GetTrueVSize(), (real_t) 0.0), fespace(f),
M(&fespace), S(&fespace), H(&fespace),
viscosity(visc), z(height/2)
{
#if defined(MFEM_USE_DOUBLE)
const real_t rel_tol = 1e-8;
const real_t newton_abs_tol = 0.0;
#elif defined(MFEM_USE_SINGLE)
const real_t rel_tol = 1e-3;
const real_t newton_abs_tol = 1e-4;
#else
#error "Only single and double precision are supported!"
const real_t rel_tol = real_t(1);
const real_t newton_abs_tol = real_t(0);
#endif
const int skip_zero_entries = 0;
const real_t ref_density = 1.0; // density in the reference configuration
ConstantCoefficient rho0(ref_density);
M.AddDomainIntegrator(new VectorMassIntegrator(rho0));
M.Assemble(skip_zero_entries);
Array<int> ess_tdof_list;
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
SparseMatrix tmp;
M.FormSystemMatrix(ess_tdof_list, tmp);
M_solver.iterative_mode = false;
M_solver.SetRelTol(rel_tol);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(30);
M_solver.SetPrintLevel(0);
M_solver.SetPreconditioner(M_prec);
M_solver.SetOperator(M.SpMat());
model = new NeoHookeanModel(mu, K);
H.AddDomainIntegrator(new HyperelasticNLFIntegrator(model));
H.SetEssentialTrueDofs(ess_tdof_list);
ConstantCoefficient visc_coeff(viscosity);
S.AddDomainIntegrator(new VectorDiffusionIntegrator(visc_coeff));
S.Assemble(skip_zero_entries);
S.FormSystemMatrix(ess_tdof_list, tmp);
reduced_oper = new ReducedSystemOperator(&M, &S, &H);
#ifndef MFEM_USE_SUITESPARSE
J_prec = new DSmoother(1);
MINRESSolver *J_minres = new MINRESSolver;
J_minres->SetRelTol(rel_tol);
J_minres->SetAbsTol(0.0);
J_minres->SetMaxIter(300);
J_minres->SetPrintLevel(-1);
J_minres->SetPreconditioner(*J_prec);
J_solver = J_minres;
#else
J_solver = new UMFPackSolver;
J_prec = NULL;
#endif
newton_solver.iterative_mode = false;
newton_solver.SetSolver(*J_solver);
newton_solver.SetOperator(*reduced_oper);
newton_solver.SetPrintLevel(1); // print Newton iterations
newton_solver.SetRelTol(rel_tol);
newton_solver.SetAbsTol(newton_abs_tol);
newton_solver.SetMaxIter(10);
}
void HyperelasticOperator::Mult(const Vector &vx, Vector &dvx_dt) const
{
// Create views to the sub-vectors v, x of vx, and dv_dt, dx_dt of dvx_dt
int sc = height/2;
Vector v(vx.GetData() + 0, sc);
Vector x(vx.GetData() + sc, sc);
Vector dv_dt(dvx_dt.GetData() + 0, sc);
Vector dx_dt(dvx_dt.GetData() + sc, sc);
H.Mult(x, z);
if (viscosity != 0.0)
{
S.AddMult(v, z);
}
z.Neg(); // z = -z
M_solver.Mult(z, dv_dt);
dx_dt = v;
}
void HyperelasticOperator::ImplicitSolve(const real_t dt,
const Vector &vx, Vector &dvx_dt)
{
int sc = height/2;
Vector v(vx.GetData() + 0, sc);
Vector x(vx.GetData() + sc, sc);
Vector dv_dt(dvx_dt.GetData() + 0, sc);
Vector dx_dt(dvx_dt.GetData() + sc, sc);
// By eliminating kx from the coupled system:
// kv = -M^{-1}*[H(x + dt*kx) + S*(v + dt*kv)]
// kx = v + dt*kv
// we reduce it to a nonlinear equation for kv, represented by the
// reduced_oper. This equation is solved with the newton_solver
// object (using J_solver and J_prec internally).
reduced_oper->SetParameters(dt, &v, &x);
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
newton_solver.Mult(zero, dv_dt);
MFEM_VERIFY(newton_solver.GetConverged(), "Newton solver did not converge.");
add(v, dt, dv_dt, dx_dt);
}
real_t HyperelasticOperator::ElasticEnergy(const Vector &x) const
{
return H.GetEnergy(x);
}
real_t HyperelasticOperator::KineticEnergy(const Vector &v) const
{
return 0.5*M.InnerProduct(v, v);
}
void HyperelasticOperator::GetElasticEnergyDensity(
const GridFunction &x, GridFunction &w, ProjectType proj_type) const
{
ElasticEnergyCoefficient w_coeff(*model, x);
w.ProjectCoefficient(w_coeff, proj_type);
}
HyperelasticOperator::~HyperelasticOperator()
{
delete J_solver;
delete J_prec;
delete reduced_oper;
delete model;
}
real_t ElasticEnergyCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
model.SetTransformation(T);
x.GetVectorGradient(T, J);
// return model.EvalW(J); // in reference configuration
return model.EvalW(J)/J.Det(); // in deformed configuration
}
void InitialDeformation(const Vector &x, Vector &y)
{
// set the initial configuration to be the same as the reference, stress
// free, configuration
y = x;
}
void InitialVelocity(const Vector &x, Vector &v)
{
const int dim = x.Size();
const real_t s = 0.1/64.;
v = 0.0;
v(dim-1) = s*x(0)*x(0)*(8.0-x(0));
v(0) = -s*x(0)*x(0);
}
+682
View File
@@ -0,0 +1,682 @@
// MFEM Example 10 - Parallel NURBS Version
//
// Compile with: make nurbs_ex10p
//
// Sample runs:
// mpirun -np 4 nurbs_ex10p -m ../../data/beam-quad-nurbs.mesh -s 23 -rs 2 -dt 3
//
// Description: This examples solves a time dependent nonlinear elasticity
// problem of the form dv/dt = H(x) + S v, dx/dt = v, where H is a
// hyperelastic model and S is a viscosity operator of Laplacian
// type. The geometry of the domain is assumed to be as follows:
//
// +---------------------+
// boundary --->| |
// attribute 1 | |
// (fixed) +---------------------+
//
// The example demonstrates the use of nonlinear operators (the
// class HyperelasticOperator defining H(x)), as well as their
// implicit time integration using a Newton method for solving an
// associated reduced backward-Euler type nonlinear equation
// (class ReducedSystemOperator). Each Newton step requires the
// inversion of a Jacobian matrix, which is done through a
// (preconditioned) inner solver. Note that implementing the
// method HyperelasticOperator::ImplicitSolve is the only
// requirement for high-order implicit (SDIRK) time integration.
//
// We recommend viewing examples 2 and 9 before viewing this
// example.
#include "mfem.hpp"
#include <memory>
#include <iostream>
#include <fstream>
using namespace std;
using namespace mfem;
class ReducedSystemOperator;
/** After spatial discretization, the hyperelastic model can be written as a
* system of ODEs:
* dv/dt = -M^{-1}*(H(x) + S*v)
* dx/dt = v,
* where x is the vector representing the deformation, v is the velocity field,
* M is the mass matrix, S is the viscosity matrix, and H(x) is the nonlinear
* hyperelastic operator.
*
* Class HyperelasticOperator represents the right-hand side of the above
* system of ODEs. */
class HyperelasticOperator : public TimeDependentOperator
{
protected:
ParFiniteElementSpace &fespace;
Array<int> ess_tdof_list;
ParBilinearForm M, S;
ParNonlinearForm H;
real_t viscosity;
HyperelasticModel *model;
HypreParMatrix *Mmat; // Mass matrix from ParallelAssemble()
CGSolver M_solver; // Krylov solver for inverting the mass matrix M
HypreSmoother M_prec; // Preconditioner for the mass matrix M
/** Nonlinear operator defining the reduced backward Euler equation for the
velocity. Used in the implementation of method ImplicitSolve. */
ReducedSystemOperator *reduced_oper;
/// Newton solver for the reduced backward Euler equation
NewtonSolver newton_solver;
/// Solver for the Jacobian solve in the Newton method
Solver *J_solver;
/// Preconditioner for the Jacobian solve in the Newton method
Solver *J_prec;
mutable Vector z; // auxiliary vector
public:
HyperelasticOperator(ParFiniteElementSpace &f, Array<int> &ess_bdr,
real_t visc, real_t mu, real_t K);
/// Compute the right-hand side of the ODE system.
void Mult(const Vector &vx, Vector &dvx_dt) const override;
/** Solve the Backward-Euler equation: k = f(x + dt*k, t), for the unknown k.
This is the only requirement for high-order SDIRK implicit integration.*/
void ImplicitSolve(const real_t dt, const Vector &x, Vector &k) override;
real_t ElasticEnergy(const ParGridFunction &x) const;
real_t KineticEnergy(const ParGridFunction &v) const;
void GetElasticEnergyDensity(const ParGridFunction &x,
ParGridFunction &w,
ProjectType proj_type) const;
~HyperelasticOperator() override;
};
/** Nonlinear operator of the form:
k --> (M + dt*S)*k + H(x + dt*v + dt^2*k) + S*v,
where M and S are given BilinearForms, H is a given NonlinearForm, v and x
are given vectors, and dt is a scalar. */
class ReducedSystemOperator : public Operator
{
private:
ParBilinearForm *M, *S;
ParNonlinearForm *H;
mutable HypreParMatrix *Jacobian;
real_t dt;
const Vector *v, *x;
mutable Vector w, z;
const Array<int> &ess_tdof_list;
public:
ReducedSystemOperator(ParBilinearForm *M_, ParBilinearForm *S_,
ParNonlinearForm *H_, const Array<int> &ess_tdof_list);
/// Set current dt, v, x values - needed to compute action and Jacobian.
void SetParameters(real_t dt_, const Vector *v_, const Vector *x_);
/// Compute y = H(x + dt (v + dt k)) + M k + S (v + dt k).
void Mult(const Vector &k, Vector &y) const override;
/// Compute J = M + dt S + dt^2 grad_H(x + dt (v + dt k)).
Operator &GetGradient(const Vector &k) const override;
~ReducedSystemOperator() override;
};
/** Function representing the elastic energy density for the given hyperelastic
model+deformation. Used in HyperelasticOperator::GetElasticEnergyDensity. */
class ElasticEnergyCoefficient : public Coefficient
{
private:
HyperelasticModel &model;
const ParGridFunction &x;
DenseMatrix J;
public:
ElasticEnergyCoefficient(HyperelasticModel &m, const ParGridFunction &x_)
: model(m), x(x_) { }
real_t Eval(ElementTransformation &T, const IntegrationPoint &ip) override;
~ElasticEnergyCoefficient() override { }
};
void InitialDeformation(const Vector &x, Vector &y);
void InitialVelocity(const Vector &x, Vector &v);
void visualize(ostream &os, ParMesh *mesh,
ParGridFunction *deformed_nodes,
ParGridFunction *field, const char *field_name = NULL,
bool init_vis = false);
int main(int argc, char *argv[])
{
// 1. Initialize MPI and HYPRE.
Mpi::Init(argc, argv);
int myid = Mpi::WorldRank();
Hypre::Init();
// 2. Parse command-line options.
const char *mesh_file = "../../data/beam-quad-nurbs.mesh";
int ser_ref_levels = 2;
int par_ref_levels = 0;
int order = 2;
int ode_solver_type = 23;
real_t t_final = 1.0;
real_t dt = 0.1;
real_t visc = 1e-2;
real_t mu = 0.25;
real_t K = 5.0;
int proj_type_int = 0;
bool adaptive_lin_rtol = true;
bool visualization = true;
int vis_steps = 1;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ser_ref_levels, "-rs", "--refine-serial",
"Number of times to refine the mesh uniformly in serial.");
args.AddOption(&par_ref_levels, "-rp", "--refine-parallel",
"Number of times to refine the mesh uniformly in parallel.");
args.AddOption(&order, "-o", "--order",
"Order (degree) of the finite elements.");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
ODESolver::Types.c_str());
args.AddOption(&t_final, "-tf", "--t-final",
"Final time; start time is 0.");
args.AddOption(&dt, "-dt", "--time-step",
"Time step.");
args.AddOption(&visc, "-v", "--viscosity",
"Viscosity coefficient.");
args.AddOption(&mu, "-mu", "--shear-modulus",
"Shear modulus in the Neo-Hookean hyperelastic model.");
args.AddOption(&K, "-K", "--bulk-modulus",
"Bulk modulus in the Neo-Hookean hyperelastic model.");
args.AddOption(&proj_type_int, "-proj", "--projection",
"Projection type:\n."
" 0 = DEFAULT: ELEMENTL2 for NURBS elements, ELEMENT else.\n"
" 1 = ELEMENT: As defined in the respective element.\n"
" 2 = GLOBALL2: Global L2 projection.\n"
" 3 = ELEMENTL2: Element L2 projection.");
args.AddOption(&adaptive_lin_rtol, "-alrtol", "--adaptive-lin-rtol",
"-no-alrtol", "--no-adaptive-lin-rtol",
"Enable or disable adaptive linear solver rtol.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.AddOption(&vis_steps, "-vs", "--visualization-steps",
"Visualize every n-th timestep.");
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
ProjectType proj_type = static_cast<ProjectType>(proj_type_int);
// 3. Read the serial mesh from the given mesh file on all processors. We can
// handle triangular, quadrilateral, tetrahedral and hexahedral meshes
// with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
// 4. Define the ODE solver used for time integration. Several implicit
// singly diagonal implicit Runge-Kutta (SDIRK) methods, as well as
// explicit Runge-Kutta methods are available.
unique_ptr<ODESolver> ode_solver = ODESolver::Select(ode_solver_type);
// 5. Refine the mesh in serial to increase the resolution. In this example
// we do 'ser_ref_levels' of uniform refinement, where 'ser_ref_levels' is
// a command-line parameter.
for (int lev = 0; lev < ser_ref_levels; lev++)
{
mesh->UniformRefinement();
}
// 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted.
ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
delete mesh;
for (int lev = 0; lev < par_ref_levels; lev++)
{
pmesh->UniformRefinement();
}
// 7. Define the parallel vector finite element spaces representing the mesh
// deformation x_gf, the velocity v_gf, and the initial configuration,
// x_ref. Define also the elastic energy density, w_gf, which is in a
// discontinuous higher-order space. Since x and v are integrated in time
// as a system, we group them together in block vector vx, on the unique
// parallel degrees of freedom, with offsets given by array true_offset.
FiniteElementCollection *fec = nullptr;
NURBSExtension *NURBSext = nullptr;
if (mesh->NURBSext)
{
NURBSext = new NURBSExtension(pmesh->NURBSext, order);
fec = new NURBSFECollection(order);
if (myid == 0) { cout << "Using NURBS FEs: " << fec->Name() << endl; }
}
else
{
fec = new H1_FECollection(order, dim);
if (myid == 0) { cout << "Using H1 FEs: " << fec->Name() << endl; }
}
ParFiniteElementSpace fespace(pmesh, NURBSext, fec, dim);
HYPRE_BigInt glob_size = fespace.GlobalTrueVSize();
if (myid == 0)
{
cout << "Number of velocity/deformation unknowns: " << glob_size << endl;
}
int true_size = fespace.TrueVSize();
Array<int> true_offset(3);
true_offset[0] = 0;
true_offset[1] = true_size;
true_offset[2] = 2*true_size;
BlockVector vx(true_offset);
ParGridFunction v_gf, x_gf;
v_gf.MakeTRef(&fespace, vx, true_offset[0]);
x_gf.MakeTRef(&fespace, vx, true_offset[1]);
ParGridFunction x_ref(&fespace);
pmesh->GetNodes(x_ref);
L2_FECollection w_fec(order + 1, dim);
ParFiniteElementSpace w_fespace(pmesh, &w_fec);
ParGridFunction w_gf(&w_fespace);
// 8. Set the initial conditions for v_gf, x_gf and vx, and define the
// boundary conditions on a beam-like mesh (see description above).
VectorFunctionCoefficient velo(dim, InitialVelocity);
v_gf.ProjectCoefficient(velo, proj_type);
v_gf.SetTrueVector();
VectorFunctionCoefficient deform(dim, InitialDeformation);
x_gf.ProjectCoefficient(deform, proj_type);
x_gf.SetTrueVector();
v_gf.SetFromTrueVector(); x_gf.SetFromTrueVector();
Array<int> ess_bdr(fespace.GetMesh()->bdr_attributes.Max());
ess_bdr = 0;
ess_bdr[0] = 1; // boundary attribute 1 (index 0) is fixed
// 9. Initialize the hyperelastic operator, the GLVis visualization and print
// the initial energies.
HyperelasticOperator oper(fespace, ess_bdr, visc, mu, K);
socketstream vis_v, vis_w;
if (visualization)
{
char vishost[] = "localhost";
int visport = 19916;
vis_v.open(vishost, visport);
vis_v.precision(8);
visualize(vis_v, pmesh, &x_gf, &v_gf, "Velocity", true);
// Make sure all ranks have sent their 'v' solution before initiating
// another set of GLVis connections (one from each rank):
MPI_Barrier(pmesh->GetComm());
vis_w.open(vishost, visport);
if (vis_w)
{
oper.GetElasticEnergyDensity(x_gf, w_gf, proj_type);
vis_w.precision(8);
visualize(vis_w, pmesh, &x_gf, &w_gf, "Elastic energy density", true);
}
if (myid == 0)
{
cout << "GLVis visualization paused."
<< " Press space (in the GLVis window) to resume it.\n";
}
}
real_t ee0 = oper.ElasticEnergy(x_gf);
real_t ke0 = oper.KineticEnergy(v_gf);
if (myid == 0)
{
cout << "initial elastic energy (EE) = " << ee0 << endl;
cout << "initial kinetic energy (KE) = " << ke0 << endl;
cout << "initial total energy (TE) = " << (ee0 + ke0) << endl;
}
real_t t = 0.0;
oper.SetTime(t);
ode_solver->Init(oper);
// 10. Perform time-integration
// (looping over the time iterations, ti, with a time-step dt).
bool last_step = false;
for (int ti = 1; !last_step; ti++)
{
real_t dt_real = min(dt, t_final - t);
ode_solver->Step(vx, t, dt_real);
last_step = (t >= t_final - 1e-8*dt);
if (last_step || (ti % vis_steps) == 0)
{
v_gf.SetFromTrueVector(); x_gf.SetFromTrueVector();
real_t ee = oper.ElasticEnergy(x_gf);
real_t ke = oper.KineticEnergy(v_gf);
if (myid == 0)
{
cout << "step " << ti << ", t = " << t << ", EE = " << ee
<< ", KE = " << ke << ", ΔTE = " << (ee+ke)-(ee0+ke0) << endl;
}
if (visualization)
{
visualize(vis_v, pmesh, &x_gf, &v_gf);
if (vis_w)
{
oper.GetElasticEnergyDensity(x_gf, w_gf, proj_type);
visualize(vis_w, pmesh, &x_gf, &w_gf);
}
}
}
}
// 11. Save the displaced mesh, the velocity and elastic energy.
{
v_gf.SetFromTrueVector(); x_gf.SetFromTrueVector();
GridFunction *nodes = &x_gf;
int owns_nodes = 0;
pmesh->SwapNodes(nodes, owns_nodes);
ostringstream mesh_name, velo_name, ee_name;
mesh_name << "deformed." << setfill('0') << setw(6) << myid;
velo_name << "velocity." << setfill('0') << setw(6) << myid;
ee_name << "elastic_energy." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->Print(mesh_ofs);
pmesh->SwapNodes(nodes, owns_nodes);
ofstream velo_ofs(velo_name.str().c_str());
velo_ofs.precision(8);
v_gf.Save(velo_ofs);
ofstream ee_ofs(ee_name.str().c_str());
ee_ofs.precision(8);
oper.GetElasticEnergyDensity(x_gf, w_gf, proj_type);
w_gf.Save(ee_ofs);
}
// 12. Free the used memory.
delete fec;
delete pmesh;
return 0;
}
void visualize(ostream &os, ParMesh *mesh,
ParGridFunction *deformed_nodes,
ParGridFunction *field, const char *field_name, bool init_vis)
{
if (!os)
{
return;
}
GridFunction *nodes = deformed_nodes;
int owns_nodes = 0;
mesh->SwapNodes(nodes, owns_nodes);
os << "parallel " << mesh->GetNRanks()
<< " " << mesh->GetMyRank() << "\n";
os << "solution\n" << *mesh << *field;
mesh->SwapNodes(nodes, owns_nodes);
if (init_vis)
{
os << "window_size 800 800\n";
os << "window_title '" << field_name << "'\n";
if (mesh->SpaceDimension() == 2)
{
os << "view 0 0\n"; // view from top
os << "keys jl\n"; // turn off perspective and light
}
os << "keys cm\n"; // show colorbar and mesh
// update value-range; keep mesh-extents fixed
os << "autoscale value\n";
os << "pause\n";
}
os << flush;
}
ReducedSystemOperator::ReducedSystemOperator(
ParBilinearForm *M_, ParBilinearForm *S_, ParNonlinearForm *H_,
const Array<int> &ess_tdof_list_)
: Operator(M_->ParFESpace()->TrueVSize()), M(M_), S(S_), H(H_),
Jacobian(NULL), dt(0.0), v(NULL), x(NULL), w(height), z(height),
ess_tdof_list(ess_tdof_list_)
{ }
void ReducedSystemOperator::SetParameters(real_t dt_, const Vector *v_,
const Vector *x_)
{
dt = dt_; v = v_; x = x_;
}
void ReducedSystemOperator::Mult(const Vector &k, Vector &y) const
{
// compute: y = H(x + dt*(v + dt*k)) + M*k + S*(v + dt*k)
add(*v, dt, k, w);
add(*x, dt, w, z);
H->Mult(z, y);
M->TrueAddMult(k, y);
S->TrueAddMult(w, y);
y.SetSubVector(ess_tdof_list, 0.0);
}
Operator &ReducedSystemOperator::GetGradient(const Vector &k) const
{
delete Jacobian;
SparseMatrix *localJ = Add(1.0, M->SpMat(), dt, S->SpMat());
add(*v, dt, k, w);
add(*x, dt, w, z);
localJ->Add(dt*dt, H->GetLocalGradient(z));
Jacobian = M->ParallelAssemble(localJ);
delete localJ;
HypreParMatrix *Je = Jacobian->EliminateRowsCols(ess_tdof_list);
delete Je;
return *Jacobian;
}
ReducedSystemOperator::~ReducedSystemOperator()
{
delete Jacobian;
}
HyperelasticOperator::HyperelasticOperator(ParFiniteElementSpace &f,
Array<int> &ess_bdr, real_t visc,
real_t mu, real_t K)
: TimeDependentOperator(2*f.TrueVSize(), (real_t) 0.0), fespace(f),
M(&fespace), S(&fespace), H(&fespace),
viscosity(visc), M_solver(f.GetComm()), newton_solver(f.GetComm()),
z(height/2)
{
#if defined(MFEM_USE_DOUBLE)
const real_t rel_tol = 1e-8;
const real_t newton_abs_tol = 0.0;
#elif defined(MFEM_USE_SINGLE)
const real_t rel_tol = 1e-3;
const real_t newton_abs_tol = 1e-4;
#else
#error "Only single and double precision are supported!"
const real_t rel_tol = real_t(1);
const real_t newton_abs_tol = real_t(0);
#endif
const int skip_zero_entries = 0;
const real_t ref_density = 1.0; // density in the reference configuration
ConstantCoefficient rho0(ref_density);
M.AddDomainIntegrator(new VectorMassIntegrator(rho0));
M.Assemble(skip_zero_entries);
M.Finalize(skip_zero_entries);
Mmat = M.ParallelAssemble();
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
HypreParMatrix *Me = Mmat->EliminateRowsCols(ess_tdof_list);
delete Me;
M_solver.iterative_mode = false;
M_solver.SetRelTol(rel_tol);
M_solver.SetAbsTol(0.0);
M_solver.SetMaxIter(30);
M_solver.SetPrintLevel(0);
M_prec.SetType(HypreSmoother::Jacobi);
M_solver.SetPreconditioner(M_prec);
M_solver.SetOperator(*Mmat);
model = new NeoHookeanModel(mu, K);
H.AddDomainIntegrator(new HyperelasticNLFIntegrator(model));
H.SetEssentialTrueDofs(ess_tdof_list);
ConstantCoefficient visc_coeff(viscosity);
S.AddDomainIntegrator(new VectorDiffusionIntegrator(visc_coeff));
S.Assemble(skip_zero_entries);
S.Finalize(skip_zero_entries);
reduced_oper = new ReducedSystemOperator(&M, &S, &H, ess_tdof_list);
HypreSmoother *J_hypreSmoother = new HypreSmoother;
J_hypreSmoother->SetType(HypreSmoother::l1Jacobi);
J_hypreSmoother->SetPositiveDiagonal(true);
J_prec = J_hypreSmoother;
MINRESSolver *J_minres = new MINRESSolver(f.GetComm());
J_minres->SetRelTol(rel_tol);
J_minres->SetAbsTol(0.0);
J_minres->SetMaxIter(300);
J_minres->SetPrintLevel(-1);
J_minres->SetPreconditioner(*J_prec);
J_solver = J_minres;
newton_solver.iterative_mode = false;
newton_solver.SetSolver(*J_solver);
newton_solver.SetOperator(*reduced_oper);
newton_solver.SetPrintLevel(1); // print Newton iterations
newton_solver.SetRelTol(rel_tol);
newton_solver.SetAbsTol(newton_abs_tol);
newton_solver.SetAdaptiveLinRtol(2, 0.5, 0.9);
newton_solver.SetMaxIter(10);
}
void HyperelasticOperator::Mult(const Vector &vx, Vector &dvx_dt) const
{
// Create views to the sub-vectors v, x of vx, and dv_dt, dx_dt of dvx_dt
int sc = height/2;
Vector v(vx.GetData() + 0, sc);
Vector x(vx.GetData() + sc, sc);
Vector dv_dt(dvx_dt.GetData() + 0, sc);
Vector dx_dt(dvx_dt.GetData() + sc, sc);
H.Mult(x, z);
if (viscosity != 0.0)
{
S.TrueAddMult(v, z);
z.SetSubVector(ess_tdof_list, 0.0);
}
z.Neg(); // z = -z
M_solver.Mult(z, dv_dt);
dx_dt = v;
}
void HyperelasticOperator::ImplicitSolve(const real_t dt,
const Vector &vx, Vector &dvx_dt)
{
int sc = height/2;
Vector v(vx.GetData() + 0, sc);
Vector x(vx.GetData() + sc, sc);
Vector dv_dt(dvx_dt.GetData() + 0, sc);
Vector dx_dt(dvx_dt.GetData() + sc, sc);
// By eliminating kx from the coupled system:
// kv = -M^{-1}*[H(x + dt*kx) + S*(v + dt*kv)]
// kx = v + dt*kv
// we reduce it to a nonlinear equation for kv, represented by the
// reduced_oper. This equation is solved with the newton_solver
// object (using J_solver and J_prec internally).
reduced_oper->SetParameters(dt, &v, &x);
Vector zero; // empty vector is interpreted as zero r.h.s. by NewtonSolver
newton_solver.Mult(zero, dv_dt);
MFEM_VERIFY(newton_solver.GetConverged(), "Newton solver did not converge.");
add(v, dt, dv_dt, dx_dt);
}
real_t HyperelasticOperator::ElasticEnergy(const ParGridFunction &x) const
{
return H.GetEnergy(x);
}
real_t HyperelasticOperator::KineticEnergy(const ParGridFunction &v) const
{
real_t energy = 0.5*M.ParInnerProduct(v, v);
return energy;
}
void HyperelasticOperator::GetElasticEnergyDensity(
const ParGridFunction &x, ParGridFunction &w, ProjectType proj_type) const
{
ElasticEnergyCoefficient w_coeff(*model, x);
w.ProjectCoefficient(w_coeff, proj_type);
}
HyperelasticOperator::~HyperelasticOperator()
{
delete J_solver;
delete J_prec;
delete reduced_oper;
delete model;
delete Mmat;
}
real_t ElasticEnergyCoefficient::Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{
model.SetTransformation(T);
x.GetVectorGradient(T, J);
// return model.EvalW(J); // in reference configuration
return model.EvalW(J)/J.Det(); // in deformed configuration
}
void InitialDeformation(const Vector &x, Vector &y)
{
// set the initial configuration to be the same as the reference, stress
// free, configuration
y = x;
}
void InitialVelocity(const Vector &x, Vector &v)
{
const int dim = x.Size();
const real_t s = 0.1/64.;
v = 0.0;
v(dim-1) = s*x(0)*x(0)*(8.0-x(0));
v(0) = -s*x(0)*x(0);
}
+49 -39
View File
@@ -1,4 +1,4 @@
// MFEM Example 24 -- modified for NURBS FE
// MFEM Example 24 -- modified for NURBS FE
//
// Compile with: make nurbs_ex24
//
@@ -43,6 +43,8 @@ void gradp_exact(const Vector &, Vector &);
real_t div_gradp_exact(const Vector &x);
void v_exact(const Vector &x, Vector &v);
void curlv_exact(const Vector &x, Vector &cv);
template <typename CoefficientType>
void Project(GridFunction &gf, CoefficientType &coef, int proj_type);
int dim;
real_t freq = 1.0, kappa;
@@ -57,6 +59,7 @@ int main(int argc, char *argv[])
int prob = 0;
bool static_cond = false;
bool pa = false;
int proj_type_int = 0;
const char *device_config = "cpu";
int visport = 19916;
bool visualization = 1;
@@ -68,14 +71,20 @@ int main(int argc, char *argv[])
"Number of times to refine the mesh uniformly, -1 for auto.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&NURBS, "-n", "--nurbs", "-nn","--no-nurbs",
"NURBS.");
args.AddOption(&NURBS, "-n", "--nurbs", "-nn", "--no-nurbs",
"Use NURBS spaces if the mesh is a NURBS mesh.");
args.AddOption(&prob, "-p", "--problem-type",
"Choose between 0: grad, 1: curl, 2: div");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&proj_type_int, "-proj", "--projection",
"Projection type:\n."
" 0 = DEFAULT: ELEMENTL2 for NURBS elements, ELEMENT else.\n"
" 1 = ELEMENT: As defined in the respective element.\n"
" 2 = GLOBALL2: Global L2 projection.\n"
" 3 = ELEMENTL2: Element L2 projection.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -86,10 +95,11 @@ int main(int argc, char *argv[])
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
args.PrintUsage(mfem::out);
return 1;
}
args.PrintOptions(cout);
args.PrintOptions(mfem::out);
ProjectType proj_type = static_cast<ProjectType>(proj_type_int);
kappa = freq * M_PI;
// 2. Enable hardware devices such as GPUs, and programming models such as
@@ -102,7 +112,7 @@ int main(int argc, char *argv[])
// the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
dim = mesh->Dimension();
if ((prob == 1) &&(dim != 3))
if ((prob == 1) && (dim != 3))
{
MFEM_ABORT("The curl problem is only defined in 3D.");
}
@@ -123,17 +133,17 @@ int main(int argc, char *argv[])
}
}
// 5. Define a finite element space on the mesh. Here we use Nedelec or
// Raviart-Thomas finite elements of the specified order.
// 5. Define a finite element space on the mesh. Here we use H^1, H(Div) or
// H(Curl) finite elements of the specified order.
FiniteElementCollection *trial_fec = nullptr;
FiniteElementCollection *test_fec = nullptr;
NURBSExtension *NURBSext = nullptr;
if (mesh->NURBSext && NURBS)
{
NURBSext = new NURBSExtension(mesh->NURBSext, order);
NURBSext = new NURBSExtension(mesh->NURBSext, order);
if (prob == 0)
{
trial_fec = new NURBSFECollection(order);
trial_fec = new NURBSFECollection(order);
test_fec = new NURBS_HCurlFECollection(order, dim);
}
else if (prob == 1)
@@ -146,7 +156,7 @@ int main(int argc, char *argv[])
trial_fec = new NURBS_HDivFECollection(order, dim);
test_fec = new NURBSFECollection(order);
}
mfem::out<<"Create NURBS fec and ext"<<std::endl;
mfem::out << "Create NURBS finite element" << endl;
}
else
{
@@ -165,6 +175,7 @@ int main(int argc, char *argv[])
trial_fec = new RT_FECollection(order-1, dim);
test_fec = new L2_FECollection(order-1, dim);
}
mfem::out << "Create standard finite elements" << endl;
}
FiniteElementSpace trial_fes(mesh, NURBSext, trial_fec);
@@ -175,20 +186,20 @@ int main(int argc, char *argv[])
if (prob == 0)
{
cout << "Number of Nedelec finite element unknowns: " << test_size << endl;
cout << "Number of H1 finite element unknowns: " << trial_size << endl;
mfem::out << "Number of HCurl finite element unknowns: " << test_size << endl;
mfem::out << "Number of H1 finite element unknowns: " << trial_size << endl;
}
else if (prob == 1)
{
cout << "Number of Nedelec finite element unknowns: " << trial_size << endl;
cout << "Number of Raviart-Thomas finite element unknowns: " << test_size <<
endl;
mfem::out << "Number of HCurl finite element unknowns: " << trial_size << endl;
mfem::out << "Number of HDiv finite element unknowns: " << test_size
<< endl;
}
else
{
cout << "Number of Raviart-Thomas finite element unknowns: "
<< trial_size << endl;
cout << "Number of L2 finite element unknowns: " << test_size << endl;
mfem::out << "Number of HDiv finite element unknowns: "
<< trial_size << endl;
mfem::out << "Number of L2 finite element unknowns: " << test_size << endl;
}
// 6. Define the solution vector as a finite element grid function
@@ -204,17 +215,16 @@ int main(int argc, char *argv[])
if (prob == 0)
{
gftrial.ProjectCoefficient(p_coef);
gftrial.ProjectCoefficient(p_coef, proj_type);
}
else if (prob == 1)
{
gftrial.ProjectCoefficient(v_coef);
gftrial.ProjectCoefficient(v_coef, proj_type);
}
else
{
gftrial.ProjectCoefficient(gradp_coef);
gftrial.ProjectCoefficient(gradp_coef, proj_type);
}
gftrial.SetTrueVector();
gftrial.SetFromTrueVector();
@@ -293,6 +303,7 @@ int main(int argc, char *argv[])
cg.SetOperator(Amat);
cg.SetPreconditioner(Jacobi);
cg.Mult(rhs, x);
}
}
@@ -300,17 +311,16 @@ int main(int argc, char *argv[])
GridFunction exact_proj(&test_fes);
if (prob == 0)
{
exact_proj.ProjectCoefficient(gradp_coef);
exact_proj.ProjectCoefficient(gradp_coef, proj_type);
}
else if (prob == 1)
{
exact_proj.ProjectCoefficient(curlv_coef);
exact_proj.ProjectCoefficient(curlv_coef, proj_type);
}
else
{
exact_proj.ProjectCoefficient(divgradp_coef);
exact_proj.ProjectCoefficient(divgradp_coef, proj_type);
}
exact_proj.SetTrueVector();
exact_proj.SetFromTrueVector();
@@ -320,20 +330,20 @@ int main(int argc, char *argv[])
real_t errSol = x.ComputeL2Error(gradp_coef);
real_t errProj = exact_proj.ComputeL2Error(gradp_coef);
cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl): "
"|| E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
cout << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
"||_{L_2} = " << errProj << '\n' << endl;
mfem::out << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl): "
"|| E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
mfem::out << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
"||_{L_2} = " << errProj << '\n' << endl;
}
else if (prob == 1)
{
real_t errSol = x.ComputeL2Error(curlv_coef);
real_t errProj = exact_proj.ComputeL2Error(curlv_coef);
cout << "\n Solution of (E_h,w) = (curl v_h,w) for E_h and w in H(div): "
"|| E_h - curl v ||_{L_2} = " << errSol << '\n' << endl;
cout << " Projection E_h of exact curl v in H(div): || E_h - curl v "
"||_{L_2} = " << errProj << '\n' << endl;
mfem::out << "\n Solution of (E_h,w) = (curl v_h,w) for E_h and w in H(div): "
"|| E_h - curl v ||_{L_2} = " << errSol << '\n' << endl;
mfem::out << " Projection E_h of exact curl v in H(div): || E_h - curl v "
"||_{L_2} = " << errProj << '\n' << endl;
}
else
{
@@ -347,11 +357,11 @@ int main(int argc, char *argv[])
real_t errSol = x.ComputeL2Error(divgradp_coef, irs);
real_t errProj = exact_proj.ComputeL2Error(divgradp_coef, irs);
cout << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
"|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
mfem::out << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
"|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
cout << " Projection f_h of exact div v in L_2: || f_h - div v "
"||_{L_2} = " << errProj << '\n' << endl;
mfem::out << " Projection f_h of exact div v in L_2: || f_h - div v "
"||_{L_2} = " << errProj << '\n' << endl;
}
// 12. Save the refined mesh and the solution. This output can be viewed
+159
View File
@@ -0,0 +1,159 @@
// MFEM Print info of NURBS mesh
//
// Compile with: make nurbs_mesh_info
//
// Sample runs:
// nurbs_mesh_info -m ../../data/cube-nurbs.mesh -o 0 -r 2
//
// Description: This code prints detailed mesh information such as:
// - Print separate patch info
// - 1D shape functions associated knot vectors
// - Give Greville, Botella and Demko points of the knot vectors
#include <iostream>
#include "mfem.hpp"
using namespace std;
using namespace mfem;
int main(int argc, char *argv[])
{
// Read parameters from command line
const char *mesh_file = "../../data/square-nurbs.mesh";
const char *ref_file = "";
int ref_levels = -1;
int order = 1;
bool visualization = true;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
args.AddOption(&ref_levels, "-r", "--refine",
"Number of times to refine the mesh uniformly, -1 for auto.");
args.AddOption(&ref_file, "-rf", "--ref-file",
"File with refinement data");
args.AddOption(&order, "-o", "--order",
"NURBS order (polynomial degree) or -1 for");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization."); // Dummy arg for `make test`
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
// Read the mesh
Mesh mesh(mesh_file, 1, 1);
NURBSExtension *ext = mesh.NURBSext;
if (!ext)
{
mfem_error("Mesh is not a NURBS mesh.");
}
// Refine the mesh as specified
mesh.DegreeElevate(16, order);
if (mesh.NURBSext && (strlen(ref_file) != 0))
{
mesh.RefineNURBSFromFile(ref_file);
}
for (int l = 0; l < ref_levels; l++)
{
mesh.UniformRefinement();
}
// Print mesh info
mesh.PrintInfo();
// Print patch info
mfem::out<<"=======================================;"<<endl;
mfem::out<<" Patch info"<<endl;
mfem::out<<"=======================================;"<<endl;
for (int p = 0; p < ext->GetNP(); p++)
{
Array<const KnotVector *> kv;
ext->GetPatchKnotVectors(p, kv);
mfem::out<<p<<": Order = "<<kv[0]->GetOrder();
for (int k = 1; k < kv.Size(); k++)
{
mfem::out<<"x"<<kv[k]->GetOrder();
}
mfem::out<<" : DOFs = "<<kv[0]->GetNCP();
for (int k = 1; k < kv.Size(); k++)
{
mfem::out<<"x"<<kv[k]->GetNCP();
}
mfem::out<<endl;
}
// Print knotvector info
for (int k = 0; k < ext->GetNKV() ; k++)
{
mfem::out<<"=======================================;"<<endl;
mfem::out<<" KnotVector "<<k<<endl;
mfem::out<<"=======================================;"<<endl;
const KnotVector &kv = *ext->GetKnotVector(k);
mfem::out<<"Knotvector : "; kv.Print(mfem::out);
std::string gnuplot = "plot 0";
Vector a(kv.GetNCP());
for (int i = 0; i < kv.GetNCP(); i++)
{
a = 0.0;
a[i] = 1.0;
std::string filename = "k" + std::to_string(k) +"_n" + std::to_string(
i) + ".dat";
mfem::out<<"Write shape function to: "<<filename<<"\n";
std::ofstream ofs(filename);
kv.PrintFunction(ofs, a, 201);
ofs.close();
gnuplot += ", '" + filename+"' u 1:2 w l";
}
mfem::out<<gnuplot<<endl;
// Greville
Vector greville(kv.GetNCP());
for (int i = 0; i < kv.GetNCP(); i++)
{
greville[i] = kv.GetGreville(i);
}
mfem::out<<"Greville points : "; greville.Print(mfem::out, 32);
// Botella
Vector botella(kv.GetNCP());
for (int i = 0; i < kv.GetNCP(); i++)
{
botella[i] = kv.GetBotella(i);
}
mfem::out<<"Botella points : "; botella.Print(mfem::out, 32);
// Demko
Vector demko(kv.GetNCP());
for (int i = 0; i < kv.GetNCP(); i++)
{
demko[i] = kv.GetDemko(i);
}
mfem::out<<"Demko points : "; demko.Print(mfem::out, 32);
// Chebyshev spline
Vector x(kv.GetNCP());
for ( int i = 0; i <kv.GetNCP(); i++)
{
x[i] = std::pow(-1.0, i);
}
kv.GetInterpolant(x, demko, a);
mfem::out<<"Chebyshev spline coeff : "; a.Print(mfem::out, 32);
std::string filename = "k" + std::to_string(k) +"_cheby.dat";
mfem::out<<"Write Chebyshev spline to: "<<filename<<"\n";
std::ofstream ofs(filename);
kv.PrintFunction(ofs, a, 201);
ofs.close();
}
}
+73 -48
View File
@@ -88,7 +88,7 @@ public:
// with an even number of control points. These may be streamlined in the
// future.
void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
Array<Vector*> &xy);
Vector &x, Vector &y);
// Function that returns a uniform knot vector based on the @a order and the
// number of control points @a ncp.
@@ -221,12 +221,8 @@ int main(int argc, char *argv[])
unique_ptr<KnotVector> kv_o1 = UniformKnotVector(1, 2);
unique_ptr<KnotVector> kv_o2 = UniformKnotVector(2, 3);
// Variables required for curve interpolation
Vector xi_args, u_args;
Array<int> i_args;
Array<Vector*> xyf(2);
xyf[0] = new Vector();
xyf[1] = new Vector();
// 3. Create required (variables for) curves: foil_section and flair
const NACA4 foil_section(foil_thickness, foil_length);
@@ -301,21 +297,32 @@ int main(int argc, char *argv[])
patch1.KnotInsert(0, *kv1);
int ncp = kv1->GetNCP();
xyf[0]->SetSize(ncp); xyf[1]->SetSize(ncp);
// Project foil
kv1->FindMaxima(i_args,xi_args, u_args);
// We locate the control points at the location of the maxima of the
// shapefunctions defined by the knot vectors -- the Botella points.
Vector u(ncp),x(ncp),y(ncp),interp(ncp);
for (int i = 0; i < ncp; i++)
{
(*xyf[0])[i] = foil_length*(1.0 - tail_fraction*u_args[i]);
(*xyf[1])[i] = -foil_section.y((*xyf[0])[i]);
u[i] = kv1->GetBotella(i);
}
kv1->FindInterpolant(xyf);
for (int i = 0; i < ncp; i++)
{
patch1(i,0,0) = (*xyf[0])[i];
patch1(i,0,1) = (*xyf[1])[i];
x[i] = foil_length*(1.0 - tail_fraction*u[i]);
}
kv1->GetInterpolant(x,u,interp);
for (int i = 0; i < ncp; i++)
{
patch1(i,0,0) = interp[i];
}
for (int i = 0; i < ncp; i++)
{
y[i] = -foil_section.y(x[i]);
}
kv1->GetInterpolant(y,u,interp);
for (int i = 0; i < ncp; i++)
{
patch1(i,0,1) = interp[i];
}
patch1.DegreeElevate(1, order-1);
@@ -368,17 +375,24 @@ int main(int argc, char *argv[])
// Project foil
int ncp = kv2->GetNCP();
xyf[0]->SetSize(ncp); xyf[1]->SetSize(ncp);
Vector x(ncp), y(ncp);
GetTipXY(foil_section, *kv2, tip_fraction, xyf);
GetTipXY(foil_section, *kv2, tip_fraction,x,y);
kv2->FindInterpolant(xyf);
Vector u(ncp),interp(ncp);
for (int i = 0; i < ncp; i++)
{
// Also deal with non-uniform weights here: convert to homogeneous
// coordinates
patch2(i,0,0) = (*xyf[0])[i]*patch2(i,0,2);
patch2(i,0,1) = (*xyf[1])[i]*patch2(i,0,2);
u[i] = kv2->GetBotella(i);
}
kv2->GetInterpolant(x,u,interp);
for (int i = 0; i < ncp; i++)
{
patch2(i,0,0) = interp[i]*patch2(i,0,2);
}
kv2->GetInterpolant(y,u,interp);
for (int i = 0; i < ncp; i++)
{
patch2(i,0,1) = interp[i]*patch2(i,0,2);
}
// Project circle
@@ -413,21 +427,31 @@ int main(int argc, char *argv[])
patch3.KnotInsert(0, *kv3);
int ncp = kv3->GetNCP();
xyf[0]->SetSize(ncp); xyf[1]->SetSize(ncp);
// Project foil
kv3->FindMaxima(i_args,xi_args, u_args);
Vector u(ncp),x(ncp),y(ncp),interp(ncp);
for (int i = 0; i < ncp; i++)
{
(*xyf[0])[i] = foil_length*(tip_fraction + tail_fraction*u_args[i]);
(*xyf[1])[i] = foil_section.y((*xyf[0])[i]);
u[i] = kv3->GetBotella(i);
}
kv3->FindInterpolant(xyf);
for (int i = 0; i < ncp; i++)
{
patch3(i,0,0) = (*xyf[0])[i];
patch3(i,0,1) = (*xyf[1])[i];
x[i] = foil_length*(tip_fraction + tail_fraction*u[i]);
}
kv3->GetInterpolant(x,u,interp);
for (int i = 0; i < ncp; i++)
{
patch3(i,0,0) = interp[i];
}
for (int i = 0; i < ncp; i++)
{
y[i] = foil_section.y(x[i]);
}
kv3->GetInterpolant(y,u,interp);
for (int i = 0; i < ncp; i++)
{
patch3(i,0,1) = interp[i];
}
patch3.DegreeElevate(1, order-1);
@@ -557,8 +581,6 @@ int main(int argc, char *argv[])
// Close
output.close();
delete xyf[0];
delete xyf[1];
cout << endl << "Boundary identifiers:" << endl;
cout << " 1 Bottom" << endl;
@@ -657,16 +679,22 @@ real_t NACA4::xl(real_t l) const
}
void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
Array<Vector*> &xy)
Vector &x,Vector &y)
{
int ncp = kv.GetNCP();
// Length of half the curve: the boundary covers both sides of the tip
const real_t l = foil_section.len(tf * foil_section.GetChord());
// Find location of maxima of knot vector
Array<int> i_args;
Vector xi_args, u_args;
kv.FindMaxima(i_args,xi_args, u_args);
Array<int> i_args(ncp);
Vector xi_args(ncp), u_args(ncp);
// kv.FindMaxima(i_args,xi_args, u_args);
for (int i = 0; i < ncp; i++)
{
u_args[i] = kv.GetBotella(i);
i_args[i] = kv.GetSpan(u_args[i]) - kv.GetOrder();
xi_args[i] = kv.GetRefPoint(u_args[i],i_args[i]+kv.GetOrder());
}
// We have two cases: one with an odd number of control points and one
// with an even number of control points.
@@ -684,17 +712,16 @@ void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
}
// Find corresponding xy vector
xy[0]->SetSize(2*n+1); xy[1]->SetSize(2*n+1);
xy[0]->Elem(n) = 0; xy[1]->Elem(n) = 0; // Foil section tip
x[n] = 0; y[n] = 0; // Foil section tip
for (int i = 0; i < n; i++)
{
// Lower half
xy[0]->Elem(i) = xcp[n-i];
xy[1]->Elem(i) = -foil_section.y(xcp[n-i]);
x[i] = xcp[n-i];
y[i] = -foil_section.y(xcp[n-i]);
// Upper half
xy[0]->Elem(n+1+i) = xcp[i+1];
xy[1]->Elem(n+1+i) = foil_section.y(xcp[i+1]);
x[n+1+i] = xcp[i+1];
y[n+1+i] = foil_section.y(xcp[i+1]);
}
}
else
@@ -708,18 +735,16 @@ void GetTipXY(const NACA4 &foil_section, const KnotVector &kv, real_t tf,
real_t lcp = u * l;
xcp[i] = foil_section.xl(lcp);
}
// Find corresponding xy vector
xy[0]->SetSize(2*n); xy[1]->SetSize(2*n);
for (int i = 0; i < n; i++)
{
// Lower half
xy[0]->Elem(i) = xcp[n-1-i];
xy[1]->Elem(i) = -foil_section.y(xcp[n-1-i]);
x[i] = xcp[n-1-i];
y[i] = -foil_section.y(xcp[n-1-i]);
// Upper half
xy[0]->Elem(n+i) = xcp[i];
xy[1]->Elem(n+i) = foil_section.y(xcp[i]);
x[n+i] = xcp[i];
y[n+i] = foil_section.y(xcp[i]);
}
}
}
+3 -5
View File
@@ -458,11 +458,9 @@ SurfaceInterpolator::SurfaceInterpolator(int num_elem_x, int num_elem_y,
hy = 1.0 / (real_t) (ncp[1] - 1);
hz = 1.0 / (real_t) (ncp[2] - 1);
Vector xi_args;
Array<int> i_args;
for (int i = 0; i < 2; ++i)
{
kv[i].FindMaxima(i_args, xi_args, ugrid[i]);
kv[i].GetDemko(ugrid[i]);
}
}
@@ -533,7 +531,7 @@ void SurfaceInterpolator::ComputeNURBS(int coordinate,
}
const bool reuse_factorization = j > 0;
kv[0].FindInterpolant(x, reuse_factorization);
kv[0].GetInterpolant(x,ugrid[0], reuse_factorization);
for (int i = 0; i < ncp[0]; i++)
{
@@ -558,7 +556,7 @@ void SurfaceInterpolator::ComputeNURBS(int coordinate,
}
const bool reuse_factorization = i > 0;
kv[1].FindInterpolant(x, reuse_factorization);
kv[1].GetInterpolant(x, ugrid[1], reuse_factorization);
for (int j = 0; j < ncp[1]; ++j)
{
+3 -5
View File
@@ -20,11 +20,9 @@ set(SRC_MESH_GF_FILES)
foreach(MESH_FILE ${MESH_GF_FILES})
list(APPEND SRC_MESH_GF_FILES ${CMAKE_CURRENT_SOURCE_DIR}/${MESH_FILE})
endforeach()
add_custom_command(OUTPUT data_is_copied
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_GF_FILES} ../gslib/.
COMMAND ${CMAKE_COMMAND} -E touch data_is_copied
COMMENT "Copying tools miniapps data files ...")
add_custom_target(copy_miniapps_tools_data DEPENDS data_is_copied)
add_custom_target(copy_miniapps_tools_data
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SRC_MESH_GF_FILES} .
COMMENT "Syncing tools miniapps data files ...")
add_mfem_miniapp(display-basis
MAIN display-basis.cpp
+2 -2
View File
@@ -27,8 +27,8 @@ accelerations, so the relationship between forces/contact pressures and
deformations/contact gaps is linear and, therefore, the problem can be solved
exactly with a single linear solve. The mortar implementation is based on [Puso
and Laursen (2004)](https://doi.org/10.1016/j.cma.2003.10.010). A description of
the Tribol implementation is available in [Serac
documentation](https://serac.readthedocs.io/en/latest/sphinx/theory_reference/solid.html#contact-mechanics).
the Tribol implementation is available in [smith
documentation](https://llnlsmith.readthedocs.io/en/latest/sphinx/theory_reference/solid.html#contact-mechanics).
Lagrange multipliers are used to solve for the pressure required to prevent
violation of the contact constraints.
+29 -15
View File
@@ -11,6 +11,24 @@
project(mfem-unit-tests NONE)
# Define a target that all examples and miniapps will depend on.
set(MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME test_exec_prerequisites)
add_custom_target(${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
# Add a target to copy the mfem data directory to the build directory
# Implementable as a single copy_directory_if_different command w/ CMake >= 3.26
file(GLOB DATA_FILES CONFIGURE_DEPENDS ${PROJECT_SOURCE_DIR}/data/*)
add_custom_target(copy_test_data
COMMAND ${CMAKE_COMMAND} -E make_directory data
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_FILES} data
COMMENT "Syncing the test data directory ...")
# Add 'copy_test_data' as a prerequisite for test executables, if the source and the
# build directories are not the same.
if (NOT ("${PROJECT_SOURCE_DIR}" STREQUAL "${PROJECT_BINARY_DIR}"))
add_dependencies(${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME} copy_test_data)
add_dependencies(${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME} copy_data)
endif()
# Include the source directory for the unit tests - catch.hpp is there.
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
@@ -53,6 +71,7 @@ set(UNIT_TESTS_SRCS
linalg/test_ode2.cpp
linalg/test_operator.cpp
linalg/test_particlevector.cpp
linalg/test_sparsesmoothers.cpp
linalg/test_vector.cpp
mesh/mesh_test_utils.cpp
mesh/test_exodus_reader.cpp
@@ -68,8 +87,6 @@ set(UNIT_TESTS_SRCS
mesh/test_psubmesh.cpp
mesh/test_submesh.cpp
mesh/test_vtu.cpp
mesh/test_nurbs.cpp
mesh/test_exodus_writer.cpp
fem/make_permuted_mesh.cpp
fem/test_1d_bilininteg.cpp
fem/test_2d_bilininteg.cpp
@@ -168,25 +185,19 @@ endif()
add_library(unit_tests_srcs OBJECT ${UNIT_TESTS_SRCS})
target_link_libraries(unit_tests_srcs PUBLIC mfem)
add_dependencies(unit_tests_srcs
${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
# All serial non-device unit tests are built into a single executable,
# 'unit_tests'.
mfem_add_executable(unit_tests unit_test_main.cpp)
target_link_libraries(unit_tests unit_tests_srcs)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} unit_tests)
# Unit tests need the ../../data directory.
add_dependencies(unit_tests copy_data)
# ParSubMesh tests need meshes in ../../miniapps/multidomain
add_dependencies(unit_tests copy_miniapps_multidomain_data)
# NURBS tests need meshes in ../../miniapps/nurbs
add_dependencies(unit_tests copy_miniapps_nurbs_data)
# Copy data to the build directory.
add_custom_command(TARGET unit_tests POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_CURRENT_SOURCE_DIR}/data data
COMMENT "Copying the unit tests data directory ...")
# Create a test called 'unit_tests' that runs the 'unit_tests' executable.
# The unit tests can be built and run separately from the rest of the tests:
# make unit_tests
@@ -205,7 +216,6 @@ if (MFEM_USE_CUDA)
set_property(SOURCE ${GPU_UNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
mfem_add_executable(gpu_unit_tests ${GPU_UNIT_TESTS_SRCS})
target_link_libraries(gpu_unit_tests unit_tests_srcs)
add_dependencies(gpu_unit_tests copy_data)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} gpu_unit_tests)
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME gpu_unit_tests COMMAND gpu_unit_tests)
@@ -221,7 +231,6 @@ if (MFEM_USE_HIP)
set(GPU_UNIT_TESTS_SRCS gpu_unit_test_main.cpp)
mfem_add_executable(gpu_unit_tests ${GPU_UNIT_TESTS_SRCS})
target_link_libraries(gpu_unit_tests unit_tests_srcs)
add_dependencies(gpu_unit_tests copy_data)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} gpu_unit_tests)
if (MFEM_USE_DOUBLE) # otherwise returns MFEM_SKIP_RETURN_VALUE
add_test(NAME gpu_unit_tests COMMAND gpu_unit_tests)
@@ -248,6 +257,8 @@ function(add_serial_miniapp_test name test_uvm)
endif(MFEM_USE_HIP)
mfem_add_executable(${name}_tests_cpu ${${NAME}_TESTS_SRCS})
add_dependencies(${name}_tests_cpu copy_miniapps_meshing_data)
add_dependencies(${name}_tests_cpu ${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
target_compile_definitions(${name}_tests_cpu PUBLIC MFEM_${NAME}_DEVICE="cpu")
target_link_libraries(${name}_tests_cpu mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_cpu)
@@ -256,6 +267,8 @@ function(add_serial_miniapp_test name test_uvm)
endif()
mfem_add_executable(${name}_tests_debug ${${NAME}_TESTS_SRCS})
add_dependencies(${name}_tests_debug copy_miniapps_meshing_data)
add_dependencies(${name}_tests_debug ${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
target_compile_definitions(${name}_tests_debug PUBLIC MFEM_${NAME}_DEVICE="debug")
target_link_libraries(${name}_tests_debug mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_debug)
@@ -265,6 +278,8 @@ function(add_serial_miniapp_test name test_uvm)
if (MFEM_USE_CUDA OR MFEM_USE_HIP)
mfem_add_executable(${name}_tests_gpu ${${NAME}_TESTS_SRCS})
add_dependencies(${name}_tests_gpu copy_miniapps_meshing_data)
add_dependencies(${name}_tests_gpu ${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
target_compile_definitions(${name}_tests_gpu PUBLIC MFEM_${NAME}_DEVICE="gpu")
target_link_libraries(${name}_tests_gpu mfem)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} ${name}_tests_gpu)
@@ -274,6 +289,8 @@ function(add_serial_miniapp_test name test_uvm)
if (test_uvm)
mfem_add_executable(${name}_tests_gpu_uvm ${${NAME}_TESTS_SRCS})
add_dependencies(${name}_tests_gpu_uvm copy_miniapps_meshing_data)
add_dependencies(${name}_tests_gpu_uvm ${MFEM_TEST_EXEC_PREREQUISITES_TARGET_NAME})
target_compile_definitions(${name}_tests_gpu_uvm PUBLIC
MFEM_${NAME}_DEVICE="gpu:uvm")
target_link_libraries(${name}_tests_gpu_uvm mfem)
@@ -289,7 +306,6 @@ endfunction(add_serial_miniapp_test)
add_serial_miniapp_test(sedov ON) # UVM ON
add_serial_miniapp_test(tmop_pa OFF) # UVM OFF
# TMOP tests need meshes in ../../miniapps/meshing
add_dependencies(tmop_pa_tests_cpu copy_miniapps_meshing_data)
#-----------------------------------------------------------
# SERIAL CEED TESTS:
@@ -343,7 +359,6 @@ if (MFEM_USE_MPI)
set(PGPU_UNIT_TESTS_SRCS pgpu_unit_test_main.cpp)
set_property(SOURCE ${PGPU_UNIT_TESTS_SRCS} PROPERTY LANGUAGE CUDA)
mfem_add_executable(pgpu_unit_tests ${PGPU_UNIT_TESTS_SRCS})
add_dependencies(pgpu_unit_tests copy_data)
target_link_libraries(pgpu_unit_tests unit_tests_srcs)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pgpu_unit_tests)
foreach(np 1 ${MFEM_MPI_NP})
@@ -359,7 +374,6 @@ if (MFEM_USE_MPI)
# pgpu_unit_tests
set(PGPU_UNIT_TESTS_SRCS pgpu_unit_test_main.cpp)
mfem_add_executable(pgpu_unit_tests ${PGPU_UNIT_TESTS_SRCS})
add_dependencies(pgpu_unit_tests copy_data)
target_link_libraries(pgpu_unit_tests unit_tests_srcs)
add_dependencies(${MFEM_ALL_TESTS_TARGET_NAME} pgpu_unit_tests)
foreach(np 1 ${MFEM_MPI_NP})
@@ -0,0 +1,55 @@
MFEM NURBS mesh v1.0
dimension
1
elements
2
1 1 0 1
1 1 2 1
boundary
2
1 0 0
1 0 2
# Both edges map to the same unique KnotVector (index 0), but the second edge
# has opposite orientation (v0 > v1), so its mapping is signed.
edges
2
0 0 1
0 2 1
vertices
3
patches
# Patch 0: u increases from x=0 to x=1
knotvectors
1
2 4 0 0 0 0.3 1 1 1
dimension
2
controlpoints
0.0 0.0 1.0
0.3 0.0 1.0
0.7 0.0 1.0
1.0 0.0 1.0
# Patch 1: same KnotVector, but control points are reversed to match the
# element/edge orientation.
knotvectors
1
2 4 0 0 0 0.3 1 1 1
dimension
2
controlpoints
2.0 0.0 1.0
1.7 0.0 1.0
1.3 0.0 1.0
1.0 0.0 1.0
@@ -0,0 +1,53 @@
MFEM NURBS mesh v1.0
dimension
1
elements
2
1 1 0 1
1 1 1 2
boundary
2
1 0 0
1 0 2
# Both edges map to the same unique KnotVector (index 0).
edges
2
0 0 1
0 1 2
vertices
3
patches
# Patch 0: quadratic, 4 control points, 1 interior knot at u=0.3
knotvectors
1
2 4 0 0 0 0.3 1 1 1
dimension
2
controlpoints
0.0 0.0 1.0
0.3 0.0 1.0
0.7 0.0 1.0
1.0 0.0 1.0
# Patch 1: same KnotVector, different control points (translated)
knotvectors
1
2 4 0 0 0 0.3 1 1 1
dimension
2
controlpoints
1.0 0.0 1.0
1.3 0.0 1.0
1.7 0.0 1.0
2.0 0.0 1.0
+35
View File
@@ -362,3 +362,38 @@ TEST_CASE("H(div) Linear Form Extension", "[LinearFormExtension], [GPU]")
d1 -= d2;
REQUIRE(d1.Norml2() == MFEM_Approx(0.0));
}
#ifdef MFEM_USE_MPI
TEST_CASE("Parallel Fast LinearForm Assembly",
"[AssemblyLevel], [Parallel], [GPU]")
{
auto order = GENERATE(1, 2);
auto mesh_fname = GENERATE(
"../../data/amr-quad.mesh",
"../../data/fichera-amr.mesh"
);
Mesh serial_mesh(mesh_fname);
ParMesh mesh(MPI_COMM_WORLD, serial_mesh);
serial_mesh.Clear();
Array<int> ess_bdr(mesh.bdr_attributes.Max());
ess_bdr = 0;
mesh.MarkExternalBoundaries(ess_bdr);
H1_FECollection fec(order, mesh.Dimension());
ParFiniteElementSpace fespace(&mesh, &fec);
Array<int> ess_tdof_list;
fespace.GetBoundaryTrueDofs(ess_tdof_list);
ParLinearForm b(&fespace);
ConstantCoefficient one(1.0);
b.AddDomainIntegrator(new DomainLFIntegrator(one));
b.AddBoundaryIntegrator(new BoundaryLFIntegrator(one), ess_bdr);
b.UseFastAssembly(true);
b.Assemble();
}
#endif
@@ -0,0 +1,76 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "mfem.hpp"
#include "unit_tests.hpp"
using namespace mfem;
static void TestTranspose(const Operator &A)
{
DenseMatrix A_dense(A.Height(), A.Width());
Vector e(A.Width());
e = 0.0;
for (int i = 0; i < A.Width(); ++i)
{
e[i] = 1.0;
Vector Ae(A.Height());
A.Mult(e, Ae);
A_dense.SetCol(i, Ae);
e[i] = 0.0;
}
Vector v(A.Height());
v.Randomize();
Vector w1(A.Width()), w2(A.Width());
A.MultTranspose(v, w1);
A_dense.MultTranspose(v, w2);
w1 -= w2;
REQUIRE(w1.Normlinf() == MFEM_Approx(0.0));
}
TEST_CASE("Sparse Smoothers Transposed", "[DSmoother][GSSmoother]")
{
const bool sym = GENERATE(true, false);
constexpr int n = 10;
SparseMatrix A(n, n);
for (int i = 0; i < n; ++i)
{
for (int j = 0; j < n; ++j)
{
const real_t val = rand_real();
A.Set(i, j, val);
if (sym) { A.Set(j, i, val); }
}
A.Add(i, i, 10.0);
}
A.Finalize();
constexpr int nit = 2; // Number of smoother iterations
TestTranspose(DSmoother(A, 0, 1.0, nit)); // scaled
if (sym)
{
TestTranspose(DSmoother(A, 1, 1.0, nit)); // l1-Jacobi
TestTranspose(DSmoother(A, 2, 1.0, nit)); // lumped Jacobi
}
TestTranspose(GSSmoother(A, 0, nit)); // symmetric
TestTranspose(GSSmoother(A, 1, nit)); // forward
TestTranspose(GSSmoother(A, 2, nit)); // backward
}
+128
View File
@@ -350,3 +350,131 @@ TEST_CASE("MakeNurbs", "[Mesh]")
Mesh::MakeCartesian3D(1, 1, 1, Element::Type::HEXAHEDRON);
test_nurbs_extension(patch_topology_3d);
}
TEST_CASE("NURBS 1D curve in 2D from patches", "[Mesh]")
{
// Build a 1D patch topology embedded in 2D physical space with
// three segments of varying orders (linear, quadratic and cubic)
constexpr int dim = 1;
constexpr int space_dim = 2;
Mesh patch_topology(dim, 0, 0, 0, space_dim);
constexpr int nv_input = 6;
for (int i = 0; i < nv_input; i++)
{
patch_topology.AddVertex((real_t)i, 0.0, 0.0);
}
patch_topology.AddSegment(0, 1, 1);
patch_topology.AddSegment(2, 3, 2);
patch_topology.AddSegment(4, 5, 3);
// Three 1D NURBS patches with variable order, each with control points
// given in (x, y, w) format so that the physical dimension is 2 while the
// topological dimension is 1.
auto set_knots = [](KnotVector &kv, std::initializer_list<real_t> knots)
{
MFEM_VERIFY(kv.Size() == static_cast<int>(knots.size()),
"KnotVector and knot list must have the same size.");
int i = 0;
for (const auto &k : knots)
{
kv[i++] = k;
}
};
auto set_cp = [](std::initializer_list<std::array<real_t, 3>> pts)
{
Array<real_t> cp(3 * static_cast<int>(pts.size()));
int i = 0;
for (const auto &[x, y, w] : pts)
{
cp[i++] = x;
cp[i++] = y;
cp[i++] = w;
}
return cp;
};
// Patch 0: order 1, 4 control points
KnotVector kv1(1, 4);
set_knots(kv1, {0.0, 0.0, 0.4, 0.6, 1.0, 1.0});
kv1.GetElements();
Array<real_t> cp1 = set_cp(
{
{0.0, 0.0, 1.0},
{0.4, 0.6, 1.0},
{0.6, 0.4, 1.0},
{1.0, 1.0, 1.0}});
Array<const KnotVector *> kvs1({&kv1});
// Patch 1: order 2, 3 control points
KnotVector kv2(2, 3);
set_knots(kv2, {0.0, 0.0, 0.0, 1.0, 1.0, 1.0});
kv2.GetElements();
Array<real_t> cp2 = set_cp(
{
{1.0, 0.0, 1.0},
{1.0, 1.0, 1.2},
{2.0, 1.0, 1.0}});
Array<const KnotVector *> kvs2({&kv2});
// Patch 2: order 3, 4 control points
KnotVector kv3(3, 4);
set_knots(kv3, {0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0});
kv3.GetElements();
Array<real_t> cp3 = set_cp(
{
{2.0, 0.0, 1.0},
{2.0, 0.9, 1.31},
{2.1, 1.0, 1.32},
{3.0, 1.0, 1.0}});
Array<const KnotVector *> kvs3({&kv3});
auto p1 = std::make_unique<NURBSPatch>(kvs1, 3, cp1.GetData());
auto p2 = std::make_unique<NURBSPatch>(kvs2, 3, cp2.GetData());
auto p3 = std::make_unique<NURBSPatch>(kvs3, 3, cp3.GetData());
Array<const NURBSPatch *> patches(3);
patches[0] = p1.get();
patches[1] = p2.get();
patches[2] = p3.get();
NURBSExtension ne(&patch_topology, patches);
Mesh mesh(ne);
// Check that we created a 1D NURBS mesh embedded in 2D physical space and
// that the associated finite element space uses the correct vector dimension.
REQUIRE(mesh.Dimension() == dim);
REQUIRE(mesh.SpaceDimension() == space_dim);
REQUIRE(mesh.GetNE() == 5);
REQUIRE(mesh.GetNV() == 8);
GridFunction *nodes = mesh.GetNodes();
REQUIRE(nodes != NULL);
REQUIRE(nodes->FESpace() != NULL);
REQUIRE(nodes->FESpace()->GetVDim() == space_dim);
REQUIRE(mesh.NURBSext != NULL);
REQUIRE(mesh.NURBSext->GetNP() == 3);
// Additionally, exercise degree elevation and ensure basic invariants hold
{
const Array<int> &orders = mesh.NURBSext->GetOrders();
const int max_order = orders.Max();
mesh.DegreeElevate(max_order, max_order);
REQUIRE(mesh.NURBSext != nullptr);
REQUIRE(mesh.Dimension() == dim);
REQUIRE(mesh.SpaceDimension() == space_dim);
REQUIRE(mesh.NURBSext->Dimension() == dim);
const Array<int> &new_orders = mesh.NURBSext->GetOrders();
REQUIRE(new_orders.Size() == orders.Size());
for (int i = 0; i < new_orders.Size(); ++i)
{
REQUIRE(new_orders[i] == max_order);
}
}
}
+353
View File
@@ -132,6 +132,188 @@ TEST_CASE("NURBS mesh reconstruction", "[NURBS]")
for (auto *p : patches) { delete p; }
}
TEST_CASE("Location conversion check", "[NURBS]")
{
KnotVector kv(3, Vector({0.0,
0.2,0.2,0.2,
0.5,0.5,0.5,
0.8,0.8,0.8,
1.0}));
mfem::out<<"knotvector : ";
kv.Print(mfem::out);
constexpr int samples = 31;
for (int i = 0; i < samples; i++)
{
const real_t u = i/real_t(samples-1);
const int ks = kv.GetSpan (u);
REQUIRE( ((kv[ks] <= u) && (u <= kv[ks+1])) );
const real_t xi = kv.GetRefPoint(u, ks);
REQUIRE( ((0.0 <= xi) && (xi <= 1.0)) );
const real_t un = kv.GetKnotLocation(xi,ks);
REQUIRE((un - u) == MFEM_Approx(0.0));
mfem::out<<i<<" : "<<ks<<" ";
mfem::out<<kv[ks] <<" "<<u<<" "<<kv[ks+1]<<" : ";
mfem::out<<u<<" "<<un<<" = "<<un -u<<std::endl;
}
for (int i = 0; i < kv.Size(); i++)
{
const real_t u = kv[i];
const int ks = kv.GetSpan (u);
REQUIRE( ((kv[ks] <= u) && (u <= kv[ks+1])) );
const real_t xi = kv.GetRefPoint(u, ks);
REQUIRE( ((0.0 <= xi) && (xi <= 1.0)) );
const real_t un = kv.GetKnotLocation(xi,ks);
REQUIRE((un - u) == MFEM_Approx(0.0));
mfem::out<<i<<" : "<<ks<<" ";
mfem::out<<kv[ks] <<" "<<u<<" "<<kv[ks+1]<<" : ";
mfem::out<<u<<" "<<un<<" = "<<un -u<<std::endl;
}
KnotVector kv2(1, Vector({0.0, 1.0/3.0, 2.0/3.0, 1.0}));
mfem::out<<"knotvector2 : ";
kv2.Print(mfem::out);
for (int i = 0; i < samples; i++)
{
const real_t u = i/real_t(samples-1);
const int ks = kv2.GetSpan (u);
REQUIRE( ((kv2[ks] <= u) && (u <= kv2[ks+1])) );
const real_t xi = kv2.GetRefPoint(u, ks);
REQUIRE( ((0.0 <= xi) && (xi <= 1.0)) );
const real_t un = kv2.GetKnotLocation(xi,ks);
REQUIRE((un - u) == MFEM_Approx(0.0));
mfem::out<<i<<" : "<<ks<<" ";
mfem::out<<kv2[ks] <<" "<<u<<" "<<kv2[ks+1]<<" : ";
mfem::out<<u<<" "<<un<<" = "<<un -u<<std::endl;
}
for (int i = 0; i < kv2.Size(); i++)
{
const real_t u = kv2[i];
const int ks = kv2.GetSpan (u);
REQUIRE( ((kv2[ks] <= u) && (u <= kv2[ks+1])) );
const real_t xi = kv2.GetRefPoint(u, ks);
REQUIRE( ((0.0 <= xi) && (xi <= 1.0)) );
const real_t un = kv2.GetKnotLocation(xi,ks);
REQUIRE((un - u) == MFEM_Approx(0.0));
mfem::out<<i<<" : "<<ks<<" ";
mfem::out<<kv2[ks] <<" "<<u<<" "<<kv2[ks+1]<<" : ";
mfem::out<<u<<" "<<un<<" = "<<un -u<<std::endl;
}
}
TEST_CASE("Greville, Botella and Demko points", "[NURBS]")
{
Vector xi;
for ( int p = 1; p <= 9; p++)
{
mfem::out<<"Order : "<<p<<std::endl;
KnotVector kvp(p, Vector({0., 1.}));
mfem::out<<"Knotvector : "; kvp.Print(mfem::out);
kvp.GetGreville(xi);
mfem::out<<"Greville points : "; xi.Print(std::cout,999);
kvp.GetBotella(xi);
mfem::out<<"Botella points : "; xi.Print(std::cout,999);
kvp.GetDemko(xi);
mfem::out<<"Demko points : "; xi.Print(std::cout,999);
}
KnotVector kv(3, Vector({0.0, 0.3, 0.3, 0.3, 0.6, 1.0}));
mfem::out<<"Knotvector : "; kv.Print(mfem::out);
// Greville
Vector greville(kv.GetNCP());
for (int i = 0; i < kv.GetNCP(); i++)
{
greville[i] = kv.GetGreville(i);
}
mfem::out<<"Greville points : "; greville.Print(mfem::out, 32);
Vector gref({0.0,0.1,0.2,0.3,0.4,19./30,26./30, 1.0});
for (int i = 0; i < kv.GetNCP(); i++)
{
REQUIRE((greville[i] - gref[i]) == MFEM_Approx(0.0));
}
// Botella
Vector botella(kv.GetNCP());
for (int i = 0; i < kv.GetNCP(); i++)
{
botella[i] = kv.GetBotella(i);
}
mfem::out<<"Botella points : "; botella.Print(mfem::out, 32);
Vector bref({0.0,0.1,0.2,0.3,
0.444007481526490333,
0.626666666666666594,
0.828131261741523739, 1.0});
for (int i = 0; i < kv.GetNCP(); i++)
{
REQUIRE((botella[i] - bref[i]) == MFEM_Approx(0.0));
}
// Demko
Vector demko(kv.GetNCP());
for (int i = 0; i < kv.GetNCP(); i++)
{
demko[i] = kv.GetDemko(i);
}
mfem::out<<"Demko points : "; demko.Print(mfem::out, 32);
Vector dref({0.0,0.075,0.225,0.3,
0.406122105546614987,
0.621569465634039919,
0.87385648854468001,1.0});
for (int i = 0; i < kv.GetNCP(); i++)
{
REQUIRE((demko[i] - dref[i]) == MFEM_Approx(0.0,1e-9,1e-9));
}
// Chebyshev spline
Vector a(kv.GetNCP());
Vector x(kv.GetNCP());
for ( int i = 0; i <x.Size(); i++)
{
x[i] = std::pow(-1.0, i);
}
kv.GetInterpolant(x, demko, a);
mfem::out<<"Chebyshev spline coeff : "; a.Print(mfem::out, 32);
Vector aref({1.0, -5.0, 5.0, -1.0,
3.24079982256718635,
-5.51623733136825933,
3.75648721902370486, -1.0});
for (int i = 0; i < kv.GetNCP(); i++)
{
REQUIRE((a[i] - aref[i]) == MFEM_Approx(0.0));
}
mfem::out<<"Chebyshev spline \n";
kv.PrintFunction(mfem::out, a, 21);
}
TEST_CASE("NURBS knotvector orientation", "[NURBS]")
{
// This will fail to load without CorrectPatchTopoOrientations
auto mesh_fname = "../../miniapps/nurbs/meshes/3patch-nurbs-flipedge.mesh";
Mesh mesh(mesh_fname, 1, 1);
REQUIRE(mesh.NURBSext->CheckPatches());
}
TEST_CASE("NURBS NC-patch mesh loading", "[NURBS]")
{
auto mesh_fname = GENERATE("../../data/nc3-nurbs.mesh",
@@ -146,6 +328,177 @@ TEST_CASE("NURBS NC-patch mesh loading", "[NURBS]")
REQUIRE(mesh.GetNE() == ne * std::pow(2, dim));
}
TEST_CASE("NURBS 1D variable-order mesh load", "[NURBS]")
{
auto mesh_fname = GENERATE("../../data/nurbs-segments2d.mesh",
"../../data/nurbs-segments3d.mesh",
"../../data/nurbs-segments2d-patches.mesh",
"../../data/nurbs-segments3d-patches.mesh",
"../../data/nurbs-segments2d-patches-multispan.mesh");
// Set up hard-coded expected values based on the input meshes.
// This should be easy to update as needed.
struct ExpectedSizes
{
int phys_dim, ne, nv, nkv;
Array<int> orders, ncp;
};
const auto expected = [&]() -> ExpectedSizes
{
ExpectedSizes e;
const bool is_2d = (std::string(mesh_fname).find("2d") != std::string::npos);
e.phys_dim = is_2d ? 2 : 3;
if (std::string(mesh_fname).find("multispan") != std::string::npos)
{
// multispan: 4 input segments w/ 9 elements, 13 vertices
e.ne = 9;
e.nv = 13;
e.nkv = 4;
e.orders = Array<int>({1, 2, 3, 4});
e.ncp = Array<int>({4, 4, 6, 5});
}
else
{
// standard: 3 elements, 6 vertices
e.ne = 3;
e.nv = 6;
e.nkv = 3;
e.orders = Array<int>({1, 2, 3});
e.ncp = Array<int>({2, 3, 4});
}
return e;
}();
Mesh mesh(mesh_fname, 1, 0);
// Basic mesh properties
REQUIRE(mesh.Dimension() == 1);
REQUIRE(mesh.SpaceDimension() == expected.phys_dim);
REQUIRE(mesh.GetNE() == expected.ne);
REQUIRE(mesh.GetNV() == expected.nv);
// NURBS extension must be present and 1D
REQUIRE(mesh.NURBSext != nullptr);
REQUIRE(mesh.NURBSext->Dimension() == 1);
// Check that we have the expected number of knotvectors
const int n_kv = mesh.NURBSext->GetNKV();
REQUIRE(n_kv == expected.nkv);
const Array<int> &orders = mesh.NURBSext->GetOrders();
REQUIRE(orders.Size() == n_kv);
// Validate each KnotVector's order and number of control points.
for (int i = 0; i < n_kv; i++)
{
const KnotVector *kv = mesh.NURBSext->GetKnotVector(i);
REQUIRE(kv != nullptr);
const int o = kv->GetOrder();
const int ncp = kv->GetNCP();
bool matched = false;
for (int j = 0; j < expected.orders.Size(); ++j)
{
if (o == expected.orders[j] && ncp == expected.ncp[j])
{
matched = true;
break;
}
}
REQUIRE(matched);
}
// Additionally, exercise degree elevation and ensure basic invariants hold
{
const int max_order = orders.Max();
mesh.DegreeElevate(max_order, max_order);
REQUIRE(mesh.NURBSext != nullptr);
REQUIRE(mesh.Dimension() == 1);
REQUIRE(mesh.SpaceDimension() == expected.phys_dim);
REQUIRE(mesh.NURBSext->Dimension() == 1);
const Array<int> &new_orders = mesh.NURBSext->GetOrders();
REQUIRE(new_orders.Size() == orders.Size());
for (int i = 0; i < new_orders.Size(); ++i)
{
REQUIRE(new_orders[i] == max_order);
}
}
}
TEST_CASE("NURBS 1D shared KnotVector in patches", "[NURBS]")
{
auto RequireSameKnotVector = [](const KnotVector &a, const KnotVector &b)
{
REQUIRE(a.GetOrder() == b.GetOrder());
REQUIRE(a.GetNCP() == b.GetNCP());
REQUIRE(a.Size() == b.Size());
for (int i = 0; i < a.Size(); i++)
{
REQUIRE( (a[i]-b[i]) == MFEM_Approx(0.));
}
};
SECTION("Same orientation")
{
const auto mesh_fname = "./data/nurbs-segments-same-orientation.mesh";
Mesh mesh(mesh_fname, 1, 0);
REQUIRE(mesh.NURBSext != nullptr);
REQUIRE(mesh.Dimension() == 1);
REQUIRE(mesh.SpaceDimension() == 2);
REQUIRE(mesh.NURBSext->GetNP() == 2);
REQUIRE(mesh.NURBSext->GetNKV() == 1);
const KnotVector *unique_kv = mesh.NURBSext->GetKnotVector(0);
REQUIRE(unique_kv != nullptr);
Array<const KnotVector *> pkv0, pkv1;
mesh.NURBSext->GetPatchKnotVectors(0, pkv0);
mesh.NURBSext->GetPatchKnotVectors(1, pkv1);
REQUIRE(pkv0.Size() == 1);
REQUIRE(pkv1.Size() == 1);
RequireSameKnotVector(*unique_kv, *pkv0[0]);
RequireSameKnotVector(*unique_kv, *pkv1[0]);
RequireSameKnotVector(*pkv0[0], *pkv1[0]);
}
SECTION("Opposite orientation")
{
const auto mesh_fname = "./data/nurbs-segments-opposite-orientation.mesh";
Mesh mesh(mesh_fname, 1, 0);
REQUIRE(mesh.NURBSext != nullptr);
REQUIRE(mesh.Dimension() == 1);
REQUIRE(mesh.SpaceDimension() == 2);
REQUIRE(mesh.NURBSext->GetNP() == 2);
REQUIRE(mesh.NURBSext->GetNKV() == 1);
const KnotVector *unique_kv = mesh.NURBSext->GetKnotVector(0);
REQUIRE(unique_kv != nullptr);
Array<const KnotVector *> pkv0, pkv1;
mesh.NURBSext->GetPatchKnotVectors(0, pkv0);
mesh.NURBSext->GetPatchKnotVectors(1, pkv1);
REQUIRE(pkv0.Size() == 1);
REQUIRE(pkv1.Size() == 1);
RequireSameKnotVector(*unique_kv, *pkv0[0]);
KnotVector flipped(*unique_kv);
flipped.Flip();
RequireSameKnotVector(flipped, *pkv1[0]);
}
}
TEST_CASE("NURBS NC-patch large meshes", "[MFEMData][NURBS]")
{
auto mesh_fname = GENERATE("bricks2D.mesh",
+25
View File
@@ -121,6 +121,31 @@ TEST_CASE("ParMeshGlobalIndices", "[Parallel], [ParMesh]")
}
}
TEST_CASE("ParMeshSharedFaces", "[Parallel], [ParMesh]")
{
const char *mesh_file = "../../data/fichera-amr.mesh";
Mesh mesh(mesh_file);
ParMesh pmesh(MPI_COMM_WORLD, mesh);
pmesh.ExchangeFaceNbrData();
const int nshared = pmesh.GetNSharedFaces();
int local_ghosts_nonmatching = 0;
for (int sf = 0; sf < nshared; sf++)
{
const int f = pmesh.GetSharedFace(sf);
FaceElementTransformations *ftr =
pmesh.GetSharedFaceTransformationsByLocalIndex(f, false);
if (f != ftr->ElementNo) { local_ghosts_nonmatching++; }
}
int global_ghosts_nonmatching = 0;
MPI_Allreduce(&local_ghosts_nonmatching, &global_ghosts_nonmatching, 1, MPI_INT,
MPI_SUM, pmesh.GetComm());
REQUIRE(global_ghosts_nonmatching == 0);
}
namespace simplicial
{