# 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.DataStructures.branch_parent_data import BranchParentData
from VeraGridEngine.Utils.Sparse.sparse_array import SparseObjectArray
from VeraGridEngine.basic_structures import Vec, IntVec, CxVec, Logger
[docs]
class PassiveBranchData(BranchParentData):
"""
Structure to host all branches data for calculation
"""
def __init__(self, nelm: int, nbus: int):
"""
Branch data arrays
:param nelm: number of elements
:param nbus: number of buses
"""
BranchParentData.__init__(self, nelm=nelm, nbus=nbus)
self.R: Vec = np.zeros(self.nelm, dtype=float)
self.X: Vec = np.zeros(self.nelm, dtype=float)
self.G: Vec = np.zeros(self.nelm, dtype=float)
self.B: Vec = np.zeros(self.nelm, dtype=float)
self.R0: Vec = np.zeros(self.nelm, dtype=float)
self.X0: Vec = np.zeros(self.nelm, dtype=float)
self.G0: Vec = np.zeros(self.nelm, dtype=float)
self.B0: Vec = np.zeros(self.nelm, dtype=float)
self.R2: Vec = np.zeros(self.nelm, dtype=float)
self.X2: Vec = np.zeros(self.nelm, dtype=float)
self.G2: Vec = np.zeros(self.nelm, dtype=float)
self.B2: Vec = np.zeros(self.nelm, dtype=float)
self.conn: IntVec = np.zeros(self.nelm, dtype=int)
self.conn_f: IntVec = np.zeros(self.nelm, dtype=int)
self.conn_t: IntVec = np.zeros(self.nelm, dtype=int)
self.m_taps = SparseObjectArray(n=self.nelm)
self.tau_taps = SparseObjectArray(n=self.nelm)
self.virtual_tap_t: Vec = np.ones(self.nelm, dtype=float)
self.virtual_tap_f: Vec = np.ones(self.nelm, dtype=float)
self.Yff3 = np.zeros((self.nelm * 4, 4), dtype=complex)
self.Yft3 = np.zeros((self.nelm * 4, 4), dtype=complex)
self.Ytf3 = np.zeros((self.nelm * 4, 4), dtype=complex)
self.Ytt3 = np.zeros((self.nelm * 4, 4), dtype=complex)
self.phN: IntVec = np.zeros(self.nelm, dtype=int)
self.phA: IntVec = np.zeros(self.nelm, dtype=int)
self.phB: IntVec = np.zeros(self.nelm, dtype=int)
self.phC: IntVec = np.zeros(self.nelm, dtype=int)
[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) -> "PassiveBranchData":
"""
Slice branch data by given indices
:param elm_idx: array of branch indices
:param bus_idx: array of bus indices
:param bus_map: map from bus index to island bus index {int(o): i for i, o in enumerate(bus_idx)}
:param logger: Logger
:return: new BranchData instance
"""
data, bus_map = super().slice(elm_idx, bus_idx, bus_map, logger)
data.__class__ = PassiveBranchData
data: PassiveBranchData = data
data.R = self.R[elm_idx]
data.X = self.X[elm_idx]
data.G = self.G[elm_idx]
data.B = self.B[elm_idx]
data.R0 = self.R0[elm_idx]
data.X0 = self.X0[elm_idx]
data.G0 = self.G0[elm_idx]
data.B0 = self.B0[elm_idx]
data.R2 = self.R2[elm_idx]
data.X2 = self.X2[elm_idx]
data.G2 = self.G2[elm_idx]
data.B2 = self.B2[elm_idx]
data.conn = self.conn[elm_idx] # winding connection
data.conn_f = self.conn_f[elm_idx]
data.conn_t = self.conn_t[elm_idx]
data.m_taps = self.m_taps.slice(elm_idx)
data.tau_taps = self.tau_taps.slice(elm_idx)
data.virtual_tap_f = self.virtual_tap_f[elm_idx]
data.virtual_tap_t = self.virtual_tap_t[elm_idx]
elm_idx_4 = ((elm_idx * 4)[:, np.newaxis] + np.arange(4)).flatten()
data.Yff3 = self.Yff3[elm_idx_4, :]
data.Yft3 = self.Yft3[elm_idx_4, :]
data.Ytt3 = self.Ytt3[elm_idx_4, :]
data.Ytf3 = self.Ytf3[elm_idx_4, :]
data.phN = self.phN[elm_idx]
data.phA = self.phA[elm_idx]
data.phB = self.phB[elm_idx]
data.phC = self.phC[elm_idx]
return data
[docs]
def copy(self) -> "PassiveBranchData":
"""
Get a deep copy of this object
:return: new BranchData instance
"""
data: PassiveBranchData = super().copy()
data.__class__ = PassiveBranchData
data.R = self.R.copy()
data.X = self.X.copy()
data.G = self.G.copy()
data.B = self.B.copy()
data.R0 = self.R0.copy()
data.X0 = self.X0.copy()
data.G0 = self.G0.copy()
data.B0 = self.B0.copy()
data.R2 = self.R2.copy()
data.X2 = self.X2.copy()
data.G2 = self.G2.copy()
data.B2 = self.B2.copy()
data.conn = self.conn.copy() # winding connection
data.conn_f = self.conn_f.copy()
data.conn_t = self.conn_t.copy()
data.m_taps = self.m_taps.copy()
data.tau_taps = self.tau_taps.copy()
data.virtual_tap_f = self.virtual_tap_f.copy()
data.virtual_tap_t = self.virtual_tap_t.copy()
data.Yff3 = self.Yff3.copy()
data.Yft3 = self.Yft3.copy()
data.Ytf3 = self.Ytf3.copy()
data.Ytt3 = self.Ytt3.copy()
data.phN = self.phN.copy()
data.phA = self.phA.copy()
data.phB = self.phB.copy()
data.phC = self.phC.copy()
return data
[docs]
def get_series_admittance(self) -> CxVec:
"""
Get the series admittance of the branches
:return: complex vector
"""
return 1.0 / (self.R + 1.0j * self.X + 1e-20)
[docs]
def detect_superconductor_at(self, k) -> None:
"""
There is a beyond terrible practice of setting branches with R=0 and X=0 as "superconductor"....
Those must be reduced of course
:param k: index
"""
# handle """superconductor branches"""
if self.R[k] == 0.0 and self.X[k] == 0.0:
self.reducible[k] = 1