Skip to contents

This package provides the tools to simulate new EV charging session, on the basis of the Gaussian Mixture Models (GMM) of different EV user profiles found and modeled with {evprof}. The EV model obtained with evprof::get_ev_model function is an object of class evmodel, which contains multiple time-cycle models (e.g. Weekdays and Weekends). At the same time, each time-cycle model consists in a combination of multiple EV user profiles and each user profile consists in multiple GMM. For more information about the EV models read this article from website of evprof package.

Required data

The only function of evsim package necessary to simulate new EV sessions is function simulate_sessions, which returns a tibble of EV charging sessions. The required input arguments to run this function are:

  • The EV model (object of class evmodel)
  • Number of daily sessions for each time-cycle model
  • User profiles distribution (ratio of every user profile) and specific charging power (if required)
  • Charging power distribution (ratio of every different charging rate)
  • Dates to simulate

EV model

Package evsim provides an EV model of example, being the model created from California ACN data in the California article from evprof documentation. This model can be accessed loading the evsim package or with the following function:

ev_model <- evsim::california_ev_model

Let’s take a look to the information that the print function shows for an evmodel object:

## EV sessions model of class "evmodel", created on 2024-01-29 
## Timezone of the model: America/Los_Angeles 
## The Gaussian Mixture Models of EV user profiles are built in:
##   - Connection Models: logarithmic scale
##   - Energy Models: logarithmic scale
## Model composed by 2 time-cycles:
##   1. Workday:
##      Months = 1-12, Week days = 1-5
##      User profiles = Visit, Worktime
##   2. Weekend:
##      Months = 1-12, Week days = 6-7
##      User profiles = Visit

We have 2 different time-cycles in this case (Workdays and Weekends), and each one has its corresponding user profiles. At the same time, each user profile of each time-cycle is modeled by two different models:

  • Connection Models: combination of multiple Gaussian distributions of two variables (Connection Start Time and Connection Duration)
  • Energy Models: combination of multiple Gaussian distributions of one variable (Energy)

Moreover, each User profile has a corresponding ratio over the total number of daily sessions. In this case, for the Workday time-cycle the half of charging sessions are Vist users and the other half Worktime users, while for the Weekend time-cycle all sessions are Visit users.

workday_models <- ev_model$models$user_profiles[[1]]
## # A tibble: 2 × 4
##   profile  ratio connection_models energy_models   
##   <chr>    <dbl> <list>            <list>          
## 1 Visit    0.460 <tibble [3 × 3]>  <tibble [1 × 3]>
## 2 Worktime 0.540 <tibble [3 × 3]>  <tibble [1 × 3]>
weekend_models <- ev_model$models$user_profiles[[2]]
## # A tibble: 1 × 4
##   profile ratio connection_models energy_models   
##   <chr>   <int> <list>            <list>          
## 1 Visit       1 <tibble [2 × 3]>  <tibble [1 × 3]>

Number of sessions per day

To set the number of sessions per day to function simulate_sessions() we need to pass a tibble with variables time_cycle (names corresponding to evmodel$models$time_cycle) and n_sessions (number of daily sessions per day for each time-cycle model). For example:

sessions_day <- tibble(
  time_cycle = ev_model$models$time_cycle,
  n_sessions = c(150, 50)
## # A tibble: 2 × 2
##   time_cycle n_sessions
##   <chr>           <dbl>
## 1 Workday           150
## 2 Weekend            50

User profiles distribution

In the “EV model” section we have seen that inside the ev_model object there is already a default distribution of user profiles, each one with the corresponding ratio. These ratio values were obtained from the clustering process done by evprof package and represent the presence of every user profile over the total number of sessions clustered.

However, we may want to simulate different scenarios or use cases where these ratios differ from the current ones. For example, to simulate the EV demand in an industrial area it could be interesting to prioritize the presence of Worktime sessions over Visit sessions. This can be done by setting a new user profiles’ distribution tibble, which must contain the variables time_cycle, profile and ratio.

The default values for these variables, included within the model, can be obtained with the function get_user_profiles_distribution. It is not mandatory that all time-cycles and user profiles appear in the user_profiles tibble. The rule is simple: only time cycles and user profiles appearing in the user_profiles tibble will be simulated.

Moreover, we can specify the charging power of a concrete user profile. This is useful in some scenarios where there is a type of vehicle (e.g. truck) that charges at different rate than the rest of vehicles. This can be done with the column power in the user_profiles tibble, setting the power values in kW. All user profiles without a specific power value will be simulated with a random charging power obtained according to the charging power distribution (see next section).

For example, let’s say we want to simulate a Workday distribution with 80% of sessions being Worktime and 20% of sessions being Visit, and considering that all Worktime sessions charge with three-phase AC vehicles with 16A connection (11 kW). Then our user_profiles tibble should be the following one:

user_profiles <- tibble(
  time_cycle = c('Workday', 'Workday', 'Weekend'),
  profile = c('Visit', 'Worktime', 'Visit'),
  ratio = c(0.2, 0.8, 1),
  power = c(NA, 11, NA)

## # A tibble: 3 × 4
##   time_cycle profile  ratio power
##   <chr>      <chr>    <dbl> <dbl>
## 1 Workday    Visit      0.2    NA
## 2 Workday    Worktime   0.8    11
## 3 Weekend    Visit      1      NA

Charging powers distribution

The charging power is not a variable modeled by Gaussian Mixture Models (GMM) like the connection variables or the energy. However, the energy GMM can be built by charging power separately (see User profiles modelling on evprof website), since in general the energy charged depends on the charging power (the higher the charging power the higher the energy demand).

The purpose of letting the charging power out of the GMM is that it is a variable that depends on the EV model and the charging point, so it may change between scenarios. For example, we may want to simulate a scenario where all vehicles have a three-phase connection (11 kW) instead of the current scenario with still a lot of vehicles charging with a single phase (3.7 kW). Therefore, it was convenient to set an option to simulate a specific distribution of charging powers for the simulated EV fleet. This is done with a charging_powers tibble with variables power and ratio. In the public charging infrastructure the most common and standard power rates are:

  • 3.7 kW AC (single-phase 16A)
  • 7.3 kW AC (two-phase 16A)
  • 11 kW AC (three-phase 16A)

If the energy GMM were built according to the charging power, it is recommended that the power values in the charging_powers tibble match the original charging power values. These can be found inside the models. For the example ev_model at issue, we see that the Workday energy models of the Visit user profile are not built by charging power since the charging_rate column shows an Unknown value:

workday_models$energy_models[[1]] # Visit profile energy models
## # A tibble: 1 × 3
##   charging_rate ratio energy_models   
##   <chr>         <int> <list>          
## 1 Unknown           1 <tibble [6 × 3]>

In this case, the same energy GMM will be used for all power values. However, if we had energy models for 3.7 kW and 11 kW separately, and we want to simulate 22 kW sessions, the models used for the 22 kW sessions will be the 11 kW models since they are the best approximate solution.

To follow the example, we can set the following charging power distribution:

charging_powers <- tibble(
  power = c(3.7, 7.3, 11),
  ratio = c(0.2, 0.4, 0.4)
## # A tibble: 3 × 2
##   power ratio
##   <dbl> <dbl>
## 1   3.7   0.2
## 2   7.3   0.4
## 3  11     0.4

This complements the variable power from the user_profiles tibble (see previous section). This charging_powers tibble will be used to assign a charging power to all user profiles’ sessions without a power specified in user_profiles.

Datetime values

The function simulate_sessions() automatically selects the time-cycle that fits to a specific date. This is why we have to pass a vector of the dates that we want to simulate. For example:

dates_sim <- seq.Date(from = as.Date('2019-09-10'), to = as.Date('2019-09-15'), by = '1 day')
## [1] "2019-09-10" "2019-09-11" "2019-09-12" "2019-09-13" "2019-09-14"
## [6] "2019-09-15"

Another datetime argument that the simulation function needs is the time resolution of the datetime variables of the sessions (connection start, connection end, charging end, …). For this example we will set a time resolution of 15 minutes, so the argument will be resolution = 15.


We have now all required data and we are ready to simulate the new sessions:

sessions_estimated <- simulate_sessions(
  resolution = 15

## # A tibble: 6 × 11
##   Session Timecycle Profile  ConnectionStartDateTime ConnectionEndDateTime
##   <chr>   <chr>     <chr>    <dttm>                  <dttm>               
## 1 S1      Workday   Visit    2019-09-10 05:00:00     2019-09-10 10:43:00  
## 2 S2      Workday   Visit    2019-09-10 05:15:00     2019-09-10 11:48:00  
## 3 S3      Workday   Worktime 2019-09-10 05:15:00     2019-09-10 14:35:00  
## 4 S4      Workday   Worktime 2019-09-10 05:15:00     2019-09-10 15:11:00  
## 5 S5      Workday   Worktime 2019-09-10 05:15:00     2019-09-10 15:28:00  
## 6 S6      Workday   Worktime 2019-09-10 05:15:00     2019-09-10 18:22:00  
## # ℹ 6 more variables: ChargingStartDateTime <dttm>, ChargingEndDateTime <dttm>,
## #   Power <dbl>, Energy <dbl>, ConnectionHours <dbl>, ChargingHours <dbl>

We have obtained a total of 700 sessions for our 7 simulation dates (i.e. 100 sessions/day), each session corresponding to a specific charging profile. We can notice that the datetime variables have a resolution of 15 minutes according to our configuration, and the energy required corresponds to product of Power and ChargingHours.

We can check that the number of sessions of each power rate corresponds to our configuration:

sessions_estimated %>% 
  group_by(Profile, Power) %>% 
  summarise(n = n()) %>% 
  mutate(pct = n/sum(n)*100)
## # A tibble: 4 × 4
## # Groups:   Profile [2]
##   Profile  Power     n   pct
##   <chr>    <dbl> <int> <dbl>
## 1 Visit      3.7    44    20
## 2 Visit      7.3    88    40
## 3 Visit     11      88    40
## 4 Worktime  11     480   100

EV demand

Finally, we can calculate the estimated demand with function get_demand(). This function accepts an optional argument, dttm_seq, which will be the datetime sequence of the time-series demand data frame returned by the function. We will set the datetime sequence from our first to last date with a resolution of 15 minutes and the timezone of the original model (using the lubridate::with_tz() function):

dttm_seq <- seq.POSIXt(
  from = as.POSIXct('2019-09-10'), 
  to = as.POSIXct('2019-09-16'), 
  by = '15 min'
) %>% 
estimated_demand <- sessions_estimated %>% 

We can also visualize the estimated demand in an HTML plot using the function plot_ts:

estimated_demand %>% 
  plot_ts(ylab = 'Power demand (kW)', fillGraph = T, stackedGraph = T)