# 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 Dict, Union, List
import numpy as np
from VeraGridEngine.basic_structures import CxMat, Mat
[docs]
def list_to_matrix(data: List[List[float]] | None, size: int) -> Mat:
"""
Attempts converting a list of lists to matrix
:param data: list of lists of floats representing a matrix
:param size: size of the matrix (square)
:return: Numpy array representing a matrix
"""
if size > 0 and len(data) > 0:
if data is None:
return np.zeros((size, size), dtype=complex)
else:
candidate = np.array(data)
if candidate.ndim == 2:
if candidate.shape[1] == size and candidate.shape[0] == size:
return candidate
else:
raise ValueError("AdmittanceMatrix values must be a square matrix")
else:
raise ValueError("AdmittanceMatrix values must be a matrix")
else:
return np.zeros((size, size), dtype=complex)
[docs]
class AdmittanceMatrix:
"""
This is the admittance matrix to store the three-phases admittance of a branch
"""
def __init__(self, size: int = 0):
"""
Constructor
:param size: size of the matrix (0 to 4)
"""
self.__size: int = size
self.__values: CxMat = np.zeros((size, size), dtype=complex)
self._phN: int = 0
self._phA: int = 0
self._phB: int = 0
self._phC: int = 0
[docs]
def copy(self) -> AdmittanceMatrix:
"""
Make a copy of the admittance matrix
:return:
"""
elm = AdmittanceMatrix(size=self.size)
elm.__values = self.__values.copy()
elm._phN = self._phN
elm._phA = self._phA
elm._phB = self._phB
elm._phC = self._phC
return elm
def __str__(self):
return f"size {self.__size}: {self._phN}, {self._phA}, {self._phB}, {self._phC}"
def __repr__(self):
return self.__str__()
@property
def phN(self):
"""
:return:
"""
return self._phN
@phN.setter
def phN(self, val: int):
if isinstance(val, int):
self._phN = val
else:
raise ValueError(f'{val} is not an int')
@property
def phA(self):
"""
:return:
"""
return self._phA
@phA.setter
def phA(self, val: int):
if isinstance(val, int):
self._phA = val
else:
raise ValueError(f'{val} is not an int')
@property
def phB(self):
return self._phB
@phB.setter
def phB(self, val: int):
if isinstance(val, int):
self._phB = val
else:
raise ValueError(f'{val} is not an int')
@property
def phC(self):
return self._phC
@phC.setter
def phC(self, val: int):
if isinstance(val, int):
self._phC = val
else:
raise ValueError(f'{val} is not an int')
@property
def size(self) -> int:
return self.__size
@size.setter
def size(self, size: int) -> None:
self.__size = size
self.__values: CxMat = np.zeros((size, size), dtype=complex)
@property
def values(self) -> CxMat:
return self.__values
@values.setter
def values(self, value: CxMat):
if isinstance(value, np.ndarray):
if value.dtype == complex:
self.__values = value
self.__size = value.shape[0]
else:
raise ValueError("AdmittanceMatrix only supports complex values")
else:
raise ValueError("AdmittanceMatrix only supports complex numpy arrays")
[docs]
def to_dict(self) -> Dict[str, Union[str, float]]:
"""
Get a dictionary representation of the tap
:return:
"""
return {
"size": self.__size,
"values_r": self.__values.real.tolist(),
"values_i": self.__values.imag.tolist(),
"phase_n": self._phN,
"phase_a": self._phA,
"phase_b": self._phB,
"phase_c": self._phC,
}
[docs]
def parse(self, data: Dict[str, Union[str, float, int]]) -> None:
"""
Parse the tap data
:param data: dictionary representation of the tap
"""
self.size: int = data.get("size", 3)
data_r = list_to_matrix(data.get("values_r", None), self.__size)
data_i = list_to_matrix(data.get("values_i", None), self.__size)
self.phN = data.get("phase_n", 0)
self.phA = data.get("phase_a", 0)
self.phB = data.get("phase_b", 0)
self.phC = data.get("phase_c", 0)
self.values = data_r + 1j * data_i
def __eq__(self, other: "AdmittanceMatrix") -> bool:
if self.size != other.size:
return False
if not np.allclose(self.values, other.values, atol=1e-10):
return False
return True