clawhdf5-netcdf4: phony dimensions, skipped types and order as netCDF-C
CI / test-arm64 (pull_request) Successful in 1m43s
CI / test (pull_request) Successful in 20m28s

Read a file's metadata the way netCDF-C 4.9.3 does (libhdf5/hdf5open.c),
for the whole file on first use (src/model.rs, replacing src/scope.rs):

- links in creation order when the group tracks it, else name order;
  a group's datasets before its subgroups; dimension ids file-wide;
- variables' dimensions from _Netcdf4Coordinates (file-wide ids), else
  the scales DIMENSION_LIST attaches when the first axis has one, else
  netCDF-C's phony dimensions phony_dim_<id> (create_phony_dims: shared
  by length and unlimitedness within a group, not between two axes of
  one variable, numbered subgroups first, a zero length unlimited);
- datasets of types netCDF-C cannot represent are not variables
  (references, bit fields, time, arrays, compounds/enums/VLENs over
  them), replaying netCDF-C's file-wide type list, failed types
  included;
- unlimited lengths as nc4_find_dim_len (its group and below).

NcType gains Enum, Compound, VLen, Opaque and is #[non_exhaustive];
Variable::nc_type is netCDF-C's type (1-byte strings NC_CHAR). New
clawhdf5_format::group_v2::links_in_creation_order_in.

Tests compare with netCDF-C itself (tests/netcdf_c_view.py calls the
libnetcdf netCDF4-python bundles through ctypes): new interop cases for
h5py files without dimension scales, every type class, link order; and
the gated corpus_vs_netcdf_c (CLAWHDF5_NETCDF_CORPUS): 420 of the 429
conformance-corpus files netCDF-C opens match (main: 68); the other 9
are explained in tests/corpus_known_differences.txt and known-issues.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
osobh
2026-09-29 20:38:51 -05:00
co-authored by Claude Opus 5.5
parent 4260af4f70
commit e5d6f59e12
18 changed files with 2263 additions and 524 deletions
+54
View File
@@ -2,6 +2,60 @@
## Unreleased
### NetCDF-4: phony dimensions, skipped types and order as in netCDF-C (2026-09-29)
- `clawhdf5-netcdf4` now reads a file's metadata as netCDF-C 4.9.3 does
(`libhdf5/hdf5open.c`), for the whole file on first use
(`src/model.rs`): links in creation order when the group tracks it,
else in name order; a group's datasets before its subgroups; dimension
ids file-wide (`_Netcdf4Dimid`, else the next free id); then variables'
dimensions, subgroups first: `_Netcdf4Coordinates` ids (looked up
file-wide), else the scales `DIMENSION_LIST` attaches (when the first
axis has one), else phony dimensions.
- **Files without dimension scales** get netCDF-C's phony dimensions,
`phony_dim_<id>` (`create_phony_dims`): per axis, the first dimension of
the variable's group of the same length and unlimitedness not used by an
earlier axis of the variable (real dimensions included), else a new one;
a length of 0 is always unlimited. They used to be one dimension per 1-D
dataset, named after it, and `dim_<size>` for other axes.
- **Datasets netCDF-C skips are not variables:** references, bit fields,
time and array types, and compounds, enums and VLENs whose members or
base type are not netCDF atomic types (4- and 8-byte floats only) or a
type read before — replaying netCDF-C's file-wide type list, which also
keeps a type it failed to read, so the second dataset of a compound with
a reference member is a variable, as in netCDF-C.
- `NcType` gains `Enum`, `Compound`, `VLen` and `Opaque` and is now
`#[non_exhaustive]` (breaking for exhaustive matches);
`Variable::nc_type` is the type netCDF-C gives the variable: a 1-byte
fixed-length string is `Char`, a longer one `String` (both were
`String`); user-defined types have their class (they were `Char`).
`dtype_to_nctype` maps compounds and enums to their classes.
- Groups (`group_names`), variables (`variables`, `variable_names`) and
dimensions (`dimensions`, by id) come in netCDF-C's order; a zero-length
dimension scale is unlimited; an unlimited dimension's length is the
longest extent along it of the variables in its group and below
(`nc4_find_dim_len`; was: of the variables its `REFERENCE_LIST` names).
`NetCDF4File::group` and `NetCDF4Group::group` take a path (`"a/b"`).
- Deliberate differences: floats of other than 4 or 8 bytes are
`Float`/`Double` (netCDF-C 4.9.3 on libhdf5 1.14.6 labels them
`NC_STRING`); an axis netCDF-C leaves without a dimension (where it
reads uninitialised memory) gets one by the phony rule.
- New `clawhdf5_format::group_v2::links_in_creation_order_in`: a group's
link names in creation order, or `None` when it does not track it.
- Tests compare with netCDF-C itself (`tests/netcdf_c_view.py` calls the
libnetcdf netCDF4-python bundles through ctypes, since netCDF4-python
hides variables of types it does not support): `interop_tests` cases of
h5py files without dimension scales (sharing, unlimited and zero-length
axes, subgroups, a real dimension taken by length), of every HDF5 type
class, of creation and name order in compact and dense groups, and a
netCDF4-python file; and the gated `tests/corpus_vs_netcdf_c.rs`
(`CLAWHDF5_NETCDF_CORPUS=<dir>`): over the conformance corpus 420 of the
429 files netCDF-C 4.9.3 opens match in groups, dimensions, variables,
types, shapes and numeric values (up to 5000 elements); `main` at
`4260af4` matched 68. The other 9 (external links, values the HDF5
reader refuses) are explained in `tests/corpus_known_differences.txt`
and `docs/known-issues.md` (tank, 2026-09-29, netCDF4-python 1.7.4).
Affected v2.1.0 to v2.7.0.
### NetCDF-4: variables' dimensions come from the file (2026-09-28)
- `clawhdf5-netcdf4` gave each variable the first unused dimension of
equal size (else an anonymous `dim_<n>`), so a variable on an unlimited