Skip to content
Merged
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}`128` Add the properties `z_line`, `y_shunt` and `with_shunt` to the `Line` class.
- {gh-pr}`125` Speed-up build of conda workflow using mamba.

## Version 0.5.0
Expand Down
22 changes: 22 additions & 0 deletions doc/models/Line/ShuntLine.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,28 @@ load = PowerLoad(
id="load", bus=bus2, powers=Q_(np.array([5.0, 2.5, 0]) * (1 - 0.3j), "kVA")
)

# The impedance matrix (in Ohm) can be accessed from the line instance
line.z_line
# array(
# [[0.3+0.35j, 0. +0.25j, 0. +0.25j, 0. +0.25j],
# [0. +0.25j, 0.3+0.35j, 0. +0.25j, 0. +0.25j],
# [0. +0.25j, 0. +0.25j, 0.3+0.35j, 0. +0.25j],
# [0. +0.25j, 0. +0.25j, 0. +0.25j, 0.3+0.35j]]
# ) <Unit('ohm')>

# The shunt admittance matrix (in Siemens) can be accessed from the line instance
line.y_shunt
# array(
# [[2.e-05+4.75e-04j, 0.e+00-6.80e-05j, 0.e+00-1.00e-05j, 0.e+00-6.80e-05j],
# [0.e+00-6.80e-05j, 2.e-05+4.75e-04j, 0.e+00-6.80e-05j, 0.e+00-1.00e-05j],
# [0.e+00-1.00e-05j, 0.e+00-6.80e-05j, 2.e-05+4.75e-04j, 0.e+00-6.80e-05j],
# [0.e+00-6.80e-05j, 0.e+00-1.00e-05j, 0.e+00-6.80e-05j, 2.e-05+4.75e-04j]]
# ) <Unit('siemens')>

# For a shunt line, the property `with_shunt` is True
line.with_shunt
# True

# Create a network and solve a load flow
en = ElectricalNetwork.from_element(bus1)
auth = ("username", "password")
Expand Down
21 changes: 21 additions & 0 deletions doc/models/Line/SimplifiedLine.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,27 @@ load = PowerLoad(
id="load", bus=bus2, powers=Q_(np.array([5.0, 2.5, 0]) * (1 - 0.3j), "kVA")
)

# The impedance matrix (in Ohm) can be accessed from the line instance
line.z_line
# array(
# [[0.35+0.j, 0. +0.j, 0. +0.j, 0. +0.j],
# [0. +0.j, 0.35+0.j, 0. +0.j, 0. +0.j],
# [0. +0.j, 0. +0.j, 0.35+0.j, 0. +0.j],
# [0. +0.j, 0. +0.j, 0. +0.j, 0.35+0.j]]
# ) <Unit('ohm')>

# For a simplified line, the property `with_shunt` is False and the `y_shunt` matrix is zero
line.with_shunt
# False

line.y_shunt
# array(
# [[0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
# [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
# [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
# [0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]]
# ) <Unit('siemens')>

# Create a network and solve a load flow
en = ElectricalNetwork.from_element(bus1)
auth = ("username", "password")
Expand Down
9 changes: 7 additions & 2 deletions doc/models/Line/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ $\underline{Y}$.

## Matrices definition

Before diving into the different line models, lets define the series impedance matrix $Z$, and the
shunt admittance matrix $Y$ used to model the lines.
Before diving into the different line models, lets define the series impedance matrix $\underline{Z}$, and the
shunt admittance matrix $\underline{Y}$ used to model the lines.

### Series impedance matrix

