Wind Turbine Formats#
.wtg - Wind Turbine Generator#
XML format storing a wind turbine’s geometry and one or more performance tables.
Each PerformanceTable describes the turbine in a single operating mode, at a
single air density, as a power curve and a thrust curve.
Functions: read_wtg(), parse_wtg_element()
Ragged modes: Different modes may tabulate different wind speeds, both in
number and in range. read_wtg reports each mode’s own wind_speed array
unchanged; it never interpolates onto a common grid. The high-level
read_wtg() does that interpolation when it builds the
xarray.Dataset, because a Dataset needs a single wind_speed
coordinate.
No coordinates: unlike the wind climate formats, a .wtg describes a
device rather than a location, so the returned dict has no coords entry.
Return structure:
{
"name": str, # Description attribute
"manufacturer": str | None,
"reference_uri": str | None,
"rotor_diameter": float,
"control_system": str | None, # only from FormatVersion 1.02
"format_version": str,
"lowest_compatible_format_version": str | None,
"suggested_heights": np.ndarray, # (n_heights,)
"comments": str | None,
"modes": [ # one entry per PerformanceTable
{
"air_density": float,
"stationary_thrust_coefficient": float | None,
"maximum_noise_level": float | None,
"blade_pitch_angle": float | None,
"data_status": str | None,
"data_source": str | None,
"reference_uri": str | None,
"release_date": str | None,
"wind_speed_cutin": float, # LowSpeedCutIn
"wind_speed_cutout": float, # HighSpeedCutOut
"low_speed_cutout": float | None, # stops here on falling wind
"high_speed_cutin": float | None, # restarts here on falling wind
"comments": str | None,
"wind_speed": np.ndarray, # (n_wind_speeds,)
"power_output": np.ndarray, # (n_wind_speeds,)
"thrust_coefficient": np.ndarray, # (n_wind_speeds,)
},
],
}
Required versus optional. The keys typed above without | None are
required by the format. If one is missing, read_wtg raises
WindkitFileFormatError, a subclass of ValueError, naming the file and
the attribute, rather than returning a turbine with holes in it:
>>> read_wtg("truncated.wtg")
WindkitFileFormatError: truncated.wtg: missing required attribute 'RotorDiameter'
Elements are required as well, and one that is present but empty counts as
missing wherever it has to yield at least one value: SuggestedHeights must
hold at least one Height, the first of which is the turbine’s hub height; a
WindTurbineGenerator must hold at least one PerformanceTable; and each
PerformanceTable must hold at least one DataPoint, as well as a
StartStopStrategy for the cut-in and cut-out wind speeds:
>>> read_wtg("no_heights.wtg")
WindkitFileFormatError: no_heights.wtg: SuggestedHeights contains no Height elements
FormatVersion is checked more closely still, because the regulation type is
decided by comparing it against "1.02". A value that is not a version
number would compare as older than 1.02 and silently discard the file’s
ControlSystem, so it is rejected outright.
Every other key is always present. What an empty attribute means depends on its type:
Optional string attributes preserve
"". This distinguishes an absentManufacturerName(None) from one written asManufacturerName=""(""), which WAsP emits routinely.Optional numeric attributes read
""asNone, since an empty string is not a number.
Malformed XML raises lxml.etree.XMLSyntaxError, which subclasses
SyntaxError.
WAsP models starting and stopping as a hysteresis loop over four wind speeds,
so that a turbine sitting near a threshold in gusty wind does not trip on and
off repeatedly. As the wind rises the turbine starts at LowSpeedCutIn and
shuts down at HighSpeedCutOut; as it falls again the turbine restarts at
HighSpeedCutIn and stops at LowSpeedCutOut. The four therefore order as
LowSpeedCutOut <= LowSpeedCutIn < HighSpeedCutIn <= HighSpeedCutOut
The high-level read_wtg() Dataset carries only the rising-wind
pair, as wind_speed_cutin and wind_speed_cutout — these are the cut-in
and cut-out wind speeds quoted on a manufacturer’s spec sheet. read_wtg
reports all four, so the falling-wind thresholds survive the read.
Example:
from windkit.io.wasp import read_wtg
data = read_wtg("turbine.wtg")
print(data["name"]) # 'V90-3.0 MW VCRS 60 Hz'
print(len(data["modes"])) # 12
for mode in data["modes"]:
ws = mode["wind_speed"]
print(mode["air_density"], ws[0], ws[-1], ws.size)
Parsing an embedded element:
.wtg elements also appear inside WAsP workspace (.wwh) XML.
parse_wtg_element accepts either an lxml.etree element or a
xml.etree.ElementTree element:
from lxml import etree
from windkit.io.wasp import parse_wtg_element
root = etree.parse("workspace.xml").getroot()
for element in root.iter("WindTurbineGenerator"):
data = parse_wtg_element(element)
print(data["name"])