Skip to content
Snippets Groups Projects
slicing.py 9.36 KiB
Newer Older
Martin Bauer's avatar
Martin Bauer committed
import sympy as sp
Martin Bauer's avatar
Martin Bauer committed
from pystencils.field import create_numpy_array_with_layout, get_layout_of_array
Martin Bauer's avatar
Martin Bauer committed

Martin Bauer's avatar
Martin Bauer committed
class SliceMaker(object):
    def __getitem__(self, item):
        return item
make_slice = SliceMaker()
class SlicedGetter(object):
Martin Bauer's avatar
Martin Bauer committed
    def __init__(self, function_returning_array):
        self._functionReturningArray = function_returning_array

    def __getitem__(self, item):
        return self._functionReturningArray(item)

class SlicedGetterDataHandling:
    def __init__(self, data_handling, name):
        self.dh = data_handling
        self.name = name

    def __getitem__(self, slice_obj):
        if slice_obj is None:
            slice_obj = make_slice[:, :] if self.data_handling.dim == 2 else make_slice[:, :, 0.5]
        return self.dh.gather_array(self.name, slice_obj).squeeze()

Martin Bauer's avatar
Martin Bauer committed
def normalize_slice(slices, sizes):
Martin Bauer's avatar
Martin Bauer committed
    """Converts slices with floating point and/or negative entries to integer slices"""

    if len(slices) != len(sizes):
        raise ValueError("Slice dimension does not match sizes")

    result = []

    for s, size in zip(slices, sizes):
        if type(s) is int:
Martin Bauer's avatar
Martin Bauer committed
            result.append(s)
            continue
        if type(s) is float:
            result.append(int(s * size))
            continue

        assert (type(s) is slice)

        if s.start is None:
Martin Bauer's avatar
Martin Bauer committed
            new_start = 0
Martin Bauer's avatar
Martin Bauer committed
        elif type(s.start) is float:
Martin Bauer's avatar
Martin Bauer committed
            new_start = int(s.start * size)
        elif not isinstance(s.start, sp.Basic) and s.start < 0:
Martin Bauer's avatar
Martin Bauer committed
            new_start = size + s.start
Martin Bauer's avatar
Martin Bauer committed
        else:
Martin Bauer's avatar
Martin Bauer committed
            new_start = s.start
Martin Bauer's avatar
Martin Bauer committed

        if s.stop is None:
Martin Bauer's avatar
Martin Bauer committed
            new_stop = size
Martin Bauer's avatar
Martin Bauer committed
        elif type(s.stop) is float:
Martin Bauer's avatar
Martin Bauer committed
            new_stop = int(s.stop * size)
Martin Bauer's avatar
Martin Bauer committed
        elif not isinstance(s.stop, sp.Basic) and s.stop < 0:
Martin Bauer's avatar
Martin Bauer committed
            new_stop = size + s.stop
Martin Bauer's avatar
Martin Bauer committed
        else:
Martin Bauer's avatar
Martin Bauer committed
            new_stop = s.stop
Martin Bauer's avatar
Martin Bauer committed

Martin Bauer's avatar
Martin Bauer committed
        result.append(slice(new_start, new_stop, s.step if s.step is not None else 1))
Martin Bauer's avatar
Martin Bauer committed

    return tuple(result)
Martin Bauer's avatar
Martin Bauer committed
def shift_slice(slices, offset):
    def shift_slice_component(slice_comp, shift_offset):
        if slice_comp is None:
Martin Bauer's avatar
Martin Bauer committed
        elif isinstance(slice_comp, int):
            return slice_comp + shift_offset
        elif isinstance(slice_comp, float):
            return slice_comp  # relative entries are not shifted
        elif isinstance(slice_comp, slice):
            return slice(shift_slice_component(slice_comp.start, shift_offset),
                         shift_slice_component(slice_comp.stop, shift_offset),
                         slice_comp.step)
        else:
            raise ValueError()

    if hasattr(offset, '__len__'):
Martin Bauer's avatar
Martin Bauer committed
        return [shift_slice_component(k, off) for k, off in zip(slices, offset)]
Martin Bauer's avatar
Martin Bauer committed
        return [shift_slice_component(k, offset) for k in slices]
