Skip to main content

Python library for reading, editing, validating, and comparing PSS/E power system models (RAW v33/34/35 and RAWX formats).

Project description

psse_model_util

Python library for reading, editing, validating, and comparing PSS/E power system models (RAW v33/34/35 and RAWX formats).

Status: Active development — proprietary, internal use only.


Overview

psse_model_util parses PSS/E RAW and RAWX files into structured Python objects backed by pandas DataFrames and a NetworkX graph. Its primary use case is comparing seasonal Bulk Electric System (BES) model variants (e.g. summer vs. winter).

Key capabilities:

  • Load RAW (v33/34/35) or RAWX (JSON) files into a Model object
  • Filter networks by area, voltage level, or arbitrary query
  • Compare two models: DataFrame-level deltas + graph topology diffs (added/removed edges, path sectionalizations, bypasses)
  • Export to CSV or pickle for downstream analysis
  • Build a NetworkX graph for topological analysis (shortest paths, connectivity, etc.)

Installation

Prerequisites

  • Python 3.11+ (tested through 3.14.4)
  • PDM for dependency management
  • Hatch for builds and versioning

Install from PyPI

# Latest stable release
pip install psse-model-util

# Latest pre-release (beta / rc) — pip skips pre-releases without --pre
pip install --pre psse-model-util

Install (editable / dev)

# Clone or copy the project directory
cd psse_model_util

# Install with PDM
pdm install

# Or install directly with pip (editable)
pip install -e .

Install dev extras

pdm install -G dev
# Includes: pytest, pytest-cov, ruff

Quick Start

Load a model

from psse_model_util.model import Model

# From a RAW file (v33/34/35)
model = Model("path/to/model.raw", name="Summer_Peak")

# From a RAWX file
model = Model("path/to/model.rawx", name="Summer_Peak")

# Access network data
buses      = model.network.bus          # pd.DataFrame
lines      = model.network.acline       # pd.DataFrame
generators = model.network.generator    # pd.DataFrame

Filter a model

# Filter by area (Model-level — delegates to network)
filtered = model.filter_by_area(areas={101: "AREA1", 102: "AREA2"})

# Filter by voltage level or section query (Network-level)
ehv = model.network.filter_by_kv(low_value=345)
model.network.filter_section("bus", "baskv >= 230", inplace=True)

Build and query the network graph

import networkx as nx

graph = model.network.graph(regenerate=True)

# Shortest path between two buses
path = nx.shortest_path(graph, ("bus", 101), ("bus", 205))

# Access bus properties
props = graph.nodes[("bus", 101)]

Compare two models

from psse_model_util.model import Model
from psse_model_util.compare import ModelComparison

model1 = Model("summer.raw", name="Summer")
model2 = Model("winter.raw", name="Winter")

# Filter to your area of interest first (recommended)
m1 = model1.filter_by_area(areas=[101, 102, 103])
m2 = model2.filter_by_area(areas=[101, 102, 103])

comp = ModelComparison(m1, m2)
comp.compare_network_dfs()   # DataFrame-level column deltas
comp.compare_graph()          # Topology: added/removed edges, path changes
comp.to_csv(df_comparison_to_csv=True, graph_comparison_to_csv=True)

Export and serialise a model

# Export all network sections to CSV
model.to_csv()

# Cache as pickle (fast reload)
model.to_pickle()

# Reload from cache
model2 = Model("path/to/model.raw")  # auto-loads from cache if available

# Serialise to a JSON string or file
json_str = model.to_json()
model.to_json(file_path="model_export.json")

# Load from a rawx dict (avoids the json_data mutation issue in to_json round-trips)
from psse_model_util.raw_to_rawx import raw_file_to_rawx_dict
rawx_dict = raw_file_to_rawx_dict("path/to/model.raw")
model2 = Model(file_path_or_json=rawx_dict)

Note: Model(file_path_or_json=json_str) works for loading but a full to_json()Model(json_str) round-trip is lossy due to a known internal mutation (see Known Issues). Use raw_file_to_rawx_dict as shown above when you need a lossless in-memory copy.


Project Structure

