# 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
from typing import Union, Tuple
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from VeraGridEngine.enumerations import DeviceType, BuildStatus, PrpCat
from VeraGridEngine.Devices.Parents.load_parent import InjectionParent
from VeraGridEngine.Devices.Parents.editable_device import get_at, GCProp
from VeraGridEngine.Devices.Profiles import ProfileFloat
[docs]
class CurrentInjection(InjectionParent):
"""
CurrentInjection
"""
__slots__ = (
'_Ir',
'_Ii',
'_Ir_prof',
'_Ii_prof',
'_Ir1',
'_Ii1',
'_Ir1_prof',
'_Ii1_prof',
'_Ir2',
'_Ii2',
'_Ir2_prof',
'_Ii2_prof',
'_Ir3',
'_Ii3',
'_Ir3_prof',
'_Ii3_prof',
)
LOCAL_PROPERTY_DECLARATIONS: Tuple[GCProp, ...] = (
GCProp(
prop_name='Ir',
units='MW',
tpe=float,
definition='Active power of the current component at V=1.0 p.u.',
profile_name='Ir_prof',
cat=[PrpCat.PF],
),
GCProp(
prop_name='Ir1',
units='MW',
tpe=float,
definition='Active power of the current component at V=1.0 p.u.',
profile_name='Ir1_prof',
cat=[PrpCat.PF3],
),
GCProp(
prop_name='Ir2',
units='MW',
tpe=float,
definition='Active power of the current component at V=1.0 p.u.',
profile_name='Ir2_prof',
cat=[PrpCat.PF3],
),
GCProp(
prop_name='Ir3',
units='MW',
tpe=float,
definition='Active power of the current component at V=1.0 p.u.',
profile_name='Ir3_prof',
cat=[PrpCat.PF3],
),
GCProp(
prop_name='Ii',
units='MVAr',
tpe=float,
definition='Reactive power of the current component at V=1.0 p.u.',
profile_name='Ii_prof',
cat=[PrpCat.PF],
),
GCProp(
prop_name='Ii1',
units='MVAr',
tpe=float,
definition='Reactive power of the current component at V=1.0 p.u.',
profile_name='Ii1_prof',
cat=[PrpCat.PF3],
),
GCProp(
prop_name='Ii2',
units='MVAr',
tpe=float,
definition='Reactive power of the current component at V=1.0 p.u.',
profile_name='Ii2_prof',
cat=[PrpCat.PF3],
),
GCProp(
prop_name='Ii3',
units='MVAr',
tpe=float,
definition='Reactive power of the current component at V=1.0 p.u.',
profile_name='Ii3_prof',
cat=[PrpCat.PF3],
),
)
def __init__(self, name='CurrentInjection', idtag=None, code='', Ir=0.0, Ii=0.0, Cost=1200.0,
Ir1=0.0, Ir2=0.0, Ir3=0.0, Ii1=0.0, Ii2=0.0, Ii3=0.0,
active=True, mttf=0.0, mttr=0.0, capex=0, opex=0,
build_status: BuildStatus = BuildStatus.Commissioned):
"""
The load object implements the so-called ZIP model, in which the load can be
represented by a combination of power (P), current(I), and impedance (Z).
The sign convention is: Positive to act as a load, negative to act as a generator.
:param name: Name of the device
:param idtag: UUID code
:param code: secondary ID code
:param Ir: Real current in equivalent MW
:param Ir1: Real phase 1 current in equivalent MW
:param Ir2: Real phase 2 current in equivalent MW
:param Ir3: Real phase 3 current in equivalent MW
:param Ii: Imaginary current in equivalent MVAr
:param Ii1: Imaginary phase 1 current in equivalent MVAr
:param Ii2: Imaginary phase 2 current in equivalent MVAr
:param Ii3: Imaginary phase 3 current in equivalent MVAr
:param Cost: Cost of load shedding
:param active: Is the load active?
:param mttf: Mean time to failure in hours
:param mttr: Mean time to recovery in hours
"""
InjectionParent.__init__(self,
name=name,
idtag=idtag,
code=code,
bus=None,
active=active,
Cost=Cost,
mttf=mttf,
mttr=mttr,
capex=capex,
opex=opex,
build_status=build_status,
device_type=DeviceType.CurrentInjectionDevice)
self.Ir = float(Ir)
self.Ir1 = float(Ir1)
self.Ir2 = float(Ir2)
self.Ir3 = float(Ir3)
self.Ii = float(Ii)
self.Ii1 = float(Ii1)
self.Ii2 = float(Ii2)
self.Ii3 = float(Ii3)
self._Ir_prof = ProfileFloat(default_value=self.Ir)
self._Ir1_prof = ProfileFloat(default_value=self.Ir1)
self._Ir2_prof = ProfileFloat(default_value=self.Ir2)
self._Ir3_prof = ProfileFloat(default_value=self.Ir3)
self._Ii_prof = ProfileFloat(default_value=self.Ii)
self._Ii1_prof = ProfileFloat(default_value=self.Ii1)
self._Ii2_prof = ProfileFloat(default_value=self.Ii2)
self._Ii3_prof = ProfileFloat(default_value=self.Ii3)
@property
def Ir_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._Ir_prof
@Ir_prof.setter
def Ir_prof(self, val: Union[ProfileFloat, np.ndarray]):
if isinstance(val, ProfileFloat):
self._Ir_prof = val
elif isinstance(val, np.ndarray):
self._Ir_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a Ir_prof')
[docs]
def get_Ir_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.Ir, self.Ir_prof, t)
@property
def Ir1_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._Ir1_prof
@Ir1_prof.setter
def Ir1_prof(self, val: Union[ProfileFloat, np.ndarray]):
if isinstance(val, ProfileFloat):
self._Ir1_prof = val
elif isinstance(val, np.ndarray):
self._Ir1_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a Ir1_prof')
[docs]
def get_Ir1_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.Ir1, self.Ir1_prof, t)
@property
def Ir2_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._Ir2_prof
@Ir2_prof.setter
def Ir2_prof(self, val: Union[ProfileFloat, np.ndarray]):
if isinstance(val, ProfileFloat):
self._Ir2_prof = val
elif isinstance(val, np.ndarray):
self._Ir2_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a Ir2_prof')
[docs]
def get_Ir2_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.Ir2, self.Ir2_prof, t)
@property
def Ir3_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._Ir3_prof
@Ir3_prof.setter
def Ir3_prof(self, val: Union[ProfileFloat, np.ndarray]):
if isinstance(val, ProfileFloat):
self._Ir3_prof = val
elif isinstance(val, np.ndarray):
self._Ir3_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a Ir3_prof')
[docs]
def get_Ir3_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.Ir3, self.Ir3_prof, t)
@property
def Ii_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._Ii_prof
@Ii_prof.setter
def Ii_prof(self, val: Union[ProfileFloat, np.ndarray]):
if isinstance(val, ProfileFloat):
self._Ii_prof = val
elif isinstance(val, np.ndarray):
self._Ii_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a Ii_prof')
[docs]
def get_Ii_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.Ii, self.Ii_prof, t)
@property
def Ii1_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._Ii1_prof
@Ii1_prof.setter
def Ii1_prof(self, val: Union[ProfileFloat, np.ndarray]):
if isinstance(val, ProfileFloat):
self._Ii1_prof = val
elif isinstance(val, np.ndarray):
self._Ii1_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a Ii1_prof')
[docs]
def get_Ii1_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.Ii1, self.Ii1_prof, t)
@property
def Ii2_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._Ii2_prof
@Ii2_prof.setter
def Ii2_prof(self, val: Union[ProfileFloat, np.ndarray]):
if isinstance(val, ProfileFloat):
self._Ii2_prof = val
elif isinstance(val, np.ndarray):
self._Ii2_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a Ii2_prof')
[docs]
def get_Ii2_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.Ii2, self.Ii2_prof, t)
@property
def Ii3_prof(self) -> ProfileFloat:
"""
Cost profile
:return: Profile
"""
return self._Ii3_prof
@Ii3_prof.setter
def Ii3_prof(self, val: Union[ProfileFloat, np.ndarray]):
if isinstance(val, ProfileFloat):
self._Ii3_prof = val
elif isinstance(val, np.ndarray):
self._Ii3_prof.set(arr=val)
else:
raise Exception(str(type(val)) + 'not supported to be set into a Ii3_prof')
[docs]
def get_Ii3_at(self, t: int | None) -> float:
"""
:param t:
:return:
"""
return get_at(self.Ii3, self.Ii3_prof, t)
[docs]
def get_I_at(self, t: int | None) -> complex:
"""
:param t:
:return:
"""
return complex(self.get_Ir_at(t), self.get_Ii_at(t))
[docs]
def get_I1_at(self, t: int | None) -> complex:
"""
:param t:
:return:
"""
return complex(self.get_Ir1_at(t), self.get_Ii1_at(t))
[docs]
def get_I2_at(self, t: int | None) -> complex:
"""
:param t:
:return:
"""
return complex(self.get_Ir2_at(t), self.get_Ii2_at(t))
[docs]
def get_I3_at(self, t: int | None) -> complex:
"""
:param t:
:return:
"""
return complex(self.get_Ir3_at(t), self.get_Ii3_at(t))
[docs]
def plot_profiles(self, time=None, show_fig=True):
"""
Plot the time series results of this object
:param time: array of time values
:param show_fig: Show the figure?
"""
if time is not None:
fig = plt.figure(figsize=(12, 8))
ax_1 = fig.add_subplot(211)
ax_2 = fig.add_subplot(212, sharex=ax_1)
# P
y = self.Ir_prof.toarray()
df = pd.DataFrame(data=y, index=time, columns=[self.name])
ax_1.set_title('Active power', fontsize=14)
ax_1.set_ylabel('MW', fontsize=11)
df.plot(ax=ax_1)
# Q
y = self.Ii_prof.toarray()
df = pd.DataFrame(data=y, index=time, columns=[self.name])
ax_2.set_title('Reactive power', fontsize=14)
ax_2.set_ylabel('MVAr', 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 Ir(self) -> float:
"""
Get ``Ir``.
:return: float
"""
return self._Ir
@Ir.setter
def Ir(self, val: float) -> None:
"""
Set ``Ir``.
:param val: Value to assign.
:return: None
"""
self._Ir = float(val)
@property
def Ir1(self) -> float:
"""
Get ``Ir1``.
:return: float
"""
return self._Ir1
@Ir1.setter
def Ir1(self, val: float) -> None:
"""
Set ``Ir1``.
:param val: Value to assign.
:return: None
"""
self._Ir1 = float(val)
@property
def Ir2(self) -> float:
"""
Get ``Ir2``.
:return: float
"""
return self._Ir2
@Ir2.setter
def Ir2(self, val: float) -> None:
"""
Set ``Ir2``.
:param val: Value to assign.
:return: None
"""
self._Ir2 = float(val)
@property
def Ir3(self) -> float:
"""
Get ``Ir3``.
:return: float
"""
return self._Ir3
@Ir3.setter
def Ir3(self, val: float) -> None:
"""
Set ``Ir3``.
:param val: Value to assign.
:return: None
"""
self._Ir3 = float(val)
@property
def Ii(self) -> float:
"""
Get ``Ii``.
:return: float
"""
return self._Ii
@Ii.setter
def Ii(self, val: float) -> None:
"""
Set ``Ii``.
:param val: Value to assign.
:return: None
"""
self._Ii = float(val)
@property
def Ii1(self) -> float:
"""
Get ``Ii1``.
:return: float
"""
return self._Ii1
@Ii1.setter
def Ii1(self, val: float) -> None:
"""
Set ``Ii1``.
:param val: Value to assign.
:return: None
"""
self._Ii1 = float(val)
@property
def Ii2(self) -> float:
"""
Get ``Ii2``.
:return: float
"""
return self._Ii2
@Ii2.setter
def Ii2(self, val: float) -> None:
"""
Set ``Ii2``.
:param val: Value to assign.
:return: None
"""
self._Ii2 = float(val)
@property
def Ii3(self) -> float:
"""
Get ``Ii3``.
:return: float
"""
return self._Ii3
@Ii3.setter
def Ii3(self, val: float) -> None:
"""
Set ``Ii3``.
:param val: Value to assign.
:return: None
"""
self._Ii3 = float(val)