Skip to content

Commit 00fe635

Browse files
authored
Merge pull request #50 from NOC-MSM/47-add-option-to-retain-ghost-points-in-nested-nemodatatrees
47 add option to retain ghost points in nested nemodatatrees
2 parents 1f46f52 + 997c721 commit 00fe635

7 files changed

Lines changed: 271 additions & 105 deletions

File tree

docs/docs/howto.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ NEMODataTree.from_paths(paths, iperio=True, nftype="T")
2626

2727
In the example above, we consider only a global parent domain, which is zonally periodic (`iperio=True`) and north-folding on **T** grid points (`nftype="T"`). Note, that we are only required to specify paths for one or more NEMO model grid types (e.g., `*_gridT.nc`).
2828

29-
For NEMO models using a linear free surface approximation (i.e., vertical scale factors are time-independent), we should also specify `key_linssh=True` to read these directly from the domain_cfg file included in the `paths` dictionary.
29+
For NEMO models using a linear free surface approximation (i.e., vertical scale factors are time-independent), we should also specify `linssh=True` to read these directly from the domain_cfg file included in the `paths` dictionary.
30+
31+
Additionally, for NEMO models configured with more complex vertical coordinates (e.g., MEs or sigma-coordinates), such that vertical reference variables (e.g., `depth`) vary spatially, we should also specify `vco="3d"` to include all vertical reference variables as 3-dimensional arrays analogously to using `key_vco_3d` within NEMO itself. By default, a `NEMODataTree` is constructed using 1-dimensional vertical reference variables.
3032

3133
### Create a NEMODataTree from `xarray.Datasets`
3234

docs/docs/user_guide.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,9 @@ This is because domain variables are assigned to their respective grid nodes dur
178178

179179
Typically, NEMO model simulations use a quasi-eulerian vertical coordinate which absorbs the divergence of horizontal barotropic velocities (e.g., $z^{*}$ or $s^{*}$), meaning that vertical grid scale factors evolve through time (i.e., a time-varying free surface translates into variations in grid cell thickness).
180180

181-
`NEMODataTree` considers the case of time-evolving vertical grid scale factors to be the default as the `key_linssh` argument to the `.from_paths()` and `.from_datasets()` constructors is set to be `False` by default. This means that vertical grid scale factors must be provided in the netCDF files or `xarray.Datasets` used to define each NEMO model grid node in the `NEMODataTree`.
181+
`NEMODataTree` considers the case of time-evolving vertical grid scale factors to be the default as the `linssh` argument to the `.from_paths()` and `.from_datasets()` constructors is set to be `False` by default. This means that vertical grid scale factors must be provided in the netCDF files or `xarray.Datasets` used to define each NEMO model grid node in the `NEMODataTree`.
182182

183-
For NEMO model simulations using a linear free surface approximation (i.e., variations in the free surface are neglected compared to the depth of the ocean), we should use `key_linssh=True` to indicate that vertical grid scale factors remain fixed through time and should be read directly from the reference variables contained within the domain_cfg netCDF file or `xarray.Dataset` (e.g., `e3t_0`, `e3u_0` etc.).
183+
For NEMO model simulations using a linear free surface approximation (i.e., variations in the free surface are neglected compared to the depth of the ocean), we should use `linssh=True` to indicate that vertical grid scale factors remain fixed through time and should be read directly from the reference variables contained within the domain_cfg netCDF file or `xarray.Dataset` (e.g., `e3t_0`, `e3u_0` etc.).
184184

185185
**Dimensions & Coordinates**
186186

@@ -268,11 +268,11 @@ In summary, defining a `NEMODataTree` for a nested configuration includes two im
268268

269269
4. Redefine the `dims` and `coords` of each grid dataset to use `i{dom}`, `j{dom}`, `k{dom}` as used to define the semi-discrete equations in NEMO, where *dom* is the unique domain number.
270270

271-
5. **Clip nested child domains to remove ghost points along the boundaries & add a mapping from the parent grid indices to the child grid indices to the `coords`.**
271+
5. **Clip nested child domains to remove ghost points along the boundaries & add a mapping from the parent grid indices to the child grid indices to the `coords`.** Alternatively, the entire child domain, including ghost points, can be retained by passing `nbghost_child=None` to the `.from_paths()` and `.from_datasets()` constructors.
272272

273273
6. **Assemble dictionaries of processed NEMO model grid datasets for each of the parent, child and grandchild domains.**
274274

