Skip to content
Snippets Groups Projects
  • Michael Kuron's avatar
    d5baec81
    Python 2.7 compatibility · d5baec81
    Michael Kuron authored and Martin Bauer's avatar Martin Bauer committed
    This commit makes the Python code backwards compatible down to Python 2.7. Previously it would only run on Python 3.5 and up.
    
    Problems fixed included:
    - `time.perf_counter()` doesn't exist
    - all classes need to be new-style
    - `functools.lru_cache` doesn't exist
    - only the last argument to a function call can be `*`-expanded
    - the `nonlocal` keyword doesn't exist
    - metaclasses are used with a different syntax
    - `yield from` doesn't exist
    - `tempdir.TemporaryDirectory` doesn't exist
    - iterators need a `next()` method
    d5baec81
    History
    Python 2.7 compatibility
    Michael Kuron authored and Martin Bauer's avatar Martin Bauer committed
    This commit makes the Python code backwards compatible down to Python 2.7. Previously it would only run on Python 3.5 and up.
    
    Problems fixed included:
    - `time.perf_counter()` doesn't exist
    - all classes need to be new-style
    - `functools.lru_cache` doesn't exist
    - only the last argument to a function call can be `*`-expanded
    - the `nonlocal` keyword doesn't exist
    - metaclasses are used with a different syntax
    - `yield from` doesn't exist
    - `tempdir.TemporaryDirectory` doesn't exist
    - iterators need a `next()` method
gridvisualization.py 3.83 KiB
import matplotlib.patches as patches


class Grid(object):
    """Visualizes a 2D LBM grid with matplotlib by drawing cells and pdf arrows"""

    def __init__(self, xCells, yCells):
        """Create a new grid with the given number of cells in x (horizontal) and y (vertical) direction"""

        self._xCells = xCells
        self._yCells = yCells

        self._patches = []
        for x in range(xCells):
            for y in range(yCells):
                self._patches.append(patches.Rectangle((x, y), 1.0, 1.0, fill=False, linewidth=3, color='#bbbbbb'))

        self._cellBoundaries = dict()  # mapping cell to rectangle patch
        self._arrows = dict()  # mapping (cell, direction) tuples to arrow patches

    def addCellBoundary(self, cell, **kwargs):
        """Draws a rectangle around a single cell. Keyword arguments are passed to the matplotlib Rectangle patch"""
        if 'fill' not in kwargs: kwargs['fill'] = False
        if 'linewidth' not in kwargs: kwargs['linewidth'] = 3
        if 'color' not in kwargs: kwargs['#bbbbbb']
        self._cellBoundaries[cell] = patches.Rectangle(cell, 1.0, 1.0, **kwargs)

    def addCellBoundaries(self, **kwargs):
        """Draws a rectangle around all cells. Keyword arguments are passed to the matplotlib Rectangle patch"""
        for x in range(self._xCells):
            for y in range(self._yCells):
                self.addCellBoundary((x, y), **kwargs)

    def addArrow(self, cell, arrowPosition, arrowDirection, **kwargs):
        """
        Draws an arrow in a cell. If an arrow exists already at this position, it is replaced.

        :param cell: cell coordinate as tuple (x,y)
        :param arrowPosition: each cell has 9 possible positions specified as tuple e.g. upper left (-1, 1)
        :param arrowDirection: direction of the arrow as (x,y) tuple
        :param kwargs: arguments passed directly to the FancyArrow patch of matplotlib
        """
        cellMidpoint = (0.5 + cell[0], 0.5 + cell[1])

        if 'width' not in kwargs: kwargs['width'] = 0.005
        if 'color' not in kwargs: kwargs['color'] = 'k'

        if arrowPosition == (0, 0):
            del kwargs['width']
            self._arrows[(cell, arrowPosition)] = patches.Circle(cellMidpoint, radius=0.03, **kwargs)
        else:
            arrowMidpoint = (cellMidpoint[0] + arrowPosition[0] * 0.25,
                             cellMidpoint[1] + arrowPosition[1] * 0.25)
            length = 0.75
            arrowStart = (arrowMidpoint[0] - arrowDirection[0] * 0.25 * length,
                          arrowMidpoint[1] - arrowDirection[1] * 0.25 * length)

            patch = patches.FancyArrow(arrowStart[0], arrowStart[1],
                                       0.25 * length * arrowDirection[0],
                                       0.25 * length * arrowDirection[1],
                                       **kwargs)
            self._arrows[(cell, arrowPosition)] = patch

    def fillWithDefaultArrows(self, **kwargs):
        """Fills the complete grid with the default pdf arrows"""
        for x in range(self._xCells):
            for y in range(self._yCells):
                for dx in [-1, 0, 1]:
                    for dy in [-1, 0, 1]:
                        if 'color' not in kwargs: kwargs['color'] = '#bbbbbb'
                        if 'width' not in kwargs: kwargs['width'] = 0.006

                        self.addArrow((x, y), (dx, dy), (dx, dy), **kwargs)

    def draw(self, ax):
        """Draw the grid into a given matplotlib axes object"""

        for p in self._patches:
            ax.add_patch(p)

        for arrowPatch in self._arrows.values():
            ax.add_patch(arrowPatch)

        offset = 0.1
        ax.set_xlim(-offset, self._xCells+offset)
        ax.set_xlim(-offset, self._xCells + offset)
        ax.set_ylim(-offset, self._yCells + offset)
        ax.set_aspect('equal')
        ax.set_axis_off()