Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update traverse API (#362, #367, #394 and #387) #426

Merged
merged 11 commits into from
May 24, 2022
8 changes: 5 additions & 3 deletions include/powsybl/iidm/Terminal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

#include <powsybl/iidm/MultiVariantObject.hpp>
#include <powsybl/iidm/TerminalSet.hpp>
#include <powsybl/iidm/TerminalTopologyTraverser.hpp>
#include <powsybl/iidm/TerminalViews.hpp>
#include <powsybl/iidm/VariantManagerHolder.hpp>
#include <powsybl/iidm/VoltageLevelTopologyTraverser.hpp>
#include <powsybl/stdcxx/reference.hpp>

namespace powsybl {
Expand All @@ -34,6 +34,8 @@ class Terminal : public MultiVariantObject {

using NodeBreakerView = terminal::NodeBreakerView;

using TopologyTraverser = terminal::TopologyTraverser;

public:
template <typename T, typename = typename std::enable_if<std::is_base_of<Connectable, T>::value>::type>
static bool isInstanceOf(const Terminal& terminal);
Expand Down Expand Up @@ -86,9 +88,9 @@ class Terminal : public MultiVariantObject {

Terminal& setQ(double q);

virtual void traverse(voltage_level::TopologyTraverser& traverser) = 0;
virtual bool traverse(TopologyTraverser& traverser) = 0;

virtual void traverse(voltage_level::TopologyTraverser& traverser, TerminalSet& traversedTerminals) = 0;
virtual bool traverse(TopologyTraverser& traverser, TerminalSet& traversedTerminals) = 0;

protected: // MultiVariantObject
void allocateVariantArrayElement(const std::set<unsigned long>& indexes, unsigned long sourceIndex) override;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
/**
* Copyright (c) 2021, RTE (http://www.rte-france.com)
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#ifndef POWSYBL_IIDM_VOLTAGELEVELTOPOLOGYTRAVERSER_HPP
#define POWSYBL_IIDM_VOLTAGELEVELTOPOLOGYTRAVERSER_HPP
#ifndef POWSYBL_IIDM_TERMINALTOPOLOGYTRAVERSER_HPP
#define POWSYBL_IIDM_TERMINALTOPOLOGYTRAVERSER_HPP

#include <powsybl/math/TraverseResult.hpp>

namespace powsybl {

Expand All @@ -15,7 +17,7 @@ namespace iidm {
class Switch;
class Terminal;

namespace voltage_level {
namespace terminal {

class TopologyTraverser {
public:
Expand All @@ -31,15 +33,15 @@ class TopologyTraverser {

TopologyTraverser& operator=(TopologyTraverser&&) noexcept = default;

virtual bool traverse(Terminal& terminal, bool connected) = 0;
virtual math::TraverseResult traverse(Terminal& terminal, bool connected) = 0;

virtual bool traverse(Switch& aSwitch) = 0;
virtual math::TraverseResult traverse(Switch& aSwitch) = 0;
};

} // namespace voltage_level
} // namespace terminal

} // namespace iidm

} // namespace powsybl

#endif // POWSYBL_IIDM_VOLTAGELEVELTOPOLOGYTRAVERSER_HPP
#endif // POWSYBL_IIDM_TERMINALTOPOLOGYTRAVERSER_HPP
3 changes: 0 additions & 3 deletions include/powsybl/iidm/VoltageLevel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include <powsybl/iidm/NetworkRef.hpp>
#include <powsybl/iidm/TerminalSet.hpp>
#include <powsybl/iidm/TopologyKind.hpp>
#include <powsybl/iidm/VoltageLevelTopologyTraverser.hpp>
#include <powsybl/iidm/VoltageLevelViews.hpp>
#include <powsybl/stdcxx/range.hpp>
#include <powsybl/stdcxx/reference.hpp>
Expand Down Expand Up @@ -55,8 +54,6 @@ class VoltageLevel : public Container {

using NodeBreakerView = voltage_level::NodeBreakerView;

using TopologyTraverser = voltage_level::TopologyTraverser;

public: // Identifiable
const Network& getNetwork() const override;

Expand Down
12 changes: 10 additions & 2 deletions include/powsybl/iidm/VoltageLevelViews.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <powsybl/iidm/InternalConnection.hpp>
#include <powsybl/iidm/InternalConnectionAdder.hpp>
#include <powsybl/iidm/SwitchAdder.hpp>
#include <powsybl/math/TraverseResult.hpp>
#include <powsybl/math/Traverser.hpp>
#include <powsybl/stdcxx/range.hpp>
#include <powsybl/stdcxx/reference.hpp>

Expand All @@ -35,6 +37,8 @@ class BusBreakerView {
public:
using SwitchAdder = bus_breaker_view::SwitchAdder;

using TopologyTraverser = std::function<math::TraverseResult(const Bus& bus1, const stdcxx::Reference<Switch>& sw, const Bus& bus2)>;

public:
virtual ~BusBreakerView() noexcept = default;

Expand Down Expand Up @@ -75,6 +79,8 @@ class BusBreakerView {
virtual void removeBus(const std::string& busId) = 0;

virtual void removeSwitch(const std::string& switchId) = 0;

virtual void traverse(const Bus& bus, const TopologyTraverser& traverser) = 0;
};

class BusView {
Expand Down Expand Up @@ -102,7 +108,7 @@ class NodeBreakerView {

using SwitchAdder = node_breaker_view::SwitchAdder;

using Traverser = std::function<bool(unsigned long node1, const stdcxx::Reference<Switch>& sw, unsigned long node2)>;
using TopologyTraverser = std::function<math::TraverseResult(unsigned long node1, const stdcxx::Reference<Switch>& sw, unsigned long node2)>;

public:
virtual ~NodeBreakerView() noexcept = default;
Expand Down Expand Up @@ -171,7 +177,9 @@ class NodeBreakerView {

virtual void removeSwitch(const std::string& switchId) = 0;

virtual void traverse(unsigned long node, const Traverser& traverser) const = 0;
virtual void traverse(unsigned long node, const TopologyTraverser& traverser) const = 0;

virtual void traverse(stdcxx::const_range<unsigned long>& nodes, const TopologyTraverser& traverser) const = 0;
};

} // namespace voltage_level
Expand Down
29 changes: 29 additions & 0 deletions include/powsybl/iidm/util/NodeBreakerTopology.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) 2022, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#ifndef POWSYBL_IIDM_UTIL_NODEBREAKERTOPOLOGY_HPP
#define POWSYBL_IIDM_UTIL_NODEBREAKERTOPOLOGY_HPP

#include <powsybl/iidm/VoltageLevelViews.hpp>

namespace powsybl {

namespace iidm {

namespace NodeBreakerTopology {

unsigned long newStandardConnection(BusbarSection& bb);

void removeIsolatedSwitches(voltage_level::NodeBreakerView& topo);

} // namespace NodeBreakerTopology

} // namespace iidm

} // namespace powsybl

#endif // POWSYBL_IIDM_UTIL_NODEBREAKERTOPOLOGY_HPP
8 changes: 7 additions & 1 deletion include/powsybl/math/TraverseResult.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,14 @@ namespace powsybl {
namespace math {

enum class TraverseResult {
/** Indicates that traversal should continue */
CONTINUE,
TERMINATE

/** Indicates that traversal should terminate on current path */
TERMINATE_PATH,

/** Indicates that traversal should break, i.e., terminate on all paths */
TERMINATE_TRAVERSER
};

} // namespace math
Expand Down
6 changes: 4 additions & 2 deletions include/powsybl/math/UndirectedGraph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@ class UndirectedGraph {

void setVertexObject(unsigned long v, const stdcxx::Reference<V>& object);

void traverse(unsigned long v, const Traverser& traverser) const;
bool traverse(unsigned long v, const Traverser& traverser) const;

void traverse(unsigned long v, const Traverser& traverser, std::vector<bool>& encountered) const;
bool traverse(const stdcxx::const_range<unsigned long>& startingVertices, const Traverser& traverser) const;

bool traverse(unsigned long v, const Traverser& traverser, std::vector<bool>& encountered) const;

bool vertexExists(unsigned long v) const;

Expand Down
42 changes: 34 additions & 8 deletions include/powsybl/math/UndirectedGraph.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -418,14 +418,26 @@ void UndirectedGraph<V, E>::setVertexObject(unsigned long v, const stdcxx::Refer
}

template <typename V, typename E>
void UndirectedGraph<V, E>::traverse(unsigned long v, const Traverser& traverser) const {
bool UndirectedGraph<V, E>::traverse(unsigned long v, const Traverser& traverser) const {
std::vector<bool> encountered(m_vertices.size(), false);

traverse(v, traverser, encountered);
return traverse(v, traverser, encountered);
}

template <typename V, typename E>
void UndirectedGraph<V, E>::traverse(unsigned long v, const Traverser& traverser, std::vector<bool>& encountered) const {
bool UndirectedGraph<V, E>::traverse(const stdcxx::const_range<unsigned long>& startingVertices, const Traverser& traverser) const {
std::vector<bool> encountered(m_vertices.size(), false);

for (unsigned long startingVertex : startingVertices) {
if (!encountered[startingVertex] && !traverse(startingVertex, traverser, encountered)) {
return false;
}
}
return true;
}

template <typename V, typename E>
bool UndirectedGraph<V, E>::traverse(unsigned long v, const Traverser& traverser, std::vector<bool>& encountered) const {
checkVertex(v);

encountered.resize(m_vertices.size(), false);
Expand All @@ -434,20 +446,34 @@ void UndirectedGraph<V, E>::traverse(unsigned long v, const Traverser& traverser
const std::vector<unsigned long>& adjacentEdges = adjacencyList[v];

encountered[v] = true;
bool keepGoing = true;
for (unsigned long e : adjacentEdges) {
const std::unique_ptr<Edge>& edge = m_edges[e];
unsigned long v1 = edge->getVertex1();
unsigned long v2 = edge->getVertex2();
if (!encountered[v1]) {
if (traverser(v2, e, v1) == TraverseResult::CONTINUE) {
const TraverseResult& traverserResult = traverser(v2, e, v1);
if (traverserResult == TraverseResult::CONTINUE) {
encountered[v1] = true;
traverse(v1, traverser, encountered);
keepGoing = traverse(v1, traverser, encountered);
} else if (traverserResult == TraverseResult::TERMINATE_TRAVERSER) {
keepGoing = false;
}
} else if (!encountered[v2]) {
const TraverseResult& traverserResult = traverser(v1, e, v2);
if (traverserResult == TraverseResult::CONTINUE) {
encountered[v2] = true;
keepGoing = traverse(v2, traverser, encountered);
} else if (traverserResult == TraverseResult::TERMINATE_TRAVERSER) {
keepGoing = false;
}
} else if (!encountered[v2] && (traverser(v1, e, v2) == TraverseResult::CONTINUE)) {
encountered[v2] = true;
traverse(v2, traverser, encountered);
}
if (!keepGoing) {
break;
}
}

return keepGoing;
}

template <typename V, typename E>
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ set(IIDM_SOURCES
iidm/util/LimitViolationUtils.cpp
iidm/util/LinkData.cpp
iidm/util/Networks.cpp
iidm/util/NodeBreakerTopology.cpp
iidm/util/Substations.cpp
iidm/util/SV.cpp
iidm/util/TerminalFinder.cpp
Expand Down
72 changes: 42 additions & 30 deletions src/iidm/BusBreakerVoltageLevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,13 @@ const TopologyKind& BusBreakerVoltageLevel::getTopologyKind() const {
return s_topologyKind;
}

math::TraverseResult BusBreakerVoltageLevel::getTraverserResult(TerminalSet& visitedTerminals, BusTerminal& terminal, Terminal::TopologyTraverser& traverser) {
if (visitedTerminals.insert(terminal).second) {
return traverser.traverse(terminal, true);
}
return math::TraverseResult::TERMINATE_PATH;
}

stdcxx::optional<unsigned long> BusBreakerVoltageLevel::getVertex(const std::string& busId, bool throwException) const {
checkNotEmpty(busId, "bus id is null");

Expand Down Expand Up @@ -361,57 +368,62 @@ void BusBreakerVoltageLevel::removeTopology() {
removeAllBuses();
}

void BusBreakerVoltageLevel::traverse(BusTerminal& terminal, VoltageLevel::TopologyTraverser& traverser) const {
bool BusBreakerVoltageLevel::traverse(BusTerminal& terminal, Terminal::TopologyTraverser& traverser) const {
TerminalSet traversedTerminals;
traverse(terminal, traverser, traversedTerminals);
return traverse(terminal, traverser, traversedTerminals);
}

void BusBreakerVoltageLevel::traverse(BusTerminal& terminal, VoltageLevel::TopologyTraverser& traverser, TerminalSet& traversedTerminals) const {
if (traversedTerminals.find(terminal) != traversedTerminals.end()) {
return;
}

TerminalSet nextTerminals;

bool BusBreakerVoltageLevel::traverse(BusTerminal& terminal, Terminal::TopologyTraverser& traverser, TerminalSet& traversedTerminals) const {
// check if we are allowed to traverse the terminal itself
if (traverser.traverse(terminal, terminal.isConnected())) {
traversedTerminals.emplace(terminal);
math::TraverseResult termTraverseResult = getTraverserResult(traversedTerminals, terminal, traverser);
if (termTraverseResult == math::TraverseResult::TERMINATE_TRAVERSER) {
return false;
}

if (termTraverseResult == math::TraverseResult::CONTINUE) {
TerminalSet nextTerminals;
addNextTerminals(terminal, nextTerminals);

// then check we can traverse terminal connected to same bus
// then check we can traverse terminals connected to same bus
unsigned long v = *getVertex(terminal.getConnectableBusId(), true);
ConfiguredBus& bus = m_graph.getVertexObject(v);
for (Terminal& t : bus.getTerminals()) {
if (!stdcxx::areSame(t, terminal) && traverser.traverse(t, t.isConnected())) {
ConfiguredBus& bus = m_graph.getVertexObject(v).get();
for (BusTerminal& t : bus.getTerminals()) {
math::TraverseResult tTraverseResult = getTraverserResult(traversedTerminals, t, traverser);
if (tTraverseResult == math::TraverseResult::TERMINATE_TRAVERSER) {
return false;
}
if (tTraverseResult == math::TraverseResult::CONTINUE) {
addNextTerminals(t, nextTerminals);
}
}

// then go through other buses of the voltage level
m_graph.traverse(v, [this, &traverser, &traversedTerminals, &nextTerminals](unsigned long /*v1*/, unsigned long e, unsigned long v2) {
bool traversalTerminated = !m_graph.traverse(v, [this, &nextTerminals, &traverser, &traversedTerminals](unsigned long /*v1*/, unsigned long e, unsigned long v2) {
Switch& aSwitch = m_graph.getEdgeObject(e);
ConfiguredBus& otherBus = m_graph.getVertexObject(v2);
if (traverser.traverse(aSwitch)) {
if (otherBus.getTerminalCount() == 0) {
return math::TraverseResult::CONTINUE;
}

BusTerminal& otherTerminal = *otherBus.getTerminals().begin();
if (traverser.traverse(otherTerminal, otherTerminal.isConnected())) {
traversedTerminals.emplace(otherTerminal);

const stdcxx::range<BusTerminal>& otherBusTerminals = m_graph.getVertexObject(v2).get().getTerminals();
math::TraverseResult switchTraverseResult = traverser.traverse(aSwitch);
if (switchTraverseResult == math::TraverseResult::CONTINUE && !otherBusTerminals.empty()) {
BusTerminal& otherTerminal = *otherBusTerminals.begin();
math::TraverseResult otherTermTraverseResult = getTraverserResult(traversedTerminals, otherTerminal, traverser);
if (otherTermTraverseResult == math::TraverseResult::CONTINUE) {
addNextTerminals(otherTerminal, nextTerminals);
return math::TraverseResult::CONTINUE;
}
return otherTermTraverseResult;
}
return math::TraverseResult::TERMINATE;
return switchTraverseResult;
});
if (traversalTerminated) {
return false;
}

for (Terminal& t : nextTerminals) {
t.traverse(traverser, traversedTerminals);
for (Terminal& nextTerminal : nextTerminals) {
if (!nextTerminal.traverse(traverser, traversedTerminals)) {
return false;
}
}
}

return true;
}

} // namespace iidm
Expand Down
Loading