From d0b1e33d1dc5971e193a9fea83c03e6e8a97eedd Mon Sep 17 00:00:00 2001 From: Gregorio Berselli Date: Sat, 18 May 2024 10:58:32 +0200 Subject: [PATCH] Add `Dynamics::optimizeTrafficLights` function (#176) * Add function to optimize traffic lights * Fix optimizing function * Fixes * Add mean over time * Final change * Add `Street::normDensity()` getter --- CMakeLists.txt | 2 +- src/dsm/dsm.hpp | 2 +- src/dsm/headers/Dynamics.hpp | 91 +++++++++++++++++++++++++++++++----- src/dsm/headers/Street.hpp | 19 +++++--- test/CMakeLists.txt | 2 +- 5 files changed, 95 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c656024..5fe101fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.0) -project(dms VERSION 1.3.7 LANGUAGES CXX) +project(dms VERSION 1.3.8 LANGUAGES CXX) # set the C++ standard set(CMAKE_CXX_STANDARD 20) diff --git a/src/dsm/dsm.hpp b/src/dsm/dsm.hpp index 9c7e03e2..634d4404 100644 --- a/src/dsm/dsm.hpp +++ b/src/dsm/dsm.hpp @@ -5,7 +5,7 @@ static constexpr uint8_t DSM_VERSION_MAJOR = 1; static constexpr uint8_t DSM_VERSION_MINOR = 3; -static constexpr uint8_t DSM_VERSION_PATCH = 7; +static constexpr uint8_t DSM_VERSION_PATCH = 8; static constexpr uint8_t DSM_VERSION_BUILD = 0; #include diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index 06307dd7..5887d5ba 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -83,6 +83,7 @@ namespace dsm { std::unordered_map m_agentNextStreetId; bool m_forcePriorities; std::array m_turnCounts; + std::unordered_map m_streetTails; /// @brief Get the next street id /// @param agentId The id of the agent @@ -149,6 +150,12 @@ namespace dsm { /// - Cycle over agents and update their times /// @param reinsert_agents If true, the agents are reinserted in the simulation after they reach their destination virtual void evolve(bool reinsert_agents = false); + /// @brief Optimize the traffic lights by changing the green and red times + /// @param percentage double, The percentage of the TOTAL cycle time to add or subtract to the green time + /// @param threshold double, The percentage of the mean capacity of the streets used as threshold for the delta between the two tails. + /// @details The function cycles over the traffic lights and, if the difference between the two tails is greater than + /// the threshold multiplied by the mean capacity of the streets, it changes the green and red times of the traffic light, keeping the total cycle time constant. + void optimizeTrafficLights(double percentage, double threshold = 0.); /// @brief Get the graph /// @return const Graph&, The graph @@ -267,13 +274,15 @@ namespace dsm { /// @return Measurement The mean flow of the streets and the standard deviation Measurement streetMeanFlow(double threshold, bool above) const; /// @brief Get the mean spire input flow of the streets in \f$s^{-1}\f$ + /// @param resetValue If true, the spire input/output flows are cleared after the computation /// @return Measurement The mean spire input flow of the streets and the standard deviation /// @details The spire input flow is computed as the sum of counts over the product of the number of spires and the time delta - Measurement meanSpireInputFlow(); + Measurement meanSpireInputFlow(bool resetValue = true); /// @brief Get the mean spire output flow of the streets in \f$s^{-1}\f$ + /// @param resetValue If true, the spire output/input flows are cleared after the computation /// @return Measurement The mean spire output flow of the streets and the standard deviation /// @details The spire output flow is computed as the sum of counts over the product of the number of spires and the time delta - Measurement meanSpireOutputFlow(); + Measurement meanSpireOutputFlow(bool resetValue = true); /// @brief Get the mean travel time of the agents in \f$s\f$ /// @param clearData If true, the travel times are cleared after the computation /// @return Measurement The mean travel time of the agents and the standard @@ -298,7 +307,11 @@ namespace dsm { m_errorProbability{0.}, m_minSpeedRateo{0.}, m_forcePriorities{false}, - m_turnCounts{0, 0, 0} {} + m_turnCounts{0, 0, 0} { + for (const auto& [streetId, street] : m_graph.streetSet()) { + m_streetTails.emplace(streetId, 0); + } + } template requires(std::unsigned_integral && std::unsigned_integral && @@ -354,6 +367,9 @@ namespace dsm { if (street->queue().empty()) { continue; } + if (m_time % 30 == 0) { + m_streetTails[streetId] += street->queue().size(); + } const auto agentId{street->queue().front()}; if (m_agents[agentId]->delay() > 0) { continue; @@ -615,6 +631,57 @@ namespace dsm { ++this->m_time; } + template + requires(std::unsigned_integral && std::unsigned_integral && + is_numeric_v) + void Dynamics::optimizeTrafficLights(double percentage, + double threshold) { + for (const auto& [nodeId, node] : m_graph.nodeSet()) { + if (!node->isTrafficLight()) { + continue; + } + auto& tl = dynamic_cast&>(*node); + const auto& streetPriorities = tl.streetPriorities(); + Size greenSum{0}; + Size redSum{0}; + Size meanCapacity{0}; + Size i{0}; + for (const auto& [streetId, _] : m_graph.adjMatrix().getCol(nodeId, true)) { + streetPriorities.contains(streetId) ? greenSum += m_streetTails[streetId] + : redSum += m_streetTails[streetId]; + meanCapacity += m_graph.streetSet()[streetId]->capacity(); + ++i; + } + if (std::abs(static_cast(greenSum - redSum)) < + threshold * (static_cast(meanCapacity) / i) or + !tl.delay().has_value()) { + continue; + } + auto [greenTime, redTime] = tl.delay().value(); + const auto cycleTime = greenTime + redTime; + if (greenSum > redSum) { + Delay delta = cycleTime * percentage; + if (redTime > delta and + static_cast(static_cast(redTime - delta) * percentage) > 0) { + greenTime += delta; + redTime -= delta; + tl.setDelay(std::make_pair(greenTime, redTime)); + } + } else { + Delay delta = cycleTime * percentage; + if (greenTime > delta and + static_cast(static_cast(greenTime - delta) * percentage) > 0) { + greenTime -= delta; + redTime += delta; + tl.setDelay(std::make_pair(greenTime, redTime)); + } + } + } + for (auto& [id, element] : m_streetTails) { + element = 0; + } + } + template requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) @@ -871,9 +938,9 @@ namespace dsm { std::vector flows; flows.reserve(m_graph.streetSet().size()); for (const auto& [streetId, street] : m_graph.streetSet()) { - if (above and (street->nAgents() > (threshold * street->capacity()))) { + if (above and (street->normDensity() > threshold)) { flows.push_back(street->density() * this->streetMeanSpeed(streetId)); - } else if (!above and (street->nAgents() < (threshold * street->capacity()))) { + } else if (!above and (street->normDensity() < threshold)) { flows.push_back(street->density() * this->streetMeanSpeed(streetId)); } } @@ -882,7 +949,7 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) - Measurement Dynamics::meanSpireInputFlow() { + Measurement Dynamics::meanSpireInputFlow(bool resetValue) { auto deltaTime{m_time - m_previousSpireTime}; if (deltaTime == 0) { return Measurement(0., 0.); @@ -893,7 +960,7 @@ namespace dsm { for (const auto& [streetId, street] : m_graph.streetSet()) { if (street->isSpire()) { auto& spire = dynamic_cast&>(*street); - flows.push_back(static_cast(spire.inputCounts()) / deltaTime); + flows.push_back(static_cast(spire.inputCounts(resetValue)) / deltaTime); } } return Measurement(flows); @@ -901,7 +968,7 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral && is_numeric_v) - Measurement Dynamics::meanSpireOutputFlow() { + Measurement Dynamics::meanSpireOutputFlow(bool resetValue) { auto deltaTime{m_time - m_previousSpireTime}; if (deltaTime == 0) { return Measurement(0., 0.); @@ -912,7 +979,7 @@ namespace dsm { for (const auto& [streetId, street] : m_graph.streetSet()) { if (street->isSpire()) { auto& spire = dynamic_cast&>(*street); - flows.push_back(static_cast(spire.outputCounts()) / deltaTime); + flows.push_back(static_cast(spire.outputCounts(resetValue)) / deltaTime); } } return Measurement(flows); @@ -998,7 +1065,7 @@ namespace dsm { const auto& agent{this->m_agents[agentId]}; const auto& street{this->m_graph.streetSet()[agent->streetId().value()]}; double speed{street->maxSpeed() * - (1. - this->m_minSpeedRateo * street->nAgents() / street->capacity())}; + (1. - this->m_minSpeedRateo * street->normDensity())}; if (this->m_speedFluctuationSTD > 0.) { std::normal_distribution speedDist{speed, speed * this->m_speedFluctuationSTD}; @@ -1093,11 +1160,11 @@ namespace dsm { speeds.reserve(this->m_graph.streetSet().size()); for (const auto& [streetId, street] : this->m_graph.streetSet()) { if (above) { - if (street->nAgents() > (threshold * street->capacity())) { + if (street->normDensity() > threshold) { speeds.push_back(this->streetMeanSpeed(streetId)); } } else { - if (street->nAgents() < (threshold * street->capacity())) { + if (street->normDensity() < threshold) { speeds.push_back(this->streetMeanSpeed(streetId)); } } diff --git a/src/dsm/headers/Street.hpp b/src/dsm/headers/Street.hpp index d0b1356b..b963d5f4 100644 --- a/src/dsm/headers/Street.hpp +++ b/src/dsm/headers/Street.hpp @@ -146,6 +146,9 @@ namespace dsm { /// @brief Get the street's density in \f$m^{-1}\f$ /// @return double, The street's density double density() const { return nAgents() / m_len; } + /// @brief Get the street's normalized density + /// @return double, The street's normalized density + double normDensity() const { return nAgents() / static_cast(m_capacity); } /// @brief Check if the street is full /// @return bool, True if the street is full, false otherwise bool isFull() const { return nAgents() == m_capacity; } @@ -331,13 +334,13 @@ namespace dsm { void addAgent(Id agentId) override; /// @brief Get the input counts of the street + /// @param resetValue If true, the counter is reset to 0 together with the output counter. /// @return Size The input counts of the street - /// @details Once the input counts are retrieved, the counter is reset to 0 together with the output counter. - Size inputCounts(); + Size inputCounts(bool resetValue = false); /// @brief Get the output counts of the street + /// @param resetValue If true, the counter is reset to 0 together with the input counter. /// @return Size The output counts of the street - /// @details Once the output counts are retrieved, the counter is reset to 0 together with the input counter. - Size outputCounts(); + Size outputCounts(bool resetValue = false); /// @brief Get the mean flow of the street /// @return int The flow of the street, i.e. the difference between input and output flows /// @details Once the flow is retrieved, bothh the input and output flows are reset to 0. @@ -383,7 +386,9 @@ namespace dsm { template requires(std::unsigned_integral && std::unsigned_integral) - Size SpireStreet::inputCounts() { + Size SpireStreet::inputCounts(bool resetValue) { + if (!resetValue) + return m_agentCounterIn; Size flow = m_agentCounterIn; m_agentCounterIn = 0; m_agentCounterOut = 0; @@ -391,7 +396,9 @@ namespace dsm { } template requires(std::unsigned_integral && std::unsigned_integral) - Size SpireStreet::outputCounts() { + Size SpireStreet::outputCounts(bool resetValue) { + if (!resetValue) + return m_agentCounterOut; Size flow = m_agentCounterOut; m_agentCounterIn = 0; m_agentCounterOut = 0; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index bdc371bb..6b846312 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16.0) -project(dsm_tests VERSION 1.3.7 LANGUAGES CXX) +project(dsm_tests VERSION 1.3.8 LANGUAGES CXX) # Set the C++ standard set(CMAKE_CXX_STANDARD 23)