This page was generated from unit-1.8-meshtopology/meshtopology.ipynb.

1.8 Exploring the mesh topology

In this tutorial, we learn how to iterate over mesh entities and obtain information about how one mesh entity is connected to others. We will be able to answer questions like the following after this tutorial.

  • What edges are connected to a vertex?

  • What are the edges of an element?

  • What are the points of an element?

  • What elements are adjacent to a face?

In some algorithms, such as custom block smoothers (that we shall see in 2.1.2), we will need to iterate over mesh entities using the concepts in this tutorial.

[1]:
from ngsolve import *
from ngsolve.webgui import Draw
[2]:
mesh = Mesh(unit_cube.GenerateMesh(maxh=1))
Draw(mesh);

Iterating over mesh objects

Iterating over vertices:

[3]:
for v in mesh.vertices:
    print (v, v.point)
V0 (0.0, 0.0, 1.0)
V1 (0.0, 0.0, 0.0)
V2 (0.0, 1.0, 1.0)
V3 (0.0, 1.0, 0.0)
V4 (1.0, 0.0, 1.0)
V5 (1.0, 0.0, 0.0)
V6 (1.0, 1.0, 1.0)
V7 (1.0, 1.0, 0.0)
V8 (0.500000000157645, 0.49999999940361106, 0.49999999983486)

What is the type of v in mesh.vertices?

[4]:
for v in mesh.vertices:
    print(type(v))
<class 'ngsolve.comp.MeshNode'>
<class 'ngsolve.comp.MeshNode'>
<class 'ngsolve.comp.MeshNode'>
<class 'ngsolve.comp.MeshNode'>
<class 'ngsolve.comp.MeshNode'>
<class 'ngsolve.comp.MeshNode'>
<class 'ngsolve.comp.MeshNode'>
<class 'ngsolve.comp.MeshNode'>
<class 'ngsolve.comp.MeshNode'>

We’ll shortly return to this MeshNode class.

Iterating over elements:

[5]:
for el in mesh.Elements(VOL):
    print(type(el))
    print ("vertices: ", el.vertices)   # get vertices of an element
    print ("edges: ", el.edges)         # get edges of an element
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V1, V2, V3, V8)
edges:  (E9, E13, E16, E5, E6, E10)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V4, V5, V7, V8)
edges:  (E20, E22, E25, E17, E19, E21)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V0, V1, V8, V2)
edges:  (E1, E5, E13, E0, E4, E9)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V0, V1, V5, V8)
edges:  (E4, E9, E22, E0, E3, E7)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V3, V6, V7, V8)
edges:  (E16, E24, E25, E14, E15, E23)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V2, V3, V8, V6)
edges:  (E12, E14, E24, E10, E13, E16)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V0, V2, V8, V4)
edges:  (E2, E11, E20, E1, E4, E13)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V2, V4, V6, V8)
edges:  (E13, E20, E24, E11, E12, E18)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V4, V6, V8, V7)
edges:  (E19, E23, E25, E18, E20, E24)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V0, V4, V8, V5)
edges:  (E3, E17, E22, E2, E4, E20)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V1, V5, V8, V7)
edges:  (E8, E21, E25, E7, E9, E22)
<class 'ngsolve.comp.Ngs_Element'>
vertices:  (V1, V3, V7, V8)
edges:  (E9, E16, E25, E6, E8, E15)

NodeId and MeshNodes

An object of type NodeId in NGSolve is just a number together with a type of the mesh entity it describes.

[6]:
v = NodeId(VERTEX,0)   # a standalone NodeId object
type(v)
print ("type = ", v.type, "v.nr =", v.nr)
type =  NODE_TYPE.VERTEX v.nr = 0

NODE_TYPE can be one of the following:

  • VERTEX: dimension 0

  • EDGE: dimension 1

  • FACE: dimension 2

  • CELL: dimension 3

  • ELEMENT: codimension 0

  • FACET: codimension 1

E.g., in \(n\) space dimensions, facets are mesh objects of dimension \(n-1\). When \(n=2\) there are no CELL entities.

Nodes can be associated to an existing mesh. Consider the above-defined node

v = NodeId(VERTEX,0)

When it is associated to mesh, it becomes an object of type MeshNode which has coordinate information.

[7]:
meshv = mesh[v]
print ("type = ", type(meshv))
print ("point = ", meshv.point)
type =  <class 'ngsolve.comp.MeshNode'>
point =  (0.0, 0.0, 1.0)
[8]:
type(v), type(meshv)   # note the different types
[8]:
(ngsolve.comp.NodeId, ngsolve.comp.MeshNode)

MeshNode objects like meshv can be queried for topology information.

E.g., what are edges connected to the mesh vertex meshv?

