boundaryconditions.py 2.84 KB
Newer Older
1
from pystencils import Assignment
2
from pystencils.boundaries.boundaryhandling import BoundaryOffsetInfo
Martin Bauer's avatar
Martin Bauer committed
3
from typing import List, Tuple, Any
4
5


6
class Boundary:
7
8
9
10
11
    """Base class all boundaries should derive from"""

    def __init__(self, name=None):
        self._name = name

Martin Bauer's avatar
Martin Bauer committed
12
13
14
15
16
17
18
19
20
    def __call__(self, field, direction_symbol, index_field) -> List[Assignment]:
        """Defines the boundary behavior and must therefore be implemented by all boundaries.

        Here the boundary is defined as a list of sympy assignments, from which a boundary kernel is generated.

        Args:
            field: pystencils field where boundary condition should be applied.
                   The current cell is cell next to the boundary, which is influenced by the boundary
                   cell i.e. has a link from the boundary cell to itself.
Martin Bauer's avatar
Martin Bauer committed
21
            direction_symbol: a sympy symbol that can be used as index to the pdf_field. It describes
Martin Bauer's avatar
Martin Bauer committed
22
23
                              the direction pointing from the fluid to the boundary cell
            index_field: the boundary index field that can be used to retrieve and update boundary data
24
25
26
27
        """
        raise NotImplementedError("Boundary class has to overwrite __call__")

    @property
Martin Bauer's avatar
Martin Bauer committed
28
    def additional_data(self) -> Tuple[str, Any]:
29
        """Return a list of (name, type) tuples for additional data items required in this boundary
Martin Bauer's avatar
Martin Bauer committed
30
31
        These data items can either be initialized in separate kernel see additional_data_kernel_init or by
        Python callbacks - see additional_data_callback """
32
        return ()
33
34

    @property
Martin Bauer's avatar
Martin Bauer committed
35
    def additional_data_init_callback(self):
36
37
38
39
40
41
42
43
44
45
46
47
        """Return a callback function called with a boundary data setter object and returning a dict of
        data-name to data for each element that should be initialized"""
        return None

    @property
    def name(self):
        if self._name:
            return self._name
        else:
            return type(self).__name__

    @name.setter
Martin Bauer's avatar
Martin Bauer committed
48
49
    def name(self, new_value):
        self._name = new_value
50
51
52


class Neumann(Boundary):
Martin Bauer's avatar
Martin Bauer committed
53
    def __call__(self, field, direction_symbol, **kwargs):
54

Martin Bauer's avatar
Martin Bauer committed
55
56
        neighbor = BoundaryOffsetInfo.offset_from_dir(direction_symbol, field.spatial_dimensions)
        if field.index_dimensions == 0:
57
            return [Assignment(field[neighbor], field.center)]
58
59
        else:
            from itertools import product
Martin Bauer's avatar
Martin Bauer committed
60
            if not field.has_fixed_index_shape:
61
                raise NotImplementedError("Neumann boundary works only for fields with fixed index shape")
Martin Bauer's avatar
Martin Bauer committed
62
63
            index_iter = product(*(range(i) for i in field.index_shape))
            return [Assignment(field[neighbor](*idx), field(*idx)) for idx in index_iter]
64
65
66
67
68
69
70

    def __hash__(self):
        # All boundaries of these class behave equal -> should also be equal
        return hash("Neumann")

    def __eq__(self, other):
        return type(other) == Neumann