LCOV - code coverage report
Current view: top level - src/utils/router - AFCentralizedSPTree.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 0.0 % 104 0
Test Date: 2025-11-14 15:59:05 Functions: 0.0 % 5 0

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2012-2025 German Aerospace Center (DLR) and others.
       4              : // This program and the accompanying materials are made available under the
       5              : // terms of the Eclipse Public License 2.0 which is available at
       6              : // https://www.eclipse.org/legal/epl-2.0/
       7              : // This Source Code may also be made available under the following Secondary
       8              : // Licenses when the conditions for such availability set forth in the Eclipse
       9              : // Public License 2.0 are satisfied: GNU General Public License, version 2
      10              : // or later which is available at
      11              : // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
      12              : // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
      13              : /****************************************************************************/
      14              : /// @file    AFCentralizedSPTree.h
      15              : /// @author  Ruediger Ebendt
      16              : /// @date    01.12.2023
      17              : ///
      18              : // Class for a label-correcting algorithm calculating multiple shortest path
      19              : // trees at once (centralized shortest path tree, cf. Hilger et al.)
      20              : // Used for setting the arc flags for the arc flag router
      21              : // @note Intended use is on a backward graph with flipped edges
      22              : /****************************************************************************/
      23              : #pragma once
      24              : #include <config.h>
      25              : 
      26              : #include <cassert>
      27              : #include <string>
      28              : #include <functional>
      29              : #include <vector>
      30              : #include <set>
      31              : #include <limits>
      32              : #include <algorithm>
      33              : #include <iterator>
      34              : #include <map>
      35              : #include <iostream>
      36              : #include <memory>
      37              : #include <stdexcept>
      38              : #include <cstddef>
      39              : #include <utils/common/MsgHandler.h>
      40              : #include <utils/common/StringTokenizer.h>
      41              : #include <utils/common/StringUtils.h>
      42              : #include <utils/common/StdDefs.h>
      43              : #include <utils/common/ToString.h>
      44              : #include <utils/iodevices/OutputDevice.h>
      45              : #include <utils/common/SUMOTime.h>
      46              : #include "SUMOAbstractRouter.h"
      47              : #include "KDTreePartition.h"
      48              : #include "AFInfo.h"
      49              : 
      50              : //#define CSPT_WRITE_QGIS_FILTERS
      51              : //#define CSPT_DEBUG_LEVEL_0
      52              : 
      53              : template<class E, class N, class V>
      54              : class AFCentralizedSPTree {
      55              : public:
      56              :     typedef typename KDTreePartition<E, N, V>::Cell Cell;
      57              :     typedef typename AFInfo<E>::ArcInfo ArcInfo;
      58              : 
      59              :     class EdgeInfoComparator {
      60              :     public:
      61              :         /** @brief Constructor
      62              :          * @param[in] arcInfos The arc informations
      63              :          */
      64            0 :         EdgeInfoComparator(std::vector<ArcInfo*>& arcInfos) : myArcInfos(arcInfos) {
      65              :         }
      66              : 
      67              :         /** @brief Comparing method
      68              :          * @param[in] edgeInfo1 The first edge information
      69              :          * @param[in] edgeInfo2 The second edge information
      70              :          * @return true iff arc info key of the first edge is greater than that of the second
      71              :          * @note In case of ties: returns true iff the numerical id of the first edge is greater that that of the second
      72              :          */
      73              :         bool operator()(const typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo1,
      74              :                         const typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo2) const {
      75            0 :             int index1 = edgeInfo1->edge->getNumericalID();
      76            0 :             int index2 = edgeInfo2->edge->getNumericalID();
      77            0 :             ArcInfo* arcInfo1 = myArcInfos[index1];
      78            0 :             ArcInfo* arcInfo2 = myArcInfos[index2];
      79            0 :             double key1 = arcInfo1->key;
      80            0 :             double key2 = arcInfo2->key;
      81              : 
      82            0 :             if (key1 == key2) { // tie
      83            0 :                 return index1 > index2;
      84              :             }
      85            0 :             return key1 > key2;
      86              :         }
      87              :     private:
      88              :         std::vector<ArcInfo*>& myArcInfos;
      89              :     };
      90              : 
      91              :     /** @brief Constructor
      92              :      * @param[in] edges The edges
      93              :      * @param[in] arcInfos The arc informations (for arc flag routing)
      94              :      * @param[in] unbuildIsWarning The flag indicating whether network unbuilds should issue warnings or errors
      95              :      * @param[in] effortProvider The effort provider
      96              :      * @param[in] havePermissions The boolean flag indicating whether edge permissions need to be considered or not
      97              :      * @param[in] haveRestrictions The boolean flag indicating whether edge restrictions need to be considered or not
      98              :      */
      99            0 :     AFCentralizedSPTree(const std::vector<E*>& edges, std::vector<ArcInfo*>& arcInfos, bool unbuildIsWarning,
     100              :                         SUMOAbstractRouter<E, V>* effortProvider,
     101              :                         const bool havePermissions = false, const bool haveRestrictions = false) :
     102            0 :         myArcInfos(arcInfos),
     103            0 :         myHavePermissions(havePermissions),
     104            0 :         myHaveRestrictions(haveRestrictions),
     105            0 :         myErrorMsgHandler(unbuildIsWarning ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
     106            0 :         myEffortProvider(effortProvider),
     107            0 :         myMaxSpeed(NUMERICAL_EPS) {
     108            0 :         for (const E* const edge : edges) {
     109            0 :             myEdgeInfos.push_back(typename SUMOAbstractRouter<E, V>::EdgeInfo(edge));
     110            0 :             myMaxSpeed = MAX2(myMaxSpeed, edge->getSpeedLimit() * MAX2(1.0, edge->getLengthGeometryFactor()));
     111              :         }
     112            0 :         myComparator = new EdgeInfoComparator(myArcInfos);
     113            0 :     }
     114              : 
     115              :     /// @brief Destructor
     116            0 :     ~AFCentralizedSPTree() {
     117            0 :         delete myComparator;
     118            0 :     }
     119              : 
     120              :     /** @brief Returns true iff driving the given vehicle on the given edge is prohibited
     121              :      * @param[in] edge The edge
     122              :      * @param[in] vehicle The vehicle
     123              :      * @return true iff driving the given vehicle on the given edge is prohibited
     124              :      */
     125            0 :     bool isProhibited(const E* const edge, const V* const vehicle) const {
     126            0 :         return (myHavePermissions && edge->prohibits(vehicle)) || (myHaveRestrictions && edge->restricts(vehicle));
     127              :     }
     128              : 
     129              :     /** @brief Computes a shortest path tree for each boundary edge of the given cell, returns true iff this was successful
     130              :      * @note This is done for all such shortest path trees at once (i.e., a centralized shortest path tree is computed, see Hilger et al.)
     131              :      * @param[in] msTime The start time of the paths/routes in milliseconds
     132              :      * @param[in] cell The cell as a part of a k-d tree partition of the network
     133              :      * @param[in] vehicle The vehicle
     134              :      * @param[in] incomingEdgesOfOutgoingBoundaryEdges Maps each outgoing boundary edge to its incoming edges
     135              :      * @param[in] silent The boolean flag indicating whether the method stays silent or puts out messages
     136              :      * @return true iff the centralized shortest path tree could successfully be calculated
     137              :      */
     138            0 :     bool computeCentralizedSPTree(SUMOTime msTime, const Cell* cell, const V* const vehicle,
     139              :                                   const std::map<const E*, std::vector<const E*>>& incomingEdgesOfOutgoingBoundaryEdges,
     140              :                                   bool silent = false) {
     141              :         assert(cell != nullptr);
     142              :         const std::unordered_set<const E*>& fromEdges = cell->getOutgoingBoundaryEdges();
     143            0 :         if (fromEdges.empty()) { // nothing to do here
     144              :             return false;
     145              :         }
     146            0 :         double length = 0.; // dummy for the via edge cost update
     147            0 :         const SUMOVehicleClass vClass = vehicle == 0 ? SVC_IGNORING : vehicle->getVClass();
     148            0 :         std::vector<const E*> fromEdgesAsVector(fromEdges.begin(), fromEdges.end());
     149            0 :         init(fromEdgesAsVector, vehicle, msTime);
     150              :         size_t numberOfVisitedFromEdges = 0;
     151              :         bool minIsFromEdge = false;
     152              : #ifdef CSPT_DEBUG_LEVEL_0
     153              :         size_t numberOfTouchedSupercellEdges = 0;
     154              :         int num_visited = 0;
     155              : #endif
     156              : #ifdef _DEBUG
     157              :         const Cell* supercell = cell->getSupercell();
     158              : #endif
     159              :         const bool mayRevisit = true;
     160              : 
     161            0 :         while (!myFrontierList.empty()) {
     162              : #ifdef CSPT_DEBUG_LEVEL_0
     163              :             num_visited += 1;
     164              : #endif
     165              :             // use the edge with the minimal length
     166            0 :             typename SUMOAbstractRouter<E, V>::EdgeInfo* minimumInfo = myFrontierList.front();
     167            0 :             const E* const minEdge = minimumInfo->edge;
     168              :             assert(!minEdge->isInternal());
     169            0 :             ArcInfo* minimumArcInfo = myArcInfos[minEdge->getNumericalID()];
     170            0 :             if (minimumInfo->visited || numberOfVisitedFromEdges < fromEdges.size()) {
     171              :                 minIsFromEdge = incomingEdgesOfOutgoingBoundaryEdges.find(minEdge) != incomingEdgesOfOutgoingBoundaryEdges.end();
     172              :             } else {
     173              :                 minIsFromEdge = false;
     174              :             }
     175            0 :             if (minIsFromEdge) {
     176            0 :                 if (numberOfVisitedFromEdges < fromEdges.size()) {
     177            0 :                     numberOfVisitedFromEdges++;
     178              :                 }
     179              :             }
     180              :             assert(incomingEdgesOfOutgoingBoundaryEdges.size() == fromEdges.size());
     181              :             assert(minimumArcInfo->key != std::numeric_limits<double>::max() && minimumArcInfo->key != UNREACHABLE);
     182              :             size_t index;
     183              : #ifdef CSPT_DEBUG_LEVEL_0
     184              :             if (supercell->contains(minEdge->getToJunction())) {
     185              :                 // minEdge is a supercell edge (we check for the to-node since we work on a backward graph)
     186              :                 if (!minimumArcInfo->touched) {
     187              :                     numberOfTouchedSupercellEdges++;
     188              :                     minimumArcInfo->touched = true;
     189              :                 }
     190              :             }
     191              : #endif
     192              : #ifdef CSPT_DEBUG_LEVEL_0
     193              :             if (num_visited % 500 == 0) {
     194              :                 std::cout << "num_visited: " << num_visited << ", numberOfTouchedSupercellEdges: " << numberOfTouchedSupercellEdges
     195              :                           << ", minimumArcInfo->key: " << minimumArcInfo->key << std::endl;
     196              :             }
     197              : #endif
     198            0 :             std::pop_heap(myFrontierList.begin(), myFrontierList.end(), *myComparator);
     199              :             myFrontierList.pop_back();
     200            0 :             myFound.push_back(minimumInfo);
     201            0 :             minimumInfo->visited = true;
     202            0 :             const double effortDelta = myEffortProvider->getEffort(minEdge, vehicle, minimumInfo->leaveTime);
     203            0 :             const double leaveTime = minimumInfo->leaveTime + myEffortProvider->getTravelTime(minEdge, vehicle, minimumInfo->leaveTime, effortDelta);
     204              : 
     205              :             // check all ways from the edge with the minimal length
     206            0 :             for (const std::pair<const E*, const E*>& follower : minEdge->getViaSuccessors(vClass)) {
     207              :                 assert(!follower.first->isInternal());
     208              :                 bool wasPushedToHeap = false;
     209            0 :                 auto& followerInfo = myEdgeInfos[follower.first->getNumericalID()];
     210            0 :                 ArcInfo* followerArcInfo = myArcInfos[follower.first->getNumericalID()];
     211              :                 // check whether it can be used
     212            0 :                 if (followerInfo.prohibited || isProhibited(follower.first, vehicle)) {
     213            0 :                     if (!silent) {
     214            0 :                         myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on source edge '" + followerInfo.edge->getID() + "'.");
     215              :                     }
     216            0 :                     continue;
     217              :                 }
     218              : 
     219            0 :                 if (followerArcInfo->effortsToBoundaryNodes.empty()) { // non-initialized non-supercell edge
     220              :                     assert(!supercell->contains(follower.first->getToJunction()));
     221            0 :                     std::fill_n(std::back_inserter(followerArcInfo->effortsToBoundaryNodes),
     222              :                                 minimumArcInfo->effortsToBoundaryNodes.size(), std::numeric_limits<double>::max());
     223              :                 }
     224              : 
     225              :                 double key = std::numeric_limits<double>::max();
     226              :                 assert(followerArcInfo->effortsToBoundaryNodes.size() == minimumArcInfo->effortsToBoundaryNodes.size());
     227              :                 bool hasImproved = false;
     228              :                 // loop over all boundary nodes
     229            0 :                 for (index = 0; index < followerArcInfo->effortsToBoundaryNodes.size(); index++) {
     230              :                     // is minEdge a from-edge (i.e., an outgoing boundary edge of the passed cell),
     231              :                     // and 'index' not the index of its from-node (i.e., not of the 'own' boundary node)?
     232            0 :                     if (minIsFromEdge && (incomingEdgesOfOutgoingBoundaryEdges.at(minEdge))[index]) {
     233              :                         // if yes, assign the successor edges of the incoming edge of minEdge on the shortest route from
     234              :                         // the boundary node with the index 'index' to followersOfIncomingEdge
     235              :                         assert((incomingEdgesOfOutgoingBoundaryEdges.at(minEdge)).size()
     236              :                                == followerArcInfo->effortsToBoundaryNodes.size());
     237              :                         const std::vector<std::pair<const E*, const E*>>& followersOfIncomingEdge
     238            0 :                                 = ((incomingEdgesOfOutgoingBoundaryEdges.at(minEdge))[index])->getViaSuccessors(vClass);
     239              :                         // is the current follower among said successor edges?
     240              :                         bool turningAllowed = false;
     241            0 :                         for (std::pair<const E*, const E*> followerOfIncomingEdge : followersOfIncomingEdge) {
     242            0 :                             if (follower.first == followerOfIncomingEdge.first) {
     243              :                                 turningAllowed = true;
     244              :                                 break;
     245              :                             }
     246              :                         }
     247              :                         // if not, then turning from said incoming edge to the current follower is not allowed
     248              :                         // and we mustn't propagate the distance to said boundary node
     249              :                         // instead simply skip this follower
     250            0 :                         if (!turningAllowed) {
     251            0 :                             continue;
     252              :                         }
     253              :                     }
     254              :                     // propagate distances to other boundary nodes
     255            0 :                     double effortToFollower = minimumArcInfo->effortsToBoundaryNodes[index] == UNREACHABLE ?
     256              :                                               UNREACHABLE : minimumArcInfo->effortsToBoundaryNodes[index] + effortDelta;
     257            0 :                     if (effortToFollower == UNREACHABLE) {
     258            0 :                         continue; // no need to consider this follower
     259              :                     }
     260            0 :                     double time = leaveTime;
     261            0 :                     myEffortProvider->updateViaEdgeCost(follower.second, vehicle, time, effortToFollower, length);
     262            0 :                     if (effortToFollower < key) {
     263              :                         key = effortToFollower;
     264              :                     }
     265            0 :                     const double oldEffort = followerArcInfo->effortsToBoundaryNodes[index];
     266            0 :                     if (oldEffort != std::numeric_limits<double>::max()) {
     267              :                         wasPushedToHeap = true; // must have been pushed to heap during an earlier visit
     268              :                     }
     269              : 
     270            0 :                     if ((!followerInfo.visited || mayRevisit)
     271              :                             && effortToFollower < oldEffort) {
     272              :                         hasImproved = true;
     273            0 :                         followerArcInfo->effortsToBoundaryNodes[index] = effortToFollower;
     274              :                     }
     275              :                 } // end index loop
     276              : 
     277            0 :                 if (!hasImproved) {
     278            0 :                     continue; // no need to re-enque this follower, continue w/ next one
     279              :                 }
     280            0 :                 followerArcInfo->key = key;
     281            0 :                 if (!wasPushedToHeap) {
     282            0 :                     myFrontierList.push_back(&followerInfo);
     283            0 :                     std::push_heap(myFrontierList.begin(), myFrontierList.end(), *myComparator);
     284              :                 } else {
     285            0 :                     auto fi = std::find(myFrontierList.begin(), myFrontierList.end(), &followerInfo);
     286            0 :                     if (fi == myFrontierList.end()) { // has already been expanded, reinsert into frontier
     287              :                         assert(mayRevisit);
     288            0 :                         myFrontierList.push_back(&followerInfo);
     289            0 :                         std::push_heap(myFrontierList.begin(), myFrontierList.end(), *myComparator);
     290              :                     } else {
     291            0 :                         std::push_heap(myFrontierList.begin(), fi + 1, *myComparator);
     292              :                     }
     293              :                 }
     294              :             } // end follower loop
     295              :         } // end while (!myFrontierList.empty())
     296              : 
     297              : #ifdef CSPT_DEBUG_LEVEL_0
     298              :         std::cout << "centralizedSPTree finished (queue empty)." << std::endl;
     299              : #endif
     300              :         return true;
     301            0 :     }
     302              : 
     303              : protected:
     304              :     /** @brief Initialize the arc flag router
     305              :      * @param[in] fromEdges The container of from-/head/source edges
     306              :      * @param[in] vehicle The vehicle
     307              :      * @param[in] msTime The start time of the paths/routes in milliseconds
     308              :      */
     309              :     void init(std::vector<const E*> fromEdges, const V* const vehicle, SUMOTime msTime);
     310              :     /// @brief The min edge heap
     311              :     /// @note A container for reusage of the min edge heap
     312              :     std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo*> myFrontierList;
     313              :     /// @brief The list of visited edges (for resetting)
     314              :     std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo*> myFound;
     315              :     /// The container of edge information
     316              :     std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo> myEdgeInfos;
     317              :     /// @brief The edge informations specific to arc flag routing
     318              :     /// @note As opposed to the standard informations in SUMOAbstractRouter<E, V>::EdgeInfo
     319              :     std::vector<ArcInfo*>& myArcInfos;
     320              :     /// @brief The boolean flag indicating whether edge permissions need to be considered or not
     321              :     const bool myHavePermissions;
     322              :     /// @brief The boolean flag indicating whether edge restrictions need to be considered or not
     323              :     const bool myHaveRestrictions;
     324              :     /// @brief The handler for routing errors
     325              :     MsgHandler* const myErrorMsgHandler;
     326              :     /// @brief The object's operation to perform
     327              :     SUMOAbstractRouter<E, V>* myEffortProvider;
     328              :     /// @brief The comparator
     329              :     EdgeInfoComparator* myComparator;
     330              :     /// @brief The maximum speed in the network
     331              :     double myMaxSpeed;
     332              : };
     333              : 
     334              : // ===========================================================================
     335              : // method definitions
     336              : // ===========================================================================
     337              : 
     338              : template<class E, class N, class V>
     339            0 : void AFCentralizedSPTree<E, N, V>::init(std::vector<const E*> fromEdges, const V* const vehicle, SUMOTime msTime) {
     340              :     // all EdgeInfos touched in the previous query are either in myFrontierList or myFound: clean those up
     341            0 :     for (auto& edgeInfo : myFrontierList) {
     342            0 :         edgeInfo->reset();
     343              :     }
     344              :     myFrontierList.clear();
     345            0 :     for (auto& edgeInfo : myFound) {
     346            0 :         edgeInfo->reset();
     347              :     }
     348            0 :     for (auto& arcInfo : myArcInfos) {
     349            0 :         arcInfo->reset(); // does not reset effortsToBoundaryNodes
     350              :     }
     351              :     myFound.clear();
     352            0 :     for (const E* from : fromEdges) {
     353            0 :         if (from->isInternal()) {
     354            0 :             continue;
     355              :         }
     356              :         int edgeID = from->getNumericalID();
     357            0 :         auto& fromInfo = myEdgeInfos[edgeID];
     358            0 :         if (fromInfo.prohibited || isProhibited(from, vehicle)) {
     359            0 :             continue;
     360              :         }
     361            0 :         fromInfo.heuristicEffort = 0.;
     362            0 :         fromInfo.prev = nullptr;
     363            0 :         fromInfo.leaveTime = STEPS2TIME(msTime);
     364            0 :         myFrontierList.push_back(&fromInfo);
     365            0 :         ArcInfo* fromArcInfo = myArcInfos[edgeID];
     366            0 :         fromArcInfo->key = 0;
     367              :     }
     368            0 : }
        

Generated by: LCOV version 2.0-1