NetCDF test fixtures#
The NetCDF test suite is driven by a curated set of small .nc files under tests/data/netcdf/ (with parallel
copies under examples/data/netcdf/ for the docs notebooks). Each fixture is named so its structure — and, where it
matters, its CRS and Y-axis orientation — is legible from the filename alone.
Naming convention#
| Segment | Meaning |
|---|---|
convention |
CF-conventions family: cf, coards, none (no Conventions attr), ugrid |
<Nv>v |
total variable count — including coordinate variables |
rank-breakdown |
count of variables at each dimensionality, e.g. 1d3-3d1 = 3 one-D + 1 three-D (sums to Nv) |
crs (optional) |
geos (geostationary), geog (geographic lat/lon), proj (projected, e.g. UTM) |
feature (optional) |
structural tags: curv (curvilinear), stag (staggered), str (string vars), nc4 (netCDF-4), groups-nc4 (has groups), scaleoffset (packed data) |
y-asc / y-desc (optional) |
Direction of the scaled Y coordinate (scale_factor/add_offset applied), not raw storage order — asc = row 0 is the south edge, desc = row 0 is the north edge. Only for files with a 1-D spatial Y axis. |
Fixtures#
| File | Conv. | Vars | Rank breakdown | CRS | Y-axis | nc4 | What it exercises |
|---|---|---|---|---|---|---|---|
cf__4v__1d3-3d1__proj__y-desc.nc |
CF-1.6 | 4 | 3×1D, 1×3D | projected | desc | ✓ | Projected (UTM) grid, north-up; pyramids-written — projected-descending orientation case |
cf__5v__1d4-3d1__geog__y-desc.nc |
CF-1.7 | 5 | 4×1D, 1×3D | geographic | desc | ✓ | ERA5 t2m(time,lat,lon); geographic-descending orientation case |
cf__5v__1d4-4d1__geog__y-desc.nc |
CF-1.6 | 5 | 4×1D, 1×4D | geographic | desc | ✓ | ERA5 pressure levels — 4-D (time,level,lat,lon), geographic-descending |
cf__5v__1d4-4d1__y-asc.nc |
CF-1.6 | 5 | 4×1D, 1×4D | geographic | asc | ✓ | pyramids-written 4-D cube (round-trip) |
cf__6v__1d2-2d4__geog__y-asc.nc |
CF-1.5 | 6 | 2×1D, 4×2D | geographic | asc | NOAH precipitation, four Band(lat,lon) vars; geographic-ascending orientation case (flipped on read) |
|
cf__7v__1d3-2d3-3d1__y-asc.nc |
CF-1.0 | 7 | 3×1D, 3×2D, 1×3D | geographic | asc | Single CF data var tos(time,lat,lon) + coords/bounds |
|
cf__8v__1d3-2d3-3d1-4d1__curv-stag.nc |
CF-1.4 | 8 | 3×1D, 3×2D, 1×3D, 1×4D | geographic | — | ✓ | Curvilinear + staggered grid |
cf__9v__1d7-2d2__geos__y-desc.nc |
CF-1.7 | 9 | 7×1D, 2×2D | geostationary | desc | ✓ | GOES-16 ABI, int16-packed radian scan-angle Y (raw ascends) — geostationary-descending; the #705 fixture |
cf__12v__1d4-2d5-3d2-4d1__y-asc.nc |
CF-1.0 | 12 | 4×1D, 5×2D, 2×3D, 1×4D | geographic | asc | Mix of all dimensionalities (CCSM sample): 3-D pr/tas, 4-D ua |
|
cf__20v__1d3-3d17__y-desc.nc |
CF-1.0 | 20 | 3×1D, 17×3D | geographic | desc | ✓ | Many 3-D vars (17 packed surface fields), ERA-40 subset |
cf__40v__1d28-2d9-3d3__nc4.nc |
CF-1.6 | 40 | 28×1D, 9×2D, 3×3D | geographic | — | ✓ | Large variable count, netCDF-4, no plain spatial Y |
cf__48v__1d17-3d21-4d10__y-asc.nc |
CF-1.0 | 48 | 17×1D, 21×3D, 10×4D | geographic | asc | ✓ | CAM init — 10 (time,lev,lat,lon) 4-D + 21 3-D |
coards__4v__1d2-2d2__scaleoffset__y-asc.nc |
COARDS/CF | 4 | 2×1D, 2×2D | geographic | asc | Two vars with scale_factor/add_offset — packed-data (unpack=True) reads |
|
coards__4v__1d3-3d1__y-desc.nc |
COARDS | 4 | 3×1D, 1×3D | geographic | desc | ✓ | COARDS air(time,lat,lon), int16-packed; geographic-descending orientation case |
coards__5v__1d4-4d1__y-desc.nc |
COARDS | 5 | 4×1D, 1×4D | geographic | desc | ✓ | Single 4-D var rhum(time,level,lat,lon), multi-level |
none__1v__1d1.nc |
none | 1 | 1×1D | none | — | Bare single 1-D variable | |
none__4v__1d1-2d2-3d1__curv.nc |
none | 4 | 1×1D, 2×2D, 1×3D | geographic | — | ✓ | Curvilinear grid (RASM-like), 2-D coordinates |
none__4v__1d3-3d1__geog__y-asc.nc |
none | 4 | 3×1D, 1×3D | geographic | asc | ✓ | MSWEP precip, geographic-ascending, no Conventions attr |
none__5v__1d2-2d2-3d1__curv.nc |
none | 5 | 2×1D, 2×2D, 1×3D | geographic | — | ✓ | Curvilinear grid (ROMS-like) |
none__11v__1d11.nc |
none | 11 | 11×1D | none | — | Multiple 1-D vars — aircraft track time series (no spatial Y axis) | |
none__17v__1d1-2d5-3d6-4d5__stag-str.nc |
none | 17 | 1×1D, 5×2D, 6×3D, 5×4D | geographic | — | ✓ | Staggered grid + string variables |
none__35v__1d35__groups-nc4.nc |
none | 35 | 35×1D | none | — | ✓ | 7 groups, netCDF-4 — group traversal |
none__111v__1d96-2d13-3d2__str.nc |
(AWIPS) | 111 | 96×1D, 13×2D, 2×3D | geographic | — | Many 1-D station-obs vars + char 2-D fields (no spatial Y axis) | |
ugrid__1v__1d1.nc |
none | 1 | 1×1D | none | — | UGRID unstructured mesh — single var | |
ugrid__1v__3d1.nc |
none | 1 | 1×3D | geographic | — | ✓ | UGRID mesh — 3-D var |
ugrid__6v__1d5-2d1.nc |
MPAS | 6 | 5×1D, 1×2D | geographic | — | ✓ | MPAS-convention unstructured mesh |
"Vars" and the rank breakdown count coordinate variables. "Y-axis" is blank for curvilinear (2-D coordinate) files,
mesh files, and files with no spatial Y axis, where a single ascending/descending direction is not meaningful. The
"CRS" column is the detected CRS pyramids resolves (most lat/lon CF/COARDS grids read as geographic,
EPSG:4326); the filename crs tag is applied only to the files a test cares about (geos for #705, geog/proj
for the orientation 2×2), so a file can be geographic here without a geog tag in its name.
The Y-orientation matrix#
tests/netcdf/spatial/test_y_orientation.py::TestOrientationAllCases verifies the interaction of
CRS type × Y-direction. One rule decides every cell: read the Y coordinate with its scale_factor /
add_offset applied, then flip iff it ascends (row 0 = south). The CRS never enters the decision — which is
the point of the projected row, since GDAL's classic driver only auto-flips a recognised geographic latitude, not
a projected projection_y_coordinate.
Both projected cells are covered by UTM grids generated at runtime (the projected_ascending_nc /
projected_descending_nc fixtures, written with WRITE_BOTTOMUP=YES / NO) rather than by an on-disk file:
| ascending (→ flip) | descending (→ keep) | |
|---|---|---|
| geostationary | (no known producer — see below) | …__geos__y-desc (GOES) |
| projected | projected_ascending_nc (runtime UTM) |
projected_descending_nc (runtime UTM) |
| geographic | …__geog__y-asc (NOAH, MSWEP) |
…__geog__y-desc (ERA5), coards…__y-desc |
The GOES granule is the reason the y-asc / y-desc tag names the scaled direction, not the storage order.
Its y is int16 packed with a negative scale_factor, so the raw values ascend (0 → 499) while the
physical scan angle descends (0.0420 → 0.0140). It is the only fixture where the two disagree, and reading the
tag off the raw order is exactly what mirrored the raster in #705. A geostationary file with a genuinely
ascending scan angle is not something any known producer writes, so that cell stays empty; the rule handles it
regardless, since nothing about it is geostationary-specific.
Matching on-disk fixtures that carry the same orientation but are not used by this parametrized test:
cf__4v__1d3-3d1__proj__y-desc.nc (projected-descending) and none__4v__1d3-3d1__geog__y-asc.nc
(MSWEP, geographic-ascending).
Related tests#
tests/netcdf/spatial/test_y_orientation.py— Y-axis orientation across the matrix above: the flip decision, a north-up geotransform, and byte-identity with a reference array reordered so row 0 sits at the largest scaled Y coordinate.tests/netcdf/spatial/test_windowed_read_705.py— the windowed-read crash on a Y-reversed multidim view (#705) and the eager-materialize path that fixes it (usescf__9v__1d7-2d2__geos__y-desc.nc).tests/netcdf/samples/— structural coverage (conventions, dimensions, groups, curvilinear, string vars) over the full fixture set.
Provenance#
The original source filenames (e.g. tos_O1_2001-2002.nc, air_temperature.nc, a GOES-16 ABI granule) and their
download/build steps are recorded in tests/data/netcdf/README.md.