LCOV - code coverage report
Current view: top level - src/microsim/trigger - MSCalibrator.cpp (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.6 % 371 336
Test Date: 2025-11-13 15:38:19 Functions: 92.3 % 26 24

            Line data    Source code
       1              : /****************************************************************************/
       2              : // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
       3              : // Copyright (C) 2005-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    MSCalibrator.cpp
      15              : /// @author  Daniel Krajzewicz
      16              : /// @author  Jakob Erdmann
      17              : /// @author  Michael Behrisch
      18              : /// @date    Tue, May 2005
      19              : ///
      20              : // Calibrates the flow on an edge by removing an inserting vehicles
      21              : /****************************************************************************/
      22              : #include <config.h>
      23              : 
      24              : #include <string>
      25              : #include <algorithm>
      26              : #include <cmath>
      27              : #include <microsim/MSNet.h>
      28              : #include <microsim/MSEdge.h>
      29              : #include <microsim/MSJunction.h>
      30              : #include <microsim/MSLane.h>
      31              : #include <microsim/MSEventControl.h>
      32              : #include <microsim/MSVehicleControl.h>
      33              : #include <microsim/output/MSRouteProbe.h>
      34              : #include <utils/xml/SUMOXMLDefinitions.h>
      35              : #include <utils/common/ToString.h>
      36              : #include <utils/common/UtilExceptions.h>
      37              : #include <utils/common/StringTokenizer.h>
      38              : #include <utils/xml/XMLSubSys.h>
      39              : #include <utils/common/StringUtils.h>
      40              : #include <utils/options/OptionsCont.h>
      41              : #include <utils/vehicle/SUMOVehicleParserHelper.h>
      42              : #include <utils/distribution/RandomDistributor.h>
      43              : #include <utils/vehicle/SUMOVehicleParameter.h>
      44              : #include "MSCalibrator.h"
      45              : 
      46              : //#define MSCalibrator_DEBUG
      47              : 
      48              : #define DEBUGID ""
      49              : #define DEBUGCOND (getID() == DEBUGID)
      50              : #define DEBUGCOND2(id) ((id) == DEBUGID)
      51              : 
      52              : // ===========================================================================
      53              : // static members
      54              : // ===========================================================================
      55              : std::vector<MSMoveReminder*> MSCalibrator::myLeftoverReminders;
      56              : std::vector<SUMOVehicleParameter*> MSCalibrator::myLeftoverVehicleParameters;
      57              : std::map<std::string, MSCalibrator*> MSCalibrator::myInstances;
      58              : 
      59              : // ===========================================================================
      60              : // CalibratorCommand method definitions
      61              : // ===========================================================================
      62              : 
      63              : SUMOTime
      64            5 : MSCalibrator::CalibratorCommand::shiftTime(SUMOTime currentTime, SUMOTime execTime, SUMOTime newTime) {
      65              :     UNUSED_PARAMETER(currentTime);
      66              :     UNUSED_PARAMETER(execTime);
      67              :     UNUSED_PARAMETER(newTime);
      68            5 :     myCalibrator->myCurrentStateInterval = myCalibrator->myIntervals.begin();
      69            5 :     return 0;
      70              : }
      71              : 
      72              : 
      73              : // ===========================================================================
      74              : // method definitions
      75              : // ===========================================================================
      76         1130 : MSCalibrator::MSCalibrator(const std::string& id,
      77              :                            MSEdge* const edge,
      78              :                            MSLane* const lane,
      79              :                            MSJunction* const node,
      80              :                            const double pos,
      81              :                            const std::string& aXMLFilename,
      82              :                            const std::string& outputFilename,
      83              :                            const SUMOTime freq, const double length,
      84              :                            const MSRouteProbe* probe,
      85              :                            const double invalidJamThreshold,
      86              :                            const std::string& vTypes,
      87              :                            const bool local,
      88         1130 :                            const bool addLaneMeanData) :
      89              :     MSRouteHandler(aXMLFilename, true),
      90              :     MSDetectorFileOutput(id, vTypes, "", (int)PersonMode::NONE), // detecting persons not yet supported
      91         1130 :     myEdge(edge),
      92         1130 :     myLane(lane),
      93         1130 :     myNode(node),
      94         1130 :     myPos(pos), myProbe(probe),
      95         2260 :     myMeanDataParent(id + "_dummyMeanData", 0, 0, false, false, false, false, false, false, 1, 0, 0, vTypes, "",
      96         1130 :                      std::vector<MSEdge*>(), false),
      97         1130 :     myEdgeMeanData(nullptr, length, false, &myMeanDataParent),
      98              :     myCurrentStateInterval(myIntervals.begin()),
      99         1130 :     myOutput(nullptr), myFrequency(freq), myRemoved(0),
     100         1130 :     myInserted(0),
     101         1130 :     myClearedInJam(0),
     102         1130 :     mySpeedIsDefault(true), myDidSpeedAdaption(false), myDidInit(false),
     103         1130 :     myDefaultSpeed(myLane == nullptr ? (myEdge == nullptr ? 0. : myEdge->getSpeedLimit()) : myLane->getSpeedLimit()),
     104         1130 :     myHaveWarnedAboutClearingJam(false),
     105         1130 :     myAmActive(false),
     106         1130 :     myInvalidJamThreshold(invalidJamThreshold),
     107         1130 :     myAmLocal(local),
     108         3390 :     myHaveInvalidJam(false) {
     109         1130 :     if (outputFilename != "") {
     110          267 :         myOutput = &OutputDevice::getDevice(outputFilename);
     111          267 :         writeXMLDetectorProlog(*myOutput);
     112              :     }
     113         1130 :     if (aXMLFilename != "") {
     114           49 :         XMLSubSys::runParser(*this, aXMLFilename);
     115           49 :         if (!myDidInit) {
     116            0 :             init();
     117              :         }
     118              :     }
     119         1130 :     myInstances[id] = this;
     120         1130 :     if (addLaneMeanData && myEdge != nullptr) {
     121              :         // disabled for METriggeredCalibrator
     122         2736 :         for (MSLane* const eLane : myEdge->getLanes()) {
     123         1702 :             if (myLane == nullptr || myLane == eLane) {
     124              :                 //std::cout << " cali=" << getID() << " myLane=" << Named::getIDSecure(myLane) << " checkLane=" << i << "\n";
     125         1172 :                 MSMeanData_Net::MSLaneMeanDataValues* laneData = new MSMeanData_Net::MSLaneMeanDataValues(eLane, eLane->getLength(), true, &myMeanDataParent);
     126         1172 :                 laneData->setDescription("meandata_calibrator_" + eLane->getID());
     127         1172 :                 myLeftoverReminders.push_back(laneData);
     128         1172 :                 myLaneMeanData.push_back(laneData);
     129         1172 :                 VehicleRemover* remover = new VehicleRemover(eLane, this);
     130         1172 :                 myLeftoverReminders.push_back(remover);
     131         1172 :                 myVehicleRemovers.push_back(remover);
     132              :             }
     133              :         }
     134              :     }
     135         1130 :     if (node != nullptr) {
     136           30 :         for (const MSEdge* inEdge : myNode->getIncoming()) {
     137           40 :             for (MSLane* const eLane : inEdge->getLanes()) {
     138           20 :                 VehicleRemover* remover = new VehicleRemover(eLane, this);
     139           20 :                 myLeftoverReminders.push_back(remover);
     140           20 :                 myVehicleRemovers.push_back(remover);
     141              :             }
     142              :         }
     143           10 :         if (local) {
     144           20 :             for (const MSEdge* outEdge : myNode->getOutgoing()) {
     145           30 :                 for (MSLane* const eLane : outEdge->getLanes()) {
     146           15 :                     VehicleRemover* remover = new VehicleRemover(eLane, this, true);
     147           15 :                     myLeftoverReminders.push_back(remover);
     148           15 :                     myVehicleRemovers.push_back(remover);
     149              :                 }
     150              :             }
     151              :         }
     152              :     }
     153         1130 : }
     154              : 
     155              : 
     156              : void
     157         1130 : MSCalibrator::init() {
     158         1130 :     if (myIntervals.size() > 0) {
     159          374 :         if (myIntervals.back().end == -1) {
     160            7 :             myIntervals.back().end = SUMOTime_MAX;
     161              :         }
     162              :         // calibration should happen after regular insertions have taken place
     163          374 :         MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(new CalibratorCommand(this));
     164              :     } else {
     165         2268 :         WRITE_WARNINGF(TL("No flow intervals in calibrator '%'."), getID());
     166              :     }
     167         1130 :     myDidInit = true;
     168         1130 : }
     169              : 
     170              : 
     171         2174 : MSCalibrator::~MSCalibrator() {
     172         1130 :     if (myCurrentStateInterval != myIntervals.end()) {
     173           92 :         intervalEnd();
     174              :     }
     175         2337 :     for (VehicleRemover* const remover : myVehicleRemovers) {
     176              :         remover->disable();
     177              :     }
     178              :     myInstances.erase(getID());
     179         2174 : }
     180              : 
     181              : 
     182              : MSCalibrator::AspiredState
     183          123 : MSCalibrator::getCurrentStateInterval() const {
     184          123 :     if (myCurrentStateInterval == myIntervals.end()) {
     185            0 :         throw ProcessError(TLF("Calibrator '%' has no active or upcoming interval", getID()));
     186              :     }
     187          123 :     return *myCurrentStateInterval;
     188              : }
     189              : 
     190              : 
     191              : void
     192          964 : MSCalibrator::myStartElement(int element,
     193              :                              const SUMOSAXAttributes& attrs) {
     194          964 :     if (element == SUMO_TAG_FLOW) {
     195              :         AspiredState state;
     196              :         SUMOTime lastEnd = -1;
     197              :         SUMOTime lastBegin = -1;
     198          682 :         if (myIntervals.size() > 0) {
     199          308 :             lastEnd = myIntervals.back().end;
     200          308 :             lastBegin = myIntervals.back().begin;
     201          308 :             if (lastEnd == -1) {
     202              :                 lastEnd = myIntervals.back().begin;
     203              :             }
     204              :         }
     205              :         try {
     206          682 :             bool ok = true;
     207          682 :             state.q = attrs.getOpt<double>(SUMO_ATTR_VEHSPERHOUR, nullptr, ok, -1.);
     208          682 :             state.v = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, -1.);
     209          682 :             state.begin = attrs.getSUMOTimeReporting(SUMO_ATTR_BEGIN, getID().c_str(), ok);
     210          682 :             if (state.begin < lastEnd) {
     211           21 :                 WRITE_ERRORF("Overlapping or unsorted intervals in calibrator '%' (end=%, begin2=%).", getID(), time2string(lastEnd), time2string(state.begin));
     212          675 :             } else if (state.begin <= lastBegin) {
     213           21 :                 WRITE_ERRORF("Overlapping or unsorted intervals in calibrator '%' (begin=%, begin2=%).", getID(), time2string(lastBegin), time2string(state.begin));
     214              :             }
     215          682 :             state.end = attrs.getOptSUMOTimeReporting(SUMO_ATTR_END, getID().c_str(), ok, -1);
     216          682 :             state.vehicleParameter = SUMOVehicleParserHelper::parseVehicleAttributes(element, attrs, true, true, true);
     217          682 :             state.vehicleParameter->parametersSet &= ~VEHPARS_CALIBRATORSPEED_SET;
     218          682 :             myLeftoverVehicleParameters.push_back(state.vehicleParameter);
     219              :             // vehicles should be inserted with max speed unless stated otherwise
     220          682 :             if (state.vehicleParameter->departSpeedProcedure == DepartSpeedDefinition::DEFAULT) {
     221          635 :                 state.vehicleParameter->departSpeedProcedure = DepartSpeedDefinition::MAX;
     222              :             }
     223              :             // vehicles should be inserted on any lane unless stated otherwise
     224          682 :             if (state.vehicleParameter->departLaneProcedure == DepartLaneDefinition::DEFAULT) {
     225          675 :                 if (myLane == nullptr) {
     226          346 :                     state.vehicleParameter->departLaneProcedure = DepartLaneDefinition::ALLOWED_FREE;
     227              :                 } else {
     228          329 :                     state.vehicleParameter->departLaneProcedure = DepartLaneDefinition::GIVEN;
     229          329 :                     state.vehicleParameter->departLane = myLane->getIndex();
     230              :                 }
     231            7 :             } else if (myLane != nullptr && (
     232              :                            state.vehicleParameter->departLaneProcedure != DepartLaneDefinition::GIVEN
     233            0 :                            || state.vehicleParameter->departLane != myLane->getIndex())) {
     234            0 :                 WRITE_WARNINGF(TL("Insertion lane may differ from calibrator lane for calibrator '%'."), getID());
     235              :             }
     236          849 :             if (state.vehicleParameter->vtypeid != DEFAULT_VTYPE_ID &&
     237          167 :                     MSNet::getInstance()->getVehicleControl().getVType(state.vehicleParameter->vtypeid) == nullptr) {
     238            0 :                 WRITE_ERRORF(TL("Unknown vehicle type '%' in calibrator '%'."), state.vehicleParameter->vtypeid, getID());
     239              :             }
     240            0 :         } catch (EmptyData&) {
     241            0 :             WRITE_ERRORF(TL("Mandatory attribute missing in definition of calibrator '%'."), getID());
     242            0 :         } catch (NumberFormatException&) {
     243            0 :             WRITE_ERRORF(TL("Non-numeric value for numeric attribute in definition of calibrator '%'."), getID());
     244            0 :         }
     245          682 :         if (state.q < 0 && state.v < 0 && state.vehicleParameter->vtypeid == DEFAULT_VTYPE_ID) {
     246            0 :             WRITE_ERRORF(TL("Either 'vehsPerHour', 'speed' or 'type' has to be set in flow definition of calibrator '%'."), getID());
     247              :         }
     248          682 :         if (MSGlobals::gUseMesoSim && state.q < 0 && state.vehicleParameter->vtypeid != DEFAULT_VTYPE_ID) {
     249           42 :             WRITE_ERRORF(TL("Type calibration is not supported in meso for calibrator '%'."), getID());
     250              :         }
     251          682 :         if (myIntervals.size() > 0 && myIntervals.back().end == -1) {
     252            7 :             myIntervals.back().end = state.begin;
     253              :         }
     254          682 :         myIntervals.push_back(state);
     255          682 :         myCurrentStateInterval = myIntervals.begin();
     256              :     } else {
     257          282 :         MSRouteHandler::myStartElement(element, attrs);
     258              :     }
     259          964 : }
     260              : 
     261              : 
     262              : void
     263         2045 : MSCalibrator::myEndElement(int element) {
     264         2045 :     if (element == SUMO_TAG_CALIBRATOR) {
     265         1130 :         if (!myDidInit) {
     266         1130 :             init();
     267              :         }
     268              :         // ensure correct state of SUMORouteHandler::myElementStack
     269         1130 :         callParentEnd(element);
     270          915 :     } else if (element != SUMO_TAG_FLOW) {
     271          233 :         MSRouteHandler::myEndElement(element);
     272              :     }
     273         2045 : }
     274              : 
     275              : 
     276              : void
     277          685 : MSCalibrator::intervalEnd() {
     278          685 :     if (myOutput != nullptr) {
     279          491 :         writeXMLOutput(*myOutput, myCurrentStateInterval->begin, myCurrentStateInterval->end);
     280              :     }
     281          685 :     myDidSpeedAdaption = false;
     282          685 :     myInserted = 0;
     283          685 :     myRemoved = 0;
     284          685 :     myClearedInJam = 0;
     285          685 :     myHaveWarnedAboutClearingJam = false;
     286          685 :     reset();
     287          685 : }
     288              : 
     289              : 
     290              : bool
     291       618571 : MSCalibrator::isCurrentStateActive(SUMOTime time) {
     292       619137 :     while (myCurrentStateInterval != myIntervals.end() && myCurrentStateInterval->end <= time) {
     293              :         // XXX what about skipped intervals?
     294              :         myCurrentStateInterval++;
     295              :     }
     296       618306 :     return myCurrentStateInterval != myIntervals.end() &&
     297       618571 :            myCurrentStateInterval->begin <= time && myCurrentStateInterval->end > time;
     298              : }
     299              : 
     300              : 
     301              : int
     302       576822 : MSCalibrator::totalWished() const {
     303       576822 :     if (myCurrentStateInterval != myIntervals.end()) {
     304       576822 :         const double totalHourFraction = STEPS2TIME(myCurrentStateInterval->end - myCurrentStateInterval->begin) / (double) 3600.;
     305       576822 :         return (int)std::floor(myCurrentStateInterval->q * totalHourFraction + 0.5); // round to closest int
     306              :     } else {
     307              :         return -1;
     308              :     }
     309              : }
     310              : 
     311              : 
     312              : double
     313            0 : MSCalibrator::currentFlow() const {
     314            0 :     const double totalHourFraction = STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep() - myCurrentStateInterval->begin) / (double) 3600.;
     315            0 :     return passed() / totalHourFraction;
     316              : }
     317              : 
     318              : 
     319              : double
     320            0 : MSCalibrator::currentSpeed() const {
     321            0 :     if (myEdgeMeanData.getSamples() > 0) {
     322            0 :         return myEdgeMeanData.getTravelledDistance() / myEdgeMeanData.getSamples();
     323              :     } else {
     324              :         return -1;
     325              :     }
     326              : }
     327              : 
     328              : 
     329              : bool
     330       518381 : MSCalibrator::removePending() {
     331       518381 :     if (myToRemove.size() > 0) {
     332        17337 :         MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
     333              :         // it is not save to remove the vehicles inside
     334              :         // VehicleRemover::notifyEnter so we do it here
     335        34675 :         for (std::set<std::string>::iterator it = myToRemove.begin(); it != myToRemove.end(); ++it) {
     336        17338 :             MSVehicle* vehicle = dynamic_cast<MSVehicle*>(vc.getVehicle(*it));
     337        17338 :             if (vehicle != nullptr) {
     338              :                 MSLane* lane = vehicle->getMutableLane();
     339        17338 :                 vehicle->onRemovalFromNet(MSMoveReminder::NOTIFICATION_VAPORIZED_CALIBRATOR);
     340        17338 :                 lane->removeVehicle(vehicle, MSMoveReminder::NOTIFICATION_VAPORIZED_CALIBRATOR);
     341        17338 :                 vc.scheduleVehicleRemoval(vehicle, true);
     342              :             } else {
     343            0 :                 WRITE_WARNINGF(TL("Calibrator '%' could not remove vehicle '%' time=%."), getID(), *it, time2string(MSNet::getInstance()->getCurrentTimeStep()));
     344              :             }
     345              :         }
     346              :         myToRemove.clear();
     347        17337 :         return true;
     348              :     }
     349              :     return false;
     350              : }
     351              : 
     352              : 
     353              : SUMOTime
     354       518381 : MSCalibrator::execute(SUMOTime currentTime) {
     355       518381 :     const bool calibrateFlow = myCurrentStateInterval->q >= 0;
     356       518381 :     const bool calibrateSpeed = myCurrentStateInterval->v >= 0;
     357              :     // get current simulation values (valid for the last simulation second)
     358       761994 :     myHaveInvalidJam = (calibrateFlow || calibrateSpeed) && invalidJam(myLane == 0 ? -1 : myLane->getIndex());
     359              :     // XXX could we miss vehicle movements if this is called less often than every DELTA_T (default) ?
     360       518381 :     updateMeanData();
     361       518381 :     const bool hadRemovals = removePending();
     362              :     // check whether an adaptation value exists
     363       518381 :     if (isCurrentStateActive(currentTime)) {
     364       473021 :         myAmActive = true;
     365              :         // all happens in isCurrentStateActive()
     366              :     } else {
     367        45360 :         myAmActive = false;
     368        45360 :         reset();
     369        45360 :         if (!mySpeedIsDefault) {
     370              :             // reset speed to default
     371          103 :             if (myLane == nullptr) {
     372           51 :                 myEdge->setMaxSpeed(myDefaultSpeed);
     373              :             } else {
     374           52 :                 myLane->setMaxSpeed(myDefaultSpeed);
     375              :             }
     376          103 :             mySpeedIsDefault = true;
     377              :         }
     378        45360 :         if (myCurrentStateInterval == myIntervals.end()) {
     379              :             // keep calibrator alive for gui but do not call again
     380          203 :             return SUMOTime_MAX - currentTime;
     381              :         }
     382        45157 :         return myFrequency;
     383              :     }
     384              :     // we are active
     385       473021 :     if (!myDidSpeedAdaption && calibrateSpeed) {
     386          170 :         if (myLane == nullptr) {
     387           86 :             myEdge->setMaxSpeed(myCurrentStateInterval->v);
     388              :         } else {
     389           84 :             myLane->setMaxSpeed(myCurrentStateInterval->v);
     390              :         }
     391          170 :         mySpeedIsDefault = false;
     392          170 :         myDidSpeedAdaption = true;
     393              :     }
     394              : 
     395       473021 :     const int totalWishedNum = totalWished();
     396       473021 :     int adaptedNum = passed() + myClearedInJam;
     397              : #ifdef MSCalibrator_DEBUG
     398              :     if (DEBUGCOND) {
     399              :         std::cout << time2string(currentTime) << " " << getID()
     400              :                   << " q=" << myCurrentStateInterval->q
     401              :                   << " totalWished=" << totalWishedNum
     402              :                   << " adapted=" << adaptedNum
     403              :                   << " jam=" << myHaveInvalidJam
     404              :                   << " entered=" << myEdgeMeanData.nVehEntered
     405              :                   << " departed=" << myEdgeMeanData.nVehDeparted
     406              :                   << " arrived=" << myEdgeMeanData.nVehArrived
     407              :                   << " left=" << myEdgeMeanData.nVehLeft
     408              :                   << " waitSecs=" << myEdgeMeanData.waitSeconds
     409              :                   << " vaporized=" << myEdgeMeanData.nVehVaporized
     410              :                   << "\n";
     411              :     }
     412              : #endif
     413       473021 :     if (calibrateFlow && adaptedNum < totalWishedNum && !hadRemovals) {
     414              :         // we need to insert some vehicles
     415       129439 :         const double hourFraction = STEPS2TIME(currentTime - myCurrentStateInterval->begin + DELTA_T) / (double) 3600.;
     416       129439 :         const int wishedNum = (int)std::floor(myCurrentStateInterval->q * hourFraction + 0.5); // round to closest int
     417              :         // only the difference between inflow and aspiredFlow should be added, thus
     418              :         // we should not count vehicles vaporized from a jam here
     419              :         // if we have enough time left we can add missing vehicles later
     420       129439 :         const int relaxedInsertion = (int)std::floor(STEPS2TIME(myCurrentStateInterval->end - currentTime) / 3);
     421       129439 :         const int insertionSlack = MAX2(0, adaptedNum + relaxedInsertion - totalWishedNum);
     422              :         // increase number of vehicles
     423              : #ifdef MSCalibrator_DEBUG
     424              :         if (DEBUGCOND) {
     425              :             std::cout << "   wished:" << wishedNum
     426              :                       << " slack:" << insertionSlack
     427              :                       << " before:" << adaptedNum
     428              :                       << "\n";
     429              :         }
     430              : #endif
     431       129439 :         MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
     432       159172 :         while (wishedNum > adaptedNum + insertionSlack) {
     433       103505 :             SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
     434       103505 :             ConstMSRoutePtr route = myProbe != nullptr ? myProbe->sampleRoute() : nullptr;
     435       103505 :             if (route == nullptr) {
     436       207010 :                 route = MSRoute::dictionary(pars->routeid);
     437              :             }
     438       103505 :             if (route == nullptr) {
     439            0 :                 WRITE_WARNINGF(TL("No valid routes in calibrator '%'."), getID());
     440            0 :                 break;
     441              :             }
     442       103505 :             if (!route->contains(myEdge)) {
     443            5 :                 WRITE_WARNINGF(TL("Route '%' in calibrator '%' does not contain edge '%'."), route->getID(), getID(), myEdge->getID());
     444            0 :                 break;
     445              :             }
     446       103505 :             const int routeIndex = (int)std::distance(route->begin(),
     447       103505 :                                    std::find(route->begin(), route->end(), myEdge));
     448       103505 :             MSVehicleType* vtype = vc.getVType(pars->vtypeid);
     449              :             assert(route != 0 && vtype != 0);
     450              :             // build the vehicle
     451       103505 :             const std::string newID = getNewVehicleID();
     452       103505 :             if (vc.getVehicle(newID) != nullptr) {
     453              :                 // duplicate ids could come from loading state
     454            7 :                 myInserted++;
     455            7 :                 break;
     456              :             }
     457       103498 :             SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
     458       103498 :             newPars->id = newID;
     459       103498 :             newPars->depart = currentTime;
     460       103498 :             newPars->routeid = route->getID();
     461       103498 :             newPars->departLaneProcedure = DepartLaneDefinition::FIRST_ALLOWED; // ensure successful vehicle creation
     462              :             MSVehicle* vehicle;
     463              :             try {
     464       103498 :                 vehicle = dynamic_cast<MSVehicle*>(vc.buildVehicle(newPars, route, vtype, true, MSVehicleControl::VehicleDefinitionSource::TRIGGER));
     465            0 :             } catch (const ProcessError& e) {
     466            0 :                 if (!MSGlobals::gCheckRoutes) {
     467            0 :                     WRITE_WARNING(e.what());
     468              :                     vehicle = nullptr;
     469              :                     break;
     470              :                 } else {
     471            0 :                     throw;
     472              :                 }
     473            0 :             }
     474              : #ifdef MSCalibrator_DEBUG
     475              :             if (DEBUGCOND) {
     476              :                 std::cout << " resetting route pos: " << routeIndex << "\n";
     477              :             }
     478              : #endif
     479       103498 :             vehicle->resetRoutePosition(routeIndex, pars->departLaneProcedure);
     480              :             bool success = false;
     481              :             try {
     482       103498 :                 success = myEdge->insertVehicle(*vehicle, currentTime);
     483            5 :             } catch (const ProcessError&) {
     484            5 :                 MSNet::getInstance()->getVehicleControl().deleteVehicle(vehicle, true);
     485            5 :                 throw;
     486            5 :             }
     487       103493 :             if (success) {
     488        29733 :                 if (!MSNet::getInstance()->getVehicleControl().addVehicle(vehicle->getID(), vehicle)) {
     489            0 :                     throw ProcessError("Emission of vehicle '" + vehicle->getID() + "' in calibrator '" + getID() + "'failed!");
     490              :                 }
     491        29733 :                 myInserted++;
     492        29733 :                 adaptedNum++;
     493              : #ifdef MSCalibrator_DEBUG
     494              :                 if (DEBUGCOND) {
     495              :                     std::cout << "I ";
     496              :                 }
     497              : #endif
     498              :             } else {
     499              :                 // could not insert vehicle
     500              : #ifdef MSCalibrator_DEBUG
     501              :                 if (DEBUGCOND) {
     502              :                     std::cout << "F ";
     503              :                 }
     504              : #endif
     505        73760 :                 MSNet::getInstance()->getVehicleControl().deleteVehicle(vehicle, true);
     506              :                 break;
     507              :             }
     508              :         }
     509              :     }
     510       473016 :     if (myCurrentStateInterval->end <= currentTime + myFrequency) {
     511          432 :         intervalEnd();
     512              :     }
     513       473016 :     return myFrequency;
     514              : }
     515              : 
     516              : 
     517              : void
     518        45884 : MSCalibrator::reset() {
     519        45884 :     myEdgeMeanData.reset();
     520       106983 :     for (MSMeanData_Net::MSLaneMeanDataValues* const val : myLaneMeanData) {
     521        61099 :         val->reset();
     522              :     }
     523        45884 : }
     524              : 
     525              : 
     526              : bool
     527       330144 : MSCalibrator::invalidJam(int laneIndex) const {
     528       330144 :     if (laneIndex < 0) {
     529        29074 :         const int numLanes = (int)myEdge->getLanes().size();
     530        86275 :         for (int i = 0; i < numLanes; ++i) {
     531        57457 :             if (invalidJam(i)) {
     532              :                 return true;
     533              :             }
     534              :         }
     535              :         return false;
     536              :     }
     537              :     assert(laneIndex < (int)myEdge->getLanes().size());
     538       301070 :     const MSLane* const lane = myEdge->getLanes()[laneIndex];
     539       301070 :     if (lane->getVehicleNumber() < 4) {
     540              :         // cannot reliably detect invalid jams
     541              :         return false;
     542              :     }
     543              :     // maxSpeed reflects the calibration target
     544       210631 :     const bool toSlow = lane->getMeanSpeed() < myInvalidJamThreshold * myEdge->getSpeedLimit();
     545       210631 :     return toSlow && remainingVehicleCapacity(laneIndex) < 1;
     546              : }
     547              : 
     548              : 
     549              : int
     550        54111 : MSCalibrator::remainingVehicleCapacity(int laneIndex) const {
     551        54111 :     if (laneIndex < 0) {
     552            0 :         const int numLanes = (int)myEdge->getLanes().size();
     553              :         int result = 0;
     554            0 :         for (int i = 0; i < numLanes; ++i) {
     555            0 :             result = MAX2(result, remainingVehicleCapacity(i));
     556              :         }
     557            0 :         return result;
     558              :     }
     559              :     assert(laneIndex < (int)myEdge->getLanes().size());
     560        54111 :     MSLane* lane = myEdge->getLanes()[laneIndex];
     561        54111 :     MSVehicle* last = lane->getLastFullVehicle();
     562        54111 :     const SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
     563        54111 :     const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(pars->vtypeid);
     564        54111 :     const double spacePerVehicle = vtype->getLengthWithGap() + myEdge->getSpeedLimit() * vtype->getCarFollowModel().getHeadwayTime();
     565        54111 :     int overallSpaceLeft = (int)ceil(lane->getLength() / spacePerVehicle) - lane->getVehicleNumber();
     566        54111 :     if (last != nullptr) {
     567        54111 :         int entrySpaceLeft = (int)(last->getPositionOnLane() / spacePerVehicle);
     568        54111 :         return MAX2(overallSpaceLeft, entrySpaceLeft);
     569              :     } else {
     570              :         return overallSpaceLeft;
     571              :     }
     572              : }
     573              : 
     574              : 
     575              : void
     576        38670 : MSCalibrator::cleanup() {
     577        78470 :     while (!myInstances.empty()) {
     578         1130 :         delete myInstances.begin()->second;
     579              :     }
     580        41049 :     for (MSMoveReminder* rem : myLeftoverReminders) {
     581         2379 :         delete rem;
     582              :     }
     583              :     myLeftoverReminders.clear();
     584        39352 :     for (SUMOVehicleParameter* par : myLeftoverVehicleParameters) {
     585          682 :         delete par;
     586              :     }
     587              :     myLeftoverVehicleParameters.clear();
     588        38670 : }
     589              : 
     590              : 
     591              : void
     592       622550 : MSCalibrator::updateMeanData() {
     593       622550 :     myEdgeMeanData.reset();
     594       622550 :     for (std::vector<MSMeanData_Net::MSLaneMeanDataValues*>::iterator it = myLaneMeanData.begin();
     595      1278746 :             it != myLaneMeanData.end(); ++it) {
     596       656196 :         (*it)->addTo(myEdgeMeanData);
     597              :     }
     598       622550 : }
     599              : 
     600              : 
     601              : bool
     602       156520 : MSCalibrator::VehicleRemover::notifyEnter(SUMOTrafficObject& veh, Notification /* reason */, const MSLane* /* enteredLane */) {
     603       156520 :     if (myParent == nullptr) {
     604              :         return false;
     605              :     }
     606       156520 :     if (!myParent->vehicleApplies(veh)) {
     607              :         return false;
     608              :     }
     609       151908 :     if (myParent->isActive()) {
     610       103801 :         myParent->updateMeanData();
     611       103801 :         const bool calibrateFlow = myParent->myCurrentStateInterval->q >= 0;
     612       103801 :         const int totalWishedNum = myParent->totalWished();
     613       103801 :         int adaptedNum = myParent->passed() + myParent->myClearedInJam;
     614       103801 :         if (calibrateFlow && adaptedNum > totalWishedNum) {
     615              : #ifdef MSCalibrator_DEBUG
     616              :             if (DEBUGCOND2(myParent->getID())) std::cout << time2string(MSNet::getInstance()->getCurrentTimeStep()) << " " << myParent->getID()
     617              :                         << " vaporizing " << vehicle->getID() << " to reduce flow\n";
     618              : #endif
     619         3636 :             if (myParent->scheduleRemoval(&veh)) {
     620         3636 :                 myParent->myRemoved++;
     621              :             }
     622       100165 :         } else if (myParent->myHaveInvalidJam) {
     623              : #ifdef MSCalibrator_DEBUG
     624              :             if (DEBUGCOND2(myParent->getID())) std::cout << time2string(MSNet::getInstance()->getCurrentTimeStep()) << " " << myParent->getID()
     625              :                         << " vaporizing " << vehicle->getID() << " to clear jam\n";
     626              : #endif
     627        13714 :             if (!myParent->myHaveWarnedAboutClearingJam) {
     628           90 :                 WRITE_WARNINGF(TL("Clearing jam at calibrator '%' at time=%."), myParent->getID(), time2string(SIMSTEP));
     629           30 :                 myParent->myHaveWarnedAboutClearingJam = true;
     630              :             }
     631        13714 :             if (myParent->scheduleRemoval(&veh)) {
     632        13702 :                 myParent->myClearedInJam++;
     633              :             }
     634              :         }
     635       103801 :         const std::string typeID = myParent->myCurrentStateInterval->vehicleParameter->vtypeid;
     636       103801 :         if (!calibrateFlow && typeID != DEFAULT_VTYPE_ID) {
     637              :             // calibrate type
     638        28020 :             const std::string origType = veh.getParameter().vtypeid; // could by id of vTypeDistribution
     639        28020 :             const MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
     640        28020 :             const RandomDistributor<MSVehicleType*>* oldDist = vc.getVTypeDistribution(origType);
     641        28020 :             const RandomDistributor<MSVehicleType*>* newDist = vc.getVTypeDistribution(typeID);
     642              :             bool matchDistribution = false;
     643        28020 :             if (oldDist != nullptr && newDist != nullptr &&  oldDist->getVals().size() == newDist->getVals().size()) {
     644          280 :                 auto it = std::find(oldDist->getVals().begin(), oldDist->getVals().end(), &veh.getVehicleType());
     645          280 :                 if (it != oldDist->getVals().end()) {
     646              :                     matchDistribution = true;
     647          280 :                     const int distIndex = (int)(it - oldDist->getVals().begin());
     648          280 :                     veh.replaceVehicleType(newDist->getVals()[distIndex]);
     649              :                 }
     650              :             }
     651              :             if (!matchDistribution) {
     652        27740 :                 MSVehicleType* vehicleType = MSNet::getInstance()->getVehicleControl().getVType(typeID);
     653        27740 :                 if (vehicleType == nullptr) {
     654            0 :                     throw ProcessError("Unknown vehicle type '" + typeID + "' in calibrator '" + myParent->getID() + "'");
     655              :                 }
     656        27740 :                 veh.replaceVehicleType(vehicleType);
     657              :             }
     658              :         }
     659              :     }
     660              :     return true;
     661              : }
     662              : 
     663              : 
     664              : bool
     665       443656 : MSCalibrator::VehicleRemover::notifyLeave(SUMOTrafficObject& veh, double /* lastPos */, Notification reason, const MSLane* /* enteredLane */) {
     666       443656 :     if (myUndoCalibration && reason != NOTIFICATION_LANE_CHANGE) {
     667              :         // TODO check for distributions
     668           15 :         veh.replaceVehicleType(MSNet::getInstance()->getVehicleControl().getVType(veh.getParameter().vtypeid));
     669              :     }
     670       443656 :     return true;
     671              : }
     672              : 
     673              : 
     674              : void
     675          491 : MSCalibrator::writeXMLOutput(OutputDevice& dev, SUMOTime startTime, SUMOTime stopTime) {
     676          491 :     updateMeanData();
     677          491 :     const int p = passed();
     678              :     // meandata will be off if vehicles are removed on the next edge instead of this one
     679          491 :     const int discrepancy = myEdgeMeanData.nVehEntered + myEdgeMeanData.nVehDeparted - myEdgeMeanData.nVehVaporized - myEdgeMeanData.nVehTeleported - passed();
     680              :     //assert(discrepancy >= 0); may go negative for lane calibrator when vehicles change lane before removal
     681          502 :     const std::string ds = (discrepancy > 0 ? "\" vaporizedOnNextEdge=\"" + toString(discrepancy) : "");
     682          491 :     const double durationSeconds = STEPS2TIME(stopTime - startTime);
     683          491 :     dev.openTag(SUMO_TAG_INTERVAL);
     684          491 :     dev.writeAttr(SUMO_ATTR_BEGIN, time2string(startTime));
     685          982 :     dev.writeAttr(SUMO_ATTR_END, time2string(stopTime));
     686          491 :     dev.writeAttr(SUMO_ATTR_ID, getID());
     687          491 :     dev.writeAttr("nVehContrib", p);
     688          491 :     dev.writeAttr("removed", myRemoved);
     689          491 :     dev.writeAttr("inserted", myInserted);
     690          491 :     dev.writeAttr("cleared", myClearedInJam);
     691          982 :     dev.writeAttr("flow", p * 3600.0 / durationSeconds);
     692          491 :     dev.writeAttr("aspiredFlow", myCurrentStateInterval->q);
     693          491 :     dev.writeAttr(SUMO_ATTR_SPEED, myEdgeMeanData.getSamples() != 0
     694          491 :                   ? myEdgeMeanData.getTravelledDistance() / myEdgeMeanData.getSamples() : -1);
     695          491 :     dev.writeAttr("aspiredSpeed", myCurrentStateInterval->v);
     696          491 :     if (discrepancy > 0) {
     697           22 :         dev.writeAttr("vaporizedOnNextEdge", discrepancy);
     698              :     }
     699          982 :     dev.closeTag();
     700          491 : }
     701              : 
     702              : void
     703          267 : MSCalibrator::writeXMLDetectorProlog(OutputDevice& dev) const {
     704          534 :     dev.writeXMLHeader("calibratorstats", "calibratorstats_file.xsd");
     705          267 : }
     706              : 
     707              : std::string
     708       117053 : MSCalibrator::getNewVehicleID() {
     709              :     // avoid name clash for subsecond interval spacing
     710       117053 :     const double beginS = STEPS2TIME(myCurrentStateInterval->begin);
     711       117053 :     const int precision = beginS == int(beginS) ? 0 : 2;
     712       351159 :     return getID() + "." + toString(beginS, precision) + "." + toString(myInserted);
     713              : }
     714              : 
     715              : void
     716           10 : MSCalibrator::setFlow(SUMOTime begin, SUMOTime end, double vehsPerHour, double speed, SUMOVehicleParameter vehicleParameter) {
     717           10 :     auto it = myCurrentStateInterval;
     718           15 :     while (it != myIntervals.end()) {
     719           10 :         if (it->begin > begin) {
     720            0 :             throw ProcessError("Cannot set flow for calibrator '" + getID() + "' with begin time=" + time2string(begin) + " in the past.");
     721           10 :         } else if (it->begin == begin && it->end == end) {
     722              :             // update current interval
     723              :             AspiredState& state = const_cast<AspiredState&>(*it);
     724            5 :             state.q = vehsPerHour;
     725            5 :             state.v = speed;
     726            5 :             state.vehicleParameter->vtypeid = vehicleParameter.vtypeid;
     727            5 :             state.vehicleParameter->routeid = vehicleParameter.routeid;
     728            5 :             state.vehicleParameter->departLane = vehicleParameter.departLane;
     729            5 :             state.vehicleParameter->departLaneProcedure = vehicleParameter.departLaneProcedure;
     730            5 :             state.vehicleParameter->departSpeed = vehicleParameter.departSpeed;
     731            5 :             state.vehicleParameter->departSpeedProcedure = vehicleParameter.departSpeedProcedure;
     732            5 :             return;
     733            5 :         } else if (begin < it->end) {
     734            0 :             throw ProcessError(TLF("Cannot set flow for calibrator '%' with overlapping interval.", getID()));
     735            5 :         } else if (begin >= end) {
     736            0 :             throw ProcessError(TLF("Cannot set flow for calibrator '%' with negative interval.", getID()));
     737              :         }
     738              :         it++;
     739              :     }
     740              :     // add interval at the end of the known intervals
     741            5 :     const int intervalIndex = (int)(myCurrentStateInterval - myIntervals.begin());
     742              :     AspiredState state;
     743            5 :     state.begin = begin;
     744            5 :     state.end = end;
     745            5 :     state.q = vehsPerHour;
     746            5 :     state.v = speed;
     747            5 :     state.vehicleParameter = new SUMOVehicleParameter(vehicleParameter);
     748            5 :     myIntervals.push_back(state);
     749              :     // fix iterator
     750            5 :     myCurrentStateInterval = myIntervals.begin() + intervalIndex;
     751              : }
     752              : 
     753              : /****************************************************************************/
        

Generated by: LCOV version 2.0-1