Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
609 changes: 410 additions & 199 deletions examples/directional_waves.ipynb

Large diffs are not rendered by default.

127 changes: 61 additions & 66 deletions examples/environmental_contours_example.ipynb

Large diffs are not rendered by default.

38 changes: 24 additions & 14 deletions examples/extreme_response_MLER_example.ipynb

Large diffs are not rendered by default.

53 changes: 33 additions & 20 deletions examples/extreme_response_contour_example.ipynb

Large diffs are not rendered by default.

418 changes: 209 additions & 209 deletions examples/extreme_response_full_sea_state_example.ipynb

Large diffs are not rendered by default.

71 changes: 68 additions & 3 deletions mhkit/tests/wave/io/test_ndbc.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,27 @@ def tearDownClass(self):
# Realtime data
def test_ndbc_read_realtime_met(self):
data, units = wave.io.ndbc.read_file(join(datadir, "46097.txt"))
cd = np.array(
[
180,
12.0,
np.nan,
np.nan,
np.nan,
np.nan,
np.nan,
1001.0,
6.1,
10.9,
np.nan,
np.nan,
np.nan,
np.nan,
],
)
expected_index0 = datetime(2019, 4, 2, 13, 50)
self.assertSetEqual(set(data.columns), set(self.expected_columns_metRT))
np.testing.assert_equal(data.iloc[-1].astype(float).values, cd)
self.assertEqual(data.index[0], expected_index0)
self.assertEqual(data.shape, (6490, 14))
self.assertEqual(units, self.expected_units_metRT)
Expand All @@ -110,8 +129,26 @@ def test_ndbc_read_realtime_met(self):
def test_ndbnc_read_historical_met(self):
# QC'd monthly data, Aug 2019
data, units = wave.io.ndbc.read_file(join(datadir, "46097h201908qc.txt"))
cd = np.array(
[
160.0,
2.7,
np.nan,
np.nan,
np.nan,
np.nan,
np.nan,
1014.9,
14.5,
13.4,
np.nan,
np.nan,
np.nan,
]
)
expected_index0 = datetime(2019, 8, 1, 0, 0)
self.assertSetEqual(set(data.columns), set(self.expected_columns_metH))
np.testing.assert_equal(data.iloc[-1].astype(float).values, cd)
self.assertEqual(data.index[0], expected_index0)
self.assertEqual(data.shape, (4464, 13))
self.assertEqual(units, self.expected_units_metH)
Expand All @@ -127,14 +164,34 @@ def test_ndbc_read_spectral(self):
def test_ndbc_read_cwind_no_units(self):
data, units = wave.io.ndbc.read_file(join(datadir, "42a01c2003.txt"))
self.assertEqual(data.shape, (4320, 5))
cd = np.array(
[
[168.0, 6.1, np.nan, np.nan, np.nan],
[153.0, 5.6, np.nan, np.nan, np.nan],
[149.0, 4.8, np.nan, np.nan, np.nan],
[147.0, 4.1, np.nan, np.nan, np.nan],
[151.0, 4.1, 169.0, np.nan, np.nan],
]
)
np.testing.assert_equal(data.tail().values, cd)
self.assertEqual(units, None)

def test_ndbc_read_cwind_units(self):
data, units = wave.io.ndbc.read_file(join(datadir, "46002c2016.txt"))
self.assertEqual(data.shape, (28468, 5))
self.assertEqual(units, wave.io.ndbc.parameter_units("cwind"))
cd = np.array(
[
[303.0, 6.2, np.nan, np.nan, np.nan],
[306.0, 7.8, np.nan, np.nan, np.nan],
[302.0, 7.6, np.nan, np.nan, np.nan],
[293.0, 6.4, np.nan, np.nan, np.nan],
[287.0, 6.3, 307.0, 9.7, 1819.0],
]
)
np.testing.assert_equal(data.tail().values, cd)

def test_ndbc_available_data(self):
# Can't test data values because datafiles are periodically updating
data = wave.io.ndbc.available_data("swden", buoy_number="46029")
cols = data.columns.tolist()
exp_cols = ["id", "year", "filename"]
Expand All @@ -158,14 +215,18 @@ def test__ndbc_parse_filenames(self):
self.assertListEqual(fnames, self.filenames)

def test_ndbc_request_data(self):
# Can't test data values because datafiles are periodically updating
filenames = pd.Series(self.filenames[0])
ndbc_data = wave.io.ndbc.request_data("swden", filenames, to_pandas=False)
self.assertTrue(xr.Dataset(self.swden).equals(ndbc_data["1996"]))

def test_ndbc_request_data_from_dataframe(self):
# Can't test data values because datafiles are periodically updating
filenames = pd.DataFrame(pd.Series(data=self.filenames[0]))
ndbc_data = wave.io.ndbc.request_data("swden", filenames)
assert_frame_equal(self.swden, ndbc_data["1996"])
assert_frame_equal(
self.swden, ndbc_data["1996"], check_column_type=False, check_dtype=False
)

def test_ndbc_request_data_filenames_length(self):
with self.assertRaises(ValueError):
Expand Down Expand Up @@ -221,9 +282,10 @@ def test_ndbc_date_string_to_datetime(self):
def test_ndbc_parameter_units(self):
parameter = "swden"
units = wave.io.ndbc.parameter_units(parameter)
self.assertEqual(units[parameter], "(m*m)/Hz")
self.assertEqual(units[parameter], "m^2/Hz")

def test_ndbc_request_directional_data(self):
# Can't test data values because datafiles are periodically updating
data = self.directional_data
# correct 5 parameters
self.assertEqual(len(data), 5)
Expand All @@ -236,12 +298,14 @@ def test_ndbc_request_directional_data(self):
self.assertEqual(len(data.frequency), 47)

def test_ndbc_create_spread_function(self):
# Can't test data values because datafiles are periodically updating
directions = np.arange(0, 360, 2.0)
spread = wave.io.ndbc.create_spread_function(self.directional_data, directions)
self.assertEqual(spread.shape, (47, 180))
self.assertEqual(spread.units, "1/Hz/deg")

def test_ndbc_create_directional_spectrum(self):
# Can't test data values because datafiles are periodically updating
directions = np.arange(0, 360, 2.0)
spectrum = wave.io.ndbc.create_directional_spectrum(
self.directional_data, directions
Expand All @@ -250,6 +314,7 @@ def test_ndbc_create_directional_spectrum(self):
self.assertEqual(spectrum.units, "m^2/Hz/deg")

def test_plot_directional_spectrum(self):
# Can't test data values because datafiles are periodically updating
directions = np.arange(0, 360, 2.0)
spectrum = wave.io.ndbc.create_spread_function(
self.directional_data, directions
Expand Down
3 changes: 3 additions & 0 deletions mhkit/wave/graphics.py
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,9 @@ def plot_directional_spectrum(

a, f = np.meshgrid(np.deg2rad(spectrum.direction), spectrum.frequency)
_, ax = plt.subplots(subplot_kw=dict(projection="polar"))
# Set 0 degrees to be at the top, and positive angles to be clockwise
ax.set_theta_zero_location("N")
ax.set_theta_direction(-1)
tmp = np.floor(np.min(spectrum.data) * 10) / 10
color_level_min = tmp if (color_level_min is None) else color_level_min
color_level_max = np.ceil(np.max(spectrum.data) * 10) / 10
Expand Down
48 changes: 28 additions & 20 deletions mhkit/wave/io/ndbc.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,13 @@ def read_file(file_name, missing_values=["MM", 9999, 999, 99], to_pandas=True):
metadata: dict or None
Dictionary with {column name: units} key value pairs when the NDBC file
contains unit information, otherwise None is returned

Notes
-----
NDBC documentation of measurement descriptions and units can be found here:
https://www.ndbc.noaa.gov/faq/measdes.shtml
"""

if not isinstance(file_name, str):
raise TypeError(f"file_name must be of type str. Got: {type(file_name)}")
if not isinstance(missing_values, list):
Expand Down Expand Up @@ -710,9 +716,9 @@ def parameter_units(parameter=""):
}
elif parameter == "cwind":
units = {
"WDIR": "degT",
"WDIR": "degT", # degree True North
"WSPD": "m/s",
"GDR": "degT",
"GDR": "degT", # degree True North
"GST": "m/s",
"GTIME": "hhmm",
}
Expand Down Expand Up @@ -764,10 +770,10 @@ def parameter_units(parameter=""):
"WWH": "m",
"WWP": "sec",
"SwD": "-",
"WWD": "degT",
"WWD": "degT", # degree True North
"STEEPNESS": "-",
"APD": "sec",
"MWD": "degT",
"MWD": "degT", # degree True North
}
elif parameter == "srad":
units = {
Expand All @@ -777,13 +783,13 @@ def parameter_units(parameter=""):
}
elif parameter == "stdmet":
units = {
"WDIR": "degT",
"WDIR": "degT", # degree True North
"WSPD": "m/s",
"GST": "m/s",
"WVHT": "m",
"DPD": "sec",
"APD": "sec",
"MWD": "degT",
"MWD": "degT", # degree True North
"PRES": "hPa",
"ATMP": "degC",
"WTMP": "degC",
Expand All @@ -797,11 +803,11 @@ def parameter_units(parameter=""):
"PRES": "hPa",
"PTIME": "hhmm",
"WSPD": "m/s",
"WDIR": "degT",
"WDIR": "degT", # degree True North
"WTIME": "hhmm",
}
elif parameter == "swden":
units = {"swden": "(m*m)/Hz"}
units = {"swden": "m^2/Hz"}
elif parameter == "swdir":
units = {"swdir": "deg"}
elif parameter == "swdir2":
Expand All @@ -812,13 +818,13 @@ def parameter_units(parameter=""):
units = {"swr2": ""}
else:
units = {
"swden": "(m*m)/Hz",
"swden": "m^2/Hz",
"PRES": "hPa",
"PTIME": "hhmm",
"WDIR": "degT",
"WDIR": "degT", # degree True North
"WTIME": "hhmm",
"DPD": "sec",
"MWD": "degT",
"MWD": "degT", # degree True North
"ATMP": "degC",
"WTMP": "degC",
"DEWP": "degC",
Expand All @@ -834,7 +840,7 @@ def parameter_units(parameter=""):
"WWH": "m",
"WWP": "sec",
"SwD": "-",
"WWD": "degT",
"WWD": "degT", # degree True North
"STEEPNESS": "-",
"APD": "sec",
"RATE": "mm/h",
Expand All @@ -859,7 +865,7 @@ def parameter_units(parameter=""):
"WSPD20": "m/s",
"T": "-",
"HEIGHT": "m",
"GDR": "degT",
"GDR": "degT", # degree True North
"GST": "m/s",
"GTIME": "hhmm",
"DEP01": "m",
Expand Down Expand Up @@ -1026,7 +1032,7 @@ def request_directional_data(buoy, year):

data_dict["swden"].attrs = {
"units": "m^2/Hz",
"long_name": "omnidirecational spectrum",
"long_name": "omnidirectional spectrum",
"standard_name": "S",
"description": "Omnidirectional *sea surface elevation variance (m^2)* spectrum (/Hz).",
}
Expand Down Expand Up @@ -1062,7 +1068,7 @@ def request_directional_data(buoy, year):
return xr.Dataset(data_dict)


def _create_spectrum(data, frequencies, directions, name, units):
def _create_spectrum_dataarray(data, frequencies, directions, name, units):
"""
Create an xarray.DataArray for storing spectrum data with correct
dimensions, coordinates, names, and units.
Expand Down Expand Up @@ -1166,16 +1172,18 @@ def create_spread_function(data, directions):

r1 = data["swr1"].data.reshape(-1, 1)
r2 = data["swr2"].data.reshape(-1, 1)
# a1 = degrees CW from N, according to NDBC documentation
a1 = data["swdir"].data.reshape(-1, 1)
# a2 degrees CW from N, according to NDBC documentation
a2 = data["swdir2"].data.reshape(-1, 1)
a = directions.reshape(1, -1)
a = directions.reshape(1, -1) # degrees
spread = (
1
/ np.pi
* (0.5 + r1 * np.cos(np.deg2rad(a - a1)) + r2 * np.cos(2 * np.deg2rad(a - a2)))
)
spread = _create_spectrum(
spread, data.frequency.values, directions, name="Spread", units="1"
spread = _create_spectrum_dataarray(
np.rad2deg(spread), data.frequency.values, directions, name="Spread", units="1"
)
return spread

Expand Down Expand Up @@ -1209,9 +1217,9 @@ def create_directional_spectrum(data, directions):
spread = create_spread_function(data, directions).values
omnidirectional_spectrum = data["swden"].data.reshape(-1, 1)
spectrum = omnidirectional_spectrum * spread
spectrum = _create_spectrum(
spectrum = _create_spectrum_dataarray(
spectrum,
data.frequency.values,
data["frequency"].values,
directions,
name="Elevation variance",
units="m^2",
Expand Down
Loading