Tutorial 08: Networks from Custom Templates

In the previous tutorial, we discussed how OpenStreetMap files can be simulated in Flow. These networks, however, may at time be imperfect, as we can see in the toll section of the Bay Bridge (see the figure below). The simulators SUMO and Aimsun both possess methods for augmenting the network after they have been imported, and store the changes in their own versions of the initial template (whether it was generated via a custom scenario class or a network imported from OpenStreetMap). In order to utilize these newly generated networks, we demonstrate in this tutorial how simulator-generated template files can be imported when running a simulation in Flow.

**Figure 1**: Example benefit of converting OpenStreetMap to a custom template

The remainder of the tutorial is organized as follows. In section 1, we begin by importing the classic set of parameters. In section 2, we introduce the template files that are used as examples for importing the template files. In section 3, we present how custom SUMO network templates, i.e. the generated .net.xml files, can be modified and simulated in Flow for the purposed of improving network features. Finally, in section 4, we demonstrate how custom Aimsun network files can be simulated in Flow.

1. Importing Modules

Before we begin, let us import all relevant Flow parameters as we have done for previous tutorials. If you are unfamiliar with these parameters, you are encouraged to review tutorial 1.


In [1]:
# the TestEnv environment is used to simply simulate the network
from flow.envs import TestEnv

# the Experiment class is used for running simulations
from flow.core.experiment import Experiment

# the base scenario class
from flow.scenarios import Scenario

# all other imports are standard
from flow.core.params import VehicleParams
from flow.core.params import NetParams
from flow.core.params import InitialConfig
from flow.core.params import EnvParams

# create some default parameters parameters
env_params = EnvParams()
initial_config = InitialConfig()
vehicles = VehicleParams()
vehicles.add('human', num_vehicles=1)

2. Example Network

In this tutorial, we use the Luxembourg SUMO Traffic (LuST) Scenario as an example use case. This example consists of a well-calibrated model of vehicles in Luxembourg. A representation of the simulation can be seen in the figure below.

Figure 2: Simulation of the LuST network

Before, continuing with this tutorial, please begin by cloning the LuST scenario repository by running the following command.

git clone https://github.com/lcodeca/LuSTScenario.git

Once you have cloned the repository, please modify the code snippet below to match correct location of the repository's main directory.


In [2]:
LuST_dir = "/home/aboudy/LuSTScenario"

3. Sumo Network Files

Sumo generates several network and simulation-specifc template files prior to starting a simulation. This procedure when creating custom scenarios and scenarios from OpenStreetMap is covered by the scenario class. Three of these files (*.net.xml, *.rou.xml, and vtype.add.xml) can be imported once again via the scenario class to recreate a previously decided scenario.

We start by creating the simulation parameters:


In [3]:
from flow.core.params import SumoParams

sim_params = SumoParams(render=True, sim_step=1)

3.1 Importing Network (*.net.xml) Files

The *.net.xml file covers the network geometry within a simulation, and can be imported independently of the SUMO route file (see section 1.2). This can be done through the template parameter within NetParams as follows:


In [4]:
import os

net_params = NetParams(
    template=os.path.join(LuST_dir, "scenario/lust.net.xml"),
)

This network alone, similar to the OpenStreetMap file, does not cover the placement of vehicles or the routes vehicles can traverse. These, however, can be defined a they were in the previous tutorial for importing networks from OpenStreetMap. For the LuST network, this looks something similar to the following code snippet (note that the specific edges were not spoken for any specific reason).


In [5]:
# specify the edges vehicles can originate on
initial_config = InitialConfig(
    edges_distribution=["-32410#3"]
)


# specify the routes for vehicles in the network
class TemplateScenario(Scenario):

    def specify_routes(self, net_params):
        return {"-32410#3": ["-32410#3"]}

The simulation can then be executed as follows:


In [6]:
# create the scenario
scenario = TemplateScenario(
    name="template",
    net_params=net_params,
    initial_config=initial_config,
    vehicles=vehicles
)

# create the environment
env = TestEnv(
    env_params=env_params,
    sim_params=sim_params,
    scenario=scenario
)

# run the simulation for 1000 steps
exp = Experiment(env=env)
_ = exp.run(1, 1000)


/usr/local/lib/python3.6/dist-packages/numpy/core/fromnumeric.py:2957: RuntimeWarning: Mean of empty slice.
  out=out, **kwargs)
/usr/local/lib/python3.6/dist-packages/numpy/core/_methods.py:80: RuntimeWarning: invalid value encountered in double_scalars
  ret = ret.dtype.type(ret / rcount)
---------------------------------------------------------------------------
FatalTraCIError                           Traceback (most recent call last)
<ipython-input-6-9d035883b579> in <module>()
     16 # run the simulation for 1000 steps
     17 exp = Experiment(env=env)
---> 18 _ = exp.run(1, 1000)

~/Documents/flow/flow/core/experiment.py in run(self, num_runs, num_steps, rl_actions, convert_to_csv)
    104             state = self.env.reset()
    105             for j in range(num_steps):
--> 106                 state, reward, done, _ = self.env.step(rl_actions(state))
    107                 vel[j] = np.mean(
    108                     self.env.k.vehicle.get_speed(self.env.k.vehicle.get_ids()))

~/Documents/flow/flow/envs/base_env.py in step(self, rl_actions)
    325 
    326             # advance the simulation in the simulator by one step
--> 327             self.k.simulation.simulation_step()
    328 
    329             # store new observations in the vehicles and traffic lights class

~/Documents/flow/flow/core/kernel/simulation/traci.py in simulation_step(self)
     54     def simulation_step(self):
     55         """See parent class."""
---> 56         self.kernel_api.simulationStep()
     57 
     58     def update(self, reset):

/usr/local/lib/python3.6/dist-packages/traci/connection.py in simulationStep(self, step)
    273         self._string += struct.pack("!BBi", 1 +
    274                                     1 + 4, tc.CMD_SIMSTEP, step)
--> 275         result = self._sendExact()
    276         for subscriptionResults in self._subscriptionMapping.values():
    277             subscriptionResults.reset()

/usr/local/lib/python3.6/dist-packages/traci/connection.py in _sendExact(self)
     95             self._socket.close()
     96             del self._socket
---> 97             raise FatalTraCIError("connection closed by SUMO")
     98         for command in self._queue:
     99             prefix = result.read("!BBB")

FatalTraCIError: connection closed by SUMO

3.2 Importing Additional Files

Sumo templates will at times contain files other than the network templates that can be used to specify the positions, speeds, and properties of vehicles at the start of a simulation, as well as the departure times of vehicles while the scenario is running and the routes that all these vehicles are meant to traverse. All these files can also be imported under the template attribute in order to recreate the simulation in it's entirety.

When incorporating files other that the net.xml file to the simulation, the template attribute is treated as a dictionary instead, with a different element for each of the additional files that are meant to be imported. Starting with the net.xml file, it is added to the template attribute as follows:


In [7]:
new_net_params = NetParams(
    template={
        # network geometry features
        "net": os.path.join(LuST_dir, "scenario/lust.net.xml")
    }
)

3.2.1 Vehicle Type (vtype.add.xml)

The vehicle types file describing the properties of different vehicle types in the network. These include parameters such as the max acceleration and comfortable deceleration of drivers. This file can be imported via the "vtype" attribute in template.

Note that, when vehicle information is being imported from a template file, the VehicleParams object does not need be modified, unless you would like additionally vehicles to enter the network as well.


In [8]:
new_net_params = NetParams(
    template={
        # network geometry features
        "net": os.path.join(LuST_dir, "scenario/lust.net.xml"),
        # features associated with the properties of drivers
        "vtype": os.path.join(LuST_dir, "scenario/vtype.add.xml")
    }
)

# we no longer need to specify anything in VehicleParams
new_vehicles = VehicleParams()

3.2.2 Route (*.rou.xml)

Next, the routes can be imported from the *.rou.xml files that are generated by SUMO. These files help define which cars enter the network at which point in time, whether it be at the beginning of a simulation or some time during it run. The route files are passed to the "rou" key in the templates attribute. Moreover, since the vehicle routes can be spread over multiple files, the "rou" key that a list of string filenames.


In [9]:
new_net_params = NetParams(
    template={
        # network geometry features
        "net": os.path.join(LuST_dir, "scenario/lust.net.xml"),
        # features associated with the properties of drivers
        "vtype": os.path.join(LuST_dir, "scenario/vtypes.add.xml"),
        # features associated with the routes vehicles take
        "rou": [os.path.join(LuST_dir, "scenario/DUARoutes/local.0.rou.xml"),
                os.path.join(LuST_dir, "scenario/DUARoutes/local.1.rou.xml"),
                os.path.join(LuST_dir, "scenario/DUARoutes/local.2.rou.xml")]
    }
)

# we no longer need to specify anything in VehicleParams
new_vehicles = VehicleParams()

3.2.3 Running the Modified Simulation

Finally, the fully imported simulation can be run as follows.

Warning: the network takes time to initialize while the departure positions and times and vehicles are specified.


In [ ]:
# create the scenario
scenario = Scenario(
    name="template",
    net_params=new_net_params,
    vehicles=new_vehicles
)

# create the environment
env = TestEnv(
    env_params=env_params,
    sim_params=sim_params,
    scenario=scenario
)

# run the simulation for 100000 steps
exp = Experiment(env=env)
_ = exp.run(1, 100000)

4. Aimsun Network Files

Flow can run templates that have been created in Aimsun and saved into an *.ang file. Although it is possible to have control over the network, for instance add vehicles and monitor them directly from Flow, this tutorial only covers how to run the network.

We will use the template located at tutorials/networks/test_template.ang, which looks like this:

Figure 2: Simulation of test_template.ang in Aimsun

It contains two input and three output centroids that define the centroid configuration Centroid Configuration 910. The inflows are defined by two OD matrices, one for the type Car (in blue), the other for the type rl (in red). Note that there is no learning in this tutorial so the two types both act as regular cars. The two OD matrices form the traffic demand Traffic Demand 925 that is used by the scenario Dynamic Scenario 927. Finally, the experiment Micro SRC Experiment 928 and the replication Replication 930 are created, and we will run this replication in the following.

First, we create the Aimsun-specific simulation parameters:


In [ ]:
from flow.core.params import AimsunParams

sim_params = AimsunParams(
    sim_step=0.1,
    render=True,
    emission_path='data',
    replication_name="Replication 930",
    centroid_config_name="Centroid Configuration 910"
)

As you can see, we need to specify the name of the replication we want to run as well as the centroid configuration that is to be used. There is an other optional parameter, subnetwork_name, that can be specified if only part of the network should be simulated. Please refer to the documentation for more information.

The template can then be imported as follows:


In [ ]:
import os
import flow.config as config

net_params = NetParams(
    template=os.path.join(config.PROJECT_PATH,
                          "tutorials/networks/test_template.ang")
)

Finally, we can run the simulation by specifying 'aimsun' as the simulator to be used:


In [ ]:
scenario = Scenario(
    name="template",    
    net_params=net_params,
    initial_config=initial_config,
    vehicles=vehicles
)

env = TestEnv(
    env_params, 
    sim_params, 
    scenario, 
    simulator='aimsun' 
)

exp = Experiment(env)
exp.run(1, 1000)