From fc56305a46f98208694c96b367683bc7d628453d Mon Sep 17 00:00:00 2001 From: grufoony Date: Mon, 26 Jan 2026 12:24:26 +0100 Subject: [PATCH 1/8] Add `RoadStatus` --- src/dsf/bindings.cpp | 18 +++ src/dsf/mobility/Road.hpp | 13 +- src/dsf/mobility/RoadNetwork.cpp | 21 +++ src/dsf/mobility/RoadNetwork.hpp | 18 +++ test/mobility/Test_graph.cpp | 237 +++++++++++++++++++++++++++++++ 5 files changed, 306 insertions(+), 1 deletion(-) diff --git a/src/dsf/bindings.cpp b/src/dsf/bindings.cpp index 0a636e5b..b52338d8 100644 --- a/src/dsf/bindings.cpp +++ b/src/dsf/bindings.cpp @@ -34,6 +34,12 @@ PYBIND11_MODULE(dsf_cpp, m) { .value("DOUBLE_TAIL", dsf::TrafficLightOptimization::DOUBLE_TAIL) .export_values(); + // Bind RoadStatus enum + pybind11::enum_(mobility, "RoadStatus") + .value("OPEN", dsf::mobility::RoadStatus::OPEN) + .value("CLOSED", dsf::mobility::RoadStatus::CLOSED) + .export_values(); + // Bind spdlog log level enum pybind11::enum_(m, "LogLevel") .value("TRACE", spdlog::level::trace) @@ -243,6 +249,18 @@ PYBIND11_MODULE(dsf_cpp, m) { pybind11::arg("cycleTime"), pybind11::arg("counter"), dsf::g_docstrings.at("dsf::mobility::RoadNetwork::makeTrafficLight").c_str()) + .def( + "setStreetStatusById", + &dsf::mobility::RoadNetwork::setStreetStatusById, + pybind11::arg("streetId"), + pybind11::arg("status"), + dsf::g_docstrings.at("dsf::mobility::RoadNetwork::setStreetStatusById").c_str()) + .def("setStreetStatusByName", + &dsf::mobility::RoadNetwork::setStreetStatusByName, + pybind11::arg("name"), + pybind11::arg("status"), + dsf::g_docstrings.at("dsf::mobility::RoadNetwork::setStreetStatusByName") + .c_str()) .def("addCoil", &dsf::mobility::RoadNetwork::addCoil, pybind11::arg("streetId"), diff --git a/src/dsf/mobility/Road.hpp b/src/dsf/mobility/Road.hpp index 4c63e98f..fe19877d 100644 --- a/src/dsf/mobility/Road.hpp +++ b/src/dsf/mobility/Road.hpp @@ -15,6 +15,10 @@ namespace dsf::mobility { TERTIARY = 3, RESIDENTIAL = 4, }; + enum class RoadStatus : std::uint8_t { + OPEN = 0, + CLOSED = 1, + }; /// @brief The Road class represents a road in the network. class Road : public Edge { @@ -29,6 +33,7 @@ namespace dsf::mobility { bool m_hasPriority = false; std::set m_forbiddenTurns; // Stores the forbidden turns (road ids) std::optional m_roadType{std::nullopt}; + RoadStatus m_roadStatus = RoadStatus::OPEN; public: /// @brief Construct a new Road object @@ -80,7 +85,10 @@ namespace dsf::mobility { void setForbiddenTurns(std::set const& forbiddenTurns); /// @brief Set the road type /// @param roadType The road type - inline void setRoadType(RoadType roadType) { m_roadType = roadType; } + inline void setRoadType(RoadType const roadType) { m_roadType = roadType; } + /// @brief Set the road status + /// @param status The road status + inline void setStatus(RoadStatus const status) { m_roadStatus = status; } /// @brief Get the length, in meters /// @return double The length, in meters @@ -111,6 +119,9 @@ namespace dsf::mobility { /// @brief Get the road type /// @return std::optional The road type inline auto roadType() const noexcept { return m_roadType; } + /// @brief Get the road status + /// @return RoadStatus The road status + inline auto roadStatus() const noexcept { return m_roadStatus; } /// @brief Get the road's turn direction given the previous road angle /// @param previousStreetAngle The angle of the previous road /// @return Direction The turn direction diff --git a/src/dsf/mobility/RoadNetwork.cpp b/src/dsf/mobility/RoadNetwork.cpp index 213933d7..e76e4ed8 100644 --- a/src/dsf/mobility/RoadNetwork.cpp +++ b/src/dsf/mobility/RoadNetwork.cpp @@ -934,6 +934,27 @@ namespace dsf::mobility { addEdge(std::move(street)); } + void RoadNetwork::setStreetStatusById(Id const streetId, RoadStatus const status) { + edge(streetId)->setStatus(status); + } + void RoadNetwork::setStreetStatusByName(std::string const& streetName, + RoadStatus const status) { + std::atomic nAffectedRoads{0}; + std::for_each(DSF_EXECUTION m_edges.cbegin(), + m_edges.cend(), + [this, &streetName, &status, &nAffectedRoads](auto const& pair) { + auto const& pStreet = pair.second; + if (pStreet->name().find(streetName) != std::string::npos) { + pStreet->setStatus(status); + ++nAffectedRoads; + } + }); + spdlog::info("Set status {} to {} streets with name containing \"{}\"", + status, + nAffectedRoads.load(), + streetName); + } + void RoadNetwork::setStreetStationaryWeights( std::unordered_map const& weights) { std::for_each(DSF_EXECUTION m_edges.cbegin(), diff --git a/src/dsf/mobility/RoadNetwork.hpp b/src/dsf/mobility/RoadNetwork.hpp index 42fb845d..c37ab917 100644 --- a/src/dsf/mobility/RoadNetwork.hpp +++ b/src/dsf/mobility/RoadNetwork.hpp @@ -198,6 +198,16 @@ namespace dsf::mobility { requires is_street_v> && (is_street_v> && ...) void addStreets(T1&& street, Tn&&... streets); + + /// @brief Set the street's status by its id + /// @param streetId The id of the street + /// @param status The status to set + void setStreetStatusById(Id const streetId, RoadStatus const status); + /// @brief Set the street's status of all streets with the given name + /// @param name The name of the street + /// @param status The status to set + void setStreetStatusByName(std::string const& name, RoadStatus const status); + /// @brief Set the streets' stationary weights /// @param streetWeights A map where the key is the street id and the value is the street stationary weight. If a street id is not present in the map, its stationary weight is set to 1.0. void setStreetStationaryWeights(std::unordered_map const& streetWeights); @@ -366,6 +376,10 @@ namespace dsf::mobility { // Explore all incoming edges (nodes that can reach currentNode) auto const& inEdges = node(currentNode)->ingoingEdges(); for (auto const& inEdgeId : inEdges) { + // Skip closed roads + if (edge(inEdgeId)->roadStatus() == RoadStatus::CLOSED) { + continue; + } Id neighborId = edge(inEdgeId)->source(); // Calculate the weight of the edge from neighbor to currentNode using the dynamics function @@ -471,6 +485,10 @@ namespace dsf::mobility { // Explore all incoming edges (nodes that can reach currentNode) auto const& inEdges = node(currentNode)->ingoingEdges(); for (auto const& inEdgeId : inEdges) { + // Skip closed roads + if (edge(inEdgeId)->roadStatus() == RoadStatus::CLOSED) { + continue; + } Id neighborId = edge(inEdgeId)->source(); // Calculate the weight of the edge from neighbor to currentNode using the dynamics function diff --git a/test/mobility/Test_graph.cpp b/test/mobility/Test_graph.cpp index 4127a16d..bff2e472 100644 --- a/test/mobility/Test_graph.cpp +++ b/test/mobility/Test_graph.cpp @@ -1311,3 +1311,240 @@ TEST_CASE("ShortestPath") { CHECK_EQ(path[0], 0); } } + +TEST_CASE("RoadStatus") { + SUBCASE("setStreetStatusById") { + RoadNetwork graph{}; + graph.addNode(0, dsf::geometry::Point(0.0, 0.0)); + graph.addNode(1, dsf::geometry::Point(1.0, 0.0)); + graph.addNode(2, dsf::geometry::Point(2.0, 0.0)); + + Street s01(0, std::make_pair(0, 1), 100.0); + Street s12(1, std::make_pair(1, 2), 100.0); + graph.addStreets(s01, s12); + + // Initially all streets are OPEN + CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::OPEN); + + // Close street by id + graph.setStreetStatusById(0, RoadStatus::CLOSED); + CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::CLOSED); + CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::OPEN); + + // Re-open street + graph.setStreetStatusById(0, RoadStatus::OPEN); + CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::OPEN); + } + + SUBCASE("setStreetStatusByName") { + RoadNetwork graph{}; + graph.addNode(0, dsf::geometry::Point(0.0, 0.0)); + graph.addNode(1, dsf::geometry::Point(1.0, 0.0)); + graph.addNode(2, dsf::geometry::Point(2.0, 0.0)); + graph.addNode(3, dsf::geometry::Point(3.0, 0.0)); + + Street s01(0, std::make_pair(0, 1), 100.0, 13.8888, 1, "Main Street"); + Street s12(1, std::make_pair(1, 2), 100.0, 13.8888, 1, "Main Street"); + Street s23(2, std::make_pair(2, 3), 100.0, 13.8888, 1, "Side Road"); + graph.addStreets(s01, s12, s23); + + // Initially all streets are OPEN + CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(2)->roadStatus(), RoadStatus::OPEN); + + // Close all streets with name "Main Street" + graph.setStreetStatusByName("Main Street", RoadStatus::CLOSED); + CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::CLOSED); + CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::CLOSED); + CHECK_EQ(graph.edge(2)->roadStatus(), RoadStatus::OPEN); + + // Re-open Main Street + graph.setStreetStatusByName("Main Street", RoadStatus::OPEN); + CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::OPEN); + CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::OPEN); + } + + SUBCASE("setStreetStatusByName - partial match") { + RoadNetwork graph{}; + graph.addNode(0, dsf::geometry::Point(0.0, 0.0)); + graph.addNode(1, dsf::geometry::Point(1.0, 0.0)); + graph.addNode(2, dsf::geometry::Point(2.0, 0.0)); + + Street s01(0, std::make_pair(0, 1), 100.0, 13.8888, 1, "Via Roma Nord"); + Street s12(1, std::make_pair(1, 2), 100.0, 13.8888, 1, "Via Roma Sud"); + graph.addStreets(s01, s12); + + // Close all streets containing "Roma" in the name + graph.setStreetStatusByName("Roma", RoadStatus::CLOSED); + CHECK_EQ(graph.edge(0)->roadStatus(), RoadStatus::CLOSED); + CHECK_EQ(graph.edge(1)->roadStatus(), RoadStatus::CLOSED); + } +} + +TEST_CASE("ShortestPath with closed roads") { + SUBCASE("Closed road forces alternative path") { + // Create a network: 0 -> 1 -> 2 + // | ^ + // +-> 3 ----+ + // Path 0->1->2 has length 200, path 0->3->2 has length 300 + // If we close 0->1, shortest path should go through 3 + RoadNetwork graph{}; + graph.addNode(0, dsf::geometry::Point(0.0, 0.0)); + graph.addNode(1, dsf::geometry::Point(1.0, 0.0)); + graph.addNode(2, dsf::geometry::Point(2.0, 0.0)); + graph.addNode(3, dsf::geometry::Point(1.0, 1.0)); + + Street s01(0, std::make_pair(0, 1), 100.0); + Street s12(1, std::make_pair(1, 2), 100.0); + Street s03(2, std::make_pair(0, 3), 150.0); + Street s32(3, std::make_pair(3, 2), 150.0); + graph.addStreets(s01, s12, s03, s32); + + // Initially, shortest path is 0 -> 1 -> 2 + auto pathMap = + graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + REQUIRE(pathMap.contains(0)); + CHECK_EQ(pathMap.at(0)[0], 1); + + // Close street 0->1 + graph.setStreetStatusById(0, RoadStatus::CLOSED); + + // Now shortest path should be 0 -> 3 -> 2 + pathMap = graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + REQUIRE(pathMap.contains(0)); + CHECK_EQ(pathMap.at(0).size(), 1); + CHECK_EQ(pathMap.at(0)[0], 3); + + REQUIRE(pathMap.contains(3)); + CHECK_EQ(pathMap.at(3).size(), 1); + CHECK_EQ(pathMap.at(3)[0], 2); + } + + SUBCASE("No path when all routes closed") { + // Create a linear network: 0 -> 1 -> 2 + RoadNetwork graph{}; + graph.addNode(0, dsf::geometry::Point(0.0, 0.0)); + graph.addNode(1, dsf::geometry::Point(1.0, 0.0)); + graph.addNode(2, dsf::geometry::Point(2.0, 0.0)); + + Street s01(0, std::make_pair(0, 1), 100.0); + Street s12(1, std::make_pair(1, 2), 100.0); + graph.addStreets(s01, s12); + + // Close the middle street + graph.setStreetStatusById(1, RoadStatus::CLOSED); + + // No path should exist from 0 to 2 + auto pathMap = + graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + CHECK(pathMap.empty()); + } + + SUBCASE("Reopening road restores path") { + RoadNetwork graph{}; + graph.addNode(0, dsf::geometry::Point(0.0, 0.0)); + graph.addNode(1, dsf::geometry::Point(1.0, 0.0)); + graph.addNode(2, dsf::geometry::Point(2.0, 0.0)); + + Street s01(0, std::make_pair(0, 1), 100.0); + Street s12(1, std::make_pair(1, 2), 100.0); + graph.addStreets(s01, s12); + + // Close and then reopen + graph.setStreetStatusById(0, RoadStatus::CLOSED); + auto pathMap = + graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + CHECK(pathMap.empty()); + + graph.setStreetStatusById(0, RoadStatus::OPEN); + pathMap = graph.shortestPath(0, 2, [](auto const& pEdge) { return pEdge->length(); }); + CHECK_FALSE(pathMap.empty()); + REQUIRE(pathMap.contains(0)); + CHECK_EQ(pathMap.at(0)[0], 1); + } +} + +TEST_CASE("allPathsTo with closed roads") { + SUBCASE("Closed road excluded from paths") { + // Create a network: 0 -> 1 -> 2 + // | ^ + // +-> 3 ----+ + RoadNetwork graph{}; + graph.addNode(0, dsf::geometry::Point(0.0, 0.0)); + graph.addNode(1, dsf::geometry::Point(1.0, 0.0)); + graph.addNode(2, dsf::geometry::Point(2.0, 0.0)); + graph.addNode(3, dsf::geometry::Point(1.0, 1.0)); + + Street s01(0, std::make_pair(0, 1), 100.0); + Street s12(1, std::make_pair(1, 2), 100.0); + Street s03(2, std::make_pair(0, 3), 150.0); + Street s32(3, std::make_pair(3, 2), 150.0); + graph.addStreets(s01, s12, s03, s32); + + // Close street 0->1 + graph.setStreetStatusById(0, RoadStatus::CLOSED); + + // allPathsTo should not include node 1 as next hop from 0 + auto pathMap = graph.allPathsTo(2, [](auto const& pEdge) { return pEdge->length(); }); + + REQUIRE(pathMap.contains(0)); + // Node 0 should only have node 3 as next hop (not 1, since 0->1 is closed) + CHECK_EQ(pathMap.at(0).size(), 1); + CHECK_EQ(pathMap.at(0)[0], 3); + + // Node 1 should still have a path to 2 via 1->2 (which is still open) + REQUIRE(pathMap.contains(1)); + CHECK_EQ(pathMap.at(1).size(), 1); + CHECK_EQ(pathMap.at(1)[0], 2); + } + + SUBCASE("All paths blocked to target") { + RoadNetwork graph{}; + graph.addNode(0, dsf::geometry::Point(0.0, 0.0)); + graph.addNode(1, dsf::geometry::Point(1.0, 0.0)); + graph.addNode(2, dsf::geometry::Point(2.0, 0.0)); + + Street s01(0, std::make_pair(0, 1), 100.0); + Street s12(1, std::make_pair(1, 2), 100.0); + graph.addStreets(s01, s12); + + // Close the only path to node 2 + graph.setStreetStatusById(1, RoadStatus::CLOSED); + + auto pathMap = graph.allPathsTo(2, [](auto const& pEdge) { return pEdge->length(); }); + + // Node 0 should not have a path to 2 + CHECK_FALSE(pathMap.contains(0)); + // Node 1 should not have a path to 2 either + CHECK_FALSE(pathMap.contains(1)); + } + + SUBCASE("Closing road by name affects allPathsTo") { + RoadNetwork graph{}; + graph.addNode(0, dsf::geometry::Point(0.0, 0.0)); + graph.addNode(1, dsf::geometry::Point(1.0, 0.0)); + graph.addNode(2, dsf::geometry::Point(2.0, 0.0)); + graph.addNode(3, dsf::geometry::Point(1.0, 1.0)); + + Street s01(0, std::make_pair(0, 1), 100.0, 13.8888, 1, "Main Road"); + Street s12(1, std::make_pair(1, 2), 100.0, 13.8888, 1, "Main Road"); + Street s03(2, std::make_pair(0, 3), 150.0, 13.8888, 1, "Side Road"); + Street s32(3, std::make_pair(3, 2), 150.0, 13.8888, 1, "Side Road"); + graph.addStreets(s01, s12, s03, s32); + + // Close all "Main Road" streets + graph.setStreetStatusByName("Main Road", RoadStatus::CLOSED); + + auto pathMap = graph.allPathsTo(2, [](auto const& pEdge) { return pEdge->length(); }); + + // From node 0, only path should be via node 3 + REQUIRE(pathMap.contains(0)); + CHECK_EQ(pathMap.at(0).size(), 1); + CHECK_EQ(pathMap.at(0)[0], 3); + + // Node 1 should have no path to 2 (since 1->2 is closed) + CHECK_FALSE(pathMap.contains(1)); + } +} From 3a9e97f34094abe349a51a1e34d90956ad3bf59d Mon Sep 17 00:00:00 2001 From: grufoony Date: Mon, 26 Jan 2026 17:32:50 +0100 Subject: [PATCH 2/8] Change capacity --- src/dsf/bindings.cpp | 10 ++++++++++ src/dsf/mobility/RoadNetwork.cpp | 31 +++++++++++++++++++++++++++---- src/dsf/mobility/RoadNetwork.hpp | 4 ++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/dsf/bindings.cpp b/src/dsf/bindings.cpp index b52338d8..b4381da8 100644 --- a/src/dsf/bindings.cpp +++ b/src/dsf/bindings.cpp @@ -261,6 +261,16 @@ PYBIND11_MODULE(dsf_cpp, m) { pybind11::arg("status"), dsf::g_docstrings.at("dsf::mobility::RoadNetwork::setStreetStatusByName") .c_str()) + .def("changeStreetCapacityById", + &dsf::mobility::RoadNetwork::changeStreetCapacityById, + pybind11::arg("streetId"), + pybind11::arg("factor"), + dsf::g_docstrings.at("dsf::mobility::RoadNetwork::changeStreetCapacityById").c_str()) + .def("changeStreetCapacityByName", + &dsf::mobility::RoadNetwork::changeStreetCapacityByName, + pybind11::arg("name"), + pybind11::arg("factor"), + dsf::g_docstrings.at("dsf::mobility::RoadNetwork::changeStreetCapacityByName").c_str()) .def("addCoil", &dsf::mobility::RoadNetwork::addCoil, pybind11::arg("streetId"), diff --git a/src/dsf/mobility/RoadNetwork.cpp b/src/dsf/mobility/RoadNetwork.cpp index e76e4ed8..6668698a 100644 --- a/src/dsf/mobility/RoadNetwork.cpp +++ b/src/dsf/mobility/RoadNetwork.cpp @@ -949,10 +949,33 @@ namespace dsf::mobility { ++nAffectedRoads; } }); - spdlog::info("Set status {} to {} streets with name containing \"{}\"", - status, - nAffectedRoads.load(), - streetName); + // spdlog::info("Set status {} to {} streets with name containing \"{}\"", + // status, + // nAffectedRoads.load(), + // streetName); + } + void RoadNetwork::changeStreetCapacityById(Id const streetId, double const factor) { + auto const& pStreet{edge(streetId)}; + auto const& currentCapacity{pStreet->capacity()}; + pStreet->setCapacity(std::ceil(currentCapacity * factor)); + } + void RoadNetwork::changeStreetCapacityByName(std::string const& streetName, + double const factor) { + std::atomic nAffectedRoads{0}; + std::for_each(DSF_EXECUTION m_edges.cbegin(), + m_edges.cend(), + [this, &streetName, &factor, &nAffectedRoads](auto const& pair) { + auto const& pStreet = pair.second; + if (pStreet->name().find(streetName) != std::string::npos) { + auto const& currentCapacity = pStreet->capacity(); + pStreet->setCapacity(std::ceil(currentCapacity * factor)); + ++nAffectedRoads; + } + }); + // spdlog::info("Changed capacity by factor {} to {} streets with name containing \"{}\"", + // factor, + // nAffectedRoads.load(), + // streetName); } void RoadNetwork::setStreetStationaryWeights( diff --git a/src/dsf/mobility/RoadNetwork.hpp b/src/dsf/mobility/RoadNetwork.hpp index c37ab917..d07d6ad5 100644 --- a/src/dsf/mobility/RoadNetwork.hpp +++ b/src/dsf/mobility/RoadNetwork.hpp @@ -208,6 +208,10 @@ namespace dsf::mobility { /// @param status The status to set void setStreetStatusByName(std::string const& name, RoadStatus const status); + void changeStreetCapacityById(Id const streetId, double const factor); + + void changeStreetCapacityByName(std::string const& name, double const factor); + /// @brief Set the streets' stationary weights /// @param streetWeights A map where the key is the street id and the value is the street stationary weight. If a street id is not present in the map, its stationary weight is set to 1.0. void setStreetStationaryWeights(std::unordered_map const& streetWeights); From 0a7236cc71cd14113cf594f1b8246d3bae69ba67 Mon Sep 17 00:00:00 2001 From: Grufoony Date: Wed, 28 Jan 2026 10:34:37 +0100 Subject: [PATCH 3/8] Better documentation --- src/dsf/mobility/RoadNetwork.hpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/dsf/mobility/RoadNetwork.hpp b/src/dsf/mobility/RoadNetwork.hpp index d07d6ad5..a6a883ee 100644 --- a/src/dsf/mobility/RoadNetwork.hpp +++ b/src/dsf/mobility/RoadNetwork.hpp @@ -204,12 +204,16 @@ namespace dsf::mobility { /// @param status The status to set void setStreetStatusById(Id const streetId, RoadStatus const status); /// @brief Set the street's status of all streets with the given name - /// @param name The name of the street + /// @param name The name to match /// @param status The status to set void setStreetStatusByName(std::string const& name, RoadStatus const status); - + /// @brief Change the street's capacity by its id + /// @param streetId The id of the street + /// @param factor The factor to multiply the capacity by void changeStreetCapacityById(Id const streetId, double const factor); - + /// @brief Change the street's capacity of all streets with the given name + /// @param name The name to match + /// @param factor The factor to multiply the capacity by void changeStreetCapacityByName(std::string const& name, double const factor); /// @brief Set the streets' stationary weights From 0eed3ec129c42cac33d3c28cf3aca5365417fa3b Mon Sep 17 00:00:00 2001 From: Grufoony Date: Wed, 28 Jan 2026 10:34:51 +0100 Subject: [PATCH 4/8] Formatting --- src/dsf/bindings.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dsf/bindings.cpp b/src/dsf/bindings.cpp index b4381da8..626fe35b 100644 --- a/src/dsf/bindings.cpp +++ b/src/dsf/bindings.cpp @@ -265,12 +265,14 @@ PYBIND11_MODULE(dsf_cpp, m) { &dsf::mobility::RoadNetwork::changeStreetCapacityById, pybind11::arg("streetId"), pybind11::arg("factor"), - dsf::g_docstrings.at("dsf::mobility::RoadNetwork::changeStreetCapacityById").c_str()) + dsf::g_docstrings.at("dsf::mobility::RoadNetwork::changeStreetCapacityById") + .c_str()) .def("changeStreetCapacityByName", &dsf::mobility::RoadNetwork::changeStreetCapacityByName, pybind11::arg("name"), pybind11::arg("factor"), - dsf::g_docstrings.at("dsf::mobility::RoadNetwork::changeStreetCapacityByName").c_str()) + dsf::g_docstrings.at("dsf::mobility::RoadNetwork::changeStreetCapacityByName") + .c_str()) .def("addCoil", &dsf::mobility::RoadNetwork::addCoil, pybind11::arg("streetId"), From e196046b5075ab07b7a84ce2aab763ca58e0de3f Mon Sep 17 00:00:00 2001 From: Grufoony Date: Wed, 28 Jan 2026 10:35:06 +0100 Subject: [PATCH 5/8] Enhance logging --- src/dsf/mobility/Road.hpp | 24 +++++++++++++++++++++++- src/dsf/mobility/RoadDynamics.hpp | 20 ++++++++++---------- src/dsf/mobility/RoadNetwork.cpp | 21 +++++++++++---------- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/dsf/mobility/Road.hpp b/src/dsf/mobility/Road.hpp index fe19877d..5e9a3c3a 100644 --- a/src/dsf/mobility/Road.hpp +++ b/src/dsf/mobility/Road.hpp @@ -2,6 +2,7 @@ #include "../base/Edge.hpp" +#include #include #include #include @@ -138,4 +139,25 @@ namespace dsf::mobility { virtual double nExitingAgents(Direction direction, bool normalizeOnNLanes) const = 0; virtual double density(bool normalized = false) const = 0; }; -} // namespace dsf::mobility \ No newline at end of file +} // namespace dsf::mobility + +template <> +struct std::formatter { + constexpr auto parse(std::format_parse_context& ctx) { return ctx.begin(); } + template + auto format(dsf::mobility::RoadStatus const& status, FormatContext&& ctx) const { + std::string_view name; + switch (status) { + case dsf::mobility::RoadStatus::OPEN: + name = "OPEN"; + break; + case dsf::mobility::RoadStatus::CLOSED: + name = "CLOSED"; + break; + default: + name = "UNKNOWN"; + break; + } + return std::format_to(ctx.out(), "{}", name); + } +}; \ No newline at end of file diff --git a/src/dsf/mobility/RoadDynamics.hpp b/src/dsf/mobility/RoadDynamics.hpp index c769928e..8f750763 100644 --- a/src/dsf/mobility/RoadDynamics.hpp +++ b/src/dsf/mobility/RoadDynamics.hpp @@ -499,10 +499,10 @@ namespace dsf::mobility { pItinerary->setPath(path); auto const newSize{pItinerary->path().size()}; if (oldSize > 0 && newSize != oldSize) { - spdlog::warn("Path for itinerary {} changed size from {} to {}", - pItinerary->id(), - oldSize, - newSize); + spdlog::debug("Path for itinerary {} changed size from {} to {}", + pItinerary->id(), + oldSize, + newSize); } if (m_bCacheEnabled) { pItinerary->save(std::format("{}{}.ity", CACHE_FOLDER, pItinerary->id())); @@ -731,7 +731,7 @@ namespace dsf::mobility { auto const timeTolerance{m_timeToleranceFactor.value() * std::ceil(pStreet->length() / pStreet->maxSpeed())}; if (timeDiff > timeTolerance) { - spdlog::warn( + spdlog::debug( "Time-step {} - {} currently on {} ({} turn - Traffic Light? {}), " "has been still for more than {} seconds ({} seconds). Killing it.", this->time_step(), @@ -1302,7 +1302,7 @@ namespace dsf::mobility { m_nAddedAgents += nAgents; if (m_timeToleranceFactor.has_value() && !m_agents.empty()) { auto const nStagnantAgents{m_agents.size()}; - spdlog::warn( + spdlog::debug( "Removing {} stagnant agents that were not inserted since the previous call to " "addAgentsUniformly().", nStagnantAgents); @@ -1414,7 +1414,7 @@ namespace dsf::mobility { m_nAddedAgents += nAgents; if (m_timeToleranceFactor.has_value() && !m_agents.empty()) { auto const nStagnantAgents{m_agents.size()}; - spdlog::warn( + spdlog::debug( "Removing {} stagnant agents that were not inserted since the previous call to " "addAgentsRandomly().", nStagnantAgents); @@ -1520,9 +1520,9 @@ namespace dsf::mobility { // Check if destination is reachable from source auto const& itinerary = itineraryIt->second; if (!itinerary->path().contains(*srcId)) { - spdlog::warn("Destination {} not reachable from source {}. Skipping agent.", - *dstId, - *srcId); + spdlog::debug("Destination {} not reachable from source {}. Skipping agent.", + *dstId, + *srcId); --nAgents; continue; } diff --git a/src/dsf/mobility/RoadNetwork.cpp b/src/dsf/mobility/RoadNetwork.cpp index 6668698a..66663e98 100644 --- a/src/dsf/mobility/RoadNetwork.cpp +++ b/src/dsf/mobility/RoadNetwork.cpp @@ -802,7 +802,7 @@ namespace dsf::mobility { } if (priorityRoads.size() < 2) { - spdlog::warn("Node {}: unable to auto-assign road priorities", pNode->id()); + spdlog::warn("{}: unable to auto-assign road priorities", *pNode); return; } @@ -949,10 +949,10 @@ namespace dsf::mobility { ++nAffectedRoads; } }); - // spdlog::info("Set status {} to {} streets with name containing \"{}\"", - // status, - // nAffectedRoads.load(), - // streetName); + spdlog::info("Set status {} to {} streets with name containing \"{}\"", + status, + nAffectedRoads.load(), + streetName); } void RoadNetwork::changeStreetCapacityById(Id const streetId, double const factor) { auto const& pStreet{edge(streetId)}; @@ -960,7 +960,7 @@ namespace dsf::mobility { pStreet->setCapacity(std::ceil(currentCapacity * factor)); } void RoadNetwork::changeStreetCapacityByName(std::string const& streetName, - double const factor) { + double const factor) { std::atomic nAffectedRoads{0}; std::for_each(DSF_EXECUTION m_edges.cbegin(), m_edges.cend(), @@ -972,10 +972,11 @@ namespace dsf::mobility { ++nAffectedRoads; } }); - // spdlog::info("Changed capacity by factor {} to {} streets with name containing \"{}\"", - // factor, - // nAffectedRoads.load(), - // streetName); + spdlog::info( + "Changed capacity by factor {} to {} streets with name containing \"{}\"", + factor, + nAffectedRoads.load(), + streetName); } void RoadNetwork::setStreetStationaryWeights( From eb1831e471929238a9d43aa899b0e238fea71dab Mon Sep 17 00:00:00 2001 From: Grufoony Date: Wed, 28 Jan 2026 10:41:22 +0100 Subject: [PATCH 6/8] Better exceptions --- src/dsf/mobility/RoadNetwork.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/dsf/mobility/RoadNetwork.cpp b/src/dsf/mobility/RoadNetwork.cpp index 66663e98..b4c5aeaa 100644 --- a/src/dsf/mobility/RoadNetwork.cpp +++ b/src/dsf/mobility/RoadNetwork.cpp @@ -935,7 +935,11 @@ namespace dsf::mobility { } void RoadNetwork::setStreetStatusById(Id const streetId, RoadStatus const status) { - edge(streetId)->setStatus(status); + try { + edge(streetId)->setStatus(status); + } catch (const std::out_of_range&) { + throw std::out_of_range(std::format("Street with id {} not found", streetId)); + } } void RoadNetwork::setStreetStatusByName(std::string const& streetName, RoadStatus const status) { @@ -955,9 +959,13 @@ namespace dsf::mobility { streetName); } void RoadNetwork::changeStreetCapacityById(Id const streetId, double const factor) { - auto const& pStreet{edge(streetId)}; - auto const& currentCapacity{pStreet->capacity()}; - pStreet->setCapacity(std::ceil(currentCapacity * factor)); + try { + auto const& pStreet{edge(streetId)}; + auto const& currentCapacity{pStreet->capacity()}; + pStreet->setCapacity(std::ceil(currentCapacity * factor)); + } catch (const std::out_of_range&) { + throw std::out_of_range(std::format("Street with id {} not found", streetId)); + } } void RoadNetwork::changeStreetCapacityByName(std::string const& streetName, double const factor) { From b3294137d3e9f1e2369a8aba5997c0a90809c3dd Mon Sep 17 00:00:00 2001 From: Grufoony Date: Wed, 28 Jan 2026 10:41:45 +0100 Subject: [PATCH 7/8] Version 4.7.6 --- src/dsf/dsf.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dsf/dsf.hpp b/src/dsf/dsf.hpp index a7154348..689da647 100644 --- a/src/dsf/dsf.hpp +++ b/src/dsf/dsf.hpp @@ -6,7 +6,7 @@ static constexpr uint8_t DSF_VERSION_MAJOR = 4; static constexpr uint8_t DSF_VERSION_MINOR = 7; -static constexpr uint8_t DSF_VERSION_PATCH = 5; +static constexpr uint8_t DSF_VERSION_PATCH = 6; static auto const DSF_VERSION = std::format("{}.{}.{}", DSF_VERSION_MAJOR, DSF_VERSION_MINOR, DSF_VERSION_PATCH); From 80e874dc23ded999f3241522e480337e27a6e609 Mon Sep 17 00:00:00 2001 From: Grufoony Date: Wed, 28 Jan 2026 10:59:12 +0100 Subject: [PATCH 8/8] I don't know what file is this [skip-ci] --- src/dsf/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/dsf/py.typed diff --git a/src/dsf/py.typed b/src/dsf/py.typed deleted file mode 100644 index e69de29b..00000000