Modifiers#
Non-mutating transformations for boundaries, strings, and layouts.
ExclusionZones subtracts a separate named area from a boundary, while
Setback takes a margin off every edge the boundary already has, the outer ring
and the rim of any hole alike. The clearance a setback delivers is a minimum,
met and exceeded slightly rather than ever falling short.
Abstract transform applied to a boundary geometry. |
|
Transform ordered logical strings without mutating the source guide. |
|
Transform a completed layout after its base algorithm has run. |
|
|
Subtract exclusion zones, such as protected areas or cables, from a boundary. |
|
Hold turbines a fixed distance inside the boundary. |
|
Remove random internal sections from logical strings. |
|
Add smooth correlated lateral variation while fixing endpoints. |
|
Remove turbines randomly or in correlated grid rows. |
|
Add boundary-constrained Gaussian turbine-position noise. |
|
Relax an existing layout inside its boundary. |
Boundary partitions#
Partition is a namespace rather than a modifier itself. Its concrete
partition modifiers retain their cells, which a Sampler given
n_farms turns into separate farms. Voronoi clips one cell around each
supplied or sampled site. PowerDiagram adjusts weighted-distance cells to
requested relative areas. Channels recursively cuts the boundary using a
string guide.
- class oowl.modifiers.Partition.Voronoi(n_farms, *, sites=None, gap=0.0, seed=None)[source]#
Partition a boundary into Voronoi cells clipped to it.
Each cell holds the points nearer its site than any other.
- Parameters:
n_farms (int) – Exact number of partition cells.
sites (Sequence[Sequence[float]] | np.ndarray | None) – Optional finite
(x, y)site per farm, fixed in the plane, so a resized boundary must still contain them. Sites are sampled uniformly inside the boundary when omitted.gap (float) – Distance in meters kept between cells, half taken from each.
seed (int | np.random.Generator | None) – Random seed used when sampling sites.
- partition(geometry)[source]#
Return Voronoi cells clipped to
geometry, one per site.Sites in a
MultiPolygonparent are allocated across components by area, at least one each. With more components than farms the smallest joins its nearest until each farm has one. Holes are preserved. Each cell loses the points withingap / 2of another. Sampled sites are redrawn a few times when the gap removes a whole cell, and given sites or a last draw that still loses one raiseValueError. Sampled sites replay fromseedand the cell order is deterministic. Sites and the draws taken are recorded indetails.- Parameters:
geometry (BaseGeometry)
- Return type:
tuple[Polygon | MultiPolygon, …]
- class oowl.modifiers.Partition.PowerDiagram(n_farms, *, area_weights=None, sites=None, gap=0.0, seed=None)[source]#
Partition one boundary into cells with controlled relative areas.
Cell
icontains points where its weighted squared distance\[d_i(x) = \lVert x-p_i\rVert^2-w_i\]is no larger than for any other site, the power diagram of Aurenhammer (1987, doi:10.1137/0216006). Additive weights
w_iare fitted so cell areas followarea_weightsbefore any gap is removed.- Parameters:
n_farms (int) – Exact number of partition cells.
area_weights (Sequence[float] | None) – Positive relative target areas, one per farm. Equal by default.
sites (Sequence[Sequence[float]] | np.ndarray | None) – Optional finite
(x, y)site per farm. Sites are sampled inside the boundary when omitted.gap (float) – Distance in meters kept between cells, half taken from each.
seed (int | np.random.Generator | None) – Random seed used when sampling sites.
- partition(geometry)[source]#
Return power cells of
geometry, one per site.Holes are honored. A parent in several disconnected polygons, as a setback or exclusion zone can leave, is fitted as one region, so the areas stay exact and a cell may take parts of several polygons. Unseeded sites are redrawn a few times before a failed area fit propagates. Each cell loses the points within
gap / 2of another, and a gap that consumes a whole cell raisesValueError. Cell order is deterministic.detailsrecords the sites, target, achieved and retained area fractions, and the fitted weights indetails["power_weights_m2"].- Parameters:
geometry (BaseGeometry)
- Return type:
tuple[Polygon | MultiPolygon, …]
- class oowl.modifiers.Partition.Channels(n_farms, *, guide=None, gap=0.0, seed=None)[source]#
Partition a boundary by recursive cuts along a string guide.
Each cut halves the number of farms left in a cell. Its orientation is drawn at random and its offset gives that share of the area exactly. The parts of a cell on one side of the cut form one new cell, so a cut across a bay of the boundary leaves a cell in several parts. For a guide that is a graph over its chord the cut moves continuously with the cell. The cuts are chosen before any gap is removed, so a resized boundary is cut alike and the area left changes smoothly with its size. Each cell then loses a band
gapwide along every cut that split one of its parents.- Parameters:
n_farms (int) – Exact number of partition cells.
guide (StringGuide | None) –
StringGuidewith one continuous string and distinct endpoints that does not cross itself, stretched across the cell to shape each cut.Straight()by default. A random guide given no seed is redrawn fromseed, so every reseeded partition bends its channels anew. A seeded guide keeps its shape.gap (float) – Width in meters of the channel each cut removes.
seed (int | np.random.Generator | None) – Random seed for the cut orientations and an unseeded guide.
- Raises:
ValueError – If
guideis not one continuous string with distinct endpoints, if it crosses itself, if no orientation gives a cut with two valid sides, or if the gap is too wide for a cell.
- partition(geometry)[source]#
Return the cells cut from
geometryby recursive channel splits.Cells in a
MultiPolygonparent are allocated across components by area, at least one each. With more components than farms the smallest joins its nearest until each farm has one. Holes survive the cuts. Cells and their order replay fromseed. The guide and the channel lines are recorded indetails.- Parameters:
geometry (BaseGeometry)
- Return type:
tuple[Polygon | MultiPolygon, …]
Modifiers of boundaries, string guides and generated layouts.
Layout modifiers change a finished farm and attach to a layout with
modify(). Boundary and string modifiers
are re-exported so every modifier is available from one module.
- class oowl.modifiers.Breaks(n_breaks=1, *, size=0.05, seed=None)[source]#
Remove random internal sections from logical strings.
Adds
n_breaksgaps, eachsizetimes the original arc length of its string, withsizein(0, 1). Withoutseed, aStringlayout draws new breaks for every farm.
- class oowl.modifiers.ExclusionZones(holes)[source]#
Subtract exclusion zones, such as protected areas or cables, from a boundary.
Collect the exclusion
holesto subtract, in meter coordinates.Each hole is a Shapely
PolygonorMultiPolygonor aBoundary, whose geometry is read once here. A random boundary hole without a seed is redrawn for each farm a sampler draws. Holes are fixed in the plane, so resizing the boundary moves its edge over them rather than scaling them. Buffer a line, such as a cable route, to its corridor width first. RaisesTypeErrorfor another kind of hole andValueErrorfor an invalid polygon.- Parameters:
holes (Sequence[shapely.Polygon | MultiPolygon | Boundary])
- class oowl.modifiers.LayoutModifier[source]#
Transform a completed layout after its base algorithm has run.
- class oowl.modifiers.Meander(roughness=0.03, *, seed=None)[source]#
Add smooth correlated lateral variation while fixing endpoints.
roughnessis the peak lateral offset as a fraction of segment arc length, and0leaves the guide unchanged. Withoutseed, aStringlayout draws a new meander for every farm.
- class oowl.modifiers.PositionNoise(relative_standard_deviation, *, minimum_spacing=None, seed=None, max_attempts=100)[source]#
Add boundary-constrained Gaussian turbine-position noise.
relative_standard_deviationis a fraction of the layout’s median nearest-neighbor spacing, not a distance. When the layout carries aStringGuide, a turbine on it moves only along it, so it stays on the string. Turbines off the guide, such as those aMixturetakes from another source, move freely.minimum_spacingin meters rejects moves that come closer to another turbine, and each turbine getsmax_attemptsdraws.- Parameters:
- apply(layout, rng, seed)[source]#
Return a new farm with Gaussian noise added to every position.
The noise has
relative_standard_deviationtimes the median nearest-neighbor spacing. Turbines move in order, each trying up tomax_attemptsdraws against the current positions of the others. A turbine with no boundary- and spacing-feasible candidate stays put. A turbine never leaves the piece of the boundary it starts in. RaisesValueErrorwhen the input already violatesminimum_spacing.
- class oowl.modifiers.Relaxation(*, method='repulsion', length_scale=None, max_iterations=100, step=0.1, tolerance=0.001, max_displacement=None, minimum_spacing=None, seed=None)[source]#
Relax an existing layout inside its boundary.
method="repulsion"uses the all-pairs potential\[E_{ij}=\frac{h^2}{d_{ij}^2+\epsilon^2}.\]Every pair pushes apart, so the layout expands into the available geometry.
method="gaussian"uses the shorter-range potential\[E_{ij}=\exp(-d_{ij}^2/h^2),\]plus a penalty for pairs closer than
length_scaleand the centroid energy\[E_c=\frac{\|\bar p-c_B\|^2}{2h^2},\]where
c_Bis the boundary centroid. Steps follow the per-turbine normalized negative gradient and accept only energy decreases. A step that would leave the boundary slides to the nearest point of its edge, unless that crosses a hole or a gap between parts, where it stops at the crossing instead. Turbine count and ordering are unchanged. The repulsion energy visits every turbine pair in cache-sized blocks, so its time and memory grow quadratically with the turbine count. The Gaussian energy only visits pairs within four length scales, as farther pairs hold less thanexp(-16)of it, and gradients are only computed for accepted steps.- Parameters:
method (Literal['repulsion', 'gaussian']) –
"repulsion"for inverse-square spreading,"gaussian"for local pair repulsion.length_scale (float | None) – The length
hof the potentials and the compact-penalty threshold, in meters. Defaults to the square root of boundary area per turbine.max_iterations (int) – Maximum number of energy-descent steps.
step (float) – Maximum movement per iteration as a fraction of
length_scale.tolerance (float) – Stop once the energy falls by at most this fraction over five accepted steps.
max_displacement (float | None) – Optional maximum total movement from each initial position, in meters.
Noneleaves displacement unconstrained.minimum_spacing (float | None) – Optional distance in meters no step brings a pair closer than. The moves of a pair that would come closer are halved until it does not. A layout filled at a minimum spacing holds its relaxation to it.
seed (int | np.random.Generator | None) – Seed used only to separate exactly coincident positions.
- apply(layout, rng, seed)[source]#
Return a new farm with positions relaxed inside the boundary.
rngonly separates exactly coincident positions. Records the energies, largest displacement and convergence inmetadata["modifiers"]. The string guide is dropped when any turbine moves. RaisesValueErrorwithout a boundary, when a position starts outside it, or when a pair starts closer thanminimum_spacing.
- class oowl.modifiers.Setback(distance)[source]#
Hold turbines a fixed distance inside the boundary.
The distance is in meters and stays so when the boundary is resized.
Set the standoff
distancefrom every boundary edge, in meters.The margin applies to the outer ring and to the rim of any hole.
- Parameters:
distance (float)
- apply(geometry)[source]#
Return
geometryeroded by at leastdistanceon every edge.The result may be a multipolygon. Raises
ValueErrorwhen the setback consumes the whole site. The buffer radius is divided by the half-angle cosine so GEOS chords lie outside the true arc. The margin is then met and exceeded by at most about 0.12 percent.- Parameters:
geometry (BaseGeometry)
- Return type:
BaseGeometry
- class oowl.modifiers.StringModifier[source]#
Transform ordered logical strings without mutating the source guide.
- class oowl.modifiers.TurbineRemoval(n_remove=None, *, relative_n_remove=None, method='random', transition_penalty=None, row_axis=0, seed=None)[source]#
Remove turbines randomly or in correlated grid rows.
n_removeremoves that many turbines, and a layout givenn_turbinesplaces them on top, so exactlyn_turbinesremain.relative_n_removein[0, 1)removes that share of the turbines the layout places, rounded down, so at least one always remains however small a sampled farm turns out. A layout givenn_turbinesthen places the fewest turbines that leave exactly that many. Give one of the two.method="random"removes a uniform random subset.method="markov"needs aGridlayout and removes runs of neighbors along its rows, which follow the first grid vector forrow_axis=0and the second forrow_axis=1. A largertransition_penaltygives longer runs.- Parameters:
- apply(layout, rng, seed)[source]#
Return a new farm with the turbines this removes dropped.
Removal is uniform for
method="random"and row-correlated formethod="markov". Records the seed, method and turbine counts inmetadata["modifiers"]and updatesmetadata["achieved_n"]. RaisesValueErrorifn_removewould leave no turbine.