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: 2026-01-01 15:49:29 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->isProhibited(from, vehicle, STEPS2TIME(msTime))) {
     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              :         // technically, a temporary permission might be lifted by the time of arrival
     402            0 :         if (this->isProhibited(to, vehicle, STEPS2TIME(msTime))) {
     403            0 :             if (!silent) {
     404            0 :                 this->myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on destination edge '" + to->getID() + "'.");
     405              :             }
     406            0 :             return false;
     407              :         }
     408              : 
     409            0 :         if (msTime >= myValidUntil) {
     410              :             assert(myBuilder != nullptr); // only time independent clones do not have a builder
     411            0 :             while (msTime >= myValidUntil) {
     412            0 :                 myValidUntil += myWeightPeriod;
     413              :             }
     414            0 :             reset(vehicle);
     415              :         }
     416              :         // rewind routing start time to building time (this can only be a gross approximation
     417              :         // of time-dependent routing)
     418            0 :         msTime = myValidUntil - myWeightPeriod;
     419              : 
     420            0 :         double length = 0.; // dummy for the via edge cost update
     421            0 :         this->startQuery();
     422            0 :         const SUMOVehicleClass vClass = vehicle == 0 ? SVC_IGNORING : vehicle->getVClass();
     423            0 :         this->init(from->getNumericalID(), msTime);
     424            0 :         this->myAmClean = false;
     425              :         // loop
     426              :         int num_visited = 0;
     427              : #ifdef AFRO_DEBUG_LEVEL_1
     428              :         int numberOfFollowers = 0;
     429              :         int numberOfAvoidedFollowers = 0;
     430              :         int numberOfEmptyFlagVectors = 0;
     431              : #endif
     432            0 :         const bool mayRevisit = myLookupTable != nullptr && !myLookupTable->consistent();
     433            0 :         const double speed = vehicle == nullptr ? myMaxSpeed : MIN2(vehicle->getMaxSpeed(), myMaxSpeed * vehicle->getChosenSpeedFactor());
     434              : 
     435            0 :         while (!this->myFrontierList.empty()) {
     436            0 :             num_visited += 1;
     437              :             // use the edge with the minimal length
     438            0 :             auto* const minimumInfo = this->myFrontierList.front();
     439            0 :             const E* const minEdge = minimumInfo->edge;
     440              :             // check whether the destination edge was already reached
     441            0 :             if (minEdge == to) {
     442            0 :                 this->buildPathFrom(minimumInfo, into);
     443            0 :                 this->endQuery(num_visited);
     444              : #ifdef AFRO_DEBUG_LEVEL_1
     445              :                 std::cout << "Found to, to->getID(): " << to->getID() << std::endl;
     446              :                 std::cout << static_cast<double>(numberOfFollowers - numberOfAvoidedFollowers) / static_cast<double>(num_visited)
     447              :                           << " followers considered (out of " << static_cast<double>(numberOfFollowers) / static_cast<double>(num_visited) << ") on average." << std::endl;
     448              :                 std::cout << static_cast<double>(numberOfFollowers - numberOfAvoidedFollowers)
     449              :                           << " followers considered (out of " << static_cast<double>(numberOfFollowers) << ")." << std::endl;
     450              :                 std::cout << numberOfEmptyFlagVectors << " out of " << numberOfFollowers << " flag vectors of followers were unassigned (i.e., empty)." << std::endl;
     451              :                 std::cout << "num_visited: " << num_visited << std::endl;
     452              : #endif
     453            0 :                 return true;
     454              :             }
     455            0 :             std::pop_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
     456              :             this->myFrontierList.pop_back();
     457            0 :             this->myFound.push_back(minimumInfo);
     458            0 :             minimumInfo->visited = true;
     459              : 
     460            0 :             const double effortDelta = this->getEffort(minEdge, vehicle, minimumInfo->leaveTime);
     461            0 :             const double leaveTime = minimumInfo->leaveTime + this->getTravelTime(minEdge, vehicle, minimumInfo->leaveTime, effortDelta);
     462              : 
     463              :             // admissible A* heuristic: straight line distance at maximum speed
     464              :             // this is calculated from the end of minEdge so it possibly includes via efforts to the followers
     465              :             double heuristic_remaining = 0.;
     466            0 :             double heuristicEffort = minimumInfo->effort + effortDelta + heuristic_remaining;
     467              :             // check all ways from the edge with the minimal length
     468            0 :             for (const std::pair<const E*, const E*>& follower : minEdge->getViaSuccessors(vClass)) {
     469            0 :                 auto& followerInfo = this->myEdgeInfos[follower.first->getNumericalID()];
     470            0 :                 const FlagInfo* followerFlagInfo = (*myFlagInfos)[follower.first->getNumericalID()];
     471              :                 // check whether it can be used
     472            0 :                 if (this->isProhibited(follower.first, vehicle, leaveTime)) {
     473            0 :                     continue;
     474              :                 }
     475              : #ifdef AFRO_DEBUG_LEVEL_1
     476              :                 numberOfFollowers++;
     477              :                 if (followerFlagInfo->arcFlags.empty()) {
     478              :                     numberOfEmptyFlagVectors++;
     479              :                 }
     480              : #endif
     481              : #ifdef AFRO_DEBUG_LEVEL_2
     482              :                 myFlagContextStartTime = SysUtils::getCurrentMillis();
     483              : #endif
     484            0 :                 std::tuple<int, int, bool> flagContext = this->flagContext(follower.first, to);
     485              :                 //std::tuple<int, int, bool> flagContext = this->flagContextNaive(follower.first, to);
     486              : #ifdef AFRO_DEBUG_LEVEL_2
     487              :                 myFlagContextTimeSum += (SysUtils::getCurrentMillis() - myFlagContextStartTime);
     488              : #endif
     489            0 :                 if (!flag(followerFlagInfo, flagContext)) {
     490              : #ifdef AFRO_DEBUG_LEVEL_1
     491              :                     numberOfAvoidedFollowers++;
     492              : #endif
     493            0 :                     continue;
     494              :                 }
     495              : 
     496              :                 // admissible A* heuristic: straight line distance at maximum speed
     497              :                 // this is calculated from the end of minEdge so it possibly includes via efforts to the followers
     498            0 :                 if (heuristic_remaining == 0 && std::get<0>(flagContext) == 0 && std::get<2>(flagContext)) {
     499              :                     // arrived at the target cell at level 0? use heuristic
     500              :                     heuristic_remaining =
     501            0 :                         (myLookupTable == nullptr ? minEdge->getDistanceTo(to) / speed :
     502            0 :                          myLookupTable->lowerBound(minEdge, to, speed, vehicle->getChosenSpeedFactor(),
     503              :                                                    minEdge->getMinimumTravelTime(nullptr), to->getMinimumTravelTime(nullptr)));
     504            0 :                     if (heuristic_remaining == UNREACHABLE) {
     505              :                         break; // -> skip remaining followers, continue with next min heap element
     506              :                     }
     507            0 :                     heuristicEffort += heuristic_remaining;
     508              :                 }
     509              : 
     510            0 :                 double effort = minimumInfo->effort + effortDelta;
     511            0 :                 double time = leaveTime;
     512            0 :                 this->updateViaEdgeCost(follower.second, vehicle, time, effort, length);
     513            0 :                 const double oldEffort = followerInfo.effort;
     514            0 :                 if ((!followerInfo.visited || mayRevisit) && effort < oldEffort) {
     515            0 :                     followerInfo.effort = effort;
     516              :                     // if we use the effort including the via effort below we would count the via twice as shown by the ticket676 test
     517              :                     // but we should never get below the real effort, see #12463
     518            0 :                     followerInfo.heuristicEffort = MAX2(MIN2(heuristicEffort, followerInfo.heuristicEffort), effort);
     519            0 :                     followerInfo.leaveTime = time;
     520            0 :                     followerInfo.prev = minimumInfo;
     521            0 :                     if (oldEffort == std::numeric_limits<double>::max()) {
     522            0 :                         this->myFrontierList.push_back(&followerInfo);
     523              :                         std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
     524              :                     } else {
     525            0 :                         auto fi = std::find(this->myFrontierList.begin(), this->myFrontierList.end(), &followerInfo);
     526            0 :                         if (fi == this->myFrontierList.end()) {
     527              :                             assert(mayRevisit);
     528            0 :                             this->myFrontierList.push_back(&followerInfo);
     529              :                             std::push_heap(this->myFrontierList.begin(), this->myFrontierList.end(), myComparator);
     530              :                         } else {
     531              :                             std::push_heap(this->myFrontierList.begin(), fi + 1, myComparator);
     532              :                         }
     533              :                     }
     534              :                 }
     535              :             } // for followers
     536              :         }
     537            0 :         this->endQuery(num_visited);
     538              : #ifdef AFRO_DEBUG_LEVEL_1
     539              :         std::cout << "Queue ran empty, no solution." << std::endl;
     540              :         std::cout << static_cast<double>(numberOfFollowers - numberOfAvoidedFollowers) / static_cast<double>(num_visited)
     541              :                   << " followers considered (out of " << static_cast<double>(numberOfFollowers) / static_cast<double>(num_visited) << ") on average." << std::endl;
     542              :         std::cout << static_cast<double>(numberOfFollowers - numberOfAvoidedFollowers)
     543              :                   << " followers considered (out of " << static_cast<double>(numberOfFollowers) << ")." << std::endl;
     544              :         std::cout << numberOfEmptyFlagVectors << " out of " << numberOfFollowers << " flag vectors of followers were unassigned (i.e., empty)." << std::endl;
     545              :         std::cout << "num_visited: " << num_visited << std::endl;
     546              : #endif
     547            0 :         if (!silent) {
     548            0 :             this->myErrorMsgHandler->informf("No connection between edge '%' and edge '%' found.", from->getID(), to->getID());
     549              :         }
     550              :         return false;
     551              :     }
     552              : 
     553              :     /// @brief Start timer for query time sum
     554              :     void startQuery();
     555              :     /// @brief Stop timer for query time sum
     556              :     void endQuery(int visits);
     557              :     /// @brief Report query time statistics
     558              :     void reportStatistics();
     559              :     /// @brief Reset query time statistics
     560              :     void resetStatistics();
     561              :     /// @brief Bulk mode is not supported
     562            0 :     virtual void setBulkMode(const bool mode) {
     563              :         UNUSED_PARAMETER(mode);
     564            0 :         throw std::runtime_error("Bulk mode is not supported by the arc flag router.");
     565              :     }
     566              : 
     567              : protected:
     568              :     /// @brief Edge infos containing the associated edge and its arc flags
     569              :     std::vector<FlagInfo*>* myFlagInfos;
     570              :     /// @brief The partition
     571              :     const KDTreePartition<E, N, V>* myPartition;
     572              :     /// @brief The comparator for edge information
     573              :     EdgeInfoComparator myComparator;
     574              :     /// @brief The lookup table for travel time heuristics
     575              :     const std::shared_ptr<const LookupTable> myLookupTable;
     576              :     /// @brief The maximum speed in the network
     577              :     double myMaxSpeed;
     578              :     /// @brief The validity duration of one weight interval
     579              :     const SUMOTime myWeightPeriod;
     580              :     /// @brief The validity duration of the current flag infos (exclusive)
     581              :     SUMOTime myValidUntil;
     582              :     /// @brief The builder
     583              :     AFBuilder<E, N, V, M>* myBuilder;
     584              :     /// @brief The type of this router
     585              :     /// @note The one in SUMOAbstractRouter is private, required for more flexible performance logging (see below)
     586              :     const std::string myType;
     587              :     /// @brief Counters for performance logging
     588              :     /// @note The ones in SUMOAbstractRouter are private - introduced to reset stats / recalculate before destruction
     589              :     long long int myQueryVisits;
     590              :     long long int myNumQueries;
     591              :     /// @brief The time spent querying in milliseconds
     592              :     /// @note The ones in SUMOAbstractRouter are private - introduced to reset stats / recalculate before destruction
     593              :     long long int myQueryStartTime;
     594              :     long long int myQueryTimeSum;
     595              : #ifdef AFRO_DEBUG_LEVEL_2
     596              :     /// @brief The time spent for flagContext in milliseconds
     597              :     long long int myFlagContextStartTime;
     598              :     long long int myFlagContextTimeSum;
     599              : #endif
     600              : private:
     601              :     /// @brief The cell of the last settled edge
     602              :     const Cell* myLastSettledEdgeCell;
     603              :     /// @brief The last flag context
     604              :     std::tuple<int, int, bool> myLastFlagContext;
     605              :     /// @brief The cell of the target edge at SHARC level 0
     606              :     const Cell* myTargetEdgeCellLevel0;
     607              : };
     608              : 
     609              : // ===========================================================================
     610              : // method definitions
     611              : // ===========================================================================
     612              : 
     613              : template<class E, class N, class V, class M>
     614              : std::vector<bool>& AFRouter<E, N, V, M>::flags(const E* edge) {
     615              :     assert(edge);
     616              :     if (!myFlagInfos) {
     617              :         throw std::runtime_error("flag infos not initialized, call compute() at least once before calling flags().");
     618              :     }
     619              :     return ((*myFlagInfos)[edge->getNumericalID()])->arcFlags;
     620              : }
     621              : 
     622              : template<class E, class N, class V, class M>
     623            0 : void AFRouter<E, N, V, M>::init(const int edgeID, const SUMOTime msTime) {
     624              :     // all EdgeInfos touched in the previous query are either in myFrontierList or myFound: clean those up
     625            0 :     myTargetEdgeCellLevel0 = nullptr;
     626            0 :     for (auto& edgeInfo : this->myFrontierList) {
     627            0 :         edgeInfo->reset();
     628              :     }
     629              :     this->myFrontierList.clear();
     630            0 :     for (auto& edgeInfo : this->myFound) {
     631            0 :         edgeInfo->reset();
     632              :     }
     633              :     this->myFound.clear();
     634            0 :     if (edgeID > -1) {
     635              :         // add begin node
     636            0 :         auto& fromInfo = this->myEdgeInfos[edgeID];
     637            0 :         fromInfo.heuristicEffort = 0.;
     638            0 :         fromInfo.effort = 0.;
     639            0 :         fromInfo.leaveTime = STEPS2TIME(msTime);
     640            0 :         fromInfo.prev = nullptr;
     641            0 :         this->myFrontierList.push_back(&fromInfo);
     642              :     }
     643            0 : }
     644              : 
     645              : template<class E, class N, class V, class M>
     646              : std::tuple<int, int, bool> AFRouter<E, N, V, M>::flagContextNaive(const E* settledEdge, const E* targetEdge) {
     647              :     assert(settledEdge != nullptr && targetEdge != nullptr);
     648              :     int sHARCLevel;
     649              :     for (sHARCLevel = 0; sHARCLevel < myPartition->getNumberOfLevels() - 1; sHARCLevel++) {
     650              :         int partitionLevel = sHARCLevel2PartitionLevel(sHARCLevel, myPartition->getNumberOfLevels());
     651              :         const std::vector<const Cell*>& levelCells = myPartition->getCellsAtLevel(partitionLevel);
     652              :         typename std::vector<const Cell*>::const_iterator first = levelCells.begin();
     653              :         typename std::vector<const Cell*>::const_iterator last = levelCells.end();
     654              :         typename std::vector<const Cell*>::const_iterator iter;
     655              :         const Cell* settledEdgeCell = nullptr;
     656              :         const Cell* targetEdgeCell = nullptr;
     657              :         // go through the cells of the level
     658              :         for (iter = first; iter != last; iter++) {
     659              :             // myPartition is assumed to partition a non-reversed (forward) graph
     660              :             if (!settledEdgeCell && (*iter)->contains(settledEdge->getFromJunction())) {
     661              :                 settledEdgeCell = *iter;
     662              :             }
     663              :             if (!targetEdgeCell && (*iter)->contains(targetEdge->getFromJunction())) {
     664              :                 targetEdgeCell = *iter;
     665              :             }
     666              :             if (settledEdgeCell && targetEdgeCell) {
     667              :                 break;
     668              :             }
     669              :         }
     670              :         assert(settledEdgeCell && targetEdgeCell); // we should find both edges on each level
     671              :         if (settledEdgeCell->getSupercell() == targetEdgeCell->getSupercell()) {
     672              :             return std::make_tuple(sHARCLevel, targetEdgeCell->isLeftOrLowerCell() ? 0 : 1,
     673              :                                    settledEdgeCell == targetEdgeCell);
     674              :         }
     675              :     }
     676              :     // we should never arrive here
     677              :     throw std::runtime_error("flagContext: relevant level could not be determined.");
     678              : }
     679              : 
     680              : template<class E, class N, class V, class M>
     681            0 : std::tuple<int, int, bool> AFRouter<E, N, V, M>::flagContext(const E* settledEdge, const E* targetEdge) {
     682              :     assert(settledEdge != nullptr && targetEdge != nullptr);
     683              :     int sHARCLevel = 0; // lowest level with smallest cells
     684              :     const Cell* settledEdgeCell = nullptr;
     685              :     const Cell* targetEdgeCell = nullptr;
     686            0 :     if (myLastSettledEdgeCell
     687            0 :             && myLastSettledEdgeCell->contains(settledEdge->getFromJunction())) {
     688              :         // exploit the partial locality of Dijkstra's algorithm: settled edge is still
     689              :         // in the same cell as the last one? Then we can simply return the
     690              :         // last flagContext tuple again.
     691            0 :         return myLastFlagContext;
     692              :     }
     693            0 :     int numberOfPartitionLevels = myPartition->getNumberOfLevels();
     694            0 :     if (numberOfPartitionLevels <= 4) { // small number of bottom cells -> go through them, no use of k-d tree
     695            0 :         int partitionLevel = sHARCLevel2PartitionLevel(sHARCLevel, myPartition->getNumberOfLevels());
     696            0 :         const std::vector<const Cell*>& levelCells = myPartition->getCellsAtLevel(partitionLevel);
     697              :         typename std::vector<const Cell*>::const_iterator first = levelCells.begin();
     698              :         typename std::vector<const Cell*>::const_iterator last = levelCells.end();
     699              :         typename std::vector<const Cell*>::const_iterator iter;
     700              :         // go through the cells of the level
     701            0 :         for (iter = first; iter != last; iter++) {
     702              :             // myPartition is assumed to partition a non-reversed (forward) graph
     703              :             if (!settledEdgeCell
     704            0 :                     && (*iter)->contains(settledEdge->getFromJunction())) {
     705              :                 settledEdgeCell = *iter;
     706              :             }
     707            0 :             if (!targetEdgeCell && myTargetEdgeCellLevel0) {
     708              :                 targetEdgeCell = myTargetEdgeCellLevel0;
     709              :             } else if (!targetEdgeCell
     710            0 :                        && (*iter)->contains(targetEdge->getFromJunction())) {
     711            0 :                 myTargetEdgeCellLevel0 = *iter;
     712              :                 targetEdgeCell = myTargetEdgeCellLevel0;
     713              :             }
     714            0 :             if (settledEdgeCell && targetEdgeCell) {
     715              :                 assert(myTargetEdgeCellLevel0);
     716              :                 break;
     717              :             }
     718              :         }
     719              :     } else { // larger number of bottom cells -> use a k-d tree
     720            0 :         settledEdgeCell = myPartition->searchNode(settledEdge->getFromJunction());
     721            0 :         if (!targetEdgeCell && myTargetEdgeCellLevel0) {
     722              :             // search only once per query
     723              :             targetEdgeCell = myTargetEdgeCellLevel0;
     724              :         } else if (!targetEdgeCell) {
     725            0 :             myTargetEdgeCellLevel0 = myPartition->searchNode(targetEdge->getFromJunction());
     726              :             targetEdgeCell = myTargetEdgeCellLevel0; // myTargetEdgeCellLevel0 is reset in init()
     727              :         }
     728              :     }
     729              :     assert(settledEdgeCell && targetEdgeCell); // we should find both edges on each level
     730            0 :     while (settledEdgeCell->getSupercell() != targetEdgeCell->getSupercell()) {
     731              :         settledEdgeCell = settledEdgeCell->getSupercell();
     732              :         targetEdgeCell = targetEdgeCell->getSupercell();
     733            0 :         sHARCLevel++;
     734              :     }
     735            0 :     myLastSettledEdgeCell = settledEdgeCell;
     736            0 :     std::tuple<int, int, bool> flagContext = std::make_tuple(sHARCLevel, targetEdgeCell->isLeftOrLowerCell() ? 0 : 1,
     737            0 :             settledEdgeCell == targetEdgeCell);
     738              :     myLastFlagContext = flagContext;
     739              :     return flagContext;
     740              : }
     741              : 
     742              : template<class E, class N, class V, class M>
     743            0 : void AFRouter<E, N, V, M>::startQuery() {
     744            0 :     myNumQueries++;
     745            0 :     myQueryStartTime = SysUtils::getCurrentMillis();
     746              :     SUMOAbstractRouter<E, V>::startQuery();
     747            0 : }
     748              : 
     749              : template<class E, class N, class V, class M>
     750            0 : void AFRouter<E, N, V, M>::endQuery(int visits) {
     751            0 :     myQueryVisits += visits;
     752            0 :     myQueryTimeSum += (SysUtils::getCurrentMillis() - myQueryStartTime);
     753              :     SUMOAbstractRouter<E, V>::endQuery(visits);
     754            0 : }
     755              : 
     756              : template<class E, class N, class V, class M>
     757              : void AFRouter<E, N, V, M>::reportStatistics() {
     758              :     if (myNumQueries > 0) {
     759              :         WRITE_MESSAGE(myType + " answered " + toString(myNumQueries) + " queries and explored " + toString((double)myQueryVisits / (double)myNumQueries) + " edges on average.");
     760              :         WRITE_MESSAGE(myType + " spent " + elapsedMs2string(myQueryTimeSum) + " answering queries (" + toString((double)myQueryTimeSum / (double)myNumQueries) + " ms on average).");
     761              : #ifdef AFRO_DEBUG_LEVEL_2
     762              :         WRITE_MESSAGE("flagContext spent " + elapsedMs2string(myFlagContextTimeSum) + " (" + toString((double)myFlagContextTimeSum / (double)myNumQueries) + " ms on average).");
     763              : #endif
     764              :     }
     765              : }
     766              : 
     767              : template<class E, class N, class V, class M>
     768              : void AFRouter<E, N, V, M>::resetStatistics() {
     769              :     myNumQueries = 0;
     770              :     myQueryVisits = 0;
     771              :     myQueryTimeSum = 0;
     772              :     myQueryStartTime = 0;
     773              : }
        

Generated by: LCOV version 2.0-1