Eclipse SUMO - Simulation of Urban MObility
MELoop.cpp
Go to the documentation of this file.
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 /****************************************************************************/
18 // The main mesocopic simulation loop
19 /****************************************************************************/
20 #include <config.h>
21 
22 #include <queue>
23 #include <vector>
24 #include <map>
25 #include <cmath>
26 
27 #include <microsim/MSNet.h>
28 #include <microsim/MSEdge.h>
29 #include <microsim/MSGlobals.h>
30 #include <microsim/MSLane.h>
31 #include <microsim/MSVehicle.h>
34 #include <utils/common/ToString.h>
36 #include <utils/common/SUMOTime.h>
38 #include "MELoop.h"
39 #include "MESegment.h"
40 #include "MEVehicle.h"
41 
42 
43 // ===========================================================================
44 // method definitions
45 // ===========================================================================
46 MELoop::MELoop(const SUMOTime recheckInterval) : myFullRecheckInterval(recheckInterval), myLinkRecheckInterval(TIME2STEPS(1)) {
47 }
48 
50  for (std::vector<MESegment*>::const_iterator j = myEdges2FirstSegments.begin(); j != myEdges2FirstSegments.end(); ++j) {
51  for (MESegment* s = *j; s != nullptr;) {
52  MESegment* n = s->getNextSegment();
53  delete s;
54  s = n;
55  }
56  }
57 }
58 
59 
60 void
62  while (!myLeaderCars.empty()) {
63  const SUMOTime time = myLeaderCars.begin()->first;
64  std::vector<MEVehicle*> vehs = myLeaderCars[time];
65  assert(time > tMax - DELTA_T || vehs.size() == 0);
66  if (time > tMax) {
67  return;
68  }
69  myLeaderCars.erase(time);
70  for (std::vector<MEVehicle*>::const_iterator i = vehs.begin(); i != vehs.end(); ++i) {
71  checkCar(*i);
72  assert(myLeaderCars.empty() || myLeaderCars.begin()->first >= time);
73  }
74  }
75 }
76 
77 
79 MELoop::changeSegment(MEVehicle* veh, SUMOTime leaveTime, MESegment* const toSegment, MSMoveReminder::Notification reason, const bool ignoreLink) const {
80  int qIdx = 0;
81  MESegment* const onSegment = veh->getSegment();
82  if (MESegment::isInvalid(toSegment)) {
83  if (veh->isStoppedTriggered()) {
84  return leaveTime + MAX2(SUMOTime(1), myLinkRecheckInterval);
85  }
86  if (onSegment != nullptr) {
87  onSegment->send(veh, toSegment, qIdx, leaveTime, reason);
88  } else {
89  WRITE_WARNINGF(TL("Vehicle '%' teleports beyond arrival edge '%', time=%."),
90  veh->getID(), veh->getEdge()->getID(), time2string(leaveTime));
91  }
92  veh->setSegment(toSegment); // signal arrival
94  return leaveTime;
95  }
96  const SUMOTime entry = toSegment->hasSpaceFor(veh, leaveTime, qIdx);
97  if (entry == leaveTime && (ignoreLink || veh->mayProceed())) {
98  if (onSegment != nullptr) {
99  if (veh->getQueIndex() == MESegment::PARKING_QUEUE) { // parking or just aborted parking
100  if (veh->isParking()) {
101  veh->processStop();
102  }
103  veh->getEdge()->getLanes()[0]->removeParking(veh); // TODO for GUI only
104  } else {
105  onSegment->send(veh, toSegment, qIdx, leaveTime, onSegment->getNextSegment() == nullptr ? MSMoveReminder::NOTIFICATION_JUNCTION : MSMoveReminder::NOTIFICATION_SEGMENT);
106  }
107  toSegment->receive(veh, qIdx, leaveTime, false, ignoreLink, &onSegment->getEdge() != &toSegment->getEdge());
108  } else {
109  WRITE_WARNINGF(TL("Vehicle '%' ends teleporting on edge '%':%, time=%."),
110  veh->getID(), toSegment->getEdge().getID(), toSegment->getIndex(), time2string(leaveTime));
111  // this is not quite correct but suffices for interrogation by
112  // subsequent methods (veh->getSpeed() needs segment != 0)
114  // clean up detectors (do not add traffic data)
115  // note: updateDatector is not called if leaveTime == getLastEntryTime()
117  toSegment->receive(veh, qIdx, leaveTime, false, true, true);
118  }
119  return entry;
120  }
121  if (entry == leaveTime && !ignoreLink) { // this is a long way of saying !veh->mayProceed() (which is a costly call)
122  return entry + MAX2(SUMOTime(1), myLinkRecheckInterval);
123  }
124  return entry;
125 }
126 
127 
128 void
130  const SUMOTime leaveTime = veh->getEventTime();
131  MESegment* const onSegment = veh->getSegment();
132  MESegment* const toSegment = veh->getQueIndex() == MESegment::PARKING_QUEUE ? onSegment : nextSegment(onSegment, veh);
133  const bool teleporting = (onSegment == nullptr); // is the vehicle currently teleporting?
134  // @note reason is only evaluated if toSegment == nullptr
135  const SUMOTime nextEntry = changeSegment(veh, leaveTime, toSegment, MSMoveReminder::NOTIFICATION_ARRIVED, teleporting);
136  if (nextEntry == leaveTime) {
137  return;
138  }
140  teleportVehicle(veh, toSegment);
141  return;
142  }
143  if (veh->getBlockTime() == SUMOTime_MAX && !veh->isStopped()) {
144  veh->setBlockTime(leaveTime);
145  }
146  if (nextEntry == SUMOTime_MAX) {
147  // all usable queues on the next segment are full
148  SUMOTime newEventTime = MAX3(toSegment->getEventTime() + 1, leaveTime + 1, leaveTime + myFullRecheckInterval);
149  if (MSGlobals::gTimeToGridlock > 0) {
150  // if teleporting is enabled, make sure we look at the vehicle when the gridlock-time is up
151  newEventTime = MAX2(MIN2(newEventTime, veh->getBlockTime() + MSGlobals::gTimeToGridlock + 1), leaveTime + DELTA_T);
152  }
153  veh->setEventTime(newEventTime);
154  } else {
155  // receiving segment has recently received another vehicle or the junction is blocked
156  veh->setEventTime(nextEntry);
157  }
158  addLeaderCar(veh, onSegment->getLink(veh));
159 }
160 
161 
162 void
163 MELoop::teleportVehicle(MEVehicle* veh, MESegment* const toSegment) {
164  const SUMOTime leaveTime = veh->getEventTime();
165  MESegment* const onSegment = veh->getSegment();
167  WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
168  veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(),
169  time2string(leaveTime));
171  int qIdx = 0;
172  onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED);
173  veh->setSegment(nullptr);
175  return;
176  }
177  const bool teleporting = (onSegment == nullptr); // is the vehicle already teleporting?
178  // try to find a place on the current edge
179  MESegment* teleSegment = toSegment->getNextSegment();
180  while (teleSegment != nullptr && changeSegment(veh, leaveTime, teleSegment, MSMoveReminder::NOTIFICATION_TELEPORT, true) != leaveTime) {
181  // @caution the time to get to the next segment here is ignored XXX
182  teleSegment = teleSegment->getNextSegment();
183  }
184  if (teleSegment != nullptr) {
185  if (!teleporting) {
186  // we managed to teleport in a single jump
187  WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':% to edge '%':%, time=%."),
188  veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(),
189  teleSegment->getEdge().getID(), teleSegment->getIndex(), time2string(leaveTime));
191  }
192  } else {
193  // teleport across the current edge and try insertion later
194  if (!teleporting) {
195  int qIdx = 0;
196  // announce start of multi-step teleport, arrival will be announced in changeSegment()
197  WRITE_WARNINGF(TL("Teleporting vehicle '%'; waited too long, from edge '%':%, time=%."),
198  veh->getID(), onSegment->getEdge().getID(), onSegment->getIndex(), time2string(leaveTime));
200  // remove from current segment
201  onSegment->send(veh, nullptr, qIdx, leaveTime, MSMoveReminder::NOTIFICATION_TELEPORT);
202  // mark veh as teleporting
203  veh->setSegment(nullptr);
204  }
205  // @caution microsim uses current travel time teleport duration
206  const SUMOTime teleArrival = leaveTime + TIME2STEPS(veh->getEdge()->getLength() / MAX2(veh->getEdge()->getSpeedLimit(), NUMERICAL_EPS));
207  const bool atDest = veh->moveRoutePointer();
208  if (atDest) {
209  // teleporting to end of route
210  changeSegment(veh, teleArrival, nullptr, MSMoveReminder::NOTIFICATION_TELEPORT_ARRIVED, true);
211  } else {
212  veh->setEventTime(teleArrival);
213  addLeaderCar(veh, nullptr);
214  // teleporting vehicles must react to rerouters
215  getSegmentForEdge(*veh->getEdge())->addReminders(veh);
217  }
218  }
219 }
220 
221 
222 void
224  myLeaderCars[veh->getEventTime()].push_back(veh);
225  veh->setApproaching(link);
226 }
227 
228 
229 void
231  myLeaderCars.clear();
232 }
233 
234 
235 bool
237  const auto candIt = myLeaderCars.find(v->getEventTime());
238  if (candIt != myLeaderCars.end()) {
239  std::vector<MEVehicle*>& cands = candIt->second;
240  auto it = find(cands.begin(), cands.end(), v);
241  if (it != cands.end()) {
242  cands.erase(it);
243  return true;
244  }
245  }
246  return false;
247 }
248 
249 
250 void
252  int qIdx = 0;
253  v->getSegment()->send(v, nullptr, qIdx, MSNet::getInstance()->getCurrentTimeStep(), reason);
254  removeLeaderCar(v);
255 }
256 
257 
258 MESegment*
260  if (s != nullptr) { // vehicle is not teleporting
261  MESegment* next = s->getNextSegment();
262  if (next != nullptr) {
263  // ok, the street continues
264  return next;
265  }
266  }
267  // we have to check the next edge in the vehicle's route
268  const MSEdge* nextEdge = v->succEdge(1);
269  if (nextEdge == nullptr) {
270  // end of route
271  return nullptr;
272  }
273  return myEdges2FirstSegments[nextEdge->getNumericalID()];
274 }
275 
276 
277 int
278 MELoop::numSegmentsFor(const double length, const double sLength) {
279  int no = (int)floor(length / sLength + 0.5);
280  if (no == 0) { // assure there is at least one segment
281  return 1;
282  } else {
283  return no;
284  }
285 }
286 
287 
288 void
291  const double length = e.getLength();
292  const int numSegments = numSegmentsFor(length, oc.getFloat("meso-edgelength"));
293  const double slength = length / (double)numSegments;
294  MESegment* newSegment = nullptr;
295  MESegment* nextSegment = nullptr;
296  const bool laneQueue = oc.getBool("meso-lane-queue");
297  bool multiQueue = laneQueue || (oc.getBool("meso-multi-queue") && e.getLanes().size() > 1 && e.getNumSuccessors() > 1);
298  for (int s = numSegments - 1; s >= 0; s--) {
299  std::string id = e.getID() + ":" + toString(s);
300  newSegment = new MESegment(id, e, nextSegment, slength, e.getLanes()[0]->getSpeedLimit(), s, multiQueue, edgeType);
301  multiQueue = laneQueue;
302  nextSegment = newSegment;
303  }
304  while (e.getNumericalID() >= static_cast<int>(myEdges2FirstSegments.size())) {
305  myEdges2FirstSegments.push_back(0);
306  }
307  myEdges2FirstSegments[e.getNumericalID()] = newSegment;
308 }
309 
310 
311 void
313  if (e.getNumericalID() < (int)myEdges2FirstSegments.size()) {
316  while (s != nullptr) {
317  s->initSegment(edgeType, e, s->getCapacity());
318  s = s->getNextSegment();
319  }
320  }
321 }
322 
323 
324 MESegment*
325 MELoop::getSegmentForEdge(const MSEdge& e, double pos) {
326  if (e.getNumericalID() >= (int)myEdges2FirstSegments.size()) {
327  return nullptr;
328  }
330  if (pos > 0) {
331  double cpos = 0;
332  while (s->getNextSegment() != nullptr && cpos + s->getLength() < pos) {
333  cpos += s->getLength();
334  s = s->getNextSegment();
335  }
336  }
337  return s;
338 }
339 
340 
341 bool
343  for (const MSEdge* succ : e.getSuccessors()) {
344  if (succ->isRoundabout()) {
345  return true;
346  }
347  }
348  return false;
349 }
350 
351 
352 /****************************************************************************/
long long int SUMOTime
Definition: GUI.h:35
#define WRITE_WARNINGF(...)
Definition: MsgHandler.h:296
#define TL(string)
Definition: MsgHandler.h:315
SUMOTime DELTA_T
Definition: SUMOTime.cpp:38
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition: SUMOTime.cpp:69
#define SUMOTime_MAX
Definition: SUMOTime.h:34
#define TIME2STEPS(x)
Definition: SUMOTime.h:57
T MIN2(T a, T b)
Definition: StdDefs.h:76
T MAX2(T a, T b)
Definition: StdDefs.h:82
T MAX3(T a, T b, T c)
Definition: StdDefs.h:96
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
MESegment * nextSegment(MESegment *s, MEVehicle *v)
Retrieve next segment.
Definition: MELoop.cpp:259
static bool isEnteringRoundabout(const MSEdge &e)
whether the given edge is entering a roundabout
Definition: MELoop.cpp:342
void updateSegmentsForEdge(const MSEdge &e)
Update segments after loading meso edge type parameters from additional file.
Definition: MELoop.cpp:312
MELoop(const SUMOTime recheckInterval)
SUMO constructor.
Definition: MELoop.cpp:46
SUMOTime changeSegment(MEVehicle *veh, SUMOTime leaveTime, MESegment *const toSegment, MSMoveReminder::Notification reason, const bool ignoreLink=false) const
change to the next segment this handles combinations of the following cases: (ending / continuing rou...
Definition: MELoop.cpp:79
MESegment * getSegmentForEdge(const MSEdge &e, double pos=0)
Get the segment for a given edge at a given position.
Definition: MELoop.cpp:325
const SUMOTime myLinkRecheckInterval
the interval at which to recheck at blocked junctions (<=0 means asap)
Definition: MELoop.h:155
void simulate(SUMOTime tMax)
Perform simulation up to the given time.
Definition: MELoop.cpp:61
bool removeLeaderCar(MEVehicle *v)
Removes the given car from the leading vehicles.
Definition: MELoop.cpp:236
const SUMOTime myFullRecheckInterval
the interval at which to recheck at full segments (<=0 means asap)
Definition: MELoop.h:152
void vaporizeCar(MEVehicle *v, MSMoveReminder::Notification reason)
remove the given car and clean up the relevant data structures
Definition: MELoop.cpp:251
void teleportVehicle(MEVehicle *veh, MESegment *const toSegment)
teleports a vehicle or continues a teleport
Definition: MELoop.cpp:163
void addLeaderCar(MEVehicle *veh, MSLink *link)
Adds the given car to the leading vehicles.
Definition: MELoop.cpp:223
void clearState()
Remove all vehicles before quick-loading state.
Definition: MELoop.cpp:230
std::vector< MESegment * > myEdges2FirstSegments
mapping from internal edge ids to their initial segments
Definition: MELoop.h:149
std::map< SUMOTime, std::vector< MEVehicle * > > myLeaderCars
leader cars in the segments sorted by exit time
Definition: MELoop.h:146
~MELoop()
Definition: MELoop.cpp:49
void checkCar(MEVehicle *veh)
Check whether the vehicle may move.
Definition: MELoop.cpp:129
static int numSegmentsFor(const double length, const double slength)
Compute number of segments per edge (best value stay close to the configured segment length)
Definition: MELoop.cpp:278
void buildSegmentsFor(const MSEdge &e, const OptionsCont &oc)
Build the segments for a given edge.
Definition: MELoop.cpp:289
A single mesoscopic segment (cell)
Definition: MESegment.h:49
void addReminders(MEVehicle *veh) const
add this lanes MoveReminders to the given vehicle
Definition: MESegment.cpp:590
static const int PARKING_QUEUE
Definition: MESegment.h:52
void receive(MEVehicle *veh, const int qIdx, SUMOTime time, const bool isDepart=false, const bool isTeleport=false, const bool newEdge=false)
Adds the vehicle to the segment, adapting its parameters.
Definition: MESegment.cpp:598
MSLink * getLink(const MEVehicle *veh, bool tlsPenalty=false) const
Returns the link the given car will use when passing the next junction.
Definition: MESegment.cpp:438
double getLength() const
Returns the length of the segment in meters.
Definition: MESegment.h:242
SUMOTime hasSpaceFor(const MEVehicle *const veh, const SUMOTime entryTime, int &qIdx, const bool init=false) const
Returns whether the given vehicle would still fit into the segment.
Definition: MESegment.cpp:302
void initSegment(const MesoEdgeType &edgeType, const MSEdge &parent, const double capacity)
set model parameters (may be updated from additional file after network loading is complete)
Definition: MESegment.cpp:147
MESegment * getNextSegment() const
Returns the following segment on the same edge (0 if it is the last).
Definition: MESegment.h:234
void send(MEVehicle *veh, MESegment *const next, const int nextQIdx, SUMOTime time, const MSMoveReminder::Notification reason)
Removes the vehicle from the segment, adapting its parameters.
Definition: MESegment.cpp:520
const MSEdge & getEdge() const
Returns the edge this segment belongs to.
Definition: MESegment.h:359
SUMOTime getEventTime() const
Returns the (planned) time at which the next vehicle leaves this segment.
Definition: MESegment.cpp:744
double getCapacity() const
Returns the sum of the lengths of all usable lanes of the segment in meters.
Definition: MESegment.h:250
int getIndex() const
Returns the running index of the segment in the edge (0 is the most upstream).
Definition: MESegment.h:226
static bool isInvalid(const MESegment *segment)
whether the given segment is 0 or encodes vaporization
Definition: MESegment.h:443
A vehicle from the mesoscopic point of view.
Definition: MEVehicle.h:42
bool mayProceed()
Returns whether the vehicle is allowed to pass the next junction, checks also for triggered stops.
Definition: MEVehicle.cpp:345
void processStop()
ends the current stop and performs loading/unloading
Definition: MEVehicle.cpp:316
SUMOTime getWaitingTime(const bool accumulated=false) const
Returns the duration for which the vehicle was blocked.
Definition: MEVehicle.h:271
bool moveRoutePointer()
Update when the vehicle enters a new edge in the move step.
Definition: MEVehicle.cpp:137
void updateDetectors(SUMOTime currentTime, const bool isLeave, const MSMoveReminder::Notification reason=MSMoveReminder::NOTIFICATION_JUNCTION)
Updates all vehicle detectors.
Definition: MEVehicle.cpp:441
SUMOTime getLastEntryTime() const
Returns the time the vehicle entered the current segment.
Definition: MEVehicle.h:248
void setEventTime(SUMOTime t, bool hasDelay=true)
Sets the (planned) time at which the vehicle leaves its current segment.
Definition: MEVehicle.h:188
void setApproaching(MSLink *link)
registers vehicle with the given link
Definition: MEVehicle.cpp:177
MESegment * getSegment() const
Returns the current segment the vehicle is on.
Definition: MEVehicle.h:218
int getQueIndex() const
Returns the index of the que the vehicle is in.
Definition: MEVehicle.h:226
virtual void setSegment(MESegment *s, int idx=0)
Sets the current segment the vehicle is at together with its que.
Definition: MEVehicle.h:209
SUMOTime getEventTime() const
Returns the (planned) time at which the vehicle leaves its current segment.
Definition: MEVehicle.h:200
void setBlockTime(const SUMOTime t)
Sets the time at which the vehicle was blocked.
Definition: MEVehicle.h:256
SUMOTime getBlockTime() const
Returns the time at which the vehicle was blocked.
Definition: MEVehicle.h:265
const MSEdge * succEdge(int nSuccs) const
Returns the nSuccs'th successor of edge the vehicle is currently at.
bool isParking() const
Returns whether the vehicle is parking.
const MSEdge * getEdge() const
Returns the edge the vehicle is currently at.
virtual void activateReminders(const MSMoveReminder::Notification reason, const MSLane *enteredLane=0)
"Activates" all current move reminder
bool isStoppedTriggered() const
Returns whether the vehicle is on a triggered stop.
bool isStopped() const
Returns whether the vehicle is at a stop.
A road/street connecting two junctions.
Definition: MSEdge.h:77
int getNumSuccessors() const
Returns the number of edges that may be reached from this edge.
Definition: MSEdge.h:376
const std::vector< MSLane * > & getLanes() const
Returns this edge's lanes.
Definition: MSEdge.h:168
double getSpeedLimit() const
Returns the speed limit of the edge @caution The speed limit of the first lane is retured; should pro...
Definition: MSEdge.cpp:1094
double getLength() const
return the length of the edge
Definition: MSEdge.h:662
int getNumericalID() const
Returns the numerical id of the edge.
Definition: MSEdge.h:303
const std::string & getEdgeType() const
Returns the type of the edge.
Definition: MSEdge.h:316
const MSEdgeVector & getSuccessors(SUMOVehicleClass vClass=SVC_IGNORING) const
Returns the following edges, restricted by vClass.
Definition: MSEdge.cpp:1194
static bool gRemoveGridlocked
Definition: MSGlobals.h:72
static SUMOTime gTimeToGridlock
Definition: MSGlobals.h:57
Notification
Definition of a vehicle state.
@ NOTIFICATION_ARRIVED
The vehicle arrived at its destination (is deleted)
@ NOTIFICATION_TELEPORT_ARRIVED
The vehicle was teleported out of the net.
@ NOTIFICATION_SEGMENT
The vehicle changes the segment (meso only)
@ NOTIFICATION_JUNCTION
The vehicle arrived at a junction.
@ NOTIFICATION_TELEPORT
The vehicle is being teleported.
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition: MSNet.cpp:182
MSVehicleControl & getVehicleControl()
Returns the vehicle control.
Definition: MSNet.h:378
const MESegment::MesoEdgeType & getMesoType(const std::string &typeID)
Returns edge type specific meso parameters if no type specific parameters have been loaded,...
Definition: MSNet.cpp:365
void registerTeleportJam()
register one non-collision-related teleport
void scheduleVehicleRemoval(SUMOVehicle *veh, bool checkDuplicate=false)
Removes a vehicle after it has ended.
const std::string & getID() const
Returns the id.
Definition: Named.h:74
A storage for options typed value containers)
Definition: OptionsCont.h:89
double getFloat(const std::string &name) const
Returns the double-value of the named option (only for Option_Float)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
edge type specific meso parameters
Definition: MESegment.h:55