PyMPDATA.vector_field

vector field abstractions for the staggered grid

  1"""
  2vector field abstractions for the staggered grid
  3"""
  4
  5import inspect
  6
  7import numpy as np
  8
  9from PyMPDATA.boundary_conditions.constant import Constant
 10from PyMPDATA.impl.enumerations import (
 11    INNER,
 12    INVALID_HALO_VALUE,
 13    INVALID_INIT_VALUE,
 14    INVALID_NULL_VALUE,
 15    MID3D,
 16    OUTER,
 17)
 18from PyMPDATA.impl.field import Field
 19from PyMPDATA.scalar_field import ScalarField
 20
 21
 22class VectorField(Field):
 23    """n-component n-dimensional vector field including halo data,
 24    used to represent the advector field"""
 25
 26    def __init__(self, data: tuple, halo: int, boundary_conditions: tuple):
 27        super().__init__(
 28            grid=tuple(datum.shape[dim] - 1 for dim, datum in enumerate(data)),
 29            boundary_conditions=boundary_conditions,
 30            halo=halo,
 31            dtype=data[0].dtype,
 32        )
 33
 34        for comp, field in enumerate(data):
 35            assert len(field.shape) == len(data)
 36            for dim, dim_length in enumerate(field.shape):
 37                assert halo <= dim_length
 38                if not (np.asarray(self.grid) == 0).all():
 39                    assert dim_length == self.grid[dim] + (dim == comp)
 40        for boundary_condition in boundary_conditions:
 41            assert not inspect.isclass(boundary_condition)
 42
 43        dims = range(self.n_dims)
 44        halos = tuple(tuple((halo - (dim == comp)) for comp in dims) for dim in dims)
 45        shape_with_halo = tuple(
 46            tuple(data[dim].shape[comp] + 2 * halos[dim][comp] for comp in dims)
 47            for dim in dims
 48        )
 49        self.data = tuple(
 50            np.full(shape_with_halo[dim], INVALID_INIT_VALUE, dtype=self.dtype)
 51            for dim in dims
 52        )
 53        self.domain = tuple(
 54            tuple(
 55                slice(halos[dim][comp], halos[dim][comp] + data[dim].shape[comp])
 56                for comp in dims
 57            )
 58            for dim in dims
 59        )
 60        for dim in dims:
 61            assert data[dim].dtype == self.dtype
 62            self.get_component(dim)[:] = data[dim][:]
 63
 64        empty = np.empty(tuple([0] * self.n_dims), dtype=self.dtype)
 65        self._impl_data = (
 66            self.data[OUTER] if self.n_dims > 1 else empty,
 67            self.data[MID3D] if self.n_dims > 2 else empty,
 68            self.data[INNER],
 69        )
 70
 71    @staticmethod
 72    def clone(field):
 73        """returns a newly allocated field of the same shape, halo and boundary conditions"""
 74        return VectorField(
 75            tuple(field.get_component(d) for d in range(field.n_dims)),
 76            field.halo,
 77            field.boundary_conditions,
 78        )
 79
 80    def get_component(self, i: int) -> np.ndarray:
 81        """returns a view over given component of the field excluding halo"""
 82        return self.data[i][self.domain[i]]
 83
 84    def div(self, grid_step: tuple) -> ScalarField:
 85        """returns a newly allocated scalar field (with no halo) containing the
 86        divergence of the vector field computed using minimal stencil"""
 87        diff_sum = None
 88        for dim in range(self.n_dims):
 89            tmp = np.diff(self.get_component(dim), axis=dim) / grid_step[dim]
 90            if diff_sum is None:
 91                diff_sum = tmp
 92            else:
 93                diff_sum += tmp
 94        result = ScalarField(
 95            diff_sum,
 96            halo=0,
 97            boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * len(grid_step)),
 98        )
 99        return result
