Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/microsim/MSInsertionControl.cpp
185785 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 MSInsertionControl.cpp
15
/// @author Christian Roessel
16
/// @author Daniel Krajzewicz
17
/// @author Axel Wegener
18
/// @author Michael Behrisch
19
/// @author Jakob Erdmann
20
/// @author Mirko Barthauer
21
/// @date Mon, 12 Mar 2001
22
///
23
// Inserts vehicles into the network when their departure time is reached
24
/****************************************************************************/
25
#include <config.h>
26
27
#include <iostream>
28
#include <algorithm>
29
#include <cassert>
30
#include <iterator>
31
#include <utils/router/IntermodalRouter.h>
32
#include <microsim/devices/MSDevice_Routing.h>
33
#include <microsim/devices/MSRoutingEngine.h>
34
#include "MSGlobals.h"
35
#include "MSVehicle.h"
36
#include "MSVehicleControl.h"
37
#include "MSLane.h"
38
#include "MSEdge.h"
39
#include "MSNet.h"
40
#include "MSRouteHandler.h"
41
#include "MSInsertionControl.h"
42
43
44
// ===========================================================================
45
// member method definitions
46
// ===========================================================================
47
MSInsertionControl::MSInsertionControl(MSVehicleControl& vc,
48
SUMOTime maxDepartDelay,
49
bool eagerInsertionCheck,
50
int maxVehicleNumber,
51
SUMOTime randomDepartOffset) :
52
myVehicleControl(vc),
53
myMaxDepartDelay(maxDepartDelay),
54
myEagerInsertionCheck(eagerInsertionCheck),
55
myMaxVehicleNumber(maxVehicleNumber),
56
myPendingEmitsUpdateTime(SUMOTime_MIN),
57
myFlowRNG("flow") {
58
myMaxRandomDepartOffset = randomDepartOffset;
59
RandHelper::initRandGlobal(&myFlowRNG);
60
}
61
62
63
MSInsertionControl::~MSInsertionControl() {
64
for (const Flow& f : myFlows) {
65
delete (f.pars);
66
}
67
}
68
69
70
void
71
MSInsertionControl::add(SUMOVehicle* veh) {
72
myAllVeh.add(veh);
73
}
74
75
76
bool
77
MSInsertionControl::addFlow(SUMOVehicleParameter* const pars, int index) {
78
if (myFlowIDs.count(pars->id) > 0) {
79
return false;
80
}
81
const bool loadingFromState = index >= 0;
82
Flow flow{pars, loadingFromState ? index : 0, initScale(pars->vtypeid)};
83
if (!loadingFromState && pars->repetitionProbability < 0 && pars->repetitionOffset < 0) {
84
// init poisson flow (but only the timing)
85
flow.pars->incrementFlow(flow.scale, &myFlowRNG);
86
flow.pars->repetitionsDone--;
87
}
88
myFlows.emplace_back(flow);
89
myFlowIDs.insert(std::make_pair(pars->id, flow.index));
90
return true;
91
}
92
93
94
double
95
MSInsertionControl::initScale(const std::string vtypeid) {
96
MSVehicleControl& vc = MSNet::getInstance()->getVehicleControl();
97
if (vc.hasVTypeDistribution(vtypeid)) {
98
double result = -1;
99
const RandomDistributor<MSVehicleType*>* dist = vc.getVTypeDistribution(vtypeid);
100
for (const MSVehicleType* t : dist->getVals()) {
101
if (result == -1) {
102
result = t->getParameter().scale;
103
} else if (result != t->getParameter().scale) {
104
// unequal scales in distribution
105
return -1;
106
}
107
}
108
return result;
109
} else {
110
// rng is not used since vtypeid is not a distribution
111
return vc.getVType(vtypeid, nullptr, true)->getParameter().scale;
112
}
113
}
114
115
116
void
117
MSInsertionControl::updateScale(const std::string vtypeid) {
118
for (Flow& f : myFlows) {
119
if (f.pars->vtypeid == vtypeid) {
120
f.scale = initScale(vtypeid);
121
}
122
}
123
}
124
125
126
int
127
MSInsertionControl::emitVehicles(SUMOTime time) {
128
// check whether any vehicles shall be emitted within this time step
129
const bool havePreChecked = MSRoutingEngine::isEnabled();
130
if (myPendingEmits.empty() || (havePreChecked && myEmitCandidates.empty())) {
131
return 0;
132
}
133
int numEmitted = 0;
134
// we use buffering for the refused emits to save time
135
// for this, we have two lists; one contains previously refused emits, the second
136
// will be used to append those vehicles that will not be able to depart in this
137
// time step
138
MSVehicleContainer::VehicleVector refusedEmits;
139
140
// go through the list of previously refused vehicles, first
141
MSVehicleContainer::VehicleVector::const_iterator veh;
142
for (veh = myPendingEmits.begin(); veh != myPendingEmits.end(); veh++) {
143
if (havePreChecked && (myEmitCandidates.count(*veh) == 0)) {
144
refusedEmits.push_back(*veh);
145
} else {
146
numEmitted += tryInsert(time, *veh, refusedEmits);
147
}
148
}
149
myEmitCandidates.clear();
150
myPendingEmits = refusedEmits;
151
return numEmitted;
152
}
153
154
155
int
156
MSInsertionControl::tryInsert(SUMOTime time, SUMOVehicle* veh,
157
MSVehicleContainer::VehicleVector& refusedEmits) {
158
assert(veh->getParameter().depart <= time);
159
const MSEdge& edge = *veh->getEdge();
160
if (veh->isOnRoad()) {
161
return 1;
162
}
163
if ((myMaxVehicleNumber < 0 || (int)MSNet::getInstance()->getVehicleControl().getRunningVehicleNo() < myMaxVehicleNumber)
164
&& edge.insertVehicle(*veh, time, false, myEagerInsertionCheck || veh->getParameter().departProcedure == DepartDefinition::SPLIT)) {
165
// Successful insertion
166
return 1;
167
}
168
if (myMaxDepartDelay >= 0 && time - veh->getParameter().depart > myMaxDepartDelay) {
169
// remove vehicles waiting too long for departure
170
myVehicleControl.deleteVehicle(veh, true);
171
} else if (edge.isVaporizing()) {
172
// remove vehicles if the edge shall be empty
173
myVehicleControl.deleteVehicle(veh, true);
174
} else if (myAbortedEmits.count(veh) > 0) {
175
// remove vehicles which shall not be inserted for some reason
176
myAbortedEmits.erase(veh);
177
myVehicleControl.deleteVehicle(veh, true);
178
} else if ((veh->getRouteValidity(false) & (
179
MSBaseVehicle::ROUTE_START_INVALID_LANE
180
| MSBaseVehicle::ROUTE_START_INVALID_PERMISSIONS)) != 0) {
181
myVehicleControl.deleteVehicle(veh, true);
182
} else {
183
// let the vehicle wait one step, we'll retry then
184
refusedEmits.push_back(veh);
185
}
186
edge.setLastFailedInsertionTime(time);
187
return 0;
188
}
189
190
191
void
192
MSInsertionControl::checkCandidates(SUMOTime time, const bool preCheck) {
193
while (myAllVeh.anyWaitingBefore(time)) {
194
const MSVehicleContainer::VehicleVector& top = myAllVeh.top();
195
copy(top.begin(), top.end(), back_inserter(myPendingEmits));
196
myAllVeh.pop();
197
}
198
if (preCheck) {
199
MSVehicleContainer::VehicleVector::const_iterator veh;
200
for (veh = myPendingEmits.begin(); veh != myPendingEmits.end(); veh++) {
201
SUMOVehicle* const v = *veh;
202
const MSEdge* const edge = v->getEdge();
203
if (edge->insertVehicle(*v, time, true, myEagerInsertionCheck)) {
204
myEmitCandidates.insert(v);
205
} else {
206
MSDevice_Routing* dev = static_cast<MSDevice_Routing*>(v->getDevice(typeid(MSDevice_Routing)));
207
if (dev != nullptr) {
208
dev->skipRouting(time);
209
}
210
}
211
}
212
}
213
}
214
215
216
void
217
MSInsertionControl::determineCandidates(SUMOTime time) {
218
MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
219
// for equidistant vehicles, up-scaling is done via repetitionOffset
220
for (std::vector<Flow>::iterator i = myFlows.begin(); i != myFlows.end();) {
221
MSVehicleType* vtype = nullptr;
222
SUMOVehicleParameter* pars = i->pars;
223
double typeScale = i->scale;
224
if (typeScale < 0) {
225
// must sample from distribution to determine scale value
226
vtype = vehControl.getVType(pars->vtypeid, MSRouteHandler::getParsingRNG());
227
typeScale = vtype->getParameter().scale;
228
}
229
double scale = vehControl.getScale() * typeScale;
230
bool tryEmitByProb = pars->repetitionProbability > 0;
231
while (scale > 0 && ((pars->repetitionProbability < 0
232
&& pars->repetitionsDone < pars->repetitionNumber * scale
233
&& pars->depart + pars->repetitionTotalOffset <= time)
234
|| (tryEmitByProb
235
&& pars->depart <= time
236
&& pars->repetitionEnd > time
237
// only call rand if all other conditions are met
238
&& RandHelper::rand(&myFlowRNG) < (pars->repetitionProbability * TS))
239
)) {
240
tryEmitByProb = false; // only emit one per step
241
SUMOVehicleParameter* newPars = new SUMOVehicleParameter(*pars);
242
newPars->id = pars->id + "." + toString(i->index);
243
newPars->depart = pars->repetitionProbability > 0 ? time : pars->depart + pars->repetitionTotalOffset + computeRandomDepartOffset();
244
pars->incrementFlow(scale, &myFlowRNG);
245
myFlowIDs[pars->id] = i->index;
246
//std::cout << SIMTIME << " flow=" << pars->id << " done=" << pars->repetitionsDone << " totalOffset=" << STEPS2TIME(pars->repetitionTotalOffset) << "\n";
247
// try to build the vehicle
248
if (vehControl.getVehicle(newPars->id) == nullptr) {
249
ConstMSRoutePtr const route = MSRoute::dictionary(pars->routeid);
250
if (vtype == nullptr) {
251
vtype = vehControl.getVType(pars->vtypeid, MSRouteHandler::getParsingRNG());
252
}
253
SUMOVehicle* const vehicle = vehControl.buildVehicle(newPars, route, vtype, !MSGlobals::gCheckRoutes);
254
// for equidistant vehicles, all scaling is done via repetitionOffset (to avoid artefacts, #11441)
255
// for probabilistic vehicles, we use the quota
256
int quota = pars->repetitionProbability < 0 ? 1 : vehControl.getQuota(scale);
257
if (quota > 0) {
258
vehControl.addVehicle(newPars->id, vehicle);
259
if (pars->departProcedure == DepartDefinition::GIVEN || pars->departProcedure == DepartDefinition::BEGIN) {
260
add(vehicle);
261
}
262
i->index++;
263
while (--quota > 0) {
264
SUMOVehicleParameter* const quotaPars = new SUMOVehicleParameter(*pars);
265
quotaPars->id = pars->id + "." + toString(i->index);
266
quotaPars->depart = pars->repetitionProbability > 0 ? time :
267
pars->depart + pars->repetitionsDone * pars->repetitionTotalOffset + computeRandomDepartOffset();
268
SUMOVehicle* const quotaVehicle = vehControl.buildVehicle(quotaPars, route, vtype, !MSGlobals::gCheckRoutes);
269
vehControl.addVehicle(quotaPars->id, quotaVehicle);
270
if (pars->departProcedure == DepartDefinition::GIVEN || pars->departProcedure == DepartDefinition::BEGIN) {
271
add(quotaVehicle);
272
}
273
pars->repetitionsDone++;
274
i->index++;
275
}
276
} else {
277
vehControl.deleteVehicle(vehicle, true);
278
}
279
} else {
280
if (MSGlobals::gStateLoaded) {
281
/// @note probably obsolete since flows save their state
282
break;
283
}
284
throw ProcessError(TLF("Another vehicle with the id '%' exists.", newPars->id));
285
}
286
vtype = nullptr;
287
}
288
if (time >= pars->repetitionEnd ||
289
(pars->repetitionNumber != std::numeric_limits<long long int>::max()
290
&& pars->repetitionsDone >= (long long int)(pars->repetitionNumber * scale + 0.5))) {
291
i = myFlows.erase(i);
292
MSRoute::checkDist(pars->routeid);
293
delete pars;
294
} else {
295
++i;
296
}
297
}
298
checkCandidates(time, MSRoutingEngine::isEnabled());
299
}
300
301
302
int
303
MSInsertionControl::getWaitingVehicleNo() const {
304
return (int)myPendingEmits.size();
305
}
306
307
308
int
309
MSInsertionControl::getPendingFlowCount() const {
310
return (int)myFlows.size();
311
}
312
313
314
void
315
MSInsertionControl::descheduleDeparture(const SUMOVehicle* veh) {
316
myAbortedEmits.insert(veh);
317
}
318
319
void
320
MSInsertionControl::retractDescheduleDeparture(const SUMOVehicle* veh) {
321
myAbortedEmits.erase(veh);
322
}
323
324
325
void
326
MSInsertionControl::alreadyDeparted(SUMOVehicle* veh) {
327
myPendingEmits.erase(std::remove(myPendingEmits.begin(), myPendingEmits.end(), veh), myPendingEmits.end());
328
myAllVeh.remove(veh);
329
}
330
331
332
void
333
MSInsertionControl::clearPendingVehicles(const std::string& route) {
334
//clear out the refused vehicle list, deleting the vehicles entirely
335
MSVehicleContainer::VehicleVector::iterator veh;
336
for (veh = myPendingEmits.begin(); veh != myPendingEmits.end();) {
337
if ((*veh)->getRoute().getID() == route || route == "") {
338
myVehicleControl.deleteVehicle(*veh, true);
339
veh = myPendingEmits.erase(veh);
340
} else {
341
++veh;
342
}
343
}
344
}
345
346
347
int
348
MSInsertionControl::getPendingEmits(const MSLane* lane) {
349
if (MSNet::getInstance()->getCurrentTimeStep() != myPendingEmitsUpdateTime) {
350
// updated pending emits (only once per time step)
351
myPendingEmitsForLane.clear();
352
for (const SUMOVehicle* const veh : myPendingEmits) {
353
const MSLane* const vlane = veh->getLane();
354
if (vlane != nullptr) {
355
myPendingEmitsForLane[vlane]++;
356
} else {
357
// no (tentative) departLane was set, increase count for all
358
// lanes of the depart edge
359
for (const MSLane* const l : veh->getEdge()->getLanes()) {
360
myPendingEmitsForLane[l]++;
361
}
362
}
363
}
364
myPendingEmitsUpdateTime = MSNet::getInstance()->getCurrentTimeStep();
365
}
366
return myPendingEmitsForLane[lane];
367
}
368
369
370
void
371
MSInsertionControl::adaptIntermodalRouter(MSTransportableRouter& router) const {
372
// fill the public transport router with pre-parsed public transport lines
373
for (const Flow& f : myFlows) {
374
if (f.pars->line != "") {
375
ConstMSRoutePtr const route = MSRoute::dictionary(f.pars->routeid);
376
router.getNetwork()->addSchedule(*f.pars, route == nullptr ? nullptr : &route->getStops());
377
}
378
}
379
}
380
381
382
void
383
MSInsertionControl::saveState(OutputDevice& out) {
384
// save flow states
385
for (const Flow& flow : myFlows) {
386
flow.pars->write(out, OptionsCont::getOptions(), SUMO_TAG_FLOWSTATE,
387
flow.pars->vtypeid == DEFAULT_VTYPE_ID ? "" : flow.pars->vtypeid);
388
if (flow.pars->repetitionProbability <= 0) {
389
out.writeAttr(SUMO_ATTR_NEXT, STEPS2TIME(flow.pars->repetitionTotalOffset));
390
}
391
out.writeAttr(SUMO_ATTR_ROUTE, flow.pars->routeid);
392
out.writeAttr(SUMO_ATTR_DONE, flow.pars->repetitionsDone);
393
out.writeAttr(SUMO_ATTR_INDEX, flow.index);
394
if (flow.pars->wasSet(VEHPARS_FORCE_REROUTE)) {
395
out.writeAttr(SUMO_ATTR_REROUTE, true);
396
}
397
for (const SUMOVehicleParameter::Stop& stop : flow.pars->stops) {
398
stop.write(out);
399
}
400
out.closeTag();
401
}
402
}
403
404
405
void
406
MSInsertionControl::clearState() {
407
for (const Flow& f : myFlows) {
408
delete (f.pars);
409
}
410
myFlows.clear();
411
myFlowIDs.clear();
412
myAllVeh.clearState();
413
myPendingEmits.clear();
414
myEmitCandidates.clear();
415
myAbortedEmits.clear();
416
// myPendingEmitsForLane must not be cleared since it updates itself on the next call
417
}
418
419
420
SUMOTime
421
MSInsertionControl::computeRandomDepartOffset() const {
422
if (myMaxRandomDepartOffset > 0) {
423
// round to the closest usable simulation step
424
return DELTA_T * ((RandHelper::rand(myMaxRandomDepartOffset, MSRouteHandler::getParsingRNG()) + DELTA_T / 2) / DELTA_T);
425
}
426
return 0;
427
}
428
429
const SUMOVehicleParameter*
430
MSInsertionControl::getFlowPars(const std::string& id) const {
431
if (hasFlow(id)) {
432
for (const Flow& f : myFlows) {
433
if (f.pars->id == id) {
434
return f.pars;
435
}
436
}
437
}
438
return nullptr;
439
}
440
441
SUMOVehicle*
442
MSInsertionControl::getLastFlowVehicle(const std::string& id) const {
443
const auto it = myFlowIDs.find(id);
444
if (it != myFlowIDs.end()) {
445
const std::string vehID = id + "." + toString(it->second);
446
return MSNet::getInstance()->getVehicleControl().getVehicle(vehID);
447
}
448
return nullptr;
449
}
450
451
452
bool
453
MSInsertionControl::hasTaxiFlow() const {
454
SumoRNG tmp("tmp");
455
for (const Flow& flow : myFlows) {
456
if (flow.scale != 0 &&
457
(StringUtils::toBool(flow.pars->getParameter("has.taxi.device", "false"))
458
|| hasTaxiDeviceType(flow.pars->vtypeid, tmp))) {
459
return true;
460
}
461
}
462
return false;
463
}
464
465
466
bool
467
MSInsertionControl::hasTaxiDeviceType(const std::string& vtypeId, SumoRNG& rng) {
468
MSVehicleControl& vehControl = MSNet::getInstance()->getVehicleControl();
469
const MSVehicleType* vtype = vehControl.getVType(vtypeId, &rng);
470
return StringUtils::toBool(vtype->getParameter().getParameter("has.taxi.device", "false"));
471
}
472
473
/****************************************************************************/
474
475