"""
>>> get_connectivity((6, ))
array([0, 1, 1, 2, 2, 3, 3, 4, 4, 5])
>>> get_connectivity((6, ), ordering='ccw')
array([1, 0, 2, 1, 3, 2, 4, 3, 5, 4])
>>> get_connectivity((3, 4))
array([ 0, 1, 5, 4, 1, 2, 6, 5, 2, 3, 7, 6, 4, 5, 9, 8, 5,
6, 10, 9, 6, 7, 11, 10])
>>> get_connectivity((3, 4), ordering='ccw')
array([ 1, 0, 4, 5, 2, 1, 5, 6, 3, 2, 6, 7, 5, 4, 8, 9, 6,
5, 9, 10, 7, 6, 10, 11])
>>> shape = np.array([3, 4, 4])
>>> (c, o) = get_connectivity(shape, with_offsets=True)
>>> len(c) == (shape - 1).prod() * 8
True
>>> len(o) == (shape - 1).prod()
True
>>> np.all(np.diff(o) == 8)
True
>>> c[:o[0]]
array([ 0, 1, 5, 4, 16, 17, 21, 20])
>>> c[o[-2]:o[-1]]
array([26, 27, 31, 30, 42, 43, 47, 46])
>>> ids = get_connectivity((3, 4, 4), ordering='ccw')
>>> ids[:8]
array([ 1, 0, 4, 5, 17, 16, 20, 21])
>>> ids[-8:]
array([27, 26, 30, 31, 43, 42, 46, 47])
"""
import numpy as np
def _get_interior_ids(shape, dtype=int):
"""
Get indices to the interior nodes of a structured grid. These are
essentially the upper-left corners of cells formed by the points.
For a 1D grid this is the left node of each line segment. For a
3D grid this is the upper-left corner of the closest face of each
cube.
>>> _get_interior_ids((6, ))
array([0, 1, 2, 3, 4])
>>> _get_interior_ids((3, 4))
array([0, 1, 2, 4, 5, 6])
>>> _get_interior_ids((2, 3, 4))
array([0, 1, 2, 4, 5, 6])
>>> _get_interior_ids((3, 3, 4))
array([ 0, 1, 2, 4, 5, 6, 12, 13, 14, 16, 17, 18])
"""
i = np.arange(np.prod(shape), dtype=dtype)
i.shape = shape
obj = []
for d in shape:
obj.append(slice(0, d - 1))
return i[tuple(obj)].flatten()
def _get_offsets(shape, ordering="cw", dtype=int):
"""
>>> _get_offsets((16, ))
array([0, 1])
>>> _get_offsets((3, 4))
array([0, 1, 5, 4])
>>> _get_offsets((3, 4, 4))
array([ 0, 1, 5, 4, 16, 17, 21, 20])
>>> _get_offsets((16, ), ordering='ccw')
array([1, 0])
>>> _get_offsets((3, 4), ordering='ccw')
array([1, 0, 4, 5])
>>> _get_offsets((3, 4, 4), ordering='ccw')
array([ 1, 0, 4, 5, 17, 16, 20, 21])
"""
if ordering not in ["cw", "ccw", "none"]:
raise TypeError(
"Ordering value not understood (known values are 'cw', 'ccw' and 'none')"
)
if ordering == "none":
ordered = False
else:
ordered = True
if ordering == "ccw":
offsets = np.array([1, 0], dtype=dtype)
else:
offsets = np.array([0, 1], dtype=dtype)
strides = np.cumprod(list(shape[-1:0:-1]))
for dim, stride in enumerate(strides):
new_offsets = offsets + stride
if ordered and dim % 2 == 0:
offsets = np.append(offsets, new_offsets[::-1])
else:
offsets = np.append(offsets, new_offsets)
return offsets
[docs]def get_connectivity(shape, **kwds):
"""
Get the connectivity (and, optionally, offset) array for an ND structured
grid. Elements will consist of two points for 1D grids, four points for
2D grids, eight points for 3D grids, etc. For a uniform rectilinear grid
this would be lines, squares, and cubes.
The nodes of an element can be ordered either clockwise or counter-clockwise.
Parameters
----------
shape: tuple of int
The shape of the grid.
ordering: {'cw', 'ccw', 'none'}, optional
Node ordering. One of 'cw' (clockwise), 'ccw' (counter-clockwise),
or 'none' (ordering not guaranteed).
dtype: str or numpy.dtype
The desired data type of the returned arrays.
with_offsets: bool
Return offset array along with connectivity
Returns
-------
ndarray of int
Array of connectivities. If with_offsets keyword is True, return a
tuple of (connectivity, offset).
Examples
--------
A 1D grid with three points has 2 elements.
>>> get_connectivity ((3, ))
array([0, 1, 1, 2])
>>> get_connectivity ((3, ), ordering='ccw')
array([1, 0, 2, 1])
A 2D grid with 3 rows and 4 columns of nodes::
( 0 ) --- ( 1 ) --- ( 2 ) --- ( 3 )
| | | |
| 0 | 1 | 2 |
| | | |
( 4 ) --- ( 5 ) --- ( 6 ) --- ( 7 )
| | | |
| 3 | 4 | 5 |
| | | |
( 8 ) --- ( 9 ) --- ( 10) --- ( 11)
>>> get_connectivity((3, 4))
array([ 0, 1, 5, 4, 1, 2, 6, 5, 2, 3, 7, 6, 4, 5, 9, 8, 5,
6, 10, 9, 6, 7, 11, 10])
>>> get_connectivity((3, 4), ordering='ccw')
array([ 1, 0, 4, 5, 2, 1, 5, 6, 3, 2, 6, 7, 5, 4, 8, 9, 6,
5, 9, 10, 7, 6, 10, 11])
If ordering doesn't matter, set ordering to 'none' as this could be slightly faster.
>>> (ids, offsets) = get_connectivity((3, 4), ordering='none', with_offsets=True)
>>> offsets
array([ 4, 8, 12, 16, 20, 24])
>>> ids
array([ 0, 1, 4, 5, 1, 2, 5, 6, 2, 3, 6, 7, 4, 5, 8, 9, 5,
6, 9, 10, 6, 7, 10, 11])
Nodes connected to the first cell,
>>> ids[:offsets[0]]
array([0, 1, 4, 5])
Nodes connected to the second cell,
>>> ids[offsets[0]:offsets[1]]
array([1, 2, 5, 6])
Instead of using an offset array to indicate the end of each cell,
you can return a list of connectivity arrays for each cell.
>>> shape = np.array((3, 4))
>>> ids = get_connectivity(shape, ordering='cw', as_cell_list=True)
>>> len(ids) == (shape - 1).prod()
True
>>> ids # doctest: +NORMALIZE_WHITESPACE
[array([0, 1, 5, 4]),
array([1, 2, 6, 5]),
array([2, 3, 7, 6]),
array([4, 5, 9, 8]),
array([ 5, 6, 10, 9]),
array([ 6, 7, 11, 10])]
"""
kwds.setdefault("dtype", int)
kwds.setdefault("ordering", "cw")
with_offsets = kwds.pop("with_offsets", False)
as_cell_list = kwds.pop("as_cell_list", False)
if with_offsets and as_cell_list:
raise ValueError("with_offsets and as_cell_list keywords cannot both be True")
c0 = _get_interior_ids(shape, dtype=kwds["dtype"])
offsets = _get_offsets(shape, **kwds)
points_per_cell = len(offsets)
if as_cell_list:
offset_list = []
for offset in offsets:
offset_list.append(c0 + offset)
c = []
for cell in zip(*offset_list):
c.append(np.array(cell, dtype=kwds["dtype"]))
else:
c = np.empty((c0.size * points_per_cell,), dtype=kwds["dtype"])
for i, offset in enumerate(offsets):
c[i::points_per_cell] = c0 + offset
if with_offsets:
o = np.arange(points_per_cell, len(c) + 1, points_per_cell, dtype=kwds["dtype"])
return (c, o)
else:
return c
[docs]def get_connectivity_2d(shape, ordering="cw", dtype=int):
"""This is a little slower than the above and less general.
Examples
--------
>>> get_connectivity_2d((3, 4))
array([ 0, 1, 5, 4, 1, 2, 6, 5, 2, 3, 7, 6, 4, 5, 9, 8, 5,
6, 10, 9, 6, 7, 11, 10])
"""
if len(shape) != 2:
raise ValueError("number of dimensions must be 2")
point_count = shape[0] * shape[1]
point_ids = np.arange(point_count, dtype=dtype)
point_ids.shape = shape
c_ul = point_ids[:-1, :-1].flatten()
c_ur = point_ids[:-1, 1:].flatten()
c_lr = point_ids[1:, 1:].flatten()
c_ll = point_ids[1:, :-1].flatten()
c = np.empty((4 * c_ul.size,), dtype=c_ul.dtype)
if ordering == "cw":
c[::4] = c_ul
c[1::4] = c_ur
c[2::4] = c_lr
c[3::4] = c_ll
else:
c[::4] = c_ur
c[1::4] = c_ul
c[2::4] = c_ll
c[3::4] = c_lr
return c
[docs]def get_connectivity_1d(shape, ordering="cw", dtype=int):
if len(shape) != 1:
raise ValueError("number of dimensions must be 1")
point_count = shape[0]
point_ids = np.arange(point_count, dtype=dtype)
c_left = point_ids[:-1]
c_right = point_ids[1:]
c = np.empty((4 * c_left.size,), dtype=c_left.dtype)
if ordering == "cw":
c[::2] = c_left
c[1::2] = c_right
else:
c[::2] = c_right
c[1::2] = c_left
return c
if __name__ == "__main__":
import doctest
doctest.testmod(optionflags=doctest.NORMALIZE_WHITESPACE)