Skip to content

Commit 1f46f52

Browse files
authored
Merge pull request #49 from NOC-MSM/46-add-support-for-3-dimensional-depth-coordinates
46 add support for 3 dimensional depth coordinates
2 parents 19a6aa9 + 758cde2 commit 1f46f52

12 files changed

Lines changed: 685 additions & 574 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ If you already use `xarray`, NEMO Cookbook should feel immediately natural:
3939
* `NEMODataArray` behaves like `xarray.DataArray`.
4040
* All standard `xarray` operations are still available!
4141

42-
What’s new is that these objects understand the NEMO grid, meaning you no longer need to manually track:
42+
What’s new is that these objects understand the NEMO grid, meaning you no longer need to manually track:
4343

4444
* which NEMO model grid a variable belongs to (e.g., T, U, V, F, W).
4545
* how variables relate across NEMO model grids.

nemo_cookbook/extract.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,9 @@ def create_section_polygon(
6868
[lat_sec, np.array([lat_sec[-1] - dlat, lat_sec[-1] - dlat, lat_sec[0]])]
6969
)
7070
elif all(lat == lat_sec[0] for lat in lat_sec):
71-
raise ValueError("extracting zonal hydrographic sections is not supported.")
71+
raise ValueError("extracting zonal hydrographic sections is not supported. Use NEMODataTree.extract_zonal_section() instead.")
7272
else:
73-
raise ValueError("hydrographic section endpoints must not have same latitude.")
73+
raise ValueError("hydrographic section endpoints must not have same latitude to form a closed polygon.")
7474

7575
return lon_poly, lat_poly
7676