[9]:
meshv.edges
[9]:
(E0, E1, E2, E3, E4)
[10]:
c = NodeId(CELL, 1)
meshc = mesh[c]
meshc.faces          # faces of a cell
[10]:
(F28, F27, F24, F23)
[11]:
f = NodeId(FACE, 2)
meshf = mesh[f]
meshf.edges, meshf.elements
[11]:
((E0, E9, E4),
 (<ngsolve.comp.ElementId at 0x7f6ce8a7e8b0>,
  <ngsolve.comp.ElementId at 0x7f6ce8a7e8f0>))

ElementId and Ngs_Element

An ElementId is made using a number together with an object like BND or VOL that knows the codimension.

[12]:
ei = ElementId(BND,0)
type(ei)
[12]:
ngsolve.comp.ElementId

As with NodeId, we may associate ElementId with a mesh to get an object of type Ngs_Element, which can be queried for topology information.

[13]:
meshel = mesh[ei]
type(meshel)
print ("type of meshel = \n ", meshel)
print ("vertices =", meshel.vertices)
type of meshel =
  <ngsolve.comp.Ngs_Element object at 0x7f6ce8a7f1f0>
vertices = (V0, V2, V1)

Note that meshel has only three vertices because it is a boundary element. Volume elements have the same type as boundary elements:

[14]:
elt0 = mesh[ElementId(VOL, 0)]
type(elt0)
[14]:
ngsolve.comp.Ngs_Element
[15]:
elt0.vertices, elt0.edges, elt0.facets
[15]:
((V1, V2, V3, V8), (E9, E13, E16, E5, E6, E10), (F16, F11, F9, F8))

You can also access the same volume element using NodeId:

[16]:
el0 = mesh[NodeId(ELEMENT, 0)]
type(el0)
[16]:
ngsolve.comp.MeshNode
[17]:
el0.vertices, el0.edges
[17]:
((V1, V2, V3, V8), (E9, E13, E16, E5, E6, E10))

Dofs

Dofs are numbers enumerating the global degrees of freedom of a finite element space. Dofs are associated to mesh entities of the previously described types.

E.g., all dofs of the Lagrange finite element space associated to edges of the mesh can be obtained as follows.

[18]:
fes = H1(mesh, order=4)
for edge in mesh.edges:
    print ("type = ", type(edge))
    print ("dofs = ", fes.GetDofNrs(edge))
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (9, 10, 11)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (12, 13, 14)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (15, 16, 17)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (18, 19, 20)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (21, 22, 23)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (24, 25, 26)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (27, 28, 29)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (30, 31, 32)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (33, 34, 35)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (36, 37, 38)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (39, 40, 41)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (42, 43, 44)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (45, 46, 47)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (48, 49, 50)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (51, 52, 53)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (54, 55, 56)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (57, 58, 59)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (60, 61, 62)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (63, 64, 65)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (66, 67, 68)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (69, 70, 71)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (72, 73, 74)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (75, 76, 77)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (78, 79, 80)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (81, 82, 83)
type =  <class 'ngsolve.comp.MeshNode'>
dofs =  (84, 85, 86)

From the output, it is evident that these edge dofs are associated to MeshNode objects.

What about dofs associated to elements?

[19]:
for el in fes.Elements(VOL):
    print(type(el))
    print (el.dofs)
