Spaces:
Runtime error
Runtime error
""" | |
Diagram Generation Utilities for Power Systems | |
Generates SVG diagrams for power system concepts | |
""" | |
import json | |
from typing import Dict, List, Tuple, Optional | |
from datetime import datetime | |
class DiagramGenerator: | |
""" | |
Generate SVG diagrams for power systems concepts | |
""" | |
def __init__(self): | |
self.svg_width = 800 | |
self.svg_height = 600 | |
self.grid_size = 20 | |
def create_svg_header(self, width: int = None, height: int = None) -> str: | |
"""Create SVG header with proper dimensions""" | |
w = width or self.svg_width | |
h = height or self.svg_height | |
return f"""<svg width="{w}" height="{h}" viewBox="0 0 {w} {h}" xmlns="http://www.w3.org/2000/svg"> | |
<defs> | |
<style> | |
.line {{ stroke: #2563eb; stroke-width: 2; fill: none; }} | |
.bus {{ stroke: #dc2626; stroke-width: 4; }} | |
.text {{ font-family: Arial, sans-serif; font-size: 12px; fill: #374151; }} | |
.title {{ font-family: Arial, sans-serif; font-size: 16px; font-weight: bold; fill: #1f2937; }} | |
.component {{ stroke: #059669; stroke-width: 2; fill: #d1fae5; }} | |
.fault {{ stroke: #dc2626; stroke-width: 3; fill: #fecaca; }} | |
.protection {{ stroke: #7c3aed; stroke-width: 2; fill: #e9d5ff; }} | |
.ground {{ stroke: #374151; stroke-width: 2; }} | |
</style> | |
<!-- Marker definitions for arrows --> | |
<marker id="arrowhead" markerWidth="10" markerHeight="7" | |
refX="9" refY="3.5" orient="auto"> | |
<polygon points="0 0, 10 3.5, 0 7" fill="#2563eb" /> | |
</marker> | |
<!-- Component symbols --> | |
<g id="generator"> | |
<circle cx="0" cy="0" r="15" class="component"/> | |
<text x="0" y="4" text-anchor="middle" class="text">G</text> | |
</g> | |
<g id="transformer"> | |
<circle cx="-10" cy="0" r="8" class="component"/> | |
<circle cx="10" cy="0" r="8" class="component"/> | |
<text x="0" y="25" text-anchor="middle" class="text">T</text> | |
</g> | |
<g id="load"> | |
<path d="M-10,-10 L10,-10 L5,10 L-5,10 Z" class="component"/> | |
<text x="0" y="25" text-anchor="middle" class="text">Load</text> | |
</g> | |
<g id="fault-symbol"> | |
<circle cx="0" cy="0" r="8" class="fault"/> | |
<line x1="-6" y1="-6" x2="6" y2="6" class="fault"/> | |
<line x1="-6" y1="6" x2="6" y2="-6" class="fault"/> | |
</g> | |
</defs> | |
""" | |
def create_svg_footer(self) -> str: | |
"""Create SVG footer""" | |
return "</svg>" | |
def generate_single_line_diagram(self, system_config: Dict) -> str: | |
"""Generate a single line diagram""" | |
svg = self.create_svg_header() | |
# Title | |
svg += f'<text x="400" y="30" text-anchor="middle" class="title">Single Line Diagram</text>\n' | |
# Main bus (horizontal line) | |
svg += f'<line x1="100" y1="100" x2="700" y2="100" class="bus"/>\n' | |
svg += f'<text x="400" y="85" text-anchor="middle" class="text">Main Bus (33kV)</text>\n' | |
# Generator | |
svg += f'<use href="#generator" transform="translate(150,100)"/>\n' | |
svg += f'<line x1="135" y1="100" x2="100" y2="100" class="line"/>\n' | |
svg += f'<text x="150" y="140" text-anchor="middle" class="text">Generator</text>\n' | |
svg += f'<text x="150" y="155" text-anchor="middle" class="text">100MVA</text>\n' | |
# Transformer | |
svg += f'<use href="#transformer" transform="translate(300,100)"/>\n' | |
svg += f'<line x1="290" y1="100" x2="250" y2="100" class="line"/>\n' | |
svg += f'<line x1="310" y1="100" x2="350" y2="100" class="line"/>\n' | |
svg += f'<text x="300" y="140" text-anchor="middle" class="text">Power Transformer</text>\n' | |
svg += f'<text x="300" y="155" text-anchor="middle" class="text">33/11kV, 50MVA</text>\n' | |
# Distribution lines | |
svg += f'<line x1="400" y1="100" x2="400" y2="200" class="line"/>\n' | |
svg += f'<line x1="350" y1="200" x2="450" y2="200" class="line"/>\n' | |
svg += f'<text x="400" y="215" text-anchor="middle" class="text">Distribution Bus (11kV)</text>\n' | |
# Loads | |
positions = [375, 425] | |
load_names = ["Industrial Load", "Commercial Load"] | |
load_powers = ["15MW", "8MW"] | |
for i, (pos, name, power) in enumerate(zip(positions, load_names, load_powers)): | |
svg += f'<use href="#load" transform="translate({pos},250)"/>\n' | |
svg += f'<line x1="{pos}" y1="200" x2="{pos}" y2="240" class="line"/>\n' | |
svg += f'<text x="{pos}" y="285" text-anchor="middle" class="text">{name}</text>\n' | |
svg += f'<text x="{pos}" y="300" text-anchor="middle" class="text">{power}</text>\n' | |
# Protection devices | |
svg += f'<rect x="195" y="95" width="10" height="10" class="protection"/>\n' | |
svg += f'<text x="200" y="120" text-anchor="middle" class="text">CB1</text>\n' | |
svg += f'<rect x="345" y="95" width="10" height="10" class="protection"/>\n' | |
svg += f'<text x="350" y="120" text-anchor="middle" class="text">CB2</text>\n' | |
# Legend | |
svg += f'<text x="50" y="450" class="title">Legend:</text>\n' | |
svg += f'<line x1="50" y1="470" x2="80" y2="470" class="bus"/>\n' | |
svg += f'<text x="90" y="475" class="text">Bus</text>\n' | |
svg += f'<line x1="50" y1="490" x2="80" y2="490" class="line"/>\n' | |
svg += f'<text x="90" y="495" class="text">Transmission Line</text>\n' | |
svg += f'<rect x="50" y="505" width="10" height="10" class="protection"/>\n' | |
svg += f'<text x="70" y="515" class="text">Circuit Breaker</text>\n' | |
svg += self.create_svg_footer() | |
return svg | |
def generate_fault_analysis_diagram(self, fault_type: str = "line_to_ground") -> str: | |
"""Generate fault analysis diagram with sequence networks""" | |
svg = self.create_svg_header(900, 700) | |
# Title | |
svg += f'<text x="450" y="30" text-anchor="middle" class="title">Fault Analysis - {fault_type.replace("_", " ").title()}</text>\n' | |
if fault_type == "line_to_ground": | |
# Positive sequence network | |
svg += f'<text x="150" y="80" text-anchor="middle" class="title">Positive Sequence</text>\n' | |
svg += self._draw_sequence_network(150, 100, "Z1", "#059669") | |
# Negative sequence network | |
svg += f'<text x="450" y="80" text-anchor="middle" class="title">Negative Sequence</text>\n' | |
svg += self._draw_sequence_network(450, 100, "Z2", "#dc2626") | |
# Zero sequence network | |
svg += f'<text x="750" y="80" text-anchor="middle" class="title">Zero Sequence</text>\n' | |
svg += self._draw_sequence_network(750, 100, "Z0", "#7c3aed") | |
# Connection diagram | |
svg += f'<text x="450" y="320" text-anchor="middle" class="title">Network Connection</text>\n' | |
# Series connection for L-G fault | |
svg += f'<line x1="400" y1="350" x2="400" y2="450" class="line"/>\n' | |
svg += f'<line x1="400" y1="370" x2="500" y2="370" class="line"/>\n' | |
svg += f'<line x1="400" y1="410" x2="500" y2="410" class="line"/>\n' | |
svg += f'<line x1="400" y1="450" x2="500" y2="450" class="line"/>\n' | |
# Impedance boxes | |
svg += f'<rect x="480" y="360" width="40" height="20" class="component"/>\n' | |
svg += f'<text x="500" y="375" text-anchor="middle" class="text">Z1</text>\n' | |
svg += f'<rect x="480" y="400" width="40" height="20" class="component"/>\n' | |
svg += f'<text x="500" y="415" text-anchor="middle" class="text">Z2</text>\n' | |
svg += f'<rect x="480" y="440" width="40" height="20" class="component"/>\n' | |
svg += f'<text x="500" y="455" text-anchor="middle" class="text">Z0</text>\n' | |
# Voltage source | |
svg += f'<circle cx="370" cy="350" r="15" class="component"/>\n' | |
svg += f'<text x="370" y="355" text-anchor="middle" class="text">Ea</text>\n' | |
# Fault point | |
svg += f'<use href="#fault-symbol" transform="translate(530,410)"/>\n' | |
svg += f'<text x="530" y="440" text-anchor="middle" class="text">Fault</text>\n' | |
# Current equation | |
svg += f'<text x="450" y="520" text-anchor="middle" class="title">Fault Current Calculation</text>\n' | |
svg += f'<text x="450" y="550" text-anchor="middle" class="text">I_fault = 3 × Ea / (Z1 + Z2 + Z0)</text>\n' | |
elif fault_type == "line_to_line": | |
# L-L fault diagram | |
svg += self._draw_ll_fault_diagram() | |
elif fault_type == "three_phase": | |
# 3-phase fault diagram | |
svg += self._draw_three_phase_fault_diagram() | |
svg += self.create_svg_footer() | |
return svg | |
def _draw_sequence_network(self, x: int, y: int, impedance: str, color: str) -> str: | |
"""Draw a sequence network""" | |
svg = "" | |
# Voltage source | |
svg += f'<circle cx="{x-50}" cy="{y+50}" r="15" stroke="{color}" stroke-width="2" fill="white"/>\n' | |
svg += f'<text x="{x-50}" y="{y+55}" text-anchor="middle" class="text">E</text>\n' | |
# Impedance | |
svg += f'<rect x="{x-10}" y="{y+40}" width="40" height="20" stroke="{color}" stroke-width="2" fill="white"/>\n' | |
svg += f'<text x="{x+10}" y="{y+55}" text-anchor="middle" class="text">{impedance}</text>\n' | |
# Connecting lines | |
svg += f'<line x1="{x-35}" y1="{y+50}" x2="{x-10}" y2="{y+50}" stroke="{color}" stroke-width="2"/>\n' | |
svg += f'<line x1="{x+30}" y1="{y+50}" x2="{x+60}" y2="{y+50}" stroke="{color}" stroke-width="2"/>\n' | |
# Ground/neutral | |
svg += f'<line x1="{x+60}" y1="{y+50}" x2="{x+60}" y2="{y+80}" stroke="{color}" stroke-width="2"/>\n' | |
svg += f'<line x1="{x+50}" y1="{y+80}" x2="{x+70}" y2="{y+80}" stroke="{color}" stroke-width="2"/>\n' | |
svg += f'<line x1="{x+52}" y1="{y+85}" x2="{x+68}" y2="{y+85}" stroke="{color}" stroke-width="2"/>\n' | |
svg += f'<line x1="{x+54}" y1="{y+90}" x2="{x+66}" y2="{y+90}" stroke="{color}" stroke-width="2"/>\n' | |
return svg | |
def _draw_ll_fault_diagram(self) -> str: | |
"""Draw line-to-line fault diagram""" | |
svg = "" | |
# Positive and negative sequence in parallel | |
svg += f'<text x="300" y="80" text-anchor="middle" class="title">L-L Fault: Z1 and Z2 in Parallel</text>\n' | |
# Parallel connection | |
svg += f'<circle cx="200" cy="150" r="15" class="component"/>\n' | |
svg += f'<text x="200" y="155" text-anchor="middle" class="text">Ea</text>\n' | |
# Upper branch (Z1) | |
svg += f'<line x1="215" y1="140" x2="300" y2="140" class="line"/>\n' | |
svg += f'<rect x="300" y="130" width="40" height="20" class="component"/>\n' | |
svg += f'<text x="320" y="145" text-anchor="middle" class="text">Z1</text>\n' | |
svg += f'<line x1="340" y1="140" x2="400" y2="140" class="line"/>\n' | |
# Lower branch (Z2) | |
svg += f'<line x1="215" y1="160" x2="300" y2="160" class="line"/>\n' | |
svg += f'<rect x="300" y="150" width="40" height="20" class="component"/>\n' | |
svg += f'<text x="320" y="165" text-anchor="middle" class="text">Z2</text>\n' | |
svg += f'<line x1="340" y1="160" x2="400" y2="160" class="line"/>\n' | |
# Connection | |
svg += f'<line x1="400" y1="140" x2="400" y2="160" class="line"/>\n' | |
# Fault | |
svg += f'<use href="#fault-symbol" transform="translate(420,150)"/>\n' | |
# Current equation | |
svg += f'<text x="300" y="250" text-anchor="middle" class="title">I_fault = √3 × Ea / (Z1 + Z2)</text>\n' | |
return svg | |
def _draw_three_phase_fault_diagram(self) -> str: | |
"""Draw three-phase fault diagram""" | |
svg = "" | |
svg += f'<text x="400" y="80" text-anchor="middle" class="title">Three-Phase Fault: Positive Sequence Only</text>\n' | |
# Simple circuit | |
svg += f'<circle cx="250" cy="150" r="15" class="component"/>\n' | |
svg += f'<text x="250" y="155" text-anchor="middle" class="text">Ea</text>\n' | |
svg += f'<line x1="265" y1="150" x2="350" y2="150" class="line"/>\n' | |
svg += f'<rect x="350" y="140" width="40" height="20" class="component"/>\n' | |
svg += f'<text x="370" y="155" text-anchor="middle" class="text">Z1</text>\n' | |
svg += f'<line x1="390" y1="150" x2="450" y2="150" class="line"/>\n' | |
svg += f'<use href="#fault-symbol" transform="translate(470,150)"/>\n' | |
# Current equation | |
svg += f'<text x="400" y="220" text-anchor="middle" class="title">I_fault = Ea / Z1</text>\n' | |
return svg | |
def generate_protection_coordination_diagram(self) -> str: | |
"""Generate time-current coordination curves""" | |
svg = self.create_svg_header(800, 600) | |
# Title | |
svg += f'<text x="400" y="30" text-anchor="middle" class="title">Protection Coordination Curves</text>\n' | |
# Axes | |
svg += f'<line x1="100" y1="500" x2="700" y2="500" class="line marker-end="url(#arrowhead)"/>\n' | |
svg += f'<line x1="100" y1="500" x2="100" y2="100" class="line marker-end="url(#arrowhead)"/>\n' | |
# Axis labels | |
svg += f'<text x="400" y="530" text-anchor="middle" class="text">Current (A)</text>\n' | |
svg += f'<text x="50" y="300" text-anchor="middle" class="text" transform="rotate(-90 50 300)">Time (s)</text>\n' | |
# Grid lines | |
for i in range(2, 7): | |
x = 100 + i * 100 | |
svg += f'<line x1="{x}" y1="100" x2="{x}" y2="500" stroke="#e5e7eb" stroke-width="1"/>\n' | |
svg += f'<text x="{x}" y="520" text-anchor="middle" class="text">{10**(i-1)}</text>\n' | |
for i in range(1, 5): | |
y = 500 - i * 80 | |
svg += f'<line x1="100" y1="{y}" x2="700" y2="{y}" stroke="#e5e7eb" stroke-width="1"/>\n' | |
svg += f'<text x="85" y="{y+5}" text-anchor="end" class="text">{10**(i-1)}</text>\n' | |
# Relay curves | |
# Primary relay (closer to load) | |
svg += self._draw_relay_curve(200, "Primary Relay", "#059669", "inverse") | |
# Backup relay | |
svg += self._draw_relay_curve(300, "Backup Relay", "#dc2626", "very_inverse") | |
# Fuse curve | |
svg += self._draw_relay_curve(150, "Fuse", "#7c3aed", "fuse") | |
# Legend | |
svg += f'<rect x="550" y="120" width="180" height="120" stroke="#374151" stroke-width="1" fill="white"/>\n' | |
svg += f'<text x="640" y="140" text-anchor="middle" class="text">Legend</text>\n' | |
svg += f'<line x1="560" y1="160" x2="590" y2="160" stroke="#7c3aed" stroke-width="3"/>\n' | |
svg += f'<text x="600" y="165" class="text">Fuse</text>\n' | |
svg += f'<line x1="560" y1="180" x2="590" y2="180" stroke="#059669" stroke-width="3"/>\n' | |
svg += f'<text x="600" y="185" class="text">Primary Relay</text>\n' | |
svg += f'<line x1="560" y1="200" x2="590" y2="200" stroke="#dc2626" stroke-width="3"/>\n' | |
svg += f'<text x="600" y="205" class="text">Backup Relay</text>\n' | |
svg += f'<text x="640" y="225" text-anchor="middle" class="text">Coordination Interval: 0.3s</text>\n' | |
svg += self.create_svg_footer() | |
return svg | |
def _draw_relay_curve(self, x_offset: int, label: str, color: str, curve_type: str) -> str: | |
"""Draw a time-current curve for a relay""" | |
svg = "" | |
points = [] | |
if curve_type == "inverse": | |
# Standard inverse curve | |
for i in range(50): | |
current = 10 ** (i / 10.0) | |
time = 0.14 / ((current/100) ** 0.02 - 1) | |
if time > 0 and time < 1000: | |
x = 100 + (i * 12) | |
y = 500 - (time * 40) | |
if 100 <= x <= 700 and 100 <= y <= 500: | |
points.append(f"{x},{y}") | |
elif curve_type == "very_inverse": | |
# Very inverse curve (higher up) | |
for i in range(50): | |
current = 10 ** (i / 10.0) | |
time = 13.5 / ((current/100) ** 1.0 - 1) | |
if time > 0 and time < 1000: | |
x = 100 + (i * 12) | |
y = 500 - (time * 20) | |
if 100 <= x <= 700 and 100 <= y <= 500: | |
points.append(f"{x},{y}") | |
elif curve_type == "fuse": | |
# Fuse curve (faster, lower) | |
for i in range(40): | |
current = 10 ** (i / 8.0) | |
time = 0.01 / ((current/50) ** 2.0 - 1) if current > 50 else 1000 | |
if time > 0 and time < 1000: | |
x = 100 + (i * 15) | |
y = 500 - (time * 60) | |
if 100 <= x <= 700 and 100 <= y <= 500: | |
points.append(f"{x},{y}") | |
if points: | |
path = f'<polyline points="{" ".join(points)}" stroke="{color}" stroke-width="3" fill="none"/>\n' | |
svg += path | |
# Label | |
if len(points) > 10: | |
mid_point = points[len(points)//2].split(',') | |
x, y = int(mid_point[0]), int(mid_point[1]) | |
svg += f'<text x="{x+10}" y="{y-5}" class="text" fill="{color}">{label}</text>\n' | |
return svg | |
def generate_phasor_diagram(self, fault_type: str = "balanced") -> str: | |
"""Generate phasor diagrams for different fault conditions""" | |
svg = self.create_svg_header(600, 400) | |
# Title | |
svg += f'<text x="300" y="30" text-anchor="middle" class="title">Phasor Diagram - {fault_type.title()} Conditions</text>\n' | |
center_x, center_y = 300, 200 | |
radius = 80 | |
if fault_type == "balanced": | |
# Three balanced phasors 120° apart | |
angles = [0, 120, 240] | |
colors = ["#dc2626", "#059669", "#2563eb"] | |
labels = ["Va", "Vb", "Vc"] | |
for i, (angle, color, label) in enumerate(zip(angles, colors, labels)): | |
x_end = center_x + radius * cos(radians(angle)) | |
y_end = center_y - radius * sin(radians(angle)) | |
svg += f'<line x1="{center_x}" y1="{center_y}" x2="{x_end}" y2="{y_end}" ' | |
svg += f'stroke="{color}" stroke-width="3" marker-end="url(#arrowhead)"/>\n' | |
# Label | |
label_x = center_x + (radius + 20) * cos(radians(angle)) | |
label_y = center_y - (radius + 20) * sin(radians(angle)) | |
svg += f'<text x="{label_x}" y="{label_y}" text-anchor="middle" class="text" fill="{color}">{label}</text>\n' | |
elif fault_type == "unbalanced": | |
# Unbalanced phasors showing fault condition | |
# Phase A (affected by fault) - reduced magnitude | |
svg += f'<line x1="{center_x}" y1="{center_y}" x2="{center_x + 40}" y2="{center_y}" ' | |
svg += f'stroke="#dc2626" stroke-width="3" marker-end="url(#arrowhead)"/>\n' | |
svg += f'<text x="{center_x + 60}" y="{center_y}" class="text" fill="#dc2626">Va (faulted)</text>\n' | |
# Phase B - normal | |
x_b = center_x + radius * cos(radians(120)) | |
y_b = center_y - radius * sin(radians(120)) | |
svg += f'<line x1="{center_x}" y1="{center_y}" x2="{x_b}" y2="{y_b}" ' | |
svg += f'stroke="#059669" stroke-width="3" marker-end="url(#arrowhead)"/>\n' | |
svg += f'<text x="{x_b-20}" y="{y_b-10}" class="text" fill="#059669">Vb</text>\n' | |
# Phase C - normal | |
x_c = center_x + radius * cos(radians(240)) | |
y_c = center_y - radius * sin(radians(240)) | |
svg += f'<line x1="{center_x}" y1="{center_y}" x2="{x_c}" y2="{y_c}" ' | |
svg += f'stroke="#2563eb" stroke-width="3" marker-end="url(#arrowhead)"/>\n' | |
svg += f'<text x="{x_c-20}" y="{y_c+20}" class="text" fill="#2563eb">Vc</text>\n' | |
# Center point | |
svg += f'<circle cx="{center_x}" cy="{center_y}" r="3" fill="#374151"/>\n' | |
# Reference circle | |
svg += f'<circle cx="{center_x}" cy="{center_y}" r="{radius}" stroke="#e5e7eb" stroke-width="1" fill="none" stroke-dasharray="5,5"/>\n' | |
svg += self.create_svg_footer() | |
return svg | |
def generate_impedance_diagram(self) -> str: | |
"""Generate impedance diagram for distance protection""" | |
svg = self.create_svg_header(600, 500) | |
# Title | |
svg += f'<text x="300" y="30" text-anchor="middle" class="title">Distance Protection - R-X Diagram</text>\n' | |
center_x, center_y = 300, 250 | |
# Axes | |
svg += f'<line x1="100" y1="{center_y}" x2="500" y2="{center_y}" class="line" marker-end="url(#arrowhead)"/>\n' | |
svg += f'<line x1="{center_x}" y1="400" x2="{center_x}" y2="100" class="line" marker-end="url(#arrowhead)"/>\n' | |
# Axis labels | |
svg += f'<text x="520" y="{center_y+5}" class="text">R (Ω)</text>\n' | |
svg += f'<text x="{center_x-10}" y="90" class="text">X (Ω)</text>\n' | |
# Mho circle (Zone 1) | |
svg += f'<circle cx="{center_x+50}" cy="{center_y}" r="80" stroke="#059669" stroke-width="2" fill="none"/>\n' | |
svg += f'<text x="{center_x+90}" y="{center_y-90}" class="text" fill="#059669">Zone 1 (Mho)</text>\n' | |
# Zone 2 (larger circle) | |
svg += f'<circle cx="{center_x+70}" cy="{center_y}" r="120" stroke="#dc2626" stroke-width="2" fill="none" stroke-dasharray="5,5"/>\n' | |
svg += f'<text x="{center_x+140}" y="{center_y-130}" class="text" fill="#dc2626">Zone 2</text>\n' | |
# Load impedance area | |
svg += f'<path d="M{center_x+20},{center_y-20} Q{center_x+80},{center_y-40} {center_x+120},{center_y-10}" ' | |
svg += f'stroke="#7c3aed" stroke-width="2" fill="none"/>\n' | |
svg += f'<text x="{center_x+70}" y="{center_y-50}" class="text" fill="#7c3aed">Load Region</text>\n' | |
# Grid marks | |
for i in range(1, 5): | |
x = center_x + i * 40 | |
svg += f'<line x1="{x}" y1="{center_y-5}" x2="{x}" y2="{center_y+5}" stroke="#374151" stroke-width="1"/>\n' | |
svg += f'<text x="{x}" y="{center_y+20}" text-anchor="middle" class="text">{i*5}</text>\n' | |
y = center_y - i * 40 | |
svg += f'<line x1="{center_x-5}" y1="{y}" x2="{center_x+5}" y2="{y}" stroke="#374151" stroke-width="1"/>\n' | |
svg += f'<text x="{center_x-20}" y="{y+5}" text-anchor="middle" class="text">{i*5}</text>\n' | |
svg += self.create_svg_footer() | |
return svg | |
# Helper functions for calculations | |
def cos(angle_deg): | |
import math | |
return math.cos(math.radians(angle_deg)) | |
def sin(angle_deg): | |
import math | |
return math.sin(math.radians(angle_deg)) | |
def radians(angle_deg): | |
import math | |
return math.radians(angle_deg) | |
# Example usage | |
if __name__ == "__main__": | |
generator = DiagramGenerator() | |
# Test diagram generation | |
diagrams = { | |
"single_line": generator.generate_single_line_diagram({}), | |
"fault_analysis": generator.generate_fault_analysis_diagram("line_to_ground"), | |
"protection_coordination": generator.generate_protection_coordination_diagram(), | |
"phasor": generator.generate_phasor_diagram("balanced"), | |
"impedance": generator.generate_impedance_diagram() | |
} | |
# Save diagrams | |
for name, svg_content in diagrams.items(): | |
with open(f"{name}_diagram.svg", "w") as f: | |
f.write(svg_content) | |
print(f"Generated {name} diagram") | |
print("All diagrams generated successfully!") |