# 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 __future__ import annotations
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from typing import List, Tuple, TYPE_CHECKING
from VeraGridEngine.Devices.Profiles import ProfileDevice, ProfileEnum, ProfileFloat
from VeraGridEngine.Devices.Substation.bus import Bus
from VeraGridEngine.enumerations import BuildStatus, ConverterControlType, ConverterFaultControlType, PrpCat
from VeraGridEngine.Devices.Parents.branch_parent import BranchParent
from VeraGridEngine.Devices.Parents.editable_device import DeviceType, GCProp
from VeraGridEngine.Devices.Parents.editable_device import get_at
if TYPE_CHECKING:
from VeraGridEngine.Devices.types import BRANCH_TYPES
[docs]
class VSC(BranchParent):
__slots__ = (
'_u_setpoint_min',
'_u_setpoint_max',
'_u_setpoint',
'_du_setpoint',
'_Q_min',
'_Q_max',
'_alpha1',
'_alpha2',
'_alpha3',
'_control1',
'_control1_prof',
'_control1_dev',
'_control1_dev_prof',
'_control1_val',
'_control1_val_prof',
'_control1_val_min',
'_control1_val_max',
'_control1_val_droop',
'_control1_val_droop_prof',
'_control1_droop_val',
'_control1_droop_val_prof',
'_control1_droop_val_min',
'_control1_droop_val_max',
'_control2',
'_control2_prof',
'_control2_dev',
'_control2_dev_prof',
'_control2_val',
'_control2_val_prof',
'_control2_val_min',
'_control2_val_max',
'_control2_val_droop',
'_control2_val_droop_prof',
'_control2_droop_val',
'_control2_droop_val_prof',
'_control2_droop_val_min',
'_control2_droop_val_max',
'_fault_control',
'_fault_control_prof',
'_bus_dc_n',
'_min_ac_voltage',
'_ysvs',
'_x',
'_y',
)
LOCAL_PROPERTY_DECLARATIONS: Tuple[GCProp, ...] = (
GCProp(
prop_name='bus_dc_n',
units="",
tpe=DeviceType.BusDevice,
definition='DC negative bus',
editable=False,
cat=[PrpCat.PF],
),
GCProp(
prop_name='alpha1',
units='',
tpe=float,
definition='Losses constant parameter (IEC 62751-2 loss Correction, idle loss).',
),
GCProp(
prop_name='alpha2',
units='',
tpe=float,
definition='Losses linear parameter (IEC 62751-2 loss Correction, Switching loss).',
),
GCProp(
prop_name='alpha3',
units='',
tpe=float,
definition='Losses quadratic parameter (IEC 62751-2 loss Correction, resistive loss).',
),
GCProp(
prop_name='control1',
units='',
tpe=ConverterControlType,
profile_name="control1_prof",
definition='Control mode 1.',
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control1_dev',
units="",
tpe=DeviceType.BusOrBranch,
profile_name="control1_dev_prof",
definition='Controlled device, None to apply to this converter',
editable=False,
cat=[PrpCat.PF],
),
GCProp(
prop_name='control1_val',
units='',
tpe=float,
profile_name="control1_val_prof",
definition='Control value 1.'
'p.u. for voltage\n'
'rad for angles\n'
'MW for P\n'
'MVAr for Q',
cat=[PrpCat.PF],
),
GCProp(
prop_name='control1_val_min',
units='',
tpe=float,
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control1_val_max',
units='',
tpe=float,
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control1_val_droop',
units='',
tpe=float,
profile_name="control1_val_droop_prof",
old_names=['control1_droop'],
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control1_droop_val',
units='',
tpe=float,
profile_name="control1_droop_val_prof",
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control1_droop_val_min',
units='',
tpe=float,
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control1_droop_val_max',
units='',
tpe=float,
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control2',
units='',
tpe=ConverterControlType,
profile_name="control2_prof",
definition='Control mode 2.',
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control2_dev',
units="",
tpe=DeviceType.BusOrBranch,
profile_name="control2_dev_prof",
definition='Controlled device, None to apply to this converter',
editable=False,
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control2_val',
units='',
tpe=float,
profile_name="control2_val_prof",
definition='Control value 2.'
'p.u. for voltage\n'
'rad for angles\n'
'MW for P\n'
'MVAr for Q',
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control2_val_min',
units='',
tpe=float,
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control2_val_max',
units='',
tpe=float,
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control2_val_droop',
units='',
tpe=float,
profile_name="control2_val_droop_prof",
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control2_droop_val',
units='',
tpe=float,
profile_name="control2_droop_val_prof",
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control2_droop_val_min',
units='',
tpe=float,
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='control2_droop_val_max',
units='',
tpe=float,
cat=[PrpCat.PF, PrpCat.OPF],
),
GCProp(
prop_name='fault_control',
units='',
tpe=ConverterFaultControlType,
profile_name="fault_control_prof",
definition='VSC control system during a short-circuit event.',
cat=[PrpCat.PF],
),
GCProp(
prop_name='min_ac_voltage',
units='p.u.',
tpe=float,
definition='Minimum AC voltage threshold. '
'If the AC bus voltage drops below this value, the VSC is disconnected.',
cat=[PrpCat.PF],
),
GCProp(
prop_name='ysvs',
units='p.u.',
tpe=float,
definition='Admittance of non-controlled Static Var Systems.',
cat=[PrpCat.PF],
),
GCProp(
prop_name='x',
units='px',
tpe=float,
definition='x position',
cat=[PrpCat.TP],
),
GCProp(
prop_name='y',
units='px',
tpe=float,
definition='y position',
cat=[PrpCat.TP],
),
)
def __init__(self,
bus_from: Bus | None = None,
bus_to: Bus | None = None,
bus_dc_n: Bus | None = None,
name='VSC',
idtag: str | None = None,
code='',
active=True,
design_rate: float = 9999.0,
rate: float = 9999.0,
alpha1=0.0001,
alpha2=0.015,
alpha3=0.2,
mttf=0.0,
mttr=0.0,
cost=100,
contingency_factor=1.0,
protection_rating_factor: float = 1.4,
contingency_enabled=True,
monitor_loading=True,
capex=0.0,
opex=0.0,
build_status: BuildStatus = BuildStatus.Commissioned,
control1: ConverterControlType = ConverterControlType.Q_droop,
control1_dev: Bus | BRANCH_TYPES | None = None,
control1_val: float = 1.0,
control1_val_min: float = -9999.0,
control1_val_max: float = 9999.0,
control1_droop: float = 0.1,
control1_droop_val: float = 1.0,
control1_droop_val_min: float = 0.9,
control1_droop_val_max: float = 1.1,
control2: ConverterControlType = ConverterControlType.Vm_dc,
control2_dev: Bus | BRANCH_TYPES | None = None,
control2_val: float = 0.0,
control2_val_min: float = -9999.0,
control2_val_max: float = 9999.0,
control2_droop: float = 0.1,
control2_droop_val: float = 1.0,
control2_droop_val_min: float = 0.9,
control2_droop_val_max: float = 1.1,
fault_control: ConverterFaultControlType = ConverterFaultControlType.Standard,
min_ac_voltage: float = 0.1,
ysvs: float = 0.0,
x: float = 0.0,
y: float = 0.0):
"""
Voltage source converter (VSC) with 3 terminals
:param bus_from: bus_dc_p
:param bus_to: bus_ac
:param bus_dc_n:
:param bus_from:
:param bus_to:
:param name:
:param idtag:
:param code:
:param active:
:param design_rate: Design rate (MVA)
:param rate:
:param alpha1:
:param alpha2:
:param alpha3:
:param mttf:
:param mttr:
:param cost:
:param contingency_factor:
:param protection_rating_factor:
:param contingency_enabled:
:param monitor_loading:
:param capex:
:param opex:
:param build_status:
:param control1:
:param control2:
:param fault_control:
:param min_ac_voltage: Voltage threshold below which the converter is disconnected
:param ysvs: Admittance of Static Var Systems
:param x: graphical x position (px)
:param y: graphical y position (px)
"""
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,
cost=cost,
mttf=mttf,
mttr=mttr,
contingency_factor=contingency_factor,
protection_rating_factor=protection_rating_factor,
contingency_enabled=contingency_enabled,
monitor_loading=monitor_loading,
capex=capex,
opex=opex,
build_status=build_status,
temp_base=25,
temp_oper=25,
alpha=0.0033,
device_type=DeviceType.VscDevice)
# TODO SANPEN: this is making the ntc test fail
# if bus_from is not None and bus_dc_n is not None and bus_to is not None:
# if bus_from.is_dc and bus_dc_n.is_dc and not bus_to.is_dc:
# # self._bus_dc_p = bus_dc_p
# # self._bus_dc_n = bus_dc_n
# # self._bus_ac = bus_ac
#
# # self._cn_dc_p = cn_dc_p
# # self._cn_dc_n = cn_dc_n
# # self._cn_ac = cn_ac
#
# self._bus_from = bus_from
# self._bus_dc_n = bus_dc_n
# self._bus_to = bus_to
#
# else:
# raise Exception('Impossible connecting a VSC device here. '
# 'VSC devices must be connected between 1 AC and 2 DC buses')
# else:
# # self._bus_dc_p = None
# # self._bus_dc_n = None
# # self._bus_ac = None
#
# # self._cn_dc_p = None
# # self._cn_dc_n = None
# # self._cn_ac = None
#
# self._bus_from = None
# self._bus_dc_n = None
# self._bus_to = None
# the VSC must only connect from an DC to a AC bus
# this connectivity sense is done to keep track with the articles that set it
# from -> DC
# to -> AC
# assert(bus_from.is_dc != bus_to.is_dc)
if bus_to is not None and bus_from is not None:
# connectivity:
# for the later primitives to make sense, the "bus from" must be AC and the "bus to" must be DC
if bus_from.is_dc and not bus_to.is_dc: # this is the correct sense
self.bus_from = bus_from
self.bus_to = bus_to
elif not bus_from.is_dc and bus_to.is_dc: # opposite sense, revert
self.bus_from = bus_to
self.bus_to = bus_from
print('Corrected the connection direction of the VSC device:', self.name)
else:
raise Exception('Impossible connecting a VSC device here. '
'VSC devices must be connected between AC and DC buses')
else:
self.bus_from = None
self.bus_to = None
# self._bus_from = bus_from
self._bus_dc_n = bus_dc_n
# self._bus_to = bus_to
self.alpha1 = float(alpha1)
self.alpha2 = float(alpha2)
self.alpha3 = float(alpha3)
# u_setpoint_min: float = 0.9, -> min_val
# u_setpoint_max: float = 1.1, -> max_val
# u_setpoint: float = 1.0, -> val
# Q_min: float = -999.0,
# Q_max: float = 999.0,
self._control1: ConverterControlType = control1
self._control1_prof: ProfileEnum = ProfileEnum(default_value=control1, enum_type=ConverterControlType)
self._control1_dev: Bus | BRANCH_TYPES | None = control1_dev
self._control1_dev_prof: ProfileDevice = ProfileDevice(default_value=control1_dev,
device_type=DeviceType.BusOrBranch)
self._control1_val = float(control1_val)
self._control1_val_prof: ProfileFloat = ProfileFloat(default_value=self._control1_val)
self._control1_val_min = float(control1_val_min)
self._control1_val_max = float(control1_val_max)
self._control1_val_droop = float(control1_droop)
self._control1_val_droop_prof: ProfileFloat = ProfileFloat(default_value=self._control1_val_droop)
self._control1_droop_val = float(control1_droop_val)
self._control1_droop_val_prof: ProfileFloat = ProfileFloat(default_value=self._control1_droop_val)
self._control1_droop_val_min = float(control1_droop_val_min)
self._control1_droop_val_max = float(control1_droop_val_max)
self._control2: ConverterControlType = control2
self._control2_prof: ProfileEnum = ProfileEnum(default_value=control2, enum_type=ConverterControlType)
self._control2_dev: Bus | BRANCH_TYPES | None = control2_dev
self._control2_dev_prof: ProfileDevice = ProfileDevice(default_value=control2_dev,
device_type=DeviceType.BusOrBranch)
self._control2_val = float(control2_val)
self._control2_val_prof: ProfileFloat = ProfileFloat(default_value=self._control2_val)
self._control2_val_min = float(control2_val_min)
self._control2_val_max = float(control2_val_max)
self._control2_val_droop = float(control2_droop)
self._control2_val_droop_prof: ProfileFloat = ProfileFloat(default_value=self._control2_val_droop)
self._control2_droop_val = float(control2_droop_val)
self._control2_droop_val_prof: ProfileFloat = ProfileFloat(default_value=self._control2_droop_val)
self._control2_droop_val_min = float(control2_droop_val_min)
self._control2_droop_val_max = float(control2_droop_val_max)
self._fault_control: ConverterFaultControlType = fault_control
self._fault_control_prof: ProfileEnum = ProfileEnum(default_value=fault_control,
enum_type=ConverterFaultControlType)
self.min_ac_voltage = float(min_ac_voltage)
self._ysvs = float(ysvs)
self.x = float(x)
self.y = float(y)
@property
def bus_from(self) -> Bus:
"""
Get the DC positive bus
"""
return self._bus_from
@bus_from.setter
def bus_from(self, value: Bus):
if value is None:
self._bus_from = value
else:
if isinstance(value, Bus):
if value.is_dc:
self._bus_from = value
else:
raise Exception('This should be a DC bus')
else:
raise Exception(str(type(value)) + 'not supported to be set into a _bus_from')
@property
def bus_dc_n(self) -> Bus:
"""
Get the DC negative bus
"""
return self._bus_dc_n
@bus_dc_n.setter
def bus_dc_n(self, value: Bus):
if value is None:
self._bus_dc_n = value
else:
if isinstance(value, Bus):
if value.is_dc:
self._bus_dc_n = value
else:
raise Exception('This should be a DC bus')
else:
raise Exception(str(type(value)) + 'not supported to be set into a _bus_dc_n')
@property
def bus_to(self) -> Bus:
"""
Get the AC bus
"""
return self._bus_to
@bus_to.setter
def bus_to(self, value: Bus):
if value is None:
self._bus_to = value
else:
if isinstance(value, Bus):
if not value.is_dc:
self._bus_to = value
else:
raise Exception('This should be an AC bus')
else:
raise Exception(str(type(value)) + 'not supported to be set into a _bus_to')
# @property
# def cn_dc_p(self) -> ConnectivityNode:
# """
# Get the DC positive connectivity node
# """
# return self._cn_dc_p
# @cn_dc_p.setter
# def cn_dc_p(self, val: ConnectivityNode):
# if val is None:
# self._cn_dc_p = val
# else:
# if isinstance(val, ConnectivityNode):
# self._cn_dc_p = val
# if self.bus_dc_p is None:
# self.bus_dc_p = self._cn_dc_p.bus
# else:
# raise Exception(str(type(val)) + 'not supported to be set into a connectivity node from')
@property
def control1(self):
"""
:return:
"""
return self._control1
@control1.setter
def control1(self, value: ConverterControlType):
if self.auto_update_enabled:
if value != self.control2:
self._control1 = value
# Revert the control in range
if (value in (ConverterControlType.Vm_dc, ConverterControlType.Vm_ac) and
not (0.9 < self.control1_val <= 1.1)):
self.control1_val = 1.0
else:
self._control1 = value
@property
def control1_prof(self) -> ProfileEnum:
"""
Cost profile
:return: Profile
"""
return self._control1_prof
@control1_prof.setter
def control1_prof(self, val: ProfileEnum | np.ndarray):
if isinstance(val, ProfileEnum):
self._control1_prof = val
elif isinstance(val, np.ndarray):
self._control1_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control1_at(self, t: int | None) -> ConverterControlType:
"""
:param t:
:return:
"""
return get_at(self.control1, self.control1_prof, t)
@property
def control2(self):
"""
:return:
"""
return self._control2
@control2.setter
def control2(self, value: ConverterControlType):
if self.auto_update_enabled:
if value != self.control1:
self._control2 = value
# Revert the control in range
if (value in (ConverterControlType.Vm_dc, ConverterControlType.Vm_ac) and
not (0.9 < self.control2_val <= 1.1)):
self.control2_val = 1.0
else:
self._control2 = value
@property
def control2_prof(self) -> ProfileEnum:
"""
Cost profile
:return: Profile
"""
return self._control2_prof
@control2_prof.setter
def control2_prof(self, val: ProfileEnum | np.ndarray):
if isinstance(val, ProfileEnum):
self._control2_prof = val
elif isinstance(val, np.ndarray):
self._control2_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control2_at(self, t: int | None) -> ConverterControlType:
"""
:param t:
:return:
"""
return get_at(self.control2, self.control2_prof, t)
@property
def fault_control(self):
"""
:return:
"""
return self._fault_control
@fault_control.setter
def fault_control(self, value: ConverterFaultControlType):
self._fault_control = value
@property
def fault_control_prof(self) -> ProfileEnum:
"""
Cost profile
:return: Profile
"""
return self._fault_control_prof
@fault_control_prof.setter
def fault_control_prof(self, val: ProfileEnum | np.ndarray):
if isinstance(val, ProfileEnum):
self._fault_control_prof = val
elif isinstance(val, np.ndarray):
self._fault_control_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_fault_control_at(self, t: int | None) -> ConverterControlType:
"""
:param t:
:return:
"""
return get_at(self.fault_control, self.fault_control_prof, t)
@property
def control1_val(self):
"""
:return:
"""
return self._control1_val
@control1_val.setter
def control1_val(self, value: float):
value = float(value)
self._control1_val = value
@property
def control1_val_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._control1_val_prof
@control1_val_prof.setter
def control1_val_prof(self, val: ProfileFloat | np.ndarray):
if isinstance(val, ProfileFloat):
self._control1_val_prof = val
elif isinstance(val, np.ndarray):
self._control1_val_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control1_val_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.control1_val, self.control1_val_prof, t)
@property
def control2_val(self):
"""
:return:
"""
return self._control2_val
@control2_val.setter
def control2_val(self, value: float):
value = float(value)
self._control2_val = value
@property
def control2_val_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._control2_val_prof
@control2_val_prof.setter
def control2_val_prof(self, val: ProfileFloat | np.ndarray):
if isinstance(val, ProfileFloat):
self._control2_val_prof = val
elif isinstance(val, np.ndarray):
self._control2_val_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control2_val_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.control2_val, self.control2_val_prof, t)
@property
def control1_val_min(self) -> float:
"""
Get ``control1_val_min``.
:return: float
"""
return self._control1_val_min
@control1_val_min.setter
def control1_val_min(self, val: float) -> None:
"""
Set ``control1_val_min``.
:param val: Value to assign.
:return: None
"""
self._control1_val_min = float(val)
@property
def control1_val_max(self) -> float:
"""
Get ``control1_val_max``.
:return: float
"""
return self._control1_val_max
@control1_val_max.setter
def control1_val_max(self, val: float) -> None:
"""
Set ``control1_val_max``.
:param val: Value to assign.
:return: None
"""
self._control1_val_max = float(val)
@property
def control1_val_droop(self) -> float:
"""
Get ``control1_val_droop``.
:return: float
"""
return self._control1_val_droop
@control1_val_droop.setter
def control1_val_droop(self, val: float) -> None:
"""
Set ``control1_val_droop``.
:param val: Value to assign.
:return: None
"""
self._control1_val_droop = float(val)
@property
def control1_val_droop_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._control1_val_droop_prof
@control1_val_droop_prof.setter
def control1_val_droop_prof(self, val: ProfileFloat | np.ndarray):
if isinstance(val, ProfileFloat):
self._control1_val_droop_prof = val
elif isinstance(val, np.ndarray):
self._control1_val_droop_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control1_val_droop_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.control1_val_droop, self.control1_val_droop_prof, t)
@property
def control1_droop_val(self) -> float:
"""
Get ``control1_droop_val``.
:return: float
"""
return self._control1_droop_val
@control1_droop_val.setter
def control1_droop_val(self, val: float) -> None:
"""
Set ``control1_droop_val``.
:param val: Value to assign.
:return: None
"""
self._control1_droop_val = float(val)
@property
def control1_droop_val_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._control1_droop_val_prof
@control1_droop_val_prof.setter
def control1_droop_val_prof(self, val: ProfileFloat | np.ndarray):
if isinstance(val, ProfileFloat):
self._control1_droop_val_prof = val
elif isinstance(val, np.ndarray):
self._control1_droop_val_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control1_droop_val_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.control1_droop_val, self.control1_droop_val_prof, t)
@property
def control1_droop_val_min(self) -> float:
"""
Get ``control1_droop_val_min``.
:return: float
"""
return self._control1_droop_val_min
@control1_droop_val_min.setter
def control1_droop_val_min(self, val: float) -> None:
"""
Set ``control1_droop_val_min``.
:param val: Value to assign.
:return: None
"""
self._control1_droop_val_min = float(val)
@property
def control1_droop_val_max(self) -> float:
"""
Get ``control1_droop_val_max``.
:return: float
"""
return self._control1_droop_val_max
@control1_droop_val_max.setter
def control1_droop_val_max(self, val: float) -> None:
"""
Set ``control1_droop_val_max``.
:param val: Value to assign.
:return: None
"""
self._control1_droop_val_max = float(val)
@property
def control2_val_min(self) -> float:
"""
Get ``control2_val_min``.
:return: float
"""
return self._control2_val_min
@control2_val_min.setter
def control2_val_min(self, val: float) -> None:
"""
Set ``control2_val_min``.
:param val: Value to assign.
:return: None
"""
self._control2_val_min = float(val)
@property
def control2_val_max(self) -> float:
"""
Get ``control2_val_max``.
:return: float
"""
return self._control2_val_max
@control2_val_max.setter
def control2_val_max(self, val: float) -> None:
"""
Set ``control2_val_max``.
:param val: Value to assign.
:return: None
"""
self._control2_val_max = float(val)
@property
def control2_val_droop(self) -> float:
"""
Get ``control2_val_droop``.
:return: float
"""
return self._control2_val_droop
@control2_val_droop.setter
def control2_val_droop(self, val: float) -> None:
"""
Set ``control2_val_droop``.
:param val: Value to assign.
:return: None
"""
self._control2_val_droop = float(val)
@property
def control2_val_droop_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._control2_val_droop_prof
@control2_val_droop_prof.setter
def control2_val_droop_prof(self, val: ProfileFloat | np.ndarray):
if isinstance(val, ProfileFloat):
self._control2_val_droop_prof = val
elif isinstance(val, np.ndarray):
self._control2_val_droop_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control2_val_droop_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.control2_val_droop, self.control2_val_droop_prof, t)
@property
def control2_droop_val(self) -> float:
"""
Get ``control2_droop_val``.
:return: float
"""
return self._control2_droop_val
@control2_droop_val.setter
def control2_droop_val(self, val: float) -> None:
"""
Set ``control2_droop_val``.
:param val: Value to assign.
:return: None
"""
self._control2_droop_val = float(val)
@property
def control2_droop_val_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._control2_droop_val_prof
@control2_droop_val_prof.setter
def control2_droop_val_prof(self, val: ProfileFloat | np.ndarray):
if isinstance(val, ProfileFloat):
self._control2_droop_val_prof = val
elif isinstance(val, np.ndarray):
self._control2_droop_val_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control2_droop_val_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.control2_droop_val, self.control2_droop_val_prof, t)
@property
def control2_droop_val_min(self) -> float:
"""
Get ``control2_droop_val_min``.
:return: float
"""
return self._control2_droop_val_min
@control2_droop_val_min.setter
def control2_droop_val_min(self, val: float) -> None:
"""
Set ``control2_droop_val_min``.
:param val: Value to assign.
:return: None
"""
self._control2_droop_val_min = float(val)
@property
def control2_droop_val_max(self) -> float:
"""
Get ``control2_droop_val_max``.
:return: float
"""
return self._control2_droop_val_max
@control2_droop_val_max.setter
def control2_droop_val_max(self, val: float) -> None:
"""
Set ``control2_droop_val_max``.
:param val: Value to assign.
:return: None
"""
self._control2_droop_val_max = float(val)
@property
def control1_dev(self):
"""
:return:
"""
return self._control1_dev
@control1_dev.setter
def control1_dev(self, value: Bus | BranchParent | None = None):
self._control1_dev = value
@property
def control1_dev_prof(self) -> ProfileDevice:
"""
Cost profile
:return: Profile
"""
return self._control1_dev_prof
@control1_dev_prof.setter
def control1_dev_prof(self, val: ProfileDevice | np.ndarray):
if isinstance(val, ProfileDevice):
self._control1_dev_prof = val
elif isinstance(val, np.ndarray):
self._control1_dev_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control1_dev_at(self, t: int | None) -> Bus | BranchParent | None:
"""
:param t:
:return:
"""
return get_at(self.control1_dev, self.control1_dev_prof, t)
@property
def control2_dev(self):
"""
:return:
"""
return self._control2_dev
@control2_dev.setter
def control2_dev(self, value: Bus | BranchParent | None = None):
self._control2_dev = value
@property
def control2_dev_prof(self) -> ProfileDevice:
"""
Cost profile
:return: Profile
"""
return self._control2_dev_prof
@control2_dev_prof.setter
def control2_dev_prof(self, val: ProfileDevice | np.ndarray):
if isinstance(val, ProfileDevice):
self._control2_dev_prof = val
elif isinstance(val, np.ndarray):
self._control2_dev_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a profile')
[docs]
def get_control2_dev_at(self, t: int | None) -> Bus | BranchParent | None:
"""
:param t:
:return:
"""
return get_at(self.control2_dev, self.control2_dev_prof, t)
[docs]
def get_coordinates(self) -> List[Tuple[float, float]]:
"""
Get the line defining coordinates
"""
return [self.bus_from.get_coordinates(), self.bus_to.get_coordinates()]
[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()
[docs]
def is_3term(self):
"""
Is this a 3-terminal VSC?
"""
return self.bus_from is not None and self.bus_to is not None and self._bus_dc_n is not None
# Scalar property accessors coerce assignments to the declared schema types.
@property
def alpha1(self) -> float:
"""
Get ``alpha1``.
:return: float
"""
return self._alpha1
@alpha1.setter
def alpha1(self, val: float) -> None:
"""
Set ``alpha1``.
:param val: Value to assign.
:return: None
"""
self._alpha1 = float(val)
@property
def alpha2(self) -> float:
"""
Get ``alpha2``.
:return: float
"""
return self._alpha2
@alpha2.setter
def alpha2(self, val: float) -> None:
"""
Set ``alpha2``.
:param val: Value to assign.
:return: None
"""
self._alpha2 = float(val)
@property
def alpha3(self) -> float:
"""
Get ``alpha3``.
:return: float
"""
return self._alpha3
@alpha3.setter
def alpha3(self, val: float) -> None:
"""
Set ``alpha3``.
:param val: Value to assign.
:return: None
"""
self._alpha3 = float(val)
@property
def min_ac_voltage(self) -> float:
"""
Get ``min_ac_voltage``.
:return: float
"""
return self._min_ac_voltage
@min_ac_voltage.setter
def min_ac_voltage(self, val: float) -> None:
"""
Set ``min_ac_voltage``.
:param val: Value to assign.
:return: None
"""
self._min_ac_voltage = float(val)
@property
def ysvs(self) -> float:
"""
Get ``ysvs``.
:return: float
"""
return self._ysvs
@ysvs.setter
def ysvs(self, val: float) -> None:
"""
Set ``ysvs``.
:param val: Value to assign.
:return: None
"""
self._ysvs = 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 y(self) -> float:
"""
Get ``y``.
:return: float
"""
return self._y
@y.setter
def y(self, val: float) -> None:
"""
Set ``y``.
:param val: Value to assign.
:return: None
"""
self._y = float(val)