psse_model_util/
├── model.py                   # Model, Network, General, Harmonics, TimeSeries classes
├── raw_to_rawx.py             # RAW file parser → rawx-compatible dict
├── compare.py                 # ModelComparison class
├── version.py                 # Legacy version shim (deprecated — use __about__.py)
├── inch.py                    # WIP: IDEV/INCH export (Phase 3.2)
├── __about__.py               # Version: 2026.4.3 (calver YYYY.M.micro)
├── __init__.py
├── CLAUDE.md                  # Architecture guide for Claude Code
├── common/
│   ├── constants.py           # INCLUDE_AREAS, filter constants, query defaults
│   ├── dataframe_util.py      # convert_df_column_dtypes()
│   ├── dirs.py                # Canonical app dirs (platformdirs)
│   ├── file_util.py           # to_pickle(), read_pickle(), wait_for_file()
│   ├── json_util.py           # load_and_clean_json()
│   └── logging_config.py      # Logger setup
├── dataformat/
│   ├── classes.py             # Data model classes
│   ├── inch_templates.py      # INCH format templates
│   ├── rawx_json_template.py  # RAWX section schema (fields, data_types, id_cols, bus_cols)
│   └── rawx_raw_map.csv       # RAW ↔ RAWX field mapping (v34 + v35 columns)
└── tests/
    ├── conftest.py            # Shared fixtures and sys.path setup
    ├── test_classes.py        # dataformat/classes.py domain types
    ├── test_compare.py        # ModelComparison
    ├── test_dataframe_util.py # common/dataframe_util helpers
    ├── test_dirs.py           # common/dirs platform paths
    ├── test_file_util.py      # common/file_util pickle helpers
    ├── test_model.py          # Model + Network (primary integration tests)
    ├── test_model2.py         # Supplemental Model method tests
    ├── test_raw_to_rawx.py    # RAW parser + substation section
    ├── test_rawx.py           # RAWX-format model loading
    ├── test_phase_*.py        # Feature-phase regression tests
    ├── legacy_tests/          # Pre-refactor scripts (not collected by pytest)
    └── data/                  # Test fixtures (do not delete)
        ├── Model_1.raw        # Synthetic v34 — baseline for ModelComparison tests
        ├── Model_2.raw        # Synthetic v34 — intentionally modified from Model_1
        ├── sample_34.raw      # Minimal v34 RAW file
        ├── sample2_34.raw     # Second minimal v34 RAW file
        ├── sample_v35.raw     # Minimal v35 RAW file
        ├── sample_v35.rawx    # Minimal v35 RAWX file
        ├── sample2_v35.rawx   # Second minimal v35 RAWX file
        ├── transformer.raw    # Transformer-focused test case
        ├── minimal.raw        # Smallest valid RAW file
        └── Model_1 and 2 differences.txt  # Documented delta between Model_1/Model_2

Dev Setup

# Lint
pdm run ruff check .

# Run all tests
pdm run pytest

# Run a single test file
pdm run pytest tests/test_model.py

# Run a specific test by name
pdm run pytest tests/test_model.py -k "test_filter_by_area"

# Test with coverage report
pdm run pytest --cov=psse_model_util --cov-report=term-missing
# Current: 434 tests, 78% coverage (CI gate: 40%)

# Version (hatch-managed calver)
hatch version

Claude Code users: CLAUDE.md at the repo root contains architecture notes, data-flow diagrams, and the rationale behind key design decisions.


Versioning & Releases

Uses CalVer: YYYY.M.micro — managed by Hatch via src/psse_model_util/__about__.py (the single source of truth for the version).

2026.4.5    →  year=2026, month=4, micro=5            (stable)
2026.4.5b1  →  beta 1 of 2026.4.5                     (pre-release)
2026.5.0rc1 →  release candidate 1 of 2026.5.0        (pre-release)

Pre-release suffixes follow PEP 440: bN (beta), rcN (release candidate). pip ignores pre-releases unless you pass --pre, so betas reach only opt-in testers while stable installs stay on full releases.

Release process

Publishing is automated via Trusted Publishing (OIDC — no API tokens) in .github/workflows/publish.yml:

  1. Bump __version__ in src/psse_model_util/__about__.py (use a bN/rcN suffix for a pre-release).
  2. Cut a GitHub Release, or run the Publish to PyPI workflow manually (Actions → Publish to PyPI → Run workflow). A release created by cd.yml uses GITHUB_TOKEN, which cannot trigger publish.yml, so the manual run is the reliable trigger.
  3. The workflow builds, publishes to TestPyPI, then to PyPI. The pypi step is gated by a required-reviewer environment, so the final publish needs a manual approval click.

Known Issues / Roadmap

Issue Phase Status
RAWX export bug — exported .rawx doesn't reload in PSS/E 2.2 Open (low priority)
to_json()Model(json_str) round-trip is lossy (json_data mutation in _create_dataframe) Open
INCH/IDEV export scaffolded but not functional 3.2 Open

License

Proprietary — internal use only.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

psse_model_util-2026.4.5b1.tar.gz (354.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

psse_model_util-2026.4.5b1-py3-none-any.whl (123.2 kB view details)

Uploaded Python 3

File details

Details for the file psse_model_util-2026.4.5b1.tar.gz.

File metadata

  • Download URL: psse_model_util-2026.4.5b1.tar.gz
  • Upload date:
  • Size: 354.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for psse_model_util-2026.4.5b1.tar.gz
Algorithm Hash digest
SHA256 eaa44077bb44faf5b8134bacd38f7ceef3c62cd1a38e66f7a044fabcc2212e97
MD5 85ccc6e20db64c58c54be5429ce33931
BLAKE2b-256 2ff141520c395c5a37033156b4519e9d56c3c72e4280ab57efac6b5e90a9aaed

See more details on using hashes here.

Provenance

The following attestation bundles were made for psse_model_util-2026.4.5b1.tar.gz:

Publisher: publish.yml on ppsyOps/psse_model_util

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file psse_model_util-2026.4.5b1-py3-none-any.whl.

File metadata

File hashes

Hashes for psse_model_util-2026.4.5b1-py3-none-any.whl
Algorithm Hash digest
SHA256 a9b457dc13acf40067b6886adbfa03f3a830bc5cff2a0f5a4d4d10d84d051b94
MD5 7a6af48f3d2a51c0f2d08e0f07489b71
BLAKE2b-256 34b83fc8d08a4472a7af40c36a004e62540348c3fbb0e2de7f40cf0a19a5443a

See more details on using hashes here.

Provenance

The following attestation bundles were made for psse_model_util-2026.4.5b1-py3-none-any.whl:

Publisher: publish.yml on ppsyOps/psse_model_util

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page