File: h5py_file.py

package info (click to toggle)
mpi4py-fft 2.0.6-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 720 kB
  • sloc: python: 3,053; ansic: 87; makefile: 42; sh: 33
file content (152 lines) | stat: -rw-r--r-- 5,889 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import numpy as np
from mpi4py import MPI
from .file_base import FileBase

__all__ = ('HDF5File',)

comm = MPI.COMM_WORLD

class HDF5File(FileBase):
    """Class for reading/writing data to HDF5 format

    Parameters
    ----------
    h5name : str
        Name of hdf5 file to be created.
    domain : sequence, optional
        An optional spatial mesh or domain to go with the data.
        Sequence of either

            - 2-tuples, where each 2-tuple contains the (origin, length)
              of each dimension, e.g., (0, 2*pi).
            - Arrays of coordinates, e.g., np.linspace(0, 2*pi, N). One
              array per dimension.
    mode : str, optional
        ``r``, ``w`` or ``a`` for read, write or append. Default is ``a``.
    kw : dict, optional
        Optional additional keyword arguments used when creating the file
        used to store data.
    """
    def __init__(self, h5name, domain=None, mode='a', **kw):
        FileBase.__init__(self, h5name, domain=domain)
        import h5py
        self.f = h5py.File(h5name, mode, driver="mpio", comm=comm, **kw)
        self.close()

    def _check_domain(self, group, field):
        if self.domain is None:
            self.domain = ((0, 2*np.pi),)*field.dimensions
        assert len(self.domain) == field.dimensions
        self.f.require_group(group)
        if not "shape" in self.f[group].attrs:
            self.f[group].attrs.create("shape", field.pencil.shape)
        if not "rank" in self.f[group].attrs:
            self.f[group].attrs.create("rank", field.rank)
        assert field.rank == self.f[group].attrs["rank"]
        assert np.all(field.pencil.shape == self.f[group].attrs["shape"])
        if isinstance(self.domain[0], np.ndarray):
            self.f[group].require_group("mesh")
        else:
            self.f[group].require_group("domain")
        for i in range(field.dimensions):
            d = self.domain[i]
            if isinstance(d, np.ndarray):
                d0 = np.squeeze(d)
                self.f[group]["mesh"].require_dataset("x{}".format(i),
                                                      shape=d0.shape,
                                                      dtype=d0.dtype,
                                                      data=d0)
            else:
                d0 = np.array([d[0], d[1]])
                self.f[group]["domain"].require_dataset("x{}".format(i),
                                                        shape=d0.shape,
                                                        dtype=d0.dtype,
                                                        data=d0)

    @staticmethod
    def backend():
        return 'hdf5'

    def open(self, mode='r+'):
        import h5py
        self.f = h5py.File(self.filename, mode, driver="mpio", comm=comm)

    def write(self, step, fields, **kw):
        """Write snapshot ``step`` of ``fields`` to HDF5 file

        Parameters
        ----------
        step : int
            Index of snapshot.
        fields : dict
            The fields to be dumped to file. (key, value) pairs are group name
            and either arrays or 2-tuples, respectively. The arrays are complete
            arrays to be stored, whereas 2-tuples are arrays with associated
            *global* slices.
        as_scalar : boolean, optional
            Whether to store rank > 0 arrays as scalars. Default is False.

        Example
        -------
        >>> from mpi4py import MPI
        >>> from mpi4py_fft import PFFT, HDF5File, newDistArray
        >>> comm = MPI.COMM_WORLD
        >>> T = PFFT(comm, (15, 16, 17))
        >>> u = newDistArray(T, forward_output=False, val=1)
        >>> v = newDistArray(T, forward_output=False, val=2)
        >>> f = HDF5File('h5filename.h5', mode='w')
        >>> f.write(0, {'u': [u, (u, [slice(None), 4, slice(None)])],
        ...             'v': [v, (v, [slice(None), 5, 5])]})
        >>> f.write(1, {'u': [u, (u, [slice(None), 4, slice(None)])],
        ...             'v': [v, (v, [slice(None), 5, 5])]})

        This stores data within two main groups ``u`` and ``v``. The HDF5 file
        will in the end contain groups::

            /u/3D/{0, 1}
            /u/2D/slice_4_slice/{0, 1}
            /v/3D/{0, 1}
            /v/1D/slice_5_5/{0, 1}

        Note
        ----
        The list of slices used in storing only parts of the arrays are views
        of the *global* arrays.

        """
        self.open()
        FileBase.write(self, step, fields, **kw)
        self.close()

    def read(self, u, name, **kw):
        step = kw.get('step', 0)
        self.open()
        s = u.local_slice()
        dset = "/".join((name, "{}D".format(u.dimensions), str(step)))
        u[:] = self.f[dset][s]
        self.close()

    def _write_slice_step(self, name, step, slices, field, **kw):
        rank = field.rank
        slices = (slice(None),)*rank + tuple(slices)
        slices = list(slices)
        ndims = slices[rank:].count(slice(None))
        slname = self._get_slice_name(slices[rank:])
        s = field.local_slice()
        slices, inside = self._get_local_slices(slices, s)
        sp = np.nonzero([isinstance(x, slice) for x in slices])[0]
        sf = tuple(np.take(s, sp))
        sl = tuple(slices)
        group = "/".join((name, "{}D".format(ndims), slname))
        self.f.require_group(group)
        N = field.global_shape
        self.f[group].require_dataset(str(step), shape=tuple(np.take(N, sp)), dtype=field.dtype)
        if inside == 1:
            self.f["/".join((group, str(step)))][sf] = field[sl]

    def _write_group(self, name, u, step, **kw):
        s = u.local_slice()
        group = "/".join((name, "{}D".format(u.dimensions)))
        self.f.require_group(group)
        self.f[group].require_dataset(str(step), shape=u.global_shape, dtype=u.dtype)
        self.f["/".join((group, str(step)))][s] = u