100
101    @staticmethod
102    def make_null(n_dims, traversals):
103        """returns a vector field instance with no allocated data storage,
104        see `Field._make_null` other properties of the returned field"""
105        return Field._make_null(
106            VectorField(
107                tuple([np.full([1] * n_dims, INVALID_NULL_VALUE)] * n_dims),
108                halo=1,
109                boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * n_dims),
110            ),
111            traversals,
112        )
class VectorField(PyMPDATA.impl.field.Field):
 23class VectorField(Field):
 24    """n-component n-dimensional vector field including halo data,
 25    used to represent the advector field"""
 26
 27    def __init__(self, data: tuple, halo: int, boundary_conditions: tuple):
 28        super().__init__(
 29            grid=tuple(datum.shape[dim] - 1 for dim, datum in enumerate(data)),
 30            boundary_conditions=boundary_conditions,
 31            halo=halo,
 32            dtype=data[0].dtype,
 33        )
 34
 35        for comp, field in enumerate(data):
 36            assert len(field.shape) == len(data)
 37            for dim, dim_length in enumerate(field.shape):
 38                assert halo <= dim_length
 39                if not (np.asarray(self.grid) == 0).all():
 40                    assert dim_length == self.grid[dim] + (dim == comp)
 41        for boundary_condition in boundary_conditions:
 42            assert not inspect.isclass(boundary_condition)
 43
 44        dims = range(self.n_dims)
 45        halos = tuple(tuple((halo - (dim == comp)) for comp in dims) for dim in dims)
 46        shape_with_halo = tuple(
 47            tuple(data[dim].shape[comp] + 2 * halos[dim][comp] for comp in dims)
 48            for dim in dims
 49        )
 50        self.data = tuple(
 51            np.full(shape_with_halo[dim], INVALID_INIT_VALUE, dtype=self.dtype)
 52            for dim in dims
 53        )
 54        self.domain = tuple(
 55            tuple(
 56                slice(halos[dim][comp], halos[dim][comp] + data[dim].shape[comp])
 57                for comp in dims
 58            )
 59            for dim in dims
 60        )
 61        for dim in dims:
 62            assert data[dim].dtype == self.dtype
 63            self.get_component(dim)[:] = data[dim][:]
 64
 65        empty = np.empty(tuple([0] * self.n_dims), dtype=self.dtype)
 66        self._impl_data = (
 67            self.data[OUTER] if self.n_dims > 1 else empty,
 68            self.data[MID3D] if self.n_dims > 2 else empty,
 69            self.data[INNER],
 70        )
 71
 72    @staticmethod
 73    def clone(field):
 74        """returns a newly allocated field of the same shape, halo and boundary conditions"""
 75        return VectorField(
 76            tuple(field.get_component(d) for d in range(field.n_dims)),
 77            field.halo,
 78            field.boundary_conditions,
 79        )
 80
 81    def get_component(self, i: int) -> np.ndarray:
 82        """returns a view over given component of the field excluding halo"""
 83        return self.data[i][self.domain[i]]
 84
 85    def div(self, grid_step: tuple) -> ScalarField:
 86        """returns a newly allocated scalar field (with no halo) containing the
 87        divergence of the vector field computed using minimal stencil"""
 88        diff_sum = None
 89        for dim in range(self.n_dims):
 90            tmp = np.diff(self.get_component(dim), axis=dim) / grid_step[dim]
 91            if diff_sum is None:
 92                diff_sum = tmp
 93            else:
 94                diff_sum += tmp
 95        result = ScalarField(
 96            diff_sum,
 97            halo=0,
 98            boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * len(grid_step)),
 99        )
100        return result
101
102    @staticmethod
103    def make_null(n_dims, traversals):
104        """returns a vector field instance with no allocated data storage,
105        see `Field._make_null` other properties of the returned field"""
106        return Field._make_null(
107            VectorField(
108                tuple([np.full([1] * n_dims, INVALID_NULL_VALUE)] * n_dims),
109                halo=1,
110                boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * n_dims),
111            ),
112            traversals,
113        )

