Demonstrate energy ratio options#

The purpose of this notebook is to show some options in calculation of energy ratio

from pathlib import Path

import floris.layout_visualization as layoutviz
import matplotlib.pyplot as plt
import numpy as np
from floris import FlorisModel, TimeSeries

from flasc import FlascDataFrame
from flasc.analysis import energy_ratio as erp
from flasc.analysis.analysis_input import AnalysisInput
from flasc.data_processing import dataframe_manipulations as dfm

Generate dataset with FLORIS#

Use FLORIS to make a simple data set consisting of 4 turbines in a box, winds from the west, turbine 0/1 upstream, turbine 2/3 downstream

file_path = Path.cwd()
fm_path = file_path / "../floris_input_artificial/gch.yaml"
fm = FlorisModel(fm_path)
fm.set(layout_x=[0, 0, 5 * 126, 6 * 126], layout_y=[0, 5 * 126, 0, 5 * 126])

# # Show the wind farm
ax = layoutviz.plot_turbine_points(fm)
layoutviz.plot_turbine_labels(fm, ax=ax)
layoutviz.plot_waking_directions(fm, ax=ax)
ax.grid()
ax.set_xlabel("x coordinate [m]")
ax.set_ylabel("y coordinate [m]")
Text(0, 0.5, 'y coordinate [m]')
../../_images/0c80ac32135a7b5744d9dc6d956340a1df2abe4092c10e6e1ae725d2eff748a6.png
# Create a time history of points where the wind
# speed and wind direction step different combinations
ws_points = np.arange(5.0, 10.0, 1.0)
wd_points = np.arange(
    250.0,
    290.0,
    2,
)
num_points_per_combination = 5  # How many "seconds" per combination

# I know this is dumb but will come back, can't quite work out the numpy version
ws_array = []
wd_array = []
for ws in ws_points:
    for wd in wd_points:
        for i in range(num_points_per_combination):
            ws_array.append(ws)
            wd_array.append(wd)
t = np.arange(len(ws_array))
wd_array = np.array(wd_array)
ws_array = np.array(ws_array)

print(f"Num Points {len(t)}")

fig, axarr = plt.subplots(2, 1, sharex=True)
axarr[0].plot(t, ws_array, label="Wind Speed")
axarr[0].set_ylabel("m/s")
axarr[0].legend()
axarr[0].grid(True)
axarr[1].plot(t, wd_array, label="Wind Direction")
axarr[1].set_ylabel("deg")
axarr[1].legend()
axarr[1].grid(True)
Num Points 500
../../_images/4dadec2dacebd5080bd3133eaa9ecd660b2482b238af8f952189f6b5018117b0.png
# Compute the power of the second turbine for two cases
# Baseline: The front turbine is aligned to the wind
# WakeSteering: The front turbine is yawed 25 deg
fm.set(
    wind_data=TimeSeries(
        wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=0.06
    )
)
fm.run()

# Collect the turbine powers
power_0 = fm.get_turbine_powers()[:, 0].flatten() / 1000.0
power_1 = fm.get_turbine_powers()[:, 1].flatten() / 1000.0
power_2 = fm.get_turbine_powers()[:, 2].flatten() / 1000.0
power_3 = fm.get_turbine_powers()[:, 3].flatten() / 1000.0

# Assume all turbine measure wind direction with some noise
wd_0 = wd_array + np.random.randn(len(wd_array)) * 2
wd_1 = wd_array + np.random.randn(len(wd_array)) * 2
wd_2 = wd_array + np.random.randn(len(wd_array)) * 2
wd_3 = wd_array + np.random.randn(len(wd_array)) * 2

