Skip to content

Commit 14342c7

Browse files
committed
Merge branch 'main' into useTP
2 parents 2126982 + 292021f commit 14342c7

File tree

10 files changed

+787
-84
lines changed

10 files changed

+787
-84
lines changed

src/dsf/bindings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ PYBIND11_MODULE(dsf_cpp, m) {
480480
dsf::g_docstrings.at("dsf::mobility::RoadDynamics::initTurnCounts").c_str())
481481
.def("updatePaths",
482482
&dsf::mobility::FirstOrderDynamics::updatePaths,
483+
pybind11::arg("throw_on_empty") = true,
483484
dsf::g_docstrings.at("dsf::mobility::RoadDynamics::updatePaths").c_str())
484485
.def(
485486
"addAgentsUniformly",

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 = 6;
9+
static constexpr uint8_t DSF_VERSION_PATCH = 9;
1010

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

src/dsf/mobility/Itinerary.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,6 @@ namespace dsf::mobility {
3636
m_path = std::move(pathCollection);
3737
}
3838

39-
Id Itinerary::id() const { return m_id; }
40-
Id Itinerary::destination() const { return m_destination; }
41-
PathCollection const& Itinerary::path() const { return m_path; }
4239
void Itinerary::save(const std::string& fileName) const {
4340
// Open binary file
4441
std::ofstream outFile{fileName, std::ios::binary};

src/dsf/mobility/Itinerary.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,16 @@ namespace dsf::mobility {
4848

4949
/// @brief Get the itinerary's id
5050
/// @return Id, The itinerary's id
51-
Id id() const;
51+
inline auto id() const noexcept { return m_id; };
5252
/// @brief Get the itinerary's destination
5353
/// @return Id, The itinerary's destination
54-
Id destination() const;
54+
inline auto destination() const noexcept { return m_destination; };
5555
/// @brief Get the itinerary's path
5656
/// @return PathCollection const&, The itinerary's path
57-
PathCollection const& path() const;
57+
inline auto const& path() const noexcept { return m_path; };
58+
/// @brief Check if the itinerary's path is empty
59+
/// @return true if the itinerary's path is empty, false otherwise
60+
inline auto empty() const noexcept { return m_path.empty(); };
5861
/// @brief Save the itinerary to a binary file
5962
/// @param fileName The name of the file to save the itinerary to
6063
void save(const std::string& fileName) const;

src/dsf/mobility/RoadDynamics.hpp

Lines changed: 58 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,10 @@ namespace dsf::mobility {
191191
void resetTurnCounts();
192192

193193
/// @brief Update the paths of the itineraries based on the given weight function
194-
void updatePaths();
194+
/// @param throw_on_empty If true, throws an exception if an itinerary has an empty path (default is true)
195+
/// If false, removes the itinerary with empty paths and the associated node from the origin/destination nodes
196+
/// @throws std::runtime_error if throw_on_empty is true and an itinerary has an empty path
197+
void updatePaths(bool const throw_on_empty = true);
195198
/// @brief Add agents uniformly on the road network
196199
/// @param nAgents The number of agents to add
197200
/// @param itineraryId The id of the itinerary to use (default is std::nullopt)
@@ -413,7 +416,6 @@ namespace dsf::mobility {
413416
for (auto const& [nodeId, weight] : this->m_destinationNodes) {
414417
m_itineraries.emplace(nodeId, std::make_unique<Itinerary>(nodeId, nodeId));
415418
}
416-
// updatePaths();
417419
std::for_each(
418420
this->graph().edges().cbegin(),
419421
this->graph().edges().cend(),
@@ -470,12 +472,6 @@ namespace dsf::mobility {
470472
471473
auto const& path{this->graph().allPathsTo(
472474
pItinerary->destination(), m_weightFunction, m_weightTreshold)};
473-
if (path.empty()) {
474-
throw std::runtime_error(
475-
std::format("No path found for itinerary {} with destination node {}",
476-
pItinerary->id(),
477-
pItinerary->destination()));
478-
}
479475
pItinerary->setPath(path);
480476
auto const newSize{pItinerary->path().size()};
481477
if (oldSize > 0 && newSize != oldSize) {
@@ -534,18 +530,21 @@ namespace dsf::mobility {
534530
double cumulativeProbability = 0.0;
535531
536532
for (const auto outEdgeId : outgoingEdges) {
537-
if (forbiddenTurns.contains(outEdgeId)) {
538-
continue;
539-
}
540-
541533
auto const& pStreetOut{this->graph().edge(outEdgeId)};
542534
543535
// Check if this is a valid path target for non-random agents
544-
bool bIsPathTarget = true;
536+
bool bIsPathTarget = false;
545537
if (!pathTargets.empty()) {
546538
bIsPathTarget =
547539
std::find(pathTargets.cbegin(), pathTargets.cend(), pStreetOut->target()) !=
548540
pathTargets.cend();
541+
}
542+
543+
if (forbiddenTurns.contains(outEdgeId) && !bIsPathTarget) {
544+
continue;
545+
}
546+
547+
if (!pathTargets.empty()) {
549548
if (!this->m_errorProbability.has_value() && !bIsPathTarget) {
550549
continue;
551550
}
@@ -571,7 +570,7 @@ namespace dsf::mobility {
571570
if (previousNodeId.has_value() && pStreetOut->target() == previousNodeId.value()) {
572571
if (pNode->isRoundabout()) {
573572
probability *= U_TURN_PENALTY_FACTOR;
574-
} else {
573+
} else if (!bIsPathTarget) {
575574
continue; // No U-turns allowed
576575
}
577576
}
@@ -582,11 +581,16 @@ namespace dsf::mobility {
582581
583582
// Select street based on weighted probabilities
584583
if (transitionProbabilities.empty()) {
585-
spdlog::trace("No valid transitions found for {} at {}", *pAgent, *pNode);
584+
spdlog::debug("No valid transitions found for {} at {}", *pAgent, *pNode);
586585
return std::nullopt;
587586
}
588587
if (transitionProbabilities.size() == 1) {
589-
return transitionProbabilities.cbegin()->first;
588+
auto const& onlyStreetId = transitionProbabilities.cbegin()->first;
589+
spdlog::debug("Only one valid transition for {} at {}: street {}",
590+
*pAgent,
591+
*pNode,
592+
onlyStreetId);
593+
return onlyStreetId;
590594
}
591595
592596
std::uniform_real_distribution<double> uniformDist{0., cumulativeProbability};
@@ -599,7 +603,10 @@ namespace dsf::mobility {
599603
}
600604
}
601605
// Return last one as fallback
602-
return std::prev(transitionProbabilities.cend())->first;
606+
auto const fallbackStreetId = std::prev(transitionProbabilities.cend())->first;
607+
spdlog::debug(
608+
"Fallback selection for {} at {}: street {}", *pAgent, *pNode, fallbackStreetId);
609+
return fallbackStreetId;
603610
}
604611
605612
template <typename delay_t>
@@ -690,15 +697,15 @@ namespace dsf::mobility {
690697
if (pStreet->isStochastic() &&
691698
uniformDist(this->m_generator) >
692699
dynamic_cast<StochasticStreet&>(*pStreet).flowRate()) {
693-
spdlog::debug("Skipping due to flow rate {:.2f} < random value",
700+
spdlog::trace("Skipping due to flow rate {:.2f} < random value",
694701
dynamic_cast<StochasticStreet&>(*pStreet).flowRate());
695702
continue;
696703
}
697704
if (i == std::ceil(transportCapacity) - 1) {
698705
double integral;
699706
double fractional = std::modf(transportCapacity, &integral);
700707
if (fractional != 0. && uniformDist(this->m_generator) > fractional) {
701-
spdlog::debug("Skipping due to fractional capacity {:.2f} < random value",
708+
spdlog::trace("Skipping due to fractional capacity {:.2f} < random value",
702709
fractional);
703710
continue;
704711
}
@@ -710,7 +717,7 @@ namespace dsf::mobility {
710717
// Logger::debug("Taking temp agent");
711718
auto const& pAgentTemp{pStreet->queue(queueIndex).front()};
712719
if (pAgentTemp->freeTime() > this->time_step()) {
713-
spdlog::debug("Skipping due to time {} < free time {}",
720+
spdlog::trace("Skipping due to time {} < free time {}",
714721
this->time_step(),
715722
pAgentTemp->freeTime());
716723
continue;
@@ -739,14 +746,14 @@ namespace dsf::mobility {
739746
pAgentTemp->setSpeed(0.);
740747
const auto& destinationNode{this->graph().node(pStreet->target())};
741748
if (destinationNode->isFull()) {
742-
spdlog::debug("Skipping due to full destination node {}", *destinationNode);
749+
spdlog::trace("Skipping due to full destination node {}", *destinationNode);
743750
continue;
744751
}
745752
if (destinationNode->isTrafficLight()) {
746753
auto& tl = dynamic_cast<TrafficLight&>(*destinationNode);
747754
auto const direction{pStreet->laneMapping().at(queueIndex)};
748755
if (!tl.isGreen(pStreet->id(), direction)) {
749-
spdlog::debug("Skipping due to red light on street {} and direction {}",
756+
spdlog::trace("Skipping due to red light on street {} and direction {}",
750757
pStreet->id(),
751758
directionToString.at(direction));
752759
continue;
@@ -1244,12 +1251,39 @@ namespace dsf::mobility {
12441251

12451252
template <typename delay_t>
12461253
requires(is_numeric_v<delay_t>)
1247-
void RoadDynamics<delay_t>::updatePaths() {
1254+
void RoadDynamics<delay_t>::updatePaths(bool const throw_on_empty) {
12481255
spdlog::debug("Init updating paths...");
1256+
tbb::concurrent_vector<Id> emptyItineraries;
12491257
tbb::parallel_for_each(
12501258
this->itineraries().cbegin(),
12511259
this->itineraries().cend(),
1252-
[this](auto const& pair) -> void { this->m_updatePath(pair.second); });
1260+
[this, throw_on_empty, &emptyItineraries](auto const& pair) -> void {
1261+
auto const& pItinerary{pair.second};
1262+
this->m_updatePath(pItinerary);
1263+
if (pItinerary->empty()) {
1264+
if (!throw_on_empty) {
1265+
spdlog::warn("No path found for itinerary {} with destination node {}",
1266+
pItinerary->id(),
1267+
pItinerary->destination());
1268+
emptyItineraries.push_back(pItinerary->id());
1269+
return;
1270+
}
1271+
throw std::runtime_error(
1272+
std::format("No path found for itinerary {} with destination node {}",
1273+
pItinerary->id(),
1274+
pItinerary->destination()));
1275+
}
1276+
});
1277+
if (!emptyItineraries.empty()) {
1278+
spdlog::warn("Removing {} itineraries with no valid path from the dynamics.",
1279+
emptyItineraries.size());
1280+
for (auto const& id : emptyItineraries) {
1281+
auto const destination = m_itineraries.at(id)->destination();
1282+
m_destinationNodes.erase(destination);
1283+
m_originNodes.erase(destination);
1284+
m_itineraries.erase(id);
1285+
}
1286+
}
12531287
spdlog::debug("End updating paths.");
12541288
}
12551289

src/dsf/mobility/RoadNetwork.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,14 +247,16 @@ namespace dsf::mobility {
247247
name,
248248
geometry));
249249
// Check if there is coilcode property
250-
if (!edge_properties.at_key("coilcode").error() &&
251-
edge_properties["coilcode"].has_value()) {
252-
if (edge_properties["coilcode"].is_string()) {
253-
std::string strCoilCode{edge_properties["coilcode"].get_string().value()};
250+
if (!edge_properties.at_key("coilcode").error()) {
251+
auto const& epCoilCode = edge_properties["coilcode"];
252+
if (epCoilCode.is_string()) {
253+
std::string strCoilCode{epCoilCode.get_string().value()};
254254
addCoil(edge_id, strCoilCode);
255-
} else if (edge_properties["coilcode"].is_number()) {
256-
std::string strCoilCode =
257-
std::to_string(edge_properties["coilcode"].get_uint64());
255+
} else if (epCoilCode.is_uint64()) {
256+
std::string strCoilCode = std::to_string(epCoilCode.get_uint64());
257+
addCoil(edge_id, strCoilCode);
258+
} else if (epCoilCode.is_int64()) {
259+
std::string strCoilCode = std::to_string(epCoilCode.get_int64());
258260
addCoil(edge_id, strCoilCode);
259261
} else {
260262
spdlog::warn("Invalid coilcode for edge {}, adding default", edge_id);

test/mobility/Test_dynamics.cpp

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,27 @@ TEST_CASE("FirstOrderDynamics") {
425425
}
426426
}
427427
}
428+
GIVEN("A disconnected graph") {
429+
Street s1{0, std::make_pair(0, 1), 10.};
430+
RoadNetwork graph;
431+
graph.addStreets(s1);
432+
FirstOrderDynamics dynamics{graph, false, 69, 0., dsf::PathWeight::LENGTH};
433+
434+
WHEN(
435+
"We add an impossible itinerary (to source node) and update paths with "
436+
"throw_on_empty=true") {
437+
dynamics.addItinerary(std::unique_ptr<Itinerary>(new Itinerary(0, 0)));
438+
THEN("It throws an exception") { CHECK_THROWS(dynamics.updatePaths(true)); }
439+
}
440+
441+
WHEN(
442+
"We add an impossible itinerary (to source node) and update paths with "
443+
"throw_on_empty=false") {
444+
dynamics.addItinerary(std::unique_ptr<Itinerary>(new Itinerary(0, 0)));
445+
dynamics.updatePaths(false);
446+
THEN("The itinerary is removed") { CHECK(dynamics.itineraries().empty()); }
447+
}
448+
}
428449
}
429450
SUBCASE("Evolve") {
430451
GIVEN("A dynamics object and an itinerary") {
@@ -549,7 +570,7 @@ TEST_CASE("FirstOrderDynamics") {
549570
}
550571
}
551572
GIVEN("A simple network and an agent with forced itinerary") {
552-
spdlog::set_level(spdlog::level::trace);
573+
// spdlog::set_level(spdlog::level::trace);
553574
Street s0_1{1, std::make_pair(0, 1), 30., 15.};
554575
Street s1_0{3, std::make_pair(1, 0), 30., 15.};
555576
Street s1_2{5, std::make_pair(1, 2), 30., 15.};
@@ -586,7 +607,7 @@ TEST_CASE("FirstOrderDynamics") {
586607
CHECK_EQ(dynamics.nAgents(), 0);
587608
}
588609
}
589-
spdlog::set_level(spdlog::level::info);
610+
// spdlog::set_level(spdlog::level::info);
590611
}
591612
}
592613
SUBCASE("TrafficLights") {

0 commit comments

Comments
 (0)