Skip to content

Commit d687e7a

Browse files
committed
Add open_dataset kwargs to .from_paths() & use 2D masks (t/u/vmaskutil) in computational methods.
1 parent 318b6ed commit d687e7a

2 files changed

Lines changed: 31 additions & 20 deletions

File tree

nemo_cookbook/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
__author__ = "Ollie Tooth"
44
__credits__ = "National Oceanography Centre"
55

6+
from nemo_cookbook import examples, extract, masks, transform
7+
68
from .nemodatatree import NEMODataTree
79
from .extract import extract_section

nemo_cookbook/nemodatatree.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ def from_paths(
5555
nests: dict[str, str] | None = None,
5656
iperio: bool = False,
5757
nftype: str | None = None,
58-
nbghost_child: int = 4
58+
nbghost_child: int = 4,
59+
**open_kwargs: dict[str, any],
5960
) -> Self:
6061
"""
6162
Create a NEMODataTree from a dictionary of paths to NEMO model output files,
@@ -113,6 +114,8 @@ def from_paths(
113114
nbghost_child : int = 4
114115
Number of ghost cells to remove from the western/southern boundaries of the (grand)child domains. Default is 4.
115116
117+
**open_kwargs : dict, optional
118+
Additional keyword arguments to pass to `xarray.open_dataset` or `xr.open_mfdataset` when opening NEMO model output files.
116119
Returns
117120
-------
118121
NEMODataTree
@@ -128,6 +131,8 @@ def from_paths(
128131
raise ValueError("north fold type of parent domain must be 'T' (T-pivot fold), 'F' (F-pivot fold), or None.")
129132
if not isinstance(nbghost_child, int):
130133
raise TypeError("number of ghost cells along the western/southern boundaries must be an integer.")
134+
if not isinstance(open_kwargs, dict):
135+
raise TypeError("open_kwargs must be a dictionary.")
131136

132137
# Define parent, child, grandchild filepath collections:
133138
d_child, d_grandchild = None, None
@@ -151,7 +156,8 @@ def from_paths(
151156
nests=nests,
152157
iperio=iperio,
153158
nftype=nftype,
154-
nbghost_child=nbghost_child
159+
nbghost_child=nbghost_child,
160+
open_kwargs=dict(**open_kwargs)
155161
)
156162

157163
datatree = super().from_dict(d_tree)
@@ -518,7 +524,7 @@ def gradient(
518524
raise ValueError("dom must be a string specifying prefix of a NEMO domain (e.g., '.', '1', '2', etc.).")
519525

520526
# -- Get NEMO model grid properties -- #
521-
_, dom_prefix, dom_suffix = cls._get_properties(dom=dom)
527+
dom_prefix, dom_suffix = cls._get_properties(dom=dom)
522528
grid_paths = cls._get_grid_paths(dom=dom)
523529
gridT, gridU, gridV, gridW = grid_paths['gridT'], grid_paths['gridU'], grid_paths['gridV'], grid_paths['gridW']
524530

@@ -537,7 +543,7 @@ def gradient(
537543
umask = cls[gridU]["umask"]
538544
else:
539545
# 2-dimensional umask:
540-
umask = cls[gridU]["umask"][0, :, :].drop_vars(f"k{dom_suffix}")
546+
umask = cls[gridU]["umaskutil"]
541547

542548
# Zonally Periodic Domain:
543549
if cls[gridT].attrs.get("iperio", False):
@@ -566,8 +572,8 @@ def gradient(
566572
if f"{dom_prefix}deptht" in da.coords:
567573
vmask = cls[gridV]["vmask"]
568574
else:
569-
# 2-dimensional vmask:
570-
vmask = cls[gridV]["vmask"][0, :, :].drop_vars(f"k{dom_suffix}")
575+
# 2-dimensional vmask (unique points):
576+
vmask = cls[gridV]["vmaskutil"]
571577

572578
# Pad with zeros after differencing (zero gradient at jmaxdom):
573579
dvar = (da
@@ -632,7 +638,7 @@ def divergence(
632638
raise ValueError("dom must be a string specifying the prefix of a NEMO domain (e.g., '.', '1', '2', etc.).")
633639

634640
# -- Get NEMO model grid properties -- #
635-
_, dom_prefix, dom_suffix = cls._get_properties(dom=dom)
641+
dom_prefix, _ = cls._get_properties(dom=dom)
636642
grid_paths = cls._get_grid_paths(dom=dom)
637643
gridT, gridU, gridV = grid_paths['gridT'], grid_paths['gridU'], grid_paths['gridV']
638644
ijk_names = cls._get_ijk_names(dom=dom)
@@ -653,8 +659,8 @@ def divergence(
653659
# 3-dimensional tmask:
654660
tmask = cls[gridT]["tmask"]
655661
else:
656-
# 2-dimensional tmask:
657-
tmask = cls[gridT]["tmask"][0, :, :].drop_vars(f"k{dom_suffix}")
662+
# 2-dimensional tmask (unique points):
663+
tmask = cls[gridT]["tmaskutil"]
658664

659665
# -- Neglecting the first T-grid points along i, j dimensions -- #
660666
e1t = cls[gridT]["e1t"].isel({i_name: slice(1, None), j_name: slice(1, None)})
@@ -712,7 +718,7 @@ def curl(
712718
raise ValueError("dom must be a string specifying the prefix of a NEMO domain (e.g., '.', '1', '2', etc.).")
713719

714720
# -- Get NEMO model grid properties -- #
715-
_, dom_prefix, dom_suffix = cls._get_properties(dom=dom)
721+
dom_prefix, _ = cls._get_properties(dom=dom)
716722
grid_paths = cls._get_grid_paths(dom=dom)
717723
gridU, gridV, gridF = grid_paths['gridU'], grid_paths['gridV'], grid_paths['gridF']
718724
ijk_names = cls._get_ijk_names(dom=dom)
@@ -733,8 +739,8 @@ def curl(
733739
# 3-dimensional fmask
734740
fmask = cls[gridF]["fmask"]
735741
else:
736-
# 2-dimensional fmask:
737-
fmask = cls[gridF]["fmask"][0, :, :].drop_vars(f"k{dom_suffix}")
742+
# 2-dimensional fmask (unique points):
743+
fmask = cls[gridF]["fmaskutil"]
738744

739745
# -- Neglecting the final F-grid points along i, j dimensions -- #
740746
e1f = cls[gridF]["e1f"].isel({i_name: slice(None, -1), j_name: slice(None, -1)})
@@ -816,7 +822,7 @@ def integral(
816822
raise ValueError(f"mask must have dimensions subset from {cls[grid].dims}.")
817823

818824
# -- Get NEMO model grid properties -- #
819-
_, dom_prefix, dom_suffix, grid_suffix = cls._get_properties(grid=grid, infer_dom=True)
825+
_, dom_prefix, _, grid_suffix = cls._get_properties(grid=grid, infer_dom=True)
820826

821827
# -- Collect variable, weights & mask -- #
822828
da = cls[grid][var].where(mask) if mask is not None else cls[grid][var]
@@ -826,8 +832,9 @@ def integral(
826832
# Apply 3-dimensional t/u/v/f/w mask:
827833
dom_mask = cls[grid][f"{grid_suffix}mask"]
828834
else:
829-
# Apply 2-dimensional t/u/v/f/w mask:
830-
dom_mask = cls[grid][f"{grid_suffix}mask"][0, :, :].drop_vars(f"k{dom_suffix}")
835+
# Apply 2-dimensional t/u/v/f mask (unique points):
836+
hgrid_type = grid_suffix if 'w' not in grid_suffix else 't'
837+
dom_mask = cls[grid][f"{hgrid_type}maskutil"]
831838

832839
# -- Perform integration -- #
833840
if cum_dims is not None:
@@ -1076,8 +1083,9 @@ def masked_statistic(
10761083
# Apply 3-dimensional t/u/v/f/w mask:
10771084
dom_mask = cls[grid][f"{grid_suffix}mask"]
10781085
else:
1079-
# Apply 2-dimensional t/u/v/f/w mask:
1080-
dom_mask = cls[grid][f"{grid_suffix}mask"][0, :, :].drop_vars(f"k{dom_suffix}")
1086+
# Apply 2-dimensional t/u/v/f mask (unique points):
1087+
hgrid_type = grid_suffix if 'w' not in grid_suffix else 't'
1088+
dom_mask = cls[grid][f"{hgrid_type}maskutil"]
10811089

10821090
da = cls[grid][var].where(dom_mask & mask_poly)
10831091

@@ -1280,15 +1288,16 @@ def binned_statistic(
12801288
raise ValueError(f"mask must have dimensions subset from {cls[grid].dims}.")
12811289

12821290
# -- Get NEMO model grid properties -- #
1283-
_, dom_prefix, dom_suffix, grid_suffix = cls._get_properties(grid=grid, infer_dom=True)
1291+
_, dom_prefix, _, grid_suffix = cls._get_properties(grid=grid, infer_dom=True)
12841292

12851293
# -- Define input variables & apply grid mask -- #
12861294
if f"{dom_prefix}depth{grid_suffix}" in cls[grid][values].coords:
12871295
# Apply 3-dimensional t/u/v/f/w mask:
12881296
dom_mask = cls[grid][f"{grid_suffix}mask"]
12891297
else:
1290-
# Apply 2-dimensional t/u/v/f/w mask:
1291-
dom_mask = cls[grid][f"{grid_suffix}mask"][0, :, :].drop_vars(f"k{dom_suffix}")
1298+
# Apply 2-dimensional t/u/v/f mask (unique points):
1299+
hgrid_type = grid_suffix if 'w' not in grid_suffix else 't'
1300+
dom_mask = cls[grid][f"{hgrid_type}maskutil"]
12921301

12931302
values_data = cls[grid][values].where(mask & dom_mask) if mask is not None else cls[grid][values].where(dom_mask)
12941303
var_data = [cls[grid][var].where(mask & dom_mask) if mask is not None else cls[grid][var].where(dom_mask) for var in vars]

0 commit comments

Comments
 (0)