Utilities & Analysis

The esapp.utils package provides analysis modules, visualization tools, and general helpers.

Contingency Builder

Fluent API for defining transient stability contingencies.

class ContingencyBuilder(name, runtime=10.0)[source]

Fluent builder for Transient Stability (TS) contingencies.

Constructs a timeline of events to be simulated using method chaining.

Parameters:
  • name (str) – Unique name for the contingency.

  • runtime (float, optional) – Simulation duration in seconds (default: 10.0).

name

The contingency name.

Type:

str

runtime

Simulation end time in seconds.

Type:

float

Example

>>> builder = ContingencyBuilder("GenTrip", runtime=5.0)
>>> builder.at(1.0).fault_bus("101").at(1.1).clear_fault("101")
add_event(obj_type, who, action)[source]

Add a generic event at the current time cursor.

Parameters:
  • obj_type (str) – PowerWorld object type (e.g., “Bus”, “Gen”, “Branch”).

  • who (str) – Object identifier string.

  • action (Union[str, SimAction]) – Action to perform (e.g., SimAction.OPEN or “OPEN”).

Returns:

ContingencyBuilder – Self for method chaining.

at(t)[source]

Set the current time cursor for subsequent events.

Parameters:

t (float) – Time in seconds (must be non-negative).

Returns:

ContingencyBuilder – Self for method chaining.

Raises:

ValueError – If time is negative.

clear_fault(bus)[source]

Clear the fault at a bus at the current time.

fault_bus(bus)[source]

Apply a 3-phase solid fault to a bus at the current time.

to_dataframes()[source]

Generates DataFrames matching the ESA GObject schemas.

Returns:

Tuple[DataFrame, DataFrame]: (Contingency Definition, Element Definitions)

trip_branch(f_bus, t_bus, ckt='1')[source]

Trip (open) a branch at the current time.

trip_gen(bus, gid='1')[source]

Trip (open) a generator at the current time.

class SimAction(value)[source]

Enumeration of standard simulation actions to prevent magic string errors.

CLEAR_FAULT = 'CLEARFAULT'
CLOSE = 'CLOSE'
FAULT_3PB = 'FAULT 3PB SOLID'
OPEN = 'OPEN'

GIC Analysis

Geomagnetically Induced Current (GIC) analysis tools.

class GIC(pw=None)[source]

Bases: object

GIC analysis application for PowerWorld integration.

Provides methods for GIC calculations, sensitivity analysis, and model generation using PowerWorld case data. All data access is delegated to the parent PowerWorld instance.

This class is accessed via PowerWorld.gic.

GIC Options

GIC analysis requires certain options to be enabled for full functionality. The most important is pf_include = True which must be set before retrieving GIC data like transformer coil resistances (GICCoilR fields). Methods like model() and gmatrix() automatically enable this option. Use configure() to set multiple options at once.

Example

>>> pw = PowerWorld("case.pwb")
>>> pw.gic.configure()  # Enable GIC with default options
>>> pw.gic.storm(100, 90)  # 100 V/km, 90 degrees
>>> pw.gic.model()
>>> G = pw.gic.gmatrix()

See also

configure

Set GIC options with sensible defaults.

settings

View or modify all GIC settings.

cleargic()[source]

Clear all GIC calculation results from the case.

configure(pf_include=True, ts_include=False, calc_mode=GICCalcMode.SNAPSHOT)[source]

Configure GIC options with sensible defaults.

This is the recommended way to initialize GIC analysis. It ensures all necessary options are set for typical GIC workflows.

Parameters:
  • pf_include (bool, default True) – Include GIC effects in power flow calculations. Required for accessing GIC-related data like transformer coil resistances.

  • ts_include (bool, default False) – Include GIC effects in transient stability simulations.

  • calc_mode (str, default 'SnapShot') – GIC calculation mode. Options: - ‘SnapShot’: Single time point calculation - ‘TimeVarying’: Time series from uniform field - ‘NonUniformTimeVarying’: Time series with spatial variation - ‘SpatiallyUniformTimeVarying’: Spatially uniform time series

Example

>>> pw.gic.configure()  # Use defaults (pf_include=True)
>>> pw.gic.configure(ts_include=True)  # Enable for transient stability
>>> pw.gic.configure(calc_mode='TimeVarying')  # For time series analysis
gmatrix(sparse=True)[source]

