@@ -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 }
@@ -570,7 +569,7 @@ namespace dsf::mobility {
570569 if (previousNodeId.has_value() && pStreetOut->target() == previousNodeId.value()) {
571570 if (pNode->isRoundabout()) {
572571 probability *= U_TURN_PENALTY_FACTOR;
573- } else {
572+ } else if (!bIsPathTarget) {
574573 continue; // No U-turns allowed
575574 }
576575 }
@@ -581,11 +580,16 @@ namespace dsf::mobility {
581580
582581 // Select street based on weighted probabilities
583582 if (transitionProbabilities.empty()) {
584- spdlog::trace ("No valid transitions found for {} at {}", *pAgent, *pNode);
583+ spdlog::debug ("No valid transitions found for {} at {}", *pAgent, *pNode);
585584 return std::nullopt;
586585 }
587586 if (transitionProbabilities.size() == 1) {
588- return transitionProbabilities.cbegin()->first;
587+ auto const& onlyStreetId = transitionProbabilities.cbegin()->first;
588+ spdlog::debug("Only one valid transition for {} at {}: street {}",
589+ *pAgent,
590+ *pNode,
591+ onlyStreetId);
592+ return onlyStreetId;
589593 }
590594
591595 std::uniform_real_distribution<double> uniformDist{0., cumulativeProbability};
@@ -598,7 +602,10 @@ namespace dsf::mobility {
598602 }
599603 }
600604 // Return last one as fallback
601- return std::prev(transitionProbabilities.cend())->first;
605+ auto const fallbackStreetId = std::prev(transitionProbabilities.cend())->first;
606+ spdlog::debug(
607+ "Fallback selection for {} at {}: street {}", *pAgent, *pNode, fallbackStreetId);
608+ return fallbackStreetId;
602609 }
603610
604611 template <typename delay_t>
@@ -689,15 +696,15 @@ namespace dsf::mobility {
689696 if (pStreet->isStochastic() &&
690697 uniformDist(this->m_generator) >
691698 dynamic_cast<StochasticStreet&>(*pStreet).flowRate()) {
692- spdlog::debug ("Skipping due to flow rate {:.2f} < random value",
699+ spdlog::trace ("Skipping due to flow rate {:.2f} < random value",
693700 dynamic_cast<StochasticStreet&>(*pStreet).flowRate());
694701 continue;
695702 }
696703 if (i == std::ceil(transportCapacity) - 1) {
697704 double integral;
698705 double fractional = std::modf(transportCapacity, &integral);
699706 if (fractional != 0. && uniformDist(this->m_generator) > fractional) {
700- spdlog::debug ("Skipping due to fractional capacity {:.2f} < random value",
707+ spdlog::trace ("Skipping due to fractional capacity {:.2f} < random value",
701708 fractional);
702709 continue;
703710 }
@@ -709,7 +716,7 @@ namespace dsf::mobility {
709716 // Logger::debug("Taking temp agent");
710717 auto const& pAgentTemp{pStreet->queue(queueIndex).front()};
711718 if (pAgentTemp->freeTime() > this->time_step()) {
712- spdlog::debug ("Skipping due to time {} < free time {}",
719+ spdlog::trace ("Skipping due to time {} < free time {}",
713720 this->time_step(),
714721 pAgentTemp->freeTime());
715722 continue;
@@ -738,14 +745,14 @@ namespace dsf::mobility {
738745 pAgentTemp->setSpeed(0.);
739746 const auto& destinationNode{this->graph().node(pStreet->target())};
740747 if (destinationNode->isFull()) {
741- spdlog::debug ("Skipping due to full destination node {}", *destinationNode);
748+ spdlog::trace ("Skipping due to full destination node {}", *destinationNode);
742749 continue;
743750 }
744751 if (destinationNode->isTrafficLight()) {
745752 auto& tl = dynamic_cast<TrafficLight&>(*destinationNode);
746753 auto const direction{pStreet->laneMapping().at(queueIndex)};
747754 if (!tl.isGreen(pStreet->id(), direction)) {
748- spdlog::debug ("Skipping due to red light on street {} and direction {}",
755+ spdlog::trace ("Skipping due to red light on street {} and direction {}",
749756 pStreet->id(),
750757 directionToString.at(direction));
751758 continue;
@@ -1243,12 +1250,39 @@ namespace dsf::mobility {
12431250
12441251 template <typename delay_t >
12451252 requires (is_numeric_v<delay_t >)
1246- void RoadDynamics<delay_t>::updatePaths() {
1253+ void RoadDynamics<delay_t>::updatePaths(bool const throw_on_empty ) {
12471254 spdlog::debug (" Init updating paths..." );
1255+ tbb::concurrent_vector<Id> emptyItineraries;
12481256 tbb::parallel_for_each (
12491257 this ->itineraries ().cbegin (),
12501258 this ->itineraries ().cend (),
1251- [this ](auto const & pair) -> void { this ->m_updatePath (pair.second ); });
1259+ [this , throw_on_empty, &emptyItineraries](auto const & pair) -> void {
1260+ auto const & pItinerary{pair.second };
1261+ this ->m_updatePath (pItinerary);
1262+ if (pItinerary->empty ()) {
1263+ if (!throw_on_empty) {
1264+ spdlog::warn (" No path found for itinerary {} with destination node {}" ,
1265+ pItinerary->id (),
1266+ pItinerary->destination ());
1267+ emptyItineraries.push_back (pItinerary->id ());
1268+ return ;
1269+ }
1270+ throw std::runtime_error (
1271+ std::format (" No path found for itinerary {} with destination node {}" ,
1272+ pItinerary->id (),
1273+ pItinerary->destination ()));
1274+ }
1275+ });
1276+ if (!emptyItineraries.empty ()) {
1277+ spdlog::warn (" Removing {} itineraries with no valid path from the dynamics." ,
1278+ emptyItineraries.size ());
1279+ for (auto const & id : emptyItineraries) {
1280+ auto const destination = m_itineraries.at (id)->destination ();
1281+ m_destinationNodes.erase (destination);
1282+ m_originNodes.erase (destination);
1283+ m_itineraries.erase (id);
1284+ }
1285+ }
12521286 spdlog::debug (" End updating paths." );
12531287 }
12541288
0 commit comments