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
7 changes: 6 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: check-builtin-literals
- id: check-json
exclude: ^.vscode/
- id: check-merge-conflict
Expand All @@ -11,12 +12,16 @@ repos:
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/python-poetry/poetry
rev: 1.5.0
hooks:
- id: poetry-check
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black-jupyter
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.0.270
rev: v0.0.275
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,16 @@ line-length = 120
target-version = "py39"
show-fixes = true
namespace-packages = ["roseau"]
select = ["E", "F", "C90", "W", "B", "UP", "I", "RUF100", "TID"]
select = ["E", "F", "C90", "W", "B", "UP", "I", "RUF100", "TID", "SIM", "PT", "PIE", "N", "C4", "NPY"]
unfixable = ["B"]
extend-ignore = ["E501", "B024"]
extend-ignore = ["E501", "B024", "N818"]

[tool.ruff.flake8-tidy-imports]
ban-relative-imports = "all"

[tool.ruff.flake8-pytest-style]
parametrize-values-type = "tuple"

[tool.ruff.mccabe]
max-complexity = 15

Expand Down
18 changes: 9 additions & 9 deletions roseau/load_flow/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
TEST_ALL_NETWORKS_DATA_FOLDER = HERE / "tests" / "data" / "networks"
TEST_ALL_NETWORKS_DATA_PARAMS = [x for x in TEST_ALL_NETWORKS_DATA_FOLDER.glob("*") if x.is_dir()]
TEST_ALL_NETWORKS_DATA_IDS = [x.name for x in TEST_ALL_NETWORKS_DATA_PARAMS]
TEST_DGS_NETWORKS = [x for x in (HERE / "tests" / "data" / "dgs").glob("*")]
TEST_DGS_NETWORKS = list((HERE / "tests" / "data" / "dgs").glob("*"))
TEST_DGS_NETWORKS_IDS = [x.stem for x in TEST_DGS_NETWORKS]

TEST_SOME_NETWORKS_NAMES_SET = {
Expand All @@ -36,44 +36,44 @@ def rg() -> np.random.Generator:
return np.random.default_rng(seed=42)


@pytest.fixture(scope="function", params=["impedance", "power"], ids=["impedance", "power"])
@pytest.fixture(params=["impedance", "power"], ids=["impedance", "power"])
def network_load_data_name(request) -> str:
return request.param


@pytest.fixture(scope="function", params=TEST_ALL_NETWORKS_DATA_PARAMS, ids=TEST_ALL_NETWORKS_DATA_IDS)
@pytest.fixture(params=TEST_ALL_NETWORKS_DATA_PARAMS, ids=TEST_ALL_NETWORKS_DATA_IDS)
def all_network_folder(request) -> Path:
return request.param


@pytest.fixture(scope="function")
@pytest.fixture()
def all_network_path(all_network_folder, network_load_data_name) -> Path:
_check_folders(all_network_folder, network_load_data_name)
return all_network_folder / f"network_{network_load_data_name}.json"


@pytest.fixture(scope="function")
@pytest.fixture()
def all_network_result(all_network_folder, network_load_data_name) -> Path:
return all_network_folder / "results_linear_method.csv"


@pytest.fixture(scope="function", params=TEST_SOME_NETWORKS_DATA_PARAMS, ids=TEST_SOME_NETWORKS_DATA_IDS)
@pytest.fixture(params=TEST_SOME_NETWORKS_DATA_PARAMS, ids=TEST_SOME_NETWORKS_DATA_IDS)
def some_network_folder(request) -> Path:
return request.param


@pytest.fixture(scope="function")
@pytest.fixture()
def some_network_path(some_network_folder, network_load_data_name) -> Path:
_check_folders(some_network_folder, network_load_data_name)
return some_network_folder / f"network_{network_load_data_name}.json"


@pytest.fixture(scope="function")
@pytest.fixture()
def some_network_result(some_network_folder, network_load_data_name) -> Path:
return some_network_folder / "results_linear_method.csv"


@pytest.fixture(scope="function", params=TEST_DGS_NETWORKS, ids=TEST_DGS_NETWORKS_IDS)
@pytest.fixture(params=TEST_DGS_NETWORKS, ids=TEST_DGS_NETWORKS_IDS)
def dgs_network_path(request) -> Path:
return request.param

Expand Down
24 changes: 11 additions & 13 deletions roseau/load_flow/io/dgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,53 +235,53 @@ def _read_dgs_json_file(filename: StrPath):
sta_cubic = pd.DataFrame(columns=data["StaCubic"]["Attributes"], data=data["StaCubic"]["Values"]).set_index("FID")

# Transformers
if "ElmTr2" in data.keys():
if "ElmTr2" in data:
elm_tr = pd.DataFrame(columns=data["ElmTr2"]["Attributes"], data=data["ElmTr2"]["Values"]).set_index("FID")
else:
elm_tr = None

# Transformer types
if "TypTr2" in data.keys():
if "TypTr2" in data:
typ_tr = pd.DataFrame(columns=data["TypTr2"]["Attributes"], data=data["TypTr2"]["Values"]).set_index("FID")
else:
typ_tr = None

# Switch
if "ElmCoup" in data.keys():
if "ElmCoup" in data:
elm_coup = pd.DataFrame(columns=data["ElmCoup"]["Attributes"], data=data["ElmCoup"]["Values"]).set_index("FID")
else:
elm_coup = None

# Lines
if "ElmLne" in data.keys():
if "ElmLne" in data:
elm_lne = pd.DataFrame(columns=data["ElmLne"]["Attributes"], data=data["ElmLne"]["Values"]).set_index("FID")
else:
elm_lne = None

# Line types
if "TypLne" in data.keys():
if "TypLne" in data:
typ_lne = pd.DataFrame(columns=data["TypLne"]["Attributes"], data=data["TypLne"]["Values"]).set_index("FID")
else:
typ_lne = None

# LV loads
if "ElmLodLV" in data.keys():
if "ElmLodLV" in data:
elm_lod_lv = pd.DataFrame(columns=data["ElmLodLV"]["Attributes"], data=data["ElmLodLV"]["Values"]).set_index(
"FID"
)
else:
elm_lod_lv = None

# MV loads
if "ElmLodmv" in data.keys():
if "ElmLodmv" in data:
elm_lod_mv = pd.DataFrame(columns=data["ElmLodmv"]["Attributes"], data=data["ElmLodmv"]["Values"]).set_index(
"FID"
)
else:
elm_lod_mv = None

# Generators
if "ElmGenStat" in data.keys():
if "ElmGenStat" in data:
elm_gen_stat = pd.DataFrame(
columns=data["ElmGenStat"]["Attributes"], data=data["ElmGenStat"]["Values"]
).set_index("FID")
Expand All @@ -290,7 +290,7 @@ def _read_dgs_json_file(filename: StrPath):

# LV generators
# Generators
if "ElmPvsys" in data.keys():
if "ElmPvsys" in data:
elm_pv_sys = pd.DataFrame(columns=data["ElmPvsys"]["Attributes"], data=data["ElmPvsys"]["Values"]).set_index(
"FID"
)
Expand Down Expand Up @@ -355,10 +355,8 @@ def _generate_loads(
sb = _compute_load_power(elm_lod, load_id, "s") * factor
sc = _compute_load_power(elm_lod, load_id, "t") * factor

if sa == 0 and sb == 0 and sc == 0: # Balanced
s = [s_phase / 3, s_phase / 3, s_phase / 3]
else: # Unbalanced
s = [sa, sb, sc]
# Balanced or Unbalanced
s = [s_phase / 3, s_phase / 3, s_phase / 3] if sa == 0 and sb == 0 and sc == 0 else [sa, sb, sc]
loads[load_id] = PowerLoad(id=load_id, phases="abcn", bus=buses[bus_id], powers=s)


Expand Down
4 changes: 2 additions & 2 deletions roseau/load_flow/models/lines/lines.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ def _check_loop(self) -> None:
element = elements.pop(-1)
visited_1.add(element)
for e in element._connected_elements:
if e not in visited_1 and (isinstance(e, Bus) or isinstance(e, Switch)) and e != self:
if e not in visited_1 and (isinstance(e, (Bus, Switch))) and e != self:
elements.append(e)
visited_2: set[Element] = set()
elements = [self.bus2]
while elements:
element = elements.pop(-1)
visited_2.add(element)
for e in element._connected_elements:
if e not in visited_2 and (isinstance(e, Bus) or isinstance(e, Switch)) and e != self:
if e not in visited_2 and (isinstance(e, (Bus, Switch))) and e != self:
elements.append(e)
if visited_1.intersection(visited_2):
msg = f"There is a loop of switch involving the switch {self.id!r}. It is not allowed."
Expand Down
4 changes: 2 additions & 2 deletions roseau/load_flow/models/loads/flexible_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,14 +103,14 @@ def _check_values(self) -> None:
elif self.type == "p_max_u_consumption":
useless_values = {"u_max": self._u_max, "u_up": self._u_up}
elif self.type == "q_u":
useless_values = dict()
useless_values = {}
else:
msg = f"Unsupported control type {self.type!r}"
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_CONTROL_TYPE)

# Warn the user if a value different from 0 was given to the control for a useless value
msg_list = list()
msg_list = []
for name, value in useless_values.items():
if not np.isclose(value, 0):
msg_list.append(f"{name!r} ({value:.1f} V)")
Expand Down
9 changes: 4 additions & 5 deletions roseau/load_flow/models/loads/loads.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,10 @@ def __init__(
)
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_SHORT_CIRCUIT)
if flexible_params:
if len(flexible_params) != self._size:
msg = f"Incorrect number of parameters: {len(flexible_params)} instead of {self._size}"
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_PARAMETERS_SIZE)
if flexible_params and len(flexible_params) != self._size:
msg = f"Incorrect number of parameters: {len(flexible_params)} instead of {self._size}"
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_PARAMETERS_SIZE)