Retrieve the G-matrix directly from PowerWorld.

This is the recommended approach when working with PowerWorld cases, as it uses the simulator’s internal GIC calculation engine and ensures consistency with PowerWorld’s results.

This method automatically enables GIC in power flow (pf_include=True) before retrieving the matrix, ensuring GIC data is available.

Parameters:

sparse (bool, default True) – If True, returns scipy sparse CSR matrix. If False, returns dense numpy array.

Returns:

scipy.sparse.csr_matrix or ndarray – The GIC conductance matrix (G-matrix) from PowerWorld.

See also

model

Generate full GIC model with H-matrix and per-unit model.

configure

Set GIC options manually.

loadb3d(ftype, fname, setuponload=True)[source]

Load B3D file containing electric field data.

Parameters:
  • ftype (str) – File type identifier.

  • fname (str) – Path to the B3D file.

  • setuponload (bool, default True) – Whether to set up time-varying series on load.

model()[source]

Generate GIC model from current PowerWorld case data.

Extracts substation, bus, line, transformer, and generator data from PowerWorld and computes all GIC matrices (incidence, G-matrix, H-matrix, per-unit linear model). Results are stored as properties on this instance.

Transformer data is sourced from the GICXFormer object type, which provides the authoritative per-winding configuration, substation assignments, and auto-transformer status used by PowerWorld’s GIC calculation engine.

This method automatically enables GIC in power flow (pf_include=True) before retrieving data.

Returns:

GIC – Self, with computed model matrices accessible via properties (G, H, A, zeta, Px, eff).

See also

gmatrix_from_powerworld

Get just the G-matrix from PowerWorld.

configure

Set GIC options manually.

settings(value=None)[source]

View or modify GIC calculation settings.

Parameters:

value (DataFrame, optional) – If provided, updates settings. If None, returns current settings.

Returns:

DataFrame or None – Current settings if value is None.

storm(maxfield, direction, solvepf=True)[source]

Configure synthetic storm with uniform electric field.

Parameters:
  • maxfield (float) – Maximum electric field magnitude (V/km).

  • direction (float) – Storm direction in degrees (0-360, 0=North).

  • solvepf (bool, default True) – Whether to include GIC results in power flow solution.

timevary_csv(fpath)[source]

Upload time-varying series voltage inputs from CSV file.

Parameters:

fpath (str) –

Path to CSV file with format:

Time In Seconds, 1, 2, 3
Branch '1' '2' '1', 0.1, 0.11, 0.14
Branch '1' '2' '2', 0.1, 0.11, 0.14
property A

General incidence matrix of the GIC network.

The first N columns are substation neutral buses, and the remaining M columns are bus nodes. The first 2X rows are high and low windings, and the remaining rows are non-winding branches.

Returns:

scipy.sparse matrix – Shape (branches, N+M).

property G

Conductance Laplacian of the GIC network.

The first N nodes are substation neutral buses, and the remaining M nodes are bus nodes. Computed as: G = A.T @ Gd @ A + Gs

Returns:

scipy.sparse matrix – Shape (N+M, N+M).

property H

Linear GIC function matrix (H-matrix).

Maps induced line voltages to signed effective transformer GICs. Values are in actual current (Amps), not per-unit.

Returns:

scipy.sparse matrix – Shape (nxfmr, nbranches).

property Px

Bus assignment permutation matrix.

Maps each transformer to the bus used to model losses (default: from-bus).

Returns:

scipy.sparse matrix – Shape (nbus, nxfmr).

bus_no_sub

Substation auto-insert option for buses without substations (str).

calc_max_direction

Automatically calculate maximum E-field direction.

calc_mode

'SnapShot', 'TimeVarying', 'NonUniformTimeVarying', or 'SpatiallyUniformTimeVarying'.

Type:

Calculation mode

property eff

Effective GIC operator matrix.

Calculates effective transformer GICs when applied to the vector of branch GICs. Includes non-winding branches; trim dimensions for faster computation when only line voltages are used.

Returns:

scipy.sparse matrix – Shape (nxfmr, nbranches).

efield_angle

Electric field storm direction in degrees (float).

efield_mag

Electric field magnitude in V/distance (float).

hotspot_include

Include hotspot in the electric field calculation.

min_kv

Minimum nominal kV to include GIC effects (float).

