Skip to content

Commit fe0ac9e

Browse files
authored
Test configs (#17)
* Adding seamount case + rmax subroutine * running pre-commit * typo * Updating with @malmans2 suggestions * Updating _calc_rmax to pass Dataset as suggested by @malmans2 * forgot pre-commit on this branch * adding in change for PR17
1 parent 1edcec7 commit fe0ac9e

2 files changed

Lines changed: 94 additions & 2 deletions

File tree

docs/developers/whats-new.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ What's New
66
Unreleased
77
==========
88

9+
- Added :py:func:`~pydomcfg.bathymetry.sea_mount`
10+
useful to generate classic sea mount test case. (:pr:`17`)
911
- Added :py:func:`~pydomcfg.utils.generate_cartesian_grid`
1012
useful to generate test datasets. (:pr:`8`)
1113
- pyDOMCFG is on GitHub! (:pr:`1`)

pydomcfg/tests/bathymetry.py

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Module to generate datasets for testing
33
"""
44

5+
import numpy as np
56
import xarray as xr
67
from xarray import Dataset
78

@@ -44,6 +45,52 @@ def flat(self, depth: float) -> Dataset:
4445
ds["Bathymetry"] = xr.full_like(ds["glamt"], depth)
4546
return _add_attributes(_add_mask(ds))
4647

48+
def sea_mount(self, depth: float, stiff=1.0) -> Dataset:
49+
"""
50+
Channel with seamount case.
51+
52+
Produces bathymetry of a channel with a Gaussian seamount in order to
53+
simulate an idealised test case. Based on Marsaleix et al., 2009
54+
doi:10.1016/j.ocemod.2009.06.011 Eq. 15.
55+
56+
Parameters
57+
----------
58+
depth: float
59+
Bottom depth (units: m).
60+
stiff: float
61+
Scale factor for steepness of seamount (units: None)
62+
63+
Returns
64+
-------
65+
Dataset
66+
"""
67+
ds = self._coords
68+
69+
# Find half way point for sea mount location
70+
half_way = {k: v // 2 for k, v in ds.sizes.items()}
71+
glamt_mid, gphit_mid = (g.isel(half_way) for g in (ds.glamt, ds.gphit))
72+
73+
# Define sea mount bathymetry
74+
ds["Bathymetry"] = depth * (
75+
1.0
76+
- 0.9
77+
* np.exp(
78+
-(
79+
stiff
80+
/ 40.0e3 ** 2
81+
* ((ds.glamt - glamt_mid) ** 2 + (ds.gphit - gphit_mid) ** 2)
82+
)
83+
)
84+
)
85+
86+
# Add rmax of Bathymetry
87+
# ds["rmax"] = DataArray(
88+
# _calc_rmax(ds["Bathymetry"].to_masked_array()), dims=["y", "x"]
89+
# )
90+
ds["rmax"] = _calc_rmax(ds["Bathymetry"])
91+
92+
return _add_attributes(_add_mask(ds))
93+
4794

4895
def _add_mask(ds: Dataset) -> Dataset:
4996
"""
@@ -58,7 +105,7 @@ def _add_mask(ds: Dataset) -> Dataset:
58105
-------
59106
Dataset
60107
"""
61-
ds["mask"] = xr.where(ds["Bathymetry"] > 0, 1, 0)
108+
ds["mask"] = xr.where(ds["Bathymetry"] > 0, 1, 0) # TODO: should this be bool?
62109
return ds
63110

64111

@@ -69,17 +116,60 @@ def _add_attributes(ds: Dataset) -> Dataset:
69116
Parameters
70117
----------
71118
ds: Dataset
72-
Dataset with bathymetry and mask variables
119+
Dataset with bathymetry and mask variables [and rmax]
73120
74121
Returns
75122
-------
76123
Dataset
77124
"""
125+
78126
attrs_dict = {
79127
"Bathymetry": dict(standard_name="sea_floor_depth_below_geoid", units="m"),
80128
"mask": dict(standard_name="sea_binary_mask", units="1"),
81129
}
130+
131+
if "rmax" in ds:
132+
attrs_dict["rmax"] = dict(standard_name="rmax", units="1")
133+
82134
for varname, attrs in attrs_dict.items():
83135
ds[varname].attrs = attrs
84136
ds[varname].attrs["coordinates"] = "glamt gphit"
85137
return ds
138+
139+
140+
def _calc_rmax(depth):
141+
"""
142+
Calculate rmax: measure of steepness
143+
144+
This function returns the slope steepness criteria rmax, which is simply
145+
(H[0] - H[1]) / (H[0] + H[1])
146+
147+
Parameters
148+
----------
149+
depth: float
150+
Bottom depth (units: m).
151+
152+
Returns
153+
-------
154+
rmax: float
155+
Slope steepness value (units: None)
156+
"""
157+
depth = depth.reset_index(list(depth.dims))
158+
159+
both_rmax = []
160+
for dim in depth.dims:
161+
162+
# (H[0] - H[1]) / (H[0] + H[1])
163+
depth_diff = depth.diff(dim)
164+
depth_rolling_sum = depth.rolling({dim: 2}).sum().dropna(dim)
165+
rmax = depth_diff / depth_rolling_sum
166+
167+
# (R[0] + R[1]) / 2
168+
rmax = rmax.rolling({dim: 2}).mean().dropna(dim)
169+
170+
# Fill first row and column
171+
rmax = rmax.pad({dim: (1, 1)}, constant_values=0)
172+
173+
both_rmax.append(np.abs(rmax))
174+
175+
return np.maximum(*both_rmax)

0 commit comments

Comments
 (0)