diff --git a/cpp/src/bindings.cpp b/cpp/src/bindings.cpp index f644913d0e..abd3be9a4b 100644 --- a/cpp/src/bindings.cpp +++ b/cpp/src/bindings.cpp @@ -486,6 +486,9 @@ PYBIND11_MODULE(_pypowsybl, m) { m.def("get_network_area_diagram_svg", &pypowsybl::getNetworkAreaDiagramSvg, "Get network area diagram SVG as a string", py::arg("network"), py::arg("voltage_level_ids"), py::arg("depth"), py::arg("high_nominal_voltage_bound"), py::arg("low_nominal_voltage_bound"), py::arg("edge_name_displayed")); + m.def("get_network_area_diagram_displayed_voltage_levels", &pypowsybl::getNetworkAreaDiagramDisplayedVoltageLevels, "Get network area diagram displayed voltage level", + py::arg("network"), py::arg("voltage_level_ids"), py::arg("depth")); + m.def("create_security_analysis", &pypowsybl::createSecurityAnalysis, "Create a security analysis"); m.def("add_contingency", &pypowsybl::addContingency, "Add a contingency to a security analysis or sensitivity analysis", diff --git a/cpp/src/pypowsybl.cpp b/cpp/src/pypowsybl.cpp index 7817164917..e84d73fa48 100644 --- a/cpp/src/pypowsybl.cpp +++ b/cpp/src/pypowsybl.cpp @@ -796,6 +796,13 @@ std::string getNetworkAreaDiagramSvg(const JavaHandle& network, const std::vecto return toString(callJava(::getNetworkAreaDiagramSvg, network, voltageLevelIdPtr.get(), voltageLevelIds.size(), depth, highNominalVoltageBound, lowNominalVoltageBound, edgeNameDisplayed)); } +std::vector getNetworkAreaDiagramDisplayedVoltageLevels(const JavaHandle& network, const std::vector& voltageLevelIds, int depth) { + ToCharPtrPtr voltageLevelIdPtr(voltageLevelIds); + auto displayedVoltageLevelIdsArrayPtr = callJava(::getNetworkAreaDiagramDisplayedVoltageLevels, network, voltageLevelIdPtr.get(), voltageLevelIds.size(), depth); + ToStringVector displayedVoltageLevelIds(displayedVoltageLevelIdsArrayPtr); + return displayedVoltageLevelIds.get(); +} + JavaHandle createSecurityAnalysis() { return callJava(::createSecurityAnalysis); } diff --git a/cpp/src/pypowsybl.h b/cpp/src/pypowsybl.h index 95967d123d..d6b6e535f1 100644 --- a/cpp/src/pypowsybl.h +++ b/cpp/src/pypowsybl.h @@ -384,6 +384,8 @@ void writeNetworkAreaDiagramSvg(const JavaHandle& network, const std::string& sv std::string getNetworkAreaDiagramSvg(const JavaHandle& network, const std::vector& voltageLevelIds, int depth, double highNominalVoltageBound, double lowNominalVoltageBound, bool edgeNameDisplayed); +std::vector getNetworkAreaDiagramDisplayedVoltageLevels(const JavaHandle& network, const std::vector& voltageLevelIds, int depth); + JavaHandle createSecurityAnalysis(); void addContingency(const JavaHandle& analysisContext, const std::string& contingencyId, const std::vector& elementsIds); diff --git a/docs/reference/network.rst b/docs/reference/network.rst index ef4acfeff8..7af7245490 100644 --- a/docs/reference/network.rst +++ b/docs/reference/network.rst @@ -217,6 +217,7 @@ Miscellaneous network functions Network.write_single_line_diagram_svg Network.get_network_area_diagram Network.write_network_area_diagram_svg + Network.get_network_area_diagram_displayed_voltage_levels Network.disconnect Network.connect Network.open_switch diff --git a/docs/user_guide/network_visualization.rst b/docs/user_guide/network_visualization.rst index eacc27b2a4..508054f240 100644 --- a/docs/user_guide/network_visualization.rst +++ b/docs/user_guide/network_visualization.rst @@ -64,3 +64,10 @@ Note that similarly to single-line diagrams, a loadflow can be run before writin >>> network = pp.network.create_ieee9() >>> result = pp.loadflow.run_ac(network) >>> network.write_network_area_diagram_svg('ieee9.svg') + +In order to get a list of the displayed voltage levels from an input voltage level (or an input list of voltage levels) and a depth: + +.. code-block:: python + + >>> network = pp.network.create_ieee300() + >>> list_vl = network.get_network_area_diagram_displayed_voltage_levels('VL1', 1) \ No newline at end of file diff --git a/java/src/main/java/com/powsybl/python/network/NetworkAreaDiagramUtil.java b/java/src/main/java/com/powsybl/python/network/NetworkAreaDiagramUtil.java index 375318be73..78483f4f5d 100644 --- a/java/src/main/java/com/powsybl/python/network/NetworkAreaDiagramUtil.java +++ b/java/src/main/java/com/powsybl/python/network/NetworkAreaDiagramUtil.java @@ -100,6 +100,10 @@ static VoltageLevelFilter getNominalVoltageFilter(Network network, List return new VoltageLevelFilter(voltageLevels); } + public static List getDisplayedVoltageLevels(Network network, List voltageLevelIds, int depth) { + return NetworkAreaDiagram.getDisplayedVoltageLevels(network, voltageLevelIds, depth); + } + private static void traverseVoltageLevels(Set voltageLevelsDepth, int depth, Set visitedVoltageLevels, double highNominalVoltageBound, double lowNominalVoltageBound) { if (depth >= 0) { diff --git a/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java b/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java index 733e765093..232a989bd8 100644 --- a/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java +++ b/java/src/main/java/com/powsybl/python/network/NetworkCFunctions.java @@ -936,6 +936,16 @@ public static CCharPointer getNetworkAreaDiagramSvg(IsolateThread thread, Object }); } + @CEntryPoint(name = "getNetworkAreaDiagramDisplayedVoltageLevels") + public static PyPowsyblApiHeader.ArrayPointer getNetworkAreaDiagramDisplayedVoltageLevels(IsolateThread thread, ObjectHandle networkHandle, CCharPointerPointer voltageLevelIdsPointer, + int voltageLevelIdCount, int depth, ExceptionHandlerPointer exceptionHandlerPtr) { + return doCatch(exceptionHandlerPtr, () -> { + Network network = ObjectHandles.getGlobal().get(networkHandle); + List voltageLevelIds = toStringList(voltageLevelIdsPointer, voltageLevelIdCount); + return createCharPtrArray(NetworkAreaDiagramUtil.getDisplayedVoltageLevels(network, voltageLevelIds, depth)); + }); + } + @CEntryPoint(name = "getValidationLevel") public static ValidationLevelType getValidationLevel(IsolateThread thread, ObjectHandle networkHandle, ExceptionHandlerPointer exceptionHandlerPtr) { exceptionHandlerPtr.setMessage(WordFactory.nullPointer()); diff --git a/java/src/test/java/com/powsybl/python/network/NetworkAreaDiagramUtilTest.java b/java/src/test/java/com/powsybl/python/network/NetworkAreaDiagramUtilTest.java index d8ebb2a3af..ef73084a8d 100644 --- a/java/src/test/java/com/powsybl/python/network/NetworkAreaDiagramUtilTest.java +++ b/java/src/test/java/com/powsybl/python/network/NetworkAreaDiagramUtilTest.java @@ -10,12 +10,13 @@ import com.powsybl.commons.test.TestUtil; import com.powsybl.ieeecdf.converter.IeeeCdfNetworkFactory; import com.powsybl.iidm.network.Network; +import com.powsybl.iidm.network.test.*; +import com.powsybl.nad.*; import org.junit.jupiter.api.Test; import java.io.IOException; import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.Objects; +import java.util.*; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,4 +32,14 @@ void test() throws IOException { assertEquals(TestUtil.normalizeLineSeparator(new String(ByteStreams.toByteArray(Objects.requireNonNull(NetworkAreaDiagramUtil.class.getResourceAsStream("/nad.svg"))), StandardCharsets.UTF_8)), TestUtil.normalizeLineSeparator(svg)); } + + @Test + void testGetVisibleVoltageLevels() { + Network network = EurostagTutorialExample1Factory.createWithTieLine(); + List ids = NetworkAreaDiagram.getDisplayedVoltageLevels(network, List.of("VLHV1"), 1); + assertEquals("VLGEN, VLHV1, VLHV2", String.join(", ", ids)); + + ids = NetworkAreaDiagram.getDisplayedVoltageLevels(network, List.of("VLHV1"), 2); + assertEquals("VLGEN, VLHV1, VLHV2, VLLOAD", String.join(", ", ids)); + } } diff --git a/pypowsybl/_pypowsybl.pyi b/pypowsybl/_pypowsybl.pyi index 5d4f564f67..495e360913 100644 --- a/pypowsybl/_pypowsybl.pyi +++ b/pypowsybl/_pypowsybl.pyi @@ -568,6 +568,7 @@ def get_security_analysis_provider_parameters_names(provider: str) -> List[str]: def get_sensitivity_analysis_provider_parameters_names(provider: str) -> List[str]: ... def get_limit_violations(result: JavaHandle) -> SeriesArray: ... def get_network_area_diagram_svg(network: JavaHandle, voltage_level_ids: Union[str, List[str]], depth: int, high_nominal_voltage_bound: float, low_nominal_voltage_bound: float, edge_name_displayed: bool) -> str: ... +def get_network_area_diagram_displayed_voltage_levels(network: JavaHandle, voltage_level_ids: Union[str, List[str]], depth: int) -> List[str]: ... def get_network_elements_ids(network: JavaHandle, element_type: ElementType, nominal_voltages: List[float], countries: List[str], main_connected_component: bool, main_synchronous_component: bool, not_connected_to_same_bus_at_both_sides: bool) -> List[str]: ... def get_network_export_formats() -> List[str]: ... def get_network_import_formats() -> List[str]: ... diff --git a/pypowsybl/network/impl/network.py b/pypowsybl/network/impl/network.py index e86432a35c..182c893e0b 100644 --- a/pypowsybl/network/impl/network.py +++ b/pypowsybl/network/impl/network.py @@ -254,6 +254,23 @@ def get_network_area_diagram(self, voltage_level_ids: Union[str, List[str]] = No high_nominal_voltage_bound, low_nominal_voltage_bound, edge_name_displayed)) + def get_network_area_diagram_displayed_voltage_levels(self, voltage_level_ids: Union[str, List[str]], + depth: int = 0) -> List[str]: + """ + Gathers the name of the displayed voltage levels of a network-area diagram in a list, according to + the input voltage level(s) and the depth of the diagram. + + Args: + voltage_level_ids: the voltage level ID(s), center(s) of the diagram + depth: the diagram depth around the voltage level + + Returns: + a list of the displayed voltage levels + """ + if isinstance(voltage_level_ids, str): + voltage_level_ids = [voltage_level_ids] + return _pp.get_network_area_diagram_displayed_voltage_levels(self._handle, voltage_level_ids, depth) + def get_elements_ids(self, element_type: ElementType, nominal_voltages: Set[float] = None, countries: Set[str] = None, main_connected_component: bool = True, main_synchronous_component: bool = True, diff --git a/tests/test_network.py b/tests/test_network.py index 53149c1f66..9db04985df 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -853,6 +853,12 @@ def test_sld_nad(): n.write_network_area_diagram_svg(test_svg, high_nominal_voltage_bound=50, depth=10) +def test_nad_displayed_voltage_levels(): + n = pp.network.create_ieee14() + list_vl = n.get_network_area_diagram_displayed_voltage_levels('VL1', 1) + assert ['VL1', 'VL2', 'VL5'] == list_vl + + def test_current_limits(): network = pp.network.create_eurostag_tutorial_example1_network() assert 9 == len(network.get_current_limits()) @@ -1245,7 +1251,8 @@ def test_network_merge(): assert 10 == len(merge.get_voltage_levels()) sub_networks = merge.get_sub_networks() expected_sub_networks = pd.DataFrame(index=pd.Series(name='id', - data=['urn:uuid:d400c631-75a0-4c30-8aed-832b0d282e73', 'urn:uuid:77b55f87-fc1e-4046-9599-6c6b4f991a86'])) + data=['urn:uuid:d400c631-75a0-4c30-8aed-832b0d282e73', + 'urn:uuid:77b55f87-fc1e-4046-9599-6c6b4f991a86'])) pd.testing.assert_frame_equal(expected_sub_networks, sub_networks, check_dtype=False) be_from_merge = merge.get_sub_network('urn:uuid:d400c631-75a0-4c30-8aed-832b0d282e73') assert 6 == len(be_from_merge.get_voltage_levels()) @@ -1253,14 +1260,14 @@ def test_network_merge(): assert 4 == len(nl_from_merge.get_voltage_levels()) be_from_merge.detach() assert 6 == len(be_from_merge.get_voltage_levels()) - assert 4 == len(merge.get_voltage_levels()) # only remain NL in the merge + assert 4 == len(merge.get_voltage_levels()) # only remain NL in the merge sub_networks = merge.get_sub_networks() expected_sub_networks = pd.DataFrame(index=pd.Series(name='id', data=['urn:uuid:77b55f87-fc1e-4046-9599-6c6b4f991a86'])) pd.testing.assert_frame_equal(expected_sub_networks, sub_networks, check_dtype=False) nl_from_merge.detach() assert 4 == len(nl_from_merge.get_voltage_levels()) - assert 0 == len(merge.get_voltage_levels()) # merge is empty + assert 0 == len(merge.get_voltage_levels()) # merge is empty def test_linear_shunt_compensator_sections():