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, file-specific readers are available:

  • .rsf: windkit.read_rsffile()

  • .wrg: windkit.read_wrgfile()

  • .grd: windkit.read_grdfile()

  • .pwc: windkit.read_pwcfile()

For writing wind climate files, windkit have the function windkit.bwc_to_tabfile() for writing BWC’s to the .tab format, windkit.to_libfile() for writing GWC’s to .lib files, and windkit.to_rsffile() and windkit.to_wrgfile() for writing WWC’s to .rsf and .wrg.

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>
Dimensions:       (point: 1, sector: 12, wsbin: 32)
Coordinates:
    height        (point) float64 25.3
    south_north   (point) float64 41.74
    west_east     (point) float64 -8.823
    crs           int8 0
  * wsbin         (wsbin) float64 0.5 1.5 2.5 3.5 4.5 ... 28.5 29.5 30.5 31.5
    wsceil        (wsbin) float64 1.0 2.0 3.0 4.0 5.0 ... 29.0 30.0 31.0 32.0
    wsfloor       (wsbin) float64 0.0 1.0 2.0 3.0 4.0 ... 28.0 29.0 30.0 31.0
  * sector        (sector) float64 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
    sector_ceil   (sector) float64 15.0 45.0 75.0 105.0 ... 285.0 315.0 345.0
    sector_floor  (sector) float64 345.0 15.0 45.0 75.0 ... 255.0 285.0 315.0
Dimensions without coordinates: point
Data variables:
    wdfreq        (sector, point) float64 0.05314 0.03321 ... 0.1148 0.0707
    wsfreq        (wsbin, sector, point) float64 0.02601 0.04219 ... 0.0 0.0
Attributes:
    Conventions:      CF-1.8
    history:          2024-06-11T13:38:19+00:00:\twindkit==0.8.0\tcreate_data...
    wasp_header:      SerraSantaluzia
    Package name:     windkit
    Package version:  0.8.0
    Creation date:    2024-06-11T13:38:19+00:00
    Object type:      Binned Wind Climate
    author:           DTU CI Config
    author_email:     pywasp@dtu.dk
    institution:      DTU Wind

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 0x7f850ff6ae50>]
../_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>
Dimensions:       (point: 1, sector: 12, wsbin: 32)
Coordinates:
    height        (point) float64 25.3
    crs           int8 0
  * wsbin         (wsbin) float64 0.5 1.5 2.5 3.5 4.5 ... 28.5 29.5 30.5 31.5
    wsceil        (wsbin) float64 1.0 2.0 3.0 4.0 5.0 ... 29.0 30.0 31.0 32.0
    wsfloor       (wsbin) float64 0.0 1.0 2.0 3.0 4.0 ... 28.0 29.0 30.0 31.0
  * sector        (sector) float64 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
    sector_ceil   (sector) float64 15.0 45.0 75.0 105.0 ... 285.0 315.0 345.0
    sector_floor  (sector) float64 345.0 15.0 45.0 75.0 ... 255.0 285.0 315.0
    west_east     (point) float64 5.147e+05
    south_north   (point) float64 4.621e+06
Dimensions without coordinates: point
Data variables:
    wdfreq        (sector, point) float64 0.05314 0.03321 ... 0.1148 0.0707
    wsfreq        (wsbin, sector, point) float64 0.02601 0.04219 ... 0.0 0.0
Attributes:
    Conventions:      CF-1.8
    history:          2024-06-11T13:38:19+00:00:\twindkit==0.8.0\tcreate_data...
    wasp_header:      SerraSantaluzia
    Package name:     windkit
    Package version:  0.8.0
    Creation date:    2024-06-11T13:38:19+00:00
    Object type:      Binned Wind Climate
    author:           DTU CI Config
    author_email:     pywasp@dtu.dk
    institution:      DTU Wind

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.add_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_windspeed(bwc)

In [10]: print(mean_ws)
<xarray.DataArray 'wspd_combined' (point: 1)>
array([6.28573163])
Coordinates:
    height       (point) float64 25.3
    crs          int8 0
    west_east    (point) float64 5.147e+05
    south_north  (point) float64 4.621e+06
