Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/mesosim/MESegment.cpp
193723 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2026 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 MESegment.cpp
15
/// @author Daniel Krajzewicz
16
/// @date Tue, May 2005
17
///
18
// A single mesoscopic segment (cell)
19
/****************************************************************************/
20
#include <config.h>
21
22
#include <algorithm>
23
#include <limits>
24
#include <utils/common/StdDefs.h>
25
#include <microsim/MSGlobals.h>
26
#include <microsim/MSEdge.h>
27
#include <microsim/MSJunction.h>
28
#include <microsim/MSNet.h>
29
#include <microsim/MSLane.h>
30
#include <microsim/MSLink.h>
31
#include <microsim/MSMoveReminder.h>
32
#include <microsim/traffic_lights/MSTrafficLightLogic.h>
33
#include <microsim/output/MSXMLRawOut.h>
34
#include <microsim/output/MSDetectorFileOutput.h>
35
#include <microsim/MSVehicleControl.h>
36
#include <microsim/devices/MSDevice.h>
37
#include <utils/common/FileHelpers.h>
38
#include <utils/common/MsgHandler.h>
39
#include <utils/iodevices/OutputDevice.h>
40
#include <utils/common/RandHelper.h>
41
#include "MEVehicle.h"
42
#include "MELoop.h"
43
#include "MESegment.h"
44
45
#define DEFAULT_VEH_LENGTH_WITH_GAP (SUMOVTypeParameter::getDefault().length + SUMOVTypeParameter::getDefault().minGap)
46
// avoid division by zero when driving very slowly
47
#define MESO_MIN_SPEED (0.05)
48
49
//#define DEBUG_OPENED
50
//#define DEBUG_JAMTHRESHOLD
51
//#define DEBUG_COND (getID() == "blocker")
52
//#define DEBUG_COND (true)
53
#define DEBUG_COND (myEdge.isSelected())
54
#define DEBUG_COND2(obj) ((obj != 0 && (obj)->isSelected()))
55
56
57
// ===========================================================================
58
// static member definition
59
// ===========================================================================
60
MSEdge MESegment::myDummyParent("MESegmentDummyParent", -1, SumoXMLEdgeFunc::UNKNOWN, "", "", "", -1, 0);
61
MESegment MESegment::myVaporizationTarget("vaporizationTarget");
62
const double MESegment::DO_NOT_PATCH_JAM_THRESHOLD(std::numeric_limits<double>::max());
63
const std::string MESegment::OVERRIDE_TLS_PENALTIES("meso.tls.control");
64
65
66
// ===========================================================================
67
// MESegment::Queue method definitions
68
// ===========================================================================
69
MEVehicle*
70
MESegment::Queue::remove(MEVehicle* v) {
71
myOccupancy -= v->getVehicleType().getLengthWithGap();
72
assert(std::find(myVehicles.begin(), myVehicles.end(), v) != myVehicles.end());
73
if (v == myVehicles.back()) {
74
myVehicles.pop_back();
75
if (myVehicles.empty()) {
76
myOccupancy = 0.;
77
} else {
78
return myVehicles.back();
79
}
80
} else {
81
myVehicles.erase(std::find(myVehicles.begin(), myVehicles.end(), v));
82
}
83
return nullptr;
84
}
85
86
void
87
MESegment::Queue::addDetector(MSMoveReminder* data) {
88
myDetectorData.push_back(data);
89
for (MEVehicle* const v : myVehicles) {
90
v->addReminder(data);
91
}
92
}
93
94
void
95
MESegment::Queue::addReminders(MEVehicle* veh) const {
96
for (MSMoveReminder* rem : myDetectorData) {
97
veh->addReminder(rem);
98
}
99
}
100
101
// ===========================================================================
102
// MESegment method definitions
103
// ===========================================================================
104
MESegment::MESegment(const std::string& id,
105
const MSEdge& parent, MESegment* next,
106
const double length, const double speed,
107
const int idx,
108
const bool multiQueue,
109
const MesoEdgeType& edgeType):
110
Named(id), myEdge(parent), myNextSegment(next),
111
myLength(length), myIndex(idx),
112
myTau_length(TIME2STEPS(1) / MAX2(MESO_MIN_SPEED, speed)),
113
myNumVehicles(0),
114
myLastHeadway(TIME2STEPS(-1)),
115
myMeanSpeed(speed),
116
myLastMeanSpeedUpdate(SUMOTime_MIN) {
117
118
const std::vector<MSLane*>& lanes = parent.getLanes();
119
int usableLanes = 0;
120
for (MSLane* const l : lanes) {
121
const SVCPermissions allow = MSEdge::getMesoPermissions(l->getPermissions());
122
if (multiQueue) {
123
myQueues.push_back(Queue(allow));
124
}
125
if (allow != 0) {
126
usableLanes++;
127
}
128
}
129
if (usableLanes == 0) {
130
// cars won't drive here. Give sensible tau values capacity for the ignored classes
131
usableLanes = 1;
132
}
133
if (multiQueue) {
134
if (next == nullptr) {
135
for (const MSEdge* const edge : parent.getSuccessors()) {
136
if (edge->isTazConnector()) {
137
continue;
138
}
139
const std::vector<MSLane*>* const allowed = parent.allowedLanes(*edge);
140
assert(allowed != nullptr);
141
assert(allowed->size() > 0);
142
for (MSLane* const l : *allowed) {
143
std::vector<MSLane*>::const_iterator it = std::find(lanes.begin(), lanes.end(), l);
144
myFollowerMap[edge] |= (1 << distance(lanes.begin(), it));
145
}
146
}
147
}
148
myQueueCapacity = length;
149
} else {
150
myQueues.push_back(Queue(parent.getPermissions()));
151
}
152
153
initSegment(edgeType, parent, length * usableLanes);
154
}
155
156
void
157
MESegment::initSegment(const MesoEdgeType& edgeType, const MSEdge& parent, const double capacity) {
158
159
myCapacity = capacity;
160
if (myQueues.size() == 1) {
161
const double laneScale = capacity / myLength;
162
myQueueCapacity = capacity;
163
myTau_length = TIME2STEPS(1) / MAX2(MESO_MIN_SPEED, myMeanSpeed) / laneScale;
164
// Eissfeldt p. 90 and 151 ff.
165
myTau_ff = (SUMOTime)((double)edgeType.tauff / laneScale);
166
myTau_fj = (SUMOTime)((double)edgeType.taufj / laneScale);
167
myTau_jf = (SUMOTime)((double)edgeType.taujf / laneScale);
168
myTau_jj = (SUMOTime)((double)edgeType.taujj / laneScale);
169
} else {
170
myTau_ff = edgeType.tauff;
171
myTau_fj = edgeType.taufj;
172
myTau_jf = edgeType.taujf;
173
myTau_jj = edgeType.taujj;
174
}
175
176
myJunctionControl = myNextSegment == nullptr && (edgeType.junctionControl || MELoop::isEnteringRoundabout(parent));
177
myTLSPenalty = ((edgeType.tlsPenalty > 0 || edgeType.tlsFlowPenalty > 0) &&
178
// only apply to the last segment of a tls-controlled edge
179
myNextSegment == nullptr && (
180
parent.getToJunction()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT ||
181
parent.getToJunction()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION ||
182
parent.getToJunction()->getType() == SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED));
183
184
// only apply to the last segment of an uncontrolled edge that has at least 1 minor link
185
myCheckMinorPenalty = (edgeType.minorPenalty > 0 &&
186
myNextSegment == nullptr &&
187
parent.getToJunction()->getType() != SumoXMLNodeType::TRAFFIC_LIGHT &&
188
parent.getToJunction()->getType() != SumoXMLNodeType::TRAFFIC_LIGHT_NOJUNCTION &&
189
parent.getToJunction()->getType() != SumoXMLNodeType::TRAFFIC_LIGHT_RIGHT_ON_RED &&
190
parent.hasMinorLink());
191
myMinorPenalty = edgeType.minorPenalty;
192
myOvertaking = edgeType.overtaking && myCapacity > myLength;
193
194
//std::cout << getID() << " myMinorPenalty=" << myMinorPenalty << " myTLSPenalty=" << myTLSPenalty << " myJunctionControl=" << myJunctionControl << " myOvertaking=" << myOvertaking << "\n";
195
196
recomputeJamThreshold(edgeType.jamThreshold);
197
}
198
199
MESegment::MESegment(const std::string& id):
200
Named(id),
201
myEdge(myDummyParent), // arbitrary edge needed to supply the needed reference
202
myNextSegment(nullptr), myLength(0), myIndex(0),
203
myTau_ff(0), myTau_fj(0), myTau_jf(0), myTau_jj(0),
204
myTLSPenalty(false),
205
myCheckMinorPenalty(false),
206
myMinorPenalty(0),
207
myJunctionControl(false),
208
myOvertaking(false),
209
myTau_length(1) {
210
}
211
212
213
void
214
MESegment::updatePermissions() {
215
if (myQueues.size() > 1) {
216
for (MSLane* lane : myEdge.getLanes()) {
217
myQueues[lane->getIndex()].setPermissions(lane->getPermissions());
218
}
219
} else {
220
myQueues.back().setPermissions(myEdge.getPermissions());
221
}
222
}
223
224
225
void
226
MESegment::recomputeJamThreshold(double jamThresh) {
227
if (jamThresh == DO_NOT_PATCH_JAM_THRESHOLD) {
228
return;
229
}
230
if (jamThresh < 0) {
231
// compute based on speed
232
myJamThreshold = jamThresholdForSpeed(myEdge.getSpeedLimit(), jamThresh);
233
} else {
234
// compute based on specified percentage
235
myJamThreshold = jamThresh * myCapacity;
236
}
237
}
238
239
240
double
241
MESegment::jamThresholdForSpeed(double speed, double jamThresh) const {
242
// vehicles driving freely at maximum speed should not jam
243
// we compute how many vehicles could possible enter the segment until the first vehicle leaves
244
// and multiply by the space these vehicles would occupy
245
// the jamThresh parameter is scale the resulting value
246
if (speed == 0) {
247
return std::numeric_limits<double>::max(); // never jam. Irrelevant at speed 0 anyway
248
}
249
#ifdef DEBUG_JAMTHRESHOLD
250
if (true || DEBUG_COND) {
251
std::cout << "jamThresholdForSpeed seg=" << getID() << " speed=" << speed << " jamThresh=" << jamThresh << " ffVehs=" << std::ceil(myLength / (-jamThresh * speed * STEPS2TIME(tauWithVehLength(myTau_ff, DEFAULT_VEH_LENGTH_WITH_GAP)))) << " thresh=" << std::ceil(myLength / (-jamThresh * speed * STEPS2TIME(tauWithVehLength(myTau_ff, DEFAULT_VEH_LENGTH_WITH_GAP)))) * DEFAULT_VEH_LENGTH_WITH_GAP
252
<< "\n";
253
}
254
#endif
255
return std::ceil(myLength / (-jamThresh * speed * STEPS2TIME(tauWithVehLength(myTau_ff, DEFAULT_VEH_LENGTH_WITH_GAP, 1.)))) * DEFAULT_VEH_LENGTH_WITH_GAP;
256
}
257
258
259
void
260
MESegment::addDetector(MSMoveReminder* data, int queueIndex) {
261
if (queueIndex == -1) {
262
for (Queue& q : myQueues) {
263
q.addDetector(data);
264
}
265
} else {
266
assert(queueIndex < (int)myQueues.size());
267
myQueues[queueIndex].addDetector(data);
268
}
269
}
270
271
272
/*
273
void
274
MESegment::removeDetector(MSMoveReminder* data) {
275
std::vector<MSMoveReminder*>::iterator it = std::find(myDetectorData.begin(), myDetectorData.end(), data);
276
if (it != myDetectorData.end()) {
277
myDetectorData.erase(it);
278
}
279
for (const Queue& q : myQueues) {
280
for (MEVehicle* const v : q.getVehicles()) {
281
v->removeReminder(data);
282
}
283
}
284
}
285
*/
286
287
288
void
289
MESegment::prepareDetectorForWriting(MSMoveReminder& data, int queueIndex) {
290
const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
291
if (queueIndex == -1) {
292
for (const Queue& q : myQueues) {
293
SUMOTime earliestExitTime = currentTime;
294
for (std::vector<MEVehicle*>::const_reverse_iterator i = q.getVehicles().rbegin(); i != q.getVehicles().rend(); ++i) {
295
const SUMOTime exitTime = MAX2(earliestExitTime, (*i)->getEventTime());
296
(*i)->updateDetectorForWriting(&data, currentTime, exitTime);
297
earliestExitTime = exitTime + tauWithVehLength(myTau_ff, (*i)->getVehicleType().getLengthWithGap(), (*i)->getVehicleType().getCarFollowModel().getHeadwayTime());
298
}
299
}
300
} else {
301
SUMOTime earliestExitTime = currentTime;
302
for (std::vector<MEVehicle*>::const_reverse_iterator i = myQueues[queueIndex].getVehicles().rbegin(); i != myQueues[queueIndex].getVehicles().rend(); ++i) {
303
const SUMOTime exitTime = MAX2(earliestExitTime, (*i)->getEventTime());
304
(*i)->updateDetectorForWriting(&data, currentTime, exitTime);
305
earliestExitTime = exitTime + tauWithVehLength(myTau_ff, (*i)->getVehicleType().getLengthWithGap(), (*i)->getVehicleType().getCarFollowModel().getHeadwayTime());
306
}
307
}
308
}
309
310
311
SUMOTime
312
MESegment::hasSpaceFor(const MEVehicle* const veh, const SUMOTime entryTime, int& qIdx, const bool init) const {
313
SUMOTime earliestEntry = SUMOTime_MAX;
314
qIdx = 0;
315
if (myNumVehicles == 0 && myQueues.size() == 1) {
316
// we have always space for at least one vehicle
317
if (myQueues.front().allows(veh->getVClass())) {
318
return entryTime;
319
} else {
320
return earliestEntry;
321
}
322
}
323
const SUMOVehicleClass svc = veh->getVClass();
324
int minSize = std::numeric_limits<int>::max();
325
const MSEdge* const succ = myNextSegment == nullptr ? veh->succEdge(veh->getEdge() == &myEdge ? 1 : 2) : nullptr;
326
for (int i = 0; i < (int)myQueues.size(); i++) {
327
const Queue& q = myQueues[i];
328
const double newOccupancy = q.size() == 0 ? 0. : q.getOccupancy() + veh->getVehicleType().getLengthWithGap();
329
if (newOccupancy <= myQueueCapacity) { // we must ensure that occupancy remains below capacity
330
if (succ == nullptr || myFollowerMap.count(succ) == 0 || ((myFollowerMap.find(succ)->second & (1 << i)) != 0)) {
331
if (q.allows(svc) && q.size() < minSize) {
332
if (init) {
333
// regular insertions and initial insertions must respect different constraints:
334
// - regular insertions must respect entryBlockTime
335
// - initial insertions should not cause additional jamming
336
// - inserted vehicle should be able to continue at the current speed
337
if (veh->getInsertionChecks() == (int)InsertionCheck::NONE) {
338
qIdx = i;
339
minSize = q.size();
340
} else if (q.getOccupancy() <= myJamThreshold && !hasBlockedLeader() && !myTLSPenalty) {
341
if (newOccupancy <= myJamThreshold) {
342
qIdx = i;
343
minSize = q.size();
344
}
345
} else {
346
if (newOccupancy <= jamThresholdForSpeed(getMeanSpeed(false), -1)) {
347
qIdx = i;
348
minSize = q.size();
349
}
350
}
351
} else if (entryTime >= q.getEntryBlockTime()) {
352
qIdx = i;
353
minSize = q.size();
354
} else {
355
earliestEntry = MIN2(earliestEntry, q.getEntryBlockTime());
356
}
357
}
358
}
359
}
360
}
361
if (minSize == std::numeric_limits<int>::max()) {
362
return earliestEntry;
363
}
364
return entryTime;
365
}
366
367
368
bool
369
MESegment::initialise(MEVehicle* veh, SUMOTime time) {
370
int qIdx = 0;
371
if (hasSpaceFor(veh, time, qIdx, true) == time) {
372
receive(veh, qIdx, time, true);
373
// we can check only after insertion because insertion may change the route via devices
374
std::string msg;
375
if (MSGlobals::gCheckRoutes && !veh->hasValidRoute(msg)) {
376
throw ProcessError(TLF("Vehicle '%' has no valid route. %", veh->getID(), msg));
377
}
378
return true;
379
}
380
return false;
381
}
382
383
384
double
385
MESegment::getMeanSpeed(bool useCached) const {
386
const SUMOTime currentTime = MSNet::getInstance()->getCurrentTimeStep();
387
if (currentTime != myLastMeanSpeedUpdate || !useCached) {
388
myLastMeanSpeedUpdate = currentTime;
389
double v = 0;
390
int count = 0;
391
for (const Queue& q : myQueues) {
392
const SUMOTime tau = q.getOccupancy() < myJamThreshold ? myTau_ff : myTau_jf;
393
SUMOTime earliestExitTime = currentTime;
394
count += q.size();
395
for (std::vector<MEVehicle*>::const_reverse_iterator veh = q.getVehicles().rbegin(); veh != q.getVehicles().rend(); ++veh) {
396
v += (*veh)->getConservativeSpeed(earliestExitTime); // earliestExitTime is updated!
397
earliestExitTime += tauWithVehLength(tau, (*veh)->getVehicleType().getLengthWithGap(), (*veh)->getVehicleType().getCarFollowModel().getHeadwayTime());
398
}
399
}
400
if (count == 0) {
401
myMeanSpeed = myEdge.getSpeedLimit();
402
} else {
403
myMeanSpeed = v / (double) count;
404
}
405
}
406
return myMeanSpeed;
407
}
408
409
410
void
411
MESegment::resetCachedSpeeds() {
412
myLastMeanSpeedUpdate = SUMOTime_MIN;
413
}
414
415
void
416
MESegment::writeVehicles(OutputDevice& of) const {
417
for (const Queue& q : myQueues) {
418
for (const MEVehicle* const veh : q.getVehicles()) {
419
MSXMLRawOut::writeVehicle(of, *veh);
420
}
421
}
422
}
423
424
425
MEVehicle*
426
MESegment::removeCar(MEVehicle* v, SUMOTime leaveTime, const MSMoveReminder::Notification reason) {
427
Queue& q = myQueues[v->getQueIndex()];
428
// One could be tempted to do v->setSegment(next); here but position on lane will be invalid if next == 0
429
v->updateDetectors(leaveTime, v->getEventTime(), true, reason);
430
myNumVehicles--;
431
myEdge.lock();
432
MEVehicle* nextLeader = q.remove(v);
433
myEdge.unlock();
434
return nextLeader;
435
}
436
437
438
SUMOTime
439
MESegment::getNextInsertionTime(SUMOTime earliestEntry) const {
440
// since we do not know which queue will be used we give a conservative estimate
441
SUMOTime earliestLeave = earliestEntry;
442
SUMOTime latestEntry = -1;
443
for (const Queue& q : myQueues) {
444
earliestLeave = MAX2(earliestLeave, q.getBlockTime());
445
latestEntry = MAX2(latestEntry, q.getEntryBlockTime());
446
}
447
if (myEdge.getSpeedLimit() == 0) {
448
return MAX2(earliestEntry, latestEntry); // FIXME: This line is just an adhoc-fix to avoid division by zero (Leo)
449
} else {
450
return MAX3(earliestEntry, earliestLeave - TIME2STEPS(myLength / myEdge.getSpeedLimit()), latestEntry);
451
}
452
}
453
454
455
MSLink*
456
MESegment::getLink(const MEVehicle* veh, bool penalty) const {
457
if (myJunctionControl || penalty) {
458
const MSEdge* const nextEdge = veh->succEdge(1);
459
if (nextEdge == nullptr || veh->getQueIndex() == PARKING_QUEUE) {
460
return nullptr;
461
}
462
// try to find any link leading to our next edge, start with the lane pointed to by the que index
463
const MSLane* const bestLane = myEdge.getLanes()[veh->getQueIndex()];
464
for (MSLink* const link : bestLane->getLinkCont()) {
465
if (&link->getLane()->getEdge() == nextEdge) {
466
return link;
467
}
468
}
469
// this is for the non-multique case, maybe we should use caching here !!!
470
for (const MSLane* const lane : myEdge.getLanes()) {
471
if (lane != bestLane) {
472
for (MSLink* const link : lane->getLinkCont()) {
473
if (&link->getLane()->getEdge() == nextEdge) {
474
return link;
475
}
476
}
477
}
478
}
479
}
480
return nullptr;
481
}
482
483
484
bool
485
MESegment::isOpen(const MEVehicle* veh) const {
486
#ifdef DEBUG_OPENED
487
if (DEBUG_COND || DEBUG_COND2(veh)) {
488
gDebugFlag1 = true;
489
std::cout << SIMTIME << " opened seg=" << getID() << " veh=" << Named::getIDSecure(veh)
490
<< " tlsPenalty=" << myTLSPenalty;
491
const MSLink* link = getLink(veh);
492
if (link == 0) {
493
std::cout << " link=0";
494
} else {
495
std::cout << " prio=" << link->havePriority()
496
<< " override=" << limitedControlOverride(link)
497
<< " isOpen=" << link->opened(veh->getEventTime(), veh->getSpeed(), veh->estimateLeaveSpeed(link),
498
veh->getVehicleType().getLengthWithGap(), veh->getImpatience(),
499
veh->getVehicleType().getCarFollowModel().getMaxDecel(), veh->getWaitingTime(),
500
0, nullptr, false, veh)
501
<< " et=" << veh->getEventTime()
502
<< " v=" << veh->getSpeed()
503
<< " vLeave=" << veh->estimateLeaveSpeed(link)
504
<< " impatience=" << veh->getImpatience()
505
<< " tWait=" << veh->getWaitingTime();
506
}
507
std::cout << "\n";
508
gDebugFlag1 = false;
509
}
510
#endif
511
if (myTLSPenalty) {
512
// XXX should limited control take precedence over tls penalty?
513
return true;
514
}
515
const MSLink* link = getLink(veh);
516
return (link == nullptr
517
|| link->havePriority()
518
|| limitedControlOverride(link)
519
|| link->opened(veh->getEventTime(), veh->getSpeed(), veh->estimateLeaveSpeed(link),
520
veh->getVehicleType().getLengthWithGap(), veh->getImpatience(),
521
veh->getVehicleType().getCarFollowModel().getMaxDecel(), veh->getWaitingTime(),
522
0, nullptr, false, veh));
523
}
524
525
526
bool
527
MESegment::limitedControlOverride(const MSLink* link) const {
528
assert(link != nullptr);
529
if (!MSGlobals::gMesoLimitedJunctionControl) {
530
return false;
531
}
532
// if the target segment of this link is not saturated junction control is disabled
533
const MSEdge& targetEdge = link->getLane()->getEdge();
534
const MESegment* target = MSGlobals::gMesoNet->getSegmentForEdge(targetEdge);
535
return (target->getBruttoOccupancy() * 2 < target->myJamThreshold) && !targetEdge.isRoundabout();
536
}
537
538
539
void
540
MESegment::send(MEVehicle* veh, MESegment* const next, const int nextQIdx, SUMOTime time, const MSMoveReminder::Notification reason) {
541
Queue& q = myQueues[veh->getQueIndex()];
542
assert(isInvalid(next) || time >= q.getBlockTime());
543
MSLink* const link = getLink(veh);
544
if (link != nullptr) {
545
link->removeApproaching(veh);
546
}
547
if (veh->isStopped()) {
548
veh->processStop();
549
}
550
MEVehicle* lc = removeCar(veh, time, reason); // new leaderCar
551
q.setBlockTime(time);
552
if (!isInvalid(next)) {
553
const bool nextFree = next->myQueues[nextQIdx].getOccupancy() <= next->myJamThreshold;
554
const SUMOTime tau = (q.getOccupancy() <= myJamThreshold
555
? (nextFree ? myTau_ff : myTau_fj)
556
: (nextFree ? myTau_jf : getTauJJ((double)next->myQueues[nextQIdx].size(), next->myQueueCapacity, next->myJamThreshold)));
557
assert(tau >= 0);
558
myLastHeadway = tauWithVehLength(tau, veh->getVehicleType().getLengthWithGap(), veh->getVehicleType().getCarFollowModel().getHeadwayTime());
559
if (myTLSPenalty) {
560
const MSLink* const tllink = getLink(veh, true);
561
if (tllink != nullptr && tllink->isTLSControlled()) {
562
assert(tllink->getGreenFraction() > 0);
563
myLastHeadway = (SUMOTime)((double)myLastHeadway / tllink->getGreenFraction());
564
}
565
}
566
q.setBlockTime(q.getBlockTime() + myLastHeadway);
567
}
568
if (lc != nullptr) {
569
lc->setEventTime(MAX2(lc->getEventTime(), q.getBlockTime()));
570
MSGlobals::gMesoNet->addLeaderCar(lc, getLink(lc));
571
}
572
}
573
574
SUMOTime
575
MESegment::getTauJJ(double nextQueueSize, double nextQueueCapacity, double nextJamThreshold) const {
576
// compute coefficients for the jam-jam headway function
577
// this function models the effect that "empty space" needs to move
578
// backwards through the downstream segment before the upstream segment may
579
// send annother vehicle.
580
// this allows jams to clear and move upstream.
581
// the headway function f(x) depends on the number of vehicles in the
582
// downstream segment x
583
// f is a linear function that passes through the following fixed points:
584
// f(n_jam_threshold) = tau_jf_withLength (for continuity)
585
// f(headwayCapacity) = myTau_jj * headwayCapacity
586
587
const SUMOTime tau_jf_withLength = tauWithVehLength(myTau_jf, DEFAULT_VEH_LENGTH_WITH_GAP, 1.);
588
// number of vehicles that fit into the NEXT queue (could be larger than expected with DEFAULT_VEH_LENGTH_WITH_GAP!)
589
const double headwayCapacity = MAX2(nextQueueSize, nextQueueCapacity / DEFAULT_VEH_LENGTH_WITH_GAP);
590
// number of vehicles above which the NEXT queue is jammed
591
const double n_jam_threshold = headwayCapacity * nextJamThreshold / nextQueueCapacity;
592
593
// slope a and axis offset b for the jam-jam headway function
594
// solving f(x) = a * x + b
595
const double a = (STEPS2TIME(myTau_jj) * headwayCapacity - STEPS2TIME(tau_jf_withLength)) / (headwayCapacity - n_jam_threshold);
596
const double b = headwayCapacity * (STEPS2TIME(myTau_jj) - a);
597
598
// it is only well defined for nextQueueSize >= n_jam_threshold (which may not be the case for longer vehicles), so we take the MAX
599
return TIME2STEPS(a * MAX2(nextQueueSize, n_jam_threshold) + b);
600
}
601
602
603
bool
604
MESegment::overtake() {
605
return myOvertaking && RandHelper::rand() > (getBruttoOccupancy() / myCapacity);
606
}
607
608
609
void
610
MESegment::addReminders(MEVehicle* veh) const {
611
if (veh->getQueIndex() != PARKING_QUEUE) {
612
myQueues[veh->getQueIndex()].addReminders(veh);
613
}
614
}
615
616
617
void
618
MESegment::receive(MEVehicle* veh, const int qIdx, SUMOTime time, const bool isDepart, const bool isTeleport, const bool newEdge) {
619
const double speed = isDepart ? -1 : MAX2(veh->getSpeed(), MESO_MIN_SPEED); // on the previous segment
620
veh->setSegment(this); // for arrival checking
621
veh->setLastEntryTime(time);
622
veh->setBlockTime(SUMOTime_MAX);
623
if (!isDepart && (
624
// arrival on entering a new edge
625
(newEdge && veh->moveRoutePointer())
626
// arrival on entering a new segment
627
|| veh->hasArrived())) {
628
// route has ended
629
veh->setEventTime(time + TIME2STEPS(myLength / speed)); // for correct arrival speed
630
addReminders(veh);
631
veh->activateReminders(MSMoveReminder::NOTIFICATION_JUNCTION);
632
veh->updateDetectors(time, veh->getEventTime(), true,
633
veh->getEdge()->isVaporizing() ? MSMoveReminder::NOTIFICATION_VAPORIZED_VAPORIZER : MSMoveReminder::NOTIFICATION_ARRIVED);
634
MSNet::getInstance()->getVehicleControl().scheduleVehicleRemoval(veh);
635
return;
636
}
637
assert(veh->getEdge() == &getEdge());
638
// route continues
639
Queue& q = myQueues[qIdx];
640
const double maxSpeedOnEdge = veh->getEdge()->getLanes()[qIdx]->getVehicleMaxSpeed(veh);
641
const double uspeed = MAX2(maxSpeedOnEdge, MESO_MIN_SPEED);
642
std::vector<MEVehicle*>& cars = q.getModifiableVehicles();
643
MEVehicle* newLeader = nullptr; // first vehicle in the current queue
644
const SUMOTime stopTime = veh->checkStop(time);
645
SUMOTime tleave = MAX2(stopTime + TIME2STEPS(myLength / uspeed) + getLinkPenalty(veh), q.getBlockTime());
646
if (veh->isStopped()) {
647
myEdge.addWaiting(veh);
648
}
649
if (veh->isParking()) {
650
// parking stops should take at least 1ms
651
veh->setEventTime(MAX2(stopTime, veh->getEventTime() + 1));
652
veh->setSegment(this, PARKING_QUEUE);
653
myEdge.getLanes()[0]->addParking(veh); // TODO for GUI only
654
} else {
655
myEdge.lock();
656
if (cars.empty()) {
657
cars.push_back(veh);
658
newLeader = veh;
659
} else {
660
SUMOTime leaderOut = cars[0]->getEventTime();
661
if (!isDepart && leaderOut > tleave && overtake()) {
662
if (cars.size() == 1) {
663
MSGlobals::gMesoNet->removeLeaderCar(cars[0]);
664
newLeader = veh;
665
}
666
cars.insert(cars.begin() + 1, veh);
667
} else {
668
tleave = MAX2(leaderOut + tauWithVehLength(myTau_ff, cars[0]->getVehicleType().getLengthWithGap(), cars[0]->getVehicleType().getCarFollowModel().getHeadwayTime()), tleave);
669
cars.insert(cars.begin(), veh);
670
}
671
}
672
myEdge.unlock();
673
myNumVehicles++;
674
if (!isDepart && !isTeleport) {
675
// departs and teleports could take place anywhere on the edge so they should not block regular flow
676
// the -1 facilitates interleaving of multiple streams
677
q.setEntryBlockTime(time + tauWithVehLength(myTau_ff, veh->getVehicleType().getLengthWithGap(), veh->getVehicleType().getCarFollowModel().getHeadwayTime()) - 1);
678
}
679
q.setOccupancy(MIN2(myQueueCapacity, q.getOccupancy() + veh->getVehicleType().getLengthWithGap()));
680
veh->setEventTime(tleave);
681
veh->setSegment(this, qIdx);
682
}
683
addReminders(veh);
684
if (isDepart) {
685
veh->onDepart();
686
veh->activateReminders(MSMoveReminder::NOTIFICATION_DEPARTED);
687
} else if (newEdge) {
688
veh->activateReminders(MSMoveReminder::NOTIFICATION_JUNCTION);
689
} else {
690
veh->activateReminders(MSMoveReminder::NOTIFICATION_SEGMENT);
691
}
692
if (veh->isParking()) {
693
MSGlobals::gMesoNet->addLeaderCar(veh, nullptr);
694
} else {
695
if (newLeader != nullptr) {
696
MSGlobals::gMesoNet->addLeaderCar(newLeader, getLink(newLeader));
697
}
698
}
699
}
700
701
702
bool
703
MESegment::vaporizeAnyCar(SUMOTime currentTime, const MSDetectorFileOutput* filter) {
704
for (const Queue& q : myQueues) {
705
if (q.size() > 0) {
706
for (MEVehicle* const veh : q.getVehicles()) {
707
if (filter->vehicleApplies(*veh)) {
708
MSGlobals::gMesoNet->removeLeaderCar(veh);
709
MSGlobals::gMesoNet->changeSegment(veh, currentTime + 1, &myVaporizationTarget, MSMoveReminder::NOTIFICATION_VAPORIZED_CALIBRATOR);
710
return true;
711
}
712
}
713
}
714
}
715
return false;
716
}
717
718
719
void
720
MESegment::setSpeedForQueue(double newSpeed, SUMOTime currentTime, SUMOTime blockTime, const std::vector<MEVehicle*>& vehs) {
721
MEVehicle* v = vehs.back();
722
SUMOTime oldEarliestExitTime = currentTime;
723
const SUMOTime oldExit = MAX2(oldEarliestExitTime, v->getEventTime());
724
v->updateDetectors(currentTime, oldExit, false);
725
oldEarliestExitTime = oldExit + tauWithVehLength(myTau_ff, v->getVehicleType().getLengthWithGap(), v->getVehicleType().getCarFollowModel().getHeadwayTime());
726
SUMOTime newEvent = MAX2(newArrival(v, newSpeed, currentTime), blockTime);
727
if (v->getEventTime() != newEvent) {
728
MSGlobals::gMesoNet->removeLeaderCar(v);
729
v->setEventTime(newEvent);
730
MSGlobals::gMesoNet->addLeaderCar(v, getLink(v));
731
}
732
for (std::vector<MEVehicle*>::const_reverse_iterator i = vehs.rbegin() + 1; i != vehs.rend(); ++i) {
733
const SUMOTime oldExitTime = MAX2(oldEarliestExitTime, (*i)->getEventTime());
734
(*i)->updateDetectors(currentTime, oldExitTime, false);
735
const SUMOTime minTau = tauWithVehLength(myTau_ff, (*i)->getVehicleType().getLengthWithGap(), (*i)->getVehicleType().getCarFollowModel().getHeadwayTime());
736
oldEarliestExitTime = oldExitTime + minTau;
737
newEvent = MAX2(newArrival(*i, newSpeed, currentTime), newEvent + minTau);
738
(*i)->setEventTime(newEvent);
739
}
740
}
741
742
743
SUMOTime
744
MESegment::newArrival(const MEVehicle* const v, double newSpeed, SUMOTime currentTime) {
745
// since speed is only an upper bound, pos may be too optimistic
746
const double pos = MIN2(myLength, STEPS2TIME(currentTime - v->getLastEntryTime()) * v->getSpeed());
747
// traveltime may not be 0
748
double tt = (myLength - pos) / MAX2(newSpeed, MESO_MIN_SPEED);
749
return currentTime + MAX2(TIME2STEPS(tt), SUMOTime(1));
750
}
751
752
753
void
754
MESegment::setSpeed(double newSpeed, SUMOTime currentTime, double jamThresh, int qIdx) {
755
recomputeJamThreshold(jamThresh);
756
//myTau_length = MAX2(MESO_MIN_SPEED, newSpeed) * myEdge.getLanes().size() / TIME2STEPS(1);
757
int i = 0;
758
for (const Queue& q : myQueues) {
759
if (q.size() != 0) {
760
if (qIdx == -1 || qIdx == i) {
761
setSpeedForQueue(newSpeed, currentTime, q.getBlockTime(), q.getVehicles());
762
}
763
}
764
i++;
765
}
766
}
767
768
769
SUMOTime
770
MESegment::getEventTime() const {
771
SUMOTime result = SUMOTime_MAX;
772
for (const Queue& q : myQueues) {
773
if (q.size() != 0 && q.getVehicles().back()->getEventTime() < result) {
774
result = q.getVehicles().back()->getEventTime();
775
}
776
}
777
if (result < SUMOTime_MAX) {
778
return result;
779
}
780
return -1;
781
}
782
783
784
void
785
MESegment::saveState(OutputDevice& out) const {
786
bool write = false;
787
for (const Queue& q : myQueues) {
788
if (q.getBlockTime() != -1 || !q.getVehicles().empty()) {
789
write = true;
790
break;
791
}
792
}
793
if (write) {
794
out.openTag(SUMO_TAG_SEGMENT).writeAttr(SUMO_ATTR_ID, getID());
795
for (const Queue& q : myQueues) {
796
out.openTag(SUMO_TAG_VIEWSETTINGS_VEHICLES);
797
out.writeAttr(SUMO_ATTR_TIME, toString<SUMOTime>(q.getBlockTime()));
798
out.writeAttr(SUMO_ATTR_BLOCKTIME, toString<SUMOTime>(q.getEntryBlockTime()));
799
out.writeAttr(SUMO_ATTR_VALUE, q.getVehicles());
800
out.closeTag();
801
}
802
out.closeTag();
803
}
804
}
805
806
807
void
808
MESegment::clearState() {
809
for (Queue& q : myQueues) {
810
q.getModifiableVehicles().clear();
811
}
812
}
813
814
void
815
MESegment::loadState(const std::vector<SUMOVehicle*>& vehs, const SUMOTime blockTime, const SUMOTime entryBlockTime, const int queIdx) {
816
Queue& q = myQueues[queIdx];
817
for (SUMOVehicle* veh : vehs) {
818
MEVehicle* v = static_cast<MEVehicle*>(veh);
819
assert(v->getSegment() == this);
820
q.getModifiableVehicles().push_back(v);
821
myNumVehicles++;
822
q.setOccupancy(q.getOccupancy() + v->getVehicleType().getLengthWithGap());
823
addReminders(v);
824
}
825
if (q.size() != 0) {
826
// add the last vehicle of this queue
827
// !!! one question - what about the previously added vehicle? Is it stored twice?
828
MEVehicle* veh = q.getVehicles().back();
829
MSGlobals::gMesoNet->addLeaderCar(veh, getLink(veh));
830
}
831
q.setBlockTime(blockTime);
832
q.setEntryBlockTime(entryBlockTime);
833
q.setOccupancy(MIN2(q.getOccupancy(), myQueueCapacity));
834
}
835
836
837
std::vector<const MEVehicle*>
838
MESegment::getVehicles() const {
839
std::vector<const MEVehicle*> result;
840
for (const Queue& q : myQueues) {
841
result.insert(result.end(), q.getVehicles().begin(), q.getVehicles().end());
842
}
843
return result;
844
}
845
846
847
bool
848
MESegment::hasBlockedLeader() const {
849
for (const Queue& q : myQueues) {
850
if (q.size() > 0 && q.getVehicles().back()->getWaitingTime() > 0) {
851
return true;
852
}
853
}
854
return false;
855
}
856
857
858
double
859
MESegment::getFlow() const {
860
return 3600 * getCarNumber() * getMeanSpeed() / myLength;
861
}
862
863
864
SUMOTime
865
MESegment::getLinkPenalty(const MEVehicle* veh) const {
866
const MSLink* link = getLink(veh, myTLSPenalty || myCheckMinorPenalty);
867
if (link != nullptr) {
868
SUMOTime result = 0;
869
if (link->isTLSControlled() && myTLSPenalty) {
870
result += link->getMesoTLSPenalty();
871
}
872
// minor tls links may get an additional penalty
873
if (!link->havePriority() &&
874
// do not apply penalty on top of tLSPenalty
875
!myTLSPenalty &&
876
// do not apply penalty if limited control is active
877
(!MSGlobals::gMesoLimitedJunctionControl || limitedControlOverride(link))) {
878
result += myMinorPenalty;
879
}
880
return result;
881
} else {
882
return 0;
883
}
884
}
885
886
887
double
888
MESegment::getWaitingSeconds() const {
889
double result = 0;
890
for (const Queue& q : myQueues) {
891
// @note: only the leader currently accumulates waitingTime but this might change in the future
892
for (const MEVehicle* veh : q.getVehicles()) {
893
result += veh->getWaitingSeconds();
894
}
895
}
896
return result;
897
}
898
899
900
/****************************************************************************/
901
902