Skip to content

Commit

Permalink
adding value functions to wind_data
Browse files Browse the repository at this point in the history
  • Loading branch information
ejsimley committed Mar 21, 2024
1 parent e31a4e9 commit e9cd449
Showing 1 changed file with 294 additions and 12 deletions.
306 changes: 294 additions & 12 deletions floris/wind_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -520,15 +520,19 @@ def plot_ti_over_ws(
Args:
ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes
on which the wind rose is plotted. Defaults to None.
plot_kwargs (dict, optional): Keyword arguments to be passed to
ax.plot().
on which the turbulence intensity is plotted. Defaults to None.
marker (str, optional): Scatter plot marker style. Defaults to ".".
ls (str, optional): Scatter plot line style. Defaults to "None".
color (str, optional): Scatter plot color. Defaults to "k".
Returns:
:py:class:`matplotlib.pyplot.axes`: A figure axes object containing
the plotted wind rose.
the plotted turbulence intensities as a function of wind speed.
"""

# TODO: Plot mean and std. devs. of TI in each ws bin in addition to
# individual points

# Set up figure
if ax is None:
_, ax = plt.subplots()
Expand All @@ -538,6 +542,110 @@ def plot_ti_over_ws(
ax.set_ylabel("Turbulence Intensity (%)")
ax.grid(True)

def assign_value_using_wd_ws_function(self, func, normalize=False):
"""
Use the passed in function to assign new values to the value table.
Args:
func (function): Function which accepts wind_directions as its
first argument and wind_speeds as second argument and returns
values.
normalize (bool, optional): If True, the value array will be
normalized by the mean value. Defaults to False.
"""
self.value_table = func(self.wd_grid, self.ws_grid)

if normalize:
self.value_table /= np.sum(self.freq_table * self.value_table)

self._build_gridded_and_flattened_version()

def assign_value_piecewise_linear(
self,
value_zero_ws=1.425,
ws_knee=4.5,
slope_1=0.0,
slope_2=-0.135,
limit_to_zero=False,
normalize=False,
):
"""
Define value as a continuous piecewise linear function of wind speed
with two line segments. The default parameters yield a value function
that approximates the normalized mean electricity price vs. wind speed
curve for the SPP market in the U.S. for years 2018-2020 from figure 7
in Simley et al. "The value of wake steering wind farm flow control in
US energy markets," Wind Energy Science, 2024.
https://doi.org/10.5194/wes-9-219-2024. This default value function is
constant at low wind speeds, then linearly decreases above 4.5 m/s.
Args:
value_zero_ws (float, optional): The value when wind speed is zero.
Defaults to 1.425.
ws_knee (float, optional): The wind speed separating line segments
1 and 2. Default = 4.5 m/s.
slope_1 (float, optional): The slope of the first line segment
(unit of value per m/s). Defaults to zero.
slope_2 (float, optional): The slope of the second line segment
(unit of value per m/s). Defaults to -0.135.
limit_to_zero (bool, optional): If True, negative values will be
set to zero. Defaults to False.
normalize (bool, optional): If True, the value array will be
normalized by the mean value. Defaults to False.
"""

def piecewise_linear_value_func(wind_directions, wind_speeds):
value = np.zeros_like(wind_speeds)
value[wind_speeds < ws_knee] = (
slope_1 * wind_speeds[wind_speeds < ws_knee] + value_zero_ws
)

offset_2 = (slope_1 - slope_2) * ws_knee + value_zero_ws

value[wind_speeds >= ws_knee] = slope_2 * wind_speeds[wind_speeds >= ws_knee] + offset_2

if limit_to_zero:
value[value < 0] = 0.0

return value

self.assign_value_using_wd_ws_function(piecewise_linear_value_func, normalize)

def plot_value_over_ws(
self,
ax=None,
marker=".",
ls="None",
color="k",
):
"""
Scatter plot the value of the energy generated against wind speed.
Args:
ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes
on which the value is plotted. Defaults to None.
marker (str, optional): Scatter plot marker style. Defaults to ".".
ls (str, optional): Scatter plot line style. Defaults to "None".
color (str, optional): Scatter plot color. Defaults to "k".
Returns:
:py:class:`matplotlib.pyplot.axes`: A figure axes object containing
the plotted value as a function of wind speed.
"""

# TODO: Plot mean and std. devs. of value in each ws bin in addition to
# individual points

# Set up figure
if ax is None:
_, ax = plt.subplots()

ax.plot(self.ws_flat, self.value_table_flat, marker=marker, ls=ls, color=color)
ax.set_xlabel("Wind Speed (m/s)")
ax.set_ylabel("Value")
ax.grid(True)


class WindTIRose(WindDataBase):
"""
Expand Down Expand Up @@ -877,16 +985,18 @@ def plot_ti_over_ws(
Args:
ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes
on which the wind rose is plotted. Defaults to None.
plot_kwargs (dict, optional): Keyword arguments to be passed to
ax.plot().
on which the mean turbulence intensity is plotted. Defaults to None.
marker (str, optional): Scatter plot marker style. Defaults to ".".
ls (str, optional): Scatter plot line style. Defaults to "None".
color (str, optional): Scatter plot color. Defaults to "k".
Returns:
:py:class:`matplotlib.pyplot.axes`: A figure axes object containing
the plotted wind rose.
the plotted mean turbulence intensities as a function of wind speed.
"""

# TODO: Plot std. devs. of TI in addition to mean values
# TODO: Plot individual points and std. devs. of TI in addition to mean
# values

# Set up figure
if ax is None:
Expand All @@ -901,6 +1011,111 @@ def plot_ti_over_ws(
ax.set_ylabel("Mean Turbulence Intensity (%)")
ax.grid(True)

def assign_value_using_wd_ws_ti_function(self, func, normalize=False):
"""
Use the passed in function to assign new values to the value table.
Args:
func (function): Function which accepts wind_directions as its
first argument, wind_speeds as its second argument, and
turbulence_intensities as its third argument and returns
values.
normalize (bool, optional): If True, the value array will be
normalized by the mean value. Defaults to False.
"""
self.value_table = func(self.wd_grid, self.ws_grid, self.ti_grid)

if normalize:
self.value_table /= np.sum(self.freq_table * self.value_table)

self._build_gridded_and_flattened_version()

def assign_value_piecewise_linear(
self,
value_zero_ws=1.425,
ws_knee=4.5,
slope_1=0.0,
slope_2=-0.135,
limit_to_zero=False,
normalize=False,
):
"""
Define value as a continuous piecewise linear function of wind speed
with two line segments. The default parameters yield a value function
that approximates the normalized mean electricity price vs. wind speed
curve for the SPP market in the U.S. for years 2018-2020 from figure 7
in Simley et al. "The value of wake steering wind farm flow control in
US energy markets," Wind Energy Science, 2024.
https://doi.org/10.5194/wes-9-219-2024. This default value function is
constant at low wind speeds, then linearly decreases above 4.5 m/s.
Args:
value_zero_ws (float, optional): The value when wind speed is zero.
Defaults to 1.425.
ws_knee (float, optional): The wind speed separating line segments
1 and 2. Default = 4.5 m/s.
slope_1 (float, optional): The slope of the first line segment
(unit of value per m/s). Defaults to zero.
slope_2 (float, optional): The slope of the second line segment
(unit of value per m/s). Defaults to -0.135.
limit_to_zero (bool, optional): If True, negative values will be
set to zero. Defaults to False.
normalize (bool, optional): If True, the value array will be
normalized by the mean value. Defaults to False.
"""

def piecewise_linear_value_func(wind_directions, wind_speeds, turbulence_intensities):
value = np.zeros_like(wind_speeds)
value[wind_speeds < ws_knee] = (
slope_1 * wind_speeds[wind_speeds < ws_knee] + value_zero_ws
)

offset_2 = (slope_1 - slope_2) * ws_knee + value_zero_ws

value[wind_speeds >= ws_knee] = slope_2 * wind_speeds[wind_speeds >= ws_knee] + offset_2

if limit_to_zero:
value[value < 0] = 0.0

return value

self.assign_value_using_wd_ws_ti_function(piecewise_linear_value_func, normalize)

def plot_value_over_ws(
self,
ax=None,
marker=".",
ls="None",
color="k",
):
"""
Scatter plot the value of the energy generated against wind speed.
Args:
ax (:py:class:`matplotlib.pyplot.axes`, optional): The figure axes
on which the value is plotted. Defaults to None.
marker (str, optional): Scatter plot marker style. Defaults to ".".
ls (str, optional): Scatter plot line style. Defaults to "None".
color (str, optional): Scatter plot color. Defaults to "k".
Returns:
:py:class:`matplotlib.pyplot.axes`: A figure axes object containing
the plotted value as a function of wind speed.
"""

# TODO: Plot mean and std. devs. of value in each ws bin in addition to
# individual points

# Set up figure
if ax is None:
_, ax = plt.subplots()

ax.plot(self.ws_flat, self.value_table_flat, marker=marker, ls=ls, color=color)
ax.set_xlabel("Wind Speed (m/s)")
ax.set_ylabel("Value")
ax.grid(True)


class TimeSeries(WindDataBase):
"""
Expand Down Expand Up @@ -966,9 +1181,8 @@ def __init__(
"wind_directions and wind_speeds must be the same length if provided as arrays"
)

if (
isinstance(wind_directions, np.ndarray)
and isinstance(turbulence_intensities, np.ndarray)
if isinstance(wind_directions, np.ndarray) and isinstance(
turbulence_intensities, np.ndarray
):
if len(wind_directions) != len(turbulence_intensities):
raise ValueError(
Expand Down Expand Up @@ -1124,6 +1338,74 @@ def iref_func(wind_directions, wind_speeds):

self.assign_ti_using_wd_ws_function(iref_func)

def assign_value_using_wd_ws_function(self, func, normalize=False):
"""
Use the passed in function to assign new values to the value table.
Args:
func (function): Function which accepts wind_directions as its
first argument and wind_speeds as second argument and returns
values.
normalize (bool, optional): If True, the value array will be
normalized by the mean value. Defaults to False.
"""
self.values = func(self.wind_directions, self.wind_speeds)

if normalize:
self.values /= np.mean(self.values)

def assign_value_piecewise_linear(
self,
value_zero_ws=1.425,
ws_knee=4.5,
slope_1=0.0,
slope_2=-0.135,
limit_to_zero=False,
normalize=False,
):
"""
Define value as a continuous piecewise linear function of wind speed
with two line segments. The default parameters yield a value function
that approximates the normalized mean electricity price vs. wind speed
curve for the SPP market in the U.S. for years 2018-2020 from figure 7
in Simley et al. "The value of wake steering wind farm flow control in
US energy markets," Wind Energy Science, 2024.
https://doi.org/10.5194/wes-9-219-2024. This default value function is
constant at low wind speeds, then linearly decreases above 4.5 m/s.
Args:
value_zero_ws (float, optional): The value when wind speed is zero.
Defaults to 1.425.
ws_knee (float, optional): The wind speed separating line segments
1 and 2. Default = 4.5 m/s.
slope_1 (float, optional): The slope of the first line segment
(unit of value per m/s). Defaults to zero.
slope_2 (float, optional): The slope of the second line segment
(unit of value per m/s). Defaults to -0.135.
limit_to_zero (bool, optional): If True, negative values will be
set to zero. Defaults to False.
normalize (bool, optional): If True, the value array will be
normalized by the mean value. Defaults to False.
"""

def piecewise_linear_value_func(wind_directions, wind_speeds):
value = np.zeros_like(wind_speeds)
value[wind_speeds < ws_knee] = (
slope_1 * wind_speeds[wind_speeds < ws_knee] + value_zero_ws
)

offset_2 = (slope_1 - slope_2) * ws_knee + value_zero_ws

value[wind_speeds >= ws_knee] = slope_2 * wind_speeds[wind_speeds >= ws_knee] + offset_2

if limit_to_zero:
value[value < 0] = 0.0

return value

self.assign_value_using_wd_ws_function(piecewise_linear_value_func, normalize)

def to_wind_rose(
self, wd_step=2.0, ws_step=1.0, wd_edges=None, ws_edges=None, bin_weights=None
):
Expand Down

0 comments on commit e9cd449

Please sign in to comment.