Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

**In development**

- {gh-pr}`146` {gh-issue}`145` Fix handling of quantities in input sequences
- {gh-pr}`142` {gh-issue}`136` Add `Bus.res_voltage_unbalance()` method to get the Voltage Unbalance
Factor (VUF) as defined by the IEC standard IEC 61000-3-14.
- {gh-pr}`141` {gh-issue}`137` Add `ElectricalNetwork.to_graph()` to get a `networkx.Graph` object
Expand Down
3 changes: 1 addition & 2 deletions roseau/load_flow/models/buses.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,12 @@ def potentials(self) -> Q_[ComplexArray]:
return self._potentials

@potentials.setter
@ureg_wraps(None, (None, "V"), strict=False)
def potentials(self, value: Sequence[complex]) -> None:
if len(value) != len(self.phases):
msg = f"Incorrect number of potentials: {len(value)} instead of {len(self.phases)}"
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_POTENTIALS_SIZE)
self._potentials = np.asarray(value, dtype=complex)
self._potentials = np.array([Q_(v, "V").m for v in value], dtype=complex)
self._invalidate_network_results()

def _res_potentials_getter(self, warning: bool) -> ComplexArray:
Expand Down
7 changes: 3 additions & 4 deletions roseau/load_flow/models/loads/loads.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def __init__(self, id: Id, bus: Bus, *, phases: Optional[str] = None, **kwargs:
self.phases = phases
self.bus = bus
self._symbol = {"power": "S", "current": "I", "impedance": "Z"}[self._type]
self._unit = {"power": "VA", "current": "A", "impedance": "ohm"}[self._type]
if len(phases) == 2 and "n" not in phases:
# This is a delta load that has one element connected between two phases
self._size = 1
Expand Down Expand Up @@ -111,12 +112,13 @@ def _validate_value(self, value: Sequence[complex]) -> ComplexArray:
raise RoseauLoadFlowException(
msg=msg, code=RoseauLoadFlowExceptionCode.from_string(f"BAD_{self._symbol}_SIZE")
)
value = np.array([Q_(v, self._unit).m for v in value], dtype=complex)
# A load cannot have any zero impedance
if self._type == "impedance" and np.isclose(value, 0).any():
msg = f"An impedance of the load {self.id!r} is null"
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_Z_VALUE)
return np.asarray(value, dtype=complex)
return value

def _res_potentials_getter(self, warning: bool) -> ComplexArray:
self._raise_disconnected_error()
Expand Down Expand Up @@ -271,7 +273,6 @@ def powers(self) -> Q_[ComplexArray]:
return self._powers

@powers.setter
@ureg_wraps(None, (None, "VA"), strict=False)
def powers(self, value: Sequence[complex]) -> None:
value = self._validate_value(value)
if self.is_flexible:
Expand Down Expand Up @@ -380,7 +381,6 @@ def currents(self) -> Q_[ComplexArray]:
return self._currents

@currents.setter
@ureg_wraps(None, (None, "A"), strict=False)
def currents(self, value: Sequence[complex]) -> None:
self._currents = self._validate_value(value)
self._invalidate_network_results()
Expand Down Expand Up @@ -431,7 +431,6 @@ def impedances(self) -> Q_[ComplexArray]:
return self._impedances

@impedances.setter
@ureg_wraps(None, (None, "ohm"), strict=False)
def impedances(self, impedances: Sequence[complex]) -> None:
self._impedances = self._validate_value(impedances)
self._invalidate_network_results()
Expand Down
3 changes: 1 addition & 2 deletions roseau/load_flow/models/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,12 @@ def voltages(self) -> Q_[ComplexArray]:
return self._voltages

@voltages.setter
@ureg_wraps(None, (None, "V"), strict=False)
def voltages(self, voltages: Sequence[complex]) -> None:
if len(voltages) != self._size:
msg = f"Incorrect number of voltages: {len(voltages)} instead of {self._size}"
logger.error(msg)
raise RoseauLoadFlowException(msg, code=RoseauLoadFlowExceptionCode.BAD_VOLTAGES_SIZE)
self._voltages = np.asarray(voltages, dtype=complex)
self._voltages = np.array([Q_(v, "V").m for v in voltages], dtype=complex)
self._invalidate_network_results()

@property
Expand Down
26 changes: 26 additions & 0 deletions roseau/load_flow/models/tests/test_buses.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import numpy as np
import pandas as pd
import pytest
from pint.errors import DimensionalityError

from roseau.load_flow import (
Q_,
Expand All @@ -20,6 +21,31 @@
)


def test_bus_units():
# Good unit constructor
bus = Bus("bus", phases="abc", potentials=Q_([1, 1, 1], "kV"))
assert np.allclose(bus._potentials, [1000, 1000, 1000])

