A Python + Flask prototype for modeling distribution feeder topology, tracing fault isolation paths, and generating switching plans using BFS. Built by a power system operator exploring how far deterministic graph algorithms can go for fault isolation and restoration planning without a full Distribution Management System (DMS).
This is a deliberate, working prototype — not a finished product. It's published because the domain is underrepresented on GitHub and the code reflects real operational thinking about how faults are isolated and load is restored.
All five feeders energized from their respective substation buses, cross-feeder ties normally open.
A fault is inserted on a feeder segment. The system traces isolation paths and determines candidate switching operations, including cross-feeder backfeed options.
BFS analysis determines energized regions, frontier edges, and possible switching plans to restore service.
- Five-feeder ring model — Hunterwood substation modeled as five feeders (HB11–HB15) connected in a ring via normally-open cross-feeder tie switches. Each feeder has a main trunk, one or more lateral branches, and multiple tie points to adjacent feeders.
- Unified graph — all feeders and cross-feeder ties are merged into a single NetworkX graph. BFS operates on the full topology; feeder namespace prefixes (
HB11.,HB12., etc.) keep nodes unambiguous. - Topologically correct tie modeling — tie nodes are real shared nodes in the graph, not magic infinite-bus endpoints. Closing a tie switch causes BFS to traverse the actual path through the neighboring feeder to reach its source bus. This correctly models backfeed path length and isolation device placement.
- Source BFS — multi-source BFS floods simultaneously from all five substation buses, respecting open and faulted edge states, and tracks which source is feeding each node.
- Regions BFS — identifies contiguous energized segments and assigns each to a source region for UI coloring.
- Fault isolation — given a faulted edge, walks the path back to the primary source and finds the nearest upstream isolation device(s).
- Alternate feed planning — dynamically discovers candidate alternate sources by scanning the frontier of open edges adjacent to the faulted zone. Traces paths from each alternate source to both endpoints of the faulted edge. Cross-feeder tie paths are naturally included without hardcoding.
- Switching plan generation — ranks and proposes a switching sequence to isolate the fault and restore as many nodes as possible via backfeed, reporting node pickup counts per candidate action.
- Interactive SVG UI — browser-based single-line diagram showing all five feeders in horizontal rows with cross-feeder ties drawn as dashed connectors between rows. Left-click toggles switches open/closed; right-click inserts or clears faults; "Run BFS" re-analyzes and repaints energization state and region coloring.
HB11 Bus ── A ── B ── C ── D ── E ── F ── G ── Tie1 ╌╌ (HB15.Tie11)
└── E1 ── E2 ── E3 ── E4 ── E5 ── Tie2 ╌╌ (HB12.D)
HB12 Bus ── A ── B ── C ── D ── E ── F ── Tie3 ╌╌ (HB13.E)
└── C1 ── C2 ── C3 ── Tie4 ╌╌ (HB13.D3)
HB13 Bus ── A ── B ── C ── D ── E ── F ── G ── H ── Tie5 ╌╌ (HB14.D)
└── D1 ── D2 ── D3 ── D4 ── Tie6 ╌╌ (HB14.B2)
└── G1 ── G2 ── Tie7 ╌╌ (HB14.C)
HB14 Bus ── A ── B ── C ── D ── E ── F ── Tie8 ╌╌ (HB15.F)
└── B1 ── B2 ── B3 ── Tie9 ╌╌ (HB15.D1)
└── D1 ── D2 ── Tie10 ╌╌ (HB15.C1)
HB15 Bus ── A ── B ── C ── D ── E ── F ── G ── Tie11 ╌╌ (HB11.Tie1)
└── C1 ── C2 ── Tie12 ╌╌ (HB14.Tie10)
└── D1 ╌╌ (HB14.Tie9)
└── E1 ── E2 ── E3 ── Tie13 ╌╌ (HB14.Tie9)
╌╌ normally-open tie switch
The restoration engine models the feeder system as a graph where:
- Nodes represent buses, taps, and connection points
- Edges represent electrical devices or line segments
- Edge state determines whether current can flow (
closed,open,faulted)
The analysis pipeline works in several stages:
A multi-source BFS begins simultaneously at all five substation buses and traverses closed edges to determine which portions of the system are energized, tracking which source feeds each node.
Energized nodes are grouped into contiguous regions associated with the source feeding them. Regions are rendered as distinct colors in the UI.
When a faulted edge is inserted, BFS seeds from all normal source buses and traces the path from the faulted edge back toward the primary source to identify the nearest upstream isolation device. The multi-source seed ensures correct behavior regardless of which feeder the fault is on.
Candidate alternate sources are discovered dynamically by scanning open edges adjacent to the faulted zone — no alternate sources are hardcoded. BFS then traces paths from each candidate to both endpoints of the faulted edge, crossing at most one open tie switch. Cross-feeder paths are handled naturally because tie nodes are real nodes in the unified graph.
Candidate switching plans are simulated against a copy of the current state and ranked by nodes restored. The output is a step-by-step switching sequence in the style of a utility switching order.
Because the topology analysis is deterministic, switching recommendations are derived entirely from graph structure and device metadata — not heuristics or machine learning.
- Python 3 / Flask
- NetworkX (graph construction)
- Vanilla JS + SVG (frontend — no framework)
- Matplotlib (used for layout math, not rendered in the UI)
pip install -r requirements.txt
python app.pyThen open http://127.0.0.1:8000 in a browser.
app.py Flask app, /analyze endpoint, orchestration
feeder_graph.py Five-feeder graph definition (nodes, edges, components,
cross-ties, SVG layout positions, initial state)
component_dictionary.py Component type metadata (isolation capability, protective, etc.)
source_bfs.py Multi-source energization trace from all substation buses
regions_bfs.py Contiguous energized region identification
find_path_bfs.py Multi-source path trace from faulted edge back to primary source
plan_path_bfs.py Dynamic alternate source discovery and backfeed path planning
rank_restore.py Switching plan generation and node pickup ranking
svg.py SVG single-line diagram builder (five-feeder layout)
static/index.html Browser UI
Tie nodes as real graph nodes. An earlier single-feeder design treated tie switch endpoints as terminal nodes with no path beyond them. The five-feeder model replaces this: a tie switch connects a node on one feeder to a real node inside another feeder's graph. Closing the tie causes BFS to traverse the actual path through the neighboring feeder to reach its source. This means isolation device placement and backfeed path length are computed correctly from topology, not estimated.
Dynamic alternate source discovery. plan_path_bfs.py no longer hardcodes tie switch endpoints as alternate sources. Instead it scans the open-edge frontier of the faulted zone and treats any energized node reachable across exactly one open edge as a candidate. This generalizes correctly as tie switch states change during a switching sequence.
Multi-source BFS seeding. find_path_bfs.py seeds BFS simultaneously from all five substation buses. This ensures the path-to-source trace works correctly for faults on any feeder without per-feeder logic.
Feeder namespace prefixing. All nodes are named HB1X.NodeName. This keeps the unified graph unambiguous and makes it easy to filter or group nodes by feeder in analysis and display code.
This is a prototype with a hardcoded feeder model. Before it could be used for anything real, it would need:
- General feeder loader — read from CIM, GeoJSON, or a database instead of hardcoded Python dicts
- Load modeling — no ampacity or thermal ratings; the switching planner picks up nodes but doesn't check whether the backfeed path can carry the load
- Persistent state —
CURRENT_STATElives in memory and resets on server restart - Substation modeling — no bus work, transformer, or high-side switching
- Radial enforcement — no check to prevent inadvertent paralleling during restoration
- Telemetry integration — all state is manually toggled; no SCADA feed
The project started with a specific goal: use an LLM as the interface between a grid operator and the feeder model.
The idea was that an operator could describe a situation in plain language — "there's a fault on the E lateral, I need to isolate it and pick up as much load as possible from the Tie2 side" — and the LLM would translate that into structured calls against the deterministic BFS engine, then format the results as a step-by-step switching procedure in the style of an actual utility switching order:
1. OPEN HB11.E|HB11.E1 (recloser) — isolate fault
2. VERIFY HB11.E1 dead
3. CLOSE HB11.E5|HB11.Tie2 (disconnect) — restore via Tie2
4. VERIFY load picked up on E1–E5 section
The BFS layer exists specifically to keep the LLM grounded. Topology, energization state, and isolation logic are computed deterministically — the LLM handles language and procedure formatting, not graph reasoning. This prevents the model from hallucinating switching steps that would be physically wrong or operationally dangerous.
That architecture — deterministic engine as the source of truth, LLM as the natural language intermediary — is the direction the project is heading.
Active development paused. The five-feeder ring topology and unified BFS engine are working. The natural next steps would be ampacity checking on backfeed paths, loading a real feeder from an external source, and wiring in the LLM switching order interface described above.
Pull requests and issues welcome, especially from anyone with domain experience.
MIT



