The DiGraph format of networkx is very flexible in its formatting.
We describe below the semantics and labelling requirements for the DiGraph in
order to use the assignments in dyntapy.
All nodes and links need to have fully specified attributes for a subset of
the General Modeling Network Specification (GMNS), see
https://github.com/zephyr-data-specs/GMNS.
For the links we need:
‘from_node_id’, ‘to_node_id’, ‘link_id’, ‘lanes’,
‘capacity’, ‘length’, ‘free_speed’
optional are:
‘geometry’: shapely.geometry.LineString
if it is not set we assume a straight line
between the two nodes.
‘link_type’: int,
set to 1 for source connectors, -1 for sinks, 0 otherwise.
‘connector’: bool,
True if the link is a connector
For the nodes:
‘node_id’, ‘x_coord’, ‘y_coord’, ‘ctrl_type’, ‘node_type’
optional are:
‘centroid’: bool,
True if the node is a centroid
The inclusion of the ‘link_type’, ‘connector’ and ‘centroid’ attributes are
deviations from GMNS.
The graph’s nodes and edges need to be labelled consecutively and starting
from 0.
Many of the assignment algorithms also implicitly assume that the graph is strongly
connected.
This can be verified with networkx:
>>> networkx.strongly_connected_components(g)
If there is only a single element returned as the list of components, the graph
is strongly connected.
All of these requirements are met if dyntapy’s functions for extracting the network
from OpenStreetMap are used.
This class stores all the information needed for the assignment itself.
upon initialisation both the network and dynamic demand are transformed into
internal representations.
All the presented options utilize the same dynamic network loading (DNL) and
route choice component, that is the iterative link transmission model [1]
and an
iterative procedure to update a time-dependent arrival map, see [2].
‘i_ltm_aon’ refers to a dynamic deterministic user equilibrium solution,
note that for congested networks this is not guaranteed to converge below a
gap of 0.01.
‘incremental_assignment’ assigns the demands in chunks and updates the
both costs and route choice after each DNL.
This class stores all the information needed for the assignment itself.
Upon initialisation both the network and demand are transformed into
internal representations.
Parameters:
g (networkx.DiGraph) – road network graph with attributes and labelling as specified in the module
description.
od_graph (networkx.DiGraph) – graph with centroids as nodes with specified coordinates as ‘x_coord’ and
‘y_coord’. For each OD pair with a non-zero demand there
is a link with a corresponding ‘flow’ element.
‘msa’ and ‘dial_b’ try to find the static deterministic
user equilibrium.
‘msa’ refers to the Method of Successive Averages, a well known method in
Traffic Assignments that tends to zig-zag around equilibrium.
‘dial_b’ refers to Dial’s Algorithm B, a bush-based assignment
algorithm [3].
‘sun’ returns a stochastic uncongested assignment of flows on the free-flow
travel times that are determined by the lengths and speeds of the links. It
is based on Dial’s method, see [4]. It does not consider the whole path set
and rests the definition of ‘efficient links’ to allow for computations on an
acyclic graph.
‘sun’ and ‘msa’ are included for educational use.
‘dial_b’ has been optimized and converges quickly even for
large networks with thousands of links.
dyntapy.demand_data.add_centroids(g, X, Y, k=1, method='turn', euclidean=False, on_top=False, **kwargs)
Adds centroids to g.
Parameters:
g (networkx.Digraph) – road network graph, containing only road network edges and nodes
X (numpy.ndarray) – float, 1D - lon of centroids
Y (numpy.ndarray) – float, 1D - lat of centroids
k (int) – number of road network nodes to connect to per centroid.
method ({'turn','link'}) – whether to add link or turn connectors
euclidean (bool, optional) – set to True for toy networks that use the euclidean coordinate system
on_top (bool, optional) – set to True if extra nodes are made on top of each other
**kwargs (iterable, optional) – any keyword arguments are passed as additional attributes into the graph and
appear as attributes of the centroids.
They are assumed to be iterable and of
the same length as X.
Returns:
networkx.DiGraph – new graph with centroids and connectors
Notes
if method is ‘link’ k*2 connectors are added per centroid, one for each direction.
if method is ‘turn’k*2+2 connectors are added per centroid. There is another
artificial node between the centroid and the first intersection node. All
connector turns share the first starting link from centroid to this artificial node.
The route choice algorithms for DTA within dyntapy rely on iterative computations on
the link-turn graph rather than the node-link graph.
Within the network one can take the current link and evaluate the options as the
set of all outgoing turns.
Evaluating the choice of the next turn to take from a centroid node without a
dummy starting link and turns is cumbersome because the structure differs.
We add these dummy turns to keep the algorithms simpler.
generates centroids for the inner and extended study area from OpenStreetMap.
The inner area is filled with a grid.
The outer buffers are queried for settlements via OSMs ‘place’ tag.
Parameters:
place (str) – name of the city or region to buffer around.
buffer_dist_close (float) – width of the inner buffer
buffer_dist_extended (float) – width of the outer buffer
inner_city_centroid_spacing (float, optional) – distance between two adjacent centroids on the grid
Returns:
X (numpy.ndarray) – float, 1D
lon of centroid locations
Y (numpy.ndarray) – float, 1D
lat of centroid locations
name (list of strings) – name of the place as specified in OSM
place (list of strings) – values for OSMs place tag
Notes
The inner buffer is queried for places that are tagged as ‘village’, ‘city’,
or ‘town’. The outer buffer is just querying for ‘city’ or ‘town’.
dyntapy.demand_data.find_nearest_centroids(X, Y, centroid_graph:networkx.DiGraph)
finds the nearest centroids in the graph for a set of locations
Parameters:
X (numpy.ndarray) – longitude of points
Y (numpy.ndarray) – latitude of points
centroid_graph (networkx.DiGraph) – with existing centroids,
coordinates stored as ‘x_coord’ and ‘y_coord’ assumed to be lon and lat
tot_ods (int) – total number of OD pairs to be generated
name (str) – name of the city or region to geocode and sample from
g (networkx.DiGraph)
max_flow (float, optional) – maximum demand for any OD pair
seed (int, optional) – random seed
Returns:
od_graph (networkx.DiGraph) – graph with centroids as nodes with specified coordinates as ‘x_coord’ and
‘y_coord’. For each OD pair with a non-zero demand there
is a link with a corresponding ‘flow’ element as read from the OD matrix.
creates od_graph from od_matrix and centroid locations
Parameters:
od_matrix (numpy.ndarray) – float, 2D
X (numpy.ndarray) – float, 1D - lon of centroid locations
Y (numpy.ndarray) – float, 1D - lat of centroid locations
Returns:
od_graph (networkx.DiGraph) – graph with centroids as nodes with specified coordinates as ‘x_coord’ and
‘y_coord’. For each OD pair with a non-zero demand there
is a link with a corresponding ‘flow’ element as read from the OD matrix.
extracts an OD matrix and X and Y coordinates as arrays from a pandas.DataFrame and
zoning provided as a geopandas.GeoDataFrame.
It is rather common to receive OD tables in the form of .csv files
and zoning in the form of .shp files. This function enables one to
extract the
full OD matrix after loading these files with pandas and geopandas,
respectively.
Parameters:
od_table (pandas.DataFrame) – each row should represent one entry in the OD matrix, with origin_column,
destination_column and flow_column as the relevant columns
zoning (gpd.GeoDataFrame) – specifies geometries of the zoning, assumed to be in lon lat. The entries in
zone_column should correspond to the entries in origin_column and
destination_column in the od_table
origin_column (str)
destination_column (str)
zone_column (str)
flow_column (str)
return_relabelling (bool, optional) – whether to return the mapping between the original zone labels in ‘zone_column’
and the indexes in the returned OD matrix
Maps travel demand to existing closest centroids in g.
The returned demand pattern is expressed as its own directed graph.
Parameters:
data (geojson) – that contains lineStrings (WGS84) as features,
each line has an associated ‘flow’ stored in the properties dictionary
g (networkx.Digraph)
Returns:
od_graph (networkx.DiGraph) – graph with centroids as nodes with specified coordinates as ‘x_coord’ and
‘y_coord’. For each OD pair with a non-zero demand there
is a link with a corresponding ‘flow’ element as read from the OD matrix.
Notes
There’s no checking on whether the data and g correspond to the same geo-coded
region.
The corresponding OD table can be retrieved through calling
relabels graph’s links and nodes consecutively starting from 0.
Graphs obtained from external sources have labels that are often
neither stable nor continuous. We relabel nodes and edges
with our internal ids. The first C nodes in the network are centroids,
with C the total number of centroids.
The first K links in the network are source connectors,
as link labelling is consecutive by the start node ids.
Sink connector ids are therefore random.
Parameters:
g (networkx.DiGraph)
Returns:
new_g (networkx.Digraph) – with continuously labelled nodes, consistent with internal notation
inverse (dict, optional) – a dictionary which maps each of the old node ids to the new ones
after initializing either a dyntapy.assignments.StaticAssignment or
dyntapy.assignments.DynamicAssignment we have access to a compiled
dyntapy.supply.Network object.
Alternatively, this can be build using dyntapy.supply_data.build_network
>>> network=dyntapy.supply_data.build_network(g)
The structure of this network object is described below.
It gives access to a Links, Nodes and Turns object such that one can easily retrieve
any network information rather intuitively.
For example, if one wanted to get the free flow travel times for all links,
simply query the underlying links object.
should not be initialized by the user, use dyntapy.supply_data.build_network
out_turns and in_turns are sparse matrices in CSR format that indicate
connected turns and their links.
Both have the same shape (network.tot_turns, network.tot_links) with the
indexes indicating the link_id and the values the to- and from_link, respectively.
There’s duplication to avoid on-the-fly transformations.
tot_out_links (numpy.ndarray) – int, 1D - number of outgoing links
tot_in_links (numpy.ndarray) – int, 1D - number of outgoing links
control_type (numpy.ndarray) – int, 1D
capacity (numpy.ndarray) – float, 1D
is_centroid (numpy.ndarray) – bool, 1D
x_coord (numpy.ndarray) – float, 1D
y_coord (numpy.ndarray) – float, 1D
Notes
should not be initialized by the user, use dyntapy.supply_data.build_network
out_links and in_links are sparse matrices in CSR format that indicate
connected links and their nodes.
Both have the same shape (network.tot_nodes, network.tot_links) with the
indexes indicating the link_id and the values the to- and from_node, respectively.
There’s duplication to avoid on-the-fly transformations.
computes k-shortest paths with a maximum overlap of sim_threshold
Parameters:
g (networkx.DiGraph) – as specified for assignments
source (int) – node id
target (int) – node id
costs (numpy.ndarray, optional) – if not set, free flow travel times are used based on defined length and speed
k (int) – number of the shortest paths to return
sim_threshold (float) – threshold for similarity between paths in the solution set, [0,1]
detour_rejection (float) – path quality criteria
Returns:
solution_paths (list of list) – each entry is a solution path
path_lengths (list) – length of each solution path as the sum of traversed link costs
Notes
detour_rejection has been added by the developers to prune bad solutions.
A value of 0.10 indicates that paths can be at most 10 percent worse than the
shortest path solution. Similar to a lower sim_threshold this setting may
affect the completeness of the results.
is_centroid (np.ndarray) – bool, 1D - centroids are ignored for routing
sim_threshold (float) – threshold for similarity between paths in the solution set, [0,1]
detour_rejection (float) – path quality criteria
Returns:
solution_paths (list of list) – each entry is a solution path
path_lengths (list) – length of each solution path as the sum of traversed link costs
Notes
detour_rejection has been added by the developers to prune bad solutions.
A value of 0.10 indicates that paths can be at most 10 percent worse than the
shortest path solution. Similar to a lower sim_threshold this setting may
affect the completeness of the results, see [5].
This module provides a simple interface for creating CSR formatted sparse matrices
that can be used in Numba.
Namely, one can import F32CSRMatrix, UI32CSRMatrix, UI8CSRMatrix from this module with
the starting letters indicating the item type of the sparse matrix. F32 stands for
float32, UI32 for unsigned int32 and UI8 for unsigned int8.
Because of the way these classes are build we cannot just integrate the docs for them in
sphinx, the source code itself however has extensive comments in the defined CSRMatrix.
flows (numpy.ndarray, optional) – float, 2D - time-dependent flows to be visualized
link_kwargs (dict, optional) – additional time-dependent link information to be displayed
key: str, value: np.ndarray - additional time-dependent link information to
be displayed
node_kwargs (dict, optional) – key: str, value: np.ndarray - additional time-dependent node information to
be displayed
highlight_links (numpy.ndarray, optional) – int, 1D or 2D - links to highlight
highlight_nodes (numpy.ndarray, optional) – int, 1D or 2D - nodes to highlight
euclidean (bool, optional) – set to True if coordinates in graph are euclidean.
toy_network (bool, optional) – deprecated, use euclidean instead.
title (str, optional)
notebook (bool, optional) – set to True if the plot should be rendered in a notebook.
show_nodes (bool, optional) – whether to render nodes
return_plot (bool, optional) – set to True if the plot object should be returned instead of showing it.
max_edge_width (float, optional) – defaults to 1, changes the width of the edges by the set factor
Examples
highlighting works just as in show_network and is not time dependent.
Will generate a plot where respective values for foo and bar can be inspected by
hovering over the link.
The values are updated as the time slider is moved.
Note that the string attribute names cannot contain spaces and that the arrays
must have the correct dimension.
node_kwargs can be passed on analogously.
Notes
highlight colors can be altered in the settings and have been chosen to still offer
visibility in a graph with loaded traffic in a green-to-red color map.
Visualizing a network with static attributes in a .html.
Parameters:
g (networkx.DiGraph)
flows (numpy.ndarray, optional) – float, 1D - flows to be visualized
link_kwargs (dict, optional) – additional link information to be displayed
key: str, value: numpy.ndarray - additional link information to be
displayed
node_kwargs (dict, optional) – key: str, value: numpy.ndarray - additional node information to be
displayed
highlight_links (numpy.ndarray or list, optional) – int, 1D or 2D - links to highlight
highlight_nodes (numpy.ndarray or list, optional) – int, 1D or 2D - nodes to highlight
euclidean (bool, optional) – set to True if coordinates in graph are euclidean.
toy_network (bool, optional) – deprecated, use euclidean instead.
title (str, optional)
notebook (bool, optional) – set to True if the plot should be rendered in a notebook.
show_nodes (bool, optional) – whether to render nodes
return_plot (bool, optional) – set to True if the plot object should be returned instead of showing it.
max_edge_width (float, optional) – defaults to 1, changes the width of the edges by the set factor
Examples
>>> show_network(g,highlight_links=[[2,4],[3,6]])
will plot the network and highlight links [2,4] in neon pink,
and [3, 6] in cyan. The order of highlight colors is neon pink, cyan, lime green,
light blue, orange, gray
will generate a plot where respective values for foo and bar can be inspected by
hovering over the link.
Note that the string attribute names cannot contain spaces and that the arrays
must have the correct dimension.
node_kwargs can be passed on analogously.
Notes
highlight colors can be altered in the settings and have been chosen to still offer
visibility in a graph with loaded traffic in a green-to-red color map.
By default most of dyntapy’s assignment algorithms utilize Numba to accelerate
computations. It is not possible to use breakpoints
inside of code that bas been JIT-compiled. We first need to disable JIT compilation
to do so:
>>> fromnumbaimportconfig>>> config.DISABLE_JIT=1
Make sure that the above is put on top of the script that you’re running the
assignment from, before all other imports.
Importing numba and changing this variable after doing so yields rather confusing
errors.
When working with breakpoints in the assignment algorithms it is advantageous to have
access to
the assignment object in order to visualize the network or get additional information
that may not be available in the context where your breakpoint is set.
From the debugger one can always import the latest instantiated assignment object as
shown below:
>>> fromdyntapy._contextimportrunning_assignment
The running_assignment object is either a dyntapy.StaticAssignment or a dyntapy
.DynamicAssignment, both share the network attribute.
Instances of dyntapy’s internal demand and supply objects, specified in dyntapy.supply and dyntapy.demand, are made available for both static and dynamic assignment instances.
If we have a dyntapy.StaticAssignment object given we can access the internal_network and demand attributes.
Note that assignment algorithms that are implemented using the internal demand and supply objects can be accelerated using Numba, which is not possible for generic python objects.
You can always visualize the network and any link and node attributes that are generated during computations.
Once your algorithm runs there is still some boilerplate code needed to fully integrate in dyntapy. For details do take a look at the existing assignments in dyntapy.assignments. The structure will essentially be the same for all static and dynamic assignments, respectively. Your compiled assignment routine returns all the arrays needed to fill the dyntapy.results.StaticResults or dyntapy.results.DynamicResults. The outer shell function creates the result object and returns it.
The dynamic assignment routines presented here work for the shown example(s) in the
tutorials, however they are not guaranteed to produce reliable result or fail
gracefully for any arbitrary network, travel demand and time configuration. If the
demand that you feed exceeds the local infrastructure and there is spillback into the
origin do not expect reasonable outputs. The same holds for demand that cannot leave
the network during the simulation,
there should always be some time periods in the dynamic assignment in which all the
queues can resolve and all vehicles can reach their destination. This
is very much following the principle of garbage-in-garbage-out.
It is best practice to first explore a small example and build some intuition for DTA
before moving on to more complex scenarios which slow you down because of their
larger computation time.
The complexity in DTA is mainly driven by the number of OD pairs, their intensity and
induced congestion, the
network’s size
(in number of links and nodes) and the number of time steps. Ideally, your example to
experiment with should be low in complexity in all of those metrics.