LCOV - code coverage report
Current view: top level - src/utils/router - AFRouter.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 0.0 % 195 0
Test Date: 2025-05-19 15:30:56 Functions: 0.0 % 15 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    AFRouter.h
      15              : /// @author  Ruediger Ebendt
      16              : /// @date    01.12.2023
      17              : ///
      18              : // Realizes an arc flag routing algorithm (Hilger et al.) in its multi-level variant
      19              : // (also called "stripped SHARC" by Delling et al.)
      20              : /****************************************************************************/
      21              : #pragma once
      22              : #include <config.h>
      23              : 
      24              : #include <cassert>
      25              : #include <string>
      26              : #include <functional>
      27              : #include <vector>
      28              : #include <set>
      29              : #include <limits>
      30              : #include <algorithm>
      31              : #include <iterator>
      32              : #include <map>
      33              : #include <iostream>
      34              : #include <memory>
      35              : #include <stdexcept>
      36              : #include <cstddef>
      37              : #include <utils/common/MsgHandler.h>
      38              : #include <utils/common/StringTokenizer.h>
      39              : #include <utils/common/StringUtils.h>
      40              : #include <utils/common/StdDefs.h>
      41              : #include <utils/common/ToString.h>
      42              : #include <utils/iodevices/OutputDevice.h>
      43              : #include "AStarLookupTable.h"
      44              : #include "SUMOAbstractRouter.h"
      45              : #include "KDTreePartition.h"
      46              : #include "FlippedEdge.h"
      47              : #include "AFInfo.h"
      48              : #include "AFBuilder.h"
      49              : 
      50              : #define UNREACHABLE (std::numeric_limits<double>::max() / 1000.0)
      51              : #define AFRO_WRITE_QGIS_FILTERS
      52              : 
      53              : //#define AFRO_DEBUG_LEVEL_0
      54              : //#define AFRO_DEBUG_LEVEL_1
      55              : //#define AFRO_DEBUG_LEVEL_2
      56              : //#define AFRO_DEBUG_LEVEL_3
      57              : 
      58              : #ifdef AFRO_DEBUG_LEVEL_3
      59              : #define AFRO_DEBUG_LEVEL_2
      60              : #endif
      61              : 
      62              : #ifdef AFRO_DEBUG_LEVEL_2
      63              : #define AFRO_DEBUG_LEVEL_1
      64              : #endif
      65              : 
      66              : #ifdef AFRO_DEBUG_LEVEL_1
      67              : #define AFRO_DEBUG_LEVEL_0
      68              : #endif
      69              : 
      70              : // ===========================================================================
      71              : // class definitions
      72              : // ===========================================================================
      73              : /**
      74              :  * @class AFRouter
      75              :  * Computes the shortest path through a network with an arc flag routing
      76              :  * algorithm (Hilger et al.) in its multi-level variant (also called
      77              :  * "stripped SHARC" by Delling et al.)
      78              :  *
      79              :  * The template parameters are:
      80              :  * @param E The edge class to use (MSEdge/ROEdge)
      81              :  * @param N The node class to use (MSJunction/RONode)
      82              :  * @param V The vehicle class to use (MSVehicle/ROVehicle)
      83              :  *
      84              :  * The router is edge-based
      85              :  * It must know the number of edges for internal reasons
      86              :  * and whether a missing connection between two given edges (unbuild route) shall
      87              :  * be reported as an error or as a warning
      88              :  *
      89              :  */
      90              : template<class E, class N, class V, class M>
      91              : class AFRouter : public SUMOAbstractRouter<E, V> {
      92              : public:
      93              :     typedef AbstractLookupTable<E, V> LookupTable;
      94              :     typedef typename KDTreePartition<E, N, V>::Cell Cell;
      95              :     typedef typename AFInfo<E>::FlagInfo FlagInfo;
      96              :     typedef AbstractLookupTable<FlippedEdge<E, N, V>, V> FlippedLookupTable;
      97              : 
      98              :     /** @brief Returns the edge information for the passed edge
      99              :      * @param[in] edge The edge
     100              :      * @note Non-const version
     101              :      * @return The edge information
     102              :      */
     103              :     typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo(const E* const edge) {
     104              :         return &(this->myEdgeInfos[edge->getNumericalID()]);
     105              :     }
     106              : 
     107              :     /** @brief Returns the edge information for the passed edge
     108              :      * @param[in] edge The edge
     109              :      * @note Const version
     110              :      * @return The edge information
     111              :      */
     112              :     const typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo(const E* const edge) const {
     113              :         return &(this->myEdgeInfos[edge->getNumericalID()]);
     114              :     }
     115              : 
     116              :     /**
     117              :      * @class EdgeInfoComparator
     118              :      *
     119              :      * Class to compare (and so sort) nodes by their effort
     120              :      */
     121              :     class EdgeInfoComparator {
     122              :     public:
     123              :         /** @brief Comparing method
     124              :          * @param[in] edgeInfo1 First edge information
     125              :          * @param[in] edgeInfo2 Second edge information
     126              :          * @return true iff heuristic effort of first edge information is greater than that of the second
     127              :          * @note In case of ties: true iff first edge information's numerical id is greater than that of the second
     128              :          */
     129              : 
     130              :         bool operator()(const typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo1, const typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo2) const {
     131            0 :             if (edgeInfo1->heuristicEffort == edgeInfo2->heuristicEffort) {
     132            0 :                 return edgeInfo1->edge->getNumericalID() > edgeInfo2->edge->getNumericalID();
     133              :             }
     134            0 :             return edgeInfo1->heuristicEffort > edgeInfo2->heuristicEffort;
     135              :         }
     136              :     };
     137              : 
     138              :     /** @brief Constructor
     139              :      * @param[in] edges The edges
     140              :      * @param[in] partition A partition of the router's network wrt a k-d tree subdivision scheme
     141              :      * @param[in] unbuildIsWarning The flag indicating whether network unbuilds should issue warnings or errors
     142              :      * @param[in] operation The operation for a forward graph
     143              :      * @param[in] flippedOperation The operation for a backward graph with flipped edges
     144              :      * @param[in] weightPeriod The validity duration of one weight interval
     145              :      * @param[in] lookup The lookup table for a forward graph
     146              :      * @param[in] flippedLookup The lookup table for a backward graph  with flipped edges
     147              :      * @param[in] havePermissions The boolean flag indicating whether edge permissions need to be considered or not
     148              :      * @param[in] haveRestrictions The boolean flag indicating whether edge restrictions need to be considered or not
     149              :      */
     150            0 :     AFRouter(const std::vector<E*>& edges,
     151              :              const KDTreePartition<E, N, V>* partition,
     152              :              bool unbuildIsWarning,
     153              :              typename SUMOAbstractRouter<E, V>::Operation operation, typename SUMOAbstractRouter<FlippedEdge<E, N, V>, V>::Operation flippedOperation,
     154              :              SUMOTime weightPeriod, const std::shared_ptr<const LookupTable> lookup = nullptr,
     155              :              const std::shared_ptr<const FlippedLookupTable> flippedLookup = nullptr,
     156              :              const bool havePermissions = false, const bool haveRestrictions = false) :
     157              :         SUMOAbstractRouter<E, V>("arcFlagRouter", unbuildIsWarning, operation, nullptr, havePermissions, haveRestrictions),
     158            0 :         myFlagInfos(nullptr),
     159            0 :         myPartition(partition),
     160              :         myLookupTable(lookup),
     161            0 :         myMaxSpeed(NUMERICAL_EPS),
     162            0 :         myWeightPeriod(weightPeriod),
     163            0 :         myValidUntil(0),
     164            0 :         myBuilder(new AFBuilder<E, N, V, M>(myPartition->getNumberOfLevels(), edges, unbuildIsWarning,
     165              :                                             flippedOperation, flippedLookup, havePermissions, haveRestrictions)),
     166            0 :         myType("arcFlagRouter"),
     167            0 :         myQueryVisits(0),
     168            0 :         myNumQueries(0),
     169            0 :         myQueryStartTime(0),
     170            0 :         myQueryTimeSum(0),
     171              : #ifdef AFRO_DEBUG_LEVEL_2
     172              :         myFlagContextStartTime(0),
     173              :         myFlagContextTimeSum(0),
     174              : #endif
     175            0 :         myLastSettledEdgeCell(nullptr),
     176            0 :         myTargetEdgeCellLevel0(nullptr) {
     177            0 :         for (const E* const edge : edges) {
     178            0 :             this->myEdgeInfos.push_back(typename SUMOAbstractRouter<E, V>::EdgeInfo(edge));
     179            0 :             myMaxSpeed = MAX2(myMaxSpeed, edge->getSpeedLimit() * MAX2(1.0, edge->getLengthGeometryFactor()));
     180              :         }
     181            0 :     }
     182              : 
     183              :     /** @brief "Normal" cloning constructor for uninitialized or time-dependent instances
     184              :      * @param[in] edges The edges
     185              :      * @param[in] partition A partition of the router's network wrt a k-d tree subdivision scheme
     186              :      * @param[in] unbuildIsWarning The flag indicating whether network unbuilds should issue warnings or errors
     187              :      * @param[in] operation The operation for a forward graph
     188              :      * @param[in] flippedOperation The operation for a backward graph with flipped edges
     189              :      * @param[in] weightPeriod The validity duration of one weight interval
     190              :      * @param[in] lookup The lookup table for a forward graph
     191              :      * @param[in] flippedLookup The lookup table for a backward graph with flipped edges
     192              :      * @param[in] havePermissions The boolean flag indicating whether edge permissions need to be considered
     193              :      * @param[in] haveRestrictions The boolean flag indicating whether edge restrictions need to be considered
     194              :      */
     195            0 :     AFRouter(const std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo>& edgeInfos,
     196              :              const std::vector<E*>& edges,
     197              :              const KDTreePartition<E, N, V>* partition,
     198              :              bool unbuildIsWarning,
     199              :              typename SUMOAbstractRouter<E, V>::Operation operation,
     200              :              typename SUMOAbstractRouter<FlippedEdge<E, N, V>, V>::Operation flippedOperation,
     201              :              SUMOTime weightPeriod, const std::shared_ptr<const LookupTable> lookup = nullptr,
     202              :              const std::shared_ptr<const FlippedLookupTable> flippedLookup = nullptr,
     203              :              const bool havePermissions = false, const bool haveRestrictions = false) :
     204              :         SUMOAbstractRouter<E, V>("arcFlagRouter", unbuildIsWarning, operation, nullptr, havePermissions, haveRestrictions),
     205            0 :         myFlagInfos(nullptr),
     206            0 :         myPartition(partition),
     207              :         myLookupTable(lookup),
     208            0 :         myMaxSpeed(NUMERICAL_EPS),
     209            0 :         myWeightPeriod(weightPeriod),
     210            0 :         myValidUntil(0),
     211            0 :         myBuilder(new AFBuilder<E, N, V, M>(myPartition->getNumberOfLevels(), edges, unbuildIsWarning,
     212              :                                             flippedOperation, flippedLookup, havePermissions, haveRestrictions)),
     213            0 :         myType("arcFlagRouter"),
     214            0 :         myQueryVisits(0),
     215            0 :         myNumQueries(0),
     216            0 :         myQueryStartTime(0),
     217            0 :         myQueryTimeSum(0),
     218              : #ifdef AFRO_DEBUG_LEVEL_2
     219              :         myFlagContextStartTime(0),
     220              :         myFlagContextTimeSum(0),
     221              : #endif
     222            0 :         myLastSettledEdgeCell(nullptr),
     223            0 :         myTargetEdgeCellLevel0(nullptr) {
     224            0 :         for (const auto& edgeInfo : edgeInfos) {
     225            0 :             this->myEdgeInfos.push_back(typename SUMOAbstractRouter<E, V>::EdgeInfo(edgeInfo.edge));
     226            0 :             myMaxSpeed = MAX2(myMaxSpeed, edgeInfo.edge->getSpeedLimit() * edgeInfo.edge->getLengthGeometryFactor());
     227              :         }
     228            0 :     }
     229              : 
     230              :     /** @brief Special cloning constructor, only for time-independent instances which never rebuild arc infos
     231              :      * @param[in] edgeInfos The vector of edge information
     232              :      * @param[in] partition A partition of the router's network wrt a k-d tree subdivision scheme
     233              :      * @param[in] unbuildIsWarning The flag indicating whether network unbuilds should issue warnings or errors
     234              :      * @param[in] operation The operation for a forward graph
     235              :      * @param[in] flagInfos The vector of arc flag information
     236              :      * @param[in] lookup The lookup table for a forward graph
     237              :      * @param[in] havePermissions The boolean flag indicating whether edge permissions need to be considered
     238              :      * @param[in] haveRestrictions The boolean flag indicating whether edge restrictions need to be considered
     239              :      */
     240            0 :     AFRouter(const std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo>& edgeInfos,
     241              :              const KDTreePartition<E, N, V>* partition,
     242              :              bool unbuildIsWarning, typename SUMOAbstractRouter<E, V>::Operation operation,
     243              :              std::vector<FlagInfo*>* flagInfos,
     244              :              const std::shared_ptr<const LookupTable> lookup = nullptr,
     245              :              const bool havePermissions = false, const bool haveRestrictions = false) :
     246              :         SUMOAbstractRouter<E, V>("arcFlagRouterClone", unbuildIsWarning, operation, nullptr, havePermissions, haveRestrictions),
     247            0 :         myFlagInfos(flagInfos),
     248            0 :         myPartition(partition),
     249              :         myLookupTable(lookup),
     250            0 :         myMaxSpeed(NUMERICAL_EPS),
     251            0 :         myWeightPeriod(SUMOTime_MAX),
     252            0 :         myValidUntil(SUMOTime_MAX),
     253            0 :         myBuilder(nullptr),
     254            0 :         myType("arcFlagRouterClone"),
     255            0 :         myQueryVisits(0),
     256            0 :         myNumQueries(0),
     257            0 :         myQueryStartTime(0),
     258            0 :         myQueryTimeSum(0),
     259              : #ifdef AFRO_DEBUG_LEVEL_2
     260              :         myFlagContextStartTime(0),
     261              :         myFlagContextTimeSum(0),
     262              : #endif
     263            0 :         myLastSettledEdgeCell(nullptr),
     264            0 :         myTargetEdgeCellLevel0(nullptr) {
     265            0 :         for (const auto& edgeInfo : edgeInfos) {
     266            0 :             this->myEdgeInfos.push_back(typename SUMOAbstractRouter<E, V>::EdgeInfo(edgeInfo.edge));
     267            0 :             myMaxSpeed = MAX2(myMaxSpeed, edgeInfo.edge->getSpeedLimit() * edgeInfo.edge->getLengthGeometryFactor());
     268              :         }
     269            0 :     }
     270              : 
     271              :     /// @brief Destructor
     272            0 :     virtual ~AFRouter() {
     273            0 :         delete myBuilder;
     274            0 :     }
     275              : 
     276              :     /// @brief Cloning method
     277            0 :     virtual SUMOAbstractRouter<E, V>* clone() {
     278              :         // I am either a clone myself, or I am already initialized and time-independent
     279              :         // (i.e., I have been created with a maximum weight period)
     280            0 :         if (myWeightPeriod == SUMOTime_MAX && myFlagInfos != nullptr) {
     281              :             // we only need the arc infos once:
     282            0 :             return new AFRouter(this->myEdgeInfos, myPartition, this->myErrorMsgHandler == MsgHandler::getWarningInstance(),
     283            0 :                                 this->myOperation, myFlagInfos, myLookupTable, this->myHavePermissions, this->myHaveRestrictions);
     284              :         }
     285              :         // I am not a clone: I am either uninitialized, or initialized but time-dependent:
     286              :         // create another such guy (also flagged as a non-clone)
     287            0 :         return new AFRouter(this->myEdgeInfos, myBuilder->getEdges(), myPartition,
     288            0 :                             this->myErrorMsgHandler == MsgHandler::getWarningInstance(),
     289            0 :                             this->myOperation, myBuilder->getArcFlagBuild()->getFlippedOperation(),
     290            0 :                             myWeightPeriod, myLookupTable, myBuilder->getArcFlagBuild()->getFlippedLookup(),
     291            0 :                             this->myHavePermissions, this->myHaveRestrictions);
     292              :     }
     293              : 
     294              :     /** @brief Converts a partition level number to a SHARC level number
     295              :      * @param[in] partitionLevel The partition level
     296              :      * @param[in] numberOfPartitionLevels The number of partition levels
     297              :      * @return The SHARC level number
     298              :      */
     299              :     static int partitionLevel2SHARCLevel(int partitionLevel, int numberOfPartitionLevels) {
     300              :         // heads up: partition levels must start at zero, with zero being an illegal argument
     301              :         // (since it would corresponds to level L = V, which is not a valid SHARC level)
     302              :         if (partitionLevel <= 0) {
     303              :             throw std::invalid_argument("partitionLevel2SHARCLevel: given partition level is zero (0) or below. This does not correspond to a valid SHARC level. Partition levels valid for conversion to SHARC levels go from one to number of partition levels minus one.");
     304              :         }
     305              :         // heads up: partition levels must start at zero
     306              :         if (partitionLevel > numberOfPartitionLevels - 1) {
     307              :             throw std::invalid_argument("partitionLevel2SHARCLevel: given partition level exceeds the number of partition levels minus one. Most likely you did not start the partition level numbering at zero (0), which is required here.");
     308              :         }
     309              :         return (numberOfPartitionLevels - 1) - partitionLevel;
     310              :     }
     311              : 
     312              :     /** @brief Converts a SHARC level number to a partition level number
     313              :      * @param[in] sHARCLevel The SHARC level
     314              :      * @param[in] numberOfPartitionLevels The number of partition levels
     315              :      * @return The partition level number
     316              :      */
     317            0 :     static int sHARCLevel2PartitionLevel(int sHARCLevel, int numberOfPartitionLevels) {
     318            0 :         int numberOfSHARCLevels = numberOfPartitionLevels - 1;
     319            0 :         if (sHARCLevel < 0) {
     320            0 :             throw std::invalid_argument("sHARCLevel2PartitionLevel: given SHARC level is negative.");
     321              :         }
     322              :         // heads up: SHARC levels must start at zero (0),
     323              :         // and end at number of partition levels minus two
     324            0 :         if (sHARCLevel > numberOfSHARCLevels - 1) {
     325            0 :             throw std::invalid_argument("sHARCLevel2PartitionLevel: given SHARC level exceeds the number of SHARC levels minus one. Most likely you did not start the SHARC level numbering at zero (0), which is required here.");
     326              :         }
     327            0 :         return numberOfSHARCLevels - sHARCLevel;
     328              :     }
     329              : 
     330              :     /** @brief Returns the arc flag of the edge in flagInfo wrt flagContext
     331              :      * @param[in] flagInfo The arc flag information
     332              :      * @param[in] flagContext The flag context tuple
     333              :      * @return The flag indicating whether the arc flag is set or not, wrt given arc flag information and context
     334              :      */
     335            0 :     static bool flag(const FlagInfo* flagInfo, const std::tuple<int, int, bool> flagContext) {
     336              :         assert(flagInfo);
     337              :         return flagInfo->arcFlags.empty() ? true : /* play it safe */
     338            0 :                (flagInfo->arcFlags)[std::get<0>(flagContext) /* assumed to be the SHARC level */ * 2
     339            0 :                                                              + std::get<1>(flagContext) /* assumed to be the cell index */];
     340              : 
     341              :     }
     342              : 
     343              :     /** @brief Returns the arc flags of the passed edge
     344              :      * @param[in] edge The edge
     345              :      * @return The arc flags of the given edge
     346              :      */
     347              :     std::vector<bool>& flags(const E* edge);
     348              : 
     349              :     /** @brief Trigger arc flags rebuild
     350              :      * @param[in] The vehicle
     351              :      */
     352            0 :     virtual void reset(const V* const vehicle) {
     353            0 :         if (myValidUntil == 0) {
     354            0 :             myValidUntil = myWeightPeriod;
     355              :         }
     356              :         assert(myBuilder);
     357              : #ifdef AFRO_DEBUG_LEVEL_0
     358              :         long long int firstCallStart = 0;
     359              :         long long int firstCallTime = 0;
     360              :         firstCallStart = SysUtils::getCurrentMillis();
     361              :         std::cout << "Calling arc flag router for the first time during current weight period (arc flags build). This might take a while... " << std::endl;
     362              : #endif
     363            0 :         myFlagInfos = &(myBuilder->build(myValidUntil - myWeightPeriod, vehicle));
     364              : #ifdef AFRO_DEBUG_LEVEL_0
     365              :         firstCallTime = (SysUtils::getCurrentMillis() - firstCallStart);
     366              :         std::cout << "Time spent for arc flags build: " << elapsedMs2string(firstCallTime) << std::endl;
     367              : #endif
     368            0 :     }
     369              : 
     370              :     /** @brief Initialize the arc flag router
     371              :      * param[in] edgeID The edge id(entifier)
     372              :      * param[in] msTime The start time of the routes in milliseconds
     373              :      */
     374              :     void init(const int edgeID, const SUMOTime msTime);
     375              :     /** @brief Returns the flag context for a route query from given settled edge to the target edge
     376              :      * @param[in] settledEdge The settled edge
     377              :      * @param[in] targetEdge The target edge
     378              :      */
     379              :     std::tuple<int, int, bool> flagContext(const E* settledEdge, const E* targetEdge);
     380              :     /// @brief Kept for runtime comparisons
     381              :     std::tuple<int, int, bool> flagContextNaive(const E* settledEdge, const E* targetEdge);
     382              : 
     383              :     /** @brief Builds the route between the given edges using the minimum travel time
     384              :      * param[in] from The from-/start/source/head edge
     385              :      * param[in] to The to-/end/target/tail edge
     386              :      * param[in] vehicle The vehicle
     387              :      * param[in] msTime The start time of the routes in milliseconds
     388              :      * param[out] into The vector of edges, into which the solution route is written
     389              :      * @param[in] silent The boolean flag indicating whether the method stays silent or puts out messages
     390              :      */
     391            0 :     bool compute(const E* from, const E* to, const V* const vehicle,
     392              :                  SUMOTime msTime, std::vector<const E*>& into, bool silent = false) {
     393              :         assert(from != nullptr && to != nullptr);
     394              :         // check whether from and to can be used
     395            0 :         if (this->myEdgeInfos[from->getNumericalID()].prohibited || this->isProhibited(from, vehicle)) {
     396            0 :             if (!silent) {
     397            0 :                 this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on source edge '" + from->getID() + "'.");
     398              :             }
     399            0 :             return false;
     400              :         }
     401            0 :         if (this->myEdgeInfos[to->getNumericalID()].prohibited || this->isProhibited(to, vehicle)) {
     402            0 :             if (!silent) {
     403            0 :                 this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on destination edge '" + to->getID() + "'.");
     404              :             }
     405            0 :             return false;
     406              :         }
     407              : 
     408            0 :         if (msTime >= myValidUntil) {
     409              :             assert(myBuilder != nullptr); // only time independent clones do not have a builder
     410            0 :             while (msTime >= myValidUntil) {
     411            0 :                 myValidUntil += myWeightPeriod;
     412              :             }
     413            0 :             reset(vehicle);
     414              :         }
     415              :         // rewind routing start time to building time (this can only be a gross approximation
     416              :         // of time-dependent routing)
     417            0 :         msTime = myValidUntil - myWeightPeriod;
     418              : 
     419            0 :         double length = 0.; // dummy for the via edge cost update
     420            0 :         this->startQuery();
     421            0 :         const SUMOVehicleClass vClass = vehicle == 0 ? SVC_IGNORING : vehicle->getVClass();
     422            0 :         this->init(from->getNumericalID(), msTime);
     423            0 :         this->myAmClean = false;
     424              :         // loop
     425              :         int num_visited = 0;
     426              : #ifdef AFRO_DEBUG_LEVEL_1
     427              :         int numberOfFollowers = 0;
     428              :         int numberOfAvoidedFollowers = 0;
     429              :         int numberOfEmptyFlagVectors = 0;
     430              : #endif
     431            0 :         const bool mayRevisit = myLookupTable != nullptr && !myLookupTable->consistent();
     432            0 :         const double speed = vehicle == nullptr ? myMaxSpeed : MIN2(vehicle->getMaxSpeed(), myMaxSpeed * vehicle->getChosenSpeedFactor());
     433              : 
     434            0 :         while (!this->myFrontierList.empty()) {
     435            0 :             num_visited += 1;
     436              :             // use the edge with the minimal length
     437            0 :             auto* const minimumInfo = this->myFrontierList.front();
     438            0 :             const E* const minEdge = minimumInfo->edge;
     439              :             // check whether the destination edge was already reached
     440            0 :             if (minEdge == to) {
     441            0 :                 this->buildPathFrom(minimumInfo, into);
     442            0 :                 this->endQuery(num_visited);
     443              : #ifdef AFRO_DEBUG_LEVEL_1
     444              :                 std::cout << "Found to, to->getID(): " << to->getID() << std::endl;
     445              :                 std::cout << static_cast<double>(numberOfFollowers - numberOfAvoidedFollowers) / static_cast<double>(num_visited)
     446              :                           << " followers considered (out of " << static_cast<double>(numberOfFollowers) / static_cast<double>(num_visited) << ") on average." << std::endl;
     447              :                 std::cout << static_cast<double>(numberOfFollowers - numberOfAvoidedFollowers)
     448              :                           << " followers considered (out of " << static_cast<double>(numberOfFollowers) << ")." << std::endl;
     449              :                 std::cout << numberOfEmptyFlagVectors << " out of " << numberOfFollowers << " flag vectors of followers were unassigned (i.e., empty)." << std::endl;
     450              :                 std::cout << "num_visited: " << num_visited << std::endl;
     451              : #endif
     452            0 :                 return true;
     453              :             }
     454            0 :             std::pop_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
     455              :             this->myFrontierList.pop_back();
     456            0 :             this->myFound.push_back(minimumInfo);
     457            0 :             minimumInfo->visited = true;
     458              : 
     459            0 :             const double effortDelta = this->getEffort(minEdge, vehicle, minimumInfo->leaveTime);
     460            0 :             const double leaveTime = minimumInfo->leaveTime + this->getTravelTime(minEdge, vehicle, minimumInfo->leaveTime, effortDelta);
     461              : 
     462              :             // admissible A* heuristic: straight line distance at maximum speed
     463              :             // this is calculated from the end of minEdge so it possibly includes via efforts to the followers
     464              :             double heuristic_remaining = 0.;
     465            0 :             double heuristicEffort = minimumInfo->effort + effortDelta + heuristic_remaining;
     466              :             // check all ways from the edge with the minimal length
     467            0 :             for (const std::pair<const E*, const E*>& follower : minEdge->getViaSuccessors(vClass)) {
     468            0 :                 auto& followerInfo = this->myEdgeInfos[follower.first->getNumericalID()];
     469            0 :                 const FlagInfo* followerFlagInfo = (*myFlagInfos)[follower.first->getNumericalID()];
     470              :                 // check whether it can be used
     471            0 :                 if (followerInfo.prohibited || this->isProhibited(follower.first, vehicle)) {
     472            0 :                     continue;
     473              :                 }
     474              : #ifdef AFRO_DEBUG_LEVEL_1
     475              :                 numberOfFollowers++;
     476              :                 if (followerFlagInfo->arcFlags.empty()) {
     477              :                     numberOfEmptyFlagVectors++;
     478              :                 }
     479              : #endif
     480              : #ifdef AFRO_DEBUG_LEVEL_2
     481              :                 myFlagContextStartTime = SysUtils::getCurrentMillis();
     482              : #endif
     483            0 :                 std::tuple<int, int, bool> flagContext = this->flagContext(follower.first, to);
     484              :                 //std::tuple<int, int, bool> flagContext = this->flagContextNaive(follower.first, to);
     485              : #ifdef AFRO_DEBUG_LEVEL_2
     486              :                 myFlagContextTimeSum += (SysUtils::getCurrentMillis() - myFlagContextStartTime);
     487              : #endif
     488            0 :                 if (!flag(followerFlagInfo, flagContext)) {
     489              : #ifdef AFRO_DEBUG_LEVEL_1
     490              :                     numberOfAvoidedFollowers++;
     491              : #endif
     492            0 :                     continue;
     493              :                 }
     494              : 
     495              :                 // admissible A* heuristic: straight line distance at maximum speed
     496              :                 // this is calculated from the end of minEdge so it possibly includes via efforts to the followers
     497            0 :                 if (heuristic_remaining == 0 && std::get<0>(flagContext) == 0 && std::get<2>(flagContext)) {
     498              :                     // arrived at the target cell at level 0? use heuristic
     499              :                     heuristic_remaining =
     500            0 :                         (myLookupTable == nullptr ? minEdge->getDistanceTo(to) / speed :
     501            0 :                          myLookupTable->lowerBound(minEdge, to, speed, vehicle->getChosenSpeedFactor(),
     502              :                                                    minEdge->getMinimumTravelTime(nullptr), to->getMinimumTravelTime(nullptr)));
     503            0 :                     if (heuristic_remaining == UNREACHABLE) {
     504              :                         break; // -> skip remaining followers, continue with next min heap element
     505              :                     }
     506            0 :                     heuristicEffort += heuristic_remaining;
     507              :                 }
     508              : 
     509            0 :                 double effort = minimumInfo->effort + effortDelta;
     510            0 :                 double time = leaveTime;
     511            0 :                 this->updateViaEdgeCost(follower.second, vehicle, time, effort, length);
     512            0 :                 const double oldEffort = followerInfo.effort;
     513            0 :                 if ((!followerInfo.visited || mayRevisit) && effort < oldEffort) {
     514            0 :                     followerInfo.effort = effort;
     515              :                     // if we use the effort including the via effort below we would count the via twice as shown by the ticket676 test
     516              :                     // but we should never get below the real effort, see #12463
     517            0 :                     followerInfo.heuristicEffort = MAX2(MIN2(heuristicEffort, followerInfo.heuristicEffort), effort);
     518            0 :                     followerInfo.leaveTime = time;
     519            0 :                     followerInfo.prev = minimumInfo;
     520            0 :                     if (oldEffort == std::numeric_limits<double>::max()) {
     521            0 :                         this->myFrontierList.push_back(&followerInfo);
     522              :                         std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
     523              :                     } else {
     524            0 :                         auto fi = std::find(this->myFrontierList.begin(), this->myFrontierList.end(), &followerInfo);
     525            0 :                         if (fi == this->myFrontierList.end()) {
     526              :                             assert(mayRevisit);
     527            0 :                             this->myFrontierList.push_back(&followerInfo);
     528              :                             std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
     529              :                         } else {
     530              :                             std::push_heap(this->myFrontierList.begin(), fi + 1, myComparator);
     531              :                         }
     532              :                     }
     533              :                 }
     534              :             } // for followers
     535              :         }
     536            0 :         this->endQuery(num_visited);
     537              : #ifdef AFRO_DEBUG_LEVEL_1
     538              :         std::cout << "Queue ran empty, no solution." << std::endl;
     539              :         std::cout << static_cast<double>(numberOfFollowers - numberOfAvoidedFollowers) / static_cast<double>(num_visited)
     540              :                   << " followers considered (out of " << static_cast<double>(numberOfFollowers) / static_cast<double>(num_visited) << ") on average." << std::endl;
     541              :         std::cout << static_cast<double>(numberOfFollowers - numberOfAvoidedFollowers)
     542              :                   << " followers considered (out of " << static_cast<double>(numberOfFollowers) << ")." << std::endl;
     543              :         std::cout << numberOfEmptyFlagVectors << " out of " << numberOfFollowers << " flag vectors of followers were unassigned (i.e., empty)." << std::endl;
     544              :         std::cout << "num_visited: " << num_visited << std::endl;
     545              : #endif
     546            0 :         if (!silent) {
     547            0 :             this->myErrorMsgHandler->informf("No connection between edge '%' and edge '%' found.", from->getID(), to->getID());
     548              :         }
     549              :         return false;
     550              :     }
     551              : 
     552              :     /// @brief Start timer for query time sum
     553              :     void startQuery();
     554              :     /// @brief Stop timer for query time sum
     555              :     void endQuery(int visits);
     556              :     /// @brief Report query time statistics
     557              :     void reportStatistics();
     558              :     /// @brief Reset query time statistics
     559              :     void resetStatistics();
     560              :     /// @brief Bulk mode is not supported
     561            0 :     virtual void setBulkMode(const bool mode) {
     562              :         UNUSED_PARAMETER(mode);
     563            0 :         throw std::runtime_error("Bulk mode is not supported by the arc flag router.");
     564              :     }
     565              : 
     566              : protected:
     567              :     /// @brief Edge infos containing the associated edge and its arc flags
     568              :     std::vector<FlagInfo*>* myFlagInfos;
     569              :     /// @brief The partition
     570              :     const KDTreePartition<E, N, V>* myPartition;
     571              :     /// @brief The comparator for edge information
     572              :     EdgeInfoComparator myComparator;
     573              :     /// @brief The lookup table for travel time heuristics
     574              :     const std::shared_ptr<const LookupTable> myLookupTable;
     575              :     /// @brief The maximum speed in the network
     576              :     double myMaxSpeed;
     577              :     /// @brief The validity duration of one weight interval
     578              :     const SUMOTime myWeightPeriod;
     579              :     /// @brief The validity duration of the current flag infos (exclusive)
     580              :     SUMOTime myValidUntil;
     581              :     /// @brief The builder
     582              :     AFBuilder<E, N, V, M>* myBuilder;
     583              :     /// @brief The type of this router
     584              :     /// @note The one in SUMOAbstractRouter is private, required for more flexible performance logging (see below)
     585              :     const std::string myType;
     586              :     /// @brief Counters for performance logging
     587              :     /// @note The ones in SUMOAbstractRouter are private - introduced to reset stats / recalculate before destruction
     588              :     long long int myQueryVisits;
     589              :     long long int myNumQueries;
     590              :     /// @brief The time spent querying in milliseconds
     591              :     /// @note The ones in SUMOAbstractRouter are private - introduced to reset stats / recalculate before destruction
     592              :     long long int myQueryStartTime;
     593              :     long long int myQueryTimeSum;
     594              : #ifdef AFRO_DEBUG_LEVEL_2
     595              :     /// @brief The time spent for flagContext in milliseconds
     596              :     long long int myFlagContextStartTime;
     597              :     long long int myFlagContextTimeSum;
     598              : #endif
     599              : private:
     600              :     /// @brief The cell of the last settled edge
     601              :     const Cell* myLastSettledEdgeCell;
     602              :     /// @brief The last flag context
     603              :     std::tuple<int, int, bool> myLastFlagContext;
     604              :     /// @brief The cell of the target edge at SHARC level 0
     605              :     const Cell* myTargetEdgeCellLevel0;
     606              : };
     607              : 
     608              : // ===========================================================================
     609              : // method definitions
     610              : // ===========================================================================
     611              : 
     612              : template<class E, class N, class V, class M>
     613              : std::vector<bool>& AFRouter<E, N, V, M>::flags(const E* edge) {
     614              :     assert(edge);
     615              :     if (!myFlagInfos) {
     616              :         throw std::runtime_error("flag infos not initialized, call compute() at least once before calling flags().");
     617              :     }
     618              :     return ((*myFlagInfos)[edge->getNumericalID()])->arcFlags;
     619              : }
     620              : 
     621              : template<class E, class N, class V, class M>
     622            0 : void AFRouter<E, N, V, M>::init(const int edgeID, const SUMOTime msTime) {
     623              :     // all EdgeInfos touched in the previous query are either in myFrontierList or myFound: clean those up
     624            0 :     myTargetEdgeCellLevel0 = nullptr;
     625            0 :     for (auto& edgeInfo : this->myFrontierList) {
     626            0 :         edgeInfo->reset();
     627              :     }
     628              :     this->myFrontierList.clear();
     629            0 :     for (auto& edgeInfo : this->myFound) {
     630            0 :         edgeInfo->reset();
     631              :     }
     632              :     this->myFound.clear();
     633            0 :     if (edgeID > -1) {
     634              :         // add begin node
     635            0 :         auto& fromInfo = this->myEdgeInfos[edgeID];
     636            0 :         fromInfo.heuristicEffort = 0.;
     637            0 :         fromInfo.effort = 0.;
     638            0 :         fromInfo.leaveTime = STEPS2TIME(msTime);
     639            0 :         fromInfo.prev = nullptr;
     640            0 :         this->myFrontierList.push_back(&fromInfo);
     641              :     }
     642            0 : }
     643              : 
     644              : template<class E, class N, class V, class M>
     645              : std::tuple<int, int, bool> AFRouter<E, N, V, M>::flagContextNaive(const E* settledEdge, const E* targetEdge) {
     646              :     assert(settledEdge != nullptr && targetEdge != nullptr);
     647              :     int sHARCLevel;
     648              :     for (sHARCLevel = 0; sHARCLevel < myPartition->getNumberOfLevels() - 1; sHARCLevel++) {
     649              :         int partitionLevel = sHARCLevel2PartitionLevel(sHARCLevel, myPartition->getNumberOfLevels());
     650              :         const std::vector<const Cell*>& levelCells = myPartition->getCellsAtLevel(partitionLevel);
     651              :         typename std::vector<const Cell*>::const_iterator first = levelCells.begin();
     652              :         typename std::vector<const Cell*>::const_iterator last = levelCells.end();
     653              :         typename std::vector<const Cell*>::const_iterator iter;
     654              :         const Cell* settledEdgeCell = nullptr;
     655              :         const Cell* targetEdgeCell = nullptr;
     656              :         // go through the cells of the level
     657              :         for (iter = first; iter != last; iter++) {
     658              :             // myPartition is assumed to partition a non-reversed (forward) graph
     659              :             if (!settledEdgeCell && (*iter)->contains(settledEdge->getFromJunction())) {
     660              :                 settledEdgeCell = *iter;
     661              :             }
     662              :             if (!targetEdgeCell && (*iter)->contains(targetEdge->getFromJunction())) {
     663              :                 targetEdgeCell = *iter;
     664              :             }
     665              :             if (settledEdgeCell && targetEdgeCell) {
     666              :                 break;
     667              :             }
     668              :         }
     669              :         assert(settledEdgeCell && targetEdgeCell); // we should find both edges on each level
     670              :         if (settledEdgeCell->getSupercell() == targetEdgeCell->getSupercell()) {
     671              :             return std::make_tuple(sHARCLevel, targetEdgeCell->isLeftOrLowerCell() ? 0 : 1,
     672              :                                    settledEdgeCell == targetEdgeCell);
     673              :         }
     674              :     }
     675              :     // we should never arrive here
     676              :     throw std::runtime_error("flagContext: relevant level could not be determined.");
     677              : }
     678              : 
     679              : template<class E, class N, class V, class M>
     680            0 : std::tuple<int, int, bool> AFRouter<E, N, V, M>::flagContext(const E* settledEdge, const E* targetEdge) {
     681              :     assert(settledEdge != nullptr && targetEdge != nullptr);
     682              :     int sHARCLevel = 0; // lowest level with smallest cells
     683              :     const Cell* settledEdgeCell = nullptr;
     684              :     const Cell* targetEdgeCell = nullptr;
     685            0 :     if (myLastSettledEdgeCell
     686            0 :             && myLastSettledEdgeCell->contains(settledEdge->getFromJunction())) {
     687              :         // exploit the partial locality of Dijkstra's algorithm: settled edge is still
     688              :         // in the same cell as the last one? Then we can simply return the
     689              :         // last flagContext tuple again.
     690            0 :         return myLastFlagContext;
     691              :     }
     692            0 :     int numberOfPartitionLevels = myPartition->getNumberOfLevels();
     693            0 :     if (numberOfPartitionLevels <= 4) { // small number of bottom cells -> go through them, no use of k-d tree
     694            0 :         int partitionLevel = sHARCLevel2PartitionLevel(sHARCLevel, myPartition->getNumberOfLevels());
     695            0 :         const std::vector<const Cell*>& levelCells = myPartition->getCellsAtLevel(partitionLevel);
     696              :         typename std::vector<const Cell*>::const_iterator first = levelCells.begin();
     697              :         typename std::vector<const Cell*>::const_iterator last = levelCells.end();
     698              :         typename std::vector<const Cell*>::const_iterator iter;
     699              :         // go through the cells of the level
     700            0 :         for (iter = first; iter != last; iter++) {
     701              :             // myPartition is assumed to partition a non-reversed (forward) graph
     702              :             if (!settledEdgeCell
     703            0 :                     && (*iter)->contains(settledEdge->getFromJunction())) {
     704              :                 settledEdgeCell = *iter;
     705              :             }
     706            0 :             if (!targetEdgeCell && myTargetEdgeCellLevel0) {
     707              :                 targetEdgeCell = myTargetEdgeCellLevel0;
     708              :             } else if (!targetEdgeCell
     709            0 :                        && (*iter)->contains(targetEdge->getFromJunction())) {
     710            0 :                 myTargetEdgeCellLevel0 = *iter;
     711              :                 targetEdgeCell = myTargetEdgeCellLevel0;
     712              :             }
     713            0 :             if (settledEdgeCell && targetEdgeCell) {
     714              :                 assert(myTargetEdgeCellLevel0);
     715              :                 break;
     716              :             }
     717              :         }
     718              :     } else { // larger number of bottom cells -> use a k-d tree
     719            0 :         settledEdgeCell = myPartition->searchNode(settledEdge->getFromJunction());
     720            0 :         if (!targetEdgeCell && myTargetEdgeCellLevel0) {
     721              :             // search only once per query
     722              :             targetEdgeCell = myTargetEdgeCellLevel0;
     723              :         } else if (!targetEdgeCell) {
     724            0 :             myTargetEdgeCellLevel0 = myPartition->searchNode(targetEdge->getFromJunction());
     725              :             targetEdgeCell = myTargetEdgeCellLevel0; // myTargetEdgeCellLevel0 is reset in init()
     726              :         }
     727              :     }
     728              :     assert(settledEdgeCell && targetEdgeCell); // we should find both edges on each level
     729            0 :     while (settledEdgeCell->getSupercell() != targetEdgeCell->getSupercell()) {
     730              :         settledEdgeCell = settledEdgeCell->getSupercell();
     731              :         targetEdgeCell = targetEdgeCell->getSupercell();
     732            0 :         sHARCLevel++;
     733              :     }
     734            0 :     myLastSettledEdgeCell = settledEdgeCell;
     735            0 :     std::tuple<int, int, bool> flagContext = std::make_tuple(sHARCLevel, targetEdgeCell->isLeftOrLowerCell() ? 0 : 1,
     736            0 :             settledEdgeCell == targetEdgeCell);
     737              :     myLastFlagContext = flagContext;
     738              :     return flagContext;
     739              : }
     740              : 
     741              : template<class E, class N, class V, class M>
     742            0 : void AFRouter<E, N, V, M>::startQuery() {
     743            0 :     myNumQueries++;
     744            0 :     myQueryStartTime = SysUtils::getCurrentMillis();
     745              :     SUMOAbstractRouter<E, V>::startQuery();
     746            0 : }
     747              : 
     748              : template<class E, class N, class V, class M>
     749            0 : void AFRouter<E, N, V, M>::endQuery(int visits) {
     750            0 :     myQueryVisits += visits;
     751            0 :     myQueryTimeSum += (SysUtils::getCurrentMillis() - myQueryStartTime);
     752              :     SUMOAbstractRouter<E, V>::endQuery(visits);
     753            0 : }
     754              : 
     755              : template<class E, class N, class V, class M>
     756              : void AFRouter<E, N, V, M>::reportStatistics() {
     757              :     if (myNumQueries > 0) {
     758              :         WRITE_MESSAGE(myType + " answered " + toString(myNumQueries) + " queries and explored " + toString((double)myQueryVisits / (double)myNumQueries) + " edges on average.");
     759              :         WRITE_MESSAGE(myType + " spent " + elapsedMs2string(myQueryTimeSum) + " answering queries (" + toString((double)myQueryTimeSum / (double)myNumQueries) + " ms on average).");
     760              : #ifdef AFRO_DEBUG_LEVEL_2
     761              :         WRITE_MESSAGE("flagContext spent " + elapsedMs2string(myFlagContextTimeSum) + " (" + toString((double)myFlagContextTimeSum / (double)myNumQueries) + " ms on average).");
     762              : #endif
     763              :     }
     764              : }
     765              : 
     766              : template<class E, class N, class V, class M>
     767              : void AFRouter<E, N, V, M>::resetStatistics() {
     768              :     myNumQueries = 0;
     769              :     myQueryVisits = 0;
     770              :     myQueryTimeSum = 0;
     771              :     myQueryStartTime = 0;
     772              : }
        

Generated by: LCOV version 2.0-1