Skip to content
123 changes: 71 additions & 52 deletions src/dsm/headers/Dynamics.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@

using TimePoint = long long unsigned int;

template <typename T>
std::unique_ptr<T> clone(const std::unique_ptr<T>& ptr) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to have a one-line function?

return std::make_unique<T>(*ptr);
}

/// @brief The Measurement struct represents the mean of a quantity and its standard deviation
/// @tparam T The type of the quantity
/// @param mean The mean
Expand Down Expand Up @@ -67,7 +72,7 @@
requires(is_numeric_v<Delay>)
class Dynamics {
protected:
std::unordered_map<Id, std::unique_ptr<Itinerary>> m_itineraries;
std::vector<std::unique_ptr<Itinerary>> m_itineraries;
std::map<Id, std::unique_ptr<Agent<Delay>>> m_agents;
TimePoint m_time, m_previousSpireTime;
Graph m_graph;
Expand Down Expand Up @@ -154,9 +159,8 @@
}
if (path.size() == 0) {
throw std::runtime_error(
buildLog(std::format("Path with id {} and destination {} is empty. Please "
buildLog(std::format("Path destination {} is empty. Please "
"check the adjacency matrix.",
pItinerary->id(),
pItinerary->destination())));
}
pItinerary->setPath(path);
Expand Down Expand Up @@ -190,7 +194,7 @@
/// @param destinationNodes The destination nodes
/// @param updatePaths If true, the paths are updated
/// @throws std::invalid_argument Ifone or more destination nodes do not exist
void setDestinationNodes(const std::span<Id>& destinationNodes,
void setDestinationNodes(std::span<Id> destinationNodes,
bool updatePaths = true);
/// @brief Set the speed of an agent
/// @details This is a pure-virtual function, it must be implemented in the derived classes
Expand Down Expand Up @@ -232,9 +236,11 @@
/// @brief Get the graph
/// @return const Graph&, The graph
const Graph& graph() const { return m_graph; };

const Itinerary* itinerary(Id destination) const;
/// @brief Get the itineraries
/// @return const std::unordered_map<Id, Itinerary>&, The itineraries
const std::unordered_map<Id, std::unique_ptr<Itinerary>>& itineraries() const {
const std::vector<std::unique_ptr<Itinerary>>& itineraries() const {
return m_itineraries;
}
/// @brief Get the agents
Expand Down Expand Up @@ -414,7 +420,7 @@
requires(is_numeric_v<Delay>)
Id Dynamics<Delay>::m_nextStreetId(Id agentId, Id nodeId, std::optional<Id> streetId) {
auto possibleMoves = m_graph.adjMatrix().getRow(nodeId, true);
if (this->m_itineraries.size() > 0 &&
if (!this->m_itineraries.empty() &&
this->m_uniformDist(this->m_generator) > this->m_errorProbability) {
const auto& it = this->m_itineraries[this->m_agents[agentId]->itineraryId()];
if (it->destination() != nodeId) {
Expand Down Expand Up @@ -662,9 +668,10 @@
template <typename Delay>
requires(is_numeric_v<Delay>)
void Dynamics<Delay>::setItineraries(std::span<Itinerary> itineraries) {
std::ranges::for_each(itineraries, [this](const auto& itinerary) {
this->m_itineraries.insert(std::make_unique<Itinerary>(itinerary));
});
std::transform(itineraries.cbegin(),
itineraries.cend(),
m_itineraries.begin(),
[this](const auto& pItinerary) { return clone(pItinerary); });

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 15.5 rule Note

MISRA 15.5 rule
}

template <typename Delay>
Expand Down Expand Up @@ -700,27 +707,42 @@

template <typename Delay>
requires(is_numeric_v<Delay>)
void Dynamics<Delay>::setDestinationNodes(const std::span<Id>& destinationNodes,
void Dynamics<Delay>::setDestinationNodes(std::span<Id> destinationNodes,

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note

MISRA 12.3 rule
bool updatePaths) {
for (const auto& nodeId : destinationNodes) {
if (!m_graph.nodeSet().contains(nodeId)) {
throw std::invalid_argument(
buildLog(std::format("Node with id {} not found", nodeId)));
}
this->addItinerary(Itinerary{nodeId, nodeId});
}
m_itineraries.resize(destinationNodes.size());
std::transform(destinationNodes.cbegin(),
destinationNodes.cend(),
m_itineraries.begin(),
[this](auto nodeId) {
if (!this->m_graph.nodeSet().contains(nodeId)) {
throw std::invalid_argument(
buildLog(std::format("Node with id {} not found", nodeId)));
}
this->addItinerary(Itinerary{nodeId});
});
if (updatePaths) {
this->updatePaths();
}
}

template <typename Delay>
requires(is_numeric_v<Delay>)
const Itinerary* Dynamics<Delay>::itinerary(Id destination) const {
auto foundIt = std::find_if(m_itineraries.begin(),
m_itineraries.end(),
[destination](const auto& pItinerary) {
pItinerary->destination() == destination;
});
return (foundIt == m_itineraries.end()) ? nullptr : (*foundIt).get();

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 15.5 rule Note

MISRA 15.5 rule
}

template <typename Delay>
requires(is_numeric_v<Delay>)
void Dynamics<Delay>::updatePaths() {
std::vector<std::thread> threads;
threads.reserve(m_itineraries.size());
std::exception_ptr pThreadException;
for (const auto& [itineraryId, itinerary] : m_itineraries) {
for (const auto& itinerary : m_itineraries) {
threads.emplace_back(std::thread([this, &itinerary, &pThreadException] {
try {
this->m_updatePath(itinerary);
Expand Down Expand Up @@ -755,7 +777,7 @@
}
// Move transport capacity agents from each node
for (const auto& [nodeId, pNode] : m_graph.nodeSet()) {
for (auto i = 0; i < pNode->transportCapacity(); ++i) {
for (auto i = 0u; i < pNode->transportCapacity(); ++i) {
if (!this->m_evolveNode(pNode)) {
break;
}
Expand Down Expand Up @@ -936,47 +958,49 @@
}
template <typename Delay>
requires(is_numeric_v<Delay>)
void Dynamics<Delay>::addAgent(Id srcNodeId, Id itineraryId) {
void Dynamics<Delay>::addAgent(Id srcNodeId, Id dstNodeId) {
if (this->m_agents.size() + 1 > this->m_graph.maxCapacity()) {
throw std::overflow_error(buildLog(
std::format("Graph its already holding the max possible number of agents ({})",
this->m_graph.maxCapacity())));
}
if (!(srcNodeId < this->m_graph.nodeSet().size())) {
throw std::invalid_argument(
buildLog(std::format("Node with id {} not found", srcNodeId)));
}
if (!(this->m_itineraries.contains(itineraryId))) {
throw std::invalid_argument(
buildLog(std::format("Itinerary with id {} not found", itineraryId)));
}
assert((void("Nodes indexes out of range."),
srcNodeId < this->m_graph.nodeSet().size() &&

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.1 rule Note

MISRA 12.1 rule
dstNodeId < this->m_graph.nodeSet().size()));
assert((void("No itineray associated with the destination node."),
std::find_if(m_itineraries.begin(),
m_itineraries.end(),
[dstNodeId](const std::unique_ptr<Itinerary>& itinerary) {
return itinerary->destination() == dstNodeId;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 15.5 rule Note

MISRA 15.5 rule
}) != m_itineraries.end()));
Size agentId{0};
if (!this->m_agents.empty()) {
agentId = this->m_agents.rbegin()->first + 1;
}
this->addAgent(Agent<Delay>{agentId, itineraryId, srcNodeId});
this->addAgent(Agent<Delay>{agentId, dstNodeId, srcNodeId});
}
template <typename Delay>
requires(is_numeric_v<Delay>)
void Dynamics<Delay>::addAgents(Id itineraryId,
void Dynamics<Delay>::addAgents(Id dstNodeId,
Size nAgents,
std::optional<Id> srcNodeId) {
if (this->m_agents.size() + nAgents > this->m_graph.maxCapacity()) {
throw std::overflow_error(buildLog(
std::format("Graph its already holding the max possible number of agents ({})",
this->m_graph.maxCapacity())));
}
auto itineraryIt{m_itineraries.find(itineraryId)};
if (itineraryIt == m_itineraries.end()) {
throw std::invalid_argument(
buildLog(std::format("Itinerary with id {} not found", itineraryId)));
}
assert((void("No itineray associated with the destination node."),
std::find_if(m_itineraries.begin(),
m_itineraries.end(),
[dstNodeId](const std::unique_ptr<Itinerary>& itinerary) {
return itinerary->destination() == dstNodeId;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 15.5 rule Note

MISRA 15.5 rule
}) != m_itineraries.end()));
Size agentId{0};
if (!this->m_agents.empty()) {
agentId = this->m_agents.rbegin()->first + 1;
}
for (Size i{0}; i < nAgents; ++i, ++agentId) {
this->addAgent(Agent<Delay>{agentId, itineraryId});
this->addAgent(Agent<Delay>{agentId, dstNodeId});
if (srcNodeId.has_value()) {
this->m_agents[agentId]->setSourceNodeId(srcNodeId.value());
}
Expand Down Expand Up @@ -1031,9 +1055,7 @@
0, static_cast<Size>(this->m_graph.streetSet().size() - 1)};
for (Size i{0}; i < nAgents; ++i) {
if (randomItinerary) {
auto itineraryIt{this->m_itineraries.begin()};
std::advance(itineraryIt, itineraryDist(this->m_generator));
itineraryId = itineraryIt->first;
itineraryId = m_itineraries[itineraryDist(m_generator)]->destination();
}
Id agentId{0};
if (!this->m_agents.empty()) {
Expand Down Expand Up @@ -1110,16 +1132,13 @@
}
}
}
// find the itinerary with the given destination as destination
auto itineraryIt{std::find_if(
m_itineraries.begin(), m_itineraries.end(), [dstId](const auto& itinerary) {
return itinerary.second->destination() == dstId;
})};
if (itineraryIt == m_itineraries.end()) {
throw std::invalid_argument(
buildLog(std::format("Itinerary with destination {} not found.", dstId)));
}
this->addAgent(srcId, itineraryIt->first);
assert((void("No itineray associated with the destination node."),
std::find_if(m_itineraries.begin(),
m_itineraries.end(),
[dstId](const std::unique_ptr<Itinerary>& itinerary) {
return itinerary->destination() == dstId;

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 15.5 rule Note

MISRA 15.5 rule
}) != m_itineraries.end()));
this->addAgent(srcId, dstId);
--nAgents;
}
}
Expand All @@ -1142,13 +1161,13 @@
template <typename Delay>
requires(is_numeric_v<Delay>)
void Dynamics<Delay>::addItinerary(const Itinerary& itinerary) {
m_itineraries.emplace(itinerary.id(), std::make_unique<Itinerary>(itinerary));
m_itineraries.push_back(std::make_unique<Itinerary>(itinerary));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emplace_back?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

push_back should be better in this case

}

template <typename Delay>
requires(is_numeric_v<Delay>)
void Dynamics<Delay>::addItinerary(std::unique_ptr<Itinerary> itinerary) {
m_itineraries.emplace(itinerary->id(), std::move(itinerary));
m_itineraries.push_back(std::move(itinerary));
}

template <typename Delay>
Expand All @@ -1163,7 +1182,7 @@
requires(is_numeric_v<Delay>)
void Dynamics<Delay>::addItineraries(std::span<Itinerary> itineraries) {
std::ranges::for_each(itineraries, [this](const auto& itinerary) -> void {
this->m_itineraries.insert(std::make_unique<Itinerary>(itinerary));
this->m_itineraries.push_back(std::make_unique<Itinerary>(itinerary));
});
}

Expand Down
11 changes: 3 additions & 8 deletions src/dsm/headers/Itinerary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@
#include "Itinerary.hpp"

namespace dsm {
Itinerary::Itinerary(Id id, Id destination) : m_id{id}, m_destination{destination} {}
Itinerary::Itinerary(Id destination) : m_destination{destination} {}

Itinerary::Itinerary(Id id, Id destination, SparseMatrix<bool> path)
: m_id{id}, m_path{std::move(path)}, m_destination{destination} {}

void Itinerary::setDestination(Id destination) {
m_destination = destination;
this->m_path.clear();
}
Itinerary::Itinerary(Id destination, SparseMatrix<bool> path)
: m_path{std::move(path)}, m_destination{destination} {}

Check notice

Code scanning / Cppcheck (reported by Codacy)

MISRA 12.3 rule Note

MISRA 12.3 rule

void Itinerary::setPath(SparseMatrix<bool> path) {
if (path.getRowDim() != path.getColDim()) {
Expand Down
11 changes: 2 additions & 9 deletions src/dsm/headers/Itinerary.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,23 @@
/// @tparam Id The type of the itinerary's id. It must be an unsigned integral type.
class Itinerary {
private:
Id m_id;
SparseMatrix<bool> m_path;
Id m_destination;

public:
/// @brief Construct a new Itinerary object
/// @param destination The itinerary's destination
Itinerary(Id id, Id destination);
Itinerary(Id destination);

Check warning

Code scanning / Cppcheck (reported by Codacy)

Class 'Itinerary' has a constructor with 1 argument that is not explicit. Warning

Class 'Itinerary' has a constructor with 1 argument that is not explicit.
/// @brief Construct a new Itinerary object
/// @param destination The itinerary's destination
/// @param path An adjacency matrix made by a SparseMatrix representing the itinerary's path
Itinerary(Id id, Id destination, SparseMatrix<bool> path);
Itinerary(Id destination, SparseMatrix<bool> path);

/// @brief Set the itinerary's destination
/// @param destination The itinerary's destination
void setDestination(Id destination);
/// @brief Set the itinerary's path
/// @param path An adjacency matrix made by a SparseMatrix representing the itinerary's path
/// @throw std::invalid_argument, if the itinerary's source or destination is not in the path's
void setPath(SparseMatrix<bool> path);

/// @brief Get the itinerary's id
/// @return Id, The itinerary's id
Id id() const { return m_id; }
/// @brief Get the itinerary's destination
/// @return Id, The itinerary's destination
Id destination() const { return m_destination; }
Expand Down
Loading
Loading