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