Martin Bauer's avatar
Martin Bauer committed
def slice_from_direction(direction_name, dim, normal_offset=0, tangential_offset=0):
    """
    Create a slice from a direction named by compass scheme:
        i.e. 'N' for north returns same as make_slice[:, -1]
        the naming is:
            - x: W, E (west, east)
            - y: S, N (south, north)
            - z: B, T (bottom, top)
    Also combinations are allowed like north-east 'NE'

Martin Bauer's avatar
Martin Bauer committed
    :param direction_name: name of direction as explained above
    :param dim: dimension of the returned slice (should be 2 or 3)
Martin Bauer's avatar
Martin Bauer committed
    :param normal_offset: the offset in 'normal' direction: e.g. slice_from_direction('N',2, normal_offset=2)
                         would return make_slice[:, -3]
Martin Bauer's avatar
Martin Bauer committed
    :param tangential_offset: offset in the other directions: e.g. slice_from_direction('N',2, tangential_offset=2)
                         would return make_slice[2:-2, -1]
Martin Bauer's avatar
Martin Bauer committed
    if tangential_offset == 0:
        result = [slice(None, None, None)] * dim
    else:
Martin Bauer's avatar
Martin Bauer committed
        result = [slice(tangential_offset, -tangential_offset, None)] * dim
Martin Bauer's avatar
Martin Bauer committed
    normal_slice_high, normal_slice_low = -1 - normal_offset, normal_offset
Martin Bauer's avatar
Martin Bauer committed
    for dim_idx, (low_name, high_name) in enumerate([('W', 'E'), ('S', 'N'), ('B', 'T')]):
        if low_name in direction_name:
            assert high_name not in direction_name, "Invalid direction name"
            result[dim_idx] = normal_slice_low
        if high_name in direction_name:
            assert low_name not in direction_name, "Invalid direction name"
            result[dim_idx] = normal_slice_high
Martin Bauer's avatar
Martin Bauer committed
def remove_ghost_layers(arr, index_dimensions=0, ghost_layers=1):
    if ghost_layers <= 0:
        return arr
    dimensions = len(arr.shape)
Martin Bauer's avatar
Martin Bauer committed
    spatial_dimensions = dimensions - index_dimensions
    indexing = [slice(ghost_layers, -ghost_layers, None), ] * spatial_dimensions
    indexing += [slice(None, None, None)] * index_dimensions
    return arr[tuple(indexing)]
Martin Bauer's avatar
Martin Bauer committed
def add_ghost_layers(arr, index_dimensions=0, ghost_layers=1, layout=None):
    dimensions = len(arr.shape)
Martin Bauer's avatar
Martin Bauer committed
    spatial_dimensions = dimensions - index_dimensions
    new_shape = [e + 2 * ghost_layers for e in arr.shape[:spatial_dimensions]] + list(arr.shape[spatial_dimensions:])
Martin Bauer's avatar
Martin Bauer committed
        layout = get_layout_of_array(arr)
    result = create_numpy_array_with_layout(new_shape, layout)
Martin Bauer's avatar
Martin Bauer committed
    indexing = [slice(ghost_layers, -ghost_layers, None), ] * spatial_dimensions
    indexing += [slice(None, None, None)] * index_dimensions
    result[tuple(indexing)] = arr
    return result
Martin Bauer's avatar
Martin Bauer committed
def get_slice_before_ghost_layer(direction, ghost_layers=1, thickness=None, full_slice=False):
Martin Bauer's avatar
Martin Bauer committed
    """
    Returns slicing expression for region before ghost layer
    :param direction: tuple specifying direction of slice
Martin Bauer's avatar
Martin Bauer committed
    :param ghost_layers: number of ghost layers
Martin Bauer's avatar
Martin Bauer committed
    :param thickness: thickness of the slice, defaults to number of ghost layers
Martin Bauer's avatar
Martin Bauer committed
    :param full_slice:  if true also the ghost cells in directions orthogonal to direction are contained in the
Martin Bauer's avatar
Martin Bauer committed
                       returned slice. Example (d=W ): if full_slice then also the ghost layer in N-S and T-B
Martin Bauer's avatar
Martin Bauer committed
                       are included, otherwise only inner cells are returned
    """
    if not thickness:
Martin Bauer's avatar
Martin Bauer committed
        thickness = ghost_layers
    full_slice_inc = ghost_layers if not full_slice else 0
Martin Bauer's avatar
Martin Bauer committed
    slices = []
Martin Bauer's avatar
Martin Bauer committed
    for dir_component in direction:
        if dir_component == -1:
Martin Bauer's avatar
Martin Bauer committed
            s = slice(ghost_layers, thickness + ghost_layers)
Martin Bauer's avatar
Martin Bauer committed
        elif dir_component == 0:
Martin Bauer's avatar
Martin Bauer committed
            end = -full_slice_inc
            s = slice(full_slice_inc, end if end != 0 else None)
Martin Bauer's avatar
Martin Bauer committed
        elif dir_component == 1:
Martin Bauer's avatar
Martin Bauer committed
            start = -thickness - ghost_layers
            end = -ghost_layers
Martin Bauer's avatar
Martin Bauer committed
            s = slice(start if start != 0 else None, end if end != 0 else None)
        else:
            raise ValueError("Invalid direction: only -1, 0, 1 components are allowed")
        slices.append(s)
    return tuple(slices)


Martin Bauer's avatar
Martin Bauer committed
def get_ghost_region_slice(direction, ghost_layers=1, thickness=None, full_slice=False):
Martin Bauer's avatar
Martin Bauer committed
    """
Martin Bauer's avatar
Martin Bauer committed
    Returns slice of ghost region. For parameters see :func:`get_slice_before_ghost_layer`
Martin Bauer's avatar
Martin Bauer committed
    """
    if not thickness:
Martin Bauer's avatar
Martin Bauer committed
        thickness = ghost_layers
Martin Bauer's avatar
Martin Bauer committed
    assert thickness > 0
Martin Bauer's avatar
Martin Bauer committed
    assert thickness <= ghost_layers
    full_slice_inc = ghost_layers if not full_slice else 0
Martin Bauer's avatar
Martin Bauer committed
    slices = []
Martin Bauer's avatar
Martin Bauer committed
    for dir_component in direction:
        if dir_component == -1:
Martin Bauer's avatar
Martin Bauer committed
            s = slice(ghost_layers - thickness, ghost_layers)
Martin Bauer's avatar
Martin Bauer committed
        elif dir_component == 0:
Martin Bauer's avatar
Martin Bauer committed
            end = -full_slice_inc
            s = slice(full_slice_inc, end if end != 0 else None)
Martin Bauer's avatar
Martin Bauer committed
        elif dir_component == 1:
Martin Bauer's avatar
Martin Bauer committed
            start = -ghost_layers
            end = - ghost_layers + thickness
Martin Bauer's avatar
Martin Bauer committed
            s = slice(start if start != 0 else None, end if end != 0 else None)
        else:
            raise ValueError("Invalid direction: only -1, 0, 1 components are allowed")
        slices.append(s)
    return tuple(slices)


Martin Bauer's avatar
Martin Bauer committed
def get_periodic_boundary_src_dst_slices(stencil, ghost_layers=1, thickness=None):
    src_dst_slice_tuples = []
Martin Bauer's avatar
Martin Bauer committed

    for d in stencil:
        if sum([abs(e) for e in d]) == 0:
            continue
Martin Bauer's avatar
Martin Bauer committed
        inv_dir = (-e for e in d)
        src = get_slice_before_ghost_layer(inv_dir, ghost_layers, thickness=thickness, full_slice=False)
        dst = get_ghost_region_slice(d, ghost_layers, thickness=thickness, full_slice=False)
        src_dst_slice_tuples.append((src, dst))
    return src_dst_slice_tuples
Martin Bauer's avatar
Martin Bauer committed
def get_periodic_boundary_functor(stencil, ghost_layers=1, thickness=None):
    """
    Returns a function that applies periodic boundary conditions
    :param stencil: sequence of directions e.g. ( [0,1], [0,-1] ) for y periodicity
Martin Bauer's avatar
Martin Bauer committed
    :param ghost_layers: how many ghost layers the array has
    :param thickness: how many of the ghost layers to copy, None means 'all'
    :return: function that takes a single array and applies the periodic copy operation
    """
Martin Bauer's avatar
Martin Bauer committed
    src_dst_slice_tuples = get_periodic_boundary_src_dst_slices(stencil, ghost_layers, thickness)
Martin Bauer's avatar
Martin Bauer committed
    def functor(pdfs, **_):
Martin Bauer's avatar
Martin Bauer committed
        for src_slice, dst_slice in src_dst_slice_tuples:
            pdfs[dst_slice] = pdfs[src_slice]
Martin Bauer's avatar
Martin Bauer committed
def slice_intersection(slice1, slice2):
    slice1 = [s if not isinstance(s, int) else slice(s, s + 1, None) for s in slice1]
    slice2 = [s if not isinstance(s, int) else slice(s, s + 1, None) for s in slice2]

Martin Bauer's avatar
Martin Bauer committed
    new_min = [max(s1.start, s2.start) for s1, s2 in zip(slice1, slice2)]
Martin Bauer's avatar
Martin Bauer committed
    new_max = [min(s1.stop, s2.stop) for s1, s2 in zip(slice1, slice2)]
Martin Bauer's avatar
Martin Bauer committed
    if any(max_p - min_p < 0 for min_p, max_p in zip(new_min, new_max)):
Martin Bauer's avatar
Martin Bauer committed
    return [slice(min_p, max_p, None) for min_p, max_p in zip(new_min, new_max)]