A production-grade Python toolkit for computing and analyzing network sensitivity factors in electricity markets and transmission planning.
Built by a power systems & market engineer with hands-on experience across ERCOT, MISO, PJM, SPP, and the Australian NEM.
Network sensitivity factors are at the heart of transmission planning, security-constrained economic dispatch (SCED), and locational marginal pricing (LMP). Every time a market operator clears energy β whether it's AEMO's NEM dispatch engine or PJM's real-time market β these linear approximations of power flow govern how generation shift propagates through the network.
This project computes, validates, and visualizes three foundational sensitivity matrices:
| Factor | Full Name | Core Question It Answers |
|---|---|---|
| PTDF | Power Transfer Distribution Factor | "If I shift 1 MW from bus A to bus B, what fraction flows on line k?" |
| LODF | Line Outage Distribution Factor | "If line k trips, what fraction of its pre-outage flow diverts onto line m?" |
| GSF / ISF | Generation Shift Factor / Injection Shift Factor | "If generator at bus i increases output by 1 MW (slack absorbs), what is the change in flow on line k?" |
Together, these factors power:
- Contingency Analysis (N-1, N-2) β rapid screening without full AC power flow
- Congestion Rent & FTR/CRR Valuation β financial transmission rights settlement
- Interface Limit Monitoring β enforcing flowgate constraints in real-time dispatch
- Integrated System Planning β REZ capacity expansion & transmission augmentation studies (e.g., AEMO ISP)
- Production Cost Modeling β PLEXOS and PROMOD constraint formulations
A power network is represented by its bus admittance matrix
Where:
-
$B_{bus} \in \mathbb{R}^{n \times n}$ β susceptance matrix (nodal) -
$B_f \in \mathbb{R}^{l \times n}$ β branch susceptance matrix -
$\theta \in \mathbb{R}^{n}$ β voltage angle vector -
$P_{inject} \in \mathbb{R}^{n}$ β net bus injections (generation β load)
The PTDF matrix
Where
represents the fraction of a 1 MW transaction from bus
Key properties:
-
$\sum_k \text{PTDF}_{k, i \to j} \neq 1$ (flows split across parallel paths) - PTDF is topology-dependent β it changes with network switching
- Used in zonal PTDFs for market clearing (e.g., AEMO's regional reference nodes)
When line
Computed analytically from the PTDF matrix:
Where
Key properties:
-
$\text{LODF}_{k,k} = -1$ (outaged line carries zero flow post-outage) - Enables N-1 contingency screening in
$O(l^2)$ vs. full power flow per contingency - Foundation of post-contingency flow calculation:
$F_m^{post} = F_m^{pre} + \text{LODF}_{m,k} \cdot F_k^{pre}$
The GSF for generator at bus
This is simply the
Relationship between factors:
Applications:
- SCED constraint formulation β each generator's impact on monitored flowgates
-
Congestion component of LMP β
$\lambda_k^{congestion} = \sum_m \mu_m \cdot \text{GSF}_{m,k}$ where$\mu_m$ is the shadow price on constraint$m$ - Redispatch analysis β identifying which generators relieve congestion most efficiently
power-sensitivity-factors/
β
βββ data/
β βββ raw/ # Raw network data (MATPOWER, PSS/E, CSV)
β βββ processed/ # Cleaned bus/branch/gen tables
β βββ test_cases/ # IEEE 14-bus, 30-bus, 118-bus test cases
β
βββ src/
β βββ network/
β β βββ __init__.py
β β βββ admittance.py # Y_bus, B_bus construction
β β βββ dc_powerflow.py # DC power flow solver
β β βββ topology.py # Network graph utilities
β β
β βββ sensitivity/
β β βββ __init__.py
β β βββ ptdf.py # PTDF computation engine
β β βββ lodf.py # LODF computation (full & sparse)
β β βββ gsf.py # GSF / ISF computation
β β βββ contingency.py # N-1 / N-2 screening using LODF
β β
β βββ market/
β β βββ lmp_decomposition.py # Energy + congestion + loss components
β β βββ ftr_valuation.py # Financial transmission rights
β β βββ flowgate_monitor.py # Real-time interface monitoring
β β
β βββ visualization/
β βββ network_plot.py # Networkx / plotly network diagrams
β βββ heatmaps.py # PTDF/LODF matrix heatmaps
β βββ congestion_map.py # Geospatial congestion overlays
β
βββ notebooks/
β βββ 01_theory_and_derivation.ipynb # Mathematical walkthrough
β βββ 02_ieee14_ptdf_demo.ipynb # PTDF on IEEE 14-bus
β βββ 03_n1_contingency_screening.ipynb # LODF-based contingency analysis
β βββ 04_lmp_decomposition.ipynb # Congestion pricing example
β βββ 05_nem_interface_monitoring.ipynb # Australian NEM flowgate case study
β
βββ tests/
β βββ test_ptdf.py
β βββ test_lodf.py
β βββ test_gsf.py
β βββ validate_against_powerworld.py # Cross-validation with commercial tools
β
βββ docs/
β βββ theory.md # Extended mathematical documentation
β βββ market_applications.md # How factors are used in market operations
β βββ aemo_case_study.md # NEM-specific application notes
β
βββ requirements.txt
βββ environment.yml
βββ README.md
git clone https://github.com/shankar/<repo-name>.git
cd power-sensitivity-factors
# Using pip
pip install -r requirements.txt
# Or using conda (recommended for power systems stack)
conda env create -f environment.yml
conda activate power-sensitivitynumpy>=1.24
scipy>=1.10 # sparse matrix operations (critical for large networks)
pandas>=2.0
networkx>=3.0 # network topology
pandapower>=2.13 # power system modeling & validation
matplotlib>=3.7
plotly>=5.15 # interactive network visualization
pypower # MATPOWER-compatible DC/AC power flowimport numpy as np
from src.network.admittance import build_Bbus
from src.sensitivity.ptdf import compute_ptdf
# Load IEEE 14-bus test case
from pypower.case14 import case14
ppc = case14()
# Build susceptance matrices
Bf, Bbus = build_Bbus(ppc)
# Compute full PTDF matrix [branches x buses]
ptdf = compute_ptdf(Bf, Bbus, ref_bus=0)
print(f"PTDF shape: {ptdf.shape}") # (20, 14) for IEEE 14-bus
print(f"PTDF[line_1, bus_2βbus_5]: {ptdf[0, 1] - ptdf[0, 4]:.4f}")from src.sensitivity.lodf import compute_lodf
# LODF matrix [branches x branches]
lodf = compute_lodf(ptdf, ppc['branch'])
# Post-contingency flow on line m when line k trips
line_k = 5 # outaged line index
pre_flow = np.array([...]) # pre-contingency flows in MW
post_flow = pre_flow + lodf[:, line_k] * pre_flow[line_k]
print(f"Post-contingency flow on line 3: {post_flow[2]:.2f} MW")from src.sensitivity.contingency import n1_screening
results = n1_screening(
base_flows=pre_flow,
lodf=lodf,
ratings=ppc['branch'][:, 5], # thermal ratings in MVA
threshold=0.95 # flag at 95% of rating
)
violations = results[results['overload_pct'] > 100]
print(violations[['outaged_line', 'overloaded_line', 'overload_pct']])The PTDF matrix visually reveals electrical coupling between transactions and branches. High-magnitude cells indicate strong sensitivity β a 1 MW shift on that bus pair significantly loads that branch.
π See
notebooks/02_ieee14_ptdf_demo.ipynbfor full visualization
| Network Size | Full AC Power Flow (per contingency) | LODF Screening |
|---|---|---|
| 14-bus (20 lines) | ~400 power flows | 1 matrix multiply |
| 118-bus (186 lines) | ~34,716 power flows | 1 matrix multiply |
| 2000-bus (3,000 lines) | ~9M power flows | Sparse matrix ops |
LODF-based screening provides exact N-1 results under DC assumptions at a fraction of the computational cost β which is precisely why real-time market engines (ERCOT SCED, AEMO's dispatch engine) rely on them.
All sensitivity matrices are cross-validated against:
- PYPOWER β open-source MATPOWER port
- pandapower β for DC power flow cross-checks
- Analytical hand-calculations β on 3-bus and 5-bus toy networks
AEMO monitors interconnector flows (e.g., Heywood, QNI, Basslink) using sensitivity-based constraint formulations. The Market Constraint Equations in NEMDE (NEM Dispatch Engine) are linear constraint rows of the form:
Ξ£ (GSF_k,i Γ P_i) β€ RHS_k for each monitored element k
This project includes a case study replicating NEM-style interface constraint formulation using publicly available AEMO network data.
π See
notebooks/05_nem_interface_monitoring.ipynb
The congestion component of an LMP at bus
Where
Financial Transmission Rights settle on the difference in LMPs weighted by the FTR MW quantity. Under the PTDF framework:
The project includes utilities for simulating FTR auction value under different congestion scenarios.
- The
$B_{bus}$ matrix inversion uses sparse LU factorization (scipy.sparse.linalg.splu) for large networks β critical for real-world systems with thousands of buses - Reference bus removal must be handled consistently β an off-by-one error here produces silently wrong PTDF values
- For near-radial networks, some LODF denominators approach zero (radial line has PTDF β 1) β these are handled with a configurable threshold
For networks > 500 buses, dense matrix operations become prohibitive. The lodf.py module offers both:
compute_lodf_dense()β for small networks and testingcompute_lodf_sparse()β scipy sparse, suitable for real ISO-scale networks
This implementation follows the from-bus injection positive convention consistent with MATPOWER and pandapower. Some commercial tools (PowerWorld, PSS/E) may use different sign conventions β always verify before cross-validation.
- AC sensitivity factors (Q-V sensitivity, voltage sensitivity matrix)
- Multi-period PTDF for time-varying topology (switching, maintenance outages)
- LODF extension to N-2 contingencies (cascading failure screening)
- Integration with NEMOSIS / AEMO data API for real NEM network data
- PLEXOS-compatible constraint export (LP file format)
- Interactive Plotly dashboard for congestion visualization
- Stott, B., Jardim, J., AlsaΓ§, O. (2009). DC Power Flow Revisited. IEEE Transactions on Power Systems.
- Wood, A.J., Wollenberg, B.F., SheblΓ©, G.B. β Power Generation, Operation and Control (3rd Ed.)
- Ott, A. (2010). Experience with PJM Market Operation, System Design, and Implementation β IEEE Trans. Power Systems
- AEMO β Integrated System Plan 2024, Constraint Formulation Guidelines
- Kirschen, D., Strbac, G. β Fundamentals of Power System Economics
- Christie, R.D., Wollenberg, B.F., Wangensteen, I. (2000). Transmission Management in the Deregulated Environment
MIT License β see LICENSE for details.