Utah Transit Authority#

In this example, we'll predict the energy consumption for some trips operated by the Utah Transit Authority (UTA) in Salt Lake City. This requires specifying the GTFS data we are analyzing, map-matching it to the road network, and running a RouteE-Compass model to produce energy estimates.

This example uses the GTFSEnergyPredictor class, which provides a clean, extensible API for transit energy prediction.

from pathlib import Path

from routee.transit import GTFSEnergyPredictor, sample_inputs_path

# Specify input data location
input_directory = sample_inputs_path() / "saltlake/gtfs"
output_directory = Path("reports/saltlake")

Quick Start: Using the run() Method#

For most use cases, the run() method provides the simplest way to perform the complete energy prediction workflow. This single method call chains together all processing steps and returns trip-level energy predictions.

We'll analyze routes 806 and 807 on August 2nd, 2023, using the Battery Electric Bus model. We'll include deadhead trips and estimated HVAC energy.

predictor = GTFSEnergyPredictor(
    gtfs_path=input_directory,
    vehicle_models=["Transit_Bus_Battery_Electric"],
    output_dir=output_directory,
)

trip_results = predictor.run(
    date="2023/08/02",
    routes=["806", "807"],
    add_depot_deadhead=True,
    add_mid_block_deadhead=True,
    add_hvac=True,
    save_results=False,
)
Downloaded: G4900350.csv
Downloaded: G4900490.csv
Downloaded: G4900450.csv
Downloaded: G4900110.csv
Downloaded: G4900570.csv
Downloaded: G4900030.csv
Downloaded: G4900430.csv
INFO:routee.transit.predictor:Initialized GTFSEnergyPredictor for /opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/routee/transit/resources/sample_inputs/saltlake/gtfs
INFO:routee.transit.predictor:Loading GTFS data...
INFO:routee.transit.predictor:Feed includes 1 agencies: ['Utah Transit Authority']. Total trips: 12037, shapes: 238
INFO:routee.transit.predictor:Loaded 12037 trips and 199 shapes
INFO:routee.transit.predictor:Filtering trips (date=2023/08/02, routes=['806', '807'])...
INFO:routee.transit.predictor:Filtered to 16 trips and 4 shapes
INFO:routee.transit.predictor:Preparing mid-block deadhead trips...
INFO:routee.transit.predictor:Preparing depot deadhead trips...
INFO:routee.transit.predictor:Matched GTFS agency 'Utah Transit Authority' to NTD ID 80001 ('Utah Transit Authority').
INFO:routee.transit.predictor:Building CompassApp from bounding box: (-112.03101, 40.22523, -111.65814, 40.482409999999994)
INFO:nrel.routee.compass.io.generate_dataset:running pipeline import with phases: [['GRAPH', 'CONFIG', 'POWERTRAIN']]
INFO:nrel.routee.compass.io.generate_dataset:processing graph topology and speeds
INFO:nrel.routee.compass.io.generate_dataset:adding grade information
INFO:nrel.routee.compass.io.utils:downloading n41w112
INFO:nrel.routee.compass.io.utils:downloading n41w113
INFO:nrel.routee.compass.io.generate_dataset:processing vertices
INFO:nrel.routee.compass.io.generate_dataset:processing edges
INFO:nrel.routee.compass.io.generate_dataset:writing vertex files
INFO:nrel.routee.compass.io.generate_dataset:writing edge files
INFO:nrel.routee.compass.io.generate_dataset:writing edge attribute files
INFO:nrel.routee.compass.io.generate_dataset:copying default configuration TOML files
INFO:nrel.routee.compass.io.generate_dataset:downloading the default RouteE Powertrain models
INFO:nrel.routee.compass.io.generate_dataset:copying vehicle configuration files
INFO:nrel.routee.compass.io.generate_dataset:running 2 dataset generation hooks
INFO:gtfs_processing:Wrote 109107 stop-edge mappings to reports/saltlake/compass_app/gtfs_stops.csv
INFO:gtfs_processing:Copied transit_energy.toml to reports/saltlake/compass_app/transit_energy.toml
graph edge list 0: /home/runner/work/routee-transit/routee-transit/docs/examples/reports/saltlake/compass_app/edges-compass.csv.gz: 46816it [00:00, 1768199.88it/s]
INFO:routee.transit.predictor:CompassApp initialized
building adjacencies: 100%|██████████| 46816/46816 [00:00<00:00, 4605235.00it/s]
INFO:routee.transit.predictor:Routing mid-block deadhead trips...
INFO:routee.transit.deadhead_router:Deadhead routing: 7 trips reduced to 3 unique O-D pairs


