Commit d01c0b9b authored by martin.moraga's avatar martin.moraga
Browse files

improve nv_state_estimator_cim.py

parent 347be7ee
...@@ -2,17 +2,22 @@ from enum import Enum ...@@ -2,17 +2,22 @@ from enum import Enum
import numpy as np import numpy as np
class ElemType(Enum): class ElemType(Enum):
Node = 1 #Node Voltage Node = 1 #Node Voltage
Branch = 2 #Complex Power Injection at node Branch = 2 #Complex Power flow at branch
class MeasType(Enum): class MeasType(Enum):
V = 1 #Node Voltage V_mag = 1 #Node Voltage
Sinj= 2 #Complex Power Injection at node Sinj_real= 2 #Complex Power Injection at node
S1 = 4 #Complex Power flow at branch, measured at initial node Sinj_imag= 3 #Complex Power Injection at node
S2 = 5 #Complex Power flow at branch, measured at final node S1_real = 4 #Active Power flow at branch, measured at initial node (S1.real)
I = 6 #Branch Current S1_imag = 5 #Reactive Power flow at branch, measured at initial node (S1.imag)
Vpmu = 7 #Node Voltage I_mag = 6 #Branch Current
Ipmu = 9 #Branch Current Vpmu_mag = 7 #Node Voltage
Vpmu_phase = 8 #Node Voltage
Ipmu_mag = 9 #Branch Current
Ipmu_phase = 10 #Branch Current
S2_real = 11 #Active Power flow at branch, measured at final node (S2.real)
S2_imag = 12 #Reactive Power flow at branch, measured at final node (S2.imag)
class Measurement(): class Measurement():
def __init__(self, element, element_type, meas_type, meas_value, std_dev): def __init__(self, element, element_type, meas_type, meas_value, std_dev):
...@@ -35,16 +40,18 @@ class Measurement(): ...@@ -35,16 +40,18 @@ class Measurement():
self.element_type = element_type self.element_type = element_type
self.meas_type = meas_type self.meas_type = meas_type
self.meas_value = meas_value self.meas_value = meas_value
self.std_dev = self.meas_value*(std_dev/300) self.std_dev = std_dev
if self.std_dev<10**(-6) self.std_dev = std_dev
self.std_dev = 10**(-6) self.mval = 0.0 #measured values (affected by uncertainty)
self.mval = 0.0 #measured values (affected by uncertainty)
class Measurents_set(): class Measurents_set():
def __init__(self): def __init__(self):
self.measurements = [] self.measurements = [] #array with all measurements
def create_measurement(self, element, element_type, meas_type, meas_value, std_dev): def create_measurement(self, element, element_type, meas_type, meas_value, std_dev):
"""
to add elements to the measurements array
"""
self.measurements.append(Measurement(element, element_type, meas_type, meas_value, std_dev)) self.measurements.append(Measurement(element, element_type, meas_type, meas_value, std_dev))
def meas_creation(self): def meas_creation(self):
...@@ -54,79 +61,114 @@ class Measurents_set(): ...@@ -54,79 +61,114 @@ class Measurents_set():
err_pu = np.random.normal(0,1,len(self.measurements)) err_pu = np.random.normal(0,1,len(self.measurements))
for index, measurement in enumerate(self.measurements): for index, measurement in enumerate(self.measurements):
measurement.mval = measurement.meas_value + self.std_dev*err_pu[index] measurement.mval = measurement.meas_value + self.std_dev*err_pu[index]
def getNumberOfMeasurements(self) def meas_creation_test(self, err_pu):
""" """
return number of measurements of each type in the array Measurents_set.measurements For test purposes.
""" It calculates the measured values (affected by uncertainty) at the measurement points.
nvi, npi, nqi, npf, nqf, nii, nvpum, nipmu = 0 This function takes as paramenter the random gaussian distribution.
for elem in self.measurements: """
if elem.meas_type is MeasType.V: for index, measurement in enumerate(self.measurements):
nvi = nvi+1 measurement.mval = measurement.meas_value + measurement.std_dev*err_pu[index]
elif elem.meas_type is MeasType.Sinj:
npi = npi+1 def getMeasurements(self, type):
nqi = nqi+1
elif elem.meas_type is MeasType.S1 or elem.meas_type is MeasType.S2:
npf = npf+1
nqf = nqf+1
elif elem.meas_type is MeasType.I:
nii = nii+1
elif elem.meas_type is MeasType.Vpmu:
nvpum = nvpum+1
elif elem.meas_type is MeasType.Ipmu:
nipmu = nipmu+1
return nvi, npi, nqi, npf, nqf, nii, nvpum, nipmu
def getMeasuredActiveInjPowers(self):
""" """
return an array with the measurements of type Sinj.real return an array with all measurements of type "type" in the array Measurents_set.measurements.
""" """
Pinj = np.array([]) measurements = []
for elem in self.measurements: for measurement in self.measurements:
if elem.meas_type is MeasType.Sinj: if measurement.meas_type is type:
Pinj = np.append(Pinj, elem.real) measurements.append(measurement)
return Pinj return measurements
def getNumberOfMeasurements(self, type):
"""
return number of measurements of type "type" in the array Measurents_set.measurements
"""
number = 0
for measurement in self.measurements:
if measurement.meas_type is type:
number = number+1
return number
def getMeasuredReactiveInjPowers(self): def getIndexOfMeasurements(self, type):
""" """
return an array with the measurements of type Sinj.imag return index of all measurements of type "type" in the array Measurents_set.measurements
""" """
Qinj = np.array([]) idx = np.zeros(self.getNumberOfMeasurements(type), dtype=int)
for elem in self.measurements: i = 0
if elem.meas_type is MeasType.Sinj: for index, measurement in enumerate(self.measurements):
Qinj = np.append(Qinj, elem.imag) if measurement.meas_type is type:
idx[i] = index
i = i+1
return idx
return Qinj def getWeightsMatrix(self):
"""
return an array the weights (obtained as standard_deviations^-2)
"""
weights = np.zeros(len(self.measurements))
for index, measurement in enumerate(self.measurements):
#the weight is small and can bring instability during matrix inversion, so we "cut" everything below 10^-6
if measurement.std_dev<10**(-6):
measurement.std_dev = 10**(-6)
weights[index] = measurement.std_dev**(-2)
def getMeasuredActiveBPowers(self): return weights
def getMeasValues(self):
""" """
return an array with the measurements of type S1.real or S2.real for test purposes
returns an array with all measured values
""" """
Pbr = np.array([]) meas_val = np.zeros(len(self.measurements))
for elem in self.measurements: for index, measurement in enumerate(self.measurements):
if elem.meas_type is MeasType.S1 or elem.meas_type is MeasType.S2: meas_val[index] = measurement.meas_value
Pbr = np.append(Pbr, elem.real)
return Pbr return meas_val
def getMeasuredReactiveBPowers(self): def getmVal(self):
""" """
return an array with the measurements of type S1.imag or S2.imag returns an array with all measured values (affected by uncertainty)
""" """
Qbr = np.array([]) mVal = np.zeros(len(self.measurements))
for elem in self.measurements: for index, measurement in enumerate(self.measurements):
if elem.meas_type is MeasType.S1 or elem.meas_type is MeasType.S2: mVal[index] = measurement.mval
Qbr = np.append(Qbr, elem.imag)
return Qbr """ Replace in mVal amplitude and phase of Vpmu by real and imaginary part """
#get all measurements of type MeasType.Vpmu_mag
Vpmu_mag_idx = self.getIndexOfMeasurements(type=MeasType.Vpmu_mag)
#get all measurements of type MeasType.Vpmu_phase
Vpmu_phase_idx = self.getIndexOfMeasurements(type=MeasType.Vpmu_phase)
for vpmu_mag_index, vpmu_phase_index in zip(Vpmu_mag_idx, Vpmu_phase_idx):
vamp = self.measurements[vpmu_mag_index].mval
vtheta = self.measurements[vpmu_phase_index].mval
mVal[vpmu_mag_index] = vamp*np.cos(vtheta)
mVal[vpmu_phase_index] = vamp*np.sin(vtheta)
def getWeightsMatrix(self) """ Replace in z amplitude and phase of Ipmu by real and imaginary part """
#get all measurements of type MeasType.Ipmu_mag
Ipmu_mag_idx = self.getIndexOfMeasurements(type=MeasType.Ipmu_mag)
#get all measurements of type MeasType.Ipmu_phase
Ipmu_phase_idx = self.getIndexOfMeasurements(type=MeasType.Ipmu_phase)
for ipmu_mag_index, ipmu_phase_index in zip(Ipmu_mag_idx, Ipmu_phase_idx):
iamp = self.measurements[ipmu_mag_index].mval
itheta = self.measurements[ipmu_phase_index].mval
mVal[ipmu_mag_index] = iamp*np.cos(itheta)
mVal[ipmu_phase_index] = iamp*np.sin(itheta)
return mVal
def getStd_Dev(self):
""" """
creates the weights matrix (obtained as standard_deviations^-2) for test purposes
returns an array with all standard deviations
""" """
weights = np.zeros(len(self.measurements)) std_dev = np.zeros(len(self.measurements))
for index, measurement in enumerate(self.measurements): for index, measurement in enumerate(self.measurements):
weights[index] = measurement.std_dev**(-2) std_dev[index] = measurement.std_dev
return np.diag(weights) return std_dev
\ No newline at end of file
\ No newline at end of file
...@@ -167,7 +167,7 @@ class PowerflowResults(): ...@@ -167,7 +167,7 @@ class PowerflowResults():
for branch in self.branches: for branch in self.branches:
S2 = np.append(S2, branch.power2) S2 = np.append(S2, branch.power2)
return S2 return S2
def solve(system): def solve(system):
"""It performs Power Flow by using rectangular node voltage state variables.""" """It performs Power Flow by using rectangular node voltage state variables."""
...@@ -254,59 +254,4 @@ def solve(system): ...@@ -254,59 +254,4 @@ def solve(system):
results.calculateS1() results.calculateS1()
results.calculateS2() results.calculateS2()
return results, num_iter return results, num_iter
\ No newline at end of file
def calculateI(system, V):
"""
To calculate the branch currents
"""
Ymatrix, Adj = Ymatrix_calc(system)
I = np.zeros((len(system.branches)), dtype=np.complex)
for idx in range(len(system.branches)):
fr = system.branches[idx].start_node.index
to = system.branches[idx].end_node.index
I[idx] = - (V[fr] - V[to])*Ymatrix[fr][to]
return I
def calculateInj(system, I):
"""
To calculate current injections at a node
"""
Iinj = np.zeros((len(system.nodes)), dtype=np.complex)
for k in range(0, (len(system.nodes))):
to=[]
fr=[]
for m in range(len(system.branches)):
if k==system.branches[m].start_node.index:
fr.append(m)
if k==system.branches[m].end_node.index:
to.append(m)
Iinj[k] = np.sum(I[to]) - np.sum(I[fr])
return Iinj
def calculateS1(system, V, I):
"""
To calculate powerflow on branches
"""
S1 = np.zeros((len(system.branches)), dtype=np.complex)
for i in range(0, len(system.branches)):
S1[i] = V[system.branches[i].start_node.index]*(np.conj(I[i]))
return S1
def calculateS2(system, V, I):
"""
To calculate powerflow on branches
"""
S2 = np.zeros((len(system.branches)), dtype=np.complex)
for i in range(0, len(system.branches)):
S2[i] = -V[system.branches[i].end_node.index]*(np.conj(I[i]))
return S2
def calculateSinj(V, Iinj):
"""
To calculate power injection at a node
"""
Sinj = np.multiply(V, np.conj(Iinj))
return Sinj
\ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment