Trip Scheduling (Probabilistic)#

For each trip, assign a departure hour based on an input lookup table of percents by tour purpose, direction (inbound/outbound), tour hour, and trip index.

  • The tour hour is the tour start hour for outbound trips and the tour end hour for inbound trips. The trip index is the trip sequence on the tour, with up to four trips per half tour

  • For outbound trips, the trip depart hour must be greater than or equal to the previously selected trip depart hour

  • For inbound trips, trips are handled in reverse order from the next-to-last trip in the leg back to the first. The tour end hour serves as the anchor time point from which to start assigning trip time periods.

  • Outbound trips on at-work subtours are assigned the tour depart hour and inbound trips on at-work subtours are assigned the tour end hour.

The assignment of trip depart time is run iteratively up to a max number of iterations since it is possible that the time period selected for an earlier trip in a half-tour makes selection of a later trip time period impossible (or very low probability). Thus, the sampling is re-run until a feasible set of trip time periods is found. If a trip can’t be scheduled after the max iterations, then the trip is assigned the previous trip’s choice (i.e. assumed to happen right after the previous trip) or dropped, as configured by the user. The trip scheduling model does not use mode choice logsums.

Alternatives: Available time periods in the tour window (i.e. tour start and end period). When processing stops on work tours, the available time periods is constrained by the at-work subtour start and end period as well.

In order to avoid trip failing, a new probabilistic trip scheduling mode was developed named “relative”. When setting the scheduling_mode option to relative, trips are scheduled relative to the previously scheduled trips. The first trip still departs when the tour starts and for every subsequent trip, the choices are selected with respect to the previous trip depart time. Inbound trips are no longer handled in reverse order. The key to this relative mode is to index the probabilities based on how much time is remaining on the tour. For tours that include subtours, the time remaining will be based on the subtour start time for outbound trips and will resume again for inbound trips after the subtour ends. By indexing the probabilities based on time remaining and scheduling relative to the previous trip, scheduling trips in relative mode will not fail. Note also that relative scheduling mode requires the use of logic version 2 (see warning about logic versions, below).

An example of trip scheduling in relative mode is included in the prototype_mwcog example. In this example, trip scheduling probabilities are indexed by the following columns:

  • periods_left_min: the minimum bin for the number of time periods left on the tour.

  • periods_left_max: the maximum bin for the number of time periods left on the tour. This is the same as periods_left_min until the final time period bin.

  • outbound: whether the trip occurs on the outbound leg of a tour.

  • tour_purpose_grouped: Tour purpose grouped into mandatory and non-mandatory categories

  • half_tour_stops_remaining_grouped: The number of stops remaining on the half tour with the categories of 0 and 1+

Each of these variables are listed as merge columns in the trip_scheduling.yaml file and are declared in the trip scheduling preprocessor. The variables above attempt to balance the statistics available for probability creation with the amount of segmentation of trip characteristics.

.. warning::

Earlier versions of ActivitySim contained a logic error in this model, whereby
the earliest departure time for inbound legs was bounded by the maximum outbound
departure time, even if there was a scheduling failure for one or more outbound
leg departures and that bound was NA.  For continuity, this process has been
retained in this ActivitySim component as *logic_version* 1, and it remains the
default process if the user does not explicitly specify a logic version in the
model settings yaml file. The revised logic includes bounding inbound legs only
when the maximum outbound departure time is well defined.  This version of the
model can be used by explicitly setting `logic_version: 2` (or greater) in the
model settings yaml file.  It is strongly recommended that all new model
development efforts use logic version 2; a future version of ActivitySim may
make this the default for this component, and/or remove logic version 1 entirely.

The main interface to the trip scheduling model is the trip_scheduling function. This function is registered as an Inject step in the example Pipeline.

Structure#

  • Configuration File: trip_scheduling.yaml

  • Core Table: trips

  • Result Field: depart

Configuration#

settings activitysim.abm.models.trip_scheduling.TripSchedulingSettings#

Bases: PydanticReadable

Settings for the trip_scheduling component.

Fields:
field COEFFICIENTS: str = 'trip_scheduling_coefficients.csv'#

Filename for the trip scheduling coefficients file

field CONSTANTS: dict[str, Any] = {}#
field DEPART_ALT_BASE: int = 5#

Integer to add to probs column index to get time period it represents. e.g. depart_alt_base = 5 means first column (column 0) represents 5 am

field FAILFIX: str = 'choose_most_initial'#
field MAX_ITERATIONS: int = 1#

Maximum iterations.

field PROBS_SPEC: str = 'trip_scheduling_probs.csv'#

Filename for the trip scheduling probabilities (.csv) file.

field logic_version: int | None = None#
field preprocessor: PreprocessorSettings | None = None#
field probs_join_cols: list[str] | None = None#
field scheduling_mode: Literal['departure', 'stop_duration', 'relative'] = 'departure'#

Examples#

Implementation#

activitysim.abm.models.trip_scheduling.trip_scheduling(state: State, trips: DataFrame, tours: DataFrame, model_settings: Optional[TripSchedulingSettings] = None, model_settings_file_name: str = 'trip_scheduling.yaml', trace_label: str = 'trip_scheduling') None#

Trip scheduling assigns depart times for trips within the start, end limits of the tour.

The algorithm is simplistic:

The first outbound trip starts at the tour start time, and subsequent outbound trips are processed in trip_num order, to ensure that subsequent trips do not depart before the trip that preceeds them.

Inbound trips are handled similarly, except in reverse order, starting with the last trip, and working backwards to ensure that inbound trips do not depart after the trip that succeeds them.

The probability spec assigns probabilities for depart times, but those possible departs must be clipped to disallow depart times outside the tour limits, the departs of prior trips, and in the case of work tours, the start/end times of any atwork subtours.

Scheduling can fail if the probability table assigns zero probabilities to all the available depart times in a trip’s depart window. (This could be avoided by giving every window a small probability, rather than zero, but the existing mtctm1 prob spec does not do this. I believe this is due to the its having been generated from a small household travel survey sample that lacked any departs for some time periods.)

Rescheduling the trips that fail (along with their inbound or outbound leg-mates) can sometimes fix this problem, if it was caused by an earlier trip’s depart choice blocking a subsequent trip’s ability to schedule a depart within the resulting window. But it can also happen if a tour is very short (e.g. one time period) and the prob spec having a zero probability for that tour hour.

Therefore we need to handle trips that could not be scheduled. There are two ways (at least) to solve this problem:

1) choose_most_initial simply assign a depart time to the trip, even if it has a zero probability. It makes most sense, in this case, to assign the ‘most initial’ depart time, so that subsequent trips are minimally impacted. This can be done in the final iteration, thus affecting only the trips that could no be scheduled by the standard approach

2) drop_and_cleanup drop trips that could no be scheduled, and adjust their leg mates, as is done for failed trips in trip_destination.

Which option is applied is determined by the FAILFIX model setting

activitysim.abm.models.trip_scheduling.set_stop_num(trips)#

Convert trip_num to stop_num in order to work with duration-based probs that are keyed on stop num. For outbound trips, trip n chooses the duration of stop n-1 (the trip origin). For inbound trips, trip n chooses the duration of stop n (the trip destination). This means outbound trips technically choose a departure time while inbound trips choose an arrival.

activitysim.abm.models.trip_scheduling.update_tour_earliest(trips, outbound_choices, logic_version: int)#

Updates “earliest” column for inbound trips based on the maximum outbound trip departure time of the tour. This is done to ensure inbound trips do not depart before the last outbound trip of a tour.

Parameters:
trips: pd.DataFrame
outbound_choices: pd.Series

time periods depart choices, one per trip (except for trips with zero probs)

logic_versionint

Logic version 1 is the original ActivitySim implementation, which sets the “earliest” value to the max outbound departure for all inbound trips, regardless of what that max outbound departure value is (even if it is NA). Logic version 2 introduces a change whereby that assignment is only made if the max outbound departure value is not NA.

Returns:
modifies trips in place
activitysim.abm.models.trip_scheduling.schedule_trips_in_leg(state: State, outbound, trips, probs_spec, model_settings: TripSchedulingSettings, is_last_iteration, trace_label, *, chunk_sizer: ChunkSizer)#
Parameters:
state
outbound
trips
probs_spec
depart_alt_base
is_last_iteration
trace_label
Returns:
choices: pd.Series

depart choice for trips, indexed by trip_id