Skip to content

Commit a15e80c

Browse files
authored
infer dimension ordering within nc_specs using cf-xarray (#533)
<!-- Please ensure the PR fulfills the following requirements! --> <!-- If this is your first PR, make sure to add your details to the AUTHORS.rst! --> ### Pull Request Checklist: - [ ] This PR addresses an already opened issue (for bug fixes / features) - This PR fixes #xyz - [ ] (If applicable) Documentation has been added / updated (for bug fixes / features). - [ ] (If applicable) Tests have been added. - [ ] CHANGELOG.rst has been updated (with summary of main changes). - [ ] Link to issue (:issue:`number`) and pull request (:pull:`number`) has been added. ### What kind of change does this PR introduce? * ... ### Does this PR introduce a breaking change? ### Other information:
2 parents cb252d6 + fcbcfdb commit a15e80c

File tree

4 files changed

+59
-4
lines changed

4 files changed

+59
-4
lines changed

CHANGELOG.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ Changelog
66
`Unreleased <https://github.com/CSHS-CWRA/RavenPy>`_ (latest)
77
-------------------------------------------------------------
88
9-
Contributors:
9+
Contributors: David Huard (:user:`huard`).
1010

1111
Changes
1212
^^^^^^^
1313
* No change.
1414

1515
Fixes
1616
^^^^^
17-
* No change.
17+
* In `nc_specs`, set `dim_names_nc` in the order expected by Raven (x, y, t). Previously, we only made sure that `time` was the last dimension, but did not ensure x and y were in the right order. (PR #533)
18+
1819

1920
.. _changes_0.19.1:
2021

src/ravenpy/config/commands.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -853,7 +853,12 @@ class ReadFromNetCDF(FlatCommand):
853853
@field_validator("dim_names_nc")
854854
@classmethod
855855
def reorder_time(cls, v):
856-
"""TODO: Return dimensions as x, y, t. Currently only puts time at the end."""
856+
"""
857+
Return dimensions as x, y, t.
858+
859+
This is a fail safe because if input files are CF-compliant, dimensions should already
860+
have been ordered by `nc_specs`.
861+
"""
857862
dims = list(v)
858863
for time_dim in ("t", "time"):
859864
if time_dim in dims:

src/ravenpy/config/utils.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def nc_specs(
7878
if v in ds.data_vars:
7979
nc_var = ds[v]
8080
attrs["var_name_nc"] = v
81-
attrs["dim_names_nc"] = nc_var.dims
81+
attrs["dim_names_nc"] = infer_dim_names(nc_var)
8282
attrs["_time_dim_name_nc"] = ds.cf["time"].name
8383
attrs["_dim_size_nc"] = dict(zip(nc_var.dims, nc_var.shape))
8484
attrs["units"] = nc_var.attrs.get("units")
@@ -185,3 +185,34 @@ def get_annotations(a):
185185
yield from get_annotations(arg)
186186
else:
187187
yield arg
188+
189+
190+
def infer_dim_names(da: xr.DataArray) -> tuple:
191+
"""
192+
Return names of dimensions in dataset in order expected by Raven.
193+
194+
If 3D, return X, Y, T axes names if they can be inferred from CF conventions.
195+
If 2D, return STATION, T
196+
"""
197+
try:
198+
if da.ndim == 1:
199+
dims = da.cf.axes["T"]
200+
if len(dims) != 1:
201+
raise ValueError("Should have exactly 1 dimension.")
202+
203+
elif da.ndim == 2:
204+
dims = list(da.dims)
205+
tdim = da.cf.axes["T"][0]
206+
dims.remove(tdim)
207+
dims.append(tdim)
208+
209+
elif da.ndim == 3:
210+
dims = da.cf.axes["X"] + da.cf.axes["Y"] + da.cf.axes["T"]
211+
if len(dims) != 3:
212+
raise ValueError("Should have exactly 3 dimensions.")
213+
214+
except:
215+
# In case CF inference fails, return the original dims.
216+
return da.dims
217+
218+
return tuple(dims)

tests/test_utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@ def test_nc_specs(yangtze):
1414
f = yangtze.fetch("raven-gr4j-cemaneige/Salmon-River-Near-Prince-George_meteo_daily.nc")
1515
attrs = nc_specs(f, "PRECIP", station_idx=1, alt_names=("rain",))
1616
assert "file_name_nc" in attrs
17+
assert attrs["dim_names_nc"] == ("time",)
18+
19+
# 2D with station dimension
20+
f = get_local_testdata("raven-gr4j-cemaneige/Salmon-River-Near-Prince-George_meteo_daily_2d.nc")
21+
attrs = nc_specs(f, "PRECIP", station_idx=1, alt_names=("rain",))
22+
assert attrs["dim_names_nc"] == (
23+
"region",
24+
"time",
25+
)
26+
27+
# 3D - Since this file is not CF compliant, nc_specs cannot infer the correct dimension order
28+
f = get_local_testdata("raven-gr4j-cemaneige/Salmon-River-Near-Prince-George_meteo_daily_3d.nc")
29+
attrs = nc_specs(f, "PRECIP", station_idx=1, alt_names=("rain",))
30+
assert attrs["dim_names_nc"] == ("time", "lon", "lat")
31+
32+
f = get_local_testdata("cmip5/tas_Amon_CanESM2_rcp85_r1i1p1_200601-210012_subset.nc")
33+
attrs = nc_specs(f, "TEMP_AVE", station_idx=1, engine="netcdf4")
34+
assert attrs["dim_names_nc"] == ("lon", "lat", "time")
1735

1836

1937
def test_nc_specs_bad(bad_netcdf):

0 commit comments

Comments
 (0)