This tutorial turns the reference-design gallery into executable examples. Each design is built with the public substation API, and its interactive Cartesian schematic is embedded directly below the explanation.
Canonical layouts include two incoming high-voltage circuits, protected transformer bays, medium-voltage bus sensing and surge protection, and equipped feeder exits. Coordinates are deterministic, so the schematics can be inspected without facility GIS data. Canonical layouts use topology-specific feeder defaults; outfeed_count can be supplied when building a design. The detailed distribution station is a fixed 69/12.47 kV example.
1. Load and Inspect the Reference Designs Index¶
Read the source page and inspect its section headings, schematic links, and the corresponding local files. The source is retained as input for the parsing and validation steps below.
2. Extract Reference Design Data¶
Parse each gallery heading and its following section to collect the design name, explanatory paragraph, schematic path, and build_reference_design key. This keeps the rendered examples tied to the source index rather than maintaining a second hand-written list.
3. Generate and Embed Substation Plots¶
Build each design from its parsed API key and display the returned Plotly figure immediately after its description. The figures are stored as notebook outputs, so readers see the schematics in the tutorial without opening separate HTML files.
Single Bus¶
A compact arrangement with all circuits connected to one common bus.
Sectionalized Single Bus¶
Two bus sections are separated by a normally open tie breaker, allowing the sections to be operated independently or coupled when needed.
Main and Transfer¶
A main bus and transfer path provide a way to bypass a circuit breaker during maintenance while retaining a connection to the station.
Double Bus, Single Breaker¶
Each circuit has one breaker and can be selected onto either of two buses.
Ring Bus¶
The breakers form a ring, with circuits connected between adjacent breakers so that a single breaker can be isolated without interrupting every circuit.
Breaker-and-a-Half¶
Two circuits share a middle breaker between two buses, providing redundancy while using fewer breakers than a double-breaker arrangement.
Double Breaker, Double Bus¶
Each circuit is connected to both buses through its own pair of breakers, allowing either bus or breaker to be isolated while maintaining service.
HV/MV Single Bus¶
A high-voltage bus supplies a medium-voltage bus through a power transformer; the feeder exits connect to the medium-voltage side.
Detailed Distribution Station¶
A fully populated 69/12.47 kV station example with two incoming circuits, two transformers, four feeders, detailed protection and metering, and station automation.
Customize a Template for a Different Station¶
For a canonical layout, build_reference_design accepts a station name, an outfeed_count, and an optional coordinate_provider. Its incoming-circuit and transformer counts are fixed by the chosen layout. This single-bus example keeps the canonical two inputs and one transformer while adding five outfeeds:
A bay groups the equipment for one circuit. When the input or transformer count must also change, use SubstationBuilder and add each circuit, transformer, and feeder explicitly. The example below builds two 69 kV inputs, three transformers in parallel between the 69 kV and 12.47 kV buses, and five named feeder outfeeds. It creates one bay for each input, transformer, and feeder.
FeederBoundary.feeder_id is matched to DistributionFeeder.name; distribution_model_reference_id identifies the associated DistributionSystem.name. The distribution model stays separate, and its feeder records can be populated with downstream buses, branches, and loads.
from gdm.systems.substation import (
ExternalCircuit,
FeederBoundary,
PowerTransformer,
build_reference_design,
)
station = build_reference_design(
"single_bus",
name="metro-template",
outfeed_count=5,
)
print({
"inputs": sum(1 for _ in station.get_components(ExternalCircuit)),
"transformers": sum(1 for _ in station.get_components(PowerTransformer)),
"outfeeds": sum(1 for _ in station.get_components(FeederBoundary)),
}){'inputs': 2, 'transformers': 1, 'outfeeds': 5}\nfrom infrasys import Location
from gdm.quantities import Voltage
from gdm.systems.distribution import DistributionSystem
from gdm.systems.distribution.components import (
DistributionFeeder,
DistributionSubstation,
)
from gdm.systems.substation import (
CircuitDirection,
ExternalCircuit,
FeederBoundary,
PowerTransformer,
SubstationBuilder,
)
from IPython.display import display
infeed_count = 2
transformer_count = 3
feeder_ids = ["north", "east", "campus", "industrial", "rural"]
distribution_model_id = "metro-distribution"
builder = SubstationBuilder(
"metro-station",
initial_voltage_level_id="mv",
initial_nominal_voltage=Voltage(12.47, "kilovolt"),
)
builder.add_voltage_level("hv", Voltage(69, "kilovolt"))
hv_bus = builder.add_bus("hv-bus", "hv")
mv_bus = builder.add_bus("mv-bus", "mv")
for number in range(1, infeed_count + 1):
circuit_id = f"incoming-{number}"
bay_id = f"{circuit_id}-bay"
line_node = f"{circuit_id}-line-node"
builder.add_external_circuit(
circuit_id, bay_id, line_node, CircuitDirection.INCOMING, "hv"
)
builder.add_breaker(
f"{circuit_id}-breaker",
bay_id,
line_node,
hv_bus.name,
voltage_level_id="hv",
)
for number in range(1, transformer_count + 1):
transformer_id = f"transformer-{number}"
bay_id = f"{transformer_id}-bay"
hv_node = builder.add_node(f"{transformer_id}-hv-node", "hv")
mv_node = builder.add_node(f"{transformer_id}-mv-node", "mv")
builder.add_breaker(
f"{transformer_id}-hv-breaker",
bay_id,
hv_bus.name,
hv_node.name,
voltage_level_id="hv",
)
builder.add_two_winding_transformer(
transformer_id, bay_id, hv_node, mv_node
)
builder.add_breaker(
f"{transformer_id}-mv-breaker",
bay_id,
mv_node.name,
mv_bus.name,
voltage_level_id="mv",
)
for feeder_id in feeder_ids:
bay_id = f"{feeder_id}-bay"
feeder_node = f"{feeder_id}-feeder-node"
_, breaker_to = builder.add_breaker(
f"{feeder_id}-breaker", bay_id, mv_bus.name, feeder_node
)
builder.add_feeder(
feeder_id,
bay_id,
feeder_node,
bus=breaker_to,
distribution_model_reference_id=distribution_model_id,
)
station = builder.build()
distribution = DistributionSystem(
name=distribution_model_id,
auto_add_composed_components=True,
)
distribution_feeders = [
DistributionFeeder(name=feeder_id) for feeder_id in feeder_ids
]
distribution_substation = DistributionSubstation(
name="metro-grid",
feeders=distribution_feeders,
)
distribution.add_component(distribution_substation)
boundaries = {
boundary.feeder_id: boundary
for boundary in station.get_components(FeederBoundary)
}
feeder_to_boundary = {
feeder.name: boundaries[feeder.name]
for feeder in distribution_feeders
}
counts = {
"inputs": sum(1 for _ in station.get_components(ExternalCircuit)),
"transformers": sum(1 for _ in station.get_components(PowerTransformer)),
"outfeeds": len(boundaries),
"mapped feeders": len(feeder_to_boundary),
}
assert all(
boundary.distribution_model_reference_id == distribution.name
for boundary in feeder_to_boundary.values()
)
print(counts)
print("Mapped feeder IDs:")
print("\n".join(sorted(feeder_to_boundary)))
def centered_x(index, count, spacing):
return (index - (count - 1) / 2) * spacing
positions = {"hv-bus": (0, 22), "mv-bus": (0, 0)}
for index in range(infeed_count):
positions[f"incoming-{index + 1}-line-node"] = (
centered_x(index, infeed_count, 14),
32,
)
for index in range(transformer_count):
x_position = centered_x(index, transformer_count, 12)
positions[f"transformer-{index + 1}-hv-node"] = (x_position, 15)
positions[f"transformer-{index + 1}-mv-node"] = (x_position, 7)
for index, feeder_id in enumerate(feeder_ids):
positions[f"{feeder_id}-feeder-node"] = (
centered_x(index, len(feeder_ids), 8),
-10,
)
for bus in builder.nodes.values():
x_position, y_position = positions[bus.name]
bus.coordinate = Location(x=x_position, y=y_position, crs="EPSG:3857")
if not station.has_component(bus.coordinate):
station.add_component(bus.coordinate)
display(station.plot_schematic(show=False)){'inputs': 2, 'transformers': 3, 'outfeeds': 5, 'mapped feeders': 5}
Mapped feeder IDs:
campus
east
industrial
north
rural
4. Create and Validate the Jupyter Notebook¶
The notebook is saved at docs/reference_designs/index.ipynb. Validate its JSON structure, language metadata, and embedded Plotly outputs so the gallery remains executable and the figures travel with the notebook.
5. Move the Page to Tutorials and Update Links¶
The MyST table of contents places this notebook under Tutorials. The substation guide links to the notebook instead of the previous Markdown gallery, while the source Markdown remains available to make the parsing steps reproducible.