Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/mesosim/METriggeredCalibrator.cpp
169666 views
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
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
const std::string& vTypes) :
59
MSCalibrator(id, edge, nullptr, nullptr, pos, aXMLFilename, outputFilename, freq, length, probe, invalidJamThreshold, vTypes, false, false),
60
mySegment(edge == nullptr ? nullptr : MSGlobals::gMesoNet->getSegmentForEdge(*edge, pos)) {
61
myEdgeMeanData.setDescription("meandata_calibrator_" + getID());
62
if (mySegment != nullptr) {
63
mySegment->addDetector(&myEdgeMeanData);
64
}
65
}
66
67
68
METriggeredCalibrator::~METriggeredCalibrator() {
69
if (myCurrentStateInterval != myIntervals.end()) {
70
// need to do it here and not in MSCalibrator because otherwise meandata is gone
71
intervalEnd();
72
// but avoid to call it again in MSCalibrator
73
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
}
78
79
80
bool
81
METriggeredCalibrator::tryEmit(MESegment* s, MEVehicle* vehicle) {
82
if (s->initialise(vehicle, vehicle->getParameter().depart)) {
83
if (!MSNet::getInstance()->getVehicleControl().addVehicle(vehicle->getID(), vehicle)) {
84
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
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
mySegment->prepareDetectorForWriting(myEdgeMeanData);
97
98
// check whether an adaptation value exists
99
if (isCurrentStateActive(currentTime)) {
100
// all happens in isCurrentStateActive()
101
myAmActive = true;
102
} else {
103
myAmActive = false;
104
myEdgeMeanData.reset(); // discard collected values
105
if (!mySpeedIsDefault) {
106
// if not, reset adaptation values
107
const double jamThresh = OptionsCont::getOptions().getFloat("meso-jam-threshold");
108
myEdge->setMaxSpeed(myDefaultSpeed, jamThresh);
109
mySpeedIsDefault = true;
110
}
111
if (myCurrentStateInterval == myIntervals.end()) {
112
// keep calibrator alive but do not call again
113
return TIME2STEPS(86400);
114
}
115
return myFrequency;
116
}
117
const bool calibrateFlow = myCurrentStateInterval->q >= 0;
118
const bool calibrateSpeed = myCurrentStateInterval->v >= 0;
119
// we are active
120
if (!myDidSpeedAdaption && calibrateSpeed && myCurrentStateInterval->v != mySegment->getEdge().getSpeedLimit()) {
121
myEdge->setMaxSpeed(myCurrentStateInterval->v);
122
mySpeedIsDefault = false;
123
myDidSpeedAdaption = true;
124
}
125
// clear invalid jams
126
bool hadInvalidJam = false;
127
while ((calibrateFlow || calibrateSpeed) && invalidJam()) {
128
hadInvalidJam = true;
129
if (!myHaveWarnedAboutClearingJam) {
130
WRITE_WARNINGF(TL("Clearing jam at calibrator '%' at time=%."), getID(), time2string(currentTime));
131
}
132
// remove one vehicle currently on the segment
133
if (mySegment->vaporizeAnyCar(currentTime, this)) {
134
myClearedInJam++;
135
} else {
136
if (!myHaveWarnedAboutClearingJam) {
137
// this frequenly happens for very short edges
138
WRITE_WARNINGF(TL("Could not clear jam at calibrator '%' at time=%."), getID(), time2string(currentTime));
139
}
140
break;
141
}
142
myHaveWarnedAboutClearingJam = true;
143
}
144
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
const double totalHourFraction = STEPS2TIME(myCurrentStateInterval->end - myCurrentStateInterval->begin) / (double) 3600.;
149
const int totalWishedNum = (int)std::floor(myCurrentStateInterval->q * totalHourFraction + 0.5); // round to closest int
150
int adaptedNum = passed() + myClearedInJam;
151
if (!hadInvalidJam) {
152
// only add vehicles if we do not have an invalid upstream jam to prevent spill-back
153
const double hourFraction = STEPS2TIME(currentTime - myCurrentStateInterval->begin + DELTA_T) / (double) 3600.;
154
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
const int relaxedInsertion = (int)std::floor(STEPS2TIME(myCurrentStateInterval->end - currentTime) / 3);
159
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
MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
163
while (wishedNum > adaptedNum + insertionSlack && remainingVehicleCapacity() > maximumInflow()) {
164
SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
165
ConstMSRoutePtr route = myProbe != nullptr ? myProbe->sampleRoute() : nullptr;
166
if (route == nullptr) {
167
route = MSRoute::dictionary(pars->routeid);
168
}
169
if (route == nullptr) {
170
WRITE_WARNINGF(TL("No valid routes in calibrator '%'."), getID());
171
break;
172
}
173
if (!route->contains(myEdge)) {
174
WRITE_WARNINGF(TL("Route '%' in calibrator '%' does not contain edge '%'."), route->getID(), getID(), myEdge->getID());
175
break;
176
}
177
MSVehicleType* vtype = vc.getVType(pars->vtypeid);
178
assert(route != 0 && vtype != 0);
179
// build the vehicle
180
const SUMOTime depart = mySegment->getNextInsertionTime(currentTime);
181
SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
182
newPars->id = getNewVehicleID();
183
newPars->depart = depart;
184
newPars->routeid = route->getID();
185
MEVehicle* vehicle;
186
try {
187
vehicle = static_cast<MEVehicle*>(vc.buildVehicle(newPars, route, vtype, false, MSVehicleControl::VehicleDefinitionSource::TRIGGER));
188
std::string msg;
189
if (!vehicle->hasValidRouteStart(msg)) {
190
throw ProcessError(msg);
191
}
192
} catch (const ProcessError& e) {
193
if (!MSGlobals::gCheckRoutes) {
194
WRITE_WARNING(e.what());
195
vehicle = nullptr;
196
break;
197
} else {
198
throw;
199
}
200
}
201
const bool duplicate = vc.getVehicle(newPars->id) != nullptr;
202
// duplicate ids could come from loading state
203
if (duplicate) {
204
vc.deleteVehicle(vehicle, true);
205
continue;
206
}
207
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
const MSEdge* myedge = &mySegment->getEdge();
211
bool atDest = false;
212
while (vehicle->getEdge() != myedge) {
213
// let the vehicle move to the next edge
214
atDest = vehicle->moveRoutePointer();
215
}
216
// insert vehicle into the net
217
if (atDest || !tryEmit(mySegment, vehicle)) {
218
//std::cout << "F ";
219
vc.deleteVehicle(vehicle, true);
220
break;
221
}
222
//std::cout << "I ";
223
myInserted++;
224
adaptedNum++;
225
}
226
}
227
//std::cout << " after:" << adaptedNum << "\n";
228
// we only remove vehicles once we really have to
229
while (totalWishedNum < adaptedNum) {
230
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
myRemoved++;
235
adaptedNum--;
236
}
237
}
238
if (myCurrentStateInterval->end <= currentTime + myFrequency) {
239
intervalEnd();
240
}
241
//assert(!invalidJam());
242
if (invalidJam()) {
243
WRITE_WARNINGF("DEBUG: Could not clear jam at calibrator '%' at time=%.", getID(), time2string(currentTime));
244
}
245
return myFrequency;
246
}
247
248
249
bool
250
METriggeredCalibrator::invalidJam() const {
251
if (mySegment->getBruttoOccupancy() == 0.) {
252
return false;
253
}
254
// maxSpeed reflects the calibration target
255
const bool toSlow = mySegment->getMeanSpeed() < myInvalidJamThreshold * mySegment->getEdge().getSpeedLimit();
256
return toSlow && remainingVehicleCapacity() < maximumInflow();
257
}
258
259
260
int
261
METriggeredCalibrator::remainingVehicleCapacity() const {
262
const SUMOVehicleParameter* pars = myCurrentStateInterval->vehicleParameter;
263
const MSVehicleType* vtype = MSNet::getInstance()->getVehicleControl().getVType(pars->vtypeid);
264
return mySegment->remainingVehicleCapacity(vtype->getLengthWithGap());
265
}
266
267
268
void
269
METriggeredCalibrator::reset() {
270
myEdgeMeanData.reset();
271
}
272
273
274
/****************************************************************************/
275
276