Dimensions without coordinates: point
Attributes:
    cell_methods:   sector: combined
    description:    Mean wind speed for all wind directions, representing the...
    long_name:      Horizontal Wind Speed
    standard_name:  wind_speed
    units:          m s-1
    grid_mapping:   crs

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>
Dimensions:       (sector: 12, point: 1)
Coordinates:
    height        (point) float64 25.3
    crs           int8 0
  * sector        (sector) float64 0.0 30.0 60.0 90.0 ... 270.0 300.0 330.0
    sector_ceil   (sector) float64 15.0 45.0 75.0 105.0 ... 285.0 315.0 345.0
    sector_floor  (sector) float64 345.0 15.0 45.0 75.0 ... 255.0 285.0 315.0
    west_east     (point) float64 5.147e+05
    south_north   (point) float64 4.621e+06
Dimensions without coordinates: point
Data variables:
    wdfreq        (sector, point) float64 0.05314 0.03321 ... 0.1148 0.0707
    A             (sector, point) float64 5.47 5.346 5.746 ... 8.029 7.312 6.022
    k             (sector, point) float64 1.924 2.145 2.594 ... 2.265 2.185 1.97
Attributes:
    Conventions:      CF-1.8
    history:          2024-06-11T13:38:19+00:00:\twindkit==0.8.0\tcreate_data...
    wasp_header:      SerraSantaluzia
    Package name:     windkit
    Package version:  0.8.0
    Creation date:    2024-06-11T13:38:19+00:00
    Object type:      Weibull Wind Climate
    author:           DTU CI Config
    author_email:     pywasp@dtu.dk
    institution:      DTU Wind

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_windspeed(wwc)

In [14]: print(mean_ws_wwc)
<xarray.DataArray 'wspd' (point: 1)>
array([6.24057066])
Coordinates:
    height       (point) float64 25.3
    crs          int8 0
    west_east    (point) float64 5.147e+05
    south_north  (point) float64 4.621e+06
Dimensions without coordinates: point
Attributes:
    description:    Speed is the magnitude of velocity. Wind is defined as a ...
    long_name:      Horizontal Wind Speed
    standard_name:  wind_speed
    units:          m s-1
    grid_mapping:   crs

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.power_density(wwc) / wk.power_density(bwc))

In [16]: print(diff_prc)
<xarray.DataArray (point: 1)>
array([2.22044605e-14])
Coordinates:
    height       (point) float64 25.3
    crs          int8 0
    west_east    (point) float64 5.147e+05
    south_north  (point) float64 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>
Dimensions:           (i: 1, wd: 13, ws: 23)
Coordinates:
    h                 (i) float64 25.3
  * wd                (wd) float64 0.0 30.0 60.0 90.0 ... 300.0 330.0 360.0
    x                 (i) float64 5.147e+05
    y                 (i) float64 4.621e+06
  * ws                (ws) int64 3 4 5 6 7 8 9 10 11 ... 18 19 20 21 22 23 24 25
Dimensions without coordinates: i
Data variables:
    Sector_frequency  (i, wd) float64 0.05314 0.03321 0.04742 ... 0.0707 0.05314
    Weibull_A         (i, wd) float64 5.47 5.346 5.746 6.73 ... 7.312 6.022 5.47
    Weibull_k         (i, wd) float64 1.924 2.145 2.594 ... 2.185 1.97 1.924
    Speedup           (i, wd) float64 1.0 1.0 1.0 1.0 1.0 ... 1.0 1.0 1.0 1.0
    TI                (i, wd) float64 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0
Attributes:
    Conventions:      CF-1.8
    history:          2024-06-11T13:38:19+00:00:\twindkit==0.8.0\tcreate_data...
    wasp_header:      SerraSantaluzia
    Package name:     windkit
    Package version:  0.8.0
    Creation date:    2024-06-11T13:38:19+00:00
    Object type:      Weibull Wind Climate
    author:           DTU CI Config
    author_email:     pywasp@dtu.dk
    institution:      DTU Wind
    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.