Spaces:
Runtime error
Runtime error
Create utils/diagram_generator.py
Browse files- utils/diagram_generator.py +145 -0
utils/diagram_generator.py
ADDED
@@ -0,0 +1,145 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Diagram Generation Utilities for Power Systems
|
3 |
+
Generates SVG diagrams for power system concepts
|
4 |
+
"""
|
5 |
+
|
6 |
+
import json
|
7 |
+
from typing import Dict, List, Tuple, Optional
|
8 |
+
from datetime import datetime
|
9 |
+
|
10 |
+
class DiagramGenerator:
|
11 |
+
"""
|
12 |
+
Generate SVG diagrams for power systems concepts
|
13 |
+
"""
|
14 |
+
|
15 |
+
def __init__(self):
|
16 |
+
self.svg_width = 800
|
17 |
+
self.svg_height = 600
|
18 |
+
self.grid_size = 20
|
19 |
+
|
20 |
+
def create_svg_header(self, width: int = None, height: int = None) -> str:
|
21 |
+
"""Create SVG header with proper dimensions"""
|
22 |
+
w = width or self.svg_width
|
23 |
+
h = height or self.svg_height
|
24 |
+
|
25 |
+
return f"""<svg width="{w}" height="{h}" viewBox="0 0 {w} {h}" xmlns="http://www.w3.org/2000/svg">
|
26 |
+
<defs>
|
27 |
+
<style>
|
28 |
+
.line {{ stroke: #2563eb; stroke-width: 2; fill: none; }}
|
29 |
+
.bus {{ stroke: #dc2626; stroke-width: 4; }}
|
30 |
+
.text {{ font-family: Arial, sans-serif; font-size: 12px; fill: #374151; }}
|
31 |
+
.title {{ font-family: Arial, sans-serif; font-size: 16px; font-weight: bold; fill: #1f2937; }}
|
32 |
+
.component {{ stroke: #059669; stroke-width: 2; fill: #d1fae5; }}
|
33 |
+
.fault {{ stroke: #dc2626; stroke-width: 3; fill: #fecaca; }}
|
34 |
+
.protection {{ stroke: #7c3aed; stroke-width: 2; fill: #e9d5ff; }}
|
35 |
+
.ground {{ stroke: #374151; stroke-width: 2; }}
|
36 |
+
</style>
|
37 |
+
|
38 |
+
<!-- Marker definitions for arrows -->
|
39 |
+
<marker id="arrowhead" markerWidth="10" markerHeight="7"
|
40 |
+
refX="9" refY="3.5" orient="auto">
|
41 |
+
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb" />
|
42 |
+
</marker>
|
43 |
+
|
44 |
+
<!-- Component symbols -->
|
45 |
+
<g id="generator">
|
46 |
+
<circle cx="0" cy="0" r="15" class="component"/>
|
47 |
+
<text x="0" y="4" text-anchor="middle" class="text">G</text>
|
48 |
+
</g>
|
49 |
+
|
50 |
+
<g id="transformer">
|
51 |
+
<circle cx="-10" cy="0" r="8" class="component"/>
|
52 |
+
<circle cx="10" cy="0" r="8" class="component"/>
|
53 |
+
<text x="0" y="25" text-anchor="middle" class="text">T</text>
|
54 |
+
</g>
|
55 |
+
|
56 |
+
<g id="load">
|
57 |
+
<path d="M-10,-10 L10,-10 L5,10 L-5,10 Z" class="component"/>
|
58 |
+
<text x="0" y="25" text-anchor="middle" class="text">Load</text>
|
59 |
+
</g>
|
60 |
+
|
61 |
+
<g id="fault-symbol">
|
62 |
+
<circle cx="0" cy="0" r="8" class="fault"/>
|
63 |
+
<line x1="-6" y1="-6" x2="6" y2="6" class="fault"/>
|
64 |
+
<line x1="-6" y1="6" x2="6" y2="-6" class="fault"/>
|
65 |
+
</g>
|
66 |
+
</defs>
|
67 |
+
"""
|
68 |
+
|
69 |
+
def create_svg_footer(self) -> str:
|
70 |
+
"""Create SVG footer"""
|
71 |
+
return "</svg>"
|
72 |
+
|
73 |
+
def generate_single_line_diagram(self, system_config: Dict) -> str:
|
74 |
+
"""Generate a single line diagram"""
|
75 |
+
svg = self.create_svg_header()
|
76 |
+
|
77 |
+
# Title
|
78 |
+
svg += f'<text x="400" y="30" text-anchor="middle" class="title">Single Line Diagram</text>\n'
|
79 |
+
|
80 |
+
# Main bus (horizontal line)
|
81 |
+
svg += f'<line x1="100" y1="100" x2="700" y2="100" class="bus"/>\n'
|
82 |
+
svg += f'<text x="400" y="85" text-anchor="middle" class="text">Main Bus (33kV)</text>\n'
|
83 |
+
|
84 |
+
# Generator
|
85 |
+
svg += f'<use href="#generator" transform="translate(150,100)"/>\n'
|
86 |
+
svg += f'<line x1="135" y1="100" x2="100" y2="100" class="line"/>\n'
|
87 |
+
svg += f'<text x="150" y="140" text-anchor="middle" class="text">Generator</text>\n'
|
88 |
+
svg += f'<text x="150" y="155" text-anchor="middle" class="text">100MVA</text>\n'
|
89 |
+
|
90 |
+
# Transformer
|
91 |
+
svg += f'<use href="#transformer" transform="translate(300,100)"/>\n'
|
92 |
+
svg += f'<line x1="290" y1="100" x2="250" y2="100" class="line"/>\n'
|
93 |
+
svg += f'<line x1="310" y1="100" x2="350" y2="100" class="line"/>\n'
|
94 |
+
svg += f'<text x="300" y="140" text-anchor="middle" class="text">Power Transformer</text>\n'
|
95 |
+
svg += f'<text x="300" y="155" text-anchor="middle" class="text">33/11kV, 50MVA</text>\n'
|
96 |
+
|
97 |
+
# Distribution lines
|
98 |
+
svg += f'<line x1="400" y1="100" x2="400" y2="200" class="line"/>\n'
|
99 |
+
svg += f'<line x1="350" y1="200" x2="450" y2="200" class="line"/>\n'
|
100 |
+
svg += f'<text x="400" y="215" text-anchor="middle" class="text">Distribution Bus (11kV)</text>\n'
|
101 |
+
|
102 |
+
# Loads
|
103 |
+
positions = [375, 425]
|
104 |
+
load_names = ["Industrial Load", "Commercial Load"]
|
105 |
+
load_powers = ["15MW", "8MW"]
|
106 |
+
|
107 |
+
for i, (pos, name, power) in enumerate(zip(positions, load_names, load_powers)):
|
108 |
+
svg += f'<use href="#load" transform="translate({pos},250)"/>\n'
|
109 |
+
svg += f'<line x1="{pos}" y1="200" x2="{pos}" y2="240" class="line"/>\n'
|
110 |
+
svg += f'<text x="{pos}" y="285" text-anchor="middle" class="text">{name}</text>\n'
|
111 |
+
svg += f'<text x="{pos}" y="300" text-anchor="middle" class="text">{power}</text>\n'
|
112 |
+
|
113 |
+
# Protection devices
|
114 |
+
svg += f'<rect x="195" y="95" width="10" height="10" class="protection"/>\n'
|
115 |
+
svg += f'<text x="200" y="120" text-anchor="middle" class="text">CB1</text>\n'
|
116 |
+
|
117 |
+
svg += f'<rect x="345" y="95" width="10" height="10" class="protection"/>\n'
|
118 |
+
svg += f'<text x="350" y="120" text-anchor="middle" class="text">CB2</text>\n'
|
119 |
+
|
120 |
+
# Legend
|
121 |
+
svg += f'<text x="50" y="450" class="title">Legend:</text>\n'
|
122 |
+
svg += f'<line x1="50" y1="470" x2="80" y2="470" class="bus"/>\n'
|
123 |
+
svg += f'<text x="90" y="475" class="text">Bus</text>\n'
|
124 |
+
svg += f'<line x1="50" y1="490" x2="80" y2="490" class="line"/>\n'
|
125 |
+
svg += f'<text x="90" y="495" class="text">Transmission Line</text>\n'
|
126 |
+
svg += f'<rect x="50" y="505" width="10" height="10" class="protection"/>\n'
|
127 |
+
svg += f'<text x="70" y="515" class="text">Circuit Breaker</text>\n'
|
128 |
+
|
129 |
+
svg += self.create_svg_footer()
|
130 |
+
return svg
|
131 |
+
|
132 |
+
def generate_fault_analysis_diagram(self, fault_type: str = "line_to_ground") -> str:
|
133 |
+
"""Generate fault analysis diagram with sequence networks"""
|
134 |
+
svg = self.create_svg_header(900, 700)
|
135 |
+
|
136 |
+
# Title
|
137 |
+
svg += f'<text x="450" y="30" text-anchor="middle" class="title">Fault Analysis - {fault_type.replace("_", " ").title()}</text>\n'
|
138 |
+
|
139 |
+
if fault_type == "line_to_ground":
|
140 |
+
# Positive sequence network
|
141 |
+
svg += f'<text x="150" y="80" text-anchor="middle" class="title">Positive Sequence</text>\n'
|
142 |
+
svg += self._draw_sequence_network(150, 100, "Z1", "#059669")
|
143 |
+
|
144 |
+
# Negative sequence network
|
145 |
+
svg += f'<text x="450" y="80" text-anchor="middle" class="
|