diff --git a/roseau/load_flow/conftest.py b/roseau/load_flow/conftest.py index d8079b39..e2181a46 100644 --- a/roseau/load_flow/conftest.py +++ b/roseau/load_flow/conftest.py @@ -4,6 +4,8 @@ import pytest from pandas.testing import assert_frame_equal +from roseau.load_flow.utils import console + # Variable to test the network HERE = Path(__file__).parent.expanduser().absolute() TEST_ALL_NETWORKS_DATA_FOLDER = HERE / "tests" / "data" / "networks" @@ -78,6 +80,11 @@ def dgs_network_path(request) -> Path: return request.param +@pytest.fixture(autouse=True, scope="session") +def _set_console_width() -> None: + console.width = 210 + + # # Utils # diff --git a/roseau/load_flow/models/tests/test_transformer_parameters.py b/roseau/load_flow/models/tests/test_transformer_parameters.py index 61a28c78..ce35849f 100644 --- a/roseau/load_flow/models/tests/test_transformer_parameters.py +++ b/roseau/load_flow/models/tests/test_transformer_parameters.py @@ -412,18 +412,18 @@ def test_print_catalogue(): # Print the entire catalogue with console.capture() as capture: TransformerParameters.print_catalogue() - assert len(capture.get().split("\n")) == 138 + assert len(capture.get().split("\n")) == 136 # Filter on a single attribute for field_name, value, expected_lines in ( - ("id", "SE_Minera_A0Ak_50kVA", 9), - ("manufacturer", "SE", 124), - ("range", r"min.*", 64), - ("efficiency", "c0", 37), - ("type", "dy", 134), - ("sn", Q_(160, "kVA"), 18), - ("uhv", Q_(20, "kV"), 138), - ("ulv", 400, 138), + ("id", "SE_Minera_A0Ak_50kVA", 7), + ("manufacturer", "SE", 122), + ("range", r"min.*", 62), + ("efficiency", "c0", 35), + ("type", "dy", 132), + ("sn", Q_(160, "kVA"), 16), + ("uhv", Q_(20, "kV"), 136), + ("ulv", 400, 136), ): with console.capture() as capture: TransformerParameters.print_catalogue(**{field_name: value}) @@ -431,13 +431,13 @@ def test_print_catalogue(): # Filter on two attributes for field_name, value, expected_lines in ( - ("id", "SE_Minera_A0Ak_50kVA", 9), - ("range", "minera", 64), - ("efficiency", "c0", 37), - ("type", r"^d.*11$", 120), - ("sn", Q_(160, "kVA"), 17), - ("uhv", Q_(20, "kV"), 124), - ("ulv", 400, 124), + ("id", "SE_Minera_A0Ak_50kVA", 7), + ("range", "minera", 62), + ("efficiency", "c0", 35), + ("type", r"^d.*11$", 118), + ("sn", Q_(160, "kVA"), 15), + ("uhv", Q_(20, "kV"), 122), + ("ulv", 400, 122), ): with console.capture() as capture: TransformerParameters.print_catalogue(**{field_name: value}, manufacturer="se") @@ -445,12 +445,12 @@ def test_print_catalogue(): # Filter on three attributes for field_name, value, expected_lines in ( - ("id", "se_VEGETA_C0BK_3150kva", 9), - ("efficiency", r"c0[abc]k", 23), - ("type", "dyn", 38), - ("sn", Q_(160, "kVA"), 10), - ("uhv", Q_(20, "kV"), 38), - ("ulv", 400, 38), + ("id", "se_VEGETA_C0BK_3150kva", 7), + ("efficiency", r"c0[abc]k", 21), + ("type", "dyn", 36), + ("sn", Q_(160, "kVA"), 8), + ("uhv", Q_(20, "kV"), 36), + ("ulv", 400, 36), ): with console.capture() as capture: TransformerParameters.print_catalogue(**{field_name: value}, manufacturer="se", range=r"^vegeta$") diff --git a/roseau/load_flow/models/transformers/parameters.py b/roseau/load_flow/models/transformers/parameters.py index c3f8acf9..a00247d6 100644 --- a/roseau/load_flow/models/transformers/parameters.py +++ b/roseau/load_flow/models/transformers/parameters.py @@ -2,6 +2,7 @@ import re import textwrap from importlib import resources +from itertools import cycle from pathlib import Path from typing import NoReturn, Optional, Union @@ -14,7 +15,7 @@ from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode from roseau.load_flow.typing import Id, JsonDict from roseau.load_flow.units import Q_, ureg_wraps -from roseau.load_flow.utils import CatalogueMixin, Identifiable, JsonMixin, console +from roseau.load_flow.utils import CatalogueMixin, Identifiable, JsonMixin, console, palette logger = logging.getLogger(__name__) @@ -487,14 +488,14 @@ def print_catalogue( # Start creating a table to display the results table = Table(title="Available Transformer Parameters") - table.add_column("Id") - table.add_column("Manufacturer", style="color(1)", header_style="color(1)") - table.add_column("Product range", style="color(2)", header_style="color(2)") - table.add_column("Efficiency", style="color(3)", header_style="color(3)") - table.add_column("Type", style="color(4)", header_style="color(4)") - table.add_column("Nominal power (kVA)", justify="right", style="color(5)", header_style="color(5)") - table.add_column("High voltage (kV)", justify="right", style="color(6)", header_style="color(6)") - table.add_column("Low voltage (kV)", justify="right", style="color(9)", header_style="color(9)") + table.add_column("Id", overflow="fold") + table.add_column("Manufacturer", overflow="fold") + table.add_column("Product range", overflow="fold") + table.add_column("Efficiency", overflow="fold") + table.add_column("Type", overflow="fold") + table.add_column("Nominal power (kVA)", justify="right", overflow="fold") + table.add_column("High voltage (kV)", justify="right", overflow="fold") + table.add_column("Low voltage (kV)", justify="right", overflow="fold") empty_table = True # Match on the manufacturer, range, efficiency and type @@ -525,6 +526,7 @@ def print_catalogue( # Iterate over the transformers selected_index = catalogue_mask[catalogue_mask].index + cycler = cycle(palette) for idx in selected_index: empty_table = False table.add_row( @@ -536,6 +538,7 @@ def print_catalogue( f"{catalogue_data.at[idx, 'sn']/1000:.1f}", # VA to kVA f"{catalogue_data.at[idx, 'uhv']/1000:.1f}", # V to kV f"{catalogue_data.at[idx, 'ulv']/1000:.1f}", # V to kV + style=next(cycler), ) # Handle the case of an empty table diff --git a/roseau/load_flow/network.py b/roseau/load_flow/network.py index dfde9152..4e18b238 100644 --- a/roseau/load_flow/network.py +++ b/roseau/load_flow/network.py @@ -8,6 +8,7 @@ import warnings from collections.abc import Sized from importlib import resources +from itertools import cycle from pathlib import Path from typing import NoReturn, Optional, TypeVar, Union from urllib.parse import urljoin @@ -37,7 +38,7 @@ ) from roseau.load_flow.solvers import check_solver_params from roseau.load_flow.typing import Authentication, Id, JsonDict, Solver, StrPath -from roseau.load_flow.utils import CatalogueMixin, JsonMixin, console +from roseau.load_flow.utils import CatalogueMixin, JsonMixin, console, palette logger = logging.getLogger(__name__) @@ -1298,14 +1299,14 @@ def print_catalogue( # Start creating a table to display the results table = Table(title="Available Networks") - table.add_column("Name") - table.add_column("Nb buses", justify="right", style="color(1)", header_style="color(1)") - table.add_column("Nb branches", justify="right", style="color(2)", header_style="color(2)") - table.add_column("Nb loads", justify="right", style="color(3)", header_style="color(3)") - table.add_column("Nb sources", justify="right", style="color(4)", header_style="color(4)") - table.add_column("Nb grounds", justify="right", style="color(5)", header_style="color(5)") - table.add_column("Nb potential refs", justify="right", style="color(6)", header_style="color(6)") - table.add_column("Available load points", justify="right", style="color(9)", header_style="color(9)") + table.add_column("Name", overflow="fold") + table.add_column("Nb buses", justify="right", overflow="fold") + table.add_column("Nb branches", justify="right", overflow="fold") + table.add_column("Nb loads", justify="right", overflow="fold") + table.add_column("Nb sources", justify="right", overflow="fold") + table.add_column("Nb grounds", justify="right", overflow="fold") + table.add_column("Nb potential refs", justify="right", overflow="fold") + table.add_column("Available load points", overflow="fold") empty_table = True # Match on the name @@ -1334,6 +1335,7 @@ def match_load_point_function(x: str) -> bool: return x.lower() == load_point_name_pattern # Iterate over the networks + cycler = cycle(palette) for c_name in match_names_list: c_data = catalogue_data[c_name] available_load_points = c_data["load_points"] @@ -1348,6 +1350,7 @@ def match_load_point_function(x: str) -> bool: str(c_data["nb_grounds"]), str(c_data["nb_potential_refs"]), ", ".join(repr(x) for x in sorted(c_data["load_points"])), + style=next(cycler), ) # Handle the case of an empty table diff --git a/roseau/load_flow/tests/test_electrical_network.py b/roseau/load_flow/tests/test_electrical_network.py index 1dbdd471..42b0fefc 100644 --- a/roseau/load_flow/tests/test_electrical_network.py +++ b/roseau/load_flow/tests/test_electrical_network.py @@ -1501,37 +1501,37 @@ def test_print_catalogue(): # Print the entire catalogue with console.capture() as capture: ElectricalNetwork.print_catalogue() - assert len(capture.get().split("\n")) == 88 + assert len(capture.get().split("\n")) == 46 # Filter on the network name with console.capture() as capture: ElectricalNetwork.print_catalogue(name="MV") - assert len(capture.get().split("\n")) == 48 + assert len(capture.get().split("\n")) == 26 with console.capture() as capture: ElectricalNetwork.print_catalogue(name=re.compile(r"^MV")) - assert len(capture.get().split("\n")) == 48 + assert len(capture.get().split("\n")) == 26 # Filter on the load point name with console.capture() as capture: ElectricalNetwork.print_catalogue(load_point_name="winter") - assert len(capture.get().split("\n")) == 88 + assert len(capture.get().split("\n")) == 46 with console.capture() as capture: ElectricalNetwork.print_catalogue(load_point_name=re.compile(r"^Winter")) - assert len(capture.get().split("\n")) == 88 + assert len(capture.get().split("\n")) == 46 # Filter on both with console.capture() as capture: ElectricalNetwork.print_catalogue(name="MV", load_point_name="winter") - assert len(capture.get().split("\n")) == 48 + assert len(capture.get().split("\n")) == 26 with console.capture() as capture: ElectricalNetwork.print_catalogue(name="MV", load_point_name=re.compile(r"^Winter")) - assert len(capture.get().split("\n")) == 48 + assert len(capture.get().split("\n")) == 26 with console.capture() as capture: ElectricalNetwork.print_catalogue(name=re.compile(r"^MV"), load_point_name="winter") - assert len(capture.get().split("\n")) == 48 + assert len(capture.get().split("\n")) == 26 with console.capture() as capture: ElectricalNetwork.print_catalogue(name=re.compile(r"^MV"), load_point_name=re.compile(r"^Winter")) - assert len(capture.get().split("\n")) == 48 + assert len(capture.get().split("\n")) == 26 # Regexp error with console.capture() as capture: @@ -1539,4 +1539,4 @@ def test_print_catalogue(): assert len(capture.get().split("\n")) == 2 with console.capture() as capture: ElectricalNetwork.print_catalogue(load_point_name=r"^winter[0-]") - assert len(capture.get().split("\n")) == 3 + assert len(capture.get().split("\n")) == 2 diff --git a/roseau/load_flow/utils/__init__.py b/roseau/load_flow/utils/__init__.py index 99e7cb65..b3156715 100644 --- a/roseau/load_flow/utils/__init__.py +++ b/roseau/load_flow/utils/__init__.py @@ -1,7 +1,7 @@ """ This module contains utility classes and functions for Roseau Load Flow. """ -from roseau.load_flow.utils.console import console +from roseau.load_flow.utils.console import console, palette from roseau.load_flow.utils.constants import CX, DELTA_P, EPSILON_0, EPSILON_R, MU_0, MU_R, OMEGA, PI, RHO, TAN_D, F from roseau.load_flow.utils.mixins import CatalogueMixin, Identifiable, JsonMixin from roseau.load_flow.utils.types import ConductorType, InsulatorType, LineType @@ -29,4 +29,5 @@ "InsulatorType", # Console "console", + "palette", ] diff --git a/roseau/load_flow/utils/console.py b/roseau/load_flow/utils/console.py index a9463afd..c16e9c7f 100644 --- a/roseau/load_flow/utils/console.py +++ b/roseau/load_flow/utils/console.py @@ -1,3 +1,25 @@ from rich.console import Console console = Console() + +palette = [ + "#4c72b0", + "#dd8452", + "#55a868", + "#c44e52", + "#8172b3", + "#937860", + "#da8bc3", + "#8c8c8c", + "#ccb974", + "#64b5cd", +] +"""Color palette for the catalogue tables. + +This is seaborn's default color palette. Generated with: +```python +import seaborn as sns +sns.set_theme() +list(sns.color_palette().as_hex()) +``` +"""