LCOV - code coverage report
Current view: top level - src/microsim/lcmodels - MSLCHelper.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 107 107
Test Date: 2026-09-20 15:45:03 Functions: 100.0 % 8 8

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2013-2026 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    MSLCHelper.cpp
      15              : /// @author  Jakob Erdmann
      16              : /// @date    Fri, 19.06.2020
      17              : ///
      18              : // Common functions for lane change models
      19              : /****************************************************************************/
      20              : 
      21              : #include <microsim/MSEdge.h>
      22              : #include <microsim/MSLane.h>
      23              : #include <microsim/MSLink.h>
      24              : #include <microsim/MSVehicle.h>
      25              : #include <microsim/lcmodels/MSAbstractLaneChangeModel.h>
      26              : #include "MSLCHelper.h"
      27              : 
      28              : // ===========================================================================
      29              : // Debug flags
      30              : // ===========================================================================
      31              : //#define DEBUG_WANTS_CHANGE
      32              : //#define DEBUG_SAVE_BLOCKER_LENGTH
      33              : //#define DEBUG_UNWILLING_TO_HELP
      34              : 
      35              : #define DEBUG_COND (veh.isSelected())
      36              : //#define DEBUG_COND (true)
      37              : 
      38              : 
      39              : // ===========================================================================
      40              : // member method definitions
      41              : // ===========================================================================
      42              : 
      43              : double
      44    359866071 : MSLCHelper::getRoundaboutDistBonus(const MSVehicle& veh,
      45              :                                    double bonusParam,
      46              :                                    const MSVehicle::LaneQ& curr,
      47              :                                    const MSVehicle::LaneQ& neigh,
      48              :                                    const MSVehicle::LaneQ& best) {
      49    359866071 :     if (veh.getLaneChangeModel().isOpposite()) {
      50              :         return 0;
      51              :     }
      52    359481934 :     const MSVehicle::LaneQ& inner = neigh.lane->getIndex() > curr.lane->getIndex() ? neigh : curr;
      53              : #ifdef DEBUG_WANTS_CHANGE
      54              :     const bool debugVehicle = veh.getLaneChangeModel().debugVehicle();
      55              :     if (debugVehicle) {
      56              :         std::cout << SIMTIME << " veh=" << veh.getID() << " getRoundaboutDistBonus bonusParam=" << bonusParam
      57              :                   << " curr=" << curr.lane->getID()
      58              :                   << " neigh=" << neigh.lane->getID()
      59              :                   << " inner=" << inner.lane->getID()
      60              :                   << " best=" << best.lane->getID()
      61              :                   << "\n   innerCont=" << toString(inner.bestContinuations)
      62              :                   << "\n   bestCont=" << toString(best.bestContinuations)
      63              :                   << "\n";
      64              :     }
      65              : #endif
      66    359481934 :     if (neigh.lane == inner.lane && curr.bestContinuations.size() < neigh.bestContinuations.size()) {
      67              :         // the current lane does not continue to the roundabout and we need a strategic change first.
      68              :         return 0;
      69              :     }
      70              : 
      71              :     int roundaboutJunctionsAhead = 0;
      72              :     bool enteredRoundabout = false;
      73    356176241 :     double seen = -veh.getPositionOnLane();
      74              : 
      75              :     // first check using only normal lanes
      76    935026106 :     for (int i = 0; i < (int)best.bestContinuations.size(); i++) {
      77    719570865 :         const MSLane* lane = best.bestContinuations[i];
      78    719570865 :         if (lane == nullptr) {
      79      3539846 :             lane = veh.getLane();
      80              :         }
      81    719570865 :         if ((!enteredRoundabout || lane->getEdge().isRoundabout()) && i >= (int)inner.bestContinuations.size()) {
      82              :             // no bonus if we cannot continue on the inner lane until leaving the roundabout
      83              : #ifdef DEBUG_WANTS_CHANGE
      84              :             if (debugVehicle) {
      85              :                 std::cout << "   noBonus: inner does not continue (lane=" << lane->getID() << ")\n";
      86              :             }
      87              : #endif
      88              :             return 0;
      89              :         }
      90    699448406 :         if (seen > 300) {
      91              :             // avoid long look-ahead
      92              : #ifdef DEBUG_WANTS_CHANGE
      93              :             if (debugVehicle) {
      94              :                 std::cout << "   noBonus: seen=" << seen << " (lane=" << lane->getID() << ")\n";
      95              :             }
      96              : #endif
      97              :             return 0;
      98              :         }
      99              :         const MSJunction* junction = lane->getEdge().getToJunction();
     100    604825533 :         if (lane->getEdge().isRoundabout()) {
     101              :             enteredRoundabout = true;
     102     54956674 :             if (junction->getIncoming().size() + junction->getOutgoing().size() > 2) {
     103     54760179 :                 roundaboutJunctionsAhead++;
     104              :             }
     105    549868859 :         } else if (enteredRoundabout) {
     106              :             // only check the first roundabout
     107              :             break;
     108              :         }
     109    578849865 :         seen += lane->getLength();
     110              :     }
     111              :     // no bonus if we want to take the next exit
     112    241430909 :     if (roundaboutJunctionsAhead < 2) {
     113              : #ifdef DEBUG_WANTS_CHANGE
     114              :         if (debugVehicle) {
     115              :             std::cout << "   noBonus: roundaboutJunctionsAhead=" << roundaboutJunctionsAhead << "\n";
     116              :         }
     117              : #endif
     118              :         return 0;
     119              :     }
     120              :     // compute bonus value based on jamming and exact distances (taking into
     121              :     // account internal lanes)
     122              :     double occupancyOuter = 0;
     123              :     double occupancyInner = 0;
     124              :     double distanceInRoundabout = 0;
     125              :     MSLane* prevNormal = nullptr;
     126              :     MSLane* prevInner = nullptr;
     127              :     enteredRoundabout = false;
     128     74210557 :     for (int i = 0; i < (int)best.bestContinuations.size(); i++) {
     129     74085123 :         MSLane* lane = best.bestContinuations[i];
     130     74085123 :         if (lane == nullptr) {
     131       692620 :             continue;
     132              :         }
     133     73392503 :         if (lane->getEdge().isRoundabout()) {
     134              :             enteredRoundabout = true;
     135     31985634 :         } else if (enteredRoundabout) {
     136              :             // only check the first roundabout
     137              :             break;
     138              :         }
     139              :         MSLane* via = nullptr;
     140     56870047 :         if (prevNormal != nullptr) {
     141    123852577 :             for (MSLink* link : prevNormal->getLinkCont()) {
     142     83630420 :                 if (link->getLane() == lane) {
     143              :                     via = link->getViaLane();
     144              :                 }
     145              :             }
     146              :         }
     147     56870047 :         if (enteredRoundabout) {
     148     41406869 :             distanceInRoundabout += lane->getLength();
     149     41406869 :             if (via != nullptr) {
     150     39748425 :                 distanceInRoundabout += via->getLength();
     151              :             }
     152              :         }
     153              :         // discount vehicles that are upstream from ego
     154     56870047 :         const double upstreamDiscount = &lane->getEdge() == &veh.getLane()->getEdge()
     155     56870047 :                                         ? (lane->getLength() - veh.getPositionOnLane()) / lane->getLength() : 1;
     156              :         prevNormal = lane;
     157     56870047 :         occupancyOuter += upstreamDiscount * lane->getBruttoVehLenSum();
     158              : #ifdef DEBUG_WANTS_CHANGE
     159              :         if (debugVehicle) {
     160              :             std::cout << " lane=" << lane->getID() << " occ=" << lane->getBruttoVehLenSum() << " discount=" << upstreamDiscount << " outer=" << occupancyOuter << "\n";
     161              :         }
     162              : #endif
     163     56870047 :         if (via != nullptr) {
     164     40087940 :             occupancyOuter += via->getBruttoVehLenSum();
     165              : #ifdef DEBUG_WANTS_CHANGE
     166              :             if (debugVehicle) {
     167              :                 std::cout << " via=" << via->getID() << " occ=" << via->getBruttoVehLenSum() << " outer=" << occupancyOuter << "\n";
     168              :             }
     169              : #endif
     170              :         }
     171     56870047 :         if (i < (int)inner.bestContinuations.size()) {
     172     56870047 :             MSLane* innerLane = inner.bestContinuations[i];
     173     56870047 :             occupancyInner += upstreamDiscount * innerLane->getBruttoVehLenSum();
     174              : #ifdef DEBUG_WANTS_CHANGE
     175              :             if (debugVehicle) {
     176              :                 std::cout << " inner=" << innerLane->getID() << " occ=" << innerLane->getBruttoVehLenSum() << " discount=" << upstreamDiscount << " inner=" << occupancyInner << "\n";
     177              :             }
     178              : #endif
     179     56870047 :             if (prevInner != nullptr) {
     180     88494942 :                 for (MSLink* link : prevInner->getLinkCont()) {
     181     48272785 :                     if (link->getLane() == innerLane && link->getViaLane() != nullptr) {
     182     40087940 :                         occupancyInner += link->getViaLane()->getBruttoVehLenSum();
     183              : #ifdef DEBUG_WANTS_CHANGE
     184              :                         if (debugVehicle) {
     185              :                             std::cout << " innerVia=" << link->getViaLane()->getID() << " occ=" << link->getViaLane()->getBruttoVehLenSum() << " inner=" << occupancyInner << "\n";
     186              :                         }
     187              : #endif
     188              :                     }
     189              :                 }
     190              :             }
     191              :             prevInner = innerLane;
     192              :         }
     193              :     }
     194              : 
     195              : #ifdef DEBUG_WANTS_CHANGE
     196              :     if (debugVehicle) {
     197              :         std::cout << "   distanceInRoundabout=" << distanceInRoundabout
     198              :                   << " roundaboutJunctionsAhead=" << roundaboutJunctionsAhead
     199              :                   << " occupancyInner=" << occupancyInner
     200              :                   << " occupancyOuter=" << occupancyOuter
     201              :                   << "\n";
     202              :     }
     203              : #endif
     204     16647890 :     if (abs(curr.bestLaneOffset) > 1 && enteredRoundabout) {
     205          134 :         const double bGap = veh.getCarFollowModel().brakeGap(veh.getSpeed() + ACCEL2SPEED(veh.getCarFollowModel().getMaxAccel()), veh.getCarFollowModel().getMaxDecel(), veh.getActionStepLengthSecs());
     206          134 :         const double reservation = veh.getLaneChangeModel().getExtraReservation(curr.bestLaneOffset);
     207          134 :         const double leftSpace = distanceInRoundabout - reservation - veh.getPositionOnLane();
     208              : #ifdef DEBUG_WANTS_CHANGE
     209              :         if (debugVehicle) {
     210              :             std::cout << "   bGap=" << bGap << " reserving=" << reservation << " leftover=" << (leftSpace - bGap) << "\n";
     211              :         }
     212              : #endif
     213          134 :         if (bGap > leftSpace) {
     214              :             return 0;
     215              :         }
     216              :     }
     217              : 
     218              :     const double maxOccupancy = MAX2(occupancyInner, occupancyOuter);
     219              :     // give some bonus for using the inside lane at equal occupancy
     220     16647884 :     const double bonus = roundaboutJunctionsAhead * 7.5;
     221     16647884 :     const double relativeJam = (occupancyOuter - occupancyInner + bonus) / (maxOccupancy + bonus);
     222              :     // no bonus if the inner lane or the left lane entering the roundabout is jammed
     223              :     double jamFactor = MAX2(0.0, relativeJam);
     224     16647884 :     if (veh.getLane()->getEdge().isRoundabout() && curr.lane->getIndex() > neigh.lane->getIndex()) {
     225              :         // only use jamFactor when deciding to move to the inside lane but prefer
     226              :         // staying inside if the distance allows it
     227              :         jamFactor = 1;
     228              :     }
     229     16647884 :     const double result = distanceInRoundabout * jamFactor * bonusParam * 9; // the 9 is abitrary and only there for backward compatibility
     230              : #ifdef DEBUG_WANTS_CHANGE
     231              :     if (debugVehicle) {
     232              :         std::cout << "   relativeJam=" << relativeJam
     233              :                   << " jamFactor=" << jamFactor
     234              :                   << " distanceBonus=" << result
     235              :                   << "\n";
     236              :     }
     237              : #endif
     238     16647884 :     return result;
     239              : }
     240              : 
     241              : 
     242              : bool
     243     11702733 : MSLCHelper::updateBlockerLength(const MSVehicle& veh,  MSVehicle* blocker, int lcaCounter, double leftSpace, bool reliefConnection, double& leadingBlockerLength) {
     244              : #ifdef DEBUG_SAVE_BLOCKER_LENGTH
     245              :     if (DEBUG_COND) {
     246              :         std::cout << SIMTIME
     247              :                   << " veh=" << veh.getID()
     248              :                   << " saveBlockerLength blocker=" << Named::getIDSecure(blocker)
     249              :                   << " bState=" << (blocker == 0 ? "None" : toString((LaneChangeAction)blocker->getLaneChangeModel().getOwnState()))
     250              :                   << "\n";
     251              :     }
     252              : #endif
     253     11702733 :     if (blocker != nullptr && (blocker->getLaneChangeModel().getOwnState() & lcaCounter) != 0) {
     254              :         // is there enough space in front of us for the blocker?
     255       519972 :         const double required = blocker->getVehicleType().getLengthWithGap() + veh.getVehicleType().getMinGap();
     256       519972 :         const double potential = leftSpace - veh.getCarFollowModel().brakeGap(
     257       519972 :                                      veh.getSpeed(), veh.getCarFollowModel().getMaxDecel(), 0);
     258       519972 :         if (required <= potential) {
     259              :             // save at least his length in myLeadingBlockerLength
     260       491175 :             leadingBlockerLength = MAX2(required, leadingBlockerLength);
     261              : #ifdef DEBUG_SAVE_BLOCKER_LENGTH
     262              :             if (DEBUG_COND) {
     263              :                 std::cout << SIMTIME
     264              :                           << " veh=" << veh.getID()
     265              :                           << " required=" << required
     266              :                           << " potential=" << potential
     267              :                           << " blocker=" << Named::getIDSecure(blocker)
     268              :                           << " saving myLeadingBlockerLength=" << leadingBlockerLength
     269              :                           << "\n";
     270              :             }
     271              : #endif
     272              :         } else {
     273              :             // we cannot save enough space for the blocker. It needs to save
     274              :             // space for ego instead
     275        81342 :             const double required2 = veh.getVehicleType().getLengthWithGap() + blocker->getVehicleType().getMinGap();
     276        81342 :             const double foeLeftSpace = leftSpace - veh.getPositionOnLane() + blocker->getPositionOnLane() - POSITION_EPS;
     277        81342 :             const bool canReserve = blocker->getLaneChangeModel().saveBlockerLength(required2, foeLeftSpace);
     278              :             //reliefConnection ? std::numeric_limits<double>::max() : leftSpace);
     279              : #ifdef DEBUG_SAVE_BLOCKER_LENGTH
     280              :             if (DEBUG_COND) {
     281              :                 std::cout << SIMTIME
     282              :                           << " veh=" << veh.getID()
     283              :                           << " required=" << required
     284              :                           << " potential=" << potential
     285              :                           << " blocker=" << Named::getIDSecure(blocker)
     286              :                           << " required2=" << required2
     287              :                           << " foeCanReserve=" << canReserve
     288              :                           << " myReserved=" << leadingBlockerLength
     289              :                           << " reliefConnection=" << reliefConnection
     290              :                           << "\n";
     291              :             }
     292              : #endif
     293        81342 :             if (!canReserve && !reliefConnection) {
     294        41754 :                 const int blockerState = blocker->getLaneChangeModel().getOwnState();
     295        41754 :                 if ((blockerState & LCA_STRATEGIC) != 0
     296              :                         && (blockerState & LCA_URGENT) != 0) {
     297              :                     // reserve anyway and try to avoid deadlock with emergency deceleration
     298        38611 :                     leadingBlockerLength = MAX2(required, leadingBlockerLength);
     299              : #ifdef DEBUG_SAVE_BLOCKER_LENGTH
     300              :                     if (DEBUG_COND) {
     301              :                         std::cout << "   reserving anyway to avoid deadlock (will cause emergency braking)\n";
     302              :                     }
     303              : #endif
     304              :                 }
     305              :             }
     306        81342 :             return canReserve;
     307              :         }
     308              :     }
     309              :     return true;
     310              : }
     311              : 
     312              : 
     313              : bool
     314       208729 : MSLCHelper::canSaveBlockerLength(const MSVehicle& veh, double requested, double leftSpace) {
     315       208729 :     const double potential = leftSpace - veh.getCarFollowModel().brakeGap(veh.getSpeed(), veh.getCarFollowModel().getMaxDecel(), veh.getActionStepLengthSecs());
     316              : #ifdef DEBUG_SAVE_BLOCKER_LENGTH
     317              :     if (DEBUG_COND) {
     318              :         std::cout << SIMTIME << " canSaveBlockerLength veh=" << veh.getID() << " requested=" << requested << " leftSpace=" << leftSpace << " potential=" << potential << "\n";
     319              :     }
     320              : #endif
     321       208729 :     return potential >= requested;
     322              : }
     323              : 
     324              : 
     325              : bool
     326     15438120 : MSLCHelper::divergentRoute(const MSVehicle& v1, const MSVehicle& v2) {
     327              :     // a sufficient, but not necessary condition for divergence
     328     15672737 :     return (v1.getLane()->isInternal() && v2.getLane()->isInternal()
     329       158253 :             && v1.getLane()->getEdge().getFromJunction() == v2.getLane()->getEdge().getFromJunction()
     330     15584814 :             && &v1.getLane()->getEdge() != &v2.getLane()->getEdge());
     331              : }
     332              : 
     333              : 
     334              : double
     335      2200471 : MSLCHelper::getSpeedPreservingSecureGap(const MSVehicle& leader, const MSVehicle& follower, double currentGap, double leaderPlannedSpeed) {
     336              :     // whatever speed the follower choses in the next step, it will change both
     337              :     // the secureGap and the required followSpeed.
     338              :     // Let's assume the leader maintains speed
     339      2200471 :     const double nextGap = currentGap + SPEED2DIST(leaderPlannedSpeed - follower.getSpeed());
     340      2200471 :     double sGap = follower.getCarFollowModel().getSecureGap(&follower, &leader, follower.getSpeed(), leaderPlannedSpeed, leader.getCarFollowModel().getMaxDecel());
     341      2200471 :     if (nextGap >= sGap) {
     342              :         // follower may still accelerate
     343      1966775 :         const double nextGapMin = currentGap + SPEED2DIST(leaderPlannedSpeed - follower.getCarFollowModel().maxNextSpeed(follower.getSpeed(), &follower));
     344      1966775 :         const double vSafe = follower.getCarFollowModel().followSpeed(
     345      1966775 :                                  &follower, follower.getSpeed(), nextGapMin, leaderPlannedSpeed, leader.getCarFollowModel().getMaxDecel());
     346      1966775 :         return MAX2(vSafe, follower.getSpeed());
     347              :     } else {
     348              :         // follower must brake. The following brakes conservatively since the actual gap will be lower due to braking.
     349       233696 :         const double vSafe = follower.getCarFollowModel().followSpeed(
     350       233696 :                                  &follower, follower.getSpeed(), nextGap, leaderPlannedSpeed, leader.getCarFollowModel().getMaxDecel());
     351              :         // avoid emergency deceleration
     352       233696 :         return MAX2(vSafe, follower.getCarFollowModel().minNextSpeed(follower.getSpeed(), &follower));
     353              :     }
     354              : }
     355              : 
     356              : 
     357              : bool
     358    133680667 : MSLCHelper::isBidiLeader(const MSVehicle* leader, const std::vector<MSLane*>& cont) {
     359    133680667 :     if (leader == nullptr) {
     360              :         return false;
     361              :     }
     362    125810427 :     const MSLane* lane1 = leader->getLane()->getNormalSuccessorLane()->getBidiLane();
     363    125810427 :     const MSLane* lane2 = leader->getLane()->getNormalPredecessorLane()->getBidiLane();
     364    125810427 :     if (lane1 == nullptr && lane2 == nullptr) {
     365              :         return false;
     366              :     }
     367      2366313 :     bool result = std::find(cont.begin(), cont.end(), lane1) != cont.end();
     368      2366313 :     if (!result && lane1 != lane2 && lane2 != nullptr) {
     369       528644 :         result = std::find(cont.begin(), cont.end(), lane2) != cont.end();
     370              :     }
     371              :     return result;
     372              : }
     373              : 
     374              : 
     375              : bool
     376        49761 : MSLCHelper::isBidiFollower(const MSVehicle* ego, const MSVehicle* follower) {
     377        49761 :     if (follower == nullptr) {
     378              :         return false;
     379              :     }
     380              :     bool result = false;
     381        38121 :     const MSLane* lane1 = follower->getLane()->getNormalSuccessorLane()->getBidiLane();
     382        38121 :     const MSLane* lane2 = follower->getLane()->getNormalPredecessorLane()->getBidiLane();
     383        38121 :     const ConstMSEdgeVector& route = ego->getRoute().getEdges();
     384        38121 :     if (lane1 != nullptr) {
     385        37402 :         result = std::find(route.begin(), route.end(), &lane1->getEdge()) != route.end();
     386              :     }
     387        38121 :     if (!result && lane1 != lane2 && lane2 != nullptr) {
     388         4562 :         result = std::find(route.begin(), route.end(), &lane2->getEdge()) != route.end();
     389              :     }
     390              :     return result;
     391              : }
     392              : 
     393              : 
     394              : bool
     395      8445211 : MSLCHelper::unwillingToHelp(const MSVehicle& ego, double plannedSpeed, const MSVehicle& nv) {
     396      8445211 :     if (nv.getLaneChangeModel().getCooperativeHelpTime() < 0 || ego.getWaitingTime() < nv.getLaneChangeModel().getCooperativeHelpTime()) {
     397              :         // ego vehicle has not been waiting long enough to be eligible for unconditional help
     398      7169104 :         if (nv.getLaneChangeModel().getCooperativeHelpThreshold() >= 0 && (nv.getSpeed() - plannedSpeed) > nv.getLaneChangeModel().getCooperativeHelpThreshold()) {
     399              :             // neighhbor not willing to help because ego is much slower
     400              : #ifdef DEBUG_UNWILLING_TO_HELP
     401              :             if (DEBUG_COND) {
     402              :                 std::cout << "\n nv=" << nv.getID() << " not willing to help because ego is much slower\n";
     403              :             }
     404              : #endif
     405              :             return true;
     406              :         }
     407      6968107 :         const double nvLaneMax = nv.getLane()->getVehicleMaxSpeed(&nv);
     408      6968107 :         if (nv.getSpeed() / nvLaneMax < nv.getLaneChangeModel().getCooperativeMinSpeed()) {
     409              :             // neighbor not willing to help because it is already to slow and does not want to disturb the flow
     410              : #ifdef DEBUG_UNWILLING_TO_HELP
     411              :             if (DEBUG_COND) {
     412              :                 std::cout << "\n nv=" << nv.getID() << " not willing to help because it is already too slow\n";
     413              :             }
     414              : #endif
     415              :             return true;
     416              :         }
     417              :     }
     418              :     return false;
     419              : }
     420              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1