Example: Optimize yaw for multiple wind directions and multiple wind speeds.#
This example demonstrates how to perform a yaw optimization for multiple wind directionsand multiple wind speeds using the WindRose objectFirst, we initialize our Floris Interface, and then generate a 3 turbine wind farm.Next, we create the yaw optimization object yaw_opt
and perform the optimization usingthe SerialRefine method. Finally, we plot the results.
import matplotlib.pyplot as plt
import numpy as np
from floris import FlorisModel, WindRose
from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR
# Load the default example floris object
fmodel = FlorisModel("../inputs/gch.yaml") # GCH model matched to the default "legacy_gauss" of V2
# fmodel = FlorisModel("inputs/cc.yaml") # New CumulativeCurl model
# Define a WindRose object with uniform TI and frequency table
wind_rose = WindRose(
wind_directions=np.arange(0.0, 360.0, 3.0),
wind_speeds=np.arange(2.0, 18.0, 1.0),
ti_table=0.06,
)
# Reinitialize as a 3-turbine farm with range of WDs and WSs
D = 126.0 # Rotor diameter for the NREL 5 MW
fmodel.set(
layout_x=[0.0, 5 * D, 10 * D],
layout_y=[0.0, 0.0, 0.0],
wind_data=wind_rose,
)
# Initialize optimizer object and run optimization using the Serial-Refine method
# Now, we enable the verify_convergence option. This function is useful to prevent
# yaw misalignment that increases the wind farm power production by a negligible
# amount. For example, at high wind speeds (e.g., 16 m/s), a turbine might yaw
# by a substantial amount to increase the power production by less than 1 W. This
# is typically the result of numerical imprecision of the power coefficient curve,
# which slightly differs for different above-rated wind speeds. The option
# verify_convergence therefore refines and validates the yaw angle choices
# but has no effect on the predicted power uplift from wake steering.
# Hence, it should mostly be used when actually synthesizing a practicable
# wind farm controller.
yaw_opt = YawOptimizationSR(fmodel)
df_opt = yaw_opt.optimize()
print("Optimization results:")
print(df_opt)
# Split out the turbine results
for t in range(3):
df_opt['t%d' % t] = df_opt.yaw_angles_opt.apply(lambda x: x[t])
# Show the results: optimal yaw angles
fig, axarr = plt.subplots(
nrows=4,
ncols=4,
sharex=True,
sharey=True,
figsize=(10, 8)
)
jj = 0
for ii, ws in enumerate(np.unique(fmodel.wind_speeds)):
xi = np.remainder(ii, 4)
if ((ii > 0) & (xi == 0)):
jj += 1
ax = axarr[np.remainder(ii, 4)][jj]
ids = (df_opt.wind_speed == ws)
wd = df_opt.loc[ids, "wind_direction"]
for t in range(3):
yaw_opt = df_opt.loc[ids, "t{:d}".format(t)]
ax.plot(wd, yaw_opt, label='Turbine {:d}'.format(t))
ax.set_title("Wind speed = {:.1f} m/s".format(ws), size=10)
if ((ii == 0) & (jj == 0)):
ax.legend()
ax.grid(True)
if jj == 0:
ax.set_ylabel('Yaw angle (deg)', size=10)
if xi == 3:
axarr[xi][jj].set_xlabel('Wind Direction (deg)', size=10)
plt.tight_layout()
# Show the results: baseline and optimized farm power
fig, axarr = plt.subplots(
nrows=4,
ncols=4,
sharex=True,
sharey=True,
figsize=(10, 8)
)
jj = 0
for ii, ws in enumerate(np.unique(fmodel.wind_speeds)):
xi = np.remainder(ii, 4)
if ((ii > 0) & (xi == 0)):
jj += 1
ax = axarr[np.remainder(ii, 4)][jj]
ids = (df_opt.wind_speed == ws)
wd = df_opt.loc[ids, "wind_direction"]
power_baseline = df_opt.loc[ids, "farm_power_baseline"]
power_opt = df_opt.loc[ids, "farm_power_opt"]
ax.plot(wd, power_baseline / 1e6, color='k', label='Baseline')
ax.plot(wd, power_opt / 1e6, color='r', label='Optimized')
ax.set_title("Wind speed = {:.1f} m/s".format(ws), size=10)
ax.set_ylim([0.0, 16.0])
if ((ii == 0) & (jj == 0)):
ax.legend()
ax.grid(True)
if jj == 0:
ax.set_ylabel('Farm Power (MW)', size=10)
if xi == 3:
axarr[xi][jj].set_xlabel('Wind Direction (deg)', size=10)
plt.tight_layout()
plt.show()
import warnings
warnings.filterwarnings('ignore')
[Serial Refine] Processing pass=0, turbine_depth=0 (0.0%)
[Serial Refine] Processing pass=0, turbine_depth=1 (16.7%)
[Serial Refine] Processing pass=0, turbine_depth=2 (33.3%)
[Serial Refine] Processing pass=1, turbine_depth=0 (50.0%)
[Serial Refine] Processing pass=1, turbine_depth=1 (66.7%)
[Serial Refine] Processing pass=1, turbine_depth=2 (83.3%)
Optimization results:
wind_direction wind_speed turbulence_intensity yaw_angles_opt \
0 0.0 2.0 0.06 [0.0, 0.0, 0.0]
1 0.0 3.0 0.06 [0.0, 0.0, 0.0]
2 0.0 4.0 0.06 [0.0, 0.0, 0.0]
3 0.0 5.0 0.06 [0.0, 0.0, 0.0]
4 0.0 6.0 0.06 [0.0, 0.0, 0.0]
... ... ... ... ...
1915 357.0 13.0 0.06 [0.0, 0.0, 0.0]
1916 357.0 14.0 0.06 [0.0, 0.0, 0.0]
1917 357.0 15.0 0.06 [0.0, 0.0, 0.0]
1918 357.0 16.0 0.06 [0.0, 0.0, 0.0]
1919 357.0 17.0 0.06 [0.0, 0.0, 0.0]
farm_power_opt farm_power_baseline
0 0.000000e+00 0.000000e+00
1 1.095593e+05 1.095593e+05
2 5.276012e+05 5.276012e+05
3 1.200541e+06 1.200541e+06
4 2.193010e+06 2.193010e+06
... ... ...
1915 1.500000e+07 1.500000e+07
1916 1.500000e+07 1.500000e+07
1917 1.500000e+07 1.500000e+07
1918 1.500000e+07 1.500000e+07
1919 1.500000e+07 1.500000e+07
[1920 rows x 6 columns]