FLASC data format#
Data used by FLASC adheres to the following conventions:
time
represents the time, preferably in UTCturbines 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 0ws_000
represents the wind speed at turbine 0wd_000
represents the wind direction at turbine 0wd
represents the wind direction chosen for example to represent the overall inflow directionws
represents the wind speed chosen for example to represent the overall inflow speedpow_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.400464 7.947982 9.414284 277.874082
1 1 8.220891 8.366322 8.500428 277.922005
2 2 8.182981 8.023292 8.582335 273.994207
3 3 8.877512 8.415830 8.823494 270.855697
4 4 8.925842 8.464731 9.175242 270.041642
wind_dir_TB02 wind_dir_TB03 power_TB01 power_TB02 power_TB03
0 270.692041 278.604296 592.802265 502.077384 834.376190
1 278.191177 277.684681 555.592851 585.603662 614.217769
2 274.995299 271.377744 547.942122 516.485109 632.144551
3 270.186794 276.152744 699.638768 596.061114 686.944727
4 272.388501 277.169842 711.127790 606.512108 772.418323
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.400464 7.947982 9.414284 277.874082 270.692041 278.604296
1 1 8.220891 8.366322 8.500428 277.922005 278.191177 277.684681
pow_000 pow_001 pow_002
0 592.802265 502.077384 834.376190
1 555.592851 585.603662 614.217769
FlascDataFrame in user (wide) format
time wind_speed_TB01 wind_speed_TB02 wind_speed_TB03 wind_dir_TB01 \
0 0 8.400464 7.947982 9.414284 277.874082
1 1 8.220891 8.366322 8.500428 277.922005
wind_dir_TB02 wind_dir_TB03 power_TB01 power_TB02 power_TB03
0 270.692041 278.604296 592.802265 502.077384 834.376190
1 278.191177 277.684681 555.592851 585.603662 614.217769
FlascDataFrame in FLASC format
time ws_000 ws_001 ws_002 wd_000 wd_001 wd_002 \
0 0 8.400464 7.947982 9.414284 277.874082 270.692041 278.604296
1 1 8.220891 8.366322 8.500428 277.922005 278.191177 277.684681
pow_000 pow_001 pow_002
0 592.802265 502.077384 834.376190
1 555.592851 585.603662 614.217769
FlascDataFrame in user (wide) format
time wind_speed_TB01 wind_speed_TB02 wind_speed_TB03 wind_dir_TB01 \
0 0 8.400464 7.947982 9.414284 277.874082
1 1 8.220891 8.366322 8.500428 277.922005
wind_dir_TB02 wind_dir_TB03 power_TB01 power_TB02 power_TB03
0 270.692041 278.604296 592.802265 502.077384 834.376190
1 278.191177 277.684681 555.592851 585.603662 614.217769
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.400464
1 1 wind_speed_TB01 8.220891
2 2 wind_speed_TB01 8.182981
3 3 wind_speed_TB01 8.877512
4 4 wind_speed_TB01 8.925842
.. ... ... ...
175 15 power_TB03 681.593617
176 16 power_TB03 670.512394
177 17 power_TB03 666.137155
178 18 power_TB03 771.768387
179 19 power_TB03 656.015237
[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.400464
1 1 wind_speed_TB01 8.220891
2 2 wind_speed_TB01 8.182981
3 3 wind_speed_TB01 8.877512
4 4 wind_speed_TB01 8.925842
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 592.802265 502.077384 834.376190 277.874082 270.692041
1 1 555.592851 585.603662 614.217769 277.922005 278.191177
wd_002 ws_000 ws_001 ws_002
0 278.604296 8.400464 7.947982 9.414284
1 277.684681 8.220891 8.366322 8.500428
FlascDataFrame in user (long) format
time channel value
0 0 power_TB01 592.802265
1 0 power_TB02 502.077384
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 592.802265 277.874082
1 555.592851 277.922005
2 547.942122 273.994207
3 699.638768 270.855697
4 711.127790 270.041642
raw_WindSpeedMean TurbineName PitchAngleMean \
TimeStamp_StartFormat
0 8.400464 000 0
1 8.220891 000 0
2 8.182981 000 0
3 8.877512 000 0
4 8.925842 000 0
GenRpmMean raw_ShutdownDuration ActivePowerMean \
TimeStamp_StartFormat
0 1000 0 592.802265
1 1000 0 555.592851
2 1000 0 547.942122
3 1000 0 699.638768
4 1000 0 711.127790
WindSpeedMean YawAngleMean ShutdownDuration
TimeStamp_StartFormat
0 8.400464 277.874082 0
1 8.220891 277.922005 0
2 8.182981 273.994207 0
3 8.877512 270.855697 0
4 8.925842 270.041642 0