Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for parametric vertical coordinate #528

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

jasonb5
Copy link

@jasonb5 jasonb5 commented Jul 18, 2024

Adds support for parametric vertical coordinates from CFConventions.

  • Update docs
  • Add name to CITATION.cff
  • Remove squeeze

cf_xarray/accessor.py Outdated Show resolved Hide resolved
cf_xarray/parametric.py Outdated Show resolved Hide resolved
cf_xarray/parametric.py Outdated Show resolved Hide resolved
return getattr(m, stdname)


def derive_dimension_order(output_order, **dim_map):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite clever, but I'd prefer if the user just did the transpose on their own after this decoding.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it fine that this would break away from the convention since it provides the output ordering in the definition?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes with Xarray we care much less about dimension order.

Copy link

@pochedls pochedls Jul 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasonb5 / @dcherian – thanks for all of your work on this. I wanted to weigh-in as a user. It would be unexpected to get a matrix of [time, lat, lon, *plev*] instead of [time, *plev*, lat, lon]. I think users would prefer the former (matches most climate data, CF conventions, and almost certainly the order of the original data, which is typically [time, *lev*, lat lon]).

If this doesn't match cf-xarray's style, xcdat could apply this functionality after calling cf-xarray's transform.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes xcdat could do .cf.transpose('T', 'Z', 'Y', 'X')

almost certainly the order of the original data, which is typically [time, lev, lat lon]).

Is this ordering not preserved here?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just did a quick check: it looks like it goes from (ds.ta.dims):

('time', 'lev', 'lat', 'lon')

to (p = ds.a * ds.p0 + ds.b * ds.ps; p.dims):

('lev', 'time', 'lat', 'lon')

In other words, the dataarray of interest is [time, lev, lat, lon], but the transform will put lev at the front [lev, time, lat, lon]

cf_xarray/parametric.py Outdated Show resolved Hide resolved
cf_xarray/parametric.py Outdated Show resolved Hide resolved
@dcherian
Copy link
Contributor

dcherian commented Jul 22, 2024

Thanks for taking this on! I've left a few high-level comments. Mostly I think it would be a lot clearer if we define a dataclass per transform that took care of the all the specialized logic: #528 (comment)

Copy link

codecov bot commented Jul 22, 2024

Codecov Report

Attention: Patch coverage is 92.69231% with 19 lines in your changes missing coverage. Please review.

Project coverage is 93.47%. Comparing base (a9cebee) to head (1c5d49f).
Report is 25 commits behind head on main.

Files Patch % Lines
cf_xarray/parametric.py 92.91% 18 Missing ⚠️
cf_xarray/accessor.py 83.33% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #528      +/-   ##
==========================================
+ Coverage   85.78%   93.47%   +7.68%     
==========================================
  Files          13       13              
  Lines        2364     2190     -174     
  Branches      183        0     -183     
==========================================
+ Hits         2028     2047      +19     
+ Misses        303      143     -160     
+ Partials       33        0      -33     
Flag Coverage Δ
mypy ?
unittests 93.47% <92.69%> (-0.52%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Comment on lines 713 to 719
z_shape = list(self.eta.shape)

z_shape.insert(1, self.sigma.shape[0])

z_dims = list(self.eta.dims)

z_dims.insert(1, self.sigma.dims[0])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
z_shape = list(self.eta.shape)
z_shape.insert(1, self.sigma.shape[0])
z_dims = list(self.eta.dims)
z_dims.insert(1, self.sigma.dims[0])
z_shape = (self.sigma.shape[0], *self.eta.shape)
z_dims = (self.sigma.dims[0], *self.eta.dims)

Comment on lines 721 to 729
z = xr.DataArray(np.empty(z_shape), dims=z_dims)

z_sigma = self.eta + self.sigma * (
np.minimum(self.depth_c, self.depth) + self.eta
)

z = xr.where(~np.isnan(self.sigma), z_sigma, z)

z = xr.where(np.isnan(self.sigma), self.zlev, z)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
z = xr.DataArray(np.empty(z_shape), dims=z_dims)
z_sigma = self.eta + self.sigma * (
np.minimum(self.depth_c, self.depth) + self.eta
)
z = xr.where(~np.isnan(self.sigma), z_sigma, z)
z = xr.where(np.isnan(self.sigma), self.zlev, z)
z_sigma = self.eta + self.sigma * (
np.minimum(self.depth_c, self.depth) + self.eta
)
z = xr.where(np.isnan(self.sigma), self.zlev, z_sigma)

Does this work?

@dcherian
Copy link
Contributor

ignore the 3.9 failures, I'll drop it soon.

cf_xarray/parametric.py Outdated Show resolved Hide resolved
cf_xarray/parametric.py Outdated Show resolved Hide resolved
cf_xarray/parametric.py Outdated Show resolved Hide resolved
cf_xarray/parametric.py Outdated Show resolved Hide resolved
cf_xarray/parametric.py Outdated Show resolved Hide resolved
@dcherian
Copy link
Contributor

Thanks! This is getting close. Just some style nits.

I haven't looked at the tests but I'm assuming you have it covered with some real life usage too? :)

@dcherian
Copy link
Contributor

dcherian commented Aug 21, 2024

Please also add your name to CITATION.cff

EDIT: I added a checklist to the top.

f + (self.sigma - 1) * (self.depth - f),
)

return z.squeeze().assign_attrs(standard_name=self.computed_standard_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return z.squeeze().assign_attrs(standard_name=self.computed_standard_name)
return z.assign_attrs(standard_name=self.computed_standard_name)

Can we delete all the squeeze too please

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dcherian Just want to check, if I remove the squeeze we'll end up with placeholder dimensions e.g. (lev: 3, dim_0: 1, lat: 2, lon: 2). If that's fine then I'll go ahead and remove the call.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where is the dim_0 being created?



def test_ocean_double_sigma_coordinate():
k_c = xr.DataArray(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't these be scalars? How are they stored in the netCDF files you're looking at?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants