caspian123 commited on
Commit
0fcbf54
·
verified ·
1 Parent(s): 77d42c1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +37 -35
app.py CHANGED
@@ -1,11 +1,3 @@
1
- # app.py
2
- # This is a self-contained Python script implementing a simplified version of PowerGrid Guardian.
3
- # It includes data ingestion (with mocks), data fusion, PyPSA simulation wrapper, LLM orchestration (with mock),
4
- # FastAPI backend (embedded), Gradio frontend, and basic tests (runnable via pytest if saved separately).
5
- # For Hugging Face Spaces deployment, this file can be used directly as the entrypoint.
6
- # Note: This is a demo system for decision-support only. Not for automatic grid control.
7
- # Updated to use Qwen model from Hugging Face, and incorporated provided API keys for services.
8
-
9
  import os
10
  import json
11
  import datetime
@@ -13,8 +5,6 @@ import random
13
  import asyncio
14
  import logging
15
  from typing import List, Dict, Any
16
-
17
- # Dependencies (ensure these are in requirements.txt for HF Spaces)
18
  import pandas as pd
19
  import numpy as np
20
  import pypsa
@@ -24,6 +14,7 @@ from pydantic import BaseModel
24
  import uvicorn
25
  import httpx
26
  from dotenv import load_dotenv
 
27
 
28
  # Load environment variables
29
  load_dotenv()
@@ -35,7 +26,7 @@ logger = logging.getLogger(__name__)
35
  # === CONFIGURATION ===
36
  DEMO_MODE = True # Use mock data if True
37
  HF_API_KEY = os.getenv("HF_API_KEY")
38
- # Provided API keys
39
  GRIDSTATUS_API_KEY = os.getenv("GRIDSTATUS_API_KEY", "e9f1146bc3d242d19f7d64cf00f9fdd3")
40
  WEATHERAPI_KEY = os.getenv("WEATHERAPI_KEY", "6325087c017744b0b0d13950252307")
41
  EIA_API_KEY = os.getenv("EIA_API_KEY", "l0LsSOevbx1XqUMSEdEP7qVwDQXoeC3bFw8LPdGZ")
@@ -225,10 +216,13 @@ async def fetch_events(params: Dict) -> List[Event]:
225
  ))
226
  return events
227
 
228
- async def load_topology() -> Dict:
229
- # Load from examples/toy_topology.json or mock
230
  topology = {
231
- "nodes": [Node(node_id="node1", name="Substation A", type="substation", lat=29.76, lon=-95.37, voltage_kv=138, capacity_mw=500, connected_edges=["edge1"]).dict()],
 
 
 
232
  "edges": [Edge(edge_id="edge1", from_node="node1", to_node="node2", r_ohm_per_km=0.1, x_ohm_per_km=0.2, length_km=12.3, thermal_limit_mw=300).dict()]
233
  }
234
  return topology
@@ -292,7 +286,7 @@ def run_n_minus_1(net: pypsa.Network) -> List[Dict]:
292
  # === LLM ORCHESTRATOR ===
293
  class LLMOrchestrator:
294
  async def build_context(self, node_id: str, timestamp: str) -> Dict:
295
- topology = await load_topology()
296
  timeseries = await fetch_grid_data({"respondent": "CISO", "start": (datetime.datetime.fromisoformat(timestamp) - datetime.timedelta(hours=24)).strftime("%Y-%m-%dT%H")})
297
  weather = await fetch_weather({"latitude": topology["nodes"][0]["lat"], "longitude": topology["nodes"][0]["lon"]})
298
  events = await fetch_events({"location": "Houston"})
@@ -347,6 +341,30 @@ class LLMOrchestrator:
347
  except:
348
  return False
349
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
350
  # === FASTAPI BACKEND ===
351
  app = FastAPI()
352
 
@@ -380,9 +398,8 @@ with gr.Blocks() as demo:
380
  gr.Markdown("# PowerGrid Guardian Demo")
381
  with gr.Row():
382
  with gr.Column(scale=2):
383
- gr.Markdown("## Map Panel (Mock)")
384
- # In real: Use Plotly for map, but for simplicity, text
385
- map_output = gr.Textbox(value="Node1 (lat:29.76, lon:-95.37) - Risk: Red", label="Map")
386
  with gr.Column(scale=1):
387
  gr.Markdown("## Alert Timeline")
388
  alerts = gr.JSON(value=get_alerts(), label="Alerts")
@@ -394,6 +411,7 @@ with gr.Blocks() as demo:
394
 
395
  rec_button.click(fn=lambda node: asyncio.run(get_recommendation(node)), inputs=node_input, outputs=rec_output)
396
  sim_button.click(fn=run_simulation, outputs=sim_output)
 
397
 
398
  # === TESTS (Pytest compatible - can be extracted to tests/ files) ===
399
  def test_fusion():
@@ -424,20 +442,4 @@ if __name__ == "__main__":
424
  uvicorn.run(app, host="0.0.0.0", port=8000)
425
  threading.Thread(target=run_api, daemon=True).start()
426
  # Launch Gradio