pf_include

Include GIC effects in power flow calculations.

segment_length_km

Maximum line segment length in km for non-uniform fields (float).

skip_equiv_lines

Skip DC voltage calculation on equivalent lines.

skip_low_r_lines

Skip DC voltage calculation on low per-unit-distance R lines.

ts_include

Include GIC effects in transient stability simulations.

update_line_volts

Auto-update line DC voltages during GIC solution.

property zeta

Per-unit linear GIC model.

Returns the constant-current load (prior to absolute value) in per-unit for each transformer. This is the fastest option for modeling GICs in power flow studies.

Returns:

scipy.sparse matrix – Per-unit GIC model matrix.

Network Topology

Network graph analysis including incidence matrices, Laplacians, and path calculations.

class Network(pw=None)[source]

Bases: object

Network matrix construction and analysis.

Builds sparse network matrices (incidence, Laplacian) and computes branch electrical parameters. AC branches and HVDC transmission lines are always included when present in the case.

This class is accessed via PowerWorld.network and delegates all data access to its parent PowerWorld instance.

Notes

Matrix dimensions follow PowerWorld bus ordering. Use busmap() to translate between bus numbers and matrix indices.

buscoords(astuple=True)[source]

Retrieve bus latitude and longitude from substation data.

Parameters:

astuple (bool, default True) – If True, return (Longitude, Latitude) Series tuple. If False, return merged DataFrame.

Returns:

tuple of pd.Series or DataFrame

busmap()[source]

Create mapping from bus numbers to matrix indices.

Returns:

pd.Series – Series indexed by BusNum with positional values.

delay(min_delay=10e-4)[source]

Compute effective propagation delay for network branches.

Parameters:

min_delay (float, default 10e-4) – Minimum delay value to prevent numerical overflow when computing 1/delay^2 in the Laplacian.

Returns:

ndarray – Effective propagation parameter beta for each branch.

Notes

  • Branch inductance: omega * L_ij = Im(Z^br_ij)

  • Effective capacitance: C_ij = (C_i + C_j) / 2

  • Propagation delay: omega * tau_ij = Im(sqrt(Z_ij * Y_ij)) = beta_ij

gamma()[source]

Compute propagation constants for each branch.

Returns:

pd.Series – Complex propagation constant gamma = sqrt(Z * Y).

incidence(remake=True)[source]

Construct the sparse arc-incidence matrix.

Each row represents a branch with +1 at the to-bus and -1 at the from-bus. HVDC lines are appended after AC branches when present.

Parameters:

remake (bool, default True) – If True, recomputes even if cached.

Returns:

scipy.sparse.csc_matrix – Sparse incidence matrix (branches x buses).

laplacian(weights, longer_xfmr_lens=True, len_thresh=0.01)[source]

Construct weighted graph Laplacian: L = A.T @ W @ A.

Parameters:
  • weights (BranchType or ndarray) – Weighting scheme or custom weight vector.

  • longer_xfmr_lens (bool, default True) – Use impedance-based pseudo-lengths for transformers.

  • len_thresh (float, default 0.01) – Threshold (km) below which branches are treated as transformers.

Returns:

scipy.sparse.csc_matrix – Sparse weighted Laplacian matrix (buses x buses).

lengths(longer_xfmr_lens=False, length_thresh_km=0.01)[source]

Get branch lengths in kilometers.

Parameters:
  • longer_xfmr_lens (bool, default False) – Calculate pseudo-lengths for transformers based on their impedance relative to average line impedance per km.

  • length_thresh_km (float, default 0.01) – Branches shorter than this are treated as transformers.

Returns:

pd.Series – Branch lengths in kilometers.

ybranch(asZ=False)[source]

Get branch admittance (or impedance) in complex form.

Parameters:

asZ (bool, default False) – If True, return impedance Z = R + jX.

Returns:

pd.Series – Complex admittance Y = 1/(R + jX) or impedance Z.

yshunt()[source]

Get branch shunt admittance in complex form.

Returns:

pd.Series – Complex shunt admittance Y = G + jB.

zmag()[source]

Get branch impedance magnitudes |Z|.

Returns:

pd.Series – Impedance magnitude for each branch.

class BranchType(value)[source]

Branch weighting schemes for Laplacian construction.

LENGTH

Weight by inverse squared physical length (km^-2).

Type:

