Wind Climates

In PyWAsP’s Terminology and Data Structures sections we outlined how we define the meaning and data structures of different kinds of wind climate objects. Here we present how common wind climate workflows can be carried out. PyWAsP relies heavily on WindKit for a lot of this functionality, so to illustrate it let’s start by importing windkit, pywasp and xarray.

In [1]: import pywasp as pw

In [2]: import windkit as wk

In [3]: import xarray as xr

Wind climate I/O

Reading and writing wind climates is a core part of WindKit

Wind climate I/O

Format

Wind Climate

Read

Write

.nc

BWC, GWC, WWC

yes

yes

.tab

BWC

yes

yes

.owc

BWC

yes

no

.omwc

BWC

yes

no

.lib

GWC

yes

yes

.gwc

GWC

yes

no

.rsf

WWC

yes

yes

.wrg

WWC

yes

yes

.grd

WWC

yes

no

.pwc

WWC

yes

no

All supported BWC’s and GWC’s can be read using windkit.read_bwc() and windkit.read_gwc(), respectively. For WWC’s, windkit.read_wwc() can be used to read most supported formats, which include:

For writing wind climate files, windkit have the function windkit.bwc_to_file(), windkit.gwc_to_file(), and windkit.wwc_to_file().

To explify, lets start by reading in a BWC stored in the .omwc file format:

In [4]: bwc = wk.read_bwc("source/tutorials/data/SerraSantaLuzia.omwc", crs="EPSG:4326")

In [5]: print(bwc)
<xarray.Dataset> Size: 4kB
Dimensions:       (point: 1, sector: 12, wsbin: 32)
Coordinates:
    height        (point) float64 8B 25.3
    south_north   (point) float64 8B 41.74
    west_east     (point) float64 8B -8.823
    crs           int8 1B 0
    wsceil        (wsbin) float64 256B 1.0 2.0 3.0 4.0 ... 29.0 30.0 31.0 32.0
    wsfloor       (wsbin) float64 256B 0.0 1.0 2.0 3.0 ... 28.0 29.0 30.0 31.0
    sector_ceil   (sector) float64 96B 15.0 45.0 75.0 ... 285.0 315.0 345.0
    sector_floor  (sector) float64 96B 345.0 15.0 45.0 ... 255.0 285.0 315.0
  * wsbin         (wsbin) float64 256B 0.5 1.5 2.5 3.5 ... 28.5 29.5 30.5 31.5
  * sector        (sector) float64 96B 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
Dimensions without coordinates: point
Data variables:
    wdfreq        (sector, point) float64 96B 0.05314 0.03321 ... 0.1148 0.0707
    wsfreq        (wsbin, sector, point) float64 3kB 0.02601 0.04219 ... 0.0 0.0
Attributes:
    Conventions:      CF-1.8
    history:          2025-07-22T14:13:15+00:00:\twindkit==1.0.3.dev1+ga61276...
    description:      SerraSantaluzia
    Package name:     windkit
    Package version:  1.0.3.dev1+ga612767
    Creation date:    2025-07-22T14:13:15+00:00
    Object type:      Binned Wind Climate
    author:           Default User
    author_email:     default_email@example.com
    institution:      Default Institution

In this case, the .owmc file only contains coordinates, but not the coordinate reference system (CRS), so we have to set that explicitly with the crs="ESPG:4326" argument.

As Discussed BWC’s are xarray.Dataset’s’ so it is easy to use the all the built-in methods of xarray to interact with the data through the labeled dimensions and coordinates. For example, we can plot the omni-directional wind speed frequencies by multiplying the wind speed frequencies, that are stored as sector-relative frequencies, with the wind direction frequencies, summing the sectors, and then plotting with the wind speed bins on the x-axis:

In [6]: (bwc.wsfreq * bwc.wdfreq).sum(dim="sector").plot.line(x="wsbin")
Out[6]: [<matplotlib.lines.Line2D at 0x704a7fa45a90>]
../_images/bwc_histogram_example.png

Geospatial transforms

WindKit's spatial module makes it easy to work with geospatial data, like the point associated with the BWC above, and to perform typical transforms, such as reprojection, spatial clipping, and spatial masking.

