Cable routing with OptiWindNet#

OptiWindNet routes cables from turbines to a substation. The boundary exterior becomes its border and each exclusion zone an obstacle.

OptiWindNet is not an OOWL dependency.

%%capture
%pip install --quiet optiwindnet==0.3.0

Import OOWL, Shapely and OptiWindNet.

import matplotlib.pyplot as plt
import numpy as np
from optiwindnet.api import EWRouter, WindFarmNetwork
from shapely.geometry import Point

import oowl
from oowl import boundaries as B
from oowl import layouts as L
from oowl import modifiers as M

A farm with an exclusion zone#

Turbines stay out of the excluded area.

exclusion = Point(600.0, 400.0).buffer(900.0, quad_segs=8)
boundary = B.Rectangle(3, 2, area=36e6).modify(M.ExclusionZones([exclusion]))
farm = oowl.Sampler(boundary, L.Grid(n_turbines=30, seed=4), seed=4).sample()[0]
farm.plot();
../_images/84222dabf6b3091cab81c19a07d6cc5c330398e762ecda92a9fcb3e6d88aa49e.png

OOWL boundary → border and obstacles#

The substation sits near one corner, where the export cable would leave the farm.

polygon = boundary.geometry
border = np.asarray(polygon.exterior.coords)[:-1]
obstacles = [np.asarray(ring.coords)[:-1] for ring in polygon.interiors]

min_x, min_y, _, _ = boundary.bounds
substations = np.array([[min_x + 200.0, min_y + 200.0]])
print(f"{len(border)} border vertices, {len(obstacles)} obstacle(s)")
print("substation inside the boundary:", bool(boundary.contains(substations).all()))

fig, ax = plt.subplots()
farm.plot(ax=ax)
ax.scatter(*substations.T, color="C3", s=100)
fig.tight_layout()
4 border vertices, 1 obstacle(s)
substation inside the boundary: True
../_images/f1121834bacc6abfe59420c25772cefad1a533f5db07621800ecbe11b1f2e65f.png

Route the collection system#

cables=6 allows six turbines per feeder. EWRouter is the fast Esau-Williams heuristic. The MILP router in the OptiWindNet documentation can find shorter networks.

network = WindFarmNetwork(
    cables=6,
    turbinesC=farm.positions,
    substationsC=substations,
    borderC=border,
    obstacleC_=obstacles,
)
network.optimize(router=EWRouter())

print(f"total cable length: {network.length() / 1000:.2f} km")
print(network.solution_info())
total cable length: 43.79 km
{'router': 'EWRouter', 'capacity': 6, 'method': 'biased_EW', 'iterations': 24}

Cables route around the exclusion zone.

network.plot()
../_images/0d6734b5e135eb4b5c7793d3f56a1d5b0e0f9efe009e82632e2026f60625dc6b.svg