Skip to content
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
36390b3
Init io node set in importOSMNodes
Grufoony Jan 30, 2025
c0d7d0e
Add general addAgentsRandomly function
Grufoony Jan 30, 2025
c92c5d6
Bugfix
Grufoony Jan 30, 2025
3ef73b2
Logging
Grufoony Jan 30, 2025
9107a59
Formatting
Grufoony Jan 30, 2025
49d79b0
Merge branch 'main' into io_nodes
Grufoony Jan 30, 2025
28da2e4
Update version
Grufoony Jan 30, 2025
ce51ac3
Bugfix + logging
Grufoony Jan 30, 2025
054471c
ChatGPPT optimization
Grufoony Jan 30, 2025
943d578
Add path caching
Grufoony Jan 31, 2025
6e6fb57
Merge branch 'pathCaching' into io_nodes
Grufoony Jan 31, 2025
1fd0ddb
Improve
Grufoony Jan 31, 2025
76f3d46
Merge branch 'pathCaching' into io_nodes
Grufoony Jan 31, 2025
8262983
To revise
Grufoony Jan 31, 2025
b0dc230
First tryal
Grufoony Jan 31, 2025
d88767b
Handle oneway
Grufoony Jan 31, 2025
ae67561
Prova
Grufoony Jan 31, 2025
12c289c
Merge branch 'main' into io_nodes
Grufoony Jan 31, 2025
c15e7bd
Update version
Grufoony Jan 31, 2025
98b3762
Merge branch 'main' into io_nodes
Grufoony Jan 31, 2025
4ff36b2
Update version
Grufoony Jan 31, 2025
fe2f5c0
Fix merge
Grufoony Jan 31, 2025
7f39bfa
Add debug logs
Grufoony Feb 3, 2025
6242461
Merge branch 'main' into io_nodes
Grufoony Feb 3, 2025
c09776c
Info to Debug
Grufoony Feb 3, 2025
f51bbf7
Enhance code security
Grufoony Feb 3, 2025
422f160
Enhance code security
Grufoony Feb 3, 2025
628b5f3
Bugfix
Grufoony Feb 3, 2025
5263389
Remove optional from street dequeue
Grufoony Feb 3, 2025
657a149
To remove asap
Grufoony Feb 3, 2025
e3c31d4
Merge branch 'main' into io_nodes
Grufoony Feb 3, 2025
731a36c
Log messages
Grufoony Feb 4, 2025
56e9d02
Bugfix
Grufoony Feb 4, 2025
69f8a99
Optimize `addAgentsRandomly` functions
Grufoony Feb 5, 2025
1201949
Merge branch 'main' into io_nodes
Grufoony Feb 5, 2025
daa2cf9
Update version
Grufoony Feb 5, 2025
81ac4f9
Fix tests
Grufoony Feb 5, 2025
87232f5
Merge branch 'main' into io_nodes
Grufoony Feb 12, 2025
912e438
Bugfix
Grufoony Feb 12, 2025
864c252
Merge branch 'main' into io_nodes
Grufoony Feb 14, 2025
53f0de3
Label coherence
Grufoony Feb 14, 2025
6a1443e
Update version
Grufoony Feb 14, 2025
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
2 changes: 1 addition & 1 deletion src/dsm/dsm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

static constexpr uint8_t DSM_VERSION_MAJOR = 2;
static constexpr uint8_t DSM_VERSION_MINOR = 3;
static constexpr uint8_t DSM_VERSION_PATCH = 18;
static constexpr uint8_t DSM_VERSION_PATCH = 19;

static auto const DSM_VERSION =
std::format("{}.{}.{}", DSM_VERSION_MAJOR, DSM_VERSION_MINOR, DSM_VERSION_PATCH);
Expand Down
75 changes: 56 additions & 19 deletions src/dsm/headers/Dynamics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,46 +101,60 @@
Size const dimension = m_graph.adjMatrix().getRowDim();
auto const destinationID = pItinerary->destination();
SparseMatrix<bool> path{dimension, dimension};
// cycle over the nodes

