Source code for waves.utilities.floris_runners

"""Provides the FLORIS-based methods for pre-processing data and post-processing results."""


from __future__ import annotations

import numpy as np
import pandas as pd
from floris import WindRose, TimeSeries


[docs] def create_single_month_wind_rose(weather_df: pd.DataFrame, month: int) -> tuple[int, WindRose]: """Creates the FLORIS ``WindRose`` object for a given :py:attr:`month` based on the :py:attr:`weather_df`'s ``DatetimeIndex``. Parameters ---------- weather_df : pd.DataFrame The weather profile used to create long-term, month-based ``WindRose`` objects month : int The month of the year to create a ``WindRose`` object. Returns ------- WindRose Month-specific ``WindRose`` object. """ wd, ws, ti = weather_df.loc[weather_df.index.month == month].values.T wind_rose = TimeSeries( wind_directions=wd, wind_speeds=ws, turbulence_intensities=ti ).to_WindRose() return wind_rose
[docs] def create_monthly_wind_rose(weather_df: pd.DataFrame) -> dict[int, WindRose]: """Create a dictionary of month and a long-term ``WindRose`` object based on all the wind condition data for that month. Parameters ---------- weather_df : pd.DataFrame The weather profile used to create long-term, month-based ``WindRose`` objects Returns ------- dict[int, WindRose] A dictionary of the integer month and the long-term ``WindRose`` object associated with all the wind conditions during that month. """ monthly_wr = { month: create_single_month_wind_rose(weather_df=weather_df, month=month) for month in range(1, 13) } return monthly_wr
[docs] def check_monthly_wind_rose( project_wind_rose: WindRose, monthly_wind_rose: dict[int, WindRose] ) -> dict[int, WindRose]: """Checks the monthly wind rose parameterizations to ensure the DataFrames are the correct shape, so that when the frequency column is extracted, the compared data is the same. Parameters ---------- project_wind_rose : WindRose The ``WindRose`` created using the long term reanalysis weather profile. monthly_wind_rose : dict[int, WindRose] A dictionary of the month as an ``int`` and ``WindRose`` created from the long term project reanalysis weather profile that was filtered on weather data for the focal month. Returns ------- dict[int, WindRose] The :py:attr:`monthly_wind_rose` but with an missing wind conditions added into the ``WindRose`` with 0 frequency. """ project_df = pd.DataFrame( project_wind_rose.freq_table, columns=project_wind_rose.wind_speeds, index=project_wind_rose.wind_directions, ) project_shape = project_df.shape project_wd = project_wind_rose.wind_directions project_ws = project_wind_rose.wind_speeds for month, wind_rose in monthly_wind_rose.items(): if wind_rose.freq_table.shape == project_shape: continue wr_df = pd.DataFrame( wind_rose.freq_table, columns=wind_rose.wind_speeds, index=wind_rose.wind_directions ) ti_df = pd.DataFrame( wind_rose.ti_table, columns=wind_rose.wind_speeds, index=wind_rose.wind_directions ) # Find the missing combinations, add them to the wind rose DataFrame, and resort missing_wd = list(set(project_wd).difference(wind_rose.wind_directions)) missing_ws = list(set(project_ws).difference(wind_rose.wind_speeds)) wr_df.loc[missing_wd] = 0 wr_df.loc[:, missing_ws] = 0 wr_df = wr_df.sort_index().T.sort_index().T if wr_df.shape != project_shape: raise ValueError("The monthly wind rose was unable to be resampled.") ti_df.loc[missing_wd] = 0 ti_df.loc[:, missing_ws] = 0 ti_df = ti_df.sort_index().T.sort_index().T if ti_df.shape != project_shape: raise ValueError("The monthly wind rose was unable to be resampled.") # Recreate the WindRose object with the missing wd/ws values added back in monthly_wind_rose[month] = WindRose( wind_directions=wr_df.index.values, wind_speeds=wr_df.columns.values, freq_table=wr_df.values, ti_table=ti_df.values, ) return monthly_wind_rose
[docs] def calculate_monthly_wind_rose_results( turbine_power: np.ndarray, wind_rose_monthly: dict[int, WindRose], ) -> pd.DataFrame: """Calculate the turbine AEP contribution for each month of a year, in MWh. Parameters ---------- turbine_power : np.ndarray The array of turbine powers, with shape (num wd x num ws x num turbines), calculated from the possible wind conditions at the site given the turbine layout. wind_rose_monthly : dict[int, WindRose] The dictionary of integer months (i.e., 1 for January) and array of frequences, with ``WindRose`` objects created by the long term wind conditions filtered on the month. Returns ------- pd.DataFrame, pd.DataFrame A DataFrame of each month's contribution to the AEP for each turbine in the wind farm, with shape (12 x num turbines). """ month_day_map = pd.DataFrame( [ [1, 31], [2, 28], [3, 31], [4, 30], [5, 31], [6, 30], [7, 31], [8, 31], [9, 30], [10, 31], [11, 30], [12, 31], ], columns=["month", "n_days"], ).set_index("month") # Calculate the monthly turbine energy and sum over the turbines to get the farm energy turbine_energy = pd.DataFrame.from_dict( { m: np.nansum(mwr[:, :, np.newaxis] * turbine_power, axis=(0, 1)) for m, mwr in wind_rose_monthly.items() }, orient="index", ).sort_index() turbine_energy *= month_day_map.n_days.values.reshape(12, 1) * 24 / 1e6 return turbine_energy