Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/detector/flowrouter.py
169673 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2007-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 flowrouter.py
15
# @author Michael Behrisch
16
# @author Daniel Krajzewicz
17
# @author Mirko Barthauer
18
# @date 2007-06-28
19
20
"""
21
This script does flow routing similar to the dfrouter.
22
It has three mandatory parameters, the SUMO net (.net.xml), a file
23
specifying detectors and one for the flows. It may detect the type
24
of the detectors (source, sink, in between) itself or read it from
25
the detectors file.
26
"""
27
from __future__ import absolute_import
28
from __future__ import division
29
from __future__ import print_function
30
import os
31
import random
32
import sys
33
import heapq
34
from xml.sax import make_parser, handler
35
from collections import defaultdict
36
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
37
import sumolib # noqa
38
from sumolib.options import ArgumentParser # noqa
39
from sumolib.net.lane import get_allowed # noqa
40
import detector # noqa
41
42
# Vertex class which stores incoming and outgoing edges as well as
43
# auxiliary data for the flow computation. The members are accessed
44
# directly.
45
46
DEBUG = False
47
PULL_FLOW = True
48
49
50
class Vertex:
51
52
def __init__(self):
53
self.inEdges = []
54
self.outEdges = []
55
self.reset()
56
57
def reset(self):
58
self.inPathEdge = None
59
self.flowDelta = sys.maxsize
60
self.gain = 0
61
self.value = 0
62
if self.outEdges:
63
for e in self.outEdges:
64
self.value += (e.flow / e.numLanes if e.flow > 0 else -e.numLanes)
65
self.value /= len(self.outEdges)
66
67
def update(self, edge, flow, isForward):
68
self.inPathEdge = edge
69
self.flowDelta = flow
70
if isForward:
71
numSatEdges = edge.source.gain // edge.source.flowDelta
72
self.gain = numSatEdges * flow
73
if edge.startCapacity < sys.maxsize:
74
self.gain += flow
75
else:
76
numSatEdges = edge.target.gain // edge.target.flowDelta
77
self.gain = numSatEdges * flow
78
if edge.startCapacity < sys.maxsize:
79
self.gain -= flow
80
81
def __repr__(self):
82
return "<%s,%s,%s>" % (self.inPathEdge, self.flowDelta, self.gain)
83
84
def __lt__(self, other):
85
return self.value < other.value
86
87
88
# Edge class which stores start and end vertex, type amd label of the edge
89
# as well as flow and capacity for the flow computation and some parameters
90
# read from the net. The members are accessed directly.
91
class Edge:
92
lanebased = False
93
94
def __init__(self, label, source, target, kind, linkDir=None):
95
self.label = label
96
self.source = source
97
self.target = target
98
self.kind = kind
99
self.maxSpeed = 0.0
100
self.linkDir = linkDir
101
self.length = 0.0
102
self.numLanes = 0
103
self.isOnSourcePath = False
104
self.isOnSinkPath = False
105
self.detGroup = []
106
self.reset()
107
108
def reset(self):
109
self.capacity = sys.maxsize
110
self.startCapacity = sys.maxsize
111
self.flow = 0
112
self.routes = []
113
self.newRoutes = []
114
115
def getDetFlow(self):
116
result = 0
117
for group in self.detGroup:
118
if int(group.totalFlow) > result:
119
result = int(group.totalFlow)
120
return result
121
122
def __repr__(self):
123
cap = str(self.capacity)
124
if self.capacity == sys.maxsize:
125
cap = "inf"
126
return self.kind + "_" + self.label + "<" + str(self.flow) + "|" + cap + ">"
127
128
def __lt__(self, other):
129
return self.label < other.label
130
131
132
# Route class storing the list of edges and the frequency of a route.
133
class Route:
134
135
def __init__(self, freq, edges):
136
self.frequency = freq
137
self.edges = edges
138
self.newFrequency = None
139
140
def __repr__(self):
141
result = str(self.frequency) + " * ["
142
lastLabel = ""
143
for edge in self.edges:
144
if edge.kind == "real":
145
if lastLabel:
146
result += lastLabel + ", "
147
lastLabel = edge.label
148
return result + lastLabel + "]"
149
150
def __lt__(self, other):
151
return self.edges < other.edges
152
153
154
# Net class which stores the network (vertex and edge collection) and the
155
# routes. All the algorithmic stuff and the output generation are also
156
# inside this class. The members are either "private" or have get/add/remove
157
# methods.
158
class Net:
159
160
def __init__(self):
161
self._vertices = []
162
self._edges = {}
163
self._internalEdges = []
164
self._possibleSources = set()
165
self._possibleSinks = set()
166
self._source = self.newVertex()
167
self._sink = self.newVertex()
168
self._edgeRestriction = {}
169
self._routeRestriction = {}
170
if options.restrictionfile:
171
for f in options.restrictionfile:
172
with open(f) as fp:
173
for line in fp:
174
ls = line.split()
175
if len(ls) == 2:
176
self._edgeRestriction[ls[1]] = int(ls[0])
177
else:
178
self._routeRestriction[tuple(ls[1:])] = int(ls[0])
179
if options.verbose:
180
print("Loaded %s edge restrictions and %s route restrictions" %
181
(len(self._edgeRestriction), len(self._routeRestriction)))
182
183
def newVertex(self):
184
v = Vertex()
185
self._vertices.append(v)
186
return v
187
188
def getEdge(self, edgeLabel):
189
if edgeLabel in self._edges:
190
return self._edges[edgeLabel]
191
else:
192
raise RuntimeError("edge '%s' not found" % edgeLabel)
193
194
def addEdge(self, edgeObj):
195
edgeObj.source.outEdges.append(edgeObj)
196
edgeObj.target.inEdges.append(edgeObj)
197
if edgeObj.kind == "real":
198
self._edges[edgeObj.label] = edgeObj
199
else:
200
assert edgeObj.target == self._sink or len(edgeObj.target.outEdges) == 1
201
if len(edgeObj.target.outEdges) == 1:
202
edgeObj.numLanes = edgeObj.target.outEdges[0].numLanes
203
else:
204
edgeObj.numLanes = 1
205
self._internalEdges.append(edgeObj)
206
207
def removeEdge(self, edgeObj):
208
edgeObj.source.outEdges.remove(edgeObj)
209
edgeObj.target.inEdges.remove(edgeObj)
210
if edgeObj.kind == "real":
211
del self._edges[edgeObj.label]
212
checkEdges = set(edgeObj.source.inEdges).union(edgeObj.target.outEdges)
213
for edge in checkEdges:
214
if edge.kind != "real":
215
self.removeEdge(edge)
216
217
def addIsolatedRealEdge(self, edgeLabel):
218
self.addEdge(Edge(edgeLabel, self.newVertex(), self.newVertex(),
219
"real"))
220
221
def addSourceEdge(self, edgeObj):
222
newEdge = Edge("s_" + edgeObj.label, self._source, edgeObj.source,
223
"source")
224
self.addEdge(newEdge)
225
226
def addSinkEdge(self, edgeObj):
227
newEdge = Edge("t_" + edgeObj.label, edgeObj.target, self._sink,
228
"sink")
229
self.addEdge(newEdge)
230
231
def trimNet(self):
232
if options.minspeed > 0.0:
233
if options.verbose:
234
print("Removing edges with maxspeed < %s," %
235
options.minspeed, end=' ')
236
# The code in the following loop assumes there are still all
237
# auxiliary junction edges present.
238
for edgeObj in self._edges.values():
239
if edgeObj.maxSpeed < options.minspeed:
240
if len(edgeObj.detGroup) == 0 or not options.keepdet:
241
for auxpred in edgeObj.source.inEdges:
242
for realpred in auxpred.source.inEdges:
243
if realpred.maxSpeed >= options.minspeed:
244
self._possibleSinks.add(realpred)
245
for auxsucc in edgeObj.target.outEdges:
246
for realsucc in auxsucc.target.outEdges:
247
if realsucc.maxSpeed >= options.minspeed:
248
self._possibleSources.add(realsucc)
249
self.removeEdge(edgeObj)
250
if options.verbose:
251
print(len(self._edges), "left")
252
if options.trimfile:
253
trimOut = open(options.trimfile, 'w')
254
for edge in self._edges.values():
255
print("edge:" + edge.label, file=trimOut)
256
trimOut.close()
257
258
def detectSourceSink(self, sources, sinks):
259
self.trimNet()
260
for id in sorted(sources):
261
self.addSourceEdge(self.getEdge(id))
262
for id in sorted(sinks):
263
self.addSinkEdge(self.getEdge(id))
264
foundSources = []
265
foundSinks = []
266
for edgeObj in sorted(self._edges.values()):
267
if len(sources) == 0 and (len(edgeObj.source.inEdges) == 0 or edgeObj in self._possibleSources):
268
if edgeObj.numLanes > 0:
269
self.addSourceEdge(edgeObj)
270
foundSources.append(edgeObj.label)
271
if len(sinks) == 0 and (len(edgeObj.target.outEdges) == 0 or edgeObj in self._possibleSinks):
272
if edgeObj.numLanes > 0:
273
if edgeObj.label in foundSources:
274
print("Warning! Edge '%s' is simultaneously source and sink." % edgeObj.label)
275
self.removeEdge(edgeObj)
276
foundSources.remove(edgeObj.label)
277
continue
278
self.addSinkEdge(edgeObj)
279
foundSinks.append(edgeObj.label)
280
if options.verbose:
281
print(("Loaded %s sources and %s sinks from detector file. Added %s sources and %s sinks " +
282
"from the network") % (
283
len(sources), len(sinks), len(foundSources), len(foundSinks)))
284
if options.source_sink_output:
285
with open(options.source_sink_output, 'w') as outf:
286
outf.write('<detectors>\n')
287
freq = options.interval * 60 if options.interval else 24 * 3600
288
for source in foundSources:
289
outf.write((' <e1Detector id="%s_0" lane="%s_0" pos="1" type="source" friendlyPos="true" ' +
290
'file="NUL" freq="%s"/>\n') %
291
(source, source, freq))
292
for sink in foundSinks:
293
outf.write((' <e1Detector id="%s_0" lane="%s_0" pos="-1" type="sink" friendlyPos="true" ' +
294
'file="NUL" freq="%s"/>\n') %
295
(sink, sink, freq))
296
outf.write('</detectors>\n')
297
298
if len(self._sink.inEdges) == 0:
299
print("Error! No sinks found.")
300
return False
301
if len(self._source.outEdges) == 0:
302
print("Error! No sources found.")
303
return False
304
return True
305
306
def initNet(self):
307
for edge in self._internalEdges:
308
edge.reset()
309
flowRestriction = sys.maxsize
310
if options.maxturnflow and edge.linkDir == 't':
311
flowRestriction = int(options.maxturnflow * options.interval / 60)
312
if edge.label in self._edgeRestriction:
313
flowRestriction = int(self._edgeRestriction[edge.label] * options.interval / 60)
314
edge.capacity = min(edge.capacity, flowRestriction)
315
for edge in self._edges.values():
316
edge.reset()
317
if len(edge.detGroup) > 0:
318
edge.capacity = 0
319
for group in edge.detGroup:
320
if int(group.totalFlow) > edge.capacity:
321
edge.capacity = int(group.totalFlow)
322
if not options.respectzero and edge.capacity == 0:
323
edge.capacity = sys.maxsize
324
edge.startCapacity = edge.capacity
325
flowRestriction = sys.maxsize if edge.numLanes > 0 else 0
326
if options.maxflow:
327
flowRestriction = int(options.maxflow * edge.numLanes * options.interval / 60)
328
if options.maxturnflow and edge.linkDir == 't':
329
flowRestriction = int(options.maxturnflow * options.interval / 60)
330
if edge.label in self._edgeRestriction:
331
flowRestriction = int(self._edgeRestriction[edge.label] * options.interval / 60)
332
edge.capacity = min(edge.capacity, flowRestriction)
333
# collect limited source edges
334
for s in self._source.outEdges:
335
queue = [s]
336
s.isOnSourcePath = True
337
while queue:
338
edgeObj = queue.pop(0)
339
if edgeObj.startCapacity == sys.maxsize:
340
if len(edgeObj.target.inEdges) > 1:
341
s.isOnSourcePath = False
342
break
343
else:
344
queue += edgeObj.target.outEdges
345
# collect limited sink edges
346
for s in self._sink.inEdges:
347
queue = [s]
348
s.isOnSinkPath = True
349
while queue:
350
edgeObj = queue.pop(0)
351
if edgeObj.startCapacity == sys.maxsize:
352
if len(edgeObj.source.outEdges) > 1:
353
s.isOnSinkPath = False
354
break
355
else:
356
queue += edgeObj.source.inEdges
357
358
if options.verbose:
359
unlimitedSource = 0
360
for edgeObj in self._source.outEdges:
361
for src in edgeObj.target.outEdges:
362
if src.capacity == sys.maxsize:
363
unlimitedSource += 1
364
unlimitedSink = 0
365
for edgeObj in self._sink.inEdges:
366
for sink in edgeObj.source.inEdges:
367
if sink.capacity == sys.maxsize:
368
unlimitedSink += 1
369
print(len(self._source.outEdges), "sources,", end=' ')
370
print(unlimitedSource, "unlimited")
371
print(len(self._sink.inEdges), "sinks,",
372
unlimitedSink, "unlimited")
373
374
def splitRoutes(self, stubs, currEdge, upstreamBackEdges, newRoutes, alteredRoutes):
375
newStubs = []
376
backSet = set(upstreamBackEdges)
377
while len(stubs) > 0:
378
routeStub = stubs.pop()
379
if len(routeStub.edges) > 0 and currEdge == routeStub.edges[0]:
380
routeStub.edges.pop(0)
381
newStubs.append(routeStub)
382
else:
383
if DEBUG:
384
print(" trying to split", routeStub)
385
assert currEdge.routes
386
for route in currEdge.routes + currEdge.newRoutes:
387
if route.newFrequency == 0:
388
continue
389
edgePos = route.edges.index(currEdge)
390
backPath = False
391
hadForward = False
392
for edge in route.edges[edgePos + 1:]:
393
if edge in backSet:
394
if hadForward:
395
if DEBUG:
396
print(" skipping", route, "because", edge, "is in", backSet)
397
backPath = True
398
break
399
else:
400
hadForward = True
401
if backPath:
402
continue
403
routeFreq = route.frequency if route.newFrequency is None else route.newFrequency
404
newRoute = Route(min(routeStub.frequency, routeFreq),
405
route.edges[:edgePos] + routeStub.edges)
406
newRoutes.append(newRoute)
407
for edge in newRoute.edges:
408
edge.newRoutes.append(route)
409
newStubs.append(Route(newRoute.frequency,
410
route.edges[edgePos + 1:]))
411
if route.newFrequency is None:
412
route.newFrequency = route.frequency
413
route.newFrequency -= newRoute.frequency
414
alteredRoutes.append(route)
415
routeStub.frequency -= newRoute.frequency
416
if routeStub.frequency == 0:
417
break
418
if routeStub.frequency > 0:
419
if DEBUG:
420
print(" Could not split", routeStub)
421
return False
422
stubs.extend(newStubs)
423
return True
424
425
def updateFlow(self, startVertex, endVertex):
426
assert endVertex.flowDelta < sys.maxsize
427
if options.limit and endVertex.flowDelta > options.limit:
428
endVertex.flowDelta = options.limit
429
stubs = [Route(endVertex.flowDelta, [])]
430
if DEBUG:
431
print(" updateFlow start=%s end=%s flowDelta=%s" % (startVertex,
432
endVertex, endVertex.flowDelta))
433
upstreamBackEdges = list(self.getBackEdges(startVertex, endVertex))
434
newRoutes = []
435
alteredRoutes = []
436
flowDeltas = []
437
cycleStartStep = (startVertex == endVertex)
438
currVertex = endVertex
439
while currVertex != startVertex or cycleStartStep:
440
cycleStartStep = False
441
currEdge = currVertex.inPathEdge
442
if currEdge.target == currVertex:
443
if DEBUG: # and not currEdge.kind == 'junction':
444
print(" incFlow edge=%s delta=%s" % (currEdge, endVertex.flowDelta))
445
flowDeltas.append((currEdge, endVertex.flowDelta))
446
currVertex = currEdge.source
447
for routeStub in stubs:
448
routeStub.edges.insert(0, currEdge)
449
else:
450
if DEBUG:
451
print(" decFlow edge=%s delta=%s" % (currEdge, endVertex.flowDelta))
452
flowDeltas.append((currEdge, -endVertex.flowDelta))
453
currVertex = currEdge.target
454
upstreamBackEdges.pop(0)
455
if not self.splitRoutes(stubs, currEdge, upstreamBackEdges, newRoutes, alteredRoutes):
456
# resetting to previous state
457
for route in alteredRoutes:
458
route.newFrequency = None
459
for route in newRoutes:
460
for edge in route.edges:
461
del edge.newRoutes[:]
462
if DEBUG:
463
self.testFlowInvariants()
464
return False
465
if DEBUG:
466
self.testFlowInvariants()
467
# up to here no modification of existing edges or routes in case splitting fails
468
for edge, delta in flowDeltas:
469
edge.flow += delta
470
for route in alteredRoutes:
471
if route.newFrequency is not None: # otherwise it has been handled before
472
if route.newFrequency == 0:
473
for edge in route.edges:
474
edge.routes.remove(route)
475
else:
476
route.frequency = route.newFrequency
477
route.newFrequency = None
478
for route in stubs + newRoutes:
479
for edge in route.edges:
480
edge.routes.append(route)
481
del edge.newRoutes[:]
482
if DEBUG:
483
self.testFlowInvariants()
484
return True
485
486
def endsRestrictedRoute(self, edge):
487
if not self._routeRestriction:
488
return False
489
currVertex = edge.source
490
routeEdgeObj = [edge]
491
route = []
492
count = currVertex.flowDelta
493
if options.limit and count > options.limit:
494
count = options.limit
495
while currVertex != self._source:
496
edge = currVertex.inPathEdge
497
if edge.target == currVertex:
498
if edge.kind == "real":
499
if Edge.lanebased:
500
edgeID = edge.label[:edge.label.rfind("_")]
501
route.insert(0, edgeID)
502
else:
503
route.insert(0, edge.label)
504
routeEdgeObj.insert(0, edge)
505
currVertex = edge.source
506
else:
507
return False
508
for r in edge.routes:
509
if r.edges == routeEdgeObj:
510
count += r.frequency
511
if DEBUG:
512
print(" checking limit for route %s count %s" % (route, count))
513
return count > self._routeRestriction.get(tuple(route),
514
# check origin-destination restriction
515
self._routeRestriction.get((route[0], route[-1]), count))
516
517
def getBackEdges(self, pathStart, currVertex):
518
cycleStartStep = (pathStart == currVertex)
519
while currVertex != pathStart or cycleStartStep:
520
cycleStartStep = False
521
edge = currVertex.inPathEdge
522
if edge.source == currVertex:
523
yield edge
524
currVertex = edge.target
525
else:
526
currVertex = edge.source
527
528
def findPath(self, startVertex, pathStart, limitedSource=True, limitedSink=True, allowBackward=True):
529
queue = [startVertex]
530
if DEBUG:
531
print(" findPath start=%s pathStart=%s limits(%s, %s)" %
532
(startVertex, pathStart, limitedSource, limitedSink))
533
while len(queue) > 0:
534
currVertex = heapq.heappop(queue)
535
if currVertex == self._sink or (currVertex == self._source and currVertex.inPathEdge):
536
if self.updateFlow(pathStart, currVertex):
537
return True
538
continue
539
for edge in currVertex.outEdges:
540
if limitedSource and currVertex == self._source and not edge.isOnSourcePath:
541
continue
542
if limitedSink and edge.target == self._sink and not edge.isOnSinkPath:
543
continue
544
if edge.target == self._sink and self.endsRestrictedRoute(edge):
545
continue
546
if not edge.target.inPathEdge and edge.flow < edge.capacity:
547
if edge.target != self._sink or currVertex.gain > 0:
548
heapq.heappush(queue, edge.target)
549
edge.target.update(edge, min(currVertex.flowDelta,
550
edge.capacity - edge.flow),
551
True)
552
if allowBackward:
553
for edge in currVertex.inEdges:
554
if not edge.source.inPathEdge and edge.flow > 0:
555
if edge.source != self._source or currVertex.gain > 0:
556
heapq.heappush(queue, edge.source)
557
edge.source.update(edge, min(currVertex.flowDelta,
558
edge.flow), False)
559
return False
560
561
def savePulledPath(self, startVertex, unsatEdge, pred):
562
numSatEdges = 1
563
currVertex = startVertex
564
while currVertex != unsatEdge.source:
565
currEdge = pred[currVertex]
566
if currEdge.target == currVertex:
567
currEdge.source.inPathEdge = currEdge
568
currVertex = currEdge.source
569
if currEdge.capacity < sys.maxsize:
570
numSatEdges -= 1
571
else:
572
currEdge.target.inPathEdge = currEdge
573
currVertex = currEdge.target
574
if currEdge.capacity < sys.maxsize:
575
numSatEdges += 1
576
startVertex.inPathEdge = None
577
unsatEdge.target.flowDelta = startVertex.flowDelta
578
unsatEdge.target.gain = startVertex.flowDelta * numSatEdges
579
580
def pullFlow(self, unsatEdge, limitSource, limitSink, allowBackward):
581
if DEBUG:
582
print("Trying to increase flow on", unsatEdge)
583
for vertex in self._vertices:
584
vertex.reset()
585
pred = {unsatEdge.target: unsatEdge, unsatEdge.source: unsatEdge}
586
unsatEdge.target.inPathEdge = unsatEdge
587
unsatEdge.source.flowDelta = unsatEdge.capacity - unsatEdge.flow
588
queue = [unsatEdge.source]
589
while len(queue) > 0:
590
currVertex = queue.pop(0)
591
if ((currVertex == self._source and (not limitSource or pred[currVertex].isOnSourcePath)) or
592
currVertex == self._sink):
593
self.savePulledPath(currVertex, unsatEdge, pred)
594
return self.findPath(unsatEdge.target, currVertex, limitSource, limitSink, allowBackward)
595
for edge in currVertex.inEdges:
596
if edge.source not in pred and edge.flow < edge.capacity:
597
queue.append(edge.source)
598
pred[edge.source] = edge
599
edge.source.flowDelta = min(
600
currVertex.flowDelta, edge.capacity - edge.flow)
601
if allowBackward:
602
# inverse find path semantics
603
for edge in currVertex.outEdges:
604
if edge.target not in pred and edge.flow > 0:
605
queue.append(edge.target)
606
pred[edge.target] = edge
607
edge.target.flowDelta = min(
608
currVertex.flowDelta, edge.flow)
609
return False
610
611
def testFlowInvariants(self):
612
# the following code only tests assertions
613
for vertex in self._vertices:
614
flowSum = 0
615
for preEdge in vertex.inEdges:
616
flowSum += preEdge.flow
617
for succEdge in vertex.outEdges:
618
flowSum -= succEdge.flow
619
totalEdgeFlow = 0
620
for route in succEdge.routes:
621
assert route.frequency > 0
622
totalEdgeFlow += route.frequency
623
if DEBUG and totalEdgeFlow != succEdge.flow:
624
print("total edge flow failed", totalEdgeFlow, succEdge)
625
for r in succEdge.routes:
626
print(r)
627
assert totalEdgeFlow == succEdge.flow
628
assert vertex == self._source or vertex == self._sink or flowSum == 0
629
630
def calcRoutes(self, allowBackward=True):
631
for limitSource, limitSink in ((True, True), (True, False), (False, True), (False, False)):
632
pathFound = True
633
while pathFound:
634
for vertex in self._vertices:
635
vertex.reset()
636
pathFound = self.findPath(self._source, self._source, limitSource, limitSink, allowBackward)
637
if not pathFound and PULL_FLOW and not limitSource and not limitSink:
638
for i, edge in enumerate(sorted(self._edges.values())):
639
if DEBUG and options.verbose and i > 0 and i % 100 == 0:
640
print("pullFlow %.2f%%" % (100 * i / len(self._edges)))
641
if edge.startCapacity < sys.maxsize:
642
while (edge.flow < edge.capacity and
643
self.pullFlow(edge, limitSource, limitSink, allowBackward)):
644
pathFound = True
645
if DEBUG:
646
totalDetFlow = 0
647
for edge in self._edges.values():
648
if edge.startCapacity < sys.maxsize:
649
totalDetFlow += edge.flow
650
print("detFlow", totalDetFlow)
651
if DEBUG:
652
self.testFlowInvariants()
653
self.consolidateRoutes()
654
self.testFlowInvariants()
655
656
def consolidateRoutes(self):
657
for edge in self._source.outEdges:
658
routeByEdges = {}
659
for route in edge.routes:
660
key = tuple([e.label for e in route.edges if e.kind == "real"])
661
if key in routeByEdges:
662
routeByEdges[key].frequency += route.frequency
663
for e in route.edges[1:]:
664
e.routes.remove(route)
665
elif route.frequency > 0:
666
routeByEdges[key] = route
667
edge.routes = sorted(routeByEdges.values())
668
669
def applyRouteRestrictions(self):
670
removed = False
671
deleteRoute = []
672
for edge in self._source.outEdges:
673
for route in edge.routes:
674
key = tuple([e.label for e in route.edges if e.kind == "real"])
675
restriction = self._routeRestriction.get(key,
676
self._routeRestriction.get((key[0], key[-1]), sys.maxsize))
677
surplus = route.frequency - restriction
678
if surplus > 0:
679
if DEBUG:
680
print("route '%s' surplus=%s" % (" ".join(key), surplus))
681
removed = True
682
for e in route.edges:
683
e.flow -= surplus
684
for e in route.edges[1:]:
685
if restriction == 0:
686
e.routes.remove(route)
687
if restriction == 0:
688
deleteRoute.append(route)
689
else:
690
route.frequency = restriction
691
if deleteRoute:
692
edge.routes = [r for r in edge.routes if r not in deleteRoute]
693
if DEBUG:
694
self.testFlowInvariants()
695
return removed
696
697
def writeRoutes(self, routeOut, suffix=""):
698
totalFlow = 0
699
for edge in self._source.outEdges:
700
totalFlow += edge.flow
701
targetCount = defaultdict(lambda: 0)
702
for route in edge.routes:
703
if routeOut is None:
704
print(route)
705
continue
706
firstReal = ''
707
lastReal = None
708
routeString = ''
709
for redge in route.edges:
710
if redge.kind == "real":
711
if options.lanebased:
712
routeString += redge.label[:redge.label.rfind("_")] + " "
713
else:
714
routeString += redge.label + " "
715
if firstReal == '':
716
firstReal = redge.label
717
lastReal = redge
718
assert firstReal != '' and lastReal is not None
719
index = "" if targetCount[lastReal] == 0 else ".%s" % targetCount[lastReal]
720
targetCount[lastReal] += 1
721
route.routeID = "%s_%s%s%s" % (firstReal, lastReal.label, index, suffix)
722
print(' <route id="%s" edges="%s"/>' % (
723
route.routeID, routeString.strip()), file=routeOut)
724
if routeOut is None:
725
print("total flow:", totalFlow)
726
727
def writeEmitters(self, emitOut, begin=0, end=3600, suffix=""):
728
if not emitOut:
729
return
730
totalFlow = 0
731
numSources = 0
732
unusedSources = []
733
for srcEdge in self._source.outEdges:
734
if len(srcEdge.routes) == 0:
735
unusedSources.append(srcEdge.target.outEdges[0].label)
736
continue
737
assert len(srcEdge.target.outEdges) == 1
738
totalFlow += srcEdge.flow
739
numSources += 1
740
edge = srcEdge.target.outEdges[0]
741
if options.random:
742
ids = " ".join(r.routeID for r in srcEdge.routes)
743
probs = " ".join([str(route.frequency)
744
for route in srcEdge.routes])
745
print(' <flow id="%s%s" %s number="%s" begin="%s" end="%s">' %
746
(edge.label, suffix, options.params, int(srcEdge.flow), begin, end), file=emitOut)
747
print(' <routeDistribution routes="%s" probabilities="%s"/>' % (ids, probs), file=emitOut)
748
print(' </flow>', file=emitOut)
749
else:
750
for route in srcEdge.routes:
751
via = ""
752
if options.viadetectors:
753
realEdges = [e for e in route.edges if e.kind == "real"]
754
# exclude detectors on 'from' and 'to' edge
755
detEdges = [e.label for e in realEdges[1:-1] if e.getDetFlow() > 0]
756
# avoid duplicate via-edges
757
viaEdges = []
758
for e in detEdges:
759
if not viaEdges or viaEdges[-1] != e:
760
viaEdges.append(e)
761
if viaEdges:
762
via = ' via="%s"' % " ".join(viaEdges)
763
if options.pedestrians:
764
print(' <personFlow id="%s" %s number="%s" begin="%s" end="%s">' %
765
(route.routeID, options.params, int(route.frequency), begin, end), file=emitOut)
766
print(' <walk route="%s"/>' % route.routeID, file=emitOut)
767
print(' </personFlow>', file=emitOut)
768
else:
769
print(' <flow id="%s" %s route="%s" number="%s" begin="%s" end="%s"%s/>' %
770
(route.routeID, options.params, route.routeID,
771
int(route.frequency), begin, end, via), file=emitOut)
772
773
if options.verbose:
774
print("Writing %s vehicles from %s sources between time %s and %s (minutes)" % (
775
totalFlow, numSources, int(begin / 60), int(end / 60)))
776
if len(unusedSources) > 0:
777
print(" unused sources:", " ".join(unusedSources))
778
779
for s in self._sink.inEdges:
780
queue = [s]
781
s.isOnSinkPath = True
782
while queue:
783
queue.pop(0)
784
785
def writeFlowPOIs(self, poiOut, suffix=""):
786
if not poiOut:
787
return
788
for edge in self._edges.values():
789
color = "0,0,1"
790
for src in edge.source.inEdges:
791
if src.source == self._source:
792
color = "0,1,0"
793
break
794
for sink in edge.target.outEdges:
795
if sink.target == self._sink:
796
color = "1," + color[2] + ",0"
797
break
798
if edge.flow == edge.startCapacity:
799
color = ".5,.5,.5"
800
cap = ":c" + str(edge.startCapacity)
801
if edge.startCapacity == sys.maxsize:
802
cap = ""
803
if edge.isOnSourcePath:
804
cap += "-source"
805
if edge.isOnSinkPath:
806
cap += "-sink"
807
lane = edge.label if options.lanebased else edge.label + "_0"
808
print(' <poi id="%s_f%s%s%s" color="%s" lane="%s" pos="%s"/>' % (
809
edge.label, edge.flow, cap, suffix, color, lane, random.random() * edge.length), file=poiOut)
810
811
812
# The class for parsing the XML and CSV input files. The data parsed is
813
# written into the net. All members are "private".
814
class NetDetectorFlowReader(handler.ContentHandler):
815
816
def __init__(self, net):
817
self._net = net
818
self._edge = ''
819
self._lane2edge = {}
820
self._detReader = None
821
822
def startElement(self, name, attrs):
823
if name == 'edge' and ('function' not in attrs or attrs['function'] != 'internal'):
824
self._edge = attrs['id']
825
if not options.lanebased:
826
self._net.addIsolatedRealEdge(attrs['id'])
827
elif name == 'connection':
828
fromEdgeID = attrs['from']
829
if fromEdgeID[0] != ":":
830
toEdgeID = attrs['to']
831
if options.lanebased:
832
fromEdgeID += "_" + attrs["fromLane"]
833
toEdgeID += "_" + attrs["toLane"]
834
v = self._net.getEdge(fromEdgeID).target
835
eID = fromEdgeID + "->" + toEdgeID
836
for e in v.outEdges:
837
if e.label == eID:
838
return
839
newEdge = Edge(eID, v, self._net.getEdge(toEdgeID).source, "junction", attrs['dir'])
840
self._net.addEdge(newEdge)
841
elif name == 'lane' and self._edge != '':
842
if options.lanebased:
843
self._net.addIsolatedRealEdge(attrs['id'])
844
self._edge = attrs['id']
845
self._lane2edge[attrs['id']] = self._edge
846
edgeObj = self._net.getEdge(self._edge)
847
edgeObj.maxSpeed = max(edgeObj.maxSpeed, float(attrs['speed']))
848
edgeObj.length = float(attrs['length'])
849
if (options.vclass is None or
850
options.vclass in get_allowed(attrs.get('allow'), attrs.get('disallow'))):
851
edgeObj.numLanes += 1
852
853
def endElement(self, name):
854
if name == 'edge':
855
self._edge = ''
856
857
def readDetectors(self, detFile):
858
self._detReader = detector.DetectorReader(detFile, self._lane2edge)
859
for edge, detGroups in self._detReader._edge2DetData.items():
860
for group in detGroups:
861
if group.isValid:
862
self._net.getEdge(edge).detGroup.append(group)
863
sources = set()
864
sinks = set()
865
for det in sumolib.xml.parse(detFile, ["detectorDefinition", "e1Detector"]):
866
if hasattr(det, "type"):
867
if det.type == "source":
868
if options.lanebased:
869
sources.add(det.lane)
870
else:
871
sources.add(det.lane[:det.lane.rfind("_")])
872
if det.type == "sink":
873
if options.lanebased:
874
sinks.add(det.lane)
875
else:
876
sinks.add(det.lane[:det.lane.rfind("_")])
877
return sources, sinks
878
879
def readFlows(self, flowFile, t=None, tMax=None):
880
if t is None:
881
return self._detReader.readFlows(flowFile, flow=options.flowcol)
882
else:
883
return self._detReader.readFlows(flowFile, flow=options.flowcol, time="Time", timeVal=t, timeMax=tMax)
884
885
def clearFlows(self):
886
self._detReader.clearFlows()
887
888
def readSyntheticFlows(self, start=None, end=None):
889
if options.syntheticflowfile is None:
890
return
891
if start is not None:
892
times = [float(t) / 100. for t in options.timeline.split(",")]
893
factor = sum([times[t] for t in range(start // 60, end // 60)])
894
else:
895
factor = 1.
896
for f in options.syntheticflowfile.split(","):
897
for line in open(f):
898
flow, edge = line.split()
899
edgeObj = self._net.getEdge(edge)
900
groups = self._detReader.getEdgeDetGroups(edge)
901
if len(groups) == 0:
902
self._detReader.addDetector(edge, edgeObj.length / 2, edge)
903
self._net.getEdge(edge).detGroup.append(self._detReader.getEdgeDetGroups(edge)[0])
904
self._detReader.addFlow(edge, int(float(flow) * factor))
905
906
907
def addFlowFile(option, opt_str, value, parser):
908
if not getattr(parser.values, option.dest, None):
909
setattr(parser.values, option.dest, [])
910
fileList = getattr(parser.values, option.dest)
911
fileList.append(value)
912
index = 0
913
while index < len(parser.rargs) and not parser.rargs[index].startswith("-"):
914
index += 1
915
fileList.extend(parser.rargs[0:index])
916
parser.rargs = parser.rargs[index:]
917
918
919
parser = ArgumentParser()
920
parser.add_argument("-n", "--net-file", dest="netfile", category="input", type=ArgumentParser.net_file,
921
help="read SUMO network from FILE (mandatory)", metavar="FILE")
922
parser.add_argument("-d", "--detector-file", dest="detfile", category="input", type=ArgumentParser.additional_file,
923
help="read detectors from FILE (mandatory)", metavar="FILE")
924
parser.add_argument("--revalidate-detectors", action="store_true", dest="revalidate",
925
default=False, help="ignore source and sink information in detector file")
926
parser.add_argument("-f", "--detector-flow-files", dest="flowfiles", type=ArgumentParser.file,
927
nargs="+", category="input",
928
help="read detector flows from FILE(s) (mandatory)", metavar="FILE")
929
parser.add_argument("--flow-column", dest="flowcol", default="qPKW", type=str,
930
help="which column contains flows", metavar="STRING")
931
parser.add_argument("-o", "--routes-output", dest="routefile", category="output", type=ArgumentParser.route_file,
932
help="write routes to FILE", metavar="FILE")
933
parser.add_argument("-e", "--emitters-output", dest="emitfile", category="output", type=ArgumentParser.file,
934
help="write emitters to FILE and create files per emitter (needs -o)", metavar="FILE")
935
parser.add_argument("-y", "--params", help="vehicle / flow params to use (vType, departPos etc.)", type=str,
936
default='departSpeed="max" departPos="last" departLane="best"', metavar="STRING")
937
parser.add_argument("-t", "--trimmed-output", dest="trimfile", category="output", type=ArgumentParser.file,
938
help="write edges of trimmed network to FILE", metavar="FILE")
939
parser.add_argument("-p", "--flow-poi-output", dest="flowpoifile", category="output", type=ArgumentParser.file,
940
help="write resulting flows as SUMO POIs to FILE", metavar="FILE")
941
parser.add_argument("--source-sink-output", dest="source_sink_output", category="output", type=ArgumentParser.file,
942
help="write sources and sinks in detector format to FILE", metavar="FILE")
943
parser.add_argument("-m", "--min-speed", type=float, dest="minspeed",
944
default=0.0, help="only consider edges where the fastest lane allows at least this " +
945
"maxspeed (m/s)")
946
parser.add_argument("-M", "--max-flow", type=int, dest="maxflow",
947
help="limit the number of vehicles per lane and hour to this value")
948
parser.add_argument("--max-turn-flow", type=int, dest="maxturnflow",
949
help="limit the number of vehicles per turn-around connection and hour to this value")
950
parser.add_argument("-r", "--flow-restrictions", dest="restrictionfile", nargs="+", type=ArgumentParser.file,
951
help="read edge and route restrictions from FILEs (each line starts with '<maxHourlyFlow> ' " +
952
"followed by <edgeID> or '<originEdgeID> <destEdgeID>' or '<e1> <e2> ... <en>')",
953
metavar="FILE+")
954
parser.add_argument("-s", "--synthetic-flows", dest="syntheticflowfile", type=ArgumentParser.file,
955
help="read artificial detector values from FILE (lines of the form '<dailyFlow> <edgeID>')",
956
metavar="FILE")
957
parser.add_argument("--timeline", default=("0.9,0.5,0.2,0.2,0.5,1.3,7.0,9.3,6.7,4.2,4.0,3.8," +
958
"4.1,4.6,5.0,6.7,9.6,9.2,7.1,4.8,3.5,2.7,2.2,1.9"), type=str,
959
help="use time line for artificial detector values")
960
parser.add_argument("-D", "--keep-det", action="store_true", dest="keepdet",
961
default=False, help='keep edges with detectors when deleting "slow" edges')
962
parser.add_argument("-z", "--respect-zero", action="store_true", dest="respectzero",
963
default=False, help="respect detectors without data (or with permanent zero) with zero flow")
964
parser.add_argument("-l", "--lane-based", action="store_true", dest="lanebased",
965
default=False, help="do not aggregate detector data and connections to edges")
966
parser.add_argument("-i", "--interval", type=ArgumentParser.time, help="aggregation interval in minutes")
967
parser.add_argument("-b", "--begin", type=ArgumentParser.time, help="begin time in minutes")
968
parser.add_argument("--pedestrians", action="store_true",
969
default=False, help="write pedestrian flows instead of vehicles flows")
970
parser.add_argument("--limit", type=int, help="limit the amount of flow assigned in a single step")
971
parser.add_argument("--vclass", help="only consider lanes that allow the given vehicle class")
972
parser.add_argument("-q", "--quiet", action="store_true", dest="quiet",
973
default=False, help="suppress warnings")
974
parser.add_argument("--random", action="store_true", dest="random",
975
default=False, help="write route distributions instead of separate flows")
976
parser.add_argument("--via-detectors", action="store_true", dest="viadetectors",
977
default=False, help="set used detectors as via-edges for generated flows")
978
parser.add_argument("-v", "--verbose", action="store_true", dest="verbose",
979
default=False, help="tell me what you are doing")
980
parser.add_argument("--debug", action="store_true", default=False, help="tell me what you are doing in high detail")
981
options = parser.parse_args()
982
if not options.netfile or not options.detfile or not options.flowfiles:
983
parser.print_help()
984
sys.exit()
985
if options.emitfile and not options.routefile:
986
parser.print_help()
987
sys.exit()
988
if (options.restrictionfile is not None or options.maxflow is not None) and options.interval is None:
989
print("Restrictions need interval length")
990
parser.print_help()
991
sys.exit()
992
993
if options.pedestrians:
994
if options.random:
995
print("Pedestrian output does not support option 'random'")
996
sys.exit()
997
# filtering out params that are not suitable for persons
998
params = options.params.split()
999
params = [p for p in params if "departSpeed" not in p and "departPos" not in
1000
p and "departLane" not in p]
1001
options.params = ' '.join(params)
1002
1003
DEBUG = options.debug
1004
saxParser = make_parser()
1005
if options.verbose:
1006
print("Reading net")
1007
Edge.lanebased = options.lanebased
1008
net = Net()
1009
reader = NetDetectorFlowReader(net)
1010
saxParser.setContentHandler(reader)
1011
saxParser.parse(options.netfile)
1012
if options.verbose:
1013
print(len(net._edges), "edges read")
1014
print("Reading detectors")
1015
sources, sinks = reader.readDetectors(options.detfile)
1016
if options.revalidate:
1017
sources = sinks = []
1018
if net.detectSourceSink(sources, sinks):
1019
routeOut = None
1020
if options.routefile:
1021
routeOut = open(options.routefile, 'w')
1022
print("<routes>", file=routeOut)
1023
emitOut = None
1024
if options.emitfile:
1025
emitOut = open(options.emitfile, 'w')
1026
print("<additional>", file=emitOut)
1027
poiOut = None
1028
if options.flowpoifile:
1029
poiOut = open(options.flowpoifile, 'w')
1030
print("<pois>", file=poiOut)
1031
if options.interval:
1032
tMin = None
1033
tMax = None
1034
for flow in options.flowfiles:
1035
tMin, tMax = reader._detReader.findTimes(flow, tMin, tMax)
1036
if tMin is None:
1037
print("No flows in '%s'" % flow)
1038
sys.exit(1)
1039
if options.begin is not None and options.begin > tMin:
1040
tMin = options.begin
1041
if options.verbose:
1042
print("Reading flows between %s and %s" % (tMin, tMax))
1043
start = int(tMin - (tMin % options.interval))
1044
while start <= tMax:
1045
suffix = ".%s.%s" % (options.flowcol, start)
1046
for flow in options.flowfiles:
1047
haveFlows = reader.readFlows(
1048
flow, start, start + options.interval)
1049
if haveFlows:
1050
reader.readSyntheticFlows(start, start + options.interval)
1051
if options.verbose:
1052
print("Calculating routes")
1053
net.initNet()
1054
net.calcRoutes()
1055
# run again (in forward only mode) if restricted routes were removed
1056
if net.applyRouteRestrictions():
1057
net.calcRoutes(False)
1058
net.writeRoutes(routeOut, suffix)
1059
net.writeEmitters(
1060
emitOut, 60 * start, 60 * (start + options.interval), suffix)
1061
net.writeFlowPOIs(poiOut, suffix)
1062
else:
1063
if options.verbose:
1064
print("No flows found")
1065
reader.clearFlows()
1066
start += options.interval
1067
else:
1068
if options.verbose:
1069
print("Reading flows")
1070
for flow in options.flowfiles:
1071
reader.readFlows(flow)
1072
reader.readSyntheticFlows()
1073
if options.verbose:
1074
print("Calculating routes")
1075
net.initNet()
1076
net.calcRoutes()
1077
# run again (in forward only mode) if restricted routes were removed
1078
if net.applyRouteRestrictions():
1079
net.calcRoutes(False)
1080
net.writeRoutes(routeOut, "." + options.flowcol)
1081
net.writeEmitters(emitOut, suffix=options.flowcol)
1082
net.writeFlowPOIs(poiOut)
1083
if routeOut:
1084
print("</routes>", file=routeOut)
1085
routeOut.close()
1086
if emitOut:
1087
print("</additional>", file=emitOut)
1088
emitOut.close()
1089
if poiOut:
1090
print("</pois>", file=poiOut)
1091
poiOut.close()
1092
1093