427
- demo.launch(server_name="0.0.0.0", server_port=7860)
428
-
429
- # For HF Spaces, the demo.launch() will be the entrypoint.
430
- # To run tests: python -m pytest app.py (if pytest installed)
431
- # requirements.txt example:
432
- # fastapi==0.104.1
433
- # uvicorn==0.24.0
434
- # gradio==4.1.2
435
- # pypsa==0.26.0
436
- # pandas==2.1.3
437
- # numpy==1.26.2
438
- # httpx==0.25.2
439
- # python-dotenv==1.0.0
440
- # To create toy files (if needed):
441
- # toy_topology.json: {"nodes": [{"node_id": "node1", "name": "A", "type": "substation", "lat": 29.76, "lon": -95.37, "voltage_kv": 138, "capacity_mw": 500, "connected_edges": ["edge1"]}], "edges": [{"edge_id": "edge1", "from_node": "node1", "to_node": "node2", "r_ohm_per_km": 0.1, "x_ohm_per_km": 0.2, "length_km": 12.3, "thermal_limit_mw": 300}]}
442
- # toy_timeseries.csv: node_id,timestamp,measured_load_mw,measured_generation_mw,forecast_load_mw,reserve_margin_percent
443
- # node1,2023-01-01T00:00:00,100,120,110,10
 
 
 
 
 
 
 
 
 
1
  import os
2
  import json
3
  import datetime
 
5
  import asyncio
6
  import logging
7
  from typing import List, Dict, Any
 
 
8
  import pandas as pd
9
  import numpy as np
10
  import pypsa
 
14
  import uvicorn
15
  import httpx
16
  from dotenv import load_dotenv
17
+ import plotly.express as px
18
 
19
  # Load environment variables
20
  load_dotenv()
 
26
  # === CONFIGURATION ===
27
  DEMO_MODE = True # Use mock data if True
28
  HF_API_KEY = os.getenv("HF_API_KEY")
29
+ # API keys
30
  GRIDSTATUS_API_KEY = os.getenv("GRIDSTATUS_API_KEY", "e9f1146bc3d242d19f7d64cf00f9fdd3")
31
  WEATHERAPI_KEY = os.getenv("WEATHERAPI_KEY", "6325087c017744b0b0d13950252307")
32
  EIA_API_KEY = os.getenv("EIA_API_KEY", "l0LsSOevbx1XqUMSEdEP7qVwDQXoeC3bFw8LPdGZ")
 
216
  ))
217
  return events
218
 
219
+ def load_topology() -> Dict:
220
+ # Load from examples/toy_topology.json or mock (made sync for simplicity in map generation)
221
  topology = {
222
+ "nodes": [
223
+ Node(node_id="node1", name="Substation A", type="substation", lat=29.7604, lon=-95.3698, voltage_kv=138, capacity_mw=500, connected_edges=["edge1"]).dict(),
224
+ Node(node_id="node2", name="Substation B", type="substation", lat=29.75, lon=-95.36, voltage_kv=138, capacity_mw=400, connected_edges=["edge1"]).dict()
225
+ ],
226
  "edges": [Edge(edge_id="edge1", from_node="node1", to_node="node2", r_ohm_per_km=0.1, x_ohm_per_km=0.2, length_km=12.3, thermal_limit_mw=300).dict()]
227
  }
228
  return topology
 
286
  # === LLM ORCHESTRATOR ===
287
  class LLMOrchestrator:
288
  async def build_context(self, node_id: str, timestamp: str) -> Dict:
289
+ topology = load_topology()
290
  timeseries = await fetch_grid_data({"respondent": "CISO", "start": (datetime.datetime.fromisoformat(timestamp) - datetime.timedelta(hours=24)).strftime("%Y-%m-%dT%H")})
291
  weather = await fetch_weather({"latitude": topology["nodes"][0]["lat"], "longitude": topology["nodes"][0]["lon"]})
292
  events = await fetch_events({"location": "Houston"})
 
341
  except:
342
  return False
343
 
344
+ # === MAP GENERATION ===
345
+ def generate_map():
346
+ topology = load_topology()
347
+ nodes = topology['nodes']
348
+ df = pd.DataFrame(nodes)
349
+ # Mock risks for each node
350
+ df['risk'] = np.random.uniform(0, 1, len(df))
351
+ df['color'] = df['risk'] # For continuous color
352
+ df['size'] = 10 # Fixed size
353
+ fig = px.scatter_mapbox(
354
+ df,
355
+ lat="lat",
356
+ lon="lon",
357
+ color="risk",
358
+ size="size",
359
+ hover_name="name",
360
+ color_continuous_scale="RdYlGn_r", # Red for high risk, green for low
361
+ zoom=10,
362
+ height=500,
363
+ mapbox_style="open-street-map"
364
+ )
365
+ fig.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
366
+ return fig
367
+
368
  # === FASTAPI BACKEND ===
369
  app = FastAPI()
370
 
 
398
  gr.Markdown("# PowerGrid Guardian Demo")
399
  with gr.Row():
400
  with gr.Column(scale=2):
401
+ gr.Markdown("## Map Panel")
402
+ map_plot = gr.Plot(label="Grid Map")
 
403
  with gr.Column(scale=1):
404
  gr.Markdown("## Alert Timeline")
405
  alerts = gr.JSON(value=get_alerts(), label="Alerts")
 
411
 
412
  rec_button.click(fn=lambda node: asyncio.run(get_recommendation(node)), inputs=node_input, outputs=rec_output)
413
  sim_button.click(fn=run_simulation, outputs=sim_output)
414
+ demo.load(generate_map, outputs=map_plot)
415
 
416
  # === TESTS (Pytest compatible - can be extracted to tests/ files) ===
417
  def test_fusion():
 
442
  uvicorn.run(app, host="0.0.0.0", port=8000)
443
  threading.Thread(target=run_api, daemon=True).start()
444
  # Launch Gradio
445
+ demo.launch(server_name="0.0.0.0", server_port=7860)