Wind Climates
As described in the WindKit Introduction, WindKit relies on specifically structured xarray datasets in addition to classes to store data in memory. Below you will find out about the different types of objects that are present in WindKit.
Time Series Wind Climate
The Time Series Wind Climate (tswc) contains a time series of wind speed
and wind direction
. It is a the most basic wind climate from which all others can be derived. It also is the largest of the wind climates retaining all of the original information.
In addition to the Core coordinates, tswc objects also contain the dimension time
, which identifies the time of the given wind data.
Creating a xarray.Dataset
object in the tswc format is quite simple using the xarray.Dataset
constructor. The following example creates a tswc object with three heights and 100 hours of data.
In [1]: import numpy as np
In [2]: import pandas as pd
In [3]: import xarray as xr
In [4]: tswc = xr.Dataset(
...: data_vars=dict(
...: wind_speed = xr.DataArray(np.random.rand(100, 3)*10, dims=['time', 'height']),
...: wind_direction = xr.DataArray(np.random.rand(100, 3)*360, dims=['time', 'height']),
...: ),
...: coords=dict(
...: time = (("time",), pd.date_range('2020-01-01', periods=100, freq='h')),
...: height = (("height"), [10, 50, 100]),
...: )
...: )
...:
In [5]: print(tswc)
<xarray.Dataset> Size: 6kB
Dimensions: (time: 100, height: 3)
Coordinates:
* time (time) datetime64[ns] 800B 2020-01-01 ... 2020-01-05T03:0...
* height (height) int64 24B 10 50 100
Data variables:
wind_speed (time, height) float64 2kB 8.005 7.678 0.148 ... 7.758 5.793
wind_direction (time, height) float64 2kB 275.9 236.8 331.8 ... 63.49 297.4
To add the spatial information to the dataset you can add a new dimension using xarray.Dataset.expand_dims
.
The projection can be added using the windkit.spatial.set_crs()
function. In this case, where we already have a height coordinate,
and we want to add the x and y coordinates, we call the point dimension stacked_point
.
In [6]: import windkit as wk
In [7]: tswc = (
...: tswc
...: .expand_dims("stacked_point")
...: .assign_coords(
...: west_east = (("stacked_point",), [10.0]),
...: south_north = (("stacked_point",), [56.0]),
...: )
...: )
...:
In [8]: tswc = wk.spatial.set_crs(tswc, crs=4326)
In [9]: tswc
Out[9]:
<xarray.Dataset> Size: 6kB
Dimensions: (time: 100, height: 3, stacked_point: 1)
Coordinates:
* time (time) datetime64[ns] 800B 2020-01-01 ... 2020-01-05T03:0...
* height (height) int64 24B 10 50 100
west_east (stacked_point) float64 8B 10.0
south_north (stacked_point) float64 8B 56.0
crs int8 1B 0
Dimensions without coordinates: stacked_point
Data variables:
wind_speed (stacked_point, time, height) float64 2kB 8.005 ... 5.793
wind_direction (stacked_point, time, height) float64 2kB 275.9 ... 297.4
Windkit provides a function to create a tswc object from a pandas.DataFrame
object using the
windkit.tswc_from_dataframe()
function. The pandas.DataFrame
should have a time index,
the coordinate and projection should be added as in the previous example,
and the columns of wind speed and direction needs to associated with heights as in the following example.
In [10]: data = {
....: "wind_speed_10m": np.random.rand(100)*10,
....: "wind_speed_50m": np.random.rand(100)*10,
....: "wind_speed_100m": np.random.rand(100)*10,
....: "wind_direction_10m": np.random.rand(100)*360,
....: "wind_direction_50m": np.random.rand(100)*360,
....: "wind_direction_100m": np.random.rand(100)*360,
....: }
....:
In [11]: df = pd.DataFrame(
....: data,
....: index=pd.date_range('2020-01-01', periods=100, freq='h'),
....: )
....:
In [12]: print(df)
wind_speed_10m ... wind_direction_100m
2020-01-01 00:00:00 0.028452 ... 320.494389
2020-01-01 01:00:00 6.690418 ... 124.555064
2020-01-01 02:00:00 0.139484 ... 121.682102
2020-01-01 03:00:00 0.180738 ... 67.837540
2020-01-01 04:00:00 4.045290 ... 304.472735
... ... ... ...
2020-01-04 23:00:00 1.621895 ... 47.750233
2020-01-05 00:00:00 5.928161 ... 53.830267
2020-01-05 01:00:00 6.387099 ... 229.569000
2020-01-05 02:00:00 8.485624 ... 324.138338
2020-01-05 03:00:00 9.902474 ... 125.753066
[100 rows x 6 columns]
In [13]: west_east, south_north = 10.0, 56.0
In [14]: crs = 4326
In [15]: height_to_columns = {
....: 10: ("wind_speed_10m", "wind_direction_10m"),
....: 50: ("wind_speed_50m", "wind_direction_50m"),
....: 100: ("wind_speed_100m", "wind_direction_100m"),
....: }
....:
In [16]: tswc = wk.tswc_from_dataframe(
....: df,
....: west_east,
....: south_north,
....: crs=4326,
....: height_to_columns=height_to_columns,
....: )
....:
In [17]: tswc
Out[17]:
<xarray.Dataset> Size: 6kB
Dimensions: (time: 100, height: 3, stacked_point: 1)
Coordinates:
west_east (stacked_point) float64 8B 10.0
south_north (stacked_point) float64 8B 56.0
crs int8 1B 0
* time (time) datetime64[ns] 800B 2020-01-01 ... 2020-01-05T03:0...
* height (height) int64 24B 10 50 100
Dimensions without coordinates: stacked_point
Data variables:
wind_speed (time, height, stacked_point) float64 2kB 0.02845 ... 6.583
wind_direction (time, height, stacked_point) float64 2kB 144.6 ... 125.8
Attributes:
history: 2025-07-21T09:49:33+00:00:\twindkit==1.0.2\twk.tswc_fro...
Conventions: CF-1.8
Package name: windkit
Package version: 1.0.2
Creation date: 2025-07-21T09:49:33+00:00
Object type: Time Series Wind Climate
author: Default User
author_email: default_email@example.com
institution: Default Institution
The result is the same as the previous example.
Binned Wind Climate
The Binned Wind Climate (bwc) contains a histogram representation of the wind, for different wind direction sectors. Historically these have been used for encoding observational data, and in WAsP Observed Wind Climate is used for this type of data. However there is no reason that they couldn’t be used for other wind data as well. In WAsP, these are stored in “.tab” and “.owc” files, which can be read using WindKit.
In addition to the Core coordinates, bwc objects also contain the dimension wsbin
, which identifies the wind speed bins of the histogram. Wind speed bins are characterized by their upper boundary, e.g. a wind speed bin from 0-1 would be identified with a wind speed value of 1.
In addition to reading bwc’s from files, you can create them from weibull distributions and time-series data.
Creating a bwc xarray.Dataset
from numpy arrays can be done like this:
In [18]: wsbins = np.linspace(0.0, 30.0, 31)
In [19]: wdbins = np.linspace(-15.0, 345.0, 13)
In [20]: wsfreq = np.random.rand(30, 12)
In [21]: wsfreq = wsfreq / wsfreq.sum(axis=0)
In [22]: wdfreq = np.random.rand(12)
In [23]: wdfreq = wdfreq / wdfreq.sum()
In [24]: bwc = xr.Dataset(
....: data_vars=dict(
....: wsfreq=(("wsbin", "sector"), wsfreq),
....: wdreq=(("sector",), wdfreq),
....: ),
....: coords=dict(
....: wsbin=(("wsbin",), (wsbins[1:] + wsbins[:-1]) / 2.0),
....: wsfloor=(("wsbin",), wsbins[:-1]),
....: wsceil=(("wsbin",), wsbins[1:]),
....: sector=(("sector",), (wdbins[1:] + wdbins[:-1]) / 2.0),
....: sector_floor=(("sector_floor",), np.mod(wdbins[:-1], 360)),
....: sector_ceil=(("sector_ceil",), np.mod(wdbins[1:], 360)),
....: )
....: )
....:
In [25]: bwc
Out[25]:
<xarray.Dataset> Size: 4kB
Dimensions: (wsbin: 30, sector: 12, sector_floor: 12, sector_ceil: 12)
Coordinates:
* wsbin (wsbin) float64 240B 0.5 1.5 2.5 3.5 ... 26.5 27.5 28.5 29.5
wsfloor (wsbin) float64 240B 0.0 1.0 2.0 3.0 ... 26.0 27.0 28.0 29.0
wsceil (wsbin) float64 240B 1.0 2.0 3.0 4.0 ... 27.0 28.0 29.0 30.0
* sector (sector) float64 96B 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
* sector_floor (sector_floor) float64 96B 345.0 15.0 45.0 ... 285.0 315.0
* sector_ceil (sector_ceil) float64 96B 15.0 45.0 75.0 ... 285.0 315.0 345.0
Data variables:
wsfreq (wsbin, sector) float64 3kB 0.02085 0.04169 ... 0.002349
wdreq (sector) float64 96B 0.07355 0.005435 ... 0.1416 0.09451
See the Time Series Wind Climate section for how to add the spatial information to the dataset.
Generalized Wind Climate
A Generalized Wind Climate is a key part of the WAsP Methodology. The Generalized Wind Climate (gwc) contains the wind in a virtual world, where there is no terrain and there are homogeneous roughness values, i.e. no roughness changes. Generalized wind climates are represented as Weibull distributions (scale [A]; shape [k]) and sector-wise frequency values. Because the gwc exists in a virtual world, it contains several additional dimensions compared to the other wind climate files. gen_height
is the height above the constant terrain in the generalized atmosphere, and gen_roughness
is the homogeneous roughness length. In WAsP, you were limited to exactly five of each of these parameters, however in WindKit you can use as many or as few as you wish.
WindKit provides the ability to create gwc objects from “.lib” and “.gwc” files. This is used
Creating a gwc xarray.Dataset
manually can be done like this:
In [26]: gen_heights = np.array([10, 25, 50, 100, 200])
In [27]: gen_roughnesses = np.array([0.0, 0.03, 0.1, 0.3, 1.5])
In [28]: A = np.random.rand(5, 5, 12) * 10
In [29]: k = np.random.rand(5, 5, 12) + 1
In [30]: wdfreq = np.random.rand(5, 5, 12) * 360.0
In [31]: gwc = xr.Dataset(
....: data_vars=dict(
....: A=(("gen_height", "gen_roughness", "sector"), A),
....: k=(("gen_height", "gen_roughness", "sector"), k),
....: wdfreq=(("gen_height", "gen_roughness", "sector"), wdfreq),
....: ),
....: coords=dict(
....: gen_height=(("gen_height",), gen_heights),
....: gen_roughness=(("gen_roughness",), gen_roughnesses),
....: sector=(("sector",), (wdbins[1:] + wdbins[:-1]) / 2.0),
....: sector_floor=(("sector_floor",), np.mod(wdbins[:-1], 360)),
....: sector_ceil=(("sector_ceil",), np.mod(wdbins[1:], 360)),
....: )
....: )
....:
In [32]: gwc
Out[32]:
<xarray.Dataset> Size: 8kB
Dimensions: (gen_height: 5, gen_roughness: 5, sector: 12,
sector_floor: 12, sector_ceil: 12)
Coordinates:
* gen_height (gen_height) int64 40B 10 25 50 100 200
* gen_roughness (gen_roughness) float64 40B 0.0 0.03 0.1 0.3 1.5
* sector (sector) float64 96B 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
* sector_floor (sector_floor) float64 96B 345.0 15.0 45.0 ... 285.0 315.0
* sector_ceil (sector_ceil) float64 96B 15.0 45.0 75.0 ... 315.0 345.0
Data variables:
A (gen_height, gen_roughness, sector) float64 2kB 5.478 ... ...
k (gen_height, gen_roughness, sector) float64 2kB 1.999 ... ...
wdfreq (gen_height, gen_roughness, sector) float64 2kB 14.67 ... ...
Remember to add spatial information to the dataset as described in the Time Series Wind Climate section.
Weibull Wind Climate
The Weibull Wind Climate (wwc) is related to the Binned Wind Climate, but instead of a histogram, it is represented solely as the weibull parameters for the different sectors. In WAsP, this was often stored as “.rsf” files, which can be read from WindKit. These are the objects that most often store the results of a WAsP simulation.
This is how you create a wwc xarray.Dataset
manually:
In [33]: A = np.random.rand(12) * 10
In [34]: k = np.random.rand(12) + 1
In [35]: wdfreq = np.random.rand(12) * 360.0
In [36]: wwc = xr.Dataset(
....: data_vars=dict(
....: A=(("sector",), A),
....: k=(("sector",), k),
....: wdfreq=(("sector",), wdfreq),
....: ),
....: coords=dict(
....: sector=(("sector",), (wdbins[1:] + wdbins[:-1]) / 2.0),
....: sector_floor=(("sector_floor",), np.mod(wdbins[:-1], 360)),
....: sector_ceil=(("sector_ceil",), np.mod(wdbins[1:], 360)),
....: )
....: )
....:
In [37]: wwc
Out[37]:
<xarray.Dataset> Size: 576B
Dimensions: (sector: 12, sector_floor: 12, sector_ceil: 12)
Coordinates:
* sector (sector) float64 96B 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
* sector_floor (sector_floor) float64 96B 345.0 15.0 45.0 ... 285.0 315.0
* sector_ceil (sector_ceil) float64 96B 15.0 45.0 75.0 ... 285.0 315.0 345.0
Data variables:
A (sector) float64 96B 2.223 0.6765 1.069 ... 1.517 0.6622 2.34
k (sector) float64 96B 1.943 1.463 1.034 ... 1.326 1.232 1.511
wdfreq (sector) float64 96B 179.6 90.14 67.78 ... 117.5 207.5 171.2