Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/dsf/mobility/RoadDynamics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ namespace dsf::mobility {
m_travelDTs.push_back({pAgent->distance(),
static_cast<double>(this->time_step() - pAgent->spawnTime())});
--m_nAgents;
++m_nKilledAgents;
auto const& streetId = pAgent->streetId();
if (streetId.has_value()) {
auto const& pStreet{this->graph().edge(streetId.value())};
Expand Down Expand Up @@ -705,8 +706,13 @@ namespace dsf::mobility {
pStreet->enqueue(laneDist(this->m_generator));
continue;
}
throw std::runtime_error(std::format(
"No next street found for agent {} at node {}", *pAgent, pStreet->target()));
this->m_killAgent(pStreet->dequeueMovingAgent());
continue;
// Grufoony - 09/03/2026
// The agent is now killed. The old behavior (throw exception) is kept here:
//
// 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())};
pAgent->setNextStreetId(pNextStreet->id());
Expand Down Expand Up @@ -789,7 +795,6 @@ namespace dsf::mobility {
timeDiff);
// Kill the agent
this->m_killAgent(pStreet->dequeue(queueIndex, this->time_step()));
++m_nKilledAgents;
continue;
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/dsf/mobility/Roundabout.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ namespace dsf::mobility {

std::unique_ptr<Agent> Roundabout::dequeue() {
assert(!m_agents.empty());
std::unique_ptr<Agent> pAgent{std::move(m_agents.front())};
m_agents.pop();
return pAgent;
return m_agents.extract_front();
}
} // namespace dsf::mobility
11 changes: 6 additions & 5 deletions src/dsf/mobility/Street.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,26 +135,27 @@ namespace dsf::mobility {
++(*m_counter);
}
}
std::unique_ptr<Agent> Street::dequeueMovingAgent() {
assert(!m_movingAgents.empty());
return m_movingAgents.extract_top();
}
void Street::enqueue(std::size_t const& queueId) {
assert(!m_movingAgents.empty());
m_movingAgents.top()->incrementDistance(m_length);
m_exitQueues[queueId].push(
std::move(const_cast<std::unique_ptr<Agent>&>(m_movingAgents.top())));
m_movingAgents.pop();
m_exitQueues[queueId].push(m_movingAgents.extract_top());
if (m_counter.has_value() && m_counterPosition == CounterPosition::MIDDLE) {
++(*m_counter);
}
}
std::unique_ptr<Agent> Street::dequeue(std::size_t const& index,
std::time_t const currentTime) {
assert(!m_exitQueues[index].empty());
auto pAgent{std::move(m_exitQueues[index].front())};
auto pAgent{m_exitQueues[index].extract_front()};
// Keep track of average speed
m_avgSpeeds.push_back(m_length /
(currentTime - m_agentsInsertionTimes[pAgent->id()]));
m_agentsInsertionTimes.erase(pAgent->id());

m_exitQueues[index].pop();
if (m_counter.has_value() && m_counterPosition == CounterPosition::EXIT) {
++(*m_counter);
}
Expand Down
3 changes: 3 additions & 0 deletions src/dsf/mobility/Street.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ namespace dsf::mobility {
/// @param pAgent The agent to add to the street
/// @param currentTime The current simulation time
void addAgent(std::unique_ptr<Agent> pAgent, std::time_t const currentTime);
/// @brief Remove the top agent from the street's moving agents priority queue
/// @return std::unique_ptr<Agent> The agent removed from the street's moving agents priority queue
std::unique_ptr<Agent> dequeueMovingAgent();
/// @brief Add an agent to the street's queue
/// @param queueId The id of the queue
/// @throw std::runtime_error If the street's queue is full
Expand Down
16 changes: 16 additions & 0 deletions src/dsf/utility/queue.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ namespace dsf {

T& operator[](size_t i) { return *(this->c.begin() + i); }
const T& operator[](size_t i) const { return *(this->c.begin() + i); }

/// @brief Extract the front element from the queue and remove it from the queue
/// @return T The front element of the queue
inline auto extract_front() {
auto front{std::move(const_cast<T&>(this->front()))};
this->pop();
return front;
}
};

template <typename T,
Expand All @@ -48,6 +56,14 @@ namespace dsf {

T& operator[](size_t i) { return *(this->c.begin() + i); }
const T& operator[](size_t i) const { return *(this->c.begin() + i); }

/// @brief Extract the top element from the priority queue and remove it from the queue
/// @return T The top element of the priority queue
inline auto extract_top() {
auto top{std::move(const_cast<T&>(this->top()))};
this->pop();
return top;
}
};

}; // namespace dsf
29 changes: 29 additions & 0 deletions test/mobility/Test_dynamics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,35 @@ TEST_CASE("FirstOrderDynamics") {
}
// spdlog::set_level(spdlog::level::info);
}
GIVEN("A non-random agent at a dead-end node with no valid next street") {
Street s0{0, std::make_pair(0, 1), 13.8888888889};

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
RoadNetwork graph2;
graph2.addStreets(s0);
FirstOrderDynamics dynamics{graph2, false, 69, 0., dsf::PathWeight::LENGTH};
// Manually construct an itinerary whose path leads through node 1, but
// node 1 has no outgoing edges. m_nextStreetId will return nullopt and
// the agent must be killed instead of throwing an exception.
auto itinerary = std::make_shared<Itinerary>(0, 2);
PathCollection path{{0, {1}}, {1, {2}}};

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note test

MISRA 12.3 rule
itinerary->setPath(path);
dynamics.addItinerary(itinerary);
dynamics.addAgent(dynamics.itineraries().at(0), 0);
WHEN("We evolve the dynamics until the agent reaches the dead-end") {
dynamics.evolve(false); // Agent enters intersection at node 0
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);
}
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);
}
}
}
}
SUBCASE("TrafficLights") {
TrafficLight::setAllowFreeTurns(false);
Expand Down