1 Carbon intensity during EV charging

To analyse the carbon intensity of the EVs, this analysis uses the KPI of gram CO2 per kilometer, gCO2/km, by using the charging data of the 80 EVs, after pre-processing, cleaning and manipulation.

# General, data manipulation
library(dplyr)
library(lubridate)
library(ggplot2)
library(tidyr)

# Spatial data analysis
library(sp) # create spatial data frame
library(rgdal) # for projecting coordinates
library(maptools)
library(leaflet)

2 German Grid Analysis

2.1 Hourly German electricity data

Using the hourly electricity data from Agora Energiewende, German electricity production for the year of 2015 is quantified into each generating sources (GigaWatt/hour).

agora2015 <- read.csv("../data/Agorameter_2015.csv")
elec2015 <- agora2015[,c(1:16)]
elec2015 <- elec2015[,(-15)]

# convert date.time column into standard date and time format
elec2015$Date.Time <- mdy_hm(elec2015$Date.Time)
str(elec2015)
## 'data.frame':    8760 obs. of  15 variables:
##  $ Year         : int  2015 2015 2015 2015 2015 2015 2015 2015 2015 2015 ...
##  $ Month        : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Day          : int  1 1 1 1 1 1 1 1 1 1 ...
##  $ Hour         : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Date.Time    : POSIXct, format: "2015-01-01 00:00:00" "2015-01-01 01:00:00" ...
##  $ Biomass      : int  5112 5112 5112 5112 5112 5112 5112 5112 5112 5112 ...
##  $ Hydro        : int  348 350 346 341 337 337 338 334 328 322 ...
##  $ Wind.onshore : int  10137 10327 10593 10614 10724 10802 11310 12027 11896 11698 ...
##  $ Wind.offshore: int  0 0 0 0 0 0 0 0 0 0 ...
##  $ PV           : int  0 0 0 0 0 0 0 0 59 852 ...
##  $ Nuclear      : int  11514 11920 11859 11860 11796 11538 11141 10881 11097 10909 ...
##  $ Lignite      : int  17583 17206 16742 15964 16016 15357 14274 13861 14144 14611 ...
##  $ Hard.Coal    : int  6427 5419 5275 5493 5337 5338 5430 5484 5402 5462 ...
##  $ Natural.Gas  : int  8011 7727 7650 7556 7601 7713 7714 7746 7783 7763 ...
##  $ Others       : int  1947 1944 1951 1954 1938 1908 1881 1925 1899 1898 ...

Each of the generation sources are then categorised into “conventional” and “renewable” energy sources.

# use tidyr gather command to turn energy sources into 1 column (wide -> long)
KeepThese <- colnames(elec2015)[1:5]
elec2015long <- gather(elec2015, sources, kWh, -one_of(KeepThese))
elec2015long$ren.con <- "renewable"
elec2015long$ren.con[which(elec2015long$sources %in% c("Hard.Coal","Lignite","Natural.Gas","Nuclear","Others"))] <- "conventional"

2.2 Emission factor table for each energy generating sources

In order to calculate the KPI of gCO2/km among the EVs, the emission factors of each energy generating sources were used. Emission factors are the amount of gCO2 that are emitted while generating 1kWh of energy from that particular source, gCO2/kWh. Usually, fossil-based sources have higher emission factors compared to renewable energy sources. This analysis uses GEMIS, an open source emission factor model from the Institute for Sustainable Analysis and Strategy. The emission factors are selected to match the electricity production and generation energy sources, with the following assumptions:

  • grouping of emission factor that has more than 1 entry on GEMIS (e.g. the analysis uses one average value for natural gas, by using the average figure for the four different gas co-generation value in GEMIS)
  • ‘others’ sources are assumed to use the emission factor of “rapeseedoil”
  • ‘pump’ electricity generation does not have emission factor (discarded in electricity consumption)
  • ‘wind onshore’ and ‘wind offshore’ uses the same emission factor

The following table summarises the emission factors used for the calculations of carbon emission in this research:

##          sources gCO2.kWh
## 1      Hard.Coal   895.04
## 2        Lignite  1007.87
## 3    Natural.Gas   410.13
## 4        Nuclear    55.48
## 5          Hydro     2.79
## 6   Wind.onshore     9.34
## 7  Wind.offshore     9.34
## 8             PV    96.16
## 9        Biomass    57.12
## 10        Others   286.34

2.3 Hourly gCO2/kWh in Grid

By merging the hourly electricity production with the respective energy generating sources, the hourly gCO2/kWh for 2015 is produced. The black areas indicate hours that the grid is more carbon intensive than the green areas.

# create column for each source's emission factor 
elec2015_CO2 <- merge(elec2015long, emfac, by="sources")
elec2015_CO2 <- elec2015_CO2[,c(2:6,1,7:9)]

# create column for CO2 emission for each sources (unit: tCO2)
elec2015_CO2$gCO2_h <- round(elec2015_CO2$kWh*elec2015_CO2$gCO2.kWh,2)
elec2015_CO2 <- arrange(elec2015_CO2, Date.Time)

emfac_hour <- elec2015_CO2 %>%
  group_by(Date.Time) %>%
  summarise(totalkWh = sum(kWh), gCO2 = sum(gCO2_h)) %>%
  mutate(gCO2_kWh=round((gCO2_kWh=gCO2/totalkWh),2)) 

ggplot(emfac_hour, aes(x=Date.Time, y=gCO2_kWh, fill=gCO2_kWh)) +
  geom_bar(stat="identity", alpha=0.8) +
  scale_fill_continuous(low="green", high="black") +
  stat_smooth(method="auto") +
  ylab("CO2 emission (gCO2/kWh)") + xlab("Date") +
  ggtitle("Hourly CO2 emission of Electricity Grid in Germany") +
  theme(axis.text=element_text(size=12))

In order to cross-relate the EV’s actual CO2 emission during the respective charge, the hourly electricity production is merged with the respective emission factor. Due to availability of 2016 electricity data, this analysis uses the electrictiy data from 2015 for 2016.

# create table just with date_hour and emission factor
emfac_2015 <- emfac_hour[,c("Date.Time", "gCO2_kWh")]
colnames(emfac_2015)[1] <- "date_hour"

# emfac for 2016 (using emfac for 2015, shift the extra day in 29th February 2015 to end of year)
emfac_2016 <- emfac_2015
emfac_2016$date_hour <- seq(as.POSIXct("2016-01-01 00:00:00"), as.POSIXct("2016-12-30 22:00:00"), by="hour")

emfac_1516 <- rbind(emfac_2015, emfac_2016)
summary(emfac_1516$gCO2_kWh)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   216.1   408.9   480.6   468.3   537.7   638.3

3 KPI gCO2/km

Developing the KPI of gCO2/km by merging gCO2/kWh from the grid, with kWh/100km from the 80 EVs.

BMW_CO2 <- merge(BMW_charge_complete, emfac_1516, by="date_hour")

BMW_CO2$gCO2 <- round(BMW_CO2$soc_final*BMW_CO2$gCO2_kWh,2)

BMW_CO2_sum <- BMW_CO2 %>% 
  group_by(vehicle_id) %>% 
  summarise(total_gCO2 = sum(gCO2))

BMW_CO2_km <- merge(BMW_CO2_sum, BMW_distance[,c(1,2,4)], by="vehicle_id")
BMW_CO2_km$gCO2_km <- round(BMW_CO2_km$total_gCO2/BMW_CO2_km$total_km,2)
summary(BMW_CO2_km$gCO2_km)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   46.73   58.63   65.60   65.53   71.35  102.20
ggplot(BMW_CO2_km, aes(x=factor(REX_BEV), y=gCO2_km, fill=factor(REX_BEV))) +
  geom_boxplot() +  
  xlab("EV type") +
  ylab("gCO2/km") +
  ggtitle("Average carbon emission for EV types (gCO2/km)") + 
  scale_fill_discrete(name="EV type:") +
  theme(axis.text=element_text(size=12))

The boxplot shows that the 2 types of EVs have almost similar median gCO2/km.

4 Smart Charging

EVs start to charge the moment they are plugged into the charging infrastructure, and stop charging when it is fully charged or unplugged. Öko-Institut e.V describes in detail smart charging strategies which involves charging only at a specific time frame, although the EV is plugged in during the whole period. Following are three possible smart charging strategies:

  • “Network-oriented charging”: where EVs are charged depending on the level of stress or usage intensity of the local electricity grids
  • “Renewable energy-oriented charging”: where EVs are charged depending on the level of fossil-fuel generation and renewable energy generation in the electricity grids
  • “Cost-oriented charging”: where EVs are charged depending on the price of electricity price

This analysis simulates the potential of “Renewable energy-oriented charging”.

4.1 Scenario A: Daily lowest gCO2/kWh hour

Describing the highest occurance of daily minimum gCO2/kWh hour in the grid for 2015-2016.

emfac_1516_lowest <- emfac_1516 %>% 
  group_by(year, month, day) %>% 
  filter(gCO2_kWh == min(gCO2_kWh)) %>% 
  arrange(year, month, day) %>% 
  mutate(gCO2_kWh_low = gCO2_kWh) 

## which hour have the most lowest gCO2/kWh in the day
emfac_1516_lowest_hour <- emfac_1516_lowest %>% 
  group_by(hour) %>% 
  summarise(count = n())

# keep hours that has 0 counts
h24 <- data.frame(hour=c(0:23), zero=0)

emfac_1516_lowest_hour <- right_join(emfac_1516_lowest_hour, h24, by="hour")
emfac_1516_lowest_hour$count[is.na(emfac_1516_lowest_hour$count)] <- 0
emfac_1516_lowest_hour$zero <- NULL

ggplot(data=emfac_1516_lowest_hour, aes(x=factor(hour), y=count, fill=count)) +
  geom_bar(stat="identity") +
  geom_text(aes(label=count), vjust=-0.25, size=3) +
  ggtitle("Lowest daily gCO2/kWh hour count for 2015 & 2016") +
  scale_x_discrete("Hours of the day") +
  scale_y_continuous("Count") +
  theme(legend.position="none") +
  theme(axis.text=element_text(size=12))

The histogram describes the count of each particular hour, that is the lowest gCO2/kWh hour for the day, across the year of 2015 and 2016. It shows that during the hours of 11-13 and 23 have relatively high occurance of lowest hourly gCO2/kWh of the day.

4.2 Scenario B: Daily highest gCO2/kWh hour

Describing the highest occurance of daily maximum gCO2/kWh hour in the grid for 2015-2016.

emfac_1516_highest <- emfac_1516 %>% 
  group_by(year, month, day) %>% 
  filter(gCO2_kWh == max(gCO2_kWh)) %>% 
  arrange(year, month, day) %>% 
  mutate(gCO2_kWh_high = gCO2_kWh)

## which hour have the most lowest gCO2/kWh in the day
emfac_1516_highest_hour <- emfac_1516_highest %>% 
  group_by(hour) %>% 
  summarise(count = n())

# keep hours that has 0 counts
emfac_1516_highest_hour <- right_join(emfac_1516_highest_hour, h24, by="hour")
emfac_1516_highest_hour$count[is.na(emfac_1516_highest_hour$count)] <- 0
emfac_1516_highest_hour$zero <- NULL

ggplot(data=emfac_1516_highest_hour, aes(x=factor(hour), y=count, fill=count)) +
  geom_bar(stat="identity") +
  geom_text(aes(label=count), vjust=-0.25, size=3.0) +
  ggtitle("Highest daily gCO2/kWh hour for 2015 & 2016 ") +
  scale_x_discrete("Hours of the day") +
  scale_y_continuous("Count") +
  theme(legend.position="none") +
  theme(axis.text=element_text(size=12))

As for the hightest gCO2/kWh hour for the day, across the year of 2015 and 2016, the histogram shows that the grid has high counts of daily highest gCO2/kWh during the evening hours as well as midnight.

4.3 Scenario comparison

Compared to the actual charging behaviours of the 80 EVs, the following diagram compares the lowest and highest gCO2/km charging scenario.

ggplot(gCO2_all_long, aes(x=factor(variables), y=score, fill=factor(variables))) +
  geom_boxplot() + 
  xlab("Scenarios") +
  ylab("gCO2/km") +
  ggtitle("Average gCO2/km for 80 EVs in 3 scenarios") +
  scale_fill_discrete(name="Emission Scenarios") +
  theme(axis.text=element_text(size=12))

By shifting the charge to the lowest gCO2/kWh hour for every day in the project period, a maximum of \(18.36%\) of carbon emissions could be reduced, while charging at the highest gCO2/kWh hour would result in an increase of \(20.88%\) of carbon emissions.

Scenarios Diff. to Actual (Min.) Diff. to Actual (Max.)
Lowest grid gCO2/kWh 7.72% 18.36%
Highest grid gCO2/kWh 7.55% 20.88%

5 Spatial Data of Charge events

5.1 Clusters of Charge Events (Overview)

The visualisation describes the cluster of charging events of the 80 EVs throughout the project period. By zooming out, in addition to the majority of charge events taking place in Berlin and Brandenburg area, there are also a minority of charge events that took place in the area of Munich and Cologne.

# colour scheme for gCO2/km
col_gCO2_km <- colorNumeric(
  palette = c("darkgreen", "navyblue", "red3"),
  domain = BMW_GPS_complete$gCO2_km
  )

popup_fast_std <- paste0("Vehicle ID: ", BMW_GPS_complete$vehicle_id,
                        "<br><strong>gCO2/km: </strong>", BMW_GPS_complete$gCO2_km,
                        "<br>charger type: ", BMW_GPS_complete$charger_type
                        )

leaflet(BMW_GPS_complete) %>% 
  setView(lng = 13.40, lat = 52.52, zoom = 10) %>% 
  addProviderTiles("Stamen.Toner", 
                   options=providerTileOptions(opacity=0.35)) %>% 
  addProviderTiles("Stamen.TonerLabels") %>% 
  addPolygons(data=plr, weight=1, color = "grey", fillColor = "lightblue") %>% 
  addCircleMarkers(weight = 1,
                   color = ~col_gCO2_km(gCO2_km),
                   popup = popup_fast_std,
                   clusterOptions = markerClusterOptions()
                   )

5.2 Charging location & gCO2/km

The carbon intensity of the 80 EVs’ charge events are visualised according to the colour scheme, i.e. the lower the carbon intensity, the greener the spot on the map.

# colour scheme for gCO2/km
col_gCO2_km2 <- colorNumeric(
  palette = c("limegreen", "navy", "red3"),
  domain = BMW_GPS_complete$gCO2_km
  )

popup_gCO2_km <- paste0("Vehicle ID: ", BMW_GPS_complete$vehicle_id,
                        "<br><strong>gCO2/km: </strong>", BMW_GPS_complete$gCO2_km, " gCO2/km"
                        )

leaflet(BMW_GPS_complete) %>% 
  setView(lng = 13.40, lat = 52.52, zoom = 10) %>% 
  addProviderTiles("Stamen.Toner", 
                   options=providerTileOptions(opacity=0.35)) %>% 
  addProviderTiles("Stamen.TonerLabels") %>% 
  addPolygons(data=plr, weight=1, color = "grey", fillColor = "#FFFFCC") %>% 
  addCircleMarkers(popup = popup_gCO2_km, 
                   color = ~col_gCO2_km2(gCO2_km),
                   stroke = FALSE, 
                   fillOpacity = 0.5,
                   radius = ~sqrt(gCO2_km)
                   ) %>% 
  addLegend("topright", 
            pal=col_gCO2_km2, 
            values = ~gCO2_km, 
            title="gCO2/km <br> (Charge Events)",
            opacity = 1
            )