Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions test/plant/test_mixed_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
"""Functional coverage for mixed turbine types and per-turbine wind resources.

The windIO standard supports assigning different turbine types (and therefore
different hub heights) to different positions in a wind farm, and supports
wind-resource data dimensioned per turbine. These tests assert that those
structures parse and round-trip through windIO's public API, rather than only
being validated implicitly via the example-validation sweep.
"""

from pathlib import Path

import windIO


def _plant_examples_dir():
return Path(windIO.plant_ex.__file__).parent


def test_mixed_turbine_types_wind_farm():
"""A wind farm assigns >1 turbine type by per-position index, and the
referenced types have distinct hub heights (mixed hub heights)."""
farm_yaml = _plant_examples_dir() / "plant_wind_farm" / "multiple_types.yaml"

# Mixed types are a first-class windIO feature -> must validate.
windIO.validate(input=farm_yaml, schema_type="plant/wind_farm")

farm = windIO.load_yaml(farm_yaml)
layout = farm["layouts"][0]
type_idx = layout["turbine_types"]
n_positions = len(layout["coordinates"]["x"])

# A per-position type index referencing a multi-entry turbine_types map.
assert len(farm["turbine_types"]) >= 2
assert len(type_idx) == n_positions
assert set(type_idx) == set(farm["turbine_types"].keys())
# The example genuinely uses both types.
assert len(set(type_idx)) >= 2

# The assigned types have distinct hub heights -> mixed hub heights.
hub_heights = {k: t["hub_height"] for k, t in farm["turbine_types"].items()}
assert len(set(hub_heights.values())) >= 2


def test_per_turbine_wind_resource_roundtrip():
"""A wind resource dimensioned per turbine (one hub height each, no shared
vertical profile) round-trips through dict_to_netcdf with the
``wind_turbine`` dimension and per-turbine height preserved."""
res_yaml = _plant_examples_dir() / "plant_energy_resource" / "WTResource.yaml"

windIO.validate(input=res_yaml, schema_type="plant/energy_resource")

resource = windIO.load_yaml(res_yaml)
ds = windIO.dict_to_netcdf(resource["wind_resource"])

# Per-turbine resource: wind_turbine is a real dimension.
assert "wind_turbine" in ds.dims
assert ds.sizes["wind_turbine"] >= 2

# Height is given per turbine (not a shared scalar / vertical profile).
assert "height" in ds.variables
assert ds["height"].dims == ("wind_turbine",)
assert ds["height"].sizes["wind_turbine"] == ds.sizes["wind_turbine"]

# The resource's data variables carry the per-turbine dimension (here
# wind_speed / wind_direction are binned coordinate axes, so check the
# actual per-turbine fields).
per_turbine_vars = [
v for v in ds.data_vars if "wind_turbine" in ds[v].dims
]
assert per_turbine_vars, "no data variable carries the wind_turbine dimension"
assert "sector_probability" in per_turbine_vars
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ attributes:
ws_superposition: Linear
ti_superposition: Linear
rotor_averaging:
name: GQGrid
name: gq_grid
n_x_grid_points: 5
n_y_grid_points: 5
background_averaging: center
Expand Down
21 changes: 15 additions & 6 deletions windIO/schemas/plant/wind_energy_system.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,13 @@ properties:
type: number # (default 0)
free_stream_ti:
title: Flag deciding to use freestream or waked TI
description: TI feeding the wake-expansion coefficient (k = k_a*TI + k_b) and TI-dependent deficits. False (default) = waked/effective TI; True = freestream TI.
type: boolean # (default to False)
ceps:
title: Bastankhah c_epsilon factor
type: number
use_effective_ws:
title: flag to use freestream wind speed for deficit computation
type: boolean
use_effective_ti:
title: flag to use effective turbulence intensity
title: flag to use local (effective) wind speed for deficit computation (True=waked, False=freestream)
type: boolean
A:
title: TurboNOJ wake expansion parameter
Expand Down Expand Up @@ -113,6 +111,9 @@ properties:
coefficients:
title: coefficients
type: array
c0:
title: STF/IEC model coefficient 0
type: number
c1:
title: STF model coefficient 1
type: number
Expand All @@ -128,7 +129,7 @@ properties:
ws_superposition:
title: Speed superposition model name
type: string
enum: ["Linear", "Squared", "Max", "Product", "Weighted", "Cumulative"]
enum: ["Linear", "Squared", "Max", "Product", "Weighted", "Cumulative", "Vector"]
ti_superposition:
title: TI superposition model name
type: string
Expand All @@ -141,8 +142,16 @@ properties:
properties:
name:
title: Rotor averaging model name
description: >-
Engine-neutral: center, grid (regular rotor grid / GridRotorAvg),
eq_grid, gq_grid, polar_grid, cgi, gaussian_overlap, area_overlap.
gaussian_overlap/area_overlap are non-node overlap models — NOT compatible
with 'Weighted' superposition, which requires a node model (use grid).
Capitalized names are deprecated aliases of the lowercase forms.
type: string
enum: ["Center", "Avg_Deficit", "EqGrid", "GQGrid", "PolarGrid", "CGI"]
enum: ["center", "grid", "eq_grid", "gq_grid", "polar_grid", "cgi",
"gaussian_overlap", "area_overlap",
"Center", "Avg_Deficit", "EqGrid", "GQGrid", "PolarGrid", "CGI"]
n:
title: Number of grid or integration points
type: integer
Expand Down