diff --git a/include/powsybl/iidm/VoltageLevelViews.hpp b/include/powsybl/iidm/VoltageLevelViews.hpp index e81d0322..1ba91724 100644 --- a/include/powsybl/iidm/VoltageLevelViews.hpp +++ b/include/powsybl/iidm/VoltageLevelViews.hpp @@ -135,6 +135,8 @@ class NodeBreakerView { virtual stdcxx::const_range getNodes() const = 0; + virtual stdcxx::const_range getNodesInternalConnectedTo(unsigned long node) const = 0; + virtual stdcxx::CReference getOptionalTerminal(unsigned long node) const = 0; virtual stdcxx::Reference getOptionalTerminal(unsigned long node) = 0; @@ -149,6 +151,10 @@ class NodeBreakerView { virtual stdcxx::range getSwitches() = 0; + virtual stdcxx::const_range getSwitches(unsigned long node) const = 0; + + virtual stdcxx::range getSwitches(unsigned long node) = 0; + virtual stdcxx::CReference getTerminal(unsigned long node) const = 0; virtual stdcxx::Reference getTerminal(unsigned long node) = 0; diff --git a/include/powsybl/math/UndirectedGraph.hpp b/include/powsybl/math/UndirectedGraph.hpp index bdef1080..744385d3 100644 --- a/include/powsybl/math/UndirectedGraph.hpp +++ b/include/powsybl/math/UndirectedGraph.hpp @@ -54,19 +54,29 @@ class UndirectedGraph { const stdcxx::Reference& getEdgeObject(unsigned long e) const; + const_range getEdgeObjectsConnectedToVertex(unsigned long vertex) const; + + const std::vector& getEdgeConnectedToVertex(unsigned long vertex) const; + + const std::vector& getEdgesConnectedToVertex(unsigned long vertex) const; + const_range getEdgeObjects() const; range getEdgeObjects(); const_range getEdgeObjects(unsigned long v1, unsigned long v2) const; + const unsigned long& getEdgeVertex1(unsigned long edge) const; + + const unsigned long& getEdgeVertex2(unsigned long edge) const; + const_range getEdges() const; unsigned long getMaxVertex() const; - unsigned long getVertex1(unsigned long e) const; + const unsigned long& getVertex1(unsigned long e) const; - unsigned long getVertex2(unsigned long e) const; + const unsigned long& getVertex2(unsigned long e) const; unsigned long getVertexCapacity() const; @@ -148,9 +158,9 @@ class UndirectedGraph::Edge { const stdcxx::Reference& getObject() const; - unsigned long getVertex1() const; + const unsigned long& getVertex1() const; - unsigned long getVertex2() const; + const unsigned long& getVertex2() const; void setObject(const stdcxx::Reference& object); diff --git a/include/powsybl/math/UndirectedGraph.hxx b/include/powsybl/math/UndirectedGraph.hxx index 2ce09c33..d929ae51 100644 --- a/include/powsybl/math/UndirectedGraph.hxx +++ b/include/powsybl/math/UndirectedGraph.hxx @@ -75,7 +75,7 @@ void UndirectedGraph::addVertexIfNotPresent(unsigned long v) { m_availableVertices.erase(it); } } else { - for (unsigned int i = m_vertices.size(); i < v; ++i) { + for (auto i = m_vertices.size(); i < v; ++i) { m_availableVertices.insert(i); } m_vertices.resize(v + 1); @@ -216,6 +216,26 @@ const stdcxx::Reference& UndirectedGraph::getEdgeObject(unsigned long e return m_edges[e]->getObject(); } +template +typename UndirectedGraph::template const_range UndirectedGraph::getEdgeObjectsConnectedToVertex(unsigned long vertex) const { + const auto& edgeMapper = [this](const unsigned long& e) -> const stdcxx::Reference& { + return getEdgeObject(e); + }; + + return getEdgeConnectedToVertex(vertex) | boost::adaptors::transformed(edgeMapper); +} + +template +const std::vector& UndirectedGraph::getEdgeConnectedToVertex(unsigned long vertex) const { + checkVertex(vertex); + return getAdjacencyList()[vertex]; +} + +template +const std::vector& UndirectedGraph::getEdgesConnectedToVertex(unsigned long vertex) const { + return getEdgeConnectedToVertex(vertex); +} + template typename UndirectedGraph::template const_range UndirectedGraph::getEdgeObjects() const { const auto& filter = [](const std::unique_ptr& edge) { @@ -255,6 +275,18 @@ typename UndirectedGraph::template const_range UndirectedGraph::g boost::adaptors::transformed(Edge::map); } +template +const unsigned long& UndirectedGraph::getEdgeVertex1(unsigned long edge) const { + checkEdge(edge); + return m_edges[edge]->getVertex1(); +} + +template +const unsigned long& UndirectedGraph::getEdgeVertex2(unsigned long edge) const { + checkEdge(edge); + return m_edges[edge]->getVertex2(); +} + template typename UndirectedGraph::template const_range UndirectedGraph::getEdges() const { const auto& filter = [this](const unsigned long e) { @@ -270,13 +302,13 @@ unsigned long UndirectedGraph::getMaxVertex() const { } template -unsigned long UndirectedGraph::getVertex1(unsigned long e) const { +const unsigned long& UndirectedGraph::getVertex1(unsigned long e) const { checkEdge(e); return m_edges.at(e).get()->getVertex1(); } template -unsigned long UndirectedGraph::getVertex2(unsigned long e) const { +const unsigned long& UndirectedGraph::getVertex2(unsigned long e) const { checkEdge(e); return m_edges.at(e).get()->getVertex2(); } @@ -500,12 +532,12 @@ const stdcxx::Reference& UndirectedGraph::Edge::map(const typename Undi } template -unsigned long UndirectedGraph::Edge::getVertex1() const { +const unsigned long& UndirectedGraph::Edge::getVertex1() const { return m_vertex1; } template -unsigned long UndirectedGraph::Edge::getVertex2() const { +const unsigned long& UndirectedGraph::Edge::getVertex2() const { return m_vertex2; } diff --git a/src/iidm/BusBreakerVoltageLevel.cpp b/src/iidm/BusBreakerVoltageLevel.cpp index 6268a1a4..82977c8b 100644 --- a/src/iidm/BusBreakerVoltageLevel.cpp +++ b/src/iidm/BusBreakerVoltageLevel.cpp @@ -99,7 +99,6 @@ bool BusBreakerVoltageLevel::connect(Terminal& terminal) { return true; } - void BusBreakerVoltageLevel::deleteVariantArrayElement(unsigned long index) { VoltageLevel::deleteVariantArrayElement(index); m_variants.deleteVariantArrayElement(index); diff --git a/src/iidm/BusBreakerVoltageLevel.hpp b/src/iidm/BusBreakerVoltageLevel.hpp index 228fa66f..9bffe6ab 100644 --- a/src/iidm/BusBreakerVoltageLevel.hpp +++ b/src/iidm/BusBreakerVoltageLevel.hpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include diff --git a/src/iidm/NodeBreakerVoltageLevelViews.cpp b/src/iidm/NodeBreakerVoltageLevelViews.cpp index f85f43a7..23c9d50a 100644 --- a/src/iidm/NodeBreakerVoltageLevelViews.cpp +++ b/src/iidm/NodeBreakerVoltageLevelViews.cpp @@ -213,6 +213,19 @@ stdcxx::const_range NodeBreakerViewImpl::getNodes() const { return m_voltageLevel.getGraph().getVertices(); } +stdcxx::const_range NodeBreakerViewImpl::getNodesInternalConnectedTo(unsigned long node) const { + const auto& isNull = [this](const unsigned long& edge) { + return !static_cast(m_voltageLevel.getGraph().getEdgeObject(edge)); + }; + + const auto& mapper = [this, node](const unsigned long& edge) -> const unsigned long& { + unsigned long vertex1 = m_voltageLevel.getGraph().getEdgeVertex1(edge); + return vertex1 != node ? m_voltageLevel.getGraph().getEdgeVertex1(edge) : m_voltageLevel.getGraph().getEdgeVertex2(edge); + }; + + return m_voltageLevel.getGraph().getEdgeConnectedToVertex(node) | boost::adaptors::filtered(isNull) | boost::adaptors::transformed(mapper); +} + stdcxx::CReference NodeBreakerViewImpl::getOptionalTerminal(unsigned long node) const { if (m_voltageLevel.getGraph().vertexExists(node)) { return stdcxx::cref(m_voltageLevel.getGraph().getVertexObject(node)); @@ -259,6 +272,30 @@ stdcxx::range NodeBreakerViewImpl::getSwitches() { return m_voltageLevel.getGraph().getEdgeObjects() | boost::adaptors::filtered(filter) | boost::adaptors::transformed(mapper); } +stdcxx::const_range NodeBreakerViewImpl::getSwitches(unsigned long node) const { + const auto& notNull = [](const stdcxx::Reference& sw) { + return static_cast(sw); + }; + + const auto& deref = [](const stdcxx::Reference& sw) -> const Switch& { + return sw.get(); + }; + + return m_voltageLevel.getGraph().getEdgeObjectsConnectedToVertex(node) | boost::adaptors::filtered(notNull) | boost::adaptors::transformed(deref); +} + +stdcxx::range NodeBreakerViewImpl::getSwitches(unsigned long node) { + const auto& notNull = [](const stdcxx::Reference& sw) { + return static_cast(sw); + }; + + const auto& deref = [](const stdcxx::Reference& sw) -> Switch& { + return sw.get(); + }; + + return m_voltageLevel.getGraph().getEdgeObjectsConnectedToVertex(node) | boost::adaptors::filtered(notNull) | boost::adaptors::transformed(deref); +} + stdcxx::CReference NodeBreakerViewImpl::getTerminal(unsigned long node) const { return stdcxx::cref(m_voltageLevel.getTerminal(node)); } diff --git a/src/iidm/NodeBreakerVoltageLevelViews.hpp b/src/iidm/NodeBreakerVoltageLevelViews.hpp index b9224836..400710bd 100644 --- a/src/iidm/NodeBreakerVoltageLevelViews.hpp +++ b/src/iidm/NodeBreakerVoltageLevelViews.hpp @@ -40,6 +40,8 @@ class NodeBreakerViewImpl : public voltage_level::NodeBreakerView { stdcxx::const_range getNodes() const override; + stdcxx::const_range getNodesInternalConnectedTo(unsigned long node) const override; + stdcxx::CReference getOptionalTerminal(unsigned long node) const override; stdcxx::Reference getOptionalTerminal(unsigned long node) override; @@ -54,6 +56,10 @@ class NodeBreakerViewImpl : public voltage_level::NodeBreakerView { stdcxx::range getSwitches() override; + stdcxx::const_range getSwitches(unsigned long node) const override; + + stdcxx::range getSwitches(unsigned long node) override; + stdcxx::CReference getTerminal(unsigned long node) const override; stdcxx::Reference getTerminal(unsigned long node) override; diff --git a/test/iidm/NodeBreakerVoltageLevelTest.cpp b/test/iidm/NodeBreakerVoltageLevelTest.cpp index d2092c60..16aec8ab 100644 --- a/test/iidm/NodeBreakerVoltageLevelTest.cpp +++ b/test/iidm/NodeBreakerVoltageLevelTest.cpp @@ -1434,6 +1434,86 @@ BOOST_AUTO_TEST_CASE(testCalculatedBus) { BOOST_CHECK(!network.getBusBreakerView().getBus("unknownBus")); } +BOOST_AUTO_TEST_CASE(getSwitches) { + Network network("test", "test"); + Substation& s = network.newSubstation() + .setId("S") + .setCountry(Country::FR) + .add(); + VoltageLevel& vl = s.newVoltageLevel() + .setId("VL") + .setNominalV(400.0) + .setTopologyKind(TopologyKind::NODE_BREAKER) + .add(); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS1") + .setNode(0) + .add(); + vl.getNodeBreakerView().newBusbarSection() + .setId("BBS2") + .setNode(1) + .add(); + vl.getNodeBreakerView().newBreaker() + .setId("C") + .setNode1(1) + .setNode2(0) + .setOpen(true) + .add(); + vl.getNodeBreakerView().newBreaker() + .setId("B2") + .setNode1(0) + .setNode2(2) + .setOpen(true) + .add(); + vl.getNodeBreakerView().newBreaker() + .setId("B1") + .setNode1(2) + .setNode2(3) + .setOpen(false) + .add(); + vl.getNodeBreakerView().newBreaker() + .setId("B3") + .setNode1(3) + .setNode2(1) + .setOpen(false) + .add(); + vl.newLoad() + .setId("LD") + .setNode(2) + .setP0(1) + .setQ0(1) + .add(); + + vl.getNodeBreakerView().newInternalConnection().setNode1(2).setNode2(0).add(); + vl.getNodeBreakerView().newInternalConnection().setNode1(0).setNode2(1).add(); + vl.getNodeBreakerView().newInternalConnection().setNode1(3).setNode2(0).add(); + + const VoltageLevel& cVl = vl; + + auto switches = vl.getNodeBreakerView().getSwitches(0); + const auto& cSwitches = cVl.getNodeBreakerView().getSwitches(0); + BOOST_CHECK_EQUAL(2, boost::size(switches)); + BOOST_CHECK_EQUAL(2, boost::size(cSwitches)); + + for (const std::string& name : {"C", "B2"}) { + const auto& lookup = [&name](const Switch& sw) { + return sw.getId() == name; + }; + auto it = std::find_if(switches.begin(), switches.end(), lookup); + BOOST_CHECK(it != switches.end()); + } + + const auto& internalConnections = vl.getNodeBreakerView().getNodesInternalConnectedTo(0); + BOOST_CHECK_EQUAL(3, boost::size(internalConnections)); + for (unsigned long expectedNode : {1, 2, 3}) { + const auto& lookup = [&expectedNode](unsigned long node) { + return node == expectedNode; + }; + const auto& it = std::find_if(internalConnections.begin(), internalConnections.end(), lookup); + BOOST_CHECK(it != internalConnections.end()); + } +} + BOOST_AUTO_TEST_SUITE_END() } // namespace iidm diff --git a/test/math/UndirectedGraphTest.cpp b/test/math/UndirectedGraphTest.cpp index fe6ef28e..99d2c6c3 100644 --- a/test/math/UndirectedGraphTest.cpp +++ b/test/math/UndirectedGraphTest.cpp @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE(getEdgeObject) { graph.addVertex(); graph.addEdge(0, 1, stdcxx::ref(expected)); - const stdcxx::Reference& edge = graph.getEdgeObject(0); + stdcxx::Reference edge = graph.getEdgeObject(0); BOOST_TEST(stdcxx::areSame(expected, edge.get())); const auto& objects = graph.getEdgeObjects(); @@ -348,6 +348,57 @@ BOOST_AUTO_TEST_CASE(traverse) { BOOST_CHECK_EQUAL_COLLECTIONS(encountered.begin(), encountered.end(), encounteredExpected3.begin(), encounteredExpected3.end()); } +BOOST_AUTO_TEST_CASE(testGetEdgesFromVertex) { + UndirectedGraph graph; + graph.addVertex(); + graph.addVertex(); + graph.addVertex(); + graph.addEdge(0, 1, stdcxx::ref()); + graph.addEdge(1, 2, stdcxx::ref()); + + const std::vector& connectedTo1Ref = {0UL, 1UL}; + const std::vector& connectedTo1 = graph.getEdgesConnectedToVertex(1); + BOOST_CHECK_EQUAL_COLLECTIONS(connectedTo1Ref.begin(), connectedTo1Ref.end(), connectedTo1.begin(), connectedTo1.end()); + + const std::vector& connectedTo0Ref = {0UL}; + const std::vector& connectedTo0 = graph.getEdgesConnectedToVertex(0); + BOOST_CHECK_EQUAL_COLLECTIONS(connectedTo0Ref.begin(), connectedTo0Ref.end(), connectedTo0.begin(), connectedTo0.end()); +} + +BOOST_AUTO_TEST_CASE(testGetEdgeObjectFromVertex) { + UndirectedGraph graph; + graph.addVertex(); + graph.addVertex(); + graph.addVertex(); + + E arrow01("Arrow01"); + E arrow12("Arrow12"); + graph.addEdge(0, 1, stdcxx::ref(arrow01)); + graph.addEdge(1, 2, stdcxx::ref(arrow12)); + + const auto& connectedTo0 = graph.getEdgeObjectsConnectedToVertex(0); + BOOST_CHECK_EQUAL(1, boost::size(connectedTo0)); + std::vector refConnectedTo0 = {"Arrow01"}; + for (const std::string& name : refConnectedTo0) { + const auto& lookup = [&name](const stdcxx::Reference& edge) { + return edge.get().getName() == name; + }; + auto it = std::find_if(connectedTo0.begin(), connectedTo0.end(), lookup); + BOOST_CHECK(it != connectedTo0.end()); + } + + const auto& connectedTo1 = graph.getEdgeObjectsConnectedToVertex(1); + BOOST_CHECK_EQUAL(2, boost::size(connectedTo1)); + std::vector refConnectedTo1 = {"Arrow01", "Arrow12"}; + for (const std::string& name : refConnectedTo1) { + const auto& lookup = [&name](const stdcxx::Reference& edge) { + return edge.get().getName() == name; + }; + auto it = std::find_if(connectedTo1.begin(), connectedTo1.end(), lookup); + BOOST_CHECK(it != connectedTo1.end()); + } +} + BOOST_AUTO_TEST_SUITE_END() } // namespace math