|
42 | 42 | "wo": "W", |
43 | 43 | } |
44 | 44 |
|
| 45 | +_COPERNICUS_MARINE_AXIS_VARNAMES = { |
| 46 | + "X": "lon", |
| 47 | + "Y": "lat", |
| 48 | + "Z": "depth", |
| 49 | + "T": "time", |
| 50 | +} |
| 51 | + |
45 | 52 |
|
46 | 53 | def _maybe_bring_UV_depths_to_depth(ds): |
47 | 54 | if "U" in ds.variables and "depthu" in ds.U.coords and "depth" in ds.coords: |
@@ -257,3 +264,67 @@ def nemo_to_sgrid(*, fields: dict[str, xr.Dataset | xr.DataArray], coords: xr.Da |
257 | 264 | # Update to use lon and lat for internal naming |
258 | 265 | ds = sgrid.rename(ds, {"gphif": "lat", "glamf": "lon"}) # TODO: Logging message about rename |
259 | 266 | return ds |
| 267 | + |
| 268 | + |
| 269 | +def copernicusmarine_to_sgrid( |
| 270 | + *, fields: dict[str, xr.Dataset | xr.DataArray], coords: xr.Dataset | None = None |
| 271 | +) -> xr.Dataset: |
| 272 | + """Create an sgrid-compliant xarray.Dataset from a dataset of Copernicus Marine netcdf files. |
| 273 | +
|
| 274 | + Parameters |
| 275 | + ---------- |
| 276 | + fields : dict[str, xr.Dataset | xr.DataArray] |
| 277 | + Dictionary of xarray.DataArray objects as obtained from a set of Copernicus Marine netcdf files. |
| 278 | + coords : xarray.Dataset, optional |
| 279 | + xarray.Dataset containing coordinate variables. By default these are time, depth, latitude, longitude |
| 280 | +
|
| 281 | + Returns |
| 282 | + ------- |
| 283 | + xarray.Dataset |
| 284 | + Dataset object following SGRID conventions to be (optionally) modified and passed to a FieldSet constructor. |
| 285 | +
|
| 286 | + Notes |
| 287 | + ----- |
| 288 | + See https://help.marine.copernicus.eu/en/collections/9080063-copernicus-marine-toolbox for more information on the copernicusmarine toolbox. |
| 289 | + The toolbox to ingest data from most of the products on the Copernicus Marine Service (https://data.marine.copernicus.eu/products) into an xarray.Dataset. |
| 290 | + You can use indexing and slicing to select a subset of the data before passing it to this function. |
| 291 | +
|
| 292 | + """ |
| 293 | + fields = fields.copy() |
| 294 | + |
| 295 | + for name, field_da in fields.items(): |
| 296 | + if isinstance(field_da, xr.Dataset): |
| 297 | + field_da = field_da[name] |
| 298 | + # TODO: logging message, warn if multiple fields are in this dataset |
| 299 | + else: |
| 300 | + field_da = field_da.rename(name) |
| 301 | + fields[name] = field_da |
| 302 | + |
| 303 | + ds = xr.merge(list(fields.values()) + ([coords] if coords is not None else [])) |
| 304 | + ds.attrs.clear() # Clear global attributes from the merging |
| 305 | + |
| 306 | + ds = _maybe_rename_coords(ds, _COPERNICUS_MARINE_AXIS_VARNAMES) |
| 307 | + if "W" in ds.data_vars: |
| 308 | + # Negate W to convert from up positive to down positive (as that's the direction of positive z) |
| 309 | + ds["W"].data *= -1 |
| 310 | + |
| 311 | + if "grid" in ds.cf.cf_roles: |
| 312 | + raise ValueError( |
| 313 | + "Dataset already has a 'grid' variable (according to cf_roles). Didn't expect there to be grid metadata on copernicusmarine datasets - please open an issue with more information about your dataset." |
| 314 | + ) |
| 315 | + ds["grid"] = xr.DataArray( |
| 316 | + 0, |
| 317 | + attrs=sgrid.Grid2DMetadata( # use dummy *_center dimensions - this is A grid data (all defined on nodes) |
| 318 | + cf_role="grid_topology", |
| 319 | + topology_dimension=2, |
| 320 | + node_dimensions=("lon", "lat"), |
| 321 | + node_coordinates=("lon", "lat"), |
| 322 | + face_dimensions=( |
| 323 | + sgrid.DimDimPadding("x_center", "lon", sgrid.Padding.LOW), |
| 324 | + sgrid.DimDimPadding("y_center", "lat", sgrid.Padding.LOW), |
| 325 | + ), |
| 326 | + vertical_dimensions=(sgrid.DimDimPadding("z_center", "depth", sgrid.Padding.LOW),), |
| 327 | + ).to_attrs(), |
| 328 | + ) |
| 329 | + |
| 330 | + return ds |
0 commit comments