Framework for developing synthetic distribution feeder model.
Project description
NREL-shift
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.
Features
- 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
Installation
From PyPI
pip install nrel-shift
From Source
git clone https://github.com/NREL-Distribution-Suites/shift.git
cd shift
pip install -e .
Optional Extras
# 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]"
Quick Start
The typical workflow has four stages: fetch data → build graph → configure mappers → build system.
1. Fetch Parcels and Road Network
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"))
2. Build a Distribution Graph
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()
3. Configure Mappers and Build the System
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.
Documentation
User Guides
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 |
MCP Server (AI Agent Integration)
NREL-shift includes an MCP server for use with LLM-based agents. See the MCP Server Guide for setup and usage.
Developer Resources
- API Reference — Quick lookup for all classes and functions
- Contributing — Development workflow and code style guidelines
- Quick Start for Contributors — Fast-track development setup
Running Tests
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
Requirements
- 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
License
BSD-3-Clause — see LICENSE.txt.
Authors
- Kapil Duwadi (Kapil.Duwadi@nrel.gov)
- Aadil Latif (Aadil.Latif@nrel.gov)
- Erik Pohl (Erik.Pohl@nrel.gov)
Citation
@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}
}
Support
- Open an issue for bugs and feature requests
- Discussions for questions
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nrel_shift-0.6.2.tar.gz.
File metadata
- Download URL: nrel_shift-0.6.2.tar.gz
- Upload date:
- Size: 1.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cde239891c14168300ca70587dd16deea8c2654cfa2dafdd5cb475a6d990302f
|
|
| MD5 |
1c61f56f40d0dd54ac973ca8d45a10e9
|
|
| BLAKE2b-256 |
397a36602503cd391d3b39cc0310140c5a038e29512e8969bd58e9ef92a9cee3
|
Provenance
The following attestation bundles were made for nrel_shift-0.6.2.tar.gz:
Publisher:
publish_to_pypi.yaml on NREL-Distribution-Suites/shift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nrel_shift-0.6.2.tar.gz -
Subject digest:
cde239891c14168300ca70587dd16deea8c2654cfa2dafdd5cb475a6d990302f - Sigstore transparency entry: 951985052
- Sigstore integration time:
-
Permalink:
NREL-Distribution-Suites/shift@959dd66b083a58d4ccb6a50283669dd2a36fcaee -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/NREL-Distribution-Suites
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yaml@959dd66b083a58d4ccb6a50283669dd2a36fcaee -
Trigger Event:
release
-
Statement type:
File details
Details for the file nrel_shift-0.6.2-py3-none-any.whl.
File metadata
- Download URL: nrel_shift-0.6.2-py3-none-any.whl
- Upload date:
- Size: 72.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dfaa2a5c07e18fdef64158872763fe6e8e9708257fbc4379d98c595fe825c1bd
|
|
| MD5 |
9595fdb3d3897e70f5f9fd5f7f37d24b
|
|
| BLAKE2b-256 |
910cafcf3f8a510b4e1ed4c0a09b9eea2f7dc4442db9da109b8a68e674a19720
|
Provenance
The following attestation bundles were made for nrel_shift-0.6.2-py3-none-any.whl:
Publisher:
publish_to_pypi.yaml on NREL-Distribution-Suites/shift
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
nrel_shift-0.6.2-py3-none-any.whl -
Subject digest:
dfaa2a5c07e18fdef64158872763fe6e8e9708257fbc4379d98c595fe825c1bd - Sigstore transparency entry: 951985064
- Sigstore integration time:
-
Permalink:
NREL-Distribution-Suites/shift@959dd66b083a58d4ccb6a50283669dd2a36fcaee -
Branch / Tag:
refs/tags/v0.6.2 - Owner: https://github.com/NREL-Distribution-Suites
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish_to_pypi.yaml@959dd66b083a58d4ccb6a50283669dd2a36fcaee -
Trigger Event:
release
-
Statement type: