Secondary Network Strategies#
New in v0.7.0
Pluggable secondary network strategies allow you to control how loads are connected to their serving transformer.
Overview#
The secondary (low-voltage) network connects individual customer loads to their distribution transformer. Different topologies suit different deployment contexts — dense urban areas may follow road paths, while rural residential areas typically use simple radial connections.
Each strategy implements the SecondaryNetworkStrategy interface and
can be passed to PRSG via the secondary_strategy parameter.
Available Strategies#
MeshSteinerStrategy (default)#
Rectangular mesh grid + Steiner tree reduction. The original SHIFT algorithm.
Algorithm: Build 2D grid in group bounding box, find Steiner tree to connect load points
Topology: Grid-aligned tree (right-angle connections)
Parameters:
spacing(default 50m)Trade-off: Works well for uniform areas; produces right-angle artifacts
RadialStrategy#
Direct star connection from transformer to each load.
Algorithm: Create center node at transformer location, connect each load directly
Topology: Pure radial star (1 hop from transformer to each load)
Trade-off: Simplest and most common real-world residential lateral topology; no intermediate nodes
Reference: Ali et al. [APM+23]
DelaunayStrategy#
Delaunay triangulation of load points with MST pruning.
Algorithm: (1) Triangulate all points (loads + center), (2) Weight edges by geodesic distance, (3) Extract MST
Topology: Organic, non-grid tree following natural point clusters
Trade-off: More natural-looking than mesh; requires ≥3 non-collinear points (falls back to radial otherwise)
OpenStreetSecondaryStrategy#
Road-aware secondary routing using local OpenStreetMap roads.
Algorithm: Fetch local road network for group area, route using a configurable
RoutingStrategy(default:WeightedSteinerTreeStrategy)Parameters:
routing_strategy,buffer(default 50m)Topology: Follows actual street layout
Trade-off: Most realistic for urban areas; requires network fetch per group (slower)
HubLineStrategy#
k-nearest-neighbor consumer-to-transformer assignment.
Algorithm: Sort loads by distance to transformer center (hub), connect each directly
Topology: Star topology with loads ordered by proximity
Trade-off: Similar output to
RadialStrategybut preserves distance-ordering information; mirrors utility hub-line assignment practice
Reference: Ali et al. [APM+23]
Usage Examples#
Radial secondary (simplest)#
from shift import PRSG, RadialStrategy, GeoLocation
builder = PRSG(
groups=clusters,
source_location=GeoLocation(-97.3, 32.75),
secondary_strategy=RadialStrategy(),
)
graph = builder.get_distribution_graph()
Road-aware secondary#
from shift import PRSG, OpenStreetSecondaryStrategy, GeoLocation
builder = PRSG(
groups=clusters,
source_location=GeoLocation(-97.3, 32.75),
secondary_strategy=OpenStreetSecondaryStrategy(),
)
graph = builder.get_distribution_graph()
Combining strategies#
from shift import (
PRSG,
WeightedSteinerTreeStrategy,
HubLineStrategy,
GeoLocation,
)
builder = PRSG(
groups=clusters,
source_location=GeoLocation(-97.3, 32.75),
routing_strategy=WeightedSteinerTreeStrategy(), # primary
secondary_strategy=HubLineStrategy(), # secondary
)
graph = builder.get_distribution_graph()
API Reference#
- class shift.SecondaryNetworkStrategy#
Abstract base class for secondary network construction strategies.
A secondary network strategy determines how load points within a group (cluster) are connected to their serving transformer location.
- abstract build(group: GroupModel) Graph#
Build the secondary network for a group of loads.
- Parameters:
group (GroupModel) – Group containing load points and a center (transformer location).
- Returns:
A connected graph with node attributes ‘x’ and ‘y’ representing the secondary network topology.
- Return type:
nx.Graph
- class shift.MeshSteinerStrategy(spacing: ~infrasys.quantities.Distance = <Quantity(50, 'meter')>)#
Bases:
SecondaryNetworkStrategyRectangular mesh grid + Steiner tree (current default behavior).
Builds a 2D rectangular grid within the bounding box of the group’s points, then computes a Steiner tree connecting the nearest grid nodes to each load point.
- Parameters:
spacing (Distance, optional) – Grid spacing. Defaults to 50 meters.
- build(group: GroupModel) Graph#
Build the secondary network for a group of loads.
- Parameters:
group (GroupModel) – Group containing load points and a center (transformer location).
- Returns:
A connected graph with node attributes ‘x’ and ‘y’ representing the secondary network topology.
- Return type:
nx.Graph
- class shift.RadialStrategy#
Bases:
SecondaryNetworkStrategyDirect radial (star) connection from transformer to each load.
Connects each load point directly to the group center (transformer location) with a straight-line edge. This is the most common topology for residential distribution laterals and eliminates the jagged mesh artifacts.
References
All papers confirm radial = most common distribution topology
- build(group: GroupModel) Graph#
Build the secondary network for a group of loads.
- Parameters:
group (GroupModel) – Group containing load points and a center (transformer location).
- Returns:
A connected graph with node attributes ‘x’ and ‘y’ representing the secondary network topology.
- Return type:
nx.Graph
- class shift.DelaunayStrategy#
Bases:
SecondaryNetworkStrategyDelaunay triangulation of load points + MST pruning.
Builds a Delaunay triangulation of the group points (including the center), then prunes it to a minimum spanning tree using geodesic distance as edge weights. Produces organic, non-grid layouts.
- build(group: GroupModel) Graph#
Build the secondary network for a group of loads.
- Parameters:
group (GroupModel) – Group containing load points and a center (transformer location).
- Returns:
A connected graph with node attributes ‘x’ and ‘y’ representing the secondary network topology.
- Return type:
nx.Graph
- class shift.OpenStreetSecondaryStrategy(routing_strategy: ~shift.graph.routing.RoutingStrategy | None = None, buffer: ~infrasys.quantities.Distance = <Quantity(50, 'meter')>)#
Bases:
SecondaryNetworkStrategyRoad-aware secondary network using local OpenStreetMap roads.
Fetches the local road network for the group’s bounding area and routes the secondary network along actual roads using a configurable routing strategy (defaults to weighted Steiner tree).
- Parameters:
routing_strategy (RoutingStrategy, optional) – Strategy for routing within the fetched road network. Defaults to WeightedSteinerTreeStrategy (geodesic distance).
buffer (Distance, optional) – Buffer around group bounding box for road fetching. Defaults to 50 meters.
References
Ali et al. 2023: “power lines follow road paths”
Caetano et al. 2026: “distribution networks are usually routed along the streets”
- build(group: GroupModel) Graph#
Build the secondary network for a group of loads.
- Parameters:
group (GroupModel) – Group containing load points and a center (transformer location).
- Returns:
A connected graph with node attributes ‘x’ and ‘y’ representing the secondary network topology.
- Return type:
nx.Graph
- class shift.HubLineStrategy#
Bases:
SecondaryNetworkStrategyk-nearest-neighbor consumer-to-transformer assignment.
Based on the hub-line algorithm from Ali et al. 2023. Connects each consumer to the transformer (hub) via the shortest road-network path, or directly if no road network is provided.
The hub-line algorithm ranks consumers by spatial distance to the transformer and connects the nearest consumers to each hub.
This produces a star topology per transformer where consumers are assigned based on proximity.
References
Ali et al. 2023: Hub-line algorithm for determining number of consumers connected to a single transformer (k-nearest neighbor)
- build(group: GroupModel) Graph#
Build the secondary network for a group of loads.
- Parameters:
group (GroupModel) – Group containing load points and a center (transformer location).
- Returns:
A connected graph with node attributes ‘x’ and ‘y’ representing the secondary network topology.
- Return type:
nx.Graph