clawhdf5-netcdf4: an unlimited dimension reports its current length
Dimension::size of an unlimited dimension was its dimension scale's extent, which netCDF-C leaves at 0, so it read 0 for a dimension holding records. It is now what netCDF-C reports (nc4_find_dim_len): the largest current extent of the variables using it in any group, found through the scale's REFERENCE_LIST, a coordinate variable's own extent included; 0 when nothing has been written. interop_tests::unlimited_dimension_lengths_match_netcdf4_python compares with netCDF4-python (variables of different lengths, one in a subgroup, an unwritten dimension, a coordinate variable shorter than another variable on its dimension, a subgroup's own dimension); before the fix it got time 0/6, rec 3/5, srec 0/1. The known-issues entry moves to Fixed; the crate README's warning goes. A new open entry records a related bug found meanwhile: variables' dimensions are matched by size, not DIMENSION_LIST. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -377,3 +377,87 @@ ds.close()
|
||||
let names = file.variable("name").unwrap().read_string().unwrap();
|
||||
assert_eq!(names, vec!["Oslo", "", "São Paulo", "x"]);
|
||||
}
|
||||
|
||||
// ===========================================================================
|
||||
// Unlimited dimensions: the length netCDF-C reports
|
||||
// ===========================================================================
|
||||
|
||||
/// An unlimited dimension's length is the largest extent of the variables
|
||||
/// using it, in any group (netCDF-C's `nc4_find_dim_len`), not its dimension
|
||||
/// scale's extent (which netCDF-C leaves at 0): variables of different
|
||||
/// lengths, one in a subgroup, a dimension no variable has written, a
|
||||
/// coordinate variable, a subgroup's own unlimited dimension. Compared with
|
||||
/// what netCDF4-python reports for the same file.
|
||||
#[test]
|
||||
fn unlimited_dimension_lengths_match_netcdf4_python() {
|
||||
skip_if_no_netcdf4!();
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let path = dir.path().join("unlimited.nc");
|
||||
let script = format!(
|
||||
r#"
|
||||
import netCDF4 as nc
|
||||
import numpy as np
|
||||
with nc.Dataset({path:?}, "w", format="NETCDF4") as f:
|
||||
f.createDimension("time", None)
|
||||
f.createDimension("empty", None)
|
||||
f.createDimension("rec", None)
|
||||
f.createDimension("x", 3)
|
||||
f.createVariable("t", "f8", ("time", "x"))[0:2, :] = np.ones((2, 3))
|
||||
f.createVariable("a", "i4", ("time",))[0:4] = np.arange(4)
|
||||
f.createVariable("e", "i4", ("empty",))
|
||||
f.createVariable("rec", "f4", ("rec",))[0:3] = [1, 2, 3]
|
||||
f.createVariable("r", "f4", ("x", "rec"))[:, 0:5] = np.ones((3, 5))
|
||||
g = f.createGroup("sub")
|
||||
g.createVariable("c", "i4", ("time",))[0:6] = np.arange(6)
|
||||
g.createDimension("srec", None)
|
||||
g.createVariable("s", "i4", ("srec", "x"))[0:1, :] = np.ones((1, 3))
|
||||
with nc.Dataset({path:?}) as f:
|
||||
for grp in (f, f.groups["sub"]):
|
||||
for name, d in grp.dimensions.items():
|
||||
print(grp.path, name, len(d), d.isunlimited())
|
||||
"#,
|
||||
path = path.display().to_string()
|
||||
);
|
||||
let out = Command::new(python())
|
||||
.args(["-c", &script])
|
||||
.output()
|
||||
.expect("failed to run python3");
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"{}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
let expected: Vec<String> = String::from_utf8(out.stdout)
|
||||
.unwrap()
|
||||
.lines()
|
||||
.map(str::to_string)
|
||||
.collect();
|
||||
// time: t has 2 records, a 4 and sub/c 6; rec: the coordinate variable
|
||||
// has 3, r 5; empty: nothing written.
|
||||
for want in [
|
||||
"/ time 6 True",
|
||||
"/ empty 0 True",
|
||||
"/ rec 5 True",
|
||||
"/ x 3 False",
|
||||
"/sub srec 1 True",
|
||||
] {
|
||||
assert!(
|
||||
expected.iter().any(|l| l == want),
|
||||
"netCDF4 reports {expected:?}"
|
||||
);
|
||||
}
|
||||
|
||||
let file = NetCDF4File::open(&path).unwrap();
|
||||
let sub = file.group("sub").unwrap();
|
||||
let mut got = Vec::new();
|
||||
for (path, dims) in [
|
||||
("/", file.dimensions().unwrap()),
|
||||
("/sub", sub.dimensions().unwrap()),
|
||||
] {
|
||||
for d in dims {
|
||||
let unlimited = if d.is_unlimited { "True" } else { "False" };
|
||||
got.push(format!("{path} {} {} {unlimited}", d.name, d.size));
|
||||
}
|
||||
}
|
||||
assert_eq!(got, expected);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user