Skip to content

Python API reference

Classes

pytraf.Network

A road network with its link delay functions and turn table.

Links are numbered by their position in the input arrays and nodes are integers from 0. Link delay is the BPR function free_flow_time * (1 + b * (volume / capacity) ** power).

Parameters:

Name Type Description Default
num_nodes int

Number of nodes; node ids are 0..num_nodes.

required
from_node NDArray[int64]

From-node of each link.

required
to_node NDArray[int64]

To-node of each link.

required
free_flow_time NDArray[float64]

Delay at zero flow, per link.

required
capacity NDArray[float64]

Capacity per link; must be positive.

required
b Optional[NDArray[float64]]

BPR b per link (default 0.15).

None
power Optional[NDArray[float64]]

BPR power per link (default 4).

None
is_centroid Optional[NDArray[bool_]]

Bool per node. A path may start or end at a centroid but never passes through it. Use for zone centroids.

None
turns Optional[Dict[str, NDArray[Any]]]

Optional turn table as a dict of arrays, with the columns from_link and to_link and optionally banned, penalty (NaN = none), delay_free_flow, delay_capacity, delay_b and delay_power. Use :meth:Network.from_tables to pass DataFrames instead.

None
allow_u_turns bool

Allow immediate returns to the node a link came from.

False

node_ids property

node_ids: Optional[List[str]]

node_id of every node as written in the GMNS files (None otherwise).

link_ids: Optional[List[str]]

link_id of every link; an undirected link appears twice (None otherwise).

num_nodes property

num_nodes: int

Number of nodes.

num_links: int

Number of links.

num_turns property

num_turns: int

Number of allowed turns in the turn table.

capacity property

capacity: NDArray[float64]

Capacity per link.

is_centroid property

is_centroid: NDArray[bool_]

Per node: True if paths may only start or end there.

from_tables staticmethod

from_tables(
    links: Any,
    turns: Optional[Any] = None,
    *,
    num_nodes: Optional[int] = None,
    is_centroid: Optional[Any] = None,
    allow_u_turns: bool = False,
    is_endpoint: Optional[Any] = None,
) -> Network

Builds a network from flat tables (pandas DataFrames or dicts of arrays).

links needs the columns from_node, to_node, free_flow_time and capacity; b (default 0.15) and power (default 4) are optional. Node ids are integers starting at 0 (num_nodes defaults to the largest id + 1). Links get their ids from their row order, 0, 1, 2, ...

