diff --git a/src/dsm/dsm.hpp b/src/dsm/dsm.hpp index c9aab8628..2627078a8 100644 --- a/src/dsm/dsm.hpp +++ b/src/dsm/dsm.hpp @@ -6,7 +6,7 @@ static constexpr uint8_t DSM_VERSION_MAJOR = 2; static constexpr uint8_t DSM_VERSION_MINOR = 2; -static constexpr uint8_t DSM_VERSION_PATCH = 2; +static constexpr uint8_t DSM_VERSION_PATCH = 3; static auto const DSM_VERSION = std::format("{}.{}.{}", DSM_VERSION_MAJOR, DSM_VERSION_MINOR, DSM_VERSION_PATCH); diff --git a/src/dsm/headers/Agent.hpp b/src/dsm/headers/Agent.hpp index fdfc65eb0..3d2ed8be0 100644 --- a/src/dsm/headers/Agent.hpp +++ b/src/dsm/headers/Agent.hpp @@ -17,47 +17,42 @@ #include "../utility/Typedef.hpp" #include -#include #include #include +#include +#include namespace dsm { /// @brief The Agent class represents an agent in the network. - /// @tparam Id, The type of the agent's id. It must be an unsigned integral type. - /// @tparam Size, The type of the size of a street. It must be an unsigned integral type. /// @tparam delay_t, The type of the agent's delay. It must be a numeric type (see utility/TypeTraits/is_numeric.hpp). template requires(is_numeric_v) class Agent { private: Id m_id; - Id m_itineraryId; + std::vector m_trip; std::optional m_streetId; std::optional m_srcNodeId; delay_t m_delay; double m_speed; double m_distance; // Travelled distance unsigned int m_time; // Travelled time + size_t m_itineraryIdx; public: /// @brief Construct a new Agent object /// @param id The agent's id /// @param itineraryId The agent's itinerary - Agent(Id id, Id itineraryId); + /// @param srcNodeId Optional, The id of the source node of the agent + Agent(Id id, Id itineraryId, std::optional srcNodeId = std::nullopt); /// @brief Construct a new Agent object /// @param id The agent's id - /// @param itineraryId The agent's itinerary - /// @param srcNodeId The id of the source node of the agent - Agent(Id id, Id itineraryId, Id srcNodeId); + /// @param itineraryIds The agent's itinerary + /// @param srcNodeId Optional, The id of the source node of the agent + Agent(Id id, std::vector const& trip, std::optional srcNodeId = std::nullopt); /// @brief Set the street occupied by the agent /// @param streetId The id of the street currently occupied by the agent void setStreetId(Id streetId) { m_streetId = streetId; } - /// @brief Set the source node id of the agent - /// @param srcNodeId The id of the source node of the agent - void setSourceNodeId(Id srcNodeId) { m_srcNodeId = srcNodeId; } - /// @brief Set the agent's itinerary - /// @param itineraryId The agent's itinerary - void setItineraryId(Id itineraryId) { m_itineraryId = itineraryId; } /// @brief Set the agent's speed /// @param speed, The agent's speed /// @throw std::invalid_argument, if speed is negative @@ -87,13 +82,29 @@ namespace dsm { void incrementTime(unsigned int const time); /// @brief Reset the agent's time to 0 void resetTime() { m_time = 0; } + /// @brief Update the agent's itinerary + /// @details If possible, the agent's itinerary is updated by removing the first element + /// from the itinerary's vector. + void updateItinerary(); + /// @brief Reset the agent + /// @details Reset the following values: + /// - street id = std::nullopt + /// - delay = 0 + /// - speed = 0 + /// - distance = 0 + /// - time = 0 + /// - itinerary index = 0 + void reset(); /// @brief Get the agent's id /// @return The agent's id Id id() const { return m_id; } /// @brief Get the agent's itinerary /// @return The agent's itinerary - Id itineraryId() const { return m_itineraryId; } + Id itineraryId() const { return m_trip[m_itineraryIdx]; } + /// @brief Get the agent's trip + /// @return The agent's trip + std::vector const& trip() const { return m_trip; } /// @brief Get the id of the street currently occupied by the agent /// @return The id of the street currently occupied by the agent std::optional streetId() const { return m_streetId; } @@ -116,24 +127,27 @@ namespace dsm { template requires(is_numeric_v) - Agent::Agent(Id id, Id itineraryId) + Agent::Agent(Id id, Id itineraryId, std::optional srcNodeId) : m_id{id}, - m_itineraryId{itineraryId}, + m_trip{itineraryId}, + m_srcNodeId{srcNodeId}, m_delay{0}, m_speed{0.}, m_distance{0.}, - m_time{0} {} + m_time{0}, + m_itineraryIdx{0} {} template requires(is_numeric_v) - Agent::Agent(Id id, Id itineraryId, Id srcNodeId) + Agent::Agent(Id id, std::vector const& trip, std::optional srcNodeId) : m_id{id}, - m_itineraryId{itineraryId}, + m_trip{trip}, m_srcNodeId{srcNodeId}, m_delay{0}, m_speed{0.}, m_distance{0.}, - m_time{0} {} + m_time{0}, + m_itineraryIdx{0} {} template requires(is_numeric_v) @@ -143,6 +157,23 @@ namespace dsm { } m_speed = speed; } + template + requires(is_numeric_v) + void Agent::updateItinerary() { + if (m_itineraryIdx < m_trip.size() - 1) { + ++m_itineraryIdx; + } + } + template + requires(is_numeric_v) + void Agent::reset() { + m_streetId = std::nullopt; + m_delay = 0; + m_speed = 0.; + m_distance = 0.; + m_time = 0; + m_itineraryIdx = 0; + } template requires(is_numeric_v) void Agent::incrementDelay() { diff --git a/src/dsm/headers/Dynamics.hpp b/src/dsm/headers/Dynamics.hpp index f96b8fc44..f1a401c5f 100644 --- a/src/dsm/headers/Dynamics.hpp +++ b/src/dsm/headers/Dynamics.hpp @@ -81,8 +81,8 @@ namespace dsm { /// @brief Update the path of a single itinerary using Dijsktra's algorithm /// @param pItinerary An std::unique_prt to the itinerary void m_updatePath(const std::unique_ptr& pItinerary) { - const Size dimension = m_graph.adjMatrix().getRowDim(); - const auto destinationID = pItinerary->destination(); + Size const dimension = m_graph.adjMatrix().getRowDim(); + auto const destinationID = pItinerary->destination(); SparseMatrix path{dimension, dimension}; // cycle over the nodes for (const auto& [nodeId, node] : m_graph.nodeSet()) { @@ -403,10 +403,7 @@ namespace dsm { agentId = m_agents.rbegin()->first + 1; } for (Size i{0}; i < nAgents; ++i, ++agentId) { - this->addAgent(Agent{agentId, itineraryId}); - if (srcNodeId.has_value()) { - m_agents[agentId]->setSourceNodeId(srcNodeId.value()); - } + this->addAgent(Agent{agentId, itineraryId, srcNodeId}); } } diff --git a/src/dsm/headers/Itinerary.cpp b/src/dsm/headers/Itinerary.cpp index 11496b04e..9121a0321 100644 --- a/src/dsm/headers/Itinerary.cpp +++ b/src/dsm/headers/Itinerary.cpp @@ -4,14 +4,6 @@ namespace dsm { Itinerary::Itinerary(Id id, Id destination) : m_id{id}, m_destination{destination} {} - Itinerary::Itinerary(Id id, Id destination, SparseMatrix path) - : m_id{id}, m_path{std::move(path)}, m_destination{destination} {} - - void Itinerary::setDestination(Id destination) { - m_destination = destination; - this->m_path.clear(); - } - void Itinerary::setPath(SparseMatrix path) { if (path.getRowDim() != path.getColDim()) { throw std::invalid_argument(buildLog( diff --git a/src/dsm/headers/Itinerary.hpp b/src/dsm/headers/Itinerary.hpp index 754218be3..06f3410a2 100644 --- a/src/dsm/headers/Itinerary.hpp +++ b/src/dsm/headers/Itinerary.hpp @@ -22,21 +22,14 @@ namespace dsm { class Itinerary { private: Id m_id; - SparseMatrix m_path; Id m_destination; + SparseMatrix m_path; public: /// @brief Construct a new Itinerary object /// @param destination The itinerary's destination Itinerary(Id id, Id destination); - /// @brief Construct a new Itinerary object - /// @param destination The itinerary's destination - /// @param path An adjacency matrix made by a SparseMatrix representing the itinerary's path - Itinerary(Id id, Id destination, SparseMatrix path); - /// @brief Set the itinerary's destination - /// @param destination The itinerary's destination - void setDestination(Id destination); /// @brief Set the itinerary's path /// @param path An adjacency matrix made by a SparseMatrix representing the itinerary's path /// @throw std::invalid_argument, if the itinerary's source or destination is not in the path's diff --git a/src/dsm/headers/RoadDynamics.hpp b/src/dsm/headers/RoadDynamics.hpp index 3262a15b5..e47ebe4b5 100644 --- a/src/dsm/headers/RoadDynamics.hpp +++ b/src/dsm/headers/RoadDynamics.hpp @@ -243,10 +243,11 @@ namespace dsm { continue; } const auto agentId{pStreet->queue(queueIndex).front()}; - if (this->m_agents[agentId]->delay() > 0) { + auto const& pAgent{this->m_agents[agentId]}; + if (pAgent->delay() > 0) { continue; } - this->m_agents[agentId]->setSpeed(0.); + pAgent->setSpeed(0.); const auto& destinationNode{this->m_graph.nodeSet()[pStreet->nodePair().second]}; if (destinationNode->isFull()) { continue; @@ -259,19 +260,12 @@ namespace dsm { } } if (destinationNode->id() == - this->m_itineraries[this->m_agents[agentId]->itineraryId()]->destination()) { + this->m_itineraries[pAgent->itineraryId()]->destination()) { pStreet->dequeue(queueIndex); - m_travelTimes.push_back(this->m_agents[agentId]->time()); + m_travelTimes.push_back(pAgent->time()); if (reinsert_agents) { - // take last agent id in map - Agent newAgent{static_cast(this->m_agents.rbegin()->first + 1), - this->m_agents[agentId]->itineraryId(), - this->m_agents[agentId]->srcNodeId().value()}; - if (this->m_agents[agentId]->srcNodeId().has_value()) { - newAgent.setSourceNodeId(this->m_agents[agentId]->srcNodeId().value()); - } - this->removeAgent(agentId); - this->addAgent(newAgent); + // reset Agent's values + pAgent->reset(); } else { this->removeAgent(agentId); } @@ -372,6 +366,10 @@ namespace dsm { agent->decrementDelay(); if (agent->delay() == 0) { auto const nLanes = street->nLanes(); + if (this->m_itineraries[agent->itineraryId()]->destination() == + street->nodePair().second) { + agent->updateItinerary(); + } if (this->m_itineraries[agent->itineraryId()]->destination() == street->nodePair().second) { std::uniform_int_distribution laneDist{ diff --git a/test/Test_dynamics.cpp b/test/Test_dynamics.cpp index 07e554da4..cc38aea6f 100644 --- a/test/Test_dynamics.cpp +++ b/test/Test_dynamics.cpp @@ -473,9 +473,7 @@ TEST_CASE("Dynamics") { CHECK_EQ(dynamics.agents().at(0)->speed(), 13.8888888889); } dynamics.evolve(false); - THEN("And again, reaching the destination") { - CHECK_EQ(dynamics.agents().size(), 0); - } + THEN("And again, reaching the destination") { CHECK(dynamics.agents().empty()); } } } GIVEN("A dynamics object, an itinerary and an agent") { @@ -500,9 +498,7 @@ TEST_CASE("Dynamics") { CHECK_EQ(dynamics.agents().at(0)->distance(), 13.8888888889); } dynamics.evolve(false); - THEN("The agent reaches the destination") { - CHECK_EQ(dynamics.agents().size(), 0); - } + THEN("The agent reaches the destination") { CHECK(dynamics.agents().empty()); } } } GIVEN("A dynamics object, an itinerary and an agent") { @@ -529,13 +525,48 @@ TEST_CASE("Dynamics") { dynamics.evolve(true); THEN("The agent is reinserted") { CHECK_EQ(dynamics.agents().size(), 1); - CHECK_EQ(dynamics.agents().at(1)->time(), 1); - CHECK_EQ(dynamics.agents().at(1)->delay(), 0); - CHECK_FALSE(dynamics.agents().at(1)->streetId().has_value()); - CHECK_EQ(dynamics.agents().at(1)->speed(), 0.); + CHECK_EQ(dynamics.agents().at(0)->time(), 1); + CHECK_EQ(dynamics.agents().at(0)->delay(), 0); + CHECK_FALSE(dynamics.agents().at(0)->streetId().has_value()); + CHECK_EQ(dynamics.agents().at(0)->speed(), 0.); } } } + GIVEN("A simple network and an agent with forced itinerary") { + Street s0_1{1, 1, 30., 15., std::make_pair(0, 1)}; + Street s1_0{3, 1, 30., 15., std::make_pair(1, 0)}; + Street s1_2{5, 1, 30., 15., std::make_pair(1, 2)}; + Street s2_1{7, 1, 30., 15., std::make_pair(2, 1)}; + Graph graph2; + graph2.addStreets(s0_1, s1_0, s1_2, s2_1); + graph2.buildAdj(); + Dynamics dynamics{graph2, 69}; + std::vector dsts{1, 2}; + dynamics.setDestinationNodes(dsts); + std::vector trip{2, 1}; + Agent agent{0, trip, 0}; + dynamics.addAgent(agent); + auto const& pAgent{dynamics.agents().at(0)}; + WHEN("We evolve the dynamics") { + dynamics.evolve(false); + dynamics.evolve(false); + dynamics.evolve(false); + dynamics.evolve(false); + dynamics.evolve(false); + THEN("The agent goes first into node 2") { + 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") { + CHECK_EQ(pAgent->streetId().value(), 7); + CHECK_EQ(pAgent->distance(), 90.); + } + dynamics.evolve(false); + THEN("The agent reaches the destination") { CHECK(dynamics.agents().empty()); } + } + } } SUBCASE("TrafficLights") { GIVEN( @@ -836,7 +867,7 @@ TEST_CASE("Dynamics") { THEN("The agent with priority leaves the roundabout") { CHECK_EQ(dynamics.agents().at(0)->streetId().value(), 5); CHECK_EQ(dynamics.agents().at(1)->streetId().value(), 3); - CHECK_EQ(rb.agents().size(), 0); + CHECK(rb.agents().empty()); } } } diff --git a/test/Test_itinerary.cpp b/test/Test_itinerary.cpp index 149b39bff..886f4b428 100644 --- a/test/Test_itinerary.cpp +++ b/test/Test_itinerary.cpp @@ -8,9 +8,9 @@ using Itinerary = dsm::Itinerary; TEST_CASE("Itinerary") { SUBCASE("Constructors") { - GIVEN("An itinerary and its destination ids") { - uint8_t itineraryId{0}; - uint8_t destinationId{2}; + GIVEN("Some parameters") { + dsm::Id itineraryId{0}; + dsm::Id destinationId{2}; WHEN("The Itinerary is constructed") { Itinerary itinerary{itineraryId, destinationId}; THEN("The source and destination are set correctly") { @@ -19,37 +19,5 @@ TEST_CASE("Itinerary") { } } } - GIVEN("An itinerary id, its destination id and a transition matrix") { - uint8_t itineraryId{0}; - uint8_t destinationId{2}; - dsm::SparseMatrix path{1, 1}; - WHEN("The Itinerary is constructed") { - Itinerary itinerary{itineraryId, destinationId, path}; - THEN("The source, destination, and path are set correctly") { - CHECK_EQ(itinerary.id(), itineraryId); - CHECK_EQ(itinerary.destination(), destinationId); - CHECK_EQ(itinerary.path().getRowDim(), 1); - CHECK_EQ(itinerary.path().getColDim(), 1); - } - } - } - } - SUBCASE("Set destination") { - GIVEN("An itinerary id, its destination id and a transition matrix") { - uint8_t itineraryId{0}; - uint8_t destinationId{2}; - dsm::SparseMatrix path{1, 1}; - Itinerary itinerary{itineraryId, destinationId, path}; - WHEN("The destination is set") { - uint8_t newDestinationId{3}; - itinerary.setDestination(newDestinationId); - THEN("The destination is set correctly and the path is cleared") { - CHECK_EQ(itinerary.destination(), newDestinationId); - CHECK_EQ(itinerary.path().getRowDim(), 0); - CHECK_EQ(itinerary.path().getColDim(), 0); - CHECK_EQ(itinerary.path().size(), 0); - } - } - } } }