n-component n-dimensional vector field including halo data, used to represent the advector field

VectorField(data: tuple, halo: int, boundary_conditions: tuple)
27    def __init__(self, data: tuple, halo: int, boundary_conditions: tuple):
28        super().__init__(
29            grid=tuple(datum.shape[dim] - 1 for dim, datum in enumerate(data)),
30            boundary_conditions=boundary_conditions,
31            halo=halo,
32            dtype=data[0].dtype,
33        )
34
35        for comp, field in enumerate(data):
36            assert len(field.shape) == len(data)
37            for dim, dim_length in enumerate(field.shape):
38                assert halo <= dim_length
39                if not (np.asarray(self.grid) == 0).all():
40                    assert dim_length == self.grid[dim] + (dim == comp)
41        for boundary_condition in boundary_conditions:
42            assert not inspect.isclass(boundary_condition)
43
44        dims = range(self.n_dims)
45        halos = tuple(tuple((halo - (dim == comp)) for comp in dims) for dim in dims)
46        shape_with_halo = tuple(
47            tuple(data[dim].shape[comp] + 2 * halos[dim][comp] for comp in dims)
48            for dim in dims
49        )
50        self.data = tuple(
51            np.full(shape_with_halo[dim], INVALID_INIT_VALUE, dtype=self.dtype)
52            for dim in dims
53        )
54        self.domain = tuple(
55            tuple(
56                slice(halos[dim][comp], halos[dim][comp] + data[dim].shape[comp])
57                for comp in dims
58            )
59            for dim in dims
60        )
61        for dim in dims:
62            assert data[dim].dtype == self.dtype
63            self.get_component(dim)[:] = data[dim][:]
64
65        empty = np.empty(tuple([0] * self.n_dims), dtype=self.dtype)
66        self._impl_data = (
67            self.data[OUTER] if self.n_dims > 1 else empty,
68            self.data[MID3D] if self.n_dims > 2 else empty,
69            self.data[INNER],
70        )
data
domain
@staticmethod
def clone(field):
72    @staticmethod
73    def clone(field):
74        """returns a newly allocated field of the same shape, halo and boundary conditions"""
75        return VectorField(
76            tuple(field.get_component(d) for d in range(field.n_dims)),
77            field.halo,
78            field.boundary_conditions,
79        )

returns a newly allocated field of the same shape, halo and boundary conditions

def get_component(self, i: int) -> numpy.ndarray:
81    def get_component(self, i: int) -> np.ndarray:
82        """returns a view over given component of the field excluding halo"""
83        return self.data[i][self.domain[i]]

returns a view over given component of the field excluding halo

def div(self, grid_step: tuple) -> PyMPDATA.scalar_field.ScalarField:
 85    def div(self, grid_step: tuple) -> ScalarField:
 86        """returns a newly allocated scalar field (with no halo) containing the
 87        divergence of the vector field computed using minimal stencil"""
 88        diff_sum = None
 89        for dim in range(self.n_dims):
 90            tmp = np.diff(self.get_component(dim), axis=dim) / grid_step[dim]
 91            if diff_sum is None:
 92                diff_sum = tmp
 93            else:
 94                diff_sum += tmp
 95        result = ScalarField(
 96            diff_sum,
 97            halo=0,
 98            boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * len(grid_step)),
 99        )
100        return result

returns a newly allocated scalar field (with no halo) containing the divergence of the vector field computed using minimal stencil

@staticmethod
def make_null(n_dims, traversals):
102    @staticmethod
103    def make_null(n_dims, traversals):
104        """returns a vector field instance with no allocated data storage,
105        see `Field._make_null` other properties of the returned field"""
106        return Field._make_null(
107            VectorField(
108                tuple([np.full([1] * n_dims, INVALID_NULL_VALUE)] * n_dims),
109                halo=1,
110                boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * n_dims),
111            ),
112            traversals,
113        )

returns a vector field instance with no allocated data storage, see Field._make_null other properties of the returned field