275-
7. Assemble the `xarray.DataTree` using a nested dictionary of NEMO model domains.
275+
7. Assemble the underlying `xarray.DataTree` using a nested dictionary of NEMO model domains.
276276

277277
### NEMODataArray :ocean: :simple-databricks:
278278
---

nemo_cookbook/nemodatatree.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def from_paths(
7474
linssh: bool = False,
7575
vco: str = "1d",
7676
vco_ref: bool = False,
77-
nbghost_child: int = 4,
77+
nbghost_child: int | None = 4,
7878
**open_kwargs: dict[str, any],
7979
) -> Self:
8080
"""
@@ -150,8 +150,9 @@ def from_paths(
150150
vco_ref: bool = False
151151
If True, add reference vertical scale factors and compute reference water column heights from domain files. Default is False.
152152
153-
nbghost_child : int = 4
153+
nbghost_child : int | None = 4
154154
Number of ghost cells to remove from the western/southern boundaries of the (grand)child domains. Default is 4.
155+
If None, no ghost cells are removed and the full (grand)child domain is used.
155156
156157
**open_kwargs : dict, optional
157158
Additional keyword arguments to pass to `xarray.open_dataset` or `xr.open_mfdataset` when opening NEMO model output files.
@@ -210,9 +211,9 @@ def from_paths(
210211
)
211212
if not isinstance(vco_ref, bool):
212213
raise TypeError("reference vertical coordinates (`vco_ref`) must be a boolean.")
213-
if not isinstance(nbghost_child, int):
214+
if not isinstance(nbghost_child, (int, type(None))):
214215
raise TypeError(
215-
"number of ghost cells along the western/southern boundaries (`nbghost_child`) must be an integer."
216+
"number of ghost cells along the western/southern boundaries (`nbghost_child`) must be an integer or None."
216217
)
217218
if not isinstance(open_kwargs, dict):
218219
raise TypeError("`open_kwargs` must be a dictionary.")
@@ -274,7 +275,7 @@ def from_datasets(
274275
vco: str = "1d",
275276
vco_ref: bool = False,
276277
maskcs: bool = False,
277-
nbghost_child: int = 4,
278+
nbghost_child: int | None = 4,
278279
) -> Self:
279280
"""
280281
Create a NEMODataTree from a dictionary of `xarray.Dataset` objects created from NEMO model output files,
@@ -333,8 +334,9 @@ def from_datasets(
333334
vco_ref: bool = False
334335
If True, add reference vertical scale factors and compute reference water column heights from domain files. Default is False.
335336
336-
nbghost_child : int = 4
337+
nbghost_child : int | None = 4
337338
Number of ghost cells to remove from the western/southern boundaries of the (grand)child domains. Default is 4.
339+
If None, no ghost cells are removed and the full (grand)child domain is used.
338340
339341
Returns
340342
-------
@@ -387,9 +389,9 @@ def from_datasets(
387389
)
388390
if not isinstance(vco_ref, bool):
389391
raise TypeError("reference vertical coordinates (`vco_ref`) must be a boolean.")
390-
if not isinstance(nbghost_child, int):
392+
if not isinstance(nbghost_child, (int, type(None))):
391393
raise TypeError(
392-
"number of ghost cells along the western/southern boundaries (`nbghost_child`) must be an integer."
394+
"number of ghost cells along the western/southern boundaries (`nbghost_child`) must be an integer or None."
393395
)
394396

395397
# -- Define parent, child, grandchild dataset collections -- #

nemo_cookbook/processing.py

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,16 @@ def _add_parent_indices(
5858
i_ic = xr.DataArray(
5959
np.repeat(i_child, repeats=ds.attrs["rx"]),
6060
dims=[f"i{label}"],
61-
coords={f"i{label}": ds[f"i{label}"]},
6261
)
62+
if i_ic.size < ds[f"i{label}"].size:
63+
# Pad i-mapping with NaNs if ghost cells remain in child domain:
64+
width = int((ds[f"i{label}"].size - i_ic.size) / 2)
65+
i_ic = i_ic.pad({f"i{label}": (width, width)})
66+
6367
ds[f"i{plabel}_i{label}"] = i_ic
68+
ds[f"i{plabel}_i{label}"] = ds[f"i{plabel}_i{label}"].assign_coords(
69+
{f"i{label}": ds[f"i{label}"]}
70+
)
6471
ds[f"i{plabel}_i{label}"] = ds[f"i{plabel}_i{label}"].assign_attrs(
6572
name=f"i{plabel}_i{label}",
6673
long_name=f"i{plabel} indices of child domain i{label} indices",
@@ -73,9 +80,16 @@ def _add_parent_indices(
7380
j_jc = xr.DataArray(
7481
np.repeat(j_child, repeats=ds.attrs["ry"]),
7582
dims=[f"j{label}"],
76-
coords={f"j{label}": ds[f"j{label}"]},
7783
)
84+
if j_jc.size < ds[f"j{label}"].size:
85+
# Pad j-mapping with NaNs if ghost cells remain in child domain:
86+
width = int((ds[f"j{label}"].size - j_jc.size) / 2)
87+
j_jc = j_jc.pad({f"j{label}": (width, width)})
88+
7889
ds[f"j{plabel}_j{label}"] = j_jc
90+
ds[f"j{plabel}_j{label}"] = ds[f"j{plabel}_j{label}"].assign_coords(
91+
{f"j{label}": ds[f"j{label}"]}
92+
)
7993
ds[f"j{plabel}_j{label}"] = ds[f"j{plabel}_j{label}"].assign_attrs(
8094
name=f"j{plabel}_j{label}",
8195
long_name=f"j{plabel} indices of child domain j{label} indices",
@@ -906,9 +920,10 @@ def _process_child(
906920
maskcs : bool = False
907921
If True, all closed seas are masked using mask_opensea variables from domain files. Default is False.
908922
909-
nbghost_child : int = _DEFAULT_NBGHOST_CHILD
923+
nbghost_child : int | None = _DEFAULT_NBGHOST_CHILD
910924
Number of ghost cells to remove from the western/southern boundaries of the (grand)child domain.
911-
Default is 4 (`_DEFAULT_NBGHOST_CHILD`).
925+
Default is 4 (`_DEFAULT_NBGHOST_CHILD`). If None, no ghost cells are removed and the full
926+
(grand)child domain is used.
912927
913928
linssh: bool = False
914929
Linear free-surface approximation. If True, vertical coordinates are time-independent and given by
@@ -971,18 +986,23 @@ def _process_child(
971986
read_mask=read_mask, maskcs=maskcs, vco=vco, vco_ref=vco_ref
972987
)
973988

974-
# Get child domain indices excluding ghost cells:
975-
ind_child = _get_child_indices(
976-
rx=d_nests.get("rx"),
977-
ry=d_nests.get("ry"),
978-
imin=d_nests.get("imin"),
979-
imax=d_nests.get("imax"),
980-
jmin=d_nests.get("jmin"),
981-
jmax=d_nests.get("jmax"),
982-
nbghost_child=nbghost_child,
983-
)
984-
i_slice = slice(ind_child[0], ind_child[1] + 1)
985-
j_slice = slice(ind_child[2], ind_child[3] + 1)
989+
if nbghost_child is not None:
990+
# Get child domain indices excluding ghost cells:
991+
ind_child = _get_child_indices(
992+
rx=d_nests.get("rx"),
993+
ry=d_nests.get("ry"),
994+
imin=d_nests.get("imin"),
995+
imax=d_nests.get("imax"),
996+
jmin=d_nests.get("jmin"),
997+
jmax=d_nests.get("jmax"),
998+
nbghost_child=nbghost_child,
999+
)
1000+
i_slice = slice(ind_child[0], ind_child[1] + 1)
1001+
j_slice = slice(ind_child[2], ind_child[3] + 1)
1002+
else:
1003+
# Use full child domain including ghost cells:
1004+
i_slice = slice(None)
1005+
j_slice = slice(None)
9861006

9871007
# Process T / U / V / W / F grids:
9881008
d_proc_grids = {}
@@ -1077,8 +1097,9 @@ def create_datatree_dict(
10771097
domain variables. Default is False.
10781098
maskcs: bool = False
10791099
If True, all closed seas are masked using mask_opensea variables from domain files. Default is False.
1080-
nbghost_child : int = _DEFAULT_NBGHOST_CHILD
1100+
nbghost_child : int | None = _DEFAULT_NBGHOST_CHILD
10811101
Number of ghost cells to remove from the western/southern boundaries of the (grand)child domain. Default is 4 (`_DEFAULT_NBGHOST_CHILD`).
1102+
If None, no ghost cells are removed and the full (grand)child domain is used.
10821103
linssh: bool = False
10831104
Linear free-surface approximation. If True, vertical coordinates are time-independent and given by (e3t_0, e3u_0, e3v_0, e3w_0). If False, vertical
10841105
coordinates are time-dependent and must be included in grid datasets. Default is False.

0 commit comments

Comments
 (0)