FLASC data format#

Data used by FLASC adheres to the following conventions:

  • time represents the time, preferably in UTC

  • turbines are sequentially numbered, starting from 0, and numbers are always 3 digits long (e.g. the "8th" turbine is represented as 007)

  • pow_000 represents the power output of turbine 0

  • ws_000 represents the wind speed at turbine 0

  • wd_000 represents the wind direction at turbine 0

  • wd represents the wind direction chosen for example to represent the overall inflow direction

  • ws represents the wind speed chosen for example to represent the overall inflow speed

  • pow_ref represents the power output of the reference turbine (or average of reference turbines)

  • pow_test represents the power output of the test turbine (or average of test turbines)

import pandas as pd

# This dataframe adhere's to FLASC's data formatting requirements and could be used for
# FLASC analysis
df = pd.DataFrame(
    {
        "time": [0, 1, 2, 3, 4, 5],
        "pow_000": [100, 100, 100, 100, 100, 100],
        "pow_001": [100, 100, 100, 100, 100, 100],
        "ws_000": [10, 10, 10, 10, 10, 10],
        "ws_001": [10, 10, 10, 10, 10, 10],
        "wd_000": [270, 270, 270, 270, 270, 270],
        "wd_001": [270, 270, 270, 270, 270, 270],
    }
)

FlascDataFrame#

FLASC has historically used a pandas.DataFrame to store the data to be processed, as demonstrated above. Beginning in version 2.1, the FlascDataFrame class was introduced to provide additional methods and functionality to the data. FlascDataFrame is a subclass of pandas.DataFrame and can be used in place of a pandas.DataFrame. The following code cells provide an overview of the FlascDataFrame class and its methods. Support is added for converting between "FLASC" style data formatting and "user" formats, to make adhering to FLASC's data formatting conventions more straightforward.

Using FlascDataFrame#

# The above pandas.DataFrame can be converted to a FlascDataFrame directly
from flasc import FlascDataFrame

fdf = FlascDataFrame(df)
print(fdf.head())
FlascDataFrame in FLASC format
   time  pow_000  pow_001  ws_000  ws_001  wd_000  wd_001
0     0      100      100      10      10     270     270
1     1      100      100      10      10     270     270
2     2      100      100      10      10     270     270
3     3      100      100      10      10     270     270
4     4      100      100      10      10     270     270
# The FlascDataFrame includes a few helper functions added to the base pandas dataframe.
# The following returns the number of turbines found in the dataframe.
print(fdf.n_turbines)
2

Creating a FlascDataFrame from User Data#

More value from a FlascDataFrame is obtained when using it convert back and forth between user-formatted data and Flasc Data.

import numpy as np

# Suppose the we have a 3 turbine farm with turbines names 'TB01', 'TB02', 'TB03'
# For each turbine we have power, wind speed and wind direction data
# Assume that in the native data collection system,
# the signal names for each channel are given below

N = 20  # Number of data points

# Wind speeds
wind_speed_TB01 = np.random.rand(N) + 8.0
wind_speed_TB02 = np.random.rand(N) + 7.5
wind_speed_TB03 = np.random.rand(N) + 8.5

# Wind directions
wind_dir_TB01 = 10 * np.random.rand(N) + 270.0
wind_dir_TB02 = 10 * np.random.rand(N) + 270.0
wind_dir_TB03 = 10 * np.random.rand(N) + 270.0

# Power
power_TB01 = wind_speed_TB01**3
power_TB02 = wind_speed_TB02**3
power_TB03 = wind_speed_TB03**3

# Time
time = np.arange(N)
# Create a dictrionary storing this data, which could be used to instantiate a pandas.DataFrame
# or a FlascDataFrame
data_dict = {
    "time": time,
    "wind_speed_TB01": wind_speed_TB01,
    "wind_speed_TB02": wind_speed_TB02,
    "wind_speed_TB03": wind_speed_TB03,
    "wind_dir_TB01": wind_dir_TB01,
    "wind_dir_TB02": wind_dir_TB02,
    "wind_dir_TB03": wind_dir_TB03,
    "power_TB01": power_TB01,
    "power_TB02": power_TB02,
    "power_TB03": power_TB03,
}

The data is currently stored using the the channel and turbine names of the user. By supplying additional metadata to the FlascDataFrame, the data can be converted to and from the FLASC format.

# Declare a channel_name_map dictionary to map the signal names to the turbine names.
# The turbine numbers when 0-indexed in FLASC format should
# align with their numbering in the FLORIS model of the same farm.
channel_name_map = {
    "time": "time",
    "wind_speed_TB01": "ws_000",
    "wind_speed_TB02": "ws_001",
    "wind_speed_TB03": "ws_002",
    "wind_dir_TB01": "wd_000",
    "wind_dir_TB02": "wd_001",
    "wind_dir_TB03": "wd_002",
    "power_TB01": "pow_000",
    "power_TB02": "pow_001",
    "power_TB03": "pow_002",
}

We are now in a position to instantiate a FlascDataFrame

fdf = FlascDataFrame(data_dict, channel_name_map=channel_name_map)
print(fdf.head())
FlascDataFrame in user (wide) format
   time  wind_speed_TB01  wind_speed_TB02  wind_speed_TB03  wind_dir_TB01  \
0     0         8.061603         7.793448         9.033996     277.456588   
1     1         8.817900         7.529401         8.679348     274.616223   
2     2         8.832154         8.320152         9.400810     277.997448   
3     3         8.517560         8.016745         8.666481     271.543981   
4     4         8.208883         7.743552         8.761684     276.995065   

   wind_dir_TB02  wind_dir_TB03  power_TB01  power_TB02  power_TB03  
0     276.613977     277.229081  523.919107  473.357117  737.292287  
1     271.922201     273.302332  685.639100  426.855977  653.824646  
2     277.292730     275.014780  688.969376  575.961917  830.798824  
3     275.400211     271.867816  617.938903  515.221825  650.921113  
4     276.511116     277.621733  553.161789  464.323419  672.609121  

