Pipeline

library(SunsVoc)

Read in Data

Data is read in from a .csv file and marked into daily increments.

What Data Should Look Like

The imported Data should have columns reflecting results from the ddiv package IVFeatures command run on individual IV curves, Voc and Isc readings from any source (if possible), a string containing the IV curve for each timestamp, poay and temperature readings for the correct timestamp, and a timestamp. All column names should be in an explicit convention.

Use the ddiv package’s command IVFeatures to extract features from individual IV curves. See package documentation for ddiv on how to use this function (note that ddiv requires the segmented package, among others). Rename columns according to the standards below. Note that ddiv returns a list, so it’s a good idea to pass it’s results to data.frame

If your IV Tracer returns Voc and Isc readings, be sure to include them with column names ‘vocc’ and ‘ishc’. If not, rename the ‘Voc’ and ‘Isc’ from the ddiv results to ‘vocc’ and ‘ishc’.

To obtain the string containing an IV curve, use the df2chr function included in this package. Make sure your IV curves are in a dataframe containing voltage and current columns (in that order). it doesn’t matter what the columns are named.

Be sure to add the POA and module temperature (in celsius) data as well. If you have GHI instead of poay, convert it first to POA. This can be done using the Python package PVLib, using latitude, longitude, and elevation.

The timestamp in each column must match the IV curve with it.

Use the following variable names in order to ensure th package processes your data correctly:

Deprecated Name HBase Name Unit/Standard
Fill Factor ffff %
Open Circuit Voltage vocc V
Short Circuit Current ishc A
Max Power Voltage vmpp V
Max Power Current impp A
Max Power Point pmpp W

These are the key variable names – look at the example data below to see other names. The extracted features obtained with ddiv should have the correct names when they are extracted.

It is recommended that the period (the third parameter below) be chosen so that each psuedo-IV curve has 300 points or more. In order to achieve, this make sure there are at least 300 Isc and Voc pairs in each period. 10 minute intervals of collection is generally good for 7 days.

The POA column is converted from the measured ghir.

Extracting I-V features with ddiv

library(dplyr)
#> 
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#> 
#>     filter, lag
#> The following objects are masked from 'package:base':
#> 
#>     intersect, setdiff, setequal, union
library(purrr)
library(magrittr)
#> 
#> Attaching package: 'magrittr'
#> The following object is masked from 'package:purrr':
#> 
#>     set_names
library(ddiv)
str_ddiv <- function(iv_str, pb) {
  iv <- char_to_df(iv_str)
  # initialize result for when IVfeature encounters an error
  err_df <- data.frame("Isc" = NA, "Rsh" = NA,
                       "Voc" = NA, "Rs" = NA,
                       "Pmp" = NA,"Imp" = NA,
                       "Vmp" = NA, "FF" = NA)

  # calculate ddiv with error trapping
  res <- df <- tryCatch(
    as.data.frame(IVfeature(I = iv$I, V = iv$V, num = 200, crt = 0.1, crtvalb = 0.2)),
    error = function(e) err_df
  )

  pb$tick()$print()
  return(res)
}

batch_ddiv <- function(df) {
  iv_list <- df$ivdf 
  pb <- dplyr::progress_estimated(length(iv_list))
  ddiv_df <- iv_list %>% map_dfr(~str_ddiv(iv_str = ., pb))
  res <- cbind(df$tmst, ddiv_df)
  return(res)
}

# it is recommended to test on a small number of iv curves
# to tweak the input parameters of ddiv::IVfeature function
test <- sample_n(df_wbw, 5)
ddiv_test <- batch_ddiv(test)
colnames(ddiv_test) <- c("tmst", "eisc", "ersh", "evoc", "exrs", "epmp", "eimp", "evmp", "exff" )

# bind ddiv result with original dataframe

test_res <- full_join(test, ddiv_test)
#> Joining, by = c("tmst", "eisc", "ersh", "evoc", "exrs", "epmp", "eimp", "evmp", "exff")

colnames(test_res)
#>  [1] "tmst"     "vocc"     "ghir"     "ivdf"     "imxp"     "pmpp"    
#>  [7] "ishc"     "modt"     "vmxp"     "ffff"     "poay"     "eisc"    
#> [13] "ersh"     "evoc"     "exrs"     "epmp"     "eimp"     "evmp"    
#> [19] "exff"     "day"      "n_period" "date"

Data from a narrow range of temperature and irradiance values are needed at one point for calculating various values. Create it now.


T_corr <- median_temp(df_wbw)

T_corr
#> [1] 30

Lastly, the power_loss_bat function, which deals with power loss modes, requires a subset of the larger dataframe. Use select_init_df to do this.


df_init <- select_init_df(df_wbw, days = 21)

colnames(df_init)
#>  [1] "tmst"     "vocc"     "ghir"     "ivdf"     "imxp"     "pmpp"    
#>  [7] "ishc"     "modt"     "vmxp"     "ffff"     "poay"     "eisc"    
#> [13] "ersh"     "evoc"     "exrs"     "epmp"     "eimp"     "evmp"    
#> [19] "exff"     "day"      "n_period" "date"

Psuedo-IV Curve Generation

This functionality is handled by a master function which calls a variety of subfunctions. IVXbyX creates the Psuedo-IV Curves.


df_full <- IVXbyX(df_wbw, corr_temp = "median", 4)

colnames(df_full)
#>  [1] "date"      "day"       "n_period"  "piv"       "pisc"      "pvoc"     
#>  [7] "prsh"      "prss"      "ppmp"      "pimp"      "pvmp"      "pfff"     
#> [13] "isc_1sun"  "voc_1sun"  "n"         "imp_fit"   "vmp_fit"   "pmp_fit"  
#> [19] "rs_fit"    "voc_adjR2" "voc_rmse"  "vmp_adjR2" "vmp_rmse"  "rs_adjR2" 
#> [25] "rs_rmse"

Power Loss Modes

res <- power_loss_phys_bat(df_full, df_init, corr_T = T_corr, N_c = 4);

# Generate Table
knitr::kable(res[1:5, ], caption = "Power Loss Modes")
Power Loss Modes
date uni_I rec rs I_mis
2019-03-01 8.971533 -2.6901230 -10.97729 -3.452138
2019-03-02 -15.278475 -1.7287541 -12.85827 9.204685
2019-03-09 1.617194 -0.5502143 -10.41154 -2.755782
2019-03-16 9.203099 -0.9165730 -15.22506 1.102804
2019-03-23 7.747943 0.2120270 -13.54368 -1.263013

Visualization of power loss modes

library(ggplot2)
ggplot(data = res, aes(x = date)) +
  geom_point(aes(y = uni_I, color = "Uniform current", shape = "Uniform current"), size = 2) +
  geom_point(aes(y = rec, color = "Recombination", shape = "Recombination"), size = 2) +
  geom_point(aes(y = rs, color = "Rs loss", shape = "Rs loss"),  size = 2) +
  geom_point(aes(y = I_mis, color = "I mismatch", shape = "I mismatch"), size = 2) +
  ylab(expression(paste(Delta, "Power (W)"))) +
  xlab("Date") +
  theme_bw() +
  theme(axis.text = element_text(size = 12),
        axis.title = element_text(size = 12),
        legend.text = element_text(size = 10),
        legend.position = "top") +
  scale_shape_manual(values = c(4, 8, 17, 16),
                     name = "Power loss \n mode") +
  scale_colour_discrete(name = "Power loss \n mode")