0
# Legacy API Compatibility
1
2
The h5netcdf legacy API provides drop-in compatibility with netCDF4-python, enabling existing code to work with minimal modifications. This API mirrors the method names, behavior, and conventions of the netCDF4-python library.
3
4
## Capabilities
5
6
### Dataset Class
7
8
Main file interface compatible with netCDF4.Dataset.
9
10
```python { .api }
11
class Dataset(File, Group, HasAttributesMixin):
12
def __init__(self, filename, mode: str = 'r', format: str = 'NETCDF4',
13
group: str = None, invalid_netcdf: bool = False,
14
phony_dims: str = None, **kwargs):
15
"""
16
Open or create a netCDF dataset (compatible with netCDF4.Dataset).
17
18
Args:
19
filename: Path to the netCDF file or file-like object
20
mode (str): File access mode ('r', 'w', 'a', 'r+')
21
format (str): File format (only 'NETCDF4' supported)
22
group (str): Group to open (None for root)
23
invalid_netcdf (bool): Allow non-netCDF4 features
24
phony_dims (str): Handle unlabeled dimensions ('sort', 'access')
25
**kwargs: Additional file creation parameters
26
"""
27
...
28
29
def close(self) -> None:
30
"""Close the dataset."""
31
...
32
33
def sync(self) -> None:
34
"""Synchronize data to disk."""
35
...
36
37
def flush(self) -> None:
38
"""Flush data to disk."""
39
...
40
```
41
42
### Group Operations
43
44
Group management with netCDF4-python compatible method names.
45
46
```python { .api }
47
class Group(Group, HasAttributesMixin):
48
def createGroup(self, name: str) -> Group:
49
"""Create child group (compatible with netCDF4)."""
50
...
51
52
@property
53
def groups(self) -> dict:
54
"""Dictionary of child groups."""
55
...
56
57
@property
58
def variables(self) -> dict:
59
"""Dictionary of variables in this group."""
60
...
61
62
@property
63
def dimensions(self) -> dict:
64
"""Dictionary of dimensions in this group."""
65
...
66
67
def createDimension(self, name: str, size: int = None) -> Dimension:
68
"""
69
Create dimension (compatible with netCDF4).
70
71
Args:
72
name (str): Dimension name
73
size (int): Dimension size (None for unlimited)
74
75
Returns:
76
Dimension: The created dimension
77
"""
78
...
79
80
def createVariable(self, varname: str, datatype, dimensions: tuple = (),
81
zlib: bool = False, complevel: int = 4, shuffle: bool = True,
82
fletcher32: bool = False, contiguous: bool = False,
83
chunksizes: tuple = None, endian: str = 'native',
84
least_significant_digit: int = None, fill_value = None,
85
chunk_cache = None, **kwargs) -> Variable:
86
"""
87
Create variable (compatible with netCDF4).
88
89
Args:
90
varname (str): Variable name
91
datatype: Data type (numpy dtype, string, or UserType)
92
dimensions (tuple): Dimension names
93
zlib (bool): Enable gzip compression
94
complevel (int): Compression level (1-9)
95
shuffle (bool): Apply shuffle filter
96
fletcher32 (bool): Apply Fletcher32 checksum
97
contiguous (bool): Store data contiguously
98
chunksizes (tuple): Chunk sizes
99
endian (str): Byte order ('native', 'little', 'big')
100
least_significant_digit (int): Precision for compression
101
fill_value: Fill value for missing data
102
chunk_cache: HDF5 chunk cache settings
103
**kwargs: Additional creation parameters
104
105
Returns:
106
Variable: The created variable
107
"""
108
...
109
```
110
111
### Type Creation
112
113
User-defined type creation with netCDF4-python method names.
114
115
```python { .api }
116
def createEnumType(self, datatype, datatype_name: str, enum_dict: dict) -> EnumType:
117
"""Create enumeration type (compatible with netCDF4)."""
118
...
119
120
def createVLType(self, datatype, datatype_name: str) -> VLType:
121
"""Create variable-length type (compatible with netCDF4)."""
122
...
123
124
def createCompoundType(self, datatype, datatype_name: str) -> CompoundType:
125
"""Create compound type (compatible with netCDF4)."""
126
...
127
128
@property
129
def enumtypes(self) -> dict:
130
"""Dictionary of enumeration types in this group."""
131
...
132
133
@property
134
def vltypes(self) -> dict:
135
"""Dictionary of variable-length types in this group."""
136
...
137
138
@property
139
def cmptypes(self) -> dict:
140
"""Dictionary of compound types in this group."""
141
...
142
```
143
144
### Variable Interface
145
146
Variable class with netCDF4-python compatible methods.
147
148
```python { .api }
149
class Variable(BaseVariable, HasAttributesMixin):
150
def chunking(self):
151
"""
152
Return chunking information (compatible with netCDF4).
153
154
Returns:
155
tuple or str: Chunk sizes or 'contiguous' if not chunked
156
"""
157
...
158
159
def filters(self) -> dict:
160
"""
161
Return HDF5 filter parameters (compatible with netCDF4).
162
163
Returns:
164
dict: Filter settings including complevel, zlib, shuffle, fletcher32
165
"""
166
...
167
168
@property
169
def dtype(self):
170
"""Return numpy dtype (compatible with netCDF4.Variable)."""
171
...
172
```
173
174
### Attribute Compatibility
175
176
NetCDF4-python style attribute access methods.
177
178
```python { .api }
179
class HasAttributesMixin:
180
def getncattr(self, name: str):
181
"""
182
Get attribute by name (compatible with netCDF4).
183
184
Args:
185
name (str): Attribute name
186
187
Returns:
188
Attribute value
189
"""
190
...
191
192
def setncattr(self, name: str, value) -> None:
193
"""
194
Set attribute by name (compatible with netCDF4).
195
196
Args:
197
name (str): Attribute name
198
value: Attribute value
199
"""
200
...
201
202
def ncattrs(self) -> list:
203
"""
204
List attribute names (compatible with netCDF4).
205
206
Returns:
207
list: Attribute names
208
"""
209
...
210
211
def __getattr__(self, name: str):
212
"""Direct attribute access (compatible with netCDF4)."""
213
...
214
215
def __setattr__(self, name: str, value) -> None:
216
"""Direct attribute assignment (compatible with netCDF4)."""
217
...
218
```
219
220
### Constants and Utilities
221
222
Default fill values and utility functions.
223
224
```python { .api }
225
default_fillvals = {
226
'S1': '\x00',
227
'i1': -127,
228
'u1': 255,
229
'i2': -32767,
230
'u2': 65535,
231
'i4': -2147483647,
232
'u4': 4294967295,
233
'i8': -9223372036854775806,
234
'u8': 18446744073709551614,
235
'f4': 9.969209968386869e36,
236
'f8': 9.969209968386869e36,
237
}
238
239
def _get_default_fillvalue(dtype) -> any:
240
"""Get default fill value for data type."""
241
...
242
243
def _check_return_dtype_endianess(endian: str = "native") -> str:
244
"""Check and normalize endianness specification."""
245
...
246
```
247
248
## Usage Examples
249
250
### Basic Migration from netCDF4-python
251
252
```python
253
# Original netCDF4-python code:
254
# import netCDF4
255
256
# h5netcdf equivalent (drop-in replacement):
257
import h5netcdf.legacyapi as netCDF4
258
259
# Rest of the code remains identical
260
with netCDF4.Dataset('data.nc', 'r') as dataset:
261
# Access variables through .variables
262
temperature = dataset.variables['temperature'][:]
263
264
# Access dimensions through .dimensions
265
time_size = len(dataset.dimensions['time'])
266
267
# Access attributes using netCDF4 methods
268
units = dataset.variables['temperature'].getncattr('units')
269
global_attrs = dataset.ncattrs()
270
```
271
272
### Creating Files with Legacy API
273
274
```python
275
import h5netcdf.legacyapi as netCDF4
276
import numpy as np
277
278
# Create file using netCDF4-python syntax
279
with netCDF4.Dataset('output.nc', 'w', format='NETCDF4') as dataset:
280
# Create dimensions
281
time_dim = dataset.createDimension('time', None) # Unlimited
282
lat_dim = dataset.createDimension('lat', 180)
283
lon_dim = dataset.createDimension('lon', 360)
284
285
# Create coordinate variables
286
times = dataset.createVariable('time', 'f8', ('time',))
287
latitudes = dataset.createVariable('lat', 'f4', ('lat',))
288
longitudes = dataset.createVariable('lon', 'f4', ('lon',))
289
290
# Create data variable with compression
291
temperature = dataset.createVariable(
292
'temperature', 'f4', ('time', 'lat', 'lon'),
293
zlib=True, # Enable compression
294
complevel=6, # Compression level
295
shuffle=True, # Shuffle filter
296
fletcher32=True # Checksum
297
)
298
299
# Set attributes using netCDF4 methods
300
dataset.setncattr('title', 'Global Temperature Data')
301
dataset.setncattr('institution', 'Climate Research Center')
302
303
temperature.setncattr('units', 'K')
304
temperature.setncattr('long_name', 'Air Temperature')
305
306
# Write coordinate data
307
latitudes[:] = np.linspace(-89.5, 89.5, 180)
308
longitudes[:] = np.linspace(-179.5, 179.5, 360)
309
310
# Write time series data
311
for t in range(10):
312
times[t] = t
313
temperature[t, :, :] = np.random.random((180, 360)) * 50 + 273.15
314
```
315
316
### Group Operations
317
318
```python
319
import h5netcdf.legacyapi as netCDF4
320
321
with netCDF4.Dataset('groups.nc', 'w') as dataset:
322
# Create groups using netCDF4 syntax
323
obs_group = dataset.createGroup('observations')
324
model_group = dataset.createGroup('model')
325
326
# Create dimensions in groups
327
obs_group.createDimension('time', 100)
328
obs_group.createDimension('station', 50)
329
330
# Create variables in groups
331
temp_obs = obs_group.createVariable('temperature', 'f4', ('time', 'station'))
332
333
# Set group attributes
334
obs_group.setncattr('description', 'Observational data')
335
model_group.setncattr('description', 'Model output')
336
```
337
338
### User-Defined Types
339
340
```python
341
import h5netcdf.legacyapi as netCDF4
342
import numpy as np
343
344
with netCDF4.Dataset('types.nc', 'w') as dataset:
345
# Create enumeration type
346
quality_enum = dataset.createEnumType('i1', 'quality_flag', {
347
'good': 0,
348
'questionable': 1,
349
'bad': 2,
350
'missing': 3
351
})
352
353
# Create compound type
354
obs_dtype = np.dtype([
355
('value', 'f4'),
356
('uncertainty', 'f4'),
357
('quality', 'i1')
358
])
359
obs_compound = dataset.createCompoundType(obs_dtype, 'observation')
360
361
# Create variable-length type
362
vlen_str = dataset.createVLType(str, 'vlen_string')
363
364
# Use these types in variables
365
dataset.createDimension('n', 100)
366
367
quality_var = dataset.createVariable('quality', quality_enum, ('n',))
368
obs_var = dataset.createVariable('observations', obs_compound, ('n',))
369
comment_var = dataset.createVariable('comments', vlen_str, ('n',))
370
```
371
372
### Attribute Access Patterns
373
374
```python
375
import h5netcdf.legacyapi as netCDF4
376
377
with netCDF4.Dataset('attributes.nc', 'r') as dataset:
378
# NetCDF4 style attribute access
379
title = dataset.getncattr('title')
380
all_global_attrs = dataset.ncattrs()
381
382
# Direct attribute access (also supported)
383
title = dataset.title # Same as getncattr('title')
384
385
# Variable attributes
386
temp = dataset.variables['temperature']
387
units = temp.getncattr('units')
388
var_attrs = temp.ncattrs()
389
390
# Direct variable attribute access
391
units = temp.units # Same as getncattr('units')
392
393
# Check attribute existence
394
if 'long_name' in temp.ncattrs():
395
long_name = temp.getncattr('long_name')
396
```
397
398
### Variable Information
399
400
```python
401
import h5netcdf.legacyapi as netCDF4
402
403
with netCDF4.Dataset('info.nc', 'r') as dataset:
404
temp = dataset.variables['temperature']
405
406
# Get chunking information (netCDF4 method)
407
chunks = temp.chunking()
408
if chunks:
409
print(f"Variable is chunked: {chunks}")
410
else:
411
print("Variable is not chunked")
412
413
# Get filter information (netCDF4 method)
414
filters = temp.filters()
415
print(f"Compression: {filters.get('zlib', False)}")
416
print(f"Shuffle: {filters.get('shuffle', False)}")
417
print(f"Fletcher32: {filters.get('fletcher32', False)}")
418
419
# Other netCDF4-compatible properties
420
print(f"Shape: {temp.shape}")
421
print(f"Dimensions: {temp.dimensions}")
422
print(f"Data type: {temp.dtype}")
423
```
424
425
### Error Handling and Compatibility
426
427
```python
428
import h5netcdf.legacyapi as netCDF4
429
430
try:
431
with netCDF4.Dataset('test.nc', 'r') as dataset:
432
# Code that works with both netCDF4-python and h5netcdf
433
pass
434
435
except FileNotFoundError:
436
print("File not found")
437
except OSError as e:
438
print(f"I/O error: {e}")
439
```
440
441
### Mixed API Usage
442
443
```python
444
# You can mix modern and legacy APIs
445
import h5netcdf
446
import h5netcdf.legacyapi as netCDF4
447
448
# Open with legacy API
449
with netCDF4.Dataset('mixed.nc', 'w') as legacy_dataset:
450
# Use legacy methods
451
legacy_dataset.createDimension('time', 10)
452
temp = legacy_dataset.createVariable('temperature', 'f4', ('time',))
453
454
# Access underlying modern API objects
455
modern_file = legacy_dataset # Dataset inherits from File
456
457
# Use modern API methods on the same object
458
modern_file.flush() # Modern API method
459
460
# Variables also support both APIs
461
temp.attrs['units'] = 'K' # Modern API attribute access
462
temp.setncattr('long_name', 'Temperature') # Legacy API method
463
```
464
465
## Migration Guidelines
466
467
### From netCDF4-python to h5netcdf
468
469
1. **Import change**: Replace `import netCDF4` with `import h5netcdf.legacyapi as netCDF4`
470
471
2. **Method compatibility**: All major netCDF4-python methods are supported
472
473
3. **Performance differences**: h5netcdf may have different performance characteristics
474
475
4. **Feature differences**: Some advanced netCDF4-python features may not be available
476
477
### Key Differences from netCDF4-python
478
479
- **Backend**: Uses h5py instead of the C netCDF library
480
- **Dependencies**: Fewer binary dependencies
481
- **Performance**: May be faster for some operations, slower for others
482
- **Memory usage**: Different memory access patterns
483
- **HDF5 features**: Direct access to HDF5-specific features when needed
484
485
### Best Practices for Legacy API
486
487
- Use the legacy API for existing codebases requiring compatibility
488
- Consider migrating to the modern API for new projects
489
- Test performance characteristics for your specific use case
490
- Be aware of subtle behavioral differences in edge cases