self._flexible_params = flexible_params
self.powers = powers
Expand Down
25 changes: 9 additions & 16 deletions roseau/load_flow/models/tests/test_line_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,16 @@ def test_line_parameters():
# Real element off the diagonal (Z)
z_line = np.ones(shape=(4, 4), dtype=complex)
y_shunt = np.eye(4, dtype=complex)

with pytest.raises(RoseauLoadFlowException) as e:
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
Line("line", bus, bus, phases="abcn", ground=ground, parameters=lp, length=2.5)
LineParameters("test", z_line=z_line, y_shunt=y_shunt)
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Z_LINE_VALUE
assert e.value.msg == "The z_line matrix of line type 'test' has off-diagonal elements with a non-zero real part."

# Real element off the diagonal (Y)
z_line = np.eye(3, dtype=complex)
y_shunt = np.ones(shape=(3, 3), dtype=complex)
with pytest.raises(RoseauLoadFlowException) as e:
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
Line("line", bus, bus, phases="abc", ground=ground, parameters=lp, length=2.5)
LineParameters("test", z_line=z_line, y_shunt=y_shunt)
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Y_SHUNT_VALUE
assert e.value.msg == "The y_shunt matrix of line type 'test' has off-diagonal elements with a non-zero real part."

Expand All @@ -36,34 +33,31 @@ def test_line_parameters():
z_line[1, 1] = -3
y_shunt = -2 * np.eye(4, dtype=complex)
with pytest.raises(RoseauLoadFlowException) as e:
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
Line("line", bus, bus, phases="abcn", ground=ground, parameters=lp, length=2.4)
LineParameters("test", z_line=z_line, y_shunt=y_shunt)
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Z_LINE_VALUE
assert e.value.msg == "The z_line matrix of line type 'test' has coefficients with negative real part."

# Negative real values (Y)
y_shunt = 2 * np.eye(3, dtype=complex)
y_shunt[1, 1] = -3
with pytest.raises(RoseauLoadFlowException):
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
Line("line", bus, bus, phases="abcn", ground=ground, parameters=lp, length=2.4)
LineParameters("test", z_line=z_line, y_shunt=y_shunt)
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Z_LINE_VALUE
assert e.value.msg == "The z_line matrix of line type 'test' has coefficients with negative real part."

# Bad shape (LV - Z)
z_line = np.eye(4, dtype=complex)[:, :2]
y_shunt = np.eye(4, dtype=complex)
with pytest.raises(RoseauLoadFlowException) as e:
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
Line("line", bus, bus, phases="abcn", ground=ground, parameters=lp, length=2.4)
LineParameters("test", z_line=z_line, y_shunt=y_shunt)
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Z_LINE_SHAPE
assert e.value.msg == "The z_line matrix of line type 'test' has incorrect dimensions (4, 2)."

