diff --git a/roseau/load_flow/converters.py b/roseau/load_flow/converters.py index 6f5c57d0..26508bcf 100644 --- a/roseau/load_flow/converters.py +++ b/roseau/load_flow/converters.py @@ -11,6 +11,8 @@ import numpy as np import pandas as pd +from roseau.load_flow.typing import ComplexArray + ALPHA = np.exp(2 / 3 * np.pi * 1j) """complex: Phasor rotation operator `alpha`, which rotates a phasor vector counterclockwise by 120 degrees when multiplied by it.""" @@ -28,7 +30,7 @@ _A_INV = np.linalg.inv(A) -def phasor_to_sym(v_abc: Sequence[complex]) -> np.ndarray[complex]: +def phasor_to_sym(v_abc: Sequence[complex]) -> ComplexArray: """Compute the symmetrical components `(0, +, -)` from the phasor components `(a, b, c)`.""" v_abc_array = np.asarray(v_abc) orig_shape = v_abc_array.shape @@ -36,7 +38,7 @@ def phasor_to_sym(v_abc: Sequence[complex]) -> np.ndarray[complex]: return v_012.reshape(orig_shape) -def sym_to_phasor(v_012: Sequence[complex]) -> np.ndarray[complex]: +def sym_to_phasor(v_012: Sequence[complex]) -> ComplexArray: """Compute the phasor components `(a, b, c)` from the symmetrical components `(0, +, -)`.""" v_012_array = np.asarray(v_012) orig_shape = v_012_array.shape @@ -107,7 +109,7 @@ def series_phasor_to_sym(s_abc: pd.Series) -> pd.Series: return s_012 -def calculate_voltages(potentials: np.ndarray, phases: str) -> np.ndarray: +def calculate_voltages(potentials: ComplexArray, phases: str) -> ComplexArray: """Calculate the voltages between phases given the potentials of each phase. Args: diff --git a/roseau/load_flow/models/branches.py b/roseau/load_flow/models/branches.py index 08459e75..2aac6a0d 100644 --- a/roseau/load_flow/models/branches.py +++ b/roseau/load_flow/models/branches.py @@ -8,7 +8,7 @@ from roseau.load_flow.converters import calculate_voltages from roseau.load_flow.models.buses import Bus from roseau.load_flow.models.core import Element -from roseau.load_flow.typing import Id, JsonDict +from roseau.load_flow.typing import ComplexArray, Id, JsonDict from roseau.load_flow.units import Q_, ureg_wraps logger = logging.getLogger(__name__) @@ -66,7 +66,7 @@ def __init__( self.bus2 = bus2 self.geometry = geometry self._connect(bus1, bus2) - self._res_currents: Optional[tuple[np.ndarray, np.ndarray]] = None + self._res_currents: Optional[tuple[ComplexArray, ComplexArray]] = None def __repr__(self) -> str: s = f"{type(self).__name__}(id={self.id!r}, phases1={self.phases1!r}, phases2={self.phases2!r}" @@ -76,16 +76,16 @@ def __repr__(self) -> str: s += ")" return s - def _res_currents_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarray]: + def _res_currents_getter(self, warning: bool) -> tuple[ComplexArray, ComplexArray]: return self._res_getter(value=self._res_currents, warning=warning) @property @ureg_wraps(("A", "A"), (None,), strict=False) - def res_currents(self) -> tuple[Q_[np.ndarray], Q_[np.ndarray]]: + def res_currents(self) -> tuple[Q_[ComplexArray], Q_[ComplexArray]]: """The load flow result of the branch currents (A).""" return self._res_currents_getter(warning=True) - def _res_powers_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarray]: + def _res_powers_getter(self, warning: bool) -> tuple[ComplexArray, ComplexArray]: cur1, cur2 = self._res_currents_getter(warning) pot1, pot2 = self._res_potentials_getter(warning=False) # we warn on the previous line powers1 = pot1 * cur1.conj() @@ -94,28 +94,28 @@ def _res_powers_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarray]: @property @ureg_wraps(("VA", "VA"), (None,), strict=False) - def res_powers(self) -> tuple[Q_[np.ndarray], Q_[np.ndarray]]: + def res_powers(self) -> tuple[Q_[ComplexArray], Q_[ComplexArray]]: """The load flow result of the branch powers (VA).""" return self._res_powers_getter(warning=True) - def _res_potentials_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarray]: + def _res_potentials_getter(self, warning: bool) -> tuple[ComplexArray, ComplexArray]: pot1 = self.bus1._get_potentials_of(self.phases1, warning) pot2 = self.bus2._get_potentials_of(self.phases2, warning=False) # we warn on the previous line return pot1, pot2 @property @ureg_wraps(("V", "V"), (None,), strict=False) - def res_potentials(self) -> tuple[Q_[np.ndarray], Q_[np.ndarray]]: + def res_potentials(self) -> tuple[Q_[ComplexArray], Q_[ComplexArray]]: """The load flow result of the branch potentials (V).""" return self._res_potentials_getter(warning=True) - def _res_voltages_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarray]: + def _res_voltages_getter(self, warning: bool) -> tuple[ComplexArray, ComplexArray]: pot1, pot2 = self._res_potentials_getter(warning) return calculate_voltages(pot1, self.phases1), calculate_voltages(pot2, self.phases2) @property @ureg_wraps(("V", "V"), (None,), strict=False) - def res_voltages(self) -> tuple[Q_[np.ndarray], Q_[np.ndarray]]: + def res_voltages(self) -> tuple[Q_[ComplexArray], Q_[ComplexArray]]: """The load flow result of the branch voltages (V).""" return self._res_voltages_getter(warning=True) diff --git a/roseau/load_flow/models/buses.py b/roseau/load_flow/models/buses.py index 928ff1e1..10aad31b 100644 --- a/roseau/load_flow/models/buses.py +++ b/roseau/load_flow/models/buses.py @@ -10,7 +10,7 @@ from roseau.load_flow.converters import calculate_voltage_phases, calculate_voltages, phasor_to_sym from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode from roseau.load_flow.models.core import Element -from roseau.load_flow.typing import Id, JsonDict +from roseau.load_flow.typing import ComplexArray, Id, JsonDict from roseau.load_flow.units import Q_, ureg_wraps logger = logging.getLogger(__name__) @@ -79,7 +79,7 @@ def __init__( self.min_voltage = min_voltage self.max_voltage = max_voltage - self._res_potentials: Optional[np.ndarray] = None + self._res_potentials: Optional[ComplexArray] = None self._short_circuits: list[dict[str, Any]] = [] def __repr__(self) -> str: @@ -87,7 +87,7 @@ def __repr__(self) -> str: @property @ureg_wraps("V", (None,), strict=False) - def potentials(self) -> Q_[np.ndarray]: + def potentials(self) -> Q_[ComplexArray]: """The potentials of the bus (V).""" return self._potentials @@ -101,22 +101,22 @@ def potentials(self, value: Sequence[complex]) -> None: self._potentials = np.asarray(value, dtype=complex) self._invalidate_network_results() - def _res_potentials_getter(self, warning: bool) -> np.ndarray: + def _res_potentials_getter(self, warning: bool) -> ComplexArray: return self._res_getter(value=self._res_potentials, warning=warning) @property @ureg_wraps("V", (None,), strict=False) - def res_potentials(self) -> Q_[np.ndarray]: + def res_potentials(self) -> Q_[ComplexArray]: """The load flow result of the bus potentials (V).""" return self._res_potentials_getter(warning=True) - def _res_voltages_getter(self, warning: bool) -> np.ndarray: + def _res_voltages_getter(self, warning: bool) -> ComplexArray: potentials = np.asarray(self._res_potentials_getter(warning=warning)) return calculate_voltages(potentials, self.phases) @property @ureg_wraps("V", (None,), strict=False) - def res_voltages(self) -> Q_[np.ndarray]: + def res_voltages(self) -> Q_[ComplexArray]: """The load flow result of the bus voltages (V). If the bus has a neutral, the voltages are phase-neutral voltages for existing phases in @@ -130,7 +130,7 @@ def voltage_phases(self) -> list[str]: """The phases of the voltages.""" return calculate_voltage_phases(self.phases) - def _get_potentials_of(self, phases: str, warning: bool) -> np.ndarray: + def _get_potentials_of(self, phases: str, warning: bool) -> ComplexArray: """Get the potentials of the given phases.""" potentials = self._res_potentials_getter(warning) return np.array([potentials[self.phases.index(p)] for p in phases]) diff --git a/roseau/load_flow/models/lines/lines.py b/roseau/load_flow/models/lines/lines.py index 606a55c5..2c7aee71 100644 --- a/roseau/load_flow/models/lines/lines.py +++ b/roseau/load_flow/models/lines/lines.py @@ -12,7 +12,7 @@ from roseau.load_flow.models.grounds import Ground from roseau.load_flow.models.lines.parameters import LineParameters from roseau.load_flow.models.sources import VoltageSource -from roseau.load_flow.typing import Id, JsonDict +from roseau.load_flow.typing import ComplexArray, Id, JsonDict from roseau.load_flow.units import Q_, ureg_wraps logger = logging.getLogger(__name__) @@ -275,13 +275,13 @@ def parameters(self, value: LineParameters) -> None: @property @ureg_wraps("ohm", (None,), strict=False) - def z_line(self) -> Q_[np.ndarray]: + def z_line(self) -> Q_[ComplexArray]: """Impedance of the line in Ohm""" return self.parameters._z_line * self._length @property @ureg_wraps("S", (None,), strict=False) - def y_shunt(self) -> Q_[np.ndarray]: + def y_shunt(self) -> Q_[ComplexArray]: """Shunt admittance of the line in Siemens""" return self.parameters._y_shunt * self._length @@ -296,33 +296,33 @@ def max_current(self) -> Optional[Q_[float]]: def with_shunt(self) -> bool: return self.parameters.with_shunt - def _res_series_values_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarray]: + def _res_series_values_getter(self, warning: bool) -> tuple[ComplexArray, ComplexArray]: pot1, pot2 = self._res_potentials_getter(warning) # V du_line = pot1 - pot2 i_line = np.linalg.inv(self.z_line.m_as("ohm")) @ du_line # Zₗ x Iₗ = ΔU -> I = Zₗ⁻¹ x ΔU return du_line, i_line - def _res_series_currents_getter(self, warning: bool) -> np.ndarray: + def _res_series_currents_getter(self, warning: bool) -> ComplexArray: _, i_line = self._res_series_values_getter(warning) return i_line @property @ureg_wraps("A", (None,), strict=False) - def res_series_currents(self) -> Q_[np.ndarray]: + def res_series_currents(self) -> Q_[ComplexArray]: """Get the current in the series elements of the line (A).""" return self._res_series_currents_getter(warning=True) - def _res_series_power_losses_getter(self, warning: bool) -> np.ndarray: + def _res_series_power_losses_getter(self, warning: bool) -> ComplexArray: du_line, i_line = self._res_series_values_getter(warning) return du_line * i_line.conj() # Sₗ = ΔU.Iₗ* @property @ureg_wraps("VA", (None,), strict=False) - def res_series_power_losses(self) -> Q_[np.ndarray]: + def res_series_power_losses(self) -> Q_[ComplexArray]: """Get the power losses in the series elements of the line (VA).""" return self._res_series_power_losses_getter(warning=True) - def _res_shunt_values_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: + def _res_shunt_values_getter(self, warning: bool) -> tuple[ComplexArray, ComplexArray, ComplexArray, ComplexArray]: assert self.with_shunt, "This method only works when there is a shunt" assert self.ground is not None pot1, pot2 = self._res_potentials_getter(warning) @@ -333,7 +333,7 @@ def _res_shunt_values_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarra i2_shunt = (y_shunt @ pot2 - yg * vg) / 2 return pot1, pot2, i1_shunt, i2_shunt - def _res_shunt_currents_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarray]: + def _res_shunt_currents_getter(self, warning: bool) -> tuple[ComplexArray, ComplexArray]: if not self.with_shunt: zeros = np.zeros(len(self.phases), dtype=complex) return zeros[:], zeros[:] @@ -342,11 +342,11 @@ def _res_shunt_currents_getter(self, warning: bool) -> tuple[np.ndarray, np.ndar @property @ureg_wraps(("A", "A"), (None,), strict=False) - def res_shunt_currents(self) -> tuple[Q_[np.ndarray], Q_[np.ndarray]]: + def res_shunt_currents(self) -> tuple[Q_[ComplexArray], Q_[ComplexArray]]: """Get the currents in the shunt elements of the line (A).""" return self._res_shunt_currents_getter(warning=True) - def _res_shunt_power_losses_getter(self, warning: bool) -> np.ndarray: + def _res_shunt_power_losses_getter(self, warning: bool) -> ComplexArray: if not self.with_shunt: return np.zeros(len(self.phases), dtype=complex) pot1, pot2, cur1, cur2 = self._res_shunt_values_getter(warning) @@ -354,18 +354,18 @@ def _res_shunt_power_losses_getter(self, warning: bool) -> np.ndarray: @property @ureg_wraps("VA", (None,), strict=False) - def res_shunt_power_losses(self) -> Q_[np.ndarray]: + def res_shunt_power_losses(self) -> Q_[ComplexArray]: """Get the power losses in the shunt elements of the line (VA).""" return self._res_shunt_power_losses_getter(warning=True) - def _res_power_losses_getter(self, warning: bool) -> np.ndarray: + def _res_power_losses_getter(self, warning: bool) -> ComplexArray: series_losses = self._res_series_power_losses_getter(warning) shunt_losses = self._res_shunt_power_losses_getter(warning=False) # we warn on the previous line return series_losses + shunt_losses @property @ureg_wraps("VA", (None,), strict=False) - def res_power_losses(self) -> Q_[np.ndarray]: + def res_power_losses(self) -> Q_[ComplexArray]: """Get the power losses in the line (VA).""" return self._res_power_losses_getter(warning=True) diff --git a/roseau/load_flow/models/lines/parameters.py b/roseau/load_flow/models/lines/parameters.py index fad31cad..a6294b2f 100644 --- a/roseau/load_flow/models/lines/parameters.py +++ b/roseau/load_flow/models/lines/parameters.py @@ -8,7 +8,7 @@ from typing_extensions import Self from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode -from roseau.load_flow.typing import Id, JsonDict +from roseau.load_flow.typing import ComplexArray, Id, JsonDict from roseau.load_flow.units import Q_, ureg_wraps from roseau.load_flow.utils import ( CX, @@ -41,7 +41,11 @@ class LineParameters(Identifiable, JsonMixin): @ureg_wraps(None, (None, None, "ohm/km", "S/km", "A"), strict=False) def __init__( - self, id: Id, z_line: np.ndarray, y_shunt: Optional[np.ndarray] = None, max_current: Optional[float] = None + self, + id: Id, + z_line: ComplexArray, + y_shunt: Optional[ComplexArray] = None, + max_current: Optional[float] = None, ) -> None: """LineParameters constructor. @@ -89,12 +93,12 @@ def __eq__(self, other: object) -> bool: @property @ureg_wraps("ohm/km", (None,), strict=False) - def z_line(self) -> Q_[np.ndarray]: + def z_line(self) -> Q_[ComplexArray]: return self._z_line @property @ureg_wraps("S/km", (None,), strict=False) - def y_shunt(self) -> Q_[np.ndarray]: + def y_shunt(self) -> Q_[ComplexArray]: return self._y_shunt @property @@ -183,7 +187,7 @@ def _sym_to_zy( xpn: Optional[float] = None, bn: Optional[float] = None, bpn: Optional[float] = None, - ) -> tuple[np.ndarray, np.ndarray]: + ) -> tuple[ComplexArray, ComplexArray]: """Create impedance and admittance matrix from a symmetrical model. Args: @@ -364,7 +368,7 @@ def _geometry_to_zy( section_neutral: float, height: float, external_diameter: float, - ) -> tuple[np.ndarray, np.ndarray]: + ) -> tuple[ComplexArray, ComplexArray]: """Create impedance and admittance matrix using a geometric model. Args: diff --git a/roseau/load_flow/models/loads/flexible_parameters.py b/roseau/load_flow/models/loads/flexible_parameters.py index 668886bc..b92ffc3f 100644 --- a/roseau/load_flow/models/loads/flexible_parameters.py +++ b/roseau/load_flow/models/loads/flexible_parameters.py @@ -3,10 +3,11 @@ from typing import TYPE_CHECKING, NoReturn, Optional import numpy as np +from numpy.typing import NDArray from typing_extensions import Self from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode -from roseau.load_flow.typing import Authentication, ControlType, JsonDict, ProjectionType +from roseau.load_flow.typing import Authentication, ComplexArray, ControlType, JsonDict, ProjectionType from roseau.load_flow.units import Q_, ureg_wraps from roseau.load_flow.utils import JsonMixin, _optional_deps @@ -963,8 +964,12 @@ def results_from_dict(self, data: JsonDict) -> NoReturn: # @ureg_wraps("VA", (None, None, "V", "VA", None), strict=False) def compute_powers( - self, auth: Authentication, voltages: np.ndarray[float], power: complex, solve_kwargs: Optional[JsonDict] = None - ) -> Q_[np.ndarray[complex]]: + self, + auth: Authentication, + voltages: NDArray[np.float_], + power: complex, + solve_kwargs: Optional[JsonDict] = None, + ) -> Q_[ComplexArray]: """Compute the flexible powers for different voltages (norms) Args: @@ -986,8 +991,8 @@ def compute_powers( return self._compute_powers(auth=auth, voltages=voltages, power=power, solve_kwargs=solve_kwargs) def _compute_powers( - self, auth: Authentication, voltages: np.ndarray[float], power: complex, solve_kwargs: Optional[JsonDict] - ) -> np.ndarray[complex]: + self, auth: Authentication, voltages: NDArray[np.float_], power: complex, solve_kwargs: Optional[JsonDict] + ) -> ComplexArray: from roseau.load_flow import Bus, ElectricalNetwork, PotentialRef, PowerLoad, VoltageSource # Format the input @@ -1016,13 +1021,13 @@ def _compute_powers( def plot_pq( self, auth: Authentication, - voltages: np.ndarray[float], + voltages: NDArray[np.float_], power: complex, ax: Optional["Axes"] = None, solve_kwargs: Optional[JsonDict] = None, - voltages_labels_mask: Optional[np.ndarray[bool]] = None, - res_flexible_powers: Optional[np.ndarray[complex]] = None, - ) -> tuple["Axes", np.ndarray[complex]]: + voltages_labels_mask: Optional[NDArray[np.bool_]] = None, + res_flexible_powers: Optional[ComplexArray] = None, + ) -> tuple["Axes", ComplexArray]: """Plot the "trajectory" of the flexible powers (in the (P, Q) plane) for the provided voltages and theoretical power. @@ -1129,12 +1134,12 @@ def plot_pq( def plot_control_p( self, auth: Authentication, - voltages: np.ndarray[float], + voltages: NDArray[np.float_], power: complex, ax: Optional["Axes"] = None, solve_kwargs: Optional[JsonDict] = None, - res_flexible_powers: Optional[np.ndarray[complex]] = None, - ) -> tuple["Axes", np.ndarray[complex]]: + res_flexible_powers: Optional[ComplexArray] = None, + ) -> tuple["Axes", ComplexArray]: """Plot the flexible active power consumed (or produced) for the provided voltages and theoretical power. Args: @@ -1195,12 +1200,12 @@ def plot_control_p( def plot_control_q( self, auth: Authentication, - voltages: np.ndarray[float], + voltages: NDArray[np.float_], power: complex, ax: Optional["Axes"] = None, solve_kwargs: Optional[JsonDict] = None, - res_flexible_powers: Optional[np.ndarray[complex]] = None, - ) -> tuple["Axes", np.ndarray[complex]]: + res_flexible_powers: Optional[ComplexArray] = None, + ) -> tuple["Axes", ComplexArray]: """Plot the flexible reactive power consumed (or produced) for the provided voltages and theoretical power. Args: @@ -1263,7 +1268,7 @@ def plot_control_q( @staticmethod def _theoretical_control_data( control: Control, v_min: float, v_max: float, power: float, s_max: float - ) -> tuple[np.ndarray[float], np.ndarray[float], np.ndarray[object]]: + ) -> tuple[NDArray[np.float_], NDArray[np.float_], NDArray[np.object_]]: """Helper to get data for the different plots of the class. It provides the theoretical control curve abscissas and ordinates values. It also provides ticks for the abscissa axis. diff --git a/roseau/load_flow/models/loads/loads.py b/roseau/load_flow/models/loads/loads.py index f4378243..fbe0e582 100644 --- a/roseau/load_flow/models/loads/loads.py +++ b/roseau/load_flow/models/loads/loads.py @@ -10,7 +10,7 @@ from roseau.load_flow.models.buses import Bus from roseau.load_flow.models.core import Element from roseau.load_flow.models.loads.flexible_parameters import FlexibleParameter -from roseau.load_flow.typing import Id, JsonDict +from roseau.load_flow.typing import ComplexArray, Id, JsonDict from roseau.load_flow.units import Q_, ureg_wraps logger = logging.getLogger(__name__) @@ -79,7 +79,7 @@ def __init__(self, id: Id, bus: Bus, *, phases: Optional[str] = None, **kwargs: self._size = len(set(phases) - {"n"}) # Results - self._res_currents: Optional[np.ndarray] = None + self._res_currents: Optional[ComplexArray] = None def __repr__(self) -> str: bus_id = self.bus.id if self.bus is not None else None @@ -95,16 +95,16 @@ def voltage_phases(self) -> list[str]: """The phases of the load voltages.""" return calculate_voltage_phases(self.phases) - def _res_currents_getter(self, warning: bool) -> np.ndarray: + def _res_currents_getter(self, warning: bool) -> ComplexArray: return self._res_getter(value=self._res_currents, warning=warning) @property @ureg_wraps("A", (None,), strict=False) - def res_currents(self) -> Q_[np.ndarray]: + def res_currents(self) -> Q_[ComplexArray]: """The load flow result of the load currents (A).""" return self._res_currents_getter(warning=True) - def _validate_value(self, value: Sequence[complex]) -> np.ndarray: + def _validate_value(self, value: Sequence[complex]) -> ComplexArray: if len(value) != self._size: msg = f"Incorrect number of {self._type}s: {len(value)} instead of {self._size}" logger.error(msg) @@ -118,34 +118,34 @@ def _validate_value(self, value: Sequence[complex]) -> np.ndarray: raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_Z_VALUE) return np.asarray(value, dtype=complex) - def _res_potentials_getter(self, warning: bool) -> np.ndarray: + def _res_potentials_getter(self, warning: bool) -> ComplexArray: self._raise_disconnected_error() return self.bus._get_potentials_of(self.phases, warning) @property @ureg_wraps("V", (None,), strict=False) - def res_potentials(self) -> Q_[np.ndarray]: + def res_potentials(self) -> Q_[ComplexArray]: """The load flow result of the load potentials (V).""" return self._res_potentials_getter(warning=True) - def _res_voltages_getter(self, warning: bool) -> np.ndarray: + def _res_voltages_getter(self, warning: bool) -> ComplexArray: potentials = self._res_potentials_getter(warning) return calculate_voltages(potentials, self.phases) @property @ureg_wraps("V", (None,), strict=False) - def res_voltages(self) -> Q_[np.ndarray]: + def res_voltages(self) -> Q_[ComplexArray]: """The load flow result of the load voltages (V).""" return self._res_voltages_getter(warning=True) - def _res_powers_getter(self, warning: bool) -> np.ndarray: + def _res_powers_getter(self, warning: bool) -> ComplexArray: curs = self._res_currents_getter(warning) pots = self._res_potentials_getter(warning=False) # we warn on the previous line return pots * curs.conj() @property @ureg_wraps("VA", (None,), strict=False) - def res_powers(self) -> Q_[np.ndarray]: + def res_powers(self) -> Q_[ComplexArray]: """The load flow result of the load powers (VA).""" return self._res_powers_getter(warning=True) @@ -254,7 +254,7 @@ def __init__( self._flexible_params = flexible_params self.powers = powers - self._res_flexible_powers: Optional[np.ndarray] = None + self._res_flexible_powers: Optional[ComplexArray] = None @property def flexible_params(self) -> Optional[list[FlexibleParameter]]: @@ -266,7 +266,7 @@ def is_flexible(self) -> bool: @property @ureg_wraps("VA", (None,), strict=False) - def powers(self) -> Q_[np.ndarray]: + def powers(self) -> Q_[ComplexArray]: """The powers of the load (VA).""" return self._powers @@ -305,12 +305,12 @@ def powers(self, value: Sequence[complex]) -> None: self._powers = value self._invalidate_network_results() - def _res_flexible_powers_getter(self, warning: bool) -> np.ndarray: + def _res_flexible_powers_getter(self, warning: bool) -> ComplexArray: return self._res_getter(value=self._res_flexible_powers, warning=warning) @property @ureg_wraps("VA", (None,), strict=False) - def res_flexible_powers(self) -> Q_[np.ndarray]: + def res_flexible_powers(self) -> Q_[ComplexArray]: """The load flow result of the load flexible powers (VA).""" return self._res_flexible_powers_getter(warning=True) @@ -375,7 +375,7 @@ def __init__( @property @ureg_wraps("A", (None,), strict=False) - def currents(self) -> Q_[np.ndarray]: + def currents(self) -> Q_[ComplexArray]: """The currents of the load (Amps).""" return self._currents @@ -426,7 +426,7 @@ def __init__( @property @ureg_wraps("ohm", (None,), strict=False) - def impedances(self) -> Q_[np.ndarray]: + def impedances(self) -> Q_[ComplexArray]: """The impedances of the load (Ohms).""" return self._impedances diff --git a/roseau/load_flow/models/sources.py b/roseau/load_flow/models/sources.py index 8f32e440..27492b13 100644 --- a/roseau/load_flow/models/sources.py +++ b/roseau/load_flow/models/sources.py @@ -9,7 +9,7 @@ from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode from roseau.load_flow.models.buses import Bus from roseau.load_flow.models.core import Element -from roseau.load_flow.typing import Id, JsonDict +from roseau.load_flow.typing import ComplexArray, Id, JsonDict from roseau.load_flow.units import Q_, ureg_wraps logger = logging.getLogger(__name__) @@ -74,7 +74,7 @@ def __init__( self.voltages = voltages # Results - self._res_currents: Optional[np.ndarray] = None + self._res_currents: Optional[ComplexArray] = None def __repr__(self) -> str: bus_id = self.bus.id if self.bus is not None else None @@ -85,7 +85,7 @@ def __repr__(self) -> str: @property @ureg_wraps("V", (None,), strict=False) - def voltages(self) -> Q_[np.ndarray]: + def voltages(self) -> Q_[ComplexArray]: """The voltages of the source (V).""" return self._voltages @@ -104,33 +104,33 @@ def voltage_phases(self) -> list[str]: """The phases of the source voltages.""" return calculate_voltage_phases(self.phases) - def _res_currents_getter(self, warning: bool) -> np.ndarray: + def _res_currents_getter(self, warning: bool) -> ComplexArray: return self._res_getter(value=self._res_currents, warning=warning) @property @ureg_wraps("A", (None,), strict=False) - def res_currents(self) -> Q_[np.ndarray]: + def res_currents(self) -> Q_[ComplexArray]: """The load flow result of the source currents (A).""" return self._res_currents_getter(warning=True) - def _res_potentials_getter(self, warning: bool) -> np.ndarray: + def _res_potentials_getter(self, warning: bool) -> ComplexArray: self._raise_disconnected_error() return self.bus._get_potentials_of(self.phases, warning) @property @ureg_wraps("V", (None,), strict=False) - def res_potentials(self) -> Q_[np.ndarray]: + def res_potentials(self) -> Q_[ComplexArray]: """The load flow result of the source potentials (V).""" return self._res_potentials_getter(warning=True) - def _res_powers_getter(self, warning: bool) -> np.ndarray: + def _res_powers_getter(self, warning: bool) -> ComplexArray: curs = self._res_currents_getter(warning) pots = self._res_potentials_getter(warning=False) # we warn on the previous line return pots * curs.conj() @property @ureg_wraps("VA", (None,), strict=False) - def res_powers(self) -> Q_[np.ndarray]: + def res_powers(self) -> Q_[ComplexArray]: """The load flow result of the source powers (VA).""" return self._res_powers_getter(warning=True) diff --git a/roseau/load_flow/typing.py b/roseau/load_flow/typing.py index 1e3f97a3..31a5cb33 100644 --- a/roseau/load_flow/typing.py +++ b/roseau/load_flow/typing.py @@ -28,10 +28,16 @@ .. class:: Authentication Valid authentication types. + +.. class:: ComplexArray + + A numpy array of complex numbers. """ import os from typing import Any, Literal, Union +import numpy as np +from numpy.typing import NDArray from requests.auth import HTTPBasicAuth from typing_extensions import TypeAlias @@ -42,6 +48,16 @@ ProjectionType: TypeAlias = Literal["euclidean", "keep_p", "keep_q"] Solver: TypeAlias = Literal["newton", "newton_goldstein"] Authentication: TypeAlias = Union[tuple[str, str], HTTPBasicAuth] - - -__all__ = ["Id", "JsonDict", "StrPath", "ControlType", "ProjectionType", "Solver", "Authentication"] +ComplexArray: TypeAlias = NDArray[np.complex_] + + +__all__ = [ + "Id", + "JsonDict", + "StrPath", + "ControlType", + "ProjectionType", + "Solver", + "Authentication", + "ComplexArray", +]