To illustrate this, let’s transform the BWC from lat-long coordinates to UTM 29N coordinates:

In [7]: bwc = wk.spatial.reproject(bwc, to_crs="EPSG:32629")

In [8]: print(bwc)
<xarray.Dataset> Size: 4kB
Dimensions:       (point: 1, sector: 12, wsbin: 32)
Coordinates:
    height        (point) float64 8B 25.3
    crs           int8 1B 0
    wsceil        (wsbin) float64 256B 1.0 2.0 3.0 4.0 ... 29.0 30.0 31.0 32.0
    wsfloor       (wsbin) float64 256B 0.0 1.0 2.0 3.0 ... 28.0 29.0 30.0 31.0
    sector_ceil   (sector) float64 96B 15.0 45.0 75.0 ... 285.0 315.0 345.0
    sector_floor  (sector) float64 96B 345.0 15.0 45.0 ... 255.0 285.0 315.0
  * wsbin         (wsbin) float64 256B 0.5 1.5 2.5 3.5 ... 28.5 29.5 30.5 31.5
  * sector        (sector) float64 96B 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
    west_east     (point) float64 8B 5.147e+05
    south_north   (point) float64 8B 4.621e+06
Dimensions without coordinates: point
Data variables:
    wdfreq        (sector, point) float64 96B 0.05314 0.03321 ... 0.1148 0.0707
    wsfreq        (wsbin, sector, point) float64 3kB 0.02601 0.04219 ... 0.0 0.0
Attributes:
    Conventions:      CF-1.8
    history:          2025-07-22T14:13:15+00:00:\twindkit==1.0.3.dev1+ga61276...
    description:      SerraSantaluzia
    Package name:     windkit
    Package version:  1.0.3.dev1+ga612767
    Creation date:    2025-07-22T14:13:15+00:00
    Object type:      Binned Wind Climate
    author:           Default User
    author_email:     default_email@example.com
    institution:      Default Institution

Note

Note that the west_east and south_north coordinate values have changed after reprojection.

To set and get the crs of an object, use windkit.spatial.set_crs() and windkit.spatial.get_crs().

Calculate statistics

WindKit includes functions to calculate common statistics from wind climate objects, such as the mean wind speed or mean power density:

In [9]: mean_ws = wk.mean_wind_speed(bwc)

In [10]: print(mean_ws)
<xarray.DataArray (point: 1)> Size: 8B
array([6.28573163])
Coordinates:
    height       (point) float64 8B 25.3
    crs          int8 1B 0
    west_east    (point) float64 8B 5.147e+05
    south_north  (point) float64 8B 4.621e+06
Dimensions without coordinates: point
Attributes:
    history:  2025-07-22T14:13:15+00:00:\twindkit==1.0.3.dev1+ga612767\tbysec...

Note

The omni-directional statistics are returned by default. To return statistics by wind direction sector, use the argument bysector=True

Fit weibull distribution

WindKit can fit weibull parameters to the wind speed frequencies in a binned wind climate, thereby transforming it to a weibull wind climate. This can be done using windkit.weibull_fit():

In [11]: wwc = wk.weibull_fit(bwc)

In [12]: print(wwc)
<xarray.Dataset> Size: 601B
Dimensions:       (sector: 12, point: 1)
Coordinates:
    height        (point) float64 8B 25.3
    crs           int8 1B 0
    sector_ceil   (sector) float64 96B 15.0 45.0 75.0 ... 285.0 315.0 345.0
    sector_floor  (sector) float64 96B 345.0 15.0 45.0 ... 255.0 285.0 315.0
    west_east     (point) float64 8B 5.147e+05
    south_north   (point) float64 8B 4.621e+06
  * sector        (sector) float64 96B 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
Dimensions without coordinates: point
Data variables:
    wdfreq        (sector, point) float64 96B 0.05314 0.03321 ... 0.1148 0.0707
    A             (sector, point) float64 96B 5.47 5.346 5.746 ... 7.312 6.022
    k             (sector, point) float64 96B 1.924 2.145 2.594 ... 2.185 1.97
