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>
Dimensions:         (time: 100, height: 3)
Coordinates:
  * time            (time) datetime64[ns] 2020-01-01 ... 2020-01-05T03:00:00
  * height          (height) int64 10 50 100
Data variables:
    wind_speed      (time, height) float64 7.608 9.454 2.167 ... 1.653 7.661
    wind_direction  (time, height) float64 145.4 49.33 174.7 ... 190.0 133.8

To add the spatial information to the dataset you can add a new dimension using :py:method:`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>
Dimensions:         (time: 100, height: 3, stacked_point: 1)
Coordinates:
  * time            (time) datetime64[ns] 2020-01-01 ... 2020-01-05T03:00:00
  * height          (height) int64 10 50 100
    west_east       (stacked_point) float64 10.0
    south_north     (stacked_point) float64 56.0
    crs             int8 0
Dimensions without coordinates: stacked_point
Data variables:
    wind_speed      (stacked_point, time, height) float64 7.608 9.454 ... 7.661
    wind_direction  (stacked_point, time, height) float64 145.4 49.33 ... 133.8

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        2.141854  ...           212.631468
2020-01-01 01:00:00        9.913006  ...            56.772946
2020-01-01 02:00:00        9.805657  ...            99.955329
2020-01-01 03:00:00        8.500576  ...           137.974765
2020-01-01 04:00:00        0.416066  ...           182.515658
...                             ...  ...                  ...
2020-01-04 23:00:00        1.225836  ...           124.656773
2020-01-05 00:00:00        1.085354  ...           252.893579
2020-01-05 01:00:00        8.315246  ...           184.493827
2020-01-05 02:00:00        4.752549  ...           329.774336
2020-01-05 03:00:00        9.964969  ...           226.926077

[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>
Dimensions:         (time: 100, height: 3, stacked_point: 1)
Coordinates:
  * time            (time) datetime64[ns] 2020-01-01 ... 2020-01-05T03:00:00
  * height          (height) int64 10 50 100
    west_east       (stacked_point) float64 10.0
    south_north     (stacked_point) float64 56.0
    crs             int8 0
Dimensions without coordinates: stacked_point
Data variables:
    wind_speed      (time, height, stacked_point) float64 2.142 4.794 ... 2.981
    wind_direction  (time, height, stacked_point) float64 45.38 235.0 ... 226.9
Attributes:
    history:          2023-11-14T14:24:53:\twindkit==0.7.0\twk.read_timeserie...
    Conventions:      CF-1.8
    Package name:     windkit
    Package version:  0.7.0
    Creation date:    2023-11-14T14:24:53
    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>
Dimensions:       (wsbin: 30, sector: 12, sector_floor: 12, sector_ceil: 12)
Coordinates:
  * wsbin         (wsbin) float64 0.5 1.5 2.5 3.5 4.5 ... 26.5 27.5 28.5 29.5
    wsfloor       (wsbin) float64 0.0 1.0 2.0 3.0 4.0 ... 26.0 27.0 28.0 29.0
    wsceil        (wsbin) float64 1.0 2.0 3.0 4.0 5.0 ... 27.0 28.0 29.0 30.0
  * sector        (sector) float64 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
  * sector_floor  (sector_floor) float64 345.0 15.0 45.0 ... 255.0 285.0 315.0
  * sector_ceil   (sector_ceil) float64 15.0 45.0 75.0 ... 285.0 315.0 345.0
Data variables:
    wsfreq        (wsbin, sector) float64 0.04954 0.01514 ... 0.03633 0.01573
    wdreq         (sector) float64 0.06919 0.01715 0.1493 ... 0.0409 0.1644

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>
Dimensions:        (gen_height: 5, gen_roughness: 5, sector: 12,
                    sector_floor: 12, sector_ceil: 12)
Coordinates:
  * gen_height     (gen_height) int64 10 25 50 100 200
  * gen_roughness  (gen_roughness) float64 0.0 0.03 0.1 0.3 1.5
  * sector         (sector) float64 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
  * sector_floor   (sector_floor) float64 345.0 15.0 45.0 ... 255.0 285.0 315.0
  * sector_ceil    (sector_ceil) float64 15.0 45.0 75.0 ... 285.0 315.0 345.0
Data variables:
    A              (gen_height, gen_roughness, sector) float64 8.629 ... 8.922
    k              (gen_height, gen_roughness, sector) float64 1.75 ... 1.92
    wdfreq         (gen_height, gen_roughness, sector) float64 198.3 ... 4.427

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>
Dimensions:       (sector: 12, sector_floor: 12, sector_ceil: 12)
Coordinates:
  * sector        (sector) float64 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
  * sector_floor  (sector_floor) float64 345.0 15.0 45.0 ... 255.0 285.0 315.0
  * sector_ceil   (sector_ceil) float64 15.0 45.0 75.0 ... 285.0 315.0 345.0
Data variables:
    A             (sector) float64 3.949 8.012 4.104 ... 9.415 0.6222 1.076
    k             (sector) float64 1.394 1.166 1.293 1.557 ... 1.795 1.096 1.159
    wdfreq        (sector) float64 304.2 183.0 154.4 176.4 ... 70.53 338.3 55.91