Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/createVehTypeDistribution.py
169659 views
1
#!/usr/bin/env python
2
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
# Copyright (C) 2010-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 createVehTypeDistribution.py
15
# @author Mirko Barthauer (Technische Universitaet Braunschweig, Institut fuer Verkehr und Stadtbauwesen)
16
# @author Jakob Erdmann
17
# @author Michael Behrisch
18
# @date 2016-06-09
19
20
"""
21
Creates a vehicle type distribution with a number of representative car-following parameter sets.
22
Optional parameters can be viewed by using the --help switch.
23
Mandatory input:
24
path to config file - defines the car-following model parameter distributions for one single vehicle type distribution
25
26
In the config file, one line is used per vehicle type attribute. The syntax is:
27
nameOfAttribute; valueOfAttribute [; limits]
28
29
ValueOfAttribute can be a string, a scalar value or a distribution definition.
30
All parameters are floating point numbers.
31
Available distributions and their syntax are:
32
"normal(mu,sd)": Normal distribution with mean mu and standard deviation sd.
33
"lognormal(mu,sd)": Log-Normal distribution with mean mu and standard deviation sd.
34
"uniform(a,b)": Uniform distribution between a and b.
35
"gamma(alpha,beta)": Gamma distribution.
36
37
Limits are optional and defined as the allowed interval: e.g. "[0,1]" or "[3.5,5.0]".
38
By default, no negative values are accepted but have to be enabled by
39
a negative lower limit.
40
"""
41
42
import sys
43
import csv
44
import re
45
46
from sumolib.options import ArgumentParser
47
from sumolib.vehicletype import CreateVehTypeDistribution
48
49
50
def get_options(args=None):
51
ap = ArgumentParser()
52
ap.add_argument("configFile", category="input",
53
help="file path of the config file which defines the car-following parameter distributions")
54
ap.add_argument("-o", "--output-file", category="output", dest="outputFile", default="vTypeDistributions.add.xml",
55
help=("file path of the output file" +
56
"(if the file already exists, the script tries to insert the distribution node into it)"))
57
ap.add_argument("-n", "--name", dest="vehDistName", default="vehDist",
58
help="alphanumerical ID used for the created vehicle type distribution")
59
ap.add_argument("-s", "--size", type=int, default=100, dest="vehicleCount",
60
help="number of vTypes in the distribution")
61
ap.add_argument("-d", "--decimal-places", type=int, default=3, dest="decimalPlaces",
62
help="number of decimal places for numeric attribute values")
63
ap.add_argument("--resampling", type=int, default=100, dest="nrSamplingAttempts",
64
help="number of attempts to resample a value until it lies in the specified bounds")
65
ap.add_argument("--seed", type=int, help="random seed", default=42)
66
67
options = ap.parse_args(args)
68
return options
69
70
71
def readConfigFile(options):
72
filePath = options.configFile
73
result = dict()
74
floatRegex = [r'\s*(-?[0-9]+(\.[0-9]+)?)\s*']
75
distSyntaxes = {'normal': r'normal\(%s\)' % (",".join(2 * floatRegex)),
76
'lognormal': r'lognormal\(%s\)' % (",".join(2 * floatRegex)),
77
'normalCapped': r'normalCapped\(%s\)' % (",".join(4 * floatRegex)),
78
'uniform': r'uniform\(%s\)' % (",".join(2 * floatRegex)),
79
'gamma': r'gamma\(%s\)' % (",".join(2 * floatRegex))}
80
81
with open(filePath) as f:
82
reader = csv.reader(f, delimiter=';')
83
for row in reader:
84
attName = None
85
lowerLimit = 0
86
upperLimit = None
87
if len(row) >= 2:
88
if len(row[0].strip()) > 0:
89
attName = row[0].strip()
90
if attName == "param":
91
# this indicates that a parameter child-element is to be created for the vTypes
92
isParam = True
93
del row[0]
94
if len(row) < 2:
95
# a parameter needs a name and a value specification
96
continue
97
attName = row[0].strip()
98
else:
99
isParam = False
100
# check if attribute value matches given distribution
101
# syntax
102
attValue = row[1].strip()
103
distFound = False
104
distAttr = None
105
for distName, distSyntax in distSyntaxes.items():
106
items = re.findall(distSyntax, attValue)
107
distFound = len(items) > 0
108
if distFound: # found distribution
109
distPar1 = float(items[0][0])
110
distPar2 = float(items[0][2])
111
if distName == 'normal':
112
distAttr = {"mu": distPar1, "sd": distPar2}
113
if distName == 'lognormal':
114
distAttr = {"mu": distPar1, "sd": distPar2}
115
elif distName == 'normalCapped':
116
cutLow = float(items[0][4])
117
cutHigh = float(items[0][6])
118
distAttr = {
119
"mu": distPar1, "sd": distPar2, 'min': cutLow, 'max': cutHigh}
120
elif distName == 'uniform':
121
distAttr = {"a": distPar1, "b": distPar2}
122
elif distName == 'gamma':
123
distAttr = {
124
"alpha": distPar1, "beta": distPar2}
125
# can only have attValue if no distribution
126
attValue = None
127
break
128
129
if not distFound:
130
distName = None
131
distAttr = None
132
133
# get optional limits
134
limits = None
135
if len(row) == 3:
136
limitValue = row[2].strip()
137
items = re.findall(
138
r'\[\s*(-?[0-9]+(\.[0-9]+)?)\s*,\s*(-?[0-9]+(\.[0-9]+)?)\s*\]', limitValue)
139
if len(items) > 0:
140
lowerLimit = float(items[0][0])
141
upperLimit = float(items[0][2])
142
limits = (lowerLimit, upperLimit)
143
144
result[attName] = {
145
"name": attName,
146
"is_param": isParam,
147
"distribution": distName,
148
"distribution_params": distAttr,
149
"bounds": limits,
150
"attribute_value": attValue
151
}
152
return result
153
154
155
def main(options):
156
157
dist_creator = CreateVehTypeDistribution(seed=options.seed,
158
size=options.vehicleCount,
159
name=options.vehDistName,
160
resampling=options.nrSamplingAttempts,
161
decimal_places=options.decimalPlaces)
162
163
params = readConfigFile(options)
164
for param_dict in params.values():
165
dist_creator.add_attribute(param_dict)
166
if ("speedFactor" in params) and ("speedDev" not in params):
167
dist_creator.add_attribute({"name": "speedDev", "is_param": False, "distribution": None,
168
"distribution_params": None, "bounds": None, "attribute_value": "0"})
169
print("Warning: Setting speedDev to 0 because only speedFactor is given.", file=sys.stderr)
170
171
dist_creator.to_xml(options.outputFile)
172
173
174
if __name__ == "__main__":
175
try:
176
main(get_options())
177
except ValueError as e:
178
sys.exit(e)
179
180