{
"cells": [
{
"cell_type": "markdown",
"id": "a40fa92e",
"metadata": {},
"source": [
"# Example: Optimize yaw and compare AEP"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b68adae7",
"metadata": {},
"outputs": [],
"source": [
"\"\"\"Example: Optimize yaw and compare AEP\n",
"\n",
"This example demonstrates how to perform a yaw optimization and evaluate the performance\n",
"over a full wind rose.\n",
"\n",
"The script performs the following steps:\n",
" 1. Load a wind rose from a csv file\n",
" 2. Calculates the optimal yaw angles for a wind speed of 8 m/s across the directions\n",
" 3. Applies the optimal yaw angles to the wind rose and calculates the AEP\n",
"\n",
"\"\"\"\n",
"\n",
"from time import perf_counter as timerpc\n",
"\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"\n",
"from floris import (\n",
" FlorisModel,\n",
" TimeSeries,\n",
" WindRose,\n",
")\n",
"from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR\n",
"\n",
"\n",
"# Load the wind rose from csv\n",
"wind_rose = WindRose.read_csv_long(\n",
" \"../inputs/wind_rose.csv\", wd_col=\"wd\", ws_col=\"ws\", freq_col=\"freq_val\", ti_col_or_value=0.06\n",
")\n",
"\n",
"# Load FLORIS\n",
"fmodel = FlorisModel(\"../inputs/gch.yaml\")\n",
"\n",
"# Specify wind farm layout and update in the floris object\n",
"N = 2 # number of turbines per row and per column\n",
"X, Y = np.meshgrid(\n",
" 5.0 * fmodel.core.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1),\n",
" 5.0 * fmodel.core.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1),\n",
")\n",
"fmodel.set(layout_x=X.flatten(), layout_y=Y.flatten())\n",
"\n",
"# Get the number of turbines\n",
"n_turbines = len(fmodel.layout_x)\n",
"\n",
"# Optimize the yaw angles. This could be done for every wind direction and wind speed\n",
"# but in practice it is much faster to optimize only for one speed and infer the rest\n",
"# using a rule of thumb\n",
"time_series = TimeSeries(\n",
" wind_directions=wind_rose.wind_directions, wind_speeds=8.0, turbulence_intensities=0.06\n",
")\n",
"fmodel.set(wind_data=time_series)\n",
"\n",
"# Get the optimal angles\n",
"start_time = timerpc()\n",
"yaw_opt = YawOptimizationSR(\n",
" fmodel=fmodel,\n",
" minimum_yaw_angle=0.0, # Allowable yaw angles lower bound\n",
" maximum_yaw_angle=20.0, # Allowable yaw angles upper bound\n",
" Ny_passes=[5, 4],\n",
" exclude_downstream_turbines=True,\n",
")\n",
"df_opt = yaw_opt.optimize()\n",
"end_time = timerpc()\n",
"t_tot = end_time - start_time\n",
"print(\"Optimization finished in {:.2f} seconds.\".format(t_tot))\n",
"\n",
"\n",
"# Calculate the AEP in the baseline case\n",
"fmodel.set(wind_data=wind_rose)\n",
"fmodel.run()\n",
"farm_power_baseline = fmodel.get_farm_power()\n",
"aep_baseline = fmodel.get_farm_AEP()\n",
"\n",
"\n",
"# Now need to apply the optimal yaw angles to the wind rose to get the optimized AEP\n",
"# do this by applying a rule of thumb where the optimal yaw is applied between 6 and 12 m/s\n",
"# and ramped down to 0 above and below this range\n",
"\n",
"# Grab wind speeds and wind directions from the fmodel. Note that we do this because the\n",
"# yaw angles will need to be n_findex long, and accounting for the fact that some wind\n",
"# directions and wind speeds may not be present in the wind rose (0 frequency) and aren't\n",
"# included in the fmodel\n",
"wind_directions = fmodel.wind_directions\n",
"wind_speeds = fmodel.wind_speeds\n",
"n_findex = fmodel.n_findex\n",
"\n",
"\n",
"# Now define how the optimal yaw angles for 8 m/s are applied over the other wind speeds\n",
"yaw_angles_opt = np.vstack(df_opt[\"yaw_angles_opt\"])\n",
"yaw_angles_wind_rose = np.zeros((n_findex, n_turbines))\n",
"for i in range(n_findex):\n",
" wind_speed = wind_speeds[i]\n",
" wind_direction = wind_directions[i]\n",
"\n",
" # Interpolate the optimal yaw angles for this wind direction from df_opt\n",
" id_opt = df_opt[\"wind_direction\"] == wind_direction\n",
" yaw_opt_full = np.array(df_opt.loc[id_opt, \"yaw_angles_opt\"])[0]\n",
"\n",
" # Now decide what to do for different wind speeds\n",
" if (wind_speed < 4.0) | (wind_speed > 14.0):\n",
" yaw_opt = np.zeros(n_turbines) # do nothing for very low/high speeds\n",
" elif wind_speed < 6.0:\n",
" yaw_opt = yaw_opt_full * (6.0 - wind_speed) / 2.0 # Linear ramp up\n",
" elif wind_speed > 12.0:\n",
" yaw_opt = yaw_opt_full * (14.0 - wind_speed) / 2.0 # Linear ramp down\n",
" else:\n",
" yaw_opt = yaw_opt_full # Apply full offsets between 6.0 and 12.0 m/s\n",
"\n",
" # Save to collective array\n",
" yaw_angles_wind_rose[i, :] = yaw_opt\n",
"\n",
"\n",
"# Now apply the optimal yaw angles and get the AEP\n",
"fmodel.set(yaw_angles=yaw_angles_wind_rose)\n",
"fmodel.run()\n",
"aep_opt = fmodel.get_farm_AEP()\n",
"aep_uplift = 100.0 * (aep_opt / aep_baseline - 1)\n",
"farm_power_opt = fmodel.get_farm_power()\n",
"\n",
"print(\"Baseline AEP: {:.2f} GWh.\".format(aep_baseline/1E9))\n",
"print(\"Optimal AEP: {:.2f} GWh.\".format(aep_opt/1E9))\n",
"print(\"Relative AEP uplift by wake steering: {:.3f} %.\".format(aep_uplift))\n",
"\n",
"# Use farm_power_baseline, farm_power_opt and wind_data to make a heat map of uplift by\n",
"# wind direction and wind speed\n",
"wind_directions = wind_rose.wind_directions\n",
"wind_speeds = wind_rose.wind_speeds\n",
"relative_gain = farm_power_opt - farm_power_baseline\n",
"\n",
"# Plot the heatmap with wind speeds on x, wind directions on y and relative gain as the color\n",
"fig, ax = plt.subplots(figsize=(10, 12))\n",
"cax = ax.imshow(relative_gain, cmap='viridis', aspect='auto')\n",
"fig.colorbar(cax, ax=ax, label=\"Relative gain (%)\")\n",
"\n",
"ax.set_yticks(np.arange(len(wind_directions)))\n",
"ax.set_yticklabels(wind_directions)\n",
"ax.set_xticks(np.arange(len(wind_speeds)))\n",
"ax.set_xticklabels(wind_speeds)\n",
"ax.set_ylabel(\"Wind direction (deg)\")\n",
"ax.set_xlabel(\"Wind speed (m/s)\")\n",
"\n",
"# Reduce x and y tick font size\n",
"for tick in ax.yaxis.get_major_ticks():\n",
" tick.label1.set_fontsize(8)\n",
"\n",
"for tick in ax.xaxis.get_major_ticks():\n",
" tick.label1.set_fontsize(8)\n",
"\n",
"# Set y ticks to be horizontal\n",
"for tick in ax.get_yticklabels():\n",
" tick.set_rotation(0)\n",
"\n",
"ax.set_title(\"Uplift in farm power by wind direction and wind speed\", fontsize=12)\n",
"\n",
"plt.tight_layout()\n",
"plt.show()\n",
"import warnings\n",
"warnings.filterwarnings('ignore')"
]
}
],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 5
}