{
"cells": [
{
"cell_type": "markdown",
"id": "462e7245",
"metadata": {},
"source": [
"# Example 5: Getting Turbine and Farm Power"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "65083f30",
"metadata": {},
"outputs": [],
"source": [
"\"\"\"Example 5: Getting Turbine and Farm Power\n",
"\n",
"After setting the FlorisModel and running, the next step is typically to get the power output\n",
"of the turbines. FLORIS has several methods for getting power:\n",
"\n",
"1. `get_turbine_powers()`: Returns the power output of each turbine in the farm for each findex\n",
" (n_findex, n_turbines)\n",
"2. `get_farm_power()`: Returns the total power output of the farm for each findex (n_findex)\n",
"3. `get_expected_farm_power()`: Returns the combination of the farm power over each findex\n",
" with the frequency of each findex to get the expected farm power\n",
"4. `get_farm_AEP()`: Multiplies the expected farm power by the number of hours in a year to get\n",
" the expected annual energy production (AEP) of the farm\n",
"\n",
"\n",
"\"\"\"\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",
"\n",
"\n",
"fmodel = FlorisModel(\"inputs/gch.yaml\")\n",
"\n",
"# Set to a 3-turbine layout\n",
"fmodel.set(layout_x=[0, 126 * 5, 126 * 10], layout_y=[0, 0, 0])\n",
"\n",
"######################################################\n",
"# Using TimeSeries\n",
"######################################################\n",
"\n",
"# Set up a time series in which the wind speed and TI are constant but the wind direction\n",
"# sweeps the range from 250 to 290 degrees\n",
"wind_directions = np.arange(250, 290, 1.0)\n",
"time_series = TimeSeries(\n",
" wind_directions=wind_directions, wind_speeds=9.9, turbulence_intensities=0.06\n",
")\n",
"fmodel.set(wind_data=time_series)\n",
"\n",
"# Run the model\n",
"fmodel.run()\n",
"\n",
"# Get the turbine powers\n",
"turbine_powers = fmodel.get_turbine_powers()\n",
"\n",
"# Turbines powers will have shape (n_findex, n_turbines) where n_findex is the number of unique\n",
"# wind conditions and n_turbines is the number of turbines in the farm\n",
"print(f\"Turbine power has shape {turbine_powers.shape}\")\n",
"\n",
"# It is also possible to get the farm power directly\n",
"farm_power = fmodel.get_farm_power()\n",
"\n",
"# Farm power has length n_findex, and is the sum of the turbine powers\n",
"print(f\"Farm power has shape {farm_power.shape}\")\n",
"\n",
"# It's possible to get these powers with wake losses disabled, this can be useful\n",
"# for computing total wake losses\n",
"fmodel.run_no_wake()\n",
"farm_power_no_wake = fmodel.get_farm_power()\n",
"\n",
"# Plot the results\n",
"fig, axarr = plt.subplots(1, 3, figsize=(15, 5))\n",
"\n",
"# Plot the turbine powers\n",
"ax = axarr[0]\n",
"for i in range(turbine_powers.shape[1]):\n",
" ax.plot(wind_directions, turbine_powers[:, i] / 1e3, label=f\"Turbine {i+1} \")\n",
"ax.set_xlabel(\"Wind Direction (deg)\")\n",
"ax.set_ylabel(\"Power (kW)\")\n",
"ax.grid(True)\n",
"ax.legend()\n",
"ax.set_title(\"Turbine Powers\")\n",
"\n",
"# Plot the farm power\n",
"ax = axarr[1]\n",
"ax.plot(wind_directions, farm_power / 1e3, label=\"Farm Power With Wakes\", color=\"k\")\n",
"ax.plot(wind_directions, farm_power_no_wake / 1e3, label=\"Farm Power No Wakes\", color=\"r\")\n",
"ax.set_xlabel(\"Wind Direction (deg)\")\n",
"ax.set_ylabel(\"Power (kW)\")\n",
"ax.grid(True)\n",
"ax.legend()\n",
"ax.set_title(\"Farm Power\")\n",
"\n",
"# Plot the percent wake losses\n",
"ax = axarr[2]\n",
"percent_wake_losses = 100 * (farm_power_no_wake - farm_power) / farm_power_no_wake\n",
"ax.plot(wind_directions, percent_wake_losses, label=\"Percent Wake Losses\", color=\"k\")\n",
"ax.set_xlabel(\"Wind Direction (deg)\")\n",
"ax.set_ylabel(\"Percent Wake Losses\")\n",
"ax.grid(True)\n",
"ax.legend()\n",
"ax.set_title(\"Percent Wake Losses\")\n",
"\n",
"\n",
"######################################################\n",
"# Using WindRose\n",
"######################################################\n",
"\n",
"# When running FLORIS using a wind rose, that is when a WindRose or WindTIRose object is\n",
"# passed into the set function. The functions get_expected_farm_power and get_farm_AEP\n",
"# will operate the same as above, however the functions get_turbine_powers and get_farm_power\n",
"# will be reshaped from (n_findex, n_turbines) and\n",
"# (n_findex) to (n_wind_dir, n_wind_speed, n_turbines)\n",
"# and (n_wind_dir, n_wind_speed) respectively. This is make the powers align more easily with the\n",
"# provided wind rose.\n",
"\n",
"# Declare a WindRose object of 2 wind directions and 3 wind speeds and constant turbulence intensity\n",
"wind_rose = WindRose(\n",
" wind_directions=np.array([270.0, 280.0]), wind_speeds=np.array([8.0, 9.0, 10.0]), ti_table=0.06\n",
")\n",
"\n",
"fmodel.set(wind_data=wind_rose)\n",
"\n",
"print(\"==========Wind Rose==========\")\n",
"print(f\"Number of conditions to simulate (2 x 3): {fmodel.n_findex}\")\n",
"\n",
"fmodel.run()\n",
"\n",
"turbine_powers = fmodel.get_turbine_powers()\n",
"\n",
"print(f\"Shape of turbine powers: {turbine_powers.shape}\")\n",
"\n",
"farm_power = fmodel.get_farm_power()\n",
"\n",
"print(f\"Shape of farm power: {farm_power.shape}\")\n",
"\n",
"\n",
"# Plot the farm power\n",
"fig, ax = plt.subplots()\n",
"\n",
"for w_idx, wd in enumerate(wind_rose.wind_directions):\n",
" ax.plot(wind_rose.wind_speeds, farm_power[w_idx, :] / 1e3, label=f\"WD: {wd}\")\n",
"\n",
"ax.set_xlabel(\"Wind Speed (m/s)\")\n",
"ax.set_ylabel(\"Power (kW)\")\n",
"ax.grid(True)\n",
"ax.legend()\n",
"ax.set_title(\"Farm Power (from Wind Rose)\")\n",
"\n",
"plt.show()\n",
"import warnings\n",
"warnings.filterwarnings('ignore')"
]
}
],
"metadata": {},
"nbformat": 4,
"nbformat_minor": 5
}