"""Contains the base network class."""
from flow.core.params import InitialConfig
from flow.core.params import TrafficLightParams
from flow.core.params import SumoCarFollowingParams
from flow.core.params import SumoLaneChangeParams
import time
import xml.etree.ElementTree as ElementTree
from lxml import etree
from collections import defaultdict
# default sumo probability value TODO (ak): remove
DEFAULT_PROBABILITY = 0
# default sumo vehicle length value (in meters) TODO (ak): remove
DEFAULT_LENGTH = 5
# default sumo vehicle class class TODO (ak): remove
DEFAULT_VCLASS = 0
[docs]class Network(object):
"""Base network class.
Initializes a new network. Networks are used to specify features of
a network, including the positions of nodes, properties of the edges
and junctions connecting these nodes, properties of vehicles and
traffic lights, and other features as well. These features can later be
acquired from this class via a plethora of get methods (see
documentation).
This class uses network specific features to generate the necessary network
configuration files needed to initialize a simulation instance. The methods
of this class are called by the base network class.
The network files can be created in one of three ways:
* Custom networks can be generated by defining the properties of the
network's directed graph. This is done by defining the nodes and edges
properties using the ``specify_nodes`` and ``specify_edges`` methods,
respectively, as well as other properties via methods including
``specify_types``, ``specify_connections``, etc... For more on this,
see the tutorial on creating custom networks or refer to some of the
available networks.
* Network data can be collected from an OpenStreetMap (.osm) file. The
.osm file is specified in the NetParams object. For example:
>>> from flow.core.params import NetParams
>>> net_params = NetParams(osm_path='/path/to/osm_file.osm')
In this case, no ``specify_nodes`` and ``specify_edges`` methods are
needed. However, a ``specify_routes`` method is still needed to specify
the appropriate routes vehicles can traverse in the network.
* Network data can be collected from an sumo-specific network (.net.xml)
file. This file is specified in the NetParams object. For example:
>>> from flow.core.params import NetParams
>>> net_params = NetParams(template='/path/to/template')
In this case, no ``specify_nodes`` and ``specify_edges`` methods are
needed. However, a ``specify_routes`` method is still needed to specify
the appropriate routes vehicles can traverse in the network.
This class can be instantiated once and reused in multiple experiments.
Note that this function stores all the relevant parameters. The
generate() function still needs to be called separately.
Attributes
----------
orig_name : str
the variable provided under the `name` parameter to this object upon
instantiation
name : str
the variable provided under the `name` parameter to this object upon
instantiation, appended with a timestamp variable. This timestamp is
meant to differentiate generated network files during parallelism
vehicles : flow.core.params.VehicleParams
vehicle specific parameters, used to specify the types and number of
vehicles at the start of a simulation
net_params : flow.core.params.NetParams
network specific parameters, used primarily to identify properties of a
network such as the lengths of edges and the number of lanes in each
edge. This attribute is very network-specific, and should contain the
variables denoted by the `ADDITIONAL_NET_PARAMS` dict in each network
class file
initial_config : flow.core.params.InitialConfig
specifies parameters that affect the positioning of vehicle in the
network at the start of a simulation. For more, see flow/core/params.py
traffic_lights : flow.core.params.TrafficLightParams
used to describe the positions and types of traffic lights in the
network. For more, see flow/core/params.py
nodes : list of dict or None
list of nodes that are assigned to the network via the `specify_nodes`
method. All nodes in this variable are expected to have the following
properties:
* **name**: a unique identifier for the node
* **x**: x-coordinate of the node, in meters
* **y**: y-coordinate of the node, in meters
If the network is meant to generate the network from an OpenStreetMap
or template file, this variable is set to None
edges : list of dict or None
edges that are assigned to the network via the `specify_edges` method.
This include the shape, position, and properties of all edges in the
network. These properties include the following mandatory properties:
* **id**: name of the edge
* **from**: name of the node the edge starts from
* **to**: the name of the node the edges ends at
* **length**: length of the edge
In addition, either the following properties need to be specifically
defined or a **type** variable property must be defined with equivalent
attributes in `self.types`:
* **numLanes**: the number of lanes on the edge
* **speed**: the speed limit for vehicles on the edge
Moreover, the following attributes may optionally be available:
* **shape**: the positions of intermediary nodes used to define the
shape of an edge. If no shape is specified, then the edge will appear
as a straight line.
Note that, if the network is meant to generate the network from an
OpenStreetMap or template file, this variable is set to None
types : list of dict or None
A variable used to ease the definition of the properties of various
edges. Each element in the list consists of a dict consisting of the
following property:
* **id**: name of the edge type. Edges in the `self.edges` attribute
with a similar value under the "type" key will adopt the properties
of other components of this list, such as "speed" and "numLanes".
If the type variable is None, then no types are available within the
network. Furthermore, a proper example of this variable being used can
be found under `specify_types` in flow/networks/loop.py.
Note that, if the network is meant to generate the network from an
OpenStreetMap or template file, this variable is set to None
connections : list of dict or None
A variable used to describe how any specific node's incoming and
outgoing edges/lane pairs are connected. If no connections are
specified, sumo generates default connections.
If the connections attribute is set to None, then the connections
within the network will be specified by the simulator.
Note that, if the network is meant to generate the network from an
OpenStreetMap or template file, this variable is set to None
routes : dict
A variable whose keys are the starting edge of a specific route, and
whose values are the list of edges a vehicle is meant to traverse
starting from that edge. These are only applied at the start of a
simulation; vehicles are allowed to reroute within the environment
immediately afterwards.
edge_starts : list of (str, float)
a list of tuples in which the first element of the tuple is the name of
the edge/intersection/internal_link, and the second value is the
distance of the link from some global reference, i.e. [(link_0, pos_0),
(link_1, pos_1), ...]
internal_edge_starts : list of (str, float)
A variable similar to `edge_starts` but for junctions within the
network. If no junctions are available, this variable will return the
default variable: `[(':', -1)]` needed by sumo simulations.
intersection_edge_starts : list of (str, float)
A variable similar to `edge_starts` but for intersections within
the network. This variable will be deprecated in future releases.
Example
-------
The following examples are derived from the `RingNetwork` Network class
located in flow/networks/ring.py, and should serve as an example of the
types of outputs to be expected from the different variables of a network
class.
First of all, the ring road network class can be instantiated by running
the following commands (note if this this unclear please refer to Tutorial
1):
>>> from flow.networks import RingNetwork
>>> from flow.core.params import NetParams, VehicleParams
>>>
>>> network = RingNetwork(
>>> name='test',
>>> vehicles=VehicleParams(),
>>> net_params=NetParams(
>>> additional_params={
>>> 'length': 230,
>>> 'lanes': 1,
>>> 'speed_limit': 30,
>>> 'resolution': 40,
>>> }
>>> )
>>> )
The various attributes then look as follows:
>>> print(network.nodes)
>>> [{'id': 'bottom', 'x': '0', 'y': '-36.60563691113593'},
>>> {'id': 'right', 'x': '36.60563691113593', 'y': '0'},
>>> {'id': 'top', 'x': '0', 'y': '36.60563691113593'},
>>> {'id': 'left', 'x': '-36.60563691113593', 'y': '0'}]
>>> print(network.edges)
>>> [
>>> {'id': 'bottom',
>>> 'type': 'edgeType',
>>> 'from': 'bottom',
>>> 'to': 'right',
>>> 'length': '57.5',
>>> 'shape': '0.00,-36.61 1.47,-36.58 2.95,-36.49 4.41,-36.34 '
>>> '5.87,-36.13 7.32,-35.87 8.76,-35.54 10.18,-35.16 '
>>> '11.59,-34.72 12.98,-34.23 14.35,-33.68 15.69,-33.07 '
>>> '17.01,-32.41 18.30,-31.70 19.56,-30.94 20.79,-30.13 '
>>> '21.99,-29.26 23.15,-28.35 24.27,-27.40 25.36,-26.40 '
>>> '26.40,-25.36 27.40,-24.27 28.35,-23.15 29.26,-21.99 '
>>> '30.13,-20.79 30.94,-19.56 31.70,-18.30 32.41,-17.01 '
>>> '33.07,-15.69 33.68,-14.35 34.23,-12.98 34.72,-11.59 '
>>> '35.16,-10.18 35.54,-8.76 35.87,-7.32 36.13,-5.87 '
>>> '36.34,-4.41 36.49,-2.95 36.58,-1.47 36.61,0.00'
>>> },
>>> {'id': 'right',
>>> 'type': 'edgeType',
>>> 'from': 'right',
>>> 'to': 'top',
>>> 'length': '57.5',
>>> 'shape': '36.61,0.00 36.58,1.47 36.49,2.95 36.34,4.41 36.13,5.87 '
>>> '35.87,7.32 35.54,8.76 35.16,10.18 34.72,11.59 '
>>> '34.23,12.98 33.68,14.35 33.07,15.69 32.41,17.01 '
>>> '31.70,18.30 30.94,19.56 30.13,20.79 29.26,21.99 '
>>> '28.35,23.15 27.40,24.27 26.40,25.36 25.36,26.40 '
>>> '24.27,27.40 23.15,28.35 21.99,29.26 20.79,30.13 '
>>> '19.56,30.94 18.30,31.70 17.01,32.41 15.69,33.07 '
>>> '14.35,33.68 12.98,34.23 11.59,34.72 10.18,35.16 '
>>> '8.76,35.54 7.32,35.87 5.87,36.13 4.41,36.34 2.95,36.49 '
>>> '1.47,36.58 0.00,36.61'
>>> },
>>> {'id': 'top',
>>> 'type': 'edgeType',
>>> 'from': 'top',
>>> 'to': 'left',
>>> 'length': '57.5',
>>> 'shape': '0.00,36.61 -1.47,36.58 -2.95,36.49 -4.41,36.34 '
>>> '-5.87,36.13 -7.32,35.87 -8.76,35.54 -10.18,35.16 '
>>> '-11.59,34.72 -12.98,34.23 -14.35,33.68 -15.69,33.07 '
>>> '-17.01,32.41 -18.30,31.70 -19.56,30.94 -20.79,30.13 '
>>> '-21.99,29.26 -23.15,28.35 -24.27,27.40 -25.36,26.40 '
>>> '-26.40,25.36 -27.40,24.27 -28.35,23.15 -29.26,21.99 '
>>> '-30.13,20.79 -30.94,19.56 -31.70,18.30 -32.41,17.01 '
>>> '-33.07,15.69 -33.68,14.35 -34.23,12.98 -34.72,11.59 '
>>> '-35.16,10.18 -35.54,8.76 -35.87,7.32 -36.13,5.87 '
>>> '-36.34,4.41 -36.49,2.95 -36.58,1.47 -36.61,0.00'
>>> },
>>> {'id': 'left',
>>> 'type': 'edgeType',
>>> 'from': 'left',
>>> 'to': 'bottom',
>>> 'length': '57.5',
>>> 'shape': '-36.61,0.00 -36.58,-1.47 -36.49,-2.95 -36.34,-4.41 '
>>> '-36.13,-5.87 -35.87,-7.32 -35.54,-8.76 -35.16,-10.18 '
>>> '-34.72,-11.59 -34.23,-12.98 -33.68,-14.35 '
>>> '-33.07,-15.69 -32.41,-17.01 -31.70,-18.30 '
>>> '-30.94,-19.56 -30.13,-20.79 -29.26,-21.99 '
>>> '-28.35,-23.15 -27.40,-24.27 -26.40,-25.36 '
>>> '-25.36,-26.40 -24.27,-27.40 -23.15,-28.35 '
>>> '-21.99,-29.26 -20.79,-30.13 -19.56,-30.94 '
>>> '-18.30,-31.70 -17.01,-32.41 -15.69,-33.07 '
>>> '-14.35,-33.68 -12.98,-34.23 -11.59,-34.72 '
>>> '-10.18,-35.16 -8.76,-35.54 -7.32,-35.87 -5.87,-36.13 '
>>> '-4.41,-36.34 -2.95,-36.49 -1.47,-36.58 -0.00,-36.61'
>>> }
>>> ]
>>> print(network.types)
>>> [{'id': 'edgeType', 'numLanes': '1', 'speed': '30'}]
>>> print(network.connections)
>>> None
>>> print(network.routes)
>>> {
>>> 'top': ['top', 'left', 'bottom', 'right'],
>>> 'left': ['left', 'bottom', 'right', 'top'],
>>> 'bottom': ['bottom', 'right', 'top', 'left'],
>>> 'right': ['right', 'top', 'left', 'bottom']
>>> }
>>> print(network.edge_starts)
>>> [('bottom', 0), ('right', 57.5), ('top', 115.0), ('left', 172.5)]
Finally, the ring network does not contain any junctions or intersections,
and as a result the `internal_edge_starts` and `intersection_edge_starts`
attributes are both set to None. For an example of a network with junctions
and intersections, please refer to: flow/networks/figure_eight.py.
>>> print(network.internal_edge_starts)
>>> [(':', -1)]
>>> print(network.intersection_edge_starts)
>>> []
"""
def __init__(self,
name,
vehicles,
net_params,
initial_config=InitialConfig(),
traffic_lights=TrafficLightParams()):
"""Instantiate the base network class.
Attributes
----------
name : str
A tag associated with the network
vehicles : flow.core.params.VehicleParams
see flow/core/params.py
net_params : flow.core.params.NetParams
see flow/core/params.py
initial_config : flow.core.params.InitialConfig
see flow/core/params.py
traffic_lights : flow.core.params.TrafficLightParams
see flow/core/params.py
"""
self.orig_name = name # To avoid repeated concatenation upon reset
self.name = name + time.strftime('_%Y%m%d-%H%M%S') + str(time.time())
self.vehicles = vehicles
self.net_params = net_params
self.initial_config = initial_config
self.traffic_lights = traffic_lights
# specify routes vehicles can take
self.routes = self.specify_routes(net_params)
if net_params.template is None and net_params.osm_path is None:
# specify the attributes of the nodes
self.nodes = self.specify_nodes(net_params)
# collect the attributes of each edge
self.edges = self.specify_edges(net_params)
# specify the types attributes (default is None)
self.types = self.specify_types(net_params)
# specify the connection attributes (default is None)
self.connections = self.specify_connections(net_params)
# this is to be used if file paths other than the the network geometry
# file is specified
elif type(net_params.template) is dict:
if 'rou' in net_params.template:
veh, rou = self._vehicle_infos(net_params.template['rou'])
vtypes = self._vehicle_type(net_params.template.get('vtype'))
cf = self._get_cf_params(vtypes)
lc = self._get_lc_params(vtypes)
# add the vehicle types to the VehicleParams object
for t in vtypes:
vehicles.add(veh_id=t, car_following_params=cf[t],
lane_change_params=lc[t], num_vehicles=0)
# add the routes of the vehicles that will be departed later
# under the name of the vehicle. This will later be identified
# by k.vehicles._add_departed
self.routes = rou
# vehicles to be added with different departure times
self.template_vehicles = veh
self.types = None
self.nodes = None
self.edges = None
self.connections = None
# osm_path or template as type str
else:
self.nodes = None
self.edges = None
self.types = None
self.connections = None
# optional parameters, used to get positions from some global reference
self.edge_starts = self.specify_edge_starts()
self.internal_edge_starts = self.specify_internal_edge_starts()
self.intersection_edge_starts = [] # this will be deprecated
# TODO: convert to property
[docs] def specify_edge_starts(self):
"""Define edge starts for road sections in the network.
This is meant to provide some global reference frame for the road
edges in the network.
By default, the edge starts are specified from the network
configuration file. Note that, the values are arbitrary but do not
allow the positions of any two edges to overlap, thereby making them
compatible with all starting position methods for vehicles.
Returns
-------
list of (str, float)
list of edge names and starting positions,
ex: [(edge0, pos0), (edge1, pos1), ...]
"""
return None
# TODO: convert to property
[docs] def specify_internal_edge_starts(self):
"""Define the edge starts for internal edge nodes.
This is meant to provide some global reference frame for the internal
edges in the network.
These edges are the result of finite-length connections between road
sections. This methods does not need to be specified if "no-internal-
links" is set to True in net_params.
By default, all internal edge starts are given a position of -1. This
may be overridden; however, in general we do not worry about internal
edges and junctions in large networks.
Returns
-------
list of (str, float)
list of internal junction names and starting positions,
ex: [(internal0, pos0), (internal1, pos1), ...]
"""
return [(':', -1)]
# TODO: convert to property
[docs] def specify_nodes(self, net_params):
"""Specify the attributes of nodes in the network.
Parameters
----------
net_params : flow.core.params.NetParams
see flow/core/params.py
Returns
-------
list of dict
A list of node attributes (a separate dict for each node). Nodes
attributes must include:
* id {string} -- name of the node
* x {float} -- x coordinate of the node
* y {float} -- y coordinate of the node
Other attributes may also be specified. See:
http://sumo.dlr.de/wiki/Networks/Building_Networks_from_own_XML-descriptions#Node_Descriptions
"""
raise NotImplementedError
# TODO: convert to property
[docs] def specify_edges(self, net_params):
"""Specify the attributes of edges connecting pairs on nodes.
Parameters
----------
net_params : flow.core.params.NetParams
see flow/core/params.py
Returns
-------
list of dict
A list of edges attributes (a separate dict for each edge). Edge
attributes must include:
* id {string} -- name of the edge
* from {string} -- name of node the directed edge starts from
* to {string} -- name of the node the directed edge ends at
In addition, the attributes must contain at least one of the
following:
* "numLanes" {int} and "speed" {float} -- the number of lanes and
speed limit of the edge, respectively
* type {string} -- a type identifier for the edge, which can be
used if several edges are supposed to possess the same number of
lanes, speed limits, etc...
Other attributes may also be specified. See:
http://sumo.dlr.de/wiki/Networks/Building_Networks_from_own_XML-descriptions#Edge_Descriptions
"""
raise NotImplementedError
# TODO: convert to property
[docs] def specify_types(self, net_params):
"""Specify the attributes of various edge types (if any exist).
Parameters
----------
net_params : flow.core.params.NetParams
see flow/core/params.py
Returns
-------
list of dict
A list of type attributes for specific groups of edges. If none are
specified, no .typ.xml file is created.
For information on type attributes, see:
http://sumo.dlr.de/wiki/Networks/Building_Networks_from_own_XML-descriptions#Type_Descriptions
"""
return None
# TODO: convert to property
[docs] def specify_connections(self, net_params):
"""Specify the attributes of connections.
These attributes are used to describe how any specific node's incoming
and outgoing edges/lane pairs are connected. If no connections are
specified, sumo generates default connections.
Parameters
----------
net_params : flow.core.params.NetParams
see flow/core/params.py
Returns
-------
list of dict
A list of connection attributes. If none are specified, no .con.xml
file is created.
For information on type attributes, see:
http://sumo.dlr.de/wiki/Networks/Building_Networks_from_own_XML-descriptions#Connection_Descriptions
"""
return None
# TODO: convert to property
[docs] def specify_routes(self, net_params):
"""Specify the routes vehicles can take starting from any edge.
Routes can be specified in one of three ways:
* In this case of deterministic routes (as is the case in the ring road
network), the routes can be specified as dictionary where the key
element represents the starting edge and the element is a single list
of edges the vehicle must traverse, with the first edge corresponding
to the edge the vehicle begins on. Note that the edges must be
connected for the route to be valid.
For example (from flow/networks/ring.py):
>>> def specify_routes(self, net_params):
>>> return {
>>> "top": ["top", "left", "bottom", "right"],
>>> "left": ["left", "bottom", "right", "top"],
>>> "bottom": ["bottom", "right", "top", "left"],
>>> "right": ["right", "top", "left", "bottom"]
>>> }
* Alternatively, if the routes are meant to be stochastic, each element
can consist of a list of (route, probability) tuples, where the first
element in the tuple is one of the routes a vehicle can take from a
specific starting edge, and the second element is the probability
that vehicles will choose that route. Note that, in this case, the
sum of probability values for each dictionary key must sum up to one.
For example, if we were to imagine the edge "right" in the ring road
examples where split into two edges, "right_0" and "right_1", the
routes for vehicles in this network in the probabilistic setting can
be:
>>> def specify_routes(self, net_params):
>>> return {
>>> "top": [
>>> (["top", "left", "bottom", "right_0"], 0.9),
>>> (["top", "left", "bottom", "right_1"], 0.1)
>>> ],
>>> "left": [
>>> (["left", "bottom", "right_0", "top"], 0.3),
>>> (["left", "bottom", "right_1", "top"], 0.7)
>>> ],
>>> "bottom": [
>>> (["bottom", "right_0", "top", "left"], 0.5),
>>> (["bottom", "right_1", "top", "left"], 0.5)
>>> ],
>>> "right_0": [
>>> (["right_0", "top", "left", "bottom"], 1)
>>> ],
>>> "right_1": [
>>> (["right_1", "top", "left", "bottom"], 1)
>>> ]
>>> }
* Finally, if you would like to assign a specific starting edge and
route to a vehicle with a specific ID, you can do so by adding a
element into the dictionary whose key is the name of the vehicle and
whose content is the list of edges the vehicle is meant to traverse
as soon as it is introduced to the network.
As an example, assume we have 4 vehicles named 'human_0', 'human_1',
'human_2', and 'human_3' in the original ring road. Then, an
appropriate definition of the routes may look something like:
>>> def specify_routes(self, net_params):
>>> return {
>>> "human_0": ["top", "left", "bottom", "right"],
>>> "human_1": ["left", "bottom", "right", "top"],
>>> "human_2": ["bottom", "right", "top", "left"],
>>> "human_3": ["right", "top", "left", "bottom"]
>>> }
**Note**: This feature is experimental, and may not always work as
expected (for example if the starting positions and routes of a
specific vehicle do not match).
The `define_routes` method is optional, and need not be defined. If it
is not implemented, vehicles that enter a network are assigned routes
consisting solely on their current edges, and exit the network once
they reach the end of their edge. Routes, however, can be reassigned
during simulation via a routing controller (see
flow/controllers/routing_controllers.py).
Parameters
----------
net_params : flow.core.params.NetParams
see flow/core/params.py
Returns
-------
dict
Key = name of the starting edge
Element = list of edges a vehicle starting from this edge must
traverse *OR* a list of (route, probability) tuples for each
starting edge
"""
return None
[docs] @staticmethod
def gen_custom_start_pos(cls, net_params, initial_config, num_vehicles):
"""Generate a user defined set of starting positions.
Parameters
----------
cls : flow.core.kernel.network.BaseKernelNetwork
flow network kernel, with all the relevant methods implemented
net_params : flow.core.params.NetParams
network-specific parameters
initial_config : flow.core.params.InitialConfig
see flow/core/params.py
num_vehicles : int
number of vehicles to be placed on the network
Returns
-------
list of tuple (float, float)
list of start positions [(edge0, pos0), (edge1, pos1), ...]
list of int
list of start lanes
list of float
list of start speeds
"""
raise NotImplementedError
@staticmethod
def _vehicle_infos(file_names):
"""Import of vehicle from a configuration file.
This is a utility function for computing vehicle information. It
imports a network configuration file, and returns the information on
the vehicle and add it into the Vehicle object.
Parameters
----------
file_names : list of str
path to the xml file to load
Returns
-------
dict <dict>
* Key = id of the vehicle
* Element = dict of departure speed, vehicle type, depart Position,
depart edges
"""
# this is meant to deal with the case that there is only one rou file
if isinstance(file_names, str):
file_names = [file_names]
vehicle_data = dict()
routes_data = dict()
type_data = defaultdict(int)
for filename in file_names:
# import the .net.xml file containing all edge/type data
parser = etree.XMLParser(recover=True)
tree = ElementTree.parse(filename, parser=parser)
root = tree.getroot()
# collect the departure properties and routes and vehicles whose
# properties are instantiated within the .rou.xml file. This will
# only apply if such data is within the file (it is not implemented
# by networks in Flow).
for vehicle in root.findall('vehicle'):
# collect the edges the vehicle is meant to traverse
route = vehicle.find('route')
route_edges = route.attrib["edges"].split(' ')
# collect the names of each vehicle type and number of vehicles
# of each type
type_vehicle = vehicle.attrib['type']
type_data[type_vehicle] += 1
vehicle_data[vehicle.attrib['id']] = {
'departSpeed': vehicle.attrib['departSpeed'],
'depart': vehicle.attrib['depart'],
'typeID': type_vehicle,
'departPos': vehicle.attrib['departPos'],
}
routes_data[vehicle.attrib['id']] = route_edges
# collect the edges the vehicle is meant to traverse for the given
# sets of routes that are not associated with individual vehicles
for route in root.findall('route'):
route_edges = route.attrib["edges"].split(' ')
routes_data[route.attrib['id']] = route_edges
return vehicle_data, routes_data
@staticmethod
def _vehicle_type(filename):
"""Import vehicle type data from a *.add.xml file.
This is a utility function for outputting all the type of vehicle.
Parameters
----------
filename : str
path to the vtypes.add.xml file to load
Returns
-------
dict or None
the key is the vehicle_type id and the value is a dict we've type
of the vehicle, depart edges, depart Speed, departPos. If no
filename is provided, this method returns None as well.
"""
if filename is None:
return None
parser = etree.XMLParser(recover=True)
tree = ElementTree.parse(filename, parser=parser)
root = tree.getroot()
veh_type = {}
# this hack is meant to support the LuST network and Flow networks
root = [root] if len(root.findall('vTypeDistribution')) == 0 \
else root.findall('vTypeDistribution')
for r in root:
for vtype in r.findall('vType'):
# TODO: make for everything
veh_type[vtype.attrib['id']] = {
'vClass': vtype.attrib.get('vClass', DEFAULT_VCLASS),
'accel': vtype.attrib['accel'],
'decel': vtype.attrib['decel'],
'sigma': vtype.attrib['sigma'],
'length': vtype.attrib.get('length', DEFAULT_LENGTH),
'minGap': vtype.attrib['minGap'],
'maxSpeed': vtype.attrib['maxSpeed'],
'probability': vtype.attrib.get(
'probability', DEFAULT_PROBABILITY),
'speedDev': vtype.attrib['speedDev']
}
return veh_type
@staticmethod
def _get_cf_params(vtypes):
"""Return the car-following sumo params from vtypes."""
ret = {}
for typ in vtypes:
# TODO: add vClass
ret[typ] = SumoCarFollowingParams(
speed_mode='all_checks',
accel=float(vtypes[typ]['accel']),
decel=float(vtypes[typ]['decel']),
sigma=float(vtypes[typ]['sigma']),
length=float(vtypes[typ]['length']),
min_gap=float(vtypes[typ]['minGap']),
max_speed=float(vtypes[typ]['maxSpeed']),
probability=float(vtypes[typ]['probability']),
speed_dev=float(vtypes[typ]['speedDev'])
)
return ret
@staticmethod
def _get_lc_params(vtypes):
"""Return the lane change sumo params from vtypes."""
ret = {}
for typ in vtypes:
ret[typ] = SumoLaneChangeParams(lane_change_mode=1621)
return ret
def __str__(self):
"""Return the name of the network and the number of vehicles."""
return 'Network ' + self.name + ' with ' + \
str(self.vehicles.num_vehicles) + ' vehicles.'