LCOV - code coverage report
Current view: top level - src/mesosim - METriggeredCalibrator.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 92.6 % 108 100
Test Date: 2024-11-20 15:55:46 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) 2001-2024 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    METriggeredCalibrator.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @date    Tue, May 2005
      17              : ///
      18              : // Calibrates the flow on a segment to a specified one
      19              : /****************************************************************************/
      20              : #include <config.h>
      21              : 
      22              : #include <string>
      23              : #include <algorithm>
      24              : #include <cmath>
      25              : #include <microsim/MSGlobals.h>
      26              : #include <microsim/MSNet.h>
      27              : #include <microsim/MSEdge.h>
      28              : #include <microsim/MSEventControl.h>
      29              : #include <microsim/MSVehicleControl.h>
      30              : #include <microsim/output/MSRouteProbe.h>
      31              : #include <utils/xml/SUMOXMLDefinitions.h>
      32              : #include <utils/common/ToString.h>
      33              : #include <utils/common/UtilExceptions.h>
      34              : #include <utils/common/StringTokenizer.h>
      35              : #include <utils/xml/XMLSubSys.h>
      36              : #include <utils/common/StringUtils.h>
      37              : #include <utils/options/OptionsCont.h>
      38              : #include <utils/vehicle/SUMOVehicleParserHelper.h>
      39              : #include <utils/distribution/RandomDistributor.h>
      40              : #include <utils/vehicle/SUMOVehicleParameter.h>
      41              : #include "MELoop.h"
      42              : #include "MESegment.h"
      43              : #include "MEVehicle.h"
      44              : #include "METriggeredCalibrator.h"
      45              : 
      46              : 
      47              : // ===========================================================================
      48              : // method definitions
      49              : // ===========================================================================
      50           84 : METriggeredCalibrator::METriggeredCalibrator(const std::string& id,
      51              :         MSEdge* const edge, const double pos,
      52              :         const std::string& aXMLFilename,
      53              :         const std::string& outputFilename,
      54              :         const SUMOTime freq, const double length,
      55              :         const MSRouteProbe* probe,
      56              :         const double invalidJamThreshold,
      57           84 :         const std::string& vTypes) :
      58              :     MSCalibrator(id, edge, nullptr, nullptr, pos, aXMLFilename, outputFilename, freq, length, probe, invalidJamThreshold, vTypes, false, false),
      59           84 :     mySegment(edge == nullptr ? nullptr : MSGlobals::gMesoNet->getSegmentForEdge(*edge, pos)) {
      60           84 :     myEdgeMeanData.setDescription("meandata_calibrator_" + getID());
      61           84 :     if (mySegment != nullptr) {
      62           80 :         mySegment->addDetector(&myEdgeMeanData);
      63              :     }
      64           84 : }
      65              : 
      66              : 
      67          168 : METriggeredCalibrator::~METriggeredCalibrator() {
      68           84 :     if (myCurrentStateInterval != myIntervals.end()) {
      69              :         // need to do it here and not in MSCalibrator because otherwise meandata is gone
      70           24 :         intervalEnd();
      71              :         // but avoid to call it again in MSCalibrator
      72           24 :         myCurrentStateInterval = myIntervals.end();
      73              :     }
      74              :     // TODO this is just commented out to work around https://github.com/eclipse/sumo/issues/7861
      75              :     //mySegment->removeDetector(&myEdgeMeanData);
      76          168 : }
      77              : 
      78              : 
      79              : bool
      80        13536 : METriggeredCalibrator::tryEmit(MESegment* s, MEVehicle* vehicle) {
      81        13536 :     if (s->initialise(vehicle, vehicle->getParameter().depart)) {
      82        13322 :         if (!MSNet::getInstance()->getVehicleControl().addVehicle(vehicle->getID(), vehicle)) {
      83            0 :             throw ProcessError("Emission of vehicle '" + vehicle->getID() + "' in calibrator '" + getID() + "'failed!");
      84              :         }
      85              :         return true;
      86              :     }
      87              :     return false;
      88              : }
      89              : 
      90              : 
      91              : SUMOTime
      92       100184 : METriggeredCalibrator::execute(SUMOTime currentTime) {
      93              :     // get current simulation values (valid for the last simulation second)
      94              :     // XXX could we miss vehicle movements if this is called less often than every DELTA_T (default) ?
      95       100184 :     mySegment->prepareDetectorForWriting(myEdgeMeanData);
      96              : 
      97              :     // check whether an adaptation value exists
      98       100184 :     if (isCurrentStateActive(currentTime)) {
      99              :         // all happens in isCurrentStateActive()
     100        82068 :         myAmActive = true;
     101              :     } else {
     102        18116 :         myAmActive = false;
     103        18116 :         myEdgeMeanData.reset(); // discard collected values
     104        18116 :         if (!mySpeedIsDefault) {
     105              :             // if not, reset adaptation values
     106           19 :             const double jamThresh = OptionsCont::getOptions().getFloat("meso-jam-threshold");
     107           19 :             myEdge->setMaxSpeed(myDefaultSpeed, jamThresh);
     108           19 :             mySpeedIsDefault = true;
     109              :         }
     110        18116 :         if (myCurrentStateInterval == myIntervals.end()) {
     111              :             // keep calibrator alive but do not call again
     112              :             return TIME2STEPS(86400);
     113              :         }
     114        18058 :         return myFrequency;
     115              :     }
     116        82068 :     const bool calibrateFlow = myCurrentStateInterval->q >= 0;
     117        82068 :     const bool calibrateSpeed = myCurrentStateInterval->v >= 0;
     118              :     // we are active
     119        82068 :     if (!myDidSpeedAdaption && calibrateSpeed && myCurrentStateInterval->v != mySegment->getEdge().getSpeedLimit()) {
     120           24 :         myEdge->setMaxSpeed(myCurrentStateInterval->v);
     121           24 :         mySpeedIsDefault = false;
     122           24 :         myDidSpeedAdaption = true;
     123              :     }
     124              :     // clear invalid jams
     125              :     bool hadInvalidJam = false;
     126        88308 :     while ((calibrateFlow || calibrateSpeed) && invalidJam()) {
     127              :         hadInvalidJam = true;
     128         6240 :         if (!myHaveWarnedAboutClearingJam) {
     129           24 :             WRITE_WARNINGF(TL("Clearing jam at calibrator '%' at time=%."), getID(), time2string(currentTime));
     130              :         }
     131              :         // remove one vehicle currently on the segment
     132         6240 :         if (mySegment->vaporizeAnyCar(currentTime, this)) {
     133         6240 :             myClearedInJam++;
     134              :         } else {
     135            0 :             if (!myHaveWarnedAboutClearingJam) {
     136              :                 // this frequenly happens for very short edges
     137            0 :                 WRITE_WARNINGF(TL("Could not clear jam at calibrator '%' at time=%."), getID(), time2string(currentTime));
     138              :             }
     139              :             break;
     140              :         }
     141         6240 :         myHaveWarnedAboutClearingJam = true;
     142              :     }
     143        82068 :     if (calibrateFlow) {
     144              :         // flow calibration starts here ...
     145              :         // compute the number of vehicles that should have passed the calibrator within the time
     146              :         // rom begin of the interval
     147        54200 :         const double totalHourFraction = STEPS2TIME(myCurrentStateInterval->end - myCurrentStateInterval->begin) / (double) 3600.;
     148        54200 :         const int totalWishedNum = (int)std::floor(myCurrentStateInterval->q * totalHourFraction + 0.5); // round to closest int
     149        54200 :         int adaptedNum = passed() + myClearedInJam;
     150        54200 :         if (!hadInvalidJam) {
     151              :             // only add vehicles if we do not have an invalid upstream jam to prevent spill-back
     152        50872 :             const double hourFraction = STEPS2TIME(currentTime - myCurrentStateInterval->begin + DELTA_T) / (double) 3600.;
     153        50872 :             const int wishedNum = (int)std::floor(myCurrentStateInterval->q * hourFraction + 0.5); // round to closest int
     154              :             // only the difference between inflow and aspiredFlow should be added, thus
     155              :             // we should not count vehicles vaporized from a jam here
     156              :             // if we have enough time left we can add missing vehicles later
     157        50872 :             const int relaxedInsertion = (int)std::floor(STEPS2TIME(myCurrentStateInterval->end - currentTime) / 3);
     158        50872 :             const int insertionSlack = MAX2(0, adaptedNum + relaxedInsertion - totalWishedNum);
     159              :             // increase number of vehicles
     160              :             //std::cout << "time:" << STEPS2TIME(currentTime) << " w:" << wishedNum << " s:" << insertionSlack << " before:" << adaptedNum;
     161        50872 :             MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
     162        64196 :             while (wishedNum > adaptedNum + insertionSlack && remainingVehicleCapacity() > maximumInflow()) {
     163        13548 :                 SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
     164        13548 :                 ConstMSRoutePtr route = myProbe != nullptr ? myProbe->sampleRoute() : nullptr;
     165        13548 :                 if (route == nullptr) {
     166        27096 :                     route = MSRoute::dictionary(pars->routeid);
     167              :                 }
     168        13548 :                 if (route == nullptr) {
     169            0 :                     WRITE_WARNINGF(TL("No valid routes in calibrator '%'."), getID());
     170            0 :                     break;
     171              :                 }
     172        13548 :                 if (!route->contains(myEdge)) {
     173            0 :                     WRITE_WARNINGF(TL("Route '%' in calibrator '%' does not contain edge '%'."), route->getID(), getID(), myEdge->getID());
     174            0 :                     break;
     175              :                 }
     176        13548 :                 MSVehicleType* vtype = vc.getVType(pars->vtypeid);
     177              :                 assert(route != 0 && vtype != 0);
     178              :                 // build the vehicle
     179        13548 :                 const SUMOTime depart = mySegment->getNextInsertionTime(currentTime);
     180        13548 :                 SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
     181        13550 :                 newPars->id = getNewVehicleID();
     182        13548 :                 newPars->depart = depart;
     183        13548 :                 newPars->routeid = route->getID();
     184              :                 MEVehicle* vehicle;
     185              :                 try {
     186        27096 :                     vehicle = static_cast<MEVehicle*>(vc.buildVehicle(newPars, route, vtype, false, MSVehicleControl::VehicleDefinitionSource::TRIGGER));
     187              :                     std::string msg;
     188        13548 :                     if (!vehicle->hasValidRouteStart(msg)) {
     189           20 :                         throw ProcessError(msg);
     190              :                     }
     191           10 :                 } catch (const ProcessError& e) {
     192           10 :                     if (!MSGlobals::gCheckRoutes) {
     193            8 :                         WRITE_WARNING(e.what());
     194              :                         vehicle = nullptr;
     195              :                         break;
     196              :                     } else {
     197            4 :                         throw e;
     198              :                     }
     199           10 :                 }
     200        13538 :                 const bool duplicate = vc.getVehicle(newPars->id) != nullptr;
     201              :                 // duplicate ids could come from loading state
     202        13540 :                 if (duplicate) {
     203            2 :                     vc.deleteVehicle(vehicle, true);
     204              :                     continue;
     205              :                 }
     206        13536 :                 vehicle->setSegment(mySegment); // needed or vehicle will not be registered (XXX why?)
     207              :                 vehicle->setEventTime(currentTime); // XXX superfluous?
     208              :                 // move vehicle forward when the route does not begin at the calibrator's edge
     209        13536 :                 const MSEdge* myedge = &mySegment->getEdge();
     210              :                 bool atDest = false;
     211        15504 :                 while (vehicle->getEdge() != myedge) {
     212              :                     // let the vehicle move to the next edge
     213         1968 :                     atDest = vehicle->moveRoutePointer();
     214              :                 }
     215              :                 // insert vehicle into the net
     216        13536 :                 if (atDest || !tryEmit(mySegment, vehicle)) {
     217              :                     //std::cout << "F ";
     218          214 :                     vc.deleteVehicle(vehicle, true);
     219              :                     break;
     220              :                 }
     221              :                 //std::cout << "I ";
     222        13322 :                 myInserted++;
     223        13322 :                 adaptedNum++;
     224              :             }
     225              :         }
     226              :         //std::cout << " after:" << adaptedNum << "\n";
     227              :         // we only remove vehicles once we really have to
     228        56106 :         while (totalWishedNum < adaptedNum) {
     229         2434 :             if (!mySegment->vaporizeAnyCar(currentTime, this)) {
     230              :                 // @bug: short edges may be jumped in a single step, giving us no chance to remove a vehicle
     231              :                 break;
     232              :             }
     233         1908 :             myRemoved++;
     234         1908 :             adaptedNum--;
     235              :         }
     236              :     }
     237        82066 :     if (myCurrentStateInterval->end <= currentTime + myFrequency) {
     238          135 :         intervalEnd();
     239              :     }
     240              :     //assert(!invalidJam());
     241        82066 :     if (invalidJam()) {
     242            0 :         WRITE_WARNINGF("DEBUG: Could not clear jam at calibrator '%' at time=%.", getID(), time2string(currentTime));
     243              :     }
     244        82066 :     return myFrequency;
     245              : }
     246              : 
     247              : 
     248              : bool
     249       170374 : METriggeredCalibrator::invalidJam() const {
     250       340748 :     if (mySegment->getBruttoOccupancy() == 0.) {
     251              :         return false;
     252              :     }
     253              :     // maxSpeed reflects the calibration target
     254       146249 :     const bool toSlow = mySegment->getMeanSpeed() < myInvalidJamThreshold * mySegment->getEdge().getSpeedLimit();
     255       146249 :     return toSlow && remainingVehicleCapacity() < maximumInflow();
     256              : }
     257              : 
     258              : 
     259              : int
     260        76041 : METriggeredCalibrator::remainingVehicleCapacity() const {
     261        76041 :     const SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
     262        76041 :     const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(pars->vtypeid);
     263        76041 :     return mySegment->remainingVehicleCapacity(vtype->getLengthWithGap());
     264              : }
     265              : 
     266              : 
     267              : void
     268          159 : METriggeredCalibrator::reset() {
     269          159 :     myEdgeMeanData.reset();
     270          159 : }
     271              : 
     272              : 
     273              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1