NREL / flasc

A rich floris-driven suite for SCADA analysis
https://nrel.github.io/flasc/
BSD 3-Clause "New" or "Revised" License
32 stars 18 forks source link

[BUG] Unintended behavior ti_array=None in get_yaw_angles_interpolant #207

Open Bartdoekemeijer opened 3 months ago

Bartdoekemeijer commented 3 months ago

Is there an existing issue for this?

Current Behavior

The default option when ti_array=None in get_yaw_angles_interpolant function is ambigious.

Detailed explanation: The currently workflow for yaw optimizations on my end is something as follows:

  1. You create a WindRose object where each wind direction and wind speed combination has one unique turbulence intensity.
  2. You optimize the yaw angles and come up with a Pandas dataframe with df.shape[0] = n_findex = (n_wind_directions * n_wind_speeds), with one corresponding ambient turbulence intensity value for each findex and one unique set of optimal yaw angles for each findex.
  3. When calculating the AEP, you use the get_yaw_angles_interpolant function from FLASC to calculate the optimal yaw angles for each findex.

However, there is an ambiguity between step 2 and step 3. For example, take the scenario where wind_direction=270.0, wind_speed=8.0 and turbulence_intensity=0.0613. After optimization, you get one set of optimal yaw angles corresponding to this turbulence intensity.

When interpolating for the optimal yaw angles using get_yaw_angles_interpolant(), if you use

a = get_yaw_angles_interpolant(270.0, 8.0)
b = get_yaw_angles_interpolant(270.0, 8.0, 0.0613)

I would think a and b should give the same solution, but they do not. This is because, when leaving the turbulence intensity unspecified, the interpolant function will assume the median turbulence intensity of all findices, rather than the turbulence intensity matching that specific wind direction and speed.

The easiest solution is to force users to specify ti_array in the interpolant_with_ramps() function.

Expected Behavior

I would expect a and b to produce identical results.

Steps To Reproduce

Here's a minimal reproducible example:

import numpy as np

from floris import FlorisModel, WindRose
from flasc.utilities.lookup_table_tools import get_yaw_angles_interpolant
from floris.optimization.yaw_optimization.yaw_optimizer_sr import YawOptimizationSR

if __name__ == "__main__":
    # Set-up a FLORIS model
    fmodel = FlorisModel("inputs/gch.yaml")

    # Setup a turbulence intensity function
    def ti_function(wd, ws):
        ti = (0.05 + 0.4 / ((ws+0.02)**1.5))
        ti = np.clip(ti, a_min=0.01, a_max=0.20)
        return ti

    # Set-up a wind rose
    wd_grid, ws_grid = np.meshgrid(
        np.arange(268.0, 272.0, 2.0),
        np.arange(9.0, 14.0, 1.0),
    )
    ti_grid = ti_function(wd_grid, ws_grid)
    wind_rose = WindRose(
        wind_directions=wd_grid[0, :],
        wind_speeds=ws_grid[:, 0],
        ti_table=ti_grid.T,
    )

    # Now optimize AEP
    fmodel.set(wind_data=wind_rose)
    sr_opt = YawOptimizationSR(fmodel)
    df_opt = sr_opt.optimize()

    # Print yaw angles for all optimized conditions
    print("Optimal solutions from yaw optimization")
    print(df_opt)

    # Set up interpolant
    yaw_interp = get_yaw_angles_interpolant(df_opt=df_opt)

    # Grab optimal value for one condition
    single_yaw_angles_opt = yaw_interp(270.0, 10.0)

    print(f"df_opt:           Optimal yaw angles for 270.0 deg, 10 m/s: {df_opt.loc[6, 'yaw_angles_opt']}")
    print(f"yaw_interpolant:  Optimal yaw angles for 270.0 deg, 10 m/s: {single_yaw_angles_opt}")

And results in:

df_opt:           Optimal yaw angles for 270.0 deg, 10 m/s: [25.      24.21875  0.     ]
yaw_interpolant:  Optimal yaw angles for 270.0 deg, 10 m/s: [25.         24.22371214  0.        ]

Admittedly, the difference is small. Though this error may become bigger when looking at sites with stronger turbulence intensity fluctuations.

Environment

- OS: Ubuntu 22 LTS
- pip environment (can be retrieved with `pip list`):
    - `flasc==2.0.1`
    - `numpy==1.26.4`
    - `scipy==1.14.0`

Anything else?

No response