Special trains at SBB

Train scheduling

SBB takes great care to setup its schedule. Making sure trains are always accessible to people that need them is not an easy task. Ensuring all constraints are met means this task is even more demanding. Limited network capacity, fixed fleet size, and labour laws need to be considered. All these factors mean train scheduling is a complex optimization problem.

The carefully designed schedule works well if everything goes as planned. The network is at its full capacity, the fleet is available, and the people flows are as expected. But what happens if one of these variables changes? Can SBB handle the new situation under its schedule?

Special trains

To ensure smooth people flow, SBB adds special trains to its schedule. Special trains are one-time connections that happen for a specific reason. Geneva Motor Show? OpenAir in St.Gallen? Long weekend when everyone travels to Italy? You will surely find special trains on those days.

Construction sites

To ensure the best operability, railways need maintenance. Performing maintenance on tracks may reduce the network capacity. Constructions don't only vary in time and space, but also in the capacity reduction to the network. Some of them will have minor impact. Others will result in closing all rail connections between two cities.


The regular train schedule satisfies hundreds of constraints. Adding special trains means putting additional weight on an already tight network. Planning construction sites means reducing the network capacity. Having special trains, and construction sites, scheduled at the same place and time may cause serious issues.

In this notebook we show how to identify lines where such conflicts occur. Having a clear picture of the conflicts will allow the scheduling team to make right decision and plan reliable service for everyday, and for special occasions.

First, let's import the dependencies. Theses functions:

  • query and load data
  • create network graph
  • use graph algorithms to determine connections
In [1]:
import datetime

import plotly.express as px
import plotly.graph_objects as go

from utils import (MapMode, create_network_graph, download_construction_sites,
                   download_special_trains, filter_constructions,
                   filter_trains, generate_map_layers, generate_network_trace,
                   plot_network, plot_network_layer)

Now, let's download the network data. We will use:

  • Railway network - by Bundesamt für Verkehr
  • Ist/soll schedule - by SBB and Zazuko
  • Construction sites - by SBB and Zazuko
In [2]:
# Core data
network = create_network_graph()
all_trains = download_special_trains()
all_constructions = download_construction_sites()

network_trace = generate_network_trace(network)
/usr/local/lib/python3.9/site-packages/geopandas/_vectorized.py:142: ShapelyDeprecationWarning: __len__ for multi-part geometries is deprecated and will be removed in Shapely 2.0. Check the length of the `geoms` property instead to get the  number of parts of a multi-part geometry.
  aout[:] = out
/usr/local/lib/python3.9/site-packages/geopandas/array.py:166: ShapelyDeprecationWarning: The array interface is deprecated and will no longer work in Shapely 2.0. Convert the '.coords' to a numpy array instead.
  return GeometryArray(vectorized.from_shapely(data), crs=crs)

Let's take a look at our network. Below you will find all railway nodes, and the connections between them. The line shapes are simplified.

In [3]:
# Network plot
fig = plot_network(network_trace)