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            fill_halos_name="fill_halos_vector",
 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        )
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            fill_halos_name="fill_halos_vector",
 34        )
 35
 36        for comp, field in enumerate(data):
 37            assert len(field.shape) == len(data)
 38            for dim, dim_length in enumerate(field.shape):
 39                assert halo <= dim_length
 40                if not (np.asarray(self.grid) == 0).all():
 41                    assert dim_length == self.grid[dim] + (dim == comp)
 42        for boundary_condition in boundary_conditions:
 43            assert not inspect.isclass(boundary_condition)
 44
 45        dims = range(self.n_dims)
 46        halos = tuple(tuple((halo - (dim == comp)) for comp in dims) for dim in dims)
 47        shape_with_halo = tuple(
 48            tuple(data[dim].shape[comp] + 2 * halos[dim][comp] for comp in dims)
 49            for dim in dims
 50        )
 51        self.data = tuple(
 52            np.full(shape_with_halo[dim], INVALID_INIT_VALUE, dtype=self.dtype)
 53            for dim in dims
 54        )
 55        self.domain = tuple(
 56            tuple(
 57                slice(halos[dim][comp], halos[dim][comp] + data[dim].shape[comp])
 58                for comp in dims
 59            )
 60            for dim in dims
 61        )
 62        for dim in dims:
 63            assert data[dim].dtype == self.dtype
 64            self.get_component(dim)[:] = data[dim][:]
 65
 66        empty = np.empty(tuple([0] * self.n_dims), dtype=self.dtype)
 67        self._impl_data = (
 68            self.data[OUTER] if self.n_dims > 1 else empty,
 69            self.data[MID3D] if self.n_dims > 2 else empty,
 70            self.data[INNER],
 71        )
 72
 73    @staticmethod
 74    def clone(field):
 75        """returns a newly allocated field of the same shape, halo and boundary conditions"""
 76        return VectorField(
 77            tuple(field.get_component(d) for d in range(field.n_dims)),
 78            field.halo,
 79            field.boundary_conditions,
 80        )
 81
 82    def get_component(self, i: int) -> np.ndarray:
 83        """returns a view over given component of the field excluding halo"""
 84        return self.data[i][self.domain[i]]
 85
 86    def div(self, grid_step: tuple) -> ScalarField:
 87        """returns a newly allocated scalar field (with no halo) containing the
 88        divergence of the vector field computed using minimal stencil"""
 89        diff_sum = None
 90        for dim in range(self.n_dims):
 91            tmp = np.diff(self.get_component(dim), axis=dim) / grid_step[dim]
 92            if diff_sum is None:
 93                diff_sum = tmp
 94            else:
 95                diff_sum += tmp
 96        result = ScalarField(
 97            diff_sum,
 98            halo=0,
 99            boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * len(grid_step)),
100        )
101        return result
102
103    @staticmethod
104    def make_null(n_dims, traversals):
105        """returns a vector field instance with no allocated data storage,
106        see `Field._make_null` other properties of the returned field"""
107        return Field._make_null(
108            VectorField(
109                tuple([np.full([1] * n_dims, INVALID_NULL_VALUE)] * n_dims),
110                halo=1,
111                boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * n_dims),
112            ),
113            traversals,
114        )

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

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

def get_component(self, i: int) -> numpy.ndarray:
82    def get_component(self, i: int) -> np.ndarray:
83        """returns a view over given component of the field excluding halo"""
84        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:
 86    def div(self, grid_step: tuple) -> ScalarField:
 87        """returns a newly allocated scalar field (with no halo) containing the
 88        divergence of the vector field computed using minimal stencil"""
 89        diff_sum = None
 90        for dim in range(self.n_dims):
 91            tmp = np.diff(self.get_component(dim), axis=dim) / grid_step[dim]
 92            if diff_sum is None:
 93                diff_sum = tmp
 94            else:
 95                diff_sum += tmp
 96        result = ScalarField(
 97            diff_sum,
 98            halo=0,
 99            boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * len(grid_step)),
100        )
101        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):
103    @staticmethod
104    def make_null(n_dims, traversals):
105        """returns a vector field instance with no allocated data storage,
106        see `Field._make_null` other properties of the returned field"""
107        return Field._make_null(
108            VectorField(
109                tuple([np.full([1] * n_dims, INVALID_NULL_VALUE)] * n_dims),
110                halo=1,
111                boundary_conditions=tuple([Constant(INVALID_HALO_VALUE)] * n_dims),
112            ),
113            traversals,
114        )

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