int

RES_DIST

Weight by inverse impedance magnitude (resistance distance).

Type:

int

DELAY

Weight by inverse squared propagation delay (s^-2).

Type:

int

DELAY = 3
LENGTH = 1
RES_DIST = 2

Bus Classification

Bus type classification engine for analyzing power flow Jacobian structure and voltage control.

class BusCat(pw=None)[source]

Bases: object

Parsed bus type classifications from a solved power flow case.

Fetches the BusCat field from PowerWorld, parses each bus into its type, control mode, regulation role, and limit status, then provides index-based access for selecting bus subsets.

Typical usage after solving power flow:

>>> pw = PowerWorld("case.pwb")
>>> pw.pflow()
>>> bc = pw.buscat.refresh()
>>> pv = bc.pv_idx()
>>> v_set = bc.v_setpoints()
>>> q_buses = bc.has_q_eqn_idx()

The internal DataFrame is available via the df property and contains one row per bus with columns: VSet, LimLow, LimHigh, Type, Ctrl, Role, Lim, SVC, Eff, Reg.

df

Parsed classification data. Raises RuntimeError if accessed before refresh() is called.

Type:

DataFrame

constrained()[source]

DataFrame of buses at reactive power limits.

constrained_idx()[source]

Indices of buses at a reactive power limit.

eff_pq_idx()[source]

Indices of buses effectively acting as PQ (includes limited PV).

eff_pv_idx()[source]

Indices of buses effectively acting as PV.

has_p_eqn_idx()[source]

Buses with an active power balance equation (all except Slack).

has_q_eqn_idx()[source]

Buses with a reactive power balance equation (effective PQ only).

has_v_eqn_idx()[source]

Buses with a voltage magnitude equation (effective PV and Slack).

local_only_idx()[source]

Indices of regulating buses with local control only (no remote/droop).

no_q_eqn_idx()[source]

Buses without a Q equation (PV and Slack regulate voltage instead).

pq_idx()[source]

Indices of PQ buses (originally typed as PQ).

primary_idx()[source]

Indices of primary regulating buses.

pv(active_only=True)[source]

DataFrame of PV bus classifications.

Parameters:

active_only (bool, default True) – If True, only include PV buses still regulating.

pv_idx(active_only=True)[source]

Indices of PV buses.

Parameters:

active_only (bool, default True) – If True, exclude PV buses that have hit a reactive limit (they are effectively PQ).

refresh()[source]

Fetch BusCat from PowerWorld and rebuild classifications.

Must be called after each power flow solve, since bus types can change when generators hit reactive limits.

Returns:

BusCat – Self, for method chaining.

regulating_idx()[source]

Indices of buses actively regulating voltage.

remote_masters()[source]

DataFrame of primary regulating buses in remote control groups.

secondary_idx()[source]

Indices of secondary regulating buses.

slack_idx()[source]

Indices of Slack buses.

svc_idx()[source]

Indices of buses with SVC or continuous shunt control.

target_idx()[source]

Indices of remotely regulated target buses.

v_setpoints()[source]

Voltage setpoints for buses with a V equation.

Returns values in the same order as has_v_eqn_idx().

Returns:

numpy.ndarray – Per-unit voltage setpoints.

property df

Parsed bus classification DataFrame.

Raises:

RuntimeError – If refresh() has not been called yet.

parse_buscat(cat)[source]

Parse a BusCat string into a bus classification dict.

Extracts the base type (Slack/PV/PQ), control modifiers, regulation role, limit status, and effective type from the descriptive string PowerWorld returns in the BusCat field.

Parameters:

cat (str) – Raw BusCat string from PowerWorld, e.g. "PQ (Remotely Regulated at Var Limit)".

Returns:

dict – Keys:

  • Type (str) – Base bus type name ("SLACK", "PV", or "PQ").

  • Ctrl (str) – Control flags joined by "+", e.g. "REMOTE+DROOP" or "NONE".

  • Role (str) – Regulation role name, or "" if the bus is not part of a regulation group.

  • Lim (bool) – True if the bus is at a reactive power limit.

  • SVC (bool) – True if the bus has SVC or continuous shunt control.

  • Eff (str) – Effective type after limit enforcement (a PV bus at its limit becomes PQ).

  • Reg (bool) – True if the bus is actively regulating voltage (PV/Slack and not limited).

