Skip to main content

Tests • Pytest • Documentation • Upload to PyPi • PyPI Downloads • License • Issues • CodeFactor • Codecov • MCP Server • MCP Tools • PyPI Downloads

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
  • Interactive UI Studio — Browser-based workflow for parcel fetch, clustering, strategy selection, graph build, and side-by-side comparisons
  • 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/NLR-Distribution-Suite/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]"

# UI API + browser studio
pip install -e ".[ui]"

# Everything
pip install -e ".[dev,doc,mcp,ui]"

UI Studio

NREL-shift includes a FastAPI-backed UI that exposes core modeling workflows through a browser interface.

pip install -e ".[ui]"
shift-ui-server

Open http://127.0.0.1:8000 to access the UI. The first vertical slice includes:

  • map-assisted source selection,
  • parcel fetch and clustering,
  • routing/secondary strategy selection,
  • graph build and strategy comparison,
  • mapper/system build endpoints ready for UI wiring.

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
UI Studio Running and using the browser-based UI/API workflow

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

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

License

BSD-3-Clause — see LICENSE.txt.

Authors

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/NLR-Distribution-Suite/shift}
}

Support

Metadata

Release files for nrel-shift 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for nrel-shift 0.8.0
File Size Uploaded
nrel_shift-0.8.0.tar.gz 2.5 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for nrel-shift 0.8.0
File Interpreter ABI Platform
nrel_shift-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.6 MB

Release files / nrel_shift-0.8.0.tar.gz

Download URL nrel_shift-0.8.0.tar.gz
Size 2.5 MB
Tags Source
SHA-256 checksum
How to use checksums
5d97f0720e2f965119da872004ccb0c6445b1ef172e6f166ffc4649b1d686567
BLAKE2b-256 checksum
How to use checksums
a4436092453f118e83c66b2acb98225e4f1d96a51a0cd7c0e1538b2672071d7d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 30, 2026.

Transparency log

Release files / nrel_shift-0.8.0-py3-none-any.whl

Download URL nrel_shift-0.8.0-py3-none-any.whl
Size 151.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d5e0e98fb3e2314866a87e53acaab0fb1158905cd1175b65dbc292a2e5e60086
BLAKE2b-256 checksum
How to use checksums
e49ff5596539cccb1b884a425f94dec1e2775ee98afc64d2dc052b12e26c58e8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page