From 807aec67264837d4f6f57eac6532e70462a43f9e Mon Sep 17 00:00:00 2001 From: grufoony Date: Mon, 13 Apr 2026 11:21:46 +0200 Subject: [PATCH 1/3] Replace return of `std::unique_ptr&` --- src/dsf/base/Network.hpp | 87 ++--- src/dsf/bindings.cpp | 64 ++-- src/dsf/mobility/FirstOrderDynamics.cpp | 100 +++--- src/dsf/mobility/FirstOrderDynamics.hpp | 23 +- src/dsf/mobility/RoadNetwork.cpp | 97 +++--- src/dsf/mobility/RoadNetwork.hpp | 24 +- test/mobility/Test_dynamics.cpp | 165 +++++---- test/mobility/Test_graph.cpp | 423 ++++++++++++------------ 8 files changed, 475 insertions(+), 508 deletions(-) diff --git a/src/dsf/base/Network.hpp b/src/dsf/base/Network.hpp index da6a8f5e..fcacd02c 100644 --- a/src/dsf/base/Network.hpp +++ b/src/dsf/base/Network.hpp @@ -65,22 +65,22 @@ namespace dsf { /// @brief Get a node by id /// @param nodeId The node's id - /// @return std::unique_ptr const& A const reference to the node - std::unique_ptr const& node(Id nodeId) const; + /// @return node_t& A reference to the node + inline node_t& node(Id nodeId) const { return *m_nodes.at(nodeId); }; /// @brief Get a node by id /// @param nodeId The node's id - /// @return std::unique_ptr& A reference to the node - std::unique_ptr& node(Id nodeId); + /// @return node_t& A reference to the node + inline node_t& node(Id nodeId) { return *m_nodes.at(nodeId); }; /// @brief Get an edge by id /// @param edgeId The edge's id - /// @return std::unique_ptr const& A const reference to the edge - std::unique_ptr const& edge(Id edgeId) const; + /// @return edge_t& A reference to the edge + inline edge_t& edge(Id edgeId) const { return *m_edges.at(edgeId); }; /// @brief Get an edge by id /// @param edgeId The edge's id - /// @return std::unique_ptr& A reference to the edge - std::unique_ptr& edge(Id edgeId); + /// @return edge_t& A reference to the edge + inline edge_t& edge(Id edgeId) { return *m_edges.at(edgeId); } - std::unique_ptr const& edge(Id source, Id target) const; + edge_t& edge(Id source, Id target) const; /// @brief Get a node by id /// @tparam TNode The type of the node /// @param nodeId The node's id @@ -97,8 +97,8 @@ namespace dsf { TEdge& edge(Id edgeId); /// @brief Compute betweenness centralities for all nodes using Brandes' algorithm - /// @tparam WeightFunc A callable type that takes a const reference to a unique_ptr and returns a double representing the edge weight - /// @param getEdgeWeight A callable that takes a const reference to a unique_ptr and returns a double (must be positive) + /// @tparam WeightFunc A callable type that takes a const reference to edge_t and returns a double representing the edge weight + /// @param getEdgeWeight A callable that takes a const reference to edge_t and returns a double (must be positive) /// @details Implements Brandes' algorithm for directed weighted graphs. /// The computed centrality for each node v is: /// C_B(v) = sum_{s != v != t} sigma_st(v) / sigma_st @@ -106,12 +106,12 @@ namespace dsf { /// and sigma_st(v) is the number of those paths passing through v. /// Results are stored via Node::setBetweennessCentrality. template - requires(std::is_invocable_r_v const&>) + requires(std::is_invocable_r_v) void computeBetweennessCentralities(WeightFunc getEdgeWeight); /// @brief Compute edge betweenness centralities for all edges using Brandes' algorithm - /// @tparam WeightFunc A callable type that takes a const reference to a unique_ptr and returns a double representing the edge weight - /// @param getEdgeWeight A callable that takes a const reference to a unique_ptr and returns a double (must be positive) + /// @tparam WeightFunc A callable type that takes a const reference to edge_t and returns a double representing the edge weight + /// @param getEdgeWeight A callable that takes a const reference to edge_t and returns a double (must be positive) /// @details Implements Brandes' algorithm for directed weighted graphs. /// The computed centrality for each edge e is: /// C_B(e) = sum_{s != t} sigma_st(e) / sigma_st @@ -119,7 +119,7 @@ namespace dsf { /// and sigma_st(e) is the number of those paths using edge e. /// Results are stored via Edge::setBetweennessCentrality. template - requires(std::is_invocable_r_v const&>) + requires(std::is_invocable_r_v) void computeEdgeBetweennessCentralities(WeightFunc getEdgeWeight); }; template @@ -213,14 +213,14 @@ namespace dsf { } // Get fresh references to both nodes after all potential vector reallocations - auto const& sourceNode = node(sourceNodeId); - auto const& targetNode = node(targetNodeId); - sourceNode->addOutgoingEdge(tmpEdge.id()); - targetNode->addIngoingEdge(tmpEdge.id()); + auto& sourceNode = node(sourceNodeId); + auto& targetNode = node(targetNodeId); + sourceNode.addOutgoingEdge(tmpEdge.id()); + targetNode.addIngoingEdge(tmpEdge.id()); if (geometry.empty()) { - if (sourceNode->geometry().has_value() && targetNode->geometry().has_value()) { + if (sourceNode.geometry().has_value() && targetNode.geometry().has_value()) { tmpEdge.setGeometry( - dsf::geometry::PolyLine{*sourceNode->geometry(), *targetNode->geometry()}); + dsf::geometry::PolyLine{*sourceNode.geometry(), *targetNode.geometry()}); } } m_edges.emplace(tmpEdge.id(), std::make_unique(std::move(tmpEdge))); @@ -228,28 +228,7 @@ namespace dsf { template requires(std::is_base_of_v && std::is_base_of_v) - std::unique_ptr const& Network::node(Id nodeId) const { - return m_nodes.at(nodeId); - } - template - requires(std::is_base_of_v && std::is_base_of_v) - std::unique_ptr& Network::node(Id nodeId) { - return m_nodes.at(nodeId); - } - template - requires(std::is_base_of_v && std::is_base_of_v) - std::unique_ptr const& Network::edge(Id edgeId) const { - return m_edges.at(edgeId); - } - template - requires(std::is_base_of_v && std::is_base_of_v) - std::unique_ptr& Network::edge(Id edgeId) { - return m_edges.at(edgeId); - } - template - requires(std::is_base_of_v && std::is_base_of_v) - std::unique_ptr const& Network::edge(Id source, - Id target) const { + edge_t& Network::edge(Id source, Id target) const { auto const it = std::find_if( m_edges.cbegin(), m_edges.cend(), [source, target](auto const& pair) { return pair.second->source() == source && pair.second->target() == target; @@ -258,7 +237,7 @@ namespace dsf { throw std::out_of_range( std::format("Edge with source {} and target {} not found.", source, target)); } - return it->second; + return *it->second; } template @@ -266,20 +245,20 @@ namespace dsf { template requires(std::is_base_of_v) TNode& Network::node(Id nodeId) { - return dynamic_cast(*node(nodeId)); + return dynamic_cast(node(nodeId)); } template requires(std::is_base_of_v && std::is_base_of_v) template requires(std::is_base_of_v) TEdge& Network::edge(Id edgeId) { - return dynamic_cast(*edge(edgeId)); + return dynamic_cast(edge(edgeId)); } template requires(std::is_base_of_v && std::is_base_of_v) template - requires(std::is_invocable_r_v const&>) + requires(std::is_invocable_r_v) void Network::computeBetweennessCentralities(WeightFunc getEdgeWeight) { // Initialize all node betweenness centralities to 0 for (auto& [nodeId, pNode] : m_nodes) { @@ -321,12 +300,12 @@ namespace dsf { S.push(v); for (auto const& edgeId : m_nodes.at(v)->outgoingEdges()) { - auto const& pEdge = m_edges.at(edgeId); - Id w = pEdge->target(); + auto const& edgeObj = *m_edges.at(edgeId); + Id w = edgeObj.target(); if (visited.contains(w)) { continue; } - double edgeWeight = getEdgeWeight(pEdge); + double edgeWeight = getEdgeWeight(edgeObj); double newDist = dist[v] + edgeWeight; if (newDist < dist[w]) { @@ -364,7 +343,7 @@ namespace dsf { template requires(std::is_base_of_v && std::is_base_of_v) template - requires(std::is_invocable_r_v const&>) + requires(std::is_invocable_r_v) void Network::computeEdgeBetweennessCentralities( WeightFunc getEdgeWeight) { // Initialize all edge betweenness centralities to 0 @@ -408,12 +387,12 @@ namespace dsf { S.push(v); for (auto const& eId : m_nodes.at(v)->outgoingEdges()) { - auto const& pEdge = m_edges.at(eId); - Id w = pEdge->target(); + auto const& edgeObj = *m_edges.at(eId); + Id w = edgeObj.target(); if (visited.contains(w)) { continue; } - double edgeWeight = getEdgeWeight(pEdge); + double edgeWeight = getEdgeWeight(edgeObj); double newDist = dist[v] + edgeWeight; if (newDist < dist[w]) { diff --git a/src/dsf/bindings.cpp b/src/dsf/bindings.cpp index 41556a0c..91c85c20 100644 --- a/src/dsf/bindings.cpp +++ b/src/dsf/bindings.cpp @@ -258,16 +258,16 @@ PYBIND11_MODULE(dsf_cpp, m) { return self.shortestPath( sourceId, targetId, - [weightFunction](const std::unique_ptr& street) { + [weightFunction](const dsf::mobility::Street& street) { switch (weightFunction) { case dsf::PathWeight::LENGTH: - return street->length(); + return street.length(); case dsf::PathWeight::TRAVELTIME: - return street->length() / street->maxSpeed(); + return street.length() / street.maxSpeed(); case dsf::PathWeight::WEIGHT: - return street->weight(); + return street.weight(); default: - return street->length() / street->maxSpeed(); + return street.length() / street.maxSpeed(); } }, threshold); @@ -289,20 +289,19 @@ PYBIND11_MODULE(dsf_cpp, m) { .def( "computeBetweennessCentralities", [](dsf::mobility::RoadNetwork& self, const std::string& weight) { - auto weightFunc = - [&weight](const std::unique_ptr& street) { - if (weight == "length") { - return street->length(); - } else if (weight == "traveltime") { - return street->length() / street->maxSpeed(); - } else if (weight == "weight") { - return street->weight(); - } else { - throw std::invalid_argument( - "Invalid weight function: '" + weight + - "'. Valid options are: 'length', 'traveltime', 'weight'."); - } - }; + auto weightFunc = [&weight](const dsf::mobility::Street& street) { + if (weight == "length") { + return street.length(); + } else if (weight == "traveltime") { + return street.length() / street.maxSpeed(); + } else if (weight == "weight") { + return street.weight(); + } else { + throw std::invalid_argument( + "Invalid weight function: '" + weight + + "'. Valid options are: 'length', 'traveltime', 'weight'."); + } + }; self.computeBetweennessCentralities(weightFunc); }, pybind11::arg("weight") = "length", @@ -316,20 +315,19 @@ PYBIND11_MODULE(dsf_cpp, m) { .def( "computeEdgeBetweennessCentralities", [](dsf::mobility::RoadNetwork& self, const std::string& weight) { - auto weightFunc = - [&weight](const std::unique_ptr& street) { - if (weight == "length") { - return street->length(); - } else if (weight == "traveltime") { - return street->length() / street->maxSpeed(); - } else if (weight == "weight") { - return street->weight(); - } else { - throw std::invalid_argument( - "Invalid weight function: '" + weight + - "'. Valid options are: 'length', 'traveltime', 'weight'."); - } - }; + auto weightFunc = [&weight](const dsf::mobility::Street& street) { + if (weight == "length") { + return street.length(); + } else if (weight == "traveltime") { + return street.length() / street.maxSpeed(); + } else if (weight == "weight") { + return street.weight(); + } else { + throw std::invalid_argument( + "Invalid weight function: '" + weight + + "'. Valid options are: 'length', 'traveltime', 'weight'."); + } + }; self.computeEdgeBetweennessCentralities(weightFunc); }, pybind11::arg("weight") = "length", diff --git a/src/dsf/mobility/FirstOrderDynamics.cpp b/src/dsf/mobility/FirstOrderDynamics.cpp index 85b2dd0a..ec0408df 100644 --- a/src/dsf/mobility/FirstOrderDynamics.cpp +++ b/src/dsf/mobility/FirstOrderDynamics.cpp @@ -33,8 +33,8 @@ namespace dsf::mobility { m_turnMapping.emplace(edgeId, std::array{-1, -1, -1, -1}); // Turn mappings const auto& srcNodeId = pEdge->target(); - for (auto const& outEdgeId : this->graph().node(srcNodeId)->outgoingEdges()) { - auto const& pStreet{this->graph().edge(outEdgeId)}; + for (auto const& outEdgeId : this->graph().node(srcNodeId).outgoingEdges()) { + auto* pStreet{&this->graph().edge(outEdgeId)}; auto const previousStreetId = pStreet->id(); auto const& delta{pEdge->deltaAngle(pStreet->angle())}; if (std::abs(delta) < std::numbers::pi) { @@ -63,8 +63,8 @@ namespace dsf::mobility { } auto const& streetId = pAgent->streetId(); if (streetId.has_value()) { - auto const& pStreet{this->graph().edge(streetId.value())}; - auto const& pNode{this->graph().node(pStreet->target())}; + auto* pStreet{&this->graph().edge(streetId.value())}; + auto const* pNode{&this->graph().node(pStreet->target())}; auto [it, bInserted] = m_destinationCounts.insert({pNode->id(), 1}); if (!bInserted) { ++it->second; @@ -256,7 +256,7 @@ namespace dsf::mobility { } std::optional FirstOrderDynamics::m_nextStreetId( - const std::unique_ptr& pAgent, const std::unique_ptr& pNode) { + const std::unique_ptr& pAgent, RoadJunction const* pNode) { spdlog::trace("Computing m_nextStreetId for {}", *pAgent); auto const& outgoingEdges = pNode->outgoingEdges(); @@ -268,7 +268,7 @@ namespace dsf::mobility { double stationaryWeightCurrent = 1.0; double bcCurrent{1.0}; if (pAgent->streetId().has_value()) { - auto const& pStreetCurrent{this->graph().edge(pAgent->streetId().value())}; + auto const* pStreetCurrent{&this->graph().edge(pAgent->streetId().value())}; previousNodeId = pStreetCurrent->source(); forbiddenTurns = pStreetCurrent->forbiddenTurns(); speedCurrent = pStreetCurrent->maxSpeed(); @@ -288,7 +288,7 @@ namespace dsf::mobility { double cumulativeProbability = 0.0; for (const auto outEdgeId : outgoingEdges) { - auto const& pStreetOut{this->graph().edge(outEdgeId)}; + auto const* pStreetOut{&this->graph().edge(outEdgeId)}; // Check if this is a valid path target for non-random agents bool bIsPathTarget = false; @@ -372,8 +372,7 @@ namespace dsf::mobility { return fallbackStreetId; } - void FirstOrderDynamics::m_evolveStreet(const std::unique_ptr& pStreet, - bool reinsert_agents) { + void FirstOrderDynamics::m_evolveStreet(Street* pStreet, bool reinsert_agents) { auto const nLanes = pStreet->nLanes(); // Enqueue moving agents if their free time is up while (!pStreet->movingAgents().empty()) { @@ -398,7 +397,7 @@ namespace dsf::mobility { continue; } auto const nextStreetId = - this->m_nextStreetId(pAgent, this->graph().node(pStreet->target())); + this->m_nextStreetId(pAgent, &this->graph().node(pStreet->target())); if (!nextStreetId.has_value()) { spdlog::debug( "No next street found for agent {} at node {}", *pAgent, pStreet->target()); @@ -416,7 +415,7 @@ namespace dsf::mobility { // throw std::runtime_error(std::format( // "No next street found for agent {} at node {}", *pAgent, pStreet->target())); } - auto const& pNextStreet{this->graph().edge(nextStreetId.value())}; + auto const* pNextStreet{&this->graph().edge(nextStreetId.value())}; pAgent->setNextStreetId(pNextStreet->id()); if (nLanes == 1) { pStreet->enqueue(0); @@ -492,7 +491,7 @@ namespace dsf::mobility { *pAgentTemp, *pStreet, directionToString.at(pStreet->laneMapping().at(queueIndex)), - this->graph().node(pStreet->target())->isTrafficLight(), + this->graph().node(pStreet->target()).isTrafficLight(), timeTolerance, timeDiff); // Kill the agent @@ -501,7 +500,7 @@ namespace dsf::mobility { } } pAgentTemp->setSpeed(0.); - const auto& destinationNode{this->graph().node(pStreet->target())}; + auto* destinationNode{&this->graph().node(pStreet->target())}; if (destinationNode->isFull()) { spdlog::trace("Skipping due to full destination node {}", *destinationNode); continue; @@ -528,12 +527,12 @@ namespace dsf::mobility { pStreet->target()); auto const& thisDirection{this->graph() .edge(pAgentTemp->nextStreetId().value()) - ->turnDirection(pStreet->angle())}; + .turnDirection(pStreet->angle())}; if (!intersection.streetPriorities().contains(pStreet->id())) { // I have to check if the agent has right of way auto const& inNeighbours{destinationNode->ingoingEdges()}; for (auto const& inEdgeId : inNeighbours) { - auto const& pStreetTemp{this->graph().edge(inEdgeId)}; + auto const* pStreetTemp{&this->graph().edge(inEdgeId)}; if (pStreetTemp->id() == pStreet->id()) { continue; } @@ -562,9 +561,9 @@ namespace dsf::mobility { auto const& otherDirection{ this->graph() .edge(pAgentTemp2->nextStreetId().value()) - ->turnDirection(this->graph() - .edge(pAgentTemp2->streetId().value()) - ->angle())}; + .turnDirection(this->graph() + .edge(pAgentTemp2->streetId().value()) + .angle())}; if (otherDirection < Direction::LEFT) { spdlog::debug( "Skipping agent emission from street {} -> {} due to right of " @@ -582,7 +581,7 @@ namespace dsf::mobility { if (streetId == pStreet->id()) { continue; } - auto const& pStreetTemp{this->graph().edge(streetId)}; + auto const* pStreetTemp{&this->graph().edge(streetId)}; for (auto i{0}; i < pStreetTemp->nLanes(); ++i) { // check queue is not empty and take the top agent if (pStreetTemp->queue(i).empty()) { @@ -595,9 +594,9 @@ namespace dsf::mobility { auto const& otherDirection{ this->graph() .edge(pAgentTemp2->nextStreetId().value()) - ->turnDirection(this->graph() - .edge(pAgentTemp2->streetId().value()) - ->angle())}; + .turnDirection(this->graph() + .edge(pAgentTemp2->streetId().value()) + .angle())}; if (otherDirection < thisDirection) { spdlog::debug( "Skipping agent emission from street {} -> {} due to right of " @@ -664,7 +663,7 @@ namespace dsf::mobility { if (!pAgentTemp->streetId().has_value()) { spdlog::error("{} has no street id", *pAgentTemp); } - auto const& nextStreet{this->graph().edge(pAgentTemp->nextStreetId().value())}; + auto* nextStreet{&this->graph().edge(pAgentTemp->nextStreetId().value())}; if (nextStreet->isFull()) { spdlog::debug( "Skipping agent emission from street {} -> {} due to full " @@ -696,7 +695,7 @@ namespace dsf::mobility { } } - void FirstOrderDynamics::m_evolveNode(const std::unique_ptr& pNode) { + void FirstOrderDynamics::m_evolveNode(RoadJunction* pNode) { auto const transportCapacity{pNode->transportCapacity()}; for (auto i{0}; i < std::ceil(transportCapacity); ++i) { if (i == std::ceil(transportCapacity) - 1) { @@ -717,7 +716,7 @@ namespace dsf::mobility { } for (auto it{intersection.agents().begin()}; it != intersection.agents().end();) { auto& pAgent{it->second}; - auto const& nextStreet{this->graph().edge(pAgent->nextStreetId().value())}; + auto* nextStreet{&this->graph().edge(pAgent->nextStreetId().value())}; if (nextStreet->isFull()) { spdlog::debug("Next street is full: {}", *nextStreet); if (m_forcePriorities) { @@ -731,7 +730,7 @@ namespace dsf::mobility { ++m_turnCounts[*(pAgent->streetId())][nextStreet->id()]; } pAgent->setStreetId(); - pAgent->setSpeed(this->m_speedFunction(nextStreet)); + pAgent->setSpeed(this->m_speedFunction(*nextStreet)); pAgent->setFreeTime(this->time_step() + std::ceil(nextStreet->length() / pAgent->speed())); spdlog::debug( @@ -752,14 +751,14 @@ namespace dsf::mobility { return; } auto const& pAgentTemp{roundabout.agents().front()}; - auto const& nextStreet{this->graph().edge(pAgentTemp->nextStreetId().value())}; + auto* nextStreet{&this->graph().edge(pAgentTemp->nextStreetId().value())}; if (!(nextStreet->isFull())) { if (!m_turnCounts.empty() && pAgentTemp->streetId().has_value()) { ++m_turnCounts[*(pAgentTemp->streetId())][nextStreet->id()]; } auto pAgent{roundabout.dequeue()}; pAgent->setStreetId(); - pAgent->setSpeed(this->m_speedFunction(nextStreet)); + pAgent->setSpeed(this->m_speedFunction(*nextStreet)); pAgent->setFreeTime(this->time_step() + std::ceil(nextStreet->length() / pAgent->speed())); spdlog::debug( @@ -792,7 +791,7 @@ namespace dsf::mobility { std::advance(nodeIt, nodeDist(this->m_generator)); pAgent->setSrcNodeId(nodeIt->second->id()); } - auto const& pSourceNode{this->graph().node(*(pAgent->srcNodeId()))}; + auto* pSourceNode{&this->graph().node(*(pAgent->srcNodeId()))}; if (pSourceNode->isFull()) { spdlog::debug("Skipping {} due to full source {}", *pAgent, *pSourceNode); ++itAgent; @@ -810,8 +809,7 @@ namespace dsf::mobility { pAgent->setNextStreetId(nextStreetId.value()); } // spdlog::debug("Checking next street {}", pAgent->nextStreetId().value()); - auto const& nextStreet{ - this->graph().edge(pAgent->nextStreetId().value())}; // next street + auto* nextStreet{&this->graph().edge(pAgent->nextStreetId().value())}; if (nextStreet->isFull()) { ++itAgent; spdlog::debug("Skipping {} due to full input {}", *pAgent, *nextStreet); @@ -1100,26 +1098,22 @@ namespace dsf::mobility { m_pathWeight = pathWeight; switch (pathWeight) { case PathWeight::LENGTH: - m_weightFunction = [](std::unique_ptr const& pStreet) { - return pStreet->length(); - }; + m_weightFunction = [](Street const& pStreet) { return pStreet.length(); }; m_weightTreshold = weightTreshold.value_or(1.); break; case PathWeight::TRAVELTIME: - m_weightFunction = [this](std::unique_ptr const& pStreet) { + m_weightFunction = [this](Street const& pStreet) { return this->m_streetEstimatedTravelTime(pStreet); }; m_weightTreshold = weightTreshold.value_or(0.0069); break; case PathWeight::WEIGHT: - m_weightFunction = [](std::unique_ptr const& pStreet) { - return pStreet->weight(); - }; + m_weightFunction = [](Street const& pStreet) { return pStreet.weight(); }; m_weightTreshold = weightTreshold.value_or(1.); break; default: spdlog::error("Invalid weight function. Defaulting to traveltime"); - m_weightFunction = [this](std::unique_ptr const& pStreet) { + m_weightFunction = [this](Street const& pStreet) { return this->m_streetEstimatedTravelTime(pStreet); }; m_weightTreshold = weightTreshold.value_or(0.0069); @@ -1200,7 +1194,7 @@ namespace dsf::mobility { throw std::runtime_error("Turn counts have already been initialized."); } for (auto const& [edgeId, pEdge] : this->graph().edges()) { - auto const& pTargetNode{this->graph().node(pEdge->target())}; + auto const* pTargetNode{&this->graph().node(pEdge->target())}; for (auto const& nextEdgeId : pTargetNode->outgoingEdges()) { spdlog::debug("Initializing turn count for edge {} -> {}", edgeId, nextEdgeId); m_turnCounts[edgeId][nextEdgeId] = 0; @@ -1216,7 +1210,7 @@ namespace dsf::mobility { throw std::runtime_error("Turn counts have not been initialized."); } for (auto const& [edgeId, pEdge] : this->graph().edges()) { - auto const& pTargetNode{this->graph().node(pEdge->target())}; + auto const* pTargetNode{&this->graph().node(pEdge->target())}; for (auto const& nextEdgeId : pTargetNode->outgoingEdges()) { m_turnCounts[edgeId][nextEdgeId] = 0; } @@ -1396,7 +1390,7 @@ namespace dsf::mobility { this->addAgent(pItinerary, street->source()); auto& pAgent{this->m_agents.back()}; pAgent->setStreetId(street->id()); - pAgent->setSpeed(this->m_speedFunction(streetIt->second)); + pAgent->setSpeed(this->m_speedFunction(*streetIt->second)); pAgent->setFreeTime(this->time_step() + std::ceil(street->length() / pAgent->speed())); street->addAgent(std::move(pAgent), this->time_step()); @@ -1482,9 +1476,9 @@ namespace dsf::mobility { tbb::blocked_range(0, numNodes, grainSize), [&](const tbb::blocked_range& range) { for (std::size_t i = range.begin(); i != range.end(); ++i) { - auto const& pNode = this->graph().node(m_nodeIndices[i]); + auto* pNode = &this->graph().node(m_nodeIndices[i]); for (auto const& inEdgeId : pNode->ingoingEdges()) { - auto const& pStreet{this->graph().edge(inEdgeId)}; + auto* pStreet{&this->graph().edge(inEdgeId)}; if (bUpdateData && pNode->isTrafficLight()) { if (!m_queuesAtTrafficLights.contains(inEdgeId)) { auto& tl = dynamic_cast(*pNode); @@ -1552,7 +1546,7 @@ namespace dsf::mobility { tbb::parallel_for(tbb::blocked_range(0, numNodes, grainSize), [&](const tbb::blocked_range& range) { for (size_t i = range.begin(); i != range.end(); ++i) { - const auto& pNode = this->graph().node(m_nodeIndices[i]); + auto* pNode = &this->graph().node(m_nodeIndices[i]); m_evolveNode(pNode); if (pNode->isTrafficLight()) { auto& tl = dynamic_cast(*pNode); @@ -1976,7 +1970,7 @@ namespace dsf::mobility { streetIds.push_back(pair.first); } for (auto const streetId : streetIds) { - auto const& pStreet{this->graph().edge(streetId)}; + auto* pStreet{&this->graph().edge(streetId)}; if (tl.streetPriorities().contains(streetId)) { for (auto& [dir, cycle] : cycles.at(streetId)) { if (isPrioritySinglePhase) { @@ -2044,7 +2038,7 @@ namespace dsf::mobility { for (auto& [direction, cycle] : cycles.at(streetId)) { if (direction == Direction::RIGHT || direction == Direction::STRAIGHT || direction == Direction::RIGHTANDSTRAIGHT) { - auto const& pStreet{this->graph().edge(streetId)}; + auto* pStreet{&this->graph().edge(streetId)}; if (logStream.has_value()) { *logStream << std::format("\tFree cycle for {} -> {}: dir {} - {}\n", pStreet->source(), @@ -2063,7 +2057,7 @@ namespace dsf::mobility { for (auto& [direction, cycle] : cycles.at(streetId)) { if (direction == Direction::RIGHT || direction == Direction::STRAIGHT || direction == Direction::RIGHTANDSTRAIGHT) { - auto const& pStreet{this->graph().edge(streetId)}; + auto* pStreet{&this->graph().edge(streetId)}; if (logStream.has_value()) { *logStream << std::format("Free cycle ({}) for {} -> {}: {} {}\n", directionToString[direction], @@ -2113,8 +2107,8 @@ namespace dsf::mobility { double density{0.}, n{0.}; auto const& inNeighbours{pNode->ingoingEdges()}; for (auto const& inEdgeId : inNeighbours) { - auto const& pStreet{this->graph().edge(inEdgeId)}; - auto const& pSourceNode{this->graph().node(pStreet->source())}; + auto* pStreet{&this->graph().edge(inEdgeId)}; + auto* pSourceNode{&this->graph().node(pStreet->source())}; if (!pSourceNode->isTrafficLight()) { continue; } @@ -2135,9 +2129,9 @@ namespace dsf::mobility { std::unordered_set optimizedNodes; for (auto const& [nodeId, density] : sortedDensities) { - auto const& inNeighbours{this->graph().node(nodeId)->ingoingEdges()}; + auto const& inNeighbours{this->graph().node(nodeId).ingoingEdges()}; for (auto const& inEdgeId : inNeighbours) { - auto const& pStreet{this->graph().edge(inEdgeId)}; + auto* pStreet{&this->graph().edge(inEdgeId)}; auto const& sourceId{pStreet->source()}; if (!densities.contains(sourceId) || optimizedNodes.contains(sourceId)) { continue; @@ -2147,7 +2141,7 @@ namespace dsf::mobility { continue; } // Try to green-wave the situation - auto& tl{dynamic_cast(*this->graph().node(sourceId))}; + auto& tl{dynamic_cast(this->graph().node(sourceId))}; tl.increasePhases(pStreet->length() / (pStreet->maxSpeed() * (1. - 0.6 * pStreet->density(true)))); optimizedNodes.insert(sourceId); diff --git a/src/dsf/mobility/FirstOrderDynamics.hpp b/src/dsf/mobility/FirstOrderDynamics.hpp index 15d3ef32..90be9740 100644 --- a/src/dsf/mobility/FirstOrderDynamics.hpp +++ b/src/dsf/mobility/FirstOrderDynamics.hpp @@ -61,7 +61,7 @@ namespace dsf::mobility { tbb::concurrent_unordered_map m_destinationCounts; std::atomic m_nAgents{0}, m_nAddedAgents{0}, m_nInsertedAgents{0}, m_nKilledAgents{0}, m_nArrivedAgents{0}; - std::function const&)> m_speedFunction; + std::function m_speedFunction; std::string m_speedFunctionDescription; protected: @@ -73,7 +73,7 @@ namespace dsf::mobility { std::time_t m_previousOptimizationTime{0}; protected: - std::function const&)> m_weightFunction; + std::function m_weightFunction; std::optional m_errorProbability{std::nullopt}; std::optional m_passageProbability{std::nullopt}; std::optional m_meanTravelDistance{std::nullopt}; @@ -108,16 +108,16 @@ namespace dsf::mobility { /// @param pNode A std::unique_ptr to the current node /// @return Id The id of the randomly selected next street std::optional m_nextStreetId(const std::unique_ptr& pAgent, - const std::unique_ptr& pNode); + RoadJunction const* pNode); /// @brief Evolve a street /// @param pStreet A std::unique_ptr to the street /// @param reinsert_agents If true, the agents are reinserted in the simulation after they reach their destination /// @details If possible, removes the first agent of the street's queue, putting it in the destination node. /// If the agent is going into the destination node, it is removed from the simulation (and then reinserted if reinsert_agents is true) - void m_evolveStreet(std::unique_ptr const& pStreet, bool reinsert_agents); + void m_evolveStreet(Street* pStreet, bool reinsert_agents); /// @brief If possible, removes one agent from the node, putting it on the next street. /// @param pNode A std::unique_ptr to the node - void m_evolveNode(const std::unique_ptr& pNode); + void m_evolveNode(RoadJunction* pNode); /// @brief Evolve the agents. /// @details Puts all new agents on a street, if possible, decrements all delays /// and increments all travel times. @@ -126,9 +126,8 @@ namespace dsf::mobility { void m_trafficlightSingleTailOptimizer(double const& beta, std::optional& logStream); - inline double m_streetEstimatedTravelTime( - std::unique_ptr const& pStreet) const { - return pStreet->length() / m_speedFunction(pStreet); + inline double m_streetEstimatedTravelTime(Street const& pStreet) const { + return pStreet.length() / m_speedFunction(pStreet); }; /// @brief Initialize the street data table. @@ -470,10 +469,10 @@ namespace dsf::mobility { } else if constexpr (!std::is_invocable_r_v< double, std::tuple_element_t<0, std::tuple>, - std::unique_ptr const&>) { + Street const&>) { throw std::invalid_argument( "Custom speed function requires a callable argument with signature " - "double(std::unique_ptr const&)"); + "double(Street const&)"); } else { m_speedFunction = std::get<0>(std::forward_as_tuple(args...)); m_speedFunctionDescription = "CUSTOM"; @@ -498,8 +497,8 @@ namespace dsf::mobility { throw std::invalid_argument( std::format("The alpha parameter ({}) must be in [0., 1)", alpha)); } - m_speedFunction = [alpha](std::unique_ptr const& pStreet) { - return pStreet->maxSpeed() * (1. - alpha * pStreet->density(true)); + m_speedFunction = [alpha](Street const& pStreet) { + return pStreet.maxSpeed() * (1. - alpha * pStreet.density(true)); }; m_speedFunctionDescription = std::format("LINEAR(alpha={})", alpha); } diff --git a/src/dsf/mobility/RoadNetwork.cpp b/src/dsf/mobility/RoadNetwork.cpp index 63708817..2743e588 100644 --- a/src/dsf/mobility/RoadNetwork.cpp +++ b/src/dsf/mobility/RoadNetwork.cpp @@ -95,22 +95,22 @@ namespace dsf::mobility { return std::tolower(c); }); if (strType.find("motorway") != std::string::npos) { - edge(streetId)->setRoadType(RoadType::HIGHWAY); + edge(streetId).setRoadType(RoadType::HIGHWAY); } else if (strType.find("primary") != std::string::npos) { - edge(streetId)->setRoadType(RoadType::PRIMARY); + edge(streetId).setRoadType(RoadType::PRIMARY); } else if (strType.find("secondary") != std::string::npos) { - edge(streetId)->setRoadType(RoadType::SECONDARY); + edge(streetId).setRoadType(RoadType::SECONDARY); } else if (strType.find("tertiary") != std::string::npos) { - edge(streetId)->setRoadType(RoadType::TERTIARY); + edge(streetId).setRoadType(RoadType::TERTIARY); } else if (strType.find("residential") != std::string::npos) { - edge(streetId)->setRoadType(RoadType::RESIDENTIAL); + edge(streetId).setRoadType(RoadType::RESIDENTIAL); } } if (bHasPriority) { try { if (row["priority"].get()) { - edge(streetId)->setPriority(); + edge(streetId).setPriority(); } } catch (...) { spdlog::warn("Invalid priority for edge {}.", streetId); @@ -131,9 +131,9 @@ namespace dsf::mobility { } if (bHasCustomWeight) { try { - edge(streetId)->setWeight(row["customWeight"].get()); + edge(streetId).setWeight(row["customWeight"].get()); } catch (...) { - spdlog::warn("Invalid custom weight for {}", *edge(streetId)); + spdlog::warn("Invalid custom weight for {}", edge(streetId)); } } } @@ -141,7 +141,7 @@ namespace dsf::mobility { this->m_edges.rehash(0); // Parse forbidden turns // for (auto const& [streetId, forbiddenTurns] : mapForbiddenTurns) { - // auto const& pStreet{edge(streetId)}; + // auto* pStreet{&edge(streetId)}; // std::istringstream iss{forbiddenTurns}; // std::string pair; // while (std::getline(iss, pair, ',')) { @@ -155,7 +155,7 @@ namespace dsf::mobility { // Id const sourceId{std::stoul(strSourceId)}; // Id const targetId{std::stoul(strTargetId)}; - // pStreet->addForbiddenTurn(edge(sourceId, targetId)->id()); + // pStreet->addForbiddenTurn(edge(sourceId, targetId).id()); // } // } } @@ -185,12 +185,12 @@ namespace dsf::mobility { auto const& strGeometry = row["geometry"].get(); if (!strGeometry.empty()) { auto const point = geometry::Point(strGeometry); - auto const& pNode{node(nodeId)}; + auto& nodeRef{node(nodeId)}; // Assign geometry or check if these geometry match the existing ones - if (!pNode->geometry().has_value()) { - pNode->setGeometry(point); + if (!nodeRef.geometry().has_value()) { + nodeRef.setGeometry(point); } else { - auto const& [oldLon, oldLat] = pNode->geometry().value(); + auto const& [oldLon, oldLat] = nodeRef.geometry().value(); auto const& [newLon, newLat] = point; if (std::abs(oldLat - newLat) > 1e-4 || std::abs(oldLon - newLon) > 1e-4) { spdlog::error( @@ -313,15 +313,15 @@ namespace dsf::mobility { return std::tolower(c); }); if (strType.find("motorway") != std::string::npos) { - edge(edge_id)->setRoadType(RoadType::HIGHWAY); + edge(edge_id).setRoadType(RoadType::HIGHWAY); } else if (strType.find("primary") != std::string::npos) { - edge(edge_id)->setRoadType(RoadType::PRIMARY); + edge(edge_id).setRoadType(RoadType::PRIMARY); } else if (strType.find("secondary") != std::string::npos) { - edge(edge_id)->setRoadType(RoadType::SECONDARY); + edge(edge_id).setRoadType(RoadType::SECONDARY); } else if (strType.find("tertiary") != std::string::npos) { - edge(edge_id)->setRoadType(RoadType::TERTIARY); + edge(edge_id).setRoadType(RoadType::TERTIARY); } else if (strType.find("residential") != std::string::npos) { - edge(edge_id)->setRoadType(RoadType::RESIDENTIAL); + edge(edge_id).setRoadType(RoadType::RESIDENTIAL); } } // Check if there is coilcode property @@ -345,7 +345,7 @@ namespace dsf::mobility { if (!edge_properties.at_key("customWeight").error()) { auto const& epCustomWeight = edge_properties["customWeight"]; if (epCustomWeight.is_number()) { - edge(edge_id)->setWeight(epCustomWeight.get_double()); + edge(edge_id).setWeight(epCustomWeight.get_double()); } else { spdlog::warn("Invalid custom weight for edge {}, keeping default", edge_id); } @@ -355,7 +355,7 @@ namespace dsf::mobility { auto const& epPriority = edge_properties["priority"]; if (epPriority.is_bool()) { if (epPriority.get_bool()) { - edge(edge_id)->setPriority(); + edge(edge_id).setPriority(); } } else { spdlog::warn("Invalid priority for edge {}, keeping default", edge_id); @@ -416,7 +416,7 @@ namespace dsf::mobility { return; } for (auto const& edgeId : inNeighbours) { - auto const& pStreet{edge(edgeId)}; + auto* pStreet{&edge(edgeId)}; double const speed{pStreet->maxSpeed()}; int const nLan{pStreet->nLanes()}; @@ -540,7 +540,7 @@ namespace dsf::mobility { greenTimes = std::make_pair(mainGreenTime, secondaryGreenTime); } std::for_each(inNeighbours.begin(), inNeighbours.end(), [&](auto const& edgeId) { - auto const streetId{this->edge(edgeId)->id()}; + auto const streetId{this->edge(edgeId).id()}; auto const nLane{nLanes.at(streetId)}; Delay greenTime{greenTimes.first}; Delay phase{0}; @@ -583,14 +583,14 @@ namespace dsf::mobility { std::for_each(inNeighbours.cbegin(), inNeighbours.cend(), [this, &maxEstimatedFlow](auto const& edgeId) { - auto const& pStreet{this->edge(edgeId)}; + auto* pStreet{&this->edge(edgeId)}; auto const estFlow{pStreet->maxSpeed() * pStreet->nLanes()}; maxEstimatedFlow = std::max(maxEstimatedFlow, estFlow); }); std::for_each(outNeighbours.cbegin(), outNeighbours.cend(), [this, &maxEstimatedFlow](auto const& edgeId) { - auto const& pStreet{this->edge(edgeId)}; + auto* pStreet{&this->edge(edgeId)}; auto const estFlow{pStreet->maxSpeed() * pStreet->nLanes()}; maxEstimatedFlow = std::max(maxEstimatedFlow, estFlow); }); @@ -598,7 +598,7 @@ namespace dsf::mobility { inNeighbours.cbegin(), inNeighbours.cend(), [this, &pNode, &outNeighbours, &maxEstimatedFlow](auto const& edgeId) { - auto const& pInStreet{this->edge(edgeId)}; + auto* pInStreet{&this->edge(edgeId)}; auto const nLanes{pInStreet->nLanes()}; if (nLanes == 1) { return; @@ -608,7 +608,7 @@ namespace dsf::mobility { outNeighbours.cbegin(), outNeighbours.cend(), [this, &pInStreet, &allowedTurns, &maxEstimatedFlow](auto const& edgeId) { - auto const& pOutStreet{this->edge(edgeId)}; + auto const* pOutStreet{&this->edge(edgeId)}; if (pOutStreet->target() == pInStreet->source() || pInStreet->forbiddenTurns().contains(pOutStreet->id())) { return; @@ -621,8 +621,8 @@ namespace dsf::mobility { } // Actually going straight means remain on the same road, thus... auto const inEstFlow{pInStreet->maxSpeed() * pInStreet->nLanes()}; - auto const outEstFlow{outOppositeStreet->get()->maxSpeed() * - outOppositeStreet->get()->nLanes()}; + auto const outEstFlow{outOppositeStreet->maxSpeed() * + outOppositeStreet->nLanes()}; if (((inEstFlow == maxEstimatedFlow) == (outEstFlow == maxEstimatedFlow)) && !allowedTurns.contains(Direction::STRAIGHT)) { @@ -804,7 +804,7 @@ namespace dsf::mobility { // first when selecting streets to mark as priority roads. std::multimap types; for (auto const& edgeId : inNeighbours) { - auto const& pStreet{this->edge(edgeId)}; + auto* pStreet{&this->edge(edgeId)}; auto const roadType = pStreet->roadType(); if (roadType.has_value()) { types.emplace(roadType.value(), pStreet->id()); @@ -837,7 +837,7 @@ namespace dsf::mobility { } for (auto const& streetId : priorityRoads) { - auto const& pStreet{this->edge(streetId)}; + auto* pStreet{&this->edge(streetId)}; pStreet->setPriority(); spdlog::debug("Setting priority to street {}", pStreet->id()); } @@ -858,13 +858,13 @@ namespace dsf::mobility { for (auto const& [_, pNode] : nodes()) { value = 0.; for (auto const& edgeId : pNode->ingoingEdges()) { - auto const& pStreet{this->edge(edgeId)}; + auto* pStreet{&this->edge(edgeId)}; value += pStreet->nLanes() * pStreet->transportCapacity(); } pNode->setCapacity(value); value = 0.; for (auto const& edgeId : pNode->outgoingEdges()) { - auto const& pStreet{this->edge(edgeId)}; + auto* pStreet{&this->edge(edgeId)}; value += pStreet->nLanes() * pStreet->transportCapacity(); } pNode->setTransportCapacity(value == 0. ? 1. : value); @@ -896,13 +896,14 @@ namespace dsf::mobility { auto const cycleTime{static_cast(std::stoul(strCycleTime))}; // Cast node(id) to traffic light - auto& pNode{node(std::stoul(strId))}; + auto const nodeId = static_cast(std::stoul(strId)); + auto& pNode = m_nodes.at(nodeId); if (!pNode->isTrafficLight()) { pNode = std::make_unique( pNode->id(), cycleTime, pNode->geometry().value()); } auto& tl = static_cast(*pNode); - auto const streetId{edge(std::stoul(streetSource), pNode->id())->id()}; + auto const streetId{edge(std::stoul(streetSource), pNode->id()).id()}; auto const greenTime{static_cast(std::stoul(strGT))}; if (!storedGreenTimes.contains(pNode->id())) { storedGreenTimes.emplace(pNode->id(), greenTime); @@ -921,24 +922,24 @@ namespace dsf::mobility { TrafficLight& RoadNetwork::makeTrafficLight(Id const nodeId, Delay const cycleTime, Delay const counter) { - auto& pNode = node(nodeId); + auto& pNode = m_nodes.at(nodeId); pNode = std::make_unique(*pNode, cycleTime, counter); return node(nodeId); } Roundabout& RoadNetwork::makeRoundabout(Id nodeId) { - auto& pNode = node(nodeId); + auto& pNode = m_nodes.at(nodeId); pNode = std::make_unique(*pNode); return node(nodeId); } Station& RoadNetwork::makeStation(Id nodeId, const unsigned int managementTime) { - auto& pNode = node(nodeId); + auto& pNode = m_nodes.at(nodeId); pNode = std::make_unique(*pNode, managementTime); return node(nodeId); } void RoadNetwork::addCoil(Id streetId, std::string const& name) { - edge(streetId)->enableCounter(name); + edge(streetId).enableCounter(name); } void RoadNetwork::addStreet(Street&& street) { @@ -966,7 +967,7 @@ namespace dsf::mobility { void RoadNetwork::setStreetStatusById(Id const streetId, RoadStatus const status) { try { - auto const& pStreet{edge(streetId)}; + auto* pStreet{&edge(streetId)}; pStreet->setStatus(status); spdlog::info("Changed status of {} to {}", *pStreet, status); } catch (const std::out_of_range&) { @@ -994,7 +995,7 @@ namespace dsf::mobility { int const nLanes, std::optional const speedFactor) { try { - edge(streetId)->changeNLanes(nLanes, speedFactor); + edge(streetId).changeNLanes(nLanes, speedFactor); } catch (const std::out_of_range&) { throw std::out_of_range(std::format("Street with id {} not found", streetId)); } @@ -1022,7 +1023,7 @@ namespace dsf::mobility { } void RoadNetwork::changeStreetCapacityById(Id const streetId, double const factor) { try { - auto const& pStreet{edge(streetId)}; + auto* pStreet{&edge(streetId)}; auto const& currentCapacity{pStreet->capacity()}; pStreet->setCapacity(std::ceil(currentCapacity * factor)); } catch (const std::out_of_range&) { @@ -1065,12 +1066,14 @@ namespace dsf::mobility { }); } - const std::unique_ptr* RoadNetwork::street(Id source, Id destination) const { - // Get the iterator at id m_cantorPairingHashing(source, destination) - try { - return &(edge(source, destination)); - } catch (const std::out_of_range&) { + Street const* RoadNetwork::street(Id source, Id destination) const { + auto const it = std::find_if( + m_edges.cbegin(), m_edges.cend(), [source, destination](auto const& pair) { + return pair.second->source() == source && pair.second->target() == destination; + }); + if (it == m_edges.cend()) { return nullptr; } + return it->second.get(); } } // namespace dsf::mobility \ No newline at end of file diff --git a/src/dsf/mobility/RoadNetwork.hpp b/src/dsf/mobility/RoadNetwork.hpp index a1784e7e..5dd5ad9e 100644 --- a/src/dsf/mobility/RoadNetwork.hpp +++ b/src/dsf/mobility/RoadNetwork.hpp @@ -233,8 +233,8 @@ namespace dsf::mobility { /// @brief Get a street from the graph /// @param source The source node /// @param destination The destination node - /// @return A std::unique_ptr to the street if it exists, nullptr otherwise - const std::unique_ptr* street(Id source, Id destination) const; + /// @return A pointer to the street if it exists, nullptr otherwise + Street const* street(Id source, Id destination) const; /// @brief Get the maximum agent capacity /// @return std::size_t The maximum agent capacity of the graph @@ -248,7 +248,7 @@ namespace dsf::mobility { /// @return A map where each key is a node id and the value is a vector of next hop node ids toward the target /// @throws std::out_of_range if the target node does not exist template - requires(std::is_invocable_r_v const&>) + requires(std::is_invocable_r_v) PathCollection allPathsTo(Id const targetId, DynamicsFunc getEdgeWeight, double const threshold = 1e-9) const; @@ -264,7 +264,7 @@ namespace dsf::mobility { /// @details Uses Dijkstra's algorithm to find shortest paths from source to target. /// Like allPathsTo, this method tracks all equivalent paths within the threshold, allowing for multiple next hops per node. template - requires(std::is_invocable_r_v const&>) + requires(std::is_invocable_r_v) PathCollection shortestPath(Id const sourceId, Id const targetId, DynamicsFunc getEdgeWeight, @@ -354,7 +354,7 @@ namespace dsf::mobility { } template - requires(std::is_invocable_r_v const&>) + requires(std::is_invocable_r_v) PathCollection RoadNetwork::allPathsTo(Id const targetId, DynamicsFunc f, double const threshold) const { @@ -392,13 +392,13 @@ namespace dsf::mobility { } // Explore all incoming edges (nodes that can reach currentNode) - auto const& inEdges = node(currentNode)->ingoingEdges(); + auto const& inEdges = node(currentNode).ingoingEdges(); for (auto const& inEdgeId : inEdges) { // Skip closed roads - if (edge(inEdgeId)->roadStatus() == RoadStatus::CLOSED) { + if (edge(inEdgeId).roadStatus() == RoadStatus::CLOSED) { continue; } - Id neighborId = edge(inEdgeId)->source(); + Id neighborId = edge(inEdgeId).source(); // Calculate the weight of the edge from neighbor to currentNode using the dynamics function double edgeWeight = f(this->edge(inEdgeId)); @@ -443,7 +443,7 @@ namespace dsf::mobility { } template - requires(std::is_invocable_r_v const&>) + requires(std::is_invocable_r_v) PathCollection RoadNetwork::shortestPath(Id const sourceId, Id const targetId, DynamicsFunc f, @@ -501,13 +501,13 @@ namespace dsf::mobility { } // Explore all incoming edges (nodes that can reach currentNode) - auto const& inEdges = node(currentNode)->ingoingEdges(); + auto const& inEdges = node(currentNode).ingoingEdges(); for (auto const& inEdgeId : inEdges) { // Skip closed roads - if (edge(inEdgeId)->roadStatus() == RoadStatus::CLOSED) { + if (edge(inEdgeId).roadStatus() == RoadStatus::CLOSED) { continue; } - Id neighborId = edge(inEdgeId)->source(); + Id neighborId = edge(inEdgeId).source(); // Calculate the weight of the edge from neighbor to currentNode using the dynamics function double edgeWeight = f(this->edge(inEdgeId)); diff --git a/test/mobility/Test_dynamics.cpp b/test/mobility/Test_dynamics.cpp index ace1207f..6f29e645 100644 --- a/test/mobility/Test_dynamics.cpp +++ b/test/mobility/Test_dynamics.cpp @@ -89,7 +89,7 @@ TEST_CASE("FirstOrderDynamics") { auto& tl = defaultNetwork.makeTrafficLight(0, 2); FirstOrderDynamics dynamics{defaultNetwork, false, 69}; THEN("The node is a traffic light") { - CHECK(dynamics.graph().node(0)->isTrafficLight()); + CHECK(dynamics.graph().node(0).isTrafficLight()); CHECK_EQ(tl.cycleTime(), 2); } } @@ -97,13 +97,13 @@ TEST_CASE("FirstOrderDynamics") { defaultNetwork.makeRoundabout(0); FirstOrderDynamics dynamics{defaultNetwork, false, 69}; THEN("The node is a roundabout") { - CHECK(dynamics.graph().node(0)->isRoundabout()); + CHECK(dynamics.graph().node(0).isRoundabout()); } } WHEN("We put a coil on the street and create the dynamics") { defaultNetwork.addCoil(8); FirstOrderDynamics dynamics{defaultNetwork, false, 69}; - THEN("The street has a coil") { CHECK(dynamics.graph().edge(8)->hasCoil()); } + THEN("The street has a coil") { CHECK(dynamics.graph().edge(8).hasCoil()); } } WHEN("We call summary") { FirstOrderDynamics dynamics{defaultNetwork, false, 69}; @@ -210,24 +210,19 @@ TEST_CASE("FirstOrderDynamics") { "the itinerary") { CHECK_EQ(dynamics.nAgents(), 3); #ifdef __APPLE__ - CHECK_EQ(dynamics.graph().edge(422)->nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(422)->movingAgents().top()->itinerary()->id(), - 2); - CHECK_EQ(dynamics.graph().edge(199)->nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(199)->movingAgents().top()->itinerary()->id(), - 2); - CHECK_EQ(dynamics.graph().edge(166)->nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(166)->movingAgents().top()->itinerary()->id(), - 1); + CHECK_EQ(dynamics.graph().edge(422).nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(422).movingAgents().top()->itinerary()->id(), 2); + CHECK_EQ(dynamics.graph().edge(199).nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(199).movingAgents().top()->itinerary()->id(), 2); + CHECK_EQ(dynamics.graph().edge(166).nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(166).movingAgents().top()->itinerary()->id(), 1); #else - CHECK_EQ(dynamics.graph().edge(13)->nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(13)->movingAgents().top()->itinerary()->id(), 2); - CHECK_EQ(dynamics.graph().edge(370)->nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(370)->movingAgents().top()->itinerary()->id(), - 1); - CHECK_EQ(dynamics.graph().edge(404)->nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(404)->movingAgents().top()->itinerary()->id(), - 2); + CHECK_EQ(dynamics.graph().edge(13).nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(13).movingAgents().top()->itinerary()->id(), 2); + CHECK_EQ(dynamics.graph().edge(370).nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(370).movingAgents().top()->itinerary()->id(), 1); + CHECK_EQ(dynamics.graph().edge(404).nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(404).movingAgents().top()->itinerary()->id(), 2); #endif } } @@ -602,8 +597,8 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); // Agent goes into node 0 dynamics.evolve(false); // Agent goes from node 0 to street 0->1 THEN("The agent evolves") { - CHECK_EQ(network.edge(0)->movingAgents().size(), 1); - auto const& pAgent{network.edge(0)->movingAgents().top()}; + CHECK_EQ(network.edge(0).movingAgents().size(), 1); + auto const& pAgent{network.edge(0).movingAgents().top()}; CHECK(pAgent); CHECK_EQ(dynamics.time_step() - pAgent->spawnTime(), dynamics.time_step()); CHECK_EQ(pAgent->freeTime(), dynamics.time_step()); @@ -612,7 +607,7 @@ TEST_CASE("FirstOrderDynamics") { } dynamics.evolve(false); // Agent enqueues on street 0->1 and changes street THEN("The agent evolves again, changing street") { - auto const& pAgent{network.edge(1)->movingAgents().top()}; + auto const& pAgent{network.edge(1).movingAgents().top()}; CHECK_EQ(dynamics.time_step() - pAgent->spawnTime(), dynamics.time_step()); CHECK_EQ(pAgent->freeTime(), dynamics.time_step()); CHECK_EQ(pAgent->streetId().value(), 1); @@ -638,7 +633,7 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); dynamics.evolve(false); THEN("The agent evolves") { - auto const& pAgent{dynamics.graph().edge(0)->movingAgents().top()}; + auto const& pAgent{dynamics.graph().edge(0).movingAgents().top()}; CHECK_EQ(dynamics.time_step() - pAgent->spawnTime(), 2); CHECK_EQ(pAgent->freeTime(), dynamics.time_step()); CHECK_EQ(pAgent->streetId().value(), 0); @@ -666,7 +661,7 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); dynamics.evolve(false); THEN("The agent evolves") { - auto const& pAgent{dynamics.graph().edge(0)->movingAgents().top()}; + auto const& pAgent{dynamics.graph().edge(0).movingAgents().top()}; CHECK_EQ(dynamics.time_step() - pAgent->spawnTime(), 2); CHECK_EQ(pAgent->freeTime(), dynamics.time_step()); CHECK_EQ(pAgent->streetId().value(), 0); @@ -699,7 +694,7 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(true); dynamics.evolve(true); THEN("The agent has correct values") { - auto const& pAgent{dynamics.graph().edge(0)->movingAgents().top()}; + auto const& pAgent{dynamics.graph().edge(0).movingAgents().top()}; CHECK_EQ(dynamics.time_step() - pAgent->spawnTime(), 2); CHECK_EQ(pAgent->freeTime(), dynamics.time_step()); CHECK_EQ(pAgent->streetId().value(), 0); @@ -709,7 +704,7 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(true); // dynamics.evolve(true); THEN("The agent is reinserted") { - CHECK(dynamics.graph().node(0)->density() > 0.); + CHECK(dynamics.graph().node(0).density() > 0.); CHECK_EQ(dynamics.nAgents(), 1); } } @@ -738,14 +733,14 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); dynamics.evolve(false); THEN("The agent goes first into node 2") { - auto const& pAgent{dynamics.graph().edge(5)->queue(0).front()}; + auto const& pAgent{dynamics.graph().edge(5).queue(0).front()}; CHECK_EQ(pAgent->streetId().value(), 5); CHECK_EQ(pAgent->distance(), 60.); } dynamics.evolve(false); dynamics.evolve(false); THEN("The agent goes then to node 1") { - auto const& pAgent{dynamics.graph().edge(7)->queue(0).front()}; + auto const& pAgent{dynamics.graph().edge(7).queue(0).front()}; CHECK_EQ(pAgent->streetId().value(), 7); CHECK_EQ(pAgent->distance(), 90.); } @@ -777,14 +772,14 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); // Agent moves onto street 0 THEN("The agent is alive and on street 0") { CHECK_EQ(dynamics.nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(0)->nMovingAgents(), 1); - CHECK_EQ(dynamics.graph().edge(0)->nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(0).nMovingAgents(), 1); + CHECK_EQ(dynamics.graph().edge(0).nAgents(), 1); } dynamics.evolve(false); // Agent reaches dead-end at node 1: killed THEN("The agent is killed instead of throwing an exception") { CHECK_EQ(dynamics.nAgents(), 0); - CHECK_EQ(dynamics.graph().edge(0)->nMovingAgents(), 0); - CHECK_EQ(dynamics.graph().edge(0)->nAgents(), 0); + CHECK_EQ(dynamics.graph().edge(0).nMovingAgents(), 0); + CHECK_EQ(dynamics.graph().edge(0).nAgents(), 0); } } } @@ -822,15 +817,15 @@ TEST_CASE("FirstOrderDynamics") { for (uint8_t i{0}; i < 5; ++i) { dynamics.evolve(false); if (i < 3) { - CHECK_EQ(network.edge(1)->nAgents(), 1); + CHECK_EQ(network.edge(1).nAgents(), 1); } else { - CHECK_EQ(network.edge(7)->nAgents(), 1); + CHECK_EQ(network.edge(7).nAgents(), 1); } if (i == 2) { - CHECK_EQ(network.edge(1)->queue(0).front()->distance(), 30.); + CHECK_EQ(network.edge(1).queue(0).front()->distance(), 30.); } } - CHECK_EQ(network.edge(7)->queue(0).front()->distance(), 60.); + CHECK_EQ(network.edge(7).queue(0).front()->distance(), 60.); } // Logger::setLogLevel(dsf::log_level_t::INFO); } @@ -883,18 +878,18 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); // Counter 0 dynamics.evolve(false); // Counter 1 THEN("The agents are correctly placed") { - CHECK_EQ(dynamics.graph().edge(1)->nAgents(), 2); + CHECK_EQ(dynamics.graph().edge(1).nAgents(), 2); } dynamics.evolve(false); // Counter 2 dynamics.evolve(false); // Counter 3 THEN("The agent 0 passes and agent 1 waits") { - CHECK_EQ(dynamics.graph().edge(1)->nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(7)->nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(1).nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(7).nAgents(), 1); } dynamics.evolve(false); // Counter 4 THEN("The agent 1 passes") { - CHECK_EQ(dynamics.graph().edge(7)->nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(9)->nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(7).nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(9).nAgents(), 1); } // Logger::setLogLevel(dsf::log_level_t::INFO); } @@ -944,28 +939,28 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); // Counter 0 dynamics.evolve(false); // Counter 1 THEN("The agents are correctly placed") { - CHECK_EQ(dynamics.graph().edge(1)->nAgents(), 2); + CHECK_EQ(dynamics.graph().edge(1).nAgents(), 2); } dynamics.evolve(false); // Counter 2 dynamics.evolve(false); // Counter 3 THEN("The agents are still") { - CHECK_EQ(dynamics.graph().edge(1)->nExitingAgents(), 2); - CHECK_EQ(dynamics.graph().edge(1)->nExitingAgents(Direction::ANY, true), + CHECK_EQ(dynamics.graph().edge(1).nExitingAgents(), 2); + CHECK_EQ(dynamics.graph().edge(1).nExitingAgents(Direction::ANY, true), doctest::Approx(0.666667)); - CHECK_EQ(dynamics.graph().edge(1)->nExitingAgents(Direction::RIGHT), 0); - CHECK_EQ(dynamics.graph().edge(1)->nExitingAgents(Direction::STRAIGHT), 1); - CHECK_EQ(dynamics.graph().edge(1)->nExitingAgents(Direction::LEFT), 1); + CHECK_EQ(dynamics.graph().edge(1).nExitingAgents(Direction::RIGHT), 0); + CHECK_EQ(dynamics.graph().edge(1).nExitingAgents(Direction::STRAIGHT), 1); + CHECK_EQ(dynamics.graph().edge(1).nExitingAgents(Direction::LEFT), 1); } dynamics.evolve(false); // Counter 4 dynamics.evolve(false); // Counter 5 dynamics.evolve(false); // Counter 0 THEN("The agent 0 passes and agent 1 waits") { - CHECK_EQ(dynamics.graph().edge(1)->nAgents(), 1); - CHECK_EQ(dynamics.graph().edge(7)->nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(1).nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(7).nAgents(), 1); } dynamics.evolve(false); // Counter 1 dynamics.evolve(false); // Counter 2 - THEN("The agent 1 passes") { CHECK_EQ(dynamics.graph().edge(9)->nAgents(), 1); } + THEN("The agent 1 passes") { CHECK_EQ(dynamics.graph().edge(9).nAgents(), 1); } } } SUBCASE("Traffic Lights optimization algorithm") { @@ -1064,22 +1059,22 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); // Agents from queues to roundabout auto const& network{dynamics.graph()}; THEN("The agents are trapped into the roundabout") { - CHECK_EQ(network.edge(1)->nAgents(), 0); - CHECK_EQ(network.edge(5)->nAgents(), 1); - CHECK_EQ(network.edge(7)->nAgents(), 1); + CHECK_EQ(network.edge(1).nAgents(), 0); + CHECK_EQ(network.edge(5).nAgents(), 1); + CHECK_EQ(network.edge(7).nAgents(), 1); CHECK_EQ(rb.agents().size(), 1); } dynamics.evolve(false); THEN("The agent with priority leaves the roundabout") { - CHECK_EQ(network.edge(3)->nAgents(), 1); - CHECK_EQ(network.edge(5)->nAgents(), 1); - CHECK_EQ(network.edge(7)->nAgents(), 0); + CHECK_EQ(network.edge(3).nAgents(), 1); + CHECK_EQ(network.edge(5).nAgents(), 1); + CHECK_EQ(network.edge(7).nAgents(), 0); CHECK(rb.agents().empty()); } dynamics.evolve(false); THEN("The agent with priority leaves the roundabout") { - CHECK_EQ(network.edge(3)->nAgents(), 0); - CHECK_EQ(network.edge(5)->nAgents(), 0); + CHECK_EQ(network.edge(3).nAgents(), 0); + CHECK_EQ(network.edge(5).nAgents(), 0); CHECK(rb.agents().empty()); } } @@ -1102,7 +1097,7 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); dynamics.evolve(false); THEN("The agent has travelled the correct distance") { - auto const& pAgent{dynamics.graph().edge(5)->movingAgents().top()}; + auto const& pAgent{dynamics.graph().edge(5).movingAgents().top()}; CHECK_EQ(dynamics.time_step() - pAgent->spawnTime(), 3); CHECK_EQ(pAgent->freeTime(), dynamics.time_step()); CHECK_EQ(pAgent->streetId().value(), 5); @@ -1160,9 +1155,9 @@ TEST_CASE("FirstOrderDynamics") { FirstOrderDynamics dynamics{graph2, false, 69}; dynamics.setWeightFunction(dsf::PathWeight::LENGTH, 0.); dynamics.setSpeedFunction(dsf::SpeedFunction::LINEAR, 0.8); - dynamics.graph().node(0)->setCapacity(3); - dynamics.graph().node(0)->setTransportCapacity(1); - auto& nodeO{dynamic_cast(*dynamics.graph().node(0))}; + dynamics.graph().node(0).setCapacity(3); + dynamics.graph().node(0).setTransportCapacity(1); + auto& nodeO{dynamic_cast(dynamics.graph().node(0))}; dynamics.addItinerary(1, 1); dynamics.addItinerary(2, 2); dynamics.updatePaths(); @@ -1175,19 +1170,19 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); dynamics.evolve(false); THEN("The agent in A passes first") { - CHECK_EQ(dynamics.graph().edge(2)->nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(2).nAgents(), 1); CHECK_EQ(nodeO.agents().size(), 2); CHECK_EQ(nodeO.agents().begin()->second->streetId().value(), 20); } dynamics.evolve(false); THEN("The agent in D passes second") { - CHECK_EQ(dynamics.graph().edge(2)->nAgents(), 2); + CHECK_EQ(dynamics.graph().edge(2).nAgents(), 2); CHECK_EQ(nodeO.agents().size(), 1); CHECK_EQ(nodeO.agents().begin()->second->streetId().value(), 15); } dynamics.evolve(false); THEN("The agent in C passes last") { - CHECK_EQ(dynamics.graph().edge(2)->nAgents(), 3); + CHECK_EQ(dynamics.graph().edge(2).nAgents(), 3); CHECK(nodeO.agents().empty()); } } @@ -1199,19 +1194,19 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); dynamics.evolve(false); THEN("The agent in D passes first") { - CHECK_EQ(dynamics.graph().edge(1)->nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(1).nAgents(), 1); CHECK_EQ(nodeO.agents().size(), 2); CHECK_EQ(nodeO.agents().begin()->second->streetId().value(), 15); } dynamics.evolve(false); THEN("The agent in C passes second") { - CHECK_EQ(dynamics.graph().edge(1)->nAgents(), 2); + CHECK_EQ(dynamics.graph().edge(1).nAgents(), 2); CHECK_EQ(nodeO.agents().size(), 1); CHECK_EQ(nodeO.agents().begin()->second->streetId().value(), 10); } dynamics.evolve(false); THEN("The agent in C passes last") { - CHECK_EQ(dynamics.graph().edge(1)->nAgents(), 3); + CHECK_EQ(dynamics.graph().edge(1).nAgents(), 3); CHECK(nodeO.agents().empty()); } } @@ -1224,7 +1219,7 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); dynamics.evolve(false); THEN("The agent in A passes first because it's on the main road") { - CHECK_EQ(dynamics.graph().edge(2)->nAgents(), 1); + CHECK_EQ(dynamics.graph().edge(2).nAgents(), 1); CHECK_EQ(nodeO.agents().size(), 1); CHECK_EQ(nodeO.agents().begin()->second->streetId().value(), 20); } @@ -1608,7 +1603,7 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); dynamics.evolve(false); dynamics.evolve(false); - CHECK_EQ(dynamics.graph().edge(0)->nAgents(), 6); + CHECK_EQ(dynamics.graph().edge(0).nAgents(), 6); dynamics.evolve(false); dynamics.evolve(false); dynamics.evolve(false); @@ -1618,9 +1613,9 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); THEN("The distribution of agents follows the transition probabilities") { - CHECK_EQ(dynamics.graph().edge(0)->nAgents(), 0); - CHECK_EQ(dynamics.graph().edge(1)->nAgents(), 4); - CHECK_EQ(dynamics.graph().edge(2)->nAgents(), 2); + CHECK_EQ(dynamics.graph().edge(0).nAgents(), 0); + CHECK_EQ(dynamics.graph().edge(1).nAgents(), 4); + CHECK_EQ(dynamics.graph().edge(2).nAgents(), 2); } // spdlog::set_level(spdlog::level::info); } @@ -1658,7 +1653,7 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); dynamics.evolve(false); dynamics.evolve(false); - CHECK_EQ(dynamics.graph().edge(0)->nAgents(), 6); + CHECK_EQ(dynamics.graph().edge(0).nAgents(), 6); dynamics.evolve(false); dynamics.evolve(false); dynamics.evolve(false); @@ -1667,10 +1662,10 @@ TEST_CASE("FirstOrderDynamics") { dynamics.evolve(false); THEN("The distribution of agents follows the transition probabilities") { - CHECK_EQ(dynamics.graph().edge(0)->nAgents(), 0); - CHECK_EQ(dynamics.graph().edge(1)->nAgents(), 2); - CHECK_EQ(dynamics.graph().edge(2)->nAgents(), 2); - CHECK_EQ(dynamics.graph().edge(3)->nAgents(), 2); + CHECK_EQ(dynamics.graph().edge(0).nAgents(), 0); + CHECK_EQ(dynamics.graph().edge(1).nAgents(), 2); + CHECK_EQ(dynamics.graph().edge(2).nAgents(), 2); + CHECK_EQ(dynamics.graph().edge(3).nAgents(), 2); } } } @@ -1722,13 +1717,13 @@ TEST_CASE("Stationary Weights Impact on Random Navigation") { // Set stationary weights // Street 0: weight 1.0 - network.edge(0)->setStationaryWeight(1.0); + network.edge(0).setStationaryWeight(1.0); // Street 1: weight 1.0 - network.edge(1)->setStationaryWeight(1.0); + network.edge(1).setStationaryWeight(1.0); // Street 2: weight 4.0 - network.edge(2)->setStationaryWeight(4.0); + network.edge(2).setStationaryWeight(4.0); // Adjust node capacities to match street capacities network.adjustNodeCapacities(); @@ -1743,7 +1738,7 @@ TEST_CASE("Stationary Weights Impact on Random Navigation") { agent->setStreetId(0); agent->setSpeed(10.0); agent->setFreeTime(0); - dynamics.graph().edge(0)->addAgent(std::move(agent), dynamics.time_step()); + dynamics.graph().edge(0).addAgent(std::move(agent), dynamics.time_step()); } // Evolve simulation @@ -1754,8 +1749,8 @@ TEST_CASE("Stationary Weights Impact on Random Navigation") { dynamics.evolve(); // Count agents on Street 1 and Street 2 - size_t countStreet1 = dynamics.graph().edge(1)->nAgents(); - size_t countStreet2 = dynamics.graph().edge(2)->nAgents(); + size_t countStreet1 = dynamics.graph().edge(1).nAgents(); + size_t countStreet2 = dynamics.graph().edge(2).nAgents(); // Expected probabilities: // P(1) ~ speed * speed * sqrt(1/1) = 100 diff --git a/test/mobility/Test_graph.cpp b/test/mobility/Test_graph.cpp index 1ec8b1fc..b36be8e8 100644 --- a/test/mobility/Test_graph.cpp +++ b/test/mobility/Test_graph.cpp @@ -52,11 +52,11 @@ TEST_CASE("RoadNetwork") { graph.addEdge(5, std::make_pair(2, 3)); CHECK_EQ(graph.nEdges(), 5); CHECK_EQ(graph.nNodes(), 4); - CHECK(graph.edge(0, 1)); - CHECK(graph.edge(1, 2)); - CHECK(graph.edge(0, 2)); - CHECK(graph.edge(0, 3)); - CHECK(graph.edge(2, 3)); + CHECK_NOTHROW(graph.edge(0, 1)); + CHECK_NOTHROW(graph.edge(1, 2)); + CHECK_NOTHROW(graph.edge(0, 2)); + CHECK_NOTHROW(graph.edge(0, 3)); + CHECK_NOTHROW(graph.edge(2, 3)); } SUBCASE("Construction with addStreets") { Street s1(1, std::make_pair(0, 1)); @@ -68,11 +68,11 @@ TEST_CASE("RoadNetwork") { graph.addStreets(s1, s2, s3, s4, s5); CHECK_EQ(graph.nEdges(), 5); CHECK_EQ(graph.nNodes(), 4); - CHECK(graph.edge(0, 1)); - CHECK(graph.edge(1, 2)); - CHECK(graph.edge(0, 2)); - CHECK(graph.edge(0, 3)); - CHECK(graph.edge(2, 3)); + CHECK_NOTHROW(graph.edge(0, 1)); + CHECK_NOTHROW(graph.edge(1, 2)); + CHECK_NOTHROW(graph.edge(0, 2)); + CHECK_NOTHROW(graph.edge(0, 3)); + CHECK_NOTHROW(graph.edge(2, 3)); // Test describe method std::ostringstream oss; graph.describe(oss); @@ -104,24 +104,24 @@ TEST_CASE("RoadNetwork") { graph.autoMapStreetLanes(); // dsf::Logger::setLogLevel(dsf::log_level_t::INFO); THEN("The lanes are correctly mapped") { - CHECK_EQ(graph.edge(0, 1)->laneMapping().size(), 3); - CHECK_EQ(graph.edge(0, 1)->laneMapping()[0], dsf::Direction::ANY); - CHECK_EQ(graph.edge(0, 1)->laneMapping()[1], dsf::Direction::ANY); - CHECK_EQ(graph.edge(0, 1)->laneMapping()[2], dsf::Direction::ANY); - CHECK_EQ(graph.edge(1, 0)->laneMapping().size(), 3); - CHECK_EQ(graph.edge(1, 0)->laneMapping()[0], dsf::Direction::RIGHT); - CHECK_EQ(graph.edge(1, 0)->laneMapping()[1], dsf::Direction::RIGHT); - CHECK_EQ(graph.edge(1, 0)->laneMapping()[2], dsf::Direction::LEFT); - CHECK_EQ(graph.edge(0, 2)->laneMapping().size(), 1); - CHECK_EQ(graph.edge(0, 2)->laneMapping()[0], dsf::Direction::ANY); - CHECK_EQ(graph.edge(2, 0)->laneMapping().size(), 1); - CHECK_EQ(graph.edge(2, 0)->laneMapping()[0], dsf::Direction::ANY); - CHECK_EQ(graph.edge(0, 3)->laneMapping().size(), 2); - CHECK_EQ(graph.edge(0, 3)->laneMapping()[0], dsf::Direction::ANY); - CHECK_EQ(graph.edge(0, 3)->laneMapping()[1], dsf::Direction::ANY); - CHECK_EQ(graph.edge(3, 0)->laneMapping().size(), 2); - CHECK_EQ(graph.edge(3, 0)->laneMapping()[0], dsf::Direction::RIGHT); - CHECK_EQ(graph.edge(3, 0)->laneMapping()[1], dsf::Direction::STRAIGHT); + CHECK_EQ(graph.edge(0, 1).laneMapping().size(), 3); + CHECK_EQ(graph.edge(0, 1).laneMapping()[0], dsf::Direction::ANY); + CHECK_EQ(graph.edge(0, 1).laneMapping()[1], dsf::Direction::ANY); + CHECK_EQ(graph.edge(0, 1).laneMapping()[2], dsf::Direction::ANY); + CHECK_EQ(graph.edge(1, 0).laneMapping().size(), 3); + CHECK_EQ(graph.edge(1, 0).laneMapping()[0], dsf::Direction::RIGHT); + CHECK_EQ(graph.edge(1, 0).laneMapping()[1], dsf::Direction::RIGHT); + CHECK_EQ(graph.edge(1, 0).laneMapping()[2], dsf::Direction::LEFT); + CHECK_EQ(graph.edge(0, 2).laneMapping().size(), 1); + CHECK_EQ(graph.edge(0, 2).laneMapping()[0], dsf::Direction::ANY); + CHECK_EQ(graph.edge(2, 0).laneMapping().size(), 1); + CHECK_EQ(graph.edge(2, 0).laneMapping()[0], dsf::Direction::ANY); + CHECK_EQ(graph.edge(0, 3).laneMapping().size(), 2); + CHECK_EQ(graph.edge(0, 3).laneMapping()[0], dsf::Direction::ANY); + CHECK_EQ(graph.edge(0, 3).laneMapping()[1], dsf::Direction::ANY); + CHECK_EQ(graph.edge(3, 0).laneMapping().size(), 2); + CHECK_EQ(graph.edge(3, 0).laneMapping()[0], dsf::Direction::RIGHT); + CHECK_EQ(graph.edge(3, 0).laneMapping()[1], dsf::Direction::STRAIGHT); } } } @@ -189,7 +189,7 @@ TEST_CASE("RoadNetwork") { CHECK_EQ(graph2.nEdges(), graph.nEdges()); CHECK_EQ(graph2.nNodes(), graph.nNodes()); for (auto const& [edgeId, pEdge] : graph2.edges()) { - CHECK_EQ(*pEdge, *graph.edge(edgeId)); + CHECK_EQ(*pEdge, graph.edge(edgeId)); } } } @@ -238,10 +238,10 @@ TEST_CASE("RoadNetwork") { graphWithPriority.importEdges(tmpCsvPath.string()); THEN("Priority is correctly imported from CSV") { - REQUIRE(graphWithPriority.edge(static_cast(100))); - REQUIRE(graphWithPriority.edge(static_cast(101))); - CHECK(graphWithPriority.edge(static_cast(100))->hasPriority()); - CHECK_FALSE(graphWithPriority.edge(static_cast(101))->hasPriority()); + REQUIRE_NOTHROW(graphWithPriority.edge(static_cast(100))); + REQUIRE_NOTHROW(graphWithPriority.edge(static_cast(101))); + CHECK(graphWithPriority.edge(static_cast(100)).hasPriority()); + CHECK_FALSE(graphWithPriority.edge(static_cast(101)).hasPriority()); std::filesystem::remove(tmpCsvPath); } } @@ -299,10 +299,10 @@ TEST_CASE("RoadNetwork") { graphWithPriority.importEdges(tmpGeoJsonPath.string()); THEN("Priority is correctly imported from GeoJSON") { - REQUIRE(graphWithPriority.edge(static_cast(200))); - REQUIRE(graphWithPriority.edge(static_cast(201))); - CHECK(graphWithPriority.edge(static_cast(200))->hasPriority()); - CHECK_FALSE(graphWithPriority.edge(static_cast(201))->hasPriority()); + REQUIRE_NOTHROW(graphWithPriority.edge(static_cast(200))); + REQUIRE_NOTHROW(graphWithPriority.edge(static_cast(201))); + CHECK(graphWithPriority.edge(static_cast(200)).hasPriority()); + CHECK_FALSE(graphWithPriority.edge(static_cast(201)).hasPriority()); std::filesystem::remove(tmpGeoJsonPath); } } @@ -316,9 +316,9 @@ TEST_CASE("RoadNetwork") { auto result = graph.street(0, 1); CHECK(result); const auto& street2 = *result; - CHECK_EQ(street2->id(), 1); - CHECK_EQ(street2->length(), 1.); - CHECK_EQ(street2->capacity(), 1); + CHECK_EQ(street2.id(), 1); + CHECK_EQ(street2.length(), 1.); + CHECK_EQ(street2.capacity(), 1); CHECK_FALSE(graph.street(1, 0)); } SUBCASE("make trafficlight") { @@ -435,8 +435,8 @@ TEST_CASE("RoadNetwork") { WHEN("We auto-init Traffic Lights") { graph.autoInitTrafficLights(); THEN("Node is converted to intersection") { - CHECK_FALSE(graph.node(0)->isTrafficLight()); - CHECK(graph.node(0)->isIntersection()); + CHECK_FALSE(graph.node(0).isTrafficLight()); + CHECK(graph.node(0).isIntersection()); } } } @@ -552,7 +552,7 @@ TEST_CASE("RoadNetwork") { graph.addStreet(Street{1, std::make_pair(0, 1)}); WHEN("We make node 0 a traffic light") { auto& tl = graph.makeTrafficLight(0, 60); - THEN("The node 0 is a traffic light") { CHECK(graph.node(0)->isTrafficLight()); } + THEN("The node 0 is a traffic light") { CHECK(graph.node(0).isTrafficLight()); } THEN("The traffic light has the correct parameters") { CHECK_EQ(tl.id(), 0); CHECK_EQ(tl.cycleTime(), 60); @@ -566,7 +566,7 @@ TEST_CASE("RoadNetwork") { graph.addStreet(Street{1, std::make_pair(0, 1)}); WHEN("We make node 0 a roundabout") { graph.makeRoundabout(0); - THEN("The node 0 is a roundabout") { CHECK(graph.node(0)->isRoundabout()); } + THEN("The node 0 is a roundabout") { CHECK(graph.node(0).isRoundabout()); } } } } @@ -576,7 +576,7 @@ TEST_CASE("RoadNetwork") { graph.addEdge(Street{0, std::make_pair(0, 1)}); WHEN("We add a coil to the street") { graph.addCoil(0); - THEN("The street has a coil") { CHECK(graph.edge(0)->hasCoil()); } + THEN("The street has a coil") { CHECK(graph.edge(0).hasCoil()); } } } } @@ -592,9 +592,9 @@ TEST_CASE("RoadNetwork") { THEN("The street is found and has correct values") { CHECK(result); const auto& road = *result; - CHECK_EQ(road->id(), 1); - CHECK_EQ(road->length(), 1.); - CHECK_EQ(road->capacity(), 1); + CHECK_EQ(road.id(), 1); + CHECK_EQ(road.length(), 1.); + CHECK_EQ(road.capacity(), 1); } } WHEN("We search for the opposite street") { @@ -602,9 +602,9 @@ TEST_CASE("RoadNetwork") { THEN("The opposite street is found and has correct values") { CHECK(result); const auto& road = *result; - CHECK_EQ(road->id(), 2); - CHECK_EQ(road->length(), 1.); - CHECK_EQ(road->capacity(), 1); + CHECK_EQ(road.id(), 2); + CHECK_EQ(road.length(), 1.); + CHECK_EQ(road.capacity(), 1); } } WHEN("We search for a not existing street") { @@ -627,27 +627,27 @@ TEST_CASE("RoadNetwork") { WHEN("We adjust node capacities") { graph.adjustNodeCapacities(); THEN("The node capacities are correct") { - CHECK_EQ(graph.node(0)->capacity(), 1); - CHECK_EQ(graph.node(1)->capacity(), 4); - CHECK_EQ(graph.node(2)->capacity(), 2); - CHECK_EQ(graph.node(3)->capacity(), 3); - CHECK_EQ(graph.node(4)->capacity(), 1); + CHECK_EQ(graph.node(0).capacity(), 1); + CHECK_EQ(graph.node(1).capacity(), 4); + CHECK_EQ(graph.node(2).capacity(), 2); + CHECK_EQ(graph.node(3).capacity(), 3); + CHECK_EQ(graph.node(4).capacity(), 1); } THEN("The transport capacities are correct") { - CHECK_EQ(graph.node(0)->transportCapacity(), 1); - CHECK_EQ(graph.node(1)->transportCapacity(), 3); - CHECK_EQ(graph.node(2)->transportCapacity(), 1); - CHECK_EQ(graph.node(3)->transportCapacity(), 3); - CHECK_EQ(graph.node(4)->transportCapacity(), 1); + CHECK_EQ(graph.node(0).transportCapacity(), 1); + CHECK_EQ(graph.node(1).transportCapacity(), 3); + CHECK_EQ(graph.node(2).transportCapacity(), 1); + CHECK_EQ(graph.node(3).transportCapacity(), 3); + CHECK_EQ(graph.node(4).transportCapacity(), 1); } } WHEN("We normalize street capacities") { // graph.normalizeStreetCapacities(); THEN("The street capacities are correct") { - CHECK_EQ(graph.edge(0, 1)->capacity(), 2); - CHECK_EQ(graph.edge(1, 2)->capacity(), 16); - CHECK_EQ(graph.edge(3, 1)->capacity(), 45); - CHECK_EQ(graph.edge(1, 4)->capacity(), 11); + CHECK_EQ(graph.edge(0, 1).capacity(), 2); + CHECK_EQ(graph.edge(1, 2).capacity(), 16); + CHECK_EQ(graph.edge(3, 1).capacity(), 45); + CHECK_EQ(graph.edge(1, 4).capacity(), 11); } } } @@ -674,9 +674,9 @@ TEST_CASE("RoadNetwork") { WHEN("We auto assign road priorities") { graph.autoAssignRoadPriorities(); THEN("The priorities are assigned to the most important roads") { - CHECK(graph.edge(1)->hasPriority()); - CHECK(graph.edge(2)->hasPriority()); - CHECK_FALSE(graph.edge(3)->hasPriority()); + CHECK(graph.edge(1).hasPriority()); + CHECK(graph.edge(2).hasPriority()); + CHECK_FALSE(graph.edge(3).hasPriority()); } } } @@ -689,7 +689,7 @@ TEST_CASE("RoadNetwork") { WHEN("We auto assign road priorities") { graph.autoAssignRoadPriorities(); - THEN("No priorities are assigned") { CHECK_FALSE(graph.edge(1)->hasPriority()); } + THEN("No priorities are assigned") { CHECK_FALSE(graph.edge(1).hasPriority()); } } } @@ -706,9 +706,9 @@ TEST_CASE("RoadNetwork") { WHEN("We auto assign road priorities") { graph.autoAssignRoadPriorities(); THEN("No priorities are assigned since count != 2") { - CHECK_FALSE(graph.edge(1)->hasPriority()); - CHECK_FALSE(graph.edge(2)->hasPriority()); - CHECK_FALSE(graph.edge(3)->hasPriority()); + CHECK_FALSE(graph.edge(1).hasPriority()); + CHECK_FALSE(graph.edge(2).hasPriority()); + CHECK_FALSE(graph.edge(3).hasPriority()); } } } @@ -723,9 +723,9 @@ TEST_CASE("RoadNetwork") { WHEN("We auto assign road priorities") { graph.autoAssignRoadPriorities(); THEN("No priorities are assigned") { - CHECK_FALSE(graph.edge(1)->hasPriority()); - CHECK_FALSE(graph.edge(2)->hasPriority()); - CHECK_FALSE(graph.edge(3)->hasPriority()); + CHECK_FALSE(graph.edge(1).hasPriority()); + CHECK_FALSE(graph.edge(2).hasPriority()); + CHECK_FALSE(graph.edge(3).hasPriority()); } } } @@ -749,11 +749,11 @@ TEST_CASE("RoadNetwork") { WHEN("We auto assign road priorities") { graph.autoAssignRoadPriorities(); THEN("Priorities go to the SECONDARY roads (first type with count == 2)") { - CHECK_FALSE(graph.edge(1)->hasPriority()); - CHECK_FALSE(graph.edge(2)->hasPriority()); - CHECK_FALSE(graph.edge(3)->hasPriority()); - CHECK(graph.edge(4)->hasPriority()); - CHECK(graph.edge(5)->hasPriority()); + CHECK_FALSE(graph.edge(1).hasPriority()); + CHECK_FALSE(graph.edge(2).hasPriority()); + CHECK_FALSE(graph.edge(3).hasPriority()); + CHECK(graph.edge(4).hasPriority()); + CHECK(graph.edge(5).hasPriority()); } } } @@ -771,9 +771,9 @@ TEST_CASE("RoadNetwork") { WHEN("We auto assign road priorities") { graph.autoAssignRoadPriorities(); THEN("Priorities are assigned to PRIMARY roads") { - CHECK(graph.edge(1)->hasPriority()); - CHECK(graph.edge(2)->hasPriority()); - CHECK_FALSE(graph.edge(3)->hasPriority()); + CHECK(graph.edge(1).hasPriority()); + CHECK(graph.edge(2).hasPriority()); + CHECK_FALSE(graph.edge(3).hasPriority()); } } } @@ -793,10 +793,10 @@ TEST_CASE("RoadNetwork") { WHEN("We auto assign road priorities") { graph.autoAssignRoadPriorities(); THEN("Priorities are assigned to RESIDENTIAL roads (first with count == 2)") { - CHECK_FALSE(graph.edge(1)->hasPriority()); - CHECK_FALSE(graph.edge(2)->hasPriority()); - CHECK(graph.edge(3)->hasPriority()); - CHECK(graph.edge(4)->hasPriority()); + CHECK_FALSE(graph.edge(1).hasPriority()); + CHECK_FALSE(graph.edge(2).hasPriority()); + CHECK(graph.edge(3).hasPriority()); + CHECK(graph.edge(4).hasPriority()); } } } @@ -823,13 +823,13 @@ TEST_CASE("RoadNetwork") { graph.autoAssignRoadPriorities(); THEN("Both nodes have correct priorities assigned") { // Node 1: HIGHWAY roads get priority - CHECK(graph.edge(1)->hasPriority()); - CHECK(graph.edge(2)->hasPriority()); - CHECK_FALSE(graph.edge(3)->hasPriority()); + CHECK(graph.edge(1).hasPriority()); + CHECK(graph.edge(2).hasPriority()); + CHECK_FALSE(graph.edge(3).hasPriority()); // Node 5: TERTIARY roads get priority - CHECK(graph.edge(4)->hasPriority()); - CHECK(graph.edge(5)->hasPriority()); - CHECK_FALSE(graph.edge(6)->hasPriority()); + CHECK(graph.edge(4).hasPriority()); + CHECK(graph.edge(5).hasPriority()); + CHECK_FALSE(graph.edge(6).hasPriority()); } } } @@ -847,7 +847,7 @@ TEST_CASE("Dijkstra") { graph.addStreets(s1, s2, s3, s4, s5); auto const& pathMap = - graph.allPathsTo(2, [](auto const& pEdge) { return pEdge->length(); }); + graph.allPathsTo(2, [](auto const& pEdge) { return pEdge.length(); }); CHECK_FALSE(pathMap.contains(2)); CHECK_EQ(pathMap.at(3).size(), 1); CHECK_EQ(pathMap.at(3)[0], 0); @@ -865,7 +865,7 @@ TEST_CASE("Dijkstra") { graph.addStreets(s1, s2, s3); auto const& pathMap = - graph.allPathsTo(2, [](auto const& pEdge) { return pEdge->length(); }); + graph.allPathsTo(2, [](auto const& pEdge) { return pEdge.length(); }); CHECK_FALSE(pathMap.contains(2)); CHECK_EQ(pathMap.at(0).size(), 1); CHECK_EQ(pathMap.at(0)[0], 1); @@ -881,7 +881,7 @@ TEST_CASE("Dijkstra") { graph.addStreets(s1, s2, s3); auto const& pathMap = - graph.allPathsTo(2, [](auto const& pEdge) { return pEdge->length(); }); + graph.allPathsTo(2, [](auto const& pEdge) { return pEdge.length(); }); CHECK_FALSE(pathMap.contains(2)); CHECK_EQ(pathMap.at(0).size(), 1); CHECK_EQ(pathMap.at(0)[0], 2); @@ -908,7 +908,7 @@ TEST_CASE("Dijkstra") { graph.addStreets(s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14); auto const& pathMap = - graph.allPathsTo(4, [](auto const& pEdge) { return pEdge->length(); }); + graph.allPathsTo(4, [](auto const& pEdge) { return pEdge.length(); }); CHECK_FALSE(pathMap.contains(4)); CHECK_EQ(pathMap.at(0).size(), 1); CHECK_EQ(pathMap.at(0)[0], 1); @@ -944,7 +944,7 @@ TEST_CASE("Dijkstra") { s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18); auto const& pathMap = - graph.allPathsTo(6, [](auto const& pEdge) { return pEdge->length(); }); + graph.allPathsTo(6, [](auto const& pEdge) { return pEdge.length(); }); CHECK_FALSE(pathMap.contains(6)); CHECK_EQ(pathMap.at(0).size(), 1); CHECK_EQ(pathMap.at(0)[0], 1); @@ -984,7 +984,7 @@ TEST_CASE("Dijkstra") { s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18); auto const& pathMap = - graph.allPathsTo(4, [](auto const& pEdge) { return pEdge->length(); }); + graph.allPathsTo(4, [](auto const& pEdge) { return pEdge.length(); }); CHECK_FALSE(pathMap.contains(4)); CHECK_EQ(pathMap.at(0).size(), 1); CHECK_EQ(pathMap.at(0)[0], 2); @@ -1007,7 +1007,7 @@ TEST_CASE("Dijkstra") { graph.addStreets(s1, s2, s3); auto const& pathMap = - graph.allPathsTo(1, [](auto const& pEdge) { return pEdge->length(); }); + graph.allPathsTo(1, [](auto const& pEdge) { return pEdge.length(); }); CHECK(pathMap.empty()); } @@ -1018,9 +1018,8 @@ TEST_CASE("Dijkstra") { RoadNetwork graph{}; graph.addStreets(s1, s2, s3); - CHECK_THROWS_AS( - graph.allPathsTo(3, [](auto const& pEdge) { return pEdge->length(); }), - std::out_of_range); + CHECK_THROWS_AS(graph.allPathsTo(3, [](auto const& pEdge) { return pEdge.length(); }), + std::out_of_range); } SUBCASE("Case 9 - Equal Lengths") { @@ -1031,7 +1030,7 @@ TEST_CASE("Dijkstra") { CHECK_EQ(graph.nEdges(), 436); auto const& path = - graph.allPathsTo(118, [](auto const& pEdge) { return pEdge->length(); }); + graph.allPathsTo(118, [](auto const& pEdge) { return pEdge.length(); }); CHECK_EQ(path.size(), 119); CHECK_FALSE(path.contains(118)); } @@ -1056,7 +1055,7 @@ TEST_CASE("ShortestPath") { graph.addStreets(s01, s12, s03, s32); auto pathMap = - graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge.length(); }); // Verify the shortest path is 0 -> 1 -> 2 REQUIRE(pathMap.contains(0)); @@ -1080,7 +1079,7 @@ TEST_CASE("ShortestPath") { graph.addStreet(std::move(s01)); auto pathMap = - graph.shortestPath(0, 0, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 0, [](auto const& pEdge) { return pEdge.length(); }); // When source equals target, should return empty map (no hops needed) CHECK(pathMap.empty()); @@ -1093,7 +1092,7 @@ TEST_CASE("ShortestPath") { graph.addStreets(s01, s23); auto pathMap = - graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge.length(); }); // No path exists, should return empty map CHECK(pathMap.empty()); @@ -1105,7 +1104,7 @@ TEST_CASE("ShortestPath") { graph.addStreet(std::move(s01)); CHECK_THROWS_AS( - graph.shortestPath(99, 1, [](auto const& pEdge) { return pEdge->length(); }), + graph.shortestPath(99, 1, [](auto const& pEdge) { return pEdge.length(); }), std::out_of_range); } @@ -1115,7 +1114,7 @@ TEST_CASE("ShortestPath") { graph.addStreet(std::move(s01)); CHECK_THROWS_AS( - graph.shortestPath(0, 99, [](auto const& pEdge) { return pEdge->length(); }), + graph.shortestPath(0, 99, [](auto const& pEdge) { return pEdge.length(); }), std::out_of_range); } @@ -1129,7 +1128,7 @@ TEST_CASE("ShortestPath") { graph.addStreets(s01, s02, s13, s23); auto pathMap = - graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge.length(); }); // Verify the shortest path is 0 -> 1 -> 3 (length 20) REQUIRE(pathMap.contains(0)); @@ -1159,7 +1158,7 @@ TEST_CASE("ShortestPath") { graph.addStreets(s01, s02, s13, s23); auto pathMap = - graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge.length(); }); // Both paths have same length (100), so node 0 should have two next hops REQUIRE(pathMap.contains(0)); @@ -1187,7 +1186,7 @@ TEST_CASE("ShortestPath") { CHECK_EQ(graph.nEdges(), 436); auto pathMap = - graph.shortestPath(0, 119, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 119, [](auto const& pEdge) { return pEdge.length(); }); // Verify that a path exists CHECK_FALSE(pathMap.empty()); @@ -1202,7 +1201,7 @@ TEST_CASE("ShortestPath") { // Verify connectivity: for each node in pathMap, verify edges exist to next hops for (auto const& [nodeId, nextHops] : pathMap) { for (auto const& nextHop : nextHops) { - CHECK(graph.edge(nodeId, nextHop)); + CHECK_NOTHROW(graph.edge(nodeId, nextHop)); } } } @@ -1219,7 +1218,7 @@ TEST_CASE("ShortestPath") { graph.addStreets(s01, s02, s13, s23); auto pathMap = - graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge->length(); }, 0.01); + graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge.length(); }, 0.01); // Check that node 0 has multiple next hops (both 1 and 2) REQUIRE(pathMap.contains(0)); @@ -1275,7 +1274,7 @@ TEST_CASE("ShortestPath") { graph.addStreets(s01, s02, s13, s24, s35, s45); auto pathMap = - graph.shortestPath(0, 5, [](auto const& pEdge) { return pEdge->length(); }, 0.01); + graph.shortestPath(0, 5, [](auto const& pEdge) { return pEdge.length(); }, 0.01); // Test explode function auto allPaths = pathMap.explode(0, 5); @@ -1318,7 +1317,7 @@ TEST_CASE("ShortestPath") { graph.addStreets(s01, s12, s03, s14, s25, s34, s45); auto pathMap = - graph.shortestPath(0, 5, [](auto const& pEdge) { return pEdge->length(); }, 0.01); + graph.shortestPath(0, 5, [](auto const& pEdge) { return pEdge.length(); }, 0.01); // Test explode function auto allPaths = pathMap.explode(0, 5); @@ -1343,7 +1342,7 @@ TEST_CASE("ShortestPath") { graph.addStreets(s01, s12, s23); auto pathMap = - graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 3, [](auto const& pEdge) { return pEdge.length(); }); // Test explode function - should return only one path auto allPaths = pathMap.explode(0, 3); @@ -1396,17 +1395,17 @@ TEST_CASE("RoadStatus") { graph.addStreets(s01, s12); // Initially all streets are OPEN - CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::OPEN); - CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(0).roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(1).roadStatus(), RoadStatus::OPEN); // Close street by id graph.setStreetStatusById(0, RoadStatus::CLOSED); - CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::CLOSED); - CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(0).roadStatus(), RoadStatus::CLOSED); + CHECK_EQ(graph.edge(1).roadStatus(), RoadStatus::OPEN); // Re-open street graph.setStreetStatusById(0, RoadStatus::OPEN); - CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(0).roadStatus(), RoadStatus::OPEN); } SUBCASE("setStreetStatusByName") { @@ -1422,20 +1421,20 @@ TEST_CASE("RoadStatus") { graph.addStreets(s01, s12, s23); // Initially all streets are OPEN - CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::OPEN); - CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::OPEN); - CHECK_EQ(graph.edge(2)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(0).roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(1).roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(2).roadStatus(), RoadStatus::OPEN); // Close all streets with name "Main Street" graph.setStreetStatusByName("Main Street", RoadStatus::CLOSED); - CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::CLOSED); - CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::CLOSED); - CHECK_EQ(graph.edge(2)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(0).roadStatus(), RoadStatus::CLOSED); + CHECK_EQ(graph.edge(1).roadStatus(), RoadStatus::CLOSED); + CHECK_EQ(graph.edge(2).roadStatus(), RoadStatus::OPEN); // Re-open Main Street graph.setStreetStatusByName("Main Street", RoadStatus::OPEN); - CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::OPEN); - CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(0).roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(1).roadStatus(), RoadStatus::OPEN); } SUBCASE("setStreetStatusByName - partial match") { @@ -1450,8 +1449,8 @@ TEST_CASE("RoadStatus") { // Close all streets containing "Roma" in the name graph.setStreetStatusByName("Roma", RoadStatus::CLOSED); - CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::CLOSED); - CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::CLOSED); + CHECK_EQ(graph.edge(0).roadStatus(), RoadStatus::CLOSED); + CHECK_EQ(graph.edge(1).roadStatus(), RoadStatus::CLOSED); } } @@ -1476,7 +1475,7 @@ TEST_CASE("ShortestPath with closed roads") { // Initially, shortest path is 0 -> 1 -> 2 auto pathMap = - graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge.length(); }); REQUIRE(pathMap.contains(0)); CHECK_EQ(pathMap.at(0)[0], 1); @@ -1484,7 +1483,7 @@ TEST_CASE("ShortestPath with closed roads") { graph.setStreetStatusById(0, RoadStatus::CLOSED); // Now shortest path should be 0 -> 3 -> 2 - pathMap = graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + pathMap = graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge.length(); }); REQUIRE(pathMap.contains(0)); CHECK_EQ(pathMap.at(0).size(), 1); CHECK_EQ(pathMap.at(0)[0], 3); @@ -1510,7 +1509,7 @@ TEST_CASE("ShortestPath with closed roads") { // No path should exist from 0 to 2 auto pathMap = - graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge.length(); }); CHECK(pathMap.empty()); } @@ -1527,11 +1526,11 @@ TEST_CASE("ShortestPath with closed roads") { // Close and then reopen graph.setStreetStatusById(0, RoadStatus::CLOSED); auto pathMap = - graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge.length(); }); CHECK(pathMap.empty()); graph.setStreetStatusById(0, RoadStatus::OPEN); - pathMap = graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + pathMap = graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge.length(); }); CHECK_FALSE(pathMap.empty()); REQUIRE(pathMap.contains(0)); CHECK_EQ(pathMap.at(0)[0], 1); @@ -1559,7 +1558,7 @@ TEST_CASE("allPathsTo with closed roads") { graph.setStreetStatusById(0, RoadStatus::CLOSED); // allPathsTo should not include node 1 as next hop from 0 - auto pathMap = graph.allPathsTo(2, [](auto const& pEdge) { return pEdge->length(); }); + auto pathMap = graph.allPathsTo(2, [](auto const& pEdge) { return pEdge.length(); }); REQUIRE(pathMap.contains(0)); // Node 0 should only have node 3 as next hop (not 1, since 0->1 is closed) @@ -1585,7 +1584,7 @@ TEST_CASE("allPathsTo with closed roads") { // Close the only path to node 2 graph.setStreetStatusById(1, RoadStatus::CLOSED); - auto pathMap = graph.allPathsTo(2, [](auto const& pEdge) { return pEdge->length(); }); + auto pathMap = graph.allPathsTo(2, [](auto const& pEdge) { return pEdge.length(); }); // Node 0 should not have a path to 2 CHECK_FALSE(pathMap.contains(0)); @@ -1609,7 +1608,7 @@ TEST_CASE("allPathsTo with closed roads") { // Close all "Main Road" streets graph.setStreetStatusByName("Main Road", RoadStatus::CLOSED); - auto pathMap = graph.allPathsTo(2, [](auto const& pEdge) { return pEdge->length(); }); + auto pathMap = graph.allPathsTo(2, [](auto const& pEdge) { return pEdge.length(); }); // From node 0, only path should be via node 3 REQUIRE(pathMap.contains(0)); @@ -1637,17 +1636,17 @@ TEST_CASE("Change Street Lanes") { auto const* pStreet01 = graph.street(0, 1); REQUIRE(pStreet01 != nullptr); - auto const initialCapacity = (*pStreet01)->capacity(); - auto const initialMaxSpeed = (*pStreet01)->maxSpeed(); + auto const initialCapacity = pStreet01->capacity(); + auto const initialMaxSpeed = pStreet01->maxSpeed(); WHEN("Lanes are increased for street by id") { graph.changeStreetNLanesById(10, 4); THEN("The street's properties are updated correctly") { - CHECK_EQ((*pStreet01)->nLanes(), 4); - CHECK_EQ((*pStreet01)->capacity(), initialCapacity * 2); - CHECK_EQ((*pStreet01)->maxSpeed(), initialMaxSpeed); - CHECK_EQ((*pStreet01)->exitQueues().size(), 4); + CHECK_EQ(pStreet01->nLanes(), 4); + CHECK_EQ(pStreet01->capacity(), initialCapacity * 2); + CHECK_EQ(pStreet01->maxSpeed(), initialMaxSpeed); + CHECK_EQ(pStreet01->exitQueues().size(), 4); } } @@ -1655,8 +1654,8 @@ TEST_CASE("Change Street Lanes") { graph.changeStreetNLanesById(10, 1, 0.6); THEN("Both lanes and speed are updated") { - CHECK_EQ((*pStreet01)->nLanes(), 1); - CHECK_EQ((*pStreet01)->maxSpeed(), doctest::Approx(initialMaxSpeed * 0.6)); + CHECK_EQ(pStreet01->nLanes(), 1); + CHECK_EQ(pStreet01->maxSpeed(), doctest::Approx(initialMaxSpeed * 0.6)); } } @@ -1699,21 +1698,21 @@ TEST_CASE("Change Street Lanes") { REQUIRE(pSideRoad1 != nullptr); REQUIRE(pSideRoad2 != nullptr); - auto const initialMainSpeed1 = (*pMainStreet1)->maxSpeed(); - auto const initialMainSpeed2 = (*pMainStreet2)->maxSpeed(); - auto const initialSideSpeed1 = (*pSideRoad1)->maxSpeed(); - auto const initialSideSpeed2 = (*pSideRoad2)->maxSpeed(); + auto const initialMainSpeed1 = pMainStreet1->maxSpeed(); + auto const initialMainSpeed2 = pMainStreet2->maxSpeed(); + auto const initialSideSpeed1 = pSideRoad1->maxSpeed(); + auto const initialSideSpeed2 = pSideRoad2->maxSpeed(); WHEN("All streets with 'Main' in name are changed") { graph.changeStreetNLanesByName("Main", 1); THEN("Only Main Streets are affected") { - CHECK_EQ((*pMainStreet1)->nLanes(), 1); - CHECK_EQ((*pMainStreet2)->nLanes(), 1); - CHECK_EQ((*pSideRoad1)->nLanes(), 1); // unchanged - CHECK_EQ((*pSideRoad2)->nLanes(), 2); // unchanged - CHECK_EQ((*pMainStreet1)->maxSpeed(), initialMainSpeed1); - CHECK_EQ((*pMainStreet2)->maxSpeed(), initialMainSpeed2); + CHECK_EQ(pMainStreet1->nLanes(), 1); + CHECK_EQ(pMainStreet2->nLanes(), 1); + CHECK_EQ(pSideRoad1->nLanes(), 1); // unchanged + CHECK_EQ(pSideRoad2->nLanes(), 2); // unchanged + CHECK_EQ(pMainStreet1->maxSpeed(), initialMainSpeed1); + CHECK_EQ(pMainStreet2->maxSpeed(), initialMainSpeed2); } } @@ -1721,14 +1720,14 @@ TEST_CASE("Change Street Lanes") { graph.changeStreetNLanesByName("Side", 3, 0.8); THEN("Only Side Roads are affected with both changes") { - CHECK_EQ((*pSideRoad1)->nLanes(), 3); - CHECK_EQ((*pSideRoad2)->nLanes(), 3); - CHECK_EQ((*pMainStreet1)->nLanes(), 2); // unchanged - CHECK_EQ((*pMainStreet2)->nLanes(), 3); // unchanged - CHECK_EQ((*pSideRoad1)->maxSpeed(), doctest::Approx(initialSideSpeed1 * 0.8)); - CHECK_EQ((*pSideRoad2)->maxSpeed(), doctest::Approx(initialSideSpeed2 * 0.8)); - CHECK_EQ((*pMainStreet1)->maxSpeed(), initialMainSpeed1); // unchanged - CHECK_EQ((*pMainStreet2)->maxSpeed(), initialMainSpeed2); // unchanged + CHECK_EQ(pSideRoad1->nLanes(), 3); + CHECK_EQ(pSideRoad2->nLanes(), 3); + CHECK_EQ(pMainStreet1->nLanes(), 2); // unchanged + CHECK_EQ(pMainStreet2->nLanes(), 3); // unchanged + CHECK_EQ(pSideRoad1->maxSpeed(), doctest::Approx(initialSideSpeed1 * 0.8)); + CHECK_EQ(pSideRoad2->maxSpeed(), doctest::Approx(initialSideSpeed2 * 0.8)); + CHECK_EQ(pMainStreet1->maxSpeed(), initialMainSpeed1); // unchanged + CHECK_EQ(pMainStreet2->maxSpeed(), initialMainSpeed2); // unchanged } } @@ -1736,10 +1735,10 @@ TEST_CASE("Change Street Lanes") { graph.changeStreetNLanesByName("NonExistent", 5); THEN("No streets are changed") { - CHECK_EQ((*pMainStreet1)->nLanes(), 2); - CHECK_EQ((*pMainStreet2)->nLanes(), 3); - CHECK_EQ((*pSideRoad1)->nLanes(), 1); - CHECK_EQ((*pSideRoad2)->nLanes(), 2); + CHECK_EQ(pMainStreet1->nLanes(), 2); + CHECK_EQ(pMainStreet2->nLanes(), 3); + CHECK_EQ(pSideRoad1->nLanes(), 1); + CHECK_EQ(pSideRoad2->nLanes(), 2); } } @@ -1747,10 +1746,10 @@ TEST_CASE("Change Street Lanes") { graph.changeStreetNLanesByName("Street", 4); THEN("All streets with 'Street' in name are changed") { - CHECK_EQ((*pMainStreet1)->nLanes(), 4); - CHECK_EQ((*pMainStreet2)->nLanes(), 4); - CHECK_EQ((*pSideRoad1)->nLanes(), 1); // unchanged - CHECK_EQ((*pSideRoad2)->nLanes(), 2); // unchanged + CHECK_EQ(pMainStreet1->nLanes(), 4); + CHECK_EQ(pMainStreet2->nLanes(), 4); + CHECK_EQ(pSideRoad1->nLanes(), 1); // unchanged + CHECK_EQ(pSideRoad2->nLanes(), 2); // unchanged } } } @@ -1774,20 +1773,20 @@ TEST_CASE("BetweennessCentrality") { graph.computeBetweennessCentralities(unitWeight); // Node 0: source only, never an intermediate -> BC = 0 - REQUIRE(graph.node(0)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(0)->betweennessCentrality(), doctest::Approx(0.0)); + REQUIRE(graph.node(0).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(0).betweennessCentrality(), doctest::Approx(0.0)); // Node 1: on path 0->2 and 0->3 -> BC = 2 - REQUIRE(graph.node(1)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(1)->betweennessCentrality(), doctest::Approx(2.0)); + REQUIRE(graph.node(1).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(1).betweennessCentrality(), doctest::Approx(2.0)); // Node 2: on path 0->3 and 1->3 -> BC = 2 - REQUIRE(graph.node(2)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(2)->betweennessCentrality(), doctest::Approx(2.0)); + REQUIRE(graph.node(2).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(2).betweennessCentrality(), doctest::Approx(2.0)); // Node 3: sink only, never an intermediate -> BC = 0 - REQUIRE(graph.node(3)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(3)->betweennessCentrality(), doctest::Approx(0.0)); + REQUIRE(graph.node(3).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(3).betweennessCentrality(), doctest::Approx(0.0)); } SUBCASE("Star topology: center node") { @@ -1802,8 +1801,8 @@ TEST_CASE("BetweennessCentrality") { graph.computeBetweennessCentralities(unitWeight); for (Id i = 0; i <= 3; ++i) { - REQUIRE(graph.node(i)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(i)->betweennessCentrality(), doctest::Approx(0.0)); + REQUIRE(graph.node(i).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(i).betweennessCentrality(), doctest::Approx(0.0)); } } @@ -1824,15 +1823,15 @@ TEST_CASE("BetweennessCentrality") { graph.addStreets(s01, s02, s13, s23); graph.computeBetweennessCentralities( - [](auto const& pEdge) { return pEdge->length(); }); + [](auto const& pEdge) { return pEdge.length(); }); // Node 1 is on the only shortest path 0->3 -> BC = 1 - REQUIRE(graph.node(1)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(1)->betweennessCentrality(), doctest::Approx(1.0)); + REQUIRE(graph.node(1).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(1).betweennessCentrality(), doctest::Approx(1.0)); // Node 2 is not on any shortest path between other pairs -> BC = 0 - REQUIRE(graph.node(2)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(2)->betweennessCentrality(), doctest::Approx(0.0)); + REQUIRE(graph.node(2).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(2).betweennessCentrality(), doctest::Approx(0.0)); } SUBCASE("Single edge") { @@ -1842,10 +1841,10 @@ TEST_CASE("BetweennessCentrality") { graph.computeBetweennessCentralities(unitWeight); - REQUIRE(graph.node(0)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(0)->betweennessCentrality(), doctest::Approx(0.0)); - REQUIRE(graph.node(1)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(1)->betweennessCentrality(), doctest::Approx(0.0)); + REQUIRE(graph.node(0).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(0).betweennessCentrality(), doctest::Approx(0.0)); + REQUIRE(graph.node(1).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(1).betweennessCentrality(), doctest::Approx(0.0)); } SUBCASE("Disconnected graph") { @@ -1859,8 +1858,8 @@ TEST_CASE("BetweennessCentrality") { graph.computeBetweennessCentralities(unitWeight); for (Id i = 0; i <= 3; ++i) { - REQUIRE(graph.node(i)->betweennessCentrality().has_value()); - CHECK_EQ(*graph.node(i)->betweennessCentrality(), doctest::Approx(0.0)); + REQUIRE(graph.node(i).betweennessCentrality().has_value()); + CHECK_EQ(*graph.node(i).betweennessCentrality(), doctest::Approx(0.0)); } } } @@ -1881,16 +1880,16 @@ TEST_CASE("EdgeBetweennessCentrality") { graph.computeEdgeBetweennessCentralities(unitWeight); - REQUIRE(graph.edge(static_cast(0))->betweennessCentrality().has_value()); - CHECK_EQ(*graph.edge(static_cast(0))->betweennessCentrality(), + REQUIRE(graph.edge(static_cast(0)).betweennessCentrality().has_value()); + CHECK_EQ(graph.edge(static_cast(0)).betweennessCentrality(), doctest::Approx(3.0)); - REQUIRE(graph.edge(static_cast(1))->betweennessCentrality().has_value()); - CHECK_EQ(*graph.edge(static_cast(1))->betweennessCentrality(), + REQUIRE(graph.edge(static_cast(1)).betweennessCentrality().has_value()); + CHECK_EQ(graph.edge(static_cast(1)).betweennessCentrality(), doctest::Approx(4.0)); - REQUIRE(graph.edge(static_cast(2))->betweennessCentrality().has_value()); - CHECK_EQ(*graph.edge(static_cast(2))->betweennessCentrality(), + REQUIRE(graph.edge(static_cast(2)).betweennessCentrality().has_value()); + CHECK_EQ(graph.edge(static_cast(2)).betweennessCentrality(), doctest::Approx(3.0)); } @@ -1911,26 +1910,26 @@ TEST_CASE("EdgeBetweennessCentrality") { graph.addStreets(s01, s02, s13, s23); graph.computeEdgeBetweennessCentralities( - [](auto const& pEdge) { return pEdge->length(); }); + [](auto const& pEdge) { return pEdge.length(); }); // Edge 0->1: used by 0->1 and 0->3 (via 0->1->3) => EBC = 2 - REQUIRE(graph.edge(static_cast(0))->betweennessCentrality().has_value()); - CHECK_EQ(*graph.edge(static_cast(0))->betweennessCentrality(), + REQUIRE(graph.edge(static_cast(0)).betweennessCentrality().has_value()); + CHECK_EQ(graph.edge(static_cast(0)).betweennessCentrality(), doctest::Approx(2.0)); // Edge 0->2: used only by 0->2 => EBC = 1 - REQUIRE(graph.edge(static_cast(1))->betweennessCentrality().has_value()); - CHECK_EQ(*graph.edge(static_cast(1))->betweennessCentrality(), + REQUIRE(graph.edge(static_cast(1)).betweennessCentrality().has_value()); + CHECK_EQ(graph.edge(static_cast(1)).betweennessCentrality(), doctest::Approx(1.0)); // Edge 1->3: used by 1->3 and 0->3 => EBC = 2 - REQUIRE(graph.edge(static_cast(2))->betweennessCentrality().has_value()); - CHECK_EQ(*graph.edge(static_cast(2))->betweennessCentrality(), + REQUIRE(graph.edge(static_cast(2)).betweennessCentrality().has_value()); + CHECK_EQ(graph.edge(static_cast(2)).betweennessCentrality(), doctest::Approx(2.0)); // Edge 2->3: used only by 2->3 => EBC = 1 - REQUIRE(graph.edge(static_cast(3))->betweennessCentrality().has_value()); - CHECK_EQ(*graph.edge(static_cast(3))->betweennessCentrality(), + REQUIRE(graph.edge(static_cast(3)).betweennessCentrality().has_value()); + CHECK_EQ(graph.edge(static_cast(3)).betweennessCentrality(), doctest::Approx(1.0)); } @@ -1942,8 +1941,8 @@ TEST_CASE("EdgeBetweennessCentrality") { graph.computeEdgeBetweennessCentralities(unitWeight); // Only one path: 0->1, using edge 0 => EBC = 1 - REQUIRE(graph.edge(static_cast(0))->betweennessCentrality().has_value()); - CHECK_EQ(*graph.edge(static_cast(0))->betweennessCentrality(), + REQUIRE(graph.edge(static_cast(0)).betweennessCentrality().has_value()); + CHECK_EQ(graph.edge(static_cast(0)).betweennessCentrality(), doctest::Approx(1.0)); } } From d1b7f7b8ce69b7e09d6f67680a4e5f18029487b5 Mon Sep 17 00:00:00 2001 From: grufoony Date: Mon, 13 Apr 2026 11:21:58 +0200 Subject: [PATCH 2/3] Bump version --- CITATION.cff | 4 ++-- src/dsf/dsf.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CITATION.cff b/CITATION.cff index a6cbeeea..88862de6 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -12,5 +12,5 @@ authors: repository-code: 'https://github.com/physycom/DynamicalSystemFramework' url: 'https://physycom.github.io/DynamicalSystemFramework/' license: CC-BY-NC-SA-4.0 -version: 5.3.3 -date-released: '2026-04-09' +version: 5.3.4 +date-released: '2026-04-13' diff --git a/src/dsf/dsf.hpp b/src/dsf/dsf.hpp index 65d0e9ad..14afdf8c 100644 --- a/src/dsf/dsf.hpp +++ b/src/dsf/dsf.hpp @@ -9,7 +9,7 @@ static constexpr uint8_t DSF_VERSION_MAJOR = 5; static constexpr uint8_t DSF_VERSION_MINOR = 3; -static constexpr uint8_t DSF_VERSION_PATCH = 3; +static constexpr uint8_t DSF_VERSION_PATCH = 4; static auto const DSF_VERSION = std::format("{}.{}.{}", DSF_VERSION_MAJOR, DSF_VERSION_MINOR, DSF_VERSION_PATCH); From a53f5a3e6578df680ad63f1ff8172045521397d2 Mon Sep 17 00:00:00 2001 From: grufoony Date: Mon, 13 Apr 2026 12:53:46 +0200 Subject: [PATCH 3/3] Fix --- benchmark/Bench_Network.cpp | 9 ++++----- examples/stalingrado.cpp | 6 +++--- profiling/parse_massif.cpp | 15 +++++++++------ src/dsf/base/Dynamics.hpp | 3 +++ src/dsf/base/Network.hpp | 12 ++++++------ src/dsf/bindings.cpp | 16 +++++++++------- src/dsf/mobility/Street.hpp | 1 + 7 files changed, 35 insertions(+), 27 deletions(-) diff --git a/benchmark/Bench_Network.cpp b/benchmark/Bench_Network.cpp index 937b03d8..f8db3b1c 100644 --- a/benchmark/Bench_Network.cpp +++ b/benchmark/Bench_Network.cpp @@ -63,8 +63,8 @@ static void BM_RoadNetwork_AllPathsTo(benchmark::State& state) { network.importNodeProperties((DATA_FOLDER / "forlì_nodes.csv").string()); auto itNode = network.nodes().cbegin(); for (auto _ : state) { - auto paths = network.allPathsTo(itNode->first, - [](auto const& pEdge) { return pEdge->length(); }); + auto paths = + network.allPathsTo(itNode->first, [](auto const& edge) { return edge.length(); }); ++itNode; } } @@ -75,9 +75,8 @@ static void BM_RoadNetwork_ShortestPath(benchmark::State& state) { auto itSource = network.nodes().cbegin(); auto itTarget = std::next(network.nodes().cbegin(), network.nodes().size() / 2); for (auto _ : state) { - auto path = network.shortestPath(itSource->first, - itTarget->first, - [](auto const& pEdge) { return pEdge->length(); }); + auto path = network.shortestPath( + itSource->first, itTarget->first, [](auto const& edge) { return edge.length(); }); benchmark::DoNotOptimize(path); ++itSource; ++itTarget; diff --git a/examples/stalingrado.cpp b/examples/stalingrado.cpp index 4eef36c0..94c93d03 100644 --- a/examples/stalingrado.cpp +++ b/examples/stalingrado.cpp @@ -74,7 +74,7 @@ int main() { graph.addStreets(s01, s12, s23, s34); graph.adjustNodeCapacities(); graph.addCoil(19); - auto const& coil = graph.edge(19); + auto& coil = graph.edge(19); // Create the dynamics FirstOrderDynamics dynamics{graph, false, 69}; @@ -101,8 +101,8 @@ int main() { ++it; } if (progress % 300 == 0) { - ofs << progress << ';' << coil->counts() << std::endl; - coil->resetCounter(); + ofs << progress << ';' << coil.counts() << std::endl; + coil.resetCounter(); } dynamics.addAgents(*it, pItinerary, 0); } diff --git a/profiling/parse_massif.cpp b/profiling/parse_massif.cpp index 19837d3e..48d45df6 100644 --- a/profiling/parse_massif.cpp +++ b/profiling/parse_massif.cpp @@ -16,19 +16,17 @@ std::vector parse_massif(const std::string& file_path) { const char equal{'='}; std::string buffer; - while (getline(file, buffer)) { + while (std::getline(file, buffer)) { if (buffer.find("mem_heap_B") != std::string::npos) { std::stringstream buffer_stream(buffer); std::string value; - getline(buffer_stream, value, equal); + std::getline(buffer_stream, value, equal); if (value == "mem_heap_B") { - getline(buffer_stream, value); + std::getline(buffer_stream, value); mem_values.push_back(std::stol(value)); } } } - - file.close(); return mem_values; }; @@ -39,7 +37,12 @@ int main(int argc, char* argv[]) { } auto mem_values{parse_massif(argv[1])}; - long long int integral{std::accumulate(mem_values.begin(), mem_values.end(), 0)}; + if (mem_values.empty()) { + std::cerr << "No mem_heap_B values found in file: " << argv[1] << '\n'; + return 1; + } + + long long integral{std::accumulate(mem_values.begin(), mem_values.end(), 0LL)}; long max_mem{*std::max_element(mem_values.begin(), mem_values.end())}; std::cout << "integral: " << integral << " B\n"; diff --git a/src/dsf/base/Dynamics.hpp b/src/dsf/base/Dynamics.hpp index 162e7058..39ad7db0 100644 --- a/src/dsf/base/Dynamics.hpp +++ b/src/dsf/base/Dynamics.hpp @@ -119,6 +119,9 @@ namespace dsf { /// @brief Get the graph /// @return const network_t&, The graph inline auto const& graph() const { return m_graph; }; + /// @brief Get the graph (mutable) + /// @return network_t&, The graph + inline auto& graph() { return m_graph; }; /// @brief Get the id of the simulation /// @return const Id&, The id of the simulation inline auto const& id() const { return m_id; }; diff --git a/src/dsf/base/Network.hpp b/src/dsf/base/Network.hpp index fcacd02c..128f1e9d 100644 --- a/src/dsf/base/Network.hpp +++ b/src/dsf/base/Network.hpp @@ -65,20 +65,20 @@ namespace dsf { /// @brief Get a node by id /// @param nodeId The node's id - /// @return node_t& A reference to the node - inline node_t& node(Id nodeId) const { return *m_nodes.at(nodeId); }; + /// @return const node_t& A reference to the node + inline const auto& node(Id nodeId) const { return *m_nodes.at(nodeId); }; /// @brief Get a node by id /// @param nodeId The node's id /// @return node_t& A reference to the node - inline node_t& node(Id nodeId) { return *m_nodes.at(nodeId); }; + inline auto& node(Id nodeId) { return *m_nodes.at(nodeId); }; /// @brief Get an edge by id /// @param edgeId The edge's id - /// @return edge_t& A reference to the edge - inline edge_t& edge(Id edgeId) const { return *m_edges.at(edgeId); }; + /// @return const edge_t& A reference to the edge + inline const auto& edge(Id edgeId) const { return *m_edges.at(edgeId); }; /// @brief Get an edge by id /// @param edgeId The edge's id /// @return edge_t& A reference to the edge - inline edge_t& edge(Id edgeId) { return *m_edges.at(edgeId); } + inline auto& edge(Id edgeId) { return *m_edges.at(edgeId); } edge_t& edge(Id source, Id target) const; /// @brief Get a node by id diff --git a/src/dsf/bindings.cpp b/src/dsf/bindings.cpp index 91c85c20..08065e0e 100644 --- a/src/dsf/bindings.cpp +++ b/src/dsf/bindings.cpp @@ -468,10 +468,9 @@ PYBIND11_MODULE(dsf_cpp, m) { reinterpret_cast(arg.cast()); self.setSpeedFunction( dsf::SpeedFunction::CUSTOM, - [func_ptr]( - std::unique_ptr const& pStreet) -> double { + [func_ptr](dsf::mobility::Street const& street) -> double { // No GIL needed — this is pure C - return func_ptr(pStreet->maxSpeed(), pStreet->density(true)); + return func_ptr(street.maxSpeed(), street.density(true)); }); break; } @@ -654,10 +653,13 @@ PYBIND11_MODULE(dsf_cpp, m) { pybind11::arg("ratio") = 1.3, dsf::g_docstrings.at("dsf::mobility::FirstOrderDynamics::optimizeTrafficLights") .c_str()) - .def("graph", - &dsf::mobility::FirstOrderDynamics::graph, - pybind11::return_value_policy::reference_internal, - dsf::g_docstrings.at("dsf::Dynamics::graph").c_str()) + .def( + "graph", + [](dsf::mobility::FirstOrderDynamics& self) -> dsf::mobility::RoadNetwork& { + return self.graph(); + }, + pybind11::return_value_policy::reference_internal, + dsf::g_docstrings.at("dsf::Dynamics::graph").c_str()) .def("nAgents", &dsf::mobility::FirstOrderDynamics::nAgents, dsf::g_docstrings.at("dsf::mobility::FirstOrderDynamics::nAgents").c_str()) diff --git a/src/dsf/mobility/Street.hpp b/src/dsf/mobility/Street.hpp index 7559d89a..a2d589c6 100644 --- a/src/dsf/mobility/Street.hpp +++ b/src/dsf/mobility/Street.hpp @@ -165,6 +165,7 @@ namespace dsf::mobility { movingAgents() { return m_movingAgents; } + inline const auto& movingAgents() const { return m_movingAgents; } static inline auto agentData() { if (!m_agentData.has_value()) { throw std::runtime_error(