Partitioning#

Partitioning divides a network into clusters of nodes based on distance metrics. NPAP provides four partitioning strategy families, each suited for different use cases.

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

Basic Usage#

All partitioning is done through the PartitionAggregatorManager:

import npap

manager = npap.PartitionAggregatorManager()
manager.load_data("networkx_direct", graph=G)

# Partition the network
partition_result = manager.partition(
    strategy="geographical_kmeans",
    n_clusters=10
)

The result is a PartitionResult object:

print(partition_result.mapping)
# {0: ['bus_1', 'bus_2'], 1: ['bus_3', 'bus_4'], ...}

print(partition_result.n_clusters)
# 10

print(partition_result.strategy_name)
# 'geographical_kmeans'

AC-Island Awareness#

What Are AC Islands?#

In networks with HVDC links, AC-connected regions form AC islands. These islands:

  • Are electrically separate in AC sense

  • Connected only through DC links

  • Have independent power flow characteristics

How NPAP Handles AC Islands#

When nodes have a ac_island attribute (automatically set by va_loader):

  1. Distance matrices include infinite distances between islands

  2. Clustering algorithms using precomputed distances respect these boundaries

  3. Partitions never span across AC island boundaries

Algorithm Support#

Algorithm

AC-Island Support

K-Means

No (works on raw coordinates)

K-Medoids

Yes

DBSCAN

Yes

HDBSCAN

Yes

Hierarchical (non-ward)

Yes

Hierarchical (ward)

No

Note

K-Means and Ward linkage will issue a warning if AC islands are detected but will proceed without respecting island boundaries.

Choosing a Strategy#

Decision Guide#

        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| K[geographical_kmeans]

    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 G fill:#0fad6b,stroke:#076b3f,color:#fff
    style H fill:#FFBF00,stroke:#cc9900,color:#1e293b
    style I fill:#0fad6b,stroke:#076b3f,color:#fff
    style K fill:#0fad6b,stroke:#076b3f,color:#fff
    

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_*

Performance Comparison#

Strategy

Speed

Memory

Robustness

K-Means

Fast

Low

Medium

K-Medoids

Medium

High (distance matrix)

High

DBSCAN

Medium

Medium

High

HDBSCAN

Slow

High

Very High

Hierarchical

Medium

High

High

Configuration Options#

All geographical partitioning strategies share a common configuration:

from npap.partitioning import GeographicalConfig

config = GeographicalConfig(
    random_state=42,              # Reproducibility
    max_iter=300,                 # K-Means iterations
    n_init=10,                    # K-Means initializations
    hierarchical_linkage="ward",  # Hierarchical linkage
    infinite_distance=1e4         # AC island separation
)

Electrical strategies have their own configuration:

from npap.partitioning import ElectricalDistanceConfig

config = ElectricalDistanceConfig(
    zero_reactance_replacement=1e-5,
    regularization_factor=1e-10,
    infinite_distance=1e4
)

Working with Partition Results#

Inspecting Results#

partition = manager.partition("geographical_kmeans", n_clusters=5)

# Cluster mapping
print(partition.mapping)
# {0: ['bus_1', 'bus_2'], 1: ['bus_3'], ...}

# Number of clusters
print(partition.n_clusters)

# Strategy used
print(partition.strategy_name)

# Graph hash for validation
print(partition.original_graph_hash)

Reusing Partitions#

Partition results can be reused for multiple aggregations:

# Partition once
partition = manager.partition("geographical_kmeans", n_clusters=10)

# Aggregate with different profiles
simple_agg = manager.aggregate(partition, mode=AggregationMode.SIMPLE)
geo_agg = manager.aggregate(partition, mode=AggregationMode.GEOGRAPHICAL)

Validation#

NPAP validates that partitions match their source graph:

# This will raise GraphCompatibilityError if graph changed
aggregated = manager.aggregate(partition)