0
# Dimensions Management
1
2
Dimensions define the coordinate system and shape constraints for variables in netCDF4 files. They can be fixed-size or unlimited, and serve as the foundation for all variable definitions and data organization.
3
4
## Capabilities
5
6
### Dimension Creation and Access
7
8
Create and manage dimensions within groups.
9
10
```python { .api }
11
class Dimensions(MutableMapping):
12
def __getitem__(self, name: str) -> Dimension:
13
"""Get dimension by name."""
14
...
15
16
def __setitem__(self, name: str, size: int) -> None:
17
"""Create new dimension with specified size."""
18
...
19
20
def __delitem__(self, name: str) -> None:
21
"""Remove dimension (if not used by variables)."""
22
...
23
24
def __contains__(self, name: str) -> bool:
25
"""Check if dimension exists."""
26
...
27
28
def __iter__(self):
29
"""Iterate over dimension names."""
30
...
31
32
def __len__(self) -> int:
33
"""Number of dimensions in this group."""
34
...
35
```
36
37
### Dimension Objects
38
39
Individual dimension properties and methods.
40
41
```python { .api }
42
class Dimension:
43
def __init__(self, parent: Group, name: str, size: int = None,
44
create_h5ds: bool = False, phony: bool = False):
45
"""
46
Create or access a dimension.
47
48
Args:
49
parent (Group): Parent group containing this dimension
50
name (str): Dimension name
51
size (int): Dimension size (None for unlimited)
52
create_h5ds (bool): Create HDF5 dimension scale
53
phony (bool): Whether this is a phony dimension
54
"""
55
...
56
57
@property
58
def name(self) -> str:
59
"""Dimension name."""
60
...
61
62
@property
63
def size(self) -> int:
64
"""Current dimension size."""
65
...
66
67
def isunlimited(self) -> bool:
68
"""Check if dimension is unlimited."""
69
...
70
71
def group(self) -> Group:
72
"""Return parent group."""
73
...
74
```
75
76
### Dimension Management Operations
77
78
Advanced dimension operations and utilities.
79
80
```python { .api }
81
def add(self, name: str) -> None:
82
"""Add existing dimension from parent groups."""
83
...
84
85
def add_phony(self, name: str, size: int) -> None:
86
"""Add phony dimension for unlabeled axes."""
87
...
88
89
def resize_dimension(self, dim: str, size: int) -> None:
90
"""Resize unlimited dimension."""
91
...
92
```
93
94
## Usage Examples
95
96
### Basic Dimension Operations
97
98
```python
99
import h5netcdf
100
101
with h5netcdf.File('dimensions.nc', 'w') as f:
102
# Create fixed-size dimensions
103
f.dimensions['lat'] = 180
104
f.dimensions['lon'] = 360
105
f.dimensions['level'] = 50
106
107
# Create unlimited dimension
108
f.dimensions['time'] = None # None indicates unlimited
109
110
# Access dimension properties
111
lat_dim = f.dimensions['lat']
112
print(f"Latitude dimension size: {lat_dim.size}")
113
print(f"Is unlimited: {lat_dim.isunlimited()}")
114
115
# Check if dimension exists
116
if 'time' in f.dimensions:
117
time_dim = f.dimensions['time']
118
print(f"Time is unlimited: {time_dim.isunlimited()}")
119
120
# List all dimensions
121
print(f"Dimensions: {list(f.dimensions.keys())}")
122
print(f"Number of dimensions: {len(f.dimensions)}")
123
```
124
125
### Unlimited Dimensions
126
127
```python
128
with h5netcdf.File('unlimited.nc', 'w') as f:
129
# Create unlimited dimension
130
f.dimensions['time'] = None
131
f.dimensions['station'] = 100
132
133
# Create variable using unlimited dimension
134
temp = f.create_variable('temperature', ('time', 'station'), dtype='f4')
135
136
# Initially, unlimited dimension has size 0
137
print(f"Initial time size: {f.dimensions['time'].size}")
138
139
# Writing data extends the unlimited dimension
140
temp[0, :] = np.random.random(100)
141
print(f"After first write: {f.dimensions['time'].size}")
142
143
temp[1, :] = np.random.random(100)
144
print(f"After second write: {f.dimensions['time'].size}")
145
146
# Can explicitly resize unlimited dimensions
147
f.resize_dimension('time', 10)
148
print(f"After resize: {f.dimensions['time'].size}")
149
```
150
151
### Coordinate Variables and Dimensions
152
153
```python
154
with h5netcdf.File('coordinates.nc', 'w') as f:
155
# Create dimensions
156
f.dimensions['x'] = 100
157
f.dimensions['y'] = 50
158
f.dimensions['time'] = None
159
160
# Create coordinate variables (same name as dimension)
161
x_coord = f.create_variable('x', ('x',), dtype='f4')
162
x_coord[:] = np.linspace(0, 99, 100)
163
x_coord.attrs['units'] = 'm'
164
x_coord.attrs['long_name'] = 'X coordinate'
165
166
y_coord = f.create_variable('y', ('y',), dtype='f4')
167
y_coord[:] = np.linspace(0, 49, 50)
168
y_coord.attrs['units'] = 'm'
169
y_coord.attrs['long_name'] = 'Y coordinate'
170
171
# Time coordinate (unlimited)
172
time_coord = f.create_variable('time', ('time',), dtype='f8')
173
time_coord.attrs['units'] = 'days since 2023-01-01'
174
time_coord.attrs['calendar'] = 'standard'
175
176
# Data variable using these dimensions
177
data = f.create_variable('temperature', ('time', 'y', 'x'), dtype='f4')
178
179
# Write time series data
180
for t in range(5):
181
time_coord[t] = t
182
data[t, :, :] = np.random.random((50, 100)) * 30 + 273.15
183
```
184
185
### Dimension Inheritance in Groups
186
187
```python
188
with h5netcdf.File('groups_dims.nc', 'w') as f:
189
# Create dimensions in root group
190
f.dimensions['time'] = None
191
f.dimensions['lat'] = 180
192
f.dimensions['lon'] = 360
193
194
# Create child group
195
surface = f.create_group('surface')
196
197
# Child group can use parent dimensions
198
surface.dimensions.add('time') # Reference parent's time dimension
199
surface.dimensions.add('lat') # Reference parent's lat dimension
200
surface.dimensions.add('lon') # Reference parent's lon dimension
201
202
# Or create group-specific dimensions
203
surface.dimensions['height'] = 10
204
205
# Create variable using mixed dimensions
206
temp = surface.create_variable(
207
'temperature',
208
('time', 'height', 'lat', 'lon'),
209
dtype='f4'
210
)
211
212
print(f"Root dimensions: {list(f.dimensions.keys())}")
213
print(f"Surface dimensions: {list(surface.dimensions.keys())}")
214
```
215
216
### Checking Dimension Usage
217
218
```python
219
with h5netcdf.File('check_usage.nc', 'r') as f:
220
for dim_name, dimension in f.dimensions.items():
221
print(f"Dimension: {dim_name}")
222
print(f" Size: {dimension.size}")
223
print(f" Unlimited: {dimension.isunlimited()}")
224
225
# Find variables using this dimension
226
using_vars = []
227
for var_name, variable in f.variables.items():
228
if dim_name in variable.dimensions:
229
using_vars.append(var_name)
230
231
print(f" Used by variables: {using_vars}")
232
233
# Check if coordinate variable exists
234
if dim_name in f.variables:
235
coord_var = f.variables[dim_name]
236
print(f" Has coordinate variable: {coord_var.dtype}")
237
else:
238
print(f" No coordinate variable")
239
```
240
241
### Phony Dimensions
242
243
```python
244
# Phony dimensions handle unlabeled axes in HDF5 files
245
with h5netcdf.File('unlabeled.nc', 'r', phony_dims='sort') as f:
246
# If file has unlabeled dimensions, they get phony names
247
for dim_name, dimension in f.dimensions.items():
248
if hasattr(dimension, 'phony') and dimension.phony:
249
print(f"Phony dimension: {dim_name} (size: {dimension.size})")
250
```
251
252
### Dimension Validation
253
254
```python
255
with h5netcdf.File('validate.nc', 'w') as f:
256
try:
257
# This will work
258
f.dimensions['valid_name'] = 100
259
260
# This might cause issues depending on backend
261
# f.dimensions[''] = 50 # Empty name
262
263
# Creating variable with non-existent dimension will fail
264
# f.create_variable('test', ('nonexistent',), dtype='f4')
265
266
except Exception as e:
267
print(f"Validation error: {e}")
268
```
269
270
### Multiple Unlimited Dimensions
271
272
```python
273
# Note: NetCDF4 format supports multiple unlimited dimensions
274
with h5netcdf.File('multi_unlimited.nc', 'w') as f:
275
# Multiple unlimited dimensions (netCDF4 feature)
276
f.dimensions['time'] = None
277
f.dimensions['ensemble'] = None
278
f.dimensions['lat'] = 90
279
f.dimensions['lon'] = 180
280
281
# Variable with multiple unlimited dimensions
282
temp = f.create_variable(
283
'temperature',
284
('time', 'ensemble', 'lat', 'lon'),
285
dtype='f4'
286
)
287
288
# Write data to extend both unlimited dimensions
289
temp[0, 0, :, :] = np.random.random((90, 180))
290
temp[1, 2, :, :] = np.random.random((90, 180))
291
292
print(f"Time size: {f.dimensions['time'].size}")
293
print(f"Ensemble size: {f.dimensions['ensemble'].size}")
294
```
295
296
## Dimension Naming Conventions
297
298
### Standard Names
299
- **time**: Temporal coordinate
300
- **lat**, **latitude**: Latitude coordinate
301
- **lon**, **longitude**: Longitude coordinate
302
- **level**, **lev**: Vertical levels
303
- **height**, **depth**: Vertical distance coordinates
304
305
### Best Practices
306
- Use descriptive names that indicate the coordinate type
307
- Avoid spaces and special characters
308
- Be consistent within and across files
309
- Consider CF (Climate and Forecast) conventions for geophysical data
310
311
## Dimension Constraints
312
313
- Dimension names must be unique within a group
314
- Cannot delete dimensions that are used by variables
315
- Unlimited dimensions can only grow, not shrink (use resize_dimension)
316
- Fixed dimensions cannot be resized after creation
317
- Each variable can reference dimensions from its group or parent groups