1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2005-2019 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
10 /// @file    MSCalibrator.cpp
11 /// @author  Daniel Krajzewicz
12 /// @author  Jakob Erdmann
13 /// @author  Michael Behrisch
14 /// @date    Tue, May 2005
15 /// @version $Id$
16 ///
17 // Calibrates the flow on an edge by removing an inserting vehicles
18 /****************************************************************************/
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <string>
27 #include <algorithm>
28 #include <cmath>
29 #include <microsim/MSNet.h>
30 #include <microsim/MSEdge.h>
31 #include <microsim/MSLane.h>
32 #include <microsim/MSEventControl.h>
33 #include <microsim/MSVehicleControl.h>
34 #include <microsim/output/MSRouteProbe.h>
35 #include <utils/xml/SUMOXMLDefinitions.h>
36 #include <utils/common/ToString.h>
37 #include <utils/common/UtilExceptions.h>
38 #include <utils/common/StringTokenizer.h>
39 #include <utils/xml/XMLSubSys.h>
40 #include <utils/common/StringUtils.h>
41 #include <utils/options/OptionsCont.h>
42 #include <utils/vehicle/SUMOVehicleParserHelper.h>
43 #include <utils/distribution/RandomDistributor.h>
44 #include <utils/vehicle/SUMOVehicleParameter.h>
45 #include "MSCalibrator.h"
46 
47 //#define MSCalibrator_DEBUG
48 
49 // ===========================================================================
50 // static members
51 // ===========================================================================
52 std::vector<MSMoveReminder*> MSCalibrator::LeftoverReminders;
53 std::vector<SUMOVehicleParameter*> MSCalibrator::LeftoverVehicleParameters;
54 
55 // ===========================================================================
56 // method definitions
57 // ===========================================================================
MSCalibrator(const std::string & id,const MSEdge * const edge,MSLane * lane,const double pos,const std::string & aXMLFilename,const std::string & outputFilename,const SUMOTime freq,const double length,const MSRouteProbe * probe,bool addLaneMeanData)58 MSCalibrator::MSCalibrator(const std::string& id,
59                            const MSEdge* const edge,
60                            MSLane* lane,
61                            const double pos,
62                            const std::string& aXMLFilename,
63                            const std::string& outputFilename,
64                            const SUMOTime freq, const double length,
65                            const MSRouteProbe* probe,
66                            bool addLaneMeanData) :
67     MSTrigger(id),
68     MSRouteHandler(aXMLFilename, true),
69     myEdge(edge),
70     myLane(lane),
71     myPos(pos), myProbe(probe),
72     myEdgeMeanData(nullptr, length, false, nullptr),
73     myCurrentStateInterval(myIntervals.begin()),
74     myOutput(nullptr), myFrequency(freq), myRemoved(0),
75     myInserted(0), myClearedInJam(0),
76     mySpeedIsDefault(true), myDidSpeedAdaption(false), myDidInit(false),
77     myDefaultSpeed(myLane == nullptr ? myEdge->getSpeedLimit() : myLane->getSpeedLimit()),
78     myHaveWarnedAboutClearingJam(false),
79     myAmActive(false) {
80     if (outputFilename != "") {
81         myOutput = &OutputDevice::getDevice(outputFilename);
82         myOutput->writeXMLHeader("calibratorstats", "calibratorstats_file.xsd");
83     }
84     if (aXMLFilename != "") {
85         XMLSubSys::runParser(*this, aXMLFilename);
86         if (!myDidInit) {
87             init();
88         }
89     }
90     if (addLaneMeanData) {
91         // disabled for METriggeredCalibrator
92         for (int i = 0; i < (int)myEdge->getLanes().size(); ++i) {
93             MSLane* lane = myEdge->getLanes()[i];
94             if (myLane == nullptr || myLane == lane) {
95                 //std::cout << " cali=" << getID() << " myLane=" << Named::getIDSecure(myLane) << " checkLane=" << i << "\n";
96                 MSMeanData_Net::MSLaneMeanDataValues* laneData = new MSMeanData_Net::MSLaneMeanDataValues(lane, lane->getLength(), true, nullptr);
97                 laneData->setDescription("meandata_calibrator_" + lane->getID());
98                 LeftoverReminders.push_back(laneData);
99                 myLaneMeanData.push_back(laneData);
100                 VehicleRemover* remover = new VehicleRemover(lane, (int)i, this);
101                 LeftoverReminders.push_back(remover);
102                 myVehicleRemovers.push_back(remover);
103             }
104         }
105     }
106 }
107 
108 
109 void
init()110 MSCalibrator::init() {
111     if (myIntervals.size() > 0) {
112         if (myIntervals.back().end == -1) {
113             myIntervals.back().end = SUMOTime_MAX;
114         }
115         // calibration should happen after regular insertions have taken place
116         MSNet::getInstance()->getEndOfTimestepEvents()->addEvent(new CalibratorCommand(this));
117     } else {
118         WRITE_WARNING("No flow intervals in calibrator '" + myID + "'.");
119     }
120     myDidInit = true;
121 }
122 
123 
~MSCalibrator()124 MSCalibrator::~MSCalibrator() {
125     if (myCurrentStateInterval != myIntervals.end()) {
126         writeXMLOutput();
127     }
128     for (std::vector<VehicleRemover*>::iterator it = myVehicleRemovers.begin(); it != myVehicleRemovers.end(); ++it) {
129         (*it)->disable();
130     }
131 }
132 
133 
134 void
myStartElement(int element,const SUMOSAXAttributes & attrs)135 MSCalibrator::myStartElement(int element,
136                              const SUMOSAXAttributes& attrs) {
137     if (element == SUMO_TAG_FLOW) {
138         AspiredState state;
139         SUMOTime lastEnd = -1;
140         if (myIntervals.size() > 0) {
141             lastEnd = myIntervals.back().end;
142             if (lastEnd == -1) {
143                 lastEnd = myIntervals.back().begin;
144             }
145         }
146         try {
147             bool ok = true;
148             state.q = attrs.getOpt<double>(SUMO_ATTR_VEHSPERHOUR, nullptr, ok, -1.);
149             state.v = attrs.getOpt<double>(SUMO_ATTR_SPEED, nullptr, ok, -1.);
150             state.begin = attrs.getSUMOTimeReporting(SUMO_ATTR_BEGIN, myID.c_str(), ok);
151             if (state.begin < lastEnd) {
152                 WRITE_ERROR("Overlapping or unsorted intervals in calibrator '" + myID + "'.");
153             }
154             state.end = attrs.getOptSUMOTimeReporting(SUMO_ATTR_END, myID.c_str(), ok, -1);
155             state.vehicleParameter = SUMOVehicleParserHelper::parseVehicleAttributes(attrs, true, true);
156             LeftoverVehicleParameters.push_back(state.vehicleParameter);
157             // vehicles should be inserted with max speed unless stated otherwise
158             if (state.vehicleParameter->departSpeedProcedure == DEPART_SPEED_DEFAULT) {
159                 state.vehicleParameter->departSpeedProcedure = DEPART_SPEED_MAX;
160             }
161             // vehicles should be inserted on any lane unless stated otherwise
162             if (state.vehicleParameter->departLaneProcedure == DEPART_LANE_DEFAULT) {
163                 if (myLane == nullptr) {
164                     state.vehicleParameter->departLaneProcedure = DEPART_LANE_ALLOWED_FREE;
165                 } else {
166                     state.vehicleParameter->departLaneProcedure = DEPART_LANE_GIVEN;
167                     state.vehicleParameter->departLane = myLane->getIndex();
168                 }
169             } else if (myLane != nullptr && (
170                            state.vehicleParameter->departLaneProcedure != DEPART_LANE_GIVEN
171                            || state.vehicleParameter->departLane != myLane->getIndex())) {
172                 WRITE_WARNING("Insertion lane may differ from calibrator lane for calibrator '" + getID() + "'.");
173             }
174             if (state.vehicleParameter->vtypeid != DEFAULT_VTYPE_ID &&
175                     MSNet::getInstance()->getVehicleControl().getVType(state.vehicleParameter->vtypeid) == nullptr) {
176                 WRITE_ERROR("Unknown vehicle type '" + state.vehicleParameter->vtypeid + "' in calibrator '" + myID + "'.");
177             }
178         } catch (EmptyData&) {
179             WRITE_ERROR("Mandatory attribute missing in definition of calibrator '" + myID + "'.");
180         } catch (NumberFormatException&) {
181             WRITE_ERROR("Non-numeric value for numeric attribute in definition of calibrator '" + myID + "'.");
182         }
183         if (state.q < 0 && state.v < 0) {
184             WRITE_ERROR("Either 'vehsPerHour' or 'speed' has to be given in flow definition of calibrator '" + myID + "'.");
185         }
186         if (myIntervals.size() > 0 && myIntervals.back().end == -1) {
187             myIntervals.back().end = state.begin;
188         }
189         myIntervals.push_back(state);
190         myCurrentStateInterval = myIntervals.begin();
191     } else {
192         MSRouteHandler::myStartElement(element, attrs);
193     }
194 }
195 
196 
197 void
myEndElement(int element)198 MSCalibrator::myEndElement(int element) {
199     if (element == SUMO_TAG_CALIBRATOR) {
200         if (!myDidInit) {
201             init();
202         }
203     } else if (element != SUMO_TAG_FLOW) {
204         MSRouteHandler::myEndElement(element);
205     }
206 }
207 
208 
209 void
writeXMLOutput()210 MSCalibrator::writeXMLOutput() {
211     if (myOutput != nullptr) {
212         updateMeanData();
213         const int p = passed();
214         // meandata will be off if vehicles are removed on the next edge instead of this one
215         const int discrepancy = myEdgeMeanData.nVehEntered + myEdgeMeanData.nVehDeparted - myEdgeMeanData.nVehVaporized - passed();
216         assert(discrepancy >= 0);
217         const std::string ds = (discrepancy > 0 ? "\" vaporizedOnNextEdge=\"" + toString(discrepancy) : "");
218         const double durationSeconds = STEPS2TIME(myCurrentStateInterval->end - myCurrentStateInterval->begin);
219         (*myOutput) << "    <interval begin=\"" << time2string(myCurrentStateInterval->begin) <<
220                     "\" end=\"" << time2string(myCurrentStateInterval->end) <<
221                     "\" id=\"" << myID <<
222                     "\" nVehContrib=\"" << p <<
223                     "\" removed=\"" << myRemoved <<
224                     "\" inserted=\"" << myInserted <<
225                     "\" cleared=\"" << myClearedInJam <<
226                     "\" flow=\"" << p * 3600.0 / durationSeconds <<
227                     "\" aspiredFlow=\"" << myCurrentStateInterval->q <<
228                     "\" speed=\"" << myEdgeMeanData.getTravelledDistance() / myEdgeMeanData.getSamples() <<
229                     "\" aspiredSpeed=\"" << myCurrentStateInterval->v <<
230                     ds << //optional
231                     "\"/>\n";
232     }
233     myDidSpeedAdaption = false;
234     myInserted = 0;
235     myRemoved = 0;
236     myClearedInJam = 0;
237     myHaveWarnedAboutClearingJam = false;
238     reset();
239 }
240 
241 
242 bool
isCurrentStateActive(SUMOTime time)243 MSCalibrator::isCurrentStateActive(SUMOTime time) {
244     while (myCurrentStateInterval != myIntervals.end() && myCurrentStateInterval->end <= time) {
245         // XXX what about skipped intervals?
246         myCurrentStateInterval++;
247     }
248     return myCurrentStateInterval != myIntervals.end() &&
249            myCurrentStateInterval->begin <= time && myCurrentStateInterval->end > time;
250 }
251 
252 int
totalWished() const253 MSCalibrator::totalWished() const {
254     if (myCurrentStateInterval != myIntervals.end()) {
255         const double totalHourFraction = STEPS2TIME(myCurrentStateInterval->end - myCurrentStateInterval->begin) / (double) 3600.;
256         return (int)std::floor(myCurrentStateInterval->q * totalHourFraction + 0.5); // round to closest int
257     } else {
258         return -1;
259     }
260 }
261 
262 
263 double
currentFlow() const264 MSCalibrator::currentFlow() const {
265     const double totalHourFraction = STEPS2TIME(MSNet::getInstance()->getCurrentTimeStep() - myCurrentStateInterval->begin) / (double) 3600.;
266     return passed() / totalHourFraction;
267 }
268 
269 double
currentSpeed() const270 MSCalibrator::currentSpeed() const {
271     if (myEdgeMeanData.getSamples() > 0) {
272         return myEdgeMeanData.getTravelledDistance() / myEdgeMeanData.getSamples();
273     } else {
274         return -1;
275     }
276 }
277 
278 
279 bool
removePending()280 MSCalibrator::removePending() {
281     if (myToRemove.size() > 0) {
282         MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
283         // it is not save to remove the vehicles inside
284         // VehicleRemover::notifyEnter so we do it here
285         for (std::set<std::string>::iterator it = myToRemove.begin(); it != myToRemove.end(); ++it) {
286             MSVehicle* vehicle = dynamic_cast<MSVehicle*>(vc.getVehicle(*it));
287             if (vehicle != nullptr) {
288                 vehicle->onRemovalFromNet(MSMoveReminder::NOTIFICATION_VAPORIZED);
289                 vehicle->getLane()->removeVehicle(vehicle, MSMoveReminder::NOTIFICATION_VAPORIZED);
290                 vc.scheduleVehicleRemoval(vehicle);
291             } else {
292                 WRITE_WARNING("Calibrator '" + getID() + "' could not remove vehicle '" + *it + "'.");
293             }
294         }
295         myToRemove.clear();
296         return true;
297     }
298     return false;
299 }
300 
301 
302 SUMOTime
execute(SUMOTime currentTime)303 MSCalibrator::execute(SUMOTime currentTime) {
304     // get current simulation values (valid for the last simulation second)
305     // XXX could we miss vehicle movements if this is called less often than every DELTA_T (default) ?
306     updateMeanData();
307     const bool hadRemovals = removePending();
308     // check whether an adaptation value exists
309     if (isCurrentStateActive(currentTime)) {
310         myAmActive = true;
311         // all happens in isCurrentStateActive()
312     } else {
313         myAmActive = false;
314         reset();
315         if (!mySpeedIsDefault) {
316             // reset speed to default
317             if (myLane == nullptr) {
318                 myEdge->setMaxSpeed(myDefaultSpeed);
319             } else {
320                 myLane->setMaxSpeed(myDefaultSpeed);
321             }
322             mySpeedIsDefault = true;
323         }
324         if (myCurrentStateInterval == myIntervals.end()) {
325             // keep calibrator alive for gui but do not call again
326             return TIME2STEPS(86400);
327         }
328         return myFrequency;
329     }
330     // we are active
331     if (!myDidSpeedAdaption && myCurrentStateInterval->v >= 0) {
332         if (myLane == nullptr) {
333             myEdge->setMaxSpeed(myCurrentStateInterval->v);
334         } else {
335             myLane->setMaxSpeed(myCurrentStateInterval->v);
336         }
337         mySpeedIsDefault = false;
338         myDidSpeedAdaption = true;
339     }
340 
341     const bool calibrateFlow = myCurrentStateInterval->q >= 0;
342     const int totalWishedNum = totalWished();
343     int adaptedNum = passed() + myClearedInJam;
344 #ifdef MSCalibrator_DEBUG
345     std::cout << time2string(currentTime) << " " << myID
346               << " q=" << myCurrentStateInterval->q
347               << " totalWished=" << totalWishedNum
348               << " adapted=" << adaptedNum
349               << " jam=" << invalidJam(myLane == 0 ? -1 : myLane->getIndex())
350               << " entered=" << myEdgeMeanData.nVehEntered
351               << " departed=" << myEdgeMeanData.nVehDeparted
352               << " arrived=" << myEdgeMeanData.nVehArrived
353               << " left=" << myEdgeMeanData.nVehLeft
354               << " waitSecs=" << myEdgeMeanData.waitSeconds
355               << " vaporized=" << myEdgeMeanData.nVehVaporized
356               << "\n";
357 #endif
358     if (calibrateFlow && adaptedNum < totalWishedNum && !hadRemovals) {
359         // we need to insert some vehicles
360         const double hourFraction = STEPS2TIME(currentTime - myCurrentStateInterval->begin + DELTA_T) / (double) 3600.;
361         const int wishedNum = (int)std::floor(myCurrentStateInterval->q * hourFraction + 0.5); // round to closest int
362         // only the difference between inflow and aspiredFlow should be added, thus
363         // we should not count vehicles vaporized from a jam here
364         // if we have enough time left we can add missing vehicles later
365         const int relaxedInsertion = (int)std::floor(STEPS2TIME(myCurrentStateInterval->end - currentTime) / 3);
366         const int insertionSlack = MAX2(0, adaptedNum + relaxedInsertion - totalWishedNum);
367         // increase number of vehicles
368 #ifdef MSCalibrator_DEBUG
369         std::cout
370                 << "   wished:" << wishedNum
371                 << " slack:" << insertionSlack
372                 << " before:" << adaptedNum
373                 << "\n";
374 #endif
375         while (wishedNum > adaptedNum + insertionSlack) {
376             SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
377             const MSRoute* route = myProbe != nullptr ? myProbe->getRoute() : nullptr;
378             if (route == nullptr) {
379                 route = MSRoute::dictionary(pars->routeid);
380             }
381             if (route == nullptr) {
382                 WRITE_WARNING("No valid routes in calibrator '" + myID + "'.");
383                 break;
384             }
385             if (!route->contains(myEdge)) {
386                 WRITE_WARNING("Route '" + route->getID() + "' in calibrator '" + myID + "' does not contain edge '" + myEdge->getID() + "'.");
387                 break;
388             }
389             const int routeIndex = (int)std::distance(route->begin(),
390                                    std::find(route->begin(), route->end(), myEdge));
391             MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(pars->vtypeid);
392             assert(route != 0 && vtype != 0);
393             // build the vehicle
394             SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
395             newPars->id = myID + "." + toString((int)STEPS2TIME(myCurrentStateInterval->begin)) + "." + toString(myInserted);
396             newPars->depart = currentTime;
397             newPars->routeid = route->getID();
398             MSVehicle* vehicle;
399             try {
400                 vehicle = dynamic_cast<MSVehicle*>(MSNet::getInstance()->getVehicleControl().buildVehicle(
401                                                        newPars, route, vtype, true, false));
402             } catch (const ProcessError& e) {
403                 if (!MSGlobals::gCheckRoutes) {
404                     WRITE_WARNING(e.what());
405                     vehicle = nullptr;
406                     break;
407                 } else {
408                     throw e;
409                 }
410             }
411 #ifdef MSCalibrator_DEBUG
412             std::cout << " resetting route pos: " << routeIndex << "\n";
413 #endif
414             vehicle->resetRoutePosition(routeIndex);
415             if (myEdge->insertVehicle(*vehicle, currentTime)) {
416                 if (!MSNet::getInstance()->getVehicleControl().addVehicle(vehicle->getID(), vehicle)) {
417                     throw ProcessError("Emission of vehicle '" + vehicle->getID() + "' in calibrator '" + getID() + "'failed!");
418                 }
419                 myInserted++;
420                 adaptedNum++;
421 #ifdef MSCalibrator_DEBUG
422                 std::cout << "I ";
423 #endif
424             } else {
425                 // could not insert vehicle
426 #ifdef MSCalibrator_DEBUG
427                 std::cout << "F ";
428 #endif
429                 MSNet::getInstance()->getVehicleControl().deleteVehicle(vehicle, true);
430                 break;
431             }
432         }
433     }
434     if (myCurrentStateInterval->end <= currentTime + myFrequency) {
435         writeXMLOutput();
436     }
437     return myFrequency;
438 }
439 
440 void
reset()441 MSCalibrator::reset() {
442     myEdgeMeanData.reset();
443     for (std::vector<MSMeanData_Net::MSLaneMeanDataValues*>::iterator it = myLaneMeanData.begin(); it != myLaneMeanData.end(); ++it) {
444         (*it)->reset();
445     }
446 }
447 
448 
449 bool
invalidJam(int laneIndex) const450 MSCalibrator::invalidJam(int laneIndex) const {
451     if (laneIndex < 0) {
452         const int numLanes = (int)myEdge->getLanes().size();
453         for (int i = 0; i < numLanes; ++i) {
454             if (invalidJam(i)) {
455                 return true;
456             }
457         }
458         return false;
459     }
460     assert(laneIndex < (int)myEdge->getLanes().size());
461     const MSLane* const lane = myEdge->getLanes()[laneIndex];
462     if (lane->getVehicleNumber() < 4) {
463         // cannot reliably detect invalid jams
464         return false;
465     }
466     // maxSpeed reflects the calibration target
467     const bool toSlow = lane->getMeanSpeed() < 0.5 * myEdge->getSpeedLimit();
468     return toSlow && remainingVehicleCapacity(laneIndex) < 1;
469 }
470 
471 
472 int
remainingVehicleCapacity(int laneIndex) const473 MSCalibrator::remainingVehicleCapacity(int laneIndex) const {
474     if (laneIndex < 0) {
475         const int numLanes = (int)myEdge->getLanes().size();
476         int result = 0;
477         for (int i = 0; i < numLanes; ++i) {
478             result = MAX2(result, remainingVehicleCapacity(i));
479         }
480         return result;
481     }
482     assert(laneIndex < (int)myEdge->getLanes().size());
483     MSLane* lane = myEdge->getLanes()[laneIndex];
484     MSVehicle* last = lane->getLastFullVehicle();
485     const SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
486     const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(pars->vtypeid);
487     const double spacePerVehicle = vtype->getLengthWithGap() + myEdge->getSpeedLimit() * vtype->getCarFollowModel().getHeadwayTime();
488     if (last == nullptr) {
489         // ensure vehicles can be inserted on short edges
490         return MAX2(1, (int)(myEdge->getLength() / spacePerVehicle));
491     } else {
492         return (int)(last->getPositionOnLane() / spacePerVehicle);
493     }
494 }
495 
496 
497 void
cleanup()498 MSCalibrator::cleanup() {
499     for (std::vector<MSMoveReminder*>::iterator it = LeftoverReminders.begin(); it != LeftoverReminders.end(); ++it) {
500         delete *it;
501     }
502     LeftoverReminders.clear();
503     for (std::vector<SUMOVehicleParameter*>::iterator it = LeftoverVehicleParameters.begin();
504             it != LeftoverVehicleParameters.end(); ++it) {
505         delete *it;
506     }
507     LeftoverVehicleParameters.clear();
508 }
509 
510 
511 void
updateMeanData()512 MSCalibrator::updateMeanData() {
513     myEdgeMeanData.reset();
514     for (std::vector<MSMeanData_Net::MSLaneMeanDataValues*>::iterator it = myLaneMeanData.begin();
515             it != myLaneMeanData.end(); ++it) {
516         (*it)->addTo(myEdgeMeanData);
517     }
518 }
519 
notifyEnter(SUMOTrafficObject & veh,Notification,const MSLane *)520 bool MSCalibrator::VehicleRemover::notifyEnter(SUMOTrafficObject& veh, Notification /* reason */, const MSLane* /* enteredLane */) {
521     if (myParent == nullptr) {
522         return false;
523     }
524     if (myParent->isActive()) {
525         myParent->updateMeanData();
526         const bool calibrateFlow = myParent->myCurrentStateInterval->q >= 0;
527         const int totalWishedNum = myParent->totalWished();
528         int adaptedNum = myParent->passed() + myParent->myClearedInJam;
529         MSVehicle* vehicle = dynamic_cast<MSVehicle*>(&veh);
530         if (calibrateFlow && adaptedNum > totalWishedNum) {
531 #ifdef MSCalibrator_DEBUG
532             std::cout << time2string(MSNet::getInstance()->getCurrentTimeStep()) << " " << myParent->getID()
533                       << " vaporizing " << vehicle->getID() << " to reduce flow\n";
534 #endif
535             if (myParent->scheduleRemoval(vehicle)) {
536                 myParent->myRemoved++;
537             }
538         } else if (myParent->invalidJam(myLaneIndex)) {
539 #ifdef MSCalibrator_DEBUG
540             std::cout << time2string(MSNet::getInstance()->getCurrentTimeStep()) << " " << myParent->getID()
541                       << " vaporizing " << vehicle->getID() << " to clear jam\n";
542 #endif
543             if (!myParent->myHaveWarnedAboutClearingJam) {
544                 WRITE_WARNING("Clearing jam at calibrator '" + myParent->myID + "' at time "
545                               + time2string(MSNet::getInstance()->getCurrentTimeStep()));
546                 myParent->myHaveWarnedAboutClearingJam = true;
547             }
548             if (myParent->scheduleRemoval(vehicle)) {
549                 myParent->myClearedInJam++;
550             }
551         }
552     }
553     return true;
554 }
555 
556 
557 
558 /****************************************************************************/
559 
560