-
Notifications
You must be signed in to change notification settings - Fork 16
Support multidimensional coordinates for bounds and regridding #820
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
70c6253
717df67
00635c2
048a07a
bc35ab9
6399d55
d13a72a
f927226
24d2c25
eb9cc42
37cc657
e0d344c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,16 +5,17 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||
| from xcdat.axis import CFAxisKey, get_coords_by_name, get_dim_coords | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| from xcdat.bounds import create_bounds | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| from xcdat.regridder import regrid2, xesmf, xgcm | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| from xcdat.regridder.base import BaseRegridder | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| from xcdat.regridder.grid import _validate_grid_has_single_axis_dim | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| HorizontalRegridTools = Literal["xesmf", "regrid2"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| HORIZONTAL_REGRID_TOOLS = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| HORIZONTAL_REGRID_TOOLS: dict[str, type[BaseRegridder]] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "regrid2": regrid2.Regrid2Regridder, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "xesmf": xesmf.XESMFRegridder, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| VerticalRegridTools = Literal["xgcm"] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| VERTICAL_REGRID_TOOLS = {"xgcm": xgcm.XGCMRegridder} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| VERTICAL_REGRID_TOOLS: dict[str, type[BaseRegridder]] = {"xgcm": xgcm.XGCMRegridder} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| @xr.register_dataset_accessor(name="regridder") | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -166,7 +167,9 @@ def horizontal( | |||||||||||||||||||||||||||||||||||||||||||||||||||
| f"Tool {e!s} does not exist, valid choices {list(HORIZONTAL_REGRID_TOOLS)}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) from e | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| input_grid = _get_input_grid(self._ds, data_var, ["X", "Y"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| input_grid = _get_input_grid( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._ds, data_var, ["X", "Y"], multidim=regrid_tool.can_handle_multidim() | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+170
to
+172
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Test needed.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added missing test. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| regridder = regrid_tool(input_grid, output_grid, **options) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| output_ds = regridder.horizontal(data_var, self._ds) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -236,20 +239,17 @@ def vertical( | |||||||||||||||||||||||||||||||||||||||||||||||||||
| f"Tool {e!s} does not exist, valid choices " | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| f"{list(VERTICAL_REGRID_TOOLS)}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) from e | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| input_grid = _get_input_grid( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| self._ds, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| data_var, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Z", | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| input_grid = _get_input_grid(self._ds, data_var, ["Z"]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| regridder = regrid_tool(input_grid, output_grid, **options) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| output_ds = regridder.vertical(data_var, self._ds) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| return output_ds | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _obj_to_grid_ds(obj: xr.Dataset | xr.DataArray) -> xr.Dataset: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _obj_to_grid_ds( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| obj: xr.Dataset | xr.DataArray, multidim: bool = False | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) -> xr.Dataset: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Convert an xarray object to a new Dataset containing axis coordinates and | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| bounds. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -304,14 +304,20 @@ def _obj_to_grid_ds(obj: xr.Dataset | xr.DataArray) -> xr.Dataset: | |||||||||||||||||||||||||||||||||||||||||||||||||||
| attrs=obj.attrs, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Multidimensional coordinates bounds generation is not supported | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if multidim: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return output_ds | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+307
to
+309
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think Copilot is referencing the logic before this conditional in xcdat/xcdat/regridder/accessor.py Lines 281 to 305 in d13a72a
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Add bounds only for axes that do not already have them. This | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # prevents multiple sets of bounds being added for the same axis. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # For example, curvilinear grids can have multiple coordinates for the | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # same axis (e.g., (nlat, lat) for X and (nlon, lon) for Y). We only | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # need lat_bnds and lon_bnds for the X and Y axes, respectively, and not | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # nlat_bnds and nlon_bnds. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| for axis, has_bounds in axis_has_bounds.items(): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if not has_bounds: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| # FIXME: Line 313 --Can't add bounds for multidimensional coordinates | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No longer relevant, will remove. |
||||||||||||||||||||||||||||||||||||||||||||||||||||
| output_ds = output_ds.bounds.add_bounds(axis=axis) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| return output_ds | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -347,7 +353,12 @@ def _get_axis_coord_and_bounds( | |||||||||||||||||||||||||||||||||||||||||||||||||||
| return coord_var, bounds_var | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _get_input_grid(ds: xr.Dataset, data_var: str, dup_check_dims: list[CFAxisKey]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| def _get_input_grid( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ds: xr.Dataset, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| data_var: str, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| dup_check_dims: list[CFAxisKey], | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| multidim: bool = False, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| Extract the grid from ``ds``. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -374,10 +385,12 @@ def _get_input_grid(ds: xr.Dataset, data_var: str, dup_check_dims: list[CFAxisKe | |||||||||||||||||||||||||||||||||||||||||||||||||||
| all_coords = set(ds.coords.keys()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| for dimension in dup_check_dims: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| coords = get_dim_coords(ds, dimension) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| coords = get_dim_coords(ds, dimension, multidim=multidim) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if isinstance(coords, xr.Dataset): | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| coord = set([get_dim_coords(ds[data_var], dimension).name]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| coord = set( | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| [get_dim_coords(ds[data_var], dimension, multidim=multidim).name] | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| dimension_coords = set(ds.cf[[dimension]].coords.keys()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -387,7 +400,7 @@ def _get_input_grid(ds: xr.Dataset, data_var: str, dup_check_dims: list[CFAxisKe | |||||||||||||||||||||||||||||||||||||||||||||||||||
| input_grid = ds.drop_dims(to_drop) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # drops extra dimensions on input grid | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| grid = input_grid.regridder.grid | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| grid = _obj_to_grid_ds(input_grid, multidim=multidim) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| # preserve mask on grid | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if "mask" in ds: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -95,6 +95,12 @@ def _drop_axis(ds: xr.Dataset, axis: list[CFAxisKey]) -> xr.Dataset: | |
| class BaseRegridder(abc.ABC): | ||
| """BaseRegridder.""" | ||
|
|
||
| supports_multidim: bool | ||
|
|
||
| @classmethod | ||
| def can_handle_multidim(cls) -> bool: | ||
| return cls.supports_multidim | ||
|
|
||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, defaulting to False. |
||
| def __init__(self, input_grid: xr.Dataset, output_grid: xr.Dataset, **options: Any): | ||
| self._input_grid = input_grid | ||
| self._output_grid = output_grid | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, this is more robust than only using
obj.cf.coordinates.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Combined
cf.coordinatesandcf.axes, should have complete coverage.