Core#
The two realized containers and the windIO persistence
functions. Realization itself is two steps and is documented on the
Sampling page. Describe the scenario you want as an
Sampler, then draw scenarios from that description with
sample() or stream().
A scenario is a Farm, whose boundary may be a multipolygon when a
single project holds several lease pieces. A description that sets n_farms= or
domain= makes a scenario a Cluster of farms placed next to one
another instead, which carries what belongs to the set rather than to any
member, namely the root seed that replays the arrangement, the placement record,
and one wind resource and surface for the whole set. Cluster.plot draws every
member on one shared map, because they were placed in one coordinate frame.
Which of the two a scenario is follows from the description rather than from
what a particular draw rolls, so a population never mixes them.
save() and load() round-trip one farm, turbine or wind
resource, and the windIO document kind fixes which one load returns. A
cluster has its own directory format through
Cluster.save and
Cluster.load. A population is an ordinary tuple, so
persisting or plotting one is an ordinary loop.
import oowl
from oowl import boundaries as B
from oowl import layouts as L
farms = oowl.Sampler(
B.OffshorePolygon, L.Grid, n_turbines=(40, 120), area=(30e6, 90e6), seed=7
).sample(5)
for index, farm in enumerate(farms):
farm.save(f"dataset/farm_{index:04d}.yaml")
|
Write obj to path as a windIO document and return the path. |
|
Read a windIO document into the OOWL object it describes. |
|
One wind farm with turbine positions and their associated context. |
|
A sequence of |
- oowl.save(obj, path, *, name=None, crs=None, energy_resource=None, document=None, surface_format='yaml')[source]#
Write obj to path as a windIO document and return the path.
- Parameters:
obj (Farm | Turbine | WindResource | Dimitrov) – A
Farm, aTurbine, aWindResourceor aDimitrovdesign.path (str | Path) – Destination
.yamlfile. Missing parent directories are created.name (str | None) – Name recorded in the document. Defaults to the farm’s
metadata["name"], as kept byload(), then to"oowl farm", to the turbine’s name for a turbine and to"oowl wind resource"for a resource.crs (str | None) – Optional PROJ/EPSG string stored with every coordinate set. Defaults to a CRS retained by
load(). A turbine or resource document has no coordinates, so passingcrsfor one raisesValueError.energy_resource (dict | None) – Optional windIO
energy_resourcemapping. Defaults to the farm’s attached resource. Only a site or system document carries one, so passing it for a turbine, a resource ordocument="wind_farm"raisesValueError.document (Literal['wind_energy_system', 'wind_farm', 'site', 'turbine', 'energy_resource'] | None) – Document kind to write. A turbine is always
"turbine"and a resource"energy_resource". A farm defaults to"wind_energy_system", and to"wind_farm"when it has no boundary, since windIO sites need one."wind_farm"writes the layout alone and"site"the boundary, resource and surface alone. A site or system for a farm without a resource gets a labeled uniform placeholder.surface_format (Literal['yaml', 'netcdf']) –
"yaml"writes a surface into the document in windIO point form, which validates against the windIO schema."netcdf"writes it beside the document as<name>_surface.nc, a grid ofx,yanddepthorelevation, and includes it with!include. That file is about five times smaller and reads and writes in a fraction of the time, and bothload()and windIO load it, but windIO’s schema does not accept the included grid, so the site no longer validates. It needs PyYAML, and a document without a site raisesValueErrorfor it.
- Return type:
Path
- oowl.load(path)[source]#
Read a windIO document into the OOWL object it describes.
A
wind_energy_systembecomes aFarmwith boundary, resource, surface and turbines. Awind_farmdocument gives a farm with only positions and turbines, and asitedocument a farm with only boundary, resource and surface. Aturbinedocument becomes aTurbineand anenergy_resourcedocument aWindResource.The boundary is the site boundaries minus exclusions, and positions come from the first layout. A circle boundary becomes a circumscribed 64-gon, so turbines on its rim stay inside. YAML
!includefiles are read relative to the including file, and a file included twice is read and built once, so the objects that include it share it. A NetCDF include is read as a surface grid, assave(..., surface_format="netcdf")writes it, and any other include is refused. A CRS found in the coordinates is kept in the farm metadata. Layoutzcoordinates are not read, because a farm takes turbine heights from its terrain, so awind_farmdocument saved from a terrain farm reloads without them.A resource loads from per-sector Weibull parameters, from a probability table over direction and speed, from a rose with one wind speed for every direction, which every case then blows at, or from a time series, counted into 12 sectors and 1 m/s speed bins. A resource mapped over the site, given per turbine or varying with height raises
ValueError. windIO has no field for the stability climate, so a loaded resource uses the defaultKellyGryningand ignores any storedLMO. windIO also cannot storeFarm.parts, so a multipart farm reloads as one farm.Cluster.savekeeps farms apart.- Parameters:
path (str | Path)
- Return type:
Farm | Turbine | WindResource
- class oowl.Farm(positions, boundary, *, metadata=None, guide=None, resource=None, turbines=None, metrics=None, surface=None)[source]#
One wind farm with turbine positions and their associated context.
positionshas shape(N, 2)in meters.turbinesis one sharedTurbineor one per position.metadatarecords how the farm was generated, andmetricscachessummary().- Parameters:
positions (ArrayLike)
boundary (Boundary | None)
metadata (dict | None)
guide (StringGuide | None)
resource (WindResource | Dimitrov | None)
metrics (dict | None)
surface (Surface | None)
- property bounds: tuple[float, float, float, float]#
Turbine-position bounding box
(min_x, min_y, max_x, max_y)in meters.
- capacity()[source]#
Installed capacity in watts, the rated powers of the turbines summed.
nanfor a farm without turbines.- Return type:
- property coordinates: ndarray#
Return turbine coordinates as
(x, y, z)rows in meters.zis the terrain elevation when the surface is terrain, else zero.
- coverage()[source]#
Share of the boundary’s area inside the convex hull of the turbines.
Near 1 when the turbines span their whole site and small when they bunch in one part of it. Turbines that enclose no area, fewer than three or in one line, cover none.
nanfor a farm without a boundary.- Return type:
- maximum_spacing()[source]#
Nearest-neighbor spacing of the loneliest turbine in meters.
The largest distance from a turbine to its nearest neighbor,
nanbelow two turbines, which the top end of a sampler’srequirements={"spacing": (None, maximum)}bounds.- Return type:
- mean_spacing()[source]#
Mean nearest-neighbor spacing in meters,
nanbelow two turbines.- Return type:
- minimum_spacing()[source]#
Closest pair in meters,
nanbelow two turbines.What a layout’s
minimum_spacingholds, so a farm sized by one reaches at least it. A sampled farm records it inmetadata["sample"]["closest_pair"].- Return type:
- nearest_neighbor_distances()[source]#
Distance in meters from each turbine to its nearest neighbor.
Shape
(N,), filled withnanwhen the farm has fewer than two turbines, as inoowl.metrics.nearest_neighbor_distances().- Return type:
- property parts: tuple[Farm, ...]#
This farm’s recorded pieces, each as a farm of its own.
Pieces come from a disjoint boundary, a partition or a region. On a partitioned boundary
farm.parts[i]is the farm infarm.boundary.parts[i]. A farm of one piece returns(self,). A boundary whose part count differs from the recordedcomponentsraisesValueError.
- plot(ax=None, **kwargs)[source]#
Plot the farm and return its Matplotlib axes.
Extra keyword arguments go to Matplotlib’s
scatterfor the turbines, such assandcolor. Turbines default to the first color of the style’s property cycle.facecolor,edgecolor,alphaandlinewidthstyle the boundary.regionsshades exclusion zones, andregions=Falsepunches them out.- Parameters:
ax (matplotlib.axes.Axes | None)
kwargs (Any)
- Return type:
- save(path, **kwargs)[source]#
Write this farm to path as a windIO document.
Wraps
oowl.save()and takes the same keyword arguments (name,crs,energy_resource,document).documentis"wind_energy_system"(default),"wind_farm"(the default for a farm without a boundary) or"site". Returns the written path.
- summary(metrics=None, refresh=False)[source]#
Overview of this farm as an ordered
name -> valuedict.metrics=Nonegives the turbine count, the mean nearest-neighbor distance and its coefficient of variation, the density and the area. Otherwise pass names fromoowl.metrics.SCALAR_METRICS. The result is cached onself.metricsuntil the positions or the boundary change. Passrefresh=Trueto recompute.
- class oowl.Cluster(farms, *, metadata=None)[source]#
A sequence of
Farmmembers with metadata for the whole set.Cluster metadata holds the root seed, the placement record and any shared wind resource and surface. A slice is a cluster of the kept members whose per-member records are cut to match.
- Parameters:
farms (Sequence[Farm])
metadata (Mapping | None)
- property bounds: tuple[float, float, float, float]#
Turbine-position bounding box
(min_x, min_y, max_x, max_y)in meters.
- classmethod load(directory)[source]#
Rebuild a cluster from a directory written by
Cluster.save().Loads each farm through windIO, then restores its metadata and partition cells from the manifest. A farm keeps the name and CRS its file records, and farms that include one file share the object it holds. Raises
FileNotFoundErrorfor a missing manifest andValueErrorfor an unsupported schema or a malformed entry.
- plot(ax=None, **kwargs)[source]#
Plot every member farm on one map and return its Matplotlib axes.
Each member is drawn in its own color of the Matplotlib cycle, in member order. For a cluster scattered across a
domain,regionsoutlines the domain and shades its exclusion zones, andregions=Falseomits them. Other keywords go toplot(), so turbine size iss. RaisesValueErrorwhen the cluster holds no farms.- Parameters:
ax (matplotlib.axes.Axes | None)
kwargs (Any)
- Return type:
- save(directory, **kwargs)[source]#
Write the cluster into
directoryand return that directory.Writes
manifest.jsonholdingschema_version, the cluster metadata and a{file, summary, metadata}entry per farm. A farm on partition cells adds their hex WKB ascells, so its parts reload in the same order. Each farm is saved as windIO YAML (farm_%04d.yaml), a system with its site where it has a boundary and awind_farmdocument of its positions and turbines where it has none, asFarm.save()writes them. Extra keyword arguments go toFarm.save, onlynameandcrsto a farm without a boundary. A resource or surface that several farms share is written once, asresource_%04d.yamlorsurface_%04d.yaml, which their files!include. windIO records no guide, and a farm without a boundary has no site to hold a resource or surface, soCluster.load()does not recover those.