# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0
from typing import Tuple
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from VeraGridEngine.Devices.Substation.bus import Bus
from VeraGridEngine.enumerations import BuildStatus, PrpCat
from VeraGridEngine.Devices.Parents.branch_parent import BranchParent
from VeraGridEngine.Devices.Parents.editable_device import DeviceType, GCProp
[docs]
class UPFC(BranchParent):
__slots__ = (
'_R',
'_X',
'_Rsh',
'_Xsh',
'_R0',
'_X0',
'_Rsh0',
'_Xsh0',
'_R2',
'_X2',
'_Rsh2',
'_Xsh2',
'_Vsh',
'_Pfset',
'_Qfset',
)
LOCAL_PROPERTY_DECLARATIONS: Tuple[GCProp, ...] = (
GCProp(
prop_name='R',
units='p.u.',
tpe=float,
definition='Series positive sequence resistance.',
old_names=['Rs'],
cat=[PrpCat.PF],
),
GCProp(
prop_name='X',
units='p.u.',
tpe=float,
definition='Series positive sequence reactance.',
old_names=['Xs'],
cat=[PrpCat.PF],
),
GCProp(
prop_name='Rsh',
units='p.u.',
tpe=float,
definition='Shunt positive sequence resistance.',
cat=[PrpCat.PF],
),
GCProp(
prop_name='Xsh',
units='p.u.',
tpe=float,
definition='Shunt positive sequence resistance.',
cat=[PrpCat.PF],
),
GCProp(
prop_name='R0',
units='p.u.',
tpe=float,
definition='Series zero sequence resistance.',
old_names=['Rs0'],
cat=[PrpCat.SC, PrpCat.PF3],
),
GCProp(
prop_name='X0',
units='p.u.',
tpe=float,
definition='Series zero sequence reactance.',
old_names=['Xs0'],
cat=[PrpCat.SC, PrpCat.PF3],
),
GCProp(
prop_name='Rsh0',
units='p.u.',
tpe=float,
definition='Shunt zero sequence resistance.',
cat=[PrpCat.SC, PrpCat.PF3],
),
GCProp(
prop_name='Xsh0',
units='p.u.',
tpe=float,
definition='Shunt zero sequence resistance.',
cat=[PrpCat.SC, PrpCat.PF3],
),
GCProp(
prop_name='R2',
units='p.u.',
tpe=float,
definition='Series negative sequence resistance.',
old_names=['Rs2'],
cat=[PrpCat.SC, PrpCat.PF3],
),
GCProp(
prop_name='X2',
units='p.u.',
tpe=float,
definition='Series negative sequence reactance.',
old_names=['Xs2'],
cat=[PrpCat.SC, PrpCat.PF3],
),
GCProp(
prop_name='Rsh2',
units='p.u.',
tpe=float,
definition='Shunt negative sequence resistance.',
cat=[PrpCat.SC, PrpCat.PF3],
),
GCProp(
prop_name='Xsh2',
units='p.u.',
tpe=float,
definition='Shunt negative sequence resistance.',
cat=[PrpCat.SC, PrpCat.PF3],
),
GCProp(
prop_name='Vsh',
units='p.u.',
tpe=float,
definition='Shunt voltage set point.',
cat=[PrpCat.PF],
),
GCProp(
prop_name='Pfset',
units='MW',
tpe=float,
definition='Active power set point.',
cat=[PrpCat.PF],
),
GCProp(
prop_name='Qfset',
units='MVAr',
tpe=float,
definition='Active power set point.',
cat=[PrpCat.PF],
),
)
def __init__(self,
bus_from: Bus = None,
bus_to: Bus = None,
name='UPFC',
code='',
idtag=None,
active=True,
rs=0.0, xs=0.00001,
rp=0.0, xp=0.0,
vp=1.0,
Pset=0.0, Qset=0.0,
design_rate: float = 9999,
rate=9999,
mttf=0, mttr=0, cost=100,
contingency_factor=1.0,
protection_rating_factor: float = 1.4,
contingency_enabled=True,
monitor_loading=True,
rs0=0.0, xs0=0.00001,
rp0=0.0, xp0=0.0,
rs2=0.0, xs2=0.00001,
rp2=0.0, xp2=0.0,
capex=0, opex=0,
build_status: BuildStatus = BuildStatus.Commissioned):
"""
Unified Power Flow Converter (UPFC)
:param bus_from:
:param bus_to:
:param name:
:param code:
:param idtag:
:param active:
:param rs: series resistance (p.u.)
:param xs: series reactance (p.u.)
:param rp: shunt resistance (p.u.)
:param xp: shunt reactance (p.u.)
:param vp: shunt voltage set point (p.u.)
:param Pset: Power set point (MW)
:param Qset:
:param design_rate: Design rate (MVA)
:param rate: Power rating (MVA)
:param mttf:
:param mttr:
:param cost:
:param contingency_factor:
:param contingency_enabled:
:param monitor_loading:
:param rs0:
:param xs0:
:param rp0:
:param xp0:
:param rs2:
:param xs2:
:param rp2:
:param xp2:
:param capex:
:param opex:
:param build_status:
"""
BranchParent.__init__(self,
name=name,
idtag=idtag,
code=code,
bus_from=bus_from,
bus_to=bus_to,
active=active,
reducible=False,
design_rate=design_rate,
rate=rate,
contingency_factor=contingency_factor,
protection_rating_factor=protection_rating_factor,
contingency_enabled=contingency_enabled,
monitor_loading=monitor_loading,
mttf=mttf,
mttr=mttr,
build_status=build_status,
capex=capex,
opex=opex,
cost=cost,
temp_base=25,
temp_oper=25,
alpha=0.0033,
device_type=DeviceType.UpfcDevice)
# total impedance and admittance in p.u.
self.R = float(rs)
self.X = float(xs)
self.Rsh = float(rp)
self.Xsh = float(xp)
self.R0 = float(rs0)
self.X0 = float(xs0)
self.Rsh0 = float(rp0)
self.Xsh0 = float(xp0)
self.R2 = float(rs2)
self.X2 = float(xs2)
self.Rsh2 = float(rp2)
self.Xsh2 = float(xp2)
self.Vsh = float(vp)
self.Pfset = float(Pset)
self.Qfset = float(Qset)
[docs]
def get_ysh1(self):
"""
:return:
"""
return 1.0 / complex(self.Rsh + 1e-20, self.Xsh)
[docs]
def get_ysh0(self):
"""
:return:
"""
return 1.0 / complex(self.Rsh0 + 1e-20, self.Xsh0)
[docs]
def get_ysh2(self):
"""
:return:
"""
return 1.0 / complex(self.Rsh2 + 1e-20, self.Xsh2)
[docs]
def get_max_bus_nominal_voltage(self):
return max(self.bus_from.Vnom, self.bus_to.Vnom)
[docs]
def get_min_bus_nominal_voltage(self):
return min(self.bus_from.Vnom, self.bus_to.Vnom)
[docs]
def change_base(self, Sbase_old: float, Sbase_new: float):
"""
:param Sbase_old:
:param Sbase_new:
:return:
"""
b = Sbase_new / Sbase_old
self.R *= b
self.X *= b
self.Rsh *= b
self.X *= b
[docs]
def plot_profiles(self, time_series=None, my_index=0, show_fig=True):
"""
Plot the time series results of this object
:param time_series: TimeSeries Instance
:param my_index: index of this object in the simulation
:param show_fig: Show the figure?
"""
if time_series is not None:
fig = plt.figure(figsize=(12, 8))
ax_1 = fig.add_subplot(211)
ax_2 = fig.add_subplot(212, sharex=ax_1)
x = time_series.results.time_array
# loading
y = time_series.results.loading.real * 100.0
df = pd.DataFrame(data=y[:, my_index], index=x, columns=[self.name])
ax_1.set_title('Loading', fontsize=14)
ax_1.set_ylabel('Loading [%]', fontsize=11)
df.plot(ax=ax_1)
# losses
y = np.abs(time_series.results.losses)
df = pd.DataFrame(data=y[:, my_index], index=x, columns=[self.name])
ax_2.set_title('Losses', fontsize=14)
ax_2.set_ylabel('Losses [MVA]', fontsize=11)
df.plot(ax=ax_2)
plt.legend()
fig.suptitle(self.name, fontsize=20)
if show_fig:
plt.show()
# Scalar property accessors coerce assignments to the declared schema types.
@property
def R(self) -> float:
"""
Get ``R``.
:return: float
"""
return self._R
@R.setter
def R(self, val: float) -> None:
"""
Set ``R``.
:param val: Value to assign.
:return: None
"""
self._R = float(val)
@property
def X(self) -> float:
"""
Get ``X``.
:return: float
"""
return self._X
@X.setter
def X(self, val: float) -> None:
"""
Set ``X``.
:param val: Value to assign.
:return: None
"""
self._X = float(val)
@property
def Rsh(self) -> float:
"""
Get ``Rsh``.
:return: float
"""
return self._Rsh
@Rsh.setter
def Rsh(self, val: float) -> None:
"""
Set ``Rsh``.
:param val: Value to assign.
:return: None
"""
self._Rsh = float(val)
@property
def Xsh(self) -> float:
"""
Get ``Xsh``.
:return: float
"""
return self._Xsh
@Xsh.setter
def Xsh(self, val: float) -> None:
"""
Set ``Xsh``.
:param val: Value to assign.
:return: None
"""
self._Xsh = float(val)
@property
def R0(self) -> float:
"""
Get ``R0``.
:return: float
"""
return self._R0
@R0.setter
def R0(self, val: float) -> None:
"""
Set ``R0``.
:param val: Value to assign.
:return: None
"""
self._R0 = float(val)
@property
def X0(self) -> float:
"""
Get ``X0``.
:return: float
"""
return self._X0
@X0.setter
def X0(self, val: float) -> None:
"""
Set ``X0``.
:param val: Value to assign.
:return: None
"""
self._X0 = float(val)
@property
def Rsh0(self) -> float:
"""
Get ``Rsh0``.
:return: float
"""
return self._Rsh0
@Rsh0.setter
def Rsh0(self, val: float) -> None:
"""
Set ``Rsh0``.
:param val: Value to assign.
:return: None
"""
self._Rsh0 = float(val)
@property
def Xsh0(self) -> float:
"""
Get ``Xsh0``.
:return: float
"""
return self._Xsh0
@Xsh0.setter
def Xsh0(self, val: float) -> None:
"""
Set ``Xsh0``.
:param val: Value to assign.
:return: None
"""
self._Xsh0 = float(val)
@property
def R2(self) -> float:
"""
Get ``R2``.
:return: float
"""
return self._R2
@R2.setter
def R2(self, val: float) -> None:
"""
Set ``R2``.
:param val: Value to assign.
:return: None
"""
self._R2 = float(val)
@property
def X2(self) -> float:
"""
Get ``X2``.
:return: float
"""
return self._X2
@X2.setter
def X2(self, val: float) -> None:
"""
Set ``X2``.
:param val: Value to assign.
:return: None
"""
self._X2 = float(val)
@property
def Rsh2(self) -> float:
"""
Get ``Rsh2``.
:return: float
"""
return self._Rsh2
@Rsh2.setter
def Rsh2(self, val: float) -> None:
"""
Set ``Rsh2``.
:param val: Value to assign.
:return: None
"""
self._Rsh2 = float(val)
@property
def Xsh2(self) -> float:
"""
Get ``Xsh2``.
:return: float
"""
return self._Xsh2
@Xsh2.setter
def Xsh2(self, val: float) -> None:
"""
Set ``Xsh2``.
:param val: Value to assign.
:return: None
"""
self._Xsh2 = float(val)
@property
def Vsh(self) -> float:
"""
Get ``Vsh``.
:return: float
"""
return self._Vsh
@Vsh.setter
def Vsh(self, val: float) -> None:
"""
Set ``Vsh``.
:param val: Value to assign.
:return: None
"""
self._Vsh = float(val)
@property
def Pfset(self) -> float:
"""
Get ``Pfset``.
:return: float
"""
return self._Pfset
@Pfset.setter
def Pfset(self, val: float) -> None:
"""
Set ``Pfset``.
:param val: Value to assign.
:return: None
"""
self._Pfset = float(val)
@property
def Qfset(self) -> float:
"""
Get ``Qfset``.
:return: float
"""
return self._Qfset
@Qfset.setter
def Qfset(self, val: float) -> None:
"""
Set ``Qfset``.
:param val: Value to assign.
:return: None
"""
self._Qfset = float(val)