GRC Bottom-Up Methodology

Author

Neha Suresh

Necessary Packages to Import Data

# Install the package if not already installed
#install.packages("readxl")


# Load necessary packages
library(readxl)
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

Data/Sources Used:

This is a list of the data sets and sources that are used for the calculations below:

  • FY2024 Data. xlsx: extracted Annual VMT per route and % of ZEB per route

  • Sustainability Report 2023

Methodology Document:

Rough notes of my methodology so far:

  • import data form FY2024 Data.xlsx –> read each sheet of the data, noting relevant informaiton

    • remove last rows of data and clean up column names

    • join both sheets by route number to get the mileage, %ZEB in same sheet

      • left join RouteFact with Clean Corridor
    • store relevant columns in the new combined data frame

  • Get Vehicle Miles Traveled for ZEB and not ZEB on Each Route:

    • create new columns for ZEB mileage per route and non-ZEB mileage per route

    • clean up data by adding 0s instead of NAs to the columns with no ZEB info

  • Find and store Fuel Efficiency:

    • store data from Sustainability Report 2023, estimate values for Battery, Fuel Cell, and Diesel fuel efficiencFy
  • Find and store Carbon Factors:

    • search AC Transit, EPA, and other reliable sources to find data on carbon factors for each bus type

To-do:

  • get fuel cell carbon factor

  • get annual carbon emissions factor for diesel buses

Importing both sheets of Data from FY2024 Data.xlsx

# Import sheet 1, Mileage data

mileage <- read_excel("VMT_Route.xlsx", sheet = "RouteFact") 

# There are 134 rows and 8 columns in this data frame
mileage = slice(mileage, 1:133) # Remove last row , which is totals

# View first 10 rows of data
slice_head(mileage, n = 10)
# A tibble: 10 × 8
   Route  Plan    Op `Not Op` `SO Y %`     Mi.   Hrs. `Avg. Speed`
   <chr> <dbl> <dbl>    <dbl>    <dbl>   <dbl>  <dbl>        <dbl>
 1 1T    80534 75009     5525    0.931 668167. 65851.         13.9
 2 40    64512 61425     3087    0.952 546216. 50451.         15.7
 3 6     61711 59918     1793    0.971 326602. 35896.         14.4
 4 51A   59806 56446     3360    0.944 460761. 45459.         14.1
 5 51B   59772 58279     1493    0.975 284410. 39346.         12.6
 6 57    59336 57725     1611    0.973 675650. 58956.         17.4
 7 73    52704 50403     2301    0.956 258287. 18802.         18.0
 8 18    46636 45208     1428    0.969 401811. 40963.         15.7
 9 10    44819 43296     1523    0.966 307729. 25198.         16.8
10 72R   44405 40958     3447    0.922 571986. 45922.         16.0
# Import sheet 2 Clean Corridor data

# There are 52 rows and 9 columns in this data frame

percent_ZEB <- read_excel("VMT_Route.xlsx", sheet = "Clean Corridor") 

# Remove last row which is the totals, change RouteAlpha to Route
percent_ZEB = slice(percent_ZEB, 1:51) |> 
  select("Route" = RouteAlpha, 2:9)

# View first 10 rows of data
slice_head(percent_ZEB, n = 10)
# A tibble: 10 × 9
   Route  Plan `Op Y` `Op N` `Op as %` `Op ZEB` `ZEB%` `Distance (A)` `Hrs. (A)`
   <chr> <dbl>  <dbl>  <dbl>     <dbl>    <dbl>  <dbl>          <dbl>      <dbl>
 1 88    39785  39014    771     0.981    16308  0.418        250345.     23265.
 2 18    46636  45208   1428     0.969    13331  0.295        401811.     40963.
 3 33    40810  39843    967     0.976    11111  0.279        309172.     27440.
 4 96    22836  22256    580     0.975     6180  0.278        206238.     19354.
 5 12    30909  30231    678     0.978     7903  0.261        289772.     31946.
 6 29    26356  25884    472     0.982     4704  0.182        205082.     21179.
 7 36    27070  26577    493     0.982     4631  0.174        209647.     19857.
 8 45    26225  25077   1148     0.956     3897  0.155        222780.     19678.
 9 52    39665  38679    986     0.975     4646  0.120        155194.     19797.
