Skip to content

Commit 292021f

Browse files
authored
Merge pull request #374 from physycom/pathThrows
Add `throw_on_empty` param to updatePaths
2 parents 7eca02b + 87e2ce0 commit 292021f

File tree

6 files changed

+64
-19
lines changed

6 files changed

+64
-19
lines changed

src/dsf/bindings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ PYBIND11_MODULE(dsf_cpp, m) {
475475
dsf::g_docstrings.at("dsf::mobility::RoadDynamics::initTurnCounts").c_str())
476476
.def("updatePaths",
477477
&dsf::mobility::FirstOrderDynamics::updatePaths,
478+
pybind11::arg("throw_on_empty") = true,
478479
dsf::g_docstrings.at("dsf::mobility::RoadDynamics::updatePaths").c_str())
479480
.def(
480481
"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 = 8;
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: 33 additions & 10 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) {
@@ -1250,12 +1246,39 @@ namespace dsf::mobility {
12501246

12511247
template <typename delay_t>
12521248
requires(is_numeric_v<delay_t>)
1253-
void RoadDynamics<delay_t>::updatePaths() {
1249+
void RoadDynamics<delay_t>::updatePaths(bool const throw_on_empty) {
12541250
spdlog::debug("Init updating paths...");
1251+
tbb::concurrent_vector<Id> emptyItineraries;
12551252
tbb::parallel_for_each(
12561253
this->itineraries().cbegin(),
12571254
this->itineraries().cend(),
1258-
[this](auto const& pair) -> void { this->m_updatePath(pair.second); });
1255+
[this, throw_on_empty, &emptyItineraries](auto const& pair) -> void {
1256+
auto const& pItinerary{pair.second};
1257+
this->m_updatePath(pItinerary);
1258+
if (pItinerary->empty()) {
1259+
if (!throw_on_empty) {
1260+
spdlog::warn("No path found for itinerary {} with destination node {}",
1261+
pItinerary->id(),
1262+
pItinerary->destination());
1263+
emptyItineraries.push_back(pItinerary->id());
1264+
return;
1265+
}
1266+
throw std::runtime_error(
1267+
std::format("No path found for itinerary {} with destination node {}",
1268+
pItinerary->id(),
1269+
pItinerary->destination()));
1270+
}
1271+
});
1272+
if (!emptyItineraries.empty()) {
1273+
spdlog::warn("Removing {} itineraries with no valid path from the dynamics.",
1274+
emptyItineraries.size());
1275+
for (auto const& id : emptyItineraries) {
1276+
auto const destination = m_itineraries.at(id)->destination();
1277+
m_destinationNodes.erase(destination);
1278+
m_originNodes.erase(destination);
1279+
m_itineraries.erase(id);
1280+
}
1281+
}
12591282
spdlog::debug("End updating paths.");
12601283
}
12611284

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)