Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
MSQueueExport.cpp
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2012-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/****************************************************************************/
21// Export the queueing length in front of a junction (very experimental!)
22/****************************************************************************/
23#include <config.h>
24
25#include <algorithm>
26#include <cmath>
27#include <limits>
29#include <microsim/MSEdge.h>
30#include <microsim/MSLane.h>
31#include <microsim/MSGlobals.h>
32#include <mesosim/MELoop.h>
33#include <mesosim/MESegment.h>
34#include <mesosim/MEVehicle.h>
39#include "MSQueueExport.h"
40#include <microsim/MSNet.h>
41#include <microsim/MSVehicle.h>
42
43
44// ===========================================================================
45// static member definitions
46// ===========================================================================
48std::map<const MSEdge*, std::vector<std::pair<double, double> > > MSQueueExport::myEdgeSamples;
49
50
51// ===========================================================================
52// method definitions
53// ===========================================================================
54void
57 const SUMOTime begin = string2time(oc.getString("begin"));
58 const SUMOTime period = string2time(oc.getString("queue-output.period"));
59 if (period > 0 && (timestep - begin) % period != 0) {
60 return;
61 }
62 const double threshold = oc.getFloat("queue-output.speed-threshold");
63 const SUMOTime aggregation = string2time(oc.getString("queue-output.aggregation"));
64 if (aggregation > 0) {
65 const SUMOTime intervalStart = begin + ((timestep - begin) / aggregation) * aggregation;
66 if (myIntervalStart != SUMOTime_MIN && intervalStart != myIntervalStart) {
68 }
69 myIntervalStart = intervalStart;
70 writeEdge(nullptr, threshold, [] {});
71 return;
72 }
73 // The timestep block is opened lazily so that with --queue-output.skip-empty
74 // (and, in particular, for columnar formats like Parquet or CSV) no row is
75 // emitted for time steps without any queue.
76 bool wrapperOpen = false;
77 auto ensureOpen = [&]() {
78 if (!wrapperOpen) {
79 of.openTag("data").writeAttr("timestep", time2string(timestep));
80 of.openTag("lanes");
81 wrapperOpen = true;
82 }
83 };
84 if (!oc.getBool("queue-output.skip-empty")) {
85 ensureOpen();
86 }
87 writeEdge(&of, threshold, ensureOpen);
88 if (wrapperOpen) {
89 of.closeTag(); // lanes
90 of.closeTag(); // data
91 }
92}
93
94
95void
97 const SUMOTime aggregation = string2time(OptionsCont::getOptions().getString("queue-output.aggregation"));
98 if (aggregation > 0 && myIntervalStart != SUMOTime_MIN) {
99 writeInterval(of, myIntervalStart, MIN2(myIntervalStart + aggregation, timestep));
100 }
102 myEdgeSamples.clear();
103}
104
105
106void
107MSQueueExport::writeEdge(OutputDevice* of, double threshold, const std::function<void()>& ensureOpen) {
109 for (const MSEdge* const edge : ec.getEdges()) {
111 double segmentOffset = 0.;
112 for (const MESegment* segment = MSGlobals::gMesoNet->getSegmentForEdge(*edge); segment != nullptr; segment = segment->getNextSegment()) {
113 for (int qIdx = 0; qIdx < segment->numQueues(); ++qIdx) {
114 writeMesoQueue(of, *edge, *segment, qIdx, segmentOffset, threshold, ensureOpen);
115 }
116 segmentOffset += segment->getLength();
117 }
118 } else {
119 for (const MSLane* const lane : edge->getLanes()) {
120 writeLane(of, *lane, threshold, ensureOpen);
121 }
122 }
123 }
124}
125
126
127void
128MSQueueExport::writeLane(OutputDevice* of, const MSLane& lane, double threshold, const std::function<void()>& ensureOpen) {
129 // maximum of all vehicle waiting times
130 double queueing_time = 0.0;
131 // back of last stopped vehicle (XXX does not check for continuous queue)
132 double queueing_length = 0.0;
133 // back of last slow vehicle (XXX does not check for continuous queue)
134 double queueing_length2 = 0.0;
135 // number of slow vehicles
136 int queueing_count = 0;
137
138 if (!lane.empty()) {
139 for (MSLane::VehCont::const_iterator it_veh = lane.myVehicles.begin(); it_veh != lane.myVehicles.end(); ++it_veh) {
140 const MSVehicle& veh = **it_veh;
141 if (!veh.isOnRoad()) {
142 continue;
143 }
144
145 if (veh.getWaitingSeconds() > 0) {
146 queueing_time = MAX2(veh.getWaitingSeconds(), queueing_time);
147 const double veh_back_to_lane_end = (lane.getLength() - veh.getPositionOnLane()) + veh.getVehicleType().getLength();
148 queueing_length = MAX2(veh_back_to_lane_end, queueing_length);
149 }
150
151 //Experimental
152 if (veh.getSpeed() < (threshold) && (veh.getPositionOnLane() > (veh.getLane()->getLength()) * 0.25)) {
153 const double veh_back_to_lane_end = (lane.getLength() - veh.getPositionOnLane()) + veh.getVehicleType().getLength();
154 queueing_length2 = MAX2(veh_back_to_lane_end, queueing_length2);
155 queueing_count++;
156 }
157 }
158 }
159
160 //Output
161 if (of != nullptr) {
162 if (queueing_length > 1 || queueing_length2 > 1) {
163 ensureOpen();
164 of->openTag("lane").writeAttr("id", lane.getID()).writeAttr("queueing_time", queueing_time).writeAttr("queueing_length", queueing_length);
165 of->writeAttr("queueing_length_experimental", queueing_length2).closeTag();
166 }
167 } else if (queueing_count > 0) {
168 myEdgeSamples[&lane.getEdge()].push_back(std::make_pair((double)queueing_count, queueing_length2));
169 }
170}
171
172
173void
174MSQueueExport::writeMesoQueue(OutputDevice* of, const MSEdge& edge, const MESegment& segment, int qIdx, double segmentOffset, double threshold, const std::function<void()>& ensureOpen) {
175 const std::vector<MEVehicle*>& queue = segment.getQueue(qIdx);
176 const int queueSize = (int)queue.size();
177 // maximum of all vehicle waiting times
178 double queueing_time = 0.0;
179 // position of the last slow vehicle counted from the head of the queue
180 int queueing_count = 0;
181 // distance from the segment end to the back of the last slow vehicle
182 double queueing_length = 0.0;
183 double occupancy = 0.0;
184 const double segLength = segment.getLength();
185 const double segmentEnd = segmentOffset + segLength;
186 const double lanesCovered = segment.numQueues() == 1 ? std::round(segment.getCapacity() / segLength) : 1.;
187 // positions and earliest exit times are interpolated as in MSEdge::getMesoPositions
188 SUMOTime earliestExitTime = segment.getQueueBlockTime(qIdx);
189 // conservative exit times use the jam-dependent headway as in MESegment::getMeanSpeed
190 SUMOTime conservativeExitTime = earliestExitTime;
191 double prevPos = std::numeric_limits<double>::max();
192 bool prevQueued = false;
193 const double now = SIMTIME;
194 for (int i = 0; i < queueSize; ++i) {
195 // vehicles are stored in reverse entry order (the queue head is at the back)
196 const MEVehicle* const veh = queue[queueSize - 1 - i];
197 const double lengthWithGap = veh->getVehicleType().getLengthWithGap();
198 occupancy += lengthWithGap;
199 earliestExitTime = MAX2(earliestExitTime, veh->getEventTime());
200 conservativeExitTime = MAX2(conservativeExitTime, veh->getEventTime());
201 double maxPos = segmentEnd;
202 if (i > 0) {
203 earliestExitTime += segment.getMinTauWithVehLength(lengthWithGap, veh->getVehicleType().getCarFollowModel().getHeadwayTime());
204 conservativeExitTime += segment.getTauWithVehLength(qIdx, lengthWithGap, veh->getVehicleType().getCarFollowModel().getHeadwayTime());
205 maxPos = MIN2(maxPos, prevPos - lengthWithGap / lanesCovered);
206 }
207 const double entry = veh->getLastEntryTimeSeconds();
208 const double travelTime = MAX2(STEPS2TIME(earliestExitTime) - entry, TS);
209 const double linearPos = MIN2(segmentEnd, segmentOffset + segLength * (now - entry) / travelTime);
210 // conservative speed of the interpolated position trajectory. Unlike
211 // getSpeed() this accounts for blocked vehicles further ahead in the queue
212 const double interpolatedSpeed = segLength / MAX2(STEPS2TIME(conservativeExitTime) - entry, TS);
213 // a vehicle is queued if it is slow by itself or if it has caught up
214 // with the clamped position behind a queued vehicle ahead
215 const bool queued = MIN2(veh->getSpeed(), interpolatedSpeed) < threshold || (prevQueued && linearPos >= maxPos - POSITION_EPS);
216 if (queued) {
217 queueing_count = i + 1;
218 queueing_time = MAX2(veh->getWaitingSeconds(), queueing_time);
220 queueing_length = MAX2(0., segmentEnd - (veh->getPositionOnLane() - veh->getVehicleType().getLength()));
221 } else {
222 queueing_length = occupancy / lanesCovered;
223 }
224 }
225 prevQueued = queued;
226 prevPos = MIN2(linearPos, maxPos);
227 }
228 if (queueing_count == 0) {
229 return;
230 }
231 if (of != nullptr) {
232 ensureOpen();
233 of->openTag("lane").writeAttr("id", edge.getLanes()[qIdx]->getID()).writeAttr("segment", segment.getIndex());
234 of->writeAttr("queueing_time", queueing_time).writeAttr("queueing_length", queueing_length).writeAttr("queueing_count", queueing_count).closeTag();
235 } else {
236 myEdgeSamples[&edge].push_back(std::make_pair((double)queueing_count, queueing_length));
237 }
238}
239
240
241void
243 // The interval element is opened lazily so that with --queue-output.skip-empty
244 // (and, in particular, for columnar formats) no row is emitted for intervals
245 // without any queue.
246 bool intervalOpen = false;
247 auto ensureOpen = [&]() {
248 if (!intervalOpen) {
249 of.openTag("interval").writeAttr("begin", time2string(begin)).writeAttr("end", time2string(end));
250 intervalOpen = true;
251 }
252 };
254 if (!oc.getBool("queue-output.skip-empty")) {
255 ensureOpen();
256 }
257 const double p = oc.getFloat("queue-output.percentile") / 100.;
258 // iterate in network edge order for deterministic output
259 for (const MSEdge* const edge : MSNet::getInstance()->getEdgeControl().getEdges()) {
260 const auto it = myEdgeSamples.find(edge);
261 if (it == myEdgeSamples.end()) {
262 continue;
263 }
264 std::vector<double> counts;
265 std::vector<double> lengths;
266 for (const auto& sample : it->second) {
267 counts.push_back(sample.first);
268 lengths.push_back(sample.second);
269 }
270 std::sort(counts.begin(), counts.end());
271 std::sort(lengths.begin(), lengths.end());
272 ensureOpen();
273 of.openTag("edge").writeAttr("id", edge->getID()).writeAttr("samples", (int)counts.size());
274 of.writeAttr("maxQueueLengthInVehicles", counts.back());
275 of.writeAttr("medianQueueLengthInVehicles", percentile(counts, 0.5));
276 of.writeAttr("percentileQueueLengthInVehicles", percentile(counts, p));
277 of.writeAttr("maxQueueLengthInMeters", lengths.back());
278 of.writeAttr("medianQueueLengthInMeters", percentile(lengths, 0.5));
279 of.writeAttr("percentileQueueLengthInMeters", percentile(lengths, p));
280 of.closeTag();
281 }
282 if (intervalOpen) {
283 of.closeTag();
284 }
285 myEdgeSamples.clear();
286}
287
288
289double
290MSQueueExport::percentile(const std::vector<double>& sorted, double p) {
291 assert(!sorted.empty());
292 const double rank = p * (double)(sorted.size() - 1);
293 const int lower = (int)rank;
294 if (lower + 1 >= (int)sorted.size()) {
295 return sorted.back();
296 }
297 const double frac = rank - (double)lower;
298 return sorted[lower] * (1. - frac) + sorted[lower + 1] * frac;
299}
300
301
302/****************************************************************************/
long long int SUMOTime
Definition GUI.h:36
SUMOTime string2time(const std::string &r)
convert string to SUMOTime
Definition SUMOTime.cpp:46
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition SUMOTime.cpp:91
#define STEPS2TIME(x)
Definition SUMOTime.h:58
#define SUMOTime_MIN
Definition SUMOTime.h:35
#define TS
Definition SUMOTime.h:45
#define SIMTIME
Definition SUMOTime.h:65
T MIN2(T a, T b)
Definition StdDefs.h:80
T MAX2(T a, T b)
Definition StdDefs.h:86
A single mesoscopic segment (cell)
Definition MESegment.h:50
SUMOTime getTauWithVehLength(int qIdx, double lengthWithGap, double vehicleTau) const
like getMinTauWithVehLength but using the jam-state dependent net time gap of the given queue (as in ...
Definition MESegment.h:506
double getLength() const
Returns the length of the segment in meters.
Definition MESegment.h:254
const std::vector< MEVehicle * > & getQueue(int index) const
Returns the cars in the queue with the given index for visualization.
Definition MESegment.h:224
int numQueues() const
return the number of queues
Definition MESegment.h:218
double getCapacity() const
Returns the sum of the lengths of all usable lanes of the segment in meters.
Definition MESegment.h:262
int getIndex() const
Returns the running index of the segment in the edge (0 is the most upstream).
Definition MESegment.h:238
SUMOTime getQueueBlockTime(int index) const
Definition MESegment.h:229
SUMOTime getMinTauWithVehLength(double lengthWithGap, double vehicleTau) const
convert net time gap (leader back to follower front) to gross time gap (leader front to follower fron...
Definition MESegment.h:501
A vehicle from the mesoscopic point of view.
Definition MEVehicle.h:42
double getLastEntryTimeSeconds() const
Returns the entry time for the current segment.
Definition MEVehicle.h:327
double getPositionOnLane() const
Get the vehicle's position along the lane.
Definition MEVehicle.cpp:73
SUMOTime getEventTime() const
Returns the (planned) time at which the vehicle leaves its current segment.
Definition MEVehicle.h:215
double getSpeed() const
Returns the vehicle's estimated speed assuming no delays.
double getWaitingSeconds() const
Returns the number of seconds waited (speed was lesser than 0.1m/s)
const MSVehicleType & getVehicleType() const
Returns the vehicle's type definition.
virtual double getHeadwayTime() const
Get the driver's desired headway [s].
Definition MSCFModel.h:355
Stores edges and lanes, performs moving of vehicle.
const MSEdgeVector & getEdges() const
Returns loaded edges.
A road/street connecting two junctions.
Definition MSEdge.h:77
const std::vector< MSLane * > & getLanes() const
Returns this edge's lanes.
Definition MSEdge.h:168
static bool gUseMesoSim
Definition MSGlobals.h:106
static bool gMesoInterpolatePos
Definition MSGlobals.h:112
static MELoop * gMesoNet
mesoscopic simulation infrastructure
Definition MSGlobals.h:115
Representation of a lane in the micro simulation.
Definition MSLane.h:84
bool empty() const
Returns true if there is not a single vehicle on the lane.
Definition MSLane.h:765
VehCont myVehicles
The lane's vehicles. This container holds all vehicles that have their front (longitudinally) and the...
Definition MSLane.h:1507
double getLength() const
Returns the lane's length.
Definition MSLane.h:632
MSEdge & getEdge() const
Returns the lane's edge.
Definition MSLane.h:790
static MSNet * getInstance()
Returns the pointer to the unique instance of MSNet (singleton).
Definition MSNet.cpp:199
MSEdgeControl & getEdgeControl()
Returns the edge control.
Definition MSNet.h:445
static SUMOTime myIntervalStart
The begin of the current aggregation interval.
static void writeMesoQueue(OutputDevice *of, const MSEdge &edge, const MESegment &segment, int qIdx, double segmentOffset, double threshold, const std::function< void()> &ensureOpen)
Checks a single segment queue (meso)
static double percentile(const std::vector< double > &sorted, double p)
Returns the given percentile (with linear interpolation) of a sorted sample vector.
static void finish(OutputDevice &of, SUMOTime timestep)
Writes the last (possibly incomplete) aggregation interval and clears the collected samples.
static void writeLane(OutputDevice *of, const MSLane &lane, double threshold, const std::function< void()> &ensureOpen)
Checks a single lane for a vehicle queue (micro)
static void writeInterval(OutputDevice &of, SUMOTime begin, SUMOTime end)
Writes aggregated per-edge queue length statistics for the given interval.
static void write(OutputDevice &of, SUMOTime timestep)
Export the queueing length in front of a junction (very experimental!)
static std::map< const MSEdge *, std::vector< std::pair< double, double > > > myEdgeSamples
Queue length samples (in vehicles, in meters) per edge, collected during the current aggregation inte...
static void writeEdge(OutputDevice *of, double threshold, const std::function< void()> &ensureOpen)
Iterates through the edges and their lanes (micro) or segment queues (meso). If of is nullptr,...
Representation of a vehicle in the micro simulation.
Definition MSVehicle.h:77
bool isOnRoad() const
Returns the information whether the vehicle is on a road (is simulated)
Definition MSVehicle.h:605
const MSLane * getLane() const
Returns the lane the vehicle is on.
Definition MSVehicle.h:581
double getSpeed() const
Returns the vehicle's current speed.
Definition MSVehicle.h:490
double getPositionOnLane() const
Get the vehicle's position along the lane.
Definition MSVehicle.h:374
double getLengthWithGap() const
Get vehicle's length including the minimum gap [m].
const MSCFModel & getCarFollowModel() const
Returns the vehicle type's car following model definition (const version)
double getLength() const
Get vehicle's length [m].
const std::string & getID() const
Returns the id.
Definition Named.h:73
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)
std::string getString(const std::string &name) const
Returns the string-value of the named option (only for Option_String)
bool getBool(const std::string &name) const
Returns the boolean-value of the named option (only for Option_Bool)
static OptionsCont & getOptions()
Retrieves the options.
Static storage of an output device and its base (abstract) implementation.
OutputDevice & openTag(const std::string &xmlElement)
Opens an XML tag.
OutputDevice & writeAttr(const ATTR_TYPE &attr, const T &val, const bool isNull=false, const bool escape=false)
writes a named attribute
bool closeTag(const std::string &comment="")
Closes the most recently opened tag and optionally adds a comment.