Binned Wind Climate (BWC)#

The binned wind climate contains a histogram representation of the wind for different wind direction sectors. In WAsP, this is called an “Observed Wind Climate” and is stored in .tab and .owc files.

Data Structure#

BWC objects are xarray.Dataset with the following schema:

Dimensions:  (sector, wsbin, ...)
Variables:
    wsfreq  (sector, wsbin, ...)  - wind speed frequency per sector/bin
    wdfreq  (sector, ...)         - wind direction frequency per sector

In addition to the core spatial coordinates, BWC objects contain:

  • wsbin - wind speed bin centers

  • wsfloor, wsceil - bin boundaries

  • sector - sector centers

  • sector_floor, sector_ceil - sector boundaries

I/O#

Format

Read

Write

Description

.tab

yes

yes

WAsP TAB format

.owc

yes

no

WAsP Observed Wind Climate

.omwc

yes

no

WAsP Observed Mesoscale Wind Climate

.nc

yes

via .to_netcdf()

NetCDF format

.yaml, .yml

yes

yes

windIO (experimental)

Reading:

windkit.read_bwc() reads binned wind climates:

bwc = wk.read_bwc("bwc.omwc", crs="EPSG:4326")

Writing:

BWC’s can be written to NetCDF using xarray’s built-in method:

bwc.to_netcdf("output.nc")

windkit.bwc_to_file() can write BWC’s to .tab and windIO YAML files:

wk.bwc_to_file(bwc, "output.tab")
wk.bwc_to_file(bwc, "output.yaml")

Creating from Arrays#

wsbins = np.linspace(0.0, 30.0, 31)
wdbins = np.linspace(-15.0, 345.0, 13)

wsfreq = np.random.rand(30, 12)
wsfreq = wsfreq / wsfreq.sum(axis=0)

wdfreq = np.random.rand(12)
wdfreq = wdfreq / wdfreq.sum()

bwc = xr.Dataset(
    data_vars=dict(
        wsfreq=(("wsbin", "sector"), wsfreq),
        wdfreq=(("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)),
    )
)
bwc
<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.04341 0.06709 ... 0.03302
    wdfreq        (sector) float64 96B 0.08239 0.1142 0.04653 ... 0.0409 0.09961

Creating from TSWC#

windkit.bwc_from_tswc() creates BWC from time series data:

# First create a TSWC
tswc = xr.Dataset(
    data_vars=dict(
        wind_speed = xr.DataArray(np.random.rand(1000)*15, dims=['time']),
        wind_direction = xr.DataArray(np.random.rand(1000)*360, dims=['time']),
    ),
    coords=dict(
        time = pd.date_range('2020-01-01', periods=1000, freq='h'),
        height = 80.0,
    )
)
tswc = tswc.expand_dims("stacked_point").assign_coords(
    west_east = (("stacked_point",), [10.0]),
    south_north = (("stacked_point",), [56.0]),
)
tswc = tswc.expand_dims("height").assign_coords(
    height = (("height",), [80.0]),
)
tswc = wk.spatial.set_crs(tswc, crs=4326)

# Convert to BWC
bwc = wk.bwc_from_tswc(tswc)

Wind Direction Frame#

The sectors of a BWC are normally relative to true north. A model that works on a projected grid may instead need them relative to grid north, which differs from true north by the grid convergence. The wind_dir_crs argument states which frame the directions are in.

windkit.bwc_from_tswc() with wind_dir_crs reprojects the time series to that CRS and rotates each wind direction from true north to its grid north before binning. The CRS is recorded in the wind_dir_crs attribute:

bwc_grid = wk.bwc_from_tswc(tswc, wind_dir_crs="EPSG:32632")
print(wk.spatial.get_crs(bwc_grid).name)
print(f"Rotation: {wk.spatial.grid_convergence(bwc_grid).item():.2f} degrees")
print("wind_dir_crs" in bwc_grid.attrs)
WGS 84 / UTM zone 32N
Rotation: 0.83 degrees
True

If the time series already has a wind_dir_crs attribute, its directions are taken to be in that frame, so they are not rotated twice.

windkit.create_bwc(), windkit.read_bwc() and windIO import accept the same argument but do not rotate anything: use it there to declare that the frequencies you have are already relative to the grid north of that CRS. A geographic CRS denotes true north.

Combining BWCs#

windkit.combine_bwcs() combines multiple BWC datasets into one:

# Create two BWCs at different locations
bwc1 = bwc.expand_dims("point").assign_coords(
    west_east=(("point",), [10.0]),
    south_north=(("point",), [56.0]),
    height=(("point",), [80.0]),
)
bwc1 = wk.spatial.set_crs(bwc1, crs=4326)

bwc2 = bwc.expand_dims("point").assign_coords(
    west_east=(("point",), [11.0]),
    south_north=(("point",), [57.0]),
    height=(("point",), [80.0]),
)
bwc2 = wk.spatial.set_crs(bwc2, crs=4326)

# Combine into one dataset
combined = wk.combine_bwcs([bwc1, bwc2])
print(f"Combined BWC points: {wk.spatial.count_spatial_points(combined)}")
Combined BWC points: 1

Validation#

wk.is_bwc(bwc)
True