Files
mfem/mesh/ncmesh.hpp
T

1431 lines
56 KiB
C++

// 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_NCMESH
#define MFEM_NCMESH
#include "../config/config.hpp"
#include "../general/hash.hpp"
#include "../general/globals.hpp"
#include "../general/sort_pairs.hpp"
#include "../linalg/densemat.hpp"
#include "element.hpp"
#include "vertex.hpp"
#include "../fem/geom.hpp"
#include <vector>
#include <map>
#include <iostream>
#include <unordered_map>
namespace mfem
{
/** Represents the index of an element to refine, plus a refinement type.
The refinement type is needed for anisotropic refinement of quads and hexes.
Bits 0,1 and 2 of 'ref_type' specify whether the element should be split
in the X, Y and Z directions, respectively (Z is ignored for quads). The
refinement spacing or scale in each direction is a number in (0,1), with the
default 0.5 meaning bisection. This linear scale parameter defines the
position of the refinement between the beginning (0) and end (1) of the
reference element in each direction. */
struct Refinement
{
int index; ///< Mesh element number
enum : char { X = 1, Y = 2, Z = 4, XY = 3, XZ = 5, YZ = 6, XYZ = 7 };
using ScaledType = std::pair<char, real_t>;
real_t s[3]; /// Refinement scale in each dimension
Refinement() = default;
/// Refinement type XYZ, with scale 0.5.
Refinement(int index);
/// Default case of empty list @a refs is XYZ with scale 0.5.
Refinement(int index, const std::initializer_list<ScaledType> &refs);
/// Refine element with a single type and scale in all dimensions.
Refinement(int index, char type, real_t scale = 0.5);
/// Return the type as char.
char GetType() const;
/// Set the element, type, and scale.
void Set(int element, char type,
real_t scale = 0.5); /// Uses @a scale in all dimensions
/// Set the type and scale, assuming the element is already set.
void SetType(char type,
real_t scale = 0.5); /// Uses @a scale in all dimensions
/// Set the scale in the directions for the currently set type.
void SetScaleForType(const real_t *scale);
private :
void SetScale(const ScaledType &ref);
};
/// Defines the position of a fine element within a coarse element.
struct Embedding
{
/// Coarse %Element index in the coarse mesh.
int parent;
/** The (geom, matrix) pair determines the sub-element transformation for the
fine element: CoarseFineTransformations::point_matrices[geom](matrix) is
the point matrix of the region within the coarse element reference
domain.*/
unsigned geom : 4;
unsigned matrix : 27;
/// For internal use: 0 if regular fine element, 1 if parallel ghost element.
unsigned ghost : 1;
Embedding() = default;
Embedding(int elem, Geometry::Type geom, int matrix = 0, bool ghost = false)
: parent(elem), geom(geom), matrix(matrix), ghost(ghost) {}
};
/// Defines the coarse-fine transformations of all fine elements.
struct CoarseFineTransformations
{
/// Fine element positions in their parents.
Array<Embedding> embeddings;
/** A "dictionary" of matrices for IsoparametricTransformation. Use
Embedding::{geom,matrix} to access a fine element point matrix. */
DenseTensor point_matrices[Geometry::NumGeom];
/** Invert the 'embeddings' array: create a Table with coarse elements as
rows and fine elements as columns. If 'want_ghosts' is false, parallel
ghost fine elements are not included in the table. */
void MakeCoarseToFineTable(Table &coarse_to_fine,
bool want_ghosts = false) const;
void Clear();
bool IsInitialized() const;
long MemoryUsage() const;
MFEM_DEPRECATED
void GetCoarseToFineMap(const Mesh &fine_mesh, Table &coarse_to_fine) const
{ MakeCoarseToFineTable(coarse_to_fine, true); (void) fine_mesh; }
};
void Swap(CoarseFineTransformations &a, CoarseFineTransformations &b);
struct MatrixMap; // for internal use
/** @brief For a NURBS mesh with nonconforming patch topology, this struct
provides a map from hanging vertices in the patch topology to the knotvector
of a neighboring patch. This facilitates ensuring mesh conformity.
*/
class VertexToKnotSpan
{
public:
/// Set the spatial dimension and number of vertices.
void SetSize(int dimension, int numVertices);
// The following set and get functions are for a single entry in the array of
// data, for a hanging vertex in the patch topology, with the given 'index'.
// The vertex index is 'v', parent vertices are 'pv', and knot-span is 'ks'.
/// Set the data for a vertex in 2D.
void SetVertex2D(int index, int v, int ks,
const std::array<int, 2> &pv);
/// Set the data for a vertex in 3D.
void SetVertex3D(int index, int v, const std::array<int, 2> &ks,
const std::array<int, 4> &pv);
/// Set the knot-span index for a vertex in 2D.
void SetKnotSpan2D(int index, int ks);
/// Set the knot-span indices for a vertex in 3D.
void SetKnotSpans3D(int index, const std::array<int, 2> &ks);
/// Get the data for a vertex in 2D.
void GetVertex2D(int index, int &v, int &ks,
std::array<int, 2> &pv) const;
/// Get the data for a vertex in 3D.
void GetVertex3D(int index, int &v, std::array<int, 2> &ks,
std::array<int, 4> &pv) const;
/// Print all the data.
void Print(std::ostream &os) const;
/// Return the number of vertices.
int Size() const { return data.NumRows(); }
/// Return the vertex pair representing the parent edge (2D) or face (3D).
std::pair<int, int> GetVertexParentPair(int index) const;
private:
int dim; /// Spatial dimension
Array2D<int> data; /// Row-wise data for each vertex.
};
/** @brief A class for non-conforming AMR. The class is not used directly by the
* user, rather it is an extension of the Mesh class.
*
* In general, the class is used by MFEM as follows:
*
* 1. NCMesh is constructed from elements of an existing Mesh. The elements are
* copied and become roots of the refinement hierarchy.
*
* 2. Some elements are refined with the Refine() method. Both isotropic and
* anisotropic refinements of quads/hexes are supported.
*
* 3. A new Mesh is created from NCMesh containing the leaf elements. This new
* Mesh may have non-conforming (hanging) edges and faces and is the one
* seen by the user.
*
* 4. FiniteElementSpace asks NCMesh for a list of conforming, master and slave
* edges/faces and creates the conforming interpolation matrix P.
*
* 5. A continuous/conforming solution is obtained by solving P'*A*P x = P'*b.
*
* 6. Repeat from step 2.
*/
class NCMesh
{
protected:
NCMesh() = default;
public:
//// Initialize with elements from an existing Mesh.
explicit NCMesh(const Mesh *mesh);
/** Load from a stream. The id header is assumed to have been read already
from \param[in] input . \param[in] version is 10 for the v1.0 NC format,
or 1 for the legacy v1.1 format. \param[out] curved is set to 1 if the
curvature GridFunction follows after mesh data. \param[out] is_nc (again
treated as a boolean) is set to 0 if the legacy v1.1 format in fact
defines a conforming mesh. See Mesh::Loader for details. */
NCMesh(std::istream &input, int version, int &curved, int &is_nc);
/// Deep copy of another instance.
NCMesh(const NCMesh &other);
/// Copy assignment not supported
NCMesh& operator=(NCMesh&) = delete;
virtual ~NCMesh();
/// Return the dimension of the NCMesh.
int Dimension() const { return Dim; }
/// Return the space dimension of the NCMesh.
int SpaceDimension() const { return spaceDim; }
/// Return the number of vertices in the NCMesh.
int GetNVertices() const { return NVertices; }
/// Return the number of edges in the NCMesh.
int GetNEdges() const { return NEdges; }
/// Return the number of (2D) faces in the NCMesh.
int GetNFaces() const { return NFaces; }
virtual int GetNGhostElements() const { return 0; }
/** Perform the given batch of refinements. Please note that in the presence
of anisotropic splits additional refinements may be necessary to keep the
mesh consistent. However, the function always performs at least the
requested refinements. */
virtual void Refine(const Array<Refinement> &refinements);
/** Check the mesh and potentially refine some elements so that the maximum
difference of refinement levels between adjacent elements is not greater
than 'max_nc_level'. */
virtual void LimitNCLevel(int max_nc_level);
/** Return a list of derefinement opportunities. Each row of the table
contains Mesh indices of existing elements that can be derefined to form
a single new coarse element. Row numbers are then passed to Derefine.
This function works both in serial and parallel. */
const Table &GetDerefinementTable();
/** Check derefinements returned by GetDerefinementTable and mark those that
can be done safely so that the maximum NC level condition is not
violated. On return, level_ok.Size() == deref_table.Size() and contains
0/1s. */
virtual void CheckDerefinementNCLevel(const Table &deref_table,
Array<int> &level_ok, int max_nc_level);
/** Perform a subset of the possible derefinements (see
GetDerefinementTable). Note that if anisotropic refinements are present
in the mesh, some of the derefinements may have to be skipped to preserve
mesh consistency. */
virtual void Derefine(const Array<int> &derefs);
// master/slave lists
/// Identifies a vertex/edge/face in both Mesh and NCMesh.
struct MeshId
{
int index; ///< Mesh number
int element; ///< NCMesh::Element containing this vertex/edge/face
signed char local; ///< local number within 'element'
signed char geom; ///< Geometry::Type (faces only) (char to save RAM)
Geometry::Type Geom() const { return Geometry::Type(geom); }
MeshId() = default;
MeshId(int index, int element, int local, int geom = -1)
: index(index), element(element), local(local), geom(geom) {}
};
/** Nonconforming edge/face that has more than one neighbor. The neighbors
are stored in NCList::slaves[i], slaves_begin <= i < slaves_end. */
struct Master : public MeshId
{
int slaves_begin, slaves_end; ///< slave faces
Master() = default;
Master(int index, int element, int local, int geom, int sb, int se)
: MeshId(index, element, local, geom)
, slaves_begin(sb), slaves_end(se) {}
};
/// Nonconforming edge/face within a bigger edge/face.
struct Slave : public MeshId
{
int master; ///< master number (in Mesh numbering)
unsigned matrix : 24; ///< index into NCList::point_matrices[geom]
unsigned edge_flags : 8; ///< orientation flags, see OrientedPointMatrix
Slave() = default;
Slave(int index, int element, int local, int geom)
: MeshId(index, element, local, geom)
, master(-1), matrix(0), edge_flags(0) {}
};
/// Lists all edges/faces in the nonconforming mesh.
struct NCList
{
Array<MeshId> conforming; ///< All MeshIds corresponding to conformal faces
Array<Master> masters; ///< All MeshIds corresponding to master faces
Array<Slave> slaves; ///< All MeshIds corresponding to slave faces
/// List of unique point matrices for each slave geometry.
Array<DenseMatrix*> point_matrices[Geometry::NumGeom];
void OrientedPointMatrix(const Slave &slave,
DenseMatrix &oriented_matrix) const;
/// Particular MeshId type, used for allowing static casting to the
/// appropriate child type after searching the NCList. UNRECOGNIZED
/// denotes that an instance is not known within the NCList, meaning that
/// it does not play a part in NC mechanics. This can be because the index
/// did not exist in the original Mesh, or because the entry is a boundary
/// face, whose NC status is always conforming.
enum class MeshIdType : char {CONFORMING, MASTER, SLAVE, UNRECOGNIZED};
/// Helper storing a reference to a MeshId type, and the face type it can
/// be cast to
struct MeshIdAndType
{
const MeshId * const id; ///< Pointer to a possible MeshId, nullptr if not found
/// MeshIdType corresponding to the MeshId. UNRECOGNIZED if unfound.
const MeshIdType type;
};
/// Return a mesh id and type for a given nc index.
MeshIdAndType GetMeshIdAndType(int index) const;
/// Return a face type for a given nc index.
MeshIdType GetMeshIdType(int index) const;
/// Given an index, check if this is a certain face type.
bool CheckMeshIdType(int index, MeshIdType type) const;
/// Erase the contents of the conforming, master and slave arrays.
void Clear();
/// Whether the NCList is empty.
bool Empty() const
{
return conforming.Size() == 0
&& masters.Size() == 0
&& slaves.Size() == 0;
}
/// The total size of the component arrays in the NCList.
long TotalSize() const
{
return conforming.Size() + masters.Size() + slaves.Size();
}
/// The memory usage of the three public arrays. Does not account for the
/// inverse index.
long MemoryUsage() const;
~NCList() { Clear(); }
private:
// Check for existence or construct the inv_index list map if necessary.
// const because only modifies the mutable member inv_index.
void BuildIndex() const;
/// A lazily constructed map from index to MeshId. Built whenever
/// GetMeshIdAndType, GetMeshIdType or CheckMeshIdType is called for the
/// first time. The MeshIdType is stored with, to enable casting to Slave
/// or Master elements appropriately.
mutable std::unordered_map<int, std::pair<MeshIdType, int>> inv_index;
};
/// Return the current list of conforming and nonconforming faces.
const NCList& GetFaceList()
{
if (face_list.Empty()) { BuildFaceList(); }
return face_list;
}
/// Return the current list of conforming and nonconforming edges.
const NCList& GetEdgeList()
{
if (edge_list.Empty()) { BuildEdgeList(); }
return edge_list;
}
/** Return a list of vertices (in 'conforming'); this function is provided
for uniformity/completeness. Needed in ParNCMesh/ParFESpace. */
const NCList& GetVertexList()
{
if (vertex_list.Empty()) { BuildVertexList(); }
return vertex_list;
}
/// Return vertex/edge/face list (entity = 0/1/2, respectively).
const NCList& GetNCList(int entity)
{
switch (entity)
{
case 0: return GetVertexList();
case 1: return GetEdgeList();
default: return GetFaceList();
}
}
const VertexToKnotSpan& GetVertexToKnotSpan() const
{
return vertex_to_knotspan;
}
/// Remap knot-span indices @a vertex_to_knotspan after refinement.
void RefineVertexToKnotSpan(const std::vector<Array<int>> &kvf,
const Array<KnotVector*> &kvext,
std::map<std::pair<int, int>,
std::array<int, 2>> &parentToKV);
// coarse/fine transforms
/** Remember the current layer of leaf elements before the mesh is refined.
Needed by GetRefinementTransforms(), must be called before Refine(). */
void MarkCoarseLevel();
/** After refinement, calculate the relation of each fine element to its
parent coarse element. Note that Refine() or LimitNCLevel() can be called
multiple times between MarkCoarseLevel() and this function. */
const CoarseFineTransformations& GetRefinementTransforms() const;
/** After derefinement, calculate the relations of previous fine elements
(some of which may no longer exist) to the current leaf elements. Unlike
for refinement, Derefine() may only be called once before this function
so there is no MarkFineLevel(). */
const CoarseFineTransformations& GetDerefinementTransforms() const;
/// Free all internal data created by the above three functions.
void ClearTransforms();
// grid ordering
/** Return a space filling curve for a rectangular grid of elements.
Implemented is a generalized Hilbert curve for arbitrary grid dimensions.
If the width is odd, height should be odd too, otherwise one diagonal
(vertex-neighbor) step cannot be avoided in the curve. Even dimensions
are recommended. */
static void GridSfcOrdering2D(int width, int height,
Array<int> &coords);
/** Return a space filling curve for a 3D rectangular grid of elements. The
Hilbert-curve-like algorithm works well for even dimensions. For odd
width/height/depth it tends to produce some diagonal (edge-neighbor)
steps. Even dimensions are recommended. */
static void GridSfcOrdering3D(int width, int height, int depth,
Array<int> &coords);
// utility
/// Return Mesh vertex indices of an edge identified by 'edge_id'.
void GetEdgeVertices(const MeshId &edge_id, int vert_index[2],
bool oriented = true) const;
/** Return "NC" orientation of an edge. As opposed to standard Mesh edge
orientation based on vertex IDs, "NC" edge orientation follows the local
edge orientation within the element 'edge_id.element' and is thus
processor independent. TODO: this seems only partially true? */
int GetEdgeNCOrientation(const MeshId &edge_id) const;
/** Return Mesh vertex and edge indices of a face identified by 'face_id'.
The return value is the number of face vertices. */
int GetFaceVerticesEdges(const MeshId &face_id,
int vert_index[4], int edge_index[4],
int edge_orientation[4]) const;
/** Given an edge (by its vertex indices v1 and v2) return the first
(geometric) parent edge that exists in the Mesh or -1 if there is no such
parent. */
int GetEdgeMaster(int v1, int v2) const;
/** Get a list of vertices (2D/3D), edges (3D) and faces (3D) that coincide
with boundary elements with the specified attributes (marked in
'bdr_attr_is_ess'). In 3D this function also reveals "hidden" boundary
edges. In parallel it helps identifying boundary vertices/edges/faces
affected by non-local boundary elements. Hidden faces can occur for an
internal boundary coincident to a processor boundary.
*/
/**
* @brief Get a list of vertices (2D/3D), edges (3D) and faces (3D) that
* coincide with boundary elements with the specified attributes (marked in
* 'bdr_attr_is_ess').
*
* @details In 3D this function also reveals "hidden" boundary edges. In
* parallel it helps identifying boundary vertices/edges/faces affected by
* non-local boundary elements. Hidden faces can occur for an internal
* boundary coincident to a processor boundary.
*
* @param bdr_attr_is_ess Indicator if a given attribute is essential.
* @param bdr_vertices Array of vertices that are essential.
* @param bdr_edges Array of edges that are essential.
* @param bdr_faces Array of faces that are essential.
*/
virtual void GetBoundaryClosure(const Array<int> &bdr_attr_is_ess,
Array<int> &bdr_vertices,
Array<int> &bdr_edges, Array<int> &bdr_faces);
/// Return element geometry type. @a index is the Mesh element number.
Geometry::Type GetElementGeometry(int index) const
{ return elements[leaf_elements[index]].Geom(); }
/// Return face geometry type. @a index is the Mesh face number.
Geometry::Type GetFaceGeometry(int index) const
{ return Geometry::Type(face_geom[index]); }
/// Return the number of root elements.
int GetNumRootElements() { return root_state.Size(); }
/// Return the distance of leaf @a i from the root.
int GetElementDepth(int i) const;
/** Return the size reduction compared to the root element (ignoring local
stretching and curvature). */
int GetElementSizeReduction(int i) const;
/// Return the faces and face attributes of leaf element @a i.
void GetElementFacesAttributes(int i, Array<int> &faces,
Array<int> &fattr) const;
/// Set the attribute of leaf element @a i, which is a Mesh element index.
void SetAttribute(int i, int attr)
{ elements[leaf_elements[i]].attribute = attr; }
/** I/O: Print the mesh in "MFEM NC mesh v1.0" format. If @a comments is
non-empty, it will be printed after the first line of the file, and each
line should begin with '#'. */
void Print(std::ostream &out, const std::string &comments = "",
bool nurbs=false) const;
/// I/O: Return true if the mesh was loaded from the legacy v1.1 format.
bool IsLegacyLoaded() const { return Legacy; }
/// I/O: Return a map from old (v1.1) vertex indices to new vertex indices.
void LegacyToNewVertexOrdering(Array<int> &order) const;
/// Save memory by releasing all non-essential and cached data.
virtual void Trim();
/// Return total number of bytes allocated.
long MemoryUsage() const;
int PrintMemoryDetail() const;
/// Return true for ParNCMesh with more than one MPI process.
virtual bool IsParallel() const { return false; }
using RefCoord = std::int64_t;
static constexpr int MaxElemNodes =
8; ///< Number of nodes an element can have
static constexpr int MaxElemEdges =
12; ///< Number of edges an element can have
static constexpr int MaxElemFaces =
6; ///< Number of faces an element can have
static constexpr int MaxElemChildren =
10; ///< Number of children an element can have
static constexpr int MaxFaceNodes =
4; ///< Number of faces an element can have
/**
* @brief Given a node index, return the vertex index associated
*
* @param node
* @return int
*/
int GetNodeVertex(int node) { return nodes[node].vert_index; }
protected: // non-public interface for the Mesh class
friend class Mesh;
/// Fill Mesh::{vertices,elements,boundary} for the current finest level.
void GetMeshComponents(Mesh &mesh) const;
/** Get edge and face numbering from 'mesh' (i.e., set all Edge::index and
Face::index) after a new mesh was created from us. */
void OnMeshUpdated(Mesh *mesh);
/** Delete top-level vertex coordinates if the Mesh became curved, e.g., by
calling Mesh::SetCurvature or otherwise setting the Nodes. */
void MakeTopologyOnly() { coordinates.DeleteAll(); }
protected: // implementation
int Dim, spaceDim; ///< dimensions of the elements and the vertex coordinates
int MyRank; ///< used in parallel, or when loading a parallel file in serial
bool Iso; ///< true if the mesh only contains isotropic refinements
int Geoms; ///< bit mask of element geometries present, see InitGeomFlags()
bool Legacy; ///< true if the mesh was loaded from the legacy v1.1 format
/** A Node can hold a vertex, an edge, or both. Elements directly point to
their corner nodes, but edge nodes also exist and can be accessed using a
hash-table given their two end-point node IDs. All nodes can be accessed
in this way, with the exception of top-level vertex nodes. When an
element is being refined, the mid-edge nodes are readily available with
this mechanism. The new elements "sign in" to the nodes by increasing the
reference counts of their vertices and edges. The parent element "signs
off" its nodes by decrementing the ref counts. */
struct Node : public Hashed2
{
char vert_refc, edge_refc;
int vert_index, edge_index;
Node() : vert_refc(0), edge_refc(0), vert_index(-1), edge_index(-1),
scale(0.5), scaleSet(false) {}
~Node();
bool HasVertex() const { return vert_refc > 0; }
bool HasEdge() const { return edge_refc > 0; }
// decrease vertex/edge ref count, return false if Node should be deleted
bool UnrefVertex() { --vert_refc; return vert_refc || edge_refc; }
bool UnrefEdge() { --edge_refc; return vert_refc || edge_refc; }
real_t GetScale() const { return scale; }
void SetScale(real_t s, bool overwrite = false);
private:
real_t scale; ///< Scale from struct Refinement, default 0.5
bool scaleSet; ///< Indicates whether scale is set and cannot be changed
#ifdef MFEM_USE_DOUBLE
static constexpr real_t scaleTol = 1.0e-8; ///< Scale comparison tolerance
#else
static constexpr real_t scaleTol = 1.0e-5; ///< Scale comparison tolerance
#endif
};
/** Similarly to nodes, faces can be accessed by hashing their four vertex
node IDs. A face knows about the one or two elements that are using it. A
face that is not on the boundary and only has one element referencing it
is either a master or a slave face. */
struct Face : public Hashed4
{
int attribute; ///< boundary element attribute, -1 if internal face
int index; ///< face number in the Mesh
int elem[2]; ///< up to 2 elements sharing the face
Face() : attribute(-1), index(-1) { elem[0] = elem[1] = -1; }
bool Boundary() const { return attribute >= 0; }
bool Unused() const { return elem[0] < 0 && elem[1] < 0; }
// add or remove an element from the 'elem[2]' array
void RegisterElement(int e);
void ForgetElement(int e);
/// Return one of elem[0] or elem[1] and make sure the other is -1.
int GetSingleElement() const;
int GetAttribute() const { return attribute; }
};
/** This is an element in the refinement hierarchy. Each element has either
been refined and points to its children, or is a leaf and points to its
vertex nodes. */
struct Element
{
char geom; ///< Geometry::Type of the element (char for storage only)
char ref_type; ///< bit mask of X,Y,Z refinements (bits 0,1,2 respectively)
char tet_type; ///< tetrahedron split type, currently always 0
char flag; ///< generic flag/marker, can be used by algorithms
int index; ///< element number in the Mesh, -1 if refined
int rank; ///< processor number (ParNCMesh), -1 if undefined/unknown
int attribute;
union
{
int node[MaxElemNodes]; ///< element corners (if ref_type == 0)
int child[MaxElemChildren]; ///< 2-10 children (if ref_type != 0)
};
int parent; ///< parent element, -1 if this is a root element, -2 if free'd
Element(Geometry::Type geom, int attr);
Geometry::Type Geom() const { return Geometry::Type(geom); }
bool IsLeaf() const { return !ref_type && (parent != -2); }
int GetAttribute() const { return attribute; }
};
// primary data
HashTable<Node> nodes; // associative container holding all Nodes
HashTable<Face> faces; // associative container holding all Faces
bool using_scaling = false; // Whether Node::scale is being used
BlockArray<Element> elements; // storage for all Elements
Array<int> free_element_ids; // unused element ids - indices into 'elements'
public:
/**
* @brief The number of Nodes.
*
* @return int
*/
int GetNumNodes() const { return nodes.Size(); }
/**
* @brief Access a Node
*
* @param i Index of the node
* @return const Node&
*/
const Node& GetNode(int i) const {return nodes[i]; }
/**
* @brief The number of faces
*
* @return int
*/
int GetNumFaces() const { return faces.Size(); }
/**
* @brief Access a Face
*
* @param i Index of the face
* @return const Face&
*/
const Face& GetFace(int i) const {return faces[i]; }
/**
* @brief The number of elements
*
* @return int
*/
int GetNumElements() const { return elements.Size(); }
/**
* @brief Access an Element
*
* @param i Index of the element
* @return const Element&
*/
const Element& GetElement(int i) const { return elements[i]; }
/**
* @brief Given a set of nodes defining a face, traverse the nodes structure
* to find the nodes that make up the parent face and replace the input nodes
* with the parent nodes. Additionally return the child index that the child
* face would be, relative to the discovered parent face.
* @details This method is concerned with the construction of an NCMesh
* structure for a d-1 manifold of an existing NCMesh. It forms a key element
* in a leaf -> root traversal of the parent ncmesh elements structure.
*
* @param[out] nodes The collection of nodes whose parent we are searching
* for
* @return int The child index corresponding to placing the face for the
* original nodes within the face defined by the returned parent nodes. If
* child index is -1, then the face is made up of root nodes, and nodes is
* unchanged.
*/
int ParentFaceNodes(std::array<int, 4> &nodes) const;
/**
* @brief Method for finding the nodes associated to a @a face
* @return Nodes making up the face
*/
std::array<int, 4> FindFaceNodes(int face) const;
std::array<int, 4> FindFaceNodes(const Face &fa) const;
/**
* @brief Backwards compatible method for finding the @a node associated to a
* @a face
*/
MFEM_DEPRECATED void FindFaceNodes(int face, int node[4]) const;
protected:
/** Initial traversal state (~ element orientation) for each root element
NOTE: M = root_state.Size() is the number of root elements. NOTE: the
first M items of 'elements' is the coarse mesh. */
Array<int> root_state;
/** Coordinates of top-level vertices (organized as triples). If empty, the
Mesh is curved (Nodes != NULL) and NCMesh is topology-only. */
Array<real_t> coordinates;
// secondary data
/** Apart from the primary data structure, which is the element/node/face
hierarchy, there is secondary data that is derived from the primary data
and needs to be updated when the primary data changes. Update() takes
care of that and needs to be called after each refinement and
derefinement. */
virtual void Update();
// set by UpdateLeafElements, UpdateVertices and OnMeshUpdated
int NElements, NVertices, NEdges, NFaces;
// NOTE: the serial code understands the bare minimum about ghost elements
// and other ghost entities in order to be able to load parallel partial
// meshes
int NGhostElements, NGhostVertices, NGhostEdges, NGhostFaces;
Array<int> leaf_elements; ///< finest elements, in Mesh ordering (+ ghosts)
Array<int> leaf_sfc_index; ///< natural tree ordering of leaf elements
Array<int> vertex_nodeId; ///< vertex-index to node-id map, see UpdateVertices
NCList face_list; ///< lazy-initialized list of faces, see GetFaceList
NCList edge_list; ///< lazy-initialized list of edges, see GetEdgeList
NCList vertex_list; ///< lazy-initialized list of vertices, see GetVertexList
Array<int> boundary_faces; ///< subset of all faces, set by BuildFaceList
Array<char> face_geom; ///< face geometry by face index, set by OnMeshUpdated
Table element_vertex; ///< leaf-element to vertex table, see FindSetNeighbors
/// Update the leaf elements indices in leaf_elements
void UpdateLeafElements();
/** @brief This method assigns indices to vertices (Node::vert_index) that
will be seen by the Mesh class and the rest of MFEM.
We must be careful to:
1. Stay compatible with the conforming code, which expects top-level
(original) vertices to be indexed first, otherwise GridFunctions
defined on a conforming mesh would no longer be valid when the mesh is
converted to an NC mesh.
2. Make sure serial NCMesh is compatible with the parallel ParNCMesh, so
it is possible to read parallel partial solutions in serial code
(e.g., serial GLVis). This means handling ghost elements, if present.
3. Assign vertices in a globally consistent order for parallel meshes: if
two vertices i,j are shared by two ranks r1,r2, and i<j on r1, then
i<j on r2 as well. This is true for top-level vertices but also for
the remaining shared vertices thanks to the globally consistent SFC
ordering of the leaf elements. This property reduces communication and
simplifies ParNCMesh. */
void UpdateVertices(); ///< update Vertex::index and vertex_nodeId
/** Collect the leaf elements in leaf_elements, and the ghost elements in
ghosts. Compute and set the element indices of @a elements. On quad and
hex refined elements tries to order leaf elements along a space-filling
curve according to the given @a state variable. */
void CollectLeafElements(int elem, int state, Array<int> &ghosts,
int &counter);
/** Try to find a space-filling curve friendly orientation of the root
elements: set 'root_state' based on the ordering of coarse elements. Note
that the coarse mesh itself must be ordered as an SFC by e.g.
Mesh::GetGeckoElementOrdering. */
void InitRootState(int root_count);
/** Compute the Geometry::Type present in the root elements (coarse elements)
and set @a Geoms bitmask accordingly. */
void InitGeomFlags();
/// Return true if the mesh contains prism elements.
bool HavePrisms() const { return Geoms & (1 << Geometry::PRISM); }
/// Return true if the mesh contains pyramid elements.
bool HavePyramids() const { return Geoms & (1 << Geometry::PYRAMID); }
/// Return true if the mesh contains tetrahedral elements.
bool HaveTets() const { return Geoms & (1 << Geometry::TETRAHEDRON); }
/// Return true if the Element @a el is a ghost element.
bool IsGhost(const Element &el) const { return el.rank != MyRank; }
// refinement/derefinement
Array<Refinement> ref_stack; ///< stack of scheduled refinements (temporary)
HashTable<Node> shadow; ///< temporary storage for reparented nodes
Array<Triple<int, int, int> > reparents; ///< scheduled node reparents (tmp)
Array<real_t> reparent_scale; ///< scale associated with reparents (tmp)
Table derefinements; ///< possible derefinements, see GetDerefinementTable
/// Refine the element @a elem with the refinement type @a ref_type.
void RefineElement(int elem, char ref_type);
/// Refine one element with type and scale specified by @a ref.
void RefineElement(const Refinement & ref);
/// Derefine the element @a elem, does nothing on leaf elements.
void DerefineElement(int elem);
/// Helper function to set scale for a node with parents @a p0, @a p1.
void SetNodeScale(int p0, int p1, real_t scale);
/// Add an Element @a el to the NCMesh, optimized to reuse freed elements.
int AddElement(const Element &el)
{
if (free_element_ids.Size())
{
int idx = free_element_ids.Last();
free_element_ids.DeleteLast();
elements[idx] = el;
return idx;
}
return elements.Append(el);
}
int AddElement(Geometry::Type geom, int attr) { return AddElement(Element(geom,attr)); }
// Free the element with index @a id.
void FreeElement(int id)
{
free_element_ids.Append(id);
elements[id].ref_type = 0;
elements[id].parent = -2; // mark the element as free
}
int NewHexahedron(int n0, int n1, int n2, int n3,
int n4, int n5, int n6, int n7, int attr,
int fattr0, int fattr1, int fattr2,
int fattr3, int fattr4, int fattr5);
int NewWedge(int n0, int n1, int n2,
int n3, int n4, int n5, int attr,
int fattr0, int fattr1,
int fattr2, int fattr3, int fattr4);
int NewTetrahedron(int n0, int n1, int n2, int n3, int attr,
int fattr0, int fattr1, int fattr2, int fattr3);
int NewPyramid(int n0, int n1, int n2, int n3, int n4, int attr,
int fattr0, int fattr1, int fattr2, int fattr3,
int fattr4);
int NewQuadrilateral(int n0, int n1, int n2, int n3, int attr,
int eattr0, int eattr1, int eattr2, int eattr3);
int NewTriangle(int n0, int n1, int n2,
int attr, int eattr0, int eattr1, int eattr2);
int NewSegment(int n0, int n1, int attr, int vattr1, int vattr2);
mfem::Element* NewMeshElement(int geom) const;
/**
* @brief Given a quad face defined by four vertices, establish which edges
* of this face have been split, and if so optionally return the mid points
* of those edges.
*
* @param n1 The first node defining the face
* @param n2 The second node defining the face
* @param n3 The third node defining the face
* @param n4 The fourth node defining the face
* @param s returns the scale of the split
* @param mid optional return of the edge mid points.
* @return int 0 -- no split, 1 -- "vertical" split, 2 -- "horizontal" split
*/
int QuadFaceSplitType(int n1, int n2, int n3, int n4, real_t & s,
int mid[5] = NULL /*optional output of mid-edge nodes*/) const;
/**
* @brief Given a tri face defined by three vertices, establish whether the
* edges that make up this face have been split, and if so optionally return
* the midpoints.
* @details This is a necessary condition for this face to have been split,
* but is not sufficient. Consider a triangle attached to three refined
* triangles, in this scenario all edges can be split but this face not be
* split. In this case, it is necessary to check if there is a face made up
* of the returned midpoint nodes.
*
* @param n1 The first node defining the face
* @param n2 The second node defining the face
* @param n3 The third node defining the face
* @param mid optional return of the edge mid points.
* @return true Splits for all edges have been found
* @return false
*/
bool TriFaceSplit(int n1, int n2, int n3, int mid[3] = NULL) const;
/**
* @brief Determine if a Triangle face is a master face
* @details This check requires looking for the edges making up the triangle
* being split, if nodes exist at their midpoints, and there are vertices at
* them, this implies the face COULD be split. To determine if it is, we then
* check whether these midpoints have all been connected, this is required to
* discriminate between an internal master face surrounded by nonconformal
* refinements and a conformal boundary face surrounded by refinements.
*
* @param n1 The first node defining the face
* @param n2 The second node defining the face
* @param n3 The third node defining the face
* @return true The face is a master
* @return false The face is not a master
*/
inline bool TriFaceIsMaster(int n1, int n2, int n3) const
{
int mid[3];
return !(!TriFaceSplit(n1, n2, n3, mid) // The edges aren't split
// OR none of the midpoints are connected.
|| (nodes.FindId(mid[0], mid[1]) < 0 &&
nodes.FindId(mid[0], mid[2]) < 0 &&
nodes.FindId(mid[1], mid[2]) < 0));
}
/**
* @brief Determine if a Quad face is a master face
*
* @param n1 The first node defining the face
* @param n2 The second node defining the face
* @param n3 The third node defining the face
* @param n4 The fourth node defining the face
* @return true The quad face is a master face
* @return false The quad face is not a master face
*/
inline bool QuadFaceIsMaster(int n1, int n2, int n3, int n4) const
{
real_t s;
return QuadFaceSplitType(n1, n2, n3, n4, s) != 0;
}
void ForceRefinement(int vn1, int vn2, int vn3, int vn4);
void FindEdgeElements(int vn1, int vn2, int vn3, int vn4,
Array<MeshId> &prisms) const;
void CheckAnisoPrism(int vn1, int vn2, int vn3, int vn4,
const Refinement *refs, int nref);
void CheckAnisoFace(int vn1, int vn2, int vn3, int vn4,
int mid12, int mid34, int level = 0);
void CheckIsoFace(int vn1, int vn2, int vn3, int vn4,
int en1, int en2, int en3, int en4, int midf);
void ReparentNode(int node, int new_p1, int new_p2, real_t scale);
int FindMidEdgeNode(int node1, int node2) const;
int GetMidEdgeNode(int node1, int node2);
int GetMidFaceNode(int en1, int en2, int en3, int en4);
/**
* @brief Add references to all nodes, edges and faces of the element
*
* @param elem index into elements
*/
void ReferenceElement(int elem);
void UnreferenceElement(int elem, Array<int> &elemFaces);
Face* GetFace(Element &elem, int face_no);
void RegisterFaces(int elem, int *fattr = NULL);
void DeleteUnusedFaces(const Array<int> &elemFaces);
void CollectDerefinements(int elem, Array<Connection> &list);
/// Return el.node[index] correctly, even if the element is refined.
int RetrieveNode(const Element &el, int index);
/// Extended version of find_node: works if 'el' is refined.
int FindNodeExt(const Element &el, int node, bool abort = true);
// face/edge lists
static int find_node(const Element &el, int node);
static int find_element_edge(const Element &el, int vn0, int vn1,
bool abort = true);
static int find_local_face(int geom, int a, int b, int c);
struct Point;
struct PointMatrix;
int ReorderFacePointMat(int v0, int v1, int v2, int v3,
int elem, const PointMatrix &pm,
PointMatrix &reordered) const;
void TraverseQuadFace(int vn0, int vn1, int vn2, int vn3,
const PointMatrix& pm, int level, Face* eface[4],
MatrixMap &matrix_map);
struct TriFaceTraverseResults
{
bool unsplit; ///< Whether this face has no further splits.
bool ghost_neighbor; ///< Whether the face neighbor is a ghost.
};
TriFaceTraverseResults TraverseTriFace(int vn0, int vn1, int vn2,
const PointMatrix& pm, int level,
MatrixMap &matrix_map);
void TraverseTetEdge(int vn0, int vn1, const Point &p0, const Point &p1,
MatrixMap &matrix_map);
void TraverseEdge(int vn0, int vn1, real_t t0, real_t t1, int flags,
int level, MatrixMap &matrix_map);
virtual void BuildFaceList();
virtual void BuildEdgeList();
virtual void BuildVertexList();
virtual void ElementSharesFace(int elem, int local, int face) {} // ParNCMesh
virtual void ElementSharesEdge(int elem, int local, int enode) {} // ParNCMesh
virtual void ElementSharesVertex(int elem, int local, int vnode) {} // ParNCMesh
// neighbors / element_vertex table
/** Return all vertex-, edge- and face-neighbors of a set of elements. The
neighbors are returned as a list (neighbors != NULL), as a set
(neighbor_set != NULL), or both. The sizes of the set arrays must match
that of leaf_elements. The function is intended to be used for large sets
of elements and its complexity is linear in the number of leaf elements
in the mesh. */
void FindSetNeighbors(const Array<char> &elem_set,
Array<int> *neighbors, /* append */
Array<char> *neighbor_set = NULL);
/** Return all vertex-, edge- and face-neighbors of a single element. You can
limit the number of elements being checked using 'search_set'. The
complexity of the function is linear in the size of the search set.*/
void FindNeighbors(int elem,
Array<int> &neighbors, /* append */
const Array<int> *search_set = NULL);
/** Expand a set of elements by all vertex-, edge- and face-neighbors. The
output array 'expanded' will contain all items from 'elems' (provided
they are in 'search_set') plus their neighbors. The neighbor search can
be limited to the optional search set. The complexity is linear in the
sum of the sizes of 'elems' and 'search_set'. */
void NeighborExpand(const Array<int> &elems,
Array<int> &expanded,
const Array<int> *search_set = NULL);
void CollectEdgeVertices(int v0, int v1, Array<int> &indices);
void CollectTriFaceVertices(int v0, int v1, int v2, Array<int> &indices);
void CollectQuadFaceVertices(int v0, int v1, int v2, int v3,
Array<int> &indices);
void BuildElementToVertexTable();
void UpdateElementToVertexTable()
{
if (element_vertex.Size() < 0) { BuildElementToVertexTable(); }
}
int GetVertexRootCoord(int elem, RefCoord coord[3]) const;
void CollectIncidentElements(int elem, const RefCoord coord[3],
Array<int> &list) const;
/** Return elements neighboring to a local vertex of element 'elem'. Only
elements from within the same refinement tree ('cousins') are returned.
Complexity is proportional to the depth of elem's refinement tree. */
void FindVertexCousins(int elem, int local, Array<int> &cousins) const;
// coarse/fine transformations
struct Point
{
int dim;
real_t coord[3];
Point() { dim = 0; }
Point(const Point &) = default;
Point(real_t x)
{ dim = 1; coord[0] = x; }
Point(real_t x, real_t y)
{ dim = 2; coord[0] = x; coord[1] = y; }
Point(real_t x, real_t y, real_t z)
{ dim = 3; coord[0] = x; coord[1] = y; coord[2] = z; }
Point(const Point& p0, const Point& p1, real_t s = 0.5)
{
dim = p0.dim;
for (int i = 0; i < dim; i++)
{
coord[i] = ((1.0 - s) * p0.coord[i]) + (s * p1.coord[i]);
}
}
Point(const Point& p0, const Point& p1, const Point& p2, const Point& p3)
{
dim = p0.dim;
MFEM_ASSERT(p1.dim == dim && p2.dim == dim && p3.dim == dim, "");
for (int i = 0; i < dim; i++)
{
coord[i] = (p0.coord[i] + p1.coord[i] + p2.coord[i] + p3.coord[i])
* 0.25;
}
}
Point& operator=(const Point& src)
{
dim = src.dim;
for (int i = 0; i < dim; i++) { coord[i] = src.coord[i]; }
return *this;
}
};
/** @brief The PointMatrix stores the coordinates of the slave face using the
master face coordinate as reference.
In 2D, the point matrix has the orientation of the parent edge, so its
columns need to be flipped when applying it, see
ApplyLocalSlaveTransformation.
In 3D, the orientation part of Elem2Inf is encoded in the point matrix.
The following transformation gives the relation between the reference
quad face coordinates (xi, eta) in [0,1]^2, and the fine quad face
coordinates (x, y):
x = a0*(1-xi)*(1-eta) + a1*xi*(1-eta) + a2*xi*eta + a3*(1-xi)*eta
y = b0*(1-xi)*(1-eta) + b1*xi*(1-eta) + b2*xi*eta + b3*(1-xi)*eta
*/
struct PointMatrix
{
int np;
Point points[MaxElemNodes];
PointMatrix() : np(0) {}
PointMatrix(const Point& p0, const Point& p1)
{ np = 2; points[0] = p0; points[1] = p1; }
PointMatrix(const Point& p0, const Point& p1, const Point& p2)
{ np = 3; points[0] = p0; points[1] = p1; points[2] = p2; }
PointMatrix(const Point& p0, const Point& p1, const Point& p2, const Point& p3)
{ np = 4; points[0] = p0; points[1] = p1; points[2] = p2; points[3] = p3; }
PointMatrix(const Point& p0, const Point& p1, const Point& p2,
const Point& p3, const Point& p4)
{
np = 5;
points[0] = p0; points[1] = p1; points[2] = p2;
points[3] = p3; points[4] = p4;
}
PointMatrix(const Point& p0, const Point& p1, const Point& p2,
const Point& p3, const Point& p4, const Point& p5)
{
np = 6;
points[0] = p0; points[1] = p1; points[2] = p2;
points[3] = p3; points[4] = p4; points[5] = p5;
}
PointMatrix(const Point& p0, const Point& p1, const Point& p2,
const Point& p3, const Point& p4, const Point& p5,
const Point& p6, const Point& p7)
{
np = 8;
points[0] = p0; points[1] = p1; points[2] = p2; points[3] = p3;
points[4] = p4; points[5] = p5; points[6] = p6; points[7] = p7;
}
Point& operator()(int i) { return points[i]; }
const Point& operator()(int i) const { return points[i]; }
bool operator==(const PointMatrix &pm) const;
void GetMatrix(DenseMatrix& point_matrix) const;
};
static PointMatrix pm_seg_identity;
static PointMatrix pm_tri_identity;
static PointMatrix pm_quad_identity;
static PointMatrix pm_tet_identity;
static PointMatrix pm_prism_identity;
static PointMatrix pm_pyramid_identity;
static PointMatrix pm_hex_identity;
static const PointMatrix& GetGeomIdentity(Geometry::Type geom);
void GetPointMatrix(Geometry::Type geom, const char* ref_path,
DenseMatrix& matrix) const;
using RefPathMap = std::map<std::string, int>;
void TraverseRefinements(int elem, int coarse_index,
std::string &ref_path, RefPathMap &map) const;
/// storage for data returned by Get[De]RefinementTransforms()
mutable CoarseFineTransformations transforms;
/// state of leaf_elements before Refine(), set by MarkCoarseLevel()
Array<int> coarse_elements;
void InitDerefTransforms();
void SetDerefMatrixCodes(int parent, Array<int> &fine_coarse);
// vertex temporary data, used by GetMeshComponents
struct TmpVertex
{
bool valid, visited;
real_t pos[3];
TmpVertex() : valid(false), visited(false) {}
};
mutable TmpVertex* tmp_vertex;
const real_t *CalcVertexPos(int node) const;
// utility
int GetEdgeMaster(int node) const;
/// Return directed scale in (0,1).
inline real_t GetScale(real_t s, bool reverse) const
{ return reverse ? 1.0 - s : s; }
/**
* @brief Return the number of splits of this edge that have occurred in the
* NCMesh. If zero, this means the segment is not the master of any other
* segments.
*
* @param vn1 The first vertex making up the segment
* @param vn2 The second vertex making up the segment
* @return int The depth of splits of this segment that are present in the
* mesh.
*/
int EdgeSplitLevel(int vn1, int vn2) const;
/**
* @brief Return the number of splits of this triangle that have occurred in
* the NCMesh. If zero, this means the triangle is neither split, nor the
* master of a split face.
*
* @param vn1 The first vertex making up the triangle
* @param vn2 The second vertex making up the triangle
* @param vn3 The third vertex making up the triangle
* @return int The depth of splits of this triangle that are present in the
* mesh.
*/
int TriFaceSplitLevel(int vn1, int vn2, int vn3) const;
/**
* @brief Computes the number of horizontal and vertical splits of this quad
* that have occurred in the NCMesh. If zero, this means the quad is not the
* master of any other quad.
*
* @param vn1 The first vertex making up the quad
* @param vn2 The second vertex making up the quad
* @param vn3 The third vertex making up the quad
* @param vn4 The fourth vertex making up the quad
* @param h_level The number of "horizontal" splits of the quad
* @param v_level The number of "vertical" splits of the quad
*/
void QuadFaceSplitLevel(int vn1, int vn2, int vn3, int vn4,
int& h_level, int& v_level) const;
/**
* @brief Returns the total number of splits of this quad that have occurred
* in the NCMesh. If zero, this means the quad is not the master of any other
* quad.
* @details This is a convenience wrapper that sums the horizontal and
* vertical levels from the full method.
*
* @param vn1 The first vertex making up the quad
* @param vn2 The second vertex making up the quad
* @param vn3 The third vertex making up the quad
* @param vn4 The fourth vertex making up the quad
* @return int The depth of splits of this triangle that are present in the
* mesh. NB: An isotropic refinement has a level of 2, one horizontal split,
* followed by a vertical split.
*/
int QuadFaceSplitLevel(int vn1, int vn2, int vn3, int vn4) const;
void CountSplits(int elem, int splits[3]) const;
void GetLimitRefinements(Array<Refinement> &refinements, int max_level);
// Checker helpers
static void CheckSupportedGeom(Geometry::Type geom)
{
MFEM_VERIFY(geom == Geometry::SEGMENT ||
geom == Geometry::TRIANGLE || geom == Geometry::SQUARE ||
geom == Geometry::CUBE || geom == Geometry::PRISM ||
geom == Geometry::PYRAMID || geom == Geometry::TETRAHEDRON,
"Element type " << geom << " is not supported by NCMesh.");
}
// I/O
/// Print the "vertex_parents" section of the mesh file.
int PrintVertexParents(std::ostream *out) const;
/// Load the vertex parent hierarchy from a mesh file.
void LoadVertexParents(std::istream &input);
/// Load VertexToKnotSpan data for the NC patch topology mesh of a 2D or 3D
/// MFEM NURBS NC-patch mesh.
void LoadVertexToKnotSpan(std::istream &input);
void LoadVertexToKnotSpan2D(std::istream &input);
void LoadVertexToKnotSpan3D(std::istream &input);
/** Print the "boundary" section of the mesh file. If out == NULL, only
return the number of boundary elements. */
int PrintBoundary(std::ostream *out) const;
/// Load the "boundary" section of the mesh file.
void LoadBoundary(std::istream &input);
/// Print the "coordinates" section of the mesh file.
void PrintCoordinates(std::ostream &out) const;
/// Load the "coordinates" section of the mesh file.
void LoadCoordinates(std::istream &input);
/// Count root elements and initialize root_state.
void InitRootElements();
/// Return the index of the last top-level node plus one.
int CountTopLevelNodes() const;
/// Return true if all root_states are zero.
bool ZeroRootStates() const;
/// Load the element refinement hierarchy from a legacy mesh file.
void LoadCoarseElements(std::istream &input);
void CopyElements(int elem, const BlockArray<Element> &tmp_elements);
/// Load the deprecated MFEM mesh v1.1 format for backward compatibility.
void LoadLegacyFormat(std::istream &input, int &curved, int &is_nc);
// geometry
/// This holds in one place the constants about the geometries we support
struct GeomInfo
{
int nv, ne, nf; // number of: vertices, edges, faces
int edges[MaxElemEdges][2]; // edge vertices (up to 12 edges)
int faces[MaxElemFaces][4]; // face vertices (up to 6 faces)
int nfv[MaxElemFaces]; // number of face vertices
bool initialized;
GeomInfo() : initialized(false) {}
GeomInfo(Geometry::Type geom) : GeomInfo() { InitGeom(geom); }
void InitGeom(Geometry::Type geom);
};
static GeomInfo GI[Geometry::NumGeom];
/// This is used for a NURBS mesh with this NCMesh as its patch topology.
VertexToKnotSpan vertex_to_knotspan;
#ifdef MFEM_DEBUG
public:
void DebugLeafOrder(std::ostream &out) const;
void DebugDump(std::ostream &out) const;
#endif
friend class ParNCMesh; // for ParNCMesh::ElementSet
friend struct MatrixMap;
friend struct PointMatrixHash;
friend class NCSubMesh; // for faces, nodes
friend class ParNCSubMesh; // for faces, nodes
};
}
#endif