├── .gitignore ├── README.md ├── Readme ├── cdf.txt └── psap.txt ├── __init__.py ├── base.py ├── config.json ├── decentralized ├── __init__.py ├── distributed_linear_powergrid.py ├── distributed_nonlinear_powergrid.py └── estimators.py ├── extract_config.py ├── linear_powergrid.py ├── main.py ├── nonlinear_powergrid.py ├── powergridbase.py ├── requirements.txt ├── rules ├── rule_WSNs_branch ├── rule_WSNs_nodes ├── rule_ieeecdf_branch ├── rule_ieeecdf_bus ├── rule_ieeepsap_bus └── rule_ieeepsap_line └── topology ├── WSNs4.txt ├── WSNs8.txt ├── ieee118cdf.txt ├── ieee118psp.txt ├── ieee14cdf.txt ├── ieee300cdf.txt ├── ieee30cdf.txt └── ieee57cdf.txt /.gitignore: -------------------------------------------------------------------------------- 1 | /save 2 | /__pycache__ 3 | */__pycache__ 4 | /.history 5 | /.vscode 6 | Introduction 7 | /cache/*.json 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PowerGrid 2 | ## Introduction 3 | 4 | Power grid simulator, in order to implement centralized and distributed [[1]](https://www.sciencedirect.com/science/article/pii/S0005109814004580) least square state estimation and bad data detection. Also, simplest False data injection Attack. 5 | 6 | This project up to now have built a DC/AC power grid simulator environment with IEEE 118-bus system (other systems will be added to this project for soon once I find relavent documents), it can 7 | 8 | - Least square state estimation 9 | - Distributed least square state estimation 10 | - Simplest FDI attack 11 | 12 | 13 | ## Require 14 | 15 | `pip install -r requirements.txt` 16 | 17 | ## Framework need to do 18 | 19 | - [ ] IEEE 300 bus system 20 | 21 | - [ ] Classical kalman filter state estimation (Centralized and Distributed) 22 | 23 | - [ ] Make the algorithm more suitable for distribution 24 | 25 | 26 | ## Alogrithm need to do 27 | 28 | - [ ] Other classical distributed state estimation algorithms 29 | - [ ] Asychronism type of these distributed algorithms 30 | - [ ] FDI Attack with implement topology information [[2]](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.714.756&rep=rep1&type=pdf) 31 | - [ ] FDI Defend [[5]](https://ptolemy.berkeley.edu/projects/truststc/conferences/10/CPSWeek/papers/scs1_paper_2.pdf) 32 | - [ ] FDI Detection by sparse optimization [[6]](https://sci-hub.se/https://ieeexplore.ieee.org/abstract/document/6740901/), machine learning [[7]](https://sci-hub.se/https://ieeexplore.ieee.org/abstract/document/7063894/) 33 | 34 | ## Have done 35 | 36 | - [x] A better GUI (FDI configuration aquired) 37 | 38 | - [x] Communication delay 39 | 40 | - [x] IEEE $< 118$ bus systems 41 | 42 | - [x] Dynamic (time varying state space equation) power system 43 | - [x] AC measure model 44 | 45 | - [x] WSNs model 46 | 47 | - [x] A crude GUI 48 | 49 | - [x] Richardson based distributed algorithm ((A)sychronism) 50 | - [x] Stochastic based distributed algorithm 51 | 52 | - [x] FDI Attack with only measurement information (ICA, PCA) [[3]](https://sci-hub.se/https://ieeexplore.ieee.org/abstract/document/6102326/) [[4]](https://sci-hub.se/https://ieeexplore.ieee.org/abstract/document/7001709/) 53 | -------------------------------------------------------------------------------- /Readme/cdf.txt: -------------------------------------------------------------------------------- 1 | Partial Description of the IEEE Common Data Format for the 2 | Exchange of Solved Load Flow Data 3 | 4 | The complete description can be found in the paper "Common Data 5 | Format for the Exchange of Solved Load Flow Data", Working Group on a 6 | Common Format for the Exchange of Solved Load Flow Data, _IEEE 7 | Transactions on Power Apparatus and Systems_, Vol. PAS-92, No. 6, 8 | November/December 1973, pp. 1916-1925. 9 | 10 | The data file has lines of up to 128 characters. The lines are grouped 11 | into sections with section headers. Data items are entered in specific 12 | columns. No blank items are allowed, enter zeros instead. Floating point 13 | items should have explicit decimal point. No implicit decimal points 14 | are used. 15 | 16 | Data type codes: A - Alphanumeric (no special characters) 17 | I - Integer 18 | F - Floating point 19 | * - Mandatory item 20 | 21 | Title Data 22 | ========== 23 | 24 | First card in file. 25 | 26 | Columns 2- 9 Date, in format DD/MM/YY with leading zeros. If no date 27 | provided, use 0b/0b/0b where b is blank. 28 | 29 | Columns 11-30 Originator's name (A) 30 | 31 | Columns 32-37 MVA Base (F*) 32 | 33 | Columns 39-42 Year (I) 34 | 35 | Column 44 Season (S - Summer, W - Winter) 36 | 37 | Column 46-73 Case identification (A) 38 | 39 | Bus Data * 40 | ========== 41 | 42 | Section start card *: 43 | --------------------- 44 | 45 | Columns 1-16 BUS DATA FOLLOWS (not clear that any more than BUS in 46 | 1-3 is significant) * 47 | 48 | Columns ?- ? NNNNN ITEMS (column not clear, I would not count on this) 49 | 50 | Bus data cards *: 51 | ----------------- 52 | 53 | Columns 1- 4 Bus number (I) * 54 | Columns 7-17 Name (A) (left justify) * 55 | Columns 19-20 Load flow area number (I) Don't use zero! * 56 | Columns 21-23 Loss zone number (I) 57 | Columns 25-26 Type (I) * (0/2) 5 58 | 0 - Unregulated (load, PQ) (负载) 59 | 1 - Hold MVAR generation within voltage limits, (PQ) 60 | 2 - Hold voltage within VAR limits (gen, PV) (发电站) 61 | 3 - Hold voltage and angle (swing, V-Theta) (must always 62 | have one) (参考节点) 63 | Columns 28-33 Final voltage, p.u. (F) * (标幺电压) 6 64 | Columns 34-40 Final angle, degrees (F) * (相角) 7 65 | Columns 41-49 Load MW (F) * (负载有功功率) 8 66 | Columns 50-59 Load MVAR (F) * (负载无功功率) 9 67 | Columns 60-67 Generation MW (F) * (生成有功功率) 10 68 | Columns 68-75 Generation MVAR (F) * (生成无功功率) 11 69 | Columns 77-83 Base KV (F) (基本电压, 全是0) 12 70 | Columns 85-90 Desired volts (pu) (F) (This is desired remote voltage if 71 | this bus is controlling another bus. (目标电压) 72 | Columns 91-98 Maximum MVAR or voltage limit (F) (最大功率或电压) 13 73 | Columns 99-106 Minimum MVAR or voltage limit (F) (最小功率或电压) 14 74 | Columns 107-114 Shunt conductance G (per unit) (F) * (全是0) 16 75 | Columns 115-122 Shunt susceptance B (per unit) (F) * (分流电纳, 有一点有数值) 17 76 | Columns 124-127 Remote controlled bus number (数据里都是0) 18 77 | 78 | Section end card: 79 | ----------------- 80 | 81 | Columns 1- 4 -999 82 | 83 | Branch Data * 84 | ============= 85 | 86 | Section start card *: 87 | --------------------- 88 | 89 | Columns 1-16 BRANCH DATA FOLLOWS (not clear that any more than BRANCH 90 | is significant) * 91 | 92 | Columns 40?- ? NNNNN ITEMS (column not clear, I would not count on this) 93 | 94 | Branch data cards *: 95 | -------------------- 96 | 97 | Columns 1- 4 Tap bus number (I) * 98 | For transformers or phase shifters, the side of the model 99 | the non-unity tap is on 100 | Columns 6- 9 Z bus number (I) * 101 | For transformers and phase shifters, the side of the model 102 | the device impedance is on. 103 | Columns 11-12 Load flow area (I) (全是1) 2 104 | Columns 13-14 Loss zone (I) (全是1) 3 105 | Column 17 Circuit (I) * (Use 1 for single lines) (全是1, 说明没有回环) 4 106 | Column 19 Type (I) * (全是1/0) 5 107 | 0 - Transmission line 108 | 1 - Fixed tap 109 | 2 - Variable tap for voltage control (TCUL, LTC) 110 | 3 - Variable tap (turns ratio) for MVAR control 111 | 4 - Variable phase angle for MW control (phase shifter) 112 | Columns 20-29 Branch resistance R, per unit (F) * (标幺电阻) 6 113 | Columns 30-40 Branch reactance X, per unit (F) * No zero impedance lines (标幺电抗) 7 114 | Columns 41-50 Line charging B, per unit (F) * (total line charging, +B) (线路的无功充电功率 MVAR, ) 8 115 | Columns 51-55 Line MVA rating No 1 (I) Left justify! 116 | Columns 57-61 Line MVA rating No 2 (I) Left justify! 117 | Columns 63-67 Line MVA rating No 3 (I) Left justify! 118 | Columns 69-72 Control bus number (全是0) 119 | Column 74 Side (I) (全是0) 120 | 0 - Controlled bus is one of the terminals 121 | 1 - Controlled bus is near the tap side 122 | 2 - Controlled bus is near the impedance side (Z bus) 123 | Columns 77-82 Transformer final turns ratio (F) (变压器最终匝数比) 14 124 | Columns 84-90 Transformer (phase shifter) final angle (F) 125 | Columns 91-97 Minimum tap or phase shift (F) 126 | Columns 98-104 Maximum tap or phase shift (F) 127 | Columns 106-111 Step size (F) 128 | Columns 113-119 Minimum voltage, MVAR or MW limit (F) 129 | Columns 120-126 Maximum voltage, MVAR or MW limit (F) 130 | 131 | Section end card: 132 | ----------------- 133 | 134 | Columns 1- 4 -999 135 | 136 | Loss Zone Data 137 | ============== 138 | 139 | Section start card 140 | ------------------ 141 | 142 | Columns 1-16 LOSS ZONES FOLLOWS (not clear that any more than LOSS 143 | is significant) 144 | 145 | Columns 40?- ? NNNNN ITEMS (column not clear, I would not count on this) 146 | 147 | Loss Zone Cards: 148 | ---------------- 149 | 150 | Columns 1- 3 Loss zone number (I) 151 | Columns 5-16 Loss zone name (A) 152 | 153 | Section end card: 154 | ----------------- 155 | 156 | Columns 1- 3 -99 157 | 158 | Interchange Data * 159 | ================== 160 | 161 | Section start card 162 | ------------------ 163 | 164 | Columns 1-16 INTERCHANGE DATA FOLLOWS (not clear that any more than 165 | first word is significant). 166 | Columns 40?- ? NNNNN ITEMS (column not clear, I would not count on this) 167 | 168 | Interchange Data Cards *: 169 | ------------------------- 170 | 171 | Columns 1- 2 Area number (I) no zeros! * 172 | Columns 4- 7 Interchange slack bus number (I) * 173 | Columns 9-20 Alternate swing bus name (A) 174 | Columns 21-28 Area interchange export, MW (F) (+ = out) * 175 | Columns 30-35 Area interchange tolerance, MW (F) * 176 | Columns 38-43 Area code (abbreviated name) (A) * 177 | Columns 46-75 Area name (A) 178 | 179 | Section end card: 180 | ----------------- 181 | 182 | Columns 1- 2 -9 183 | 184 | Tie Line Data 185 | ============= 186 | 187 | Section start card 188 | ------------------ 189 | 190 | Columns 1-16 TIE LINES FOLLOW (not clear that any more than TIE 191 | is significant) 192 | 193 | Columns 40?- ? NNNNN ITEMS (column not clear, I would not count on this) 194 | 195 | Tie Line Cards: 196 | --------------- 197 | 198 | Columns 1- 4 Metered bus number (I) 199 | Columns 7-8 Metered area number (I) 200 | Columns 11-14 Non-metered bus number (I) 201 | Columns 17-18 Non-metered area number (I) 202 | Column 21 Circuit number 203 | 204 | Section end card: 205 | ----------------- 206 | 207 | Columns 1- 3 -999 208 | 209 | -------------------------------------------------------------------------------- /Readme/psap.txt: -------------------------------------------------------------------------------- 1 | PSAP File Format 2 | 3 | May 20, 1993 4 | 5 | The PECO PSAP File Format is fully described in the _PJM Power System 6 | Analysis Package Use's Guide_, available from the Philadelphia 7 | Electric Company. The following is a rough description of the 8 | most important parts of the format. 9 | 10 | A PSAP data file is divided into sections by code cards. The code is 11 | in the first three columns. There are something like 60 codes, of 12 | which only four are described in this document. 13 | 14 | The 1 code indicates that the next card is the case title. Only one 15 | title is allowed per case. 16 | 17 | The 4 card indicates that line data follows. The line data ends with 18 | a 9999 card. 19 | 20 | The 5 card indicates that bus data follows. The bus data ends with 21 | a 9999 card. 22 | 23 | The 15 card indicates that area interchange data follows. The data ends with 24 | a 9999 card. 25 | 26 | Line Data Card (Code 4 cards) 27 | ============================= 28 | 29 | Cols Data 30 | 1-4 From bus number (源节点) 0 31 | 6 Change code (blank in 4 section) 32 | 7 'C' if second card present for same line. Used for transformers. (有'C'就是变压器) 33 | 9-12 To bus number (目标节点) 3 34 | 14 Circuit number (blank in 4 section) 35 | 16 'T' or 'F' - Load flow area of bus at this end of line gets losses. 36 | 18-23 Line resistance in percent of base. (NOT per unit.) (线路电阻) 6 37 | (percent = 100 x per unit) Two default decimal places. 38 | 24-29 Line reactance, in percent. Two default decimal places. (线路电抗) 7 39 | 30-35 Line charging MVAR (total). Three default decimal places. 40 | 36-40 Transformer tap (per unit turns ratio). Three default decimal 41 | places, 1000 = 1.000. 42 | 41-45 Min tap, for OLTC. Three default decimal places. 43 | 46-50 Max tap, for OLTC. Three default decimal places. 44 | 51-55 Phase shift angle, for OL phase shifter. Two default decimal places. (移相角) 12 45 | 56-60 Remote voltage control bus number. Negative if lower tap increases 46 | voltage of this bus. 47 | 61-64 Normal MVA rating 48 | 65-68 Emergency MVA rating 49 | 69-72 MVA Base. Default value 100 MVA if blank. 50 | 51 | Second Line Card (follows 'C' in first card) 52 | ============================================ 53 | 54 | 1-17 Same as first card, except no 'C'. Can be left blank. 55 | 35-40 Desired MVAR flow or Min voltage setpoint for OLTC. 56 | 41-45 Min phase shifter degrees. Two default decimal places. 57 | 46-50 Max phase shifter degrees. Two default decimal places. 58 | 51-55 Desired MW flow for phase shifter. 59 | 57-60 Controlled line from bus. 60 | 62-65 Controlled line to bus. 61 | 67-70 Available taps (number of taps) 62 | 71-75 Maximum voltage setpoint. Three default decimal places. 63 | 64 | Bus Cards (Code 5 cards) 65 | ======================== 66 | 67 | 1-4 Bus number 68 | 6 Change code (blank in 5 section) 69 | 7 Continue code (blank in 5 section) 70 | 8 Regulated bus code: 71 | Blank - load (PQ) bus 72 | 1 - gen (PV) bus 73 | 2 - swing (V-Theta) bus 74 | 10-21 Name 75 | 23-26 Bus voltage (control setpoint or solved value). 76 | Three default decimal places. 77 | 27-30 Bus angle 78 | 31-35 Generation MW 79 | 36-40 Generation MVAR (from solution) 80 | 41-45 Generation MVAR low limit 81 | 46-50 Generation MVAR high limit 82 | 51-55 Bus at which generation controls voltage 83 | 56-60 Load MW 84 | 61-65 Load MVAR 85 | 66-70 Shunt MVAR. Reactors are minus. 86 | 71-72 Load flow area. (Used for area interchange and losses). 87 | 88 | Area Interchange Cards (Code 15 cards) 89 | ====================================== 90 | 91 | 3-4 Load flow area number 92 | 5-8 Swing bus for area interchange. Adjusts generation at this bus 93 | to meet area interchange requirement. 94 | 9-14 Area exports, MW. (+ = out of area) 95 | 15-19 Area Interchange tolerance, MW 96 | 20-55 Area name 97 | 56-60 Area load (usually left blank) 98 | 61-65 Area losses (usually left blank) 99 | 100 | 101 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huminan/PowerGrid/a4f09d786658c2160df1dd9fa434c45124e72db0/__init__.py -------------------------------------------------------------------------------- /base.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | import numpy as np 3 | #import scipy as sp 4 | import sympy as sb 5 | import matplotlib.pyplot as plt 6 | import matplotlib.pylab as pylab 7 | plt.rcParams['font.sans-serif'] = ['SimSun'] # 正常显示中文 8 | plt.rcParams['axes.unicode_minus'] = False # 正常显示负号 9 | plt.rcParams['figure.figsize'] = (2.36, 1.75) # 图片大小(英寸)(保证坐标框架为46*30mm:宽2.36->2.42) 10 | plt.rcParams['lines.linewidth'] = 0.75 # 线条宽度0.25mm(0.75pt) 11 | plt.rcParams['lines.markersize'] = 2.5 12 | plt.rcParams['font.size'] = 8.7 # 字体大小8.7pt(换算px) 13 | plt.rcParams['xtick.direction'] = 'in' # 坐标轴刻度朝内 14 | plt.rcParams['ytick.direction'] = 'in' 15 | 16 | from numpy import linalg 17 | from scipy.linalg import block_diag 18 | from sklearn.decomposition import PCA 19 | from decimal import Decimal 20 | from autograd import grad 21 | import os 22 | import json 23 | 24 | import extract_config 25 | 26 | class ComplexEncoder(json.JSONEncoder): 27 | def default(self, obj): 28 | # 如果要转换的对象是复数类型,程序负责处理 29 | if isinstance(obj, complex): 30 | return {"__complex__": 'true', 'real': obj.real, 'imag': obj.imag} 31 | # 对于其他类型,还使用JSONEncoder的默认处理 32 | return json.JSONEncoder.default(self, obj) 33 | 34 | def as_complex(dct): 35 | if '__complex__' in dct: 36 | return complex(dct['real'], dct['imag']) 37 | return dct 38 | 39 | #************************************************************ 40 | #* StateEstimationBase 类 -- 41 | #* 基类,初始化系统参数 42 | #* 有:电网模型参数,无线传感网参数 43 | #* 44 | #************************************************************ 45 | class StateEstimationBase(object): 46 | def __init__(self, pmu=[], conf_dict={}): 47 | # GUI 配置 48 | self.model_name = conf_dict['model_name'] 49 | self.size = int(conf_dict['network_size']) 50 | self.state_variance = float(conf_dict['state_change']) 51 | self.sim_time = int(conf_dict['sim_time']) 52 | self.pmu_voltage_variance = float(conf_dict['pmu_voltage_variance']) 53 | self.pmu_angle_variance = float(conf_dict['pmu_angle_variance']) 54 | self.scada_voltage_variance = float(conf_dict['scada_voltage_variance']) 55 | self.scada_power_variance = float(conf_dict['scada_power_variance']) 56 | # 非线性参数 57 | self.iter_time = int(conf_dict['nonlinear_iter_time']) 58 | self.stop_error = int(conf_dict['nonlinear_stop_error']) 59 | 60 | self.pmu = [] 61 | for i in range(self.size): 62 | if i in pmu: 63 | self.pmu.append('PMU') 64 | else: 65 | self.pmu.append('SCADA') 66 | # 状态轨迹 67 | self.A = np.mat(np.eye(2*self.size), dtype=complex) # 状态转移矩阵 68 | self.G = np.mat(np.zeros((2*self.size,1), dtype=complex)) # 状态轨迹向量 69 | # 声明 70 | self.bus_info_dict = {} 71 | self.GBBSH = [] 72 | self.branch = [] 73 | self.H = np.mat(np.zeros([0, 2*self.size]), dtype=complex) # 量测矩阵 74 | self.R_real = np.mat(np.zeros([0, 0]), dtype=complex) # 真实量测协方差矩阵 75 | self.R = np.mat(np.zeros([0, 0]), dtype=complex) # 计算量测协方差矩阵 76 | self.z_observed = np.mat(np.zeros([0,0])) # 量测值 77 | self.measure_who = [] # Indicate whose z: single i -> bus i 78 | # [i, i] -> pmu i 79 | # [i, j] -> branch i -> j 80 | self.x_who = list(range(1,self.size+1)) # Indicate whose x: per x has 2 members 81 | self.nodes_ref = [] 82 | # Defult the order of Bus 83 | # 标签 84 | self.is_distribute = False # Defult is a centralized system 85 | self.is_reference_deleted = False 86 | # 构建系统模型 87 | self.set_model() 88 | 89 | def set_model(self): 90 | if self.model_name == 'PowerGrid': 91 | # 为求偏导准备的符号 92 | G_ij = sb.symbols("G_ij") 93 | B_ij = sb.symbols("B_ij") 94 | BSH_ij = sb.symbols("BSH_ij") 95 | Vi = sb.symbols("V_i") 96 | Vj = sb.symbols("V_j") 97 | Theta_i = sb.symbols("Theta_i") 98 | Theta_j = sb.symbols("Theta_j") 99 | # alpha_ij = sb.symbols("alpha_ij") 不知道什么用 100 | # 边量测计算公式 101 | P_ij = G_ij*Vi**2 - G_ij*Vi*Vj*sb.cos(Theta_i - Theta_j) - B_ij*Vi*Vj*sb.sin(Theta_i - Theta_j) 102 | Q_ij = -(B_ij + BSH_ij)*Vi**2 + B_ij*Vi*Vj*sb.cos(Theta_i-Theta_j) - G_ij*Vi*Vj*sb.sin(Theta_i-Theta_j) 103 | self.h = [P_ij, Q_ij] 104 | self.value_symbol = [G_ij, B_ij, BSH_ij] 105 | self.state_i_symbol = [Vi, Theta_i] 106 | self.state_j_symbol = [Vj, Theta_j] 107 | self.jacobi_h_ij = self.__jacob(self.h, self.state_i_symbol) 108 | self.jacobi_h_ji = self.__jacob(self.h, self.state_j_symbol) 109 | #self.h_egde = [active_power, reactive_power] 110 | # 从配置文件提取 111 | if os.path.exists('cache/IEEE_'+str(self.size)+'_info.json') is True: # 从缓存提取 112 | with open('cache/IEEE_'+str(self.size)+'_info.json','r',encoding='utf-8') as f: 113 | saved_conf = json.load(f, object_hook=as_complex) 114 | self.x_real = np.mat(saved_conf['x_real']) 115 | self.bus_type = saved_conf['bus_type'] 116 | GBBSH = saved_conf['GBBSH'] 117 | bus = saved_conf['bus'] 118 | branch = saved_conf['branch'] 119 | self.bus_info_dict = self.set_nodes_info(bus,branch,self.pmu,GBBSH,'single') 120 | else: 121 | self.bus_cdf = extract_config.tools('./topology/ieee'+str(self.size)+'cdf.txt', './rules/rule_ieeecdf_bus', 0) 122 | self.branch_cdf = extract_config.tools('./topology/ieee'+str(self.size)+'cdf.txt', './rules/rule_ieeecdf_branch', 1) 123 | x_real = self.bus_cdf.get_items([6, 8]) #第一次获得的真实状态值 124 | x = np.mat(np.zeros([2*self.size,1])) 125 | for cnt,(i, j) in enumerate(zip(x_real[0], x_real[1])): 126 | x[2*cnt, 0] = i 127 | x[2*cnt+1, 0] = j 128 | self.x_real = x 129 | self.bus_type = self.bus_cdf.get_items(5) # Bus type: 0 -> Load 130 | # 2 -> Generation 131 | # 3 -> Reference 132 | self.__gen_gbbsh(is_ignore=False) 133 | bus = self.bus_cdf.get_items(0) 134 | self.bus_info_dict = self.set_nodes_info(bus, zip(self.branch[0],self.branch[1]), self.pmu, zip(self.GBBSH[0], self.GBBSH[1], self.GBBSH[2]), 'single') 135 | # 保存当前配置 136 | conf_to_save = { 137 | 'x_real': self.x_real.tolist(), 138 | 'bus_type': self.bus_type, 139 | 'GBBSH': list(zip(self.GBBSH[0], self.GBBSH[1], self.GBBSH[2])), 140 | 'bus': bus, 141 | 'branch': list(zip(self.branch[0],self.branch[1])) 142 | } 143 | with open('cache/IEEE_'+str(self.size)+'_info.json','w',encoding='utf-8') as f: 144 | f.write(json.dumps(conf_to_save,ensure_ascii=False,cls=ComplexEncoder)) 145 | elif self.model_name == 'WSNs': 146 | #X = sb.symbols("X") 147 | #Y = sb.symbols("Y") 148 | Xi = sb.symbols("X_i") 149 | Xj = sb.symbols("X_j") 150 | Yi = sb.symbols("Y_i") 151 | Yj = sb.symbols("Y_j") 152 | # 本地测量 153 | #M_ij = sb.sqrt((X-Xi)**2 + (Y-Yi)**2) # 已知节点对未知节点的测量 154 | # 边量测计算公式 155 | Z_ij = sb.sqrt((Xj-Xi)**2 + (Yj-Yi)**2) # 未知节点之间的距离 156 | A_ij = sb.atan((Yj-Yi) / (Xj-Xi)) # 未知节点之间的角度 157 | self.h = [Z_ij, A_ij] 158 | #self.value_symbol = [X, Y] 159 | self.state_i_symbol = [Xi, Yi] 160 | self.state_j_symbol = [Xj, Yj] 161 | self.jacobi_h_ij = self.__jacob(self.h, self.state_i_symbol) 162 | self.jacobi_h_ji = self.__jacob(self.h, self.state_j_symbol) 163 | # 从配置文件提取 164 | if os.path.exists('cache/WSNs_'+str(self.size)+'_info.json') is True: # 从缓存提取 165 | with open('cache/WSNs_'+str(self.size)+'_info.json','r',encoding='utf-8') as f: 166 | saved_conf = json.load(f, object_hook=as_complex) 167 | self.x_real = np.mat(saved_conf['x_real']) 168 | self.bus_type = saved_conf['bus_type'] 169 | bus = saved_conf['bus'] 170 | self.branch = saved_conf['branch'] 171 | self.pmu = saved_conf['reference_nodes'] 172 | distance = saved_conf['distance'] 173 | self.bus_info_dict = self.set_nodes_info(bus,self.branch,self.pmu,distance,'single') 174 | else: 175 | self.nodes_cdf = extract_config.tools('./topology/WSNs'+str(self.size)+'.txt', './rules/rule_WSNs_nodes', 0) 176 | self.branch_cdf = extract_config.tools('./topology/WSNs'+str(self.size)+'.txt', './rules/rule_WSNs_branch', 1) 177 | x_real = self.nodes_cdf.get_items([1, 3]) #第一次获得的真实状态值 178 | x = np.mat(np.zeros([2*self.size,1])) 179 | for cnt,(i, j) in enumerate(zip(x_real[0], x_real[1])): 180 | x[2*cnt, 0] = i 181 | x[2*cnt+1, 0] = j 182 | self.x_real = x 183 | self.bus_type = self.nodes_cdf.get_items(3) # Nodes type: 0 -> Unkown 184 | # 3 -> Reference 185 | bus = self.nodes_cdf.get_items(0) 186 | self.branch = tuple(self.branch_cdf.get_items([0,2])) # extract the bus number of out-in side. 187 | distance = tuple(self.branch_cdf.get_items(2)) # 没用的占位 188 | # 重新定义ref节点为pmu 189 | self.pmu = [] 190 | for i in self.bus_type: 191 | if i == 3: 192 | self.pmu.append('PMU') 193 | else: 194 | self.pmu.append('SCADA') 195 | self.bus_info_dict = self.set_nodes_info(bus, zip(self.branch[0],self.branch[1]), self.pmu, distance, 'single') 196 | # 保存当前配置 197 | conf_to_save = { 198 | 'x_real': self.x_real.tolist(), 199 | 'bus_type': self.bus_type, 200 | 'bus': bus, 201 | 'branch': list(zip(self.branch[0],self.branch[1])), 202 | 'distance': distance, 203 | 'reference_nodes': self.pmu, 204 | } 205 | with open('cache/WSNs_'+str(self.size)+'_info.json','w',encoding='utf-8') as f: 206 | f.write(json.dumps(conf_to_save,ensure_ascii=False,cls=ComplexEncoder)) 207 | else: 208 | raise("error") 209 | 210 | def set_nodes_info(self, nodes, connections, attrs, paras, direction='double'): 211 | """ 212 | 将一一对应的列表构造成节点的连接字典 213 | 214 | 输入 215 | ---- 216 | nodes: [<节点号>,...] 217 | connections: [[<节点号>,<节点号>],...,[...]] 218 | attrs: [<节点特性>,...] 219 | paras[[边参数们],...] 220 | direction: 连接方式| 单向: 'single'; 双向: 'double' 221 | 222 | 输出 223 | ---- 224 | { '<节点号>': 225 | 'attr':<节点属性> 226 | { 'connect': 227 | [ { 'dst':'<连接节点号>' 228 | 'para':'<连接参数>' 229 | } 230 | ... 231 | ] 232 | ... 233 | } 234 | ... 235 | } 236 | """ 237 | connection_dict = {} 238 | for cnt,i in enumerate(nodes): 239 | connection_dict[i] = {'connect':[], 'attr':attrs[cnt]} 240 | is_double = False 241 | if direction is 'double': 242 | is_double = True 243 | for (bus,bus_connect),para in zip(connections,paras): 244 | connection_dict[bus]['connect'].append({'dst':bus_connect, 'para':para}) 245 | if is_double: 246 | connection_dict[bus_connect]['connect'].append({'dst':bus, 'para':para}) 247 | return connection_dict 248 | 249 | def __gen_gbbsh(self, is_ignore=False): 250 | """ 251 | 计算建模所需电路参数 252 | 253 | 输入 254 | ---- 255 | * 电阻r(resistance), 256 | * 电抗x(reactance), 257 | * 分流电导gsh, 258 | * 分流电纳bsh, 259 | * is_ignore: 是否忽略接地shunt和传输电阻,若忽略则H是实数矩阵 260 | 261 | 计算 262 | ---- 263 | * 电导g(conductance), 264 | * 电纳b(susceptance), 265 | * 分流导纳ysh(admittance_shunt) 266 | 267 | 公式 268 | ---- 269 | Note: 阻抗z(impedance) 270 | z = r + jx |=> g = r/(r**2 + x**2) 271 | y = g + jb = z^{-1} |=> b = x/(r**2 + x**2) 272 | branch上的ysh = (相连的两个bus上的ysh)/2 273 | 274 | 返回 275 | ---- 276 | self.GBBSH = ((g), (b), (ysh)); 277 | """ 278 | branch_num = self.branch_cdf.get_items([0,2]) # extract the bus number of out-in side. 279 | resistance = self.branch_cdf.get_items(6) # line resistance 280 | reactance = self.branch_cdf.get_items(7) # line reactance 281 | shunt_conductance = self.bus_cdf.get_items(16) # Shunt conductance 282 | shunt_susceptance = self.bus_cdf.get_items(17) # Shunt susceptance 283 | 284 | self.branch = tuple(branch_num) 285 | if is_ignore is False: 286 | conductance = tuple([r/(r**2 + x**2) for r,x in zip(resistance, reactance)]) 287 | susceptance = tuple([-x/(r**2 + x**2) for r,x in zip(resistance, reactance)]) 288 | else: 289 | conductance = tuple([0 for r,x in zip(resistance, reactance)]) 290 | susceptance = tuple([1 for r,x in zip(resistance, reactance)]) 291 | self.GBBSH.append(conductance) 292 | self.GBBSH.append(susceptance) 293 | 294 | shunt_admittance_bus =[] 295 | shunt_admittance_branch = [] 296 | if is_ignore is False: 297 | for c, s in zip(shunt_conductance, shunt_susceptance): 298 | shunt_admittance_bus.append( complex(c, s) ) 299 | for o, i in zip(branch_num[0], branch_num[1]): 300 | shunt_admittance_branch.append((shunt_admittance_bus[o-1] + shunt_admittance_bus[i-1])/2) 301 | else: # 忽略对地shunt,电导为0 302 | for o, i in zip(branch_num[0], branch_num[1]): 303 | shunt_admittance_branch.append(0) 304 | self.GBBSH.append(shunt_admittance_branch) 305 | 306 | self.GBBSH = tuple(self.GBBSH) 307 | 308 | def __jacob(self, M, X): 309 | """ 310 | 计算雅可比矩阵 311 | 312 | 输入 313 | ---- 314 | * M: a symbolic 1d-list 315 | * every element is a function with unkown values <- X 316 | 317 | 输出 318 | ---- 319 | a symbolic 2d-list, consist a symbolic jacobi matrix. 320 | Then, use function __symbol_assignment() to assign value 321 | """ 322 | J = [] 323 | for i in range(len(M)): 324 | J.append([]) 325 | for j in range(len(X)): 326 | J[i].append(sb.diff(M[i], X[j])) 327 | return J 328 | 329 | def delete_measurements(self, amount=1, busTobeDeleted=None): 330 | if busTobeDeleted is None: # 随机剔除 331 | is_full_rank = False 332 | loop_time = 0 333 | while is_full_rank is not True: 334 | sequence_toBeDeleted = np.random.random_integers(self.measure_size ,size=amount) 335 | sequence_toBeDeleted = np.sort(sequence_toBeDeleted) 336 | tmp_H = self.H 337 | tmp_R_I = self.R_I 338 | tmp_R = self.R 339 | tmp_z = self.z_observed 340 | loc = 1 341 | for measure_location in sequence_toBeDeleted: 342 | tmp_H = np.delete(tmp_H, measure_location-loc, 0) 343 | loc += 1 344 | if np.linalg.matrix_rank(tmp_H) == self.state_size: 345 | self.measure_size -= amount 346 | loc=1 347 | for measure_location in sequence_toBeDeleted: 348 | tmp_R = np.delete(tmp_R, measure_location-loc, 0) 349 | tmp_R = np.delete(tmp_R, measure_location-loc, 1) 350 | tmp_R_I = np.delete(tmp_R_I, measure_location-loc, 0) 351 | tmp_R_I = np.delete(tmp_R_I, measure_location-loc, 1) 352 | tmp_z = np.delete(tmp_z, measure_location-loc, 0) 353 | loc += 1 354 | self.reduced_R = tmp_R 355 | self.reduced_R_I = tmp_R_I 356 | self.reduced_z_real = tmp_z 357 | self.reduced_H = tmp_H 358 | is_full_rank = True 359 | print('经过'+str(loop_time+1)+'轮随机,得到降维后的测量矩阵.') 360 | 361 | if loop_time == 1000: 362 | print('删掉'+str(amount)+'个测量值随机选了1000次测量矩阵还是没满秩!') 363 | return False 364 | loop_time += 1 365 | measurement_tobe_del_list = list(sequence_toBeDeleted) 366 | else: # 剔除掉攻击向量 367 | tmp_H = self.H 368 | tmp_R_I = self.R_I 369 | tmp_R = self.R 370 | tmp_z = self.z_observed 371 | 372 | random_choose_to_del_amount = np.random.randint(len(busTobeDeleted)) 373 | np.random.shuffle(busTobeDeleted) 374 | 375 | del_cnt = 0 376 | measurement_tobe_del_list = [] 377 | for i in range(random_choose_to_del_amount): 378 | tmp_H = np.delete(tmp_H, busTobeDeleted[i]-del_cnt, 0) 379 | tmp_R = np.delete(tmp_R, busTobeDeleted[i]-del_cnt, 0) 380 | tmp_R = np.delete(tmp_R, busTobeDeleted[i]-del_cnt, 1) 381 | tmp_R_I = np.delete(tmp_R_I, busTobeDeleted[i]-del_cnt, 0) 382 | tmp_R_I = np.delete(tmp_R_I, busTobeDeleted[i]-del_cnt, 1) 383 | tmp_z = np.delete(tmp_z, busTobeDeleted[i]-del_cnt, 0) 384 | measurement_tobe_del_list.append(busTobeDeleted[i]) 385 | del_cnt += 1 386 | amount = del_cnt 387 | if np.linalg.matrix_rank(tmp_H) == self.state_size: 388 | self.reduced_measure_size = self.measure_size - random_choose_to_del_amount 389 | self.reduced_R = tmp_R 390 | self.reduced_R_I = tmp_R_I 391 | self.reduced_z_real = tmp_z 392 | self.reduced_H = tmp_H 393 | else: 394 | print('删掉这'+ str(del_cnt) +'行测量值后系统测量矩阵不再满秩!('+str(np.linalg.matrix_rank(tmp_H))+'!='+str(np.linalg.matrix_rank(self.H))+').') 395 | return False 396 | 397 | reduced_x_est = (self.reduced_H.H * self.reduced_R_I * self.reduced_H).I * self.reduced_H.H * self.reduced_R_I * self.reduced_z_real 398 | 399 | print('删除的测量值的位置序列: '+ str(measurement_tobe_del_list) +'.') 400 | plt.figure('删除 '+str(amount)+'个测量值后预测测量值的变化量') 401 | plt.plot(np.arange(len(self.reduced_z_real)), (self.reduced_z_real-self.reduced_H*reduced_x_est), '.') 402 | 403 | plt.figure('原残差') 404 | plt.plot(np.arange(len(self.z_observed)), (self.z_observed-self.H*self.x_est_center), '.') 405 | 406 | plt.figure('前后状态差') 407 | plt.plot(range(len(reduced_x_est)), self.x_est_center-reduced_x_est, '.') 408 | plt.show() 409 | return True 410 | 411 | # 置信区间为 confidence,维数为dim 的卡方分布的值 412 | def chi2square_val(self, dim, confidence): 413 | mu = 0 414 | sigma = 1 415 | x = np.arange(-100,100,0.1) 416 | pdf = np.exp(-((x - mu)**2)/(2*sigma**2)) / (sigma * np.sqrt(2*np.pi)) 417 | 418 | z_a = np.percentile(pdf, confidence) # 标准正态分布的confidence分位数 419 | chi = 1/2*(z_a + np.sqrt(2*dim-1))**2 420 | return chi 421 | 422 | def summary(self, para=None, record=False, visualize=False): 423 | """ 424 | 查看模型各项参数 425 | 看不懂了... 426 | 但有一个功能是打印某总线或某两个总线之间的(功率、电压等)信息 427 | 428 | 输入 429 | ---- 430 | * para: 431 | |-- # 总线编号 432 | |-- [#,#] 两个总线编号 433 | |-- None 待开发 434 | * visualize: 矩阵参数可视化 435 | * record: 是否保存矩阵为txt格式文件 436 | 437 | 返回 438 | ---- 439 | NULL 440 | """ 441 | # 声明变量 442 | Gamma = np.empty((self.state_size,self.state_size), dtype=complex) 443 | 444 | # H 矩阵的性质 445 | print("H矩阵拥有%i行(测量数)和%i列(状态数)"%(self.measure_size,np.shape(self.H)[1])) 446 | 447 | # 显示bus之间的参数 448 | if para is not None: 449 | try: 450 | stop = self.measure_who.index(para) 451 | except ValueError: 452 | print('wrong input!') 453 | exit() 454 | 455 | t = 0 # type: 0->Bus; 1->PMU; 2->Branch 456 | cnt = 0 457 | H_cnt = 0 458 | 459 | ''' 460 | ## eliminate Reference bus's state 461 | bus_ref = self.x_who.index(self.bus_ref) 462 | H_real = np.delete(self.H, [bus_ref, bus_ref+1], axis=1) 463 | # and others 464 | Phi_real = H_real.H*self.R*H_real 465 | 466 | # eliminate state num 467 | for i in range(self.nodes_num): 468 | bus_ref -= self.node_col_amount[i] 469 | if bus_ref < 0: 470 | self.node_col_amount[i] -= 2 471 | break 472 | node_col_amount = self.node_col_amount 473 | # Precondition matrix 474 | P = [] 475 | iterator = 0 476 | for i in range(self.nodes_num): 477 | P.append(Phi_real[iterator:node_col_amount[i]+iterator, iterator:node_col_amount[i]+iterator].I) 478 | iterator += node_col_amount[i] 479 | Precondition_center = P[0] 480 | for i in range(1,self.nodes_num): 481 | Precondition_center = block_diag(Precondition_center,P[i]) 482 | 483 | # print(np.linalg.matrix_rank(H_real)) 484 | ''' 485 | 486 | ## alpha 487 | #alpha = self.H.H * self.R * self.z_observed 488 | #print(alpha) 489 | print('====================================') 490 | 491 | ## WLS Estimate 492 | x_est = self.Phi.I * self.H.H * self.R.I * self.z_observed 493 | # z_est = self.H * x_est 494 | 495 | print('====================================') 496 | for i in self.measure_who: 497 | if cnt == stop: 498 | if type(i) is type([]): 499 | if i[0] == i[1]: 500 | t=1 501 | print('Bus(PMU): '+str(i[0])) 502 | print('') 503 | print('====================================') 504 | print('Estimate state[V, angle]: '+str(x_est[[2*self.x_who.index(i[0]), 2*self.x_who.index(i[0])+1], :].T)) 505 | # prepare for z 506 | z = self.z_observed[(H_cnt, H_cnt+1), :] 507 | else: 508 | t=2 509 | print('Branch: '+str(i[0])+' -> '+str(i[1])) 510 | print('') 511 | print('====================================') 512 | # prepare for z, z* = z + h(x) - Bij*x - Bji*x 513 | x_location_i = 2*self.x_who.index(i[0]) 514 | x_location_j = 2*self.x_who.index(i[1]) 515 | print('Estimate Bus '+ str(i[0]) +' state[V, angle]: '+str(x_est[[x_location_i, x_location_i+1], :].T)) 516 | print('Estimate Bus '+ str(i[1]) +' state[V, angle]: '+str(x_est[[x_location_j, x_location_j+1], :].T)) 517 | 518 | print('Real Bus '+ str(i[0]) +' state[V, angle]: '+str(self.x_observed[[x_location_i, x_location_i+1], :].T)) 519 | print('Real Bus '+ str(i[1]) +' state[V, angle]: '+str(self.x_observed[[x_location_j, x_location_j+1], :].T)) 520 | 521 | H_0 = [] 522 | for a in range(len(self.h)): 523 | H_0.append(self.h[a]) 524 | for k in range(len(self.state_i_symbol)): 525 | H_0[a] = H_0[a].subs(self.state_i_symbol[k], self.x_observed[x_location_i+k,:]) 526 | for k in range(len(self.state_j_symbol)): 527 | H_0[a] = H_0[a].subs(self.state_j_symbol[k], self.x_observed[x_location_j+k,:]) 528 | for k in range(len(self.value_symbol)): 529 | H_0[a] = H_0[a].subs(self.value_symbol[k], self.GBBSH[k][stop]) 530 | 531 | z = self.z_observed[(H_cnt, H_cnt+1), :] - self.H[(H_cnt, H_cnt+1),(x_location_i,x_location_i+1)]*self.x_observed[(x_location_i,x_location_i+1),:] - self.H[(H_cnt, H_cnt+1),(x_location_j,x_location_j+1)]*self.x_observed[(x_location_j,x_location_j+1),:] + np.mat(H_0, dtype=complex).T 532 | #print(self.H[(H_cnt, H_cnt+1),:]) 533 | else: 534 | t=0 535 | print('Bus: '+str(i)) 536 | print('') 537 | print('====================================') 538 | print('Estimate state[V, angle]: '+str(x_est[[2*self.x_who.index(i), 2*self.x_who.index(i)+1], :].T)) 539 | print('Real state[V, angle]: '+str(self.x_observed[[2*self.x_who.index(i), 2*self.x_who.index(i)+1], :].T)) 540 | #print(self.H[H_cnt,:]) 541 | # prepare for z 542 | z = self.z_observed[H_cnt, :] 543 | break 544 | if type(i) is type([]): 545 | H_cnt+=2 546 | else: 547 | H_cnt+=1 548 | cnt+=1 549 | print('') 550 | print('====================================') 551 | print('Row location: '+str(H_cnt)) 552 | loc = self.H[H_cnt, :].nonzero() 553 | print('Col location: '+str(loc[1])) 554 | if self.is_distribute is True: 555 | r=1 556 | for i in self.node_row_amount: 557 | H_cnt-=i 558 | if H_cnt<0: 559 | break 560 | r+=1 561 | print('Row in node: '+str(r)) 562 | print('Col in node:', end=' ') 563 | for j in range(len(loc[1])): 564 | c=1 565 | Hc_cnt = loc[1][j] 566 | for i in self.node_col_amount: 567 | Hc_cnt-=i 568 | if Hc_cnt<0: 569 | break 570 | c+=1 571 | print(str(c), end=' ') 572 | print('') 573 | print('') 574 | print('====================================') 575 | # print z 576 | if t==0: 577 | print('Bus Voltage: ' + str(z.T)) 578 | elif t==1: 579 | print('[Bus Voltage, Bus angle]:' + str(z.T)) 580 | elif t==2: 581 | print('[P, Q]:' + str(z.T)) 582 | else: 583 | pass 584 | print('') 585 | 586 | # 计算参数矩阵的特性 587 | print('====================================') 588 | print('-----------HTH的复数性质-------------') 589 | a,b = np.linalg.eig(self.H.H*self.H) 590 | u,v,w = np.linalg.svd(self.H.H*self.H) 591 | print('复数条件数: ' + str(linalg.cond(self.H))) 592 | print('特征值: ' + str(a)) 593 | print('奇异值: ' + str(v)) 594 | print('====================================') 595 | print('-----------Phi的复数性质-------------') 596 | a,b = np.linalg.eig(self.Phi) 597 | u,v,w = np.linalg.svd(self.Phi) 598 | print('复数条件数: ' + str(linalg.cond(self.Phi))) 599 | print('最大特征值: ' + str(max(a))) 600 | print('最小特征值: ' + str(min(a))) 601 | print('最大奇异值: ' + str(max(v))) 602 | print('最小奇异值: ' + str(min(v))) 603 | print('-----------Phi的实数性质-------------') 604 | a,b = np.linalg.eig(self.Phi.astype(float)) 605 | u,v,w = np.linalg.svd(self.Phi.astype(float)) 606 | print('实数数条件数: ' + str(linalg.cond(self.Phi.astype(float)))) 607 | print('最大特征值: ' + str(max(a))) 608 | print('最小特征值: ' + str(min(a))) 609 | print('最大奇异值: ' + str(max(v))) 610 | print('最小奇异值: ' + str(min(v))) 611 | print('====================================') 612 | # 计算Gamma的特性 613 | if self.is_distribute is True: 614 | print('----------Gamma的复数性质------------') 615 | Gamma = np.linalg.cholesky(self.Precondition_center).T * self.Phi * np.linalg.cholesky(self.Precondition_center) 616 | a,b = np.linalg.eig(Gamma) 617 | u,v,w = np.linalg.svd(Gamma) 618 | print('复数条件数: ' + str(linalg.cond(Gamma))) 619 | print('最大特征值: ' + str(max(a))) 620 | print('最小特征值: ' + str(min(a))) 621 | print('最大奇异值: ' + str(max(v))) 622 | print('最小奇异值: ' + str(min(v))) 623 | #print('对称性:' + str(np.linalg.norm(Gamma.H-Gamma, ord=2))) 624 | print('----------Gamma的实数性质------------') 625 | Gamma = Gamma.astype(float) 626 | a,b = np.linalg.eig(Gamma) 627 | u,v,w = np.linalg.svd(Gamma) 628 | print('实数条件数: ' + str(linalg.cond(Gamma))) 629 | print('最大特征值: ' + str(max(a))) 630 | print('最小特征值: ' + str(min(a))) 631 | print('最大奇异值: ' + str(max(v))) 632 | print('最小奇异值: ' + str(min(v))) 633 | #print('对称性:' + str(np.linalg.norm(Gamma.H-Gamma, ord=2))) 634 | print('====================================') 635 | 636 | # 可视化 637 | if visualize is True: 638 | plt.matshow(self.Phi.astype(float)) 639 | plt.show() 640 | if self.is_distribute is True: 641 | plt.matshow(self.Precondition_center.astype(float)) 642 | plt.show() 643 | plt.matshow(Gamma.astype(float)) 644 | plt.show() 645 | plt.matshow(self.nodes_graph) 646 | plt.show() 647 | 648 | # 保存矩阵到txt文件 649 | if record is True: 650 | np.savetxt('./save/H', self.H, delimiter = ',') 651 | if self.is_distribute is True: 652 | np.savetxt('./save/Gamma', Gamma, fmt='%.1f', delimiter = ',') 653 | np.savetxt('./save/Precondition', self.Precondition_center, fmt='%.1f', delimiter = ',') 654 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | {"network_size": "118", "is_centralized": false, "is_linear": true, "sim_time": "4", "state_change": "0.3", "pmu_voltage_variance": "0.002", "pmu_angle_variance": "0.01", "scada_voltage_variance": "0.3", "scada_power_variance": "0.3", "nonlinear_iter_time": "5", "nonlinear_stop_error": "5", "decentralized_method": "Richardson", "decentralized_main_period": "150", "decentralized_child_period": "100", "is_asyn": false, "asyn_tolerance_diff": "15", "is_plot": true, "is_inneriter_plot": false, "is_outeriter_plot": false, "model_name": "PowerGrid", "network_size_list": ["118"], "window_size": [370, 572]} -------------------------------------------------------------------------------- /decentralized/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/huminan/PowerGrid/a4f09d786658c2160df1dd9fa434c45124e72db0/decentralized/__init__.py -------------------------------------------------------------------------------- /decentralized/distributed_linear_powergrid.py: -------------------------------------------------------------------------------- 1 | from linear_powergrid import LinearPowerGrid 2 | from decentralized.estimators import Richardson,Stocastic 3 | 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import matplotlib.pylab as pylab 7 | from scipy.linalg import block_diag 8 | import copy 9 | import time 10 | import json 11 | import os 12 | 13 | class ComplexEncoder(json.JSONEncoder): 14 | def default(self, obj): 15 | # 如果要转换的对象是复数类型,程序负责处理 16 | if isinstance(obj, complex): 17 | return {"__complex__": 'true', 'real': obj.real, 'imag': obj.imag} 18 | # 对于其他类型,还使用JSONEncoder的默认处理 19 | return json.JSONEncoder.default(self, obj) 20 | 21 | def as_complex(dct): 22 | if '__complex__' in dct: 23 | return complex(dct['real'], dct['imag']) 24 | return dct 25 | 26 | class DistributedLinearPowerGrid(LinearPowerGrid): 27 | def __init__(self, nodes = [], pmu=[],conf_dict={}): 28 | super().__init__(pmu=pmu, conf_dict=conf_dict) 29 | self.is_distribute = True 30 | self.x_est_center_list = [] 31 | self.x_est_distribute = np.zeros((self.size*2,1)) 32 | self.x_est_distribute_lists = [] 33 | self.Phi_distribute = [] 34 | self.cluster = {} 35 | self.is_distribute = True 36 | # 簇配置 37 | if self.model_name == 'PowerGrid': 38 | self.nodes_num = len(nodes) # 表示有多少个节点 39 | elif self.model_name == 'WSNs': 40 | self.nodes_num = self.size 41 | nodes = [] 42 | for i in range(1,self.size+1): 43 | nodes.append([i]) 44 | # 分布式配置 45 | self.reorder(nodes) # 9.8s 46 | self.x_real_list, self.z_distribute, self.H_distribute,self.Phi_distribute = self.distribulize(self.x_real, self.z_observed, self.H, self.Phi, graph=True) # 0.018s 47 | # GUI 配置 48 | main_period = int(conf_dict['decentralized_main_period']) 49 | gamma_period = int(conf_dict['decentralized_child_period']) 50 | self.decentralized_method = conf_dict['decentralized_method'] 51 | is_async = conf_dict['is_asyn'] 52 | if conf_dict['decentralized_method'] == 'Richardson(finite)': is_finite = True; main_period=self.nodes_num-2 53 | else: is_finite = False 54 | diff_limit = int(conf_dict['asyn_tolerance_diff']) 55 | self.estimator_conf_dict = { 56 | 'main_period': main_period, 57 | 'gamma_period': gamma_period, 58 | 'is_async': is_async, 59 | 'is_finite': is_finite, 60 | 'diff_limit': diff_limit, 61 | 'attacked_nodes': None, 62 | 'is_DoS': conf_dict['is_DoS'], 63 | } 64 | if conf_dict['is_DoS'] is True: 65 | self.estimator_conf_dict['DoS_dict'] = conf_dict['DoS_dict'] 66 | self.is_plot = conf_dict['is_plot'] 67 | 68 | def jaccobi_H_distribute(self, x_operation): 69 | # 初始化 70 | Jaccobi_distribue = [] 71 | Phi_distribute = [] 72 | node_list = list(map(int,self.bus_info_dict.keys())) 73 | # 配置总线测量 74 | cnt = 0 75 | for cluster,cluster_info_dict in self.cluster_info_dict.items(): 76 | Jaccobi_H = np.mat(np.zeros([0, 2*self.size]), dtype=complex) # 量测矩阵 77 | for bus in cluster_info_dict['cluster']: 78 | if self.bus_info_dict[bus]['attr'] == 'PMU': 79 | a = np.mat(np.zeros([2, 2*self.size])) 80 | a[0,cnt] = 1 81 | a[1,cnt+1] = 1 82 | Jaccobi_H = np.row_stack((Jaccobi_H, a)) 83 | else: 84 | a = np.mat(np.zeros([1, 2*self.size])) 85 | a[0,cnt] = 1 86 | Jaccobi_H = np.row_stack((Jaccobi_H, a)) 87 | # 配置边测量 88 | for connect_info_dict in self.bus_info_dict[bus]['connect']: 89 | a = np.mat(np.zeros([2, 2*self.size]), dtype=complex) 90 | # Substitute operation point x0 and g_ij, b_ij, bsh_ij to jacobbi matrices B_ij and B_ji 91 | for i,J_i in enumerate(self.jacobi_h_ij): 92 | for j,J_ij in enumerate(J_i): 93 | for k,symbol_i in enumerate(self.state_i_symbol): 94 | J_ij = J_ij.subs(symbol_i, x_operation[cnt+k,0]) 95 | for k,symbol_j in enumerate(self.state_j_symbol): 96 | J_ij = J_ij.subs(symbol_j, x_operation[2*node_list.index(connect_info_dict['dst'])+k,0]) 97 | for k,symbol_value in enumerate(self.value_symbol): 98 | J_ij = J_ij.subs(symbol_value, connect_info_dict['para'][k]) 99 | a[i, cnt+j] = J_ij 100 | for j,J_j in enumerate(self.jacobi_h_ji): 101 | for i,J_ji in enumerate(J_j): 102 | for k,symbol_i in enumerate(self.state_i_symbol): 103 | J_ji = J_ji.subs(symbol_i, x_operation[cnt+k,0]) 104 | for k,symbol_j in enumerate(self.state_j_symbol): 105 | J_ji = J_ji.subs(symbol_j, x_operation[2*node_list.index(connect_info_dict['dst'])+k,0]) 106 | for k,symbol_value in enumerate(self.value_symbol): 107 | J_ji = J_ji.subs(symbol_value, connect_info_dict['para'][k]) 108 | a[j, 2*node_list.index(connect_info_dict['dst'])+i] = J_ji 109 | Jaccobi_H = np.row_stack((Jaccobi_H, a)) # Augment H 110 | cnt += 2 111 | Jaccobi_distribue.append(Jaccobi_H) 112 | Jaccobi_H = np.vstack([x for y in Jaccobi_distribue for x in y]) 113 | Phi = Jaccobi_H.H * self.R_I * Jaccobi_H 114 | # distribulize H 115 | H_distribute = [] 116 | col_cnt = 0 117 | for cluster_num,Jaccobi_row in enumerate(Jaccobi_distribue): 118 | H_distribute.append([]) 119 | for col in self.node_col_amount: 120 | H_distribute[cluster_num].append(Jaccobi_row[:, col_cnt:col_cnt+col]) 121 | col_cnt += col 122 | return Jaccobi_H, H_distribute, Phi 123 | 124 | def reorder(self, cluster_s): 125 | """ 126 | 将系统按节点分割, 记录重新排列后的集中式次序 127 | 并将计算中不变的参数分布化得到分布化后的列表(R,x_real) 128 | 129 | 输入 130 | ---- 131 | cluster_s: [[...nodes...], [...], ...] 132 | 133 | 得到 134 | ---- 135 | nodes_num: 分割成多少个节点 136 | node_row_amount[]: 每个节点有多少行(测量值) (顺序为节点顺序) 137 | node_col_amount[]: 每个节点有多少列(状态值) (顺序为节点顺序) 138 | R_I_distribute_diag[]: 每个节点的局部协方差矩阵(...) 139 | 140 | 返回 141 | ---- 142 | {:{'cluster':[...buses...], 'connect':[...clusters...], 'row_amount':<>, 'col_amount':<>}, ...} 143 | { '': 144 | { 'cluster': [...nodes...] 145 | 'connect': 146 | [ { 'dst':'<连接节点号>' 147 | 'para':'<连接参数>' 148 | } ] 149 | 'row_amount':<量测个数> 150 | 'col_amount':<状态个数> 151 | } 152 | 153 | ... 154 | } 155 | """ 156 | nodes = [] 157 | for cluster in cluster_s: 158 | nodes += cluster 159 | ## 160 | #row_seq = [] 161 | self.node_row_amount=[] # 表示每个节点有多少测量值 162 | self.node_col_amount=[] # 表示每个节点有多少状态值 163 | self.node_state_split=[] 164 | self.node_measure_split=[] 165 | ## 166 | bus_connection_reorder = {} 167 | cluster_info_dict = {} 168 | # 将self.bus_info_dict重新排列, 并产生cluster_info_dict 169 | for cnt,cluster in enumerate(cluster_s): 170 | cluster_info_dict[cnt] = {} 171 | cluster_info_dict[cnt]['connect'] = [] 172 | row_amount = 0 173 | if self.model_name == 'PowerGrid': 174 | for node in cluster: 175 | ## 假设所有边测量的仪表都部署在输出端,若两个节点之间有连接,那么输出总线在哪个节点,哪个节点就包含另一个节点的数据。 176 | # 不知道怎么写 177 | ## 178 | bus_connection_reorder[node] = self.bus_info_dict[node] 179 | if bus_connection_reorder[node]['attr'] == 'PMU': 180 | row_amount += 2 181 | else: 182 | row_amount += 1 183 | row_amount += len(bus_connection_reorder[node]['connect'])*len(self.h) 184 | elif self.model_name == 'WSNs': 185 | for node in cluster: 186 | bus_connection_reorder[node] = self.bus_info_dict[node] 187 | if bus_connection_reorder[node]['attr'] == 'PMU': 188 | row_amount += 2 189 | row_amount += len(bus_connection_reorder[node]['connect'])*len(self.h) 190 | cluster_info_dict[cnt]['cluster'] = cluster 191 | #cluster_info_dict[cnt]['connect'].append({'dst':?, 'para':?}) 192 | cluster_info_dict[cnt]['row_amount'] = row_amount 193 | cluster_info_dict[cnt]['col_amount'] = len(cluster)*2 194 | ## 195 | self.node_row_amount.append(cluster_info_dict[cnt]['row_amount']) 196 | self.node_col_amount.append(cluster_info_dict[cnt]['col_amount']) 197 | if cnt == 0: 198 | self.node_state_split.append(self.node_col_amount[cnt]) 199 | self.node_measure_split.append(self.node_row_amount[cnt]) 200 | elif cnt != self.nodes_num-1: 201 | self.node_state_split.append(self.node_col_amount[cnt]+self.node_state_split[cnt-1]) 202 | self.node_measure_split.append(self.node_row_amount[cnt]+self.node_measure_split[cnt-1]) 203 | ## 204 | # cols reordered sequence 205 | col_seq = [] 206 | for bus in list(bus_connection_reorder.keys()): 207 | if (bus in self.nodes_ref) and (self.is_reference_deleted is True): 208 | continue 209 | col_seq.append(2*(bus-1)) 210 | col_seq.append(2*(bus-1)+1) 211 | # 排序转换矩阵 212 | #self.row_reorder_matrix = np.mat(np.eye(self.measure_size)[row_seq,:]) 213 | self.col_reorder_matrix = np.mat(np.eye(self.state_size)[:,col_seq]) 214 | # Reorder 215 | self.bus_info_dict, self.cluster_info_dict = bus_connection_reorder,cluster_info_dict 216 | self.x_real = self.col_reorder_matrix.T * self.x_real 217 | self.set_variance_matrix() 218 | self.H,self.Phi = self.jaccobi_H(self.x_real) 219 | self.z_observed = self.create_measurement(self.x_real) + self.R_error * np.mat(np.random.random((self.measure_size,1))) 220 | #print(self.z_observed-self.create_measurement(self.x_real));exit() # 看看噪声多大 221 | # 分布化R_I 222 | R_I_distribute_diag = [] 223 | row_cnt = 0 224 | for row in self.node_row_amount: 225 | R_I_distribute_diag.append(self.R_I[row_cnt:row_cnt+row, row_cnt:row_cnt+row]) 226 | row_cnt += row 227 | self.R_I_distribute_diag = R_I_distribute_diag 228 | 229 | def distribulize(self, state_vec, measure_vec, Jaccobi_mat, Phi_mat, graph=False): 230 | """ 231 | 将H矩阵和z向量的相关参数按节点分块重新排列, 并得到分布化的列表 232 | 233 | 得到 234 | ---- 235 | z_distribute[] 每个节点的测量向量 (顺序为节点顺序) 236 | x_real_list[] 每个节点上一时刻的状态向量 (...) 237 | H_distribute[] 每个节点的局部H矩阵 (...) 238 | nodes_graph(np.mat) 各节点间的连接图矩阵 239 | Phi_graph(np.mat) 各节点间Phi矩阵的连接图矩阵 240 | Precondition_center(np.mat) precondition矩阵 241 | Precondition_distribute{} 空的字典, 在这里先声明, 之后迭代计算时用到 242 | 243 | 返回 244 | ---- 245 | 分布化后的结果 246 | """ 247 | # 分布化 x_real 248 | state_vec_distribute = copy.deepcopy(np.array_split(state_vec, self.node_state_split)) 249 | # 分布化 z_observed 250 | measure_vec_distribute = copy.deepcopy(np.array_split(measure_vec, self.node_measure_split)) 251 | # distribulize H 252 | Jaccobi_mat_distribute = [] 253 | Jaccobi_tmp = copy.deepcopy(np.array_split(Jaccobi_mat, self.node_measure_split, axis=0)) # 按行分割 254 | for i in Jaccobi_tmp: 255 | Jaccobi_mat_distribute.append(np.array_split(i, self.node_state_split, axis=1)) # 按列分割 256 | # distribulize Phi 257 | Phi_mat_distribute = [] 258 | Phi_tmp = copy.deepcopy(np.array_split(Phi_mat, self.node_state_split, axis=0)) # 按行分割 259 | for i in Phi_tmp: 260 | Phi_mat_distribute.append(np.array_split(i, self.node_state_split, axis=1)) # 按列分割 261 | if graph is True: 262 | # real graph: 通过H分块矩阵是否0矩阵来判断 263 | g=np.mat(np.zeros([self.nodes_num, self.nodes_num]), dtype='int') 264 | i=0 265 | for m in Jaccobi_mat_distribute: 266 | j=0 267 | for n in m: 268 | if (len(n.nonzero()[0]) != 0):# and (i!=j): 269 | g[i,j] = 1 270 | g[j,i] = 1 271 | j+=1 272 | i+=1 273 | self.nodes_graph = g 274 | # Phi graph 275 | #pg = self.nodes_graph*self.nodes_graph 276 | pg = np.mat(np.zeros([self.nodes_num, self.nodes_num]), dtype='int') 277 | i=0 278 | for m in Phi_mat_distribute: 279 | j=0 280 | for n in m: 281 | if (len(n.nonzero()[0]) != 0):# and (i!=j): 282 | pg[i,j] = 1 283 | j+=1 284 | i+=1 285 | self.Phi_graph = pg 286 | # cluster连接图 287 | self.neighbors_dict = {} 288 | for num in range(self.nodes_num): 289 | self.neighbors_dict[num] = {'him': self.get_neighbor(num, -1),'me': self.get_neighbor(num, 1),'neighbors': self.get_neighbor(num, 2), 'nn':self.get_neighbor(num, 3)} 290 | # cluster连接图2 291 | for num in range(self.nodes_num): 292 | self.cluster_info_dict[num]['connect'] = self.get_neighbor(num, -1) 293 | return state_vec_distribute,measure_vec_distribute,Jaccobi_mat_distribute,Phi_mat_distribute 294 | 295 | def estimator(self, is_async=False, plot=[]): 296 | """ 297 | 进行分布式估计 298 | 299 | 输入 300 | ---- 301 | self.sim_time: 仿真时间 302 | is_async: 是否使用异步算法: [False,True] 303 | plot 第个时刻需要画分布式迭代图: [#,#,#...] 304 | 305 | 返回 306 | ---- 307 | NULL 308 | """ 309 | # 哪些节点遭受FDI攻击 310 | if self.is_FDI is True or self.is_FDI_PCA is True: 311 | self.attacked_nodes = [] 312 | for i in self.FDI_conf_dic['which_state']: 313 | for nodenum,nodedict in self.cluster_info_dict.items(): 314 | if i/2 in nodedict['cluster']: 315 | self.attacked_nodes.append(nodenum) 316 | self.attacked_nodes = list(set(self.attacked_nodes)) #去重复 317 | self.attacked_nodes.sort() #排序 318 | self.estimator_conf_dict['attacked_nodes'] = self.attacked_nodes 319 | # DoS攻击配置 320 | 321 | # 初始化分布式估计器 322 | if self.decentralized_method == 'Richardson' or self.decentralized_method == 'Richardson(finite)': 323 | child_estimator = Richardson(self.cluster_info_dict, self.neighbors_dict, self.x_real, self.x_est_center, self.estimator_conf_dict) 324 | elif self.decentralized_method == 'Stocastics': 325 | child_estimator = Stocastic(self.cluster_info_dict, self.neighbors_dict, self.x_real, self.x_est_center, self.estimator_conf_dict) 326 | else: 327 | print(self.decentralized_method+'is not known!') 328 | exit() 329 | # 估计器配置 330 | pass 331 | # 开始 332 | res = {'sim_time':self.sim_time, 'state_est':np.empty((self.state_size,0)), 'state_predict':np.zeros((self.state_size,1)), 'state_real':np.empty((self.state_size,0)), 'state_error':np.empty((self.state_size,0))} 333 | a = np.copy(self.x_real) 334 | b = np.zeros((self.size*2,1), dtype=complex) 335 | state_error_mat = np.mat(np.eye(self.state_size)) 336 | self.x_est_distribute = self.x_real # 第一次将真实状态作为上一次的估计结果(也可以使用全0向量,但是需要将next函数放在循环尾部) 337 | for t in range(self.sim_time): 338 | self.next() # 进入下一时刻 339 | if self.is_baddata is True: 340 | self.__inject_baddata(t) 341 | if self.is_FDI is True: 342 | self.__inject_falsedata(t) 343 | elif self.is_FDI_PCA is True: 344 | self.z_observed_history = np.column_stack((self.z_observed_history, self.z_observed)) 345 | self.__inject_falsedata_PCA(t) 346 | # 全局状态估计 347 | is_bad_centralized,residual_centralized = super().estimator(once=True) 348 | self.x_est_center_list = copy.deepcopy(np.array_split(self.x_est_center, self.node_state_split)) 349 | # 分布式状态估计 350 | self.x_est_distribute_lists,self.x_est_distribute = child_estimator.algorithm(self.H_distribute, self.Phi_distribute, self.R_I_distribute_diag, self.z_distribute, is_plot=self.is_plot) 351 | is_bad,residual = self.detect_baddata(is_plot=self.is_plot) 352 | # 预测 353 | a,b,state_error_mat = self.predict(self.x_est_distribute,[a,b,state_error_mat]) 354 | # 画出中间结果 355 | if self.is_plot is True: 356 | # 估计误差(按节点划分) 357 | plt.figure('估计误差(乱序)') 358 | # 与全局估计相比 359 | plt.subplot(211) 360 | plt.title('与全局估计相比') 361 | plt.plot(range(self.state_size), self.x_est_distribute - self.x_est_center, 'b.') # 点图 362 | plt.xlim([0,self.state_size]) 363 | plt.xlabel("状态") 364 | plt.ylabel("误差") 365 | # 与真实状态相比 366 | plt.subplot(212) 367 | tmp_cnt=0 368 | plt.title('与真实状态相比') 369 | plt.plot(range(self.state_size), self.x_est_distribute - self.x_real, 'b.') # 点图 370 | plt.xlim([0,self.state_size]) 371 | plt.xlabel("状态") 372 | plt.ylabel("误差") 373 | plt.draw() 374 | # 估计误差(按状态划分) 375 | plt.figure('估计误差(顺序)') 376 | # 与全局估计相比 377 | plt.subplot(211) 378 | plt.title('与全局估计相比') 379 | plt.plot(range(self.state_size), self.col_reorder_matrix.T * (self.x_est_distribute - self.x_est_center), 'b.') # 点图 380 | plt.xlim([0,self.state_size]) 381 | plt.xlabel("状态") 382 | plt.ylabel("误差") 383 | # 与真实状态相比 384 | plt.subplot(212) 385 | tmp_cnt=0 386 | plt.title('与真实状态相比') 387 | plt.plot(range(self.state_size), self.col_reorder_matrix.T * (self.x_est_distribute - self.x_real), 'b.') # 点图 388 | plt.xlim([0,self.state_size]) 389 | plt.xlabel("状态") 390 | plt.ylabel("误差") 391 | plt.show() 392 | # 坏值检测 393 | if is_bad is True: 394 | print('分布式估计器在第%i时刻检测到坏值,估计的残差为: %s' % (t, str(residual))) 395 | if is_bad_centralized is True: 396 | print('集中式估计器在第%i时刻检测到坏值,估计的残差为: %.3f' % (t, residual_centralized)) 397 | # 记录 398 | res['state_est'] = np.column_stack((res['state_est'], self.x_est_distribute)) 399 | res['state_real'] = np.column_stack((res['state_real'], self.x_real)) 400 | res['state_error'] = np.column_stack((res['state_error'], np.array(self.x_est_distribute-self.x_real))) 401 | res['state_predict'] = np.column_stack((res['state_predict'], self.x_predict)) 402 | self.next() # 进入下一时刻 403 | # 画图 404 | self.plot(res) 405 | 406 | def next(self, diff=None): 407 | """ 408 | 将分布式系统更新至下一时刻 409 | 410 | 输入 411 | ---- 412 | diff: 自定义状态变化量 (array) 413 | 414 | 返回 415 | ---- 416 | NULL 417 | """ 418 | if diff is None: 419 | self.x_real += np.random.random((self.state_size,1)) * self.state_variance 420 | else: 421 | self.x_real += diff + np.random.random((self.state_size,1)) * self.state_variance 422 | #self.z_observed = self.H * self.x_real + np.random.random((self.measure_size,1)) # 线性 423 | self.H, self.Phi = self.jaccobi_H(self.x_est_distribute) 424 | self.z_observed = self.create_measurement(self.x_real) + self.R_error * np.mat(np.random.random((self.measure_size,1))) 425 | self.x_real_list,self.z_distribute,self.H_distribute,self.Phi_distribute = self.distribulize(self.x_real, self.z_observed, self.H, self.Phi, graph=True) 426 | 427 | def get_neighbor(self, n, t=2): 428 | """ 429 | 获取节点的邻居 430 | 431 | 输入 432 | ---- 433 | n 节点号(从0开始计) 434 | t -> -1: Aji != 0 435 | 1: Aij != 0 436 | 2: Aij and Aji !=0 437 | 3: n 的邻居的所有邻居 438 | 439 | 返回 440 | ---- 441 | 邻居节点号 (1d-list)(start from 0) 442 | """ 443 | res = [] 444 | for i in range(self.nodes_num): 445 | if t == -1: 446 | if self.nodes_graph[i, n] != 0: 447 | res.append(i) 448 | elif t == 1: 449 | if self.nodes_graph[n, i] != 0: 450 | res.append(i) 451 | elif t == 2: 452 | if (self.nodes_graph[n, i] != 0) or (self.nodes_graph[i, n] != 0): 453 | res.append(i) 454 | elif t == 3: 455 | if i == n: 456 | continue 457 | if (self.Phi_graph[n, i] != 0): 458 | res.append(i) 459 | return res 460 | 461 | def plot(self, est_res, choosen_state=9): 462 | x_single_axis = np.arange(1,self.sim_time+1).T 463 | x_axis = np.tile(np.arange(1,self.sim_time+1),[self.state_size,1]).T # 向下复制 464 | plt.figure('各仿真时刻状态估计结果') 465 | plt.subplot(211) 466 | plt.title(str(choosen_state) + '状态细节图') 467 | plt.plot(x_single_axis, est_res['state_est'][choosen_state,:].T, 'g*-') 468 | plt.plot(x_single_axis, est_res['state_predict'][choosen_state,:-1].T) 469 | plt.plot(x_single_axis, est_res['state_real'][choosen_state,:].T, 'y*-') 470 | plt.legend(['估计','预测','真实'], loc='upper right', frameon=False) 471 | plt.xlabel("时刻") 472 | plt.ylabel("幅值") 473 | plt.subplot(212) 474 | plt.title('非线性算法各状态估计误差') 475 | plt.plot(x_axis, est_res['state_error'].T, '.-') 476 | plt.xlabel("时刻") 477 | plt.ylabel("误差") 478 | plt.show() 479 | if self.model_name == 'WSNs': 480 | for i in range(est_res['sim_time']): 481 | plt.figure(str(i)+'时刻定位结果') 482 | plt.plot(est_res['state_real'][0::2,i],est_res['state_real'][1::2,i], 'o') 483 | plt.plot(est_res['state_est'][0::2,i],est_res['state_est'][1::2,i], 'x') 484 | plt.legend(['真实位置','估计位置'], loc='upper right', frameon=False) 485 | plt.xlabel("x") 486 | plt.ylabel("y") 487 | plt.show() 488 | 489 | def __inject_falsedata(self, t): 490 | """ 491 | 注入虚假数据 492 | 493 | 输入 494 | ---- 495 | t: 仿真的当前时刻 496 | 497 | 返回 498 | ---- 499 | None 500 | """ 501 | # 攻击是否开始 502 | if self.time_falsedata <= t: 503 | self.API_inject_falsedata(t) 504 | # 注入后更新z_distribute 505 | z_distribute = [] 506 | row_cnt = 0 507 | for row in self.node_row_amount: 508 | z_distribute.append(self.z_observed[row_cnt:row_cnt+row, 0]) 509 | row_cnt += row 510 | self.z_distribute = z_distribute 511 | 512 | def __inject_falsedata_PCA(self, t): 513 | """ 514 | 注入PCA虚假数据 515 | 516 | 输入 517 | ---- 518 | t: 仿真的当前时刻 519 | 520 | 返回 521 | ---- 522 | None 523 | """ 524 | if self.time_falsedata <= t: 525 | self.API_inject_falsedata_PCA(t) 526 | z_distribute = [] 527 | row_cnt = 0 528 | for row in self.node_row_amount: 529 | z_distribute.append(self.z_observed[row_cnt:row_cnt+row, 0]) 530 | row_cnt += row 531 | self.z_distribute = z_distribute 532 | 533 | def __inject_baddata(self, t): 534 | """ 535 | 注入虚假数据 536 | 537 | 输入 538 | ---- 539 | t: 仿真的当前时刻 540 | 541 | 返回 542 | ---- 543 | None 544 | """ 545 | if self.time_baddata <= t: 546 | self.API_inject_baddata(t) 547 | z_distribute = [] 548 | row_cnt = 0 549 | for row in self.node_row_amount: 550 | z_distribute.append(self.z_observed[row_cnt:row_cnt+row, 0]) 551 | row_cnt += row 552 | self.z_distribute = z_distribute 553 | 554 | def detect_baddata(self, confidence=0.5, centralized=False, is_plot = True): 555 | """ 556 | 坏值检测 557 | 558 | 输入 559 | ---- 560 | confidence: 置信区间 561 | centralized: [True] - 集中式坏值检测结果 562 | 563 | 返回 564 | ---- 565 | 邻居节点号: (1d-list)(start from 0) 566 | """ 567 | is_bad = False 568 | if centralized: 569 | residual = 0.0 570 | is_bad,residual = super().estimator(0) 571 | else: 572 | if self.x_est_distribute_lists[0].shape[0] != self.state_size: # 每个节点只估计本地状态 573 | residual = [] 574 | est_z_list = [] 575 | for i in range(self.nodes_num): 576 | tmp_est_z = np.zeros((self.node_row_amount[i],1), dtype=complex) 577 | tmp_neighbor = self.get_neighbor(i, 1) 578 | for j in tmp_neighbor: 579 | tmp_est_z += self.H_distribute[i][j] * self.x_est_distribute_lists[j] 580 | est_z_list.append(tmp_est_z) 581 | else: # 估计全局状态 582 | residual = [] 583 | est_z_list = [] 584 | for i in range(self.nodes_num): 585 | tmp_est_z = np.column_stack(self.H_distribute[i]) * self.x_est_distribute_lists[i] 586 | est_z_list.append(tmp_est_z) 587 | cnt = 0 588 | detect_res_list = [] 589 | chi_list = [] 590 | for i in est_z_list: 591 | detect_res_list.append( round(float(np.sqrt((self.z_distribute[cnt] - i).T * (self.z_distribute[cnt] - i))[0,0]),3) ) 592 | chi_list.append( self.chi2square_val(self.node_row_amount[cnt] - self.node_col_amount[cnt], confidence) ) 593 | if detect_res_list[cnt] > chi_list[cnt]: 594 | is_bad = True 595 | cnt += 1 596 | if is_plot is True: 597 | plt.figure('坏值检测') 598 | plt.plot(chi_list, 'r--', marker='.') 599 | plt.plot(detect_res_list, 'b', marker='.') 600 | plt.legend(['阈值', '残差'], loc='upper right', frameon=False) 601 | plt.axis([0,self.nodes_num-1,0,110]) 602 | plt.xlabel("子系统号") 603 | plt.ylabel("幅值") 604 | plt.draw() 605 | return is_bad,detect_res_list 606 | 607 | def gen_graph(self): 608 | return self.nodes_graph,self.Phi_graph -------------------------------------------------------------------------------- /decentralized/distributed_nonlinear_powergrid.py: -------------------------------------------------------------------------------- 1 | from decentralized.distributed_linear_powergrid import DistributedLinearPowerGrid 2 | from nonlinear_powergrid import NonLinearPowerGrid 3 | from decentralized.estimators import Richardson,Stocastic 4 | import numpy as np 5 | import random 6 | import matplotlib.pyplot as plt 7 | from tqdm import tqdm 8 | import matplotlib.pylab as pylab 9 | import copy 10 | import time 11 | 12 | class DistributedNonLinearPowerGrid(DistributedLinearPowerGrid): 13 | def __init__(self, nodes = [], pmu=[], conf_dict={}): 14 | super().__init__(nodes=nodes, pmu=pmu, conf_dict=conf_dict) 15 | self.is_inneriter_plot = conf_dict['is_inneriter_plot'] 16 | self.is_outeriter_plot = conf_dict['is_outeriter_plot'] 17 | 18 | def estimator(self,estimator_name='Richardson'): 19 | """ 20 | 进行分布式估计 21 | """ 22 | self.estimator_conf_dict['is_finite'] = True 23 | self.estimator_conf_dict['main_period'] = self.nodes_num-2 24 | child_estimator = Richardson(self.cluster_info_dict, self.neighbors_dict, self.x_real, self.x_est_center, self.estimator_conf_dict) 25 | 26 | res = {'sim_time':self.sim_time, 'state_est':np.empty((self.state_size,0)), 'state_predict':np.empty((self.state_size,1)), 'state_real':np.empty((self.state_size,0)), 'state_error':np.empty((self.state_size,0))} 27 | a = self.x_real.copy() 28 | b = np.zeros((self.size*2,1), dtype=complex) 29 | state_error_mat = np.mat(np.eye(self.state_size)) 30 | # 设置初始值 31 | self.x_est_distribute_lists = copy.deepcopy(np.array_split(self.x_real, self.node_state_split)) 32 | self.x_est_distribute = self.x_real.copy() 33 | # 开始仿真 34 | for t in range(self.sim_time): 35 | self.next() # 进入下一时刻 36 | if self.is_baddata is True: 37 | self.__inject_baddata(t) 38 | if self.is_FDI is True: 39 | self.__inject_falsedata(t) 40 | elif self.is_FDI_PCA is True: 41 | self.z_observed_history = np.column_stack((self.z_observed_history, self.z_observed)) 42 | self.__inject_falsedata_PCA(t) 43 | ''' 44 | # 全局状态估计 45 | is_bad_centralized,residual_centralized = super().estimator(0) 46 | # 全局估计分布化 47 | self.x_est_center_list = [] 48 | tmp_cnt = 0 49 | for i in range(self.nodes_num): 50 | self.x_est_center_list.append(self.x_est_center[tmp_cnt:tmp_cnt+self.node_col_amount[i],:]) 51 | tmp_cnt += self.node_col_amount[i] 52 | ''' 53 | # 开始状态估计 54 | bar=tqdm(total=self.iter_time) 55 | record = []; record_error = [] 56 | for u in range(self.iter_time): 57 | # 计算参数并分布化 58 | J,Phi = self.jaccobi_H(self.x_est_distribute) # 5.4s 59 | residual = self.z_observed-self.create_measurement(self.x_est_distribute) # 2.47s 60 | x,residual_distribute,J_distribute,Phi_distribute = self.distribulize(self.x_real,residual,J,Phi) # x没用的, 0.001s 61 | # 分布式计算delta 62 | delta_distribute_lists,delta_distribute = child_estimator.algorithm(J_distribute, Phi_distribute, self.R_I_distribute_diag, residual_distribute, is_plot=False) # 1.83s 63 | # 增量 64 | for i in range(self.nodes_num): 65 | self.x_est_distribute_lists[i] += delta_distribute_lists[i] 66 | self.x_est_distribute += delta_distribute 67 | # 中间参数 68 | if self.is_inneriter_plot is True: 69 | plt.figure('第'+str(t)+'次外部迭代的第'+str(u)+'次内部迭代细节图') 70 | plt.subplot(311);'''plt.title('测量残差')''';plt.plot(np.arange(1,self.measure_size+1).T, residual);plt.xlabel('测量编号');plt.ylabel('残差') 71 | plt.subplot(312);'''plt.title('状态增量')''';plt.plot(np.arange(1,self.state_size+1).T, delta_distribute);plt.xlabel('状态编号');plt.ylabel('增量') 72 | plt.subplot(313);'''plt.title('状态误差')''';plt.plot(np.arange(1,self.state_size+1).T, self.x_est_distribute-self.x_real);plt.xlabel('状态编号');plt.ylabel('误差') 73 | plt.show() 74 | record.append(np.array(delta_distribute/self.x_est_distribute)[:,0]);record_error.append(np.array(self.x_est_distribute-self.x_real)[:,0]) 75 | bar.update(1) 76 | bar.close() 77 | # 预测 78 | a,b,state_error_mat = self.predict(self.x_est_distribute,[a,b,state_error_mat]) 79 | # 画出中间过程 80 | if self.is_outeriter_plot is True: 81 | x_axis = np.tile(np.arange(1,self.iter_time+1),[self.state_size,1]).T 82 | plt.figure('第'+str(t)+'次外部迭代状态细节图') 83 | plt.subplot(211);plt.title('状态相对变化: (变化值)/估计值');plt.xlabel('内迭代次数');plt.ylabel('相对增量');plt.plot(x_axis,record) 84 | plt.subplot(212);plt.title('状态误差图');plt.xlabel('内迭代次数');plt.ylabel('误差');plt.plot(x_axis,record_error) 85 | plt.show() 86 | ''' 87 | is_bad,residual = self.detect_baddata(is_plot=False) 88 | # 画图 89 | if is_bad is True: 90 | print('分布式估计器在第%i时刻检测到坏值,估计的残差为: %s' % (t, str(residual))) 91 | if is_bad_centralized is True: 92 | print('集中式估计器在第%i时刻检测到坏值,估计的残差为: %.3f' % (t, residual_centralized)) 93 | ''' 94 | res['state_est'] = np.column_stack((res['state_est'], self.x_est_distribute)) 95 | res['state_real'] = np.column_stack((res['state_real'], self.x_real)) 96 | res['state_error'] = np.column_stack((res['state_error'], np.array(self.x_est_distribute-self.x_real))) 97 | res['state_predict'] = np.column_stack((res['state_predict'], self.x_predict)) 98 | # 总体报告 99 | self.plot(res) 100 | 101 | 102 | -------------------------------------------------------------------------------- /extract_config.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | # Definitions of rule's file 4 | FILE_FORMAT_NUM = 2 # how many parameters to determine rule's format 5 | SEPARATOR_FORMAT_NUM = 2 # how many parameters in the rule 6 | FIXEDSEAT_FORMAT_NUM = 3 7 | 8 | class tools(object): 9 | def __init__(self, filename, rulepath, section): 10 | self.filename = filename 11 | self.rulepath = rulepath 12 | self.section = section 13 | self.items_name = [] # list items' names 14 | self.rules = {} # dict int/float/char of every items 15 | self.items = {} # dict items' values 16 | self.num_cnt = 0 17 | self.flg_end = '-999' # Default end flag 18 | self.__extract_rule() 19 | 20 | def __rules(self, i_rule, i_file_to_read): 21 | RULES = { 22 | 'SEPARATOR': lambda:self.__rule_separator(i_file_to_read), 23 | 'FIXEDSEAT': lambda:self.__rule_fixedseat(i_file_to_read), 24 | } 25 | func = RULES.get(i_rule) 26 | if func is None: 27 | return None 28 | func() 29 | return True 30 | 31 | def __rule_separator(self, i_file_to_read): 32 | while True: # Extract data 33 | lines = i_file_to_read.readline() # 整行读取数据 34 | if not lines: 35 | break 36 | if lines[0]=='#': 37 | continue 38 | if lines.strip()=='': 39 | continue 40 | t_line_elements = lines.split() 41 | if len(t_line_elements) != SEPARATOR_FORMAT_NUM: 42 | raise Exception('extract_rule: Too many or too less elements in one line!') 43 | break 44 | if t_line_elements[1] == '9': # Update end flag 45 | self.flg_end = t_line_elements[0] 46 | continue 47 | self.rules[t_line_elements[0]]=int(t_line_elements[1]) 48 | self.items_name.append(t_line_elements[0]) 49 | for v_names in self.items_name: # Initiallize items 50 | self.items[v_names]=[] 51 | self.__separator() 52 | 53 | ########################################### 54 | # __separator(self, v_separator=None) 55 | # Extracting data through separators, in this way, data must be complete even it is null, otherwise work bad. 56 | # v_separator: default nothing, can be set ',' or ';' and so on; 57 | ########################################### 58 | def __separator(self, v_separator=None): 59 | with open(self.filename, 'r') as file_to_read: 60 | t_cnt = 0 61 | while t_cnt < self.section: 62 | lines = file_to_read.readline() 63 | if not lines: 64 | break 65 | if lines[0]=='#': 66 | continue 67 | if lines[0:len(self.flg_end)] == self.flg_end: 68 | t_cnt += 1 69 | 70 | while True: 71 | lines = file_to_read.readline() 72 | if not lines: 73 | break 74 | if lines[0]=='#': 75 | continue 76 | if lines[0:len(self.flg_end)] == self.flg_end: 77 | break 78 | 79 | cnt = 0 80 | for t_data in lines.split(v_separator): 81 | if self.rules[self.items_name[cnt]] == 1: 82 | print(self.items_name[cnt], cnt, self.num_cnt) 83 | self.items[self.items_name[cnt]].append(int(t_data)) 84 | elif self.rules[self.items_name[cnt]] == 2: 85 | self.items[self.items_name[cnt]].append(float(t_data)) 86 | elif self.rules[self.items_name[cnt]] == 3: 87 | self.items[self.items_name[cnt]].append(t_data) 88 | cnt += 1 89 | self.num_cnt += 1 90 | 91 | def __rule_fixedseat(self, i_file_to_read): 92 | while True: # Extract data 93 | lines = i_file_to_read.readline() # 整行读取数据 94 | if not lines: 95 | break 96 | if lines[0]=='#': 97 | continue 98 | if lines.strip()=='': 99 | continue 100 | t_line_elements = lines.split() 101 | if t_line_elements[1] == '9': # Update end flag 102 | self.flg_end = t_line_elements[0] 103 | continue 104 | if len(t_line_elements) != FIXEDSEAT_FORMAT_NUM: 105 | raise Exception('extract_rule: Too many elements in one line!') 106 | break 107 | t_range_elements = t_line_elements[2].split('-') 108 | self.rules[t_line_elements[0]]=(int(t_line_elements[1]), int(t_range_elements[0]), int(t_range_elements[1])) 109 | self.items_name.append(t_line_elements[0]) 110 | 111 | for v_names in self.items_name: # Initiallize items 112 | self.items[v_names]=[] 113 | self.__fixedseat() 114 | 115 | ''' Extracting data through setting data in a fixed location''' 116 | def __fixedseat(self): 117 | with open(self.filename, 'r') as file_to_read: 118 | t_cnt = 0 119 | while t_cnt < self.section: 120 | lines = file_to_read.readline() 121 | if not lines: 122 | break 123 | if lines[0]=='#': 124 | continue 125 | if lines[0:len(self.flg_end)] == self.flg_end: 126 | t_cnt += 1 127 | 128 | while True: 129 | lines = file_to_read.readline() 130 | if not lines: 131 | break 132 | if lines[0]=='#': 133 | continue 134 | if lines[0:len(self.flg_end)] == self.flg_end: 135 | break 136 | 137 | for t_rule, t_value in self.rules.items(): 138 | tt_value = lines[t_value[1]-1:t_value[2]].strip() 139 | if t_value[0] == 1: 140 | if tt_value is '': 141 | self.items[t_rule].append(0) 142 | else: 143 | self.items[t_rule].append(int(tt_value)) 144 | elif t_value[0] == 2: 145 | if tt_value is '': 146 | self.items[t_rule].append(0) 147 | else: 148 | self.items[t_rule].append(float(tt_value)) 149 | elif t_value[0] == 3: 150 | self.items[t_rule].append(tt_value) 151 | self.num_cnt += 1 152 | 153 | def __extract_rule(self): 154 | with open(self.rulepath, 'r') as file_to_read: 155 | while True: # Determine which format to extract 156 | lines = file_to_read.readline() 157 | if not lines: 158 | raise Exception('extract_rule: No file format!') 159 | return 160 | if lines[0]=='#': 161 | continue 162 | t_line_elements = lines.split() 163 | if len(t_line_elements) != FILE_FORMAT_NUM: 164 | raise Exception('extract_rule: Wrong file format!') 165 | return 166 | if self.__rules(t_line_elements[0], file_to_read) is None: 167 | raise Exception('extract_rule: Unkown format!') 168 | return 169 | break 170 | file_to_read.close() 171 | return 172 | 173 | def get_items(self, index): 174 | if type(index) is type([]): 175 | if len(index) != 2: 176 | raise Exception('get_items: Wrong list elements!') 177 | res = [] 178 | for i in range(index[0], index[1]): 179 | res.append(self.items[self.items_name[i]]) 180 | return res 181 | else: 182 | return self.items[self.items_name[index]] 183 | 184 | def get_item(self, index, iindex): 185 | return self.items[self.items_name[index]][iindex] 186 | 187 | def s_print(self, code): 188 | print(self.items_name[code]) 189 | print(self.items[self.items_name[code]]) 190 | # print(sum(self.items[self.items_name[code]])) -------------------------------------------------------------------------------- /linear_powergrid.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | from base import StateEstimationBase 3 | import numpy as np 4 | import random 5 | from scipy.linalg import block_diag 6 | from numpy import linalg 7 | import matplotlib.pyplot as plt 8 | import matplotlib.pylab as pylab 9 | import time 10 | import os 11 | import json 12 | 13 | PMU_VOLTAGE_VARIANCE = .002 14 | PMU_ANGLE_VARIANCE = .01 15 | SCADA_VOLTAGE_VARIANCE = .3 16 | SCADA_POWER_VARIANCE = .3 17 | 18 | class LinearPowerGrid(StateEstimationBase): 19 | def __init__(self, pmu=[], conf_dict={}): 20 | super().__init__(pmu=pmu, conf_dict=conf_dict) 21 | # 标记 22 | self.is_baddata = False 23 | self.is_FDI = False 24 | self.is_FDI_PCA = False 25 | # 变量声明 26 | self.time_baddata = 0 27 | self.time_falsedata = 0 28 | self.x_est_center = np.zeros((2*self.size,1)) 29 | self.z_observed_history = None 30 | self.x_predict = np.zeros((2*self.size,1)) 31 | # 提前声明 32 | self.row_reorder_matrix = None 33 | self.col_reorder_matrix = None 34 | self.FDI_conf_dic = {'which_state':(),'effect':()} 35 | # 计算参数(R) 36 | self.set_variance_matrix() 37 | self.set_linear_model() 38 | # 创建量测 39 | self.z_observed = self.create_measurement(self.x_real) + self.R_error * np.mat(np.random.random((self.measure_size,1))) # 真实量测加上噪声 40 | 41 | def set_linear_model(self): 42 | if self.model_name == 'PowerGrid': 43 | # 从缓存提取 44 | if os.path.exists('cache/IEEE_'+str(self.size)+'_linear_info.json') is True: 45 | with open('cache/IEEE_'+str(self.size)+'_linear_info.json','r',encoding='utf-8') as f: 46 | saved_conf = json.load(f) 47 | self.H = np.mat(saved_conf['H_real'],dtype=complex)+np.mat(saved_conf['H_imag'])*(1j) 48 | self.measure_size = saved_conf['measure_size'] 49 | self.state_size = saved_conf['state_size'] 50 | self.Phi = self.H.H * self.R_I * self.H 51 | else: 52 | # 计算H矩阵 53 | self.H,self.Phi = self.jaccobi_H(self.x_real) 54 | self.measure_size = self.H.shape[0] # 量测数量 55 | self.state_size = self.H.shape[1] # 状态数量 56 | # 保存当前配置 57 | conf_to_save = { 58 | 'H_real': self.H.real.astype(float).tolist(), 59 | 'H_imag': self.H.imag.astype(float).tolist(), 60 | 'measure_size': self.measure_size, 61 | 'state_size': self.state_size, 62 | } 63 | with open('cache/IEEE_'+str(self.size)+'_linear_info.json','w',encoding='utf-8') as f: 64 | f.write(json.dumps(conf_to_save,ensure_ascii=False)) 65 | elif self.model_name == 'WSNs': 66 | # 从缓存提取 67 | if os.path.exists('cache/WSNs_'+str(self.size)+'_linear_info.json') is True: 68 | with open('cache/WSNs_'+str(self.size)+'_linear_info.json','r',encoding='utf-8') as f: 69 | saved_conf = json.load(f) 70 | self.H = np.mat(saved_conf['H_real'],dtype=complex)+np.mat(saved_conf['H_imag'])*(1j) 71 | self.measure_size = saved_conf['measure_size'] 72 | self.state_size = saved_conf['state_size'] 73 | self.Phi = self.H.H * self.R_I * self.H 74 | else: 75 | # 计算H矩阵 76 | self.H,self.Phi = self.jaccobi_H(self.x_real) 77 | self.measure_size = self.H.shape[0] # 量测数量 78 | self.state_size = self.H.shape[1] # 状态数量 79 | # 保存当前配置 80 | conf_to_save = { 81 | 'H_real': self.H.real.astype(float).tolist(), 82 | 'H_imag': self.H.imag.astype(float).tolist(), 83 | 'measure_size': self.measure_size, 84 | 'state_size': self.state_size, 85 | } 86 | with open('cache/WSNs_'+str(self.size)+'_linear_info.json','w',encoding='utf-8') as f: 87 | f.write(json.dumps(conf_to_save,ensure_ascii=False)) 88 | 89 | def jaccobi_H(self, x_operation): 90 | """ 91 | 计算H矩阵中由branch与bus决定的参数 92 | 以文件中的out和in为准, 将测量仪表放置在out处. 93 | 94 | 输入 95 | ---- 96 | order: 节点排列的顺序(对分布式系统有用) 97 | x_operation: 上一采样时刻的状态估计值(第一次将初始时刻真实值代入) 98 | 99 | 中间结果 100 | ---- 101 | 测量数: measure_size (不该在这个函数里首次定义) 102 | 状态数: state_size (不该在这个函数里首次定义) 103 | 104 | 公式 105 | ---- 106 | SCADA只可以测量(电压) 107 | PMU可以测量(电压,相角) 108 | 109 | 返回 110 | ---- 111 | 雅可比矩阵: Jaccobi_H 112 | """ 113 | # 初始化 114 | Jaccobi_H = np.mat(np.zeros([0, 2*self.size]), dtype=complex) # 量测矩阵 115 | node_list = list(self.bus_info_dict.keys()) 116 | if self.model_name == 'PowerGrid': 117 | # 配置总线测量 118 | cnt = 0 119 | for bus,bus_info_dict in self.bus_info_dict.items(): 120 | if bus_info_dict['attr'] == 'PMU': 121 | a = np.mat(np.zeros([2, 2*self.size])) 122 | a[0,cnt] = 1 123 | a[1,cnt+1] = 1 124 | Jaccobi_H = np.row_stack((Jaccobi_H, a)) 125 | else: 126 | a = np.mat(np.zeros([1, 2*self.size])) 127 | a[0,cnt] = 1 128 | Jaccobi_H = np.row_stack((Jaccobi_H, a)) 129 | # 配置边测量 130 | for connect_info_dict in bus_info_dict['connect']: 131 | a = np.mat(np.zeros([2, 2*self.size]), dtype=complex) 132 | # Substitute operation point x0 and g_ij, b_ij, bsh_ij to jacobbi matrices B_ij and B_ji 133 | for i,J_i in enumerate(self.jacobi_h_ij): 134 | for j,J_ij in enumerate(J_i): 135 | for k,symbol_i in enumerate(self.state_i_symbol): 136 | J_ij = J_ij.subs(symbol_i, x_operation[cnt+k,0]) 137 | for k,symbol_j in enumerate(self.state_j_symbol): 138 | J_ij = J_ij.subs(symbol_j, x_operation[2*node_list.index(connect_info_dict['dst'])+k,0]) 139 | for k,symbol_value in enumerate(self.value_symbol): 140 | J_ij = J_ij.subs(symbol_value, connect_info_dict['para'][k]) 141 | a[i, cnt+j] = J_ij 142 | for j,J_j in enumerate(self.jacobi_h_ji): 143 | for i,J_ji in enumerate(J_j): 144 | for k,symbol_i in enumerate(self.state_i_symbol): 145 | J_ji = J_ji.subs(symbol_i, x_operation[cnt+k,0]) 146 | for k,symbol_j in enumerate(self.state_j_symbol): 147 | J_ji = J_ji.subs(symbol_j, x_operation[2*node_list.index(connect_info_dict['dst'])+k,0]) 148 | for k,symbol_value in enumerate(self.value_symbol): 149 | J_ji = J_ji.subs(symbol_value, connect_info_dict['para'][k]) 150 | a[j, 2*node_list.index(connect_info_dict['dst'])+i] = J_ji 151 | Jaccobi_H = np.row_stack((Jaccobi_H, a)) # Augment H 152 | cnt += 2 # 4~9s 153 | elif self.model_name == 'WSNs': 154 | cnt = 0 155 | for bus,bus_info_dict in self.bus_info_dict.items(): 156 | if bus_info_dict['attr'] == 'PMU': # ref节点知道自己的位置 157 | a = np.mat(np.zeros([2, 2*self.size])) 158 | a[0,cnt] = 1 159 | a[1,cnt+1] = 1 160 | Jaccobi_H = np.row_stack((Jaccobi_H, a)) 161 | # 配置边测量 162 | for connect_info_dict in bus_info_dict['connect']: 163 | a = np.mat(np.zeros([len(self.h), 2*self.size]), dtype=complex) 164 | for i,J_i in enumerate(self.jacobi_h_ij): 165 | for j,J_ij in enumerate(J_i): 166 | for k,symbol_i in enumerate(self.state_i_symbol): 167 | J_ij = J_ij.subs(symbol_i, x_operation[cnt+k,0]) 168 | for k,symbol_j in enumerate(self.state_j_symbol): 169 | J_ij = J_ij.subs(symbol_j, x_operation[2*node_list.index(connect_info_dict['dst'])+k,0]) 170 | a[i, cnt+j] = J_ij 171 | for j,J_j in enumerate(self.jacobi_h_ji): 172 | for i,J_ji in enumerate(J_j): 173 | for k,symbol_i in enumerate(self.state_i_symbol): 174 | J_ji = J_ji.subs(symbol_i, x_operation[cnt+k,0]) 175 | for k,symbol_j in enumerate(self.state_j_symbol): 176 | J_ji = J_ji.subs(symbol_j, x_operation[2*node_list.index(connect_info_dict['dst'])+k,0]) 177 | a[j, 2*node_list.index(connect_info_dict['dst'])+i] = J_ji 178 | Jaccobi_H = np.row_stack((Jaccobi_H, a)) # Augment H 179 | cnt += 2 # 4~9s 180 | Phi = Jaccobi_H.H * self.R_I * Jaccobi_H # 0.04s 181 | return Jaccobi_H, Phi 182 | 183 | def create_measurement(self, x_operation): 184 | """ 185 | 通过模型构造量测(虚假), 并画出非线性模型与线性模型的量测的差值. 186 | """ 187 | h_operation = np.array((),dtype=complex) 188 | node_list = list(self.bus_info_dict.keys()) 189 | cnt = 0 190 | if self.model_name == 'PowerGrid': 191 | for bus,bus_info_dict in self.bus_info_dict.items(): 192 | # 配置总线测量 193 | if bus_info_dict['attr'] == 'PMU': 194 | h_operation = np.append(h_operation, x_operation[cnt]) 195 | h_operation = np.append(h_operation, x_operation[cnt+1]) 196 | else: # SCADA 197 | h_operation = np.append(h_operation, x_operation[cnt]) 198 | # 配置边测量 199 | for connect_info_dict in bus_info_dict['connect']: 200 | for z_tmp in self.h: 201 | for k,symbol_i in enumerate(self.state_i_symbol): 202 | z_tmp = z_tmp.subs(symbol_i, x_operation[cnt+k,0]) 203 | for k,symbol_j in enumerate(self.state_j_symbol): 204 | z_tmp = z_tmp.subs(symbol_j, x_operation[2*node_list.index(connect_info_dict['dst'])+k,0]) 205 | for k,symbol_value in enumerate(self.value_symbol): 206 | z_tmp = z_tmp.subs(symbol_value, connect_info_dict['para'][k]) 207 | h_operation = np.append(h_operation, z_tmp) 208 | cnt += 2 209 | elif self.model_name == 'WSNs': 210 | for bus,bus_info_dict in self.bus_info_dict.items(): 211 | if bus_info_dict['attr'] == 'PMU': # ref节点的位置 212 | h_operation = np.append(h_operation, x_operation[cnt]) 213 | h_operation = np.append(h_operation, x_operation[cnt+1]) 214 | # 配置边测量 215 | for connect_info_dict in bus_info_dict['connect']: 216 | for z_tmp in self.h: 217 | for k,symbol_i in enumerate(self.state_i_symbol): 218 | z_tmp = z_tmp.subs(symbol_i, x_operation[cnt+k,0]) 219 | for k,symbol_j in enumerate(self.state_j_symbol): 220 | z_tmp = z_tmp.subs(symbol_j, x_operation[2*node_list.index(connect_info_dict['dst'])+k,0]) 221 | h_operation = np.append(h_operation, z_tmp) 222 | cnt += 2 223 | h_operation = np.mat(h_operation, dtype=complex).T 224 | 225 | h_operation = self.H * x_operation # 线性模型 226 | ''' 画出两种量测计算方法的差值 227 | __z_real = self.H * self.x_real # 通过状态x_real*H得到, 真实情况是非线性的, 因此存在很大的问题. 228 | plt.figure('两种量测的差值') 229 | plt.plot(list(range(self.measure_size)), h_operation - __z_real, 'b.') 230 | plt.show() 231 | ''' 232 | return h_operation 233 | 234 | def set_variance_matrix(self): 235 | """ 236 | 设置参数 237 | """ 238 | self.R_real = np.mat(np.zeros([0, 0]), dtype=complex) # 真实量测协方差矩阵 239 | self.R = np.mat(np.zeros([0, 0]), dtype=complex) # 计算量测协方差矩阵 240 | self.R_error = np.mat(np.zeros([0, 0]), dtype=complex) # 真实量测误差 241 | for bus,bus_info_dict in self.bus_info_dict.items(): 242 | if self.model_name == 'PowerGrid': 243 | # 配置总线测量 244 | if bus_info_dict['attr'] == 'PMU': 245 | self.measure_who.append([bus, bus]) 246 | self.R = block_diag(self.R, np.eye(1)*(self.pmu_voltage_variance**2)) 247 | self.R = block_diag(self.R, np.eye(1)*(self.pmu_angle_variance**2)) 248 | self.R_real = block_diag(self.R_real, np.eye(1)*(PMU_VOLTAGE_VARIANCE**2)) 249 | self.R_real = block_diag(self.R_real, np.eye(1)*(PMU_ANGLE_VARIANCE**2)) 250 | self.R_error = block_diag(self.R_error, np.eye(1)*(PMU_VOLTAGE_VARIANCE)) 251 | self.R_error = block_diag(self.R_error, np.eye(1)*(PMU_ANGLE_VARIANCE)) 252 | else: # SCADA 253 | self.measure_who.append(bus) 254 | self.R = block_diag(self.R, np.eye(1)*(self.scada_voltage_variance**2)) 255 | self.R_real = block_diag(self.R_real, np.eye(1)*(SCADA_VOLTAGE_VARIANCE**2)) 256 | self.R_error = block_diag(self.R_error, np.eye(1)*(SCADA_VOLTAGE_VARIANCE)*3) # 这个*3是干嘛的? 257 | if self.bus_type[bus-1] == 3: # if reference 258 | self.nodes_ref.append(bus) 259 | # 配置边测量 260 | for connect_info_dict in bus_info_dict['connect']: 261 | self.measure_who.append([bus, connect_info_dict['dst']]) 262 | self.R = block_diag(self.R, np.eye(2)*(self.scada_power_variance**2)) 263 | self.R_real = block_diag(self.R_real, np.eye(2)*(SCADA_POWER_VARIANCE**2)) 264 | self.R_error = block_diag(self.R_error, np.eye(2)*(SCADA_POWER_VARIANCE)) 265 | elif self.model_name == 'WSNs': 266 | if bus_info_dict['attr'] == 'PMU': 267 | self.measure_who.append([bus, bus]) 268 | self.R = block_diag(self.R, np.eye(1)*(self.pmu_voltage_variance**2)) 269 | self.R = block_diag(self.R, np.eye(1)*(self.pmu_voltage_variance**2)) 270 | self.R_real = block_diag(self.R_real, np.eye(1)*(PMU_VOLTAGE_VARIANCE**2)) 271 | self.R_real = block_diag(self.R_real, np.eye(1)*(PMU_VOLTAGE_VARIANCE**2)) 272 | self.R_error = block_diag(self.R_error, np.eye(1)*(PMU_VOLTAGE_VARIANCE)) 273 | self.R_error = block_diag(self.R_error, np.eye(1)*(PMU_VOLTAGE_VARIANCE)) 274 | if self.bus_type[bus-1] == 3: # if reference 275 | self.nodes_ref.append(bus) 276 | # 配置边测量 277 | for connect_info_dict in bus_info_dict['connect']: 278 | self.measure_who.append([bus, connect_info_dict['dst']]) 279 | self.R = block_diag(self.R, np.eye(1)*(self.scada_power_variance**2)) 280 | self.R = block_diag(self.R, np.eye(1)*(self.pmu_angle_variance**2)) 281 | self.R_real = block_diag(self.R_real, np.eye(1)*(SCADA_POWER_VARIANCE**2)) 282 | self.R_real = block_diag(self.R_real, np.eye(1)*(PMU_ANGLE_VARIANCE**2)) 283 | self.R_error = block_diag(self.R_error, np.eye(1)*(SCADA_POWER_VARIANCE)) 284 | self.R_error = block_diag(self.R_error, np.eye(1)*(PMU_ANGLE_VARIANCE)) 285 | # 总结 286 | self.R = np.mat(self.R) 287 | self.R_real = np.mat(self.R_real) 288 | self.R_error = np.mat(self.R_error) 289 | self.R_I = self.R.I 290 | 291 | def delete_reference_bus(self): 292 | """ 293 | 删除H矩阵中的reference总线 294 | 295 | 状态更改 296 | ------- 297 | * is_reference_deleted: True 298 | 299 | 返回 300 | ---- 301 | NULL 302 | """ 303 | for ref_node in self.nodes_ref: 304 | self.H = np.delete(self.H, (ref_node-1)*2, 1) 305 | self.H = np.delete(self.H, (ref_node-1)*2, 1) 306 | self.x_real = np.delete(self.x_real, (ref_node-1)*2, 0) 307 | self.x_real = np.delete(self.x_real, (ref_node-1)*2, 0) 308 | self.state_size -= 2 309 | self.z_observed = self.H * self.x_real + self.R * np.random.random((self.measure_size,1)) 310 | self.Phi = self.H.H*self.R_I*self.H 311 | self.is_reference_deleted = True 312 | 313 | def estimator(self, once=False): 314 | """ 315 | 调用估计器 316 | 317 | 输入 318 | ---- 319 | self.sim_time: 仿真时间(多少次),当取0时,一般是被子类调用,只进行一次估计,然后检测,其它什么都别干 320 | * is_bad_data: 是否会产生坏数据 321 | * falsedata_type: 虚假数据注入攻击的方法 322 | |- 'normal' 323 | |- 'pca' 324 | 325 | 返回 326 | ---- 327 | NULL 328 | """ 329 | a = np.copy(self.x_real) 330 | b = np.zeros((self.size*2,1), dtype=complex) 331 | state_error_mat = np.mat(np.eye(self.state_size)) 332 | is_bad = False 333 | residual = 0.0 334 | if once is True: 335 | self.x_est_center = self.Phi.I*self.H.H*self.R_I*self.z_observed 336 | is_bad,residual = self.__detect_baddata() 337 | return is_bad,residual 338 | else: 339 | if self.is_FDI_PCA is True: 340 | self.z_observed_history = np.mat(np.empty((self.measure_size,0))) 341 | res = [[],[],[],[]] # 估计、预测、真实 342 | for t in range(self.sim_time+1): 343 | if self.is_baddata is True: 344 | self.__inject_baddata(t) 345 | if self.is_FDI is True: 346 | self.__inject_falsedata(t) 347 | elif self.is_FDI_PCA is True: 348 | self.z_observed_history = np.column_stack((self.z_observed_history, self.z_observed)) 349 | self.__inject_falsedata_PCA(t) 350 | self.x_est_center = self.Phi.I*self.H.H*self.R_I*self.z_observed 351 | res[0].append(complex(self.x_est_center[0])) 352 | res[2].append(complex(self.x_real[0])) 353 | res[3].append(np.array(self.x_est_center-self.x_real)[:,0]) 354 | is_bad,residual = self.__detect_baddata() 355 | if is_bad is True: 356 | print('第%i时刻检测到坏值,估计的残差为: %.3f' % (t, residual)) 357 | if t is not self.sim_time: 358 | a,b,state_error_mat = self.predict(self.x_est_center,[a,b,state_error_mat]) 359 | self.next() 360 | res[1].append(complex(self.x_predict[0])) 361 | plt.figure('状态演变') 362 | plt.subplot(211) 363 | plt.title('某状态跟随图') 364 | plt.plot(res[0], 'g*-') 365 | plt.plot([0]+res[1], 'b*-') 366 | plt.plot(res[2], 'y*-') 367 | plt.legend(['估计','预测','真实'], loc='upper right', frameon=False) 368 | plt.xlabel("时刻") 369 | plt.ylabel("幅值") 370 | plt.subplot(212) 371 | plt.title('状态误差') 372 | plt.plot(res[3], '.-') 373 | plt.show() 374 | 375 | def next(self, diff=None): 376 | """ 377 | 将系统更新至下一时刻 378 | 379 | 输入 380 | ---- 381 | * diff: 自定义状态变化量 (array) 382 | 383 | 返回 384 | ---- 385 | NULL 386 | """ 387 | if diff is None: 388 | self.x_real += np.random.random((self.state_size,1)) * self.state_variance 389 | else: 390 | self.x_real += diff + np.random.random((self.state_size,1)) * self.state_variance 391 | #self.z_observed = self.H * self.x_real + np.random.random((self.measure_size,1)) 392 | self.H,self.Phi = self.jaccobi_H(self.x_est_center) 393 | self.z_observed = self.create_measurement(self.x_real) + self.R_error * np.mat(np.random.random((self.measure_size,1))) 394 | 395 | def predict(self, est_x, para_before=[], model=1, para=None): 396 | """ 397 | 预测下一时刻的电网参数,这里以上一时刻估计值作为真实值 398 | 399 | 输入 400 | ---- 401 | * est_x: 上一时刻的状态估计值 402 | * para_before: 上一时刻的参数 403 | * model: 电网参数预测的模型 404 | * para: None - default [0.8,0.5] 405 | [alpha,beta] 406 | 407 | 返回 408 | ---- 409 | NULL 410 | """ 411 | if model == 1: 412 | alpha = np.zeros((self.size*2,1), dtype=complex) 413 | beta = np.zeros((self.size*2,1), dtype=complex) 414 | if para is None: 415 | for i in range(self.size*2): 416 | alpha[i] = 0.8 417 | beta[i] = 0.5 418 | elif min(para[0]+para[1])>0 and max(para[0]+para[1])<1: 419 | alpha = para[0] 420 | beta = para[1] 421 | a_before = para_before[0] 422 | b_before = para_before[1] 423 | state_error_mat_before = para_before[2] 424 | a = np.zeros((self.size*2,1), dtype=complex) 425 | b = np.zeros((self.size*2,1), dtype=complex) 426 | for i in range(self.size*2): 427 | a[i] = alpha[i] * est_x[i] + (1-alpha[i]) * self.x_real[i] 428 | b[i] = beta[i] * (a[i] - a_before[i]) + (1-beta[i]) * b_before[i] 429 | for i in range(self.size*2): 430 | self.G[i] = (1+beta[i])*(1-alpha[i])*est_x[i] - beta[i]*a_before[i] + (1-beta[i])*b_before[i] 431 | self.A[i,i] = alpha[i]*(1+beta[i]) 432 | # 预测下一步 433 | self.x_predict = self.A * est_x + self.G 434 | # 估计状态误差协方差矩阵 435 | state_error_mat = self.A * state_error_mat_before * self.A.H + np.mat(np.eye(self.state_size))*self.state_variance 436 | 437 | return a,b,state_error_mat 438 | 439 | def inject_baddata(self, moment=1, probability=10): 440 | """ 441 | 注入坏值 442 | 443 | 输入 444 | ---- 445 | * moment: 在哪个时刻产生了坏数据 446 | * probability: 每个仪表产生坏值的概率: p/10e7 447 | 448 | 返回 449 | ---- 450 | ---(已删)--- 451 | * baddata_info_dict [type: dic] 452 | * measurement_injected: 注入的测量攻击的值(非攻击向量,) 453 | * measurement_injected_amount: 注入了多少个测量值 454 | """ 455 | self.is_baddata = True 456 | self.time_baddata = moment 457 | if probability<100 and probability>0: 458 | self.baddata_prob = probability 459 | else: 460 | raise ValueError('probability 只能设置为 0-100!') 461 | 462 | def API_inject_baddata(self, t): 463 | self.__inject_baddata(t) 464 | 465 | def __inject_baddata(self, t): 466 | if self.time_baddata <= t: 467 | #np.random.seed(0) 468 | p = np.array([1-5e-6*self.baddata_prob*self.measure_size, 5e-6*self.baddata_prob*self.measure_size]) 469 | index = np.random.choice([0,1], p=p) 470 | if index == 1: 471 | sparse_amount = random.randint(1,10) # 产生 1-10 个 幅值 0-100 的坏值 472 | measure_tobe_injected = np.c_[np.ones((1,sparse_amount)), np.zeros((1,self.measure_size-sparse_amount))][0] * 100 473 | np.random.shuffle(measure_tobe_injected) 474 | measure_tobe_injected = np.mat(measure_tobe_injected).T 475 | self.z_observed += measure_tobe_injected 476 | print('第%i时刻产生了%i个坏值!'%(t,sparse_amount)) 477 | ## 看往哪些测量注入了坏值,暂时用不上,但是保留 478 | ''' 479 | cnt = 0 480 | nonzero_cnt = 0 481 | measure = [] 482 | measure_tobe_injected_list = [] 483 | for i in np.array(measure_tobe_injected): 484 | for j in i: 485 | if round(j,1)!=0.0: 486 | measure.append(round(j,1)) 487 | measure_tobe_injected_list.append(cnt) 488 | nonzero_cnt += 1 489 | cnt += 1 490 | ''' 491 | 492 | def inject_falsedata(self, moment, conf_dic, mode): 493 | """ 494 | 注入虚假数据 495 | 该方法在指定状态后,以尽可能少地篡改测量仪表达成攻击目标(还未实现) 496 | 现在仅仅是随意攻击 497 | 498 | 输入 499 | ---- 500 | * moment: 攻击开始的时间 501 | * conf_dic: 攻击的配置 {字典} 502 | |- which_state 503 | |- effect 504 | * mode: 攻击的类型 505 | """ 506 | if mode == 'general': 507 | self.is_FDI = True 508 | elif mode == 'PCA': 509 | self.is_FDI_PCA = True 510 | self.time_falsedata = moment 511 | self.FDI_conf_dic = conf_dic 512 | 513 | def API_inject_falsedata(self, t): 514 | self.__inject_falsedata(t) 515 | 516 | def __inject_falsedata(self, t): 517 | if self.time_falsedata <= t: 518 | state_tobe_injected = np.zeros((1,self.state_size)) 519 | if len(self.FDI_conf_dic['which_state']) == 0: # 当没有输入时就用随机数 520 | sparse_amount = random.randint(1,10) # 产生对 1-10 个状态的幅值 0-100 的虚假数据攻击 521 | state_tobe_injected = np.c_[np.random.random((1,sparse_amount)), np.zeros((1,self.state_size-sparse_amount))][0] * 100 522 | np.random.shuffle(state_tobe_injected) 523 | print('第%i时刻对%i个状态注入了虚假数据'%(t,sparse_amount))#后,更改了'+str(nonzero_cnt)+'个测量值.'%(t,sparse_amount,nonzero_cnt)) 524 | else: 525 | if self.is_distribute is True: 526 | for i,j in zip(self.FDI_conf_dic['which_state'], self.FDI_conf_dic['effect']): 527 | state_tobe_injected[0,int((np.array(range(self.state_size))*self.col_reorder_matrix)[0,i])] = j 528 | else: 529 | for i,j in zip(self.FDI_conf_dic['which_state'], self.FDI_conf_dic['effect']): 530 | state_tobe_injected[0,i] = j 531 | measure_tobe_injected = (self.H + np.multiply(self.H, np.random.rand(self.measure_size,self.state_size)*0.05)) * np.mat(state_tobe_injected).T 532 | self.z_observed += measure_tobe_injected 533 | # 看哪些状态和测量被攻击了,暂时保留 534 | ''' 535 | which_state_tobe_injected_list = [] 536 | cnt = 0 537 | for i in state_tobe_injected: 538 | if i != 0: 539 | which_state_tobe_injected_list.append(cnt) 540 | cnt+=1 541 | cnt = 0 542 | nonzero_cnt = 0 543 | measure_tobe_injected_list = [] 544 | for i in measure_tobe_injected: 545 | if i!=0: 546 | measure_tobe_injected_list.append(cnt) 547 | nonzero_cnt += 1 548 | cnt += 1 549 | ''' 550 | 551 | def API_inject_falsedata_PCA(self, t): 552 | self.__inject_falsedata_PCA(t) 553 | 554 | def __inject_falsedata_PCA(self, t): 555 | if self.time_falsedata <= t: 556 | eigval,eigvec = linalg.eig(self.z_observed_history * self.z_observed_history.T) 557 | eig_enum = [] 558 | # 给z的奇异值排序 559 | for i,e in enumerate(eigval): 560 | eig_enum.append([i,e]) 561 | eig_enum.sort(key=(lambda x:x[1])) 562 | eig_sorted = [] 563 | for i in eig_enum: 564 | eig_sorted.append(i[0]) 565 | 566 | eigvec_sorted = eigvec[:,eig_sorted] 567 | H_pca = eigvec_sorted[:,:self.state_size] 568 | 569 | #H_pca = Vt[:self.state_size, :].T # shape(m,n), 取前n个特征值对应的特征向量 570 | ''' 571 | state_tobe_injected = np.c_[np.ones((1,sparse_amount)), np.zeros((1,self.state_size-sparse_amount))][0] * 100 572 | np.random.shuffle(state_tobe_injected) 573 | ''' 574 | state_tobe_injected = np.zeros((1,self.state_size)) 575 | if self.FDI_conf_dic['FDI_state'] == 0: # 当没有输入时就用随机数 576 | sparse_amount = random.randint(1,10) # 产生对 1-10 个状态的幅值 0-100 的虚假数据攻击 577 | state_tobe_injected = np.c_[np.random.random((1,sparse_amount)), np.zeros((1,self.state_size-sparse_amount))][0] * 100 578 | np.random.shuffle(state_tobe_injected) 579 | print('第%i时刻对%i个状态注入了虚假数据'%(t,sparse_amount))#后,更改了'+str(nonzero_cnt)+'个测量值.'%(t,sparse_amount,nonzero_cnt)) 580 | else: 581 | if self.is_distribute is True: 582 | for i,j in zip(self.FDI_conf_dic['which_state'], self.FDI_conf_dic['effect']): 583 | state_tobe_injected[0,int((np.array(range(self.state_size))*self.col_reorder_matrix)[0,i])] = j 584 | else: 585 | for i,j in zip(self.FDI_conf_dic['which_state'], self.FDI_conf_dic['effect']): 586 | state_tobe_injected[0,i] = j 587 | measure_tobe_injected = H_pca * np.mat(state_tobe_injected).T 588 | self.z_observed += measure_tobe_injected 589 | 590 | # 想哪些状态和测量值注入了攻击 591 | ''' 592 | which_state_tobe_injected_list = [] 593 | cnt = 0 594 | for i in state_tobe_injected: 595 | if i != 0: 596 | which_state_tobe_injected_list.append(cnt) 597 | cnt+=1 598 | 599 | cnt = 0 600 | nonzero_cnt = 0 601 | measure = [] 602 | measure_tobe_injected_list = [] 603 | for i in np.array(measure_tobe_injected): 604 | for j in i: 605 | if round(j.real,1)>1.0: 606 | measure.append(round(j.real,1)) 607 | measure_tobe_injected_list.append(cnt) 608 | nonzero_cnt += 1 609 | cnt += 1 610 | ''' 611 | print('对'+str(sparse_amount)+'个状态注入虚假数据') 612 | 613 | def __detect_baddata(self): 614 | """ 615 | 坏值检测(线性集中系统) 616 | 617 | 输入 618 | ---- 619 | NULL 620 | 621 | 返回 622 | ---- 623 | * 测量误差的二范数(float) 624 | """ 625 | is_bad = False 626 | detect_res = np.sqrt((self.z_observed - self.H * self.x_est_center).T * (self.z_observed - self.H * self.x_est_center))[0,0] 627 | if detect_res > self.chi2square_val(self.measure_size,0.5): 628 | is_bad = True 629 | return is_bad,float(detect_res) 630 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | #-*- coding: utf-8 -*- 2 | from linear_powergrid import LinearPowerGrid 3 | from nonlinear_powergrid import NonLinearPowerGrid 4 | from decentralized.distributed_linear_powergrid import DistributedLinearPowerGrid 5 | from decentralized.distributed_nonlinear_powergrid import DistributedNonLinearPowerGrid 6 | from decentralized.estimators import Richardson,Stocastic 7 | import tkinter as tk 8 | from tkinter import ttk 9 | from tkinter import messagebox 10 | import json 11 | import re 12 | import os 13 | 14 | # 不显示warnings 15 | import warnings 16 | #warnings.filterwarnings("ignore") 17 | # 18 | 19 | '''# 14-Bus 20 | #cycle 21 | PMU = [3,5,9,12] 22 | node1 = [1,2,3,4,5] 23 | node2 = [6,10,11,12,13,14] 24 | node3 = [7,8,9] 25 | nodes = [node1,node2,node3] 26 | ''' 27 | '''# 30-Bus 28 | # cycle 29 | PMU = [3,5,9,12,15,17,21,25,28] 30 | node1 = [1,2,3,4,5,6,7,8,28] 31 | node2 = [12,13,14,15,16,18] 32 | node3 = [9,10,11,17,19,20,21,22,23,24] 33 | node4 = [25,26,27,29,30] 34 | nodes = [node1,node2,node3,node4] 35 | ''' 36 | # 57-Bus 37 | PMU = [3,5,9,12,15,17,21,25,28,40,37,34] 38 | node1 = [4,5,6,18,19,20,21,45] 39 | node2 = [1,2,3,12,13,14,15,16,17,50] 40 | node3 = [7,8,24,25,26,27,28,29,30,31,52] 41 | node4 = [11,22,23,32,33,34,35,36,37,38,39,40,41,42,43,44,46,47,48,49,56,57] 42 | node5 = [9,10,51,53,54,55] 43 | nodes = [node1,node2,node3,node4,node5] 44 | ''' # 118-Bus 45 | # cycle 46 | PMU = [3,5,9,12,15,17,21,25,114,28,40,37,34,70,71,53,56,45,49,62,64,68,105,110,76,79,100,92,96,85,86,89] 47 | node1 = [1,2,3,4,5,6,7,8,9,10,11,12,14,16,117] 48 | node2 = [13,15,17,18,19,20,21,22,23,25,26,27,28,29,30,31,32,33,113,114,115] 49 | node3 = [24,38,70,71,72,73,74] 50 | node4 = [34,35,36,37,39,40,41,42,43] 51 | node5 = [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,77,80,81,100,116] 52 | node6 = [75,76,78,79,82,95,96,97,98,118] 53 | node7 = [83,84,85,86,87,88,89,90,91,92,93,94] 54 | node8 = [99,101,102,103,104,105,106,107,108,109,110,111,112] 55 | nodes = [node1,node2,node3,node4,node5,node6,node7,node8] 56 | ''' 57 | ''' 58 | # acycle 59 | node1 = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,117] 60 | node2 = [23,25,26,27,28,29,31,32,113,114,115] 61 | node3 = [15,16,17,18,19,20,21,22,24,30,33,34,35,36,37,39,40,71,72,73] 62 | node4 = [38,41,42,43,44,45,46,47,48,69,70,74,75,76,77,118] 63 | node5 = [49,50,51,54,65,66,68,78,79,80,81,82,95,96,97,98,99,116] 64 | node6 = [52,53,55,56,57,58,59,60,61,62,63,64,67] 65 | node7 = [83,84,85,86,87,88,89,90,91,92,93,94,100,101,102,103,104,105,106,107,108,109,110,111,112] 66 | nodes = [node1,node2,node3,node4,node5,node6,node7] 67 | ''' 68 | 69 | class Window(object): 70 | def __init__(self): 71 | self.wind_main = tk.Tk() 72 | self.wind_main.title('状态估计系统仿真') 73 | self.load_conf() # 载入变量 74 | ''' 菜单 ''' 75 | menu_dict = {'Files':['Export']} # 菜单项目 76 | menu_object_dict = {} # 子菜单对象字典 77 | menubar = tk.Menu(self.wind_main) 78 | for key,val in menu_dict.items(): 79 | menu_object_dict[key] = tk.Menu(menubar) # 创建子菜单对象们 80 | for item in val: 81 | menu_object_dict[key].add_command(label=item,command=lambda:self.menu_select_event(obj=item)) # 创建子菜单内的选项 82 | for menu_son,obj in menu_object_dict.items(): # 主菜单绑定子菜单们 83 | menubar.add_cascade(label=menu_son, menu=obj) 84 | self.wind_main['menu'] = menubar 85 | # 计数器 86 | line0_cnt = 0 87 | line1_cnt = 0 88 | ''' 第一列 ''' 89 | line0_frame = tk.Frame(self.wind_main) 90 | # 网络大小 91 | tmp_frame = tk.Frame(line0_frame) 92 | framework_lable = tk.Label(tmp_frame, text='SIZE') 93 | framework_lable.grid(row=0,column=0) 94 | self.network_size = ttk.Combobox(tmp_frame, textvariable=self.networkSizeVal, width=5) # 选择分布式方法 95 | self.network_size.grid(row=0,column=1) 96 | self.network_size['values']=self.network_size_list 97 | self.network_size['state']='readonly' 98 | tmp_frame.grid(row=line0_cnt,column=0);line0_cnt+=1 99 | # 选择框架(分布式/集中式) 100 | framework_lable = tk.Label(line0_frame, text='- select framework -') 101 | framework_lable.grid(row=line0_cnt,column=0, pady=10, padx=15,sticky=tk.W);line0_cnt+=1 102 | centralized = tk.Radiobutton(line0_frame, text='centralized', variable=self.isCentralizedVal, value=True,command=lambda:self.radiobutton_select_event('framework',True)) 103 | decentralized = tk.Radiobutton(line0_frame, text='decentralized', variable=self.isCentralizedVal, value=False,command=lambda:self.radiobutton_select_event('framework',False)) 104 | centralized.grid(row=line0_cnt,column=0,sticky=tk.W);line0_cnt+=1 105 | decentralized.grid(row=line0_cnt,column=0,sticky=tk.W);line0_cnt+=1 106 | # 选择框架(线性/非线性) 107 | linearlized_lable = tk.Label(line0_frame, text='- linear/nonlinear -') 108 | linearlized_lable.grid(row=line0_cnt,column=0, pady=10, padx=15,sticky=tk.W);line0_cnt+=1 109 | linearlized = tk.Radiobutton(line0_frame, text='linear', variable=self.isLinearVal, value=True,command=lambda:self.radiobutton_select_event('linearlized',True)) 110 | nonlinearlized = tk.Radiobutton(line0_frame, text='nonlinear', variable=self.isLinearVal, value=False,command=lambda:self.radiobutton_select_event('linearlized',False)) 111 | linearlized.grid(row=line0_cnt,column=0,sticky=tk.W);line0_cnt+=1 112 | nonlinearlized.grid(row=line0_cnt,column=0,sticky=tk.W);line0_cnt+=1 113 | # 非线性配置栏 114 | nonlinear_conf_frame = tk.Frame(line0_frame) 115 | linearlized_lable = tk.Label(nonlinear_conf_frame, text='- nonlinear -') 116 | linearlized_lable.grid(row=0,column=0,columnspan=2, pady=10, padx=15,sticky=tk.W) 117 | iter_time_label = tk.Label(nonlinear_conf_frame, text='iter time') 118 | self.iter_time = tk.Entry(nonlinear_conf_frame, state=tk.NORMAL, textvariable=self.nonLinearIterVal, width=5) 119 | iter_time_label.grid(row=1,column=0,sticky=tk.E) 120 | self.iter_time.grid(row=1,column=1) 121 | stop_label = tk.Label(nonlinear_conf_frame, text='stop error') 122 | self.stop = tk.Entry(nonlinear_conf_frame, state=tk.NORMAL, textvariable=self.nonLinearStopVal, width=5) 123 | stop_label.grid(row=2,column=0,sticky=tk.E) 124 | self.stop.grid(row=2,column=1) 125 | sym_label = tk.Label(nonlinear_conf_frame, text='%') 126 | sym_label.grid(row=2,column=2,sticky=tk.W) 127 | nonlinear_conf_frame.grid(row=line0_cnt,column=0,sticky=tk.E);line0_cnt+=1 128 | #只读配置 129 | if self.isLinearVal.get() is True: 130 | self.iter_time.config(state=tk.DISABLED) 131 | self.stop.config(state=tk.DISABLED) 132 | # 分布式配置栏 133 | decentralized_conf_frame = tk.Frame(line0_frame) 134 | decentralized_lable = tk.Label(decentralized_conf_frame, text='- decentralized -') 135 | decentralized_lable.grid(row=0,column=0,columnspan=2, pady=10, padx=15,sticky=tk.W) 136 | self.decentralized_method = ttk.Combobox(decentralized_conf_frame,textvariable=self.decentralizedMethodVal) # 选择分布式方法 137 | self.decentralized_method.grid(row=1,column=0,columnspan=2) 138 | self.decentralized_method['value']=('Richardson','Richardson(finite)','Stocastics') 139 | self.decentralized_method['state']='readonly' 140 | self.decentralized_method.bind("<>",self.decentralized_method_event) 141 | main_period_label = tk.Label(decentralized_conf_frame, text='main period') 142 | self.main_period = tk.Entry(decentralized_conf_frame, state=tk.NORMAL, textvariable=self.mainPeriodVal, width=5) 143 | main_period_label.grid(row=2,column=0,sticky=tk.E) 144 | self.main_period.grid(row=2,column=1) 145 | child_period_label = tk.Label(decentralized_conf_frame, text='child period') 146 | self.child_period = tk.Entry(decentralized_conf_frame, state=tk.NORMAL, textvariable=self.childPeriodVal, width=5) 147 | child_period_label.grid(row=3,column=0,sticky=tk.E) 148 | self.child_period.grid(row=3,column=1) 149 | self.synchronized = tk.Radiobutton(decentralized_conf_frame, text='synchronized', variable=self.isAsynchronizeVal, value=False,command=lambda:self.radiobutton_select_event('synchronized',True)) 150 | self.asynchronized = tk.Radiobutton(decentralized_conf_frame, text='asynchronized', variable=self.isAsynchronizeVal, value=True,command=lambda:self.radiobutton_select_event('synchronized',False)) 151 | self.synchronized.grid(row=4,column=0,sticky=tk.W) 152 | self.asynchronized.grid(row=5,column=0,sticky=tk.W) 153 | tolerance_label = tk.Label(decentralized_conf_frame, text='tolerate diff') 154 | self.tolerance = tk.Entry(decentralized_conf_frame, state=tk.NORMAL, textvariable=self.asynToleranceDiffVal, width=5) 155 | tolerance_label.grid(row=6,column=0,sticky=tk.E) 156 | self.tolerance.grid(row=6,column=1) 157 | decentralized_conf_frame.grid(row=line0_cnt,column=0,sticky=tk.E);line0_cnt+=1 158 | line0_frame.grid(row=0,column=0,sticky=tk.N) 159 | # 只读配置 160 | if self.isCentralizedVal.get() is True: # 集中式 161 | self.main_period.config(state=tk.DISABLED) 162 | self.child_period.config(state=tk.DISABLED) 163 | self.synchronized.config(state=tk.DISABLED) 164 | self.asynchronized.config(state=tk.DISABLED) 165 | self.tolerance.config(state=tk.DISABLED) 166 | else: # 分布式 167 | if self.isAsynchronizeVal.get() is False: 168 | self.tolerance.config(state=tk.DISABLED) 169 | if self.decentralizedMethodVal.get() != 'Richardson': 170 | self.child_period.config(state=tk.DISABLED) 171 | '''第二列''' 172 | line1_frame = tk.Frame(self.wind_main) 173 | # 选择模型 174 | model_frame = tk.Frame(line1_frame) 175 | model_lable = tk.Label(model_frame, text='- select model -') 176 | model_lable.grid(row=0,column=0,columnspan=2, pady=10, padx=15,sticky=tk.W) 177 | PowerGrid = tk.Radiobutton(model_frame, text='PowerGrid', variable=self.modelVal, value='PowerGrid',command=lambda:self.radiobutton_select_event('model','PowerGrid')) 178 | WSNs = tk.Radiobutton(model_frame, text='WSNs', variable=self.modelVal, value='WSNs',command=lambda:self.radiobutton_select_event('model','WSNs')) 179 | PowerGrid.grid(row=1,column=0,sticky=tk.W) 180 | WSNs.grid(row=2,column=0,sticky=tk.W) 181 | model_frame.grid(row=line1_cnt,column=0);line1_cnt+=1 182 | # 基本配置 183 | configuration_frame = tk.Frame(line1_frame) 184 | normal_lable = tk.Label(configuration_frame, text='- configuration -') 185 | normal_lable.grid(row=0,column=0,columnspan=2, pady=10,padx=15,sticky=tk.W) 186 | simTime_label = tk.Label(configuration_frame, text='simulation time') 187 | sim_time = tk.Entry(configuration_frame, state=tk.NORMAL, textvariable=self.simTimeVal, width=5) 188 | simTime_label.grid(row=1,column=0,sticky=tk.W) 189 | sim_time.grid(row=1,column=1) 190 | state_change_label = tk.Label(configuration_frame, text='state change') 191 | state_change = tk.Entry(configuration_frame, state=tk.NORMAL, textvariable=self.stateChangeVal, width=5) 192 | state_change_label.grid(row=2,column=0,sticky=tk.W) 193 | state_change.grid(row=2,column=1) 194 | configuration_frame.grid(row=line1_cnt,column=0);line1_cnt+=1 195 | # 误差配置(PMU,SCADA) 196 | variance_frame = tk.Frame(line1_frame) 197 | variance_lable = tk.Label(variance_frame, text='- variance -') 198 | variance_lable.grid(row=0,column=0,columnspan=2, pady=10,padx=15,sticky=tk.W) 199 | self.pmu_voltage_label = tk.Label(variance_frame, text='PMU Volt') 200 | self.pmu_angle_label = tk.Label(variance_frame, text='PMU Angl') 201 | self.scada_voltage_label = tk.Label(variance_frame, text='SCA Volt') 202 | self.scada_angle_label = tk.Label(variance_frame, text='SCA Powr') 203 | pmu_voltage = tk.Entry(variance_frame, state=tk.NORMAL, textvariable=self.pmuVoltageVarianceVal, width=5) 204 | pmu_angle = tk.Entry(variance_frame, state=tk.NORMAL, textvariable=self.pmuAngleVarianceVal, width=5) 205 | scada_voltage = tk.Entry(variance_frame, state=tk.NORMAL, textvariable=self.scadaVoltageVarianceVal, width=5) 206 | self.scada_angle = tk.Entry(variance_frame, state=tk.NORMAL, textvariable=self.scadaAngleVarianceVal, width=5) 207 | self.pmu_voltage_label.grid(row=1,column=0,sticky=tk.E) 208 | pmu_voltage.grid(row=1,column=1) 209 | self.pmu_angle_label.grid(row=2,column=0,sticky=tk.E) 210 | pmu_angle.grid(row=2,column=1) 211 | self.scada_voltage_label.grid(row=3,column=0,sticky=tk.E) 212 | scada_voltage.grid(row=3,column=1) 213 | self.scada_angle_label.grid(row=4,column=0,sticky=tk.E) 214 | self.scada_angle.grid(row=4,column=1) 215 | variance_frame.grid(row=line1_cnt,column=0);line1_cnt+=1 216 | line1_frame.grid(row=0,column=1,sticky=tk.N) 217 | # 只读配置 218 | if self.modelVal.get() == 'WSNs': 219 | self.pmu_voltage_label.config(text='Ref x/y') 220 | self.pmu_angle_label.config(text='Angle ') 221 | self.scada_voltage_label.config(text='Distance') 222 | self.scada_angle_label.config(text=' ') 223 | self.scada_angle.config(state=tk.DISABLED) 224 | # 画图配置 225 | plot_frame = tk.Frame(line1_frame) 226 | plot_lable = tk.Label(plot_frame, text='- plot -') 227 | plot_lable.grid(row=0,column=0, pady=10) 228 | ''' 229 | confirm_button = tk.Button(plot_frame, text='Plot configure', padx=10, pady=5, command=self.plot_button) 230 | confirm_button.grid(row=1,column=0,sticky=tk.W+tk.E) 231 | ''' 232 | is_plot = tk.Checkbutton(plot_frame, text='plot every time', variable=self.isPlotVal, onvalue=True, offvalue=False) 233 | self.is_inneriter_plot = tk.Checkbutton(plot_frame, text='plot inner iter', variable=self.isInnerIterPlotVal, onvalue=True, offvalue=False) 234 | self.is_outeriter_plot = tk.Checkbutton(plot_frame, text='plot outer iter', variable=self.isStateIterPlotVal, onvalue=True, offvalue=False) 235 | is_plot.grid(row=1,column=0,sticky=tk.W) 236 | self.is_inneriter_plot.grid(row=2,column=0,sticky=tk.W) 237 | self.is_outeriter_plot.grid(row=3,column=0,sticky=tk.W) 238 | plot_frame.grid(row=line1_cnt,column=0);line1_cnt+=1 239 | # 只读配置 240 | if self.isLinearVal.get() is True and self.isCentralizedVal is True: 241 | self.is_inneriter_plot.config(state=tk.DISABLED) 242 | self.is_outeriter_plot.config(state=tk.DISABLED) 243 | 244 | '''第三列''' 245 | line2_frame = tk.Frame(self.wind_main) 246 | # FDI攻击配置 247 | FDI_button = tk.Button(line2_frame, text='FDIA Settings', padx=10, pady=5, command=self.FDI_button_surface) # 点击进入配置界面 248 | self.checkbutton_select_event('FDIA',FDI_button) 249 | is_FDI = tk.Checkbutton(line2_frame, text='FDIA', variable=self.isFDIVal, onvalue=True, offvalue=False, command=lambda:self.checkbutton_select_event('FDIA',FDI_button)) 250 | is_FDI.grid(row=0,column=0) 251 | FDI_button.grid(row=1,column=0,sticky=tk.W+tk.E) 252 | # DoS攻击配置 253 | DoS_button = tk.Button(line2_frame, text='DoS Settings', padx=10, pady=5, command=self.DoS_button_surface) # 点击进入配置界面 254 | self.checkbutton_select_event('DoS',DoS_button) 255 | is_DoS = tk.Checkbutton(line2_frame, text='DoS', variable=self.isDoSVal, onvalue=True, offvalue=False, command=lambda:self.checkbutton_select_event('DoS',DoS_button)) 256 | is_DoS.grid(row=2,column=0) 257 | DoS_button.grid(row=3,column=0,sticky=tk.W+tk.E) 258 | 259 | line2_frame.grid(row=0,column=2,sticky=tk.N) 260 | 261 | '''确认按钮''' 262 | confirm_button = tk.Button(self.wind_main, text='Confirm', padx=10, pady=5, command=self.confirm) 263 | confirm_button.grid(row=11,column=0,columnspan=3,pady=20,sticky=tk.W+tk.E) 264 | '''窗口大小''' 265 | width=self.windowSize[0] 266 | height=self.windowSize[1] 267 | screenwidth = self.wind_main.winfo_screenwidth() 268 | screenheight = self.wind_main.winfo_screenheight() 269 | alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth-width)/2, (screenheight-height)/2) 270 | self.wind_main.geometry(alignstr) 271 | 272 | def load_conf(self): 273 | '''定义全局变量''' 274 | self.networkSizeVal = tk.StringVar() # 网络大小 275 | self.isCentralizedVal = tk.BooleanVar() 276 | self.isLinearVal = tk.BooleanVar() 277 | self.nonLinearIterVal = tk.StringVar() # 迭代次数 278 | self.nonLinearStopVal = tk.StringVar() # 停止条件(迭代误差阈值) 279 | self.mainPeriodVal = tk.StringVar() # 主算法迭代次数 280 | self.childPeriodVal = tk.StringVar() # 子算法迭代次数(目前只针对Richardson方法计算特征值) 281 | self.isAsynchronizeVal = tk.BooleanVar() # 同步/异步 282 | self.asynToleranceDiffVal = tk.StringVar() # 异步算法最大容忍落后差 283 | self.simTimeVal = tk.StringVar() # 仿真时间 284 | self.stateChangeVal = tk.StringVar() # 每一时刻的状态变化 285 | self.pmuVoltageVarianceVal = tk.StringVar() 286 | self.pmuAngleVarianceVal = tk.StringVar() 287 | self.scadaVoltageVarianceVal = tk.StringVar() 288 | self.scadaAngleVarianceVal = tk.StringVar() 289 | self.isPlotVal = tk.BooleanVar() # 是否每次迭代过程都画(针对分布式线性) 290 | self.isInnerIterPlotVal = tk.BooleanVar() # 是否画内部迭代(针对分布式非线性) 291 | self.isStateIterPlotVal = tk.BooleanVar() # 是否画状态更新迭代(针对分布式非线性) 292 | self.decentralizedMethodVal = tk.StringVar() 293 | self.modelVal = tk.StringVar() 294 | self.isFDIVal = tk.BooleanVar() # 是否实施FDI攻击 295 | self.isDoSVal = tk.BooleanVar() # 是否实施DoS攻击 296 | # 读取全局配置文件 297 | if os.path.exists('cache/config.json') is True: 298 | with open('cache/config.json','r',encoding='utf-8') as f: 299 | conf_dict = json.load(f) 300 | self.isCentralizedVal.set(conf_dict['is_centralized']) 301 | self.isLinearVal.set(conf_dict['is_linear']) 302 | self.nonLinearIterVal.set(conf_dict['nonlinear_iter_time']) 303 | self.nonLinearStopVal.set(conf_dict['nonlinear_stop_error']) 304 | self.mainPeriodVal.set(conf_dict['decentralized_main_period']) 305 | self.childPeriodVal.set(conf_dict['decentralized_child_period']) 306 | self.isAsynchronizeVal.set(conf_dict['is_asyn']) 307 | self.asynToleranceDiffVal.set(conf_dict['asyn_tolerance_diff']) 308 | self.simTimeVal.set(conf_dict['sim_time']) 309 | self.stateChangeVal.set(conf_dict['state_change']) 310 | self.pmuVoltageVarianceVal.set(conf_dict['pmu_voltage_variance']) 311 | self.pmuAngleVarianceVal.set(conf_dict['pmu_angle_variance']) 312 | self.scadaVoltageVarianceVal.set(conf_dict['scada_voltage_variance']) 313 | self.scadaAngleVarianceVal.set(conf_dict['scada_power_variance']) 314 | self.isPlotVal.set(conf_dict['is_plot']) 315 | self.isInnerIterPlotVal.set(conf_dict['is_inneriter_plot']) 316 | self.isStateIterPlotVal.set(conf_dict['is_outeriter_plot']) 317 | self.networkSizeVal.set(conf_dict['network_size']) 318 | self.decentralizedMethodVal.set(conf_dict['decentralized_method']) 319 | self.modelVal.set(conf_dict['model_name']) 320 | self.isFDIVal.set(conf_dict['is_FDI']) 321 | self.isDoSVal.set(conf_dict['is_DoS']) 322 | # GUI自用变量(以后应该从conf_dict剔除) 323 | self.network_size_list = conf_dict['network_size_list'] 324 | self.windowSize = conf_dict['window_size'] 325 | else: # 默认值 326 | self.network_size_list = ('118','300','57','30','14') # 下拉栏内容 327 | self.networkSizeVal.set('118') 328 | self.isCentralizedVal.set(False) 329 | self.isLinearVal.set(False) 330 | self.isFDIVal.set(False) 331 | self.isDoSVal.set(False) 332 | self.nonLinearIterVal.set('5') 333 | self.nonLinearStopVal.set('5') 334 | self.mainPeriodVal.set('150') 335 | self.childPeriodVal.set('100') 336 | self.isAsynchronizeVal.set(False) 337 | self.asynToleranceDiffVal.set('15') 338 | self.simTimeVal.set('4') 339 | self.stateChangeVal.set('0.3') 340 | self.pmuVoltageVarianceVal.set('0.002') 341 | self.pmuAngleVarianceVal.set('0.01') 342 | self.scadaVoltageVarianceVal.set('0.3') 343 | self.scadaAngleVarianceVal.set('0.3') 344 | self.isPlotVal.set(True) 345 | self.decentralizedMethodVal.set('Richardson') 346 | self.modelVal.set('PowerGrid') 347 | self.windowSize = [450,450] # 窗口默认大小 348 | '''定义FDI攻击变量''' 349 | self.FDIStateVal = tk.StringVar() 350 | self.FDIInjectionVal = tk.StringVar() 351 | self.FDIModeVal = tk.StringVar() 352 | self.FDIStartMomentVal = tk.IntVar() 353 | if os.path.exists('cache/FDI_conf.json') is True: 354 | with open('cache/FDI_conf.json','r',encoding='utf-8') as f: 355 | FDI_conf_dict = json.load(f) 356 | self.FDIStateVal.set(FDI_conf_dict['FDI_state']) 357 | self.FDIInjectionVal.set(FDI_conf_dict['FDI_injection']) 358 | self.FDIModeVal.set(FDI_conf_dict['FDI_mode']) 359 | self.FDIStartMomentVal.set(FDI_conf_dict['FDI_start']) 360 | self.FDI_conf_dict = { 361 | 'FDI_start': FDI_conf_dict['FDI_start'], 362 | 'FDI_state': FDI_conf_dict['FDI_state'], 363 | 'FDI_injection': FDI_conf_dict['FDI_injection'], 364 | 'FDI_mode': FDI_conf_dict['FDI_mode'], 365 | } 366 | else: # 默认配置 367 | self.FDIStateVal.set('') 368 | self.FDIInjectionVal.set('') 369 | self.FDIModeVal.set('general') 370 | self.FDIStartMomentVal.set(0) 371 | '''定义DoS攻击变量''' 372 | self.DoSStartMomentVal = tk.IntVar() 373 | self.DoSNodesVal = tk.StringVar() 374 | self.DoSDelayVal = tk.IntVar() 375 | self.DoSIsRandomVal = tk.BooleanVar() 376 | self.DoSRandomRatio = tk.IntVar() 377 | if os.path.exists('cache/DoS_conf.json') is True: 378 | with open('cache/DoS_conf.json','r',encoding='utf-8') as f: 379 | DoS_conf_dict = json.load(f) 380 | self.DoSStartMomentVal.set(DoS_conf_dict['DoS_start']) 381 | self.DoSNodesVal.set(DoS_conf_dict['DoS_nodes']) 382 | self.DoSDelayVal.set(DoS_conf_dict['DoS_delay']) 383 | self.DoSIsRandomVal.set(DoS_conf_dict['DoS_is_random']) 384 | self.DoSRandomRatio.set(DoS_conf_dict['DoS_random_ratio']) 385 | self.DoS_conf_dict = { 386 | 'DoS_start': DoS_conf_dict['DoS_start'], 387 | 'DoS_nodes': DoS_conf_dict['DoS_nodes'], 388 | 'DoS_delay': DoS_conf_dict['DoS_delay'], 389 | 'DoS_is_random': DoS_conf_dict['DoS_is_random'], 390 | 'DoS_random_ratio': DoS_conf_dict['DoS_random_ratio'], 391 | } 392 | else: # 默认配置 393 | self.DoSStartMomentVal.set(0) 394 | self.DoSNodesVal.set(0) 395 | self.DoSDelayVal.set(10) 396 | self.DoSIsRandomVal.set(False) 397 | self.DoSRandomRatio.set(5) 398 | 399 | def FDI_button_surface(self): 400 | """ 401 | FDI攻击配置界面 402 | """ 403 | wind_FDI = tk.Toplevel(self.wind_main) 404 | wind_FDI.title('FDI攻击配置') 405 | # 开始FDI时刻 406 | start_moment_lable = tk.Label(wind_FDI, text='start moment: ') 407 | start_moment = tk.Entry(wind_FDI, state=tk.NORMAL, width=3, textvariable=self.FDIStartMomentVal) 408 | start_moment_lable.grid(row=0,column=0, pady=10, padx=15,sticky=tk.W) 409 | start_moment.grid(row=0, column=1, sticky=tk.W) 410 | # 配置 411 | which_state_lable = tk.Label(wind_FDI, text='state: ') 412 | measurement_injection_lable = tk.Label(wind_FDI, text='injection: ') 413 | which_state = tk.Entry(wind_FDI, state=tk.NORMAL, textvariable=self.FDIStateVal, width=15) 414 | measurement_injection = tk.Entry(wind_FDI, state=tk.NORMAL, textvariable=self.FDIInjectionVal, width=15) 415 | which_state_lable.grid(row=1,column=0, pady=10, padx=15,sticky=tk.W) 416 | measurement_injection_lable.grid(row=2,column=0, pady=10, padx=15,sticky=tk.W) 417 | which_state.grid(row=1,column=1) 418 | measurement_injection.grid(row=2,column=1) 419 | # 选择攻击模式 420 | FDI_mode_lable = tk.Label(wind_FDI, text='- FDI Mode -') 421 | general_FDI = tk.Radiobutton(wind_FDI, text='general', variable=self.FDIModeVal, value='general',command=lambda:self.radiobutton_select_event('FDI_mode','general')) 422 | PCA_FDI = tk.Radiobutton(wind_FDI, text='PCA', variable=self.FDIModeVal, value='PCA',command=lambda:self.radiobutton_select_event('FDI_mode','PCA')) 423 | FDI_mode_lable.grid(row=3,column=0,columnspan=2) 424 | general_FDI.grid(row=4,column=0,columnspan=2,sticky=tk.W) 425 | PCA_FDI.grid(row=5,column=0,columnspan=2,sticky=tk.W) 426 | # 保存按钮 427 | FDI_conf_dict = { 428 | 'FDI_start':start_moment, 429 | 'FDI_state':which_state, 430 | 'FDI_injection':measurement_injection, 431 | 'FDI_mode': self.FDIModeVal.get(), 432 | } 433 | save_button = tk.Button(wind_FDI, text='Save', padx=10, pady=5, command=lambda:self.saveconf_event('FDI', FDI_conf_dict, wind_FDI)) 434 | save_button.grid(row=6,column=0,columnspan=2,pady=20,sticky=tk.W+tk.E) 435 | # 窗口大小 436 | width=260 437 | height=280 438 | screenwidth = wind_FDI.winfo_screenwidth() 439 | screenheight = wind_FDI.winfo_screenheight() 440 | alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth-width)/2, (screenheight-height)/2) 441 | wind_FDI.geometry(alignstr) 442 | 443 | def DoS_button_surface(self): 444 | """ 445 | DoS攻击配置界面 446 | """ 447 | wind_DoS = tk.Toplevel(self.wind_main) 448 | wind_DoS.title('DoS攻击配置') 449 | '''第一列''' 450 | col1_frame = tk.Frame(wind_DoS) 451 | # 开始DoS时刻 452 | start_moment_lable = tk.Label(col1_frame, text='start moment: ') 453 | start_moment = tk.Entry(col1_frame, state=tk.NORMAL, width=3, textvariable=self.DoSStartMomentVal) 454 | start_moment_lable.grid(row=0,column=0, pady=10, padx=15,sticky=tk.W) 455 | start_moment.grid(row=0, column=1, sticky=tk.W) 456 | # 配置 457 | delay_lable = tk.Label(col1_frame, text='delay: ') 458 | which_nodes_lable = tk.Label(col1_frame, text='nodes: ') 459 | delay = tk.Entry(col1_frame, state=tk.NORMAL, textvariable=self.DoSDelayVal, width=3) 460 | which_nodes = tk.Entry(col1_frame, state=tk.NORMAL, textvariable=self.DoSNodesVal, width=15) 461 | delay_lable.grid(row=1,column=0, pady=10, padx=15,sticky=tk.E) 462 | which_nodes_lable.grid(row=2,column=0, pady=10, padx=15,sticky=tk.E) 463 | delay.grid(row=1,column=1,sticky=tk.W) 464 | which_nodes.grid(row=2,column=1,sticky=tk.W) 465 | # 结束第一列 466 | col1_frame.grid(row=0,column=0) 467 | '''第二列''' 468 | col2_frame = tk.Frame(wind_DoS) 469 | # 随机DoS攻击 和 攻击率 470 | DoS_ratio_label = tk.Label(col2_frame, text='ratio: ') 471 | DoS_ratio = tk.Entry(col2_frame, state=tk.NORMAL, textvariable=self.DoSRandomRatio, width=3) 472 | self.checkbutton_select_event('DoS_random', DoS_ratio) 473 | is_random = tk.Checkbutton(col2_frame, text='Random', variable=self.DoSIsRandomVal, onvalue=True, offvalue=False, command=lambda:self.checkbutton_select_event('DoS_random',DoS_ratio)) 474 | is_random.grid(row=0,column=0,sticky=tk.W) 475 | DoS_ratio_label.grid(row=1,column=0,sticky=tk.E) 476 | DoS_ratio.grid(row=1,column=1,sticky=tk.W) 477 | # 结束第二列 478 | col2_frame.grid(row=0,column=1,sticky=tk.N) 479 | # 保存按钮 480 | DoS_conf_dict = { 481 | 'DoS_start':start_moment, 482 | 'DoS_nodes':which_nodes, 483 | 'DoS_delay':delay, 484 | 'DoS_is_random':is_random, 485 | 'DoS_random_ratio':DoS_ratio, 486 | } 487 | save_button = tk.Button(wind_DoS, text='Save', padx=10, pady=5, command=lambda:self.saveconf_event('DoS', DoS_conf_dict, wind_DoS)) 488 | save_button.grid(row=6,column=0,columnspan=2,pady=20,sticky=tk.W+tk.E) 489 | # 窗口大小 490 | width=260 491 | height=250 492 | screenwidth = wind_DoS.winfo_screenwidth() 493 | screenheight = wind_DoS.winfo_screenheight() 494 | alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth-width)/2, (screenheight-height)/2) 495 | wind_DoS.geometry(alignstr) 496 | 497 | def checkbutton_select_event(self, which, param): 498 | """ 499 | 在checkbutton被选中时, 执行相应的操作. 500 | 501 | 输入 502 | ---- 503 | which: 哪一个checkbutton发生的事件; 504 | param: 需要操控的组件 505 | """ 506 | if which == 'FDIA': 507 | if self.isFDIVal.get() is True: 508 | param.config(state=tk.NORMAL) 509 | else: 510 | param.config(state=tk.DISABLED) 511 | if which == 'DoS': 512 | if self.isDoSVal.get() is True: 513 | param.config(state=tk.NORMAL) 514 | else: 515 | param.config(state=tk.DISABLED) 516 | if which == 'DoS_random': 517 | if self.DoSIsRandomVal.get() is True: 518 | param.config(state=tk.NORMAL) 519 | else: 520 | param.config(state=tk.DISABLED) 521 | 522 | def radiobutton_select_event(self, which, param): 523 | """ 524 | 在radiobutton被选中时, 执行相应的操作. 525 | 526 | 输入 527 | ---- 528 | which: 哪一个checkbutton发生的事件; 529 | param: 选择参数 530 | """ 531 | if which == 'model': # 选择模型的事件 532 | if param == 'WSNs': 533 | self.network_size['values']=('8','4') 534 | self.network_size_list = ('8','4') 535 | # variance 536 | self.pmu_voltage_label.config(text='Ref x/y') 537 | self.pmu_angle_label.config(text='Angle') 538 | self.scada_voltage_label.config(text='Distance') 539 | self.scada_angle_label.config(text=' ') 540 | self.scada_angle.config(state=tk.DISABLED) 541 | elif param == 'PowerGrid': 542 | self.network_size['values']=('118','300','57','30','14') 543 | self.network_size_list = ('118','300','57','30','14') 544 | #variance 545 | self.pmu_voltage_label.config(text='PMU Volt') 546 | self.pmu_angle_label.config(text='PMU Angl') 547 | self.scada_voltage_label.config(text='SCA Volt') 548 | self.scada_angle_label.config(text='SCA Powr') 549 | self.scada_angle.config(state=tk.NORMAL) 550 | self.network_size.current(0) 551 | elif which == 'framework': # 选中框架的事件 552 | if param is True: 553 | self.main_period.config(state=tk.DISABLED) 554 | self.child_period.config(state=tk.DISABLED) 555 | self.synchronized.config(state=tk.DISABLED) 556 | self.asynchronized.config(state=tk.DISABLED) 557 | self.tolerance.config(state=tk.DISABLED) 558 | # plot button 559 | self.is_inneriter_plot.config(state=tk.DISABLED) 560 | self.is_outeriter_plot.config(state=tk.DISABLED) 561 | else: 562 | self.main_period.config(state=tk.NORMAL) 563 | self.child_period.config(state=tk.NORMAL) 564 | self.synchronized.config(state=tk.NORMAL) 565 | self.asynchronized.config(state=tk.NORMAL) 566 | self.tolerance.config(state=tk.NORMAL) 567 | # plot button 568 | if self.isLinearVal.get() is False: 569 | self.is_inneriter_plot.config(state=tk.NORMAL) 570 | self.is_outeriter_plot.config(state=tk.NORMAL) 571 | elif which == 'linearlized': # 选中是否线性模型的事件 572 | if param is True: 573 | self.iter_time.config(state=tk.DISABLED) 574 | self.stop.config(state=tk.DISABLED) 575 | # plot button 576 | self.is_inneriter_plot.config(state=tk.DISABLED) 577 | self.is_outeriter_plot.config(state=tk.DISABLED) 578 | else: 579 | self.iter_time.config(state=tk.NORMAL) 580 | self.stop.config(state=tk.NORMAL) 581 | # plot button 582 | if self.isCentralizedVal.get() is False: 583 | self.is_inneriter_plot.config(state=tk.NORMAL) 584 | self.is_outeriter_plot.config(state=tk.NORMAL) 585 | elif which == 'synchronized': 586 | if param is True: 587 | self.tolerance.config(state=tk.DISABLED) 588 | else: 589 | self.tolerance.config(state=tk.NORMAL) 590 | elif which == 'FDI_mode': # 选中FDI模式 591 | if param == 'general': 592 | pass 593 | elif param == 'PCA': 594 | pass 595 | 596 | def menu_select_event(self, obj): 597 | ''' 598 | if obj == 'Export': 599 | wind_export = tk.Toplevel(self.wind_main) 600 | wind_export.title('导出') 601 | self.isPlotVal = tk.BooleanVar() # 是否 602 | self.isInnerIterPlotVal = tk.BooleanVar() # 是否 603 | is_plot = tk.Checkbutton(wind_export, text='plot every time', variable=self.isPlotVal, onvalue=True, offvalue=False) 604 | is_inneriter_plot = tk.Checkbutton(wind_export, text='plot nonlinear inner iter', variable=self.isInnerIterPlotVal, onvalue=True, offvalue=False) 605 | is_plot.grid(row=0,column=0) 606 | is_inneriter_plot.grid(row=1,column=0) 607 | # 确认按钮 608 | confirm_button = tk.Button(wind_export, text='Confirm', padx=10, pady=5, command=self.plot_confirm) 609 | confirm_button.grid(row=2,column=0,pady=20,sticky=tk.W+tk.E) 610 | # 窗口大小 611 | width=200 612 | height=150 613 | screenwidth = wind_export.winfo_screenwidth() 614 | screenheight = wind_export.winfo_screenheight() 615 | alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth-width)/2, (screenheight-height)/2) 616 | wind_export.geometry(alignstr) 617 | ''' 618 | 619 | def decentralized_method_event(self, event): 620 | if self.decentralizedMethodVal.get() == 'Richardson': 621 | self.child_period.config(state=tk.NORMAL) 622 | else: 623 | self.child_period.config(state=tk.DISABLED) 624 | 625 | ''' 626 | def plot_button(self): 627 | """ 628 | 画图配置 629 | """ 630 | wind_plot = tk.Toplevel(self.wind_main) 631 | wind_plot.title('画图配置') 632 | self.isPlotVal = tk.BooleanVar() # 是否每次迭代过程都画(针对分布式线性) 633 | self.isInnerIterPlotVal = tk.BooleanVar() # 是否画内部迭代(针对分布式非线性) 634 | is_plot = tk.Checkbutton(wind_plot, text='plot every time', variable=self.isPlotVal, onvalue=True, offvalue=False) 635 | is_inneriter_plot = tk.Checkbutton(wind_plot, text='plot nonlinear inner iter', variable=self.isInnerIterPlotVal, onvalue=True, offvalue=False) 636 | is_plot.grid(row=0,column=0) 637 | is_inneriter_plot.grid(row=1,column=0) 638 | # 确认按钮 639 | confirm_button = tk.Button(wind_plot, text='Confirm', padx=10, pady=5, command=self.plot_confirm) 640 | confirm_button.grid(row=2,column=0,pady=20,sticky=tk.W+tk.E) 641 | # 窗口大小 642 | width=200 643 | height=150 644 | screenwidth = wind_plot.winfo_screenwidth() 645 | screenheight = wind_plot.winfo_screenheight() 646 | alignstr = '%dx%d+%d+%d' % (width, height, (screenwidth-width)/2, (screenheight-height)/2) 647 | wind_plot.geometry(alignstr) 648 | ''' 649 | 650 | def saveconf_event(self, which, conf, wind): 651 | """ 652 | 保存配置结果 653 | 654 | 输入 655 | ---- 656 | which: 什么事情触发的事件 657 | conf: 要保存的配置 658 | """ 659 | if which == 'FDI': 660 | a = re.split(r'[\s\,]+', conf['FDI_state'].get()) 661 | b = re.split(r'[\s\,]+', conf['FDI_injection'].get()) 662 | if len(a) != len(b): 663 | raise('Amount of these two guys must be the same.') 664 | if len(a) != 0: 665 | c = [int(i) for i in a] 666 | d = [float(i) for i in b] 667 | conf_dict = { 668 | 'FDI_start': self.FDIStartMomentVal.get(), 669 | 'FDI_state': c, 670 | 'FDI_injection': d, 671 | 'FDI_mode': self.FDIModeVal.get() 672 | } 673 | self.FDI_conf_dict = conf_dict 674 | elif which == 'DoS': 675 | a = re.findall(r'\d', self.DoSNodesVal.get()) 676 | if len(a) != 0: 677 | b = [int(i) for i in a] 678 | conf_dict = { 679 | 'DoS_start': self.DoSStartMomentVal.get(), 680 | 'DoS_nodes': b, 681 | 'DoS_delay': self.DoSDelayVal.get(), 682 | 'DoS_is_random': self.DoSIsRandomVal.get(), 683 | 'DoS_random_ratio': self.DoSRandomRatio.get(), 684 | } 685 | self.DoS_conf_dict = conf_dict 686 | with open('cache/'+ which +'_conf.json','w',encoding='utf-8') as f: 687 | f.write(json.dumps(conf_dict,ensure_ascii=False)) 688 | tk.messagebox.showinfo('提示','保存成功') 689 | wind.destroy() 690 | 691 | def confirm(self): 692 | """ 693 | 仿真按钮触发事件 694 | """ 695 | # 记录当前窗口大小 696 | self.windowSize[0]=self.wind_main.winfo_width() 697 | self.windowSize[1]=self.wind_main.winfo_height() 698 | # 记录当前配置 699 | conf_dict = { 700 | 'network_size': self.networkSizeVal.get(), 701 | 'is_centralized': self.isCentralizedVal.get(), 702 | 'is_linear': self.isLinearVal.get(), 703 | 'sim_time': self.simTimeVal.get(), 704 | 'state_change': self.stateChangeVal.get(), 705 | 'pmu_voltage_variance': self.pmuVoltageVarianceVal.get(), 706 | 'pmu_angle_variance': self.pmuAngleVarianceVal.get(), 707 | 'scada_voltage_variance': self.scadaVoltageVarianceVal.get(), 708 | 'scada_power_variance': self.scadaAngleVarianceVal.get(), 709 | 'nonlinear_iter_time': self.nonLinearIterVal.get(), 710 | 'nonlinear_stop_error': self.nonLinearStopVal.get(), 711 | 'decentralized_method': self.decentralizedMethodVal.get(), 712 | 'decentralized_main_period': self.mainPeriodVal.get(), 713 | 'decentralized_child_period': self.childPeriodVal.get(), 714 | 'is_asyn': self.isAsynchronizeVal.get(), 715 | 'asyn_tolerance_diff': self.asynToleranceDiffVal.get(), 716 | 'is_plot': self.isPlotVal.get(), 717 | 'is_inneriter_plot': self.isInnerIterPlotVal.get(), 718 | 'is_outeriter_plot': self.isStateIterPlotVal.get(), 719 | 'model_name': self.modelVal.get(), 720 | 'is_FDI': self.isFDIVal.get(), 721 | 'is_DoS': self.isDoSVal.get(), 722 | # GUI自用变量 723 | 'network_size_list': self.network_size_list, 724 | 'window_size': self.windowSize, 725 | } 726 | # 保存当前配置 727 | with open('cache/config.json','w',encoding='utf-8') as f: 728 | f.write(json.dumps(conf_dict,ensure_ascii=False)) 729 | # 添加配置 730 | if self.isDoSVal.get() is True: 731 | conf_dict['is_DoS'] = True 732 | conf_dict['DoS_dict'] = self.DoS_conf_dict 733 | else: 734 | conf_dict['is_DoS'] = False 735 | # 开始跑仿真 736 | if conf_dict['is_centralized'] is True: 737 | if conf_dict['is_linear'] is True: 738 | model = LinearPowerGrid(PMU, conf_dict) 739 | else: 740 | model = NonLinearPowerGrid(PMU, conf_dict) 741 | else: 742 | if conf_dict['is_linear'] is True: 743 | model = DistributedLinearPowerGrid(nodes, PMU, conf_dict) 744 | else: 745 | model = DistributedNonLinearPowerGrid(nodes, PMU, conf_dict) 746 | # FDI 747 | if conf_dict['is_FDI'] is True: 748 | # [10,28,50,100,200,17,75,91,125,171], [5,4.5,4,3.5,3,30,30,30,30,30] 749 | false_dic = {'which_state':self.FDI_conf_dict['FDI_state'],'effect':self.FDI_conf_dict['FDI_injection']} #自定义FDI 750 | model.inject_falsedata(moment=self.FDI_conf_dict['FDI_start'], conf_dic=false_dic, mode=self.FDI_conf_dict['FDI_mode']) # 在1时刻注入虚假数据 751 | # 估计 752 | model.estimator() 753 | 754 | def main(): 755 | w = Window() 756 | w.wind_main.mainloop() 757 | exit() 758 | 759 | if __name__ == '__main__': 760 | main() 761 | -------------------------------------------------------------------------------- /nonlinear_powergrid.py: -------------------------------------------------------------------------------- 1 | from linear_powergrid import LinearPowerGrid 2 | import numpy as np 3 | import random 4 | import matplotlib.pyplot as plt 5 | from tqdm import tqdm 6 | import matplotlib.pylab as pylab 7 | 8 | class NonLinearPowerGrid(LinearPowerGrid): 9 | def __init__(self, pmu=[], conf_dict={}): 10 | super().__init__(pmu=pmu, conf_dict=conf_dict) 11 | self.x_est_center = np.mat(self.x_real,dtype=complex) 12 | 13 | def estimator(self, once=False): 14 | """ 15 | 调用估计器 16 | 17 | 输入 18 | ---- 19 | once: 当取True时,一般是被子类调用,只进行一次估计,然后检测,其它什么都别干 20 | 21 | 返回 22 | ---- 23 | NULL 24 | """ 25 | a = np.copy(self.x_real) 26 | b = np.zeros((self.size*2,1), dtype=complex) 27 | state_error_mat = np.mat(np.eye(self.state_size)) 28 | #is_bad = False 29 | #residual = 0.0 30 | if once is True: 31 | for u in range(self.iter_time): 32 | J,Phi = self.jaccobi_H(self.x_est_center) 33 | z_model = self.create_measurement(self.x_est_center) 34 | self.x_est_center += (J.H*self.R_I*J).I* J.H*self.R_I*(self.z_observed - z_model) 35 | #is_bad,residual = self.__detect_baddata() 36 | #return is_bad,residual 37 | else: 38 | if self.is_FDI_PCA is True: 39 | self.z_observed_history = np.mat(np.empty((self.measure_size,0))) 40 | res = [[],[],[],[]] # 估计、预测、真实 41 | for t in range(self.sim_time+1): 42 | if self.is_baddata is True: 43 | self.__inject_baddata(t) 44 | if self.is_FDI is True: 45 | self.__inject_falsedata(t) 46 | elif self.is_FDI_PCA is True: 47 | self.z_observed_history = np.column_stack((self.z_observed_history, self.z_observed)) 48 | self.__inject_falsedata_PCA(t) 49 | # 非线性最小二乘估计(迭代) 50 | bar=tqdm(total=self.iter_time) 51 | for u in range(self.iter_time): 52 | J,Phi = self.jaccobi_H(self.x_est_center) 53 | z_model = self.create_measurement(self.x_est_center) 54 | self.x_est_center += Phi.I* J.H*self.R_I*(self.z_observed - z_model) 55 | bar.update(1) 56 | bar.close() 57 | res[0].append(complex(self.x_est_center[10])) 58 | #is_bad,residual = self.__detect_baddata() 59 | #if is_bad is True: 60 | # print('第%i时刻检测到坏值,估计的残差为: %.3f' % (t, residual)) 61 | res[2].append(complex(self.x_real[10])) 62 | res[3].append(np.array(self.x_est_center-self.x_real)[:,0]) 63 | if t is not self.sim_time: 64 | a,b,state_error_mat = self.predict(self.x_est_center,[a,b,state_error_mat]) 65 | self.next() 66 | res[1].append(complex(self.x_predict[10])) 67 | plt.figure('状态演变') 68 | plt.subplot(211) 69 | plt.title('第10状态跟随图') 70 | plt.plot(res[0], 'g*-') 71 | plt.plot([0]+res[1], 'b*-') 72 | plt.plot(res[2], 'y*-') 73 | plt.legend(['估计','预测','真实'], loc='upper right', frameon=False) 74 | plt.xlabel("时刻") 75 | plt.ylabel("幅值") 76 | plt.subplot(212) 77 | plt.title('状态误差') 78 | plt.plot(res[3], '.-') 79 | plt.show() 80 | -------------------------------------------------------------------------------- /powergridbase.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | from base import StateEstimationBase 3 | import numpy as np 4 | #import scipy as sp 5 | import sympy as sb 6 | from numpy import linalg 7 | from scipy.linalg import block_diag 8 | from sklearn.decomposition import PCA 9 | from decimal import Decimal 10 | from autograd import grad 11 | import os 12 | import json 13 | 14 | import extract_config 15 | 16 | ###### 真实误差参数 ###### 17 | # PMU: 电压-0.002%pu; 相角-0.01度 18 | # SCADA: 电压-0.3%pu; 功率-0.3%pu 19 | 20 | def voltage(x, bus): 21 | return x 22 | 23 | def angle(x,bus): 24 | return x 25 | 26 | def active_power(params, v1, v2, a1, a2): 27 | """ 28 | 传输线路上有功功率的计算公式: 29 | P_ij = G_ij*Vi**2 - G_ij*Vi*Vj*sb.cos(Theta_i - Theta_j) - B_ij*Vi*Vj*sb.sin(Theta_i - Theta_j) 30 | 31 | 输入 32 | ---- 33 | * params: np.array((Gij, Bij, BSHij)) 34 | * v1: 总线电压(out) 35 | * v2: 总线电压(in) 36 | * a1: 电压相角(out) 37 | * a2: 电压相角(in) 38 | 39 | 返回 40 | ---- 41 | * 有功功率 42 | """ 43 | weights = np.dot(params, np.array([ 44 | [1,0,0], 45 | [1,0,0], 46 | [0,1,0]]) ) 47 | states = np.array(v1**2, -v1*v2*np.cos(a1-a2), -v1*v2*np.sin(a1-a2)) 48 | return np.dot(weights, states) 49 | 50 | def reactive_power(params, v1, v2, a1, a2): 51 | """ 52 | 传输线路上无功功率的计算公式: 53 | Q_ij = -(B_ij + BSH_ij)*Vi**2 + B_ij*Vi*Vj*sb.cos(Theta_i-Theta_j) - G_ij*Vi*Vj*sb.sin(Theta_i-Theta_j) 54 | 55 | 输入 56 | ---- 57 | * params: np.array((Gij, Bij, BSHij)) 58 | * v1: 总线电压(out) 59 | * v2: 总线电压(in) 60 | * a1: 电压相角(out) 61 | * a2: 电压相角(in) 62 | 63 | 返回 64 | ---- 65 | * 无功功率 66 | """ 67 | weights = np.dot(params, np.array([ 68 | [0,1,1], 69 | [0,1,0], 70 | [1,0,0]]) ) 71 | states = np.array(-v1**2, v1*v2*np.cos(a1-a2), -v1*v2*np.sin(a1-a2)) 72 | return np.dot(weights, states) 73 | 74 | class PowerGridBase(StateEstimationBase): 75 | def __init__(self, conf_path, bus_rule_path, branch_rule_path, pmu=[], conf_dict={}): 76 | # GUI 配置 77 | self.model_name = conf_dict['model_name'] 78 | self.size = int(conf_dict['network_size']) 79 | self.state_variance = float(conf_dict['state_change']) 80 | self.sim_time = int(conf_dict['sim_time']) 81 | self.pmu_voltage_variance = float(conf_dict['pmu_voltage_variance']) 82 | self.pmu_angle_variance = float(conf_dict['pmu_angle_variance']) 83 | self.scada_voltage_variance = float(conf_dict['scada_voltage_variance']) 84 | self.scada_power_variance = float(conf_dict['scada_power_variance']) 85 | # 非线性参数 86 | self.iter_time = int(conf_dict['nonlinear_iter_time']) 87 | self.stop_error = int(conf_dict['nonlinear_stop_error']) 88 | 89 | self.pmu = [] 90 | for i in range(self.size): 91 | if i in pmu: 92 | self.pmu.append('PMU') 93 | else: 94 | self.pmu.append('SCADA') 95 | # 状态轨迹 96 | self.A = np.mat(np.eye(2*self.size), dtype=complex) # 状态转移矩阵 97 | self.G = np.mat(np.zeros((2*self.size,1), dtype=complex)) # 状态轨迹向量 98 | # 初始化 99 | self.GBBSH = [] 100 | self.branch = [] 101 | self.H = np.mat(np.zeros([0, 2*self.size]), dtype=complex) # 量测矩阵 102 | self.R_real = np.mat(np.zeros([0, 0]), dtype=complex) # 真实量测协方差矩阵 103 | self.R = np.mat(np.zeros([0, 0]), dtype=complex) # 计算量测协方差矩阵 104 | self.z_observed = np.mat(np.zeros([0,0])) # 量测值 105 | self.measure_who = [] # Indicate whose z: single i -> bus i 106 | # [i, i] -> pmu i 107 | # [i, j] -> branch i -> j 108 | self.x_who = list(range(1,self.size+1)) # Indicate whose x: per x has 2 members 109 | # Defult the order of Bus 110 | # 标签 111 | self.is_distribute = False # Defult is a centralized system 112 | self.is_reference_deleted = False 113 | 114 | # 构建系统模型 115 | self.model_function() 116 | 117 | # 从配置文件提取 118 | if os.path.exists('cache/IEEE_'+str(self.size)+'_info.json') is True: # 从缓存提取 119 | with open('cache/IEEE_'+str(self.size)+'_info.json','r',encoding='utf-8') as f: 120 | saved_conf = json.load(f, object_hook=as_complex) 121 | self.x_real = np.mat(saved_conf['x_real']) 122 | self.bus_type = saved_conf['bus_type'] 123 | GBBSH = saved_conf['GBBSH'] 124 | bus = saved_conf['bus'] 125 | branch = saved_conf['branch'] 126 | self.bus_info_dict = self.set_nodes_info(bus,branch,self.pmu,GBBSH,'single') 127 | else: 128 | self.bus_cdf = extract_config.tools(conf_path, bus_rule_path, 0) 129 | self.branch_cdf = extract_config.tools(conf_path, branch_rule_path, 1) 130 | x_real = self.bus_cdf.get_items([6, 8]) #第一次获得的真实状态值 131 | x = np.mat(np.zeros([2*self.size,1])) 132 | for cnt,(i, j) in enumerate(zip(x_real[0], x_real[1])): 133 | x[2*cnt, 0] = i 134 | x[2*cnt+1, 0] = j 135 | self.x_real = x 136 | self.bus_type = self.bus_cdf.get_items(5) # Bus type: 0 -> Load 137 | # 2 -> Generation 138 | # 3 -> Reference 139 | self.__gen_gbbsh(is_ignore=False) 140 | bus = self.bus_cdf.get_items(0) 141 | self.bus_info_dict = self.set_nodes_info(bus, zip(self.branch[0],self.branch[1]), self.pmu, zip(self.GBBSH[0], self.GBBSH[1], self.GBBSH[2]), 'single') 142 | # 保存当前配置 143 | conf_to_save = { 144 | 'x_real': self.x_real.tolist(), 145 | 'bus_type': self.bus_type, 146 | 'GBBSH': list(zip(self.GBBSH[0], self.GBBSH[1], self.GBBSH[2])), 147 | 'bus': bus, 148 | 'branch': list(zip(self.branch[0],self.branch[1])) 149 | } 150 | with open('cache/IEEE_'+str(self.size)+'_info.json','w',encoding='utf-8') as f: 151 | f.write(json.dumps(conf_to_save,ensure_ascii=False,cls=ComplexEncoder)) 152 | 153 | def model_function(self): 154 | """ 155 | 设置量测模型,并配置相关变量和参数 156 | """ 157 | if self.model_name is 'PowerGrid': 158 | # 为求偏导准备的符号 159 | G_ij = sb.symbols("G_ij") 160 | B_ij = sb.symbols("B_ij") 161 | BSH_ij = sb.symbols("BSH_ij") 162 | Vi = sb.symbols("V_i") 163 | Vj = sb.symbols("V_j") 164 | Theta_i = sb.symbols("Theta_i") 165 | Theta_j = sb.symbols("Theta_j") 166 | # alpha_ij = sb.symbols("alpha_ij") 不知道什么用 167 | # 边量测计算公式 168 | P_ij = G_ij*Vi**2 - G_ij*Vi*Vj*sb.cos(Theta_i - Theta_j) - B_ij*Vi*Vj*sb.sin(Theta_i - Theta_j) 169 | Q_ij = -(B_ij + BSH_ij)*Vi**2 + B_ij*Vi*Vj*sb.cos(Theta_i-Theta_j) - G_ij*Vi*Vj*sb.sin(Theta_i-Theta_j) 170 | self.h = [P_ij, Q_ij] 171 | self.value_symbol = [G_ij, B_ij, BSH_ij] 172 | self.state_i_symbol = [Vi, Theta_i] 173 | self.state_j_symbol = [Vj, Theta_j] 174 | self.jacobi_h_ij = self.__jacob(self.h, self.state_i_symbol) 175 | self.jacobi_h_ji = self.__jacob(self.h, self.state_j_symbol) 176 | #self.h_egde = [active_power, reactive_power] 177 | elif self.model_name is 'WSNs': 178 | X = sb.symbols("X") 179 | Y = sb.symbols("Y") 180 | Xi = sb.symbols("X_i") 181 | Xj = sb.symbols("X_j") 182 | Yi = sb.symbols("Y_i") 183 | Yj = sb.symbols("Y_j") 184 | # 本地测量 185 | M_ij = sb.sqrt((X-Xi)**2 + (Y-Yi)**2) # 已知节点对未知节点的测量 186 | # 边量测计算公式 187 | Z_ij = sb.sqrt((Xj-Xi)**2 + (Yj-Yi)**2) # 未知节点之间的测量 188 | self.h = [Z_ij] 189 | self.value_symbol = [X, Y] 190 | self.state_i_symbol = [Xi, Yi] 191 | self.state_j_symbol = [Xj, Yj] 192 | self.jacobi_h_ij = self.__jacob(self.h, self.state_i_symbol) 193 | self.jacobi_h_ji = self.__jacob(self.h, self.state_j_symbol) 194 | 195 | def __gen_gbbsh(self, is_ignore=False): 196 | """ 197 | 计算建模所需电路参数 198 | 199 | 输入 200 | ---- 201 | * 电阻r(resistance), 202 | * 电抗x(reactance), 203 | * 分流电导gsh, 204 | * 分流电纳bsh, 205 | * is_ignore: 是否忽略接地shunt和传输电阻,若忽略则H是实数矩阵 206 | 207 | 计算 208 | ---- 209 | * 电导g(conductance), 210 | * 电纳b(susceptance), 211 | * 分流导纳ysh(admittance_shunt) 212 | 213 | 公式 214 | ---- 215 | Note: 阻抗z(impedance) 216 | z = r + jx |=> g = r/(r**2 + x**2) 217 | y = g + jb = z^{-1} |=> b = x/(r**2 + x**2) 218 | branch上的ysh = (相连的两个bus上的ysh)/2 219 | 220 | 返回 221 | ---- 222 | self.GBBSH = ((g), (b), (ysh)); 223 | """ 224 | branch_num = self.branch_cdf.get_items([0,2]) # extract the bus number of out-in side. 225 | resistance = self.branch_cdf.get_items(6) # line resistance 226 | reactance = self.branch_cdf.get_items(7) # line reactance 227 | shunt_conductance = self.bus_cdf.get_items(16) # Shunt conductance 228 | shunt_susceptance = self.bus_cdf.get_items(17) # Shunt susceptance 229 | 230 | self.branch = tuple(branch_num) 231 | if is_ignore is False: 232 | conductance = tuple([r/(r**2 + x**2) for r,x in zip(resistance, reactance)]) 233 | susceptance = tuple([-x/(r**2 + x**2) for r,x in zip(resistance, reactance)]) 234 | else: 235 | conductance = tuple([0 for r,x in zip(resistance, reactance)]) 236 | susceptance = tuple([1 for r,x in zip(resistance, reactance)]) 237 | self.GBBSH.append(conductance) 238 | self.GBBSH.append(susceptance) 239 | 240 | shunt_admittance_bus =[] 241 | shunt_admittance_branch = [] 242 | if is_ignore is False: 243 | for c, s in zip(shunt_conductance, shunt_susceptance): 244 | shunt_admittance_bus.append( complex(c, s) ) 245 | for o, i in zip(branch_num[0], branch_num[1]): 246 | shunt_admittance_branch.append((shunt_admittance_bus[o-1] + shunt_admittance_bus[i-1])/2) 247 | else: # 忽略对地shunt,电导为0 248 | for o, i in zip(branch_num[0], branch_num[1]): 249 | shunt_admittance_branch.append(0) 250 | self.GBBSH.append(shunt_admittance_branch) 251 | 252 | self.GBBSH = tuple(self.GBBSH) 253 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | sympy 2 | scipy 3 | tqdm 4 | sklearn 5 | matplotlib 6 | autograd 7 | -------------------------------------------------------------------------------- /rules/rule_WSNs_branch: -------------------------------------------------------------------------------- 1 | # 1-int 2-float 3-character 9-end-flag 2 | SEPARATOR 2 3 | rcv_node_number 1 4 | send_node_number 1 5 | distance 2 6 | -999 9 7 | -------------------------------------------------------------------------------- /rules/rule_WSNs_nodes: -------------------------------------------------------------------------------- 1 | # 1-int 2-float 3-character 9-end-flag 2 | SEPARATOR 2 3 | node_number 1 4 | location_x 2 5 | location_y 2 6 | is_ref 1 7 | -999 9 8 | -------------------------------------------------------------------------------- /rules/rule_ieeecdf_branch: -------------------------------------------------------------------------------- 1 | # 1-int 2-float 3-character 9-end-flag 2 | SEPARATOR 2 3 | Tap_bus_number 1 4 | Z_bus_number 1 5 | Load_flow_area 1 6 | Loss_zone 1 7 | Circuit 1 8 | Type 1 9 | Branch_resistance 2 10 | Branch_reactance 2 11 | Line_charging 2 12 | Line_MVA_rating_No_1 1 13 | Line_MVA_rating_No_2 1 14 | Line_MVA_rating_No_3 1 15 | Control_bus_number 1 16 | Side 1 17 | Transformer_final_turns_ratio 2 18 | Transformer_final_angle 2 19 | Minimum_tap 2 20 | Maximum_tap 2 21 | Step_size 2 22 | Minimum_voltage 2 23 | Maximum_voltage 2 24 | -999 9 -------------------------------------------------------------------------------- /rules/rule_ieeecdf_bus: -------------------------------------------------------------------------------- 1 | # 1-int 2-float 3-character 9-end-flag 2 | SEPARATOR 2 3 | bus_number 1 4 | name 3 5 | name_non 3 6 | load_flow_area_number 3 7 | loss_zone_number 1 8 | type 1 9 | final_voltage 2 10 | final_angle 2 11 | load_MW 2 12 | load_MVAR 2 13 | generation_MW 2 14 | generation_MVAR 2 15 | base_KV 2 16 | desired_volts 2 17 | max_MVAR 2 18 | min_MVAR 2 19 | shunt_conductance 2 20 | shunt_susceptance 2 21 | remote_controlled_bus_number 1 22 | -999 9 -------------------------------------------------------------------------------- /rules/rule_ieeepsap_bus: -------------------------------------------------------------------------------- 1 | FIXEDSEAT 3 2 | Bus_number 1 1-4 3 | Change_code 1 6-6 4 | Continue_code 1 7-7 5 | Regulated_bus_code 1 8-8 6 | Name 3 10-21 7 | Bus_voltage 1 23-26 8 | Bus_angle 2 27-30 9 | Generation_MW 2 31-35 10 | Generation_MVAR 1 36-40 11 | Generation_MVAR_low_limit 1 41-45 12 | Generation_MVAR_high_limit 1 46-50 13 | Bus_controls_voltage 1 51-55 14 | Load_MW 1 56-60 15 | Load_MVAR 1 61-65 16 | Shunt_MVAR 1 66-70 17 | Load_flow_area 1 71-72 18 | 9999 9 -------------------------------------------------------------------------------- /rules/rule_ieeepsap_line: -------------------------------------------------------------------------------- 1 | FIXEDSEAT 3 2 | From_bus_number 1 1-4 3 | Change_code 1 6-6 4 | 'C' 3 7-7 5 | To_bus_number 1 9-12 6 | Circuit_number 1 14-14 7 | Load_flow_area 3 16-16 8 | Line_resistance 2 18-23 9 | Line_reactance 2 24-29 10 | Line_charging_MVAR 2 30-35 11 | Transformer_tap 2 36-40 12 | Min_tap 2 41-45 13 | Max_tap 2 46-50 14 | Phase_shift_angle 2 51-55 15 | Remote_voltage_bus_number 1 56-60 16 | Normal_MVA_rating 1 61-64 17 | Emergency_MVA_rating 1 65-68 18 | MVA_Base 2 69-72 19 | 9999 9 -------------------------------------------------------------------------------- /topology/WSNs4.txt: -------------------------------------------------------------------------------- 1 | # 08/25/93 UW ARCHIVE 100.0 1961 W IEEE 118 Bus Test Case 2 | #BUS DATA FOLLOWS 57 ITEMS 3 | 1 7.000 7.000 3 4 | 2 0.000 0.000 0 5 | 3 3.250 0.467 0 6 | 4 2.180 3.130 0 7 | -999 8 | #BRANCH DATA FOLLOWS 80 ITEMS 9 | 2 3 3.2647 10 | 2 4 6.7984 11 | 3 1 7.0767 12 | 3 4 6.3922 13 | 4 1 4.5749 14 | -999 15 | END OF DATA 16 | -------------------------------------------------------------------------------- /topology/WSNs8.txt: -------------------------------------------------------------------------------- 1 | # 08/25/93 UW ARCHIVE 100.0 1961 W IEEE 118 Bus Test Case 2 | #BUS DATA FOLLOWS 57 ITEMS 3 | 1 0.000 0.500 3 4 | 2 10.00 0.000 3 5 | 3 1.250 10.00 3 6 | 4 9.880 9.130 3 7 | 5 3.250 0.810 0 8 | 6 6.770 3.240 0 9 | 7 3.970 7.850 0 10 | 8 7.130 5.890 0 11 | -999 12 | #BRANCH DATA FOLLOWS 80 ITEMS 13 | 5 1 3.2647 14 | 5 2 6.7984 15 | 5 7 7.0767 16 | 5 8 6.3922 17 | 6 2 4.5749 18 | 6 4 6.6606 19 | 6 5 4.2773 20 | 7 1 8.3536 21 | 7 3 3.4671 22 | 7 4 6.0470 23 | 8 4 4.2497 24 | 8 6 2.6743 25 | -999 26 | END OF DATA 27 | -------------------------------------------------------------------------------- /topology/ieee118psp.txt: -------------------------------------------------------------------------------- 1 | # 1 2 | #IEEE 118 bus test case 3 | 4 4 | 1 2 T 3.03 9.99 2.54 100 110 5 | 1 3 T 1.29 4.24 1.082 100 110 6 | 4 5 T 0.176 0.798 0.21 100 110 7 | 3 5 T 2.41 10.8 2.84 100 110 8 | 5 6 T 1.19 5.4 1.426 100 110 9 | 6 7 T 0.459 2.08 0.55 100 110 10 | 8 9 T 0.244 3.05 116.2 100 110 11 | 8 C 5 T 0.0 2.67 0.0 985 985 985 8 100 110 12 | 8 5 1.000 1.000 13 | 9 10 T 0.258 3.22 123.0 100 110 14 | 4 11 T 2.09 6.88 1.748 100 110 15 | 5 11 T 2.03 6.82 1.738 100 110 16 | 11 12 T 0.595 1.96 0.502 100 110 17 | 2 12 T 1.87 6.16 1.572 100 110 18 | 3 12 T 4.84 16.0 4.06 100 110 19 | 7 12 T 0.862 3.4 0.874 100 110 20 | 11 13 T 2.225 7.31 1.876 100 110 21 | 12 14 T 2.15 7.07 1.816 100 110 22 | 13 15 T 7.44 24.44 6.268 100 110 23 | 14 15 T 5.95 19.5 5.02 100 110 24 | 12 16 T 2.12 8.34 2.14 100 110 25 | 15 17 T 1.32 4.37 4.44 100 110 26 | 16 17 T 4.54 18.01 4.66 100 110 27 | 17 18 T 1.23 5.05 1.298 100 110 28 | 18 19 T 1.119 4.93 1.142 100 110 29 | 19 20 T 2.52 11.7 2.98 100 110 30 | 15 19 T 1.2 3.94 1.01 100 110 31 | 20 21 T 1.83 8.49 2.16 100 110 32 | 21 22 T 2.09 9.7 2.46 100 110 33 | 22 23 T 3.42 15.9 4.04 100 110 34 | 23 24 T 1.35 4.92 4.98 100 110 35 | 23 25 T 1.56 8.0 8.64 100 110 36 | 26 C 25 T 0.0 3.82 0.0 960 960 960 26 100 110 37 | 26 25 1.000 1.000 38 | 25 27 T 3.18 16.3 17.64 100 110 39 | 27 28 T 1.913 8.55 2.16 100 110 40 | 28 29 T 2.37 9.43 2.38 100 110 41 | 30 C 17 T 0.0 3.88 0.0 960 960 960 30 100 110 42 | 30 17 1.000 1.000 43 | 8 30 T 0.431 5.04 51.4 100 110 44 | 26 30 T 0.799 8.6 90.8 100 110 45 | 17 31 T 4.74 15.63 3.99 100 110 46 | 29 31 T 1.08 3.31 0.83 100 110 47 | 23 32 T 3.17 11.53 11.73 100 110 48 | 31 32 T 2.98 9.85 2.51 100 110 49 | 27 32 T 2.29 7.55 1.926 100 110 50 | 15 33 T 3.8 12.44 3.194 100 110 51 | 19 34 T 7.52 24.7 6.32 100 110 52 | 35 36 T 0.224 1.02 0.268 100 110 53 | 35 37 T 1.1 4.97 1.318 100 110 54 | 33 37 T 4.15 14.2 3.66 100 110 55 | 34 36 T 0.871 2.68 0.568 100 110 56 | 34 37 T 0.256 0.94 0.984 100 110 57 | 38 C 37 T 0.0 3.75 0.0 935 935 935 100 110 58 | 38 37 1.000 1.000 59 | 37 39 T 3.21 10.6 2.7 100 110 60 | 37 40 T 5.93 16.8 4.2 100 110 61 | 30 38 T 0.464 5.4 42.2 100 110 62 | 39 40 T 1.84 6.05 1.552 100 110 63 | 40 41 T 1.45 4.87 1.222 100 110 64 | 40 42 T 5.55 18.3 4.66 100 110 65 | 41 42 T 4.1 13.5 3.44 100 110 66 | 43 44 T 6.08 24.54 6.068 100 110 67 | 34 43 T 4.13 16.81 4.226 100 110 68 | 44 45 T 2.24 9.01 2.24 100 110 69 | 45 46 T 4.0 13.56 3.32 100 110 70 | 46 47 T 3.8 12.7 3.16 100 110 71 | 46 48 T 6.01 18.9 4.72 100 110 72 | 47 49 T 1.91 6.25 1.604 100 110 73 | 42 49 T 7.15 32.3 8.6 100 110 74 | 42 49 T 7.15 32.3 8.6 100 110 75 | 45 49 T 6.84 18.6 4.44 100 110 76 | 48 49 T 1.79 5.05 1.258 100 110 77 | 49 50 T 2.67 7.52 1.874 100 110 78 | 49 51 T 4.86 13.7 3.42 100 110 79 | 51 52 T 2.03 5.88 1.396 100 110 80 | 52 53 T 4.05 16.35 4.058 100 110 81 | 53 54 T 2.63 12.2 3.1 100 110 82 | 49 54 T 7.3 28.9 7.38 100 110 83 | 49 54 T 8.69 29.1 7.3 100 110 84 | 54 55 T 1.69 7.07 2.02 100 110 85 | 54 56 T 0.275 0.955 0.732 100 110 86 | 55 56 T 0.488 1.51 0.374 100 110 87 | 56 57 T 3.43 9.66 2.42 100 110 88 | 50 57 T 4.74 13.4 3.32 100 110 89 | 56 58 T 3.43 9.66 2.42 100 110 90 | 51 58 T 2.55 7.19 1.788 100 110 91 | 54 59 T 5.03 22.93 5.98 100 110 92 | 56 59 T 8.25 25.1 5.69 100 110 93 | 56 59 T 8.03 23.9 5.36 100 110 94 | 55 59 T 4.739 21.58 5.646 100 110 95 | 59 60 T 3.17 14.5 3.76 100 110 96 | 59 61 T 3.28 15.0 3.88 100 110 97 | 60 61 T 0.264 1.35 1.456 100 110 98 | 60 62 T 1.23 5.61 1.468 100 110 99 | 61 62 T 0.824 3.76 0.98 100 110 100 | 63 C 59 T 0.0 3.86 0.0 960 960 960 63 100 110 101 | 63 59 1.000 1.000 102 | 63 64 T 0.172 2.0 21.6 100 110 103 | 64 C 61 T 0.0 2.68 0.0 985 985 985 64 100 110 104 | 64 61 1.000 1.000 105 | 38 65 T 0.901 9.86 104.6 100 110 106 | 64 65 T 0.269 3.02 38.0 100 110 107 | 49 66 T 1.80 9.19 2.48 100 110 108 | 49 66 T 1.80 9.19 2.48 100 110 109 | 62 66 T 4.82 21.8 5.78 100 110 110 | 62 67 T 2.58 11.7 3.1 100 110 111 | 65 C 66 T 0.0 3.7 0.0 935 935 935 65 100 110 112 | 63 59 1.000 1.000 113 | 66 67 T 2.24 10.15 2.682 100 110 114 | 65 68 T 0.138 1.6 63.8 100 110 115 | 47 69 T 8.44 27.78 7.092 100 110 116 | 49 69 T 9.85 32.4 8.28 100 110 117 | 68 C 69 T 0.0 3.7 0.0 935 935 935 68 100 110 118 | 68 69 1.000 1.000 119 | 69 70 T 3.0 12.7 12.2 100 110 120 | 24 70 T 0.221 41.15 10.198 100 110 121 | 70 71 T 0.882 3.55 0.878 100 110 122 | 24 72 T 4.88 19.6 4.88 100 110 123 | 71 72 T 4.46 18.0 4.444 100 110 124 | 71 73 T 0.866 4.54 1.178 100 110 125 | 70 74 T 4.01 13.23 3.368 100 110 126 | 70 75 T 4.28 14.1 3.6 100 110 127 | 69 75 T 4.05 12.2 12.4 100 110 128 | 74 75 T 1.23 4.06 1.034 100 110 129 | 76 77 T 4.44 14.8 3.68 100 110 130 | 69 77 T 3.09 10.1 10.38 100 110 131 | 75 77 T 6.01 19.99 4.978 100 110 132 | 77 78 T 0.376 1.24 1.264 100 110 133 | 78 79 T 0.546 2.44 0.648 100 110 134 | 77 80 T 1.7 4.85 4.72 100 110 135 | 77 80 T 2.94 10.5 2.28 100 110 136 | 79 80 T 1.56 7.04 1.87 100 110 137 | 68 81 T 0.175 2.02 80.8 100 110 138 | 81 C 80 T 0.0 3.7 0.0 935 935 935 81 100 110 139 | 81 80 1.000 1.000 140 | 77 82 T 2.98 8.53 8.174 100 110 141 | 82 83 T 1.12 3.665 3.796 100 110 142 | 83 84 T 6.25 13.2 2.58 100 110 143 | 83 85 T 4.3 14.8 3.48 100 110 144 | 84 85 T 3.02 6.41 1.234 100 110 145 | 85 86 T 3.5 12.3 2.76 100 110 146 | 86 87 T 2.828 20.74 4.45 100 110 147 | 85 88 T 2.0 10.2 2.76 100 110 148 | 85 89 T 2.39 17.3 4.7 100 110 149 | 88 89 T 1.39 7.12 1.934 100 110 150 | 89 90 T 5.18 18.8 5.28 100 110 151 | 89 90 T 2.38 9.97 10.6 100 110 152 | 90 91 T 2.54 8.36 2.14 100 110 153 | 89 92 T 0.99 5.05 5.48 100 110 154 | 89 92 T 3.93 15.81 4.14 100 110 155 | 91 92 T 3.87 12.72 3.268 100 110 156 | 92 93 T 2.58 8.48 2.18 100 110 157 | 92 94 T 4.81 15.8 4.06 100 110 158 | 93 94 T 2.23 7.32 1.876 100 110 159 | 94 95 T 1.32 4.34 1.11 100 110 160 | 80 96 T 3.56 18.2 4.94 100 110 161 | 82 96 T 1.62 5.3 5.44 100 110 162 | 94 96 T 2.69 8.69 2.3 100 110 163 | 80 97 T 1.83 9.34 2.54 100 110 164 | 80 98 T 2.38 10.8 2.86 100 110 165 | 80 99 T 4.54 20.6 5.46 100 110 166 | 92 100 T 6.48 29.5 7.72 100 110 167 | 94 100 T 1.78 5.8 6.04 100 110 168 | 95 96 T 1.71 5.47 1.474 100 110 169 | 96 97 T 1.73 8.85 2.4 100 110 170 | 98 100 T 3.97 17.9 4.76 100 110 171 | 99 100 T 1.8 8.13 2.16 100 110 172 | 100 101 T 2.77 12.62 3.28 100 110 173 | 92 102 T 1.23 5.59 1.464 100 110 174 | 101 102 T 2.46 11.2 2.94 100 110 175 | 100 103 T 1.6 5.25 5.36 100 110 176 | 100 104 T 4.51 20.4 5.41 100 110 177 | 103 104 T 4.66 15.84 4.07 100 110 178 | 103 105 T 5.35 16.25 4.08 100 110 179 | 100 106 T 6.05 22.9 6.2 100 110 180 | 104 105 T 0.994 3.78 0.986 100 110 181 | 105 106 T 1.4 5.47 1.434 100 110 182 | 105 107 T 5.3 18.3 4.72 100 110 183 | 105 108 T 2.61 7.03 1.844 100 110 184 | 106 107 T 5.3 18.3 4.72 100 110 185 | 108 109 T 1.05 2.88 0.76 100 110 186 | 103 110 T 3.906 18.13 4.61 100 110 187 | 109 110 T 2.78 7.62 2.02 100 110 188 | 110 111 T 2.2 7.55 2.0 100 110 189 | 110 112 T 2.47 6.4 6.2 100 110 190 | 17 113 T 0.913 3.01 0.768 100 110 191 | 32 113 T 6.15 20.3 5.18 100 110 192 | 32 114 T 1.35 6.12 1.628 100 110 193 | 27 115 T 1.64 7.41 1.972 100 110 194 | 114 115 T 0.23 1.04 0.276 100 110 195 | 68 116 T 0.034 0.405 16.4 100 110 196 | 12 117 T 3.29 14.0 3.58 100 110 197 | 75 118 T 1.45 4.81 1.198 100 110 198 | 76 118 T 1.64 5.44 1.356 100 110 199 | 9999 200 | # 5 201 | 1 1 Riverside138 955 0 -5 15 1 51 27 1 202 | 2 Pokagon 138 1000 0 20 9 1 203 | 3 HickoryCk138 1000 0 39 10 1 204 | 4 1 NCarlisle138 998 0 -9 0 -300 300 4 30 12 1 205 | 5 Olive 138 1000 0 0 0 -40 1 206 | 6 1 Kankakee 138 990 0 -13 50 6 52 22 1 207 | 7 JacksonRd138 1000 0 19 2 1 208 | 8 1 Olive 345 1015 0 -28 0 -300 300 8 0 0 1 209 | 9 Beguine 345 1000 0 0 0 1 210 | 10 1 Breed 345 1050 0 450 0 -147 200 10 0 0 1 211 | 11 SouthBend138 1000 0 70 23 1 212 | 12 1 TwnBranch138 990 0 85 -35 120 12 47 10 1 213 | 13 Concord 138 1000 0 34 16 1 214 | 14 GoshenJct138 1000 0 14 1 1 215 | 15 1 FortWayne138 970 0 -10 30 15 90 30 1 216 | 16 N. E. 138 1000 0 25 10 1 217 | 17 Sorenson 138 1000 0 11 3 1 218 | 18 1 McKinley 138 973 0 -16 50 18 60 34 1 219 | 19 1 Lincoln 138 962 0 -8 24 19 45 25 1 220 | 20 Adams 138 1000 0 18 3 1 221 | 21 Jay 138 1000 0 14 8 1 222 | 22 Randolph 138 1000 0 10 5 1 223 | 23 CollgeCnr138 1000 0 7 3 1 224 | 24 1 Trenton 138 992 0 -13 -300 300 24 0 0 1 225 | 25 1 TannersCk138 1050 0 220 -47 140 25 0 0 1 226 | 26 1 TannersCk345 1015 0 314 -1000 1000 26 0 0 1 227 | 27 1 Madison 138 968 0 -9 -300 300 27 62 13 1 228 | 28 Mullin 138 1000 0 17 7 1 229 | 29 Grant 138 1000 0 24 4 1 230 | 30 Sorenson 345 1000 0 0 0 1 231 | 31 1 DeerCreek138 967 0 7 -300 300 31 43 27 1 232 | 32 1 Delaware 138 963 0 -14 42 32 59 23 1 233 | 33 Haviland 138 1000 0 23 9 1 234 | 34 1 Rockhill 138 984 0 -8 24 34 59 26 14 1 235 | 35 West Lima138 1000 0 33 9 1 236 | 36 1 Sterling 138 980 0 -8 24 36 31 17 1 237 | 37 East Lima138 1000 0 0 0 -25 1 238 | 38 East Lima345 1000 0 0 0 1 239 | 39 NwLiberty138 1000 0 27 11 1 240 | 40 1 West End 138 970 0 -46 -300 300 40 20 23 1 241 | 41 S. Tiffin138 1000 0 37 10 1 242 | 42 1 Howard 138 985 0 -59 -300 300 42 37 23 1 243 | 43 S. Kenton138 1000 0 18 7 1 244 | 44 WMtVernon138 1000 0 16 8 10 1 245 | 45 N. Newark138 1000 0 53 22 10 1 246 | 46 1 W.Lancstr138 1005 0 19 -100 100 46 28 10 10 1 247 | 47 Crooksvil138 1000 0 34 0 1 248 | 48 Zanesvile138 1000 0 20 11 15 1 249 | 49 1 Philo 138 1025 0 204 -85 210 49 87 30 1 250 | 50 W.Cambrdg138 1000 0 17 4 1 251 | 51 Newcmrstn138 1000 0 17 8 1 252 | 52 SCoshoctn138 1000 0 18 5 1 253 | 53 Wooster 138 1000 0 23 11 1 254 | 54 1 Torrey 138 955 0 48 -300 300 54 113 32 1 255 | 55 1 Wagenhals138 952 0 -8 23 55 63 22 1 256 | 56 1 Sunnyside138 954 0 -8 15 56 84 18 1 257 | 57 WNwPhila1138 1000 0 12 3 1 258 | 58 WNwPhila2138 1000 0 12 3 1 259 | 59 1 Tidd 138 985 0 155 -60 180 59 277 113 1 260 | 60 SW Kammer138 1000 0 78 3 1 261 | 61 1 W. Kammer138 995 0 160 -100 300 61 0 0 1 262 | 62 1 Natrium 138 998 0 -20 20 62 77 14 1 263 | 63 Tidd 345 1000 0 0 0 1 264 | 64 Kammer 345 1000 0 0 0 1 265 | 65 1 Muskingum345 1005 0 391 -67 200 65 0 0 1 266 | 66 1 Muskingum138 1050 0 392 -67 200 66 39 18 1 267 | 67 Summerfld138 1000 0 28 7 1 268 | 68 Sporn 345 1000 0 0 0 1 269 | 69 2 Sporn 138 1035 0.5 16.4 -300 300 69 0 0 1 270 | 70 1 Portsmoth138 984 0 -10 32 70 66 20 1 271 | 71 NPortsmth138 1000 0 0 0 1 272 | 72 1 Hillsboro138 980 0 -12 -100 100 72 0 0 1 273 | 73 1 Sargents 138 991 0 -6 -100 100 73 0 0 1 274 | 74 1 Bellefont138 958 0 -6 9 74 68 27 12 1 275 | 75 Sth Point138 1000 0 47 11 1 276 | 76 1 Darrah 138 943 0 -8 23 76 68 36 1 277 | 77 1 Turner 138 1006 0 -20 70 77 61 28 1 278 | 78 Chemical 138 1000 0 71 26 1 279 | 79 CapitolHl138 1000 0 39 32 20 1 280 | 80 1 Cabin Crk138 1040 0 477 -165 280 80 130 26 1 281 | 81 Kanawha 345 1000 0 0 0 1 282 | 82 Logan 138 1000 0 54 27 20 1 283 | 83 Sprigg 138 1000 0 20 10 10 1 284 | 84 BetsyLayn138 1000 0 11 7 1 285 | 85 1 BeaverCrk138 985 0 -8 23 85 24 15 1 286 | 86 Hazard 138 1000 0 21 10 1 287 | 87 1 Pineville161 1015 0 4 -100 1000 87 0 0 1 288 | 88 Fremont 138 1000 0 48 10 1 289 | 89 1 ClinchRvr138 1005 0 607 -210 300 89 0 0 1 290 | 90 1 Holston 138 985 0 -85 -300 300 90 78 42 1 291 | 91 1 HolstonTP138 980 0 -10 -100 100 91 0 0 1 292 | 92 1 Saltville138 990 0 -3 9 92 65 10 1 293 | 93 Tazewell 138 1000 0 12 7 1 294 | 94 Switchbak138 1000 0 30 16 1 295 | 95 Caldwell 138 1000 0 42 31 1 296 | 96 Baileysvl138 1000 0 38 15 1 297 | 97 Sundial 138 1000 0 15 9 1 298 | 98 Bradley 138 1000 0 34 8 1 299 | 99 1 Hinton 138 1010 0 -42 -100 100 99 0 0 1 300 | 100 1 Glen Lyn 138 1017 0 252 -50 155 100 37 18 1 301 | 101 Wythe 138 1000 0 22 15 1 302 | 102 Smyth 138 1000 0 5 3 1 303 | 103 1 Claytor 138 1010 0 40 -15 40 103 23 16 1 304 | 104 1 Hancock 138 971 0 -8 23 104 38 25 1 305 | 105 1 Roanoke 138 965 0 -8 23 105 31 26 20 1 306 | 106 Cloverdle138 1000 0 43 16 1 307 | 107 1 Reusens 138 952 0 -22 -200 200 107 28 12 6 1 308 | 108 Blaine 138 1000 0 2 1 1 309 | 109 Franklin 138 1000 0 8 3 1 310 | 110 1 Fieldale 138 973 0 -8 23 110 39 30 6 1 311 | 111 1 Dan River138 980 0 36 -100 1000 111 0 0 1 312 | 112 1 Danville 138 975 0 -43 -100 1000 112 25 13 1 313 | 113 1 DeerCk TP138 993 0 -6 -100 200 113 0 0 1 314 | 114 W Medford138 1000 0 8 3 1 315 | 115 Medford 138 1000 0 22 7 1 316 | 116 1 Kyger Crk138 1005 0 -184 -1000 1000 116 0 0 1 317 | 117 Corey 138 1000 0 20 8 1 318 | 118 WHuntngdn138 1000 0 33 15 1 319 | 9999 320 | # 15 321 | 1 69 0 10.00Our Utility 322 | 9999 323 |  -------------------------------------------------------------------------------- /topology/ieee14cdf.txt: -------------------------------------------------------------------------------- 1 | # 08/19/93 UW ARCHIVE 100.0 1962 W IEEE 14 Bus Test Case 2 | #BUS DATA FOLLOWS 14 ITEMS 3 | 1 Bus-1 HV 1 1 3 1.060 0.0 0.0 0.0 232.4 -16.9 0.0 1.060 0.0 0.0 0.0 0.0 0 4 | 2 Bus-2 HV 1 1 2 1.045 -4.98 21.7 12.7 40.0 42.4 0.0 1.045 50.0 -40.0 0.0 0.0 0 5 | 3 Bus-3 HV 1 1 2 1.010 -12.72 94.2 19.0 0.0 23.4 0.0 1.010 40.0 0.0 0.0 0.0 0 6 | 4 Bus-4 HV 1 1 0 1.019 -10.33 47.8 -3.9 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 7 | 5 Bus-5 HV 1 1 0 1.020 -8.78 7.6 1.6 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 8 | 6 Bus-6 LV 1 1 2 1.070 -14.22 11.2 7.5 0.0 12.2 0.0 1.070 24.0 -6.0 0.0 0.0 0 9 | 7 Bus-7 ZV 1 1 0 1.062 -13.37 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 10 | 8 Bus-8 TV 1 1 2 1.090 -13.36 0.0 0.0 0.0 17.4 0.0 1.090 24.0 -6.0 0.0 0.0 0 11 | 9 Bus-9 LV 1 1 0 1.056 -14.94 29.5 16.6 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.19 0 12 | 10 Bus-10 LV 1 1 0 1.051 -15.10 9.0 5.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 13 | 11 Bus-11 LV 1 1 0 1.057 -14.79 3.5 1.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 14 | 12 Bus-12 LV 1 1 0 1.055 -15.07 6.1 1.6 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 15 | 13 Bus-13 LV 1 1 0 1.050 -15.16 13.5 5.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 16 | 14 Bus-14 LV 1 1 0 1.036 -16.04 14.9 5.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 17 | -999 18 | #BRANCH DATA FOLLOWS 20 ITEMS 19 | 1 2 1 1 1 0 0.01938 0.05917 0.0528 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 20 | 1 5 1 1 1 0 0.05403 0.22304 0.0492 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 21 | 2 3 1 1 1 0 0.04699 0.19797 0.0438 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 22 | 2 4 1 1 1 0 0.05811 0.17632 0.0340 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 23 | 2 5 1 1 1 0 0.05695 0.17388 0.0346 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 24 | 3 4 1 1 1 0 0.06701 0.17103 0.0128 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 25 | 4 5 1 1 1 0 0.01335 0.04211 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 26 | 4 7 1 1 1 0 0.0 0.20912 0.0 0 0 0 0 0 0.978 0.0 0.0 0.0 0.0 0.0 0.0 27 | 4 9 1 1 1 0 0.0 0.55618 0.0 0 0 0 0 0 0.969 0.0 0.0 0.0 0.0 0.0 0.0 28 | 5 6 1 1 1 0 0.0 0.25202 0.0 0 0 0 0 0 0.932 0.0 0.0 0.0 0.0 0.0 0.0 29 | 6 11 1 1 1 0 0.09498 0.19890 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 30 | 6 12 1 1 1 0 0.12291 0.25581 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 31 | 6 13 1 1 1 0 0.06615 0.13027 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 32 | 7 8 1 1 1 0 0.0 0.17615 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 33 | 7 9 1 1 1 0 0.0 0.11001 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 34 | 9 10 1 1 1 0 0.03181 0.08450 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 35 | 9 14 1 1 1 0 0.12711 0.27038 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 36 | 10 11 1 1 1 0 0.08205 0.19207 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 37 | 12 13 1 1 1 0 0.22092 0.19988 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 38 | 13 14 1 1 1 0 0.17093 0.34802 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 39 | -999 40 | LOSS ZONES FOLLOWS 1 ITEMS 41 | 1 IEEE 14 BUS 42 | -99 43 | INTERCHANGE DATA FOLLOWS 1 ITEMS 44 | 1 2 Bus 2 HV 0.0 999.99 IEEE14 IEEE 14 Bus Test Case 45 | -9 46 | TIE LINES FOLLOWS 0 ITEMS 47 | -999 48 | END OF DATA 49 | -------------------------------------------------------------------------------- /topology/ieee30cdf.txt: -------------------------------------------------------------------------------- 1 | # 08/20/93 UW ARCHIVE 100.0 1961 W IEEE 30 Bus Test Case 2 | #BUS DATA FOLLOWS 30 ITEMS 3 | 1 Glen-Lyn 132 1 1 3 1.060 0.0 0.0 0.0 260.2 -16.1 132.0 1.060 0.0 0.0 0.0 0.0 0 4 | 2 Claytor 132 1 1 2 1.043 -5.48 21.7 12.7 40.0 50.0 132.0 1.045 50.0 -40.0 0.0 0.0 0 5 | 3 Kumis 132 1 1 0 1.021 -7.96 2.4 1.2 0.0 0.0 132.0 0.0 0.0 0.0 0.0 0.0 0 6 | 4 Hancock 132 1 1 0 1.012 -9.62 7.6 1.6 0.0 0.0 132.0 0.0 0.0 0.0 0.0 0.0 0 7 | 5 Fieldale 132 1 1 2 1.010 -14.37 94.2 19.0 0.0 37.0 132.0 1.010 40.0 -40.0 0.0 0.0 0 8 | 6 Roanoke 132 1 1 0 1.010 -11.34 0.0 0.0 0.0 0.0 132.0 0.0 0.0 0.0 0.0 0.0 0 9 | 7 Blaine 132 1 1 0 1.002 -13.12 22.8 10.9 0.0 0.0 132.0 0.0 0.0 0.0 0.0 0.0 0 10 | 8 Reusens 132 1 1 2 1.010 -12.10 30.0 30.0 0.0 37.3 132.0 1.010 40.0 -10.0 0.0 0.0 0 11 | 9 Roanoke 1.0 1 1 0 1.051 -14.38 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 0.0 0 12 | 10 Roanoke 33 1 1 0 1.045 -15.97 5.8 2.0 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.19 0 13 | 11 Roanoke 11 1 1 2 1.082 -14.39 0.0 0.0 0.0 16.2 11.0 1.082 24.0 -6.0 0.0 0.0 0 14 | 12 Hancock 33 1 1 0 1.057 -15.24 11.2 7.5 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 15 | 13 Hancock 11 1 1 2 1.071 -15.24 0.0 0.0 0.0 10.6 11.0 1.071 24.0 -6.0 0.0 0.0 0 16 | 14 Bus-14 33 1 1 0 1.042 -16.13 6.2 1.6 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 17 | 15 Bus-15 33 1 1 0 1.038 -16.22 8.2 2.5 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 18 | 16 Bus-16 33 1 1 0 1.045 -15.83 3.5 1.8 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 19 | 17 Bus-17 33 1 1 0 1.040 -16.14 9.0 5.8 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 20 | 18 Bus-18 33 1 1 0 1.028 -16.82 3.2 0.9 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 21 | 19 Bus-19 33 1 1 0 1.026 -17.00 9.5 3.4 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 22 | 20 Bus-20 33 1 1 0 1.030 -16.80 2.2 0.7 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 23 | 21 Bus-21 33 1 1 0 1.033 -16.42 17.5 11.2 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 24 | 22 Bus-22 33 1 1 0 1.033 -16.41 0.0 0.0 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 25 | 23 Bus-23 33 1 1 0 1.027 -16.61 3.2 1.6 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 26 | 24 Bus-24 33 1 1 0 1.021 -16.78 8.7 6.7 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.043 0 27 | 25 Bus-25 33 1 1 0 1.017 -16.35 0.0 0.0 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 28 | 26 Bus-26 33 1 1 0 1.000 -16.77 3.5 2.3 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 29 | 27 Cloverdle 33 1 1 0 1.023 -15.82 0.0 0.0 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 30 | 28 Cloverdle 32 1 1 0 1.007 -11.97 0.0 0.0 0.0 0.0 132.0 0.0 0.0 0.0 0.0 0.0 0 31 | 29 Bus-29 33 1 1 0 1.003 -17.06 2.4 0.9 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 32 | 30 Bus-30 33 1 1 0 0.992 -17.94 10.6 1.9 0.0 0.0 33.0 0.0 0.0 0.0 0.0 0.0 0 33 | -999 34 | #BRANCH DATA FOLLOWS 41 ITEMS 35 | 1 2 1 1 1 0 0.0192 0.0575 0.0528 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 36 | 1 3 1 1 1 0 0.0452 0.1652 0.0408 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 37 | 2 4 1 1 1 0 0.0570 0.1737 0.0368 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 38 | 3 4 1 1 1 0 0.0132 0.0379 0.0084 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 39 | 2 5 1 1 1 0 0.0472 0.1983 0.0418 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 40 | 2 6 1 1 1 0 0.0581 0.1763 0.0374 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 41 | 4 6 1 1 1 0 0.0119 0.0414 0.0090 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 42 | 5 7 1 1 1 0 0.0460 0.1160 0.0204 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 43 | 6 7 1 1 1 0 0.0267 0.0820 0.0170 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 44 | 6 8 1 1 1 0 0.0120 0.0420 0.0090 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 45 | 6 9 1 1 1 0 0.0 0.2080 0.0 0 0 0 0 0 0.978 0.0 0.0 0.0 0.0 0.0 0.0 46 | 6 10 1 1 1 0 0.0 0.5560 0.0 0 0 0 0 0 0.969 0.0 0.0 0.0 0.0 0.0 0.0 47 | 9 11 1 1 1 0 0.0 0.2080 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 48 | 9 10 1 1 1 0 0.0 0.1100 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 49 | 4 12 1 1 1 0 0.0 0.2560 0.0 0 0 0 0 0 0.932 0.0 0.0 0.0 0.0 0.0 0.0 50 | 12 13 1 1 1 0 0.0 0.1400 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 51 | 12 14 1 1 1 0 0.1231 0.2559 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 52 | 12 15 1 1 1 0 0.0662 0.1304 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 53 | 12 16 1 1 1 0 0.0945 0.1987 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 54 | 14 15 1 1 1 0 0.2210 0.1997 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 55 | 16 17 1 1 1 0 0.0524 0.1923 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 56 | 15 18 1 1 1 0 0.1073 0.2185 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 57 | 18 19 1 1 1 0 0.0639 0.1292 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 58 | 19 20 1 1 1 0 0.0340 0.0680 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 59 | 10 20 1 1 1 0 0.0936 0.2090 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 60 | 10 17 1 1 1 0 0.0324 0.0845 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 61 | 10 21 1 1 1 0 0.0348 0.0749 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 62 | 10 22 1 1 1 0 0.0727 0.1499 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 63 | 21 22 1 1 1 0 0.0116 0.0236 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 64 | 15 23 1 1 1 0 0.1000 0.2020 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 65 | 22 24 1 1 1 0 0.1150 0.1790 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 66 | 23 24 1 1 1 0 0.1320 0.2700 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 67 | 24 25 1 1 1 0 0.1885 0.3292 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 68 | 25 26 1 1 1 0 0.2544 0.3800 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 69 | 25 27 1 1 1 0 0.1093 0.2087 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 70 | 28 27 1 1 1 0 0.0 0.3960 0.0 0 0 0 0 0 0.968 0.0 0.0 0.0 0.0 0.0 0.0 71 | 27 29 1 1 1 0 0.2198 0.4153 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 72 | 27 30 1 1 1 0 0.3202 0.6027 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 73 | 29 30 1 1 1 0 0.2399 0.4533 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 74 | 8 28 1 1 1 0 0.0636 0.2000 0.0428 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 75 | 6 28 1 1 1 0 0.0169 0.0599 0.0130 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 76 | -999 77 | LOSS ZONES FOLLOWS 1 ITEMS 78 | 1 IEEE 30 BUS 79 | -99 80 | INTERCHANGE DATA FOLLOWS 1 ITEMS 81 | -9 82 | 1 2 Claytor 132 0.0 999.99 IEEE30 IEEE 30 Bus Test Case 83 | TIE LINES FOLLOWS 0 ITEMS 84 | -999 85 | END OF DATA 86 | -------------------------------------------------------------------------------- /topology/ieee57cdf.txt: -------------------------------------------------------------------------------- 1 | # 08/25/93 UW ARCHIVE 100.0 1961 W IEEE 57 Bus Test Case 2 | #BUS DATA FOLLOWS 57 ITEMS 3 | 1 Kanawha V1 1 1 3 1.040 0.0 55.0 17.0 128.9 -16.1 0.0 1.040 0.0 0.0 0.0 0.0 0 4 | 2 Turner V1 1 1 2 1.010 -1.18 3.0 88.0 0.0 -0.8 0.0 1.010 50.0 -17.0 0.0 0.0 0 5 | 3 Logan V1 1 1 2 0.985 -5.97 41.0 21.0 40.0 -1.0 0.0 0.985 60.0 -10.0 0.0 0.0 0 6 | 4 Sprigg V1 1 1 0 0.981 -7.32 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 7 | 5 Bus-5 V1 1 1 0 0.976 -8.52 13.0 4.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 8 | 6 Beaver-Ck V1 1 1 2 0.980 -8.65 75.0 2.0 0.0 0.8 0.0 0.980 25.0 -8.0 0.0 0.0 0 9 | 7 Bus-7 V1 1 1 0 0.984 -7.58 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 10 | 8 Clinch-Rv V1 1 1 2 1.005 -4.45 150.0 22.0 450.0 62.1 0.0 1.005 200.0 -140.0 0.0 0.0 0 11 | 9 Saltville V1 1 1 2 0.980 -9.56 121.0 26.0 0.0 2.2 0.0 0.980 9.0 -3.0 0.0 0.0 0 12 | 10 Bus-10 V1 1 1 0 0.986 -11.43 5.0 2.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 13 | 11 Tazewell V1 1 1 0 0.974 -10.17 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 14 | 12 Glen-Lyn V1 1 1 2 1.015 -10.46 377.0 24.0 310.0 128.5 0.0 1.015 155.0 -150.0 0.0 0.0 0 15 | 13 Bus-13 V1 1 1 0 0.979 -9.79 18.0 2.3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 16 | 14 Bus-14 V1 1 1 0 0.970 -9.33 10.5 5.3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 17 | 15 Bus-15 V1 1 1 0 0.988 -7.18 22.0 5.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 18 | 16 Bus-16 V1 1 1 0 1.013 -8.85 43.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 19 | 17 Bus-17 V1 1 1 0 1.017 -5.39 42.0 8.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 20 | 18 Sprigg V2 1 1 0 1.001 -11.71 27.2 9.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.10 0 21 | 19 Bus-19 V2 1 1 0 0.970 -13.20 3.3 0.6 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 22 | 20 Bus-20 V2 1 1 0 0.964 -13.41 2.3 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 23 | 21 Bus-21 V3 1 1 0 1.008 -12.89 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 24 | 22 Bus-22 V3 1 1 0 1.010 -12.84 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 25 | 23 Bus-23 V3 1 1 0 1.008 -12.91 6.3 2.1 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 26 | 24 Bus-24 V3 1 1 0 0.999 -13.25 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 27 | 25 Bus-25 V4 1 1 0 0.982 -18.13 6.3 3.2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.059 0 28 | 26 Bus-26 V5 1 1 0 0.959 -12.95 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 29 | 27 Bus-27 V5 1 1 0 0.982 -11.48 9.3 0.5 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 30 | 28 Bus-28 V5 1 1 0 0.997 -10.45 4.6 2.3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 31 | 29 Bus-29 V5 1 1 0 1.010 -9.75 17.0 2.6 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 32 | 30 Bus-30 V4 1 1 0 0.962 -18.68 3.6 1.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 33 | 31 Bus-31 V4 1 1 0 0.936 -19.34 5.8 2.9 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 34 | 32 Bus-32 V4 1 1 0 0.949 -18.46 1.6 0.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 35 | 33 Bus-33 V4 1 1 0 0.947 -18.50 3.8 1.9 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 36 | 34 Bus-34 V3 1 1 0 0.959 -14.10 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 37 | 35 Bus-35 V3 1 1 0 0.966 -13.86 6.0 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 38 | 36 Bus-36 V3 1 1 0 0.976 -13.59 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 39 | 37 Bus-37 V3 1 1 0 0.985 -13.41 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 40 | 38 Bus-38 V3 1 1 0 1.013 -12.71 14.0 7.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 41 | 39 Bus-39 V3 1 1 0 0.983 -13.46 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 42 | 40 Bus-40 V3 1 1 0 0.973 -13.62 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 43 | 41 Tazewell V6 1 1 0 0.996 -14.05 6.3 3.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 44 | 42 Bus-42 V6 1 1 0 0.966 -15.50 7.1 4.4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 45 | 43 Tazewell V7 1 1 0 1.010 -11.33 2.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 46 | 44 Bus-44 V3 1 1 0 1.017 -11.86 12.0 1.8 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 47 | 45 Bus-45 V3 1 1 0 1.036 -9.25 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 48 | 46 Bus-46 V3 1 1 0 1.050 -11.89 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 49 | 47 Bus-47 V3 1 1 0 1.033 -12.49 29.7 11.6 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 50 | 48 Bus-48 V3 1 1 0 1.027 -12.59 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 51 | 49 Bus-49 V3 1 1 0 1.036 -12.92 18.0 8.5 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 52 | 50 Bus-50 V3 1 1 0 1.023 -13.39 21.0 10.5 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 53 | 51 Bus-51 V3 1 1 0 1.052 -12.52 18.0 5.3 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 54 | 52 Bus-52 V5 1 1 0 0.980 -11.47 4.9 2.2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 55 | 53 Bus-53 V5 1 1 0 0.971 -12.23 20.0 10.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.063 0 56 | 54 Bus-54 V5 1 1 0 0.996 -11.69 4.1 1.4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 57 | 55 Saltville V5 1 1 0 1.031 -10.78 6.8 3.4 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 58 | 56 Bus-56 V6 1 1 0 0.968 -16.04 7.6 2.2 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 59 | 57 Bus-57 V6 1 1 0 0.965 -16.56 6.7 2.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0 60 | -999 61 | #BRANCH DATA FOLLOWS 80 ITEMS 62 | 1 2 1 1 1 0 0.0083 0.0280 0.1290 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 63 | 2 3 1 1 1 0 0.0298 0.0850 0.0818 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 64 | 3 4 1 1 1 0 0.0112 0.0366 0.0380 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 65 | 4 5 1 1 1 0 0.0625 0.1320 0.0258 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 66 | 4 6 1 1 1 0 0.0430 0.1480 0.0348 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 67 | 6 7 1 1 1 0 0.0200 0.1020 0.0276 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 68 | 6 8 1 1 1 0 0.0339 0.1730 0.0470 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 69 | 8 9 1 1 1 0 0.0099 0.0505 0.0548 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 70 | 9 10 1 1 1 0 0.0369 0.1679 0.0440 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 71 | 9 11 1 1 1 0 0.0258 0.0848 0.0218 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 72 | 9 12 1 1 1 0 0.0648 0.2950 0.0772 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 73 | 9 13 1 1 1 0 0.0481 0.1580 0.0406 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 74 | 13 14 1 1 1 0 0.0132 0.0434 0.0110 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 75 | 13 15 1 1 1 0 0.0269 0.0869 0.0230 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 76 | 1 15 1 1 1 0 0.0178 0.0910 0.0988 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 77 | 1 16 1 1 1 0 0.0454 0.2060 0.0546 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 78 | 1 17 1 1 1 0 0.0238 0.1080 0.0286 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 79 | 3 15 1 1 1 0 0.0162 0.0530 0.0544 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 80 | 4 18 1 1 1 0 0.0 0.5550 0.0 0 0 0 0 0 0.970 0.0 0.0 0.0 0.0 0.0 0.0 81 | 4 18 1 1 1 0 0.0 0.4300 0.0 0 0 0 0 0 0.978 0.0 0.0 0.0 0.0 0.0 0.0 82 | 5 6 1 1 1 0 0.0302 0.0641 0.0124 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 83 | 7 8 1 1 1 0 0.0139 0.0712 0.0194 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 84 | 10 12 1 1 1 0 0.0277 0.1262 0.0328 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 85 | 11 13 1 1 1 0 0.0223 0.0732 0.0188 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 86 | 12 13 1 1 1 0 0.0178 0.0580 0.0604 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 87 | 12 16 1 1 1 0 0.0180 0.0813 0.0216 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 88 | 12 17 1 1 1 0 0.0397 0.1790 0.0476 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 89 | 14 15 1 1 1 0 0.0171 0.0547 0.0148 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 90 | 18 19 1 1 1 0 0.4610 0.6850 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 91 | 19 20 1 1 1 0 0.2830 0.4340 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 92 | 21 20 1 1 1 0 0.0 0.7767 0.0 0 0 0 0 0 1.043 0.0 0.0 0.0 0.0 0.0 0.0 93 | 21 22 1 1 1 0 0.0736 0.1170 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 94 | 22 23 1 1 1 0 0.0099 0.0152 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 95 | 23 24 1 1 1 0 0.1660 0.2560 0.0084 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 96 | 24 25 1 1 1 0 0.0 1.1820 0.0 0 0 0 0 0 1.000 0.0 0.0 0.0 0.0 0.0 0.0 97 | 24 25 1 1 1 0 0.0 1.2300 0.0 0 0 0 0 0 1.000 0.0 0.0 0.0 0.0 0.0 0.0 98 | 24 26 1 1 1 0 0.0 0.0473 0.0 0 0 0 0 0 1.043 0.0 0.0 0.0 0.0 0.0 0.0 99 | 26 27 1 1 1 0 0.1650 0.2540 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 100 | 27 28 1 1 1 0 0.0618 0.0954 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 101 | 28 29 1 1 1 0 0.0418 0.0587 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 102 | 7 29 1 1 1 0 0.0 0.0648 0.0 0 0 0 0 0 0.967 0.0 0.0 0.0 0.0 0.0 0.0 103 | 25 30 1 1 1 0 0.1350 0.2020 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 104 | 30 31 1 1 1 0 0.3260 0.4970 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 105 | 31 32 1 1 1 0 0.5070 0.7550 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 106 | 32 33 1 1 1 0 0.0392 0.0360 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 107 | 34 32 1 1 1 0 0.0 0.9530 0.0 0 0 0 0 0 0.975 0.0 0.0 0.0 0.0 0.0 0.0 108 | 34 35 1 1 1 0 0.0520 0.0780 0.0032 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 109 | 35 36 1 1 1 0 0.0430 0.0537 0.0016 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 110 | 36 37 1 1 1 0 0.0290 0.0366 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 111 | 37 38 1 1 1 0 0.0651 0.1009 0.0020 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 112 | 37 39 1 1 1 0 0.0239 0.0379 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 113 | 36 40 1 1 1 0 0.0300 0.0466 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 114 | 22 38 1 1 1 0 0.0192 0.0295 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 115 | 11 41 1 1 1 0 0.0 0.7490 0.0 0 0 0 0 0 0.955 0.0 0.0 0.0 0.0 0.0 0.0 116 | 41 42 1 1 1 0 0.2070 0.3520 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 117 | 41 43 1 1 1 0 0.0 0.4120 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 118 | 38 44 1 1 1 0 0.0289 0.0585 0.0020 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 119 | 15 45 1 1 1 0 0.0 0.1042 0.0 0 0 0 0 0 0.955 0.0 0.0 0.0 0.0 0.0 0.0 120 | 14 46 1 1 1 0 0.0 0.0735 0.0 0 0 0 0 0 0.900 0.0 0.0 0.0 0.0 0.0 0.0 121 | 46 47 1 1 1 0 0.0230 0.0680 0.0032 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 122 | 47 48 1 1 1 0 0.0182 0.0233 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 123 | 48 49 1 1 1 0 0.0834 0.1290 0.0048 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 124 | 49 50 1 1 1 0 0.0801 0.1280 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 125 | 50 51 1 1 1 0 0.1386 0.2200 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 126 | 10 51 1 1 1 0 0.0 0.0712 0.0 0 0 0 0 0 0.930 0.0 0.0 0.0 0.0 0.0 0.0 127 | 13 49 1 1 1 0 0.0 0.1910 0.0 0 0 0 0 0 0.895 0.0 0.0 0.0 0.0 0.0 0.0 128 | 29 52 1 1 1 0 0.1442 0.1870 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 129 | 52 53 1 1 1 0 0.0762 0.0984 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 130 | 53 54 1 1 1 0 0.1878 0.2320 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 131 | 54 55 1 1 1 0 0.1732 0.2265 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 132 | 11 43 1 1 1 0 0.0 0.1530 0.0 0 0 0 0 0 0.958 0.0 0.0 0.0 0.0 0.0 0.0 133 | 44 45 1 1 1 0 0.0624 0.1242 0.0040 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 134 | 40 56 1 1 1 0 0.0 1.1950 0.0 0 0 0 0 0 0.958 0.0 0.0 0.0 0.0 0.0 0.0 135 | 56 41 1 1 1 0 0.5530 0.5490 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 136 | 56 42 1 1 1 0 0.2125 0.3540 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 137 | 39 57 1 1 1 0 0.0 1.3550 0.0 0 0 0 0 0 0.980 0.0 0.0 0.0 0.0 0.0 0.0 138 | 57 56 1 1 1 0 0.1740 0.2600 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 139 | 38 49 1 1 1 0 0.1150 0.1770 0.0030 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 140 | 38 48 1 1 1 0 0.0312 0.0482 0.0 0 0 0 0 0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 141 | 9 55 1 1 1 0 0.0 0.1205 0.0 0 0 0 0 0 0.940 0.0 0.0 0.0 0.0 0.0 0.0 142 | -999 143 | LOSS ZONES FOLLOWS 1 ITEMS 144 | 1 IEEE 57 BUS 145 | -99 146 | INTERCHANGE DATA FOLLOWS 1 ITEMS 147 | -9 148 | 1 8 Clinch Rv V1 0.0 999.99 IEEE57 IEEE 57 Bus Test Case 149 | TIE LINES FOLLOWS 0 ITEMS 150 | -999 151 | END OF DATA 152 | --------------------------------------------------------------------------------