NREL / floris

A controls-oriented engineering wake model.
http://nrel.github.io/floris
BSD 3-Clause "New" or "Revised" License
214 stars 156 forks source link

Allow TI to impact turbine power production #785

Open paulf81 opened 9 months ago

paulf81 commented 9 months ago

Allow TI to impact turbine power production

In V4, TI can be set per time step in TimeSeries or wd/ws bin in WindRose. It would be useful to reflect TI impact on power production of a turbine, separate of wake model impacts. Talking this over with @ejsimley we think we have a way forward with 90% of the details in mind. Here is the proposed solution to implement:

  # define wind speed, ti, and power curve components
  ws = np.array(self.power_thrust_table["wind_speed"])
  cp = np.array(self.power_thrust_table["power"])
  ws = ws[np.where(cp != 0)]
  ciws = ws[0]  # cut in wind speed
  cows = ws[len(ws) - 1]  # cut out wind speed
  speed = self.average_velocity
  ti = self.current_turbulence_intensity

  if ciws >= speed or cows <= speed or ti == 0.0 or math.isnan(speed):
      return 1.0
  else:
      # define mean and standard deviation to create normalized pdf with sum = 1
      mu = speed
      sigma = ti * mu
      if mu + sigma >= cows:
          xp = np.linspace((mu - sigma), cows, 100)
      else:
          xp = np.linspace((mu - sigma), (mu + sigma), 100)
      pdf = norm.pdf(xp, mu, sigma)
      npdf = np.array(pdf) * (1 / np.sum(pdf))

      # calculate turbulence parameter (ratio of corrected power to original power)
      return np.sum([npdf[k] * self.powInterp(xp[k]) for k in range(100)]) / (
          self.powInterp(mu)
      )

@ejsimley @misi9170 @rafmudaf : Thought I'd start with this issue before starting the pull request so we have a place to iron out the intention before starting. I will link this issue to the card in the project board.

Bartdoekemeijer commented 9 months ago

Hi @paulf81. Nice idea! This approach makes sense if you only have the aeroelastic curves defined for one turbulence intensity. What if we have multiple aeroelastic performance curves for a range of turbulence intensities? This is not uncommon. I would love a solution where we can use this information directly in FLORIS when we have it.

paulf81 commented 9 months ago

Thank you for pointing this out! @misi9170 maybe you and I can discuss some more about this. The proposal I had based on the v2 code is using a blurring effect, but if we have pre-defined curves for different turbulence intensities these should route through the multi-dim code right?

misi9170 commented 9 months ago

Thank you for pointing this out! @misi9170 maybe you and I can discuss some more about this. The proposal I had based on the v2 code is using a blurring effect, but if we have pre-defined curves for different turbulence intensities these should route through the multi-dim code right?

Yes, that sounds like a good use case for multidim, and should be straightforward to implement that way. If we are going to do it that way, we should probably avoid supporting TI variations in a new (TI-specific) operation model. I think we could still go a little further to align the multidim model with the operation model paradigm.

paulf81 commented 9 months ago

Thank you for pointing this out! @misi9170 maybe you and I can discuss some more about this. The proposal I had based on the v2 code is using a blurring effect, but if we have pre-defined curves for different turbulence intensities these should route through the multi-dim code right?

Yes, that sounds like a good use case for multidim, and should be straightforward to implement that way. If we are going to do it that way, we should probably avoid supporting TI variations in a new (TI-specific) operation model. I think we could still go a little further to align the multidim model with the operation model paradigm.

I agree, this should be across operation models, but we can support handling TI impact on power/thrust 2 ways:

1) Via gaussian blending based on ws_std 2) Via multi-dim

And both of these could apply in all operation models

paulf81 commented 8 months ago

Just adding some references on standard approaches for later:

https://www.researchgate.net/profile/Axel-Albers/publication/279586111_Turbulence_and_shear_normalisation_of_wind_turbine_power_curve/links/5a26cb6c0f7e9b71dd0c7dee/Turbulence-and-shear-normalisation-of-wind-turbine-power-curve.pdf

https://link.springer.com/content/pdf/10.1007/978-3-540-33866-6_28.pdf

https://hal.science/hal-03646791/document

https://pcwg.org/consensus/Consensus%20Analysis%20Turbulence%20Renormalisation%20Documentation.pdf

Bartdoekemeijer commented 1 month ago

Hi, I'm trying to do this now with via the multidim functionality, but not succeeding. Has this not yet been resolved, or what am I doing wrong?

Here's a minimal example:

import numpy as np
import os
import pandas as pd

from floris import FlorisModel

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

    # Now create a multidimensional power thrust table
    pow_thrust_table = fmodel.core.farm.turbine_definitions[0]

    # Take nominal NREL 5MW curves
    wind_speed = pow_thrust_table["power_thrust_table"]["wind_speed"]
    power = pow_thrust_table["power_thrust_table"]["power"]
    thrust_coefficient = pow_thrust_table["power_thrust_table"]["thrust_coefficient"]

    # Turn into TI-dependent curves
    df_list = []
    for ti in [0.04, 0.06, 0.08, 0.10]:
        dict_add = {
            "turbulence_intensity": ti * np.ones_like(wind_speed),
            "ws": wind_speed,
            "power": power,
            "thrust_coefficient": thrust_coefficient
        }
        df_list.append(pd.DataFrame(dict_add))
    df_multidim = pd.concat(df_list, axis=0).reset_index(drop=True)

    # Save locally
    root_path = os.path.dirname(os.path.abspath(__file__))
    fn_csv = os.path.join(root_path, "nrel5mw_ti_dependent_power_thrust_table.csv")
    df_multidim.to_csv(fn_csv, index=False)
    print(f"Multidimensional turbine aerolastic table saved to '{fn_csv}'")

    # Now add multidimensional table to first turbine in FLORIS
    turbine_definitions = fmodel.core.farm.turbine_definitions
    turbine_definitions[0]["turbine_type"] = "NREL5MW_tiDependentCpCt"
    turbine_definitions[0]["multi_dimensional_cp_ct"] = True
    turbine_definitions[0]["power_thrust_table"]["power_thrust_data_file"] = fn_csv
    fmodel.set(turbine_type=turbine_definitions)

    # Now run with multidimensional table
    fmodel.run()
paulf81 commented 1 month ago

Hi @Bartdoekemeijer , thank you for flagging this, I think this is indeed in need of finishing. I'm going to a seperate issue to propose an action list to get this completed.

paulf81 commented 1 month ago

Ok added #989 to put this plan into action