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 absent ManufacturerName (None) from one written as ManufacturerName="" (""), which WAsP emits routinely.

  • Optional numeric attributes read "" as None, 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"])