Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ For comprehensive guides, API reference, and tutorials, visit the official docum
- **Multiple Partitioning Algorithms** - K-means, K-medoids, DBSCAN, HDBSCAN, and hierarchical clustering
- **Distance Metrics** - Euclidean for local coordinates, Haversine for geographic data
- **Electrical Distance** - Partition based on PTDF-derived electrical proximity
- **Price-Based Clustering** - Partition based on Locational Marginal Price (LMP) profiles
- **Voltage-Aware Clustering** - Respects voltage levels and transformer boundaries
- **Flexible Aggregation** - Sum, average, or custom strategies for node/edge properties
- **Extensible Design** - Easy to add your own partitioning or aggregation strategies
Expand Down
7 changes: 7 additions & 0 deletions docs/api/partitioning.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@ Voltage-Aware Partitioning
:members:
:show-inheritance:
:no-index:

Locational Marginal Price Partitioning
--------------------------------------
.. automodule:: npap.partitioning.locational_marginal_price
:members:
:show-inheritance:
:no-index:
32 changes: 23 additions & 9 deletions docs/user-guide/available-strategies.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,29 +164,43 @@ Combines electrical distance (PTDF approach) with voltage level and AC island co

**Required attributes**: Nodes: `voltage`, `ac_island` | Edges: `x` (reactance)

### Locational Marginal Price Partitioning

Clusters nodes based on nodal prices (LMP) or price profiles.

| Strategy | Algorithm | Description |
|----------|-----------|-------------|
| `lmp_hierarchical_connected` | Hierarchical | Spatially contiguous price-based clustering |

**Required attributes**: Nodes: `lmp`

### Choosing a Partitioning Strategy

```{mermaid}
flowchart TD
A[Start] --> B{Multi-voltage?}
B -->|Yes| C{Need electrical distance?}
B -->|No| D{Need electrical distance?}
C -->|Yes| E[va_electrical_*]
C -->|No| F[va_geographical_*]
D -->|Yes| G[electrical_*]
D -->|No| H{AC islands?}
H -->|Yes| I[geographical_kmedoids_*]
H -->|No| L[geographical_*]
B -->|No| D{Price-based?}
D -->|Yes| E[lmp_*]
D -->|No| F{Need electrical distance?}
C -->|Yes| G[va_electrical_*]
C -->|No| H[va_geographical_*]
F -->|Yes| I[electrical_*]
F -->|No| J{AC islands?}
J -->|Yes| K[geographical_kmedoids_*]
J -->|No| L[geographical_*]

style A fill:#2993B5,stroke:#1d6f8a,color:#fff
style B fill:#FFBF00,stroke:#cc9900,color:#1e293b
style C fill:#FFBF00,stroke:#cc9900,color:#1e293b
style D fill:#FFBF00,stroke:#cc9900,color:#1e293b
style E fill:#0fad6b,stroke:#076b3f,color:#fff
style F fill:#0fad6b,stroke:#076b3f,color:#fff
style F fill:#FFBF00,stroke:#cc9900,color:#1e293b
style G fill:#0fad6b,stroke:#076b3f,color:#fff
style H fill:#FFBF00,stroke:#cc9900,color:#1e293b
style H fill:#0fad6b,stroke:#076b3f,color:#fff
style I fill:#0fad6b,stroke:#076b3f,color:#fff
style J fill:#FFBF00,stroke:#cc9900,color:#1e293b
style K fill:#0fad6b,stroke:#076b3f,color:#fff
style L fill:#0fad6b,stroke:#076b3f,color:#fff
```

Expand Down
11 changes: 6 additions & 5 deletions docs/user-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,12 @@ G = nx.MultiDiGraph()

Different strategies require specific node and edge attributes:

