Stencil
Stencil is a subclass of NumPy ndarrays to represent neighbourhoods in a discrete 3dimensional space. Certain functions can be assigned to stencil to add functionalities similar to kernels to them. They also provide convenience functions for defining "Moore" or Von Neumann" neighbourhoods. Ufuncs can also be used to alter stencils (Addition, subtraction, etc)]
expand(self, order='dist')
¶
will return the local address of each filled cell. This can be utilized to access the neighbours of a cell in lattice.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
order |
str |
[description]. Defaults to "dist". |
'dist' |
Returns:
Type | Description |
---|---|
numpy.ndarray |
array in shape of (n, 3) to describe the relative location of neighbours |
Source code in topogenesis/datastructures/datastructures.py
def expand(self, order: str = "dist"):
"""will return the local address of each filled cell. This can be utilized to access the neighbours of a cell in lattice.
Args:
order (str, optional): [description]. Defaults to "dist".
Returns:
numpy.ndarray: array in shape of (n, 3) to describe the relative location of neighbours
"""
# list the locations
locations = self.origin - np.argwhere(self)
# check the ordering method
# 'dist' means to sort based on the distance from origin
if order == "dist":
# calculating the distance of each neighbour
sums = np.abs(locations).sum(axis=1)
# sorting to identify the main cell
ordered = np.argsort(sums)
# āFā means to sort in column-major (Fortran- style) order
elif order == "F":
ordered = np.arange(self.size).reshape(self.shape).flatten('F')
# āCā means to sort in row-major (C-style) order
elif order == "C":
ordered = np.arange(self.size).reshape(self.shape).flatten('C')
# sort and return
return locations[ordered].astype(int)
set_index(self, index, value)
¶
Sets the value of a cell in stencil via local indexing (based on the origin cell)
Parameters:
Name | Type | Description | Default |
---|---|---|---|
index |
list, numpy.array |
local address of the desired cell |
required |
value |
int |
the desired value to be set one of {0, 1} |
required |
Exceptions:
Type | Description |
---|---|
ValueError |
if the local address is non-existence or incompatible with the shape of stencil |
Source code in topogenesis/datastructures/datastructures.py
def set_index(self, index, value: int):
"""Sets the value of a cell in stencil via local indexing (based on the origin cell)
Args:
index (list, numpy.array): local address of the desired cell
value (int): the desired value to be set one of {0, 1}
Raises:
ValueError: if the local address is non-existence or incompatible with the shape of stencil
"""
ind = np.array(index) + self.origin
if ind.size != 3:
raise ValueError(" the index needs to have three components")
self[ind[0], ind[1], ind[2]] = value
topoGenesis DataStructure
create_stencil(type_str, steps, clip=None)
¶
Creates a stencil based on predefined neighbourhoods such as "von_neumann" or "moore".
Parameters:
Name | Type | Description | Default |
---|---|---|---|
type_str |
str |
one of {"von_neumann", "moore", "boolean_marching_cube"} |
required |
steps |
int |
{"von_neumann", "moore"} neighbourhoods are defined based on how many steps far from the origin cell should be included. |
required |
clip |
int |
will clip the defined neighbourhood, for example ("von_neumann", step=1) describes the 6-neighbourhood of a cell in 3dimensional lattice, ("von_neumann", step=2, clip=1) describes the 18-neighbourhood, and ("moore", step=1) describes 26-neighbourhood, defaults to None |
None |
Exceptions:
Type | Description |
---|---|
ValueError |
if the neighbourhood type is unknown |
Returns:
Type | Description |
---|---|
topogenesis.Stencil |
the stencil of the perscribed neighbourhood |
Source code in topogenesis/datastructures/datastructures.py
def create_stencil(type_str: str, steps: int, clip: int = None):
"""Creates a stencil based on predefined neighbourhoods such as "von_neumann" or "moore".
Args:
type_str (str):
one of {"von_neumann", "moore", "boolean_marching_cube"}
steps (int):
{"von_neumann", "moore"} neighbourhoods are defined based on how many steps far from the origin cell should be included.
clip (int, optional):
will clip the defined neighbourhood, for example ("von_neumann", step=1) describes the 6-neighbourhood of a cell in 3dimensional lattice, ("von_neumann", step=2, clip=1) describes the 18-neighbourhood, and ("moore", step=1) describes 26-neighbourhood, defaults to None
Raises:
ValueError: if the neighbourhood type is unknown
Returns:
topogenesis.Stencil: the stencil of the perscribed neighbourhood
"""
# check if clip is specified. if it is not, set it to the steps
if clip == None:
clip = steps
# von neumann neighborhood
if type_str == "von_neumann":
# https://en.wikipedia.org/wiki/Von_Neumann_neighborhood
# computing all the possible shifts to apply to the array
shifts = np.array(list(itertools.product(
list(range(-clip, clip+1)), repeat=3)))
# the number of steps that the neighbour is appart from
# the cell (step=1 : 6 neighbour, step=2 : 18 neighbours,
# step=3 : 26 neighbours)
shift_steps = np.sum(np.absolute(shifts), axis=1)
# check the number of steps
chosen_shift_ind = np.argwhere(shift_steps <= steps).ravel()
# select the valid indices from shifts variable,
# transpose them to get
# separate indices in rows, add the number of
# steps to make this an index
locs = np.transpose(shifts[chosen_shift_ind]) + clip
# initialize the stencil
s = np.zeros((clip*2+1, clip*2+1, clip*2+1)).astype(int)
# fill in the stencil
s[locs[0], locs[1], locs[2]] = 1
return stencil(s,
ntype=type_str,
origin=np.array([clip, clip, clip]))
elif type_str == "moore":
# https://en.wikipedia.org/wiki/Moore_neighborhood
# computing all the possible shifts to apply to the array
shifts = np.array(list(itertools.product(
list(range(-clip, clip+1)), repeat=3)))
# the number of steps that the neighbour is appart from the origin cell
shift_steps = np.max(np.absolute(shifts), axis=1)
# check the number of steps
chosen_shift_ind = np.argwhere(shift_steps <= steps).ravel()
# select the valid indices from shifts variable,
# transpose them to get separate indices in rows,
# add the number of steps to make this an index
locs = np.transpose(shifts[chosen_shift_ind]) + clip
# initialize the stencil
s = np.zeros((clip*2+1, clip*2+1, clip*2+1)).astype(int)
# fill in the stencil
s[locs[0], locs[1], locs[2]] = 1
return stencil(s, ntype=type_str, origin=np.array([clip, clip, clip]))
elif type_str == "boolean_marching_cube":
# initialize the stencil
s = np.ones((2, 2, 2)).astype(int)
return stencil(s, ntype=type_str, origin=np.array([0, 0, 0]))
else:
raise ValueError(
'non-valid neighbourhood type for stencil creation')