@@ -191,7 +191,10 @@ namespace dsf::mobility {
191191 void resetTurnCounts ();
192192
193193 // / @brief Update the paths of the itineraries based on the given weight function
194- void updatePaths ();
194+ // / @param throw_on_empty If true, throws an exception if an itinerary has an empty path (default is true)
195+ // / If false, removes the itinerary with empty paths and the associated node from the origin/destination nodes
196+ // / @throws std::runtime_error if throw_on_empty is true and an itinerary has an empty path
197+ void updatePaths (bool const throw_on_empty = true );
195198 // / @brief Add agents uniformly on the road network
196199 // / @param nAgents The number of agents to add
197200 // / @param itineraryId The id of the itinerary to use (default is std::nullopt)
@@ -413,7 +416,6 @@ namespace dsf::mobility {
413416 for (auto const& [nodeId, weight] : this->m_destinationNodes) {
414417 m_itineraries.emplace(nodeId, std::make_unique<Itinerary>(nodeId, nodeId));
415418 }
416- // updatePaths();
417419 std::for_each(
418420 this->graph().edges().cbegin(),
419421 this->graph().edges().cend(),
@@ -470,12 +472,6 @@ namespace dsf::mobility {
470472
471473 auto const& path{this->graph().allPathsTo(
472474 pItinerary->destination(), m_weightFunction, m_weightTreshold)};
473- if (path.empty()) {
474- throw std::runtime_error(
475- std::format("No path found for itinerary {} with destination node {}",
476- pItinerary->id(),
477- pItinerary->destination()));
478- }
479475 pItinerary->setPath(path);
480476 auto const newSize{pItinerary->path().size()};
481477 if (oldSize > 0 && newSize != oldSize) {
@@ -534,18 +530,21 @@ namespace dsf::mobility {
534530 double cumulativeProbability = 0.0;
535531
536532 for (const auto outEdgeId : outgoingEdges) {
537- if (forbiddenTurns.contains(outEdgeId)) {
538- continue;
539- }
540-
541533 auto const& pStreetOut{this->graph().edge(outEdgeId)};
542534
543535 // Check if this is a valid path target for non-random agents
544- bool bIsPathTarget = true ;
536+ bool bIsPathTarget = false ;
545537 if (!pathTargets.empty()) {
546538 bIsPathTarget =
547539 std::find(pathTargets.cbegin(), pathTargets.cend(), pStreetOut->target()) !=
548540 pathTargets.cend();
541+ }
542+
543+ if (forbiddenTurns.contains(outEdgeId) && !bIsPathTarget) {
544+ continue;
545+ }
546+
547+ if (!pathTargets.empty()) {
549548 if (!this->m_errorProbability.has_value() && !bIsPathTarget) {
550549 continue;
551550 }
@@ -571,7 +570,7 @@ namespace dsf::mobility {
571570 if (previousNodeId.has_value() && pStreetOut->target() == previousNodeId.value()) {
572571 if (pNode->isRoundabout()) {
573572 probability *= U_TURN_PENALTY_FACTOR;
574- } else {
573+ } else if (!bIsPathTarget) {
575574 continue; // No U-turns allowed
576575 }
577576 }
@@ -582,11 +581,16 @@ namespace dsf::mobility {
582581
583582 // Select street based on weighted probabilities
584583 if (transitionProbabilities.empty()) {
585- spdlog::trace ("No valid transitions found for {} at {}", *pAgent, *pNode);
584+ spdlog::debug ("No valid transitions found for {} at {}", *pAgent, *pNode);
586585 return std::nullopt;
587586 }
588587 if (transitionProbabilities.size() == 1) {
589- return transitionProbabilities.cbegin()->first;
588+ auto const& onlyStreetId = transitionProbabilities.cbegin()->first;
589+ spdlog::debug("Only one valid transition for {} at {}: street {}",
590+ *pAgent,
591+ *pNode,
592+ onlyStreetId);
593+ return onlyStreetId;
590594 }
591595
592596 std::uniform_real_distribution<double> uniformDist{0., cumulativeProbability};
@@ -599,7 +603,10 @@ namespace dsf::mobility {
599603 }
600604 }
601605 // Return last one as fallback
602- return std::prev(transitionProbabilities.cend())->first;
606+ auto const fallbackStreetId = std::prev(transitionProbabilities.cend())->first;
607+ spdlog::debug(
608+ "Fallback selection for {} at {}: street {}", *pAgent, *pNode, fallbackStreetId);
609+ return fallbackStreetId;
603610 }
604611
605612 template <typename delay_t>
@@ -690,15 +697,15 @@ namespace dsf::mobility {
690697 if (pStreet->isStochastic() &&
691698 uniformDist(this->m_generator) >
692699 dynamic_cast<StochasticStreet&>(*pStreet).flowRate()) {
693- spdlog::debug ("Skipping due to flow rate {:.2f} < random value",
700+ spdlog::trace ("Skipping due to flow rate {:.2f} < random value",
694701 dynamic_cast<StochasticStreet&>(*pStreet).flowRate());
695702 continue;
696703 }
697704 if (i == std::ceil(transportCapacity) - 1) {
698705 double integral;
699706 double fractional = std::modf(transportCapacity, &integral);
700707 if (fractional != 0. && uniformDist(this->m_generator) > fractional) {
701- spdlog::debug ("Skipping due to fractional capacity {:.2f} < random value",
708+ spdlog::trace ("Skipping due to fractional capacity {:.2f} < random value",
702709 fractional);
703710 continue;
704711 }
@@ -710,7 +717,7 @@ namespace dsf::mobility {
710717 // Logger::debug("Taking temp agent");
711718 auto const& pAgentTemp{pStreet->queue(queueIndex).front()};
712719 if (pAgentTemp->freeTime() > this->time_step()) {
713- spdlog::debug ("Skipping due to time {} < free time {}",
720+ spdlog::trace ("Skipping due to time {} < free time {}",
714721 this->time_step(),
715722 pAgentTemp->freeTime());
716723 continue;
@@ -739,14 +746,14 @@ namespace dsf::mobility {
739746 pAgentTemp->setSpeed(0.);
740747 const auto& destinationNode{this->graph().node(pStreet->target())};
741748 if (destinationNode->isFull()) {
742- spdlog::debug ("Skipping due to full destination node {}", *destinationNode);
749+ spdlog::trace ("Skipping due to full destination node {}", *destinationNode);
743750 continue;
744751 }
745752 if (destinationNode->isTrafficLight()) {
746753 auto& tl = dynamic_cast<TrafficLight&>(*destinationNode);
747754 auto const direction{pStreet->laneMapping().at(queueIndex)};
748755 if (!tl.isGreen(pStreet->id(), direction)) {
749- spdlog::debug ("Skipping due to red light on street {} and direction {}",
756+ spdlog::trace ("Skipping due to red light on street {} and direction {}",
750757 pStreet->id(),
751758 directionToString.at(direction));
752759 continue;
@@ -1244,12 +1251,39 @@ namespace dsf::mobility {
12441251
12451252 template <typename delay_t >
12461253 requires (is_numeric_v<delay_t >)
1247- void RoadDynamics<delay_t>::updatePaths() {
1254+ void RoadDynamics<delay_t>::updatePaths(bool const throw_on_empty ) {
12481255 spdlog::debug (" Init updating paths..." );
1256+ tbb::concurrent_vector<Id> emptyItineraries;
12491257 tbb::parallel_for_each (
12501258 this ->itineraries ().cbegin (),
12511259 this ->itineraries ().cend (),
1252- [this ](auto const & pair) -> void { this ->m_updatePath (pair.second ); });
1260+ [this , throw_on_empty, &emptyItineraries](auto const & pair) -> void {
1261+ auto const & pItinerary{pair.second };
1262+ this ->m_updatePath (pItinerary);
1263+ if (pItinerary->empty ()) {
1264+ if (!throw_on_empty) {
1265+ spdlog::warn (" No path found for itinerary {} with destination node {}" ,
1266+ pItinerary->id (),
1267+ pItinerary->destination ());
1268+ emptyItineraries.push_back (pItinerary->id ());
1269+ return ;
1270+ }
1271+ throw std::runtime_error (
1272+ std::format (" No path found for itinerary {} with destination node {}" ,
1273+ pItinerary->id (),
1274+ pItinerary->destination ()));
1275+ }
1276+ });
1277+ if (!emptyItineraries.empty ()) {
1278+ spdlog::warn (" Removing {} itineraries with no valid path from the dynamics." ,
1279+ emptyItineraries.size ());
1280+ for (auto const & id : emptyItineraries) {
1281+ auto const destination = m_itineraries.at (id)->destination ();
1282+ m_destinationNodes.erase (destination);
1283+ m_originNodes.erase (destination);
1284+ m_itineraries.erase (id);
1285+ }
1286+ }
12531287 spdlog::debug (" End updating paths." );
12541288 }
12551289
0 commit comments