Wind Climate Objects

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 (twc) 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, twc objects also contain the dimension time, which identifies the time of the given wind data.

Creating a xarray.Dataset object in the twc format is quite simple using the xarray.Dataset constructor. The following example creates a twc 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 3.235 4.405 5.022 ... 1.853 6.148
    wind_direction  (time, height) float64 2kB 351.4 190.8 89.14 ... 57.27 52.32

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.add_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.add_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 3.235 ... 6.148
    wind_direction  (stacked_point, time, height) float64 2kB 351.4 ... 52.32

Windkit provides a function to create a twc object from a pandas.DataFrame object using the windkit.read_timeseries_from_pandas() 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        8.990897  ...             8.203588
2020-01-01 01:00:00        4.080368  ...            97.050091
2020-01-01 02:00:00        0.148608  ...           169.324875
2020-01-01 03:00:00        8.563131  ...            99.470242
2020-01-01 04:00:00        9.036603  ...           258.821191
...                             ...  ...                  ...
2020-01-04 23:00:00        3.691127  ...           131.456121
2020-01-05 00:00:00        5.715991  ...           251.237014
2020-01-05 01:00:00        1.075239  ...           276.963440
2020-01-05 02:00:00        9.797873  ...           282.629557
2020-01-05 03:00:00        4.603236  ...           349.278724

[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.read_timeseries_from_pandas(
   ....:     df,
   ....:     west_east,
   ....:     south_north,
   ....:     crs=4326,
   ....:     height_to_columns=height_to_columns,
   ....: )
   ....: 
Please enter your name 
EOF when reading a line
Please enter your name 
EOF when reading a line
Please enter your name 
EOF when reading a line
Please enter your name 
EOF when reading a line
Please enter your name 
EOF when reading a line
Please enter your email adress. 
EOF when reading a line
Please enter your email adress. 
EOF when reading a line
Please enter your email adress. 
EOF when reading a line
Please enter your email adress. 
EOF when reading a line
Please enter your email adress. 
EOF when reading a line
Please enter your institution 
EOF when reading a line
Please enter your institution 
EOF when reading a line
Please enter your institution 
EOF when reading a line
Please enter your institution 
EOF when reading a line
Please enter your institution 
EOF when reading a line

In [17]: tswc
Out[17]: 
<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      (time, height, stacked_point) float64 2kB 8.991 ... 7.447
    wind_direction  (time, height, stacked_point) float64 2kB 292.7 ... 349.3
Attributes:
    history:          2024-06-10T14:18:15+00:00:\twindkit==0.8.0\twk.read_tim...
    Conventions:      CF-1.8
    Package name:     windkit
    Package version:  0.8.0
    Creation date:    2024-06-10T14:18:16+00:00
    Object type:      Time Series Wind Climate
    author:           None
    author_email:     None
    institution:      None

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.02824 0.015 ... 0.05107 0.007025
    wdreq         (sector) float64 96B 0.1435 0.06093 ... 0.06878 0.005435

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 4.232 ... ...
    k              (gen_height, gen_roughness, sector) float64 2kB 1.249 ... ...
    wdfreq         (gen_height, gen_roughness, sector) float64 2kB 17.77 ... ...

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 6.392 9.529 5.907 ... 6.323 2.678 3.6
    k             (sector) float64 96B 1.499 1.516 1.963 ... 1.108 1.364 1.899
    wdfreq        (sector) float64 96B 92.85 81.14 242.9 ... 0.8642 104.8 104.9