# Good unit setter
bus = Bus("bus", phases="abc", potentials=[100, 100, 100])
assert np.allclose(bus._potentials, [100, 100, 100])
bus.potentials = Q_([1, 1, 1], "kV")
assert np.allclose(bus._potentials, [1000, 1000, 1000])

# Units in a list
bus = Bus("bus", phases="abc", potentials=[Q_(1_000, "V"), Q_(1, "kV"), Q_(0.001, "MV")])
assert np.allclose(bus._potentials, [1000, 1000, 1000])

# Bad unit constructor
with pytest.raises(DimensionalityError, match=r"Cannot convert from 'ampere' \(\[current\]\) to 'volt'"):
Bus("bus", phases="abc", potentials=Q_([100, 100, 100], "A"))

# Bad unit setter
bus = Bus("bus", phases="abc", potentials=[100, 100, 100])
with pytest.raises(DimensionalityError, match=r"Cannot convert from 'ampere' \(\[current\]\) to 'volt'"):
bus.potentials = Q_([100, 100, 100], "A")


def test_bus_potentials_of_phases():
bus = Bus("bus", phases="abcn")
bus._res_potentials = [1, 2, 3, 4]
Expand Down
16 changes: 14 additions & 2 deletions roseau/load_flow/models/tests/test_loads.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,13 +354,25 @@ def test_loads_units():
load.powers = Q_([1, 1, 1], "kVA")
assert np.allclose(load._powers, [1000, 1000, 1000])

# Also works as a quantity array
load = PowerLoad("load", bus, powers=Q_(10, "kVA") * np.ones(3), phases="abcn")
assert np.allclose(load._powers, [10000, 10000, 10000])

# Units in a list
load = PowerLoad("load", bus, powers=[Q_(1_000, "VA"), Q_(1, "kVA"), Q_(0.001, "MVA")], phases="abcn")
assert np.allclose(load._powers, [1000, 1000, 1000])
load = CurrentLoad("load", bus, currents=[Q_(1_000, "A"), Q_(1, "kA"), Q_(0.001, "MA")], phases="abcn")
assert np.allclose(load._currents, [1000, 1000, 1000])
load = ImpedanceLoad("load", bus, impedances=[Q_(1_000, "ohm"), Q_(1, "kohm"), Q_(0.001, "Mohm")], phases="abcn")
assert np.allclose(load._impedances, [1000, 1000, 1000])

# Bad unit constructor
with pytest.raises(DimensionalityError, match=r"Cannot convert from 'ampere' \(\[current\]\) to 'VA'"):
with pytest.raises(DimensionalityError, match=r"Cannot convert from 'ampere' \(\[current\]\) to 'volt_ampere'"):
PowerLoad("load", bus, powers=Q_([100, 100, 100], "A"), phases="abcn")

# Bad unit setter
load = PowerLoad("load", bus, powers=[100, 100, 100], phases="abcn")
with pytest.raises(DimensionalityError, match=r"Cannot convert from 'ampere' \(\[current\]\) to 'VA'"):
with pytest.raises(DimensionalityError, match=r"Cannot convert from 'ampere' \(\[current\]\) to 'volt_ampere'"):
load.powers = Q_([100, 100, 100], "A")


Expand Down
32 changes: 32 additions & 0 deletions roseau/load_flow/models/tests/test_sources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import numpy as np
import pytest
from pint.errors import DimensionalityError

from roseau.load_flow import Q_, Bus, VoltageSource


def test_source_units():
bus = Bus("bus", phases="abcn")

# Good unit constructor
vs = VoltageSource("vs", bus, voltages=Q_([1, 1, 1], "kV"), phases="abcn")
assert np.allclose(vs._voltages, [1000, 1000, 1000])

# Good unit setter
vs = VoltageSource("vs", bus, voltages=[100, 100, 100], phases="abcn")
assert np.allclose(vs._voltages, [100, 100, 100])
vs.voltages = Q_([1, 1, 1], "kV")
assert np.allclose(vs._voltages, [1000, 1000, 1000])

# Units in a list
vs = VoltageSource("vs", bus, voltages=[Q_(1_000, "V"), Q_(1, "kV"), Q_(0.001, "MV")], phases="abcn")
assert np.allclose(vs._voltages, [1000, 1000, 1000])

# Bad unit constructor
with pytest.raises(DimensionalityError, match=r"Cannot convert from 'ampere' \(\[current\]\) to 'volt'"):
VoltageSource("vs", bus, voltages=Q_([100, 100, 100], "A"), phases="abcn")

# Bad unit setter
vs = VoltageSource("vs", bus, voltages=[100, 100, 100], phases="abcn")
with pytest.raises(DimensionalityError, match=r"Cannot convert from 'ampere' \(\[current\]\) to 'volt'"):
vs.voltages = Q_([100, 100, 100], "A")