powergrid_synth.transmission.generation_dispatcher

Generation dispatch for synthetic power grids.

This module implements the generation dispatch algorithm from Sadeghian et al. (2018). It partitions generator units into three groups — uncommitted (\(\alpha = 0\)), partially committed (\(0 < \alpha < 1\)), and fully committed (\(\alpha \approx 1\)) — then assigns participation factors (dispatch factors) and iteratively balances total generation against total load. The dispatch factor is defined as

\[\alpha_i = P_{g_i} / P_{g_i}^{\max}, \qquad i = 1, \ldots, N_G\]

The correlation between normalised capacity and dispatch factor is reproduced via a 2-D empirical PMF table (Tab_2D_Pg).

Ported from the SynGrid MATLAB function sg_gen_dist.m.

Module Contents

class powergrid_synth.transmission.generation_dispatcher.GenerationDispatcher(graph, ref_sys_id=1)[source]

Assign active-power dispatch to each generator bus.

The algorithm follows Sadeghian et al. (2018) (Section III):

  1. Uncommitted units (10–20 %): \(\alpha = 0\), selected via targets drawn from Uniform[0, 0.6].

  2. Partially committed units (40–50 %): selected via exponential distribution on capacity; dispatch factors assigned through a 2-D bin-matching table Tab_2D_Pg (\(14 \times 10\)).

  3. Fully committed units (remainder): \(\alpha = 1\).

  4. Balancing loop: iteratively adjusts dispatch to match total load within 1 % tolerance.

Parameters:
  • graph (networkx.Graph) – Power grid graph. Generator nodes must have 'bus_type' == 'Gen' and 'pg_max' (MW) attributes. Load nodes must have 'pl' (MW).

  • ref_sys_id (int, optional) – Reference system for statistical tables (1 = NYISO-2935, 2 = WECC-16994, 3 = additional reference). Default is 1.

alpha_mod

Loading-level flag from the reference system. When 0 all alphas are Uniform[0, 1]; otherwise 0.5 % receive negative dispatch (e.g., pumped-storage hydro).

Type:

int

mu_committed

Exponential-distribution parameter for committed-unit capacities.

Type:

float

tab_2d_pg

2-D empirical PMF table (14 capacity bins × 10 alpha bins).

Type:

numpy.ndarray

_assign_alphas(units, alphas)[source]

Assign dispatch factors to committed units via 2-D bin matching.

Units are sorted by normalised capacity and alphas by value, then distributed into bins defined by Tab_2D_Pg (14 capacity bins \(\times\) 10 alpha bins). Within each bin, units and alphas are paired randomly (high-to-low bin traversal). Any leftovers are paired sequentially as a fallback.

This reproduces the empirical joint distribution \(f(\bar{P}_{g}^{\max}, \alpha)\) from the reference system (Sadeghian et al., 2018, Table I).

Parameters:
  • units (numpy.ndarray, shape (n, 2)) – [bus_id, normalised_capacity].

  • alphas (numpy.ndarray, shape (n, 1)) – Dispatch-factor values from _generate_alphas().

Returns:

[bus_id, normalised_capacity, alpha].

Return type:

numpy.ndarray, shape (m, 3)

_generate_alphas(n_comm)[source]

Generate dispatch factors for partially committed units.

When alpha_mod == 0 (e.g. NYISO), all \(\alpha\) values are drawn from Uniform[0, 1]. When alpha_mod != 0 (e.g. WECC), 99.5 % are Uniform[0, 1] and 0.5 % are negative, representing reverse dispatch such as pumped-storage hydro.

Parameters:

n_comm (int) – Number of committed units requiring \(\alpha\) values.

Returns:

Dispatch-factor values.

Return type:

numpy.ndarray, shape (n_comm, 1)

_select_committed(norm_pg_max, total_units_count)[source]

Select generators to be partially committed (\(0 < \alpha < 1\)).

Selects 40–50 % of total generator count. 99 % of these are chosen by matching to targets drawn from an exponential distribution with parameter \(\mu_{\text{committed}}\); the remaining 1 % are drawn from the extreme tail Uniform[0.5, 1.0], capturing super-large units (Sadeghian et al., 2018, Sec. III-A).

Parameters:
  • norm_pg_max (numpy.ndarray, shape (n, 2)) – Remaining units after uncommitted selection: [bus_id, normalised_capacity].

  • total_units_count (int) – Original total number of generator units (before any selection).

Returns:

  • committed (numpy.ndarray, shape (m, 2)) – Committed units: [bus_id, norm_cap].

  • remaining (numpy.ndarray, shape (n-m, 2)) – Units not selected (will become fully committed).

Return type:

Tuple[numpy.ndarray, numpy.ndarray]

_select_uncommitted(norm_pg_max)[source]

Select generators to be uncommitted (\(\alpha = 0\)).

Randomly selects 10–20 % of total generator units. Target capacities are drawn from Uniform[0, 0.6] and the unit whose normalised capacity is closest to each target is selected. This reproduces the empirical observation that uncommitted units tend to be small or medium-size (Sadeghian et al., 2018, Sec. III).

Parameters:

norm_pg_max (numpy.ndarray, shape (n, 2)) – Array with columns [bus_id, normalised_capacity].

Returns:

  • uncommitted (numpy.ndarray, shape (m, 3)) – Uncommitted units: [bus_id, norm_cap, alpha=0].

  • remaining (numpy.ndarray, shape (n-m, 2)) – Units not selected.

Return type:

Tuple[numpy.ndarray, numpy.ndarray]

dispatch()[source]

Run the full generation dispatch pipeline.

Implements the algorithm of Sadeghian et al. (2018):

  1. Collect generator buses and normalise capacities by \(P^{\max}_{g_{\max}}\).

  2. Partition generators into uncommitted (\(\alpha = 0\)), partially committed (\(0 < \alpha < 1\)), and fully committed (\(\alpha = 1\)).

  3. Assign dispatch factors to partially committed units via the 2-D bin-matching table Tab_2D_Pg.

  4. Iteratively balance total generation against total load (1 % tolerance, up to 50 iterations) by scaling committed \(\alpha\) values and toggling uncommitted / full-load units on or off.

  5. Convert normalised dispatch back to MW: \(P_{g_i} = \alpha_i \cdot \bar{P}_{g_i}^{\max} \cdot P^{\max}_{g_{\max}}\).

Returns:

Mapping of generator bus ID to dispatched active power (MW).

Return type:

dict