Skip to content

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')