diff --git a/.gitignore b/.gitignore
index e2a2e99043c..9640e0f33a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,8 @@ config/sample-runs-build.log
doc/CodeDocumentation.conf
doc/CodeDocumentation.html
doc/CodeDocumentation
+doc/undoc.log
+doc/warnings.log
# Temporary files created by the tests.
*.stderr
diff --git a/.travis.yml b/.travis.yml
index b678d918093..4adc20c5514 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,8 +11,6 @@
language: cpp
-sudo: false
-
stages:
- checks
- tests
@@ -370,8 +368,10 @@ script:
# Compiler
- if [ $MPI == "YES" ]; then
export MYCXX=mpic++;
+ export MAKE_CXX_FLAG=MPICXX=$MYCXX;
else
export MYCXX="$CXX";
+ export MAKE_CXX_FLAG=CXX=$MYCXX;
fi
# Print the compiler version
@@ -384,12 +384,9 @@ script:
if [ "$CODECOV" == "YES" ]; then
CPPFLAGS="--coverage -g";
fi;
- if [ "$CXX" == "clang++" ]; then
- export MFEM_PERF_SW=clang;
- fi
# Configure the library
- - make config MFEM_USE_MPI=$MPI MFEM_DEBUG=$DEBUG MFEM_CXX="$MYCXX"
+ - make config MFEM_USE_MPI=$MPI MFEM_DEBUG=$DEBUG $MAKE_CXX_FLAG
MFEM_MPI_NP=$NPROCS CPPFLAGS="$CPPFLAGS"
# Show the configuration
- make info
diff --git a/CHANGELOG b/CHANGELOG
index 907f91922a9..8d5d626105a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -23,6 +23,27 @@ Meshing improvements
Hessian for r-adaptivity using discrete fields, and allows use of skewness
and orientation based metrics.
+- Added support for r-adaptivity with more than one discrete field. This allows
+ the user to specify different discrete functions for controlling the
+ size, aspect-ratio, orientation, and skew of elements in the mesh.
+
+- Added TMOP capability for approximate tangential mesh relaxation.
+
+- Added support for reading periodic meshes in Gmsh format (version 2.2). See
+ for example the periodic-annulus-sector and periodic-torus-sector files in
+ the data directory.
+
+Performance improvements
+------------------------
+- Added support for explicit vectorization in the high-performance templated
+ code, which can now take advantage of specific intrinsics classes on the
+ following architectures:
+ - x86 (SSE/AVX/AVX2/AVX512),
+ - Power8 & Power9 (VSX),
+ - BG/Q (QPX).
+ These are now enabled by default, and can be disabled with MFEM_USE_SIMD=NO.
+ See the new file linalg/simd.hpp and the new directory linalg/simd.
+
Improved GPU capabilities
-------------------------
- Added support for Chebyshev accelerated polynomial smoother on GPU.
@@ -35,6 +56,24 @@ Discretization improvements
- Added support for simplices in GSLIB-FindPoints.
+- Added support for H1 and L2 element matrix assembly in the mass, convection,
+ diffusion, transpose, and the face DG trace integrators. This is compatible
+ with GPU device execution and is illustrated in Example 9/9p, see the option
+ '-ea'. When enabled, this level of assembly stores independent dense matrices
+ for the elements, and independent dense matrices for the faces in the DG case.
+
+- Added new partial assembly kernels for H(div) bilinear forms, as well as
+ VectorFEDivergenceIntegrator.
+
+- Improved the documentation of the GridFunction GetValue and GetVectorValue
+ methods. Expanded the GetValue and GetVectorValue methods which accept an
+ ElementTransformation argument to support evaluation on boundary elements
+ and, in the continuous field case, arbitrary mesh edges and faces.
+
+- Added new coefficient and vector coefficient classes for QuadratureFunctions.
+ Additionaly, new LinearForm integrators were also added which make use of
+ these new QuadratureFunction coefficient classes.
+
Linear and nonlinear solvers
----------------------------
- Added power method to iteratively estimate the largest eigenvalue and the
@@ -43,6 +82,17 @@ Linear and nonlinear solvers
- Added initial support for h- and p-multigrid solvers and preconditioners for
matrix-based and matrix-free discretizations with basic GPU capability.
+- Added a new IterativeSolverMonitor class that allows to monitor the residual
+ and solution during the solving process of an IterativeSolver after every
+ iteration.
+
+- Block arrays of parallel matrices can now be merged into a single parallel
+ matrix with the function HypreParMatrixFromBlocks. This could be useful for
+ solving block systems with parallel direct solvers such as STRUMPACK.
+
+- In SLISolver, changed the residual inner product from (Br,r) to (Br,Br) so the
+ solver can work with non-SPD preconditioner B.
+
New and updated examples and miniapps
-------------------------------------
- Added a new example, Example 25/25p, to demonstrate the use of a Perfectly
@@ -65,6 +115,12 @@ New and updated examples and miniapps
- Added a new meshing miniapp, Minimal Surface, which solves Plateau's problem:
the Dirichlet problem for the minimal surface equation.
+- Added partial assembly support to examples 4/4p and 5/5p, with diagonal
+ preconditioning.
+
+- Added a new test problem in example 24/24p, demonstrating a mixed bilinear
+ form for H(div) and L_2, with partial assembly support.
+
Improved testing
----------------
- Added a GitLab pipeline that automates PR testing on supercomputing systems
@@ -74,15 +130,17 @@ Improved testing
Miscellaneous
-------------
-- In SLISolver, changed the residual inner product from (Br,r) to (Br,Br) so the
- solver can work with non-SPD preconditioner B.
-
- Added support for ADIOS2 for parallel I/O with ParaView visualization. The
classes adios2stream and ADIOS2DataCollection are introduced in mfem as the
interfaces to generate ADIOS2 Binary Pack (BP4) directory datasets for the
entire spatial and temporal data. In addition, ADIOS2 allows for setting a
user-defined number of data substreams/subfiles. See examples 5, 9, 12, 16.
+- The integration order used in the ComputeLpError and ComputeElementLpError
+ methods of class GridFunction has been increased.
+
+- Various other simplifications, extensions, and bugfixes in the code.
+
Version 4.1, released on March 10, 2020
=======================================
diff --git a/INSTALL b/INSTALL
index ddc911225b8..46a714f7c3e 100644
--- a/INSTALL
+++ b/INSTALL
@@ -396,6 +396,12 @@ MFEM_USE_SIDRE = YES/NO
blueprint specification. When enabled, this option requires installation of
HDF5 (see also MFEM_USE_NETCDF), Conduit and LLNL's axom project.
+MFEM_USE_SIMD = YES/NO
+ Enables the high performance templated classes to use architecture dependent
+ SIMD intrinsics instead of the generic implementation of class AutoSIMD in
+ linalg/simd/auto.hpp. This option should be combined with suitable
+ compiler options, such as -march=native, to enable optimal vectorization.
+
MFEM_USE_CONDUIT = YES/NO
Enables support for converting MFEM Mesh and Grid Function objects to and
from Conduit Mesh Blueprint Descriptions (https://github.com/LLNL/conduit/)
@@ -426,6 +432,8 @@ MFEM_USE_PUMI = YES/NO
data management system that is capable of handling general non-manifold
models and effectively supports automated adaptive analysis. PUMI enables
support for parallel unstructured mesh modifications in MFEM.
+ The develop branch of PUMI repository (https://github.com/SCOREC/core)
+ should be used for most updated features.
MFEM_USE_UMPIRE = YES/NO
Enables support for Umpire, a resource management library that allows the
@@ -609,8 +617,9 @@ The specific libraries and their options are:
- PUMI (optional), used when MFEM_USE_PUMI = YES.
URL: https://scorec.rpi.edu/pumi
+ https://github.com/SCOREC/core
Options: PUMI_OPT, PUMI_LIB.
- Versions: PUMI >= 2.2.0.
+ Versions: PUMI >= 2.2.3.
- HiOp (optional), used when MFEM_USE_HIOP = YES.
URL: https://github.com/LLNL/hiop
diff --git a/config/cmake/MFEMConfig.cmake.in b/config/cmake/MFEMConfig.cmake.in
index 896e1c35105..7e98372647a 100644
--- a/config/cmake/MFEMConfig.cmake.in
+++ b/config/cmake/MFEMConfig.cmake.in
@@ -47,6 +47,7 @@ set(MFEM_USE_OCCA @MFEM_USE_OCCA@)
set(MFEM_USE_RAJA @MFEM_USE_RAJA@)
set(MFEM_USE_CEED @MFEM_USE_CEED@)
set(MFEM_USE_UMPIRE @MFEM_USE_UMPIRE@)
+set(MFEM_USE_SIMD @MFEM_USE_SIMD@)
set(MFEM_USE_ADIOS2 @MFEM_USE_ADIOS2@)
set(MFEM_CXX_COMPILER "@CMAKE_CXX_COMPILER@")
diff --git a/config/cmake/config.hpp.in b/config/cmake/config.hpp.in
index 26327ae59b4..4bcbf59d885 100644
--- a/config/cmake/config.hpp.in
+++ b/config/cmake/config.hpp.in
@@ -107,6 +107,9 @@
// Enable MFEM functionality based on the Sidre library
#cmakedefine MFEM_USE_SIDRE
+// Enable the use of SIMD in the high performance templated classes
+#cmakedefine MFEM_USE_SIMD
+
// Enable MFEM functionality based on Conduit
#cmakedefine MFEM_USE_CONDUIT
diff --git a/config/cmake/modules/MfemCmakeUtilities.cmake b/config/cmake/modules/MfemCmakeUtilities.cmake
index 6f3428933c1..850479680fb 100644
--- a/config/cmake/modules/MfemCmakeUtilities.cmake
+++ b/config/cmake/modules/MfemCmakeUtilities.cmake
@@ -733,7 +733,7 @@ function(mfem_export_mk_files)
MFEM_USE_SUPERLU MFEM_USE_STRUMPACK MFEM_USE_GNUTLS
MFEM_USE_GSLIB MFEM_USE_NETCDF MFEM_USE_PETSC MFEM_USE_MPFR MFEM_USE_SIDRE
MFEM_USE_CONDUIT MFEM_USE_PUMI MFEM_USE_CUDA MFEM_USE_OCCA MFEM_USE_RAJA
- MFEM_USE_UMPIRE)
+ MFEM_USE_UMPIRE MFEM_USE_SIMD MFEM_USE_ADIOS2)
foreach(var ${CONFIG_MK_BOOL_VARS})
if (${var})
set(${var} YES)
@@ -743,6 +743,7 @@ function(mfem_export_mk_files)
endforeach()
# TODO: Add support for MFEM_USE_CUDA=YES
set(MFEM_CXX ${CMAKE_CXX_COMPILER})
+ set(MFEM_HOST_CXX ${MFEM_CXX})
set(MFEM_CPPFLAGS "")
string(STRIP "${CMAKE_CXX_FLAGS_${BUILD_TYPE}} ${CMAKE_CXX_FLAGS}"
MFEM_CXXFLAGS)
diff --git a/config/config.hpp.in b/config/config.hpp.in
index d71cfcfbe0b..56a8cee7da2 100644
--- a/config/config.hpp.in
+++ b/config/config.hpp.in
@@ -106,6 +106,9 @@
// Enable Sidre support
// #define MFEM_USE_SIDRE
+// Enable the use of SIMD in the high performance templated classes
+// #define MFEM_USE_SIMD
+
// Enable Conduit support
// #define MFEM_USE_CONDUIT
diff --git a/config/config.mk.in b/config/config.mk.in
index 4d7c34affcc..99d0c822b8e 100644
--- a/config/config.mk.in
+++ b/config/config.mk.in
@@ -49,10 +49,12 @@ MFEM_USE_RAJA = @MFEM_USE_RAJA@
MFEM_USE_OCCA = @MFEM_USE_OCCA@
MFEM_USE_CEED = @MFEM_USE_CEED@
MFEM_USE_UMPIRE = @MFEM_USE_UMPIRE@
+MFEM_USE_SIMD = @MFEM_USE_SIMD@
MFEM_USE_ADIOS2 = @MFEM_USE_ADIOS2@
# Compiler, compile options, and link options
MFEM_CXX = @MFEM_CXX@
+MFEM_HOST_CXX = @MFEM_HOST_CXX@
MFEM_CPPFLAGS = @MFEM_CPPFLAGS@
MFEM_CXXFLAGS = @MFEM_CXXFLAGS@
MFEM_TPLFLAGS = @MFEM_TPLFLAGS@
diff --git a/config/defaults.cmake b/config/defaults.cmake
index 5854e6916e2..5703860e106 100644
--- a/config/defaults.cmake
+++ b/config/defaults.cmake
@@ -49,6 +49,7 @@ option(MFEM_USE_OCCA "Enable OCCA" OFF)
option(MFEM_USE_RAJA "Enable RAJA" OFF)
option(MFEM_USE_CEED "Enable CEED" OFF)
option(MFEM_USE_UMPIRE "Enable Umpire" OFF)
+option(MFEM_USE_SIMD "Enable use of SIMD intrinsics" ON)
option(MFEM_USE_ADIOS2 "Enable ADIOS2" OFF)
set(MFEM_MPI_NP 4 CACHE STRING "Number of processes used for MPI tests")
diff --git a/config/defaults.mk b/config/defaults.mk
index 490fa4f0e30..ed5ab20f563 100644
--- a/config/defaults.mk
+++ b/config/defaults.mk
@@ -137,6 +137,7 @@ MFEM_USE_RAJA = NO
MFEM_USE_OCCA = NO
MFEM_USE_CEED = NO
MFEM_USE_UMPIRE = NO
+MFEM_USE_SIMD = YES
MFEM_USE_ADIOS2 = NO
# Compile and link options for zlib.
diff --git a/config/tconfig.hpp b/config/tconfig.hpp
index 59cc77ecd98..a10ae8f4b68 100644
--- a/config/tconfig.hpp
+++ b/config/tconfig.hpp
@@ -29,8 +29,20 @@
#define MFEM_ALWAYS_INLINE
#endif
+// --- MFEM_VECTORIZE_LOOP (disabled)
+#if (__cplusplus >= 201103L) && !defined(MFEM_DEBUG) && defined(__GNUC__)
+//#define MFEM_VECTORIZE_LOOP _Pragma("GCC ivdep")
+#define MFEM_VECTORIZE_LOOP
+#else
+#define MFEM_VECTORIZE_LOOP
+#endif
+
+// MFEM_TEMPLATE_BLOCK_SIZE is the block size used by the template matrix-matrix
+// multiply, Mult_AB, defined in tmatrix.hpp. This parameter will generally
+// require tuning to determine good value. It is probably highly influenced by
+// the SIMD width when Mult_AB is used with a SIMD type like AutoSIMD.
#define MFEM_TEMPLATE_BLOCK_SIZE 4
-#define MFEM_SIMD_SIZE 32
+
#define MFEM_TEMPLATE_ENABLE_SERIALIZE
// #define MFEM_TEMPLATE_ELTRANS_HAS_NODE_DOFS
@@ -38,11 +50,6 @@
// #define MFEM_TEMPLATE_FIELD_EVAL_DATA_HAS_DOFS
#define MFEM_TEMPLATE_INTRULE_COEFF_PRECOMP
-// derived macros
-#define MFEM_ROUNDUP(val,base) ((((val)+(base)-1)/(base))*(base))
-#define MFEM_ALIGN_SIZE(size,type) \
- MFEM_ROUNDUP(size,(MFEM_SIMD_SIZE)/sizeof(type))
-
#ifdef MFEM_COUNT_FLOPS
namespace mfem
{
diff --git a/data/periodic-annulus-sector.geo b/data/periodic-annulus-sector.geo
new file mode 100644
index 00000000000..655b41bddf3
--- /dev/null
+++ b/data/periodic-annulus-sector.geo
@@ -0,0 +1,37 @@
+SetFactory("OpenCASCADE");
+
+R1 = 1.0;
+R2 = 2.0;
+
+Point(1) = {0.0, 0, 0, 1.0};
+Point(2) = {R1, 0, 0, 1.0};
+Point(3) = {R2, 0, 0, 1.0};
+Point(4) = {R1*Cos(Pi/3), R1*Sin(Pi/3), 0, 1.0};
+Point(5) = {R2*Cos(Pi/3), R2*Sin(Pi/3), 0, 1.0};
+Line(1) = {2, 3};
+Line(2) = {4, 5};
+Circle(3) = {2, 1, 4};
+Circle(4) = {3, 1, 5};
+Curve Loop(5) = {1, 4, -2, -3};
+Plane Surface(1) = {5};
+
+Transfinite Curve{1} = 7;
+Transfinite Curve{2} = 7;
+Transfinite Curve{3} = 4;
+Transfinite Curve{4} = 10;
+
+// Set a rotation periodicity constraint:
+Periodic Line{1} = {2} Rotate{{0,0,1}, {0,0,0}, -Pi/3};
+
+// Tag surfaces and volumes with positive integers
+Physical Curve(1) = {3};
+Physical Curve(2) = {4};
+Physical Curve(3) = {1};
+Physical Curve(4) = {2};
+Physical Surface(1) = {1};
+
+// Generate 2D mesh
+Mesh 2;
+Mesh.MshFileVersion = 2.2;
+
+Save "periodic-annulus-sector.msh";
diff --git a/data/periodic-annulus-sector.msh b/data/periodic-annulus-sector.msh
new file mode 100644
index 00000000000..8c70fef1f69
--- /dev/null
+++ b/data/periodic-annulus-sector.msh
@@ -0,0 +1,185 @@
+$MeshFormat
+2.2 0 8
+$EndMeshFormat
+$Nodes
+55
+1 1 0 0
+2 2 0 0
+3 0.5000000000000001 0.8660254037844386 0
+4 1 1.732050807568877 0
+5 1.166666666666667 0 0
+6 1.333333333333333 0 0
+7 1.5 0 0
+8 1.666666666666667 0 0
+9 1.833333333333333 0 0
+10 0.5833333333333335 1.010362971081845 0
+11 0.6666666666666667 1.154700538379251 0
+12 0.7500000000000002 1.299038105676658 0
+13 0.8333333333333335 1.443375672974064 0
+14 0.9166666666666669 1.587713240271471 0
+15 0.9396926207859085 0.3420201433256683 0
+16 0.7660444431189786 0.6427876096865386 0
+17 1.986476715483886 0.2321858282504602 0
+18 1.946089741159648 0.4612317414848793 0
+19 1.879385241571817 0.6840402866513365 0
+20 1.787265280646825 0.8975983604009234 0
+21 1.670975622825874 1.09901795614161 0
+22 1.532088886237958 1.285575219373077 0
+23 1.372483275737469 1.454747283146095 0
+24 1.194317183405575 1.604246385510085 0
+25 1.425989114816062 0.1915326920916892 0
+26 0.8788667344146573 1.13917645290495 0
+27 1.630372059110754 0.7154531062316609 0
+28 1.436395769298814 1.053728612482506 0
+29 1.081023776188756 0.6241293681829633 0
+30 1.168737372335971 1.428012728596308 0
+31 1.821063986059922 0.298149890497067 0
+32 1.234707097211386 0.3469796339295647 0
+33 1.377747393186519 0.6200150626754309 0
+34 1.457047681210906 0.3890895843559762 0
+35 0.917846726184522 0.8957978954532204 0
+36 1.218335619030348 0.9017812086952638 0
+37 1.066623110765233 1.061857005744772 0
+38 1.587029716281926 0.1355955181472859 0
+39 1.744445799211916 0.1441515753740107 0
+40 1.25 0.1443375672974065 0
+41 1.453660070628011 0.8435769396609902 0
+42 1.741367044061892 0.499612708014486 0
+43 1.30550638526547 1.257610469847477 0
+44 1.118213276932792 0.1666674689105279 0
+45 0.9109440214958271 1.306610291787315 0
+46 0.9970618258753989 1.438658589955562 0
+47 0.7499999999999998 1.010362971081845 0
+48 0.7034449005273667 0.8850673702175776 0
+49 1.605449512513618 0.9269067082200894 0
+50 1.561654019115059 0.5298592532912715 0
+51 1.229782222487711 1.096820457143683 0
+52 1.617066998712459 0.3090202662210922 0
+53 1.079645953234324 1.246963713711438 0
+54 1.877063966817811 0.1348974588243076 0
+55 1.055356609656722 1.558136350380461 0
+$EndNodes
+$Elements
+108
+1 1 2 3 1 1 5
+2 1 2 3 1 5 6
+3 1 2 3 1 6 7
+4 1 2 3 1 7 8
+5 1 2 3 1 8 9
+6 1 2 3 1 9 2
+7 1 2 4 2 3 10
+8 1 2 4 2 10 11
+9 1 2 4 2 11 12
+10 1 2 4 2 12 13
+11 1 2 4 2 13 14
+12 1 2 4 2 14 4
+13 1 2 1 3 1 15
+14 1 2 1 3 15 16
+15 1 2 1 3 16 3
+16 1 2 2 4 2 17
+17 1 2 2 4 17 18
+18 1 2 2 4 18 19
+19 1 2 2 4 19 20
+20 1 2 2 4 20 21
+21 1 2 2 4 21 22
+22 1 2 2 4 22 23
+23 1 2 2 4 23 24
+24 1 2 2 4 24 4
+25 2 2 1 1 32 40 25
+26 2 2 1 1 25 34 32
+27 2 2 1 1 33 41 36
+28 2 2 1 1 38 52 25
+29 2 2 1 1 33 36 29
+30 2 2 1 1 26 47 35
+31 2 2 1 1 35 37 26
+32 2 2 1 1 25 52 34
+33 2 2 1 1 32 44 40
+34 2 2 1 1 15 32 29
+35 2 2 1 1 15 29 16
+36 2 2 1 1 36 41 28
+37 2 2 1 1 32 33 29
+38 2 2 1 1 50 52 42
+39 2 2 1 1 32 34 33
+40 2 2 1 1 42 52 31
+41 2 2 1 1 43 53 51
+42 2 2 1 1 27 41 33
+43 2 2 1 1 26 53 45
+44 2 2 1 1 18 31 17
+45 2 2 1 1 29 35 16
+46 2 2 1 1 29 36 35
+47 2 2 1 1 24 30 23
+48 2 2 1 1 30 53 43
+49 2 2 1 1 17 54 2
+50 2 2 1 1 4 55 24
+51 2 2 1 1 28 51 36
+52 2 2 1 1 47 48 35
+53 2 2 1 1 36 37 35
+54 2 2 1 1 37 53 26
+55 2 2 1 1 22 28 21
+56 2 2 1 1 20 27 19
+57 2 2 1 1 33 50 27
+58 2 2 1 1 15 44 32
+59 2 2 1 1 18 42 31
+60 2 2 1 1 30 43 23
+61 2 2 1 1 35 48 16
+62 2 2 1 1 31 54 17
+63 2 2 1 1 9 39 8
+64 2 2 1 1 8 38 7
+65 2 2 1 1 7 25 6
+66 2 2 1 1 22 43 28
+67 2 2 1 1 23 43 22
+68 2 2 1 1 39 54 31
+69 2 2 1 1 19 42 18
+70 2 2 1 1 24 55 30
+71 2 2 1 1 27 42 19
+72 2 2 1 1 13 46 14
+73 2 2 1 1 51 53 37
+74 2 2 1 1 39 52 38
+75 2 2 1 1 6 40 5
+76 2 2 1 1 34 52 50
+77 2 2 1 1 12 45 13
+78 2 2 1 1 30 55 46
+79 2 2 1 1 10 47 11
+80 2 2 1 1 8 39 38
+81 2 2 1 1 28 49 21
+82 2 2 1 1 7 38 25
+83 2 2 1 1 41 49 28
+84 2 2 1 1 20 49 27
+85 2 2 1 1 11 26 12
+86 2 2 1 1 27 49 41
+87 2 2 1 1 31 52 39
+88 2 2 1 1 25 40 6
+89 2 2 1 1 2 54 9
+90 2 2 1 1 14 55 4
+91 2 2 1 1 45 53 46
+92 2 2 1 1 45 46 13
+93 2 2 1 1 5 44 1
+94 2 2 1 1 21 49 20
+95 2 2 1 1 46 53 30
+96 2 2 1 1 3 48 10
+97 2 2 1 1 34 50 33
+98 2 2 1 1 36 51 37
+99 2 2 1 1 26 45 12
+100 2 2 1 1 11 47 26
+101 2 2 1 1 27 50 42
+102 2 2 1 1 40 44 5
+103 2 2 1 1 43 51 28
+104 2 2 1 1 10 48 47
+105 2 2 1 1 9 54 39
+106 2 2 1 1 46 55 14
+107 2 2 1 1 1 44 15
+108 2 2 1 1 16 48 3
+$EndElements
+$Periodic
+1
+1 1 2
+Affine 0.5000000000000001 0.8660254037844386 0 0 -0.8660254037844386 0.5000000000000001 0 0 0 0 1 0 0 0 0 1
+7
+9 14
+6 11
+8 13
+5 10
+7 12
+2 4
+1 3
+$EndPeriodic
diff --git a/data/periodic-torus-sector.geo b/data/periodic-torus-sector.geo
new file mode 100644
index 00000000000..05eacae9089
--- /dev/null
+++ b/data/periodic-torus-sector.geo
@@ -0,0 +1,25 @@
+SetFactory("OpenCASCADE");
+
+R = 1.5;
+r = 0.5;
+
+Torus(1) = {0,0,0, R, r, Pi/3};
+
+pts() = PointsOf{ Volume{1}; };
+
+Characteristic Length{ pts() } = 0.25;
+
+// Set a rotation periodicity constraint:
+Periodic Surface{3} = {2} Rotate{{0,0,1}, {0,0,0}, Pi/3};
+
+// Tag surfaces and volumes with positive integers
+Physical Surface(1) = {1};
+Physical Surface(2) = {2};
+Physical Surface(3) = {3};
+Physical Volume(1) = {1};
+
+// Generate 3D mesh
+Mesh 3;
+
+Mesh.MshFileVersion = 2.2;
+Save "periodic-torus-sector.msh";
diff --git a/data/periodic-torus-sector.msh b/data/periodic-torus-sector.msh
new file mode 100644
index 00000000000..2bb34fd344f
--- /dev/null
+++ b/data/periodic-torus-sector.msh
@@ -0,0 +1,1056 @@
+$MeshFormat
+2.2 0 8
+$EndMeshFormat
+$Nodes
+178
+1 1 1.732050807568877 -1.224646799147353e-16
+2 2 0 -1.224646799147353e-16
+3 1.986476715483886 0.2321858282504604 -1.224646799147353e-16
+4 1.946089741159648 0.4612317414848803 -1.224646799147353e-16
+5 1.879385241571817 0.6840402866513373 -1.224646799147353e-16
+6 1.787265280646825 0.8975983604009242 -1.224646799147353e-16
+7 1.670975622825873 1.099017956141612 -1.224646799147353e-16
+8 1.532088886237956 1.285575219373079 -1.224646799147353e-16
+9 1.372483275737467 1.454747283146097 -1.224646799147353e-16
+10 1.194317183405573 1.604246385510087 -1.224646799147353e-16
+11 0.9713640064107713 1.682451811747116 0.2323615860315311
+12 0.8920161844090693 1.545017352570237 0.4114919360856992
+13 0.7801341700627819 1.351232019269317 0.4963544370492818
+14 0.6613487770564798 1.145489683385365 0.467508120445488
+15 0.5628723129571309 0.9749234442155601 0.3315613291201853
+16 0.5072645455148519 0.8786079657100585 0.1196578310999915
+17 0.5072645456434897 0.8786079659328657 -0.1196578321437994
+18 0.5628723130955459 0.9749234444553019 -0.3315613294326618
+19 0.6613487782393664 1.145489685434185 -0.4675081213427075
+20 0.7801341701356748 1.351232019395571 -0.4963544370315802
+21 0.8920161866827891 1.545017356508435 -0.4114919329468283
+22 0.9713640064234279 1.682451811769038 -0.2323615859833005
+23 1.942728012826605 0 0.2323615860218843
+24 1.784032373365578 0 0.4114919329468282
+25 1.560268340127662 0 0.496354437049027
+26 1.322697556478732 0 0.4675081213427075
+27 1.12574462591445 0 0.3315613291203977
+28 1.014529091286974 0 0.119657832143779
+29 1.014529091286974 0 -0.1196578321437787
+30 1.125744625914449 0 -0.3315613291203975
+31 1.322697556478732 0 -0.4675081213427074
+32 1.560268340127661 0 -0.496354437049027
+33 1.784032373365578 0 -0.4114919329468283
+34 1.942728012826605 0 -0.2323615860218846
+35 0.9094092833839373 1.140104827634976 0.4982645365438731
+36 0.9145403631597188 1.127554721496875 -0.4976726875517566
+37 1.439543163169036 0.2066277058988615 0.4979068470620236
+38 1.438046243463446 0.2043628722395764 -0.4977381412060781
+39 1.696686878481083 0.9795826260215588 0.1979072290649842
+40 1.705046974097277 0.9627338322458719 -0.2004271899713759
+41 1.425045163821982 1.344460771417579 0.1979072290649849
+42 1.426940472289519 1.3461283575362 -0.1919478807927782
+43 1.881724626638111 0.5593582007143618 -0.1885115572979801
+44 1.876859764350238 0.561894927701204 0.197907229064985
+45 0.6673803921678336 0.7447234565204733 0.002192541055854352
+46 1.050984583067059 0.1682970125584115 0.2454183754604045
+47 1.04172076116343 0.2047537321359636 -0.240523499060071
+48 1.113311450848127 1.513972982303749 0.3258377535851458
+49 1.113311453634618 1.513972982601351 -0.3258377513847104
+50 1.867794790374375 0.2071695099506198 0.3258377513964845
+51 1.867794790374375 0.2071695099506223 -0.3258377513964847
+52 0.699980884447671 0.7923257843216313 0.2322978976561902
+53 0.8143742402024221 0.6091370533569924 0.1292055331351144
+54 0.9234512163817853 0.6950259596839291 0.362644934140704
+55 0.8149471563398465 0.6014811214666318 -0.1127408743463922
+56 0.9089882740735398 0.4178664121885499 0.02087403616690475
+57 0.8036391734156695 0.9174735877750071 0.4140227221617467
+58 1.04528975873691 0.9197904008254907 0.488274495579655
+59 1.132701372162777 0.6901822877229037 0.4688993254694935
+60 1.277010042388928 0.8802828084812561 0.4973904977528614
+61 1.353438092664882 0.6544862687155961 0.4999885855347576
+62 1.206606309387528 1.137304388082402 0.4743397986991702
+63 1.203861688615928 0.4781648664665324 0.456198716333284
+64 1.420536892021458 1.019694143898059 0.4338017162656254
+65 0.9513629883317927 0.4940253704652492 0.2584636609340892
+66 0.706186918123033 0.8068591038835513 -0.258902571664125
+67 0.8997137668513249 0.6778946202619701 -0.332423851739337
+68 0.8754534158794457 0.8896155968337219 -0.431929083733448
+69 1.068014665374453 0.7835782676774907 -0.4682372295982862
+70 1.094885326848669 0.5587005809631567 -0.420314909746101
+71 1.278914902324639 0.6814395891060806 -0.4974057052434921
+72 1.253859094818866 0.9188999749034799 -0.4970184215451375
+73 1.467592256574374 0.8234236412687718 -0.4653816350826456
+74 1.493455917091833 0.5922523984885606 -0.4885036149289653
+75 1.427903945844154 1.053079485024634 -0.4180895076610792
+76 1.192654033939925 1.176482818815241 -0.468273221931405
+77 1.664532079158557 0.7347462962335058 -0.3846173119001672
+78 1.684193467076019 0.4966353289223597 -0.4295574916595252
+79 0.9581284611377014 0.4449942507914502 -0.2307371775066812
+80 1.060510603934137 1.323992179178645 0.4598289463295096
+81 1.20965670082534 0.1918462924921296 -0.4174342094103178
+82 1.306569861240723 0.4081890859478611 -0.4824924751602535
+83 1.222973879657449 0.2262967791144349 0.429334321634214
+84 1.804078223352526 0.7663783517359284 -0.1956990807188041
+85 1.57086252165904 1.155773912505517 -0.2174559627111528
+86 1.574823294802248 1.167621289969356 0.1948731313716022
+87 1.799061725861226 0.7760394771228164 0.1975920088688854
+88 1.736321519153448 0.6195058019401377 0.3633011133312876
+89 1.680111207596174 0.2053409065696982 0.4614111573472731
+90 1.585805226690124 0.4095866832898705 0.4806230171620565
+91 1.033769170861085 1.346420377509085 -0.4593379913029474
+92 1.686177954337897 0.2595089698405005 -0.4555780070333795
+93 0.9805825388808332 0.1961068189584932 0
+94 1.267064046804422 1.349831092695157 0.3557435462640699
+95 1.479801458923729 0.8235937501426396 0.4610179491301168
+96 1.806713163893442 0.41291149496386 0.3538099754989008
+97 1.660304769719565 0.8158013561953625 0.3571657671790813
+98 1.272522679585459 1.496027967309635 0.1862154784961512
+99 1.273193945142967 1.495820795927453 -0.1855231731675253
+100 1.262667463342245 1.368719575871037 -0.3447086919841124
+101 1.931859564293957 0.3540229855991108 0.1862154775733828
+102 1.934684648085233 0.3534998953881237 -0.1793804689566357
+103 1.044602110326715 0.9883306626754986 -0.496147496375881
+104 1.531662137703001 0.3834487538500693 -0.4937306551344234
+105 1.551796568960546 0.6139737231494753 0.4706294779813407
+106 1.581724214084509 0.9482897902504869 -0.3626575574879038
+107 1.096782839124086 0.35444480392985 -0.3596337488065346
+108 1.387291936348416 0.4077234230541955 0.497071738421552
+109 1.08924964123622 0.3634521195547355 0.355383901525496
+110 1.441115266930556 1.203974457102059 0.3274445044904424
+111 1.395080190203861 1.235707600010656 -0.3431511175769478
+112 1.133587364788978 1.610473031723204 -0.1721570661176043
+113 1.961520334484167 0.1716255200510413 0.1732789072467822
+114 1.961422513597609 0.1736228878529459 -0.1730685535438358
+115 1.82696920620189 0.4017949294223938 -0.3356092840765813
+116 1.131073087671027 1.61183028132059 0.1730685525791028
+117 1.580661987779667 0.9904905866376601 0.3413397946090942
+118 1.053798949591161 0.5492367868134354 0.3909838746147544
+119 0.7242610698965356 0.986066622565408 -0.4165714295663997
+120 1.776913423346694 0.6100558234668472 -0.3264522078233214
+121 0.976815381999997 0.3286770548430039 0.1723113781768278
+122 0.9684597721183544 0.3086573347446573 -0.1272227140227045
+123 1.598657272365354 0 -0.2601378477606267
+124 1.336856475131923 0 0.2435465079686098
+125 1.264366353304017 0 -0.1342496457412713
+126 1.708248762070371 0 0.1844923042412902
+127 1.770132970054707 0 -0.06658179719356594
+128 1.627974743938903 0 0.3374416670744554
+129 1.518381683403412 0 0.189367998345964
+130 1.388026045801723 0 0.02759910094603191
+131 1.57082733902861 0 -0.01324440776767719
+132 1.205770154876862 0 0.09185560579891296
+133 1.448748593218543 0 -0.1456951042254292
+134 1.386747123568126 0 -0.3059177475399098
+135 1.773887657153061 0 -0.2426432909807264
+136 1.855277436237921 0 0.08756802326740212
+137 1.456499046249551 0 0.3582625863623187
+138 0.7993286361826776 1.384477809813136 -0.2601378477606267
+139 0.6684282375659617 1.157751668677965 0.2435465079686098
+140 0.6321831766520087 1.09497338165157 -0.1342496457412713
+141 0.8541243810351855 1.47938682393626 0.1844923042412902
+142 0.885066485027354 1.532980120143776 -0.06658179719356594
+143 0.8139873719694519 1.409867484970557 0.3374416670744554
+144 0.7591908417017061 1.314957110468335 0.189367998345964
+145 0.6940130229008615 1.202065816778755 0.02759910094603191
+146 0.7854136695143054 1.360376380557887 -0.01324440776767719
+147 0.6028850774384313 1.04422758524846 0.09185560579891296
+148 0.7243742966092718 1.254653085424226 -0.1456951042254292
+149 0.6933735617840632 1.200958237634995 -0.3059177475399098
+150 0.8869438285765309 1.536231774554212 -0.2426432909807264
+151 0.9276387181189605 1.606717390850103 0.08756802326740212
+152 0.7282495231247758 1.261365174639917 0.3582625863623187
+153 1.411918130602168 0.496166241357142 -0.00157291651681768
+154 1.137417628047573 0.9720715936541285 -0.004558233820192492
+155 1.450923907048078 0.835854081603992 0.008303709726106997
+156 1.195281628759975 0.6892135661795487 0.1379139557683655
+157 1.667647997439524 0.3046155345731573 -0.01063985748379645
+158 1.095077566932522 1.30506262456116 0.008783669059526909
+159 1.449746181951948 0.2899752883377874 -0.2128942904574569
+160 1.425404656176498 0.2838646166858353 0.2090378240215375
+161 1.237422074642534 0.6934452933460877 -0.1740536126458837
+162 0.9265939870131148 1.089233825798041 0.146036662270623
+163 0.9141871309006279 1.035605401598253 -0.1819109563473867
+164 1.230559284884559 0.2965650718118637 -0.0489211473166503
+165 1.633700383529476 0.5873280387970766 -0.1078820007088586
+166 1.381887166693965 0.5422592078649243 0.2595533680109433
+167 1.628614112376115 0.5120437923677087 0.1405317590914031
+168 1.296790159421439 0.9296724900104604 0.2103100611208717
+169 1.43040652357321 0.5390860673292487 -0.2497801650023363
+170 1.187552760136346 0.4469311325853691 0.1497889600434833
+171 1.136555246481813 0.9253132713408032 -0.2607675417720481
+172 1.169875547734923 1.157176703594696 0.209783121723046
+173 1.252256239782287 1.161539312959809 -0.1396154626225087
+174 0.9854410532992778 1.295312212838649 0.232826117690098
+175 0.8998494176549744 0.8641570369932714 0.01526132714743774
+176 1.047849560177692 0.8788161627502054 0.2215760845022245
+177 1.402089433100673 1.077521710164203 0.04039637986273873
+178 1.03525188898502 1.249979305919021 -0.2281501268865102
+$EndNodes
+$Elements
+835
+1 2 2 1 1 10 112 1
+2 2 2 1 1 1 116 10
+3 2 2 1 1 11 116 1
+4 2 2 1 1 1 112 22
+5 2 2 1 1 3 113 2
+6 2 2 1 1 2 114 3
+7 2 2 1 1 2 113 23
+8 2 2 1 1 34 114 2
+9 2 2 1 1 4 101 3
+10 2 2 1 1 3 102 4
+11 2 2 1 1 101 113 3
+12 2 2 1 1 3 114 102
+13 2 2 1 1 4 43 5
+14 2 2 1 1 5 44 4
+15 2 2 1 1 4 102 43
+16 2 2 1 1 44 101 4
+17 2 2 1 1 5 84 6
+18 2 2 1 1 6 87 5
+19 2 2 1 1 43 84 5
+20 2 2 1 1 5 87 44
+21 2 2 1 1 7 39 6
+22 2 2 1 1 6 40 7
+23 2 2 1 1 39 87 6
+24 2 2 1 1 6 84 40
+25 2 2 1 1 7 85 8
+26 2 2 1 1 8 86 7
+27 2 2 1 1 7 86 39
+28 2 2 1 1 40 85 7
+29 2 2 1 1 9 41 8
+30 2 2 1 1 8 42 9
+31 2 2 1 1 41 86 8
+32 2 2 1 1 8 85 42
+33 2 2 1 1 10 98 9
+34 2 2 1 1 9 99 10
+35 2 2 1 1 9 98 41
+36 2 2 1 1 42 99 9
+37 2 2 1 1 10 116 98
+38 2 2 1 1 99 112 10
+39 2 2 1 1 23 50 24
+40 2 2 1 1 23 113 50
+41 2 2 1 1 24 89 25
+42 2 2 1 1 50 89 24
+43 2 2 1 1 25 37 26
+44 2 2 1 1 25 89 37
+45 2 2 1 1 26 83 27
+46 2 2 1 1 37 83 26
+47 2 2 1 1 27 46 28
+48 2 2 1 1 27 83 46
+49 2 2 1 1 28 93 29
+50 2 2 1 1 46 93 28
+51 2 2 1 1 29 47 30
+52 2 2 1 1 29 93 47
+53 2 2 1 1 30 81 31
+54 2 2 1 1 47 81 30
+55 2 2 1 1 31 38 32
+56 2 2 1 1 31 81 38
+57 2 2 1 1 32 92 33
+58 2 2 1 1 38 92 32
+59 2 2 1 1 33 51 34
+60 2 2 1 1 33 92 51
+61 2 2 1 1 51 114 34
+62 2 2 1 1 12 48 11
+63 2 2 1 1 48 116 11
+64 2 2 1 1 13 80 12
+65 2 2 1 1 12 80 48
+66 2 2 1 1 14 35 13
+67 2 2 1 1 35 80 13
+68 2 2 1 1 15 57 14
+69 2 2 1 1 14 57 35
+70 2 2 1 1 16 52 15
+71 2 2 1 1 52 57 15
+72 2 2 1 1 17 45 16
+73 2 2 1 1 45 52 16
+74 2 2 1 1 18 66 17
+75 2 2 1 1 17 66 45
+76 2 2 1 1 19 119 18
+77 2 2 1 1 18 119 66
+78 2 2 1 1 20 36 19
+79 2 2 1 1 36 119 19
+80 2 2 1 1 21 91 20
+81 2 2 1 1 20 91 36
+82 2 2 1 1 22 49 21
+83 2 2 1 1 49 91 21
+84 2 2 1 1 22 112 49
+85 2 2 1 1 57 58 35
+86 2 2 1 1 58 62 35
+87 2 2 1 1 62 80 35
+88 2 2 1 1 36 103 68
+89 2 2 1 1 68 119 36
+90 2 2 1 1 36 91 76
+91 2 2 1 1 76 103 36
+92 2 2 1 1 37 108 83
+93 2 2 1 1 89 90 37
+94 2 2 1 1 90 108 37
+95 2 2 1 1 81 82 38
+96 2 2 1 1 82 104 38
+97 2 2 1 1 38 104 92
+98 2 2 1 1 86 117 39
+99 2 2 1 1 39 97 87
+100 2 2 1 1 39 117 97
+101 2 2 1 1 40 84 77
+102 2 2 1 1 77 106 40
+103 2 2 1 1 40 106 85
+104 2 2 1 1 41 110 86
+105 2 2 1 1 41 98 94
+106 2 2 1 1 94 110 41
+107 2 2 1 1 85 111 42
+108 2 2 1 1 42 100 99
+109 2 2 1 1 42 111 100
+110 2 2 1 1 43 120 84
+111 2 2 1 1 102 115 43
+112 2 2 1 1 115 120 43
+113 2 2 1 1 87 88 44
+114 2 2 1 1 88 96 44
+115 2 2 1 1 96 101 44
+116 2 2 1 1 45 53 52
+117 2 2 1 1 45 55 53
+118 2 2 1 1 45 66 55
+119 2 2 1 1 83 109 46
+120 2 2 1 1 46 121 93
+121 2 2 1 1 109 121 46
+122 2 2 1 1 79 107 47
+123 2 2 1 1 47 122 79
+124 2 2 1 1 47 107 81
+125 2 2 1 1 93 122 47
+126 2 2 1 1 80 94 48
+127 2 2 1 1 94 98 48
+128 2 2 1 1 98 116 48
+129 2 2 1 1 49 100 91
+130 2 2 1 1 99 100 49
+131 2 2 1 1 49 112 99
+132 2 2 1 1 50 96 89
+133 2 2 1 1 50 101 96
+134 2 2 1 1 50 113 101
+135 2 2 1 1 92 115 51
+136 2 2 1 1 102 114 51
+137 2 2 1 1 51 115 102
+138 2 2 1 1 53 54 52
+139 2 2 1 1 54 57 52
+140 2 2 1 1 53 65 54
+141 2 2 1 1 55 56 53
+142 2 2 1 1 56 65 53
+143 2 2 1 1 54 58 57
+144 2 2 1 1 54 59 58
+145 2 2 1 1 54 118 59
+146 2 2 1 1 65 118 54
+147 2 2 1 1 55 79 56
+148 2 2 1 1 66 67 55
+149 2 2 1 1 67 79 55
+150 2 2 1 1 56 121 65
+151 2 2 1 1 79 122 56
+152 2 2 1 1 93 121 56
+153 2 2 1 1 56 122 93
+154 2 2 1 1 59 60 58
+155 2 2 1 1 60 62 58
+156 2 2 1 1 59 61 60
+157 2 2 1 1 59 63 61
+158 2 2 1 1 59 118 63
+159 2 2 1 1 61 95 60
+160 2 2 1 1 60 64 62
+161 2 2 1 1 60 95 64
+162 2 2 1 1 63 108 61
+163 2 2 1 1 61 105 95
+164 2 2 1 1 61 108 105
+165 2 2 1 1 64 110 62
+166 2 2 1 1 62 94 80
+167 2 2 1 1 62 110 94
+168 2 2 1 1 83 108 63
+169 2 2 1 1 63 109 83
+170 2 2 1 1 63 118 109
+171 2 2 1 1 95 117 64
+172 2 2 1 1 64 117 110
+173 2 2 1 1 109 118 65
+174 2 2 1 1 65 121 109
+175 2 2 1 1 66 68 67
+176 2 2 1 1 66 119 68
+177 2 2 1 1 68 69 67
+178 2 2 1 1 69 70 67
+179 2 2 1 1 70 79 67
+180 2 2 1 1 68 103 69
+181 2 2 1 1 69 71 70
+182 2 2 1 1 69 72 71
+183 2 2 1 1 69 103 72
+184 2 2 1 1 71 82 70
+185 2 2 1 1 70 107 79
+186 2 2 1 1 82 107 70
+187 2 2 1 1 72 73 71
+188 2 2 1 1 73 74 71
+189 2 2 1 1 74 82 71
+190 2 2 1 1 72 75 73
+191 2 2 1 1 72 76 75
+192 2 2 1 1 72 103 76
+193 2 2 1 1 73 77 74
+194 2 2 1 1 75 106 73
+195 2 2 1 1 73 106 77
+196 2 2 1 1 77 78 74
+197 2 2 1 1 78 104 74
+198 2 2 1 1 74 104 82
+199 2 2 1 1 76 111 75
+200 2 2 1 1 85 106 75
+201 2 2 1 1 75 111 85
+202 2 2 1 1 91 100 76
+203 2 2 1 1 100 111 76
+204 2 2 1 1 77 120 78
+205 2 2 1 1 84 120 77
+206 2 2 1 1 92 104 78
+207 2 2 1 1 78 115 92
+208 2 2 1 1 78 120 115
+209 2 2 1 1 81 107 82
+210 2 2 1 1 110 117 86
+211 2 2 1 1 87 97 88
+212 2 2 1 1 90 96 88
+213 2 2 1 1 88 105 90
+214 2 2 1 1 97 105 88
+215 2 2 1 1 89 96 90
+216 2 2 1 1 105 108 90
+217 2 2 1 1 95 105 97
+218 2 2 1 1 97 117 95
+219 2 2 2 2 23 136 2
+220 2 2 2 2 2 127 34
+221 2 2 2 2 2 136 127
+222 2 2 2 2 24 126 23
+223 2 2 2 2 126 136 23
+224 2 2 2 2 25 128 24
+225 2 2 2 2 24 128 126
+226 2 2 2 2 26 137 25
+227 2 2 2 2 25 137 128
+228 2 2 2 2 27 124 26
+229 2 2 2 2 124 137 26
+230 2 2 2 2 28 132 27
+231 2 2 2 2 27 132 124
+232 2 2 2 2 29 132 28
+233 2 2 2 2 30 125 29
+234 2 2 2 2 125 132 29
+235 2 2 2 2 31 134 30
+236 2 2 2 2 30 134 125
+237 2 2 2 2 32 134 31
+238 2 2 2 2 33 123 32
+239 2 2 2 2 123 134 32
+240 2 2 2 2 34 135 33
+241 2 2 2 2 33 135 123
+242 2 2 2 2 127 135 34
+243 2 2 2 2 127 131 123
+244 2 2 2 2 123 135 127
+245 2 2 2 2 131 133 123
+246 2 2 2 2 133 134 123
+247 2 2 2 2 124 130 129
+248 2 2 2 2 129 137 124
+249 2 2 2 2 124 132 130
+250 2 2 2 2 130 132 125
+251 2 2 2 2 125 133 130
+252 2 2 2 2 125 134 133
+253 2 2 2 2 126 131 127
+254 2 2 2 2 127 136 126
+255 2 2 2 2 128 129 126
+256 2 2 2 2 129 131 126
+257 2 2 2 2 128 137 129
+258 2 2 2 2 130 131 129
+259 2 2 2 2 130 133 131
+260 2 2 3 3 11 1 151
+261 2 2 3 3 1 22 142
+262 2 2 3 3 1 142 151
+263 2 2 3 3 12 11 141
+264 2 2 3 3 141 11 151
+265 2 2 3 3 13 12 143
+266 2 2 3 3 12 141 143
+267 2 2 3 3 14 13 152
+268 2 2 3 3 13 143 152
+269 2 2 3 3 15 14 139
+270 2 2 3 3 139 14 152
+271 2 2 3 3 16 15 147
+272 2 2 3 3 15 139 147
+273 2 2 3 3 17 16 147
+274 2 2 3 3 18 17 140
+275 2 2 3 3 140 17 147
+276 2 2 3 3 19 18 149
+277 2 2 3 3 18 140 149
+278 2 2 3 3 20 19 149
+279 2 2 3 3 21 20 138
+280 2 2 3 3 138 20 149
+281 2 2 3 3 22 21 150
+282 2 2 3 3 21 138 150
+283 2 2 3 3 142 22 150
+284 2 2 3 3 142 138 146
+285 2 2 3 3 138 142 150
+286 2 2 3 3 146 138 148
+287 2 2 3 3 148 138 149
+288 2 2 3 3 139 144 145
+289 2 2 3 3 144 139 152
+290 2 2 3 3 139 145 147
+291 2 2 3 3 145 140 147
+292 2 2 3 3 140 145 148
+293 2 2 3 3 140 148 149
+294 2 2 3 3 141 142 146
+295 2 2 3 3 142 141 151
+296 2 2 3 3 143 141 144
+297 2 2 3 3 144 141 146
+298 2 2 3 3 143 144 152
+299 2 2 3 3 145 144 146
+300 2 2 3 3 145 146 148
+301 4 2 1 1 106 161 155 171
+302 4 2 1 1 50 157 126 160
+303 4 2 1 1 56 161 156 170
+304 4 2 1 1 157 159 130 160
+305 4 2 1 1 50 96 157 160
+306 4 2 1 1 106 73 161 171
+307 4 2 1 1 106 155 75 171
+308 4 2 1 1 75 171 155 173
+309 4 2 1 1 126 129 128 160
+310 4 2 1 1 79 164 161 170
+311 4 2 1 1 55 156 56 161
+312 4 2 1 1 130 160 159 164
+313 4 2 1 1 56 79 161 170
+314 4 2 1 1 85 173 155 177
+315 4 2 1 1 126 128 89 160
+316 4 2 1 1 97 166 155 167
+317 4 2 1 1 106 75 73 171
+318 4 2 1 1 115 159 157 165
+319 4 2 1 1 50 126 89 160
+320 4 2 1 1 96 157 160 167
+321 4 2 1 1 75 155 85 173
+322 4 2 1 1 156 161 55 175
+323 4 2 1 1 50 89 96 160
+324 4 2 1 1 131 130 157 159
+325 4 2 1 1 75 106 85 155
+326 4 2 1 1 155 77 165 169
+327 4 2 1 1 115 78 159 165
+328 4 2 1 1 56 164 79 170
+329 4 2 1 1 106 77 155 169
+330 4 2 1 1 137 128 129 160
+331 4 2 1 1 106 73 77 169
+332 4 2 1 1 131 157 130 160
+333 4 2 1 1 107 161 164 169
+334 4 2 1 1 87 155 6 165
+335 4 2 1 1 87 155 165 167
+336 4 2 1 1 131 133 130 159
+337 4 2 1 1 97 155 87 167
+338 4 2 1 1 153 159 157 160
+339 4 2 1 1 161 164 153 170
+340 4 2 1 1 129 126 157 160
+341 4 2 1 1 56 122 79 164
+342 4 2 1 1 145 148 146 163
+343 4 2 1 1 79 55 56 161
+344 4 2 1 1 137 89 128 160
+345 4 2 1 1 159 160 153 164
+346 4 2 1 1 107 161 79 164
+347 4 2 1 1 127 51 135 157
+348 4 2 1 1 55 53 56 156
+349 4 2 1 1 159 165 78 169
+350 4 2 1 1 130 159 133 164
+351 4 2 1 1 107 164 159 169
+352 4 2 1 1 55 53 156 175
+353 4 2 1 1 61 166 156 168
+354 4 2 1 1 114 135 127 51
+355 4 2 1 1 67 161 171 175
+356 4 2 1 1 127 135 123 157
+357 4 2 1 1 61 95 166 168
+358 4 2 1 1 148 146 163 178
+359 4 2 1 1 156 166 155 168
+360 4 2 1 1 136 126 113 157
+361 4 2 1 1 153 155 166 167
+362 4 2 1 1 87 6 5 165
+363 4 2 1 1 153 164 161 169
+364 4 2 1 1 9 41 158 177
+365 4 2 1 1 129 157 131 160
+366 4 2 1 1 67 55 161 175
+367 4 2 1 1 101 50 96 157
+368 4 2 1 1 156 161 153 170
+369 4 2 1 1 155 171 154 173
+370 4 2 1 1 46 160 164 170
+371 4 2 1 1 59 61 156 168
+372 4 2 1 1 3 136 113 157
+373 4 2 1 1 150 49 112 178
+374 4 2 1 1 115 157 43 165
+375 4 2 1 1 158 162 146 163
+376 4 2 1 1 97 39 87 155
+377 4 2 1 1 145 146 162 163
+378 4 2 1 1 102 43 115 157
+379 4 2 1 1 117 155 97 168
+380 4 2 1 1 154 155 173 177
+381 4 2 1 1 155 156 153 166
+382 4 2 1 1 77 40 155 165
+383 4 2 1 1 129 131 130 160
+384 4 2 1 1 9 158 173 177
+385 4 2 1 1 158 163 146 178
+386 4 2 1 1 150 112 142 178
+387 4 2 1 1 154 161 156 175
+388 4 2 1 1 41 172 158 177
+389 4 2 1 1 133 131 123 159
+390 4 2 1 1 126 50 113 157
+391 4 2 1 1 117 97 95 168
+392 4 2 1 1 56 156 65 170
+393 4 2 1 1 3 127 136 157
+394 4 2 1 1 125 130 133 164
+395 4 2 1 1 101 113 50 157
+396 4 2 1 1 51 102 115 157
+397 4 2 1 1 125 159 81 164
+398 4 2 1 1 153 155 165 169
+399 4 2 1 1 153 165 155 167
+400 4 2 1 1 87 165 5 167
+401 4 2 1 1 87 39 6 155
+402 4 2 1 1 37 89 137 160
+403 4 2 1 1 107 70 161 169
+404 4 2 1 1 46 160 132 164
+405 4 2 1 1 40 106 77 155
+406 4 2 1 1 125 133 159 164
+407 4 2 1 1 67 171 163 175
+408 4 2 1 1 59 60 61 168
+409 4 2 1 1 131 157 123 159
+410 4 2 1 1 127 114 51 157
+411 4 2 1 1 118 156 59 166
+412 4 2 1 1 117 39 97 155
+413 4 2 1 1 116 141 151 158
+414 4 2 1 1 101 157 96 167
+415 4 2 1 1 72 161 73 171
+416 4 2 1 1 53 65 56 156
+417 4 2 1 1 67 55 79 161
+418 4 2 1 1 96 160 90 167
+419 4 2 1 1 126 131 129 157
+420 4 2 1 1 43 157 4 165
+421 4 2 1 1 105 166 97 167
+422 4 2 1 1 157 159 153 165
+423 4 2 1 1 153 156 155 161
+424 4 2 1 1 59 156 61 166
+425 4 2 1 1 155 161 154 171
+426 4 2 1 1 107 159 82 169
+427 4 2 1 1 72 73 75 171
+428 4 2 1 1 107 81 159 164
+429 4 2 1 1 145 140 163 175
+430 4 2 1 1 95 97 105 166
+431 4 2 1 1 153 161 155 169
+432 4 2 1 1 156 166 118 170
+433 4 2 1 1 115 78 92 159
+434 4 2 1 1 10 116 151 158
+435 4 2 1 1 63 166 160 170
+436 4 2 1 1 96 89 90 160
+437 4 2 1 1 43 102 4 157
+438 4 2 1 1 124 132 46 160
+439 4 2 1 1 102 51 114 157
+440 4 2 1 1 4 165 157 167
+441 4 2 1 1 124 46 83 160
+442 4 2 1 1 145 163 162 175
+443 4 2 1 1 130 132 160 164
+444 4 2 1 1 61 60 95 168
+445 4 2 1 1 85 106 40 155
+446 4 2 1 1 154 171 161 175
+447 4 2 1 1 151 142 10 158
+448 4 2 1 1 83 160 46 170
+449 4 2 1 1 137 124 83 160
+450 4 2 1 1 47 125 81 164
+451 4 2 1 1 136 127 126 157
+452 4 2 1 1 107 70 79 161
+453 4 2 1 1 85 155 7 177
+454 4 2 1 1 9 41 98 158
+455 4 2 1 1 77 84 40 165
+456 4 2 1 1 39 155 117 177
+457 4 2 1 1 58 162 172 176
+458 4 2 1 1 5 165 4 167
+459 4 2 1 1 104 159 78 169
+460 4 2 1 1 9 8 41 177
+461 4 2 1 1 59 168 156 176
+462 4 2 1 1 98 158 41 172
+463 4 2 1 1 155 156 154 161
+464 4 2 1 1 78 115 120 165
+465 4 2 1 1 112 158 142 178
+466 4 2 1 1 109 83 46 170
+467 4 2 1 1 112 99 158 178
+468 4 2 1 1 112 49 99 178
+469 4 2 1 1 125 134 81 159
+470 4 2 1 1 63 118 59 166
+471 4 2 1 1 101 4 157 167
+472 4 2 1 1 154 162 158 163
+473 4 2 1 1 156 175 53 176
+474 4 2 1 1 71 73 72 161
+475 4 2 1 1 123 131 127 157
+476 4 2 1 1 155 168 117 177
+477 4 2 1 1 153 165 159 169
+478 4 2 1 1 137 83 37 160
+479 4 2 1 1 73 161 71 169
+480 4 2 1 1 66 67 163 175
+481 4 2 1 1 9 173 8 177
+482 4 2 1 1 46 164 121 170
+483 4 2 1 1 153 159 164 169
+484 4 2 1 1 48 116 98 174
+485 4 2 1 1 88 105 97 167
+486 4 2 1 1 116 158 98 174
+487 4 2 1 1 139 57 52 162
+488 4 2 1 1 160 166 90 167
+489 4 2 1 1 103 163 171 178
+490 4 2 1 1 145 147 140 175
+491 4 2 1 1 153 160 157 167
+492 4 2 1 1 104 92 78 159
+493 4 2 1 1 140 66 163 175
+494 4 2 1 1 101 44 4 167
+495 4 2 1 1 124 130 132 160
+496 4 2 1 1 113 101 3 157
+497 4 2 1 1 158 172 98 174
+498 4 2 1 1 154 163 158 178
+499 4 2 1 1 58 162 35 172
+500 4 2 1 1 67 68 163 171
+501 4 2 1 1 141 158 116 174
+502 4 2 1 1 118 166 63 170
+503 4 2 1 1 152 162 144 174
+504 4 2 1 1 3 114 127 157
+505 4 2 1 1 107 82 70 169
+506 4 2 1 1 120 115 43 165
+507 4 2 1 1 107 47 81 164
+508 4 2 1 1 108 160 63 166
+509 4 2 1 1 85 40 7 155
+510 4 2 1 1 35 162 152 174
+511 4 2 1 1 172 173 158 177
+512 4 2 1 1 139 52 147 162
+513 4 2 1 1 44 5 4 167
+514 4 2 1 1 107 81 82 159
+515 4 2 1 1 78 165 120 169
+516 4 2 1 1 6 84 5 165
+517 4 2 1 1 144 152 139 162
+518 4 2 1 1 54 156 53 176
+519 4 2 1 1 35 172 162 174
+520 4 2 1 1 98 172 94 174
+521 4 2 1 1 99 9 158 173
+522 4 2 1 1 7 6 39 155
+523 4 2 1 1 140 45 66 175
+524 4 2 1 1 151 141 142 158
+525 4 2 1 1 124 129 130 160
+526 4 2 1 1 5 43 4 165
+527 4 2 1 1 54 65 53 156
+528 4 2 1 1 145 162 147 175
+529 4 2 1 1 69 67 161 171
+530 4 2 1 1 129 124 137 160
+531 4 2 1 1 86 39 117 177
+532 4 2 1 1 148 145 140 163
+533 4 2 1 1 102 114 3 157
+534 4 2 1 1 143 35 152 174
+535 4 2 1 1 154 156 155 168
+536 4 2 1 1 149 148 163 178
+537 4 2 1 1 70 67 79 161
+538 4 2 1 1 153 164 160 170
+539 4 2 1 1 54 59 118 156
+540 4 2 1 1 7 155 39 177
+541 4 2 1 1 101 4 3 157
+542 4 2 1 1 59 60 168 176
+543 4 2 1 1 152 139 57 14
+544 4 2 1 1 125 133 134 159
+545 4 2 1 1 99 173 158 178
+546 4 2 1 1 56 121 164 170
+547 4 2 1 1 142 138 150 178
+548 4 2 1 1 146 162 158 174
+549 4 2 1 1 99 42 9 173
+550 4 2 1 1 112 10 142 158
+551 4 2 1 1 58 57 162 176
+552 4 2 1 1 49 150 91 178
+553 4 2 1 1 144 162 146 174
+554 4 2 1 1 4 102 3 157
+555 4 2 1 1 46 93 121 164
+556 4 2 1 1 38 123 92 159
+557 4 2 1 1 103 36 163 178
+558 4 2 1 1 83 63 108 160
+559 4 2 1 1 65 156 118 170
+560 4 2 1 1 68 66 67 163
+561 4 2 1 1 147 45 140 175
+562 4 2 1 1 54 118 65 156
+563 4 2 1 1 154 158 173 178
+564 4 2 1 1 128 137 25 89
+565 4 2 1 1 71 161 72 171
+566 4 2 1 1 52 175 162 176
+567 4 2 1 1 48 98 94 174
+568 4 2 1 1 69 67 70 161
+569 4 2 1 1 52 162 57 176
+570 4 2 1 1 103 171 76 178
+571 4 2 1 1 123 38 134 159
+572 4 2 1 1 142 158 146 178
+573 4 2 1 1 86 117 168 177
+574 4 2 1 1 98 41 94 172
+575 4 2 1 1 131 126 127 157
+576 4 2 1 1 88 97 87 167
+577 4 2 1 1 112 99 10 158
+578 4 2 1 1 56 93 122 164
+579 4 2 1 1 9 42 8 173
+580 4 2 1 1 91 150 138 178
+581 4 2 1 1 147 162 52 175
+582 4 2 1 1 99 100 173 178
+583 4 2 1 1 75 76 171 173
+584 4 2 1 1 138 146 148 178
+585 4 2 1 1 10 99 9 158
+586 4 2 1 1 157 165 153 167
+587 4 2 1 1 144 145 146 162
+588 4 2 1 1 77 120 165 169
+589 4 2 1 1 149 138 148 178
+590 4 2 1 1 137 26 83 124
+591 4 2 1 1 149 163 36 178
+592 4 2 1 1 87 5 44 167
+593 4 2 1 1 153 166 160 167
+594 4 2 1 1 154 173 163 178
+595 4 2 1 1 71 69 161 171
+596 4 2 1 1 70 71 161 169
+597 4 2 1 1 153 160 166 170
+598 4 2 1 1 154 175 156 176
+599 4 2 1 1 61 95 105 166
+600 4 2 1 1 48 141 116 174
+601 4 2 1 1 10 98 116 158
+602 4 2 1 1 66 55 67 175
+603 4 2 1 1 90 166 105 167
+604 4 2 1 1 56 121 93 164
+605 4 2 1 1 156 153 166 170
+606 4 2 1 1 9 98 10 158
+607 4 2 1 1 7 40 6 155
+608 4 2 1 1 58 57 35 162
+609 4 2 1 1 163 173 171 178
+610 4 2 1 1 46 132 93 164
+611 4 2 1 1 111 75 85 173
+612 4 2 1 1 139 57 15 52
+613 4 2 1 1 171 173 76 178
+614 4 2 1 1 62 58 35 172
+615 4 2 1 1 29 125 47 164
+616 4 2 1 1 80 172 35 174
+617 4 2 1 1 17 66 140 45
+618 4 2 1 1 77 78 120 169
+619 4 2 1 1 138 142 146 178
+620 4 2 1 1 119 140 66 163
+621 4 2 1 1 90 89 37 160
+622 4 2 1 1 86 7 39 177
+623 4 2 1 1 73 71 74 169
+624 4 2 1 1 134 38 81 159
+625 4 2 1 1 149 36 138 178
+626 4 2 1 1 59 156 54 176
+627 4 2 1 1 154 171 163 173
+628 4 2 1 1 117 110 86 168
+629 4 2 1 1 63 59 61 166
+630 4 2 1 1 123 134 133 159
+631 4 2 1 1 141 146 158 174
+632 4 2 1 1 75 111 76 173
+633 4 2 1 1 86 168 110 177
+634 4 2 1 1 36 91 138 178
+635 4 2 1 1 82 159 104 169
+636 4 2 1 1 93 29 47 164
+637 4 2 1 1 158 172 154 173
+638 4 2 1 1 81 47 30 125
+639 4 2 1 1 37 26 83 137
+640 4 2 1 1 52 53 175 176
+641 4 2 1 1 154 173 172 177
+642 4 2 1 1 146 142 141 158
+643 4 2 1 1 90 160 108 166
+644 4 2 1 1 70 82 71 169
+645 4 2 1 1 163 171 154 175
+646 4 2 1 1 44 101 96 167
+647 4 2 1 1 73 74 77 169
+648 4 2 1 1 158 162 154 172
+649 4 2 1 1 152 57 35 14
+650 4 2 1 1 71 72 69 171
+651 4 2 1 1 143 152 144 174
+652 4 2 1 1 62 60 58 168
+653 4 2 1 1 92 51 135 33
+654 4 2 1 1 48 143 141 174
+655 4 2 1 1 91 49 21 150
+656 4 2 1 1 42 85 8 173
+657 4 2 1 1 99 49 100 178
+658 4 2 1 1 130 125 132 164
+659 4 2 1 1 41 110 172 177
+660 4 2 1 1 15 147 139 52
+661 4 2 1 1 64 117 95 168
+662 4 2 1 1 13 143 35 152
+663 4 2 1 1 136 3 2 127
+664 4 2 1 1 58 168 60 176
+665 4 2 1 1 103 76 36 178
+666 4 2 1 1 10 1 142 151
+667 4 2 1 1 64 110 168 172
+668 4 2 1 1 138 91 21 150
+669 4 2 1 1 52 54 53 176
+670 4 2 1 1 38 82 81 159
+671 4 2 1 1 67 69 68 171
+672 4 2 1 1 138 91 36 20
+673 4 2 1 1 92 135 123 33
+674 4 2 1 1 48 80 143 174
+675 4 2 1 1 8 173 85 177
+676 4 2 1 1 27 46 83 124
+677 4 2 1 1 70 71 69 161
+678 4 2 1 1 103 163 68 171
+679 4 2 1 1 119 149 140 163
+680 4 2 1 1 147 17 140 45
+681 4 2 1 1 137 37 25 89
+682 4 2 1 1 168 172 110 177
+683 4 2 1 1 46 121 109 170
+684 4 2 1 1 149 119 36 163
+685 4 2 1 1 154 168 155 177
+686 4 2 1 1 64 168 62 172
+687 4 2 1 1 38 92 104 159
+688 4 2 1 1 109 118 63 170
+689 4 2 1 1 59 58 60 176
+690 4 2 1 1 75 76 72 171
+691 4 2 1 1 38 104 82 159
+692 4 2 1 1 99 100 42 173
+693 4 2 1 1 143 80 35 174
+694 4 2 1 1 162 163 154 175
+695 4 2 1 1 138 36 149 20
+696 4 2 1 1 117 64 110 168
+697 4 2 1 1 36 68 103 163
+698 4 2 1 1 90 105 88 167
+699 4 2 1 1 80 62 35 172
+700 4 2 1 1 139 145 144 162
+701 4 2 1 1 147 52 45 175
+702 4 2 1 1 141 144 146 174
+703 4 2 1 1 56 65 121 170
+704 4 2 1 1 108 90 37 160
+705 4 2 1 1 134 81 30 125
+706 4 2 1 1 154 156 168 176
+707 4 2 1 1 148 140 149 163
+708 4 2 1 1 123 32 38 92
+709 4 2 1 1 116 11 151 141
+710 4 2 1 1 119 18 66 140
+711 4 2 1 1 113 136 23 126
+712 4 2 1 1 83 108 37 160
+713 4 2 1 1 145 139 147 162
+714 4 2 1 1 96 90 88 167
+715 4 2 1 1 68 36 119 163
+716 4 2 1 1 76 173 100 178
+717 4 2 1 1 64 62 110 172
+718 4 2 1 1 64 60 62 168
+719 4 2 1 1 64 95 60 168
+720 4 2 1 1 12 143 141 48
+721 4 2 1 1 74 104 78 169
+722 4 2 1 1 48 12 143 80
+723 4 2 1 1 154 172 168 177
+724 4 2 1 1 52 57 54 176
+725 4 2 1 1 77 74 78 169
+726 4 2 1 1 119 66 68 163
+727 4 2 1 1 43 5 84 165
+728 4 2 1 1 162 154 172 176
+729 4 2 1 1 77 120 84 165
+730 4 2 1 1 38 32 123 134
+731 4 2 1 1 47 122 93 164
+732 4 2 1 1 42 111 85 173
+733 4 2 1 1 100 76 111 173
+734 4 2 1 1 66 45 55 175
+735 4 2 1 1 127 3 2 114
+736 4 2 1 1 162 172 158 174
+737 4 2 1 1 168 172 154 176
+738 4 2 1 1 41 86 110 177
+739 4 2 1 1 109 65 118 170
+740 4 2 1 1 1 10 142 112
+741 4 2 1 1 87 44 88 167
+742 4 2 1 1 74 71 82 169
+743 4 2 1 1 41 8 86 177
+744 4 2 1 1 46 27 132 124
+745 4 2 1 1 43 84 120 165
+746 4 2 1 1 13 143 80 35
+747 4 2 1 1 8 85 7 177
+748 4 2 1 1 82 104 74 169
+749 4 2 1 1 91 36 76 178
+750 4 2 1 1 103 72 76 171
+751 4 2 1 1 80 94 172 174
+752 4 2 1 1 132 93 28 46
+753 4 2 1 1 116 11 141 48
+754 4 2 1 1 126 23 113 50
+755 4 2 1 1 94 41 110 172
+756 4 2 1 1 69 103 68 171
+757 4 2 1 1 45 147 16 52
+758 4 2 1 1 26 27 83 124
+759 4 2 1 1 80 48 94 174
+760 4 2 1 1 15 57 139 14
+761 4 2 1 1 24 23 126 50
+762 4 2 1 1 90 108 105 166
+763 4 2 1 1 91 100 49 178
+764 4 2 1 1 38 81 31 134
+765 4 2 1 1 105 108 61 166
+766 4 2 1 1 150 22 142 112
+767 4 2 1 1 138 21 91 20
+768 4 2 1 1 59 54 58 176
+769 4 2 1 1 114 127 135 34
+770 4 2 1 1 2 3 136 113
+771 4 2 1 1 1 10 116 151
+772 4 2 1 1 154 162 175 176
+773 4 2 1 1 119 149 18 140
+774 4 2 1 1 17 147 16 45
+775 4 2 1 1 61 108 63 166
+776 4 2 1 1 29 30 47 125
+777 4 2 1 1 92 123 32 33
+778 4 2 1 1 76 100 91 178
+779 4 2 1 1 93 28 29 132
+780 4 2 1 1 121 65 109 170
+781 4 2 1 1 58 54 57 176
+782 4 2 1 1 100 111 42 173
+783 4 2 1 1 140 18 66 17
+784 4 2 1 1 21 49 22 150
+785 4 2 1 1 8 7 86 177
+786 4 2 1 1 94 110 62 172
+787 4 2 1 1 12 141 11 48
+788 4 2 1 1 96 88 44 167
+789 4 2 1 1 80 94 62 172
+790 4 2 1 1 49 22 150 112
+791 4 2 1 1 45 53 55 175
+792 4 2 1 1 135 114 34 51
+793 4 2 1 1 45 52 53 175
+794 4 2 1 1 13 143 12 80
+795 4 2 1 1 22 1 142 112
+796 4 2 1 1 37 25 26 137
+797 4 2 1 1 114 2 127 34
+798 4 2 1 1 19 149 36 20
+799 4 2 1 1 81 30 31 134
+800 4 2 1 1 119 19 149 36
+801 4 2 1 1 69 72 103 171
+802 4 2 1 1 15 16 147 52
+803 4 2 1 1 128 25 24 89
+804 4 2 1 1 51 34 135 33
+805 4 2 1 1 144 141 143 174
+806 4 2 1 1 11 1 116 151
+807 4 2 1 1 38 31 32 134
+808 4 2 1 1 136 23 2 113
+809 4 2 1 1 28 27 132 46
+810 4 2 1 1 35 13 152 14
+811 4 2 1 1 149 18 19 119
+812 4 2 1 1 161 106 169 73
+813 4 2 1 1 161 169 106 155
+814 4 2 1 1 168 97 166 155
+815 4 2 1 1 168 166 97 95
+816 4 2 1 1 159 51 115 157
+817 4 2 1 1 159 115 51 92
+818 4 2 1 1 165 40 6 84
+819 4 2 1 1 165 6 40 155
+820 4 2 1 1 126 89 24 50
+821 4 2 1 1 24 89 126 128
+822 4 2 1 1 170 83 63 109
+823 4 2 1 1 170 63 83 160
+824 4 2 1 1 135 92 159 51
+825 4 2 1 1 159 92 135 123
+826 4 2 1 1 159 157 135 51
+827 4 2 1 1 135 157 159 123
+828 4 2 1 1 152 57 162 35
+829 4 2 1 1 152 162 57 139
+830 4 2 1 1 164 29 132 93
+831 4 2 1 1 164 132 29 125
+832 4 2 1 1 172 58 168 62
+833 4 2 1 1 172 168 58 176
+834 4 2 1 1 79 47 164 122
+835 4 2 1 1 79 164 47 107
+$EndElements
+$Periodic
+1
+2 3 2
+Affine 0.5000000000000001 -0.8660254037844386 0 0 0.8660254037844386 0.5000000000000001 0 0 0 0 1 0 0 0 0 1
+28
+152 137
+141 126
+147 132
+150 135
+149 134
+145 130
+139 124
+144 129
+143 128
+140 125
+138 123
+148 133
+142 127
+151 136
+146 131
+11 23
+12 24
+13 25
+16 28
+14 26
+17 29
+15 27
+18 30
+21 33
+19 31
+22 34
+20 32
+1 2
+$EndPeriodic
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 8a48e4045ae..57ef3f97ab2 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -16,36 +16,21 @@ if (DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CodeDocumentation.conf.in
${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.conf @ONLY)
- if (UNIX)
- # Only create symlinks if UNIX operating system
- add_custom_target(doc
- COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.conf
- COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
- COMMAND ${CMAKE_COMMAND} -E create_symlink
- ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation/html/index.html
- ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
- BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation/html/index.html
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- COMMENT "Generating API documentation with Doxygen to CodeDocumentation.html"
- VERBATIM)
- add_custom_target(clean-doc
- COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
- COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation
- COMMENT "Removing API documentation"
- VERBATIM)
+ add_custom_target(doc
+ COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.conf
+ COMMAND echo "" > ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
+ BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation/html/index.html
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ COMMENT "Generating API documentation with Doxygen to CodeDocumentation.html"
+ VERBATIM)
+
+ add_custom_target(clean-doc
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.html
+ COMMAND ${CMAKE_COMMAND} -E remove -f ${CMAKE_CURRENT_BINARY_DIR}/warnings.log
+ COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation
+ COMMENT "Removing API documentation"
+ VERBATIM)
- else (UNIX)
- add_custom_target(doc
- COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation.conf
- BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation/html/index.html
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- COMMENT "Generating API documentation with Doxygen to CodeDocumentation/html/index.html"
- VERBATIM)
- add_custom_target(clean-doc
- COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/CodeDocumentation
- COMMENT "Removing API documentation"
- VERBATIM)
- endif (UNIX)
endif (DOXYGEN_FOUND)
diff --git a/doc/CodeDocumentation.conf.in b/doc/CodeDocumentation.conf.in
index 8b2f2284063..20bd4d2b104 100644
--- a/doc/CodeDocumentation.conf.in
+++ b/doc/CodeDocumentation.conf.in
@@ -51,7 +51,7 @@ PROJECT_BRIEF = "Finite element discretization library"
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
-PROJECT_LOGO =
+PROJECT_LOGO = web/logo-small.png
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
@@ -746,7 +746,7 @@ WARN_FORMAT = "$file:$line: $text"
# messages should be written. If left blank the output is written to standard
# error (stderr).
-WARN_LOGFILE =
+WARN_LOGFILE = warnings.log
#---------------------------------------------------------------------------
# Configuration options related to the input files
@@ -1470,7 +1470,7 @@ MATHJAX_FORMAT = HTML-CSS
# The default value is: http://cdn.mathjax.org/mathjax/latest.
# This tag requires that the tag USE_MATHJAX is set to YES.
-MATHJAX_RELPATH = https://cdn.llnl.gov/mathjax/2.7.2
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
diff --git a/doc/makefile b/doc/makefile
index f8e111f1f3e..9a6ff290352 100644
--- a/doc/makefile
+++ b/doc/makefile
@@ -9,18 +9,25 @@
# terms of the BSD-3 license. We welcome feedback and contributions, see file
# CONTRIBUTING.md for details.
+SHELL = /bin/bash
MFEM_DIR ?= ..
DOXYGEN_CONF = CodeDocumentation.conf
+
# doxygen uses: graphviz, latex
html: $(DOXYGEN_CONF)
- doxygen $(DOXYGEN_CONF)
- rm -f CodeDocumentation.html
- ln -s CodeDocumentation/html/index.html CodeDocumentation.html
+ @# Generate the html documentation
+ @doxygen $(DOXYGEN_CONF)
+ @echo "" > CodeDocumentation.html
+ @cat warnings.log
+ @# Generate the log of undocumented methods
+ @( cat $(DOXYGEN_CONF) ; echo "GENERATE_HTML=NO" ; echo "EXTRACT_ALL=NO" ; echo "WARN_LOGFILE=undoc.log" ; echo "QUIET=YES" ) | doxygen - &> /dev/null
clean:
rm -rf $(DOXYGEN_CONF) CodeDocumentation CodeDocumentation.html *~
+ rm -rf undoc.log warnings.log
$(DOXYGEN_CONF): $(MFEM_DIR)/doc/$(DOXYGEN_CONF).in
- sed -e 's%@MFEM_SOURCE_DIR@%$(MFEM_DIR)%g' $(<) \
+ @sed -e 's%@MFEM_SOURCE_DIR@%$(MFEM_DIR)%g' $(<) \
> $(DOXYGEN_CONF)
+
diff --git a/doc/web/logo-small.png b/doc/web/logo-small.png
new file mode 100644
index 00000000000..021cbcfe8a7
Binary files /dev/null and b/doc/web/logo-small.png differ
diff --git a/examples/ex1.cpp b/examples/ex1.cpp
index c5e240c1b6d..50bbd175c5e 100644
--- a/examples/ex1.cpp
+++ b/examples/ex1.cpp
@@ -9,6 +9,8 @@
// ex1 -m ../data/fichera.mesh
// ex1 -m ../data/fichera-mixed.mesh
// ex1 -m ../data/toroid-wedge.mesh
+// ex1 -m ../data/periodic-annulus-sector.msh
+// ex1 -m ../data/periodic-torus-sector.msh
// ex1 -m ../data/square-disc-p2.vtk -o 2
// ex1 -m ../data/square-disc-p3.mesh -o 3
// ex1 -m ../data/square-disc-nurbs.mesh -o -1
diff --git a/examples/ex11p.cpp b/examples/ex11p.cpp
index 1dc807f8596..de1e7ce6893 100644
--- a/examples/ex11p.cpp
+++ b/examples/ex11p.cpp
@@ -8,6 +8,8 @@
// mpirun -np 4 ex11p -m ../data/escher.mesh
// mpirun -np 4 ex11p -m ../data/fichera.mesh
// mpirun -np 4 ex11p -m ../data/fichera-mixed.mesh
+// mpirun -np 4 ex11p -m ../data/periodic-annulus-sector.msh
+// mpirun -np 4 ex11p -m ../data/periodic-torus-sector.msh -rs 1
// mpirun -np 4 ex11p -m ../data/toroid-wedge.mesh -o 2
// mpirun -np 4 ex11p -m ../data/square-disc-p2.vtk -o 2
// mpirun -np 4 ex11p -m ../data/square-disc-p3.mesh -o 3
diff --git a/examples/ex14p.cpp b/examples/ex14p.cpp
index 39eb63f7825..471d9c94101 100644
--- a/examples/ex14p.cpp
+++ b/examples/ex14p.cpp
@@ -35,6 +35,38 @@
using namespace std;
using namespace mfem;
+class CustomSolverMonitor : public IterativeSolverMonitor
+{
+public:
+ CustomSolverMonitor(const ParMesh *m,
+ ParGridFunction *f) :
+ pmesh(m),
+ pgf(f) {}
+
+ void MonitorSolution(int i, double norm, const Vector &x, bool final)
+ {
+ char vishost[] = "localhost";
+ int visport = 19916;
+ int num_procs, myid;
+
+ MPI_Comm_size(pmesh->GetComm(),&num_procs);
+ MPI_Comm_rank(pmesh->GetComm(),&myid);
+
+ pgf->SetFromTrueDofs(x);
+
+ socketstream sol_sock(vishost, visport);
+ sol_sock << "parallel " << num_procs << " " << myid << "\n";
+ sol_sock.precision(8);
+ sol_sock << "solution\n" << *pmesh << *pgf
+ << "window_title 'Iteration no " << i << "'"
+ << "keys rRjlc\n" << flush;
+ }
+
+private:
+ const ParMesh *pmesh;
+ ParGridFunction *pgf;
+};
+
int main(int argc, char *argv[])
{
// 1. Initialize MPI.
@@ -188,6 +220,7 @@ int main(int argc, char *argv[])
}
else
{
+ CustomSolverMonitor monitor(pmesh, &x);
GMRESSolver gmres(MPI_COMM_WORLD);
gmres.SetAbsTol(0.0);
gmres.SetRelTol(1e-12);
@@ -196,6 +229,7 @@ int main(int argc, char *argv[])
gmres.SetPrintLevel(1);
gmres.SetOperator(*A);
gmres.SetPreconditioner(*amg);
+ gmres.SetMonitor(monitor);
gmres.Mult(*B, *X);
}
delete amg;
diff --git a/examples/ex18.hpp b/examples/ex18.hpp
index fe0e38ffc2a..75fa5e885bc 100644
--- a/examples/ex18.hpp
+++ b/examples/ex18.hpp
@@ -418,7 +418,7 @@ void FaceIntegrator::AssembleFaceVector(const FiniteElement &el1,
{
intorder++;
}
- const IntegrationRule *ir = &IntRules.Get(Tr.FaceGeom, intorder);
+ const IntegrationRule *ir = &IntRules.Get(Tr.GetGeometryType(), intorder);
for (int i = 0; i < ir->GetNPoints(); i++)
{
@@ -435,10 +435,10 @@ void FaceIntegrator::AssembleFaceVector(const FiniteElement &el1,
elfun1_mat.MultTranspose(shape1, funval1);
elfun2_mat.MultTranspose(shape2, funval2);
- Tr.Face->SetIntPoint(&ip);
+ Tr.SetIntPoint(&ip);
// Get the normal vector and the flux on the face
- CalcOrtho(Tr.Face->Jacobian(), nor);
+ CalcOrtho(Tr.Jacobian(), nor);
const double mcs = rsolver.Eval(funval1, funval2, nor, fluxN);
// Update max char speed
diff --git a/examples/ex19.cpp b/examples/ex19.cpp
index 16db0a167ef..0e70d483c29 100644
--- a/examples/ex19.cpp
+++ b/examples/ex19.cpp
@@ -38,6 +38,42 @@
using namespace std;
using namespace mfem;
+class GeneralResidualMonitor : public IterativeSolverMonitor
+{
+public:
+ GeneralResidualMonitor(const std::string& prefix_, int print_lvl)
+ : prefix(prefix_)
+ {
+ print_level = print_lvl;
+ }
+
+ virtual void MonitorResidual(int it, double norm, const Vector &r, bool final);
+
+private:
+ const std::string prefix;
+ int print_level;
+ mutable double norm0;
+};
+
+void GeneralResidualMonitor::MonitorResidual(int it, double norm,
+ const Vector &r, bool final)
+{
+ if (print_level == 1 || (print_level == 3 && (final || it == 0)))
+ {
+ mfem::out << prefix << " iteration " << setw(2) << it
+ << " : ||r|| = " << norm;
+ if (it > 0)
+ {
+ mfem::out << ", ||r||/||r_0|| = " << norm/norm0;
+ }
+ else
+ {
+ norm0 = norm;
+ }
+ mfem::out << '\n';
+ }
+}
+
// Custom block preconditioner for the Jacobian of the incompressible nonlinear
// elasticity operator. It has the form
//
@@ -103,9 +139,11 @@ class RubberOperator : public Operator
// Newton solver for the hyperelastic operator
NewtonSolver newton_solver;
+ GeneralResidualMonitor newton_monitor;
// Solver for the Jacobian solve in the Newton method
Solver *j_solver;
+ GeneralResidualMonitor j_monitor;
// Preconditioner for the Jacobian
Solver *j_prec;
@@ -410,7 +448,8 @@ RubberOperator::RubberOperator(Array &fes,
int iter,
Coefficient &c_mu)
: Operator(fes[0]->GetVSize() + fes[1]->GetVSize()),
- newton_solver(), mu(c_mu), block_offsets(offsets)
+ newton_solver(), newton_monitor("Newton", 1),
+ j_monitor(" GMRES", 3), mu(c_mu), block_offsets(offsets)
{
Array rhs(2);
rhs = NULL; // Set all entries in the array
@@ -446,7 +485,8 @@ RubberOperator::RubberOperator(Array &fes,
j_gmres->SetRelTol(1e-12);
j_gmres->SetAbsTol(1e-12);
j_gmres->SetMaxIter(300);
- j_gmres->SetPrintLevel(0);
+ j_gmres->SetPrintLevel(-1);
+ j_gmres->SetMonitor(j_monitor);
j_gmres->SetPreconditioner(*j_prec);
j_solver = j_gmres;
@@ -454,7 +494,8 @@ RubberOperator::RubberOperator(Array &fes,
newton_solver.iterative_mode = true;
newton_solver.SetSolver(*j_solver);
newton_solver.SetOperator(*this);
- newton_solver.SetPrintLevel(1);
+ newton_solver.SetPrintLevel(-1);
+ newton_solver.SetMonitor(newton_monitor);
newton_solver.SetRelTol(rel_tol);
newton_solver.SetAbsTol(abs_tol);
newton_solver.SetMaxIter(iter);
diff --git a/examples/ex19p.cpp b/examples/ex19p.cpp
index 3372531e276..f2382f14247 100644
--- a/examples/ex19p.cpp
+++ b/examples/ex19p.cpp
@@ -38,6 +38,56 @@
using namespace std;
using namespace mfem;
+class GeneralResidualMonitor : public IterativeSolverMonitor
+{
+public:
+ GeneralResidualMonitor(MPI_Comm comm, const std::string& prefix_,
+ int print_lvl)
+ : prefix(prefix_)
+ {
+#ifndef MFEM_USE_MPI
+ print_level = print_lvl;
+#else
+ int rank;
+ MPI_Comm_rank(comm, &rank);
+ if (rank == 0)
+ {
+ print_level = print_lvl;
+ }
+ else
+ {
+ print_level = -1;
+ }
+#endif
+ }
+
+ virtual void MonitorResidual(int it, double norm, const Vector &r, bool final);
+
+private:
+ const std::string prefix;
+ int print_level;
+ mutable double norm0;
+};
+
+void GeneralResidualMonitor::MonitorResidual(int it, double norm,
+ const Vector &r, bool final)
+{
+ if (print_level == 1 || (print_level == 3 && (final || it == 0)))
+ {
+ mfem::out << prefix << " iteration " << setw(2) << it
+ << " : ||r|| = " << norm;
+ if (it > 0)
+ {
+ mfem::out << ", ||r||/||r_0|| = " << norm/norm0;
+ }
+ else
+ {
+ norm0 = norm;
+ }
+ mfem::out << '\n';
+ }
+}
+
// Custom block preconditioner for the Jacobian of the incompressible nonlinear
// elasticity operator. It has the form
//
@@ -103,9 +153,11 @@ class RubberOperator : public Operator
// Newton solver for the hyperelastic operator
NewtonSolver newton_solver;
+ GeneralResidualMonitor newton_monitor;
// Solver for the Jacobian solve in the Newton method
Solver *j_solver;
+ GeneralResidualMonitor j_monitor;
// Preconditioner for the Jacobian
Solver *j_prec;
@@ -459,7 +511,10 @@ RubberOperator::RubberOperator(Array &fes,
int iter,
Coefficient &c_mu)
: Operator(fes[0]->TrueVSize() + fes[1]->TrueVSize()),
- newton_solver(fes[0]->GetComm()), mu(c_mu), block_trueOffsets(trueOffsets)
+ newton_solver(fes[0]->GetComm()),
+ newton_monitor(fes[0]->GetComm(), "Newton", 1),
+ j_monitor(fes[0]->GetComm(), " GMRES", 3),
+ mu(c_mu), block_trueOffsets(trueOffsets)
{
Array rhs(2);
rhs = NULL; // Set all entries in the array
@@ -499,7 +554,8 @@ RubberOperator::RubberOperator(Array &fes,
j_gmres->SetRelTol(1e-12);
j_gmres->SetAbsTol(1e-12);
j_gmres->SetMaxIter(300);
- j_gmres->SetPrintLevel(0);
+ j_gmres->SetPrintLevel(-1);
+ j_gmres->SetMonitor(j_monitor);
j_gmres->SetPreconditioner(*j_prec);
j_solver = j_gmres;
@@ -507,7 +563,8 @@ RubberOperator::RubberOperator(Array &fes,
newton_solver.iterative_mode = true;
newton_solver.SetSolver(*j_solver);
newton_solver.SetOperator(*this);
- newton_solver.SetPrintLevel(1);
+ newton_solver.SetPrintLevel(-1);
+ newton_solver.SetMonitor(newton_monitor);
newton_solver.SetRelTol(rel_tol);
newton_solver.SetAbsTol(abs_tol);
newton_solver.SetMaxIter(iter);
diff --git a/examples/ex1p.cpp b/examples/ex1p.cpp
index 83ede5910ae..649ecbf1172 100644
--- a/examples/ex1p.cpp
+++ b/examples/ex1p.cpp
@@ -9,6 +9,8 @@
// mpirun -np 4 ex1p -m ../data/fichera.mesh
// mpirun -np 4 ex1p -m ../data/fichera-mixed.mesh
// mpirun -np 4 ex1p -m ../data/toroid-wedge.mesh
+// mpirun -np 4 ex1p -m ../data/periodic-annulus-sector.msh
+// mpirun -np 4 ex1p -m ../data/periodic-torus-sector.msh
// mpirun -np 4 ex1p -m ../data/square-disc-p2.vtk -o 2
// mpirun -np 4 ex1p -m ../data/square-disc-p3.mesh -o 3
// mpirun -np 4 ex1p -m ../data/square-disc-nurbs.mesh -o -1
diff --git a/examples/ex24.cpp b/examples/ex24.cpp
index a39809e401d..23f7cc361bd 100644
--- a/examples/ex24.cpp
+++ b/examples/ex24.cpp
@@ -6,6 +6,7 @@
// ex24 -m ../data/square-disc.mesh -o 2
// ex24 -m ../data/beam-tet.mesh
// ex24 -m ../data/beam-hex.mesh -o 2 -pa
+// ex24 -m ../data/beam-hex.mesh -o 2 -pa -p 1
// ex24 -m ../data/escher.mesh
// ex24 -m ../data/escher.mesh -o 2
// ex24 -m ../data/fichera.mesh
@@ -23,11 +24,15 @@
// ex24 -m ../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code illustrates usage of mixed finite element
-// spaces. Using two different approaches, we project a gradient
-// of a function in H^1 to H(curl). Other spaces and example
-// computations are to be added in the future.
+// spaces, with two variants:
//
-// We recommend viewing examples 1 and 3 before viewing this
+// 1) (grad p, u) for p in H^1 tested against u in H(curl)
+// 2) (div v, q) for v in H(div) tested against q in L_2
+//
+// Using different approaches, we project the gradient or
+// divergence to the appropriate space.
+//
+// We recommend viewing examples 1, 3, and 5 before viewing this
// example.
#include "mfem.hpp"
@@ -39,6 +44,7 @@ using namespace mfem;
double p_exact(const Vector &x);
void gradp_exact(const Vector &, Vector &);
+double div_gradp_exact(const Vector &x);
int dim;
@@ -47,6 +53,7 @@ int main(int argc, char *argv[])
// 1. Parse command-line options.
const char *mesh_file = "../data/beam-hex.mesh";
int order = 1;
+ int prob = 0;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
@@ -57,6 +64,8 @@ int main(int argc, char *argv[])
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
+ args.AddOption(&prob, "-p", "--problem-type",
+ "Choose between 0: H(Curl) or 1: H(Div)");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
@@ -100,72 +109,107 @@ int main(int argc, char *argv[])
}
mesh->ReorientTetMesh();
- // 5. Define a parallel finite element space on the parallel mesh. Here we
- // use the Nedelec finite elements of the specified order.
- FiniteElementCollection *fec = new ND_FECollection(order, dim);
- FiniteElementCollection *H1fec = new H1_FECollection(order, dim);
- FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
- FiniteElementSpace *H1fespace = new FiniteElementSpace(mesh, H1fec);
-
- int size = fespace->GetTrueVSize();
- int H1size = H1fespace->GetTrueVSize();
- cout << "Number of Nedelec finite element unknowns: " << size << endl;
- cout << "Number of H1 finite element unknowns: " << H1size << endl;
-
- // 6. Define the solution vector x as a parallel finite element grid function
- // corresponding to fespace. Initialize x by projecting the exact
- // solution. Note that only values from the boundary edges will be used
- // when eliminating the non-homogeneous boundary condition to modify the
- // r.h.s. vector b.
- GridFunction x(fespace);
- FunctionCoefficient p_coef(p_exact);
- GridFunction p(H1fespace);
- p.ProjectCoefficient(p_coef);
- p.SetTrueVector();
- p.SetFromTrueVector();
+ // 5. Define a finite element space on the mesh. Here we use Nedelec or
+ // Raviart-Thomas finite elements of the specified order.
+ FiniteElementCollection *trial_fec = NULL;
+ FiniteElementCollection *test_fec = NULL;
+
+ if (prob == 0)
+ {
+ trial_fec = new H1_FECollection(order, dim);
+ test_fec = new ND_FECollection(order, dim);
+ }
+ else
+ {
+ trial_fec = new RT_FECollection(order - 1, dim);
+ test_fec = new L2_FECollection(order - 1, dim);
+ }
+
+ FiniteElementSpace trial_fes(mesh, trial_fec);
+ FiniteElementSpace test_fes(mesh, test_fec);
+
+ int trial_size = trial_fes.GetTrueVSize();
+ int test_size = test_fes.GetTrueVSize();
+
+ if (prob == 0)
+ {
+ cout << "Number of Nedelec finite element unknowns: " << test_size << endl;
+ cout << "Number of H1 finite element unknowns: " << trial_size << endl;
+ }
+ else
+ {
+ cout << "Number of Raviart-Thomas finite element unknowns: "
+ << trial_size << endl;
+ cout << "Number of L2 finite element unknowns: " << test_size << endl;
+ }
+ // 6. Define the solution vector as a finite element grid function
+ // corresponding to the trial fespace.
+ GridFunction gftest(&test_fes);
+ GridFunction gftrial(&trial_fes);
+ GridFunction x(&test_fes);
+ FunctionCoefficient p_coef(p_exact);
VectorFunctionCoefficient gradp_coef(sdim, gradp_exact);
+ FunctionCoefficient divgradp_coef(div_gradp_exact);
- // 7. Set up the bilinear forms.
- Coefficient *muinv = new ConstantCoefficient(1.0);
- Coefficient *sigma = new ConstantCoefficient(1.0);
- BilinearForm *a = new BilinearForm(fespace);
- MixedBilinearForm *a_NDH1 = new MixedBilinearForm(H1fespace, fespace);
+ if (prob == 0)
+ {
+ gftrial.ProjectCoefficient(p_coef);
+ }
+ else
+ {
+ gftrial.ProjectCoefficient(gradp_coef);
+ }
+
+ gftrial.SetTrueVector();
+ gftrial.SetFromTrueVector();
+
+ // 7. Set up the bilinear forms for L2 projection.
+ ConstantCoefficient one(1.0);
+ BilinearForm a(&test_fes);
+ MixedBilinearForm a_mixed(&trial_fes, &test_fes);
if (pa)
{
- a->SetAssemblyLevel(AssemblyLevel::PARTIAL);
- a_NDH1->SetAssemblyLevel(AssemblyLevel::PARTIAL);
+ a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
+ a_mixed.SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
- // First approach: L2 projection
- a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
- a_NDH1->AddDomainIntegrator(new MixedVectorGradientIntegrator(*muinv));
+ if (prob == 0)
+ {
+ a.AddDomainIntegrator(new VectorFEMassIntegrator(one));
+ a_mixed.AddDomainIntegrator(new MixedVectorGradientIntegrator(one));
+ }
+ else
+ {
+ a.AddDomainIntegrator(new MassIntegrator(one));
+ a_mixed.AddDomainIntegrator(new VectorFEDivergenceIntegrator(one));
+ }
- // 8. Assemble the parallel bilinear form and the corresponding linear
- // system, applying any necessary transformations such as: parallel
- // assembly, eliminating boundary conditions, applying conforming
- // constraints for non-conforming AMR, static condensation, etc.
- if (static_cond) { a->EnableStaticCondensation(); }
+ // 8. Assemble the bilinear form and the corresponding linear system,
+ // applying any necessary transformations such as: eliminating boundary
+ // conditions, applying conforming constraints for non-conforming AMR,
+ // static condensation, etc.
+ if (static_cond) { a.EnableStaticCondensation(); }
- a->Assemble();
- if (!pa) { a->Finalize(); }
+ a.Assemble();
+ if (!pa) { a.Finalize(); }
- a_NDH1->Assemble();
- if (!pa) { a_NDH1->Finalize(); }
+ a_mixed.Assemble();
+ if (!pa) { a_mixed.Finalize(); }
if (pa)
{
- a_NDH1->Mult(p, x);
+ a_mixed.Mult(gftrial, x);
}
else
{
- SparseMatrix& NDH1 = a_NDH1->SpMat();
- NDH1.Mult(p, x);
+ SparseMatrix& mixed = a_mixed.SpMat();
+ mixed.Mult(gftrial, x);
}
// 9. Define and apply a PCG solver for Ax = b with Jacobi preconditioner.
{
- GridFunction rhs(fespace);
+ GridFunction rhs(&test_fes);
rhs = x;
x = 0.0;
@@ -176,15 +220,15 @@ int main(int argc, char *argv[])
if (pa)
{
Array ess_tdof_list; // empty
- OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
+ OperatorJacobiSmoother Jacobi(a, ess_tdof_list);
- cg.SetOperator(*a);
+ cg.SetOperator(a);
cg.SetPreconditioner(Jacobi);
cg.Mult(rhs, x);
}
else
{
- SparseMatrix& Amat = a->SpMat();
+ SparseMatrix& Amat = a.SpMat();
DSmoother Jacobi(Amat);
cg.SetOperator(Amat);
@@ -193,33 +237,68 @@ int main(int argc, char *argv[])
}
}
- // 10. Second approach: compute the same solution by applying
- // GradientInterpolator in H(curl).
- DiscreteLinearOperator grad(H1fespace, fespace);
- grad.AddDomainInterpolator(new GradientInterpolator());
- grad.Assemble();
+ // 10. Compute the same field by applying a DiscreteInterpolator.
+ GridFunction discreteInterpolant(&test_fes);
+ DiscreteLinearOperator dlo(&trial_fes, &test_fes);
+ if (prob == 0)
+ {
+ dlo.AddDomainInterpolator(new GradientInterpolator());
+ }
+ else
+ {
+ dlo.AddDomainInterpolator(new DivergenceInterpolator());
+ }
- GridFunction gradp(fespace);
- grad.Mult(p, gradp);
+ dlo.Assemble();
+ dlo.Mult(gftrial, discreteInterpolant);
- // 11. Compute the projection of the exact grad p.
- GridFunction exact_gradp(fespace);
- exact_gradp.ProjectCoefficient(gradp_coef);
- exact_gradp.SetTrueVector();
- exact_gradp.SetFromTrueVector();
+ // 11. Compute the projection of the exact field.
+ GridFunction exact_proj(&test_fes);
+ if (prob == 0)
+ {
+ exact_proj.ProjectCoefficient(gradp_coef);
+ }
+ else
+ {
+ exact_proj.ProjectCoefficient(divgradp_coef);
+ }
- // 12. Compute and print the L^2 norm of the error.
+ exact_proj.SetTrueVector();
+ exact_proj.SetFromTrueVector();
+
+ // 12. Compute and print the L_2 norm of the error.
+ if (prob == 0)
{
double errSol = x.ComputeL2Error(gradp_coef);
- double errInterp = gradp.ComputeL2Error(gradp_coef);
- double errProj = exact_gradp.ComputeL2Error(gradp_coef);
+ double errInterp = discreteInterpolant.ComputeL2Error(gradp_coef);
+ double errProj = exact_proj.ComputeL2Error(gradp_coef);
cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl): "
- "|| E_h - grad p ||_{L^2} = " << errSol << '\n' << endl;
+ "|| E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
cout << " Gradient interpolant E_h = grad p_h in H(curl): || E_h - grad p"
- "||_{L^2} = " << errInterp << '\n' << endl;
+ "||_{L_2} = " << errInterp << '\n' << endl;
cout << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
- "||_{L^2} = " << errProj << '\n' << endl;
+ "||_{L_2} = " << errProj << '\n' << endl;
+ }
+ else
+ {
+ int order_quad = max(2, 2*order+1);
+ const IntegrationRule *irs[Geometry::NumGeom];
+ for (int i=0; i < Geometry::NumGeom; ++i)
+ {
+ irs[i] = &(IntRules.Get(i, order_quad));
+ }
+
+ double errSol = x.ComputeL2Error(divgradp_coef, irs);
+ double errInterp = discreteInterpolant.ComputeL2Error(divgradp_coef, irs);
+ double errProj = exact_proj.ComputeL2Error(divgradp_coef, irs);
+
+ cout << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
+ "|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
+ cout << " Divergence interpolant f_h = div v_h in L_2: || f_h - div v"
+ "||_{L_2} = " << errInterp << '\n' << endl;
+ cout << " Projection f_h of exact div v in L_2: || f_h - div v "
+ "||_{L_2} = " << errProj << '\n' << endl;
}
// 13. Save the refined mesh and the solution. This output can be viewed
@@ -242,14 +321,8 @@ int main(int argc, char *argv[])
}
// 15. Free the used memory.
- delete a;
- delete a_NDH1;
- delete sigma;
- delete muinv;
- delete fespace;
- delete H1fespace;
- delete fec;
- delete H1fec;
+ delete trial_fec;
+ delete test_fec;
delete mesh;
return 0;
@@ -284,3 +357,17 @@ void gradp_exact(const Vector &x, Vector &f)
if (x.Size() == 3) { f(2) = 0.0; }
}
}
+
+double div_gradp_exact(const Vector &x)
+{
+ if (dim == 3)
+ {
+ return -3.0 * sin(x(0)) * sin(x(1)) * sin(x(2));
+ }
+ else if (dim == 2)
+ {
+ return -2.0 * sin(x(0)) * sin(x(1));
+ }
+
+ return 0.0;
+}
diff --git a/examples/ex24p.cpp b/examples/ex24p.cpp
index 5bcd4afa853..b1bcccfd227 100644
--- a/examples/ex24p.cpp
+++ b/examples/ex24p.cpp
@@ -6,6 +6,7 @@
// mpirun -np 4 ex24p -m ../data/square-disc.mesh -o 2
// mpirun -np 4 ex24p -m ../data/beam-tet.mesh
// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -o 2 -pa
+// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -o 2 -p 1 -pa
// mpirun -np 4 ex24p -m ../data/escher.mesh
// mpirun -np 4 ex24p -m ../data/escher.mesh -o 2
// mpirun -np 4 ex24p -m ../data/fichera.mesh
@@ -23,11 +24,15 @@
// mpirun -np 4 ex24p -m ../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code illustrates usage of mixed finite element
-// spaces. Using two different approaches, we project a gradient
-// of a function in H^1 to H(curl). Other spaces and example
-// computations are to be added in the future.
+// spaces, with two variants:
//
-// We recommend viewing examples 1 and 3 before viewing this
+// 1) (grad p, u) for p in H^1 tested against u in H(curl)
+// 2) (div v, q) for v in H(div) tested against q in L_2
+//
+// Using different approaches, we project the gradient or
+// divergence to the appropriate space.
+//
+// We recommend viewing examples 1, 3, and 5 before viewing this
// example.
#include "mfem.hpp"
@@ -39,6 +44,7 @@ using namespace mfem;
double p_exact(const Vector &x);
void gradp_exact(const Vector &, Vector &);
+double div_gradp_exact(const Vector &x);
int dim;
@@ -53,6 +59,7 @@ int main(int argc, char *argv[])
// 2. Parse command-line options.
const char *mesh_file = "../data/beam-hex.mesh";
int order = 1;
+ int prob = 0;
bool static_cond = false;
bool pa = false;
const char *device_config = "cpu";
@@ -63,6 +70,8 @@ int main(int argc, char *argv[])
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
+ args.AddOption(&prob, "-p", "--problem-type",
+ "Choose between 0: H(Curl) or 1: H(Div)");
args.AddOption(&static_cond, "-sc", "--static-condensation", "-no-sc",
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
@@ -129,80 +138,115 @@ int main(int argc, char *argv[])
pmesh->ReorientTetMesh();
// 7. Define a parallel finite element space on the parallel mesh. Here we
- // use the Nedelec finite elements of the specified order.
- FiniteElementCollection *fec = new ND_FECollection(order, dim);
- FiniteElementCollection *H1fec = new H1_FECollection(order, dim);
- ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
- ParFiniteElementSpace *H1fespace = new ParFiniteElementSpace(pmesh, H1fec);
- HYPRE_Int size = fespace->GlobalTrueVSize();
- HYPRE_Int H1size = H1fespace->GlobalTrueVSize();
+ // use Nedelec or Raviart-Thomas finite elements of the specified order.
+ FiniteElementCollection *trial_fec = NULL;
+ FiniteElementCollection *test_fec = NULL;
+
+ if (prob == 0)
+ {
+ trial_fec = new H1_FECollection(order, dim);
+ test_fec = new ND_FECollection(order, dim);
+ }
+ else
+ {
+ trial_fec = new RT_FECollection(order - 1, dim);
+ test_fec = new L2_FECollection(order - 1, dim);
+ }
+
+ ParFiniteElementSpace trial_fes(pmesh, trial_fec);
+ ParFiniteElementSpace test_fes(pmesh, test_fec);
+
+ HYPRE_Int trial_size = trial_fes.GlobalTrueVSize();
+ HYPRE_Int test_size = test_fes.GlobalTrueVSize();
+
if (myid == 0)
{
- cout << "Number of Nedelec finite element unknowns: " << size << endl;
- cout << "Number of H1 finite element unknowns: " << H1size << endl;
+ if (prob == 0)
+ {
+ cout << "Number of Nedelec finite element unknowns: " << test_size << endl;
+ cout << "Number of H1 finite element unknowns: " << trial_size << endl;
+ }
+ else
+ {
+ cout << "Number of Raviart-Thomas finite element unknowns: "
+ << trial_size << endl;
+ cout << "Number of L2 finite element unknowns: " << test_size << endl;
+ }
}
- // 8. Define the solution vector x as a parallel finite element grid function
- // corresponding to fespace. Initialize x by projecting the exact
- // solution. Note that only values from the boundary edges will be used
- // when eliminating the non-homogeneous boundary condition to modify the
- // r.h.s. vector b.
- ParGridFunction x(fespace);
+ // 8. Define the solution vector as a parallel finite element grid function
+ // corresponding to the trial fespace.
+ ParGridFunction gftest(&test_fes);
+ ParGridFunction gftrial(&trial_fes);
+ ParGridFunction x(&test_fes);
FunctionCoefficient p_coef(p_exact);
- ParGridFunction p(H1fespace);
- p.ProjectCoefficient(p_coef);
- p.SetTrueVector();
- p.SetFromTrueVector();
-
VectorFunctionCoefficient gradp_coef(sdim, gradp_exact);
+ FunctionCoefficient divgradp_coef(div_gradp_exact);
+
+ if (prob == 0)
+ {
+ gftrial.ProjectCoefficient(p_coef);
+ }
+ else
+ {
+ gftrial.ProjectCoefficient(gradp_coef);
+ }
+
+ gftrial.SetTrueVector();
+ gftrial.SetFromTrueVector();
- // 9. Set up the parallel bilinear forms.
- Coefficient *muinv = new ConstantCoefficient(1.0);
- Coefficient *sigma = new ConstantCoefficient(1.0);
- ParBilinearForm *a = new ParBilinearForm(fespace);
- ParMixedBilinearForm *a_NDH1 = new ParMixedBilinearForm(H1fespace, fespace);
+ // 9. Set up the parallel bilinear forms for L2 projection.
+ ConstantCoefficient one(1.0);
+ ParBilinearForm a(&test_fes);
+ ParMixedBilinearForm a_mixed(&trial_fes, &test_fes);
if (pa)
{
- a->SetAssemblyLevel(AssemblyLevel::PARTIAL);
- a_NDH1->SetAssemblyLevel(AssemblyLevel::PARTIAL);
+ a.SetAssemblyLevel(AssemblyLevel::PARTIAL);
+ a_mixed.SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
- // First approach: L2 projection
- a->AddDomainIntegrator(new VectorFEMassIntegrator(*sigma));
- a_NDH1->AddDomainIntegrator(new MixedVectorGradientIntegrator(*muinv));
+ if (prob == 0)
+ {
+ a.AddDomainIntegrator(new VectorFEMassIntegrator(one));
+ a_mixed.AddDomainIntegrator(new MixedVectorGradientIntegrator(one));
+ }
+ else
+ {
+ a.AddDomainIntegrator(new MassIntegrator(one));
+ a_mixed.AddDomainIntegrator(new VectorFEDivergenceIntegrator(one));
+ }
// 10. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation, etc.
- if (static_cond) { a->EnableStaticCondensation(); }
+ if (static_cond) { a.EnableStaticCondensation(); }
- a->Assemble();
- if (!pa) { a->Finalize(); }
+ a.Assemble();
+ if (!pa) { a.Finalize(); }
- a_NDH1->Assemble();
- if (!pa) { a_NDH1->Finalize(); }
+ a_mixed.Assemble();
+ if (!pa) { a_mixed.Finalize(); }
- Vector B(fespace->GetTrueVSize());
- Vector X(fespace->GetTrueVSize());
+ Vector B(test_fes.GetTrueVSize());
+ Vector X(test_fes.GetTrueVSize());
if (pa)
{
- ParLinearForm *b = new ParLinearForm(fespace); // used as a vector
- a_NDH1->Mult(p, *b); // process-local multiplication
- b->ParallelAssemble(B);
- delete b;
+ ParLinearForm b(&test_fes); // used as a vector
+ a_mixed.Mult(gftrial, b); // process-local multiplication
+ b.ParallelAssemble(B);
}
else
{
- HypreParMatrix *NDH1 = a_NDH1->ParallelAssemble();
+ HypreParMatrix *mixed = a_mixed.ParallelAssemble();
- Vector P(H1fespace->GetTrueVSize());
- p.GetTrueDofs(P);
+ Vector P(trial_fes.GetTrueVSize());
+ gftrial.GetTrueDofs(P);
- NDH1->Mult(P,B);
+ mixed->Mult(P,B);
- delete NDH1;
+ delete mixed;
}
// 11. Define and apply a parallel PCG solver for AX=B with Jacobi
@@ -212,9 +256,9 @@ int main(int argc, char *argv[])
Array ess_tdof_list; // empty
OperatorPtr A;
- a->FormSystemMatrix(ess_tdof_list, A);
+ a.FormSystemMatrix(ess_tdof_list, A);
- OperatorJacobiSmoother Jacobi(*a, ess_tdof_list);
+ OperatorJacobiSmoother Jacobi(a, ess_tdof_list);
CGSolver cg(MPI_COMM_WORLD);
cg.SetRelTol(1e-12);
@@ -227,7 +271,7 @@ int main(int argc, char *argv[])
}
else
{
- HypreParMatrix *Amat = a->ParallelAssemble();
+ HypreParMatrix *Amat = a.ParallelAssemble();
HypreDiagScale Jacobi(*Amat);
HyprePCG pcg(*Amat);
pcg.SetTol(1e-12);
@@ -242,35 +286,73 @@ int main(int argc, char *argv[])
x.SetFromTrueDofs(X);
- // 12. Second approach: compute the same solution by applying
- // GradientInterpolator in H(curl).
- ParDiscreteLinearOperator grad(H1fespace, fespace);
- grad.AddDomainInterpolator(new GradientInterpolator());
- grad.Assemble();
+ // 12. Compute the same field by applying a DiscreteInterpolator.
+ ParGridFunction discreteInterpolant(&test_fes);
+ ParDiscreteLinearOperator dlo(&trial_fes, &test_fes);
+ if (prob == 0)
+ {
+ dlo.AddDomainInterpolator(new GradientInterpolator());
+ }
+ else
+ {
+ dlo.AddDomainInterpolator(new DivergenceInterpolator());
+ }
+
+ dlo.Assemble();
+ dlo.Mult(gftrial, discreteInterpolant);
- ParGridFunction gradp(fespace);
- grad.Mult(p, gradp);
+ // 13. Compute the projection of the exact field.
+ ParGridFunction exact_proj(&test_fes);
+ if (prob == 0)
+ {
+ exact_proj.ProjectCoefficient(gradp_coef);
+ }
+ else
+ {
+ exact_proj.ProjectCoefficient(divgradp_coef);
+ }
- // 13. Compute the projection of the exact grad p.
- ParGridFunction exact_gradp(fespace);
- exact_gradp.ProjectCoefficient(gradp_coef);
- exact_gradp.SetTrueVector();
- exact_gradp.SetFromTrueVector();
+ exact_proj.SetTrueVector();
+ exact_proj.SetFromTrueVector();
- // 14. Compute and print the L^2 norm of the error.
+ // 14. Compute and print the L_2 norm of the error.
+ if (prob == 0)
{
double errSol = x.ComputeL2Error(gradp_coef);
- double errInterp = gradp.ComputeL2Error(gradp_coef);
- double errProj = exact_gradp.ComputeL2Error(gradp_coef);
+ double errInterp = discreteInterpolant.ComputeL2Error(gradp_coef);
+ double errProj = exact_proj.ComputeL2Error(gradp_coef);
if (myid == 0)
{
- cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in "
- "H(curl): || E_h - grad p ||_{L^2} = " << errSol << '\n' << endl;
- cout << " Gradient interpolant E_h = grad p_h in H(curl): || E_h - "
- "grad p ||_{L^2} = " << errInterp << '\n' << endl;
+ cout << "\n Solution of (E_h,v) = (grad p_h,v) for E_h and v in H(curl): "
+ "|| E_h - grad p ||_{L_2} = " << errSol << '\n' << endl;
+ cout << " Gradient interpolant E_h = grad p_h in H(curl): || E_h - grad p"
+ "||_{L_2} = " << errInterp << '\n' << endl;
cout << " Projection E_h of exact grad p in H(curl): || E_h - grad p "
- "||_{L^2} = " << errProj << '\n' << endl;
+ "||_{L_2} = " << errProj << '\n' << endl;
+ }
+ }
+ else
+ {
+ int order_quad = max(2, 2*order+1);
+ const IntegrationRule *irs[Geometry::NumGeom];
+ for (int i=0; i < Geometry::NumGeom; ++i)
+ {
+ irs[i] = &(IntRules.Get(i, order_quad));
+ }
+
+ double errSol = x.ComputeL2Error(divgradp_coef, irs);
+ double errInterp = discreteInterpolant.ComputeL2Error(divgradp_coef, irs);
+ double errProj = exact_proj.ComputeL2Error(divgradp_coef, irs);
+
+ if (myid == 0)
+ {
+ cout << "\n Solution of (f_h,q) = (div v_h,q) for f_h and q in L_2: "
+ "|| f_h - div v ||_{L_2} = " << errSol << '\n' << endl;
+ cout << " Divergence interpolant f_h = div v_h in L_2: || f_h - div v"
+ "||_{L_2} = " << errInterp << '\n' << endl;
+ cout << " Projection f_h of exact div v in L_2: || f_h - div v "
+ "||_{L_2} = " << errProj << '\n' << endl;
}
}
@@ -302,14 +384,8 @@ int main(int argc, char *argv[])
}
// 17. Free the used memory.
- delete a;
- delete a_NDH1;
- delete sigma;
- delete muinv;
- delete fespace;
- delete H1fespace;
- delete fec;
- delete H1fec;
+ delete trial_fec;
+ delete test_fec;
delete pmesh;
MPI_Finalize();
@@ -346,3 +422,17 @@ void gradp_exact(const Vector &x, Vector &f)
if (x.Size() == 3) { f(2) = 0.0; }
}
}
+
+double div_gradp_exact(const Vector &x)
+{
+ if (dim == 3)
+ {
+ return -3.0 * sin(x(0)) * sin(x(1)) * sin(x(2));
+ }
+ else if (dim == 2)
+ {
+ return -2.0 * sin(x(0)) * sin(x(1));
+ }
+
+ return 0.0;
+}
diff --git a/examples/ex4.cpp b/examples/ex4.cpp
index 6b2d656a00f..6e6c43129c4 100644
--- a/examples/ex4.cpp
+++ b/examples/ex4.cpp
@@ -6,6 +6,7 @@
// ex4 -m ../data/star.mesh
// ex4 -m ../data/beam-tet.mesh
// ex4 -m ../data/beam-hex.mesh
+// 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-q2.vtk
@@ -20,6 +21,12 @@
// ex4 -m ../data/fichera-amr.mesh -o 2 -sc
// ex4 -m ../data/star-surf.mesh -o 1
//
+// Device sample runs:
+// ex4 -m ../data/star.mesh -pa -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
+//
// Description: This example code solves a simple 2D/3D H(div) diffusion
// problem corresponding to the second order definite equation
// -grad(alpha div F) + beta F = f with boundary condition F dot n
@@ -55,6 +62,8 @@ int main(int argc, char *argv[])
bool set_bc = true;
bool static_cond = false;
bool hybridization = false;
+ bool pa = false;
+ const char *device_config = "cpu";
bool visualization = 1;
OptionsParser args(argc, argv);
@@ -70,6 +79,10 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
"--no-hybridization", "Enable hybridization.");
+ args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
+ "--no-partial-assembly", "Enable Partial Assembly.");
+ args.AddOption(&device_config, "-d", "--device",
+ "Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -82,14 +95,19 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
kappa = freq * M_PI;
- // 2. Read the mesh from the given mesh file. We can handle triangular,
+ // 2. Enable hardware devices such as GPUs, and programming models such as
+ // CUDA, OCCA, RAJA and OpenMP based on command line options.
+ Device device(device_config);
+ device.Print();
+
+ // 3. Read the mesh from the given mesh file. We can handle triangular,
// quadrilateral, tetrahedral, hexahedral, surface and volume, as well as
// periodic meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
- // 3. Refine the mesh to increase the resolution. In this example we do
+ // 4. Refine the mesh to increase the resolution. In this example we do
// 'ref_levels' of uniform refinement. We choose 'ref_levels' to be the
// largest number that gives a final mesh with no more than 25,000
// elements.
@@ -102,14 +120,14 @@ int main(int argc, char *argv[])
}
}
- // 4. Define a finite element space on the mesh. Here we use the
+ // 5. Define a finite element space on the mesh. Here we use the
// Raviart-Thomas finite elements of the specified order.
FiniteElementCollection *fec = new RT_FECollection(order-1, dim);
FiniteElementSpace *fespace = new FiniteElementSpace(mesh, fec);
cout << "Number of finite element unknowns: "
<< fespace->GetTrueVSize() << endl;
- // 5. Determine the list of true (i.e. conforming) essential boundary dofs.
+ // 6. Determine the list of true (i.e. conforming) essential boundary dofs.
// In this example, the boundary conditions are defined by marking all
// the boundary attributes from the mesh as essential (Dirichlet) and
// converting them to a list of true dofs.
@@ -121,7 +139,7 @@ int main(int argc, char *argv[])
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
- // 6. Set up the linear form b(.) which corresponds to the right-hand side
+ // 7. Set up the linear form b(.) which corresponds to the right-hand side
// of the FEM linear system, which in this case is (f,phi_i) where f is
// given by the function f_exact and phi_i are the basis functions in the
// finite element fespace.
@@ -130,7 +148,7 @@ int main(int argc, char *argv[])
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
b->Assemble();
- // 7. Define the solution vector x as a finite element grid function
+ // 8. Define the solution vector x as a finite element grid function
// corresponding to fespace. Initialize x by projecting the exact
// solution. Note that only values from the boundary faces will be used
// when eliminating the non-homogeneous boundary condition to modify the
@@ -139,16 +157,17 @@ int main(int argc, char *argv[])
VectorFunctionCoefficient F(sdim, F_exact);
x.ProjectCoefficient(F);
- // 8. Set up the bilinear form corresponding to the H(div) diffusion operator
+ // 9. Set up the bilinear form corresponding to the H(div) diffusion operator
// grad alpha div + beta I, by adding the div-div and the mass domain
// integrators.
Coefficient *alpha = new ConstantCoefficient(1.0);
Coefficient *beta = new ConstantCoefficient(1.0);
BilinearForm *a = new BilinearForm(fespace);
+ if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
- // 9. Assemble the bilinear form and the corresponding linear system,
+ // 10. Assemble the bilinear form and the corresponding linear system,
// applying any necessary transformations such as: eliminating boundary
// conditions, applying conforming constraints for non-conforming AMR,
// static condensation, hybridization, etc.
@@ -167,32 +186,47 @@ int main(int argc, char *argv[])
}
a->Assemble();
- SparseMatrix A;
+ OperatorPtr A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
- cout << "Size of linear system: " << A.Height() << endl;
+ cout << "Size of linear system: " << A->Height() << endl;
+ // 11. Solve the linear system A X = B.
+ if (!pa)
+ {
#ifndef MFEM_USE_SUITESPARSE
- // 10. Define a simple symmetric Gauss-Seidel preconditioner and use it to
- // solve the system A X = B with PCG.
- GSSmoother M(A);
- PCG(A, M, B, X, 1, 10000, 1e-20, 0.0);
+ // Use a simple symmetric Gauss-Seidel preconditioner with PCG.
+ GSSmoother M((SparseMatrix&)(*A));
+ PCG(*A, M, B, X, 1, 10000, 1e-20, 0.0);
#else
- // 10. If compiled with SuiteSparse support, use UMFPACK to solve the system.
- UMFPackSolver umf_solver;
- umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
- umf_solver.SetOperator(A);
- umf_solver.Mult(B, X);
+ // If MFEM was compiled with SuiteSparse, use UMFPACK to solve the system.
+ UMFPackSolver umf_solver;
+ umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
+ umf_solver.SetOperator(*A);
+ umf_solver.Mult(B, X);
#endif
+ }
+ else // Jacobi preconditioning in partial assembly mode
+ {
+ if (UsesTensorBasis(*fespace))
+ {
+ OperatorJacobiSmoother M(*a, ess_tdof_list);
+ PCG(*A, M, B, X, 1, 10000, 1e-20, 0.0);
+ }
+ else
+ {
+ CG(*A, B, X, 1, 10000, 1e-20, 0.0);
+ }
+ }
- // 11. Recover the solution as a finite element grid function.
+ // 12. Recover the solution as a finite element grid function.
a->RecoverFEMSolution(X, *b, x);
- // 12. Compute and print the L^2 norm of the error.
+ // 13. Compute and print the L^2 norm of the error.
cout << "\n|| F_h - F ||_{L^2} = " << x.ComputeL2Error(F) << '\n' << endl;
- // 13. Save the refined mesh and the solution. This output can be viewed
+ // 14. Save the refined mesh and the solution. This output can be viewed
// later using GLVis: "glvis -m refined.mesh -g sol.gf".
{
ofstream mesh_ofs("refined.mesh");
@@ -203,7 +237,7 @@ int main(int argc, char *argv[])
x.Save(sol_ofs);
}
- // 14. Send the solution by socket to a GLVis server.
+ // 15. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -213,7 +247,7 @@ int main(int argc, char *argv[])
sol_sock << "solution\n" << *mesh << x << flush;
}
- // 15. Free the used memory.
+ // 16. Free the used memory.
delete hfes;
delete hfec;
delete a;
@@ -235,7 +269,7 @@ void F_exact(const Vector &p, Vector &F)
double x = p(0);
double y = p(1);
- // double z = (dim == 3) ? p(2) : 0.0;
+ // double z = (dim == 3) ? p(2) : 0.0; // Uncomment if F is changed to depend on z
F(0) = cos(kappa*x)*sin(kappa*y);
F(1) = cos(kappa*y)*sin(kappa*x);
@@ -252,7 +286,7 @@ void f_exact(const Vector &p, Vector &f)
double x = p(0);
double y = p(1);
- // double z = (dim == 3) ? p(2) : 0.0;
+ // double z = (dim == 3) ? p(2) : 0.0; // Uncomment if f is changed to depend on z
double temp = 1 + 2*kappa*kappa;
diff --git a/examples/ex4p.cpp b/examples/ex4p.cpp
index ee81d7c4e6b..282ebb4fa3c 100644
--- a/examples/ex4p.cpp
+++ b/examples/ex4p.cpp
@@ -6,6 +6,7 @@
// mpirun -np 4 ex4p -m ../data/star.mesh
// mpirun -np 4 ex4p -m ../data/beam-tet.mesh
// mpirun -np 4 ex4p -m ../data/beam-hex.mesh
+// 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-q2.vtk
@@ -19,6 +20,12 @@
// mpirun -np 4 ex4p -m ../data/amr-hex.mesh -o 2 -hb
// 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 -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
+//
// Description: This example code solves a simple 2D/3D H(div) diffusion
// problem corresponding to the second order definite equation
// -grad(alpha div F) + beta F = f with boundary condition F dot n
@@ -60,6 +67,8 @@ int main(int argc, char *argv[])
bool set_bc = true;
bool static_cond = false;
bool hybridization = false;
+ bool pa = false;
+ const char *device_config = "cpu";
bool visualization = 1;
OptionsParser args(argc, argv);
@@ -75,6 +84,10 @@ int main(int argc, char *argv[])
"--no-static-condensation", "Enable static condensation.");
args.AddOption(&hybridization, "-hb", "--hybridization", "-no-hb",
"--no-hybridization", "Enable hybridization.");
+ args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
+ "--no-partial-assembly", "Enable Partial Assembly.");
+ args.AddOption(&device_config, "-d", "--device",
+ "Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -94,14 +107,19 @@ int main(int argc, char *argv[])
}
kappa = freq * M_PI;
- // 3. Read the (serial) mesh from the given mesh file on all processors. We
+ // 3. Enable hardware devices such as GPUs, and programming models such as
+ // CUDA, OCCA, RAJA and OpenMP based on command line options.
+ Device device(device_config);
+ if (myid == 0) { device.Print(); }
+
+ // 4. Read the (serial) mesh from the given mesh file on all processors. We
// can handle triangular, quadrilateral, tetrahedral, hexahedral, surface
// and volume, as well as periodic meshes with the same code.
Mesh *mesh = new Mesh(mesh_file, 1, 1);
int dim = mesh->Dimension();
int sdim = mesh->SpaceDimension();
- // 4. Refine the serial mesh on all processors to increase the resolution. In
+ // 5. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
// more than 1,000 elements.
@@ -114,7 +132,7 @@ int main(int argc, char *argv[])
}
}
- // 5. Define a parallel mesh by a partitioning of the serial mesh. Refine
+ // 6. Define a parallel mesh by a partitioning of the serial mesh. Refine
// this mesh further in parallel to increase the resolution. Once the
// parallel mesh is defined, the serial mesh can be deleted. Tetrahedral
// meshes need to be reoriented before we can define high-order Nedelec
@@ -130,7 +148,7 @@ int main(int argc, char *argv[])
}
pmesh->ReorientTetMesh();
- // 6. Define a parallel finite element space on the parallel mesh. Here we
+ // 7. Define a parallel finite element space on the parallel mesh. Here we
// use the Raviart-Thomas finite elements of the specified order.
FiniteElementCollection *fec = new RT_FECollection(order-1, dim);
ParFiniteElementSpace *fespace = new ParFiniteElementSpace(pmesh, fec);
@@ -140,7 +158,7 @@ int main(int argc, char *argv[])
cout << "Number of finite element unknowns: " << size << endl;
}
- // 7. Determine the list of true (i.e. parallel conforming) essential
+ // 8. Determine the list of true (i.e. parallel conforming) essential
// boundary dofs. In this example, the boundary conditions are defined
// by marking all the boundary attributes from the mesh as essential
// (Dirichlet) and converting them to a list of true dofs.
@@ -152,7 +170,7 @@ int main(int argc, char *argv[])
fespace->GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
}
- // 8. Set up the parallel linear form b(.) which corresponds to the
+ // 9. Set up the parallel linear form b(.) which corresponds to the
// right-hand side of the FEM linear system, which in this case is
// (f,phi_i) where f is given by the function f_exact and phi_i are the
// basis functions in the finite element fespace.
@@ -161,7 +179,7 @@ int main(int argc, char *argv[])
b->AddDomainIntegrator(new VectorFEDomainLFIntegrator(f));
b->Assemble();
- // 9. Define the solution vector x as a parallel finite element grid function
+ // 10. Define the solution vector x as a parallel finite element grid function
// corresponding to fespace. Initialize x by projecting the exact
// solution. Note that only values from the boundary faces will be used
// when eliminating the non-homogeneous boundary condition to modify the
@@ -170,16 +188,17 @@ int main(int argc, char *argv[])
VectorFunctionCoefficient F(sdim, F_exact);
x.ProjectCoefficient(F);
- // 10. Set up the parallel bilinear form corresponding to the H(div)
+ // 11. Set up the parallel bilinear form corresponding to the H(div)
// diffusion operator grad alpha div + beta I, by adding the div-div and
// the mass domain integrators.
Coefficient *alpha = new ConstantCoefficient(1.0);
Coefficient *beta = new ConstantCoefficient(1.0);
ParBilinearForm *a = new ParBilinearForm(fespace);
+ if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
- // 11. Assemble the parallel bilinear form and the corresponding linear
+ // 12. Assemble the parallel bilinear form and the corresponding linear
// system, applying any necessary transformations such as: parallel
// assembly, eliminating boundary conditions, applying conforming
// constraints for non-conforming AMR, static condensation,
@@ -199,41 +218,43 @@ int main(int argc, char *argv[])
}
a->Assemble();
- HypreParMatrix A;
+ OperatorPtr A;
Vector B, X;
a->FormLinearSystem(ess_tdof_list, x, *b, A, X, B);
- HYPRE_Int glob_size = A.GetGlobalNumRows();
- if (myid == 0)
+ if (myid == 0 && !pa)
{
- cout << "Size of linear system: " << glob_size << endl;
+ cout << "Size of linear system: "
+ << A.As()->GetGlobalNumRows() << endl;
}
- // 12. Define and apply a parallel PCG solver for A X = B with the 2D AMS or
+ // 13. Define and apply a parallel PCG solver for A X = B with the 2D AMS or
// the 3D ADS preconditioners from hypre. If using hybridization, the
- // system is preconditioned with hypre's BoomerAMG.
- HypreSolver *prec = NULL;
- CGSolver *pcg = new CGSolver(A.GetComm());
- pcg->SetOperator(A);
+ // system is preconditioned with hypre's BoomerAMG. In the partial
+ // assembly case, use Jacobi preconditioning.
+ Solver *prec = NULL;
+ CGSolver *pcg = new CGSolver(MPI_COMM_WORLD);
+ pcg->SetOperator(*A);
pcg->SetRelTol(1e-12);
- pcg->SetMaxIter(500);
+ pcg->SetMaxIter(2000);
pcg->SetPrintLevel(1);
- if (hybridization) { prec = new HypreBoomerAMG(A); }
+ if (hybridization) { prec = new HypreBoomerAMG(*A.As()); }
+ else if (pa) { prec = new OperatorJacobiSmoother(*a, ess_tdof_list); }
else
{
ParFiniteElementSpace *prec_fespace =
(a->StaticCondensationIsEnabled() ? a->SCParFESpace() : fespace);
- if (dim == 2) { prec = new HypreAMS(A, prec_fespace); }
- else { prec = new HypreADS(A, prec_fespace); }
+ if (dim == 2) { prec = new HypreAMS(*A.As(), prec_fespace); }
+ else { prec = new HypreADS(*A.As(), prec_fespace); }
}
pcg->SetPreconditioner(*prec);
pcg->Mult(B, X);
- // 13. Recover the parallel grid function corresponding to X. This is the
+ // 14. Recover the parallel grid function corresponding to X. This is the
// local finite element solution on each processor.
a->RecoverFEMSolution(X, *b, x);
- // 14. Compute and print the L^2 norm of the error.
+ // 15. Compute and print the L^2 norm of the error.
{
double err = x.ComputeL2Error(F);
if (myid == 0)
@@ -242,7 +263,7 @@ int main(int argc, char *argv[])
}
}
- // 15. Save the refined mesh and the solution in parallel. This output can
+ // 16. Save the refined mesh and the solution in parallel. This output can
// be viewed later using GLVis: "glvis -np -m mesh -g sol".
{
ostringstream mesh_name, sol_name;
@@ -258,7 +279,7 @@ int main(int argc, char *argv[])
x.Save(sol_ofs);
}
- // 16. Send the solution by socket to a GLVis server.
+ // 17. Send the solution by socket to a GLVis server.
if (visualization)
{
char vishost[] = "localhost";
@@ -269,7 +290,7 @@ int main(int argc, char *argv[])
sol_sock << "solution\n" << *pmesh << x << flush;
}
- // 17. Free the used memory.
+ // 18. Free the used memory.
delete pcg;
delete prec;
delete hfes;
@@ -295,7 +316,7 @@ void F_exact(const Vector &p, Vector &F)
double x = p(0);
double y = p(1);
- // double z = (dim == 3) ? p(2) : 0.0;
+ // double z = (dim == 3) ? p(2) : 0.0; // Uncomment if F is changed to depend on z
F(0) = cos(kappa*x)*sin(kappa*y);
F(1) = cos(kappa*y)*sin(kappa*x);
@@ -312,7 +333,7 @@ void f_exact(const Vector &p, Vector &f)
double x = p(0);
double y = p(1);
- // double z = (dim == 3) ? p(2) : 0.0;
+ // double z = (dim == 3) ? p(2) : 0.0; // Uncomment if f is changed to depend on z
double temp = 1 + 2*kappa*kappa;
diff --git a/examples/ex5.cpp b/examples/ex5.cpp
index 596157e9c52..b08ecb7aff1 100644
--- a/examples/ex5.cpp
+++ b/examples/ex5.cpp
@@ -4,8 +4,10 @@
//
// Sample runs: ex5 -m ../data/square-disc.mesh
// ex5 -m ../data/star.mesh
+// ex5 -m ../data/star.mesh -pa
// ex5 -m ../data/beam-tet.mesh
// ex5 -m ../data/beam-hex.mesh
+// ex5 -m ../data/beam-hex.mesh -pa
// ex5 -m ../data/escher.mesh
// ex5 -m ../data/fichera.mesh
//
@@ -47,6 +49,7 @@ int main(int argc, char *argv[])
// 1. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
int order = 1;
+ bool pa = false;
bool visualization = 1;
OptionsParser args(argc, argv);
@@ -54,6 +57,8 @@ int main(int argc, char *argv[])
"Mesh file to use.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
+ args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
+ "--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -146,22 +151,39 @@ int main(int argc, char *argv[])
BilinearForm *mVarf(new BilinearForm(R_space));
MixedBilinearForm *bVarf(new MixedBilinearForm(R_space, W_space));
+ if (pa) { mVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
mVarf->AddDomainIntegrator(new VectorFEMassIntegrator(k));
mVarf->Assemble();
- mVarf->Finalize();
- SparseMatrix &M(mVarf->SpMat());
+ if (!pa) { mVarf->Finalize(); }
+ if (pa) { bVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
bVarf->AddDomainIntegrator(new VectorFEDivergenceIntegrator);
bVarf->Assemble();
- bVarf->Finalize();
- SparseMatrix & B(bVarf->SpMat());
- B *= -1.;
- SparseMatrix *BT = Transpose(B);
+ if (!pa) { bVarf->Finalize(); }
- BlockMatrix darcyMatrix(block_offsets);
- darcyMatrix.SetBlock(0,0, &M);
- darcyMatrix.SetBlock(0,1, BT);
- darcyMatrix.SetBlock(1,0, &B);
+ BlockOperator darcyOp(block_offsets);
+
+ TransposeOperator *Bt = NULL;
+
+ if (pa)
+ {
+ Bt = new TransposeOperator(bVarf);
+
+ darcyOp.SetBlock(0,0, mVarf);
+ darcyOp.SetBlock(0,1, Bt, -1.0);
+ darcyOp.SetBlock(1,0, bVarf, -1.0);
+ }
+ else
+ {
+ SparseMatrix &M(mVarf->SpMat());
+ SparseMatrix &B(bVarf->SpMat());
+ B *= -1.;
+ Bt = new TransposeOperator(&B);
+
+ darcyOp.SetBlock(0,0, &M);
+ darcyOp.SetBlock(0,1, Bt);
+ darcyOp.SetBlock(1,0, &B);
+ }
// 9. Construct the operators for preconditioner
//
@@ -170,27 +192,57 @@ int main(int argc, char *argv[])
//
// Here we use Symmetric Gauss-Seidel to approximate the inverse of the
// pressure Schur Complement
- SparseMatrix *MinvBt = Transpose(B);
- Vector Md(M.Height());
- M.GetDiag(Md);
- for (int i = 0; i < Md.Size(); i++)
+ SparseMatrix *MinvBt = NULL;
+ Vector Md(mVarf->Height());
+
+ BlockDiagonalPreconditioner darcyPrec(block_offsets);
+ Solver *invM, *invS;
+ SparseMatrix *S = NULL;
+
+ if (pa)
{
- MinvBt->ScaleRow(i, 1./Md(i));
+ mVarf->AssembleDiagonal(Md);
+ Vector invMd(mVarf->Height());
+ for (int i=0; iHeight(); ++i)
+ {
+ invMd(i) = 1.0 / Md(i);
+ }
+
+ Vector BMBt_diag(bVarf->Height());
+ bVarf->AssembleDiagonal_ADAt(invMd, BMBt_diag);
+
+ Array ess_tdof_list; // empty
+
+ invM = new OperatorJacobiSmoother(Md, ess_tdof_list);
+ invS = new OperatorJacobiSmoother(BMBt_diag, ess_tdof_list);
}
- SparseMatrix *S = Mult(B, *MinvBt);
+ else
+ {
+ SparseMatrix &M(mVarf->SpMat());
+ M.GetDiag(Md);
+
+ SparseMatrix &B(bVarf->SpMat());
+ MinvBt = Transpose(B);
+
+ for (int i = 0; i < Md.Size(); i++)
+ {
+ MinvBt->ScaleRow(i, 1./Md(i));
+ }
+
+ S = Mult(B, *MinvBt);
+
+ invM = new DSmoother(M);
- Solver *invM, *invS;
- invM = new DSmoother(M);
#ifndef MFEM_USE_SUITESPARSE
- invS = new GSSmoother(*S);
+ invS = new GSSmoother(*S);
#else
- invS = new UMFPackSolver(*S);
+ invS = new UMFPackSolver(*S);
#endif
+ }
invM->iterative_mode = false;
invS->iterative_mode = false;
- BlockDiagonalPreconditioner darcyPrec(block_offsets);
darcyPrec.SetDiagonalBlock(0, invM);
darcyPrec.SetDiagonalBlock(1, invS);
@@ -206,7 +258,7 @@ int main(int argc, char *argv[])
solver.SetAbsTol(atol);
solver.SetRelTol(rtol);
solver.SetMaxIter(maxIter);
- solver.SetOperator(darcyMatrix);
+ solver.SetOperator(darcyOp);
solver.SetPreconditioner(darcyPrec);
solver.SetPrintLevel(1);
x = 0.0;
@@ -295,8 +347,8 @@ int main(int argc, char *argv[])
delete invM;
delete invS;
delete S;
+ delete Bt;
delete MinvBt;
- delete BT;
delete mVarf;
delete bVarf;
delete W_space;
diff --git a/examples/ex5p.cpp b/examples/ex5p.cpp
index 389dcb6b363..546a1df3a5a 100644
--- a/examples/ex5p.cpp
+++ b/examples/ex5p.cpp
@@ -4,8 +4,10 @@
//
// Sample runs: mpirun -np 4 ex5p -m ../data/square-disc.mesh
// mpirun -np 4 ex5p -m ../data/star.mesh
+// mpirun -np 4 ex5p -m ../data/star.mesh -r 2 -pa
// mpirun -np 4 ex5p -m ../data/beam-tet.mesh
// mpirun -np 4 ex5p -m ../data/beam-hex.mesh
+// mpirun -np 4 ex5p -m ../data/beam-hex.mesh -pa
// mpirun -np 4 ex5p -m ../data/escher.mesh
// mpirun -np 4 ex5p -m ../data/fichera.mesh
//
@@ -54,19 +56,25 @@ int main(int argc, char *argv[])
// 2. Parse command-line options.
const char *mesh_file = "../data/star.mesh";
+ int ref_levels = -1;
int order = 1;
bool par_format = false;
+ bool pa = false;
bool visualization = 1;
bool adios2 = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
"Mesh file to use.");
+ args.AddOption(&ref_levels, "-r", "--refine",
+ "Number of times to refine the mesh uniformly.");
args.AddOption(&order, "-o", "--order",
"Finite element order (polynomial degree).");
args.AddOption(&par_format, "-pf", "--parallel-format", "-sf",
"--serial-format",
"Format to use when saving the results for VisIt.");
+ args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
+ "--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
@@ -97,10 +105,13 @@ int main(int argc, char *argv[])
// 4. Refine the serial mesh on all processors to increase the resolution. In
// this example we do 'ref_levels' of uniform refinement. We choose
// 'ref_levels' to be the largest number that gives a final mesh with no
- // more than 10,000 elements.
+ // more than 10,000 elements, unless the user specifies it as input.
{
- int ref_levels =
- (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
+ if (ref_levels == -1)
+ {
+ ref_levels = (int)floor(log(10000./mesh->GetNE())/log(2.)/dim);
+ }
+
for (int l = 0; l < ref_levels; l++)
{
mesh->UniformRefinement();
@@ -196,25 +207,47 @@ int main(int argc, char *argv[])
ParBilinearForm *mVarf(new ParBilinearForm(R_space));
ParMixedBilinearForm *bVarf(new ParMixedBilinearForm(R_space, W_space));
- HypreParMatrix *M, *B;
+ HypreParMatrix *M = NULL;
+ HypreParMatrix *B = NULL;
+ if (pa) { mVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
mVarf->AddDomainIntegrator(new VectorFEMassIntegrator(k));
mVarf->Assemble();
- mVarf->Finalize();
- M = mVarf->ParallelAssemble();
+ if (!pa) { mVarf->Finalize(); }
+ if (pa) { bVarf->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
bVarf->AddDomainIntegrator(new VectorFEDivergenceIntegrator);
bVarf->Assemble();
- bVarf->Finalize();
- B = bVarf->ParallelAssemble();
- (*B) *= -1;
-
- HypreParMatrix *BT = B->Transpose();
+ if (!pa) { bVarf->Finalize(); }
BlockOperator *darcyOp = new BlockOperator(block_trueOffsets);
- darcyOp->SetBlock(0,0, M);
- darcyOp->SetBlock(0,1, BT);
- darcyOp->SetBlock(1,0, B);
+
+ Array empty_tdof_list; // empty
+ OperatorPtr opM, opB;
+
+ TransposeOperator *Bt = NULL;
+
+ if (pa)
+ {
+ mVarf->FormSystemMatrix(empty_tdof_list, opM);
+ bVarf->FormRectangularSystemMatrix(empty_tdof_list, empty_tdof_list, opB);
+ Bt = new TransposeOperator(opB.Ptr());
+
+ darcyOp->SetBlock(0,0, opM.Ptr());
+ darcyOp->SetBlock(0,1, Bt, -1.0);
+ darcyOp->SetBlock(1,0, opB.Ptr(), -1.0);
+ }
+ else
+ {
+ M = mVarf->ParallelAssemble();
+ B = bVarf->ParallelAssemble();
+ (*B) *= -1;
+ Bt = new TransposeOperator(B);
+
+ darcyOp->SetBlock(0,0, M);
+ darcyOp->SetBlock(0,1, Bt);
+ darcyOp->SetBlock(1,0, B);
+ }
// 11. Construct the operators for preconditioner
//
@@ -223,17 +256,43 @@ int main(int argc, char *argv[])
//
// Here we use Symmetric Gauss-Seidel to approximate the inverse of the
// pressure Schur Complement.
- HypreParMatrix *MinvBt = B->Transpose();
- HypreParVector *Md = new HypreParVector(MPI_COMM_WORLD, M->GetGlobalNumRows(),
- M->GetRowStarts());
- M->GetDiag(*Md);
+ HypreParMatrix *MinvBt = NULL;
+ HypreParVector *Md = NULL;
+ HypreParMatrix *S = NULL;
+ Vector Md_PA;
+ Solver *invM, *invS;
+
+ if (pa)
+ {
+ Md_PA.SetSize(R_space->GetTrueVSize());
+ mVarf->AssembleDiagonal(Md_PA);
+ Vector invMd(Md_PA.Size());
+ for (int i=0; iGetTrueVSize());
+ bVarf->AssembleDiagonal_ADAt(invMd, BMBt_diag);
- MinvBt->InvScaleRows(*Md);
- HypreParMatrix *S = ParMult(B, MinvBt);
+ Array ess_tdof_list; // empty
- HypreSolver *invM, *invS;
- invM = new HypreDiagScale(*M);
- invS = new HypreBoomerAMG(*S);
+ invM = new OperatorJacobiSmoother(Md_PA, ess_tdof_list);
+ invS = new OperatorJacobiSmoother(BMBt_diag, ess_tdof_list);
+ }
+ else
+ {
+ Md = new HypreParVector(MPI_COMM_WORLD, M->GetGlobalNumRows(),
+ M->GetRowStarts());
+ M->GetDiag(*Md);
+
+ MinvBt = B->Transpose();
+ MinvBt->InvScaleRows(*Md);
+ S = ParMult(B, MinvBt);
+
+ invM = new HypreDiagScale(*M);
+ invS = new HypreBoomerAMG(*S);
+ }
invM->iterative_mode = false;
invS->iterative_mode = false;
@@ -245,7 +304,7 @@ int main(int argc, char *argv[])
// 12. Solve the linear system with MINRES.
// Check the norm of the unpreconditioned residual.
- int maxIter(500);
+ int maxIter(pa ? 1000 : 500);
double rtol(1.e-6);
double atol(1.e-10);
@@ -395,7 +454,7 @@ int main(int argc, char *argv[])
delete S;
delete Md;
delete MinvBt;
- delete BT;
+ delete Bt;
delete B;
delete M;
delete mVarf;
diff --git a/examples/ex9.cpp b/examples/ex9.cpp
index 91175dc0215..03a73f8bd66 100644
--- a/examples/ex9.cpp
+++ b/examples/ex9.cpp
@@ -19,6 +19,7 @@
//
// Device sample runs:
// ex9 -pa
+// ex9 -ea
// ex9 -pa -m ../data/periodic-cube.mesh
// ex9 -pa -m ../data/periodic-cube.mesh -d cuda
//
@@ -142,6 +143,7 @@ int main(int argc, char *argv[])
int ref_levels = 2;
int order = 3;
bool pa = false;
+ bool ea = false;
const char *device_config = "cpu";
int ode_solver_type = 4;
double t_final = 10.0;
@@ -166,6 +168,8 @@ int main(int argc, char *argv[])
"Order (degree) of the finite elements.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
+ args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
+ "--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
@@ -269,6 +273,11 @@ int main(int argc, char *argv[])
m.SetAssemblyLevel(AssemblyLevel::PARTIAL);
k.SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
+ else if (ea)
+ {
+ m.SetAssemblyLevel(AssemblyLevel::ELEMENT);
+ k.SetAssemblyLevel(AssemblyLevel::ELEMENT);
+ }
m.AddDomainIntegrator(new MassIntegrator);
k.AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k.AddInteriorFaceIntegrator(
@@ -429,8 +438,9 @@ FE_Evolution::FE_Evolution(BilinearForm &_M, BilinearForm &_K, const Vector &_b)
: TimeDependentOperator(_M.Height()), M(_M), K(_K), b(_b), z(_M.Height())
{
bool pa = M.GetAssemblyLevel() == AssemblyLevel::PARTIAL;
+ bool ea = M.GetAssemblyLevel() == AssemblyLevel::ELEMENT;
Array ess_tdof_list;
- if (pa)
+ if (pa || ea)
{
M_prec = new OperatorJacobiSmoother(M, ess_tdof_list);
M_solver.SetOperator(M);
diff --git a/examples/ex9p.cpp b/examples/ex9p.cpp
index b81e8c3471e..68143e1861b 100644
--- a/examples/ex9p.cpp
+++ b/examples/ex9p.cpp
@@ -19,6 +19,7 @@
//
// Device sample runs:
// mpirun -np 4 ex9p -pa
+// mpirun -np 4 ex9p -ea
// mpirun -np 4 ex9p -pa -m ../data/periodic-cube.mesh
// mpirun -np 4 ex9p -pa -m ../data/periodic-cube.mesh -d cuda
//
@@ -161,6 +162,7 @@ int main(int argc, char *argv[])
int par_ref_levels = 0;
int order = 3;
bool pa = false;
+ bool ea = false;
const char *device_config = "cpu";
int ode_solver_type = 4;
double t_final = 10.0;
@@ -188,6 +190,8 @@ int main(int argc, char *argv[])
"Order (degree) of the finite elements.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
+ args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
+ "--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&ode_solver_type, "-s", "--ode-solver",
@@ -319,6 +323,11 @@ int main(int argc, char *argv[])
m->SetAssemblyLevel(AssemblyLevel::PARTIAL);
k->SetAssemblyLevel(AssemblyLevel::PARTIAL);
}
+ else if (ea)
+ {
+ m->SetAssemblyLevel(AssemblyLevel::ELEMENT);
+ k->SetAssemblyLevel(AssemblyLevel::ELEMENT);
+ }
m->AddDomainIntegrator(new MassIntegrator);
k->AddDomainIntegrator(new ConvectionIntegrator(velocity, -1.0));
k->AddInteriorFaceIntegrator(
@@ -556,8 +565,9 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
z(_M.Height())
{
bool pa = _M.GetAssemblyLevel()==AssemblyLevel::PARTIAL;
+ bool ea = _M.GetAssemblyLevel()==AssemblyLevel::ELEMENT;
- if (pa)
+ if (pa || ea)
{
M.Reset(&_M, false);
K.Reset(&_K, false);
@@ -571,7 +581,7 @@ FE_Evolution::FE_Evolution(ParBilinearForm &_M, ParBilinearForm &_K,
M_solver.SetOperator(*M);
Array ess_tdof_list;
- if (pa)
+ if (pa || ea)
{
M_prec = new OperatorJacobiSmoother(_M, ess_tdof_list);
dg_solver = NULL;
diff --git a/examples/pumi/ex1.cpp b/examples/pumi/ex1.cpp
index 95def29a732..c72d5ab5baf 100644
--- a/examples/pumi/ex1.cpp
+++ b/examples/pumi/ex1.cpp
@@ -32,6 +32,13 @@
// is used for the Finite Element order and "-go" is used for the
// geometry order. Note that they can be used independently, i.e.
// "-o 8 -go 3" solves for 8th order FE on a third order geometry.
+//
+// NOTE: Model/Mesh files for this example are in the (large) data file
+// repository of MFEM here https://github.com/mfem/data under the
+// folder named "pumi", which consists of the following sub-folders:
+// a) geom --> model files
+// b) parallel --> parallel pumi mesh files
+// c) serial --> serial pumi mesh files
#include "mfem.hpp"
#include
diff --git a/examples/pumi/ex1p.cpp b/examples/pumi/ex1p.cpp
index c17a9aee8dd..acf6b9832fa 100644
--- a/examples/pumi/ex1p.cpp
+++ b/examples/pumi/ex1p.cpp
@@ -36,6 +36,14 @@
// option "-o" is used for the Finite Element order and "-go" for
// the geometry order. Note that they can be used independently:
// "-o 8 -go 3" solves for 8th order FE on third order geometry.
+//
+// NOTE: Model/Mesh files for this example are in the (large) data file
+// repository of MFEM here https://github.com/mfem/data under the
+// folder named "pumi", which consists of the following sub-folders:
+// a) geom --> model files
+// b) parallel --> parallel pumi mesh files
+// c) serial --> serial pumi mesh files
+
#include "mfem.hpp"
#include
diff --git a/examples/pumi/ex2.cpp b/examples/pumi/ex2.cpp
index f7b54c195d9..e294b0e4b93 100644
--- a/examples/pumi/ex2.cpp
+++ b/examples/pumi/ex2.cpp
@@ -43,6 +43,14 @@
// also illustrated.
//
// We recommend viewing Example 1 before viewing this example.
+//
+// NOTE: Model/Mesh files for this example are in the (large) data file
+// repository of MFEM here https://github.com/mfem/data under the
+// folder named "pumi", which consists of the following sub-folders:
+// a) geom --> model files
+// b) parallel --> parallel pumi mesh files
+// c) serial --> serial pumi mesh files
+
#include "mfem.hpp"
#include
diff --git a/examples/pumi/ex6p.cpp b/examples/pumi/ex6p.cpp
index fc9fb5386e8..70b6cd9554f 100644
--- a/examples/pumi/ex6p.cpp
+++ b/examples/pumi/ex6p.cpp
@@ -1,7 +1,7 @@
// MFEM Example 6 - Parallel Version
// PUMI Modification
//
-// Compile with: make ex1p
+// Compile with: make ex6p
//
// Sample runs: mpirun -np 8 ex6p
//
@@ -18,6 +18,13 @@
// is added to modify the "adapt_ratio" which is the fraction of
// allowable error that scales the output size field of the error
// estimator.
+//
+// NOTE: Model/Mesh files for this example are in the (large) data file
+// repository of MFEM here https://github.com/mfem/data under the
+// folder named "pumi", which consists of the following sub-folders:
+// a) geom --> model files
+// b) parallel --> parallel pumi mesh files
+// c) serial --> serial pumi mesh files
#include "mfem.hpp"
#include
@@ -332,7 +339,6 @@ int main(int argc, char *argv[])
apf::destroyField(Tmag_field);
apf::destroyField(ipfield);
- apf::destroyNumbering(pumi_mesh->findNumbering("LocalVertexNumbering"));
// 18. Perform MesAdapt.
ma::Input* erinput = ma::configure(pumi_mesh, sizefield);
diff --git a/fem/CMakeLists.txt b/fem/CMakeLists.txt
index 1a6417b8db6..4b7dc7bdbda 100644
--- a/fem/CMakeLists.txt
+++ b/fem/CMakeLists.txt
@@ -13,13 +13,20 @@ set(SRCS
bilinearform.cpp
bilinearform_ext.cpp
bilininteg.cpp
- bilininteg_convection.cpp
- bilininteg_dgtrace.cpp
- bilininteg_diffusion.cpp
+ bilininteg_convection_pa.cpp
+ bilininteg_convection_ea.cpp
+ bilininteg_dgtrace_pa.cpp
+ bilininteg_dgtrace_ea.cpp
+ bilininteg_diffusion_pa.cpp
+ bilininteg_diffusion_ea.cpp
bilininteg_divergence.cpp
bilininteg_hcurl.cpp
+ bilininteg_hdiv.cpp
+ bilininteg_vectorfe.cpp
bilininteg_gradient.cpp
- bilininteg_mass.cpp
+ bilininteg_mass_pa.cpp
+ bilininteg_mass_ea.cpp
+ bilininteg_transpose_ea.cpp
bilininteg_vecdiffusion.cpp
bilininteg_vecmass.cpp
coefficient.cpp
diff --git a/fem/adios2datacollection.cpp b/fem/adios2datacollection.cpp
index 8c2c8e64259..51703c01cc0 100644
--- a/fem/adios2datacollection.cpp
+++ b/fem/adios2datacollection.cpp
@@ -15,6 +15,8 @@
#include "adios2datacollection.hpp"
+#ifdef MFEM_USE_ADIOS2
+
namespace mfem
{
@@ -87,4 +89,4 @@ noexcept
} //end namespace mfem
-
+#endif // MFEM_USE_ADIOS2
diff --git a/fem/adios2datacollection.hpp b/fem/adios2datacollection.hpp
index c611f736755..dd05b4c4787 100644
--- a/fem/adios2datacollection.hpp
+++ b/fem/adios2datacollection.hpp
@@ -17,6 +17,9 @@
#define MFEM_ADIOS2DATACOLLECTION
#include "../config/config.hpp"
+
+#ifdef MFEM_USE_ADIOS2
+
#include "../general/adios2stream.hpp"
#include "datacollection.hpp"
@@ -85,4 +88,6 @@ class ADIOS2DataCollection : public DataCollection
} // namespace mfem
+#endif // MFEM_USE_ADIOS2
+
#endif /* MFEM_ADIOS2DATACOLLECTION */
diff --git a/fem/bilinearform.cpp b/fem/bilinearform.cpp
index 5dd7f08244f..2629834bf68 100644
--- a/fem/bilinearform.cpp
+++ b/fem/bilinearform.cpp
@@ -126,8 +126,7 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
// Use the original BilinearForm implementation for now
break;
case AssemblyLevel::ELEMENT:
- mfem_error("Element assembly not supported yet... stay tuned!");
- // ext = new EABilinearFormExtension(this);
+ ext = new EABilinearFormExtension(this);
break;
case AssemblyLevel::PARTIAL:
ext = new PABilinearFormExtension(this);
@@ -1432,6 +1431,54 @@ void MixedBilinearForm::Assemble (int skip_zeros)
}
}
+void MixedBilinearForm::AssembleDiagonal_ADAt(const Vector &D,
+ Vector &diag) const
+{
+ if (ext)
+ {
+ MFEM_ASSERT(diag.Size() == test_fes->GetTrueVSize(),
+ "Vector for holding diagonal has wrong size!");
+ MFEM_ASSERT(D.Size() == trial_fes->GetTrueVSize(),
+ "Vector for holding diagonal has wrong size!");
+ const Operator *P_trial = trial_fes->GetProlongationMatrix();
+ const Operator *P_test = test_fes->GetProlongationMatrix();
+ if (!IsIdentityProlongation(P_trial))
+ {
+ Vector local_D(P_trial->Height());
+ P_trial->Mult(D, local_D);
+
+ if (!IsIdentityProlongation(P_test))
+ {
+ Vector local_diag(P_test->Height());
+ ext->AssembleDiagonal_ADAt(local_D, local_diag);
+ P_test->MultTranspose(local_diag, diag);
+ }
+ else
+ {
+ ext->AssembleDiagonal_ADAt(local_D, diag);
+ }
+ }
+ else
+ {
+ if (!IsIdentityProlongation(P_test))
+ {
+ Vector local_diag(P_test->Height());
+ ext->AssembleDiagonal_ADAt(D, local_diag);
+ P_test->MultTranspose(local_diag, diag);
+ }
+ else
+ {
+ ext->AssembleDiagonal_ADAt(D, diag);
+ }
+ }
+ }
+ else
+ {
+ MFEM_ABORT("Not implemented. Maybe assemble your bilinear form into a "
+ "matrix and use SparseMatrix functions?");
+ }
+}
+
void MixedBilinearForm::ConformingAssemble()
{
if (assembly != AssemblyLevel::FULL)
diff --git a/fem/bilinearform.hpp b/fem/bilinearform.hpp
index d616a3dcafd..179ff64f596 100644
--- a/fem/bilinearform.hpp
+++ b/fem/bilinearform.hpp
@@ -25,8 +25,8 @@
namespace mfem
{
-/// Enumeration defining the assembly level for bilinear and nonlinear form
-/// classes derived from Operator.
+/** @brief Enumeration defining the assembly level for bilinear and nonlinear
+ form classes derived from Operator. */
enum class AssemblyLevel
{
/// Fully assembled form, i.e. a global sparse matrix in MFEM, Hypre or PETSC
@@ -44,15 +44,19 @@ enum class AssemblyLevel
};
-/** Class for bilinear form - "Matrix" with associated FE space and
- BLFIntegrators. */
+/** @brief A "square matrix" operator for the associated FE space and
+ BLFIntegrators The sum of all the BLFIntegrators can be used form the matrix
+ M. This class also supports other assembly levels specified via the
+ SetAssemblyLevel() function. */
class BilinearForm : public Matrix
{
protected:
- /// Sparse matrix to be associated with the form. Owned.
+ /// Sparse matrix \f$ M \f$ to be associated with the form. Owned.
SparseMatrix *mat;
- /// Matrix used to eliminate b.c. Owned.
+ /** @brief Sparse Matrix \f$ M_e \f$ used to store the eliminations
+ from the b.c. Owned.
+ \f$ M + M_e = M_{original} \f$ */
SparseMatrix *mat_e;
/// FE space on which the form lives. Not owned.
@@ -62,12 +66,12 @@ class BilinearForm : public Matrix
AssemblyLevel assembly;
/// Element batch size used in the form action (1, 8, num_elems, etc.)
int batch;
- /** Extension for supporting Full Assembly (FA), Element Assembly (EA),
+ /** @brief Extension for supporting Full Assembly (FA), Element Assembly (EA),
Partial Assembly (PA), or Matrix Free assembly (MF). */
BilinearFormExtension *ext;
- /// Indicates the Mesh::sequence corresponding to the current state of the
- /// BilinearForm.
+ /** @brief Indicates the Mesh::sequence corresponding to the current state of
+ the BilinearForm. */
long sequence;
/** @brief Indicates the BilinearFormIntegrator%s stored in #dbfi, #bbfi,
@@ -147,35 +151,43 @@ class BilinearForm : public Matrix
/// Get the size of the BilinearForm as a square matrix.
int Size() const { return height; }
- /// Set the desired assembly level. The default is AssemblyLevel::FULL.
- /** This method must be called before assembly. */
+ /// Set the desired assembly level.
+ /** Valid choices are:
+
+ - AssemblyLevel::FULL (default)
+ - AssemblyLevel::PARTIAL
+ - AssemblyLevel::ELEMENT
+ - AssemblyLevel::NONE
+
+ This method must be called before assembly. */
void SetAssemblyLevel(AssemblyLevel assembly_level);
/// Returns the assembly level
AssemblyLevel GetAssemblyLevel() const { return assembly; }
- /** Enable the use of static condensation. For details see the description
- for class StaticCondensation in fem/staticcond.hpp This method should be
- called before assembly. If the number of unknowns after static
+ /** @brief Enable the use of static condensation. For details see the
+ description for class StaticCondensation in fem/staticcond.hpp This method
+ should be called before assembly. If the number of unknowns after static
condensation is not reduced, it is not enabled. */
void EnableStaticCondensation();
- /** Check if static condensation was actually enabled by a previous call to
- EnableStaticCondensation(). */
+ /** @brief Check if static condensation was actually enabled by a previous
+ call to EnableStaticCondensation(). */
bool StaticCondensationIsEnabled() const { return static_cond; }
/// Return the trace FE space associated with static condensation.
FiniteElementSpace *SCFESpace() const
{ return static_cond ? static_cond->GetTraceFESpace() : NULL; }
- /** Enable hybridization; for details see the description for class
+ /// Enable hybridization.
+ /** For details see the description for class
Hybridization in fem/hybridization.hpp. This method should be called
before assembly. */
void EnableHybridization(FiniteElementSpace *constr_space,
BilinearFormIntegrator *constr_integ,
const Array &ess_tdof_list);
- /** For scalar FE spaces, precompute the sparsity pattern of the matrix
+ /** @brief For scalar FE spaces, precompute the sparsity pattern of the matrix
(assuming dense element matrices) based on the types of integrators
present in the bilinear form. */
void UsePrecomputedSparsity(int ps = 1) { precompute_sparsity = ps; }
@@ -194,15 +206,16 @@ class BilinearForm : public Matrix
/// Use the sparsity of @a A to allocate the internal SparseMatrix.
void UseSparsity(SparseMatrix &A);
- /** Pre-allocate the internal SparseMatrix before assembly. If the flag
- 'precompute sparsity' is set, the matrix is allocated in CSR format (i.e.
+ /// Pre-allocate the internal SparseMatrix before assembly.
+ /** If the flag 'precompute sparsity'
+ is set, the matrix is allocated in CSR format (i.e.
finalized) and the entries are initialized with zeros. */
void AllocateMatrix() { if (mat == NULL) { AllocMat(); } }
- /// Access all integrators added with AddDomainIntegrator().
+ /// Access all the integrators added with AddDomainIntegrator().
Array *GetDBFI() { return &dbfi; }
- /// Access all integrators added with AddBoundaryIntegrator().
+ /// Access all the integrators added with AddBoundaryIntegrator().
Array *GetBBFI() { return &bbfi; }
/** @brief Access all boundary markers added with AddBoundaryIntegrator().
If no marker was specified when the integrator was added, the
@@ -219,64 +232,85 @@ class BilinearForm : public Matrix
corresponding pointer (to Array) will be NULL. */
Array*> *GetBFBFI_Marker() { return &bfbfi_marker; }
+ /// Returns a reference to: \f$ M_{ij} \f$
const double &operator()(int i, int j) { return (*mat)(i,j); }
- /// Returns reference to a_{ij}.
+ /// Returns a reference to: \f$ M_{ij} \f$
virtual double &Elem(int i, int j);
- /// Returns constant reference to a_{ij}.
+ /// Returns constant reference to: \f$ M_{ij} \f$
virtual const double &Elem(int i, int j) const;
- /// Matrix vector multiplication.
+ /// Matrix vector multiplication: \f$ y = M x \f$
virtual void Mult(const Vector &x, Vector &y) const;
+ /** @brief Matrix vector multiplication with the original uneliminated
+ matrix. The original matrix is \f$ M + M_e \f$ so we have:
+ \f$ y = M x + M_e x \f$ */
void FullMult(const Vector &x, Vector &y) const
{ mat->Mult(x, y); mat_e->AddMult(x, y); }
+ /// Add the matrix vector multiple to a vector: \f$ y += a M x \f$
virtual void AddMult(const Vector &x, Vector &y, const double a = 1.0) const
{ mat -> AddMult (x, y, a); }
+ /** @brief Add the original uneliminated matrix vector multiple to a vector.
+ The original matrix is \f$ M + Me \f$ so we have:
+ \f$ y += M x + M_e x \f$ */
void FullAddMult(const Vector &x, Vector &y) const
{ mat->AddMult(x, y); mat_e->AddMult(x, y); }
+ /// Add the matrix transpose vector multiplication: \f$ y += a M^T x \f$
virtual void AddMultTranspose(const Vector & x, Vector & y,
const double a = 1.0) const
{ mat->AddMultTranspose(x, y, a); }
+ /** @brief Add the original uneliminated matrix transpose vector
+ multiple to a vector. The original matrix is \f$ M + M_e \f$
+ so we have: \f$ y += M^T x + {M_e}^T x \f$ */
void FullAddMultTranspose(const Vector & x, Vector & y) const
{ mat->AddMultTranspose(x, y); mat_e->AddMultTranspose(x, y); }
+ /// Matrix transpose vector multiplication: \f$ y = M^T x \f$
virtual void MultTranspose(const Vector & x, Vector & y) const
{ y = 0.0; AddMultTranspose (x, y); }
+ /// Compute \f$ y^T M x \f$
double InnerProduct(const Vector &x, const Vector &y) const
{ return mat->InnerProduct (x, y); }
- /// Returns a pointer to (approximation) of the matrix inverse.
+ /// Returns a pointer to (approximation) of the matrix inverse: \f$ M^{-1} \f$
virtual MatrixInverse *Inverse() const;
/// Finalizes the matrix initialization.
virtual void Finalize(int skip_zeros = 1);
- /// Returns a reference to the sparse matrix
+ /// Returns a const reference to the sparse matrix.
const SparseMatrix &SpMat() const
{
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
return *mat;
}
+
+ /// Returns a reference to the sparse matrix: \f$ M \f$
SparseMatrix &SpMat()
{
MFEM_VERIFY(mat, "mat is NULL and can't be dereferenced");
return *mat;
}
+
+ /** @brief Nullifies the internal matrix \f$ M \f$ and returns a pointer
+ to it. Used for transfering ownership. */
SparseMatrix *LoseMat() { SparseMatrix *tmp = mat; mat = NULL; return tmp; }
- /// Returns a reference to the sparse matrix of eliminated b.c.
+ /// Returns a const reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
const SparseMatrix &SpMatElim() const
{
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
return *mat_e;
}
+
+ /// Returns a reference to the sparse matrix of eliminated b.c.: \f$ M_e \f$
SparseMatrix &SpMatElim()
{
MFEM_VERIFY(mat_e, "mat_e is NULL and can't be dereferenced");
@@ -311,6 +345,7 @@ class BilinearForm : public Matrix
void AddBdrFaceIntegrator(BilinearFormIntegrator *bfi,
Array &bdr_marker);
+ /// Sets all sparse values of \f$ M \f$ and \f$ M_e \f$ to 'a'.
void operator=(const double a)
{
if (mat != NULL) { *mat = a; }
@@ -328,10 +363,10 @@ class BilinearForm : public Matrix
for an AMR mesh. */
void AssembleDiagonal(Vector &diag) const;
- /// Get the finite element space prolongation matrix
+ /// Get the finite element space prolongation operator.
virtual const Operator *GetProlongation() const
{ return fes->GetConformingProlongation(); }
- /// Get the finite element space restriction matrix
+ /// Get the finite element space restriction operator
virtual const Operator *GetRestriction() const
{ return fes->GetConformingRestriction(); }
/// Get the output finite element space prolongation matrix
@@ -491,10 +526,12 @@ class BilinearForm : public Matrix
double value);
/// Eliminate the given @a vdofs. NOTE: here, @a vdofs is a list of DOFs.
+ /** In this case the eliminations are applied to the internal \f$ M \f$
+ and @a rhs without storing the elimination matrix \f$ M_e \f$. */
void EliminateVDofs(const Array &vdofs, const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy = DIAG_ONE);
- /// Eliminate the given @a vdofs, storing the eliminated part internally.
+ /// Eliminate the given @a vdofs, storing the eliminated part internally in \f$ M_e \f$.
/** This method works in conjunction with EliminateVDofsInRHS() and allows
elimination of boundary conditions in multiple right-hand sides. In this
method, @a vdofs is a list of DOFs. */
@@ -523,9 +560,11 @@ class BilinearForm : public Matrix
void EliminateVDofsInRHS(const Array &vdofs, const Vector &x,
Vector &b);
+ /// Compute inner product for full uneliminated matrix \f$ y^T M x + y^T M_e x \f$
double FullInnerProduct(const Vector &x, const Vector &y) const
{ return mat->InnerProduct(x, y) + mat_e->InnerProduct(x, y); }
+ /// Update the @a FiniteElementSpace and delete all data associated with the old one.
virtual void Update(FiniteElementSpace *nfes = NULL);
/// (DEPRECATED) Return the FE space associated with the BilinearForm.
@@ -537,7 +576,13 @@ class BilinearForm : public Matrix
/// Read-only access to the associated FiniteElementSpace.
const FiniteElementSpace *FESpace() const { return fes; }
- /// Sets diagonal policy used upon construction of the linear system
+ /// Sets diagonal policy used upon construction of the linear system.
+ /** Policies include:
+
+ - DIAG_ZERO (Set the diagonal values to zero)
+ - DIAG_ONE (Set the diagonal values to one)
+ - DIAG_KEEP (Keep the diagonal values)
+ */
void SetDiagonalPolicy(DiagonalPolicy policy);
/// Indicate that integrators are not owned by the BilinearForm
@@ -550,16 +595,16 @@ class BilinearForm : public Matrix
/**
Class for assembling of bilinear forms `a(u,v)` defined on different
- trial and test spaces. The assembled matrix `A` is such that
+ trial and test spaces. The assembled matrix `M` is such that
- a(u,v) = V^t A U
+ a(u,v) = V^t M U
where `U` and `V` are the vectors representing the functions `u` and `v`,
respectively. The first argument, `u`, of `a(,)` is in the trial space
and the second argument, `v`, is in the test space. Thus,
- # of rows of A = dimension of the test space and
- # of cols of A = dimension of the trial space.
+ # of rows of M = dimension of the test space and
+ # of cols of M = dimension of the trial space.
Both trial and test spaces should be defined on the same mesh.
*/
@@ -628,11 +673,15 @@ class MixedBilinearForm : public Matrix
FiniteElementSpace *te_fes,
MixedBilinearForm *mbf);
+ /// Returns a reference to: \f$ M_{ij} \f$
virtual double &Elem(int i, int j);
+ /// Returns a reference to: \f$ M_{ij} \f$
virtual const double &Elem(int i, int j) const;
+ /// Matrix multiplication: \f$ y = M x \f$
virtual void Mult(const Vector & x, Vector & y) const;
+
virtual void AddMult(const Vector & x, Vector & y,
const double a = 1.0) const;
@@ -642,6 +691,7 @@ class MixedBilinearForm : public Matrix
virtual MatrixInverse *Inverse() const;
+ /// Finalizes the matrix initialization.
virtual void Finalize(int skip_zeros = 1);
/** Extract the associated matrix as SparseMatrix blocks. The number of
@@ -649,8 +699,14 @@ class MixedBilinearForm : public Matrix
test and trial spaces, respectively. */
void GetBlocks(Array2D &blocks) const;
+ /// Returns a const reference to the sparse matrix: \f$ M \f$
const SparseMatrix &SpMat() const { return *mat; }
+
+ /// Returns a reference to the sparse matrix: \f$ M \f$
SparseMatrix &SpMat() { return *mat; }
+
+ /** @brief Nullifies the internal matrix \f$ M \f$ and returns a pointer
+ to it. Used for transfering ownership. */
SparseMatrix *LoseMat() { SparseMatrix *tmp = mat; mat = NULL; return tmp; }
/// Adds a domain integrator. Assumes ownership of @a bfi.
@@ -697,6 +753,7 @@ class MixedBilinearForm : public Matrix
corresponding pointer (to Array) will be NULL. */
Array*> *GetBTFBFI_Marker() { return &btfbfi_marker; }
+ /// Sets all sparse values of \f$ M \f$ to @a a.
void operator=(const double a) { *mat = a; }
/// Set the desired assembly level. The default is AssemblyLevel::FULL.
@@ -705,6 +762,10 @@ class MixedBilinearForm : public Matrix
void Assemble(int skip_zeros = 1);
+ /** @brief Assemble the diagonal of ADA^T into diag, where A is this mixed
+ bilinear form and D is a diagonal. */
+ void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const;
+
/// Get the input finite element space prolongation matrix
virtual const Operator *GetProlongation() const
{ return trial_fes->GetProlongationMatrix(); }
diff --git a/fem/bilinearform_ext.cpp b/fem/bilinearform_ext.cpp
index 00e57b093f5..a758370a491 100644
--- a/fem/bilinearform_ext.cpp
+++ b/fem/bilinearform_ext.cpp
@@ -47,7 +47,7 @@ PABilinearFormExtension::PABilinearFormExtension(BilinearForm *form)
bdr_face_restrict_lex = NULL;
}
-void PABilinearFormExtension::SetupRestrictionOperators()
+void PABilinearFormExtension::SetupRestrictionOperators(const L2FaceValues m)
{
ElementDofOrdering ordering = UsesTensorBasis(*a->FESpace())?
ElementDofOrdering::LEXICOGRAPHIC:
@@ -65,7 +65,8 @@ void PABilinearFormExtension::SetupRestrictionOperators()
if (int_face_restrict_lex == NULL && a->GetFBFI()->Size() > 0)
{
int_face_restrict_lex = trialFes->GetFaceRestriction(
- ElementDofOrdering::LEXICOGRAPHIC, FaceType::Interior);
+ ElementDofOrdering::LEXICOGRAPHIC,
+ FaceType::Interior);
faceIntX.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType());
faceIntY.SetSize(int_face_restrict_lex->Height(), Device::GetMemoryType());
faceIntY.UseDevice(true); // ensure 'faceIntY = 0.0' is done on device
@@ -74,7 +75,9 @@ void PABilinearFormExtension::SetupRestrictionOperators()
if (bdr_face_restrict_lex == NULL && a->GetBFBFI()->Size() > 0)
{
bdr_face_restrict_lex = trialFes->GetFaceRestriction(
- ElementDofOrdering::LEXICOGRAPHIC, FaceType::Boundary);
+ ElementDofOrdering::LEXICOGRAPHIC,
+ FaceType::Boundary,
+ m);
faceBdrX.SetSize(bdr_face_restrict_lex->Height(), Device::GetMemoryType());
faceBdrY.SetSize(bdr_face_restrict_lex->Height(), Device::GetMemoryType());
faceBdrY.UseDevice(true); // ensure 'faceBoundY = 0.0' is done on device
@@ -83,7 +86,7 @@ void PABilinearFormExtension::SetupRestrictionOperators()
void PABilinearFormExtension::Assemble()
{
- SetupRestrictionOperators();
+ SetupRestrictionOperators(L2FaceValues::DoubleValued);
Array &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
@@ -287,6 +290,311 @@ void PABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
}
}
+// Data and methods for element-assembled bilinear forms
+EABilinearFormExtension::EABilinearFormExtension(BilinearForm *form)
+ : PABilinearFormExtension(form)
+{
+}
+
+void EABilinearFormExtension::Assemble()
+{
+ SetupRestrictionOperators(L2FaceValues::SingleValued);
+
+ ne = trialFes->GetMesh()->GetNE();
+ elemDofs = trialFes->GetFE(0)->GetDof();
+
+ ea_data.SetSize(ne*elemDofs*elemDofs, Device::GetMemoryType());
+ ea_data.UseDevice(true);
+ ea_data = 0.0;
+
+ Array &integrators = *a->GetDBFI();
+ const int integratorCount = integrators.Size();
+ for (int i = 0; i < integratorCount; ++i)
+ {
+ integrators[i]->AssembleEA(*a->FESpace(), ea_data);
+ }
+
+ faceDofs = trialFes ->
+ GetTraceElement(0, trialFes->GetMesh()->GetFaceBaseGeometry(0)) ->
+ GetDof();
+
+ Array &intFaceIntegrators = *a->GetFBFI();
+ const int intFaceIntegratorCount = intFaceIntegrators.Size();
+ if (intFaceIntegratorCount>0)
+ {
+ nf_int = trialFes->GetNFbyType(FaceType::Interior);
+ ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
+ ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
+ ea_data_int = 0.0;
+ ea_data_ext = 0.0;
+ }
+ for (int i = 0; i < intFaceIntegratorCount; ++i)
+ {
+ intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(),
+ ea_data_int,
+ ea_data_ext);
+ }
+
+ Array &bdrFaceIntegrators = *a->GetBFBFI();
+ const int boundFaceIntegratorCount = bdrFaceIntegrators.Size();
+ if (boundFaceIntegratorCount>0)
+ {
+ nf_bdr = trialFes->GetNFbyType(FaceType::Boundary);
+ ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs, Device::GetMemoryType());
+ ea_data_bdr = 0.0;
+ }
+ for (int i = 0; i < boundFaceIntegratorCount; ++i)
+ {
+ bdrFaceIntegrators[i]->AssembleEABoundaryFaces(*a->FESpace(),ea_data_bdr);
+ }
+}
+
+void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
+{
+ // Apply the Element Restriction
+ const bool useRestrict = !DeviceCanUseCeed() && elem_restrict;
+ if (!useRestrict)
+ {
+ y.UseDevice(true); // typically this is a large vector, so store on device
+ y = 0.0;
+ }
+ else
+ {
+ elem_restrict->Mult(x, localX);
+ localY = 0.0;
+ }
+ // Apply the Element Matrices
+ const int NDOFS = elemDofs;
+ auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
+ auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
+ auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
+ MFEM_FORALL(glob_j, ne*NDOFS,
+ {
+ const int e = glob_j/NDOFS;
+ const int j = glob_j%NDOFS;
+ double res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A(i, j, e)*X(i, e);
+ }
+ Y(j, e) += res;
+ });
+ // Apply the Element Restriction transposed
+ if (useRestrict)
+ {
+ elem_restrict->MultTranspose(localY, y);
+ }
+
+ // Treatment of interior faces
+ Array &intFaceIntegrators = *a->GetFBFI();
+ const int iFISz = intFaceIntegrators.Size();
+ if (int_face_restrict_lex && iFISz>0)
+ {
+ // Apply the Interior Face Restriction
+ int_face_restrict_lex->Mult(x, faceIntX);
+ if (faceIntX.Size()>0)
+ {
+ faceIntY = 0.0;
+ // Apply the interior face matrices
+ const int NDOFS = faceDofs;
+ auto X = Reshape(faceIntX.Read(), NDOFS, 2, nf_int);
+ auto Y = Reshape(faceIntY.ReadWrite(), NDOFS, 2, nf_int);
+ auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
+ MFEM_FORALL(glob_j, nf_int*NDOFS,
+ {
+ const int f = glob_j/NDOFS;
+ const int j = glob_j%NDOFS;
+ double res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A_int(i, j, 0, f)*X(i, 0, f);
+ }
+ Y(j, 0, f) += res;
+ res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A_int(i, j, 1, f)*X(i, 1, f);
+ }
+ Y(j, 1, f) += res;
+ });
+ auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
+ MFEM_FORALL(glob_j, nf_int*NDOFS,
+ {
+ const int f = glob_j/NDOFS;
+ const int j = glob_j%NDOFS;
+ double res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A_ext(i, j, 0, f)*X(i, 0, f);
+ }
+ Y(j, 1, f) += res;
+ res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A_ext(i, j, 1, f)*X(i, 1, f);
+ }
+ Y(j, 0, f) += res;
+ });
+ // Apply the Interior Face Restriction transposed
+ int_face_restrict_lex->MultTranspose(faceIntY, y);
+ }
+ }
+
+ // Treatment of boundary faces
+ Array &bdrFaceIntegrators = *a->GetBFBFI();
+ const int bFISz = bdrFaceIntegrators.Size();
+ if (bdr_face_restrict_lex && bFISz>0)
+ {
+ // Apply the Boundary Face Restriction
+ bdr_face_restrict_lex->Mult(x, faceBdrX);
+ if (faceBdrX.Size()>0)
+ {
+ faceBdrY = 0.0;
+ // Apply the boundary face matrices
+ const int NDOFS = faceDofs;
+ auto X = Reshape(faceBdrX.Read(), NDOFS, nf_bdr);
+ auto Y = Reshape(faceBdrY.ReadWrite(), NDOFS, nf_bdr);
+ auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
+ MFEM_FORALL(glob_j, nf_bdr*NDOFS,
+ {
+ const int f = glob_j/NDOFS;
+ const int j = glob_j%NDOFS;
+ double res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A(i, j, f)*X(i, f);
+ }
+ Y(j, f) += res;
+ });
+ // Apply the Boundary Face Restriction transposed
+ bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
+ }
+ }
+}
+
+void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
+{
+ // Apply the Element Restriction
+ const bool useRestrict = DeviceCanUseCeed() || !elem_restrict;
+ if (!useRestrict)
+ {
+ y.UseDevice(true); // typically this is a large vector, so store on device
+ y = 0.0;
+ }
+ else
+ {
+ elem_restrict->Mult(x, localX);
+ localY = 0.0;
+ }
+ // Apply the Element Matrices transposed
+ const int NDOFS = elemDofs;
+ auto X = Reshape(useRestrict?localX.Read():x.Read(), NDOFS, ne);
+ auto Y = Reshape(useRestrict?localY.ReadWrite():y.ReadWrite(), NDOFS, ne);
+ auto A = Reshape(ea_data.Read(), NDOFS, NDOFS, ne);
+ MFEM_FORALL(glob_j, ne*NDOFS,
+ {
+ const int e = glob_j/NDOFS;
+ const int j = glob_j%NDOFS;
+ double res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A(j, i, e)*X(i, e);
+ }
+ Y(j, e) += res;
+ });
+ // Apply the Element Restriction transposed
+ if (useRestrict)
+ {
+ elem_restrict->MultTranspose(localY, y);
+ }
+
+ // Treatment of interior faces
+ Array &intFaceIntegrators = *a->GetFBFI();
+ const int iFISz = intFaceIntegrators.Size();
+ if (int_face_restrict_lex && iFISz>0)
+ {
+ // Apply the Interior Face Restriction
+ int_face_restrict_lex->Mult(x, faceIntX);
+ if (faceIntX.Size()>0)
+ {
+ faceIntY = 0.0;
+ // Apply the interior face matrices transposed
+ const int NDOFS = faceDofs;
+ auto X = Reshape(faceIntX.Read(), NDOFS, 2, nf_int);
+ auto Y = Reshape(faceIntY.ReadWrite(), NDOFS, 2, nf_int);
+ auto A_int = Reshape(ea_data_int.Read(), NDOFS, NDOFS, 2, nf_int);
+ MFEM_FORALL(glob_j, nf_int*NDOFS,
+ {
+ const int f = glob_j/NDOFS;
+ const int j = glob_j%NDOFS;
+ double res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A_int(j, i, 0, f)*X(i, 0, f);
+ }
+ Y(j, 0, f) += res;
+ res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A_int(j, i, 1, f)*X(i, 1, f);
+ }
+ Y(j, 1, f) += res;
+ });
+ auto A_ext = Reshape(ea_data_ext.Read(), NDOFS, NDOFS, 2, nf_int);
+ MFEM_FORALL(glob_j, nf_int*NDOFS,
+ {
+ const int f = glob_j/NDOFS;
+ const int j = glob_j%NDOFS;
+ double res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A_ext(j, i, 0, f)*X(i, 0, f);
+ }
+ Y(j, 1, f) += res;
+ res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A_ext(j, i, 1, f)*X(i, 1, f);
+ }
+ Y(j, 0, f) += res;
+ });
+ // Apply the Interior Face Restriction transposed
+ int_face_restrict_lex->MultTranspose(faceIntY, y);
+ }
+ }
+
+ // Treatment of boundary faces
+ Array &bdrFaceIntegrators = *a->GetBFBFI();
+ const int bFISz = bdrFaceIntegrators.Size();
+ if (bdr_face_restrict_lex && bFISz>0)
+ {
+ // Apply the Boundary Face Restriction
+ bdr_face_restrict_lex->Mult(x, faceBdrX);
+ if (faceBdrX.Size()>0)
+ {
+ faceBdrY = 0.0;
+ // Apply the boundary face matrices transposed
+ const int NDOFS = faceDofs;
+ auto X = Reshape(faceBdrX.Read(), NDOFS, nf_bdr);
+ auto Y = Reshape(faceBdrY.ReadWrite(), NDOFS, nf_bdr);
+ auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
+ MFEM_FORALL(glob_j, nf_bdr*NDOFS,
+ {
+ const int f = glob_j/NDOFS;
+ const int j = glob_j%NDOFS;
+ double res = 0.0;
+ for (int i = 0; i < NDOFS; i++)
+ {
+ res += A(j, i, f)*X(i, f);
+ }
+ Y(j, f) += res;
+ });
+ // Apply the Boundary Face Restriction transposed
+ bdr_face_restrict_lex->MultTranspose(faceBdrY, y);
+ }
+ }
+}
+
MixedBilinearFormExtension::MixedBilinearFormExtension(MixedBilinearForm *form)
: Operator(form->Height(), form->Width()), a(form)
{
@@ -487,4 +795,68 @@ void PAMixedBilinearFormExtension::AddMultTranspose(const Vector &x, Vector &y,
}
}
+void PAMixedBilinearFormExtension::AssembleDiagonal_ADAt(const Vector &D,
+ Vector &diag) const
+{
+ Array &integrators = *a->GetDBFI();
+
+ const int iSz = integrators.Size();
+
+ if (elem_restrict_trial)
+ {
+ const ElementRestriction* H1elem_restrict_trial =
+ dynamic_cast(elem_restrict_trial);
+ if (H1elem_restrict_trial)
+ {
+ H1elem_restrict_trial->MultUnsigned(D, localTrial);
+ }
+ else
+ {
+ elem_restrict_trial->Mult(D, localTrial);
+ }
+ }
+
+ if (elem_restrict_test)
+ {
+ localTest = 0.0;
+ for (int i = 0; i < iSz; ++i)
+ {
+ if (elem_restrict_trial)
+ {
+ integrators[i]->AssembleDiagonalPA_ADAt(localTrial, localTest);
+ }
+ else
+ {
+ integrators[i]->AssembleDiagonalPA_ADAt(D, localTest);
+ }
+ }
+ const ElementRestriction* H1elem_restrict_test =
+ dynamic_cast(elem_restrict_test);
+ if (H1elem_restrict_test)
+ {
+ H1elem_restrict_test->MultTransposeUnsigned(localTest, diag);
+ }
+ else
+ {
+ elem_restrict_test->MultTranspose(localTest, diag);
+ }
+ }
+ else
+ {
+ diag.UseDevice(true); // typically this is a large vector, so store on device
+ diag = 0.0;
+ for (int i = 0; i < iSz; ++i)
+ {
+ if (elem_restrict_trial)
+ {
+ integrators[i]->AssembleDiagonalPA_ADAt(localTrial, diag);
+ }
+ else
+ {
+ integrators[i]->AssembleDiagonalPA_ADAt(D, diag);
+ }
+ }
+ }
+}
+
} // namespace mfem
diff --git a/fem/bilinearform_ext.hpp b/fem/bilinearform_ext.hpp
index d14eae739c0..57fbc027870 100644
--- a/fem/bilinearform_ext.hpp
+++ b/fem/bilinearform_ext.hpp
@@ -22,9 +22,12 @@ namespace mfem
class BilinearForm;
class MixedBilinearForm;
-
-/** @brief Class extending the BilinearForm class to support the different
- AssemblyLevel%s. */
+/// Class extending the BilinearForm class to support different AssemblyLevels.
+/** FA - Full Assembly
+ PA - Partial Assembly
+ EA - Element Assembly
+ MF - Matrix Free
+*/
class BilinearFormExtension : public Operator
{
protected:
@@ -42,6 +45,7 @@ class BilinearFormExtension : public Operator
/// Get the finite element space restriction matrix
virtual const Operator *GetRestriction() const;
+ /// Assemble at the level given for the BilinearFormExtension subclass
virtual void Assemble() = 0;
virtual void AssembleDiagonal(Vector &diag) const
@@ -58,7 +62,8 @@ class BilinearFormExtension : public Operator
virtual void Update() = 0;
};
-/// Data and methods for fully-assembled bilinear forms
+/** @brief Data and methods for fully-assembled bilinear forms.
+ Not yet implemented! Use the BilinearForm Class instead. */
class FABilinearFormExtension : public BilinearFormExtension
{
public:
@@ -78,26 +83,6 @@ class FABilinearFormExtension : public BilinearFormExtension
~FABilinearFormExtension() {}
};
-/// Data and methods for element-assembled bilinear forms
-class EABilinearFormExtension : public BilinearFormExtension
-{
-public:
- EABilinearFormExtension(BilinearForm *form)
- : BilinearFormExtension(form) { }
-
- /// TODO
- void Assemble() {}
- void FormSystemMatrix(const Array &ess_tdof_list, OperatorHandle &A) {}
- void FormLinearSystem(const Array &ess_tdof_list,
- Vector &x, Vector &b,
- OperatorHandle &A, Vector &X, Vector &B,
- int copy_interior = 0) {}
- void Mult(const Vector &x, Vector &y) const {}
- void MultTranspose(const Vector &x, Vector &y) const {}
- void Update() {}
- ~EABilinearFormExtension() {}
-};
-
/// Data and methods for partially-assembled bilinear forms
class PABilinearFormExtension : public BilinearFormExtension
{
@@ -113,7 +98,6 @@ class PABilinearFormExtension : public BilinearFormExtension
public:
PABilinearFormExtension(BilinearForm*);
- void SetupRestrictionOperators();
void Assemble();
void AssembleDiagonal(Vector &diag) const;
void FormSystemMatrix(const Array &ess_tdof_list, OperatorHandle &A);
@@ -121,14 +105,34 @@ class PABilinearFormExtension : public BilinearFormExtension
Vector &x, Vector &b,
OperatorHandle &A, Vector &X, Vector &B,
int copy_interior = 0);
-
void Mult(const Vector &x, Vector &y) const;
void MultTranspose(const Vector &x, Vector &y) const;
void Update();
+
+protected:
+ void SetupRestrictionOperators(const L2FaceValues m);
};
+/// Data and methods for element-assembled bilinear forms
+class EABilinearFormExtension : public PABilinearFormExtension
+{
+protected:
+ int ne;
+ int elemDofs;
+ Vector ea_data;
+ int nf_int, nf_bdr;
+ int faceDofs;
+ Vector ea_data_int, ea_data_ext, ea_data_bdr;
+
+public:
+ EABilinearFormExtension(BilinearForm *form);
+
+ void Assemble();
+ void Mult(const Vector &x, Vector &y) const;
+ void MultTranspose(const Vector &x, Vector &y) const;
+};
-/// Data and methods for matrix-free bilinear forms
+/// Data and methods for matrix-free bilinear forms NOT YET IMPLEMENTED.
class MFBilinearFormExtension : public BilinearFormExtension
{
public:
@@ -148,8 +152,12 @@ class MFBilinearFormExtension : public BilinearFormExtension
~MFBilinearFormExtension() {}
};
-/** @brief Class extending the MixedBilinearForm class to support the different
- AssemblyLevel%s. */
+/// Class extending the MixedBilinearForm class to support different AssemblyLevels.
+/** FA - Full Assembly
+ PA - Partial Assembly
+ EA - Element Assembly
+ MF - Matrix Free
+*/
class MixedBilinearFormExtension : public Operator
{
protected:
@@ -186,6 +194,8 @@ class MixedBilinearFormExtension : public Operator
virtual void AddMultTranspose(const Vector &x, Vector &y,
const double c=1.0) const = 0;
+ virtual void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const = 0;
+
virtual void Update() = 0;
};
@@ -236,6 +246,9 @@ class PAMixedBilinearFormExtension : public MixedBilinearFormExtension
void MultTranspose(const Vector &x, Vector &y) const;
/// y += c*A^T*x
void AddMultTranspose(const Vector &x, Vector &y, const double c=1.0) const;
+ /// Assemble the diagonal of ADA^T for a diagonal vector D.
+ void AssembleDiagonal_ADAt(const Vector &D, Vector &diag) const;
+
/// Update internals for when a new MixedBilinearForm is given to this class
void Update();
};
diff --git a/fem/bilininteg.cpp b/fem/bilininteg.cpp
index f145ed596a2..f10e14959a5 100644
--- a/fem/bilininteg.cpp
+++ b/fem/bilininteg.cpp
@@ -47,7 +47,37 @@ void BilinearFormIntegrator::AssemblePABoundaryFaces(const FiniteElementSpace&)
void BilinearFormIntegrator::AssembleDiagonalPA(Vector &)
{
- MFEM_ABORT("BilinearFormIntegrator::AssembleDiagonalPA(...)\n"
+ mfem_error ("BilinearFormIntegrator::AssembleDiagonalPA(...)\n"
+ " is not implemented for this class.");
+}
+
+void BilinearFormIntegrator::AssembleEA(const FiniteElementSpace &fes,
+ Vector &emat)
+{
+ mfem_error ("BilinearFormIntegrator::AssembleEA(...)\n"
+ " is not implemented for this class.");
+}
+
+void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
+ &fes,
+ Vector &ea_data_int,
+ Vector &ea_data_ext)
+{
+ mfem_error ("BilinearFormIntegrator::AssembleEAInteriorFaces(...)\n"
+ " is not implemented for this class.");
+}
+
+void BilinearFormIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace
+ &fes,
+ Vector &ea_data_bdr)
+{
+ mfem_error ("BilinearFormIntegrator::AssembleEABoundaryFaces(...)\n"
+ " is not implemented for this class.");
+}
+
+void BilinearFormIntegrator::AssembleDiagonalPA_ADAt(const Vector &, Vector &)
+{
+ MFEM_ABORT("BilinearFormIntegrator::AssembleDiagonalPA_ADAt(...)\n"
" is not implemented for this class.");
}
@@ -889,7 +919,7 @@ void BoundaryMassIntegrator::AssembleFaceMatrix(
{
int order = 2 * el1.GetOrder();
- ir = &IntRules.Get(Trans.FaceGeom, order);
+ ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
elmat = 0.0;
@@ -900,11 +930,11 @@ void BoundaryMassIntegrator::AssembleFaceMatrix(
Trans.Loc1.Transform(ip, eip);
el1.CalcShape(eip, shape);
- Trans.Face->SetIntPoint(&ip);
- w = Trans.Face->Weight() * ip.weight;
+ Trans.SetIntPoint(&ip);
+ w = Trans.Weight() * ip.weight;
if (Q)
{
- w *= Q -> Eval(*Trans.Face, ip);
+ w *= Q -> Eval(Trans, ip);
}
AddMult_a_VVt(w, shape, elmat);
@@ -1974,7 +2004,7 @@ void VectorFEMassIntegrator::AssembleElementMatrix2(
D.SetSize(VQ ? VQ->GetVDim() : 0);
K.SetSize(MQ ? MQ->GetVDim() : 0, MQ ? MQ->GetVDim() : 0);
#endif
- DenseMatrix tmp(trial_vshape.Height(), K.Width());
+ DenseMatrix tmp(test_vshape.Height(), K.Width());
elmat.SetSize (test_dof, trial_dof);
@@ -2535,7 +2565,7 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
{
order++;
}
- ir = &IntRules.Get(Trans.FaceGeom, order);
+ ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
for (int p = 0; p < ir->GetNPoints(); p++)
@@ -2549,8 +2579,7 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
}
el1.CalcShape(eip1, shape1);
- Trans.Face->SetIntPoint(&ip);
- Trans.Elem1->SetIntPoint(&eip1);
+ Trans.SetIntPoint(&ip);
u->Eval(vu, *Trans.Elem1, eip1);
@@ -2560,7 +2589,7 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
}
else
{
- CalcOrtho(Trans.Face->Jacobian(), nor);
+ CalcOrtho(Trans.Jacobian(), nor);
}
un = vu * nor;
@@ -2575,7 +2604,6 @@ void DGTraceIntegrator::AssembleFaceMatrix(const FiniteElement &el1,
double rho_p;
if (un >= 0.0 && ndof2)
{
- Trans.Elem2->SetIntPoint(&eip2);
rho_p = rho->Eval(*Trans.Elem2, eip2);
}
else
@@ -2691,7 +2719,7 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
{
order = 2*el1.GetOrder();
}
- ir = &IntRules.Get(Trans.FaceGeom, order);
+ ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
// assemble: < {(Q \nabla u).n},[v] > --> elmat
@@ -2702,19 +2730,18 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
IntegrationPoint eip1, eip2;
Trans.Loc1.Transform(ip, eip1);
- Trans.Face->SetIntPoint(&ip);
+ Trans.SetIntPoint(&ip);
if (dim == 1)
{
nor(0) = 2*eip1.x - 1.0;
}
else
{
- CalcOrtho(Trans.Face->Jacobian(), nor);
+ CalcOrtho(Trans.Jacobian(), nor);
}
el1.CalcShape(eip1, shape1);
el1.CalcDShape(eip1, dshape1);
- Trans.Elem1->SetIntPoint(&eip1);
w = ip.weight/Trans.Elem1->Weight();
if (ndof2)
{
@@ -2763,7 +2790,6 @@ void DGDiffusionIntegrator::AssembleFaceMatrix(
Trans.Loc2.Transform(ip, eip2);
el2.CalcShape(eip2, shape2);
el2.CalcDShape(eip2, dshape2);
- Trans.Elem2->SetIntPoint(&eip2);
w = ip.weight/2/Trans.Elem2->Weight();
if (!MQ)
{
@@ -2973,7 +2999,7 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
{
// a simple choice for the integration order; is this OK?
const int order = 2 * max(el1.GetOrder(), ndofs2 ? el2.GetOrder() : 0);
- ir = &IntRules.Get(Trans.FaceGeom, order);
+ ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
for (int pind = 0; pind < ir->GetNPoints(); ++pind)
@@ -2981,8 +3007,7 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
const IntegrationPoint &ip = ir->IntPoint(pind);
IntegrationPoint eip1, eip2; // integration point in the reference space
Trans.Loc1.Transform(ip, eip1);
- Trans.Face->SetIntPoint(&ip);
- Trans.Elem1->SetIntPoint(&eip1);
+ Trans.SetIntPoint(&ip);
el1.CalcShape(eip1, shape1);
el1.CalcDShape(eip1, dshape1);
@@ -2996,14 +3021,13 @@ void DGElasticityIntegrator::AssembleFaceMatrix(
}
else
{
- CalcOrtho(Trans.Face->Jacobian(), nor);
+ CalcOrtho(Trans.Jacobian(), nor);
}
double w, wLM;
if (ndofs2)
{
Trans.Loc2.Transform(ip, eip2);
- Trans.Elem2->SetIntPoint(&eip2);
el2.CalcShape(eip2, shape2);
el2.CalcDShape(eip2, dshape2);
CalcAdjugate(Trans.Elem2->Jacobian(), adjJ);
@@ -3133,9 +3157,9 @@ void TraceJumpIntegrator::AssembleFaceMatrix(
order += trial_face_fe.GetOrder();
if (trial_face_fe.GetMapType() == FiniteElement::VALUE)
{
- order += Trans.Face->OrderW();
+ order += Trans.OrderW();
}
- ir = &IntRules.Get(Trans.FaceGeom, order);
+ ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
for (int p = 0; p < ir->GetNPoints(); p++)
@@ -3143,23 +3167,21 @@ void TraceJumpIntegrator::AssembleFaceMatrix(
const IntegrationPoint &ip = ir->IntPoint(p);
IntegrationPoint eip1, eip2;
// Trace finite element shape function
- Trans.Face->SetIntPoint(&ip);
+ Trans.SetIntPoint(&ip);
trial_face_fe.CalcShape(ip, face_shape);
// Side 1 finite element shape function
Trans.Loc1.Transform(ip, eip1);
test_fe1.CalcShape(eip1, shape1);
- Trans.Elem1->SetIntPoint(&eip1);
if (ndof2)
{
// Side 2 finite element shape function
Trans.Loc2.Transform(ip, eip2);
test_fe2.CalcShape(eip2, shape2);
- Trans.Elem2->SetIntPoint(&eip2);
}
w = ip.weight;
if (trial_face_fe.GetMapType() == FiniteElement::VALUE)
{
- w *= Trans.Face->Weight();
+ w *= Trans.Weight();
}
face_shape *= w;
for (i = 0; i < ndof1; i++)
@@ -3224,7 +3246,7 @@ void NormalTraceJumpIntegrator::AssembleFaceMatrix(
order = test_fe1.GetOrder() - 1;
}
order += trial_face_fe.GetOrder();
- ir = &IntRules.Get(Trans.FaceGeom, order);
+ ir = &IntRules.Get(Trans.GetGeometryType(), order);
}
for (int p = 0; p < ir->GetNPoints(); p++)
diff --git a/fem/bilininteg.hpp b/fem/bilininteg.hpp
index 98eb04c53ce..a7469fe438d 100644
--- a/fem/bilininteg.hpp
+++ b/fem/bilininteg.hpp
@@ -57,6 +57,9 @@ class BilinearFormIntegrator : public NonlinearFormIntegrator
/// Assemble diagonal and add it to Vector @a diag.
virtual void AssembleDiagonalPA(Vector &diag);
+ /// Assemble diagonal of ADA^T (A is this integrator) and add it to @a diag.
+ virtual void AssembleDiagonalPA_ADAt(const Vector &D, Vector &diag);
+
/// Method for partially assembled action.
/** Perform the action of integrator on the input @a x and add the result to
the output @a y. Both @a x and @a y are E-vectors, i.e. they represent
@@ -75,6 +78,22 @@ class BilinearFormIntegrator : public NonlinearFormIntegrator
called. */
virtual void AddMultTransposePA(const Vector &x, Vector &y) const;
+ /// Method defining element assembly.
+ /** The result of the element assembly is added and stored in the @a emat
+ Vector. */
+ virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
+ /** Used with BilinearFormIntegrators that have different spaces. */
+ // virtual void AssembleEA(const FiniteElementSpace &trial_fes,
+ // const FiniteElementSpace &test_fes,
+ // Vector &emat);
+
+ virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
+ Vector &ea_data_int,
+ Vector &ea_data_ext);
+
+ virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
+ Vector &ea_data_bdr);
+
/// Given a particular Finite Element computes the element matrix elmat.
virtual void AssembleElementMatrix(const FiniteElement &el,
ElementTransformation &Trans,
@@ -180,6 +199,8 @@ class BilinearFormIntegrator : public NonlinearFormIntegrator
virtual ~BilinearFormIntegrator() { }
};
+/** Wraps a given @a BilinearFormIntegrator and transposes the resulting element
+ matrices. See for example ex9, ex9p. */
class TransposeIntegrator : public BilinearFormIntegrator
{
private:
@@ -234,6 +255,15 @@ class TransposeIntegrator : public BilinearFormIntegrator
bfi->AddMultTransposePA(x, y);
}
+ virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
+
+ virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
+ Vector &ea_data_int,
+ Vector &ea_data_ext);
+
+ virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
+ Vector &ea_data_bdr);
+
virtual ~TransposeIntegrator() { if (own_bfi) { delete bfi; } }
};
@@ -1885,6 +1915,8 @@ class DiffusionIntegrator: public BilinearFormIntegrator
virtual void AssemblePA(const FiniteElementSpace &fes);
+ virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
+
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AddMultPA(const Vector&, Vector&) const;
@@ -1958,6 +1990,8 @@ class MassIntegrator: public BilinearFormIntegrator
virtual void AssemblePA(const FiniteElementSpace &fes);
+ virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
+
virtual void AssembleDiagonalPA(Vector &diag);
virtual void AddMultPA(const Vector&, Vector&) const;
@@ -1969,6 +2003,7 @@ class MassIntegrator: public BilinearFormIntegrator
void SetupPA(const FiniteElementSpace &fes, const bool force = false);
};
+/** Mass integrator (u, v) restricted to the boundary of a domain */
class BoundaryMassIntegrator : public MassIntegrator
{
public:
@@ -2011,6 +2046,8 @@ class ConvectionIntegrator : public BilinearFormIntegrator
virtual void AssemblePA(const FiniteElementSpace&);
+ virtual void AssembleEA(const FiniteElementSpace &fes, Vector &emat);
+
virtual void AddMultPA(const Vector&, Vector&) const;
static const IntegrationRule &GetRule(const FiniteElement &el,
@@ -2110,11 +2147,25 @@ class VectorFEDivergenceIntegrator : public BilinearFormIntegrator
protected:
Coefficient *Q;
+ using BilinearFormIntegrator::AssemblePA;
+ virtual void AssemblePA(const FiniteElementSpace &trial_fes,
+ const FiniteElementSpace &test_fes);
+
+ virtual void AddMultPA(const Vector&, Vector&) const;
+ virtual void AddMultTransposePA(const Vector&, Vector&) const;
+
private:
#ifndef MFEM_THREAD_SAFE
Vector divshape, shape;
#endif
+ // PA extension
+ Vector pa_data;
+ const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
+ const DofToQuad *L2mapsO; ///< Not owned. DOF-to-quad map, open.
+ const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
+ int dim, ne, dofs1D, L2dofs1D, quad1D;
+
public:
VectorFEDivergenceIntegrator() { Q = NULL; }
VectorFEDivergenceIntegrator(Coefficient &q) { Q = &q; }
@@ -2125,6 +2176,8 @@ class VectorFEDivergenceIntegrator : public BilinearFormIntegrator
const FiniteElement &test_fe,
ElementTransformation &Trans,
DenseMatrix &elmat);
+
+ virtual void AssembleDiagonalPA_ADAt(const Vector &D, Vector &diag);
};
@@ -2308,7 +2361,7 @@ class VectorFEMassIntegrator: public BilinearFormIntegrator
const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
const GeometricFactors *geom; ///< Not owned
- int dim, ne, nq, dofs1D, quad1D;
+ int dim, ne, nq, dofs1D, quad1D, fetype;
public:
VectorFEMassIntegrator() { Init(NULL, NULL, NULL); }
@@ -2387,11 +2440,23 @@ class DivDivIntegrator: public BilinearFormIntegrator
protected:
Coefficient *Q;
+ using BilinearFormIntegrator::AssemblePA;
+ virtual void AssemblePA(const FiniteElementSpace &fes);
+ virtual void AddMultPA(const Vector &x, Vector &y) const;
+ virtual void AssembleDiagonalPA(Vector& diag);
+
private:
#ifndef MFEM_THREAD_SAFE
Vector divshape;
#endif
+ // PA extension
+ Vector pa_data;
+ const DofToQuad *mapsO; ///< Not owned. DOF-to-quad map, open.
+ const DofToQuad *mapsC; ///< Not owned. DOF-to-quad map, closed.
+ const GeometricFactors *geom; ///< Not owned
+ int dim, ne, dofs1D, quad1D;
+
public:
DivDivIntegrator() { Q = NULL; }
DivDivIntegrator(Coefficient &q) : Q(&q) { }
@@ -2544,6 +2609,13 @@ class DGTraceIntegrator : public BilinearFormIntegrator
virtual void AddMultPA(const Vector&, Vector&) const;
+ virtual void AssembleEAInteriorFaces(const FiniteElementSpace& fes,
+ Vector &ea_data_int,
+ Vector &ea_data_ext);
+
+ virtual void AssembleEABoundaryFaces(const FiniteElementSpace& fes,
+ Vector &ea_data_bdr);
+
static const IntegrationRule &GetRule(Geometry::Type geom, int order,
FaceElementTransformations &T);
diff --git a/fem/bilininteg_convection_ea.cpp b/fem/bilininteg_convection_ea.cpp
new file mode 100644
index 00000000000..90630f7fa6c
--- /dev/null
+++ b/fem/bilininteg_convection_ea.cpp
@@ -0,0 +1,258 @@
+// Copyright (c) 2010-2020, 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 "../general/forall.hpp"
+#include "bilininteg.hpp"
+#include "gridfunc.hpp"
+
+namespace mfem
+{
+
+template
+static void EAConvectionAssemble1D(const int NE,
+ const Array &b,
+ const Array &g,
+ const Vector &padata,
+ Vector &eadata,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(b.Read(), Q1D, D1D);
+ auto G = Reshape(g.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, NE);
+ auto A = Reshape(eadata.Write(), D1D, D1D, NE);
+ MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_Gi[MQ1];
+ double r_Bj[MQ1];
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_Gi[q] = G(q,MFEM_THREAD_ID(x));
+ r_Bj[q] = B(q,MFEM_THREAD_ID(y));
+ }
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(j1,y,D1D)
+ {
+ double val = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ val += r_Bj[k1] * D(k1, e) * r_Gi[k1];
+ }
+ A(i1, j1, e) = val;
+ }
+ }
+ });
+}
+
+template
+static void EAConvectionAssemble2D(const int NE,
+ const Array &b,
+ const Array &g,
+ const Vector &padata,
+ Vector &eadata,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(b.Read(), Q1D, D1D);
+ auto G = Reshape(g.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, Q1D, 2, NE);
+ auto A = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, NE);
+ MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_B[MQ1][MD1];
+ double r_G[MQ1][MD1];
+ for (int d = 0; d < D1D; d++)
+ {
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_B[q][d] = B(q,d);
+ r_G[q][d] = G(q,d);
+ }
+ }
+ MFEM_SHARED double s_D[MQ1][MQ1][2];
+ MFEM_FOREACH_THREAD(k1,x,Q1D)
+ {
+ MFEM_FOREACH_THREAD(k2,y,Q1D)
+ {
+ s_D[k1][k2][0] = D(k1,k2,0,e);
+ s_D[k1][k2][1] = D(k1,k2,1,e);
+ }
+ }
+ MFEM_SYNC_THREAD;
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(i2,y,D1D)
+ {
+ for (int j1 = 0; j1 < D1D; ++j1)
+ {
+ for (int j2 = 0; j2 < D1D; ++j2)
+ {
+ double val = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ for (int k2 = 0; k2 < Q1D; ++k2)
+ {
+ val += (r_G[k1][i1] * r_B[k2][i2] * s_D[k1][k2][0]
+ + r_B[k1][i1] * r_G[k2][i2] * s_D[k1][k2][1])
+ * r_B[k1][j1]* r_B[k2][j2];
+ }
+ }
+ A(i1, i2, j1, j2, e) = val;
+ }
+ }
+ }
+ }
+ });
+}
+
+template
+static void EAConvectionAssemble3D(const int NE,
+ const Array &b,
+ const Array &g,
+ const Vector &padata,
+ Vector &eadata,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(b.Read(), Q1D, D1D);
+ auto G = Reshape(g.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, 3, NE);
+ auto A = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
+ MFEM_FORALL_3D(e, NE, D1D, D1D, D1D,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_B[MQ1][MD1];
+ double r_G[MQ1][MD1];
+ for (int d = 0; d < D1D; d++)
+ {
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_B[q][d] = B(q,d);
+ r_G[q][d] = G(q,d);
+ }
+ }
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(i2,y,D1D)
+ {
+ MFEM_FOREACH_THREAD(i3,z,D1D)
+ {
+ for (int j1 = 0; j1 < D1D; ++j1)
+ {
+ for (int j2 = 0; j2 < D1D; ++j2)
+ {
+ for (int j3 = 0; j3 < D1D; ++j3)
+ {
+ double val = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ for (int k2 = 0; k2 < Q1D; ++k2)
+ {
+ for (int k3 = 0; k3 < Q1D; ++k3)
+ {
+ double D0 = D(k1,k2,k3,0,e);
+ double D1 = D(k1,k2,k3,1,e);
+ double D2 = D(k1,k2,k3,2,e);
+ val += (r_G[k1][i1] * r_B[k2][i2] * r_B[k3][i3] * D0
+ + r_B[k1][i1] * r_G[k2][i2] * r_B[k3][i3] * D1
+ + r_B[k1][i1] * r_B[k2][i2] * r_G[k3][i3] * D2)
+ * r_B[k1][j1] * r_B[k2][j2] * r_B[k3][j3];
+ }
+ }
+ }
+ A(i1, i2, i3, j1, j2, j3, e) = val;
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+}
+
+void ConvectionIntegrator::AssembleEA(const FiniteElementSpace &fes,
+ Vector &ea_data)
+{
+ AssemblePA(fes);
+ const int ne = fes.GetMesh()->GetNE();
+ const Array &B = maps->B;
+ const Array &G = maps->G;
+ if (dim == 1)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x22: return EAConvectionAssemble1D<2,2>(ne,B,G,pa_data,ea_data);
+ case 0x33: return EAConvectionAssemble1D<3,3>(ne,B,G,pa_data,ea_data);
+ case 0x44: return EAConvectionAssemble1D<4,4>(ne,B,G,pa_data,ea_data);
+ case 0x55: return EAConvectionAssemble1D<5,5>(ne,B,G,pa_data,ea_data);
+ case 0x66: return EAConvectionAssemble1D<6,6>(ne,B,G,pa_data,ea_data);
+ case 0x77: return EAConvectionAssemble1D<7,7>(ne,B,G,pa_data,ea_data);
+ case 0x88: return EAConvectionAssemble1D<8,8>(ne,B,G,pa_data,ea_data);
+ case 0x99: return EAConvectionAssemble1D<9,9>(ne,B,G,pa_data,ea_data);
+ default: return EAConvectionAssemble1D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
+ }
+ }
+ else if (dim == 2)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x22: return EAConvectionAssemble2D<2,2>(ne,B,G,pa_data,ea_data);
+ case 0x33: return EAConvectionAssemble2D<3,3>(ne,B,G,pa_data,ea_data);
+ case 0x44: return EAConvectionAssemble2D<4,4>(ne,B,G,pa_data,ea_data);
+ case 0x55: return EAConvectionAssemble2D<5,5>(ne,B,G,pa_data,ea_data);
+ case 0x66: return EAConvectionAssemble2D<6,6>(ne,B,G,pa_data,ea_data);
+ case 0x77: return EAConvectionAssemble2D<7,7>(ne,B,G,pa_data,ea_data);
+ case 0x88: return EAConvectionAssemble2D<8,8>(ne,B,G,pa_data,ea_data);
+ case 0x99: return EAConvectionAssemble2D<9,9>(ne,B,G,pa_data,ea_data);
+ default: return EAConvectionAssemble2D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
+ }
+ }
+ else if (dim == 3)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x23: return EAConvectionAssemble3D<2,3>(ne,B,G,pa_data,ea_data);
+ case 0x34: return EAConvectionAssemble3D<3,4>(ne,B,G,pa_data,ea_data);
+ case 0x45: return EAConvectionAssemble3D<4,5>(ne,B,G,pa_data,ea_data);
+ case 0x56: return EAConvectionAssemble3D<5,6>(ne,B,G,pa_data,ea_data);
+ case 0x67: return EAConvectionAssemble3D<6,7>(ne,B,G,pa_data,ea_data);
+ case 0x78: return EAConvectionAssemble3D<7,8>(ne,B,G,pa_data,ea_data);
+ case 0x89: return EAConvectionAssemble3D<8,9>(ne,B,G,pa_data,ea_data);
+ default: return EAConvectionAssemble3D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
+ }
+ }
+ MFEM_ABORT("Unknown kernel.");
+}
+
+}
diff --git a/fem/bilininteg_convection.cpp b/fem/bilininteg_convection_pa.cpp
similarity index 100%
rename from fem/bilininteg_convection.cpp
rename to fem/bilininteg_convection_pa.cpp
diff --git a/fem/bilininteg_dgtrace_ea.cpp b/fem/bilininteg_dgtrace_ea.cpp
new file mode 100644
index 00000000000..e3a9bc93b9e
--- /dev/null
+++ b/fem/bilininteg_dgtrace_ea.cpp
@@ -0,0 +1,414 @@
+// Copyright (c) 2010-2020, 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 "../general/forall.hpp"
+#include "bilininteg.hpp"
+#include "gridfunc.hpp"
+
+namespace mfem
+{
+
+static void EADGTraceAssemble1DInt(const int NF,
+ const Array &basis,
+ const Vector &padata,
+ Vector &eadata_int,
+ Vector &eadata_ext)
+{
+ auto D = Reshape(padata.Read(), 2, 2, NF);
+ auto A_int = Reshape(eadata_int.ReadWrite(), 2, NF);
+ auto A_ext = Reshape(eadata_ext.ReadWrite(), 2, NF);
+ MFEM_FORALL(f, NF,
+ {
+ double val_int0, val_int1, val_ext01, val_ext10;
+ val_int0 = D(0, 0, f);
+ val_ext10 = D(1, 0, f);
+ val_ext01 = D(0, 1, f);
+ val_int1 = D(1, 1, f);
+ A_int(0, f) += val_int0;
+ A_int(1, f) += val_int1;
+ A_ext(0, f) += val_ext01;
+ A_ext(1, f) += val_ext10;
+ });
+}
+
+static void EADGTraceAssemble1DBdr(const int NF,
+ const Array &basis,
+ const Vector &padata,
+ Vector &eadata_bdr)
+{
+ auto D = Reshape(padata.Read(), 2, 2, NF);
+ auto A_bdr = Reshape(eadata_bdr.ReadWrite(), NF);
+ MFEM_FORALL(f, NF,
+ {
+ A_bdr(f) += D(0, 0, f);
+ });
+}
+
+template
+static void EADGTraceAssemble2DInt(const int NF,
+ const Array &basis,
+ const Vector &padata,
+ Vector &eadata_int,
+ Vector &eadata_ext,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(basis.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, 2, 2, NF);
+ auto A_int = Reshape(eadata_int.ReadWrite(), D1D, D1D, 2, NF);
+ auto A_ext = Reshape(eadata_ext.ReadWrite(), D1D, D1D, 2, NF);
+ MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(j1,y,D1D)
+ {
+ double val_int0 = 0.0;
+ double val_int1 = 0.0;
+ double val_ext01 = 0.0;
+ double val_ext10 = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ val_int0 += B(k1,i1) * B(k1,j1) * D(k1, 0, 0, f);
+ val_ext01 += B(k1,i1) * B(k1,j1) * D(k1, 0, 1, f);
+ val_ext10 += B(k1,i1) * B(k1,j1) * D(k1, 1, 0, f);
+ val_int1 += B(k1,i1) * B(k1,j1) * D(k1, 1, 1, f);
+ }
+ A_int(i1, j1, 0, f) += val_int0;
+ A_int(i1, j1, 1, f) += val_int1;
+ A_ext(i1, j1, 0, f) += val_ext01;
+ A_ext(i1, j1, 1, f) += val_ext10;
+ }
+ }
+ });
+}
+
+template
+static void EADGTraceAssemble2DBdr(const int NF,
+ const Array &basis,
+ const Vector &padata,
+ Vector &eadata_bdr,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(basis.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, 2, 2, NF);
+ auto A_bdr = Reshape(eadata_bdr.ReadWrite(), D1D, D1D, NF);
+ MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(j1,y,D1D)
+ {
+ double val_bdr = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ val_bdr += B(k1,i1) * B(k1,j1) * D(k1, 0, 0, f);
+ }
+ A_bdr(i1, j1, f) += val_bdr;
+ }
+ }
+ });
+}
+
+template
+static void EADGTraceAssemble3DInt(const int NF,
+ const Array &basis,
+ const Vector &padata,
+ Vector &eadata_int,
+ Vector &eadata_ext,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(basis.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, Q1D, 2, 2, NF);
+ auto A_int = Reshape(eadata_int.ReadWrite(), D1D, D1D, D1D, D1D, 2, NF);
+ auto A_ext = Reshape(eadata_ext.ReadWrite(), D1D, D1D, D1D, D1D, 2, NF);
+ MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_B[MQ1][MD1];
+ for (int d = 0; d < D1D; d++)
+ {
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_B[q][d] = B(q,d);
+ }
+ }
+ MFEM_SHARED double s_D[MQ1][MQ1][2][2];
+ for (int i=0; i < 2; i++)
+ {
+ for (int j=0; j < 2; j++)
+ {
+ MFEM_FOREACH_THREAD(k1,x,Q1D)
+ {
+ MFEM_FOREACH_THREAD(k2,y,Q1D)
+ {
+ s_D[k1][k2][i][j] = D(k1,k2,i,j,f);
+ }
+ }
+ }
+ }
+ MFEM_SYNC_THREAD;
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(i2,y,D1D)
+ {
+ for (int j1 = 0; j1 < D1D; ++j1)
+ {
+ for (int j2 = 0; j2 < D1D; ++j2)
+ {
+ double val_int0 = 0.0;
+ double val_int1 = 0.0;
+ double val_ext01 = 0.0;
+ double val_ext10 = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ for (int k2 = 0; k2 < Q1D; ++k2)
+ {
+ val_int0 += r_B[k1][i1] * r_B[k1][j1]
+ * r_B[k2][i2] * r_B[k2][j2]
+ * s_D[k1][k2][0][0];
+ val_int1 += r_B[k1][i1] * r_B[k1][j1]
+ * r_B[k2][i2] * r_B[k2][j2]
+ * s_D[k1][k2][1][1];
+ val_ext01+= r_B[k1][i1] * r_B[k1][j1]
+ * r_B[k2][i2] * r_B[k2][j2]
+ * s_D[k1][k2][0][1];
+ val_ext10+= r_B[k1][i1] * r_B[k1][j1]
+ * r_B[k2][i2] * r_B[k2][j2]
+ * s_D[k1][k2][1][0];
+ }
+ }
+ A_int(i1, i2, j1, j2, 0, f) += val_int0;
+ A_int(i1, i2, j1, j2, 1, f) += val_int1;
+ A_ext(i1, i2, j1, j2, 0, f) += val_ext01;
+ A_ext(i1, i2, j1, j2, 1, f) += val_ext10;
+ }
+ }
+ }
+ }
+ });
+}
+
+template
+static void EADGTraceAssemble3DBdr(const int NF,
+ const Array &basis,
+ const Vector &padata,
+ Vector &eadata_bdr,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(basis.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, Q1D, 2, 2, NF);
+ auto A_bdr = Reshape(eadata_bdr.ReadWrite(), D1D, D1D, D1D, D1D, NF);
+ MFEM_FORALL_3D(f, NF, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_B[MQ1][MD1];
+ for (int d = 0; d < D1D; d++)
+ {
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_B[q][d] = B(q,d);
+ }
+ }
+ MFEM_SHARED double s_D[MQ1][MQ1][2][2];
+ for (int i=0; i < 2; i++)
+ {
+ for (int j=0; j < 2; j++)
+ {
+ MFEM_FOREACH_THREAD(k1,x,Q1D)
+ {
+ MFEM_FOREACH_THREAD(k2,y,Q1D)
+ {
+ s_D[k1][k2][i][j] = D(k1,k2,i,j,f);
+ }
+ }
+ }
+ }
+ MFEM_SYNC_THREAD;
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(i2,y,D1D)
+ {
+ for (int j1 = 0; j1 < D1D; ++j1)
+ {
+ for (int j2 = 0; j2 < D1D; ++j2)
+ {
+ double val_bdr = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ for (int k2 = 0; k2 < Q1D; ++k2)
+ {
+ val_bdr += r_B[k1][i1] * r_B[k1][j1]
+ * r_B[k2][i2] * r_B[k2][j2]
+ * s_D[k1][k2][0][0];
+ }
+ }
+ A_bdr(i1, i2, j1, j2, f) += val_bdr;
+ }
+ }
+ }
+ }
+ });
+}
+
+void DGTraceIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace& fes,
+ Vector &ea_data_int,
+ Vector &ea_data_ext)
+{
+ SetupPA(fes, FaceType::Interior);
+ nf = fes.GetNFbyType(FaceType::Interior);
+ if (nf==0) { return; }
+ const Array &B = maps->B;
+ if (dim == 1)
+ {
+ return EADGTraceAssemble1DInt(nf,B,pa_data,ea_data_int,ea_data_ext);
+ }
+ else if (dim == 2)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x22:
+ return EADGTraceAssemble2DInt<2,2>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x33:
+ return EADGTraceAssemble2DInt<3,3>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x44:
+ return EADGTraceAssemble2DInt<4,4>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x55:
+ return EADGTraceAssemble2DInt<5,5>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x66:
+ return EADGTraceAssemble2DInt<6,6>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x77:
+ return EADGTraceAssemble2DInt<7,7>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x88:
+ return EADGTraceAssemble2DInt<8,8>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x99:
+ return EADGTraceAssemble2DInt<9,9>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ default:
+ return EADGTraceAssemble2DInt(nf,B,pa_data,ea_data_int,
+ ea_data_ext,dofs1D,quad1D);
+ }
+ }
+ else if (dim == 3)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x23:
+ return EADGTraceAssemble3DInt<2,3>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x34:
+ return EADGTraceAssemble3DInt<3,4>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x45:
+ return EADGTraceAssemble3DInt<4,5>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x56:
+ return EADGTraceAssemble3DInt<5,6>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x67:
+ return EADGTraceAssemble3DInt<6,7>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x78:
+ return EADGTraceAssemble3DInt<7,8>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ case 0x89:
+ return EADGTraceAssemble3DInt<8,9>(nf,B,pa_data,ea_data_int,
+ ea_data_ext);
+ default:
+ return EADGTraceAssemble3DInt(nf,B,pa_data,ea_data_int,
+ ea_data_ext,dofs1D,quad1D);
+ }
+ }
+ MFEM_ABORT("Unknown kernel.");
+}
+
+void DGTraceIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace& fes,
+ Vector &ea_data_bdr)
+{
+ SetupPA(fes, FaceType::Boundary);
+ nf = fes.GetNFbyType(FaceType::Boundary);
+ if (nf==0) { return; }
+ const Array &B = maps->B;
+ if (dim == 1)
+ {
+ return EADGTraceAssemble1DBdr(nf,B,pa_data,ea_data_bdr);
+ }
+ else if (dim == 2)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x22: return EADGTraceAssemble2DBdr<2,2>(nf,B,pa_data,ea_data_bdr);
+ case 0x33: return EADGTraceAssemble2DBdr<3,3>(nf,B,pa_data,ea_data_bdr);
+ case 0x44: return EADGTraceAssemble2DBdr<4,4>(nf,B,pa_data,ea_data_bdr);
+ case 0x55: return EADGTraceAssemble2DBdr<5,5>(nf,B,pa_data,ea_data_bdr);
+ case 0x66: return EADGTraceAssemble2DBdr<6,6>(nf,B,pa_data,ea_data_bdr);
+ case 0x77: return EADGTraceAssemble2DBdr<7,7>(nf,B,pa_data,ea_data_bdr);
+ case 0x88: return EADGTraceAssemble2DBdr<8,8>(nf,B,pa_data,ea_data_bdr);
+ case 0x99: return EADGTraceAssemble2DBdr<9,9>(nf,B,pa_data,ea_data_bdr);
+ default:
+ return EADGTraceAssemble2DBdr(nf,B,pa_data,ea_data_bdr,dofs1D,quad1D);
+ }
+ }
+ else if (dim == 3)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x23: return EADGTraceAssemble3DBdr<2,3>(nf,B,pa_data,ea_data_bdr);
+ case 0x34: return EADGTraceAssemble3DBdr<3,4>(nf,B,pa_data,ea_data_bdr);
+ case 0x45: return EADGTraceAssemble3DBdr<4,5>(nf,B,pa_data,ea_data_bdr);
+ case 0x56: return EADGTraceAssemble3DBdr<5,6>(nf,B,pa_data,ea_data_bdr);
+ case 0x67: return EADGTraceAssemble3DBdr<6,7>(nf,B,pa_data,ea_data_bdr);
+ case 0x78: return EADGTraceAssemble3DBdr<7,8>(nf,B,pa_data,ea_data_bdr);
+ case 0x89: return EADGTraceAssemble3DBdr<8,9>(nf,B,pa_data,ea_data_bdr);
+ default:
+ return EADGTraceAssemble3DBdr(nf,B,pa_data,ea_data_bdr,dofs1D,quad1D);
+ }
+ }
+ MFEM_ABORT("Unknown kernel.");
+}
+
+}
diff --git a/fem/bilininteg_dgtrace.cpp b/fem/bilininteg_dgtrace_pa.cpp
similarity index 100%
rename from fem/bilininteg_dgtrace.cpp
rename to fem/bilininteg_dgtrace_pa.cpp
diff --git a/fem/bilininteg_diffusion_ea.cpp b/fem/bilininteg_diffusion_ea.cpp
new file mode 100644
index 00000000000..2b40a7bb0cd
--- /dev/null
+++ b/fem/bilininteg_diffusion_ea.cpp
@@ -0,0 +1,275 @@
+// Copyright (c) 2010-2020, 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 "../general/forall.hpp"
+#include "bilininteg.hpp"
+#include "gridfunc.hpp"
+
+namespace mfem
+{
+
+template
+static void EADiffusionAssemble1D(const int NE,
+ const Array &b,
+ const Array &g,
+ const Vector &padata,
+ Vector &eadata,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto G = Reshape(g.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, NE);
+ auto A = Reshape(eadata.Write(), D1D, D1D, NE);
+ MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_Gi[MQ1];
+ double r_Gj[MQ1];
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_Gi[q] = G(q,MFEM_THREAD_ID(x));
+ r_Gj[q] = G(q,MFEM_THREAD_ID(y));
+ }
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(j1,y,D1D)
+ {
+ double val = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ val += r_Gj[k1] * D(k1, e) * r_Gi[k1];
+ }
+ A(i1, j1, e) = val;
+ }
+ }
+ });
+}
+
+template
+static void EADiffusionAssemble2D(const int NE,
+ const Array &b,
+ const Array &g,
+ const Vector &padata,
+ Vector &eadata,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(b.Read(), Q1D, D1D);
+ auto G = Reshape(g.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, Q1D, 3, NE);
+ auto A = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, NE);
+ MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_B[MQ1][MD1];
+ double r_G[MQ1][MD1];
+ for (int d = 0; d < D1D; d++)
+ {
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_B[q][d] = B(q,d);
+ r_G[q][d] = G(q,d);
+ }
+ }
+ MFEM_SYNC_THREAD;
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(i2,y,D1D)
+ {
+ for (int j1 = 0; j1 < D1D; ++j1)
+ {
+ for (int j2 = 0; j2 < D1D; ++j2)
+ {
+ double val = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ for (int k2 = 0; k2 < Q1D; ++k2)
+ {
+ double bgi = r_G[k1][i1] * r_B[k2][i2];
+ double gbi = r_B[k1][i1] * r_G[k2][i2];
+ double bgj = r_G[k1][j1] * r_B[k2][j2];
+ double gbj = r_B[k1][j1] * r_G[k2][j2];
+ double D00 = D(k1,k2,0,e);
+ double D10 = D(k1,k2,1,e);
+ double D01 = D10;
+ double D11 = D(k1,k2,2,e);
+ val += bgi * D00 * bgj
+ + gbi * D01 * bgj
+ + bgi * D10 * gbj
+ + gbi * D11 * gbj;
+ }
+ }
+ A(i1, i2, j1, j2, e) = val;
+ }
+ }
+ }
+ }
+ });
+}
+
+template
+static void EADiffusionAssemble3D(const int NE,
+ const Array &g,
+ const Array &b,
+ const Vector &padata,
+ Vector &eadata,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(b.Read(), Q1D, D1D);
+ auto G = Reshape(g.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, 6, NE);
+ auto A = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
+ MFEM_FORALL_3D(e, NE, D1D, D1D, D1D,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_B[MQ1][MD1];
+ double r_G[MQ1][MD1];
+ for (int d = 0; d < D1D; d++)
+ {
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_B[q][d] = B(q,d);
+ r_G[q][d] = G(q,d);
+ }
+ }
+ MFEM_SYNC_THREAD;
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(i2,y,D1D)
+ {
+ MFEM_FOREACH_THREAD(i3,z,D1D)
+ {
+ for (int j1 = 0; j1 < D1D; ++j1)
+ {
+ for (int j2 = 0; j2 < D1D; ++j2)
+ {
+ for (int j3 = 0; j3 < D1D; ++j3)
+ {
+ double val = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ for (int k2 = 0; k2 < Q1D; ++k2)
+ {
+ for (int k3 = 0; k3 < Q1D; ++k3)
+ {
+ double bbgi = r_G[k1][i1] * r_B[k2][i2] * r_B[k3][i3];
+ double bgbi = r_B[k1][i1] * r_G[k2][i2] * r_B[k3][i3];
+ double gbbi = r_B[k1][i1] * r_B[k2][i2] * r_G[k3][i3];
+ double bbgj = r_G[k1][j1] * r_B[k2][j2] * r_B[k3][j3];
+ double bgbj = r_B[k1][j1] * r_G[k2][j2] * r_B[k3][j3];
+ double gbbj = r_B[k1][j1] * r_B[k2][j2] * r_G[k3][j3];
+ double D00 = D(k1,k2,k3,0,e);
+ double D10 = D(k1,k2,k3,1,e);
+ double D20 = D(k1,k2,k3,2,e);
+ double D01 = D10;
+ double D11 = D(k1,k2,k3,3,e);
+ double D21 = D(k1,k2,k3,4,e);
+ double D02 = D20;
+ double D12 = D21;
+ double D22 = D(k1,k2,k3,5,e);
+ val += bbgi * D00 * bbgj
+ + bgbi * D10 * bbgj
+ + gbbi * D20 * bbgj
+ + bbgi * D01 * bgbj
+ + bgbi * D11 * bgbj
+ + gbbi * D21 * bgbj
+ + bbgi * D02 * gbbj
+ + bgbi * D12 * gbbj
+ + gbbi * D22 * gbbj;
+ }
+ }
+ }
+ A(i1, i2, i3, j1, j2, j3, e) = val;
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+}
+
+void DiffusionIntegrator::AssembleEA(const FiniteElementSpace &fes,
+ Vector &ea_data)
+{
+ AssemblePA(fes);
+ const int ne = fes.GetMesh()->GetNE();
+ const Array &B = maps->B;
+ const Array &G = maps->G;
+ if (dim == 1)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x22: return EADiffusionAssemble1D<2,2>(ne,B,G,pa_data,ea_data);
+ case 0x33: return EADiffusionAssemble1D<3,3>(ne,B,G,pa_data,ea_data);
+ case 0x44: return EADiffusionAssemble1D<4,4>(ne,B,G,pa_data,ea_data);
+ case 0x55: return EADiffusionAssemble1D<5,5>(ne,B,G,pa_data,ea_data);
+ case 0x66: return EADiffusionAssemble1D<6,6>(ne,B,G,pa_data,ea_data);
+ case 0x77: return EADiffusionAssemble1D<7,7>(ne,B,G,pa_data,ea_data);
+ case 0x88: return EADiffusionAssemble1D<8,8>(ne,B,G,pa_data,ea_data);
+ case 0x99: return EADiffusionAssemble1D<9,9>(ne,B,G,pa_data,ea_data);
+ default: return EADiffusionAssemble1D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
+ }
+ }
+ else if (dim == 2)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x22: return EADiffusionAssemble2D<2,2>(ne,B,G,pa_data,ea_data);
+ case 0x33: return EADiffusionAssemble2D<3,3>(ne,B,G,pa_data,ea_data);
+ case 0x44: return EADiffusionAssemble2D<4,4>(ne,B,G,pa_data,ea_data);
+ case 0x55: return EADiffusionAssemble2D<5,5>(ne,B,G,pa_data,ea_data);
+ case 0x66: return EADiffusionAssemble2D<6,6>(ne,B,G,pa_data,ea_data);
+ case 0x77: return EADiffusionAssemble2D<7,7>(ne,B,G,pa_data,ea_data);
+ case 0x88: return EADiffusionAssemble2D<8,8>(ne,B,G,pa_data,ea_data);
+ case 0x99: return EADiffusionAssemble2D<9,9>(ne,B,G,pa_data,ea_data);
+ default: return EADiffusionAssemble2D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
+ }
+ }
+ else if (dim == 3)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x23: return EADiffusionAssemble3D<2,3>(ne,B,G,pa_data,ea_data);
+ case 0x34: return EADiffusionAssemble3D<3,4>(ne,B,G,pa_data,ea_data);
+ case 0x45: return EADiffusionAssemble3D<4,5>(ne,B,G,pa_data,ea_data);
+ case 0x56: return EADiffusionAssemble3D<5,6>(ne,B,G,pa_data,ea_data);
+ case 0x67: return EADiffusionAssemble3D<6,7>(ne,B,G,pa_data,ea_data);
+ case 0x78: return EADiffusionAssemble3D<7,8>(ne,B,G,pa_data,ea_data);
+ case 0x89: return EADiffusionAssemble3D<8,9>(ne,B,G,pa_data,ea_data);
+ default: return EADiffusionAssemble3D(ne,B,G,pa_data,ea_data,dofs1D,quad1D);
+ }
+ }
+ MFEM_ABORT("Unknown kernel.");
+}
+
+}
diff --git a/fem/bilininteg_diffusion.cpp b/fem/bilininteg_diffusion_pa.cpp
similarity index 100%
rename from fem/bilininteg_diffusion.cpp
rename to fem/bilininteg_diffusion_pa.cpp
diff --git a/fem/bilininteg_hcurl.cpp b/fem/bilininteg_hcurl.cpp
index 3ee6f63aed3..3dd3f8209bc 100644
--- a/fem/bilininteg_hcurl.cpp
+++ b/fem/bilininteg_hcurl.cpp
@@ -24,12 +24,12 @@ constexpr int HCURL_MAX_D1D = 5;
constexpr int HCURL_MAX_Q1D = 6;
// PA H(curl) Mass Assemble 2D kernel
-static void PAHcurlSetup2D(const int Q1D,
- const int NE,
- const Array &w,
- const Vector &j,
- Vector &_coeff,
- Vector &op)
+void PAHcurlSetup2D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op)
{
const int NQ = Q1D*Q1D;
auto W = w.Read();
@@ -55,12 +55,12 @@ static void PAHcurlSetup2D(const int Q1D,
}
// PA H(curl) Mass Assemble 3D kernel
-static void PAHcurlSetup3D(const int Q1D,
- const int NE,
- const Array &w,
- const Vector &j,
- Vector &_coeff,
- Vector &op)
+void PAHcurlSetup3D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op)
{
const int NQ = Q1D*Q1D*Q1D;
auto W = w.Read();
@@ -106,78 +106,16 @@ static void PAHcurlSetup3D(const int Q1D,
});
}
-void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
-{
- // Assumes tensor-product elements
- Mesh *mesh = fes.GetMesh();
- const FiniteElement *fel = fes.GetFE(0);
-
- const VectorTensorFiniteElement *el =
- dynamic_cast(fel);
- MFEM_VERIFY(el != NULL, "Only VectorTensorFiniteElement is supported!");
-
- const IntegrationRule *ir
- = IntRule ? IntRule : &MassIntegrator::GetRule(*el, *el,
- *mesh->GetElementTransformation(0));
- const int dims = el->GetDim();
- MFEM_VERIFY(dims == 2 || dims == 3, "");
-
- const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
- const int nq = ir->GetNPoints();
- dim = mesh->Dimension();
- MFEM_VERIFY(dim == 2 || dim == 3, "");
-
- ne = fes.GetNE();
- geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
- mapsC = &el->GetDofToQuad(*ir, DofToQuad::TENSOR);
- mapsO = &el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
- dofs1D = mapsC->ndof;
- quad1D = mapsC->nqpt;
-
- MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
-
- pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
-
- Vector coeff(ne * nq);
- coeff = 1.0;
- if (Q)
- {
- for (int e=0; eGetElementTransformation(e);
- for (int p=0; pEval(*tr, ir->IntPoint(p));
- }
- }
- }
-
- if (el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
- {
- PAHcurlSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
- coeff, pa_data);
- }
- else if (el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
- {
- PAHcurlSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
- coeff, pa_data);
- }
- else
- {
- MFEM_ABORT("Unknown kernel.");
- }
-}
-
-static void PAHcurlMassApply2D(const int D1D,
- const int Q1D,
- const int NE,
- const Array &_Bo,
- const Array &_Bc,
- const Array &_Bot,
- const Array &_Bct,
- const Vector &_op,
- const Vector &_x,
- Vector &_y)
+void PAHcurlMassApply2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
{
constexpr static int VDIM = 2;
@@ -294,13 +232,13 @@ static void PAHcurlMassApply2D(const int D1D,
}); // end of element loop
}
-static void PAHcurlMassAssembleDiagonal2D(const int D1D,
- const int Q1D,
- const int NE,
- const Array &_Bo,
- const Array &_Bc,
- const Vector &_op,
- Vector &_diag)
+void PAHcurlMassAssembleDiagonal2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Vector &_op,
+ Vector &_diag)
{
constexpr static int VDIM = 2;
@@ -348,15 +286,17 @@ static void PAHcurlMassAssembleDiagonal2D(const int D1D,
}); // end of element loop
}
-template
-static void PAHcurlMassAssembleDiagonal3D(const int D1D,
- const int Q1D,
- const int NE,
- const Array &_Bo,
- const Array &_Bc,
- const Vector &_op,
- Vector &_diag)
+void PAHcurlMassAssembleDiagonal3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Vector &_op,
+ Vector &_diag)
{
+ constexpr static int MAX_D1D = HCURL_MAX_D1D;
+ constexpr static int MAX_Q1D = HCURL_MAX_Q1D;
+
MFEM_VERIFY(D1D <= MAX_D1D, "Error: D1D > MAX_D1D");
MFEM_VERIFY(Q1D <= MAX_Q1D, "Error: Q1D > MAX_Q1D");
constexpr static int VDIM = 3;
@@ -416,28 +356,20 @@ static void PAHcurlMassAssembleDiagonal3D(const int D1D,
}); // end of element loop
}
-void VectorFEMassIntegrator::AssembleDiagonalPA(Vector& diag)
+void PAHcurlMassApply3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
{
- if (dim == 3)
- PAHcurlMassAssembleDiagonal3D(dofs1D, quad1D, ne,
- mapsO->B, mapsC->B, pa_data, diag);
- else
- PAHcurlMassAssembleDiagonal2D(dofs1D, quad1D, ne,
- mapsO->B, mapsC->B, pa_data, diag);
-}
+ constexpr static int MAX_D1D = HCURL_MAX_D1D;
+ constexpr static int MAX_Q1D = HCURL_MAX_Q1D;
-template
-static void PAHcurlMassApply3D(const int D1D,
- const int Q1D,
- const int NE,
- const Array &_Bo,
- const Array &_Bc,
- const Array &_Bot,
- const Array &_Bct,
- const Vector &_op,
- const Vector &_x,
- Vector &_y)
-{
MFEM_VERIFY(D1D <= MAX_D1D, "Error: D1D > MAX_D1D");
MFEM_VERIFY(Q1D <= MAX_Q1D, "Error: Q1D > MAX_Q1D");
constexpr static int VDIM = 3;
@@ -615,20 +547,6 @@ static void PAHcurlMassApply3D(const int D1D,
}); // end of element loop
}
-void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
-{
- if (dim == 3)
- {
- PAHcurlMassApply3D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
- mapsC->Bt, pa_data, x, y);
- }
- else
- {
- PAHcurlMassApply2D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
- mapsC->Bt, pa_data, x, y);
- }
-}
-
// PA H(curl) curl-curl assemble 2D kernel
static void PACurlCurlSetup2D(const int Q1D,
const int NE,
@@ -1678,92 +1596,25 @@ void CurlCurlIntegrator::AssembleDiagonalPA(Vector& diag)
}
}
-void MixedVectorGradientIntegrator::AssemblePA(const FiniteElementSpace
- &trial_fes,
- const FiniteElementSpace &test_fes)
-{
- // Assumes tensor-product elements, with a vector test space and H^1 trial space.
- Mesh *mesh = trial_fes.GetMesh();
- const FiniteElement *trial_fel = trial_fes.GetFE(0);
- const FiniteElement *test_fel = test_fes.GetFE(0);
-
- const NodalTensorFiniteElement *trial_el =
- dynamic_cast(trial_fel);
- MFEM_VERIFY(trial_el != NULL, "Only NodalTensorFiniteElement is supported!");
-
- const VectorTensorFiniteElement *test_el =
- dynamic_cast(test_fel);
- MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
-
- const IntegrationRule *ir
- = IntRule ? IntRule : &MassIntegrator::GetRule(*trial_el, *trial_el,
- *mesh->GetElementTransformation(0));
- const int dims = trial_el->GetDim();
- MFEM_VERIFY(dims == 2 || dims == 3, "");
-
- const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
- const int nq = ir->GetNPoints();
- dim = mesh->Dimension();
- MFEM_VERIFY(dim == 2 || dim == 3, "");
-
- MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(), "");
-
- ne = trial_fes.GetNE();
- geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
- mapsC = &test_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
- mapsO = &test_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
- dofs1D = mapsC->ndof;
- quad1D = mapsC->nqpt;
-
- MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
-
- pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
-
- Vector coeff(ne * nq);
- coeff = 1.0;
- if (Q)
- {
- for (int e=0; eGetElementTransformation(e);
- for (int p=0; pEval(*tr, ir->IntPoint(p));
- }
- }
- }
-
- // Use the same setup functions as VectorFEMassIntegrator.
- if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
- {
- PAHcurlSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
- coeff, pa_data);
- }
- else if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
- {
- PAHcurlSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
- coeff, pa_data);
- }
- else
- {
- MFEM_ABORT("Unknown kernel.");
- }
-}
-
// Apply to x corresponding to DOF's in H^1 (trial), whose gradients are integrated
// against H(curl) test functions corresponding to y.
-template
-static void PAHcurlH1Apply3D(const int D1D,
- const int Q1D,
- const int NE,
- const Array &_Bc,
- const Array &_Gc,
- const Array &_Bot,
- const Array &_Bct,
- const Vector &_op,
- const Vector &_x,
- Vector &_y)
+void PAHcurlH1Apply3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bc,
+ const Array &_Gc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
{
+ constexpr static int MAX_D1D = HCURL_MAX_D1D;
+ constexpr static int MAX_Q1D = HCURL_MAX_Q1D;
+
+ MFEM_VERIFY(D1D <= MAX_D1D, "Error: D1D > MAX_D1D");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "Error: Q1D > MAX_Q1D");
+
constexpr static int VDIM = 3;
auto Bc = Reshape(_Bc.Read(), Q1D, D1D);
@@ -1937,16 +1788,16 @@ static void PAHcurlH1Apply3D(const int D1D,
// Apply to x corresponding to DOF's in H^1 (trial), whose gradients are integrated
// against H(curl) test functions corresponding to y.
-static void PAHcurlH1Apply2D(const int D1D,
- const int Q1D,
- const int NE,
- const Array &_Bc,
- const Array &_Gc,
- const Array &_Bot,
- const Array &_Bct,
- const Vector &_op,
- const Vector &_x,
- Vector &_y)
+void PAHcurlH1Apply2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bc,
+ const Array &_Gc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
{
constexpr static int VDIM = 2;
@@ -2057,18 +1908,4 @@ static void PAHcurlH1Apply2D(const int D1D,
}); // end of element loop
}
-void MixedVectorGradientIntegrator::AddMultPA(const Vector &x, Vector &y) const
-{
- if (dim == 3)
- PAHcurlH1Apply3D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
- mapsO->Bt, mapsC->Bt, pa_data, x, y);
- else if (dim == 2)
- PAHcurlH1Apply2D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
- mapsO->Bt, mapsC->Bt, pa_data, x, y);
- else
- {
- MFEM_ABORT("Unsupported dimension!");
- }
-}
-
} // namespace mfem
diff --git a/fem/bilininteg_hdiv.cpp b/fem/bilininteg_hdiv.cpp
new file mode 100644
index 00000000000..59c14d1c293
--- /dev/null
+++ b/fem/bilininteg_hdiv.cpp
@@ -0,0 +1,2022 @@
+// Copyright (c) 2010-2020, 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 "../general/forall.hpp"
+#include "bilininteg.hpp"
+#include "gridfunc.hpp"
+#include "libceed/mass.hpp"
+
+using namespace std;
+
+
+// Piola transformation in H(div): w = (1 / det (dF)) dF \hat{w}
+// div w = (1 / det (dF)) \hat{div} \hat{w}
+
+namespace mfem
+{
+
+// Local maximum size of dofs and quads in 1D
+constexpr int HDIV_MAX_D1D = 5;
+constexpr int HDIV_MAX_Q1D = 6;
+
+
+// PA H(div) Mass Assemble 2D kernel
+void PAHdivSetup2D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op)
+{
+ const int NQ = Q1D*Q1D;
+ auto W = w.Read();
+
+ auto J = Reshape(j.Read(), NQ, 2, 2, NE);
+ auto coeff = Reshape(_coeff.Read(), NQ, NE);
+ auto y = Reshape(op.Write(), NQ, 3, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ for (int q = 0; q < NQ; ++q)
+ {
+ const double J11 = J(q,0,0,e);
+ const double J21 = J(q,1,0,e);
+ const double J12 = J(q,0,1,e);
+ const double J22 = J(q,1,1,e);
+ const double c_detJ = W[q] * coeff(q, e) / ((J11*J22)-(J21*J12));
+ // (c/detJ) J^T J
+ y(q,0,e) = c_detJ * (J11*J11 + J21*J21); // 1,1
+ y(q,1,e) = c_detJ * (J11*J12 + J21*J22); // 1,2
+ y(q,2,e) = c_detJ * (J12*J12 + J22*J22); // 2,2
+ }
+ });
+}
+
+// PA H(div) Mass Assemble 3D kernel
+void PAHdivSetup3D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op)
+{
+ const int NQ = Q1D*Q1D*Q1D;
+ auto W = w.Read();
+ auto J = Reshape(j.Read(), NQ, 3, 3, NE);
+ auto coeff = Reshape(_coeff.Read(), NQ, NE);
+ auto y = Reshape(op.Write(), NQ, 6, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ for (int q = 0; q < NQ; ++q)
+ {
+ const double J11 = J(q,0,0,e);
+ const double J21 = J(q,1,0,e);
+ const double J31 = J(q,2,0,e);
+ const double J12 = J(q,0,1,e);
+ const double J22 = J(q,1,1,e);
+ const double J32 = J(q,2,1,e);
+ const double J13 = J(q,0,2,e);
+ const double J23 = J(q,1,2,e);
+ const double J33 = J(q,2,2,e);
+ const double detJ = J11 * (J22 * J33 - J32 * J23) -
+ /* */ J21 * (J12 * J33 - J32 * J13) +
+ /* */ J31 * (J12 * J23 - J22 * J13);
+ const double c_detJ = W[q] * coeff(q, e) / detJ;
+ // (c/detJ) J^T J
+ y(q,0,e) = c_detJ * (J11*J11 + J21*J21 + J31*J31); // 1,1
+ y(q,1,e) = c_detJ * (J12*J11 + J22*J21 + J32*J31); // 2,1
+ y(q,2,e) = c_detJ * (J13*J11 + J23*J21 + J33*J31); // 3,1
+ y(q,3,e) = c_detJ * (J12*J12 + J22*J22 + J32*J32); // 2,2
+ y(q,4,e) = c_detJ * (J13*J12 + J23*J22 + J33*J32); // 3,2
+ y(q,5,e) = c_detJ * (J13*J13 + J23*J23 + J33*J33); // 3,3
+ }
+ });
+}
+
+void PAHdivMassApply2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
+{
+ constexpr static int VDIM = 2;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Bc = Reshape(_Bc.Read(), Q1D, D1D);
+ auto Bot = Reshape(_Bot.Read(), D1D-1, Q1D);
+ auto Bct = Reshape(_Bct.Read(), D1D, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, 3, NE);
+ auto x = Reshape(_x.Read(), 2*(D1D-1)*D1D, NE);
+ auto y = Reshape(_y.ReadWrite(), 2*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ double mass[MAX_Q1D][MAX_Q1D][VDIM];
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int c = 0; c < VDIM; ++c)
+ {
+ mass[qy][qx][c] = 0.0;
+ }
+ }
+ }
+
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y components
+ {
+ const int D1Dx = (c == 1) ? D1D - 1 : D1D;
+ const int D1Dy = (c == 0) ? D1D - 1 : D1D;
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ double massX[MAX_Q1D];
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ massX[qx] = 0.0;
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ const double t = x(dx + (dy * D1Dx) + osc, e);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ massX[qx] += t * ((c == 0) ? Bc(qx,dx) : Bo(qx,dx));
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 1) ? Bc(qy,dy) : Bo(qy,dy);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ mass[qy][qx][c] += massX[qx] * wy;
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy;
+ } // loop (c) over components
+
+ // Apply D operator.
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ const double O11 = op(qx,qy,0,e);
+ const double O12 = op(qx,qy,1,e);
+ const double O22 = op(qx,qy,2,e);
+ const double massX = mass[qy][qx][0];
+ const double massY = mass[qy][qx][1];
+ mass[qy][qx][0] = (O11*massX)+(O12*massY);
+ mass[qy][qx][1] = (O12*massX)+(O22*massY);
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y components
+ {
+ const int D1Dx = (c == 1) ? D1D - 1 : D1D;
+ const int D1Dy = (c == 0) ? D1D - 1 : D1D;
+
+ double massX[MAX_D1D];
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ massX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ massX[dx] += mass[qy][qx][c] * ((c == 0) ? Bct(dx,qx) :
+ Bot(dx,qx));
+ }
+ }
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ const double wy = (c == 1) ? Bct(dy,qy) : Bot(dy,qy);
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ y(dx + (dy * D1Dx) + osc, e) += massX[dx] * wy;
+ }
+ }
+
+ osc += D1Dx * D1Dy;
+ } // loop c
+ } // loop qy
+ }); // end of element loop
+}
+
+void PAHdivMassAssembleDiagonal2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Vector &_op,
+ Vector &_diag)
+{
+ constexpr static int VDIM = 2;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Bc = Reshape(_Bc.Read(), Q1D, D1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, 3, NE);
+ auto diag = Reshape(_diag.ReadWrite(), 2*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y components
+ {
+ const int D1Dx = (c == 1) ? D1D - 1 : D1D;
+ const int D1Dy = (c == 0) ? D1D - 1 : D1D;
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ double mass[MAX_Q1D];
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ mass[qx] = 0.0;
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 1) ? Bc(qy,dy) : Bo(qy,dy);
+ mass[qx] += wy*wy*((c == 0) ? op(qx,qy,0,e) : op(qx,qy,2,e));
+ }
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ double val = 0.0;
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ const double wx = (c == 0) ? Bc(qx,dx) : Bo(qx,dx);
+ val += mass[qx] * wx * wx;
+ }
+ diag(dx + (dy * D1Dx) + osc, e) += val;
+ }
+ }
+
+ osc += D1Dx * D1Dy;
+ } // loop (c) over components
+ }); // end of element loop
+}
+
+void PAHdivMassAssembleDiagonal3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Vector &_op,
+ Vector &_diag)
+{
+ MFEM_VERIFY(D1D <= HDIV_MAX_D1D, "Error: D1D > HDIV_MAX_D1D");
+ MFEM_VERIFY(Q1D <= HDIV_MAX_Q1D, "Error: Q1D > HDIV_MAX_Q1D");
+ constexpr static int VDIM = 3;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Bc = Reshape(_Bc.Read(), Q1D, D1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, 6, NE);
+ auto diag = Reshape(_diag.ReadWrite(), 3*(D1D-1)*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dz = (c == 2) ? D1D : D1D - 1;
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ const int opc = (c == 0) ? 0 : ((c == 1) ? 3 : 5);
+
+ double mass[HDIV_MAX_Q1D];
+
+ for (int dz = 0; dz < D1Dz; ++dz)
+ {
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ mass[qx] = 0.0;
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 1) ? Bc(qy,dy) : Bo(qy,dy);
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ const double wz = (c == 2) ? Bc(qz,dz) : Bo(qz,dz);
+ mass[qx] += wy * wy * wz * wz * op(qx,qy,qz,opc,e);
+ }
+ }
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ double val = 0.0;
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ const double wx = (c == 0) ? Bc(qx,dx) : Bo(qx,dx);
+ val += mass[qx] * wx * wx;
+ }
+ diag(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) += val;
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy * D1Dz;
+ } // loop c
+ }); // end of element loop
+}
+
+void PAHdivMassApply3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
+{
+ MFEM_VERIFY(D1D <= HDIV_MAX_D1D, "Error: D1D > HDIV_MAX_D1D");
+ MFEM_VERIFY(Q1D <= HDIV_MAX_Q1D, "Error: Q1D > HDIV_MAX_Q1D");
+ constexpr static int VDIM = 3;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Bc = Reshape(_Bc.Read(), Q1D, D1D);
+ auto Bot = Reshape(_Bot.Read(), D1D-1, Q1D);
+ auto Bct = Reshape(_Bct.Read(), D1D, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, 6, NE);
+ auto x = Reshape(_x.Read(), 3*(D1D-1)*(D1D-1)*D1D, NE);
+ auto y = Reshape(_y.ReadWrite(), 3*(D1D-1)*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ double mass[HDIV_MAX_Q1D][HDIV_MAX_Q1D][HDIV_MAX_Q1D][VDIM];
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int c = 0; c < VDIM; ++c)
+ {
+ mass[qz][qy][qx][c] = 0.0;
+ }
+ }
+ }
+ }
+
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dz = (c == 2) ? D1D : D1D - 1;
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dz = 0; dz < D1Dz; ++dz)
+ {
+ double massXY[HDIV_MAX_Q1D][HDIV_MAX_Q1D];
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ massXY[qy][qx] = 0.0;
+ }
+ }
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ double massX[HDIV_MAX_Q1D];
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ massX[qx] = 0.0;
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ const double t = x(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ massX[qx] += t * ((c == 0) ? Bc(qx,dx) : Bo(qx,dx));
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 1) ? Bc(qy,dy) : Bo(qy,dy);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ const double wx = massX[qx];
+ massXY[qy][qx] += wx * wy;
+ }
+ }
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ const double wz = (c == 2) ? Bc(qz,dz) : Bo(qz,dz);
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ mass[qz][qy][qx][c] += massXY[qy][qx] * wz;
+ }
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy * D1Dz;
+ } // loop (c) over components
+
+ // Apply D operator.
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ const double O11 = op(qx,qy,qz,0,e);
+ const double O12 = op(qx,qy,qz,1,e);
+ const double O13 = op(qx,qy,qz,2,e);
+ const double O22 = op(qx,qy,qz,3,e);
+ const double O23 = op(qx,qy,qz,4,e);
+ const double O33 = op(qx,qy,qz,5,e);
+ const double massX = mass[qz][qy][qx][0];
+ const double massY = mass[qz][qy][qx][1];
+ const double massZ = mass[qz][qy][qx][2];
+ mass[qz][qy][qx][0] = (O11*massX)+(O12*massY)+(O13*massZ);
+ mass[qz][qy][qx][1] = (O12*massX)+(O22*massY)+(O23*massZ);
+ mass[qz][qy][qx][2] = (O13*massX)+(O23*massY)+(O33*massZ);
+ }
+ }
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ double massXY[HDIV_MAX_D1D][HDIV_MAX_D1D];
+
+ osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dz = (c == 2) ? D1D : D1D - 1;
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ massXY[dy][dx] = 0;
+ }
+ }
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ double massX[HDIV_MAX_D1D];
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ massX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ massX[dx] += mass[qz][qy][qx][c] *
+ ((c == 0) ? Bct(dx,qx) : Bot(dx,qx));
+ }
+ }
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ const double wy = (c == 1) ? Bct(dy,qy) : Bot(dy,qy);
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ massXY[dy][dx] += massX[dx] * wy;
+ }
+ }
+ }
+
+ for (int dz = 0; dz < D1Dz; ++dz)
+ {
+ const double wz = (c == 2) ? Bct(dz,qz) : Bot(dz,qz);
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) +=
+ massXY[dy][dx] * wz;
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy * D1Dz;
+ } // loop c
+ } // loop qz
+ }); // end of element loop
+}
+
+// PA H(div) div-div assemble 2D kernel
+// NOTE: this is identical to PACurlCurlSetup3D
+static void PADivDivSetup2D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op)
+{
+ const int NQ = Q1D*Q1D;
+ auto W = w.Read();
+ auto J = Reshape(j.Read(), NQ, 2, 2, NE);
+ auto coeff = Reshape(_coeff.Read(), NQ, NE);
+ auto y = Reshape(op.Write(), NQ, NE);
+ MFEM_FORALL(e, NE,
+ {
+ for (int q = 0; q < NQ; ++q)
+ {
+ const double J11 = J(q,0,0,e);
+ const double J21 = J(q,1,0,e);
+ const double J12 = J(q,0,1,e);
+ const double J22 = J(q,1,1,e);
+ const double detJ = (J11*J22)-(J21*J12);
+ y(q,e) = W[q] * coeff(q,e) / detJ;
+ }
+ });
+}
+
+static void PADivDivSetup3D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op)
+{
+ const int NQ = Q1D*Q1D*Q1D;
+ auto W = w.Read();
+ auto J = Reshape(j.Read(), NQ, 3, 3, NE);
+ auto coeff = Reshape(_coeff.Read(), NQ, NE);
+ auto y = Reshape(op.Write(), NQ, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ for (int q = 0; q < NQ; ++q)
+ {
+ const double J11 = J(q,0,0,e);
+ const double J21 = J(q,1,0,e);
+ const double J31 = J(q,2,0,e);
+ const double J12 = J(q,0,1,e);
+ const double J22 = J(q,1,1,e);
+ const double J32 = J(q,2,1,e);
+ const double J13 = J(q,0,2,e);
+ const double J23 = J(q,1,2,e);
+ const double J33 = J(q,2,2,e);
+ const double detJ = J11 * (J22 * J33 - J32 * J23) -
+ /* */ J21 * (J12 * J33 - J32 * J13) +
+ /* */ J31 * (J12 * J23 - J22 * J13);
+ y(q,e) = W[q] * coeff(q, e) / detJ;
+ }
+ });
+}
+
+static void PADivDivApply2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Gc,
+ const Array &_Bot,
+ const Array &_Gct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
+{
+ constexpr static int VDIM = 2;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Bot = Reshape(_Bot.Read(), D1D-1, Q1D);
+ auto Gc = Reshape(_Gc.Read(), Q1D, D1D);
+ auto Gct = Reshape(_Gct.Read(), D1D, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
+ auto x = Reshape(_x.Read(), 2*(D1D-1)*D1D, NE);
+ auto y = Reshape(_y.ReadWrite(), 2*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ double div[MAX_Q1D][MAX_Q1D];
+
+ // div[qy][qx] will be computed as du_x/dx + du_y/dy
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] = 0;
+ }
+ }
+
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y components
+ {
+ const int D1Dx = (c == 1) ? D1D - 1 : D1D;
+ const int D1Dy = (c == 0) ? D1D - 1 : D1D;
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ double gradX[MAX_Q1D];
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ gradX[qx] = 0;
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ const double t = x(dx + (dy * D1Dx) + osc, e);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ gradX[qx] += t * ((c == 0) ? Gc(qx,dx) : Bo(qx,dx));
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 0) ? Bo(qy,dy) : Gc(qy,dy);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] += gradX[qx] * wy;
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy;
+ } // loop (c) over components
+
+ // Apply D operator.
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] *= op(qx,qy,e);
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y components
+ {
+ const int D1Dx = (c == 1) ? D1D - 1 : D1D;
+ const int D1Dy = (c == 0) ? D1D - 1 : D1D;
+
+ double gradX[MAX_D1D];
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ gradX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ gradX[dx] += div[qy][qx] * (c == 0 ? Gct(dx,qx) : Bot(dx,qx));
+ }
+ }
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ const double wy = (c == 0) ? Bot(dy,qy) : Gct(dy,qy);
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ y(dx + (dy * D1Dx) + osc, e) += gradX[dx] * wy;
+ }
+ }
+
+ osc += D1Dx * D1Dy;
+ } // loop c
+ } // loop qy
+ }); // end of element loop
+}
+
+static void PADivDivApply3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Gc,
+ const Array &_Bot,
+ const Array &_Gct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
+{
+ MFEM_VERIFY(D1D <= HDIV_MAX_D1D, "Error: D1D > HDIV_MAX_D1D");
+ MFEM_VERIFY(Q1D <= HDIV_MAX_Q1D, "Error: Q1D > HDIV_MAX_Q1D");
+ constexpr static int VDIM = 3;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Gc = Reshape(_Gc.Read(), Q1D, D1D);
+ auto Bot = Reshape(_Bot.Read(), D1D-1, Q1D);
+ auto Gct = Reshape(_Gct.Read(), D1D, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
+ auto x = Reshape(_x.Read(), 3*(D1D-1)*(D1D-1)*D1D, NE);
+ auto y = Reshape(_y.ReadWrite(), 3*(D1D-1)*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ double div[HDIV_MAX_Q1D][HDIV_MAX_Q1D][HDIV_MAX_Q1D];
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] = 0.0;
+ }
+ }
+ }
+
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dz = (c == 2) ? D1D : D1D - 1;
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dz = 0; dz < D1Dz; ++dz)
+ {
+ double aXY[HDIV_MAX_Q1D][HDIV_MAX_Q1D];
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aXY[qy][qx] = 0.0;
+ }
+ }
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ double aX[HDIV_MAX_Q1D];
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] = 0.0;
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ const double t = x(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] += t * ((c == 0) ? Gc(qx,dx) : Bo(qx,dx));
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 1) ? Gc(qy,dy) : Bo(qy,dy);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ const double wx = aX[qx];
+ aXY[qy][qx] += wx * wy;
+ }
+ }
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ const double wz = (c == 2) ? Gc(qz,dz) : Bo(qz,dz);
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] += aXY[qy][qx] * wz;
+ }
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy * D1Dz;
+ } // loop (c) over components
+
+ // Apply D operator.
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] *= op(qx,qy,qz,e);
+ }
+ }
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ double aXY[HDIV_MAX_D1D][HDIV_MAX_D1D];
+
+ osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dz = (c == 2) ? D1D : D1D - 1;
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aXY[dy][dx] = 0;
+ }
+ }
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ double aX[HDIV_MAX_D1D];
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] += div[qz][qy][qx] *
+ (c == 0 ? Gct(dx,qx) : Bot(dx,qx));
+ }
+ }
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ const double wy = (c == 1) ? Gct(dy,qy) : Bot(dy,qy);
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aXY[dy][dx] += aX[dx] * wy;
+ }
+ }
+ }
+
+ for (int dz = 0; dz < D1Dz; ++dz)
+ {
+ const double wz = (c == 2) ? Gct(dz,qz) : Bot(dz,qz);
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) +=
+ aXY[dy][dx] * wz;
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy * D1Dz;
+ } // loop c
+ } // loop qz
+ }); // end of element loop
+}
+
+void DivDivIntegrator::AssemblePA(const FiniteElementSpace &fes)
+{
+ // Assumes tensor-product elements
+ Mesh *mesh = fes.GetMesh();
+ const FiniteElement *fel = fes.GetFE(0);
+
+ const VectorTensorFiniteElement *el =
+ dynamic_cast(fel);
+ MFEM_VERIFY(el != NULL, "Only VectorTensorFiniteElement is supported!");
+
+ const IntegrationRule *ir = IntRule ? IntRule : &MassIntegrator::GetRule
+ (*el, *el, *mesh->GetElementTransformation(0));
+
+ const int dims = el->GetDim();
+ MFEM_VERIFY(dims == 2 || dims == 3, "");
+
+ const int nq = ir->GetNPoints();
+ dim = mesh->Dimension();
+ MFEM_VERIFY(dim == 2 || dim == 3, "");
+
+ ne = fes.GetNE();
+ geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
+ mapsC = &el->GetDofToQuad(*ir, DofToQuad::TENSOR);
+ mapsO = &el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
+ dofs1D = mapsC->ndof;
+ quad1D = mapsC->nqpt;
+
+ MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
+
+ pa_data.SetSize(nq * ne, Device::GetMemoryType());
+
+ Vector coeff(ne * nq);
+ coeff = 1.0;
+ if (Q)
+ {
+ for (int e=0; eGetElementTransformation(e);
+ for (int p=0; pEval(*tr, ir->IntPoint(p));
+ }
+ }
+ }
+
+ if (el->GetDerivType() == mfem::FiniteElement::DIV && dim == 3)
+ {
+ PADivDivSetup3D(quad1D, ne, ir->GetWeights(), geom->J, coeff, pa_data);
+ }
+ else if (el->GetDerivType() == mfem::FiniteElement::DIV && dim == 2)
+ {
+ PADivDivSetup2D(quad1D, ne, ir->GetWeights(), geom->J, coeff, pa_data);
+ }
+ else
+ {
+ MFEM_ABORT("Unknown kernel.");
+ }
+}
+
+void DivDivIntegrator::AddMultPA(const Vector &x, Vector &y) const
+{
+ if (dim == 3)
+ PADivDivApply3D(dofs1D, quad1D, ne, mapsO->B, mapsC->G,
+ mapsO->Bt, mapsC->Gt, pa_data, x, y);
+ else if (dim == 2)
+ PADivDivApply2D(dofs1D, quad1D, ne, mapsO->B, mapsC->G,
+ mapsO->Bt, mapsC->Gt, pa_data, x, y);
+ else
+ {
+ MFEM_ABORT("Unsupported dimension!");
+ }
+}
+
+static void PADivDivAssembleDiagonal2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Gc,
+ const Vector &_op,
+ Vector &_diag)
+{
+ constexpr static int VDIM = 2;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Gc = Reshape(_Gc.Read(), Q1D, D1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
+ auto diag = Reshape(_diag.ReadWrite(), 2*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y components
+ {
+ const int D1Dx = (c == 1) ? D1D - 1 : D1D;
+ const int D1Dy = (c == 0) ? D1D - 1 : D1D;
+
+ double div[MAX_Q1D];
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qx] = 0.0;
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 0) ? Bo(qy,dy) : Gc(qy,dy);
+ div[qx] += wy * wy * op(qx,qy,e);
+ }
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ double val = 0.0;
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ const double wx = (c == 0) ? Gc(qx,dx) : Bo(qx,dx);
+ val += div[qx] * wx * wx;
+ }
+ diag(dx + (dy * D1Dx) + osc, e) += val;
+ }
+ }
+
+ osc += D1Dx * D1Dy;
+ } // loop c
+ });
+}
+
+static void PADivDivAssembleDiagonal3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Gc,
+ const Vector &_op,
+ Vector &_diag)
+{
+ MFEM_VERIFY(D1D <= HDIV_MAX_D1D, "Error: D1D > HDIV_MAX_D1D");
+ MFEM_VERIFY(Q1D <= HDIV_MAX_Q1D, "Error: Q1D > HDIV_MAX_Q1D");
+ constexpr static int VDIM = 3;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Gc = Reshape(_Gc.Read(), Q1D, D1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
+ auto diag = Reshape(_diag.ReadWrite(), 3*(D1D-1)*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dz = (c == 2) ? D1D : D1D - 1;
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dz = 0; dz < D1Dz; ++dz)
+ {
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ double a[HDIV_MAX_Q1D];
+
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ a[qx] = 0.0;
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 1) ? Gc(qy,dy) : Bo(qy,dy);
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ const double wz = (c == 2) ? Gc(qz,dz) : Bo(qz,dz);
+ a[qx] += wy * wy * wz * wz * op(qx,qy,qz,e);
+ }
+ }
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ double val = 0.0;
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ const double wx = (c == 0) ? Gc(qx,dx) : Bo(qx,dx);
+ val += a[qx] * wx * wx;
+ }
+ diag(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) += val;
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy * D1Dz;
+ } // loop c
+ }); // end of element loop
+}
+
+void DivDivIntegrator::AssembleDiagonalPA(Vector& diag)
+{
+ if (dim == 3)
+ {
+ PADivDivAssembleDiagonal3D(dofs1D, quad1D, ne,
+ mapsO->B, mapsC->G, pa_data, diag);
+ }
+ else
+ {
+ PADivDivAssembleDiagonal2D(dofs1D, quad1D, ne,
+ mapsO->B, mapsC->G, pa_data, diag);
+ }
+}
+
+// PA H(div)-L2 (div u, p) assemble 2D kernel
+static void PADivL2Setup2D(const int Q1D,
+ const int NE,
+ const Array &w,
+ Vector &_coeff,
+ Vector &op)
+{
+ const int NQ = Q1D*Q1D;
+ auto W = w.Read();
+ auto coeff = Reshape(_coeff.Read(), NQ, NE);
+ auto y = Reshape(op.Write(), NQ, NE);
+ MFEM_FORALL(e, NE,
+ {
+ for (int q = 0; q < NQ; ++q)
+ {
+ y(q,e) = W[q] * coeff(q,e);
+ }
+ });
+}
+
+static void PADivL2Setup3D(const int Q1D,
+ const int NE,
+ const Array &w,
+ Vector &_coeff,
+ Vector &op)
+{
+ const int NQ = Q1D*Q1D*Q1D;
+ auto W = w.Read();
+ auto coeff = Reshape(_coeff.Read(), NQ, NE);
+ auto y = Reshape(op.Write(), NQ, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ for (int q = 0; q < NQ; ++q)
+ {
+ y(q,e) = W[q] * coeff(q, e);
+ }
+ });
+}
+
+void
+VectorFEDivergenceIntegrator::AssemblePA(const FiniteElementSpace &trial_fes,
+ const FiniteElementSpace &test_fes)
+{
+ // Assumes tensor-product elements, with a vector test space and
+ // scalar trial space.
+ Mesh *mesh = trial_fes.GetMesh();
+ const FiniteElement *trial_fel = trial_fes.GetFE(0);
+ const FiniteElement *test_fel = test_fes.GetFE(0);
+
+ const VectorTensorFiniteElement *trial_el =
+ dynamic_cast(trial_fel);
+ MFEM_VERIFY(trial_el != NULL, "Only VectorTensorFiniteElement is supported!");
+
+ const NodalTensorFiniteElement *test_el =
+ dynamic_cast(test_fel);
+ MFEM_VERIFY(test_el != NULL, "Only NodalTensorFiniteElement is supported!");
+
+ const IntegrationRule *ir = IntRule ? IntRule : &MassIntegrator::GetRule(
+ *trial_el, *trial_el,
+ *mesh->GetElementTransformation(0));
+
+ const int dims = trial_el->GetDim();
+ MFEM_VERIFY(dims == 2 || dims == 3, "");
+
+ const int nq = ir->GetNPoints();
+ dim = mesh->Dimension();
+ MFEM_VERIFY(dim == 2 || dim == 3, "");
+
+ MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder() + 1, "");
+
+ ne = trial_fes.GetNE();
+ mapsC = &trial_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
+ mapsO = &trial_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
+ dofs1D = mapsC->ndof;
+ quad1D = mapsC->nqpt;
+
+ L2mapsO = &test_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
+ L2dofs1D = L2mapsO->ndof;
+
+ MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
+ if (dim == 2)
+ {
+ MFEM_VERIFY(nq == quad1D * quad1D, "");
+ }
+ else
+ {
+ MFEM_VERIFY(nq == quad1D * quad1D * quad1D, "");
+ }
+
+ pa_data.SetSize(nq * ne, Device::GetMemoryType());
+
+ Vector coeff(ne * nq);
+ coeff = 1.0;
+ if (Q)
+ {
+ for (int e=0; eGetElementTransformation(e);
+ for (int p=0; pEval(*tr, ir->IntPoint(p));
+ }
+ }
+ }
+
+ if (trial_el->GetDerivType() == mfem::FiniteElement::DIV && dim == 3)
+ {
+ PADivL2Setup3D(quad1D, ne, ir->GetWeights(), coeff, pa_data);
+ }
+ else if (trial_el->GetDerivType() == mfem::FiniteElement::DIV && dim == 2)
+ {
+ PADivL2Setup2D(quad1D, ne, ir->GetWeights(), coeff, pa_data);
+ }
+ else
+ {
+ MFEM_ABORT("Unknown kernel.");
+ }
+}
+
+// Apply to x corresponding to DOF's in H(div) (trial), whose divergence is
+// integrated against L_2 test functions corresponding to y.
+static void PAHdivL2Apply3D(const int D1D,
+ const int Q1D,
+ const int L2D1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Gc,
+ const Array &_L2Bot,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
+{
+ MFEM_VERIFY(D1D <= HDIV_MAX_D1D, "Error: D1D > HDIV_MAX_D1D");
+ MFEM_VERIFY(Q1D <= HDIV_MAX_Q1D, "Error: Q1D > HDIV_MAX_Q1D");
+ constexpr static int VDIM = 3;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Gc = Reshape(_Gc.Read(), Q1D, D1D);
+ auto L2Bot = Reshape(_L2Bot.Read(), L2D1D, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
+ auto x = Reshape(_x.Read(), 3*(D1D-1)*(D1D-1)*D1D, NE);
+ auto y = Reshape(_y.ReadWrite(), L2D1D, L2D1D, L2D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ double div[HDIV_MAX_Q1D][HDIV_MAX_Q1D][HDIV_MAX_Q1D];
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] = 0.0;
+ }
+ }
+ }
+
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dz = (c == 2) ? D1D : D1D - 1;
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dz = 0; dz < D1Dz; ++dz)
+ {
+ double aXY[HDIV_MAX_Q1D][HDIV_MAX_Q1D];
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aXY[qy][qx] = 0.0;
+ }
+ }
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ double aX[HDIV_MAX_Q1D];
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] = 0.0;
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ const double t = x(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] += t * ((c == 0) ? Gc(qx,dx) : Bo(qx,dx));
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 1) ? Gc(qy,dy) : Bo(qy,dy);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aXY[qy][qx] += aX[qx] * wy;
+ }
+ }
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ const double wz = (c == 2) ? Gc(qz,dz) : Bo(qz,dz);
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] += aXY[qy][qx] * wz;
+ }
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy * D1Dz;
+ } // loop (c) over components
+
+ // Apply D operator.
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] *= op(qx,qy,qz,e);
+ }
+ }
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ double aXY[HDIV_MAX_D1D][HDIV_MAX_D1D];
+
+ for (int dy = 0; dy < L2D1D; ++dy)
+ {
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ aXY[dy][dx] = 0;
+ }
+ }
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ double aX[HDIV_MAX_D1D];
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ aX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ aX[dx] += div[qz][qy][qx] * L2Bot(dx,qx);
+ }
+ }
+ for (int dy = 0; dy < L2D1D; ++dy)
+ {
+ const double wy = L2Bot(dy,qy);
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ aXY[dy][dx] += aX[dx] * wy;
+ }
+ }
+ }
+
+ for (int dz = 0; dz < L2D1D; ++dz)
+ {
+ const double wz = L2Bot(dz,qz);
+ for (int dy = 0; dy < L2D1D; ++dy)
+ {
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ y(dx,dy,dz,e) += aXY[dy][dx] * wz;
+ }
+ }
+ }
+ } // loop qz
+ }); // end of element loop
+}
+
+// Apply to x corresponding to DOF's in H(div) (trial), whose divergence is
+// integrated against L_2 test functions corresponding to y.
+static void PAHdivL2Apply2D(const int D1D,
+ const int Q1D,
+ const int L2D1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Gc,
+ const Array &_L2Bot,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
+{
+ constexpr static int VDIM = 2;
+
+ auto Bo = Reshape(_Bo.Read(), Q1D, D1D-1);
+ auto Gc = Reshape(_Gc.Read(), Q1D, D1D);
+ auto L2Bot = Reshape(_L2Bot.Read(), L2D1D, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
+ auto x = Reshape(_x.Read(), 2*(D1D-1)*D1D, NE);
+ auto y = Reshape(_y.ReadWrite(), L2D1D, L2D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ double div[MAX_Q1D][MAX_Q1D];
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] = 0.0;
+ }
+ }
+
+ int osc = 0;
+
+ for (int c = 0; c < VDIM; ++c) // loop over x, y components
+ {
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ double aX[MAX_Q1D];
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] = 0.0;
+ }
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ const double t = x(dx + (dy * D1Dx) + osc, e);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] += t * ((c == 0) ? Gc(qx,dx) : Bo(qx,dx));
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = (c == 1) ? Gc(qy,dy) : Bo(qy,dy);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] += aX[qx] * wy;
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy;
+ } // loop (c) over components
+
+ // Apply D operator.
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] *= op(qx,qy,e);
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ double aX[MAX_D1D];
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ aX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ aX[dx] += div[qy][qx] * L2Bot(dx,qx);
+ }
+ }
+ for (int dy = 0; dy < L2D1D; ++dy)
+ {
+ const double wy = L2Bot(dy,qy);
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ y(dx,dy,e) += aX[dx] * wy;
+ }
+ }
+ }
+ }); // end of element loop
+}
+
+static void PAHdivL2ApplyTranspose3D(const int D1D,
+ const int Q1D,
+ const int L2D1D,
+ const int NE,
+ const Array &_L2Bo,
+ const Array &_Gct,
+ const Array &_Bot,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
+{
+ MFEM_VERIFY(D1D <= HDIV_MAX_D1D, "Error: D1D > HDIV_MAX_D1D");
+ MFEM_VERIFY(Q1D <= HDIV_MAX_Q1D, "Error: Q1D > HDIV_MAX_Q1D");
+ constexpr static int VDIM = 3;
+
+ auto L2Bo = Reshape(_L2Bo.Read(), Q1D, L2D1D);
+ auto Gct = Reshape(_Gct.Read(), D1D, Q1D);
+ auto Bot = Reshape(_Bot.Read(), D1D-1, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
+ auto x = Reshape(_x.Read(), L2D1D, L2D1D, L2D1D, NE);
+ auto y = Reshape(_y.ReadWrite(), 3*(D1D-1)*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ double div[HDIV_MAX_Q1D][HDIV_MAX_Q1D][HDIV_MAX_Q1D];
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] = 0.0;
+ }
+ }
+ }
+
+ for (int dz = 0; dz < L2D1D; ++dz)
+ {
+ double aXY[HDIV_MAX_Q1D][HDIV_MAX_Q1D];
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aXY[qy][qx] = 0.0;
+ }
+ }
+
+ for (int dy = 0; dy < L2D1D; ++dy)
+ {
+ double aX[HDIV_MAX_Q1D];
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] = 0.0;
+ }
+
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ const double t = x(dx,dy,dz,e);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] += t * L2Bo(qx,dx);
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = L2Bo(qy,dy);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aXY[qy][qx] += aX[qx] * wy;
+ }
+ }
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ const double wz = L2Bo(qz,dz);
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] += aXY[qy][qx] * wz;
+ }
+ }
+ }
+ }
+
+ // Apply D operator.
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] *= op(qx,qy,qz,e);
+ }
+ }
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ double aXY[HDIV_MAX_D1D][HDIV_MAX_D1D];
+
+ int osc = 0;
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dz = (c == 2) ? D1D : D1D - 1;
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aXY[dy][dx] = 0;
+ }
+ }
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ double aX[HDIV_MAX_D1D];
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] += div[qz][qy][qx] * ((c == 0) ? Gct(dx,qx) :
+ Bot(dx,qx));
+ }
+ }
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ const double wy = (c == 1) ? Gct(dy,qy) : Bot(dy,qy);
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aXY[dy][dx] += aX[dx] * wy;
+ }
+ }
+ }
+
+ for (int dz = 0; dz < D1Dz; ++dz)
+ {
+ const double wz = (c == 2) ? Gct(dz,qz) : Bot(dz,qz);
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ y(dx + ((dy + (dz * D1Dy)) * D1Dx) + osc, e) +=
+ aXY[dy][dx] * wz;
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy * D1Dz;
+ } // loop c
+ } // loop qz
+ }); // end of element loop
+}
+
+static void PAHdivL2ApplyTranspose2D(const int D1D,
+ const int Q1D,
+ const int L2D1D,
+ const int NE,
+ const Array &_L2Bo,
+ const Array &_Gct,
+ const Array &_Bot,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y)
+{
+ constexpr static int VDIM = 2;
+
+ auto L2Bo = Reshape(_L2Bo.Read(), Q1D, L2D1D);
+ auto Gct = Reshape(_Gct.Read(), D1D, Q1D);
+ auto Bot = Reshape(_Bot.Read(), D1D-1, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
+ auto x = Reshape(_x.Read(), L2D1D, L2D1D, NE);
+ auto y = Reshape(_y.ReadWrite(), 2*(D1D-1)*D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ double div[MAX_Q1D][MAX_Q1D];
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] = 0.0;
+ }
+ }
+
+ for (int dy = 0; dy < L2D1D; ++dy)
+ {
+ double aX[MAX_Q1D];
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] = 0.0;
+ }
+
+ for (int dx = 0; dx < L2D1D; ++dx)
+ {
+ const double t = x(dx,dy,e);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ aX[qx] += t * L2Bo(qx,dx);
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ const double wy = L2Bo(qy,dy);
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] += aX[qx] * wy;
+ }
+ }
+ }
+
+ // Apply D operator.
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] *= op(qx,qy,e);
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ double aX[HDIV_MAX_D1D];
+
+ int osc = 0;
+ for (int c = 0; c < VDIM; ++c) // loop over x, y components
+ {
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] += div[qy][qx] * ((c == 0) ? Gct(dx,qx) : Bot(dx,qx));
+ }
+ }
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ const double wy = (c == 0) ? Bot(dy,qy) : Gct(dy,qy);
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ y(dx + (dy * D1Dx) + osc, e) += aX[dx] * wy;
+ }
+ }
+
+ osc += D1Dx * D1Dy;
+ } // loop c
+ } // loop qy
+ }); // end of element loop
+}
+
+void VectorFEDivergenceIntegrator::AddMultPA(const Vector &x, Vector &y) const
+{
+ if (dim == 3)
+ PAHdivL2Apply3D(dofs1D, quad1D, L2dofs1D, ne, mapsO->B, mapsC->G,
+ L2mapsO->Bt, pa_data, x, y);
+ else if (dim == 2)
+ PAHdivL2Apply2D(dofs1D, quad1D, L2dofs1D, ne, mapsO->B, mapsC->G,
+ L2mapsO->Bt, pa_data, x, y);
+ else
+ {
+ MFEM_ABORT("Unsupported dimension!");
+ }
+}
+
+void VectorFEDivergenceIntegrator::AddMultTransposePA(const Vector &x,
+ Vector &y) const
+{
+ if (dim == 3)
+ PAHdivL2ApplyTranspose3D(dofs1D, quad1D, L2dofs1D, ne, L2mapsO->B,
+ mapsC->Gt, mapsO->Bt, pa_data, x, y);
+ else if (dim == 2)
+ PAHdivL2ApplyTranspose2D(dofs1D, quad1D, L2dofs1D, ne, L2mapsO->B,
+ mapsC->Gt, mapsO->Bt, pa_data, x, y);
+ else
+ {
+ MFEM_ABORT("Unsupported dimension!");
+ }
+}
+
+static void PAHdivL2AssembleDiagonal_ADAt_3D(const int D1D,
+ const int Q1D,
+ const int L2D1D,
+ const int NE,
+ const Array &_L2Bo,
+ const Array &_Gct,
+ const Array &_Bot,
+ const Vector &_op,
+ const Vector &_D,
+ Vector &_diag)
+{
+ MFEM_VERIFY(D1D <= HDIV_MAX_D1D, "Error: D1D > HDIV_MAX_D1D");
+ MFEM_VERIFY(Q1D <= HDIV_MAX_Q1D, "Error: Q1D > HDIV_MAX_Q1D");
+ constexpr static int VDIM = 3;
+
+ auto L2Bo = Reshape(_L2Bo.Read(), Q1D, L2D1D);
+ auto Gct = Reshape(_Gct.Read(), D1D, Q1D);
+ auto Bot = Reshape(_Bot.Read(), D1D-1, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, Q1D, NE);
+ auto D = Reshape(_D.Read(), 3*(D1D-1)*(D1D-1)*D1D, NE);
+ auto diag = Reshape(_diag.ReadWrite(), L2D1D, L2D1D, L2D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ for (int rz = 0; rz < L2D1D; ++rz)
+ {
+ for (int ry = 0; ry < L2D1D; ++ry)
+ {
+ for (int rx = 0; rx < L2D1D; ++rx)
+ {
+ // Compute row (rx,ry,rz), assuming all contributions are from
+ // a single element.
+
+ double row[3*HDIV_MAX_D1D*(HDIV_MAX_D1D-1)*(HDIV_MAX_D1D-1)];
+ double div[HDIV_MAX_Q1D][HDIV_MAX_Q1D][HDIV_MAX_Q1D];
+
+ for (int i=0; i<3*D1D*(D1D - 1)*(D1D - 1); ++i)
+ {
+ row[i] = 0;
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qz][qy][qx] = op(qx,qy,qz,e) * L2Bo(qx,rx) *
+ L2Bo(qy,ry) * L2Bo(qz,rz);
+ }
+ }
+ }
+
+ for (int qz = 0; qz < Q1D; ++qz)
+ {
+ double aXY[HDIV_MAX_D1D][HDIV_MAX_D1D];
+
+ int osc = 0;
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dz = (c == 2) ? D1D : D1D - 1;
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aXY[dy][dx] = 0;
+ }
+ }
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ double aX[HDIV_MAX_D1D];
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] += div[qz][qy][qx] * ((c == 0) ? Gct(dx,qx)
+ : Bot(dx,qx));
+ }
+ }
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ const double wy = (c == 1) ? Gct(dy,qy) : Bot(dy,qy);
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aXY[dy][dx] += aX[dx] * wy;
+ }
+ }
+ }
+
+ for (int dz = 0; dz < D1Dz; ++dz)
+ {
+ const double wz = (c == 2) ? Gct(dz,qz) : Bot(dz,qz);
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ row[dx + ((dy + (dz * D1Dy)) * D1Dx) + osc] +=
+ aXY[dy][dx] * wz;
+ }
+ }
+ }
+
+ osc += D1Dx * D1Dy * D1Dz;
+ } // loop c
+ } // loop qz
+
+ double val = 0.0;
+ for (int i=0; i<3*D1D*(D1D - 1)*(D1D - 1); ++i)
+ {
+ val += row[i] * row[i] * D(i,e);
+ }
+ diag(rx,ry,rz,e) += val;
+ } // loop rx
+ } // loop ry
+ } // loop rz
+ }); // end of element loop
+}
+
+static void PAHdivL2AssembleDiagonal_ADAt_2D(const int D1D,
+ const int Q1D,
+ const int L2D1D,
+ const int NE,
+ const Array &_L2Bo,
+ const Array &_Gct,
+ const Array &_Bot,
+ const Vector &_op,
+ const Vector &_D,
+ Vector &_diag)
+{
+ constexpr static int VDIM = 2;
+
+ auto L2Bo = Reshape(_L2Bo.Read(), Q1D, L2D1D);
+ auto Gct = Reshape(_Gct.Read(), D1D, Q1D);
+ auto Bot = Reshape(_Bot.Read(), D1D-1, Q1D);
+ auto op = Reshape(_op.Read(), Q1D, Q1D, NE);
+ auto D = Reshape(_D.Read(), 2*(D1D-1)*D1D, NE);
+ auto diag = Reshape(_diag.ReadWrite(), L2D1D, L2D1D, NE);
+
+ MFEM_FORALL(e, NE,
+ {
+ for (int ry = 0; ry < L2D1D; ++ry)
+ {
+ for (int rx = 0; rx < L2D1D; ++rx)
+ {
+ // Compute row (rx,ry), assuming all contributions are from
+ // a single element.
+
+ double row[2*HDIV_MAX_D1D*(HDIV_MAX_D1D-1)];
+ double div[HDIV_MAX_Q1D][HDIV_MAX_Q1D];
+
+ for (int i=0; i<2*D1D*(D1D - 1); ++i)
+ {
+ row[i] = 0;
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ div[qy][qx] = op(qx,qy,e) * L2Bo(qx,rx) * L2Bo(qy,ry);
+ }
+ }
+
+ for (int qy = 0; qy < Q1D; ++qy)
+ {
+ int osc = 0;
+ for (int c = 0; c < VDIM; ++c) // loop over x, y, z components
+ {
+ const int D1Dy = (c == 1) ? D1D : D1D - 1;
+ const int D1Dx = (c == 0) ? D1D : D1D - 1;
+
+ double aX[HDIV_MAX_D1D];
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] = 0;
+ }
+ for (int qx = 0; qx < Q1D; ++qx)
+ {
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ aX[dx] += div[qy][qx] * ((c == 0) ? Gct(dx,qx) :
+ Bot(dx,qx));
+ }
+ }
+
+ for (int dy = 0; dy < D1Dy; ++dy)
+ {
+ const double wy = (c == 1) ? Gct(dy,qy) : Bot(dy,qy);
+
+ for (int dx = 0; dx < D1Dx; ++dx)
+ {
+ row[dx + (dy * D1Dx) + osc] += aX[dx] * wy;
+ }
+ }
+
+ osc += D1Dx * D1Dy;
+ } // loop c
+ } // loop qy
+
+ double val = 0.0;
+ for (int i=0; i<2*D1D*(D1D - 1); ++i)
+ {
+ val += row[i] * row[i] * D(i,e);
+ }
+ diag(rx,ry,e) += val;
+ } // loop rx
+ } // loop ry
+ }); // end of element loop
+}
+
+void VectorFEDivergenceIntegrator::AssembleDiagonalPA_ADAt(const Vector &D,
+ Vector &diag)
+{
+ if (dim == 3)
+ PAHdivL2AssembleDiagonal_ADAt_3D(dofs1D, quad1D, L2dofs1D, ne, L2mapsO->B,
+ mapsC->Gt, mapsO->Bt, pa_data, D, diag);
+ else if (dim == 2)
+ PAHdivL2AssembleDiagonal_ADAt_2D(dofs1D, quad1D, L2dofs1D, ne, L2mapsO->B,
+ mapsC->Gt, mapsO->Bt, pa_data, D, diag);
+ else
+ {
+ MFEM_ABORT("Unsupported dimension!");
+ }
+}
+
+} // namespace mfem
diff --git a/fem/bilininteg_mass_ea.cpp b/fem/bilininteg_mass_ea.cpp
new file mode 100644
index 00000000000..16aad03eefc
--- /dev/null
+++ b/fem/bilininteg_mass_ea.cpp
@@ -0,0 +1,255 @@
+// Copyright (c) 2010-2020, 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 "../general/forall.hpp"
+#include "bilininteg.hpp"
+#include "gridfunc.hpp"
+
+namespace mfem
+{
+
+template
+static void EAMassAssemble1D(const int NE,
+ const Array &basis,
+ const Vector &padata,
+ Vector &eadata,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(basis.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, NE);
+ auto M = Reshape(eadata.Write(), D1D, D1D, NE);
+ MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_Bi[MQ1];
+ double r_Bj[MQ1];
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_Bi[q] = B(q,MFEM_THREAD_ID(x));
+ r_Bj[q] = B(q,MFEM_THREAD_ID(y));
+ }
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(j1,y,D1D)
+ {
+ double val = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ val += r_Bi[k1] * r_Bj[k1] * D(k1, e);
+ }
+ M(i1, j1, e) = val;
+ }
+ }
+ });
+}
+
+template
+static void EAMassAssemble2D(const int NE,
+ const Array &basis,
+ const Vector &padata,
+ Vector &eadata,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(basis.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, Q1D, NE);
+ auto M = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, NE);
+ MFEM_FORALL_3D(e, NE, D1D, D1D, 1,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_B[MQ1][MD1];
+ for (int d = 0; d < D1D; d++)
+ {
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_B[q][d] = B(q,d);
+ }
+ }
+ MFEM_SHARED double s_D[MQ1][MQ1];
+ MFEM_FOREACH_THREAD(k1,x,Q1D)
+ {
+ MFEM_FOREACH_THREAD(k2,y,Q1D)
+ {
+ s_D[k1][k2] = D(k1,k2,e);
+ }
+ }
+ MFEM_SYNC_THREAD;
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(i2,y,D1D)
+ {
+ for (int j1 = 0; j1 < D1D; ++j1)
+ {
+ for (int j2 = 0; j2 < D1D; ++j2)
+ {
+ double val = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ for (int k2 = 0; k2 < Q1D; ++k2)
+ {
+ val += r_B[k1][i1] * r_B[k1][j1]
+ * r_B[k2][i2] * r_B[k2][j2]
+ * s_D[k1][k2];
+ }
+ }
+ M(i1, i2, j1, j2, e) = val;
+ }
+ }
+ }
+ }
+ });
+}
+
+template
+static void EAMassAssemble3D(const int NE,
+ const Array &basis,
+ const Vector &padata,
+ Vector &eadata,
+ const int d1d = 0,
+ const int q1d = 0)
+{
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ MFEM_VERIFY(D1D <= MAX_D1D, "");
+ MFEM_VERIFY(Q1D <= MAX_Q1D, "");
+ auto B = Reshape(basis.Read(), Q1D, D1D);
+ auto D = Reshape(padata.Read(), Q1D, Q1D, Q1D, NE);
+ auto M = Reshape(eadata.Write(), D1D, D1D, D1D, D1D, D1D, D1D, NE);
+ MFEM_FORALL_3D(e, NE, D1D, D1D, D1D,
+ {
+ const int D1D = T_D1D ? T_D1D : d1d;
+ const int Q1D = T_Q1D ? T_Q1D : q1d;
+ constexpr int MD1 = T_D1D ? T_D1D : MAX_D1D;
+ constexpr int MQ1 = T_Q1D ? T_Q1D : MAX_Q1D;
+ double r_B[MQ1][MD1];
+ for (int d = 0; d < D1D; d++)
+ {
+ for (int q = 0; q < Q1D; q++)
+ {
+ r_B[q][d] = B(q,d);
+ }
+ }
+ MFEM_SHARED double s_D[MQ1][MQ1][MQ1];
+ MFEM_FOREACH_THREAD(k1,x,Q1D)
+ {
+ MFEM_FOREACH_THREAD(k2,y,Q1D)
+ {
+ MFEM_FOREACH_THREAD(k3,z,Q1D)
+ {
+ s_D[k1][k2][k3] = D(k1,k2,k3,e);
+ }
+ }
+ }
+ MFEM_SYNC_THREAD;
+ MFEM_FOREACH_THREAD(i1,x,D1D)
+ {
+ MFEM_FOREACH_THREAD(i2,y,D1D)
+ {
+ MFEM_FOREACH_THREAD(i3,z,D1D)
+ {
+ for (int j1 = 0; j1 < D1D; ++j1)
+ {
+ for (int j2 = 0; j2 < D1D; ++j2)
+ {
+ for (int j3 = 0; j3 < D1D; ++j3)
+ {
+ double val = 0.0;
+ for (int k1 = 0; k1 < Q1D; ++k1)
+ {
+ for (int k2 = 0; k2 < Q1D; ++k2)
+ {
+ for (int k3 = 0; k3 < Q1D; ++k3)
+ {
+ val += r_B[k1][i1] * r_B[k1][j1]
+ * r_B[k2][i2] * r_B[k2][j2]
+ * r_B[k3][i3] * r_B[k3][j3]
+ * s_D[k1][k2][k3];
+ }
+ }
+ }
+ M(i1, i2, i3, j1, j2, j3, e) = val;
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+}
+
+void MassIntegrator::AssembleEA(const FiniteElementSpace &fes,
+ Vector &ea_data)
+{
+ AssemblePA(fes);
+ const int ne = fes.GetMesh()->GetNE();
+ const Array &B = maps->B;
+ if (dim == 1)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x22: return EAMassAssemble1D<2,2>(ne,B,pa_data,ea_data);
+ case 0x33: return EAMassAssemble1D<3,3>(ne,B,pa_data,ea_data);
+ case 0x44: return EAMassAssemble1D<4,4>(ne,B,pa_data,ea_data);
+ case 0x55: return EAMassAssemble1D<5,5>(ne,B,pa_data,ea_data);
+ case 0x66: return EAMassAssemble1D<6,6>(ne,B,pa_data,ea_data);
+ case 0x77: return EAMassAssemble1D<7,7>(ne,B,pa_data,ea_data);
+ case 0x88: return EAMassAssemble1D<8,8>(ne,B,pa_data,ea_data);
+ case 0x99: return EAMassAssemble1D<9,9>(ne,B,pa_data,ea_data);
+ default: return EAMassAssemble1D(ne,B,pa_data,ea_data,dofs1D,quad1D);
+ }
+ }
+ else if (dim == 2)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x22: return EAMassAssemble2D<2,2>(ne,B,pa_data,ea_data);
+ case 0x33: return EAMassAssemble2D<3,3>(ne,B,pa_data,ea_data);
+ case 0x44: return EAMassAssemble2D<4,4>(ne,B,pa_data,ea_data);
+ case 0x55: return EAMassAssemble2D<5,5>(ne,B,pa_data,ea_data);
+ case 0x66: return EAMassAssemble2D<6,6>(ne,B,pa_data,ea_data);
+ case 0x77: return EAMassAssemble2D<7,7>(ne,B,pa_data,ea_data);
+ case 0x88: return EAMassAssemble2D<8,8>(ne,B,pa_data,ea_data);
+ case 0x99: return EAMassAssemble2D<9,9>(ne,B,pa_data,ea_data);
+ default: return EAMassAssemble2D(ne,B,pa_data,ea_data,dofs1D,quad1D);
+ }
+ }
+ else if (dim == 3)
+ {
+ switch ((dofs1D << 4 ) | quad1D)
+ {
+ case 0x23: return EAMassAssemble3D<2,3>(ne,B,pa_data,ea_data);
+ case 0x34: return EAMassAssemble3D<3,4>(ne,B,pa_data,ea_data);
+ case 0x45: return EAMassAssemble3D<4,5>(ne,B,pa_data,ea_data);
+ case 0x56: return EAMassAssemble3D<5,6>(ne,B,pa_data,ea_data);
+ case 0x67: return EAMassAssemble3D<6,7>(ne,B,pa_data,ea_data);
+ case 0x78: return EAMassAssemble3D<7,8>(ne,B,pa_data,ea_data);
+ case 0x89: return EAMassAssemble3D<8,9>(ne,B,pa_data,ea_data);
+ default: return EAMassAssemble3D(ne,B,pa_data,ea_data,dofs1D,quad1D);
+ }
+ }
+ MFEM_ABORT("Unknown kernel.");
+}
+
+}
diff --git a/fem/bilininteg_mass.cpp b/fem/bilininteg_mass_pa.cpp
similarity index 100%
rename from fem/bilininteg_mass.cpp
rename to fem/bilininteg_mass_pa.cpp
diff --git a/fem/bilininteg_transpose_ea.cpp b/fem/bilininteg_transpose_ea.cpp
new file mode 100644
index 00000000000..645efe1cc05
--- /dev/null
+++ b/fem/bilininteg_transpose_ea.cpp
@@ -0,0 +1,103 @@
+// Copyright (c) 2010-2020, 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 "../general/forall.hpp"
+#include "bilininteg.hpp"
+
+namespace mfem
+{
+
+void TransposeIntegrator::AssembleEA(const FiniteElementSpace &fes,
+ Vector &ea_data)
+{
+ Vector ea_data_tmp(ea_data.Size());
+ ea_data_tmp = 0.0;
+ bfi->AssembleEA(fes, ea_data_tmp);
+ const int ne = fes.GetNE();
+ if (ne == 0) { return; }
+ const int dofs = fes.GetFE(0)->GetDof();
+ auto A = Reshape(ea_data_tmp.Write(), dofs, dofs, ne);
+ auto AT = Reshape(ea_data.Write(), dofs, dofs, ne);
+ MFEM_FORALL(e, ne,
+ {
+ for (int i = 0; i < dofs; i++)
+ {
+ for (int j = 0; j < dofs; j++)
+ {
+ const double a = A(i, j, e);
+ AT(j, i, e) += a;
+ }
+ }
+ });
+}
+
+void TransposeIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace& fes,
+ Vector &ea_data_int,
+ Vector &ea_data_ext)
+{
+ const int nf = fes.GetNFbyType(FaceType::Interior);
+ if (nf == 0) { return; }
+ Vector ea_data_int_tmp(ea_data_int.Size());
+ Vector ea_data_ext_tmp(ea_data_ext.Size());
+ ea_data_int_tmp = 0.0;
+ ea_data_ext_tmp = 0.0;
+ bfi->AssembleEAInteriorFaces(fes, ea_data_int_tmp, ea_data_ext_tmp);
+ const int faceDofs = fes.GetTraceElement(0,
+ fes.GetMesh()->GetFaceBaseGeometry(0))->GetDof();
+ auto A_int = Reshape(ea_data_int_tmp.Read(), faceDofs, faceDofs, 2, nf);
+ auto A_ext = Reshape(ea_data_ext_tmp.Read(), faceDofs, faceDofs, 2, nf);
+ auto AT_int = Reshape(ea_data_int.ReadWrite(), faceDofs, faceDofs, 2, nf);
+ auto AT_ext = Reshape(ea_data_ext.ReadWrite(), faceDofs, faceDofs, 2, nf);
+ MFEM_FORALL(f, nf,
+ {
+ for (int i = 0; i < faceDofs; i++)
+ {
+ for (int j = 0; j < faceDofs; j++)
+ {
+ const double a_int0 = A_int(i, j, 0, f);
+ const double a_int1 = A_int(i, j, 1, f);
+ const double a_ext0 = A_ext(i, j, 0, f);
+ const double a_ext1 = A_ext(i, j, 1, f);
+ AT_int(j, i, 0, f) += a_int0;
+ AT_int(j, i, 1, f) += a_int1;
+ AT_ext(j, i, 0, f) += a_ext1;
+ AT_ext(j, i, 1, f) += a_ext0;
+ }
+ }
+ });
+}
+
+void TransposeIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace& fes,
+ Vector &ea_data_bdr)
+{
+ const int nf = fes.GetNFbyType(FaceType::Boundary);
+ if (nf == 0) { return; }
+ Vector ea_data_bdr_tmp(ea_data_bdr.Size());
+ ea_data_bdr_tmp = 0.0;
+ bfi->AssembleEABoundaryFaces(fes, ea_data_bdr_tmp);
+ const int faceDofs = fes.GetTraceElement(0,
+ fes.GetMesh()->GetFaceBaseGeometry(0))->GetDof();
+ auto A_bdr = Reshape(ea_data_bdr_tmp.Read(), faceDofs, faceDofs, nf);
+ auto AT_bdr = Reshape(ea_data_bdr.ReadWrite(), faceDofs, faceDofs, nf);
+ MFEM_FORALL(f, nf,
+ {
+ for (int i = 0; i < faceDofs; i++)
+ {
+ for (int j = 0; j < faceDofs; j++)
+ {
+ const double a_bdr = A_bdr(i, j, f);
+ AT_bdr(j, i, f) += a_bdr;
+ }
+ }
+ });
+}
+
+}
diff --git a/fem/bilininteg_vectorfe.cpp b/fem/bilininteg_vectorfe.cpp
new file mode 100644
index 00000000000..9059bdd9178
--- /dev/null
+++ b/fem/bilininteg_vectorfe.cpp
@@ -0,0 +1,379 @@
+// Copyright (c) 2010-2020, 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 "bilininteg.hpp"
+
+namespace mfem
+{
+
+void PAHcurlSetup2D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op);
+
+void PAHcurlSetup3D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op);
+
+void PAHcurlMassAssembleDiagonal2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Vector &_op,
+ Vector &_diag);
+
+void PAHcurlMassAssembleDiagonal3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Vector &_op,
+ Vector &_diag);
+
+void PAHcurlMassApply2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y);
+
+void PAHcurlMassApply3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y);
+
+void PAHdivSetup2D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op);
+
+void PAHdivSetup3D(const int Q1D,
+ const int NE,
+ const Array &w,
+ const Vector &j,
+ Vector &_coeff,
+ Vector &op);
+
+void PAHcurlH1Apply2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bc,
+ const Array &_Gc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y);
+
+void PAHcurlH1Apply3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bc,
+ const Array &_Gc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y);
+
+void PAHdivMassAssembleDiagonal2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Vector &_op,
+ Vector &_diag);
+
+void PAHdivMassAssembleDiagonal3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Vector &_op,
+ Vector &_diag);
+
+void PAHdivMassApply2D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y);
+
+void PAHdivMassApply3D(const int D1D,
+ const int Q1D,
+ const int NE,
+ const Array &_Bo,
+ const Array &_Bc,
+ const Array &_Bot,
+ const Array &_Bct,
+ const Vector &_op,
+ const Vector &_x,
+ Vector &_y);
+
+void VectorFEMassIntegrator::AssemblePA(const FiniteElementSpace &fes)
+{
+ // Assumes tensor-product elements
+ Mesh *mesh = fes.GetMesh();
+ const FiniteElement *fel = fes.GetFE(0);
+
+ const VectorTensorFiniteElement *el =
+ dynamic_cast(fel);
+ MFEM_VERIFY(el != NULL, "Only VectorTensorFiniteElement is supported!");
+
+ const IntegrationRule *ir
+ = IntRule ? IntRule : &MassIntegrator::GetRule(*el, *el,
+ *mesh->GetElementTransformation(0));
+ const int dims = el->GetDim();
+ MFEM_VERIFY(dims == 2 || dims == 3, "");
+
+ const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
+ const int nq = ir->GetNPoints();
+ dim = mesh->Dimension();
+ MFEM_VERIFY(dim == 2 || dim == 3, "");
+
+ ne = fes.GetNE();
+ geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
+ mapsC = &el->GetDofToQuad(*ir, DofToQuad::TENSOR);
+ mapsO = &el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
+ dofs1D = mapsC->ndof;
+ quad1D = mapsC->nqpt;
+
+ MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
+
+ pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
+
+ Vector coeff(ne * nq);
+ coeff = 1.0;
+ if (Q)
+ {
+ for (int e=0; eGetElementTransformation(e);
+ for (int p=0; pEval(*tr, ir->IntPoint(p));
+ }
+ }
+ }
+
+ fetype = el->GetDerivType();
+
+ if (el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
+ {
+ PAHcurlSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
+ coeff, pa_data);
+ }
+ else if (el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
+ {
+ PAHcurlSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
+ coeff, pa_data);
+ }
+ else if (el->GetDerivType() == mfem::FiniteElement::DIV && dim == 3)
+ {
+ PAHdivSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
+ coeff, pa_data);
+ }
+ else if (el->GetDerivType() == mfem::FiniteElement::DIV && dim == 2)
+ {
+ PAHdivSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
+ coeff, pa_data);
+ }
+ else
+ {
+ MFEM_ABORT("Unknown kernel.");
+ }
+}
+
+void VectorFEMassIntegrator::AssembleDiagonalPA(Vector& diag)
+{
+ if (dim == 3)
+ {
+ if (fetype == mfem::FiniteElement::CURL)
+ {
+ PAHcurlMassAssembleDiagonal3D(dofs1D, quad1D, ne,
+ mapsO->B, mapsC->B, pa_data, diag);
+ }
+ else if (fetype == mfem::FiniteElement::DIV)
+ {
+ PAHdivMassAssembleDiagonal3D(dofs1D, quad1D, ne,
+ mapsO->B, mapsC->B, pa_data, diag);
+ }
+ else
+ {
+ MFEM_ABORT("Unknown kernel.");
+ }
+ }
+ else
+ {
+ if (fetype == mfem::FiniteElement::CURL)
+ {
+ PAHcurlMassAssembleDiagonal2D(dofs1D, quad1D, ne,
+ mapsO->B, mapsC->B, pa_data, diag);
+ }
+ else if (fetype == mfem::FiniteElement::DIV)
+ {
+ PAHdivMassAssembleDiagonal2D(dofs1D, quad1D, ne,
+ mapsO->B, mapsC->B, pa_data, diag);
+ }
+ else
+ {
+ MFEM_ABORT("Unknown kernel.");
+ }
+ }
+}
+
+void VectorFEMassIntegrator::AddMultPA(const Vector &x, Vector &y) const
+{
+ if (dim == 3)
+ {
+ if (fetype == mfem::FiniteElement::CURL)
+ {
+ PAHcurlMassApply3D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
+ mapsC->Bt, pa_data, x, y);
+ }
+ else if (fetype == mfem::FiniteElement::DIV)
+ {
+ PAHdivMassApply3D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
+ mapsC->Bt, pa_data, x, y);
+ }
+ else
+ {
+ MFEM_ABORT("Unknown kernel.");
+ }
+ }
+ else
+ {
+ if (fetype == mfem::FiniteElement::CURL)
+ {
+ PAHcurlMassApply2D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
+ mapsC->Bt, pa_data, x, y);
+ }
+ else if (fetype == mfem::FiniteElement::DIV)
+ {
+ PAHdivMassApply2D(dofs1D, quad1D, ne, mapsO->B, mapsC->B, mapsO->Bt,
+ mapsC->Bt, pa_data, x, y);
+ }
+ else
+ {
+ MFEM_ABORT("Unknown kernel.");
+ }
+ }
+}
+
+void MixedVectorGradientIntegrator::AssemblePA(const FiniteElementSpace
+ &trial_fes,
+ const FiniteElementSpace &test_fes)
+{
+ // Assumes tensor-product elements, with a vector test space and H^1 trial space.
+ Mesh *mesh = trial_fes.GetMesh();
+ const FiniteElement *trial_fel = trial_fes.GetFE(0);
+ const FiniteElement *test_fel = test_fes.GetFE(0);
+
+ const NodalTensorFiniteElement *trial_el =
+ dynamic_cast(trial_fel);
+ MFEM_VERIFY(trial_el != NULL, "Only NodalTensorFiniteElement is supported!");
+
+ const VectorTensorFiniteElement *test_el =
+ dynamic_cast(test_fel);
+ MFEM_VERIFY(test_el != NULL, "Only VectorTensorFiniteElement is supported!");
+
+ const IntegrationRule *ir
+ = IntRule ? IntRule : &MassIntegrator::GetRule(*trial_el, *trial_el,
+ *mesh->GetElementTransformation(0));
+ const int dims = trial_el->GetDim();
+ MFEM_VERIFY(dims == 2 || dims == 3, "");
+
+ const int symmDims = (dims * (dims + 1)) / 2; // 1x1: 1, 2x2: 3, 3x3: 6
+ const int nq = ir->GetNPoints();
+ dim = mesh->Dimension();
+ MFEM_VERIFY(dim == 2 || dim == 3, "");
+
+ MFEM_VERIFY(trial_el->GetOrder() == test_el->GetOrder(), "");
+
+ ne = trial_fes.GetNE();
+ geom = mesh->GetGeometricFactors(*ir, GeometricFactors::JACOBIANS);
+ mapsC = &test_el->GetDofToQuad(*ir, DofToQuad::TENSOR);
+ mapsO = &test_el->GetDofToQuadOpen(*ir, DofToQuad::TENSOR);
+ dofs1D = mapsC->ndof;
+ quad1D = mapsC->nqpt;
+
+ MFEM_VERIFY(dofs1D == mapsO->ndof + 1 && quad1D == mapsO->nqpt, "");
+
+ pa_data.SetSize(symmDims * nq * ne, Device::GetMemoryType());
+
+ Vector coeff(ne * nq);
+ coeff = 1.0;
+ if (Q)
+ {
+ for (int e=0; eGetElementTransformation(e);
+ for (int p=0; pEval(*tr, ir->IntPoint(p));
+ }
+ }
+ }
+
+ // Use the same setup functions as VectorFEMassIntegrator.
+ if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 3)
+ {
+ PAHcurlSetup3D(quad1D, ne, ir->GetWeights(), geom->J,
+ coeff, pa_data);
+ }
+ else if (test_el->GetDerivType() == mfem::FiniteElement::CURL && dim == 2)
+ {
+ PAHcurlSetup2D(quad1D, ne, ir->GetWeights(), geom->J,
+ coeff, pa_data);
+ }
+ else
+ {
+ MFEM_ABORT("Unknown kernel.");
+ }
+}
+
+void MixedVectorGradientIntegrator::AddMultPA(const Vector &x, Vector &y) const
+{
+ if (dim == 3)
+ PAHcurlH1Apply3D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
+ mapsO->Bt, mapsC->Bt, pa_data, x, y);
+ else if (dim == 2)
+ PAHcurlH1Apply2D(dofs1D, quad1D, ne, mapsC->B, mapsC->G,
+ mapsO->Bt, mapsC->Bt, pa_data, x, y);
+ else
+ {
+ MFEM_ABORT("Unsupported dimension!");
+ }
+}
+
+} // namespace mfem
diff --git a/fem/coefficient.cpp b/fem/coefficient.cpp
index 1f1d3069196..4ecddeb634f 100644
--- a/fem/coefficient.cpp
+++ b/fem/coefficient.cpp
@@ -49,7 +49,7 @@ double FunctionCoefficient::Eval(ElementTransformation & T,
double GridFunctionCoefficient::Eval (ElementTransformation &T,
const IntegrationPoint &ip)
{
- return GridF -> GetValue (T.ElementNo, ip, Component);
+ return GridF -> GetValue (T, ip, Component);
}
double TransformedCoefficient::Eval(ElementTransformation &T,
@@ -160,13 +160,13 @@ void VectorArrayCoefficient::Eval(Vector &V, ElementTransformation &T,
}
VectorGridFunctionCoefficient::VectorGridFunctionCoefficient (
- GridFunction *gf)
+ const GridFunction *gf)
: VectorCoefficient ((gf) ? gf -> VectorDim() : 0)
{
GridFunc = gf;
}
-void VectorGridFunctionCoefficient::SetGridFunction(GridFunction *gf)
+void VectorGridFunctionCoefficient::SetGridFunction(const GridFunction *gf)
{
GridFunc = gf; vdim = (gf) ? gf -> VectorDim() : 0;
}
@@ -174,24 +174,7 @@ void VectorGridFunctionCoefficient::SetGridFunction(GridFunction *gf)
void VectorGridFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip)
{
- Mesh *mesh = GridFunc->FESpace()->GetMesh();
- if (mesh->Dimension() == T.GetDimension())
- {
- GridFunc->GetVectorValue(T.ElementNo, ip, V);
- }
- else // Assuming T is a boundary element transformation
- {
- int el_id, el_info;
- mesh->GetBdrElementAdjacentElement(T.ElementNo, el_id, el_info);
- IntegrationPointTransformation loc_T;
- mesh->GetLocalFaceTransformation(mesh->GetBdrElementType(T.ElementNo),
- mesh->GetElementType(el_id),
- loc_T.Transf,
- el_info);
- IntegrationPoint eip;
- loc_T.Transform(ip, eip);
- GridFunc->GetVectorValue(el_id, eip, V);
- }
+ GridFunc->GetVectorValue(T, ip, V);
}
void VectorGridFunctionCoefficient::Eval(
@@ -201,14 +184,14 @@ void VectorGridFunctionCoefficient::Eval(
}
GradientGridFunctionCoefficient::GradientGridFunctionCoefficient (
- GridFunction *gf)
+ const GridFunction *gf)
: VectorCoefficient((gf) ?
gf -> FESpace() -> GetMesh() -> SpaceDimension() : 0)
{
GridFunc = gf;
}
-void GradientGridFunctionCoefficient::SetGridFunction(GridFunction *gf)
+void GradientGridFunctionCoefficient::SetGridFunction(const GridFunction *gf)
{
GridFunc = gf; vdim = (gf) ?
gf -> FESpace() -> GetMesh() -> SpaceDimension() : 0;
@@ -227,14 +210,14 @@ void GradientGridFunctionCoefficient::Eval(
}
CurlGridFunctionCoefficient::CurlGridFunctionCoefficient (
- GridFunction *gf)
+ const GridFunction *gf)
: VectorCoefficient ((gf) ?
gf -> FESpace() -> GetMesh() -> SpaceDimension() : 0)
{
GridFunc = gf;
}
-void CurlGridFunctionCoefficient::SetGridFunction(GridFunction *gf)
+void CurlGridFunctionCoefficient::SetGridFunction(const GridFunction *gf)
{
GridFunc = gf; vdim = (gf) ?
gf -> FESpace() -> GetMesh() -> SpaceDimension() : 0;
@@ -247,7 +230,7 @@ void CurlGridFunctionCoefficient::Eval(Vector &V, ElementTransformation &T,
}
DivergenceGridFunctionCoefficient::DivergenceGridFunctionCoefficient (
- GridFunction *gf) : Coefficient()
+ const GridFunction *gf) : Coefficient()
{
GridFunc = gf;
}
@@ -775,4 +758,61 @@ double ComputeGlobalLpNorm(double p, VectorCoefficient &coeff, ParMesh &pmesh,
}
#endif
+VectorQuadratureFunctionCoefficient::VectorQuadratureFunctionCoefficient(
+ QuadratureFunction &qf)
+ : VectorCoefficient(qf.GetVDim()), QuadF(qf), index(0) { }
+
+void VectorQuadratureFunctionCoefficient::SetComponent(int _index, int _length)
+{
+ MFEM_VERIFY(_index >= 0, "Index must be >= 0");
+ MFEM_VERIFY(_index < QuadF.GetVDim(),
+ "Index must be < QuadratureFunction length");
+ index = _index;
+
+ MFEM_VERIFY(_length > 0, "Length must be > 0");
+ MFEM_VERIFY(_length <= QuadF.GetVDim() - index,
+ "Length must be <= (QuadratureFunction length - index)");
+
+ vdim = _length;
+}
+
+void VectorQuadratureFunctionCoefficient::Eval(Vector &V,
+ ElementTransformation &T,
+ const IntegrationPoint &ip)
+{
+ QuadF.HostRead();
+
+ if (index == 0 && vdim == QuadF.GetVDim())
+ {
+ QuadF.GetElementValues(T.ElementNo, ip.index, V);
+ }
+ else
+ {
+ Vector temp;
+ QuadF.GetElementValues(T.ElementNo, ip.index, temp);
+ V.SetSize(vdim);
+ for (int i = 0; i < vdim; i++)
+ {
+ V(i) = temp(index + i);
+ }
+ }
+
+ return;
+}
+
+QuadratureFunctionCoefficient::QuadratureFunctionCoefficient(
+ QuadratureFunction &qf) : QuadF(qf)
+{
+ MFEM_VERIFY(qf.GetVDim() == 1, "QuadratureFunction's vdim must be 1");
+}
+
+double QuadratureFunctionCoefficient::Eval(ElementTransformation &T,
+ const IntegrationPoint &ip)
+{
+ QuadF.HostRead();
+ Vector temp(1);
+ QuadF.GetElementValues(T.ElementNo, ip.index, temp);
+ return temp[0];
+}
+
}
diff --git a/fem/coefficient.hpp b/fem/coefficient.hpp
index a50b1235156..ccae771d370 100644
--- a/fem/coefficient.hpp
+++ b/fem/coefficient.hpp
@@ -27,7 +27,10 @@ class ParMesh;
#endif
-/// Base class Coefficient that may optionally depend on time.
+/** @brief Base class Coefficients that optionally depend on space and time.
+ These are used by the BilinearFormIntegrator, LinearFormIntegrator, and
+ NonlinearFormIntegrator classes to represent the physical coefficients in
+ the PDEs that are being discretized. */
class Coefficient
{
protected:
@@ -36,7 +39,10 @@ class Coefficient
public:
Coefficient() { time = 0.; }
+ /// Set the time for time dependent coefficients
void SetTime(double t) { time = t; }
+
+ /// Get the time for time dependent coefficients
double GetTime() { return time; }
/** @brief Evaluate the coefficient in the element described by @a T at the
@@ -63,7 +69,7 @@ class Coefficient
};
-/// Subclass constant coefficient.
+/// A coefficient that is constant across space and time
class ConstantCoefficient : public Coefficient
{
public:
@@ -72,13 +78,14 @@ class ConstantCoefficient : public Coefficient
/// c is value of constant function
explicit ConstantCoefficient(double c = 1.0) { constant=c; }
- /// Evaluate the coefficient
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{ return (constant); }
};
-/// class for piecewise constant coefficient
+/** @brief A piecewise constant coefficient with the constants keyed
+ off the element attribute numbers. */
class PWConstCoefficient : public Coefficient
{
private:
@@ -90,30 +97,31 @@ class PWConstCoefficient : public Coefficient
explicit PWConstCoefficient(int NumOfSubD = 0) : constants(NumOfSubD)
{ constants = 0.0; }
- /** c should be a vector defined by attributes, so for region with
- attribute i c[i-1] is the coefficient in that region */
+ /// Construct the constant coefficient using a vector of constants.
+ /** @a c should be a vector defined by attributes, so for region with
+ attribute @a i @a c[i-1] is the coefficient in that region */
PWConstCoefficient(Vector &c)
{ constants.SetSize(c.Size()); constants=c; }
- /// Update constants
+ /// Update the constants with vector @a c.
void UpdateConstants(Vector &c) { constants.SetSize(c.Size()); constants=c; }
- /// Member function to access or modify the value of the i-th constant
+ /// Return a reference to the i-th constant
double &operator()(int i) { return constants(i-1); }
- /// Set domain constants equal to the same constant c
+ /// Set the constants for all attributes to constant @a c.
void operator=(double c) { constants = c; }
- /// Returns the number of constants
+ /// Returns the number of constants representing different attributes.
int GetNConst() { return constants.Size(); }
- /// Evaluate the coefficient function
+ /// Evaluate the coefficient.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
-/// class for C-function coefficient
+/// A general C-function coefficient
class FunctionCoefficient : public Coefficient
{
protected:
@@ -121,14 +129,14 @@ class FunctionCoefficient : public Coefficient
double (*TDFunction)(const Vector &, double);
public:
- /// Define a time-independent coefficient from a C-function
+ /// Define a time-independent coefficient from a pointer to a C-function
FunctionCoefficient(double (*f)(const Vector &))
{
Function = f;
TDFunction = NULL;
}
- /// Define a time-dependent coefficient from a C-function
+ /// Define a time-dependent coefficient from a pointer to a C-function
FunctionCoefficient(double (*tdf)(const Vector &, double))
{
Function = NULL;
@@ -153,7 +161,7 @@ class FunctionCoefficient : public Coefficient
TDFunction = reinterpret_cast(tdf);
}
- /// Evaluate coefficient
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
@@ -164,23 +172,34 @@ class GridFunction;
class GridFunctionCoefficient : public Coefficient
{
private:
- GridFunction *GridF;
+ const GridFunction *GridF;
int Component;
public:
GridFunctionCoefficient() : GridF(NULL), Component(1) { }
/** Construct GridFunctionCoefficient from a given GridFunction, and
optionally specify a component to use if it is a vector GridFunction. */
- GridFunctionCoefficient (GridFunction *gf, int comp = 1)
+ GridFunctionCoefficient (const GridFunction *gf, int comp = 1)
{ GridF = gf; Component = comp; }
- void SetGridFunction(GridFunction *gf) { GridF = gf; }
- GridFunction * GetGridFunction() const { return GridF; }
+ /// Set the internal GridFunction
+ void SetGridFunction(const GridFunction *gf) { GridF = gf; }
+
+ /// Get the internal GridFunction
+ const GridFunction * GetGridFunction() const { return GridF; }
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
+
+/** @brief A coefficient that depends on 1 or 2 parent coefficients and a
+ transformation rule represented by a C-function.
+
+ \f$ C(x,t) = T(Q1(x,t)) \f$ or \f$ C(x,t) = T(Q1(x,t), Q2(x,t)) \f$
+
+ where T is the transformation rule, and Q1/Q2 are the parent coefficients.*/
class TransformedCoefficient : public Coefficient
{
private:
@@ -196,10 +215,20 @@ class TransformedCoefficient : public Coefficient
double (*F)(double,double))
: Q1(q1), Q2(q2), Transform2(F) { Transform1 = 0; }
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip);
};
-/// Delta function coefficient
+/** @brief Delta function coefficient optionally multiplied by a weight
+ coefficient and a scaled time dependent C-function.
+
+ \f$ F(x,t) = w(x,t) s T(t) d(x - xc) \f$
+
+ where w is the optional weight coefficient, @a s is a scale factor
+ T is an optional time-dependent function and d is a delta function.
+
+ WARNING this cannot be used as a normal coefficient. The usual Eval
+ method is disabled. */
class DeltaCoefficient : public Coefficient
{
protected:
@@ -209,34 +238,49 @@ class DeltaCoefficient : public Coefficient
double (*tdf)(double);
public:
+
+ /// Construct a unit delta function centered at (0.0,0.0,0.0)
DeltaCoefficient()
{
center[0] = center[1] = center[2] = 0.; scale = 1.; tol = 1e-12;
weight = NULL; sdim = 0; tdf = NULL;
}
+
+ /// Construct a delta function scaled by @a s and centered at (x,0.0,0.0)
DeltaCoefficient(double x, double s)
{
center[0] = x; center[1] = 0.; center[2] = 0.; scale = s; tol = 1e-12;
weight = NULL; sdim = 1; tdf = NULL;
}
+
+ /// Construct a delta function scaled by @a s and centered at (x,y,0.0)
DeltaCoefficient(double x, double y, double s)
{
center[0] = x; center[1] = y; center[2] = 0.; scale = s; tol = 1e-12;
weight = NULL; sdim = 2; tdf = NULL;
}
+
+ /// Construct a delta function scaled by @a s and centered at (x,y,z)
DeltaCoefficient(double x, double y, double z, double s)
{
center[0] = x; center[1] = y; center[2] = z; scale = s; tol = 1e-12;
weight = NULL; sdim = 3; tdf = NULL;
}
+
+ /// Set the center location of the delta function.
void SetDeltaCenter(const Vector& center);
+
+ /// Set the scale value multiplying the delta function.
void SetScale(double _s) { scale = _s; }
+
/// Set a time-dependent function that multiplies the Scale().
void SetFunction(double (*f)(double)) { tdf = f; }
+
/** @brief Set the tolerance used during projection onto GridFunction to
- identifying the Mesh vertex where the Center() of the delta function
- lies. */
+ identify the Mesh vertex where the Center() of the delta function
+ lies. (default 1e-12)*/
void SetTol(double _tol) { tol = _tol; }
+
/// Set a weight Coefficient that multiplies the DeltaCoefficient.
/** The weight Coefficient multiplies the value returned by EvalDelta() but
not the value returned by Scale().
@@ -244,16 +288,26 @@ class DeltaCoefficient : public Coefficient
projecting the DeltaCoefficient onto a GridFunction, so that the weighted
integral of the projection is exactly equal to the Scale(). */
void SetWeight(Coefficient *w) { weight = w; }
+
+ /// Return a pointer to a c-array representing the center of the delta
+ /// function.
const double *Center() { return center; }
- /** @brief Return the scale set by SetScale() multiplied by the
- time-dependent function specified by SetFunction(), if set. */
+
+ /** @brief Return the scale factor times the optional time dependent
+ function. Returns \f$ s T(t) \f$ with \f$ T(t) = 1 \f$ when
+ not set by the user. */
double Scale() { return tdf ? (*tdf)(GetTime())*scale : scale; }
- /// See SetTol() for description of the tolerance parameter.
+
+ /// Return the tolerance used to identify the mesh vertices
double Tol() { return tol; }
+
/// See SetWeight() for description of the weight Coefficient.
Coefficient *Weight() { return weight; }
+
+ /// Write the center of the delta function into @a center.
void GetDeltaCenter(Vector& center);
- /// Return the Scale() multiplied by the weight Coefficient, if any.
+
+ /// The value of the function assuming we are evaluating at the delta center.
virtual double EvalDelta(ElementTransformation &T, const IntegrationPoint &ip);
/** @brief A DeltaFunction cannot be evaluated. Calling this method will
cause an MFEM error, terminating the application. */
@@ -262,7 +316,8 @@ class DeltaCoefficient : public Coefficient
virtual ~DeltaCoefficient() { delete weight; }
};
-/// Coefficient defined on a subset of domain or boundary attributes
+/** @brief Derived coefficient that takes the value of the parent coefficient
+ for the active attributes and is zero otherwise. */
class RestrictedCoefficient : public Coefficient
{
private:
@@ -270,13 +325,18 @@ class RestrictedCoefficient : public Coefficient
Array active_attr;
public:
+ /** @brief Construct with a parent coefficient and an array with
+ ones marking the attributes on which this coefficient should be
+ active. */
RestrictedCoefficient(Coefficient &_c, Array &attr)
{ c = &_c; attr.Copy(active_attr); }
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip)
{ return active_attr[T.Attribute-1] ? c->Eval(T, ip, GetTime()) : 0.0; }
};
+/// Base class for vector Coefficients that optionally depend on time and space.
class VectorCoefficient
{
protected:
@@ -284,9 +344,13 @@ class VectorCoefficient
double time;
public:
+ /// Initialize the VectorCoefficient with vector dimension @a vd.
VectorCoefficient(int vd) { vdim = vd; time = 0.; }
+ /// Set the time for time dependent coefficients
void SetTime(double t) { time = t; }
+
+ /// Get the time for time dependent coefficients
double GetTime() { return time; }
/// Returns dimension of the vector.
@@ -318,19 +382,27 @@ class VectorCoefficient
virtual ~VectorCoefficient() { }
};
+
+/// Vector coefficient that is constant in space and time.
class VectorConstantCoefficient : public VectorCoefficient
{
private:
Vector vec;
public:
+ /// Construct the coefficient with constant vector @a v.
VectorConstantCoefficient(const Vector &v)
: VectorCoefficient(v.Size()), vec(v) { }
using VectorCoefficient::Eval;
+
+ /// Evaluate the vector coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip) { V = vec; }
+
+ /// Return a reference to the constant vector in this class.
const Vector& GetVec() { return vec; }
};
+/// A general C-function vector coefficient
class VectorFunctionCoefficient : public VectorCoefficient
{
private:
@@ -359,13 +431,17 @@ class VectorFunctionCoefficient : public VectorCoefficient
}
using VectorCoefficient::Eval;
+ /// Evaluate the vector coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~VectorFunctionCoefficient() { }
};
-/// Vector coefficient defined by an array of scalar coefficients.
+/** @brief Vector coefficient defined by an array of scalar coefficients.
+ Coefficients that are not set will evaluate to zero in the vector. This
+ object takes ownership of the array of coefficients inside it and deletes
+ them at object destruction. */
class VectorArrayCoefficient : public VectorCoefficient
{
private:
@@ -373,22 +449,27 @@ class VectorArrayCoefficient : public VectorCoefficient
Array ownCoeff;
public:
- /// Construct vector of dim coefficients.
+ /** @brief Construct vector of dim coefficients. The actual coefficients
+ still need to be added with Set(). */
explicit VectorArrayCoefficient(int dim);
/// Returns i'th coefficient.
Coefficient* GetCoeff(int i) { return Coeff[i]; }
+ /// Returns the entire array of coefficients.
Coefficient **GetCoeffs() { return Coeff; }
/// Sets coefficient in the vector.
void Set(int i, Coefficient *c, bool own=true);
- /// Evaluates i'th component of the vector.
+ /// Evaluates i'th component of the vector of coefficients and returns the
+ /// value.
double Eval(int i, ElementTransformation &T, const IntegrationPoint &ip)
{ return Coeff[i] ? Coeff[i]->Eval(T, ip, GetTime()) : 0.0; }
using VectorCoefficient::Eval;
+ /** @brief Evaluate the coefficient. Each element of vector V comes from the
+ associated array of scalar coefficients. */
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -400,18 +481,31 @@ class VectorArrayCoefficient : public VectorCoefficient
class VectorGridFunctionCoefficient : public VectorCoefficient
{
protected:
- GridFunction *GridFunc;
+ const GridFunction *GridFunc;
public:
+ /** @brief Construct an empty coefficient. Calling Eval() before the grid
+ function is set will cause a segfault. */
VectorGridFunctionCoefficient() : VectorCoefficient(0), GridFunc(NULL) { }
- VectorGridFunctionCoefficient(GridFunction *gf);
- void SetGridFunction(GridFunction *gf);
- GridFunction * GetGridFunction() const { return GridFunc; }
+ /** @brief Construct the coefficient with grid function @a gf. The
+ grid function is not owned by the coefficient. */
+ VectorGridFunctionCoefficient(const GridFunction *gf);
+
+ /** @brief Set the grid function for this coefficient. Also sets the Vector
+ dimension to match that of the @a gf. */
+ void SetGridFunction(const GridFunction *gf);
+
+ /// Returns a pointer to the grid function in this Coefficient
+ const GridFunction * GetGridFunction() const { return GridFunc; }
+ /// Evaluate the vector coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
+ /** @brief Evaluate the vector coefficients at all of the locations in the
+ integration rule and write the vectors into the columns of matrix @a
+ M. */
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir);
@@ -422,17 +516,27 @@ class VectorGridFunctionCoefficient : public VectorCoefficient
class GradientGridFunctionCoefficient : public VectorCoefficient
{
protected:
- GridFunction *GridFunc;
+ const GridFunction *GridFunc;
public:
- GradientGridFunctionCoefficient(GridFunction *gf);
- void SetGridFunction(GridFunction *gf);
- GridFunction * GetGridFunction() const { return GridFunc; }
+ /** @brief Construct the coefficient with a scalar grid function @a gf. The
+ grid function is not owned by the coefficient. */
+ GradientGridFunctionCoefficient(const GridFunction *gf);
+ ///Set the scalar grid function.
+ void SetGridFunction(const GridFunction *gf);
+
+ ///Get the scalar grid function.
+ const GridFunction * GetGridFunction() const { return GridFunc; }
+
+ /// Evaluate the gradient vector coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
+ /** @brief Evaluate the gradient vector coefficient at all of the locations
+ in the integration rule and write the vectors into columns of matrix @a
+ M. */
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir);
@@ -443,15 +547,21 @@ class GradientGridFunctionCoefficient : public VectorCoefficient
class CurlGridFunctionCoefficient : public VectorCoefficient
{
protected:
- GridFunction *GridFunc;
+ const GridFunction *GridFunc;
public:
- CurlGridFunctionCoefficient(GridFunction *gf);
+ /** @brief Construct the coefficient with a vector grid function @a gf. The
+ grid function is not owned by the coefficient. */
+ CurlGridFunctionCoefficient(const GridFunction *gf);
+
+ /// Set the vector grid function.
+ void SetGridFunction(const GridFunction *gf);
- void SetGridFunction(GridFunction *gf);
- GridFunction * GetGridFunction() const { return GridFunc; }
+ /// Get the vector grid function.
+ const GridFunction * GetGridFunction() const { return GridFunc; }
using VectorCoefficient::Eval;
+ /// Evaluate the vector curl coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
@@ -462,21 +572,31 @@ class CurlGridFunctionCoefficient : public VectorCoefficient
class DivergenceGridFunctionCoefficient : public Coefficient
{
protected:
- GridFunction *GridFunc;
+ const GridFunction *GridFunc;
public:
- DivergenceGridFunctionCoefficient(GridFunction *gf);
+ /** @brief Construct the coefficient with a vector grid function @a gf. The
+ grid function is not owned by the coefficient. */
+ DivergenceGridFunctionCoefficient(const GridFunction *gf);
- void SetGridFunction(GridFunction *gf) { GridFunc = gf; }
- GridFunction * GetGridFunction() const { return GridFunc; }
+ /// Set the vector grid function.
+ void SetGridFunction(const GridFunction *gf) { GridFunc = gf; }
+ /// Get the vector grid function.
+ const GridFunction * GetGridFunction() const { return GridFunc; }
+
+ /// Evaluate the scalar divergence coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~DivergenceGridFunctionCoefficient() { }
};
-/// VectorDeltaCoefficient: DeltaCoefficient with a direction
+/** @brief Vector coefficient defined by a scalar DeltaCoefficient and a
+ constant vector direction.
+
+ WARNING this cannot be used as a normal coefficient. The usual Eval method
+ is disabled. */
class VectorDeltaCoefficient : public VectorCoefficient
{
protected:
@@ -484,22 +604,36 @@ class VectorDeltaCoefficient : public VectorCoefficient
DeltaCoefficient d;
public:
+ /// Construct with a vector of dimension @a _vdim.
VectorDeltaCoefficient(int _vdim)
: VectorCoefficient(_vdim), dir(_vdim), d() { }
+
+ /** @brief Construct with a Vector object representing the direction and a
+ unit delta function centered at (0.0,0.0,0.0) */
VectorDeltaCoefficient(const Vector& _dir)
: VectorCoefficient(_dir.Size()), dir(_dir), d() { }
+
+ /** @brief Construct with a Vector object representing the direction and a
+ delta function scaled by @a s and centered at (x,0.0,0.0) */
VectorDeltaCoefficient(const Vector& _dir, double x, double s)
: VectorCoefficient(_dir.Size()), dir(_dir), d(x,s) { }
+
+ /** @brief Construct with a Vector object representing the direction and a
+ delta function scaled by @a s and centered at (x,y,0.0) */
VectorDeltaCoefficient(const Vector& _dir, double x, double y, double s)
: VectorCoefficient(_dir.Size()), dir(_dir), d(x,y,s) { }
+
+ /** @brief Construct with a Vector object representing the direction and a
+ delta function scaled by @a s and centered at (x,y,z) */
VectorDeltaCoefficient(const Vector& _dir, double x, double y, double z,
double s)
: VectorCoefficient(_dir.Size()), dir(_dir), d(x,y,z,s) { }
- /// Replace the associated DeltaCoeficient with a new DeltaCoeficient.
- /** The new DeltaCoeficient cannot have a specified weight Coefficient, i.e.
- DeltaCoeficient::Weight() should return NULL. */
+ /// Replace the associated DeltaCoefficient with a new DeltaCoefficient.
+ /** The new DeltaCoefficient cannot have a specified weight Coefficient, i.e.
+ DeltaCoefficient::Weight() should return NULL. */
void SetDeltaCoefficient(const DeltaCoefficient& _d) { d = _d; }
+
/// Return the associated scalar DeltaCoefficient.
DeltaCoefficient& GetDeltaCoefficient() { return d; }
@@ -514,6 +648,7 @@ class VectorDeltaCoefficient : public VectorCoefficient
DeltaCoefficient. */
virtual void EvalDelta(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
+
using VectorCoefficient::Eval;
/** @brief A VectorDeltaFunction cannot be evaluated. Calling this method
will cause an MFEM error, terminating the application. */
@@ -523,7 +658,8 @@ class VectorDeltaCoefficient : public VectorCoefficient
virtual ~VectorDeltaCoefficient() { }
};
-/// VectorCoefficient defined on a subset of domain or boundary attributes
+/** @brief Derived vector coefficient that has the value of the parent vector
+ where it is active and is zero otherwise. */
class VectorRestrictedCoefficient : public VectorCoefficient
{
private:
@@ -531,18 +667,26 @@ class VectorRestrictedCoefficient : public VectorCoefficient
Array active_attr;
public:
+ /** @brief Construct with a parent vector coefficient and an array of zeros
+ and ones representing the attributes for which this coefficient should be
+ active. */
VectorRestrictedCoefficient(VectorCoefficient &vc, Array &attr)
: VectorCoefficient(vc.GetVDim())
{ c = &vc; attr.Copy(active_attr); }
+ /// Evaluate the vector coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
+ /** @brief Evaluate the vector coefficient at all of the locations in the
+ integration rule and write the vectors into the columns of matrix @a
+ M. */
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationRule &ir);
};
+/// Base class for Matrix Coefficients that optionally depend on time and space.
class MatrixCoefficient
{
protected:
@@ -550,16 +694,25 @@ class MatrixCoefficient
double time;
public:
+ /// Construct a dim x dim matrix coefficient.
explicit MatrixCoefficient(int dim) { height = width = dim; time = 0.; }
+ /// Construct a h x w matrix coefficient.
MatrixCoefficient(int h, int w) : height(h), width(w), time(0.) { }
+ /// Set the time for time dependent coefficients
void SetTime(double t) { time = t; }
+
+ /// Get the time for time dependent coefficients
double GetTime() { return time; }
+ /// Get the height of the matrix.
int GetHeight() const { return height; }
+
+ /// Get the width of the matrix.
int GetWidth() const { return width; }
- // For backward compatibility
+
+ /// For backward compatibility get the width of the matrix.
int GetVDim() const { return width; }
/** @brief Evaluate the matrix coefficient in the element described by @a T
@@ -573,18 +726,26 @@ class MatrixCoefficient
virtual ~MatrixCoefficient() { }
};
+
+/// A matrix coefficient that is constant in space and time.
class MatrixConstantCoefficient : public MatrixCoefficient
{
private:
DenseMatrix mat;
public:
+ ///Construct using matrix @a m for the constant.
MatrixConstantCoefficient(const DenseMatrix &m)
: MatrixCoefficient(m.Height(), m.Width()), mat(m) { }
using MatrixCoefficient::Eval;
+ /// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip) { M = mat; }
};
+
+/** @brief A matrix coefficient with an optional scalar coefficient multiplier
+ \a q. The matrix function can either be represented by a C-function or a
+ constant matrix provided when constructing this object. */
class MatrixFunctionCoefficient : public MatrixCoefficient
{
private:
@@ -594,7 +755,8 @@ class MatrixFunctionCoefficient : public MatrixCoefficient
DenseMatrix mat;
public:
- /// Construct a time-independent square matrix coefficient from a C-function
+ /// Construct a square matrix coefficient from a C-function without time
+ /// dependence.
MatrixFunctionCoefficient(int dim, void (*F)(const Vector &, DenseMatrix &),
Coefficient *q = NULL)
: MatrixCoefficient(dim), Q(q)
@@ -613,7 +775,8 @@ class MatrixFunctionCoefficient : public MatrixCoefficient
mat = m;
}
- /// Construct a time-dependent square matrix coefficient from a C-function
+ /// Construct a square matrix coefficient from a C-function with
+ /// time-dependence.
MatrixFunctionCoefficient(int dim,
void (*TDF)(const Vector &, double, DenseMatrix &),
Coefficient *q = NULL)
@@ -624,12 +787,18 @@ class MatrixFunctionCoefficient : public MatrixCoefficient
mat.SetSize(0);
}
+ /// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~MatrixFunctionCoefficient() { }
};
+
+
+/** @brief Matrix coefficient defined by a matrix of scalar coefficients.
+ Coefficients that are not set will evaluate to zero in the vector. The
+ coefficient is stored as a flat Array with indexing (i,j) -> i*width+j. */
class MatrixArrayCoefficient : public MatrixCoefficient
{
private:
@@ -637,23 +806,33 @@ class MatrixArrayCoefficient : public MatrixCoefficient
Array ownCoeff;
public:
-
+ /** @brief Construct a coefficient matrix of dimensions @a dim * @a dim. The
+ actual coefficients still need to be added with Set(). */
explicit MatrixArrayCoefficient (int dim);
+ /// Get the coefficient located at (i,j) in the matrix.
Coefficient* GetCoeff (int i, int j) { return Coeff[i*width+j]; }
+ /** @brief Set the coefficient located at (i,j) in the matrix. By default by
+ default this will take ownership of the Coefficient passed in, but this
+ can be overridden with the @a own parameter. */
void Set(int i, int j, Coefficient * c, bool own=true);
+ /// Evaluate coefficient located at (i,j) in the matrix using integration
+ /// point @a ip.
double Eval(int i, int j, ElementTransformation &T, const IntegrationPoint &ip)
{ return Coeff[i*width+j] ? Coeff[i*width+j] -> Eval(T, ip, GetTime()) : 0.0; }
+ /// Evaluate the matrix coefficient @a ip.
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
virtual ~MatrixArrayCoefficient();
};
-/// MatrixCoefficient defined on a subset of domain or boundary attributes
+
+/** @brief Derived matrix coefficient that has the value of the parent matrix
+ coefficient where it is active and is zero otherwise. */
class MatrixRestrictedCoefficient : public MatrixCoefficient
{
private:
@@ -661,10 +840,14 @@ class MatrixRestrictedCoefficient : public MatrixCoefficient
Array active_attr;
public:
+ /** @brief Construct with a parent matrix coefficient and an array of zeros
+ and ones representing the attributes for which this coefficient should be
+ active. */
MatrixRestrictedCoefficient(MatrixCoefficient &mc, Array &attr)
: MatrixCoefficient(mc.GetHeight(), mc.GetWidth())
{ c = &mc; attr.Copy(active_attr); }
+ /// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &K, ElementTransformation &T,
const IntegrationPoint &ip);
};
@@ -682,12 +865,12 @@ class SumCoefficient : public Coefficient
double beta;
public:
- // Result is _alpha * A + _beta * B
+ /// Construct with the two coefficients. Result is _alpha * A + _beta * B.
SumCoefficient(Coefficient &A, Coefficient &B,
double _alpha = 1.0, double _beta = 1.0)
: a(&A), b(&B), alpha(_alpha), beta(_beta) { }
- /// Evaluate the coefficient
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{ return alpha * a->Eval(T, ip) + beta * b->Eval(T, ip); }
@@ -701,10 +884,11 @@ class ProductCoefficient : public Coefficient
Coefficient * b;
public:
+ /// Construct with the two coefficients. Result is A * B.
ProductCoefficient(Coefficient &A, Coefficient &B)
: a(&A), b(&B) { }
- /// Evaluate the coefficient
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{ return a->Eval(T, ip) * b->Eval(T, ip); }
@@ -719,16 +903,17 @@ class PowerCoefficient : public Coefficient
double p;
public:
- // Result is A^p
+ /// Construct with a coefficient and a constant power @a _p. Result is A^p.
PowerCoefficient(Coefficient &A, double _p)
: a(&A), p(_p) { }
- /// Evaluate the coefficient
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip)
{ return pow(a->Eval(T, ip), p); }
};
+
/// Scalar coefficient defined as the inner product of two vector coefficients
class InnerProductCoefficient : public Coefficient
{
@@ -739,14 +924,15 @@ class InnerProductCoefficient : public Coefficient
mutable Vector va;
mutable Vector vb;
public:
+ /// Construct with the two vector coefficients. Result is \f$ A \cdot B \f$.
InnerProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
- /// Evaluate the coefficient
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
-/// Scalar coefficient defined as a cross product of two vectors in 2D
+/// Scalar coefficient defined as a cross product of two vectors in the xy-plane.
class VectorRotProductCoefficient : public Coefficient
{
private:
@@ -757,8 +943,10 @@ class VectorRotProductCoefficient : public Coefficient
mutable Vector vb;
public:
+ /// Construct with the two vector coefficients. Result is \f$ A_x B_y - A_y * B_x; \f$.
VectorRotProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
+ /// Evaluate the coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
@@ -772,9 +960,10 @@ class DeterminantCoefficient : public Coefficient
mutable DenseMatrix ma;
public:
+ /// Construct with the matrix.
DeterminantCoefficient(MatrixCoefficient &A);
- /// Evaluate the coefficient
+ /// Evaluate the determinant coefficient at @a ip.
virtual double Eval(ElementTransformation &T,
const IntegrationPoint &ip);
};
@@ -792,17 +981,17 @@ class VectorSumCoefficient : public VectorCoefficient
mutable Vector va;
public:
- // Result is _alpha * A + _beta * B
+ /// Construct with the two vector coefficients. Result is _alpha * A + _beta * B.
VectorSumCoefficient(VectorCoefficient &A, VectorCoefficient &B,
double _alpha = 1.0, double _beta = 1.0);
- /// Evaluate the coefficient
+ /// Evaluate the coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
-/// Vector coefficient defined as a product of a scalar and a vector
+/// Vector coefficient defined as a product of scalar and vector coefficients.
class ScalarVectorProductCoefficient : public VectorCoefficient
{
private:
@@ -810,8 +999,10 @@ class ScalarVectorProductCoefficient : public VectorCoefficient
VectorCoefficient * b;
public:
+ /// Construct with the two coefficients. Result is A * B.
ScalarVectorProductCoefficient(Coefficient &A, VectorCoefficient &B);
+ /// Evaluate the coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
@@ -828,14 +1019,17 @@ class VectorCrossProductCoefficient : public VectorCoefficient
mutable Vector vb;
public:
+ /// Construct with the two coefficients. Result is A x B.
VectorCrossProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
+ /// Evaluate the coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
-/// Vector coefficient defined as a matrix vector product
+/** @brief Vector coefficient defined as a product of a matrix coefficient and
+ a vector coefficient. */
class MatVecCoefficient : public VectorCoefficient
{
private:
@@ -846,28 +1040,32 @@ class MatVecCoefficient : public VectorCoefficient
mutable Vector vb;
public:
+ /// Construct with the two coefficients. Result is A*B.
MatVecCoefficient(MatrixCoefficient &A, VectorCoefficient &B);
+ /// Evaluate the vector coefficient at @a ip.
virtual void Eval(Vector &V, ElementTransformation &T,
const IntegrationPoint &ip);
using VectorCoefficient::Eval;
};
-/// Matrix coefficient defined as the identity of dimension d
+/// Constant matrix coefficient defined as the identity of dimension d
class IdentityMatrixCoefficient : public MatrixCoefficient
{
private:
int dim;
public:
+ /// Construct with the dimension of the square identity matrix.
IdentityMatrixCoefficient(int d)
: MatrixCoefficient(d, d), dim(d) { }
+ /// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
-/// Matrix coefficient defined as the sum of two matrix coefficients
+/// Matrix coefficient defined as the sum of two matrix coefficients.
class MatrixSumCoefficient : public MatrixCoefficient
{
private:
@@ -880,16 +1078,17 @@ class MatrixSumCoefficient : public MatrixCoefficient
mutable DenseMatrix ma;
public:
- // Result is _alpha * A + _beta * B
+ /// Construct with the two coefficients. Result is _alpha * A + _beta * B.
MatrixSumCoefficient(MatrixCoefficient &A, MatrixCoefficient &B,
double _alpha = 1.0, double _beta = 1.0);
- /// Evaluate the coefficient
+ /// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
-/// Matrix coefficient defined as a product of a scalar and a matrix
+/** @brief Matrix coefficient defined as a product of a scalar coefficient and a
+ matrix coefficient.*/
class ScalarMatrixProductCoefficient : public MatrixCoefficient
{
private:
@@ -897,39 +1096,45 @@ class ScalarMatrixProductCoefficient : public MatrixCoefficient
MatrixCoefficient * b;
public:
+ /// Construct with the two coefficients. Result is A*B.
ScalarMatrixProductCoefficient(Coefficient &A, MatrixCoefficient &B);
+ /// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
-/// Matrix coefficient defined as the transpose a matrix
+/// Matrix coefficient defined as the transpose a matrix coefficient
class TransposeMatrixCoefficient : public MatrixCoefficient
{
private:
MatrixCoefficient * a;
public:
+ /// Construct with the matrix coefficient. Result is \f$ A^T \f$.
TransposeMatrixCoefficient(MatrixCoefficient &A);
+ /// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
-/// Matrix coefficient defined as the inverse a matrix
+/// Matrix coefficient defined as the inverse a matrix coefficient.
class InverseMatrixCoefficient : public MatrixCoefficient
{
private:
MatrixCoefficient * a;
public:
+ /// Construct with the matrix coefficient. Result is \f$ A^{-1} \f$.
InverseMatrixCoefficient(MatrixCoefficient &A);
+ /// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
-/// Matrix coefficient defined as the outer product of two vectors
+/// Matrix coefficient defined as the outer product of two vector coefficients.
class OuterProductCoefficient : public MatrixCoefficient
{
private:
@@ -940,29 +1145,80 @@ class OuterProductCoefficient : public MatrixCoefficient
mutable Vector vb;
public:
+ /// Construct with two vector coefficients. Result is \f$ A B^T \f$.
OuterProductCoefficient(VectorCoefficient &A, VectorCoefficient &B);
+ /// Evaluate the matrix coefficient at @a ip.
virtual void Eval(DenseMatrix &M, ElementTransformation &T,
const IntegrationPoint &ip);
};
-/** Compute the Lp norm of a function f.
+
+class QuadratureFunction;
+
+/** @brief Vector quadrature function coefficient which requires that the
+ quadrature rules used for this vector coefficient be the same as those that
+ live within the supplied QuadratureFunction. */
+class VectorQuadratureFunctionCoefficient : public VectorCoefficient
+{
+private:
+ const QuadratureFunction &QuadF; //do not own
+ int index;
+
+public:
+ /// Constructor with a quadrature function as input
+ VectorQuadratureFunctionCoefficient(QuadratureFunction &qf);
+
+ /** Set the starting index within the QuadFunc that'll be used to project
+ outwards as well as the corresponding length. The projected length should
+ have the bounds of 1 <= length <= (length QuadFunc - index). */
+ void SetComponent(int _index, int _length);
+
+ const QuadratureFunction& GetQuadFunction() const { return QuadF; }
+
+ using VectorCoefficient::Eval;
+ virtual void Eval(Vector &V, ElementTransformation &T,
+ const IntegrationPoint &ip);
+
+ virtual ~VectorQuadratureFunctionCoefficient() { }
+};
+
+/** @brief Quadrature function coefficient which requires that the quadrature
+ rules used for this coefficient be the same as those that live within the
+ supplied QuadratureFunction. */
+class QuadratureFunctionCoefficient : public Coefficient
+{
+private:
+ const QuadratureFunction &QuadF;
+
+public:
+ /// Constructor with a quadrature function as input
+ QuadratureFunctionCoefficient(QuadratureFunction &qf);
+
+ const QuadratureFunction& GetQuadFunction() const { return QuadF; }
+
+ virtual double Eval(ElementTransformation &T, const IntegrationPoint &ip);
+
+ virtual ~QuadratureFunctionCoefficient() { }
+};
+
+/** @brief Compute the Lp norm of a function f.
\f$ \| f \|_{Lp} = ( \int_\Omega | f |^p d\Omega)^{1/p} \f$ */
double ComputeLpNorm(double p, Coefficient &coeff, Mesh &mesh,
const IntegrationRule *irs[]);
-/** Compute the Lp norm of a vector function f = {f_i}_i=1...N.
+/** @brief Compute the Lp norm of a vector function f = {f_i}_i=1...N.
\f$ \| f \|_{Lp} = ( \sum_i \| f_i \|_{Lp}^p )^{1/p} \f$ */
double ComputeLpNorm(double p, VectorCoefficient &coeff, Mesh &mesh,
const IntegrationRule *irs[]);
#ifdef MFEM_USE_MPI
-/** Compute the global Lp norm of a function f.
+/** @brief Compute the global Lp norm of a function f.
\f$ \| f \|_{Lp} = ( \int_\Omega | f |^p d\Omega)^{1/p} \f$ */
double ComputeGlobalLpNorm(double p, Coefficient &coeff, ParMesh &pmesh,
const IntegrationRule *irs[]);
-/** Compute the global Lp norm of a vector function f = {f_i}_i=1...N.
+/** @brief Compute the global Lp norm of a vector function f = {f_i}_i=1...N.
\f$ \| f \|_{Lp} = ( \sum_i \| f_i \|_{Lp}^p )^{1/p} \f$ */
double ComputeGlobalLpNorm(double p, VectorCoefficient &coeff, ParMesh &pmesh,
const IntegrationRule *irs[]);
diff --git a/fem/datacollection.cpp b/fem/datacollection.cpp
index 0e8d1aca8d1..20aee629d2a 100644
--- a/fem/datacollection.cpp
+++ b/fem/datacollection.cpp
@@ -739,12 +739,6 @@ ParaViewDataCollection::ParaViewDataCollection(const std::string&
#endif
}
-void ParaViewDataCollection::RegisterField(const std::string& field_name,
- mfem::GridFunction *gf)
-{
- DataCollection::RegisterField(field_name,gf);
-}
-
void ParaViewDataCollection::SetLevelsOfDetail(int levels_of_detail_)
{
levels_of_detail = levels_of_detail_;
@@ -815,7 +809,7 @@ void ParaViewDataCollection::Save()
// the directory is created
// create pvd file if needed
- if (!pvd_stream.is_open())
+ if (myid == 0 && !pvd_stream.is_open())
{
std::string dpath=GenerateCollectionPath();
std::string pvdname=dpath+"/"+GeneratePVDFileName();
diff --git a/fem/datacollection.hpp b/fem/datacollection.hpp
index 5153ced9e81..0ed925b6ebc 100644
--- a/fem/datacollection.hpp
+++ b/fem/datacollection.hpp
@@ -501,10 +501,6 @@ class ParaViewDataCollection : public DataCollection
ParaViewDataCollection(const std::string& collection_name,
mfem::Mesh *mesh_ = NULL);
- /// Add a grid function to the collection
- virtual void RegisterField(const std::string& field_name,
- mfem::GridFunction *gf) override;
-
/// Set refinement levels - every element is uniformly split based on
/// levels_of_detail_
void SetLevelsOfDetail(int levels_of_detail_);
diff --git a/fem/eltrans.cpp b/fem/eltrans.cpp
index 26419b25322..a0b1c5dda9d 100644
--- a/fem/eltrans.cpp
+++ b/fem/eltrans.cpp
@@ -19,6 +19,7 @@ namespace mfem
ElementTransformation::ElementTransformation()
: IntPoint(static_cast(NULL)),
EvalState(0),
+ geom(Geometry::INVALID),
Attribute(-1),
ElementNo(-1)
{ }
@@ -551,4 +552,76 @@ void IntegrationPointTransformation::Transform (const IntegrationRule &ir1,
}
}
+void FaceElementTransformations::SetIntPoint(const IntegrationPoint *ip)
+{
+ IsoparametricTransformation::SetIntPoint(ip);
+
+ if (Elem1)
+ {
+ Loc1.Transform(*ip, eip1);
+ Elem1->SetIntPoint(&eip1);
+ }
+ if (Elem2)
+ {
+ Loc2.Transform(*ip, eip2);
+ Elem2->SetIntPoint(&eip2);
+ }
+}
+
+ElementTransformation &
+FaceElementTransformations::GetElement1Transformation()
+{
+ MFEM_VERIFY(mask & 1 && Elem1 != NULL, "The ElementTransformation "
+ "for the element has not been configured for side 1.");
+ return *Elem1;
+}
+
+ElementTransformation &
+FaceElementTransformations::GetElement2Transformation()
+{
+ MFEM_VERIFY(mask & 2 && Elem2 != NULL, "The ElementTransformation "
+ "for the element has not been configured for side 2.");
+ return *Elem2;
+}
+
+IntegrationPointTransformation &
+FaceElementTransformations::GetIntPoint1Transformation()
+{
+ MFEM_VERIFY(mask & 4, "The IntegrationPointTransformation "
+ "for the element has not been configured for side 1.");
+ return Loc1;
+}
+
+IntegrationPointTransformation &
+FaceElementTransformations::GetIntPoint2Transformation()
+{
+ MFEM_VERIFY(mask & 8, "The IntegrationPointTransformation "
+ "for the element has not been configured for side 2.");
+ return Loc2;
+}
+
+void FaceElementTransformations::Transform(const IntegrationPoint &ip,
+ Vector &trans)
+{
+ MFEM_VERIFY(mask & 16, "The ElementTransformation "
+ "for the face has not been configured.");
+ IsoparametricTransformation::Transform(ip, trans);
+}
+
+void FaceElementTransformations::Transform(const IntegrationRule &ir,
+ DenseMatrix &tr)
+{
+ MFEM_VERIFY(mask & 16, "The ElementTransformation "
+ "for the face has not been configured.");
+ IsoparametricTransformation::Transform(ir, tr);
+}
+
+void FaceElementTransformations::Transform(const DenseMatrix &matrix,
+ DenseMatrix &result)
+{
+ MFEM_VERIFY(mask & 16, "The ElementTransformation "
+ "for the face has not been configured.");
+ IsoparametricTransformation::Transform(matrix, result);
+}
+
}
diff --git a/fem/eltrans.hpp b/fem/eltrans.hpp
index 78daf9a733c..6fe35ee25f5 100644
--- a/fem/eltrans.hpp
+++ b/fem/eltrans.hpp
@@ -38,9 +38,12 @@ class ElementTransformation
};
Geometry::Type geom;
- // Evaluate the Jacobian of the transformation at the IntPoint and store it
- // in dFdx.
+ /** @brief Evaluate the Jacobian of the transformation at the IntPoint and
+ store it in dFdx. */
virtual const DenseMatrix &EvalJacobian() = 0;
+
+ /** @brief Evaluate the Hessian of the transformation at the IntPoint and
+ store it in d2Fdx2. */
virtual const DenseMatrix &EvalHessian() = 0;
double EvalWeight();
@@ -48,18 +51,53 @@ class ElementTransformation
const DenseMatrix &EvalInverseJ();
public:
- int Attribute, ElementNo;
+
+ /** This enumeration declares the values stored in
+ ElementTransformation::ElementType and indicates which group of objects
+ the index stored in ElementTransformation::ElementNo refers:
+
+ | ElementType | Range of ElementNo
+ +-------------+-------------------------
+ | ELEMENT | [0, Mesh::GetNE() )
+ | BDR_ELEMENT | [0, Mesh::GetNBE() )
+ | EDGE | [0, Mesh::GetNEdges() )
+ | FACE | [0, Mesh::GetNFaces() )
+ | BDR_FACE | [0, Mesh::GetNBE() )
+ */
+ enum
+ {
+ ELEMENT = 1,
+ BDR_ELEMENT = 2,
+ EDGE = 3,
+ FACE = 4,
+ BDR_FACE = 5
+ };
+
+ int Attribute, ElementNo, ElementType;
ElementTransformation();
+ /** @brief Set the integration point @a ip that weights and Jacobians will
+ be evaluated at. */
void SetIntPoint(const IntegrationPoint *ip)
{ IntPoint = ip; EvalState = 0; }
+
+ /** @brief Get a const reference to the currently set integration point. This
+ will return NULL if no integration point is set. */
const IntegrationPoint &GetIntPoint() { return *IntPoint; }
+ /** @brief Transform integration point from reference coordinates to
+ physical coordinates and store them in the vector. */
virtual void Transform(const IntegrationPoint &, Vector &) = 0;
+
+ /** @brief Transform all the integration points from the integration rule
+ from reference coordinates to physical
+ coordinates and store them as column vectors in the matrix. */
virtual void Transform(const IntegrationRule &, DenseMatrix &) = 0;
- /// Transform columns of 'matrix', store result in 'result'.
+ /** @brief Transform all the integration points from the column vectors
+ of @a matrix from reference coordinates to physical
+ coordinates and store them as column vectors in @a result. */
virtual void Transform(const DenseMatrix &matrix, DenseMatrix &result) = 0;
/** @brief Return the Jacobian matrix of the transformation at the currently
@@ -70,27 +108,44 @@ class ElementTransformation
const DenseMatrix &Jacobian()
{ return (EvalState & JACOBIAN_MASK) ? dFdx : EvalJacobian(); }
+
+ /** @brief Return the Hessian matrix of the transformation at the currently
+ set IntegrationPoint, using the method SetIntPoint(). */
const DenseMatrix &Hessian()
{ return (EvalState & HESSIAN_MASK) ? d2Fdx2 : EvalHessian(); }
+ /** @brief Return the weight of the Jacobian matrix of the transformation
+ at the currently set IntegrationPoint.
+ The Weight evaluates to \f$ \sqrt{\lvert J^T J \rvert} \f$. */
double Weight() { return (EvalState & WEIGHT_MASK) ? Wght : EvalWeight(); }
+ /** @brief Return the adjugate of the Jacobian matrix of the transformation
+ at the currently set IntegrationPoint. */
const DenseMatrix &AdjugateJacobian()
{ return (EvalState & ADJUGATE_MASK) ? adjJ : EvalAdjugateJ(); }
+ /** @brief Return the inverse of the Jacobian matrix of the transformation
+ at the currently set IntegrationPoint. */
const DenseMatrix &InverseJacobian()
{ return (EvalState & INVERSE_MASK) ? invJ : EvalInverseJ(); }
+ /// Return the order of the current element we are using for the transformation.
virtual int Order() const = 0;
+
+ /// Return the order of the elements of the Jacobian of the transformation.
virtual int OrderJ() const = 0;
+
+ /** @brief Return the order of the determinant of the Jacobian (weight)
+ of the transformation. */
virtual int OrderW() const = 0;
- /// Order of adj(J)^t.grad(fi)
+
+ /// Return the order of \f$ adj(J)^T \nabla fi \f$
virtual int OrderGrad(const FiniteElement *fe) const = 0;
/// Return the Geometry::Type of the reference element.
Geometry::Type GetGeometryType() const { return geom; }
- /// Return the dimension of the reference element.
+ /// Return the topological dimension of the reference element.
int GetDimension() const { return Geometry::Dimension[geom]; }
/// Get the dimension of the target (physical) space.
@@ -286,7 +341,7 @@ class InverseElementTransformation
virtual int Transform(const Vector &pt, IntegrationPoint &ip);
};
-
+/// A standard isoparametric element transformation
class IsoparametricTransformation : public ElementTransformation
{
private:
@@ -296,26 +351,29 @@ class IsoparametricTransformation : public ElementTransformation
const FiniteElement *FElem;
DenseMatrix PointMat; // dim x dof
- // Evaluate the Jacobian of the transformation at the IntPoint and store it
- // in dFdx.
+ /** @brief Evaluate the Jacobian of the transformation at the IntPoint and
+ store it in dFdx. */
virtual const DenseMatrix &EvalJacobian();
// Evaluate the Hessian of the transformation at the IntPoint and store it
// in d2Fdx2.
virtual const DenseMatrix &EvalHessian();
public:
+ /// Set the element that will be used to compute the transformations
void SetFE(const FiniteElement *FE) { FElem = FE; geom = FE->GetGeomType(); }
+
+ /// Get the current element used to compute the transformations
const FiniteElement* GetFE() const { return FElem; }
/// @brief Set the underlying point matrix describing the transformation.
/** The dimensions of the matrix are space-dim x dof. The transformation is
defined as
+ \f$ x = F( \hat x ) = P \phi( \hat x ) \f$
- x = F(xh) = P . phi(xh),
-
- where xh (x hat) is the reference point, x is the corresponding physical
- point, P is the point matrix, and phi(xh) is the column-vector of all
- basis functions evaluated at xh. The columns of P represent the control
- points in physical space defining the transformation. */
+ where \f$ \hat x \f$ is the reference point, @a x is the corresponding
+ physical point, @a P is the point matrix, and \f$ \phi( \hat x ) \f$ is
+ the column-vector of all basis functions evaluated at \f$ \hat x \f$ .
+ The columns of @a P represent the control points in physical space
+ defining the transformation. */
void SetPointMat(const DenseMatrix &pm) { PointMat = pm; }
/// Return the stored point matrix.
@@ -324,19 +382,44 @@ class IsoparametricTransformation : public ElementTransformation
/// Write access to the stored point matrix. Use with caution.
DenseMatrix &GetPointMat() { return PointMat; }
+ /// Set the FiniteElement Geometry for the reference elements being used.
void SetIdentityTransformation(Geometry::Type GeomType);
+ /** @brief Transform integration point from reference coordinates to
+ physical coordinates and store them in the vector. */
virtual void Transform(const IntegrationPoint &, Vector &);
+
+ /** @brief Transform all the integration points from the integration rule
+ from reference coordinates to physical
+ coordinates and store them as column vectors in the matrix. */
virtual void Transform(const IntegrationRule &, DenseMatrix &);
+
+ /** @brief Transform all the integration points from the column vectors
+ of @a matrix from reference coordinates to physical
+ coordinates and store them as column vectors in @a result. */
virtual void Transform(const DenseMatrix &matrix, DenseMatrix &result);
+ /// Return the order of the current element we are using for the transformation.
virtual int Order() const { return FElem->GetOrder(); }
+
+ /// Return the order of the elements of the Jacobian of the transformation.
virtual int OrderJ() const;
+
+ /** @brief Return the order of the determinant of the Jacobian (weight)
+ of the transformation. */
virtual int OrderW() const;
+
+ /// Return the order of \f$ adj(J)^T \nabla fi \f$
virtual int OrderGrad(const FiniteElement *fe) const;
virtual int GetSpaceDim() const { return PointMat.Height(); }
+ /** @brief Transform a point @a pt from physical space to a point @a ip in
+ reference space. */
+ /** Attempt to find the IntegrationPoint that is transformed into the given
+ point in physical space. If the inversion fails a non-zero value is
+ returned. This method is not 100 percent reliable for non-linear
+ transformations. */
virtual int TransformBack(const Vector & v, IntegrationPoint & ip)
{
InverseElementTransformation inv_tr(this);
@@ -356,15 +439,62 @@ class IntegrationPointTransformation
void Transform (const IntegrationRule &, IntegrationRule &);
};
-class FaceElementTransformations
+
+class FaceElementTransformations : public IsoparametricTransformation
{
+private:
+ int mask;
+
+ IntegrationPoint eip1, eip2;
+
public:
- int Elem1No, Elem2No, FaceGeom;
- ElementTransformation *Elem1, *Elem2, *Face;
+ int Elem1No, Elem2No;
+ Geometry::Type &FaceGeom; ///< @deprecated Use GetGeometryType instead
+ ElementTransformation *Elem1, *Elem2;
+ ElementTransformation *Face; ///< @deprecated No longer necessary
IntegrationPointTransformation Loc1, Loc2;
+
+ FaceElementTransformations() : FaceGeom(geom), Face(this) {}
+
+ /** @brief Method to set the geometry type of the face.
+
+ @note This method is designed to be used when
+ [Par]Mesh::GetFaceTransformation will not be called i.e. when the face
+ transformation will not be needed but the neighboring element
+ transformations will be. Using this method to override the GeometryType
+ should only be done with great care.
+ */
+ void SetGeometryType(Geometry::Type g) { geom = g; }
+
+ /// Set the mask indicating which portions of the object have been setup
+ /** The argument @a m is a bitmask used in
+ Mesh::GetFaceElementTransformations to indicate which portions of the
+ FaceElement Transformations object have been configured.
+
+ mask & 1: Elem1 is configured
+ mask & 2: Elem2 is configured
+ mask & 4: Loc1 is configured
+ mask & 8: Loc2 is configured
+ mask & 16: The Face transformation itself is configured
+ */
+ void SetConfigurationMask(int m) { mask = m; }
+ int GetConfigurationMask() const { return mask; }
+
+ /** @brief Set the integration point in the Face and the two neighboring
+ elements, if present. */
+ void SetIntPoint(const IntegrationPoint *ip);
+
+ virtual void Transform(const IntegrationPoint &, Vector &);
+ virtual void Transform(const IntegrationRule &, DenseMatrix &);
+ virtual void Transform(const DenseMatrix &matrix, DenseMatrix &result);
+
+ ElementTransformation & GetElement1Transformation();
+ ElementTransformation & GetElement2Transformation();
+ IntegrationPointTransformation & GetIntPoint1Transformation();
+ IntegrationPointTransformation & GetIntPoint2Transformation();
};
-/* Elem1(Loc1(x)) = Face(x) = Elem2(Loc2(x))
+/** Elem1(Loc1(x)) = Face(x) = Elem2(Loc2(x))
Physical Space
diff --git a/fem/estimators.cpp b/fem/estimators.cpp
index 47851c4e82f..a0289b5ccab 100644
--- a/fem/estimators.cpp
+++ b/fem/estimators.cpp
@@ -50,4 +50,21 @@ void L2ZienkiewiczZhuEstimator::ComputeEstimates()
#endif // MFEM_USE_MPI
+void LpErrorEstimator::ComputeEstimates()
+{
+ MFEM_VERIFY(coef != NULL || vcoef != NULL,
+ "LpErrorEstimator has no coefficient! Call SetCoef first.");
+
+ error_estimates.SetSize(sol->FESpace()->GetMesh()->GetNE());
+ if (coef)
+ {
+ sol->ComputeElementLpErrors(local_norm_p, *coef, error_estimates);
+ }
+ else
+ {
+ sol->ComputeElementLpErrors(local_norm_p, *vcoef, error_estimates);
+ }
+ current_sequence = sol->FESpace()->GetMesh()->GetSequence();
+}
+
} // namespace mfem
diff --git a/fem/estimators.hpp b/fem/estimators.hpp
index 75c4eba8256..16f66024dee 100644
--- a/fem/estimators.hpp
+++ b/fem/estimators.hpp
@@ -45,6 +45,7 @@ class ErrorEstimator : public AbstractErrorEstimator
/// Force recomputation of the estimates on the next call to GetLocalErrors.
virtual void Reset() = 0;
+ /// Destruct the error estimator
virtual ~ErrorEstimator() { }
};
@@ -66,6 +67,14 @@ class AnisotropicErrorEstimator : public ErrorEstimator
/** @brief The ZienkiewiczZhuEstimator class implements the Zienkiewicz-Zhu
error estimation procedure.
+ Zienkiewicz, O.C. and Zhu, J.Z., The superconvergent patch recovery
+ and a posteriori error estimates. Part 1: The recovery technique.
+ Int. J. Num. Meth. Engng. 33, 1331-1364 (1992).
+
+ Zienkiewicz, O.C. and Zhu, J.Z., The superconvergent patch recovery
+ and a posteriori error estimates. Part 2: Error estimates and adaptivity.
+ Int. J. Num. Meth. Engng. 33, 1365-1382 (1992).
+
The required BilinearFormIntegrator must implement the methods
ComputeElementFlux() and ComputeFluxEnergy().
*/
@@ -217,6 +226,7 @@ class L2ZienkiewiczZhuEstimator : public ErrorEstimator
class when needed.*/
bool own_flux_fes; ///< Ownership flag for flux_space and smooth_flux_space.
+ /// Initialize with the integrator, solution, and flux finite element spaces.
void Init(BilinearFormIntegrator &integ,
ParGridFunction &sol,
ParFiniteElementSpace *flux_fes,
@@ -304,6 +314,88 @@ class L2ZienkiewiczZhuEstimator : public ErrorEstimator
#endif // MFEM_USE_MPI
+/** @brief The LpErrorEstimator class compares the solution to a known
+ coefficient.
+
+ This class can be used, for example, to adapt a mesh to a non-trivial
+ initial condition in a time-dependent simulation. It can also be used to
+ force refinement in the neighborhood of small features before switching to a
+ more traditional error estimator.
+
+ The LpErrorEstimator supports either scalar or vector coefficients and works
+ both in serial and in parallel.
+*/
+class LpErrorEstimator : public ErrorEstimator
+{
+protected:
+ long current_sequence;
+ int local_norm_p;
+ Vector error_estimates;
+
+ Coefficient * coef;
+ VectorCoefficient * vcoef;
+ GridFunction * sol;
+
+ /// Check if the mesh of the solution was modified.
+ bool MeshIsModified()
+ {
+ long mesh_sequence = sol->FESpace()->GetMesh()->GetSequence();
+ MFEM_ASSERT(mesh_sequence >= current_sequence, "");
+ return (mesh_sequence > current_sequence);
+ }
+
+ /// Compute the element error estimates.
+ void ComputeEstimates();
+
+public:
+ /** @brief Construct a new LpErrorEstimator object for a scalar field.
+ @param p Integer which selects which Lp norm to use.
+ @param sol The GridFunction representation of the scalar field.
+ Note: the coefficient must be set before use with the SetCoef method.
+ */
+ LpErrorEstimator(int p, GridFunction &sol)
+ : current_sequence(-1), local_norm_p(p),
+ error_estimates(0), coef(NULL), vcoef(NULL), sol(&sol) { }
+
+ /** @brief Construct a new LpErrorEstimator object for a scalar field.
+ @param p Integer which selects which Lp norm to use.
+ @param coef The scalar Coefficient to compare to the solution.
+ @param sol The GridFunction representation of the scalar field.
+ */
+ LpErrorEstimator(int p, Coefficient &coef, GridFunction &sol)
+ : current_sequence(-1), local_norm_p(p),
+ error_estimates(0), coef(&coef), vcoef(NULL), sol(&sol) { }
+
+ /** @brief Construct a new LpErrorEstimator object for a vector field.
+ @param p Integer which selects which Lp norm to use.
+ @param coef The vector VectorCoefficient to compare to the solution.
+ @param sol The GridFunction representation of the vector field.
+ */
+ LpErrorEstimator(int p, VectorCoefficient &coef, GridFunction &sol)
+ : current_sequence(-1), local_norm_p(p),
+ error_estimates(0), coef(NULL), vcoef(&coef), sol(&sol) { }
+
+ /** @brief Set the exponent, p, of the Lp norm used for computing the local
+ element errors. */
+ void SetLocalErrorNormP(int p) { local_norm_p = p; }
+
+ void SetCoef(Coefficient &A) { coef = &A; }
+ void SetCoef(VectorCoefficient &A) { vcoef = &A; }
+
+ /// Reset the error estimator.
+ virtual void Reset() { current_sequence = -1; }
+
+ /// Get a Vector with all element errors.
+ virtual const Vector &GetLocalErrors()
+ {
+ if (MeshIsModified()) { ComputeEstimates(); }
+ return error_estimates;
+ }
+
+ /// Destructor
+ virtual ~LpErrorEstimator() {}
+};
+
} // namespace mfem
#endif // MFEM_ERROR_ESTIMATORS
diff --git a/fem/fe.cpp b/fem/fe.cpp
index 5a3f16f78f4..46e657a2794 100644
--- a/fem/fe.cpp
+++ b/fem/fe.cpp
@@ -25,15 +25,15 @@ using namespace std;
FiniteElement::FiniteElement(int D, Geometry::Type G, int Do, int O, int F)
: Nodes(Do)
{
- Dim = D ; GeomType = G ; Dof = Do ; Order = O ; FuncSpace = F;
- RangeType = SCALAR;
- MapType = VALUE;
- DerivType = NONE;
- DerivRangeType = SCALAR;
- DerivMapType = VALUE;
- for (int i = 0; i < Geometry::MaxDim; i++) { Orders[i] = -1; }
+ dim = D ; geom_type = G ; dof = Do ; order = O ; func_space = F;
+ range_type = SCALAR;
+ map_type = VALUE;
+ deriv_type = NONE;
+ deriv_range_type = SCALAR;
+ deriv_map_type = VALUE;
+ for (int i = 0; i < Geometry::MaxDim; i++) { orders[i] = -1; }
#ifndef MFEM_THREAD_SAFE
- vshape.SetSize(Dof, Dim);
+ vshape.SetSize(dof, dim);
#endif
}
@@ -75,12 +75,12 @@ void FiniteElement::CalcCurlShape(const IntegrationPoint &ip,
void FiniteElement::CalcPhysCurlShape(ElementTransformation &Trans,
DenseMatrix &curl_shape) const
{
- switch (Dim)
+ switch (dim)
{
case 3:
{
#ifdef MFEM_THREAD_SAFE
- DenseMatrix vshape(Dof, Dim);
+ DenseMatrix vshape(dof, dim);
#endif
CalcCurlShape(Trans.GetIntPoint(), vshape);
MultABt(vshape, Trans.Jacobian(), curl_shape);
@@ -93,7 +93,7 @@ void FiniteElement::CalcPhysCurlShape(ElementTransformation &Trans,
curl_shape *= (1.0 / Trans.Weight());
break;
default:
- MFEM_ABORT("Invalid dimension, Dim = " << Dim);
+ MFEM_ABORT("Invalid dimension, Dim = " << dim);
}
}
@@ -186,7 +186,7 @@ void FiniteElement::CalcPhysShape(ElementTransformation &Trans,
Vector &shape) const
{
CalcShape(Trans.GetIntPoint(), shape);
- if (MapType == INTEGRAL)
+ if (map_type == INTEGRAL)
{
shape /= Trans.Weight();
}
@@ -195,9 +195,9 @@ void FiniteElement::CalcPhysShape(ElementTransformation &Trans,
void FiniteElement::CalcPhysDShape(ElementTransformation &Trans,
DenseMatrix &dshape) const
{
- MFEM_ASSERT(MapType == VALUE, "");
+ MFEM_ASSERT(map_type == VALUE, "");
#ifdef MFEM_THREAD_SAFE
- DenseMatrix vshape(Dof, Dim);
+ DenseMatrix vshape(dof, dim);
#endif
CalcDShape(Trans.GetIntPoint(), vshape);
Mult(vshape, Trans.InverseJacobian(), dshape);
@@ -206,7 +206,7 @@ void FiniteElement::CalcPhysDShape(ElementTransformation &Trans,
void FiniteElement::CalcPhysLaplacian(ElementTransformation &Trans,
Vector &Laplacian) const
{
- MFEM_ASSERT(MapType == VALUE, "");
+ MFEM_ASSERT(map_type == VALUE, "");
// Simpler routine if mapping is affine
if (Trans.Hessian().FNorm2() < 1e-20)
@@ -216,27 +216,27 @@ void FiniteElement::CalcPhysLaplacian(ElementTransformation &Trans,
}
// Compute full Hessian first if non-affine
- int size = (Dim*(Dim+1))/2;
- DenseMatrix hess(Dof, size);
+ int size = (dim*(dim+1))/2;
+ DenseMatrix hess(dof, size);
CalcPhysHessian(Trans,hess);
- if (Dim == 3)
+ if (dim == 3)
{
- for (int nd = 0; nd < Dof; nd++)
+ for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = hess(nd,0) + hess(nd,4) + hess(nd,5);
}
}
- else if (Dim == 2)
+ else if (dim == 2)
{
- for (int nd = 0; nd < Dof; nd++)
+ for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = hess(nd,0) + hess(nd,2);
}
}
else
{
- for (int nd = 0; nd < Dof; nd++)
+ for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = hess(nd,0);
}
@@ -248,16 +248,16 @@ void FiniteElement::CalcPhysLaplacian(ElementTransformation &Trans,
void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
Vector &Laplacian) const
{
- MFEM_ASSERT(MapType == VALUE, "");
- int size = (Dim*(Dim+1))/2;
- DenseMatrix hess(Dof, size);
- DenseMatrix Gij(Dim,Dim);
+ MFEM_ASSERT(map_type == VALUE, "");
+ int size = (dim*(dim+1))/2;
+ DenseMatrix hess(dof, size);
+ DenseMatrix Gij(dim,dim);
Vector scale(size);
CalcHessian (Trans.GetIntPoint(), hess);
MultAAt(Trans.InverseJacobian(), Gij);
- if (Dim == 3)
+ if (dim == 3)
{
scale[0] = Gij(0,0);
scale[1] = 2*Gij(0,1);
@@ -268,7 +268,7 @@ void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
scale[5] = Gij(1,1);
}
- else if (Dim == 2)
+ else if (dim == 2)
{
scale[0] = Gij(0,0);
scale[1] = 2*Gij(0,1);
@@ -279,7 +279,7 @@ void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
scale[0] = Gij(0,0);
}
- for (int nd = 0; nd < Dof; nd++)
+ for (int nd = 0; nd < dof; nd++)
{
Laplacian[nd] = 0.0;
for (int ii = 0; ii < size; ii++)
@@ -293,11 +293,11 @@ void FiniteElement::CalcPhysLinLaplacian(ElementTransformation &Trans,
void FiniteElement::CalcPhysHessian(ElementTransformation &Trans,
DenseMatrix& Hessian) const
{
- MFEM_ASSERT(MapType == VALUE, "");
+ MFEM_ASSERT(map_type == VALUE, "");
// Roll 2-Tensors in vectors and 4-Tensor in Matrix, exploiting symmetry
- Array