// Cache shortest paths to avoid redundant calculations
std::unordered_map<Id, std::optional<DijkstraResult>> shortestPaths;

for (const auto& [nodeId, node] : m_graph.nodeSet()) {
if (nodeId == destinationID) {
if (nodeId == destinationID || m_graph.adjMatrix().getRow(nodeId).empty()) {
continue;
}
auto result{m_graph.shortestPath(nodeId, destinationID)};

// Compute and cache shortest path from nodeId to destination
auto result = m_graph.shortestPath(nodeId, destinationID);
if (!result.has_value()) {
Logger::warning(

Check warning on line 116 in src/dsm/headers/Dynamics.hpp

View check run for this annotation

Codecov / codecov/patch

src/dsm/headers/Dynamics.hpp#L116

Added line #L116 was not covered by tests
std::format("No path found from node {} to {}", nodeId, destinationID));
continue;
}
// save the minimum distance between i and the destination
const auto minDistance{result.value().distance()};
for (const auto [nextNodeId, _] : m_graph.adjMatrix().getRow(nodeId)) {
if (nextNodeId == destinationID &&
minDistance ==
m_graph.streetSet()[nodeId * dimension + nextNodeId]->length()) {
path.insert(nodeId, nextNodeId, true);

const auto minDistance = result.value().distance();
shortestPaths[nodeId] = result; // Cache for reuse

for (const auto& [nextNodeId, _] : m_graph.adjMatrix().getRow(nodeId)) {
if (nextNodeId == destinationID) {
if (minDistance ==
m_graph.streetSet()[nodeId * dimension + nextNodeId]->length()) {
path.insert(nodeId, nextNodeId, true);
}
continue;
}
result = m_graph.shortestPath(nextNodeId, destinationID);

if (result.has_value()) {
// if the shortest path exists, save the distance
// Use cached shortest path if available
auto it = shortestPaths.find(nextNodeId);
if (it == shortestPaths.end()) {
shortestPaths[nextNodeId] = m_graph.shortestPath(nextNodeId, destinationID);
}

if (shortestPaths[nextNodeId].has_value()) {
const auto nextDistance = shortestPaths[nextNodeId].value().distance();
if (minDistance ==
result.value().distance() +
nextDistance +
m_graph.streetSet()[nodeId * dimension + nextNodeId]->length()) {
path.insert(nodeId, nextNodeId, true);
}
} else if ((nextNodeId != destinationID)) {
Logger::warning(std::format(
"No path found from node {} to node {}", nextNodeId, destinationID));
}
}
}

if (path.size() == 0) {
Logger::error(
std::format("Path with id {} and destination {} is empty. Please "
"check the adjacency matrix.",
std::format("Path with id {} and destination {} is empty. Please check the "
"adjacency matrix.",
pItinerary->id(),
pItinerary->destination()));
}

pItinerary->setPath(path);
if (m_bCacheEnabled) {
pItinerary->path().cache(
Expand Down Expand Up @@ -230,6 +244,8 @@
/// @param itineraries Generic container of itineraries, represented by an std::span
void addItineraries(std::span<Itinerary> itineraries);

void enableCache();

/// @brief Reset the simulation time
void resetTime();

Expand Down Expand Up @@ -298,7 +314,7 @@
/// @brief Save the street output counts in csv format
/// @param filename The name of the file
/// @param reset If true, the output counts are cleared after the computation
/// @details NOTE: counts are printed only if the street is a spire

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 14.4 rule Note

MISRA 14.4 rule
void saveOutputStreetCounts(const std::string& filename, bool reset = false);
};

Expand All @@ -320,13 +336,25 @@
}
Logger::info(std::format("Cache enabled (default folder is {})", g_cacheFolder));
}
for (const auto& nodeId : this->m_graph.outputNodes()) {
addItinerary(Itinerary{nodeId, nodeId});
m_updatePath(m_itineraries.at(nodeId));

Check warning on line 341 in src/dsm/headers/Dynamics.hpp

View check run for this annotation

Codecov / codecov/patch

src/dsm/headers/Dynamics.hpp#L340-L341

Added lines #L340 - L341 were not covered by tests
}
// updatePaths();
}

template <typename agent_t>
void Dynamics<agent_t>::updatePaths() {
if (m_bCacheEnabled) {
if (!std::filesystem::exists(g_cacheFolder)) {
std::filesystem::create_directory(g_cacheFolder);

Check warning on line 350 in src/dsm/headers/Dynamics.hpp

View check run for this annotation

Codecov / codecov/patch

src/dsm/headers/Dynamics.hpp#L350

Added line #L350 was not covered by tests
}
}

std::vector<std::thread> threads;
threads.reserve(m_itineraries.size());
std::exception_ptr pThreadException;
Logger::info(std::format("Init computing {} paths", m_itineraries.size()));
for (const auto& [itineraryId, itinerary] : m_itineraries) {
threads.emplace_back(std::thread([this, &itinerary, &pThreadException] {
try {
Expand All @@ -343,6 +371,8 @@
// Throw the exception launched first
if (pThreadException)
std::rethrow_exception(pThreadException);

Logger::info("End computing paths");
}

template <typename agent_t>
Expand Down Expand Up @@ -371,6 +401,7 @@
std::format("Agent with id {} already exists.", agent->id())));
}
m_agents.emplace(agent->id(), std::move(agent));
Logger::debug(std::format("Added agent with id {}", m_agents.rbegin()->first));
}

template <typename agent_t>
Expand Down Expand Up @@ -449,6 +480,12 @@
});
}

template <typename agent_t>
void Dynamics<agent_t>::enableCache() {
m_bCacheEnabled = true;
Logger::info(std::format("Cache enabled (default folder is {})", g_cacheFolder));
}

template <typename agent_t>
void Dynamics<agent_t>::resetTime() {
m_time = 0;
Expand Down
9 changes: 9 additions & 0 deletions src/dsm/headers/Graph.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ namespace dsm {
std::unordered_map<Id, std::unique_ptr<Node>> m_nodes;
std::unordered_map<Id, std::unique_ptr<Street>> m_streets;
std::unordered_map<std::string, Id> m_nodeMapping;
std::vector<Id> m_inputNodes;
std::vector<Id> m_outputNodes;
SparseMatrix<bool> m_adjacency;
unsigned long long m_maxAgentCapacity;

Expand Down Expand Up @@ -80,6 +82,8 @@ namespace dsm {
});
m_nodeMapping = other.m_nodeMapping;
m_adjacency = other.m_adjacency;
m_inputNodes = other.m_inputNodes;
m_outputNodes = other.m_outputNodes;
}

Graph& operator=(const Graph& other) {
Expand All @@ -94,6 +98,8 @@ namespace dsm {
});
m_nodeMapping = other.m_nodeMapping;
m_adjacency = other.m_adjacency;
m_inputNodes = other.m_inputNodes;
m_outputNodes = other.m_outputNodes;

return *this;
}
Expand Down Expand Up @@ -261,6 +267,9 @@ namespace dsm {
/// @return unsigned long long The maximum agent capacity of the graph
unsigned long long maxCapacity() const { return m_maxAgentCapacity; }

std::vector<Id> const& inputNodes() const { return m_inputNodes; }
std::vector<Id> const& outputNodes() const { return m_outputNodes; }

/// @brief Get the shortest path between two nodes using dijkstra algorithm
/// @param source The source node
/// @param destination The destination node
Expand Down
24 changes: 24 additions & 0 deletions src/dsm/headers/RoadDynamics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@
const TContainer& dst_weights,
const size_t minNodeDistance = 0);

