Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/purgatory/routeChoices.py
169674 views
1
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
2
# Copyright (C) 2007-2025 German Aerospace Center (DLR) and others.
3
# This program and the accompanying materials are made available under the
4
# terms of the Eclipse Public License 2.0 which is available at
5
# https://www.eclipse.org/legal/epl-2.0/
6
# This Source Code may also be made available under the following Secondary
7
# Licenses when the conditions for such availability set forth in the Eclipse
8
# Public License 2.0 are satisfied: GNU General Public License, version 2
9
# or later which is available at
10
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
11
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
12
13
# @file routeChoices.py
14
# @author Evamarie Wiessner
15
# @author Yun-Pang Floetteroed
16
# @author Michael Behrisch
17
# @date 2007-02-27
18
19
"""
20
This script is to calculate the route choice probabilities based on different methods.
21
- Gawron
22
- step-size (TBD)
23
- ......
24
"""
25
from __future__ import absolute_import
26
from __future__ import print_function
27
28
import os
29
import random
30
import math
31
from xml.sax import handler
32
from xml.sax import parse
33
34
35
class Vehicle:
36
37
def __init__(self, label, depart, departlane='first', departpos='base', departspeed=0):
38
self.label = label
39
self.CO_abs = 0.
40
self.CO2_abs = 0.
41
self.HC_abs = 0.
42
self.PMx_abs = 0.
43
self.NOx_abs = 0.
44
self.fuel_abs = 0.
45
self.routesList = []
46
# self.speed = 0.
47
self.depart = float(depart)
48
self.departlane = departlane
49
self.departpos = departpos
50
self.departspeed = departspeed
51
self.selectedRoute = None
52
53
54
class Edge:
55
56
def __init__(self, label):
57
self.label = label
58
self.length = 0.
59
self.freespeed = 0.
60
self.CO_abs = 0.
61
self.CO2_abs = 0.
62
self.HC_abs = 0.
63
self.PMx_abs = 0.
64
self.NOx_abs = 0.
65
self.fuel_abs = 0.
66
self.traveltime = 0.
67
self.CO_perVeh = 0.
68
self.CO2_perVeh = 0.
69
self.HC_perVeh = 0.
70
self.PMx_perVeh = 0.
71
self.NOx_perVeh = 0.
72
self.fuel_perVeh = 0.
73
# only one veh on the edge
74
self.fuel_perVeh_default = 0.
75
self.CO_perVeh_default = 0.
76
self.CO2_perVeh_default = 0.
77
self.HC_perVeh_default = 0.
78
self.PMx_perVeh_default = 0.
79
self.NOx_perVeh_default = 0.
80
self.fuel_perVeh_default = 0.
81
self.freetraveltime = 0.
82
83
84
pathNum = 0
85
86
87
class Route:
88
89
def __init__(self, edges):
90
global pathNum
91
self.label = "%s" % pathNum
92
pathNum += 1
93
self.edges = edges
94
# self.ex_probability = None
95
self.probability = 0.
96
self.selected = False
97
self.ex_cost = 0.
98
self.act_cost = 0.
99
100
101
class netReader(handler.ContentHandler):
102
103
def __init__(self, edgesList, edgesMap):
104
self._edgesList = edgesList
105
self._edgesMap = edgesMap
106
self._edgeObj = None
107
108
def startElement(self, name, attrs):
109
if name == 'edge' and 'function' not in attrs:
110
if attrs['id'] not in self._edgesMap:
111
self._edgeObj = Edge(attrs['id'])
112
self._edgesList.append(self._edgeObj)
113
self._edgesMap[attrs['id']] = self._edgeObj
114
if self._edgeObj and name == 'lane':
115
self._edgeObj.length = float(attrs['length'])
116
self._edgeObj.freespeed = float(attrs['speed'])
117
self._edgeObj.freetraveltime = self._edgeObj.length / \
118
self._edgeObj.freespeed
119
120
def endElement(self, name):
121
if name == 'edge':
122
self._edgeObj = None
123
124
125
class addweightsReader(handler.ContentHandler):
126
127
def __init__(self, edgesList, edgesMap):
128
self._edgesList = edgesList
129
self._edgesMap = edgesMap
130
self._edgObj = None
131
132
def startElement(self, name, attrs):
133
if name == 'edge':
134
if attrs['id'] in self._edgesMap:
135
self._edgeObj = self._edgesMap[attrs['id']]
136
if 'traveltime' in attrs:
137
self._edgeObj.freetraveltime = float(attrs['traveltime'])
138
if 'CO_perVeh' in attrs:
139
self._edgeObj.CO_perVeh_default = float(attrs['CO_perVeh'])
140
if 'CO2_perVeh' in attrs:
141
self._edgeObj.CO2_perVeh_default = float(attrs['CO2_perVeh'])
142
if 'HC_perVeh' in attrs:
143
self._edgeObj.HC_perVeh_default = float(attrs['HC_perVeh'])
144
if 'PMx_perVeh' in attrs:
145
self._edgeObj.PMx_perVeh_default = float(attrs['PMx_perVeh'])
146
if 'NOx_perVeh' in attrs:
147
self._edgeObj.NOx_perVeh_default = float(attrs['NOx_perVeh'])
148
if 'fuel_perVeh' in attrs:
149
self._edgeObj.fuel_perVeh_default = float(attrs['fuel_perVeh'])
150
if 'fuel_abs' in attrs:
151
self._edgeObj.fuel_abs_default = float(attrs['fuel_abs'])
152
if 'NOx_abs' in attrs:
153
self._edgeObj.NOx_abs_default = float(attrs['NOx_abs'])
154
if 'PMx_abs' in attrs:
155
self._edgeObj.PMx_abs_default = float(attrs['PMx_abs'])
156
if 'HC_abs' in attrs:
157
self._edgeObj.HC_abs_default = float(attrs['HC_abs'])
158
if 'CO2_abs' in attrs:
159
self._edgeObj.CO2_abs_default = float(attrs['CO2_abs'])
160
if 'CO_abs' in attrs:
161
self._edgeObj.CO_abs_default = float(attrs['CO_abs'])
162
163
164
class routeReader(handler.ContentHandler):
165
166
def __init__(self, vehList, vehMap):
167
self._vehList = vehList
168
self._vehMap = vehMap
169
self._vehObj = None
170
self._routObj = None
171
172
def startElement(self, name, attrs):
173
if name == 'vehicle':
174
if ('departPos' in attrs):
175
self._vehObj = Vehicle(attrs['id'], attrs['depart'], attrs[
176
'departLane'], attrs['departPos'], attrs['departSpeed'])
177
else:
178
self._vehObj = Vehicle(attrs['id'], attrs['depart'])
179
self._vehMap[attrs['id']] = self._vehObj
180
self._vehList.append(self._vehObj)
181
182
if self._vehObj and name == 'route':
183
edgesList = attrs['edges'].split(' ')
184
self._routObj = Route(" ".join(edgesList))
185
self._vehObj.routesList.append(self._routObj)
186
187
def endElement(self, name):
188
if name == 'vehicle':
189
self._vehObj = None
190
self._routObj = None
191
192
193
class vehrouteReader(handler.ContentHandler):
194
195
def __init__(self, vehList, vehMap, edgesMap, fout, foutrout, ecoMeasure, alpha, beta):
196
self._vehList = vehList
197
self._vehMap = vehMap
198
self._edgesMap = edgesMap
199
self._fout = fout
200
self._foutrout = foutrout
201
self._ecoMeasure = ecoMeasure
202
self._newroutesList = []
203
self._alpha = alpha
204
self._beta = beta
205
self._vehObj = None
206
self._routObj = None
207
self._selected = None
208
self._currentSelected = None
209
self._count = 0
210
self._existed = False
211
212
def startElement(self, name, attrs):
213
if name == 'vehicle':
214
self._vehObj = self._vehMap[attrs['id']]
215
216
if self._vehObj and name == 'routeDistribution':
217
self._currentSelected = attrs['last']
218
219
if self._vehObj and name == 'route':
220
if self._count == int(self._currentSelected):
221
self._vehObj.selectedRouteEdges = attrs['edges']
222
self._count += 1
223
for r in self._vehObj.routesList:
224
if r.edges == attrs['edges']:
225
self._existed = True
226
self._routObj = r
227
break
228
if not self._existed:
229
self._routObj = Route(attrs['edges'])
230
self._vehObj.routesList.append(self._routObj)
231
232
if 'probability' in attrs:
233
self._routObj.probability = float(attrs['probability'])
234
if self._routObj.probability == 0.0:
235
# check with Micha if there is a better way to avoid the
236
# prob. = 0.
237
self._routObj.probability = 1.02208127529e-16
238
if 'cost' in attrs:
239
self._routObj.ex_cost = float(attrs['cost'])
240
for e in self._routObj.edges.split(' '):
241
eObj = self._edgesMap[e]
242
if self._ecoMeasure != 'fuel' and eObj.traveltime == 0.:
243
self._routObj.act_cost += eObj.freetraveltime
244
elif self._ecoMeasure != 'fuel' and eObj.traveltime > 0.:
245
self._routObj.act_cost += eObj.traveltime
246
elif self._ecoMeasure == 'fuel' and eObj.fuel_perVeh == 0.:
247
self._routObj.act_cost += eObj.fuel_perVeh_default
248
elif self._ecoMeasure == 'fuel' and eObj.fuel_perVeh > 0.:
249
self._routObj.act_cost += eObj.fuel_perVeh
250
if self._routObj.ex_cost == 0.:
251
self._routObj.ex_cost = self._routObj.act_cost
252
253
def endElement(self, name):
254
if name == 'vehicle':
255
# if len(self._vehObj.routesList) == 1:
256
# self._vehObj.routesList[0].probability = 1.
257
# for the routes which are from the sumo's rou.alt.xml file
258
for r in self._vehObj.routesList:
259
if r.act_cost == 0.:
260
for e in r.edges.split(' '):
261
eObj = self._edgesMap[e]
262
if self._ecoMeasure != 'fuel' and eObj.traveltime == 0.:
263
r.act_cost += eObj.freetraveltime
264
elif self._ecoMeasure != 'fuel' and eObj.traveltime > 0.:
265
r.act_cost += eObj.traveltime
266
elif self._ecoMeasure == 'fuel' and eObj.fuel_perVeh == 0.:
267
r.act_cost += eObj.fuel_perVeh_default
268
elif self._ecoMeasure == 'fuel' and eObj.fuel_perVeh > 0.:
269
r.act_cost += eObj.fuel_perVeh
270
if r.ex_cost == 0.:
271
r.ex_cost = r.act_cost
272
# calcuate the probabilites for the new routes
273
if not r.probability:
274
r.probability = 1. / float(len(self._vehObj.routesList))
275
print('new probability for route', r.label,
276
'for veh', self._vehObj.label)
277
self._newroutesList.append(r)
278
279
# adjust the probabilites of the existing routes due to the new
280
# routes
281
if len(self._newroutesList) > 0:
282
addProb = 0.
283
origProbSum = 0.
284
for r in self._vehObj.routesList:
285
if r in self._newroutesList:
286
addProb += r.probability
287
else:
288
origProbSum += r.probability
289
for r in self._vehObj.routesList:
290
if r not in self._newroutesList:
291
r.probability = r.probability / \
292
origProbSum * (1. - addProb)
293
294
# update the costs of routes not used by the driver
295
for r in self._vehObj.routesList:
296
if r.edges != self._vehObj.selectedRouteEdges:
297
r.act_cost = self._beta * r.act_cost + \
298
(1. - self._beta) * r.ex_cost
299
300
# calcuate the route choice probabilities based on Gawron
301
# todo: add "one used route to all routes"
302
for r1 in self._vehObj.routesList:
303
for r2 in self._vehObj.routesList:
304
if r1.label != r2.label:
305
gawron(r1, r2, self._alpha)
306
307
# decide which route will be selected
308
randProb = random.random()
309
if len(self._vehObj.routesList) == 1:
310
self._vehObj.routesList[0].probability = 1.
311
self._selected = 0
312
else:
313
cumulatedProbs = 0.
314
for i, r in enumerate(self._vehObj.routesList):
315
cumulatedProbs += r.probability
316
if cumulatedProbs >= randProb:
317
self._selected = i
318
break
319
320
# generate the *.rou.xml
321
self._foutrout.write(' <vehicle id="%s" depart="%.2f" departLane="%s" departPos="%s" departSpeed="%s">\n'
322
% (self._vehObj.label, self._vehObj.depart, self._vehObj.departlane,
323
self._vehObj.departpos, self._vehObj.departspeed))
324
self._foutrout.write(
325
' <route edges="%s"/>\n' % self._vehObj.routesList[self._selected].edges)
326
self._foutrout.write(' </vehicle> \n')
327
328
# generate the *.rou.alt.xml
329
self._fout.write(' <vehicle id="%s" depart="%.2f" departLane="%s" departPos="%s" departSpeed="%s">\n'
330
% (self._vehObj.label, self._vehObj.depart, self._vehObj.departlane,
331
self._vehObj.departpos, self._vehObj.departspeed))
332
self._fout.write(
333
' <routeDistribution last="%s">\n' % self._selected)
334
335
for route in self._vehObj.routesList:
336
self._fout.write(' <route cost="%.4f" probability="%s" edges="%s"/>\n' % (
337
route.act_cost, route.probability, route.edges))
338
self._fout.write(' </routeDistribution>\n')
339
self._fout.write(' </vehicle> \n')
340
341
self._newroutesList = []
342
self._vehObj = None
343
self._selected = None
344
self._currentSelected = None
345
self._count = 0
346
if name == 'route':
347
self._routObj = None
348
if (name == 'route-alternatives' or name == 'routes'):
349
self._fout.write('</route-alternatives>\n')
350
self._fout.close()
351
self._foutrout.write('</routes>\n')
352
self._foutrout.close()
353
354
355
class dumpsReader(handler.ContentHandler):
356
357
def __init__(self, edgesList, edgesMap):
358
self._edgesList = edgesList
359
self._edgeObj = None
360
self._edgesMap = edgesMap
361
362
def startElement(self, name, attrs):
363
if name == 'edge':
364
if attrs['id'] not in self._edgesMap:
365
self._edgeObj = Edge(attrs['id'])
366
self._edgesList.append(self._edgeObj)
367
self._edgesMap[attrs['id']] = self._edgeObj
368
else:
369
self._edgeObj = self._edgesMap[attrs['id']]
370
371
if 'traveltime' in attrs:
372
self._edgeObj.traveltime = float(attrs['traveltime'])
373
if 'CO_perVeh' in attrs:
374
self._edgeObj.CO_perVeh = float(attrs['CO_perVeh'])
375
if 'CO2_perVeh' in attrs:
376
self._edgeObj.CO2_perVeh = float(attrs['CO2_perVeh'])
377
if 'HC_perVeh' in attrs:
378
self._edgeObj.HC_perVeh = float(attrs['HC_perVeh'])
379
if 'PMx_perVeh' in attrs:
380
self._edgeObj.PMx_perVeh = float(attrs['PMx_perVeh'])
381
if 'NOx_perVeh' in attrs:
382
self._edgeObj.NOx_perVeh = float(attrs['NOx_perVeh'])
383
if 'fuel_perVeh' in attrs:
384
self._edgeObj.fuel_perVeh = float(attrs['fuel_perVeh'])
385
if 'fuel_abs' in attrs:
386
self._edgeObj.fuel_abs = float(attrs['fuel_abs'])
387
if 'NOx_abs' in attrs:
388
self._edgeObj.NOx_abs = float(attrs['NOx_abs'])
389
if 'PMx_abs' in attrs:
390
self._edgeObj.PMx_abs = float(attrs['PMx_abs'])
391
if 'HC_abs' in attrs:
392
self._edgeObj.HC_abs = float(attrs['HC_abs'])
393
if 'CO2_abs' in attrs:
394
self._edgeObj.CO2_abs = float(attrs['CO2_abs'])
395
if 'CO_abs' in attrs:
396
self._edgeObj.CO_abs = float(attrs['CO_abs'])
397
398
def endElement(self, name):
399
if name == 'edge':
400
self._edgeObj = None
401
402
403
def resetEdges(edgesMap):
404
for eid in edgesMap:
405
e = edgesMap[eid]
406
e.traveltime = 0.
407
e.CO_abs = 0.
408
e.CO2_abs = 0.
409
e.HC_abs = 0.
410
e.PMx_abs = 0.
411
e.NOx_abs = 0.
412
e.fuel_abs = 0.
413
e.CO_perVeh = 0.
414
e.CO2_perVeh = 0.
415
e.HC_perVeh = 0.
416
e.PMx_perVeh = 0.
417
e.NOx_perVeh = 0.
418
e.fuel_perVeh = 0.
419
420
421
# check with Micha
422
def getRouteChoices(edgesMap, dumpfile, routeAltfile, netfile, addWeightsfile, alpha, beta, step, ecoMeasure=None):
423
random.seed(42) # check with micha
424
edgesList = []
425
vehList = []
426
vehMap = {}
427
print('run getRouteChoices')
428
print('ecoMeasure:', ecoMeasure)
429
outputPath = os.path.abspath(routeAltfile)
430
outputPath = os.path.dirname(outputPath)
431
prefix = os.path.basename(routeAltfile)
432
# prefix = prefix[:prefix.find('.')]
433
prefix = prefix[:-12]
434
# print('outputPath:', outputPath)
435
print('prefix:', prefix)
436
outputAltfile = os.path.join(outputPath, prefix + '.rou.galt.xml')
437
outputRoufile = os.path.join(outputPath, prefix + '.grou.xml')
438
439
if len(edgesMap) == 0:
440
try:
441
print('parse network file')
442
parse(netfile, netReader(edgesList, edgesMap))
443
except AttributeError:
444
print("could not parse netfile: " + str(netfile))
445
try:
446
parse(addWeightsfile, addweightsReader(edgesList, edgesMap))
447
except AttributeError:
448
print("could not parse weights file: " + str(addWeightsfile))
449
else:
450
resetEdges(edgesMap)
451
452
fout = open(outputAltfile, 'w')
453
foutrout = open(outputRoufile, 'w')
454
fout.write('<?xml version="1.0"?>\n')
455
fout.write('<!--\n')
456
fout.write('route choices are generated with use of %s' %
457
os.path.join(os.getcwd(), 'routeChoices.py'))
458
fout.write('-->\n')
459
fout.write('<route-alternatives>\n')
460
foutrout.write('<?xml version="1.0"?>\n')
461
foutrout.write('<!--\n')
462
foutrout.write('route choices are generated with use of %s' %
463
os.path.join(os.getcwd(), 'routeChoices.py'))
464
foutrout.write('-->\n')
465
foutrout.write('<routes>')
466
467
print('parse dumpfile')
468
print(dumpfile)
469
parse(dumpfile, dumpsReader(edgesList, edgesMap))
470
# parse routeAltfile from SUMO
471
try:
472
print('parse routeAltfile:', routeAltfile)
473
parse(routeAltfile, routeReader(vehList, vehMap))
474
except IOError:
475
print('could not parse routeAltfile:', routeAltfile)
476
ex_outputAltFile = prefix[
477
:prefix.rfind('_')] + '_%03i' % (step - 1) + '.rou.galt.xml'
478
try:
479
print('parse routeAltfile from externalGawron: ', ex_outputAltFile)
480
parse(ex_outputAltFile, vehrouteReader(
481
vehList, vehMap, edgesMap, fout, foutrout, ecoMeasure, alpha, beta))
482
except IOError:
483
print('could not parse routeAltfile from externalGawron:', ex_outputAltFile)
484
return outputRoufile, edgesMap
485
486
487
def gawron(r1, r2, alpha):
488
a = alpha
489
delta = (r2.act_cost - r1.act_cost) / (r1.act_cost + r2.act_cost)
490
g = math.exp(a * delta / (1 - delta * delta))
491
ex_prob = r1.probability
492
r1.probability = (r1.probability * (r1.probability + r2.probability) *
493
g) / (r1.probability * g + r2.probability) # check together with Eva
494
r2.probability = ex_prob + r2.probability - r1.probability
495
496
497
def calFirstRouteProbs(dumpfile, sumoAltFile, addweights, ecoMeasure=None):
498
basename = sumoAltFile.split('_')[0]
499
outputAltFile = basename + "_001.rou.galt.xml"
500
outputRouFile = basename + "_001.rou.alt.xml"
501
edgesList = []
502
edgesMap = {}
503
vehList = []
504
vehMap = {}
505
# parse(netfile, netReader(edgesList, edgesMap))
506
parse(addweights, addweightsReader(edgesList, edgesMap))
507
parse(dumpfile, dumpsReader(edgesList, edgesMap))
508
parse(sumoAltFile, routeReader(vehList, vehMap))
509
510
fout = open(outputAltFile, 'w')
511
foutrout = open(outputRouFile, 'w')
512
fout.write('<?xml version="1.0"?>\n')
513
fout.write('<!--\n')
514
fout.write('route choices are generated with use of %s' %
515
os.path.join(os.getcwd(), 'routeChoices.py'))
516
fout.write('-->\n')
517
fout.write('<route-alternatives>\n')
518
foutrout.write('<?xml version="1.0"?>\n')
519
foutrout.write('<!--\n')
520
foutrout.write('route choices are generated with use of %s' %
521
os.path.join(os.getcwd(), 'routeChoices.py'))
522
foutrout.write('-->\n')
523
foutrout.write('<routes>')
524
525
for v in vehMap:
526
vehObj = vehMap[v]
527
for r in vehObj.routesList:
528
for e in r.edges.split(' '):
529
eObj = edgesMap[e]
530
if ecoMeasure != 'fuel' and eObj.traveltime == 0.:
531
r.act_cost += eObj.freetraveltime
532
r.ex_cost += eObj.freetraveltime
533
elif ecoMeasure != 'fuel' and eObj.traveltime > 0.:
534
r.act_cost += eObj.traveltime
535
r.ex_cost += eObj.freetraveltime
536
elif ecoMeasure == 'fuel' and eObj.fuel_perVeh == 0.:
537
r.act_cost += eObj.fuel_perVeh_default
538
r.ex_cost += eObj.fuel_perVeh_default
539
elif ecoMeasure == 'fuel' and eObj.fuel_perVeh > 0.:
540
r.act_cost += eObj.fuel_perVeh
541
r.ex_cost += eObj.fuel_perVeh
542
costSum = 0.
543
for r in vehObj.routesList:
544
costSum += r.ex_cost
545
for r in vehObj.routesList:
546
r.ex_probability = r.ex_cost / costSum
547
548
randProb = random.random()
549
selected = 0
550
if len(vehObj.routesList) > 1:
551
cumulatedProbs = 0.
552
for i, r in enumerate(vehObj.routesList):
553
cumulatedProbs += r.ex_probability
554
if cumulatedProbs >= randProb:
555
selected = i
556
break
557
558
# generate the *.rou.xml
559
foutrout.write(' <vehicle id="%s" depart="%.2f" departLane="%s" departPos="%s" departSpeed="%s">\n'
560
% (vehObj.label, vehObj.depart, vehObj.departlane, vehObj.departpos, vehObj.departspeed))
561
foutrout.write(
562
' <route edges="%s"/>\n' % vehObj.routesList[selected].edges)
563
foutrout.write(' </vehicle> \n')
564
565
# generate the *.rou.alt.xml
566
fout.write(' <vehicle id="%s" depart="%.2f" departLane="%s" departPos="%s" departSpeed="%s">\n'
567
% (vehObj.label, vehObj.depart, vehObj.departlane, vehObj.departpos, vehObj.departspeed))
568
fout.write(' <routeDistribution last="%s">\n' % selected)
569
570
for route in vehObj.routesList:
571
fout.write(' <route cost="%.4f" probability="%s" edges="%s"/>\n' %
572
(route.act_cost, route.ex_probability, route.edges))
573
fout.write(' </routeDistribution>\n')
574
fout.write(' </vehicle> \n')
575
fout.write('</route-alternatives>\n')
576
fout.close()
577
foutrout.write('</routes>\n')
578
foutrout.close()
579
580