# Only collect the wind speeds of the upstream turbines
ws_0 = ws_array + np.random.randn(len(wd_array)) * 1
ws_1 = ws_array + np.random.randn(len(wd_array)) * 1
/opt/hostedtoolcache/Python/3.14.3/x64/lib/python3.14/site-packages/floris/core/flow_field.py:169: UserWarning: 'where' used without 'out', expect unitialized memory in output. If this is intentional, use out=None.
  * np.power(
# Now build the dataframe
df = FlascDataFrame(
    {
        "pow_000": power_0,
        "pow_001": power_1,
        "pow_002": power_2,
        "pow_003": power_3,
        "ws_000": ws_0,
        "ws_001": ws_1,
        "wd_000": wd_0,
        "wd_001": wd_1,
        "wd_002": wd_2,
        "wd_003": wd_3,
    }
)
# Build the energy ratio input
a_in = AnalysisInput([df], ["baseline"], num_blocks=10)
# Calculate and plot the energy ratio of turbine 2 with respect to
# turbine 0, using turbine 0's measurements of wind speed and wind direction
er_out = erp.compute_energy_ratio(
    a_in, test_turbines=[2], ref_turbines=[0], ws_turbines=[0], wd_turbines=[0], N=50
)
er_out.plot_energy_ratios()
array([<Axes: title={'center': 'Energy Ratio'}, ylabel='Energy Ratio'>,
       <Axes: title={'center': 'Number of Points per Bin'}, ylabel='Number of Points'>,
       <Axes: title={'center': 'Minimum Number of Points per Bin'}, xlabel='Wind Direction (deg)', ylabel='Wind Speed (m/s)'>],
      dtype=object)
../../_images/c243985ba1ef0faa196f8f81934ae6d5af175fbc0fb29104ea5ce89df83d866d.png
# Reverse the above calculation showing the energy ratio of T0 / T2,
# letting T1 supply wind speed and direction
er_out = erp.compute_energy_ratio(
    a_in, test_turbines=[0], ref_turbines=[2], ws_turbines=[1], wd_turbines=[1], N=50
)
er_out.plot_energy_ratios()
array([<Axes: title={'center': 'Energy Ratio'}, ylabel='Energy Ratio'>,
       <Axes: title={'center': 'Number of Points per Bin'}, ylabel='Number of Points'>,
       <Axes: title={'center': 'Minimum Number of Points per Bin'}, xlabel='Wind Direction (deg)', ylabel='Wind Speed (m/s)'>],
      dtype=object)
../../_images/b2b3bb7849f6623e477f60fd76b5c10d8142d705f913b1db468ff5e176946cd9.png
# Overplot the energy ratios of turbine 2 and 3, with respect to the averages of turbines 0 and 1
er_out_2 = erp.compute_energy_ratio(
    a_in, test_turbines=[2], ref_turbines=[0, 1], ws_turbines=[0, 1], wd_turbines=[0, 1], N=50
)

er_out_3 = erp.compute_energy_ratio(
    a_in, test_turbines=[3], ref_turbines=[0, 1], ws_turbines=[0, 1], wd_turbines=[0, 1], N=50
)

fig, axarr = plt.subplots(3, 1, sharex=True, figsize=(8, 11))

er_out_2.plot_energy_ratios(axarr=axarr, labels="T2")
er_out_3.plot_energy_ratios(
    axarr=axarr[0],
    show_wind_direction_distribution=False,
    show_wind_speed_distribution=False,
    labels="T3",
    color_dict={"T3": "r"},
)
<Axes: title={'center': 'Energy Ratio'}, xlabel='Wind Direction (deg)', ylabel='Energy Ratio'>
../../_images/c919576b18bfd83d7eba6aefed4d45ba395c4faf9263cd5668d4897e92213ceb.png

Illustrating pre-computing reference wind speed, direction and power#

# Use the FLASC function for defining wind speed and direction via upstream turbines

df = dfm.set_wd_by_all_turbines(df)
df = dfm.set_ws_by_turbines(df, [0.1])
df = dfm.set_pow_ref_by_turbines(df, [0.1])

df.head()
FlascDataFrame in user (wide) format
pow_000 pow_001 pow_002 pow_003 ws_000 ws_001 wd_000 wd_001 wd_002 wd_003 wd ws pow_ref
0 400.180232 400.180232 400.053966 400.171201 6.398087 5.347404 246.578415 249.616188 246.792455 250.477679 248.366162 6.398087 400.180232
1 400.180232 400.180232 400.053966 400.171201 4.924014 4.227719 250.789450 254.459099 251.994894 250.212121 251.863742 4.924014 400.180232
2 400.180232 400.180232 400.053966 400.171201 5.432284 7.136335 251.320316 248.065936 249.199280 250.687944 249.818389 5.432284 400.180232
3 400.180232 400.180232 400.053966 400.171201 6.547193 4.280995 251.712198 249.904597 250.329453 251.867612 250.953467 6.547193 400.180232
4 400.180232 400.180232 400.053966 400.171201 3.919828 4.409893 250.747811 253.526250 249.161604 248.179465 250.403559 3.919828 400.180232
# Now use the predefined values in the calculation of the average of turbines 2 and 3

a_in = AnalysisInput([df], ["baseline"], num_blocks=10)

er_out = erp.compute_energy_ratio(
    a_in,
    test_turbines=[2, 3],
    use_predefined_ref=True,
    use_predefined_wd=True,
    use_predefined_ws=True,
    N=50,
)
er_out.plot_energy_ratios()
array([<Axes: title={'center': 'Energy Ratio'}, ylabel='Energy Ratio'>,
       <Axes: title={'center': 'Number of Points per Bin'}, ylabel='Number of Points'>,
       <Axes: title={'center': 'Minimum Number of Points per Bin'}, xlabel='Wind Direction (deg)', ylabel='Wind Speed (m/s)'>],
      dtype=object)
../../_images/f9b2e341fe262ae5e0e4f16831d8447555dc39e9b4d21930cb6536e7b11ebf01.png