Skip to content

Commit 8698c8b

Browse files
authored
Parallelize evolve function (#284)
* Init tbb::arena * Remove useless virtualization * Evolving one agent at time * Implement < operator for Agents * Simplified setStreetId * cose sensate * Cose meno sensate * Remove copy constructors from Edges * Working streets * Working streets * Working * Seems to compile * Working on... * Fix one roundabout test * Remove useless headers * Small refactor * Enqueued agents must have null speed * Fix node evoution * Last intersection test * Agent loop optimization * More tests! * Fix mean speed getter * More tests... * Some traffic light's tests * Finish tl tests * One more test * Finish dynamic tests * Revert Adj tests * Update action * Try to fix macos test * Update version * Fix macos tests * Fix examples * Refactor limits * Parallel * Update version * Add `saveMacroscopicObservables` as utility function * Fix * Fix * Fix * Add getters with references for I/O nodes * Purtroppo non va * io nodes * Revert "Purtroppo non va" This reverts commit 81da46a. * Add ghost agents to `saveMacroscopicObservables` function * Add optimized installation instruction on readme * Bugfix and add test * New compiler flags * Fix compilation * Let's try variant * Length * Uffa * Debug log * custom src weights * Revert "custom src weights" This reverts commit 2d7c0bb. * While? * AAAAAAAAAAAAAAAAAAAAAAAAA * Fix test * Update * Add RoadJunction and floating point transport capacity * Working * Better save for coils
1 parent f2b73de commit 8698c8b

File tree

5 files changed

+158
-111
lines changed

5 files changed

+158
-111
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
# DynamicalSystemFramework
2+
[![Latest Release](https://img.shields.io/github/v/release/physycom/DynamicalSystemFramework)](https://github.com/physycom/DynamicalSystemFramework/releases/latest)
23
[![Standard](https://img.shields.io/badge/C%2B%2B-20/23-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization)
34
[![TBB](https://img.shields.io/badge/TBB-C%2B%2B20%2F23-blue.svg)](https://github.com/oneapi-src/oneTBB)
45
[![codecov](https://codecov.io/gh/physycom/DynamicalSystemFramework/graph/badge.svg?token=JV53J6IUJ3)](https://codecov.io/gh/physycom/DynamicalSystemFramework)
5-
[![Latest Release](https://img.shields.io/github/v/release/physycom/DynamicalSystemFramework)](https://github.com/physycom/DynamicalSystemFramework/releases/latest)
6-
76

87
The aim of this project is to rework the original [Traffic Flow Dynamics Model](https://github.com/Grufoony/TrafficFlowDynamicsModel).
98
This rework consists of a full code rewriting, in order to implement more features (like *intersections*) and get advantage from the latest C++ updates.

src/dsm/headers/Dynamics.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <filesystem>
2626
#include <functional>
2727

28+
#include <tbb/tbb.h>
29+
2830
#include "Network.hpp"
2931
#include "../utility/Logger.hpp"
3032
#include "../utility/Typedef.hpp"
@@ -65,6 +67,7 @@ namespace dsm {
6567
network_t m_graph;
6668

6769
protected:
70+
tbb::task_arena m_taskArena;
6871
Time m_time;
6972
std::mt19937_64 m_generator;
7073

@@ -94,5 +97,6 @@ namespace dsm {
9497
if (seed.has_value()) {
9598
m_generator.seed(*seed);
9699
}
100+
m_taskArena.initialize();
97101
}
98102
}; // namespace dsm

src/dsm/headers/RoadDynamics.hpp

Lines changed: 139 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <exception>
2424
#include <fstream>
2525
#include <iomanip>
26+
#include <variant>
2627

2728
#include <tbb/tbb.h>
2829

@@ -50,7 +51,7 @@ namespace dsm {
5051
std::unordered_map<Id, std::array<unsigned long long, 4>> m_turnCounts;
5152
std::unordered_map<Id, std::array<long, 4>> m_turnMapping;
5253
std::unordered_map<Id, double> m_streetTails;
53-
std::vector<std::pair<double, double>> m_travelDTs;
54+
tbb::concurrent_vector<std::pair<double, double>> m_travelDTs;
5455
Time m_previousOptimizationTime, m_previousSpireTime;
5556

5657
private:
@@ -147,16 +148,20 @@ namespace dsm {
147148
/// @param nAgents The number of agents to add
148149
/// @param src_weights The weights of the source nodes
149150
/// @param dst_weights The weights of the destination nodes
151+
/// @param minNodeDistance The minimum distance between the source and destination nodes
150152
/// @throw std::invalid_argument If the source and destination nodes are the same
151153
template <typename TContainer>
152154
requires(std::is_same_v<TContainer, std::unordered_map<Id, double>> ||
153155
std::is_same_v<TContainer, std::map<Id, double>>)
154156
void addAgentsRandomly(Size nAgents,
155157
const TContainer& src_weights,
156158
const TContainer& dst_weights,
157-
const size_t minNodeDistance = 0);
159+
const std::variant<std::monostate, size_t, double>
160+
minNodeDistance = std::monostate{});
158161

159-
void addAgentsRandomly(Size nAgents, const size_t minNodeDistance = 0);
162+
void addAgentsRandomly(Size nAgents,
163+
const std::variant<std::monostate, size_t, double>
164+
minNodeDistance = std::monostate{});
160165

161166
/// @brief Add an agent to the simulation
162167
/// @param agent std::unique_ptr to the agent
@@ -798,19 +803,18 @@ namespace dsm {
798803
template <typename TContainer>
799804
requires(std::is_same_v<TContainer, std::unordered_map<Id, double>> ||
800805
std::is_same_v<TContainer, std::map<Id, double>>)
801-
void RoadDynamics<delay_t>::addAgentsRandomly(Size nAgents,
802-
const TContainer& src_weights,
803-
const TContainer& dst_weights,
804-
const size_t minNodeDistance) {
806+
void RoadDynamics<delay_t>::addAgentsRandomly(
807+
Size nAgents,
808+
const TContainer& src_weights,
809+
const TContainer& dst_weights,
810+
const std::variant<std::monostate, size_t, double> minNodeDistance) {
805811
auto const& nSources{src_weights.size()};
806812
auto const& nDestinations{dst_weights.size()};
807813
Logger::debug(
808-
std::format("Init addAgentsRandomly for {} agents from {} nodes to {} nodes with "
809-
"minNodeDistance {}",
814+
std::format("Init addAgentsRandomly for {} agents from {} nodes to {} nodes.",
810815
nAgents,
811816
nSources,
812-
dst_weights.size(),
813-
minNodeDistance));
817+
dst_weights.size()));
814818
if (nSources == 1 && nDestinations == 1 &&
815819
src_weights.begin()->first == dst_weights.begin()->first) {
816820
throw std::invalid_argument(Logger::buildExceptionMessage(
@@ -871,10 +875,23 @@ namespace dsm {
871875
if (this->itineraries().at(id)->path()->getRow(srcId).empty()) {
872876
continue;
873877
}
874-
if (nDestinations > 1 && minNodeDistance > 0) {
875-
// NOTE: Result must have a value in this case, so we can use value() as sort-of assertion
878+
if (std::holds_alternative<size_t>(minNodeDistance)) {
879+
auto const minDistance{std::get<size_t>(minNodeDistance)};
876880
if (this->graph().shortestPath(srcId, id).value().path().size() <
877-
minNodeDistance) {
881+
minDistance) {
882+
continue;
883+
}
884+
} else if (std::holds_alternative<double>(minNodeDistance)) {
885+
auto const minDistance{std::get<double>(minNodeDistance)};
886+
if (this->graph()
887+
.shortestPath(srcId, id, weight_functions::streetLength)
888+
.value()
889+
.distance() < minDistance) {
890+
Logger::debug(
891+
std::format("Skipping node {} because the distance from the source "
892+
"is less than {}",
893+
id,
894+
minDistance));
878895
continue;
879896
}
880897
}
@@ -901,8 +918,8 @@ namespace dsm {
901918

902919
template <typename delay_t>
903920
requires(is_numeric_v<delay_t>)
904-
void RoadDynamics<delay_t>::addAgentsRandomly(Size nAgents,
905-
const size_t minNodeDistance) {
921+
void RoadDynamics<delay_t>::addAgentsRandomly(
922+
Size nAgents, const std::variant<std::monostate, size_t, double> minNodeDistance) {
906923
std::unordered_map<Id, double> src_weights, dst_weights;
907924
for (auto const& id : this->graph().inputNodes()) {
908925
src_weights[id] = 1.;
@@ -967,99 +984,114 @@ namespace dsm {
967984
bool const bUpdateData =
968985
m_dataUpdatePeriod.has_value() && this->time() % m_dataUpdatePeriod.value() == 0;
969986
auto const N{this->graph().nNodes()};
970-
std::for_each(
971-
this->graph().nodes().cbegin(),
972-
this->graph().nodes().cend(),
973-
[&](const auto& pair) {
974-
for (auto const& sourceId :
975-
this->graph().adjacencyMatrix().getCol(pair.first)) {
976-
auto const streetId = sourceId * N + pair.first;
977-
auto const& pStreet{this->graph().edge(streetId)};
978-
if (bUpdateData) {
979-
m_streetTails[streetId] += pStreet->nExitingAgents();
980-
}
981-
m_evolveStreet(pStreet, reinsert_agents);
982-
983-
while (!pStreet->movingAgents().empty()) {
984-
auto const& pAgent{pStreet->movingAgents().top()};
985-
if (pAgent->freeTime() < this->time()) {
986-
break;
987-
}
988-
pAgent->setSpeed(0.);
989-
auto const nLanes = pStreet->nLanes();
990-
bool bArrived{false};
991-
if (!pAgent->isRandom()) {
992-
if (this->itineraries().at(pAgent->itineraryId())->destination() ==
993-
pStreet->target()) {
994-
pAgent->updateItinerary();
995-
}
996-
if (this->itineraries().at(pAgent->itineraryId())->destination() ==
997-
pStreet->target()) {
998-
bArrived = true;
987+
auto const numNodes{this->graph().nNodes()};
988+
const auto& nodes =
989+
this->graph().nodes(); // assuming a container with contiguous indices
990+
const unsigned int concurrency = std::thread::hardware_concurrency();
991+
// Calculate a grain size to partition the nodes into roughly "concurrency" blocks
992+
const size_t grainSize = std::max(size_t(1), numNodes / concurrency);
993+
this->m_taskArena.execute([&] {
994+
tbb::parallel_for(
995+
tbb::blocked_range<size_t>(0, numNodes, grainSize),
996+
[&](const tbb::blocked_range<size_t>& range) {
997+
for (size_t i = range.begin(); i != range.end(); ++i) {
998+
const auto& pNode = nodes.at(i);
999+
for (auto const& sourceId :
1000+
this->graph().adjacencyMatrix().getCol(pNode->id())) {
1001+
auto const streetId = sourceId * N + pNode->id();
1002+
auto const& pStreet{this->graph().edge(streetId)};
1003+
if (bUpdateData) {
1004+
m_streetTails[streetId] += pStreet->nExitingAgents();
9991005
}
1000-
}
1001-
if (bArrived) {
1002-
std::uniform_int_distribution<size_t> laneDist{
1003-
0, static_cast<size_t>(nLanes - 1)};
1004-
pStreet->enqueue(laneDist(this->m_generator));
1005-
continue;
1006-
}
1007-
auto const nextStreetId =
1008-
this->m_nextStreetId(pAgent, pStreet->target(), pStreet->id());
1009-
auto const& pNextStreet{this->graph().edge(nextStreetId)};
1010-
pAgent->setNextStreetId(nextStreetId);
1011-
if (nLanes == 1) {
1012-
pStreet->enqueue(0);
1013-
continue;
1014-
}
1015-
auto const deltaAngle{pNextStreet->deltaAngle(pStreet->angle())};
1016-
if (std::abs(deltaAngle) < std::numbers::pi) {
1017-
// Lanes are counted as 0 is the far right lane
1018-
if (std::abs(deltaAngle) < std::numbers::pi / 4) {
1019-
std::vector<double> weights;
1020-
for (auto const& queue : pStreet->exitQueues()) {
1021-
weights.push_back(1. / (queue.size() + 1));
1006+
m_evolveStreet(pStreet, reinsert_agents);
1007+
1008+
while (!pStreet->movingAgents().empty()) {
1009+
auto const& pAgent{pStreet->movingAgents().top()};
1010+
if (pAgent->freeTime() < this->time()) {
1011+
break;
10221012
}
1023-
// If all weights are the same, make the last 0
1024-
if (std::all_of(weights.begin(), weights.end(), [&](double w) {
1025-
return std::abs(w - weights.front()) <
1026-
std::numeric_limits<double>::epsilon();
1027-
})) {
1028-
weights.back() = 0.;
1029-
if (nLanes > 2) {
1030-
weights.front() = 0.;
1013+
pAgent->setSpeed(0.);
1014+
auto const nLanes = pStreet->nLanes();
1015+
bool bArrived{false};
1016+
if (!pAgent->isRandom()) {
1017+
if (this->itineraries().at(pAgent->itineraryId())->destination() ==
1018+
pStreet->target()) {
1019+
pAgent->updateItinerary();
1020+
}
1021+
if (this->itineraries().at(pAgent->itineraryId())->destination() ==
1022+
pStreet->target()) {
1023+
bArrived = true;
10311024
}
10321025
}
1033-
// Normalize the weights
1034-
auto const sum = std::accumulate(weights.begin(), weights.end(), 0.);
1035-
for (auto& w : weights) {
1036-
w /= sum;
1026+
if (bArrived) {
1027+
std::uniform_int_distribution<size_t> laneDist{
1028+
0, static_cast<size_t>(nLanes - 1)};
1029+
pStreet->enqueue(laneDist(this->m_generator));
1030+
continue;
1031+
}
1032+
auto const nextStreetId =
1033+
this->m_nextStreetId(pAgent, pStreet->target(), pStreet->id());
1034+
auto const& pNextStreet{this->graph().edge(nextStreetId)};
1035+
pAgent->setNextStreetId(nextStreetId);
1036+
if (nLanes == 1) {
1037+
pStreet->enqueue(0);
1038+
continue;
1039+
}
1040+
auto const deltaAngle{pNextStreet->deltaAngle(pStreet->angle())};
1041+
if (std::abs(deltaAngle) < std::numbers::pi) {
1042+
// Lanes are counted as 0 is the far right lane
1043+
if (std::abs(deltaAngle) < std::numbers::pi / 4) {
1044+
std::vector<double> weights;
1045+
for (auto const& queue : pStreet->exitQueues()) {
1046+
weights.push_back(1. / (queue.size() + 1));
1047+
}
1048+
// If all weights are the same, make the last 0
1049+
if (std::all_of(weights.begin(), weights.end(), [&](double w) {
1050+
return std::abs(w - weights.front()) <
1051+
std::numeric_limits<double>::epsilon();
1052+
})) {
1053+
weights.back() = 0.;
1054+
if (nLanes > 2) {
1055+
weights.front() = 0.;
1056+
}
1057+
}
1058+
// Normalize the weights
1059+
auto const sum =
1060+
std::accumulate(weights.begin(), weights.end(), 0.);
1061+
for (auto& w : weights) {
1062+
w /= sum;
1063+
}
1064+
std::discrete_distribution<size_t> laneDist{weights.begin(),
1065+
weights.end()};
1066+
pStreet->enqueue(laneDist(this->m_generator));
1067+
} else if (deltaAngle < 0.) { // Right
1068+
pStreet->enqueue(0); // Always the first lane
1069+
} else { // Left (deltaAngle > 0.)
1070+
pStreet->enqueue(nLanes - 1); // Always the last lane
1071+
}
1072+
} else { // U turn
1073+
pStreet->enqueue(nLanes - 1); // Always the last lane
10371074
}
1038-
std::discrete_distribution<size_t> laneDist{weights.begin(),
1039-
weights.end()};
1040-
pStreet->enqueue(laneDist(this->m_generator));
1041-
} else if (deltaAngle < 0.) { // Right
1042-
pStreet->enqueue(0); // Always the first lane
1043-
} else { // Left (deltaAngle > 0.)
1044-
pStreet->enqueue(nLanes - 1); // Always the last lane
10451075
}
1046-
} else { // U turn
1047-
pStreet->enqueue(nLanes - 1); // Always the last lane
10481076
}
10491077
}
1050-
}
1051-
});
1078+
});
1079+
});
10521080
Logger::debug("Pre-nodes");
10531081
// Move transport capacity agents from each node
1054-
std::for_each(this->graph().nodes().cbegin(),
1055-
this->graph().nodes().cend(),
1056-
[&](const auto& pair) {
1057-
m_evolveNode(pair.second);
1058-
if (pair.second->isTrafficLight()) {
1059-
auto& tl = dynamic_cast<TrafficLight&>(*pair.second);
1060-
++tl;
1061-
}
1062-
});
1082+
this->m_taskArena.execute([&] {
1083+
tbb::parallel_for(tbb::blocked_range<size_t>(0, numNodes, grainSize),
1084+
[&](const tbb::blocked_range<size_t>& range) {
1085+
for (size_t i = range.begin(); i != range.end(); ++i) {
1086+
const auto& pNode = nodes.at(i);
1087+
m_evolveNode(pNode);
1088+
if (pNode->isTrafficLight()) {
1089+
auto& tl = dynamic_cast<TrafficLight&>(*pNode);
1090+
++tl;
1091+
}
1092+
}
1093+
});
1094+
});
10631095
// cycle over agents and update their times
10641096
std::uniform_int_distribution<Id> nodeDist{
10651097
0, static_cast<Id>(this->graph().nNodes() - 1)};
@@ -1519,8 +1551,12 @@ namespace dsm {
15191551
}
15201552
if (bEmptyFile) {
15211553
file << "time";
1522-
for (auto const& [streetId, _] : this->graph().edges()) {
1523-
file << separator << streetId;
1554+
for (auto const& [streetId, pStreet] : this->graph().edges()) {
1555+
if (!pStreet->isSpire()) {
1556+
continue;
1557+
}
1558+
auto& spire = dynamic_cast<SpireStreet&>(*pStreet);
1559+
file << separator << spire.code();
15241560
}
15251561
file << std::endl;
15261562
}
@@ -1533,8 +1569,8 @@ namespace dsm {
15331569
} else {
15341570
value = dynamic_cast<SpireStreet&>(*pStreet).outputCounts(reset);
15351571
}
1572+
file << separator << value;
15361573
}
1537-
file << separator << value;
15381574
}
15391575
file << std::endl;
15401576
file.close();
@@ -1645,6 +1681,7 @@ namespace dsm {
16451681
file << speed.mean << separator << speed.std << std::endl;
16461682
m_travelDTs.clear();
16471683
}
1684+
m_travelDTs.clear();
16481685
file.close();
16491686
}
16501687
}; // namespace dsm

src/dsm/sources/RoadNetwork.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,8 +642,13 @@ namespace dsm {
642642
file << ";traffic_light";
643643
} else if (pNode->isRoundabout()) {
644644
file << ";roundabout";
645+
} else if (std::find(m_inputNodes.begin(), m_inputNodes.end(), nodeId) !=
646+
m_inputNodes.end() ||
647+
std::find(m_outputNodes.begin(), m_outputNodes.end(), nodeId) !=
648+
m_outputNodes.end()) {
649+
file << ";io";
645650
} else {
646-
file << ";intersection";
651+
file << ";";
647652
}
648653
file << '\n';
649654
}

0 commit comments

Comments
 (0)