10 39    10112   9953    159     0.984     1109  0.111         61794.      5343.

Joining both Sheets on Route Number:

# Make sure to exclude last column which is the totals on both sheets

# Left join mileage with percent_ZEB on route number

mileage_ZEB = left_join(x = mileage, 
          y = percent_ZEB,
          by = join_by(Route))

# View first 10 rows of new data set of necessary columns Route, Mi., Op ZEB, ZEB%, Op, Not Op, SO Y%

mileage_ZEB = mileage_ZEB |>
  select(Route, `Mi.`, `Op`, `Not Op`, `SO Y %`, `Op ZEB`, `ZEB%`) 

slice_head(mileage_ZEB, n = 10)
# A tibble: 10 × 7
   Route     Mi.    Op `Not Op` `SO Y %` `Op ZEB`     `ZEB%`
   <chr>   <dbl> <dbl>    <dbl>    <dbl>    <dbl>      <dbl>
 1 1T    668167. 75009     5525    0.931       NA NA        
 2 40    546216. 61425     3087    0.952       28  0.000456 
 3 6     326602. 59918     1793    0.971     4901  0.0818   
 4 51A   460761. 56446     3360    0.944     5186  0.0919   
 5 51B   284410. 58279     1493    0.975     5731  0.0983   
 6 57    675650. 57725     1611    0.973      640  0.0111   
 7 73    258287. 50403     2301    0.956       76  0.00151  
 8 18    401811. 45208     1428    0.969    13331  0.295    
 9 10    307729. 43296     1523    0.966       NA NA        
10 72R   571986. 40958     3447    0.922        1  0.0000244

Get Vehicle Miles Traveled for ZEB and not ZEB on Each Route:

# Mutating new columns to calcualte VMT for ZEB and not ZEB per route

mileage_ZEB = mileage_ZEB |>
  mutate(VMT_ZEB = `Mi.`*`ZEB%`,
         VMT_ZEB = replace_na(VMT_ZEB, 0),
         VMT_Not_ZEB = `Mi.`-(VMT_ZEB))

# View VMT ZEB and not ZEB for first 10 rows

mileage_ZEB |>
  select(Route, VMT_ZEB, VMT_Not_ZEB) |>
  slice_head(n = 10)
# A tibble: 10 × 3
   Route  VMT_ZEB VMT_Not_ZEB
   <chr>    <dbl>       <dbl>
 1 1T         0       668167.
 2 40       249.      545967.
 3 6      26714.      299887.
 4 51A    42333.      418429.
 5 51B    27968.      256442.
 6 57      7491.      668160.
 7 73       389.      257898.
 8 18    118486.      283324.
 9 10         0       307729.
10 72R       14.0     571972.

Find and store Fuel Efficiency:

Data is from 2023 Sustainability report: https://actransit.legistar.com/View.ashx?M=F&ID=13043668&GUID=9DD959BB-CF05-4738-BF53-8219F18D7B30

Energy/Fuel Fuel Efficiency Metric Equivalent Efficiency
Diesel 4.78 miles/gallon 4.78
Fuel Cell 7.50 miles/kg 8.33
Battery 0.40 miles/kwh 15.21
# Storing Fuel Efficiency Values in respective variables

diesel_fe = 4.78       # miles/gallon
fuel_cell_fe_eq = 8.33    # miles/gallon eq
battery_fe_eq = 15.21      # mile/kwh eq

Find and store Emissions Factors for Diesel, Fuel Cell, and Battery Buses:

Diesel Carbon Factor:

  • Extracted from Table 2 and 4 Diesel Passenger Cars: https://www.epa.gov/sites/default/files/2015-07/documents/emission-factors_2014.pdf

  • Assuming that most Diesel buses were manufactured 1983-present, as if not emissions factors would be slightly higher

  • CO2 Emissions Factor (kg CO2 per gallon): 10.21

    • CH4 Emissions Factor (g/mile): 0.0005

    • N20 Emissions Factor (g/mile): 0.0010

    • Conversion to CO2e (same as Co2 (100 year Global Warming Potential), Multiply each factor by their respective GWP values:

      • CH4: 0.0005 * 25 (CO2e g/ mile)

      • N20: 0.0010 * 298 (CO2e g/ mile)

    • Then convert units to miles/gallon:

      • CH4: (0.0005 * 25) * 0.001 / 10.21 (gallons/miles)

      • N20: (0.0010 * 298) * 0.001 / 10.21 (gallons/miles)

        • assume that after this multiplication units are kg CO2 per gallon or g CO2 per gallon?
    • Then can add together both values that have been converted to the same units:

      • total_co2e_emissions_factor = (0.0005 * 25) * 0.001 / 10.21 (gallons/miles) + N20: (0.0010 * 298) * 0.001 / 10.21 (gallons/miles)

Battery (electric) Factor:

  • Got data from: https://www.epa.gov/egrid/power-profiler#/ –> from California Region

  • 1 KWh = 0.001 MWh

    • CO2 emissions factor: 497.4 lbs/MWh

    • SO2 emissions factor: 0.03 lbs/MWh

    • NOx emissions factor: 0.467 lbs/MWh

Fuel Cell (hydrogen) Factor:

# Storing variables for diesel emissions factors & conversions:

# emissions factor * global warming potential * g to kg conversion / CO2 diesel gallons conversion

CO2_gallons_conversion = 10.21

CH4_emissions_diesel = (0.0005 * 25 * 0.001) / CO2_gallons_conversion

N20_emissions_diesel = (0.0010 * 298 * 0.001) / CO2_gallons_conversion

emissions_factor = CH4_emissions_diesel + N20_emissions_diesel

# check with luke if CO2 emissions diesel works out for calculations (kg CO2 per gallon)


# Storing variables for battery factors and conversions to from mwh to kwh

CO2_emissions_electric = 497.4 * 0.001 

SO2_emissions_electric = 0.03 * 0.001 

NOX_emissions_electric = 0.467 * 0.001 

Calculate Annual Diesel (Non-ZEB) Emissions for each Route:

mileage_ZEB = mileage_ZEB |>
  mutate("total_gallons_diesel" = VMT_Not_ZEB / diesel_fe,
         "CO2e_emissions_Non_ZEB" = total_gallons_diesel * emissions_factor) 

mileage_ZEB |>
    select(Route, 
           VMT_Not_ZEB, 
           total_gallons_diesel, 
           CO2e_emissions_Non_ZEB)
# A tibble: 133 × 4
   Route VMT_Not_ZEB total_gallons_diesel CO2e_emissions_Non_ZEB
   <chr>       <dbl>                <dbl>                  <dbl>
 1 1T        668167.              139784.                   4.25
 2 40        545967.              114219.                   3.47
 3 6         299887.               62738.                   1.91
 4 51A       418429.               87537.                   2.66
 5 51B       256442.               53649.                   1.63
 6 57        668160.              139782.                   4.25
 7 73        257898.               53954.                   1.64
 8 18        283324.               59273.                   1.80
 9 10        307729.               64378.                   1.96
10 72R       571972.              119659.                   3.64
# ℹ 123 more rows

ASK LUKE IF FOLLOWING IS OK:

average_ZEB_fe = (fuel_cell_fe_eq + battery_fe_eq) / 2


mileage_ZEB = mileage_ZEB |>
  mutate("total_gallons_diesel_eq" = VMT_ZEB / average_ZEB_fe,
         "CO2e_emissions_ZEB" = total_gallons_diesel_eq * emissions_factor) 

mileage_ZEB |>
    select(Route, 
           total_gallons_diesel,
           total_gallons_diesel_eq,
           CO2e_emissions_ZEB,
           CO2e_emissions_Non_ZEB)
# A tibble: 133 × 5
   Route total_gallons_diesel total_gallons_diesel_eq CO2e_emissions_ZEB
   <chr>                <dbl>                   <dbl>              <dbl>
 1 1T                 139784.                    0             0        
 2 40                 114219.                   21.2           0.000643 
 3 6                   62738.                 2270.            0.0690   
 4 51A                 87537.                 3597.            0.109    
 5 51B                 53649.                 2376.            0.0723   
 6 57                 139782.                  636.            0.0194   
 7 73                  53954.                   33.1           0.00101  
 8 18                  59273.                10067.            0.306    
 9 10                  64378.                    0             0        
10 72R                119659.                    1.19          0.0000361
# ℹ 123 more rows
# ℹ 1 more variable: CO2e_emissions_Non_ZEB <dbl>