@@ -154,8 +154,19 @@ def create_boundary_dataset(
154154
ds_bdy[f"{dom_prefix}gphib"][vbdy_mask] = nemo[gridV][f"{dom_prefix}gphiv"].sel(
155155
i=ds_bdy["i_bdy"][vbdy_mask], j=ds_bdy["j_bdy"][vbdy_mask]
156156
)
157-
ds_bdy[f"{dom_prefix}depthb"][:, ubdy_mask] = nemo[gridU][f"{dom_prefix}depthu"]
158-
ds_bdy[f"{dom_prefix}depthb"][:, vbdy_mask] = nemo[gridV][f"{dom_prefix}depthv"]
157+
158+
if (nemo[gridU][f"{dom_prefix}depthu"].ndim == 1) and (nemo[gridV][f"{dom_prefix}depthv"].ndim == 1):
159+
# Using 1-dimensional vertical reference (depth) coordinate:
160+
ds_bdy[f"{dom_prefix}depthb"][:, ubdy_mask] = nemo[gridU][f"{dom_prefix}depthu"]
161+
ds_bdy[f"{dom_prefix}depthb"][:, vbdy_mask] = nemo[gridV][f"{dom_prefix}depthv"]
162+
else:
163+
# Using 3-dimensional vertical reference (depth) coordinate:
164+
ds_bdy[f"{dom_prefix}depthb"][:, ubdy_mask] = nemo[gridU][f"{dom_prefix}depthu"].sel(
165+
i=ds_bdy["i_bdy"][ubdy_mask], j=ds_bdy["j_bdy"][ubdy_mask]
166+
)
167+
ds_bdy[f"{dom_prefix}depthb"][:, vbdy_mask] = nemo[gridV][f"{dom_prefix}depthv"].sel(
168+
i=ds_bdy["i_bdy"][vbdy_mask], j=ds_bdy["j_bdy"][vbdy_mask]
169+
)
159170

160171
return ds_bdy
161172

nemo_cookbook/nemodataarray.py

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -882,8 +882,17 @@ def depth_integral(
882882
)
883883

884884
# -- Define input variables -- #
885-
var_in = self.masked.data
886-
e3_in = self.metrics["e3"].masked.data
885+
var_in = (self
886+
.masked
887+
.data
888+
.chunk({self.k_name: -1})
889+
)
890+
891+
e3_in = (self.metrics["e3"]
892+
.masked
893+
.data
894+
.chunk({self.k_name: -1})
895+
)
887896

888897
# -- Vertically integrate w.r.t depth -- #
889898
result = xr.apply_ufunc(
@@ -1150,14 +1159,23 @@ def transform_vertical_grid(
11501159
)
11511160

11521161
# -- Define input variables -- #
1153-
var_in = self.masked.data
1154-
e3_in = self.metrics["e3"].masked.data
1162+
var_in = (self
1163+
.masked
1164+
.data
1165+
.chunk({self.k_name: -1})
1166+
)
1167+
1168+
e3_in = (self.metrics["e3"]
1169+
.masked
1170+
.data
1171+
.chunk({self.k_name: -1})
1172+
)
11551173

11561174
# Ensure total depth of new vertical grid >= total depth of NEMO model vertical grid:
1157-
depth_max = self._tree[self._grid][f"{self._dom_prefix}depth{self._grid_suffix}"].max(self.k_name)
1175+
depth_max = self._tree[self._grid][f"{self._dom_prefix}depth{self._grid_suffix}"].max()
11581176
if e3_new.sum(dim="k_new") < depth_max:
11591177
raise ValueError(
1160-
f"e3_new must sum to at least the maximum depth ({depth_max.item()} m) of the original vertical grid."
1178+
f"e3_new must sum to at least the maximum depth ({depth_max.values.item()} m) of the original vertical grid."
11611179
)
11621180

11631181
# -- Transform variable to target vertical grid -- #
@@ -1183,7 +1201,8 @@ def transform_vertical_grid(
11831201
result = xr.Dataset(
11841202
data_vars={self.name: var_out, f"e3{self._grid_suffix}_new": e3_out},
11851203
coords={
1186-
f"depth{self._grid_suffix}_new": ("k_new", e3_new.cumsum(dim="k_new").data)
1204+
# Vertical reference (depth of cell center) coordinates of new vertical grid:
1205+
f"depth{self._grid_suffix}_new": ("k_new", (e3_new.cumsum(dim="k_new") - (e3_new / 2)).data),
11871206
},
11881207
)
11891208

nemo_cookbook/nemodatatree.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@ def from_paths(
7171
nftype: str | None = None,
7272
read_mask: bool = False,
7373
maskcs: bool = False,
74-
key_linssh: bool = False,
74+
linssh: bool = False,
75+
vco: str = "1d",
7576
vco_ref: bool = False,
7677
nbghost_child: int = 4,
7778
**open_kwargs: dict[str, any],
@@ -139,10 +140,13 @@ def from_paths(
139140
maskcs: bool = False
140141
If True, all closed seas are masked using mask_opensea variables from domain files. Default is False.
141142
142-
key_linssh: bool = False
143+
linssh: bool = False
143144
Linear free-surface approximation. If True, vertical coordinates are time-independent and given by (e3t_0, e3u_0, e3v_0, e3w_0) in domain_cfg.
144145
If False, vertical coordinates are time-dependent and must be specified in NEMO model grid datasets. Default is False.
145146
147+
vco : str = "1d"
148+
Vertical reference variables. Options are '1d' to use 1-dimensional vertical reference coordinates or '3d' to use 3-dimensional vertical reference coordinates (deptht, depthu, depthv, depthw, depthf). Default is '1d'.
149+
146150
vco_ref: bool = False
147151
If True, add reference vertical scale factors and compute reference water column heights from domain files. Default is False.
148152
@@ -173,7 +177,7 @@ def from_paths(
173177
174178
Create a regional `NEMODataTree` using a linear free-surface approximation from a dictionary of paths to remote netCDF files:
175179
176-
>>> nemo = NEMODataTree.from_paths(paths, name="My NEMO model", iperio=False, nftype=None, key_linssh=True)
180+
>>> nemo = NEMODataTree.from_paths(paths, name="My NEMO model", iperio=False, nftype=None, linssh=True)
177181
178182
See Also
179183
--------
@@ -196,8 +200,14 @@ def from_paths(
196200
raise TypeError("reading land-sea masks from domain_cfg (`read_mask`) must be a boolean.")
197201
if not isinstance(maskcs, bool):
198202
raise TypeError("masking of closed seas (`maskcs`) must be a boolean.")
199-
if not isinstance(key_linssh, bool):
200-
raise TypeError("linear free-surface approximation (`key_linssh`) must be a boolean.")
203+
if not isinstance(linssh, bool):
204+
raise TypeError("linear free-surface approximation (`linssh`) must be a boolean.")
205+
if not isinstance(vco, str):
206+
raise TypeError("vertical reference coordinates (`vco`) must be a string.")
207+
if vco not in ("1d", "3d"):
208+
raise ValueError(
209+
"vertical reference coordinates (`vco`) must be '1d' (1-dimensional) or '3d' (3-dimensional)."
210+
)
201211
if not isinstance(vco_ref, bool):
202212
raise TypeError("reference vertical coordinates (`vco_ref`) must be a boolean.")
203213
if not isinstance(nbghost_child, int):
@@ -240,7 +250,8 @@ def from_paths(
240250
read_mask=read_mask,
241251
maskcs=maskcs,
242252
nbghost_child=nbghost_child,
243-
key_linssh=key_linssh,
253+
linssh=linssh,
254+
vco=vco,
244255
vco_ref=vco_ref,
245256
open_kwargs=dict(**open_kwargs),
246257
)
@@ -259,7 +270,8 @@ def from_datasets(
259270
iperio: bool = False,
260271
nftype: str | None = None,
261272
read_mask: bool = False,
262-
key_linssh: bool = False,
273+
linssh: bool = False,
274+
vco: str = "1d",
263275
vco_ref: bool = False,
264276
maskcs: bool = False,
265277
nbghost_child: int = 4,
@@ -311,10 +323,13 @@ def from_datasets(
311323
maskcs: bool = False
312324
If True, all closed seas are masked using mask_opensea variables from domain files. Default is False.
313325
314-
key_linssh: bool = False
326+
linssh: bool = False
315327
Linear free-surface approximation. If True, vertical coordinates are time-independent and given by (e3t_0, e3u_0, e3v_0, e3w_0) in domain_cfg.
316328
If False, vertical coordinates are time-dependent and must be specified in NEMO model grid datasets. Default is False.
317329
330+
vco : str = "1d"
331+
Vertical reference variables. Options are '1d' to use 1-dimensional vertical reference coordinates or '3d' to use 3-dimensional vertical reference coordinates (deptht, depthu, depthv, depthw, depthf). Default is '1d'.
332+
318333
vco_ref: bool = False
319334
If True, add reference vertical scale factors and compute reference water column heights from domain files. Default is False.
320335
@@ -362,8 +377,14 @@ def from_datasets(
362377
raise TypeError("reading land-sea masks from domain_cfg (`read_mask`) must be a boolean.")
363378
if not isinstance(maskcs, bool):
364379
raise TypeError("masking of closed seas (`maskcs`) must be a boolean.")
365-
if not isinstance(key_linssh, bool):
366-
raise TypeError("linear free-surface approximation (`key_linssh`) must be a boolean.")
380+
if not isinstance(linssh, bool):
381+
raise TypeError("linear free-surface approximation (`linssh`) must be a boolean.")
382+
if not isinstance(vco, str):
383+
raise TypeError("vertical reference coordinates (`vco`) must be a string.")
384+
if vco not in ("1d", "3d"):
385+
raise ValueError(
386+
"vertical reference coordinates (`vco`) must be '1d' (1-dimensional) or '3d' (3-dimensional)."
387+
)
367388
if not isinstance(vco_ref, bool):
368389
raise TypeError("reference vertical coordinates (`vco_ref`) must be a boolean.")
369390
if not isinstance(nbghost_child, int):
@@ -403,7 +424,8 @@ def from_datasets(
403424
nftype=nftype,
404425
read_mask=read_mask,
405426
maskcs=maskcs,
406-
key_linssh=key_linssh,
427+
linssh=linssh,
428+
vco=vco,
407429
vco_ref=vco_ref,
408430
nbghost_child=nbghost_child,
409431
)

0 commit comments

Comments
 (0)