A Python framework for building synthetic power distribution feeder models from open-source geospatial data. NREL-shift fetches building parcels and road networks from OpenStreetMap, constructs graph-based network topologies, and exports simulator-ready models through Grid Data Models and Ditto.
- Automated Feeder Generation — Build distribution feeder models directly from OpenStreetMap data
- Graph-Based Network Modeling — Represent distribution networks as NetworkX graphs with typed nodes and edges
- Equipment Mapping — Assign transformers, loads, and other equipment to network components
- Phase Balancing — Automatically balance phases across distribution transformers
- Voltage Mapping — Assign voltage levels throughout the distribution network based on transformer topology
- Visualization — Built-in Plotly-based plotting for parcels, networks, and phase/voltage overlays
- Simulator Export — Write models to OpenDSS, CYME, Synergi, and other simulators via Grid Data Models
pip install nrel-shiftgit clone https://github.com/NREL-Distribution-Suites/shift.git
cd shift
pip install -e .# Development (testing + linting)
pip install -e ".[dev]"
# Documentation
pip install -e ".[doc]"
# MCP server (AI agent integration)
pip install -e ".[mcp]"
# Everything
pip install -e ".[dev,doc,mcp]"The typical workflow has four stages: fetch data → build graph → configure mappers → build system.
from shift import parcels_from_location, get_road_network, GeoLocation
from gdm.quantities import Distance
# Fetch building parcels from OpenStreetMap
parcels = parcels_from_location("Fort Worth, TX", Distance(500, "m"))
# Fetch the road network for the same area
road_network = get_road_network("Fort Worth, TX", Distance(500, "m"))You can also pass coordinates instead of an address:
location = GeoLocation(longitude=-97.3, latitude=32.75)
parcels = parcels_from_location(location, Distance(500, "m"))from shift import get_kmeans_clusters, PRSG, GeoLocation
# Cluster parcels for transformer placement (~2 customers per transformer)
parcel_points = [
p.geometry[0] if isinstance(p.geometry, list) else p.geometry
for p in parcels
]
clusters = get_kmeans_clusters(max(len(parcels) // 2, 1), parcel_points)
# Build the distribution graph from clusters and road network
builder = PRSG(
groups=clusters,
source_location=GeoLocation(-97.3, 32.75),
)
graph = builder.get_distribution_graph()from shift import (
BalancedPhaseMapper,
TransformerVoltageMapper,
EdgeEquipmentMapper,
DistributionSystemBuilder,
TransformerPhaseMapperModel,
TransformerVoltageModel,
TransformerTypes,
)
from gdm import DistributionTransformer
from gdm.quantities import ApparentPower, Voltage
# Phase mapper — assign phases to transformer secondaries
transformer_phase_models = [
TransformerPhaseMapperModel(
tr_name=edge.name,
tr_type=TransformerTypes.SPLIT_PHASE,
tr_capacity=ApparentPower(25, "kilovoltampere"),
location=graph.get_node(from_node).location,
)
for from_node, _, edge in graph.get_edges()
if edge.edge_type is DistributionTransformer
]
phase_mapper = BalancedPhaseMapper(graph, mapper=transformer_phase_models, method="agglomerative")
# Voltage mapper — assign primary/secondary voltages
voltage_mapper = TransformerVoltageMapper(
graph,
xfmr_voltage=[
TransformerVoltageModel(
name=edge.name,
voltages=[Voltage(7.2, "kilovolt"), Voltage(120, "volt")],
)
for _, _, edge in graph.get_edges()
if edge.edge_type is DistributionTransformer
],
)
# Build the system
from pathlib import Path
from gdm import DistributionSystem
import shift
MODELS_FOLDER = Path(shift.__file__).parent.parent.parent / "tests" / "models"
catalog_sys = DistributionSystem.from_json(MODELS_FOLDER / "p1rhs7_1247.json")
system = DistributionSystemBuilder(
name="fort_worth_feeder",
dist_graph=graph,
phase_mapper=phase_mapper,
voltage_mapper=voltage_mapper,
equipment_mapper=EdgeEquipmentMapper(graph, catalog_sys, voltage_mapper, phase_mapper),
)
distribution_system = system.get_system()See the Complete Example for a full end-to-end walkthrough.
These guides walk through individual stages of the workflow:
| Guide | Description |
|---|---|
| Complete Example | End-to-end workflow from data fetching to system export |
| Fetching Parcels | Loading building parcels from CSV, addresses, or GeoDataFrames |
| Building a Graph | Constructing distribution graphs from clustered parcels |
| Updating Branch Types | Changing edge types for specific equipment models |
| Mapping Phases | Assigning phases with balanced or custom mappers |
| Mapping Voltages | Assigning voltage levels via transformer topology |
| Mapping Equipment | Mapping loads, sources, and catalog equipment |
| Building a System | Assembling the final distribution system model |
NREL-shift includes an MCP server for use with LLM-based agents. See the MCP Server Guide for setup and usage.
- API Reference — Quick lookup for all classes and functions
- Contributing — Development workflow and code style guidelines
- Quick Start for Contributors — Fast-track development setup
pip install -e ".[dev]"
pytest # Run all tests
pytest --cov=shift --cov-report=html # With coverage report
pytest tests/test_graph.py # Single test file
pytest -m "not slow" # Skip slow tests- Python >= 3.10
- OSMnx — OpenStreetMap data access
- NetworkX — Graph operations
- Grid Data Models — Power system component models
- See pyproject.toml for the complete dependency list
BSD-3-Clause — see LICENSE.txt.
- Kapil Duwadi (Kapil.Duwadi@nrel.gov)
- Aadil Latif (Aadil.Latif@nrel.gov)
- Erik Pohl (Erik.Pohl@nrel.gov)
@software{nrel_shift,
title = {NREL-shift: Framework for Developing Synthetic Distribution Feeder Models},
author = {Duwadi, Kapil and Latif, Aadil and Pohl, Erik},
year = {2026},
url = {https://github.com/NREL-Distribution-Suites/shift}
}- Open an issue for bugs and feature requests
- Discussions for questions