Reading Data
ESA++ uses an indexable interface to read grid data using Python’s bracket notation. The idea is simple: pass a component type (like Bus or Gen) and optionally a field name, and you get back a pandas DataFrame. Primary-key columns are always included automatically so you can join or filter results without extra bookkeeping.
from esapp import PowerWorld
from esapp.components import *
pw = PowerWorld("path/to/case.pwb")
Importing * from esapp.components brings in all component types (Bus, Gen, Load, Branch, Shunt, Area, Zone, etc.) so they’re available directly in your code.
Read Patterns
There are four ways to read data, ranging from just the keys to every available field:
Syntax |
Returns |
|---|---|
|
Key columns only (e.g. |
|
Keys + one field |
|
Keys + multiple fields |
|
Keys + every defined field |
Let’s walk through each one.
Passing just the component type returns its primary-key columns. Buses have a single key (BusNum); generators have a compound key (BusNum, GenID).
In [2]:
pw[Bus].head()
Out[2]:
| BusNum | |
|---|---|
| 0 | 1 |
| 1 | 2 |
| 2 | 3 |
| 3 | 4 |
| 4 | 5 |
In [3]:
pw[Gen].head()
Out[3]:
| BusNum | GenID | |
|---|---|---|
| 0 | 2 | 1 |
| 1 | 2 | 2 |
| 2 | 2 | 3 |
| 3 | 2 | 4 |
| 4 | 23 | 1 |
Adding a field name retrieves that column alongside the keys. You can pass a single string or a list of strings for multiple columns. Field names can also be specified as component enum attributes (e.g. Bus.BusPUVolt) for IDE autocomplete and typo protection.
In [4]:
pw[Bus, "BusPUVolt"].head()
Out[4]:
| BusNum | BusPUVolt | |
|---|---|---|
| 0 | 1 | 0.993545 |
| 1 | 2 | 0.991225 |
| 2 | 3 | 0.984548 |
| 3 | 4 | 0.978800 |
| 4 | 5 | 0.988985 |
In [5]:
pw[Gen, ["GenMW", "GenMVR", "GenStatus"]].head()
Out[5]:
| BusNum | GenID | GenMVR | GenMW | GenStatus | |
|---|---|---|---|---|---|
| 0 | 2 | 1 | 0.80000 | 2.500000 | Closed |
| 1 | 2 | 2 | 0.80000 | 2.500000 | Closed |
| 2 | 2 | 3 | 0.80000 | 2.500000 | Closed |
| 3 | 2 | 4 | 0.80000 | 2.500000 | Closed |
| 4 | 23 | 1 | 0.04408 | 69.274741 | Closed |
In [6]:
# Same query using enum attributes instead of strings:
pw[Bus, [Bus.BusName, Bus.BusPUVolt, Bus.BusAngle]].head()
Out[6]:
| BusAngle | BusName | BusNum | BusPUVolt | |
|---|---|---|---|---|
| 0 | -1.119907 | ALOHA138 | 1 | 0.993545 |
| 1 | -3.927372 | ALOHA69 | 2 | 0.991225 |
| 2 | -4.731145 | FLOWER69 | 3 | 0.984548 |
| 3 | -5.745870 | WAVE69 | 4 | 0.978800 |
| 4 | -2.069792 | HONOLULU138 | 5 | 0.988985 |
The slice syntax pw[Bus, :] retrieves every defined field at once. This is handy for exploration, though it can produce wide DataFrames.
In [7]:
pw[Bus, :].shape
Out[7]:
(37, 581)
Working with Results
Every result is a standard pandas DataFrame, so all the usual filtering, grouping, and aggregation operations work directly. There’s no need to convert or reshape anything before using pandas tools you already know.
In [8]:
# Filter generators by status
gens = pw[Gen, ["GenMW", "GenMVR", "GenStatus"]]
online = gens[gens["GenStatus"] == "Closed"]
In [9]:
# Aggregate load totals
loads = pw[Load, ["LoadMW", "LoadMVR"]]
loads[["LoadMW", "LoadMVR"]].sum()
Out[9]:
LoadMW 1136.290004
LoadMVR 0.000000
dtype: float64