Market Scores#
Market-output scoring functions, including PTDF construction and node-level scores from dispatch, prices, and congestion duals.
Score Definitions#
The table below introduces the notation used throughout the market-based scores.
Symbol |
Meaning |
|---|---|
\(g\) |
Generator |
\(v\) |
Node |
\(G(v)\) |
Set of generators connected to node \(v\) |
\(d_g\) |
Dispatch of generator \(g\) |
\(c_g\) |
Marginal cost of generator \(g\) |
\(\lambda_v\) |
Nodal price at node \(v\) |
\(\gamma_g\) |
Generator-capacity dual value |
\(P_g^{\max}\) |
Maximum capacity of generator \(g\) |
\(L_v\) |
Load at node \(v\) |
\(\mu_{vw}^{+}, \mu_{vw}^{-}\) |
Congestion duals on line \((v,w)\) |
\(F_{vw}^{\max}\) |
Capacity of line \((v,w)\) |
\(\delta(v)\) |
Set of lines incident to node \(v\) |
\(\mathrm{PTDF}_{\ell,v}\) |
PTDF coefficient of line \(\ell\) with respect to node \(v\) |
\(m_\ell\) |
Residual margin on line \(\ell\) |
\(\varepsilon\) |
Small positive constant to avoid division by zero |
The main market-based scores are summarized below.
Score |
Definition |
Interpretation |
|---|---|---|
Rent-Weighted Dispatch |
\(s_g = \max(0, \lambda_{n(g)} - c_g)d_g,\quad S_v = \sum_{g \in G(v)} s_g\) |
Rewards nodes whose dispatched generators earn positive margin at the baseline prices. |
Dispatch Volume |
\(S_v = \sum_{g \in G(v)} d_g\) |
Measures how much generation is dispatched at each node. |
Gamma-Capacity |
\(s_g = \gamma_g P_g^{\max},\quad S_v = \sum_{g \in G(v)} s_g\) |
Emphasizes nodes with generators that are scarce in the baseline solution. |
Gamma-Capacity-Congestion |
\(S_v = \sum_{g \in G(v)} \gamma_g P_g^{\max} + \sum_{(v,w) \in \delta(v)} F_{vw}^{\max}(\mu_{vw}^{+} + \mu_{vw}^{-})\) |
Combines generator scarcity with congestion on adjacent lines. |
Load-Weighted LMP |
\(S_v = \lambda_v L_v\), or \(S_v = \min(\lambda_v, \mathrm{VOLL})L_v\) when capping is used |
Highlights nodes where high prices coincide with high demand. |
PTDF Stress |
\(S_v = \left(\sum_{g \in G(v)} P_g^{\max}\right)\sum_{\ell}\frac{|\mathrm{PTDF}_{\ell,v}|}{m_\ell + \varepsilon}\) |
Captures how strongly a node’s installed capacity is exposed to tight transmission constraints. |
API path: node_ranking.market_scores
Market-based score functions and PTDF utilities for node ranking.
This module contains:
PTDF construction helpers for DC network representations
node-level scores based on baseline dispatch outputs
score formulations that use dispatch, nodal prices, scarcity duals, and congestion duals
- build_B_matrix(G, b_attr='B')[source]#
Build the nodal susceptance (Laplacian) matrix for DC power flow.
- Parameters:
G (nx.Graph) – Undirected graph with edge attribute b_attr = susceptance.
b_attr (str) – Edge attribute name for susceptance.
- Returns:
B ((n, n) np.ndarray) – Nodal susceptance matrix.
nodes (list) – Node labels in the order used for B.
node_index (dict) – Map node label -> row/col index in B.
- Return type:
tuple[ndarray, list[Hashable], dict[Hashable, int]]
Notes
For each edge
(u, v), the edge susceptance is added to the diagonal entries ofuandvand subtracted from the corresponding off-diagonal entries.
- compute_bus_angle_basis(Binv, n, slack_idx, mask)[source]#
Compute bus-angle responses for unit injections at non-slack buses.
- Parameters:
Binv (np.ndarray) – Inverse of the reduced susceptance matrix.
n (int) – Number of buses in the original full system.
slack_idx (int) – Index of the slack bus in the full system. This argument is included for interface clarity; the slack angle is fixed implicitly by reinserting a zero row.
mask (list[int]) – Full-system indices of the non-slack buses.
- Returns:
theta_full – Voltage-angle responses with slack angle fixed at zero.
- Return type:
(n, n-1) np.ndarray
- compute_ptdf(G, slack=None, b_attr='B')[source]#
Compute the PTDF matrix (1 MW injection, slack withdrawal convention).
- Parameters:
G (nx.Graph) – Network with line susceptance stored in
b_attr.slack (hashable or None, default=None) – Label of the slack node. If omitted, the first node in
G.nodes()is used.b_attr (str, default="B") – Edge attribute containing line susceptance.
- Returns:
ptdf ((m, n-1) np.ndarray) – Rows = lines in edges, cols = non-slack buses (order = mask).
edges (list[tuple]) – Edge list in the row order of ptdf.
nodes (list) – Node labels in column/angle order used to build B.
mask (list[int]) – Indices of non-slack buses (maps ptdf columns -> nodes[mask[c]]).
slack_node (hashable) – The chosen slack node label.
- Return type:
tuple[ndarray, list[tuple[Hashable, Hashable, dict[str, Any]]], list[Hashable], list[int], Hashable]
Notes
The returned PTDF has one column per non-slack bus. Column
ccorresponds to nodenodes[mask[c]].
- dispatch_volume_score(nodes, generators, baseline_result)[source]#
Compute node dispatch-volume scores from a baseline dispatch.
This score is the total dispatched generation connected to each node.
Score definition:
\[S_v = \sum_{g \in G(v)} d_g\]- Parameters:
nodes (dict) – Node data keyed by node label.
generators (dict) – Generator data keyed by generator id. Each generator must provide a
nodeentry.baseline_result (dict) – Baseline dispatch outputs containing
dispatch.
- Returns:
Node-level dispatch volume scores.
- Return type:
dict[str, float]
- gamma_capacity_congestion_score(nodes, generators, lines, baseline_result)[source]#
Compute gamma-capacity-congestion score (GCCS) per node.
This score adds two components at each node:
a generator-scarcity term based on
gamma_g P_g^{max}a congestion term based on the dual values of incident line limits
Score definition:
\[S_v = \sum_{g \in G(v)} \gamma_g P_g^{\max} + \sum_{(v,w) \in \delta(v)} F_{vw}^{\max}\left(\mu_{vw}^{+} + \mu_{vw}^{-}\right)\]- Parameters:
nodes (dict) – Node data keyed by node label.
generators (dict) – Generator data keyed by generator id.
lines (dict) – Line data keyed by line id. Each line must provide
endsandcapacity.baseline_result (dict) – Baseline dispatch outputs containing
gamma,mu_plus, andmu_minus.
- Returns:
Node-level gamma-capacity-congestion scores.
- Return type:
dict[str, float]
- gamma_capacity_score(nodes, generators, baseline_result)[source]#
Compute gamma-capacity scores from a baseline dispatch.
This score combines the generator-capacity dual
gammawith installed capacity and then aggregates the resulting generator-level scarcity signal to nodes.Score definition:
\[s_g = \gamma_g P_g^{\max}\]\[S_v = \sum_{g \in G(v)} s_g\]- Parameters:
nodes (dict) – Node data keyed by node label.
generators (dict) – Generator data keyed by generator id. Each generator must provide
nodeandp_max.baseline_result (dict) – Baseline dispatch outputs containing
gamma.
- Returns:
(node_scores, gen_scores)with node-level and generator-level gamma-capacity scores.- Return type:
tuple[dict[str, float], dict[str, float]]
- invert_reduced_B(B, slack_idx)[source]#
Invert the reduced B matrix after removing the slack row/column.
- Parameters:
B (np.ndarray) – Full nodal susceptance matrix.
slack_idx (int) – Index of the slack bus in
B.
- Returns:
Binv ((n-1, n-1) np.ndarray) – Inverse of the reduced B matrix.
mask (list[int]) – Indices of non-slack buses kept in the reduced system.
full2red (np.ndarray) – Map full-bus index -> reduced index (or -1 for slack).
- Return type:
tuple[ndarray, list[int], ndarray]
- load_weighted_lmp_score(nodes, baseline_result, VOLL=500.0, cap_lambda=True)[source]#
Compute load-weighted LMP scores from baseline nodal prices and load.
This score highlights nodes where high prices coincide with high load. Optionally, nodal prices can be capped at
VOLLbefore weighting.Score definition:
\[S_v = \lambda_v L_v\]When
cap_lambdais enabled, the score becomes:\[S_v = \min(\lambda_v, \mathrm{VOLL}) L_v\]- Parameters:
nodes (dict) – Node data keyed by node label. Each node must provide
load.baseline_result (dict) – Baseline dispatch outputs containing
lambdas.VOLL (float, default=500.0) – Price cap applied when
cap_lambdais enabled.cap_lambda (bool, default=True) – Whether to cap nodal prices at
VOLLbefore scoring.
- Returns:
Node-level load-weighted LMP scores.
- Return type:
dict[str, float]
- ptdf_stress_score(ptdf, nodes, mask, generators, line_margins, epsilon=1e-06)[source]#
Compute PTDF stress score (PTDFS) per node.
This score combines installed capacity at a node with the node’s PTDF exposure to lines that have little residual margin. Higher values indicate nodes whose injections are both large and strongly coupled to tight lines.
For each non-slack node \(v\):
\[S_v = \left(\sum_{g \in G(v)} P_g^{\max}\right)\sum_{\ell}\frac{\left|\mathrm{PTDF}_{\ell,v}\right|}{m_{\ell} + \varepsilon}\]where \(m_{\ell}\) is the residual line margin and \(\varepsilon\) avoids division by zero. Slack node score is set to 0 because PTDF is provided only for non-slack columns (given by mask).
- Parameters:
ptdf (np.ndarray) – PTDF matrix with rows as lines and columns as non-slack buses.
nodes (list[hashable]) – Node labels in the full network order.
mask (list[int]) – Indices of non-slack buses corresponding to PTDF columns.
generators (dict) – Generator data keyed by generator id. Each generator must provide
nodeandp_max.line_margins (np.ndarray or list[float]) – Residual margin for each PTDF row / line.
epsilon (float, default=1e-6) – Small positive stabilizer in the denominator.
- Returns:
PTDF stress score for each node in the full node set.
- Return type:
dict[hashable, float]
- rent_weighted_dispatch_score(nodes, generators, baseline_result)[source]#
Compute rent-weighted dispatch scores from a baseline dispatch.
This score rewards dispatched generators that earn a positive gross margin at the baseline nodal price, then aggregates those generator-level scores to nodes.
Score definition:
\[s_g = \max(0, \lambda_{n(g)} - c_g)\, d_g\]\[S_v = \sum_{g \in G(v)} s_g\]where \(d_g\) is dispatch, \(c_g\) is generator cost, and \(\lambda_{n(g)}\) is the nodal price at the generator’s node.
- Parameters:
nodes (dict) – Node data keyed by node label. Used to initialize the node-score map.
generators (dict) – Generator data keyed by generator id. Each generator must provide at least
nodeandcost.baseline_result (dict) – Baseline dispatch outputs containing
dispatchandlambdas.
- Returns:
(node_scores, gen_scores)with node-level and generator-level rent-weighted dispatch scores.- Return type:
tuple[dict[str, float], dict[str, float]]
Reference#
M. Bichler and T. Dobos. A market-based analysis of critical nodes in power systems. In International Conference on the European Energy Market, EEM 2026, June 2026.