Files
mfem/fem/fespace.hpp
T

952 lines
39 KiB
C++

// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
#ifndef MFEM_FESPACE
#define MFEM_FESPACE
#include "../config/config.hpp"
#include "../linalg/sparsemat.hpp"
#include "../mesh/mesh.hpp"
#include "fe_coll.hpp"
#include "restriction.hpp"
#include <iostream>
#include <unordered_map>
namespace mfem
{
/** @brief The ordering method used when the number of unknowns per mesh node
(vector dimension) is bigger than 1. */
class Ordering
{
public:
/// %Ordering methods:
enum Type
{
byNODES, /**< loop first over the nodes (inner loop) then over the vector
dimension (outer loop); symbolically it can be represented
as: XXX...,YYY...,ZZZ... */
byVDIM /**< loop first over the vector dimension (inner loop) then over
the nodes (outer loop); symbolically it can be represented
as: XYZ,XYZ,XYZ,... */
};
template <Type Ord>
static inline int Map(int ndofs, int vdim, int dof, int vd);
template <Type Ord>
static void DofsToVDofs(int ndofs, int vdim, Array<int> &dofs);
};
template <> inline int
Ordering::Map<Ordering::byNODES>(int ndofs, int vdim, int dof, int vd)
{
MFEM_ASSERT(dof < ndofs && -1-dof < ndofs && 0 <= vd && vd < vdim, "");
return (dof >= 0) ? dof+ndofs*vd : dof-ndofs*vd;
}
template <> inline int
Ordering::Map<Ordering::byVDIM>(int ndofs, int vdim, int dof, int vd)
{
MFEM_ASSERT(dof < ndofs && -1-dof < ndofs && 0 <= vd && vd < vdim, "");
return (dof >= 0) ? vd+vdim*dof : -1-(vd+vdim*(-1-dof));
}
/// Constants describing the possible orderings of the DOFs in one element.
enum class ElementDofOrdering
{
/// Native ordering as defined by the FiniteElement.
/** This ordering can be used by tensor-product elements when the
interpolation from the DOFs to quadrature points does not use the
tensor-product structure. */
NATIVE,
/// Lexicographic ordering for tensor-product FiniteElements.
/** This ordering can be used only with tensor-product elements. */
LEXICOGRAPHIC
};
// Forward declarations
class NURBSExtension;
class BilinearFormIntegrator;
class QuadratureSpace;
class QuadratureInterpolator;
class FaceQuadratureInterpolator;
/** @brief Class FiniteElementSpace - responsible for providing FEM view of the
mesh, mainly managing the set of degrees of freedom. */
class FiniteElementSpace
{
friend class InterpolationGridTransfer;
friend class PRefinementTransferOperator;
protected:
/// The mesh that FE space lives on (not owned).
Mesh *mesh;
/// Associated FE collection (not owned).
const FiniteElementCollection *fec;
/// %Vector dimension (number of unknowns per degree of freedom).
int vdim;
/** Type of ordering of the vector dofs when #vdim > 1.
- Ordering::byNODES - first nodes, then vector dimension,
- Ordering::byVDIM - first vector dimension, then nodes */
Ordering::Type ordering;
/// Number of degrees of freedom. Number of unknowns is #ndofs * #vdim.
int ndofs;
int nvdofs, nedofs, nfdofs, nbdofs;
int *fdofs, *bdofs;
mutable Table *elem_dof; // if NURBS FE space, not owned; otherwise, owned.
Table *bdrElem_dof; // used only with NURBS FE spaces; not owned.
Array<int> dof_elem_array, dof_ldof_array;
NURBSExtension *NURBSext;
int own_ext;
/** Matrix representing the prolongation from the global conforming dofs to
a set of intermediate partially conforming dofs, e.g. the dofs associated
with a "cut" space on a non-conforming mesh. */
mutable SparseMatrix *cP; // owned
/// Conforming restriction matrix such that cR.cP=I.
mutable SparseMatrix *cR; // owned
mutable bool cP_is_set;
/// Transformation to apply to GridFunctions after space Update().
OperatorHandle Th;
/// The element restriction operators, see GetElementRestriction().
mutable OperatorHandle L2E_nat, L2E_lex;
/// The face restriction operators, see GetFaceRestriction().
using key_face = std::tuple<bool, ElementDofOrdering, FaceType, L2FaceValues>;
struct key_hash
{
std::size_t operator()(const key_face& k) const
{
return std::get<0>(k)
+ 2 * (int)std::get<1>(k)
+ 4 * (int)std::get<2>(k)
+ 8 * (int)std::get<3>(k);
}
};
using map_L2F = std::unordered_map<const key_face,Operator*,key_hash>;
mutable map_L2F L2F;
mutable Array<QuadratureInterpolator*> E2Q_array;
mutable Array<FaceQuadratureInterpolator*> E2IFQ_array;
mutable Array<FaceQuadratureInterpolator*> E2BFQ_array;
long sequence; // should match Mesh::GetSequence
void UpdateNURBS();
void Construct();
void Destroy();
void BuildElementToDofTable() const;
/// Helpers to remove encoded sign from a DOF
static inline int DecodeDof(int dof)
{
return (dof >= 0) ? dof : (-1 - dof);
}
static inline int DecodeDof(int dof, double& sign)
{ return (dof >= 0) ? (sign = 1, dof) : (sign = -1, (-1 - dof)); }
/// Helper to get vertex, edge or face DOFs (entity=0,1,2 resp.).
void GetEntityDofs(int entity, int index, Array<int> &dofs,
Geometry::Type master_geom = Geometry::INVALID) const;
// Get degenerate face DOFs: see explanation in method implementation.
void GetDegenerateFaceDofs(int index, Array<int> &dofs,
Geometry::Type master_geom) const;
/// Calculate the cP and cR matrices for a nonconforming mesh.
void BuildConformingInterpolation() const;
static void AddDependencies(SparseMatrix& deps, Array<int>& master_dofs,
Array<int>& slave_dofs, DenseMatrix& I);
static bool DofFinalizable(int dof, const Array<bool>& finalized,
const SparseMatrix& deps);
/// Replicate 'mat' in the vector dimension, according to vdim ordering mode.
void MakeVDimMatrix(SparseMatrix &mat) const;
/// GridFunction interpolation operator applicable after mesh refinement.
class RefinementOperator : public Operator
{
const FiniteElementSpace* fespace;
DenseTensor localP[Geometry::NumGeom];
Table* old_elem_dof; // Owned.
public:
/** Construct the operator based on the elem_dof table of the original
(coarse) space. The class takes ownership of the table. */
RefinementOperator(const FiniteElementSpace* fespace,
Table *old_elem_dof/*takes ownership*/, int old_ndofs);
RefinementOperator(const FiniteElementSpace *fespace,
const FiniteElementSpace *coarse_fes);
virtual void Mult(const Vector &x, Vector &y) const;
virtual void MultTranspose(const Vector &x, Vector &y) const;
virtual ~RefinementOperator();
};
/// Derefinement operator, used by the friend class InterpolationGridTransfer.
class DerefinementOperator : public Operator
{
const FiniteElementSpace *fine_fes; // Not owned.
DenseTensor localR[Geometry::NumGeom];
Table *coarse_elem_dof; // Owned.
Table coarse_to_fine;
Array<int> coarse_to_ref_type;
Array<Geometry::Type> ref_type_to_geom;
Array<int> ref_type_to_fine_elem_offset;
public:
DerefinementOperator(const FiniteElementSpace *f_fes,
const FiniteElementSpace *c_fes,
BilinearFormIntegrator *mass_integ);
virtual void Mult(const Vector &x, Vector &y) const;
virtual ~DerefinementOperator();
};
/** This method makes the same assumptions as the method:
void GetLocalRefinementMatrices(
const FiniteElementSpace &coarse_fes, Geometry::Type geom,
DenseTensor &localP) const
which is defined below. It also assumes that the coarse fes and this have
the same vector dimension, vdim. */
SparseMatrix *RefinementMatrix_main(const int coarse_ndofs,
const Table &coarse_elem_dof,
const DenseTensor localP[]) const;
void GetLocalRefinementMatrices(Geometry::Type geom,
DenseTensor &localP) const;
void GetLocalDerefinementMatrices(Geometry::Type geom,
DenseTensor &localR) const;
/** Calculate explicit GridFunction interpolation matrix (after mesh
refinement). NOTE: consider using the RefinementOperator class instead
of the fully assembled matrix, which can take a lot of memory. */
SparseMatrix* RefinementMatrix(int old_ndofs, const Table* old_elem_dof);
/// Calculate GridFunction restriction matrix after mesh derefinement.
SparseMatrix* DerefinementMatrix(int old_ndofs, const Table* old_elem_dof);
/** @brief Return in @a localP the local refinement matrices that map
between fespaces after mesh refinement. */
/** This method assumes that this->mesh is a refinement of coarse_fes->mesh
and that the CoarseFineTransformations of this->mesh are set accordingly.
Another assumption is that the FEs of this use the same MapType as the FEs
of coarse_fes. Finally, it assumes that the spaces this and coarse_fes are
NOT variable-order spaces. */
void GetLocalRefinementMatrices(const FiniteElementSpace &coarse_fes,
Geometry::Type geom,
DenseTensor &localP) const;
/// Help function for constructors + Load().
void Constructor(Mesh *mesh, NURBSExtension *ext,
const FiniteElementCollection *fec,
int vdim = 1, int ordering = Ordering::byNODES);
public:
/** @brief Default constructor: the object is invalid until initialized using
the method Load(). */
FiniteElementSpace();
/** @brief Copy constructor: deep copy all data from @a orig except the Mesh,
the FiniteElementCollection, ans some derived data. */
/** If the @a mesh or @a fec pointers are NULL (default), then the new
FiniteElementSpace will reuse the respective pointers from @a orig. If
any of these pointers is not NULL, the given pointer will be used instead
of the one used by @a orig.
@note The objects pointed to by the @a mesh and @a fec parameters must be
either the same objects as the ones used by @a orig, or copies of them.
Otherwise, the behavior is undefined.
@note Derived data objects, such as the conforming prolongation and
restriction matrices, and the update operator, will not be copied, even
if they are created in the @a orig object. */
FiniteElementSpace(const FiniteElementSpace &orig, Mesh *mesh = NULL,
const FiniteElementCollection *fec = NULL);
FiniteElementSpace(Mesh *mesh,
const FiniteElementCollection *fec,
int vdim = 1, int ordering = Ordering::byNODES)
{ Constructor(mesh, NULL, fec, vdim, ordering); }
/// Construct a NURBS FE space based on the given NURBSExtension, @a ext.
/** @note If the pointer @a ext is NULL, this constructor is equivalent to
the standard constructor with the same arguments minus the
NURBSExtension, @a ext. */
FiniteElementSpace(Mesh *mesh, NURBSExtension *ext,
const FiniteElementCollection *fec,
int vdim = 1, int ordering = Ordering::byNODES)
{ Constructor(mesh, ext, fec, vdim, ordering); }
/// Returns the mesh
inline Mesh *GetMesh() const { return mesh; }
const NURBSExtension *GetNURBSext() const { return NURBSext; }
NURBSExtension *GetNURBSext() { return NURBSext; }
NURBSExtension *StealNURBSext();
bool Conforming() const { return mesh->Conforming(); }
bool Nonconforming() const { return mesh->Nonconforming(); }
/// The returned SparseMatrix is owned by the FiniteElementSpace.
const SparseMatrix *GetConformingProlongation() const;
/// The returned SparseMatrix is owned by the FiniteElementSpace.
const SparseMatrix *GetConformingRestriction() const;
/// The returned Operator is owned by the FiniteElementSpace.
virtual const Operator *GetProlongationMatrix() const
{ return GetConformingProlongation(); }
/// The returned SparseMatrix is owned by the FiniteElementSpace.
virtual const SparseMatrix *GetRestrictionMatrix() const
{ return GetConformingRestriction(); }
/// Return an Operator that converts L-vectors to E-vectors.
/** An L-vector is a vector of size GetVSize() which is the same size as a
GridFunction. An E-vector represents the element-wise discontinuous
version of the FE space.
The layout of the E-vector is: ND x VDIM x NE, where ND is the number of
degrees of freedom, VDIM is the vector dimension of the FE space, and NE
is the number of the mesh elements.
The parameter @a e_ordering describes how the local DOFs in each element
should be ordered, see ElementDofOrdering.
For discontinuous spaces, the element restriction corresponds to a
permutation of the degrees of freedom, implemented by the
L2ElementRestriction class.
The returned Operator is owned by the FiniteElementSpace. */
const Operator *GetElementRestriction(ElementDofOrdering e_ordering) const;
/// Return an Operator that converts L-vectors to E-vectors on each face.
virtual const Operator *GetFaceRestriction(
ElementDofOrdering e_ordering, FaceType,
L2FaceValues mul = L2FaceValues::DoubleValued) const;
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
quadrature point values and/or derivatives (Q-vectors). */
/** An E-vector represents the element-wise discontinuous version of the FE
space and can be obtained, for example, from a GridFunction using the
Operator returned by GetElementRestriction().
All elements will use the same IntegrationRule, @a ir as the target
quadrature points. */
const QuadratureInterpolator *GetQuadratureInterpolator(
const IntegrationRule &ir) const;
/** @brief Return a QuadratureInterpolator that interpolates E-vectors to
quadrature point values and/or derivatives (Q-vectors). */
/** An E-vector represents the element-wise discontinuous version of the FE
space and can be obtained, for example, from a GridFunction using the
Operator returned by GetElementRestriction().
The target quadrature points in the elements are described by the given
QuadratureSpace, @a qs. */
const QuadratureInterpolator *GetQuadratureInterpolator(
const QuadratureSpace &qs) const;
/** @brief Return a FaceQuadratureInterpolator that interpolates E-vectors to
quadrature point values and/or derivatives (Q-vectors). */
const FaceQuadratureInterpolator *GetFaceQuadratureInterpolator(
const IntegrationRule &ir, FaceType type) const;
/// Returns vector dimension.
inline int GetVDim() const { return vdim; }
/// Returns the order of the i'th finite element
int GetOrder(int i) const;
/// Returns the order of the i'th face finite element
int GetFaceOrder(int i) const;
/// Returns number of degrees of freedom.
inline int GetNDofs() const { return ndofs; }
/// Return the number of vector dofs, i.e. GetNDofs() x GetVDim().
inline int GetVSize() const { return vdim * ndofs; }
/// Return the number of vector true (conforming) dofs.
virtual int GetTrueVSize() const { return GetConformingVSize(); }
/// Returns the number of conforming ("true") degrees of freedom
/// (if the space is on a nonconforming mesh with hanging nodes).
int GetNConformingDofs() const;
int GetConformingVSize() const { return vdim * GetNConformingDofs(); }
/// Return the ordering method.
inline Ordering::Type GetOrdering() const { return ordering; }
const FiniteElementCollection *FEColl() const { return fec; }
/// Number of all scalar vertex dofs
int GetNVDofs() const { return nvdofs; }
/// Number of all scalar edge-interior dofs
int GetNEDofs() const { return nedofs; }
/// Number of all scalar face-interior dofs
int GetNFDofs() const { return nfdofs; }
/// Returns number of vertices in the mesh.
inline int GetNV() const { return mesh->GetNV(); }
/// Returns number of elements in the mesh.
inline int GetNE() const { return mesh->GetNE(); }
/// Returns number of faces (i.e. co-dimension 1 entities) in the mesh.
/** The co-dimension 1 entities are those that have dimension 1 less than the
mesh dimension, e.g. for a 2D mesh, the faces are the 1D entities, i.e.
the edges. */
inline int GetNF() const { return mesh->GetNumFaces(); }
/// Returns number of boundary elements in the mesh.
inline int GetNBE() const { return mesh->GetNBE(); }
/// Returns the number of faces according to the requested type.
/** If type==Boundary returns only the "true" number of boundary faces
contrary to GetNBE() that returns "fake" boundary faces associated to
visualization for GLVis.
Similarly, if type==Interior, the "fake" boundary faces associated to
visualization are counted as interior faces. */
inline int GetNFbyType(FaceType type) const
{ return mesh->GetNFbyType(type); }
/// Returns the type of element i.
inline int GetElementType(int i) const
{ return mesh->GetElementType(i); }
/// Returns the vertices of element i.
inline void GetElementVertices(int i, Array<int> &vertices) const
{ mesh->GetElementVertices(i, vertices); }
/// Returns the type of boundary element i.
inline int GetBdrElementType(int i) const
{ return mesh->GetBdrElementType(i); }
/// Returns ElementTransformation for the @a i-th element.
ElementTransformation *GetElementTransformation(int i) const
{ return mesh->GetElementTransformation(i); }
/** @brief Returns the transformation defining the @a i-th element in the
user-defined variable @a ElTr. */
void GetElementTransformation(int i, IsoparametricTransformation *ElTr)
{ mesh->GetElementTransformation(i, ElTr); }
/// Returns ElementTransformation for the @a i-th boundary element.
ElementTransformation *GetBdrElementTransformation(int i) const
{ return mesh->GetBdrElementTransformation(i); }
int GetAttribute(int i) const { return mesh->GetAttribute(i); }
int GetBdrAttribute(int i) const { return mesh->GetBdrAttribute(i); }
/// Returns indexes of degrees of freedom in array dofs for i'th element.
virtual void GetElementDofs(int i, Array<int> &dofs) const;
/// Returns indexes of degrees of freedom for i'th boundary element.
virtual void GetBdrElementDofs(int i, Array<int> &dofs) const;
/** @brief eturns the indexes of the degrees of freedom for i'th face
including the dofs for the edges and the vertices of the face. */
virtual void GetFaceDofs(int i, Array<int> &dofs) const;
/** @brief Returns the indexes of the degrees of freedom for i'th edge
including the dofs for the vertices of the edge. */
void GetEdgeDofs(int i, Array<int> &dofs) const;
void GetVertexDofs(int i, Array<int> &dofs) const;
void GetElementInteriorDofs(int i, Array<int> &dofs) const;
void GetFaceInteriorDofs(int i, Array<int> &dofs) const;
int GetNumElementInteriorDofs(int i) const
{ return fec->DofForGeometry(mesh->GetElementBaseGeometry(i)); }
void GetEdgeInteriorDofs(int i, Array<int> &dofs) const;
void DofsToVDofs(Array<int> &dofs, int ndofs = -1) const;
void DofsToVDofs(int vd, Array<int> &dofs, int ndofs = -1) const;
int DofToVDof(int dof, int vd, int ndofs = -1) const;
int VDofToDof(int vdof) const
{ return (ordering == Ordering::byNODES) ? (vdof%ndofs) : (vdof/vdim); }
static void AdjustVDofs(Array<int> &vdofs);
/// Returns indexes of degrees of freedom in array dofs for i'th element.
void GetElementVDofs(int i, Array<int> &vdofs) const;
/// Returns indexes of degrees of freedom for i'th boundary element.
void GetBdrElementVDofs(int i, Array<int> &vdofs) const;
/// Returns indexes of degrees of freedom for i'th face element (2D and 3D).
void GetFaceVDofs(int i, Array<int> &vdofs) const;
/// Returns indexes of degrees of freedom for i'th edge.
void GetEdgeVDofs(int i, Array<int> &vdofs) const;
void GetVertexVDofs(int i, Array<int> &vdofs) const;
void GetElementInteriorVDofs(int i, Array<int> &vdofs) const;
void GetEdgeInteriorVDofs(int i, Array<int> &vdofs) const;
void RebuildElementToDofTable();
/** @brief Reorder the scalar DOFs based on the element ordering.
The new ordering is constructed as follows: 1) loop over all elements as
ordered in the Mesh; 2) for each element, assign new indices to all of
its current DOFs that are still unassigned; the new indices we assign are
simply the sequence `0,1,2,...`; if there are any signed DOFs their sign
is preserved. */
void ReorderElementToDofTable();
void BuildDofToArrays();
const Table &GetElementToDofTable() const { return *elem_dof; }
const Table &GetBdrElementToDofTable() const { return *bdrElem_dof; }
int GetElementForDof(int i) const { return dof_elem_array[i]; }
int GetLocalDofForDof(int i) const { return dof_ldof_array[i]; }
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th element in the mesh object. */
const FiniteElement *GetFE(int i) const;
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th boundary face in the mesh object. */
const FiniteElement *GetBE(int i) const;
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th face in the mesh object. Faces in this case refer
to the MESHDIM-1 primitive so in 2D they are segments and in 1D they are
points.*/
const FiniteElement *GetFaceElement(int i) const;
/** @brief Returns pointer to the FiniteElement in the FiniteElementCollection
associated with i'th edge in the mesh object. */
const FiniteElement *GetEdgeElement(int i) const;
/// Return the trace element from element 'i' to the given 'geom_type'
const FiniteElement *GetTraceElement(int i, Geometry::Type geom_type) const;
/** @brief Mark degrees of freedom associated with boundary elements with
the specified boundary attributes (marked in 'bdr_attr_is_ess').
For spaces with 'vdim' > 1, the 'component' parameter can be used
to restricts the marked vDOFs to the specified component. */
virtual void GetEssentialVDofs(const Array<int> &bdr_attr_is_ess,
Array<int> &ess_vdofs,
int component = -1) const;
/** @brief Get a list of essential true dofs, ess_tdof_list, corresponding to the
boundary attributes marked in the array bdr_attr_is_ess.
For spaces with 'vdim' > 1, the 'component' parameter can be used
to restricts the marked tDOFs to the specified component. */
virtual void GetEssentialTrueDofs(const Array<int> &bdr_attr_is_ess,
Array<int> &ess_tdof_list,
int component = -1);
/// Convert a Boolean marker array to a list containing all marked indices.
static void MarkerToList(const Array<int> &marker, Array<int> &list);
/** @brief Convert an array of indices (list) to a Boolean marker array where all
indices in the list are marked with the given value and the rest are set
to zero. */
static void ListToMarker(const Array<int> &list, int marker_size,
Array<int> &marker, int mark_val = -1);
/** @brief For a partially conforming FE space, convert a marker array (nonzero
entries are true) on the partially conforming dofs to a marker array on
the conforming dofs. A conforming dofs is marked iff at least one of its
dependent dofs is marked. */
void ConvertToConformingVDofs(const Array<int> &dofs, Array<int> &cdofs);
/** @brief For a partially conforming FE space, convert a marker array (nonzero
entries are true) on the conforming dofs to a marker array on the
(partially conforming) dofs. A dof is marked iff it depends on a marked
conforming dofs, where dependency is defined by the ConformingRestriction
matrix; in other words, a dof is marked iff it corresponds to a marked
conforming dof. */
void ConvertFromConformingVDofs(const Array<int> &cdofs, Array<int> &dofs);
/** @brief Generate the global restriction matrix from a discontinuous
FE space to the continuous FE space of the same polynomial degree. */
SparseMatrix *D2C_GlobalRestrictionMatrix(FiniteElementSpace *cfes);
/** @brief Generate the global restriction matrix from a discontinuous
FE space to the piecewise constant FE space. */
SparseMatrix *D2Const_GlobalRestrictionMatrix(FiniteElementSpace *cfes);
/** @brief Construct the restriction matrix from the FE space given by
(*this) to the lower degree FE space given by (*lfes) which
is defined on the same mesh. */
SparseMatrix *H2L_GlobalRestrictionMatrix(FiniteElementSpace *lfes);
/** @brief Construct and return an Operator that can be used to transfer
GridFunction data from @a coarse_fes, defined on a coarse mesh, to @a
this FE space, defined on a refined mesh. */
/** It is assumed that the mesh of this FE space is a refinement of the mesh
of @a coarse_fes and the CoarseFineTransformations returned by the method
Mesh::GetRefinementTransforms() of the refined mesh are set accordingly.
The Operator::Type of @a T can be set to request an Operator of the set
type. Currently, only Operator::MFEM_SPARSEMAT and Operator::ANY_TYPE
(matrix-free) are supported. When Operator::ANY_TYPE is requested, the
choice of the particular Operator sub-class is left to the method. This
method also works in parallel because the transfer operator is local to
the MPI task when the input is a synchronized ParGridFunction. */
void GetTransferOperator(const FiniteElementSpace &coarse_fes,
OperatorHandle &T) const;
/** @brief Construct and return an Operator that can be used to transfer
true-dof data from @a coarse_fes, defined on a coarse mesh, to @a this FE
space, defined on a refined mesh.
This method calls GetTransferOperator() and multiplies the result by the
prolongation operator of @a coarse_fes on the right, and by the
restriction operator of this FE space on the left.
The Operator::Type of @a T can be set to request an Operator of the set
type. In serial, the supported types are: Operator::MFEM_SPARSEMAT and
Operator::ANY_TYPE (matrix-free). In parallel, the supported types are:
Operator::Hypre_ParCSR and Operator::ANY_TYPE. Any other type is treated
as Operator::ANY_TYPE: the operator representation choice is made by this
method. */
virtual void GetTrueTransferOperator(const FiniteElementSpace &coarse_fes,
OperatorHandle &T) const;
/** @brief Reflect changes in the mesh: update number of DOFs, etc. Also, calculate
GridFunction transformation operator (unless want_transform is false).
Safe to call multiple times, does nothing if space already up to date. */
virtual void Update(bool want_transform = true);
/// Get the GridFunction update operator.
const Operator* GetUpdateOperator() { Update(); return Th.Ptr(); }
/// Return the update operator in the given OperatorHandle, @a T.
void GetUpdateOperator(OperatorHandle &T) { T = Th; }
/** @brief Set the ownership of the update operator: if set to false, the
Operator returned by GetUpdateOperator() must be deleted outside the
FiniteElementSpace. */
/** The update operator ownership is automatically reset to true when a new
update operator is created by the Update() method. */
void SetUpdateOperatorOwner(bool own) { Th.SetOperatorOwner(own); }
/// Specify the Operator::Type to be used by the update operators.
/** The default type is Operator::ANY_TYPE which leaves the choice to this
class. The other currently supported option is Operator::MFEM_SPARSEMAT
which is only guaranteed to be honored for a refinement update operator.
Any other type will be treated as Operator::ANY_TYPE.
@note This operation destroys the current update operator (if owned). */
void SetUpdateOperatorType(Operator::Type tid) { Th.SetType(tid); }
/// Free the GridFunction update operator (if any), to save memory.
virtual void UpdatesFinished() { Th.Clear(); }
/// Return update counter (see Mesh::sequence)
long GetSequence() const { return sequence; }
/// Return whether or not the space is discontinuous (L2)
bool IsDGSpace() const
{
return dynamic_cast<const L2_FECollection*>(fec) != NULL;
}
/// Save finite element space to output stream @a out.
void Save(std::ostream &out) const;
/** @brief Read a FiniteElementSpace from a stream. The returned
FiniteElementCollection is owned by the caller. */
FiniteElementCollection *Load(Mesh *m, std::istream &input);
virtual ~FiniteElementSpace();
};
/// Class representing the storage layout of a QuadratureFunction.
/** Multiple QuadratureFunction%s can share the same QuadratureSpace. */
class QuadratureSpace
{
protected:
friend class QuadratureFunction; // Uses the element_offsets.
Mesh *mesh;
int order;
int size;
const IntegrationRule *int_rule[Geometry::NumGeom];
int *element_offsets; // scalar offsets; size = number of elements + 1
// protected functions
// Assuming mesh and order are set, construct the members: int_rule,
// element_offsets, and size.
void Construct();
public:
/// Create a QuadratureSpace based on the global rules from #IntRules.
QuadratureSpace(Mesh *mesh_, int order_)
: mesh(mesh_), order(order_) { Construct(); }
/// Read a QuadratureSpace from the stream @a in.
QuadratureSpace(Mesh *mesh_, std::istream &in);
virtual ~QuadratureSpace() { delete [] element_offsets; }
/// Return the total number of quadrature points.
int GetSize() const { return size; }
/// Get the IntegrationRule associated with mesh element @a idx.
const IntegrationRule &GetElementIntRule(int idx) const
{ return *int_rule[mesh->GetElementBaseGeometry(idx)]; }
/// Write the QuadratureSpace to the stream @a out.
void Save(std::ostream &out) const;
};
/** @brief Base class for transfer algorithms that construct transfer Operator%s
between two finite element (FE) spaces. */
/** Generally, the two FE spaces (domain and range) can be defined on different
meshes. */
class GridTransfer
{
protected:
FiniteElementSpace &dom_fes; ///< Domain FE space
FiniteElementSpace &ran_fes; ///< Range FE space
/** @brief Desired Operator::Type for the construction of all operators
defined by the underlying transfer algorithm. It can be ignored by
derived classes. */
Operator::Type oper_type;
OperatorHandle fw_t_oper; ///< Forward true-dof operator
OperatorHandle bw_t_oper; ///< Backward true-dof operator
#ifdef MFEM_USE_MPI
bool parallel;
#endif
bool Parallel() const
{
#ifndef MFEM_USE_MPI
return false;
#else
return parallel;
#endif
}
const Operator &MakeTrueOperator(FiniteElementSpace &fes_in,
FiniteElementSpace &fes_out,
const Operator &oper,
OperatorHandle &t_oper);
public:
/** Construct a transfer algorithm between the domain, @a dom_fes_, and
range, @a ran_fes_, FE spaces. */
GridTransfer(FiniteElementSpace &dom_fes_, FiniteElementSpace &ran_fes_);
/// Virtual destructor
virtual ~GridTransfer() { }
/** @brief Set the desired Operator::Type for the construction of all
operators defined by the underlying transfer algorithm. */
/** The default value is Operator::ANY_TYPE which typically corresponds to
a matrix-free operator representation. Note that derived classes are not
required to support this setting and can ignore it. */
void SetOperatorType(Operator::Type type) { oper_type = type; }
/** @brief Return an Operator that transfers GridFunction%s from the domain
FE space to GridFunction%s in the range FE space. */
virtual const Operator &ForwardOperator() = 0;
/** @brief Return an Operator that transfers GridFunction%s from the range
FE space back to GridFunction%s in the domain FE space. */
virtual const Operator &BackwardOperator() = 0;
/** @brief Return an Operator that transfers true-dof Vector%s from the
domain FE space to true-dof Vector%s in the range FE space. */
/** This method is implemented in the base class, based on ForwardOperator(),
however, derived classes can overload the construction, if necessary. */
virtual const Operator &TrueForwardOperator()
{
return MakeTrueOperator(dom_fes, ran_fes, ForwardOperator(), fw_t_oper);
}
/** @brief Return an Operator that transfers true-dof Vector%s from the
range FE space back to true-dof Vector%s in the domain FE space. */
/** This method is implemented in the base class, based on
BackwardOperator(), however, derived classes can overload the
construction, if necessary. */
virtual const Operator &TrueBackwardOperator()
{
return MakeTrueOperator(ran_fes, dom_fes, BackwardOperator(), bw_t_oper);
}
};
/** @brief Transfer data between a coarse mesh and an embedded refined mesh
using interpolation. */
/** The forward, coarse-to-fine, transfer uses nodal interpolation. The
backward, fine-to-coarse, transfer is defined locally (on a coarse element)
as B = (F^t M_f F)^{-1} F^t M_f, where F is the forward transfer matrix, and
M_f is a mass matrix on the union of all fine elements comprising the coarse
element. Note that the backward transfer operator, B, is a left inverse of
the forward transfer operator, F, i.e. B F = I. Both F and B are defined in
reference space and do not depend on the actual physical shape of the mesh
elements.
It is assumed that both the coarse and the fine FiniteElementSpace%s use
compatible types of elements, e.g. finite elements with the same map-type
(VALUE, INTEGRAL, H_DIV, H_CURL - see class FiniteElement). Generally, the
FE spaces can have different orders, however, in order for the backward
operator to be well-defined, the (local) number of the fine dofs should not
be smaller than the number of coarse dofs. */
class InterpolationGridTransfer : public GridTransfer
{
protected:
BilinearFormIntegrator *mass_integ; ///< Ownership depends on #own_mass_integ
bool own_mass_integ; ///< Ownership flag for #mass_integ
OperatorHandle F; ///< Forward, coarse-to-fine, operator
OperatorHandle B; ///< Backward, fine-to-coarse, operator
public:
InterpolationGridTransfer(FiniteElementSpace &coarse_fes,
FiniteElementSpace &fine_fes)
: GridTransfer(coarse_fes, fine_fes),
mass_integ(NULL), own_mass_integ(false)
{ }
virtual ~InterpolationGridTransfer();
/** @brief Assign a mass integrator to be used in the construction of the
backward, fine-to-coarse, transfer operator. */
void SetMassIntegrator(BilinearFormIntegrator *mass_integ_,
bool own_mass_integ_ = true);
virtual const Operator &ForwardOperator();
virtual const Operator &BackwardOperator();
};
/** @brief Transfer data between a coarse mesh and an embedded refined mesh
using L2 projection. */
/** The forward, coarse-to-fine, transfer uses L2 projection. The backward,
fine-to-coarse, transfer is defined locally (on a coarse element) as
B = (F^t M_f F)^{-1} F^t M_f, where F is the forward transfer matrix, and
M_f is the mass matrix on the union of all fine elements comprising the
coarse element. Note that the backward transfer operator, B, is a left
inverse of the forward transfer operator, F, i.e. B F = I. Both F and B are
defined in physical space and, generally, vary between different mesh
elements.
This class currently only fully supports L2 finite element spaces and fine
meshes that are a uniform refinement of the coarse mesh. Generally, the
coarse and fine FE spaces can have different orders, however, in order for
the backward operator to be well-defined, the number of the fine dofs (in a
coarse element) should not be smaller than the number of coarse dofs.
If used on H1 finite element spaces, the transfer will be performed locally,
and the value of shared (interface) degrees of freedom will be determined by
the value of the last transfer to be performed (according to the element
numbering in the finite element space). As a consequence, the mass
conservation properties for this operator from the L2 case do not carry over
to H1 spaces. */
class L2ProjectionGridTransfer : public GridTransfer
{
protected:
/** Class representing projection operator between a high-order L2 finite
element space on a coarse mesh, and a low-order L2 finite element space
on a refined mesh (LOR). We assume that the low-order space, fes_lor,
lives on a mesh obtained by refining the mesh of the high-order space,
fes_ho. */
class L2Projection : public Operator
{
const FiniteElementSpace &fes_ho;
const FiniteElementSpace &fes_lor;
int ndof_lor, ndof_ho, nref;
Table ho2lor;
DenseTensor R, P;
public:
L2Projection(const FiniteElementSpace &fes_ho_,
const FiniteElementSpace &fes_lor_);
/// Perform the L2 projection onto the LOR space
virtual void Mult(const Vector &x, Vector &y) const;
/// Perform the mass conservative left-inverse prolongation operation.
/// This functionality is also provided as an Operator by L2Prolongation.
void Prolongate(const Vector &x, Vector &y) const;
virtual ~L2Projection() { }
};
/** Mass-conservative prolongation operator going in the opposite direction
as L2Projection. This operator is a left inverse to the L2Projection. */
class L2Prolongation : public Operator
{
const L2Projection &l2proj;
public:
L2Prolongation(const L2Projection &l2proj_)
: Operator(l2proj_.Width(), l2proj_.Height()), l2proj(l2proj_) { }
void Mult(const Vector &x, Vector &y) const
{
l2proj.Prolongate(x, y);
}
virtual ~L2Prolongation() { }
};
L2Projection *F; ///< Forward, coarse-to-fine, operator
L2Prolongation *B; ///< Backward, fine-to-coarse, operator
public:
L2ProjectionGridTransfer(FiniteElementSpace &coarse_fes,
FiniteElementSpace &fine_fes)
: GridTransfer(coarse_fes, fine_fes),
F(NULL), B(NULL)
{ }
virtual const Operator &ForwardOperator();
virtual const Operator &BackwardOperator();
};
inline bool UsesTensorBasis(const FiniteElementSpace& fes)
{
return dynamic_cast<const mfem::TensorBasisElement *>(fes.GetFE(0))!=nullptr;
}
}
#endif