Attributes:
    Conventions:      CF-1.8
    history:          2025-07-22T14:13:15+00:00:\twindkit==1.0.3.dev1+ga61276...
    description:      SerraSantaluzia
    Package name:     windkit
    Package version:  1.0.3.dev1+ga612767
    Creation date:    2025-07-22T14:13:16+00:00
    Object type:      Weibull Wind Climate
    author:           Default User
    author_email:     default_email@example.com
    institution:      Default Institution

We can check that the estimated mean wind speed of the WWC is close to the BWC above:

In [13]: mean_ws_wwc = wk.mean_wind_speed(wwc)

In [14]: print(mean_ws_wwc)
<xarray.DataArray (point: 1)> Size: 8B
array([6.24057066])
Coordinates:
    height       (point) float64 8B 25.3
    crs          int8 1B 0
    west_east    (point) float64 8B 5.147e+05
    south_north  (point) float64 8B 4.621e+06
Dimensions without coordinates: point

We see that the mean wind speed of the BWC and WWC differ by about 0.7% (6.29 vs 6.24 m/s). The weibull fit in WindKit (and in WAsP) uses the third moment of wind speed to fit Weibull parameters, thus preserving the mean power density well, but not the mean wind speed. We can check this by calculating and comparing the mean power densities pf the BWC and WWC.

In [15]: diff_prc = 100.0 * (1.0 - wk.mean_power_density(wwc) / wk.mean_power_density(bwc))

In [16]: print(diff_prc)
<xarray.DataArray (point: 1)> Size: 8B
array([2.22044605e-14])
Coordinates:
    height       (point) float64 8B 25.3
    crs          int8 1B 0
    west_east    (point) float64 8B 5.147e+05
    south_north  (point) float64 8B 4.621e+06
Dimensions without coordinates: point

We see that the difference in mean power density between the two is just 0.04%.

Using wind climates as site objects in PyWake

PyWAsP wind climates objects can be used as site objects for wind farm flow modeling in PyWake. This is done by instanciating an XRSite using the py_wake.Site.XRSsite.from_pwc() method.

In [17]: import py_wake

In [18]: site = py_wake.site.XRSite.from_pwc(wwc)

In [19]: print(site.ds)
<xarray.Dataset> Size: 832B
Dimensions:           (i: 1, wd: 13, ws: 23)
Coordinates:
    h                 (i) float64 8B 25.3
    x                 (i) float64 8B 5.147e+05
    y                 (i) float64 8B 4.621e+06
  * wd                (wd) float64 104B 0.0 30.0 60.0 90.0 ... 300.0 330.0 360.0
  * ws                (ws) int64 184B 3 4 5 6 7 8 9 10 ... 19 20 21 22 23 24 25
Dimensions without coordinates: i
Data variables:
    Sector_frequency  (i, wd) float64 104B 0.05314 0.03321 ... 0.0707 0.05314
    Weibull_A         (i, wd) float64 104B 5.47 5.346 5.746 ... 7.312 6.022 5.47
    Weibull_k         (i, wd) float64 104B 1.924 2.145 2.594 ... 1.97 1.924
    Speedup           (i, wd) float64 104B 1.0 1.0 1.0 1.0 ... 1.0 1.0 1.0 1.0
    TI                (i, wd) float64 104B 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
Attributes:
    Conventions:      CF-1.8
    history:          2025-07-22T14:13:15+00:00:\twindkit==1.0.3.dev1+ga61276...
    description:      SerraSantaluzia
    Package name:     windkit
    Package version:  1.0.3.dev1+ga612767
    Creation date:    2025-07-22T14:13:16+00:00
    Object type:      Weibull Wind Climate
    author:           Default User
    author_email:     default_email@example.com
    institution:      Default Institution
    sector_width:     30.0

Spatial inhomogeneity can be taken into account in PyWake by speedup and turning variables in the site object. These variables track the relative speed-up and turning of the wind in the spatial domain during the wind farm flow modeling.

The py_wake.Site.XRSsite.from_pwc() method can take different speedup arguments for specifying the way to calculate speed-ups between spatial points. By default, the “park” method is used, which calculates the sector-wise speed-ups relative to the maximum mean wind speed in that sector.

Depending on how the speed-up is defined/calculated in the PyWake site object, the post-processing, or aggregation, of wind farm flow results should take this into account.