void addAgentsRandomly(Size nAgents, const size_t minNodeDistance = 0);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 17.8 rule Note

MISRA 17.8 rule

/// @brief Evolve the simulation
/// @details Evolve the simulation by moving the agents and updating the travel times.
/// In particular:
Expand Down Expand Up @@ -596,6 +598,13 @@
const TContainer& src_weights,
const TContainer& dst_weights,
const size_t minNodeDistance) {
Logger::debug(
std::format("Init addAgentsRandomly for {} agents from {} nodes to {} nodes with "
"minNodeDistance {}",
nAgents,
src_weights.size(),
dst_weights.size(),
minNodeDistance));
if (src_weights.size() == 1 && dst_weights.size() == 1 &&
src_weights.begin()->first == dst_weights.begin()->first) {
throw std::invalid_argument(Logger::buildExceptionMessage(
Expand Down Expand Up @@ -630,6 +639,7 @@
if (!this->agents().empty()) {
agentId = this->agents().rbegin()->first + 1;
}
Logger::debug(std::format("Adding {} agents at time {}.", nAgents, this->time()));

Check notice

Code scanning / Cppcheck (reported by Codacy)

time is Y2038-unsafe Note

time is Y2038-unsafe
while (nAgents > 0) {
Id srcId{0}, dstId{0};
if (dst_weights.size() == 1) {
Expand Down Expand Up @@ -683,6 +693,20 @@
}
}

template <typename delay_t>
requires(is_numeric_v<delay_t>)
void RoadDynamics<delay_t>::addAgentsRandomly(Size nAgents,
const size_t minNodeDistance) {
std::unordered_map<Id, double> src_weights, dst_weights;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note

MISRA 12.3 rule
for (auto const& id : this->m_graph.inputNodes()) {
src_weights[id] = 1.;
}
for (auto const& id : this->m_graph.outputNodes()) {
dst_weights[id] = 1.;
}
addAgentsRandomly(nAgents, src_weights, dst_weights, minNodeDistance);
}

template <typename delay_t>
requires(is_numeric_v<delay_t>)
void RoadDynamics<delay_t>::evolve(bool reinsert_agents) {
Expand Down
2 changes: 2 additions & 0 deletions src/dsm/headers/SparseMatrix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ namespace dsm {
/// @return number of non zero elements
Id size() const { return _matrix.size(); };

bool empty() const { return _matrix.empty(); }

/// @brief get the maximum number of elements in the matrix
/// @return maximum number of elements
Id max_size() const { return _rows * _cols; }
Expand Down
14 changes: 14 additions & 0 deletions src/dsm/sources/Graph.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@

#include "../headers/Graph.hpp"

#include <algorithm>

namespace dsm {
Graph::Graph()
: m_adjacency{SparseMatrix<bool>()},
Expand Down Expand Up @@ -339,13 +341,24 @@
} else {
addNode<Intersection>(nodeIndex,
std::make_pair(std::stod(lat), std::stod(lon)));
if ((highway.find("in_out") != std::string::npos) ||
(highway.find("outgoing_only") != std::string::npos)) {
Logger::debug(std::format("Setting node {} as an output node", nodeIndex));
m_outputNodes.push_back(nodeIndex);

Check warning on line 347 in src/dsm/sources/Graph.cpp

View check run for this annotation

Codecov / codecov/patch

src/dsm/sources/Graph.cpp#L346-L347

Added lines #L346 - L347 were not covered by tests
}
if ((highway.find("in_out") != std::string::npos) ||
(highway.find("incoming_only") != std::string::npos)) {
Logger::debug(std::format("Setting node {} as an input node", nodeIndex));
m_inputNodes.push_back(nodeIndex);

Check warning on line 352 in src/dsm/sources/Graph.cpp

View check run for this annotation

Codecov / codecov/patch

src/dsm/sources/Graph.cpp#L351-L352

Added lines #L351 - L352 were not covered by tests
}
}
m_nodeMapping.emplace(std::make_pair(id, nodeIndex));
++nodeIndex;
}
} else {
Logger::error(std::format("File extension ({}) not supported", fileExt));
}
Logger::info(std::format("Successfully imported {} nodes", nNodes()));
}

void Graph::importOSMEdges(const std::string& fileName) {
Expand Down Expand Up @@ -425,6 +438,7 @@
throw std::invalid_argument(
Logger::buildExceptionMessage("File extension not supported"));
}
Logger::info(std::format("Successfully imported {} edges", nEdges()));
}

void Graph::exportMatrix(std::string path, bool isAdj) {
Expand Down
Loading