| Strategy Type | Node Attributes | Edge Attributes |
|---------------|-----------------|-----------------|
| Geographical | `lat`, `lon` | — |
| Electrical | `ac_island` | `x` (reactance) |
| Voltage-Aware | `lat`, `lon`, `voltage`, `ac_island` | `x`, `type` |
| Strategy Type | Node Attributes | Edge Attributes |
|---------------------------|-----------------|-----------------|
| Geographical | `lat`, `lon` | — |
| Electrical | `ac_island` | `x` (reactance) |
| Voltage-Aware | `lat`, `lon`, `voltage`, `ac_island` | `x`, `type` |
| Locational Marginal Price | `lmp` | — |

## Error Handling

Expand Down
29 changes: 16 additions & 13 deletions docs/user-guide/partitioning/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ Partitioning divides a network into clusters of nodes based on distance metrics.

## Overview

| Strategy Family | Distance Metric | Use Case |
|-----------------|----------------------------|-------------------------------------------|
| Geographical | Euclidean / Haversine | Spatial proximity clustering |
| Electrical | PTDF-based | Electrical behavior clustering |
| VA Geographical | Geographical + Voltage aware | Multi-voltage-level networks |
| VA Electrical | PTDF + Voltage aware | Multi-voltage-level electrical clustering |
| Strategy Family | Distance Metric | Use Case |
|----------------------------------|-----------------------------------|-------------------------------------------|
| Geographical | Euclidean / Haversine | Spatial proximity clustering |
| Electrical | PTDF-based | Electrical behavior clustering |
| VA Geographical | Geographical + Voltage aware | Multi-voltage-level networks |
| VA Electrical | PTDF + Voltage aware | Multi-voltage-level electrical clustering |
| Locational Marginal Prices (LMP) | Duals of nodal balance constraint | Congestion sensitive clustering |

## Basic Usage

Expand Down Expand Up @@ -47,6 +48,7 @@ print(partition_result.strategy_name)

geographical
electrical
locational_marginal_price
va-geographical
va-electrical
```
Expand Down Expand Up @@ -114,13 +116,14 @@ flowchart TD

### Recommendations by Use Case

| Use Case | Recommended Strategy |
|------------------------------|---------------------|
| Geographical clustering | `geographical_kmeans` |
| Geographical with AC islands | `geographical_kmedoids_haversine` |
| Electrical behavior grouping | `electrical_kmedoids` |
| Multi-voltage network | `va_geographical_kmedoids_haversine` |
| Unknown cluster count | `geographical_dbscan_*` or `geographical_hdbscan_*` |
| Use Case | Recommended Strategy |
|---------------------------------|-----------------------------------------------------|
| Geographical clustering | `geographical_kmeans` |
| Geographical with AC islands | `geographical_kmedoids_haversine` |
| Electrical behavior grouping | `electrical_kmedoids` |
| Multi-voltage network | `va_geographical_kmedoids_haversine` |
| Unknown cluster count | `geographical_dbscan_*` or `geographical_hdbscan_*` |
| Identification of Bidding Zones | `lmp_hierarchical_connected` |

### Performance Comparison

Expand Down
91 changes: 91 additions & 0 deletions docs/user-guide/partitioning/locational_marginal_price.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Locational Marginal Price Partitioning

Clusters nodes based on Locational Marginal Prices (LMP) or price profiles. The implementation of this strategy is based on the findings in the
paper "Congestion-Sensitive Grid Aggregation for DC Optimal Power Flow" by B. Stöckl et al. ([2025](https://doi.org/10.1109/PowerTech59965.2025.11180585)).

## Required Attributes

- **Nodes**: `lmp` (can be a scalar value or a time-series vector/array)

## Available Strategies

| Strategy | Algorithm | Description | AC-Island Aware |
|------------------------------|--------------|--------------------------------------------------------|-----------------|
| `lmp_hierarchical_connected` | Hierarchical | Agglomerative clustering with connectivity constraints | Yes |

## Overview

LMP-based partitioning is specifically designed for market-based network analysis. It identifies regions with similar price behaviors, which is often
used to define bidding zones or identify structural congestion in a power system.

The strategy calculates the Euclidean distance between LMP profiles (vectors of prices over time) to determine nodal similarity.

### Connectivity Constraint

By default, the `lmp_hierarchical_connected` strategy uses the graph's topology as a constraint. This ensures that resulting clusters are **spatially
contiguous**—nodes in the same cluster must be physically connected in the network. This prevents "scattered" clusters that would be unrealistic for
geographical price zones.

### Mathematical Background

Locational Marginal Prices (LMPs) are often used in power systems to reflect the cost of supplying the next increment of load at a specific location,
considering generation costs and network constraints.

In an optimal power flow (OPF) with a bus angle formulation, they can be mathematically defined as the dual variables $\lambda$ associated with the
nodal balance constraints.

In an optimal power flow implementation based on PTDF power flows, the LMPs can be expressed as:
$$\mathrm{LMP} = \lambda_{slack} + \mathrm{PTDF}^\top (\bar\varphi - \underline\varphi)$$
where:

- $ \lambda_{slack} $ is the LMP at the slack bus (reference node).
- $ \mathrm{PTDF} $ is the Power Transfer Distribution Factor matrix, which describes how power injections at nodes affect line flows.
- $ \bar\varphi $ and $ \underline\varphi $ are the dual variables associated with the upper and lower line flow constraints, respectively.

### AC-Island Awareness

If the graph contains `ac_island` data (automatically set by `va_loader`), the strategy respects AC island boundaries by assigning infinite distance
between nodes in different islands. This ensures that price zones do not span across asynchronous regions.

## Usage

```python
import npap

