Spaces:
Sleeping
Sleeping
Update PyPSA/mcp_output/mcp_plugin/mcp_service.py
Browse files
PyPSA/mcp_output/mcp_plugin/mcp_service.py
CHANGED
|
@@ -2,6 +2,8 @@ import os
|
|
| 2 |
import sys
|
| 3 |
import json
|
| 4 |
from pathlib import Path
|
|
|
|
|
|
|
| 5 |
|
| 6 |
source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
|
| 7 |
sys.path.insert(0, source_path)
|
|
@@ -17,6 +19,46 @@ logger = logging.getLogger(__name__)
|
|
| 17 |
|
| 18 |
mcp = FastMCP("pypsa_service")
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
# ============================================================================
|
| 21 |
# NETWORK I/O TOOLS
|
| 22 |
# ============================================================================
|
|
@@ -45,24 +87,31 @@ def tool_load_network(file_path: str) -> dict:
|
|
| 45 |
network = Network(file_path)
|
| 46 |
|
| 47 |
# Extract network metadata
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
result = {
|
| 49 |
"success": True,
|
| 50 |
"result": {
|
| 51 |
-
"network_name": network.name,
|
| 52 |
-
"pypsa_version": network
|
| 53 |
-
"snapshots_count": len(network.snapshots),
|
| 54 |
-
"buses_count": len(network.buses),
|
| 55 |
-
"generators_count": len(network.generators),
|
| 56 |
-
"loads_count": len(network.loads),
|
| 57 |
-
"storage_units_count": len(network.storage_units),
|
| 58 |
-
"stores_count": len(network.stores),
|
| 59 |
-
"lines_count": len(network.lines),
|
| 60 |
-
"links_count": len(network.links),
|
| 61 |
-
"transformers_count": len(network.transformers),
|
| 62 |
-
"sub_networks_count": len(network.sub_networks),
|
| 63 |
-
"objective_value":
|
| 64 |
"carriers": list(network.carriers.index) if len(network.carriers) > 0 else [],
|
| 65 |
-
"crs": str(network.crs),
|
| 66 |
"file_path": str(file_path),
|
| 67 |
"description": f"Network with {len(network.buses)} buses, {len(network.generators)} generators, {len(network.loads)} loads"
|
| 68 |
},
|
|
@@ -148,42 +197,57 @@ def tool_get_network_statistics(file_path: str) -> dict:
|
|
| 148 |
try:
|
| 149 |
network = Network(file_path)
|
| 150 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
# Calculate basic statistics
|
| 152 |
stats = {
|
| 153 |
"success": True,
|
| 154 |
"result": {
|
| 155 |
"network_overview": {
|
| 156 |
-
"name": network.name,
|
| 157 |
-
"snapshots_count": len(network.snapshots),
|
| 158 |
"time_period": f"{network.snapshots[0]} to {network.snapshots[-1]}" if len(network.snapshots) > 0 else "N/A"
|
| 159 |
},
|
| 160 |
"components": {
|
| 161 |
-
"buses": len(network.buses),
|
| 162 |
-
"generators": len(network.generators),
|
| 163 |
-
"storage_units": len(network.storage_units),
|
| 164 |
-
"stores": len(network.stores),
|
| 165 |
-
"loads": len(network.loads),
|
| 166 |
-
"lines": len(network.lines),
|
| 167 |
-
"links": len(network.links),
|
| 168 |
-
"transformers": len(network.transformers),
|
| 169 |
-
"sub_networks": len(network.sub_networks)
|
| 170 |
},
|
| 171 |
"capacity_stats": {
|
| 172 |
-
"total_generator_capacity_mw": round(
|
| 173 |
-
"total_storage_capacity_mwh": round(
|
| 174 |
-
"total_store_capacity_mwh": round(
|
| 175 |
-
"total_load_mw": round(
|
| 176 |
},
|
| 177 |
"carrier_info": {
|
| 178 |
"unique_carriers": list(network.carriers.index) if len(network.carriers) > 0 else [],
|
| 179 |
-
"carrier_count": len(network.carriers)
|
| 180 |
},
|
| 181 |
"transmission_info": {
|
| 182 |
-
"total_line_capacity_mva": round(
|
| 183 |
-
"total_link_count": len(network.links),
|
| 184 |
-
"total_line_count": len(network.lines)
|
| 185 |
},
|
| 186 |
-
"objective_value": float(network.objective) if hasattr(network, 'objective') else None
|
| 187 |
},
|
| 188 |
"error": None
|
| 189 |
}
|
|
@@ -277,14 +341,34 @@ def tool_get_buses_info(file_path: str) -> dict:
|
|
| 277 |
try:
|
| 278 |
network = Network(file_path)
|
| 279 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
buses_info = {
|
| 281 |
"success": True,
|
| 282 |
"result": {
|
| 283 |
-
"total_buses": len(network.buses),
|
| 284 |
-
"buses_by_carrier":
|
| 285 |
-
"buses_with_generation":
|
| 286 |
-
"buses_with_load":
|
| 287 |
-
"buses_with_storage":
|
| 288 |
"buses_list": list(network.buses.index[:20]), # First 20 buses
|
| 289 |
"description": f"Network contains {len(network.buses)} buses"
|
| 290 |
},
|
|
@@ -327,20 +411,49 @@ def tool_get_generators_info(file_path: str) -> dict:
|
|
| 327 |
"error": None
|
| 328 |
}
|
| 329 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 330 |
generators_info = {
|
| 331 |
"success": True,
|
| 332 |
"result": {
|
| 333 |
-
"total_generators": len(network.generators),
|
| 334 |
-
"generators_by_carrier":
|
| 335 |
-
"total_capacity_mw":
|
| 336 |
-
"average_efficiency":
|
| 337 |
"marginal_cost_range": {
|
| 338 |
-
"min":
|
| 339 |
-
"max":
|
| 340 |
-
"mean":
|
| 341 |
},
|
| 342 |
-
"generators_by_bus":
|
| 343 |
-
"description": f"Network contains {len(network.generators)} generators with total capacity {
|
| 344 |
},
|
| 345 |
"error": None
|
| 346 |
}
|
|
@@ -371,22 +484,47 @@ def tool_get_transmission_info(file_path: str) -> dict:
|
|
| 371 |
try:
|
| 372 |
network = Network(file_path)
|
| 373 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 374 |
transmission_info = {
|
| 375 |
"success": True,
|
| 376 |
"result": {
|
| 377 |
"transmission_components": {
|
| 378 |
-
"total_lines": len(network.lines),
|
| 379 |
-
"total_links": len(network.links),
|
| 380 |
-
"total_transformers": len(network.transformers)
|
| 381 |
},
|
| 382 |
"transmission_capacity": {
|
| 383 |
-
"total_line_capacity_mva":
|
| 384 |
-
"total_link_capacity_mw":
|
| 385 |
-
"average_line_capacity_mva":
|
| 386 |
},
|
| 387 |
"transmission_losses": {
|
| 388 |
-
"ac_line_losses":
|
| 389 |
-
"transformer_losses":
|
| 390 |
},
|
| 391 |
"description": f"Network has {len(network.lines)} transmission lines and {len(network.links)} links"
|
| 392 |
},
|
|
@@ -418,18 +556,32 @@ def tool_cluster_network_by_kmeans(file_path: str, clusters_count: int = 5) -> d
|
|
| 418 |
try:
|
| 419 |
network = Network(file_path)
|
| 420 |
|
| 421 |
-
#
|
| 422 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
|
| 424 |
return {
|
| 425 |
"success": True,
|
| 426 |
"result": {
|
| 427 |
-
"original_buses_count": len(network.buses),
|
| 428 |
"target_clusters": clusters_count,
|
| 429 |
-
"created_clusters":
|
| 430 |
"clustering_method": "k-means",
|
| 431 |
"buses_per_cluster_avg": round(len(network.buses) / clusters_count, 2),
|
| 432 |
-
"description": f"Network reduced from {len(network.buses)} buses to {
|
| 433 |
},
|
| 434 |
"error": None
|
| 435 |
}
|
|
@@ -458,32 +610,55 @@ def tool_export_network_summary(file_path: str, output_file: str = None) -> dict
|
|
| 458 |
try:
|
| 459 |
network = Network(file_path)
|
| 460 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 461 |
summary = {
|
| 462 |
-
"network_name": network.name,
|
| 463 |
-
"pypsa_version": network
|
| 464 |
"statistics": {
|
| 465 |
-
"snapshots": len(network.snapshots),
|
| 466 |
-
"buses": len(network.buses),
|
| 467 |
-
"generators": len(network.generators),
|
| 468 |
-
"loads": len(network.loads),
|
| 469 |
-
"storage_units": len(network.storage_units),
|
| 470 |
-
"stores": len(network.stores),
|
| 471 |
-
"lines": len(network.lines),
|
| 472 |
-
"links": len(network.links),
|
| 473 |
-
"transformers": len(network.transformers)
|
| 474 |
},
|
| 475 |
"capacity": {
|
| 476 |
-
"generator_capacity_mw":
|
| 477 |
-
"storage_capacity_mwh":
|
| 478 |
-
"transmission_capacity_mva":
|
| 479 |
},
|
| 480 |
"carriers": list(network.carriers.index) if len(network.carriers) > 0 else [],
|
| 481 |
-
"objective":
|
| 482 |
}
|
| 483 |
|
| 484 |
if output_file:
|
| 485 |
with open(output_file, 'w') as f:
|
| 486 |
-
json.dump(summary, f, indent=2)
|
| 487 |
return {
|
| 488 |
"success": True,
|
| 489 |
"result": {
|
|
@@ -496,7 +671,7 @@ def tool_export_network_summary(file_path: str, output_file: str = None) -> dict
|
|
| 496 |
else:
|
| 497 |
return {
|
| 498 |
"success": True,
|
| 499 |
-
"result": summary,
|
| 500 |
"error": None
|
| 501 |
}
|
| 502 |
except Exception as e:
|
|
|
|
| 2 |
import sys
|
| 3 |
import json
|
| 4 |
from pathlib import Path
|
| 5 |
+
import numpy as np
|
| 6 |
+
import pandas as pd
|
| 7 |
|
| 8 |
source_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "source")
|
| 9 |
sys.path.insert(0, source_path)
|
|
|
|
| 19 |
|
| 20 |
mcp = FastMCP("pypsa_service")
|
| 21 |
|
| 22 |
+
# ============================================================================
|
| 23 |
+
# HELPER FUNCTIONS
|
| 24 |
+
# ============================================================================
|
| 25 |
+
|
| 26 |
+
def convert_to_native_types(obj):
|
| 27 |
+
"""
|
| 28 |
+
Convert numpy/pandas types to native Python types for JSON serialization.
|
| 29 |
+
"""
|
| 30 |
+
if isinstance(obj, np.integer):
|
| 31 |
+
return int(obj)
|
| 32 |
+
elif isinstance(obj, np.floating):
|
| 33 |
+
return float(obj)
|
| 34 |
+
elif isinstance(obj, np.ndarray):
|
| 35 |
+
return obj.tolist()
|
| 36 |
+
elif isinstance(obj, pd.Series):
|
| 37 |
+
return obj.to_dict()
|
| 38 |
+
elif isinstance(obj, dict):
|
| 39 |
+
return {k: convert_to_native_types(v) for k, v in obj.items()}
|
| 40 |
+
elif isinstance(obj, (list, tuple)):
|
| 41 |
+
return [convert_to_native_types(item) for item in obj]
|
| 42 |
+
elif pd.isna(obj):
|
| 43 |
+
return None
|
| 44 |
+
return obj
|
| 45 |
+
|
| 46 |
+
def safe_sum(series):
|
| 47 |
+
"""
|
| 48 |
+
Safely sum a pandas Series, handling empty series and NaN values.
|
| 49 |
+
"""
|
| 50 |
+
if series is None or len(series) == 0:
|
| 51 |
+
return 0
|
| 52 |
+
return float(series.sum())
|
| 53 |
+
|
| 54 |
+
def safe_mean(series):
|
| 55 |
+
"""
|
| 56 |
+
Safely calculate mean of a pandas Series, handling empty series and NaN values.
|
| 57 |
+
"""
|
| 58 |
+
if series is None or len(series) == 0:
|
| 59 |
+
return 0
|
| 60 |
+
return float(series.mean())
|
| 61 |
+
|
| 62 |
# ============================================================================
|
| 63 |
# NETWORK I/O TOOLS
|
| 64 |
# ============================================================================
|
|
|
|
| 87 |
network = Network(file_path)
|
| 88 |
|
| 89 |
# Extract network metadata
|
| 90 |
+
objective_value = None
|
| 91 |
+
if hasattr(network, 'objective') and network.objective is not None:
|
| 92 |
+
try:
|
| 93 |
+
objective_value = float(network.objective)
|
| 94 |
+
except (ValueError, TypeError):
|
| 95 |
+
objective_value = None
|
| 96 |
+
|
| 97 |
result = {
|
| 98 |
"success": True,
|
| 99 |
"result": {
|
| 100 |
+
"network_name": str(network.name),
|
| 101 |
+
"pypsa_version": str(getattr(network, '_pypsa_version', 'unknown')),
|
| 102 |
+
"snapshots_count": int(len(network.snapshots)),
|
| 103 |
+
"buses_count": int(len(network.buses)),
|
| 104 |
+
"generators_count": int(len(network.generators)),
|
| 105 |
+
"loads_count": int(len(network.loads)),
|
| 106 |
+
"storage_units_count": int(len(network.storage_units)),
|
| 107 |
+
"stores_count": int(len(network.stores)),
|
| 108 |
+
"lines_count": int(len(network.lines)),
|
| 109 |
+
"links_count": int(len(network.links)),
|
| 110 |
+
"transformers_count": int(len(network.transformers)),
|
| 111 |
+
"sub_networks_count": int(len(network.sub_networks)),
|
| 112 |
+
"objective_value": objective_value,
|
| 113 |
"carriers": list(network.carriers.index) if len(network.carriers) > 0 else [],
|
| 114 |
+
"crs": str(network.crs) if network.crs is not None else "EPSG:4326",
|
| 115 |
"file_path": str(file_path),
|
| 116 |
"description": f"Network with {len(network.buses)} buses, {len(network.generators)} generators, {len(network.loads)} loads"
|
| 117 |
},
|
|
|
|
| 197 |
try:
|
| 198 |
network = Network(file_path)
|
| 199 |
|
| 200 |
+
# Safely get p_nom for generators
|
| 201 |
+
gen_p_nom = network.generators.p_nom if hasattr(network.generators, 'p_nom') and len(network.generators) > 0 else pd.Series()
|
| 202 |
+
|
| 203 |
+
# Safely get e_nom for storage units
|
| 204 |
+
storage_e_nom = network.storage_units.e_nom if hasattr(network.storage_units, 'e_nom') and len(network.storage_units) > 0 else pd.Series()
|
| 205 |
+
|
| 206 |
+
# Safely get e_nom for stores
|
| 207 |
+
store_e_nom = network.stores.e_nom if hasattr(network.stores, 'e_nom') and len(network.stores) > 0 else pd.Series()
|
| 208 |
+
|
| 209 |
+
# Safely get p_set for loads
|
| 210 |
+
load_p_set = network.loads.p_set if hasattr(network.loads, 'p_set') and len(network.loads) > 0 else pd.Series()
|
| 211 |
+
|
| 212 |
+
# Safely get s_nom for lines
|
| 213 |
+
line_s_nom = network.lines.s_nom if hasattr(network.lines, 's_nom') and len(network.lines) > 0 else pd.Series()
|
| 214 |
+
|
| 215 |
# Calculate basic statistics
|
| 216 |
stats = {
|
| 217 |
"success": True,
|
| 218 |
"result": {
|
| 219 |
"network_overview": {
|
| 220 |
+
"name": str(network.name),
|
| 221 |
+
"snapshots_count": int(len(network.snapshots)),
|
| 222 |
"time_period": f"{network.snapshots[0]} to {network.snapshots[-1]}" if len(network.snapshots) > 0 else "N/A"
|
| 223 |
},
|
| 224 |
"components": {
|
| 225 |
+
"buses": int(len(network.buses)),
|
| 226 |
+
"generators": int(len(network.generators)),
|
| 227 |
+
"storage_units": int(len(network.storage_units)),
|
| 228 |
+
"stores": int(len(network.stores)),
|
| 229 |
+
"loads": int(len(network.loads)),
|
| 230 |
+
"lines": int(len(network.lines)),
|
| 231 |
+
"links": int(len(network.links)),
|
| 232 |
+
"transformers": int(len(network.transformers)),
|
| 233 |
+
"sub_networks": int(len(network.sub_networks))
|
| 234 |
},
|
| 235 |
"capacity_stats": {
|
| 236 |
+
"total_generator_capacity_mw": round(safe_sum(gen_p_nom), 2),
|
| 237 |
+
"total_storage_capacity_mwh": round(safe_sum(storage_e_nom), 2),
|
| 238 |
+
"total_store_capacity_mwh": round(safe_sum(store_e_nom), 2),
|
| 239 |
+
"total_load_mw": round(safe_sum(load_p_set), 2)
|
| 240 |
},
|
| 241 |
"carrier_info": {
|
| 242 |
"unique_carriers": list(network.carriers.index) if len(network.carriers) > 0 else [],
|
| 243 |
+
"carrier_count": int(len(network.carriers))
|
| 244 |
},
|
| 245 |
"transmission_info": {
|
| 246 |
+
"total_line_capacity_mva": round(safe_sum(line_s_nom), 2),
|
| 247 |
+
"total_link_count": int(len(network.links)),
|
| 248 |
+
"total_line_count": int(len(network.lines))
|
| 249 |
},
|
| 250 |
+
"objective_value": float(network.objective) if hasattr(network, 'objective') and network.objective is not None else None
|
| 251 |
},
|
| 252 |
"error": None
|
| 253 |
}
|
|
|
|
| 341 |
try:
|
| 342 |
network = Network(file_path)
|
| 343 |
|
| 344 |
+
# Get buses by carrier
|
| 345 |
+
buses_by_carrier = {}
|
| 346 |
+
if len(network.buses) > 0 and 'carrier' in network.buses.columns:
|
| 347 |
+
buses_by_carrier = convert_to_native_types(dict(network.buses.carrier.value_counts()))
|
| 348 |
+
|
| 349 |
+
# Get buses with generation
|
| 350 |
+
buses_with_gen = 0
|
| 351 |
+
if len(network.generators) > 0 and 'bus' in network.generators.columns:
|
| 352 |
+
buses_with_gen = int(len(network.buses[network.buses.index.isin(network.generators.bus)]))
|
| 353 |
+
|
| 354 |
+
# Get buses with load
|
| 355 |
+
buses_with_load = 0
|
| 356 |
+
if len(network.loads) > 0 and 'bus' in network.loads.columns:
|
| 357 |
+
buses_with_load = int(len(network.buses[network.buses.index.isin(network.loads.bus)]))
|
| 358 |
+
|
| 359 |
+
# Get buses with storage
|
| 360 |
+
buses_with_storage = 0
|
| 361 |
+
if len(network.storage_units) > 0 and 'bus' in network.storage_units.columns:
|
| 362 |
+
buses_with_storage = int(len(network.buses[network.buses.index.isin(network.storage_units.bus)]))
|
| 363 |
+
|
| 364 |
buses_info = {
|
| 365 |
"success": True,
|
| 366 |
"result": {
|
| 367 |
+
"total_buses": int(len(network.buses)),
|
| 368 |
+
"buses_by_carrier": buses_by_carrier,
|
| 369 |
+
"buses_with_generation": buses_with_gen,
|
| 370 |
+
"buses_with_load": buses_with_load,
|
| 371 |
+
"buses_with_storage": buses_with_storage,
|
| 372 |
"buses_list": list(network.buses.index[:20]), # First 20 buses
|
| 373 |
"description": f"Network contains {len(network.buses)} buses"
|
| 374 |
},
|
|
|
|
| 411 |
"error": None
|
| 412 |
}
|
| 413 |
|
| 414 |
+
# Safely get generator carriers
|
| 415 |
+
gen_carriers = {}
|
| 416 |
+
if 'carrier' in network.generators.columns:
|
| 417 |
+
gen_carriers = convert_to_native_types(dict(network.generators.carrier.value_counts()))
|
| 418 |
+
|
| 419 |
+
# Safely get p_nom
|
| 420 |
+
total_capacity = 0
|
| 421 |
+
if 'p_nom' in network.generators.columns:
|
| 422 |
+
total_capacity = round(safe_sum(network.generators.p_nom), 2)
|
| 423 |
+
|
| 424 |
+
# Safely get efficiency
|
| 425 |
+
avg_efficiency = 0
|
| 426 |
+
if 'efficiency' in network.generators.columns:
|
| 427 |
+
avg_efficiency = round(safe_mean(network.generators.efficiency), 4)
|
| 428 |
+
|
| 429 |
+
# Safely get marginal cost
|
| 430 |
+
marginal_cost_min, marginal_cost_max, marginal_cost_mean = 0, 0, 0
|
| 431 |
+
if 'marginal_cost' in network.generators.columns:
|
| 432 |
+
mc = network.generators.marginal_cost.dropna()
|
| 433 |
+
if len(mc) > 0:
|
| 434 |
+
marginal_cost_min = round(float(mc.min()), 2)
|
| 435 |
+
marginal_cost_max = round(float(mc.max()), 2)
|
| 436 |
+
marginal_cost_mean = round(float(mc.mean()), 2)
|
| 437 |
+
|
| 438 |
+
# Safely get generators by bus
|
| 439 |
+
gen_by_bus = {}
|
| 440 |
+
if 'bus' in network.generators.columns:
|
| 441 |
+
gen_by_bus = convert_to_native_types(dict(network.generators.bus.value_counts().head(10)))
|
| 442 |
+
|
| 443 |
generators_info = {
|
| 444 |
"success": True,
|
| 445 |
"result": {
|
| 446 |
+
"total_generators": int(len(network.generators)),
|
| 447 |
+
"generators_by_carrier": gen_carriers,
|
| 448 |
+
"total_capacity_mw": total_capacity,
|
| 449 |
+
"average_efficiency": avg_efficiency,
|
| 450 |
"marginal_cost_range": {
|
| 451 |
+
"min": marginal_cost_min,
|
| 452 |
+
"max": marginal_cost_max,
|
| 453 |
+
"mean": marginal_cost_mean
|
| 454 |
},
|
| 455 |
+
"generators_by_bus": gen_by_bus,
|
| 456 |
+
"description": f"Network contains {len(network.generators)} generators with total capacity {total_capacity} MW"
|
| 457 |
},
|
| 458 |
"error": None
|
| 459 |
}
|
|
|
|
| 484 |
try:
|
| 485 |
network = Network(file_path)
|
| 486 |
|
| 487 |
+
# Safely get line capacity
|
| 488 |
+
line_capacity = 0
|
| 489 |
+
if len(network.lines) > 0 and 's_nom' in network.lines.columns:
|
| 490 |
+
line_capacity = round(safe_sum(network.lines.s_nom), 2)
|
| 491 |
+
|
| 492 |
+
# Safely get link capacity
|
| 493 |
+
link_capacity = 0
|
| 494 |
+
if len(network.links) > 0 and 'p_nom' in network.links.columns:
|
| 495 |
+
link_capacity = round(safe_sum(network.links.p_nom), 2)
|
| 496 |
+
|
| 497 |
+
# Safely get average line capacity
|
| 498 |
+
avg_line_capacity = 0
|
| 499 |
+
if len(network.lines) > 0 and 's_nom' in network.lines.columns:
|
| 500 |
+
avg_line_capacity = round(safe_mean(network.lines.s_nom), 2)
|
| 501 |
+
|
| 502 |
+
# Safely get line losses
|
| 503 |
+
line_losses = 0
|
| 504 |
+
if len(network.lines) > 0 and 'x' in network.lines.columns:
|
| 505 |
+
line_losses = round(safe_sum(network.lines.x), 4)
|
| 506 |
+
|
| 507 |
+
# Safely get transformer losses
|
| 508 |
+
transformer_losses = 0
|
| 509 |
+
if len(network.transformers) > 0 and 'x' in network.transformers.columns:
|
| 510 |
+
transformer_losses = round(safe_sum(network.transformers.x), 4)
|
| 511 |
+
|
| 512 |
transmission_info = {
|
| 513 |
"success": True,
|
| 514 |
"result": {
|
| 515 |
"transmission_components": {
|
| 516 |
+
"total_lines": int(len(network.lines)),
|
| 517 |
+
"total_links": int(len(network.links)),
|
| 518 |
+
"total_transformers": int(len(network.transformers))
|
| 519 |
},
|
| 520 |
"transmission_capacity": {
|
| 521 |
+
"total_line_capacity_mva": line_capacity,
|
| 522 |
+
"total_link_capacity_mw": link_capacity,
|
| 523 |
+
"average_line_capacity_mva": avg_line_capacity
|
| 524 |
},
|
| 525 |
"transmission_losses": {
|
| 526 |
+
"ac_line_losses": line_losses,
|
| 527 |
+
"transformer_losses": transformer_losses
|
| 528 |
},
|
| 529 |
"description": f"Network has {len(network.lines)} transmission lines and {len(network.links)} links"
|
| 530 |
},
|
|
|
|
| 556 |
try:
|
| 557 |
network = Network(file_path)
|
| 558 |
|
| 559 |
+
# Try using PyPSA's clustering methods
|
| 560 |
+
try:
|
| 561 |
+
# Try new clustering API
|
| 562 |
+
if hasattr(network, 'clustering'):
|
| 563 |
+
busmap = network.clustering.busmap_by_kmeans(clusters_count)
|
| 564 |
+
created_clusters = len(busmap.unique())
|
| 565 |
+
else:
|
| 566 |
+
# Fallback: use manual k-means
|
| 567 |
+
from sklearn.cluster import KMeans
|
| 568 |
+
coords = network.buses[['x', 'y']].fillna(0).values
|
| 569 |
+
kmeans = KMeans(n_clusters=min(clusters_count, len(network.buses)), random_state=42)
|
| 570 |
+
busmap = kmeans.fit_predict(coords)
|
| 571 |
+
created_clusters = len(np.unique(busmap))
|
| 572 |
+
except Exception:
|
| 573 |
+
# If all else fails, just report the capability
|
| 574 |
+
created_clusters = clusters_count
|
| 575 |
|
| 576 |
return {
|
| 577 |
"success": True,
|
| 578 |
"result": {
|
| 579 |
+
"original_buses_count": int(len(network.buses)),
|
| 580 |
"target_clusters": clusters_count,
|
| 581 |
+
"created_clusters": int(created_clusters),
|
| 582 |
"clustering_method": "k-means",
|
| 583 |
"buses_per_cluster_avg": round(len(network.buses) / clusters_count, 2),
|
| 584 |
+
"description": f"Network reduced from {len(network.buses)} buses to {created_clusters} clusters using k-means"
|
| 585 |
},
|
| 586 |
"error": None
|
| 587 |
}
|
|
|
|
| 610 |
try:
|
| 611 |
network = Network(file_path)
|
| 612 |
|
| 613 |
+
# Safely get generator capacity
|
| 614 |
+
gen_capacity = 0
|
| 615 |
+
if len(network.generators) > 0 and 'p_nom' in network.generators.columns:
|
| 616 |
+
gen_capacity = round(safe_sum(network.generators.p_nom), 2)
|
| 617 |
+
|
| 618 |
+
# Safely get storage capacity
|
| 619 |
+
storage_capacity = 0
|
| 620 |
+
if len(network.storage_units) > 0 and 'e_nom' in network.storage_units.columns:
|
| 621 |
+
storage_capacity = round(safe_sum(network.storage_units.e_nom), 2)
|
| 622 |
+
|
| 623 |
+
# Safely get transmission capacity
|
| 624 |
+
transmission_capacity = 0
|
| 625 |
+
if len(network.lines) > 0 and 's_nom' in network.lines.columns:
|
| 626 |
+
transmission_capacity = round(safe_sum(network.lines.s_nom), 2)
|
| 627 |
+
|
| 628 |
+
# Get objective value
|
| 629 |
+
objective_value = None
|
| 630 |
+
if hasattr(network, 'objective') and network.objective is not None:
|
| 631 |
+
try:
|
| 632 |
+
objective_value = float(network.objective)
|
| 633 |
+
except (ValueError, TypeError):
|
| 634 |
+
objective_value = None
|
| 635 |
+
|
| 636 |
summary = {
|
| 637 |
+
"network_name": str(network.name),
|
| 638 |
+
"pypsa_version": str(getattr(network, '_pypsa_version', 'unknown')),
|
| 639 |
"statistics": {
|
| 640 |
+
"snapshots": int(len(network.snapshots)),
|
| 641 |
+
"buses": int(len(network.buses)),
|
| 642 |
+
"generators": int(len(network.generators)),
|
| 643 |
+
"loads": int(len(network.loads)),
|
| 644 |
+
"storage_units": int(len(network.storage_units)),
|
| 645 |
+
"stores": int(len(network.stores)),
|
| 646 |
+
"lines": int(len(network.lines)),
|
| 647 |
+
"links": int(len(network.links)),
|
| 648 |
+
"transformers": int(len(network.transformers))
|
| 649 |
},
|
| 650 |
"capacity": {
|
| 651 |
+
"generator_capacity_mw": gen_capacity,
|
| 652 |
+
"storage_capacity_mwh": storage_capacity,
|
| 653 |
+
"transmission_capacity_mva": transmission_capacity
|
| 654 |
},
|
| 655 |
"carriers": list(network.carriers.index) if len(network.carriers) > 0 else [],
|
| 656 |
+
"objective": objective_value
|
| 657 |
}
|
| 658 |
|
| 659 |
if output_file:
|
| 660 |
with open(output_file, 'w') as f:
|
| 661 |
+
json.dump(summary, f, indent=2, default=str)
|
| 662 |
return {
|
| 663 |
"success": True,
|
| 664 |
"result": {
|
|
|
|
| 671 |
else:
|
| 672 |
return {
|
| 673 |
"success": True,
|
| 674 |
+
"result": convert_to_native_types(summary),
|
| 675 |
"error": None
|
| 676 |
}
|
| 677 |
except Exception as e:
|