@@ -29,19 +29,24 @@ class NCFile(FileBase):
29
29
mode : str
30
30
``r``, ``w`` or ``a`` for read, write or append. Default is ``a``.
31
31
clobber : bool, optional
32
+ If True (default), opening a file with mode='w' will clobber an
33
+ existing file with the same name. If False, an exception will be
34
+ raised if a file with the same name already exists.
35
+ kw : dict, optional
36
+ Optional additional keyword arguments used when creating the backend
37
+ file.
32
38
33
39
Note
34
40
----
35
41
Each class instance creates one unique NetCDF4-file, with one step-counter.
36
42
It is possible to store multiple fields in each file, but all snapshots of
37
43
the fields must be taken at the same time. If you want one field stored
38
44
every 10th timestep and another every 20th timestep, then use two different
39
- class instances and as such two NetCDF4-files .
45
+ class instances with two different filenames ``ncname`` .
40
46
"""
41
47
def __init__ (self , ncname , domain = None , mode = 'a' , clobber = True , ** kw ):
42
- FileBase .__init__ (self , domain = domain , ** kw )
48
+ FileBase .__init__ (self , ncname , domain = domain )
43
49
from netCDF4 import Dataset
44
- self .filename = ncname
45
50
# netCDF4 does not seem to handle 'a' if the file does not already exist
46
51
if mode == 'a' and not os .path .exists (ncname ):
47
52
mode = 'w'
@@ -51,11 +56,9 @@ def __init__(self, ncname, domain=None, mode='a', clobber=True, **kw):
51
56
if not 'time' in self .f .variables :
52
57
self .f .createDimension ('time' , None )
53
58
self .f .createVariable ('time' , np .float , ('time' ))
54
-
55
59
self .close ()
56
60
57
- def _check_domain (self , write_domain , field ):
58
- """Check dimensions of domain and write to file if missing"""
61
+ def _check_domain (self , group , field ):
59
62
N = field .global_shape [field .rank :]
60
63
if self .domain is None :
61
64
self .domain = []
@@ -92,22 +95,59 @@ def _check_domain(self, write_domain, field):
92
95
def backend ():
93
96
return 'netcdf4'
94
97
95
- def open (self ):
98
+ def open (self , mode = 'r+' ):
96
99
from netCDF4 import Dataset
97
- self .f = Dataset (self .filename , mode = 'r+' , parallel = True , comm = comm )
100
+ self .f = Dataset (self .filename , mode = mode , parallel = True , comm = comm )
98
101
99
102
def write (self , step , fields , ** kw ):
100
- """Write snapshot step of ``fields`` to NetCDF4 file
103
+ """Write snapshot `` step`` of ``fields`` to NetCDF4 file
101
104
102
105
Parameters
103
106
----------
104
107
step : int
105
- Index of snapshot
108
+ Index of snapshot.
106
109
fields : dict
107
110
The fields to be dumped to file. (key, value) pairs are group name
108
111
and either arrays or 2-tuples, respectively. The arrays are complete
109
112
arrays to be stored, whereas 2-tuples are arrays with associated
110
113
*global* slices.
114
+ as_scalar : boolean, optional
115
+ Whether to store rank > 0 arrays as scalars. Default is False.
116
+
117
+ Example
118
+ -------
119
+ >>> from mpi4py import MPI
120
+ >>> from mpi4py_fft import PFFT, NCFile, newDistArray
121
+ >>> comm = MPI.COMM_WORLD
122
+ >>> T = PFFT(comm, (15, 16, 17))
123
+ >>> u = newDistArray(T, forward_output=False, val=1)
124
+ >>> v = newDistArray(T, forward_output=False, val=2)
125
+ >>> f = NCFile('ncfilename.nc', mode='w')
126
+ >>> f.write(0, {'u': [u, (u, [slice(None), 4, slice(None)])],
127
+ ... 'v': [v, (v, [slice(None), 5, 5])]})
128
+ >>> f.write(1, {'u': [u, (u, [slice(None), 4, slice(None)])],
129
+ ... 'v': [v, (v, [slice(None), 5, 5])]})
130
+ >>> f.close()
131
+
132
+ This stores the following datasets to the file ``ncfilename.nc``.
133
+ Using in a terminal 'ncdump -h ncfilename.nc', one gets::
134
+
135
+ netcdf ncfilename {
136
+ dimensions:
137
+ time = UNLIMITED ; // (2 currently)
138
+ x = 15 ;
139
+ y = 16 ;
140
+ z = 17 ;
141
+ variables:
142
+ double time(time) ;
143
+ double x(x) ;
144
+ double y(y) ;
145
+ double z(z) ;
146
+ double u(time, x, y, z) ;
147
+ double u_slice_4_slice(time, x, z) ;
148
+ double v(time, x, y, z) ;
149
+ double v_slice_5_5(time, x) ;
150
+ }
111
151
112
152
"""
113
153
self .open ()
@@ -122,17 +162,6 @@ def write(self, step, fields, **kw):
122
162
self .close ()
123
163
124
164
def read (self , u , name , ** kw ):
125
- """Read into array ``u``
126
-
127
- Parameters
128
- ----------
129
- u : array
130
- The array to read into.
131
- name : str
132
- Name of array to be read.
133
- step : int, optional
134
- Index of field to be read. Default is 0.
135
- """
136
165
step = kw .get ('step' , 0 )
137
166
self .open ()
138
167
s = u .local_slice ()
@@ -141,10 +170,9 @@ def read(self, u, name, **kw):
141
170
self .close ()
142
171
143
172
def _write_slice_step (self , name , step , slices , field , ** kw ):
144
- assert name not in self .dims
173
+ assert name not in self .dims # Crashes if user tries to name fields x, y, z, .
145
174
rank = field .rank
146
- slices = (slice (None ),)* rank + tuple (slices )
147
- slices = list (slices )
175
+ slices = list ((slice (None ),)* rank + tuple (slices ))
148
176
slname = self ._get_slice_name (slices [rank :])
149
177
s = field .local_slice ()
150
178
slices , inside = self ._get_local_slices (slices , s )
@@ -168,7 +196,7 @@ def _write_slice_step(self, name, step, slices, field, **kw):
168
196
self .f .sync ()
169
197
170
198
def _write_group (self , name , u , step , ** kw ):
171
- assert name not in self .dims
199
+ assert name not in self .dims # Crashes if user tries to name fields x, y, z, .
172
200
s = u .local_slice ()
173
201
if name not in self .f .variables :
174
202
h = self .f .createVariable (name , u .dtype , self .dims )
0 commit comments