Expand Down Expand Up @@ -158,6 +158,11 @@ shunt_line_parameters = LineParameters(
)
```

```{tip}
The `Line` instance itself has the `z_line` and `y_shunt` properties. They retrieve the line impedance in $\Omega$
and the line shunt admittance in Siemens (taking into account the length of the line).
```

There are several alternative constructors for `LineParameters` objects. The description of them can be found in the
dedicated [Line parameters page](Parameters.md).

Expand Down
36 changes: 25 additions & 11 deletions roseau/load_flow/models/lines/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def __init__(
self._initialized = True

# Handle the ground
if self.ground is not None and not self.parameters.with_shunt:
if self.ground is not None and not self.with_shunt:
warnings.warn(
message=(
f"The ground element must not be provided for line {self.id!r} as it does not have a shunt "
Expand All @@ -227,7 +227,7 @@ def __init__(
stacklevel=2,
)
self.ground = None
elif self.parameters.with_shunt:
elif self.with_shunt:
# Connect the ground
self._connect(self.ground)

Expand Down Expand Up @@ -260,7 +260,7 @@ def parameters(self, value: LineParameters) -> None:
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_Z_LINE_SHAPE)

if value.with_shunt:
if self._initialized and not self.parameters.with_shunt:
if self._initialized and not self.with_shunt:
msg = "Cannot set line parameters with a shunt to a line that does not have shunt components."
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_LINE_MODEL)
Expand All @@ -273,18 +273,33 @@ def parameters(self, value: LineParameters) -> None:
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_LINE_TYPE)
else:
if self._initialized and self.parameters.with_shunt:
if self._initialized and self.with_shunt:
msg = "Cannot set line parameters without a shunt to a line that has shunt components."
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_LINE_MODEL)
self._parameters = value
self._invalidate_network_results()

@property
@ureg_wraps("ohm", (None,), strict=False)
def z_line(self) -> Q_[np.ndarray]:
"""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]:
"""Shunt admittance of the line in Siemens"""
return self.parameters._y_shunt * self._length

@property
def with_shunt(self) -> bool:
return self.parameters.with_shunt

def _res_series_values_getter(self, warning: bool) -> tuple[np.ndarray, np.ndarray]:
pot1, pot2 = self._res_potentials_getter(warning) # V
du_line = pot1 - pot2
z_line = self.parameters.z_line * self.length
i_line = np.linalg.inv(z_line.m_as("ohm")) @ du_line # Zₗ x Iₗ = ΔU -> I = Zₗ⁻¹ x ΔU
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:
Expand All @@ -308,19 +323,18 @@ def res_series_power_losses(self) -> Q_[np.ndarray]:
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]:
assert self.parameters.with_shunt, "this method only works when there is a shunt"
y_shunt = self.parameters.y_shunt
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)
vg = self.ground.res_potential.m_as("V")
y_shunt = (y_shunt * self.length).m_as("S")
y_shunt = self.y_shunt.m_as("S")
yg = y_shunt.sum(axis=1) # y_ig = Y_ia + Y_ib + Y_ic + Y_in for i in {a, b, c, n}
i1_shunt = (y_shunt @ pot1 - yg * vg) / 2
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]:
if not self.parameters.with_shunt:
if not self.with_shunt:
zeros = np.zeros(len(self.phases), dtype=complex)
return zeros[:], zeros[:]
_, _, cur1, cur2 = self._res_shunt_values_getter(warning)
Expand All @@ -333,7 +347,7 @@ def res_shunt_currents(self) -> tuple[Q_[np.ndarray], Q_[np.ndarray]]:
return self._res_shunt_currents_getter(warning=True)

def _res_shunt_power_losses_getter(self, warning: bool) -> np.ndarray:
if not self.parameters.with_shunt:
if not self.with_shunt:
return np.zeros(len(self.phases), dtype=complex)
pot1, pot2, cur1, cur2 = self._res_shunt_values_getter(warning)
return pot1 * cur1.conj() + pot2 * cur2.conj()
Expand Down
38 changes: 36 additions & 2 deletions roseau/load_flow/models/tests/test_lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
import pytest
from pint import DimensionalityError

from roseau.load_flow import RoseauLoadFlowException, RoseauLoadFlowExceptionCode
from roseau.load_flow.models import Bus, Line, LineParameters
from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode
from roseau.load_flow.models import Bus, Ground, Line, LineParameters
from roseau.load_flow.units import Q_


Expand Down Expand Up @@ -61,3 +61,37 @@ def test_lines_units():
line = Line("line", bus1=bus1, bus2=bus2, parameters=lp, length=5)
with pytest.raises(DimensionalityError, match=r"Cannot convert from 'ampere' \(\[current\]\) to 'km'"):
line.length = Q_(6.5, "A")


def test_line_parameters_shortcut():
bus1 = Bus("bus1", phases="abcn")
bus2 = Bus("bus1", phases="abcn")

#
# Without shunt
#
lp = LineParameters("lp", z_line=np.eye(4, dtype=complex))

# Z
line = Line("line", bus1=bus1, bus2=bus2, parameters=lp, length=Q_(50, "m"))
assert np.allclose(line.z_line.m_as("ohm"), 0.05 * np.eye(4, dtype=complex))

# Y
assert not line.with_shunt
assert np.allclose(line.y_shunt.m_as("S"), np.zeros(shape=(4, 4), dtype=complex))

#
# With shunt
#
z_line = 0.01 * np.eye(4, dtype=complex)
y_shunt = 1e-5 * np.eye(4, dtype=complex)
lp = LineParameters("lp", z_line=z_line, y_shunt=y_shunt)

# Z
ground = Ground("ground")
line = Line("line", bus1=bus1, bus2=bus2, parameters=lp, length=Q_(50, "m"), ground=ground)
assert np.allclose(line.z_line.m_as("ohm"), 0.05 * z_line)

# Y
assert line.with_shunt
assert np.allclose(line.y_shunt.m_as("S"), 0.05 * y_shunt)
2 changes: 1 addition & 1 deletion roseau/load_flow/solvers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
from typing import Optional

from roseau.load_flow import RoseauLoadFlowException, RoseauLoadFlowExceptionCode
from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode
from roseau.load_flow.typing import JsonDict, Solver

logger = logging.getLogger(__name__)
Expand Down
2 changes: 1 addition & 1 deletion roseau/load_flow/tests/test_solvers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import pytest

from roseau.load_flow import RoseauLoadFlowException, RoseauLoadFlowExceptionCode
from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode
from roseau.load_flow.solvers import check_solver_params


Expand Down