Converting this to the FLASC format (and back) now simply requires calling the appropriate method. This makes it convenient to work with FLASC functions (that require the data to be in FLASC format) and user-provided functions (that may require the user's formatting) within the same workflow.

# Convert now into FLASC format (as a copy)
fdf_flasc = fdf.convert_to_flasc_format()
print(fdf_flasc.head(2))

print("\n\n")
# Convert back to user format (as a copy)
fdf_user = fdf_flasc.convert_to_user_format()
print(fdf_user.head(2))

print("\n\n")
# Conversions can also happen in place, if the inplace argument is set to True
fdf.convert_to_flasc_format(inplace=True)
print(fdf.head(2))
print("\n")
fdf.convert_to_user_format(inplace=True)
print(fdf.head(2))
FlascDataFrame in FLASC format
   time    ws_000    ws_001    ws_002      wd_000      wd_001      wd_002  \
0     0  8.061603  7.793448  9.033996  277.456588  276.613977  277.229081   
1     1  8.817900  7.529401  8.679348  274.616223  271.922201  273.302332   

      pow_000     pow_001     pow_002  
0  523.919107  473.357117  737.292287  
1  685.639100  426.855977  653.824646  



FlascDataFrame in user (wide) format
   time  wind_speed_TB01  wind_speed_TB02  wind_speed_TB03  wind_dir_TB01  \
0     0         8.061603         7.793448         9.033996     277.456588   
1     1         8.817900         7.529401         8.679348     274.616223   

   wind_dir_TB02  wind_dir_TB03  power_TB01  power_TB02  power_TB03  
0     276.613977     277.229081  523.919107  473.357117  737.292287  
1     271.922201     273.302332  685.639100  426.855977  653.824646  



FlascDataFrame in FLASC format
   time    ws_000    ws_001    ws_002      wd_000      wd_001      wd_002  \
0     0  8.061603  7.793448  9.033996  277.456588  276.613977  277.229081   
1     1  8.817900  7.529401  8.679348  274.616223  271.922201  273.302332   

      pow_000     pow_001     pow_002  
0  523.919107  473.357117  737.292287  
1  685.639100  426.855977  653.824646  


FlascDataFrame in user (wide) format
   time  wind_speed_TB01  wind_speed_TB02  wind_speed_TB03  wind_dir_TB01  \
0     0         8.061603         7.793448         9.033996     277.456588   
1     1         8.817900         7.529401         8.679348     274.616223   

   wind_dir_TB02  wind_dir_TB03  power_TB01  power_TB02  power_TB03  
0     276.613977     277.229081  523.919107  473.357117  737.292287  
1     271.922201     273.302332  685.639100  426.855977  653.824646  

Converting Wide and Long#

FlascDataFrame also provides methods to convert between wide and long formats. FLASC's native format is always "wide", that is, each channel has its own column. But FlascDataFrame can be used to convert to a user format that is "long" where each channel is a row in the dataframe.

df = pd.DataFrame(
    {
        "time": time,
        "wind_speed_TB01": wind_speed_TB01,
        "wind_speed_TB02": wind_speed_TB02,
        "wind_speed_TB03": wind_speed_TB03,
        "wind_dir_TB01": wind_dir_TB01,
        "wind_dir_TB02": wind_dir_TB02,
        "wind_dir_TB03": wind_dir_TB03,
        "power_TB01": power_TB01,
        "power_TB02": power_TB02,
        "power_TB03": power_TB03,
    }
)

# Convert to "long" format; this is taken to be the user's desired format in this example.
df = pd.melt(df, id_vars=["time"], var_name="channel", value_name="value")
print(df)
     time          channel       value
0       0  wind_speed_TB01    8.061603
1       1  wind_speed_TB01    8.817900
2       2  wind_speed_TB01    8.832154
3       3  wind_speed_TB01    8.517560
4       4  wind_speed_TB01    8.208883
..    ...              ...         ...
175    15       power_TB03  839.126419
176    16       power_TB03  797.651737
177    17       power_TB03  646.260739
178    18       power_TB03  623.388230
179    19       power_TB03  647.364554

[180 rows x 3 columns]
# This time include in the specification of the FlascDataFrame the name of the
# columns of the long data
fdf = FlascDataFrame(
    df,
    channel_name_map=channel_name_map,
    long_data_columns={"variable_column": "channel", "value_column": "value"},
)
print(fdf.head())
FlascDataFrame in user (long) format
   time          channel     value
0     0  wind_speed_TB01  8.061603
1     1  wind_speed_TB01  8.817900
2     2  wind_speed_TB01  8.832154
3     3  wind_speed_TB01  8.517560
4     4  wind_speed_TB01  8.208883

The data can still be converted to FLASC format (and back)

fdf_flasc = fdf.convert_to_flasc_format()
print(fdf_flasc.head(2))
print("\n\n")
fdf_user = fdf_flasc.convert_to_user_format()
print(fdf_user.head(2))

# As before, conversions can also happen in place, if the inplace argument is set to True
FlascDataFrame in FLASC format
   time     pow_000     pow_001     pow_002      wd_000      wd_001  \
0     0  523.919107  473.357117  737.292287  277.456588  276.613977   
1     1  685.639100  426.855977  653.824646  274.616223  271.922201   

       wd_002    ws_000    ws_001    ws_002  
0  277.229081  8.061603  7.793448  9.033996  
1  273.302332  8.817900  7.529401  8.679348  



FlascDataFrame in user (long) format
   time     channel       value
0     0  power_TB01  523.919107
1     0  power_TB02  473.357117

Exporting to wind-up format#

Another use case for FlascDataFrame is to export the data into the "wind-up" format. Wind-up is an open source tool for assessing uplift provided by RES. This conversion provides a convenient way to assess the data, in the case of uplift assessment, using the wind-up tool, which is imported by FLASC. A full demonstration of the usage of the wind-up tool in FLASC is provided within the Smarteole example set.

fdf = fdf.convert_to_flasc_format()

df_windup = fdf.export_to_windup_format()  # df_windup is a pandas DataFrame
print(df_windup.head())
                       raw_ActivePowerMean  raw_YawAngleMean  \
TimeStamp_StartFormat                                          
0                               523.919107        277.456588   
1                               685.639100        274.616223   
2                               688.969376        277.997448   
3                               617.938903        271.543981   
4                               553.161789        276.995065   

                       raw_WindSpeedMean TurbineName  PitchAngleMean  \
TimeStamp_StartFormat                                                  
0                               8.061603         000               0   
1                               8.817900         000               0   
2                               8.832154         000               0   
3                               8.517560         000               0   
4                               8.208883         000               0   

                       GenRpmMean  raw_ShutdownDuration  ActivePowerMean  \
TimeStamp_StartFormat                                                      
0                            1000                     0       523.919107   
1                            1000                     0       685.639100   
2                            1000                     0       688.969376   
3                            1000                     0       617.938903   
4                            1000                     0       553.161789   

                       WindSpeedMean  YawAngleMean  ShutdownDuration  
TimeStamp_StartFormat                                                 
0                           8.061603    277.456588                 0  
1                           8.817900    274.616223                 0  
2                           8.832154    277.997448                 0  
3                           8.517560    271.543981                 0  
4                           8.208883    276.995065                 0