optiwindnet.loads¶
Cable-load computation over solution topologies and routesets.
Module Contents¶
- optiwindnet.loads.quantize_for_capacity(powers_set: collections.abc.Iterable[float | fractions.Fraction], capacity_nominal: float | fractions.Fraction, power_rtol: float = DEFAULT_POWER_RTOL) tuple[dict[fractions.Fraction, int], fractions.Fraction, int][source]¶
Quantize turbine power while preserving cable-packing feasibility.
The capacity constrains
power_per_inflowbeyond accuracy: among the mappings that keep every power withinpower_rtol, this takes the first in order from coarse to fine that preserves packing. It selects the least-denominator rational nearest the least-squares fit within that mapping’s feasible interval. A cable then admits the same sets of turbines in inflow as in nominal power, and the routed network is the one the nominal powers define.The exact
power_per_inflowpreserves packing because every achievable load is a whole multiple of it. It guarantees a candidate when its inflows are within the solver limit and the integer capacity is positive.Only the distinct powers matter, not how many turbines share each. Results are memoized.
- Parameters:
powers_set – distinct turbine powers, in nominal units.
capacity_nominal – cable capacity, in the same nominal units.
power_rtol – most per-turbine deviation accepted from a larger
power_per_inflow.
- Returns:
Inflow of each distinct power, nominal power per inflow, and the capacity in whole inflow.
- Raises:
ValueError – the inputs are not positive finite numbers,
power_rtolis not in[0, 1), or nopower_per_inflowpreserves the packing at an inflow a MILP can carry.
- optiwindnet.loads.quantized(A: networkx.Graph, *, capacity: int | None = None, capacity_nominal: float | fractions.Fraction | None = None, power_rtol: float = DEFAULT_POWER_RTOL) tuple[networkx.Graph, int, dict[str, Any]][source]¶
Validate the power of
Aand quantize it for a solve.Exactly one of
capacityandcapacity_nominalis given.capacityis in inflow and requiresAto declare no unequal power.capacity_nominalis in the unit of the powerAdeclares; unequal powers are quantized for it (seequantize_for_capacity()) on a copy ofAwhose terminals get'inflow'.- Parameters:
A – location or available-links graph.
capacity – cable capacity in inflow.
capacity_nominal – cable capacity in nominal power.
power_rtol – quantization tolerance, used with
capacity_nominal.
- Returns:
The graph to solve over, the capacity in inflow, and the graph attributes that record the quantization on the solution.
- Raises:
ValueError – both or neither capacity is given, the capacity does not suit the power
Adeclares, or the quantization fails.
- optiwindnet.loads.total_inflow(G: networkx.Graph) int[source]¶
Return total terminal power injection in inflow integer units.
- optiwindnet.loads.nonunit_inflow(G: networkx.Graph) dict[int, int][source]¶
Return terminal injections differing from one inflow, keyed by node.
- optiwindnet.loads.validate_terminal_power(G: networkx.Graph) None[source]¶
Validate terminal power, normalizing declared values to fractions.
'power_per_inflow'is a positive rational and terminal'inflow'a positive integer, one by default. With'powers_set'(at least two distinct powers), every terminal has a'power'listed there. A graph with'powers_set'but no'power_per_inflow'declares unequal power, and its terminals have no'inflow'. Otherwise, a terminal’s'power'must quantize to its inflow (their ratio to'power_per_inflow'rounds half up to it), and the graph attribute'power_quantization_inexact'is refreshed.- Raises:
ValueError – any of these conventions is violated.
- optiwindnet.loads.set_terminal_power(L: networkx.Graph, powers: collections.abc.Sequence[float | fractions.Fraction] | numpy.ndarray, power_unit: str | None = None) None[source]¶
Set the nominal power of each terminal, replacing any power attributes.
Equal powers become the graph attribute
'power_per_inflow'; unequal ones become terminal'power'and the graph attribute'powers_set'.- Parameters:
L – location or available-links graph, changed in place.
powers – nominal power of each terminal; floats are rationalized.
power_unit – physical unit of the powers, e.g.
'MW'.
- Raises:
ValueError –
powersare notTpositive finite numbers.
- optiwindnet.loads.bfs_subtree_loads(G, parent, children, subtree, visited=None)[source]¶
Descend the subtree, updating edge and node attributes.
Meant to be called by
calcload(), but can be used independently (e.g. from PathFinder). Nodes must not have a'load'attribute.- Parameters:
G – graph to traverse.
parent – node the traversal descends from.
children – nodes of
Gto descend into.subtree – subtree id to assign to every node visited.
visited – nodes already claimed by this traversal; pass one set across several calls to keep them from claiming a node twice. A fresh set is used when omitted.
- Returns:
Load of
parentafter accumulating its descendants’ loads, including its own inflow if it is a terminal.- Raises:
ValueError – a node is reached twice, so the traversal is not descending a tree –
Gholds a cycle, or two roots reach the same node.
- optiwindnet.loads.split_rings_and_calc_loads(S: networkx.Graph, A: networkx.Graph) None[source]¶
Close path-form ring arms into canonical rings and compute their loads.
Only the ringed builders (HGS, LKH and the
method='ringed'constructor) call this, on a solutionSthat is still a set of simpleroot → … → rootpaths missing their zero-load links. Each path is walked and closed into a canonical ring (see_add_ring_to_S()), usingAto pick the longer zero-load link on odd-length rings; a tail already touching a root bridges two roots(r1, r2). Every ring receives exactly one zero-load link (load=0, no current flows through it), and each node’s subtree id and load, the edges’ loads, and the graph’smax_load/has_loads/ root loads are set.All ringed solvers must call this before returning a solution, so that every ringed
Scarries exactly oneload=0link per ring.
- optiwindnet.loads.calcload(G: networkx.Graph, *, nominal: bool = False) None[source]¶
Calculate link loads and update edge and node attributes of
G.Gmust already be in final form (a forest, or a ring-form graph whoseload=0zero-load links are present). A breadth-first traversal of each root’s subtree propagates the loads, treatingload=0links (ring zero-load links) as breaks. Each node’s subtree id and outgoing load land on its'subtree'/'load'attributes. Edge loads, root loads, and the graph’s'max_load'and'has_loads'attributes are set.With
nominal, only'load_nominal'is updated on nodes and edges; integer loads, subtree ids, edge directions, and graph metadata are retained. Ring breaks are always identified by'load' == 0.The traversal must reach all
Tterminals, regardless of their contribution. Ring construction — closing path-form arms into rings — lives insplit_rings_and_calc_loads(), which the ringed builders call instead.- Parameters:
G – Forest or ring-form graph to update.
nominal – Accumulate declared terminal
'power'into'load_nominal'instead of integer inflow into'load'. Terminals without a declaration contribute their inflow times'power_per_inflow'. Only worth the traversal when quantization is inexact: exact quantization makes nominal loads'load'times'power_per_inflow'.