Compare commits

..
Author SHA1 Message Date
Sohail Reddy b7753cb4db fixed cmake 2026-01-06 18:59:44 -08:00
Sohail Reddy 842fec483f Merge branch 'master' into multiapp 2026-01-06 18:53:05 -08:00
Sohail Reddy 4b0eafd047 Fixed const correctness 2026-01-06 18:49:38 -08:00
Sohail Reddy e793c70058 Updated fsi app for constrained inner product on ess_dof 2025-11-19 14:28:38 -08:00
Sohail Reddy 1856912d63 Fixed style 2025-11-19 14:24:23 -08:00
Sohail Reddy 3b6817e36b Added custom inner product to fixed point relaxation methods 2025-11-19 14:24:04 -08:00
Sohail Reddy e4dcfb684f Merge branch 'ode-stage-solve' into multiapp 2025-11-18 23:26:55 -08:00
Sohail Reddy 7dcf77da54 Merge branch 'master' into multiapp 2025-11-18 23:25:23 -08:00
Sohail Reddy 3e823f1da7 updated morpher coupled bc 2025-11-18 23:16:06 -08:00
Sohail Reddy 9a2fee8b6f moved FPI relaxation member functions to cpp 2025-10-19 21:44:25 -07:00
Sohail Reddy 4b38bd79e9 clean up fsi example 2025-10-16 18:05:41 -07:00
Sohail Reddy d1378b8a7b bug fix fpisolver; reset relaxation each call 2025-10-16 18:04:17 -07:00
Sohail Reddy 3972b24f3b fixed ale velocity calculation 2025-10-15 22:19:59 -07:00
Sohail Reddy 48bd66640d added command line option for relaxation factor and fixed mpi_c_bool 2025-10-15 22:19:35 -07:00
Sohail Reddy a230b87a20 Fixed pure virtual function overide if GSLib not built 2025-10-14 18:31:08 -07:00
Sohail Reddy 5f9f782ad2 fixed style 2025-10-14 17:01:51 -07:00
Sohail Reddy 159a259f30 fixed redeclared variable in FPISolver 2025-10-14 16:56:20 -07:00
Sohail Reddy 67da324c26 Merge branch 'master' into multiapp 2025-10-14 16:28:08 -07:00
Sohail Reddy b5cf20e76c added cmake for multiapp miniapps 2025-10-14 16:26:00 -07:00
Sohail Reddy 1510492dfd fixed style 2025-10-14 16:24:23 -07:00
Sohail Reddy 360e662cf8 FSI miniapp and updated elasticity and navier-stokes apps 2025-10-14 16:22:53 -07:00
Sohail Reddy c1ec6a8c72 bug fix in navier miniapp for Step() function 2025-10-14 13:27:04 -07:00
Sohail Reddy 805499d799 bugfix for updating grid functions for transfer 2025-10-14 13:26:15 -07:00
Sohail Reddy 7b0330ffca Removed repetitive code in cht miniapp 2025-10-14 13:23:55 -07:00
Sohail Reddy 68fd37de1d Added completed conjugate heat transfer test case 2025-10-11 17:44:41 -07:00
Sohail Reddy 9f1d2ac249 reverted changes to navier miniapp 2025-10-11 17:43:24 -07:00
Sohail Reddy a594ab5a3a fixed namespace std use 2025-10-11 17:42:57 -07:00
Sohail Reddy 343a8b473e moved fixed point solver to solvers.cpp 2025-10-11 17:13:31 -07:00
Sohail Reddy 911709c619 fixed use of MFEM_USE_MPI in multiapp 2025-10-11 16:10:17 -07:00
Sohail Reddy c303956b9f fixed source field ownership and cleaning 2025-10-11 01:29:50 -07:00
Sohail Reddy c031ec4466 Fixed const correctness and added operator ownership in multiapp 2025-10-10 17:37:12 -07:00
Sohail Reddy 58c56ec9dc added LinkedFieldsCollection to manage linked fields for each application 2025-10-10 00:01:12 -07:00
Sohail Reddy 7db740ede3 Added documentation, updated class names for coupling schemes 2025-09-28 23:36:06 -07:00
Sohail Reddy 0436adfb1c updated fixed point solver 2025-09-28 23:31:33 -07:00
Sohail Reddy a6f2d8981a Merge branch 'master' into multiapp 2025-09-08 18:02:54 -07:00
Sohail Reddy 90edc198e3 update fsi app 2025-09-08 17:58:24 -07:00
Sohail Reddy c691c7c854 Formatted multiapp files and added documentation 2025-09-08 10:21:16 -07:00
Sohail Reddy 7c9105bd0e fixed style 2025-09-08 01:41:26 -07:00
Sohail Reddy 773bac0114 updated navier-stokes miniapp for ALE 2025-09-08 01:29:33 -07:00
Sohail Reddy 440db8189c Added option for ALE to VectorConvectionNLFIntegrator 2025-09-08 01:28:26 -07:00
Sohail Reddy 94efb4cd81 Updated multiapp interface for type-erased operator, field transfer and coupling scheme 2025-09-08 01:27:47 -07:00
Sohail Reddy ce3bdc0dfc Simplified mesh-morphing/diffusion app 2025-09-08 01:24:53 -07:00
Sohail Reddy aaf59d3a83 updated elasticity miniapp to use Schur complement 2025-09-08 01:22:39 -07:00
Sohail Reddy 3b3ffa7d7a updated native navier miniapp to use standard ODESolve::Step signature 2025-09-05 13:53:12 -07:00
Sohail Reddy 2898ec669c Updated native navier miniapp to own/not-own the boundary conditions 2025-09-04 17:19:45 -07:00
Sohail Reddy f34bfbbd8d Added field transfer based on FindPoints 2025-09-04 10:35:50 -07:00
Sohail Reddy 3cec2cba78 updated navier-stokes and elasticity examples 2025-09-03 16:34:45 -07:00
Sohail Reddy f769e2a7d7 updated morpher to diffuse displacement or velocity 2025-09-03 16:34:08 -07:00
Sohail Reddy 493674e650 updated navier-stokes miniapp to work with scaled pressure 2025-09-03 16:31:58 -07:00
Sohail Reddy 13632cde51 updated elasticity miniapp 2025-09-03 16:31:03 -07:00
Sohail Reddy ae63fda0fd Updated multiapp object for multi-stage coupling 2025-08-19 11:54:02 -07:00
Sohail Reddy 48b3e428fb clean up 2025-08-19 11:53:32 -07:00
Sohail Reddy 89dd191866 bug-fix: fixed point solver; updated relaxation schemes to clamp relaxation factors 2025-08-19 11:47:14 -07:00
Sohail Reddy 2a4f9184be Updated Navier-Stokes miniapp for multi-stage coupling and artificial compressibility 2025-08-19 11:40:47 -07:00
Sohail Reddy ff66dce92e updated elasticity and morpher miniapps for multi-stage coupling 2025-08-19 11:38:25 -07:00
Sohail Reddy 01ef886faf Added parallelized DistanceSquared for Vectors 2025-08-19 11:09:47 -07:00
Sohail Reddy 4be67907c4 Working FSI miniapp 2025-08-13 16:12:04 -07:00
Sohail Reddy 9443523dec Updated the multiapp (CoupledApplication) framework 2025-08-13 16:10:19 -07:00
Sohail Reddy fac6dfe283 Added mesh morphing app 2025-08-13 16:09:50 -07:00
Sohail Reddy 7776766ee7 updated FPISolver 2025-08-13 16:09:09 -07:00
Sohail Reddy 8e24224a18 updated cht example...unused atm 2025-08-13 16:08:33 -07:00
Sohail Reddy fa4c607622 updated Navier-Stokes miniapp for partial assembly 2025-08-13 16:07:09 -07:00
Sohail Reddy 29bd541a3e updated elasticity miniapp 2025-08-13 16:03:23 -07:00
Sohail Reddy 369f82adad Added function to fetch TimeDependantOperator from ODESolver 2025-08-13 16:01:39 -07:00
Sohail Reddy e2bc1aefd8 Working elasticity miniapp 2025-08-01 19:09:05 -07:00
Sohail Reddy 033814fe70 fixed code style 2025-07-31 10:14:48 -07:00
Sohail Reddy f4d4a5c4da Working fully implicit incompressible navier-stokes solver 2025-07-31 10:13:33 -07:00
Sohail Reddy 6b8d341478 Renamed some multiapp member functions 2025-07-31 09:56:27 -07:00
Sohail Reddy 7a02905d32 Updated constructors for some bilinear and nonlinearform integrator to now take integration rule as input 2025-07-31 09:53:16 -07:00
Sohail Reddy a4c2ad2f3c Updated type erasure to support non-mfem apps 2025-07-21 10:27:35 -07:00
Sohail Reddy 4a11684323 Working monolithic (linear and nonlinear) solves for multiapp 2025-07-17 18:22:41 -07:00
Sohail Reddy 7b4b2de092 Merge branch 'master' into multiapp 2025-07-15 16:37:35 -07:00
Sohail Reddy 285aa03049 conv-diff mulitapp example 2025-07-15 16:35:56 -07:00
Sohail Reddy 27af36adc5 Changed class naming and fixed point solver and relaxation schemes 2025-07-15 16:35:03 -07:00
Sohail Reddy de781c05b8 Fixed function pointer for relaxation method in FPISolver 2025-07-10 17:04:58 -07:00
Sohail Reddy 125f5c9e48 Added relaxation methods to fixed point solvers and updated multiapp for explicit coupling 2025-07-10 16:59:03 -07:00
Sohail Reddy 0d6c8fbbf1 Updated multiapp partition implicit solve to use fixed point Solver object 2025-07-09 19:48:04 -07:00
Sohail Reddy 34ebc0f158 Changed scheme naming convention and added/separated Addtive and Alternating Schwarz coupling schemes 2025-07-08 18:16:32 -07:00
Sohail Reddy 8db7813d86 Working paritioned implicit solves 2025-07-08 11:06:27 -07:00
Sohail Reddy d31e5c8af0 Merge branch 'master' into multiapp 2025-07-02 20:46:57 -07:00
Sohail Reddy d7e43cfc74 added test case for two-way partitioned coupled solve 2025-07-02 20:45:51 -07:00
Sohail Reddy 82193e0404 Merge branch 'master' into multiapp 2025-06-16 15:09:01 -07:00
Sohail Reddy 5796633307 updated example for implicit time-integration 2025-06-16 15:05:22 -07:00
Sohail Reddy 3d55c021e3 updated header copyright 2025-06-12 14:47:11 -07:00
Sohail Reddy a30303ecf9 fixed type checking with SFINAE 2025-06-12 14:31:05 -07:00
Sohail Reddy 9287488024 Merge branch 'master' into multiapp 2025-06-11 14:11:32 -07:00
Sohail Reddy 9aa2866b0d Merge branch 'master' into multiapp 2025-05-29 14:00:03 -07:00
Sohail Reddy ad16d2f5ae added field transfer, multiapp abstractions and working one-way coupling 2025-05-29 13:58:58 -07:00
Sohail Reddy c552925a54 Merge branch 'master' into multiapp 2025-03-13 15:41:44 -07:00
Sohail Reddy c3b9b62336 updated mfem_application to perform index controlled operators 2025-02-20 11:28:02 -08:00
Sohail Reddy c36e5facd1 initial multiapp skeleton with MFEMApplication class and type-erased multiapp 2024-10-02 17:11:14 -07:00
147 changed files with 8266 additions and 9929 deletions
-10
View File
@@ -313,8 +313,6 @@ 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
@@ -340,14 +338,7 @@ 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
@@ -369,7 +360,6 @@ miniapps/shifted/lsf_integral
miniapps/tools/display-basis
miniapps/tools/load-dc
miniapps/tools/convert-dc
miniapps/tools/compare-dc
miniapps/tools/gridfunction-bounds
miniapps/tools/lor-transfer
miniapps/tools/plor-transfer
-33
View File
@@ -11,22 +11,6 @@
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.
New and updated examples and miniapps
-------------------------------------
- Electromagnetics/lorentz miniapp has been updated to leverage the ParticleSet
capability.
Version 4.9, released on Dec 11, 2025
=====================================
@@ -111,23 +95,6 @@ Linear and nonlinear solvers
Filtering (AMGF), providing robust preconditioning for linear systems arising
in constrained optimization problems such as frictionless contact.
Added 'GetResiduals' and 'GetFinalAbsResidualNorm' to 'HyprePCG',
'HypreGMRES', and 'HypreFGMRES' to get 'r' and '|r|_p'. Note that the latter
computes '|r|_p' from 'r' instead of returning a cached value like the
relative 'GetFinalResidualNorm'. These require Hypre >= 2.15.0.
Changed the default solver parameters for 'HyprePCG' to 'tol=1e-6' and
'max_iter=1000'. This matches the default parameters in Hypre 3.0.
Added various helper functions for querying/modifying Hypre solvers:
'HypreSmoother::GetType', 'HypreSmoother::GetSOROptions',
'HypreSmoother::GetPolyOptions', 'HypreSmoother::GetWindowParameters',
'HypreSmoother::IsOperatorSymmetric', 'HyprePCG::GetTol',
'HyprePCG::GetAbsTol', 'HyprePCG::GetMaxIter', 'HyprePCG::SetUseTwoNorm',
'HypreGMRES::GetTol', 'HypreGMRES::GetAbsTol', 'HypreGMRES::GetMaxIter',
'HypreGMRES::GetKDim', 'HypreFGMRES::GetTol', 'HypreFGMRES::GetMaxIter',
'HypreFGMRES::GetKDim', and 'HypreBoomerAMG::GetMaxIter'.
GPU computing
-------------
- Added the 'gpu', 'raja-gpu', and 'ceed-gpu' backend aliases/shortcuts which
+5 -10
View File
@@ -870,12 +870,11 @@ add_dependencies(exec
# - https://cmake.org/Bug/view.php?id=8438
# 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_data
COMMAND ${CMAKE_COMMAND} -E make_directory data
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DATA_FILES} data
COMMENT "Syncing the data 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)
# 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}"))
@@ -1007,10 +1006,6 @@ 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'
-1
View File
@@ -18,7 +18,6 @@
# Some choices below are based on the OS type:
NOTMAC := $(subst Darwin,,$(shell uname -s))
ASTYLE_BIN = astyle
ETAGS_BIN = $(shell command -v etags 2> /dev/null)
EGREP_BIN = $(shell command -v egrep 2> /dev/null)
@@ -1,86 +0,0 @@
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
@@ -1,79 +0,0 @@
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
@@ -1,72 +0,0 @@
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
@@ -1,79 +0,0 @@
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
@@ -1,72 +0,0 @@
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
-3
View File
@@ -190,8 +190,6 @@ 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.
@@ -200,7 +198,6 @@ 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
+1 -1
View File
@@ -119,7 +119,7 @@ int main(int argc, char *argv[])
}
LinearForm b(&fespace);
b.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
b.AddDomainIntegrator(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.AddBoundaryIntegrator(new VectorBoundaryLFIntegrator(f));
b.AddDomainIntegrator(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 -5
View File
@@ -9,7 +9,6 @@
// ex4 -m ../data/beam-hex.mesh -o 2 -pa
// ex4 -m ../data/escher.mesh
// ex4 -m ../data/fichera.mesh -o 2 -hb
// ex4 -m ../data/fichera.mesh -o 2 -hb -ea
// ex4 -m ../data/fichera-q2.vtk
// ex4 -m ../data/fichera-q3.mesh -o 2 -sc
// ex4 -m ../data/square-disc-nurbs.mesh
@@ -19,7 +18,6 @@
// ex4 -m ../data/amr-quad.mesh
// ex4 -m ../data/amr-hex.mesh
// ex4 -m ../data/amr-hex.mesh -o 2 -hb
// ex4 -m ../data/amr-hex.mesh -o 2 -hb -ea
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
// ex4 -m ../data/ref-prism.mesh -o 1
// ex4 -m ../data/octahedron.mesh -o 1
@@ -27,8 +25,6 @@
//
// Device sample runs:
// ex4 -m ../data/star.mesh -pa -d cuda
// ex4 -m ../data/star.mesh -hb -ea -d cuda
// ex4 -m ../data/amr-quad.mesh -hb -ea -d cuda
// ex4 -m ../data/star.mesh -pa -d raja-cuda
// ex4 -m ../data/star.mesh -pa -d raja-omp
// ex4 -m ../data/beam-hex.mesh -pa -d cuda
@@ -197,7 +193,7 @@ int main(int argc, char *argv[])
cout << "Size of linear system: " << A->Height() << endl;
// 11. Solve the linear system A X = B.
if (!pa && (!ea || hybridization))
if (!pa)
{
#ifndef MFEM_USE_SUITESPARSE
// Use a simple symmetric Gauss-Seidel preconditioner with PCG.
+1 -6
View File
@@ -9,7 +9,6 @@
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -o 2 -pa
// mpirun -np 4 ex4p -m ../data/escher.mesh -o 2 -sc
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb
// mpirun -np 4 ex4p -m ../data/fichera.mesh -o 2 -hb -ea
// mpirun -np 4 ex4p -m ../data/fichera-q2.vtk
// mpirun -np 4 ex4p -m ../data/fichera-q3.mesh -o 2 -sc
// mpirun -np 4 ex4p -m ../data/square-disc-nurbs.mesh -o 3
@@ -18,18 +17,14 @@
// mpirun -np 4 ex4p -m ../data/periodic-cube.mesh -no-bc
// mpirun -np 4 ex4p -m ../data/amr-quad.mesh
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb
// mpirun -np 3 ex4p -m ../data/amr-quad.mesh -o 2 -hb -ea
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -sc
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb -ea
// mpirun -np 4 ex4p -m ../data/ref-prism.mesh -o 1
// mpirun -np 4 ex4p -m ../data/octahedron.mesh -o 1
// mpirun -np 4 ex4p -m ../data/star-surf.mesh -o 3 -hb
//
// Device sample runs:
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d cuda
// mpirun -np 4 ex4p -m ../data/star.mesh -ea -hb -d cuda
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -ea -hb -d cuda
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-cuda
// mpirun -np 4 ex4p -m ../data/star.mesh -pa -d raja-omp
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh -pa -d cuda
@@ -235,7 +230,7 @@ int main(int argc, char *argv[])
pcg->SetMaxIter(2000);
pcg->SetPrintLevel(1);
if (hybridization) { prec = new HypreBoomerAMG(*A.As<HypreParMatrix>()); }
else if (pa || ea) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
else
{
ParFiniteElementSpace *prec_fespace =
+6 -35
View File
@@ -825,46 +825,14 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
Vector &b, OperatorHandle &A, Vector &X,
Vector &B, int copy_interior)
{
const SparseMatrix *P = fes->GetConformingProlongation();
const SparseMatrix *R = fes->GetConformingRestriction();
if (ext)
{
if (hybridization)
{
FormSystemMatrix(ess_tdof_list, A);
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
{
Operator *op;
Operator::FormSystemOperator(ess_tdof_list, op);
return dynamic_cast<ConstrainedOperator*>(op);
}());
MFEM_ASSERT(A_constrained != nullptr, "");
Vector conf_b, conf_x;
if (P)
{
// Nonconforming
conf_b.SetSize(P->Width());
conf_x.SetSize(P->Width());
P->MultTranspose(b, conf_b);
R->Mult(x, conf_x);
}
else
{
// Conforming
conf_b.MakeRef(b, 0, b.Size());
conf_x.MakeRef(x, 0, x.Size());
}
A_constrained->EliminateRHS(conf_x, conf_b);
if (P)
{
R->MultTranspose(conf_b, b); // store eliminated rhs in b
}
hybridization->ReduceRHS(conf_b, B);
ConstrainedOperator A_constrained(this, ess_tdof_list);
A_constrained.EliminateRHS(x, b);
hybridization->ReduceRHS(b, B);
X.SetSize(B.Size());
X = 0.0;
}
@@ -874,6 +842,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
}
return;
}
const SparseMatrix *P = fes->GetConformingProlongation();
FormSystemMatrix(ess_tdof_list, A);
// Transform the system and perform the elimination in B, based on the
@@ -909,6 +878,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
if (hybridization)
{
// Reduction to the Lagrange multipliers system
const SparseMatrix *R = fes->GetConformingRestriction();
Vector conf_b(P->Width()), conf_x(P->Width());
P->MultTranspose(b, conf_b);
R->Mult(x, conf_x);
@@ -921,6 +891,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
else
{
// Variational restriction with P
const SparseMatrix *R = fes->GetConformingRestriction();
B.SetSize(P->Width());
P->MultTranspose(b, B);
X.SetSize(R->Height());
+21 -14
View File
@@ -2137,14 +2137,14 @@ private:
int trial_dofs1D, test_dofs1D, quad1D;
public:
GradientIntegrator() :
Q{NULL}, trial_maps{NULL}, test_maps{NULL}, geom{NULL}
GradientIntegrator(const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q{NULL}, trial_maps{NULL}, test_maps{NULL}, geom{NULL}
{ }
GradientIntegrator(Coefficient *q_) :
Q{q_}, trial_maps{NULL}, test_maps{NULL}, geom{NULL}
GradientIntegrator(Coefficient *q_, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q{q_}, trial_maps{NULL}, test_maps{NULL}, geom{NULL}
{ }
GradientIntegrator(Coefficient &q) :
Q{&q}, trial_maps{NULL}, test_maps{NULL}, geom{NULL}
GradientIntegrator(Coefficient &q, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q{&q}, trial_maps{NULL}, test_maps{NULL}, geom{NULL}
{ }
void AssembleElementMatrix2(const FiniteElement &trial_fe,
@@ -2612,6 +2612,11 @@ protected:
Vector pa_data;
public:
VectorMassIntegrator(const IntegrationRule *ir)
: BilinearFormIntegrator(ir), vdim(-1), Q_order(0), Q(NULL), VQ(NULL),
MQ(NULL) { }
/// Construct an integrator with coefficient 1.0
VectorMassIntegrator() = default;
@@ -3038,14 +3043,15 @@ private:
int trial_dofs1D, test_dofs1D, quad1D;
public:
VectorDivergenceIntegrator() :
Q(NULL), trial_maps(NULL), test_maps(NULL), geom(NULL)
VectorDivergenceIntegrator(const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(NULL), trial_maps(NULL), test_maps(NULL),
geom(NULL)
{ }
VectorDivergenceIntegrator(Coefficient *q_) :
Q(q_), trial_maps(NULL), test_maps(NULL), geom(NULL)
VectorDivergenceIntegrator(Coefficient *q_, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(q_), trial_maps(NULL), test_maps(NULL), geom(NULL)
{ }
VectorDivergenceIntegrator(Coefficient &q) :
Q(&q), trial_maps(NULL), test_maps(NULL), geom(NULL)
VectorDivergenceIntegrator(Coefficient &q, const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir), Q(&q), trial_maps(NULL), test_maps(NULL), geom(NULL)
{ }
void AssembleElementMatrix2(const FiniteElement &trial_fe,
@@ -3263,8 +3269,9 @@ private:
void SetUpQuadratureSpaceAndCoefficients(const FiniteElementSpace &fes);
public:
ElasticityIntegrator(Coefficient &l, Coefficient &m)
{ lambda = &l; mu = &m; }
ElasticityIntegrator(Coefficient &l, Coefficient &m,
const IntegrationRule *ir = NULL) :
BilinearFormIntegrator(ir) { lambda = &l; mu = &m; }
/** With this constructor $\lambda = q_l m$ and $\mu = q_m m$
if $dim q_l + 2 q_m = 0$ then $tr(\sigma) = 0$. */
ElasticityIntegrator(Coefficient &m, real_t q_l, real_t q_m)
-67
View File
@@ -1302,73 +1302,6 @@ 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),
+5 -83
View File
@@ -114,10 +114,11 @@ 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(const Vector &c) { UpdateConstants(c); }
PWConstCoefficient(Vector &c)
{ constants.SetSize(c.Size()); constants=c; }
/// Update the constants with vector @a c.
void UpdateConstants(const Vector &c) { constants = c; }
void UpdateConstants(Vector &c) { constants.SetSize(c.Size()); constants=c; }
/// Return a reference to the i-th constant
real_t &operator()(int i) { return constants(i-1); }
@@ -1331,8 +1332,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
this will take ownership of the Coefficient passed in, but this
/** @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
can be overridden with the @a own parameter. */
void Set(int i, int j, Coefficient * c, bool own=true);
@@ -1872,85 +1873,6 @@ 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
{
+22
View File
@@ -652,6 +652,16 @@ void forall(func_t f,
class FDJacobian : public Operator
{
public:
FDJacobian(const Operator &op, real_t fixed_eps = 0.0) :
Operator(op.Height(), op.Width()),
op(op),
x(op.Width()),
fixed_eps(fixed_eps)
{
f.SetSize(Height());
xpev.SetSize(Width());
}
FDJacobian(const Operator &op, const Vector &x, real_t fixed_eps = 0.0) :
Operator(op.Height(), op.Width()),
op(op),
@@ -671,6 +681,18 @@ public:
MPI_COMM_WORLD);
}
void Update(const Vector &x_new)
{
x = x_new;
f.UseDevice(x.UseDevice());
xpev.UseDevice(x.UseDevice());
op.Mult(x, f);
const real_t xnorm_local = x.Norml2();
MPI_Allreduce(&xnorm_local, &xnorm, 1, MPITypeMap<real_t>::mpi_type, MPI_SUM,
MPI_COMM_WORLD);
}
void Mult(const Vector &v, Vector &y) const override
{
// See [1] for choice of eps.
+165
View File
@@ -12,6 +12,7 @@
#include "dgmassinv.hpp"
#include "bilinearform.hpp"
#include "dgmassinv_kernels.hpp"
#include "../general/forall.hpp"
namespace mfem
{
@@ -118,6 +119,151 @@ 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.
@@ -160,4 +306,23 @@ 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,7 +15,6 @@
#include "../linalg/kernels.hpp"
#include "kernels.hpp"
#include "integ/bilininteg_mass_kernels.hpp"
#include "dgmassinv.hpp"
namespace mfem
{
@@ -334,170 +333,6 @@ 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<NB*NB>(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
@@ -69,9 +69,9 @@ inline int ToLexOrdering2D(const int face_id, const int size1d, const int i)
}
/// @brief Given a face DOF index on a shared face, ordered lexicographically
/// relative to the element (where the local face is face_id), return the
/// corresponding face DOF index ordered lexicographically relative to the face
/// itself.
/// relative to element the element (where the local face is face_id), and
/// return the corresponding face DOF index ordered lexicographically relative
/// to the face itself.
MFEM_HOST_DEVICE
inline int PermuteFace2D(const int face_id, const int orientation,
const int size1d, const int index)
+34 -22
View File
@@ -231,7 +231,7 @@ void FiniteElement::CalcPhysLaplacian(ElementTransformation &Trans,
{
for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = hess(nd,0) + hess(nd,3) + hess(nd,5);
Laplacian[nd] = hess(nd,0) + hess(nd,4) + hess(nd,5);
}
}
else if (dim == 2)
@@ -268,9 +268,11 @@ void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
scale[0] = Gij(0,0);
scale[1] = 2*Gij(0,1);
scale[2] = 2*Gij(0,2);
scale[3] = Gij(1,1);
scale[4] = 2*Gij(1,2);
scale[5] = Gij(2,2);
scale[3] = 2*Gij(1,2);
scale[4] = Gij(2,2);
scale[5] = Gij(1,1);
}
else if (dim == 2)
{
@@ -307,12 +309,12 @@ void FiniteElement::CalcPhysHessian(ElementTransformation &Trans,
map[2] = 2;
map[3] = 1;
map[4] = 3;
map[5] = 4;
map[4] = 5;
map[5] = 3;
map[6] = 2;
map[7] = 4;
map[8] = 5;
map[7] = 3;
map[8] = 4;
}
else if (dim == 2)
{
@@ -380,7 +382,11 @@ const DofToQuad &FiniteElement::GetDofToQuad(const IntegrationRule &ir,
#pragma omp critical (DofToQuad)
#endif
{
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
for (int i = 0; i < dof2quad_array.Size(); i++)
{
d2q = dof2quad_array[i];
if (d2q->IntRule != &ir || d2q->mode != mode) { d2q = nullptr; }
}
if (!d2q)
{
#ifdef MFEM_THREAD_SAFE
@@ -655,22 +661,14 @@ void ScalarFiniteElement::ScalarLocalL2Restriction(
void NodalFiniteElement::CreateLexicographicFullMap(const IntegrationRule &ir)
const
{
// Get the FULL version of the map. This call contains omp critical region,
// so it is done before the critical region below.
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (DofToQuad)
#endif
{
// If the new Dof2Quad is already present, e.g. added in a previous call
// or added by another omp thread, return.
if (DofToQuad::SearchArray(dof2quad_array, ir,
DofToQuad::LEXICOGRAPHIC_FULL))
{ return; }
// Undo the native ordering which is what FiniteElement::GetDofToQuad
// returns.
// Get the FULL version of the map.
auto &d2q = GetDofToQuad(ir, DofToQuad::FULL);
//Undo the native ordering which is what FiniteElement::GetDofToQuad returns.
auto *d2q_new = new DofToQuad(d2q);
d2q_new->mode = DofToQuad::LEXICOGRAPHIC_FULL;
const int nqpt = ir.GetNPoints();
@@ -726,7 +724,13 @@ const DofToQuad &NodalFiniteElement::GetDofToQuad(const IntegrationRule &ir,
#pragma omp critical (DofToQuad)
#endif
{
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
//Should make this loop a function of FiniteElement
for (int i = 0; i < dof2quad_array.Size(); i++)
{
d2q = dof2quad_array[i];
if (d2q->IntRule == &ir && d2q->mode == mode) { break; }
d2q = nullptr;
}
}
if (d2q) { return *d2q; }
if (mode != DofToQuad::LEXICOGRAPHIC_FULL)
@@ -2627,7 +2631,15 @@ const DofToQuad &TensorBasisElement::GetTensorDofToQuad(
#pragma omp critical (DofToQuad)
#endif
{
d2q = DofToQuad::SearchArray(dof2quad_array, ir, mode);
for (int i = 0; i < dof2quad_array.Size(); i++)
{
auto* d2q_ = dof2quad_array[i];
if (d2q_->IntRule == &ir && d2q_->mode == mode)
{
d2q = d2q_;
break;
}
}
if (!d2q)
{
d2q = new DofToQuad;
+3 -25
View File
@@ -44,7 +44,7 @@ public:
NumBasisTypes = 9 /**< Keep track of maximum types to prevent
hard-coding */
};
/** @brief If the input does not represent a valid BasisType, abort with an
/** @brief If the input does not represents 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 represent a valid nodal BasisType, abort
/** @brief If the input does not represents a valid nodal BasisType, abort
with an error; otherwise return the input. */
static int CheckNodal(int b_type)
{
@@ -222,12 +222,6 @@ public:
/// Returns absolute value of the maps
DofToQuad Abs() const;
/// Auxiliary function for searching DofToQuad arrays.
static inline DofToQuad *SearchArray(
const Array<DofToQuad*> &dof2quad_array,
const IntegrationRule &ir,
DofToQuad::Mode mode);
};
/// Describes the function space on each element
@@ -413,7 +407,6 @@ public:
/** Each row of the result DenseMatrix @a Hessian contains upper triangular
part of the Hessian of one shape function.
The order in 2D is {u_xx, u_xy, u_yy}.
The order in 3D is {u_xx, u_xy, u_xz, u_yy, u_yz, u_zz}.
The size (#dof x (#dim (#dim+1)/2) of @a Hessian must be set in advance.*/
virtual void CalcHessian(const IntegrationPoint &ip,
DenseMatrix &Hessian) const;
@@ -1127,7 +1120,7 @@ public:
return GetPoints(p, btype, on_device);
}
/// Get coordinates of a closed (GaussLobatto) set of points if degree @a p
/// Get coordinates of a closed (GaussLegendre) set of points if degree @a p
const real_t *ClosedPoints(const int p,
const int btype = BasisType::GaussLobatto,
bool on_device = false)
@@ -1383,21 +1376,6 @@ public:
void InvertLinearTrans(ElementTransformation &trans,
const IntegrationPoint &pt, Vector &x);
// static inline method
inline DofToQuad *DofToQuad::SearchArray(
const Array<DofToQuad*> &dof2quad_array,
const IntegrationRule &ir,
DofToQuad::Mode mode)
{
for (int i = 0; i < dof2quad_array.Size(); i++)
{
DofToQuad *d2q = dof2quad_array[i];
if (d2q->IntRule == &ir && d2q->mode == mode) { return d2q; }
}
return nullptr;
}
} // namespace mfem
#endif
-48
View File
@@ -60,12 +60,6 @@ void Linear1DFiniteElement::CalcDShape(const IntegrationPoint &ip,
dshape(1,0) = 1.;
}
void Linear1DFiniteElement::CalcHessian(const IntegrationPoint &ip,
DenseMatrix &h) const
{
h = 0.0;
}
Linear2DFiniteElement::Linear2DFiniteElement()
: NodalFiniteElement(2, Geometry::TRIANGLE, 3, 1)
{
@@ -93,11 +87,6 @@ void Linear2DFiniteElement::CalcDShape(const IntegrationPoint &ip,
dshape(2,0) = 0.; dshape(2,1) = 1.;
}
void Linear2DFiniteElement::CalcHessian(const IntegrationPoint &ip,
DenseMatrix &h) const
{
h = 0.0;
}
BiLinear2DFiniteElement::BiLinear2DFiniteElement()
: NodalFiniteElement(2, Geometry::SQUARE, 4, 1, FunctionSpace::Qk)
@@ -1267,12 +1256,6 @@ void Linear3DFiniteElement::CalcDShape(const IntegrationPoint &ip,
}
}
void Linear3DFiniteElement::CalcHessian(const IntegrationPoint &ip,
DenseMatrix &h) const
{
h = 0.0;
}
void Linear3DFiniteElement::GetFaceDofs (int face, int **dofs, int *ndofs)
const
{
@@ -1649,37 +1632,6 @@ void TriLinear3DFiniteElement::CalcDShape(const IntegrationPoint &ip,
dshape(7,2) = ox * y;
}
void TriLinear3DFiniteElement::CalcHessian(const IntegrationPoint &ip,
DenseMatrix &h) const
{
real_t x = ip.x, y = ip.y, z = ip.z;
real_t ox = 1.-x, oy = 1.-y, oz = 1.-z;
h(0,0) = 0.; h(0,1) = oz; h(0,2) = oy;
h(0,3) = 0.; h(0,4) = ox; h(0,5) = 0.;
h(1,0) = 0.; h(1,1) = -oz; h(1,2) = -oy;
h(1,3) = 0.; h(1,4) = x; h(1,5) = 0.;
h(2,0) = 0.; h(2,1) = oz; h(2,2) = -y;
h(2,3) = 0.; h(2,4) = -x; h(2,5) = 0.;
h(3,0) = 0.; h(3,1) = -oz; h(3,2) = y;
h(3,3) = 0.; h(3,4) = -ox; h(3,5) = 0.;
h(4,0) = 0.; h(4,1) = z; h(4,2) = -oy;
h(4,3) = 0.; h(4,4) = -ox; h(4,5) = 0.;
h(5,0) = 0.; h(5,1) = -z; h(5,2) = oy;
h(5,3) = 0.; h(5,4) = -x; h(5,5) = 0.;
h(6,0) = 0.; h(6,1) = z; h(6,2) = y;
h(6,3) = 0.; h(6,4) = x; h(6,5) = 0.;
h(7,0) = 0.; h(7,1) = -z; h(7,2) = -y;
h(7,3) = 0.; h(7,4) = ox; h(7,5) = 0.;
}
P0SegmentFiniteElement::P0SegmentFiniteElement(int Ord)
: NodalFiniteElement(1, Geometry::SEGMENT, 1, Ord) // default Ord = 0
+1 -9
View File
@@ -50,8 +50,6 @@ public:
contains the derivative of one shape function */
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
void CalcHessian(const IntegrationPoint &ip,
DenseMatrix &h) const override;
};
/// A 2D linear element on triangle with nodes at the vertices of the triangle
@@ -72,8 +70,6 @@ public:
so that each row contains the derivatives of one shape function */
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
void CalcHessian(const IntegrationPoint &ip,
DenseMatrix &h) const override;
void ProjectDelta(int vertex, Vector &dofs) const override
{ dofs = 0.0; dofs(vertex) = 1.0; }
};
@@ -408,9 +404,6 @@ public:
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
void CalcHessian(const IntegrationPoint &ip,
DenseMatrix &h) const override;
void ProjectDelta(int vertex, Vector &dofs) const override
{ dofs = 0.0; dofs(vertex) = 1.0; }
@@ -452,8 +445,7 @@ public:
so that each row contains the derivatives of one shape function */
void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const override;
void CalcHessian(const IntegrationPoint &ip,
DenseMatrix &h) const override;
void ProjectDelta(int vertex, Vector &dofs) const override
{ dofs = 0.0; dofs(vertex) = 1.0; }
};
+5 -519
View File
@@ -84,46 +84,6 @@ 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
{
@@ -255,63 +215,6 @@ 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
{
@@ -445,10 +348,11 @@ void NURBS3DFiniteElement::CalcHessian (const IntegrationPoint &ip,
d2sum[0] += ( hessian(o,0) = d2sx*sy*sz*weights(o) );
d2sum[1] += ( hessian(o,1) = dsx*dsy*sz*weights(o) );
d2sum[2] += ( hessian(o,2) = dsx*sy*dsz*weights(o) );
d2sum[3] += ( hessian(o,3) = sx*d2sy*sz*weights(o) );
d2sum[4] += ( hessian(o,4) = sx*dsy*dsz*weights(o) );
d2sum[5] += ( hessian(o,5) = sx*sy*d2sz*weights(o) );
d2sum[3] += ( hessian(o,3) = sx*dsy*dsz*weights(o) );
d2sum[4] += ( hessian(o,4) = sx*sy*d2sz*weights(o) );
d2sum[5] += ( hessian(o,5) = sx*d2sy*sz*weights(o) );
}
}
}
@@ -497,85 +401,6 @@ 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
{
@@ -692,63 +517,6 @@ 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]; }
@@ -928,120 +696,6 @@ 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]; }
@@ -1163,68 +817,13 @@ 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();
@@ -1404,124 +1003,11 @@ 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,18 +86,6 @@ 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
@@ -133,18 +121,6 @@ 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
@@ -185,18 +161,6 @@ 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;
};
@@ -278,13 +242,6 @@ 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();
};
@@ -379,13 +336,6 @@ 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();
};
@@ -465,13 +415,6 @@ 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();
};
@@ -563,13 +506,6 @@ 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 | * | 2 | VALUE | H1 nodal elements |
| H1Pos_[DIM]_[ORDER] | H1 | * | 1 | 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} | * | * | 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) |
| 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,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_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
| ND_Trace@[CBTYPE][OBTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | H_CURL | H^{1/2}-conforming trace elements for H(curl) defined on the interface between mesh elements (faces) |
| ND_R1D_[DIM]_[ORDER] | H(curl) | * | 1 / 0 | H_CURL | 3D H(curl)-conforming Nedelec vector elements in 1D. |
| ND_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} | * | 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_Trace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_ValTrace_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_Trace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | INTEGRAL | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_ValTrace@[BTYPE]_[DIM]_[ORDER] | H^{1/2} | * | 1 / 0 | VALUE | H^{1/2}-conforming trace elements for H(div) defined on the interface between mesh elements (faces) |
| RT_R1D_[DIM]_[ORDER] | H(div) | * | 1 / 0 | H_DIV | 3D H(div)-conforming Raviart-Thomas vector elements in 1D. |
| RT_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 | * | * | VALUE | Discontinuous L2 elements |
| L2_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | VALUE | Discontinuous L2 elements |
| L2Int_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
| L2Int_T[BTYPE]_[DIM]_[ORDER] | L2 | * | * | INTEGRAL | Discontinuous L2 elements |
| L2Int_T[BTYPE]_[DIM]_[ORDER] | L2 | * | 0 | INTEGRAL | Discontinuous L2 elements |
| DG_Iface_[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_Iface@[BTYPE]_[DIM]_[ORDER] | - | * | 0 | 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] | - | * | * | 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) |
| 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 6-Serendipity 7-ClosedGL 8-IntegratedGLL) |
| [BTYPE] | BasisType of the element (0-GaussLegendre, 1 - GaussLobatto, 2-Bernstein, 3-OpenUniform, 4-CloseUniform, 5-OpenHalfUniform) |
| [OBTYPE] | Open BasisType of the element for elements which have both types |
| [CBTYPE] | Closed BasisType of the element for elements which have both types |
+13 -50
View File
@@ -1516,76 +1516,36 @@ const FaceRestriction *FiniteElementSpace::GetFaceRestriction(
const bool is_dg_space = IsDGSpace();
const L2FaceValues m = (is_dg_space && mul==L2FaceValues::DoubleValued) ?
L2FaceValues::DoubleValued : L2FaceValues::SingleValued;
auto key = std::make_tuple(is_dg_space, f_ordering, type, m);
key_face key = std::make_tuple(is_dg_space, f_ordering, type, m);
auto itr = L2F.find(key);
if (itr != L2F.end())
{
return itr->second.get();
return itr->second;
}
else
{
std::unique_ptr<FaceRestriction> res;
FaceRestriction *res;
if (is_dg_space)
{
if (Conforming())
{
res.reset(new L2FaceRestriction(*this, f_ordering, type, m));
res = new L2FaceRestriction(*this, f_ordering, type, m);
}
else
{
res.reset(new NCL2FaceRestriction(*this, f_ordering, type, m));
res = new NCL2FaceRestriction(*this, f_ordering, type, m);
}
}
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
{
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
}
else
{
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
res = new ConformingFaceRestriction(*this, f_ordering, type);
}
return L2F.emplace(key, std::move(res)).first->second.get();
}
}
const InterpolationManager &FiniteElementSpace::GetInterpolationManager(
ElementDofOrdering f_ordering, FaceType type) const
{
const auto key = make_tuple(f_ordering, type);
auto it = interpolations.find(key);
if (it != interpolations.end())
{
return *it->second;
}
else
{
auto interp = make_unique<InterpolationManager>(*this, f_ordering, type);
int face_idx = 0;
for (int f = 0; f < mesh->GetNumFacesWithGhost(); ++f)
{
Mesh::FaceInformation face = mesh->GetFaceInformation(f);
if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse())
{
continue;
}
if (face.IsConforming() || face.IsBoundary())
{
interp->RegisterFaceConformingInterpolation(face, face_idx);
}
else
{
interp->RegisterFaceCoarseToFineInterpolation(face, face_idx);
}
++face_idx;
}
// Transform the interpolation matrix map into contiguous memory.
interp->LinearizeInterpolatorMapIntoVector();
interp->InitializeNCInterpConfig();
return *interpolations.emplace(key, std::move(interp)).first->second;
L2F[key] = res;
return res;
}
}
@@ -4009,8 +3969,11 @@ void FiniteElementSpace::Destroy()
delete E2Q_array[i];
}
E2Q_array.SetSize(0);
for (auto &x : L2F)
{
delete x.second;
}
L2F.clear();
interpolations.clear();
for (int i = 0; i < E2IFQ_array.Size(); i++)
{
delete E2IFQ_array[i];
+12 -9
View File
@@ -13,7 +13,6 @@
#define MFEM_FESPACE
#include "../config/config.hpp"
#include "../general/hash_util.hpp"
#include "../linalg/ordering.hpp"
#include "../linalg/sparsemat.hpp"
#include "../mesh/mesh.hpp"
@@ -321,11 +320,18 @@ protected:
mutable OperatorHandle L2E_nat, L2E_lex;
/// The face restriction operators, see GetFaceRestriction().
using key_face = std::tuple<bool, ElementDofOrdering, FaceType, L2FaceValues>;
mutable std::unordered_map<key_face,std::unique_ptr<FaceRestriction>,
TupleHasher> L2F;
mutable std::unordered_map<std::tuple<ElementDofOrdering,FaceType>,
std::unique_ptr<InterpolationManager>, TupleHasher> interpolations;
struct key_hash
{
std::size_t operator()(const key_face& k) const
{
return std::get<0>(k)
+ 2 * (int)std::get<1>(k)
+ 4 * (int)std::get<2>(k)
+ 8 * (int)std::get<3>(k);
}
};
using map_L2F = std::unordered_map<const key_face,FaceRestriction*,key_hash>;
mutable map_L2F L2F;
mutable Array<QuadratureInterpolator*> E2Q_array;
mutable Array<FaceQuadratureInterpolator*> E2IFQ_array;
@@ -745,9 +751,6 @@ public:
ElementDofOrdering f_ordering, FaceType,
L2FaceValues mul = L2FaceValues::DoubleValued) const;
const InterpolationManager &GetInterpolationManager(
ElementDofOrdering f_ordering, FaceType type) const;
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
quadrature point values and/or derivatives (Q-vectors). */
/** An E-vector represents the element-wise discontinuous version of the FE
+60 -521
View File
@@ -2352,83 +2352,52 @@ void GridFunction::ProjectDeltaCoefficient(DeltaCoefficient &delta_coeff,
}
}
void GridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
void GridFunction::ProjectCoefficient(Coefficient &coeff)
{
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
DofTransformation doftrans;
Array<int> vdofs;
Vector vals;
if (delta_c == NULL)
{
if (fes->GetNURBSext() == NULL)
{
switch (type)
Array<int> vdofs;
Vector vals;
for (int i = 0; i < fes->GetNE(); i++)
{
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);
}
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
{
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 linear form
LinearForm b(fes);
b.AddDomainIntegrator(new DomainLFIntegrator(coeff));
b.Assemble();
for (int i = 0; i < fes->GetNE(); i++)
{
fes->GetElementVDofs(i, vdofs, doftrans);
vals.SetSize(vdofs.Size());
vals = signal;
// Define and assemble bilinear form
BilinearForm a(fes);
a.AddDomainIntegrator(new MassIntegrator());
a.Assemble();
fes->GetFE(i)->Project(coeff,
*fes->GetElementTransformation(i),
vals);
doftrans.TransformPrimal(vals);
// 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);
// 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);
}
}
// Solve and get solution
*this = 0.0;
cg.Mult(b,*this);
}
}
else
@@ -2441,167 +2410,6 @@ void GridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
}
}
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)
{
@@ -2626,318 +2434,49 @@ void GridFunction::ProjectCoefficient(
}
}
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff,
ProjectType type)
void GridFunction::ProjectCoefficient(VectorCoefficient &vcoeff)
{
Array<int> vdofs;
Vector vals;
DofTransformation doftrans;
if (fes->GetNURBSext() == NULL)
{
switch (type)
int i;
Array<int> vdofs;
Vector vals;
for (i = 0; i < fes->GetNE(); i++)
{
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);
}
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
{
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)
{
// Define and assemble linear form
LinearForm b(fes);
b.AddDomainIntegrator(new VectorFEDomainLFIntegrator(vcoeff));
b.Assemble();
// Define and assemble bilinear form
BilinearForm a(fes);
a.AddDomainIntegrator(new VectorFEMassIntegrator());
}
else
{
b.AddDomainIntegrator(new VectorDomainLFIntegrator(vcoeff));
a.AddDomainIntegrator(new VectorMassIntegrator());
}
a.Assemble();
b.Assemble();
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);
// 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);
// 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);
}
// Solve and get solution
*this = 0.0;
cg.Mult(b,*this);
}
}
+7 -71
View File
@@ -27,24 +27,6 @@
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
{
@@ -84,17 +66,13 @@ 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.
@@ -106,10 +84,6 @@ 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
@@ -446,30 +420,9 @@ 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 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);
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);
/** @brief Project @a coeff Coefficient to @a this GridFunction, using one
element for each degree of freedom in @a dofs and nodal interpolation on
@@ -479,26 +432,9 @@ 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 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);
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);
/** @brief Project @a vcoeff VectorCoefficient to @a this GridFunction, using
one element for each degree of freedom in @a dofs and nodal interpolation
+32 -59
View File
@@ -234,7 +234,7 @@ void FindPointsGSLIB::Setup(Mesh &m, const double bb_t, const double newt_tol,
}
void FindPointsGSLIB::FindPoints(const Vector &point_pos,
const int point_pos_ordering)
int point_pos_ordering)
{
MFEM_VERIFY(setupflag, "Use FindPointsGSLIB::Setup before finding points.");
bool dev_mode = (point_pos.UseDevice() && Device::IsEnabled());
@@ -482,7 +482,7 @@ void FindPointsGSLIB::SetupDevice()
}
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
const int point_pos_ordering)
int point_pos_ordering)
{
if (!DEV.setup_device)
{
@@ -505,13 +505,13 @@ void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
if (dim == 2)
{
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem,
gsl_ref, gsl_dist, points_cnt);
FindPointsLocal2(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
gsl_dist, points_cnt);
}
else
{
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem,
gsl_ref, gsl_dist, points_cnt);
FindPointsLocal3(point_pos, point_pos_ordering, gsl_code, gsl_elem, gsl_ref,
gsl_dist, points_cnt);
}
// Sync from device to host
@@ -1085,7 +1085,7 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
#else
void FindPointsGSLIB::SetupDevice() {};
void FindPointsGSLIB::FindPointsOnDevice(const Vector &point_pos,
const int point_pos_ordering) {};
int point_pos_ordering) {};
void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
Vector &field_out,
const int nel, const int ncomp,
@@ -1094,8 +1094,7 @@ void FindPointsGSLIB::InterpolateOnDevice(const Vector &field_in_evec,
#endif
void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
const int point_pos_ordering,
const double bb_t,
int point_pos_ordering, const double bb_t,
const double newt_tol, const int npt_max)
{
if (!setupflag || (mesh != &m) )
@@ -1106,28 +1105,16 @@ void FindPointsGSLIB::FindPoints(Mesh &m, const Vector &point_pos,
}
void FindPointsGSLIB::Interpolate(const Vector &point_pos,
const GridFunction &field_in,
Vector &field_out,
const int point_pos_ordering)
const GridFunction &field_in, Vector &field_out,
int point_pos_ordering)
{
FindPoints(point_pos, point_pos_ordering);
Interpolate(field_in, field_out);
}
void FindPointsGSLIB::Interpolate(const Vector &point_pos,
const GridFunction &field_in,
Vector &field_out,
const int point_pos_ordering,
const int field_out_ordering)
{
FindPoints(point_pos, point_pos_ordering);
Interpolate(field_in, field_out, field_out_ordering);
}
void FindPointsGSLIB::Interpolate(Mesh &m, const Vector &point_pos,
const GridFunction &field_in,
Vector &field_out,
const int point_pos_ordering)
const GridFunction &field_in, Vector &field_out,
int point_pos_ordering)
{
FindPoints(m, point_pos, point_pos_ordering);
Interpolate(field_in, field_out);
@@ -1483,7 +1470,7 @@ void FindPointsGSLIB::SetupSplitMeshesAndIntegrationRules(const int order)
}
void FindPointsGSLIB::GetNodalValues(const GridFunction *gf_in,
Vector &node_vals) const
Vector &node_vals)
{
const GridFunction *nodes = gf_in;
const FiniteElementSpace *fes = nodes->FESpace();
@@ -1771,13 +1758,6 @@ void FindPointsGSLIB::MapRefPosAndElemIndices()
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
Vector &field_out)
{
Interpolate(field_in, field_out, field_in.FESpace()->GetOrdering());
}
void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
Vector &field_out,
const int field_out_ordering)
{
const int gf_order = field_in.FESpace()->GetMaxElementOrder(),
mesh_order = mesh->GetNodalFESpace()->GetMaxElementOrder();
@@ -1820,7 +1800,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
const int maxOrder = field_in.FESpace()->GetMaxElementOrder();
InterpolateOnDevice(node_vals, field_out, NE_split_total, ncomp,
maxOrder+1, field_out_ordering);
maxOrder+1, field_in.FESpace()->GetOrdering());
return;
#endif
}
@@ -1832,13 +1812,12 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
field_in.FESpace()->IsVariableOrder() ==
mesh->GetNodalFESpace()->IsVariableOrder())
{
InterpolateH1(field_in, field_out, field_out_ordering);
InterpolateH1(field_in, field_out);
return;
}
else
{
InterpolateGeneral(field_in, field_out,
field_out_ordering);
InterpolateGeneral(field_in, field_out);
if (!fec_l2 || avgtype == AvgType::NONE) { return; }
}
@@ -1882,11 +1861,11 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
if (gf_order_h1 == mesh_order) // basis is GaussLobatto by default
{
InterpolateH1(field_in_h1, field_out_l2, field_out_ordering);
InterpolateH1(field_in_h1, field_out_l2);
}
else
{
InterpolateGeneral(field_in_h1, field_out_l2, field_out_ordering);
InterpolateGeneral(field_in_h1, field_out_l2);
}
// Copy interpolated values for the points on element border
@@ -1894,7 +1873,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
{
for (int i = 0; i < indl2.Size(); i++)
{
int idx = field_out_ordering == Ordering::byNODES?
int idx = field_in_h1.FESpace()->GetOrdering() == Ordering::byNODES?
indl2[i] + j*points_cnt:
indl2[i]*ncomp + j;
field_out(idx) = field_out_l2(idx);
@@ -1904,8 +1883,7 @@ void FindPointsGSLIB::Interpolate(const GridFunction &field_in,
}
void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
Vector &field_out,
const int field_out_ordering)
Vector &field_out)
{
FiniteElementSpace ind_fes(mesh, field_in.FESpace()->FEColl());
if (field_in.FESpace()->IsVariableOrder())
@@ -1935,8 +1913,7 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
dataptrout = i*points_cnt;
if (field_in.FESpace()->GetOrdering() == Ordering::byNODES)
{
field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin,
points_fld);
field_in_scalar.NewDataAndSize(field_in.GetData()+dataptrin, points_fld);
}
else
{
@@ -1968,7 +1945,7 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
(gslib::findpts_data_3 *)this->fdataD);
}
}
if (field_out_ordering == Ordering::byVDIM)
if (field_in.FESpace()->GetOrdering() == Ordering::byVDIM)
{
Vector field_out_temp = field_out;
for (int i = 0; i < ncomp; i++)
@@ -1982,8 +1959,7 @@ void FindPointsGSLIB::InterpolateH1(const GridFunction &field_in,
}
void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
Vector &field_out,
const int field_out_ordering)
Vector &field_out)
{
int ncomp = field_in.VectorDim(),
nptorig = points_cnt,
@@ -2003,7 +1979,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
if (dim == 3) { ip.z = gsl_mfem_ref(index*dim + 2); }
Vector localval(ncomp);
field_in.GetVectorValue(gsl_mfem_elem[index], ip, localval);
if (field_out_ordering == Ordering::byNODES)
if (field_in.FESpace()->GetOrdering() == Ordering::byNODES)
{
for (int i = 0; i < ncomp; i++)
{
@@ -2038,10 +2014,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
for (int index = 0; index < npt; index++)
{
if (gsl_code[index] == 2) { continue; }
for (int d = 0; d < dim; ++d)
{
pt->r[d]= gsl_mfem_ref(index*dim + d);
}
for (int d = 0; d < dim; ++d) { pt->r[d]= gsl_mfem_ref(index*dim + d); }
pt->index = index;
pt->proc = gsl_proc[index];
pt->el = gsl_mfem_elem[index];
@@ -2131,7 +2104,7 @@ void FindPointsGSLIB::InterpolateGeneral(const GridFunction &field_in,
sdpt = (struct send_pt *)sendpt->ptr;
for (int index = 0; index < static_cast<int>(sendpt->n); index++)
{
int idx = field_out_ordering == Ordering::byNODES ?
int idx = field_in.FESpace()->GetOrdering() == Ordering::byNODES ?
sdpt->index + j*nptorig :
sdpt->index*ncomp + j;
field_out(idx) = sdpt->ival;
@@ -2273,7 +2246,7 @@ void FindPointsGSLIB::DistributeInterpolatedValues(const Vector &int_vals,
}
}
void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb) const
void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb)
{
MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
@@ -2344,7 +2317,7 @@ void FindPointsGSLIB::GetAxisAlignedBoundingBoxes(Vector &aabb) const
}
void FindPointsGSLIB::GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
Vector &obbV) const
Vector &obbV)
{
MFEM_VERIFY(setupflag, "Call FindPointsGSLIB::Setup method first");
auto *findptsData3 = (gslib::findpts_data_3 *)this->fdataD;
@@ -2529,8 +2502,8 @@ void OversetFindPointsGSLIB::Setup(Mesh &m, const int meshid,
}
void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
const Array<unsigned int> &point_id,
const int point_pos_ordering)
Array<unsigned int> &point_id,
int point_pos_ordering)
{
MFEM_VERIFY(setupflag, "Use OversetFindPointsGSLIB::Setup before "
"finding points.");
@@ -2609,10 +2582,10 @@ void OversetFindPointsGSLIB::FindPoints(const Vector &point_pos,
}
void OversetFindPointsGSLIB::Interpolate(const Vector &point_pos,
const Array<unsigned int> &point_id,
Array<unsigned int> &point_id,
const GridFunction &field_in,
Vector &field_out,
const int point_pos_ordering)
int point_pos_ordering)
{
FindPoints(point_pos, point_id, point_pos_ordering);
Interpolate(field_in, field_out);
+15 -32
View File
@@ -119,13 +119,11 @@ protected:
} DEV;
/// Use GSLIB for communication and interpolation
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out,
const int field_out_ordering);
virtual void InterpolateH1(const GridFunction &field_in, Vector &field_out);
/// Uses GSLIB Crystal Router for communication followed by MFEM's
/// interpolation functions
virtual void InterpolateGeneral(const GridFunction &field_in,
Vector &field_out,
const int field_out_ordering);
Vector &field_out);
/// Since GSLIB is designed to work with quads/hexes, we split every
/// triangle/tet/prism/pyramid element into quads/hexes.
@@ -142,7 +140,7 @@ protected:
virtual void SetupSplitMeshesAndIntegrationRules(const int order);
/// Get GridFunction value at the points expected by GSLIB.
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals) const;
virtual void GetNodalValues(const GridFunction *gf_in, Vector &node_vals);
/// Map {r,s,t} coordinates from [-1,1] to [0,1] for MFEM. For simplices,
/// find the original element number (that was split into micro quads/hexes)
@@ -184,7 +182,7 @@ protected:
These positions can be ordered byNodes: (XXX...,YYY...,ZZZ) or
byVDim: (XYZ,XYZ,....XYZ) specified by @a point_pos_ordering. */
void FindPointsOnDevice(const Vector &point_pos,
const int point_pos_ordering = Ordering::byNODES);
int point_pos_ordering = Ordering::byNODES);
/** Interpolation of field values at prescribed reference space positions.
@param[in] field_in_evec E-vector of grid function to be interpolated.
@@ -255,15 +253,10 @@ public:
#gsl_dist Distance between the sought and the found point
in physical space. */
void FindPoints(const Vector &point_pos,
const int point_pos_ordering = Ordering::byNODES);
/// Convenience function when point positions are in a ParticleVector
void FindPoints(const ParticleVector &point_pos)
{
FindPoints(point_pos, point_pos.GetOrdering());
}
int point_pos_ordering = Ordering::byNODES);
/// Setup FindPoints and search positions
void FindPoints(Mesh &m, const Vector &point_pos,
const int point_pos_ordering = Ordering::byNODES,
int point_pos_ordering = Ordering::byNODES,
const double bb_t = 0.1, const double newt_tol = 1.0e-12,
const int npt_max = 256);
@@ -273,28 +266,20 @@ public:
\p field_in is in H1 and in the same space as the
mesh that was given to Setup().
@param[out] field_out Interpolated values. For points that are not found
the value is set to #default_interp_value.
The output ordering is determined from field_in.*/
the value is set to #default_interp_value. */
virtual void Interpolate(const GridFunction &field_in, Vector &field_out);
/// Interpolation of field values, with output ordering specification.
virtual void Interpolate(const GridFunction &field_in, Vector &field_out,
const int field_out_ordering);
/** Search positions and interpolate. The ordering (byNODES or byVDIM) of
the output values in \p field_out corresponds to the ordering used
in the input GridFunction \p field_in. */
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
Vector &field_out,
const int point_pos_ordering = Ordering::byNODES);
/// Search positions and interpolate with given point and output ordering.
void Interpolate(const Vector &point_pos, const GridFunction &field_in,
Vector &field_out, const int point_pos_ordering,
const int field_out_ordering);
int point_pos_ordering = Ordering::byNODES);
/** Setup FindPoints, search positions and interpolate. The ordering (byNODES
or byVDIM) of the output values in \p field_out corresponds to the
ordering used in the input GridFunction \p field_in. */
void Interpolate(Mesh &m, const Vector &point_pos,
const GridFunction &field_in, Vector &field_out,
const int point_pos_ordering = Ordering::byNODES);
int point_pos_ordering = Ordering::byNODES);
/// Average type to be used for L2 functions in-case a point is located at
/// an element boundary where the function might be multi-valued.
@@ -391,7 +376,7 @@ public:
/// The size of the returned vector is (nel x nverts x dim), where nel is the
/// number of elements (after splitting for simplcies), nverts is number of
/// vertices (4 in 2D, 8 in 3D), and dim is the spatial dimension.
void GetAxisAlignedBoundingBoxes(Vector &aabb) const;
void GetAxisAlignedBoundingBoxes(Vector &aabb);
/// Return the oriented bounding boxes (OBB) computed during \ref Setup.
/// Each OBB is represented using the inverse transformation (A^{-1}) and
@@ -401,8 +386,7 @@ public:
/// size (dim x dim x nel), and the OBB centers are returned in \p obbC,
/// a vector of size (nel x dim). The vertices of the OBBs are returned in
/// \p obbV, a vector of size (nel x nverts x dim) .
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC,
Vector &obbV) const;
void GetOrientedBoundingBoxes(DenseTensor &obbA, Vector &obbC, Vector &obbV);
};
/** \brief OversetFindPointsGSLIB enables use of findpts for arbitrary number of
@@ -462,14 +446,13 @@ public:
byNodes: (XXX...,YYY...,ZZZ) or
byVDim: (XYZ,XYZ,....XYZ) */
void FindPoints(const Vector &point_pos,
const Array<unsigned int> &point_id,
const int point_pos_ordering = Ordering::byNODES);
Array<unsigned int> &point_id,
int point_pos_ordering = Ordering::byNODES);
/** Search positions and interpolate */
void Interpolate(const Vector &point_pos,
const Array<unsigned int> &point_id,
void Interpolate(const Vector &point_pos, Array<unsigned int> &point_id,
const GridFunction &field_in, Vector &field_out,
const int point_pos_ordering = Ordering::byNODES);
int point_pos_ordering = Ordering::byNODES);
using FindPointsGSLIB::Interpolate;
};
+1 -7
View File
@@ -789,6 +789,7 @@ void Hybridization::ComputeH()
}
else
{
// TODO: add ones on the diagonal of zero rows
V->Finalize();
Array<HYPRE_BigInt> V_J(V->NumNonZeroElems());
MFEM_ASSERT(c_pfes, "");
@@ -822,13 +823,6 @@ void Hybridization::ComputeH()
MFEM_VERIFY(pH.Type() != Operator::PETSC_MATIS, "To be implemented");
pH.MakePtAP(plpH, pP);
delete lpH;
HypreParMatrix *hH = pH.As<HypreParMatrix>();
MFEM_ASSERT(hH, "");
SparseMatrix H_diag;
hH->GetDiag(H_diag);
H_diag.SetDiagIdentity();
}
#endif
}
+273 -453
View File
File diff suppressed because it is too large Load Diff
+2 -20
View File
@@ -14,11 +14,8 @@
#include "../config/config.hpp"
#include "../general/array.hpp"
#include "../linalg/operator.hpp"
#include "../linalg/vector.hpp"
#include <memory>
namespace mfem
{
@@ -48,30 +45,15 @@ protected:
Array<int> hat_dof_gather_map;
Array<DofType> hat_dof_marker;
Array<int> el_to_face; ///< Element to face connectivity.
Array<int> el_face_offsets; ///< Per-element offsets into @a el_to_face.
Array<int> face_to_el; ///< Face-to-element connectivity.
Array<int> face_face_offsets; ///< Face-to-face offsets.
int n_el_face; ///< Total number of element-to-face connections.
int n_face_face; ///< Total number of face-to-face connections.
Array<int> el_to_face;
Array<int> face_to_el;
Vector Ct_mat; ///< Constraint matrix (transposed) stored element-wise.
/// @name For parallel non-conforming meshes
///@{
std::unique_ptr<Operator> P_pc; ///< Partially conforming prolongation.
std::unique_ptr<Operator> P_nbr; ///< Face-neighbor prolongation.
///@}
Array<int> idofs, bdofs;
Vector Ahat, Ahat_ii, Ahat_ib, Ahat_bi, Ahat_bb;
Array<int> Ahat_ii_piv, Ahat_bb_piv;
/// Return the (partially) conforming prolongation on the constraint space.
const Operator &GetProlongation() const;
public:
/// Construct the constraint matrix.
void ConstructC();
+5 -8
View File
@@ -1004,16 +1004,13 @@ inline void SmemPADiffusionApply3D(const int NE,
const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
MFEM_VERIFY(D1D <= max_d1d, "");
MFEM_VERIFY(Q1D <= max_q1d, "");
const auto b = Reshape(b_.Read(), Q1D, D1D);
const auto g = Reshape(g_.Read(), Q1D, D1D);
const auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
const auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto b = Reshape(b_.Read(), Q1D, D1D);
auto g = Reshape(g_.Read(), Q1D, D1D);
auto d = Reshape(d_.Read(), Q1D, Q1D, Q1D, symmetric ? 6 : 9, NE);
auto x = Reshape(x_.Read(), D1D, D1D, D1D, NE);
auto y = Reshape(y_.ReadWrite(), D1D, D1D, D1D, NE);
MFEM_VERIFY(D1D <= Q1D, "THREAD_DIRECT requires D1D <= Q1D");
mfem::forall_3D<T_Q1D*T_Q1D*T_Q1D>(NE,
Q1D, Q1D, Q1D,
[=] MFEM_HOST_DEVICE (int e)
mfem::forall_3D(NE, Q1D, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
+6 -6
View File
@@ -1133,11 +1133,11 @@ inline void SmemPAMassApply3D(const int NE,
const int max_d1d = T_D1D ? T_D1D : DeviceDofQuadLimits::Get().MAX_D1D;
MFEM_VERIFY(D1D <= max_d1d, "");
MFEM_VERIFY(Q1D <= max_q1d, "");
const auto b = b_.Read();
const auto d = d_.Read();
const auto x = x_.Read();
auto b = b_.Read();
auto d = d_.Read();
auto x = x_.Read();
auto y = y_.ReadWrite();
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE (int e)
{
internal::SmemPAMassApply3D_Element<T_D1D,T_Q1D>(e, NE, b, d, x, y, d1d, q1d);
});
@@ -1156,8 +1156,8 @@ inline void EAMassAssemble1D(const int NE,
const int Q1D = T_Q1D ? T_Q1D : q1d;
MFEM_VERIFY(D1D <= DeviceDofQuadLimits::Get().MAX_D1D, "");
MFEM_VERIFY(Q1D <= DeviceDofQuadLimits::Get().MAX_Q1D, "");
const auto B = Reshape(basis.Read(), Q1D, D1D);
const auto D = Reshape(padata.Read(), Q1D, NE);
auto B = Reshape(basis.Read(), Q1D, D1D);
auto D = Reshape(padata.Read(), Q1D, NE);
auto M = Reshape(add ? eadata.ReadWrite() : eadata.Write(), D1D, D1D, NE);
mfem::forall_2D(NE, D1D, D1D, [=] MFEM_HOST_DEVICE (int e)
{
+20 -98
View File
@@ -28,7 +28,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
const FaceType ftype = FaceType::Interior;
const int nf = mesh.GetNFbyType(ftype);
const Geometry::Type geom = mesh.GetTypicalFaceGeometry();
const Geometry::Type geom = mesh.GetFaceGeometry(0);
const int trial_order = trial_fes.GetMaxElementOrder();
const int test_order = test_fes.GetMaxElementOrder();
const int qorder = test_order + trial_order - 1;
@@ -47,7 +47,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
});
}
const FiniteElement &trial_face_el = *trial_fes.GetTypicalTraceElement();
const FiniteElement &trial_face_el = *trial_fes.GetFaceElement(0);
const auto maps = &trial_face_el.GetDofToQuad(ir, DofToQuad::TENSOR);
const int ndof_face = trial_face_el.GetDof();
@@ -72,7 +72,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
MFEM_ABORT("Unknown kernel.");
}
const FiniteElement &test_el = *test_fes.GetTypicalFE();
const FiniteElement &test_el = *test_fes.GetFE(0);
const int n_faces_per_el = 2*dim; // assuming tensor product
// Get all the local face maps (mapping from lexicographic face index to
// lexicographic volume index, depending on the local face index).
@@ -90,10 +90,10 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
Array<int> face_info(nf * 4);
{
int fidx = 0;
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
Mesh::FaceInformation finfo = mesh.GetFaceInformation(f);
if (!finfo.IsInterior() || finfo.IsNonconformingCoarse()) { continue; }
if (!finfo.IsInterior()) { continue; }
face_info[0 + fidx*4] = finfo.element[0].local_face_id;
face_info[1 + fidx*4] = finfo.element[0].orientation;
face_info[2 + fidx*4] = finfo.element[1].local_face_id;
@@ -114,7 +114,7 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
else
{
d_emat = emat.Write();
emat = 0.0; // Will execute on device, since Write() sets the device flag
mfem::forall(emat.Size(), [=] MFEM_HOST_DEVICE (int i) { d_emat[i] = 0.0; });
}
const auto face_mats = Reshape(mass_emat.Read(), ndof_face, ndof_face, nf);
@@ -133,104 +133,26 @@ void NormalTraceJumpIntegrator::AssembleEAInteriorFaces(
}
};
auto permute_face_2 = [=] MFEM_HOST_DEVICE(int local_face_1, int local_face_2,
int orient, int size1d, int index)
mfem::forall_3D(nf, ndof_face, ndof_face, 2, [=] MFEM_HOST_DEVICE (int f)
{
if (dim == 2)
MFEM_FOREACH_THREAD(el_i, z, 2)
{
return internal::PermuteFace2D(local_face_1, local_face_2, orient,
size1d, index);
}
else // dim == 3
{
return internal::PermuteFace3D(local_face_1, local_face_2, orient,
size1d, index);
}
};
if (mesh.Conforming())
{
mfem::forall_3D(nf, ndof_face, ndof_face, 2, [=] MFEM_HOST_DEVICE (int f)
{
MFEM_FOREACH_THREAD(el_i, z, 2)
const int lf_i = d_face_info(0, el_i, f);
const int orient = d_face_info(1, el_i, f);
// Loop over face indices in "native ordering"
MFEM_FOREACH_THREAD(i_lex, x, ndof_face)
{
const int lf_i = d_face_info(0, el_i, f);
const int orient = d_face_info(1, el_i, f);
// Loop over face indices in "native ordering"
MFEM_FOREACH_THREAD(i_lex, x, ndof_face)
// Convert to lexicographic relative to the face itself
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
// Convert from lexicographic face DOF to volume DOF
const int i = d_face_maps(i_lex, lf_i);
MFEM_FOREACH_THREAD(j, y, ndof_face)
{
// Convert to lexicographic relative to the face itself
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
// Convert from lexicographic face DOF to volume DOF
const int i = d_face_maps(i_lex, lf_i);
MFEM_FOREACH_THREAD(j, y, ndof_face)
{
el_mats(i, j, el_i, f) += face_mats(i_face, j, f);
}
el_mats(i, j, el_i, f) += face_mats(i_face, j, f);
}
}
});
}
else
{
const InterpolationManager &interp =
test_fes.GetInterpolationManager(ElementDofOrdering::LEXICOGRAPHIC, ftype);
auto interp_configs = interp.GetFaceInterpConfig().Read();
const int nc_size = interp.GetNumInterpolators();
auto d_interp = Reshape(interp.GetInterpolators().Read(),
ndof_face, ndof_face, nc_size);
mfem::forall(nf, [=] MFEM_HOST_DEVICE (int f)
{
const InterpConfig conf = interp_configs[f];
const int master_side = conf.master_side;
const int interp_index = conf.index;
const int lf_0 = d_face_info(0, 0, f);
for (int el_i = 0; el_i < 2; ++el_i)
{
const int lf_i = d_face_info(0, el_i, f);
const int orient = d_face_info(1, el_i, f);
for (int j = 0; j < ndof_face; j++)
{
for (int i_lex = 0; i_lex < ndof_face; i_lex++)
{
real_t val = 0.0;
if (conf.is_non_conforming && el_i == master_side)
{
// Interpolate from el_i (coarse element) to the fine face.
// The mapping is given by d_interp, which uses indices
// relative to element 0.
// i0 is lexicographic relative to element 0
const int i0 = permute_face_2(lf_i, lf_0, orient, d1d, i_lex);
// k0 is lexicographic relative to element 0
for (int k0 = 0; k0 < ndof_face; k0++)
{
// k is relative to the face itself
const int k = permute_face(lf_0, orient, d1d, k0);
val += d_interp(k0, i0, interp_index)
* face_mats(k, j, f);
}
}
else
{
// Convert to lexicographic relative to the face itself
const int i_face = permute_face(lf_i, orient, d1d, i_lex);
val = face_mats(i_face, j, f);
}
// Convert from lexicographic face DOF to volume DOF
const int i = d_face_maps(i_lex, lf_i);
el_mats(i, j, el_i, f) += val;
}
}
}
});
}
}
});
}
}
+2 -2
View File
@@ -54,7 +54,7 @@ void SmemPAVectorDiffusionApply2D(const int NE,
const auto XE = Reshape(x.Read(), D1D, D1D, SDIM, NE);
auto YE = Reshape(y.ReadWrite(), D1D, D1D, SDIM, NE);
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
@@ -120,7 +120,7 @@ void SmemPAVectorDiffusionApply3D(const int NE,
const auto XE = Reshape(x.Read(), D1D, D1D, D1D, SDIM, NE);
auto YE = Reshape(y.ReadWrite(), D1D, D1D, D1D, SDIM, NE);
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
+2 -2
View File
@@ -51,7 +51,7 @@ void SmemPAVectorMassApply2D(const int NE,
const auto X = Reshape(x.Read(), D1D, D1D, VDIM, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, VDIM, NE);
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
@@ -119,7 +119,7 @@ void SmemPAVectorMassApply3D(const int NE,
const auto X = Reshape(x.Read(), D1D, D1D, D1D, VDIM, NE);
auto Y = Reshape(y.ReadWrite(), D1D, D1D, D1D, VDIM, NE);
mfem::forall_2D<T_Q1D*T_Q1D>(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
mfem::forall_2D(NE, Q1D, Q1D, [=] MFEM_HOST_DEVICE(int e)
{
constexpr int MD1 = T_D1D > 0 ? SetMaxOf(T_D1D) : DofQuadLimits::MAX_T1D;
constexpr int MQ1 = T_Q1D > 0 ? SetMaxOf(T_Q1D) : DofQuadLimits::MAX_T1D;
+31 -3
View File
@@ -14,7 +14,6 @@
#include "../config/config.hpp"
#include "kernel_reporter.hpp"
#include "../general/hash_util.hpp"
#include <unordered_map>
#include <tuple>
#include <type_traits>
@@ -87,6 +86,35 @@ namespace mfem
} \
}
/// @brief Hashes variadic packs for which each type contained in the variadic
/// pack has a specialization of `std::hash` available.
///
/// For example, packs containing int, bool, enum values, etc.
template<typename ...KernelParameters>
struct KernelDispatchKeyHash
{
private:
template<int N>
size_t operator()(std::tuple<KernelParameters...> value) const { return 0; }
// The hashing formula here is taken directly from the Boost library, with
// the magic number 0x9e3779b9 chosen to minimize hashing collisions.
template<std::size_t N, typename THead, typename... TTail>
size_t operator()(std::tuple<KernelParameters...> value) const
{
constexpr int Index = N - sizeof...(TTail) - 1;
auto lhs_hash = std::hash<THead>()(std::get<Index>(value));
auto rhs_hash = operator()<N, TTail...>(value);
return lhs_hash^(rhs_hash + 0x9e3779b9 + (lhs_hash<<6) + (lhs_hash>>2));
}
public:
/// Returns the hash of the given @a value.
size_t operator()(std::tuple<KernelParameters...> value) const
{
return operator()<sizeof...(KernelParameters),KernelParameters...>(value);
}
};
namespace internal { template<typename... Types> struct KernelTypeList { }; }
template<typename... T> class KernelDispatchTable { };
@@ -100,8 +128,8 @@ class KernelDispatchTable<Kernels,
internal::KernelTypeList<Params...>,
internal::KernelTypeList<OptParams...>>
{
using TableType =
std::unordered_map<std::tuple<Params...>, Signature, TupleHasher>;
using TableType = std::unordered_map<std::tuple<Params...>,
Signature, KernelDispatchKeyHash<Params...>>;
TableType table;
/// @brief Call function @a f with arguments @a args (perfect forwaring).
+4 -1
View File
@@ -78,7 +78,10 @@ template <int Dim>
void BuildBoxes(const Mesh &mesh,
std::vector<::moonolith::AABB<Dim, double>> &element_boxes)
{
MFEM_ASSERT(mesh.Dimension() == Dim, "Mesh and box dimensions mismatched");
#ifndef NDEBUG
const int dim = mesh.Dimension();
assert(dim == Dim);
#endif
element_boxes.resize(mesh.GetNE());
DenseMatrix pts;
+13 -2
View File
@@ -758,7 +758,7 @@ void VectorConvectionNLFIntegrator::AssembleElementVector(
EF.UseExternalData(elfun.GetData(), nd, dim);
ELV.UseExternalData(elvect.GetData(), nd, dim);
Vector vec1(dim), vec2(dim);
Vector vec1(dim), vec2(dim), vecvq(dim);
const IntegrationRule *ir = GetIntegrationRule(el, T);
ELV = 0.0;
for (int i = 0; i < ir->GetNPoints(); i++)
@@ -772,6 +772,11 @@ void VectorConvectionNLFIntegrator::AssembleElementVector(
MultAtB(EF, dshape, gradEF);
EF.MultTranspose(shape, vec1);
if (VQ)
{
VQ->Eval(vecvq, T, ip);
vec1 += vecvq;
}
gradEF.Mult(vec1, vec2);
vec2 *= w;
AddMultVWt(shape, vec2, ELV);
@@ -797,7 +802,7 @@ void VectorConvectionNLFIntegrator::AssembleElementGrad(
EF.UseExternalData(elfun.GetData(), nd, dim);
real_t w;
Vector vec1(dim), vec2(dim), vec3(nd);
Vector vec1(dim), vec2(dim), vec3(nd), vecvq(dim);
const IntegrationRule *ir = GetIntegrationRule(el, trans);
@@ -822,6 +827,12 @@ void VectorConvectionNLFIntegrator::AssembleElementGrad(
MultAtB(EF, dshapex, gradEF);
EF.MultTranspose(shape, vec1);
if (VQ)
{
VQ->Eval(vecvq, trans, ip);
vec1 += vecvq;
}
trans.AdjugateJacobian().Mult(vec1, vec2);
vec2 *= w;
+8 -1
View File
@@ -381,6 +381,7 @@ class VectorConvectionNLFIntegrator : public NonlinearFormIntegrator
{
private:
Coefficient *Q{};
VectorCoefficient *VQ{};
DenseMatrix dshape, dshapex, EF, gradEF, ELV, elmat_comp;
Vector shape;
// PA extension
@@ -390,7 +391,13 @@ private:
int dim, ne, nq;
public:
VectorConvectionNLFIntegrator(Coefficient &q): Q(&q) { }
VectorConvectionNLFIntegrator(Coefficient &q,
const IntegrationRule *ir = NULL) :
NonlinearFormIntegrator(ir), Q(&q) { }
VectorConvectionNLFIntegrator(Coefficient &q, VectorCoefficient &vq,
const IntegrationRule *ir = NULL) :
NonlinearFormIntegrator(ir), Q(&q), VQ(&vq) { }
VectorConvectionNLFIntegrator() = default;
+3 -9
View File
@@ -488,16 +488,10 @@ void ParBilinearForm::FormLinearSystem(
R.Mult(x, true_X);
FormSystemMatrix(ess_tdof_list, A);
std::unique_ptr<ConstrainedOperator> A_constrained([&]()
{
Operator *op;
Operator::FormSystemOperator(ess_tdof_list, op);
return dynamic_cast<ConstrainedOperator*>(op);
}());
MFEM_ASSERT(A_constrained != nullptr, "");
ConstrainedOperator *A_constrained;
Operator::FormConstrainedSystemOperator(ess_tdof_list, A_constrained);
A_constrained->EliminateRHS(true_X, true_B);
delete A_constrained;
R.MultTranspose(true_B, b);
hybridization->ReduceRHS(true_B, B);
X.SetSize(B.Size());
+9 -8
View File
@@ -646,38 +646,39 @@ const FaceRestriction *ParFiniteElementSpace::GetFaceRestriction(
auto itr = L2F.find(key);
if (itr != L2F.end())
{
return itr->second.get();
return itr->second;
}
else
{
std::unique_ptr<FaceRestriction> res;
FaceRestriction *res;
if (is_dg_space)
{
if (Conforming())
{
res.reset(new ParL2FaceRestriction(*this, f_ordering, type, m));
res = new ParL2FaceRestriction(*this, f_ordering, type, m);
}
else
{
res.reset(new ParNCL2FaceRestriction(*this, f_ordering, type, m));
res = new ParNCL2FaceRestriction(*this, f_ordering, type, m);
}
}
else if (dynamic_cast<const DG_Interface_FECollection*>(fec))
{
res.reset(new L2InterfaceFaceRestriction(*this, f_ordering, type));
res = new L2InterfaceFaceRestriction(*this, f_ordering, type);
}
else
{
if (Conforming())
{
res.reset(new ConformingFaceRestriction(*this, f_ordering, type));
res = new ConformingFaceRestriction(*this, f_ordering, type);
}
else
{
res.reset(new ParNCH1FaceRestriction(*this, f_ordering, type));
res = new ParNCH1FaceRestriction(*this, f_ordering, type);
}
}
return L2F.emplace(key, std::move(res)).first->second.get();
L2F[key] = res;
return res;
}
}
-2
View File
@@ -483,8 +483,6 @@ public:
const FiniteElement *GetFaceNbrFaceFE(int i) const;
const Array<HYPRE_BigInt> &GetFaceNbrGlobalDofMapArray() { return face_nbr_glob_dof_map; }
const HYPRE_BigInt *GetFaceNbrGlobalDofMap() { return face_nbr_glob_dof_map; }
const Array<HYPRE_BigInt> &GetFaceNbrGlobalDofMapArray() const
{ return face_nbr_glob_dof_map; }
ElementTransformation *GetFaceNbrElementTransformation(int i) const
{ return pmesh->GetFaceNbrElementTransformation(i); }
+2 -152
View File
@@ -543,22 +543,13 @@ void ParGridFunction::GetElementDofValues(int el, Vector &dof_vals) const
}
}
void ParGridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
void ParGridFunction::ProjectCoefficient(Coefficient &coeff)
{
DeltaCoefficient *delta_c = dynamic_cast<DeltaCoefficient *>(&coeff);
if (delta_c == NULL)
{
(*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);
}
GridFunction::ProjectCoefficient(coeff);
}
else
{
@@ -574,147 +565,6 @@ void ParGridFunction::ProjectCoefficient(Coefficient &coeff, ProjectType type)
}
}
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
+1 -21
View File
@@ -72,10 +72,6 @@ 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
@@ -261,11 +257,7 @@ public:
void GetElementDofValues(int el, Vector &dof_vals) const override;
using GridFunction::ProjectCoefficient;
void ProjectCoefficient(Coefficient &coeff,
ProjectType type = ProjectType::DEFAULT) override;
void ProjectCoefficient(VectorCoefficient &vcoeff,
ProjectType type = ProjectType::DEFAULT) override;
void ProjectCoefficient(Coefficient &coeff) override;
using GridFunction::ProjectDiscCoefficient;
/** @brief Project a discontinuous vector coefficient as a grid function on
@@ -290,18 +282,6 @@ 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[],
+7
View File
@@ -994,6 +994,7 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
{
if ( face.IsConforming() )
{
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
SetFaceDofsScatterIndices1(face,f_ind);
if ( m==L2FaceValues::DoubleValued )
{
@@ -1009,6 +1010,7 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
}
else // Non-conforming face
{
interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
SetFaceDofsScatterIndices1(face,f_ind);
if ( m==L2FaceValues::DoubleValued )
{
@@ -1026,6 +1028,7 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
}
else if (type==FaceType::Boundary && face.IsBoundary())
{
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
SetFaceDofsScatterIndices1(face,f_ind);
if ( m==L2FaceValues::DoubleValued )
{
@@ -1043,6 +1046,10 @@ void ParNCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
{
gather_offsets[i] += gather_offsets[i - 1];
}
// Transform the interpolation matrix map into a contiguous memory structure.
interpolations.LinearizeInterpolatorMapIntoVector();
interpolations.InitializeNCInterpConfig();
}
void ParNCL2FaceRestriction::ComputeGatherIndices()
+6 -2
View File
@@ -326,7 +326,9 @@ public:
@param[in] keep_nbr_block When set to true the SparseMatrix will
include the rows (in addition to the columns)
corresponding to face-neighbor dofs. The
default behavior is to disregard those rows. */
default behavior is to disregard those rows.
@warning This method is not implemented yet. */
void FillI(SparseMatrix &mat,
const bool keep_nbr_block = false) const override;
@@ -362,7 +364,9 @@ public:
@param[in] keep_nbr_block When set to true the SparseMatrix will
include the rows (in addition to the columns)
corresponding to face-neighbor dofs. The
default behavior is to disregard those rows. */
default behavior is to disregard those rows.
@warning This method is not implemented yet. */
void FillJAndData(const Vector &fea_data,
SparseMatrix &mat,
const bool keep_nbr_block = false) const override;
+1 -7
View File
@@ -50,13 +50,7 @@ QuadratureInterpolator::DetKernelType
QuadratureInterpolator::DetKernels::Fallback(
int DIM, int SDIM, int D1D, int Q1D)
{
if (DIM == 1)
{
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<0,0,2>; }
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<0,0,3>; }
else { MFEM_ABORT(""); }
}
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D; }
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface; }
else if (DIM == 3)
+1 -51
View File
@@ -56,50 +56,6 @@ inline void Det1D(const int NE,
});
}
template<int T_D1D = 0, int T_Q1D = 0, int T_SDIM = 3>
inline void Det1DSurface(const int NE,
const real_t *b,
const real_t *g,
const real_t *x,
real_t *y,
const int d1d = 0,
const int q1d = 0,
Vector *d_buff = nullptr)
{
MFEM_CONTRACT_VAR(b);
MFEM_CONTRACT_VAR(d_buff);
const int D1D = T_D1D ? T_D1D : d1d;
const int Q1D = T_Q1D ? T_Q1D : q1d;
const auto G = Reshape(g, Q1D, D1D);
const auto X = Reshape(x, D1D, T_SDIM, NE);
auto Y = Reshape(y, Q1D, NE);
mfem::forall(NE, [=] MFEM_HOST_DEVICE (int e)
{
for (int q = 0; q < Q1D; q++)
{
real_t grad[T_SDIM];
for (int s = 0; s < T_SDIM; s++) { grad[s] = 0.0; }
for (int d = 0; d < D1D; d++)
{
const real_t gval = G(q, d);
for (int s = 0; s < T_SDIM; s++)
{
grad[s] += gval * X(d, s, e);
}
}
real_t norm2 = 0.0;
for (int s = 0; s < T_SDIM; s++)
{
norm2 += grad[s] * grad[s];
}
Y(q, e) = std::sqrt(norm2);
}
});
}
template<int T_D1D = 0, int T_Q1D = 0>
inline void Det2D(const int NE,
const real_t *b,
@@ -334,13 +290,7 @@ template<int DIM, int SDIM, int D1D, int Q1D>
QuadratureInterpolator::DetKernelType
QuadratureInterpolator::DetKernels::Kernel()
{
if (DIM == 1)
{
if (SDIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if (SDIM == 2) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 2>; }
else if (SDIM == 3) { return internal::quadrature_interpolator::Det1DSurface<D1D, Q1D, 3>; }
else { MFEM_ABORT(""); }
}
if (DIM == 1) { return internal::quadrature_interpolator::Det1D; }
else if (DIM == 2 && SDIM == 2) { return internal::quadrature_interpolator::Det2D<D1D, Q1D>; }
else if (DIM == 2 && SDIM == 3) { return internal::quadrature_interpolator::Det2DSurface<D1D, Q1D>; }
else if (DIM == 3) { return internal::quadrature_interpolator::Det3D<D1D, Q1D>; }
+1 -2
View File
@@ -542,8 +542,7 @@ void QuadratureInterpolator::Mult(const Vector &e_vec,
}
MFEM_ASSERT(!(eval_flags & DETERMINANTS) || dim == vdim ||
(dim == 2 && vdim == 3) || (dim == 1 && vdim == 2) ||
(dim == 1 && vdim == 3), "Invalid dimensions for determinants.");
(dim == 2 && vdim == 3), "Invalid dimensions for determinants.");
MFEM_ASSERT(fespace->GetMesh()->GetNumGeometries(
fespace->GetMesh()->Dimension()) == 1,
"mixed meshes are not supported");
+42 -118
View File
@@ -1506,12 +1506,12 @@ void L2FaceRestriction::EnsureNormalDerivativeRestriction() const
}
}
InterpolationManager::InterpolationManager(const FiniteElementSpace &fes_,
ElementDofOrdering ordering_,
InterpolationManager::InterpolationManager(const FiniteElementSpace &fes,
ElementDofOrdering ordering,
FaceType type)
: fes(fes_),
ordering(ordering_),
interp_config(fes.GetNFbyType(type)),
: fes(fes),
ordering(ordering),
interp_config( fes.GetNFbyType(type) ),
nc_cpt(0)
{ }
@@ -1536,8 +1536,7 @@ void InterpolationManager::RegisterFaceCoarseToFineInterpolation(
face.element[0].local_face_id +
6*face.element[1].local_face_id +
36*face.element[1].orientation ;
// Unfortunately we can't trust uniqueness of the ptMat to identify the
// transformation.
// Unfortunately we can't trust unicity of the ptMat to identify the transformation.
Key key(ptMat, face_key);
auto itr = interp_map.find(key);
if ( itr == interp_map.end() )
@@ -1584,27 +1583,17 @@ const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
IsoparametricTransformation isotr;
isotr.SetIdentityTransformation(trace_fe->GetGeomType());
isotr.SetPointMat(*ptMat);
DenseMatrix& trans_pt_mat = isotr.GetPointMat();
// PointMatrix needs to be flipped in 2D
if ( trace_fe->GetGeomType()==Geometry::SEGMENT && !is_ghost_slave )
{
std::swap(trans_pt_mat(0,0),trans_pt_mat(0,1));
}
DenseMatrix native_interpolator(face_dofs,face_dofs);
trace_fe->GetLocalInterpolation(isotr, native_interpolator);
if (trace_fe->GetMapType() == FiniteElement::INTEGRAL)
{
// Handle potentially inverted Jacobian matrix
isotr.SetIntPoint(&Geometries.GetCenter(trace_fe->GetGeomType()));
native_interpolator *= (isotr.Weight() >= 0) ? 1.0 : -1.0;
}
const int dim = trace_fe->GetDim()+1;
const int dof1d = trace_fe->GetOrder()+1;
int orientation_i = face.element[1].orientation;
const int orientation_j = face.element[1].orientation;
// In 2D, need to flip orientation of the segments`
if (trace_fe->GetGeomType() == Geometry::SEGMENT && !is_ghost_slave)
{
orientation_i = 1;
}
const int orientation = face.element[1].orientation;
for (int i = 0; i < face_dofs; i++)
{
const int ni = (dof_map.Size()==0) ? i : dof_map[i];
@@ -1613,7 +1602,7 @@ const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
{
// master side is elem 2, so we permute to order dofs as elem 1.
li = PermuteFaceL2(dim, face_id2, face_id1,
orientation_i, dof1d, li);
orientation, dof1d, li);
}
for (int j = 0; j < face_dofs; j++)
{
@@ -1622,7 +1611,7 @@ const DenseMatrix* InterpolationManager::GetCoarseToFineInterpolation(
{
// master side is elem 2, so we permute to order dofs as elem 1.
lj = PermuteFaceL2(dim, face_id2, face_id1,
orientation_j, dof1d, lj);
orientation, dof1d, lj);
}
const int nj = (dof_map.Size()==0) ? j : dof_map[j];
(*interpolator)(li,lj) = native_interpolator(ni,nj);
@@ -1687,7 +1676,7 @@ NCL2FaceRestriction::NCL2FaceRestriction(const FiniteElementSpace &fes,
const L2FaceValues m,
bool build)
: L2FaceRestriction(fes, f_ordering, type, m, false),
interpolations(fes.GetInterpolationManager(ordering, type))
interpolations(fes, f_ordering, type)
{
if (!build) { return; }
x_interp.UseDevice(true);
@@ -2213,6 +2202,14 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
{
PermuteAndSetFaceDofsScatterIndices2(face,f_ind);
}
if ( face.IsConforming() )
{
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
}
else // Non-conforming face
{
interpolations.RegisterFaceCoarseToFineInterpolation(face,f_ind);
}
f_ind++;
}
else if ( type==FaceType::Boundary && face.IsBoundary() )
@@ -2222,6 +2219,7 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
{
SetBoundaryDofsScatterIndices2(face,f_ind);
}
interpolations.RegisterFaceConformingInterpolation(face,f_ind);
f_ind++;
}
}
@@ -2234,6 +2232,10 @@ void NCL2FaceRestriction::ComputeScatterIndicesAndOffsets()
{
gather_offsets[i] += gather_offsets[i - 1];
}
// Transform the interpolation matrix map into a contiguous memory structure.
interpolations.LinearizeInterpolatorMapIntoVector();
interpolations.InitializeNCInterpConfig();
}
void NCL2FaceRestriction::ComputeGatherIndices()
@@ -2276,18 +2278,6 @@ void NCL2FaceRestriction::ComputeGatherIndices()
gather_offsets[0] = 0;
}
static int GetSharedVSize(const FiniteElementSpace &fes)
{
#ifdef MFEM_USE_MPI
if (auto pfes = dynamic_cast<const ParFiniteElementSpace*>(&fes))
{
const_cast<ParFiniteElementSpace*>(pfes)->ExchangeFaceNbrData();
return pfes->GetFaceNbrVSize();
}
#endif
return 0;
}
L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
const FiniteElementSpace& fes_,
const ElementDofOrdering ordering_,
@@ -2298,54 +2288,25 @@ L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
nfaces(fes.GetNFbyType(type)),
vdim(fes.GetVDim()),
byvdim(fes.GetOrdering() == Ordering::byVDIM),
face_dofs(fes.GetTypicalTraceElement()->GetDof()),
face_dofs(nfaces > 0 ? fes.GetFaceElement(0)->GetDof() : 0),
nfdofs(face_dofs*nfaces),
ndofs(fes.GetNDofs()),
nsdofs(GetSharedVSize(fes))
ndofs(fes.GetNDofs())
{
height = nfdofs;
width = ndofs;
#ifdef MFEM_USE_MPI
auto pfes = dynamic_cast<const ParFiniteElementSpace*>(&fes);
#endif
const Table &face2dof = fes.GetFaceToDofTable();
const Mesh &mesh = *fes.GetMesh();
int face_idx = 0;
scatter_map.SetSize(nfdofs);
gather_map.SetSize(ndofs + nsdofs);
gather_map = -1;
Array<int> dofs;
for (int f = 0; f < mesh.GetNumFacesWithGhost(); ++f)
gather_map.SetSize(nfdofs);
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
Mesh::FaceInformation face = mesh.GetFaceInformation(f);
if (!face.IsOfFaceType(type) || face.IsNonconformingCoarse()) { continue; }
if (f < mesh.GetNumFaces())
if (!face.IsOfFaceType(type)) { continue; }
for (int i = 0; i < face_dofs; ++i)
{
// Local face
face2dof.GetRow(f, dofs);
for (int i = 0; i < face_dofs; ++i)
{
scatter_map[i + face_idx*face_dofs] = dofs[i];
gather_map[dofs[i]] = i + face_idx*face_dofs;
}
}
else
{
// Shared (non-conforming) ghost face
#ifdef MFEM_USE_MPI
MFEM_ASSERT(pfes != nullptr, "");
pfes->GetFaceNbrFaceVDofs(f, dofs);
for (int i = 0; i < face_dofs; ++i)
{
scatter_map[i + face_idx*face_dofs] = ndofs + dofs[i];
gather_map[ndofs + dofs[i]] = i + face_idx*face_dofs;
}
#endif
gather_map[i + face_idx*face_dofs] = face2dof.GetJ()[i + f*face_dofs];
}
++face_idx;
}
@@ -2353,19 +2314,13 @@ L2InterfaceFaceRestriction::L2InterfaceFaceRestriction(
void L2InterfaceFaceRestriction::Mult(const Vector &x, Vector &y) const
{
const int NDOFS = ndofs;
const int nd = face_dofs;
const int nf = nfaces;
const int vd = vdim;
const bool t = byvdim;
const int *map = scatter_map.Read();
Vector face_nbr_data = GetLVectorFaceNbrData(fes, x, type);
MFEM_ASSERT(face_nbr_data.Size() / vd == nsdofs, "");
const int *map = gather_map.Read();
const auto d_x = Reshape(x.Read(), t?vd:ndofs, t?ndofs:vd);
const auto d_x_shared = Reshape(face_nbr_data.Read(),
t?vd:nsdofs, t?nsdofs:vd);
auto d_y = Reshape(y.Write(), nd, vd, nf);
mfem::forall(nd*nf, [=] MFEM_HOST_DEVICE (int i)
@@ -2373,8 +2328,7 @@ void L2InterfaceFaceRestriction::Mult(const Vector &x, Vector &y) const
const int j = map[i];
for (int c = 0; c < vd; ++c)
{
if (j < NDOFS) { d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c); }
else { d_y(i % nd, c, i / nd) = d_x_shared(t?c:(j-NDOFS), t?(j-NDOFS):c); }
d_y(i % nd, c, i / nd) = d_x(t?c:j, t?j:c);
}
});
}
@@ -2389,39 +2343,15 @@ void L2InterfaceFaceRestriction::AddMultTranspose(
const int *map = gather_map.Read();
const auto d_x = Reshape(x.Read(), nd, vd, nf);
auto d_y = Reshape(y.ReadWrite(), t?vd:ndofs, t?ndofs:vd);
auto d_y = Reshape(y.Write(), t?vd:ndofs, t?ndofs:vd);
mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i)
mfem::forall(ndofs, [=] MFEM_HOST_DEVICE (int i) { d_y[i] = 0.0; });
mfem::forall(nd*nf, [=] MFEM_HOST_DEVICE (int i)
{
const int j = map[i];
if (j < 0) { return; }
for (int c = 0; c < vd; ++c)
{
d_y(t?c:i, t?i:c) += a*d_x(j % nd, c, j / nd);
}
});
}
void L2InterfaceFaceRestriction::MultTransposeShared(
const Vector &x, Vector &y) const
{
const int nd = face_dofs;
const int nf = nfaces;
const int vd = vdim;
const bool t = byvdim;
const int *map = gather_map.Read();
const auto d_x = Reshape(x.Read(), nd, vd, nf);
auto d_y = Reshape(y.Write(), t?vd:(ndofs+nsdofs), t?(ndofs+nsdofs):vd);
y = 0.0;
mfem::forall(ndofs + nsdofs, [=] MFEM_HOST_DEVICE (int i)
{
const int j = map[i];
if (j < 0) { return; }
for (int c = 0; c < vd; ++c)
{
d_y(t?c:i, t?i:c) = d_x(j % nd, c, j / nd);
d_y(t?c:j, t?j:c) = d_x(i % nd, c, i / nd);
}
});
}
@@ -2431,11 +2361,6 @@ const Array<int> &L2InterfaceFaceRestriction::GatherMap() const
return gather_map;
}
const Array<int> &L2InterfaceFaceRestriction::ScatterMap() const
{
return scatter_map;
}
Vector GetLVectorFaceNbrData(
const FiniteElementSpace &fes, const Vector &x, FaceType ftype)
{
@@ -2457,7 +2382,6 @@ Vector GetLVectorFaceNbrData(
{
ParGridFunction gf(pfes, const_cast<Vector&>(x));
gf.ExchangeFaceNbrData();
x.SyncMemory(gf);
return std::move(gf.FaceNbrData());
}
}
+14 -26
View File
@@ -812,12 +812,13 @@ protected:
PointMatrix and a local face identifier. */
using Key = std::pair<const DenseMatrix*,int>;
/// The temporary map used to store the different interpolators.
using Map =
std::unordered_map<Key, std::pair<int,const DenseMatrix*>, PairHasher>;
using Map = std::map<Key, std::pair<int,const DenseMatrix*>>;
Map interp_map; // The temporary map that stores the interpolators.
public:
/** @brief Constructor.
InterpolationManager() = delete;
/** @brief main constructor.
@param[in] fes The FiniteElementSpace on which this operates
@param[in] ordering Request a specific element ordering.
@@ -908,7 +909,7 @@ private:
class NCL2FaceRestriction : virtual public L2FaceRestriction
{
protected:
const InterpolationManager &interpolations;
InterpolationManager interpolations;
mutable Vector x_interp;
/** @brief Constructs an NCL2FaceRestriction, this is a specialization of a
@@ -995,7 +996,9 @@ public:
@param[in] keep_nbr_block When set to true the SparseMatrix will
include the rows (in addition to the columns)
corresponding to face-neighbor dofs. The
default behavior is to disregard those rows. */
default behavior is to disregard those rows.
@warning This method is not implemented yet. */
void FillI(SparseMatrix &mat,
const bool keep_nbr_block = false) const override;
@@ -1013,7 +1016,9 @@ public:
@param[in] keep_nbr_block When set to true the SparseMatrix will
include the rows (in addition to the columns)
corresponding to face-neighbor dofs. The
default behavior is to disregard those rows. */
default behavior is to disregard those rows.
@warning This method is not implemented yet. */
void FillJAndData(const Vector &fea_data,
SparseMatrix &mat,
const bool keep_nbr_block = false) const override;
@@ -1031,7 +1036,9 @@ public:
added the face contributions.
The format is: dofs x dofs x ne, where dofs is the
number of dofs per element and ne the number of
elements. */
elements.
@warning This method is not implemented yet. */
void AddFaceMatricesToElementMatrices(const Vector &fea_data,
Vector &ea_data) const override;
@@ -1123,9 +1130,7 @@ protected:
const int face_dofs; ///< Number of dofs on each face
const int nfdofs; ///< Total number of dofs on the faces (E-vector size)
const int ndofs; ///< Number of dofs in the space (L-vector size)
const int nsdofs; ///< Number of shared face neighbor (ghost) dofs
Array<int> gather_map; ///< Gather map
Array<int> scatter_map; ///< Scatter map
public:
/** @brief Constructs an L2InterfaceFaceRestriction.
@@ -1163,24 +1168,7 @@ public:
void AddMultTranspose(const Vector &x, Vector &y,
const real_t a = 1.0) const override;
/// @brief Gather degrees of freedom, from face E-vector to L-vector and
/// shared (ghost) DOFs.
///
/// @param[in] x The face E-Vector degrees of freedom with size
/// (face_dofs, vdim, nf), where nf is the number of
/// interior or boundary faces requested by @a type in the
/// constructor. The face_dofs should be ordered according
/// to the given ElementDofOrdering
/// @param[out] y Vector of length vsize + face neighbor vsize
void MultTransposeShared(const Vector &x, Vector &y) const;
const Array<int> &GatherMap() const override;
/// @brief Return the low-level mapping from L-dofs to E-dofs.
///
/// L-dofs that do not correspond to an E-dof (e.g. that lie on a face of a
/// different type) are given index -1.
const Array<int> &ScatterMap() const;
};
/** @brief Convert a dof face index from Native ordering to lexicographic
+12 -31
View File
@@ -333,12 +333,6 @@ void L2ProjectionGridTransfer::L2Projection::MixedMassEA(
int nel_ho = mesh_ho->GetNE();
int nel_lor = mesh_lor->GetNE();
if (nel_ho == 0)
{
M_LH.SetSize(0);
return;
}
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
int nref_max = 0;
@@ -837,17 +831,11 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::Mult(
void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMult(
const Vector &x, Vector &y) const
{
const int nel_ho = fes_ho.GetMesh()->GetNE();
if (nel_ho == 0)
{
return;
}
const int iho = 0;
const int nref = ho2lor.RowSize(iho);
const int ndof_ho = fes_ho.GetFE(iho)->GetDof();
const int ndof_lor = fes_lor.GetFE(ho2lor.GetRow(iho)[0])->GetDof();
const int nel_ho = fes_ho.GetMesh()->GetNE();
DenseTensor R_dt;
R_dt.NewMemoryAndSize(R.GetMemory(), ndof_lor*nref, ndof_ho, nel_ho, false);
@@ -899,17 +887,11 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::MultTranspose(
void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMultTranspose(
const Vector &x, Vector &y) const
{
const int nel_ho = fes_ho.GetMesh()->GetNE();
if (nel_ho == 0)
{
return;
}
const int iho = 0;
const int nref = ho2lor.RowSize(iho);
const int ndof_ho = fes_ho.GetFE(iho)->GetDof();
const int ndof_lor = fes_lor.GetFE(ho2lor.GetRow(iho)[0])->GetDof();
const int nel_ho = fes_ho.GetMesh()->GetNE();
DenseTensor R_dt;
R_dt.NewMemoryAndSize(R.GetMemory(), ndof_lor*nref, ndof_ho, nel_ho, false);
@@ -919,6 +901,7 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAMultTranspose(
void L2ProjectionGridTransfer::L2ProjectionL2Space::Prolongate(
const Vector &x, Vector &y) const
{
if (fes_ho.GetNE() == 0) { return; }
if (use_ea)
@@ -977,13 +960,14 @@ void L2ProjectionGridTransfer::L2ProjectionL2Space::EAProlongate(
void L2ProjectionGridTransfer::L2ProjectionL2Space::ProlongateTranspose(
const Vector &x, Vector &y) const
{
if (fes_ho.GetNE() == 0) { return; }
if (use_ea)
{
return EAProlongateTranspose(x,y);
}
if (fes_ho.GetNE() == 0) { return; }
MFEM_VERIFY(P.Size() > 0, "Prolongation not supported for these spaces.")
int vdim = fes_ho.GetVDim();
Array<int> vdofs;
@@ -1260,6 +1244,13 @@ void L2ProjectionGridTransfer::L2ProjectionH1Space::EAL2ProjectionH1Space
int ndof_ho = pfes_ho.GetNDofs();
int ndof_lor = pfes_lor.GetNDofs();
// If the local mesh is empty, skip all computations
if (nel_ho == 0)
{
return;
}
const CoarseFineTransformations& cf_tr = mesh_lor->GetRefinementTransforms();
int nref_max = 0;
@@ -1869,11 +1860,6 @@ L2ProjectionGridTransfer::H1SpaceMixedMassOperator::H1SpaceMixedMassOperator(
void L2ProjectionGridTransfer::H1SpaceMixedMassOperator::Mult(const Vector &x,
Vector &y) const
{
if (fes_ho->GetNE() == 0)
{
return;
}
const Operator* elem_restrict_ho = fes_ho->GetElementRestriction(
ElementDofOrdering::NATIVE);
const Operator* elem_restrict_lor = fes_lor->GetElementRestriction(
@@ -1920,11 +1906,6 @@ void L2ProjectionGridTransfer::H1SpaceMixedMassOperator::Mult(const Vector &x,
void L2ProjectionGridTransfer::H1SpaceMixedMassOperator::MultTranspose(
const Vector &x, Vector &y) const
{
if (fes_ho->GetNE() == 0)
{
return;
}
const Operator* elem_restrict_ho = fes_ho->GetElementRestriction(
ElementDofOrdering::NATIVE);
const Operator* elem_restrict_lor = fes_lor->GetElementRestriction(
-2
View File
@@ -18,7 +18,6 @@ list(APPEND SRCS
gecko.cpp
globals.cpp
hash.cpp
hash_util.cpp
isockstream.cpp
mem_manager.cpp
occa.cpp
@@ -47,7 +46,6 @@ list(APPEND HDRS
globals.hpp
zstr.hpp
hash.hpp
hash_util.hpp
isockstream.hpp
kdtree.hpp
mem_alloc.hpp
-2
View File
@@ -44,7 +44,6 @@
#endif
#if !defined(MFEM_USE_CUDA_OR_HIP)
constexpr bool mfem_use_gpu = false;
#define MFEM_DEVICE
#define MFEM_HOST
#define MFEM_LAMBDA
@@ -53,7 +52,6 @@ constexpr bool mfem_use_gpu = false;
#define MFEM_DEVICE_SYNC
// MFEM_STREAM_SYNC is used for UVM and MPI GPU-Aware kernels
#define MFEM_STREAM_SYNC
#define MFEM_LAUNCH_BOUNDS(...)
#endif
#if !((defined(MFEM_USE_CUDA) && defined(__CUDA_ARCH__)) || \
-2
View File
@@ -20,11 +20,9 @@
#if defined(MFEM_USE_CUDA) && defined(__CUDACC__)
#define MFEM_USE_CUDA_OR_HIP
constexpr bool mfem_use_gpu = true;
#define MFEM_DEVICE __device__
#define MFEM_HOST __host__
#define MFEM_LAMBDA __host__
#define MFEM_LAUNCH_BOUNDS __launch_bounds__
// #define MFEM_HOST_DEVICE __host__ __device__ // defined in config/config.hpp
#define MFEM_DEVICE_SYNC MFEM_GPU_CHECK(cudaDeviceSynchronize())
#define MFEM_STREAM_SYNC MFEM_GPU_CHECK(cudaStreamSynchronize(0))
+40 -203
View File
@@ -295,12 +295,11 @@ using hip_threads_z =
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_CUDA) && defined(__CUDACC__)
template <typename DBODY>
template <const int BLOCKS = MFEM_CUDA_BLOCKS, typename DBODY>
void RajaCuWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
RAJA::forall<RAJA::cuda_exec<MFEM_CUDA_BLOCKS,true>>(RAJA::RangeSegment(0,N),
d_body);
RAJA::forall<RAJA::cuda_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
}
template <typename DBODY>
@@ -363,18 +362,18 @@ struct RajaCuWrap;
template <>
struct RajaCuWrap<1>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaCuWrap1D(N, d_body);
RajaCuWrap1D<BLCK>(N, d_body);
}
};
template <>
struct RajaCuWrap<2>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -385,7 +384,7 @@ struct RajaCuWrap<2>
template <>
struct RajaCuWrap<3>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -396,12 +395,11 @@ struct RajaCuWrap<3>
#endif
#if defined(MFEM_USE_RAJA) && defined(RAJA_ENABLE_HIP) && defined(__HIP__)
template <typename DBODY>
template <const int BLOCKS = MFEM_HIP_BLOCKS, typename DBODY>
void RajaHipWrap1D(const int N, DBODY &&d_body)
{
//true denotes asynchronous kernel
RAJA::forall<RAJA::hip_exec<MFEM_HIP_BLOCKS,true>>(RAJA::RangeSegment(0,N),
d_body);
RAJA::forall<RAJA::hip_exec<BLOCKS,true>>(RAJA::RangeSegment(0,N),d_body);
}
template <typename DBODY>
@@ -464,18 +462,18 @@ struct RajaHipWrap;
template <>
struct RajaHipWrap<1>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
RajaHipWrap1D(N, d_body);
RajaHipWrap1D<BLCK>(N, d_body);
}
};
template <>
struct RajaHipWrap<2>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -486,7 +484,7 @@ struct RajaHipWrap<2>
template <>
struct RajaHipWrap<3>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -586,31 +584,12 @@ void CuKernel2D(const int N, BODY body)
body(k);
}
// __launch_bounds__ second argument is omitted to get the default behavior
template <int MAX_THREADS_PER_BLOCK, typename BODY>
__global__
MFEM_LAUNCH_BOUNDS(MAX_THREADS_PER_BLOCK)
static void CuKernel2DLaunchBounds(const int N, BODY body)
{
const int k = blockIdx.x*blockDim.z + threadIdx.z;
if (k >= N) { return; }
body(k);
}
template <typename BODY> __global__ static
void CuKernel3D(const int N, BODY body)
{
for (int k = blockIdx.x; k < N; k += gridDim.x) { body(k); }
}
template <int MAX_THREADS_PER_BLOCK, typename BODY>
__global__
MFEM_LAUNCH_BOUNDS(MAX_THREADS_PER_BLOCK)
static void CuKernel3DLaunchBounds(const int N, BODY body)
{
for (int k = blockIdx.x; k < N; k += gridDim.x) { body(k); }
}
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
void CuWrap1D(const int N, DBODY &&d_body)
{
@@ -625,8 +604,6 @@ void CuWrap2D(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
{
if (N==0) { return; }
// required for optimized GCC/NVCC builds to prevent runtime
// ODR/linkage violations of inlined templated kernel helpers
MFEM_VERIFY(BZ>0, "");
const int GRID = (N+BZ-1)/BZ;
const dim3 BLCK(X,Y,BZ);
@@ -634,19 +611,6 @@ void CuWrap2D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(cudaGetLastError());
}
template <int MAX_THREADS_PER_BLOCK, typename DBODY>
void CuWrap2DLaunchBounds(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
{
if (N==0) { return; }
MFEM_VERIFY(BZ>0, "");
const int GRID = (N+BZ-1)/BZ;
const dim3 BLCK(X,Y,BZ);
static_assert(MAX_THREADS_PER_BLOCK > 0);
CuKernel2DLaunchBounds<MAX_THREADS_PER_BLOCK><<<GRID,BLCK>>>(N, d_body);
MFEM_GPU_CHECK(cudaGetLastError());
}
template <typename DBODY>
void CuWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
@@ -658,35 +622,24 @@ void CuWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(cudaGetLastError());
}
template <int MAX_THREADS_PER_BLOCK, typename DBODY>
void CuWrap3DLaunchBounds(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
if (N==0) { return; }
const int GRID = G == 0 ? N : G;
const dim3 BLCK(X,Y,Z);
static_assert(MAX_THREADS_PER_BLOCK > 0);
CuKernel3DLaunchBounds<MAX_THREADS_PER_BLOCK><<<GRID, BLCK>>>(N, d_body);
MFEM_GPU_CHECK(cudaGetLastError());
}
template <int Dim>
struct CuWrap;
template <int Dim, int MAX_THREADS_PER_BLOCK> struct CuWrap;
template <int MAX_THREADS_PER_BLOCK>
struct CuWrap<1, MAX_THREADS_PER_BLOCK>
template <>
struct CuWrap<1>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
CuWrap1D<MFEM_CUDA_BLOCKS>(N, d_body);
CuWrap1D<BLCK>(N, d_body);
}
};
template <>
struct CuWrap<2, 0>
struct CuWrap<2>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -694,22 +647,10 @@ struct CuWrap<2, 0>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct CuWrap<2, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
static_assert(MAX_THREADS_PER_BLOCK > 0);
CuWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
}
};
template <>
struct CuWrap<3, 0>
struct CuWrap<3>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -717,17 +658,6 @@ struct CuWrap<3, 0>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct CuWrap<3, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
CuWrap3DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z, G);
}
};
#endif // defined(MFEM_USE_CUDA) && defined(__CUDACC__)
@@ -750,31 +680,13 @@ void HipKernel2D(const int N, BODY body)
body(k);
}
template <int MAX_THREADS_PER_BLOCK, typename BODY>
__global__
MFEM_LAUNCH_BOUNDS(MAX_THREADS_PER_BLOCK)
static void HipKernel2DLaunchBounds(const int N, BODY body)
{
const int k = hipBlockIdx_x*hipBlockDim_z + hipThreadIdx_z;
if (k >= N) { return; }
body(k);
}
template <typename BODY> __global__ static
void HipKernel3D(const int N, BODY body)
{
for (int k = hipBlockIdx_x; k < N; k += hipGridDim_x) { body(k); }
}
template <int MAX_THREADS_PER_BLOCK, typename BODY>
__global__
MFEM_LAUNCH_BOUNDS(MAX_THREADS_PER_BLOCK)
static void HipKernel3DLaunchBounds(const int N, BODY body)
{
for (int k = hipBlockIdx_x; k < N; k += hipGridDim_x) { body(k); }
}
template <int BLCK = MFEM_HIP_BLOCKS, typename DBODY>
template <const int BLCK = MFEM_HIP_BLOCKS, typename DBODY>
void HipWrap1D(const int N, DBODY &&d_body)
{
if (N==0) { return; }
@@ -788,27 +700,12 @@ void HipWrap2D(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
{
if (N==0) { return; }
MFEM_VERIFY(BZ>0, "");
const int GRID = (N+BZ-1)/BZ;
const dim3 BLCK(X,Y,BZ);
hipLaunchKernelGGL(HipKernel2D,GRID,BLCK,0,nullptr,N,d_body);
MFEM_GPU_CHECK(hipGetLastError());
}
template <int MAX_THREADS_PER_BLOCK, typename DBODY>
void HipWrap2DLaunchBounds(const int N, DBODY &&d_body,
const int X, const int Y, const int BZ)
{
if (N==0) { return; }
MFEM_VERIFY(BZ>0, "");
const int GRID = (N+BZ-1)/BZ;
const dim3 BLCK(X,Y,BZ);
static_assert(MAX_THREADS_PER_BLOCK > 0);
HipKernel2DLaunchBounds<MAX_THREADS_PER_BLOCK><<<dim3(GRID), dim3(BLCK), 0, 0>>>
(N, d_body);
MFEM_GPU_CHECK(hipGetLastError());
}
template <typename DBODY>
void HipWrap3D(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
@@ -820,36 +717,24 @@ void HipWrap3D(const int N, DBODY &&d_body,
MFEM_GPU_CHECK(hipGetLastError());
}
template <int MAX_THREADS_PER_BLOCK, typename DBODY>
void HipWrap3DLaunchBounds(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
if (N==0) { return; }
const int GRID = G == 0 ? N : G;
const dim3 BLCK(X,Y,Z);
static_assert(MAX_THREADS_PER_BLOCK > 0);
HipKernel3DLaunchBounds<MAX_THREADS_PER_BLOCK><<<dim3(GRID), dim3(BLCK), 0, 0>>>
(N, d_body);
MFEM_GPU_CHECK(hipGetLastError());
}
template <int Dim>
struct HipWrap;
template <int Dim, int MAX_THREADS_PER_BLOCK> struct HipWrap;
template <int MAX_THREADS_PER_BLOCK>
struct HipWrap<1, MAX_THREADS_PER_BLOCK>
template <>
struct HipWrap<1>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
HipWrap1D<MFEM_HIP_BLOCKS>(N, d_body);
HipWrap1D<BLCK>(N, d_body);
}
};
template <>
struct HipWrap<2, 0>
struct HipWrap<2>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -857,21 +742,10 @@ struct HipWrap<2, 0>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct HipWrap<2, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
HipWrap2DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z);
}
};
template <>
struct HipWrap<3, 0>
struct HipWrap<3>
{
template <typename DBODY>
template <const int BLCK = MFEM_CUDA_BLOCKS, typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
@@ -879,24 +753,11 @@ struct HipWrap<3, 0>
}
};
template <int MAX_THREADS_PER_BLOCK>
struct HipWrap<3, MAX_THREADS_PER_BLOCK>
{
template <typename DBODY>
static void run(const int N, DBODY &&d_body,
const int X, const int Y, const int Z, const int G)
{
HipWrap3DLaunchBounds<MAX_THREADS_PER_BLOCK>(N, d_body, X, Y, Z, G);
}
};
#endif // defined(MFEM_USE_HIP) && defined(__HIP__)
///////////////////////////////////////////////////////////////////////////////
/// Forall host & device kernel dispatch
template <int DIM, int MAX_THREADS_PER_BLOCK = 0,
typename d_lambda, typename h_lambda>
/// The forall kernel body wrapper
template <const int DIM, typename d_lambda, typename h_lambda>
inline void ForallWrap(const bool use_dev, const int N,
d_lambda &&d_body, h_lambda &&h_body,
const int X=0, const int Y=0, const int Z=0,
@@ -929,7 +790,7 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::CUDA is allowed, use it
if (Device::Allows(Backend::CUDA))
{
return CuWrap<DIM, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
return CuWrap<DIM>::run(N, d_body, X, Y, Z, G);
}
#endif
@@ -937,7 +798,7 @@ inline void ForallWrap(const bool use_dev, const int N,
// If Backend::HIP is allowed, use it
if (Device::Allows(Backend::HIP))
{
return HipWrap<DIM, MAX_THREADS_PER_BLOCK>::run(N, d_body, X, Y, Z, G);
return HipWrap<DIM>::run(N, d_body, X, Y, Z, G);
}
#endif
@@ -966,9 +827,7 @@ backend_cpu:
for (int k = 0; k < N; k++) { h_body(k); }
}
///////////////////////////////////////////////////////////////////////////////
/// Forall host & device kernel wrappers
template <int DIM, typename lambda>
template <const int DIM, typename lambda>
inline void ForallWrap(const bool use_dev, const int N, lambda &&body,
const int X=0, const int Y=0, const int Z=0,
const int G=0)
@@ -976,16 +835,6 @@ inline void ForallWrap(const bool use_dev, const int N, lambda &&body,
ForallWrap<DIM>(use_dev, N, body, body, X, Y, Z, G);
}
template <int DIM, int MAX_THREADS_PER_BLOCK, typename lambda>
inline void ForallWrap(const bool use_dev, const int N, lambda &&body,
const int X=0, const int Y=0, const int Z=0,
const int G=0)
{
ForallWrap<DIM, MAX_THREADS_PER_BLOCK>(use_dev, N, body, body, X, Y, Z, G);
}
///////////////////////////////////////////////////////////////////////////////
// forall interfaces
template<typename lambda>
inline void forall(int N, lambda &&body) { ForallWrap<1>(true, N, body); }
@@ -994,7 +843,7 @@ inline void forall(int Nx, int Ny, lambda &&body)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
mfem::forall(Nx * Ny, [=] MFEM_HOST_DEVICE(int idx)
forall(Nx * Ny, [=] MFEM_HOST_DEVICE(int idx)
{
int j = idx / Nx;
int i = idx % Nx;
@@ -1030,7 +879,7 @@ inline void forall(int Nx, int Ny, int Nz, lambda &&body)
{
if (Device::Allows(Backend::DEVICE_MASK))
{
mfem::forall(Nx * Ny * Nz, [=] MFEM_HOST_DEVICE(int idx)
forall(Nx * Ny * Nz, [=] MFEM_HOST_DEVICE(int idx)
{
int i = idx % Nx;
int j = idx / Nx;
@@ -1078,12 +927,6 @@ inline void forall_2D(int N, int X, int Y, lambda &&body)
ForallWrap<2>(true, N, body, X, Y, 1);
}
template<int MAX_THREADS_PER_BLOCK, typename lambda>
inline void forall_2D(int N, int X, int Y, lambda &&body)
{
ForallWrap<2, MAX_THREADS_PER_BLOCK>(true, N, body, X, Y, 1);
}
template<typename lambda>
inline void forall_2D_batch(int N, int X, int Y, int BZ, lambda &&body)
{
@@ -1096,12 +939,6 @@ inline void forall_3D(int N, int X, int Y, int Z, lambda &&body)
ForallWrap<3>(true, N, body, X, Y, Z, 0);
}
template<int MAX_THREADS_PER_BLOCK, typename lambda>
inline void forall_3D(int N, int X, int Y, int Z, lambda &&body)
{
ForallWrap<3, MAX_THREADS_PER_BLOCK>(true, N, body, X, Y, Z, 0);
}
template<typename lambda>
inline void forall_3D_grid(int N, int X, int Y, int Z, int G, lambda &&body)
{
+155
View File
@@ -80,4 +80,159 @@ std::string HashFunction::GetHash() const
return hash;
}
constexpr static uint64_t rotl64(uint64_t x, int r)
{
return (x << r) | (x >> (64 - r));
}
void Hasher::init(uint64_t seed)
{
data[0] = seed;
data[1] = seed;
nbytes = 0;
}
void Hasher::add_block(uint64_t k1, uint64_t k2)
{
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
constexpr uint64_t c2 = 0x4cf5ad432745937full;
k1 *= c1;
k1 = rotl64(k1, 31);
k1 *= c2;
data[0] ^= k1;
data[0] = rotl64(data[0], 27);
data[0] += data[1];
data[0] = data[0] * 5 + 0x52dce729ull;
k2 *= c2;
k2 = rotl64(k2, 33);
k2 *= c1;
data[1] ^= k2;
data[1] = rotl64(data[1], 31);
data[1] += data[0];
data[1] = data[1] * 5 + 0x38495ab5ull;
}
static uint64_t fmix64(uint64_t k)
{
// http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
// mix13
k ^= k >> 30;
k *= 0xbf58476d1ce4e5b9ull;
k ^= k >> 27;
k *= 0x94d049bb133111ebull;
k ^= k >> 31;
return k;
}
void Hasher::append(const uint8_t *vs, uint64_t bytes)
{
if (bytes == 0)
{
return;
}
auto rem = nbytes % 16;
nbytes += bytes;
uint8_t *tmp = reinterpret_cast<uint8_t *>(buf_);
while (true)
{
if (bytes + rem >= 16)
{
std::copy(vs, vs + 16 - rem, tmp + rem);
add_block(buf_[0], buf_[1]);
vs += (16 - rem);
bytes -= (16 - rem);
rem = 0;
}
else
{
std::copy(vs, vs + bytes, tmp + rem);
return;
}
}
}
void Hasher::finalize()
{
auto rem = nbytes % 16;
if (rem > 0)
{
nbytes -= rem;
if (rem <= 8)
{
finalize(buf_[0], rem);
}
else
{
finalize(buf_[0], buf_[1], rem);
}
return;
}
data[0] ^= nbytes;
data[1] ^= nbytes;
data[0] += data[1];
data[1] += data[0];
data[0] = fmix64(data[0]);
data[1] = fmix64(data[1]);
data[0] += data[1];
data[1] += data[0];
}
void Hasher::finalize(uint64_t k1, int num)
{
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
constexpr uint64_t c2 = 0x4cf5ad432745937full;
nbytes += num;
k1 *= c1;
k1 = rotl64(k1, 31);
k1 *= c2;
data[0] ^= k1;
data[0] ^= nbytes;
data[1] ^= nbytes;
data[0] += data[1];
data[1] += data[0];
data[0] = fmix64(data[0]);
data[1] = fmix64(data[1]);
data[0] += data[1];
data[1] += data[0];
}
void Hasher::finalize(uint64_t k1, uint64_t k2, int num)
{
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
constexpr uint64_t c2 = 0x4cf5ad432745937full;
nbytes += num;
k2 *= c2;
k2 = rotl64(k2, 33);
k2 *= c1;
data[1] ^= k2;
k1 *= c1;
k1 = rotl64(k1, 31);
k1 *= c2;
data[0] ^= k1;
data[0] ^= nbytes;
data[1] ^= nbytes;
data[0] += data[1];
data[1] += data[0];
data[0] = fmix64(data[0]);
data[1] = fmix64(data[1]);
data[0] += data[1];
data[1] += data[0];
}
} // namespace mfem
+70 -1
View File
@@ -15,8 +15,8 @@
#include "../config/config.hpp"
#include "array.hpp"
#include "globals.hpp"
#include "hash_util.hpp"
#include <array>
#include <cstdint>
#include <type_traits>
#include <utility>
@@ -457,6 +457,75 @@ protected:
int BinSize(int idx) const;
};
///
/// @brief streaming implementation for murmurhash3 128 (x64).
/// Constructs the hash in 3 stages: init, append, finalize.
///
struct Hasher
{
/// where the final hash result is stored after finalize. Use data[1] when
/// only 64 bits are required.
uint64_t data[2] = {0, 0};
private:
uint64_t nbytes = 0;
uint64_t buf_[2] = {0, 0};
public:
/// resets this hasher back to an initial seed
void init(uint64_t seed = 0);
void append(const uint8_t *vs, uint64_t bytes);
void finalize();
private:
// add 16 bytes
void add_block(uint64_t k1, uint64_t k2);
// add [1-8] more bytes, then finalize
void finalize(uint64_t k1, int num);
// add [1-15] more bytes, then finalize
// 0 < num < 16
void finalize(uint64_t k1, uint64_t k2, int num);
};
/// Helper class for hashing std::pair. Usable in place of std::hash<std::pair<T,U>>
struct PairHasher
{
template <class T, class V>
size_t operator()(const std::pair<T, V> &v) const noexcept
{
Hasher hash;
// chosen randomly with a 2^64-sided dice
hash.init(0xfebd1fe69813c14full);
hash.append(reinterpret_cast<const uint8_t *>(&v.first), sizeof(T));
hash.append(reinterpret_cast<const uint8_t *>(&v.second), sizeof(V));
hash.finalize();
return hash.data[1];
}
};
/// Helper class for hashing std::array. Usable in place of std::hash<std::array<T,N>>
struct ArrayHasher
{
template <class T, size_t N>
size_t operator()(const std::array<T, N> &v) const noexcept
{
Hasher hash;
// chosen randomly with a 2^64-sided dice
hash.init(0xfebd1fe69813c14full);
for (size_t i = 0; i < N; ++i)
{
hash.append(reinterpret_cast<const uint8_t *>(&v[i]), sizeof(T));
}
hash.finalize();
return hash.data[1];
}
};
/// Hash function for data sequences.
/** Depends on GnuTLS for SHA-256 hashing. */
class HashFunction
-172
View File
@@ -1,172 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#include "hash_util.hpp"
namespace mfem
{
constexpr static uint64_t rotl64(uint64_t x, int r)
{
return (x << r) | (x >> (64 - r));
}
void Hasher::init(uint64_t seed)
{
data[0] = seed;
data[1] = seed;
nbytes = 0;
}
void Hasher::add_block(uint64_t k1, uint64_t k2)
{
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
constexpr uint64_t c2 = 0x4cf5ad432745937full;
k1 *= c1;
k1 = rotl64(k1, 31);
k1 *= c2;
data[0] ^= k1;
data[0] = rotl64(data[0], 27);
data[0] += data[1];
data[0] = data[0] * 5 + 0x52dce729ull;
k2 *= c2;
k2 = rotl64(k2, 33);
k2 *= c1;
data[1] ^= k2;
data[1] = rotl64(data[1], 31);
data[1] += data[0];
data[1] = data[1] * 5 + 0x38495ab5ull;
}
static uint64_t fmix64(uint64_t k)
{
// http://zimbry.blogspot.com/2011/09/better-bit-mixing-improving-on.html
// mix13
k ^= k >> 30;
k *= 0xbf58476d1ce4e5b9ull;
k ^= k >> 27;
k *= 0x94d049bb133111ebull;
k ^= k >> 31;
return k;
}
void Hasher::append(const std::byte *vs, uint64_t bytes)
{
if (bytes == 0)
{
return;
}
auto rem = nbytes % 16;
nbytes += bytes;
std::byte *tmp = reinterpret_cast<std::byte *>(buf_);
while (true)
{
if (bytes + rem >= 16)
{
std::copy(vs, vs + 16 - rem, tmp + rem);
add_block(buf_[0], buf_[1]);
vs += (16 - rem);
bytes -= (16 - rem);
rem = 0;
}
else
{
std::copy(vs, vs + bytes, tmp + rem);
return;
}
}
}
void Hasher::finalize()
{
auto rem = nbytes % 16;
if (rem > 0)
{
nbytes -= rem;
if (rem <= 8)
{
finalize(buf_[0], rem);
}
else
{
finalize(buf_[0], buf_[1], rem);
}
return;
}
data[0] ^= nbytes;
data[1] ^= nbytes;
data[0] += data[1];
data[1] += data[0];
data[0] = fmix64(data[0]);
data[1] = fmix64(data[1]);
data[0] += data[1];
data[1] += data[0];
}
void Hasher::finalize(uint64_t k1, int num)
{
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
constexpr uint64_t c2 = 0x4cf5ad432745937full;
nbytes += num;
k1 *= c1;
k1 = rotl64(k1, 31);
k1 *= c2;
data[0] ^= k1;
data[0] ^= nbytes;
data[1] ^= nbytes;
data[0] += data[1];
data[1] += data[0];
data[0] = fmix64(data[0]);
data[1] = fmix64(data[1]);
data[0] += data[1];
data[1] += data[0];
}
void Hasher::finalize(uint64_t k1, uint64_t k2, int num)
{
constexpr uint64_t c1 = 0x87c37b91114253d5ull;
constexpr uint64_t c2 = 0x4cf5ad432745937full;
nbytes += num;
k2 *= c2;
k2 = rotl64(k2, 33);
k2 *= c1;
data[1] ^= k2;
k1 *= c1;
k1 = rotl64(k1, 31);
k1 *= c2;
data[0] ^= k1;
data[0] ^= nbytes;
data[1] ^= nbytes;
data[0] += data[1];
data[1] += data[0];
data[0] = fmix64(data[0]);
data[1] = fmix64(data[1]);
data[0] += data[1];
data[1] += data[0];
}
}
-172
View File
@@ -1,172 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_HASH_UTIL_HPP
#define MFEM_HASH_UTIL_HPP
#include <array>
#include <cstddef>
#include <tuple>
#include <functional>
#include <utility>
#include <cstdint>
namespace mfem
{
/// @brief streaming implementation for murmurhash3 128 (x64).
///
/// Constructs the hash in 3 stages: init, append, finalize.
struct Hasher
{
/// @brief Storage for the final hash result after finalize() is called.
///
/// Use data[1] when only 64 bits are required.
uint64_t data[2] = {0, 0};
private:
uint64_t nbytes = 0;
uint64_t buf_[2] = {0, 0};
public:
/// Resets the Hasher back to an initial seed
void init(uint64_t seed = 0);
/// Append data @a vs of size @a bytes.
void append(const std::byte *vs, uint64_t bytes);
void finalize();
private:
/// Add a block of 16 bytes.
void add_block(uint64_t k1, uint64_t k2);
/// @brief Add [1-8] more bytes, then finalize.
///
/// @a num must satisfy 0 < num < 9.
void finalize(uint64_t k1, int num);
/// @brief Add [1-15] more bytes, then finalize.
///
/// @a num must satisfy 0 < num < 16.
void finalize(uint64_t k1, uint64_t k2, int num);
};
template <class T> struct ChainedHasher
{
static void Append(Hasher &hasher, const T &value)
{
if constexpr (std::is_fundamental_v<T> || std::is_pointer_v<T>)
{
hasher.append(reinterpret_cast<const std::byte *>(&value), sizeof(T));
}
else
{
std::hash<T> h;
auto v = h(value);
hasher.append(reinterpret_cast<std::byte *>(&v), sizeof(v));
}
}
};
template <class T, class V> struct ChainedHasher<std::pair<T, V>>
{
static void Append(Hasher &hasher, const std::pair<T, V> &value)
{
ChainedHasher<T>::Append(hasher, value.first);
ChainedHasher<V>::Append(hasher, value.second);
}
};
template <class T, size_t N> struct ChainedHasher<std::array<T, N>>
{
static void Append(Hasher &hasher, const std::array<T, N> &value)
{
for (size_t i = 0; i < N; ++i)
{
ChainedHasher<T>::Append(hasher, value[i]);
}
}
};
template<class... Ts> struct ChainedHasher<std::tuple<Ts...>>
{
private:
template <size_t N>
static void AppendImpl(Hasher &hasher, const std::tuple<Ts...> &value)
{
ChainedHasher<std::decay_t<decltype(std::get<N>(value))>>::Append(
hasher, std::get<N>(value));
if constexpr (N + 1 < sizeof...(Ts))
{
AppendImpl<N + 1>(hasher, value);
}
}
public:
static void Append(Hasher &hasher, const std::tuple<Ts...> &value)
{
if constexpr (sizeof...(Ts))
{
AppendImpl<0>(hasher, value);
}
}
};
/// Helper class for hashing std::pair of hashable types.
struct PairHasher
{
template <class T, class V>
size_t operator()(const std::pair<T, V> &v) const noexcept
{
Hasher hash;
// chosen randomly with a 2^64-sided dice
hash.init(0xfebd1fe69813c14full);
ChainedHasher<std::pair<T, V>>::Append(hash, v);
hash.finalize();
return hash.data[1];
}
};
/// Helper class for hashing std::array of a hashable type.
struct ArrayHasher
{
template <class T, size_t N>
size_t operator()(const std::array<T, N> &v) const noexcept
{
Hasher hash;
// chosen randomly with a 2^64-sided dice
hash.init(0xfebd1fe69813c14full);
ChainedHasher<std::array<T, N>>::Append(hash, v);
hash.finalize();
return hash.data[1];
}
};
/// Helper class for hashing std::tuple of hashable types.
struct TupleHasher
{
template <class T>
size_t operator()(const T &v) const noexcept
{
Hasher hash;
// chosen randomly with a 2^64-sided dice
hash.init(0xfebd1fe69813c14full);
ChainedHasher<T>::Append(hash, v);
hash.finalize();
return hash.data[1];
}
};
} // namespace mfem
#endif
-2
View File
@@ -20,11 +20,9 @@
#if defined(MFEM_USE_HIP) && defined(__HIP__)
#define MFEM_USE_CUDA_OR_HIP
constexpr bool mfem_use_gpu = true;
#define MFEM_DEVICE __device__
#define MFEM_HOST __host__
#define MFEM_LAMBDA __host__ __device__
#define MFEM_LAUNCH_BOUNDS __launch_bounds__
// #define MFEM_HOST_DEVICE __host__ __device__ // defined in config/config.hpp
#define MFEM_DEVICE_SYNC MFEM_GPU_CHECK(hipDeviceSynchronize())
#define MFEM_STREAM_SYNC MFEM_GPU_CHECK(hipStreamSynchronize(0))
-1
View File
@@ -55,7 +55,6 @@ list(APPEND HDRS
dinvariants.hpp
dtensor.hpp
dual.hpp
eigensolver.hpp
filteredsolver.hpp
handle.hpp
invariants.hpp
-29
View File
@@ -1370,35 +1370,6 @@ 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);
+2 -6
View File
@@ -346,12 +346,8 @@ 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|
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 Getl1Diag(Vector &l) const;
/// Compute the row sums of the DenseMatrix
void GetRowSums(Vector &l) const;
/// Creates n x n diagonal matrix with diagonal elements c
-203
View File
@@ -1,203 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
/**
* @file eigensolver.hpp
*
* @brief This file contains a common interface for all eigensolver classes
*/
#ifndef MFEM_EIGENSOLVER
#define MFEM_EIGENSOLVER
#ifdef MFEM_HYPRE
#include "hypre.hpp"
#endif
#ifdef MFEM_SLEPC
#include "slepc.hpp"
#endif
namespace mfem
{
enum class EigenSolverType
{
HYPRE,
SLEPC,
INVALID_TYPE
};
/// Provides base class for MFEM Eigensolvers
class EigenSolverBase
{
public:
EigenSolverBase() {}
/// Destructor
virtual ~EigenSolverBase() = default;
/// Solves the eigenvalue problem
virtual void Solve() = 0;
/// Set the required number of modes
virtual void SetNumModes(int num_Modes)
{
numModes=num_Modes;
}
/// @brief Set the operator to the eigenvalue problem
/// @param A - operator
virtual void SetOperator(Operator& A) = 0;
/// @brief Sets operators for the generalized eigenvalue problem
/// @param A - operator
/// @param M - mass matrix
virtual void SetOperator(Operator& A, Operator& M)
{
MFEM_ABORT("Generalized eigensolver is not supported!");
}
/// Optional method - sets preconditioner for the
/// eigenvalue solver.
virtual void SetPreconditioner(Solver& precond)
{
MFEM_ABORT("Preconditioner is not supported!");
}
/// Returns the converged eigenvalues
virtual void GetEigenvalues(Array<real_t>& eigen_vals) = 0;
/// Returns the vec_index eigenvector.
virtual void GetEigenvector(int vec_index, Vector& vector) = 0;
/// Returns the eigensolver type.
EigenSolverType GetSolverType() { return eigSolverType; }
protected:
int numModes = 0;
EigenSolverType eigSolverType = EigenSolverType::INVALID_TYPE;
};
#ifdef MFEM_HYPRE
class EigenSolverHypreLOBPCG : public EigenSolverBase
{
public:
EigenSolverHypreLOBPCG(MPI_Comm comm)
{
eigenSolver = std::make_unique<HypreLOBPCG>(comm);
eigSolverType = EigenSolverType::HYPRE;
}
~EigenSolverHypreLOBPCG() {}
void Solve() override { eigenSolver->Solve(); }
void SetNumModes(int num_Modes) override
{
eigenSolver->SetNumModes(num_Modes);
numModes = num_Modes;
}
void SetOperator(Operator& A) override { eigenSolver->SetOperator(A); }
void SetOperator(Operator& A, Operator& M) override
{
eigenSolver->SetOperator(A);
eigenSolver->SetMassMatrix(M);
}
void SetPreconditioner(Solver& precond) override { eigenSolver->SetPreconditioner(precond); }
void GetEigenvalues(Array<real_t>& eigen_vals) override { eigenSolver->GetEigenvalues(eigen_vals); }
void GetEigenvector(int vec_index, Vector& vector) override
{
const HypreParVector& eigenvec = eigenSolver->GetEigenvector(vec_index);
vector = eigenvec;
}
void SetTol(real_t tol) { eigenSolver->SetTol(tol); }
void SetRelTol(real_t rel_tol) { eigenSolver->SetRelTol(rel_tol); }
void SetMaxIter(int max_iter) { eigenSolver->SetMaxIter(max_iter); }
void SetPrintLevel(int logging) { eigenSolver->SetPrintLevel(logging); }
void SetRandomSeed(int seed) { eigenSolver->SetRandomSeed(seed); }
void SetPrecondUsageMode(int usage_mode) { eigenSolver->SetPrecondUsageMode(usage_mode); }
private:
std::unique_ptr<HypreLOBPCG> eigenSolver = nullptr;
};
#endif
#ifdef MFEM_SLEPC
class EigenSolverSlepc : public EigenSolverBase
{
public:
EigenSolverSlepc(MPI_Comm comm)
{
eigSolverType = EigenSolverType::SLEPC;
eigenSolver = std::make_unique<SlepcEigenSolver>(comm);
eigenSolver->SetWhichEigenpairs(SlepcEigenSolver::TARGET_REAL);
eigenSolver->SetTarget(0.0);
eigenSolver->SetSpectralTransformation(SlepcEigenSolver::SHIFT_INVERT);
}
~EigenSolverSlepc() {}
void Solve() override { eigenSolver->Solve(); }
void SetNumModes(int num_Modes) override
{
eigenSolver->SetNumModes(num_Modes);
numModes = num_Modes;
}
/// @brief Set the operator to the slepc eigenvalue problem. This method deep copies data to create a PetscParMatrix
/// @param A - operator, must be of type HypreParMatrix.
void SetOperator(Operator& A) override
{
petscMatA = std::make_unique<PetscParMatrix>
(dynamic_cast<HypreParMatrix*>(&A));
eigenSolver->SetOperator(*petscMatA);
}
/// @brief Set the operators to the slepc eigenvalue problem. This method deep copies data to create a PetscParMatrix
/// @param A - operator, must be of type HypreParMatrix.
/// @param M - operator, must be of type HypreParMatrix.
void SetOperator(Operator& A, Operator& M) override
{
petscMatA = std::make_unique<PetscParMatrix>
(dynamic_cast<const HypreParMatrix*>(&A));
petscMatM = std::make_unique<PetscParMatrix>
(dynamic_cast<const HypreParMatrix*>(&M));
eigenSolver->SetOperators(*petscMatA, *petscMatM);
}
void SetPreconditioner([[maybe_unused]] Solver& precond) override {}
void GetEigenvalues(Array<real_t>& eigen_vals) override
{
eigen_vals.SetSize(numModes);
for (int ik = 0; ik < numModes; ik++)
{
eigenSolver->GetEigenvalue(static_cast<unsigned int>(ik), eigen_vals[ik]);
}
}
void GetEigenvector( int vec_index, Vector& vector) override
{ eigenSolver->GetEigenvector(vec_index, vector); }
void SetTol(real_t tol) { eigenSolver->SetTol(tol); }
void SetMaxIter(int max_iter) { eigenSolver->SetMaxIter(max_iter); }
private:
std::unique_ptr<SlepcEigenSolver> eigenSolver = nullptr;
std::unique_ptr<PetscParMatrix> petscMatA = nullptr;
std::unique_ptr<PetscParMatrix> petscMatM = nullptr;
};
#endif
} // namespace mfem
#endif
-179
View File
@@ -3634,25 +3634,12 @@ void HypreSmoother::SetType(HypreSmoother::Type type_, int relax_times_)
relax_times = relax_times_;
}
void HypreSmoother::GetType(HypreSmoother::Type &type_, int &relax_times_) const
{
type_ = static_cast<HypreSmoother::Type>(type);
relax_times_ = relax_times;
}
void HypreSmoother::SetSOROptions(real_t relax_weight_, real_t omega_)
{
relax_weight = relax_weight_;
omega = omega_;
}
void HypreSmoother::GetSOROptions(real_t &relax_weight_, real_t &omega_) const
{
// TODO: are these used for all smoother types?
relax_weight_ = relax_weight;
omega_ = omega;
}
void HypreSmoother::SetPolyOptions(int poly_order_, real_t poly_fraction_,
int eig_est_cg_iter_)
{
@@ -3661,15 +3648,6 @@ void HypreSmoother::SetPolyOptions(int poly_order_, real_t poly_fraction_,
eig_est_cg_iter = eig_est_cg_iter_;
}
void HypreSmoother::GetPolyOptions(int &poly_order_, real_t &poly_fraction_,
int &eig_est_cg_iter_) const
{
// TODO: are these used for all smoother types?
poly_order_ = poly_order;
poly_fraction_ = poly_fraction;
eig_est_cg_iter_ = eig_est_cg_iter;
}
void HypreSmoother::SetTaubinOptions(real_t lambda_, real_t mu_,
int taubin_iter_)
{
@@ -3678,14 +3656,6 @@ void HypreSmoother::SetTaubinOptions(real_t lambda_, real_t mu_,
taubin_iter = taubin_iter_;
}
void HypreSmoother::GetTaubinOptions(real_t &lambda_, real_t &mu_,
int &taubin_iter_) const
{
lambda_ = lambda;
mu_ = mu;
taubin_iter_ = taubin_iter;
}
void HypreSmoother::SetWindowByName(const char* name)
{
real_t a = -1, b, c;
@@ -3708,13 +3678,6 @@ void HypreSmoother::SetWindowParameters(real_t a, real_t b, real_t c)
window_params[2] = c;
}
void HypreSmoother::GetWindowParameters(real_t &a, real_t &b, real_t &c) const
{
a = window_params[0];
b = window_params[1];
c = window_params[2];
}
void HypreSmoother::SetOperator(const Operator &op)
{
A = const_cast<HypreParMatrix *>(dynamic_cast<const HypreParMatrix *>(&op));
@@ -4210,20 +4173,12 @@ HypreSolver::~HypreSolver()
auxX.Delete();
}
void HyprePCG::SetDefaultOptions()
{
// Explicitly set just in case past/future versions of hypre change the
// defaults
SetTol(1e-6);
SetMaxIter(1000);
}
HyprePCG::HyprePCG(MPI_Comm comm) : precond(NULL)
{
iterative_mode = true;
HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
SetDefaultOptions();
}
HyprePCG::HyprePCG(const HypreParMatrix &A_) : HypreSolver(&A_), precond(NULL)
@@ -4235,7 +4190,6 @@ HyprePCG::HyprePCG(const HypreParMatrix &A_) : HypreSolver(&A_), precond(NULL)
HYPRE_ParCSRMatrixGetComm(*A, &comm);
HYPRE_ParCSRPCGCreate(comm, &pcg_solver);
SetDefaultOptions();
}
void HyprePCG::SetOperator(const Operator &op)
@@ -4260,54 +4214,21 @@ void HyprePCG::SetOperator(const Operator &op)
auxX.Delete(); auxX.Reset();
}
void HyprePCG::SetUseTwoNorm(bool val)
{
HYPRE_PCGSetTwoNorm(pcg_solver, val);
}
bool HyprePCG::GetUseTwoNorm() const
{
HYPRE_Int val;
HYPRE_PCGGetTwoNorm(pcg_solver, &val);
return val != 0;
}
void HyprePCG::SetTol(real_t tol)
{
HYPRE_PCGSetTol(pcg_solver, tol);
}
real_t HyprePCG::GetTol() const
{
HYPRE_Real tol;
HYPRE_PCGGetTol(pcg_solver, &tol);
return tol;
}
void HyprePCG::SetAbsTol(real_t atol)
{
HYPRE_PCGSetAbsoluteTol(pcg_solver, atol);
}
real_t HyprePCG::GetAbsTol() const
{
HYPRE_Real atol;
hypre_PCGGetAbsoluteTol(pcg_solver, &atol);
return atol;
}
void HyprePCG::SetMaxIter(int max_iter)
{
HYPRE_PCGSetMaxIter(pcg_solver, max_iter);
}
int HyprePCG::GetMaxIter() const
{
HYPRE_Int max_iter;
HYPRE_PCGGetMaxIter(pcg_solver, &max_iter);
return max_iter;
}
void HyprePCG::SetLogging(int logging)
{
HYPRE_PCGSetLogging(pcg_solver, logging);
@@ -4423,20 +4344,6 @@ HyprePCG::~HyprePCG()
HYPRE_ParCSRPCGDestroy(pcg_solver);
}
#if MFEM_HYPRE_VERSION >= 21500
HypreParVector HyprePCG::GetResiduals() const
{
HYPRE_ParVector r;
HYPRE_ParCSRPCGGetResidual(pcg_solver, &r);
return HypreParVector(r);
}
void HyprePCG::GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p) const
{
auto r = GetResiduals();
ParNormlp(r, p, r.GetComm());
}
#endif
HypreGMRES::HypreGMRES(MPI_Comm comm) : precond(NULL)
{
@@ -4492,69 +4399,26 @@ void HypreGMRES::SetOperator(const Operator &op)
auxX.Delete(); auxX.Reset();
}
#if MFEM_HYPRE_VERSION >= 21500
HypreParVector HypreGMRES::GetResiduals() const
{
HYPRE_ParVector r;
HYPRE_ParCSRGMRESGetResidual(gmres_solver, &r);
return HypreParVector(r);
}
void HypreGMRES::GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p) const
{
auto r = GetResiduals();
ParNormlp(r, p, r.GetComm());
}
#endif
void HypreGMRES::SetTol(real_t tol)
{
HYPRE_GMRESSetTol(gmres_solver, tol);
}
real_t HypreGMRES::GetTol()const
{
HYPRE_Real tol;
HYPRE_GMRESGetTol(gmres_solver, &tol);
return tol;
}
void HypreGMRES::SetAbsTol(real_t tol)
{
HYPRE_GMRESSetAbsoluteTol(gmres_solver, tol);
}
real_t HypreGMRES::GetAbsTol() const
{
HYPRE_Real atol;
HYPRE_GMRESGetAbsoluteTol(gmres_solver, &atol);
return atol;
}
void HypreGMRES::SetMaxIter(int max_iter)
{
HYPRE_GMRESSetMaxIter(gmres_solver, max_iter);
}
int HypreGMRES::GetMaxIter() const
{
HYPRE_Int max_iter;
HYPRE_GMRESGetMaxIter(gmres_solver, &max_iter);
return max_iter;
}
void HypreGMRES::SetKDim(int k_dim)
{
HYPRE_GMRESSetKDim(gmres_solver, k_dim);
}
int HypreGMRES::GetKDim() const
{
HYPRE_Int k_dim;
HYPRE_GMRESGetKDim(gmres_solver, &k_dim);
return k_dim;
}
void HypreGMRES::SetLogging(int logging)
{
HYPRE_GMRESSetLogging(gmres_solver, logging);
@@ -4712,37 +4576,16 @@ void HypreFGMRES::SetTol(real_t tol)
HYPRE_ParCSRFlexGMRESSetTol(fgmres_solver, tol);
}
real_t HypreFGMRES::GetTol() const
{
HYPRE_Real tol;
HYPRE_FlexGMRESGetTol(fgmres_solver, &tol);
return tol;
}
void HypreFGMRES::SetMaxIter(int max_iter)
{
HYPRE_ParCSRFlexGMRESSetMaxIter(fgmres_solver, max_iter);
}
int HypreFGMRES::GetMaxIter() const
{
HYPRE_Int max_iter;
HYPRE_FlexGMRESGetMaxIter(fgmres_solver, &max_iter);
return max_iter;
}
void HypreFGMRES::SetKDim(int k_dim)
{
HYPRE_ParCSRFlexGMRESSetKDim(fgmres_solver, k_dim);
}
int HypreFGMRES::GetKDim() const
{
HYPRE_Int k_dim;
HYPRE_FlexGMRESGetKDim(fgmres_solver, &k_dim);
return k_dim;
}
void HypreFGMRES::SetLogging(int logging)
{
HYPRE_ParCSRFlexGMRESSetLogging(fgmres_solver, logging);
@@ -4839,21 +4682,6 @@ HypreFGMRES::~HypreFGMRES()
HYPRE_ParCSRFlexGMRESDestroy(fgmres_solver);
}
#if MFEM_HYPRE_VERSION >= 21500
HypreParVector HypreFGMRES::GetResiduals() const
{
HYPRE_ParVector r;
HYPRE_ParCSRFlexGMRESGetResidual(fgmres_solver, &r);
return HypreParVector(r);
}
void HypreFGMRES::GetFinalAbsResidualNorm(real_t &final_res_norm,
real_t p) const
{
auto r = GetResiduals();
ParNormlp(r, p, r.GetComm());
}
#endif
void HypreDiagScale::SetOperator(const Operator &op)
{
@@ -5342,13 +5170,6 @@ void HypreBoomerAMG::ResetAMGPrecond()
}
}
int HypreBoomerAMG::GetMaxIter() const
{
HYPRE_Int max_iter;
HYPRE_BoomerAMGGetMaxIter(amg_precond, &max_iter);
return max_iter;
}
void HypreBoomerAMG::SetOperator(const Operator &op)
{
const HypreParMatrix *new_A = dynamic_cast<const HypreParMatrix *>(&op);
+7 -97
View File
@@ -1160,15 +1160,6 @@ public:
return HypreUsingGPU() ? l1Jacobi : l1GS;
}
/// Default solver settings:
/// type = DefaultType()
/// relax_times = 1
/// omega = 1.0
/// poly_order = 2
/// poly_fraction = 0.3
/// lambda = 0.5
/// mu = -0.5
/// taubin_iter = 40
HypreSmoother();
HypreSmoother(const HypreParMatrix &A_, int type = DefaultType(),
@@ -1178,28 +1169,20 @@ public:
/// Set the relaxation type and number of sweeps
void SetType(HypreSmoother::Type type, int relax_times = 1);
using Operator::GetType;
void GetType(HypreSmoother::Type &type, int &relax_times) const;
/// Set SOR-related parameters
void SetSOROptions(real_t relax_weight, real_t omega);
void GetSOROptions(real_t &relax_weight, real_t &omega) const;
/// Set parameters for polynomial smoothing
/** By default, 10 iterations of CG are used to estimate the eigenvalues.
Setting eig_est_cg_iter = 0 uses hypre's hypre_ParCSRMaxEigEstimate() instead. */
void SetPolyOptions(int poly_order, real_t poly_fraction,
int eig_est_cg_iter = 10);
void GetPolyOptions(int &poly_order, real_t &poly_fraction,
int &eig_est_cg_iter) const;
/// Set parameters for Taubin's lambda-mu method
void SetTaubinOptions(real_t lambda, real_t mu, int iter);
void GetTaubinOptions(real_t &lambda, real_t &mu, int &iter) const;
/// Convenience function for setting canonical windowing parameters
void SetWindowByName(const char* window_name);
/// Set parameters for windowing function for FIR smoother.
void SetWindowParameters(real_t a, real_t b, real_t c);
void GetWindowParameters(real_t &a, real_t &b, real_t &c) const;
/// Compute window and Chebyshev coefficients for given polynomial order.
void SetFIRCoefficients(real_t max_eig);
@@ -1207,15 +1190,12 @@ public:
/** By default, the l1-norms take their sign from the corresponding diagonal
entries in the associated matrix. */
void SetPositiveDiagonal(bool pos = true) { pos_l1_norms = pos; }
bool IsPositiveDiagonal() const { return pos_l1_norms; };
/** Explicitly indicate whether the linear system matrix A is symmetric. If A
is symmetric, the smoother will also be symmetric. In this case, calling
MultTranspose will be redirected to Mult. (This is also done if the
smoother is diagonal.) By default, A is assumed to be nonsymmetric. */
void SetOperatorSymmetry(bool is_sym) { A_is_symmetric = is_sym; }
/// @return true if the smoother assumes A is symmetric, false otherwise
bool IsOperatorSymmetric() const { return A_is_symmetric; }
/** Set/update the associated operator. Must be called after setting the
HypreSmoother type and options. */
@@ -1347,7 +1327,6 @@ public:
#endif
/// PCG solver in hypre
/// Defaults to (relative) tol=1e-6, atol=0, max_iter=1000
class HyprePCG : public HypreSolver
{
private:
@@ -1355,9 +1334,6 @@ private:
HypreSolver * precond;
/// Default PCG options
void SetDefaultOptions();
public:
HyprePCG(MPI_Comm comm);
@@ -1366,11 +1342,8 @@ public:
void SetOperator(const Operator &op) override;
void SetTol(real_t tol);
real_t GetTol() const;
void SetAbsTol(real_t atol);
real_t GetAbsTol() const;
void SetMaxIter(int max_iter);
int GetMaxIter() const;
void SetLogging(int logging);
void SetPrintLevel(int print_lvl);
@@ -1395,32 +1368,12 @@ public:
num_iterations = internal::to_int(num_it);
}
/// Gets the relative residual norm
void GetFinalResidualNorm(real_t &final_res_norm) const
{
HYPRE_ParCSRPCGGetFinalRelativeResidualNorm(pcg_solver,
&final_res_norm);
}
/// @param[in] use
/// Convergence criterion:
/// - when true: (r, r) < max(r_tol^2 (b, b), a_tol^2)
/// - when false: (r, A r) < max(r_tol^2 (b, A b), a_tol^2)
/// @sa HYPRE_PCGSetTwoNorm
void SetUseTwoNorm(bool use);
/// @sa HYPRE_PCGGetTwoNorm
bool GetUseTwoNorm() const;
#if MFEM_HYPRE_VERSION >= 21500
/// Gets the internal Hypre solver residual vector.
/// @sa HYPRE_ParCSRPCGGetResidual
HypreParVector GetResiduals() const;
/// Computes the absolute residual p-norm.
void GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p = 2) const;
#endif
/// The typecast to HYPRE_Solver returns the internal pcg_solver
operator HYPRE_Solver() const override { return pcg_solver; }
@@ -1438,8 +1391,7 @@ public:
virtual ~HyprePCG();
};
/// GMRES solver in hypre.
/// Defaults to k=50, (relative) tol=1e-6, atol=0, max_iter=100.
/// GMRES solver in hypre
class HypreGMRES : public HypreSolver
{
private:
@@ -1458,13 +1410,9 @@ public:
void SetOperator(const Operator &op) override;
void SetTol(real_t tol);
real_t GetTol() const;
void SetAbsTol(real_t tol);
real_t GetAbsTol() const;
void SetMaxIter(int max_iter);
int GetMaxIter() const;
void SetKDim(int dim);
int GetKDim() const;
void SetLogging(int logging);
void SetPrintLevel(int print_lvl);
@@ -1484,22 +1432,12 @@ public:
num_iterations = internal::to_int(num_it);
}
/// Gets the relative residual norm
void GetFinalResidualNorm(real_t &final_res_norm) const
{
HYPRE_ParCSRGMRESGetFinalRelativeResidualNorm(gmres_solver,
&final_res_norm);
}
#if MFEM_HYPRE_VERSION >= 21500
/// Gets the internal Hypre solver residual vector.
/// @sa HYPRE_ParCSRGMRESGetResidual
HypreParVector GetResiduals() const;
/// Computes the absolute residual p-norm.
void GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p = 2) const;
#endif
/// The typecast to HYPRE_Solver returns the internal gmres_solver
operator HYPRE_Solver() const override { return gmres_solver; }
@@ -1517,8 +1455,7 @@ public:
virtual ~HypreGMRES();
};
/// Flexible GMRES solver in hypre.
/// Defaults to k=50, (relative) tol=1e-6, max_iter=100.
/// Flexible GMRES solver in hypre
class HypreFGMRES : public HypreSolver
{
private:
@@ -1537,11 +1474,8 @@ public:
void SetOperator(const Operator &op) override;
void SetTol(real_t tol);
real_t GetTol() const;
void SetMaxIter(int max_iter);
int GetMaxIter() const;
void SetKDim(int dim);
int GetKDim() const;
void SetLogging(int logging);
void SetPrintLevel(int print_lvl);
@@ -1561,22 +1495,12 @@ public:
num_iterations = internal::to_int(num_it);
}
/// Gets the relative residual norm
void GetFinalResidualNorm(real_t &final_res_norm) const
{
HYPRE_ParCSRFlexGMRESGetFinalRelativeResidualNorm(fgmres_solver,
&final_res_norm);
}
#if MFEM_HYPRE_VERSION >= 21500
/// Gets the internal Hypre solver residual vector.
/// @sa HYPRE_ParCSRFlexGMRESGetResidual
HypreParVector GetResiduals() const;
/// Computes the absolute residual p-norm.
void GetFinalAbsResidualNorm(real_t &final_res_norm, real_t p = 2) const;
#endif
/// The typecast to HYPRE_Solver returns the internal fgmres_solver
operator HYPRE_Solver() const override { return fgmres_solver; }
@@ -1632,8 +1556,7 @@ public:
virtual ~HypreDiagScale() { }
};
/// The ParaSails preconditioner in hypre.
/// See SetDefaultOptions() for default solver options.
/// The ParaSails preconditioner in hypre
class HypreParaSails : public HypreSolver
{
private:
@@ -1762,14 +1685,10 @@ public:
/**
@brief Wrapper for Hypre's native parallel ILU preconditioner.
Default parameters: ILU(k) factorization type, tol=0.0 (for use as a
preconditioner), fill level = 1 (for ILU(k)), reverse Cuthill-McKee (RCM)
re-ordering.
If you need to change this, or any other option, you can use the HYPRE_Solver
method to cast the object for use with Hypre's native functions. For example, if
want to use natural ordering rather than RCM reordering, you can use the
following approach:
The default ILU factorization type is ILU(k). If you need to change this, or
any other option, you can use the HYPRE_Solver method to cast the object for use
with Hypre's native functions. For example, if want to use natural ordering
rather than RCM reordering, you can use the following approach:
@code
mfem::HypreILU ilu();
@@ -1910,7 +1829,6 @@ public:
void SetMaxIter(int max_iter)
{ HYPRE_BoomerAMGSetMaxIter(amg_precond, max_iter); }
int GetMaxIter() const;
/// Expert option - consult hypre documentation/team
void SetMaxLevels(int max_levels)
@@ -1935,8 +1853,6 @@ public:
/// Expert option - consult hypre documentation/team
void SetRelaxType(int relax_type)
{ HYPRE_BoomerAMGSetRelaxType(amg_precond, relax_type); }
// not implemented in hypre
// int GetRelaxType() const;
/// Expert option - consult hypre documentation/team
void SetCycleType(int cycle_type)
@@ -2237,14 +2153,8 @@ public:
~HypreLOBPCG();
void SetTol(real_t tol);
// not implemented in HYPRE
// real_t GetTol() const;
void SetRelTol(real_t rel_tol);
// not implemented in HYPRE
// real_t GetRelTol() const;
void SetMaxIter(int max_iter);
// not implemented in HYPRE
// int GetMaxIter() const;
void SetPrintLevel(int logging);
void SetNumModes(int num_eigs) { nev = num_eigs; }
void SetPrecondUsageMode(int pcg_mode);
+3
View File
@@ -193,6 +193,9 @@ public:
/// Returns how many State vectors the ODE requires
virtual int GetStateSize() { return 0; };
/// Returns the associated TimeDependentOperator
TimeDependentOperator* GetTimeDependentOperator() { return f; }
///@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().
-13
View File
@@ -3639,20 +3639,12 @@ void PetscBDDCSolver::BDDCSolverConstructor(const PetscBDDCSolverParams &opts)
// make sure ess/nat_dof have been collectively set
PetscBool lpr = PETSC_FALSE,pr;
if (opts.ess_dof) { lpr = PETSC_TRUE; }
#if PETSC_VERSION_LT(3,24,0)
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPIU_BOOL,MPI_LOR,comm);
#else
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPI_C_BOOL,MPI_LOR,comm);
#endif
CCHKERRQ(comm,mpiierr);
MFEM_VERIFY(lpr == pr,"ess_dof should be collectively set");
lpr = PETSC_FALSE;
if (opts.nat_dof) { lpr = PETSC_TRUE; }
#if PETSC_VERSION_LT(3,24,0)
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPIU_BOOL,MPI_LOR,comm);
#else
mpiierr = MPI_Allreduce(&lpr,&pr,1,MPI_C_BOOL,MPI_LOR,comm);
#endif
CCHKERRQ(comm,mpiierr);
MFEM_VERIFY(lpr == pr,"nat_dof should be collectively set");
// make sure fields have been collectively set
@@ -4066,13 +4058,8 @@ void PetscNonlinearSolver::SetOperator(const Operator &op)
ls = (PetscBool)(height == op.Height() && width == op.Width() &&
(void*)&op == fctx &&
(void*)&op == jctx);
#if PETSC_VERSION_LT(3,24,0)
mpiierr = MPI_Allreduce(&ls,&gs,1,MPIU_BOOL,MPI_LAND,
PetscObjectComm((PetscObject)snes));
#else
mpiierr = MPI_Allreduce(&ls,&gs,1,MPI_C_BOOL,MPI_LAND,
PetscObjectComm((PetscObject)snes));
#endif
CCHKERRQ(PetscObjectComm((PetscObject)snes),mpiierr);
if (!gs)
{
+210
View File
@@ -851,6 +851,216 @@ void SLI(const Operator &A, Solver &B, const Vector &b, Vector &x,
sli.Mult(b, x);
}
real_t FPIRelaxation::Dot(const Vector &x, const Vector &y) const
{
if (dot_oper) { return dot_oper->Eval(x,y); } // Use custom inner product (if provided)
#ifndef MFEM_USE_MPI
return (x * y);
#else
return InnerProduct(comm, x, y);
#endif
}
real_t FPIRelaxation::SquaredDistance(const Vector &x, const Vector &y) const
{
#ifndef MFEM_USE_MPI
return x.DistanceSquaredTo(y);
#else
return DistanceSquared(comm, x, y);
#endif
}
void AitkenRelaxation::Init()
{
FPIRelaxation::Init();
MemoryType mt = GetMemoryType(oper->GetMemoryClass());
rold.SetSize(oper->Width(), mt);
rold.UseDevice(true);
rold = 0.0;
rold_nsq = 0.0;
}
real_t AitkenRelaxation::Eval(const Vector &state, const Vector &residual,
real_t res_norm, real_t rfactor)
{
real_t rdot = Dot(rold, residual);
real_t rnsq = res_norm * res_norm;
real_t denom = rnsq - 2.0*rdot + rold_nsq; // ||rold - residual||^2
real_t ratio = (rdot - rold_nsq) / denom;
rold_nsq = rnsq;
rold = residual;
if (ratio == 0.0) { return rfactor; } // Avoid factor = 0.0 at first call
return Clamp(-rfactor * ratio);
}
void SteepestDescentRelaxation::Init()
{
FPIRelaxation::Init();
MemoryType mt = GetMemoryType(oper->GetMemoryClass());
z.SetSize(oper->Width(), mt);
z.UseDevice(true);
}
real_t SteepestDescentRelaxation::Eval(const Vector &state,
const Vector &residual,
real_t res_norm, real_t rfactor)
{
MFEM_VERIFY(oper,"Operator not set; set using SetOperator(Operator&)")
Operator *J = &oper->GetGradient(state);
real_t num = res_norm * res_norm;
J->Mult(residual, z); // rold = F'(x) * rnew;
real_t denom = Dot(z, residual);
return Clamp(num/denom);
}
/// Fixed point iteration solver: x <- f(x)
void FPISolver::UpdateVectors()
{
MemoryType mt = GetMemoryType(oper->GetMemoryClass());
r.SetSize(width, mt);
r.UseDevice(true);
z.SetSize(width, mt);
z.UseDevice(true);
}
void FPISolver::SetOperator(const Operator &op)
{
IterativeSolver::SetOperator(op);
UpdateVectors();
if (!relax_method)
{
relax_method = new FPIRelaxation(); // Default relaxation strategy
relax_owned = true;
}
relax_method->SetOperator(*oper);
}
void FPISolver::SetRelaxation(real_t rfactor, FPIRelaxation *relaxation,
bool own)
{
relax_factor = rfactor;
if (relaxation)
{
if (relax_method && relax_owned) { delete relax_method; }
relax_method = relaxation;
relax_owned = own;
}
#ifdef MFEM_USE_MPI
if (relax_method) { relax_method->SetComm(this->GetComm()); }
#endif
}
/// Iterative solution of the (non)linear system using Fixed Point Iteration
void FPISolver::Mult(const Vector &b, Vector &x) const
{
int i;
real_t factor = relax_factor;
real_t r0, nom, nom0, nomold = 1, cf, fac_old = factor;
if (iterative_mode)
{
oper->Mult(x, z); // z = F(x)
subtract(z, x, r); // r = z - x
}
else
{
x = 0.0;
oper->Mult(x, r); // r = F(x)
}
nom0 = nom = sqrt(Dot(r, r));
initial_norm = nom0;
if (print_options.iterations | print_options.first_and_last)
{
mfem::out << " Iteration : " << setw(3) << right << 0 << " ||Br|| = "
<< nom << (print_options.first_and_last ? " ..." : "") << '\n';
}
r0 = std::max(nom*rel_tol, abs_tol);
if (nom <= r0)
{
converged = true;
final_iter = 0;
final_norm = nom;
return;
}
// start iteration
converged = false;
final_iter = max_iter;
relax_method->Init();
for (i = 1; true; )
{
factor = relax_method->Eval(x,r,nom,fac_old);
x.Add(factor, r); // x = x + factor * r
oper->Mult(x, z); // z = F(x)
subtract(z, x, r); // r = z - x
nom = sqrt(Dot(r, r));
cf = nom/nomold;
nomold = nom;
fac_old = factor;
bool done = false;
if (nom < r0)
{
converged = true;
final_iter = i;
done = true;
}
if (++i > max_iter)
{
done = true;
}
if (print_options.iterations || (done && print_options.first_and_last))
{
mfem::out << " Iteration : " << setw(3) << right << (i-1)
<< " ||r|| = " << setw(11) << left << nom
<< "\trlx. fac.: " << fac_old << '\n';
}
if (done) { break; }
}
if (print_options.summary || (print_options.warnings && !converged))
{
const auto rf = pow (nom/nom0, 1.0/final_iter);
mfem::out << "FPI: Number of iterations: " << final_iter << '\n'
<< "Conv. rate: " << cf << '\n'
<< "Average reduction factor: "<< rf << '\n';
}
if (print_options.warnings && !converged)
{
mfem::out << "FPI: No convergence!" << '\n';
}
final_norm = nom;
}
void FPI(const Operator &A, const Vector &b, Vector &x,
int print_iter, int max_num_iter,
real_t RTOLERANCE, real_t ATOLERANCE,
real_t relax_factor, FPIRelaxation *relax_method)
{
MFEM_PERF_FUNCTION;
FPISolver fpi;
fpi.SetPrintLevel(print_iter);
fpi.SetMaxIter(max_num_iter);
fpi.SetRelTol(sqrt(RTOLERANCE));
fpi.SetAbsTol(sqrt(ATOLERANCE));
fpi.SetOperator(A);
fpi.SetRelaxation(relax_factor,relax_method,false);
fpi.Mult(b, x);
}
void CGSolver::UpdateVectors()
{
+178
View File
@@ -622,6 +622,184 @@ void SLI(const Operator &A, Solver &B, const Vector &b, Vector &x,
real_t RTOLERANCE = 1e-12, real_t ATOLERANCE = 1e-24);
/**
@brief A class to handle fixed point iteration relaxation methods.
This class provides a base for implementing various relaxation strategies
for fixed point iteration solvers.
*/
class FPIRelaxation
{
#ifdef MFEM_USE_MPI
private:
MPI_Comm comm = MPI_COMM_NULL;
#endif
protected:
const Operator *oper = nullptr;
real_t lbnd = std::numeric_limits<real_t>::lowest();
real_t ubnd = std::numeric_limits<real_t>::max();
real_t abs_lbnd = 0.0;
InnerProductOperator *dot_oper = nullptr;
public:
FPIRelaxation() = default;
#ifdef MFEM_USE_MPI
FPIRelaxation(MPI_Comm comm_) {comm = comm_;}
void SetComm(MPI_Comm comm_) {comm = comm_;}
#endif
/// @brief Set the operator for the relaxation method.
virtual void SetOperator(const Operator &op) {oper = &op;}
/// @brief Initialize the relaxation method.
virtual void Init() {}
/// @brief Set the lower bound for the relaxation factor.
virtual void SetLowerBound(real_t lb) { lbnd = lb; }
/// @brief Set the upper bound for the relaxation factor.
virtual void SetUpperBound(real_t ub) { ubnd = ub; }
/// @brief Set both lower and upper bounds for the relaxation factor.
virtual void SetBounds(real_t lb, real_t ub)
{ lbnd = lb; ubnd = ub;}
/// @brief Set the absolute bound for the relaxation factor.
virtual void SetAbsoluteLowerBound(real_t abs_lb)
{ abs_lbnd = std::abs(abs_lb); }
/// @brief Set a user-defined inner product operator (not owned)
void SetInnerProduct(InnerProductOperator *ipo) { dot_oper = ipo; }
/// @brief Clamp the relaxation factor to the specified range.
virtual real_t Clamp(real_t factor) const
{
real_t afac = (abs_lbnd == 0.0) ? factor :
std::copysign(std::max(std::abs(factor), abs_lbnd), factor);
return std::max(std::min(afac, ubnd), lbnd);
}
/**
@brief Compute the relaxation factor for Fixed Point Iteration.
@param state Current state vector
@param residual Current residual vector
@param res_norm Norm of the current residual vector
@param rfactor Current relaxation factor
@return real_t The computed relaxation factor
*/
virtual real_t Eval(const Vector &state, const Vector &residual,
real_t res_norm, real_t rfactor)
{
return rfactor; // Default implementation returns the fixed factor
};
real_t Dot(const Vector &x, const Vector &y) const;
real_t SquaredDistance(const Vector &x, const Vector &y) const;
virtual ~FPIRelaxation() {}
};
/**
@brief A class to implement Aitken relaxation for Fixed Point Iteration
to accelerate the convergence of fixed point iterations.
*/
class AitkenRelaxation : public FPIRelaxation
{
protected:
Vector rold; // Old residual vector
real_t rold_nsq = 0.0;
public:
AitkenRelaxation() = default;
#ifdef MFEM_USE_MPI
AitkenRelaxation(MPI_Comm comm_) : FPIRelaxation(comm_) {}
#endif
void Init() override;
/**
@brief Compute the Aitken relaxation factor for Fixed Point Iteration.
@param state Current state vector
@param residual Current residual vector
@param res_norm Norm of the current residual vector
@param rfactor Current relaxation factor
@return The computed relaxation factor
*/
real_t Eval(const Vector &state, const Vector &residual, real_t res_norm,
real_t rfactor) override;
};
/**
@brief A class to implement steepest descent relaxation for Fixed Point Iteration
to accelerate the convergence of fixed point iterations.
*/
class SteepestDescentRelaxation : public FPIRelaxation
{
protected:
Vector z;
public:
SteepestDescentRelaxation() = default;
#ifdef MFEM_USE_MPI
SteepestDescentRelaxation(MPI_Comm comm_) : FPIRelaxation(comm_) {}
#endif
void Init() override;
/**
@brief Compute the steepest descent relaxation factor for Fixed Point Iteration.
@param state Current state vector
@param residual Current residual vector
@param res_norm Norm of the current residual vector
@param rfactor Current relaxation factor
@return The computed relaxation factor
*/
real_t Eval(const Vector &state, const Vector &residual, real_t res_norm,
real_t rfactor) override;
};
/// Fixed point iteration solver: x <- f(x)
class FPISolver : public IterativeSolver
{
protected:
FPIRelaxation *relax_method = nullptr; ///< Relaxation strategy for FPI
bool relax_owned = false;
mutable real_t relax_factor = 1.0;
mutable Vector r, z;
void UpdateVectors();
public:
FPISolver() { }
#ifdef MFEM_USE_MPI
FPISolver(MPI_Comm comm_) : IterativeSolver(comm_) { }
#endif
virtual void SetOperator(const Operator &op) override;
void SetRelaxation(real_t rfactor, FPIRelaxation *relaxation = nullptr,
bool own = false);
/// Iterative solution of the (non)linear system using Fixed Point Iteration
/// b is not used
void Mult(const Vector &b, Vector &x) const override;
};
/// Fixed point iteration. (tolerances are squared)
void FPI(const Operator &A, const Vector &b, Vector &x,
int print_iter = 0, int max_num_iter = 1000,
real_t RTOLERANCE = 1e-12, real_t ATOLERANCE = 1e-24,
real_t relax_factor = 1.0, FPIRelaxation *relax_method = nullptr);
/// Conjugate gradient method
class CGSolver : public IterativeSolver
{
-5
View File
@@ -1066,11 +1066,6 @@ void SparseMatrix::BooleanMultTranspose(const Array<int> &x,
y.SetSize(Width());
y = 0;
HostReadI();
HostReadJ();
x.HostRead();
y.HostReadWrite();
for (int i = 0; i < Height(); i++)
{
if (x[i])
+1 -12
View File
@@ -363,19 +363,14 @@ void SuperLUSolver::Init(MPI_Comm comm)
// Set default options:
// options.Fact = DOFACT;
// options.Equil = YES;
// options.ParSymbFact = NO;
// options.ColPerm = METIS_AT_PLUS_A;
// options.RowPerm = LargeDiag_MC64;
// options.ReplaceTinyPivot = NO;
// options.IterRefine = SLU_DOUBLE;
// options.Trans = NOTRANS;
// options.IterRefine = SLU_DOUBLE;
// options.SolveInitialized = NO;
// options.RefineInitialized = NO;
// options.PrintStat = YES;
// options.lookahead_etree = NO;
// options.num_lookaheads = 10;
// options.superlu_acc_offload = 1;
// options.SymPattern = NO;
superlu_dist_options_t *options = (superlu_dist_options_t *)optionsPtr_;
set_default_options_dist(options);
#if SUPERLU_DIST_MAJOR_VERSION > 7 || \
@@ -477,12 +472,6 @@ void SuperLUSolver::SetFact(superlu::Fact fact)
options->Fact = opt;
}
void SuperLUSolver::SetDeviceOffload(bool offload)
{
superlu_dist_options_t *options = (superlu_dist_options_t *)optionsPtr_;
options->superlu_acc_offload = offload;
}
void SuperLUSolver::SetOperator(const Operator &op)
{
// Verify that we have a compatible operator
+1 -6
View File
@@ -250,8 +250,7 @@ public:
work (default false) */
void SetSymmetricPattern(bool sym);
/** @brief Specify whether to perform parallel symbolic factorization
(default false)
/** @brief Specify whether to perform parallel symbolic factorization.
@note If true SuperLU will use superlu::PARMETIS for the Column
Permutation regardless of the setting */
void SetParSymbFact(bool par);
@@ -264,10 +263,6 @@ public:
superlu::FACTORED*/
void SetFact(superlu::Fact fact);
/** @brief Specify whether to offload numerical factorization onto the device
(default true if SuperLU_DIST has been compiled with GPU support) */
void SetDeviceOffload(bool offload);
// Processor grid for SuperLU_DIST.
const int nprow_, npcol_, npdep_;
+23
View File
@@ -777,8 +777,31 @@ inline real_t InnerProduct(MPI_Comm comm, const Vector &x, const Vector &y)
MPI_Allreduce(&loc_prod, &glb_prod, 1, MFEM_MPI_REAL_T, MPI_SUM, comm);
return glb_prod;
}
/// Returns the square of the Euclidean distance between two vectors in parallel
/** In parallel this computes the square of the Euclidean distance of the local
vectors, producing identical results on each MPI rank.*/
inline real_t DistanceSquared(MPI_Comm comm, const real_t *x, const real_t *y,
const int n)
{
real_t d = DistanceSquared(x, y, n);
real_t glb_d;
MPI_Allreduce(&d, &glb_d, 1, MFEM_MPI_REAL_T, MPI_SUM, comm);
return glb_d;
}
inline real_t DistanceSquared(MPI_Comm comm, const Vector &x, const Vector &y)
{
MFEM_ASSERT(x.Size() == y.Size(), "Incompatible vector sizes.");
real_t d = x.DistanceSquaredTo(y);
real_t glb_d;
MPI_Allreduce(&d, &glb_d, 1, MFEM_MPI_REAL_T, MPI_SUM, comm);
return glb_d;
}
#endif
} // namespace mfem
#endif
+1
View File
@@ -794,6 +794,7 @@ status info:
$(info MFEM_MPI_NP = $(MFEM_MPI_NP))
@true
ASTYLE_BIN = astyle
ASTYLE = $(ASTYLE_BIN) --options=$(SRC)config/mfem.astylerc
ASTYLE_VER = "Artistic Style Version 3.1"
FORMAT_FILES = $(foreach dir,$(DIRS) $(EM_DIRS) config,$(dir)/*.?pp)
+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_VERIFY(attrs.Min() >= 1, "Found attribute less than one")
MFEM_ASSERT(attrs.Max() <= max_attr, "Found attribute greater than max_attr")
MFEM_ASSERT(attrs.Max() <= max_attr, "Invalid attribute number present.");
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;
+4 -171
View File
@@ -4792,12 +4792,11 @@ Mesh::Mesh(const NURBSExtension& ext)
if (NURBSext->HavePatches())
{
NURBSFECollection *fec = new NURBSFECollection(NURBSext->GetOrder());
const int vdim = NURBSext->GetPatchSpaceDimension();
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
Ordering::byVDIM);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec);
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
NURBSext->SetCoordsFromPatches(*Nodes);
own_nodes = 1;
spaceDim = Nodes->VectorDim();
for (int i = 0; i < spaceDim; i++)
@@ -6411,7 +6410,7 @@ void Mesh::UpdateNURBS()
NURBSext->SetKnotsFromPatches();
Dim = NURBSext->Dimension();
spaceDim = Nodes->FESpace()->GetVDim();
spaceDim = Dim;
if (NumOfElements != NURBSext->GetNE())
{
@@ -6436,8 +6435,7 @@ void Mesh::UpdateNURBS()
Nodes->FESpace()->Update();
Nodes->Update();
NodesUpdated();
const int vdim = Nodes->FESpace()->GetVDim();
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
NURBSext->SetCoordsFromPatches(*Nodes);
if (NumOfVertices != NURBSext->GetNV())
{
@@ -7836,17 +7834,6 @@ 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);
@@ -12402,38 +12389,6 @@ 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++)
{
@@ -15746,128 +15701,6 @@ 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)
+10 -37
View File
@@ -2078,13 +2078,12 @@ public:
contrary to the ones obtained through Mesh::GetFacesElements and can
directly be used, e.g., Elem1 and Elem2 indices.
Likewise the orientations for Elem1 and Elem2 already take into account
special cases and can be used as is. */
special cases and can be used as is.
*/
struct FaceInformation
{
/// The face topology (boundary, conforming, or nonconforming).
FaceTopology topology;
/// Information about the adjacent elements.
struct
{
ElementLocation location;
@@ -2094,13 +2093,8 @@ public:
int orientation;
} element[2];
/// Detailed face information (see FaceInfoTag).
FaceInfoTag tag;
/// If the face is nonconforming, the index of the NC face. -1 otherwise.
int ncface;
/// The point matrix for nonconforming faces.
const DenseMatrix* point_matrix;
/** @brief Return true if the face is a local interior face which is NOT
@@ -2119,20 +2113,21 @@ public:
/** @brief return true if the face is an interior face to the computation
domain, either a local or shared interior face (not a boundary face)
which is NOT a master nonconforming face. */
which is NOT a master nonconforming face.
*/
bool IsInterior() const
{
return topology == FaceTopology::Conforming ||
topology == FaceTopology::Nonconforming;
}
/// Return true if the face is a boundary face.
/** @brief Return true if the face is a boundary face. */
bool IsBoundary() const
{
return topology == FaceTopology::Boundary;
}
/// Return true if the face is of the same type as @a type.
/// @brief Return true if the face is of the same type as @a type.
bool IsOfFaceType(FaceType type) const
{
switch (type)
@@ -2146,13 +2141,13 @@ public:
}
}
/// Return true if the face is a conforming face.
/// @brief Return true if the face is a conforming face.
bool IsConforming() const
{
return topology == FaceTopology::Conforming;
}
/// Return true if the face is a nonconforming fine face.
/// @brief Return true if the face is a nonconforming fine face.
bool IsNonconformingFine() const
{
return topology == FaceTopology::Nonconforming &&
@@ -2160,7 +2155,7 @@ public:
element[1].conformity == ElementConformity::Superset);
}
/// Return true if the face is a nonconforming coarse face.
/// @brief Return true if the face is a nonconforming coarse face.
/** Note that ghost nonconforming master faces cannot be clearly
identified as such with the currently available information, so this
method will return false for such faces. */
@@ -2170,7 +2165,7 @@ public:
element[1].conformity == ElementConformity::Subset;
}
/// cast operator from FaceInformation to FaceInfo.
/// @brief cast operator from FaceInformation to FaceInfo.
operator Mesh::FaceInfo() const;
};
@@ -3212,28 +3207,6 @@ 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)
{
+2 -3
View File
@@ -1328,12 +1328,11 @@ void Mesh::ReadNURBSMesh(std::istream &input, int &curved, int &read_gf,
if (NURBSext->HavePatches())
{
NURBSFECollection *fec = new NURBSFECollection(NURBSext->GetOrder());
const int vdim = NURBSext->GetPatchSpaceDimension();
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, vdim,
FiniteElementSpace *fes = new FiniteElementSpace(this, fec, Dim,
Ordering::byVDIM);
Nodes = new GridFunction(fes);
Nodes->MakeOwner(fec);
NURBSext->SetCoordsFromPatches(*Nodes, vdim);
NURBSext->SetCoordsFromPatches(*Nodes);
own_nodes = 1;
read_gf = 0;
spaceDim = Nodes->VectorDim();
+234 -631
View File
File diff suppressed because it is too large Load Diff
+32 -141
View File
@@ -51,21 +51,6 @@ 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;
@@ -74,28 +59,18 @@ public:
integers are read, for order and number of control points. */
KnotVector(std::istream &input);
/** @brief Create a KnotVector with order @a order.
When @a NCP is not provided the number of control points is set to
@a order + 1, and the first @a order + 1 knots are set to 0 and last
@a order + 1 knots are set to 1.
When @a NCP is given number of control points is @a NCP and
the knots are initialized to -1) */
KnotVector(int order, int NCP = -1);
/** @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 with undefined knots (initialized to -1) of
order @a order and number of control points @a NCP. */
KnotVector(int order, int NCP);
/** @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.
The intervals refer to spans between unique knot values (not counting
zero-size intervals at repeated knots), and the continuity values should
be >= -1 (discontinuous) and <= order-1 (maximally-smooth for the given
polynomial degree). Periodicity is not supported.*/
polynomial degree). Periodicity is not supported.
*/
KnotVector(int order, const Vector& intervals,
const Array<int>& continuity);
@@ -128,69 +103,13 @@ public:
with @a isElement for non-empty knot spans (elements). */
int GetNKS() const { return NumOfControlPoints - Order; }
/// 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 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
real_t getKnotLocation(real_t xi, int ni) const
{ return (xi*knot(ni+1) + (1. - xi)*knot(ni)); }
/// 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;
int findKnotSpan(real_t u) const;
// The following functions evaluate shape functions, which are B-spline basis
// functions.
@@ -217,32 +136,19 @@ 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.
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;
@a u. */
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);
/** @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;
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);
/** 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
@@ -285,18 +191,6 @@ 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() { }
@@ -315,6 +209,14 @@ 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
};
@@ -697,26 +599,22 @@ protected:
/// Throw an error if any boundary patch has invalid KnotVector orientation.
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, one per patch and
parametric direction, accounting for the edge orientations. */
/** @brief Create the comprehensive set of KnotVectors. In 1D, this set is
identical to the unique set of KnotVectors. */
void CreateComprehensiveKV();
/** @brief Update the unique set of KnotVectors from the comprehensive set
of KnotVectors. */
/** Update the unique set of KnotVectors. In 1D, this set is identical to
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. */
difference, true otherwise. This function throws an error in 1D. */
bool ConsistentKVSets();
/// Return KnotVectors in @a kv in each dimension for patch @a p.
@@ -922,13 +820,6 @@ 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(); }
@@ -1042,9 +933,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,
with physical vector dimension @a vdim, and erase @a patches. */
void SetCoordsFromPatches(Vector &Nodes, int vdim);
/** @brief Set FE coordinates in @a Nodes, using data from @a patches, and
erase @a patches. */
void SetCoordsFromPatches(Vector &Nodes);
/** @brief Read a GridFunction @a sol from stream @a input, written
patch-by-patch, e.g. with PrintSolution(). */
+3 -13
View File
@@ -3041,7 +3041,7 @@ void ParMesh::GetSharedFaceTransformationsByLocalIndex(
// for ghost faces we need a special version of GetFaceTransformation
if (is_ghost)
{
GetGhostFaceTransformation(FaceNo, FElTr);
GetGhostFaceTransformation(FElTr, face_type, face_geom);
mask |= FaceElementTransformations::HAVE_FACE;
}
@@ -3064,29 +3064,19 @@ void ParMesh::GetSharedFaceTransformationsByLocalIndex(
}
void ParMesh::GetGhostFaceTransformation(
int FaceNo, FaceElementTransformations &FElTr) const
FaceElementTransformations &FElTr, Element::Type face_type,
Geometry::Type face_geom) 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),
+9 -1
View File
@@ -150,7 +150,15 @@ protected:
int elem, int start, int end, const int fverts[][N]);
void GetGhostFaceTransformation(
int FaceNo, FaceElementTransformations &FElTr) const;
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);
}
/// Update the groups after triangle refinement
void RefineGroups(const DSTable &v_to_v, int *middle);
+4 -10
View File
@@ -1195,22 +1195,14 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
}
}
// 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 there are shared slaves, they will also need to be updated.
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; }
// enlarge Mesh::faces_info for ghost slaves
MFEM_ASSERT(pmesh.faces_info.Size() == nfaces, "");
MFEM_ASSERT(pmesh.GetNumFaces() == nfaces, "");
pmesh.faces_info.SetSize(nfaces + nghosts);
for (int i = nfaces; i < pmesh.faces_info.Size(); i++)
@@ -1311,12 +1303,14 @@ void ParNCMesh::GetFaceNeighbors(ParMesh &pmesh)
// Mesh::ApplyLocalSlaveTransformation.
}
MFEM_ASSERT(fi.NCFace < 0, "fi.NCFace = " << fi.NCFace);
fi.NCFace = pmesh.nc_faces_info.Size();
pmesh.nc_faces_info.Append(Mesh::NCFaceInfo(true, sf.master, pm));
}
}
}
// In 3D some extra orientation data structures can be needed.
if (Dim == 3)
{
+6 -8
View File
@@ -34,16 +34,14 @@ 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 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.
* 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).
*
* If a surface is extracted from a volume, the boundary attribute from the
* 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.
* 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).
*
* For more customized boundary attributes, the resulting ParSubMesh has to be
* postprocessed.
+6 -8
View File
@@ -28,16 +28,14 @@ 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 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.
* 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).
*
* If a surface is extracted from a volume, the boundary attribute from the
* 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.
* 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).
*
* For more customized boundary attributes, the resulting SubMesh has to be
* postprocessed.
+21
View File
@@ -232,6 +232,27 @@ 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)
{
+3 -8
View File
@@ -33,14 +33,9 @@ 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 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); }
}
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);
/// Transform a mesh according to an arbitrary affine transformation
/// y = A x + b
+9 -109
View File
@@ -174,6 +174,7 @@ ParticleTrajectories::ParticleTrajectories(const ParticleSet &particles,
void ParticleTrajectories::AddSegmentStart()
{
if (!pset.GetNParticles()) { return; }
// Create a new mesh for all particle segments for this timestep
segment_meshes.emplace_front(1, pset.GetNParticles()*2,
pset.GetNParticles(),
@@ -199,10 +200,11 @@ void ParticleTrajectories::AddSegmentStart()
void ParticleTrajectories::SetSegmentEnd()
{
if (segment_meshes.empty()) { return; } // no segments to end
const Array<ParticleSet::IDType> &end_ids = pset.GetIDs();
// Add all endpoint vertices + segments for all particles that were in
// SetSegmentStart
// Add all endpoint vertices + segments for all particles
int num_start = segment_ids.front().Size();
for (int i = 0; i < num_start; i++)
{
@@ -228,6 +230,11 @@ void ParticleTrajectories::SetSegmentEnd()
void ParticleTrajectories::Visualize()
{
SetSegmentEnd();
if (segment_meshes.empty() && !mesh)
{
AddSegmentStart();
return;
}
// Create a mesh of all the trajectory segments
std::vector<Mesh*> all_meshes;
@@ -239,23 +246,8 @@ void ParticleTrajectories::Visualize()
{
all_meshes.push_back(mesh);
}
if (mesh_bb)
{
all_meshes.push_back(mesh_bb);
}
Mesh trajectories(all_meshes.data(), all_meshes.size());
bool vis = trajectories.GetNE() > 0;
#ifdef MFEM_USE_MPI
MPI_Allreduce(MPI_IN_PLACE, &vis, 1, MFEM_MPI_CXX_BOOL,
MPI_LOR, pset.GetComm());
#endif // MFEM_USE_MPI
if (!vis) // if all rank have 0 elements, skip visualization
{
AddSegmentStart();
return;
}
#ifdef MFEM_USE_MPI
VisualizeMesh(sock, vishost, visport, trajectories, comm,
@@ -268,97 +260,5 @@ void ParticleTrajectories::Visualize()
AddSegmentStart();
}
void ParticleTrajectories::SetVisualizationBoundingBox(const Vector &xmin,
const Vector &xmax)
{
MFEM_VERIFY(xmin.Size() == pset.GetDim() &&
xmax.Size() == pset.GetDim(),
"Bounding box dimension must match ParticleSet dimension.");
// Create a box mesh for visualization
if (mesh_bb)
{
delete mesh_bb;
mesh_bb = nullptr;
}
if (pset.GetDim() == 2)
{
int dim = 2;
int nvert = 4;
int nelem = 4;
mesh_bb = new Mesh(1, nvert, nelem, 0, dim);
Vector v0(dim), v1(dim), v2(dim), v3(dim);
v0 = xmin;
v1 = xmax;
v2[0] = xmax[0]; v2[1] = xmin[1];
v3[0] = xmin[0]; v3[1] = xmax[1];
mesh_bb->AddVertex(v0);
mesh_bb->AddVertex(v1);
mesh_bb->AddVertex(v2);
mesh_bb->AddVertex(v3);
int vi[2] = {0,1};
mesh_bb->AddSegment(vi);
vi[0] = 1; vi[1] = 2;
mesh_bb->AddSegment(vi);
vi[0] = 2; vi[1] = 3;
mesh_bb->AddSegment(vi);
vi[0] = 3; vi[1] = 0;
mesh_bb->AddSegment(vi);
mesh_bb->FinalizeMesh();
}
else // dim == 3
{
int dim = 3;
int nvert = 8;
int nelem = 12;
mesh_bb = new Mesh(1, nvert, nelem, 0, dim);
Vector v(dim);
// Vertices
v[0] = xmin[0]; v[1] = xmin[1]; v[2] = xmin[2];
mesh_bb->AddVertex(v); // 0: 000
v[0] = xmax[0]; v[1] = xmin[1]; v[2] = xmin[2];
mesh_bb->AddVertex(v); // 1: 100
v[0] = xmax[0]; v[1] = xmax[1]; v[2] = xmin[2];
mesh_bb->AddVertex(v); // 2: 110
v[0] = xmin[0]; v[1] = xmax[1]; v[2] = xmin[2];
mesh_bb->AddVertex(v); // 3: 010
v[0] = xmin[0]; v[1] = xmin[1]; v[2] = xmax[2];
mesh_bb->AddVertex(v); // 4: 001
v[0] = xmax[0]; v[1] = xmin[1]; v[2] = xmax[2];
mesh_bb->AddVertex(v); // 5: 101
v[0] = xmax[0]; v[1] = xmax[1]; v[2] = xmax[2];
mesh_bb->AddVertex(v); // 6: 111
v[0] = xmin[0]; v[1] = xmax[1]; v[2] = xmax[2];
mesh_bb->AddVertex(v); // 7: 011
// Segments
int vi[2];
// Bottom face
vi[0] = 0; vi[1] = 1; mesh_bb->AddSegment(vi);
vi[0] = 1; vi[1] = 2; mesh_bb->AddSegment(vi);
vi[0] = 2; vi[1] = 3; mesh_bb->AddSegment(vi);
vi[0] = 3; vi[1] = 0; mesh_bb->AddSegment(vi);
// Top face
vi[0] = 4; vi[1] = 5; mesh_bb->AddSegment(vi);
vi[0] = 5; vi[1] = 6; mesh_bb->AddSegment(vi);
vi[0] = 6; vi[1] = 7; mesh_bb->AddSegment(vi);
vi[0] = 7; vi[1] = 4; mesh_bb->AddSegment(vi);
// Vertical edges
vi[0] = 0; vi[1] = 4; mesh_bb->AddSegment(vi);
vi[0] = 1; vi[1] = 5; mesh_bb->AddSegment(vi);
vi[0] = 2; vi[1] = 6; mesh_bb->AddSegment(vi);
vi[0] = 3; vi[1] = 7; mesh_bb->AddSegment(vi);
mesh_bb->FinalizeMesh();
}
}
} // namespace common
} // namespace mfem
+2 -17
View File
@@ -46,8 +46,7 @@ class ParticleTrajectories
{
protected:
const ParticleSet &pset;
Mesh *mesh = nullptr; // optional edge mesh to visualize along with particles
Mesh *mesh_bb = nullptr; // optional bounding box mesh for visualization
Mesh *mesh = nullptr;
socketstream sock;
/// Track particle IDs that exist at the segment start.
@@ -91,24 +90,10 @@ public:
const char *keys_=nullptr);
/// Add a mesh to be visualized along with the particle trajectories.
void AddMeshForVisualization(Mesh *mesh_)
{
MFEM_VERIFY(mesh_->Dimension() == 1,
"Mesh dimension must be 1 to match the particle trajectory.");
mesh = mesh_;
}
void AddMeshForVisualization(Mesh *mesh_) { mesh = mesh_; }
/// Visualize the particle trajectories (and mesh if provided).
void Visualize();
/// Set the bounding box for visualization.
void SetVisualizationBoundingBox(const Vector &xmin, const Vector &xmax);
/// Destructor
~ParticleTrajectories()
{
delete mesh_bb;
}
};
+5 -7
View File
@@ -34,13 +34,11 @@ if (MFEM_USE_MPI)
EXTRA_HEADERS maxwell_solver.hpp ${MFEM_MINIAPPS_COMMON_HEADERS}
LIBRARIES mfem-common)
if (MFEM_USE_GSLIB)
add_mfem_miniapp(lorentz
MAIN lorentz.cpp
EXTRA_HEADERS ${MFEM_MINIAPPS_COMMON_HEADERS}
LIBRARIES mfem-common)
endif()
add_mfem_miniapp(lorentz
MAIN lorentz.cpp
EXTRA_HEADERS ${MFEM_MINIAPPS_COMMON_HEADERS}
LIBRARIES mfem-common)
# Add the corresponding tests to the "test" target
if (MFEM_ENABLE_TESTING)
add_test(NAME tesla_np=4

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