<class 'ngsolve.comp.FESpaceElement'>
[1, 2, 3, 8, 36, 37, 38, 48, 49, 50, 57, 58, 59, 24, 25, 26, 27, 28, 29, 39, 40, 41, 135, 136, 137, 120, 121, 122, 114, 115, 116, 111, 112, 113, 177]
<class 'ngsolve.comp.FESpaceElement'>
[4, 5, 7, 8, 69, 70, 71, 75, 76, 77, 84, 85, 86, 60, 61, 62, 66, 67, 68, 72, 73, 74, 171, 172, 173, 168, 169, 170, 159, 160, 161, 156, 157, 158, 178]
<class 'ngsolve.comp.FESpaceElement'>
[0, 1, 8, 2, 12, 13, 14, 24, 25, 26, 48, 49, 50, 9, 10, 11, 21, 22, 23, 36, 37, 38, 114, 115, 116, 99, 100, 101, 87, 88, 89, 93, 94, 95, 179]
<class 'ngsolve.comp.FESpaceElement'>
[0, 1, 5, 8, 21, 22, 23, 36, 37, 38, 75, 76, 77, 9, 10, 11, 18, 19, 20, 30, 31, 32, 126, 127, 128, 108, 109, 110, 93, 94, 95, 90, 91, 92, 180]
<class 'ngsolve.comp.FESpaceElement'>
[3, 6, 7, 8, 57, 58, 59, 81, 82, 83, 84, 85, 86, 51, 52, 53, 54, 55, 56, 78, 79, 80, 174, 175, 176, 153, 154, 155, 150, 151, 152, 147, 148, 149, 181]
<class 'ngsolve.comp.FESpaceElement'>
[2, 3, 8, 6, 45, 46, 47, 51, 52, 53, 81, 82, 83, 39, 40, 41, 48, 49, 50, 57, 58, 59, 150, 151, 152, 144, 145, 146, 132, 133, 134, 135, 136, 137, 182]
<class 'ngsolve.comp.FESpaceElement'>
[0, 2, 8, 4, 15, 16, 17, 42, 43, 44, 69, 70, 71, 12, 13, 14, 21, 22, 23, 48, 49, 50, 141, 142, 143, 105, 106, 107, 96, 97, 98, 99, 100, 101, 183]
<class 'ngsolve.comp.FESpaceElement'>
[2, 4, 6, 8, 48, 49, 50, 69, 70, 71, 81, 82, 83, 42, 43, 44, 45, 46, 47, 63, 64, 65, 165, 166, 167, 144, 145, 146, 141, 142, 143, 138, 139, 140, 184]
<class 'ngsolve.comp.FESpaceElement'>
[4, 6, 8, 7, 66, 67, 68, 78, 79, 80, 84, 85, 86, 63, 64, 65, 69, 70, 71, 81, 82, 83, 174, 175, 176, 168, 169, 170, 162, 163, 164, 165, 166, 167, 185]
<class 'ngsolve.comp.FESpaceElement'>
[0, 4, 8, 5, 18, 19, 20, 60, 61, 62, 75, 76, 77, 15, 16, 17, 21, 22, 23, 69, 70, 71, 159, 160, 161, 108, 109, 110, 102, 103, 104, 105, 106, 107, 186]
<class 'ngsolve.comp.FESpaceElement'>
[1, 5, 8, 7, 33, 34, 35, 72, 73, 74, 84, 85, 86, 30, 31, 32, 36, 37, 38, 75, 76, 77, 171, 172, 173, 129, 130, 131, 123, 124, 125, 126, 127, 128, 187]
<class 'ngsolve.comp.FESpaceElement'>
[1, 3, 7, 8, 36, 37, 38, 57, 58, 59, 84, 85, 86, 27, 28, 29, 33, 34, 35, 54, 55, 56, 153, 154, 155, 129, 130, 131, 120, 121, 122, 117, 118, 119, 188]

The type ngsolve.comp.FESpaceElement appearing in the output is derived from Ngs_Element as can be seen from the documentation:

[20]:
from ngsolve.comp import FESpaceElement
help(FESpaceElement)
Help on class FESpaceElement in module ngsolve.comp:

class FESpaceElement(Ngs_Element)
 |  Method resolution order:
 |      FESpaceElement
 |      Ngs_Element
 |      pybind11_builtins.pybind11_object
 |      builtins.object
 |
 |  Methods defined here:
 |
 |  GetFE(...)
 |      GetFE(self: ngsolve.comp.FESpaceElement) -> ngsolve.fem.FiniteElement
 |
 |      the finite element containing shape functions
 |
 |  GetTrafo(...)
 |      GetTrafo(self: ngsolve.comp.FESpaceElement) -> ngsolve.fem.ElementTransformation
 |
 |      the transformation from reference element to physical element
 |
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |
 |  ----------------------------------------------------------------------
 |  Readonly properties defined here:
 |
 |  dofs
 |      degrees of freedom of element
 |
 |  ----------------------------------------------------------------------
 |  Methods inherited from Ngs_Element:
 |
 |  VB(...)
 |      VB(self: ngsolve.comp.Ngs_Element) -> ngsolve.comp.VorB
 |
 |      VorB of element
 |
 |  ----------------------------------------------------------------------
 |  Readonly properties inherited from Ngs_Element:
 |
 |  edges
 |      tuple of global edge numbers
 |
 |  elementnode
 |      inner node, i.e. cell, face or edge node for 3D/2D/1D
 |
 |  faces
 |      tuple of global face numbers
 |
 |  facets
 |      tuple of global face, edge or vertex numbers
 |
 |  index
 |      material or boundary condition index
 |
 |  mat
 |      material or boundary condition label
 |
 |  nr
 |      the element number
 |
 |  type
 |      geometric shape of element
 |
 |  valid
 |      is element valid
 |
 |  vertices
 |      tuple of global vertex numbers
 |
 |  ----------------------------------------------------------------------
 |  Static methods inherited from pybind11_builtins.pybind11_object:
 |
 |  __new__(*args, **kwargs) from pybind11_builtins.pybind11_type
 |      Create and return a new object.  See help(type) for accurate signature.