BayAreaMetro / tm2py

Travel Model Two - python edition
https://bayareametro.github.io/tm2py/
Apache License 2.0
2 stars 8 forks source link

🚀 Feature: Reduce Number Transit Journey Levels -- Phase 1 #145

Closed DavidOry closed 1 month ago

DavidOry commented 3 months ago

User Story

The initial translation of the Cube transit assignment scripts were faithfully translated to EMME. This included the translation of the fare matrix. Mechanically, this was done via an algorithm that considered the distance between services and their differences in fares, with a goal of creating fewer than the maximum number of 30 journey levels. Because EMME considers the fare in the path building stage, there is a severe runtime penalty for each journey level. An analysis of the algorithmically created journey levels revealed that a lot of them were working to create accurate representations of fares for low ridership services (that have complicated fare structures) -- see tree map below.

Screenshot 2023-09-29 at 4 16 19 PM

As a first step in assessing the impact of reducing the journey levels on runtime and outcomes, we will manually specify journey levels within tm2py, starting with @inrokevin's recommendations. Subsequent efforts will create a set of procedures so that the specifications can be derived from a set of GTFS files and guidance documents.

Progress:

Priority

High

Level of Effort

~80 hours

Resolution Ideas

  1. Create a scheme in the model_config.yml file that specifies the desired journey levels. Per @inrokevin, this could look something like:
[transfer_fares]
    groups = [
        {group_id = 1, fare_systems = [1, 2, 3]},
        {group_id = 2, fare_systems = [4, 5, 6]},
        {group_id = 3, fare_systems = [7, 8, 9]},
    ]
    from_fares = [
        {group_id = 1, values=[0, 0.88, 1.22]}
        {group_id = 2, values=[-1.1, 0, 2.5]}
        {group_id = 3, values=[0, 0.44, 0]}
    ]

Project

MTC's ActivitySim/Emme Conversion, Task Order 5

Who should be involved?

Users: @lmz Reviewers: @inrokevin, @gregerhardt

Risk

Yes. The outcomes from the assignment may be different due to changes in the fare assumptions. However, manually specifying a small number of journey levels should make the code simpler and more robust. Right now, the algorithm can return more than the maximum number of journey levels.

Tests

We want to run the following tests:

gregerhardt commented 3 months ago

I recommend being as aggressive as we reasonably can in setting these. The tree map shows 21 journey levels. The initial recommendations included 8 journey levels:

  1. BART
  2. Caltrain
  3. Muni + VTA + AC Transit + All other Local Bus — use ridership-weighted mean flat fare across Muni, VTA, AC Transit
  4. Muni + VTA LRT — use mean flat fare, perhaps weighted by boardings
  5. Ferries — all, using a mean per mile fare
  6. Express buses — all, using a mean per mile fare, perhaps weighted by boardings
  7. ACE
  8. Capitol Corridor

A few possibilities:

Those two changes could potentially get us from 8 down to 5 journey levels.

gregerhardt commented 3 months ago

And one more question: Does EMME include the concept of fare links? If so, can we use them to customize as we need? For example, we could merge Ferries with express buses, but have an additional $10 fare link at the dock.

DavidOry commented 3 months ago

@gregerhardt I was thinking we start even more aggressive. One thing to note is that the journey levels are states. So, if a system like Caltrain does not give any discounts after riding Caltrain, then it does not need a separate journey level. Said another way, being in the state of having already boarded Caltrain does not change how you interact with the other services. We can accurately (well, after transforming it to a distance-based approximation) represent the Caltrain (and Ferry, and BART) fares without them being separate journey levels. The only thing we get wrong is the transfer discount when moving to another operator. I did not fully understand this -- probably still do not, thankfully we have @inrokevin to help us -- when I wrote the original recommendation for eight.

I think to start we go down to 2 journey levels (plus the others we'll need for park and ride and to prevent walking all the way), as follows:

  1. Base state -- have yet to board a transit service
  2. Already boarded Muni state

This is a minimum configuration in which we are acknowledging the free transfers on Muni (which is important), but ignoring all other transfer discounts. We can then assign the on-board demand with this configuration and see what we need to add, e.g., the discount when going from AC Transbay to AC Local, which @e-lo said was a problem in a long ago SFCTA calibration.

@inrokevin: what do you think about this approach?

inrokevin commented 3 months ago

You have the right approach Dave. Small clarification (probably what you meant but just to be clear) the base state would be not yet boarded Muni, i.e. would include not having boarded at all and having boarded any other services. So this proposal would no longer differentiate initial and transfer boardings.

A 3-level approach (1. not boarded anything, 2. boarded muni, 3. boarded anything else) is something to consider if transfer behavior is really off. This would also allow preventing "walk all the way" trips at the same time.

Any additional operator-to-operator fare discounts should be considered in the context of the number of transfers. A review of operator-to-operator transfers and important fare discounts might reveal some reasonable "over-discount" options. SO, for example, for the AC Transbay / Local case: if few transfers from other operators occur we could give the "from AC Transbay" discount for all transfer boardings to AC Local, knowing that fares for some passengers will be under-represented.

Of course, I would recommend trying a simpler version first and seeing if there are problems and only adding complexity as necessary.

Fare links and virtual stops can be used to represent special transfer combinations. This can be much more efficient for very local and well defined cases, e.g. special transfers between ferries, or ferries to certain bus services. However, the network visualization and maintenance can be a little more difficult.

DavidOry commented 3 months ago

Thanks @inrokevin. Can you explain the "additional operator-to-operator fare discounts" a bit more? Is this another journey level or something else?

inrokevin commented 3 months ago

By "additional operator-to-operator fare discounts" I am referring to the real world cases, for example the AC Transbay & Local transfer fare discount. Before we decide to allocate a journey level to representing this case, we should consider that there are options for representing the fares without journey levels.

  1. Have no discount to AC Local, fares when transferring from AC Transbay will be too high. (This seems to be the default, but it might not make sense if the AC Transbay to Local is actually the most important operator-to-operator transfer)
  2. Everyone gets a discount, all transfer boardings to AC Local get the AC Transbay discount, and fares when boarding from other services will be too low.
  3. Split the difference: everyone gets half discount.
  4. The fare can vary over the network, so nodes which have both transbay and local get a discount, all other nodes have the full fare. This can lead to some short illogical walks to a cheaper boarding stop, but may actually get the least number of trips wrong.

My main point was that in order to prioritize, we should consider the number of passengers affected by analyzing the operator-to-operator transfers.

DavidOry commented 2 months ago

Initial test results are in. The test includes three journey levels, as follows:

  1. Not yet boarded transit
  2. Boarded SF MTA
  3. Boarded all other transit providers

Headline of run-time comparison: for the morning commute period congested assignment of the on-board survey demand:

See the below spreadsheet for additional details, including the compute spec.

observed-transit-assign-journey-level-runtime.xlsx

The two assignments give highly similar results (for the morning commute). See this Tableau workbook. The key differences are as follows:

Recommend moving forward with this configuration for next round of testing and to discuss the user experience on our July 3 call. Additional calibration to the address Caltrain and Ferry can occur if the pattern replicates in the 2023 base year.

@lmz, @vivverma9, @gregerhardt, @FlaviaTsang, @inrokevin

DavidOry commented 2 months ago

@vivverma9 Here's what I'm thinking in terms of the user experience:

https://github.com/BayAreaMetro/travel-model-two-config/blob/3c11035556f25d2407faade3804468dc64b3a53b/2015-tm22-dev-sprint-03/model_config.toml#L865-L869

DavidOry commented 1 month ago

@vivverma9, @AshishKuls Updates per our conversation https://github.com/BayAreaMetro/travel-model-two-config/blob/fa7d224dd4151931e5818315e664a94908d2ee34/2015-tm22-dev-sprint-03/model_config.toml#L865-L873

DavidOry commented 1 month ago

Some additional runtime statistics for a test with four journey levels for morning commute assignments:

  1. Baseline assignment runtime: 12 hours, 28 minutes
  2. Test assignment with three journey levels: 3 hours, 26 minutes
  3. Test assignment with four journey levels: 4 hours, 12 minutes

Excel: observed-transit-assign-journey-level-runtime-update.xlsx

DavidOry commented 1 month ago

Here are Tableau summaries showing the outcomes of the above two tests, with three and four journey levels respectively.

https://mtcdrive.box.com/s/4wd1vvt9vpn3wjk5hdreu2tsvh9ehjf5