Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for io flags in importOSMNodes function #256

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 19 commits
Commits
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 = 17;
static constexpr uint8_t DSM_VERSION_PATCH = 18;

static auto const DSM_VERSION =
std::format("{}.{}.{}", DSM_VERSION_MAJOR, DSM_VERSION_MINOR, DSM_VERSION_PATCH);
Expand Down
74 changes: 55 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;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note

MISRA 12.3 rule

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

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.1 rule Note

MISRA 12.1 rule
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(
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)) {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note

MISRA 12.3 rule
if (nextNodeId == destinationID) {
if (minDistance ==
m_graph.streetSet()[nodeId * dimension + nextNodeId]->length()) {
path.insert(nodeId, nextNodeId, true);

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.1 rule Note

MISRA 12.1 rule
}
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()) {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 14.4 rule Note

MISRA 14.4 rule
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));
}
// 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);
}
}

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 @@ -449,6 +479,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
16 changes: 16 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 @@ -683,6 +685,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
35 changes: 33 additions & 2 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);
}
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);
}
}
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 @@ -394,19 +407,19 @@
lanes = "1"; // Default to 1 lane if lanes is invalid
}
}
if (!m_nodeMapping.contains(sourceId)) {

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.1 rule Note

MISRA 12.1 rule
Logger::error(std::format("Node with id {} not found.", sourceId));
}
if (!m_nodeMapping.contains(targetId)) {
Logger::error(std::format("Node with id {} not found.", targetId));
}
if (sourceId == targetId) {
Logger::warning(

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.1 rule Note

MISRA 12.1 rule
std::format("Self loop detected: {}->{}. Skipping.", sourceId, targetId));
continue;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note

MISRA 12.3 rule
}
auto const srcId{m_nodeMapping.at(sourceId)};
auto const dstId{m_nodeMapping.at(targetId)};
auto srcId{m_nodeMapping.at(sourceId)};
auto dstId{m_nodeMapping.at(targetId)};
if (static_cast<unsigned long long>(srcId * nNodes + dstId) >
std::numeric_limits<Id>::max()) {
throw std::invalid_argument(Logger::buildExceptionMessage(
Expand All @@ -421,11 +434,29 @@
std::stod(maxspeed),
std::stoul(lanes),
name);
if (oneway == "False") {
std::swap(srcId, dstId);
if (static_cast<unsigned long long>(srcId * nNodes + dstId) >
std::numeric_limits<Id>::max()) {
throw std::invalid_argument(Logger::buildExceptionMessage(std::format(
"Street id {}->{} would too large for the current type of Id.",
srcId,
dstId)));
}
Id streetId = srcId * nNodes + dstId;
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
addEdge<Street>(streetId,
std::make_pair(srcId, dstId),
std::stod(length),
std::stod(maxspeed),
std::stoul(lanes),
name);
}
}
} else {
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
8 changes: 4 additions & 4 deletions src/dsm/utility/Logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@
};

void Logger::debug(const std::string& message, const std::source_location& location) {
#ifndef NDEBUG
std::clog << buildMessage("\033[38;2;0;255;0mDEBUG", message, location, m_verbose) +
"\033[0m\n";
#endif
if (m_verbose) {
std::clog << buildMessage("\033[38;2;0;255;0mDEBUG", message, location, m_verbose) +

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 4.1 rule Note

MISRA 4.1 rule

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 18.4 rule Note

MISRA 18.4 rule
"\033[0m\n";

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 4.1 rule Note

MISRA 4.1 rule
}
};
void Logger::info(const std::string& message, const std::source_location& location) {
std::clog << buildMessage("\033[1;32mINFO", message, location, m_verbose) +
Expand Down
Loading