The Mesh Topology¶
The MeshTopology class provides topological information about the mesh, i.e.
it gives access to its entities such as edges, faces or cells and neighborly
relation, e.g. the neighboring cells to each edge.
All the information is stored using incidence matrices. So, for every pair \((d,d')\) of topological dimensions \(0 \leq d,d' \leq D\) where \(D \in \{ 1,2,3 \}\) is the spatial dimension of the mesh, there exists a matrix \(M_{d,d'}\) that for the \(d\)-dimensional entities stores their incident entities of topological dimension \(d'\). The entries \(m_{ij}\) are either \(1\) or \(0\) depending on whether the \(j\)-th \(d'\)-dimensional mesh entity is incident to the \(i\)-th mesh entity of topological dimension \(d\).
To explain how to get to these matrices we will consider the following simple sample mesh in 2D.
![\coordinate [label={below left:{\small $1$}}] (1) at (0,0);
\coordinate [label={below right:{\small $2$}}] (2) at (2,0);
\coordinate [label={above left:{\small $3$}}] (3) at (0,2);
\coordinate [label={above right:{\small $4$}}] (4) at (2,2);
\draw (3) -- (1) -- (2) -- (3) -- (4) -- (2);
\draw (0.6,0.6) node[circle, draw, inner sep=1pt, minimum size=0pt] {\small 1};
\draw (1.4,1.4) node[circle, draw, inner sep=1pt, minimum size=0pt] {\small 2};
\draw (1.0,-0.2) node {\tiny\underline{1}};
\draw (-0.1,1.0) node {\tiny\underline{2}};
\draw (1.2,1.0) node {\tiny\underline{3}};
\draw (2.1,1.0) node {\tiny\underline{4}};
\draw (1.0,2.2) node {\tiny\underline{5}};](../../../_images/tikz-c42dc042394446478b79ad486dfadf364b3895ed.png)
which consists of 4 vertices, 5 edges and 2 triangular cells. For this mesh the incidence relation \(D \to 0\), i.e. the vertices incident to each cell \((D=2)\), would be stored in a matrix
This matrix shows that e.g. the first cell, which corresponds to the first row in the matrix, is incident to the vertices with indices \(1,2,3\), as those are the non-zero column indices which represent the mesh vertices.
Note that this mesh is very simple and coarse. If the meshes get finer the
number of non-zero entries will become very small compared to the total number
of entries, i.e. the incidence matrices will be sparse. To make use of this fact
we will utilize the sparse matrix capabilities of the scipy
package which provides sparse matrix classes that only store the non-zero
entries, hence saving lots of memory.
Similarly to [Log09] the construction of these matrices relies on the three algorithms build, transpose and intersection, which will be explained in the following sections.
Contents
Initialize¶
A prerequisite for the algorithms to work is, that we already have the incidence matrix for the relation \(D \to 0\). This matrix is constructed using the following auxiliary intialization method.
First, we need the connectivity array that defines the mesh cells (entities of topological dimension \(D\)) row-wise via their node indices. For the sample mesh this array would be
i.e. the first cell is define by the node indices \(1,2,3\) and the second
cell is defined by the nodes \(2,3,4\). This array has to be supplied by
the user and passed as an argument when creating an instance of the MeshTopology
class.
In the end we will represent the incidence relation using a sparse matrix in coordinate format (COO) which stores its non-zero entries by their ‘cordinates’ \((i,j)\), i.e. their row and column indices. To do so, three 1-dimensional arrays are used. The first one is the data array which contains the values of the non-zero entries. In our case this will be an array of \(1\)‘s. The second array will contain the row indices and the third one the respective column indices.
To create the row index array we need to know the number of vertices that define the cells of the mesh. For a mesh of triangles this number is equal to \(nV = 3\) (in 3D for tetrahedra it would be \(nV = 4\)). Remember that we want to construct the incidence relation between the mesh cells and the mesh vertices, so if each cell is defined by \(nV\) vertices there will be that many non-zero entries in every row of the matrix. Hence, for each row index \(i\) (which corresponds to one cell in the mesh) there will be \(nV\) coordinate tuples \((i,j)\) that have this index as a first component. Therefore, the number of vertices tells us how often each cell index appears in the row array.
For our sample mesh the row index array would be:
containing each row index \(nV = 3\) times.
The columns of the incidence matrix we create correspond to the vertices of the mesh. So, each index in the column array is the column index of the non-zero entry corresponding to the respective index in the row array. As those non-zero entries represent the incident vertices for each cell the column indices are given by the connectivity array that has been passed as an argument.
Again, for our sample mesh this column index array would be:
As stated before, the remaining data array contains the values of the non-zero entries in the matrix. For us those values are all equal to \(1\), so the data array would be
and we can now create the incidence matrix for the relation \(D \to 0\) by passing the three array to the scipy class for a sparse matrix in COO format.
Build¶
The build algorithm computes the incidence relation \(d \to 0\) for a topological dimension \(0 < d < D\), i.e. it determines the incident vertices for every \(d\)-dimensional mesh entity.
Starting from the incidence relation \(D \to 0\) that was constructed using the initialization method above, we first have to determine the local vertex sets for every \(d\)-dimensional subentity of the mesh cells, i.e. we need for each subentity of topological dimension \(d\) of every mesh cell to vertex indices that define it.
For example, assume we want to build the incidence relation \(1 \to 0\), i.e. the incident vertices of every edge (topological dimension \(d = 1\)). As every triangular cell is incident to three edges and each edge is defined by two vertex indices, the local vertex set for the edges of every cell will consist of three 2-tuples of indices.
For our sample mesh the local vertex set for the edges of the first cell would be
because the first edge of the cell is defined by the vertex indices \(1\) and \(2\), the second edge is defined by the indices \(1,3\) and the third edge by the indices \(2,3\). Analogously, the local vertex for the edges of the second cell would be
Obviously, there are duplicates in the local vertex sets because edges will be incident to two neighboring triangular cells if they are not part of the boundary of the domain. So, the next step is to determine the unique vertex tuples in all the local vertex sets. For us this would result in the following array, where the tuples are now written as the rows.
This array is now a connectivity array that defines the edges of our sample mesh just like the one we had in the initialization method for the cells. That means we can use the same procedure to construct the sparse matrix for the incidence relation \(d \to 0\).
As there are five edges in the mesh and each edge is incident to two vertices the row index array for the edges will contain each index from \(1\) to \(5\) twice.
The column index array just consists of the indices given by the connectivity array flattened row-wise
and the data array is an array of \(1\)‘s with the same length as the row and column index arrays.
Transpose¶
The transpose algorithm computes the incidence relation \(d \to d'\) for \(d < d'\) from \(d' \to d\).
This is the simplest and most straight forward of the algorithms. Assuming we already have the incidence matrix \(M_{d',d}\) for the relation \(d' \to d\) we get the incidence matrix \(M_{d,d'}\) simply by transposing
For example, we already know the incident vertices to every mesh edge
If we now want to know the incident edges for every mesh vertex we just have to transpose this matrix to get
where we can see for each of the four mesh vertices the edges that are incident to them.
Intersection¶
The intersection algorithm computes the incidence relation \(d \to d'\) for \(d \geq d'\) from the two relations \(d \to d''\) and \(d'' \to d'\).
The auxiliary topological dimension \(d''\) is set to \(0\), so we assume that we already have the incidence matrices \(M_{d,0}, M_{0,d'}\) for the incidence relations \(d \to 0\) and \(0 \to d'\).
Then, the intersection is computed as the dot product of these two matrices. The resulting intersection matrix \(I_{d,d'}\) describes for every \(d\)-dimensional mesh entity (represented by the rows) how many vertices (\(d'' = 0\)-dimensional entities) it shares with each of the mesh entities of topological dimension \(d\) (represented by the columns).
Next, we have to distinguish between two cases to define when the incidence relation \(d \to d'\) holds for two entities.
The case \(d = d'\)¶
In case \(d = d'\), e.g. if we want the neighboring (incident) cells for every mesh cell (\(2 \to 2\)), we define two entities of topological dimension \(d\) incident to each other if they share a \(d - 1 = \tilde{d}\)-dimensional mesh entity. So, for two triangles (\(d = 2\)) to be incident they must share an edge (\(\tilde{d} = 1\)).
Simplicial entities of dimension \(d\), i.e. triangles for \(d = 2\) or tetrahedra for \(d = 3\), which are the only type of entities that currently are supported by Py\(\small\mathbb{SOFE}\), are defined by \(d + 1\) vertices. So, as the computed intersection matrix \(I_{d,d'}\) tells us the number of vertices shared between each of the \(d\)-dimensional entities and every entity of topological dimension \(d'\) the incidence matrix \(M_{d,d'}\) for \(d = d'\) is defined by
Suppose, we want to know the neighboring edges for every edge of our sample mesh, i.e. we are interested in the incidence relation \(1 \to 1\). The necessary incidence matrices for computing the intersection are
Then the intersection matrix is
and therefore the resulting incidence matrix would be
A special case is the relation \(0 \to 0\) for which we define that vertices are incident to themselves only. So the incidence matrix \(M_{0,0}\) would be the identity matrix.
The case \(d > d'\)¶
In case \(d > d'\) we define a entity of topological dimension \(d'\) to be incident to a \(d\)-dimensional one if all its vertices are incident to this entity of dimension \(d\).
Since the \(d'\)-dimensional mesh entities are defined by \(d' + 1\) vertices the incidence matrix \(M_{d,d'}\) is defined similarly to the previous section via the intersection matrix
Suppose, we want to know the incident edges for every cell of our sample mesh, i.e. we are interested in the incidence relation \(2 \to 1\). The necessary incidence matrices for computing the intersection are
Then the intersection matrix is
and therefore the resulting incidence matrix would be
Computing Incidence Relations¶
Now that we have the algorithms build, transpose and intersection we can construct the incidence matrix for any relation \(d \to d'\) by a combination of those algorithms as follows
Compute relation (d -> d')
--------------------------
if relation (d -> 0) does not exist
build relation (d -> 0)
if relation (d' -> 0) does not exist
build relation (d' -> 0)
if d < d'
compute relation (d' -> d)
transpose relation (d' -> d)
else
intersect relation (d -> dd)