Quick Start#
This guide demonstrates a complete NPAP workflow using a voltage-aware (VA) power network. By the end, you’ll understand how to load data, partition a network, aggregate it, and visualize the results.
The NPAP Workflow#
flowchart LR
A[Load Data] --> B[Aggregate Parallel Edges]
B --> C[Group by Voltage]
C --> D[Partition]
D --> E[Aggregate]
E --> F[Visualize]
style A fill:#2993B5,stroke:#1d6f8a,color:#fff
style B fill:#0fad6b,stroke:#076b3f,stroke-dasharray: 5 5,color:#fff
style C fill:#0fad6b,stroke:#076b3f,stroke-dasharray: 5 5,color:#fff
style D fill:#2993B5,stroke:#1d6f8a,color:#fff
style E fill:#2993B5,stroke:#1d6f8a,color:#fff
style F fill:#2993B5,stroke:#1d6f8a,color:#fff
Dashed boxes indicate optional steps
Complete Example: Voltage-Aware Network#
This example shows the full workflow for a multi-voltage power system with transmission lines, transformers, and DC links.
import npap
from npap import AggregationMode
import networkx as nx
# =============================================================================
# Step 1: Initialize the Manager
# =============================================================================
manager = npap.PartitionAggregatorManager()
# =============================================================================
# Step 2: Load Voltage-Aware Data
# =============================================================================
# The VA loader handles multi-voltage networks with transformers and DC links
graph = manager.load_data(
strategy="va_loader",
node_file="buses.csv", # Bus/substation data
line_file="lines.csv", # AC transmission lines
transformer_file="transformers.csv", # Transformers between voltage levels
converter_file="converters.csv", # AC/DC converters
link_file="dc_links.csv" # HVDC links
)
print(f"Loaded network: {graph.number_of_nodes()} nodes, {graph.number_of_edges()} edges")
# =============================================================================
# Step 3: Aggregate Parallel Edges (if present)
# =============================================================================
# Power networks often have parallel transmission lines
if isinstance(graph, nx.MultiDiGraph):
manager.aggregate_parallel_edges(
edge_properties={
"x": "equivalent_reactance", # Parallel reactance formula: 1/(1/x1 + 1/x2)
"r": "equivalent_reactance", # Same for resistance
"p_max": "sum", # Sum the capacities
},
default_strategy="average",
warn_on_defaults=False
)
print(f"After parallel edge aggregation: {manager.get_current_graph().number_of_edges()} edges")
# =============================================================================
# Step 4: Partition the Network
# =============================================================================
# VA-geographical partitioning respects voltage levels and AC islands
partition_result = manager.partition(
strategy="va_geographical_kmedoids_haversine",
n_clusters=50
)
print(f"Created {partition_result.n_clusters} clusters")
print(f"Strategy used: {partition_result.strategy_name}")
# Inspect cluster assignments
for cluster_id, nodes in list(partition_result.mapping.items())[:3]:
print(f" Cluster {cluster_id}: {len(nodes)} nodes")
# =============================================================================
# Step 5: Visualize Partitioned Network
# =============================================================================
# Plot the network colored by cluster assignment
manager.plot_network(
style="clustered",
title="Partitioned VA Network"
)
# =============================================================================
# Step 6: Aggregate the Network
# =============================================================================
# Aggregate using the GEOGRAPHICAL mode (averages coordinates, sums loads)
aggregated_graph = manager.aggregate(mode=AggregationMode.GEOGRAPHICAL)
print(f"Aggregated network: {aggregated_graph.number_of_nodes()} nodes, "
f"{aggregated_graph.number_of_edges()} edges")
# =============================================================================
# Step 7: Visualize Aggregated Network
# =============================================================================
manager.plot_network(
graph=aggregated_graph,
style="voltage_aware",
title="Aggregated Network"
)
Understanding the Data Files#
buses.csv (Nodes)#
id,lat,lon,voltage,p_load,q_load
bus_001,47.0667,15.4333,380,100.0,30.0
bus_002,47.1234,15.5678,380,150.0,45.0
bus_003,48.2089,16.3726,220,200.0,60.0
lines.csv (AC Transmission Lines)#
from,to,x,r,p_max,length
bus_001,bus_002,0.0123,0.0045,500,125.5
bus_002,bus_004,0.0234,0.0089,400,98.2
transformers.csv#
from,to,x,r,s_nom,tap_ratio
bus_002,bus_003,0.05,0.01,400,1.0
converters.csv (Optional)#
id,bus,p_max
conv_1,bus_010,1000
conv_2,bus_020,1000
dc_links.csv (Optional)#
from,to,p_max,converter_from,converter_to
conv_1,conv_2,800,conv_1,conv_2
Simple Example: NetworkX Graph#
For simpler use cases without CSV files:
import npap
import networkx as nx
# Create a graph programmatically
G = nx.DiGraph()
# Add nodes with geographic coordinates
G.add_node("bus_1", lat=47.0, lon=15.0, load=100.0)
G.add_node("bus_2", lat=47.1, lon=15.1, load=150.0)
G.add_node("bus_3", lat=47.2, lon=15.0, load=200.0)
G.add_node("bus_4", lat=47.0, lon=15.2, load=120.0)
# Add edges with electrical properties
G.add_edge("bus_1", "bus_2", x=0.01, p_max=500)
G.add_edge("bus_2", "bus_3", x=0.02, p_max=400)
G.add_edge("bus_3", "bus_4", x=0.015, p_max=450)
G.add_edge("bus_4", "bus_1", x=0.018, p_max=300)
# Initialize manager and load
manager = npap.PartitionAggregatorManager()
manager.load_data("networkx_direct", graph=G)
# Partition
partition = manager.partition("geographical_kmeans", n_clusters=2)
# Aggregate
aggregated = manager.aggregate(mode=npap.AggregationMode.GEOGRAPHICAL)
# Visualize
manager.plot_network(style="voltage_aware")
Custom Aggregation Profile#
For more control over how properties are aggregated:
from npap import AggregationProfile
# Define custom aggregation rules
profile = AggregationProfile(
topology_strategy="simple",
node_properties={
"lat": "average", # Geographic center
"lon": "average",
"p_load": "sum", # Total load
"q_load": "sum",
"voltage": "average", # Average voltage
},
edge_properties={
"x": "equivalent_reactance", # Parallel impedance formula
"r": "equivalent_reactance",
"p_max": "sum", # Total capacity
"length": "sum", # Total length
},
default_node_strategy="sum",
default_edge_strategy="average"
)
aggregated = manager.aggregate(profile=profile)
Key Points#
Manager Pattern: All operations go through
PartitionAggregatorManagerStrategy Selection: Choose strategies based on your data and requirements
Voltage Awareness: Use
va_loaderandva_geographical_*orva_electrical_*strategies for multi-voltage networksAC Island Handling: NPAP automatically detects and respects AC island boundaries (if
ac_islandattribute is present)Parallel Edges: Aggregate them before partitioning
Next Steps#
Available Strategies - Explore all partitioning and aggregation options
Data Loading - Detailed guide on loading different data formats
Partitioning - Deep dive into clustering algorithms
Aggregation - Learn about the three-tier aggregation system