Skip to content

About

Active Transportation Bicycle Routing Model

Resources

Stars

3 stars

Watchers

1 watching

Forks

Repository files navigation

Velocipede: An Active Transport Bike Model

Velocipede logo

Velocipede is a Python-based implementation of a bike route choice model adapted from SANDAG's ABM. The bike route choice model is a multinomial logit discrete choice model that estimates the probabilities of an individual's choosing among multiple alternative routes between a given origin and destination. This discrete choice model forms the basis of both the estimation of the level of service between OD pairs used by the demand models and the estimation of the number of cyclists assigned to network links. The level of service is the expected maximum utility, or logsum, from the model, and network link assignments are made with individual route probabilities.

Setup and Running

Installation

This project uses UV as its package manager. To install UV, follow the instructions at https://docs.astral.sh/uv/getting-started/installation/ or run:

# Windows
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

# macOS/Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

Once UV is installed, create the virtual environment and install dependencies:

uv sync --locked

Running the Model

The bike model is launched from bike_route_choice.py and accepts a single optional command-line argument specifying the path to a settings YAML file. The default value is bike_route_choice_settings.yaml.

# Using the default settings file
uv run python bike_route_choice.py

# Specifying a settings file
uv run python bike_route_choice.py spec/or_metro/bike_route_choice_settings_taz.yaml

Example settings files are provided in spec/ for both the Oregon Metro (spec/or_metro/) and SANDAG (spec/sandag/) networks.

Configuration

A settings YAML file defines all model inputs, parameters, and outputs. Below is a minimal example:

# Network inputs
node_file: nodes.csv
link_file: links.csv
traversal_file: turns.csv
data_dir: path/to/network/data

# Utility specifications (relative to spec/ directory)
edge_util_file: or_metro/bike_edge_utils.csv
traversal_util_file: or_metro/bike_traversal_utils.csv

# Zone configuration
zone_level: taz            # "taz" or "maz"
max_dijkstra_utility: 28   # threshold utility for early termination

# Path sampling
number_of_iterations: 5    # number of Dijkstra iterations per OD pair

# Runtime
number_of_batches: 10
number_of_processors: 3

# Output
output_path: output/bike/taz
bike_speed: 3.5

Settings Reference

Network

  • data_dir: path to the directory containing network input files
  • node_file: input file containing bike network nodes (shapefile or CSV with WKT geometry)
  • link_file: input file containing bike network links (shapefile or CSV with WKT geometry)
  • traversal_file: input file containing turn/traversal records (optional if generate_traversals: True)
  • connector_file: input file containing centroid connector links (optional if read_centroid_connectors: True)
  • zone_level: zone level on which path choice should be performed. Allowed values: taz, maz
  • zone_subset: subset of zones to use for model testing (integer count or list of zone IDs)
  • bidirectionalize_links: whether to create reverse-direction copies of all links
  • generate_traversals: whether to derive traversal records from the link topology
  • trim_dead_ends: whether to remove dead-end links from the network

Utilities

  • edge_util_file: file containing the utility specification for link costs
  • traversal_util_file: file containing the utility specification for traversal costs
  • path_util_file: file containing the utility specification for path-level costs (optional)
  • path_attributor_file: Python module with functions to calculate path attributes (optional)
  • random_scale_coef: scaling coefficient for randomized utility coefficients (default: 0.1)
  • random_scale_link: scaling coefficient for randomized edge cost multiplier (default: 0.2)

Model Hyperparameters

  • number_of_iterations: number of Dijkstra iterations (paths sampled) per OD pair
  • number_of_batches: number of batches into which origin centroids are divided for sequential processing
  • number_of_processors: number of processors to use for parallel processing within each batch
  • max_dijkstra_utility: cutoff threshold utility (positive) for early termination of shortest-path search
  • min_iterations: minimum number of paths required; OD pairs with fewer paths are discarded

Demand Assignment

  • demand_matrix_file: path to an OpenMatrix (.omx) file containing OD demand
  • demand_segments: dictionary of demand segment names to matrix mappings
  • assign_demand: whether to assign demand to paths based on calculated utilities
  • demand_assignment_method: "proportional" or "monte_carlo"

Output and Caching

  • output_path: directory in which model outputs are written
  • logsum_format: output format for logsums ("csv" or "omx")
  • save_bike_net: whether to write the derived network to CSV cache
  • read_cached_bike_net: whether to read a cached network instead of re-deriving
  • use_cached_paths: whether to skip Dijkstra and load pre-computed paths from a previous run
  • bike_speed: speed (in distance units per hour) used to calculate bike travel times

Tracing

  • trace_bike_utilities: whether to trace edge and traversal utility calculations
  • trace_origins: list of origin zone IDs whose paths will be traced
  • trace_destinations: list of destination zone IDs corresponding to the traced origins
  • generate_shapefile: whether to output traced paths as a shapefile
  • crs: coordinate reference system for output shapefiles (e.g., epsg:2913)