applying input plugin 1: 100%|██████████| 3/3 [00:00<00:00, 8182.21it/s]

applying input plugin 2: 100%|██████████| 3/3 [00:00<00:00, 234594.92it/s]

search: 100%|██████████| 3/3 [00:00<00:00, 83.99it/s]
/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/routee/transit/predictor.py:805: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
  self.feed.stops = pd.concat(
INFO:routee.transit.predictor:Added 7 mid-block deadhead trips
INFO:routee.transit.predictor:Routing depot deadhead trips...
INFO:routee.transit.deadhead_router:Deadhead routing: 9 trips reduced to 3 unique O-D pairs


applying input plugin 1: 100%|██████████| 3/3 [00:00<00:00, 47534.54it/s]

applying input plugin 2: 100%|██████████| 3/3 [00:00<00:00, 300751.91it/s]

search: 100%|██████████| 3/3 [00:00<00:00, 34.27it/s]
INFO:routee.transit.deadhead_router:Deadhead routing: 9 trips reduced to 3 unique O-D pairs


applying input plugin 1: 100%|██████████| 3/3 [00:00<00:00, 39431.14it/s]


applying input plugin 2: 100%|██████████| 3/3 [00:00<00:00, 124398.74it/s]

search: 100%|██████████| 3/3 [00:00<00:00, 60.43it/s]
/opt/hostedtoolcache/Python/3.11.15/x64/lib/python3.11/site-packages/routee/transit/predictor.py:1046: FutureWarning: The behavior of DataFrame concatenation with empty or all-NA entries is deprecated. In a future version, this will no longer exclude empty or all-NA columns when determining the result dtypes. To retain the old behavior, exclude the relevant entries before the concat operation.
  self._deadhead_stops = pd.concat(
INFO:routee.transit.predictor:Added 18 depot deadhead trips
INFO:routee.transit.predictor:Predicting energy for 1 vehicle model(s)...
INFO:routee.transit.predictor:Running map matching for 13 shapes...
map matching:  15%|█▅        |  2/13 [00:00<00:00, 17.30it/s]
map matching: 100%|██████████| 13/13 [00:01<00:00, 6.94it/s]
INFO:routee.transit.predictor:Running energy prediction via CompassApp for: Transit_Bus_Battery_Electric
calculating paths: 100%|██████████| 13/13 [00:00<00:00, 726.72it/s]
INFO:routee.transit.predictor:Adding HVAC energy impacts...
INFO:routee.transit.predictor:Energy prediction complete

The run() method automatically performs all these steps:

  1. Loads the GTFS feed

  2. Filters trips by date and routes

  3. Adds mid-block deadhead trips (between consecutive trips)

  4. Adds depot deadhead trips (to/from depot)

  5. Matches shapes to OpenStreetMap road network and adds road grade (via RouteE-Compass)

  6. Predicts energy consumption with RouteE-Compass

  7. Adds estimated HVAC energy impacts

  8. Saves results to CSV files

Let's examine the results. The columns include information about the energy predictions made (including any HVAC component) as well as GTFS fields that apply to each trip, and a description of the weather scenario considered:

trip_results.columns
trip_results[["trip_id", "vehicle", "scenario", "energy_used", "miles"]].head()
trip_id vehicle scenario energy_used miles
0 5181298_to_5181301 Transit_Bus_Battery_Electric median 17.652321 16.290775
1 5181298_to_5181301 Transit_Bus_Battery_Electric summer 16.710315 16.290775
2 5181298_to_5181301 Transit_Bus_Battery_Electric winter 45.223844 16.290775
3 5181297_to_5181299 Transit_Bus_Battery_Electric median 13.284857 16.290775
4 5181297_to_5181299 Transit_Bus_Battery_Electric summer 13.272138 16.290775

Analyze Energy Efficiency#

We can calculate energy efficiency in kWh per mile, including HVAC loads. The results include a scenario column (summer/winter.median) for HVAC impacts.

Weather Impacts#

Let's see how energy efficiency in kWh/mi compares across different weather scenarios:

if "scenario" in trip_results.columns:
    trip_results["kwh_per_mi"] = trip_results["energy_used"] / trip_results["miles"]

trip_results.groupby("scenario")["kwh_per_mi"].mean()
scenario
median    1.674372
summer    1.961275
winter    5.371088
Name: kwh_per_mi, dtype: float64

We can see that winter requires the greatest energy, since the cold climate in Utah requires a heavy HVAC load.

Efficiency by Route#

How does typical energy efficiency in the median scenario (meaning temperatures are taken from the day of a typical year with the median average temperature)?

We can check by filtering by scenario and then grouping by route:

# First, filter out any deadhead trips
median_results = trip_results[trip_results["trip_type"] == "service"].copy()
# Then, only include median weather impacts
median_results = median_results[median_results["scenario"] == "median"]
median_results.groupby("route_short_name")["kwh_per_mi"].mean().sort_values(
    ascending=False
)
route_short_name
807    1.295621
806    1.020716
Name: kwh_per_mi, dtype: float64

Route 807 requires more energy on average.

Access Additional Results#

Besides trip-level results, you can also access link-level results. These detailed results can help you better understand differences in predictions across trips.

# Link-level predictions show energy for each road segment
link_results = predictor.get_link_predictions()
link_results.head()
match_id edge_index edge_list_id edge_id trip_elevation_loss trip_time edge_distance trip_energy_electric trip_distance edge_grade ... edge_turn_delay battery_capacity edge_time trip_elevation_gain trip_soc geometry shape_id vehicle energy_used energy_unit
0 0 0 0 30082 0.000000 0.055853 0.025420 0.080083 0.025420 0.034760 ... 0.0 500.0 0.055853 0.000884 0.999840 LINESTRING (-111.71143 40.27542, -111.71156 40... -111.712,40.276->-111.895,40.426 Transit_Bus_Battery_Electric 13.044992 kWh
1 0 1 0 30083 0.000000 0.138225 0.029904 0.252935 0.055324 0.088317 ... 1.0 500.0 0.082372 0.003525 0.999494 LINESTRING (-111.71166 40.27568, -111.71109 40... -111.712,40.276->-111.895,40.426 Transit_Bus_Battery_Electric 13.044992 kWh
2 0 2 0 246 -0.006952 0.497408 0.091411 0.020610 0.146736 -0.076052 ... 9.5 500.0 0.359183 0.003525 0.999959 LINESTRING (-111.71109 40.27571, -111.71139 40... -111.712,40.276->-111.895,40.426 Transit_Bus_Battery_Electric 13.044992 kWh
3 0 3 0 40030 -0.006952 0.541968 0.020931 0.046976 0.167667 0.001036 ... 0.5 500.0 0.044559 0.003546 0.999906 LINESTRING (-111.71275 40.27596, -111.71275 40... -111.712,40.276->-111.895,40.426 Transit_Bus_Battery_Electric 13.044992 kWh
4 0 4 0 259 -0.007189 0.572492 0.017637 0.051436 0.185304 -0.013422 ... 0.0 500.0 0.030525 0.003546 0.999897 LINESTRING (-111.71305 40.27608, -111.7131 40.... -111.712,40.276->-111.895,40.426 Transit_Bus_Battery_Electric 13.044992 kWh

5 rows × 22 columns