# Bad shape (LV - Y)
z_line = np.eye(4, dtype=complex)
y_shunt = np.eye(3, dtype=complex)
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
with pytest.raises(RoseauLoadFlowException) as e:
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
Line("line", bus, bus, phases="abcn", ground=ground, parameters=lp, length=2.4)
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Y_SHUNT_SHAPE
assert e.value.msg == "Incorrect y_shunt dimensions for line 'line': (3, 3) instead of (4, 4)"
Expand All @@ -72,25 +66,24 @@ def test_line_parameters():
z_line = np.eye(4, dtype=complex)[:, :2]
y_shunt = np.eye(3, dtype=complex)
with pytest.raises(RoseauLoadFlowException) as e:
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
Line("line", bus, bus, phases="abc", ground=ground, parameters=lp, length=2.4)
LineParameters("test", z_line=z_line, y_shunt=y_shunt)
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Z_LINE_SHAPE
assert e.value.msg == "The z_line matrix of line type 'test' has incorrect dimensions (4, 2)."

# Bad shape (MV - Y)
z_line = np.eye(3, dtype=complex)
y_shunt = np.eye(6, dtype=complex)
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
with pytest.raises(RoseauLoadFlowException) as e:
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
Line("line", bus, bus, phases="abc", ground=ground, parameters=lp, length=2.4)
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Y_SHUNT_SHAPE
assert e.value.msg == "Incorrect y_shunt dimensions for line 'line': (6, 6) instead of (3, 3)"

# LV line with not zero shunt admittance
z_line = np.eye(3, dtype=complex)
y_shunt = np.eye(3, dtype=complex)
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
with pytest.raises(RoseauLoadFlowException) as e:
lp = LineParameters("test", z_line=z_line, y_shunt=y_shunt)
Line("line", bus, bus, phases="abcn", ground=ground, parameters=lp, length=2.4)
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Z_LINE_SHAPE
assert e.value.msg == "Incorrect z_line dimensions for line 'line': (3, 3) instead of (4, 4)"
Expand Down
8 changes: 4 additions & 4 deletions roseau/load_flow/models/tests/test_loads.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,23 +65,23 @@ def test_loads():
assert "Incorrect number of impedances" in e.value.msg
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_Z_SIZE

fp = [FlexibleParameter.constant()] * 3
with pytest.raises(RoseauLoadFlowException) as e:
fp = [FlexibleParameter.constant()] * 3
PowerLoad("load", bus, phases="abcn", powers=[100, 100], flexible_params=fp)
assert "Incorrect number of powers" in e.value.msg
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_S_SIZE
fp = [FlexibleParameter.constant()] * 3
with pytest.raises(RoseauLoadFlowException) as e:
fp = [FlexibleParameter.constant()] * 3
PowerLoad("load", bus, phases="abcn", powers=[100, 100, 100, 100], flexible_params=fp)
assert "Incorrect number of powers" in e.value.msg
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_S_SIZE
fp = [FlexibleParameter.constant()] * 2
with pytest.raises(RoseauLoadFlowException) as e:
fp = [FlexibleParameter.constant()] * 2
PowerLoad("load", bus, phases="abcn", powers=[100, 100, 100], flexible_params=fp)
assert "Incorrect number of parameters" in e.value.msg
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_PARAMETERS_SIZE
fp = [FlexibleParameter.constant()] * 4
with pytest.raises(RoseauLoadFlowException) as e:
fp = [FlexibleParameter.constant()] * 4
PowerLoad("load", bus, phases="abcn", powers=[100, 100, 100], flexible_params=fp)
assert "Incorrect number of parameters" in e.value.msg
assert e.value.code == RoseauLoadFlowExceptionCode.BAD_PARAMETERS_SIZE
Expand Down
14 changes: 5 additions & 9 deletions roseau/load_flow/solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ def check_solver_params(solver: Solver, params: Optional[JsonDict]) -> JsonDict:
Returns:
The updated solver parameters.
"""
if params is None:
params = {}
else:
params = params.copy()
params = {} if params is None else params.copy()

# Check the solver
if solver not in _SOLVERS_PARAMS:
Expand All @@ -51,10 +48,9 @@ def check_solver_params(solver: Solver, params: Optional[JsonDict]) -> JsonDict:
# Extra checks per solver
if solver == "newton":
pass # Nothing more to check
elif solver == "newton_goldstein":
if params.get("m1", 0.1) >= params.get("m2", 0.9):
msg = "For the 'newton_goldstein' solver, the inequality m1 < m2 should be respected."
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_SOLVER_PARAMS)
elif solver == "newton_goldstein" and params.get("m1", 0.1) >= params.get("m2", 0.9):
msg = "For the 'newton_goldstein' solver, the inequality m1 < m2 should be respected."
logger.error(msg)
raise RoseauLoadFlowException(msg=msg, code=RoseauLoadFlowExceptionCode.BAD_SOLVER_PARAMS)

return params
Loading