Skip to content

Commit

Permalink
Merge pull request #5 from RoseauTechnologies/fix-loading
Browse files Browse the repository at this point in the history
Fix loading calculation of lines and transformers
  • Loading branch information
benoit9126 authored Dec 2, 2024
2 parents d97bbee + 4b8ac84 commit cce60b1
Show file tree
Hide file tree
Showing 10 changed files with 51 additions and 297 deletions.
7 changes: 3 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ dependencies = [
"pyproj>=3.3.0",
"certifi>=2023.5.7",
"platformdirs>=4.0.0",
"roseau-load-flow-engine>=0.16.0a",
"roseau-load-flow>=0.10.0",
"roseau-load-flow-engine==0.16.0",
"roseau-load-flow==0.11.0",
]

[project.urls]
Expand All @@ -62,7 +62,6 @@ dev = [

[tool.uv]
managed = true
override-dependencies = ["roseau-load-flow"] # TODO Temporary

[build-system]
requires = ["hatchling"]
Expand All @@ -81,7 +80,7 @@ show-fixes = true
namespace-packages = ["roseau"]

[tool.ruff.lint]
select = ["E", "F", "C90", "W", "B", "UP", "I", "RUF100", "TID", "SIM", "PT", "PIE", "N", "C4", "NPY", "T10", "PTH"]
select = ["E", "F", "C90", "W", "B", "UP", "I", "RUF100", "TID", "SIM", "PT", "PIE", "N", "C4", "NPY", "T10", "PTH", "T201"]
unfixable = ["B"]
ignore = ["E501", "B024", "N818", "UP038", "PTH123"]
flake8-tidy-imports.ban-relative-imports = "all"
Expand Down
1 change: 0 additions & 1 deletion roseau/load_flow_single/models/buses.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ def nominal_voltage(self, value: float | Q_[float] | None) -> None:
value = None
if value is None:
if self._max_voltage_level is not None or self._min_voltage_level is not None:
print("hey")
warnings.warn(
message=(
f"The nominal voltage of the bus {self.id!r} is required to use `min_voltage_level` and "
Expand Down
6 changes: 1 addition & 5 deletions roseau/load_flow_single/models/line_parameters.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import logging
import re
from pathlib import Path
from typing import Literal, NoReturn, TypeAlias, TypeVar
from typing import Literal, NoReturn

import numpy as np
import pandas as pd
from typing_extensions import Self

from roseau.load_flow._compat import StrEnum
from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode
from roseau.load_flow.models import LineParameters as MultiLineParameters
from roseau.load_flow.typing import Complex, Float, Id, JsonDict
Expand All @@ -17,9 +16,6 @@
logger = logging.getLogger(__name__)


_StrEnumType: TypeAlias = TypeVar("_StrEnumType", bound=StrEnum)


class LineParameters(Identifiable, JsonMixin, CatalogueMixin[pd.DataFrame]):
"""Parameters that define electrical models of lines."""

Expand Down
20 changes: 7 additions & 13 deletions roseau/load_flow_single/models/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,27 +260,21 @@ def res_power_losses(self) -> Q_[Complex]:

@property
def res_loading(self) -> Q_[float] | None:
"""Get the loading of the line (unitless)."""
amp = self._parameters._ampacity
if amp is None:
"""The loading of the line (unitless) if ``self.parameters.ampacity`` is set, else ``None``."""
if (amp := self._parameters._ampacity) is None:
return None
current1, current2 = self._res_currents_getter(warning=True)
i_max = amp * self._max_loading
return Q_(max(abs(current1), abs(current2)) / i_max, "")
return Q_(max(abs(current1), abs(current2)) / amp, "")

@property
def res_violated(self) -> bool | None:
"""Whether the line current exceeds the maximal current of the line (computed with the parameters' ampacities
and the maximal loading of the line itself).
"""Whether the line current loading exceeds its maximal loading.
Returns ``None`` if the ampacities or the `max_loading` is not set are not set.
Returns ``None`` if the ``self.parameters.ampacity`` is not set.
"""
amp = self._parameters._ampacity
if amp is None:
if (loading := self.res_loading) is None:
return None
current1, current2 = self._res_currents_getter(warning=True)
i_max = amp * self._max_loading
return abs(current1) > i_max or abs(current2) > i_max
return bool(loading.m > self._max_loading)

#
# Json Mixin interface
Expand Down
6 changes: 3 additions & 3 deletions roseau/load_flow_single/models/tests/test_lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ def test_res_violated():
lp = LineParameters(id="lp", z_line=1.0)
line = Line(id="line", bus1=bus1, bus2=bus2, parameters=lp, length=Q_(50, "m"))

bus1._res_potentials = 230
bus2._res_potentials = 225
bus1._res_potential = 230
bus2._res_potential = 225
line._res_currents = 10, -10

# No limits
Expand All @@ -138,7 +138,7 @@ def test_res_violated():
line.max_loading = Q_(50, "%")
assert line.max_loading.m == 0.5
assert line.res_violated
assert np.allclose(line.res_loading, 10 / (11 * 0.5))
assert np.allclose(line.res_loading, 10 / 11)

# Two violations
lp = LineParameters(id="lp", z_line=1.0, ampacity=9)
Expand Down
4 changes: 2 additions & 2 deletions roseau/load_flow_single/models/tests/test_transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ def test_res_violated():
# Two violations
transformer.max_loading = 4 / 5
assert transformer.res_violated is True
assert np.allclose(transformer.res_loading, 0.8 * 20 * 3 / 40)
assert np.allclose(transformer.res_loading, 0.8 * 20 * 3 / 50)

# Primary side violation
transformer.max_loading = Q_(45, "%")
assert transformer.res_violated is True
assert np.allclose(transformer.res_loading, 0.8 * 20 * 3 / (50 * 0.45))
assert np.allclose(transformer.res_loading, 0.8 * 20 * 3 / 50)

# Secondary side violation
transformer.max_loading = 1
Expand Down
22 changes: 6 additions & 16 deletions roseau/load_flow_single/models/transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,28 +170,18 @@ def res_power_losses(self) -> Q_[complex]:
return power1 + power2

@property
def res_loading(self) -> Q_[float] | None:
@ureg_wraps("", (None,))
def res_loading(self) -> Q_[float]:
"""Get the loading of the transformer (unitless)."""
sn = self._parameters._sn
if sn is None:
return None
power1, power2 = self._res_powers_getter(warning=True)
s_max = sn * self._max_loading
return Q_(max(abs(power1), abs(power2)) / s_max, "")
return max(abs(power1), abs(power2)) / sn

@property
def res_violated(self) -> bool | None:
"""Whether the transformer power exceeds the maximum power (loading > max_loading).
Returns ``None`` if the maximum power is not set.
"""
sn = self._parameters._sn
if sn is None:
return None
power1, power2 = self._res_powers_getter(warning=True)
s_max = sn * self._max_loading
def res_violated(self) -> bool:
"""Whether the transformer power loading exceeds its maximal loading."""
# True if either the primary or secondary is overloaded
return abs(power1) > s_max or abs(power2) > s_max
return bool(self.res_loading.m > self._max_loading)

#
# Json Mixin interface
Expand Down
14 changes: 4 additions & 10 deletions roseau/load_flow_single/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ def solve_load_flow(
To get the results of the load flow for the whole network, use the `res_` properties on the
network (e.g. ``print(net.res_buses``). To get the results for a specific element, use the
`res_` properties on the element (e.g. ``print(net.buses["bus1"].res_potentials)``.
`res_` properties on the element (e.g. ``print(net.buses["bus1"].res_voltage)``.
You need to activate the license before calling this method. Alternatively you may set the
environment variable ``ROSEAU_LOAD_FLOW_LICENSE_KEY`` to your license key and it will be
Expand Down Expand Up @@ -681,8 +681,7 @@ def res_lines(self) -> pd.DataFrame:
loading = None
violated = None
else:
i_max = ampacity * max_loading
loading = max(abs(current1), abs(current2)) / i_max
loading = max(abs(current1), abs(current2)) / ampacity
violated = loading > max_loading
res_dict["line_id"].append(line.id)
res_dict["current1"].append(current1)
Expand Down Expand Up @@ -739,13 +738,8 @@ def res_transformers(self) -> pd.DataFrame:
power2 = potential2 * current2.conjugate() * 3.0
sn = transformer.parameters._sn
max_loading = transformer._max_loading
if sn is None:
violated = None
loading = None
else:
s_max = sn * max_loading
loading = max(abs(power1), abs(power2)) / s_max
violated = loading > max_loading
loading = max(abs(power1), abs(power2)) / sn
violated = loading > max_loading
res_dict["transformer_id"].append(transformer.id)
res_dict["current1"].append(current1)
res_dict["current2"].append(current2)
Expand Down
4 changes: 2 additions & 2 deletions roseau/load_flow_single/tests/test_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ def test_import():
# Typing used for a deprecation
"Any",
}

assert rlfs_dir - rlf_dir == set()
# conftest is not included in wheels
assert rlfs_dir - rlf_dir == {"conftest"}
Loading

0 comments on commit cce60b1

Please sign in to comment.