-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgrid_ops_server.py
More file actions
153 lines (129 loc) · 4.9 KB
/
grid_ops_server.py
File metadata and controls
153 lines (129 loc) · 4.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#The MCP server provides tools and resources for grid operations research. Let's create a file
#named grid_ops_server.py:
"""
grid_ops_server.py - MCP Server for Grid Operations Research
This server provides tools and resources for grid operations research.
This server provides tools and resources for analyzing grid outages, monitoring data,
accessing research papers, and generating visualizations.
"""
import json
import os
import pandas as pd
import matplotlib
import numpy as np
import io
import base64
from datetime import datetime
from typing import List, Dict, Any, Optional, Union
# Set matplotlib backend before importing pyplot
matplotlib.use('Agg')
import matplotlib.pyplot as plt
from mcp.server.fastmcp import FastMCP
# Initialize the MCP server
mcp = FastMCP("Grid Operations Assistant")
# ----- Resources -----
@mcp.resource("grid://topology/{region}")
def get_grid_topology(region: str) -> Dict[str, Any]:
"""Retrieve power grid topology for a specific region."""
topologies = {
"northeast": {
"voltage_levels": [345, 138, 69], # kV
"substations": 45,
"transmission_lines": 1200, # miles
"primary_generators": ["Nuclear", "Hydro", "Wind"]
},
"southwest": {
"voltage_levels": [500, 230, 115],
"substations": 32,
"transmission_lines": 950,
"primary_generators": ["Solar", "Natural Gas", "Coal"]
}
}
return topologies.get(region.lower(), {"error": f"Topology for {region} not found"})
@mcp.resource("grid://load/{dataset_id}")
def get_grid_load_data(dataset_id: str) -> Dict[str, Any]:
"""Retrieve grid load dataset by ID."""
datasets = {
"peak_load_2023": {
"name": "Regional Peak Load Analysis",
"source": "NERC",
"time_range": "2023",
"unit": "MW",
"data": {
"regions": ["Northeast", "Southeast", "Midwest", "West"],
"peak_loads": [65000, 72000, 58000, 48000]
}
},
"hourly_load": {
"name": "Hourly Load Profile",
"source": "ISO-NE",
"time_range": "2024-01-01 to 2024-01-07",
"unit": "MW",
"data": {
"hours": list(range(24)),
"load": [np.random.normal(15000, 2000) for _ in range(24)]
}
}
}
return datasets.get(dataset_id, {"error": f"Dataset {dataset_id} not found"})
# ----- Tools -----
@mcp.tool()
def analyze_load_pattern(dataset_id: str, window_hours: int = 24) -> Dict[str, Any]:
"""Analyze load patterns in grid data."""
data = get_grid_load_data(dataset_id)
if "error" in data:
return data
df = pd.DataFrame(data["data"])
df['load'] = df['load'].rolling(window=window_hours).mean()
return {
"dataset": data["name"],
"analysis_window": f"{window_hours}h",
"max_load": round(df['load'].max(), 2),
"min_load": round(df['load'].min(), 2),
"avg_load": round(df['load'].mean(), 2),
"trend": "stable" if df['load'].std() < 1000 else "volatile"
}
@mcp.tool()
def predict_outage_risk(equipment_id: str, weather_data: Dict[str, float]) -> Dict[str, Any]:
"""Predict outage risk for grid equipment based on weather conditions."""
# Simulated risk model
base_risk = 0.05
risk_factors = {
"temperature": 0.001 * abs(weather_data.get("temp_c", 25) - 25),
"wind_speed": 0.002 * weather_data.get("wind_kph", 0),
"precipitation": 0.003 * weather_data.get("precip_mm", 0)
}
total_risk = base_risk + sum(risk_factors.values())
return {
"equipment_id": equipment_id,
"risk_score": round(total_risk, 4),
"risk_category": "high" if total_risk > 0.1 else "medium" if total_risk > 0.05 else "low",
"factors": risk_factors
}
@mcp.tool()
def generate_grid_visualization(dataset_id: str) -> Dict[str, Any]:
"""Generate visualization of grid operational data."""
data = get_grid_load_data(dataset_id)
plt.figure(figsize=(10, 6))
if "hours" in data["data"]:
plt.plot(data["data"]["hours"], data["data"]["load"], 'b-', linewidth=2)
plt.title("Hourly Load Profile")
plt.xlabel("Hour of Day")
plt.ylabel("Load (MW)")
elif "regions" in data["data"]:
plt.bar(data["data"]["regions"], data["data"]["peak_loads"])
plt.title("Regional Peak Loads")
plt.ylabel("Peak Load (MW)")
plt.grid(True, linestyle='--', alpha=0.7)
buf = io.BytesIO()
plt.savefig(buf, format='png', dpi=100)
buf.seek(0)
plt.close()
return {
"visualization": f"data:image/png;base64,{base64.b64encode(buf.read()).decode('utf-8')}",
"dataset": data["name"]
}
# ----- Server Execution -----
if __name__ == "__main__":
print("GridOperationsServer:STARTED", flush=True)
mcp.run(transport='stdio')