# 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 numpy as np
from VeraGridEngine.Utils.Sparse.sparse_array import SparseObjectArray
from VeraGridEngine.basic_structures import Vec, IntVec, CxVec, Logger
[docs]
class ActiveBranchData:
"""
ControllableBranchData
"""
def __init__(self, nelm: int, nbus: int):
self.nelm: int = nelm
self.nbus: int = nbus
self.is_controlled: IntVec = np.zeros(self.nelm, dtype=int)
self.m_taps = SparseObjectArray(n=self.nelm)
self.tau_taps = SparseObjectArray(n=self.nelm)
self.tap_module: Vec = np.ones(nelm, dtype=float)
self.tap_module_min: Vec = np.full(nelm, fill_value=0.1, dtype=float)
self.tap_module_max: Vec = np.full(nelm, fill_value=1.5, dtype=float)
self.tap_angle: Vec = np.zeros(nelm, dtype=float)
self.tap_angle_min: Vec = np.full(nelm, fill_value=-6.28, dtype=float)
self.tap_angle_max: Vec = np.full(nelm, fill_value=6.28, dtype=float)
self.tap_module_control_mode: IntVec = np.zeros(self.nelm, dtype=int)
self.tap_phase_control_mode: IntVec = np.zeros(self.nelm, dtype=int)
self.tap_controlled_buses: IntVec = np.zeros(self.nelm, dtype=int)
self.Pset: Vec = np.zeros(nelm, dtype=float) # always over the controlled side
self.Qset: Vec = np.zeros(nelm, dtype=float) # always over the controlled side
self.vset: Vec = np.ones(nelm, dtype=float) # always over the controlled side
self._any_pf_control = False
@property
def any_pf_control(self):
"""
:return:
"""
return self._any_pf_control
@any_pf_control.setter
def any_pf_control(self, value):
self._any_pf_control = value
[docs]
def size(self) -> int:
"""
Get size of the structure
:return:
"""
return self.nelm
[docs]
def slice(self, elm_idx: IntVec, bus_idx: IntVec,
bus_map: IntVec, logger: Logger | None) -> ActiveBranchData:
"""
Slice branch data by given indices
:param elm_idx: array of branch indices
:param bus_idx: array of bus indices
:param bus_map: array of bus indices to re index main to island indices
:param logger: Logger (optional)
:return: new BranchData instance
"""
data = ActiveBranchData(nelm=len(elm_idx), nbus=len(bus_idx))
data.is_controlled = self.is_controlled[elm_idx]
data.m_taps = self.m_taps.slice(elm_idx)
data.tau_taps = self.tau_taps.slice(elm_idx)
data.tap_module = self.tap_module[elm_idx]
data.tap_module_min = self.tap_module_min[elm_idx]
data.tap_module_max = self.tap_module_max[elm_idx]
data.tap_angle = self.tap_angle[elm_idx]
data.tap_angle_min = self.tap_angle_min[elm_idx]
data.tap_angle_max = self.tap_angle_max[elm_idx]
data.tap_phase_control_mode = self.tap_phase_control_mode[elm_idx]
data.tap_module_control_mode = self.tap_module_control_mode[elm_idx]
data.tap_controlled_buses = self.tap_controlled_buses[elm_idx]
data.Pset = self.Pset[elm_idx]
data.Qset = self.Qset[elm_idx]
data.vset = self.vset[elm_idx]
data.any_pf_control = self.any_pf_control
for k in range(data.nelm):
if data.tap_controlled_buses[k] != 0:
data.tap_controlled_buses[k] = bus_map[data.tap_controlled_buses[k]]
if data.tap_controlled_buses[k] == -1:
if logger is not None:
logger.add_error(f"Branch {k}, is controlling a bus from another island ",
value=data.tap_controlled_buses[k])
return data
[docs]
def copy(self) -> ActiveBranchData:
"""
:return:
"""
data = ActiveBranchData(nelm=self.nelm, nbus=self.nbus)
data.is_controlled = self.is_controlled.copy()
data.m_taps = self.m_taps.copy()
data.tau_taps = self.tau_taps.copy()
data.tap_module = self.tap_module.copy()
data.tap_module_min = self.tap_module_min.copy()
data.tap_module_max = self.tap_module_max.copy()
data.tap_angle = self.tap_angle.copy()
data.tap_angle_min = self.tap_angle_min.copy()
data.tap_angle_max = self.tap_angle_max.copy()
data.tap_module_control_mode = self.tap_module_control_mode.copy()
data.tap_phase_control_mode = self.tap_phase_control_mode.copy()
data.tap_controlled_buses = self.tap_controlled_buses.copy()
data.Pset = self.Pset.copy()
data.Qset = self.Qset.copy()
data.vset = self.vset.copy()
data.any_pf_control = self.any_pf_control
return data
[docs]
def remap(self, bus_map_arr: IntVec) -> None:
"""
Remapping of the branch-controlled bus indices.
:param bus_map_arr: array of old-to-new buses
"""
for k in range(self.nelm):
if self.is_controlled[k]:
bus_idx = self.tap_controlled_buses[k]
if bus_idx >= 0:
self.tap_controlled_buses[k] = bus_map_arr[bus_idx]
@property
def tap(self) -> CxVec:
"""
:return:
"""
return self.tap_module * np.exp(1.0j * self.tap_angle)