Utility Calculation

The variable specifications used in the utility function for edges and traversals are stored in CSV files (e.g., bike_edge_utils.csv, bike_traversal_utils.csv, and optionally bike_path_utils.csv), including the expressions and their associated coefficients. Utilities can be specified at three levels:

  • Edge utilities: additive costs evaluated per-link (distance, facility type, traffic exposure)
  • Traversal utilities: costs associated with transitions between links (turns, signal delay, unsignalized crossings)
  • Path utilities (optional): non-additive costs evaluated over the full path (share of distance on facility types, slope, log-distance)

To account for the correlation in the random utilities of overlapping routes, the utility function in the model includes a "path size" measure, described in the following section.

Path Size

The size of path alternative $n_i$ in alternative set $\textbf{n}$ is calculated using the formula:

$$size(n_i)=\sum_{l\in n_i}\frac{edgeLength(l)}{pathLength(n_i)*pathsUsing(\textbf{n},l)}$$

where $pathsUsing$ is the integer number of paths in the alternative set $\textbf{n}$ which contain link $l$. Its use derives from the theory of aggregate and elemental alternatives, where a link is an aggregation of all paths that use the link. If multiple routes overlap, their "size" is less than one. If two routes overlap completely, their size will be one-half.

Path Sampling

Given an origin, paths are sampled to all relevant destinations in the network by repeatedly applying Dijkstra's algorithm to search for the paths that minimize a stochastic link generalized cost with a mean given by the additive inverse of the path utility. For each path sampling iteration, a random coefficient vector is first sampled from a non-negative multivariate uniform distribution with zero covariance and mean equal to the link generalized cost coefficients corresponding to the path choice utility function. As the path search extends outward from the origin, the random cost coefficients do not vary over links, but only over iterations of path sampling. In the path search, the cost of each subsequent link is calculated by summing the product of the random coefficients with their respective link attributes, and then multiplying the result by a non-negative discrete random edge cost multiplier. Path sampling is repeated until both a minimum count of paths and a preset target for the total of all path sizes in each alternative list is reached. If the total path size does not reach its target after a given maximum number of sampling iterations, sampling terminates to prevent excessively long computation time.

Demand Assignment

When a demand matrix is provided (via demand_matrix_file and demand_segments), the model can assign OD demand to network links using one of two methods:

  • Proportional: demand is distributed across paths in proportion to their multinomial logit probabilities.
  • Monte Carlo: for each OD pair, individual trips are randomly assigned to paths by drawing from the multinomial probability distribution.

Edge-level demand is written to CSV files in the output directory (one per demand segment).

Threshold Utility and Distance

To improve runtime, the Dijkstra's algorithm implementation allows for early termination once a predetermined utility threshold has been reached in its search, with no paths found for zone pairs whose utility is beyond the threshold. However, the utility of paths is not necessarily the most user-friendly metric — it is reasonable to aim to define the cutoff by distance rather than utility. Because the underlying implementation of Dijkstra's algorithm is only aware of utilities, not distances, this is not directly feasible, as utility and distance do not correspond directly.

To address this, the bike_threshold_calculator.py script provides a mechanism to search for a utility threshold which approximates a desired distance cutoff. Given a valid configuration YAML file, the script iteratively performs a binary search, using the YAML file's max_dijkstra_utility setting as a starting threshold utility and modifying its value on subsequent iterations until the target distance is within the allowed margin of error (or the maximum number of bike model iterations has been completed).

Usage:
    uv run python bike_threshold_calculator.py <settings filepath> <target distance> [target_margin [percentile [max_iterations]]]

    parameters:
        settings filepath:  path to YAML file containing bike model settings
        target distance:    the distance for which the search should aim (in miles)
        target margin:      the margin of error (< 1) allowed before termination (optional, default: 0.1)
        percentile:         the percentile of distance to compare against the target (optional, default: 0.99)
        max iterations:     the most bike model iterations that can be performed in the search (optional, default: 20)

    examples:

        python bike_threshold_calculator.py bike_route_choice_settings_taz.yaml 20
        # the resulting 99th %ile distance must be w/in 10% of the 20-mile target distance
        # equivalent to:
            python bike_threshold_calculator.py bike_route_choice_settings_taz.yaml 20 0.1 0.99 20

        python bike_threshold_calculator.py bike_route_choice_settings_maz.yaml 3 0.05
        # the resulting 99th %ile distance must be w/in 5% of the three-mile target distance

With the default parameters, the script will search until either 20 iterations have elapsed or the 99th percentile of distances found is within 10% of the specified target distance. At termination, a CSV named threshold_results.csv will be written to the output directory with the input utility thresholds, the iteration runtime, and the chosen percentile of distance.

About

Active Transportation Bicycle Routing Model

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages