Timeseries data¶
In this example we calculate the data of a wind farm with 67 turbines in a time series containing 8000 uniform inflow states.
The required imports are:
%matplotlib inline
import matplotlib.pyplot as plt
import foxes
import foxes.variables as FV
/home/runner/work/foxes/foxes/foxes/core/engine.py:6: TqdmExperimentalWarning: Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)
from tqdm.autonotebook import tqdm
First, we create the states. The data_source can be any csv-type file (or pandas readable equivalent), or a pandas.DataFrame object. If it is a file path, then it will first be searched in the file system, and if not found, in the static data. If it is also not found there, an error showing the available static data file names is displayed.
In this example the static data file timeseries_8000.csv.gz will be used, with content
Time,ws,wd,ti
2017-01-01 00:00:00,15.62,244.06,0.0504
2017-01-01 00:30:00,15.99,243.03,0.0514
2017-01-01 01:00:00,16.31,243.01,0.0522
2017-01-01 01:30:00,16.33,241.26,0.0523
...
Notice the column names, and how they appear in the Timeseries constructor:
states = foxes.input.states.Timeseries(
data_source="timeseries_8000.csv.gz",
output_vars=[FV.WS, FV.WD, FV.TI, FV.RHO],
var2col={FV.WS: "ws", FV.WD: "wd", FV.TI: "ti"},
fixed_vars={FV.RHO: 1.225},
)
We can visualize the wind distribution via the StatesRosePlotOutput. Here we display the ambient wind speed in a wind rose with 16 wind direction sectors and 5 wind speed bins:
o = foxes.output.StatesRosePlotOutput(states, point=[0.0, 0.0, 100.0])
o.get_figure(16, FV.AMB_WS, [0, 3.5, 6, 10, 15, 20], figsize=(6, 6))
plt.show()
DefaultEngine: Selecting engine 'single'
SingleChunkEngine: Calculating 8000 states for 1 turbines
SingleChunkEngine: Starting calculation using a single worker.
SingleChunkEngine: Completed all 1 chunks
For the time series with uniform data, any choice of the point argument will produce the same figure.
Next, we create the example wind farm with 67 turbines from static data. The file test_farm_67.csv has the following structure:
index,label,x,y
0,T0,101872.70,1004753.57
1,T1,103659.97,1002993.29
2,T2,100780.09,1000779.97
3,T3,100290.42,1004330.88
...
For more options, check the API section foxes.input.farm_layout.
We consider two turbine models in this example: the wind turbine type NREL5MW and the turbine model kTI_02, both from the default model book. The latter model adds the variable k for each state and turbine, calculated as k = kTI * TI, with constant kTI = 0.2. The parameter k will later be used by the wake model.
farm = foxes.WindFarm()
foxes.input.farm_layout.add_from_file(
farm, "test_farm_67.csv", turbine_models=["NREL5MW"], verbosity=0
)
Next, we create the algorithm, with further model selections. In particular, two wake models are invoked, the model Bastankhah_quadratic for wind speed deficits and the model CrespoHernandez_max for turbulence intensity:
algo = foxes.algorithms.Downwind(
farm,
states,
rotor_model="centre",
wake_models=["Bastankhah2014_quadratic_ka02", "CrespoHernandez_max_ka04"],
verbosity=0,
)
Also notice the chunks parameter, specifying that always 1000 states should be considered in vectorized form during calculations. The progress is automatically visualized when invoking the DaskRunner:
farm_results = algo.calc_farm()
fr = farm_results.to_dataframe()
print("\n", fr[[FV.WD, FV.AMB_REWS, FV.REWS, FV.AMB_P, FV.P]])
DefaultEngine: Selecting engine 'process'
ProcessEngine: Calculating 8000 states for 67 turbines
ProcessEngine: Starting calculation using 3 workers, for 3 states chunks.
ProcessEngine: Completed all 3 chunks
WD AMB_REWS REWS AMB_P P
state turbine
2017-01-01 00:00:00 0 244.06 15.62 15.610694 5000.00 5000.000000
1 244.06 15.62 14.023199 5000.00 5000.000000
2 244.06 15.62 15.030115 5000.00 5000.000000
3 244.06 15.62 15.562097 5000.00 5000.000000
4 244.06 15.62 14.567762 5000.00 5000.000000
... ... ... ... ... ...
2017-06-16 15:30:00 62 299.19 11.70 8.162589 4868.75 1892.635221
63 299.19 11.70 11.495171 4868.75 4779.137111
64 299.19 11.70 11.700000 4868.75 4868.750000
65 299.19 11.70 11.643951 4868.75 4844.228507
66 299.19 11.70 8.056987 4868.75 1813.697804
[536000 rows x 5 columns]
Let’s evaluate the results:
# add capacity factor and efficiency to farm_results:
o = foxes.output.FarmResultsEval(farm_results, algo=algo)
o.add_capacity_factor()
o.add_capacity_factor(ambient=True)
o.add_efficiency()
# print results by turbine:
turbine_results = o.reduce_states()
turbine_results[FV.AMB_YLD] = o.calc_yield(annual=True, ambient=True)
turbine_results[FV.YLD] = o.calc_yield(annual=True)
turbine_results[FV.EFF] = turbine_results[FV.P] / turbine_results[FV.AMB_P]
print("\nResults by turbine:\n")
print(turbine_results)
# print power results:
P0 = o.calc_mean_farm_power(ambient=True)
P = o.calc_mean_farm_power()
print(f"\nFarm power : {P / 1000:.1f} MW")
print(f"Farm ambient power: {P0 / 1000:.1f} MW")
print(f"Farm efficiency : {o.calc_farm_efficiency():.2f}")
print(f"Annual farm yield : {turbine_results[FV.YLD].sum():.2f} GWh")
Capacity factor added to farm results
Ambient capacity factor added to farm results
Efficiency added to farm results
Results by turbine:
AMB_CT AMB_P AMB_REWS AMB_RHO AMB_TI AMB_WD \
turbine
0 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
1 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
2 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
3 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
4 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
... ... ... ... ... ... ...
62 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
63 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
64 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
65 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
66 0.593832 2.454179e+07 10.142951 1.225 0.056837 214.291794
AMB_YAW CT D H ... YAW order \
turbine ...
0 214.291794 0.629229 126.0 90.0 ... 214.291794 30.747125
1 214.291794 0.648116 126.0 90.0 ... 214.291794 29.423750
2 214.291794 0.622349 126.0 90.0 ... 214.291794 33.122125
3 214.291794 0.622030 126.0 90.0 ... 214.291794 34.077750
4 214.291794 0.640870 126.0 90.0 ... 214.291794 31.093875
... ... ... ... ... ... ... ...
62 214.291794 0.633944 126.0 90.0 ... 214.291794 31.599000
63 214.291794 0.637109 126.0 90.0 ... 214.291794 35.948000
64 214.291794 0.614452 126.0 90.0 ... 214.291794 39.153125
65 214.291794 0.641647 126.0 90.0 ... 214.291794 27.239750
66 214.291794 0.642135 126.0 90.0 ... 214.291794 28.507375
order_inv order_ssel CAPF AMB_CAPF EFF tname \
turbine
0 31.543750 1332.833375 4223.640756 4908.357436 0.860500 T0
1 37.537250 1332.833375 3682.833890 4908.357436 0.750319 T1
2 32.728875 1332.833375 4106.338258 4908.357436 0.836601 T2
3 33.087375 1332.833375 4178.623243 4908.357436 0.851328 T3
4 29.326000 1332.833375 3831.506281 4908.357436 0.780609 T4
... ... ... ... ... ... ...
62 37.140000 1332.833375 3935.498952 4908.357436 0.801796 T62
63 30.083750 1332.833375 3900.188526 4908.357436 0.794602 T63
64 33.625125 1332.833375 4411.658934 4908.357436 0.898806 T64
65 31.799375 1332.833375 3816.136780 4908.357436 0.777477 T65
66 29.193875 1332.833375 3856.550328 4908.357436 0.785711 T66
AMB_YLD YLD
turbine
0 26.873257 23.124433
1 26.873257 20.163516
2 26.873257 22.482202
3 26.873257 22.877962
4 26.873257 20.977497
... ... ...
62 26.873257 21.546857
63 26.873257 21.353532
64 26.873257 24.153833
65 26.873257 20.893349
66 26.873257 21.114613
[67 rows x 27 columns]
Farm power : 166.4 MW
Farm ambient power: 205.5 MW
Farm efficiency : 0.81
Annual farm yield : 1457.46 GWh
We can visualize the mean rotor equivalent wind speed as seen by each turbine and also the mean efficiency with respect to the time series data as colored layout plots:
fig, axs = plt.subplots(1, 2, figsize=(14, 5))
o = foxes.output.FarmLayoutOutput(farm, farm_results)
o.get_figure(
fig=fig, ax=axs[0], color_by="mean_REWS", title="Mean REWS [m/s]", s=150, annotate=0
)
o.get_figure(
fig=fig,
ax=axs[1],
color_by="mean_EFF",
title="Mean efficiency [%]",
s=150,
annotate=0,
)
plt.show()