Topology vs Geometry

Rather than having a single concept of what is typically referred to as the 'mesh', Nutils maintains a strict separation of topology and geometry. The nutils.topology.Topology represents a collection of elements and inter-element connectivity, along with recipes for creating bases. It has no (public) notion of position. The geometry takes the nutils.topology.Topology and positions it in space. This separation makes it possible to define multiple geometries belonging to a single nutils.topology.Topology, a feature that is useful for example in certain Lagrangian formulations.

While not having mesh objects, Nutils does have a nutils.mesh module, which hosts functions that return tuples of topology and geometry. Nutils provides two builtin mesh generators: nutils.mesh.rectilinear, a generator for structured topologies (i.e. tensor products of one or more one-dimensional topologies), and nutils.mesh.unitsquare, a unit square mesh generator with square or triangular elements or a mixture of both. The latter is mostly useful for testing. In addition to generators, Nutils also provides the nutils.mesh.gmsh importer for gmsh-generated meshes.

The structured mesh generator takes as its first argument a list of element vertices per dimension. A one-dimensional topology with four elements of equal size between 0 and 1 is generated by

mesh.rectilinear([[0, 0.25, 0.5, 0.75, 1.0]])
# (StructuredTopology<4>, Array<1>)

Alternatively we could have used numpy.linspace to generate a sequence of equidistant vertices, and unpack the resulting tuple:

topo, geom = mesh.rectilinear([numpy.linspace(0, 1, 5)])

We will use this topology and geometry throughout the remainder of this tutorial.

Note that the argument is a list of length one: this outer sequence lists the dimensions, the inner the vertices per dimension. To generate a two-dimensional topology, simply add a second list of vertices to the outer list. For example, an equidistant topology with four by eight elements with a unit square geometry is generated by

mesh.rectilinear([numpy.linspace(0, 1, 5), numpy.linspace(0, 1, 9)])
# (StructuredTopology<4x8>, Array<2>)

Any topology defines a boundary via the nutils.topology.Topology.boundary attribute. Optionally, a topology can offer subtopologies via the getitem operator. The rectilinear mesh generator automatically defines 'left' and 'right' boundary groups for the first dimension, making the left boundary accessible as:

topo.boundary['left']
# StructuredTopology<>

Optionally, a topology can be made periodic in one or more dimensions by passing a list of dimension indices to be periodic via the keyword argument periodic. For example, to make the second dimension of the above two-dimensional mesh periodic, add periodic=[1]:

mesh.rectilinear([numpy.linspace(0, 1, 5), numpy.linspace(0, 1, 9)], periodic=[1])
# (StructuredTopology<4x8p>, Array<2>)

Note that in this case the boundary topology, though still available, is empty.