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
0 commit comments