Workspace Format (.wwh)#
Reading WAsP Workspaces#
A .wwh file is a zip archive whose Inventory.xml holds the whole WAsP
session: the projects, met stations, generalized wind climates, vector maps,
turbine sites and turbines, nested in a hierarchy. Besides those inputs it
holds what WAsP calculated from them, which no other WAsP file carries
together: the predicted wind climate and site effects at every met station,
reference site and turbine site, the annual energy production of every turbine
site, and WAsP’s Weibull fit of the observed wind climate.
read_wwh() returns the hierarchy as plain dicts:
from windkit.io.wasp import read_wwh
workspace = read_wwh("site.wwh")
print(workspace["format_version"]) # e.g. '4.01.0006'
print(len(workspace["members"])) # one dict per object in the workspace
for member in workspace["members"]:
print(member["class_description"], member["description"])
members is in document order, so a member comes before its children, and
each one records its place in the hierarchy as indices into that list:
root = workspace["members"][0]
print(root["parent"]) # None: the workspace itself
print(root["children"]) # indices of its child members
The same integers are the object IDs of the high-level
windkit.Workspace, so workspace["members"][24] is the object
Workspace.get_wwc(24) reads.
What a Member Holds#
Every member dict has the same keys, and a part the member does not hold is
None. Which parts are filled follows the kind of object:
Met. station coords, site_assessment, predicted_wind_climate,
observed_wind_climate_weibull_fit
Reference site coords, site_assessment, predicted_wind_climate
Turbine site coords, site_assessment, predicted_wind_climate, production
Turbine site group wake_modelling
Observed wind climate observed_wind_climate
Generalised wind climate generalized_wind_climate
Wind turbine generator wind_turbine_generator, air_density_correction
WAsP project, Terrain analysis (IBZ), Resource grid parameters
Vector map data layer data_layer
Reading one object’s results:
turbine = workspace["members"][3]
production = turbine["production"]
print(production["gross"]) # per sector, Wh/y as stored
print(production["sum_of_sectors_gross"]) # WAsP's total, Wh/y
print(production["wake_loss"]) # fraction, 0-1
station = workspace["members"][24]
print(station["predicted_wind_climate"]["A"]) # per sector
print(station["site_assessment"]["dirrix"]) # per sector
print(station["results_dirty"]) # {'Calculation1': False, ...}
results_dirty is WAsP’s own flag: it marks a calculation whose inputs
changed after it ran, so results read from that object may not match the rest
of the workspace.
The observed and generalized wind climates and the wind turbine generator are
parsed by parse_owc_element(),
parse_gwc_element() and
parse_wtg_element(), so they follow those functions’
own data contracts.
Parsing an Element Directly#
parse_wwh_element() reads a root you parsed yourself,
and parse_wwh_member_element() reads a single
WaspHierarchyMember element and only that member’s own data, not the
members below it. Both accept lxml.etree and
xml.etree.ElementTree elements:
import zipfile
from lxml import etree
from windkit.io.wasp import parse_wwh_element, parse_wwh_member_element
with zipfile.ZipFile("site.wwh") as archive:
root = etree.parse(archive.open("Inventory.xml")).getroot()
workspace = parse_wwh_element(root, context="site.wwh")
element = next(
el
for el in root.iter("WaspHierarchyMember")
if el.get("ClassDescription") == "Met. station"
)
station = parse_wwh_member_element(element, context="site.wwh")
Units and Precision#
The dicts mirror the file. WAsP writes its numbers as text with up to seven
significant digits, and they are parsed straight into float64, so reading adds
no rounding of its own. Energies are in Wh/y, frequencies, Rix and wake
losses are fractions (0-1), and turnings are in degrees, as stored. Nothing is
renormalized: the sector frequencies of a wind rose sum to 1 only to within the
rounding of the stored values.
WAsP Versions#
A part an older WAsP version did not write reads as None, so the same code
works across workspace generations. The differences worth knowing:
DisplacementHeightandSiteRoughnessare absent before format 4.01.0006, sosite_assessment["displ"]and["site_roughness"]areNone.The mesoscale climate a generalized wind climate was made with —
stability_inputsandgeostrophic_shear— is written by WAsP 12.8 and newer only.barometric_referenceis written by older versions too, but itslapse_rateonly from WAsP 12.6.Older workspaces instead record the profile model parameters of their generalization, as
profile_model_parameters.Format 4.01.0002 and older store combined
TerrainSpeedMultiplierandTerrainDeflectioninstead of the separate roughness and orographic speed-ups and turnings, and noRix,TotalRixorRoughnessChanges. Those keys ofsite_assessmentareNone, and the combined terrain effects are not read. Format 3.03 also stores noMeanAirDensity, sopredicted_wind_climate["air_density"]isNone.
Which WAsP versions a workspace may come from is not checked when reading; the
high-level windkit.Workspace rejects formats below 4.01.0002.
Errors#
Within a part that is present, required elements and attributes are validated
at the file boundary: a missing or unparseable one raises
WindkitFileFormatError, a subclass of ValueError. The message names the
member by its class and description, then locates the element by a zero-based
index into the document order of the elements the reader reads:
site.wwh: Turbine site 'T1': SiteAssessment: SectorAssessment[3]: missing required attribute 'Rix'
See Also#
WAsP - the high-level
windkit.WorkspaceclassWind Climate Formats - the wind climate formats a workspace embeds