Skip to content

Commit 418c6ba

Browse files
authored
Add utility to kill stagnant agents (#341)
* Implement `killStagnantAgents` * Agent control in add function * Bump version
1 parent 4d47642 commit 418c6ba

File tree

3 files changed

+61
-51
lines changed

3 files changed

+61
-51
lines changed

src/dsf/bindings.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,11 @@ PYBIND11_MODULE(dsf_cpp, m) {
414414
&dsf::mobility::FirstOrderDynamics::setWeightFunction,
415415
pybind11::arg("weightFunction"),
416416
pybind11::arg("weightThreshold") = std::nullopt)
417+
.def(
418+
"killStagnantAgents",
419+
&dsf::mobility::FirstOrderDynamics::killStagnantAgents,
420+
pybind11::arg("timeToleranceFactor") = 3.,
421+
dsf::g_docstrings.at("dsf::mobility::RoadDynamics::killStagnantAgents").c_str())
417422
.def(
418423
"setDestinationNodes",
419424
[](dsf::mobility::FirstOrderDynamics& self,

src/dsf/dsf.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
static constexpr uint8_t DSF_VERSION_MAJOR = 4;
88
static constexpr uint8_t DSF_VERSION_MINOR = 4;
9-
static constexpr uint8_t DSF_VERSION_PATCH = 1;
9+
static constexpr uint8_t DSF_VERSION_PATCH = 2;
1010

1111
static auto const DSF_VERSION =
1212
std::format("{}.{}.{}", DSF_VERSION_MAJOR, DSF_VERSION_MINOR, DSF_VERSION_PATCH);

src/dsf/mobility/RoadDynamics.hpp

Lines changed: 55 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ namespace dsf::mobility {
6565
double m_maxTravelDistance;
6666
std::time_t m_maxTravelTime;
6767
double m_weightTreshold;
68+
std::optional<double> m_timeToleranceFactor;
6869
std::optional<delay_t> m_dataUpdatePeriod;
6970
bool m_bCacheEnabled;
7071
bool m_forcePriorities;
@@ -125,9 +126,16 @@ namespace dsf::mobility {
125126
/// @details The passage probability is the probability of passing through a node
126127
/// It is useful in the case of random agents
127128
void setPassageProbability(double passageProbability);
128-
129+
/// @brief Set the time tolerance factor for killing stagnant agents.
130+
/// An agent will be considered stagnant if it has not moved for timeToleranceFactor * std::ceil(street_length / street_maxSpeed) time units.
131+
/// @param timeToleranceFactor The time tolerance factor
132+
/// @throw std::invalid_argument If the time tolerance factor is not positive
133+
void killStagnantAgents(double timeToleranceFactor = 3.);
134+
/// @brief Set the weight function
135+
/// @param pathWeight The dsf::PathWeight function to use for the pathfinding
136+
/// @param weightThreshold The weight threshold for updating the paths (default is std::nullopt)
129137
void setWeightFunction(PathWeight const pathWeight,
130-
std::optional<double> weigthThreshold = std::nullopt);
138+
std::optional<double> weightThreshold = std::nullopt);
131139
/// @brief Set the force priorities flag
132140
/// @param forcePriorities The flag
133141
/// @details If true, if an agent cannot move to the next street, the whole node is skipped
@@ -376,6 +384,7 @@ namespace dsf::mobility {
376384
m_passageProbability{std::nullopt},
377385
m_maxTravelDistance{std::numeric_limits<double>::max()},
378386
m_maxTravelTime{std::numeric_limits<std::time_t>::max()},
387+
m_timeToleranceFactor{std::nullopt},
379388
m_bCacheEnabled{useCache},
380389
m_forcePriorities{false} {
381390
this->setWeightFunction(weightFunction, weightTreshold);
@@ -652,37 +661,31 @@ namespace dsf::mobility {
652661
pAgentTemp->freeTime());
653662
continue;
654663
}
655-
bool overtimed{false};
656-
{
664+
665+
if (m_timeToleranceFactor.has_value()) {
657666
auto const timeDiff{this->time_step() - pAgentTemp->freeTime()};
658-
// A minute of delay has never hurt anyone, right?
659-
auto const timeTolerance{
660-
std::max(static_cast<std::time_t>(60),
661-
static_cast<std::time_t>(
662-
3 * std::ceil(pStreet->length() / pStreet->maxSpeed())))};
667+
auto const timeTolerance{m_timeToleranceFactor.value() *
668+
std::ceil(pStreet->length() / pStreet->maxSpeed())};
663669
if (timeDiff > timeTolerance) {
664-
overtimed = true;
665670
spdlog::warn(
666-
"Time {} - {} currently on {} ({} turn - Traffic Light? {}), "
667-
"has been still for more than {} seconds ({} seconds)",
671+
"Time-step {} - {} currently on {} ({} turn - Traffic Light? {}), "
672+
"has been still for more than {} seconds ({} seconds). Killing it.",
668673
this->time_step(),
669674
*pAgentTemp,
670675
*pStreet,
671676
directionToString.at(pStreet->laneMapping().at(queueIndex)),
672677
this->graph().node(pStreet->target())->isTrafficLight(),
673678
timeTolerance,
674679
timeDiff);
680+
// Kill the agent
681+
auto pAgent{pStreet->dequeue(queueIndex)};
682+
continue;
675683
}
676684
}
677685
pAgentTemp->setSpeed(0.);
678686
const auto& destinationNode{this->graph().node(pStreet->target())};
679687
if (destinationNode->isFull()) {
680-
if (overtimed) {
681-
spdlog::warn("Skipping due to full destination node {}", *destinationNode);
682-
} else {
683-
spdlog::debug("Skipping due to space at destination node {}",
684-
*destinationNode);
685-
}
688+
spdlog::debug("Skipping due to full destination node {}", *destinationNode);
686689
continue;
687690
}
688691
if (destinationNode->isTrafficLight()) {
@@ -792,17 +795,9 @@ namespace dsf::mobility {
792795
}
793796
if (!bCanPass) {
794797
spdlog::debug(
795-
"Skipping agent emission from street {} -> {} due to right of way.",
798+
"Skipping agent emission from street {} -> {} due to right of way",
796799
pStreet->source(),
797800
pStreet->target());
798-
if (overtimed) {
799-
spdlog::warn(
800-
"Skipping agent emission from street {} -> {} due to right of way "
801-
"and overtimed agent {}",
802-
pStreet->source(),
803-
pStreet->target(),
804-
pAgentTemp->id());
805-
}
806801
continue;
807802
}
808803
}
@@ -817,14 +812,6 @@ namespace dsf::mobility {
817812
"probability",
818813
pStreet->source(),
819814
pStreet->target());
820-
if (overtimed) {
821-
spdlog::warn(
822-
"Skipping agent emission from street {} -> {} due to passage "
823-
"probability and overtimed agent {}",
824-
pStreet->source(),
825-
pStreet->target(),
826-
pAgentTemp->id());
827-
}
828815
continue;
829816
}
830817
}
@@ -865,21 +852,12 @@ namespace dsf::mobility {
865852
}
866853
auto const& nextStreet{this->graph().edge(pAgentTemp->nextStreetId().value())};
867854
if (nextStreet->isFull()) {
868-
if (overtimed) {
869-
spdlog::warn(
870-
"Skipping agent emission from street {} -> {} due to full "
871-
"next street: {}",
872-
pStreet->source(),
873-
pStreet->target(),
874-
*nextStreet);
875-
} else {
876-
spdlog::debug(
877-
"Skipping agent emission from street {} -> {} due to full "
878-
"next street: {}",
879-
pStreet->source(),
880-
pStreet->target(),
881-
*nextStreet);
882-
}
855+
spdlog::debug(
856+
"Skipping agent emission from street {} -> {} due to full "
857+
"next street: {}",
858+
pStreet->source(),
859+
pStreet->target(),
860+
*nextStreet);
883861
continue;
884862
}
885863
auto pAgent{pStreet->dequeue(queueIndex)};
@@ -1042,6 +1020,15 @@ namespace dsf::mobility {
10421020
}
10431021
m_passageProbability = passageProbability;
10441022
}
1023+
template <typename delay_t>
1024+
requires(is_numeric_v<delay_t>)
1025+
void RoadDynamics<delay_t>::killStagnantAgents(double timeToleranceFactor) {
1026+
if (timeToleranceFactor <= 0.) {
1027+
throw std::invalid_argument(std::format(
1028+
"The time tolerance factor ({}) must be positive", timeToleranceFactor));
1029+
}
1030+
m_timeToleranceFactor = timeToleranceFactor;
1031+
}
10451032
template <typename delay_t>
10461033
requires(is_numeric_v<delay_t>)
10471034
void RoadDynamics<delay_t>::setWeightFunction(PathWeight const pathWeight,
@@ -1200,6 +1187,15 @@ namespace dsf::mobility {
12001187
requires(is_numeric_v<delay_t>)
12011188
void RoadDynamics<delay_t>::addAgentsUniformly(Size nAgents,
12021189
std::optional<Id> optItineraryId) {
1190+
if (m_timeToleranceFactor.has_value() && !m_agents.empty()) {
1191+
auto const nStagnantAgents{m_agents.size()};
1192+
spdlog::warn(
1193+
"Removing {} stagnant agents that were not inserted since the previous call to "
1194+
"addAgentsUniformly().",
1195+
nStagnantAgents);
1196+
m_agents.clear();
1197+
m_nAgents -= nStagnantAgents;
1198+
}
12031199
if (optItineraryId.has_value() && !this->itineraries().contains(*optItineraryId)) {
12041200
throw std::invalid_argument(
12051201
std::format("No itineraries available. Cannot add agents with itinerary id {}",
@@ -1256,6 +1252,15 @@ namespace dsf::mobility {
12561252
void RoadDynamics<delay_t>::addAgentsRandomly(Size nAgents,
12571253
const TContainer& src_weights,
12581254
const TContainer& dst_weights) {
1255+
if (m_timeToleranceFactor.has_value() && !m_agents.empty()) {
1256+
auto const nStagnantAgents{m_agents.size()};
1257+
spdlog::warn(
1258+
"Removing {} stagnant agents that were not inserted since the previous call to "
1259+
"addAgentsRandomly().",
1260+
nStagnantAgents);
1261+
m_agents.clear();
1262+
m_nAgents -= nStagnantAgents;
1263+
}
12591264
auto const& nSources{src_weights.size()};
12601265
auto const& nDestinations{dst_weights.size()};
12611266
spdlog::debug("Init addAgentsRandomly for {} agents from {} nodes to {} nodes.",

0 commit comments

Comments
 (0)