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 : /****************************************************************************/
|