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 centerswsfloor,wsceil- bin boundariessector- sector centerssector_floor,sector_ceil- sector boundaries
I/O#
Format |
Read |
Write |
Description |
|---|---|---|---|
|
yes |
yes |
WAsP TAB format |
|
yes |
no |
WAsP Observed Wind Climate |
|
yes |
no |
WAsP Observed Mesoscale Wind Climate |
|
yes |
via |
NetCDF format |
|
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.09961Creating 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#
windkit.validate_bwc()- Validate BWC dataset structurewindkit.is_bwc()- Check if dataset is valid BWC
wk.is_bwc(bwc)
True