Quickstart with TraSMAPy

Generating our first network

After installing TraSMAPy, you need a network to work with. The netgenerate utility from SUMO can be used to generate a simple network. The following command will generate a random network:

netgenerate --rand -o rand.net.xml

The file rand.net.xml containes our network. Now we need a sumo configuration file to run the simulation. You can put the following in the a rand.sumocfg file:

<configuration>
    <input>
        <net-file value="rand.net.xml"/>
    </input>
</configuration>

Note that you can also consult the official SUMO documentation for more information about importing, building, and customizing networks.

Using TraSMAPy for the first time

With this, we are ready to write our runner script and use TraSMAPy. Create a runner.py file with the following content:

#!/usr/bin/env python

from trasmapy import TraSMAPy

def run(traSMAPy: TraSMAPy):
    """execute the TraCI control loop"""
    while traSMAPy.minExpectedNumber > 0:
        traSMAPy.doSimulationStep()

    traSMAPy.closeSimulation()


if __name__ == "__main__":
    traSMAPy = TraSMAPy("rand.sumocfg")
    run(traSMAPy)

Running this python script will open the sumo-gui. You’ll notice that if you start the simulation (by pressing the play button), the simulation will end immediately. This is because we have not added any vehicles to the simulation and are we only ticking the simulation until there are no vehicles.

Adding vehicles to the simulation

We can add vehicles to the simulation by chaging our runner.py file like so:

def run(traSMAPy: TraSMAPy):
    for i in range(100):
        traSMAPy.users.createVehicle(f"v{i}")

    """execute the TraCI control loop"""
    while traSMAPy.minExpectedNumber > 0:
        traSMAPy.doSimulationStep()

    traSMAPy.closeSimulation()

This will spawn 100 vehicles in the simulation. If you run the simulation again, you’ll notice that the vehicles will move around the network. You can also use the TraSMAPy API to control the vehicles. For example, you can change the speed of all vehicles like so:

def run(traSMAPy: TraSMAPy):
    for i in range(100):
        v = traSMAPy.users.createVehicle(f"v{i}")
        v.speed = 10

    """execute the TraCI control loop"""
    while traSMAPy.minExpectedNumber > 0:
        for vehicle in traSMAPy.users.getVehicles():
            vehicle.setSpeed(10)
        traSMAPy.doSimulationStep()

    traSMAPy.closeSimulation()

As you can see, vehicle, just like everything in TraSMAPy, are objects. This abstracts away the complexity of the TraCI API and makes it easier to use.

Examining the network

TraSMAPy also provides an API to examine the network. For example, you can get a sum of all CO2Emissions in all edges in the network for each simulation tick like so:

def run(traSMAPy: TraSMAPy):
    """execute the TraCI control loop"""
    for i in range(100):
        v = traSMAPy.users.createVehicle(f"v{i}")
        v.speed = 10

    while traSMAPy.minExpectedNumber > 0:
        traSMAPy.doSimulationStep()

        edges = traSMAPy.network.edges
        co2Emissions = 0
        for edge in edges:
            co2Emissions += edge.CO2Emissions
        print(co2Emissions)

    traSMAPy.closeSimulation()

You’ll probably notice that this makes the simulation run very slowly. This is because you are iterating all network edges for each simulation tick.

Introduction to queries

TraSMAPy provides a query API to make it easier to query the network and aggregate statistics. For this, there are two query mecanisms available: Python functions, and the Pyflwor query language. The Pyflwor query language is a query language that is inspired by the XQuery language, and is probably the easiest way to make simple queries. Let’s convert the previous example to a Pyflwor query:

def run(traSMAPy: TraSMAPy):
    """execute the TraCI control loop"""
    for i in range(100):
        v = traSMAPy.users.createVehicle(f"v{i}")
        v.speed = 10

    while traSMAPy.minExpectedNumber > 0:
        traSMAPy.doSimulationStep()

        print(traSMAPy.query("return sum(<network/edges/CO2Emissions>)"))

    traSMAPy.closeSimulation()

Since we are interested in collecting this statistic for each simulation tick, we can register the query to be executed every simulation tick. This can be done by using the registerQuery method of the TraSMAPy class. Let’s register the previous query (note that you need to provide a name for registered queries):

def run(traSMAPy: TraSMAPy):
    """execute the TraCI control loop"""
    for i in range(100):
        v = traSMAPy.users.createVehicle(f"v{i}")
        v.speed = 10

    traSMAPy.registerQuery("Total CO2 Emissions", "return sum(<network/edges/CO2Emissions>)")

    while traSMAPy.minExpectedNumber > 0:
        traSMAPy.doSimulationStep()

        print(traSMAPy.collectedStatistics)

    traSMAPy.closeSimulation()

As you can see, the collectedStatistics attribute of the TraSMAPy class contains all the statistics collected by the registered queries, organized by tick and name.

We can also take advantage of the registerQuery method to register a query that doesn’t run every simulation tick, thus having a smaller performance hit. For example, we can register a query that runs every 10 simulation ticks:

def run(traSMAPy: TraSMAPy):
    """execute the TraCI control loop"""
    for i in range(100):
        v = traSMAPy.users.createVehicle(f"v{i}")
        v.speed = 10

    traSMAPy.registerQuery("Total CO2 Emissions", "return sum(<network/edges/CO2Emissions>)", tickInterval=10)

    while traSMAPy.minExpectedNumber > 0:
        traSMAPy.doSimulationStep()

        print(traSMAPy.collectedStatistics)

    traSMAPy.closeSimulation()

The next steps

This is just a small introduction to TraSMAPy. For more information, please refer to the API reference, and to the examples on the TraSMAPy repository.