Dynamics

Transient stability simulation utilities for field-watching and result processing.

class TSWatch[source]

Manages TS result field registration and environment preparation.

Example

>>> tsw = TSWatch()
>>> tsw.watch(Gen, [TS.Gen.P, TS.Gen.W])
>>> fields = tsw.prepare(pw)
prepare(pw)[source]

Configure the ESA environment for simulation and build retrieval fields.

Parameters:

pw (PowerWorld) – An initialized PowerWorld instance.

Returns:

List[str] – List of retrieval field strings for TSGetResults.

watch(gtype, fields)[source]

Register fields to record during simulation for a specific object type.

Parameters:
  • gtype (Type[GObject]) – The GObject type to watch (e.g., Gen, Bus, Branch).

  • fields (list) – List of TS field constants or field name strings.

Returns:

TSWatch – Self for method chaining.

property fields

Currently registered watch fields.

get_ts_results(esa, ctg, fields)[source]

Retrieve results for a single contingency using TSGetResults.

Parameters:
  • esa (SAW) – The SAW (SimAuto Wrapper) instance.

  • ctg (str) – Contingency name.

  • fields (List[str]) – List of fields/plots to retrieve.

Returns:

Tuple[Optional[DataFrame], Optional[DataFrame]] – Tuple of (Metadata DataFrame, Data DataFrame), or (None, None).

process_ts_results(meta, df, ctg_name)[source]

Clean and format raw transient stability simulation results.

Parameters:
  • meta (DataFrame) – Metadata DataFrame from TSGetResults.

  • df (DataFrame) – Time-series data DataFrame from TSGetResults.

  • ctg_name (str) – Name of the contingency (added as a column to metadata).

Returns:

Tuple[DataFrame, DataFrame] – (Processed metadata, Processed time-series data).

B3D File Format

Binary 3D electric field data I/O.

Binary 3D (B3D) file format handler for electric field data.

The B3D format stores time-varying electric field data (Ex, Ey) at geographic locations. It is used by PowerWorld for GIC electric field input. This module supports version 4 with variable location points and variable time points.

class B3D(fname=None)[source]

Handler for the B3D (Binary 3D) electric field file format.

Supports reading, writing, and constructing B3D files containing time-varying electric field vectors at geographic locations.

Parameters:

fname (str, optional) – Path to a B3D file to load on initialization.

comment

Metadata comment string.

Type:

str

time_0

Reference time origin.

Type:

int

time_units

Time unit code (0 = milliseconds).

Type:

int

lat, lon

1D arrays of geographic coordinates (float64).

Type:

ndarray

grid_dim

Grid dimensions [nx, ny].

Type:

list of int

time

1D array of time points (uint32).

Type:

ndarray

ex, ey

2D arrays of electric field components, shape (nt, n), dtype float32.

Type:

ndarray

classmethod from_mesh(long, lat, ex, ey, times=None, comment='GWB Electric Field Data')[source]

Construct a B3D from mesh-grid style electric field data.

Currently supports static (single time step) fields only.

Parameters:
  • long (ndarray) – Array of longitudes, shape (n,).

  • lat (ndarray) – Array of latitudes, shape (m,).

  • ex (ndarray) – X-component electric field, shape (n, m).

  • ey (ndarray) – Y-component electric field, shape (n, m).

  • times (ndarray, optional) – Time points. Currently unused.

  • comment (str, default "GWB Electric Field Data") – Metadata comment string.

Returns:

B3D – Initialized B3D object.

load_b3d_file(fname)[source]

Load a B3D binary file into this object.

Parameters:

fname (str) – Path to the B3D file.

Raises:

IOError – If the file is not a valid B3D file or uses an unsupported format.

write_b3d_file(fname)[source]

Write the B3D data to a binary file.

Parameters:

fname (str) – Output file path.

Raises:

ValueError – If data arrays have inconsistent shapes or incorrect dtypes.

General Helpers

Power system utilities and general-purpose helpers.

This module provides: - Function decorators for debugging and profiling

timing(func)[source]

Decorator that prints the execution time of a function.

Parameters:

func (callable) – The function to wrap.

Returns:

callable – Wrapped function that prints timing information.

Examples

>>> @timing
... def slow_function():
...     time.sleep(1)
...
>>> slow_function()
'slow_function' took: 1.0012 sec