Skip to content

Commit

Permalink
adding functions and tests to set wind direction by upstream turbines
Browse files Browse the repository at this point in the history
  • Loading branch information
ejsimley committed Nov 8, 2024
1 parent ec11cfe commit 1427fa6
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
119 changes: 119 additions & 0 deletions flasc/data_processing/dataframe_manipulations.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,59 @@ def set_wd_by_all_turbines(
return _set_col_by_turbines("wd", "wd", df, "all", True)


def set_wd_by_upstream_turbines(
df: Union[pd.DataFrame, FlascDataFrame], df_upstream, exclude_turbs=[]
) -> Union[pd.DataFrame, FlascDataFrame]:
"""Add wind direction column using upstream turbines.
Add a column called 'wd' in your dataframe with value equal
to the averaged wind direction measurements of all the turbines
upstream, excluding the turbines listed in exclude_turbs. As an
intermediate step, the average wind direction over all turbines
is used to determine the set of upstream turbines from which the
final wind direction signal is derived.
Args:
df (pd.DataFrame | FlascDataFrame): Dataframe with measurements. This dataframe
typically consists of wd_%03d, ws_%03d, ti_%03d, pow_%03d, and
potentially additional measurements.
df_upstream (pd.DataFrame): Dataframe containing rows indicating
wind direction ranges and the corresponding upstream turbines for
that wind direction range. This variable can be generated with
flasc.utilities.floris_tools.get_upstream_turbs_floris(...).
exclude_turbs ([list, array]): array-like variable containing
turbine indices that should be excluded in determining the column
mean quantity.
exclude_turbs ([list, array]): array-like variable containing
turbine indices that should be excluded in determining the column
mean quantity.
Returns:
pd.Dataframe | FlascDataFrame: Dataframe which equals the inserted dataframe
plus the additional column called 'wd'.
"""

# First, set wind direction using all turbines
df = set_wd_by_all_turbines(df)

# Use the farm-average wind direction to determine a new wind direction signal
# using only upstream turbines
df = _set_col_by_upstream_turbines(
col_out="wd_upstream",
col_prefix="wd",
df=df,
df_upstream=df_upstream,
circular_mean=True,
exclude_turbs=exclude_turbs,
)

df = df.drop(columns=["wd"])

df = df.rename(columns={"wd_upstream": "wd"})

return df


def set_wd_by_radius_from_turbine(
df: Union[pd.DataFrame, FlascDataFrame],
turb_no: int,
Expand Down Expand Up @@ -415,6 +468,72 @@ def set_wd_by_radius_from_turbine(
)


def set_wd_by_upstream_turbines_in_radius(
df: Union[pd.DataFrame, FlascDataFrame],
df_upstream: pd.DataFrame,
turb_no: int,
x_turbs: List[float],
y_turbs: List[float],
max_radius: float,
include_itself: bool = True,
) -> Union[pd.DataFrame, FlascDataFrame]:
"""Add wind direction column using in-radius upstream turbines.
Add a column called 'wd' to your dataframe, which is the
mean of the columns wd_%03d for turbines that are upstream and
also within radius [max_radius] of the turbine of interest
[turb_no]. As an intermediate step, the average wind direction
over all turbines is used to determine the set of upstream turbines
from which the final wind direction signal is derived.
Args:
df (pd.DataFrame | FlascDataFrame): Dataframe with measurements. This dataframe
typically consists of wd_%03d, ws_%03d, ti_%03d, pow_%03d, and
potentially additional measurements.
df_upstream (pd.DataFrame): Dataframe containing rows indicating
wind direction ranges and the corresponding upstream turbines for
that wind direction range. This variable can be generated with
flasc.utilities.floris_tools.get_upstream_turbs_floris(...).
turb_no (int): Turbine number from which the radius should be calculated.
x_turbs ([list, array]): Array containing x locations of turbines.
y_turbs ([list, array]): Array containing y locations of turbines.
max_radius (float): Maximum radius for the upstream turbines
until which they are still considered as relevant/used for the
calculation of the averaged column quantity.
include_itself (bool, optional): Include the measurements of turbine
turb_no in the determination of the averaged column quantity. Defaults
to False.
Returns:
pd.Dataframe | FlascDataFrame: Dataframe which equals the inserted dataframe
plus the additional column called 'wd'.
"""

# First, set wind direction using all turbines
df = set_wd_by_all_turbines(df)

# Use the farm-average wind direction to determine a new wind direction signal
# using only upstream turbines within radius
df = _set_col_by_upstream_turbines_in_radius(
col_out="wd_upstream",
col_prefix="wd",
df=df,
df_upstream=df_upstream,
turb_no=turb_no,
x_turbs=x_turbs,
y_turbs=y_turbs,
max_radius=max_radius,
circular_mean=True,
include_itself=include_itself,
)

df = df.drop(columns=["wd"])

df = df.rename(columns={"wd_upstream": "wd"})

return df


def set_ws_by_turbines(
df: Union[pd.DataFrame, FlascDataFrame], turbine_numbers: List[int]
) -> Union[pd.DataFrame, FlascDataFrame]:
Expand Down
12 changes: 12 additions & 0 deletions tests/dataframe_manipulations_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,11 @@ def test_set_by_upstream_turbines(self):
df_test = dfm.set_wd_by_all_turbines(df_test)
df_test = dfm.set_ws_by_upstream_turbines(df_test, df_upstream)
df_test = dfm.set_ti_by_upstream_turbines(df_test, df_upstream)
df_test = dfm.set_wd_by_upstream_turbines(df_test, df_upstream)

self.assertAlmostEqual(df_test.loc[0, "ws"], np.mean([5.0, 17.0]))
self.assertAlmostEqual(df_test.loc[0, "ti"], np.mean([0.03, 0.09]))
self.assertAlmostEqual(df_test.loc[0, "wd"], circmean([350.0, 3.0], high=360.0))

def test_set_by_upstream_turbines_in_radius(self):
# Test set_*_by_upstream_turbines_in_radius functions
Expand Down Expand Up @@ -104,10 +106,20 @@ def test_set_by_upstream_turbines_in_radius(self):
max_radius=1000,
include_itself=True, # Include itself
)
df_test = dfm.set_wd_by_upstream_turbines_in_radius(
df_test,
df_upstream,
turb_no=1,
x_turbs=np.array([0.0, 500.0, 1000.0, 1500.0]),
y_turbs=np.array([0.0, 500.0, 1000.0, 1500.0]),
max_radius=1000,
include_itself=False, # Include itself
)

self.assertAlmostEqual(df_test.loc[0, "ws"], np.mean([5.0, 17.0]))
self.assertAlmostEqual(df_test.loc[0, "ti"], np.mean([0.09]))
self.assertAlmostEqual(df_test.loc[0, "pow_ref"], np.mean([1500.0, 1800.0]))
self.assertAlmostEqual(df_test.loc[0, "wd"], circmean([350.0], high=360.0))

def test_is_day_or_night(self):
# Test is day night using noon and midnight Oct 1 2023 in London, UK
Expand Down

0 comments on commit 1427fa6

Please sign in to comment.