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