Compare commits

..
Author SHA1 Message Date
Stowell, Mark L. ef5f729245 Merge remote-tracking branch 'origin/master' into aniso-diffusion-dev
# Conflicts:
#	fem/bilininteg.hpp
#	fem/coefficient.cpp
#	fem/coefficient.hpp
#	linalg/hypre.cpp
#	linalg/hypre.hpp
#	linalg/solvers.cpp
#	makefile
#	miniapps/common/pfem_extras.hpp
#	miniapps/electromagnetics/tesla_solver.hpp
2025-03-13 17:43:24 -07:00
Stowell, Mark L 6fa5a0b096 Merge remote-tracking branch 'origin/master' into aniso-diffusion-dev
# Conflicts:
#	fem/coefficient.cpp
#	fem/coefficient.hpp
2019-04-01 11:27:41 -07:00
Stowell, Mark L 3bd8349909 Merge remote-tracking branch 'origin/master' into aniso-diffusion-dev 2018-11-08 18:00:03 -08:00
Stowell, Mark L af82ee8560 Merge remote-tracking branch 'origin/master' into aniso-diffusion-dev 2018-10-20 12:18:07 -07:00
Stowell, Mark L 2ac542c720 Attempting to support 2D curl cleaning 2018-10-20 12:17:00 -07:00
Mark L. Stowell e6f828a5fe Attempting to add curl free projection... 2018-10-18 13:07:44 -07:00
Mark L. Stowell 4d756edd80 Adding DivergenceFree/Irrotational projectors for RT spaces 2018-10-18 10:29:50 -07:00
Stowell, Mark L cf5bd1f5cc make style 2018-10-18 00:17:32 -07:00
Stowell, Mark L 7173dd2002 Adding H1 diffusion solver 2018-10-18 00:16:56 -07:00
Stowell, Mark L 50182bf440 Adding perturbed elliptic case 2018-10-17 19:51:12 -07:00
Stowell, Mark L 24bfcc5165 Initializing a solution vector before solve 2018-10-17 10:20:38 -07:00
Stowell, Mark L 302f22f297 Switching to analytic evaluation of b vector field 2018-10-16 15:34:29 -07:00
Stowell, Mark L 1637fcd933 Adding argument to control lower bound of mesh size 2018-10-16 13:13:56 -07:00
Stowell, Mark L 2563506174 make style 2018-10-14 16:14:31 -07:00
Stowell, Mark L 60640c3f7e Adding computation of full thermal flux 2018-10-14 10:39:39 -07:00
Stowell, Mark L f221521203 make style 2018-10-14 10:12:37 -07:00
Stowell, Mark L 1d9e736af6 Adding a miniapp which solve for thermal flux in HDiv 2018-10-14 10:10:55 -07:00
Stowell, Mark L d80dbfd99a Adding flux computation 2018-10-10 16:46:17 -07:00
Stowell, Mark L 3f44043e60 Adding steady state anisotropic diffusion solver 2018-10-10 12:48:07 -07:00
Stowell, Mark L b218959bca Inserting the thermal flux solver 2018-10-03 10:33:45 -07:00
Stowell, Mark L aee7bc9d43 Adding first draft of hybrid diffusion solver 2018-10-01 16:14:53 -07:00
Stowell, Mark L 31cac320d4 Bugfix in activation of nonlinear solver 2018-10-01 15:08:15 -07:00
Stowell, Mark L e7e0fb0a88 Adding a specialized miniapp to duplicate results from the van Es papper 2018-09-30 21:47:54 -07:00
Stowell, Mark L ba71d13980 Merge remote-tracking branch 'origin/master' into aniso-diffusion-dev 2018-09-25 13:04:15 -07:00
Stowell, Mark L 5c326a5535 Fixing a typo in a comment 2018-09-25 13:02:39 -07:00
Stowell, Mark L 9457f7e5b6 Switching to nonlinear solver 2018-09-24 15:45:49 -07:00
Stowell, Mark L ff030ee970 Adding another time dependent test case 2018-09-24 12:45:36 -07:00
Stowell, Mark L 2be9e1f36c Adding a steady state solver to the thermal miniapps 2018-09-24 12:45:03 -07:00
Stowell, Mark L 7671cd9f36 Merge remote-tracking branch 'origin/master' into aniso-diffusion-dev 2018-09-16 13:17:34 -07:00
Stowell, Mark L f89a633fda make style 2018-09-14 14:41:13 -07:00
Stowell, Mark L 4ce1cef6b8 Adding SetOperator methods to HyprePCG, HypreGMRES, HypreDiagScale, and HypreParaSails 2018-09-14 14:34:13 -07:00
Stowell, Mark L c667bf3025 Fixing HypreGMRES::SetOperator method in the presence of a preconditioner 2018-09-14 13:47:29 -07:00
Stowell, Mark L e1678afe40 Using new HypreGMRES with SetOperator method 2018-09-10 16:46:25 -07:00
Stowell, Mark L c0291398ed Implementing HypreGMRES::SetOperator method 2018-09-10 16:45:59 -07:00
Stowell, Mark L 0b4f10d79d Debugging gradient check 2018-09-09 16:29:15 -07:00
Stowell, Mark L 8dfd0e1547 Adding NewtonSolver method to validate gradient 2018-09-09 16:28:18 -07:00
Stowell, Mark L caf239c99a Updating with time dependent source and exact solution 2018-09-09 00:39:58 -07:00
Stowell, Mark L 44c33aece0 Merge remote-tracking branch 'origin/master' into aniso-diffusion-dev 2018-09-08 23:59:16 -07:00
Stowell, Mark L ed49856390 Merge branch 'aniso-diffusion-dev' of github.com:mfem/mfem into aniso-diffusion-dev 2018-09-06 14:13:18 -07:00
Stowell, Mark L 4fef6ca298 make style 2018-09-06 14:12:28 -07:00
Stowell, Mark L d9d809e81c Adding "thermal" to miniapps subdirectories 2018-09-06 14:12:16 -07:00
Stowell, Mark L 51e85ccd84 Fixing nonlinear solve and applying 'make style' 2018-09-06 14:11:54 -07:00
Mark L. Stowell 4304159303 Merge branch 'aniso-diffusion-dev' of github.com:mfem/mfem into aniso-diffusion-dev 2018-09-05 16:56:05 -07:00
Stowell, Mark L 6276268e52 Retain zeros to maintain sparsity pattern 2018-09-05 16:54:59 -07:00
Mark L. Stowell 580ae34842 Retaining zeros to maintain sparsity pattern 2018-09-05 16:51:10 -07:00
Stowell, Mark L 97eaf8efbc Parallelizing the linear solves 2018-09-05 15:48:28 -07:00
Stowell, Mark L e6621c9b0c Parallel bug 2018-09-05 15:25:18 -07:00
Stowell, Mark L 461246f80e Adding a missing overload 2018-09-05 13:29:10 -07:00
Stowell, Mark L a5941ee72f Bugfix: reinitializing matrices before reassembling 2018-09-05 11:10:09 -07:00
Stowell, Mark L c4c2ceab59 Linear case now working 2018-09-05 10:15:17 -07:00
Stowell, Mark L 88b99a1719 Fixing vector dimension in vector grid functions 2018-09-05 10:14:52 -07:00
Stowell, Mark L 7aa7b4ee53 Adjusting initialization order so that vector size is known earlier 2018-09-03 11:19:27 -07:00
Stowell, Mark L bcf87fee29 Modifying VectorGridFunctionCoefs to accept NULL pointers 2018-09-03 11:06:32 -07:00
Stowell, Mark L 2949dc5a46 Adding makefile for miniapps/thermal 2018-09-03 10:48:11 -07:00
Stowell, Mark L b0dbadd007 Adding first draft of non-linear thermal diffusion solver 2018-09-03 10:19:34 -07:00
Stowell, Mark L 6ca1f95979 Adding scalar multiplication by a constant 2018-08-31 22:54:36 -07:00
Stowell, Mark L 39794585c4 Adding an Identity Matrix Coefficient 2018-08-31 16:50:02 -07:00
Stowell, Mark L 65a71259f1 Merge remote-tracking branch 'origin/elementwise-error-dev' into aniso-diffusion-dev 2018-08-31 16:49:37 -07:00
Stowell, Mark L 3f4e8324d4 Adding a coefficient which computes a unit vector field from a vector field 2018-08-29 14:26:29 -07:00
Stowell, Mark L 9fca398741 Adding ability to alter derived coefficients 2018-08-29 13:59:38 -07:00
Stowell, Mark L a2b8f7a129 Merge remote-tracking branch 'origin/master' into aniso-diffusion-dev 2018-08-29 09:28:15 -07:00
Stowell, Mark L a367631ce5 Adding various coefficients which are sums or products of other coefficients 2018-08-28 16:34:13 -07:00
Stowell, Mark L d7718f5c57 make style 2018-08-28 14:51:48 -07:00
Stowell, Mark L fe88c4685d Adding coefficients to compute div, grad, or curl of grid functions. 2018-08-28 14:24:15 -07:00
318 changed files with 15626 additions and 32991 deletions
+2 -10
View File
@@ -114,13 +114,6 @@ jobs:
build-system: make
hypre-target: int32
precision: fp32
- os: macos-latest
target: opt
codecov: NO
mpi: par
build-system: make
hypre-target: int32
precision: fp32
name: ${{ matrix.os }}-${{ matrix.build-system }}-${{ matrix.target }}-${{ matrix.mpi }}-${{ matrix.hypre-target }}-${{ matrix.precision }}
runs-on: ${{ matrix.os }}
@@ -161,8 +154,7 @@ jobs:
- name: get MPI (Linux)
if: matrix.mpi == 'par' && matrix.os == 'ubuntu-latest'
run: |
sudo apt-get install openmpi-bin libopenmpi-dev
export OMPI_MCA_rmaps_base_oversubscribe=1
sudo apt-get install mpich libmpich-dev
- name: get lcov (Linux)
if: matrix.codecov == 'YES' && matrix.os == 'ubuntu-latest'
@@ -199,7 +191,7 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-${{ matrix.hypre-target }}-${{ matrix.precision }}-v2.5
- name: get hypre
if: matrix.mpi == 'par' && steps.hypre-cache.outputs.cache-hit != 'true' && matrix.os != 'windows-latest'
+2 -3
View File
@@ -45,15 +45,14 @@ jobs:
- name: Get MPI (Linux)
run: |
sudo apt-get install openmpi-bin libopenmpi-dev
export OMPI_MCA_rmaps_base_oversubscribe=1
sudo apt-get install mpich libmpich-dev
- name: Cache Hypre Install
id: hypre-cache
uses: actions/cache@v4
with:
path: ${{ env.HYPRE_TOP_DIR }}
key: ${{ runner.os }}-ompi-build-${{ env.HYPRE_TOP_DIR }}-v2.5
key: ${{ runner.os }}-build-${{ env.HYPRE_TOP_DIR }}-v2.5
- name: Get Hypre
if: steps.hypre-cache.outputs.cache-hit != 'true'
-9
View File
@@ -80,7 +80,6 @@ examples/sol_p.*
examples/sol_r.*
examples/sol_i.*
examples/ex6p-checkpoint.*
examples/order.*
examples/ex9.mesh
examples/ex9-mesh.*
examples/ex9-init.*
@@ -236,8 +235,6 @@ miniapps/meshing/minimal-surface
miniapps/meshing/pminimal-surface
miniapps/meshing/polar-nc
miniapps/meshing/mesh-quality
miniapps/meshing/hpref
miniapps/meshing/phpref
miniapps/meshing/mobius-strip.mesh
miniapps/meshing/klein-bottle.mesh
miniapps/meshing/toroid-*.mesh
@@ -255,10 +252,6 @@ miniapps/meshing/sol.gf
miniapps/meshing/optimized*
miniapps/meshing/perturbed*
miniapps/meshing/polar-nc.mesh
miniapps/meshing/mesh.*
miniapps/meshing/order.*
miniapps/meshing/sol.*
miniapps/meshing/refined.mesh
miniapps/mtop/parheat
miniapps/mtop/ParHeat*
@@ -281,10 +274,8 @@ miniapps/navier/navier_shear
miniapps/navier/navier_3dfoc
miniapps/navier/navier_turbchan
miniapps/navier/navier_cht
miniapps/navier/navier_windtunnel
miniapps/navier/tgv_out*.txt
miniapps/navier/*_output
miniapps/navier/inputs/
miniapps/nurbs/nurbs_ex1
miniapps/nurbs/nurbs_ex1p
+108 -160
View File
@@ -8,225 +8,159 @@
https://mfem.org
Version 4.8.1 (development)
Version 4.7.1 (development)
===========================
- Added support for variational resampling of H1 vector fields to ParMoonolith
integration.
- The function Vector::SetSubVector(const Array<int> &, const real_t) now
executes on device if either the vector or the array have the device flag
set. This is most often used for setting constant essential boundary
conditions. A new function Vector::SetSubVectorHost has been added in cases
where host execution is always needed (e.g. when the DOFs array is small).
Version 4.8, released on Apr 9, 2025
====================================
Discretization improvements
---------------------------
- Added high-order basis functions on pyramid-shaped elements for all spaces in
the de Rham complex based on the paper "Orientation embedded high order shape
functions for the exact sequence elements of all shapes" by Fuentes, Keith,
Demkowicz and Nagaraj (doi.org/10.1016/j.camwa.2015.04.027). Positive basis
functions (Bernstein basis) for H1 and L2 on pyramids were also added.
- Added support for parallel p- and hp-refinement on quad/hex meshes. For hp, we
currently support only isotropic refinement with L2 or H1 spaces. See the new
miniapps hpref and phpref in the miniapps/meshing/ directory.
- Added several improvements for hyperbolic problems:
* Assembly of Jacobians in HyperbolicFormIntegrator
* Component-wise upwinded flux (ComponentwiseUpwindFlux)
* Average fluxes in NumericalFlux (formerly RiemannSolver) and FluxFunction
- Added convenience methods to class FiniteElementSpace to directly identify all
degrees of freedom on the exterior faces of the domain, without referencing
boundary element attributes (GetExteriorVDofs and GetExteriorTrueDofs).
- Refactored ALGOIM cut integration rules. The interface is unified with
the interface for moment based cut integration rules.
- Altered (Par)GridFunction::Compute*Error functions to ensure they return
non-negative values and therefore behave as "norms".
- SubMesh and ParSubMesh have been extended to work on nonconforming meshes.
Extracting volume and exterior surface submeshes are both supported.
Discretization improvements
---------------------------
- Added NURBS-based H(div) and H(curl) elements in 2D and 3D. Only on single
patch meshes. Only implemented for serial computations.
- LinearFormIntegrator, BilinearFormIntegrator and NonlinearFormIntegrator now
inherit from a base class Integrator that centralizes the logic for selecting
quadrature rules. This includes a virtual method GetDefaultIntegrationRule,
which should be favored over directly defining a default integration rule in
the element-level assembly routines. The latter is still possible, by leaving
the new virtual method as its default base implementation of returning NULL.
- Added support for boundary constraints to the hybridization class.
- Added NURBS-based H(div) and H(curl) elements in 2D and 3D. Currently only for
single patch meshes in serial.
- Added support for external boundary submeshes with nonconformal mesh adaptation.
- Refactored ALGOIM cut integration rules. The interface is unified with the
interface for moment based cut integration rules.
- Added assembly of Jacobians to `HyperbolicFormIntegrator`.
- FiniteElementSpace has new methods to directly set the prolongation and
restriction operators to user-specified sparse matrices.
- Added average fluxes to `NumericalFlux` (formerly `RiemannSolver`)
and `FluxFunction`.
- Added support for H(div) spaces in class QuadratureInterpolator. Currently only
- Added component-wise upwinded flux (`ComponentwiseUpwindFlux`).
- Added support for H(div) spaces in class QuadratureInterpolator. For now, only
(vector) VALUES, (vector) PHYSICAL_VALUES, and PHYSICAL_MAGNITUDES evaluations
are implemented.
- Added support for boundary constraints to class Hybridization.
- Added support for custom interpolation procedure in FindPointsGSLIB.
- Added GSLIB-based gather-scatter operator.
are implemented. [PR #4669]
Meshing improvements
--------------------
- Added support for nonuniform anisotropic mesh refinement on serial quad/hex
meshes with arbitrary spacing in each direction. This enables in particular
3:1 refinement, as demonstrated in the new meshing miniapp ref321.
- Added native AD support for numerous TMOP metrics that didn't have first or
second derivative implementations.
- Added capabilities for optimization and adaptation of periodic meshes with
TMOP. The internals of TMOP were modified so that the optimization problem is
always solved with respect to mesh displacements.
- The ExodusII reader now handles pyramid and wedge element types. Mixed meshes
are also supported.
- New convenience methods for manipulating boundary attribute markers in class
Mesh: MarkExternalBoundaries, MarkNamedBoundaries, UnmarkInternalBoundaries
and UnmarkNamedBoundaries. See Examples 1/1p and 11p for basic usage.
- Added support for nonuniform anisotropic (nonconforming) mesh refinement with
arbitrary spacing in each direction, for quadrilateral (2D) and hexahedral
(3D) meshes. This enables in particular 3:1 refinement, as demonstrated in the
new meshing miniapp ref321.
- Added a new method, GetExteriorFaceMarker, to the serial and parallel mesh
classes for identifying faces on the exterior of the mesh irrespective of
their presence in the list of "boundary elements".
New and updated examples and miniapps
-------------------------------------
- Added miniapps to demonstrate the H(div) and H(curl) NURBS elements.
- Added native AD support for computing the derivatives of numerous TMOP metrics
that didn't have first or second derivative implementations.
- Added an MFEM example for the eikonal equation. This new solver is based on
the proximal Galerkin method introduced by Keith and Surowiec.
- Added ExodusII output capability which can handle in particular pyramid and
wedge element types. Mixed meshes are also supported.
- Added a new toy miniapp that animates an interesting fidget spiral cone toy.
See miniapps/toys/spiral.cpp.
- Added InverseElementTransformation::InitGuessType::EdgeScan as an alternative
initial guess type. This guess type tries solving with multiple initial guesses
along the r/s/t=0 edges of the element until a valid solution is found or all
initial guess points are exhausted.
- Added new convenience constructors for NURBS patches and knot vectors.
- Added a command line option to all miniapps (`-p` or `--send-port`) for
specifying the GLVis server socket port (19916 by default).
GPU computing
-------------
- Extended FindPointsGSLIB to support general field interpolation on GPUs. Note
that this requires that switch from gslib v1.0.7 to v1.0.9.
- Added support for GPU-accelerated batched linear algebra (using cuBLAS,
hipBLAS, MAGMA, or native MFEM functionality) through the BatchedLinAlg class.
- A new GPU kernel dispatch mechanism was introduced. Users can instantiate
specialized kernels for specific combinations of (for example) polynomial
degree and number of quadrature points using method AddSpecialization in
classes DiffusionIntegrator and MassIntegrator (this functionality may be
added to more integrators in the future).
degree and number of quadrature points using
`DiffusionIntegrator::AddSpecialization` and
`MassIntegrator::AddSpecialization` (this functionality may be added to more
integrators in the future).
- Added BatchInverseElementTransformation to batch InverseElementTransformation
searches. Batch searches are currently limited to meshes with a single element
geometry type of SEGMENT, SQUARE, or CUBE. Additional element geometry types
may be added in the future. Mixed element order meshes are supported. Batch
searches can be performed on the CPU (serial per MPI rank) or GPU. Embedded
elements (SEGMENT in 2D/3D space or SQUARE in 3D space) are supported, however
the existing solvers may struggle to find a valid solution.
- Calls to slower fallback kernels can be reported to `mfem::err` by setting
the environment variable `MFEM_REPORT_KERNELS` to any value other than `NO`
or by explicitly calling `KernelReporter::Enable`. Users can then add
specializations for these kernels to achieve higher performance.
- Calls to slower fallback kernels can be reported to mfem::err by setting the
environment variable MFEM_REPORT_KERNELS to any value other than NO or by
explicitly calling KernelReporter::Enable. For higher performance, users can
then add specializations for these kernels.
- Element assembly kernels have been added for low-order refined to
high-order transfer operators. New kernels can be offloaded as device
kernels. Example usage may be found in lor-transfer.cpp under miniapps/tools.
- Element assembly kernels have been added for low-order refined -> high-order
transfer operators. New kernels can be offloaded as device kernels. Example
usage may be found in lor-transfer.cpp in the miniapps/tools/ directory.
- Added GPU acceleration and element assembly for DivDivIntegrator and
NormalTraceJumpIntegrator.
- Added support for GPU accelerated FindPointsGSLIB. Note that this will require
the users to switch from gslib v1.0.7 to v1.0.9.
- Allow BlockLowerTriangularPreconditioner to run on GPU.
- Use device vectors in GMRES, FGMRES and other iterative methods.
Linear and nonlinear solvers
----------------------------
- Added GPU acceleration of the algebraic hybridization solver for grad-div
problems in H(div). See Example 4.
- Added a self-contained implementation of the Method of Moving Asymptotes (MMA)
for solving optimization problems.
Miscellaneous
-------------
- Added support for SUNDIALS v7. See the section "API changes" for some small
changes related to this new version.
- Changed the name of class IterativeSolverMonitor to IterativeSolverController,
which now allows for specifying convergence by a user defined criterion. For
backward compatibility, the old name is still available.
- Refactored the `ARKStepSolver` class (ARKODE interface) to use
`TimeDependentOperator::Mult` only when the associated ODE operator is
expressed in explicit form (i.e., `TimeDependentOperator::isExplicit()`),
otherwise `TimeDependentOperator::ExplicitMult` is used. A check has been
added to `ARKStepSolver` to verify that the associated ODE operator is not in
explicit form when a mass matrix solver is enabled via a call to either the
`UseMFEMMassLinearSolver` or `UseSundialsMassLinearSolver` methods. This is
because enabling a mass matrix solver assumes that F(u,k,t) = M k in the
associated ODE operator.
- Refactored the ARKStepSolver class (ARKODE interface) to use the Mult() method
of TimeDependentOperator only when the associated ODE operator is expressed in
explicit form (i.e., TimeDependentOperator::isExplicit()), otherwise the
method ExplicitMult() is used. A check has been added to ARKStepSolver to
verify that the associated ODE operator is not in explicit form when a mass
matrix solver is enabled via a call to either the UseMFEMMassLinearSolver or
UseSundialsMassLinearSolver methods. This is because enabling a mass matrix
solver assumes that F(u,k,t) = M k in the associated ODE operator.
- Added support for custom interpolation procedure in FindPointsGSLIB.
- Added ODE solvers selection routines. This creates a uniformity across
examples, miniapps and other executables in regard to ODE (time-integrator)
selection.
- `FiniteElementSpace` has new methods to directly set prolongation and
restriction operators to arbitrary sparse matrices.
- Added new mechanism for retrieving and setting state vectors in ODE solvers.
This is relevant for AB/AM and gen-alpha solvers.
- There are new convenience constructors for NURBS patches and knot vectors.
- Added ODEsolver/ODEsolver2 unit tests to verify order of convergence and
read/write functionality.
- Added convenience methods for manipulating boundary attribute marker arrays;
`(Par)Mesh::MarkExternalBoundaries`, `(Par)Mesh::UnmarkInternalBoundaries`,
`(Par)Mesh::MarkNamedBoundaries`, and `(Par)Mesh::UnmarkNamedBoundaries`.
See examples `ex1.cpp`, `ex1p.cpp`, and `ex11p.cpp` for basic usage.
New and updated examples and miniapps
-------------------------------------
- Added an MFEM example for the eikonal equation (examples/ex40) based on the
proximal Galerkin method introduced by Keith and Surowiec.
- Added `(Par)Mesh::GetExteriorFaceMarker` for identifying faces on the
exterior of the mesh irrespective of their presence in the list of "boundary
elements".
- Added miniapps to demonstrate the H(div) and H(curl) NURBS elements.
- Added methods to `(Par)FiniteElementSpace` to identify all degrees of freedom
located on the exterior of the domain without reference to the list of
"boundary elements"; `GetExteriorVDofs` and `GetExteriorTrueDofs`.
- Fixed element visualization in the Mesh explorer miniapp.
- `LinearFormIntegrator` and `NonlinearFormIntegrator` (including
`BilinearFormIntegrator`) now all inherit from a base class `Integrator`
that combines some logic related to selecting quadrature rules. This includes
a virtual method `Integrator::GetDefaultIntegrationRule`, which should be
favored over directly defining a default integration rule in the element-level
assembly routines (although the latter is still possible, by leaving the new
virtual method as its default base implementation of returning `NULL`).
- Added a command line option to all miniapps (-p or --send-port) for
specifying the GLVis server socket port (19916 by default).
- Added a new toy miniapp that animates an interesting fidget spiral cone toy.
See miniapps/toys/spiral.cpp.
Miscellaneous
-------------
- Updated the benchmarks (in tests/benchmarks) to work with the latest Google
Benchmarks classes (version 1.9.1). Renamed the MFEM_ENABLE_GOOGLE_BENCHMARKS
CMake option to just MFEM_ENABLE_BENCHMARKS.
- Updated the minimum CMake version requirements:
* CMake >= 3.12 for CPU builds,
* CMake >= 3.17 for CUDA builds, and
* CMake >= 3.14 for HIP builds (CMake >= 3.12 may work as well, not tested).
- Various other simplifications, extensions, and bugfixes in the code.
- Changed the name of `IterativeSolverMonitor` to `IterativeSolverController`
which now allows for declaring convergence by a user defined criterion. For
backward compatibility, the old name is still available.
API changes
-----------
- In class GridFunction, fec was renamed to fec_owned.
- API change: 'TMOP_Metric_skew2D' has been marked as deprecated.
- RiemannSolver was renamed to NumericalFlux (the old name has been deprecated
through typedef).
- API change: in class GridFunction, 'fec' was renamed to 'fec_owned'.
- API changes due to SUNDIALS v7:
* the SUNDIALS types realtype and booleantype are no longer defined by v7
and therefore MFEM now uses the new type names sunrealtype and
sunbooleantype, respectively, which MFEM defines when using SUNDIALS < v6
- API change: `RiemannSolver` was renamed to `NumericalFlux` (the old name has
been been deprecated through typedef)
- API change: support for SUNDIALS v7:
* the SUNDIALS types `realtype` and `booleantype` are no longer defined by v7
and therefore MFEM now uses the new type names `sunrealtype` and
`sunbooleantype`, respectively, which MFEM defines when using SUNDIALS < v6
where these types were not defined.
* The SUNDIALS macro SUNLS_SUCCESS and some other *_SUCCESS macros were
removed and replaced by SUN_SUCCESS in v7, so to avoid tedious checks for
SUNDIALS versions, MFEM now defines and uses the constant SUN_SUCCESS when
* The SUNDIALS macro `SUNLS_SUCCESS` and some other `*_SUCCESS` macros were
removed and replaced by `SUN_SUCCESS` in v7, so to avoid tedious checks for
SUNDIALS versions, MFEM now defines and uses the constant `SUN_SUCCESS` when
using SUNDIALS < v7.
* The constants SUN_PREC_*, introduced by SUNDIALS v6 are now introduced by
* The constants `SUN_PREC_*`, introduced by SUNDIALS v6 are now introduced by
MFEM when using SUNDIALS < v6 to avoid tedious version checks.
- TMOP_Metric_skew2D has been marked as deprecated.
Version 4.7, released on May 7, 2024
====================================
@@ -251,6 +185,9 @@ Meshing improvements
- Added support for internal boundary elements in nonconforming meshes.
- Added ExodusII output capability. The writer can handle first-order (Pyramid5,
Wedge6, Hex8, Tet4) and second-order FE types (Pyramid14, Wedge18, Hex27, Tet10).
- The ReadCubit Genesis mesh importer has been rewritten to improve readability.
Discretization improvements
@@ -310,6 +247,15 @@ New and updated examples and miniapps
- Added two new example codes: 38 and 39/39p described above. Substantially
updated Example 18/18p.
- Added ODE solvers selection routines. This creates a uniformity across examples,
miniapps and other executables in regard to ODE(time-integrator) selection.
- Added new mechanism for retrieving and setting state vectors in ODE solvers.
This is relevant for AB/AM and gen-alpha solvers.
- Added ODEsolver/ODEsolver2 unit tests to verify order of convergence and
read/write functionality.
Miscellaneous
-------------
- Updated the Doxygen documentation style, which now requires Doxygen version
@@ -323,6 +269,8 @@ Miscellaneous
- Various other simplifications, extensions, and bugfixes in the code.
- Added GSLIB-based gather-scatter operator.
Version 4.6, released on September 27, 2023
===========================================
+24 -30
View File
@@ -12,8 +12,7 @@
# The variable CMAKE_CXX_STANDARD and related were introduced in CMake v3.1
# Version 3.8 fixes the handling of CMAKE_CXX_STANDARD for try_compile.
# Version 3.8 or newer is required for direct CUDA support.
# Version 3.12 or newer is required for setting maximum policy version.
cmake_minimum_required(VERSION 3.12.0...4.0.0)
cmake_minimum_required(VERSION 3.8)
message(STATUS "CMake version: ${CMAKE_VERSION}")
set(USER_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/config/user.cmake" CACHE PATH
"Path to optional user configuration file.")
@@ -59,7 +58,7 @@ project(mfem NONE)
# Current version of MFEM, see also `makefile`.
# mfem_VERSION = (string)
# MFEM_VERSION = (int) [automatically derived from mfem_VERSION]
set(${PROJECT_NAME}_VERSION 4.8.1)
set(${PROJECT_NAME}_VERSION 4.7.1)
# Prohibit in-source build
if (${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR})
@@ -105,13 +104,6 @@ endif()
# Include xSDK default CMake file.
include("${CMAKE_CURRENT_SOURCE_DIR}/config/XSDKDefaults.cmake")
# Path to MFEM's CMake modules and utilities.
set(MFEM_CMAKE_PATH ${PROJECT_SOURCE_DIR}/config)
set(CMAKE_MODULE_PATH ${MFEM_CMAKE_PATH}/cmake/modules)
# Load MFEM CMake utilities.
include(MfemCmakeUtilities)
# Enable languages.
enable_language(CXX)
if (MINGW)
@@ -124,15 +116,16 @@ if (MFEM_USE_CUDA)
if (MFEM_USE_HIP)
message(FATAL_ERROR " *** MFEM_USE_HIP cannot be combined with MFEM_USE_CUDA.")
endif()
# CUDAToolkit was added in CMake 3.17, so we require at least CMake 3.17 when
# CUDA is enabled:
if (CMAKE_VERSION VERSION_LESS 3.17.0)
message(FATAL_ERROR "CUDA support requires CMake >= 3.17")
endif()
# Use ${CMAKE_CXX_COMPILER} as the cuda host compiler.
if (NOT CMAKE_CUDA_HOST_COMPILER)
set(CMAKE_CUDA_HOST_COMPILER ${CMAKE_CXX_COMPILER})
endif()
enable_language(CUDA)
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
"CUDA standard to use.")
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL
"Force the use of the chosen CUDA standard.")
set(CMAKE_CUDA_EXTENSIONS OFF CACHE BOOL "Enable CUDA standard extensions.")
set(CUDA_FLAGS "--expt-extended-lambda")
if (CMAKE_VERSION VERSION_LESS 3.18.0)
set(CUDA_FLAGS "-arch=${CUDA_ARCH} ${CUDA_FLAGS}")
@@ -147,19 +140,18 @@ if (MFEM_USE_CUDA)
set(CUDA_ARCH "CMAKE_CUDA_ARCHITECTURES: ${CMAKE_CUDA_ARCHITECTURES}")
endif()
message(STATUS "Using CUDA architecture: ${CUDA_ARCH}")
enable_language(CUDA)
set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD} CACHE STRING
"CUDA standard to use.")
set(CMAKE_CUDA_STANDARD_REQUIRED ON CACHE BOOL
"Force the use of the chosen CUDA standard.")
set(CMAKE_CUDA_EXTENSIONS OFF CACHE BOOL "Enable CUDA standard extensions.")
if (CMAKE_VERSION VERSION_LESS 3.12.0)
# CMake versions 3.8 and 3.9 require this to work; 3.10 and 3.11 are not
# tested and may not actually need this (but should be ok to keep).
set(CUDA_FLAGS "-ccbin=${CMAKE_CXX_COMPILER} ${CUDA_FLAGS}")
set(CMAKE_CUDA_HOST_LINK_LAUNCHER ${CMAKE_CXX_COMPILER})
endif()
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} ${CUDA_FLAGS}")
find_package(CUDAToolkit REQUIRED)
set(CUSPARSE_FOUND TRUE)
set(CUBLAS_FOUND TRUE)
# Initialize CUSPARSE_LIBRARIES and CUBLAS_LIBRARIES:
mfem_culib_set_libraries(CUSPARSE cusparse)
mfem_culib_set_libraries(CUBLAS cublas)
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
endif()
if (XSDK_ENABLE_C)
@@ -172,6 +164,13 @@ endif()
# Suppress warnings about MACOSX_RPATH
set(CMAKE_MACOSX_RPATH OFF CACHE BOOL "")
# CMake needs to know where to find things
set(MFEM_CMAKE_PATH ${PROJECT_SOURCE_DIR}/config)
set(CMAKE_MODULE_PATH ${MFEM_CMAKE_PATH}/cmake/modules)
# Load MFEM CMake utilities.
include(MfemCmakeUtilities)
string(TOUPPER "${PROJECT_NAME}" PROJECT_NAME_UC)
mfem_version_to_int(${${PROJECT_NAME}_VERSION} ${PROJECT_NAME_UC}_VERSION)
set(${PROJECT_NAME_UC}_VERSION_STRING ${${PROJECT_NAME}_VERSION})
@@ -388,11 +387,6 @@ if (MFEM_USE_GSLIB)
find_package(GSLIB REQUIRED)
endif()
# HDF5
if (MFEM_USE_HDF5)
find_package(HDF5 REQUIRED)
endif()
# NetCDF
if (MFEM_USE_NETCDF)
find_package(NetCDF REQUIRED)
@@ -574,7 +568,7 @@ find_package(Threads REQUIRED)
# integers, the METIS header (with 32-bit indices, as used by mfem) needs to
# be before SuiteSparse.
set(MFEM_TPLS OPENMP HYPRE LAPACK BLAS SuperLUDist STRUMPACK METIS SuiteSparse
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB HDF5
SUNDIALS PETSC SLEPC MUMPS AXOM FMS CONDUIT Ginkgo GNUTLS GSLIB
NETCDF MPFR PUMI HIOP POSIXCLOCKS MFEMBacktrace ZLIB OCCA CEED RAJA UMPIRE
ADIOS2 MKL_CPARDISO MKL_PARDISO AMGX MAGMA CUSPARSE CUBLAS CALIPER CODIPACK
BENCHMARK PARELAG TRIBOL MPI_CXX HIP HIPBLAS HIPSPARSE MOONOLITH BLITZ
-3
View File
@@ -120,9 +120,7 @@ The MFEM source code has the following structure:
| └── superlu
├── fem
│ ├── ceed
│ ├── eltrans
│ ├── fe
│ ├── gslib
│ ├── integ
│ ├── lor
│ ├── moonolith
@@ -130,7 +128,6 @@ The MFEM source code has the following structure:
│ └── tmop
├── general
├── linalg
│ ├── batched
│ └── simd
├── mesh
│ └── submesh
+4 -30
View File
@@ -122,14 +122,9 @@ Parallel build:
(For METIS 5, see https://mfem.org/building/#parallel-build-using-metis-5)
CUDA build:
(this build requires CMake 3.17 or newer)
(this build requires CMake 3.8 or newer)
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_CUDA=YES -DCUDA_ARCH=sm_70
make -j 4
HIP build:
mkdir <mfem-build-dir> ; cd <mfem-build-dir>
cmake <mfem-source-dir> -DMFEM_USE_HIP=YES -DHIP_ARCH=gfx942 -DCMAKE_CXX_COMPILER=amdclang++ -DCMAKE_HIP_COMPILER=amdclang++
cmake <mfem-source-dir> -DMFEM_USE_CUDA=YES
make -j 4
Example codes (serial/parallel, depending on the build):
@@ -423,10 +418,6 @@ MFEM_USE_GNUTLS = YES/NO
When MFEM_USE_GNUTLS is enabled, the additional build options, GNUTLS_*, are
also used, see below.
MFEM_USE_HDF5 = YES/NO
The HDF5 library is used for input and output of HDF5 files, for example
Cubit mesh files or VTKHDF files for ParaView.
MFEM_USE_NETCDF = YES/NO
NetCDF is the library that is used by the SNL Cubit mesh generator to create
Genesis mesh files. This option enables a reader for these files, which
@@ -737,9 +728,6 @@ The specific libraries and their options are:
Options: GNUTLS_OPT, GNUTLS_LIB.
Versions: GnuTLS >= 2.12.0, older versions may work too.
- HDF5 (optional), used when MFEM_USE_HDF5 = YES, required for reading and
writing files in VTKHDF format.
- NetCDF (optional), used when MFEM_USE_NETCDF = YES, required for reading Cubit
mesh files. Also requires installation of HDF5 and ZLIB, as explained at the
NetCDF web site. Note that we use the plain vanilla "C" version of NetCDF, you
@@ -1014,7 +1002,7 @@ Configuration variables (CMake)
===============================
See the configuration file config/defaults.cmake for the default settings.
Note: the option MFEM_USE_CUDA requires CMake version 3.17 or newer!
Note: the option MFEM_USE_CUDA requires CMake version 3.8 or newer!
Non-standard CMake variables for compilers:
CXX - If set, overwrite the auto-detected C++ compiler, serial build
@@ -1045,7 +1033,6 @@ MFEM_USE_STRUMPACK
MFEM_USE_GINKGO
MFEM_USE_AMGX
MFEM_USE_GNUTLS
MFEM_USE_HDF5
MFEM_USE_NETCDF
MFEM_USE_MPFR
MFEM_USE_ZLIB
@@ -1174,19 +1161,6 @@ larger problems, there are two options:
1. Building hypre with '--enable-bigint' defines the local and global indices to
be 64-bit. This is convenient, but requires more memory than necessary.
2. Building hypre with '--enable-mixedint' defines the local indices to be
2. Building hypre with '--enable-mixedint' defines the local indiced to be
32-bit, while using a 64-bit storage for global indices. This option is
currently tested only in ex1p, and may not work in more general settings.
Specific options for HIP
========================
MFEM expects the `ROCM_PATH` environment variable to be set to the path of the
ROCM install, as well as having `$ROCM_PATH/bin` in `PATH`.
Specific options for RAJA+HIP+MPI
=================================
RAJA uses CMake's built-in HIP support (added in CMake 3.21), while MFEM uses
the older HIP C++ library build/linkage. To ensure proper build and linkage
check that `CMAKE_CXX_COMPILER` and `CMAKE_HIP_COMPILER` are set to the same
compiler. This is especially important when using an MPI compiler (for example
crayCC) where some linker flags may get dropped if these two are not identical.
-1
View File
@@ -41,7 +41,6 @@ set(MFEM_USE_MAGMA @MFEM_USE_MAGMA@)
set(MFEM_USE_HIOP @MFEM_USE_HIOP@)
set(MFEM_USE_GNUTLS @MFEM_USE_GNUTLS@)
set(MFEM_USE_GSLIB @MFEM_USE_GSLIB@)
set(MFEM_USE_HDF5 @MFEM_USE_HDF5@)
set(MFEM_USE_NETCDF @MFEM_USE_NETCDF@)
set(MFEM_USE_PETSC @MFEM_USE_PETSC@)
set(MFEM_USE_SLEPC @MFEM_USE_SLEPC@)
-3
View File
@@ -132,9 +132,6 @@
// Enable Conduit support.
#cmakedefine MFEM_USE_CONDUIT
// Enable functionality based on the HDF5 library (reading VTKHDF files).
#cmakedefine MFEM_USE_HDF5
// Enable functionality based on the NetCDF library (reading CUBIT files).
#cmakedefine MFEM_USE_NETCDF
+4 -14
View File
@@ -25,12 +25,7 @@ if (HYPRE_FOUND)
find_package(rocsparse REQUIRED)
find_package(rocrand REQUIRED)
endif()
if (HYPRE_LIBRARIES AND HYPRE_INCLUDE_DIRS AND HYPRE_VERSION)
find_package_handle_standard_args(HYPRE
REQUIRED_VARS HYPRE_LIBRARIES HYPRE_INCLUDE_DIRS HYPRE_VERSION
)
return()
endif()
return()
endif()
include(MfemCmakeUtilities)
@@ -82,10 +77,9 @@ endif()
if (HYPRE_FOUND AND HYPRE_USING_CUDA)
find_package(CUDAToolkit REQUIRED)
# Initialize CUSPARSE_LIBRARIES, CURAND_LIBRARIES, and CUBLAS_LIBRARIES:
mfem_culib_set_libraries(CUSPARSE cusparse)
mfem_culib_set_libraries(CURAND curand)
mfem_culib_set_libraries(CUBLAS cublas)
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
get_target_property(CURAND_LIBRARIES CUDA::curand LOCATION)
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
list(APPEND HYPRE_LIBRARIES ${CUSPARSE_LIBRARIES} ${CURAND_LIBRARIES}
${CUBLAS_LIBRARIES})
set(HYPRE_LIBRARIES ${HYPRE_LIBRARIES} CACHE STRING
@@ -101,7 +95,3 @@ if (HYPRE_FOUND AND HYPRE_USING_HIP)
"HYPRE libraries + dependencies." FORCE)
message(STATUS "Updated HYPRE_LIBRARIES: ${HYPRE_LIBRARIES}")
endif()
find_package_handle_standard_args(HYPRE
REQUIRED_VARS HYPRE_LIBRARIES HYPRE_INCLUDE_DIRS HYPRE_VERSION
)
+2 -4
View File
@@ -19,10 +19,8 @@ mfem_find_package(MAGMA MAGMA MAGMA_DIR "include" "magma.h" "lib" "magma"
"Paths to headers required by MAGMA." "Libraries required by MAGMA.")
if (MAGMA_FOUND AND MFEM_USE_CUDA)
find_package(CUDAToolkit REQUIRED)
# Initialize CUSPARSE_LIBRARIES and CUBLAS_LIBRARIES:
mfem_culib_set_libraries(CUSPARSE cusparse)
mfem_culib_set_libraries(CUBLAS cublas)
get_target_property(CUSPARSE_LIBRARIES CUDA::cusparse LOCATION)
get_target_property(CUBLAS_LIBRARIES CUDA::cublas LOCATION)
list(APPEND MAGMA_LIBRARIES ${CUSPARSE_LIBRARIES} ${CUBLAS_LIBRARIES})
set(MAGMA_LIBRARIES ${MAGMA_LIBRARIES} CACHE STRING
"MAGMA libraries + dependencies." FORCE)
+11 -20
View File
@@ -123,9 +123,16 @@ macro(add_mfem_miniapp MFEM_EXE_NAME)
# If CUDA is enabled, tag source files to be compiled with nvcc.
if (MFEM_USE_CUDA)
set_source_files_properties(${MAIN_LIST} ${EXTRA_SOURCES_LIST}
PROPERTIES LANGUAGE CUDA)
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
set_source_files_properties(${MAIN_LIST} ${EXTRA_SOURCES_LIST} PROPERTIES LANGUAGE CUDA)
if (CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
list(TRANSFORM EXTRA_OPTIONS_LIST PREPEND "-Xcompiler=")
else()
set(LIST_)
foreach(item IN LISTS EXTRA_OPTIONS_LIST)
list(APPEND LIST_ "-Xcompiler=${item}")
endforeach()
set(EXTRA_OPTIONS_LIST ${LIST_})
endif()
endif()
# Actually add the executable
@@ -150,21 +157,6 @@ macro(add_mfem_miniapp MFEM_EXE_NAME)
endif()
endmacro()
# Macro for setting variables like '<culib>_LIBRARIES' where <culib> is a CUDA
# library like cublas. This macro assumes that the CUDAToolkit module was loaded
# successfully. Example usage:
# mfem_culib_set_libraries(CUBLAS cublas)
macro(mfem_culib_set_libraries _CULIB _culib)
# The following command does not work with older CMake versions, e.g. 3.20:
# get_target_property(${_CULIB}_LIBRARIES CUDA::${_culib} LOCATION)
# Therefore, we use the respective internal variable:
set(${_CULIB}_LIBRARIES ${CUDA_${_culib}_LIBRARY})
if (NOT ${_CULIB}_LIBRARIES)
message(FATAL_ERROR
"Error setting ${_CULIB}_LIBRARIES: ${${_CULIB}_LIBRARIES}")
endif()
endmacro()
# Auxiliary function, used in mfem_find_package().
function(mfem_find_component Prefix DirVar IncSuffixes Header LibSuffixes Lib
@@ -877,8 +869,7 @@ function(mfem_export_mk_files)
MFEM_USE_OCCA MFEM_USE_CEED MFEM_USE_CALIPER MFEM_USE_UMPIRE MFEM_USE_SIMD
MFEM_USE_ADIOS2 MFEM_USE_MKL_CPARDISO MFEM_USE_MKL_PARDISO
MFEM_USE_ADFORWARD MFEM_USE_CODIPACK MFEM_USE_BENCHMARK MFEM_USE_PARELAG
MFEM_USE_TRIBOL MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME
MFEM_USE_HDF5)
MFEM_USE_TRIBOL MFEM_USE_MOONOLITH MFEM_USE_ALGOIM MFEM_USE_ENZYME)
foreach(var ${CONFIG_MK_BOOL_VARS})
if (${var})
set(${var} YES)
@@ -93,17 +93,17 @@ macro (RESOLVE_LIBRARIES LIBS LINK_LINE)
set (_directory_list ${_directory_list} ${libpath})
set (token ${libname})
endif (token MATCHES "^/")
set (_lib "NOTFOUND")
set (_lib "NOTFOUND" CACHE FILEPATH "Cleared" FORCE)
find_library (_lib ${token} HINTS ${_directory_list} ${_root})
if (_lib)
string (REPLACE "//" "/" _lib ${_lib})
string (REPLACE "//" "/" _lib ${_lib})
list (APPEND _libs_found ${_lib})
else (_lib)
message (STATUS "Unable to find library ${token}")
endif (_lib)
unset(_lib CACHE)
endif (token MATCHES "-L([^\" ]+|\"[^\"]+\")")
endforeach (token)
set (_lib "NOTFOUND" CACHE INTERNAL "Scratch variable" FORCE)
# only the LAST occurrence of each library is required since there should be no circular dependencies
if (_libs_found)
list (REVERSE _libs_found)
-3
View File
@@ -132,9 +132,6 @@
// Enable Conduit support.
// #define MFEM_USE_CONDUIT
// Enable functionality based on the HDF5 library
// #define MFEM_USE_HDF5
// Enable functionality based on the NetCDF library (reading CUBIT files).
// #define MFEM_USE_NETCDF
-1
View File
@@ -40,7 +40,6 @@ MFEM_USE_GINKGO = @MFEM_USE_GINKGO@
MFEM_USE_AMGX = @MFEM_USE_AMGX@
MFEM_USE_MAGMA = @MFEM_USE_MAGMA@
MFEM_USE_GNUTLS = @MFEM_USE_GNUTLS@
MFEM_USE_HDF5 = @MFEM_USE_HDF5@
MFEM_USE_NETCDF = @MFEM_USE_NETCDF@
MFEM_USE_PETSC = @MFEM_USE_PETSC@
MFEM_USE_SLEPC = @MFEM_USE_SLEPC@
-1
View File
@@ -43,7 +43,6 @@ option(MFEM_USE_AMGX "Enable AmgX usage" OFF)
option(MFEM_USE_MAGMA "Enable MAGMA usage" OFF)
option(MFEM_USE_GNUTLS "Enable GNUTLS usage" OFF)
option(MFEM_USE_GSLIB "Enable GSLIB usage" OFF)
option(MFEM_USE_HDF5 "Enable HDF5 usage" OFF)
option(MFEM_USE_NETCDF "Enable NETCDF usage" OFF)
option(MFEM_USE_PETSC "Enable PETSc support." OFF)
option(MFEM_USE_SLEPC "Enable SLEPc support." OFF)
+4 -14
View File
@@ -145,7 +145,6 @@ MFEM_USE_GINKGO = NO
MFEM_USE_AMGX = NO
MFEM_USE_MAGMA = NO
MFEM_USE_GNUTLS = NO
MFEM_USE_HDF5 = NO
MFEM_USE_NETCDF = NO
MFEM_USE_PETSC = NO
MFEM_USE_SLEPC = NO
@@ -402,14 +401,9 @@ MAGMA_LIB = -L$(MAGMA_DIR)/lib -l:libmagma.a -lcublas -lcusparse $(LAPACK_LIB)
GNUTLS_OPT =
GNUTLS_LIB = -lgnutls
# HDF5 library configuration
HDF5_DIR = $(HOME)/local
HDF5_OPT = -I$(HDF5_DIR)/include
HDF5_LIB = $(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib -lhdf5_hl -lhdf5 \
$(ZLIB_LIB)
# NetCDF library configuration
NETCDF_DIR = $(HOME)/local
HDF5_DIR = $(HOME)/local
NETCDF_OPT = -I$(NETCDF_DIR)/include -I$(HDF5_DIR)/include $(ZLIB_OPT)
NETCDF_LIB = $(XLINKER)-rpath,$(NETCDF_DIR)/lib -L$(NETCDF_DIR)/lib\
$(XLINKER)-rpath,$(HDF5_DIR)/lib -L$(HDF5_DIR)/lib\
@@ -490,8 +484,8 @@ SIDRE_LIB = \
# Note that PUMI_DIR is needed -- it is used to check for gmi_sim.h
PUMI_DIR = @MFEM_DIR@/../pumi-2.1.0
PUMI_OPT = -I$(PUMI_DIR)/include
PUMI_LIB = -L$(PUMI_DIR)/lib64 -L$(PUMI_DIR)/lib -lpumi -lcrv -lma -lmds -lapf\
-lpcu -lgmi -lparma -llion -lmth -lapf_zoltan -lspr
PUMI_LIB = -L$(PUMI_DIR)/lib -lpumi -lcrv -lma -lmds -lapf -lpcu -lgmi -lparma\
-llion -lmth -lapf_zoltan -lspr
# HIOP
HIOP_DIR = @MFEM_DIR@/../hiop/install
@@ -574,11 +568,7 @@ RAJA_LIB = $(XLINKER)-rpath,$(RAJA_DIR)/lib -L$(RAJA_DIR)/lib -lRAJA $(CAMP_LIB)
# UMPIRE library configuration
UMPIRE_DIR = @MFEM_DIR@/../umpire
UMPIRE_OPT = -I$(UMPIRE_DIR)/include $(if $(CAMP_DIR), -I$(CAMP_DIR)/include)
UMPIRE_LIB = -L$(UMPIRE_DIR)/lib -L$(UMPIRE_DIR)/lib64 -lumpire $(CAMP_LIB)
ifdef FMT_DIR
UMPIRE_OPT += -I$(FMT_DIR)/include
UMPIRE_LIB += -L$(FMT_DIR)/lib -L$(FMT_DIR)/lib64 -lfmt
endif
UMPIRE_LIB = -L$(UMPIRE_DIR)/lib -lumpire $(CAMP_LIB)
# MKL CPardiso library configuration
MKL_CPARDISO_DIR ?=
+3 -3
View File
@@ -17,7 +17,7 @@ We provide two containers, which you can either build or use directly from
In the above, "ghcr.io" means "GitHub Container Registry" and
is the [GitHub packages](https://github.com/features/packages) registry that supports
Docker images and other OCI artifacts.
Docker images and other OCI artifacts.
### Ubuntu
@@ -132,7 +132,7 @@ examples.
> Use this build for a development environment with spack and mfem
This container is also [provided on GitHub packages](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu-base),
This container is also [provided on GitHub packages](https://github.com/mfem/mfem/pkgs/container/mfem-ubuntu-base),
however you can build it locally too:
```bash
@@ -197,7 +197,7 @@ Average reduction factor = 0.140201
This container is likely ideal for someone that wants to develop mfem itself.
For other use cases, we recommend using the slimmer image. As an example,
if you want to develop with your own code base (and mfem as is in the container)
if you want to develop with your own code base (and mfem as is in the container)
you can bind to somewhere else in the container (e.g., src)
```bash
@@ -1,20 +0,0 @@
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 186a320..39e5356 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.12.0...4.0.0)
project(METIS)
set(GKLIB_PATH "${CMAKE_SOURCE_DIR}/GKlib" CACHE PATH "path to GKlib")
diff --git a/GKlib/CMakeLists.txt b/GKlib/CMakeLists.txt
index 67b600a..44321a6 100644
--- a/GKlib/CMakeLists.txt
+++ b/GKlib/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 2.8)
+cmake_minimum_required(VERSION 3.12.0...4.0.0)
project(GKlib)
get_filename_component(abs "." ABSOLUTE)
@@ -22,7 +22,6 @@ vcpkg_extract_source_archive_ex(
fix-linux-build-error.patch
install-metisConfig.patch
fix-INT_MIN_define.patch
cmake4.patch
)
vcpkg_configure_cmake(
-38
View File
@@ -1,38 +0,0 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see fem/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
# PYRAMID = 7
#
dimension
3
elements
1
1 7 0 1 2 3 4
boundary
5
1 3 3 2 1 0
2 2 0 1 4
3 2 1 2 4
4 2 2 3 4
5 2 3 0 4
vertices
5
3
-0.5 -0.5 0
0.5 -0.5 0
0.5 0.5 0
-0.5 0.5 0
0 0 0.7071067811865475
-108
View File
@@ -1,108 +0,0 @@
MFEM mesh v1.0
#
# MFEM Geometry Types (see fem/geom.hpp):
#
# POINT = 0
# SEGMENT = 1
# TRIANGLE = 2
# SQUARE = 3
# TETRAHEDRON = 4
# CUBE = 5
# PRISM = 6
# PYRAMID = 7
#
dimension
3
elements
16
1 5 12 13 16 15 21 22 25 24
1 6 9 12 8 18 21 17
1 6 11 8 12 20 17 21
1 6 3 2 12 6 5 15
1 6 11 12 2 14 15 5
1 6 3 12 0 4 13 1
1 6 9 0 12 10 1 13
1 7 12 13 22 21 19
1 7 15 16 13 12 7
1 7 12 21 24 15 23
1 7 9 12 21 18 19
1 7 11 20 21 12 23
1 7 9 10 13 12 19
1 7 11 12 15 14 23
1 7 3 6 15 12 7
1 7 3 12 13 4 7
boundary
39
1 3 5 6 3 2
2 2 6 7 3
2 2 7 4 3
3 3 3 4 1 0
4 2 11 12 8
4 2 9 8 12
5 2 3 12 2
5 2 11 2 12
6 3 0 1 10 9
7 2 10 19 9
7 2 18 9 19
8 3 8 9 18 17
9 2 4 13 1
9 2 10 1 13
10 2 4 7 13
10 2 16 13 7
11 3 13 16 25 22
12 2 10 13 19
12 2 22 19 13
13 2 6 15 7
13 2 16 7 15
14 2 6 5 15
14 2 14 15 5
15 2 14 23 15
15 2 24 15 23
16 3 16 15 24 25
17 3 5 2 11 14
18 2 3 0 12
18 2 9 12 0
19 3 11 8 17 20
20 2 14 11 23
20 2 20 23 11
21 2 18 21 17
21 2 20 17 21
22 2 18 19 21
22 2 22 21 19
23 3 21 22 25 24
24 2 20 21 23
24 2 24 23 21
vertices
26
3
0 -1 -1
1 -1 -1
-1 0 -1
0 0 -1
1 0 -1
-1 1 -1
0 1 -1
1 1 -1
-1 -1 0
0 -1 0
1 -1 0
-1 0 0
0 0 0
1 0 0
-1 1 0
0 1 0
1 1 0
-1 -1 1
0 -1 1
1 -1 1
-1 0 1
0 0 1
1 0 1
-1 1 1
0 1 1
1 1 1
+3 -3
View File
@@ -1,9 +1,9 @@
MFEM INLINE mesh v1.0
type = pyramid
nx = 2
ny = 2
nz = 2
nx = 4
ny = 4
nz = 4
sx = 1.0
sy = 1.0
sz = 1.0
+5 -8
View File
@@ -48,7 +48,7 @@ PROJECT_NAME = MFEM
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = v4.8.1
PROJECT_NUMBER = v4.7.1
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
@@ -951,10 +951,7 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/fem/ceed/integrators/nlconvection \
@MFEM_SOURCE_DIR@/fem/ceed/interface \
@MFEM_SOURCE_DIR@/fem/ceed/solvers \
@MFEM_SOURCE_DIR@/fem/eltrans \
@MFEM_SOURCE_DIR@/fem/fe \
@MFEM_SOURCE_DIR@/fem/gslib \
@MFEM_SOURCE_DIR@/fem/integ \
@MFEM_SOURCE_DIR@/fem/lor \
@MFEM_SOURCE_DIR@/fem/moonolith \
@MFEM_SOURCE_DIR@/fem/qinterp \
@@ -972,8 +969,6 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/adjoint \
@MFEM_SOURCE_DIR@/miniapps/autodiff \
@MFEM_SOURCE_DIR@/miniapps/common \
@MFEM_SOURCE_DIR@/miniapps/dpg \
@MFEM_SOURCE_DIR@/miniapps/dpg/util \
@MFEM_SOURCE_DIR@/miniapps/electromagnetics \
@MFEM_SOURCE_DIR@/miniapps/gslib \
@MFEM_SOURCE_DIR@/miniapps/hdiv-linear-solver \
@@ -991,10 +986,12 @@ INPUT = @MFEM_SOURCE_DIR@/doc/CodeDocumentation.dox \
@MFEM_SOURCE_DIR@/miniapps/performance \
@MFEM_SOURCE_DIR@/miniapps/shifted \
@MFEM_SOURCE_DIR@/miniapps/solvers \
@MFEM_SOURCE_DIR@/miniapps/spde \
@MFEM_SOURCE_DIR@/miniapps/tools \
@MFEM_SOURCE_DIR@/miniapps/toys \
@MFEM_SOURCE_DIR@/miniapps/tribol
@MFEM_SOURCE_DIR@/miniapps/tribol \
@MFEM_SOURCE_DIR@/miniapps/spde \
@MFEM_SOURCE_DIR@/miniapps/dpg \
@MFEM_SOURCE_DIR@/miniapps/dpg/util
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+16 -16
View File
@@ -42,10 +42,10 @@ namespace mfem {
* - mfem::forall functions in forall.hpp
*
* <H3>Example codes</H3>
* - <a class="el" href="ex0_8cpp_source.html">Example 0</a>: simplest example, nodal H1 FEM for the Poisson problem
* - <a class="el" href="ex0p_8cpp_source.html">Example 0p</a>: simplest parallel example, nodal H1 FEM for the Poisson problem
* - <a class="el" href="examples_2ex1_8cpp_source.html">Example 1</a>: nodal H1 FEM for the Poisson problem (same discretization as ex0 but with more sophisticated options)
* - <a class="el" href="examples_2ex1p_8cpp_source.html">Example 1p</a>: parallel nodal H1 FEM for the Poisson problem (same discretization as ex0p but with more sophisticated options)
* - <a class="el" href="ex0_8cpp_source.html">Example 0</a>: simplest example, nodal H1 FEM for the Laplace problem
* - <a class="el" href="ex0p_8cpp_source.html">Example 0p</a>: simplest parallel example, nodal H1 FEM for the Laplace problem
* - <a class="el" href="examples_2ex1_8cpp_source.html">Example 1</a>: nodal H1 FEM for the Laplace problem (same discretization as ex0 but with more sophisticated options)
* - <a class="el" href="examples_2ex1p_8cpp_source.html">Example 1p</a>: parallel nodal H1 FEM for the Laplace problem (same discretization as ex0p but with more sophisticated options)
* - <a class="el" href="ex2_8cpp_source.html">Example 2</a>: vector FEM for linear elasticity
* - <a class="el" href="ex2p_8cpp_source.html">Example 2p</a>: parallel vector FEM for linear elasticity
* - <a class="el" href="ex3_8cpp_source.html">Example 3</a>: Nedelec H(curl) FEM for the definite Maxwell problem
@@ -54,12 +54,12 @@ namespace mfem {
* - <a class="el" href="ex4p_8cpp_source.html">Example 4p</a>: parallel Raviart-Thomas H(div) FEM for the grad-div problem
* - <a class="el" href="ex5_8cpp_source.html">Example 5</a>: mixed pressure-velocity FEM for the Darcy problem
* - <a class="el" href="ex5p_8cpp_source.html">Example 5p</a>: parallel mixed pressure-velocity FEM for the Darcy problem
* - <a class="el" href="ex6_8cpp_source.html">Example 6</a>: non-conforming adaptive mesh refinement for the Poisson problem
* - <a class="el" href="ex6p_8cpp_source.html">Example 6p</a>: parallel non-conforming adaptive mesh refinement for the Poisson problem
* - <a class="el" href="ex7_8cpp_source.html">Example 7</a>: screened Poisson problem on a surface (the unit sphere)
* - <a class="el" href="ex7p_8cpp_source.html">Example 7p</a>: parallel screened Poisson problem on a surface (the unit sphere)
* - <a class="el" href="ex8_8cpp_source.html">Example 8</a>: Discontinuous Petrov-Galerkin (DPG) for the Poisson problem
* - <a class="el" href="ex8p_8cpp_source.html">Example 8p</a>: parallel Discontinuous Petrov-Galerkin (DPG) for the Poisson problem
* - <a class="el" href="ex6_8cpp_source.html">Example 6</a>: non-conforming adaptive mesh refinement for the Laplace problem
* - <a class="el" href="ex6p_8cpp_source.html">Example 6p</a>: parallel non-conforming adaptive mesh refinement for the Laplace problem
* - <a class="el" href="ex7_8cpp_source.html">Example 7</a>: Laplace problem on a surface (the unit sphere)
* - <a class="el" href="ex7p_8cpp_source.html">Example 7p</a>: parallel Laplace problem on a surface (the unit sphere)
* - <a class="el" href="ex8_8cpp_source.html">Example 8</a>: Discontinuous Petrov-Galerkin (DPG) for the Laplace problem
* - <a class="el" href="ex8p_8cpp_source.html">Example 8p</a>: parallel Discontinuous Petrov-Galerkin (DPG) for the Laplace problem
* - <a class="el" href="ex9_8cpp_source.html">Example 9</a>: Discontinuous Galerkin (DG) time-dependent advection
* - <a class="el" href="ex9p_8cpp_source.html">Example 9p</a>: parallel Discontinuous Galerkin (DG) time-dependent advection
* - <a class="el" href="ex10_8cpp_source.html">Example 10</a>: time-dependent implicit nonlinear elasticity
@@ -67,8 +67,8 @@ namespace mfem {
* - <a class="el" href="ex11p_8cpp_source.html">Example 11p</a>: parallel Laplace eigensolver
* - <a class="el" href="ex12p_8cpp_source.html">Example 12p</a>: parallel linear elasticity eigensolver
* - <a class="el" href="ex13p_8cpp_source.html">Example 13p</a>: parallel Maxwell eigensolver
* - <a class="el" href="ex14_8cpp_source.html">Example 14</a>: Discontinuous Galerkin (DG) for the Poisson problem
* - <a class="el" href="ex14p_8cpp_source.html">Example 14p</a>: parallel Discontinuous Galerkin (DG) for the Poisson problem
* - <a class="el" href="ex14_8cpp_source.html">Example 14</a>: Discontinuous Galerkin (DG) for the Laplace problem
* - <a class="el" href="ex14p_8cpp_source.html">Example 14p</a>: parallel Discontinuous Galerkin (DG) for the Laplace problem
* - <a class="el" href="ex15_8cpp_source.html">Example 15</a>: dynamic AMR for Laplace with prescribed time-dependent source
* - <a class="el" href="ex15p_8cpp_source.html">Example 15p</a>: parallel dynamic AMR for Laplace with prescribed time-dependent source
* - <a class="el" href="ex16_8cpp_source.html">Example 16</a>: time-dependent nonlinear heat equation
@@ -90,8 +90,8 @@ namespace mfem {
* - <a class="el" href="ex24p_8cpp_source.html">Example 24p</a>: parallel mixed finite element spaces and interpolators
* - <a class="el" href="ex25_8cpp_source.html">Example 25</a>: simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
* - <a class="el" href="ex25p_8cpp_source.html">Example 25p</a>: parallel simulation of electromagnetic wave propagation using a Perfectly Matched Layer (PML)
* - <a class="el" href="ex26_8cpp_source.html">Example 26</a>: multigrid preconditioner for the Poisson problem using nodal H1 FEM
* - <a class="el" href="ex26p_8cpp_source.html">Example 26p</a>: parallel multigrid preconditioner for the Poisson problem using nodal H1 FEM
* - <a class="el" href="ex26_8cpp_source.html">Example 26</a>: multigrid preconditioner for the Laplace problem using nodal H1 FEM
* - <a class="el" href="ex26p_8cpp_source.html">Example 26p</a>: parallel multigrid preconditioner for the Laplace problem using nodal H1 FEM
* - <a class="el" href="ex27_8cpp_source.html">Example 27</a>: boundary conditions for the Laplace problem
* - <a class="el" href="ex27p_8cpp_source.html">Example 27p</a>: parallel boundary conditions for the Laplace problem
* - <a class="el" href="ex28_8cpp_source.html">Example 28</a>: sliding contact in elasticity
@@ -230,8 +230,8 @@ namespace mfem {
* - <a class="el" href="parheat_8cpp_source.html">Optimization gradients</a>: Gradients of PDE-constrained function
* - <a class="el" href="par__example_8cpp_source.html">Parallel AD</a>: Parallel p-Laplacian example
* - <a class="el" href="seq__example_8cpp_source.html">Serial AD</a>: Serial p-Laplacian example
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Poisson problem
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Poisson problem
* - <a class="el" href="miniapps_2performance_2ex1_8cpp_source.html">HPC Example 1</a>: high-performance nodal H1 FEM for the Laplace problem
* - <a class="el" href="miniapps_2performance_2ex1p_8cpp_source.html">HPC Example 1p</a>: high-performance parallel nodal H1 FEM for the Laplace problem
* - <a class="el" href="generate__random__field_8cpp_source.html">SPDE Solvers</a>: SPDE solver random field generation
* - <a class="el" href="contact-patch-test_8cpp_source.html">Contact</a>: mortar contact patch test for elasticity
* - <a class="el" href="multidomain_8cpp_source.html">Multidomain miniapp</a>: Multidomain and Submesh demonstration miniapp
+1 -1
View File
@@ -12,7 +12,7 @@
// ex1 --amgx-file precon.json --amgx-preconditioner -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -10,7 +10,7 @@
// mpirun -np 4 ex1p --amgx-file amg_pcg.json
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -7,7 +7,7 @@
// ex0 -m ../data/square-disc.mesh -o 2
//
// Description: This example code demonstrates the most basic usage of MFEM to
// define a simple finite element discretization of the Poisson
// define a simple finite element discretization of the Laplace
// problem -Delta u = 1 with zero Dirichlet boundary conditions.
// General 2D/3D mesh files and finite element polynomial degrees
// can be specified by command line options.
+1 -1
View File
@@ -8,7 +8,7 @@
//
// Description: This example code demonstrates the most basic parallel usage of
// MFEM to define a simple finite element discretization of the
// Poisson problem -Delta u = 1 with zero Dirichlet boundary
// Laplace problem -Delta u = 1 with zero Dirichlet boundary
// conditions. General 2D/3D serial mesh files and finite element
// polynomial degrees can be specified by command line options.
+1 -1
View File
@@ -50,7 +50,7 @@
// ex1 -m ../data/beam-tet.mesh -pa -d ceed-cuda:/gpu/cuda/ref
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -27,7 +27,7 @@
//
// Description: This example code demonstrates the use of MFEM to define a
// discontinuous Galerkin (DG) finite element discretization of
// the Poisson problem -Delta u = 1 with homogeneous Dirichlet
// the Laplace problem -Delta u = 1 with homogeneous Dirichlet
// boundary conditions. Finite element spaces of any order,
// including zero on regular grids, are supported. The example
// highlights the use of discontinuous spaces and DG-specific face
+1 -1
View File
@@ -26,7 +26,7 @@
//
// Description: This example code demonstrates the use of MFEM to define a
// discontinuous Galerkin (DG) finite element discretization of
// the Poisson problem -Delta u = 1 with homogeneous Dirichlet
// the Laplace problem -Delta u = 1 with homogeneous Dirichlet
// boundary conditions. Finite element spaces of any order,
// including zero on regular grids, are supported. The example
// highlights the use of discontinuous spaces and DG-specific face
+2 -2
View File
@@ -159,7 +159,7 @@ int main(int argc, char *argv[])
FiniteElementSpace fespace(&mesh, &fec);
// 6. As in Example 1p, we set up bilinear and linear forms corresponding to
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the inner loop.
BilinearForm a(&fespace);
LinearForm b(&fespace);
@@ -446,7 +446,7 @@ real_t bdr_func(const Vector &pt, real_t t)
return composite_func(pt, t, front, ball);
}
// Laplacian of the exact solution, used for the right hand side.
// Laplace of the exact solution, used for the right hand side.
real_t rhs_func(const Vector &pt, real_t t)
{
return composite_func(pt, t, front_laplace, ball_laplace);
+2 -2
View File
@@ -181,7 +181,7 @@ int main(int argc, char *argv[])
ParFiniteElementSpace fespace(&pmesh, &fec);
// 7. As in Example 1p, we set up bilinear and linear forms corresponding to
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the inner loop.
ParBilinearForm a(&fespace);
ParLinearForm b(&fespace);
@@ -507,7 +507,7 @@ real_t bdr_func(const Vector &pt, real_t t)
return composite_func(pt, t, front, ball);
}
// Laplacian of the exact solution, used for the right hand side.
// Laplace of the exact solution, used for the right hand side.
real_t rhs_func(const Vector &pt, real_t t)
{
return composite_func(pt, t, front_laplace, ball_laplace);
+1 -1
View File
@@ -45,7 +45,7 @@
// mpirun -np 4 ex1p -m ../data/beam-tet.mesh -pa -d ceed-cpu
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -94,7 +94,7 @@ WaveOperator::WaveOperator(FiniteElementSpace &f,
M->AddDomainIntegrator(new MassIntegrator());
M->Assemble();
// Apply BCs
// Apply Bcs
fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
K->FormSystemMatrix(ess_tdof_list, Kmat);
M->FormSystemMatrix(ess_tdof_list, Mmat);
+1 -1
View File
@@ -17,7 +17,7 @@
// ex26 -m ../data/beam-hex.mesh -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions
// as in Example 1.
//
+1 -1
View File
@@ -14,7 +14,7 @@
// mpirun -np 4 ex26p -d ceed-cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions
// as in Example 1.
//
+1 -1
View File
@@ -10,7 +10,7 @@
// Description: This example code demonstrates the use of MFEM to define a
// finite element discretization of a PDE on a 2 dimensional
// surface embedded in a 3 dimensional domain. In this case we
// solve the Poisson problem -Div(sigma Grad u) = 1, with
// solve the Laplace problem -Div(sigma Grad u) = 1, with
// homogeneous Dirichlet boundary conditions, where sigma is an
// anisotropic diffusion constant defined as a 3x3 matrix
// coefficient.
+1 -1
View File
@@ -10,7 +10,7 @@
// Description: This example code demonstrates the use of MFEM to define a
// finite element discretization of a PDE on a 2 dimensional
// surface embedded in a 3 dimensional domain. In this case we
// solve the Poisson problem -Div(sigma Grad u) = 1, with
// solve the Laplace problem -Div(sigma Grad u) = 1, with
// homogeneous Dirichlet boundary conditions, where sigma is an
// anisotropic diffusion constant defined as a 3x3 matrix
// coefficient.
+3 -3
View File
@@ -390,7 +390,7 @@ public:
/**
@brief Class for surface linear form integrator
@brief Class for surface linearform integrator
Integrator to demonstrate the use of the surface integration rule on an
implicit surface defined by a level-set.
@@ -460,7 +460,7 @@ public:
};
/**
@brief Class for subdomain linear form integrator
@brief Class for subdomain linearform integrator
Integrator to demonstrate the use of the subdomain integration rule within
an area defined by an implicit surface defined by a level-set.
@@ -546,7 +546,7 @@ int main(int argc, char *argv[])
args.AddOption(&ref_levels, "-r", "--refine", "Number of meh refinements");
args.AddOption(&method, "-m", "--method",
"Cut integration method: 0 for moments-based, 1 for Algoim.");
args.AddOption(&inttype, "-i", "--integration-type",
args.AddOption(&inttype, "-i", "--integrationtype",
"IntegrationType to demonstrate");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
+1 -1
View File
@@ -16,7 +16,7 @@
//
// The particular problem being solved here is nearly the same
// as that in example 1 i.e. a simple finite element
// discretization of the Poisson problem -Delta u = 1 with
// discretization of the Laplace problem -Delta u = 1 with
// homogeneous Dirichlet boundary conditions and, in this case,
// an inhomogeneous diffusion coefficient. The diffusion
// coefficient is given a small default value throughout the
+1 -1
View File
@@ -16,7 +16,7 @@
//
// The particular problem being solved here is nearly the same
// as that in example 1 i.e. a simple finite element
// discretization of the Poisson problem -Delta u = 1 with
// discretization of the Laplace problem -Delta u = 1 with
// homogeneous Dirichlet boundary conditions and, in this case,
// an inhomogeneous diffusion coefficient. The diffusion
// coefficient is given a small default value throughout the
+7 -5
View File
@@ -65,7 +65,6 @@ int main(int argc, char *argv[])
bool static_cond = false;
bool hybridization = false;
bool pa = false;
bool ea = false;
const char *device_config = "cpu";
bool visualization = 1;
@@ -84,14 +83,18 @@ int main(int argc, char *argv[])
"--no-hybridization", "Enable hybridization.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
"--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.ParseCheck();
args.Parse();
if (!args.Good())
{
args.PrintUsage(cout);
return 1;
}
args.PrintOptions(cout);
kappa = freq * M_PI;
// 2. Enable hardware devices such as GPUs, and programming models such as
@@ -163,7 +166,6 @@ int main(int argc, char *argv[])
Coefficient *beta = new ConstantCoefficient(1.0);
BilinearForm *a = new BilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
if (ea) { a->SetAssemblyLevel(AssemblyLevel::ELEMENT); }
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
+13 -5
View File
@@ -71,7 +71,6 @@ int main(int argc, char *argv[])
bool static_cond = false;
bool hybridization = false;
bool pa = false;
bool ea = false;
const char *device_config = "cpu";
bool visualization = 1;
@@ -90,14 +89,24 @@ int main(int argc, char *argv[])
"--no-hybridization", "Enable hybridization.");
args.AddOption(&pa, "-pa", "--partial-assembly", "-no-pa",
"--no-partial-assembly", "Enable Partial Assembly.");
args.AddOption(&ea, "-ea", "--element-assembly", "-no-ea",
"--no-element-assembly", "Enable Element Assembly.");
args.AddOption(&device_config, "-d", "--device",
"Device configuration string, see Device::Configure().");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
"--no-visualization",
"Enable or disable GLVis visualization.");
args.ParseCheck();
args.Parse();
if (!args.Good())
{
if (myid == 0)
{
args.PrintUsage(cout);
}
return 1;
}
if (myid == 0)
{
args.PrintOptions(cout);
}
kappa = freq * M_PI;
// 3. Enable hardware devices such as GPUs, and programming models such as
@@ -185,7 +194,6 @@ int main(int argc, char *argv[])
Coefficient *beta = new ConstantCoefficient(1.0);
ParBilinearForm *a = new ParBilinearForm(fespace);
if (pa) { a->SetAssemblyLevel(AssemblyLevel::PARTIAL); }
if (ea) { a->SetAssemblyLevel(AssemblyLevel::ELEMENT); }
a->AddDomainIntegrator(new DivDivIntegrator(*alpha));
a->AddDomainIntegrator(new VectorFEMassIntegrator(*beta));
+2 -2
View File
@@ -25,7 +25,7 @@
// ex6 -pa -d ceed-cuda:/gpu/cuda/shared
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Poisson
// refinement loop. The problem being solved is again the Laplace
// equation -Delta u = 1 with homogeneous Dirichlet boundary
// conditions. The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
@@ -113,7 +113,7 @@ int main(int argc, char *argv[])
FiniteElementSpace fespace(&mesh, &fec);
// 6. As in Example 1, we set up bilinear and linear forms corresponding to
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the main loop.
BilinearForm a(&fespace);
if (pa)
+7 -104
View File
@@ -3,7 +3,6 @@
// Compile with: make ex6p
//
// Sample runs: mpirun -np 4 ex6p -m ../data/star-hilbert.mesh -o 2
// mpirun -np 4 ex6p -m ../data/star-hilbert.mesh -pref
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -rm 1 -o 1
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -rm 1 -o 2 -h1
// mpirun -np 4 ex6p -m ../data/square-disc.mesh -o 2 -cs
@@ -29,7 +28,7 @@
// mpirun -np 4 ex6p -pa -d ceed-cuda:/gpu/cuda/shared
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Poisson
// refinement loop. The problem being solved is again the Laplace
// equation -Delta u = 1 with homogeneous Dirichlet boundary
// conditions. The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
@@ -42,12 +41,6 @@
// from coarse to fine meshes, restarting from a checkpoint, as
// well as persistent GLVis visualization are also illustrated.
//
// There is also the option to use hp-refinement. Real
// applications should use some problem-dependent criteria for
// selecting between h- and p-refinement, but in this example, we
// simply alternate between refinement types to demonstrate the
// capabilities.
//
// We recommend viewing Example 1 before viewing this example.
#include "mfem.hpp"
@@ -76,8 +69,6 @@ int main(int argc, char *argv[])
bool smooth_rt = true;
bool restart = false;
bool visualization = true;
bool rebalance = true;
bool usePRefinement = false;
OptionsParser args(argc, argv);
args.AddOption(&mesh_file, "-m", "--mesh",
@@ -99,10 +90,6 @@ int main(int argc, char *argv[])
"Stop after reaching this many degrees of freedom.");
args.AddOption(&smooth_rt, "-rt", "--smooth-rt", "-h1", "--smooth-h1",
"Represent the smooth flux in RT or vector H1 space.");
args.AddOption(&usePRefinement, "-pref", "--p-refine", "-no-pref",
"--no-p-refine", "Alternate between h- and p-refinement.");
args.AddOption(&rebalance, "-reb", "--rebalance", "-no-reb",
"--no-rebalance", "Load balance the nonconforming mesh.");
args.AddOption(&restart, "-res", "--restart", "-no-res", "--no-restart",
"Restart computation from the last checkpoint.");
args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
@@ -122,15 +109,6 @@ int main(int argc, char *argv[])
args.PrintOptions(cout);
}
if (usePRefinement && rebalance)
{
rebalance = false;
if (myid == 0)
{
cout << "Load balancing is not performed with p-refinements.\n";
}
}
// 3. Enable hardware devices such as GPUs, and programming models such as
// CUDA, OCCA, RAJA and OpenMP based on command line options.
Device device(device_config);
@@ -208,7 +186,7 @@ int main(int argc, char *argv[])
ParFiniteElementSpace fespace(pmesh, &fec);
// 11. As in Example 1p, we set up bilinear and linear forms corresponding to
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the main loop.
ParBilinearForm a(&fespace);
if (pa)
@@ -343,15 +321,7 @@ int main(int argc, char *argv[])
if (visualization)
{
sout << "parallel " << num_procs << " " << myid << "\n";
if (usePRefinement)
{
std::unique_ptr<GridFunction> vis_x = x.ProlongateToMaxOrder();
sout << "solution\n" << *pmesh << *vis_x << flush;
}
else
{
sout << "solution\n" << *pmesh << x << flush;
}
sout << "solution\n" << *pmesh << x << flush;
}
if (global_dofs >= max_dofs)
@@ -367,31 +337,8 @@ int main(int argc, char *argv[])
// estimator to obtain element errors, then it selects elements to be
// refined and finally it modifies the mesh. The Stop() method can be
// used to determine if a stopping criterion was met.
// Simply alternate between h- and p-refinement.
const bool pRefine = usePRefinement && ((it % 2) == 1);
bool stop = false;
Array<pRefinement> prefinements;
if (pRefine)
{
Array<Refinement> refinements;
refiner.MarkWithoutRefining(*pmesh, refinements);
stop = pmesh->ReduceInt(refinements.Size()) == 0LL;
prefinements.SetSize(refinements.Size());
for (int i=0; i<refinements.Size(); ++i)
{
prefinements[i].index = refinements[i].index;
prefinements[i].delta = 1; // Increase the element order by 1
}
}
else
{
refiner.Apply(*pmesh);
stop = refiner.Stop();
}
if (stop)
refiner.Apply(*pmesh);
if (refiner.Stop())
{
if (myid == 0)
{
@@ -405,20 +352,12 @@ int main(int argc, char *argv[])
// to any GridFunctions over the space. In this case, the update
// matrix is an interpolation matrix so the updated GridFunction will
// still represent the same function as before refinement.
if (pRefine)
{
fespace.PRefineAndUpdate(prefinements);
}
else
{
fespace.Update();
}
fespace.Update();
x.Update();
// 25. Load balance the mesh, and update the space and solution. Currently
// available only for nonconforming meshes.
if (pmesh->Nonconforming() && rebalance)
if (pmesh->Nonconforming())
{
pmesh->Rebalance();
@@ -450,42 +389,6 @@ int main(int argc, char *argv[])
}
}
// Save result
if (usePRefinement)
{
L2_FECollection fecL2(0, dim);
ParFiniteElementSpace l2fespace(pmesh, &fecL2);
ParGridFunction xo(&l2fespace); // Element order field
xo = 0.0;
for (int e=0; e<pmesh->GetNE(); ++e)
{
const int p_elem = fespace.GetElementOrder(e);
Array<int> dofs;
l2fespace.GetElementDofs(e, dofs);
xo[dofs[0]] = p_elem;
}
ostringstream mesh_name, sol_name, order_name;
mesh_name << "mesh." << setfill('0') << setw(6) << myid;
sol_name << "sol." << setfill('0') << setw(6) << myid;
order_name << "order." << setfill('0') << setw(6) << myid;
ofstream mesh_ofs(mesh_name.str().c_str());
mesh_ofs.precision(8);
pmesh->ParPrint(mesh_ofs);
ofstream sol_ofs(sol_name.str().c_str());
sol_ofs.precision(8);
std::unique_ptr<ParGridFunction> vis_x = x.ProlongateToMaxOrder();
vis_x->Save(sol_ofs);
ofstream order_ofs(order_name.str().c_str());
order_ofs.precision(8);
xo.Save(order_ofs);
}
delete smooth_flux_fes;
delete smooth_flux_fec;
delete pmesh;
+2 -2
View File
@@ -9,8 +9,8 @@
//
// Description: This example code demonstrates the use of MFEM to define a
// triangulation of a unit sphere and a simple isoparametric
// finite element discretization of the screened Poisson problem,
// -Delta u + u = f.
// finite element discretization of the Laplace problem with mass
// term, -Delta u + u = f.
//
// The example highlights mesh generation, the use of mesh
// refinement, high-order meshes and finite elements, as well as
+2 -2
View File
@@ -9,8 +9,8 @@
//
// Description: This example code demonstrates the use of MFEM to define a
// triangulation of a unit sphere and a simple isoparametric
// finite element discretization of the screened Poisson problem,
// -Delta u + u = f.
// finite element discretization of the Laplace problem with mass
// term, -Delta u + u = f.
//
// The example highlights mesh generation, the use of mesh
// refinement, high-order meshes and finite elements, as well as
+1 -1
View File
@@ -15,7 +15,7 @@
//
// Description: This example code demonstrates the use of the Discontinuous
// Petrov-Galerkin (DPG) method in its primal 2x2 block form as a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = f with homogeneous Dirichlet boundary conditions. We
// use high-order continuous trial space, a high-order interfacial
// (trace) space, and a high-order discontinuous test space
+1 -1
View File
@@ -14,7 +14,7 @@
//
// Description: This example code demonstrates the use of the Discontinuous
// Petrov-Galerkin (DPG) method in its primal 2x2 block form as a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = f with homogeneous Dirichlet boundary conditions. We
// use high-order continuous trial space, a high-order interfacial
// (trace) space, and a high-order discontinuous test space
+1 -1
View File
@@ -35,7 +35,7 @@
// ex1 -m ../../data/beam-hex.mesh -pa -d cuda
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -33,7 +33,7 @@
"id": "public-white",
"metadata": {},
"source": [
"This is the simplest MFEM example and a good starting point for new users. The example demonstrates the use of MFEM to define and solve an $H^1$ finite element discretization of the Poisson problem\n",
"This is the simplest MFEM example and a good starting point for new users. The example demonstrates the use of MFEM to define and solve an $H^1$ finite element discretization of the Laplace problem\n",
"\n",
"$$\n",
"-\\Delta u = 1\n",
+1 -1
View File
@@ -189,7 +189,7 @@ clean-build:
clean-exec:
@rm -f refined.mesh displaced.mesh mesh.* ex5.mesh ex6p-checkpoint.*
@rm -rf Example5* Example9* Example15* Example16* Example23* ParaView
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.* order.*
@rm -f sphere_refined.* sol.* sol_u.* sol_p.* sol_r.* sol_i.*
@rm -f ex9.mesh ex9-mesh.* ex9-init.* ex9-final.*
@rm -f deformed.* velocity.* elastic_energy.* mode_* mode_deriv_* flux.*
@rm -f ex5-p-*.bp ex9-p-*.bp ex12-p-*.bp ex16-p-*.bp
+2 -8
View File
@@ -16,16 +16,10 @@
// multi-physics applications.
//
// This particular example is only for serial runtimes.
// For non-conforming meshes please have a look at example
// "ex2p.cpp".
#include "example_utils.hpp"
#include "mfem.hpp"
#ifndef MFEM_USE_MOONOLITH
#error This example requires that MFEM is built with MFEM_USE_MOONOLITH=YES
#endif
using namespace mfem;
using namespace std;
@@ -221,8 +215,8 @@ int main(int argc, char *argv[])
mfem::out << "l2 error: src: " << src_err << ", dest: " << dest_err
<< std::endl;
plot(*src_mesh, src_fun, "source", 0);
plot(*dest_mesh, dest_fun, "destination", 1);
plot(*src_mesh, src_fun, "source");
plot(*dest_mesh, dest_fun, "destination");
}
}
else
+16 -54
View File
@@ -8,23 +8,18 @@
// mpirun -np 4 ex1p -s ../../data/inline-hex.mesh -d ../../data/inline-tet.mesh
//
// Description: This example code demonstrates the use of MFEM for transferring
// discrete fields from one conforming finite element mesh to another. The
// discrete fields from one finite element mesh to another. The
// meshes can be of arbitrary shape and completely unrelated with
// each other. This feature can be used for implementing immersed
// domain methods for fluid-structure interaction or general
// multi-physics applications.
//
// This particular example is for parallel runtimes. Vector FE is
// an experimental feature in parallel. For non-conforming meshes
// please have a look at example "ex2p.cpp".
// an experimental feature in parallel.
#include "example_utils.hpp"
#include "mfem.hpp"
#ifndef MFEM_USE_MOONOLITH
#error This example requires that MFEM is built with MFEM_USE_MOONOLITH=YES
#endif
using namespace mfem;
using namespace std;
@@ -55,8 +50,6 @@ int main(int argc, char *argv[])
int dest_fe_order = 1;
bool visualization = true;
bool use_vector_fe = false;
bool use_h1 = true;
bool use_vector_space = false;
bool verbose = false;
bool assemble_mass_and_coupling_together = true;
@@ -79,28 +72,14 @@ int main(int argc, char *argv[])
args.AddOption(&verbose, "-verb", "--verbose", "--no-verb", "--no-verbose",
"Enable/Disable verbose output");
args.AddOption(&use_vector_fe, "-vfe", "--use_vector_fe", "-no-vfe",
"--no-vector_fe",
"Use RT|ND vector finite elements (Experimental)");
args.AddOption(&use_vector_space, "-vfs", "--use_vector_space", "-no-vfs",
"--no-vector_space",
"Use Lagrange vector finite elements (Experimental)");
args.AddOption(&use_h1, "-h1", "--use-h1", "-nh1", "--no-h1",
"Use H1 collection");
"--no-vector_fe", "Use vector finite elements (Experimental)");
args.AddOption(&assemble_mass_and_coupling_together, "-act",
"--assemble_mass_and_coupling_together", "-no-act",
"--no-assemble_mass_and_coupling_together",
"Assemble mass and coupling operators together (better for "
"non-affine elements)");
"Assemble mass and coupling operators together (better for non-affine elements)");
args.Parse();
check_options(args);
if (use_vector_fe && use_vector_space)
{
mfem::err <<
"WARNING: use_vector_fe and use_vector_space options"
"are both true, ignoring use_vector_fe\n";
}
shared_ptr<Mesh> src_mesh, dest_mesh;
ifstream imesh;
@@ -190,30 +169,17 @@ int main(int argc, char *argv[])
}
else
{
if (use_h1)
{
src_fe_coll =
make_shared<H1_FECollection>(source_fe_order, src_mesh->Dimension());
dest_fe_coll =
make_shared<H1_FECollection>(dest_fe_order, dest_mesh->Dimension());
}
else
{
src_fe_coll =
make_shared<L2_FECollection>(source_fe_order, src_mesh->Dimension());
dest_fe_coll =
make_shared<L2_FECollection>(dest_fe_order, dest_mesh->Dimension());
}
src_fe_coll =
make_shared<L2_FECollection>(source_fe_order, src_mesh->Dimension());
dest_fe_coll =
make_shared<L2_FECollection>(dest_fe_order, dest_mesh->Dimension());
}
auto src_fe = make_shared<ParFiniteElementSpace>(
p_src_mesh.get(), src_fe_coll.get(),
use_vector_space ? src_mesh->Dimension() : 1);
auto src_fe =
make_shared<ParFiniteElementSpace>(p_src_mesh.get(), src_fe_coll.get());
auto dest_fe = make_shared<ParFiniteElementSpace>(
p_dest_mesh.get(), dest_fe_coll.get(),
use_vector_space ? dest_mesh->Dimension() : 1);
auto dest_fe =
make_shared<ParFiniteElementSpace>(p_dest_mesh.get(), dest_fe_coll.get());
ParGridFunction src_fun(src_fe.get());
@@ -223,7 +189,7 @@ int main(int argc, char *argv[])
// To be used with vector fe
VectorFunctionCoefficient vector_coeff(dim, &vector_fun);
if (use_vector_fe || use_vector_space)
if (use_vector_fe)
{
src_fun.ProjectCoefficient(vector_coeff);
src_fun.Update();
@@ -243,11 +209,7 @@ int main(int argc, char *argv[])
assemble_mass_and_coupling_together);
assembler.SetVerbose(verbose);
if (use_vector_space)
{
assembler.AddMortarIntegrator(make_shared<LagrangeVectorL2MortarIntegrator>());
}
else if (use_vector_fe)
if (use_vector_fe)
{
assembler.AddMortarIntegrator(make_shared<VectorL2MortarIntegrator>());
}
@@ -281,8 +243,8 @@ int main(int argc, char *argv[])
<< std::endl;
}
plot(*p_src_mesh, src_fun, "source", 0);
plot(*p_dest_mesh, dest_fun, "destination", 1);
plot(*p_src_mesh, src_fun, "source");
plot(*p_dest_mesh, dest_fun, "destination");
}
}
else
+2 -6
View File
@@ -20,10 +20,6 @@
#include "example_utils.hpp"
#include "mfem.hpp"
#ifndef MFEM_USE_MOONOLITH
#error This example requires that MFEM is built with MFEM_USE_MOONOLITH=YES
#endif
using namespace mfem;
using namespace std;
@@ -190,8 +186,8 @@ int main(int argc, char *argv[])
<< std::endl;
}
plot(*p_src_mesh, src_fun, "source", 0);
plot(*p_dest_mesh, dest_fun, "destination", 1);
plot(*p_src_mesh, src_fun, "source");
plot(*p_dest_mesh, dest_fun, "destination");
}
}
else
+1 -15
View File
@@ -84,8 +84,7 @@ void vector_fun(const mfem::Vector &x, mfem::Vector &f)
f = n;
}
inline void plot(mfem::Mesh &mesh, mfem::GridFunction &x, std::string title,
const int plot_number = 0)
inline void plot(mfem::Mesh &mesh, mfem::GridFunction &x, std::string title)
{
using namespace std;
using namespace mfem;
@@ -104,18 +103,5 @@ inline void plot(mfem::Mesh &mesh, mfem::GridFunction &x, std::string title,
sol_sock.precision(8);
sol_sock << "solution\n" << mesh << x
<< "window_title '"<< title << "'\n" << flush;
sol_sock << "window_geometry ";
sol_sock << (plot_number * 600) << " " << 0 << " " << 600 << " " << 600 <<
"\n";
if (mesh.Dimension() == 2)
{
sol_sock << "keys jRmclA\n";
}
else
{
sol_sock << "keys rmclAa\n";
}
sol_sock << flush;
}
+1 -1
View File
@@ -10,7 +10,7 @@
// mpirun -np 4 ex1p -pa -d cuda --petscopts rc_ex1p_device
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+2 -2
View File
@@ -8,7 +8,7 @@
// mpirun -np 4 ex6p -m ../../data/amr-quad.mesh -nonoverlapping
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Poisson
// refinement loop. The problem being solved is again the Laplace
// equation -Delta u = 1 with homogeneous Dirichlet boundary
// conditions. The problem is solved on a sequence of meshes which
// are locally refined in a conforming (triangles, tetrahedrons)
@@ -131,7 +131,7 @@ int main(int argc, char *argv[])
ParFiniteElementSpace fespace(&pmesh, &fec);
// 7. As in Example 1p, we set up bilinear and linear forms corresponding to
// the Poisson problem -\Delta u = 1. We don't assemble the discrete
// the Laplace problem -\Delta u = 1. We don't assemble the discrete
// problem yet, this will be done in the main loop.
ParBilinearForm a(&fespace);
ParLinearForm b(&fespace);
+1 -1
View File
@@ -11,7 +11,7 @@
// creating a symbolic link to the above directory in ../../data.
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -12,7 +12,7 @@
// creating a symbolic link to the above directory in ../../data.
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
+1 -1
View File
@@ -6,7 +6,7 @@
// Sample runs: mpirun -np 8 ex6p
//
// Description: This is a version of Example 1 with a simple adaptive mesh
// refinement loop. The problem being solved is again the Poisson
// refinement loop. The problem being solved is again the Laplace
// equation -Delta u = 1 with homogeneous Dirichlet boundary
// conditions. The problem is solved on a sequence of meshes which
// are adapted in a conforming (tetrahedrons) manner according
+1 -1
View File
@@ -26,7 +26,7 @@
// mpirun -np 4 ex1p -m ../../data/mobius-strip.mesh
//
// Description: This example code demonstrates the use of MFEM to define a
// simple finite element discretization of the Poisson problem
// simple finite element discretization of the Laplace problem
// -Delta u = 1 with homogeneous Dirichlet boundary conditions.
// Specifically, we discretize using a FE space of the specified
// order, or if order < 1 using an isoparametric/isogeometric
-7
View File
@@ -35,7 +35,6 @@ set(SRCS
integ/bilininteg_mass_ea.cpp
integ/bilininteg_mixedcurl_pa.cpp
integ/bilininteg_mixedvecgrad_pa.cpp
integ/bilininteg_trace_jump_ea.cpp
integ/bilininteg_transpose_ea.cpp
integ/bilininteg_vecdiffusion_mf.cpp
integ/bilininteg_vecdiffusion_pa.cpp
@@ -47,7 +46,6 @@ set(SRCS
integ/bilininteg_diffusion_kernels.cpp
integ/bilininteg_elasticity_kernels.cpp
integ/bilininteg_hcurl_kernels.cpp
integ/bilininteg_hdiv_ea.cpp
integ/bilininteg_hdiv_kernels.cpp
integ/bilininteg_hcurlhdiv_kernels.cpp
integ/bilininteg_mass_kernels.cpp
@@ -65,7 +63,6 @@ set(SRCS
dgmassinv.cpp
doftrans.cpp
eltrans.cpp
batchitrans.cpp
estimators.cpp
fe.cpp
fe/face_map_utils.cpp
@@ -76,7 +73,6 @@ set(SRCS
fe/fe_nd.cpp
fe/fe_nurbs.cpp
fe/fe_pos.cpp
fe/fe_pyramid.cpp
fe/fe_rt.cpp
fe/fe_ser.cpp
fe_coll.cpp
@@ -84,7 +80,6 @@ set(SRCS
geom.cpp
gridfunc.cpp
hybridization.cpp
hybridization_ext.cpp
intrules.cpp
intrules_cut.cpp
ceed/interface/basis.cpp
@@ -191,7 +186,6 @@ set(HDRS
fe/fe_nd.hpp
fe/fe_nurbs.hpp
fe/fe_pos.hpp
fe/fe_pyramid.hpp
fe/fe_rt.hpp
fe/fe_ser.hpp
fe_coll.hpp
@@ -200,7 +194,6 @@ set(HDRS
geom.hpp
gridfunc.hpp
hybridization.hpp
hybridization_ext.hpp
intrules.hpp
intrules_cut.hpp
kernel_dispatch.hpp
-2035
View File
File diff suppressed because it is too large Load Diff
+50 -75
View File
@@ -71,11 +71,15 @@ BilinearForm::BilinearForm(FiniteElementSpace * f)
sequence = f->GetSequence();
mat = mat_e = NULL;
extern_bfs = 0;
element_matrices = NULL;
static_cond = NULL;
hybridization = NULL;
precompute_sparsity = 0;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::LEGACY;
batch = 1;
ext = NULL;
}
BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
@@ -85,11 +89,15 @@ BilinearForm::BilinearForm (FiniteElementSpace * f, BilinearForm * bf, int ps)
sequence = f->GetSequence();
mat_e = NULL;
extern_bfs = 1;
element_matrices = NULL;
static_cond = NULL;
hybridization = NULL;
precompute_sparsity = ps;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::LEGACY;
batch = 1;
ext = NULL;
// Copy the pointers to the integrators
domain_integs = bf->domain_integs;
@@ -119,16 +127,16 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
break;
case AssemblyLevel::FULL:
SetDiagonalPolicy( DIAG_ONE ); // Only diagonal policy supported on device
ext.reset(new FABilinearFormExtension(this));
ext = new FABilinearFormExtension(this);
break;
case AssemblyLevel::ELEMENT:
ext.reset(new EABilinearFormExtension(this));
ext = new EABilinearFormExtension(this);
break;
case AssemblyLevel::PARTIAL:
ext.reset(new PABilinearFormExtension(this));
ext = new PABilinearFormExtension(this);
break;
case AssemblyLevel::NONE:
ext.reset(new MFBilinearFormExtension(this));
ext = new MFBilinearFormExtension(this);
break;
default:
MFEM_ABORT("BilinearForm: unknown assembly level");
@@ -137,13 +145,14 @@ void BilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
void BilinearForm::EnableStaticCondensation()
{
delete static_cond;
if (assembly != AssemblyLevel::LEGACY)
{
static_cond.reset();
static_cond = NULL;
MFEM_WARNING("Static condensation not supported for this assembly level");
return;
}
static_cond.reset(new StaticCondensation(fes));
static_cond = new StaticCondensation(fes);
if (static_cond->ReducesTrueVSize())
{
bool symmetric = false; // TODO
@@ -152,7 +161,8 @@ void BilinearForm::EnableStaticCondensation()
}
else
{
static_cond.reset();
delete static_cond;
static_cond = NULL;
}
}
@@ -160,18 +170,15 @@ void BilinearForm::EnableHybridization(FiniteElementSpace *constr_space,
BilinearFormIntegrator *constr_integ,
const Array<int> &ess_tdof_list)
{
if (assembly != AssemblyLevel::LEGACY && assembly != AssemblyLevel::ELEMENT)
delete hybridization;
if (assembly != AssemblyLevel::LEGACY)
{
delete constr_integ;
hybridization.reset();
hybridization = NULL;
MFEM_WARNING("Hybridization not supported for this assembly level");
return;
}
hybridization.reset(new Hybridization(fes, constr_space));
if (assembly == AssemblyLevel::ELEMENT)
{
hybridization->EnableDeviceExecution();
}
hybridization = new Hybridization(fes, constr_space);
hybridization->SetConstraintIntegrator(constr_integ);
hybridization->Init(ess_tdof_list);
}
@@ -224,8 +231,8 @@ void BilinearForm::Finalize (int skip_zeros)
if (!static_cond) { mat->Finalize(skip_zeros); }
if (mat_e) { mat_e->Finalize(skip_zeros); }
if (static_cond) { static_cond->Finalize(); }
if (hybridization) { hybridization->Finalize(); }
}
if (hybridization) { hybridization->Finalize(); }
}
void BilinearForm::AddDomainIntegrator(BilinearFormIntegrator *bfi)
@@ -458,10 +465,6 @@ void BilinearForm::Assemble(int skip_zeros)
if (ext)
{
ext->Assemble();
if (hybridization)
{
hybridization->AssembleElementMatrices(GetElementMatrices());
}
return;
}
@@ -832,19 +835,7 @@ void BilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list, Vector &x,
{
if (ext)
{
if (hybridization)
{
FormSystemMatrix(ess_tdof_list, A);
ConstrainedOperator A_constrained(this, ess_tdof_list);
A_constrained.EliminateRHS(x, b);
hybridization->ReduceRHS(b, B);
X.SetSize(B.Size());
X = 0.0;
}
else
{
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
}
ext->FormLinearSystem(ess_tdof_list, x, b, A, X, B, copy_interior);
return;
}
const SparseMatrix *P = fes->GetConformingProlongation();
@@ -912,16 +903,7 @@ void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
{
if (ext)
{
if (hybridization)
{
const int remove_zeros = 0;
Finalize(remove_zeros);
A.Reset(&hybridization->GetMatrix(), false);
}
else
{
ext->FormSystemMatrix(ess_tdof_list, A);
}
ext->FormSystemMatrix(ess_tdof_list, A);
return;
}
@@ -962,7 +944,7 @@ void BilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
void BilinearForm::RecoverFEMSolution(const Vector &X,
const Vector &b, Vector &x)
{
if (ext && !hybridization)
if (ext)
{
ext->RecoverFEMSolution(X, b, x);
return;
@@ -1019,26 +1001,16 @@ void BilinearForm::RecoverFEMSolution(const Vector &X,
void BilinearForm::ComputeElementMatrices()
{
if (element_matrices) { return; }
if (auto *ea_ext = dynamic_cast<EABilinearFormExtension*>(ext.get()))
if (element_matrices || domain_integs.Size() == 0 || fes->GetNE() == 0)
{
element_matrices.reset(new DenseTensor);
ea_ext->GetElementMatrices(*element_matrices, ElementDofOrdering::NATIVE, true);
return;
}
if (domain_integs.Size() == 0 || fes->GetNE() == 0)
{
element_matrices.reset(new DenseTensor);
return;
}
int num_elements = fes->GetNE();
int num_dofs_per_el = fes->GetTypicalFE()->GetDof() * fes->GetVDim();
element_matrices.reset(new DenseTensor(num_dofs_per_el, num_dofs_per_el,
num_elements));
element_matrices = new DenseTensor(num_dofs_per_el, num_dofs_per_el,
num_elements);
DenseMatrix tmp;
IsoparametricTransformation eltrans;
@@ -1069,12 +1041,6 @@ void BilinearForm::ComputeElementMatrices()
}
}
const DenseTensor &BilinearForm::GetElementMatrices()
{
ComputeElementMatrices(); // Won't recompute if element_matrices exists
return *element_matrices;
}
void BilinearForm::EliminateEssentialBC(const Array<int> &bdr_attr_is_ess,
const Vector &sol, Vector &rhs,
DiagonalPolicy dpolicy)
@@ -1262,13 +1228,15 @@ void BilinearForm::Update(FiniteElementSpace *nfes)
delete mat_e;
mat_e = NULL;
FreeElementMatrices();
static_cond.reset();
delete static_cond;
static_cond = NULL;
if (full_update)
{
delete mat;
mat = NULL;
hybridization.reset();
delete hybridization;
hybridization = NULL;
sequence = fes->GetSequence();
}
else
@@ -1291,6 +1259,9 @@ BilinearForm::~BilinearForm()
{
delete mat_e;
delete mat;
delete element_matrices;
delete static_cond;
delete hybridization;
if (!extern_bfs)
{
@@ -1302,6 +1273,8 @@ BilinearForm::~BilinearForm()
for (k=0; k < boundary_face_integs.Size(); k++)
{ delete boundary_face_integs[k]; }
}
delete ext;
}
@@ -1328,6 +1301,7 @@ MixedBilinearForm::MixedBilinearForm (FiniteElementSpace *tr_fes,
mat = NULL;
mat_e = NULL;
extern_bfs = 1;
ext = NULL;
// Copy the pointers to the integrators
domain_integs = mbf->domain_integs;
@@ -1357,22 +1331,22 @@ void MixedBilinearForm::SetAssemblyLevel(AssemblyLevel assembly_level)
case AssemblyLevel::LEGACY:
break;
case AssemblyLevel::FULL:
// ext.reset(new FAMixedBilinearFormExtension(this));
// ext = new FAMixedBilinearFormExtension(this);
// Use the original BilinearForm implementation for now
break;
case AssemblyLevel::ELEMENT:
MFEM_ABORT("Element assembly not supported yet... stay tuned!");
// ext.reset(new EAMixedBilinearFormExtension(this));
mfem_error("Element assembly not supported yet... stay tuned!");
// ext = new EAMixedBilinearFormExtension(this);
break;
case AssemblyLevel::PARTIAL:
ext.reset(new PAMixedBilinearFormExtension(this));
ext = new PAMixedBilinearFormExtension(this);
break;
case AssemblyLevel::NONE:
MFEM_ABORT("Matrix-free action not supported yet... stay tuned!");
// ext.reset(new MFMixedBilinearFormExtension(this));
mfem_error("Matrix-free action not supported yet... stay tuned!");
// ext = new MFMixedBilinearFormExtension(this);
break;
default:
MFEM_ABORT("Unknown assembly level");
mfem_error("Unknown assembly level");
}
}
@@ -2369,6 +2343,7 @@ MixedBilinearForm::~MixedBilinearForm()
for (i = 0; i < boundary_trace_face_integs.Size(); i++)
{ delete boundary_trace_face_integs[i]; }
}
delete ext;
}
void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
@@ -2385,16 +2360,16 @@ void DiscreteLinearOperator::SetAssemblyLevel(AssemblyLevel assembly_level)
// Use the original implementation for now
break;
case AssemblyLevel::ELEMENT:
MFEM_ABORT("Element assembly not supported yet... stay tuned!");
mfem_error("Element assembly not supported yet... stay tuned!");
break;
case AssemblyLevel::PARTIAL:
ext.reset(new PADiscreteLinearOperatorExtension(this));
ext = new PADiscreteLinearOperatorExtension(this);
break;
case AssemblyLevel::NONE:
MFEM_ABORT("Matrix-free action not supported yet... stay tuned!");
mfem_error("Matrix-free action not supported yet... stay tuned!");
break;
default:
MFEM_ABORT("Unknown assembly level");
mfem_error("Unknown assembly level");
}
}
+13 -19
View File
@@ -83,7 +83,7 @@ protected:
/** @brief Extension for supporting Full Assembly (FA),
Element Assembly (EA),Partial Assembly (PA),
or Matrix Free assembly (MF). */
std::unique_ptr<BilinearFormExtension> ext;
BilinearFormExtension *ext;
/** Indicates if the sparse matrix is sorted after assembly when using
Full Assembly (FA). */
@@ -122,10 +122,10 @@ protected:
mutable DenseMatrix elemmat;
mutable Array<int> vdofs;
std::unique_ptr<DenseTensor> element_matrices;
DenseTensor *element_matrices; ///< Owned.
std::unique_ptr<StaticCondensation> static_cond;
std::unique_ptr<Hybridization> hybridization;
StaticCondensation *static_cond; ///< Owned.
Hybridization *hybridization; ///< Owned.
/** @brief This data member allows one to specify what should be done to the
diagonal matrix entries and corresponding RHS values upon elimination of
@@ -148,11 +148,13 @@ protected:
BilinearForm() : Matrix (0)
{
fes = NULL; sequence = -1;
mat = mat_e = NULL; extern_bfs = 0;
mat = mat_e = NULL; extern_bfs = 0; element_matrices = NULL;
static_cond = NULL; hybridization = NULL;
precompute_sparsity = 0;
diag_policy = DIAG_KEEP;
assembly = AssemblyLevel::LEGACY;
batch = 1;
ext = NULL;
}
private:
@@ -212,7 +214,7 @@ public:
/// Returns the assembly level
AssemblyLevel GetAssemblyLevel() const { return assembly; }
Hybridization *GetHybridization() const { return hybridization.get(); }
Hybridization *GetHybridization() const { return hybridization; }
/** @brief Enable the use of static condensation. For details see the
description for class StaticCondensation in fem/staticcond.hpp This
@@ -222,7 +224,7 @@ public:
/** @brief Check if static condensation was actually enabled by a previous
call to EnableStaticCondensation(). */
bool StaticCondensationIsEnabled() const { return static_cond != nullptr; }
bool StaticCondensationIsEnabled() const { return static_cond; }
/// Return the trace FE space associated with static condensation.
FiniteElementSpace *SCFESpace() const
@@ -567,20 +569,12 @@ public:
void RecoverFEMSolution(const Vector &X, const Vector &b,
Vector &x) override;
/// @brief Compute and store internally all element matrices.
///
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLevel(), this will
/// use efficient (device-accelerated) assembly of the element matrices.
/// Compute and store internally all element matrices.
void ComputeElementMatrices();
/// Free the memory used by the element matrices.
void FreeElementMatrices() { element_matrices.reset(); }
/// @brief Return a DenseTensor containing the assembled element matrices.
///
/// If AssemblyLevel::ELEMENT is selected with SetAssemblyLevel(), this will
/// use efficient (device-accelerated) assembly of the element matrices.
const DenseTensor &GetElementMatrices();
void FreeElementMatrices()
{ delete element_matrices; element_matrices = NULL; }
/// Compute the element matrix of the given element
/** The element matrix is computed by calling the domain integrators
@@ -766,7 +760,7 @@ protected:
/** Extension for supporting Full Assembly (FA), Element Assembly (EA),
Partial Assembly (PA), or Matrix Free assembly (MF). */
std::unique_ptr<MixedBilinearFormExtension> ext;
MixedBilinearFormExtension *ext;
/** @brief Indicates the BilinearFormIntegrator%s stored in
MixedBilinearForm#domain_integs, MixedBilinearForm#boundary_integs,
+77 -287
View File
@@ -16,7 +16,6 @@
#include "bilinearform.hpp"
#include "pbilinearform.hpp"
#include "pgridfunc.hpp"
#include "fe/face_map_utils.hpp"
#include "ceed/interface/util.hpp"
namespace mfem
@@ -865,137 +864,52 @@ void EABilinearFormExtension::Assemble()
ne = trial_fes->GetMesh()->GetNE();
elemDofs = trial_fes->GetTypicalFE()->GetDof();
Vector ea_data_tmp;
ea_data.SetSize(ne*elemDofs*elemDofs, Device::GetMemoryType());
ea_data.UseDevice(true);
auto add_with_markers = [&](const Vector &ea_1, Vector &ea_2, const int ne_,
const Array<int> &markers, const Array<int> &attrs,
const bool add)
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
const int integratorCount = integrators.Size();
if ( integratorCount == 0 )
{
if (ne_ == 0) { return; }
const int sz = ea_1.Size() / ne_;
const int *d_m = markers.Read();
const int *d_a = attrs.Read();
const auto d_ea_1 = Reshape(ea_1.Read(), sz, ne_);
auto d_ea_2 = Reshape(add ? ea_2.ReadWrite() : ea_2.Write(), sz, ne_);
mfem::forall(sz*ne_, [=] MFEM_HOST_DEVICE (int idx)
{
const int i = idx % sz;
const int e = idx / sz;
const real_t val = d_m[d_a[e] - 1] ? d_ea_1(i, e) : 0.0;
if (add)
{
d_ea_2(i, e) += val;
}
else
{
d_ea_2(i, e) = val;
}
});
};
ea_data = 0.0;
}
for (int i = 0; i < integratorCount; ++i)
{
ea_data.SetSize(ne*elemDofs*elemDofs);
ea_data.UseDevice(true);
Array<BilinearFormIntegrator*> &integrators = *a->GetDBFI();
Array<Array<int>*> &markers_array = *a->GetDBFI_Marker();
if (integrators.Size() == 0) { ea_data = 0.0; }
for (int i = 0; i < integrators.Size(); ++i)
{
const bool add = (i > 0);
const Array<int> *markers = markers_array[i];
if (markers == nullptr)
{
integrators[i]->AssembleEA(*a->FESpace(), ea_data, add);
}
else
{
ea_data_tmp.SetSize(ea_data.Size());
integrators[i]->AssembleEA(*a->FESpace(), ea_data_tmp, false);
add_with_markers(ea_data_tmp, ea_data, ne, *markers,
elem_attributes, add);
}
}
integrators[i]->AssembleEA(*a->FESpace(), ea_data, i);
}
faceDofs = trial_fes->GetTypicalTraceElement()->GetDof();
MFEM_VERIFY(a->GetBBFI()->Size() == 0,
"Element assembly does not support AddBoundaryIntegrator yet.");
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int intFaceIntegratorCount = intFaceIntegrators.Size();
if (intFaceIntegratorCount>0)
{
Array<BilinearFormIntegrator*> &bdr_integs = *a->GetBBFI();
Array<Array<int>*> &markers_array = *a->GetBBFI_Marker();
const int n_bdr_integs = bdr_integs.Size();
if (n_bdr_integs > 0)
{
nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs);
}
for (int i = 0; i < n_bdr_integs; ++i)
{
const bool add = (i > 0);
const Array<int> *markers = markers_array[i];
if (markers == nullptr)
{
bdr_integs[i]->AssembleEABoundary(*a->FESpace(), ea_data_bdr, add);
}
else
{
ea_data_tmp.SetSize(ea_data_bdr.Size());
bdr_integs[i]->AssembleEABoundary(*a->FESpace(), ea_data_tmp, add);
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
bdr_attributes, add);
}
}
nf_int = trial_fes->GetNFbyType(FaceType::Interior);
ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs, Device::GetMemoryType());
}
for (int i = 0; i < intFaceIntegratorCount; ++i)
{
intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(),
ea_data_int,
ea_data_ext,
i);
}
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int boundFaceIntegratorCount = bdrFaceIntegrators.Size();
if (boundFaceIntegratorCount>0)
{
Array<BilinearFormIntegrator*> &intFaceIntegrators = *a->GetFBFI();
const int intFaceIntegratorCount = intFaceIntegrators.Size();
if (intFaceIntegratorCount>0)
{
nf_int = trial_fes->GetNFbyType(FaceType::Interior);
ea_data_int.SetSize(2*nf_int*faceDofs*faceDofs);
ea_data_ext.SetSize(2*nf_int*faceDofs*faceDofs);
}
for (int i = 0; i < intFaceIntegratorCount; ++i)
{
const bool add = (i > 0);
intFaceIntegrators[i]->AssembleEAInteriorFaces(*a->FESpace(),
ea_data_int,
ea_data_ext,
add);
}
nf_bdr = trial_fes->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)
{
Array<BilinearFormIntegrator*> &bdr_face_integs = *a->GetBFBFI();
Array<Array<int>*> &markers_array = *a->GetBFBFI_Marker();
const int n_bdr_face_integs = bdr_face_integs.Size();
if (n_bdr_face_integs > 0)
{
nf_bdr = trial_fes->GetNFbyType(FaceType::Boundary);
ea_data_bdr.SetSize(nf_bdr*faceDofs*faceDofs);
}
for (int i = 0; i < n_bdr_face_integs; ++i)
{
const bool add = (i > 0);
const Array<int> *markers = markers_array[i];
if (markers == nullptr)
{
bdr_face_integs[i]->AssembleEABoundaryFaces(
*a->FESpace(), ea_data_bdr, add);
}
else
{
ea_data_tmp.SetSize(ea_data_bdr.Size());
bdr_face_integs[i]->AssembleEABoundaryFaces(*a->FESpace(),
ea_data_tmp,
add);
add_with_markers(ea_data_tmp, ea_data_bdr, nf_bdr, *markers,
bdr_attributes, add);
}
}
bdrFaceIntegrators[i]->AssembleEABoundaryFaces(*a->FESpace(),ea_data_bdr,i);
}
if (factorize_face_terms && int_face_restrict_lex)
@@ -1107,29 +1021,34 @@ void EABilinearFormExtension::Mult(const Vector &x, Vector &y) const
}
// Treatment of boundary faces
if (!factorize_face_terms && bdr_face_restrict_lex && ea_data_bdr.Size() > 0)
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int bFISz = bdrFaceIntegrators.Size();
if (!factorize_face_terms && bdr_face_restrict_lex && bFISz>0)
{
// Apply the Boundary Face Restriction
bdr_face_restrict_lex->Mult(x, bdr_face_X);
bdr_face_Y = 0.0;
// Apply the boundary face matrices
const int NDOFS = faceDofs;
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
if (bdr_face_X.Size()>0)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
bdr_face_Y = 0.0;
// Apply the boundary face matrices
const int NDOFS = faceDofs;
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
res += A(i, j, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
res += A(i, j, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
}
}
@@ -1230,163 +1149,34 @@ void EABilinearFormExtension::MultTranspose(const Vector &x, Vector &y) const
}
// Treatment of boundary faces
if (!factorize_face_terms && bdr_face_restrict_lex && ea_data_bdr.Size() > 0)
Array<BilinearFormIntegrator*> &bdrFaceIntegrators = *a->GetBFBFI();
const int bFISz = bdrFaceIntegrators.Size();
if (!factorize_face_terms && bdr_face_restrict_lex && bFISz>0)
{
// Apply the Boundary Face Restriction
bdr_face_restrict_lex->Mult(x, bdr_face_X);
bdr_face_Y = 0.0;
// Apply the boundary face matrices transposed
const int NDOFS = faceDofs;
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
if (bdr_face_X.Size()>0)
{
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
bdr_face_Y = 0.0;
// Apply the boundary face matrices transposed
const int NDOFS = faceDofs;
auto X = Reshape(bdr_face_X.Read(), NDOFS, nf_bdr);
auto Y = Reshape(bdr_face_Y.ReadWrite(), NDOFS, nf_bdr);
auto A = Reshape(ea_data_bdr.Read(), NDOFS, NDOFS, nf_bdr);
mfem::forall(nf_bdr*NDOFS, [=] MFEM_HOST_DEVICE (int glob_j)
{
res += A(j, i, f)*X(i, f);
}
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
}
void EABilinearFormExtension::GetElementMatrices(
DenseTensor &element_matrices, ElementDofOrdering ordering, bool add_bdr)
{
// Ensure the EA data is assembled
if (ea_data.Size() == 0) { Assemble(); }
const int ndofs = elemDofs;
element_matrices.SetSize(ndofs, ndofs, ne);
const int N = element_matrices.TotalSize();
const auto d_ea_data = Reshape(ea_data.Read(), ndofs, ndofs, ne);
auto d_element_matrices = Reshape(element_matrices.Write(),
ndofs, ndofs,
ne);
const int *d_dof_map = nullptr;
Array<int> dof_map;
if (ordering == ElementDofOrdering::NATIVE)
{
const TensorBasisElement* tbe =
dynamic_cast<const TensorBasisElement*>(trial_fes->GetFE(0));
if (tbe)
{
// Deep copy to avoid issues with host device (see similar comment in
// HybridizationExtension::ConstructC).
dof_map = tbe->GetDofMap();
d_dof_map = dof_map.Read();
}
}
if (d_dof_map)
{
// Reordering required
mfem::forall(N, [=] MFEM_HOST_DEVICE (int idx)
{
const int e = idx / ndofs / ndofs;
const int i = idx % ndofs;
const int j = (idx / ndofs) % ndofs;
const int ii_s = d_dof_map[i];
const int ii = (ii_s >= 0) ? ii_s : -1 - ii_s;
const int s_i = (ii_s >= 0) ? 1 : -1;
const int jj_s = d_dof_map[j];
const int jj = (jj_s >= 0) ? jj_s : -1 - jj_s;
const int s_j = (jj_s >= 0) ? 1 : -1;
d_element_matrices(ii, jj, e) = s_i*s_j*d_ea_data(j, i, e);
});
}
else
{
// No reordering required
mfem::forall(N, [=] MFEM_HOST_DEVICE (int idx)
{
const int e = idx / ndofs / ndofs;
const int i = idx % ndofs;
const int j = (idx / ndofs) % ndofs;
d_element_matrices(i, j, e) = d_ea_data(j, i, e);
});
}
if (add_bdr && ea_data_bdr.Size() > 0)
{
const int ndof_face = faceDofs;
const auto d_ea_bdr = Reshape(ea_data_bdr.Read(),
ndof_face, ndof_face, nf_bdr);
// Get all the local face maps (mapping from lexicographic face index to
// lexicographic volume index, depending on the local face index).
const Mesh &mesh = *trial_fes->GetMesh();
const int dim = mesh.Dimension();
const int n_faces_per_el = 2*dim; // assuming tensor product
Array<int> face_maps(ndof_face * n_faces_per_el);
for (int lf_i = 0; lf_i < n_faces_per_el; ++lf_i)
{
Array<int> face_map(ndof_face);
trial_fes->GetFE(0)->GetFaceMap(lf_i, face_map);
for (int i = 0; i < ndof_face; ++i)
{
face_maps[i + lf_i*ndof_face] = face_map[i];
}
}
Array<int> face_info(nf_bdr * 2);
{
int fidx = 0;
for (int f = 0; f < mesh.GetNumFaces(); ++f)
{
Mesh::FaceInformation finfo = mesh.GetFaceInformation(f);
if (!finfo.IsBoundary()) { continue; }
face_info[0 + fidx*2] = finfo.element[0].local_face_id;
face_info[1 + fidx*2] = finfo.element[0].index;
fidx++;
}
}
const auto d_face_maps = Reshape(face_maps.Read(), ndof_face, n_faces_per_el);
const auto d_face_info = Reshape(face_info.Read(), 2, nf_bdr);
const bool reorder = (ordering == ElementDofOrdering::NATIVE);
mfem::forall_2D(nf_bdr, ndof_face, ndof_face, [=] MFEM_HOST_DEVICE (int f)
{
const int lf_i = d_face_info(0, f);
const int e = d_face_info(1, f);
// Loop over face indices in "native ordering"
MFEM_FOREACH_THREAD(i_lex_face, x, ndof_face)
{
// Convert from lexicographic face DOF to volume DOF
const int i_lex = d_face_maps(i_lex_face, lf_i);
const int ii_s = d_dof_map[i_lex];
const int ii = (ii_s >= 0) ? ii_s : -1 - ii_s;
const int i = reorder ? ii : i_lex;
const int s_i = (ii_s < 0 && reorder) ? -1 : 1;
MFEM_FOREACH_THREAD(j_lex_face, y, ndof_face)
const int f = glob_j/NDOFS;
const int j = glob_j%NDOFS;
real_t res = 0.0;
for (int i = 0; i < NDOFS; i++)
{
// Convert from lexicographic face DOF to volume DOF
const int j_lex = d_face_maps(j_lex_face, lf_i);
const int jj_s = d_dof_map[j_lex];
const int jj = (jj_s >= 0) ? jj_s : -1 - jj_s;
const int j = reorder ? jj : j_lex;
const int s_j = (jj_s < 0 && reorder) ? -1 : 1;
AtomicAdd(d_element_matrices(i, j, e),
s_i*s_j*d_ea_bdr(i_lex_face, j_lex_face, f));
res += A(j, i, f)*X(i, f);
}
}
});
Y(j, f) += res;
});
// Apply the Boundary Face Restriction transposed
bdr_face_restrict_lex->AddMultTransposeInPlace(bdr_face_Y, y);
}
}
}
-11
View File
@@ -154,17 +154,6 @@ public:
void Assemble() override;
void Mult(const Vector &x, Vector &y) const override;
void MultTranspose(const Vector &x, Vector &y) const override;
/// @brief Populates @a element_matrices with the element matrices.
///
/// The element matrices are converted from row-major (how they are stored in
/// @a ea_data) to column-major format.
///
/// If @a ordering is ElementDofOrdering::NATIVE, then the matrices are
/// reordered from the lexicographic ordering used internally.
void GetElementMatrices(DenseTensor &element_matrices,
ElementDofOrdering ordering,
bool add_bdr);
};
/// Data and methods for fully-assembled bilinear forms
+5 -31
View File
@@ -72,14 +72,6 @@ void BilinearFormIntegrator::AssembleEA(const FiniteElementSpace &fes,
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEABoundary(const FiniteElementSpace &fes,
Vector &emat,
const bool add)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleEABoundary(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
&fes,
Vector &ea_data_int,
@@ -90,16 +82,6 @@ void BilinearFormIntegrator::AssembleEAInteriorFaces(const FiniteElementSpace
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEAInteriorFaces(
const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes,
Vector &emat,
const bool add)
{
MFEM_ABORT("BilinearFormIntegrator::AssembleEAInteriorFaces(...)\n"
" is not implemented for this class.");
}
void BilinearFormIntegrator::AssembleEABoundaryFaces(const FiniteElementSpace
&fes,
Vector &ea_data_bdr,
@@ -2847,18 +2829,17 @@ void VectorDivergenceIntegrator::AssembleElementMatrix2(
DenseMatrix &elmat)
{
dim = trial_fe.GetDim();
sdim = Trans.GetSpaceDim();
int trial_dof = trial_fe.GetDof();
int test_dof = test_fe.GetDof();
real_t c;
dshape.SetSize (trial_dof, dim);
gshape.SetSize (trial_dof, sdim);
Jadj.SetSize (dim, sdim);
divshape.SetSize (sdim*trial_dof);
gshape.SetSize (trial_dof, dim);
Jadj.SetSize (dim);
divshape.SetSize (dim*trial_dof);
shape.SetSize (test_dof);
elmat.SetSize (test_dof, sdim*trial_dof);
elmat.SetSize (test_dof, dim*trial_dof);
const IntegrationRule *ir = GetIntegrationRule(trial_fe, test_fe, Trans);
@@ -2872,15 +2853,13 @@ void VectorDivergenceIntegrator::AssembleElementMatrix2(
trial_fe.CalcDShape (ip, dshape);
test_fe.CalcPhysShape (Trans, shape);
// AdjugateJacobian = / adj(J), if J is square
// \ adj(J^t.J).J^t, otherwise
CalcAdjugate(Trans.Jacobian(), Jadj);
Mult (dshape, Jadj, gshape);
gshape.GradToDiv (divshape);
c = ip.weight;
if (dim != sdim) { c /= Trans.Weight(); }
if (Q)
{
c *= Q -> Eval (Trans, ip);
@@ -2921,11 +2900,6 @@ void DivDivIntegrator::AssembleElementMatrix(
if (ir == NULL)
{
int order = 2 * el.GetOrder() - 2; // <--- OK for RTk
if (el.Space() == FunctionSpace::Uk)
{
order += 2;
}
ir = &IntRules.Get(el.GetGeomType(), order);
}
+7 -39
View File
@@ -124,25 +124,11 @@ public:
/// Assemble diagonal and add it to Vector @a diag.
virtual void AssembleDiagonalMF(Vector &diag);
virtual void AssembleEABoundary(const FiniteElementSpace &fes,
Vector &ea_data_bdr,
const bool add = true);
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext,
const bool add = true);
/// @brief Method defining element assembly for mixed trace integrators.
///
/// This is the element assembly analogue of AssembleFaceMatrix(const
/// FiniteElement&, const FiniteElement&, const FiniteElement&,
/// FaceElementTransformations&, DenseMatrix&).
virtual void AssembleEAInteriorFaces(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes,
Vector &emat,
const bool add = true);
virtual void AssembleEABoundaryFaces(const FiniteElementSpace &fes,
Vector &ea_data_bdr,
const bool add = true);
@@ -397,7 +383,6 @@ public:
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
using BilinearFormIntegrator::AssembleEAInteriorFaces;
void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext,
@@ -509,7 +494,6 @@ public:
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
using BilinearFormIntegrator::AssembleEAInteriorFaces;
void AssembleEAInteriorFaces(const FiniteElementSpace &fes,
Vector &ea_data_int,
Vector &ea_data_ext,
@@ -2366,8 +2350,6 @@ protected:
const FaceGeometricFactors *face_geom; ///< Not owned
int dim, ne, nq, dofs1D, quad1D;
void AssembleEA_(Vector &ea, const bool add);
public:
using ApplyKernelType = void(*)(const int, const Array<real_t>&,
@@ -2408,10 +2390,7 @@ public:
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
virtual void AssembleEABoundary(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
virtual void AssembleDiagonalPA(Vector &diag) override;
void AssembleDiagonalPA(Vector &diag) override;
void AssembleDiagonalMF(Vector &diag) override;
@@ -2935,8 +2914,6 @@ public:
void AddMultPA(const Vector &x, Vector &y) const override;
void AddMultTransposePA(const Vector &x, Vector &y) const override;
void AssembleDiagonalPA(Vector& diag) override;
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
const Coefficient *GetCoefficient() const { return Q; }
};
@@ -2958,7 +2935,7 @@ private:
Vector pa_data;
const DofToQuad *trial_maps, *test_maps; ///< Not owned
const GeometricFactors *geom; ///< Not owned
int dim, sdim, ne, nq;
int dim, ne, nq;
int trial_dofs1D, test_dofs1D, quad1D;
public:
@@ -3004,6 +2981,11 @@ class DivDivIntegrator: public BilinearFormIntegrator
protected:
Coefficient *Q;
using BilinearFormIntegrator::AssemblePA;
void AssemblePA(const FiniteElementSpace &fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AssembleDiagonalPA(Vector& diag) override;
private:
#ifndef MFEM_THREAD_SAFE
Vector divshape, te_divshape;
@@ -3030,13 +3012,6 @@ public:
ElementTransformation &Trans,
DenseMatrix &elmat) override;
using BilinearFormIntegrator::AssemblePA;
void AssemblePA(const FiniteElementSpace &fes) override;
void AddMultPA(const Vector &x, Vector &y) const override;
void AssembleDiagonalPA(Vector& diag) override;
void AssembleEA(const FiniteElementSpace &fes, Vector &emat,
const bool add) override;
const Coefficient *GetCoefficient() const { return Q; }
};
@@ -3336,7 +3311,6 @@ public:
void AddMultPA(const Vector&, Vector&) const override;
using BilinearFormIntegrator::AssembleEAInteriorFaces;
void AssembleEAInteriorFaces(const FiniteElementSpace& fes,
Vector &ea_data_int,
Vector &ea_data_ext,
@@ -3650,12 +3624,6 @@ public:
const FiniteElement &test_fe2,
FaceElementTransformations &Trans,
DenseMatrix &elmat) override;
using BilinearFormIntegrator::AssembleEAInteriorFaces;
void AssembleEAInteriorFaces(const FiniteElementSpace &trial_fes,
const FiniteElementSpace &test_fes,
Vector &emat,
const bool add = true) override;
};
/** Integrator for the DPG form:$ \langle v, w \rangle $ over a face (the interface) where
+2 -7
View File
@@ -240,9 +240,7 @@ public:
Vector argument instead of Vector. */
MFEM_DEPRECATED FunctionCoefficient(real_t (*f)(Vector &))
{
// Cast first to (void*) to suppress a warning from newer version of
// Clang when using -Wextra.
Function = reinterpret_cast<real_t(*)(const Vector&)>((void*)f);
Function = reinterpret_cast<real_t(*)(const Vector&)>(f);
TDFunction = NULL;
}
@@ -252,10 +250,7 @@ public:
MFEM_DEPRECATED FunctionCoefficient(real_t (*tdf)(Vector &, real_t))
{
Function = NULL;
// Cast first to (void*) to suppress a warning from newer version of
// Clang when using -Wextra.
TDFunction =
reinterpret_cast<real_t(*)(const Vector&,real_t)>((void*)tdf);
TDFunction = reinterpret_cast<real_t(*)(const Vector&,real_t)>(tdf);
}
/// Evaluate the coefficient at @a ip.
+4 -4
View File
@@ -487,7 +487,7 @@ SesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
if (RealInteg() && ImagInteg())
{
// Modify RHS and off-diagonal blocks (imaginary parts of the matrix) to
// Modify RHS and offdiagonal blocks (imaginary parts of the matrix) to
// conform with standard essential BC treatment
if (A_i.Is<ConstrainedOperator>())
{
@@ -576,7 +576,7 @@ SesquilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
if (RealInteg() && ImagInteg())
{
// Modify off-diagonal blocks (imaginary parts of the matrix) to conform
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
// with standard essential BC treatment
if (A_i.Is<ConstrainedOperator>())
{
@@ -1236,7 +1236,7 @@ ParSesquilinearForm::FormLinearSystem(const Array<int> &ess_tdof_list,
d_B_r[j] = d_X_r[j];
d_B_i[j] = d_X_i[j];
});
// Modify off-diagonal blocks (imaginary parts of the matrix) to conform
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
// with standard essential BC treatment
if (A_i.Type() == Operator::Hypre_ParCSR)
{
@@ -1324,7 +1324,7 @@ ParSesquilinearForm::FormSystemMatrix(const Array<int> &ess_tdof_list,
if (RealInteg() && ImagInteg())
{
// Modify off-diagonal blocks (imaginary parts of the matrix) to conform
// Modify offdiagonal blocks (imaginary parts of the matrix) to conform
// with standard essential BC treatment
if ( A_i.Type() == Operator::Hypre_ParCSR )
{
+52 -118
View File
@@ -12,7 +12,6 @@
#include "fem.hpp"
#include "../mesh/nurbs.hpp"
#include "../mesh/vtk.hpp"
#include "../mesh/vtkhdf.hpp"
#include "../general/binaryio.hpp"
#include "../general/text.hpp"
#include "picojson.h"
@@ -759,10 +758,18 @@ void VisItDataCollection::ParseVisItRootString(const std::string& json)
}
}
ParaViewDataCollectionBase::ParaViewDataCollectionBase(
const std::string &name, Mesh *mesh) : DataCollection(name, mesh)
ParaViewDataCollection::ParaViewDataCollection(const std::string&
collection_name,
Mesh *mesh_)
: DataCollection(collection_name, mesh_),
levels_of_detail(1),
pv_data_format(VTKFormat::BINARY),
high_order_output(false),
restart_mode(false)
{
cycle = 0;
cycle = 0; // always include a valid cycle index in file names
compression_level = -1; // default zlib compression level, equivalent to 6
#ifdef MFEM_USE_ZLIB
compression = true; // if we have zlib, enable compression
#else
@@ -770,53 +777,16 @@ ParaViewDataCollectionBase::ParaViewDataCollectionBase(
#endif
}
void ParaViewDataCollectionBase::SetLevelsOfDetail(int levels_of_detail_)
void ParaViewDataCollection::SetLevelsOfDetail(int levels_of_detail_)
{
levels_of_detail = levels_of_detail_;
}
void ParaViewDataCollectionBase::SetHighOrderOutput(bool high_order_output_)
void ParaViewDataCollection::Load(int )
{
high_order_output = high_order_output_;
MFEM_WARNING("ParaViewDataCollection::Load() is not implemented!");
}
void ParaViewDataCollectionBase::SetCompressionLevel(int compression_level_)
{
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
"Compression level must be between -1 and 9 (inclusive).");
compression_level = compression_level_;
compression = compression_level_ != 0;
}
void ParaViewDataCollectionBase::SetCompression(bool compression_)
{
compression = compression_;
}
int ParaViewDataCollectionBase::GetCompressionLevel() const
{
return compression ? compression_level : 0;
}
void ParaViewDataCollectionBase::SetDataFormat(VTKFormat fmt)
{
pv_data_format = fmt;
}
bool ParaViewDataCollectionBase::IsBinaryFormat() const
{
return pv_data_format != VTKFormat::ASCII;
}
void ParaViewDataCollectionBase::UseRestartMode(bool restart_mode_)
{
restart_mode = restart_mode_;
}
ParaViewDataCollection::ParaViewDataCollection(
const std::string& collection_name, Mesh *mesh_)
: ParaViewDataCollectionBase(collection_name, mesh_) { }
std::string ParaViewDataCollection::GenerateCollectionPath()
{
return prefix_path + DataCollection::GetCollectionName();
@@ -931,7 +901,7 @@ void ParaViewDataCollection::Save()
// Initialize new pvd file.
pvd_stream.open(pvdname,std::ios::out|std::ios::trunc);
pvd_stream << "<?xml version=\"1.0\"?>\n";
pvd_stream << "<VTKFile type=\"Collection\" version=\"2.2\"";
pvd_stream << "<VTKFile type=\"Collection\" version=\"0.1\"";
pvd_stream << " byte_order=\"" << VTKByteOrder() << "\">\n";
pvd_stream << "<Collection>" << std::endl;
}
@@ -1031,7 +1001,7 @@ void ParaViewDataCollection::WritePVTUHeader(std::ostream &os)
{
os << "<?xml version=\"1.0\"?>\n";
os << "<VTKFile type=\"PUnstructuredGrid\"";
os << " version =\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
os << " version =\"0.1\" byte_order=\"" << VTKByteOrder() << "\">\n";
os << "<PUnstructuredGrid GhostLevel=\"0\">\n";
os << "<PPoints>\n";
@@ -1072,7 +1042,7 @@ void ParaViewDataCollection::SaveDataVTU(std::ostream &os, int ref)
{
os << " compressor=\"vtkZLibDataCompressor\"";
}
os << " version=\"2.2\" byte_order=\"" << VTKByteOrder() << "\">\n";
os << " version=\"0.1\" byte_order=\"" << VTKByteOrder() << "\">\n";
os << "<UnstructuredGrid>\n";
mesh->PrintVTU(os,ref,pv_data_format,high_order_output,GetCompressionLevel());
@@ -1145,6 +1115,39 @@ void ParaViewDataCollection::SaveGFieldVTU(std::ostream &os, int ref_,
os << "</DataArray>" << std::endl;
}
void ParaViewDataCollection::SetDataFormat(VTKFormat fmt)
{
pv_data_format = fmt;
}
bool ParaViewDataCollection::IsBinaryFormat() const
{
return pv_data_format != VTKFormat::ASCII;
}
void ParaViewDataCollection::SetHighOrderOutput(bool high_order_output_)
{
high_order_output = high_order_output_;
}
void ParaViewDataCollection::SetCompressionLevel(int compression_level_)
{
MFEM_ASSERT(compression_level_ >= -1 && compression_level_ <= 9,
"Compression level must be between -1 and 9 (inclusive).");
compression_level = compression_level_;
compression = compression_level_ != 0;
}
void ParaViewDataCollection::SetCompression(bool compression_)
{
compression = compression_;
}
void ParaViewDataCollection::UseRestartMode(bool restart_mode_)
{
restart_mode = restart_mode_;
}
const char *ParaViewDataCollection::GetDataFormatString() const
{
if (pv_data_format == VTKFormat::ASCII)
@@ -1169,78 +1172,9 @@ const char *ParaViewDataCollection::GetDataTypeString() const
}
}
#ifdef MFEM_USE_HDF5
ParaViewHDFDataCollection::ParaViewHDFDataCollection(
const std::string &collection_name, Mesh *mesh)
: ParaViewDataCollectionBase(collection_name, mesh)
{ }
void ParaViewHDFDataCollection::EnsureVTKHDF()
int ParaViewDataCollection::GetCompressionLevel() const
{
if (!vtkhdf)
{
if (!prefix_path.empty())
{
const int error_code = create_directory(prefix_path, mesh, myid);
MFEM_VERIFY(error_code == 0, "Error creating directory " << prefix_path);
}
std::string fname = prefix_path + name + ".vtkhdf";
bool use_mpi = false;
#ifdef MFEM_USE_MPI
if (ParMesh *pmesh = dynamic_cast<ParMesh*>(mesh))
{
use_mpi = true;
#ifdef MFEM_PARALLEL_HDF5
vtkhdf.reset(new VTKHDF(fname, pmesh->GetComm(), {restart_mode, time}));
#else
MFEM_ABORT("Requires HDF5 library with parallel support enabled");
#endif
}
#endif
if (!use_mpi)
{
vtkhdf.reset(new VTKHDF(fname, {restart_mode, time}));
}
}
return compression ? compression_level : 0;
}
template <typename FP_T>
void ParaViewHDFDataCollection::TSave()
{
EnsureVTKHDF();
if (compression)
{
vtkhdf->EnableCompression(compression_level >= 0 ? compression_level : 6);
}
else
{
vtkhdf->DisableCompression();
}
vtkhdf->SaveMesh<FP_T>(*mesh, high_order_output, levels_of_detail);
for (const auto &field : field_map)
{
vtkhdf->SaveGridFunction<FP_T>(*field.second, field.first);
}
vtkhdf->UpdateSteps(time);
vtkhdf->Flush();
}
void ParaViewHDFDataCollection::Save()
{
switch (pv_data_format)
{
case VTKFormat::BINARY32: TSave<float>(); break;
case VTKFormat::BINARY: TSave<double>(); break;
default: MFEM_ABORT("Unsupported VTK format.");
}
}
ParaViewHDFDataCollection::~ParaViewHDFDataCollection() = default;
#endif
} // end namespace MFEM
+63 -112
View File
@@ -502,27 +502,60 @@ public:
};
/// Abstract base class for ParaViewDataCollection and ParaViewHDFDataCollection
class ParaViewDataCollectionBase : public DataCollection
/// Helper class for ParaView visualization data
class ParaViewDataCollection : public DataCollection
{
protected:
int levels_of_detail = 1;
int compression_level = -1;
bool high_order_output = false;
bool restart_mode = false;
VTKFormat pv_data_format = VTKFormat::BINARY;
public:
ParaViewDataCollectionBase(const std::string &name, Mesh *mesh);
private:
int levels_of_detail;
int compression_level;
std::fstream pvd_stream;
VTKFormat pv_data_format;
bool high_order_output;
bool restart_mode;
/// @brief Set the refinement level.
///
/// In "low-order mode", every element is uniformly split based on the levels
/// of detail. In "high-order mode", this sets the polynomial degree for the
/// element transformations.
///
/// The initial value is 1.
protected:
void WritePVTUHeader(std::ostream &out);
void WritePVTUFooter(std::ostream &out, const std::string &vtu_prefix);
void SaveDataVTU(std::ostream &out, int ref);
void SaveGFieldVTU(std::ostream& out, int ref_, const FieldMapIterator& it);
const char *GetDataFormatString() const;
const char *GetDataTypeString() const;
/// @brief If compression is enabled, return the compression level, otherwise
/// return 0.
int GetCompressionLevel() const;
std::string GenerateCollectionPath();
std::string GenerateVTUFileName(const std::string &prefix, int rank);
std::string GenerateVTUPath();
std::string GeneratePVDFileName();
std::string GeneratePVTUFileName(const std::string &prefix);
std::string GeneratePVTUPath();
public:
/// Constructor. The collection name is used when saving the data.
/** If @a mesh_ is NULL, then the mesh can be set later by calling SetMesh().
Before saving the data collection, some parameters in the collection can
be adjusted, e.g. SetPadDigits(), SetPrefixPath(), etc. */
ParaViewDataCollection(const std::string& collection_name,
mfem::Mesh *mesh_ = NULL);
/// Set refinement levels - every element is uniformly split based on
/// levels_of_detail_. The initial value is 1.
void SetLevelsOfDetail(int levels_of_detail_);
/// Save the collection - the directory name is constructed based on the
/// cycle value
void Save() override;
/// Set the data format for the ParaView output files. Possible options are
/// VTKFormat::ASCII, VTKFormat::BINARY, and VTKFormat::BINARY32.
/// The ASCII and BINARY options output double precision data, whereas the
/// BINARY32 option outputs single precision data.
///
/// The initial format is VTKFormat::BINARY.
void SetDataFormat(VTKFormat fmt);
/// @brief Set the zlib compression level.
///
/// 0 indicates no compression, -1 indicates the default compression level.
@@ -537,110 +570,28 @@ public:
/// Any nonzero compression level will enable compression.
void SetCompressionLevel(int compression_level_);
/// @brief Enable or disable zlib compression.
///
/// If the input is true, use the default zlib compression level (unless the
/// compression level has previously been set by calling
/// SetCompressionLevel()).
/// Enable or disable zlib compression. If the input is true, use the default
/// zlib compression level (unless the compression level has previously been
/// set by calling SetCompressionLevel()).
void SetCompression(bool compression_) override;
/// @brief Sets whether or not to output the data as high-order elements
/// (false by default).
///
/// Reading high-order data requires ParaView 5.5 or later.
void SetHighOrderOutput(bool high_order_output_);
/// If compression is enabled, return the compression level, else return 0.
int GetCompressionLevel() const;
/// @brief Set the data format for the ParaView output files.
///
/// Possible options are VTKFormat::ASCII, VTKFormat::BINARY, and
/// VTKFormat::BINARY32. The ASCII and BINARY options output double precision
/// data, whereas the BINARY32 option outputs single precision data.
///
/// The initial format is VTKFormat::BINARY.
///
/// VTKFormat::ASCII is not supported by ParaViewHDFDataCollection.
void SetDataFormat(VTKFormat fmt);
/// Returns true if the output format is BINARY or BINARY32, false if ASCII.
bool IsBinaryFormat() const;
/// @brief Enable or disable restart mode.
///
/// If restart is enabled, new writes will preserve timestep metadata for any
/// solutions prior to the currently defined time.
/// Sets whether or not to output the data as high-order elements (false
/// by default). Reading high-order data requires ParaView 5.5 or later.
void SetHighOrderOutput(bool high_order_output_);
/// Enable or disable restart mode. If restart is enabled, new writes will
/// preserve timestep metadata for any solutions prior to the currently
/// defined time.
///
/// Initially, restart mode is disabled.
void UseRestartMode(bool restart_mode_);
/// Load the collection - not implemented in the ParaView writer
void Load(int cycle_ = 0) override;
};
/// Writer for ParaView visualization (PVD and VTU format)
class ParaViewDataCollection : public ParaViewDataCollectionBase
{
private:
std::fstream pvd_stream;
protected:
void WritePVTUHeader(std::ostream &out);
void WritePVTUFooter(std::ostream &out, const std::string &vtu_prefix);
void SaveDataVTU(std::ostream &out, int ref);
void SaveGFieldVTU(std::ostream& out, int ref_, const FieldMapIterator& it);
const char *GetDataFormatString() const;
const char *GetDataTypeString() const;
std::string GenerateCollectionPath();
std::string GenerateVTUFileName(const std::string &prefix, int rank);
std::string GenerateVTUPath();
std::string GeneratePVDFileName();
std::string GeneratePVTUFileName(const std::string &prefix);
std::string GeneratePVTUPath();
public:
/// Constructor. The collection name is used when saving the data.
/** If @a mesh_ is NULL, then the mesh can be set later by calling SetMesh().
Before saving the data collection, some parameters in the collection can
be adjusted, e.g. SetPadDigits(), SetPrefixPath(), etc. */
ParaViewDataCollection(const std::string& collection_name,
Mesh *mesh_ = nullptr);
/// Save the collection - the directory name is constructed based on the
/// cycle value
void Save() override;
};
#ifdef MFEM_USE_HDF5
/// Writer for ParaView visualization (%VTKHDF format)
class ParaViewHDFDataCollection : public ParaViewDataCollectionBase
{
/// The low-level VTKHDF object for I/O (pointer to implementation idiom).
std::unique_ptr<class VTKHDF> vtkhdf;
/// Create the VTKHDF object if it doesn't exist already.
void EnsureVTKHDF();
/// Save the collection (templated on floating point type).
template <typename FP_T> void TSave();
public:
/// @brief Constructor. The collection name is used when saving the data.
///
/// If @a mesh_ is NULL, then the mesh can be set later by calling SetMesh().
/// Before saving the data collection, some parameters in the collection can
/// be adjusted, e.g. SetPadDigits(), SetPrefixPath(), etc.
ParaViewHDFDataCollection(const std::string& collection_name,
Mesh *mesh_ = nullptr);
/// Save the collection.
void Save() override;
/// Destructor.
~ParaViewHDFDataCollection();
};
#endif
}
#endif
+52 -65
View File
@@ -17,10 +17,7 @@
namespace mfem
{
struct DGMassInvKernels { DGMassInvKernels(); };
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
Coefficient *coeff,
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_orig, Coefficient *coeff,
const IntegrationRule *ir,
int btype)
: Solver(fes_orig.GetTrueVSize()),
@@ -30,8 +27,6 @@ DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
fes_orig.GetTypicalFE()->GetMapType()),
fes(fes_orig.GetMesh(), &fec)
{
static DGMassInvKernels kernels;
MFEM_VERIFY(fes.IsDGSpace(), "Space must be DG.");
MFEM_VERIFY(!fes.IsVariableOrder(), "Variable orders not supported.");
@@ -51,7 +46,7 @@ DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
const FiniteElement &fe = *fes.GetTypicalFE();
d2q = &fe_orig.GetDofToQuad(fe.GetNodes(), mode);
const int n = d2q->ndof;
int n = d2q->ndof;
Array<real_t> B_inv = d2q->B; // deep copy
Array<int> ipiv(n);
// solver basis to original
@@ -76,7 +71,7 @@ DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
// Only need transformed RHS if basis is different
if (btype_orig != btype) { b2_.SetSize(height); }
M.reset(new BilinearForm(&fes));
M = new BilinearForm(&fes);
M->AddDomainIntegrator(m); // M assumes ownership of m
M->SetAssemblyLevel(AssemblyLevel::PARTIAL);
@@ -84,19 +79,19 @@ DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_orig,
Update();
}
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
int btype)
: DGMassInverse(fes_, &coeff, nullptr, btype) { }
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
const IntegrationRule &ir, int btype)
: DGMassInverse(fes_, &coeff, &ir, btype) { }
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_,
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_,
const IntegrationRule &ir, int btype)
: DGMassInverse(fes_, nullptr, &ir, btype) { }
DGMassInverse::DGMassInverse(const FiniteElementSpace &fes_, int btype)
DGMassInverse::DGMassInverse(FiniteElementSpace &fes_, int btype)
: DGMassInverse(fes_, nullptr, nullptr, btype) { }
void DGMassInverse::SetOperator(const Operator &op)
@@ -117,7 +112,10 @@ void DGMassInverse::Update()
diag_inv.Reciprocal();
}
DGMassInverse::~DGMassInverse() = default;
DGMassInverse::~DGMassInverse()
{
delete M;
}
template<int DIM, int D1D, int Q1D>
void DGMassInverse::DGMassCGIteration(const Vector &b_, Vector &u_) const
@@ -271,58 +269,47 @@ void DGMassInverse::Mult(const Vector &Mu, Vector &u) const
const int d1d = m->dofs1D;
const int q1d = m->quad1D;
CGKernels::Run(dim, d1d, q1d, *this, Mu, u);
const int id = (d1d << 4) | q1d;
if (dim == 2)
{
switch (id)
{
case 0x11: return DGMassCGIteration<2,1,1>(Mu, u);
case 0x22: return DGMassCGIteration<2,2,2>(Mu, u);
case 0x33: return DGMassCGIteration<2,3,3>(Mu, u);
case 0x35: return DGMassCGIteration<2,3,5>(Mu, u);
case 0x44: return DGMassCGIteration<2,4,4>(Mu, u);
case 0x46: return DGMassCGIteration<2,4,6>(Mu, u);
case 0x55: return DGMassCGIteration<2,5,5>(Mu, u);
case 0x57: return DGMassCGIteration<2,5,7>(Mu, u);
case 0x66: return DGMassCGIteration<2,6,6>(Mu, u);
case 0x68: return DGMassCGIteration<2,6,8>(Mu, u);
default: return DGMassCGIteration<2>(Mu, u); // Fallback
}
}
else if (dim == 3)
{
switch (id)
{
case 0x22: return DGMassCGIteration<3,2,2>(Mu, u);
case 0x23: return DGMassCGIteration<3,2,3>(Mu, u);
case 0x33: return DGMassCGIteration<3,3,3>(Mu, u);
case 0x34: return DGMassCGIteration<3,3,4>(Mu, u);
case 0x35: return DGMassCGIteration<3,3,5>(Mu, u);
case 0x44: return DGMassCGIteration<3,4,4>(Mu, u);
case 0x45: return DGMassCGIteration<3,4,5>(Mu, u);
case 0x46: return DGMassCGIteration<3,4,6>(Mu, u);
case 0x48: return DGMassCGIteration<3,4,8>(Mu, u);
case 0x55: return DGMassCGIteration<3,5,5>(Mu, u);
case 0x56: return DGMassCGIteration<3,5,6>(Mu, u);
case 0x57: return DGMassCGIteration<3,5,7>(Mu, u);
case 0x58: return DGMassCGIteration<3,5,8>(Mu, u);
case 0x66: return DGMassCGIteration<3,6,6>(Mu, u);
case 0x67: return DGMassCGIteration<3,6,7>(Mu, u);
default: return DGMassCGIteration<3>(Mu, u); // Fallback
}
}
}
DGMassInvKernels::DGMassInvKernels()
{
using k = DGMassInverse::CGKernels;
// 2D
k::Specialization<2,1,1>::Add();
k::Specialization<2,2,2>::Add();
k::Specialization<2,3,3>::Add();
k::Specialization<2,3,5>::Add();
k::Specialization<2,4,4>::Add();
k::Specialization<2,4,6>::Add();
k::Specialization<2,5,5>::Add();
k::Specialization<2,5,7>::Add();
k::Specialization<2,6,6>::Add();
k::Specialization<2,6,8>::Add();
// 3D
k::Specialization<3,2,2>::Add();
k::Specialization<3,2,3>::Add();
k::Specialization<3,3,3>::Add();
k::Specialization<3,3,4>::Add();
k::Specialization<3,3,5>::Add();
k::Specialization<3,4,4>::Add();
k::Specialization<3,4,5>::Add();
k::Specialization<3,4,6>::Add();
k::Specialization<3,4,8>::Add();
k::Specialization<3,5,5>::Add();
k::Specialization<3,5,6>::Add();
k::Specialization<3,5,7>::Add();
k::Specialization<3,5,8>::Add();
k::Specialization<3,6,6>::Add();
k::Specialization<3,6,7>::Add();
}
/// @cond Suppress_Doxygen_warnings
template <int DIM, int D1D, int Q1D>
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Kernel()
{
return &DGMassInverse::DGMassCGIteration<DIM,D1D,Q1D>;
}
DGMassInverse::CGKernelType DGMassInverse::CGKernels::Fallback(
int dim, int, int)
{
if (dim == 1) { return &DGMassInverse::DGMassCGIteration<1>; }
else if (dim == 2) { return &DGMassInverse::DGMassCGIteration<2>; }
else if (dim == 3) { return &DGMassInverse::DGMassCGIteration<3>; }
else { MFEM_ABORT("Unsupported dimension."); }
}
/// @endcond
} // namespace mfem
+9 -15
View File
@@ -14,8 +14,6 @@
#include "../linalg/operator.hpp"
#include "fespace.hpp"
#include "kernel_dispatch.hpp"
#include <memory>
namespace mfem
{
@@ -34,7 +32,7 @@ protected:
const DofToQuad *d2q; ///< Change of basis. Not owned.
Array<real_t> B_; ///< Inverse of change of basis.
Array<real_t> Bt_; ///< Inverse of change of basis, transposed.
std::unique_ptr<class BilinearForm> M; ///< Mass bilinear form.
class BilinearForm *M; ///< Mass bilinear form, owned.
class MassIntegrator *m; ///< Mass integrator, owned by the form @ref M.
Vector diag_inv; ///< Jacobi preconditioner.
real_t rel_tol = 1e-12; ///< Relative CG tolerance.
@@ -50,7 +48,7 @@ protected:
///
/// Custom coefficient and integration rule are used if @a coeff and @a ir
/// are non-NULL.
DGMassInverse(const FiniteElementSpace &fes_, Coefficient *coeff,
DGMassInverse(FiniteElementSpace &fes_, Coefficient *coeff,
const IntegrationRule *ir, int btype);
public:
/// @brief Construct the DG inverse mass operator for @a fes_.
@@ -63,37 +61,36 @@ public:
/// The solution and right-hand side used for the solver are not affected by
/// this basis (they correspond to the basis of @a fes_). @a btype is only
/// used internally, and only has an effect on the convergence rate.
DGMassInverse(const FiniteElementSpace &fes_,
int btype=BasisType::GaussLegendre);
DGMassInverse(FiniteElementSpace &fes_, int btype=BasisType::GaussLegendre);
/// @brief Construct the DG inverse mass operator for @a fes_ with
/// Coefficient @a coeff.
///
/// @sa DGMassInverse(FiniteElementSpace&, int) for information about @a
/// btype.
DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
int btype=BasisType::GaussLegendre);
/// @brief Construct the DG inverse mass operator for @a fes_ with
/// Coefficient @a coeff and IntegrationRule @a ir.
///
/// @sa DGMassInverse(FiniteElementSpace&, int) for information about @a
/// btype.
DGMassInverse(const FiniteElementSpace &fes_, Coefficient &coeff,
DGMassInverse(FiniteElementSpace &fes_, Coefficient &coeff,
const IntegrationRule &ir, int btype=BasisType::GaussLegendre);
/// @brief Construct the DG inverse mass operator for @a fes_ with
/// IntegrationRule @a ir.
///
/// @sa DGMassInverse(FiniteElementSpace&, int) for information about @a
/// btype.
DGMassInverse(const FiniteElementSpace &fes_, const IntegrationRule &ir,
DGMassInverse(FiniteElementSpace &fes_, const IntegrationRule &ir,
int btype=BasisType::GaussLegendre);
/// @brief Solve the system M b = u.
///
/// If @ref iterative_mode is @a true, @a u is used as an initial guess.
void Mult(const Vector &b, Vector &u) const override;
void Mult(const Vector &b, Vector &u) const;
/// Same as Mult() since the mass matrix is symmetric.
void MultTranspose(const Vector &b, Vector &u) const override { Mult(b, u); }
void MultTranspose(const Vector &b, Vector &u) const { Mult(b, u); }
/// Not implemented. Aborts.
void SetOperator(const Operator &op) override;
void SetOperator(const Operator &op);
/// Set the relative tolerance.
void SetRelTol(const real_t rel_tol_);
/// Set the absolute tolerance.
@@ -110,9 +107,6 @@ public:
/// extended lambda used in an mfem::forall kernel (nvcc limitation)
template<int DIM, int D1D = 0, int Q1D = 0>
void DGMassCGIteration(const Vector &b_, Vector &u_) const;
using CGKernelType = void(DGMassInverse::*)(const Vector &b_, Vector &u) const;
MFEM_REGISTER_KERNELS(CGKernels, CGKernelType, (int, int, int));
};
} // namespace mfem
+1 -49
View File
@@ -37,13 +37,6 @@ void DGMassApply(const int e,
constexpr bool use_smem = (D1D > 0 && Q1D > 0);
constexpr bool ACCUM = false;
constexpr int NBZ = 1;
if (DIM == 1)
{
PAMassApply1D_Element<ACCUM>(e, NE, B, Bt, pa_data, x, y, d1d, q1d);
return;
}
if (use_smem)
{
// cannot specialize functions below with D1D or Q1D equal to zero
@@ -179,43 +172,6 @@ real_t DGMassDot(const int e,
return s_dot[0];
}
template<int T_D1D = 0>
MFEM_HOST_DEVICE inline
void DGMassBasis1D(const int e,
const int NE,
const real_t *b_,
const real_t *x_,
real_t *y_,
const int d1d = 0)
{
const int D1D = T_D1D ? T_D1D : d1d;
const auto b = Reshape(b_, D1D, D1D);
const auto x = Reshape(x_, D1D, NE);
auto y = Reshape(y_, D1D, NE);
constexpr int MD1 = T_D1D ? T_D1D : DofQuadLimits::MAX_D1D;
real_t Y[MD1];
MFEM_FOREACH_THREAD(i,x,D1D)
{
real_t val = 0.0;
for (int j = 0; j < D1D; ++j)
{
val += b(i,j)*x(j,e);
}
Y[i] = val;
}
MFEM_SYNC_THREAD;
if (MFEM_THREAD_ID(y) == 0)
{
MFEM_FOREACH_THREAD(i,x,D1D)
{
y(i,e) = Y[i];
}
}
}
template<int T_D1D = 0>
MFEM_HOST_DEVICE inline
void DGMassBasis2D(const int e,
@@ -313,11 +269,7 @@ void DGMassBasis(const int e,
real_t *y_,
const int d1d = 0)
{
if (DIM == 1)
{
DGMassBasis1D<T_D1D>(e, NE, b_, x_, y_, d1d);
}
else if (DIM == 2)
if (DIM == 2)
{
DGMassBasis2D<T_D1D>(e, NE, b_, x_, y_, d1d);
}
+19 -59
View File
@@ -201,16 +201,13 @@ const DenseTensor ND_DofTransformation
::TInv(const_cast<real_t *>(TInv_data), 2, 2, 6);
ND_DofTransformation::ND_DofTransformation(int size, int p, int num_edges,
int num_faces,
int face_types[])
int num_tri_faces)
: StatelessDofTransformation(size)
, order(p)
, nedofs(p)
, ntdofs(p*(p-1))
, nqdofs(2*p*(p-1))
, nfdofs(p*(p-1))
, nedges(num_edges)
, nfaces(num_faces)
, ftypes(face_types)
, nfaces(num_tri_faces)
{
}
@@ -224,7 +221,6 @@ void ND_DofTransformation::TransformPrimal(const Array<int> & Fo,
"Face orientation array is shorter than the number of faces in "
"ND_DofTransformation");
int of = 0;
real_t data[2];
Vector v2(data, 2);
DenseMatrix T2;
@@ -232,19 +228,11 @@ void ND_DofTransformation::TransformPrimal(const Array<int> & Fo,
// Transform face DoFs
for (int f=0; f<nfaces; f++)
{
if (ftypes[f] == Geometry::TRIANGLE)
for (int i=0; i<nfdofs/2; i++)
{
for (int i=0; i<ntdofs/2; i++)
{
v2 = &v[nedges*nedofs + of + 2*i];
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
T2.Mult(v2, &v[nedges*nedofs + of + 2*i]);
}
of += ntdofs;
}
else
{
of += nqdofs;
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
T2.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
}
}
}
@@ -259,7 +247,6 @@ void ND_DofTransformation::InvTransformPrimal(const Array<int> & Fo,
"Face orientation array is shorter than the number of faces in "
"ND_DofTransformation");
int of = 0;
real_t data[2];
Vector v2(data, 2);
DenseMatrix T2Inv;
@@ -267,19 +254,11 @@ void ND_DofTransformation::InvTransformPrimal(const Array<int> & Fo,
// Transform face DoFs
for (int f=0; f<nfaces; f++)
{
if (ftypes[f] == Geometry::TRIANGLE)
for (int i=0; i<nfdofs/2; i++)
{
for (int i=0; i<ntdofs/2; i++)
{
v2 = &v[nedges*nedofs + of + 2*i];
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.Mult(v2, &v[nedges*nedofs + of + 2*i]);
}
of += ntdofs;
}
else
{
of += nqdofs;
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.Mult(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
}
}
}
@@ -293,7 +272,6 @@ void ND_DofTransformation::TransformDual(const Array<int> & Fo, real_t *v) const
"Face orientation array is shorter than the number of faces in "
"ND_DofTransformation");
int of = 0;
real_t data[2];
Vector v2(data, 2);
DenseMatrix T2Inv;
@@ -301,21 +279,12 @@ void ND_DofTransformation::TransformDual(const Array<int> & Fo, real_t *v) const
// Transform face DoFs
for (int f=0; f<nfaces; f++)
{
if (ftypes[f] == Geometry::TRIANGLE)
for (int i=0; i<nfdofs/2; i++)
{
for (int i=0; i<ntdofs/2; i++)
{
v2 = &v[nedges*nedofs + of + 2*i];
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.MultTranspose(v2, &v[nedges*nedofs + of + 2*i]);
}
of += ntdofs;
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T2Inv.UseExternalData(const_cast<real_t *>(TInv.GetData(Fo[f])), 2, 2);
T2Inv.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
}
else
{
of += nqdofs;
}
}
}
@@ -329,7 +298,6 @@ void ND_DofTransformation::InvTransformDual(const Array<int> & Fo,
"Face orientation array is shorter than the number of faces in "
"ND_DofTransformation");
int of = 0;
real_t data[2];
Vector v2(data, 2);
DenseMatrix T2;
@@ -337,19 +305,11 @@ void ND_DofTransformation::InvTransformDual(const Array<int> & Fo,
// Transform face DoFs
for (int f=0; f<nfaces; f++)
{
if (ftypes[f] == Geometry::TRIANGLE)
for (int i=0; i<nfdofs/2; i++)
{
for (int i=0; i<ntdofs/2; i++)
{
v2 = &v[nedges*nedofs + of + 2*i];
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
T2.MultTranspose(v2, &v[nedges*nedofs + of + 2*i]);
}
of += ntdofs;
}
else
{
of += nqdofs;
v2 = &v[nedges*nedofs + f*nfdofs + 2*i];
T2.UseExternalData(const_cast<real_t *>(T.GetData(Fo[f])), 2, 2);
T2.MultTranspose(v2, &v[nedges*nedofs + f*nfdofs + 2*i]);
}
}
}
+10 -31
View File
@@ -306,16 +306,13 @@ private:
static const DenseTensor T, TInv;
protected:
const int order; // basis function order
const int nedofs; // number of DoFs per edge
const int ntdofs; // number of DoFs per triangular face
const int nqdofs; // number of DoFs per quadrilateral face
const int nedges; // number of edges per element
const int nfaces; // number of faces per element
const int *ftypes; // Pointer to array of Geometry::Type for each face
const int order; // basis function order
const int nedofs; // number of DoFs per edge
const int nfdofs; // number of DoFs per face
const int nedges; // number of edges per element
const int nfaces; // number of triangular faces per element
ND_DofTransformation(int size, int order, int num_edges, int num_faces,
int *face_types);
ND_DofTransformation(int size, int order, int num_edges, int num_tri_faces);
public:
// Return the 2x2 transformation operator for the given face orientation
@@ -325,7 +322,7 @@ public:
static const DenseMatrix & GetFaceInverseTransform(int ori)
{ return TInv(ori); }
bool IsIdentity() const override { return ntdofs < 2; }
bool IsIdentity() const override { return nfdofs < 2; }
void TransformPrimal(const Array<int> & Fo, real_t *v) const override;
void InvTransformPrimal(const Array<int> & Fo, real_t *v) const override;
@@ -337,11 +334,9 @@ public:
/// triangles
class ND_TriDofTransformation : public ND_DofTransformation
{
private:
const int face_type[1] = { Geometry::TRIANGLE };
public:
ND_TriDofTransformation(int order)
: ND_DofTransformation(order*(order + 2), order, 3, 1, (int *)face_type)
: ND_DofTransformation(order*(order + 2), order, 3, 1)
{}
};
@@ -350,9 +345,7 @@ class ND_TetDofTransformation : public ND_DofTransformation
{
public:
ND_TetDofTransformation(int order)
: ND_DofTransformation(order*(order + 2)*(order + 3)/2, order, 6, 4,
(int *)Geometry::Constants<Geometry::TETRAHEDRON>::
FaceTypes)
: ND_DofTransformation(order*(order + 2)*(order + 3)/2, order, 6, 4)
{}
};
@@ -362,21 +355,7 @@ class ND_WedgeDofTransformation : public ND_DofTransformation
public:
ND_WedgeDofTransformation(int order)
: ND_DofTransformation(3 * order * ((order + 1) * (order + 2))/2,
order, 9, 5,
(int *)Geometry::Constants<Geometry::PRISM>::
FaceTypes)
{}
};
/// DoF transformation implementation for the Nedelec basis on pyramid elements
class ND_PyramidDofTransformation : public ND_DofTransformation
{
public:
ND_PyramidDofTransformation(int order)
: ND_DofTransformation(2 * order * (order * (order + 1) + 2),
order, 8, 5,
(int *)Geometry::Constants<Geometry::PYRAMID>::
FaceTypes)
order, 9, 2)
{}
};
+10 -53
View File
@@ -11,8 +11,6 @@
#include "../mesh/mesh_headers.hpp"
#include "fem.hpp"
#include "eltrans/eltrans_basis.hpp"
#include <cmath>
namespace mfem
@@ -68,6 +66,7 @@ const DenseMatrix &ElementTransformation::EvalInverseJ()
return invJ;
}
int InverseElementTransformation::FindClosestPhysPoint(
const Vector& pt, const IntegrationRule &ir)
{
@@ -181,13 +180,12 @@ int InverseElementTransformation::NewtonSolve(const Vector &pt,
const int dim = T->GetDimension();
const int sdim = T->GetSpaceDim();
IntegrationPoint xip, prev_xip;
real_t xd[3], yd[3], dxd[3], dxpd[3], dx_norm = -1.0, err_phys,
real_dx_norm = -1.0;
Vector x(xd, dim), y(yd, sdim), dx(dxd, dim), dx_prev(dxpd, dim);
real_t xd[3], yd[3], dxd[3], dx_norm = -1.0, err_phys, real_dx_norm = -1.0;
Vector x(xd, dim), y(yd, sdim), dx(dxd, dim);
bool hit_bdr = false, prev_hit_bdr = false;
// Use ip0 as initial guess:
xip = ip0;
xip = *ip0;
xip.Get(xd, dim); // xip -> x
if (print_level >= 3)
{
@@ -344,18 +342,16 @@ int InverseElementTransformation::Transform(const Vector &pt,
switch (init_guess_type)
{
case Center:
ip0 = Geometries.GetCenter(T->GetGeometryType());
ip0 = &Geometries.GetCenter(T->GetGeometryType());
break;
case ClosestPhysNode:
case ClosestRefNode:
{
const int order = qpts_order >= 0
? qpts_order
: std::max(T->Order() + rel_qpts_order, 0);
const int order = std::max(T->Order()+rel_qpts_order, 0);
if (order == 0)
{
ip0 = Geometries.GetCenter(T->GetGeometryType());
ip0 = &Geometries.GetCenter(T->GetGeometryType());
}
else
{
@@ -363,45 +359,11 @@ int InverseElementTransformation::Transform(const Vector &pt,
int closest_idx = (init_guess_type == ClosestPhysNode) ?
FindClosestPhysPoint(pt, RefG.RefPts) :
FindClosestRefPoint(pt, RefG.RefPts);
ip0 = RefG.RefPts.IntPoint(closest_idx);
}
break;
}
case EdgeScan:
{
const int order = qpts_order >= 0
? qpts_order
: std::max(T->Order() + rel_qpts_order, 0);
if (order == 0)
{
ip0 = Geometries.GetCenter(T->GetGeometryType());
}
else
{
auto &ir = *refiner.EdgeScan(T->GetGeometryType(), order + 1);
int res = Outside;
int npts = ir.GetNPoints();
// will return Inside if any test point reports Inside, Outside if
// all points report Outside, else Unknown
for (int i = 0; i < npts; ++i)
{
ip0 = ir.IntPoint(i);
int tmp_res = NewtonSolve(pt, ip);
switch (tmp_res)
{
case Inside:
return Inside;
case Outside:
break;
case Unknown:
res = Unknown;
break;
}
}
return res;
ip0 = &RefG.RefPts.IntPoint(closest_idx);
}
break;
}
case GivenPoint:
break;
@@ -482,8 +444,6 @@ int IsoparametricTransformation::OrderJ() const
return (FElem->GetOrder()-1);
case FunctionSpace::Qk:
return (FElem->GetOrder());
case FunctionSpace::Uk:
return (FElem->GetOrder());
default:
MFEM_ABORT("unsupported finite element");
}
@@ -498,8 +458,6 @@ int IsoparametricTransformation::OrderW() const
return (FElem->GetOrder() - 1) * FElem->GetDim();
case FunctionSpace::Qk:
return (FElem->GetOrder() * FElem->GetDim() - 1);
case FunctionSpace::Uk:
return (FElem->GetOrder() * FElem->GetDim() - 1);
default:
MFEM_ABORT("unsupported finite element");
}
@@ -519,8 +477,6 @@ int IsoparametricTransformation::OrderGrad(const FiniteElement *fe) const
return ((k-1)*(d-1)+(l-1));
case FunctionSpace::Qk:
return (k*(d-1)+(l-1));
case FunctionSpace::Uk:
return (k*(d-1)+(l-1));
default:
MFEM_ABORT("unsupported finite element");
}
@@ -756,4 +712,5 @@ real_t FaceElementTransformations::CheckConsistency(int print_level,
return max_dist;
}
}
+30 -281
View File
@@ -17,13 +17,9 @@
#include "intrules.hpp"
#include "fe.hpp"
#include "kernel_dispatch.hpp"
namespace mfem
{
class GridFunction;
class ElementTransformation
{
protected:
@@ -202,46 +198,40 @@ public:
/// Algorithms for selecting an initial guess.
enum InitGuessType
{
Center = 0, ///< Use the center of the reference element.
Center = 0, ///< Use the center of the reference element.
ClosestPhysNode = 1, /**<
Use the point returned by FindClosestPhysPoint() from a reference-space
grid of type and size controlled by SetInitGuessPointsType() and
SetInitGuessRelOrder(), respectively. */
ClosestRefNode = 2, /**<
Use the point returned by FindClosestRefPoint() from a reference-space
grid of type and size controlled by SetInitGuessPointsType() and
SetInitGuessRelOrder(), respectively. */
GivenPoint = 3, ///< Use a specific point, set with SetInitialGuess().
EdgeScan =
4, /**< Performs full solves on multiple points along the r/s/t=0 edges
of the element. It is recommended that SetInitGuessRelOrder() is
chosen such that max(trans_order+order,0)+1 <= 4 with
SetInitGuessPointsType() as Quadrature1D::ClosedUniform. @see
GeometryRefiner::EdgeScan */
Use the point returned by FindClosestPhysPoint() from a reference-space
grid of type and size controlled by SetInitGuessPointsType() and
SetInitGuessRelOrder(), respectively. */
ClosestRefNode = 2, /**<
Use the point returned by FindClosestRefPoint() from a reference-space
grid of type and size controlled by SetInitGuessPointsType() and
SetInitGuessRelOrder(), respectively. */
GivenPoint = 3 ///< Use a specific point, set with SetInitialGuess().
};
/// Solution strategy.
enum SolverType
{
Newton = 0, /**<
Use Newton's algorithm, without restricting the reference-space points
(iterates) to the reference element. */
Newton = 0, /**<
Use Newton's algorithm, without restricting the reference-space points
(iterates) to the reference element. */
NewtonSegmentProject = 1, /**<
Use Newton's algorithm, restricting the reference-space points to the
reference element by scaling back the Newton increments, i.e.
projecting new iterates, x_new, lying outside the element, to the
intersection of the line segment [x_old, x_new] with the boundary. */
NewtonElementProject = 2, /**<
Use Newton's algorithm, restricting the reference-space points to the
reference element by projecting new iterates, x_new, lying outside the
element, to the point on the boundary closest (in reference-space) to
x_new. */
Use Newton's algorithm, restricting the reference-space points to the
reference element by scaling back the Newton increments, i.e.
projecting new iterates, x_new, lying outside the element, to the
intersection of the line segment [x_old, x_new] with the boundary. */
NewtonElementProject = 2 /**<
Use Newton's algorithm, restricting the reference-space points to the
reference element by projecting new iterates, x_new, lying outside the
element, to the point on the boundary closest (in reference-space) to
x_new. */
};
/// Values returned by Transform().
enum TransformResult
{
Inside = 0, ///< The point is inside the element
Inside = 0, ///< The point is inside the element
Outside = 1, ///< The point is _probably_ outside the element
Unknown = 2 ///< The algorithm failed to determine where the point is
};
@@ -251,11 +241,9 @@ protected:
ElementTransformation *T;
// Parameters of the inversion algorithms:
IntegrationPoint ip0;
const IntegrationPoint *ip0;
int init_guess_type; // algorithm to use
GeometryRefiner refiner; // geometry refiner for initial guess
int qpts_order; // num_1D_qpts = rel_qpts_order + 1, or < 0 to use
// rel_qpts_order.
int rel_qpts_order; // num_1D_qpts = max(trans_order+rel_qpts_order,0)+1
int solver_type; // solution strategy to use
int max_iter; // max. number of Newton iterations
@@ -296,19 +284,19 @@ public:
tolerances. */
InverseElementTransformation(ElementTransformation *Trans = NULL)
: T(Trans),
ip0(NULL),
init_guess_type(Center),
refiner(Quadrature1D::OpenHalfUniform),
qpts_order(-1),
rel_qpts_order(-1),
solver_type(NewtonElementProject),
max_iter(16),
#ifdef MFEM_USE_DOUBLE
ref_tol(1e-15),
phys_rtol(4e-15),
phys_rtol(1e-15),
ip_tol(1e-8),
#elif defined(MFEM_USE_SINGLE)
ref_tol(4e-7),
phys_rtol(1e-6),
ref_tol(1e-7),
phys_rtol(1e-7),
ip_tol(1e-4),
#endif
print_level(-1)
@@ -326,28 +314,16 @@ public:
/** @brief Set the initial guess for subsequent calls to Transform(),
switching to the #GivenPoint #InitGuessType at the same time. */
void SetInitialGuess(const IntegrationPoint &init_ip)
{ ip0 = init_ip; SetInitialGuessType(GivenPoint); }
{ ip0 = &init_ip; SetInitialGuessType(GivenPoint); }
/// Set the Quadrature1D type used for the `Closest*` and `EdgeScan` initial
/// guess types.
/// Set the Quadrature1D type used for the `Closest*` initial guess types.
void SetInitGuessPointsType(int q_type) { refiner.SetType(q_type); }
/// Set the relative order used for the `Closest*` initial guess types.
/** The number of points in each spatial direction is given by the formula
max(trans_order+order,0)+1, where trans_order is the order of the current
ElementTransformation. */
void SetInitGuessRelOrder(int order)
{
qpts_order = -1;
rel_qpts_order = order;
}
/** The number of points in each spatial direction is given by the formula
order+1. */
void SetInitGuessOrder(int order)
{
qpts_order = order;
}
void SetInitGuessRelOrder(int order) { rel_qpts_order = order; }
/** @brief Specify which algorithm to use for solving the transformation
equation, i.e. when calling the Transform() method. */
@@ -397,233 +373,6 @@ public:
virtual int Transform(const Vector &pt, IntegrationPoint &ip);
};
/**
* @brief Performs batch inverse element transforms. Currently only supports
* non-mixed meshes with SEGMENT, SQUARE, or CUBE geometries. Mixed
* element order meshes are projected onto an equivalent uniform order mesh.
*/
class BatchInverseElementTransformation
{
// nodes grid function, not owned
const GridFunction *gf_ = nullptr;
// initial guess algorithm to use
InverseElementTransformation::InitGuessType init_guess_type =
InverseElementTransformation::ClosestPhysNode;
int qpts_order = -1; // num_1D_qpts = rel_qpts_order + 1, or < 0 to use
// rel_qpts_order.
// num_1D_qpts = max(trans_order+rel_qpts_order,0)+1
int rel_qpts_order = 0;
// solution strategy to use
InverseElementTransformation::SolverType solver_type =
InverseElementTransformation::NewtonElementProject;
// basis type stored in points1d
int basis_type = BasisType::Invalid;
// initial guess points type. Quadrature1D::Invalid is used for match
// basis_type.
int guess_points_type = Quadrature1D::Invalid;
// max. number of Newton iterations
int max_iter = 16;
// internal element node locations cache
Vector node_pos;
#ifdef MFEM_USE_DOUBLE
// reference space tolerance
real_t ref_tol = 1e-15;
// physical space tolerance (relative)
real_t phys_rtol = 4e-15;
#else
// reference space tolerance
real_t ref_tol = 4e-7;
// physical space tolerance (relative)
real_t phys_rtol = 1e-6;
#endif
// not owned, location of tensor product basis nodes in reference space
const Array<real_t> *points1d = nullptr;
public:
/// Uninitialized BatchInverseElementTransformation. Users must call
/// UpdateNodes before Transform.
BatchInverseElementTransformation();
///
/// Constructs a BatchInverseElementTransformation given @a nodes representing
/// the mesh nodes.
///
BatchInverseElementTransformation(const GridFunction &nodes,
MemoryType d_mt = MemoryType::DEFAULT);
///
/// Constructs a BatchInverseElementTransformation for a given @a mesh.
/// mesh.GetNodes() must not be null.
///
BatchInverseElementTransformation(const Mesh &mesh,
MemoryType d_mt = MemoryType::DEFAULT);
~BatchInverseElementTransformation();
/** @brief Choose how the initial guesses for subsequent calls to Transform()
will be selected. ClosestRefNode is currently not supported. */
void SetInitialGuessType(InverseElementTransformation::InitGuessType itype)
{
MFEM_ASSERT(itype != InverseElementTransformation::ClosestRefNode,
"ClosestRefNode is currently not supported");
init_guess_type = itype;
}
/// Set the Quadrature1D type used for the `Closest*` and `EdgeScan` initial
/// guess types.
void SetInitGuessPointsType(int q_type) { guess_points_type = q_type; }
/// Set the relative order used for the `Closest*` initial guess types.
/** The number of points in each spatial direction is given by the formula
max(trans_order+order,0)+1, where trans_order is the order of the
current ElementTransformation. */
void SetInitGuessRelOrder(int order)
{
qpts_order = -1;
rel_qpts_order = order;
}
/** The number of points in each spatial direction is given by the formula
order+1. */
void SetInitGuessOrder(int order) { qpts_order = order; }
/// @b Gets the basis type nodes are projected onto, or BasisType::Invalid if
/// uninitialized.
int GetBasisType() const { return basis_type; }
/** @brief Specify which algorithm to use for solving the transformation
equation, i.e. when calling the Transform() method. NewtonSegmentProject
is currently not supported. */
void SetSolverType(InverseElementTransformation::SolverType stype)
{
MFEM_ASSERT(stype != InverseElementTransformation::NewtonSegmentProject,
"NewtonSegmentProject is currently not supported");
solver_type = stype;
}
/// Set the maximum number of iterations when solving for a reference point.
void SetMaxIter(int max_it) { max_iter = max_it; }
/// Set the reference-space convergence tolerance.
void SetReferenceTol(real_t ref_sp_tol) { ref_tol = ref_sp_tol; }
/// Set the relative physical-space convergence tolerance.
void SetPhysicalRelTol(real_t phys_rel_tol) { phys_rtol = phys_rel_tol; }
/**
* @brief Updates internal datastructures if @a nodes change. Some version
* of UpdateNodes must be called at least once before calls to Transform if
* nodes have changed.
*/
void UpdateNodes(const GridFunction &nodes,
MemoryType d_mt = MemoryType::DEFAULT);
/**
* @brief Updates internal datastructures if @a mesh nodes change. Some version
* of UpdateNodes must be called at least once before calls to Transform if
* mesh nodes have changed. mesh.GetNodes() must not be null.
*/
void UpdateNodes(const Mesh &mesh, MemoryType d_mt = MemoryType::DEFAULT);
/** @brief Performs a batch request of a set of points belonging to the given
elements.
@a pts list of physical point coordinates ordered by
Ordering::Type::byNODES.
@a elems which element index to search for each corresponding point in
@a pts
@a types output search classification (@see
InverseElementTransformation::TransformResult).
@a refs result reference point coordinates ordered by
Ordering::Type::byNODES. If using InitGuessType::GivenPoint, this should
contain the initial guess for each point.
@a use_device hint for if device acceleration should be used.
Device acceleration is currently only implemented for meshes containing
only a single tensor product basis element type.
@a iters optional array storing how many iterations was spent on each
tested point
*/
void Transform(const Vector &pts, const Array<int> &elems, Array<int> &types,
Vector &refs, bool use_device = true,
Array<int> *iters = nullptr) const;
using ClosestPhysPointKernelType = void (*)(int, int, int, int,
const real_t *, const real_t *,
const int *, const real_t *,
const real_t *, real_t *);
// specialization params: Geom, SDim, use_device
MFEM_REGISTER_KERNELS(FindClosestPhysPoint, ClosestPhysPointKernelType,
(int, int, bool));
using ClosestPhysDofKernelType = void (*)(int, int, int,
const real_t *, const real_t *,
const int *, const real_t *,
real_t *);
// specialization params: Geom, SDim, use_device
MFEM_REGISTER_KERNELS(FindClosestPhysDof, ClosestPhysDofKernelType,
(int, int, bool));
using ClosestRefDofKernelType = void (*)(int, int, int, const real_t *,
const real_t *, const int *,
const real_t *, real_t *);
// specialization params: Geom, SDim, use_device
MFEM_REGISTER_KERNELS(FindClosestRefDof, ClosestRefDofKernelType,
(int, int, bool));
using ClosestRefPointKernelType = void (*)(int, int, int, int, const real_t *,
const real_t *, const int *,
const real_t *, const real_t *,
real_t *);
// specialization params: Geom, SDim, use_device
MFEM_REGISTER_KERNELS(FindClosestRefPoint, ClosestRefPointKernelType,
(int, int, bool));
using NewtonKernelType = void (*)(real_t, real_t, int, int, int, int,
const real_t *, const real_t *,
const int *, const real_t *, int *, int*,
real_t *);
// specialization params: Geom, SDim, SolverType, use_device
MFEM_REGISTER_KERNELS(NewtonSolve, NewtonKernelType,
(int, int, InverseElementTransformation::SolverType,
bool));
using NewtonEdgeScanKernelType = void (*)(real_t, real_t, int, int, int, int,
const real_t *, const real_t *,
const int *, const real_t *,
const real_t *, int, int *, int *,
real_t *);
// specialization params: Geom, SDim, SolverType, use_device
MFEM_REGISTER_KERNELS(NewtonEdgeScan, NewtonEdgeScanKernelType,
(int, int, InverseElementTransformation::SolverType,
bool));
struct Kernels { Kernels(); };
template <int Dim, int SDim>
static void AddFindClosestSpecialization()
{
FindClosestPhysPoint::Specialization<Dim, SDim, true>::Add();
FindClosestRefPoint::Specialization<Dim, SDim, true>::Add();
FindClosestPhysPoint::Specialization<Dim, SDim, false>::Add();
FindClosestRefPoint::Specialization<Dim, SDim, false>::Add();
FindClosestPhysDof::Specialization<Dim, SDim, true>::Add();
FindClosestRefDof::Specialization<Dim, SDim, true>::Add();
FindClosestPhysDof::Specialization<Dim, SDim, false>::Add();
FindClosestRefDof::Specialization<Dim, SDim, false>::Add();
}
template <int Dim, int SDim, InverseElementTransformation::SolverType SType>
static void AddNewtonSolveSpecialization()
{
NewtonSolve::Specialization<Dim, SDim, SType, true>::Add();
NewtonEdgeScan::Specialization<Dim, SDim, SType, true>::Add();
NewtonSolve::Specialization<Dim, SDim, SType, false>::Add();
NewtonEdgeScan::Specialization<Dim, SDim, SType, false>::Add();
}
};
/// A standard isoparametric element transformation
class IsoparametricTransformation : public ElementTransformation
{
-187
View File
@@ -1,187 +0,0 @@
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_ELTRANS_BASIS
#define MFEM_ELTRANS_BASIS
#include "../../general/forall.hpp"
#include "../geom.hpp"
// this file contains utilities for computing nodal basis functions and their
// derivatives in device kernels
namespace mfem
{
namespace eltrans
{
/// Various utilities for working with different element geometries
template <int GeomType> struct GeometryUtils;
template <> struct GeometryUtils<Geometry::SEGMENT>
{
static constexpr MFEM_HOST_DEVICE int Dimension() { return 1; }
/// @b true if the given point x in ref space is inside the element
static bool MFEM_HOST_DEVICE inside(real_t x) { return x >= 0 && x <= 1; }
/// @b Bound the reference coordinate @a x += dx to be inside the segment.
/// @a dx is updated to be dx = project(x+dx) - x
/// @return true if x + dx hit a boundary
static bool MFEM_HOST_DEVICE project(real_t &x, real_t &dx)
{
real_t tmp = x;
x += dx;
if (x < 0)
{
x = 0;
dx = x - tmp;
return true;
}
if (x > 1)
{
x = 1;
dx = x - tmp;
return true;
}
return false;
}
};
template <> struct GeometryUtils<Geometry::SQUARE>
{
static constexpr MFEM_HOST_DEVICE int Dimension() { return 2; }
/// @b true if the given point (x,y) in ref space is inside the element
static bool MFEM_HOST_DEVICE inside(real_t x, real_t y)
{
return (x >= 0) && (x <= 1) && (y >= 0) && (y <= 1);
}
/// @b Bound the reference coordinate @a (x,y) += (dx,dy) to be inside the
/// square.
/// @a dx and @a dy are updated to be (dx,dy) = project(x+dx,y+dy) - (x,y)
/// @return true if (x,y) + (dx,dy) hit a boundary
static bool MFEM_HOST_DEVICE project(real_t &x, real_t &y, real_t &dx,
real_t &dy)
{
bool x_cond = GeometryUtils<Geometry::SEGMENT>::project(x, dx);
bool y_cond = GeometryUtils<Geometry::SEGMENT>::project(y, dy);
return x_cond || y_cond;
}
};
template <> struct GeometryUtils<Geometry::CUBE>
{
static constexpr MFEM_HOST_DEVICE int Dimension() { return 3; }
/// @b true if the given point (x,y,z) in ref space is inside the element
static bool MFEM_HOST_DEVICE inside(real_t x, real_t y, real_t z)
{
return (x >= 0) && (x <= 1) && (y >= 0) && (y <= 1) && (z >= 0) && (z <= 1);
}
/// @b Bound the reference coordinate @a (x,y,z) += (dx,dy,dz) to be inside
/// the cube.
/// @a dx, @a dy, and @ dz are updated to be
/// (dx,dy,dz) = project(x+dx,y+dy,z+dz) - (x,y,z)
/// @return true if (x,y,z) + (dx,dy,dz) hit a boundary
static bool MFEM_HOST_DEVICE project(real_t &x, real_t &y, real_t &z,
real_t &dx, real_t &dy, real_t &dz)
{
bool x_cond = GeometryUtils<Geometry::SEGMENT>::project(x, dx);
bool y_cond = GeometryUtils<Geometry::SEGMENT>::project(y, dy);
bool z_cond = GeometryUtils<Geometry::SEGMENT>::project(z, dz);
return x_cond || y_cond || z_cond;
}
};
/// 1D Lagrange basis from [0, 1]
class Lagrange
{
public:
/// interpolant node locations, in reference space
const real_t *z;
/// number of points
int pN;
/// @b Evaluates the @a i'th Lagrange polynomial at @a x
real_t MFEM_HOST_DEVICE eval(real_t x, int i) const
{
real_t u0 = 1;
real_t den = 1;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
real_t d_j = (x - z[j]);
u0 = d_j * u0;
den *= (z[i] - z[j]);
}
}
den = 1 / den;
return u0 * den;
}
/// @b Evaluates the @a i'th Lagrange polynomial and its first derivative at
/// @a x
void MFEM_HOST_DEVICE eval_d1(real_t &p, real_t &d1, real_t x, int i) const
{
real_t u0 = 1;
real_t u1 = 0;
real_t den = 1;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
real_t d_j = (x - z[j]);
u1 = d_j * u1 + u0;
u0 = d_j * u0;
den *= (z[i] - z[j]);
}
}
den = 1 / den;
p = u0 * den;
d1 = u1 * den;
}
/// @b Evaluates the @a i'th Lagrange polynomial and its first and second
/// derivatives at @a x
void MFEM_HOST_DEVICE eval_d2(real_t &p, real_t &d1, real_t &d2, real_t x,
int i) const
{
real_t u0 = 1;
real_t u1 = 0;
real_t u2 = 0;
real_t den = 1;
for (int j = 0; j < pN; ++j)
{
if (i != j)
{
real_t d_j = (x - z[j]);
u2 = d_j * u2 + u1;
u1 = d_j * u1 + u0;
u0 = d_j * u0;
den *= (z[i] - z[j]);
}
}
den = 1 / den;
p = den * u0;
d1 = den * u1;
d2 = 2 * den * u2;
}
};
} // namespace eltrans
} // namespace mfem
#endif
+2 -2
View File
@@ -623,7 +623,7 @@ private:
public:
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
@param di_ The bilinear form to compute the interface flux.
@param di_ The bilinearform to compute the interface flux.
@param sol_ The solution field whose error is to be estimated.
@param flux_fes_ The finite element space for the interface flux.
@param attributes_ The attributes of the subdomain(s) for which the
@@ -635,7 +635,7 @@ public:
const Array<int> &attributes_ = Array<int>());
/** @brief Construct a new KellyErrorEstimator object for a scalar field.
@param di_ The bilinear form to compute the interface flux.
@param di_ The bilinearform to compute the interface flux.
@param sol_ The solution field whose error is to be estimated.
@param flux_fes_ The finite element space for the interface flux.
@param attributes_ The attributes of the subdomain(s) for which the
+17 -41
View File
@@ -69,16 +69,16 @@ inline int ToLexOrdering2D(const int face_id, const int size1d, const int i)
}
/// @brief Given a face DOF index on a shared face, ordered lexicographically
/// relative to element the element (where the local face is face_id), and
/// return the corresponding face DOF index ordered lexicographically relative
/// to the face itself.
/// relative to element 1, return the corresponding face DOF index ordered
/// lexicographically relative to element 2.
MFEM_HOST_DEVICE
inline int PermuteFace2D(const int face_id, const int orientation,
const int size1d, const int index)
inline int PermuteFace2D(const int face_id1, const int face_id2,
const int orientation, const int size1d,
const int index)
{
int new_index;
// Convert from element 1 lex ordering to native ordering
if (face_id == 2 || face_id == 3)
if (face_id1 == 2 || face_id1 == 3)
{
new_index = size1d-1-index;
}
@@ -91,18 +91,7 @@ inline int PermuteFace2D(const int face_id, const int orientation,
{
new_index = size1d-1-new_index;
}
return new_index;
}
/// @brief Given a face DOF index on a shared face, ordered lexicographically
/// relative to element 1, return the corresponding face DOF index ordered
/// lexicographically relative to element 2.
MFEM_HOST_DEVICE
inline int PermuteFace2D(const int face_id1, const int face_id2,
const int orientation, const int size1d,
const int index)
{
const int new_index = PermuteFace2D(face_id1, orientation, size1d, index);
// Covert to element 2 lex ordering
return ToLexOrdering2D(face_id2, size1d, new_index);
}
@@ -127,22 +116,26 @@ inline int ToLexOrdering3D(const int face_id, const int size1d, const int i,
}
}
/// @brief Given the index of a face DOF in lexicographic ordering relative the
/// element (where the local face id is @a face_id), permute the index so that
/// it is lexicographically ordered relative to the face itself.
/// @brief Given the index of a face DOF in lexicographic ordering relative
/// element 1, permute the index so that it is lexicographically ordered
/// relative to element 2.
///
/// The given face corresponds to local face index @a face_id1 relative to
/// element 1, and @a face_id2 (with @a orientation) relative to element 2.
MFEM_HOST_DEVICE
inline int PermuteFace3D(const int face_id, const int orientation,
inline int PermuteFace3D(const int face_id1, const int face_id2,
const int orientation,
const int size1d, const int index)
{
int i=0, j=0, new_i=0, new_j=0;
i = index%size1d;
j = index/size1d;
// Convert from lex ordering
if (face_id==3 || face_id==4)
if (face_id1==3 || face_id1==4)
{
i = size1d-1-i;
}
else if (face_id==0)
else if (face_id1==0)
{
j = size1d-1-j;
}
@@ -182,23 +175,6 @@ inline int PermuteFace3D(const int face_id, const int orientation,
new_j = (size1d-1-j);
break;
}
return new_i + new_j*size1d;
}
/// @brief Given the index of a face DOF in lexicographic ordering relative
/// element 1, permute the index so that it is lexicographically ordered
/// relative to element 2.
///
/// The given face corresponds to local face index @a face_id1 relative to
/// element 1, and @a face_id2 (with @a orientation) relative to element 2.
MFEM_HOST_DEVICE
inline int PermuteFace3D(const int face_id1, const int face_id2,
const int orientation,
const int size1d, const int index)
{
const int new_index = PermuteFace3D(face_id1, orientation, size1d, index);
const int new_i = new_index%size1d;
const int new_j = new_index/size1d;
return ToLexOrdering3D(face_id2, size1d, new_i, new_j);
}
+62 -95
View File
@@ -973,22 +973,6 @@ void NodalFiniteElement::ProjectDiv(
}
}
void NodalFiniteElement::ReorderLexToNative(int ncomp,
Vector &dofs) const
{
MFEM_ASSERT(lex_ordering.Size() == dof, "Permutation is not defined by FE.");
MFEM_ASSERT(dofs.Size() == ncomp * dof, "Wrong input size.");
Vector dofs_native(ncomp * dof);
for (int i = 0; i < dof; i++)
{
for (int c = 0; c < ncomp; c++)
{
dofs_native(c*dof + lex_ordering[i]) = dofs(c*dof + i);
}
}
dofs = dofs_native;
}
VectorFiniteElement::VectorFiniteElement(int D, Geometry::Type G,
int Do, int O, int M, int F)
@@ -2184,64 +2168,6 @@ void Poly_1D::CalcDBinomTerms(const int p, const real_t x, const real_t y,
}
}
void Poly_1D::CalcDxBinomTerms(const int p, const real_t x, const real_t y,
real_t *u)
{
if (p == 0)
{
u[0] = 0.;
}
else
{
int i;
const int *b = Binom(p);
real_t z = 1.;
for (i = 1; i < p; i++)
{
u[i] = i * b[i]*z;
z *= x;
}
u[p] = i * z;
z = y;
for (i--; i > 0; i--)
{
u[i] *= z;
z *= y;
}
u[0] = 0;
}
}
void Poly_1D::CalcDyBinomTerms(const int p, const real_t x, const real_t y,
real_t *u)
{
if (p == 0)
{
u[0] = 0.;
}
else
{
int i;
const int *b = Binom(p);
real_t z = x;
for (i = 1; i < p; i++)
{
u[i] = b[i]*z;
z *= x;
}
u[p] = 0.;
z = 1.;
for (i--; i > 0; i--)
{
u[i] *= (p - i) * z;
z *= y;
}
u[0] = p * z;
}
}
void Poly_1D::CalcLegendre(const int p, const real_t x, real_t *u)
{
// use the recursive definition for [-1,1]:
@@ -2341,58 +2267,99 @@ void Poly_1D::CalcChebyshev(const int p, const real_t x, real_t *u, real_t *d,
}
}
const Array<real_t>* Poly_1D::GetPointsArray(const int p, const int btype)
const real_t *Poly_1D::GetPoints(const int p, const int btype)
{
Array<real_t> *val;
Array<real_t*> *pts;
BasisType::Check(btype);
const int qtype = BasisType::GetQuadrature1D(btype);
if (qtype == Quadrature1D::Invalid) { return nullptr; }
if (qtype == Quadrature1D::Invalid) { return NULL; }
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (Poly1DGetPoints)
#endif
{
std::pair<int, int> key(btype, p);
auto it = points_container.find(key);
if (it == points_container.end())
auto it = points_container.find(btype);
if (it != points_container.end())
{
it = points_container.emplace(key, new Array<real_t>(p + 1, h_mt)).first;
val = it->second.get();
real_t* hptr = val->HostWrite();
quad_func.GivePolyPoints(p + 1, hptr, qtype);
pts = it->second;
}
else
{
val = it->second.get();
pts = new Array<real_t*>(h_mt);
points_container[btype] = pts;
}
if (pts->Size() <= p)
{
pts->SetSize(p + 1, NULL);
}
if ((*pts)[p] == NULL)
{
(*pts)[p] = new real_t[p + 1];
quad_func.GivePolyPoints(p + 1, (*pts)[p], qtype);
}
}
return val;
return (*pts)[p];
}
Poly_1D::Basis &Poly_1D::GetBasis(const int p, const int btype)
{
Array<Basis*> *bases;
BasisType::Check(btype);
Basis* val;
#if defined(MFEM_THREAD_SAFE) && defined(MFEM_USE_OPENMP)
#pragma omp critical (Poly1DGetBasis)
#endif
{
std::pair<int, int> key(btype, p);
auto it = bases_container.find(key);
if (it == bases_container.end())
auto it = bases_container.find(btype);
if (it != bases_container.end())
{
bases = it->second;
}
else
{
// we haven't been asked for basis or points of this type yet
bases = new Array<Basis*>(h_mt);
bases_container[btype] = bases;
}
if (bases->Size() <= p)
{
bases->SetSize(p + 1, NULL);
}
if ((*bases)[p] == NULL)
{
EvalType etype;
if (btype == BasisType::Positive) { etype = Positive; }
else if (btype == BasisType::IntegratedGLL) { etype = Integrated; }
else { etype = Barycentric; }
it = bases_container
.emplace(key, new Basis(p, GetPoints(p, btype), etype))
.first;
(*bases)[p] = new Basis(p, GetPoints(p, btype), etype);
}
val = it->second.get();
}
return *val;
return *(*bases)[p];
}
Poly_1D::~Poly_1D()
{
for (PointsMap::iterator it = points_container.begin();
it != points_container.end() ; ++it)
{
Array<real_t*>& pts = *it->second;
for (int i = 0; i < pts.Size(); ++i)
{
delete [] pts[i];
}
delete it->second;
}
for (BasisMap::iterator it = bases_container.begin();
it != bases_container.end() ; ++it)
{
Array<Basis*>& bases = *it->second;
for (int i = 0; i < bases.Size(); ++i)
{
delete bases[i];
}
delete it->second;
}
}
+11 -61
View File
@@ -15,12 +15,8 @@
#include "../intrules.hpp"
#include "../geom.hpp"
#include "../doftrans.hpp"
#include "../../general/hash.hpp"
#include <map>
#include <memory>
#include <unordered_map>
#include <utility>
namespace mfem
{
@@ -227,10 +223,9 @@ class FunctionSpace
public:
enum
{
Pk, ///< Polynomials of order k
Qk, ///< Tensor products of polynomials of order k
rQk, ///< Refined tensor products of polynomials of order k
Uk ///< Rational polynomials of order k
Pk, ///< Polynomials of order k
Qk, ///< Tensor products of polynomials of order k
rQk ///< Refined tensor products of polynomials of order k
};
};
@@ -799,12 +794,6 @@ public:
TensorBasisElement::GetDofMap, but it is also available for non-tensor
elements. */
const Array<int> &GetLexicographicOrdering() const { return lex_ordering; }
/// Given a lexicographically ordered Vector @a dofs, containing @a ncomp
/// components of the size of the scalar FiniteElement, reorder its entries
/// into native (H1) ordering.
/// The function assumes that GetLexicographicOrdering() is not empty.
void ReorderLexToNative(int ncomp, Vector &dofs) const;
};
/** @brief Intermediate class for finite elements whose basis functions return
@@ -1049,14 +1038,8 @@ public:
};
private:
/// key: (btype, p), value: underlying storage Array
typedef std::unordered_map<std::pair<int, int>,
std::unique_ptr<Basis>, PairHasher>
BasisMap;
/// key: (btype, p), value: underlying storage Array
typedef std::unordered_map<std::pair<int, int>,
std::unique_ptr<Array<real_t>>, PairHasher>
PointsMap;
typedef std::map<int, Array<real_t*>*> PointsMap;
typedef std::map<int, Array<Basis*>*> BasisMap;
MemoryType h_mt;
PointsMap points_container;
@@ -1090,40 +1073,17 @@ public:
@return A pointer to an array containing the `p+1` coordinates of the
points. Returns NULL if the BasisType has no associated set of
points. */
const Array<real_t>* GetPointsArray(const int p, const int btype);
/** @brief Get the coordinates of the points of the given BasisType,
@a btype.
@param[in] p The polynomial degree; the number of points is `p+1`.
@param[in] btype The BasisType.
@param[in] on_device true if the requested pointer should be accessible
from the device.
@return A pointer to an array containing the `p+1` coordinates of the
points. Returns NULL if the BasisType has no associated set of
points. */
const real_t *GetPoints(const int p, const int btype,
bool on_device = false)
{
return GetPointsArray(p, btype)->Read(on_device);
}
const real_t *GetPoints(const int p, const int btype);
/// Get coordinates of an open (GaussLegendre) set of points if degree @a p
const real_t *OpenPoints(const int p,
const int btype = BasisType::GaussLegendre,
bool on_device = false)
{
return GetPoints(p, btype, on_device);
}
const int btype = BasisType::GaussLegendre)
{ return GetPoints(p, btype); }
/// Get coordinates of a closed (GaussLegendre) set of points if degree @a p
const real_t *ClosedPoints(const int p,
const int btype = BasisType::GaussLobatto,
bool on_device = false)
{
return GetPoints(p, btype, on_device);
}
const int btype = BasisType::GaussLobatto)
{ return GetPoints(p, btype); }
/** @brief Get a Poly_1D::Basis object of the given degree and BasisType,
@a btype.
@@ -1198,16 +1158,6 @@ public:
in the already allocated @a d array.*/
static void CalcDBinomTerms(const int p, const real_t x, const real_t y,
real_t *d);
/** @brief Compute the derivatives (w.r.t. x) of the terms in the expansion
of the binomial (x + y)^p. Store the results in the already allocated
@a d array.*/
static void CalcDxBinomTerms(const int p, const real_t x, const real_t y,
real_t *d);
/** @brief Compute the derivatives (w.r.t. y) of the terms in the expansion
of the binomial (x + y)^p. Store the results in the already allocated
@a d array.*/
static void CalcDyBinomTerms(const int p, const real_t x, const real_t y,
real_t *d);
/** @brief Compute the values of the Bernstein basis functions of order
@a p at coordinate @a x and store the results in the already allocated
@@ -1236,7 +1186,7 @@ public:
static void CalcLegendre(const int p, const real_t x, real_t *u);
static void CalcLegendre(const int p, const real_t x, real_t *u, real_t *d);
~Poly_1D() = default;
~Poly_1D();
};
extern MFEM_EXPORT Poly_1D poly1d;
+34 -573
View File
@@ -1341,7 +1341,7 @@ const
LinearPyramidFiniteElement::LinearPyramidFiniteElement()
: NodalFiniteElement(3, Geometry::PYRAMID, 5, 1, FunctionSpace::Uk)
: NodalFiniteElement(3, Geometry::PYRAMID, 5, 1)
{
Nodes.IntPoint(0).x = 0.0;
Nodes.IntPoint(0).y = 0.0;
@@ -3015,7 +3015,7 @@ void P0WdgFiniteElement::CalcDShape(const IntegrationPoint &ip,
P0PyrFiniteElement::P0PyrFiniteElement()
: NodalFiniteElement(3, Geometry::PYRAMID, 1, 0, FunctionSpace::Uk)
: NodalFiniteElement(3, Geometry::PYRAMID, 1, 0, FunctionSpace::Qk)
{
Nodes.IntPoint(0).x = 0.375;
Nodes.IntPoint(0).y = 0.375;
@@ -4818,7 +4818,7 @@ void Nedelec1WdgFiniteElement::ProjectGrad(const FiniteElement &fe,
Nedelec1PyrFiniteElement::Nedelec1PyrFiniteElement()
: VectorFiniteElement(3, Geometry::PYRAMID, 8, 1, H_CURL, FunctionSpace::Uk)
: VectorFiniteElement(3, Geometry::PYRAMID, 8, 1, H_CURL)
{
// not real nodes ...
Nodes.IntPoint(0).x = 0.5;
@@ -4866,11 +4866,7 @@ void Nedelec1PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
{
// We must return the limit of the basis functions as z->1. In order to
// remain inside the pyramid in this limit the x and y coordinates must
// be approaching 0. Unfortunately we obtain different limits if we
// approach (0,0,1) from different directions. The values provided below
// are the limits as x->(1-z)/2 and y->(1-z)/2 i.e. along the line from
// the center of the base of the pyramid towards the apex. The resulting
// limiting basis function values are:
// be approaching 0. The resulting limiting basis function values are:
shape(0,0) = 0.;
shape(0,1) = 0.;
shape(0,2) = 0.;
@@ -4887,21 +4883,21 @@ void Nedelec1PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
shape(3,1) = 0.;
shape(3,2) = 0.;
shape(4,0) = 0.5;
shape(4,1) = 0.5;
shape(4,2) = 0.75;
shape(4,0) = 1.;
shape(4,1) = 1.;
shape(4,2) = 1.;
shape(5,0) = - 0.5;
shape(5,1) = 0.5;
shape(5,2) = 0.25;
shape(5,0) = - 1.;
shape(5,1) = 0.;
shape(5,2) = 0.;
shape(6,0) = - 0.5;
shape(6,1) = - 0.5;
shape(6,2) = - 0.25;
shape(6,0) = 0.;
shape(6,1) = 0.;
shape(6,2) = 0.;
shape(7,0) = 0.5;
shape(7,1) = - 0.5;
shape(7,2) = 0.25;
shape(7,0) = 0.;
shape(7,1) = - 1.;
shape(7,2) = 0.;
return;
}
@@ -4956,36 +4952,36 @@ const
// In order to remain inside the pyramid in this limit the x and y
// coordinates must be approaching 0. The resulting limiting basis
// function values are:
curl_shape(0,0) = - 0.5;
curl_shape(0,1) = - 1.5;
curl_shape(0,0) = 0.;
curl_shape(0,1) = - 2.;
curl_shape(0,2) = 1.;
curl_shape(1,0) = 0.5;
curl_shape(1,1) = - 0.5;
curl_shape(1,0) = 0.;
curl_shape(1,1) = 0.;
curl_shape(1,2) = 1.;
curl_shape(2,0) = 0.5;
curl_shape(2,1) = - 0.5;
curl_shape(2,0) = 0.;
curl_shape(2,1) = 0.;
curl_shape(2,2) = - 1.;
curl_shape(3,0) = 1.5;
curl_shape(3,1) = 0.5;
curl_shape(3,0) = 2.;
curl_shape(3,1) = 0.;
curl_shape(3,2) = - 1.;
curl_shape(4,0) = - 1.;
curl_shape(4,1) = 1.;
curl_shape(4,0) = - 2.;
curl_shape(4,1) = 2.;
curl_shape(4,2) = 0.;
curl_shape(5,0) = - 1.;
curl_shape(5,1) = - 1.;
curl_shape(5,0) = 0.;
curl_shape(5,1) = - 2.;
curl_shape(5,2) = 0.;
curl_shape(6,0) = 1.;
curl_shape(6,1) = - 1.;
curl_shape(6,0) = 0.;
curl_shape(6,1) = 0.;
curl_shape(6,2) = 0.;
curl_shape(7,0) = 1.;
curl_shape(7,1) = 1.;
curl_shape(7,0) = 2.;
curl_shape(7,1) = 0.;
curl_shape(7,2) = 0.;
return;
@@ -5123,540 +5119,6 @@ void Nedelec1PyrFiniteElement::ProjectGrad(const FiniteElement &fe,
}
Nedelec2PyrFiniteElement::Nedelec2PyrFiniteElement()
: VectorFiniteElement(3, Geometry::PYRAMID, 28, 2, H_CURL, FunctionSpace::Uk)
{
const real_t *eop = poly1d.OpenPoints(2 - 1);
const real_t fop = 1. / 3.;
// not real nodes ...
Nodes.IntPoint(0).Set3(eop[0], 0., 0.);
Nodes.IntPoint(1).Set3(eop[1], 0., 0.);
Nodes.IntPoint(2).Set3(1.0, eop[0], 0.);
Nodes.IntPoint(3).Set3(1.0, eop[1], 0.);
Nodes.IntPoint(4).Set3(eop[0], 1.0, 0.);
Nodes.IntPoint(5).Set3(eop[1], 1.0, 0.);
Nodes.IntPoint(6).Set3(0., eop[0], 0.);
Nodes.IntPoint(7).Set3(0., eop[1], 0.);
Nodes.IntPoint(8).Set3(0., 0., eop[0]);
Nodes.IntPoint(9).Set3(0., 0., eop[1]);
Nodes.IntPoint(10).Set3(eop[1], 0., eop[0]);
Nodes.IntPoint(11).Set3(eop[0], 0., eop[1]);
Nodes.IntPoint(12).Set3(eop[1], eop[1], eop[0]);
Nodes.IntPoint(13).Set3(eop[0], eop[0], eop[1]);
Nodes.IntPoint(14).Set3(0., eop[1], eop[0]);
Nodes.IntPoint(15).Set3(0., eop[0], eop[1]);
Nodes.IntPoint(16).Set3(eop[0], 0.5, 0.);
Nodes.IntPoint(17).Set3(eop[1], 0.5, 0.);
Nodes.IntPoint(18).Set3(0.5, eop[0], 0.);
Nodes.IntPoint(19).Set3(0.5, eop[1], 0.);
Nodes.IntPoint(20).Set3(fop, 0., fop);
Nodes.IntPoint(21).Set3(fop, 0., fop);
Nodes.IntPoint(22).Set3(2.*fop, fop, fop);
Nodes.IntPoint(23).Set3(2.*fop, fop, fop);
Nodes.IntPoint(24).Set3(fop, 2.*fop, fop);
Nodes.IntPoint(25).Set3(fop, 2.*fop, fop);
Nodes.IntPoint(26).Set3(0., fop, fop);
Nodes.IntPoint(27).Set3(0., fop, fop);
{
int n = 28;
DenseMatrix I(n,n);
DenseMatrix vecs(n,3);
I = 0.0;
for (int i=0; i<n; i++)
{
CalcVShape(Nodes.IntPoint(i), vecs);
for (int j=0; j<n; j++)
{
I(j,i) = vecs(j,0)*tk[i][0]+vecs(j,1)*tk[i][1]+vecs(j,2)*tk[i][2];
}
}
}
}
void Nedelec2PyrFiniteElement::CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const
{
shape = 0.0;
const real_t one = 1.0;
const real_t x = ip.x, y = ip.y, z = ip.z;
const real_t ox = one - x - z, oy = one - y - z, oz = one - z;
const real_t sq3 = sqrt(3.0);
const real_t tol = 1e-6;
if (oz <= tol)
{
// We must return the limit of the basis functions as z->1. In order to
// remain inside the pyramid in this limit the x and y coordinates must
// be approaching 0. The resulting limiting basis function values are:
shape(0,0) = 0.;
shape(0,1) = 0.;
shape(0,2) = 0.;
shape(1,0) = 0.;
shape(1,1) = 0.;
shape(1,2) = 0.;
shape(2,0) = 0.;
shape(2,1) = 0.;
shape(2,2) = 0.;
shape(3,0) = 0.;
shape(3,1) = 0.;
shape(3,2) = 0.;
shape(4,0) = 0.;
shape(4,1) = 0.;
shape(4,2) = 0.;
shape(5,0) = 0.;
shape(5,1) = 0.;
shape(5,2) = 0.;
shape(6,0) = 0.;
shape(6,1) = 0.;
shape(6,2) = 0.;
shape(7,0) = 0.;
shape(7,1) = 0.;
shape(7,2) = 0.;
return;
}
const real_t ozi = one / oz;
const real_t me0120[3] = {oy, 0., x * oy * ozi};
const real_t me1120[3] = {(x - ox) * oy, 0., (x - ox) * x * oy * ozi};
const real_t me0121[3] = {y, 0., x * y * ozi};
const real_t me1121[3] = {(x - ox) * y, 0., (x - ox) * x * y * ozi};
const real_t me0210[3] = {0., ox, ox * y * ozi};
const real_t me1210[3] = {0., ox * (y - oy), ox * y * (y - oy) * ozi};
const real_t me0211[3] = {0., x, x * y * ozi};
const real_t me1211[3] = {0., x * (y - oy), x * y * (y - oy) * ozi};
const real_t te01[3] = {oy * z * ozi, ox * z * ozi,
(ox * oy + (x * oy + ox * y) * z) * ozi * ozi
};
const real_t te11[3] = {oy * z * (z * oz - ox * oy) * ozi * ozi,
ox * z * (z * oz - ox * oy) * ozi * ozi,
(ox * oy + z * (x * oy + ox * y)) *
(z * oz - ox * oy) * ozi * ozi * ozi
};
const real_t te02[3] = {-oy * z * ozi, x * z * ozi,
x * (y * z + oy * oz) * ozi * ozi
};
const real_t te12[3] = {oy * z * (x * oy - z * oz) * ozi * ozi,
-x * z * (x * oy - z * oz) * ozi * ozi,
-x * (y * z + oy * oz) * (x * oy - z * oz)
* ozi * ozi * ozi
};
const real_t te03[3] = {-y * z * ozi, -x * z * ozi,
x * y * (one - 2_r * z) * ozi * ozi
};
const real_t te13[3] = {y * z * (x * y - z * oz) * ozi * ozi,
x * z * (x * y - z * oz) * ozi * ozi,
-x * y * (one - 2_r * z) * (x * y - z * oz)
* ozi * ozi * ozi
};
const real_t te04[3] = {y * z * ozi, -ox * z * ozi,
y * (x * z + ox * oz) * ozi * ozi
};
const real_t te14[3] = {-y * z * (ox * y - z * oz) * ozi * ozi,
ox * z * (ox * y - z * oz) * ozi * ozi,
-y * (x * z + ox * oz) * (ox * y - z * oz)
* ozi * ozi * ozi
};
const real_t qI02[3] = {-y * oy * ozi, 0., -x * y * oy * ozi * ozi};
const real_t qI12[3] = {-(x - ox) * y * oy * ozi * ozi, 0.,
-(x - ox) * x * y * oy * ozi * ozi * ozi
};
const real_t qII02[3] = {0., -x * ox * ozi, -x * y * ox * ozi * ozi};
const real_t qII12[3] = {0., -x * ox * (y - oy) * ozi * ozi,
-x * ox * y * (y - oy) * ozi * ozi * ozi
};
const real_t tI120[3] = {oy * z, 0., x * oy * z * ozi};
const real_t tI121[3] = {y * z, 0., x * y * z * ozi};
const real_t tI210[3] = {0., ox * z, ox * y * z * ozi};
const real_t tI211[3] = {0., x * z, x * y * z * ozi};
const real_t tII120[3] = {-ox * oy * z * ozi, 0., x * ox * oy * ozi};
const real_t tII121[3] = {-ox * y * z * ozi, 0., x * ox * y * ozi};
const real_t tII210[3] = {0., -ox * oy * z * ozi, ox * y * oy * ozi};
const real_t tII211[3] = {0., -x * oy * z * ozi, x * y * oy * ozi};
// Edge 0,1
for (int d=0; d<3; d++)
{
shape(0,d) = 0.5 * me0120[d] + qI02[d]
- sq3 * (0.5 * me1120[d] + qI12[d]) - 1.5 * tI120[d];
}
for (int d=0; d<3; d++)
{
shape(1,d) = 0.5 * me0120[d] + qI02[d]
+ sq3 * (0.5 * me1120[d] + qI12[d]) - 1.5 * tI120[d];
}
// Edge 1,2
for (int d=0; d<3; d++)
{
shape(2,d) = 0.5 * me0211[d] + qII02[d]
- sq3 * (0.5 * me1211[d] + qII12[d]) - 1.5 * tI211[d];
}
for (int d=0; d<3; d++)
{
shape(3,d) = 0.5 * me0211[d] + qII02[d]
+ sq3 * (0.5 * me1211[d] + qII12[d]) - 1.5 * tI211[d];
}
// Edge 3,2
for (int d=0; d<3; d++)
{
shape(4,d) = 0.5 * me0121[d] + qI02[d]
- sq3 * (0.5 * me1121[d] + qI12[d]) - 1.5 * tI121[d];
}
for (int d=0; d<3; d++)
{
shape(5,d) = 0.5 * me0121[d] + qI02[d]
+ sq3 * (0.5 * me1121[d] + qI12[d]) - 1.5 * tI121[d];
}
// Edge 0,3
for (int d=0; d<3; d++)
{
shape(6,d) = 0.5 * me0210[d] + qII02[d]
- sq3 * (0.5 * me1210[d] + qII12[d]) - 1.5 * tI210[d];
}
for (int d=0; d<3; d++)
{
shape(7,d) = 0.5 * me0210[d] + qII02[d]
+ sq3 * (0.5 * me1210[d] + qII12[d]) - 1.5 * tI210[d];
}
// Edge 0,4
for (int d=0; d<3; d++)
{
shape(8,d) = 0.5 * te01[d] - sq3 * 0.5 * te11[d]
- 1.5 * (tI120[d] + tII120[d] + tI210[d] + tII210[d]);
}
for (int d=0; d<3; d++)
{
shape(9,d) = 0.5 * te01[d] + sq3 * 0.5 * te11[d]
- 1.5 * (tI120[d] + tII120[d] + tI210[d] + tII210[d]);
}
// Edge 1,4
for (int d=0; d<3; d++)
{
shape(10,d) = 0.5 * te02[d] - sq3 * 0.5 * te12[d]
- 1.5 * (tII120[d] + tI211[d] + tII211[d]);
}
for (int d=0; d<3; d++)
{
shape(11,d) = 0.5 * te02[d] + sq3 * 0.5 * te12[d]
- 1.5 * (tII120[d] + tI211[d] + tII211[d]);
}
// Edge 2,4
for (int d=0; d<3; d++)
{
shape(12,d) = 0.5 * te03[d] - sq3 * 0.5 * te13[d]
- 1.5 * (tII211[d] + tII121[d]);
}
for (int d=0; d<3; d++)
{
shape(13,d) = 0.5 * te03[d] + sq3 * 0.5 * te13[d]
- 1.5 * (tII211[d] + tII121[d]);
}
// Edge 3,4
for (int d=0; d<3; d++)
{
shape(14,d) = 0.5 * te04[d] - sq3 * 0.5 * te14[d]
- 1.5 * (tI121[d] + tII121[d] + tII210[d]);
}
for (int d=0; d<3; d++)
{
shape(15,d) = 0.5 * te04[d] + sq3 * 0.5 * te14[d]
- 1.5 * (tI121[d] + tII121[d] + tII210[d]);
}
// Quadrilateral face
for (int d=0; d<3; d++)
{
shape(16,d) = -2. * qI02[d] + 2. * sq3 * qI12[d];
}
for (int d=0; d<3; d++)
{
shape(17,d) = -2. * qI02[d] - 2. * sq3 * qI12[d];
}
for (int d=0; d<3; d++)
{
shape(18,d) = 2. * qII02[d] - 2. * sq3 * qII12[d];
}
for (int d=0; d<3; d++)
{
shape(19,d) = 2. * qII02[d] + 2. * sq3 * qII12[d];
}
// Triangular face 0,1,4
for (int d=0; d<3; d++)
{
shape(20,d) = 3. * tI120[d] - 3. * tII120[d];
}
for (int d=0; d<3; d++)
{
shape(21,d) = 3. * tI120[d] + 6. * tII120[d];
}
// Triangular face 1,2,4
for (int d=0; d<3; d++)
{
shape(22,d) = 3. * tI211[d] - 3. * tII211[d];
}
for (int d=0; d<3; d++)
{
shape(23,d) = 3. * tI211[d] + 6. * tII211[d];
}
// Triangular face 2,3,4
for (int d=0; d<3; d++)
{
shape(24,d) = -6. * tI121[d] - 3. * tII121[d];
}
for (int d=0; d<3; d++)
{
shape(25,d) = 3. * tI121[d] + 6. * tII121[d];
}
// Triangular face 3,0,4
for (int d=0; d<3; d++)
{
shape(26,d) = -6. * tI210[d] - 3. * tII210[d];
}
for (int d=0; d<3; d++)
{
shape(27,d) = 3. * tI210[d] + 6. * tII210[d];
}
}
void Nedelec2PyrFiniteElement::CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape)
const
{
const real_t one = 1.0;
const real_t x = ip.x, y = ip.y, z = ip.z, z2 = 2. * z;
const real_t ox = one - x - z, oy = one - y - z, oz = one - z;
const real_t tol = 1e-6;
if (oz <= tol)
{
// We must return the limit of the basis function derivatives as z->1.
// In order to remain inside the pyramid in this limit the x and y
// coordinates must be approaching 0. The resulting limiting basis
// function values are:
curl_shape(0,0) = 0.;
curl_shape(0,1) = - 2.;
curl_shape(0,2) = 1.;
curl_shape(1,0) = 0.;
curl_shape(1,1) = 0.;
curl_shape(1,2) = 1.;
curl_shape(2,0) = 0.;
curl_shape(2,1) = 0.;
curl_shape(2,2) = - 1.;
curl_shape(3,0) = 2.;
curl_shape(3,1) = 0.;
curl_shape(3,2) = - 1.;
curl_shape(4,0) = - 2.;
curl_shape(4,1) = 2.;
curl_shape(4,2) = 0.;
curl_shape(5,0) = 0.;
curl_shape(5,1) = - 2.;
curl_shape(5,2) = 0.;
curl_shape(6,0) = 0.;
curl_shape(6,1) = 0.;
curl_shape(6,2) = 0.;
curl_shape(7,0) = 2.;
curl_shape(7,1) = 0.;
curl_shape(7,2) = 0.;
return;
}
real_t ozi = one / oz;
curl_shape(0,0) = - x * ozi;
curl_shape(0,1) = - 2. + y * ozi;
curl_shape(0,2) = 1.;
curl_shape(1,0) = x * ozi;
curl_shape(1,1) = - y * ozi;
curl_shape(1,2) = 1.;
curl_shape(2,0) = x * ozi;
curl_shape(2,1) = - y * ozi;
curl_shape(2,2) = - 1.;
curl_shape(3,0) = (2. - x - z2) * ozi;
curl_shape(3,1) = y * ozi;
curl_shape(3,2) = - 1.;
curl_shape(4,0) = - 2. * ox * ozi;
curl_shape(4,1) = 2. * oy * ozi;
curl_shape(4,2) = 0.;
curl_shape(5,0) = - 2. * x * ozi;
curl_shape(5,1) = - 2. * oy * ozi;
curl_shape(5,2) = 0.;
curl_shape(6,0) = 2. * x * ozi;
curl_shape(6,1) = - 2. * y * ozi;
curl_shape(6,2) = 0.;
curl_shape(7,0) = 2. * ox * ozi;
curl_shape(7,1) = 2. * y * ozi;
curl_shape(7,2) = 0.;
}
const real_t Nedelec2PyrFiniteElement::tk[28][3] =
{
{1,0,0}, {1,0,0}, {0,1,0}, {0,1,0},
{1,0,0}, {1,0,0}, {0,1,0}, {0,1,0},
{0,0,1}, {0,0,1}, {-1,0,1}, {-1,0,1},
{-1,-1,1}, {-1,-1,1}, {0,-1,1}, {0,-1,1},
{1,0,0}, {1,0,0}, {0,-1,0}, {0,-1,0},
{1,0,0}, {0,0,1}, {0,1,0}, {-1,0,1},
{-1,0,0}, {-1,-1,1}, {0,-1,0}, {0,-1,1}
};
void Nedelec2PyrFiniteElement::GetLocalInterpolation (
ElementTransformation &Trans, DenseMatrix &I) const
{
int k, j;
#ifdef MFEM_THREAD_SAFE
DenseMatrix vshape(dof, dim);
#endif
#ifdef MFEM_DEBUG
for (k = 0; k < dof; k++)
{
CalcVShape (Nodes.IntPoint(k), vshape);
for (j = 0; j < dof; j++)
{
real_t d = ( vshape(j,0)*tk[k][0] + vshape(j,1)*tk[k][1] +
vshape(j,2)*tk[k][2] );
if (j == k) { d -= 1.0; }
if (fabs(d) > 1.0e-12)
{
mfem::err << "Nedelec1PyrFiniteElement::GetLocalInterpolation (...)\n"
" k = " << k << ", j = " << j << ", d = " << d << endl;
mfem_error();
}
}
}
#endif
IntegrationPoint ip;
ip.x = ip.y = ip.z = 0.0;
Trans.SetIntPoint (&ip);
// Trans must be linear
const DenseMatrix &J = Trans.Jacobian();
real_t vk[3];
Vector xk (vk, 3);
for (k = 0; k < dof; k++)
{
Trans.Transform (Nodes.IntPoint (k), xk);
ip.x = vk[0]; ip.y = vk[1]; ip.z = vk[2];
CalcVShape (ip, vshape);
// vk = J tk
vk[0] = J(0,0)*tk[k][0]+J(0,1)*tk[k][1]+J(0,2)*tk[k][2];
vk[1] = J(1,0)*tk[k][0]+J(1,1)*tk[k][1]+J(1,2)*tk[k][2];
vk[2] = J(2,0)*tk[k][0]+J(2,1)*tk[k][1]+J(2,2)*tk[k][2];
for (j = 0; j < dof; j++)
if (fabs (I(k,j) = (vshape(j,0)*vk[0]+vshape(j,1)*vk[1]+
vshape(j,2)*vk[2])) < 1.0e-12)
{
I(k,j) = 0.0;
}
}
}
void Nedelec2PyrFiniteElement::Project (
VectorCoefficient &vc, ElementTransformation &Trans,
Vector &dofs) const
{
real_t vk[3];
Vector xk (vk, 3);
for (int k = 0; k < dof; k++)
{
Trans.SetIntPoint (&Nodes.IntPoint (k));
const DenseMatrix &J = Trans.Jacobian();
vc.Eval (xk, Trans, Nodes.IntPoint (k));
// xk^t J tk
dofs(k) =
vk[0] * ( J(0,0)*tk[k][0]+J(0,1)*tk[k][1]+J(0,2)*tk[k][2] ) +
vk[1] * ( J(1,0)*tk[k][0]+J(1,1)*tk[k][1]+J(1,2)*tk[k][2] ) +
vk[2] * ( J(2,0)*tk[k][0]+J(2,1)*tk[k][1]+J(2,2)*tk[k][2] );
}
}
void Nedelec2PyrFiniteElement::ProjectGrad(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &grad) const
{
DenseMatrix dshape(fe.GetDof(), 3);
Vector grad_k(fe.GetDof());
grad.SetSize(dof, fe.GetDof());
for (int k = 0; k < dof; k++)
{
fe.CalcDShape(Nodes.IntPoint(k), dshape);
dshape.Mult(tk[k], grad_k);
for (int j = 0; j < grad_k.Size(); j++)
{
grad(k,j) = (fabs(grad_k(j)) < 1e-12) ? 0.0 : grad_k(j);
}
}
}
RT0HexFiniteElement::RT0HexFiniteElement()
: VectorFiniteElement(3, Geometry::CUBE, 6, 1, H_DIV, FunctionSpace::Qk)
{
@@ -6488,8 +5950,7 @@ void RT0WdgFiniteElement::ProjectCurl(const FiniteElement &fe,
}
RT0PyrFiniteElement::RT0PyrFiniteElement(bool rt0tets)
: VectorFiniteElement(3, Geometry::PYRAMID, 5, 1, H_DIV, FunctionSpace::Uk),
rt0(rt0tets)
: VectorFiniteElement(3, Geometry::PYRAMID, 5, 1, H_DIV), rt0(rt0tets)
{
// not real nodes ...
Nodes.IntPoint(0).x = 0.5;
@@ -6612,7 +6073,7 @@ void RT0PyrFiniteElement::CalcDivShape(const IntegrationPoint &ip,
}
const real_t RT0PyrFiniteElement::nk[5][3] =
{{0.,0.,-1}, {0,-1,0}, {1,0,1}, {0,1,1}, {-1,0,0}};
{{0.,0.,-1.}, {0,-.5,0}, {.5,0,.5}, {0,.5,.5}, {-.5,0,0}};
void RT0PyrFiniteElement::GetLocalInterpolation (
ElementTransformation &Trans, DenseMatrix &I) const
-28
View File
@@ -1018,34 +1018,6 @@ public:
};
/// A 3D 2nd order Nedelec element on a pyramid
class Nedelec2PyrFiniteElement : public VectorFiniteElement
{
private:
static const real_t tk[28][3];
public:
/// Construct the Nedelec2PyrFiniteElement
Nedelec2PyrFiniteElement();
virtual void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const;
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_ND(Trans, shape); }
virtual void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const;
virtual void GetLocalInterpolation (ElementTransformation &Trans,
DenseMatrix &I) const;
using FiniteElement::Project;
virtual void Project (VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const;
virtual void ProjectGrad(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &grad) const;
};
/// A 3D 0th order Raviert-Thomas element on a cube
class RT0HexFiniteElement : public VectorFiniteElement
{
-825
View File
@@ -1040,829 +1040,4 @@ void H1_WedgeElement::CalcDShape(const IntegrationPoint &ip,
}
}
H1_FuentesPyramidElement::H1_FuentesPyramidElement(const int p, const int btype)
: NodalFiniteElement(3, Geometry::PYRAMID,
p * (p * p + 3) + 1, // Fuentes et al
p, FunctionSpace::Uk)
{
zmax = 0.0;
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
#ifndef MFEM_THREAD_SAFE
tmp_i.SetSize(p + 1);
tmp1_ij.SetSize(p + 1, p + 1);
tmp2_ij.SetSize(p + 1, dim);
tmp_ijk.SetSize(p + 1, p + 1, dim);
tmp_u.SetSize(dof);
tmp_du.SetSize(dof, dim);
#else
Vector tmp_i(p + 1);
DenseMatrix tmp1_ij(p + 1, p + 1);
#endif
// vertices
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
Nodes.IntPoint(2).Set3(cp[p], cp[p], cp[0]);
Nodes.IntPoint(3).Set3(cp[0], cp[p], cp[0]);
Nodes.IntPoint(4).Set3(cp[0], cp[0], cp[p]);
// edges
int o = 5;
for (int i = 1; i < p; i++) // (0,1)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
}
for (int i = 1; i < p; i++) // (1,2)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (3,2)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[0]);
}
for (int i = 1; i < p; i++) // (0,3)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (0,4)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (1,4)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (2,4)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[p-i], cp[i]);
}
for (int i = 1; i < p; i++) // (3,4)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
}
// quadrilateral face
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p-j], cp[0]);
}
}
// triangular faces
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0,1,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (1,2,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3((cp[i] + cp[p-i-j])/w, cp[i]/w, cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (2,3,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[p-i-j]/w, (cp[i] + cp[p-i-j])/w, cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (3,0,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i-j]/w, cp[j]/w);
}
// Points based on Fuentes' interior bubbles
for (int k = 1; k < p; k++)
{
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i] * (1.0 - cp[k]),
cp[j] * (1.0 - cp[k]),
cp[k]);
}
}
}
MFEM_ASSERT(o == dof,
"Number of nodes does not match the "
"number of degrees of freedom");
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
Vector col(T.GetColumn(m), dof);
calcBasis(order, ip, tmp_i, tmp1_ij, col);
}
Ti.Factor(T);
}
void H1_FuentesPyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector tmp_i(p + 1);
Vector tmp_u(dof);
DenseMatrix tmp1_ij(p + 1, p + 1);
#endif
calcBasis(p, ip, tmp_i, tmp1_ij, tmp_u);
Ti.Mult(tmp_u, shape);
}
void H1_FuentesPyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector tmp_i(p + 1);
DenseMatrix tmp1_ij(p + 1, p + 1);
DenseMatrix tmp2_ij(p + 1, dim);
DenseTensor tmp_ijk(p + 1, p + 1, dim);
DenseMatrix tmp_du(dof, dim);
#endif
calcGradBasis(p, ip, tmp_i, tmp2_ij, tmp1_ij, tmp_ijk, tmp_du);
Ti.Mult(tmp_du, dshape);
}
void H1_FuentesPyramidElement::CalcRawShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector tmp_i(p + 1);
DenseMatrix tmp1_ij(p + 1, p + 1);
#endif
calcBasis(p, ip, tmp_i, tmp1_ij, shape);
}
void H1_FuentesPyramidElement::CalcRawDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector tmp_i(p + 1);
DenseMatrix tmp1_ij(p + 1, p + 1);
DenseMatrix tmp2_ij(p + 1, dim);
DenseTensor tmp_ijk(p + 1, p + 1, dim);
#endif
calcGradBasis(p, ip, tmp_i, tmp2_ij, tmp1_ij, tmp_ijk, dshape);
}
void H1_FuentesPyramidElement::calcBasis(const int p,
const IntegrationPoint &ip,
Vector &phi_i, DenseMatrix &phi_ij,
Vector &u) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
zmax = std::max(z, zmax);
real_t mu;
int o = 0;
// Vertices
u[0] = lam1(x, y, z);
u[1] = lam2(x, y, z);
u[2] = lam3(x, y, z);
u[3] = lam4(x, y, z);
u[4] = lam5(x, y, z);
o += 5;
// Mixed edges (base edges)
if (CheckZ(z) && p >= 2)
{
// (a,b) = (1,2), c = 0
phi_E(p, nu01(z, xy, 1), phi_i);
mu = mu0(z, xy, 2);
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_i[i];
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_i[i];
}
// (a,b) = (2,1), c = 0
phi_E(p, nu01(z, xy, 2), phi_i);
mu = mu0(z, xy, 1);
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_i[i];
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_i[i];
}
}
else
{
for (int i = 0; i < 4 * (p - 1); i++, o++)
{
u[o] = 0.0;
}
}
// Triangle edges (upright edges)
if (p >= 2)
{
phi_E(p, lam15(x, y, z), phi_i);
for (int i = 2; i<= p; i++, o++)
{
u[o] = phi_i[i];
}
phi_E(p, lam25(x, y, z), phi_i);
for (int i = 2; i<= p; i++, o++)
{
u[o] = phi_i[i];
}
phi_E(p, lam35(x, y, z), phi_i);
for (int i = 2; i<= p; i++, o++)
{
u[o] = phi_i[i];
}
phi_E(p, lam45(x, y, z), phi_i);
for (int i = 2; i<= p; i++, o++)
{
u[o] = phi_i[i];
}
}
// Quadrilateral face
if (CheckZ(z) && p >= 2)
{
phi_Q(p, mu01(z, xy, 1), mu01(z, xy, 2), phi_ij);
mu = mu0(z);
for (int j = 2; j <= p; j++)
{
for (int i = 2; i <= p; i++, o++)
{
u[o] = mu * phi_ij(i,j);
}
}
}
else
{
for (int j = 2; j <= p; j++)
{
for (int i = 2; i <= p; i++, o++)
{
u[o] = 0.0;
}
}
}
// Triangular faces
if (CheckZ(z) && p >= 3)
{
// (a,b) = (1,2), c = 0
phi_T(p, nu012(z, xy, 1), phi_ij);
mu = mu0(z, xy, 2);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
{
u[o] = mu * phi_ij(i,j);
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
{
u[o] = mu * phi_ij(i,j);
}
// (a,b) = (2,1), c = 0
phi_T(p, nu012(z, xy, 2), phi_ij);
mu = mu0(z, xy, 1);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
{
u[o] = mu * phi_ij(i,j);
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
{
u[o] = mu * phi_ij(i,j);
}
}
else
{
for (int i = 0; i < 2 * (p - 1) * (p - 2); i++, o++)
{
u[o] = 0.0;
}
}
// Interior
if (CheckZ(z) && p >= 2)
{
phi_Q(p, mu01(z, xy, 1), mu01(z, xy, 2), phi_ij);
phi_E(p, mu01(z), phi_i);
for (int k = 2; k <= p; k++)
{
for (int j = 2; j <= p; j++)
{
for (int i = 2; i <= p; i++, o++)
{
u[o] = phi_ij(i,j) * phi_i(k);
}
}
}
}
else
{
for (int i = 0; i < (p - 1) * (p - 1) * (p - 1); i++, o++)
{
u[o]= 0.0;
}
}
}
void H1_FuentesPyramidElement::calcGradBasis(const int p,
const IntegrationPoint &ip,
Vector &phi_i,
DenseMatrix &dphi_i,
DenseMatrix &phi_ij,
DenseTensor &dphi_ij,
DenseMatrix &du) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
zmax = std::max(z, zmax);
real_t mu;
Vector dmu(3);
Vector dlam(3);
int o = 0;
// Vertices
dlam = grad_lam1(x, y, z);
for (int d=0; d<3; d++) { du(0, d) = dlam(d); }
dlam = grad_lam2(x, y, z);
for (int d=0; d<3; d++) { du(1, d) = dlam(d); }
dlam = grad_lam3(x, y, z);
for (int d=0; d<3; d++) { du(2, d) = dlam(d); }
dlam = grad_lam4(x, y, z);
for (int d=0; d<3; d++) { du(3, d) = dlam(d); }
dlam = grad_lam5(x, y, z);
for (int d=0; d<3; d++) { du(4, d) = dlam(d); }
o += 5;
// Mixed edges (base edges)
if (CheckZ(z) && p >= 2)
{
// (a,b) = (1,2), c = 0
phi_E(p, nu01(z, xy, 1), grad_nu01(z, xy, 1), phi_i, dphi_i);
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);;
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);;
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
}
// (a,b) = (2,1), c = 0
phi_E(p, nu01(z, xy, 2), grad_nu01(z, xy, 2), phi_i, dphi_i);
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);;
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);;
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_i[i] + mu * dphi_i(i, d);
}
}
else
{
for (int i = 0; i < 4 * (p - 1); i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = 0.0;
}
}
// Triangle edges (upright edges)
if (p >= 2)
{
phi_E(p, lam15(x, y, z), grad_lam15(x,y,z), phi_i, dphi_i);
for (int i = 2; i<= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dphi_i(i, d);
}
phi_E(p, lam25(x, y, z), grad_lam25(x, y, z), phi_i, dphi_i);
for (int i = 2; i<= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dphi_i(i, d);
}
phi_E(p, lam35(x, y, z), grad_lam35(x, y, z), phi_i, dphi_i);
for (int i = 2; i<= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dphi_i(i, d);
}
phi_E(p, lam45(x, y, z), grad_lam45(x, y, z), phi_i, dphi_i);
for (int i = 2; i<= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dphi_i(i, d);
}
}
// Quadrilateral face
if (CheckZ(z) && p >= 2)
{
phi_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu01(z, xy, 2), grad_mu01(z, xy, 2), phi_ij, dphi_ij);
mu = mu0(z);
dmu = grad_mu0(z);
for (int j = 2; j <= p; j++)
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
}
else
{
for (int j = 2; j <= p; j++)
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = 0.0;
}
}
// Triangular faces
if (CheckZ(z) && p >= 3)
{
// (a,b) = (1,2), c = 0
phi_T(p, nu012(z, xy, 1), grad_nu012(z, xy, 1), phi_ij, dphi_ij);
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
// (a,b) = (1,2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
// (a,b) = (2,1), c = 0
phi_T(p, nu012(z, xy, 2), grad_nu012(z, xy, 2), phi_ij, dphi_ij);
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
// (a,b) = (2,1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);
for (int i = 2; i < p; i++)
for (int j = 1; i + j <= p; j++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = dmu(d) * phi_ij(i, j) + mu * dphi_ij(i, j, d);
}
}
else
{
for (int i = 0; i < 2 * (p - 1) * (p - 2); i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = 0.0;
}
}
// Interior
if (CheckZ(z) && p >= 2)
{
phi_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu01(z, xy, 2), grad_mu01(z, xy, 2), phi_ij, dphi_ij);
phi_E(p, mu01(z), grad_mu01(z), phi_i, dphi_i);
for (int k = 2; k <= p; k++)
for (int j = 2; j <= p; j++)
for (int i = 2; i <= p; i++, o++)
for (int d=0; d<3; d++)
du(o, d) = dphi_ij(i, j, d) * phi_i(k) +
phi_ij(i, j) * dphi_i(k, d);
}
else
{
for (int i = 0; i < (p - 1) * (p - 1) * (p - 1); i++, o++)
for (int d=0; d<3; d++)
{
du(o, d) = 0.0;
}
}
}
H1_BergotPyramidElement::H1_BergotPyramidElement(const int p, const int btype)
: NodalFiniteElement(3, Geometry::PYRAMID,
(p + 1) * (p + 2) * (2 * p + 3) / 6, // Bergot (JSC)
p, FunctionSpace::Uk)
{
const real_t *cp = poly1d.ClosedPoints(p, VerifyNodal(VerifyClosed(btype)));
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(p + 1);
shape_y.SetSize(p + 1);
shape_z.SetSize(p + 1);
dshape_x.SetSize(p + 1);
dshape_y.SetSize(p + 1);
dshape_z.SetSize(p + 1);
dshape_z_dt.SetSize(p + 1);
ddshape_x.SetSize(p + 1);
ddshape_y.SetSize(p + 1);
ddshape_z.SetSize(p + 1);
u.SetSize(dof);
du.SetSize(dof, dim);
ddu.SetSize(dof, (dim * (dim + 1)) / 2);
#else
Vector shape_x(p + 1), shape_y(p + 1), shape_z(p + 1);
#endif
// vertices
Nodes.IntPoint(0).Set3(cp[0], cp[0], cp[0]);
Nodes.IntPoint(1).Set3(cp[p], cp[0], cp[0]);
Nodes.IntPoint(2).Set3(cp[p], cp[p], cp[0]);
Nodes.IntPoint(3).Set3(cp[0], cp[p], cp[0]);
Nodes.IntPoint(4).Set3(cp[0], cp[0], cp[p]);
// edges
int o = 5;
for (int i = 1; i < p; i++) // (0,1)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[0], cp[0]);
}
for (int i = 1; i < p; i++) // (1,2)
{
Nodes.IntPoint(o++).Set3(cp[p], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (3,2)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[p], cp[0]);
}
for (int i = 1; i < p; i++) // (0,3)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[i], cp[0]);
}
for (int i = 1; i < p; i++) // (0,4)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (1,4)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[0], cp[i]);
}
for (int i = 1; i < p; i++) // (2,4)
{
Nodes.IntPoint(o++).Set3(cp[p-i], cp[p-i], cp[i]);
}
for (int i = 1; i < p; i++) // (3,4)
{
Nodes.IntPoint(o++).Set3(cp[0], cp[p-i], cp[i]);
}
// quadrilateral face
for (int j = 1; j < p; j++)
{
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o++).Set3(cp[i], cp[j], cp[0]);
}
}
// triangular faces
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0,1,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[i]/w, cp[0], cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (1,2,4)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(1.0 - cp[j]/w, cp[i]/w, cp[j]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (3,4,2)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[j]/w, 1.0 - cp[i]/w, cp[i]/w);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0,4,3)
{
real_t w = cp[i] + cp[j] + cp[p-i-j];
Nodes.IntPoint(o++).Set3(cp[0], cp[j]/w, cp[i]/w);
}
// interior
for (int k = 1; k < p - 1; k++)
{
for (int j = 1; j < p - k; j++)
{
real_t wjk = cp[j] + cp[k] + cp[p-j-k];
for (int i = 1; i < p - k; i++)
{
real_t wik = cp[i] + cp[k] + cp[p-i-k];
real_t w = wik * wjk * cp[p-k];
Nodes.IntPoint(o++).Set3(cp[i] * (cp[j] + cp[p-j-k]) / w,
cp[j] * (cp[i] + cp[p-i-k]) / w,
cp[k] * cp[p-k] / w);
}
}
}
MFEM_ASSERT(o == dof,
"Number of nodes does not match the "
"number of degrees of freedom");
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
o = 0;
for (int i = 0; i <= p; i++)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0, shape_z);
for (int k = 0; k <= p - maxij; k++)
{
T(o++, m) = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
}
}
}
}
Ti.Factor(T);
}
void H1_BergotPyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(order+1);
Vector shape_y(order+1);
Vector shape_z(order+1);
Vector u(dof);
#endif
real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
shape_z);
for (int k = 0; k <= p - maxij; k++)
u[o++] = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
}
Ti.Mult(u, shape);
}
void H1_BergotPyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix du(dof, dim);
Vector shape_x(order+1);
Vector shape_y(order+1);
Vector shape_z(order+1);
Vector dshape_x(order+1);
Vector dshape_y(order+1);
Vector dshape_z(order+1);
Vector dshape_z_dt(order+1);
#endif
real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData(), dshape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData(), dshape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
shape_z, dshape_z, dshape_z_dt);
for (int k = 0; k <= p - maxij; k++, o++)
{
du(o,0) = dshape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,1) = shape_x(i) * dshape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,2) = shape_x(i) * shape_y(j) * dshape_z(k) *
pow(1.0 - ip.z, maxij) +
(ip.x * dshape_x(i) * shape_y(j) +
ip.y * shape_x(i) * dshape_y(j)) *
shape_z(k) * pow(1.0 - ip.z, maxij - 2) -
maxij * shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
}
}
Ti.Mult(du, dshape);
}
}
-68
View File
@@ -13,7 +13,6 @@
#define MFEM_FE_H1
#include "fe_base.hpp"
#include "fe_pyramid.hpp"
namespace mfem
{
@@ -149,73 +148,6 @@ public:
DenseMatrix &dshape) const override;
};
/** Arbitrary order H1 basis functions defined on pyramid-shaped elements
This implementation is closely based on the finite elements
described in section 9.1 of the paper "Orientation embedded high
order shape functions for the exact sequence elements of all shapes"
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
*/
class H1_FuentesPyramidElement
: public NodalFiniteElement, public FuentesPyramid
{
private:
mutable real_t zmax;
#ifndef MFEM_THREAD_SAFE
mutable Vector tmp_i, tmp_u;
mutable DenseMatrix tmp1_ij, tmp2_ij, tmp_du;
mutable DenseTensor tmp_ijk;
#endif
DenseMatrixInverse Ti;
void calcBasis(const int p, const IntegrationPoint &ip,
Vector &phi_i, DenseMatrix &phi_ij, Vector &u) const;
void calcGradBasis(const int p, const IntegrationPoint &ip,
Vector &phi_i, DenseMatrix &dphi_i,
DenseMatrix &phi_ij, DenseTensor &dphi_ij,
DenseMatrix &du) const;
public:
H1_FuentesPyramidElement(const int p,
const int btype = BasisType::GaussLobatto);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
void CalcRawShape(const IntegrationPoint &ip, Vector &shape) const;
void CalcRawDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
real_t GetZetaMax() const { return zmax; }
};
/** Arbitrary order H1 basis functions defined on pyramid-shaped elements
This implementation is based on the finite elements described in the
2010 paper "Higher-Order Finite Elements for Hybrid Meshes Using New
Nodal Pyramidal Elements" by Morgane Bergot, Gary Cohen, and Marc
Durufle, see https://hal.archives-ouvertes.fr/hal-00454261.
*/
class H1_BergotPyramidElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z;
mutable Vector dshape_x, dshape_y, dshape_z, dshape_z_dt, u;
mutable Vector ddshape_x, ddshape_y, ddshape_z;
mutable DenseMatrix du, ddu;
#endif
DenseMatrixInverse Ti;
public:
H1_BergotPyramidElement(const int p,
const int btype = BasisType::GaussLobatto);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
};
} // namespace mfem
#endif
-317
View File
@@ -923,321 +923,4 @@ void L2_WedgeElement::CalcDShape(const IntegrationPoint &ip,
}
}
L2_FuentesPyramidElement::L2_FuentesPyramidElement(const int p, const int btype)
: NodalFiniteElement(3, Geometry::PYRAMID, ((p + 1)*(p + 1)*(p + 1)),
p, FunctionSpace::Uk)
{
const real_t *op = poly1d.OpenPoints(p, VerifyOpen(btype));
// These basis functions are not independent on a closed set of
// interpolation points when p >= 1. For this reason we force the points
// to be open in the z direction whenever closed points are requested.
// This should be regarded as a limitation of this choice of basis function.
// If a truly closed set of points is needed consider using
// L2_BergotPyramidElement instead.
real_t a = 1.0;
if (IsClosedType(btype) && p > 0)
{
a = (poly1d.GetPoints(p, BasisType::GaussLegendre))[p];
}
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(p + 1);
shape_y.SetSize(p + 1);
shape_z.SetSize(p + 1);
dshape_x.SetSize(p + 1);
dshape_y.SetSize(p + 1);
dshape_z.SetSize(p + 1);
u.SetSize(dof);
du.SetSize(dof, dim);
#else
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
#endif
int o = 0;
for (int k = 0; k <= p; k++)
for (int j = 0; j <= p; j++)
for (int i = 0; i <= p; i++)
{
Nodes.IntPoint(o++).Set3(op[i] * (1.0 - a * op[k]),
op[j] * (1.0 - a * op[k]),
a * op[k]);
}
MFEM_ASSERT(o == dof,
"Number of nodes does not match the "
"number of degrees of freedom");
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
CalcHomogenizedScaLegendre(p, mu0(z, xy, 1), mu1(z, xy, 1), shape_x);
CalcHomogenizedScaLegendre(p, mu0(z, xy, 2), mu1(z, xy, 2), shape_y);
CalcHomogenizedScaLegendre(p, mu0(z), mu1(z), shape_z);
o = 0;
for (int k = 0; k <= p; k++)
{
for (int j = 0; j <= p; j++)
{
for (int i = 0; i <= p; i++, o++)
{
T(o, m) = shape_x[i] * shape_y[j] * shape_z[k];
}
}
}
}
Ti.Factor(T);
}
void L2_FuentesPyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector u(dof);
#endif
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y});
if (z < 1.0)
{
CalcHomogenizedScaLegendre(p, mu0(z, xy, 1), mu1(z, xy, 1), shape_x);
CalcHomogenizedScaLegendre(p, mu0(z, xy, 2), mu1(z, xy, 2), shape_y);
}
else
{
shape_x = 0.0; shape_x(0) = 1.0;
shape_y = 0.0; shape_y(0) = 1.0;
}
CalcHomogenizedScaLegendre(p, mu0(z), mu1(z), shape_z);
int o = 0;
for (int k = 0; k <= p; k++)
for (int j = 0; j <= p; j++)
for (int i = 0; i <= p; i++, o++)
{
u[o] = shape_x[i] * shape_y[j] * shape_z[k];
}
Ti.Mult(u, shape);
}
void L2_FuentesPyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector dshape_x(p + 1);
Vector dshape_y(p + 1);
Vector dshape_z(p + 1);
DenseMatrix du(dof, dim);
#endif
Poly_1D::CalcLegendre(p, ip.x / (1.0 - ip.z), shape_x.GetData(),
dshape_x.GetData());
Poly_1D::CalcLegendre(p, ip.y / (1.0 - ip.z), shape_y.GetData(),
dshape_y.GetData());
Poly_1D::CalcLegendre(p, ip.z, shape_z.GetData(), dshape_z.GetData());
int o = 0;
for (int k = 0; k <= p; k++)
for (int j = 0; j <= p; j++)
for (int i = 0; i <= p; i++, o++)
{
du(o, 0) = dshape_x[i] * shape_y[j] * shape_z[k] / (1.0 - ip.z);
du(o, 1) = shape_x[i] * dshape_y[j] * shape_z[k] / (1.0 - ip.z);
du(o, 2) = shape_x[i] * shape_y[j] * dshape_z[k] +
(ip.x * dshape_x[i] * shape_y[j] +
ip.y * shape_x[i] * dshape_y[j]) *
shape_z[k] / pow(1.0 - ip.z, 2);
}
Ti.Mult(du, dshape);
}
L2_BergotPyramidElement::L2_BergotPyramidElement(const int p, const int btype)
: NodalFiniteElement(3, Geometry::PYRAMID, (p + 1)*(p + 2)*(2*p + 3)/6,
p, FunctionSpace::Pk)
{
const real_t *op = poly1d.OpenPoints(p, VerifyOpen(btype));
#ifndef MFEM_THREAD_SAFE
shape_x.SetSize(p + 1);
shape_y.SetSize(p + 1);
shape_z.SetSize(p + 1);
dshape_x.SetSize(p + 1);
dshape_y.SetSize(p + 1);
dshape_z.SetSize(p + 1);
dshape_z_dt.SetSize(p + 1);
u.SetSize(dof);
du.SetSize(dof, dim);
#else
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector dshape_z_dt(p + 1);
#endif
int o = 0;
for (int k = 0; k <= p; k++)
for (int j = 0; j <= p - k; j++)
{
const real_t wjk = op[j] + op[k] + op[p-j-k];
for (int i = 0; i <= p - k; i++)
{
const real_t wik = op[i] + op[k] + op[p-i-k];
const real_t w = wik * wjk * op[p-k];
Nodes.IntPoint(o++).Set3(op[i] * (op[j] + op[p-j-k]) / w,
op[j] * (op[j] + op[p-j-k]) / w,
op[k] * op[p-k] / w);
}
}
MFEM_ASSERT(o == dof,
"Number of nodes does not match the "
"number of degrees of freedom");
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
const real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
o = 0;
for (int i = 0; i <= p; i++)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0),
z, 1.0, shape_z);
for (int k = 0; k <= p - maxij; k++)
{
T(o++, m) = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
}
}
}
}
Ti.Factor(T);
}
void L2_BergotPyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector u(dof);
#endif
const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
const real_t z = ip.z;
poly1d.CalcLegendre(p, x, shape_x.GetData());
poly1d.CalcLegendre(p, y, shape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
shape_z);
for (int k = 0; k <= p - maxij; k++)
{
u[o++] = shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij);
}
}
}
Ti.Mult(u, shape);
}
void L2_BergotPyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
Vector shape_x(p + 1);
Vector shape_y(p + 1);
Vector shape_z(p + 1);
Vector dshape_x(p + 1);
Vector dshape_y(p + 1);
Vector dshape_z(p + 1);
Vector dshape_z_dt(p + 1);
DenseMatrix du(dof, dim);
#endif
const real_t x = (ip.z < 1.0) ? (ip.x / (1.0 - ip.z)) : 0.0;
const real_t y = (ip.z < 1.0) ? (ip.y / (1.0 - ip.z)) : 0.0;
const real_t z = ip.z;
Poly_1D::CalcLegendre(p, x, shape_x.GetData(), dshape_x.GetData());
Poly_1D::CalcLegendre(p, y, shape_y.GetData(), dshape_y.GetData());
int o = 0;
for (int i = 0; i <= p; i++)
{
for (int j = 0; j <= p; j++)
{
int maxij = std::max(i, j);
FuentesPyramid::CalcScaledJacobi(p-maxij, 2.0 * (maxij + 1.0), z, 1.0,
shape_z, dshape_z, dshape_z_dt);
for (int k = 0; k <= p - maxij; k++, o++)
{
du(o,0) = dshape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,1) = shape_x(i) * dshape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1);
du(o,2) = shape_x(i) * shape_y(j) * dshape_z(k) *
pow(1.0 - ip.z, maxij) +
(ip.x * dshape_x(i) * shape_y(j) +
ip.y * shape_x(i) * dshape_y(j)) *
shape_z(k) * pow(1.0 - ip.z, maxij - 2) -
((maxij > 0) ? (maxij * shape_x(i) * shape_y(j) * shape_z(k) *
pow(1.0 - ip.z, maxij - 1)) : 0.0);
}
}
}
Ti.Mult(du, dshape);
}
}
-51
View File
@@ -13,7 +13,6 @@
#define MFEM_FE_L2
#include "fe_base.hpp"
#include "fe_pyramid.hpp"
namespace mfem
{
@@ -184,56 +183,6 @@ public:
DenseMatrix &dshape) const override;
};
/** Arbitrary order L2 basis functions defined on pyramid-shaped elements
This implementation is closely based on the finite elements
described in section 9.4 of the paper "Orientation embedded high
order shape functions for the exact sequence elements of all shapes"
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
*/
class L2_FuentesPyramidElement
: public NodalFiniteElement, public FuentesPyramid
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z;
mutable Vector dshape_x, dshape_y, dshape_z;
mutable Vector u;
mutable DenseMatrix du;
#endif
DenseMatrixInverse Ti;
public:
/// Construct the L2_PyramidElement of order @a p and BasisType @a btype
L2_FuentesPyramidElement(const int p,
const int btype = BasisType::GaussLegendre);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
};
/// Arbitrary order L2 elements in 3D on a pyramid
class L2_BergotPyramidElement : public NodalFiniteElement
{
private:
#ifndef MFEM_THREAD_SAFE
mutable Vector shape_x, shape_y, shape_z;
mutable Vector dshape_x, dshape_y, dshape_z, dshape_z_dt;
mutable Vector u;
mutable DenseMatrix du;
#endif
DenseMatrixInverse Ti;
public:
/// Construct the L2_PyramidElement of order @a p and BasisType @a btype
L2_BergotPyramidElement(const int p,
const int btype = BasisType::GaussLegendre);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
};
} // namespace mfem
#endif
-948
View File
@@ -1581,954 +1581,6 @@ void ND_WedgeElement::CalcCurlShape(const IntegrationPoint &ip,
}
}
const real_t ND_FuentesPyramidElement::tk[27] =
{
1., 0., 0., 0., 1., 0., 0., 0., 1.,
-1., 0., 1., -1.,-1., 1., 0.,-1., 1.,
-1., 0., 0., 0.,-1., 0., -M_SQRT1_2,-M_SQRT1_2,M_SQRT2
};
ND_FuentesPyramidElement::ND_FuentesPyramidElement(const int p,
const int cb_type,
const int ob_type)
: VectorFiniteElement(3, Geometry::PYRAMID, p * (3 * p * p + 5), p,
H_CURL, FunctionSpace::Uk),
dof2tk(dof), doftrans(p)
{
zmax = 0.0;
const real_t *eop = poly1d.OpenPoints(p - 1);
const real_t *top = (p > 1) ? poly1d.OpenPoints(p - 2) : NULL;
const real_t *qop = poly1d.OpenPoints(p - 1, ob_type);
const real_t *qcp = poly1d.ClosedPoints(p, cb_type);
const int pm2 = p - 2;
#ifndef MFEM_THREAD_SAFE
tmp_E_E_ij.SetSize(p, dim);
tmp_dE_E_ij.SetSize(p, dim);
tmp_E_Q1_ijk.SetSize(p, p + 1, dim);
tmp_dE_Q1_ijk.SetSize(p, p + 1, dim);
tmp_E_Q2_ijk.SetSize(p, p + 1, dim);
tmp_dE_Q2_ijk.SetSize(p, p + 1, dim);
tmp_E_T_ijk.SetSize(p - 1, p, dim);
tmp_dE_T_ijk.SetSize(p - 1, p, dim);
tmp_phi_Q1_ij.SetSize(p + 1, p + 1);
tmp_dphi_Q1_ij.SetSize(p + 1, p + 1, dim);
tmp_phi_Q2_ij.SetSize(p + 1, p + 1);
tmp_dphi_Q2_ij.SetSize(p + 1, p + 1, dim);
tmp_phi_E_i.SetSize(p + 1);
tmp_dphi_E_i.SetSize(p + 1, dim);
u.SetSize(dof, dim);
curlu.SetSize(dof, dim);
#else
DenseMatrix tmp_E_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseTensor tmp_dE_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q1_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q1_ij(p + 1, p + 1, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
DenseMatrix u(dof, dim);
#endif
int o = 0;
// edges
for (int i = 0; i < p; i++) // (0, 1)
{
Nodes.IntPoint(o).Set3(eop[i], 0., 0.);
dof2tk[o++] = 0;
}
for (int i = 0; i < p; i++) // (1, 2)
{
Nodes.IntPoint(o).Set3(1., eop[i], 0.);
dof2tk[o++] = 1;
}
for (int i = 0; i < p; i++) // (3, 2)
{
Nodes.IntPoint(o).Set3(eop[i], 1., 0.);
dof2tk[o++] = 0;
}
for (int i = 0; i < p; i++) // (0, 3)
{
Nodes.IntPoint(o).Set3(0., eop[i], 0.);
dof2tk[o++] = 1;
}
for (int i = 0; i < p; i++) // (0, 4)
{
Nodes.IntPoint(o).Set3(0., 0., eop[i]);
dof2tk[o++] = 2;
}
for (int i = 0; i < p; i++) // (1, 4)
{
Nodes.IntPoint(o).Set3(1. - eop[i], 0., eop[i]);
dof2tk[o++] = 3;
}
for (int i = 0; i < p; i++) // (2, 4)
{
Nodes.IntPoint(o).Set3(1. - eop[i], 1. - eop[i], eop[i]);
dof2tk[o++] = 4;
}
for (int i = 0; i < p; i++) // (3, 4)
{
Nodes.IntPoint(o).Set3(0., 1. - eop[i], eop[i]);
dof2tk[o++] = 5;
}
// quadrilateral face (3, 2, 1, 0)
// x-components
for (int j = 1; j < p; j++)
for (int i = 0; i < p; i++)
{
Nodes.IntPoint(o).Set3(qop[i], qcp[p-j], 0.);
dof2tk[o++] = 0; // (1 0 0)
}
// y-components
for (int j = 0; j < p; j++)
for (int i = 1; i < p; i++)
{
Nodes.IntPoint(o).Set3(qcp[i], qop[p-1-j], 0.);
dof2tk[o++] = 7; // (0 -1 0)
}
// triangular faces
for (int j = 0; j <= pm2; j++) // (0, 1, 4)
for (int i = 0; i + j <= pm2; i++)
{
real_t w = top[i] + top[j] + top[pm2-i-j];
Nodes.IntPoint(o).Set3(top[i]/w, 0., top[j]/w);
dof2tk[o++] = 0;
Nodes.IntPoint(o).Set3(top[i]/w, 0., top[j]/w);
dof2tk[o++] = 2;
}
for (int j = 0; j <= pm2; j++) // (1, 2, 4)
for (int i = 0; i + j <= pm2; i++)
{
real_t w = top[i] + top[j] + top[pm2-i-j];
Nodes.IntPoint(o).Set3((top[i] + top[pm2-i-j])/w, top[i]/w, top[j]/w);
dof2tk[o++] = 1;
Nodes.IntPoint(o).Set3((top[i] + top[pm2-i-j])/w, top[i]/w, top[j]/w);
dof2tk[o++] = 3;
}
for (int j = 0; j <= pm2; j++) // (2, 3, 4)
for (int i = 0; i + j <= pm2; i++)
{
real_t w = top[i] + top[j] + top[pm2-i-j];
Nodes.IntPoint(o).Set3(top[pm2-i-j]/w, (top[i] + top[pm2-i-j])/w,
top[j]/w);
dof2tk[o++] = 6;
Nodes.IntPoint(o).Set3(top[pm2-i-j]/w, (top[i] + top[pm2-i-j])/w,
top[j]/w);
dof2tk[o++] = 4;
}
for (int j = 0; j <= pm2; j++) // (3, 0, 4)
for (int i = 0; i + j <= pm2; i++)
{
real_t w = top[i] + top[j] + top[pm2-i-j];
Nodes.IntPoint(o).Set3(0., top[pm2-i-j]/w, top[j]/w);
dof2tk[o++] = 7;
Nodes.IntPoint(o).Set3(0., top[pm2-i-j]/w, top[j]/w);
dof2tk[o++] = 5;
}
// interior
// x-components
for (int k = 1; k < p; k++)
for (int j = 1; j < p; j++)
for (int i = 0; i < p; i++)
{
real_t w = 1.0 - qcp[k];
Nodes.IntPoint(o).Set3(qop[i]*w, qcp[j]*w, qcp[k]);
dof2tk[o++] = 0;
}
// y-components
for (int k = 1; k < p; k++)
for (int j = 0; j < p; j++)
for (int i = 1; i < p; i++)
{
real_t w = 1.0 - qcp[k];
Nodes.IntPoint(o).Set3(qcp[i]*w, qop[j]*w, qcp[k]);
dof2tk[o++] = 1;
}
// z-components
for (int k = 0; k < p; k++)
for (int j = 1; j < p; j++)
for (int i = 1; i < p; i++)
{
real_t w = 1.0 - qop[k];
Nodes.IntPoint(o).Set3(qcp[i]*w, qcp[j]*w, qop[k]);
dof2tk[o++] = 8;
}
DenseMatrix T(dof);
for (int m = 0; m < dof; m++)
{
const IntegrationPoint &ip = Nodes.IntPoint(m);
calcBasis(p, ip, tmp_E_E_ij, tmp_E_Q1_ijk, tmp_E_Q2_ijk, tmp_E_T_ijk,
tmp_phi_Q1_ij, tmp_dphi_Q1_ij, tmp_phi_Q2_ij,
tmp_phi_E_i, tmp_dphi_E_i, u);
const Vector tm({tk[3*dof2tk[m]], tk[3*dof2tk[m]+1], tk[3*dof2tk[m]+2]});
u.Mult(tm, T.GetColumn(m));
}
Ti.Factor(T);
}
void ND_FuentesPyramidElement::CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix tmp_E_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q1_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q1_ij(p + 1, p + 1, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
DenseMatrix u(dof, dim);
#endif
calcBasis(p, ip, tmp_E_E_ij, tmp_E_Q1_ijk, tmp_E_Q2_ijk, tmp_E_T_ijk,
tmp_phi_Q1_ij, tmp_dphi_Q1_ij, tmp_phi_Q2_ij,
tmp_phi_E_i, tmp_dphi_E_i, u);
Ti.Mult(u, shape);
}
void ND_FuentesPyramidElement::CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix tmp_E_E_ij(p, dim);
DenseMatrix tmp_dE_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseTensor tmp_dE_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q2_ij(p + 1, p + 1, dim);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
DenseMatrix curlu(dof, dim);
#endif
calcCurlBasis(p, ip, tmp_E_E_ij, tmp_dE_E_ij, tmp_E_Q1_ijk, tmp_dE_Q1_ijk,
tmp_E_Q2_ijk, tmp_dE_Q2_ijk, tmp_E_T_ijk, tmp_dE_T_ijk,
tmp_phi_Q2_ij, tmp_dphi_Q2_ij, tmp_phi_E_i, tmp_dphi_E_i,
curlu);
Ti.Mult(curlu, curl_shape);
}
void ND_FuentesPyramidElement::CalcRawVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix tmp_E_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q1_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q1_ij(p + 1, p + 1, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
#endif
calcBasis(p, ip, tmp_E_E_ij, tmp_E_Q1_ijk, tmp_E_Q2_ijk, tmp_E_T_ijk,
tmp_phi_Q1_ij, tmp_dphi_Q1_ij, tmp_phi_Q2_ij,
tmp_phi_E_i, tmp_dphi_E_i, shape);
}
void ND_FuentesPyramidElement::CalcRawCurlShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
const int p = order;
#ifdef MFEM_THREAD_SAFE
DenseMatrix tmp_E_E_ij(p, dim);
DenseMatrix tmp_dE_E_ij(p, dim);
DenseTensor tmp_E_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q1_ijk(p, p + 1, dim);
DenseTensor tmp_E_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_dE_Q2_ijk(p, p + 1, dim);
DenseTensor tmp_E_T_ijk(p - 1, p, dim);
DenseTensor tmp_dE_T_ijk(p - 1, p, dim);
DenseMatrix tmp_phi_Q2_ij(p + 1, p + 1);
DenseTensor tmp_dphi_Q2_ij(p + 1, p + 1, dim);
Vector tmp_phi_E_i(p + 1);
DenseMatrix tmp_dphi_E_i(p + 1, dim);
#endif
calcCurlBasis(p, ip, tmp_E_E_ij, tmp_dE_E_ij, tmp_E_Q1_ijk, tmp_dE_Q1_ijk,
tmp_E_Q2_ijk, tmp_dE_Q2_ijk, tmp_E_T_ijk, tmp_dE_T_ijk,
tmp_phi_Q2_ij, tmp_dphi_Q2_ij, tmp_phi_E_i, tmp_dphi_E_i,
dshape);
}
void ND_FuentesPyramidElement::calcBasis(const int p,
const IntegrationPoint &ip,
DenseMatrix & E_E_ik,
DenseTensor & E_Q1_ijk,
DenseTensor & E_Q2_ijk,
DenseTensor & E_T_ijk,
DenseMatrix & phi_Q1_ij,
DenseTensor & dphi_Q1_ij,
DenseMatrix & phi_Q2_ij,
Vector & phi_E_k,
DenseMatrix & dphi_E_k,
DenseMatrix &W) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y}), dmu(3);
real_t mu, mu2;
if (std::fabs(1.0 - z) < apex_tol)
{
z = 1.0 - apex_tol;
y = 0.5 * (1.0 - z);
x = 0.5 * (1.0 - z);
xy(0) = x; xy(1) = y;
}
zmax = std::max(z, zmax);
W = 0.0;
int o = 0;
// Mixed Edges
if (z < 1.0)
{
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
E_E(p, nu01(z, xy, 1), nu01_grad_nu01(z, xy, 1), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_E_ik(i, k);
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_E_ik(i, k);
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
E_E(p, nu01(z, xy, 2), nu01_grad_nu01(z, xy, 2), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_E_ik(i, k);
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_E_ik(i, k);
}
}
// Triangle Edges
if (z < 1.0)
{
E_E(p, lam15(x, y, z), lam15_grad_lam15(x, y, z), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = E_E_ik(i, k);
}
E_E(p, lam25(x, y, z), lam25_grad_lam25(x, y, z), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = E_E_ik(i, k);
}
E_E(p, lam35(x, y, z), lam35_grad_lam35(x, y, z), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = E_E_ik(i, k);
}
E_E(p, lam45(x, y, z), lam45_grad_lam45(x, y, z), E_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = E_E_ik(i, k);
}
}
// Quadrilateral Face
if (z < 1.0 && p >= 2)
{
mu = mu0(z);
mu2 = mu * mu;
// Family I
E_Q(p, mu01(z, xy, 1), mu01_grad_mu01(z, xy, 1), mu01(z, xy, 2),
E_Q1_ijk);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu2 * E_Q1_ijk(i, j, k);
}
// Family II
E_Q(p, mu01(z, xy, 2), mu01_grad_mu01(z, xy, 2), mu01(z, xy, 1),
E_Q2_ijk);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu2 * E_Q2_ijk(i, j, k);
}
}
// Triangular Faces
if (z < 1.0 && p >= 2)
{
// Family I
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
E_T(p, nu012(z, xy, 1), nu01_grad_nu01(z, xy, 1), E_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
E_T(p, nu012(z, xy, 2), nu01_grad_nu01(z, xy, 2), E_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// Family II
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
E_T(p, nu120(z, xy, 1), nu12_grad_nu12(z, xy, 1), E_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
E_T(p, nu120(z, xy, 2), nu12_grad_nu12(z, xy, 2), E_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
for (int k=0; k<3; k++)
{
W(o, k) = mu * E_T_ijk(i, j, k);
}
}
// Interior
if (z < 1.0 && p >= 2)
{
// Family I
phi_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1), mu01(z, xy, 2),
grad_mu01(z, xy, 2), phi_Q1_ij, dphi_Q1_ij);
phi_E(p, mu01(z), grad_mu01(z), phi_E_k, dphi_E_k);
for (int k=2; k<=p; k++)
for (int j=2; j<=p; j++)
for (int i=2; i<=p; i++, o++)
for (int l=0; l<3; l++)
W(o, l) = dphi_Q1_ij(i, j, l) * phi_E_k(k) +
phi_Q1_ij(i, j) * dphi_E_k(k, l);
// Family II
mu = mu0(z);
for (int k=2; k<=p; k++)
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
for (int l=0; l<3; l++)
{
W(o, l) = mu * E_Q1_ijk(i, j, l) * phi_E_k(k);
}
// Family III
for (int k=2; k<=p; k++)
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
for (int l=0; l<3; l++)
{
W(o, l) = mu * E_Q2_ijk(i, j, l) * phi_E_k(k);
}
// Family IV
// Re-using mu from Family II
dmu = grad_mu0(z);
phi_Q(p, mu01(z, xy, 2), mu01(z, xy, 1), phi_Q2_ij);
for (int j=2; j<=p; j++)
for (int i=2; i<=p; i++, o++)
{
const int n = std::max(i,j);
const real_t nmu = n * pow(mu, n-1);
for (int l=0; l<3; l++)
{
W(o, l) = nmu * phi_Q2_ij(i, j) * dmu(l);
}
}
}
}
void ND_FuentesPyramidElement::calcCurlBasis(const int p,
const IntegrationPoint &ip,
DenseMatrix & E_E_ik,
DenseMatrix & dE_E_ik,
DenseTensor & E_Q1_ijk,
DenseTensor & dE_Q1_ijk,
DenseTensor & E_Q2_ijk,
DenseTensor & dE_Q2_ijk,
DenseTensor & E_T_ijk,
DenseTensor & dE_T_ijk,
DenseMatrix & phi_Q2_ij,
DenseTensor & dphi_Q2_ij,
Vector & phi_E_k,
DenseMatrix & dphi_E_k,
DenseMatrix & dW) const
{
real_t x = ip.x;
real_t y = ip.y;
real_t z = ip.z;
Vector xy({x,y}), dmu(3);
Vector dmuxE(3), E(3), dphi(3), muphi(3);
real_t mu, mu2;
if (std::fabs(1.0 - z) < apex_tol)
{
z = 1.0 - apex_tol;
y = 0.5 * (1.0 - z);
x = 0.5 * (1.0 - z);
xy(0) = x; xy(1) = y;
}
zmax = std::max(z, zmax);
dW = 0.0;
int o = 0;
// Mixed Edges
if (z < 1.0)
{
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);
E_E(p, nu01(z, xy, 1), grad_nu01(z, xy, 1), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
{
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
}
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);
for (int i=0; i<p; i++, o++)
{
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);
E_E(p, nu01(z, xy, 2), grad_nu01(z, xy, 2), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
{
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);
for (int i=0; i<p; i++, o++)
{
E(0) = E_E_ik(i, 0); E(1) = E_E_ik(i, 1); E(2) = E_E_ik(i, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_E_ik(i, k) + dmuxE(k);
}
}
}
// Triangle Edges
if (z < 1.0)
{
E_E(p, lam15(x, y, z), grad_lam15(x, y, z), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
dW(o, k) = dE_E_ik(i, k);
}
E_E(p, lam25(x, y, z), grad_lam25(x, y, z), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
dW(o, k) = dE_E_ik(i, k);
}
E_E(p, lam35(x, y, z), grad_lam35(x, y, z), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
dW(o, k) = dE_E_ik(i, k);
}
E_E(p, lam45(x, y, z), grad_lam45(x, y, z), E_E_ik, dE_E_ik);
for (int i=0; i<p; i++, o++)
for (int k=0; k<3; k++)
{
dW(o, k) = dE_E_ik(i, k);
}
}
// Quadrilateral Face
if (z < 1.0 && p >= 2)
{
mu = mu0(z);
mu2 = mu * mu;
dmu = grad_mu0(z);
// Family I
E_Q(p, mu01(z, xy, 1), grad_mu01(z, xy, 1),
mu01(z, xy, 2), grad_mu01(z, xy, 2), E_Q1_ijk, dE_Q1_ijk);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_Q1_ijk(i, j, 0);
E(1) = E_Q1_ijk(i, j, 1);
E(2) = E_Q1_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu2 * dE_Q1_ijk(i, j, k) + 2.0 * mu * dmuxE(k);
}
}
// Family II
E_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2),
mu01(z, xy, 1), grad_mu01(z, xy, 1), E_Q2_ijk, dE_Q2_ijk);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_Q2_ijk(i, j, 0);
E(1) = E_Q2_ijk(i, j, 1);
E(2) = E_Q2_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu2 * dE_Q2_ijk(i, j, k) + 2.0 * mu * dmuxE(k);
}
}
}
// Triangular Faces
if (z < 1.0 && p >= 2)
{
// Family I
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);
E_T(p, nu012(z, xy, 1), grad_nu012(z, xy, 1), E_T_ijk, dE_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);
E_T(p, nu012(z, xy, 2), grad_nu012(z, xy, 2), E_T_ijk, dE_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// Family II
// (a, b) = (1, 2), c = 0
mu = mu0(z, xy, 2);
dmu = grad_mu0(z, xy, 2);
E_T(p, nu120(z, xy, 1), grad_nu120(z, xy, 1), E_T_ijk, dE_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (1, 2), c = 1
mu = mu1(z, xy, 2);
dmu = grad_mu1(z, xy, 2);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 0
mu = mu0(z, xy, 1);
dmu = grad_mu0(z, xy, 1);
E_T(p, nu120(z, xy, 2), grad_nu120(z, xy, 2), E_T_ijk, dE_T_ijk);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
// (a, b) = (2, 1), c = 1
mu = mu1(z, xy, 1);
dmu = grad_mu1(z, xy, 1);
for (int j=1; j<p; j++)
for (int i=0; i+j<p; i++, o++)
{
E(0) = E_T_ijk(i, j, 0);
E(1) = E_T_ijk(i, j, 1);
E(2) = E_T_ijk(i, j, 2);
dmu.cross3D(E, dmuxE);
for (int k=0; k<3; k++)
{
dW(o, k) = mu * dE_T_ijk(i, j, k) + dmuxE(k);
}
}
}
// Interior
if (z < 1.0 && p >= 2)
{
// Family I
// Curl is zero so skip these functions
o += (p - 1) * (p - 1) * (p - 1);
// Family II
mu = mu0(z);
dmu = grad_mu0(z);
phi_E(p, mu01(z), grad_mu01(z), phi_E_k, dphi_E_k);
for (int k=2; k<=p; k++)
{
dphi(0) = dphi_E_k(k, 0);
dphi(1) = dphi_E_k(k, 1);
dphi(2) = dphi_E_k(k, 2);
add(mu, dphi, phi_E_k(k), dmu, muphi);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_Q1_ijk(i, j, 0);
E(1) = E_Q1_ijk(i, j, 1);
E(2) = E_Q1_ijk(i, j, 2);
muphi.cross3D(E, dmuxE);
for (int l=0; l<3; l++)
{
dW(o, l) = mu * dE_Q1_ijk(i, j, l) * phi_E_k(k) + dmuxE(l);
}
}
}
// Family III
for (int k=2; k<=p; k++)
{
dphi(0) = dphi_E_k(k, 0);
dphi(1) = dphi_E_k(k, 1);
dphi(2) = dphi_E_k(k, 2);
add(mu, dphi, phi_E_k(k), dmu, muphi);
for (int j=2; j<=p; j++)
for (int i=0; i<p; i++, o++)
{
E(0) = E_Q2_ijk(i, j, 0);
E(1) = E_Q2_ijk(i, j, 1);
E(2) = E_Q2_ijk(i, j, 2);
muphi.cross3D(E, dmuxE);
for (int l=0; l<3; l++)
{
dW(o, l) = mu * dE_Q2_ijk(i, j, l) * phi_E_k(k) + dmuxE(l);
}
}
}
// Family IV
// Re-using mu from Family II
dmu = grad_mu0(z);
phi_Q(p, mu01(z, xy, 2), grad_mu01(z, xy, 2), mu01(z, xy, 1),
grad_mu01(z, xy, 1), phi_Q2_ij, dphi_Q2_ij);
for (int j=2; j<=p; j++)
for (int i=2; i<=p; i++, o++)
{
const int n = std::max(i,j);
const real_t nmu = n * pow(mu, n-1);
dphi(0) = dphi_Q2_ij(i, j, 0);
dphi(1) = dphi_Q2_ij(i, j, 1);
dphi(2) = dphi_Q2_ij(i, j, 2);
dphi.cross3D(dmu, muphi);
for (int l=0; l<3; l++)
{
dW(o, l) = nmu * muphi(l);
}
}
}
}
ND_R1D_PointElement::ND_R1D_PointElement(int p)
: VectorFiniteElement(1, Geometry::POINT, 2, p,
H_CURL, FunctionSpace::Pk)
-121
View File
@@ -14,7 +14,6 @@
#include "fe_base.hpp"
#include "fe_h1.hpp"
#include "fe_pyramid.hpp"
namespace mfem
{
@@ -416,126 +415,6 @@ public:
};
/** Arbitrary order H(Curl) basis functions defined on pyramid-shaped elements
This implementation is closely based on the finite elements
described in section 9.2 of the paper "Orientation embedded high
order shape functions for the exact sequence elements of all shapes"
by Federico Fuentes, Brendan Keith, Leszek Demkowicz, and Sriram
Nagaraj, see https://doi.org/10.1016/j.camwa.2015.04.027.
*/
class ND_FuentesPyramidElement
: public VectorFiniteElement, public FuentesPyramid
{
private:
static const real_t tk[27];
mutable real_t zmax;
#ifndef MFEM_THREAD_SAFE
mutable DenseMatrix tmp_E_E_ij;
mutable DenseMatrix tmp_dE_E_ij;
mutable DenseTensor tmp_E_Q1_ijk;
mutable DenseTensor tmp_dE_Q1_ijk;
mutable DenseTensor tmp_E_Q2_ijk;
mutable DenseTensor tmp_dE_Q2_ijk;
mutable DenseTensor tmp_E_T_ijk;
mutable DenseTensor tmp_dE_T_ijk;
mutable DenseMatrix tmp_phi_Q1_ij;
mutable DenseTensor tmp_dphi_Q1_ij;
mutable DenseMatrix tmp_phi_Q2_ij;
mutable DenseTensor tmp_dphi_Q2_ij;
mutable Vector tmp_phi_E_i;
mutable DenseMatrix tmp_dphi_E_i;
mutable DenseMatrix u;
mutable DenseMatrix curlu;
#endif
Array<int> dof2tk;
DenseMatrixInverse Ti;
ND_PyramidDofTransformation doftrans;
void calcBasis(const int p, const IntegrationPoint & ip,
DenseMatrix & E_E_ik,
DenseTensor & E_Q1_ijk,
DenseTensor & E_Q2_ijk,
DenseTensor & E_T_ijk,
DenseMatrix & phi_Q1_ij,
DenseTensor & dphi_Q1_ij,
DenseMatrix & phi_Q2_ij,
Vector & phi_E_k,
DenseMatrix & dphi_E_k,
DenseMatrix & W) const;
void calcCurlBasis(const int p, const IntegrationPoint & ip,
DenseMatrix & E_E_ik,
DenseMatrix & dE_E_ik,
DenseTensor & E_Q1_ijk,
DenseTensor & dE_Q1_ijk,
DenseTensor & E_Q2_ijk,
DenseTensor & dE_Q2_ijk,
DenseTensor & E_T_ijk,
DenseTensor & dE_T_ijk,
DenseMatrix & phi_Q2_ij,
DenseTensor & dphi_Q2_ij,
Vector & phi_E_k,
DenseMatrix & dphi_E_k,
DenseMatrix & dW) const;
public:
ND_FuentesPyramidElement(const int p,
const int cb_type = BasisType::GaussLobatto,
const int ob_type = BasisType::GaussLegendre);
virtual void CalcVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const;
virtual void CalcVShape(ElementTransformation &Trans,
DenseMatrix &shape) const
{ CalcVShape_ND(Trans, shape); }
virtual void CalcCurlShape(const IntegrationPoint &ip,
DenseMatrix &curl_shape) const;
virtual void GetLocalInterpolation(ElementTransformation &Trans,
DenseMatrix &I) const
{ LocalInterpolation_ND(*this, tk, dof2tk, Trans, I); }
virtual void GetLocalRestriction(ElementTransformation &Trans,
DenseMatrix &R) const
{ LocalRestriction_ND(tk, dof2tk, Trans, R); }
virtual void GetTransferMatrix(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &I) const
{ LocalInterpolation_ND(CheckVectorFE(fe), tk, dof2tk, Trans, I); }
using FiniteElement::Project;
virtual void Project(VectorCoefficient &vc,
ElementTransformation &Trans, Vector &dofs) const
{ Project_ND(tk, dof2tk, vc, Trans, dofs); }
virtual void ProjectMatrixCoefficient(
MatrixCoefficient &mc, ElementTransformation &T, Vector &dofs) const
{ ProjectMatrixCoefficient_ND(tk, dof2tk, mc, T, dofs); }
virtual void Project(const FiniteElement &fe, ElementTransformation &Trans,
DenseMatrix &I) const
{ Project_ND(tk, dof2tk, fe, Trans, I); }
virtual void ProjectGrad(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &grad) const
{ ProjectGrad_ND(tk, dof2tk, fe, Trans, grad); }
virtual void ProjectCurl(const FiniteElement &fe,
ElementTransformation &Trans,
DenseMatrix &curl) const
{ ProjectCurl_ND(tk, dof2tk, fe, Trans, curl); }
void CalcRawVShape(const IntegrationPoint &ip,
DenseMatrix &shape) const;
void CalcRawCurlShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
real_t GetZetaMax() const { return zmax; }
};
/// A 0D Nedelec finite element for the boundary of a 1D domain
/** ND_R1D_PointElement provides a representation of the trace of a three
component Nedelec basis restricted to 1D.
+1 -1
View File
@@ -47,7 +47,7 @@ public:
int GetPatch () const { return patch; }
/// Set which patch should be evaluated
void SetPatch (int p) const { patch = p; }
/// Set which element should be evaluated
/// Set which elemenet should be evaluated
int GetElement () const { return elem; }
/// Get which element is currently considered
void SetElement (int e) const { elem = e; }
-752
View File
@@ -1042,435 +1042,6 @@ void H1Pos_WedgeElement::CalcDShape(const IntegrationPoint &ip,
}
}
H1Pos_PyramidElement::H1Pos_PyramidElement(const int p)
: PositiveFiniteElement(3, Geometry::PYRAMID,
((p + 1)*(p + 2)*(2 * p + 3))/6, p,
FunctionSpace::Uk),
nterms(((p + 1)*(p + 2)*(p + 3)*(p + 4))/24)
{
#ifndef MFEM_THREAD_SAFE
m_shape_1d.SetSize(order + 1);
m_shape.SetSize(nterms);
m_dshape.SetSize(nterms, dim);
#endif
Index idx;
// vertices
dof_map[idx(p,0,0,0,0)] = 0;
Nodes.IntPoint(0).Set3(0., 0., 0.);
dof_map[idx(0,p,0,0,0)] = 1;
Nodes.IntPoint(1).Set3(1., 0., 0.);
dof_map[idx(0,0,p,0,0)] = 2;
Nodes.IntPoint(2).Set3(1., 1., 0.);
dof_map[idx(0,0,0,p,0)] = 3;
Nodes.IntPoint(3).Set3(0., 1., 0.);
dof_map[idx(0,0,0,0,p)] = 4;
Nodes.IntPoint(4).Set3(0., 0., 1.);
// edges (see Geometry::Constants<Geometry::PYRAMID>::Edges
// in fem/geom.cpp)
int o = 5;
for (int i = 1; i < p; i++) // (0,1)
{
dof_map[idx(p-i,i,0,0,0)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, 0., 0.);
}
for (int i = 1; i < p; i++) // (1,2)
{
dof_map[idx(0,p-i,i,0,0)] = o;
Nodes.IntPoint(o++).Set3(1.0, real_t(i)/p, 0.);
}
for (int i = 1; i < p; i++) // (3,2)
{
dof_map[idx(0,0,i,p-i,0)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, 1., 0.);
}
for (int i = 1; i < p; i++) // (0,3)
{
dof_map[idx(p-i,0,0,i,0)] = o;
Nodes.IntPoint(o++).Set3(0., real_t(i)/p, 0.);
}
for (int i = 1; i < p; i++) // (0,4)
{
dof_map[idx(p-i,0,0,0,i)] = o;
Nodes.IntPoint(o++).Set3(0., 0., real_t(i)/p);
}
for (int i = 1; i < p; i++) // (1,4)
{
dof_map[idx(0,p-i,0,0,i)] = o;
Nodes.IntPoint(o++).Set3(real_t(p-i)/p, 0., real_t(i)/p);
}
for (int i = 1; i < p; i++) // (2,4)
{
dof_map[idx(0,0,p-i,0,i)] = o;
Nodes.IntPoint(o++).Set3(real_t(p-i)/p, real_t(p-i)/p, real_t(i)/p);
}
for (int i = 1; i < p; i++) // (3,4)
{
dof_map[idx(0,0,0,p-i,i)] = o;
Nodes.IntPoint(o++).Set3(0., real_t(p-i)/p, real_t(i)/p);
}
// faces (see Geometry::Constants<Geometry::PYRAMID>::FaceVert
// in fem/geom.cpp)
for (int j = 1; j < p; j++)
{
int i1 = j;
int i2 = 0;
int i3 = 0;
int i4 = p - j;
const int i5 = 0;
for (int i = 1; i <= p - j; i++) // (3,2,1,0)
{
i3++;
i4--;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(p-j)/p, 0);
}
for (int i = p - j + 1; i < p; i++) // (3,2,1,0)
{
i1--;
i2++;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(p-j)/p, 0);
}
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (0, 1, 4)
{
dof_map[idx(p-i-j,i,0,0,j)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, 0., real_t(j)/p);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (1, 2, 4)
{
dof_map[idx(0,p-i-j,i,0,j)] = o;
Nodes.IntPoint(o++).Set3(real_t(p-j)/p, real_t(i)/p, real_t(j)/p);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (2, 3, 4)
{
dof_map[idx(0,0,p-i-j,i,j)] = o;
Nodes.IntPoint(o++).Set3(real_t(p-i-j)/p, real_t(p-j)/p, real_t(j)/p);
}
for (int j = 1; j < p; j++)
for (int i = 1; i + j < p; i++) // (3, 0, 4)
{
dof_map[idx(i,0,0,p-i-j,j)] = o;
Nodes.IntPoint(o++).Set3(0., real_t(p-i-j)/p, real_t(j)/p);
}
// interior
for (int k = 1; k < p; k++)
for (int j = 1; j + k < p; j++)
{
int i1 = p - j - k;
int i2 = 0;
int i3 = 0;
int i4 = j;
const int i5 = k;
for (int i = 1; i <= j; i++)
{
i3++;
i4--;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
}
for (int i = j + 1; i + k < p; i++)
{
i1--;
i2++;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
}
}
}
// static method
void H1Pos_PyramidElement::CalcShape(const int p, const real_t x,
const real_t y, const real_t z,
real_t *shape_1d,
real_t *shape)
{
const int lshape = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
for (int i=0; i<lshape; i++) { shape[i] = 0.0; }
const real_t l1 = lam1(x, y, z);
const real_t l2 = lam2(x, y, z);
const real_t l3 = lam3(x, y, z);
const real_t l4 = lam4(x, y, z);
const real_t l5 = lam5(x, y, z);
// The basis functions are the terms in the expansion:
// (l1 + l2 + l3 + l4 + l5)^p =
// \sum_{l=0}^p \binom{p}{l} l5^l
// \sum_{k=0}^{p-l} \binom{p-l}{k} l4^k
// \sum_{j=0}^{p-l-k} \binom{p-l-k}{j} l3^j
// \sum_{i=0}^{p-l-k-j} \binom{p-l-k-j}{i} l2^i l1^{p-l-k-j-i}
Index idx;
const int *bp = Poly_1D::Binom(p);
real_t l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, shape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
shape_1d[i2] *= ei345;
shape[o] += shape_1d[i2];
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
}
// static method
void H1Pos_PyramidElement::CalcDShape(const int p, const real_t x,
const real_t y, const real_t z,
real_t *dshape_1d, real_t *dshape)
{
const int nterms = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
for (int i=0; i<3*nterms; i++) { dshape[i] = 0.0; }
const real_t l1 = lam1(x, y, z);
const real_t l2 = lam2(x, y, z);
const real_t l3 = lam3(x, y, z);
const real_t l4 = lam4(x, y, z);
const real_t l5 = lam5(x, y, z);
const Vector dl1 = grad_lam1(x, y, z);
const Vector dl2 = grad_lam2(x, y, z);
const Vector dl3 = grad_lam3(x, y, z);
const Vector dl4 = grad_lam4(x, y, z);
const Vector dl5 = grad_lam5(x, y, z);
// The basis functions are the terms in the expansion:
// (l1 + l2 + l3 + l4 + l5)^p
// We will compute the derivative by first computing the derivatives
// of these terms w.r.t each of the l1, l2, l3, l4, and l5 and summing
// the results together.
Index idx;
// Derivative w.r.t. l1 times grad(l1)
const int *bp = Poly_1D::Binom(p);
real_t l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcDyBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl1 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl1 * dl1[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l2 times grad(l2)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcDxBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl2 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl2*dl2[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l3 times grad(l3)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 1; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = i3*ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl3 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl3*dl3[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l4 times grad(l4)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 1; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = i4*ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl4 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl4*dl4[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l5 times grad(l5)
l5i5 = 1.;
for (int i5 = 1; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = i5*bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl5 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl5*dl5[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
}
void H1Pos_PyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
#ifdef MFEM_THREAD_SAFE
Vector m_shape_1d(order + 1);
Vector m_shape(nterms);
#endif
CalcShape(order, ip.x, ip.y, ip.z, m_shape_1d.GetData(), m_shape.GetData());
for (auto const& it : dof_map)
{
if (it.first < m_shape.Size()) { shape[it.second] = m_shape[it.first]; }
}
}
void H1Pos_PyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
#ifdef MFEM_THREAD_SAFE
Vector m_shape_1d(order + 1);
DenseMatrix m_dshape(nterms, 3);
#endif
CalcDShape(order, ip.x, ip.y, ip.z,
m_shape_1d.GetData(), m_dshape.GetData());
for (auto const& it : dof_map)
for (int d=0; d<3; d++)
{
dshape(it.second, d) = m_dshape(it.first, d);
}
}
L2Pos_SegmentElement::L2Pos_SegmentElement(const int p)
: PositiveTensorFiniteElement(1, p, L2_DOF_MAP)
{
@@ -1877,327 +1448,4 @@ void L2Pos_WedgeElement::CalcDShape(const IntegrationPoint &ip,
}
}
L2Pos_PyramidElement::L2Pos_PyramidElement(const int p)
: PositiveFiniteElement(3, Geometry::PYRAMID,
((p + 1)*(p + 2)*(2 * p + 3))/6, p,
FunctionSpace::Uk),
nterms(((p + 1)*(p + 2)*(p + 3)*(p + 4))/24)
{
#ifndef MFEM_THREAD_SAFE
m_shape_1d.SetSize(order + 1);
m_shape.SetSize(nterms);
m_dshape.SetSize(nterms, dim);
#endif
Index idx;
// interior
for (int o = 0, k = 0; k <= p; k++)
for (int j = 0; j + k <= p; j++)
{
int i1 = p - j - k;
int i2 = 0;
int i3 = -1;
int i4 = j + 1;
const int i5 = k;
for (int i = 0; i <= j; i++)
{
i3++;
i4--;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
}
for (int i = j + 1; i + k <= p; i++)
{
i1--;
i2++;
dof_map[idx(i1,i2,i3,i4,i5)] = o;
Nodes.IntPoint(o++).Set3(real_t(i)/p, real_t(j)/p, 0);
}
}
}
// static method
void L2Pos_PyramidElement::CalcShape(const int p, const real_t x,
const real_t y, const real_t z,
real_t *shape_1d,
real_t *shape)
{
const int lshape = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
for (int i=0; i<lshape; i++) { shape[i] = 0.0; }
const real_t l1 = lam1(x, y, z);
const real_t l2 = lam2(x, y, z);
const real_t l3 = lam3(x, y, z);
const real_t l4 = lam4(x, y, z);
const real_t l5 = lam5(x, y, z);
// The basis functions are the terms in the expansion:
// (l1 + l2 + l3 + l4 + l5)^p =
// \sum_{l=0}^p \binom{p}{l} l5^l
// \sum_{k=0}^{p-l} \binom{p-l}{k} l4^k
// \sum_{j=0}^{p-l-k} \binom{p-l-k}{j} l3^j
// \sum_{i=0}^{p-l-k-j} \binom{p-l-k-j}{i} l2^i l1^{p-l-k-j-i}
Index idx;
const int *bp = Poly_1D::Binom(p);
real_t l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, shape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
shape_1d[i2] *= ei345;
shape[o] += shape_1d[i2];
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
}
// static method
void L2Pos_PyramidElement::CalcDShape(const int p, const real_t x,
const real_t y, const real_t z,
real_t *dshape_1d, real_t *dshape)
{
const int nterms = ((p + 1)*(p + 2)*(p + 3)*(p + 4))/24;
for (int i=0; i<3*nterms; i++) { dshape[i] = 0.0; }
const real_t l1 = lam1(x, y, z);
const real_t l2 = lam2(x, y, z);
const real_t l3 = lam3(x, y, z);
const real_t l4 = lam4(x, y, z);
const real_t l5 = lam5(x, y, z);
const Vector dl1 = grad_lam1(x, y, z);
const Vector dl2 = grad_lam2(x, y, z);
const Vector dl3 = grad_lam3(x, y, z);
const Vector dl4 = grad_lam4(x, y, z);
const Vector dl5 = grad_lam5(x, y, z);
// The basis functions are the terms in the expansion:
// (l1 + l2 + l3 + l4 + l5)^p
// We will compute the derivative by first computing the derivatives
// of these terms w.r.t each of the l1, l2, l3, l4, and l5 and summing
// the results together.
Index idx;
// Derivative w.r.t. l1 times grad(l1)
const int *bp = Poly_1D::Binom(p);
real_t l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcDyBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl1 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl1 * dl1[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l2 times grad(l2)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcDxBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl2 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl2*dl2[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l3 times grad(l3)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 1; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = i3*ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl3 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl3*dl3[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l4 times grad(l4)
l5i5 = 1.;
for (int i5 = 0; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 1; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = i4*ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl4 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl4*dl4[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
// Derivative w.r.t. l5 times grad(l5)
l5i5 = 1.;
for (int i5 = 1; i5 <= p; i5++)
{
const int *bpi5 = Poly_1D::Binom(p - i5);
const real_t ei5 = i5*bp[i5]*l5i5;
real_t l4i4 = 1.;
for (int i4 = 0; i4 <= p - i5; i4++)
{
const int *bpi45 = Poly_1D::Binom(p - i5 - i4);
const real_t ei45 = ei5*bpi5[i4]*l4i4;
real_t l3i3 = 1.;
for (int i3 = 0; i3 <= p - i5 - i4; i3++)
{
Poly_1D::CalcBinomTerms(p - i5 - i4 - i3, l2, l1, dshape_1d);
real_t ei345 = ei45*bpi45[i3]*l3i3;
for (int i2 = 0; i2 <= p - i5 - i4 - i3; i2++)
{
const int i1 = p - i5 - i4 - i3 - i2;
const int o = idx(i1,i2,i3,i4,i5);
const real_t dshape_dl5 = dshape_1d[i2]*ei345;
for (int d = 0; d < 3; d++)
{
dshape[o + d * nterms] += dshape_dl5*dl5[d];
}
}
l3i3 *= l3;
}
l4i4 *= l4;
}
l5i5 *= l5;
}
}
void L2Pos_PyramidElement::CalcShape(const IntegrationPoint &ip,
Vector &shape) const
{
#ifdef MFEM_THREAD_SAFE
Vector m_shape_1d(order + 1);
Vector m_shape(nterms);
#endif
CalcShape(order, ip.x, ip.y, ip.z, m_shape_1d.GetData(), m_shape.GetData());
for (auto const& it : dof_map)
{
if (it.first < m_shape.Size()) { shape[it.second] = m_shape[it.first]; }
}
}
void L2Pos_PyramidElement::CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const
{
#ifdef MFEM_THREAD_SAFE
Vector m_shape_1d(order + 1);
DenseMatrix m_dshape(nterms, 3);
#endif
CalcDShape(order, ip.x, ip.y, ip.z,
m_shape_1d.GetData(), m_dshape.GetData());
for (auto const& it : dof_map)
for (int d=0; d<3; d++)
{
dshape(it.second, d) = m_dshape(it.first, d);
}
}
}
-119
View File
@@ -13,7 +13,6 @@
#define MFEM_FE_POS
#include "fe_base.hpp"
#include "fe_pyramid.hpp"
namespace mfem
{
@@ -257,69 +256,6 @@ public:
};
/// Arbitrary order H1 elements in 3D utilizing the Bernstein basis on a pyramid
///
/// The pyramid affine-related coordinates $\lambda_i$ for $i=1,\ldots,5$ can
/// be used to define a positive H1 basis by noting that $\lambda_i \ge 0$
/// inside the pyramid for all $i$ and that $\sum_{i=1}^5\lambda_i=1$. This
/// leads to $1 = (\sum_{i=1}^5\lambda_i)^p$. The terms of this product,
/// expanded as a polynomial in the $\lambda_i$, can be used as a Bernstein
/// basis of order $p$ on a pyramid.
class H1Pos_PyramidElement : public PositiveFiniteElement, FuentesPyramid
{
protected:
const int nterms;
#ifndef MFEM_THREAD_SAFE
mutable Vector m_shape_1d;
mutable Vector m_shape;
mutable DenseMatrix m_dshape;
#endif
std::map<int,int> dof_map;
struct Index
{
Index() = default;
int operator()(int i1, int i2, int i3, int i4, int i5)
{
const int p = i1 + i2 + i3 + i4 + i5;
const int min24 = std::min(i2,i4);
i1 += min24;
i2 -= min24;
i3 += min24;
i4 -= min24;
return i2 + i3 * (p - i4 - i5) - i4 * i5 * (p + 2)
- ((i3 - 3) * i3) / 2 + i4 * ((p + 1) * (p + 2)) / 2
+ (i4 * i5 * (i4 + i5)) / 2 + ((i4 - 1) * i4 * (i4 +1)) / 6
- (p + 2) * ((i4 - 1) * i4) / 2
+ (i5 * (5 + 2 * p - i5) * (i5 * i5 - i5 * (5 + 2 * p)
+ 2 * (5 + 5 * p + p * p))) / 24;
}
};
public:
/// Construct the H1Pos_PyramidElement of order @a p
H1Pos_PyramidElement(const int p);
// The size of shape is (p+1)(p+2)(p+3)(p+4)/24.
// The size of shape_1d should be at least p+1.
static void CalcShape(const int p, const real_t x, const real_t y,
const real_t z, real_t *shape_1d, real_t *shape);
// The size of dshape is (p+1)(p+2)(p+3)(p+4)/24 by 3.
// The size of dshape_1d should be at least p+1.
static void CalcDShape(const int p, const real_t x, const real_t y,
const real_t z, real_t *dshape_1d, real_t *dshape);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
// Returns (p+1)(p+2)(p+3)(p+4)/24 which is the size of the temporary arrays
// needed above
int GetNumTerms() const { return nterms; }
};
/// Arbitrary order L2 elements in 1D utilizing the Bernstein basis on a segment
class L2Pos_SegmentElement : public PositiveTensorFiniteElement
{
@@ -433,61 +369,6 @@ public:
DenseMatrix &dshape) const override;
};
/// Arbitrary order L2 elements in 3D utilizing the Bernstein basis on a pyramid
class L2Pos_PyramidElement : public PositiveFiniteElement, FuentesPyramid
{
protected:
const int nterms;
#ifndef MFEM_THREAD_SAFE
mutable Vector m_shape_1d;
mutable Vector m_shape;
mutable DenseMatrix m_dshape;
#endif
std::map<int,int> dof_map;
struct Index
{
Index() = default;
int operator()(int i1, int i2, int i3, int i4, int i5)
{
const int p = i1 + i2 + i3 + i4 + i5;
const int min24 = std::min(i2,i4);
i1 += min24;
i2 -= min24;
i3 += min24;
i4 -= min24;
return i2 + i3 * (p - i4 - i5) - i4 * i5 * (p + 2)
- ((i3 - 3) * i3) / 2 + i4 * ((p + 1) * (p + 2)) / 2
+ (i4 * i5 * (i4 + i5)) / 2 + ((i4 - 1) * i4 * (i4 +1)) / 6
- (p + 2) * ((i4 - 1) * i4) / 2
+ (i5 * (5 + 2 * p - i5) * (i5 * i5 - i5 * (5 + 2 * p)
+ 2 * (5 + 5 * p + p * p))) / 24;
}
};
// Returns (p+1)(p+2)(p+3)(p+4)/24 which is the size of the temporary arrays
// needed below
int GetNumTerms() const { return nterms; }
// The size of shape is (p+1)(p+2)(p+3)(p+4)/24.
// The size of shape_1d should be at least p+1.
static void CalcShape(const int p, const real_t x, const real_t y,
const real_t z, real_t *shape_1d, real_t *shape);
// The size of dshape is (p+1)(p+2)(p+3)(p+4)/24 by 3.
// The size of dshape_1d should be at least p+1.
static void CalcDShape(const int p, const real_t x, const real_t y,
const real_t z, real_t *dshape_1d, real_t *dshape);
public:
/// Construct the L2Pos_PyramidElement of order @a p
L2Pos_PyramidElement(const int p);
virtual void CalcShape(const IntegrationPoint &ip, Vector &shape) const;
virtual void CalcDShape(const IntegrationPoint &ip,
DenseMatrix &dshape) const;
};
} // namespace mfem
#endif

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