wykresy kołowe Z I U

Dodany plik requirements.txt pozwalakacy na szybka instalacje bibliotek przez pip install -r requirements.txt
This commit is contained in:
2026-02-19 10:33:28 +01:00
parent 6c948b44fa
commit b53c7e7b1d
4 changed files with 231 additions and 86 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

View File

@@ -0,0 +1,15 @@
# Wynik analizy dla zwarcie_testowe
## Parametry zabezpieczenia (DistanceRelayZDistA (bazowany na ZDistA_komp.c))
- Impedancja linii: R=2.00 Ohm, X=8.00 Ohm
- Kat linii: 75.0 st.
- Strefa 1: 80% (0ms)
- Strefa 2: 120% (300ms)
- Strefa 3: 150% (600ms)
## Wykrycie zwarcia: TAK
- Czas wykrycia: 256.00 ms
- Faza: L2
## Wartości w momencie zwarcia
- R = -11.7875 Ohm, X = -3.3899 Ohm
- |Z| = 12.2652 Ohm, Kat Z = -163.96 st.

3
requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
numpy
matplotlib
comtrade

279
tester.py
View File

@@ -1,20 +1,48 @@
import numpy as np import numpy as np
import matplotlib import matplotlib
matplotlib.use('Agg') # Non-interactive backend for saving plots # matplotlib.use('Agg') jest teraz warunkowe w zależności od argumentów
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from comtrade import Comtrade from comtrade import Comtrade
import math import math
import sys import sys
import os import os
import argparse
# ============================================================
# KONFIGURACJA - ARGUMENTY
# ============================================================
parser = argparse.ArgumentParser(
description="Analizator rejestracji zwarciowych dla zabezpieczeń odległościowych.",
formatter_class=argparse.RawTextHelpFormatter # Umożliwia lepsze formatowanie tekstu pomocy
)
parser.add_argument("base_name", nargs='?', default="pomiary/zwarcie_testowe",
help="Ścieżka do pliku COMTRADE (bez rozszerzenia .cfg/.dat).\n"
"Domyślnie: pomiary/zwarcie_testowe")
parser.add_argument("--live-plot", type=str, choices=['z', 'u', 'i'],
help="Włącz podgląd wybranej wielkości na żywo w formie wektorowej:\n"
" 'z' - wektory impedancji\n"
" 'u' - wektory napięć\n"
" 'i' - wektory prądów")
parser.add_argument("--speed", type=float, default=0.001,
help="Szybkość przetwarzania dla podglądu na żywo (opóźnienie w sekundach między próbkami).\n"
"Mniejsza wartość = szybciej. Użyj 0 dla maksymalnej prędkości.\n"
"Domyślnie: 0.001s.")
parser.add_argument('--algorithm', type=int, default=2, choices=[1, 2],
help="Wybierz algorytm:\n"
" 1 - DistanceRelay (podstawowy)\n"
" 2 - DistanceRelayZDistA (bazowany na ZDistA_komp.c)\n"
"Domyślnie: 2")
args = parser.parse_args()
# Konfiguracja backendu Matplotlib
if not args.live_plot:
matplotlib.use('Agg') # Użyj nieinteraktywnego backendu, jeśli nie ma podglądu na żywo
# ============================================================ # ============================================================
# KONFIGURACJA - WYBIERZ ALGORYTM # KONFIGURACJA - WYBIERZ ALGORYTM
# ============================================================ # ============================================================
# Dostępne algorytmy: ALGORITHM = args.algorithm
# 1 - distance_algorithm (DistanceRelay) - podstawowy
# 2 - distance_algorithm_zimba (DistanceRelayZDistA) - bazowany na ZDistA_komp.c
ALGORITHM = 2 # <-- zmień tę wartość aby przełączyć algorytm
if ALGORITHM == 1: if ALGORITHM == 1:
from distance_algorithm import DistanceRelay from distance_algorithm import DistanceRelay
@@ -26,6 +54,7 @@ elif ALGORITHM == 2:
ALGORITHM_NAME = "zdistA" ALGORITHM_NAME = "zdistA"
ALGORITHM_DESC = "DistanceRelayZDistA (bazowany na ZDistA_komp.c)" ALGORITHM_DESC = "DistanceRelayZDistA (bazowany na ZDistA_komp.c)"
else: else:
# Ten kod jest na wszelki wypadek, argparse powinien to wyłapać
raise ValueError(f"Nieznany algorytm: {ALGORITHM}") raise ValueError(f"Nieznany algorytm: {ALGORITHM}")
print(f"=== Używany algorytm: {ALGORITHM_DESC} ===") print(f"=== Używany algorytm: {ALGORITHM_DESC} ===")
@@ -82,13 +111,19 @@ PRZEKLADNIA_NAPIECIA = 1100.0
KIERUNEK = 0 # 0=bez, 1=do linii, 2=do szyn KIERUNEK = 0 # 0=bez, 1=do linii, 2=do szyn
# Obsluga argumentow wiersza polecen # Obsluga argumentow wiersza polecen
if len(sys.argv) > 1: base_name = args.base_name
base_name = sys.argv[1]
else:
base_name = "zwarcie_testowe"
# Wczytywanie nastaw z pliku # Wczytywanie nastaw z pliku
if os.path.isdir(base_name):
directory = base_name
print(f"BŁĄD: Podano katalog '{directory}', proszę podać pełną ścieżkę do pliku bez rozszerzenia.")
sys.exit(1)
else:
directory = os.path.dirname(os.path.abspath(base_name)) directory = os.path.dirname(os.path.abspath(base_name))
if not directory:
directory = '.' # przypadek, gdy podano tylko nazwę pliku
settings = parse_settings(directory) settings = parse_settings(directory)
# Nadpisz domyślne wartości, jeśli istnieją w pliku nastaw # Nadpisz domyślne wartości, jeśli istnieją w pliku nastaw
@@ -106,6 +141,10 @@ PRZEKLADNIA_EFF = PRZEKLADNIA_NAPIECIA / PRZEKLADNIA
cfg_file = f"{base_name}.cfg" cfg_file = f"{base_name}.cfg"
dat_file = f"{base_name}.dat" dat_file = f"{base_name}.dat"
if not os.path.exists(cfg_file):
print(f"BŁĄD: Plik konfiguracyjny {cfg_file} nie istnieje.")
sys.exit(1)
print(f"Wczytywanie rejestracji: {cfg_file}") print(f"Wczytywanie rejestracji: {cfg_file}")
rec = Comtrade() rec = Comtrade()
@@ -123,7 +162,7 @@ for encoding in encodings:
continue continue
if not loaded: if not loaded:
print(f"BŁĄD: Nie można wczytać pliku {cfg_file}") print(f"BŁĄD: Nie można wczytać pliku {cfg_file} z żadnym ze znanych kodowań.")
sys.exit(1) sys.exit(1)
# Sprawdz czy mamy wystarczajaco kanalow analogowych # Sprawdz czy mamy wystarczajaco kanalow analogowych
@@ -212,7 +251,6 @@ else:
# Utworzenie relay z nastawami z pliku # Utworzenie relay z nastawami z pliku
relay = DistanceRelay(Z_line_R=Z_line_R, Z_line_X=Z_line_X, line_angle=line_angle, kierunek=KIERUNEK, settings=settings) relay = DistanceRelay(Z_line_R=Z_line_R, Z_line_X=Z_line_X, line_angle=line_angle, kierunek=KIERUNEK, settings=settings)
# ... (reszta skryptu bez zmian) ...
# Macierz operatora obrotu dla składowych symetrycznych # Macierz operatora obrotu dla składowych symetrycznych
a = complex(-0.5, np.sqrt(3)/2) a = complex(-0.5, np.sqrt(3)/2)
a2 = complex(-0.5, -np.sqrt(3)/2) a2 = complex(-0.5, -np.sqrt(3)/2)
@@ -225,38 +263,67 @@ trip_history_L1 = []
trip_history_L2 = [] trip_history_L2 = []
trip_history_L3 = [] trip_history_L3 = []
# Impedance phasors (R + jX) z1_r_history, z1_x_history = [], []
z1_r_history = [] z2_r_history, z2_x_history = [], []
z1_x_history = [] z3_r_history, z3_x_history = [], []
z2_r_history = []
z2_x_history = []
z3_r_history = []
z3_x_history = []
def calculate_impedance(u_cpx, i_cpx): def calculate_impedance(u_cpx, i_cpx):
"""Oblicza impedancję Z = U/I jako liczbę zespoloną""" """Oblicza impedancję Z = U/I jako liczbę zespoloną"""
# Konwersja do wartości wtórnych i_cpx_sec = complex(i_cpx.real / PRZEKLADNIA, i_cpx.imag / PRZEKLADNIA)
i_cpx = complex(i_cpx.real / PRZEKLADNIA, i_cpx.imag / PRZEKLADNIA) u_cpx_sec = complex(u_cpx.real / PRZEKLADNIA_NAPIECIA, u_cpx.imag / PRZEKLADNIA_NAPIECIA)
u_cpx = complex(u_cpx.real / PRZEKLADNIA_NAPIECIA, u_cpx.imag / PRZEKLADNIA_NAPIECIA)
i_mag_sq = i_cpx.real**2 + i_cpx.imag**2 i_mag_sq = i_cpx_sec.real**2 + i_cpx_sec.imag**2
if i_mag_sq < 1e-9: # Zabezpieczenie przed dzieleniem przez zero if i_mag_sq < 1e-9:
return 0.0, 0.0 return 0.0, 0.0
# Z = U / I = U * conj(I) / |I|^2 z_cpx = u_cpx_sec * i_cpx_sec.conjugate() / i_mag_sq
z_cpx = u_cpx * complex(i_cpx.real, -i_cpx.imag) / i_mag_sq
return z_cpx.real, z_cpx.imag return z_cpx.real, z_cpx.imag
# Symulacja "czasu rzeczywistego" próbka po próbce # ===== Inicjalizacja podglądu na żywo =====
for i in range(N, len(t)): live_plot_fig = None
# Pobranie okna danych (historyczne N próbek aż do obecnej chwili i) live_ax = None
window_i1 = i1_raw[i-N:i] live_lines = {}
window_i2 = i2_raw[i-N:i] live_data = {}
window_i3 = i3_raw[i-N:i]
window_u1 = u1_raw[i-N:i] if args.live_plot:
window_u2 = u2_raw[i-N:i] plt.ion()
window_u3 = u3_raw[i-N:i] live_plot_fig, live_ax = plt.subplots(figsize=(8, 8), subplot_kw={'projection': 'polar'})
live_data = {'L1': ([], []), 'L2': ([], []), 'L3': ([], [])} # Kąty, Moduły
if args.live_plot == 'z':
live_ax.set_title('Podgląd wektorów impedancji na żywo')
line_angle_rad = math.radians(line_angle)
live_ax.plot([line_angle_rad, line_angle_rad], [0, Z_line_mag], color='k', linestyle='--', linewidth=2, label=f'Linia Z ({line_angle:.1f} deg)')
rmax = getattr(relay, 'Z3_R', getattr(relay, 'Z5_R', 20)) * 1.5
live_ax.set_rmax(rmax)
elif args.live_plot == 'u':
live_ax.set_title('Podgląd wektorów napięć na żywo')
data_max = max(d.max() for d in (u1_raw, u2_raw, u3_raw) if len(d) > 0)
live_ax.set_rmax(data_max * 1.2 if data_max > 0 else 1)
elif args.live_plot == 'i':
live_ax.set_title('Podgląd wektorów prądów na żywo')
data_max = max(d.max() for d in (i1_raw, i2_raw, i3_raw) if len(d) > 0)
live_ax.set_rmax(data_max * 1.2 if data_max > 0 else 1)
# Wspólna konfiguracja dla wszystkich wykresów wektorowych
live_lines['L1'], = live_ax.plot([], [], 'b.', markersize=7, label='L1')
live_lines['L2'], = live_ax.plot([], [], 'g.', markersize=7, label='L2')
live_lines['L3'], = live_ax.plot([], [], 'o', color='orange', markerfacecolor='none', markersize=7, label='L3')
live_ax.legend(loc='upper right', fontsize=8)
live_ax.grid(True)
live_plot_fig.tight_layout()
live_plot_fig.canvas.draw()
live_plot_fig.canvas.flush_events()
# ==========================================
# Główna pętla symulacji
for i in range(N, len(t)):
window_i1, window_i2, window_i3 = i1_raw[i-N:i], i2_raw[i-N:i], i3_raw[i-N:i]
window_u1, window_u2, window_u3 = u1_raw[i-N:i], u2_raw[i-N:i], u3_raw[i-N:i]
# Filtracja DFT dla wszystkich faz
i1_re, i1_im = fcdft(window_i1) i1_re, i1_im = fcdft(window_i1)
i2_re, i2_im = fcdft(window_i2) i2_re, i2_im = fcdft(window_i2)
i3_re, i3_im = fcdft(window_i3) i3_re, i3_im = fcdft(window_i3)
@@ -264,21 +331,14 @@ for i in range(N, len(t)):
u2_re, u2_im = fcdft(window_u2) u2_re, u2_im = fcdft(window_u2)
u3_re, u3_im = fcdft(window_u3) u3_re, u3_im = fcdft(window_u3)
# Tworzenie liczb zespolonych I1_cpx, I2_cpx, I3_cpx = complex(i1_re, i1_im), complex(i2_re, i2_im), complex(i3_re, i3_im)
I1_cpx = complex(i1_re, i1_im) U1_cpx, U2_cpx, U3_cpx = complex(u1_re, u1_im), complex(u2_re, u2_im), complex(u3_re, u3_im)
I2_cpx = complex(i2_re, i2_im)
I3_cpx = complex(i3_re, i3_im)
U1_cpx = complex(u1_re, u1_im)
U2_cpx = complex(u2_re, u2_im)
U3_cpx = complex(u3_re, u3_im)
# Obliczanie składowych symetrycznych
I0_cpx = (I1_cpx + I2_cpx + I3_cpx) / 3.0 I0_cpx = (I1_cpx + I2_cpx + I3_cpx) / 3.0
I1zg_cpx = (I1_cpx + a * I2_cpx + a2 * I3_cpx) / 3.0 I1zg_cpx = (I1_cpx + a * I2_cpx + a2 * I3_cpx) / 3.0
U0_cpx = (U1_cpx + U2_cpx + U3_cpx) / 3.0 U0_cpx = (U1_cpx + U2_cpx + U3_cpx) / 3.0
U1zg_cpx = (U1_cpx + a * U2_cpx + a2 * U3_cpx) / 3.0 U1zg_cpx = (U1_cpx + a * U2_cpx + a2 * U3_cpx) / 3.0
# Przekazanie danych do algorytmu zabezpieczeniowego
trip_l1 = relay.step_relay('L1', U1_cpx.real, U1_cpx.imag, I1_cpx.real, I1_cpx.imag, I0_cpx.real, I0_cpx.imag, U0_cpx.real, U0_cpx.imag, U1zg_cpx.real, U1zg_cpx.imag, I1zg_cpx.real, I1zg_cpx.imag) trip_l1 = relay.step_relay('L1', U1_cpx.real, U1_cpx.imag, I1_cpx.real, I1_cpx.imag, I0_cpx.real, I0_cpx.imag, U0_cpx.real, U0_cpx.imag, U1zg_cpx.real, U1zg_cpx.imag, I1zg_cpx.real, I1zg_cpx.imag)
trip_l2 = relay.step_relay('L2', U2_cpx.real, U2_cpx.imag, I2_cpx.real, I2_cpx.imag, I0_cpx.real, I0_cpx.imag, U0_cpx.real, U0_cpx.imag, U1zg_cpx.real, U1zg_cpx.imag, I1zg_cpx.real, I1zg_cpx.imag) trip_l2 = relay.step_relay('L2', U2_cpx.real, U2_cpx.imag, I2_cpx.real, I2_cpx.imag, I0_cpx.real, I0_cpx.imag, U0_cpx.real, U0_cpx.imag, U1zg_cpx.real, U1zg_cpx.imag, I1zg_cpx.real, I1zg_cpx.imag)
trip_l3 = relay.step_relay('L3', U3_cpx.real, U3_cpx.imag, I3_cpx.real, I3_cpx.imag, I0_cpx.real, I0_cpx.imag, U0_cpx.real, U0_cpx.imag, U1zg_cpx.real, U1zg_cpx.imag, I1zg_cpx.real, I1zg_cpx.imag) trip_l3 = relay.step_relay('L3', U3_cpx.real, U3_cpx.imag, I3_cpx.real, I3_cpx.imag, I0_cpx.real, I0_cpx.imag, U0_cpx.real, U0_cpx.imag, U1zg_cpx.real, U1zg_cpx.imag, I1zg_cpx.real, I1zg_cpx.imag)
@@ -298,9 +358,55 @@ for i in range(N, len(t)):
z3_r_history.append(z3_r) z3_r_history.append(z3_r)
z3_x_history.append(z3_x) z3_x_history.append(z3_x)
# 5. Rysowanie wyników # ===== Aktualizacja podglądu na żywo =====
if args.live_plot and live_plot_fig:
if args.live_plot == 'z':
z1_mag, z1_ang = math.sqrt(z1_r**2 + z1_x**2), math.atan2(z1_x, z1_r)
z2_mag, z2_ang = math.sqrt(z2_r**2 + z2_x**2), math.atan2(z2_x, z2_r)
z3_mag, z3_ang = math.sqrt(z3_r**2 + z3_x**2), math.atan2(z3_x, z3_r)
if z1_mag > 0.01: live_data['L1'][0].append(z1_ang); live_data['L1'][1].append(z1_mag)
if z2_mag > 0.01: live_data['L2'][0].append(z2_ang); live_data['L2'][1].append(z2_mag)
if z3_mag > 0.01: live_data['L3'][0].append(z3_ang); live_data['L3'][1].append(z3_mag)
elif args.live_plot == 'u':
u1_mag, u1_ang = abs(U1_cpx), np.angle(U1_cpx)
u2_mag, u2_ang = abs(U2_cpx), np.angle(U2_cpx)
u3_mag, u3_ang = abs(U3_cpx), np.angle(U3_cpx)
live_data['L1'][0].append(u1_ang); live_data['L1'][1].append(u1_mag)
live_data['L2'][0].append(u2_ang); live_data['L2'][1].append(u2_mag)
live_data['L3'][0].append(u3_ang); live_data['L3'][1].append(u3_mag)
elif args.live_plot == 'i':
i1_mag, i1_ang = abs(I1_cpx), np.angle(I1_cpx)
i2_mag, i2_ang = abs(I2_cpx), np.angle(I2_cpx)
i3_mag, i3_ang = abs(I3_cpx), np.angle(I3_cpx)
live_data['L1'][0].append(i1_ang); live_data['L1'][1].append(i1_mag)
live_data['L2'][0].append(i2_ang); live_data['L2'][1].append(i2_mag)
live_data['L3'][0].append(i3_ang); live_data['L3'][1].append(i3_mag)
# Wspólna aktualizacja dla wszystkich wykresów wektorowych
live_lines['L1'].set_data(live_data['L1'])
live_lines['L2'].set_data(live_data['L2'])
live_lines['L3'].set_data(live_data['L3'])
live_ax.set_title(f'Podgląd na żywo (próbka {i+1}/{len(t)})')
live_plot_fig.canvas.draw()
live_plot_fig.canvas.flush_events()
if args.speed > 0:
plt.pause(args.speed)
# ==========================================
if args.live_plot:
plt.ioff()
print("Podgląd na żywo zakończony. Zostanie wyświetlony końcowy raport graficzny.")
plt.show() # Utrzymaj okno podglądu otwarte
# ===== Rysowanie końcowych wyników =====
print("Generowanie końcowego raportu graficznego...")
plt.figure(figsize=(16, 12)) plt.figure(figsize=(16, 12))
# ... (reszta kodu rysującego i generującego raporty bez zmian, ponieważ bazuje na obiekcie `relay`, który jest już poprawnie skonfigurowany)
# Prądy faz L1, L2, L3 # Prądy faz L1, L2, L3
plt.subplot(3, 2, 1) plt.subplot(3, 2, 1)
plt.plot(t[N:], i1_raw[N:], label='I_L1', color='blue') plt.plot(t[N:], i1_raw[N:], label='I_L1', color='blue')
@@ -314,33 +420,31 @@ plt.grid(True)
# Charakterystyka R-X # Charakterystyka R-X
plt.subplot(3, 2, 2) plt.subplot(3, 2, 2)
angle_rad = math.radians(relay.angle_r1) angle_rad = math.radians(relay.line_angle)
# Strefa 1
if hasattr(relay, 'Z1_R'): if hasattr(relay, 'Z1_R'):
plt.fill_between(np.linspace(0, relay.Z1_R, 2), -relay.Z1_X, relay.Z1_X, alpha=0.2, color='green', label='Strefa 1') plt.fill_between([0, relay.Z1_R], relay.Z1_X, -relay.Z1_X, color='green', alpha=0.2, label='Strefa 1')
# Strefa 2
if hasattr(relay, 'Z2_R'): if hasattr(relay, 'Z2_R'):
plt.fill_between(np.linspace(0, relay.Z2_R, 2), -relay.Z2_X, relay.Z2_X, alpha=0.15, color='yellow', label='Strefa 2') plt.fill_between([0, relay.Z2_R], relay.Z2_X, -relay.Z2_X, color='yellow', alpha=0.15, label='Strefa 2')
# Strefa 3
if hasattr(relay, 'Z3_R'): if hasattr(relay, 'Z3_R'):
plt.fill_between(np.linspace(0, relay.Z3_R, 2), -relay.Z3_X, relay.Z3_X, alpha=0.1, color='red', label='Strefa 3') plt.fill_between([0, relay.Z3_R], relay.Z3_X, -relay.Z3_X, color='red', alpha=0.1, label='Strefa 3')
z_line = np.linspace(0, relay.Z_line_mag * 1.5, 50) z_line = np.linspace(0, relay.Z_line_mag * 1.5, 50)
x_line = z_line * math.tan(angle_rad) x_line = z_line * math.tan(angle_rad)
plt.plot(z_line, x_line, 'k--', linewidth=1, label='Linia Z') plt.plot(z_line, x_line, 'k--', linewidth=1, label=f'Linia Z ({line_angle:.1f} deg)')
plt.plot(z1_r_history, z1_x_history, 'b.', markersize=1, alpha=0.5, label='Trajektoria L1') plt.plot(z1_r_history, z1_x_history, 'b.', markersize=1, alpha=0.5, label='Trajektoria L1')
plt.plot(z2_r_history, z2_x_history, 'g.', markersize=1, alpha=0.5, label='Trajektoria L2') plt.plot(z2_r_history, z2_x_history, 'g.', markersize=1, alpha=0.5, label='Trajektoria L2')
plt.plot(z3_r_history, z3_x_history, 'o', color='orange', markersize=1, alpha=0.5, label='Trajektoria L3') plt.plot(z3_r_history, z3_x_history, 'o', color='orange', markersize=1, alpha=0.5, label='Trajektoria L3')
xlim_max = (hasattr(relay, 'Z3_R') and relay.Z3_R or (hasattr(relay, 'Z5_R') and relay.Z5_R or 10)) xlim_max = getattr(relay, 'Z3_R', getattr(relay, 'Z5_R', 20))
ylim_max = (hasattr(relay, 'Z3_X') and relay.Z3_X or (hasattr(relay, 'Z5_X') and relay.Z5_X or 20)) ylim_max = getattr(relay, 'Z3_X', getattr(relay, 'Z5_X', 40))
plt.xlim(-1, xlim_max * 1.2) plt.xlim(max(-5, -xlim_max * 0.2), xlim_max * 1.2)
plt.ylim(-ylim_max, ylim_max * 1.2) plt.ylim(max(-5, -ylim_max * 1.2), ylim_max * 1.2)
plt.xlabel('R [Ohm]') plt.xlabel('R [Ohm]')
plt.ylabel('X [Ohm]') plt.ylabel('X [Ohm]')
plt.title('Charakterystyka R-X zabezpieczenia odleglosciowego') plt.title('Charakterystyka R-X zabezpieczenia odleglosciowego')
plt.legend(loc='upper right', fontsize=8) plt.legend(loc='upper right', fontsize=8)
plt.grid(True) plt.grid(True)
plt.gca().set_aspect('equal', adjustable='box')
plt.axhline(y=0, color='k', linewidth=0.5) plt.axhline(y=0, color='k', linewidth=0.5)
plt.axvline(x=0, color='k', linewidth=0.5) plt.axvline(x=0, color='k', linewidth=0.5)
@@ -371,13 +475,17 @@ plt.grid(True)
# Wyjscia zabezpieczenia # Wyjscia zabezpieczenia
plt.subplot(3, 2, 5) plt.subplot(3, 2, 5)
max_val = max(max(i1_raw), max(i2_raw), max(i3_raw)) if any(i1_raw) else 1 max_i = max(np.max(i1_raw), np.max(i2_raw), np.max(i3_raw)) if len(i1_raw)>0 else 1
min_i = min(np.min(i1_raw), np.min(i2_raw), np.min(i3_raw)) if len(i1_raw)>0 else -1
trip_plot_height = max_i if max_i > abs(min_i) else abs(min_i)
trip_plot_height = trip_plot_height if trip_plot_height > 0 else 1.0
plt.plot(t[N:], i1_raw[N:], label='I_L1', color='blue', alpha=0.5) plt.plot(t[N:], i1_raw[N:], label='I_L1', color='blue', alpha=0.5)
plt.plot(t[N:], i2_raw[N:], label='I_L2', color='green', alpha=0.5) plt.plot(t[N:], i2_raw[N:], label='I_L2', color='green', alpha=0.5)
plt.plot(t[N:], i3_raw[N:], label='I_L3', color='orange', alpha=0.5) plt.plot(t[N:], i3_raw[N:], label='I_L3', color='orange', alpha=0.5)
plt.plot(t[N:], np.array(trip_history_L1) * max_val, label='Trip L1-E', color='red', linewidth=2) plt.plot(t[N:], np.array(trip_history_L1) * trip_plot_height, label='Trip L1-E', color='red', linewidth=2)
plt.plot(t[N:], np.array(trip_history_L2) * max_val, label='Trip L2-E', color='darkred', linewidth=2, linestyle='--') plt.plot(t[N:], np.array(trip_history_L2) * trip_plot_height, label='Trip L2-E', color='darkred', linewidth=2, linestyle='--')
plt.plot(t[N:], np.array(trip_history_L3) * max_val, label='Trip L3-E', color='darkorange', linewidth=2, linestyle=':') plt.plot(t[N:], np.array(trip_history_L3) * trip_plot_height, label='Trip L3-E', color='darkorange', linewidth=2, linestyle=':')
plt.title(f'Wynik testu algorytmu {ALGORITHM_NAME}') plt.title(f'Wynik testu algorytmu {ALGORITHM_NAME}')
plt.xlabel('Czas [s]') plt.xlabel('Czas [s]')
plt.ylabel('Wartosc') plt.ylabel('Wartosc')
@@ -392,6 +500,14 @@ z3_mag = np.sqrt(np.array(z3_r_history)**2 + np.array(z3_x_history)**2)
plt.plot(t[N:], z1_mag, label='|Z_L1|', color='blue') plt.plot(t[N:], z1_mag, label='|Z_L1|', color='blue')
plt.plot(t[N:], z2_mag, label='|Z_L2|', color='green') plt.plot(t[N:], z2_mag, label='|Z_L2|', color='green')
plt.plot(t[N:], z3_mag, label='|Z_L3|', color='orange') plt.plot(t[N:], z3_mag, label='|Z_L3|', color='orange')
if hasattr(relay, 'z1_reach'): # Dla algorytmu bazującego na procencie linii
z1_val = relay.z1_reach * relay.Z_line_mag
plt.axhline(y=z1_val, color='green', linestyle='--', label=f'Z1 ({relay.z1_reach*100:.0f}%)')
z2_val = relay.z2_reach * relay.Z_line_mag
plt.axhline(y=z2_val, color='orange', linestyle='--', label=f'Z2 ({relay.z2_reach*100:.0f}%)')
z3_val = relay.z3_reach * relay.Z_line_mag
plt.axhline(y=z3_val, color='red', linestyle='--', label=f'Z3 ({relay.z3_reach*100:.0f}%)')
else: # Dla algorytmu bazującego na R i X
if hasattr(relay, 'Z1_R'): if hasattr(relay, 'Z1_R'):
z1_reach = np.sqrt(relay.Z1_R**2 + relay.Z1_X**2) z1_reach = np.sqrt(relay.Z1_R**2 + relay.Z1_X**2)
plt.axhline(y=z1_reach, color='green', linestyle='--', label='Z1 reach') plt.axhline(y=z1_reach, color='green', linestyle='--', label='Z1 reach')
@@ -401,33 +517,39 @@ if hasattr(relay, 'Z2_R'):
if hasattr(relay, 'Z3_R'): if hasattr(relay, 'Z3_R'):
z3_reach = np.sqrt(relay.Z3_R**2 + relay.Z3_X**2) z3_reach = np.sqrt(relay.Z3_R**2 + relay.Z3_X**2)
plt.axhline(y=z3_reach, color='red', linestyle='--', label='Z3 reach') plt.axhline(y=z3_reach, color='red', linestyle='--', label='Z3 reach')
plt.title('Modul impedancji |Z|') plt.title('Modul impedancji |Z|')
plt.xlabel('Czas [s]') plt.xlabel('Czas [s]')
plt.ylabel('|Z| [Ohm]') plt.ylabel('|Z| [Ohm]')
plt.legend() plt.legend()
plt.grid(True) plt.grid(True)
plt.ylim(bottom=0)
plt.tight_layout() plt.tight_layout()
output_filename = f"{os.path.splitext(base_name)[0]}_result.png" output_filename = f"{os.path.splitext(base_name)[0]}_result_{ALGORITHM_NAME}.png"
plt.savefig(output_filename) plt.savefig(output_filename)
print(f"Wynik zapisany do {output_filename}") print(f"Wynik zapisany do {output_filename}")
# Generowanie pliku rezultat.md # Generowanie pliku rezultat.md
def generate_result_md(t, trip_history_L1, trip_history_L2, trip_history_L3, def generate_result_md(t, trip_history_L1, trip_history_L2, trip_history_L3,
z1_r_history, z1_x_history, relay, base_name): z_history, relay, base_name):
"""Generuje plik rezultat.md z informacjami o wykryciu zwarcia""" """Generuje plik rezultat.md z informacjami o wykryciu zwarcia"""
z1_r_hist, z1_x_hist = z_history['L1']
md_content = [f"# Wynik analizy dla {os.path.basename(base_name)}", ""] md_content = [f"# Wynik analizy dla {os.path.basename(base_name)}", ""]
md_content.append(f"## Parametry zabezpieczenia ({ALGORITHM_DESC})") md_content.append(f"## Parametry zabezpieczenia ({ALGORITHM_DESC})")
md_content.append(f"- Impedancja linii: R={relay.Z_line_R:.2f} Ohm, X={relay.Z_line_X:.2f} Ohm") md_content.append(f"- Impedancja linii: R={relay.Z_line_R:.2f} Ohm, X={relay.Z_line_X:.2f} Ohm")
md_content.append(f"- Kat linii: {relay.line_angle:.1f} st.") md_content.append(f"- Kat linii: {relay.line_angle:.1f} st.")
if hasattr(relay, 'z1_reach'):
# Dodanie informacji o strefach w zależności od typu algorytmu
if hasattr(relay, 'z1_reach'): # algorytm %
md_content.append(f"- Strefa 1: {relay.z1_reach*100:.0f}% ({relay.t_z1}ms)") md_content.append(f"- Strefa 1: {relay.z1_reach*100:.0f}% ({relay.t_z1}ms)")
md_content.append(f"- Strefa 2: {relay.z2_reach*100:.0f}% ({relay.t_z2}ms)") md_content.append(f"- Strefa 2: {relay.z2_reach*100:.0f}% ({relay.t_z2}ms)")
md_content.append(f"- Strefa 3: {relay.z3_reach*100:.0f}% ({relay.t_z3}ms)") md_content.append(f"- Strefa 3: {relay.z3_reach*100:.0f}% ({relay.t_z3}ms)")
else: else: # algorytm R/X
md_content.append(f"- Strefa 1: R={relay.Z1_R:.2f} Ohm, X={relay.Z1_X:.2f} Ohm ({relay.tZ1}ms)") md_content.append(f"- Strefa 1: R={getattr(relay, 'Z1_R', 'N/A'):.2f} Ohm, X={getattr(relay, 'Z1_X', 'N/A'):.2f} Ohm ({getattr(relay, 'tZ1', 'N/A')}ms)")
md_content.append(f"- Strefa 2: R={relay.Z2_R:.2f} Ohm, X={relay.Z2_X:.2f} Ohm ({relay.tZ2}ms)") md_content.append(f"- Strefa 2: R={getattr(relay, 'Z2_R', 'N/A'):.2f} Ohm, X={getattr(relay, 'Z2_X', 'N/A'):.2f} Ohm ({getattr(relay, 'tZ2', 'N/A')}ms)")
md_content.append(f"- Strefa 3: R={relay.Z3_R:.2f} Ohm, X={relay.Z3_X:.2f} Ohm ({relay.tZ3}ms)") md_content.append(f"- Strefa 3: R={getattr(relay, 'Z3_R', 'N/A'):.2f} Ohm, X={getattr(relay, 'Z3_X', 'N/A'):.2f} Ohm ({getattr(relay, 'tZ3', 'N/A')}ms)")
md_content.append("") md_content.append("")
trip_any = any(trip_history_L1) or any(trip_history_L2) or any(trip_history_L3) trip_any = any(trip_history_L1) or any(trip_history_L2) or any(trip_history_L3)
@@ -435,6 +557,7 @@ def generate_result_md(t, trip_history_L1, trip_history_L2, trip_history_L3,
if trip_any: if trip_any:
md_content.append("## Wykrycie zwarcia: TAK") md_content.append("## Wykrycie zwarcia: TAK")
fault_time, fault_phase, fault_idx = -1, "Brak", -1 fault_time, fault_phase, fault_idx = -1, "Brak", -1
for i, (t1, t2, t3) in enumerate(zip(trip_history_L1, trip_history_L2, trip_history_L3)): for i, (t1, t2, t3) in enumerate(zip(trip_history_L1, trip_history_L2, trip_history_L3)):
if t1 or t2 or t3: if t1 or t2 or t3:
fault_time = t[N + i] fault_time = t[N + i]
@@ -447,9 +570,9 @@ def generate_result_md(t, trip_history_L1, trip_history_L2, trip_history_L3,
md_content.append(f"- Czas wykrycia: {fault_time * 1000:.2f} ms") md_content.append(f"- Czas wykrycia: {fault_time * 1000:.2f} ms")
md_content.append(f"- Faza: {fault_phase}") md_content.append(f"- Faza: {fault_phase}")
z_r, z_x = z1_r_history[fault_idx], z1_x_history[fault_idx] z_r, z_x = z1_r_hist[fault_idx], z1_x_hist[fault_idx]
z_mag = math.sqrt(z_r**2 + z_x**2) z_mag = math.sqrt(z_r**2 + z_x**2)
z_angle = math.degrees(math.atan2(z_x, z_r)) z_angle = math.degrees(math.atan2(z_x, z_r)) if z_mag > 0 else 0
md_content.append("## Wartości w momencie zwarcia") md_content.append("## Wartości w momencie zwarcia")
md_content.append(f"- R = {z_r:.4f} Ohm, X = {z_x:.4f} Ohm") md_content.append(f"- R = {z_r:.4f} Ohm, X = {z_x:.4f} Ohm")
@@ -457,13 +580,17 @@ def generate_result_md(t, trip_history_L1, trip_history_L2, trip_history_L3,
else: else:
md_content.append("## Wykrycie zwarcia: NIE") md_content.append("## Wykrycie zwarcia: NIE")
report_filename = f"{os.path.splitext(base_name)[0]}_rezultat.md" report_filename = f"{os.path.splitext(base_name)[0]}_rezultat_{ALGORITHM_NAME}.md"
with open(report_filename, 'w', encoding='utf-8') as f: with open(report_filename, 'w', encoding='utf-8') as f:
f.write("\n".join(md_content)) f.write("\n".join(md_content))
print(f"Raport zapisany do {report_filename}") print(f"Raport tekstowy zapisany do {report_filename}")
z_history_all = {
'L1': (z1_r_history, z1_x_history),
'L2': (z2_r_history, z2_x_history),
'L3': (z3_r_history, z3_x_history)
}
generate_result_md(t, trip_history_L1, trip_history_L2, trip_history_L3, generate_result_md(t, trip_history_L1, trip_history_L2, trip_history_L3,
z1_r_history, z1_x_history, relay, base_name) z_history_all, relay, base_name)
print("Analiza zakończona.") print("Analiza zakończona.")