manager = npap.PartitionAggregatorManager()
manager.load_data("va_loader", node_file="nodes.csv", line_file="lines.csv")

# Partition based on LMP profiles
partition = manager.partition(
strategy="lmp_hierarchical_connected",
n_clusters=15,
lmp_attr="lmp", # Node attribute containing prices
use_connectivity=True # Ensure contiguous clusters
)
```

## Configuration

The LMP strategy uses {py:class}`~npap.partitioning.locational_marginal_price.LMPConfig`:

```python
from npap.partitioning.locational_marginal_price import LMPConfig

config = LMPConfig(
hierarchical_linkage="complete",
infinite_distance=1e4,
use_connectivity=True
)

partition = manager.partition(
"lmp_hierarchical_connected",
n_clusters=15,
config=config
)
```

| Parameter | Default | Description |
|------------------------|--------------|---------------------------------------------------------------|
| `hierarchical_linkage` | `"complete"` | Linkage criterion for hierarchical clustering. |
| `infinite_distance` | `1e4` | Value used to separate nodes in different AC islands. |
| `use_connectivity` | `True` | Whether to use graph connectivity as a clustering constraint. |
5 changes: 4 additions & 1 deletion npap/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
TopologyStrategy,
)
from npap.logging import LogCategory, log_debug, log_info, log_warning
from npap.partitioning import VAElectricalDistancePartitioning
from npap.partitioning import LMPPartitioning, VAElectricalDistancePartitioning


class InputDataManager:
Expand Down Expand Up @@ -156,6 +156,9 @@ def _register_default_strategies(self) -> None:
algorithm="hierarchical"
)

# Locational Marginal Price partitioning strategies
self._strategies["lmp_hierarchical_connected"] = LMPPartitioning(algorithm="hierarchical")

log_debug("Registered default partitioning strategies", LogCategory.MANAGER)


Expand Down
2 changes: 2 additions & 0 deletions npap/partitioning/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@

from .electrical import ElectricalDistancePartitioning
from .geographical import GeographicalPartitioning
from .locational_marginal_price import LMPPartitioning
from .va_electrical import VAElectricalDistancePartitioning
from .va_geographical import VAGeographicalPartitioning

__all__ = [
"ElectricalDistancePartitioning",
"GeographicalPartitioning",
"LMPPartitioning",
"VAElectricalDistancePartitioning",
"VAGeographicalPartitioning",
]
Loading
Loading