Compare commits
64
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef5f729245 | ||
|
|
6fa5a0b096 | ||
|
|
3bd8349909 | ||
|
|
af82ee8560 | ||
|
|
2ac542c720 | ||
|
|
e6f828a5fe | ||
|
|
4d756edd80 | ||
|
|
cf5bd1f5cc | ||
|
|
7173dd2002 | ||
|
|
50182bf440 | ||
|
|
24bfcc5165 | ||
|
|
302f22f297 | ||
|
|
1637fcd933 | ||
|
|
2563506174 | ||
|
|
60640c3f7e | ||
|
|
f221521203 | ||
|
|
1d9e736af6 | ||
|
|
d80dbfd99a | ||
|
|
3f44043e60 | ||
|
|
b218959bca | ||
|
|
aee7bc9d43 | ||
|
|
31cac320d4 | ||
|
|
e7e0fb0a88 | ||
|
|
ba71d13980 | ||
|
|
5c326a5535 | ||
|
|
9457f7e5b6 | ||
|
|
ff030ee970 | ||
|
|
2be9e1f36c | ||
|
|
7671cd9f36 | ||
|
|
f89a633fda | ||
|
|
4ce1cef6b8 | ||
|
|
c667bf3025 | ||
|
|
e1678afe40 | ||
|
|
c0291398ed | ||
|
|
0b4f10d79d | ||
|
|
8dfd0e1547 | ||
|
|
caf239c99a | ||
|
|
44c33aece0 | ||
|
|
ed49856390 | ||
|
|
4fef6ca298 | ||
|
|
d9d809e81c | ||
|
|
51e85ccd84 | ||
|
|
4304159303 | ||
|
|
6276268e52 | ||
|
|
580ae34842 | ||
|
|
97eaf8efbc | ||
|
|
e6621c9b0c | ||
|
|
461246f80e | ||
|
|
a5941ee72f | ||
|
|
c4c2ceab59 | ||
|
|
88b99a1719 | ||
|
|
7aa7b4ee53 | ||
|
|
bcf87fee29 | ||
|
|
2949dc5a46 | ||
|
|
b0dbadd007 | ||
|
|
6ca1f95979 | ||
|
|
39794585c4 | ||
|
|
65a71259f1 | ||
|
|
3f4e8324d4 | ||
|
|
9fca398741 | ||
|
|
a2b8f7a129 | ||
|
|
a367631ce5 | ||
|
|
d7718f5c57 | ||
|
|
fe88c4685d |
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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@)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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@
|
||||
|
||||
@@ -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
@@ -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 ?=
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+50
-75
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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; }
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user