turns is optional and lists exceptions and delays by link ids. Columns: from_link and to_link (required), banned (bool), penalty (fixed delay, NaN = none) and delay_free_flow, delay_capacity, delay_b, delay_power (a BPR delay on the turn's own flow; give all four or none). Turns not listed are allowed at zero cost, except U-turns unless allow_u_turns. Use :meth:turn_links to see which turns exist.

is_centroid marks nodes (zone centroids) that paths may start or end at but not pass through. is_endpoint is the deprecated old name.

from_tntp staticmethod

from_tntp(path: str) -> Network

Reads a TNTP *_net.tntp file. Node numbers become 0-based, links keep file order and <FIRST THRU NODE> becomes the centroid mask.

from_gmns staticmethod

from_gmns(
    path: str,
    length_unit: Optional[str] = None,
    speed_unit: Optional[str] = None,
    zone_nodes_are_centroids: bool = False,
    zone_nodes_are_endpoints: Optional[bool] = None,
) -> Network

Reads a GMNS dataset directory (node.csv, link.csv, optional movement.csv and config.csv). Times are in minutes; capacity is per link (per-lane capacity times lanes). Undirected links become two links.

length_unit (e.g. "mile", "foot", "km") and speed_unit ("mph", "kph") override config.csv and are required without it. A movement table restricts an inbound link only if it lists a movement for it. With zone_nodes_are_centroids zone nodes cannot be passed through. zone_nodes_are_endpoints is the deprecated old name.

link_nodes() -> Tuple[
    NDArray[np.uint32], NDArray[np.uint32]
]

Returns the from-node and to-node of every link.

turn_links() -> Tuple[
    NDArray[np.uint32], NDArray[np.uint32]
]

Returns the incoming and outgoing link of every turn, indexed by turn id.

assign

assign(
    classes: Union[
        Demand,
        TrafficClass,
        Sequence[Union[Demand, TrafficClass]],
    ],
    *,
    gap: float = 1e-06,
    max_iter: int = 200,
    order: Union[Order, str] = Order.FIXED,
    seed: int = 0,
    chunk_size: int = 0,
    threads: Optional[int] = None,
    link_preload: Optional[NDArray[float64]] = None,
    turn_preload: Optional[NDArray[float64]] = None,
    unreachable: Union[
        Unreachable, str
    ] = Unreachable.ERROR,
) -> AssignResult

Finds the user equilibrium on this network by path-based gradient projection.

The same as pytraf.assign(self, classes, ...), where all the options are described. Everything after classes is keyword-only.

pytraf.Demand

Trips between nodes in long format (a zone is a node).

Repeated origin-destination pairs add up. Trips with the same origin and destination use no network and are skipped.

Parameters:

Name Type Description Default
num_nodes int

Number of nodes of the network the demand is for.

required
origins NDArray[int64]

Origin node of each entry.

required
destinations NDArray[int64]

Destination node of each entry.

required
flows NDArray[float64]

Trips per entry; non-negative.

required

unknown_zone_flow property

unknown_zone_flow: float

Trips dropped because their zone has no node (0 unless read with from_gmns).

num_nodes property

num_nodes: int

Number of nodes the demand was built for.

from_table staticmethod

from_table(network: Network, table: Any) -> Demand

Builds the demand for network from a long-format table (a pandas DataFrame or a dict of arrays) with columns origin, destination and flow. Origins and destinations are node ids.

from_tntp staticmethod

from_tntp(network: Network, path: str) -> Demand

Reads a TNTP *_trips.tntp file for network.

from_gmns staticmethod

from_gmns(
    network: Network,
    path: str,
    origin: str = "o_zone_id",
    destination: str = "d_zone_id",
    flow: str = "volume",
    skip_unknown_zones: bool = False,
) -> Demand

Reads a zone-to-zone demand CSV for a network read with Network.from_gmns. origin, destination and flow name the columns. A zone's trips are split equally over its nodes. Rows of zones without a node are an error unless skip_unknown_zones, in which case they are summed in unknown_zone_flow.

total

total() -> float

Total number of trips.

pytraf.TrafficClass

One traffic class: demand plus how its vehicles load and move on the network.

Parameters:

Name Type Description Default
demand Demand

The class's trips.

required
pce float

Passenger-car units one vehicle loads on the network (cars 1.0, trucks 2.5). Link delays depend on the PCE-weighted volume.

1.0
extra_link_cost Optional[NDArray[float64]]

Extra cost per link in the same units as travel time, for example a toll divided by the class's value of time.

None
allowed_links Optional[NDArray[bool_]]

Bool per link. A turn is usable only if both its links are.

None
name Optional[str]

Label for the class in result tables (default: its position).

None

Results

assign returns an AssignResult, which groups what it holds into the objects below.

pytraf.AssignResult

The outcome of :func:pytraf.assign; build it with network.assign(...).

Attributes:

Name Type Description
converged bool

Whether the relative gap reached the requested gap.

iterations int

Sweeps over all origins that were run.

relative_gap float

The relative gap at the end.

network Network

The network the result belongs to.

class_names List[str]

One name per class: the TrafficClass name, or its position.

links

Flows and times per link.

turns

Flows and times per turn.

paths

The paths and their flows.

unrouted

Demand left out by unreachable="skip".

diagnostics

Sweep gaps, phase times and the objective.

summary

summary() -> str

A short readable account of the run: convergence, trips per class, demand left out and the busiest link.

pytraf.results.LinkResults

Bases: _Elements

Flows and times per link, indexed by link id.

voc cached property

voc: NDArray[float64]

Volume over capacity: volume plus the preload, divided by the capacity.

volume cached property

volume: NDArray[float64]

Flow in car units (vehicles times their PCE), summed over the classes. This is what the delay functions see, without the preload.

time cached property

time: NDArray[float64]

Travel time at the final volumes, preload included.

preload cached property

preload: Optional[NDArray[float64]]

The fixed volume passed to assign, or None if there was none.

by_class cached property

by_class: ClassFlows

Vehicles per class. Index by position or by class name.

flow cached property

flow: NDArray[float64]

Vehicles of all classes (not weighted by PCE).

table

table() -> Any

One row per link as a pandas DataFrame.

Columns: link_id for GMNS networks, from_node, to_node, flow (vehicles of all classes), flow_<class> when there are several classes, volume (in car units), preload if one was given, voc (volume plus preload over capacity) and time.

pytraf.results.TurnResults

Bases: _Elements

Flows and times per turn, indexed by turn id (see Network.turn_links).

volume cached property

volume: NDArray[float64]

Flow in car units (vehicles times their PCE), summed over the classes. This is what the delay functions see, without the preload.

time cached property

time: NDArray[float64]

Travel time at the final volumes, preload included.

preload cached property

preload: Optional[NDArray[float64]]

The fixed volume passed to assign, or None if there was none.

by_class cached property

by_class: ClassFlows

Vehicles per class. Index by position or by class name.

flow cached property

flow: NDArray[float64]

Vehicles of all classes (not weighted by PCE).

table

table() -> Any

One row per turn as a pandas DataFrame.

Columns: from_link, to_link, node, then the same flow, volume, preload and time columns as the link table (no voc).

pytraf.results.ClassFlows

Bases: Sequence

Vehicles per class, as one array per class.

Index by position (flows[0]) or by class name (flows["truck"]); iterate for the arrays in class order.

names property

names: List[str]

The class names, in class order.

pytraf.results.PathResults

The paths of the final solution, with the flow and travel time of each.

arrays

arrays(class_index: int = 0) -> Dict[str, NDArray[Any]]

Every path of a class as flat arrays.

Returns origin, destination, flow and time (one entry per path), link_offsets (one more entry) and links (all link ids back to back; the links of path i are links[link_offsets[i]:link_offsets[i + 1]]).

Raises:

Type Description
IndexError

If the class does not exist.

table

table(class_index: Optional[int] = None) -> Any

One row per path as a pandas DataFrame.

Columns: class (when there are several), origin, destination, flow, time and links (a tuple of link ids). class_index limits it to one class.

pytraf.results.UnroutedDemand

Demand that unreachable="skip" left out because no allowed path serves it.

Empty (len(result.unrouted) == 0) when everything was routable or the unreachable option is "error".

flow cached property

flow: List[float]

Trips left out, one total per class.

total property

total: float

Trips left out, over all classes.

arrays

arrays() -> Dict[str, NDArray[Any]]

The left-out pairs as flat arrays: class (index), origin, destination and flow.

table

table() -> Any

One row per left-out origin-destination pair as a pandas DataFrame.

Columns: class (when there are several), origin, destination, flow.

pytraf.results.Diagnostics dataclass

How the solver got to the result.

sweep_gaps instance-attribute

sweep_gaps: NDArray[float64]

The relative gap after each sweep.

times instance-attribute

times: Dict[str, float]

Seconds per phase: initial_assignment, shortest_paths, flow_updates, sweep_rebuild and gap_checks.

objective instance-attribute

objective: float

The Beckmann objective at the solution, or NaN when it is not defined: it needs a single class with PCE 1.

Options

pytraf.Order

Bases: _Checked

The order in which a sweep visits the origins.

FIXED class-attribute instance-attribute

FIXED = 'fixed'

The same order every sweep.

SHUFFLE class-attribute instance-attribute

SHUFFLE = 'shuffle'

A new random order each sweep, seeded by seed.

ALTERNATE class-attribute instance-attribute

ALTERNATE = 'alternate'

Forwards, then backwards, on alternate sweeps.

LARGEST_GAP class-attribute instance-attribute

LARGEST_GAP = 'largest_gap'

Origins with the largest remaining gap first.

LARGEST_DEMAND class-attribute instance-attribute

LARGEST_DEMAND = 'largest_demand'

Origins with the most demand first.

SMALLEST_DEMAND class-attribute instance-attribute

SMALLEST_DEMAND = 'smallest_demand'

Origins with the least demand first.

pytraf.Unreachable

Bases: _Checked

What :func:pytraf.assign does with demand that no allowed path can serve.

ERROR class-attribute instance-attribute

ERROR = 'error'

Raise a ValueError that names the origin-destination pairs.

SKIP class-attribute instance-attribute

SKIP = 'skip'

Leave those trips out, assign the rest and report them in result.unrouted.

Functions

pytraf.assign

assign(
    network: Network,
    classes: Union[
        Demand,
        TrafficClass,
        Sequence[Union[Demand, TrafficClass]],
    ],
    *,
    gap: float = 1e-06,
    max_iter: int = 200,
    order: Union[Order, str] = Order.FIXED,
    seed: int = 0,
    chunk_size: int = 0,
    threads: Optional[int] = None,
    link_preload: Optional[NDArray[float64]] = None,
    turn_preload: Optional[NDArray[float64]] = None,
    unreachable: Union[
        Unreachable, str
    ] = Unreachable.ERROR,
) -> AssignResult

Finds the user equilibrium by path-based gradient projection.

This is also a method of the network: network.assign(classes, ...) is pytraf.assign(network, classes, ...). Everything after classes is keyword-only.

Parameters:

Name Type Description Default
network Network

The network.

required
classes Union[Demand, TrafficClass, Sequence[Union[Demand, TrafficClass]]]

A Demand, a TrafficClass, or a list of them.

required
gap float

Stop when the relative gap is at or below this.

1e-06
max_iter int

Maximum number of sweeps over all origins.

200
order Union[Order, str]

Origin order per sweep, an Order or its string value.

FIXED
seed int

Seed for Order.SHUFFLE.

0
chunk_size int

Origins whose shortest paths are searched in parallel before their flow shifts are applied one at a time. 0 picks about 1/32 of the origins (between 1 and 64); 1 is the plain sequential sweep. Results do not depend on the number of threads.

0
threads Optional[int]

Number of threads for the parallel parts (default: all cores, or RAYON_NUM_THREADS).

None
link_preload Optional[NDArray[float64]]

Fixed volume per link in car units, such as buses or through traffic, that is not assigned but adds to the volume the delays see.

None
turn_preload Optional[NDArray[float64]]

The same per turn.

None
unreachable Union[Unreachable, str]

What to do with demand between nodes that no allowed path joins, an Unreachable or its string value. Unreachable.ERROR raises and names the pairs; Unreachable.SKIP leaves those trips out, assigns the rest and reports them in AssignResult.unrouted.

ERROR

Returns:

Type Description
AssignResult

The flows, paths and convergence information.

Raises:

Type Description
ValueError

For malformed input, an unknown order or unreachable, or demand that cannot be routed while unreachable is Unreachable.ERROR.