Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
82 changes: 51 additions & 31 deletions floris/wind_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,13 @@ def __init__(
if not isinstance(wind_speeds, np.ndarray):
raise TypeError("wind_speeds must be a NumPy array")

# Confirm that none of wind_directions or wind_speeds contain NaN values
if np.isnan(wind_directions).any():
raise ValueError("wind_directions contains NaN values")

if np.isnan(wind_speeds).any():
raise ValueError("wind_speeds contains NaN values")

# Confirm that both wind_directions and wind_speeds are monitonically
# increasing and evenly spaced
if len(wind_directions) > 1:
Expand Down Expand Up @@ -589,15 +596,17 @@ def upsample(self, wd_step=None, ws_step=None, method="linear", inplace=False):
# This is the case when wind directions doesn't cover the full range of possible
# degrees (0-360)
if np.abs((wd_range_min_current % 360.0) - (wd_range_max_current % 360.0)) > 1e-6:
wind_direction_column = np.concatenate((
np.array([wd_range_min_current]),
wind_direction_column,
np.array([wd_range_max_current])
))
ti_matrix = ti_matrix = np.vstack((ti_matrix[0, :], ti_matrix, ti_matrix[-1,:]))
freq_matrix = np.vstack((freq_matrix[0, :], freq_matrix, freq_matrix[-1,:]))
wind_direction_column = np.concatenate(
(
np.array([wd_range_min_current]),
wind_direction_column,
np.array([wd_range_max_current]),
)
)
ti_matrix = ti_matrix = np.vstack((ti_matrix[0, :], ti_matrix, ti_matrix[-1, :]))
freq_matrix = np.vstack((freq_matrix[0, :], freq_matrix, freq_matrix[-1, :]))
if self.value_table is not None:
value_matrix = np.vstack((value_matrix[0, :], value_matrix, value_matrix[-1,:]))
value_matrix = np.vstack((value_matrix[0, :], value_matrix, value_matrix[-1, :]))

# In the alternative case, where the wind directions cover the full range
# ie, 0, 10, 20 30, ...350, then need to place 0 at 360 and 350 at -10
Expand All @@ -620,11 +629,7 @@ def upsample(self, wd_step=None, ws_step=None, method="linear", inplace=False):

# Pad out the wind speeds
wind_speed_column = np.concatenate(
(
np.array([ws_range_min_current]),
wind_speed_column,
np.array([ws_range_max_current])
)
(np.array([ws_range_min_current]), wind_speed_column, np.array([ws_range_max_current]))
)
ti_matrix = np.hstack(
(ti_matrix[:, 0].reshape((-1, 1)), ti_matrix, ti_matrix[:, -1].reshape((-1, 1)))
Expand All @@ -637,7 +642,7 @@ def upsample(self, wd_step=None, ws_step=None, method="linear", inplace=False):
(
value_matrix[:, 0].reshape((-1, 1)),
value_matrix,
value_matrix[:, -1].reshape((-1, 1))
value_matrix[:, -1].reshape((-1, 1)),
)
)

Expand Down Expand Up @@ -1121,6 +1126,14 @@ def __init__(
if not isinstance(turbulence_intensities, np.ndarray):
raise TypeError("turbulence_intensities must be a NumPy array")

# Check that wind_directions, wind_speeds, and turbulence_intensities do not contain NaNs
if np.isnan(wind_directions).any():
raise ValueError("wind_directions must not contain NaNs")
if np.isnan(wind_speeds).any():
raise ValueError("wind_speeds must not contain NaNs")
if np.isnan(turbulence_intensities).any():
raise ValueError("turbulence_intensities must not contain NaNs")

# Confirm that both wind_directions and wind_speeds
# and turbulence intensities are monotonically
# increasing and evenly spaced
Expand Down Expand Up @@ -1579,21 +1592,21 @@ def upsample(self, wd_step=None, ws_step=None, ti_step=None, method="linear", in
(
np.array([wd_range_min_current]),
wind_direction_column,
np.array([wd_range_max_current])
np.array([wd_range_max_current]),
)
)
freq_matrix = np.concatenate(
(freq_matrix[0, :, :][None, :, :], freq_matrix, freq_matrix[-1, :, :][None, :, :]),
axis=0
axis=0,
)
if self.value_table is not None:
value_matrix = np.concatenate(
(
value_matrix[0, :, :][None, :, :],
value_matrix,
value_matrix[-1, :, :][None, :, :]
value_matrix[-1, :, :][None, :, :],
),
axis=0
axis=0,
)

# In the alternative case, where the wind directions cover the full range
Expand Down Expand Up @@ -1624,46 +1637,42 @@ def upsample(self, wd_step=None, ws_step=None, ti_step=None, method="linear", in

# Pad out the wind speeds
wind_speed_column = np.concatenate(
(
np.array([ws_range_min_current]),
wind_speed_column,
np.array([ws_range_max_current])
)
(np.array([ws_range_min_current]), wind_speed_column, np.array([ws_range_max_current]))
)
freq_matrix = np.concatenate(
(freq_matrix[:, 0, :][:, None, :], freq_matrix, freq_matrix[:, -1, :][:, None, :]),
axis=1
axis=1,
)
if self.value_table is not None:
value_matrix = np.concatenate(
(
value_matrix[:, 0, :][:, None, :],
value_matrix,
value_matrix[:, -1, :][:, None, :]
value_matrix[:, -1, :][:, None, :],
),
axis=1
axis=1,
)

# Pad out the turbulence intensities
turbulence_intensity_column = np.concatenate(
(
np.array([ti_range_min_current]),
turbulence_intensity_column,
np.array([ti_range_max_current])
np.array([ti_range_max_current]),
)
)
freq_matrix = np.concatenate(
(freq_matrix[:, :, 0][:, :, None], freq_matrix, freq_matrix[:, :, -1][:, :, None]),
axis=2
axis=2,
)
if self.value_table is not None:
value_matrix = np.concatenate(
(
value_matrix[:, :, 0][:, :, None],
value_matrix,
value_matrix[:, :, -1][:, :, None]
value_matrix[:, :, -1][:, :, None],
),
axis=2
axis=2,
)

# Grid wind directions, wind speeds and turbulence intensities to match the
Expand Down Expand Up @@ -2178,6 +2187,17 @@ def __init__(
if len(wind_directions) != len(values):
raise ValueError("wind_directions and values must be the same length")

# Confirm that none of wind_directions, wind_speeds, turbulence_intensitiess and
# values contain NaNs
if np.isnan(wind_directions).any():
raise ValueError("wind_directions must not contain NaNs")
if np.isnan(wind_speeds).any():
raise ValueError("wind_speeds must not contain NaNs")
if np.isnan(turbulence_intensities).any():
raise ValueError("turbulence_intensities must not contain NaNs")
if values is not None and np.isnan(values).any():
raise ValueError("values must not contain NaNs")

self.wind_directions = wind_directions
self.wind_speeds = wind_speeds
self.turbulence_intensities = turbulence_intensities
Expand Down Expand Up @@ -2985,7 +3005,7 @@ def _generate_wind_speed_frequencies_from_weibull(self, A, k, wind_speeds=None):
wind_speeds = self.wind_speeds
ws_steps = np.diff(wind_speeds)
if not np.all(np.isclose(ws_steps, ws_steps[0])):
raise ValueError("wind_speeds must be equally spaced.")
raise ValueError("wind_speeds must be equally spaced.")
else:
ws_step = ws_steps[0]

Expand Down
66 changes: 66 additions & 0 deletions tests/wind_data_integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1109,3 +1109,69 @@ def test_read_csv_long_ti():

expected_result = np.array([0.06, 0.07])
np.testing.assert_allclose(wind_ti_rose.turbulence_intensities, expected_result)


def test_wind_rose_nan_values():
Copy link
Collaborator

@misi9170 misi9170 Mar 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests don't seem to actually test the desired behavior. They pass on both the develop branch (without the new NaN check) and with the new code, because in both cases, a ValueError is raised.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've now fixed this by adding a match kwarg to pytest.raises()

wind_directions = np.array([0.0, 90.0, 180.0, 270.0])
wind_speeds = np.array([5.0, 10.0, 15.0])

WindRose(wind_directions, wind_speeds, ti_table=0.06)

# Introduce NaN values
wind_directions[1] = np.nan
with pytest.raises(ValueError):
WindRose(wind_directions, wind_speeds, ti_table=0.06)

wind_directions[1] = 90 # Reset
wind_speeds[1] = np.nan
with pytest.raises(ValueError):
WindRose(wind_directions, wind_speeds, ti_table=0.06)


def test_wind_ti_rose_nan_values():
wind_directions = np.array([0.0, 90.0, 180.0, 270.0])
wind_speeds = np.array([5.0, 10.0, 15.0])
turbulence_intensities = np.array([0.1, 0.2, 0.3])

WindTIRose(wind_directions, wind_speeds, turbulence_intensities)

# Introduce NaN values
wind_directions[1] = np.nan
with pytest.raises(ValueError):
WindTIRose(wind_directions, wind_speeds, turbulence_intensities)

wind_directions[1] = 90 # Reset
wind_speeds[1] = np.nan
with pytest.raises(ValueError):
WindTIRose(wind_directions, wind_speeds, turbulence_intensities)

wind_speeds[1] = 10 # Reset
turbulence_intensities[1] = np.nan
with pytest.raises(ValueError):
WindTIRose(wind_directions, wind_speeds, turbulence_intensities)


def test_time_series_nan_values():
wind_directions = np.array([0.0, 90.0, 180.0, 270.0])
wind_speeds = np.array([5.0, 10.0, 15.0])
turbulence_intensities = np.array([0.1, 0.2, 0.3, 0.4])
values = np.array([1.0, 2.0, 3.0, 4.0])

# Introduce NaN values
wind_directions[1] = np.nan
with pytest.raises(ValueError):
TimeSeries(wind_directions, wind_speeds, turbulence_intensities, values)

wind_directions[1] = 90 # Reset
wind_speeds[1] = np.nan
with pytest.raises(ValueError):
TimeSeries(wind_directions, wind_speeds, turbulence_intensities, values)

wind_speeds[1] = 10 # Reset
turbulence_intensities[1] = np.nan
with pytest.raises(ValueError):
TimeSeries(wind_directions, wind_speeds, turbulence_intensities, values)

values[1] = np.nan
with pytest.raises(ValueError):
TimeSeries(wind_directions, wind_speeds, turbulence_intensities, values)